/* This example shows how to specify an AST-building parser for a simple toy calculator language "calc". The calc language includes floats, multiplication, and let-expressions with one or multiple variable bindings. Calc also includes an expression "ask user" which interactively asks the user to enter a value. Optionally, a default value can be specified for an "ask user" expression. Here is an example program in this language: let radius = ask user 1.0?, pi = 3.14 in 2.0 * pi * radius ni */ /* *** Specification of the parser class *** */ options { NODE_DEFAULT_VOID = true; // don't generate nodes by default NODE_PREFIX = ""; // No prefix for generated AST classes MULTI = true; // many class names } PARSER_BEGIN(CalcParser) package AST; public class CalcParser {} PARSER_END(CalcParser) /* *** Token specification *** */ /* Skip whitespace */ SKIP : { " " | "\t" | "\n" | "\r" } /* Reserved words */ TOKEN [IGNORE_CASE]: { < LET: "let"> | < IN: "in"> | < NI: "ni"> | < ASK: "ask"> | < USER: "user"> } /* Literals */ TOKEN: { < FPLIT: (["0"-"9"])+ "." (["0"-"9"])+ > // Floating point literal } /* Operators and Separators*/ TOKEN: { < ASSIGN: "=" > | < MUL: "*" > | < COMMA: "," > | < QUESTION: "?" > } /* Identifiers */ TOKEN: { < ID: (["A"-"Z", "a"-"z"])+ > } /* *** Context-free grammar (EBNF) *** */ /* ** The start nonterminal and its productions. ** */ /* The Start nonterminal corresponds to the concrete class Start. An Start node is generated by the notation "#Start" in the heading of the nonterminal method. JJTree maintains a stack of created nodes. The "#X" notation (when written in the heading like this) means: - create a new object of type X - pop the nodes that were created during this parse method and insert them as children to the new X node - then push the new X node For this nonterminal (Start), JJTree will pop the node created by the Exp() parse and insert it as a child to the Start node. During parsing, new nodes are kept track of by the JJTree internal stack as explained above. However, when an external client calls the parser (by calling the method of the nonterminal start symbol), it is useful to let that method return the resulting tree. For this reason, the start nonterminal is given a result type (Start) and the action "return jjtThis" is added to return a reference to the new Start node. The variable "jjtThis" refers to the new node created by the "#X" command (the Start node in this case). Other nonterminals need not return any nodes since they are not called by external clients. Therefore, they have no result type (void). */ Start Start() #Start: {} // Start -> Exp { Exp() { return jjtThis; } } /* ** Other nonterminals and their productions ** */ /* Exp does not correspond to any concrete class. Therefore, there is no command "#Exp" in the heading of this nonterminal. If the list (of "*" and "/") is empty, this nonterminal does not change the stack of nodes. The top node of the stack will be the node constructed by the call to Factor(). If the list is non-empty, the result of executing this nonterminal method will be to replace the top node of the stack with a left-associative tree rooted by a MulExp or DivExp node and consisting of possibly more MulExp and DivExp nodes. Here are the details of how the MulExp or DivExp tree is constructed: If there is one element in the list, a MulExp or a DivExp node will be created, and the two topmost nodes will be popped off the stack and inserted as children to the new MulExp/DivExp node. The new node is then pushed onto the stack. The notation "#MulExp(2)" means that precisely two nodes are popped. Simply writing "#MulExp" would have resulted in only popping one node. This is because the default scope of the "#X" command includes only the nodes within the current alternative. If there is more than one element in the list, the actions described above are repeated. The previous MulExp or DivExp node will be inserted as the first child of the new MulExp/DivExp node, and the node resulting from the Factor() parse will be inserted as the second child. Hence, the tree is built left-associative. */ void Exp() : {} // Exp -> Factor ("*" Factor | "/" Factor)* { Factor() ( "*" Factor() #MulExp(2) | "/" Factor() #DivExp(2) )* } /* Factor does not correspond to any concrete class. Therefore, there is no command "#Factor" in the heading of this nonterminal. This nonterminal does not create any new AST node. The top node of the stack will be the node constructed by the call to LetExp(), FPLitExp() or IdExp(). */ void Factor() : {} // Factor -> LetExp | FPLitExp | IdExp | AskUserExp { LetExp() | FPLitExp() | IdExp() | AskUserExp() } /* LetExp corresponds to the concrete class LetExp. A new LetExp node is generated by the notation "#LetExp" in the heading. The two nodes created by the BindingList() and Exp() calls will be popped off the stack and inserted as children to the new LetExp node. Then, the new LetExp node is pushed onto the stack. */ void LetExp() #LetExp: {} // LetExp -> "let" BindingList "in" Exp "ni" { BindingList() Exp() } /* BindingList corresponds to the concrete generic list class List. A new List node is generated by the notation "#List" in the heading. All the Binding nodes created by the repeated Binding() calls will be popped off the stack and inserted as children to the new List node. Then, the new List node is pushed onto the stack. */ void BindingList() #List : {} // BindingList -> Binding ("," Binding)* { Binding() ( "," Binding() )* } /* Binding corresponds to the concrete class Binding. A new Binding node is generated by the notation "#Binding" in the heading. The two nodes created by the IdExp() and Exp() calls will be popped off the stack and inserted as children to the new Binding node. Then, the new Binding node is pushed onto the stack. */ void Binding() #Binding: {} // Binding -> IdExp "=" Exp { IdExp() "=" Exp() } /* FPLitExp corresponds to the concrete class FPLitExp. A new FPLitExp node is generated by the notation "#FPLitExp" in the heading. Since this nonterminal contains no calls to other nonterminals, the new node will have no children. The new node is pushed onto the stack. The token resulting from parsing is stored in a temporary variable "t" during parsing. The "t" variable is used for setting the FPLIT String field of the FPLitExp node (by calling the setFPLIT method). The jjtThis variable refers to the node created by the "#X" command, i.e., the new FPLitExp node in this case. */ void FPLitExp() #FPLitExp: // FPLitExp -> FPLIT { Token t; } { t = { jjtThis.setFPLIT(t.image); } } /* IdExp corresponds to the concrete class IdExp. A new IdExp node is generated by the notation "#IdExp" in the heading. Since this nonterminal contains no calls to other nonterminals, the new node will have no children. The new node is pushed onto the stack. The token resulting from parsing is stored in a temporary variable "t" during parsing. The "t" variable is used for setting the ID String field of the IdExp node (by calling the setID method). The jjtThis variable refers to the node created by the "#X" command, i.e., the new IdExp node in this case. */ void IdExp() #IdExp: // IdExp -> ID { Token t; } { t = { jjtThis.setID(t.image); } } /* AskUserExp corresponds to the concrete class AskUserExp. A new AskUserExp node is generated by the notation "#AskUserExp" in the heading. The node created by the OptDefaultValue() call will be popped off the stack and inserted as a child to the new AskUserExp node. Then, the new AskUserExp node is pushed onto the stack. */ void AskUserExp() #AskUserExp: {} // AskUserExp -> "ask" "user" OptDefaultValue "?" { OptDefaultValue() "?" } /* OptDefaultValue corresponds to the concrete generic optional class Opt. A new Opt node is generated by the notation "#Opt" in the heading. If the optional Exp() is called, the resulting node will be popped off the stack and inserted as a child to the new Opt node. Otherwise, Opt will have no children. Then, the new Opt node is pushed onto the stack. */ void OptDefaultValue() #Opt: {} // OptDefaultValue -> [ Exp ] { [ Exp() ] }