public static async Task <GlobalContext> CompileModule( TopLevel toplevel, IResourceProvider resources) { // We may have some Dictionary <string, TopLevel> modules = new Dictionary <string, TopLevel>(); modules[toplevel.ModuleName] = toplevel; List <Variable> specialMappedTypeVariables = GetSpecialMappedVariables(); // If it has a dotwalk, it must be an import prefix List <ImportDetails> imports = toplevel .Walk() .ToList() .MatchWith(specialMappedTypeVariables) .Select((NameExpression ne) => new ImportDetails(toplevel.ModuleName, ne)) .ToList(); for (int i = 0; i < imports.Count; i++) { // Allow people to compile modules standalone without a // compiler, but don't let them compile if there are // unresolved modules if (resources == null) { throw new NotImplementedException(); } // If we haven't already got the module, then we // will have to go parse it ourselves if (!modules.ContainsKey(imports[i].From)) { // TODO: Other ways of caching modules? if (!resources.TryGetResource(imports[i].From, out Stream stream)) { throw new NotImplementedException(); } StreamReader sr = new StreamReader(stream); TopLevel tl = await new Parser(sr).ParseModule(imports[i].From); imports.AddRange( tl.Walk() .ToList() .MatchWith(specialMappedTypeVariables) .Select((NameExpression ne) => new ImportDetails(imports[i].To, ne)) ); modules[imports[i].From] = tl; } Variable variable = modules[imports[i].From] .DeclaredVariables .FirstOrDefault(variable => variable.Name == imports[i].Name); if (variable == null) { throw new NotImplementedException(); } imports[i].Variable = variable; } // We need to bind twice in order to determine some of // the types, such as for a method in a class first, // then bind again for its usage foreach (TopLevel module in modules.Values) { module.Bind(new Binder()); } if (toplevel.ModuleName == null) { toplevel.Bind(new Binder()); } foreach (TopLevel module in modules.Values) { module.Bind(new Binder()); } if (toplevel.ModuleName == null) { toplevel.Bind(new Binder()); } Dictionary <string, GlobalContext> contexts = new Dictionary <string, GlobalContext>(); foreach (TopLevel module in modules.Values) { contexts[module.ModuleName] = BuildContext(module); } // TODO: what if someone tries to import an import? foreach (ImportDetails import in imports) { contexts[import.To].AssignVariable( import.NameExpression.Variable.Name, contexts[import.From].LookupVariable(import.Name) ); } return(contexts[toplevel.ModuleName]); GlobalContext BuildContext(TopLevel toplevel) { GlobalContext context = new GlobalContext(); InternalLambda initializationLambda = new InternalLambda { closures = new Closure[0], description = new InternalLambdaDescription { argTypes = new RedwoodType[0], // All fields should go the global definition, but some // temporary variables may be used to define constructors stackSize = toplevel.StackSize, closureSize = 0, instructions = toplevel.Compile().ToArray(), returnType = null }, context = context }; // Populate the global context initializationLambda.Run(); return(context); } }