diff --git a/src/compiler/Compile.go b/src/compiler/Compile.go index 4fd2a2c..9a9a774 100644 --- a/src/compiler/Compile.go +++ b/src/compiler/Compile.go @@ -33,28 +33,9 @@ func Compile(build *config.Build) (*core.Environment, error) { env.Main = main - // Parse struct field types and calculate the size of all structs. - // We couldn't do that during the scan phase because it's possible - // that a field references a type that will only be known after the - // full scan is finished. - err = parseStructs(env.Structs(), env) - - if err != nil { - return nil, err - } - - // Parse global variable types. - err = parseGlobals(env.Globals(), env) - - if err != nil { - return nil, err - } - - // Parse input and output types so we have type information - // ready for all functions before parallel compilation starts. - // This ensures that the function compilers have access to - // type checking for all function calls. - err = parseFunctions(env.Functions(), env) + // Resolve all types so that we have type information ready + // for all functions before parallel compilation starts. + err = env.ResolveTypes() if err != nil { return nil, err @@ -63,7 +44,9 @@ func Compile(build *config.Build) (*core.Environment, error) { // 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() }) + parallel(env.Functions(), func(f *core.Function) { + f.Compile() + }) // Report errors if any occurred for f := range env.Functions() { @@ -83,7 +66,9 @@ func Compile(build *config.Build) (*core.Environment, error) { // 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() }) + parallel(env.LiveFunctions(), func(f *core.Function) { + f.Assemble() + }) return env, nil } \ No newline at end of file diff --git a/src/compiler/errors.go b/src/compiler/errors.go index 4cb0421..9643789 100644 --- a/src/compiler/errors.go +++ b/src/compiler/errors.go @@ -11,16 +11,6 @@ var ( MissingMainFunction = errors.String("Missing main function") ) -// CycleDetected error is created when an invalid dependency cycle was detected. -type CycleDetected struct { - A string - B string -} - -func (err *CycleDetected) Error() string { - return fmt.Sprintf("Cycle detected: '%s' depends on '%s' which depends on '%s'", err.A, err.B, err.A) -} - // UnusedImport error is created when an import is never used. type UnusedImport struct { Package string diff --git a/src/core/Environment.go b/src/core/Environment.go index 4dcb3ac..7730bf7 100644 --- a/src/core/Environment.go +++ b/src/core/Environment.go @@ -103,6 +103,23 @@ func (env *Environment) LiveFunctions() iter.Seq[*Function] { } } +// ResolveTypes resolves all the type tokens in structs, globals and function parameters. +func (env *Environment) ResolveTypes() error { + err := env.parseStructs(env.Structs()) + + if err != nil { + return err + } + + err = env.parseGlobals(env.Globals()) + + if err != nil { + return err + } + + return env.parseParameters(env.Functions()) +} + // Structs returns an iterator over all structs. func (env *Environment) Structs() iter.Seq[*types.Struct] { return func(yield func(*types.Struct) bool) { diff --git a/src/core/errors.go b/src/core/errors.go index 39ab601..eea2641 100644 --- a/src/core/errors.go +++ b/src/core/errors.go @@ -25,6 +25,16 @@ var ( UnnecessaryCast = errors.String("Unnecessary type cast") ) +// CycleDetected error is created when an invalid dependency cycle was detected. +type CycleDetected struct { + A string + B string +} + +func (err *CycleDetected) Error() string { + return fmt.Sprintf("Cycle detected: '%s' depends on '%s' which depends on '%s'", err.A, err.B, err.A) +} + // DefinitionCountMismatch error is created when the number of provided definitions doesn't match the return type. type DefinitionCountMismatch struct { Function string diff --git a/src/compiler/parseGlobals.go b/src/core/parseGlobals.go similarity index 65% rename from src/compiler/parseGlobals.go rename to src/core/parseGlobals.go index bad52f8..0716463 100644 --- a/src/compiler/parseGlobals.go +++ b/src/core/parseGlobals.go @@ -1,13 +1,11 @@ -package compiler +package core import ( "iter" - - "git.urbach.dev/cli/q/src/core" ) // parseGlobals parses the tokens of global variables. -func parseGlobals(globals iter.Seq[*core.Global], env *core.Environment) error { +func (env *Environment) parseGlobals(globals iter.Seq[*Global]) error { for global := range globals { typ, err := env.TypeFromTokens(global.Tokens[1:], global.File) diff --git a/src/compiler/parseFunctions.go b/src/core/parseParameters.go similarity index 86% rename from src/compiler/parseFunctions.go rename to src/core/parseParameters.go index cb810be..bca962f 100644 --- a/src/compiler/parseFunctions.go +++ b/src/core/parseParameters.go @@ -1,16 +1,15 @@ -package compiler +package core import ( "iter" "strings" - "git.urbach.dev/cli/q/src/core" "git.urbach.dev/cli/q/src/token" "git.urbach.dev/cli/q/src/types" ) -// parseFunctions parses the tokens of the input and output types. -func parseFunctions(functions iter.Seq[*core.Function], env *core.Environment) error { +// parseParameters parses the tokens of the input and output types. +func (env *Environment) parseParameters(functions iter.Seq[*Function]) error { for f := range functions { f.Type = &types.Function{ Input: make([]types.Type, len(f.Input)), diff --git a/src/compiler/parseStructs.go b/src/core/parseStructs.go similarity index 77% rename from src/compiler/parseStructs.go rename to src/core/parseStructs.go index 3b57625..b60ded7 100644 --- a/src/compiler/parseStructs.go +++ b/src/core/parseStructs.go @@ -1,9 +1,8 @@ -package compiler +package core import ( "iter" - "git.urbach.dev/cli/q/src/core" "git.urbach.dev/cli/q/src/errors" "git.urbach.dev/cli/q/src/fs" "git.urbach.dev/cli/q/src/types" @@ -18,11 +17,11 @@ const ( ) // parseStructs parses the tokens of the struct field types. -func parseStructs(structs iter.Seq[*types.Struct], env *core.Environment) error { +func (env *Environment) parseStructs(structs iter.Seq[*types.Struct]) error { processed := map[*types.Struct]state{} for structure := range structs { - err := parseStruct(structure, env, processed) + err := env.parseStruct(structure, processed) if err != nil { return err @@ -33,7 +32,7 @@ func parseStructs(structs iter.Seq[*types.Struct], env *core.Environment) error } // parseStruct parses the field tokens of a single struct. -func parseStruct(structure *types.Struct, env *core.Environment, processed map[*types.Struct]state) error { +func (env *Environment) parseStruct(structure *types.Struct, processed map[*types.Struct]state) error { if processed[structure] == Finished { return nil } @@ -54,7 +53,7 @@ func parseStruct(structure *types.Struct, env *core.Environment, processed map[* if isStruct { switch processed[dependency] { case NotStarted: - err := parseStruct(dependency, env, processed) + err := env.parseStruct(dependency, processed) if err != nil { return err diff --git a/src/compiler/testdata/CycleDetected.q b/src/core/testdata/CycleDetected.q similarity index 100% rename from src/compiler/testdata/CycleDetected.q rename to src/core/testdata/CycleDetected.q