Simplified file structure
This commit is contained in:
parent
1b13539b22
commit
66569446b1
146
README.md
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
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
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
Loading…
Reference in New Issue
Block a user