/* * Encodes the handle-finding FSA as a LR parse table. * * * * The parse table. */ protected Tuple <ActionAndGoTo, List <Conflict> > BuildParseTable(Automaton automaton, Grammar grammar) { int conflictsMode = grammar.ConflictsMode; List <Conflict> conflicts = new List <Conflict>(); Dictionary <int, Dictionary <string, bool> > errors = new Dictionary <int, Dictionary <string, bool> >(); // initialize the table ActionAndGoTo table = new ActionAndGoTo(); foreach (var kvp in automaton.TransitionTable) { var num = kvp.Key; var transitions = kvp.Value; foreach (var kvp2 in transitions) { var trigger = kvp2.Key; var destination = kvp2.Value; if (!grammar.HasNonterminal(trigger)) { // terminal implies shift table.AddAction(num, trigger, destination); } else { // nonterminal goes in the goto table table.AddGoTo(num, trigger, destination); } } } foreach (var kvp in automaton.States) { var num = kvp.Key; var state = kvp.Value; Dictionary <string, int> actionDictionary = table.Action.GetOrAdd(num, () => new Dictionary <string, int>()); foreach (var item in state.Items) { if (item.IsReduceItem) { int ruleNumber = item.Rule.Number; foreach (var token in item.Lookahead) { Dictionary <string, bool> errorsOfNum; if (errors.TryGetValue(num, out errorsOfNum) && errorsOfNum.ContainsKey(token)) { // there was a previous conflict resolved as an error // entry for this token. continue; } int instruction; if (actionDictionary.TryGetValue(token, out instruction)) { // conflict if (instruction > 0) { if ((conflictsMode & Grammar.OPERATORS) == Grammar.OPERATORS) { if (grammar.HasOperator(token)) { Dictionary <string, int> operatorInfo = grammar.GetOperatorInfo(token); Nullable <int> rulePrecedence = item.Rule.Precedence; // unless the rule has given precedence if (rulePrecedence == null) { foreach (string c in item.Rule.Components.Reverse()) { // try to extract it from the rightmost terminal if (grammar.HasOperator(c)) { Dictionary <string, int> ruleOperatorInfo = grammar.GetOperatorInfo(c); rulePrecedence = ruleOperatorInfo["prec"]; break; } } } if (rulePrecedence != null) { // if we actually have a rule precedence int tokenPrecedence = operatorInfo["prec"]; if (rulePrecedence > tokenPrecedence) { // if the rule precedence is higher, reduce actionDictionary[token] = -ruleNumber; } else if (rulePrecedence < tokenPrecedence) { // if the token precedence is higher, shift // (i.e. don"t modify the table) } else { // precedences are equal, let"s turn to associativity int assoc = operatorInfo["assoc"]; if (assoc == Grammar.RIGHT) { // if right-associative, shift // (i.e. don"t modify the table) } else if (assoc == Grammar.LEFT) { // if left-associative, reduce actionDictionary[token] = -ruleNumber; } else if (assoc == Grammar.NONASSOC) { // the token is nonassociative. // this actually means an input error, so // remove the shift entry from the table // and mark this as an explicit error // entry actionDictionary.Remove(token); errors[num].Add(token, true); } } continue; // resolved the conflict, phew } // we couldn"t calculate the precedence => the conflict was not resolved // move along. } } // s/r if ((conflictsMode & Grammar.SHIFT) == Grammar.SHIFT) { conflicts.Add(new Conflict(num, token, item.Rule, Grammar.SHIFT)); continue; } else { throw new ShiftReduceConflictException(num, item.Rule, token, automaton); } } else { // r/r Rule originalRule = grammar.GetRule(-instruction); Rule newRule = item.Rule; if ((conflictsMode & Grammar.LONGER_REDUCE) == Grammar.LONGER_REDUCE) { int count1 = originalRule.Components.Length; int count2 = newRule.Components.Length; if (count1 > count2) { // original rule is longer Rule[] resolvedRules = new Rule[] { originalRule, newRule }; conflicts.Add(new Conflict(num, token, resolvedRules, Grammar.LONGER_REDUCE)); continue; } else if (count2 > count1) { // new rule is longer actionDictionary[token] = -ruleNumber; Rule[] resolvedRules = new Rule[] { newRule, originalRule }; conflicts.Add(new Conflict(num, token, resolvedRules, Grammar.LONGER_REDUCE)); continue; } } if ((conflictsMode & Grammar.EARLIER_REDUCE) == Grammar.EARLIER_REDUCE) { if (-instruction < ruleNumber) { // original rule was earlier Rule[] resolvedRules = new Rule[] { originalRule, newRule }; conflicts.Add(new Conflict(num, token, resolvedRules, Grammar.EARLIER_REDUCE)); continue; } else { // new rule was earlier actionDictionary[token] = -ruleNumber; //WTM: Change: In PHP, this resolvedRules declaration was below the conflicts.Add statement, but this else statement was never reached anyway as far as I can tell. Rule[] resolvedRules = new Rule[] { newRule, originalRule }; conflicts.Add(new Conflict(num, token, resolvedRules, Grammar.EARLIER_REDUCE)); continue; } } // everything failed, throw an exception throw new ReduceReduceConflictException(num, originalRule, newRule, token, automaton); } } actionDictionary[token] = -ruleNumber; } } } } return(new Tuple <ActionAndGoTo, List <Conflict> >(table, conflicts)); }
/* * Constructor. * * The parse table. * * An array of conflicts resolved during parse table * construction. */ public AnalysisResult(ActionAndGoTo parseTable, Automaton automaton, List <Conflict> conflicts) { this.ParseTable = parseTable; this.Automaton = automaton; this.ResolvedConflicts = conflicts; }