Improved instruction parser

This commit is contained in:
Eduard Urbach 2024-06-15 14:46:44 +02:00
parent cf696a6f10
commit 57f1da10fe
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
7 changed files with 114 additions and 54 deletions

View File

@ -6,7 +6,6 @@ import (
// Compile compiles all the functions. // Compile compiles all the functions.
func Compile(functions <-chan *Function, errors <-chan error) (map[string]*Function, error) { func Compile(functions <-chan *Function, errors <-chan error) (map[string]*Function, error) {
wg := sync.WaitGroup{}
allFunctions := map[string]*Function{} allFunctions := map[string]*Function{}
for functions != nil || errors != nil { for functions != nil || errors != nil {
@ -25,17 +24,28 @@ func Compile(functions <-chan *Function, errors <-chan error) (map[string]*Funct
continue continue
} }
wg.Add(1)
go func() {
defer wg.Done()
function.Compile()
}()
allFunctions[function.Name] = function allFunctions[function.Name] = function
} }
} }
wg := sync.WaitGroup{}
for _, function := range allFunctions {
wg.Add(1)
go func() {
defer wg.Done()
function.Compile()
}()
}
wg.Wait() wg.Wait()
for _, function := range allFunctions {
if function.Error != nil {
return nil, function.Error
}
}
return allFunctions, nil return allFunctions, nil
} }

9
src/build/File.go Normal file
View File

@ -0,0 +1,9 @@
package build
import "git.akyoto.dev/cli/q/src/build/token"
// File represents a single source file.
type File struct {
Tokens token.List
Path string
}

View File

@ -14,6 +14,7 @@ import (
// Function represents a function. // Function represents a function.
type Function struct { type Function struct {
Name string Name string
File *File
Head token.List Head token.List
Body token.List Body token.List
Variables map[string]*Variable Variables map[string]*Variable
@ -24,32 +25,60 @@ type Function struct {
// Compile turns a function into machine code. // Compile turns a function into machine code.
func (f *Function) Compile() { func (f *Function) Compile() {
if config.Verbose { if config.Verbose {
ansi.Underline.Println(f.Name) ansi.Bold.Println(f.Name)
} }
for _, line := range f.Lines() { start := 0
if len(line) == 0 { groupLevel := 0
for i, t := range f.Body {
if start == i && (t.Kind == token.NewLine || t.Kind == token.BlockStart || t.Kind == token.BlockEnd) {
start = i + 1
continue continue
} }
if config.Verbose { switch t.Kind {
ansi.Dim.Print("[ ] ") case token.NewLine:
fmt.Println(line) if groupLevel > 0 {
} continue
}
err := f.compileInstruction(line) if start != -1 {
instruction := f.Body[start:i]
err := f.CompileInstruction(instruction)
if err != nil { if err != nil {
ansi.Red.Println(err) f.Error = err
return return
}
start = -1
}
start = i + 1
case token.GroupStart:
groupLevel++
case token.GroupEnd:
groupLevel--
case token.BlockStart:
// Add scope
case token.BlockEnd:
// Remove scope
} }
} }
f.Assembler.Return() f.Assembler.Return()
if config.Verbose { if config.Verbose {
fmt.Println()
ansi.Bold.Println(f.Name + ".asm")
for _, x := range f.Assembler.Instructions { for _, x := range f.Assembler.Instructions {
ansi.Dim.Print("[asm] ") ansi.Dim.Print(" ")
fmt.Print(x.Mnemonic.String()) fmt.Print(x.Mnemonic.String())
if x.Data != nil { if x.Data != nil {
@ -61,27 +90,44 @@ func (f *Function) Compile() {
} }
} }
// compileInstruction compiles a single instruction. // CompileInstruction compiles a single instruction.
func (f *Function) compileInstruction(line token.List) error { func (f *Function) CompileInstruction(line token.List) error {
switch line[0].Kind { if config.Verbose {
case token.Keyword: ansi.Dim.Print("│ ")
fmt.Println(line)
}
if line[0].Kind == token.Keyword {
switch line[0].Text() { switch line[0].Text() {
case "return": case "return":
f.Assembler.Return() f.Assembler.Return()
} }
case token.Identifier: return nil
if len(line) >= 2 && line[1].Kind == token.Define { }
if line[0].Kind == token.Identifier {
if len(line) < 2 {
return fmt.Errorf("error to be implemented")
}
if line[1].Kind == token.Define {
name := line[0].Text() name := line[0].Text()
value := line[2:] value := line[2:]
if len(value) == 0 {
return fmt.Errorf("error to be implemented")
// return errors.New(errors.MissingAssignmentValue, f.File.Path, f.File.Tokens, f.Cursor)
}
if config.Verbose { if config.Verbose {
ansi.Dim.Printf("[var] ") ansi.Dim.Printf("├── var ")
fmt.Println(name, value) fmt.Println(name, "=", value)
} }
f.Variables[name] = &Variable{ f.Variables[name] = &Variable{
Value: line[2:], Name: name,
Value: value,
IsConst: true, IsConst: true,
} }
@ -115,7 +161,7 @@ func (f *Function) compileInstruction(line token.List) error {
variable, exists := f.Variables[name] variable, exists := f.Variables[name]
if !exists { if !exists {
return fmt.Errorf("Unknown identifier '%s'", name) panic("Unknown identifier " + name)
} }
if !variable.IsConst { if !variable.IsConst {
@ -142,30 +188,6 @@ func (f *Function) compileInstruction(line token.List) error {
return nil 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. // String returns the function name.
func (f *Function) String() string { func (f *Function) String() string {
return f.Name return f.Name

View File

@ -86,6 +86,11 @@ func scanFile(path string, functions chan<- *Function) error {
tokens := token.Tokenize(contents) tokens := token.Tokenize(contents)
file := &File{
Tokens: tokens,
Path: path,
}
var ( var (
i = 0 i = 0
groupLevel = 0 groupLevel = 0
@ -216,6 +221,7 @@ func scanFile(path string, functions chan<- *Function) error {
functions <- &Function{ functions <- &Function{
Name: tokens[nameStart].Text(), Name: tokens[nameStart].Text(),
File: file,
Head: tokens[paramsStart:bodyStart], Head: tokens[paramsStart:bodyStart],
Body: tokens[bodyStart : i+1], Body: tokens[bodyStart : i+1],
Variables: map[string]*Variable{}, Variables: map[string]*Variable{},

View File

@ -4,6 +4,7 @@ import "git.akyoto.dev/cli/q/src/build/token"
// Variable represents a variable in a function. // Variable represents a variable in a function.
type Variable struct { type Variable struct {
Name string
Value token.List Value token.List
IsConst bool IsConst bool
} }

View File

@ -1,5 +1,7 @@
package token package token
import "fmt"
// Token represents a single element in a source file. // Token represents a single element in a source file.
// The characters that make up an identifier are grouped into a single token. // The characters that make up an identifier are grouped into a single token.
// This makes parsing easier and allows us to do better syntax checks. // This makes parsing easier and allows us to do better syntax checks.
@ -9,6 +11,11 @@ type Token struct {
Bytes []byte Bytes []byte
} }
// String creates a human readable representation for debugging purposes.
func (t Token) String() string {
return fmt.Sprintf("%s %s", t.Kind, t.Text())
}
// Text returns the token text. // Text returns the token text.
func (t Token) Text() string { func (t Token) Text() string {
return string(t.Bytes) return string(t.Bytes)

View File

@ -0,0 +1,5 @@
package errors
var (
MissingAssignmentValue = &Base{"Missing assignment value"}
)