From 77236f2ee34acdc26ddbf720a87649085d0f0831 Mon Sep 17 00:00:00 2001 From: b3b00 Date: Tue, 21 Feb 2023 14:07:42 +0100 Subject: [PATCH] feature #343 : concrete syntax tree visitor --- ParserTests/ExplicitTokensTests.cs | 10 +- .../visitor/ConcreteSyntaxTreeWalker.cs | 111 +++++++++ .../visitor/GraphVizEBNFSyntaxTreeVisitor.cs | 219 ++++++++---------- .../visitor/IConcreteSyntaxTreeVisitor.cs | 16 ++ 4 files changed, 233 insertions(+), 123 deletions(-) create mode 100644 sly/parser/generator/visitor/ConcreteSyntaxTreeWalker.cs create mode 100644 sly/parser/generator/visitor/IConcreteSyntaxTreeVisitor.cs diff --git a/ParserTests/ExplicitTokensTests.cs b/ParserTests/ExplicitTokensTests.cs index c689d18e..a952110f 100644 --- a/ParserTests/ExplicitTokensTests.cs +++ b/ParserTests/ExplicitTokensTests.cs @@ -90,6 +90,8 @@ private BuildResult> BuildParser() var parserInstance = new ExplicitTokensParser(); var builder = new ParserBuilder(); var result = builder.BuildParser(parserInstance, ParserType.EBNF_LL_RECURSIVE_DESCENT, "expression"); + + return result; } @@ -109,6 +111,9 @@ public void BuildParserTest() Check.That(parser.IsOk).IsTrue(); Check.That(parser.Result).IsNotNull(); var r = parser.Result.Parse("2.0 - 2.0 + bozzo + Test"); + + + Check.That(r).IsOkParsing(); // grammar is left associative so expression really is // (2.0 - (2.0 + (bozzo + Test))) = 2 - ( 2 + (42 + 0)) = 2 - (2 + 42) = 2 - 44 = -42 @@ -129,9 +134,12 @@ public void BuildExpressionParserTest() var json = $@"{{ {tree.ToJson()} }}"; + var root = graphviz.VisitTree(tree); string graph = graphviz.Graph.Compile(); - + Check.That(graph).Contains(@"label=""\""bozzo\""""") + .And.Contains(@"label=""\""+\"""""); + Check.That(r.Result).IsEqualTo(2 - 2 + 42 + 0); } diff --git a/sly/parser/generator/visitor/ConcreteSyntaxTreeWalker.cs b/sly/parser/generator/visitor/ConcreteSyntaxTreeWalker.cs new file mode 100644 index 00000000..d37e5c3d --- /dev/null +++ b/sly/parser/generator/visitor/ConcreteSyntaxTreeWalker.cs @@ -0,0 +1,111 @@ +using System.Collections.Generic; +using System.Linq; +using sly.lexer; +using sly.parser.syntax.tree; + +namespace sly.parser.generator.visitor +{ + public class ConcreteSyntaxTreeWalker where IN : struct + { + + public IConcreteSyntaxTreeVisitor Visitor { get; set; } + + public ConcreteSyntaxTreeWalker(IConcreteSyntaxTreeVisitor visitor) + { + Visitor = visitor; + } + + private OUT VisitLeaf(SyntaxLeaf leaf) + { + if (leaf.Token.IsIndent) + { + return Visitor.VisitLeaf(leaf.Token); + } + else if (leaf.Token.IsUnIndent) + { + return Visitor.VisitLeaf(leaf.Token); + } + else if (leaf.Token.IsExplicit) + { + return Visitor.VisitLeaf(leaf.Token); + } + return Visitor.VisitLeaf(leaf.Token); + } + + public OUT Visit(ISyntaxNode n) + { + switch (n) + { + case SyntaxLeaf leaf: + return VisitLeaf(leaf); + case GroupSyntaxNode node: + return Visit(node); + case ManySyntaxNode node: + return Visit(node); + case OptionSyntaxNode node: + return Visit(node); + case SyntaxNode node: + return Visit(node); + case SyntaxEpsilon epsilon: + { + return Visitor.VisitEpsilon(); + } + default: + return Visitor.VisitLeaf(new Token() {TokenID = default(IN),SpanValue="NULL".ToCharArray()}); + } + } + + private OUT Visit(GroupSyntaxNode node) + { + return Visit(node as SyntaxNode); + } + + private OUT Visit(OptionSyntaxNode node) + { + var child = node.Children != null && node.Children.Any>() ? node.Children[0] : null; + if (child == null || node.IsEmpty) + { + Visitor.VisitOptionNode(false, default(OUT)); + } + var r = Visit(child); + return r; + } + + + private OUT Visit(SyntaxNode node) + { + + var children = new List(); + + foreach (var n in node.Children) + { + var v = Visit(n); + + children.Add(v); + } + + + //return callback(node, children); + return Visitor.VisitNode(node,children); + } + + private OUT Visit(ManySyntaxNode manyNode) + { + + var children = new List(); + + foreach (var n in manyNode.Children) + { + var v = Visit(n); + + children.Add(v); + } + + + //return callback(node, children); + return Visitor.VisitManyNode(manyNode,children); + } + + + } +} \ No newline at end of file diff --git a/sly/parser/generator/visitor/GraphVizEBNFSyntaxTreeVisitor.cs b/sly/parser/generator/visitor/GraphVizEBNFSyntaxTreeVisitor.cs index 27b3d3c3..633647e7 100644 --- a/sly/parser/generator/visitor/GraphVizEBNFSyntaxTreeVisitor.cs +++ b/sly/parser/generator/visitor/GraphVizEBNFSyntaxTreeVisitor.cs @@ -1,13 +1,14 @@ +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Linq; +using sly.lexer; using sly.parser.generator.visitor.dotgraph; using sly.parser.syntax.tree; namespace sly.parser.generator.visitor { [ExcludeFromCodeCoverage] - public class GraphVizEBNFSyntaxTreeVisitor where IN : struct + public class GraphVizEBNFSyntaxTreeVisitor : IConcreteSyntaxTreeVisitor where IN : struct { public DotGraph Graph { get; private set; } @@ -18,22 +19,106 @@ public GraphVizEBNFSyntaxTreeVisitor() private int NodeCounter = 0; + public DotNode VisitTree(ISyntaxNode root) + { + Graph = new DotGraph("syntaxtree", true); + + ConcreteSyntaxTreeWalker walker = new ConcreteSyntaxTreeWalker(this); + var dot = walker.Visit(root); + return dot; + } + + private DotNode Node(string label) + { + var node = new DotNode(NodeCounter.ToString()) + { + // Set all available properties + Shape = "ellipse", + Label = label, + FontColor = "black", + Style = null, + Height = 0.5f + }; + NodeCounter++; + Graph.Add(node); + return node; + } - private DotNode Leaf(SyntaxLeaf leaf) + public DotNode VisitOptionNode(bool exists, DotNode child) { - if (leaf.Token.IsIndent) + if (!exists) { - return Leaf(leaf.Token.TokenID, "INDENT>>"); + return VisitLeaf(new Token() { TokenID = default(IN), SpanValue = "".ToCharArray() }); } - else if (leaf.Token.IsUnIndent) + return child; + } + + public DotNode VisitNode(SyntaxNode node, IList children) + { + DotNode result = null; + + result = Node(GetNodeLabel(node)); + //children.ForEach(c => + foreach (var child in children) { - return Leaf(leaf.Token.TokenID, "< node, IList children) + { + DotNode result = null; + + result = Node(GetNodeLabel(node)); + Graph.Add(result); + //children.ForEach(c => + foreach (var child in children) { - return Leaf(leaf.Token.Value); + if (child != null) // Prevent arrows with null destinations + { + var edge = new DotArrow(result, child) + { + // Set all available properties + ArrowHeadShape = "none" + }; + Graph.Add(edge); + } } - return Leaf(leaf.Token.TokenID, leaf.Token.Value); + return result; + } + + public DotNode VisitEpsilon() + { + return VisitLeaf(new Token() { TokenID = default(IN), SpanValue = "epsilon".ToCharArray() }); + } + + + + public DotNode VisitLeaf(Token token) + { + if (token.IsIndent) + { + return Leaf(token.TokenID, "INDENT>>"); + } + else if (token.IsUnIndent) + { + return Leaf(token.TokenID, "< root) - { - Graph = new DotGraph("syntaxtree", true); - return Visit(root); - } - - private DotNode Node(string label) - { - var node = new DotNode(NodeCounter.ToString()) - { - // Set all available properties - Shape = "ellipse", - Label = label, - FontColor = "black", - Style = null, - Height = 0.5f - }; - NodeCounter++; - Graph.Add(node); - return node; - } - - protected DotNode Visit(ISyntaxNode n) - { - switch (n) - { - case SyntaxLeaf leaf: - return Visit(leaf); - case GroupSyntaxNode node: - return Visit(node); - case ManySyntaxNode node: - return Visit(node); - case OptionSyntaxNode node: - return Visit(node); - case SyntaxNode node: - return Visit(node); - case SyntaxEpsilon epsilon: - { - return Leaf(default(IN), "Epsilon"); - } - default: - return Leaf(default(IN),"NULL"); - } - } - - private DotNode Visit(GroupSyntaxNode node) - { - return Visit(node as SyntaxNode); - } - - private DotNode Visit(OptionSyntaxNode node) - { - var child = node.Children != null && node.Children.Any>() ? node.Children[0] : null; - if (child == null || node.IsEmpty) - { - return Leaf(default(IN),""); - } - var r = Visit(child); - return r; - } - + + private string GetNodeLabel(SyntaxNode node) { string label = node.Name; return label; } - private DotNode Visit(SyntaxNode node) - { - DotNode result = null; - - - var children = new List(); - - foreach (var n in node.Children) - { - var v = Visit(n); - - children.Add(v); - } - - - result = Node(GetNodeLabel(node)); - Graph.Add(result); - children.ForEach(c => - { - if (c != null) // Prevent arrows with null destinations - { - var edge = new DotArrow(result, c) - { - // Set all available properties - ArrowHeadShape = "none" - }; - Graph.Add(edge); - } - }); - return result; - } - - private DotNode Visit(ManySyntaxNode node) - { - return Visit(node as SyntaxNode); - } - - - private DotNode Visit(SyntaxLeaf leaf) - { - if (leaf.Token.IsIndent) - { - return Leaf(leaf.Token.TokenID, "INDENT>>"); - } - else if (leaf.Token.IsUnIndent) - { - return Leaf(leaf.Token.TokenID, "< where IN : struct + { + OUT VisitOptionNode(bool exists, OUT child); + OUT VisitNode(SyntaxNode node, IList children); + OUT VisitManyNode(ManySyntaxNode node, IList children); + + OUT VisitEpsilon(); + OUT VisitLeaf(Token token); + } +} \ No newline at end of file