Simplified file structure

This commit is contained in:
2024-08-07 19:39:10 +02:00
parent 1b13539b22
commit 66569446b1
219 changed files with 453 additions and 457 deletions
README.mdgo.modgo.sum
src
arch
asm
ast
build
cli
compiler
config
core
cpu
data
elf
errors
expression
fs
os/linux
register
scanner
scope
sizeof
token
types
tests

146
README.md

@ -24,79 +24,6 @@ Build a Linux x86-64 ELF executable from `examples/hello` and run it:
./q run examples/hello
```
## Documentation
### [main.go](main.go)
Entry point. It simply calls `cli.Main` which we can use for testing.
### [src/cli/Main.go](src/cli/Main.go)
The command line interface expects a command like `build` as the first argument.
Commands are implemented as functions in the [src/cli](src/cli) directory.
Each command has its own set of parameters.
### [src/cli/Build.go](src/cli/Build.go)
The build command creates a new `Build` instance with the given directory and calls the `Run` method.
If no directory is specified, it will use the current directory.
If the `--dry` flag is specified, it will perform all tasks except the final write to disk.
This flag should be used in most tests and benchmarks to avoid needless disk writes.
```shell
q build
q build examples/hello
q build examples/hello --dry
```
Adding the `-a` or `--assembler` flag shows the generated assembly instructions:
```shell
q build examples/hello -a
```
Adding the `-v` or `--verbose` flag shows verbose compiler information:
```shell
q build examples/hello -v
```
### [src/build/Build.go](src/build/Build.go)
The `Build` type defines all the information needed to start building an executable file.
The name of the executable will be equal to the name of the build directory.
`Run` starts the build which will scan all `.q` source files in the build directory.
Every source file is scanned in its own goroutine for performance reasons.
Parallelization here is possible because the order of files in a directory is not significant.
The main thread is meanwhile waiting for new function objects to arrive from the scanners.
Once a function has arrived, it will be stored for compilation later.
We need to wait with the compilation step until we have enough information about all identifiers from the scan.
Then all the functions that were scanned will be compiled in parallel.
We create a separate goroutine for each function compilation.
Each function will then be translated to generic assembler instructions.
All the functions that are required to run the program will be added to the final assembler.
The final assembler resolves label addresses, optimizes the performance and generates the specific x86-64 machine code from the generic instruction set.
### [src/build/core/Function.go](src/build/core/Function.go)
This is the "heart" of the compiler.
Each function runs `f.Compile` which organizes the source code into an abstract syntax tree that is then compiled via `f.CompileAST`.
You can think of AST nodes as the individual statements in your source code.
### [src/build/ast/Parse.go](src/build/ast/Parse.go)
This is what generates the AST from tokens.
### [src/build/expression/Parse.go](src/build/expression/Parse.go)
This is what generates expressions from tokens.
## Todo
### Compiler
@ -176,6 +103,79 @@ This is what generates expressions from tokens.
- [ ] Mac
- [ ] Windows
## Documentation
### [main.go](main.go)
Entry point. It simply calls `cli.Main` which we can use for testing.
### [src/cli/Main.go](src/cli/Main.go)
The command line interface expects a command like `build` as the first argument.
Commands are implemented as functions in the [src/cli](src/cli) directory.
Each command has its own set of parameters.
### [src/cli/Build.go](src/cli/Build.go)
The build command creates a new `Build` instance with the given directory and calls the `Run` method.
If no directory is specified, it will use the current directory.
If the `--dry` flag is specified, it will perform all tasks except the final write to disk.
This flag should be used in most tests and benchmarks to avoid needless disk writes.
```shell
q build
q build examples/hello
q build examples/hello --dry
```
Adding the `-a` or `--assembler` flag shows the generated assembly instructions:
```shell
q build examples/hello -a
```
Adding the `-v` or `--verbose` flag shows verbose compiler information:
```shell
q build examples/hello -v
```
### [src/build/Build.go](src/build/Build.go)
The `Build` type defines all the information needed to start building an executable file.
The name of the executable will be equal to the name of the build directory.
`Run` starts the build which will scan all `.q` source files in the build directory.
Every source file is scanned in its own goroutine for performance reasons.
Parallelization here is possible because the order of files in a directory is not significant.
The main thread is meanwhile waiting for new function objects to arrive from the scanners.
Once a function has arrived, it will be stored for compilation later.
We need to wait with the compilation step until we have enough information about all identifiers from the scan.
Then all the functions that were scanned will be compiled in parallel.
We create a separate goroutine for each function compilation.
Each function will then be translated to generic assembler instructions.
All the functions that are required to run the program will be added to the final assembler.
The final assembler resolves label addresses, optimizes the performance and generates the specific x86-64 machine code from the generic instruction set.
### [src/core/Function.go](src/core/Function.go)
This is the "heart" of the compiler.
Each function runs `f.Compile` which organizes the source code into an abstract syntax tree that is then compiled via `f.CompileAST`.
You can think of AST nodes as the individual statements in your source code.
### [src/ast/Parse.go](src/ast/Parse.go)
This is what generates the AST from tokens.
### [src/expression/Parse.go](src/expression/Parse.go)
This is what generates expressions from tokens.
## Tests
```shell

4
go.mod

@ -1,10 +1,10 @@
module git.akyoto.dev/cli/q
go 1.22.4
go 1.22.6
require (
git.akyoto.dev/go/assert v0.1.3
git.akyoto.dev/go/color v0.1.1
)
require golang.org/x/sys v0.22.0 // indirect
require golang.org/x/sys v0.23.0 // indirect

4
go.sum

@ -2,5 +2,5 @@ git.akyoto.dev/go/assert v0.1.3 h1:QwCUbmG4aZYsNk/OuRBz1zWVKmGlDUHhOnnDBfn8Qw8=
git.akyoto.dev/go/assert v0.1.3/go.mod h1:0GzMaM0eURuDwtGkJJkCsI7r2aUKr+5GmWNTFPgDocM=
git.akyoto.dev/go/color v0.1.1 h1:mMAoMIwLBPNy7ocRSxdsCFs7onPC3GfDEiJErCneqRE=
git.akyoto.dev/go/color v0.1.1/go.mod h1:ywOjoD0O0sk6bIn92uAJf7mErlEFCuQInL84y4Lqi3Q=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=

@ -1,6 +1,6 @@
package arm64
import "git.akyoto.dev/cli/q/src/build/cpu"
import "git.akyoto.dev/cli/q/src/cpu"
const (
X0 cpu.Register = iota

@ -1,6 +1,6 @@
package riscv
import "git.akyoto.dev/cli/q/src/build/cpu"
import "git.akyoto.dev/cli/q/src/cpu"
const (
X0 cpu.Register = iota

@ -1,7 +1,7 @@
package x64
import (
"git.akyoto.dev/cli/q/src/build/cpu"
"git.akyoto.dev/cli/q/src/cpu"
)
// AddRegisterNumber adds a number to the given register.

@ -3,8 +3,8 @@ package x64_test
import (
"testing"
"git.akyoto.dev/cli/q/src/build/arch/x64"
"git.akyoto.dev/cli/q/src/build/cpu"
"git.akyoto.dev/cli/q/src/arch/x64"
"git.akyoto.dev/cli/q/src/cpu"
"git.akyoto.dev/go/assert"
)

@ -1,7 +1,7 @@
package x64
import (
"git.akyoto.dev/cli/q/src/build/cpu"
"git.akyoto.dev/cli/q/src/cpu"
)
// AndRegisterNumber performs a bitwise AND using a register and a number.

@ -1,6 +1,6 @@
package x64
import "git.akyoto.dev/cli/q/src/build/cpu"
import "git.akyoto.dev/cli/q/src/cpu"
// Compares the register with the number and sets the status flags in the EFLAGS register.
func CompareRegisterNumber(code []byte, register cpu.Register, number int) []byte {

@ -3,8 +3,8 @@ package x64_test
import (
"testing"
"git.akyoto.dev/cli/q/src/build/arch/x64"
"git.akyoto.dev/cli/q/src/build/cpu"
"git.akyoto.dev/cli/q/src/arch/x64"
"git.akyoto.dev/cli/q/src/cpu"
"git.akyoto.dev/go/assert"
)

@ -1,6 +1,6 @@
package x64
import "git.akyoto.dev/cli/q/src/build/cpu"
import "git.akyoto.dev/cli/q/src/cpu"
// DivRegister divides RDX:RAX by the value in the register.
func DivRegister(code []byte, divisor cpu.Register) []byte {

@ -3,8 +3,8 @@ package x64_test
import (
"testing"
"git.akyoto.dev/cli/q/src/build/arch/x64"
"git.akyoto.dev/cli/q/src/build/cpu"
"git.akyoto.dev/cli/q/src/arch/x64"
"git.akyoto.dev/cli/q/src/cpu"
"git.akyoto.dev/go/assert"
)

@ -3,7 +3,7 @@ package x64_test
import (
"testing"
"git.akyoto.dev/cli/q/src/build/arch/x64"
"git.akyoto.dev/cli/q/src/arch/x64"
"git.akyoto.dev/go/assert"
)

@ -1,6 +1,6 @@
package x64
import "git.akyoto.dev/cli/q/src/build/cpu"
import "git.akyoto.dev/cli/q/src/cpu"
// LoadRegister loads from memory into a register.
func LoadRegister(code []byte, destination cpu.Register, offset byte, length byte, source cpu.Register) []byte {

@ -3,8 +3,8 @@ package x64_test
import (
"testing"
"git.akyoto.dev/cli/q/src/build/arch/x64"
"git.akyoto.dev/cli/q/src/build/cpu"
"git.akyoto.dev/cli/q/src/arch/x64"
"git.akyoto.dev/cli/q/src/cpu"
"git.akyoto.dev/go/assert"
)

@ -3,7 +3,7 @@ package x64_test
import (
"testing"
"git.akyoto.dev/cli/q/src/build/arch/x64"
"git.akyoto.dev/cli/q/src/arch/x64"
"git.akyoto.dev/go/assert"
)

@ -3,8 +3,8 @@ package x64
import (
"encoding/binary"
"git.akyoto.dev/cli/q/src/build/cpu"
"git.akyoto.dev/cli/q/src/build/sizeof"
"git.akyoto.dev/cli/q/src/cpu"
"git.akyoto.dev/cli/q/src/sizeof"
)
// MoveRegisterNumber moves an integer into the given register.

@ -3,8 +3,8 @@ package x64_test
import (
"testing"
"git.akyoto.dev/cli/q/src/build/arch/x64"
"git.akyoto.dev/cli/q/src/build/cpu"
"git.akyoto.dev/cli/q/src/arch/x64"
"git.akyoto.dev/cli/q/src/cpu"
"git.akyoto.dev/go/assert"
)

@ -1,6 +1,6 @@
package x64
import "git.akyoto.dev/cli/q/src/build/cpu"
import "git.akyoto.dev/cli/q/src/cpu"
// MulRegisterNumber multiplies a register with a number.
func MulRegisterNumber(code []byte, destination cpu.Register, number int) []byte {

@ -3,8 +3,8 @@ package x64_test
import (
"testing"
"git.akyoto.dev/cli/q/src/build/arch/x64"
"git.akyoto.dev/cli/q/src/build/cpu"
"git.akyoto.dev/cli/q/src/arch/x64"
"git.akyoto.dev/cli/q/src/cpu"
"git.akyoto.dev/go/assert"
)

@ -1,6 +1,6 @@
package x64
import "git.akyoto.dev/cli/q/src/build/cpu"
import "git.akyoto.dev/cli/q/src/cpu"
// NegateRegister negates the value in the register.
func NegateRegister(code []byte, register cpu.Register) []byte {

@ -3,8 +3,8 @@ package x64_test
import (
"testing"
"git.akyoto.dev/cli/q/src/build/arch/x64"
"git.akyoto.dev/cli/q/src/build/cpu"
"git.akyoto.dev/cli/q/src/arch/x64"
"git.akyoto.dev/cli/q/src/cpu"
"git.akyoto.dev/go/assert"
)

@ -1,7 +1,7 @@
package x64
import (
"git.akyoto.dev/cli/q/src/build/cpu"
"git.akyoto.dev/cli/q/src/cpu"
)
// OrRegisterNumber performs a bitwise OR using a register and a number.

@ -1,6 +1,6 @@
package x64
import "git.akyoto.dev/cli/q/src/build/cpu"
import "git.akyoto.dev/cli/q/src/cpu"
// PopRegister pops a value from the stack and saves it into the register.
func PopRegister(code []byte, register cpu.Register) []byte {

@ -3,8 +3,8 @@ package x64_test
import (
"testing"
"git.akyoto.dev/cli/q/src/build/arch/x64"
"git.akyoto.dev/cli/q/src/build/cpu"
"git.akyoto.dev/cli/q/src/arch/x64"
"git.akyoto.dev/cli/q/src/cpu"
"git.akyoto.dev/go/assert"
)

@ -1,6 +1,6 @@
package x64
import "git.akyoto.dev/cli/q/src/build/cpu"
import "git.akyoto.dev/cli/q/src/cpu"
// PushRegister pushes the value inside the register onto the stack.
func PushRegister(code []byte, register cpu.Register) []byte {

@ -3,8 +3,8 @@ package x64_test
import (
"testing"
"git.akyoto.dev/cli/q/src/build/arch/x64"
"git.akyoto.dev/cli/q/src/build/cpu"
"git.akyoto.dev/cli/q/src/arch/x64"
"git.akyoto.dev/cli/q/src/cpu"
"git.akyoto.dev/go/assert"
)

@ -3,7 +3,7 @@ package x64_test
import (
"testing"
"git.akyoto.dev/cli/q/src/build/arch/x64"
"git.akyoto.dev/cli/q/src/arch/x64"
"git.akyoto.dev/go/assert"
)

@ -1,6 +1,6 @@
package x64
import "git.akyoto.dev/cli/q/src/build/cpu"
import "git.akyoto.dev/cli/q/src/cpu"
const (
RAX cpu.Register = iota

@ -3,7 +3,7 @@ package x64_test
import (
"testing"
"git.akyoto.dev/cli/q/src/build/arch/x64"
"git.akyoto.dev/cli/q/src/arch/x64"
"git.akyoto.dev/go/assert"
)

@ -1,7 +1,7 @@
package x64
import (
"git.akyoto.dev/cli/q/src/build/cpu"
"git.akyoto.dev/cli/q/src/cpu"
)
// ShiftLeftNumber shifts the register value by `bitCount` bits to the left.

@ -3,7 +3,7 @@ package x64
import (
"encoding/binary"
"git.akyoto.dev/cli/q/src/build/cpu"
"git.akyoto.dev/cli/q/src/cpu"
)
// StoreNumber stores a number into the memory address included in the given register.

@ -3,8 +3,8 @@ package x64_test
import (
"testing"
"git.akyoto.dev/cli/q/src/build/arch/x64"
"git.akyoto.dev/cli/q/src/build/cpu"
"git.akyoto.dev/cli/q/src/arch/x64"
"git.akyoto.dev/cli/q/src/cpu"
"git.akyoto.dev/go/assert"
)

@ -1,7 +1,7 @@
package x64
import (
"git.akyoto.dev/cli/q/src/build/cpu"
"git.akyoto.dev/cli/q/src/cpu"
)
// SubRegisterNumber subtracts a number from the given register.

@ -3,8 +3,8 @@ package x64_test
import (
"testing"
"git.akyoto.dev/cli/q/src/build/arch/x64"
"git.akyoto.dev/cli/q/src/build/cpu"
"git.akyoto.dev/cli/q/src/arch/x64"
"git.akyoto.dev/cli/q/src/cpu"
"git.akyoto.dev/go/assert"
)

@ -1,7 +1,7 @@
package x64
import (
"git.akyoto.dev/cli/q/src/build/cpu"
"git.akyoto.dev/cli/q/src/cpu"
)
// XorRegisterNumber performs a bitwise XOR using a register and a number.

@ -1,6 +1,6 @@
package x64
import "git.akyoto.dev/cli/q/src/build/cpu"
import "git.akyoto.dev/cli/q/src/cpu"
// encode is the core function that encodes an instruction.
func encode(code []byte, mod AddressMode, reg cpu.Register, rm cpu.Register, numBytes byte, opCodes ...byte) []byte {

@ -3,8 +3,8 @@ package x64
import (
"encoding/binary"
"git.akyoto.dev/cli/q/src/build/cpu"
"git.akyoto.dev/cli/q/src/build/sizeof"
"git.akyoto.dev/cli/q/src/cpu"
"git.akyoto.dev/cli/q/src/sizeof"
)
// encodeNum encodes an instruction with up to two registers and a number parameter.

@ -1,6 +1,6 @@
package x64
import "git.akyoto.dev/cli/q/src/build/cpu"
import "git.akyoto.dev/cli/q/src/cpu"
// memoryAccess encodes a memory access.
func memoryAccess(code []byte, opCode8 byte, opCode32 byte, register cpu.Register, offset byte, numBytes byte, source cpu.Register) []byte {

@ -3,7 +3,7 @@ package x64_test
import (
"testing"
"git.akyoto.dev/cli/q/src/build/arch/x64"
"git.akyoto.dev/cli/q/src/arch/x64"
"git.akyoto.dev/go/assert"
)

@ -3,7 +3,7 @@ package asm
import (
"maps"
"git.akyoto.dev/cli/q/src/build/data"
"git.akyoto.dev/cli/q/src/data"
)
// Assembler contains a list of instructions.

@ -6,10 +6,10 @@ import (
"slices"
"strings"
"git.akyoto.dev/cli/q/src/build/arch/x64"
"git.akyoto.dev/cli/q/src/build/config"
"git.akyoto.dev/cli/q/src/build/elf"
"git.akyoto.dev/cli/q/src/build/sizeof"
"git.akyoto.dev/cli/q/src/arch/x64"
"git.akyoto.dev/cli/q/src/config"
"git.akyoto.dev/cli/q/src/elf"
"git.akyoto.dev/cli/q/src/sizeof"
)
// Finalize generates the final machine code.

@ -1,6 +1,6 @@
package asm
import "git.akyoto.dev/cli/q/src/build/cpu"
import "git.akyoto.dev/cli/q/src/cpu"
type Memory struct {
Base cpu.Register

@ -3,7 +3,7 @@ package asm
import (
"fmt"
"git.akyoto.dev/cli/q/src/build/cpu"
"git.akyoto.dev/cli/q/src/cpu"
)
// MemoryRegister operates with a memory address and a number.

@ -1,6 +1,6 @@
package asm
import "git.akyoto.dev/cli/q/src/build/cpu"
import "git.akyoto.dev/cli/q/src/cpu"
// unnecessary returns true if the register/register operation can be skipped.
func (a *Assembler) unnecessary(mnemonic Mnemonic, left cpu.Register, right cpu.Register) bool {

@ -1,7 +1,7 @@
package asm
import (
"git.akyoto.dev/cli/q/src/build/cpu"
"git.akyoto.dev/cli/q/src/cpu"
)
// Register operates with a single register.

@ -3,7 +3,7 @@ package asm
import (
"fmt"
"git.akyoto.dev/cli/q/src/build/cpu"
"git.akyoto.dev/cli/q/src/cpu"
)
// RegisterLabel operates with a register and a label.

@ -3,7 +3,7 @@ package asm
import (
"fmt"
"git.akyoto.dev/cli/q/src/build/cpu"
"git.akyoto.dev/cli/q/src/cpu"
)
// RegisterNumber operates with a register and a number.

@ -3,7 +3,7 @@ package asm
import (
"fmt"
"git.akyoto.dev/cli/q/src/build/cpu"
"git.akyoto.dev/cli/q/src/cpu"
)
// RegisterRegister operates with two registers.

@ -1,7 +1,7 @@
package ast
import (
"git.akyoto.dev/cli/q/src/build/expression"
"git.akyoto.dev/cli/q/src/expression"
)
// Assert represents a condition that must be true, otherwise the program stops.

@ -1,7 +1,7 @@
package ast
import (
"git.akyoto.dev/cli/q/src/build/expression"
"git.akyoto.dev/cli/q/src/expression"
)
// Assign represents an assignment to an existing variable or memory location.

@ -1,6 +1,6 @@
package ast
import "git.akyoto.dev/cli/q/src/build/expression"
import "git.akyoto.dev/cli/q/src/expression"
// Call represents a function call.
type Call struct {

@ -1,6 +1,6 @@
package ast
import "git.akyoto.dev/cli/q/src/build/token"
import "git.akyoto.dev/cli/q/src/token"
// Count counts how often the given token appears in the AST.
func Count(body AST, buffer []byte, kind token.Kind, name string) uint8 {

@ -1,7 +1,7 @@
package ast
import (
"git.akyoto.dev/cli/q/src/build/expression"
"git.akyoto.dev/cli/q/src/expression"
)
// Define represents a variable definition.

@ -1,6 +1,6 @@
package ast
import "git.akyoto.dev/cli/q/src/build/token"
import "git.akyoto.dev/cli/q/src/token"
// EachInstruction calls the function on each instruction.
func EachInstruction(body token.List, call func(token.List) error) error {

@ -1,7 +1,7 @@
package ast
import (
"git.akyoto.dev/cli/q/src/build/expression"
"git.akyoto.dev/cli/q/src/expression"
)
// If represents an if statement.

@ -1,8 +1,8 @@
package ast
import (
"git.akyoto.dev/cli/q/src/build/expression"
"git.akyoto.dev/cli/q/src/build/token"
"git.akyoto.dev/cli/q/src/expression"
"git.akyoto.dev/cli/q/src/token"
)
// Parse generates an AST from a list of tokens.

@ -1,7 +1,7 @@
package ast
import (
"git.akyoto.dev/cli/q/src/build/expression"
"git.akyoto.dev/cli/q/src/expression"
)
// Return represents a return statement.

@ -1,7 +1,7 @@
package ast
import (
"git.akyoto.dev/cli/q/src/build/expression"
"git.akyoto.dev/cli/q/src/expression"
)
// Switch represents a switch statement.

@ -1,9 +1,9 @@
package ast
import (
"git.akyoto.dev/cli/q/src/build/errors"
"git.akyoto.dev/cli/q/src/build/expression"
"git.akyoto.dev/cli/q/src/build/token"
"git.akyoto.dev/cli/q/src/errors"
"git.akyoto.dev/cli/q/src/expression"
"git.akyoto.dev/cli/q/src/token"
)
// parseKeyword generates a keyword node from an instruction.

@ -1,9 +1,9 @@
package ast
import (
"git.akyoto.dev/cli/q/src/build/errors"
"git.akyoto.dev/cli/q/src/build/expression"
"git.akyoto.dev/cli/q/src/build/token"
"git.akyoto.dev/cli/q/src/errors"
"git.akyoto.dev/cli/q/src/expression"
"git.akyoto.dev/cli/q/src/token"
)
// parseNode generates an AST node from an instruction.

@ -1,8 +1,8 @@
package ast
import (
"git.akyoto.dev/cli/q/src/build/expression"
"git.akyoto.dev/cli/q/src/build/token"
"git.akyoto.dev/cli/q/src/expression"
"git.akyoto.dev/cli/q/src/token"
)
// parseSwitch generates the cases inside a switch statement.

@ -4,8 +4,8 @@ import (
"path/filepath"
"strings"
"git.akyoto.dev/cli/q/src/build/compiler"
"git.akyoto.dev/cli/q/src/build/scanner"
"git.akyoto.dev/cli/q/src/compiler"
"git.akyoto.dev/cli/q/src/scanner"
)
// Build describes a compiler build.

@ -1,55 +0,0 @@
package expression
import (
"git.akyoto.dev/cli/q/src/build/token"
)
// NewList generates a list of expressions from comma separated parameters.
func NewList(tokens token.List) []*Expression {
var list []*Expression
EachParameter(tokens, func(parameter token.List) error {
expression := Parse(parameter)
list = append(list, expression)
return nil
})
return list
}
// EachParameter calls the callback function on each parameter in a comma separated list.
func EachParameter(tokens token.List, call func(token.List) error) error {
start := 0
groupLevel := 0
for i, t := range tokens {
switch t.Kind {
case token.GroupStart, token.ArrayStart, token.BlockStart:
groupLevel++
case token.GroupEnd, token.ArrayEnd, token.BlockEnd:
groupLevel--
case token.Separator:
if groupLevel > 0 {
continue
}
parameter := tokens[start:i]
err := call(parameter)
if err != nil {
return err
}
start = i + 1
}
}
if start != len(tokens) {
parameter := tokens[start:]
return call(parameter)
}
return nil
}

@ -1,18 +0,0 @@
package token_test
import (
"testing"
"git.akyoto.dev/cli/q/src/build/token"
"git.akyoto.dev/go/assert"
)
func TestIndexKind(t *testing.T) {
tokens := token.Tokenize([]byte("a{{}}"))
assert.Equal(t, tokens.IndexKind(token.NewLine), -1)
assert.Equal(t, tokens.LastIndexKind(token.NewLine), -1)
assert.Equal(t, tokens.IndexKind(token.BlockStart), 1)
assert.Equal(t, tokens.LastIndexKind(token.BlockStart), 2)
assert.Equal(t, tokens.IndexKind(token.BlockEnd), 3)
assert.Equal(t, tokens.LastIndexKind(token.BlockEnd), 4)
}

@ -6,8 +6,8 @@ import (
"strings"
"git.akyoto.dev/cli/q/src/build"
"git.akyoto.dev/cli/q/src/build/config"
"git.akyoto.dev/cli/q/src/build/errors"
"git.akyoto.dev/cli/q/src/config"
"git.akyoto.dev/cli/q/src/errors"
)
// Build parses the arguments and creates a build.

@ -5,7 +5,7 @@ import (
"os"
"os/exec"
"git.akyoto.dev/cli/q/src/build/errors"
"git.akyoto.dev/cli/q/src/errors"
)
// Run builds and runs the executable.

@ -5,7 +5,7 @@ import (
"runtime"
"strconv"
"git.akyoto.dev/cli/q/src/build/config"
"git.akyoto.dev/cli/q/src/config"
)
// System shows system information.

@ -3,9 +3,9 @@ package compiler
import (
"sync"
"git.akyoto.dev/cli/q/src/build/core"
"git.akyoto.dev/cli/q/src/build/errors"
"git.akyoto.dev/cli/q/src/build/fs"
"git.akyoto.dev/cli/q/src/core"
"git.akyoto.dev/cli/q/src/errors"
"git.akyoto.dev/cli/q/src/fs"
)
// Compile waits for the scan to finish and compiles all functions.

@ -5,11 +5,11 @@ import (
"io"
"os"
"git.akyoto.dev/cli/q/src/build/arch/x64"
"git.akyoto.dev/cli/q/src/build/asm"
"git.akyoto.dev/cli/q/src/build/core"
"git.akyoto.dev/cli/q/src/build/elf"
"git.akyoto.dev/cli/q/src/build/os/linux"
"git.akyoto.dev/cli/q/src/arch/x64"
"git.akyoto.dev/cli/q/src/asm"
"git.akyoto.dev/cli/q/src/core"
"git.akyoto.dev/cli/q/src/elf"
"git.akyoto.dev/cli/q/src/os/linux"
)
// Result contains all the compiled functions in a build.

@ -1,10 +1,10 @@
package core
import (
"git.akyoto.dev/cli/q/src/build/ast"
"git.akyoto.dev/cli/q/src/build/errors"
"git.akyoto.dev/cli/q/src/build/expression"
"git.akyoto.dev/cli/q/src/build/token"
"git.akyoto.dev/cli/q/src/ast"
"git.akyoto.dev/cli/q/src/errors"
"git.akyoto.dev/cli/q/src/expression"
"git.akyoto.dev/cli/q/src/token"
)
// Compare evaluates a boolean expression.

@ -1,7 +1,7 @@
package core
import (
"git.akyoto.dev/cli/q/src/build/ast"
"git.akyoto.dev/cli/q/src/ast"
)
// CompileAST compiles an abstract syntax tree.

@ -1,7 +1,7 @@
package core
import (
"git.akyoto.dev/cli/q/src/build/ast"
"git.akyoto.dev/cli/q/src/ast"
)
// CompileASTNode compiles a node in the AST.

@ -3,8 +3,8 @@ package core
import (
"fmt"
"git.akyoto.dev/cli/q/src/build/asm"
"git.akyoto.dev/cli/q/src/build/ast"
"git.akyoto.dev/cli/q/src/asm"
"git.akyoto.dev/cli/q/src/ast"
)
// CompileAssert compiles an assertion.

@ -1,9 +1,9 @@
package core
import (
"git.akyoto.dev/cli/q/src/build/ast"
"git.akyoto.dev/cli/q/src/build/errors"
"git.akyoto.dev/cli/q/src/build/token"
"git.akyoto.dev/cli/q/src/ast"
"git.akyoto.dev/cli/q/src/errors"
"git.akyoto.dev/cli/q/src/token"
)
// CompileAssign compiles an assign statement.

@ -1,9 +1,9 @@
package core
import (
"git.akyoto.dev/cli/q/src/build/asm"
"git.akyoto.dev/cli/q/src/build/ast"
"git.akyoto.dev/cli/q/src/build/errors"
"git.akyoto.dev/cli/q/src/asm"
"git.akyoto.dev/cli/q/src/ast"
"git.akyoto.dev/cli/q/src/errors"
)
// CompileAssignArray compiles an assign statement for array elements.

@ -1,10 +1,10 @@
package core
import (
"git.akyoto.dev/cli/q/src/build/arch/x64"
"git.akyoto.dev/cli/q/src/build/asm"
"git.akyoto.dev/cli/q/src/build/ast"
"git.akyoto.dev/cli/q/src/build/errors"
"git.akyoto.dev/cli/q/src/arch/x64"
"git.akyoto.dev/cli/q/src/asm"
"git.akyoto.dev/cli/q/src/ast"
"git.akyoto.dev/cli/q/src/errors"
)
// CompileAssignDivision compiles an assign statement that has quotient and remainder on the left side and division on the right.

@ -3,9 +3,9 @@ package core
import (
"strings"
"git.akyoto.dev/cli/q/src/build/asm"
"git.akyoto.dev/cli/q/src/build/errors"
"git.akyoto.dev/cli/q/src/build/expression"
"git.akyoto.dev/cli/q/src/asm"
"git.akyoto.dev/cli/q/src/errors"
"git.akyoto.dev/cli/q/src/expression"
)
// CompileCall executes a function call.

@ -3,8 +3,8 @@ package core
import (
"fmt"
"git.akyoto.dev/cli/q/src/build/expression"
"git.akyoto.dev/cli/q/src/build/token"
"git.akyoto.dev/cli/q/src/expression"
"git.akyoto.dev/cli/q/src/token"
)
// CompileCondition inserts code to jump to the start label or end label depending on the truth of the condition.

@ -1,11 +1,11 @@
package core
import (
"git.akyoto.dev/cli/q/src/build/asm"
"git.akyoto.dev/cli/q/src/build/ast"
"git.akyoto.dev/cli/q/src/build/errors"
"git.akyoto.dev/cli/q/src/build/expression"
"git.akyoto.dev/cli/q/src/build/types"
"git.akyoto.dev/cli/q/src/asm"
"git.akyoto.dev/cli/q/src/ast"
"git.akyoto.dev/cli/q/src/errors"
"git.akyoto.dev/cli/q/src/expression"
"git.akyoto.dev/cli/q/src/types"
)
// CompileDefinition compiles a variable definition.

Some files were not shown because too many files have changed in this diff Show More