public List <ISymbol[]> GetProductionRules(Nonterminal v) { List <ISymbol[]> productionRules = null; productions.TryGetValue(v, out productionRules); return(productionRules); }
// create a context free grammar object with some starting symbol and sets of terminals and nonterminals public CFG(Nonterminal start, ICollection <Terminal> terminals, ICollection <Nonterminal> nonterminals) { this.startSymbol = start; this.terminals = terminals.ToList(); this.terminals.Add(Terminal.EOF); this.nonterminals = nonterminals; }
// create a context free grammar object with some starting symbol and sets of terminals and nonterminals public CFG(Nonterminal start, ICollection<Terminal> terminals, ICollection<Nonterminal> nonterminals) { this.startSymbol = start; this.terminals = terminals.ToList(); this.terminals.Add(Terminal.EOF); this.nonterminals = nonterminals; }
// can get production from nonterminal and terminal as usual public ISymbol[] Get(Nonterminal var, Terminal term) { if (!table.ContainsKey(var)) return null; if (!table[var].ContainsKey(term)) return null; return table[var][term]; }
// Add a production for some (nonterminal, terminal) pair to the table public void Add(Nonterminal var, Terminal term, ISymbol[] production) { if (!table.ContainsKey(var)) { table[var] = new Dictionary <Terminal, ISymbol[]>(); } table[var][term] = production; }
public Parser(CFG grammar, Terminal syncTerm) { this.grammar = grammar; this.table = grammar.CreateLL1ParseTable(); this.start = grammar.StartSymbol; this.syncTerm = syncTerm; if (table == null) throw new Exception("GRAMMAR NOT LL(1)"); }
// or from a nonterminal and a token, in which case we need to check // all the terminals for the given nonterminal and see if one matches the token public ISymbol[] Get(Nonterminal var, Token token) { Dictionary<Terminal, ISymbol[]> tableRow = table[var]; foreach (Terminal term in tableRow.Keys) if (term.Matches(token)) return tableRow[term]; return null; }
public Parser(CFG grammar, Terminal syncTerm) { this.grammar = grammar; this.table = grammar.CreateLL1ParseTable(); this.start = grammar.StartSymbol; this.syncTerm = syncTerm; if (table == null) { throw new Exception("GRAMMAR NOT LL(1)"); } }
// can get production from nonterminal and terminal as usual public ISymbol[] Get(Nonterminal var, Terminal term) { if (!table.ContainsKey(var)) { return(null); } if (!table[var].ContainsKey(term)) { return(null); } return(table[var][term]); }
public void AddProductionRule(Nonterminal v, ISymbol[] result) { if (productions.ContainsKey(v)) { List <ISymbol[]> vProductions = productions[v]; vProductions.Add(result); } else { List <ISymbol[]> vProductions = new List <ISymbol[]>(); vProductions.Add(result); productions.Add(v, vProductions); } }
// or from a nonterminal and a token, in which case we need to check // all the terminals for the given nonterminal and see if one matches the token public ISymbol[] Get(Nonterminal var, Token token) { Dictionary <Terminal, ISymbol[]> tableRow = table[var]; foreach (Terminal term in tableRow.Keys) { if (term.Matches(token)) { return(tableRow[term]); } } return(null); }
public void AddProductionRule(Nonterminal v, ISymbol[] result) { if (productions.ContainsKey(v)) { List<ISymbol[]> vProductions = productions[v]; vProductions.Add(result); } else { List<ISymbol[]> vProductions = new List<ISymbol[]>(); vProductions.Add(result); productions.Add(v, vProductions); } }
// iteratively computer the follow set for each grammar nonterminal private void ComputeFollowSets() { if (Program.debug) { Console.WriteLine("========================================================="); Console.WriteLine("CFG: Computing follow sets"); } // initialize follow sets for each nonterminal by going through every production // and finding terminals that come immediately after nonterminals in the productions foreach (Nonterminal A in nonterminals) { follow[A] = new HashSet <Terminal>(); if (A == startSymbol) { follow[A].Add(Terminal.EOF); } foreach (Nonterminal B in nonterminals) { foreach (ISymbol[] prod in productions[B]) { for (int i = 0; i < prod.Length - 1; i++) { if (prod[i] == A && prod[i + 1] is Terminal) { follow[A].Add(prod[i + 1] as Terminal); } } } } if (Program.debug) { Console.WriteLine("CFG: Follow " + A + " = " + SymbolsToString(follow[A])); } } if (Program.debug) { Console.WriteLine("CFG: Initial step done"); } // loop until follow sets unchanged bool converged = false; while (!converged) { converged = true; if (Program.debug) { Console.WriteLine("--iteration--"); } // foreach nonterminal and for each of its productions foreach (Nonterminal B in nonterminals) { foreach (ISymbol[] prod in productions[B]) { // for every nonterminal in the production for (int i = 0; i < prod.Length; i++) { if (prod[i] is Nonterminal) { // A is a nonterminal symbol in a production rule for B Nonterminal A = prod[i] as Nonterminal; int tmp = follow[A].Count; // A is last symbol in a production of B, add Follow(B) to Follow(A) if (i == prod.Length - 1) { follow[A].UnionWith(follow[B]); } else { ISymbol[] w = prod.Skip(i + 1).ToArray(); // rest of the production after A ISet <Terminal> fw = First(w); // if epsilon can be derived from symbols following A in the production // add Follow(B) to Follow(A) if (fw.Contains(Terminal.EPSILON)) { follow[A].UnionWith(follow[B]); fw.Remove(Terminal.EPSILON); } // add first set of the symbols following A in the production to Follow(A) in any case follow[A].UnionWith(fw); } if (follow[A].Count > tmp) { // a follow set was changed if (Program.debug) { Console.WriteLine("CFG: used rule " + B + " -> " + SymbolsToString(prod) + "for " + A); Console.WriteLine("CFG: Follow " + A + " = " + SymbolsToString(follow[A])); Console.WriteLine(); } converged = false; } } } } } if (Program.debug) { foreach (Nonterminal A in nonterminals) { Console.WriteLine("CFG: Follow " + A + " = " + SymbolsToString(follow[A])); } } } if (Program.debug) { Console.WriteLine("CFG: follow sets converged"); } }
// Parse the given stream of tokens public ParseTree Parse(IEnumerable <Token> tokenSource) { isValidParseTree = true; errors = new List <Error>(); Stack <ISymbol> symbolStack = new Stack <ISymbol>(); symbolStack.Push(Terminal.EOF); symbolStack.Push(start); ParseTree parseTree = new ParseTree(start); Stack <IParseNode> treeStack = new Stack <IParseNode>(); treeStack.Push(new ParseLeaf(Terminal.EOF)); treeStack.Push(parseTree); IEnumerator <Token> tokenStream = tokenSource.GetEnumerator(); tokenStream.MoveNext(); while (symbolStack.Count > 0) { if (Program.debug) { Console.WriteLine("========================================================="); Console.WriteLine(" PARSE: Stack " + SymbolsToString(symbolStack)); Console.WriteLine(" PARSE: expecting " + symbolStack.Peek()); Console.WriteLine(" PARSE: token " + tokenStream.Current); } // ignore error tokens if (tokenStream.Current.Type == TokenType.ERROR) { if (Program.debug) { Console.WriteLine(" PARSE: skipping error token"); } errors.Add(new LexicalError(tokenStream.Current)); tokenStream.MoveNext(); continue; } if (symbolStack.Peek() is Terminal) { Terminal term = symbolStack.Peek() as Terminal; ParseLeaf leaf = treeStack.Peek() as ParseLeaf; if (term == Terminal.EPSILON) { // epsilon production was used, exclude from parse tree if (Program.debug) { Console.WriteLine(" PARSE: ignore epsilon"); } symbolStack.Pop(); treeStack.Pop(); } else if (term.Matches(tokenStream.Current)) { // current token matches the top of the parse stack, add it to parse tree if (Program.debug) { Console.WriteLine(" PARSE: Terminal match"); } leaf.Token = tokenStream.Current; tokenStream.MoveNext(); symbolStack.Pop(); treeStack.Pop(); } else { // current token does no match, recover from error if (Program.debug) { Console.WriteLine(" PARSE: Error, Terminal mismatch"); } errors.Add(new SyntaxError(tokenStream.Current)); Synchronize(symbolStack, treeStack, tokenStream); } } else // top of stack is a nonterminal { Nonterminal var = symbolStack.Pop() as Nonterminal; IParseNode popped = treeStack.Pop(); ParseTree subtree = popped as ParseTree; ISymbol[] production = table.Get(var, tokenStream.Current); if (production == null) { // cannot derive the current token from the nonterminal at the top of the stack if (Program.debug) { Console.WriteLine(" PARSE: Error, No such production"); } symbolStack.Push(var); treeStack.Push(popped); errors.Add(new SyntaxError(tokenStream.Current)); Synchronize(symbolStack, treeStack, tokenStream); } else { // use the production specified by the parse table, add node to parse tree if (Program.debug) { Console.WriteLine(" PARSE: Using production " + SymbolsToString(production)); } for (int i = production.Length - 1; i >= 0; i--) { IParseNode treeChild; if (production[i] is Terminal) { treeChild = new ParseLeaf(production[i] as Terminal); } else { treeChild = new ParseTree(production[i] as Nonterminal); } subtree.Children.Insert(0, treeChild); treeStack.Push(treeChild); symbolStack.Push(production[i]); } } } } if (Program.debug) { Console.WriteLine(parseTree); } return(parseTree); }
// Add a production for some (nonterminal, terminal) pair to the table public void Add(Nonterminal var, Terminal term, ISymbol[] production) { if (!table.ContainsKey(var)) table[var] = new Dictionary<Terminal, ISymbol[]>(); table[var][term] = production; }
// get the follow set for a grammar nonterminal public ISet <Terminal> Follow(Nonterminal startVar) { return(follow[startVar]); }
// get the follow set for a grammar nonterminal public ISet<Terminal> Follow(Nonterminal startVar) { return follow[startVar]; }
public ParseTree(Nonterminal var) { this.nonterminal = var; }
private MiniPL() { // Create NFA-type things for tokens using regular operations Regex reBlockCommentStart = Regex.Concat("/*"); Regex reBlockCommentEnd = Regex.Concat("*/"); Regex reLineComment = Regex.Concat("//").Concat(Regex.Not('\n').Star()); Regex reWhitespace = Regex.Union(" \t\r\n").Star().Union(reLineComment); Regex reString = Regex.Char('"').Concat(Regex.Char('\\').Concat(Regex.Any()).Union(Regex.Not('"', '\\')).Star()).Concat(Regex.Char('"')); Regex reBinaryOperator = Regex.Union("+-*/<=&"); Regex reUnaryOperator = Regex.Char('!'); Regex reKeyword = Regex.Union(Regex.Concat("var"), Regex.Concat("for"), Regex.Concat("end"), Regex.Concat("in"), Regex.Concat("do"), Regex.Concat("read"), Regex.Concat("print"), Regex.Concat("assert")); Regex reType = Regex.Union(Regex.Concat("bool"), Regex.Concat("int"), Regex.Concat("string")); Regex reParenRight = Regex.Char(')'), reParenLeft = Regex.Char('('), reColon = Regex.Char(':'), reSemicolon = Regex.Char(';'), reAssignment = Regex.Concat(":="), reDots = Regex.Concat(".."); Regex reIdentifier = Regex.Union(Regex.Range('A', 'Z'), Regex.Range('a', 'z')) .Concat(Regex.Union(Regex.Range('A', 'Z'), Regex.Range('a', 'z'), Regex.Range('0', '9'), Regex.Char('_')).Star()); Regex reInteger = Regex.Range('0', '9').Plus(); // Define token types tokenTypes["block_comment_start"] = new TokenType("block_comment_start", reBlockCommentStart); tokenTypes["block_comment_end"] = new TokenType("block_comment_end", reBlockCommentEnd); tokenTypes["int"] = new TokenType("int", reInteger); tokenTypes["whitespace"] = new TokenType("whitespace", reWhitespace, priority: TokenType.Priority.Whitespace); tokenTypes["string"] = new TokenType("string", reString); tokenTypes["binary_op"] = new TokenType("binary op", reBinaryOperator); tokenTypes["unary_op"] = new TokenType("unary op", reUnaryOperator); tokenTypes["keyword"] = new TokenType("keyword", reKeyword, priority: TokenType.Priority.Keyword); tokenTypes["type"] = new TokenType("type", reType, priority: TokenType.Priority.Keyword); tokenTypes["left_paren"] = new TokenType("left paren", reParenLeft); tokenTypes["right_paren"] = new TokenType("right paren", reParenRight); tokenTypes["colon"] = new TokenType("colon", reColon); tokenTypes["semicolon"] = new TokenType("semicolon", reSemicolon); tokenTypes["assignment"] = new TokenType("assignment", reAssignment); tokenTypes["dots"] = new TokenType("dots", reDots); tokenTypes["identifier"] = new TokenType("identifier", reIdentifier); // create combined automaton and scanner object TokenAutomaton automaton = TokenType.CombinedAutomaton(tokenTypes.Values.ToArray()); scanner = new Scanner(automaton, tokenTypes["block_comment_start"], tokenTypes["block_comment_end"]); // Define nonterminal variables of CFG nonterminals["program"] = new Nonterminal("PROG"); nonterminals["statements"] = new Nonterminal("STMTS"); nonterminals["statements_head"] = new Nonterminal("STMTS_HEAD"); nonterminals["statements_tail"] = new Nonterminal("STMTS_TAIL"); nonterminals["statement"] = new Nonterminal("STMT"); nonterminals["declaration"] = new Nonterminal("DECL"); nonterminals["declaration_assignment"] = new Nonterminal("DECL_ASSIGN"); nonterminals["expression"] = new Nonterminal("EXPR"); nonterminals["unary_operation"] = new Nonterminal("UNARY_OP"); nonterminals["binary_operation"] = new Nonterminal("BINARY_OP"); nonterminals["operand"] = new Nonterminal("OPND"); // Define terminal variables of CFG terminals["identifier"] = new Terminal(tokenTypes["identifier"]); terminals["assert"] = new Terminal("assert"); terminals["print"] = new Terminal("print"); terminals["read"] = new Terminal("read"); terminals["for"] = new Terminal("for"); terminals["in"] = new Terminal("in"); terminals["end"] = new Terminal("end"); terminals["do"] = new Terminal("do"); terminals["var"] = new Terminal("var"); terminals["type"] = new Terminal(tokenTypes["type"]); terminals["string"] = new Terminal(tokenTypes["string"]); terminals["int"] = new Terminal(tokenTypes["int"]); terminals[")"] = new Terminal(")"); terminals["("] = new Terminal("("); terminals[".."] = new Terminal(".."); terminals[":="] = new Terminal(":="); terminals[":"] = new Terminal(":"); terminals[";"] = new Terminal(";"); terminals["binary_operator"] = new Terminal(tokenTypes["binary_op"]); terminals["unary_operator"] = new Terminal(tokenTypes["unary_op"]); // Create the Mini-PL grammar grammar = new CFG(nonterminals["program"], terminals.Values, nonterminals.Values); // define production rules for the grammar grammar.AddProductionRule(nonterminals["program"], new ISymbol[] { nonterminals["statements"] }); grammar.AddProductionRule(nonterminals["statements"], new ISymbol[] { nonterminals["statements_head"], nonterminals["statements_tail"] }); grammar.AddProductionRule(nonterminals["statements_head"], new ISymbol[] { nonterminals["statement"], terminals[";"] }); grammar.AddProductionRule(nonterminals["statements_tail"], new ISymbol[] { nonterminals["statements_head"], nonterminals["statements_tail"] }); grammar.AddProductionRule(nonterminals["statements_tail"], new ISymbol[] { Terminal.EPSILON }); grammar.AddProductionRule(nonterminals["statement"], new ISymbol[] { nonterminals["declaration"] }); grammar.AddProductionRule(nonterminals["statement"], new ISymbol[] { terminals["identifier"], terminals[":="], nonterminals["expression"] }); grammar.AddProductionRule(nonterminals["statement"], new ISymbol[] { terminals["for"], terminals["identifier"], terminals["in"], nonterminals["expression"], terminals[".."], nonterminals["expression"], terminals["do"], nonterminals["statements"], terminals["end"], terminals["for"] }); grammar.AddProductionRule(nonterminals["statement"], new ISymbol[] { terminals["read"], terminals["identifier"] }); grammar.AddProductionRule(nonterminals["statement"], new ISymbol[] { terminals["print"], nonterminals["expression"] }); grammar.AddProductionRule(nonterminals["statement"], new ISymbol[] { terminals["assert"], terminals["("], nonterminals["expression"], terminals[")"] }); grammar.AddProductionRule(nonterminals["declaration"], new ISymbol[] { terminals["var"], terminals["identifier"], terminals[":"], terminals["type"], nonterminals["declaration_assignment"] }); grammar.AddProductionRule(nonterminals["declaration_assignment"], new ISymbol[] { terminals[":="], nonterminals["expression"] }); grammar.AddProductionRule(nonterminals["declaration_assignment"], new ISymbol[] { Terminal.EPSILON }); grammar.AddProductionRule(nonterminals["expression"], new ISymbol[] { nonterminals["unary_operation"] }); grammar.AddProductionRule(nonterminals["expression"], new ISymbol[] { nonterminals["operand"], nonterminals["binary_operation"] }); grammar.AddProductionRule(nonterminals["unary_operation"], new ISymbol[] { terminals["unary_operator"], nonterminals["operand"] }); grammar.AddProductionRule(nonterminals["binary_operation"], new ISymbol[] { terminals["binary_operator"], nonterminals["operand"] }); grammar.AddProductionRule(nonterminals["binary_operation"], new ISymbol[] { Terminal.EPSILON }); grammar.AddProductionRule(nonterminals["operand"], new ISymbol[] { terminals["int"] }); grammar.AddProductionRule(nonterminals["operand"], new ISymbol[] { terminals["string"] }); grammar.AddProductionRule(nonterminals["operand"], new ISymbol[] { terminals["identifier"] }); grammar.AddProductionRule(nonterminals["operand"], new ISymbol[] { terminals["("], nonterminals["expression"], terminals[")"] }); // use ; as synchronizing token for Mini-PL parser = new Parser(grammar, terminals[";"]); }
public Runnable ProcessParseTree(ParseTree parseTree, IEnumerable<Error> parseErrors, bool isValidParseTree) { Runnable prog = new Runnable(); foreach (Error err in parseErrors) { prog.errors.Add(err); } // can't construct AST if parse tree is bad if (!isValidParseTree) return prog; // first remove unnecessary symbols ; : .. ( ) := and epsilons String[] pruneTokens = { "(", ")", ";", ":", "..", ":=", "var", "in", "for", "end", "do" }; Predicate<IParseNode> isUnnecessaryTerminal = n => (n is ParseLeaf) ? (n as ParseLeaf).Token == null || pruneTokens.Contains((n as ParseLeaf).Token.Lexeme) : false; parseTree.RemoveNodes(isUnnecessaryTerminal); // remove any tree nodes with no children Predicate<IParseNode> isEmptyNonterminal = v => (v is ParseTree) ? (v as ParseTree).Children.Count == 0 : false; parseTree.RemoveNodes(isEmptyNonterminal); // refactor // STMTS->STMTS_HEAD STMTS_TAIL to STMTS->(STMT)+ // DECL->"var" <IDENT> ":" <TYPE> ASSIGN to DECL->"var" <IDENT> ":" <TYPE> [":=" <EXPR>] // EXPR->UNARY|OPND BINARY to EXPR-> unary_op OPND | OPND | OPND binary_op OPND // OPND-><INT>|<STRING>|<IDENT>|<EXPR> to just <INT>|<STRING>|<IDENT>|<EXPR> Nonterminal[] pruneVariables = new Nonterminal[] { nonterminals["statements_head"], nonterminals["statements_tail"], nonterminals["unary_operation"], nonterminals["binary_operation"], nonterminals["declaration_assignment"], nonterminals["operand"] }; Predicate<IParseNode> isUnnecessaryNonterminal = n => (n is ParseTree) ? pruneVariables.Contains((n as ParseTree).Nonterminal) : false; parseTree.RemoveNodes(isUnnecessaryNonterminal); if (Program.debug) Console.WriteLine(parseTree); // AST is formed at this point, so do semantic checks // find declarations, produce errors if identifier declared multiple times foreach (IParseNode node in parseTree.Nodes()) { if (node is ParseTree) { ParseTree subtree = node as ParseTree; if (subtree.Nonterminal == nonterminals["declaration"]) { ParseLeaf idLeaf = (subtree.Children[0] as ParseLeaf); ParseLeaf typeLeaf = (subtree.Children[1] as ParseLeaf); Token idToken = idLeaf.Token; Token typeToken = typeLeaf.Token; string identifier = idToken.Lexeme; ValueType type = Value.TypeFromString(typeToken.Lexeme); Statement.DeclarationStmt declaration; switch (subtree.Children.Count) { case 2: // simple declaration declaration = new Statement.DeclarationStmt(identifier, type, idToken); break; case 3: // declaration with assignment ParseLeaf valueLeaf = (subtree.Children[2] as ParseLeaf); Expression expr = Expression.FromTreeNode(subtree.Children[2], terminals, nonterminals); declaration = new Statement.DeclarationStmt(identifier, type, idToken, expr); break; default: throw new Exception("BAD AST STRUCTURE"); } if (prog.declarations.ContainsKey(identifier)) prog.errors.Add(new SemanticError(idToken, identifier + " multiply defined")); else prog.declarations[identifier] = declaration; } } } // check that variables are defined before use foreach (IParseNode node in parseTree.Nodes()) { if (node is ParseLeaf) { ParseLeaf leaf = node as ParseLeaf; Token leafToken = leaf.Token; if (leafToken.Type == tokenTypes["identifier"]) { string identifier = leafToken.Lexeme; Position idPosition = leafToken.TextPosition; if (!prog.declarations.ContainsKey(identifier)) prog.errors.Add(new SemanticError(leafToken, identifier + " never defined")); else if (idPosition.CompareTo(prog.declarations[identifier].Token.TextPosition) < 0) prog.errors.Add(new SemanticError(leafToken, identifier + " not defined before use")); } } } // add statements to runnable ParseTree statementListNode = parseTree.Children[0] as ParseTree; foreach (IParseNode statementNode in statementListNode.Children) prog.statements.Add(Statement.FromTreeNode(statementNode, terminals, nonterminals)); // check that for-loop control variables are not modified inside the for-loop foreach (Statement stmt in prog.statements) { if (stmt is Statement.ForStmt) { Statement.ForStmt forStmt = stmt as Statement.ForStmt; Stack<Statement> stmtStack = new Stack<Statement>(); foreach (Statement substmt in forStmt.Block) stmtStack.Push(substmt); while (stmtStack.Count != 0) { Statement s = stmtStack.Pop(); if (s is Statement.AssignStmt) { Statement.AssignStmt assignment = s as Statement.AssignStmt; if (assignment.Identifier == forStmt.Identifier) prog.errors.Add(new SemanticError(assignment.Token, forStmt.Identifier + " cannot be modified inside for-loop")); } else if (s is Statement.DeclarationStmt) { Statement.DeclarationStmt declaration = s as Statement.DeclarationStmt; if (declaration.Identifier == forStmt.Identifier) prog.errors.Add(new SemanticError(declaration.Token, forStmt.Identifier + " cannot be modified inside for-loop")); } else if (s is Statement.ForStmt) { Statement.ForStmt nestedFor = s as Statement.ForStmt; if (nestedFor.Identifier == forStmt.Identifier) prog.errors.Add(new SemanticError(nestedFor.Token, forStmt.Identifier + " cannot be modified inside for-loop")); foreach (Statement substmt in nestedFor.Block) stmtStack.Push(substmt); } } } } // typecheck each statement foreach (Statement stmt in prog.statements) stmt.TypeCheck(prog); return prog; }
public List<ISymbol[]> GetProductionRules(Nonterminal v) { List<ISymbol[]> productionRules = null; productions.TryGetValue(v, out productionRules); return productionRules; }
// Uses phrase-level error recovery with First and Follow sets to recover from bad state private void Synchronize(Stack <ISymbol> symbolStack, Stack <IParseNode> treeStack, IEnumerator <Token> tokenStream) { // Loop until good state found (return statements) or no more symbols on stack. sync : while (symbolStack.Count > 0) { if (Program.debug) { Console.WriteLine(" PARSE: Synchronize token " + tokenStream.Current + " symbol " + symbolStack.Peek()); } // no recovery from end of file, empty the parse stack if (tokenStream.Current.Type == TokenType.EOF) { while (symbolStack.Count > 0) { isValidParseTree = false; symbolStack.Pop(); treeStack.Pop(); } if (Program.debug) { Console.WriteLine("PARSE: Unexpected EOF"); } return; } if (symbolStack.Peek() is Terminal) { Terminal t = symbolStack.Peek() as Terminal; if (t.Matches(tokenStream.Current)) { // good state reached if (Program.debug) { Console.WriteLine(" PARSE: Token matches"); } return; } if (syncTerm.Matches(tokenStream.Current)) { // special case: we have a synchronising token like ';' that we do not want to skip over // so remove symbol from parse stack if (Program.debug) { Console.WriteLine(" PARSE: Discarding symbol " + symbolStack.Peek()); } isValidParseTree = false; symbolStack.Pop(); treeStack.Pop(); continue; } // normal case: discard token and continue with recovery if (Program.debug) { Console.WriteLine(" PARSE: Discard token"); } if (!tokenStream.MoveNext()) { throw new Exception("OUT OF TOKENS"); } continue; } else { Nonterminal v = symbolStack.Peek() as Nonterminal; if (table.Get(v, tokenStream.Current) != null) { // good state reached (current token in First set of symbol at top of stack) if (Program.debug) { Console.WriteLine(" PARSE: Valid production exists"); } return; } // if current terminal could be matched by something in Follow set of // symbol at top of stack, then skip the current symbol foreach (ISymbol sym in grammar.Follow(v)) { if (sym is Terminal) { Terminal followTerm = sym as Terminal; if (followTerm.Matches(tokenStream.Current)) { if (Program.debug) { Console.WriteLine(" PARSE: Discarding symbol " + symbolStack.Peek()); } isValidParseTree = false; symbolStack.Pop(); treeStack.Pop(); goto sync; } } if (sym is Nonterminal) { Nonterminal followVar = sym as Nonterminal; if (table.Get(followVar, tokenStream.Current) != null) { if (Program.debug) { Console.WriteLine(" PARSE: Discarding symbol " + symbolStack.Peek()); } isValidParseTree = false; symbolStack.Pop(); treeStack.Pop(); goto sync; } } } // default action: skip current terminal if (Program.debug) { Console.WriteLine(" PARSE: Discard token"); } if (!tokenStream.MoveNext()) { throw new Exception("OUT OF TOKENS"); } } } }