package compiler import ( "git.urbach.dev/cli/q/src/config" "git.urbach.dev/cli/q/src/core" "git.urbach.dev/cli/q/src/errors" "git.urbach.dev/cli/q/src/resolver" "git.urbach.dev/cli/q/src/scanner" ) // Compile waits for the scan to finish and compiles all functions. func Compile(build *config.Build) (*core.Environment, error) { env, err := scanner.Scan(build) if err != nil { return nil, err } // Check for existence of `run.init` init := env.Function("run", "init") if init == nil { return nil, MissingInitFunction } env.Init = init // Check for existence of `main.main` main := env.Function("main", "main") if main == nil { return nil, MissingMainFunction } env.Main = main // Resolve all types so that we have type information ready // for all functions before parallel compilation starts. err = resolver.ResolveTypes(env) if err != nil { return nil, err } // Start parallel compilation of all functions. // We compile every function for syntax checks even if // they are thrown away later during dead code elimination. parallel(env.Functions(), func(f *core.Function) { f.Compile() }) // Report errors if any occurred err = findError(env.Functions()) if err != nil { return nil, err } // Check for unused imports in all files if build.LintUnusedImports { for _, file := range env.Files { for _, imp := range file.Imports { if !imp.IsUsed() { return nil, errors.New(&UnusedImport{Package: imp.Package}, file, imp.Tokens) } } } } // Check that every package has enough assertions if build.LintAssertionDensity { err = lintAssertionDensity(env) if err != nil { return nil, err } } // Now that we know which functions are alive, start parallel // assembly code generation only for the live functions. parallel(env.LiveFunctions(), func(f *core.Function) { f.Assemble() }) return env, nil }