200 lines
4.5 KiB
Markdown
Raw Normal View History

2023-10-17 09:06:14 +00:00
# q
A simple programming language.
2023-10-17 13:10:35 +00:00
## Features
2023-10-21 15:46:20 +00:00
* Fast compilation
* Small binaries
2024-07-31 11:37:46 +00:00
* High performance
2023-10-17 13:10:35 +00:00
2023-10-17 09:06:14 +00:00
## Installation
```shell
git clone https://git.akyoto.dev/cli/q
cd q
go build
```
2023-10-17 13:10:35 +00:00
## Usage
2023-10-17 09:06:14 +00:00
2024-07-06 17:40:20 +00:00
Build a Linux x86-64 ELF executable from `examples/hello` and run it:
2023-10-21 15:46:20 +00:00
2023-10-17 13:10:35 +00:00
```shell
2024-07-06 17:40:20 +00:00
./q run examples/hello
2023-10-17 13:10:35 +00:00
```
2023-10-17 09:06:14 +00:00
2024-07-24 17:39:13 +00:00
## Todo
### Compiler
- [x] Tokenizer
- [x] Scanner
- [x] Functions
- [x] Variables
- [x] Error messages
- [x] Expression parser
- [x] Function calls
- [x] Parallel compilation
- [x] Syscalls
- [x] Variable lifetimes
- [x] Branches
- [x] Loops
- [x] Hexadecimal, octal and binary literals
2024-07-31 15:50:31 +00:00
- [x] Escape sequences
2024-08-05 10:39:07 +00:00
- [x] Multiple return values
2024-07-24 17:39:13 +00:00
- [ ] Type system
2024-08-05 17:33:23 +00:00
- [ ] Type operator `?`
- [ ] Data structures
- [ ] Slices
- [ ] Floating-point arithmetic
2024-07-24 17:39:13 +00:00
- [ ] Error handling
- [ ] Threading library
- [ ] Self-hosted compiler
### Keywords
2024-07-25 14:47:25 +00:00
- [x] `assert`
2024-07-30 14:36:33 +00:00
- [x] `else`
2024-07-24 17:39:13 +00:00
- [ ] `for`
- [x] `if`
- [x] `import`
- [x] `loop`
- [x] `return`
2024-08-03 21:05:09 +00:00
- [x] `switch`
2024-07-24 17:39:13 +00:00
### Optimizations
- [x] Exclude unused functions
2024-07-29 12:45:53 +00:00
- [x] Constant folding
2024-08-07 21:30:53 +00:00
- [ ] Constant propagation
2024-07-24 17:39:13 +00:00
- [ ] Function call inlining
- [ ] Loop unrolls
2024-08-07 21:30:53 +00:00
- [ ] Assembler optimization backend
2024-07-24 17:39:13 +00:00
### Linter
- [x] Unused variables
- [x] Unused parameters
2024-07-31 09:55:21 +00:00
- [x] Unused imports
2024-07-24 17:39:13 +00:00
- [ ] Unnecessary newlines
- [ ] Ineffective assignments
### Operators
2024-07-25 10:21:10 +00:00
- [x] `=`, `:=`
- [x] `+`, `-`, `*`, `/`, `%`
- [x] `+=`, `-=`, `*=`, `/=`, `%=`
2024-07-25 12:17:51 +00:00
- [x] `&`, `|`, `^`
- [x] `&=`, `|=`, `^=`
2024-07-25 13:47:19 +00:00
- [x] `<<`, `>>`
- [x] `<<=`, `>>=`
2024-07-25 10:21:10 +00:00
- [x] `==`, `!=`, `<`, `<=`, `>`, `>=`
- [x] `&&`, `||`
2024-07-27 10:49:39 +00:00
- [ ] `!`, `-`
2024-07-24 17:39:13 +00:00
### Architecture
- [ ] arm64
2024-08-05 15:16:32 +00:00
- [ ] riscv
- [x] x64
2024-07-24 17:39:13 +00:00
### Platform
- [x] Linux
- [ ] Mac
- [ ] Windows
2024-08-07 17:39:10 +00:00
## 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.
2023-10-23 10:37:20 +00:00
## Tests
2023-10-21 15:46:20 +00:00
```shell
2024-07-24 17:39:13 +00:00
go test ./... -v -cover
2023-10-23 10:37:20 +00:00
```
## Benchmarks
```shell
2024-07-03 09:39:24 +00:00
go test ./tests -bench=. -benchmem
2023-10-21 15:46:20 +00:00
```
2023-10-17 09:06:14 +00:00
## License
Please see the [license documentation](https://akyoto.dev/license).
## Copyright
© 2023 Eduard Urbach