1+ #region BSD 3-Clause License
2+
3+ // <copyright file="ParseTreeGrapher.cs" company="Edgerunner.org">
4+ // Copyright 2020 Thaddeus Ryker
5+ // </copyright>
6+ //
7+ // BSD 3-Clause License
8+ //
9+ // Copyright (c) 2020, Thaddeus Ryker
10+ // All rights reserved.
11+ //
12+ // Redistribution and use in source and binary forms, with or without
13+ // modification, are permitted provided that the following conditions are met:
14+ //
15+ // 1. Redistributions of source code must retain the above copyright notice, this
16+ // list of conditions and the following disclaimer.
17+ //
18+ // 2. Redistributions in binary form must reproduce the above copyright notice,
19+ // this list of conditions and the following disclaimer in the documentation
20+ // and/or other materials provided with the distribution.
21+ //
22+ // 3. Neither the name of the copyright holder nor the names of its
23+ // contributors may be used to endorse or promote products derived from
24+ // this software without specific prior written permission.
25+ //
26+ // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
27+ // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28+ // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
29+ // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
30+ // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31+ // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32+ // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33+ // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
34+ // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35+ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36+
37+ #endregion
38+
39+ using System ;
40+ using System . Collections . Generic ;
41+
42+ using Antlr4 . Runtime ;
43+ using Antlr4 . Runtime . Tree ;
44+
45+ using Microsoft . Msagl . Drawing ;
46+
47+ namespace Org . Edgerunner . ANTLR4 . Tools . Graphing
48+ {
49+ /// <summary>
50+ /// Class used to graph ANTLR4 parse trees.
51+ /// </summary>
52+ /// <remarks>This class uses Microsoft's Automatic Graph Layout Library to do the actual dirty work.</remarks>
53+ public class ParseTreeGrapher : IParseTreeGrapher
54+ {
55+ #region Constructors And Finalizers
56+
57+ /// <summary>
58+ /// Initializes a new instance of the <see cref="ParseTreeGrapher" /> class.
59+ /// </summary>
60+ /// <param name="tree">The tree to graph.</param>
61+ /// <param name="rules">The parser rules.</param>
62+ /// <seealso cref="Antlr4.Runtime.Tree.ITree" />
63+ public ParseTreeGrapher ( ITree tree , IList < string > rules )
64+ {
65+ Subject = tree ;
66+ ParserRules = rules ?? new List < string > ( ) ;
67+ }
68+
69+ #endregion
70+
71+ /// <summary>
72+ /// Gets the subject parse tree to graph.
73+ /// </summary>
74+ /// <value>The subject tree.</value>
75+ public ITree Subject { get ; }
76+
77+ /// <summary>
78+ /// Gets the parser rules.
79+ /// </summary>
80+ /// <value>The parser rules.</value>
81+ public IList < string > ParserRules { get ; }
82+
83+ /// <summary>
84+ /// Gets or sets the color of the background.
85+ /// </summary>
86+ /// <value>The color of the background.</value>
87+ public Color ? BackgroundColor { get ; set ; }
88+
89+ /// <summary>
90+ /// Gets or sets the color of the text.
91+ /// </summary>
92+ /// <value>The color of the text.</value>
93+ public Color ? TextColor { get ; set ; }
94+
95+ /// <summary>
96+ /// Gets or sets the color of the border.
97+ /// </summary>
98+ /// <value>The color of the border.</value>
99+ public Color ? BorderColor { get ; set ; }
100+
101+ /// <summary>
102+ /// Creates the parse tree graph.
103+ /// </summary>
104+ /// <returns>A new <see cref="Graph"/>.</returns>
105+ public Graph CreateGraph ( )
106+ {
107+ var graph = new Graph ( ) ;
108+ if ( Subject != null )
109+ {
110+ GraphEdges ( graph , Subject ) ;
111+ FormatNodes ( graph , Subject ) ;
112+ }
113+
114+ return graph ;
115+ }
116+
117+ private void GraphEdges ( Graph graph , ITree tree )
118+ {
119+ for ( var i = tree . ChildCount - 1 ; i > - 1 ; i -- )
120+ {
121+ var child = tree . GetChild ( i ) ;
122+ graph . AddEdge ( tree . GetHashCode ( ) . ToString ( ) , child . GetHashCode ( ) . ToString ( ) ) ;
123+
124+ GraphEdges ( graph , child ) ;
125+ }
126+ }
127+
128+ private void FormatNodes ( Graph graph , ITree tree )
129+ {
130+ var node = graph . FindNode ( tree . GetHashCode ( ) . ToString ( ) ) ;
131+ if ( node != null )
132+ {
133+ node . LabelText = Trees . GetNodeText ( tree , ParserRules ) ;
134+
135+ var ruleFailedAndMatchedNothing = false ;
136+
137+ if ( tree is ParserRuleContext context )
138+ ruleFailedAndMatchedNothing =
139+ // ReSharper disable once ComplexConditionExpression
140+ context . exception != null &&
141+ context . stop != null
142+ && context . stop . TokenIndex < context . start . TokenIndex ;
143+
144+ if ( TextColor . HasValue || tree is IErrorNode || ruleFailedAndMatchedNothing )
145+ if ( tree is IErrorNode || ruleFailedAndMatchedNothing )
146+ node . Label . FontColor = Color . Red ;
147+ else
148+ node . Label . FontColor = TextColor . Value ;
149+
150+ if ( BackgroundColor . HasValue )
151+ node . Attr . FillColor = BackgroundColor . Value ;
152+
153+ if ( BorderColor . HasValue )
154+ node . Attr . Color = BorderColor . Value ;
155+
156+ node . UserData = tree ;
157+ }
158+
159+ for ( int i = 0 ; i < tree . ChildCount ; i ++ )
160+ FormatNodes ( graph , tree . GetChild ( i ) ) ;
161+ }
162+ }
163+ }
0 commit comments