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 }