Improved instruction parser
This commit is contained in:
parent
cf696a6f10
commit
57f1da10fe
@ -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
9
src/build/File.go
Normal 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
|
||||||
|
}
|
@ -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
|
||||||
|
@ -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{},
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
5
src/errors/CompileErrors.go
Normal file
5
src/errors/CompileErrors.go
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package errors
|
||||||
|
|
||||||
|
var (
|
||||||
|
MissingAssignmentValue = &Base{"Missing assignment value"}
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user