root/livinglogic.java.ul4/src/main/antlr3/com/livinglogic/ul4/UL4.g @ 686:9c00a7e3b94e

Revision 686:9c00a7e3b94e, 10.5 KB (checked in by Walter Doerwald <walter@…>, 7 years ago)

Variable unpacking in for loops can now be nested arbitrarily deep.

Line 
1grammar UL4;
2
3options
4{
5    language=Java;
6    backtrack=true;
7}
8
9@header
10{
11    package com.livinglogic.ul4;
12
13    import java.util.Date;
14
15    import com.livinglogic.ul4.Utils;
16    import com.livinglogic.ul4.Color;
17    import com.livinglogic.ul4.CallArg;
18}
19
20@lexer::header
21{
22    package com.livinglogic.ul4;
23}
24
25
26@lexer::members
27{
28    private Location location;
29
30    public UL4Lexer(Location location, CharStream input)
31    {
32        super(input);
33        this.location = location;
34    }
35
36    @Override
37    public void displayRecognitionError(String[] tokenNames, RecognitionException e)
38    {
39        String message = getErrorMessage(e, tokenNames) + " (at index " + e.index + ")";
40        throw new SyntaxException(message, e);
41    }
42}
43
44@parser::members
45{
46    private Location location;
47
48    public UL4Parser(Location location, TokenStream input)
49    {
50        super(input);
51        this.location = location;
52    }
53
54    @Override
55    public void displayRecognitionError(String[] tokenNames, RecognitionException e)
56    {
57        String message = getErrorMessage(e, tokenNames) + " (at index " + e.index + ")";
58        throw new SyntaxException(message, e);
59    }
60}
61
62NONE
63    : 'None'
64    ;
65
66TRUE
67    : 'True'
68    ;
69
70FALSE
71    : 'False'
72    ;
73
74NAME
75    : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*
76    ;
77
78fragment
79DIGIT
80    : '0'..'9'
81    ;
82
83fragment
84BIN_DIGIT
85    : ('0'|'1')
86    ;
87
88fragment
89OCT_DIGIT
90    : '0'..'7'
91    ;
92
93fragment
94HEX_DIGIT
95    : ('0'..'9'|'a'..'f'|'A'..'F')
96    ;
97
98/* We don't have negative ints (as this would lex "1-2" wrong) */
99INT
100    : DIGIT+
101    | '0' ('b'|'B') BIN_DIGIT+
102    | '0' ('o'|'O') OCT_DIGIT+
103    | '0' ('x'|'X') HEX_DIGIT+
104    ;
105
106fragment
107EXPONENT
108    : ('e'|'E') ('+'|'-')? DIGIT+
109    ;
110
111FLOAT
112    : DIGIT+ '.' DIGIT* EXPONENT?
113    | '.' DIGIT+ EXPONENT?
114    | DIGIT+ EXPONENT
115    ;
116
117fragment
118TIME
119    : DIGIT DIGIT ':' DIGIT DIGIT ( ':' DIGIT DIGIT ( '.' DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT)?)?;
120
121DATE
122    : '@' '(' DIGIT DIGIT DIGIT DIGIT '-' DIGIT DIGIT '-' DIGIT DIGIT ('T' TIME?)? ')';
123
124COLOR
125    : '#' HEX_DIGIT HEX_DIGIT HEX_DIGIT
126    | '#' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
127    | '#' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
128    | '#' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
129    ;
130
131WS
132    : (' '|'\t'|'\r'|'\n') { $channel=HIDDEN; }
133    ;
134
135STRING
136    : '"' ( ESC_SEQ | ~('\\'|'"') )* '"'
137    | '\'' ( ESC_SEQ | ~('\\'|'\'') )* '\''
138    ;
139
140fragment
141ESC_SEQ
142    : '\\' ('a'|'b'|'e'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')
143    | UNICODE1_ESC
144    | UNICODE2_ESC
145    | UNICODE4_ESC
146    ;
147
148fragment
149UNICODE1_ESC
150    : '\\' 'x' HEX_DIGIT HEX_DIGIT
151    ;
152
153fragment
154UNICODE2_ESC
155    : '\\' 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
156    ;
157
158fragment
159UNICODE4_ESC
160    : '\\' 'U' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
161    ;
162
163
164/* Rules common to all tags */
165
166none returns [AST node]
167    : NONE { $node = new LoadNone(location); }
168    ;
169
170true_ returns [AST node]
171    : TRUE { $node = new LoadTrue(location); }
172    ;
173
174false_ returns [AST node]
175    : FALSE { $node = new LoadFalse(location); }
176    ;
177
178name returns [Var node]
179    : NAME { $node = new Var(location, $NAME.text); }
180    ;
181
182int_ returns [AST node]
183    : INT { $node = new LoadInt(location, Utils.parseUL4Int($INT.text)); }
184    ;
185
186float_ returns [AST node]
187    : FLOAT { $node = new LoadFloat(location, Double.parseDouble($FLOAT.text)); }
188    ;
189
190string returns [AST node]
191    : STRING { $node = new LoadStr(location, Utils.unescapeUL4String($STRING.text.substring(1, $STRING.text.length()-1))); }
192    ;
193
194date returns [AST node]
195    : DATE { $node = new LoadDate(location, Utils.isoparse($DATE.text.substring(2, $DATE.text.length()-1))); }
196    ;
197
198color returns [AST node]
199    : COLOR { $node = new LoadColor(location, Color.fromrepr($COLOR.text)); }
200    ;
201
202literal returns [AST node]
203    : e_none=none { $node = $e_none.node; }
204    | e_false=false_ { $node = $e_false.node; }
205    | e_true=true_ { $node = $e_true.node; }
206    | e_name=name { $node = $e_name.node; }
207    | e_int=int_ { $node = $e_int.node; }
208    | e_float=float_ { $node = $e_float.node; }
209    | e_string=string { $node = $e_string.node; }
210    | e_date=date { $node = $e_date.node; }
211    | e_color=color { $node = $e_color.node; }
212    ;
213
214/* List literals */
215list returns [com.livinglogic.ul4.List node]
216    :
217        '['
218        ']' { $node = new com.livinglogic.ul4.List(location); }
219    |
220        '[' {$node = new com.livinglogic.ul4.List(location); }
221        e1=expr1 { $node.append($e1.node); }
222        (
223            ','
224            e2=expr1 { $node.append($e2.node); }
225        )*
226        ','?
227        ']'
228    ;
229
230/* Dict literal */
231fragment
232dictitem returns [DictItem node]
233    :
234        k=expr1
235        ':'
236        v=expr1 { $node = new DictItemKeyValue($k.node, $v.node); }
237    |
238        '**'
239        d=expr1 { $node = new DictItemDict($d.node); }
240    ;
241
242dict returns [Dict node]
243    :
244        '{'
245        '}' { $node = new Dict(location); }
246    |
247        '{' { $node = new Dict(location); }
248        i1=dictitem { $node.append($i1.node); }
249        (
250            ','
251            i2=dictitem { $node.append($i2.node); }
252        )*
253        ','?
254        '}'
255    ;
256
257atom returns [AST node]
258    : e_literal=literal { $node = $e_literal.node; }
259    | e_list=list { $node = $e_list.node; }
260    | e_dict=dict { $node = $e_dict.node; }
261    | '(' e_bracket=expr1 ')' { $node = $e_bracket.node; }
262    ;
263
264/* Function call */
265expr10 returns [AST node]
266    : a=atom { $node = $a.node; }
267    | n=name '(' ')' { $node = new CallFunc(location, $n.text); }
268    |
269        n=name { $node = new CallFunc(location, $n.text); }
270        '('
271        a1=expr1 { ((CallFunc)$node).append($a1.node); }
272        (
273            ','
274            a2=expr1 { ((CallFunc)$node).append($a2.node); }
275        )*
276        ','?
277        ')'
278    ;
279
280/* Attribute access, method call, item access, slice access */
281fragment
282callarg returns [CallArg node]
283    :
284        n=name
285        '='
286        e=expr1 { $node = new CallArgNamed($n.text, $e.node); }
287    |
288        '**'
289        e=expr1 { $node = new CallArgDict($e.node); }
290    ;
291
292expr9 returns [AST node]
293    @init
294    {
295        boolean callmeth = false;
296        AST index1 = null;
297        AST index2 = null;
298        boolean slice = false;
299    }
300    :
301        e1=expr10 { $node = $e1.node; }
302        (
303            /* Attribute access/function call */
304            '.'
305            n=name
306            (
307                /* Function call */
308                (
309                    /* No arguments */
310                    '('
311                    ')' { callmeth = true; $node = new CallMeth(location, $node, $n.text); }
312                |
313                    /* Positional argument */
314                    '(' { callmeth = true; $node = new CallMeth(location, $node, $n.text); }
315                    pa1=expr1 { ((CallMeth)$node).append($pa1.node); }
316                    (
317                        ','
318                        pa2=expr1 { ((CallMeth)$node).append($pa2.node); }
319                    )*
320                    ','?
321                    ')'
322                |
323                    /* Keyword arguments */
324                    '(' { callmeth = true; $node = new CallMethKeywords(location, $node, $n.text); }
325                    kwa1=callarg { ((CallMethKeywords)$node).append($kwa1.node); }
326                    (
327                        ','
328                        kwa2=callarg { ((CallMethKeywords)$node).append($kwa2.node); }
329                    )*
330                    ','?
331                    ')'
332                )
333            )? { if (!callmeth) $node = new GetAttr(location, $node, $n.text); }
334        |
335            /* Item/slice access */
336            '['
337            (
338                ':'
339                (
340                    e2=expr1 { index2 = $e2.node; }
341                )? { $node = new GetSlice(location, $node, null, index2); }
342            |
343                e2=expr1 { index1 = $e2.node; }
344                (
345                    ':' { slice = true; }
346                    (
347                        e3=expr1 { index2 = $e3.node; }
348                    )?
349                )? { $node = slice ? new GetSlice(location, $node, index1, index2) : new GetItem(location, $node, index1); }
350            )
351            ']'
352        )*
353    ;
354
355/* Negation */
356expr8 returns [AST node]
357    @init
358    {
359        int count = 0;
360    }
361    :
362        (
363            '-' { ++count; }
364        )*
365        e=expr9 { $node = $e.node; while (count-- != 0) { $node = new Neg(location, $node); } }
366    ;
367
368/* Multiplication, division, modulo */
369expr7 returns [AST node]
370    @init
371    {
372        int opcode = -1;
373    }
374    :
375        e1=expr8 { $node = $e1.node; }
376        (
377            (
378                '*' { opcode = 0; }
379            |
380                '/' { opcode = 1; }
381            |
382                '//' { opcode = 2; }
383            |
384                '%' { opcode = 3; }
385            )
386            e2=expr8 { switch (opcode) { case 0: $node = new Mul(location, $node, $e2.node); break; case 1: $node = new TrueDiv(location, $node, $e2.node); break; case 2: $node = new FloorDiv(location, $node, $e2.node); break; case 3: $node = new Mod(location, $node, $e2.node); break; } }
387        )*
388    ;
389
390/* Addition, substraction */
391expr6 returns [AST node]
392    @init
393    {
394        boolean add = false;
395    }
396    :
397        e1=expr7 { $node = $e1.node; }
398        (
399            (
400                '+' { add = true; }
401            |
402                '-' { add = false; }
403            )
404            e2=expr7 { $node = add ? new Add(location, $node, $e2.node) : new Sub(location, $node, $e2.node); }
405        )*
406    ;
407
408/* Comparisons */
409expr5 returns [AST node]
410    @init
411    {
412        int opcode = -1;
413    }
414    :
415        e1=expr6 { $node = $e1.node; }
416        (
417            (
418                '==' { opcode = 0; }
419            |
420                '!=' { opcode = 1; }
421            |
422                '<' { opcode = 2; }
423            |
424                '<=' { opcode = 3; }
425            |
426                '>' { opcode = 4; }
427            |
428                '>=' { opcode = 5; }
429            )
430            e2=expr6 { switch (opcode) { case 0: $node = new EQ(location, $node, $e2.node); break; case 1: $node = new NE(location, $node, $e2.node); break; case 2: $node = new LT(location, $node, $e2.node); break; case 3: $node = new LE(location, $node, $e2.node); break; case 4: $node = new GT(location, $node, $e2.node); break; case 5: $node = new GE(location, $node, $e2.node); break; } }
431        )*
432    ;
433
434/* "in"/"not in" operator */
435expr4 returns [AST node]
436    @init
437    {
438        boolean not = false;
439    }
440    :
441        e1=expr5 { $node = $e1.node; }
442        (
443           
444            (
445                'not' { not = true; }
446            )?
447            'in'
448            e2=expr5 { $node = not ? new NotContains(location, $node, $e2.node) : new Contains(location, $node, $e2.node); }
449        )?
450    ;
451
452/* Not operator */
453expr3 returns [AST node]
454    :
455        'not'
456        e=expr4 { $node = new Not(location, $e.node); }
457    |
458        e=expr4 { $node = $e.node; }
459    ;
460
461
462/* And operator */
463expr2 returns [AST node]
464    :
465        e1=expr3 { $node = $e1.node; }
466        (
467            'and'
468            e2=expr3 { $node = new And(location, $node, $e2.node); }
469        )*
470    ;
471
472/* Or operator */
473expr1 returns [AST node]
474    :
475        e1=expr2 { $node = $e1.node; }
476        (
477            'or'
478            e2=expr2 { $node = new Or(location, $node, $e2.node); }
479        )*
480    ;
481
482expression returns [AST node]
483    : e=expr1 EOF { $node = $e.node; }
484    ;
485
486
487/* Additional rules for "for" tag */
488
489nestedvarname returns [Object varname]
490    :
491        n=name { $varname = $n.text; }
492    |
493        '(' n0=nestedvarname ',' ')' { $varname = java.util.Arrays.asList($n0.varname); }
494    |
495        '('
496        n1=nestedvarname
497        ','
498        n2=nestedvarname { $varname = new ArrayList(2); ((ArrayList)$varname).add($n1.varname); ((ArrayList)$varname).add($n2.varname); }
499        (
500            ','
501            n3=nestedvarname { ((ArrayList)$varname).add($n3.varname); }
502        )*
503        ','?
504        ')'
505    ;
506
507for_ returns [For node]
508    :
509        n=nestedvarname
510        'in'
511        e=expr1 { $node = new For(location, $n.varname, $e.node); }
512        EOF
513    ;
514
515
516/* Additional rules for "code" tag */
517
518stmt returns [AST node]
519    : n=name '=' e=expr1 EOF { $node = new StoreVar(location, $n.text, $e.node); }
520    | n=name '+=' e=expr1 EOF { $node = new AddVar(location, $n.text, $e.node); }
521    | n=name '-=' e=expr1 EOF { $node = new SubVar(location, $n.text, $e.node); }
522    | n=name '*=' e=expr1 EOF { $node = new MulVar(location, $n.text, $e.node); }
523    | n=name '/=' e=expr1 EOF { $node = new TrueDivVar(location, $n.text, $e.node); }
524    | n=name '//=' e=expr1 EOF { $node = new FloorDivVar(location, $n.text, $e.node); }
525    | n=name '%=' e=expr1 EOF { $node = new ModVar(location, $n.text, $e.node); }
526    | 'del' n=name EOF { $node = new DelVar(location, $n.text); }
527    ;
Note: See TracBrowser for help on using the browser.