diff --git a/README.md b/README.md index 2b6cfb4..bd50be9 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,12 @@ Build a Linux ELF executable from `examples/hello`: ./examples/hello/hello ``` +To produce verbose output, add the `-v` flag: + +```shell +./q build examples/hello -v +``` + ## Documentation ### [main.go](main.go) diff --git a/examples/hello/hello.q b/examples/hello/hello.q index 2981aa1..6c3365a 100644 --- a/examples/hello/hello.q +++ b/examples/hello/hello.q @@ -1,7 +1,7 @@ main() { - let write = 1 - let exit = 60 - let stdout = 1 + write := 1 + exit := 60 + stdout := 1 syscall(write, stdout, 4194305, 3) syscall(exit, 0) diff --git a/src/build/Function.go b/src/build/Function.go index 2a2d670..83367de 100644 --- a/src/build/Function.go +++ b/src/build/Function.go @@ -18,6 +18,7 @@ type Function struct { Body token.List Variables map[string]*Variable Assembler asm.Assembler + Error error } // Compile turns a function into machine code. @@ -27,35 +28,53 @@ func (f *Function) Compile() { } 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:] - - if config.Verbose { - fmt.Println("[variable]", name, value) - } - - f.Variables[name] = &Variable{ - Value: line[3:], - IsConst: true, - } - - case "return": - f.Assembler.Return() - } + if config.Verbose { + fmt.Println("[line]", line) } - if line[0].Kind == token.Identifier && line[0].Text() == "syscall" { + 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 @@ -78,7 +97,11 @@ func (f *Function) Compile() { switch list[0].Kind { case token.Identifier: name := list[0].Text() - variable := f.Variables[name] + variable, exists := f.Variables[name] + + if !exists { + return fmt.Errorf("Unknown identifier '%s'", name) + } if !variable.IsConst { panic("Not implemented yet") @@ -101,7 +124,7 @@ func (f *Function) Compile() { } } - f.Assembler.Return() + return nil } // Lines returns the lines in the function body. diff --git a/src/build/token/Keywords.go b/src/build/token/Keywords.go index a8eec3c..adb8de7 100644 --- a/src/build/token/Keywords.go +++ b/src/build/token/Keywords.go @@ -2,6 +2,5 @@ package token // Keywords defines the keywords used in the language. var Keywords = map[string]bool{ - "let": true, "return": true, } diff --git a/src/build/token/List.go b/src/build/token/List.go index 9cef514..be3a1ff 100644 --- a/src/build/token/List.go +++ b/src/build/token/List.go @@ -13,7 +13,7 @@ func (list List) String() string { var last Token for _, t := range list { - if last.Kind == Keyword || last.Kind == Separator { + if last.Kind == Keyword || last.Kind == Separator || last.Kind == Operator || t.Kind == Operator { builder.WriteByte(' ') } diff --git a/src/build/token/Tokenize.go b/src/build/token/Tokenize.go index 53cdbb6..ac5d8b0 100644 --- a/src/build/token/Tokenize.go +++ b/src/build/token/Tokenize.go @@ -70,9 +70,6 @@ func Tokenize(buffer []byte) List { case ']': tokens = append(tokens, Token{ArrayEnd, i, arrayEndBytes}) - case '=', ':', '+', '-', '*', '/', '<', '>', '!': - tokens = append(tokens, Token{Operator, i, buffer[i : i+1]}) - // Separator case ',': tokens = append(tokens, Token{Separator, i, separatorBytes}) @@ -122,6 +119,24 @@ func Tokenize(buffer []byte) List { continue } + + // Operators + if isOperator(buffer[i]) { + position := i + i++ + + for i < len(buffer) && isOperator(buffer[i]) { + i++ + } + + tokens = append(tokens, Token{ + Operator, + position, + buffer[position:i], + }) + + continue + } } i++