public string GenerateCode(Skeleton skeleton, string generatorVersion)
        {
            var template = skeleton.Template;
            var equivalenceClassDataType = GenDataType(skeleton, equivalenceTable.Max(ec => ec.Value));

            template = Replace(template, "EquivalenceClassType", equivalenceClassDataType);
            template = Replace(template, "GeneratorVersion", generatorVersion);
            template = Replace(template, "ClassName", lexerSpec.LexerName ?? "Lexer");
            template = Replace(template, "ClassNamespace", lexerSpec.LexerNamespace);
            template = Replace(template, "Channels", GenChannels());
            template = Replace(template, "TokenTypes", GenTokenTypes());
            template = Replace(template, "Modes", GetModes());
            template = Replace(template, "InitialMode", lexerSpec.InitialMode.ToString());
            template = Replace(template, "ErrorState", errorState.ToString());
            template = Replace(template, "PlaneOffsets", GenPlaneOffsets(skeleton).ToList());
            template = Replace(template, "EquivalenceClassCases", GenEquivalenceClassCases().ToList());
            template = Replace(template, "EquivalenceTable", GenEquivalenceTable());
            template = Replace(template, "RowMapType", GenDataType(skeleton, rowMap.Max()));
            template = Replace(template, "RowMap", GenInts(rowMap));
            template = Replace(template, "StateType", GenDataType(skeleton, transitions.Max()));
            // TODO need to create an error state so that we don't need to put negative values in the table
            template = Replace(template, "Transitions", GenInts(transitions));
            template = Replace(template, "ActionMapType", GenDataType(skeleton, actionMap.Max()));
            template = Replace(template, "ActionMap", GenInts(actionMap));
            template = Replace(template, "Actions", GenActions(skeleton).ToList());
            return template;
        }
 public string FileName(Skeleton skeleton)
 {
     return Path.ChangeExtension(lexerSpec.LexerName, skeleton.Extension);
 }
        private IEnumerable<string> GenActions(Skeleton skeleton)
        {
            yield return "case 0:";
            yield return "	continue;";

            for(var i = 1; i < actions.Length; i++)
            {
                var action = actions[i];
                yield return $"case {i}:";
                // Lexer Input Action
                DecodeValue decodeAction;
                TextValue valueAction;
                if(action.ValueAction == LexerValueAction.Capture)
                    yield return "	captureBuffer.Append(tokenBuffer.ToString());";
                else if((decodeAction = action.ValueAction as DecodeValue) != null)
                    yield return $"	captureBuffer.Append(Decode(tokenBuffer.ToString(), {decodeAction.Base}));";
                else if((valueAction = action.ValueAction as TextValue) != null)
                    yield return $"	tokenBuffer.Append(\"{valueAction.Value}\");";
                else if(action.ValueAction == LexerValueAction.Ignore)
                {
                    // Nothing to do on ignore
                }
                else
                    throw new NotSupportedException($"Unsupported LexerValueAction type '{action.ValueAction.GetType().FullName}'");

                foreach(var modeAction in action.ModeActions)
                {
                    if(modeAction == LexerModeAction.Pop)
                        yield return "	currentMode = modeStack.Pop();";
                    else if(modeAction == LexerModeAction.Push)
                        yield return "	modeStack.Push(currentMode);";
                    else
                    {
                        var mode = ((SetMode)modeAction).Mode;
                        yield return $"	currentMode = Mode.{mode};";
                    }
                }
                EmitToken emitAction;
                if((emitAction = action.EmitAction as EmitToken) != null)
                {
                    yield return $"	token = new Adamant.CompilerCompiler.Lex.Runtime.Token<Channel, TokenType>(Channel.{emitAction.Channel}, TokenType.{emitAction.TokenType}, false, default(Adamant.CompilerCompiler.Lex.Runtime.FilePosition), default(Adamant.CompilerCompiler.Lex.Runtime.FilePosition), captureBuffer.ToString());";
                }

                if(action.Code != null)
                    yield return action.Code;

                yield return "	break;";
            }
        }
 private IEnumerable<string> GenPlaneOffsets(Skeleton skeleton)
 {
     foreach(var planeOffset in planeOffsets)
     {
         if(planeOffset.Value.Length == 1) continue;
         var dataType = GenDataType(skeleton, planeOffset.Value.Max());
         yield return $"private static readonly {dataType}[] plane{planeOffset.Key}Offsets =";
         yield return "{";
         foreach(var line in GenCommaSeparatedLines(planeOffset.Value.Select(v => v.ToString())))
             yield return "\t" + line;
         yield return "};";
     }
 }
 private static string GenDataType(Skeleton skeleton, int value)
 {
     return skeleton.DataTypes.OrderBy(dt => dt.Bits).First(dt => value <= Math.Pow(2, dt.Bits)).Name;
 }