14 using System.Collections.Generic;
20 internal class GrammarDataBuilder {
25 internal int _lastItemId;
29 _grammar = _language.Grammar;
32 internal void Build() {
33 _grammarData = _language.GrammarData;
34 CreateAugmentedRoots();
35 CollectTermsFromGrammar();
36 AssignWhitespaceAndDelimiters();
38 FillOperatorReportGroup();
41 ComputeNonTerminalsNullability(_grammarData);
42 ComputeTailsNullability(_grammarData);
46 private void CreateAugmentedRoots() {
47 _grammarData.AugmentedRoot = CreateAugmentedRoot(_grammar.Root);
48 foreach(var snippetRoot
in _grammar.SnippetRoots)
49 _grammarData.AugmentedSnippetRoots.Add(CreateAugmentedRoot(snippetRoot));
54 result.SetFlag(TermFlags.NoAstNode);
58 private void CollectTermsFromGrammar() {
60 _grammarData.AllTerms.Clear();
63 t.SetFlag(TermFlags.IsNonGrammar);
64 _grammarData.AllTerms.Add(t);
67 CollectTermsRecursive(_grammarData.AugmentedRoot);
68 foreach(var augmRoot
in _grammarData.AugmentedSnippetRoots)
69 CollectTermsRecursive(augmRoot);
71 _grammarData.AllTerms.Add(_grammar.SyntaxError);
74 private void CollectTermsRecursive(
BnfTerm term) {
75 if (_grammarData.AllTerms.Contains(term))
return;
76 _grammarData.AllTerms.Add(term);
78 if (nt == null)
return;
80 if (
string.IsNullOrEmpty(nt.Name)) {
81 if (nt.Rule != null && !
string.IsNullOrEmpty(nt.Rule.Name))
82 nt.
Name = nt.Rule.Name;
84 nt.Name =
"Unnamed" + (_unnamedCount++);
90 for (
int i = 0; i < elemList.Count; i++) {
102 CollectTermsRecursive(child);
106 private void FillOperatorReportGroup() {
107 foreach(var group
in _grammar.TermReportGroups)
109 foreach(var term
in _grammarData.Terminals)
110 if (term.FlagIsSet(
TermFlags.IsOperator))
111 group.Terminals.Add(term);
116 private void FindClosingBraces() {
117 foreach(var term
in _grammar.KeyTerms.Values) {
118 if (term.FlagIsSet(
TermFlags.IsCloseBrace))
119 _grammarData.ClosingBraces.Add(term.Text);
123 private void AssignWhitespaceAndDelimiters() {
124 var delims = _grammar.Delimiters;
127 delims = string.Empty;
128 var commonDelims =
",;[](){}";
129 foreach(var delim
in commonDelims)
130 if (_grammar.KeyTerms.ContainsKey(delim.ToString()))
133 _grammarData.WhitespaceAndDelimiters = _grammar.WhitespaceChars + delims
139 private void InitTermLists() {
141 foreach (
BnfTerm term
in _grammarData.AllTerms) {
142 if (term is
NonTerminal) _grammarData.NonTerminals.Add((NonTerminal)term);
143 if (term is
Terminal) _grammarData.Terminals.Add((Terminal)term);
145 _grammarData.Terminals.Sort(Terminal.ByName);
147 foreach (var term
in _grammarData.Terminals) {
149 if (symTerm == null)
continue;
150 if (!
string.IsNullOrEmpty(symTerm.Text) && char.IsLetter(symTerm.Text[0]))
151 symTerm.SetFlag(TermFlags.IsKeyword);
154 foreach (var term
in _grammarData.AllTerms)
155 term.Init(_grammarData);
158 private void CreateProductions() {
161 for (
int i = 0; i < _grammarData.NonTerminals.Count; i++) {
162 var nt = _grammarData.NonTerminals[i];
163 nt.Productions.Clear();
165 BnfExpressionData allData =
new BnfExpressionData();
166 allData.AddRange(nt.Rule.Data);
167 if (nt.ErrorRule != null)
168 allData.AddRange(nt.ErrorRule.Data);
171 Production prod = CreateProduction(nt, prodOperands);
172 nt.Productions.Add(prod);
175 nt.InsertCustomHints();
183 foreach (
BnfTerm operand
in operands) {
184 if (operand == _grammar.Empty)
194 prod.RValues.Add(operand);
195 prod.LR0Items.Add(
new LR0Item(_lastItemId++, prod, prod.RValues.Count - 1, hints));
199 if (prod.RValues.Count == 0)
200 prod.Flags |= ProductionFlags.IsEmpty;
202 ComputeProductionFlags(prod);
203 prod.LR0Items.Add(
new LR0Item(_lastItemId++, prod, prod.RValues.Count, hints));
207 private void ComputeProductionFlags(
Production production) {
208 production.Flags = ProductionFlags.None;
209 foreach (var rv
in production.
RValues) {
213 production.Flags |= ProductionFlags.HasTerminals;
214 if (t.Category ==
TokenCategory.Error) production.Flags |= ProductionFlags.IsError;
216 if(rv.FlagIsSet(
TermFlags.IsPunctuation))
continue;
218 var lvalue = production.LValue;
225 private static void ComputeNonTerminalsNullability(
GrammarData data) {
227 while (undecided.Count > 0) {
230 if (!ComputeNullability(nt))
231 newUndecided.Add(nt);
232 if (undecided.Count == newUndecided.Count)
return;
233 undecided = newUndecided;
237 private static bool ComputeNullability(
NonTerminal nonTerminal) {
240 nonTerminal.SetFlag(TermFlags.IsNullable);
246 bool allNullable =
true;
248 allNullable &= child.FlagIsSet(TermFlags.IsNullable);
251 nonTerminal.SetFlag(TermFlags.IsNullable);
258 private static void ComputeTailsNullability(
GrammarData data) {
260 foreach (var prod
in nt.Productions) {
261 var
count = prod.LR0Items.Count;
262 for (
int i = count - 1; i >= 0; i--) {
263 var item = prod.LR0Items[i];
264 item.TailIsNullable =
true;
265 if (item.Current == null)
continue;
266 if (!item.Current.FlagIsSet(
TermFlags.IsNullable))
273 #region Grammar Validation
274 private void ValidateGrammar() {
275 var createAst = _grammar.FlagIsSet(LanguageFlags.CreateAst);
278 foreach(var nt
in _grammarData.NonTerminals) {
280 if(createAst && nt.AstNodeCreator == null && nt.AstNodeType == null && !nt.FlagIsSet(
TermFlags.NoAstNode))
281 missingAstTypeSet.Add(nt);
282 if(nt.FlagIsSet(
TermFlags.IsTransient)) {
287 foreach(var prod
in nt.Productions)
288 if (CountNonPunctuationTerms(prod) > 1) invalidTransSet.Add(nt);
291 foreach(var prod
in nt.Productions)
293 var lastTerm = prod.RValues[prod.RValues.Count -1];
294 if (!(lastTerm is
Terminal) || lastTerm == _grammar.SyntaxError)
300 if (missingAstTypeSet.Count > 0)
302 if (invalidTransSet.Count > 0)
306 private int CountNonPunctuationTerms(
Production production) {
308 foreach(var rvalue
in production.
RValues)
TokenCategory
Token category.
readonly ProductionList Productions
readonly NonTerminal LValue
A strongly-typed resource class, for looking up localized strings, etc.
readonly TerminalSet NonGrammarTerminals
static string ErrLastTermOfErrorProd
Looks up a localized string similar to The last term of production containing SyntaxError must be a t...
static string ErrRuleContainsNull
Looks up a localized string similar to Rule for NonTerminal {0} contains null as an operand in positi...
static string ErrTransientNtMustHaveOneTerm
Looks up a localized string similar to Transient non-terminal must have zero or one non-punctuation c...
static string ErrListCannotBeTransient
Looks up a localized string similar to List non-terminals cannot be marked transient; list: ({0})...
bool FlagIsSet(TermFlags flag)
readonly NonTerminalList NonTerminals
bool IsSet(ProductionFlags flag)
readonly BnfTermList RValues
static string ErrNodeTypeNotSetOn
Looks up a localized string similar to Warning: AstNodeType or AstNodeCreator is not set on non-termi...
static string ErrNtRuleIsNull
Looks up a localized string similar to Non-terminal {0} has uninitialized Rule property..