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
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
@ -15,6 +14,7 @@ import (
|
||||
"robaertschi.xyz/robaertschi/tt/token"
|
||||
"robaertschi.xyz/robaertschi/tt/ttir"
|
||||
"robaertschi.xyz/robaertschi/tt/typechecker"
|
||||
"robaertschi.xyz/robaertschi/tt/utils"
|
||||
)
|
||||
|
||||
type ToPrintFlags int
|
||||
@ -32,55 +32,6 @@ type Arguments struct {
|
||||
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) {
|
||||
output := args.Output
|
||||
input := args.Input
|
||||
@ -168,7 +119,7 @@ func Compile(args Arguments) {
|
||||
if !onlyEmitAsm {
|
||||
args := []string{asmOutputName, output}
|
||||
cmd := exec.Command(fasmPath, args...)
|
||||
cmd.Stdout = NewPrefixWriterString(os.Stdout, "fasm output: ")
|
||||
cmd.Stdout = utils.NewPrefixWriterString(os.Stdout, "fasm output: ")
|
||||
cmd.Stderr = cmd.Stdout
|
||||
|
||||
err = cmd.Run()
|
||||
|
41
language.md
41
language.md
@ -9,3 +9,44 @@ fn main() = {
|
||||
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
|
||||
|
||||
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