Beispiel #1
0
        public static Compilation Compile(string src, string path = "test.sc", IUsingSourceResolver?sourceResolver = null)
        {
            var c = new Compilation {
                SourceResolver = sourceResolver
            };

            using var r = new StringReader(src);
            c.SetMainModule(r, path);
            c.Compile();
            return(c);
        }
Beispiel #2
0
        public ICodeblock Compile(string macro)
        {
            Compilation <object, RuntimeCodeblockDelegate> compiler = GetCompiler(true);
            var m = compiler.Compile(macro);

            if (m.Diagnostic != null)
            {
                throw m.Diagnostic;
            }
            return(new RuntimeCodeblock(m.Macro, m.ParamCount));
        }
Beispiel #3
0
        internal static void EmitCorLibWithAssemblyReferences(
            Compilation comp,
            string pdbPath,
            Func <CommonPEModuleBuilder, EmitOptions, CommonPEModuleBuilder> getModuleBuilder,
            out ImmutableArray <byte> peBytes,
            out ImmutableArray <byte> pdbBytes)
        {
            var diagnostics   = DiagnosticBag.GetInstance();
            var emitOptions   = EmitOptions.Default.WithRuntimeMetadataVersion("0.0.0.0").WithDebugInformationFormat(DebugInformationFormat.PortablePdb);
            var moduleBuilder = comp.CheckOptionsAndCreateModuleBuilder(
                diagnostics,
                null,
                emitOptions,
                null,
                null,
                null,
                null,
                default(CancellationToken));

            // Wrap the module builder in a module builder that
            // reports the "System.Object" type as having no base type.
            moduleBuilder = getModuleBuilder(moduleBuilder, emitOptions);
            bool result = comp.Compile(
                moduleBuilder,
                emittingPdb: pdbPath != null,
                diagnostics: diagnostics,
                filterOpt: null,
                cancellationToken: default(CancellationToken));

            using (var peStream = new MemoryStream())
            {
                using (var pdbStream = new MemoryStream())
                {
                    PeWriter.WritePeToStream(
                        new EmitContext(moduleBuilder, null, diagnostics, metadataOnly: false, includePrivateMembers: true),
                        comp.MessageProvider,
                        () => peStream,
                        () => pdbStream,
                        null, null,
                        metadataOnly: true,
                        isDeterministic: false,
                        emitTestCoverageData: false,
                        privateKeyOpt: null,
                        cancellationToken: default(CancellationToken));

                    peBytes  = peStream.ToImmutable();
                    pdbBytes = pdbStream.ToImmutable();
                }
            }

            diagnostics.Verify();
            diagnostics.Free();
        }
Beispiel #4
0
        public static void DoTest()
        {
            //NativeDB.Fetch(new Uri("https://raw.githubusercontent.com/alloc8or/gta5-nativedb-data/master/natives.json"), "ScriptHookV_1.0.2060.1.zip")
            //    .ContinueWith(t => File.WriteAllText("nativedb.json", t.Result.ToJson()))
            //    .Wait();

            var nativeDB = NativeDB.FromJson(File.ReadAllText("nativedb.json"));

            using var reader = new StringReader(Code);
            var comp = new Compilation {
                NativeDB = nativeDB
            };

            comp.SetMainModule(reader);
            comp.Compile();
            File.WriteAllText("test_script.ast.txt", comp.MainModule.GetAstDotGraph());

            var d       = comp.GetAllDiagnostics();
            var symbols = comp.MainModule.SymbolTable;

            Console.WriteLine($"Errors:   {d.HasErrors} ({d.Errors.Count()})");
            Console.WriteLine($"Warnings: {d.HasWarnings} ({d.Warnings.Count()})");
            foreach (var diagnostic in d.AllDiagnostics)
            {
                diagnostic.Print(Console.Out);
            }

            foreach (var s in symbols.Symbols)
            {
                if (s is TypeSymbol t && t.Type is StructType struc)
                {
                    Console.WriteLine($"  > '{t.Name}' Size = {struc.SizeOf}");
                }
            }

            Console.WriteLine();
            new Dumper(comp.CompiledScript).Dump(Console.Out, true, true, true, true, true);

            YscFile ysc = new YscFile
            {
                Script = comp.CompiledScript
            };

            string outputPath = "test_script.ysc";

            byte[] data = ysc.Save(Path.GetFileName(outputPath));
            File.WriteAllBytes(outputPath, data);

            outputPath = Path.ChangeExtension(outputPath, "unencrypted.ysc");
            data       = ysc.Save();
            File.WriteAllBytes(outputPath, data);
            ;
        }
Beispiel #5
0
        public ICodeblock Compile(string macro, bool lAllowSingleQuotes, Module module, out bool isCodeblock, out bool addsMemVars)

        {
            isCodeblock = macro.Replace(" ", "").StartsWith("{|");
            addsMemVars = false;
            Compilation <object, RuntimeCodeblockDelegate> compiler = GetCompiler(lAllowSingleQuotes);
            var m = compiler.Compile(macro);

            if (m.Diagnostic != null)
            {
                throw m.Diagnostic;
            }
            addsMemVars = m.CreatesAutoVars;
            return(new RuntimeCodeblock(m.Macro, m.ParamCount));
        }
Beispiel #6
0
        public void TestComplexExpression()
        {
            const bool Expected = (-5 * 3 + 2 * (2 | 4)) == 4 && 3.0f != (8.0f / 2.0f);
            var        comp     = new Compilation();

            comp.SetMainModule(new StringReader(@"
                BOOL dummy = (-5 * 3 + 2 * (2 | 4)) == 4 AND 3.0 <> (8.0 / 2.0)

                PROC MAIN()
                ENDPROC
            "));
            comp.Compile();

            var diagnostics = comp.GetAllDiagnostics();

            Assert.False(diagnostics.HasErrors);
            Assert.NotNull(comp.CompiledScript.Statics);
            Assert.Single(comp.CompiledScript.Statics);
            Assert.Equal(Expected, comp.CompiledScript.Statics[0].AsInt64 == 1);
        }
Beispiel #7
0
        public static int Main(string[] args)
        {
            var outputPath     = (string)null;
            var moduleName     = (string)null;
            var referencePaths = new List <string>();
            var sourcePaths    = new List <string>();
            var printTree      = false;
            var needsHelp      = false;

            var options = new OptionSet()
            {
                "usage: gsc <source-paths> [options]",
                "",
                { "r=", "The {path} of a assembly to referenc", r => referencePaths.Add(r) },
                { "o=", "The {path} of the output file", o => outputPath = o },
                { "m=", "The {name} of the module", m => moduleName = m },
                { "d|dump", "Displays the bound tree in the console", d => printTree = true },
                { "<>", s => sourcePaths.Add(s) },
                { "?|h|help", "Displays Help", h => needsHelp = true },
                "",
            };

            try
            {
                options.Parse(args);
            }
            catch (OptionException e)
            {
                WriteError(e.Message);
                return(5);
            }

            if (needsHelp)
            {
                options.WriteOptionDescriptions(Console.Out);
                return(0);
            }

            var paths = ValidatePaths(sourcePaths);

            if (paths is null)
            {
                return(5);
            }
            if (paths.Length == 0)
            {
                WriteError("No source files are provided.");
                return(5);
            }

            if (outputPath is null)
            {
                outputPath = Path.ChangeExtension(paths[0], ".exe");
            }

            if (moduleName is null)
            {
                moduleName = Path.GetFileNameWithoutExtension(outputPath);
            }

            var sourceTexts = paths.Select(p => new SourceText(File.ReadAllText(p), p)).ToArray();
            var compilation = Compilation.Compile(sourceTexts, moduleName, referencePaths.ToArray());
            var diagnostics = compilation.Emit(outputPath);

            diagnostics.WriteTo(Console.Out);

            if (printTree)
            {
                Console.WriteLine();
                compilation.WriteBoundTree(Console.Out);
            }

            if (!diagnostics.HasErrors)
            {
                Console.Out.ColorWrite($"Sucessfully created {outputPath}\n", ConsoleColor.Green);
            }

            return(0);
        }
Beispiel #8
0
        /*
         * identity : (a:*) -> a -> a
         * identity [a] x = x
         *
         * echo : !((a:P) -> a -> ?a)
         * echo [a] x = return x
         *
         * forward : !((a:N) -> !a -> a)
         * forward [a] x = force x
         *
         * echo/cps : ~(a:*, a, ~a)
         * echo/cps ([a], x, return) = return x
         *
         * forward/cps : ~(a:*, ~a, a)
         * forward/cps ([a], thunk, k) = thunk k
         *
         */
        public static void Main(string[] args)
        {
            var identity = Identity();

            var echo = Echo(Guid.NewGuid(), Guid.NewGuid());

            // TypeChecking.VerifyClosed(echo);

            var forward = Forward(Guid.NewGuid(), Guid.NewGuid());

            // TypeChecking.VerifyClosed(forward);

            var echoN = new Expression <Unit, uint, ITerm <Unit, uint> >(
                universe: new Universe(0, Polarity.Exists),
                type: new Term <Unit, uint>(new TermF <Unit, uint, ITerm <Unit, uint> > .Type(new Class <Unit, uint, ITerm <Unit, uint> > .Shift(
                                                                                                  content: new Term <Unit, uint>(new TermF <Unit, uint, ITerm <Unit, uint> > .Type(new Class <Unit, uint, ITerm <Unit, uint> > .Quantifier(
                                                                                                                                                                                       dependency: new Expression <Unit, uint, Unit>(new Universe(1, null), new Term <Unit, uint>(new TermF <Unit, uint, ITerm <Unit, uint> > .Universe(new Universe(0, Polarity.Exists))), Unit.Singleton),
                                                                                                                                                                                       dependent: new Term <Unit, uint>(new TermF <Unit, uint, ITerm <Unit, uint> > .Type(new Class <Unit, uint, ITerm <Unit, uint> > .Quantifier(
                                                                                                                                                                                                                                                                              dependency: new Expression <Unit, uint, Unit>(new Universe(0, Polarity.Exists), new Term <Unit, uint>(new TermF <Unit, uint, ITerm <Unit, uint> > .Variable(0)), Unit.Singleton),
                                                                                                                                                                                                                                                                              dependent: new Term <Unit, uint>(new TermF <Unit, uint, ITerm <Unit, uint> > .Type(new Class <Unit, uint, ITerm <Unit, uint> > .Shift(
                                                                                                                                                                                                                                                                                                                                                                     content: new Term <Unit, uint>(new TermF <Unit, uint, ITerm <Unit, uint> > .Variable(1)))))))))))))),
                term: new Term <Unit, uint>(new TermF <Unit, uint, ITerm <Unit, uint> > .Constructor(
                                                initialization: new Initialization <Unit, ITerm <Unit, uint> > .Exists.Shift(
                                                    body: new Term <Unit, uint>(new TermF <Unit, uint, ITerm <Unit, uint> > .Constructor(
                                                                                    initialization: new Initialization <Unit, ITerm <Unit, uint> > .Forall.Quantifier(Unit.Singleton,
                                                                                                                                                                      body: new Term <Unit, uint>(new TermF <Unit, uint, ITerm <Unit, uint> > .Constructor(
                                                                                                                                                                                                      initialization: new Initialization <Unit, ITerm <Unit, uint> > .Forall.Quantifier(Unit.Singleton,
                                                                                                                                                                                                                                                                                        body: new Term <Unit, uint>(new TermF <Unit, uint, ITerm <Unit, uint> > .Constructor(
                                                                                                                                                                                                                                                                                                                        initialization: new Initialization <Unit, ITerm <Unit, uint> > .Forall.Shift(
                                                                                                                                                                                                                                                                                                                            body: new Term <Unit, uint>(new TermF <Unit, uint, ITerm <Unit, uint> > .Variable(0)))))))))))))));


            var computation = (Value <int> .Continuation)Evaluation.Evaluate <int>(echoN);

            var number = computation.Content.Throw(
                argument: new Value <int> .Continuation(new Continuation <int>(
                                                            throwF: result =>
            {
                var delayed = (Value <int> .Continuation)result;

                return(delayed.Content.Throw(
                           argument: new Value <int> .Pair(
                               left: new Value <int> .Builtin <int>(4),
                               right: new Value <int> .Continuation(Returning <int>()))));
            })));

            Console.WriteLine("Result: " + number);
            Console.WriteLine();

            var serializer = new Serialization <Unit, uint>(
                useDeclarationF: unit => new Term <Unit, uint>(new TermF <Unit, uint, ITerm <Unit, uint> > .Variable(0)),
                serializeBinding: _ => new Bits[0],
                serializeIdentifier: annotated =>
            {
                return(Encoding.EncodeNumber(annotated.Expression.Term));
            });

            var serializer2 = new Serialization <Guid, Guid>(
                useDeclarationF: identifier => new Term <Guid, Guid>(new TermF <Guid, Guid, ITerm <Guid, Guid> > .Variable(identifier)),
                serializeBinding: identifier => new Bits[0],
                serializeIdentifier: annotated =>
            {
                var identifier = annotated.Expression.Term;

                var index = annotated.Environment.IndexOf(
                    selector: binding =>
                {
                    return(binding.Term.Equals(identifier) ? Match.Yes : Match.Potentially);
                });

                return(Encoding.EncodeNumber(index));
            });

            var serializer3 = new Serialization <Guid, Guid>(
                useDeclarationF: identifier => new Term <Guid, Guid>(new TermF <Guid, Guid, ITerm <Guid, Guid> > .Variable(identifier)),
                serializeBinding: identifier => new Bits[0],
                serializeIdentifier: annotated =>
            {
                var identifier = annotated.Expression.Term;

                var index = annotated.Environment.IndexOf(
                    selector: binding =>
                {
                    if (binding.Universe.Rank != annotated.Expression.Universe.Rank)
                    {
                        return(Match.No);
                    }

                    return(binding.Term.Equals(identifier) ? Match.Yes : Match.Potentially);
                });

                return(Encoding.EncodeNumber(index));
            });

            var bits = serializer.SerializeFully(echoN).ToArray();

            Console.WriteLine("Size: " + bits.Length);
            foreach (var bit in bits)
            {
                Console.Write(bit + ", ");
            }

            Console.WriteLine();
            Console.WriteLine();

            var bits2 = serializer2.SerializeFully(echo).ToArray();

            Console.WriteLine("Size: " + bits2.Length);
            foreach (var bit in bits2)
            {
                Console.Write(bit + ", ");
            }

            Console.WriteLine();
            Console.WriteLine();

            var bits3 = serializer3.SerializeFully(echo).ToArray();

            Console.WriteLine("Size: " + bits3.Length);
            foreach (var bit in bits3)
            {
                Console.Write(bit + ", ");
            }

            Console.WriteLine();
            Console.WriteLine();

            var cps = Compilation.Compile(echo);

            var display = new TypeTheory.ContinuationPassing.Display <Guid>(id => id.ToString("D")[0].ToString());

            Console.WriteLine(display.ToString(cps));

            Console.ReadLine();
        }
        /// <summary>
        /// Emits the compilation into given <see cref="ModuleBuilder"/> using Reflection.Emit APIs.
        /// </summary>
        /// <param name="compilation">Compilation.</param>
        /// <param name="moduleBuilder">
        /// The module builder to add the types into. Can be reused for multiple compilation units.
        /// </param>
        /// <param name="assemblyLoader">
        /// Loads an assembly given an <see cref="AssemblyIdentity"/>.
        /// This callback is used for loading assemblies referenced by the compilation.
        /// <see cref="System.Reflection.Assembly.Load(AssemblyName)"/> is used if not specified.
        /// </param>
        /// <param name="assemblySymbolMapper">
        /// Applied when converting assembly symbols to assembly references.
        /// <see cref="IAssemblySymbol"/> is mapped to its <see cref="IAssemblySymbol.Identity"/> by default.
        /// </param>
        /// <param name="cancellationToken">Can be used to cancel the emit process.</param>
        /// <param name="recoverOnError">If false the method returns an unsuccessful result instead of falling back to CCI writer.</param>
        /// <param name="compiledAssemblyImage">Assembly image, returned only if we fallback to CCI writer.</param>
        /// <param name="entryPoint">An entry point or null if not applicable or on failure.</param>
        /// <param name="diagnostics">Diagnostics.</param>
        /// <returns>True on success, false if a compilation error occurred or the compilation doesn't contain any code or declarations.</returns>
        /// <remarks>
        /// Reflection.Emit doesn't support all metadata constructs. If an unsupported construct is
        /// encountered a metadata writer that procudes uncollectible code is used instead. This is
        /// indicated by
        /// <see cref="ReflectionEmitResult.IsUncollectible"/> flag on the result.
        ///
        /// Reusing <see cref="System.Reflection.Emit.ModuleBuilder"/> may be beneficial in certain
        /// scenarios. For example, when emitting a sequence of code snippets one at a time (like in
        /// REPL). All the snippets can be compiled into a single module as long as the types being
        /// emitted have unique names. Reusing a single module/assembly reduces memory overhead. On
        /// the other hand, collectible assemblies are units of collection. Defining too many
        /// unrelated types in a single assemly might prevent the unused types to be collected.
        ///
        /// No need to provide a name override when using Reflection.Emit, since the assembly already
        /// exists.
        /// </remarks>
        /// <exception cref="InvalidOperationException">Referenced assembly can't be resolved.</exception>
        internal static bool Emit(
            this Compilation compilation,
            ModuleBuilder moduleBuilder,
            AssemblyLoader assemblyLoader,
            Func <IAssemblySymbol, AssemblyIdentity> assemblySymbolMapper,
            bool recoverOnError,
            DiagnosticBag diagnostics,
            CancellationToken cancellationToken,
            out MethodInfo entryPoint,
            out byte[] compiledAssemblyImage)
        {
            compiledAssemblyImage = default(byte[]);

            var moduleBeingBuilt = compilation.CreateModuleBuilder(
                emitOptions: EmitOptions.Default,
                manifestResources: null,
                assemblySymbolMapper: assemblySymbolMapper,
                testData: null,
                diagnostics: diagnostics,
                cancellationToken: cancellationToken);

            if (moduleBeingBuilt == null)
            {
                entryPoint = null;
                return(false);
            }

            if (!compilation.Compile(
                    moduleBeingBuilt,
                    win32Resources: null,
                    xmlDocStream: null,
                    generateDebugInfo: false,
                    diagnostics: diagnostics,
                    filterOpt: null,
                    cancellationToken: cancellationToken))
            {
                entryPoint = null;
                return(false);
            }

            Cci.IMethodReference cciEntryPoint = moduleBeingBuilt.EntryPoint;

            cancellationToken.ThrowIfCancellationRequested();

            DiagnosticBag metadataDiagnostics = DiagnosticBag.GetInstance();

            var context = new EmitContext((Cci.IModule)moduleBeingBuilt, null, metadataDiagnostics);

            // try emit via Reflection.Emit
            try
            {
                var referencedAssemblies = from referencedAssembly in compilation.GetBoundReferenceManager().GetReferencedAssemblies()
                                           let peReference = referencedAssembly.Key as PortableExecutableReference
                                                             select KeyValuePair.Create(
                    moduleBeingBuilt.Translate(referencedAssembly.Value, metadataDiagnostics),
                    (peReference != null)?peReference.FilePath : null);

                entryPoint = ReflectionEmitter.Emit(
                    context,
                    referencedAssemblies,
                    moduleBuilder,
                    assemblyLoader ?? AssemblyLoader.Default,
                    cciEntryPoint,
                    cancellationToken);

                // translate metadata errors.
                return(compilation.FilterAndAppendAndFreeDiagnostics(diagnostics, ref metadataDiagnostics));
            }
            catch (TypeLoadException)
            {
                // attempted to emit reference to a type that can't be loaded (has invalid metadata)
            }
            catch (NotSupportedException)
            {
                // nop
            }

            // TODO (tomat):
            //
            // Another possible approach would be to just return an error, that we can't emit via
            // Ref.Emit and let the user choose another method of emitting. For that we would want
            // to preserve the state of the Emit.Assembly object with all the compiled methods so
            // that the subsequent emit doesn't need to compile method bodies again.

            // TODO (tomat):
            //
            // If Ref.Emit fails to emit the code the type builders already created will stay
            // defined on the module builder. Ideally we would clean them up but Ref.Emit doesn't
            // provide any API to do so. In fact it also keeps baked TypeBuilders alive as well.

            if (!recoverOnError)
            {
                metadataDiagnostics.Free();
                entryPoint = null;
                return(false);
            }

            using (var stream = new System.IO.MemoryStream())
            {
                Cci.PeWriter.WritePeToStream(
                    context,
                    compilation.MessageProvider,
                    stream,
                    pdbWriterOpt: null,
                    allowMissingMethodBodies: false,
                    deterministic: false,
                    cancellationToken: cancellationToken);

                compiledAssemblyImage = stream.ToArray();
            }

            var compiledAssembly = Assembly.Load(compiledAssemblyImage);

            entryPoint = (cciEntryPoint != null) ? ReflectionEmitter.ResolveEntryPoint(compiledAssembly, cciEntryPoint, context) : null;

            // translate metadata errors.
            return(compilation.FilterAndAppendAndFreeDiagnostics(diagnostics, ref metadataDiagnostics));
        }
        /// <summary>
        /// Emits the compilation into given <see cref="ModuleBuilder"/> using Reflection.Emit APIs.
        /// </summary>
        /// <param name="compilation">Compilation.</param>
        /// <param name="cancellationToken">Can be used to cancel the emit process.</param>
        /// <param name="compiledAssemblyImage">Assembly image, returned only if we fallback to CCI writer.</param>
        /// <param name="entryPointTypeName">An entry point or null on failure.</param>
        /// <param name="entryPointMethodName">An entry point or null on failure.</param>
        /// <param name="diagnostics">Diagnostics.</param>
        /// <returns>True on success, false if a compilation error occurred or the compilation doesn't contain any code or declarations.</returns>
        /// <exception cref="InvalidOperationException">Referenced assembly can't be resolved.</exception>
        internal static bool Emit(
            this Compilation compilation,
            DiagnosticBag diagnostics,
            out string entryPointTypeName,
            out string entryPointMethodName,
            out byte[] compiledAssemblyImage,
            CancellationToken cancellationToken)
        {
            compiledAssemblyImage = null;

            var moduleBeingBuilt = compilation.CreateModuleBuilder(
                emitOptions: EmitOptions.Default,
                manifestResources: null,
                testData: null,
                diagnostics: diagnostics,
                cancellationToken: cancellationToken);

            if (moduleBeingBuilt == null)
            {
                entryPointTypeName   = null;
                entryPointMethodName = null;
                return(false);
            }

            if (!compilation.Compile(
                    moduleBeingBuilt,
                    win32Resources: null,
                    xmlDocStream: null,
                    emittingPdb: false,
                    diagnostics: diagnostics,
                    filterOpt: null,
                    cancellationToken: cancellationToken))
            {
                entryPointTypeName   = null;
                entryPointMethodName = null;
                return(false);
            }

            cancellationToken.ThrowIfCancellationRequested();

            DiagnosticBag metadataDiagnostics = DiagnosticBag.GetInstance();

            var context = new EmitContext((Cci.IModule)moduleBeingBuilt, null, metadataDiagnostics);

            using (var stream = new System.IO.MemoryStream())
            {
                Cci.PeWriter.WritePeToStream(
                    context,
                    compilation.MessageProvider,
                    () => stream,
                    nativePdbWriterOpt: null,
                    pdbPathOpt: null,
                    allowMissingMethodBodies: false,
                    deterministic: false,
                    cancellationToken: cancellationToken);

                compiledAssemblyImage = stream.ToArray();
            }

            var containingType = (Cci.INamespaceTypeReference)moduleBeingBuilt.EntryPoint.GetContainingType(context);

            entryPointTypeName   = MetadataHelpers.BuildQualifiedName(containingType.NamespaceName, Cci.MetadataWriter.GetMangledName(containingType));
            entryPointMethodName = moduleBeingBuilt.EntryPoint.Name;

            // translate metadata errors.
            return(compilation.FilterAndAppendAndFreeDiagnostics(diagnostics, ref metadataDiagnostics));
        }
Beispiel #11
0
        static int Main(string[] args)
        {
            //
            // Start loading in the background
            //
            Library.LoadStandardLibrariesAsync();

            //
            // Inputs
            //
            var files       = new List <string> ();
            var extraArgs   = new List <string> ();
            var outName     = "";
            var safeMemory  = false;
            var showHelp    = false;
            var showVersion = false;

            //
            // Parse command line
            //
            for (int i = 0; i < args.Length;)
            {
                var a = args[i];
                if (a[0] == '-')
                {
                    if (a == "-o")
                    {
                        if (i + 1 < args.Length)
                        {
                            outName = args[i + 1];
                            i      += 2;
                        }
                        else
                        {
                            i++;
                        }
                    }
                    else if (a == "-h" || a == "--help" || a == "-?")
                    {
                        showHelp = true;
                        i++;
                    }
                    else if (a == "-v" || a == "--version")
                    {
                        showVersion = true;
                        i++;
                    }
                    else if (a == "--safe-memory")
                    {
                        safeMemory = true;
                        i++;
                    }
                    else
                    {
                        extraArgs.Add(a);
                        i++;
                    }
                }
                else
                {
                    files.Add(a);
                    i++;
                }
            }

            if (showVersion)
            {
                var version = typeof(Program).Assembly.GetName().Version;
                Console.WriteLine($"Krueger Systems IRIL {version}");
                if (!showHelp)
                {
                    return(0);
                }
            }

            if (showHelp)
            {
                Console.WriteLine($"OVERVIEW: C/C++ to .NET assembly compiler by Frank A. Krueger");
                Console.WriteLine();
                Console.WriteLine($"USAGE: iril [options] <inputs>");
                Console.WriteLine();
                Console.WriteLine($"INPUTS: .c and .ll files");
                Console.WriteLine();
                Console.WriteLine($"OPTIONS:");
                Console.WriteLine($"  -o <asm file>      Path to the assembly .dll to output");
                Console.WriteLine($"  -h, -?, --help     Display this help");
                Console.WriteLine($"  -v, --version      Display the version");
                Console.WriteLine($"  --safe-memory      Verify memory accesses to make code safe from crashes");
                return(0);
            }

            //
            // Cleanup input
            //
            if (string.IsNullOrWhiteSpace(outName) && files.Count > 0)
            {
                outName = Path.ChangeExtension(Path.GetFileName(files[0]), ".dll");
            }

            //
            // Compile C Files
            //
            var clang   = new ClangTool();
            var cfiles  = files.Where(x => clang.InputExtensions.Contains(Path.GetExtension(x)));
            var context = new ToolContext {
                InputFiles     = cfiles.ToArray(),
                ExtraArguments = extraArgs.ToArray(),
                OutputFile     = outName,
            };
            var cllfiles = clang.Run(context);

            var llfiles = (from f in files
                           let e = Path.GetExtension(f)
                                   where e == ".ll" || e == ".o"
                                   select f)
                          .Concat(cllfiles)
                          .ToList();

            //
            // Early out
            //
            if (llfiles.Count == 0)
            {
                if (context.ExtraArguments.Contains("-c"))
                {
                    return(0);
                }
                Error("No inputs");
                return(1);
            }

            try {
                //
                // Parse
                //
                Info($"Parsing {llfiles.Count} files...");
                var modules = llfiles.AsParallel().Select(x => {
                    var code = File.ReadAllText(x);
                    return(Module.Parse(code, x));
                }).ToList();

                //
                // Compile
                //
                Info("Compiling...");
                var comp = new Compilation(new CompilationOptions(modules, outName, safeMemory: safeMemory));
                comp.Compile();

                //
                // Show errors
                //
                var errors = (from m in modules from e in m.Errors select e)
                             .Concat(comp.Messages)
                             .OrderBy(x => x.FilePath).ThenBy(x => x.Text)
                             .ToList();
                foreach (var e in errors)
                {
                    Console.Write("iril: ");
                    if (e.Type == MessageType.Error)
                    {
                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.Write("error: ");
                    }
                    else
                    {
                        Console.ForegroundColor = ConsoleColor.Yellow;
                        Console.Write("warning: ");
                    }
                    Console.ResetColor();

                    if (!string.IsNullOrEmpty(e.FilePath))
                    {
                        Console.ForegroundColor = ConsoleColor.Gray;
                        Console.Write(e.FilePath);
                        Console.Write(": ");
                        Console.ResetColor();
                    }

                    Console.WriteLine(e.Text);

                    if (!string.IsNullOrEmpty(e.Surrounding))
                    {
                        Console.WriteLine(e.Surrounding);
                    }
#if DEBUG
                    if (e.Exception != null)
                    {
                        Console.ForegroundColor = ConsoleColor.DarkYellow;
                        Console.WriteLine(e.Exception);
                        Console.ResetColor();
                    }
#endif
                }

                //
                // Output
                //
                Info($"Writing {outName}...");
                comp.WriteAssembly(outName);

                if (errors.Count > 0)
                {
                    Info($"{errors.Count(x => x.Type == MessageType.Error)} errors, {errors.Count (x => x.Type == MessageType.Warning)} warnings");
                }

                return(modules.Any(m => m.HasErrors) ? 3 : 0);
            }
            catch (Exception ex) {
                Error(ex.ToString());
                return(2);
            }
        }