public static void EmitTable(Context ctx, StreamReader r, StreamWriter w, bool makeHeaders, string filePath) { var lineno = 1; Template.Transfer(ctx.Name, r, w, ref lineno); /* Generate the include code, if any */ Template.Write(w, ctx, ctx.Include, ref lineno, filePath); if (makeHeaders) { // TODO: get headerfile var filePathH = filePath; w.WriteLine(ref lineno, "#include \"{0}\"", filePathH); } Template.Transfer(ctx.Name, r, w, ref lineno); /* Generate #defines for all tokens */ if (makeHeaders) { w.WriteLine(ref lineno, "#if INTERFACE"); var prefix = (ctx.TokenPrefix ?? string.Empty); for (var i = 1; i < ctx.Terminals; i++) { w.WriteLine(ref lineno, "#define {0}{1,-30} {2,2}", prefix, ctx.Symbols[i].Name, i); } w.WriteLine(ref lineno, "#endif"); } Template.Transfer(ctx.Name, r, w, ref lineno); /* Generate the defines */ w.WriteLine(ref lineno, "#define YYCODETYPE {0}", GetMinimumSizeType(0, ctx.Symbols.Length)); w.WriteLine(ref lineno, "#define YYNOCODE {0}", ctx.Symbols.Length); w.WriteLine(ref lineno, "#define YYACTIONTYPE {0}", GetMinimumSizeType(0, ctx.States + ctx.Rules + 5)); if (ctx.Wildcard != null) { w.WriteLine(ref lineno, "#define YYWILDCARD {0}", ctx.Wildcard.ID); } EmitStackUnion(w, ctx, ref lineno, makeHeaders); w.WriteLine(ref lineno, "#ifndef YYSTACKDEPTH"); if (ctx.StackSize != null) { w.WriteLine(ref lineno, "#define YYSTACKDEPTH {0}", ctx.StackSize); } else { w.WriteLine(ref lineno, "#define YYSTACKDEPTH 100"); } w.WriteLine(ref lineno, "#endif"); if (makeHeaders) { w.WriteLine(ref lineno, "#if INTERFACE"); } var name = (ctx.Name ?? "Parse"); if (!string.IsNullOrEmpty(ctx.ExtraArg)) { var i = ctx.ExtraArg.Length; while (i >= 1 && char.IsWhiteSpace(ctx.ExtraArg[i - 1])) { i--; } while (i >= 1 && (char.IsLetterOrDigit(ctx.ExtraArg[i - 1]) || ctx.ExtraArg[i - 1] == '_')) { i--; } w.WriteLine(ref lineno, "#define {0}ARG_SDECL {1};", name, ctx.ExtraArg); w.WriteLine(ref lineno, "#define {0}ARG_PDECL ,{1}", name, ctx.ExtraArg); w.WriteLine(ref lineno, "#define {0}ARG_FETCH {1} = yypParser.{2}", name, ctx.ExtraArg, ctx.ExtraArg.Substring(i)); w.WriteLine(ref lineno, "#define {0}ARG_STORE yypParser.{1} = {2}", name, ctx.ExtraArg.Substring(i), ctx.ExtraArg.Substring(i)); } else { w.WriteLine(ref lineno, "#define {0}ARG_SDECL", name); w.WriteLine(ref lineno, "#define {0}ARG_PDECL", name); w.WriteLine(ref lineno, "#define {0}ARG_FETCH", name); w.WriteLine(ref lineno, "#define {0}ARG_STORE", name); } if (makeHeaders) { w.WriteLine(ref lineno, "#endif"); } w.WriteLine(ref lineno, "#define YYNSTATE {0}", ctx.States); w.WriteLine(ref lineno, "#define YYNRULE {0}", ctx.Rules); if (ctx.ErrorSymbol.Uses > 0) { w.WriteLine(ref lineno, "#define YYERRORSYMBOL {0}", ctx.ErrorSymbol.ID); w.WriteLine(ref lineno, "#define YYERRSYMDT yy{0}", ctx.ErrorSymbol.DataTypeID); } if (ctx.HasFallback) { w.WriteLine(ref lineno, "#define YYFALLBACK 1"); } Template.Transfer(ctx.Name, r, w, ref lineno); int maxTokenOffset; int minTokenOffset; int maxNonTerminalOffset; int minNonTerminalOffset; var actionTable = EmitterActionTable.Make(ctx, out maxTokenOffset, out minTokenOffset, out maxNonTerminalOffset, out minNonTerminalOffset); /* Output the yy_action table */ var n = actionTable.Size; w.WriteLine(ref lineno, "#define YY_ACTTAB_COUNT ({0})", n); w.WriteLine(ref lineno, "static const YYACTIONTYPE yy_action[] = {"); for (int i = 0, j = 0; i < n; i++) { var action = actionTable.GetAction(i); if (action < 0) { action = ctx.States + ctx.Rules + 2; } if (j == 0) { w.Write(" /* {0,5} */ ", i); } w.Write(" {0,4},", action); if (j == 9 || i == n - 1) { w.WriteLine(ref lineno); j = 0; } else { j++; } } w.WriteLine(ref lineno, "};"); /* Output the yy_lookahead table */ w.WriteLine(ref lineno, "static const YYCODETYPE yy_lookahead[] = {"); for (int i = 0, j = 0; i < n; i++) { var lookahead = actionTable.GetLookahead(i); if (lookahead < 0) { lookahead = ctx.Symbols.Length - 1; } if (j == 0) { w.Write(" /* {0,5} */ ", i); } w.Write(" {0,4},", lookahead); if (j == 9 || i == n - 1) { w.WriteLine(ref lineno); j = 0; } else { j++; } } w.WriteLine(ref lineno, "};"); /* Output the yy_shift_ofst[] table */ w.WriteLine(ref lineno, "#define YY_SHIFT_USE_DFLT ({0})", minTokenOffset - 1); n = ctx.States; while (n > 0 && ctx.Sorted[n - 1].TokenOffset == State.NO_OFFSET) { n--; } w.WriteLine(ref lineno, "#define YY_SHIFT_COUNT ({0})", n - 1); w.WriteLine(ref lineno, "#define YY_SHIFT_MIN ({0})", minTokenOffset); w.WriteLine(ref lineno, "#define YY_SHIFT_MAX ({0})", maxTokenOffset); w.WriteLine(ref lineno, "static const {0} yy_shift_ofst[] = {{", GetMinimumSizeType(minTokenOffset - 1, maxTokenOffset)); for (int i = 0, j = 0; i < n; i++) { var state = ctx.Sorted[i]; var offset = state.TokenOffset; if (offset == State.NO_OFFSET) { offset = minTokenOffset - 1; } if (j == 0) { w.Write(" /* {0,5} */ ", i); } w.Write(" {0,4},", offset); if (j == 9 || i == n - 1) { w.WriteLine(ref lineno); j = 0; } else { j++; } } w.WriteLine(ref lineno, "};"); /* Output the yy_reduce_ofst[] table */ w.WriteLine(ref lineno, "#define YY_REDUCE_USE_DFLT ({0})", minNonTerminalOffset - 1); n = ctx.States; while (n > 0 && ctx.Sorted[n - 1].NonTerminalOffset == State.NO_OFFSET) { n--; } w.WriteLine(ref lineno, "#define YY_REDUCE_COUNT ({0})", n - 1); w.WriteLine(ref lineno, "#define YY_REDUCE_MIN ({0})", minNonTerminalOffset); w.WriteLine(ref lineno, "#define YY_REDUCE_MAX ({0})", maxNonTerminalOffset); w.WriteLine(ref lineno, "static const {0} yy_reduce_ofst[] = {{", GetMinimumSizeType(minNonTerminalOffset - 1, maxNonTerminalOffset)); for (int i = 0, j = 0; i < n; i++) { var state = ctx.Sorted[i]; var offset = state.NonTerminalOffset; if (offset == State.NO_OFFSET) { offset = minNonTerminalOffset - 1; } if (j == 0) { w.Write(" /* {0,5} */ ", i); } w.Write(" {0,4},", offset); if (j == 9 || i == n - 1) { w.WriteLine(ref lineno); j = 0; } else { j++; } } w.WriteLine(ref lineno, "};"); /* Output the default action table */ w.WriteLine(ref lineno, "static const YYACTIONTYPE yy_default[] = {"); n = ctx.States; for (int i = 0, j = 0; i < n; i++) { var state = ctx.Sorted[i]; if (j == 0) { w.Write(" /* {0,5} */ ", i); } w.Write(" {0,4},", state.Default); if (j == 9 || i == n - 1) { w.WriteLine(ref lineno); j = 0; } else { j++; } } w.WriteLine(ref lineno, "};"); Template.Transfer(ctx.Name, r, w, ref lineno); /* Generate the table of fallback tokens. */ if (ctx.HasFallback) { var mx = ctx.Terminals - 1; while (mx > 0 && ctx.Symbols[mx].Fallback == null) { mx--; } for (var i = 0; i <= mx; i++) { var symbol = ctx.Symbols[i]; if (symbol.Fallback == null) { w.WriteLine(ref lineno, " 0, /* {0,10} => nothing */", symbol.Name); } else { w.WriteLine(ref lineno, " {0,3}, /* {1,10} => {2} */", symbol.Fallback.ID, symbol.Name, symbol.Fallback.Name); } } } Template.Transfer(ctx.Name, r, w, ref lineno); /* Generate a table containing the symbolic name of every symbol */ var i_ = 0; for (i_ = 0; i_ < ctx.Symbols.Length - 1; i_++) { w.Write(" {0,-15}", string.Format("\"{0}\",", ctx.Symbols[i_].Name)); if ((i_ & 3) == 3) { w.WriteLine(ref lineno); } } if ((i_ & 3) != 0) { w.WriteLine(ref lineno); } Template.Transfer(ctx.Name, r, w, ref lineno); /* Generate a table containing a text string that describes every rule in the rule set of the grammar. This information is used when tracing REDUCE actions. */ i_ = 0; for (var rule = ctx.Rule; rule != null; rule = rule.Next, i_++) { Debug.Assert(rule.ID == i_); w.Write(" /* {0,3} */ \"", i_); WriteRuleText(w, rule); w.WriteLine(ref lineno, "\","); } Template.Transfer(ctx.Name, r, w, ref lineno); /* Generate code which executes every time a symbol is popped from the stack while processing errors or while destroying the parser. (In other words, generate the %destructor actions) */ if (ctx.TokenDestructor != null) { var once = true; for (var i = 0; i < ctx.Symbols.Length - 1; i++) { var symbol = ctx.Symbols[i]; if (symbol == null || symbol.Type != SymbolType.Terminal) { continue; } if (once) { w.WriteLine(ref lineno, " /* TERMINAL Destructor */"); once = false; } w.WriteLine(ref lineno, " case {0}: /* {1} */", symbol.ID, symbol.Name); } var i2 = 0; for (; i2 < (ctx.Symbols.Length - 1) && (ctx.Symbols[i2].Type != SymbolType.Terminal); i2++) { ; } if (i2 < ctx.Symbols.Length - 1) { EmitDestructor(w, ctx.Symbols[i2], ctx, ref lineno, filePath); w.WriteLine(ref lineno, " break;"); } } if (ctx.DefaultDestructor != null) { var dflt_sp = (Symbol)null; var once = true; for (var i = 0; i < ctx.Symbols.Length - 1; i++) { var symbol = ctx.Symbols[i]; if (symbol == null || symbol.Type == SymbolType.Terminal || symbol.ID <= 0 || symbol.Destructor != null) { continue; } if (once) { w.WriteLine(ref lineno, " /* Default NON-TERMINAL Destructor */"); once = false; } w.WriteLine(ref lineno, " case {0}: /* {1} */", symbol.ID, symbol.Name); dflt_sp = symbol; } if (dflt_sp != null) { EmitDestructor(w, dflt_sp, ctx, ref lineno, filePath); } w.WriteLine(ref lineno, " break;"); } for (var i = 0; i < ctx.Symbols.Length - 1; i++) { var symbol = ctx.Symbols[i]; if (symbol == null || symbol.Type == SymbolType.Terminal || symbol.Destructor == null) { continue; } w.WriteLine(ref lineno, " case {0}: /* {1} */", symbol.ID, symbol.Name); /* Combine duplicate destructors into a single case */ for (var j = i + 1; j < ctx.Symbols.Length - 1; j++) { var symbol2 = ctx.Symbols[j]; if (symbol2 != null && symbol2.Type != SymbolType.Terminal && symbol2.Destructor != null && symbol2.DataTypeID == symbol.DataTypeID && symbol.Destructor == symbol2.Destructor) { w.WriteLine(ref lineno, " case {0}: /* {1} */", symbol2.ID, symbol2.Name); symbol2.Destructor = null; } } EmitDestructor(w, ctx.Symbols[i], ctx, ref lineno, filePath); w.WriteLine(ref lineno, " break;"); } Template.Transfer(ctx.Name, r, w, ref lineno); /* Generate code which executes whenever the parser stack overflows */ Template.Write(w, ctx, ctx.StackOverflow, ref lineno, filePath); Template.Transfer(ctx.Name, r, w, ref lineno); /* Generate the table of rule information ** Note: This code depends on the fact that rules are number sequentually beginning with 0. */ for (var rule = ctx.Rule; rule != null; rule = rule.Next) { w.WriteLine(ref lineno, " {{ {0}, {1} }},", rule.LHSymbol.ID, rule.RHSymbols.Length); } Template.Transfer(ctx.Name, r, w, ref lineno); /* Generate code which execution during each REDUCE action */ for (var rule = ctx.Rule; rule != null; rule = rule.Next) { TranslateRuleCode(ctx, rule); } /* First output rules other than the default: rule */ for (var rule = ctx.Rule; rule != null; rule = rule.Next) { if (rule.Code == null) { continue; } if (rule.Code[0] == '\n' && rule.Code.Length == 1) { continue; /* Will be default: */ } w.Write(" case {0}: /* ", rule.ID); WriteRuleText(w, rule); w.WriteLine(ref lineno, " */"); for (var rule2 = rule.Next; rule2 != null; rule2 = rule2.Next) { if (rule2.Code == rule.Code) { w.Write(" case {0}: /* ", rule2.ID); WriteRuleText(w, rule2); w.WriteLine(ref lineno, " */ yytestcase(yyruleno=={0});", rule2.ID); rule2.Code = null; } } EmitRuleCode(w, rule, ctx, ref lineno, filePath); w.WriteLine(ref lineno, " break;"); rule.Code = null; } /* Finally, output the default: rule. We choose as the default: all empty actions. */ w.WriteLine(ref lineno, " default:"); for (var rule = ctx.Rule; rule != null; rule = rule.Next) { if (rule.Code == null) { continue; } Debug.Assert(rule.Code[0] == '\n' && rule.Code.Length == 1); w.Write(" /* ({0}) ", rule.ID); WriteRuleText(w, rule); w.WriteLine(ref lineno, " */ yytestcase(yyruleno=={0});", rule.ID); } w.WriteLine(ref lineno, " break;"); Template.Transfer(ctx.Name, r, w, ref lineno); /* Generate code which executes if a parse fails */ Template.Write(w, ctx, ctx.ParseFailure, ref lineno, filePath); Template.Transfer(ctx.Name, r, w, ref lineno); /* Generate code which executes when a syntax error occurs */ Template.Write(w, ctx, ctx.SyntaxError, ref lineno, filePath); Template.Transfer(ctx.Name, r, w, ref lineno); /* Generate code which executes when the parser accepts its input */ Template.Write(w, ctx, ctx.ParseAccept, ref lineno, filePath); Template.Transfer(ctx.Name, r, w, ref lineno); /* Append any addition code the user desires */ Template.Write(w, ctx, ctx.ExtraCode, ref lineno, filePath); }
/* Generate the action table and its associates: ** ** yy_action[] A single table containing all actions. ** yy_lookahead[] A table containing the lookahead for each entry in ** yy_action. Used to detect hash collisions. ** yy_shift_ofst[] For each state, the offset into yy_action for ** shifting terminals. ** yy_reduce_ofst[] For each state, the offset into yy_action for ** shifting non-terminals after a reduce. ** yy_default[] Default action for each state. */ public static EmitterActionTable Make(Context ctx, out int maxTokenOffset, out int minTokenOffset, out int maxNonTerminalOffset, out int minNonTerminalOffset) { /* Compute the actions on all states and count them up */ var ax = new AX[ctx.States * 2]; for (var i = 0; i < ctx.States; i++) { var state = ctx.Sorted[i]; ax[i * 2] = new AX { State = state, Token = true, Actions = state.TokenActions }; ax[i * 2 + 1] = new AX { State = state, Token = false, Actions = state.NonTerminalActions }; } maxTokenOffset = minTokenOffset = 0; maxNonTerminalOffset = minNonTerminalOffset = 0; /* Compute the action table. In order to try to keep the size of the action table to a minimum, the heuristic of placing the largest action sets first is used. */ for (var i = 0; i < ctx.States * 2; i++) { ax[i].iOrder = i; } Array.Sort(ax, _keyComparer); var actionTable = new EmitterActionTable(); for (var i = 0; i < ctx.States * 2 && ax[i].Actions > 0; i++) { var state = ax[i].State; if (ax[i].Token) { foreach (var action in state.Actions) { if (action.Symbol.ID >= ctx.Terminals) { continue; } var actionID = action.ComputeID(ctx); if (actionID < 0) { continue; } actionTable.Action(action.Symbol.ID, actionID); } state.TokenOffset = actionTable.Insert(); if (state.TokenOffset < minTokenOffset) { minTokenOffset = state.TokenOffset; } if (state.TokenOffset > maxTokenOffset) { maxTokenOffset = state.TokenOffset; } } else { foreach (var action in state.Actions) { if (action.Symbol.ID < ctx.Terminals) { continue; } if (action.Symbol.ID == ctx.Symbols.Length - 1) { continue; } var actionID = action.ComputeID(ctx); if (actionID < 0) { continue; } actionTable.Action(action.Symbol.ID, actionID); } state.NonTerminalOffset = actionTable.Insert(); if (state.NonTerminalOffset < minNonTerminalOffset) { minNonTerminalOffset = state.NonTerminalOffset; } if (state.NonTerminalOffset > maxNonTerminalOffset) { maxNonTerminalOffset = state.NonTerminalOffset; } } } ax = null; return(actionTable); }