package build import ( "fmt" "git.akyoto.dev/cli/q/src/build/ast" "git.akyoto.dev/cli/q/src/build/config" "git.akyoto.dev/cli/q/src/build/fs" "git.akyoto.dev/cli/q/src/build/token" "git.akyoto.dev/cli/q/src/errors" ) // Function represents a function. type Function struct { File *fs.File Name string Body token.List compiler } // Compile turns a function into machine code. func (f *Function) Compile() { defer close(f.finished) f.assembler.Label(f.Name) f.err = f.CompileTokens(f.Body) f.assembler.Return() } // CompileTokens compiles a token list. func (f *Function) CompileTokens(tokens token.List) error { tree, err := ast.Parse(tokens) if err != nil { err.(*errors.Error).File = f.File return err } return f.CompileAST(tree) } // CompileAST compiles an abstract syntax tree. func (f *Function) CompileAST(tree ast.AST) error { for _, node := range tree { if config.Verbose { f.Logf("%T %s", node, node) } if config.Assembler { f.debug = append(f.debug, debug{ position: len(f.assembler.Instructions), source: node, }) } err := f.CompileNode(node) if err != nil { return err } } return nil } // CompileNode compiles a node in the AST. func (f *Function) CompileNode(node ast.Node) error { switch node := node.(type) { case *ast.Call: return f.CompileFunctionCall(node.Expression) case *ast.Define: return f.CompileVariableDefinition(node) case *ast.Return: return f.CompileReturn(node) case *ast.Loop: return f.CompileLoop(node) default: panic("Unknown AST type") } } // Logf formats a message for verbose output. func (f *Function) Logf(format string, data ...any) { fmt.Printf("[%s @ %d] %s\n", f, len(f.assembler.Instructions), fmt.Sprintf(format, data...)) } // String returns the function name. func (f *Function) String() string { return f.Name } // Wait will block until the compilation finishes. func (f *Function) Wait() { <-f.finished }