grammar miniC; options { language=Java; } // START:scope // rules prog, func, and block share the same global scope // and, therefore, push their scopes onto the same stack // of scopes as you would expect for C (a code block's // scope hides the function scope, which in turn, hides // the global scope). scope CScope { String name; List symbols; } // END:scope @header { package edu.lantoniak.minic; } @lexer::header { package edu.lantoniak.minic; } // START:members @members { int loopCounter = 5; boolean evaluate = true; boolean isLoop = false; /** Is id defined in a CScope? Walk from top of stack * downwards looking for a symbols list containing id. */ boolean isDefined(String id) { for (int s=$CScope.size()-1; s>=0; s--) { if ( $CScope[s]::symbols.contains(id) ) { System.out.println(id+" found in "+$CScope[s]::name); return true; } } return false; } } // END:members // START:augment prog scope CScope; @init { // initialize a scope for overall C program $CScope::symbols = new ArrayList(); $CScope::name = "global"; System.out.println("Begin program"); } @after { // dump global symbols after matching entire program System.out.println("End of program. Global symbols = "+$CScope::symbols); } : prog_content* ; prog_content : decl | func ; func scope CScope; @init { // initialize a scope for this function $CScope::symbols = new ArrayList(); } @after { // dump variables defined within the function itself System.out.println("End function "+$CScope::name+" symbols = "+ $CScope::symbols); } : 'void' ID {$CScope::name=$ID.text; System.out.println("Begin function: " + $ID.text);} '(' ')' block //'{' decl* stat+ cond* loop* '}' ; block scope CScope; @init { // initialize a scope for this code block $CScope::symbols = new ArrayList(); $CScope::name = "level "+$CScope.size(); System.out.println("Begin block: " + $CScope::name); } @after { // dump variables defined within this code block System.out.println("End code block level "+$CScope.size()+" = "+ $CScope::symbols); } : '{' block_content* '}' //decl* stat+ loop* cond* '}' ; // END:augment block_content : decl | stat | loop | cond | '{' {System.out.println("Blok anonimowy");} block_content* '}' ; /** Match a declaration and had the variable name to the current scope */ decl: 'int' ID {if (evaluate) { $CScope::symbols.add($ID.text); System.out.println("Decalred variable: " + $ID.text);}} ';' ; /** Match an assignment and call isDefined to ensure that the variable * is defined by looking it up in the stack of scopes. */ stat: ID '=' INT ';' { if ( !isDefined($ID.text) ) { System.err.println("undefined variable level "+ $CScope.size()+": "+$ID.text); } } // TODO why? | block ; loop @init { int start = -1; } @after { if (--loopCounter > 0) { System.out.println("rewinding: " + loopCounter); input.rewind(start); } else { System.out.println("End loop block."); } } : 'for' val=ID 'in' set=ID {start = input.mark(); System.out.println("start: " + start);} block {if (--loopCounter > 0) { System.out.println("rewinding: " + loopCounter); input.rewind(start); }} ; cond //scope CScope; @init { // $CScope::symbols = new ArrayList(); // $CScope::name = "if block "+$CScope.size(); } @after { evaluate = true; } : 'if' condition=ID {if (true) { evaluate = true; } else { evaluate = false; }} block ; ID : 'a'..'z'+ ; INT : '0'..'9'+ ; WS : (' '|'\n'|'\r')+ {$channel = HIDDEN;} ; COMMENT : '//' (~('\n'|'\r'))* {$channel = HIDDEN;} ; ML_COMMENT : '/*' ( options {greedy=false;} : . )* '*/' ;