q/src/build/Function.go

131 lines
2.4 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
}
// 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 config.Verbose {
fmt.Println("[line]", line)
}
if len(line) == 0 {
continue
}
if line[0].Kind == token.Keyword {
switch line[0].Text() {
case "let":
name := line[1].Text()
value := line[3:]
fmt.Println("[variable]", name, value)
f.Variables[name] = &Variable{
Value: line[3:],
IsConst: true,
}
case "return":
f.Assembler.Return()
}
}
if line[0].Kind == token.Identifier && line[0].Text() == "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 := f.Variables[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()
}
}
f.Assembler.Return()
}
// 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
}