/// <summary> /// Processes the rule name reference. /// Depending on the current parser walker context this can be: /// 1) new rule definition; /// 2) reference to an existing rule from excluded global rules list; /// 3) reference to an existing rule from the rule body. /// </summary> /// <param name="match">Current parse tree node</param> private void EnterRuleName(ITextParseTreeNode match) { var ruleName = match.GetImage(); if (match.Parent.RuleName == ExtGrammarRules.Rule) { rules.Add(new RuleInfo { Name = ruleName }); output.Write("{0}Rule", ruleName); } else { referencedRules.Add(ruleName); if (match.Parent.RuleName == ExtGrammarRules.ExcludedGlobalRules) { excludedGlobalRules.Add(ruleName); } else { ruleBodyExpression.AddChild(new RuleExpression(ruleName)); } } }
/// <summary> /// Adds alternate expression (Rule1 | Rule2) to the expression tree for the current rule. /// </summary> /// <param name="match">Current parse tree node</param> private void EnterAlternate(ITextParseTreeNode match) { /* Alternate expressions are parsed like this: * * Rule1 | Rule2 | Rule3 * * RuleName (Rule1) * AlternateExpression * -RuleName (Rule2) * -AlternateExpression * --RuleName (Rule3) */ if (ruleBodyExpression is OneOfExpression) { // Already alternating, just add to children base.Enter(match); } else { // Start new alternate with previous expression var previousExpression = ruleBodyExpression.RemoveLastChild(); var alternateExpression = new OneOfExpression(); alternateExpression.AddChild(previousExpression); ruleBodyExpression.AddChild(alternateExpression); ruleBodyExpression = alternateExpression; base.Enter(match); ruleBodyExpression = ruleBodyExpression.Parent; } }
/// <summary> /// Generates the contents (properties and methods) for the class that defines the parser rule. /// </summary> /// <param name="match">Current parse tree node</param> private void EnterRuleBody(ITextParseTreeNode match) { ruleBodyExpression = new SequenceExpression(); output.WriteLine(" : TextProductionRule"); output.WriteLine("{"); WriteExcludedGlobalRules(); WriteRuleNameProperty(); output.WriteLine("protected override IParserRule<char> GetBody()"); output.WriteLine("{"); base.Enter(match); output.WriteLine("return"); ruleBodyExpression.Visit( new CodeGenVisitor(grammarName, output)); output.WriteLine(";"); output.WriteLine("}"); }
/// <summary> /// Enters the parse tree node, that represents matched rule. /// </summary> /// <param name="match">Match of the text parser rule</param> protected virtual void Enter(ITextParseTreeNode match) { foreach (var child in match.Children) { Enter(child); } }
/// <summary> /// Writes the rule name to the output and enters all the matched children rules. /// </summary> /// <param name="match">Match of the text parser rule</param> protected sealed override void Enter(ITextParseTreeNode match) { WriteMatch(match); ++indentLevel; base.Enter(match); --indentLevel; }
/// <summary> /// Adds quantifier expression (*, +) to the expression tree for the current rule. /// </summary> /// <param name="match">Current parse tree node</param> private void EnterQuantifier <TExpression>(ITextParseTreeNode match) where TExpression : ExpressionTreeNode, new() { var previousExpression = ruleBodyExpression.RemoveLastChild(); var quantifierExpression = new TExpression(); quantifierExpression.AddChild(previousExpression); ruleBodyExpression.AddChild(quantifierExpression); }
/// <summary> /// Adds simple expression to the expression tree for the current rule. /// There are some expressions that don't require any special handling /// and just need to be added to the expression tree at the point where /// they are met in the parse tree. /// </summary> /// <param name="match">Current parse tree node</param> private void EnterSimpleExpression <TExpression>(ITextParseTreeNode match) where TExpression : ExpressionTreeNode, new() { var newExpression = new TExpression(); ruleBodyExpression.AddChild(newExpression); ruleBodyExpression = newExpression; base.Enter(match); ruleBodyExpression = ruleBodyExpression.Parent; }
/// <summary> /// Adds character range expression ('x'..'y') to the expression tree for the current rule. /// </summary> /// <param name="match">Current parse tree node</param> private void EnterCharacterRange(ITextParseTreeNode match) { if (match.Children.Count != 2) { throw new InvalidOperationException("Character range match is expected to have exactly two children"); } ruleBodyExpression.AddChild( new CharacterRangeExpression( match.Children[0].GetImage(), match.Children[1].GetImage())); }
/// <summary> /// Writes rule name with additional information (like string literal or character range) /// to the output and enters all the matched children rules. /// </summary> /// <param name="match">Match of the text parser rule</param> protected override void WriteMatch(ITextParseTreeNode match) { if (match.RuleName == ExtGrammarRules.RuleName) { Write(match.RuleName + " (" + match.GetImage() + ")"); } else if (match.RuleName == ExtGrammarRules.LiteralValue || match.RuleName == ExtGrammarRules.CharacterRange) { Write(match.RuleName + " " + match.GetImage()); } else { base.WriteMatch(match); } }
/// <summary> /// Generates rules namespace (usings block and rules classes). /// </summary> /// <param name="match">Current parse tree node</param> private void EnterGrammar(ITextParseTreeNode match) { output.WriteLine("namespace {0}.{1}.Rules", namespaceName, grammarName); output.WriteLine("{"); output.WriteLine("using System.Collections.Generic;"); output.WriteLine("using ExtParser.Core;"); output.WriteLine("using ExtParser.Text;"); base.Enter(match); output.WriteLine("}"); WriteRuleNameConstants(); WriteParser(); }
/// <summary> /// Generates rule class stub. /// </summary> /// <param name="match">Current parse tree node</param> private void EnterRule(ITextParseTreeNode match) { output.Write("public partial class "); ruleBodyExpression = null; excludedGlobalRules = null; base.Enter(match); if (ruleBodyExpression == null) { // Rules without a body treated as terminals. // There are basically two types of rules - production and terminal. All productions // can be expressed through the grammar. If there was no body defined, then most likely // it was meant to be a light-weight terminal rule. output.WriteLine(" : TerminalParserRule<char>"); output.WriteLine("{"); WriteRuleNameProperty(); } output.WriteLine("}"); }
/// <summary> /// Walks the parse tree over the given input. /// </summary> /// <param name="parseTree">Parse tree for the given input</param> public void Walk(ITextParseTreeNode parseTree) { Enter(parseTree); }
/// <summary> /// Dispatches the logic based on the matched rule. /// </summary> /// <param name="match">Information about the matched rule</param> protected override void Enter(ITextParseTreeNode match) { switch (match.RuleName) { case ExtGrammarRules.Grammar: EnterGrammar(match); break; case ExtGrammarRules.Rule: EnterRule(match); break; case ExtGrammarRules.RuleName: EnterRuleName(match); break; case ExtGrammarRules.IsGlobalRule: EnterIsGlobalRule(match); break; case ExtGrammarRules.ExcludedGlobalRules: EnterExcludedGlobalRules(match); break; case ExtGrammarRules.RuleBody: EnterRuleBody(match); break; // Body rules case ExtGrammarRules.ExpressionGroup: EnterSimpleExpression <SequenceExpression>(match); break; case ExtGrammarRules.OptionalExpression: EnterSimpleExpression <OptionalExpression>(match); break; case ExtGrammarRules.ZeroOrMoreTimes: EnterQuantifier <ZeroOrMoreTimesExpression>(match); break; case ExtGrammarRules.OneOrMoreTimes: EnterQuantifier <OneOrMoreTimesExpression>(match); break; case ExtGrammarRules.AlternateExpression: EnterAlternate(match); break; case ExtGrammarRules.LiteralValue: EnterLiteralValue(match); break; case ExtGrammarRules.CharacterRange: EnterCharacterRange(match); break; // Default default: base.Enter(match); break; } }
/// <summary> /// Resets the <see cref="excludedGlobalRules"/> list and visits all children elements /// that are expected to be excluded rule names. /// </summary> /// <param name="match">Current parse tree node</param> private void EnterExcludedGlobalRules(ITextParseTreeNode match) { excludedGlobalRules = new List <string>(); base.Enter(match); }
/// <summary> /// Marks current rule as global. /// </summary> /// <param name="match">Current parse tree node</param> private void EnterIsGlobalRule(ITextParseTreeNode match) { rules[rules.Count - 1].IsGlobal = true; }
/// <summary> /// Writes the rule name with the current indentation level. /// </summary> /// <param name="match">Match of the text parser rule</param> protected virtual void WriteMatch(ITextParseTreeNode match) { Write(match.RuleName); }
/// <summary> /// Adds literal value expression ("abc") to the expression tree for the current rule. /// </summary> /// <param name="match">Current parse tree node</param> private void EnterLiteralValue(ITextParseTreeNode match) { ruleBodyExpression.AddChild( new LiteralExpression(match.GetImage())); }