1 %{
2 package parse
3 
4 import (
5   "github.com/yuin/gopher-lua/ast"
6 )
7 %}
8 %type<stmts> chunk
9 %type<stmts> chunk1
10 %type<stmts> block
11 %type<stmt>  stat
12 %type<stmts> elseifs
13 %type<stmt>  laststat
14 %type<funcname> funcname
15 %type<funcname> funcname1
16 %type<exprlist> varlist
17 %type<expr> var
18 %type<namelist> namelist
19 %type<exprlist> exprlist
20 %type<expr> expr
21 %type<expr> string
22 %type<expr> prefixexp
23 %type<expr> functioncall
24 %type<expr> afunctioncall
25 %type<exprlist> args
26 %type<expr> function
27 %type<funcexpr> funcbody
28 %type<parlist> parlist
29 %type<expr> tableconstructor
30 %type<fieldlist> fieldlist
31 %type<field> field
32 %type<fieldsep> fieldsep
33 
34 %union {
35   token  ast.Token
36 
37   stmts    []ast.Stmt
38   stmt     ast.Stmt
39 
40   funcname *ast.FuncName
41   funcexpr *ast.FunctionExpr
42 
43   exprlist []ast.Expr
44   expr   ast.Expr
45 
46   fieldlist []*ast.Field
47   field     *ast.Field
48   fieldsep  string
49 
50   namelist []string
51   parlist  *ast.ParList
52 }
53 
54 /* Reserved words */
55 %token<token> TAnd TBreak TDo TElse TElseIf TEnd TFalse TFor TFunction TIf TIn TLocal TNil TNot TOr TReturn TRepeat TThen TTrue TUntil TWhile
56 
57 /* Literals */
58 %token<token> TEqeq TNeq TLte TGte T2Comma T3Comma TIdent TNumber TString '{' '('
59 
60 /* Operators */
61 %left TOr
62 %left TAnd
63 %left '>' '<' TGte TLte TEqeq TNeq
64 %right T2Comma
65 %left '+' '-'
66 %left '*' '/' '%'
67 %right UNARY /* not # -(unary) */
68 %right '^'
69 
70 %%
71 
72 chunk:
73         chunk1 {
74             $$ = $1
75             if l, ok := yylex.(*Lexer); ok {
76                 l.Stmts = $$
77             }
78         } |
79         chunk1 laststat {
80             $$ = append($1, $2)
81             if l, ok := yylex.(*Lexer); ok {
82                 l.Stmts = $$
83             }
84         } |
85         chunk1 laststat ';' {
86             $$ = append($1, $2)
87             if l, ok := yylex.(*Lexer); ok {
88                 l.Stmts = $$
89             }
90         }
91 
92 chunk1:
93         {
94             $$ = []ast.Stmt{}
95         } |
96         chunk1 stat {
97             $$ = append($1, $2)
98         } |
99         chunk1 ';' {
100             $$ = $1
101         }
102 
103 block:
104         chunk {
105             $$ = $1
106         }
107 
108 stat:
109         varlist '=' exprlist {
110             $$ = &ast.AssignStmt{Lhs: $1, Rhs: $3}
111             $$.SetLine($1[0].Line())
112         } |
113         /* 'stat = functioncal' causes a reduce/reduce conflict */
114         prefixexp {
115             if _, ok := $1.(*ast.FuncCallExpr); !ok {
116                yylex.(*Lexer).Error("parse error")
117             } else {
118               $$ = &ast.FuncCallStmt{Expr: $1}
119               $$.SetLine($1.Line())
120             }
121         } |
122         TDo block TEnd {
123             $$ = &ast.DoBlockStmt{Stmts: $2}
124             $$.SetLine($1.Pos.Line)
125             $$.SetLastLine($3.Pos.Line)
126         } |
127         TWhile expr TDo block TEnd {
128             $$ = &ast.WhileStmt{Condition: $2, Stmts: $4}
129             $$.SetLine($1.Pos.Line)
130             $$.SetLastLine($5.Pos.Line)
131         } |
132         TRepeat block TUntil expr {
133             $$ = &ast.RepeatStmt{Condition: $4, Stmts: $2}
134             $$.SetLine($1.Pos.Line)
135             $$.SetLastLine($4.Line())
136         } |
137         TIf expr TThen block elseifs TEnd {
138             $$ = &ast.IfStmt{Condition: $2, Then: $4}
139             cur := $$
140             for _, elseif := range $5 {
141                 cur.(*ast.IfStmt).Else = []ast.Stmt{elseif}
142                 cur = elseif
143             }
144             $$.SetLine($1.Pos.Line)
145             $$.SetLastLine($6.Pos.Line)
146         } |
147         TIf expr TThen block elseifs TElse block TEnd {
148             $$ = &ast.IfStmt{Condition: $2, Then: $4}
149             cur := $$
150             for _, elseif := range $5 {
151                 cur.(*ast.IfStmt).Else = []ast.Stmt{elseif}
152                 cur = elseif
153             }
154             cur.(*ast.IfStmt).Else = $7
155             $$.SetLine($1.Pos.Line)
156             $$.SetLastLine($8.Pos.Line)
157         } |
158         TFor TIdent '=' expr ',' expr TDo block TEnd {
159             $$ = &ast.NumberForStmt{Name: $2.Str, Init: $4, Limit: $6, Stmts: $8}
160             $$.SetLine($1.Pos.Line)
161             $$.SetLastLine($9.Pos.Line)
162         } |
163         TFor TIdent '=' expr ',' expr ',' expr TDo block TEnd {
164             $$ = &ast.NumberForStmt{Name: $2.Str, Init: $4, Limit: $6, Step:$8, Stmts: $10}
165             $$.SetLine($1.Pos.Line)
166             $$.SetLastLine($11.Pos.Line)
167         } |
168         TFor namelist TIn exprlist TDo block TEnd {
169             $$ = &ast.GenericForStmt{Names:$2, Exprs:$4, Stmts: $6}
170             $$.SetLine($1.Pos.Line)
171             $$.SetLastLine($7.Pos.Line)
172         } |
173         TFunction funcname funcbody {
174             $$ = &ast.FuncDefStmt{Name: $2, Func: $3}
175             $$.SetLine($1.Pos.Line)
176             $$.SetLastLine($3.LastLine())
177         } |
178         TLocal TFunction TIdent funcbody {
179             $$ = &ast.LocalAssignStmt{Names:[]string{$3.Str}, Exprs: []ast.Expr{$4}}
180             $$.SetLine($1.Pos.Line)
181             $$.SetLastLine($4.LastLine())
182         } |
183         TLocal namelist '=' exprlist {
184             $$ = &ast.LocalAssignStmt{Names: $2, Exprs:$4}
185             $$.SetLine($1.Pos.Line)
186         } |
187         TLocal namelist {
188             $$ = &ast.LocalAssignStmt{Names: $2, Exprs:[]ast.Expr{}}
189             $$.SetLine($1.Pos.Line)
190         }
191 
192 elseifs:
193         {
194             $$ = []ast.Stmt{}
195         } |
196         elseifs TElseIf expr TThen block {
197             $$ = append($1, &ast.IfStmt{Condition: $3, Then: $5})
198             $$[len($$)-1].SetLine($2.Pos.Line)
199         }
200 
201 laststat:
202         TReturn {
203             $$ = &ast.ReturnStmt{Exprs:nil}
204             $$.SetLine($1.Pos.Line)
205         } |
206         TReturn exprlist {
207             $$ = &ast.ReturnStmt{Exprs:$2}
208             $$.SetLine($1.Pos.Line)
209         } |
210         TBreak  {
211             $$ = &ast.BreakStmt{}
212             $$.SetLine($1.Pos.Line)
213         }
214 
215 funcname:
216         funcname1 {
217             $$ = $1
218         } |
219         funcname1 ':' TIdent {
220             $$ = &ast.FuncName{Func:nil, Receiver:$1.Func, Method: $3.Str}
221         }
222 
223 funcname1:
224         TIdent {
225             $$ = &ast.FuncName{Func: &ast.IdentExpr{Value:$1.Str}}
226             $$.Func.SetLine($1.Pos.Line)
227         } |
228         funcname1 '.' TIdent {
229             key:= &ast.StringExpr{Value:$3.Str}
230             key.SetLine($3.Pos.Line)
231             fn := &ast.AttrGetExpr{Object: $1.Func, Key: key}
232             fn.SetLine($3.Pos.Line)
233             $$ = &ast.FuncName{Func: fn}
234         }
235 
236 varlist:
237         var {
238             $$ = []ast.Expr{$1}
239         } |
240         varlist ',' var {
241             $$ = append($1, $3)
242         }
243 
244 var:
245         TIdent {
246             $$ = &ast.IdentExpr{Value:$1.Str}
247             $$.SetLine($1.Pos.Line)
248         } |
249         prefixexp '[' expr ']' {
250             $$ = &ast.AttrGetExpr{Object: $1, Key: $3}
251             $$.SetLine($1.Line())
252         } |
253         prefixexp '.' TIdent {
254             key := &ast.StringExpr{Value:$3.Str}
255             key.SetLine($3.Pos.Line)
256             $$ = &ast.AttrGetExpr{Object: $1, Key: key}
257             $$.SetLine($1.Line())
258         }
259 
260 namelist:
261         TIdent {
262             $$ = []string{$1.Str}
263         } |
264         namelist ','  TIdent {
265             $$ = append($1, $3.Str)
266         }
267 
268 exprlist:
269         expr {
270             $$ = []ast.Expr{$1}
271         } |
272         exprlist ',' expr {
273             $$ = append($1, $3)
274         }
275 
276 expr:
277         TNil {
278             $$ = &ast.NilExpr{}
279             $$.SetLine($1.Pos.Line)
280         } |
281         TFalse {
282             $$ = &ast.FalseExpr{}
283             $$.SetLine($1.Pos.Line)
284         } |
285         TTrue {
286             $$ = &ast.TrueExpr{}
287             $$.SetLine($1.Pos.Line)
288         } |
289         TNumber {
290             $$ = &ast.NumberExpr{Value: $1.Str}
291             $$.SetLine($1.Pos.Line)
292         } |
293         T3Comma {
294             $$ = &ast.Comma3Expr{}
295             $$.SetLine($1.Pos.Line)
296         } |
297         function {
298             $$ = $1
299         } |
300         prefixexp {
301             $$ = $1
302         } |
303         string {
304             $$ = $1
305         } |
306         tableconstructor {
307             $$ = $1
308         } |
309         expr TOr expr {
310             $$ = &ast.LogicalOpExpr{Lhs: $1, Operator: "or", Rhs: $3}
311             $$.SetLine($1.Line())
312         } |
313         expr TAnd expr {
314             $$ = &ast.LogicalOpExpr{Lhs: $1, Operator: "and", Rhs: $3}
315             $$.SetLine($1.Line())
316         } |
317         expr '>' expr {
318             $$ = &ast.RelationalOpExpr{Lhs: $1, Operator: ">", Rhs: $3}
319             $$.SetLine($1.Line())
320         } |
321         expr '<' expr {
322             $$ = &ast.RelationalOpExpr{Lhs: $1, Operator: "<", Rhs: $3}
323             $$.SetLine($1.Line())
324         } |
325         expr TGte expr {
326             $$ = &ast.RelationalOpExpr{Lhs: $1, Operator: ">=", Rhs: $3}
327             $$.SetLine($1.Line())
328         } |
329         expr TLte expr {
330             $$ = &ast.RelationalOpExpr{Lhs: $1, Operator: "<=", Rhs: $3}
331             $$.SetLine($1.Line())
332         } |
333         expr TEqeq expr {
334             $$ = &ast.RelationalOpExpr{Lhs: $1, Operator: "==", Rhs: $3}
335             $$.SetLine($1.Line())
336         } |
337         expr TNeq expr {
338             $$ = &ast.RelationalOpExpr{Lhs: $1, Operator: "~=", Rhs: $3}
339             $$.SetLine($1.Line())
340         } |
341         expr T2Comma expr {
342             $$ = &ast.StringConcatOpExpr{Lhs: $1, Rhs: $3}
343             $$.SetLine($1.Line())
344         } |
345         expr '+' expr {
346             $$ = &ast.ArithmeticOpExpr{Lhs: $1, Operator: "+", Rhs: $3}
347             $$.SetLine($1.Line())
348         } |
349         expr '-' expr {
350             $$ = &ast.ArithmeticOpExpr{Lhs: $1, Operator: "-", Rhs: $3}
351             $$.SetLine($1.Line())
352         } |
353         expr '*' expr {
354             $$ = &ast.ArithmeticOpExpr{Lhs: $1, Operator: "*", Rhs: $3}
355             $$.SetLine($1.Line())
356         } |
357         expr '/' expr {
358             $$ = &ast.ArithmeticOpExpr{Lhs: $1, Operator: "/", Rhs: $3}
359             $$.SetLine($1.Line())
360         } |
361         expr '%' expr {
362             $$ = &ast.ArithmeticOpExpr{Lhs: $1, Operator: "%", Rhs: $3}
363             $$.SetLine($1.Line())
364         } |
365         expr '^' expr {
366             $$ = &ast.ArithmeticOpExpr{Lhs: $1, Operator: "^", Rhs: $3}
367             $$.SetLine($1.Line())
368         } |
369         '-' expr %prec UNARY {
370             $$ = &ast.UnaryMinusOpExpr{Expr: $2}
371             $$.SetLine($2.Line())
372         } |
373         TNot expr %prec UNARY {
374             $$ = &ast.UnaryNotOpExpr{Expr: $2}
375             $$.SetLine($2.Line())
376         } |
377         '#' expr %prec UNARY {
378             $$ = &ast.UnaryLenOpExpr{Expr: $2}
379             $$.SetLine($2.Line())
380         }
381 
382 string:
383         TString {
384             $$ = &ast.StringExpr{Value: $1.Str}
385             $$.SetLine($1.Pos.Line)
386         }
387 
388 prefixexp:
389         var {
390             $$ = $1
391         } |
392         afunctioncall {
393             $$ = $1
394         } |
395         functioncall {
396             $$ = $1
397         } |
398         '(' expr ')' {
399             $$ = $2
400             $$.SetLine($1.Pos.Line)
401         }
402 
403 afunctioncall:
404         '(' functioncall ')' {
405             $2.(*ast.FuncCallExpr).AdjustRet = true
406             $$ = $2
407         }
408 
409 functioncall:
410         prefixexp args {
411             $$ = &ast.FuncCallExpr{Func: $1, Args: $2}
412             $$.SetLine($1.Line())
413         } |
414         prefixexp ':' TIdent args {
415             $$ = &ast.FuncCallExpr{Method: $3.Str, Receiver: $1, Args: $4}
416             $$.SetLine($1.Line())
417         }
418 
419 args:
420         '(' ')' {
421             if yylex.(*Lexer).PNewLine {
422                yylex.(*Lexer).TokenError($1, "ambiguous syntax (function call x new statement)")
423             }
424             $$ = []ast.Expr{}
425         } |
426         '(' exprlist ')' {
427             if yylex.(*Lexer).PNewLine {
428                yylex.(*Lexer).TokenError($1, "ambiguous syntax (function call x new statement)")
429             }
430             $$ = $2
431         } |
432         tableconstructor {
433             $$ = []ast.Expr{$1}
434         } |
435         string {
436             $$ = []ast.Expr{$1}
437         }
438 
439 function:
440         TFunction funcbody {
441             $$ = &ast.FunctionExpr{ParList:$2.ParList, Stmts: $2.Stmts}
442             $$.SetLine($1.Pos.Line)
443             $$.SetLastLine($2.LastLine())
444         }
445 
446 funcbody:
447         '(' parlist ')' block TEnd {
448             $$ = &ast.FunctionExpr{ParList: $2, Stmts: $4}
449             $$.SetLine($1.Pos.Line)
450             $$.SetLastLine($5.Pos.Line)
451         } |
452         '(' ')' block TEnd {
453             $$ = &ast.FunctionExpr{ParList: &ast.ParList{HasVargs: false, Names: []string{}}, Stmts: $3}
454             $$.SetLine($1.Pos.Line)
455             $$.SetLastLine($4.Pos.Line)
456         }
457 
458 parlist:
459         T3Comma {
460             $$ = &ast.ParList{HasVargs: true, Names: []string{}}
461         } |
462         namelist {
463           $$ = &ast.ParList{HasVargs: false, Names: []string{}}
464           $$.Names = append($$.Names, $1...)
465         } |
466         namelist ',' T3Comma {
467           $$ = &ast.ParList{HasVargs: true, Names: []string{}}
468           $$.Names = append($$.Names, $1...)
469         }
470 
471 
472 tableconstructor:
473         '{' '}' {
474             $$ = &ast.TableExpr{Fields: []*ast.Field{}}
475             $$.SetLine($1.Pos.Line)
476         } |
477         '{' fieldlist '}' {
478             $$ = &ast.TableExpr{Fields: $2}
479             $$.SetLine($1.Pos.Line)
480         }
481 
482 
483 fieldlist:
484         field {
485             $$ = []*ast.Field{$1}
486         } |
487         fieldlist fieldsep field {
488             $$ = append($1, $3)
489         } |
490         fieldlist fieldsep {
491             $$ = $1
492         }
493 
494 field:
495         TIdent '=' expr {
496             $$ = &ast.Field{Key: &ast.StringExpr{Value:$1.Str}, Value: $3}
497             $$.Key.SetLine($1.Pos.Line)
498         } |
499         '[' expr ']' '=' expr {
500             $$ = &ast.Field{Key: $2, Value: $5}
501         } |
502         expr {
503             $$ = &ast.Field{Value: $1}
504         }
505 
506 fieldsep:
507         ',' {
508             $$ = ","
509         } |
510         ';' {
511             $$ = ";"
512         }
513 
514 %%
515 
516 func TokenName(c int) string {
517 	if c >= TAnd && c-TAnd < len(yyToknames) {
518 		if yyToknames[c-TAnd] != "" {
519 			return yyToknames[c-TAnd]
520 		}
521 	}
522     return string([]byte{byte(c)})
523 }
524 
525