q/src/build/Function.go

157 lines
2.9 KiB
Go

package build
import (
"fmt"
"strconv"
"git.akyoto.dev/cli/q/src/build/arch/x64"
"git.akyoto.dev/cli/q/src/build/asm"
"git.akyoto.dev/cli/q/src/build/config"
"git.akyoto.dev/cli/q/src/build/token"
"git.akyoto.dev/go/color/ansi"
)
// Function represents a function.
type Function struct {
Name string
Head token.List
Body token.List
Variables map[string]*Variable
Assembler asm.Assembler
Error error
}
// Compile turns a function into machine code.
func (f *Function) Compile() {
if config.Verbose {
ansi.Underline.Println(f.Name)
}
for _, line := range f.Lines() {
if len(line) == 0 {
continue
}
if config.Verbose {
fmt.Println("[line]", line)
}
err := f.compileInstruction(line)
if err != nil {
ansi.Red.Println(err)
return
}
}
f.Assembler.Return()
}
// compileInstruction compiles a single instruction.
func (f *Function) compileInstruction(line token.List) error {
switch line[0].Kind {
case token.Keyword:
switch line[0].Text() {
case "return":
f.Assembler.Return()
}
case token.Identifier:
if len(line) >= 2 && line[1].Kind == token.Operator && line[1].Text() == ":=" {
name := line[0].Text()
value := line[2:]
if config.Verbose {
fmt.Println("[variable]", name, value)
}
f.Variables[name] = &Variable{
Value: line[2:],
IsConst: true,
}
return nil
}
switch line[0].Text() {
case "syscall":
paramTokens := line[2 : len(line)-1]
start := 0
i := 0
var parameters []token.List
for i < len(paramTokens) {
if paramTokens[i].Kind == token.Separator {
parameters = append(parameters, paramTokens[start:i])
start = i + 1
}
i++
}
if i != start {
parameters = append(parameters, paramTokens[start:i])
}
for i, list := range parameters {
switch list[0].Kind {
case token.Identifier:
name := list[0].Text()
variable, exists := f.Variables[name]
if !exists {
return fmt.Errorf("Unknown identifier '%s'", name)
}
if !variable.IsConst {
panic("Not implemented yet")
}
n, _ := strconv.Atoi(variable.Value[0].Text())
f.Assembler.MoveRegisterNumber(x64.SyscallArgs[i], uint64(n))
case token.Number:
value := list[0].Text()
n, _ := strconv.Atoi(value)
f.Assembler.MoveRegisterNumber(x64.SyscallArgs[i], uint64(n))
default:
panic("Unknown expression")
}
}
f.Assembler.Syscall()
}
}
return nil
}
// Lines returns the lines in the function body.
func (f *Function) Lines() []token.List {
var (
lines []token.List
start = 0
i = 0
)
for i < len(f.Body) {
if f.Body[i].Kind == token.NewLine {
lines = append(lines, f.Body[start:i])
start = i + 1
}
i++
}
if i != start {
lines = append(lines, f.Body[start:i])
}
return lines
}
// String returns the function name.
func (f *Function) String() string {
return f.Name
}