q/src/compiler/Compile.go

121 lines
2.4 KiB
Go

package compiler
import (
"sync"
"git.akyoto.dev/cli/q/src/core"
"git.akyoto.dev/cli/q/src/errors"
"git.akyoto.dev/cli/q/src/fs"
"git.akyoto.dev/cli/q/src/types"
)
// Compile waits for the scan to finish and compiles all functions.
func Compile(files <-chan *fs.File, functions <-chan *core.Function, structs <-chan *types.Struct, errs <-chan error) (Result, error) {
result := Result{}
allFiles := make([]*fs.File, 0, 8)
allFunctions := map[string]*core.Function{}
allStructs := map[string]*types.Struct{}
for functions != nil || files != nil || errs != nil {
select {
case function, ok := <-functions:
if !ok {
functions = nil
continue
}
function.Functions = allFunctions
function.Structs = allStructs
allFunctions[function.UniqueName] = function
case structure, ok := <-structs:
if !ok {
structs = nil
continue
}
allStructs[structure.UniqueName] = structure
case file, ok := <-files:
if !ok {
files = nil
continue
}
allFiles = append(allFiles, file)
case err, ok := <-errs:
if !ok {
errs = nil
continue
}
return result, err
}
}
// Calculate size of structs
for _, structure := range allStructs {
structure.Update(allStructs)
}
// Resolve the types
for _, function := range allFunctions {
err := function.ResolveTypes()
if err != nil {
return result, err
}
}
// Start parallel compilation
CompileFunctions(allFunctions)
// Report errors if any occurred
for _, function := range allFunctions {
if function.Err != nil {
return result, function.Err
}
result.InstructionCount += len(function.Assembler.Instructions)
result.DataCount += len(function.Assembler.Data)
}
// Check for unused imports in all files
for _, file := range allFiles {
for _, pkg := range file.Imports {
if !pkg.Used {
return result, errors.New(&errors.UnusedImport{Package: pkg.Path}, file, pkg.Position)
}
}
}
// Check for existence of `main`
main, exists := allFunctions["main.main"]
if !exists {
return result, errors.MissingMainFunction
}
result.Main = main
result.Functions = allFunctions
result.finalize()
return result, nil
}
// CompileFunctions starts a goroutine for each function compilation and waits for completion.
func CompileFunctions(functions map[string]*core.Function) {
wg := sync.WaitGroup{}
for _, function := range functions {
wg.Add(1)
go func() {
defer wg.Done()
function.Compile()
}()
}
wg.Wait()
}