14 using System.Collections;
15 using System.Collections.Generic;
18 using System.Diagnostics;
20 namespace Irony.
Parsing.Construction {
25 internal partial class ParserDataBuilder {
26 LanguageData _language;
27 internal ParserData Data;
29 ParserStateHash _stateHash =
new ParserStateHash();
31 internal ParserDataBuilder(LanguageData language) {
33 _grammar = _language.Grammar;
38 Data = _language.ParserData;
40 var itemsNeedLookaheads = GetReduceItemsInInadequateState();
41 ComputeTransitions(itemsNeedLookaheads);
42 ComputeLookaheads(itemsNeedLookaheads);
43 ComputeAndResolveConflicts();
44 CreateRemainingReduceActions();
45 ComputeStatesExpectedTerminals();
48 #region Creating parser states
49 private void CreateParserStates() {
50 var grammarData = _language.GrammarData;
53 Data.InitialState = CreateInitialState(grammarData.AugmentedRoot);
54 ExpandParserStateList(0);
55 CreateAcceptAction(Data.InitialState, grammarData.AugmentedRoot);
58 foreach(var augmRoot
in grammarData.AugmentedSnippetRoots) {
59 var initialState = CreateInitialState(augmRoot);
60 ExpandParserStateList(Data.States.Count - 1);
61 CreateAcceptAction(initialState, augmRoot);
65 private void CreateAcceptAction(ParserState initialState, NonTerminal augmentedRoot) {
66 var root = augmentedRoot.Productions[0].RValues[0];
67 var shiftOverRootState = initialState.Actions[root].NewState;
68 shiftOverRootState.Actions[_grammar.Eof] =
new ParserAction(
ParserActionType.Accept, null, null);
72 private ParserState CreateInitialState(NonTerminal augmentedRoot) {
74 var iniItemSet =
new LR0ItemSet();
75 iniItemSet.Add(augmentedRoot.Productions[0].LR0Items[0]);
76 var initialState = FindOrCreateState(iniItemSet);
77 var rootNt = augmentedRoot.Productions[0].RValues[0] as
NonTerminal;
78 Data.InitialStates[rootNt] = initialState;
82 private void ExpandParserStateList(
int initialIndex) {
84 for (
int index = initialIndex; index < Data.States.Count; index++) {
85 var state = Data.States[index];
87 foreach (var term
in state.BuilderData.ShiftTerms) {
88 var shiftItems = state.BuilderData.ShiftItems.SelectByCurrent(term);
90 var shiftedCoreItems = shiftItems.GetShiftedCores();
91 var newState = FindOrCreateState(shiftedCoreItems);
94 state.Actions[term] = newAction;
96 foreach (var shiftItem
in shiftItems) {
97 shiftItem.ShiftedItem = newState.BuilderData.AllItems.FindByCore(shiftItem.Core.ShiftedItem);
103 private ParserState FindOrCreateState(LR0ItemSet coreItems) {
104 string key = ComputeLR0ItemSetKey(coreItems);
106 if (_stateHash.TryGetValue(key, out state))
109 state =
new ParserState(
"S" + Data.States.Count);
110 state.BuilderData =
new ParserStateData(state, coreItems);
111 Data.States.Add(state);
112 _stateHash[key] = state;
118 #region Compute transitions, lookbacks, lookaheads
124 private void ComputeTransitions(LRItemSet forItems) {
125 var newItemsNeedLookbacks = forItems;
126 while(newItemsNeedLookbacks.Count > 0) {
127 var newTransitions = CreateLookbackTransitions(newItemsNeedLookbacks);
128 newItemsNeedLookbacks = SelectNewItemsThatNeedLookback(newTransitions);
132 private LRItemSet SelectNewItemsThatNeedLookback(TransitionList transitions) {
134 var items =
new LRItemSet();
135 foreach(var trans
in transitions)
136 foreach(var item
in trans.Items)
137 if (item.Core.TailIsNullable && item.Lookbacks.Count == 0)
142 private LRItemSet GetReduceItemsInInadequateState() {
143 var result =
new LRItemSet();
144 foreach(var state
in Data.States) {
145 if (state.BuilderData.IsInadequate)
146 result.UnionWith(state.BuilderData.ReduceItems);
151 private TransitionList CreateLookbackTransitions(LRItemSet sourceItems) {
152 var newTransitions =
new TransitionList();
156 var iniCores =
new LR0ItemSet();
157 foreach(var sourceItem
in sourceItems)
158 iniCores.Add(sourceItem.Core.Production.LR0Items[0]);
160 foreach(var state
in Data.States) {
161 foreach(var iniItem
in state.BuilderData.InitialItems) {
162 if (!iniCores.Contains(iniItem.Core))
continue;
163 var iniItemNt = iniItem.Core.Production.LValue;
164 Transition lookback = null;
165 var currItem = iniItem;
166 while (currItem != null) {
167 if(sourceItems.Contains(currItem)) {
170 if(lookback == null && !state.BuilderData.Transitions.TryGetValue(iniItemNt, out lookback)) {
171 lookback =
new Transition(state, iniItemNt);
172 newTransitions.Add(lookback);
176 if (currItem.Core.IsFinal)
177 currItem.Lookbacks.Add(lookback);
183 currItem.Transition.Include(lookback);
186 currItem = currItem.ShiftedItem;
190 return newTransitions;
193 private void ComputeLookaheads(LRItemSet forItems) {
194 foreach(var reduceItem
in forItems) {
196 var sourceStates =
new ParserStateSet();
197 foreach(var lookbackTrans
in reduceItem.Lookbacks) {
198 sourceStates.Add(lookbackTrans.ToState);
199 sourceStates.UnionWith(lookbackTrans.ToState.BuilderData.ReadStateSet);
200 foreach(var includeTrans
in lookbackTrans.Includes) {
201 sourceStates.Add(includeTrans.ToState);
202 sourceStates.UnionWith(includeTrans.ToState.BuilderData.ReadStateSet);
206 foreach(var state
in sourceStates)
207 reduceItem.Lookaheads.UnionWith(state.BuilderData.ShiftTerminals);
209 if (reduceItem.Lookaheads.Contains(_grammar.SyntaxError))
210 reduceItem.Lookaheads.Remove(_grammar.SyntaxError);
212 if (reduceItem.Lookaheads.Count == 0)
213 _language.Errors.Add(GrammarErrorLevel.InternalError, reduceItem.State,
"Reduce item '{0}' in state {1} has no lookaheads.", reduceItem.Core, reduceItem.State);
219 #region Analyzing and resolving conflicts
220 private void ComputeAndResolveConflicts() {
221 foreach(var state
in Data.States) {
222 if(!state.BuilderData.IsInadequate)
225 var stateData = state.BuilderData;
226 stateData.Conflicts.Clear();
227 var allLkhds =
new BnfTermSet();
229 foreach(var item
in stateData.ReduceItems) {
230 foreach(var lkh
in item.Lookaheads) {
231 if(allLkhds.Contains(lkh)) {
232 state.BuilderData.Conflicts.Add(lkh);
238 foreach(var term
in stateData.ShiftTerminals)
239 if(allLkhds.Contains(term)) {
240 stateData.Conflicts.Add(term);
244 if(stateData.Conflicts.Count > 0) {
246 foreach (var conflict
in stateData.Conflicts)
247 ResolveConflictByHints(state, conflict);
248 stateData.Conflicts.ExceptWith(state.BuilderData.ResolvedConflicts);
250 foreach (var conflict
in stateData.Conflicts)
251 ResolveConflictByPrecedence(state, conflict);
252 stateData.Conflicts.ExceptWith(state.BuilderData.ResolvedConflicts);
254 if (stateData.Conflicts.Count > 0)
255 ReportAndCreateDefaultActionsForConflicts(state);
260 private void ResolveConflictByHints(ParserState state, Terminal conflict) {
261 var stateData = state.BuilderData;
264 var reduceItems = stateData.ReduceItems.SelectByLookahead(conflict);
265 foreach(var reduceItem
in reduceItems)
266 if(reduceItem.Core.Hints.Find(h => h.HintType ==
HintType.ResolveToReduce) != null) {
267 state.Actions[conflict] =
new ParserAction(
ParserActionType.Reduce, null, reduceItem.Core.Production);
268 state.BuilderData.ResolvedConflicts.Add(conflict);
273 var shiftItems = stateData.ShiftItems.SelectByCurrent(conflict);
274 foreach (var shiftItem
in shiftItems)
275 if(shiftItem.Core.Hints.Find(h => h.HintType ==
HintType.ResolveToShift) != null) {
277 state.BuilderData.ResolvedConflicts.Add(conflict);
283 var reduceProduction = reduceItems.First().Core.Production;
284 ParserState newState = (state.Actions.ContainsKey(conflict) ? state.Actions[conflict].NewState : null);
286 var allItems =
new LRItemList();
287 allItems.AddRange(state.BuilderData.ShiftItems.SelectByCurrent(conflict));
288 allItems.AddRange(state.BuilderData.ReduceItems.SelectByLookahead(conflict));
290 foreach (var item
in allItems) {
291 if (item.Core.Hints.Find(h => h.HintType ==
HintType.ResolveInCode) != null) {
292 state.Actions[conflict] =
new ParserAction(
ParserActionType.Code, newState, reduceProduction);
293 state.BuilderData.ResolvedConflicts.Add(conflict);
300 var customHints =
new List<CustomGrammarHint>();
301 var hintItems =
new Dictionary<CustomGrammarHint, LRItem>();
302 LRItem defaultItem = null;
303 foreach (var item
in allItems) {
304 var hints = item.Core.Hints.OfType<CustomGrammarHint>();
305 foreach (var hint
in hints) {
306 customHints.Add(hint);
307 hintItems[hint] = item;
309 if (defaultItem == null && !hints.Any())
313 if (customHints.Count > 0) {
314 state.Actions[conflict] =
new ParserAction(newState, reduceProduction, args => {
316 foreach (var customHint
in customHints) {
317 if (customHint.Match(args)) {
318 var item = hintItems[customHint];
320 if (args.ReduceProduction == null)
321 args.ReduceProduction = item.Core.Production;
326 if (defaultItem != null) {
327 args.ReduceProduction = defaultItem.Core.Production;
328 args.Result = args.NewShiftState != null ? ParserActionType.Shift : ParserActionType.Reduce;
332 args.Result = args.NewShiftState != null ? ParserActionType.Shift : ParserActionType.Reduce;
335 state.BuilderData.ResolvedConflicts.Add(conflict);
340 private void ResolveConflictByPrecedence(ParserState state, Terminal conflict) {
341 if (!conflict.FlagIsSet(
TermFlags.IsOperator))
return;
342 var stateData = state.BuilderData;
343 if (!stateData.ShiftTerminals.Contains(conflict))
return;
344 var shiftAction = state.Actions[conflict];
346 var shiftItems = stateData.ShiftItems.SelectByCurrent(conflict);
348 var reduceItems = stateData.ReduceItems.SelectByLookahead(conflict);
349 if (reduceItems.Count > 1)
return;
350 var reduceItem = reduceItems.First();
351 shiftAction.ChangeToOperatorAction(reduceItem.Core.Production);
352 stateData.ResolvedConflicts.Add(conflict);
356 private void ReportAndCreateDefaultActionsForConflicts(ParserState state) {
357 var shiftReduceConflicts = state.BuilderData.GetShiftReduceConflicts();
358 var reduceReduceConflicts = state.BuilderData.GetReduceReduceConflicts();
359 var stateData = state.BuilderData;
360 if (shiftReduceConflicts.Count > 0)
361 _language.Errors.Add(GrammarErrorLevel.Conflict, state,
Resources.ErrSRConflict, state, shiftReduceConflicts.ToString());
362 if (reduceReduceConflicts.Count > 0)
363 _language.Errors.Add(GrammarErrorLevel.Conflict, state,
Resources.ErrRRConflict, state, reduceReduceConflicts.ToString());
367 foreach (var conflict
in reduceReduceConflicts) {
368 var reduceItems = stateData.ReduceItems.SelectByLookahead(conflict);
369 var firstProd = reduceItems.First().Core.Production;
371 state.Actions[conflict] = action;
374 stateData.ResolvedConflicts.UnionWith(shiftReduceConflicts);
375 stateData.ResolvedConflicts.UnionWith(reduceReduceConflicts);
376 stateData.Conflicts.ExceptWith(stateData.ResolvedConflicts);
381 #region final actions: creating remaining reduce actions, computing expected terminals, cleaning up state data
382 private void CreateRemainingReduceActions() {
383 foreach (var state
in Data.States) {
384 var stateData = state.BuilderData;
385 if (stateData.ShiftItems.Count == 0 && stateData.ReduceItems.Count == 1) {
386 state.DefaultAction =
new ParserAction(
ParserActionType.Reduce, null, stateData.ReduceItems.First().Core.Production);
390 foreach (var item
in state.BuilderData.ReduceItems) {
391 var action =
new ParserAction(
ParserActionType.Reduce, null, item.Core.Production);
392 foreach (var lkh
in item.Lookaheads) {
393 if (state.Actions.ContainsKey(lkh))
continue;
394 state.Actions[lkh] = action;
401 private void ComputeStatesExpectedTerminals() {
402 foreach (var state
in Data.States) {
403 state.ExpectedTerminals.UnionWith(state.BuilderData.ShiftTerminals);
405 foreach (var reduceItem
in state.BuilderData.ReduceItems)
406 state.ExpectedTerminals.UnionWith(reduceItem.Lookaheads);
407 RemoveTerminals(state.ExpectedTerminals, _grammar.SyntaxError, _grammar.Eof);
411 private void RemoveTerminals(TerminalSet terms, params Terminal[] termsToRemove) {
412 foreach(var termToRemove
in termsToRemove)
413 if (terms.Contains(termToRemove)) terms.Remove(termToRemove);
416 public void CleanupStateData() {
417 foreach (var state
in Data.States)
422 #region Utilities: ComputeLR0ItemSetKey
431 public static string ComputeLR0ItemSetKey(LR0ItemSet items) {
432 if (items.Count == 0)
return string.Empty;
434 LR0ItemList itemList =
new LR0ItemList();
435 foreach (var item
in items)
438 if (itemList.Count == 1)
439 return itemList[0].ID.ToString();
440 itemList.Sort(CompareLR0Items);
442 StringBuilder sb =
new StringBuilder(100);
443 foreach (LR0Item item
in itemList) {
447 return sb.ToString();
450 private static int CompareLR0Items(LR0Item x, LR0Item
y) {
451 if (x.ID < y.ID)
return -1;
452 if (x.ID == y.ID)
return 0;
_In_ size_t _In_ DXGI_FORMAT _In_ size_t _In_ float size_t y
static string ErrRRConflict
Looks up a localized string similar to Reduce-reduce conflict. State {0}, lookaheads: {1}...
static string ErrSRConflict
Looks up a localized string similar to Shift-reduce conflict. State {0}, lookaheads [{1}]...