Skip to content

Commit

Permalink
поддержка выражений в конце модуля (вне процедуры/функции)
Browse files Browse the repository at this point in the history
  • Loading branch information
LazarenkoA committed Feb 15, 2024
1 parent 350d7b4 commit f03f578
Show file tree
Hide file tree
Showing 8 changed files with 572 additions and 363 deletions.
4 changes: 4 additions & 0 deletions ast/ast_print.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ func (p *astPrint) print() (result string) {
if pf, ok := node.(*FunctionOrProcedure); ok {
builder.WriteString(p.printFunctionOrProcedure(pf))
builder.WriteString(p.newLine(3))
} else {
builder.WriteString(p.newLine(1))
builder.WriteString(p.printBodyItem(node, 0))
}
}

Expand All @@ -80,6 +83,7 @@ func (p *astPrint) printGlobalVariables(variables GlobalVariables) (result strin
builder.WriteString("Перем ")
builder.WriteString(variables.Var.Name)
builder.WriteString(export)
builder.WriteString(";")

return
}
Expand Down
14 changes: 13 additions & 1 deletion ast/ast_struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ type GlobalVariables struct {

type ModuleStatement struct {
Name string
GlobalVariables map[string]GlobalVariables
GlobalVariables map[string]GlobalVariables `json:"GlobalVariables,omitempty"`
Body []Statement
}

Expand Down Expand Up @@ -283,6 +283,18 @@ func (m *ModuleStatement) Append(item Statement, yylex yyLexer) {
for _, item := range v {
m.Append(item, yylex)
}
case []Statement:
m.Body = append(m.Body, v...)
case *FunctionOrProcedure:
// если предыдущее выражение не процедура функция, то это значит что какой-то умник вначале или в середине модуля вставил какие-то выражения, а это нельзя. 1С разрешает выражения только в конце модуля
if len(m.Body) > 0 {
if _, ok := m.Body[len(m.Body)-1].(*FunctionOrProcedure); !ok {
yylex.Error("procedure and function definitions should be placed before the module body statements")
return
}
}

m.Body = append(m.Body, item)
default:
m.Body = append(m.Body, item)
}
Expand Down
73 changes: 67 additions & 6 deletions ast/ast_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ func TestParseModule(t *testing.T) {
&НаКлиенте
Процедура вв2()
Конецпроцедуры`

a := NewAST(code)
Expand All @@ -177,20 +177,76 @@ func TestParseModule(t *testing.T) {
code := `Перем в;
Процедура вв()
Конецпроцедуры
Перем а;`

a := NewAST(code)
err := a.Parse()
assert.ErrorContains(t, err, "variable declarations must be placed at the beginning of the module")
})
t.Run("bosh", func(t *testing.T) {
code := `fdfd`
t.Run("without FunctionProcedure pass", func(t *testing.T) {
code := `
Пока Истина Цикл
КонецЦикла;
ВызватьИсключение "";
Если Истина Тогда
а = 0;
КонецЕсли`

a := NewAST(code)
err := a.Parse()
assert.NoError(t, err)
})
t.Run("without FunctionProcedure pass", func(t *testing.T) {
code := `Перем в;
Функция test1()
КонецФункции
Функция test1()
КонецФункции
Пока Истина Цикл
КонецЦикла;
ВызватьИсключение "";
Если Истина Тогда
а = 0;
КонецЕсли;`

a := NewAST(code)
err := a.Parse()
assert.NoError(t, err)

// fmt.Println(a.Print(PrintConf{Margin: 4}))
})
t.Run("without FunctionProcedure error", func(t *testing.T) {
code := `
Пока Истина Цикл
КонецЦикла;
ВызватьИсключение "";
Если Истина Тогда
а = 0;
КонецЕсли;
Процедура test()
КонецПроцедуры`

a := NewAST(code)
err := a.Parse()
assert.EqualError(t, err, "syntax error. line: 1, column: 0 (unexpected literal: \"fdfd\")")
assert.Error(t, err)
// assert.ErrorContains(t, err, "procedure and function definitions should be placed before the module body statements")
})
}

Expand Down Expand Up @@ -1463,7 +1519,12 @@ func TestParseAST(t *testing.T) {
ОткрытьСправку(НавигационнаяСсылка);
Возврат;
КонецЕсли;
КонецПроцедуры`
КонецПроцедуры
Если Оповещение <> Неопределено Тогда
ПриложениеЗапущено = Истина;
ВыполнитьОбработкуОповещения(Оповещение, ПриложениеЗапущено);
КонецЕсли;`

a := NewAST(code)
err := a.Parse()
Expand Down
57 changes: 35 additions & 22 deletions ast/grammar.y
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,20 @@ package ast
%%
mains: main
| mains main;
module: body {
if ast, ok := yylex.(*AstNode); ok {
ast.ModuleStatement.Append($1, yylex)
}
}
| main_items opt_body {
if ast, ok := yylex.(*AstNode); ok {
ast.ModuleStatement.Append($2, yylex)
}
};
main_items: main
| main_items main
;
main: global_variables {
if ast, ok := yylex.(*AstNode); ok {
Expand All @@ -109,7 +120,8 @@ main: global_variables {
if ast, ok := yylex.(*AstNode); ok {
ast.ModuleStatement.Append($1, yylex)
}
};
}
;
opt_directive: { $$ = nil}
| Directive { $$ = &$1}
Expand Down Expand Up @@ -147,6 +159,7 @@ opt_body: { $$ = nil }
| body { $$ = $1 }
;
body: stmt { $$ = []Statement{$1} }
| body separator opt_stmt {
if $2.literal == ":" && len($1) > 0 {
Expand Down Expand Up @@ -224,26 +237,26 @@ ternary: '?' '(' expr comma expr comma expr ')' {

/* циклы */
stmt_loop: For Each Identifier In through_dot Loop { setLoopFlag(true, yylex) } opt_body EndLoop {
$$ = &LoopStatement{
For: $3.literal,
In: $5,
Body: $8,
}
setLoopFlag(false, yylex)
}
| For expr To expr Loop { setLoopFlag(true, yylex) } opt_body EndLoop {
$$ = &LoopStatement{
For: $2,
To: $4,
Body: $7,
}
setLoopFlag(false, yylex)
}
|While expr Loop { setLoopFlag(true, yylex) } opt_body EndLoop {
$$ = &LoopStatement{
WhileExpr: $2,
Body: $5,
$$ = &LoopStatement{
For: $3.literal,
In: $5,
Body: $8,
}
setLoopFlag(false, yylex)
}
| For expr To expr Loop { setLoopFlag(true, yylex) } opt_body EndLoop {
$$ = &LoopStatement{
For: $2,
To: $4,
Body: $7,
}
setLoopFlag(false, yylex)
}
|While expr Loop { setLoopFlag(true, yylex) } opt_body EndLoop {
$$ = &LoopStatement{
WhileExpr: $2,
Body: $5,
}
};

stmt : expr { $$ = $1 }
Expand Down
90 changes: 89 additions & 1 deletion ast/tokens_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func Test_Next(t *testing.T) {
})
t.Run("error", func(t *testing.T) {
tok := new(Token)
code := `тестПерем = '0001k101'`
code := `тестПерем = 'gfdgfg'`

token, err := tok.Next(code)
assert.NoError(t, err)
Expand Down Expand Up @@ -378,6 +378,94 @@ func Benchmark(b *testing.B) {
})
_ = test
})
b.Run("next", func(b *testing.B) {
tok := new(Token)
tok.srs = `
Процедура ОткрытьНавигационнуюСсылку(НавигационнаяСсылка, Знач Оповещение = Неопределено) Экспорт
ПустаяДата = '00010101000000';
ПустаяДата = '20131231235959';
КлючЗаписиРегистра = Новый("РегистрСведенийКлючЗаписи.СостоянияОригиналовПервичныхДокументов", ПараметрыМассив);
МассаДМ = ВыборкаЕдИзм.МассаДМ/Количество;
стр = новый Структура("Цикл", 1);
стр.Цикл = 0;
Если КодСимвола < 1040 ИЛИ КодСимвола > 1103 И КодыДопустимыхСимволов.Найти(КодСимвола) = Неопределено И Не (Не УчитыватьРазделителиСлов И ЭтоРазделительСлов(КодСимвола)) Тогда
Возврат ;
КонецЕсли;
перейти ~метка;
МассивСтроки.Добавить(Новый ФорматированнаяСтрока(ЧастьСтроки.Значение, Новый Шрифт(,,Истина)));
Позиция = Найти(Строка, Разделитель);
Пока Позиция > 0 Цикл
Подстрока = Лев(Строка, Позиция - 1);
Если Не ПропускатьПустыеСтроки Или Не ПустаяСтрока(Подстрока) Тогда
Если СокращатьНепечатаемыеСимволы Тогда
Результат.Добавить(СокрЛП(Подстрока));
Иначе
Результат.Добавить(Подстрока);
КонецЕсли;
КонецЕсли;
Строка = Сред(Строка, Позиция + СтрДлина(Разделитель));
Позиция = Найти(Строка, Разделитель);
КонецЦикла;
~метка:
вы = ввывыв[0];
СтрокаСпискаПП[ТекКолонка.Ключ].Вставить(ТекКолонкаЗначение.Ключ, УровеньГруппировки3[ПрефиксПоля + СтрЗаменить(ТекКолонкаЗначение.Значение, ".", "")]);
Контекст = Новый Структура();
Контекст.Вставить("НавигационнаяСсылка", НавигационнаяСсылка);
Контекст.Вставить("Оповещение", Оповещение);
ОписаниеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
НСтр("ru = 'Не удалось перейти по ссылке ""%1"" по причине:
|Неверно задана навигационная ссылка.'"),
НавигационнаяСсылка);
Если Не ОбщегоНазначенияСлужебныйКлиент.ЭтоДопустимаяСсылка(НавигационнаяСсылка) Тогда
ОбщегоНазначенияСлужебныйКлиент.ОткрытьНавигационнуюСсылкуОповеститьОбОшибке(ОписаниеОшибки, Контекст);
Возврат;
КонецЕсли;
Если ОбщегоНазначенияСлужебныйКлиент.ЭтоВебСсылка(НавигационнаяСсылка)
Или ОбщегоНазначенияСлужебныйКлиент.ЭтоНавигационнаяСсылка(НавигационнаяСсылка) Тогда
Попытка
а = а /0;
Исключение
ОбщегоНазначенияСлужебныйКлиент.ОткрытьНавигационнуюСсылкуОповеститьОбОшибке(ОписаниеОшибки, Контекст);
Возврат;
КонецПопытки;
Если Оповещение <> Неопределено Тогда
ПриложениеЗапущено = Истина;
ВыполнитьОбработкуОповещения(Оповещение, ПриложениеЗапущено);
КонецЕсли;
Возврат;
КонецЕсли;
Если ОбщегоНазначенияСлужебныйКлиент.ЭтоСсылкаНаСправку(НавигационнаяСсылка) Тогда
ОткрытьСправку(НавигационнаяСсылка);
Возврат;
КонецЕсли;
КонецПроцедуры`

for i := 0; i < b.N; i++ {
for _, _, err := tok.next(); err != nil; _, _, err = tok.next() {

}
}
})
}

func IsDigitRegExp(str string) bool {
Expand Down
Loading

0 comments on commit f03f578

Please sign in to comment.