Simplified file structure

This commit is contained in:
Eduard Urbach 2024-08-07 19:39:10 +02:00
parent 1b13539b22
commit 66569446b1
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
219 changed files with 453 additions and 457 deletions

146
README.md
View File

@ -24,79 +24,6 @@ Build a Linux x86-64 ELF executable from `examples/hello` and run it:
./q run examples/hello ./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 ## Todo
### Compiler ### Compiler
@ -176,6 +103,79 @@ This is what generates expressions from tokens.
- [ ] Mac - [ ] Mac
- [ ] Windows - [ ] 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 ## Tests
```shell ```shell

4
go.mod
View File

@ -1,10 +1,10 @@
module git.akyoto.dev/cli/q module git.akyoto.dev/cli/q
go 1.22.4 go 1.22.6
require ( require (
git.akyoto.dev/go/assert v0.1.3 git.akyoto.dev/go/assert v0.1.3
git.akyoto.dev/go/color v0.1.1 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
View File

@ -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/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 h1:mMAoMIwLBPNy7ocRSxdsCFs7onPC3GfDEiJErCneqRE=
git.akyoto.dev/go/color v0.1.1/go.mod h1:ywOjoD0O0sk6bIn92uAJf7mErlEFCuQInL84y4Lqi3Q= 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.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
package x64 package x64
import ( 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. // AndRegisterNumber performs a bitwise AND using a register and a number.

View File

@ -1,6 +1,6 @@
package x64 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. // 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 { func CompareRegisterNumber(code []byte, register cpu.Register, number int) []byte {

View File

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

View File

@ -1,6 +1,6 @@
package x64 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. // DivRegister divides RDX:RAX by the value in the register.
func DivRegister(code []byte, divisor cpu.Register) []byte { func DivRegister(code []byte, divisor cpu.Register) []byte {

View File

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

View File

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

View File

@ -1,6 +1,6 @@
package x64 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. // LoadRegister loads from memory into a register.
func LoadRegister(code []byte, destination cpu.Register, offset byte, length byte, source cpu.Register) []byte { func LoadRegister(code []byte, destination cpu.Register, offset byte, length byte, source cpu.Register) []byte {

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
package x64 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. // MulRegisterNumber multiplies a register with a number.
func MulRegisterNumber(code []byte, destination cpu.Register, number int) []byte { func MulRegisterNumber(code []byte, destination cpu.Register, number int) []byte {

View File

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

View File

@ -1,6 +1,6 @@
package x64 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. // NegateRegister negates the value in the register.
func NegateRegister(code []byte, register cpu.Register) []byte { func NegateRegister(code []byte, register cpu.Register) []byte {

View File

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

View File

@ -1,7 +1,7 @@
package x64 package x64
import ( 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. // OrRegisterNumber performs a bitwise OR using a register and a number.

View File

@ -1,6 +1,6 @@
package x64 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. // PopRegister pops a value from the stack and saves it into the register.
func PopRegister(code []byte, register cpu.Register) []byte { func PopRegister(code []byte, register cpu.Register) []byte {

View File

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

View File

@ -1,6 +1,6 @@
package x64 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. // PushRegister pushes the value inside the register onto the stack.
func PushRegister(code []byte, register cpu.Register) []byte { func PushRegister(code []byte, register cpu.Register) []byte {

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
package x64 package x64
import ( 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. // ShiftLeftNumber shifts the register value by `bitCount` bits to the left.

View File

@ -3,7 +3,7 @@ package x64
import ( import (
"encoding/binary" "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. // StoreNumber stores a number into the memory address included in the given register.

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
package x64 package x64
import ( 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. // XorRegisterNumber performs a bitwise XOR using a register and a number.

View File

@ -1,6 +1,6 @@
package x64 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. // 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 { func encode(code []byte, mod AddressMode, reg cpu.Register, rm cpu.Register, numBytes byte, opCodes ...byte) []byte {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,7 +3,7 @@ package asm
import ( import (
"fmt" "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. // MemoryRegister operates with a memory address and a number.

View File

@ -1,6 +1,6 @@
package asm 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. // unnecessary returns true if the register/register operation can be skipped.
func (a *Assembler) unnecessary(mnemonic Mnemonic, left cpu.Register, right cpu.Register) bool { func (a *Assembler) unnecessary(mnemonic Mnemonic, left cpu.Register, right cpu.Register) bool {

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
package ast package ast
import ( 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. // Assert represents a condition that must be true, otherwise the program stops.

View File

@ -1,7 +1,7 @@
package ast package ast
import ( 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. // Assign represents an assignment to an existing variable or memory location.

View File

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

View File

@ -1,6 +1,6 @@
package ast 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. // Count counts how often the given token appears in the AST.
func Count(body AST, buffer []byte, kind token.Kind, name string) uint8 { func Count(body AST, buffer []byte, kind token.Kind, name string) uint8 {

View File

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

View File

@ -1,6 +1,6 @@
package ast 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. // EachInstruction calls the function on each instruction.
func EachInstruction(body token.List, call func(token.List) error) error { func EachInstruction(body token.List, call func(token.List) error) error {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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
}

View File

@ -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)
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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