private static precedenceMode getPrec(string mode) { mode = mode.ToLower(); precedenceMode prec; if (mode == "rs") { prec = Precedence.precedenceMode.ReduceShift; } else if (mode == "rr") { prec = Precedence.precedenceMode.ReduceReduce; } else if (mode == "op") { prec = Precedence.precedenceMode.Operator; } else { throw ParseControlException.NewAndRun("Not recognized precedence mode " + mode); } return(prec); }
private static AssociativityEnum getAssoc(string associativity) { associativity = associativity.ToLower(); AssociativityEnum assoc; if (associativity == "none") { assoc = AssociativityEnum.None; } else if (associativity == "left") { assoc = AssociativityEnum.Left; } else if (associativity == "right") { assoc = AssociativityEnum.Right; } else if (associativity == "try") { assoc = AssociativityEnum.Try; } else if (associativity == "shift") { assoc = AssociativityEnum.Shift; } else if (associativity == "reduce") { assoc = AssociativityEnum.Reduce; } else { throw ParseControlException.NewAndRun("Not recognized precedence associativity " + associativity); } return(assoc); }
public AltRule(SymbolPosition pos, string markWith, IEnumerable <RhsGroup> groups, CodeMix gen) { this.Position = pos; this.MarkWith = markWith; // [@PARSER_USER_ACTION] // here we swap the notion of user action // if in grammar user didn't pass ANY code -- it means identity function // if the grammar user pass empty code -- it means passing null value, so as shorcut set null as entire user action if (gen == null) // if there was no code at all, we infer the end object { CodeMix mix = null; if (inferEndObject(groups, ref mix)) { this.Code = mix; } else { throw ParseControlException.NewAndRun("Couldn't infer which object to pass at " + pos.XYString()); } } // if there was an empty code, such code does not produce anything (i.e. null) // thus we set entire action as null -- it is a shortcut else if (gen.IsEmpty) { this.Code = null; } else { this.Code = gen; } SetGroups(groups); }
private RhsGroup(SymbolPosition position, string name, IEnumerable <IRhsEntity> chunks, RepetitionEnum repetition, ModeEnum mode) { this.Position = position; this.ObjName = name; if (chunks.Count() == 1 && chunks.Single().IsGroup() && chunks.Single().AsGroup().Repetition == RepetitionEnum.Once) { // there is only one chunk, so each symbol is really a chunk -- recreate the list this.elements = chunks.Single().AsGroup().Elements.ToArray(); } else { this.elements = chunks.ToArray(); } this.Repetition = repetition; this.Mode = mode; string err = findError(this.ObjName, this.Elements, this.Repetition, this.Mode); if (err != null) { throw ParseControlException.NewAndRun(err); } }
private void createPrecedences() { var errors = new List <string>(); foreach (Precedence prec in grammar.ParserPrecedences) { if (prec.PriorityGroupStart) { precedenceTable.StartPriorityGroup(); } try { if (prec is OperatorPrecedence) { precedenceTable.AddOperator(prec.Associativity, (prec as OperatorPrecedence).UsedTokens.Select(it => it.GetSymbolId(grammar)).ToArray()); } else if (prec is ReduceShiftPrecedence) { ReduceShiftPrecedence rs_prec = prec as ReduceShiftPrecedence; foreach (PrecedenceWord input in rs_prec.InputSet) { precedenceTable.AddReduceShiftPattern(prec.Associativity, input.GetSymbolId(grammar), rs_prec.ReduceSymbol.GetSymbolId(grammar), rs_prec.ReduceSymbol.StackSymbols.Select(it => grammar.GetSymbolId(it)), // reduce stack operators rs_prec.ShiftSymbols.First().GetSymbolId(grammar), rs_prec.ShiftSymbols.Skip(1).Select(it => it.GetSymbolId(grammar)).ToArray()); // rest of shift symbols } } else if (prec is ReduceReducePrecedence) { ReduceReducePrecedence rr_prec = prec as ReduceReducePrecedence; foreach (PrecedenceWord input in rr_prec.InputSet) { precedenceTable.AddReduceReducePattern(prec.Associativity, input.GetSymbolId(grammar), rr_prec.ReduceSymbols.First().GetSymbolId(grammar), rr_prec.ReduceSymbols.Skip(1).Select(it => it.GetSymbolId(grammar)).ToArray()); } } else { throw new Exception("Unrecognized precedence"); } } catch (Exception ex) { errors.Add(ex.Message + " " + prec.Coords.FirstPosition.XYString() + "."); } if (prec.PriorityGroupEnd) { precedenceTable.EndPriorityGroup(); } } ParseControlException.ThrowAndRun(errors); }
private void createImplicitNames() { if (Code == null) { return; } // we have to check the source of the symbol // named group cannot have named symbols inside HashSet <RhsSymbol> symbols_named_groups = new HashSet <RhsSymbol>( rhsGroups .Where(it => it.ObjName != null) .Select(it => it.GetSymbols()).Flatten() ); // symbols which can have implicit obj name Dictionary <string, RhsSymbol> implicit_symbols = rhsGroups.Select(it => it.GetSymbols()).Flatten() .GroupBy(it => it.SymbolName) .Where(it => it.Count() == 1 && it.Single().ObjName == null && !symbols_named_groups.Contains(it.Single())) .ToDictionary(it => it.Key, it => it.Single()); var errors = new List <string>(); foreach (string placeholder in Code.GetPlaceholders().ToHashSet()) { RhsSymbol symbol; CodeBody code; if (standardMacros.TryGetValue(placeholder, out code)) { Code.ConvertPlaceholderToCode(placeholder, code); } if (!implicit_symbols.TryGetValue(placeholder, out symbol)) { if (code == null) { errors.Add("Cannot bind placeholder \"" + placeholder + "\" to any symbol" + " in: " + groupsToString() + "."); } } else if (code != null) { errors.Add("Ambiguous placeholder \"" + placeholder + "\" in: " + groupsToString() + "."); } else { string var_name = Code.RegisterNewIdentifier(symbol.SymbolName); Code.ConvertPlaceholderToCode(placeholder, new CodeBody().AddIdentifier(var_name)); symbol.SetImplicitUserObjName(var_name); } } if (errors.Any()) { throw ParseControlException.NewAndRun(errors.Join(Environment.NewLine)); } }
public LabeledSymbol(string userLabel, string symbolName) { this.ObjName = userLabel; this.__symbolName = symbolName; if (ObjName != null && new Regex("^_+\\d+$").IsMatch(ObjName)) { throw ParseControlException.NewAndRun("\"" + ObjName + "\" is reserved name"); } }
private static void validate(IEnumerable <Tuple <int, int> > ranges) { IEnumerable <Tuple <int, int> > invalid = ranges.Where(it => it.Item1 > it.Item2).ToList(); if (invalid.Any()) { throw ParseControlException.NewAndRun("Min limit of character range cannot be bigger than max limit: " + invalid.Select(it => (char)it.Item1 + "-" + (char)it.Item2).Join(",")); } }
private LexPattern mergeLexPatterns(IEnumerable <ILexPattern> rawPatterns) { if (!rawPatterns.Any()) { throw new ArgumentException("Internal error"); } if (rawPatterns.WhereType <LexPattern>().Any(it => it.Type == LexPattern.TypeEnum.EofAction)) { if (rawPatterns.Count() != 1) { throw new ArgumentException("Internal error"); } else { return(rawPatterns.WhereType <LexPattern>().Single()); } } IEnumerable <string> unknowns = rawPatterns .WhereType <LexPatternName>() .Select(it => it.Name) .Where(it => !lexerPatternNames.ContainsKey(it)) .ToArray(); if (unknowns.Any()) { ParseControlException.ThrowAndRun(new[] { "Unknown pattern name(s): " + unknowns.Join(", ") + "." }); } IEnumerable <LexPattern> patterns = rawPatterns.Select(it => { var name = it as LexPatternName; if (name == null) { return(it as LexPattern); } else { return(lexerPatternNames[name.Name]); } }).ToArray(); LexPattern head = patterns.First(); if (patterns.Any(it => head.Type != it.Type || head.StringComparison != it.StringComparison)) { ParseControlException.ThrowAndRun(new[] { "Cannot mix pattern modes." }); } return(LexPattern.Merge(head.Type, head.StringComparison, patterns)); }
private Repetition(int min, int?max) { this.Min = min; this.Max = max; if (Max.HasValue && Max == 0) { throw ParseControlException.NewAndRun("Max repetition cannot be zero."); } if (Max.HasValue && Max < Min) { throw ParseControlException.NewAndRun("Max repetition less than min repetition."); } }
public static Precedence Create(SymbolCoordinates coords, string mode, string associativity, IEnumerable <string> inputSymbols, IEnumerable <PrecedenceWord> conflictProds) { precedenceMode prec = getPrec(mode); AssociativityEnum assoc = getAssoc(associativity); switch (prec) { case precedenceMode.Operator: return(new OperatorPrecedence(coords, assoc, inputSymbols.Select(it => new PrecedenceWord(it)), conflictProds)); case precedenceMode.ReduceShift: return(new ReduceShiftPrecedence(coords, assoc, inputSymbols.Select(it => new PrecedenceWord(it)), conflictProds)); case precedenceMode.ReduceReduce: return(new ReduceReducePrecedence(coords, assoc, inputSymbols.Select(it => new PrecedenceWord(it)), conflictProds)); } throw ParseControlException.NewAndRun("Not recognized precedence"); }
public string Make(Dictionary<string, string> placeholderMapping, bool encloseStatements = true) { foreach (string s in placeholders) if (!placeholderMapping.ContainsKey(s)) throw ParseControlException.NewAndRun("Placeholder \"" + s + "\" not mapped."); var builder = new StringBuilder(); foreach (CodePiece piece in pieces) if (piece.Type == CodePiece.TypeEnum.Placeholder) builder.Append(placeholderMapping[piece.Content]); else builder.Append(piece.Content); string result = builder.ToString().Trim(); if (HasStatement && encloseStatements) return "{" + result + "}"; else return result; }
public ReduceReducePrecedence(SymbolCoordinates coords, AssociativityEnum associativity, IEnumerable <PrecedenceWord> inputTokens, IEnumerable <PrecedenceWord> conflictTokens) : base(coords, associativity, inputTokens.Concat(conflictTokens)) { try { InputSet = inputTokens.ToArray(); ReduceSymbols = conflictTokens.ToArray(); if (ReduceSymbols.Count() < 2) { throw new Exception(); } } catch { throw ParseControlException.NewAndRun("Incorrect reduce/reduce precedence rule"); } }
public AltRegex GetRegex(string strRegex) { IEnumerable <ITokenMatch <SymbolEnum> > tokens = lexer.ScanText(strRegex); ITokenMatch <SymbolEnum> err_token = tokens.FirstOrDefault(it => it.Token == SymbolEnum.Error); if (err_token != null) { throw ParseControlException.NewAndRun("Invalid regex input " + (err_token.ErrorMessage != null?"(" + err_token.ErrorMessage + ")":"") + ": " + strRegex.EscapedString()); } var regex = parser.Parse(tokens, new ParserOptions()).FirstOrDefault() as AltRegex; if (regex == null) { throw ParseControlException.NewAndRun("Invalid regex syntax: " + strRegex.EscapedString()); } return(regex); }
private void validate() { Dictionary <string, int> name_counts = RhsGroups.Select(gr => gr.ObjName) .Concat(RhsGroups.Select(gr => gr.GetSymbols().Select(sym => sym.ObjName)).Flatten()) .Where(it => it != null) .GroupBy(it => it) .ToDictionary(it => it.Key, it => it.Count()); if (name_counts.Any(it => it.Value > 1)) { throw ParseControlException.NewAndRun("Duplicated name"); } if (Code != null) { HashSet <string> named_symbols = new HashSet <string>(objNameCollector(RhsGroups) .Where(it => it != null) ); foreach (string control_var in Code.GetMacroControlVariables()) { if (!named_symbols.Contains(control_var)) { var symbols = new HashSet <string>(rhsGroups .Select(it => it.GetSymbols()).Flatten() .Select(it => it.SymbolName)); var message = "Control name \"" + control_var + "\" is not defined"; if (symbols.Contains(control_var)) { throw ParseControlException.NewAndRun(message + " -- did you mean \"" + CodePiece.PlaceholderSigil + control_var + "\"?"); } else { throw ParseControlException.NewAndRun(message + " (currently: " + named_symbols.Join(",") + ")"); } } } } }
public void ConvertPlaceholderToCode(string placeholder, CodeBody varName) { if (getControlPlaceholder() == placeholder) { if (varName.Pieces.Count() == 1) { ControlName = varName.Pieces.Single(); } else { // the reason for this is we don't handle compound variable/expressions like "foo.field.bar" // and the reason for the reason is, such compound expression can come only from standard placeholders // like $pos or $coords, and it does not make sense to check them if they exist or if they are null or not throw ParseControlException.NewAndRun("Placeholder \"" + placeholder + "\" cannot be used as control variable in macro."); } } varBody.ConvertPlaceholderToCode(placeholder, varName); foreach (CodeMix mix in altCodes) { mix.ConvertPlaceholderToCode(placeholder, varName); } }
internal LexPattern SetStringComparison(StringCaseComparison stringComparison) { if (this.stringComparison.HasValue) { throw new ArgumentException(); } this.stringComparison = stringComparison; if (Type == TypeEnum.Regex) { try { Rule.FormatAsRegex(pattern.AsInternalRegex(), stringComparison); } catch (ArgumentException ex) { throw ParseControlException.NewAndRun(ex.Message); } } return(this); }
internal GrammarOptions(params Tuple <string, bool>[] options) { UseMRE = true; var unknown = new List <string>(); foreach (Tuple <string, bool> opt in options) { if (opt.Item1 == "mre") { UseMRE = opt.Item2; } else { unknown.Add(opt.Item1); } } if (unknown.Any()) { throw ParseControlException.NewAndRun("Unknown option(s): " + unknown.Join(",")); } }
internal Grammar(IEnumerable <Tuple <GrammarElementEnum, object> > elements, IEnumerable <LexItem> implicitLexerRules, List <string> warnings) { var throw_errors = new List <string>(); { Dictionary <GrammarElementEnum, int> elem_counts = EnumExtensions.GetValues <GrammarElementEnum>().ToDictionary(it => it, it => 0); foreach (Tuple <GrammarElementEnum, object> elem in elements) { if (elem.Item2 != null) { ++elem_counts[elem.Item1]; } } var optionals = new HashSet <GrammarElementEnum>(new GrammarElementEnum[] { GrammarElementEnum.Types, GrammarElementEnum.Terminals, GrammarElementEnum.Options, GrammarElementEnum.Prededence, GrammarElementEnum.LexerTypeInfo, GrammarElementEnum.ExplicitLexerRules, GrammarElementEnum.LexerStates, GrammarElementEnum.ParserTypeInfo, GrammarElementEnum.ParserRules, GrammarElementEnum.PatternsInfo, GrammarElementEnum.Using }); foreach (KeyValuePair <GrammarElementEnum, int> count in elem_counts.Where(it => it.Value != 1)) { if (count.Value > 1) { throw ParseControlException.NewAndRun(count.Key.ToString() + " section is duplicated"); } // if not optional section else if (!optionals.Contains(count.Key)) { throw ParseControlException.NewAndRun(count.Key.ToString() + " section is missing"); } } { // if we have lexer name section, then we have to have lexer rules as well // in reverse -- if we don't have the first, we cannot have the latter one and lexer states section // so it is not symmetric! var lexer_count = elem_counts[GrammarElementEnum.LexerTypeInfo]; if (elem_counts[GrammarElementEnum.LexerStates] > lexer_count || elem_counts[GrammarElementEnum.ExplicitLexerRules] != lexer_count) { throw ParseControlException.NewAndRun("Lexer definition is given only partially: lexer name section " + (lexer_count > 0 ? "exists" : "does not exist") + " while lexer states section " + (elem_counts[GrammarElementEnum.LexerStates] > 0 ? "exists" : "does not exist") + " and lexer rules section " + (elem_counts[GrammarElementEnum.ExplicitLexerRules] > 0 ? "exists" : "does not exist") + "."); } } if (elem_counts[GrammarElementEnum.ParserRules] != elem_counts[GrammarElementEnum.ParserTypeInfo]) { throw ParseControlException.NewAndRun("Parser definition is given only partially"); } } Dictionary <GrammarElementEnum, object> dict_elements = EnumExtensions.GetValues <GrammarElementEnum>() .ToDictionary(it => it, it => (object)null); foreach (Tuple <GrammarElementEnum, object> elem in elements) { dict_elements[elem.Item1] = elem.Item2; } this.usingList = (((IEnumerable <string>)dict_elements[GrammarElementEnum.Using]) ?? new string[] { }).ToList(); this.types = (((IEnumerable <SymbolInfo>)dict_elements[GrammarElementEnum.Types]) ?? new SymbolInfo[] { }).ToDictionary(it => it.SymbolName, it => it.TypeName); this.guessed_types = new Dictionary <string, string>(); this.NamespaceName = ((string)dict_elements[GrammarElementEnum.Namespace]); this.TokenTypeInfo = ((TokenInfo)dict_elements[GrammarElementEnum.TokenName]); this.PatternsTypeInfo = ((PatternsInfo)dict_elements[GrammarElementEnum.PatternsInfo]) ?? new PatternsInfo(null, null); this.LexerTypeInfo = ((FactoryTypeInfo)dict_elements[GrammarElementEnum.LexerTypeInfo]); this.Options = ((GrammarOptions)dict_elements[GrammarElementEnum.Options]) ?? new GrammarOptions(); this.LexerStates = ((StatesInfo)dict_elements[GrammarElementEnum.LexerStates]) ?? StatesInfo.CreateDefault(); // implicit rules first, because lexer matches text in longest-first fashion // when user explicitly adds something like <anything> rule with reversed concat order // implicit rules will never be matched this.lexerRules = implicitLexerRules.Concat((((IEnumerable <IScanningRule>)dict_elements[GrammarElementEnum.ExplicitLexerRules]) ?? new LexItem[] { }) .Select(it => it as LexItem).Where(it => it != null)).ToList(); { IEnumerable <LexPatternVariable> pattern_vars = (((IEnumerable <IScanningRule>)dict_elements[GrammarElementEnum.ExplicitLexerRules]) ?? new LexPatternVariable[] { }) .Select(it => it as LexPatternVariable).Where(it => it != null).ToArray(); this.LexerPatternFields = pattern_vars.Where(it => it.WithField).ToArray(); this.lexerPatternNames = pattern_vars.ToDictionary(it => it.Name, it => it.Pattern); } this.lexerRules.ForEach(it => it.OutputPattern = mergeLexPatterns(it.InputPatterns)); this.ParserTypeInfo = ((FactoryTypeInfo)dict_elements[GrammarElementEnum.ParserTypeInfo]); this.parserPrecedences = (((IEnumerable <Precedence>)dict_elements[GrammarElementEnum.Prededence]) ?? Enumerable.Empty <Precedence>()).ToList(); this.terminalRegistry = new OrderedSet <string>( (new[] { EOFSymbol }) .Concat((((IEnumerable <string>)dict_elements[GrammarElementEnum.Terminals]) ?? new string[] { })) .Concat( lexerRules.Select(lex_it => lex_it.Context.Concat(lex_it.TerminalName)).Flatten() // error symbol can appear in lexer productions, it is not a terminal though .Where(s => s != null && s != Grammar.ErrorSymbol))); this.parserProductions = new List <Production>(); AddProductions(((IEnumerable <Production>)dict_elements[GrammarElementEnum.ParserRules]) ?? new Production[] { }); this.symbolRegistry = new OrderedSet <string>( (new[] { ErrorSymbol }) .Concat(terminalRegistry) // EOF starts that set .Concat(nonTerminalRegistry) .Concat(ParserProductions .Select(prod => prod.RhsAlternatives .Select(alt => alt.RhsGroups .Select(grp => grp.GetSymbols().Select(s => s.SymbolName)) .Flatten()) .Flatten()) .Flatten())); // here we get partial mapping (useful for lexer, which sees only part of all symbols) InitSymbolMapping(); if (!IsParserBuilt) { var errors = new List <string>(); if (dict_elements[GrammarElementEnum.Types] != null) { errors.Add("types"); } if (dict_elements[GrammarElementEnum.Prededence] != null) { errors.Add("precedence"); } if (dict_elements[GrammarElementEnum.ParserRules] != null) { errors.Add("parser productions"); } if (errors.Any()) { throw ParseControlException.NewAndRun("Parser is not built (no parser name is given); " + errors.Join(", ") + " section(s) are meaningless."); } } { IEnumerable <string> undef_symbols = new HashSet <string>(types.Select(it => it.Key).Where(it => !symbolRegistry.Contains(it))); if (undef_symbols.Any()) { warnings.Add("Undefined symbol(s) in types section: " + String.Join(", ", undef_symbols)); } } if (IsParserBuilt) { var rhs_symbol_names = new HashSet <string>(ParserProductions.Select(it => it.RhsAlternatives).Flatten() .Select(it => it.RhsGroups).Flatten() .Select(it => it.GetSymbols()).Flatten() .Select(it => it.SymbolName)); IEnumerable <string> unused_terminals = terminalRegistry.Where(it => it != Grammar.EOFSymbol && !rhs_symbol_names.Contains(it)).ToList(); if (unused_terminals.Any()) { warnings.Add("Unused terminal(s) in parsing section: " + String.Join(", ", unused_terminals)); } } ParseControlException.ThrowAndRun(throw_errors); }
internal void AddProductions(IEnumerable <Production> productions) { foreach (SymbolInfo sym_info in productions.Select(it => it.LhsSymbol).Where(it => it.TypeName != null)) { { string typename; if (!this.types.TryGetValue(sym_info.SymbolName, out typename)) { setTypeNameOfSymbol(sym_info.SymbolName, sym_info.TypeName); } else if (typename != sym_info.TypeName) { throw ParseControlException.NewAndRun(sym_info.SymbolName + " defined as " + sym_info.TypeName + " has already type " + typename); } } } foreach (Production prod in productions) { foreach (AltRule alt in prod.RhsAlternatives.Where(it => it.Code != null)) { string guess = alt.Code.BuildBody(presentVariables: null).GuessTypeName(); string typename; if (!this.guessed_types.TryGetValue(prod.LhsSymbol.SymbolName, out typename)) { // it can be null, meaning it was not recognized this.guessed_types.Add(prod.LhsSymbol.SymbolName, guess); } // if there is a conflict with guessing set it is as not recognized else if (typename != guess) { this.guessed_types[prod.LhsSymbol.SymbolName] = null; } } } parserProductions.AddRange(productions); this.nonTerminalRegistry = new HashSet <string>(ParserProductions.Select(prod => prod.LhsSymbol.SymbolName)); var unknown = new HashSet <string>(); var lhs_terminals = new List <string>(); foreach (Production prod in productions) { foreach (AltRule alt in prod.RhsAlternatives) { foreach (IEnumerable <RhsSymbol> symbols in alt.RhsGroups.Select(it => it.GetSymbols())) { unknown.AddRange(symbols .Where(sym => !terminalRegistry.Contains(sym.SymbolName) && !nonTerminalRegistry.Contains(sym.SymbolName) && !types.ContainsKey(sym.SymbolName) && sym.SymbolName != ErrorSymbol) .Select(it => it.Coords.XYString() + " " + it.SymbolName)); } } if (terminalRegistry.Contains(prod.LhsSymbol.SymbolName)) { lhs_terminals.Add(prod.LhsSymbol.SymbolName); } } var errors = new List <string>(); if (lhs_terminals.Any()) { errors.Add("Terminal(s) " + lhs_terminals.Join(",") + " cannot be used at LHS of the production"); } if (unknown.Any()) { errors.Add("Undefined symbol(s): " + String.Join(",", unknown)); } ParseControlException.ThrowAndRun(errors); }
internal static IEnumerable <Precedence> CreateOperators(SymbolCoordinates coords, string mode, IEnumerable <Tuple <string, string> > __opPairs, // it can be single and then serves as both reduce production and shift production // or it can be many, but then remember to put reduce production as first one (exactly as in RS rule) IEnumerable <string> __conflictProds) { if (!__conflictProds.Any()) { throw ParseControlException.NewAndRun("There has to be at least one production given for operator precedence."); } string reduce_prod = __conflictProds.First(); IEnumerable <string> shift_prod = __conflictProds.Count() == 1 ? new [] { __conflictProds.Single() }: __conflictProds.Skip(1).ToArray(); precedenceMode prec = getPrec(mode); if (prec != precedenceMode.ReduceShift) { throw ParseControlException.NewAndRun("Only shift-reduce mode is supported with this syntax."); } Tuple <string, string>[] op_pairs = __opPairs.ToArray(); var result = new List <Precedence>(); for (int i = 0; i < op_pairs.Length; ++i) { string input_sym = op_pairs[i].Item2; var group = new List <Precedence>(); { // same vs same // here we go as given associativity, because priority is the same (of course) AssociativityEnum assoc = getAssoc(op_pairs[i].Item1); group.Add(new ReduceShiftPrecedence(coords, assoc, new[] { new PrecedenceWord(input_sym) }, PrecedenceWord.Create(reduce_prod, input_sym).Concat(shift_prod.Select(it => new PrecedenceWord(it)))) { PriorityGroupEnd = false, PriorityGroupStart = false }); } if (i > 0) { // higher priority means that if master is in input we shift, and when it is on stack we reduce group.Add(new ReduceShiftPrecedence(coords, AssociativityEnum.Reduce, op_pairs.Take(i).Select(it => new PrecedenceWord(it.Item2)), PrecedenceWord.Create(reduce_prod, input_sym).Concat(shift_prod.Select(it => new PrecedenceWord(it)))) { PriorityGroupEnd = false, PriorityGroupStart = false }); group.Add(new ReduceShiftPrecedence(coords, AssociativityEnum.Shift, new[] { new PrecedenceWord(input_sym) }, new PrecedenceWord(reduce_prod, op_pairs.Take(i).Select(it => it.Item2)).Concat(shift_prod.Select(it => new PrecedenceWord(it)))) { PriorityGroupEnd = false, PriorityGroupStart = false }); } group.First().PriorityGroupStart = true; group.Last().PriorityGroupEnd = true; result.AddRange(group); } return(result); }