/// <summary> /// validates whether $ variables are corresponding to valid symbols /// errors are added to the tree Error object. /// </summary> /// <param name="nts">non terminal and its production rule</param> /// <returns>a formated codeblock</returns> private void ValidateCodeBlock(ParseTree tree, NonTerminalSymbol nts, ParseNode node) { if (nts == null) { return; } string codeblock = nts.CodeBlock; Regex var = new Regex(@"\$(?<var>[a-zA-Z_0-9]+)(\[(?<index>[^]]+)\])?", RegexOptions.Compiled); Symbols symbols = nts.DetermineProductionSymbols(); MatchCollection matches = var.Matches(codeblock); foreach (Match match in matches) { Symbol s = symbols.Find(match.Groups["var"].Value); if (s == null) { tree.Errors.Add(new ParseError("Variable $" + match.Groups["var"].Value + " cannot be matched.", 0x1016, node.Token.File, node.Token.StartPos + match.Groups["var"].Index, node.Token.StartPos + match.Groups["var"].Index, match.Groups["var"].Length)); break; // error situation } } }
protected override object EvalProduction(ParseTree tree, params object[] paramlist) { Grammar g = (Grammar)paramlist[0]; if (Nodes[2].Nodes[0].Token.Type == TokenType.STRING) { TerminalSymbol term = g.Symbols.Find(Nodes[0].Token.Text) as TerminalSymbol; if (term == null) { tree.Errors.Add(new ParseError("Symbol '" + Nodes[0].Token.Text + "' is not declared. ", 0x1040, Nodes[0])); } } else { NonTerminalSymbol nts = g.Symbols.Find(Nodes[0].Token.Text) as NonTerminalSymbol; if (nts == null) { tree.Errors.Add(new ParseError("Symbol '" + Nodes[0].Token.Text + "' is not declared. ", 0x1041, Nodes[0])); } Rule r = (Rule)Nodes[2].Eval(tree, g, nts); if (nts != null) { nts.Rules.Add(r); } if (Nodes[3].Token.Type == TokenType.CODEBLOCK) { string codeblock = Nodes[3].Token.Text; nts.CodeBlock = codeblock; ValidateCodeBlock(tree, nts, Nodes[3]); // beautify the codeblock format codeblock = codeblock.Substring(1, codeblock.Length - 3).Trim(); nts.CodeBlock = codeblock; } } return(g); }
internal bool DetermineFirstTerminals(Symbols FirstTerminals, int index) { // indicates if Nonterminal can evaluate to an empty terminal (e.g. in case T -> a? or T -> a*) // in which case the parent rule should continue scanning after this nonterminal for Firsts. bool containsEmpty = false; // assume terminal is found switch (Type) { case RuleType.Terminal: if (Symbol == null) { return(true); } if (!FirstTerminals.Exists(Symbol)) { FirstTerminals.Add(Symbol); } else { Console.WriteLine("throw new Exception(\"Terminal already exists\");"); } break; case RuleType.NonTerminal: if (Symbol == null) { return(true); } NonTerminalSymbol nts = Symbol as NonTerminalSymbol; containsEmpty = nts.DetermineFirstTerminals(); // add first symbols of the nonterminal if not already added foreach (TerminalSymbol t in nts.FirstTerminals) { if (!FirstTerminals.Exists(t)) { FirstTerminals.Add(t); } else { Console.WriteLine("throw new Exception(\"Terminal already exists\");"); } } break; case RuleType.Choice: { // all subrules must be evaluated to determine if they contain first terminals // if any subrule contains an empty, then this rule also contains an empty foreach (Rule r in Rules) { containsEmpty |= r.DetermineFirstTerminals(FirstTerminals); } break; } case RuleType.OneOrMore: { // if a non-empty subrule was found, then stop further parsing. foreach (Rule r in Rules) { containsEmpty = r.DetermineFirstTerminals(FirstTerminals); if (!containsEmpty) // found the final set of first terminals { break; } } break; } case RuleType.Concat: { // if a non-empty subrule was found, then stop further parsing. // start scanning from Index for (int i = index; i < Rules.Count; i++) { containsEmpty = Rules[i].DetermineFirstTerminals(FirstTerminals); if (!containsEmpty) // found the final set of first terminals { break; } } // assign this concat rule to each terminal foreach (TerminalSymbol t in FirstTerminals) { t.Rule = this; } break; } case RuleType.Option: case RuleType.ZeroOrMore: { // empty due to the nature of this rule (A? or A* can always be empty) containsEmpty = true; // if a non-empty subrule was found, then stop further parsing. foreach (Rule r in Rules) { containsEmpty |= r.DetermineFirstTerminals(FirstTerminals); if (!containsEmpty) // found the final set of first terminals { break; } } break; } default: throw new NotImplementedException(); } return(containsEmpty); }
/// <summary> /// EvalStart will first do a semantic check to see if symbols are declared correctly /// then it will also check for attributes and parse the directives /// after that it will complete the transformation to the grammar tree. /// </summary> /// <param name="tree"></param> /// <param name="paramlist"></param> /// <returns></returns> protected override object EvalStart(ParseTree tree, params object[] paramlist) { TerminalSymbol terminal = null; bool StartFound = false; Grammar g = new Grammar(); foreach (ParseNode n in Nodes) { if (n.Token.Type == TokenType.Directive) { EvalDirective(tree, new object[] { g, n }); } if (n.Token.Type == TokenType.ExtProduction) { if (n.Nodes[n.Nodes.Count - 1].Nodes[2].Nodes[0].Token.Type == TokenType.STRING) { try { terminal = new TerminalSymbol(n.Nodes[n.Nodes.Count - 1].Nodes[0].Token.Text, (string)n.Nodes[n.Nodes.Count - 1].Nodes[2].Nodes[0].Token.Text); for (int i = 0; i < n.Nodes.Count - 1; i++) { if (n.Nodes[i].Token.Type == TokenType.Attribute) { EvalAttribute(tree, new object[] { g, terminal, n.Nodes[i] }); } } } catch (Exception ex) { tree.Errors.Add(new ParseError("regular expression for '" + n.Nodes[n.Nodes.Count - 1].Nodes[0].Token.Text + "' results in error: " + ex.Message, 0x1020, n.Nodes[0])); continue; } if (terminal.Name == "Start") { tree.Errors.Add(new ParseError("'Start' symbol cannot be a regular expression.", 0x1021, n.Nodes[0])); } if (g.Symbols.Find(terminal.Name) == null) { g.Symbols.Add(terminal); } else { tree.Errors.Add(new ParseError("Terminal already declared: " + terminal.Name, 0x1022, n.Nodes[0])); } } else { NonTerminalSymbol nts = new NonTerminalSymbol(n.Nodes[n.Nodes.Count - 1].Nodes[0].Token.Text); if (g.Symbols.Find(nts.Name) == null) { g.Symbols.Add(nts); } else { tree.Errors.Add(new ParseError("Non terminal already declared: " + nts.Name, 0x1023, n.Nodes[0])); } for (int i = 0; i < n.Nodes.Count - 1; i++) { if (n.Nodes[i].Token.Type == TokenType.Attribute) { EvalAttribute(tree, new object[] { g, nts, n.Nodes[i] }); } } if (nts.Name == "Start") { StartFound = true; } } } } if (!StartFound) { tree.Errors.Add(new ParseError("The grammar requires 'Start' to be a production rule.", 0x0024)); return(g); } foreach (ParseNode n in Nodes) { if (n.Token.Type == TokenType.ExtProduction) { n.Eval(tree, g); } } return(g); }