115 lines
2.4 KiB
Go
115 lines
2.4 KiB
Go
package core
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"git.akyoto.dev/cli/q/src/build/arch/x64"
|
|
"git.akyoto.dev/cli/q/src/build/asm"
|
|
"git.akyoto.dev/cli/q/src/build/ast"
|
|
"git.akyoto.dev/cli/q/src/build/cpu"
|
|
"git.akyoto.dev/cli/q/src/build/errors"
|
|
"git.akyoto.dev/cli/q/src/build/fs"
|
|
"git.akyoto.dev/cli/q/src/build/token"
|
|
)
|
|
|
|
// Function represents the smallest unit of code.
|
|
type Function struct {
|
|
Name string
|
|
File *fs.File
|
|
Body token.List
|
|
state
|
|
}
|
|
|
|
// NewFunction creates a new function.
|
|
func NewFunction(name string, file *fs.File, body token.List) *Function {
|
|
return &Function{
|
|
Name: name,
|
|
File: file,
|
|
Body: body,
|
|
state: state{
|
|
assembler: asm.Assembler{
|
|
Instructions: make([]asm.Instruction, 0, 32),
|
|
},
|
|
cpu: cpu.CPU{
|
|
Input: x64.CallRegisters,
|
|
General: x64.GeneralRegisters,
|
|
Syscall: x64.SyscallRegisters,
|
|
Output: x64.ReturnValueRegisters,
|
|
},
|
|
variables: map[string]*Variable{},
|
|
finished: make(chan struct{}),
|
|
},
|
|
}
|
|
}
|
|
|
|
// 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 {
|
|
err := f.CompileASTNode(node)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// CompileASTNode compiles a node in the AST.
|
|
func (f *Function) CompileASTNode(node ast.Node) error {
|
|
switch node := node.(type) {
|
|
case *ast.Assign:
|
|
return f.CompileAssign(node)
|
|
|
|
case *ast.Call:
|
|
return f.CompileCall(node.Expression)
|
|
|
|
case *ast.Define:
|
|
return f.CompileDefinition(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
|
|
}
|