q/src/build/Function.go

98 lines
1.9 KiB
Go

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
}