Skip to content

Commit

Permalink
поддержка перейти + нейминг структур зарезервированными словами
Browse files Browse the repository at this point in the history
  • Loading branch information
LazarenkoA committed Feb 11, 2024
1 parent 7e91598 commit f8c9adb
Show file tree
Hide file tree
Showing 9 changed files with 500 additions and 354 deletions.
26 changes: 26 additions & 0 deletions ast/ast_print.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,9 @@ func (p *astPrint) printBodyItem(item Statement, depth int) (result string) {
case MethodStatement:
builder.WriteString(p.printVarStatement(v))
builder.WriteString(";")
case GoToStatement, *GoToLabelStatement:
builder.WriteString(p.printGoTo(v, depth))
builder.WriteString("\n")
}

return
Expand Down Expand Up @@ -363,3 +366,26 @@ func (p *astPrint) printTryStatement(try TryStatement, depth int) (result string

return
}

func (p *astPrint) printGoTo(gotoStat Statement, depth int) (result string) {
builder := strings.Builder{}
defer func() { result = builder.String() }()

// spaces := strings.Repeat(" ", p.conf.Margin*depth)

switch v := gotoStat.(type) {
case *GoToLabelStatement:
// builder.WriteString(spaces)
builder.WriteString("~")
builder.WriteString(v.Name)
builder.WriteString(":")
case GoToStatement:
// builder.WriteString(spaces)
builder.WriteString("Перейти ")
builder.WriteString("~")
builder.WriteString(v.Label.Name)
builder.WriteString(";")
}

return
}
8 changes: 8 additions & 0 deletions ast/ast_struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,14 @@ type ItemStatement struct {
Object Statement
}

type GoToStatement struct {
Label *GoToLabelStatement
}

type GoToLabelStatement struct {
Name string
}

func (p *ParamStatement) Fill(valueParam *Token, identifier Token) *ParamStatement {
p.IsValue = valueParam != nil
p.Name = identifier.literal
Expand Down
12 changes: 7 additions & 5 deletions ast/ast_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1340,10 +1340,14 @@ func TestParseBaseExpression(t *testing.T) {
func TestParseAST(t *testing.T) {
code := `Процедура ОткрытьНавигационнуюСсылку(НавигационнаяСсылка, Знач Оповещение = Неопределено) Экспорт
стр = новый Структура("Цикл", 1);
стр.Цикл = 0;
Если КодСимвола < 1040 ИЛИ КодСимвола > 1103 И КодыДопустимыхСимволов.Найти(КодСимвола) = Неопределено И Не (Не УчитыватьРазделителиСлов И ЭтоРазделительСлов(КодСимвола)) Тогда
Возврат ;
КонецЕсли;
перейти ~метка;
МассивСтроки.Добавить(Новый ФорматированнаяСтрока(ЧастьСтроки.Значение, Новый Шрифт(,,Истина)));
Expand All @@ -1361,6 +1365,7 @@ func TestParseAST(t *testing.T) {
Позиция = Найти(Строка, Разделитель);
КонецЦикла;
~метка:
вуцуцу = Дата('00010101');
Expand Down Expand Up @@ -1414,9 +1419,6 @@ func TestParseAST(t *testing.T) {
p := a.Print(&PrintConf{Margin: 4})
// fmt.Println(p)
assert.Equal(t, true, compareHashes(code, p))

// sIf := a.ModuleStatement.Body[0].(*FunctionOrProcedure).Body[0]
// fmt.Println(a.PrintStatement(sIf, &PrintConf{Margin: 4}))
}

func TestBigProcedure(t *testing.T) {
Expand All @@ -1433,8 +1435,8 @@ func TestBigProcedure(t *testing.T) {
fmt.Println("milliseconds -", time.Since(s).Milliseconds())
assert.NoError(t, err)

p := a.Print(&PrintConf{Margin: 4})
fmt.Println(p)
// p := a.Print(&PrintConf{Margin: 4})
// fmt.Println(p)
}

func TestTernaryOperator(t *testing.T) {
Expand Down
35 changes: 29 additions & 6 deletions ast/grammar.y
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ package ast
%type<identifiers> identifiers
%type<stmt_tryCatch> stmt_tryCatch
%type<identifier> identifier
%type<goToLabel> goToLabel
%type<token> separator
%type<token> semicolon
%type<token> colon
%type<token> ':'
%type<token> ';'


%union {
Expand Down Expand Up @@ -62,10 +68,13 @@ package ast
opt_explicit_variables map[string]VarStatement
identifiers []Token
identifier Statement
goToLabel *GoToLabelStatement
opt_goToLabel *GoToLabelStatement

}

%token<token> Directive Identifier Procedure Var EndProcedure If Then ElseIf Else EndIf For Each In To Loop EndLoop Break Not ValueParam While
%token<token> Continue Try Catch EndTry Number String New Function EndFunction Return Throw NeEq Le Ge Or And True False Undefind Export Date
%token<token> Directive Identifier Procedure Var EndProcedure If Then ElseIf Else EndIf For Each In To Loop EndLoop Break Not ValueParam While GoToLabel
%token<token> Continue Try Catch EndTry Number String New Function EndFunction Return Throw NeEq Le Ge Or And True False Undefind Export Date GoTo


//%right '='
Expand Down Expand Up @@ -125,17 +134,26 @@ opt_body: { $$ = nil }
;
body: stmt { $$ = []Statement{$1} }
| body semicolon opt_stmt {
| body separator opt_stmt {
if $2.literal == ":" && len($1) > 0 {
if _, ok := $1[len($1)-1].(*GoToLabelStatement); !ok {
yylex.Error("semicolon (;) is expected")
}
}
if $3 != nil {
$$ = append($$, $3)
}
}

;

opt_stmt: { $$ = nil }
| stmt { $$ = $1 }
;

separator: semicolon { $$ = $1} | colon { $$ = $1};


/* переменные */
opt_explicit_variables: { $$ = map[string]VarStatement{} }
| explicit_variables { $$ = $1 }
Expand Down Expand Up @@ -264,6 +282,7 @@ expr : simple_expr { $$ = $1 }
| expr Ge expr { $$ = &ExpStatement{Operation: OpGe, Left: $1, Right: $3 } }
| Not expr { $$ = not($2) }
| new_object { $$ = $1 }
| GoTo goToLabel { $$ = GoToStatement{ Label: $2 } }
;
opt_expr: { $$ = nil } | expr { $$ = $1 };
Expand All @@ -290,6 +309,7 @@ simple_expr: String { $$ = $1.value }
| False { $$ = $1.value }
| Date { $$ = $1.value }
| Undefind { $$ = UndefinedStatement{} }
| goToLabel { $$ = $1}
| through_dot {
if tok, ok := $1.(Token); ok {
$$ = tok.literal
Expand All @@ -300,6 +320,8 @@ simple_expr: String { $$ = $1.value }
| ternary { $$ = $1 } // тернарный оператор
;
goToLabel: GoToLabel { $$ = &GoToLabelStatement{ Name: $1.literal } }
exprs : opt_expr {$$ = []Statement{$1} }
| exprs comma opt_expr { $$ = append($$, $3); }
;
Expand All @@ -308,8 +330,9 @@ identifiers: Identifier { $$ = []Token{$1} }
| identifiers comma Identifier {$$ = append($$, $3) }
;
semicolon: ';'
comma: ','
dot: '.'
semicolon: ';' {$$ = $1};
colon: ':'{$$ = $1};
comma: ',';
dot: '.';
%%
26 changes: 23 additions & 3 deletions ast/tokens.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type Token struct {
position Position
literal string
value interface{}
prevDot bool
}

const (
Expand All @@ -36,6 +37,7 @@ var (
tokens = map[string]int{
"процедура": Procedure,
"перем": Var,
"перейти": GoTo,
"конецпроцедуры": EndProcedure,
"знач": ValueParam,
"если": If,
Expand Down Expand Up @@ -90,7 +92,6 @@ func (t *Token) Next(srs string) (token int, err error) {
switch token {
case Number:
t.value, err = strconv.ParseFloat(t.literal, 64)
// t.value, _ = strconv.ParseInt(t.literal, 10, 64)
case String:
t.value = t.literal
case Date:
Expand All @@ -111,6 +112,10 @@ func (t *Token) next() (int, string, error) {
t.skipComment()
t.skipRegions()

if t.prevDot {
defer func() { t.prevDot = false }()
}

switch let := t.currentLet(); {
case isLetter(let):
literal, err := t.scanIdentifier()
Expand All @@ -119,7 +124,7 @@ func (t *Token) next() (int, string, error) {
}

lowLit := fastToLower(literal)
if tName, ok := tokens[lowLit]; ok {
if tName, ok := tokens[lowLit]; ok && !t.prevDot {
return tName, literal, nil
} else {
return Identifier, literal, nil
Expand Down Expand Up @@ -148,7 +153,7 @@ func (t *Token) next() (int, string, error) {
}

return Date, literal, nil
case let == '=' || let == '-' || let == '+' || let == '*' || let == '/' || let == '(' || let == '?' || let == ')' || let == '[' || let == ']' || let == ':' || let == ';' || let == '.' || let == ',' || let == '%':
case let == '=' || let == '-' || let == '+' || let == '*' || let == '/' || let == '(' || let == '?' || let == ')' || let == '[' || let == ']' || let == ':' || let == ';' || let == ',' || let == '%':
t.nextPos()
return int(let), string(let), nil
case let == '<':
Expand Down Expand Up @@ -179,6 +184,14 @@ func (t *Token) next() (int, string, error) {
// if err != nil {
// return EOF, emptyLit, err
// }
case let == '.':
// если после точки у нас следует идентификатор то нам нужно читать его обычным идентификатором
// Могут быть таие случаи стр.Истина = 1 или стр.Функция = 2 (стр в данном случае какой-то объект, например структура)
// нам нужно что бы то что следует после точки считалось Identifier, а не определенным зарезервированным токеном
t.prevDot = true

t.nextPos()
return int(let), string(let), nil
case let == '&':
t.nextPos()
pos := t.offset
Expand All @@ -195,6 +208,13 @@ func (t *Token) next() (int, string, error) {
t.offset = pos
return int(let), string(let), fmt.Errorf(`syntax error %q`, string(let))
}
case let == '~':
t.nextPos()
literal, err := t.scanIdentifier()
if err != nil {
return EOF, emptyLit, err
}
return GoToLabel, literal, err
default:
switch let {
case EOF:
Expand Down
24 changes: 24 additions & 0 deletions ast/tokens_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,30 @@ func Test_Next(t *testing.T) {
assert.Equal(t, token, result[tok.literal])
}
})

t.Run("goto", func(t *testing.T) {
tok := new(Token)
code := `Процедура ДобавитьРегистрНаСервере()
перейти ~метка;
~метка:
КонецПроцедуры`

result := map[string]int{
"Процедура": Procedure,
"ДобавитьРегистрНаСервере": Identifier,
"КонецПроцедуры": EndProcedure,
"(": '(',
")": ')',
"перейти": GoTo,
"метка": GoToLabel,
";": ';',
":": ':',
}

for token, err := tok.Next(code); err == nil && token > 0; token, err = tok.Next(code) {
assert.Equal(t, token, result[tok.literal])
}
})
}

func Benchmark(b *testing.B) {
Expand Down
Loading

0 comments on commit f8c9adb

Please sign in to comment.