Esempio n. 1
0
 /// <summary>
 /// Generates the abstract syntax tree based on the token list of the lexer
 /// </summary>
 /// <param name="rdp">Parser that will generate the AST</param>
 /// <param name="lexer">Lexer containing the token list</param>
 /// <returns></returns>
 private static Node GenerateAst(RecursiveDescentParser rdp, Lexer.Lexer lexer)
 {
     rdp.SetTokens(lexer.GetTokens());
     var parentNode = rdp.ParseTokens();
     if (rdp.HasTokensRemaining)
     {
         throw new UnexpectedTokenException(
             $"Reached end of parse with tokens remaining! {rdp.NextToken.StringRepresentation} on line {rdp.NextToken.LineNumber}");
     }
     return parentNode;
 }
Esempio n. 2
0
        private static void Main(string[] args)
        {
            // Loads input string from source file
            var sourceString = GetSourceFileText(args);

            // initializes lexer and RDP classes
            var lexer = new Lexer.Lexer(Lexer.Lexer.LexerLoggingMode.None);
            var rdp = new RecursiveDescentParser();

            // adds rules to lexer and parser
            RulesModule.AddLexerRules(lexer);
            RulesModule.AddRdpRules(rdp);

            try
            {
                Console.WriteLine("Parsing tokens from source...");
                // parse tokens from source using lexer
                ParseTokens(sourceString, lexer);
                // generate AST from token list
                Console.WriteLine("\n\nGenerating AST...");
                Node rootNode = GenerateAst(rdp, lexer);

                Console.WriteLine();

                // generate AST visualization and compiled code
                GenerateGraphvis(rootNode);
                GenerateCompiledCode(rootNode);
                Console.WriteLine("Success!");
            }
            catch (InvalidTokenException e)
            {
                // Handle token parsing issues
                Console.WriteLine($"Parse Error: {e.Message}");
            }
            catch (UnexpectedTokenException e)
            {
                // unexpected token / syntax error
                Console.WriteLine(e.Message);
            }
            
        }
Esempio n. 3
0
        /// <summary>
        /// Adds the following grammar rules to the RDP parser passed into the function:
        /// 
        /// base        -> stmt
        /// stmt        -> IF expr THEN stmt ELSE stmt
        ///              | WHILE expr DO stmt
        ///              | INPUT ID
        ///              | ID ASSIGN expr
        ///              | WRITE expr
        ///              | BEGIN stmtlist END
        /// stmtlist    -> stmt SEMICOLON stmtlist'
        /// stmtlist'   -> stmt SEMICOLON stmtlist'
        ///              | .
        /// expr        -> term expr'
        /// expr'       -> addop term expr'
        ///              | .
        /// term        -> factor term'
        /// term'       -> mulop factor term'
        ///              | .
        /// factor      -> LPAR expr RPAR
        ///              | ID
        ///              | NUM
        ///              | SUB NUM
        /// addop       -> ADD | SUB
        /// mulop       -> MUL | DIV
        /// </summary>
        /// <param name="parser">Parser that should accept the rules</param>
        public static void AddRdpRules(RecursiveDescentParser parser)
        {
            // base -> stmt
            parser.AddRule(
                name: "base",
                decider: rdp => true,
                evaluator: rdp => rdp.InvokeRule("stmt"));

            // stmt -> IF expr THEN stmt ELSE stmt
            parser.AddRule( // if
                name: "stmt",
                decider: rdp => rdp.TryMatch(TokenType.If),
                evaluator: rdp =>
                {
                    var node = new Node {Contents = "IF Statement", Type = NodeType.If};
                    node.AddChild(rdp.TryConsumeToken(TokenType.If));
                    node.AddChild(rdp.InvokeRule("expr"));
                    node.AddChild(rdp.TryConsumeToken(TokenType.Then));
                    node.AddChild(rdp.InvokeRule("stmt"));
                    node.AddChild(rdp.TryConsumeToken(TokenType.Else));
                    node.AddChild(rdp.InvokeRule("stmt"));
                    return node;
                });

            //  stmt -> WHILE expr DO stmt
            parser.AddRule( // while
                name: "stmt",
                decider: rdp => rdp.TryMatch(TokenType.While),
                evaluator: rdp =>
                {
                    var node = new Node {Contents = "WHILE Statement", Type = NodeType.While};
                    node.AddChild(rdp.TryConsumeToken(TokenType.While));
                    node.AddChild(rdp.InvokeRule("expr"));
                    node.AddChild(rdp.TryConsumeToken(TokenType.Do));
                    node.AddChild(rdp.InvokeRule("stmt"));
                    return node;
                });

            // stmt -> INPUT ID
            parser.AddRule( // input
                name: "stmt",
                decider: rpd => rpd.TryMatch(TokenType.Input),
                evaluator: rdp =>
                {
                    var node = new Node {Contents = "INPUT Statement", Type = NodeType.Input};
                    node.AddChild(rdp.TryConsumeToken(TokenType.Input));
                    node.AddChild(rdp.TryConsumeToken(TokenType.Id));
                    return node;
                });

            // stmt -> ID ASS expr
            parser.AddRule( // assign
                name: "stmt",
                decider: rdp => rdp.TryMatch(TokenType.Id),
                evaluator: rdp =>
                {
                    var node = new Node {Contents = "Assign Statement", Type = NodeType.Assign};
                    node.AddChild(rdp.TryConsumeToken(TokenType.Id));
                    node.AddChild(rdp.TryConsumeToken(TokenType.Assign));
                    node.AddChild(rdp.InvokeRule("expr"));
                    return node;
                });

            // stmt -> WRITE expr
            parser.AddRule( // write
                name: "stmt",
                decider: rdp => rdp.TryMatch(TokenType.Write),
                evaluator: rdp =>
                {
                    var node = new Node {Contents = "WRITE Statement", Type = NodeType.Write};
                    node.AddChild(rdp.TryConsumeToken(TokenType.Write));
                    node.AddChild(rdp.InvokeRule("expr"));
                    return node;
                });

            // stmt -> BEGIN stmtlist END
            parser.AddRule( // begin
                name: "stmt",
                decider: rdp => rdp.TryMatch(TokenType.Begin),
                evaluator: rdp =>
                {
                    var node = new Node {Contents = "BEGIN Statement", Type = NodeType.Begin};
                    node.AddChild(rdp.TryConsumeToken(TokenType.Begin));
                    node.AddChild(rdp.InvokeRule("stmtlist"));
                    node.AddChild(rdp.TryConsumeToken(TokenType.End));
                    return node;
                });

            // stmtlist -> stmt SEMICOLON stmtlist'
            parser.AddRule(
                name: "stmtlist",
                decider: rdp => rdp.TryMatch("stmt"),
                evaluator: rdp =>
                {
                    var node = new Node {Contents = "Statement List", Type = NodeType.StatementList};
                    node.AddChild(rdp.InvokeRule("stmt"));
                    node.AddChild(rdp.TryConsumeToken(TokenType.Semicolon));
                    node.AddChild(rdp.InvokeRule("stmtlist'"));
                    return node;
                });

            // Transformed rule
            // stmtlist' -> stmt SEMICOLON stmtlist'
            parser.AddRule(
                name: "stmtlist'",
                decider: rdp => rdp.TryMatch("stmt"),
                evaluator: rdp =>
                {
                    var node = new Node {Contents = "Statement List", Type = NodeType.MoreStatements};
                    
                    node.AddChild(rdp.InvokeRule("stmt"));
                    node.AddChild(rdp.TryConsumeToken(TokenType.Semicolon));
                    node.AddChild(rdp.InvokeRule("stmtlist'"));
                    return node;
                });

            // Transformed rule
            // stmtlist' -> .
            parser.AddRule(
                name: "stmtlist'",
                decider: rdp => !rdp.TryMatch("stmt"),
                evaluator: rdp => new Node {Contents = "Null Statement", Type = NodeType.Null});

            // expr -> term expr'
            parser.AddRule(
                name: "expr",
                decider: rdp => rdp.TryMatch("term"),
                evaluator: rdp =>
                {
                    var node = new Node {Contents = "Expr Statement", Type = NodeType.Expression};
                    node.AddChild(rdp.InvokeRule("term"));
                    node.AddChild(rdp.InvokeRule("expr'"));
                    return node;
                });

            // Transformed rule
            // expr' -> addop term expr'
            parser.AddRule(
                name: "expr'",
                decider: rdp => rdp.TryMatch("addop"),
                evaluator: rdp =>
                {
                    var node = new Node {Contents = "Expr' Statement", Type = NodeType.MoreExpression};
                    node.AddChild(rdp.InvokeRule("addop"));
                    node.AddChild(rdp.InvokeRule("term"));
                    node.AddChild(rdp.InvokeRule("expr'"));
                    return node;
                });

            // Transformed rule
            // expr' -> .
            parser.AddRule(
                name: "expr'",
                decider: rdp => !rdp.TryMatch("addop"),
                evaluator: rdp => new Node {Contents = "Null expr", Type = NodeType.Null});

            // term -> factor term'
            parser.AddRule(
                name: "term",
                decider: rdp => rdp.TryMatch("factor"),
                evaluator: rdp =>
                {
                    var node = new Node {Contents = "Term statement", Type = NodeType.Term};
                    node.AddChild(rdp.InvokeRule("factor"));
                    node.AddChild(rdp.InvokeRule("term'"));
                    return node;
                });

            // Transformed rule
            // term' -> mulop factor term'
            parser.AddRule(
                name: "term'",
                decider: rdp => rdp.TryMatch("mulop"),
                evaluator: rdp =>
                {
                    var node = new Node {Contents = "Term' statement", Type = NodeType.MoreTerms};
                    node.AddChild(rdp.InvokeRule("mulop"));
                    node.AddChild(rdp.InvokeRule("factor"));
                    node.AddChild(rdp.InvokeRule("term'"));
                    return node;
                });

            // Transformed rule
            // term' -> .
            parser.AddRule(
                name: "term'",
                decider: rdp => !rdp.TryMatch("mulop"),
                evaluator: rdp => new Node {Contents = "Null term", Type = NodeType.Null});

            // mulop -> MUL
            parser.AddRule(
                name: "mulop",
                decider: rdp => rdp.TryMatch(TokenType.Mul),
                evaluator: rdp => rdp.TryConsumeToken(TokenType.Mul));

            // mulop -> DIV
            parser.AddRule(
                name: "mulop",
                decider: rdp => rdp.TryMatch(TokenType.Div),
                evaluator: rdp => rdp.TryConsumeToken(TokenType.Div));

            // addop -> ADD
            parser.AddRule(
                name: "addop",
                decider: rdp => rdp.TryMatch(TokenType.Add),
                evaluator: rdp => rdp.TryConsumeToken(TokenType.Add));

            // addop -> SUB
            parser.AddRule(
                name: "addop",
                decider: rdp => rdp.TryMatch(TokenType.Sub),
                evaluator: rdp => rdp.TryConsumeToken(TokenType.Sub));

            // factor -> LPAR expr RPAR
            parser.AddRule(
                name: "factor",
                decider: rdp => rdp.TryMatch(TokenType.LPar),
                evaluator: rdp =>
                {
                    var node = new Node {Contents = "Factor statement", Type = NodeType.Factor};
                    node.AddChild(rdp.TryConsumeToken(TokenType.LPar));
                    node.AddChild(rdp.InvokeRule("expr"));
                    node.AddChild(rdp.TryConsumeToken(TokenType.RPar));
                    return node;
                });

            // factor -> ID
            parser.AddRule(
                name: "factor",
                decider: rdp => rdp.TryMatch(TokenType.Id),
                evaluator: rdp =>
                {
                    var node = new Node {Contents = "Factor statement", Type = NodeType.Factor};
                    node.AddChild(rdp.TryConsumeToken(TokenType.Id));
                    return node;
                });

            // factor -> NUM
            parser.AddRule(
                name: "factor",
                decider: rdp => rdp.TryMatch(TokenType.Num),
                evaluator: rdp =>
                {
                    var node = new Node {Contents = "Factor statement", Type = NodeType.Factor};
                    node.AddChild(rdp.TryConsumeToken(TokenType.Num));
                    return node;
                });

            // factor -> SUB NUM
            parser.AddRule(
                name: "factor",
                decider: rdp => rdp.TryMatch(TokenType.Sub),
                evaluator: rdp =>
                {
                    var node = new Node {Contents = "Factor statement", Type = NodeType.Factor};
                    node.AddChild(rdp.TryConsumeToken(TokenType.Sub));
                    node.AddChild(rdp.TryConsumeToken(TokenType.Num));
                    return node;
                });
        }
Esempio n. 4
0
 /// <summary>
 /// Constructor for ParsingRule Class
 /// </summary>
 /// <param name="parent">Recursive Descent Parser to which the rule belongs</param>
 public ParsingRule(RecursiveDescentParser parent)
 {
     _parent = parent;
     _productions = new Dictionary<ProductionDecider, ProductionEvaluator>();
 }