67b11fbaa7a1abc257a4c7117387036f4ae89758
[simantics/platform.git] / bundles / org.simantics.graph.compiler / src / org / simantics / graph / compiler / internal / parsing / Graph.g
1 grammar Graph;\r
2 \r
3 options {\r
4   language = Java;\r
5   output = AST;\r
6   ASTLabelType=CommonTree;\r
7 }\r
8 \r
9 tokens {\r
10     // lexer\r
11     INDENT;\r
12     DEDENT;\r
13     \r
14     // graph\r
15     FILE;\r
16     RESOURCE;\r
17     PROPERTY;\r
18     VARIABLE;\r
19     EMBEDDED_VALUE;\r
20     EMBEDDED_TYPE;\r
21     TEMPLATE_INSTANCE;\r
22     TEMPLATE_DEFINITION;\r
23     \r
24     BLANK;\r
25     REF;\r
26     \r
27     EQUALS;\r
28     INSTANCE_OF;\r
29     INHERITS;\r
30     SUBRELATION_OF;\r
31     HAS_DOMAIN;\r
32     HAS_RANGE;\r
33     DOMAIN_OF;\r
34     REQUIRES_VALUE_TYPE;\r
35 \r
36     // data\r
37     TYPE_DEFINITIONS;\r
38     TYPE_DEFINITION;    \r
39 \r
40     UNION_TYPE;    \r
41     RECORD_TYPE;\r
42     TUPLE_TYPE;\r
43     ARRAY_TYPE;\r
44     TYPE_REFERENCE;\r
45     TYPE_ANNOTATION;    \r
46     TYPE_COMPONENT;\r
47     \r
48     VALUE_DEFINITIONS;\r
49     VALUE_DEFINITION;\r
50 \r
51     NO_VALUE;    \r
52     VARIANT;\r
53     ARRAY;\r
54     TUPLE;\r
55     TAGGED_VALUE;\r
56     RECORD;\r
57     MAP;\r
58     ASSIGNMENT;\r
59     TRUE;\r
60     FALSE;\r
61 }\r
62 \r
63 @parser::header { package org.simantics.graph.compiler.internal.parsing; }\r
64 @lexer::header { package org.simantics.graph.compiler.internal.parsing; \r
65 \r
66 import gnu.trove.list.array.*;\r
67 }\r
68 \r
69 @lexer::members {\r
70 int inParen = 0;\r
71 \r
72 TIntArrayList iStack = new TIntArrayList();\r
73 { iStack.add(0); }\r
74 \r
75 List tokens = new ArrayList();\r
76 public void emit(Token token) {\r
77     state.token = token;\r
78     tokens.add(token);\r
79 }\r
80 public Token nextToken() {\r
81     if(tokens.isEmpty()) {\r
82         super.nextToken();\r
83         if ( tokens.isEmpty() ) {\r
84             /* When end-of-file is encountered, we \r
85                emit balancing number of DEDENT tokens.\r
86             */\r
87             if(iStack.size() <= 1)\r
88                 return Token.EOF_TOKEN;\r
89             else {                \r
90                 while(iStack.size() > 1) {\r
91                     iStack.removeAt(iStack.size()-1);\r
92                     state.type = DEDENT;\r
93                     emit();\r
94                 }\r
95                 iStack.clear();\r
96             }\r
97         } \r
98     }\r
99     return (Token)tokens.remove(0);\r
100 }\r
101 \r
102 }\r
103 \r
104 // ------------------------------------------------------------------\r
105 // LEXER\r
106 // ------------------------------------------------------------------\r
107 \r
108 ID  : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*\r
109     ;\r
110 \r
111 COMMENT\r
112     :   '//' ~('\n')* {$channel=HIDDEN;}\r
113     |   '/*' ( options {greedy=false;} : . )* '*/' {$channel=HIDDEN;}\r
114     ;\r
115 \r
116 WS  : ( ' '\r
117       | '\t'\r
118       | '\r'\r
119       ) {$channel=HIDDEN;}\r
120     ;\r
121     \r
122 LPAREN    : '(' { ++ inParen; } ;\r
123 RPAREN    : ')' { -- inParen; } ;    \r
124 LBRACKET  : '[' { ++ inParen; } ;\r
125 RBRACKET  : ']' { -- inParen; } ;\r
126 LCURLY    : '{' { ++ inParen; } ;\r
127 RCURLY    : '}' { -- inParen; } ;\r
128 \r
129 INT_RANGE : INT '..' INT?\r
130           | '..' INT\r
131           ;\r
132 RANGE     : FLOAT '..' (FLOAT | INT)?\r
133           | '..' FLOAT\r
134           | INT '..' FLOAT\r
135           ;\r
136 \r
137 NEWLINE\r
138 @init { int spaces = 0; } \r
139     : '\n'\r
140       ( ' ' { ++spaces; } \r
141       | '//' ~('\n')* '\n' { spaces = 0; } \r
142       | '/*' ( options {greedy=false;} : . )* '*/'\r
143       | '\r'\r
144       | '\n' { spaces = 0; }\r
145       )*\r
146       { \r
147           int c = input.LA(1);\r
148           \r
149           if(inParen > 0) {\r
150               $channel = HIDDEN;\r
151           }\r
152           else if(c == EOF) {\r
153               while(iStack.size() > 1) {\r
154                   iStack.removeAt(iStack.size()-1);\r
155                   state.type = DEDENT;\r
156                   emit();\r
157               }\r
158               $channel = HIDDEN;\r
159               iStack.clear();\r
160           }\r
161           else {\r
162               int stackTop = iStack.get(iStack.size()-1);\r
163               if(spaces > stackTop) {\r
164                   iStack.add(spaces);\r
165                   $type = INDENT;\r
166               }\r
167               else if(spaces < stackTop) {\r
168                   while(spaces < iStack.get(iStack.size()-1)) {\r
169                       iStack.removeAt(iStack.size()-1);\r
170                       state.type = DEDENT;\r
171                       emit();\r
172                   }\r
173                   state.type = NEWLINE;\r
174                   emit();\r
175                   // TODO check that spaces == iStack.get(iStack.size()-1)\r
176               }\r
177           }\r
178       }\r
179     ;\r
180   \r
181 INDENT: { false }?=> 'INDENT' ;\r
182 DEDENT: { false }?=> 'DEDENT' ;  \r
183     \r
184 INT : '-'? '0'..'9'+\r
185     ;\r
186 \r
187 FLOAT\r
188     : '-'? \r
189     ( ('0'..'9')+ '.' ('0'..'9')* EXPONENT?\r
190     | ('0'..'9')+ EXPONENT\r
191     )\r
192     ;\r
193   \r
194 STRING\r
195     :  '"' ( ESC_SEQ | ~('\\'|'"') )* '"'\r
196     |  '"""' ( ~('"') | '"' ~('"') | '""' ~('"') )* '"""'\r
197     ;\r
198 \r
199 URI\r
200     :  '<http:' ( ~('>') )* '>'\r
201     ;\r
202 \r
203 fragment\r
204 EXPONENT : ('e'|'E') ('+'|'-')? ('0'..'9')+ ;\r
205 \r
206 fragment\r
207 HEX_DIGIT : ('0'..'9'|'a'..'f'|'A'..'F') ;\r
208 \r
209 fragment\r
210 ESC_SEQ\r
211     :   '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')\r
212     |   UNICODE_ESC\r
213     ;\r
214 \r
215 fragment\r
216 UNICODE_ESC\r
217     :   '\\' 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT\r
218     ;\r
219 \r
220 // ------------------------------------------------------------------\r
221 // STATEMENTS    \r
222 // ------------------------------------------------------------------\r
223 \r
224 file : NEWLINE? resourceDefinitions? EOF -> ^(FILE resourceDefinitions?) ;\r
225 \r
226 resourceDefinitions \r
227      : resourceDefinition (NEWLINE! resourceDefinition)*\r
228      ;\r
229              \r
230 resourceDefinition \r
231     : resource\r
232       localProperty*\r
233       (INDENT property (NEWLINE property)* DEDENT)?\r
234     -> ^(RESOURCE resource localProperty* property*) \r
235     | template -> ^(RESOURCE ^(BLANK template) template)\r
236     ;\r
237     \r
238 localProperty\r
239     : relation resource\r
240     -> ^(PROPERTY relation ^(RESOURCE resource))\r
241     ;    \r
242 \r
243 property\r
244     : relation\r
245       ( resourceDefinition -> ^(PROPERTY relation resourceDefinition)\r
246       | INDENT resourceDefinitions DEDENT -> ^(PROPERTY relation resourceDefinitions)\r
247       )\r
248     | template\r
249     ;\r
250     \r
251 template\r
252     : '@' \r
253     ( {input.LT(1).getText().equals("template")}?=>\r
254       ID resource+ \r
255       INDENT resourceDefinitions DEDENT\r
256       -> ^(TEMPLATE_DEFINITION resource+ resourceDefinitions)\r
257     | resource+ \r
258       (INDENT resourceDefinitions DEDENT)?\r
259       -> ^(TEMPLATE_INSTANCE resource+ resourceDefinitions?)\r
260     )\r
261     ;    \r
262 \r
263 // ------------------------------------------------------------------\r
264 // RESOURCES\r
265 // ------------------------------------------------------------------\r
266 \r
267 relation \r
268     : ( ID -> ID) \r
269       ('.' ID -> ^(REF $relation ID))*\r
270     | URI\r
271     | '<T' -> INHERITS\r
272     | '<R' -> SUBRELATION_OF\r
273     | '<--' -> HAS_DOMAIN\r
274     | '-->' -> HAS_RANGE\r
275     | '==>' -> REQUIRES_VALUE_TYPE\r
276     | '>--' -> DOMAIN_OF    \r
277     | ':' -> INSTANCE_OF\r
278     | '=' -> EQUALS\r
279     | '%' ID -> ^(VARIABLE ID)\r
280     ;\r
281 \r
282 resource\r
283     : ( {input.LT(1).getText().equals("_")}?=> ID -> ^(BLANK ID) \r
284       | ID -> ID) \r
285       ('.' (ID -> ^(REF $resource ID)\r
286            |STRING -> ^(REF $resource STRING)\r
287            )\r
288       )*\r
289     | URI\r
290     | simpleValue -> ^(EMBEDDED_VALUE simpleValue)\r
291     | '$' basicType -> ^(EMBEDDED_TYPE basicType)\r
292     | '%' ID -> ^(VARIABLE ID)\r
293     ;\r
294 \r
295 // ------------------------------------------------------------------\r
296 // TYPE DEFINITIONS    \r
297 // ------------------------------------------------------------------\r
298  \r
299 /*typeDefinitions : typeDefinition* -> ^(TYPE_DEFINITIONS typeDefinition*);\r
300 \r
301 typeDefinition \r
302     : 'type' ID '=' type -> ^(TYPE_DEFINITION ID type)\r
303     ;\r
304 */\r
305   \r
306 type \r
307     : arrayType\r
308     | unionType\r
309     ;      \r
310 \r
311 unionType\r
312     :\r
313      ('|' unionComponent)+\r
314     -> ^(UNION_TYPE unionComponent+)\r
315     ;\r
316 \r
317 unionComponent : ID ((arrayType) => arrayType)? -> ^(TYPE_COMPONENT ID arrayType?) ;\r
318 \r
319 arrayType \r
320     : (basicType -> basicType)\r
321       (LBRACKET arrayLength? RBRACKET -> ^(ARRAY_TYPE $arrayType arrayLength?))* ;\r
322 \r
323 arrayLength \r
324     : INT\r
325     | INT_RANGE\r
326     ;\r
327 \r
328 basicType \r
329     : tupleType\r
330     | recordType\r
331     | typeReference\r
332     ;\r
333     \r
334 tupleType \r
335     : LPAREN (type (',' type)*)? RPAREN \r
336     -> ^(TUPLE_TYPE type*) \r
337     ;\r
338 \r
339 recordType \r
340     : LCURLY (component (',' component)*)? RCURLY \r
341     -> ^(RECORD_TYPE component*)\r
342     ;\r
343 \r
344 component \r
345     : ID ':' type \r
346     -> ^(TYPE_COMPONENT ID type) \r
347     ;\r
348 \r
349 typeReference \r
350     : ID ((LPAREN)=> LPAREN parameter (',' parameter)* RPAREN)? \r
351     -> ^(TYPE_REFERENCE ID parameter*)\r
352     ;\r
353 \r
354 parameter \r
355     : ID '=' parameterValue -> ^(TYPE_ANNOTATION ID parameterValue)\r
356     | type \r
357     ;\r
358 \r
359 parameterValue \r
360     : string\r
361     | boolean_\r
362     | number\r
363     | rangePar -> ^(RANGE rangePar)    \r
364     ;\r
365     \r
366 rangePar : (LBRACKET | LPAREN) range (RBRACKET | RPAREN) ;    \r
367     \r
368 range\r
369     : number\r
370     | RANGE\r
371     | INT_RANGE    \r
372     ;    \r
373     \r
374 number\r
375     : INT\r
376     | FLOAT\r
377     ;\r
378 \r
379 string\r
380     : STRING\r
381     ;\r
382     \r
383 boolean_\r
384     : 'true' -> TRUE\r
385     | 'false' -> FALSE\r
386     ;\r
387 \r
388 // ------------------------------------------------------------------\r
389 // VALUE DEFINITIONS    \r
390 // ------------------------------------------------------------------\r
391 \r
392 valueDefinitions : valueDefinition* -> ^(VALUE_DEFINITIONS valueDefinition*);\r
393 \r
394 valueDefinition \r
395     : ID ':' type '=' value\r
396     -> ^(VALUE_DEFINITION ID type value) \r
397     ;\r
398 \r
399 value \r
400     : (basicValue -> basicValue)\r
401       (':' type -> ^(VARIANT type $value))* \r
402     ;\r
403 \r
404 basicValue \r
405     : simpleValue\r
406     | map\r
407     | {input.LT(1).getText().equals("null")}? ID -> NO_VALUE\r
408     | taggedValue    \r
409     ;\r
410     \r
411 simpleValue\r
412     : string\r
413     | number\r
414     | boolean_\r
415     | array\r
416     | tuple\r
417     | record\r
418     ;   \r
419 \r
420 array \r
421     : LBRACKET (value (',' value)*)? RBRACKET\r
422     -> ^(ARRAY value*) \r
423     ;\r
424 \r
425 tuple \r
426     : LPAREN (value (',' value)*)? RPAREN\r
427     -> ^(TUPLE value*) \r
428     ;\r
429 \r
430 taggedValue \r
431     : ID simpleValue?\r
432     -> ^(TAGGED_VALUE ID simpleValue?) \r
433     ;\r
434 \r
435 record \r
436     : LCURLY (recordAssignment (',' recordAssignment)*)? RCURLY\r
437     -> ^(RECORD recordAssignment*) \r
438     ;\r
439 \r
440 recordAssignment \r
441     : ID '=' value\r
442     -> ^(ASSIGNMENT ID value)\r
443     ;\r
444 \r
445 map : {input.LT(1).getText().equals("map")}?=> ID LCURLY (mapAssignment (',' mapAssignment)*)? RCURLY\r
446     -> ^(MAP mapAssignment*) \r
447     ;\r
448 \r
449 mapAssignment \r
450     : value '=' value\r
451     -> ^(ASSIGNMENT value*) \r
452     ;\r
453