<|start_filename|>TestPrograms/FloatCalculation.c<|end_filename|>
int printf(char *, ...);
int main() {
    float f1;
    float f2;
    double d1;
    double d2;
    f1 = 2.333f;
    f2 = 33.3f;
    d1 = 1.23456789;
    d2 = 2.3456789;
    printf("lf\n", f1 + f2);
    printf("lf\n", d1 / d2);
    printf("lf\n", f1 * d1);
    printf("lf\n", f2 - d2);
    return 0;
}


<|start_filename|>CompilerTests/CompilerTests.cs<|end_filename|>
﻿using System;
using System.IO;
using System.Linq;
using CodeGeneration;
using LexicalAnalysis;
using NUnit.Framework;
using Parsing;

namespace CompilerTests {
    [TestFixture]
    public class CompilerTests {

        [Test]
        public void TestCompiler() {
            Console.WriteLine(Directory.GetCurrentDirectory());

            var files = Directory
                .GetFiles("../../TestPrograms")
                .Where(_ => _.EndsWith(".c"));

            var sources = files.Select(File.ReadAllText).ToArray();

            foreach (var source in sources) {
                var scanner = new Scanner(source);
                var tokens = scanner.Tokens;
                var env = new ParserEnvironment();
                var input = new ParserInput(env, tokens);
                var parserResult = CParsers.TranslationUnit.Parse(input);
                var semantResult = parserResult.Result.GetTranslnUnit();
                semantResult.Value.CodeGenerate(new CGenState());
            }
        }

    }
}


<|start_filename|>CGen/TypeCast.cs<|end_filename|>
﻿using System;
using CodeGeneration;

namespace ABT {
    public sealed partial class TypeCast {
        public override Reg CGenValue(CGenState state) {
            Reg ret = this.Expr.CGenValue(state);
            switch (this.Kind) {
                case TypeCastType.DOUBLE_TO_FLOAT:
                case TypeCastType.FLOAT_TO_DOUBLE:
                case TypeCastType.PRESERVE_INT16:
                case TypeCastType.PRESERVE_INT8:
                case TypeCastType.NOP:
                    return ret;

                case TypeCastType.DOUBLE_TO_INT32:
                case TypeCastType.FLOAT_TO_INT32:
                    state.CGenConvertFloatToLong();
                    return Reg.EAX;

                case TypeCastType.INT32_TO_DOUBLE:
                case TypeCastType.INT32_TO_FLOAT:
                    state.CGenConvertLongToFloat();
                    return Reg.ST0;

                case TypeCastType.INT16_TO_INT32:
                    state.MOVSWL(Reg.AX, Reg.EAX);
                    return ret;

                case TypeCastType.INT8_TO_INT16:
                case TypeCastType.INT8_TO_INT32:
                    state.MOVSBL(Reg.AL, Reg.EAX);
                    return ret;

                case TypeCastType.UINT16_TO_UINT32:
                    state.MOVZWL(Reg.AX, Reg.EAX);
                    return ret;

                case TypeCastType.UINT8_TO_UINT16:
                case TypeCastType.UINT8_TO_UINT32:
                    state.MOVZBL(Reg.AL, Reg.EAX);
                    return ret;

                default:
                    throw new InvalidProgramException();
            }
        }

        public override void CGenAddress(CGenState state) {
            throw new InvalidOperationException("Cannot get the address of a cast expression.");
        }
    }
}

<|start_filename|>Parser/Declaration.cs<|end_filename|>
﻿using System;
using System.Collections.Immutable;
using System.Linq;
using AST;
using static Parsing.ParserCombinator;

namespace Parsing {
    public partial class CParsers {

        /// <summary>
        /// declaration
        ///   : declaration-specifiers [Init-declarator-list]? ';'
        /// </summary>
        public static NamedParser<Decln>
            Declaration { get; } = new NamedParser<Decln>("declaration");

        /// <summary>
        /// declaration-specifiers
        ///   : [ storage-class-specifier | Type-specifier | Type-qualifier ]+
        /// </summary>
        /// <remarks>
        /// 1. You can only have **one** storage class specifier.
        /// 2. You can have duplicate Type qualifiers, since it doesn't cause ambiguity.
        /// </remarks>
        public static NamedParser<DeclnSpecs>
            DeclarationSpecifiers { get; } = new NamedParser<DeclnSpecs>("declaration-specifiers");

        /// <summary>
        /// Init-declarator-list
        ///   : Init-declarator [ ',' Init-declarator ]*
        /// </summary>
        /// <remarks>
        /// a non-empty list of init_declarators separated by ','
        /// </remarks>
        public static NamedParser<ImmutableList<InitDeclr>>
            InitDeclaratorList { get; } = new NamedParser<ImmutableList<InitDeclr>>("Init-declarator-list");

        /// <summary>
        /// Init-declarator
        ///   : declarator [ '=' initializer ]?
        /// </summary>
        public static NamedParser<InitDeclr>
            InitDeclarator { get; } = new NamedParser<InitDeclr>("Init-declarator");

        /// <summary>
        /// storage-class-specifier
        ///   : auto | register | static | extern | typedef
        /// </summary>
        /// <remarks>
        /// There can only be *one* storage class specifier in one declaration.
        /// </remarks>
        public static NamedParser<StorageClsSpec>
            StorageClassSpecifier { get; } = new NamedParser<StorageClsSpec>("storage-class-specifier");

        /// <summary>
        /// Type-specifier
        ///   : void
        ///   | char
        ///   | short
        ///   | int
        ///   | long
        ///   | float
        ///   | double
        ///   | signed
        ///   | unsigned
        ///   | struct-or-union-specifier
        ///   | enum-specifier
        ///   | typedef-name
        /// </summary>
        /// <remarks>
        /// 1. void, char, short, int, long, float, double, signed, unsigned are called "basic Type specifiers".
        /// 2. struct-or-union_specifier and enum-specifier need more complicated parsing.
        /// 3. Parsing typedef-name actually requires the environment to participate. For example, consider this statement:
        ///      T *v;
        ///    Is T a Type or an object? If T is a Type, then this statement is a declaration: v is a pointer; if T is a object, then this statement is an expression.
        ///    So, we need to keep track of the typedefs in the environment even in the parsing stage!
        /// </remarks>
        public static NamedParser<TypeSpec>
            TypeSpecifier { get; } = new NamedParser<TypeSpec>("Type-specifier");

        /// <summary>
        /// Type-qualifier
        ///   : const
        ///   | volatile
        /// </summary>
        /// <remarks>
        /// Note that there can be multiple Type qualifiers in one declarations.
        /// </remarks>
        public static NamedParser<TypeQual>
            TypeQualifier { get; } = new NamedParser<TypeQual>("Type-qualifier");

        /// <summary>
        /// declarator
        ///   : [pointer]? direct-declarator
        /// </summary>
        /// <remarks>
        /// A declarator gives a name to the object and also modifies the Type.
        /// </remarks>
        public static NamedParser<Declr>
            Declarator { get; } = new NamedParser<Declr>("declarator");

        /// <summary>
        /// pointer
        ///   : [ '*' [Type-qualifier-list]? ]+
        /// </summary>
        public static NamedParser<ImmutableList<PointerModifier>>
            Pointer { get; } = new NamedParser<ImmutableList<PointerModifier>>("pointer");

        /// <summary>
        /// parameter-Type-list
        ///   : parameter-list [ ',' '...' ]?
        /// </summary>
        /// <remarks>
        /// A parameter list and an optional vararg signature.
        /// Used in function declarations.
        /// </remarks>
        public static NamedParser<ParamTypeList>
            ParameterTypeList { get; } = new NamedParser<ParamTypeList>("parameter-Type-list");

        /// <summary>
        /// parameter-list
        ///   : parameter-declaration [ ',' parameter-declaration ]*
        /// </summary>
        /// <remarks>
        /// A non-empty list of parameters separated by ','.
        /// Used in a function signature.
        /// </remarks>
        public static NamedParser<ImmutableList<ParamDecln>>
            ParameterList { get; } = new NamedParser<ImmutableList<ParamDecln>>("parameter-list");

        /// <summary>
        /// Type-qualifier-list
        ///   : [Type-qualifier]+
        /// </summary>
        /// <remarks>
        /// A non-empty list of Type qualifiers.
        /// </remarks>
        public static NamedParser<ImmutableList<TypeQual>>
            TypeQualifierList { get; } = new NamedParser<ImmutableList<TypeQual>>("Type-qualifier-list");

        /// <summary>
        /// direct-declarator
        ///   : [
        ///         identifier | '(' declarator ')'
        ///     ] [
        ///         '[' [constant-expression]? ']'
        ///       | '(' [parameter-Type-list]? ')'
        ///     ]*
        /// </summary>
        /// <remarks>
        /// There is an old style of function definition:
        /// +-------------------------------+
        /// |    int foo(param1, param2)    |
        /// |    int  param1;               |
        /// |    char param2;               |
        /// |    {                          |
        /// |        ....                   |
        /// |    }                          |
        /// +-------------------------------+
        /// 
        /// I'm not gonna support this style, and function definitions should always be like this:
        /// +------------------------------------------+
        /// |    int foo(int param1, char param2) {    |
        /// |        ....                              |
        /// |    }                                     |
        /// +------------------------------------------+
        /// </remarks>
        public static NamedParser<Declr>
            DirectDeclarator { get; } = new NamedParser<Declr>("direct-declarator");

        /// <summary>
        /// enum-specifier
        ///   : enum [identifier]? '{' enumerator-list '}'
        ///   | enum identifier
        /// </summary>
        public static NamedParser<EnumSpec>
            EnumSpecifier { get; } = new NamedParser<EnumSpec>("enum-specifier");

        /// <summary>
        /// enumerator-list
        ///   : enumerator [ ',' enumerator ]*
        /// </summary>
        public static NamedParser<ImmutableList<Enumr>>
            EnumeratorList { get; } = new NamedParser<ImmutableList<Enumr>>("enumerator-list");

        /// <summary>
        /// enumerator
        ///   : enumeration-constant [ '=' constant-expression ]?
        /// </summary>
        public static NamedParser<Enumr>
            Enumerator { get; } = new NamedParser<Enumr>("enumerator");

        /// <summary>
        /// enumeration-constant
        ///   : identifier
        /// </summary>
        public static NamedParser<String>
            EnumerationConstant { get; } = new NamedParser<string>("enumeration-constant");

        /// <summary>
        /// struct-or-union-specifier
        ///   : struct-or-union [identifier]? { struct-declaration-list }
        ///   | struct-or-union identifier
        /// </summary>
        /// <remarks>
        /// Note: if no struct-declaration-list given, the Type is considered incomplete.
        /// </remarks>
        public static NamedParser<StructOrUnionSpec>
            StructOrUnionSpecifier { get; } = new NamedParser<StructOrUnionSpec>("struct-or-union-specifier");

        /// <summary>
        /// struct-or-union
        ///   : struct | union
        /// </summary>
        public static NamedParser<StructOrUnion>
            StructOrUnion { get; } = new NamedParser<StructOrUnion>("struct-or-union");

        /// <summary>
        /// struct-declaration-list
        ///   : [struct-declaration]+
        /// </summary>
        public static NamedParser<ImmutableList<StructDecln>>
            StructDeclarationList { get; } = new NamedParser<ImmutableList<StructDecln>>("struct-declaration-list");

        /// <summary>
        /// struct-declaration
        ///   : specifier-qualifier-list struct-declarator-list ';'
        /// </summary>
        /// <remarks>
        /// Note that a struct declaration does not need a storage class specifier.
        /// </remarks>
        public static NamedParser<StructDecln>
            StructDeclaration { get; } = new NamedParser<StructDecln>("struct-declaration");

        /// <summary>
        /// specifier-qualifier-list
        ///   : [ Type-specifier | Type-qualifier ]+
        /// </summary>
        public static NamedParser<SpecQualList>
            SpecifierQualifierList { get; } = new NamedParser<SpecQualList>("specifier-qualifier-list");

        /// <summary>
        /// struct-declarator-list
        ///   : struct-declarator [ ',' struct-declarator ]*
        /// </summary>
        public static NamedParser<ImmutableList<StructDeclr>>
            StructDeclaratorList { get; } = new NamedParser<ImmutableList<StructDeclr>>("struct-declarator-list");

        /// <summary>
        /// struct-declarator
        ///   : [declarator]? ':' constant-expression
        ///   | declarator
        /// </summary>
        /// <remarks>
        /// Note that the second one represents a 'bit-field', which I'm not going to support.
        /// </remarks>
        public static NamedParser<StructDeclr>
            StructDeclarator { get; } = new NamedParser<StructDeclr>("struct-declarator");

        /// <summary>
        /// parameter-declaration
        ///   : declaration-specifiers [ declarator | abstract-declarator ]?
        /// </summary>
        /// <remarks>
        /// int foo(int arg1, int arg2);
        ///         ~~~~~~~~
        /// 
        /// int foo(int, int);
        ///         ~~~
        /// 
        /// The declarator can be completely omitted.
        /// </remarks>
        public static NamedParser<ParamDecln>
            ParameterDeclaration { get; } = new NamedParser<ParamDecln>("parameter-declaration");

        // identifier_list
        //   : /* old style, i'm deleting this */

        /// <summary>
        /// abstract-declarator
        ///   : [pointer]? direct-abstract-declarator
        ///   | pointer
        /// </summary>
        /// <remarks>
        /// An abstract declarator is a non-empty list of (pointer, function, or array) Type modifiers
        /// </remarks>
        public static NamedParser<AbstractDeclr>
            AbstractDeclarator { get; } = new NamedParser<AbstractDeclr>("abstract-declarator");

        /// <summary>
        /// direct-abstract-declarator
        ///   : [
        ///         '(' abstract-declarator ')'
        ///       | '[' [constant-expression]? ']'  // array modifier
        ///       | '(' [parameter-type_list]? ')'  // function modifier
        ///     ] [
        ///         '[' [constant-expression]? ']'  // array modifier
        ///       | '(' [parameter-Type-list]? ')'  // function modifier
        ///     ]*
        /// </summary>
        public static NamedParser<AbstractDeclr>
            DirectAbstractDeclarator { get; } = new NamedParser<AbstractDeclr>("direct-abstract-declarator");

        /// <summary>
        /// initializer
        ///   : assignment-expression
        ///   | '{' initializer-list '}'
        ///   | '{' initializer-list ',' '}'
        /// </summary>
        public static NamedParser<Initr>
            Initializer { get; } = new NamedParser<Initr>("initializer");

        /// <summary>
        /// initializer-list
        ///   : initializer [ ',' initializer ]*
        /// 
        /// A non-empty list of initializers.
        /// </summary>
        public static NamedParser<Initr>
            InitializerList { get; } = new NamedParser<Initr>("initializer-list");

        /// <summary>
        /// Type-name
        ///   : specifier-qualifier-list [abstract-declarator]?
        /// </summary>
        public static NamedParser<TypeName>
            TypeName { get; } = new NamedParser<TypeName>("Type-name");

        /// <summary>
        /// typedef-name
        ///   : identifier
        /// </summary>
        /// <remarks>
        /// It must be something already defined.
        /// We need to look it up in the parser environment.
        /// </remarks>
        public static NamedParser<String>
            TypeDefName { get; } = new NamedParser<String>("typedef-name");

        public static void SetDeclarationRules() {

            // declaration
            //   : declaration-specifiers [Init-declarator-list]? ';'
            Declaration.Is(
                (DeclarationSpecifiers)
                .Then(InitDeclaratorList.Optional(ImmutableList<InitDeclr>.Empty))
                .Then(Semicolon)
                .Then(Decln.Create)
                .TransformResult(
                    _ => {
                        var result = _.Result;
                        var env = _.Environment;
                        env = result.InitDeclrs.Aggregate(env, (currentEnv, initDeclr) =>
                                      currentEnv.AddSymbol(
                                          initDeclr.Declr.Name,
                                          result.DeclnSpecs.StorageClsSpecs.DefaultIfEmpty(StorageClsSpec.AUTO).First()
                                      )
                        );
                        return ParserSucceeded.Create(result, env, _.Source);
                    }
                )
            );

            // declaration-specifiers
            //   : [ storage-class-specifier | Type-specifier | Type-qualifier ]+
            DeclarationSpecifiers.Is(
                Parser.Seed(DeclnSpecs.Empty)
                .Then(
                    Either(
                        Given<DeclnSpecs>()
                        .Then(StorageClassSpecifier)
                        .Then(DeclnSpecs.Add)
                    ).Or(
                        Given<DeclnSpecs>()
                        .Then(TypeSpecifier)
                        .Then(DeclnSpecs.Add)
                    ).Or(
                        Given<DeclnSpecs>()
                        .Then(TypeQualifier)
                        .Then(DeclnSpecs.Add)
                    ).OneOrMore()
                )
            );

            // Init-declarator-list
            //   : Init-declarator [ ',' Init-declarator ]*
            InitDeclaratorList.Is(
                InitDeclarator.OneOrMore(Comma)
            );

            // Init-declarator
            //   : declarator [ '=' initializer ]?
            InitDeclarator.Is(
                (Declarator)
                .Then(
                    (Assign).Then(Initializer).Optional()
                ).Then(InitDeclr.Create)
            );

            // storage-class-specifier
            //   : auto | register | static | extern | typedef
            StorageClassSpecifier.Is(
                Either(Auto)
                .Or(Register)
                .Or(Static)
                .Or(Extern)
                .Or(Typedef)
            );

            // Type-specifier
            //   : void
            //   | char
            //   | short
            //   | int
            //   | long
            //   | float
            //   | double
            //   | signed
            //   | unsigned
            //   | struct-or-union-specifier
            //   | enum-specifier
            //   | typedef-name
            TypeSpecifier.Is(
                Either(
                    Either(Void)
                    .Or(Char)
                    .Or(Short)
                    .Or(Int)
                    .Or(Long)
                    .Or(Float)
                    .Or(Double)
                    .Or(Signed)
                    .Or(Unsigned)
                    .Then(kind => new BasicTypeSpec(kind) as TypeSpec)
                )
                .Or(StructOrUnionSpecifier)
                .Or(EnumSpecifier)
                .Or(TypeDefName.Then(TypedefName.Create))
            );

            // type_qualifier
            //   : const
            //   | volatile
            TypeQualifier.Is(
                Either(Const).Or(Volatile)
            );

            // declarator
            //   : [pointer]? direct-declarator
            Declarator.Is(
                (Pointer.Optional())
                .Then(DirectDeclarator)
                .Then(Declr.Create)
            );

            // pointer
            //   : [ '*' [Type-qualifier-list]? ]+
            Pointer.Is(
                (
                    Mult.
                    Then(TypeQualifierList.Optional(ImmutableList<TypeQual>.Empty))
                    .Then(PointerModifier.Create)
                ).OneOrMore()
                .Then(pointerModifiers => pointerModifiers.Reverse())
            );

            // parameter-Type-list
            //   : parameter-list [ ',' '...' ]?
            ParameterTypeList.Is(
                ParameterList
                .Then(
                    (Comma)
                    .Then(Period).Then(Period).Then(Period)
                    .Optional()
                ).Then(ParamTypeList.Create)
            );

            // parameter-list
            //   : parameter-declaration [ ',' parameter-declaration ]*
            ParameterList.Is(
                ParameterDeclaration.OneOrMore(Comma)
            );

            // Type-qualifier-list
            //   : [Type-qualifier]+
            TypeQualifierList.Is(
                TypeQualifier.OneOrMore()
            );

            // direct-declarator
            //   : [
            //         identifier | '(' declarator ')'
            //     ] [
            //         '[' [constant-expression]? ']'
            //       | '(' [parameter-Type-list]? ')'
            //     ]*
            DirectDeclarator.Is(
                (
                    Either(
                        (Identifier).Then(Declr.Create)
                    ).Or(
                        (LeftParen).Then(Declarator).Then(RightParen)
                    )
                ).Then(
                    Either(
                        Given<Declr>()
                        .Then(LeftBracket)
                        .Then(
                            ConstantExpression.Optional().Then(ArrayModifier.Create)
                        ).Then(RightBracket)
                        .Then(Declr.Add)
                    ).Or(
                        Given<Declr>()
                        .Then(LeftParen)
                        .Then(
                            ParameterTypeList
                            .Optional()
                            .Then(FunctionModifier.Create)
                        ).Then(RightParen)
                        .Then(Declr.Add)
                    )
                    .ZeroOrMore()
                )
            );

            // enum-specifier
            //   : enum [identifier]? '{' enumerator-list '}'
            //   | enum identifier
            EnumSpecifier.Is(
                (Enum)
                .Then(
                    Either(
                        Identifier.Optional()
                        .Then(LeftCurlyBrace)
                        .Then(EnumeratorList)
                        .Then(RightCurlyBrace)
                        .Then(EnumSpec.Create)
                    ).Or(
                        (Identifier)
                        .Then(EnumSpec.Create)
                    )
                )
            );

            // enumerator-list
            //   : enumerator [ ',' enumerator ]*
            EnumeratorList.Is(
                Enumerator.OneOrMore(Comma)
            );

            // enumerator
            //   : enumeration-constant [ '=' constant-expression ]?
            Enumerator.Is(
                EnumerationConstant
                .Then(
                    (Assign)
                    .Then(ConstantExpression)
                    .Optional()
                ).Then(Enumr.Create)
            );

            // enumeration-constant
            //   : identifier
            EnumerationConstant.Is(
                Identifier
            );

            // struct-or-union-specifier
            //   : struct-or-union [identifier]? { struct-declaration-list }
            //   | struct-or-union identifier
            StructOrUnionSpecifier.Is(
                (StructOrUnion)
                .Then(
                    Either(
                        Given<StructOrUnion>()
                        .Then(Identifier.Optional())
                        .Then(LeftCurlyBrace)
                        .Then(StructDeclarationList)
                        .Then(RightCurlyBrace)
                        .Then(StructOrUnionSpec.Create)
                    ).Or(
                        Given<StructOrUnion>()
                        .Then(Identifier)
                        .Then(StructOrUnionSpec.Create)
                    )
                )
            );

            // struct-or-union
            //   : struct | union
            StructOrUnion.Is(
                Either(Struct).Or(Union)
            );

            // struct-declaration-list
            //   : [struct-declaration]+
            StructDeclarationList.Is(
                StructDeclaration.OneOrMore()
            );

            // struct-declaration
            //   : specifier-qualifier-list struct-declarator-list ';'
            StructDeclaration.Is(
                (SpecifierQualifierList)
                .Then(StructDeclaratorList)
                .Then(Semicolon)
                .Then(StructDecln.Create)
            );

            // specifier-qualifier-list
            //   : [ Type-specifier | Type-qualifier ]+
            SpecifierQualifierList.Is(
                Parser.Seed(SpecQualList.Empty)
                .Then(
                    Either(
                        Given<SpecQualList>()
                        .Then(TypeSpecifier)
                        .Then(SpecQualList.Add)
                    ).Or(
                        Given<SpecQualList>()
                        .Then(TypeQualifier)
                        .Then(SpecQualList.Add)
                    )
                    .OneOrMore()
                )
            );

            // struct-declarator-list
            //   : struct-declarator [ ',' struct-declarator ]*
            StructDeclaratorList.Is(
                StructDeclarator.OneOrMore(Comma)
            );

            // struct-declarator
            //   : [declarator]? ':' constant-expression
            //   | declarator
            StructDeclarator.Is(
                Either(
                    (Declarator.Optional())
                    .Then(Colon)
                    .Then(ConstantExpression)
                    .Then(StructDeclr.Create)
                ).Or(
                    (Declarator)
                    .Then(StructDeclr.Create)
                )
            );

            // parameter-declaration
            //   : declaration-specifiers [ declarator | abstract-declarator ]?
            ParameterDeclaration.Is(
                (DeclarationSpecifiers)
                .Then(
                    Either(
                        (Declarator).Then(ParamDeclr.Create)
                    ).Or(
                        (AbstractDeclarator).Then(ParamDeclr.Create)
                    ).Optional()
                ).Then(ParamDecln.Create)
            );

            // abstract-declarator
            //   : [pointer]? direct-abstract-declarator
            //   | pointer
            AbstractDeclarator.Is(
                Either(
                    (Pointer.Optional(ImmutableList<PointerModifier>.Empty))
                    .Then(DirectAbstractDeclarator)
                    .Then(AbstractDeclr.Add)
                ).Or(
                    (Pointer)
                    .Then(AbstractDeclr.Create)
                )
            );

            // direct-abstract-declarator
            //   : [
            //         '(' abstract-declarator ')'
            //       | '[' [constant-expression]? ']'  // array modifier
            //       | '(' [parameter-type_list]? ')'  // function modifier
            //     ] [
            //         '[' [constant-expression]? ']'  // array modifier
            //       | '(' [parameter-Type-list]? ')'  // function modifier
            //     ]*
            DirectAbstractDeclarator.Is(
                (
                    Either(
                        (LeftParen)
                        .Then(AbstractDeclarator)
                        .Then(RightParen)
                    ).Or(
                        (LeftBracket)
                        .Then(ConstantExpression.Optional())
                        .Then(RightBracket)
                        .Then(ArrayModifier.Create)
                        .Then<ArrayModifier, ImmutableList<ArrayModifier>>(ImmutableList.Create)
                        .Then(AbstractDeclr.Create)
                    ).Or(
                        (LeftParen)
                        .Then(ParameterTypeList.Optional())
                        .Then(RightParen)
                        .Then(FunctionModifier.Create)
                        .Then<FunctionModifier, ImmutableList<FunctionModifier>>(ImmutableList.Create)
                        .Then(AbstractDeclr.Create)
                    )
                ).Then(
                    Either(
                        Given<AbstractDeclr>()
                        .Then(
                            LeftBracket
                            .Then(ConstantExpression.Optional())
                            .Then(RightBracket)
                            .Then(ArrayModifier.Create)
                        ).Then(
                            AbstractDeclr.Add
                        )
                    ).Or(
                        Given<AbstractDeclr>()
                        .Then(
                            (LeftParen)
                            .Then(ParameterTypeList.Optional())
                            .Then(RightParen)
                            .Then(FunctionModifier.Create)
                        ).Then(
                            AbstractDeclr.Add
                        )
                    )
                    .ZeroOrMore()
                )
            );

            // initializer
            //   : assignment-expression
            //   | '{' initializer-list '}'
            //   | '{' initializer-list ',' '}'
            Initializer.Is(
                Either<Initr>(
                    (AssignmentExpression)
                    .Then(InitExpr.Create)
                ).Or(
                    (LeftCurlyBrace)
                    .Then(InitializerList)
                    .Then(RightCurlyBrace)
                ).Or(
                    (LeftCurlyBrace)
                    .Then(InitializerList)
                    .Then(Comma)
                    .Then(RightCurlyBrace)
                )
            );

            // initializer-list
            //   : initializer [ ',' initializer ]*
            InitializerList.Is(
                Initializer.OneOrMore(Comma)
                .Then(InitList.Create)
            );

            // Type-name
            //   : specifier-qualifier-list [abstract-declarator]?
            TypeName.Is(
                (SpecifierQualifierList)
                .Then(AbstractDeclarator.Optional())
                .Then(AST.TypeName.Create)
            );

            // typedef-name
            //   : identifier
            TypeDefName.Is(
                (Identifier)
                .Check(
                    result => result.Environment.IsTypedefName(result.Result)
                )
            );
        }
    }
}


<|start_filename|>AST/ConstExpressions.cs<|end_filename|>
﻿using System;
using LexicalAnalysis;

namespace AST {

    public abstract class Literal : Expr { }

	/// <summary>
	/// May be a float or double
	/// </summary>
	public class FloatLiteral : Literal {
		public FloatLiteral(Double value, TokenFloat.FloatSuffix floatSuffix) {
			this.Value = value;
			this.FloatSuffix = floatSuffix;
		}

		public TokenFloat.FloatSuffix FloatSuffix { get; }

		public Double Value { get; }

        public override ABT.Expr GetExpr(ABT.Env env) {
            switch (this.FloatSuffix) {
                case TokenFloat.FloatSuffix.F:
                    return new ABT.ConstFloat((Single)this.Value, env);

                case TokenFloat.FloatSuffix.NONE:
                case TokenFloat.FloatSuffix.L:
                    return new ABT.ConstDouble(this.Value, env);

                default:
                    throw new InvalidOperationException();
            }
        }
	}

	/// <summary>
	/// May be signed or unsigned
    /// C doesn't have char constant, only int constant
	/// </summary>
	public class IntLiteral : Literal {
		public IntLiteral(Int64 value, TokenInt.IntSuffix suffix) {
			this.Value = value;
			this.Suffix = suffix;
		}

		public TokenInt.IntSuffix Suffix { get; }
		public Int64 Value { get; }

        public override ABT.Expr GetExpr(ABT.Env env) {
            switch (this.Suffix) {
                case TokenInt.IntSuffix.U:
                case TokenInt.IntSuffix.UL:
                    return new ABT.ConstULong((UInt32)this.Value, env);

                case TokenInt.IntSuffix.NONE:
                case TokenInt.IntSuffix.L:
                    return new ABT.ConstLong((Int32)this.Value, env);

                default:
                    throw new InvalidOperationException();
            }
        }
    }

	/// <summary>
	/// String Literal
	/// </summary>
	public class StringLiteral : Expr {
		public StringLiteral(String value) {
			this.Value = value;
		}

		public String Value { get; }

		public override ABT.Expr GetExpr(ABT.Env env) {
			return new ABT.ConstStringLiteral(this.Value, env);
		}
	}

}

<|start_filename|>ABT/UnaryOperators.cs<|end_filename|>
﻿using System;
using System.Diagnostics;
using CodeGeneration;

namespace ABT {

    // IncDecExpr
    //   |
    //   +-- PostIncrement, PostDecrement, PreIncrement, PreDecrement

    public abstract partial class IncDecExpr : Expr {
        protected IncDecExpr(Expr expr) {
            if (!(expr.Type is ScalarType)) {
                throw new InvalidOperationException("Only supports scalars.");
            }
            this.Expr = expr;
        }

        public Expr Expr { get; }

        public override Env Env => this.Expr.Env;

        public override Boolean IsLValue => false;

        public override ExprType Type => this.Expr.Type;
    }

    /// <summary>
    /// Expr++: must be integral, float or pointer.
    /// 
    /// If Expr is an array, it is converted to a pointer in semantic analysis.
    /// </summary>
    public sealed partial class PostIncrement : IncDecExpr {
        public PostIncrement(Expr expr)
            : base(expr) { }
    }

    /// <summary>
    /// Expr--: must be a scalar
    /// </summary>
    public sealed partial class PostDecrement : IncDecExpr {
        public PostDecrement(Expr expr)
            : base(expr) { }
    }

    /// <summary>
    /// ++Expr: must be a scalar
    /// </summary>
    public sealed partial class PreIncrement : IncDecExpr {
        public PreIncrement(Expr expr)
            : base(expr) { }
    }

    /// <summary>
    /// --Expr: must be a scalar
    /// </summary>
    public sealed partial class PreDecrement : IncDecExpr {
        public PreDecrement(Expr expr)
            : base(expr) { }
    }

    public abstract partial class UnaryArithOp : Expr {
        protected UnaryArithOp(Expr expr) {
            this.Expr = expr;
        }

        public Expr Expr { get; }

        public override Env Env => this.Expr.Env;

        public override Boolean IsLValue => false;

        public override abstract ExprType Type { get; }
    }

    /// <summary>
    /// -Expr: only takes arithmetic Type.
    /// 
    /// After semantic analysis, only the following 4 types are possible:
    /// 1) long
    /// 2) ulong
    /// 3) float
    /// 4) double
    /// </summary>
    public sealed partial class Negative : UnaryArithOp {
        public Negative(Expr expr)
            : base(expr) { }

        public override ExprType Type => this.Expr.Type;
    }

    /// <summary>
    /// ~Expr: only takes integral Type.
    /// 
    /// After semantic analysis, only the following 2 types are possible:
    /// 1) long
    /// 2) ulong
    /// </summary>
    public sealed partial class BitwiseNot : UnaryArithOp {
        public BitwiseNot(Expr expr)
            : base(expr) {
            if (!(expr.Type is LongType || expr.Type is ULongType)) {
                throw new InvalidOperationException("Invalid operand type.");
            }
        }

        public override ExprType Type => this.Expr.Type;
    }

    /// <summary>
    /// !Expr: only takes scalar Type.
    /// 
    /// After semantic analysis, only the following 4 types are possible:
    /// 1) long
    /// 2) ulong
    /// 3) float
    /// 4) double
    /// 
    /// Pointers are converted to ulongs.
    /// </summary>
    public sealed partial class LogicalNot : UnaryArithOp {
        public LogicalNot(Expr expr)
            : base(expr) {
            if (!(expr.Type is LongType || expr.Type is ULongType
                  || expr.Type is FloatType || expr.Type is DoubleType)) {
                throw new InvalidOperationException("Invalid operand type.");
            }
        }

        private static ExprType _type = new LongType(true);
        public override ExprType Type => _type;
    }
}


<|start_filename|>ABT/StmtVisitor.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;

namespace ABT {
    public abstract class StmtVisitor {
        public virtual void Visit(Stmt stmt) {}
        public virtual void Visit(GotoStmt stmt) {}
        public virtual void Visit(LabeledStmt stmt) {}
        public virtual void Visit(ContStmt stmt) {}
        public virtual void Visit(BreakStmt stmt) {}
        public virtual void Visit(ExprStmt stmt) {}
        public virtual void Visit(CompoundStmt stmt) {}
        public virtual void Visit(ReturnStmt stmt) {}
        public virtual void Visit(WhileStmt stmt) {}
        public virtual void Visit(DoWhileStmt stmt) {}
        public virtual void Visit(ForStmt stmt) {}
        public virtual void Visit(SwitchStmt stmt) {}
        public virtual void Visit(CaseStmt stmt) {}
        public virtual void Visit(DefaultStmt stmt) {}
        public virtual void Visit(IfStmt stmt) {}
        public virtual void Visit(IfElseStmt stmt) {}
    }

    public class CaseLabelsGrabber : StmtVisitor {
        private readonly List<Int32> _labels = new List<Int32>();
        public IReadOnlyList<Int32> Labels => this._labels;

        public static IReadOnlyList<Int32> GrabLabels(SwitchStmt stmt) {
            CaseLabelsGrabber grabber = new CaseLabelsGrabber();
            stmt.Stmt.Accept(grabber);
            return grabber.Labels;
        }

        public override void Visit(Stmt stmt) {
            throw new InvalidOperationException("Cannot visit abstract Stmt");
        }

        public override void Visit(GotoStmt stmt) {}

        public override void Visit(LabeledStmt stmt) =>
            stmt.Stmt.Accept(this);

        public override void Visit(ContStmt stmt) {}

        public override void Visit(BreakStmt stmt) {}

        public override void Visit(ExprStmt stmt) {}

        public override void Visit(CompoundStmt stmt) =>
            stmt.Stmts.ForEach(_ => _.Item2.Accept(this));

        public override void Visit(ReturnStmt stmt) {}

        public override void Visit(WhileStmt stmt) =>
            stmt.Body.Accept(this);

        public override void Visit(DoWhileStmt stmt) =>
            stmt.Body.Accept(this);

        public override void Visit(ForStmt stmt) =>
            stmt.Body.Accept(this);

        public override void Visit(SwitchStmt stmt) {
            // Must ignore this.
        }

        public override void Visit(CaseStmt stmt) {
            // Record the Value.
            this._labels.Add(stmt.Value);
            stmt.Stmt.Accept(this);
        }

        public override void Visit(DefaultStmt stmt) =>
            stmt.Stmt.Accept(this);

        public override void Visit(IfStmt stmt) =>
            stmt.Stmt.Accept(this);

        public override void Visit(IfElseStmt stmt) {
            stmt.TrueStmt.Accept(this);
            stmt.FalseStmt.Accept(this);
        }
    }

    public class GotoLabelsGrabber : StmtVisitor {
        private readonly List<String> _labels = new List<String>();
        public IReadOnlyList<String> Labels => this._labels;

        public static IReadOnlyList<String> GrabLabels(Stmt stmt) {
            GotoLabelsGrabber grabber = new GotoLabelsGrabber();
            stmt.Accept(grabber);
            return grabber.Labels;
        }

        public override void Visit(Stmt stmt) {
            throw new InvalidOperationException("Cannot visit abstract Stmt");
        }

        public override void Visit(GotoStmt stmt) { }

        public override void Visit(LabeledStmt stmt) {
            this._labels.Add(stmt.Label);
            stmt.Stmt.Accept(this);
        }

        public override void Visit(ContStmt stmt) { }

        public override void Visit(BreakStmt stmt) { }

        public override void Visit(ExprStmt stmt) { }

        public override void Visit(CompoundStmt stmt) =>
            stmt.Stmts.ForEach(_ => _.Item2.Accept(this));

        public override void Visit(ReturnStmt stmt) { }

        public override void Visit(WhileStmt stmt) =>
            stmt.Body.Accept(this);

        public override void Visit(DoWhileStmt stmt) =>
            stmt.Body.Accept(this);

        public override void Visit(ForStmt stmt) =>
            stmt.Body.Accept(this);

        public override void Visit(SwitchStmt stmt) {
            stmt.Stmt.Accept(this);
        }

        public override void Visit(CaseStmt stmt) {
            stmt.Stmt.Accept(this);
        }

        public override void Visit(DefaultStmt stmt) =>
            stmt.Stmt.Accept(this);

        public override void Visit(IfStmt stmt) =>
            stmt.Stmt.Accept(this);

        public override void Visit(IfElseStmt stmt) {
            stmt.TrueStmt.Accept(this);
            stmt.FalseStmt.Accept(this);
        }
    }
}



<|start_filename|>ParserTests/TestExpressions.cs<|end_filename|>
﻿using NUnit.Framework;
using Parsing;
using static CompilerTests.ParserTestUtils;

namespace CompilerTests {
    [TestFixture]
    public class TestExpressions {

        [Test]
        public void Expression() {
            TestParserRule(
                CParsers.Expression,
                "a = b, c = d, e = f"
                );
        }

        [Test]
        public void PrimaryExpression() {
            TestParserRule("a", CParsers.PrimaryExpression);
            TestParserRule("3", CParsers.PrimaryExpression);
            TestParserRule("3.0f", CParsers.PrimaryExpression);
            TestParserRule("'a'", CParsers.PrimaryExpression);
            TestParserRule("\"Hello, world!\"", CParsers.PrimaryExpression);
            TestParserRule("(a)", CParsers.PrimaryExpression);
        }

        [Test]
        public void Variable() {
            TestParserRule("a", CParsers.Variable);
        }

        [Test]
        public void Constant() {
            TestParserRule(
                CParsers.Constant,
                "'a'",
                "3",
                "3.0f"
                );
        }
    
        [Test]
        public void ConstChar() {
            TestParserRule("'a'", CParsers.ConstChar);
        }

        [Test]
        public void ConstInt() {
            TestParserRule("3", CParsers.ConstInt);
        }

        [Test]
        public void ConstFloat() {
            TestParserRule("3.0f", CParsers.ConstFloat);
        }

        [Test]
        public void StringLiteral() {
            TestParserRule("\"Haha\"", CParsers.StringLiteral);
        }

        [Test]
        public void ConstantExpression() {
            TestParserRule(
                CParsers.ConstantExpression,
                "a || b ? a = b : a ? b : c"
                );
        }

        [Test]
        public void ConditionalExpression() {
            TestParserRule(
                CParsers.ConditionalExpression,
                "a || b",
                "a || b ? a = b : a ? b : c"
                );
        }

        [Test]
        public void AssignmentExpression() {
            TestParserRule(
                CParsers.AssignmentExpression,
                "a ? b : c",
                "a = b",
                "a"
                );
        }

        [Test]
        public void PostfixExpression() {
            TestParserRule("a++", CParsers.PostfixExpression);
            TestParserRule("a--", CParsers.PostfixExpression);
            TestParserRule("a->b", CParsers.PostfixExpression);
            TestParserRule("a.b", CParsers.PostfixExpression);
            TestParserRule("a(1)", CParsers.PostfixExpression);
            TestParserRule("a()", CParsers.PostfixExpression);
            TestParserRule("a(1, 2)", CParsers.PostfixExpression);
            TestParserRule("a[b]", CParsers.PostfixExpression);
            TestParserRule("a[b]->b++", CParsers.PostfixExpression);
            TestParserRule("qsort(arr, sizeof(arr) / sizeof(int), sizeof(int), &awesome_cmp)", CParsers.PostfixExpression);
        }

        [Test]
        public void ArgumentExpressionList() {
            TestParserRule(
                CParsers.ArgumentExpressionList,
                "a = 3, 3",
                "3"
                );
        }

        [Test]
        public void UnaryExpression() {
            TestParserRule(
                CParsers.UnaryExpression,
                "++a",
                "--a",
                "&a",
                "*a",
                "+a",
                "-a",
                "~a",
                "!a",
                "++ -- & * + - ~ ! a",
                "sizeof 3",
                "sizeof(3)",
                "sizeof(int)"
                );
        }

        [Test]
        public void CastExpression() {
            TestParserRule(
                CParsers.CastExpression,
                "a",
                "(int)a",
                "(int)(char)a"
                );
        }

        [Test]
        public void MultiplicativeExpression() {
            TestParserRule(
                CParsers.MultiplicativeExpression,
                "a * b",
                "a / b",
                "a % c",
                "a * b / c % d"
                );
        }

        [Test]
        public void AdditiveExpression() {
            TestParserRule(
                CParsers.AdditiveExpression,
                "a * b / c % d + e",
                "a - b",
                "a + b - c"
                );
        }

        [Test]
        public void ShiftExpression() {
            TestParserRule(
                CParsers.ShiftExpression,
                "a << b",
                "a >> b"
                );
        }

        [Test]
        public void RelationalExpression() {
            TestParserRule(
                CParsers.RelationalExpression,
                "a < b",
                "a > b",
                "a <= b",
                "a >= b"
                );
        }

        [Test]
        public void EqualityExpression() {
            TestParserRule(
                CParsers.EqualityExpression,
                "a == b",
                "a != b"
                );
        }

        [Test]
        public void AndExpression() {
            TestParserRule("a & b", CParsers.AndExpression);
        }

        [Test]
        public void ExclusiveOrExpression() {
            TestParserRule("a ^ b", CParsers.ExclusiveOrExpression);
        }

        [Test]
        public void InclusiveOrExpression() {
            TestParserRule("a | b", CParsers.InclusiveOrExpression);
        }

        [Test]
        public void LogicalAndExpression() {
            TestParserRule("a && b", CParsers.LogicalAndExpression);
        }

        [Test]
        public void LogicalOrExpression() {
            TestParserRule("a || b", CParsers.LogicalOrExpression);
        }
    }
}

<|start_filename|>TestPrograms/SwitchStmt.c<|end_filename|>
int printf(char *, ...);

int main() {
    int a = 3;
    switch (a) {
        case 1:
            printf("1\n");
            break;
        case 20:
            printf("2\n");
            break;
        case 300:
            printf("3\n");
            break;
        default:
            printf("default\n");
            break;
    }
    return 0;
}


<|start_filename|>Scanner/Keyword.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;

namespace LexicalAnalysis {
    public enum KeywordVal {
        AUTO,
        DOUBLE,
        INT,
        STRUCT,
        BREAK,
        ELSE,
        LONG,
        SWITCH,
        CASE,
        ENUM,
        REGISTER,
        TYPEDEF,
        CHAR,
        EXTERN,
        RETURN,
        UNION,
        CONST,
        FLOAT,
        SHORT,
        UNSIGNED,
        CONTINUE,
        FOR,
        SIGNED,
        VOID,
        DEFAULT,
        GOTO,
        SIZEOF,
        VOLATILE,
        DO,
        IF,
        STATIC,
        WHILE
    }

    public class TokenKeyword : Token {
        public TokenKeyword(KeywordVal val) {
            this.Val = val;
        }

        public override TokenKind Kind { get; } = TokenKind.KEYWORD;
        public KeywordVal Val { get; }
        public static Dictionary<String, KeywordVal> Keywords { get; } = new Dictionary<String, KeywordVal>(StringComparer.InvariantCultureIgnoreCase) {
            { "AUTO",        KeywordVal.AUTO      },
            { "DOUBLE",      KeywordVal.DOUBLE    },
            { "INT",         KeywordVal.INT       },
            { "STRUCT",      KeywordVal.STRUCT    },
            { "BREAK",       KeywordVal.BREAK     },
            { "ELSE",        KeywordVal.ELSE      },
            { "LONG",        KeywordVal.LONG      },
            { "SWITCH",      KeywordVal.SWITCH    },
            { "CASE",        KeywordVal.CASE      },
            { "ENUM",        KeywordVal.ENUM      },
            { "REGISTER",    KeywordVal.REGISTER  },
            { "TYPEDEF",     KeywordVal.TYPEDEF   },
            { "CHAR",        KeywordVal.CHAR      },
            { "EXTERN",      KeywordVal.EXTERN    },
            { "RETURN",      KeywordVal.RETURN    },
            { "UNION",       KeywordVal.UNION     },
            { "CONST",       KeywordVal.CONST     },
            { "FLOAT",       KeywordVal.FLOAT     },
            { "SHORT",       KeywordVal.SHORT     },
            { "UNSIGNED",    KeywordVal.UNSIGNED  },
            { "CONTINUE",    KeywordVal.CONTINUE  },
            { "FOR",         KeywordVal.FOR       },
            { "SIGNED",      KeywordVal.SIGNED    },
            { "VOID",        KeywordVal.VOID      },
            { "DEFAULT",     KeywordVal.DEFAULT   },
            { "GOTO",        KeywordVal.GOTO      },
            { "SIZEOF",      KeywordVal.SIZEOF    },
            { "VOLATILE",    KeywordVal.VOLATILE  },
            { "DO",          KeywordVal.DO        },
            { "IF",          KeywordVal.IF        },
            { "STATIC",      KeywordVal.STATIC    },
            { "WHILE",       KeywordVal.WHILE     }
        };

        public override String ToString() {
            return this.Kind + ": " + this.Val;
        }

    }
}

<|start_filename|>ABT/ConstExpressions.cs<|end_filename|>
﻿using System;
using CodeGeneration;

namespace ABT {

    /// <summary>
    /// Compile-time constant. Cannot get the address.
    /// </summary>
    public abstract partial class ConstExpr : Expr {
        protected ConstExpr(Env env) {
            this.Env = env;
        }

        public override sealed Env Env { get; }

        public override sealed Boolean IsConstExpr => true;

        public override sealed Boolean IsLValue => false;
    }

    public sealed partial class ConstLong : ConstExpr {
        public ConstLong(Int32 value, Env env)
            : base(env) {
            this.Value = value;
        }
        public Int32 Value { get; }

        public override String ToString() => $"{this.Value}";

        private static ExprType _type = new LongType(true);
        public override ExprType Type => _type;
    }

    public sealed partial class ConstULong : ConstExpr {
        public ConstULong(UInt32 value, Env env)
            : base(env) {
            this.Value = value;
        }
        public UInt32 Value { get; }

        public override String ToString() => $"{this.Value}u";

        private static ExprType _type = new ULongType(true);
        public override ExprType Type => _type;
    }

    public sealed partial class ConstShort : ConstExpr {
        public ConstShort(Int16 value, Env env)
            : base(env) {
            this.Value = value;
        }

        public Int16 Value { get; }

        private static ExprType _type = new ShortType(true);
        public override ExprType Type => _type;
    }

    public sealed partial class ConstUShort : ConstExpr {
        public ConstUShort(UInt16 value, Env env)
            : base(env) {
            this.Value = value;
        }

        public UInt16 Value { get; }

        private static ExprType _type = new UShortType(true);
        public override ExprType Type => _type;
    }

    public sealed partial class ConstChar : ConstExpr {
        public ConstChar(SByte value, Env env)
            : base(env) {
            this.Value = value;
        }

        public SByte Value { get; }

        private static ExprType _type = new CharType(true);
        public override ExprType Type => _type;
    }

    public sealed partial class ConstUChar : ConstExpr {
        public ConstUChar(Byte value, Env env)
            : base(env) {
            this.Value = value;
        }

        public Byte Value { get; }

        private static ExprType _type = new UCharType(true);
        public override ExprType Type => _type;
    }

    public sealed partial class ConstPtr : ConstExpr {
        public ConstPtr(UInt32 value, ExprType type, Env env)
            : base(env) {
            this.Value = value;
            this.Type = type;
        }

        public UInt32 Value { get; }

        public override ExprType Type { get; }

        public override String ToString() =>
            $"({this.Type} *)0x{this.Value.ToString("X8")}";
    }

    public sealed partial class ConstFloat : ConstExpr {
        public ConstFloat(Single value, Env env)
            : base(env) {
            this.Value = value;
        }

        public Single Value { get; }

        public override String ToString() => $"{this.Value}f";

        private static ExprType _type = new FloatType(true);
        public override ExprType Type => _type;
    }

    public sealed partial class ConstDouble : ConstExpr {
        public ConstDouble(Double value, Env env)
            : base(env) {
            this.Value = value;
        }

        public Double Value { get; }

        public override String ToString() => $"{this.Value}";

        private static ExprType _type = new DoubleType(true);
        public override ExprType Type => _type;
    }

    public sealed partial class ConstStringLiteral : ConstExpr {
        public ConstStringLiteral(String value, Env env)
            : base(env) {
            this.Value = value;
        }

        public String Value { get; }

        public override String ToString() => $"\"{this.Value}\"";

        private static ExprType _type = new PointerType(new CharType(true), true);
        public override ExprType Type => _type;
    }

}


<|start_filename|>Scanner/Float.cs<|end_filename|>
﻿using System;

namespace LexicalAnalysis {
    /// <summary>
    /// The token representing a floating number.
    /// It can either be a float or double.
    /// </summary>
    public sealed class TokenFloat : Token {

        public enum FloatSuffix {
            NONE,
            F,
            L
        }

        public TokenFloat(Double value, FloatSuffix suffix, String source) {
            this.Value = value;
            this.Suffix = suffix;
            this.Source = source;
        }

        public override TokenKind Kind { get; } = TokenKind.FLOAT;
        public Double Value { get; }
        public String Source { get; }
        public FloatSuffix Suffix { get; }

        public override String ToString() {
            String str = this.Kind.ToString();
            switch (this.Suffix) {
                case FloatSuffix.F:
                    str += "(float)";
                    break;
                case FloatSuffix.L:
                    str += "(long double)";
                    break;
                default:
                    str += "(double)";
                    break;
            }
            return str + ": " + this.Value + " \"" + this.Source + "\"";
        }
    }

    /// <summary>
    /// The FSA for scanning a float.
    /// </summary>
    public sealed class FSAFloat : FSA {
        private enum State {
            START,
            END,
            ERROR,
            D,
            P,
            DP,
            PD,
            DE,
            DES,
            DED,
            PDF,
            DPL
        };

        private String _raw;
        private Int64 _intPart;
        private Int64 _fracPart;
        private Int64 _fracCount;
        private Int64 _expPart;
        private Boolean _expPos;
        private TokenFloat.FloatSuffix _suffix;
        private State _state;

        public FSAFloat() {
            this._state = State.START;
            this._intPart = 0;
            this._fracPart = 0;
            this._fracCount = 0;
            this._expPart = 0;
            this._suffix = TokenFloat.FloatSuffix.NONE;
            this._expPos = true;
            this._raw = "";
        }

        public override void Reset() {
            this._state = State.START;
            this._intPart = 0;
            this._fracPart = 0;
            this._fracCount = 0;
            this._expPart = 0;
            this._suffix = TokenFloat.FloatSuffix.NONE;
            this._expPos = true;
            this._raw = "";
        }

        public override FSAStatus GetStatus() {
            switch (this._state) {
                case State.START:
                    return FSAStatus.NONE;
                case State.END:
                    return FSAStatus.END;
                case State.ERROR:
                    return FSAStatus.ERROR;
                default:
                    return FSAStatus.RUNNING;
            }
        }

        public override Token RetrieveToken() {
            Double val;
            if (this._expPos) {
                val = (this._intPart + this._fracPart * Math.Pow(0.1, this._fracCount)) * Math.Pow(10, this._expPart);
            } else {
                val = (this._intPart + this._fracPart * Math.Pow(0.1, this._fracCount)) * Math.Pow(10, -this._expPart);
            }
            return new TokenFloat(val, this._suffix, this._raw.Substring(0, this._raw.Length - 1));
        }

        public override void ReadChar(Char ch) {
            this._raw += ch;
            switch (this._state) {
                case State.ERROR:
                case State.END:
                    this._state = State.ERROR;
                    break;

                case State.START:
                    if (Char.IsDigit(ch)) {
                        this._intPart = ch - '0';
                        this._state = State.D;
                    } else if (ch == '.') {
                        this._state = State.P;
                    } else {
                        this._state = State.ERROR;
                    }
                    break;

                case State.D:
                    if (Char.IsDigit(ch)) {
                        this._intPart *= 10;
                        this._intPart += ch - '0';
                        this._state = State.D;
                    } else if (ch == 'e' || ch == 'E') {
                        this._state = State.DE;
                    } else if (ch == '.') {
                        this._state = State.DP;
                    } else {
                        this._state = State.ERROR;
                    }
                    break;

                case State.P:
                    if (Char.IsDigit(ch)) {
                        this._fracPart = ch - '0';
                        this._fracCount = 1;
                        this._state = State.PD;
                    } else {
                        this._state = State.ERROR;
                    }
                    break;

                case State.DP:
                    if (Char.IsDigit(ch)) {
                        this._fracPart = ch - '0';
                        this._fracCount = 1;
                        this._state = State.PD;
                    } else if (ch == 'e' || ch == 'E') {
                        this._state = State.DE;
                    } else if (ch == 'f' || ch == 'F') {
                        this._suffix = TokenFloat.FloatSuffix.F;
                        this._state = State.PDF;
                    } else if (ch == 'l' || ch == 'L') {
                        this._suffix = TokenFloat.FloatSuffix.L;
                        this._state = State.DPL;
                    } else {
                        this._state = State.END;
                    }
                    break;

                case State.PD:
                    if (Char.IsDigit(ch)) {
                        this._fracPart *= 10;
                        this._fracPart += ch - '0';
                        this._fracCount++;
                        this._state = State.PD;
                    } else if (ch == 'e' || ch == 'E') {
                        this._state = State.DE;
                    } else if (ch == 'f' || ch == 'F') {
                        this._suffix = TokenFloat.FloatSuffix.F;
                        this._state = State.PDF;
                    } else if (ch == 'l' || ch == 'L') {
                        this._suffix = TokenFloat.FloatSuffix.L;
                        this._state = State.DPL;
                    } else {
                        this._state = State.END;
                    }
                    break;

                case State.DE:
                    if (Char.IsDigit(ch)) {
                        this._expPart = ch - '0';
                        this._state = State.DED;
                    } else if (ch == '+' || ch == '-') {
                        if (ch == '-') {
                            this._expPos = false;
                        }
                        this._state = State.DES;
                    } else {
                        this._state = State.ERROR;
                    }
                    break;

                case State.DES:
                    if (Char.IsDigit(ch)) {
                        this._expPart = ch - '0';
                        this._state = State.DED;
                    } else {
                        this._state = State.ERROR;
                    }
                    break;

                case State.DPL:
                    this._suffix = TokenFloat.FloatSuffix.L;
                    this._state = State.END;
                    break;

                case State.DED:
                    if (Char.IsDigit(ch)) {
                        this._expPart *= 10;
                        this._expPart += ch - '0';
                        this._state = State.DED;
                    } else if (ch == 'f' || ch == 'F') {
                        this._suffix = TokenFloat.FloatSuffix.F;
                        this._state = State.PDF;
                    } else if (ch == 'l' || ch == 'L') {
                        this._suffix = TokenFloat.FloatSuffix.L;
                        this._state = State.DPL;
                    } else {
                        this._state = State.END;
                    }
                    break;

                case State.PDF:
                    this._state = State.END;
                    break;

                default:
                    this._state = State.ERROR;
                    break;
            }

        }

        public override void ReadEOF() {
            switch (this._state) {
                case State.DP:
                case State.PD:
                case State.DED:
                case State.PDF:
                case State.DPL:
                    this._state = State.END;
                    break;
                default:
                    this._state = State.ERROR;
                    break;
            }
        }

    }
}

<|start_filename|>AST/Declarators.cs<|end_filename|>
﻿using System;
using System.Collections.Immutable;
using System.Linq;

namespace AST {
    using static SemanticAnalysis;

    /// <summary>
    /// Modify a Type into a function, array, or pointer
    /// </summary>
    public abstract class TypeModifier : ISyntaxTreeNode {
        [SemantMethod]
        public abstract ISemantReturn<ABT.ExprType> DecorateType(ABT.Env env, ABT.ExprType baseType);
    }

    public sealed class FunctionModifier : TypeModifier {
        private FunctionModifier(Option<ParamTypeList> paramTypeList) {
            this.ParamTypeList = paramTypeList;
        }

        public static FunctionModifier Create(Option<ParamTypeList> paramTypeList) =>
            new FunctionModifier(paramTypeList);

        public Option<ParamTypeList> ParamTypeList { get; }
        
        [SemantMethod]
        public override ISemantReturn<ABT.ExprType> DecorateType(ABT.Env env, ABT.ExprType returnType) {
            if (this.ParamTypeList.IsNone) {
                return SemantReturn.Create(env, ABT.FunctionType.Create(returnType));
            }

            var paramTypeList = this.ParamTypeList.Value;

            var namesAndTypes = Semant(paramTypeList.GetNamesAndTypes, ref env);
            var hasVarArgs = paramTypeList.HasVarArgs;

            return SemantReturn.Create(env, ABT.FunctionType.Create(returnType, namesAndTypes, hasVarArgs));
        }

    }

    /// <summary>
    /// parameter-Type-list
    ///   : parameter-list [ ',' '...' ]?
    /// 
    /// parameter-list
    ///   : parameter-declaration [ ',' parameter-declaration ]*
    /// </summary>
    public sealed class ParamTypeList : ISyntaxTreeNode {
        private ParamTypeList(ImmutableList<ParamDecln> paramDeclns, Boolean hasVarArgs) {
            this.ParamDeclns = paramDeclns;
            this.HasVarArgs = hasVarArgs;
        }

        public static ParamTypeList Create(ImmutableList<ParamDecln> paramDeclns, Boolean hasVarArgs) =>
            new ParamTypeList(paramDeclns, hasVarArgs);

        public static ParamTypeList Create() =>
            Create(ImmutableList<ParamDecln>.Empty, true);

        public ImmutableList<ParamDecln> ParamDeclns { get; }
        public Boolean HasVarArgs { get; }

        [SemantMethod]
        public ISemantReturn<ImmutableList<Tuple<Option<String>, ABT.ExprType>>> GetNamesAndTypes(ABT.Env env) {
            var namesAndTypes = this.ParamDeclns.ConvertAll(
                paramDecln =>
                    Tuple.Create(
                        paramDecln.ParamDeclr.Name,
                        Semant(paramDecln.GetParamType, ref env)
                    )
            );
            return SemantReturn.Create(env, namesAndTypes);
        }
    }

    public sealed class ArrayModifier : TypeModifier {
        private ArrayModifier(Option<Expr> numElements) {
            this.NumElems = numElements;
        }

        public static ArrayModifier Create(Option<Expr> numElements) =>
            new ArrayModifier(numElements);

        [SemantMethod]
        public override ISemantReturn<ABT.ExprType> DecorateType(ABT.Env env, ABT.ExprType elemType) {
            if (this.NumElems.IsNone) {
                return SemantReturn.Create(env, new ABT.IncompleteArrayType(elemType));
            }

            // Get number of elements.
            // Be careful: the environment might change.
            var numElems = SemantExpr(this.NumElems.Value, ref env);

            // Try to cast number of elements to a integer.
            // TODO: allow float???
            numElems = ABT.TypeCast.MakeCast(numElems, new ABT.LongType(true, false));

            if (!numElems.IsConstExpr) {
                throw new InvalidOperationException("Number of elements of an array must be constant.");
            }

            return SemantReturn.Create(env, new ABT.ArrayType(elemType, ((ABT.ConstLong) numElems).Value));
        }

        public Option<Expr> NumElems { get; }
    }

    public sealed class PointerModifier : TypeModifier {
        private PointerModifier(ImmutableList<TypeQual> typeQuals) {
            this.TypeQuals = typeQuals;
        }
        
        public static PointerModifier Create(ImmutableList<TypeQual> typeQuals) =>
            new PointerModifier(typeQuals);

        [SemantMethod]
        public override ISemantReturn<ABT.ExprType> DecorateType(ABT.Env env, ABT.ExprType targetType) {
            var isConst = this.TypeQuals.Contains(TypeQual.CONST);
            var isVolatile = this.TypeQuals.Contains(TypeQual.VOLATILE);
            return SemantReturn.Create(env, new ABT.PointerType(targetType, isConst, isVolatile));
        }

        public ImmutableList<TypeQual> TypeQuals { get; }
    }
    
    /// <summary>
    /// Has a list of modifiers. Has an optional name.
    /// </summary>
    public class ParamDeclr : AbstractDeclr {
        protected ParamDeclr(Option<String> name, ImmutableList<TypeModifier> typeModifiers)
            : base(typeModifiers) {
            this.Name = name;
        }

        public static ParamDeclr Create(Declr declr) =>
            new ParamDeclr(Option.Some(declr.Name), declr.TypeModifiers);

        public static ParamDeclr Create(AbstractDeclr abstractDeclr) =>
            new ParamDeclr(Option<String>.None, abstractDeclr.TypeModifiers);

        public new static ParamDeclr Empty { get; }
            = new ParamDeclr(Option<String>.None, ImmutableList<TypeModifier>.Empty);

        public Option<String> Name { get; }
    }

    /// <summary>
    /// struct-declarator
    ///   : [declarator]? ':' constant-expression
    ///   | declarator
    /// </summary>
    public sealed class StructDeclr : ParamDeclr {
        private StructDeclr(Option<String> name, ImmutableList<TypeModifier> typeModifiers, Option<Expr> numBits)
            : base(name, typeModifiers) {
            this.NumBits = numBits;
        }

        public static StructDeclr Create(Option<Declr> declr, Expr numBits) {
            return declr.IsNone
                ? new StructDeclr(Option<String>.None, ImmutableList<TypeModifier>.Empty, Option.Some(numBits))
                : new StructDeclr(Option.Some(declr.Value.Name), declr.Value.TypeModifiers, Option<Expr>.None);
        }

        public new static StructDeclr Create(Declr declr) =>
            new StructDeclr(Option.Some(declr.Name), declr.TypeModifiers, Option<Expr>.None);

        public Option<Expr> NumBits { get; }
    }

    /// <summary>
    /// abstract-declarator
    ///   : [pointer]? direct-abstract-declarator
    ///   | pointer
    /// 
    /// direct-abstract-declarator
    ///   : [
    ///         '(' abstract-declarator ')'
    ///       | '[' [constant-expression]? ']'  // array modifier
    ///       | '(' [parameter-type_list]? ')'  // function modifier
    ///     ] [
    ///         '[' [constant-expression]? ']'  // array modifier
    ///       | '(' [parameter-Type-list]? ')'  // function modifier
    ///     ]*
    /// 
    /// An abstract declarator is a list of (pointer, function, or array) Type modifiers
    /// </summary>
    public class AbstractDeclr {
        protected AbstractDeclr(ImmutableList<TypeModifier> typeModifiers) {
            this.TypeModifiers = typeModifiers;
        }
        
        public static AbstractDeclr Create<Modifier>(ImmutableList<Modifier> typeModifiers) where Modifier : TypeModifier =>
            new AbstractDeclr(typeModifiers.ToImmutableList<TypeModifier>());
        
        public static AbstractDeclr Empty { get; } =
            Create(ImmutableList<TypeModifier>.Empty);

        public static AbstractDeclr Add(AbstractDeclr abstractDeclr, TypeModifier typeModifier) =>
            Create(abstractDeclr.TypeModifiers.Add(typeModifier));

        public static AbstractDeclr Add(ImmutableList<PointerModifier> pointerModifiers, AbstractDeclr abstractDeclr) =>
            Create(abstractDeclr.TypeModifiers.AddRange(pointerModifiers));

        [SemantMethod]
        public ISemantReturn<ABT.ExprType> DecorateType(ABT.Env env, ABT.ExprType baseType) {
            var type = this.TypeModifiers
                .Reverse()  // The first Type modifier is nearest to the symbol name, which indicates the outmost Type.
                .Aggregate( // Wrap up the Type based on the Type modifiers.
                    baseType, (currentType, typeModifier) => Semant(typeModifier.DecorateType, currentType, ref env)
                );

            return SemantReturn.Create(env, type);
        }

        public ImmutableList<TypeModifier> TypeModifiers { get; }
    }

    /// <summary>
    /// Has a name and a list of modifiers.
    /// </summary>
    public sealed class Declr : AbstractDeclr {
        private Declr(String name, ImmutableList<TypeModifier> typeModifiers)
            : base(typeModifiers) {
            this.Name = name;
        }

        public static Declr Create(String name, ImmutableList<TypeModifier> typeModifiers) =>
            new Declr(name, typeModifiers);

        public static Declr Create(String name) =>
            new Declr(name, ImmutableList<TypeModifier>.Empty);

        public static Declr Create(Option<ImmutableList<PointerModifier>> pointerModifiers, Declr declr) =>
            Add(pointerModifiers.IsSome ? pointerModifiers.Value : ImmutableList<PointerModifier>.Empty, declr);
        
        public static Declr Add(Declr declr, TypeModifier typeModifier) =>
            Create(declr.Name, declr.TypeModifiers.Add(typeModifier));

        public static Declr Add(ImmutableList<PointerModifier> pointerModifiers, Declr declr) =>
            Create(declr.Name, declr.TypeModifiers.AddRange(pointerModifiers));

        public String Name { get; }
    }

    /// <summary>
    /// Init-declarator
    ///   : declarator [ '=' initializer ]?
    /// </summary>
    public sealed class InitDeclr : ISyntaxTreeNode {
        private InitDeclr(Declr declr, Option<Initr> initr) {
            this.Declr = declr;
            this.Initr = initr;
        }

        public Declr Declr { get; }
        public Option<Initr> Initr { get; }

        public static InitDeclr Create(Declr declr, Option<Initr> initr)
            => new InitDeclr(declr, initr);

        [SemantMethod]
        public String GetName() => this.Declr.Name;

        [SemantMethod]
        public ISemantReturn<Tuple<ABT.ExprType, Option<ABT.Initr>>> GetDecoratedTypeAndInitr(ABT.Env env, ABT.ExprType baseType) {

            // Get the Type based on the declarator. Note that this might be an incomplete array.
            var type = Semant(this.Declr.DecorateType, baseType, ref env);

            Option<ABT.Initr> initrOption;

            if (this.Initr.IsNone) {
                initrOption = Option<ABT.Initr>.None;

            } else {
                // If an initializer is present:

                var initr = Semant(this.Initr.Value.GetInitr, ref env);

                // Check that the initializer conforms to the Type.
                initr = initr.ConformType(type);

                // If the object is an incomplete array, we must determine the length based on the initializer.
                if (type.Kind == ABT.ExprTypeKind.INCOMPLETE_ARRAY) {
                    // Now we need to determine the length.
                    // Find the last element in the Init list.
                    var lastOffset = -1;
                    initr.Iterate(type, (offset, _) => { lastOffset = offset; });

                    if (lastOffset == -1) {
                        throw new InvalidOperationException("Cannot determine the length of the array based on an empty initializer list.");
                    }

                    var elemType = ((ABT.IncompleteArrayType)type).ElemType;

                    var numElems = 1 + lastOffset / ((ABT.IncompleteArrayType)type).ElemType.SizeOf;

                    type = new ABT.ArrayType(elemType, numElems);
                }

                initrOption = Option.Some(initr);
            }
            
            // Now everything is created. Check that the Type is complete.
            if (!type.IsComplete) {
                throw new InvalidOperationException("Cannot create an object with an incomplete Type.");
            }

            return SemantReturn.Create(env, Tuple.Create(type, initrOption));
        }
        
    }

    /// <summary>
    /// initializer
    ///   : assignment-expression
    ///   | '{' initializer-list '}'
    ///   | '{' initializer-list ',' '}'
    /// 
    /// initializer-list
    ///   : initializer [ ',' initializer ]*
    /// </summary>
    public abstract class Initr : ISyntaxTreeNode {
        [SemantMethod]
        public abstract ISemantReturn<ABT.Initr> GetInitr(ABT.Env env);
    }

    public sealed class InitExpr : Initr {
        private InitExpr(Expr expr) {
            this.Expr = expr;
        }

        public static InitExpr Create(Expr expr) =>
            new InitExpr(expr);

        public Expr Expr { get; }

        [SemantMethod]
        public override ISemantReturn<ABT.Initr> GetInitr(ABT.Env env) {
            var expr = SemantExpr(this.Expr, ref env);
            return SemantReturn.Create(env, new ABT.InitExpr(expr));
        }
    }

    public sealed class InitList : Initr {
        private InitList(ImmutableList<Initr> initrs) {
            this.Initrs = initrs;
        }

        public static InitList Create(ImmutableList<Initr> initrs) =>
            new InitList(initrs);

        public ImmutableList<Initr> Initrs { get; }

        [SemantMethod]
        public override ISemantReturn<ABT.Initr> GetInitr(ABT.Env env) {
            var initrs = this.Initrs.ConvertAll(
                initr => Semant(initr.GetInitr, ref env)
            );
            return SemantReturn.Create(env, new ABT.InitList(initrs.ToList()));
        }
    }
}

<|start_filename|>TestPrograms/IfElseStmt.c<|end_filename|>
int printf(char *, ...);
int main() {
    int a = 3;
    int b = 4;
    if (a > b) {
        a = 3;
        printf("a > b\n");
    } else {
        printf("a <= b\n");
    }

    if (a <= b) {
        printf("a <= b\n");
    } else {
        printf("a > b\n");
    }

    return 0;
}


<|start_filename|>AST/Declarations.cs<|end_filename|>
﻿using System;
using System.Collections.Immutable;
using System.Linq;
using ABT;
using static AST.SemanticAnalysis;

namespace AST {

    /// <summary>
    /// declaration
    ///   : declaration-specifiers [Init-declarator-list]? ';'
    /// </summary>
    public sealed class Decln : IExternDecln {
        private Decln(DeclnSpecs declnSpecs, ImmutableList<InitDeclr> initDeclrs) {
            this.DeclnSpecs = declnSpecs;
            this.InitDeclrs = initDeclrs;
        }

        public static Decln Create(DeclnSpecs declnSpecs, ImmutableList<InitDeclr> initDeclrs) =>
            new Decln(declnSpecs, initDeclrs);

        public DeclnSpecs DeclnSpecs { get; }
        public ImmutableList<InitDeclr> InitDeclrs { get; }

        [SemantMethod]
        public ISemantReturn<ImmutableList<Tuple<Env, ABT.Decln>>> GetDeclns(Env env) {
            var storageClass = this.DeclnSpecs.GetStorageClass();
            var baseType = Semant(this.DeclnSpecs.GetExprType, ref env);

            var declns = this.InitDeclrs.ConvertAll(
                initDeclr => {
                    var typeAndInitr = Semant(initDeclr.GetDecoratedTypeAndInitr, baseType, ref env);
                    var type = typeAndInitr.Item1;
                    var initr = typeAndInitr.Item2;
                    var name = initDeclr.GetName();

                    // Add the new symbol into the environment.
                    Env.EntryKind kind;
                    switch (storageClass) {
                        case StorageClass.AUTO:
                            if (env.IsGlobal()) {
                                kind = Env.EntryKind.GLOBAL;
                            } else {
                                kind = Env.EntryKind.STACK;
                            }
                            break;
                        case StorageClass.EXTERN:
                            kind = Env.EntryKind.GLOBAL;
                            break;
                        case StorageClass.STATIC:
                            kind = Env.EntryKind.GLOBAL;
                            break;
                        case StorageClass.TYPEDEF:
                            kind = Env.EntryKind.TYPEDEF;
                            break;
                        default:
                            throw new InvalidOperationException();
                    }
                    env = env.PushEntry(kind, name, type);

                    return Tuple.Create(env, new ABT.Decln(name, storageClass, type, initr));
                }
            );

            return SemantReturn.Create(env, declns);
        }

        [SemantMethod]
        public ISemantReturn<ImmutableList<Tuple<Env, ExternDecln>>> GetExternDecln(Env env) {
            var declns = Semant(GetDeclns, ref env);
            var externDeclns = declns.ConvertAll(_ => Tuple.Create(_.Item1, _.Item2 as ExternDecln));
            return SemantReturn.Create(env, externDeclns);
        }
    }

    /// <summary>
    /// struct-declaration
    ///   : specifier-qualifier-list struct-declarator-list ';'
    /// 
    /// struct-declarator-list
    ///   : struct-declarator [ ',' struct-declarator ]*
    /// 
    /// struct-declarator
    ///   : declarator
    ///   | [declarator]? ':' constant-expression
    /// </summary>
    public class StructDecln : ISyntaxTreeNode {
        protected StructDecln(SpecQualList specQualList, ImmutableList<StructDeclr> structDeclrs) {
            this.SpecQualList = specQualList;
            this.StructDeclrs = structDeclrs;
        }

        public static StructDecln Create(SpecQualList specQualList, ImmutableList<StructDeclr> structDeclrs) =>
            new StructDecln(specQualList, structDeclrs);

        public SpecQualList SpecQualList { get; }
        public ImmutableList<StructDeclr> StructDeclrs { get; }

        [SemantMethod]
        public ISemantReturn<ImmutableList<Tuple<Option<String>, ExprType>>> GetMemberDeclns(Env env) {
            // Semant specifier-qualifier-list.
            var baseType = Semant(this.SpecQualList.GetExprType, ref env);

            // Decorate types, based on struct declarators.
            var memberTypes =
                this.StructDeclrs
                .ConvertAll(
                    structDeclr =>
                        Semant(structDeclr.DecorateType, baseType, ref env)
                );

            // Get (optional) member names.
            var memberNames =
                this.StructDeclrs
                .ConvertAll(
                    structDeclr => structDeclr.Name
                );

            return SemantReturn.Create(env, memberNames.Zip(memberTypes, Tuple.Create).ToImmutableList());
        }

    }

    /// <summary>
    /// Parameter Declaration.
    /// 
    /// int foo(int arg0, int arg1);
    ///         ~~~~~~~~
    /// 
    /// int foo(int, int);
    ///         ~~~
    /// 
    /// The declarator can be completely omitted.
    /// </summary>
    public class ParamDecln : ISyntaxTreeNode {
        protected ParamDecln(DeclnSpecs declnSpecs, ParamDeclr paramDeclr) {
            this.DeclnSpecs = declnSpecs;
            this.ParamDeclr = paramDeclr;
        }

        public static ParamDecln Create(DeclnSpecs declnSpecs, Option<ParamDeclr> paramDeclr) =>
            new ParamDecln(declnSpecs, paramDeclr.IsSome ? paramDeclr.Value : ParamDeclr.Empty);

        public DeclnSpecs DeclnSpecs { get; }
        public ParamDeclr ParamDeclr { get; }

        [SemantMethod]
        public ISemantReturn<ExprType> GetParamType(Env env) {
            var baseType = Semant(this.DeclnSpecs.GetExprType, ref env);
            var type = Semant(this.ParamDeclr.DecorateType, baseType, ref env);
            return SemantReturn.Create(env, type);
        }
    }

    /// <summary>
    /// Type-name
    ///   : specifier-qualifier-list [abstract-declarator]?
    /// </summary>
    public class TypeName : ISyntaxTreeNode {
        protected TypeName(SpecQualList specQualList, AbstractDeclr abstractDeclr) {
            this.SpecQualList = specQualList;
            this.AbstractDeclr = abstractDeclr;
        }

        public static TypeName Create(SpecQualList specQualList, Option<AbstractDeclr> abstractDeclr) =>
            new TypeName(specQualList, abstractDeclr.IsSome ? abstractDeclr.Value : AbstractDeclr.Empty);

        public SpecQualList SpecQualList { get; }
        public AbstractDeclr AbstractDeclr { get; }

        [SemantMethod]
        public ISemantReturn<ExprType> GetExprType(Env env) {
            var baseType = Semant(this.SpecQualList.GetExprType, ref env);
            var type = Semant(this.AbstractDeclr.DecorateType, baseType, ref env);
            return SemantReturn.Create(env, type);
        }

    }
}

<|start_filename|>ParserTests/ParserTestUtils.cs<|end_filename|>
﻿using System;
using System.Linq;
using LexicalAnalysis;
using NUnit.Framework;
using Parsing;

namespace CompilerTests {
    public static class ParserTestUtils {
        public static ParserInput CreateInput(String source) {
            var scanner = new Scanner(source);
            return new ParserInput(new ParserEnvironment(), scanner.Tokens);
        }

        public static void TestParserRule<R>(String source, ParserEnvironment env, IParser<R> parser) {
            var scanner = new Scanner(source);
            var input = new ParserInput(env, scanner.Tokens);
            var result = parser.Parse(input);
            Assert.IsTrue(result.IsSuccessful);
            Assert.IsTrue(result.Source.Count() == 1);
        }

        public static void TestParserRule<R>(String source, IParser<R> parser) =>
            TestParserRule(source, new ParserEnvironment(), parser);

        public static void TestParserRule<R>(IParser<R> parser, params String[] sources) {
            foreach (var source in sources) {
                TestParserRule(source, parser);
            }
        }

    
    }
}

<|start_filename|>CGen/UnaryOperators.cs<|end_filename|>
﻿using System;
using System.Diagnostics;
using CodeGeneration;

namespace ABT {
    public abstract partial class IncDecExpr {

        // Integral
        // Before the actual calculation, the state is set to this.
        // 
        // regs:
        // %eax = expr
        // %ebx = expr
        // %ecx = &expr
        // (Yes, both %eax and %ebx are expr.)
        // 
        // stack:
        // +-------+
        // | ..... | <- %esp
        // +-------+
        // 
        // After the calculation, the result should be in %eax,
        // and memory should be updated.
        //
        public abstract void CalcAndSaveLong(CGenState state);

        public abstract void CalcAndSaveWord(CGenState state);

        public abstract void CalcAndSaveByte(CGenState state);

        public abstract void CalcAndSavePtr(CGenState state);

        // Float
        // Before the actual calculation, the state is set to this.
        // 
        // regs:
        // %ecx = &expr
        // 
        // stack:
        // +-------+
        // | ..... | <- %esp
        // +-------+
        // 
        // float stack:
        // +-------+
        // | expr  | <- %st(1)
        // +-------+
        // |  1.0  | <- %st(0)
        // +-------+
        // 
        // After the calculation, the result should be in %st(0),
        // and memory should be updated.
        // 
        public abstract void CalcAndSaveFloat(CGenState state);

        public abstract void CalcAndSaveDouble(CGenState state);

        public override sealed Reg CGenValue(CGenState state) {

            // 1. Get the address of expr.
            // 
            // regs:
            // %eax = &expr
            // 
            // stack:
            // +-------+
            // | ..... | <- %esp
            // +-------+
            // 
            this.Expr.CGenAddress(state);

            // 2. Push address.
            // 
            // regs:
            // %eax = &expr
            // 
            // stack:
            // +-------+
            // | ..... |
            // +-------+
            // | &expr | <- %esp
            // +-------+
            // 
            Int32 stack_size = state.CGenPushLong(Reg.EAX);

            // 3. Get current Value of expr.
            // 
            // 1) If expr is an integral or pointer:
            // 
            // regs:
            // %eax = expr
            // 
            // stack:
            // +-------+
            // | ..... |
            // +-------+
            // | &expr | <- %esp
            // +-------+
            // 
            // 
            // 2) If expr is a float:
            // 
            // regs:
            // %eax = &expr
            // 
            // stack:
            // +-------+
            // | ..... |
            // +-------+
            // | &expr | <- %esp
            // +-------+
            // 
            // float stack:
            // +-------+
            // | expr  | <- %st(0)
            // +-------+
            // 
            Reg ret = this.Expr.CGenValue(state);

            switch (ret) {
                case Reg.EAX:
                    // expr is an integral or pointer.

                    // 4. Pop address to %ecx.
                    // 
                    // regs:
                    // %eax = expr
                    // %ecx = &expr
                    // 
                    // stack:
                    // +-------+
                    // | ..... | <- %esp
                    // +-------+
                    // 
                    state.CGenPopLong(stack_size, Reg.ECX);

                    // 5. Cache current Value of Expr in %ebx.
                    // 
                    // regs:
                    // %eax = expr
                    // %ebx = expr
                    // %ecx = &expr
                    // 
                    // stack:
                    // +-------+
                    // | ..... | <- %esp
                    // +-------+
                    // 
                    state.MOVL(Reg.EAX, Reg.EBX);

                    // 6. Calculate the new value in %ebx or %eax and save.
                    //    Set %eax to be the return Value.
                    // 
                    // regs:
                    // %eax = expr or (expr +- 1)
                    // %ebx = (expr +- 1) or expr
                    // %ecx = &expr
                    // 
                    // stack:
                    // +-------+
                    // | ..... | <- %esp
                    // +-------+
                    // 
                    switch (this.Expr.Type.Kind) {
                        case ExprTypeKind.CHAR:
                        case ExprTypeKind.UCHAR:
                            CalcAndSaveByte(state);
                            return Reg.EAX;

                        case ExprTypeKind.SHORT:
                        case ExprTypeKind.USHORT:
                            CalcAndSaveWord(state);
                            return Reg.EAX;

                        case ExprTypeKind.LONG:
                        case ExprTypeKind.ULONG:
                            CalcAndSaveByte(state);
                            return Reg.EAX;

                        case ExprTypeKind.POINTER:
                            CalcAndSavePtr(state);
                            return Reg.EAX;

                        default:
                            throw new InvalidProgramException();
                    }

                case Reg.ST0:
                    // Expr is a float.

                    // 4. Pop address to %ecx.
                    // 
                    // regs:
                    // %ecx = &expr
                    // 
                    // stack:
                    // +-------+
                    // | ..... | <- %esp
                    // +-------+
                    // 
                    state.CGenPopLong(stack_size, Reg.ECX);

                    // 5. Load 1.0 to FPU stack.
                    // 
                    // regs:
                    // %ecx = &expr
                    // 
                    // stack:
                    // +-------+
                    // | ..... | <- %esp
                    // +-------+
                    // 
                    // float stack:
                    // +-------+
                    // | expr  | <- %st(1)
                    // +-------+
                    // |  1.0  | <- %st(0)
                    // +-------+
                    // 
                    state.FLD1();

                    // 6. Calculate the new value and save back.
                    //    Set %st(0) to be the new or original Value.
                    // 
                    // regs:
                    // %ecx = &Expr
                    // 
                    // stack:
                    // +-------+
                    // | ..... | <- %esp
                    // +-------+
                    // 
                    // float stack:
                    // +---------------------+
                    // | expr or (epxr +- 1) | <- %st(0)
                    // +---------------------+
                    // 
                    switch (this.Expr.Type.Kind) {
                        case ExprTypeKind.FLOAT:
                            CalcAndSaveFloat(state);
                            return Reg.ST0;

                        case ExprTypeKind.DOUBLE:
                            CalcAndSaveDouble(state);
                            return Reg.ST0;

                        default:
                            throw new InvalidProgramException();
                    }

                default:
                    throw new InvalidProgramException();
            }

        }

        public override sealed void CGenAddress(CGenState state) {
            throw new InvalidOperationException(
                "Cannot get the address of an increment/decrement expression."
            );
        }
    }

    public sealed partial class PostIncrement {
        public override void CalcAndSaveLong(CGenState state) {
            state.ADDL(1, Reg.EBX);
            state.MOVL(Reg.EBX, 0, Reg.ECX);
        }

        public override void CalcAndSaveWord(CGenState state) {
            state.ADDL(1, Reg.EBX);
            state.MOVW(Reg.BX, 0, Reg.ECX);
        }

        public override void CalcAndSaveByte(CGenState state) {
            state.ADDL(1, Reg.EBX);
            state.MOVB(Reg.BL, 0, Reg.ECX);
        }

        public override void CalcAndSavePtr(CGenState state) {
            state.ADDL(this.Expr.Type.SizeOf, Reg.EBX);
            state.MOVL(Reg.EBX, 0, Reg.ECX);
        }

        public override void CalcAndSaveFloat(CGenState state) {
            state.FADD(1, 0);
            state.FSTPS(0, Reg.ECX);
        }

        public override void CalcAndSaveDouble(CGenState state) {
            state.FADD(1, 0);
            state.FSTPL(0, Reg.ECX);
        }
    }

    public sealed partial class PostDecrement {
        public override void CalcAndSaveLong(CGenState state) {
            state.SUBL(1, Reg.EBX);
            state.MOVL(Reg.EBX, 0, Reg.ECX);
        }

        public override void CalcAndSaveWord(CGenState state) {
            state.SUBL(1, Reg.EBX);
            state.MOVW(Reg.BX, 0, Reg.ECX);
        }

        public override void CalcAndSaveByte(CGenState state) {
            state.SUBL(1, Reg.EBX);
            state.MOVB(Reg.BL, 0, Reg.ECX);
        }

        public override void CalcAndSavePtr(CGenState state) {
            state.SUBL(this.Expr.Type.SizeOf, Reg.EBX);
            state.MOVL(Reg.EBX, 0, Reg.ECX);
        }

        public override void CalcAndSaveFloat(CGenState state) {
            state.FSUB(1, 0);
            state.FSTPS(0, Reg.ECX);
        }

        public override void CalcAndSaveDouble(CGenState state) {
            state.FSUB(1, 0);
            state.FSTPL(0, Reg.ECX);
        }
    }

    public sealed partial class PreIncrement {
        public override void CalcAndSaveLong(CGenState state) {
            state.ADDL(1, Reg.EAX);
            state.MOVL(Reg.EAX, 0, Reg.ECX);
        }

        public override void CalcAndSaveWord(CGenState state) {
            state.ADDL(1, Reg.EAX);
            state.MOVW(Reg.AX, 0, Reg.ECX);
        }

        public override void CalcAndSaveByte(CGenState state) {
            state.ADDL(1, Reg.EAX);
            state.MOVB(Reg.AL, 0, Reg.ECX);
        }

        public override void CalcAndSavePtr(CGenState state) {
            state.ADDL(this.Expr.Type.SizeOf, Reg.EAX);
            state.MOVL(Reg.EAX, 0, Reg.ECX);
        }

        public override void CalcAndSaveFloat(CGenState state) {
            state.FADD(1, 0);
            state.FSTS(0, Reg.ECX);
        }

        public override void CalcAndSaveDouble(CGenState state) {
            state.FADD(1, 0);
            state.FSTL(0, Reg.ECX);
        }
    }

    public sealed partial class PreDecrement {
        public override void CalcAndSaveLong(CGenState state) {
            state.SUBL(1, Reg.EAX);
            state.MOVL(Reg.EAX, 0, Reg.ECX);
        }

        public override void CalcAndSaveWord(CGenState state) {
            state.SUBL(1, Reg.EAX);
            state.MOVW(Reg.AX, 0, Reg.ECX);
        }

        public override void CalcAndSaveByte(CGenState state) {
            state.SUBL(1, Reg.EAX);
            state.MOVB(Reg.AL, 0, Reg.ECX);
        }

        public override void CalcAndSavePtr(CGenState state) {
            state.SUBL(this.Expr.Type.SizeOf, Reg.EAX);
            state.MOVL(Reg.EAX, 0, Reg.ECX);
        }

        public override void CalcAndSaveFloat(CGenState state) {
            state.FSUB(1, 0);
            state.FSTS(0, Reg.ECX);
        }

        public override void CalcAndSaveDouble(CGenState state) {
            state.FSUB(1, 0);
            state.FSTL(0, Reg.ECX);
        }
    }

    public abstract partial class UnaryArithOp {
        public override sealed void CGenAddress(CGenState state) {
            throw new InvalidOperationException(
                "Cannot get the address of an unary arithmetic operator."
            );
        }
    }

    public sealed partial class Negative {
        public override Reg CGenValue(CGenState state) {
            Reg ret = this.Expr.CGenValue(state);
            switch (ret) {
                case Reg.EAX:
                    state.NEG(Reg.EAX);
                    return Reg.EAX;

                case Reg.ST0:
                    state.FCHS();
                    return Reg.ST0;

                default:
                    throw new InvalidProgramException();
            }
        }
    }

    public sealed partial class BitwiseNot {
        public override Reg CGenValue(CGenState state) {
            Reg ret = this.Expr.CGenValue(state);
            if (ret != Reg.EAX) {
                throw new InvalidProgramException();
            }
            state.NOT(Reg.EAX);
            return Reg.EAX;
        }
    }

    public sealed partial class LogicalNot {
        public override Reg CGenValue(CGenState state) {
            Reg ret = this.Expr.CGenValue(state);
            switch (ret) {
                case Reg.EAX:
                    state.TESTL(Reg.EAX, Reg.EAX);
                    state.SETE(Reg.AL);
                    state.MOVZBL(Reg.AL, Reg.EAX);
                    return Reg.EAX;

                case Reg.ST0:
                    /// Compare Expr with 0.0
                    /// < see cref = "BinaryComparisonOp.OperateFloat(CGenState)" />
                    state.FLDZ();
                    state.FUCOMIP();
                    state.FSTP(Reg.ST0);
                    state.SETE(Reg.AL);
                    state.MOVZBL(Reg.AL, Reg.EAX);
                    return Reg.EAX;

                default:
                    throw new InvalidProgramException();
            }
        }
    }
}


<|start_filename|>ABT/Expressions.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using CodeGeneration;

namespace ABT {
    // Expr 
    // ========================================================================

    /// <summary>
    /// The cdecl calling convention:
    /// 1. arguments are passed on the stack, right to left.
    /// 2. int values and pointer values are returned in %eax.
    /// 3. floats are returned in %st(0).
    /// 4. when calling a function, %st(0) ~ %st(7) are all free.
    /// 5. functions are free to use %eax, %ecx, %edx, because caller needs to save them.
    /// 6. stack must be aligned to 4 bytes (before gcc 4.5, for gcc 4.5+, aligned to 16 bytes).
    /// </summary>

    public abstract partial class Expr {
        protected Expr() { }

        /// <summary>
        /// Whether the Value is known at compile time.
        /// </summary>
        public virtual Boolean IsConstExpr => false;

        /// <summary>
        /// Whether the expression refers to an object (that can be assigned to).
        /// </summary>
        public abstract Boolean IsLValue { get; }

        public abstract Env Env { get; }

        public abstract ExprType Type { get; }
    }

    public sealed partial class Variable : Expr {
        public Variable(ExprType type, String name, Env env) {
            this.Name = name;
            this.Env = env;
            this.Type = type;
        }

        public String Name { get; }

        public override Env Env { get; }

        public override ExprType Type { get; }

        public override Boolean IsLValue => !(Type is FunctionType);
    }

    public sealed partial class AssignList : Expr {
        public AssignList(ImmutableList<Expr> exprs) {
            if (exprs.Count == 0) {
                throw new InvalidOperationException("Need at least one expression.");
            }
            this.Exprs = exprs;
        }

        public ImmutableList<Expr> Exprs { get; }

        public override Env Env => this.Exprs.Last().Env;

        public override Boolean IsLValue => false;

        public override ExprType Type => this.Exprs.Last().Type;
    }

    public sealed partial class Assign : Expr {
        public Assign(Expr left, Expr right) {
            this.Left = left;
            this.Right = right;

            if (!this.Left.IsLValue) {
                throw new InvalidOperationException("Can only assign to lvalue.");
            }
        }

        public Expr Left { get; }

        public Expr Right { get; }

        public override Env Env => this.Right.Env;

        public override Boolean IsLValue => false;

        public override ExprType Type => this.Left.Type.GetQualifiedType(false, false);
    }

    public sealed partial class ConditionalExpr : Expr {
        public ConditionalExpr(Expr cond, Expr trueExpr, Expr falseExpr, ExprType type) {
            this.Cond = cond;
            this.TrueExpr = trueExpr;
            this.FalseExpr = falseExpr;
            this.Type = type;
        }

        public readonly Expr Cond;

        public readonly Expr TrueExpr;

        public readonly Expr FalseExpr;

        public override Boolean IsLValue => false;

        public override ExprType Type { get; }

        public override Env Env => this.FalseExpr.Env;
    }
        
    public sealed partial class FuncCall : Expr {
        public FuncCall(Expr func, FunctionType funcType, List<Expr> args) {
            this.Func = func;
            this.FuncType = funcType;
            this.Args = args;
        }

        public Expr Func { get; }

        public FunctionType FuncType { get; }

        public IReadOnlyList<Expr> Args { get; }

        public override ExprType Type => this.FuncType.ReturnType;

        public override Env Env => this.Args.Any() ? this.Args.Last().Env : this.Func.Env;

        public override Boolean IsLValue => false;
    }

    /// <summary>
    /// Expr.name: Expr must be a struct or union.
    /// </summary>
    public sealed partial class Attribute : Expr {
        public Attribute(Expr expr, String name, ExprType type) {
            this.Expr = expr;
            this.Name = name;
            this.Type = type;
        }

        public Expr Expr { get; }

        public String Name { get; }

        public override Env Env => this.Expr.Env;

        public override ExprType Type { get; }

        // You might want to think of some special case like this.
        // struct EvilStruct {
        //     int a[10];
        // } evil;
        // evil.a <--- is this an lvalue?
        // Yes, it is. It cannot be assigned, but that's because of the wrong Type.
        public override Boolean IsLValue => this.Expr.IsLValue;
    }

    /// <summary>
    /// &amp;Expr: get the address of Expr.
    /// </summary>
    public sealed partial class Reference : Expr {
        public Reference(Expr expr) {
            this.Expr = expr;
            this.Type = new PointerType(expr.Type);
        }

        public Expr Expr { get; }

        public override Env Env => this.Expr.Env;

        public override ExprType Type { get; }

        // You might want to think of some special case like this.
        // int *a;
        // &(*a) = 3; // Is this okay?
        // But this should lead to an error: lvalue required.
        // The 'reference' operator only gets the 'current address'.
        public override Boolean IsLValue => false;
    }

    /// <summary>
    /// *Expr: Expr must be a pointer.
    /// 
    /// Arrays and functions are implicitly converted to pointers.
    /// 
    /// This is an lvalue, so it has an address.
    /// </summary>
    public sealed partial class Dereference : Expr {
        public Dereference(Expr expr, ExprType type) {
            this.Expr = expr;
            this.Type = type;
        }

        public Expr Expr { get; }

        public override Env Env => this.Expr.Env;

        public override Boolean IsLValue => true;

        public override ExprType Type { get; }
    }
}

<|start_filename|>Parser/Tokens.cs<|end_filename|>
﻿using System;
using LexicalAnalysis;
using AST;

namespace Parsing {

    public partial class CParsers {
        
        public static IParser<String> Identifier { get; } = new IdentifierParser();
        public static IParser<Expr> ConstChar { get; } = new ConstCharParser();
        public static IParser<Expr> ConstInt { get; } = new ConstIntParser();
        public static IParser<Expr> ConstFloat { get; } = new ConstFloatParser();
        public static IParser<Expr> StringLiteral { get; } = new StringLiteralParser();

        public static IConsumer LeftParen { get; } = OperatorConsumer.Create(OperatorVal.LPAREN);
        public static IConsumer RightParen { get; } = OperatorConsumer.Create(OperatorVal.RPAREN);
        public static IConsumer Question { get; } = OperatorConsumer.Create(OperatorVal.QUESTION);
        public static IConsumer Comma { get; } = OperatorConsumer.Create(OperatorVal.COMMA);
        public static IConsumer Colon { get; } = OperatorConsumer.Create(OperatorVal.COLON);

        public static IConsumer Assign { get; } = OperatorConsumer.Create(OperatorVal.ASSIGN);
        public static IConsumer MultAssign { get; } = OperatorConsumer.Create(OperatorVal.MULTASSIGN);
        public static IConsumer DivAssign { get; } = OperatorConsumer.Create(OperatorVal.DIVASSIGN);
        public static IConsumer ModAssign { get; } = OperatorConsumer.Create(OperatorVal.MODASSIGN);
        public static IConsumer AddAssign { get; } = OperatorConsumer.Create(OperatorVal.ADDASSIGN);
        public static IConsumer SubAssign { get; } = OperatorConsumer.Create(OperatorVal.SUBASSIGN);
        public static IConsumer LeftShiftAssign { get; } = OperatorConsumer.Create(OperatorVal.LSHIFTASSIGN);
        public static IConsumer RightShiftAssign { get; } = OperatorConsumer.Create(OperatorVal.RSHIFTASSIGN);
        public static IConsumer BitwiseAndAssign { get; } = OperatorConsumer.Create(OperatorVal.ANDASSIGN);
        public static IConsumer XorAssign { get; } = OperatorConsumer.Create(OperatorVal.XORASSIGN);
        public static IConsumer BitwiseOrAssign { get; } = OperatorConsumer.Create(OperatorVal.ORASSIGN);
        public static IConsumer LeftBracket { get; } = OperatorConsumer.Create(OperatorVal.LBRACKET);
        public static IConsumer RightBracket { get; } = OperatorConsumer.Create(OperatorVal.RBRACKET);
        public static IConsumer LeftCurlyBrace { get; } = OperatorConsumer.Create(OperatorVal.LCURL);
        public static IConsumer RightCurlyBrace { get; } = OperatorConsumer.Create(OperatorVal.RCURL);
        public static IConsumer Period { get; } = OperatorConsumer.Create(OperatorVal.PERIOD);
        public static IConsumer RightArrow { get; } = OperatorConsumer.Create(OperatorVal.RARROW);
        public static IConsumer Increment { get; } = OperatorConsumer.Create(OperatorVal.INC);
        public static IConsumer Decrement { get; } = OperatorConsumer.Create(OperatorVal.DEC);
        public static IConsumer Mult { get; } = OperatorConsumer.Create(OperatorVal.MULT);
        public static IConsumer Add { get; } = OperatorConsumer.Create(OperatorVal.ADD);
        public static IConsumer Sub { get; } = OperatorConsumer.Create(OperatorVal.SUB);
        public static IConsumer BitwiseNot { get; } = OperatorConsumer.Create(OperatorVal.TILDE);
        public static IConsumer LogicalNot { get; } = OperatorConsumer.Create(OperatorVal.NOT);
        public static IConsumer Div { get; } = OperatorConsumer.Create(OperatorVal.DIV);
        public static IConsumer Mod { get; } = OperatorConsumer.Create(OperatorVal.MOD);
        public static IConsumer LeftShift { get; } = OperatorConsumer.Create(OperatorVal.LSHIFT);
        public static IConsumer RightShift { get; } = OperatorConsumer.Create(OperatorVal.RSHIFT);
        public static IConsumer Less { get; } = OperatorConsumer.Create(OperatorVal.LT);
        public static IConsumer Greater { get; } = OperatorConsumer.Create(OperatorVal.GT);
        public static IConsumer LessEqual { get; } = OperatorConsumer.Create(OperatorVal.LEQ);
        public static IConsumer GreaterEqual { get; } = OperatorConsumer.Create(OperatorVal.GEQ);
        public static IConsumer Equal { get; } = OperatorConsumer.Create(OperatorVal.EQ);
        public static IConsumer NotEqual { get; } = OperatorConsumer.Create(OperatorVal.NEQ);
        public static IConsumer Xor { get; } = OperatorConsumer.Create(OperatorVal.XOR);
        public static IConsumer BitwiseAnd { get; } = OperatorConsumer.Create(OperatorVal.BITAND);
        public static IConsumer BitwiseOr { get; } = OperatorConsumer.Create(OperatorVal.BITOR);
        public static IConsumer LogicalAnd { get; } = OperatorConsumer.Create(OperatorVal.AND);
        public static IConsumer LogicalOr { get; } = OperatorConsumer.Create(OperatorVal.OR);
        public static IConsumer Semicolon { get; } = OperatorConsumer.Create(OperatorVal.SEMICOLON);

        public static IConsumer SizeOf { get; } = KeywordConsumer.Create(KeywordVal.SIZEOF);
        public static IParser<StorageClsSpec> Auto { get; } = KeywordParser.Create(KeywordVal.AUTO, StorageClsSpec.AUTO);
        public static IParser<StorageClsSpec> Register { get; } = KeywordParser.Create(KeywordVal.REGISTER, StorageClsSpec.REGISTER);
        public static IParser<StorageClsSpec> Static { get; } = KeywordParser.Create(KeywordVal.STATIC, StorageClsSpec.STATIC);
        public static IParser<StorageClsSpec> Extern { get; } = KeywordParser.Create(KeywordVal.EXTERN, StorageClsSpec.EXTERN);
        public static IParser<StorageClsSpec> Typedef { get; } = KeywordParser.Create(KeywordVal.TYPEDEF, StorageClsSpec.TYPEDEF);

        public static IParser<TypeSpecKind> Void { get; } = KeywordParser.Create(KeywordVal.VOID, TypeSpecKind.VOID);
        public static IParser<TypeSpecKind> Char { get; } = KeywordParser.Create(KeywordVal.CHAR, TypeSpecKind.CHAR);
        public static IParser<TypeSpecKind> Short { get; } = KeywordParser.Create(KeywordVal.SHORT, TypeSpecKind.SHORT);
        public static IParser<TypeSpecKind> Int { get; } = KeywordParser.Create(KeywordVal.INT, TypeSpecKind.INT);
        public static IParser<TypeSpecKind> Long { get; } = KeywordParser.Create(KeywordVal.LONG, TypeSpecKind.LONG);
        public static IParser<TypeSpecKind> Float { get; } = KeywordParser.Create(KeywordVal.FLOAT, TypeSpecKind.FLOAT);
        public static IParser<TypeSpecKind> Double { get; } = KeywordParser.Create(KeywordVal.DOUBLE, TypeSpecKind.DOUBLE);
        public static IParser<TypeSpecKind> Signed { get; } = KeywordParser.Create(KeywordVal.SIGNED, TypeSpecKind.SIGNED);
        public static IParser<TypeSpecKind> Unsigned { get; } = KeywordParser.Create(KeywordVal.UNSIGNED, TypeSpecKind.UNSIGNED);

        public static IParser<TypeQual> Const { get; } = KeywordParser.Create(KeywordVal.CONST, TypeQual.CONST);
        public static IParser<TypeQual> Volatile { get; } = KeywordParser.Create(KeywordVal.VOLATILE, TypeQual.VOLATILE);

        public static IConsumer Enum { get; } = KeywordConsumer.Create(KeywordVal.ENUM);
        public static IParser<StructOrUnion> Struct { get; } = KeywordParser.Create(KeywordVal.STRUCT, AST.StructOrUnion.STRUCT);
        public static IParser<StructOrUnion> Union { get; } = KeywordParser.Create(KeywordVal.UNION, AST.StructOrUnion.UNION);

        public static IConsumer Goto { get; } = KeywordConsumer.Create(KeywordVal.GOTO);
        public static IParser<Stmt> Continue { get; } = KeywordParser.Create(KeywordVal.CONTINUE, new ContStmt());
        public static IParser<Stmt> Break { get; } = KeywordParser.Create(KeywordVal.BREAK, new BreakStmt());
        public static IConsumer Return { get; } = KeywordConsumer.Create(KeywordVal.RETURN);

        public static IConsumer While { get; } = KeywordConsumer.Create(KeywordVal.WHILE);
        public static IConsumer Do { get; } = KeywordConsumer.Create(KeywordVal.DO);
        public static IConsumer For { get; } = KeywordConsumer.Create(KeywordVal.FOR);

        public static IConsumer If { get; } = KeywordConsumer.Create(KeywordVal.IF);
        public static IConsumer Else { get; } = KeywordConsumer.Create(KeywordVal.ELSE);
        public static IConsumer Switch { get; } = KeywordConsumer.Create(KeywordVal.SWITCH);

        public static IConsumer Case { get; } = KeywordConsumer.Create(KeywordVal.CASE);
        public static IConsumer Default { get; } = KeywordConsumer.Create(KeywordVal.DEFAULT);
    }
}


<|start_filename|>CompilerTests/TypePrinterTests.cs<|end_filename|>
﻿using System;
using System.Collections.Immutable;
using NUnit.Framework;

namespace CompilerTests {
    [TestFixture]
    public static class TypePrinterTests {
        [Test]
        public static void TestVoid() {
            var type = new ABT.VoidType(isConst: true);
            Assert.AreEqual("const void a", type.Decl("a"));
            Assert.AreEqual("const void", type.Decl());
        }

        [Test]
        public static void TestChar() {
            var type = new ABT.CharType();
            Assert.AreEqual("char a", type.Decl("a"));
            Assert.AreEqual("char", type.Decl());
        }

        [Test]
        public static void TestUChar() {
            var type = new ABT.UCharType();
            Assert.AreEqual("unsigned char a", type.Decl("a"));
            Assert.AreEqual("unsigned char", type.Decl());
        }

        [Test]
        public static void TestLong() {
            var type = new ABT.LongType(isConst: true, isVolatile: true);
            Assert.AreEqual("const volatile long a", type.Decl("a"));
            Assert.AreEqual("const volatile long", type.Decl());
        }

        [Test]
        public static void TestULong() {
            var type = new ABT.ULongType(isConst: true);
            Assert.AreEqual("const unsigned long a", type.Decl("a"));
            Assert.AreEqual("const unsigned long", type.Decl());
        }

        [Test]
        public static void TestShort() {
            var type = new ABT.ShortType(isConst: true);
            Assert.AreEqual("const short a", type.Decl("a"));
            Assert.AreEqual("const short", type.Decl());

        }

        [Test]
        public static void TestUShort() {
            var type = new ABT.UShortType(isConst: true);
            Assert.AreEqual("const unsigned short a", type.Decl("a"));
            Assert.AreEqual("const unsigned short", type.Decl());
        }

        [Test]
        public static void TestFloat() {
            var type = new ABT.FloatType(isConst: true);
            Assert.AreEqual("const float a", type.Decl("a"));
            Assert.AreEqual("const float", type.Decl());
        }

        [Test]
        public static void TestDouble() {
            var type = new ABT.DoubleType(isConst: true);
            Assert.AreEqual("const double a", type.Decl("a"));
            Assert.AreEqual("const double", type.Decl());
        }

        [Test]
        public static void TestPointer() {
            var type = new ABT.PointerType(
                new ABT.LongType(isConst: true),
                isConst: true,
                isVolatile: true
            );
            Assert.AreEqual("const long *const volatile a", type.Decl("a"));
            Assert.AreEqual("const long *const volatile", type.Decl());
        }

        [Test]
        public static void TestIncompleteArrayType() {
            var type = new ABT.IncompleteArrayType(
                new ABT.LongType(isConst: true)
            );
            Assert.AreEqual("const long a[]", type.Decl("a"));
            Assert.AreEqual("const long []", type.Decl());

            type = new ABT.IncompleteArrayType(
                new ABT.IncompleteArrayType(
                    new ABT.LongType()
                )
            );
            Assert.AreEqual("long a[][]", type.Decl("a"));
            Assert.AreEqual("long [][]", type.Decl());
        }

        [Test]
        public static void TestArrayType() {
            var type = new ABT.ArrayType(
                new ABT.LongType(isConst: true),
                3
            );
            Assert.AreEqual("const long a[3]", type.Decl("a"));
            Assert.AreEqual("const long [3]", type.Decl());

            var multiDimArrayType = new ABT.ArrayType(
                new ABT.ArrayType(
                    new ABT.LongType(isConst: true),
                    3
                ),
                4
            );
            Assert.AreEqual("const long a[4][3]", multiDimArrayType.Decl("a"));
            Assert.AreEqual("const long [3]", type.Decl());
        }

        [Test]
        public static void TestStructOrUnionType() {
            var type = ABT.StructOrUnionType.CreateIncompleteStruct("my_struct", false, true);
            Assert.AreEqual("volatile struct my_struct a", type.Decl("a"));
        }

        [Test]
        public static void TestFunctionType() {
            var oneArg = ABT.FunctionType.Create(
                ABT.StructOrUnionType.CreateIncompleteStruct("my_struct", false, false),
                ImmutableList.Create(
                    Tuple.Create(Option<String>.None, new ABT.LongType() as ABT.ExprType)
                ),
                false
            );
            Assert.AreEqual("struct my_struct a(long)", oneArg.Decl("a"));
            Assert.AreEqual("struct my_struct (long)", oneArg.Decl());

            var moreArg = ABT.FunctionType.Create(
                ABT.StructOrUnionType.CreateIncompleteStruct("my_struct", false, false),
                ImmutableList.Create(
                    Tuple.Create(Option<String>.None, new ABT.LongType() as ABT.ExprType),
                    Tuple.Create(Option<String>.None, new ABT.FloatType(isConst: true) as ABT.ExprType)
                ),
                false
            );
            Assert.AreEqual("struct my_struct a(long, const float)", moreArg.Decl("a"));
            Assert.AreEqual("struct my_struct (long, const float)", moreArg.Decl());

            var emptyArg = ABT.FunctionType.Create(
                ABT.StructOrUnionType.CreateIncompleteStruct("my_struct", false, false),
                ImmutableList<Tuple<Option<String>, ABT.ExprType>>.Empty,
                false
            );
            Assert.AreEqual("struct my_struct a(void)", emptyArg.Decl("a"));
            Assert.AreEqual("struct my_struct (void)", emptyArg.Decl());

            var emptyVarArg = ABT.FunctionType.Create(
                ABT.StructOrUnionType.CreateIncompleteStruct("my_struct", false, false),
                ImmutableList<Tuple<Option<String>, ABT.ExprType>>.Empty,
                true
            );
            Assert.AreEqual("struct my_struct a(...)", emptyVarArg.Decl("a"));
            Assert.AreEqual("struct my_struct (...)", emptyVarArg.Decl());

            var varArg = ABT.FunctionType.Create(
                ABT.StructOrUnionType.CreateIncompleteStruct("my_struct", false, false),
                ImmutableList.Create(
                    Tuple.Create(Option<String>.None, new ABT.LongType() as ABT.ExprType),
                    Tuple.Create(Option<String>.None, new ABT.FloatType(isConst: true) as ABT.ExprType)
                ),
                true
            );
            Assert.AreEqual("struct my_struct a(long, const float, ...)", varArg.Decl("a"));
            Assert.AreEqual("struct my_struct (long, const float, ...)", varArg.Decl());
        }

        [Test]
        public static void TestFunctionPointer() {
            var funcPtr = new ABT.PointerType(
                ABT.FunctionType.Create(
                    ABT.StructOrUnionType.CreateIncompleteStruct("my_struct", false, false),
                    ImmutableList.Create(
                        Tuple.Create(Option<String>.None, new ABT.LongType() as ABT.ExprType)
                    ),
                    false
                )
            );
            Assert.AreEqual("struct my_struct (*a)(long)", funcPtr.Decl("a"));
            Assert.AreEqual("struct my_struct (*)(long)", funcPtr.Decl());
        }

        [Test]
        public static void TestPointerFunction() {
            var ptrFunc = ABT.FunctionType.Create(
                new ABT.PointerType(
                    new ABT.LongType()
                ),
                ImmutableList.Create(
                    Tuple.Create(Option<String>.None, new ABT.LongType() as ABT.ExprType)
                ),
                false
            );
            Assert.AreEqual("long *a(long)", ptrFunc.Decl("a"));
            Assert.AreEqual("long *(long)", ptrFunc.Decl());
        }

        [Test]
        public static void TestArrayPointer() {
            var arrPtr = new ABT.PointerType(
                new ABT.ArrayType(
                    new ABT.LongType(),
                    3
                )
            );
            Assert.AreEqual("long (*a)[3]", arrPtr.Decl("a"));
            Assert.AreEqual("long (*)[3]", arrPtr.Decl());
        }

        [Test]
        public static void TestPointerArray() {
            var ptrArr = new ABT.ArrayType(
                new ABT.PointerType(
                    new ABT.LongType()
                ),
                3
            );
            Assert.AreEqual("long *a[3]", ptrArr.Decl("a"));
            Assert.AreEqual("long *[3]", ptrArr.Decl());
        }
    }
}



<|start_filename|>Scanner/Identifier.cs<|end_filename|>
﻿using System;

namespace LexicalAnalysis {
    /// <summary>
    /// If the identifier is found to be a keyword, then it will be a keyword
    /// </summary>
    public sealed class TokenIdentifier : Token {
        public TokenIdentifier(String val) {
            this.Val = val;
        }

        public override TokenKind Kind { get; } = TokenKind.IDENTIFIER;
        public String Val { get; }
        public override String ToString() {
            return this.Kind + ": " + this.Val;
        }
    }

    public sealed class FSAIdentifier : FSA {
        private enum State {
            START,
            END,
            ERROR,
            ID
        };
        private State _state;
        private String _scanned;

        public FSAIdentifier() {
            this._state = State.START;
            this._scanned = "";
        }

        public override void Reset() {
            this._state = State.START;
            this._scanned = "";
        }

        public override FSAStatus GetStatus() {
            if (this._state == State.START) {
                return FSAStatus.NONE;
            }
            if (this._state == State.END) {
                return FSAStatus.END;
            }
            if (this._state == State.ERROR) {
                return FSAStatus.ERROR;
            }
            return FSAStatus.RUNNING;
        }

        public override Token RetrieveToken() {
            String name = this._scanned.Substring(0, this._scanned.Length - 1);
            if (TokenKeyword.Keywords.ContainsKey(name)) {
                return new TokenKeyword(TokenKeyword.Keywords[name]);
            }
            return new TokenIdentifier(name);
        }

        public override void ReadChar(Char ch) {
            this._scanned = this._scanned + ch;
            switch (this._state) {
                case State.END:
                case State.ERROR:
                    this._state = State.ERROR;
                    break;
                case State.START:
                    if (ch == '_' || Char.IsLetter(ch)) {
                        this._state = State.ID;
                    } else {
                        this._state = State.ERROR;
                    }
                    break;
                case State.ID:
                    if (Char.IsLetterOrDigit(ch) || ch == '_') {
                        this._state = State.ID;
                    } else {
                        this._state = State.END;
                    }
                    break;
            }
        }

        public override void ReadEOF() {
            this._scanned = this._scanned + '0';
            switch (this._state) {
                case State.ID:
                    this._state = State.END;
                    break;
                default:
                    this._state = State.ERROR;
                    break;
            }
        }
    }
}

<|start_filename|>Scanner/String.cs<|end_filename|>
﻿using System;

namespace LexicalAnalysis {
    /// <summary>
    /// String literal
    /// </summary>
    public sealed class TokenString : Token {
        public TokenString(String val, String raw) {
            if (val == null) {
                throw new ArgumentNullException(nameof(val));
            }
            this.Val = val;
            this.Raw = raw;
        }

        public override TokenKind Kind { get; } = TokenKind.STRING;
        public String Raw { get; }
        public String Val { get; }

        public override String ToString() =>
            $"{this.Kind}: \"{this.Raw}\"\n\"{this.Val}\"";
    }

    public sealed class FSAString : FSA {
        private enum State {
            START,
            END,
            ERROR,
            L,
            Q,
            QQ
        };

        private State _state;
        private readonly FSAChar _fsachar;
        private String _val;
        private String _raw;

        public FSAString() {
            this._state = State.START;
            this._fsachar = new FSAChar('\"');
            this._raw = "";
            this._val = "";
        }

        public override void Reset() {
            this._state = State.START;
            this._fsachar.Reset();
            this._raw = "";
            this._val = "";
        }

        public override FSAStatus GetStatus() {
            if (this._state == State.START) {
                return FSAStatus.NONE;
            }
            if (this._state == State.END) {
                return FSAStatus.END;
            }
            if (this._state == State.ERROR) {
                return FSAStatus.ERROR;
            }
            return FSAStatus.RUNNING;
        }

        public override Token RetrieveToken() {
            return new TokenString(this._val, this._raw);
        }

        public override void ReadChar(Char ch) {
            switch (this._state) {
                case State.END:
                case State.ERROR:
                    this._state = State.ERROR;
                    break;
                case State.START:
                    switch (ch) {
                        case 'L':
                            this._state = State.L;
                            break;
                        case '\"':
                            this._state = State.Q;
                            this._fsachar.Reset();
                            break;
                        default:
                            this._state = State.ERROR;
                            break;
                    }
                    break;
                case State.L:
                    if (ch == '\"') {
                        this._state = State.Q;
                        this._fsachar.Reset();
                    } else {
                        this._state = State.ERROR;
                    }
                    break;
                case State.Q:
                    if (this._fsachar.GetStatus() == FSAStatus.NONE && ch == '\"') {
                        this._state = State.QQ;
                        this._fsachar.Reset();
                    } else {
                        this._fsachar.ReadChar(ch);
                        switch (this._fsachar.GetStatus()) {
                            case FSAStatus.END:
                                this._state = State.Q;
                                this._val = this._val + this._fsachar.RetrieveChar();
                                this._raw = this._raw + this._fsachar.RetrieveRaw();
                                this._fsachar.Reset();
                                ReadChar(ch);
                                break;
                            case FSAStatus.ERROR:
                                this._state = State.ERROR;
                                break;
                            default:
                                break;
                        }
                    }
                    break;
                case State.QQ:
                    this._state = State.END;
                    break;
                default:
                    this._state = State.ERROR;
                    break;
            }
        }

        public override void ReadEOF() {
            if (this._state == State.QQ) {
                this._state = State.END;
            } else {
                this._state = State.ERROR;
            }
        }

    }
}

<|start_filename|>Parser/ParserCombinator.cs<|end_filename|>
﻿using System;
using System.Collections.Immutable;

namespace Parsing {
    public static class ParserCombinator {

        /// <summary>
        /// Parser then Parser = Parser
        /// ( => R1 ) then ( => R2 ) is ( => Tuple[R2, R1] )
        /// </summary>
        public static IParser<Tuple<R2, R1>> Then<R1, R2>(this IParser<R1> firstParser, IParser<R2> secondParser) =>
            new ParserThenParser<R1, R2>(firstParser, secondParser);

        /// <summary>
        /// Parser then Consumer = Parser
        /// ( => R ) then ( => ) is ( => R )
        /// </summary>
        public static IParser<R> Then<R>(this IParser<R> parser, IConsumer consumer) =>
            new ParserThenConsumer<R>(parser, consumer);

        /// <summary>
        /// Parser then Transformer = Parser
        /// ( => R1 ) then ( R1 => R2 ) is ( => R2 )
        /// </summary>
        public static IParser<R2> Then<R1, R2>(this IParser<R1> parser, ITransformer<R1, R2> transformer) =>
            new ParserThenTransformer<R1, R2>(parser, transformer);

        public static IParser<R> Then<S, R>(this IParser<S> parser, Func<S, R> transformer) =>
            parser.Then(new SimpleTransformer<S, R>(transformer));

        public static IParser<R> Then<I1, I2, R>(this IParser<Tuple<I2, I1>> parser, Func<I1, I2, R> transformer) =>
            parser.Then(_ => transformer(_.Item2, _.Item1));

        public static IParser<R> Then<I1, I2, I3, R>(this IParser<Tuple<I3, Tuple<I2, I1>>> parser, Func<I1, I2, I3, R> transformer) =>
            parser.Then(_ => transformer(_.Item2.Item2, _.Item2.Item1, _.Item1));

        public static IParser<R> Then<I1, I2, I3, I4, R>(this IParser<Tuple<I4, Tuple<I3, Tuple<I2, I1>>>> parser, Func<I1, I2, I3, I4, R> transformer) =>
            parser.Then(_ => transformer(_.Item2.Item2.Item2, _.Item2.Item2.Item1, _.Item2.Item1, _.Item1));

        /// <summary>
        /// Consumer then Parser = Parser
        /// ( => ) then ( => R ) is ( => R )
        /// </summary>
        public static IParser<R> Then<R>(this IConsumer consumer, IParser<R> parser) =>
            new ConsumerThenParser<R>(consumer, parser);

        /// <summary>
        /// Consumer then Consumer = Consumer
        /// ( => ) then ( => ) is ( => )
        /// </summary>
        public static IConsumer Then(this IConsumer firstConsumer, IConsumer secondConsumer) =>
            new ConsumerThenConsumer(firstConsumer, secondConsumer);

        /// <summary>
        /// Transformer then Parser = Transformer
        /// ( S => R1 ) then ( => R2 ) is ( S => Tuple[R2, R1] )
        /// </summary>
        public static ITransformer<S, Tuple<R2, R1>> Then<S, R1, R2>(this ITransformer<S, R1> transformer, IParser<R2> parser) =>
            new TransformerThenParser<S, R1, R2>(transformer, parser);

        /// <summary>
        /// Transformer then Consumer = Transformer
        /// ( S => R ) then ( => ) is ( S => R )
        /// </summary>
        public static ITransformer<S, R> Then<S, R>(this ITransformer<S, R> transformer, IConsumer consumer) =>
            new TransformerThenConsumer<S, R>(transformer, consumer);

        /// <summary>
        /// Transformer then Transformer = Transformer
        /// ( S => I ) then ( I => R ) is ( S => R )
        /// </summary>
        public static ITransformer<S, R> Then<S, I, R>(this ITransformer<S, I> firstTransformer, ITransformer<I, R> secondTransformer) =>
            new TransformerThenTransformer<S, I, R>(firstTransformer, secondTransformer);

        public static ITransformer<S, R> Then<S, I, R>(this ITransformer<S, I> firstTransformer, Func<I, R> secondTransformer) =>
            firstTransformer.Then(new SimpleTransformer<I, R>(secondTransformer));

        public static ITransformer<S, R> Then<S, I1, I2, R>(this ITransformer<S, Tuple<I2, I1>> firstTransformer, Func<I1, I2, R> secondTransformer) =>
            firstTransformer.Then(_ => secondTransformer(_.Item2, _.Item1));

        public static ITransformer<S, R> Then<S, I1, I2, I3, R>(this ITransformer<S, Tuple<I3, Tuple<I2, I1>>> firstTransformer, Func<I1, I2, I3, R> secondTransformer) =>
            firstTransformer.Then(_ => secondTransformer(_.Item2.Item2, _.Item2.Item1, _.Item1));

        /// <summary>
        /// Create an optional parser.
        /// </summary>
        public static IParser<Option<R>> Optional<R>(this IParser<R> parser) =>
            new OptionalParser<R>(parser);

        public static IParser<R> Optional<R>(this IParser<R> parser, R defaultValue) =>
            new OptionalParserWithDefault<R>(parser, defaultValue);

        public static OrConsumer Either(IConsumer consumer) =>
            new OrConsumer(ImmutableList.Create(consumer));

        public static OrConsumer Or(ImmutableList<IConsumer> firstConsumer, IConsumer secondConsumer) =>
            new OrConsumer(firstConsumer.Add(secondConsumer));

        /// <summary>
        /// Consumer or Consumer
        /// </summary>
        //public static IConsumer Or(this IConsumer firstConsumer, IConsumer secondConsumer) =>
        //    new ConsumerOrConsumer(firstConsumer, secondConsumer);

        /// <summary>
        /// ( => R ) check Predicate[IParserResult[R]] is ( => R)
        /// </summary>
        public static IParser<R> Check<R>(this IParser<R> parser, Predicate<IParserResult<R>> predicate) =>
            new ParserThenCheck<R>(parser, predicate);

        public static IParser<R> TransformResult<R>(this IParser<R> parser, Func<IParserResult<R>, IParserResult<R>> transformFunc) =>
            Then(parser, new ResultTransformer<R>(transformFunc));

        public static IParser<R> TransformEnvironment<R>(this IParser<R> parser,
            Func<ParserEnvironment, ParserEnvironment> transformFunc) =>
                parser.Then(new EnvironmentTransformer(transformFunc));

        public static IConsumer TransformEnvironment(this IConsumer consumer,
            Func<ParserEnvironment, ParserEnvironment> transformFunc) =>
                consumer.Then(new EnvironmentTransformer(transformFunc));

        public static ITransformer<R, R> Optional<R>(this ITransformer<R, R> transformer) =>
            new OptionalTransformer<R>(transformer);

        public static OrTransformer<S, R> Either<S, R>(ITransformer<S, R> transformer) =>
            new OrTransformer<S, R>(ImmutableList.Create(transformer));

        public static OrTransformer<S, R> Or<S, R>(this OrTransformer<S, R> firstTransformer, ITransformer<S, R> secondTransformer) =>
            new OrTransformer<S, R>(firstTransformer.Transformers.Add(secondTransformer));

        //public static ITransformer<S, R> Or<S, R>(this ITransformer<S, R> firstTransformer, ITransformer<S, R> secondTransformer) =>
        //    new TransformerOrTransformer<S, R>(firstTransformer, secondTransformer);

        //public static IParser<R> Or<R>(this IParser<R> firstParser, IParser<R> secondParser) =>
        //    new ParserOrParser<R>(firstParser, secondParser);

        public static OrParser<R> Either<R>(IParser<R> parser) =>
            new OrParser<R>(ImmutableList.Create(parser));

        public static OrParser<R> Or<R>(this OrParser<R> firstParser, IParser<R> secondParser) =>
            new OrParser<R>(firstParser.Parsers.Add(secondParser));

        public static IParser<Boolean> Optional(this IConsumer consumer) =>
            new OptionalConsumer(consumer);
        
        public static ITransformer<R, R> ZeroOrMore<R>(this ITransformer<R, R> transformer) =>
            new ZeroOrMoreTransformer<R>(transformer);

        public static ITransformer<R, R> OneOrMore<R>(this ITransformer<R, R> transformer) =>
            new OneOrMoreTransformer<R>(transformer);

        public static IParser<ImmutableList<R>> ZeroOrMore<R>(this IParser<R> parser) =>
            new ZeroOrMoreParser<R>(parser);

        public static IParser<ImmutableList<R>> OneOrMore<R>(this IParser<R> parser) =>
            new OneOrMoreParser<R>(parser);

        public static IParser<ImmutableList<R>> OneOrMore<R>(this IParser<R> elementParser, IConsumer separatorConsumer) =>
            new OneOrMoreParserWithSeparator<R>(separatorConsumer, elementParser);

        public static ITransformer<R, R> Given<R>() =>
            new IdentityTransformer<R>();

        public static ITransformer<Tuple<R2, R1>, Tuple<R2, R1>> Given<R1, R2>() =>
            Given<Tuple<R2, R1>>();
    }
}


<|start_filename|>Driver/Option.cs<|end_filename|>
﻿using System;

public abstract class Option<T> {
    public Option<O> Map<O>(Converter<T, O> converter) {
        if (this.IsSome) {
            return new Some<O>(converter(this.Value));
        }
        return new None<O>();
    }

    public abstract T Value { get; }
    public abstract Boolean IsSome { get; }
    public abstract Boolean IsNone { get; }

    public static Option<T> None { get; } = new None<T>();
}

public static class Option {
    public static Option<T> Some<T>(T value) => new Some<T>(value);
}

public sealed class None<T> : Option<T> {
    public override T Value {
        get {
            throw new NotSupportedException("No Value in None.");
        }
    }
    public override Boolean IsSome => false;
    public override Boolean IsNone => true;
}

public sealed class Some<T> : Option<T> {
    public Some(T value) {
        if (value == null) {
            throw new ArgumentNullException(nameof(value), "The Value in Some cannot be null.");
        }
        this.Value = value;
    }
    public override T Value { get; }
    public override Boolean IsSome => true;
    public override Boolean IsNone => false;
}


<|start_filename|>Driver/Checked.cs<|end_filename|>
﻿using System;

public class Checked : Attribute {
}



<|start_filename|>Scanner/Int.cs<|end_filename|>
﻿using System;

namespace LexicalAnalysis {
    /// <summary>
    /// There are four types of integers: signed, unsigned, signed long, unsigned long
    /// </summary>
    public sealed class TokenInt : Token {
        public enum IntSuffix {
            NONE,
            U,
            L,
            UL
        };

        public TokenInt(Int64 val, IntSuffix suffix, String raw) {
            this.Val = val;
            this.Suffix = suffix;
            this.Raw = raw;
        }

        public override TokenKind Kind { get; } = TokenKind.INT;

        public override String ToString() {
            String str = this.Kind.ToString();
            switch (this.Suffix) {
                case IntSuffix.L:
                    str += "(long)";
                    break;
                case IntSuffix.U:
                    str += "(unsigned)";
                    break;
                case IntSuffix.UL:
                    str += "(unsigned long)";
                    break;
                default:
                    break;
            }
            return str + ": " + this.Val + " \"" + this.Raw + "\"";
        }

        public readonly Int64 Val;
        public readonly String Raw;
        public readonly IntSuffix Suffix;
    }

    public sealed class FSAInt : FSA {
        private enum State {
            START,
            END,
            ERROR,
            Z,
            O,
            D,
            ZX,
            H,
            L,
            U,
            UL
        };

        private Int64 _val;
        private String _raw;
        private TokenInt.IntSuffix _suffix;
        private State _state;

        public FSAInt() {
            this._state = State.START;
            this._val = 0;
            this._raw = "";
            this._suffix = TokenInt.IntSuffix.NONE;
        }

        public override void Reset() {
            this._state = State.START;
            this._val = 0;
            this._raw = "";
            this._suffix = TokenInt.IntSuffix.NONE;
        }

        public override FSAStatus GetStatus() {
            switch (this._state) {
                case State.START:
                    return FSAStatus.NONE;
                case State.END:
                    return FSAStatus.END;
                case State.ERROR:
                    return FSAStatus.ERROR;
                default:
                    return FSAStatus.RUNNING;
            }
        }

        public override Token RetrieveToken() {
            return new TokenInt(this._val, this._suffix, this._raw.Substring(0, this._raw.Length - 1));
        }

        public override void ReadChar(Char ch) {
            this._raw += ch;
            switch (this._state) {
                case State.ERROR:
                case State.END:
                    this._state = State.ERROR;
                    break;
                case State.START:
                    if (ch == '0') {
                        this._state = State.Z;
                    } else if (Char.IsDigit(ch)) {
                        this._state = State.D;
                        this._val += ch - '0';
                    } else {
                        this._state = State.ERROR;
                    }
                    break;
                case State.Z:
                    if (ch == 'x' || ch == 'X') {
                        this._state = State.ZX;
                    } else if (Utils.IsOctDigit(ch)) {
                        this._val *= 8;
                        this._val += ch - '0';
                        this._state = State.O;
                    } else if (ch == 'u' || ch == 'U') {
                        this._suffix = TokenInt.IntSuffix.U;
                        this._state = State.U;
                    } else if (ch == 'l' || ch == 'L') {
                        this._suffix = TokenInt.IntSuffix.L;
                        this._state = State.L;
                    } else {
                        this._state = State.END;
                    }
                    break;
                case State.D:
                    if (Char.IsDigit(ch)) {
                        this._val *= 10;
                        this._val += ch - '0';
                        this._state = State.D;
                    } else if (ch == 'u' || ch == 'U') {
                        this._suffix = TokenInt.IntSuffix.U;
                        this._state = State.U;
                    } else if (ch == 'l' || ch == 'L') {
                        this._suffix = TokenInt.IntSuffix.L;
                        this._state = State.L;
                    } else {
                        this._state = State.END;
                    }
                    break;
                case State.ZX:
                    if (Utils.IsHexDigit(ch)) {
                        this._val *= 0x10;
                        this._val += Utils.GetHexDigit(ch);
                        this._state = State.H;
                    } else {
                        this._state = State.ERROR;
                    }
                    break;
                case State.O:
                    if (Utils.IsOctDigit(ch)) {
                        this._val *= 8;
                        this._val += ch - '0';
                        this._state = State.O;
                    } else if (ch == 'u' || ch == 'U') {
                        this._suffix = TokenInt.IntSuffix.U;
                        this._state = State.U;
                    } else if (ch == 'l' || ch == 'L') {
                        this._suffix = TokenInt.IntSuffix.L;
                        this._state = State.L;
                    } else {
                        this._state = State.END;
                    }
                    break;
                case State.L:
                    if (ch == 'u' || ch == 'U') {
                        this._suffix = TokenInt.IntSuffix.UL;
                        this._state = State.UL;
                    } else {
                        this._state = State.END;
                    }
                    break;
                case State.H:
                    if (Utils.IsHexDigit(ch)) {
                        this._val *= 0x10;
                        this._val += Utils.GetHexDigit(ch);
                        this._state = State.H;
                    } else if (ch == 'u' || ch == 'U') {
                        this._suffix = TokenInt.IntSuffix.U;
                        this._state = State.U;
                    } else if (ch == 'l' || ch == 'L') {
                        this._suffix = TokenInt.IntSuffix.L;
                        this._state = State.L;
                    } else {
                        this._state = State.END;
                    }
                    break;
                case State.U:
                    if (ch == 'l' || ch == 'L') {
                        this._suffix = TokenInt.IntSuffix.UL;
                        this._state = State.UL;
                    } else {
                        this._state = State.END;
                    }
                    break;
                case State.UL:
                    this._state = State.END;
                    break;
                default:
                    this._state = State.ERROR;
                    break;
            }
        }

        public override void ReadEOF() {
            switch (this._state) {
                case State.D:
                case State.Z:
                case State.O:
                case State.L:
                case State.H:
                case State.U:
                case State.UL:
                    this._state = State.END;
                    break;
                default:
                    this._state = State.ERROR;
                    break;
            }
        }

    }
}

<|start_filename|>TestPrograms/FunctionPointer.c<|end_filename|>
int awesome_add(int a, int b) {
    return a + b;
}

int awesome_cmp(void *a, void *b) {
    return *(int *)a - *(int *)b;
}

void qsort(void *base, unsigned int nitems, unsigned int size, int (*compar)(const void *, const void*));
int printf(char *, ...);

int main() {
    int (*fp)(int, int);
	int arr[] = { 1, 6, 3, 8, 4, 5, 0, 2 };
	int i;

	fp = &awesome_add;
	fp = awesome_add;
	fp(1, 2);
	(*fp)(1, 2);

	qsort(arr, sizeof(arr) / sizeof(int), sizeof(int), &awesome_cmp);

	for (i = 0; i < sizeof(arr) / sizeof(int); ++i) {
		printf("%d\t", arr[i]);
	}

	return 0;
}


<|start_filename|>Driver/SetOnce.cs<|end_filename|>
﻿using System;

public class SetOnce<T> {
    public SetOnce() {
        this.IsSet = false;
        this._value = default(T);
    }

    public Boolean IsSet { get; private set; }
    private T _value;

    public T Value {
        get {
            if (!this.IsSet) {
                throw new InvalidOperationException("Value hasn't been set.");
            }
            return this._value;
        }
        set {
            if (this.IsSet) {
                throw new InvalidOperationException("Value cannot be set twice.");
            }
            this._value = value;
            this.IsSet = true;
        }
    }

}



<|start_filename|>Parser/ParserClasses.cs<|end_filename|>
﻿using System;
using System.Collections.Immutable;
using System.Linq;
using LexicalAnalysis;

namespace Parsing {
    public class ParserThenParser<R1, R2> : IParser<Tuple<R2, R1>> {
        public ParserThenParser(IParser<R1> firstParser, IParser<R2> secondParser) {
            this.FirstParser = firstParser;
            this.SecondParser = secondParser;
        }

        public IParser<R1> FirstParser { get; }
        public IParser<R2> SecondParser { get; }
        public RuleCombining Combining => RuleCombining.THEN;

        public IParserResult<Tuple<R2, R1>> Parse(ParserInput input) {
            var firstResult = this.FirstParser.Parse(input);
            if (!firstResult.IsSuccessful) {
                return new ParserFailed<Tuple<R2, R1>>();
            }
            var secondResult = this.SecondParser.Parse(firstResult.ToInput());
            if (!secondResult.IsSuccessful) {
                return new ParserFailed<Tuple<R2, R1>>();
            }
            return ParserSucceeded.Create(Tuple.Create(secondResult.Result, firstResult.Result), secondResult.Environment, secondResult.Source);
        }
    }

    public class ParserThenConsumer<R> : IParser<R> {
        public ParserThenConsumer(IParser<R> parser, IConsumer consumer) {
            this.Parser = parser;
            this.Consumer = consumer;
        }

        public IParser<R> Parser { get; }
        public IConsumer Consumer { get; }
        public RuleCombining Combining => RuleCombining.THEN;

        public IParserResult<R> Parse(ParserInput input) {
            var firstResult = this.Parser.Parse(input);
            if (!firstResult.IsSuccessful) {
                return new ParserFailed<R>();
            }
            var secondResult = this.Consumer.Consume(firstResult.ToInput());
            if (!secondResult.IsSuccessful) {
                return new ParserFailed<R>();
            }
            return ParserSucceeded.Create(firstResult.Result, secondResult.Environment, secondResult.Source);
        }
    }

    public class ParserThenTransformer<R1, R2> : IParser<R2> {
        public ParserThenTransformer(IParser<R1> parser, ITransformer<R1, R2> transformer) {
            this.Parser = parser;
            this.Transformer = transformer;
        }

        public IParser<R1> Parser { get; }
        public ITransformer<R1, R2> Transformer { get; }
        public RuleCombining Combining => RuleCombining.THEN;

        public IParserResult<R2> Parse(ParserInput input) {
            var firstResult = this.Parser.Parse(input);
            if (!firstResult.IsSuccessful) {
                return new ParserFailed<R2>();
            }
            return this.Transformer.Transform(firstResult.Result, firstResult.ToInput());
        }
    }

    public class ConsumerThenParser<R> : IParser<R> {
        public ConsumerThenParser(IConsumer consumer, IParser<R> parser) {
            this.Consumer = consumer;
            this.Parser = parser;
        }

        public IConsumer Consumer { get; }
        public IParser<R> Parser { get; }
        public RuleCombining Combining => RuleCombining.THEN;

        public IParserResult<R> Parse(ParserInput input) {
            var firstResult = this.Consumer.Consume(input);
            if (!firstResult.IsSuccessful) {
                return new ParserFailed<R>();
            }
            return this.Parser.Parse(firstResult.ToInput());
        }
    }

    public class ConsumerThenConsumer : IConsumer {
        public ConsumerThenConsumer(IConsumer firstConsumer, IConsumer secondConsumer) {
            this.FirstConsumer = firstConsumer;
            this.SecondConsumer = secondConsumer;
        }

        public IConsumer FirstConsumer { get; }
        public IConsumer SecondConsumer { get; }

        public IParserResult Consume(ParserInput input) {
            var result1 = this.FirstConsumer.Consume(input);
            if (!result1.IsSuccessful) {
                return result1;
            }
            return this.SecondConsumer.Consume(result1.ToInput());
        }
    }

    public class TransformerThenParser<S, R1, R2> : ITransformer<S, Tuple<R2, R1>> {
        public TransformerThenParser(ITransformer<S, R1> transformer, IParser<R2> parser) {
            this.Transformer = transformer;
            this.Parser = parser;
        }

        public ITransformer<S, R1> Transformer { get; }
        public IParser<R2> Parser { get; }

        public IParserResult<Tuple<R2, R1>> Transform(S seed, ParserInput input) {
            var result1 = this.Transformer.Transform(seed, input);
            if (!result1.IsSuccessful) {
                return new ParserFailed<Tuple<R2, R1>>();
            }
            var result2 = this.Parser.Parse(result1.ToInput());
            if (!result2.IsSuccessful) {
                return new ParserFailed<Tuple<R2, R1>>();
            }
            return ParserSucceeded.Create(Tuple.Create(result2.Result, result1.Result), result2.Environment, result2.Source);
        }
    }

    public class TransformerThenConsumer<S, R> : ITransformer<S, R> {
        public TransformerThenConsumer(ITransformer<S, R> transformer, IConsumer consumer) {
            this.Transformer = transformer;
            this.Consumer = consumer;
        }

        public ITransformer<S, R> Transformer { get; }
        public IConsumer Consumer { get; }

        public IParserResult<R> Transform(S seed, ParserInput input) {
            var result1 = this.Transformer.Transform(seed, input);
            if (!result1.IsSuccessful) {
                return result1;
            }
            var result2 = this.Consumer.Consume(result1.ToInput());
            if (!result2.IsSuccessful) {
                return new ParserFailed<R>();
            }
            return ParserSucceeded.Create(result1.Result, result2.Environment, result2.Source);
        }
    }

    public class TransformerThenTransformer<S, I, R> : ITransformer<S, R> {
        public TransformerThenTransformer(ITransformer<S, I> firstTransformer, ITransformer<I, R> secondTransformer) {
            this.FirstTransformer = firstTransformer;
            this.SecondTransformer = secondTransformer;
        }

        public ITransformer<S, I> FirstTransformer { get; }
        public ITransformer<I, R> SecondTransformer { get; }

        public IParserResult<R> Transform(S seed, ParserInput input) {
            var result1 = this.FirstTransformer.Transform(seed, input);
            if (!result1.IsSuccessful) {
                return new ParserFailed<R>();
            }
            return this.SecondTransformer.Transform(result1.Result, result1.ToInput());
        }
    }

    public enum RuleCombining {
        NONE,
        THEN,
        OR
    }

    /// <summary>
    /// A parser consumes one or several tokens, and produces a result.
    /// </summary>
    public interface IParser<out R> {
        IParserResult<R> Parse(ParserInput input);
        RuleCombining Combining { get; }
    }

    public static class Parser {
        public static NamedParser<R> Create<R>(String name) =>
            new NamedParser<R>(name);
        public static IParser<R> Seed<R>(R seed) =>
            new AlwaysSucceedingParser<R>(seed);
    }

    public class NamedParser<R> : IParser<R> {
        public NamedParser(String name) {
            this.Name = name;
            this.Parser = new SetOnce<IParser<R>>();
        }

        public SetOnce<IParser<R>> Parser { get; }

        public void Is(IParser<R> parser) {
            this.Parser.Value = parser;
        }

        public IParserResult<R> Parse(ParserInput input) =>
            this.Parser.Value.Parse(input);

        public String Name { get; }
        public String Rule => this.Parser.Value.ToString();
        public RuleCombining Combining => RuleCombining.NONE;
        public override String ToString() => this.Name;
    }

    public class OptionalParser<R> : IParser<Option<R>> {
        public OptionalParser(IParser<R> parser) {
            this.Parser = parser;
        }
        public IParser<R> Parser { get; }
        public RuleCombining Combining => RuleCombining.NONE;
        public IParserResult<Option<R>> Parse(ParserInput input) {
            var result = this.Parser.Parse(input);
            if (result.IsSuccessful) {
                return ParserSucceeded.Create(new Some<R>(result.Result), result.Environment, result.Source);
            }
            return ParserSucceeded.Create(new None<R>(), input.Environment, input.Source);
        }
    }

    public class OptionalParserWithDefault<R> : IParser<R> {
        public OptionalParserWithDefault(IParser<R> parser, R defaultValue) {
            this.Parser = parser;
            this.DefaultValue = defaultValue;
        }
        public IParser<R> Parser { get; }
        public R DefaultValue { get; }
        public RuleCombining Combining => RuleCombining.NONE;
        public IParserResult<R> Parse(ParserInput input) {
            var result = this.Parser.Parse(input);
            if (result.IsSuccessful) {
                return result;
            }
            return ParserSucceeded.Create(this.DefaultValue, input.Environment, input.Source);
        }
    }

    public class AlwaysSucceedingParser<R> : IParser<R> {
        public AlwaysSucceedingParser(R result) {
            this.Result = result;
        }
        public RuleCombining Combining => RuleCombining.NONE;
        public R Result { get; }
        public IParserResult<R> Parse(ParserInput input) =>
            ParserSucceeded.Create(this.Result, input.Environment, input.Source);
    }

    public class ParserThenCheck<R> : IParser<R> {
        public ParserThenCheck(IParser<R> parser, Predicate<IParserResult<R>> predicate) {
            this.Parser = parser;
            this.Predicate = predicate;
        }
        public IParser<R> Parser { get; }
        public RuleCombining Combining => RuleCombining.NONE;
        public Predicate<IParserResult<R>> Predicate { get; }
        public IParserResult<R> Parse(ParserInput input) {
            var result1 = this.Parser.Parse(input);
            if (result1.IsSuccessful && !this.Predicate(result1)) {
                return new ParserFailed<R>();
            }
            return result1;
        }
    }

    public class OrParser<R> : IParser<R> {
        public OrParser(ImmutableList<IParser<R>> parsers) {
            this.Parsers = parsers;
        }
        public ImmutableList<IParser<R>> Parsers { get; }
        public RuleCombining Combining => RuleCombining.OR;
        public IParserResult<R> Parse(ParserInput input) {
            foreach (var parser in this.Parsers) {
                var result = parser.Parse(input);
                if (result.IsSuccessful) {
                    return result;
                }
            }
            return new ParserFailed<R>();
        }
    }

    //public class ParserOrParser<R> : IParser<R> {
    //    public ParserOrParser(IParser<R> firstParser, IParser<R> secondParser) {
    //        this.FirstParser = firstParser;
    //        this.SecondParser = secondParser;
    //    }
    //    public IParser<R> FirstParser { get; }
    //    public IParser<R> SecondParser { get; }
    //    public RuleCombining Combining => RuleCombining.OR;
    //    public IParserResult<R> Parse(ParserInput input) {
    //        var result1 = this.FirstParser.Parse(input);
    //        if (result1.IsSuccessful) {
    //            return result1;
    //        }
    //        return this.SecondParser.Parse(input);
    //    }
    //}

    public class ZeroOrMoreParser<R> : IParser<ImmutableList<R>> {
        public ZeroOrMoreParser(IParser<R> parser) {
            this.Parser = parser;
        }

        public RuleCombining Combining => RuleCombining.NONE;

        public IParser<R> Parser { get; }

        public IParserResult<ImmutableList<R>> Parse(ParserInput input) {
            var list = ImmutableList<R>.Empty;
            IParserResult<R> curResult;
            while ((curResult = this.Parser.Parse(input)).IsSuccessful) {
                list = list.Add(curResult.Result);
                input = curResult.ToInput();
            }
            return ParserSucceeded.Create(list, input.Environment, input.Source);
        }
    }

    public class OneOrMoreParser<R> : IParser<ImmutableList<R>> {
        public OneOrMoreParser(IParser<R> parser) {
            this.Parser = parser;
        }

        public RuleCombining Combining => RuleCombining.NONE;
        public IParser<R> Parser { get; }

        public IParserResult<ImmutableList<R>> Parse(ParserInput input) {
            var list = ImmutableList<R>.Empty;
            var curResult = this.Parser.Parse(input);
            if (!curResult.IsSuccessful) {
                return new ParserFailed<ImmutableList<R>>();
            }

            IParserResult<R> lastSuccessfulResult;
            do {
                list = list.Add(curResult.Result);
                lastSuccessfulResult = curResult;
                curResult = this.Parser.Parse(lastSuccessfulResult.ToInput());
            } while (curResult.IsSuccessful);

            return ParserSucceeded.Create(list, lastSuccessfulResult.Environment, lastSuccessfulResult.Source);
        }
    }

    public class OneOrMoreParserWithSeparator<R> : IParser<ImmutableList<R>> {
        public OneOrMoreParserWithSeparator(IConsumer separatorConsumer, IParser<R> elementParser) {
            this.SeparatorConsumer = separatorConsumer;
            this.ElementParser = elementParser;
        }
        public RuleCombining Combining => RuleCombining.NONE;
        public IConsumer SeparatorConsumer { get; }
        public IParser<R> ElementParser { get; }

        public IParserResult<ImmutableList<R>> Parse(ParserInput input) {
            var list = ImmutableList<R>.Empty;
            var curResult = this.ElementParser.Parse(input);
            if (!curResult.IsSuccessful) {
                return new ParserFailed<ImmutableList<R>>();
            }
            IParserResult<R> lastElementResult;

            do {
                list = list.Add(curResult.Result);
                lastElementResult = curResult;

                var separatorResult = this.SeparatorConsumer.Consume(curResult.ToInput());
                if (!separatorResult.IsSuccessful) {
                    break;
                }
                curResult = this.ElementParser.Parse(separatorResult.ToInput());
            } while (curResult.IsSuccessful);

            return ParserSucceeded.Create(list, lastElementResult.Environment, lastElementResult.Source);
        }
    }

    /// <summary>
    /// A consumer consumes one or several tokens, and doesn't produce any result.
    /// </summary>
    public interface IConsumer {
        IParserResult Consume(ParserInput input);
    }

    public class NamedConsumer : IConsumer {
        public NamedConsumer() {
            this.Consumer = new SetOnce<IConsumer>();
        }

        public SetOnce<IConsumer> Consumer { get; }

        public void Is(IConsumer consumer) {
            this.Consumer.Value = consumer;
        }

        public IParserResult Consume(ParserInput input) =>
            this.Consumer.Value.Consume(input);

        public override String ToString() {
            if (this.Consumer.IsSet) {
                return this.Consumer.Value.ToString();
            }
            return "<Unset Consumer>";
        }
    }

    public class OptionalConsumer : IParser<Boolean> {
        public OptionalConsumer(IConsumer consumer) {
            this.Consumer = consumer;
        }

        public RuleCombining Combining => RuleCombining.NONE;

        public IConsumer Consumer { get; }

        public IParserResult<Boolean> Parse(ParserInput input) {
            var result = this.Consumer.Consume(input);
            if (result.IsSuccessful) {
                return ParserSucceeded.Create(true, result.Environment, result.Source);
            }
            return ParserSucceeded.Create(false, input.Environment, input.Source);
        }
    }

    public class OrConsumer : IConsumer {
        public OrConsumer(ImmutableList<IConsumer> consumers) {
            this.Consumers = consumers;
        }

        public ImmutableList<IConsumer> Consumers { get; }

        public IParserResult Consume(ParserInput input) {
            foreach (var consumer in this.Consumers) {
                var result = consumer.Consume(input);
                if (result.IsSuccessful) {
                    return result;
                }
            }
            return new ParserFailed();
        }
    }

    //public class ConsumerOrConsumer : IConsumer {
    //    public ConsumerOrConsumer(IConsumer firstConsumer, IConsumer secondConsumer) {
    //        this.FirstConsumer = firstConsumer;
    //        this.SecondConsumer = secondConsumer;
    //    }

    //    public IConsumer FirstConsumer { get; }
    //    public IConsumer SecondConsumer { get; }

    //    public IParserResult Consume(ParserInput input) {
    //        var result1 = this.FirstConsumer.Consume(input);
    //        if (!result1.IsSuccessful) {
    //            return new ParserFailed();
    //        }
    //        return this.SecondConsumer.Consume(result1.ToInput());
    //    }
    //}

    public class EnvironmentTransformer : IConsumer {
        public EnvironmentTransformer(Func<ParserEnvironment, ParserEnvironment> transformer) {
            this.Transformer = transformer;
        }

        public Func<ParserEnvironment, ParserEnvironment> Transformer { get; }

        public IParserResult Consume(ParserInput input) {
            return ParserSucceeded.Create(this.Transformer(input.Environment), input.Source);
        }
    }

    /// <summary>
    /// A transformer consumes zero or more tokens, and takes a previous result to produce a new result.
    /// </summary>
    public interface ITransformer<in S, out R> {
        IParserResult<R> Transform(S seed, ParserInput input);
    }

    public class IdentityTransformer<R> : ITransformer<R, R> {
        public IParserResult<R> Transform(R seed, ParserInput input) =>
            ParserSucceeded.Create(seed, input.Environment, input.Source);
    }

    public class SimpleTransformer<S, R> : ITransformer<S, R> {
        public SimpleTransformer(Func<S, R> transformFunc) {
            this.TransformFunc = transformFunc;
        }
        public Func<S, R> TransformFunc { get; }
        public IParserResult<R> Transform(S seed, ParserInput input) =>
            ParserSucceeded.Create(this.TransformFunc(seed), input.Environment, input.Source);
    }

    public class NamedTransformer<S, R> : ITransformer<S, R> {
        public NamedTransformer() {
            this.Transformer = new SetOnce<ITransformer<S, R>>();
        }

        public SetOnce<ITransformer<S, R>> Transformer { get; }

        public void Is(ITransformer<S, R> transformer) {
            this.Transformer.Value = transformer;
        }

        public IParserResult<R> Transform(S seed, ParserInput input) =>
            this.Transformer.Value.Transform(seed, input);

        public override String ToString() {
            if (this.Transformer.IsSet) {
                return this.Transformer.Value.ToString();
            }
            return "<Unset transformer>";
        }
    }

    public class OptionalTransformer<R> : ITransformer<R, R> {
        public OptionalTransformer(ITransformer<R, R> transformer) {
            this.Transformer = transformer;
        }
        public ITransformer<R, R> Transformer { get; }
        public IParserResult<R> Transform(R seed, ParserInput input) {
            var result = this.Transformer.Transform(seed, input);
            if (result.IsSuccessful) {
                return result;
            }
            return ParserSucceeded.Create(seed, input.Environment, input.Source);
        }
    }

    public class OrTransformer<S, R> : ITransformer<S, R> {
        public OrTransformer(ImmutableList<ITransformer<S, R>> transformers) {
            this.Transformers = transformers;
        }

        public ImmutableList<ITransformer<S, R>> Transformers { get; }

        public IParserResult<R> Transform(S seed, ParserInput input) {
            foreach (var transformer in this.Transformers) {
                var result = transformer.Transform(seed, input);
                if (result.IsSuccessful) {
                    return result;
                }
            }
            return new ParserFailed<R>();
        }
    }

    //public class TransformerOrTransformer<S, R> : ITransformer<S, R> {
    //    public TransformerOrTransformer(ITransformer<S, R> firstTransformer, ITransformer<S, R> secondTransformer) {
    //        this.FirstTransformer = firstTransformer;
    //        this.SecondTransformer = secondTransformer;
    //    }

    //    public ITransformer<S, R> FirstTransformer { get; }
    //    public ITransformer<S, R> SecondTransformer { get; }

    //    public IParserResult<R> Transform(S seed, ParserInput input) {
    //        var result1 = this.FirstTransformer.Transform(seed, input);
    //        if (result1.IsSuccessful) {
    //            return result1;
    //        }
    //        return this.SecondTransformer.Transform(seed, input);

    //    }

    //    public override String ToString() {
    //        return this.FirstTransformer + " | " + this.SecondTransformer;
    //    }
    //}

    public class ResultTransformer<R> : ITransformer<R, R> {
        public ResultTransformer(Func<IParserResult<R>, IParserResult<R>> transformFunc) {
            this.TransformFunc = transformFunc;
        }

        public Func<IParserResult<R>, IParserResult<R>> TransformFunc { get; }

        public IParserResult<R> Transform(R seed, ParserInput input) =>
            this.TransformFunc(ParserSucceeded.Create(seed, input.Environment, input.Source));
    }

    public class ZeroOrMoreTransformer<R> : ITransformer<R, R> {
        public ZeroOrMoreTransformer(ITransformer<R, R> transformer) {
            this.Transformer = transformer;
        }
        public ITransformer<R, R> Transformer { get; }
        public IParserResult<R> Transform(R seed, ParserInput input) {
            IParserResult<R> curResult = ParserSucceeded.Create(seed, input.Environment, input.Source);

            IParserResult<R> lastSuccessfulResult;
            do {
                lastSuccessfulResult = curResult;
                curResult = this.Transformer.Transform(lastSuccessfulResult.Result, lastSuccessfulResult.ToInput());
            } while (curResult.IsSuccessful);

            return lastSuccessfulResult;
        }
    }

    public class OneOrMoreTransformer<R> : ITransformer<R, R> {
        public OneOrMoreTransformer(ITransformer<R, R> transformer) {
            this.Transformer = transformer;
        }
        public ITransformer<R, R> Transformer { get; }
        public IParserResult<R> Transform(R seed, ParserInput input) {
            var curResult = this.Transformer.Transform(seed, input);
            if (!curResult.IsSuccessful) {
                return new ParserFailed<R>();
            }

            IParserResult<R> lastSuccessfulResult;
            do {
                lastSuccessfulResult = curResult;
                curResult = this.Transformer.Transform(lastSuccessfulResult.Result, lastSuccessfulResult.ToInput());
            } while (curResult.IsSuccessful);

            return lastSuccessfulResult;
        }
    }

    public class OperatorConsumer : IConsumer {
        public OperatorConsumer(OperatorVal operatorVal) {
            this.OperatorVal = operatorVal;
        }

        public static IConsumer Create(OperatorVal operatorVal) =>
            new OperatorConsumer(operatorVal);

        public OperatorVal OperatorVal { get; }

        public IParserResult Consume(ParserInput input) {
            if ((input.Source.First() as TokenOperator)?.Val == this.OperatorVal) {
                return ParserSucceeded.Create(input.Environment, input.Source.Skip(1));
            }
            return new ParserFailed();
        }
    }

    public class IdentifierParser : IParser<String> {
        public RuleCombining Combining => RuleCombining.NONE;
        public IParserResult<String> Parse(ParserInput input) {
            var token = input.Source.First() as TokenIdentifier;
            if (token == null) {
                return new ParserFailed<String>();
            }
            return ParserSucceeded.Create(token.Val, input.Environment, input.Source.Skip(1));
        }
    }

    public class KeywordConsumer : IConsumer {
        public KeywordConsumer(KeywordVal keywordVal) {
            this.KeywordVal = keywordVal;
        }
        public KeywordVal KeywordVal { get; }
        public static KeywordConsumer Create(KeywordVal keywordVal) =>
            new KeywordConsumer(keywordVal);
        public IParserResult Consume(ParserInput input) {
            if ((input.Source.First() as TokenKeyword)?.Val == this.KeywordVal) {
                return ParserSucceeded.Create(input.Environment, input.Source.Skip(1));
            }
            return new ParserFailed();
        }
    }

    public class KeywordParser<R> : IParser<R> {
        public KeywordParser(KeywordVal keywordVal, R result) {
            this.KeywordVal = keywordVal;
            this.Result = result;
        }

        public RuleCombining Combining => RuleCombining.NONE;

        public KeywordVal KeywordVal { get; }
        public R Result { get; }

        public IParserResult<R> Parse(ParserInput input) {
            if ((input.Source.First() as TokenKeyword)?.Val == this.KeywordVal) {
                return ParserSucceeded.Create(this.Result, input.Environment, input.Source.Skip(1));
            }
            return new ParserFailed<R>();
        }
    }

    public class KeywordParser {
        public static KeywordParser<R> Create<R>(KeywordVal keywordVal, R result) =>
            new KeywordParser<R>(keywordVal, result);
    }

}

<|start_filename|>ABT/Environment.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;

namespace ABT {
    public class Env {

        // enum EntryLoc
        // =============
        // the location of an object
        //   STACK: this is a variable stored in the stack
        //   FRAME: this is a function parameter
        //   GLOBAL: this is a global symbol
        // 
        public enum EntryKind {
            ENUM,
            TYPEDEF,
            STACK,
            FRAME,
            GLOBAL
        }
        
        
        // class Entry
        // ===========
        // the return Value when searching for a symbol in the environment
        // attributes:
        //   entry_loc: the location of this object
        //   entry_type: the Type of the object
        //   entry_offset: this is used to determine the address of the object
        //              STACK: addr = %ebp - offset
        //              GLOBAL: N/A
        // 
        public class Entry {
            public Entry(EntryKind kind, ExprType type, Int32 offset) {
                this.Kind = kind;
                this.Type = type;
                this.Offset = offset;
            }
            public readonly EntryKind Kind;
            public readonly ExprType  Type;
            public readonly Int32     Offset;
        }

        private class Scope {

            // private constructor
            // ===================
            // 
            private Scope(List<Utils.StoreEntry> stack_entries,
                          Int32 stack_offset,
                          List<Utils.StoreEntry> global_entries,
                          FunctionType curr_func,
                          List<Utils.StoreEntry> typedef_entries,
                          List<Utils.StoreEntry> enum_entries) {
                this.locals = stack_entries;
                this.esp_pos = stack_offset;
                this.globals = global_entries;
                this.func = curr_func;
                this.typedefs = typedef_entries;
                this.enums = enum_entries;
            }

            // copy constructor
            // ================
            // 
            private Scope(Scope other)
                : this(new List<Utils.StoreEntry>(other.locals),
                       other.esp_pos,
                       new List<Utils.StoreEntry>(other.globals),
                       other.func,
                       new List<Utils.StoreEntry>(other.typedefs),
                       new List<Utils.StoreEntry>(other.enums)) {}

            // empty Scope
            // ===========
            // 
            public Scope()
                : this(new List<Utils.StoreEntry>(),
                       0,
                       new List<Utils.StoreEntry>(),
                       new EmptyFunctionType(),
                       new List<Utils.StoreEntry>(),
                       new List<Utils.StoreEntry>()) {}


            // InScope
            // =======
            // create a new scope with:
            //   the same stack offset
            //   the same current function
            //   other entries are empty
            // 
            public Scope InScope() {
                return new Scope(new List<Utils.StoreEntry>(), this.esp_pos,
                                 new List<Utils.StoreEntry>(), this.func,
                                 new List<Utils.StoreEntry>(),
                                 new List<Utils.StoreEntry>());
            }


            // PushEntry
            // =========
            // input: loc, name, Type
            // output: Scope
            // returns a new scope with everything the same as this, excpet for a new entry
            // 
            public Scope PushEntry(EntryKind loc, String name, ExprType type) {
                Scope scope = new Scope(this);
                switch (loc) {
                case EntryKind.STACK:
                    scope.esp_pos -= Utils.RoundUp(type.SizeOf, 4);
                    scope.locals.Add(new Utils.StoreEntry(name, type, scope.esp_pos));
                    break;
                case EntryKind.GLOBAL:
                    scope.globals.Add(new Utils.StoreEntry(name, type, 0));
                    break;
                case EntryKind.TYPEDEF:
                    scope.typedefs.Add(new Utils.StoreEntry(name, type, 0));
                    break;
                default:
                    return null;
                }
                return scope;
            }


            // PushEnum
            // ========
            // input: name, Type
            // output: Environment
            // return a new environment which adds a enum Value
            // 
            public Scope PushEnum(String name, ExprType type, Int32 value) {
                Scope scope = new Scope(this);
                scope.enums.Add(new Utils.StoreEntry(name, type, value));
                return scope;
            }


            // SetCurrFunc
            // ===========
            // set the current function
            public Scope SetCurrentFunction(FunctionType type) {
                return new Scope(this.locals, this.esp_pos, this.globals,
                    type, this.typedefs, this.enums
                );
            }


            // Find
            // ====
            // input: name
            // output: Entry
            // search for a symbol in the current scope
            // 
            public Entry Find(String name) {
                Utils.StoreEntry store_entry;

                // search the enum entries
                if ((store_entry = this.enums.FindLast(entry => entry.name == name)) != null) {
                    return new Entry(EntryKind.ENUM, store_entry.type, store_entry.offset);
                }

                // search the typedef entries
                if ((store_entry = this.typedefs.FindLast(entry => entry.name == name)) != null) {
                    return new Entry(EntryKind.TYPEDEF, store_entry.type, store_entry.offset);
                }
                
                // search the stack entries
                if ((store_entry = this.locals.FindLast(entry => entry.name == name)) != null) {
                    return new Entry(EntryKind.STACK, store_entry.type, store_entry.offset);
                }

                // search the function arguments
                if ((store_entry = this.func.Args.FindLast(entry => entry.name == name)) != null) {
                    return new Entry(EntryKind.FRAME, store_entry.type, store_entry.offset);
                }

                // search the global entries
                if ((store_entry = this.globals.FindLast(entry => entry.name == name)) != null) {
                    return new Entry(EntryKind.GLOBAL, store_entry.type, store_entry.offset);
                }

                return null;
            }


            // Dump
            // ====
            // input: depth, indent
            // output: String
            // dumps the content in this level
            // 
            public String Dump(Int32 depth, String single_indent) {
                String indent = "";
                for (; depth > 0; depth--) {
                    indent += single_indent;
                }

                String str = "";
                foreach (Utils.StoreEntry entry in this.func.Args) {
                    str += indent;
                    str += "[%ebp + " + entry.offset + "] " + entry.name + " : " + entry.type + "\n";
                }
                foreach (Utils.StoreEntry entry in this.globals) {
                    str += indent;
                    str += "[extern] " + entry.name + " : " + entry.type + "\n";
                }
                foreach (Utils.StoreEntry entry in this.locals) {
                    str += indent;
                    str += "[%ebp - " + entry.offset + "] " + entry.name + " : " + entry.type + "\n";
                }
                foreach (Utils.StoreEntry entry in this.typedefs) {
                    str += indent;
                    str += "typedef: " + entry.name + " <- " + entry.type + "\n";
                }
                foreach (Utils.StoreEntry entry in this.enums) {
                    str += indent;
                    str += entry.name + " = " + entry.offset + "\n";
                }
                return str;

            }


            // ================================================================
            //  private members
            // ================================================================
            public readonly List<Utils.StoreEntry> locals;
            public readonly FunctionType              func;
            public readonly List<Utils.StoreEntry> globals;
            public readonly List<Utils.StoreEntry> typedefs;
            public readonly List<Utils.StoreEntry> enums;
            public Int32 esp_pos;

        }

        // Environment
        // ===========
        // construct an environment with a single empty scope
        public Env() {
            this._scopes = ImmutableStack.Create(new Scope());
        }

        // Environment
        // ===========
        // construct an environment with the given scopes
        // 
        private Env(ImmutableStack<Scope> scopes) {
            this._scopes = scopes;
        }
        
        // InScope
        // =======
        // input: void
        // output: Environment
        // return a new environment which has a new inner scope
        // 
        public Env InScope() {
            return new Env(this._scopes.Push(this._scopes.Peek().InScope()));
        }

        // OutScope
        // ========
        // input: void
        // output: Environment
        // return a new environment which goes out of the most inner scope of the current environment
        // 
        public Env OutScope() {
            return new Env(this._scopes.Pop());
        }

        // PushEntry
        // =========
        // input: loc, name, Type
        // ouput: Environment
        // return a new environment which adds a symbol entry
        // 
        public Env PushEntry(EntryKind loc, String name, ExprType type) {
            Scope top = this._scopes.Peek();
            return new Env(this._scopes.Pop().Push(top.PushEntry(loc, name, type)));
        }

        // PushEnum
        // ========
        // input: name, Type
        // output: Environment
        // return a new environment which adds a enum Value
        // 
        public Env PushEnum(String name, ExprType type, Int32 value) {
            Scope top = this._scopes.Peek();
            return new Env(this._scopes.Pop().Push(top.PushEnum(name, type, value)));
        }

        // SetCurrentFunction
        // ==================
        // input: Type
        // ouput: Environment
        // return a new environment which sets the current function
        // 
        public Env SetCurrentFunction(FunctionType type) {
            Scope top = this._scopes.Peek();
            return new Env(this._scopes.Pop().Push(top.SetCurrentFunction(type)));
        }

        // GetCurrentFunction
        // ==================
        // input: void
        // output: FunctionType
        // return the Type of the current function
        // 
        public FunctionType GetCurrentFunction() {
            return this._scopes.Peek().func;
        }

        // GetStackOffset
        // ==============
        // input: void
        // output: Int32
        // return the current stack size
        // 
        public Int32 StackSize => -this._scopes.Peek().esp_pos;

        public Option<Entry> Find(String name) {
            Entry entry = null;
            foreach (Scope scope in this._scopes) {
                if ((entry = scope.Find(name)) != null) {
                    return new Some<Entry>(entry);
                }
            }
			return new None<Entry>();
        }

        public Option<Entry> FindInCurrentScope(String name) {
            var entry = this._scopes.Peek().Find(name);
            if (entry == null) {
                return Option<Entry>.None;
            }
            return Option.Some(entry);
        }

        public Boolean IsGlobal() {
            return this._scopes.Count() == 1;
        }

        public String Dump() {
            String str = "";
            Int32 depth = 0;
            foreach (Scope scope in this._scopes) {
                str += scope.Dump(depth, "  ");
                depth++;
            }
            return str;
        }

        private readonly ImmutableStack<Scope> _scopes;

    }

    /// <summary>
    /// 1. A global scope.
    /// 2. A function scope, with multiple name scopes.
    /// 3. ObjectId.
    /// 4. TypeId.
    /// 
    /// </summary>
    public sealed class Env2 {

        // global scope
        //   global object
        //   static object
        //   typedef
        //   enum value
        // local scope
        //   static object
        //   typedef
        //   frame object
        //   enum value
        //   parameter object

        public enum EntryKind {
            FRAME,
            // STACK,
            GLOBAL,
            TYPEDEF,
            ENUM,
            NAMED,
            PARAM
        }

        public abstract class SymbolEntry {
            public abstract EntryKind Kind { get; }
        }

        public abstract class ObjectEntry : SymbolEntry {
            public ExprType Type { get; }
        }

        // public sealed class StackObjectEntry : ObjectEntry {
        //     public override EntryKind Kind => EntryKind.STACK;
        // }

        public sealed class ParameterObjectEntry : ObjectEntry {
            public override EntryKind Kind => EntryKind.PARAM;
        }

        public sealed class FrameObjectEntry : ObjectEntry {
            public override EntryKind Kind => EntryKind.FRAME;
        }

        public sealed class NamedObjectEntry : ObjectEntry {
            public override EntryKind Kind => EntryKind.NAMED;
        }

        public sealed class TypeEntry : SymbolEntry {
            public override EntryKind Kind => EntryKind.TYPEDEF;
            public ExprType Type { get; }
        }

        public sealed class EnumEntry : SymbolEntry {
            public override EntryKind Kind => EntryKind.ENUM;
        }

        private abstract class SymbolTable {
            protected SymbolTable(ImmutableList<TypeEntry> typeDefs, ImmutableList<EnumEntry> enums) {
                this.TypeDefs = typeDefs;
                this.Enums = enums;
            }

            protected SymbolTable()
                : this(ImmutableList<TypeEntry>.Empty, ImmutableList<EnumEntry>.Empty) { }

            public ImmutableList<TypeEntry> TypeDefs { get; }

            public ImmutableList<EnumEntry> Enums { get; }
        }

        private sealed class GlobalSymbolTable : SymbolTable {
            public GlobalSymbolTable(ImmutableList<TypeEntry> typeDefs, ImmutableList<EnumEntry> enums, ImmutableList<NamedObjectEntry> globalObjects)
                : base(typeDefs, enums) {
                this.GlobalObjects = globalObjects;
            }

            public GlobalSymbolTable()
                : this(ImmutableList<TypeEntry>.Empty, ImmutableList<EnumEntry>.Empty, ImmutableList<NamedObjectEntry>.Empty) { }

            public GlobalSymbolTable Add(TypeEntry typeEntry) =>
                new GlobalSymbolTable(this.TypeDefs.Add(typeEntry), this.Enums, this.GlobalObjects);

            public GlobalSymbolTable Add(EnumEntry enumEntry) =>
                new GlobalSymbolTable(this.TypeDefs, this.Enums.Add(enumEntry), this.GlobalObjects);

            public GlobalSymbolTable Add(NamedObjectEntry globalObjectEntry) =>
                new GlobalSymbolTable(this.TypeDefs, this.Enums, this.GlobalObjects.Add(globalObjectEntry));

            public ImmutableList<NamedObjectEntry> GlobalObjects { get; }
        }

        private sealed class LocalSymbolTable : SymbolTable {
            public LocalSymbolTable(ImmutableList<TypeEntry> typeDefs, ImmutableList<EnumEntry> enums, ImmutableList<FrameObjectEntry> frameObjects)
                : base(typeDefs, enums) {
                this.FrameObjects = frameObjects;
            }

            public LocalSymbolTable() {
                this.FrameObjects = ImmutableList<FrameObjectEntry>.Empty;
            }

            public LocalSymbolTable Add(TypeEntry typeEntry) =>
                new LocalSymbolTable(this.TypeDefs.Add(typeEntry), this.Enums, this.FrameObjects);

            public LocalSymbolTable Add(EnumEntry enumEntry) =>
                new LocalSymbolTable(this.TypeDefs, this.Enums.Add(enumEntry), this.FrameObjects);

            public LocalSymbolTable Add(FrameObjectEntry frameObjectEntry) =>
                new LocalSymbolTable(this.TypeDefs, this.Enums, this.FrameObjects.Add(frameObjectEntry));

            public ImmutableList<FrameObjectEntry> FrameObjects { get; }
        }

        /// <summary>
        /// Function info, local symbol tables.
        /// </summary>
        private sealed class FunctionScope {
            public FunctionScope(FunctionType functionType, ImmutableList<ParameterObjectEntry> functionParams, ImmutableStack<LocalSymbolTable> localScopes) {
                this.FunctionType = functionType;
                this.FunctionParams = FunctionParams;
                this.LocalScopes = localScopes;
            }

            public FunctionScope(FunctionType functionType, ImmutableList<ParameterObjectEntry> functionParams)
                : this(functionType, functionParams, ImmutableStack<LocalSymbolTable>.Empty) { }

            public FunctionScope Add(TypeEntry typeEntry) {
                var localSymbleTable = this.LocalScopes.Peek().Add(typeEntry);
                return new FunctionScope(
                    this.FunctionType,
                    this.FunctionParams,
                    this.LocalScopes.Pop().Push(localSymbleTable)
                );
            }

            public FunctionScope Add(EnumEntry enumEntry) {
                var localSymbleTable = this.LocalScopes.Peek().Add(enumEntry);
                return new FunctionScope(
                    this.FunctionType,
                    this.FunctionParams,
                    this.LocalScopes.Pop().Push(localSymbleTable)
                );
            }

            public FunctionScope Add(FrameObjectEntry frameObjectEntry) {
                var localSymbleTable = this.LocalScopes.Peek().Add(frameObjectEntry);
                return new FunctionScope(
                    this.FunctionType,
                    this.FunctionParams,
                    this.LocalScopes.Pop().Push(localSymbleTable)
                );
            }

            public FunctionType FunctionType { get; }

            public ImmutableList<ParameterObjectEntry> FunctionParams { get; }

            public ImmutableStack<LocalSymbolTable> LocalScopes { get; }
        }

        private Env2(GlobalSymbolTable globalSymbolTable, Option<FunctionScope> functionScope) {
            this._globalSymbolTable = globalSymbolTable;
            this._functionScope = functionScope;
        }

        public Env2() : this(new GlobalSymbolTable(), Option<FunctionScope>.None) { }

        private GlobalSymbolTable _globalSymbolTable { get; }

        private Option<FunctionScope> _functionScope { get; }

        public Env2 InFunction(FunctionType functionType, ImmutableList<ParameterObjectEntry> functionParams) {
            if (this._functionScope.IsSome) {
                throw new InvalidProgramException("Is already in a function. Cannot go in function.");
            }
            return new Env2(
                this._globalSymbolTable,
                Option.Some(new FunctionScope(functionType, functionParams))
            );
        }

        public Env2 OutFunction() {
            if (this._functionScope.IsNone) {
                throw new InvalidProgramException("Is already global. Cannot go out of function.");
            }
            return new Env2(
                this._globalSymbolTable,
                Option<FunctionScope>.None
            );
        }

        /// <summary>
        /// Add a new local symbol table.
        /// </summary>
        /// <returns>
        /// The new environment.
        /// </returns>
        public Env2 InScope() {
            if (this._functionScope.IsNone) {
                throw new InvalidProgramException("Isn't in a function. Cannot push scope.");
            }

            return new Env2(
                this._globalSymbolTable,
                Option.Some(new FunctionScope(
                    this._functionScope.Value.FunctionType,
                    this._functionScope.Value.FunctionParams,
                    this._functionScope.Value.LocalScopes.Push(new LocalSymbolTable())
                ))
            );
        }

        /// <summary>
        /// Pop a local symbol table.
        /// </summary>
        /// <returns>
        /// The new environment.
        /// </returns>
        public Env2 OutScope() {
            if (this._functionScope.IsNone) {
                throw new InvalidProgramException("Isn't in a function. Cannot pop scope.");
            }
            if (this._functionScope.Value.LocalScopes.IsEmpty) {
                throw new InvalidProgramException("No Local scope to pop.");
            }
            return new Env2(
                this._globalSymbolTable,
                Option.Some(new FunctionScope(
                    this._functionScope.Value.FunctionType,
                    this._functionScope.Value.FunctionParams,
                    this._functionScope.Value.LocalScopes.Pop()
                ))
            );
        }

        public Env2 Add(EnumEntry entry) {
            if (this._functionScope.IsNone) {
                // global
                return new Env2(
                    this._globalSymbolTable.Add(entry),
                    this._functionScope
                );
            } else {
                // local
                return new Env2(
                    this._globalSymbolTable,
                    Option.Some(this._functionScope.Value.Add(entry))
                );
            }
        }

        public Env2 Add(TypeEntry entry) {
            if (this._functionScope.IsNone) {
                // global
                return new Env2(
                    this._globalSymbolTable.Add(entry),
                    this._functionScope
                );
            } else {
                // local
                return new Env2(
                    this._globalSymbolTable,
                    Option.Some(this._functionScope.Value.Add(entry))
                );
            }
        }

        public Env2 Add(NamedObjectEntry entry) {
            if (this._functionScope.IsSome) {
                
            }
            throw new Exception();
        }
    }
}

<|start_filename|>Driver/Program.cs<|end_filename|>
﻿using System;
using System.Linq;

namespace Driver {
    class Program {
        static void Main(String[] args) {
            if (!args.Any()) {
                String src = @"
int printf(char *, ...);
int main(int argc, char **argv) {
    printf(""%d"", argc);
    return 0;
}
";
                Compiler compiler = Compiler.FromSource(src);
                Console.WriteLine(compiler.Assembly);
            } else {
                Compiler compiler = Compiler.FromFile(args[0]);
                Console.WriteLine(compiler.Assembly);
            }
        }
    }
}


<|start_filename|>TestPrograms/WeiredSwitch.c<|end_filename|>
int printf(const char *, ...);
int main() {
    int a = 4;
    switch (a) {
        case 1:
        case 2:
        case 3:
            switch (a) {
                case 4:
                    printf("inner switch 4\n");
                default:
                    break;
            }
            break;
        default:
            break;
    }
    return 0;
}


<|start_filename|>TestPrograms/StructReturn.c<|end_filename|>
﻿struct S {
	int member;
};

struct S foo() {
	struct S s = { 1 };
	return s;
}

int printf(char *, ...);

int main() {
	struct S s = foo();
	printf("%d", s.member);
}

<|start_filename|>Scanner/Operator.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;

namespace LexicalAnalysis {
    /// <summary>
    /// Note that '...' is recognized as three '.'s
    /// </summary>
    public enum OperatorVal {
        LBRACKET,
        RBRACKET,
        LPAREN,
        RPAREN,
        PERIOD,
        COMMA,
        QUESTION,
        COLON,
        TILDE,
        SUB,
        RARROW,
        DEC,
        SUBASSIGN,
        ADD,
        INC,
        ADDASSIGN,
        BITAND,
        AND,
        ANDASSIGN,
        MULT,
        MULTASSIGN,
        LT,
        LEQ,
        LSHIFT,
        LSHIFTASSIGN,
        GT,
        GEQ,
        RSHIFT,
        RSHIFTASSIGN,
        ASSIGN,
        EQ,
        BITOR,
        OR,
        ORASSIGN,
        NOT,
        NEQ,
        DIV,
        DIVASSIGN,
        MOD,
        MODASSIGN,
        XOR,
        XORASSIGN,
        SEMICOLON,
        LCURL,
        RCURL
    }

    public sealed class TokenOperator : Token {
        public TokenOperator(OperatorVal val) {
            this.Val = val;
        }

        public override TokenKind Kind { get; } = TokenKind.OPERATOR;
        public OperatorVal Val { get; }

        public static Dictionary<String, OperatorVal> Operators { get; } = new Dictionary<String, OperatorVal> {
            { "[",    OperatorVal.LBRACKET     },
            { "]",    OperatorVal.RBRACKET     },
            { "(",    OperatorVal.LPAREN       },
            { ")",    OperatorVal.RPAREN       },
            { ".",    OperatorVal.PERIOD       },
            { ",",    OperatorVal.COMMA        },
            { "?",    OperatorVal.QUESTION     },
            { ":",    OperatorVal.COLON        },
            { "~",    OperatorVal.TILDE        },
            { "-",    OperatorVal.SUB          },
            { "->",   OperatorVal.RARROW       },
            { "--",   OperatorVal.DEC          },
            { "-=",   OperatorVal.SUBASSIGN    },
            { "+",    OperatorVal.ADD          },
            { "++",   OperatorVal.INC          },
            { "+=",   OperatorVal.ADDASSIGN    },
            { "&",    OperatorVal.BITAND       },
            { "&&",   OperatorVal.AND          },
            { "&=",   OperatorVal.ANDASSIGN    },
            { "*",    OperatorVal.MULT         },
            { "*=",   OperatorVal.MULTASSIGN   },
            { "<",    OperatorVal.LT           },
            { "<=",   OperatorVal.LEQ          },
            { "<<",   OperatorVal.LSHIFT       },
            { "<<=",  OperatorVal.LSHIFTASSIGN },
            { ">",    OperatorVal.GT           },
            { ">=",   OperatorVal.GEQ          },
            { ">>",   OperatorVal.RSHIFT       },
            { ">>=",  OperatorVal.RSHIFTASSIGN },
            { "=",    OperatorVal.ASSIGN       },
            { "==",   OperatorVal.EQ           },
            { "|",    OperatorVal.BITOR        },
            { "||",   OperatorVal.OR           },
            { "|=",   OperatorVal.ORASSIGN     },
            { "!",    OperatorVal.NOT          },
            { "!=",   OperatorVal.NEQ          },
            { "/",    OperatorVal.DIV          },
            { "/=",   OperatorVal.DIVASSIGN    },
            { "%",    OperatorVal.MOD          },
            { "%=",   OperatorVal.MODASSIGN    },
            { "^",    OperatorVal.XOR          },
            { "^=",   OperatorVal.XORASSIGN    },
            { ";",    OperatorVal.SEMICOLON    },
            { "{",    OperatorVal.LCURL        },
            { "}",    OperatorVal.RCURL        }
        };

        public override String ToString() {
            return this.Kind + " [" + this.Val + "]: " + Operators.First(pair => pair.Value == this.Val).Key;
        }
    }

    public class FSAOperator : FSA {
        private enum State {
            START,
            END,
            ERROR,
            FINISH,
            SUB,
            ADD,
            AMP,
            MULT,
            LT,
            LTLT,
            GT,
            GTGT,
            EQ,
            OR,
            NOT,
            DIV,
            MOD,
            XOR
        };

        public static ImmutableHashSet<Char> OperatorChars { get; } = ImmutableHashSet.Create(
            '[',
            ']',
            '(',
            ')',
            '.',
            ',',
            '?',
            ':',
            '-',
            '>',
            '+',
            '&',
            '*',
            '~',
            '!',
            '/',
            '%',
            '<',
            '=',
            '^',
            '|',
            ';',
            '{',
            '}'
            );

        private State _state;
        private String _scanned;

        public FSAOperator() {
            this._state = State.START;
            this._scanned = "";
        }

        public override sealed void Reset() {
            this._state = State.START;
            this._scanned = "";
        }

        public override sealed FSAStatus GetStatus() {
            switch (this._state) {
                case State.START:
                    return FSAStatus.NONE;
                case State.END:
                    return FSAStatus.END;
                case State.ERROR:
                    return FSAStatus.ERROR;
                default:
                    return FSAStatus.RUNNING;
            }
        }

        public override sealed Token RetrieveToken() {
            return new TokenOperator(TokenOperator.Operators[this._scanned.Substring(0, this._scanned.Length - 1)]);
        }

        public override sealed void ReadChar(Char ch) {
            this._scanned = this._scanned + ch;
            switch (this._state) {
                case State.END:
                case State.ERROR:
                    this._state = State.ERROR;
                    break;
                case State.START:
                    if (OperatorChars.Contains(ch)) {
                        switch (ch) {
                            case '-':
                                this._state = State.SUB;
                                break;
                            case '+':
                                this._state = State.ADD;
                                break;
                            case '&':
                                this._state = State.AMP;
                                break;
                            case '*':
                                this._state = State.MULT;
                                break;
                            case '<':
                                this._state = State.LT;
                                break;
                            case '>':
                                this._state = State.GT;
                                break;
                            case '=':
                                this._state = State.EQ;
                                break;
                            case '|':
                                this._state = State.OR;
                                break;
                            case '!':
                                this._state = State.NOT;
                                break;
                            case '/':
                                this._state = State.DIV;
                                break;
                            case '%':
                                this._state = State.MOD;
                                break;
                            case '^':
                                this._state = State.XOR;
                                break;
                            default:
                                this._state = State.FINISH;
                                break;
                        }
                    } else {
                        this._state = State.ERROR;
                    }
                    break;
                case State.FINISH:
                    this._state = State.END;
                    break;
                case State.SUB:
                    switch (ch) {
                        case '>':
                        case '-':
                        case '=':
                            this._state = State.FINISH;
                            break;
                        default:
                            this._state = State.END;
                            break;
                    }
                    break;
                case State.ADD:
                    switch (ch) {
                        case '+':
                        case '=':
                            this._state = State.FINISH;
                            break;
                        default:
                            this._state = State.END;
                            break;
                    }
                    break;
                case State.AMP:
                    switch (ch) {
                        case '&':
                        case '=':
                            this._state = State.FINISH;
                            break;
                        default:
                            this._state = State.END;
                            break;
                    }
                    break;
                case State.MULT:
                    if (ch == '=') {
                        this._state = State.FINISH;
                    } else {
                        this._state = State.END;
                    }
                    break;
                case State.LT:
                    switch (ch) {
                        case '=':
                            this._state = State.FINISH;
                            break;
                        case '<':
                            this._state = State.LTLT;
                            break;
                        default:
                            this._state = State.END;
                            break;
                    }
                    break;
                case State.GT:
                    switch (ch) {
                        case '=':
                            this._state = State.FINISH;
                            break;
                        case '>':
                            this._state = State.GTGT;
                            break;
                        default:
                            this._state = State.END;
                            break;
                    }
                    break;
                case State.EQ:
                    if (ch == '=') {
                        this._state = State.FINISH;
                    } else {
                        this._state = State.END;
                    }
                    break;
                case State.OR:
                    switch (ch) {
                        case '|':
                        case '=':
                            this._state = State.FINISH;
                            break;
                        default:
                            this._state = State.END;
                            break;
                    }
                    break;
                case State.NOT:
                    if (ch == '=') {
                        this._state = State.FINISH;
                    } else {
                        this._state = State.END;
                    }
                    break;
                case State.DIV:
                    if (ch == '=') {
                        this._state = State.FINISH;
                    } else {
                        this._state = State.END;
                    }
                    break;
                case State.MOD:
                    if (ch == '=') {
                        this._state = State.FINISH;
                    } else {
                        this._state = State.END;
                    }
                    break;
                case State.XOR:
                    if (ch == '=') {
                        this._state = State.FINISH;
                    } else {
                        this._state = State.END;
                    }
                    break;
                case State.LTLT:
                    if (ch == '=') {
                        this._state = State.FINISH;
                    } else {
                        this._state = State.END;
                    }
                    break;
                case State.GTGT:
                    if (ch == '=') {
                        this._state = State.FINISH;
                    } else {
                        this._state = State.END;
                    }
                    break;
                default:
                    this._state = State.ERROR;
                    break;
            }
        }

        public override sealed void ReadEOF() {
            this._scanned = this._scanned + '0';
            switch (this._state) {
                case State.FINISH:
                case State.SUB:
                case State.ADD:
                case State.AMP:
                case State.MULT:
                case State.LT:
                case State.LTLT:
                case State.GT:
                case State.GTGT:
                case State.EQ:
                case State.OR:
                case State.NOT:
                case State.DIV:
                case State.MOD:
                case State.XOR:
                    this._state = State.END;
                    break;
                default:
                    this._state = State.ERROR;
                    break;
            }
        }

    }
}

<|start_filename|>AST/Expressions.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;

namespace AST {

    // 3.2.1.5
    /* First, if either operand has Type long double, the other operand is converted to long double.
     * Otherwise, if either operand has Type double, the other operand is converted to double.
     * Otherwise, if either operand has Type float, the other operand is converted to float.
     * Otherwise, the integral promotions are performed on both operands.
     * Then the following rules are applied:
     * If either operand has Type unsigned long Int32, the other operand is converted to unsigned long Int32.
     * Otherwise, if one operand has Type long Int32 and the other has Type unsigned Int32, if a long Int32 can represent all values of an unsigned Int32, the operand of Type unsigned Int32 is converted to long Int32;
     * if a long Int32 cannot represent all the values of an unsigned Int32, both operands are converted to unsigned long Int32. Otherwise, if either operand has Type long Int32, the other operand is converted to long Int32.
     * Otherwise, if either operand has Type unsigned Int32, the other operand is converted to unsigned Int32.
     * Otherwise, both operands have Type Int32.*/

    // My simplification:
    // I let long = int, long double = double

    public abstract class Expr : ISyntaxTreeNode {
        public abstract ABT.Expr GetExpr(ABT.Env env);
    }

    /// <summary>
    /// Only a name
    /// </summary>
    public class Variable : Expr {
        public Variable(String name) {
            this.Name = name;
        }

        public static Expr Create(String name) =>
            new Variable(name);

        public String Name { get; }

        public override ABT.Expr GetExpr(ABT.Env env) {
            Option<ABT.Env.Entry> entry_opt = env.Find(this.Name);

            if (entry_opt.IsNone) {
                throw new InvalidOperationException($"Cannot find variable '{this.Name}'");
            }

            ABT.Env.Entry entry = entry_opt.Value;

            switch (entry.Kind) {
                case ABT.Env.EntryKind.TYPEDEF:
                    throw new InvalidOperationException($"Expected a variable '{this.Name}', not a typedef.");
                case ABT.Env.EntryKind.ENUM:
                    return new ABT.ConstLong(entry.Offset, env);
                case ABT.Env.EntryKind.FRAME:
                case ABT.Env.EntryKind.GLOBAL:
                case ABT.Env.EntryKind.STACK:
                    return new ABT.Variable(entry.Type, this.Name, env);
                default:
                    throw new InvalidOperationException($"Cannot find variable '{this.Name}'");
            }
        }
    }

    /// <summary>
    /// A list of assignment expressions.
    /// e.g.
    ///   a = 3, b = 4;
    /// </summary>
	public class AssignmentList : Expr {
        protected AssignmentList(ImmutableList<Expr> exprs) {
            this.Exprs = exprs;
        }

        public ImmutableList<Expr> Exprs { get; }

        public static Expr Create(ImmutableList<Expr> exprs) =>
            new AssignmentList(exprs);

        public override ABT.Expr GetExpr(ABT.Env env) {
            ImmutableList<ABT.Expr> exprs = this.Exprs.ConvertAll(expr => expr.GetExpr(env));
            return new ABT.AssignList(exprs);
        }
    }

    /// <summary>
    /// Conditional Expression
    /// 
    /// Cond ? true_expr : false_expr
    /// 
    /// Cond must be of scalar Type
    /// 
    /// 1. if both true_expr and false_expr have arithmetic types
    ///    perform usual arithmetic conversion
    /// 2. 
    /// </summary>
    // TODO : What if const???
    public class ConditionalExpression : Expr {
        public ConditionalExpression(Expr cond, Expr trueExpr, Expr falseExpr) {
            this.Cond = cond;
            this.TrueExpr = trueExpr;
            this.FalseExpr = falseExpr;
        }

        public Expr Cond { get; }
        public Expr TrueExpr { get; }
        public Expr FalseExpr { get; }

        public static Expr Create(Expr cond, Expr trueExpr, Expr falseExpr) =>
            new ConditionalExpression(cond, trueExpr, falseExpr);

        public override ABT.Expr GetExpr(ABT.Env env) {
            ABT.Expr cond = this.Cond.GetExpr(env);

            if (!cond.Type.IsScalar) {
                throw new InvalidOperationException("Expected a scalar condition in conditional expression.");
            }

            if (cond.Type.IsIntegral) {
                cond = ABT.TypeCast.IntegralPromotion(cond).Item1;
            }

            ABT.Expr true_expr = this.TrueExpr.GetExpr(env);
            ABT.Expr false_expr = this.FalseExpr.GetExpr(env);

            // 1. if both true_expr and false_Expr have arithmetic types:
            //    perform usual arithmetic conversion
            if (true_expr.Type.IsArith && false_expr.Type.IsArith) {
                var r_cast = ABT.TypeCast.UsualArithmeticConversion(true_expr, false_expr);
                true_expr = r_cast.Item1;
                false_expr = r_cast.Item2;
                return new ABT.ConditionalExpr(cond, true_expr, false_expr, true_expr.Type);
            }

            if (true_expr.Type.Kind != false_expr.Type.Kind) {
                throw new InvalidOperationException("Operand types not match in conditional expression.");
            }

            switch (true_expr.Type.Kind) {
                // 2. if both true_expr and false_expr have struct or union Type
                //    make sure they are compatible
                case ABT.ExprTypeKind.STRUCT_OR_UNION:
                    if (!true_expr.Type.EqualType(false_expr.Type)) {
                        throw new InvalidOperationException("Expected compatible types in conditional expression.");
                    }
                    return new ABT.ConditionalExpr(cond, true_expr, false_expr, true_expr.Type);

                // 3. if both true_expr and false_expr have void Type
                //    return void
                case ABT.ExprTypeKind.VOID:
                    return new ABT.ConditionalExpr(cond, true_expr, false_expr, true_expr.Type);

                // 4. if both true_expr and false_expr have pointer Type
                case ABT.ExprTypeKind.POINTER:

                    // if either points to void, convert to void *
                    if (((ABT.PointerType)true_expr.Type).RefType.Kind == ABT.ExprTypeKind.VOID
                        || ((ABT.PointerType)false_expr.Type).RefType.Kind == ABT.ExprTypeKind.VOID) {
                        return new ABT.ConditionalExpr(cond, true_expr, false_expr, new ABT.PointerType(new ABT.VoidType()));
                    }

                    throw new NotImplementedException("More comparisons here.");

                default:
                    throw new InvalidOperationException("Expected compatible types in conditional expression.");
            }
        }
    }

    /// <summary>
    /// Function call: func(args)
    /// </summary>
    public class FuncCall : Expr {
        protected FuncCall(Expr func, ImmutableList<Expr> args) {
            this.Func = func;
            this.Args = args;
        }
        
        public static Expr Create(Expr func, ImmutableList<Expr> args) =>
            new FuncCall(func, args);

        public Expr Func { get; }

        public ImmutableList<Expr> Args { get; }

        public override ABT.Expr GetExpr(ABT.Env env) {

            // Step 1: get arguments passed into the function.
            // Note that currently the arguments are not casted based on the prototype.
            var args = this.Args.Select(_ => _.GetExpr(env)).ToList();

            // A special case:
            // If we cannot find the function prototype in the environment, make one up.
            // This function returns int.
            // Update the environment to add this function Type.
            if ((this.Func is Variable) && env.Find((this.Func as Variable).Name).IsNone) {
                // TODO: get this env used.
                env = env.PushEntry(ABT.Env.EntryKind.TYPEDEF, (this.Func as Variable).Name, ABT.FunctionType.Create(new ABT.LongType(true), args.ConvertAll(_ => Tuple.Create("", _.Type)), false
                    )
                );
            }

            // Step 2: get function expression.
            ABT.Expr func = this.Func.GetExpr(env);

            // Step 3: get the function Type.
            ABT.FunctionType func_type;
            switch (func.Type.Kind) {
                case ABT.ExprTypeKind.FUNCTION:
                    func_type = func.Type as ABT.FunctionType;
                    break;

                case ABT.ExprTypeKind.POINTER:
                    var ref_t = (func.Type as ABT.PointerType).RefType;
                    if (!(ref_t is ABT.FunctionType)) {
                        throw new InvalidOperationException("Expected a function pointer.");
                    }
                    func_type = ref_t as ABT.FunctionType;
                    break;

                default:
                    throw new InvalidOperationException("Expected a function in function call.");
            }


            Int32 num_args_prototype = func_type.Args.Count;
            Int32 num_args_actual = args.Count;

            // If this function doesn't take varargs, make sure the number of arguments match that in the prototype.
            if (!func_type.HasVarArgs && num_args_actual != num_args_prototype) {
                throw new InvalidOperationException("Number of arguments mismatch.");
            }

            // Anyway, you can't call a function with fewer arguments than the prototype.
            if (num_args_actual < num_args_prototype) {
                throw new InvalidOperationException("Too few arguments.");
            }

            // Make implicit cast.
            args = args.GetRange(0, num_args_prototype).Zip(func_type.Args,
                (arg, entry) => ABT.TypeCast.MakeCast(arg, entry.type)
            ).Concat(args.GetRange(num_args_prototype, num_args_actual - num_args_prototype)).ToList();

            return new ABT.FuncCall(func, func_type, args);
        }
    }

    /// <summary>
    /// Expr.attrib: get an attribute from a struct or union
    /// </summary>
    public class Attribute : Expr {
        protected Attribute(Expr expr, String member) {
            this.Expr = expr;
            this.Member = member;
        }

        public Expr Expr { get; }
        public String Member { get; }

        public static Expr Create(Expr expr, String member) =>
            new Attribute(expr, member);

        public override ABT.Expr GetExpr(ABT.Env env) {
            ABT.Expr expr = this.Expr.GetExpr(env);
            String name = this.Member;

            if (expr.Type.Kind != ABT.ExprTypeKind.STRUCT_OR_UNION) {
                throw new InvalidOperationException("Must get the attribute from a struct or union.");
            }

            ABT.Utils.StoreEntry entry = (expr.Type as ABT.StructOrUnionType).Attribs.First(_ => _.name == name);
            ABT.ExprType type = entry.type;

            return new ABT.Attribute(expr, name, type);
        }
    }

}

<|start_filename|>ParserTests/TestStatements.cs<|end_filename|>
﻿using NUnit.Framework;
using Parsing;
using static CompilerTests.ParserTestUtils;

namespace CompilerTests {
    [TestFixture]
    public class TestStatements {

        [Test]
        public void Statement() {
            TestParserRule(
                CParsers.Statement,
                "finish: a = 3;",
                "{ a = 3; b = 4; }",
                "a = 3;",
                "if (1) { 1; }",
                "for (;;) ;",
                "goto finish;"
                );
        }

        [Test]
        public void JumpStatement() {
            TestParserRule(
                CParsers.Statement,
                "goto finish;",
                "continue;",
                "break;",
                "return;",
                "return 3;"
                );
        }

        [Test]
        public void CompoundStatement() {
            TestParserRule(
                CParsers.CompoundStatement,
                "{ }",
                "{ int a; }",
                "{ a; }",
                "{ int a; a; }"
                );
        }

        [Test]
        public void DeclarationList() {
            TestParserRule(
                CParsers.DeclarationList,
                "int a, b = 3; const char *str = 0;"
                );
        }

        [Test]
        public void StatementList() {
            TestParserRule(
                CParsers.StatementList,
                "a = 3; 4; a = b = 5;"
                );
        }

        [Test]
        public void ExpressionStatement() {
            TestParserRule(
                CParsers.ExpressionStatement,
                "3;",
                "a = b;"
                );
        }

        [Test]
        public void IterationStatement() {
            TestParserRule(
                CParsers.IterationStatement,
                "while (1) { 1; }",
                "do { 1; } while (1);",
                "for (1; 1; 1) { 1; }"
                );
        }

        [Test]
        public void SelectionStatement() {
            TestParserRule(
                CParsers.SelectionStatement,
                "if (1) 1; else 2;",
                "if (1) 1;",
                "switch (1) 1;"
                );
        }

        [Test]
        public void LabeledStatement() {
            TestParserRule(
                CParsers.LabeledStatement,
                "finish: return;",
                "case 3: return;",
                "default: return;"
                );
        }
    }
}

<|start_filename|>Driver/Compiler.cs<|end_filename|>
﻿using System;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using CodeGeneration;
using LexicalAnalysis;
using Parsing;

namespace Driver {
    public class Compiler {
        private Compiler(String source) {
            this.Source = source;

            // Lexical analysis
            Scanner scanner = new Scanner(source);
            this.Tokens = scanner.Tokens.ToImmutableList();

            // Parse
            var parserResult = CParsers.Parse(this.Tokens);
            if (parserResult.Source.Count() != 1) {
                throw new InvalidOperationException("Error: not finished parsing");
            }
            this.SyntaxTree = parserResult.Result;

            // Semantic analysis
            var semantReturn = this.SyntaxTree.GetTranslnUnit();
            this.AbstractSyntaxTree = semantReturn.Value;
            this.Environment = semantReturn.Env;

            // Code generation
            var state = new CGenState();
            this.AbstractSyntaxTree.CodeGenerate(state);
            this.Assembly = state.ToString();
        }

        public static Compiler FromSource(String src) {
            return new Compiler(src);
        }

        public static Compiler FromFile(String fileName) {
            if (File.Exists(fileName)) {
                return new Compiler(File.ReadAllText(fileName));
            }
            throw new FileNotFoundException($"{fileName} does not exist!");
        }

        public readonly String Source;
        public readonly ImmutableList<Token> Tokens;
        public readonly AST.TranslnUnit SyntaxTree;
        public readonly ABT.TranslnUnit AbstractSyntaxTree;
        public readonly ABT.Env Environment;
        public readonly String Assembly;
    }
}

<|start_filename|>Parser/ExternalDefinitions.cs<|end_filename|>
﻿using AST;
using static Parsing.ParserCombinator;

namespace Parsing {
    public partial class CParsers {
        
        /// <summary>
        /// translation-unit
        ///   : [external-declaration]+
        /// </summary>
        public static NamedParser<TranslnUnit>
            TranslationUnit { get; } = new NamedParser<TranslnUnit>("translation-unit");

        /// <summary>
        /// external-declaration
        ///   : function-definition | declaration
        /// </summary>
        public static NamedParser<IExternDecln>
            ExternalDeclaration { get; } = new NamedParser<IExternDecln>("external-declaration");

        /// <summary>
        /// function-definition
        ///   : [declaration-specifiers]? declarator [declaration-list]? compound-statement
        ///
        /// NOTE: the optional declaration_list is for the **old-style** function prototype like this:
        /// +-------------------------------+
        /// |    int foo(param1, param2)    |
        /// |    int param1;                |
        /// |    char param2;               |
        /// |    {                          |
        /// |        ....                   |
        /// |    }                          |
        /// +-------------------------------+
        ///
        /// i'm **not** going to support this style. function prototypes should always be like this:
        /// +------------------------------------------+
        /// |    int foo(int param1, char param2) {    |
        /// |        ....                              |
        /// |    }                                     |
        /// +------------------------------------------+
        ///
        /// so the grammar becomes:
        /// function-definition
        ///   : [declaration-specifiers]? declarator compound-statement
        /// </summary>
        public static NamedParser<FuncDef>
            FunctionDefinition { get; } = new NamedParser<FuncDef>("function-definition");

        public static void SetExternalDefinitionRules() {

            // translation-unit
            //   : [external-declaration]+
            TranslationUnit.Is(
                (ExternalDeclaration)
                .OneOrMore()
                .Then(TranslnUnit.Create)
            );

            // external-declaration
            //   : function-definition | declaration
            ExternalDeclaration.Is(
                Either<IExternDecln>(
                    FunctionDefinition
                ).Or(
                    Declaration
                )
            );

            // function-definition
            //   : [declaration-specifiers]? declarator compound-statement
            FunctionDefinition.Is(
                DeclarationSpecifiers.Optional()
                .Then(Declarator)
                .Then(CompoundStatement)
                .Then(FuncDef.Create)
            );

        }
    }
}


<|start_filename|>ParserTests/TestExternalDefinitions.cs<|end_filename|>
﻿using System.IO;
using System.Linq;
using LexicalAnalysis;
using NUnit.Framework;
using Parsing;

namespace CompilerTests {
    [TestFixture]
    public class TestExternalDefinitions {
        [Test]
        public void TestParser() {
            System.Console.WriteLine(Directory.GetCurrentDirectory());

            var files = Directory
                .GetFiles("../../TestPrograms")
                .Where(_ => _.EndsWith(".c"));

            var sources = files.Select(File.ReadAllText).ToArray();

            foreach (var source in sources) {
                var scanner = new Scanner(source);
                var tokens = scanner.Tokens;
                var env = new ParserEnvironment();
                var input = new ParserInput(env, scanner.Tokens);
                var result = CParsers.TranslationUnit.Parse(input);
                Assert.IsTrue(result.IsSuccessful);
                Assert.IsTrue(result.Source.Count() == 1);
            }
        }
    }
}

<|start_filename|>CGen/ConstExpressions.cs<|end_filename|>
﻿using System;
using CodeGeneration;

namespace ABT {
    public abstract partial class ConstExpr {
        public override sealed void CGenAddress(CGenState state) {
            throw new InvalidOperationException("Cannot get the address of a constant");
        }
    }

    public sealed partial class ConstLong {
        public override Reg CGenValue(CGenState state) {
            state.MOVL(this.Value, Reg.EAX);
            return Reg.EAX;
        }
    }

    public sealed partial class ConstULong {
        public override Reg CGenValue(CGenState state) {
            state.MOVL((Int32)this.Value, Reg.EAX);
            return Reg.EAX;
        }
    }

    public sealed partial class ConstShort {
        public override Reg CGenValue(CGenState state) {
            state.MOVL(this.Value, Reg.EAX);
            return Reg.EAX;
        }
    }

    public sealed partial class ConstUShort {
        public override Reg CGenValue(CGenState state) {
            state.MOVL(this.Value, Reg.EAX);
            return Reg.EAX;
        }
    }

    public sealed partial class ConstChar {
        public override Reg CGenValue(CGenState state) {
            state.MOVL(this.Value, Reg.EAX);
            return Reg.EAX;
        }
    }

    public sealed partial class ConstUChar {
        public override Reg CGenValue(CGenState state) {
            state.MOVL(this.Value, Reg.EAX);
            return Reg.EAX;
        }
    }

    public sealed partial class ConstPtr {
        public override Reg CGenValue(CGenState state) {
            state.MOVL((Int32)this.Value, Reg.EAX);
            return Reg.EAX;
        }
    }

    public sealed partial class ConstFloat {
        /// <summary>
        /// flds addr
        /// </summary>
        public override Reg CGenValue(CGenState state) {
            byte[] bytes = BitConverter.GetBytes(this.Value);
            Int32 intval = BitConverter.ToInt32(bytes, 0);
            String name = state.CGenLongConst(intval);
            state.FLDS(name);
            return Reg.ST0;
        }
    }

    public sealed partial class ConstDouble {
        /// <summary>
        /// fldl addr
        /// </summary>
        public override Reg CGenValue(CGenState state) {
            byte[] bytes = BitConverter.GetBytes(this.Value);
            Int32 firstInt = BitConverter.ToInt32(bytes, 0);
            Int32 secondInt = BitConverter.ToInt32(bytes, 4);
            String name = state.CGenLongLongConst(firstInt, secondInt);
            state.FLDL(name);
            return Reg.ST0;
        }
    }

    public sealed partial class ConstStringLiteral {
        public override Reg CGenValue(CGenState state) {
            String name = state.CGenString(this.Value);
            state.LEA(name, Reg.EAX);
            return Reg.EAX;
        }
    }
}

<|start_filename|>ABT/Types/TypePrinter.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;

namespace ABT {
    public abstract partial class ExprType {
        public virtual Int32 Precedence => 0;

        public abstract String Decl(String name, Int32 precedence);

        public String Decl(String name) => this.Decl(name, 0);

        public String Decl() => this.Decl("");
    }

    public partial class VoidType {
        public override String Decl(String name, Int32 precedence) =>
            $"{this.DumpQualifiers()}void {name}".TrimEnd(' ');
    }

    public partial class CharType {
        public override String Decl(String name, Int32 precedence) =>
            $"{this.DumpQualifiers()}char {name}".TrimEnd(' ');
    }

    public partial class UCharType {
        public override String Decl(String name, Int32 precedence) =>
            $"{this.DumpQualifiers()}unsigned char {name}".TrimEnd(' ');
    }

    public partial class ShortType {
        public override String Decl(String name, Int32 precedence) =>
            $"{this.DumpQualifiers()}short {name}".TrimEnd(' ');
    }

    public partial class UShortType {
        public override String Decl(String name, Int32 precedence) =>
            $"{this.DumpQualifiers()}unsigned short {name}".TrimEnd(' ');
    }

    public partial class LongType {
        public override String Decl(String name, Int32 precedence) =>
            $"{this.DumpQualifiers()}long {name}".TrimEnd(' ');
    }

    public partial class ULongType {
        public override String Decl(String name, Int32 precedence) =>
            $"{DumpQualifiers()}unsigned long {name}".TrimEnd(' ');
    }

    public partial class FloatType {
        public override String Decl(String name, Int32 precedence) =>
            $"{this.DumpQualifiers()}float {name}".TrimEnd(' ');
    }

    public partial class DoubleType {
        public override String Decl(String name, Int32 precedence) =>
            $"{this.DumpQualifiers()}double {name}".TrimEnd(' ');
    }

    public partial class PointerType {
        public override String Decl(String name, Int32 precedence) {
            if (precedence > this.Precedence) {
                name = $"({name})";
            }
            return this.RefType.Decl($"*{this.DumpQualifiers()}{name}", this.Precedence);
        }
    }

    public partial class IncompleteArrayType {
        public override String Decl(String name, Int32 precedence) {
            if (precedence > this.Precedence) {
                name = $"({name})";
            }
            return this.ElemType.Decl($"{name}[]", this.Precedence);
        }
    }

    public partial class ArrayType {
        public override String Decl(String name, Int32 precedence) {
            if (precedence > this.Precedence) {
                name = $"({name})";
            }
            return this.ElemType.Decl($"{name}[{this.NumElems}]", this.Precedence);
        }
    }

    public partial class StructOrUnionType {
        public override String Decl(String name, Int32 precedence) =>
            $"{this.DumpQualifiers()}{this._layout.TypeName} {name}".TrimEnd(' ');
    }

    public partial class FunctionType {
        public override String Decl(String name, Int32 precedence) {
            if (precedence > this.Precedence) {
                name = $"({name})";
            }

            String str = "";
            if (this.Args.Count == 0) {
                if (this.HasVarArgs) {
                    str = "(...)";
                } else {
                    str = "(void)";
                }
            } else {
                str = this.Args[0].type.Decl();
                for (Int32 i = 1; i < this.Args.Count; ++i) {
                    str += $", {this.Args[i].type.Decl()}";
                }
                if (this.HasVarArgs) {
                    str += ", ...";
                }
                str = $"({str})";
            }

            return this.ReturnType.Decl($"{name}{str}", this.Precedence);
        }
    }
}


<|start_filename|>ParserTests/TestDeclarations.cs<|end_filename|>
﻿using NUnit.Framework;
using Parsing;
using AST;
using static CompilerTests.ParserTestUtils;

namespace CompilerTests {
    [TestFixture]
    public class TestDeclarations {

        [Test]
        public void Declaration() {
            TestParserRule(
                CParsers.Declaration,
                "const int i = 3;",
                "const volatile *i = 0, (* const j)[3] = { 0 };"
                );
        }

        [Test]
        public void DeclarationSpecifiers() {
            TestParserRule(
                CParsers.DeclarationSpecifiers,
                "auto int const",
                "typedef struct MyStruct volatile",
                "int auto int const short"
                );
        }

        [Test]
        public void InitDeclaratorList() {
            TestParserRule(
                CParsers.InitDeclaratorList,
                "var1, var2",
                "*var1 = 0, * const var2()[]"
                );
        }

        [Test]
        public void InitDeclarator() {
            TestParserRule(
                CParsers.InitDeclarator,
                "name",
                "name = 3",
                "*name = 3",
                "* const name = { 3 }"
                );
        }

        [Test]
        public void StorageClassSpecifier() {
            TestParserRule(
                CParsers.StorageClassSpecifier,
                "auto",
                "register",
                "static",
                "extern",
                "typedef"
                );
        }

        [Test]
        public void TypeSpecifier() {
            TestParserRule(
                CParsers.TypeSpecifier,
                "void",
                "char",
                "short",
                "int",
                "long",
                "float",
                "double",
                "signed",
                "unsigned",
                "struct MyStruct",
                "enum MyEnum"
                );

            TestParserRule("MyAwesomeType", new ParserEnvironment().AddSymbol("MyAwesomeType", StorageClsSpec.TYPEDEF), CParsers.TypeDefName
                );
        }

        [Test]
        public void TypeQualifier() {
            TestParserRule(
                CParsers.TypeQualifier,
                "const",
                "volatile"
                );
        }

        [Test]
        public void Declarator() {
            TestParserRule(
                CParsers.Declarator,
                "*name[3]",
                "* const *name",
                "name[3][]",
                "*name()"
                );
        }

        [Test]
        public void Pointer() {
            TestParserRule(
                CParsers.Pointer,
                "*",
                "* const volatile",
                "* const * const *"
                );
        }

        [Test]
        public void ParameterTypeList() {
            TestParserRule(
                CParsers.ParameterTypeList,
                "int a, int b, int",
                "int a, int b, int, ..."
                );
        }

        [Test]
        public void ParameterList() {
            TestParserRule(
                CParsers.ParameterList,
                "int a, int b, int"
                );
        }

        [Test]
        public void TypeQualifierList() {
            TestParserRule(
                CParsers.TypeQualifierList,
                "const const"
                );
        }

        [Test]
        public void DirectDeclarator() {
            TestParserRule(
                CParsers.DirectDeclarator,
                "name",
                "(name)",
                "name[]",
                "name[3]",
                "name(int)",
                "name[3][4]",
                "name[][]",
                "(*name)[3](int arg)",
                "name()"
                );
        }

        [Test]
        public void EnumSpecifier() {
            TestParserRule(
                CParsers.EnumSpecifier,
                "enum MyEnum",
                "enum MyEnum { NAME = 1, NAME }",
                "enum { NAME = 1, NAME }"
                );
        }

        [Test]
        public void EnumeratorList() {
            TestParserRule(
                CParsers.EnumeratorList,
                "NAME, NAME = 3, NAME, NAME = 4"
                );
        }

        [Test]
        public void Enumerator() {
            TestParserRule(
                CParsers.Enumerator,
                "NAME = 3",
                "NAME"
                );
        }

        [Test]
        public void EnumerationConstant() {
            TestParserRule(
                CParsers.EnumerationConstant,
                "name"
                );
        }

        [Test]
        public void StructOrUnionSpecifier() {
            TestParserRule(
                CParsers.StructOrUnion,
                "struct",
                "union"
                );
        }

        [Test]
        public void StructDeclarationList() {
            TestParserRule(
                CParsers.StructDeclarationList,
                "int a; int b;"
                );
        }

        [Test]
        public void StructDeclaration() {
            TestParserRule(
                CParsers.StructDeclaration,
                "int const a[3], b : 3, * const c(int arg);"
                );
        }

        [Test]
        public void SpecifierQualifierList() {
            TestParserRule(
                CParsers.SpecifierQualifierList,
                "int const",
                "const int",
                "char char char",
                "short int const volatile"
                );
        }

        [Test]
        public void StructDeclaratorList() {
            TestParserRule(
                CParsers.StructDeclaratorList,
                "name, name, name[3]",
                "name : 3, name()"
                );
        }

        [Test]
        public void StructDeclarator() {
            TestParserRule(
                CParsers.StructDeclarator,
                "name",
                "name : 3"
                );
        }

        [Test]
        public void ParameterDeclaration() {
            TestParserRule(
                CParsers.ParameterDeclaration,
                "register int name[3]",
                "register int[3]"
                );
        }

        [Test]
        public void AbstractDeclarator() {
            TestParserRule(
                CParsers.AbstractDeclarator,
                "* const (*)()[]"
                );
        }

        [Test]
        public void DirectAbstractDeclarator() {
            TestParserRule(
                CParsers.DirectAbstractDeclarator,
                "[3][]",
                "(int arg1, char arg2)()",
                "[]()",
                "(*)()"
                );
        }

        [Test]
        public void Initializer() {
            TestParserRule(
                CParsers.Initializer,
                "3",
                "{ 3 }",
                "{ 3, 4, }",
                "{ 3, { 3, 4 }, 5 }"
                );
        }

        [Test]
        public void InitializerList() {
            TestParserRule(
                CParsers.InitializerList,
                "3",
                "3, 4",
                "3, 4, 5"
                );
        }

        [Test]
        public void TypeName() {
            TestParserRule(
                CParsers.TypeName,
                "int",
                "const int *",
                "const int (*)(int arg)"
                );
        }

        [Test]
        public void TypeDefName() {
            TestParserRule("MyAwesomeType", new ParserEnvironment().AddSymbol("MyAwesomeType", StorageClsSpec.TYPEDEF), CParsers.TypeDefName
                );
        }

    }
}

<|start_filename|>Scanner/FSA.cs<|end_filename|>
﻿using System;

namespace LexicalAnalysis {
    public enum FSAStatus {
        NONE,
        END,
        RUNNING,
        ERROR
    }

    public abstract class FSA {
        public abstract FSAStatus GetStatus();
        public abstract void ReadChar(Char ch);
        public abstract void Reset();
        public abstract void ReadEOF();
        public abstract Token RetrieveToken();
    }
}