mirror of
https://github.com/RoBaertschi/tt.git
synced 2025-04-15 21:43:30 +00:00
begin qbe support and custom build system
This commit is contained in:
parent
bc554cdedf
commit
b53d40d101
8
asm/backend.go
Normal file
8
asm/backend.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package asm
|
||||||
|
|
||||||
|
type Backend string
|
||||||
|
|
||||||
|
const (
|
||||||
|
Fasm Backend = "fasm"
|
||||||
|
Qbe Backend = "qbe"
|
||||||
|
)
|
1
asm/qbe/qbe.go
Normal file
1
asm/qbe/qbe.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package qbe
|
30
asm/qbe/qbe_stub.asm
Normal file
30
asm/qbe/qbe_stub.asm
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
; Reference: https://filippo.io/linux-syscall-table/
|
||||||
|
; https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/
|
||||||
|
; https://en.wikipedia.org/wiki/X86_calling_conventions#List_of_x86_calling_conventions
|
||||||
|
format ELF64
|
||||||
|
|
||||||
|
public syscall1
|
||||||
|
public syscall2
|
||||||
|
public syscall3
|
||||||
|
; rdi => Syscall number, rsi => argument
|
||||||
|
syscall1:
|
||||||
|
mov rax, rdi
|
||||||
|
mov rdi, rsi
|
||||||
|
syscall
|
||||||
|
ret
|
||||||
|
|
||||||
|
syscall2:
|
||||||
|
mov rax, rdi
|
||||||
|
mov rdi, rsi
|
||||||
|
mov rsi, rdx
|
||||||
|
syscall
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
syscall3:
|
||||||
|
mov rax, rdi
|
||||||
|
mov rdi, rsi
|
||||||
|
mov rsi, rdx
|
||||||
|
mov rdx, rcx
|
||||||
|
syscall
|
||||||
|
ret
|
143
build/build.go
Normal file
143
build/build.go
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
// Build allows the user to build a tt file. The goal is to make it easy to support multiple backends with different requirements
|
||||||
|
package build
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
|
"robaertschi.xyz/robaertschi/tt/asm"
|
||||||
|
"robaertschi.xyz/robaertschi/tt/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SourceProgram struct {
|
||||||
|
// The tt source file
|
||||||
|
InputFile string
|
||||||
|
// A list of additional assembly files to compile
|
||||||
|
// This file could be extended by different backends
|
||||||
|
// .asm is for fasm, .S for gas
|
||||||
|
InputAssemblies []string
|
||||||
|
// Additional object files, will also include the generated object files
|
||||||
|
ObjectFiles []string
|
||||||
|
// The linkded executable
|
||||||
|
OutputFile string
|
||||||
|
}
|
||||||
|
|
||||||
|
type task interface {
|
||||||
|
Run(id int, doneChan chan taskResult)
|
||||||
|
}
|
||||||
|
|
||||||
|
type processTask struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewProcessTask(id int, name string, args ...string) task {
|
||||||
|
return &processTask{
|
||||||
|
name: name,
|
||||||
|
args: args,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pt *processTask) Run(id int, doneChan chan taskResult) {
|
||||||
|
cmd := exec.Command(pt.name, pt.args...)
|
||||||
|
cmd.Stdout = utils.NewPrefixWriterString(os.Stdout, pt.name+" output: ")
|
||||||
|
cmd.Stderr = cmd.Stdout
|
||||||
|
|
||||||
|
err := cmd.Run()
|
||||||
|
var exitError error
|
||||||
|
if cmd.ProcessState.ExitCode() != 0 {
|
||||||
|
exitError = fmt.Errorf("command %q failed with exit code %d", pt.name, cmd.ProcessState.ExitCode())
|
||||||
|
}
|
||||||
|
doneChan <- taskResult{
|
||||||
|
Id: id,
|
||||||
|
Err: errors.Join(err, exitError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type removeFileTask struct {
|
||||||
|
file string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRemoveFileTask(file string) task {
|
||||||
|
return &removeFileTask{file: file}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rft *removeFileTask) Run(id int, doneChan chan taskResult) {
|
||||||
|
doneChan <- taskResult{
|
||||||
|
Id: id,
|
||||||
|
Err: os.Remove(rft.file),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type taskResult struct {
|
||||||
|
Id int
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
type node struct {
|
||||||
|
next *int
|
||||||
|
previous []int
|
||||||
|
task task
|
||||||
|
}
|
||||||
|
|
||||||
|
func runTasks(nodes map[int]*node, rootNodes []int) {
|
||||||
|
done := make(map[int]bool)
|
||||||
|
running := []int{}
|
||||||
|
doneChan := make(chan taskResult)
|
||||||
|
|
||||||
|
for id, node := range nodes {
|
||||||
|
// Initalize map
|
||||||
|
done[id] = false
|
||||||
|
|
||||||
|
// because we are already going trough the whole map, we might as well
|
||||||
|
// check the relations
|
||||||
|
if node.next != nil {
|
||||||
|
if _, ok := nodes[*node.next]; !ok {
|
||||||
|
panic(fmt.Sprintf("task with id %d has a invalid next node", id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, prev := range node.previous {
|
||||||
|
if _, ok := nodes[prev]; !ok {
|
||||||
|
panic(fmt.Sprintf("task with id %d has a invalid prev node", id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, rootNode := range rootNodes {
|
||||||
|
node := nodes[rootNode]
|
||||||
|
go node.task.Run(rootNode, doneChan)
|
||||||
|
running = append(running, rootNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case result := <-doneChan:
|
||||||
|
done[result.Id] = true
|
||||||
|
for i, id := range running {
|
||||||
|
if id == result.Id {
|
||||||
|
running = make([]int, len(running)-1)
|
||||||
|
running = append(running[:i], running[i+1:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node := nodes[result.Id]
|
||||||
|
if node.next != nil {
|
||||||
|
allDone := true
|
||||||
|
for _, prev := range node.previous {
|
||||||
|
if !done[prev] {
|
||||||
|
allDone = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sp *SourceProgram) Build(backend asm.Backend) {
|
||||||
|
|
||||||
|
}
|
53
cmd/cmd.go
53
cmd/cmd.go
@ -1,7 +1,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
@ -15,6 +14,7 @@ import (
|
|||||||
"robaertschi.xyz/robaertschi/tt/token"
|
"robaertschi.xyz/robaertschi/tt/token"
|
||||||
"robaertschi.xyz/robaertschi/tt/ttir"
|
"robaertschi.xyz/robaertschi/tt/ttir"
|
||||||
"robaertschi.xyz/robaertschi/tt/typechecker"
|
"robaertschi.xyz/robaertschi/tt/typechecker"
|
||||||
|
"robaertschi.xyz/robaertschi/tt/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ToPrintFlags int
|
type ToPrintFlags int
|
||||||
@ -32,55 +32,6 @@ type Arguments struct {
|
|||||||
ToPrint ToPrintFlags
|
ToPrint ToPrintFlags
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefix writer writes a prefix before each new line from another io.Writer
|
|
||||||
type PrefixWriter struct {
|
|
||||||
output io.Writer
|
|
||||||
outputPrefix []byte
|
|
||||||
outputPrefixWritten bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPrefixWriter(output io.Writer, prefix []byte) *PrefixWriter {
|
|
||||||
return &PrefixWriter{
|
|
||||||
output: output,
|
|
||||||
outputPrefix: prefix,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPrefixWriterString(output io.Writer, prefix string) *PrefixWriter {
|
|
||||||
return &PrefixWriter{
|
|
||||||
output: output,
|
|
||||||
outputPrefix: []byte(prefix),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *PrefixWriter) Write(p []byte) (n int, err error) {
|
|
||||||
|
|
||||||
toWrites := bytes.SplitAfter(p, []byte{'\n'})
|
|
||||||
|
|
||||||
for _, toWrite := range toWrites {
|
|
||||||
if len(toWrite) <= 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !w.outputPrefixWritten {
|
|
||||||
w.outputPrefixWritten = true
|
|
||||||
w.output.Write(w.outputPrefix)
|
|
||||||
}
|
|
||||||
|
|
||||||
if bytes.Contains(toWrite, []byte{'\n'}) {
|
|
||||||
w.outputPrefixWritten = false
|
|
||||||
}
|
|
||||||
|
|
||||||
var written int
|
|
||||||
written, err = w.output.Write(toWrite)
|
|
||||||
n += written
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func Compile(args Arguments) {
|
func Compile(args Arguments) {
|
||||||
output := args.Output
|
output := args.Output
|
||||||
input := args.Input
|
input := args.Input
|
||||||
@ -168,7 +119,7 @@ func Compile(args Arguments) {
|
|||||||
if !onlyEmitAsm {
|
if !onlyEmitAsm {
|
||||||
args := []string{asmOutputName, output}
|
args := []string{asmOutputName, output}
|
||||||
cmd := exec.Command(fasmPath, args...)
|
cmd := exec.Command(fasmPath, args...)
|
||||||
cmd.Stdout = NewPrefixWriterString(os.Stdout, "fasm output: ")
|
cmd.Stdout = utils.NewPrefixWriterString(os.Stdout, "fasm output: ")
|
||||||
cmd.Stderr = cmd.Stdout
|
cmd.Stderr = cmd.Stdout
|
||||||
|
|
||||||
err = cmd.Run()
|
err = cmd.Run()
|
||||||
|
41
language.md
41
language.md
@ -9,3 +9,44 @@ fn main() = {
|
|||||||
i
|
i
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Reference
|
||||||
|
|
||||||
|
### Basic Datatypes
|
||||||
|
|
||||||
|
#### Numbers
|
||||||
|
|
||||||
|
There is currently only one number type, `i64`, `i64` is a signed integer of the size of 64 bits.
|
||||||
|
|
||||||
|
#### Booleans
|
||||||
|
|
||||||
|
The boolean type `bool` can be either true or false, nothing else, it's size is implementation dependend and is only guaranteed to be 1 bit big.
|
||||||
|
|
||||||
|
### Expressions
|
||||||
|
|
||||||
|
There are many types of expression, tt is expression oriented.
|
||||||
|
|
||||||
|
#### Integer Expression
|
||||||
|
|
||||||
|
A Integer Expression contains an untyped, non-floating point, integer.
|
||||||
|
```tt
|
||||||
|
1234567890
|
||||||
|
100000
|
||||||
|
```
|
||||||
|
The Integer Expression must at minimum support the largest number type.
|
||||||
|
|
||||||
|
#### Boolean Expression
|
||||||
|
Is either the keyword `true` or `false`.
|
||||||
|
```tt
|
||||||
|
true
|
||||||
|
false
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Binary Expression
|
||||||
|
|
||||||
|
A Binary Expression is a expression with two expression and an operator between them. A Operator has a precedence, that deteirmines, which way they have to be parsed.
|
||||||
|
|
||||||
|
##### Operators
|
||||||
|
- `+` Adds two numbers with the same type together
|
||||||
|
- `-` Subtracts the left expression with the right expression, they have the same type
|
||||||
|
- `*`
|
||||||
|
@ -1 +1,55 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Prefix writer writes a prefix before each new line from another io.Writer
|
||||||
|
type PrefixWriter struct {
|
||||||
|
output io.Writer
|
||||||
|
outputPrefix []byte
|
||||||
|
outputPrefixWritten bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPrefixWriter(output io.Writer, prefix []byte) *PrefixWriter {
|
||||||
|
return &PrefixWriter{
|
||||||
|
output: output,
|
||||||
|
outputPrefix: prefix,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPrefixWriterString(output io.Writer, prefix string) *PrefixWriter {
|
||||||
|
return &PrefixWriter{
|
||||||
|
output: output,
|
||||||
|
outputPrefix: []byte(prefix),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *PrefixWriter) Write(p []byte) (n int, err error) {
|
||||||
|
|
||||||
|
toWrites := bytes.SplitAfter(p, []byte{'\n'})
|
||||||
|
|
||||||
|
for _, toWrite := range toWrites {
|
||||||
|
if len(toWrite) <= 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !w.outputPrefixWritten {
|
||||||
|
w.outputPrefixWritten = true
|
||||||
|
w.output.Write(w.outputPrefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Contains(toWrite, []byte{'\n'}) {
|
||||||
|
w.outputPrefixWritten = false
|
||||||
|
}
|
||||||
|
|
||||||
|
var written int
|
||||||
|
written, err = w.output.Write(toWrite)
|
||||||
|
n += written
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user