105 lines
3.0 KiB
Markdown
105 lines
3.0 KiB
Markdown
# q
|
|
|
|
A simple programming language.
|
|
|
|
## Features
|
|
|
|
* Fast compilation
|
|
* Small binaries
|
|
|
|
## Installation
|
|
|
|
```shell
|
|
git clone https://git.akyoto.dev/cli/q
|
|
cd q
|
|
go build
|
|
```
|
|
|
|
## Usage
|
|
|
|
Build a Linux x86-64 ELF executable from `examples/hello`:
|
|
|
|
```shell
|
|
./q build examples/hello
|
|
./examples/hello/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
|
|
```
|
|
|
|
To produce verbose output, add the `-v` flag which shows the generated assembly instructions:
|
|
|
|
```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/Function.go](src/build/Function.go)
|
|
|
|
This is the "heart" of the compiler.
|
|
Each function runs `f.Compile()` which organizes the source code into instructions that are then compiled via `f.CompileInstruction`.
|
|
You can think of instructions as the individual lines in your source code, but instructions can also span over multiple lines.
|
|
|
|
## Tests
|
|
|
|
```shell
|
|
go test -coverpkg=./...
|
|
```
|
|
|
|
## Benchmarks
|
|
|
|
```shell
|
|
go test -bench=. -benchmem
|
|
```
|
|
|
|
## License
|
|
|
|
Please see the [license documentation](https://akyoto.dev/license).
|
|
|
|
## Copyright
|
|
|
|
© 2023 Eduard Urbach
|