Exemple #1
0
        public List <ISymbol[]> GetProductionRules(Nonterminal v)
        {
            List <ISymbol[]> productionRules = null;

            productions.TryGetValue(v, out productionRules);
            return(productionRules);
        }
Exemple #2
0
 // 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;
 }
Exemple #3
0
 // 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];
 }
Exemple #5
0
 // 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;
        }
Exemple #8
0
        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)");
            }
        }
Exemple #9
0
 // 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]);
 }
Exemple #10
0
 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);
     }
 }
Exemple #11
0
        // 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);
        }
Exemple #12
0
 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);
     }
 }
Exemple #13
0
        // 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");
            }
        }
Exemple #14
0
        // 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;
 }
Exemple #16
0
 // get the follow set for a grammar nonterminal
 public ISet <Terminal> Follow(Nonterminal startVar)
 {
     return(follow[startVar]);
 }
Exemple #17
0
 // get the follow set for a grammar nonterminal
 public ISet<Terminal> Follow(Nonterminal startVar)
 {
     return follow[startVar];
 }
 public ParseTree(Nonterminal var)
 {
     this.nonterminal = var;
 }
Exemple #19
0
        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[";"]);
        }
Exemple #20
0
        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;
        }
Exemple #21
0
 public List<ISymbol[]> GetProductionRules(Nonterminal v)
 {
     List<ISymbol[]> productionRules = null;
     productions.TryGetValue(v, out productionRules);
     return productionRules;
 }
Exemple #22
0
 public ParseTree(Nonterminal var)
 {
     this.nonterminal = var;
 }
Exemple #23
0
        // 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");
                    }
                }
            }
        }