--- /dev/null
+grammar Graph;\r
+\r
+options {\r
+ language = Java;\r
+ output = AST;\r
+ ASTLabelType=CommonTree;\r
+}\r
+\r
+tokens {\r
+ // lexer\r
+ INDENT;\r
+ DEDENT;\r
+ \r
+ // graph\r
+ FILE;\r
+ RESOURCE;\r
+ PROPERTY;\r
+ VARIABLE;\r
+ EMBEDDED_VALUE;\r
+ EMBEDDED_TYPE;\r
+ TEMPLATE_INSTANCE;\r
+ TEMPLATE_DEFINITION;\r
+ \r
+ BLANK;\r
+ REF;\r
+ \r
+ EQUALS;\r
+ INSTANCE_OF;\r
+ INHERITS;\r
+ SUBRELATION_OF;\r
+ HAS_DOMAIN;\r
+ HAS_RANGE;\r
+ DOMAIN_OF;\r
+ REQUIRES_VALUE_TYPE;\r
+\r
+ // data\r
+ TYPE_DEFINITIONS;\r
+ TYPE_DEFINITION; \r
+\r
+ UNION_TYPE; \r
+ RECORD_TYPE;\r
+ TUPLE_TYPE;\r
+ ARRAY_TYPE;\r
+ TYPE_REFERENCE;\r
+ TYPE_ANNOTATION; \r
+ TYPE_COMPONENT;\r
+ \r
+ VALUE_DEFINITIONS;\r
+ VALUE_DEFINITION;\r
+\r
+ NO_VALUE; \r
+ VARIANT;\r
+ ARRAY;\r
+ TUPLE;\r
+ TAGGED_VALUE;\r
+ RECORD;\r
+ MAP;\r
+ ASSIGNMENT;\r
+ TRUE;\r
+ FALSE;\r
+}\r
+\r
+@parser::header { package org.simantics.graph.compiler.internal.parsing; }\r
+@lexer::header { package org.simantics.graph.compiler.internal.parsing; \r
+\r
+import gnu.trove.list.array.*;\r
+}\r
+\r
+@lexer::members {\r
+int inParen = 0;\r
+\r
+TIntArrayList iStack = new TIntArrayList();\r
+{ iStack.add(0); }\r
+\r
+List tokens = new ArrayList();\r
+public void emit(Token token) {\r
+ state.token = token;\r
+ tokens.add(token);\r
+}\r
+public Token nextToken() {\r
+ if(tokens.isEmpty()) {\r
+ super.nextToken();\r
+ if ( tokens.isEmpty() ) {\r
+ /* When end-of-file is encountered, we \r
+ emit balancing number of DEDENT tokens.\r
+ */\r
+ if(iStack.size() <= 1)\r
+ return Token.EOF_TOKEN;\r
+ else { \r
+ while(iStack.size() > 1) {\r
+ iStack.removeAt(iStack.size()-1);\r
+ state.type = DEDENT;\r
+ emit();\r
+ }\r
+ iStack.clear();\r
+ }\r
+ } \r
+ }\r
+ return (Token)tokens.remove(0);\r
+}\r
+\r
+}\r
+\r
+// ------------------------------------------------------------------\r
+// LEXER\r
+// ------------------------------------------------------------------\r
+\r
+ID : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*\r
+ ;\r
+\r
+COMMENT\r
+ : '//' ~('\n')* {$channel=HIDDEN;}\r
+ | '/*' ( options {greedy=false;} : . )* '*/' {$channel=HIDDEN;}\r
+ ;\r
+\r
+WS : ( ' '\r
+ | '\t'\r
+ | '\r'\r
+ ) {$channel=HIDDEN;}\r
+ ;\r
+ \r
+LPAREN : '(' { ++ inParen; } ;\r
+RPAREN : ')' { -- inParen; } ; \r
+LBRACKET : '[' { ++ inParen; } ;\r
+RBRACKET : ']' { -- inParen; } ;\r
+LCURLY : '{' { ++ inParen; } ;\r
+RCURLY : '}' { -- inParen; } ;\r
+\r
+INT_RANGE : INT '..' INT?\r
+ | '..' INT\r
+ ;\r
+RANGE : FLOAT '..' (FLOAT | INT)?\r
+ | '..' FLOAT\r
+ | INT '..' FLOAT\r
+ ;\r
+\r
+NEWLINE\r
+@init { int spaces = 0; } \r
+ : '\n'\r
+ ( ' ' { ++spaces; } \r
+ | '//' ~('\n')* '\n' { spaces = 0; } \r
+ | '/*' ( options {greedy=false;} : . )* '*/'\r
+ | '\r'\r
+ | '\n' { spaces = 0; }\r
+ )*\r
+ { \r
+ int c = input.LA(1);\r
+ \r
+ if(inParen > 0) {\r
+ $channel = HIDDEN;\r
+ }\r
+ else if(c == EOF) {\r
+ while(iStack.size() > 1) {\r
+ iStack.removeAt(iStack.size()-1);\r
+ state.type = DEDENT;\r
+ emit();\r
+ }\r
+ $channel = HIDDEN;\r
+ iStack.clear();\r
+ }\r
+ else {\r
+ int stackTop = iStack.get(iStack.size()-1);\r
+ if(spaces > stackTop) {\r
+ iStack.add(spaces);\r
+ $type = INDENT;\r
+ }\r
+ else if(spaces < stackTop) {\r
+ while(spaces < iStack.get(iStack.size()-1)) {\r
+ iStack.removeAt(iStack.size()-1);\r
+ state.type = DEDENT;\r
+ emit();\r
+ }\r
+ state.type = NEWLINE;\r
+ emit();\r
+ // TODO check that spaces == iStack.get(iStack.size()-1)\r
+ }\r
+ }\r
+ }\r
+ ;\r
+ \r
+INDENT: { false }?=> 'INDENT' ;\r
+DEDENT: { false }?=> 'DEDENT' ; \r
+ \r
+INT : '-'? '0'..'9'+\r
+ ;\r
+\r
+FLOAT\r
+ : '-'? \r
+ ( ('0'..'9')+ '.' ('0'..'9')* EXPONENT?\r
+ | ('0'..'9')+ EXPONENT\r
+ )\r
+ ;\r
+ \r
+STRING\r
+ : '"' ( ESC_SEQ | ~('\\'|'"') )* '"'\r
+ | '"""' ( ~('"') | '"' ~('"') | '""' ~('"') )* '"""'\r
+ ;\r
+\r
+URI\r
+ : '<http:' ( ~('>') )* '>'\r
+ ;\r
+\r
+fragment\r
+EXPONENT : ('e'|'E') ('+'|'-')? ('0'..'9')+ ;\r
+\r
+fragment\r
+HEX_DIGIT : ('0'..'9'|'a'..'f'|'A'..'F') ;\r
+\r
+fragment\r
+ESC_SEQ\r
+ : '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')\r
+ | UNICODE_ESC\r
+ ;\r
+\r
+fragment\r
+UNICODE_ESC\r
+ : '\\' 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT\r
+ ;\r
+\r
+// ------------------------------------------------------------------\r
+// STATEMENTS \r
+// ------------------------------------------------------------------\r
+\r
+file : NEWLINE? resourceDefinitions? EOF -> ^(FILE resourceDefinitions?) ;\r
+\r
+resourceDefinitions \r
+ : resourceDefinition (NEWLINE! resourceDefinition)*\r
+ ;\r
+ \r
+resourceDefinition \r
+ : resource\r
+ localProperty*\r
+ (INDENT property (NEWLINE property)* DEDENT)?\r
+ -> ^(RESOURCE resource localProperty* property*) \r
+ | template -> ^(RESOURCE ^(BLANK template) template)\r
+ ;\r
+ \r
+localProperty\r
+ : relation resource\r
+ -> ^(PROPERTY relation ^(RESOURCE resource))\r
+ ; \r
+\r
+property\r
+ : relation\r
+ ( resourceDefinition -> ^(PROPERTY relation resourceDefinition)\r
+ | INDENT resourceDefinitions DEDENT -> ^(PROPERTY relation resourceDefinitions)\r
+ )\r
+ | template\r
+ ;\r
+ \r
+template\r
+ : '@' \r
+ ( {input.LT(1).getText().equals("template")}?=>\r
+ ID resource+ \r
+ INDENT resourceDefinitions DEDENT\r
+ -> ^(TEMPLATE_DEFINITION resource+ resourceDefinitions)\r
+ | resource+ \r
+ (INDENT resourceDefinitions DEDENT)?\r
+ -> ^(TEMPLATE_INSTANCE resource+ resourceDefinitions?)\r
+ )\r
+ ; \r
+\r
+// ------------------------------------------------------------------\r
+// RESOURCES\r
+// ------------------------------------------------------------------\r
+\r
+relation \r
+ : ( ID -> ID) \r
+ ('.' ID -> ^(REF $relation ID))*\r
+ | URI\r
+ | '<T' -> INHERITS\r
+ | '<R' -> SUBRELATION_OF\r
+ | '<--' -> HAS_DOMAIN\r
+ | '-->' -> HAS_RANGE\r
+ | '==>' -> REQUIRES_VALUE_TYPE\r
+ | '>--' -> DOMAIN_OF \r
+ | ':' -> INSTANCE_OF\r
+ | '=' -> EQUALS\r
+ | '%' ID -> ^(VARIABLE ID)\r
+ ;\r
+\r
+resource\r
+ : ( {input.LT(1).getText().equals("_")}?=> ID -> ^(BLANK ID) \r
+ | ID -> ID) \r
+ ('.' (ID -> ^(REF $resource ID)\r
+ |STRING -> ^(REF $resource STRING)\r
+ )\r
+ )*\r
+ | URI\r
+ | simpleValue -> ^(EMBEDDED_VALUE simpleValue)\r
+ | '$' basicType -> ^(EMBEDDED_TYPE basicType)\r
+ | '%' ID -> ^(VARIABLE ID)\r
+ ;\r
+\r
+// ------------------------------------------------------------------\r
+// TYPE DEFINITIONS \r
+// ------------------------------------------------------------------\r
+ \r
+/*typeDefinitions : typeDefinition* -> ^(TYPE_DEFINITIONS typeDefinition*);\r
+\r
+typeDefinition \r
+ : 'type' ID '=' type -> ^(TYPE_DEFINITION ID type)\r
+ ;\r
+*/\r
+ \r
+type \r
+ : arrayType\r
+ | unionType\r
+ ; \r
+\r
+unionType\r
+ :\r
+ ('|' unionComponent)+\r
+ -> ^(UNION_TYPE unionComponent+)\r
+ ;\r
+\r
+unionComponent : ID ((arrayType) => arrayType)? -> ^(TYPE_COMPONENT ID arrayType?) ;\r
+\r
+arrayType \r
+ : (basicType -> basicType)\r
+ (LBRACKET arrayLength? RBRACKET -> ^(ARRAY_TYPE $arrayType arrayLength?))* ;\r
+\r
+arrayLength \r
+ : INT\r
+ | INT_RANGE\r
+ ;\r
+\r
+basicType \r
+ : tupleType\r
+ | recordType\r
+ | typeReference\r
+ ;\r
+ \r
+tupleType \r
+ : LPAREN (type (',' type)*)? RPAREN \r
+ -> ^(TUPLE_TYPE type*) \r
+ ;\r
+\r
+recordType \r
+ : LCURLY (component (',' component)*)? RCURLY \r
+ -> ^(RECORD_TYPE component*)\r
+ ;\r
+\r
+component \r
+ : ID ':' type \r
+ -> ^(TYPE_COMPONENT ID type) \r
+ ;\r
+\r
+typeReference \r
+ : ID ((LPAREN)=> LPAREN parameter (',' parameter)* RPAREN)? \r
+ -> ^(TYPE_REFERENCE ID parameter*)\r
+ ;\r
+\r
+parameter \r
+ : ID '=' parameterValue -> ^(TYPE_ANNOTATION ID parameterValue)\r
+ | type \r
+ ;\r
+\r
+parameterValue \r
+ : string\r
+ | boolean_\r
+ | number\r
+ | rangePar -> ^(RANGE rangePar) \r
+ ;\r
+ \r
+rangePar : (LBRACKET | LPAREN) range (RBRACKET | RPAREN) ; \r
+ \r
+range\r
+ : number\r
+ | RANGE\r
+ | INT_RANGE \r
+ ; \r
+ \r
+number\r
+ : INT\r
+ | FLOAT\r
+ ;\r
+\r
+string\r
+ : STRING\r
+ ;\r
+ \r
+boolean_\r
+ : 'true' -> TRUE\r
+ | 'false' -> FALSE\r
+ ;\r
+\r
+// ------------------------------------------------------------------\r
+// VALUE DEFINITIONS \r
+// ------------------------------------------------------------------\r
+\r
+valueDefinitions : valueDefinition* -> ^(VALUE_DEFINITIONS valueDefinition*);\r
+\r
+valueDefinition \r
+ : ID ':' type '=' value\r
+ -> ^(VALUE_DEFINITION ID type value) \r
+ ;\r
+\r
+value \r
+ : (basicValue -> basicValue)\r
+ (':' type -> ^(VARIANT type $value))* \r
+ ;\r
+\r
+basicValue \r
+ : simpleValue\r
+ | map\r
+ | {input.LT(1).getText().equals("null")}? ID -> NO_VALUE\r
+ | taggedValue \r
+ ;\r
+ \r
+simpleValue\r
+ : string\r
+ | number\r
+ | boolean_\r
+ | array\r
+ | tuple\r
+ | record\r
+ ; \r
+\r
+array \r
+ : LBRACKET (value (',' value)*)? RBRACKET\r
+ -> ^(ARRAY value*) \r
+ ;\r
+\r
+tuple \r
+ : LPAREN (value (',' value)*)? RPAREN\r
+ -> ^(TUPLE value*) \r
+ ;\r
+\r
+taggedValue \r
+ : ID simpleValue?\r
+ -> ^(TAGGED_VALUE ID simpleValue?) \r
+ ;\r
+\r
+record \r
+ : LCURLY (recordAssignment (',' recordAssignment)*)? RCURLY\r
+ -> ^(RECORD recordAssignment*) \r
+ ;\r
+\r
+recordAssignment \r
+ : ID '=' value\r
+ -> ^(ASSIGNMENT ID value)\r
+ ;\r
+\r
+map : {input.LT(1).getText().equals("map")}?=> ID LCURLY (mapAssignment (',' mapAssignment)*)? RCURLY\r
+ -> ^(MAP mapAssignment*) \r
+ ;\r
+\r
+mapAssignment \r
+ : value '=' value\r
+ -> ^(ASSIGNMENT value*) \r
+ ;\r
+
\ No newline at end of file