%{ package parse import ( "github.com/yuin/gopher-lua/ast" ) %} %type chunk %type chunk1 %type block %type stat %type elseifs %type laststat %type funcname %type funcname1 %type varlist %type var %type namelist %type exprlist %type expr %type string %type prefixexp %type functioncall %type afunctioncall %type args %type function %type funcbody %type parlist %type tableconstructor %type fieldlist %type field %type fieldsep %union { token ast.Token stmts []ast.Stmt stmt ast.Stmt funcname *ast.FuncName funcexpr *ast.FunctionExpr exprlist []ast.Expr expr ast.Expr fieldlist []*ast.Field field *ast.Field fieldsep string namelist []string parlist *ast.ParList } /* Reserved words */ %token TAnd TBreak TDo TElse TElseIf TEnd TFalse TFor TFunction TIf TIn TLocal TNil TNot TOr TReturn TRepeat TThen TTrue TUntil TWhile /* Literals */ %token TEqeq TNeq TLte TGte T2Comma T3Comma TIdent TNumber TString '{' '(' /* Operators */ %left TOr %left TAnd %left '>' '<' TGte TLte TEqeq TNeq %right T2Comma %left '+' '-' %left '*' '/' '%' %right UNARY /* not # -(unary) */ %right '^' %% chunk: chunk1 { $$ = $1 if l, ok := yylex.(*Lexer); ok { l.Stmts = $$ } } | chunk1 laststat { $$ = append($1, $2) if l, ok := yylex.(*Lexer); ok { l.Stmts = $$ } } | chunk1 laststat ';' { $$ = append($1, $2) if l, ok := yylex.(*Lexer); ok { l.Stmts = $$ } } chunk1: { $$ = []ast.Stmt{} } | chunk1 stat { $$ = append($1, $2) } | chunk1 ';' { $$ = $1 } block: chunk { $$ = $1 } stat: varlist '=' exprlist { $$ = &ast.AssignStmt{Lhs: $1, Rhs: $3} $$.SetLine($1[0].Line()) } | /* 'stat = functioncal' causes a reduce/reduce conflict */ prefixexp { if _, ok := $1.(*ast.FuncCallExpr); !ok { yylex.(*Lexer).Error("parse error") } else { $$ = &ast.FuncCallStmt{Expr: $1} $$.SetLine($1.Line()) } } | TDo block TEnd { $$ = &ast.DoBlockStmt{Stmts: $2} $$.SetLine($1.Pos.Line) $$.SetLastLine($3.Pos.Line) } | TWhile expr TDo block TEnd { $$ = &ast.WhileStmt{Condition: $2, Stmts: $4} $$.SetLine($1.Pos.Line) $$.SetLastLine($5.Pos.Line) } | TRepeat block TUntil expr { $$ = &ast.RepeatStmt{Condition: $4, Stmts: $2} $$.SetLine($1.Pos.Line) $$.SetLastLine($4.Line()) } | TIf expr TThen block elseifs TEnd { $$ = &ast.IfStmt{Condition: $2, Then: $4} cur := $$ for _, elseif := range $5 { cur.(*ast.IfStmt).Else = []ast.Stmt{elseif} cur = elseif } $$.SetLine($1.Pos.Line) $$.SetLastLine($6.Pos.Line) } | TIf expr TThen block elseifs TElse block TEnd { $$ = &ast.IfStmt{Condition: $2, Then: $4} cur := $$ for _, elseif := range $5 { cur.(*ast.IfStmt).Else = []ast.Stmt{elseif} cur = elseif } cur.(*ast.IfStmt).Else = $7 $$.SetLine($1.Pos.Line) $$.SetLastLine($8.Pos.Line) } | TFor TIdent '=' expr ',' expr TDo block TEnd { $$ = &ast.NumberForStmt{Name: $2.Str, Init: $4, Limit: $6, Stmts: $8} $$.SetLine($1.Pos.Line) $$.SetLastLine($9.Pos.Line) } | TFor TIdent '=' expr ',' expr ',' expr TDo block TEnd { $$ = &ast.NumberForStmt{Name: $2.Str, Init: $4, Limit: $6, Step:$8, Stmts: $10} $$.SetLine($1.Pos.Line) $$.SetLastLine($11.Pos.Line) } | TFor namelist TIn exprlist TDo block TEnd { $$ = &ast.GenericForStmt{Names:$2, Exprs:$4, Stmts: $6} $$.SetLine($1.Pos.Line) $$.SetLastLine($7.Pos.Line) } | TFunction funcname funcbody { $$ = &ast.FuncDefStmt{Name: $2, Func: $3} $$.SetLine($1.Pos.Line) $$.SetLastLine($3.LastLine()) } | TLocal TFunction TIdent funcbody { $$ = &ast.LocalAssignStmt{Names:[]string{$3.Str}, Exprs: []ast.Expr{$4}} $$.SetLine($1.Pos.Line) $$.SetLastLine($4.LastLine()) } | TLocal namelist '=' exprlist { $$ = &ast.LocalAssignStmt{Names: $2, Exprs:$4} $$.SetLine($1.Pos.Line) } | TLocal namelist { $$ = &ast.LocalAssignStmt{Names: $2, Exprs:[]ast.Expr{}} $$.SetLine($1.Pos.Line) } elseifs: { $$ = []ast.Stmt{} } | elseifs TElseIf expr TThen block { $$ = append($1, &ast.IfStmt{Condition: $3, Then: $5}) $$[len($$)-1].SetLine($2.Pos.Line) } laststat: TReturn { $$ = &ast.ReturnStmt{Exprs:nil} $$.SetLine($1.Pos.Line) } | TReturn exprlist { $$ = &ast.ReturnStmt{Exprs:$2} $$.SetLine($1.Pos.Line) } | TBreak { $$ = &ast.BreakStmt{} $$.SetLine($1.Pos.Line) } funcname: funcname1 { $$ = $1 } | funcname1 ':' TIdent { $$ = &ast.FuncName{Func:nil, Receiver:$1.Func, Method: $3.Str} } funcname1: TIdent { $$ = &ast.FuncName{Func: &ast.IdentExpr{Value:$1.Str}} $$.Func.SetLine($1.Pos.Line) } | funcname1 '.' TIdent { key:= &ast.StringExpr{Value:$3.Str} key.SetLine($3.Pos.Line) fn := &ast.AttrGetExpr{Object: $1.Func, Key: key} fn.SetLine($3.Pos.Line) $$ = &ast.FuncName{Func: fn} } varlist: var { $$ = []ast.Expr{$1} } | varlist ',' var { $$ = append($1, $3) } var: TIdent { $$ = &ast.IdentExpr{Value:$1.Str} $$.SetLine($1.Pos.Line) } | prefixexp '[' expr ']' { $$ = &ast.AttrGetExpr{Object: $1, Key: $3} $$.SetLine($1.Line()) } | prefixexp '.' TIdent { key := &ast.StringExpr{Value:$3.Str} key.SetLine($3.Pos.Line) $$ = &ast.AttrGetExpr{Object: $1, Key: key} $$.SetLine($1.Line()) } namelist: TIdent { $$ = []string{$1.Str} } | namelist ',' TIdent { $$ = append($1, $3.Str) } exprlist: expr { $$ = []ast.Expr{$1} } | exprlist ',' expr { $$ = append($1, $3) } expr: TNil { $$ = &ast.NilExpr{} $$.SetLine($1.Pos.Line) } | TFalse { $$ = &ast.FalseExpr{} $$.SetLine($1.Pos.Line) } | TTrue { $$ = &ast.TrueExpr{} $$.SetLine($1.Pos.Line) } | TNumber { $$ = &ast.NumberExpr{Value: $1.Str} $$.SetLine($1.Pos.Line) } | T3Comma { $$ = &ast.Comma3Expr{} $$.SetLine($1.Pos.Line) } | function { $$ = $1 } | prefixexp { $$ = $1 } | string { $$ = $1 } | tableconstructor { $$ = $1 } | expr TOr expr { $$ = &ast.LogicalOpExpr{Lhs: $1, Operator: "or", Rhs: $3} $$.SetLine($1.Line()) } | expr TAnd expr { $$ = &ast.LogicalOpExpr{Lhs: $1, Operator: "and", Rhs: $3} $$.SetLine($1.Line()) } | expr '>' expr { $$ = &ast.RelationalOpExpr{Lhs: $1, Operator: ">", Rhs: $3} $$.SetLine($1.Line()) } | expr '<' expr { $$ = &ast.RelationalOpExpr{Lhs: $1, Operator: "<", Rhs: $3} $$.SetLine($1.Line()) } | expr TGte expr { $$ = &ast.RelationalOpExpr{Lhs: $1, Operator: ">=", Rhs: $3} $$.SetLine($1.Line()) } | expr TLte expr { $$ = &ast.RelationalOpExpr{Lhs: $1, Operator: "<=", Rhs: $3} $$.SetLine($1.Line()) } | expr TEqeq expr { $$ = &ast.RelationalOpExpr{Lhs: $1, Operator: "==", Rhs: $3} $$.SetLine($1.Line()) } | expr TNeq expr { $$ = &ast.RelationalOpExpr{Lhs: $1, Operator: "~=", Rhs: $3} $$.SetLine($1.Line()) } | expr T2Comma expr { $$ = &ast.StringConcatOpExpr{Lhs: $1, Rhs: $3} $$.SetLine($1.Line()) } | expr '+' expr { $$ = &ast.ArithmeticOpExpr{Lhs: $1, Operator: "+", Rhs: $3} $$.SetLine($1.Line()) } | expr '-' expr { $$ = &ast.ArithmeticOpExpr{Lhs: $1, Operator: "-", Rhs: $3} $$.SetLine($1.Line()) } | expr '*' expr { $$ = &ast.ArithmeticOpExpr{Lhs: $1, Operator: "*", Rhs: $3} $$.SetLine($1.Line()) } | expr '/' expr { $$ = &ast.ArithmeticOpExpr{Lhs: $1, Operator: "/", Rhs: $3} $$.SetLine($1.Line()) } | expr '%' expr { $$ = &ast.ArithmeticOpExpr{Lhs: $1, Operator: "%", Rhs: $3} $$.SetLine($1.Line()) } | expr '^' expr { $$ = &ast.ArithmeticOpExpr{Lhs: $1, Operator: "^", Rhs: $3} $$.SetLine($1.Line()) } | '-' expr %prec UNARY { $$ = &ast.UnaryMinusOpExpr{Expr: $2} $$.SetLine($2.Line()) } | TNot expr %prec UNARY { $$ = &ast.UnaryNotOpExpr{Expr: $2} $$.SetLine($2.Line()) } | '#' expr %prec UNARY { $$ = &ast.UnaryLenOpExpr{Expr: $2} $$.SetLine($2.Line()) } string: TString { $$ = &ast.StringExpr{Value: $1.Str} $$.SetLine($1.Pos.Line) } prefixexp: var { $$ = $1 } | afunctioncall { $$ = $1 } | functioncall { $$ = $1 } | '(' expr ')' { $$ = $2 $$.SetLine($1.Pos.Line) } afunctioncall: '(' functioncall ')' { $2.(*ast.FuncCallExpr).AdjustRet = true $$ = $2 } functioncall: prefixexp args { $$ = &ast.FuncCallExpr{Func: $1, Args: $2} $$.SetLine($1.Line()) } | prefixexp ':' TIdent args { $$ = &ast.FuncCallExpr{Method: $3.Str, Receiver: $1, Args: $4} $$.SetLine($1.Line()) } args: '(' ')' { if yylex.(*Lexer).PNewLine { yylex.(*Lexer).TokenError($1, "ambiguous syntax (function call x new statement)") } $$ = []ast.Expr{} } | '(' exprlist ')' { if yylex.(*Lexer).PNewLine { yylex.(*Lexer).TokenError($1, "ambiguous syntax (function call x new statement)") } $$ = $2 } | tableconstructor { $$ = []ast.Expr{$1} } | string { $$ = []ast.Expr{$1} } function: TFunction funcbody { $$ = &ast.FunctionExpr{ParList:$2.ParList, Stmts: $2.Stmts} $$.SetLine($1.Pos.Line) $$.SetLastLine($2.LastLine()) } funcbody: '(' parlist ')' block TEnd { $$ = &ast.FunctionExpr{ParList: $2, Stmts: $4} $$.SetLine($1.Pos.Line) $$.SetLastLine($5.Pos.Line) } | '(' ')' block TEnd { $$ = &ast.FunctionExpr{ParList: &ast.ParList{HasVargs: false, Names: []string{}}, Stmts: $3} $$.SetLine($1.Pos.Line) $$.SetLastLine($4.Pos.Line) } parlist: T3Comma { $$ = &ast.ParList{HasVargs: true, Names: []string{}} } | namelist { $$ = &ast.ParList{HasVargs: false, Names: []string{}} $$.Names = append($$.Names, $1...) } | namelist ',' T3Comma { $$ = &ast.ParList{HasVargs: true, Names: []string{}} $$.Names = append($$.Names, $1...) } tableconstructor: '{' '}' { $$ = &ast.TableExpr{Fields: []*ast.Field{}} $$.SetLine($1.Pos.Line) } | '{' fieldlist '}' { $$ = &ast.TableExpr{Fields: $2} $$.SetLine($1.Pos.Line) } fieldlist: field { $$ = []*ast.Field{$1} } | fieldlist fieldsep field { $$ = append($1, $3) } | fieldlist fieldsep { $$ = $1 } field: TIdent '=' expr { $$ = &ast.Field{Key: &ast.StringExpr{Value:$1.Str}, Value: $3} $$.Key.SetLine($1.Pos.Line) } | '[' expr ']' '=' expr { $$ = &ast.Field{Key: $2, Value: $5} } | expr { $$ = &ast.Field{Value: $1} } fieldsep: ',' { $$ = "," } | ';' { $$ = ";" } %% func TokenName(c int) string { if c >= TAnd && c-TAnd < len(yyToknames) { if yyToknames[c-TAnd] != "" { return yyToknames[c-TAnd] } } return string([]byte{byte(c)}) }