Implemented source tracking and type checking
All checks were successful
/ test (push) Successful in 21s
All checks were successful
/ test (push) Successful in 21s
This commit is contained in:
parent
70c2da4a4d
commit
329fcfff6f
30 changed files with 427 additions and 125 deletions
|
@ -6,6 +6,7 @@ import (
|
|||
"git.urbach.dev/cli/q/src/build"
|
||||
"git.urbach.dev/cli/q/src/core"
|
||||
"git.urbach.dev/cli/q/src/scanner"
|
||||
"git.urbach.dev/cli/q/src/types"
|
||||
)
|
||||
|
||||
// Compile waits for the scan to finish and compiles all functions.
|
||||
|
@ -16,13 +17,43 @@ func Compile(b *build.Build) (*core.Environment, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// Check for existence of `init`
|
||||
init, exists := all.Functions["core.init"]
|
||||
|
||||
if !exists {
|
||||
return nil, MissingInitFunction
|
||||
}
|
||||
|
||||
// Check for existence of `main`
|
||||
_, exists := all.Functions["main.main"]
|
||||
_, exists = all.Functions["main.main"]
|
||||
|
||||
if !exists {
|
||||
return nil, MissingMainFunction
|
||||
}
|
||||
|
||||
// Resolve types
|
||||
for _, f := range all.Functions {
|
||||
f.Type = &types.Function{
|
||||
Input: make([]types.Type, len(f.Input)),
|
||||
Output: make([]types.Type, len(f.Output)),
|
||||
}
|
||||
|
||||
for i, input := range f.Input {
|
||||
input.Typ = types.Parse(input.Source[1:], f.File.Bytes)
|
||||
f.Type.Input[i] = input.Typ
|
||||
}
|
||||
|
||||
for i, output := range f.Output {
|
||||
if len(output.Source) > 1 {
|
||||
output.Typ = types.Parse(output.Source[1:], f.File.Bytes)
|
||||
} else {
|
||||
output.Typ = types.Parse(output.Source, f.File.Bytes)
|
||||
}
|
||||
|
||||
f.Type.Output[i] = output.Typ
|
||||
}
|
||||
}
|
||||
|
||||
compileFunctions(maps.Values(all.Functions))
|
||||
|
||||
for _, f := range all.Functions {
|
||||
|
@ -32,7 +63,7 @@ func Compile(b *build.Build) (*core.Environment, error) {
|
|||
}
|
||||
|
||||
if b.ShowSSA {
|
||||
showSSA(maps.Values(all.Functions))
|
||||
showSSA(init)
|
||||
}
|
||||
|
||||
return all, nil
|
||||
|
|
|
@ -3,5 +3,6 @@ package compiler
|
|||
import "git.urbach.dev/cli/q/src/errors"
|
||||
|
||||
var (
|
||||
MissingInitFunction = errors.String("Missing init function")
|
||||
MissingMainFunction = errors.String("Missing main function")
|
||||
)
|
|
@ -2,16 +2,15 @@ package compiler
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"iter"
|
||||
|
||||
"git.urbach.dev/cli/q/src/core"
|
||||
"git.urbach.dev/go/color/ansi"
|
||||
)
|
||||
|
||||
// showSSA shows the SSA IR.
|
||||
func showSSA(functions iter.Seq[*core.Function]) {
|
||||
for f := range functions {
|
||||
ansi.Bold.Printf("%s:\n", f.UniqueName)
|
||||
func showSSA(root *core.Function) {
|
||||
root.EachDependency(make(map[*core.Function]bool), func(f *core.Function) {
|
||||
ansi.Yellow.Printf("%s:\n", f.UniqueName)
|
||||
|
||||
for i, block := range f.Blocks {
|
||||
if i != 0 {
|
||||
|
@ -19,8 +18,13 @@ func showSSA(functions iter.Seq[*core.Function]) {
|
|||
}
|
||||
|
||||
for i, instr := range block.Instructions {
|
||||
fmt.Printf("t%d = %s\n", i, instr.String())
|
||||
ansi.Dim.Printf("%-4d", i)
|
||||
fmt.Printf("%-40s", instr.String())
|
||||
ansi.Cyan.Printf("%-30s", instr.Type().Name())
|
||||
ansi.Dim.Printf("%s\n", f.File.Bytes[instr.Start():instr.End()])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
})
|
||||
}
|
|
@ -8,7 +8,7 @@ import (
|
|||
func (f *Function) CheckDeadCode() error {
|
||||
for instr := range f.Values {
|
||||
if instr.IsConst() && instr.Alive() == 0 {
|
||||
return errors.New(&UnusedValue{Value: instr.String()}, f.File, instr.Token().Position)
|
||||
return errors.New(&UnusedValue{Value: instr.String()}, f.File, instr.Start())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"git.urbach.dev/cli/q/src/asm"
|
||||
"git.urbach.dev/cli/q/src/cpu"
|
||||
"git.urbach.dev/cli/q/src/ssa"
|
||||
"git.urbach.dev/cli/q/src/token"
|
||||
"git.urbach.dev/cli/q/src/types"
|
||||
)
|
||||
|
||||
// Compile turns a function into machine code.
|
||||
|
@ -18,12 +18,35 @@ func (f *Function) Compile() {
|
|||
continue
|
||||
}
|
||||
|
||||
f.Identifiers[input.Name] = f.AppendRegister(i + extra)
|
||||
array, isArray := input.Typ.(*types.Array)
|
||||
|
||||
if input.TypeTokens[0].Kind == token.ArrayStart {
|
||||
if isArray {
|
||||
pointer := &ssa.Parameter{
|
||||
Index: uint8(i + extra),
|
||||
Name: input.Name,
|
||||
Typ: &types.Pointer{To: array.Of},
|
||||
Source: input.Source,
|
||||
}
|
||||
|
||||
f.Append(pointer)
|
||||
f.Identifiers[pointer.Name] = pointer
|
||||
extra++
|
||||
f.Identifiers[input.Name+".length"] = f.AppendRegister(i + extra)
|
||||
|
||||
length := &ssa.Parameter{
|
||||
Index: uint8(i + extra),
|
||||
Name: input.Name + ".len",
|
||||
Typ: types.AnyInt,
|
||||
Source: input.Source,
|
||||
}
|
||||
|
||||
f.Append(length)
|
||||
f.Identifiers[length.Name] = length
|
||||
continue
|
||||
}
|
||||
|
||||
input.Index = uint8(i + extra)
|
||||
f.Append(input)
|
||||
f.Identifiers[input.Name] = input
|
||||
}
|
||||
|
||||
for instr := range f.Body.Instructions {
|
||||
|
@ -49,15 +72,15 @@ func (f *Function) Compile() {
|
|||
for instr := range f.Values {
|
||||
switch instr := instr.(type) {
|
||||
case *ssa.Call:
|
||||
f.mv(instr.Args[1:], f.CPU.Call)
|
||||
f.mv(instr.Arguments[1:], f.CPU.Call)
|
||||
|
||||
switch arg := instr.Args[0].(type) {
|
||||
switch arg := instr.Arguments[0].(type) {
|
||||
case *ssa.Function:
|
||||
f.Assembler.Instructions = append(f.Assembler.Instructions, &asm.Call{Label: arg.UniqueName})
|
||||
}
|
||||
|
||||
case *ssa.Syscall:
|
||||
f.mv(instr.Args, f.CPU.Syscall)
|
||||
f.mv(instr.Arguments, f.CPU.Syscall)
|
||||
f.Assembler.Append(&asm.Syscall{})
|
||||
|
||||
case *ssa.Return:
|
||||
|
|
|
@ -16,9 +16,8 @@ func (f *Function) CompileReturn(tokens token.List) error {
|
|||
}
|
||||
|
||||
f.Append(&ssa.Return{
|
||||
Arguments: ssa.Arguments{
|
||||
Args: []ssa.Value{value},
|
||||
},
|
||||
Arguments: []ssa.Value{value},
|
||||
Source: ssa.Source(tokens),
|
||||
})
|
||||
|
||||
return nil
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"git.urbach.dev/cli/q/src/expression"
|
||||
"git.urbach.dev/cli/q/src/ssa"
|
||||
"git.urbach.dev/cli/q/src/token"
|
||||
"git.urbach.dev/cli/q/src/types"
|
||||
)
|
||||
|
||||
// Evaluate converts an expression to an SSA value.
|
||||
|
@ -26,8 +27,8 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) {
|
|||
}
|
||||
|
||||
f.Dependencies.Add(function)
|
||||
v := f.AppendFunction(function.UniqueName)
|
||||
v.Source = expr.Token
|
||||
v := f.AppendFunction(function.UniqueName, function.Type)
|
||||
v.Source = ssa.Source(expr.Source)
|
||||
return v, nil
|
||||
}
|
||||
|
||||
|
@ -41,14 +42,14 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) {
|
|||
}
|
||||
|
||||
v := f.AppendInt(number)
|
||||
v.Source = expr.Token
|
||||
v.Source = ssa.Source(expr.Source)
|
||||
return v, nil
|
||||
|
||||
case token.String:
|
||||
data := expr.Token.Bytes(f.File.Bytes)
|
||||
data = Unescape(data)
|
||||
v := f.AppendBytes(data)
|
||||
v.Source = expr.Token
|
||||
v.Source = ssa.Source(expr.Source)
|
||||
return v, nil
|
||||
}
|
||||
|
||||
|
@ -65,7 +66,13 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) {
|
|||
|
||||
if funcName == "len" {
|
||||
identifier := children[1].String(f.File.Bytes)
|
||||
return f.Identifiers[identifier+".length"], nil
|
||||
length, exists := f.Identifiers[identifier+".len"]
|
||||
|
||||
if !exists {
|
||||
return nil, errors.New(&UnknownIdentifier{Name: identifier + ".len"}, f.File, expr.Token.Position)
|
||||
}
|
||||
|
||||
return length, nil
|
||||
}
|
||||
|
||||
if funcName == "syscall" {
|
||||
|
@ -87,19 +94,37 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) {
|
|||
}
|
||||
|
||||
if isSyscall {
|
||||
v := f.Append(&ssa.Syscall{
|
||||
Arguments: ssa.Arguments{Args: args},
|
||||
HasToken: ssa.HasToken{Source: expr.Token},
|
||||
})
|
||||
syscall := &ssa.Syscall{
|
||||
Arguments: args,
|
||||
Source: ssa.Source(expr.Source),
|
||||
}
|
||||
|
||||
return v, nil
|
||||
return f.Append(syscall), nil
|
||||
} else {
|
||||
v := f.Append(&ssa.Call{
|
||||
Arguments: ssa.Arguments{Args: args},
|
||||
HasToken: ssa.HasToken{Source: expr.Token},
|
||||
})
|
||||
name := args[0].(*ssa.Function).UniqueName
|
||||
fn := f.All.Functions[name]
|
||||
parameters := args[1:]
|
||||
|
||||
return v, nil
|
||||
if len(parameters) != len(fn.Input) {
|
||||
return nil, errors.New(&ParameterCountMismatch{Function: name, Count: len(parameters), ExpectedCount: len(fn.Input)}, f.File, expr.Source[0].Position)
|
||||
}
|
||||
|
||||
for i, param := range parameters {
|
||||
if !types.Is(param.Type(), fn.Input[i].Typ) {
|
||||
return nil, errors.New(&TypeMismatch{
|
||||
Encountered: param.Type().Name(),
|
||||
Expected: fn.Input[i].Typ.Name(),
|
||||
ParameterName: fn.Input[i].Name,
|
||||
}, f.File, param.Start())
|
||||
}
|
||||
}
|
||||
|
||||
call := &ssa.Call{
|
||||
Arguments: args,
|
||||
Source: ssa.Source(expr.Source),
|
||||
}
|
||||
|
||||
return f.Append(call), nil
|
||||
}
|
||||
|
||||
case token.Dot:
|
||||
|
@ -113,8 +138,8 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) {
|
|||
}
|
||||
|
||||
f.Dependencies.Add(function)
|
||||
v := f.AppendFunction(function.UniqueName)
|
||||
v.Source = expr.Children[1].Token
|
||||
v := f.AppendFunction(function.UniqueName, function.Type)
|
||||
v.Source = ssa.Source(expr.Source)
|
||||
return v, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"git.urbach.dev/cli/q/src/set"
|
||||
"git.urbach.dev/cli/q/src/ssa"
|
||||
"git.urbach.dev/cli/q/src/token"
|
||||
"git.urbach.dev/cli/q/src/types"
|
||||
)
|
||||
|
||||
// Function is the smallest unit of code.
|
||||
|
@ -17,14 +18,15 @@ type Function struct {
|
|||
Name string
|
||||
UniqueName string
|
||||
File *fs.File
|
||||
Input []*Parameter
|
||||
Output []*Parameter
|
||||
Input []*ssa.Parameter
|
||||
Output []*ssa.Parameter
|
||||
Body token.List
|
||||
Identifiers map[string]ssa.Value
|
||||
All *Environment
|
||||
Dependencies set.Ordered[*Function]
|
||||
Assembler asm.Assembler
|
||||
CPU *cpu.CPU
|
||||
Type *types.Function
|
||||
Err error
|
||||
}
|
||||
|
||||
|
|
|
@ -18,8 +18,4 @@ func TestFunction(t *testing.T) {
|
|||
assert.False(t, main.IsExtern())
|
||||
assert.Equal(t, main.UniqueName, "main.main")
|
||||
assert.Equal(t, main.String(), main.UniqueName)
|
||||
|
||||
write, exists := env.Functions["io.write"]
|
||||
assert.True(t, exists)
|
||||
write.Output[0].Type()
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"git.urbach.dev/cli/q/src/token"
|
||||
"git.urbach.dev/cli/q/src/types"
|
||||
)
|
||||
|
||||
// Parameter is an input or output parameter in a function.
|
||||
type Parameter struct {
|
||||
Name string
|
||||
TypeTokens token.List
|
||||
typ types.Type
|
||||
}
|
||||
|
||||
// Type returns the data type of the parameter.
|
||||
func (p *Parameter) Type() types.Type {
|
||||
return p.typ
|
||||
}
|
|
@ -12,6 +12,43 @@ var (
|
|||
InvalidRune = errors.String("Invalid rune")
|
||||
)
|
||||
|
||||
// ParameterCountMismatch error is created when the number of provided parameters doesn't match the function signature.
|
||||
type ParameterCountMismatch struct {
|
||||
Function string
|
||||
Count int
|
||||
ExpectedCount int
|
||||
}
|
||||
|
||||
func (err *ParameterCountMismatch) Error() string {
|
||||
if err.Count > err.ExpectedCount {
|
||||
return fmt.Sprintf("Too many parameters in '%s' function call", err.Function)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Not enough parameters in '%s' function call", err.Function)
|
||||
}
|
||||
|
||||
// TypeMismatch represents an error where a type requirement was not met.
|
||||
type TypeMismatch struct {
|
||||
Encountered string
|
||||
Expected string
|
||||
ParameterName string
|
||||
IsReturn bool
|
||||
}
|
||||
|
||||
func (err *TypeMismatch) Error() string {
|
||||
subject := "type"
|
||||
|
||||
if err.IsReturn {
|
||||
subject = "return type"
|
||||
}
|
||||
|
||||
if err.ParameterName != "" {
|
||||
return fmt.Sprintf("Expected parameter '%s' of %s '%s' (encountered '%s')", err.ParameterName, subject, err.Expected, err.Encountered)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Expected %s '%s' instead of '%s'", subject, err.Expected, err.Encountered)
|
||||
}
|
||||
|
||||
// UnknownIdentifier represents unknown identifiers.
|
||||
type UnknownIdentifier struct {
|
||||
Name string
|
||||
|
|
|
@ -11,6 +11,7 @@ type Expression struct {
|
|||
Parent *Expression
|
||||
Children []*Expression
|
||||
Token token.Token
|
||||
Source token.List
|
||||
precedence int8
|
||||
}
|
||||
|
||||
|
|
|
@ -7,12 +7,13 @@ import (
|
|||
)
|
||||
|
||||
// Parse generates an expression tree from tokens.
|
||||
func Parse(tokens []token.Token) *Expression {
|
||||
func Parse(tokens token.List) *Expression {
|
||||
var (
|
||||
cursor *Expression
|
||||
root *Expression
|
||||
groupLevel = 0
|
||||
groupPosition = 0
|
||||
cursorStart = 0
|
||||
)
|
||||
|
||||
for i, t := range tokens {
|
||||
|
@ -38,6 +39,7 @@ func Parse(tokens []token.Token) *Expression {
|
|||
parameters := NewList(tokens[groupPosition:i])
|
||||
node := New()
|
||||
node.Token.Position = tokens[groupPosition].Position
|
||||
node.Source = tokens[groupPosition:i]
|
||||
|
||||
switch t.Kind {
|
||||
case token.GroupEnd:
|
||||
|
@ -72,6 +74,7 @@ func Parse(tokens []token.Token) *Expression {
|
|||
continue
|
||||
}
|
||||
|
||||
group.Source = tokens[groupPosition:i]
|
||||
group.precedence = math.MaxInt8
|
||||
|
||||
if cursor == nil {
|
||||
|
@ -79,6 +82,7 @@ func Parse(tokens []token.Token) *Expression {
|
|||
cursor = New()
|
||||
cursor.Token.Position = tokens[groupPosition].Position
|
||||
cursor.Token.Kind = token.Array
|
||||
cursor.Source = tokens[groupPosition:i]
|
||||
cursor.precedence = precedence(token.Array)
|
||||
cursor.AddChild(group)
|
||||
root = cursor
|
||||
|
@ -88,6 +92,7 @@ func Parse(tokens []token.Token) *Expression {
|
|||
}
|
||||
} else {
|
||||
cursor.AddChild(group)
|
||||
cursor.Source = tokens[cursorStart : i+1]
|
||||
}
|
||||
|
||||
continue
|
||||
|
@ -100,9 +105,13 @@ func Parse(tokens []token.Token) *Expression {
|
|||
if t.Kind == token.Identifier || t.Kind == token.Number || t.Kind == token.String || t.Kind == token.Rune {
|
||||
if cursor != nil {
|
||||
node := NewLeaf(t)
|
||||
node.Source = tokens[i : i+1]
|
||||
cursor.Source = tokens[cursorStart : i+1]
|
||||
cursor.AddChild(node)
|
||||
} else {
|
||||
cursor = NewLeaf(t)
|
||||
cursorStart = i
|
||||
cursor.Source = tokens[i : i+1]
|
||||
root = cursor
|
||||
}
|
||||
|
||||
|
@ -115,12 +124,15 @@ func Parse(tokens []token.Token) *Expression {
|
|||
|
||||
if cursor == nil {
|
||||
cursor = NewLeaf(t)
|
||||
cursorStart = i
|
||||
cursor.Source = tokens[i : i+1]
|
||||
cursor.precedence = precedence(t.Kind)
|
||||
root = cursor
|
||||
continue
|
||||
}
|
||||
|
||||
node := NewLeaf(t)
|
||||
node.Source = tokens[i : i+1]
|
||||
node.precedence = precedence(t.Kind)
|
||||
|
||||
if cursor.Token.IsOperator() {
|
||||
|
@ -131,6 +143,7 @@ func Parse(tokens []token.Token) *Expression {
|
|||
if len(cursor.Children) == numOperands(cursor.Token.Kind) {
|
||||
cursor.LastChild().InsertAbove(node)
|
||||
} else {
|
||||
cursor.Source = tokens[cursorStart : i+1]
|
||||
cursor.AddChild(node)
|
||||
}
|
||||
} else {
|
||||
|
@ -169,5 +182,9 @@ func Parse(tokens []token.Token) *Expression {
|
|||
cursor = node
|
||||
}
|
||||
|
||||
if root != nil {
|
||||
root.Source = tokens
|
||||
}
|
||||
|
||||
return root
|
||||
}
|
|
@ -4,6 +4,7 @@ import (
|
|||
"git.urbach.dev/cli/q/src/core"
|
||||
"git.urbach.dev/cli/q/src/errors"
|
||||
"git.urbach.dev/cli/q/src/fs"
|
||||
"git.urbach.dev/cli/q/src/ssa"
|
||||
"git.urbach.dev/cli/q/src/token"
|
||||
)
|
||||
|
||||
|
@ -94,9 +95,9 @@ func scanSignature(file *fs.File, tokens token.List, i int, delimiter token.Kind
|
|||
return nil, i, errors.New(MissingType, file, param[0].End())
|
||||
}
|
||||
|
||||
function.Input = append(function.Input, &core.Parameter{
|
||||
Name: param[0].String(file.Bytes),
|
||||
TypeTokens: param[1:],
|
||||
function.Input = append(function.Input, &ssa.Parameter{
|
||||
Name: param[0].String(file.Bytes),
|
||||
Source: ssa.Source(param),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -127,18 +128,19 @@ func scanSignature(file *fs.File, tokens token.List, i int, delimiter token.Kind
|
|||
}
|
||||
|
||||
if len(param) == 1 {
|
||||
function.Output = append(function.Output, &core.Parameter{
|
||||
Name: "",
|
||||
TypeTokens: param,
|
||||
function.Output = append(function.Output, &ssa.Parameter{
|
||||
Name: "",
|
||||
Source: ssa.Source(param),
|
||||
})
|
||||
} else {
|
||||
function.Output = append(function.Output, &core.Parameter{
|
||||
Name: param[0].String(file.Bytes),
|
||||
TypeTokens: param[1:],
|
||||
function.Output = append(function.Output, &ssa.Parameter{
|
||||
Name: param[0].String(file.Bytes),
|
||||
Source: ssa.Source(param),
|
||||
})
|
||||
}
|
||||
|
||||
errorPos = param[len(param)-1].End() + 1
|
||||
}
|
||||
|
||||
return function, i, nil
|
||||
}
|
|
@ -1,20 +1,18 @@
|
|||
package ssa
|
||||
|
||||
type Arguments struct {
|
||||
Args []Value
|
||||
}
|
||||
type Arguments []Value
|
||||
|
||||
func (v *Arguments) Dependencies() []Value {
|
||||
return v.Args
|
||||
func (v Arguments) Dependencies() []Value {
|
||||
return v
|
||||
}
|
||||
|
||||
func (a Arguments) Equals(b Arguments) bool {
|
||||
if len(a.Args) != len(b.Args) {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := range a.Args {
|
||||
if !a.Args[i].Equals(b.Args[i]) {
|
||||
for i := range a {
|
||||
if !a[i].Equals(b[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
|
||||
"git.urbach.dev/cli/q/src/expression"
|
||||
"git.urbach.dev/cli/q/src/token"
|
||||
"git.urbach.dev/cli/q/src/types"
|
||||
)
|
||||
|
||||
type BinaryOperation struct {
|
||||
|
@ -12,7 +13,7 @@ type BinaryOperation struct {
|
|||
Right Value
|
||||
Op token.Kind
|
||||
Liveness
|
||||
HasToken
|
||||
Source
|
||||
}
|
||||
|
||||
func (v *BinaryOperation) Dependencies() []Value {
|
||||
|
@ -26,10 +27,6 @@ func (a *BinaryOperation) Equals(v Value) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
if a.Source.Kind != b.Source.Kind {
|
||||
return false
|
||||
}
|
||||
|
||||
if !a.Left.Equals(b.Left) {
|
||||
return false
|
||||
}
|
||||
|
@ -48,3 +45,7 @@ func (v *BinaryOperation) IsConst() bool {
|
|||
func (v *BinaryOperation) String() string {
|
||||
return fmt.Sprintf("%s %s %s", v.Left, expression.Operators[v.Op].Symbol, v.Right)
|
||||
}
|
||||
|
||||
func (v *BinaryOperation) Type() types.Type {
|
||||
return v.Left.Type()
|
||||
}
|
|
@ -1,11 +1,16 @@
|
|||
package ssa
|
||||
|
||||
import "bytes"
|
||||
import (
|
||||
"bytes"
|
||||
"strconv"
|
||||
|
||||
"git.urbach.dev/cli/q/src/types"
|
||||
)
|
||||
|
||||
type Bytes struct {
|
||||
Bytes []byte
|
||||
Liveness
|
||||
HasToken
|
||||
Source
|
||||
}
|
||||
|
||||
func (v *Bytes) Dependencies() []Value {
|
||||
|
@ -27,5 +32,9 @@ func (v *Bytes) IsConst() bool {
|
|||
}
|
||||
|
||||
func (v *Bytes) String() string {
|
||||
return string(v.Bytes)
|
||||
return strconv.Quote(string(v.Bytes))
|
||||
}
|
||||
|
||||
func (v *Bytes) Type() types.Type {
|
||||
return types.String
|
||||
}
|
|
@ -1,11 +1,15 @@
|
|||
package ssa
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.urbach.dev/cli/q/src/types"
|
||||
)
|
||||
|
||||
type Call struct {
|
||||
Arguments
|
||||
Liveness
|
||||
HasToken
|
||||
Source
|
||||
}
|
||||
|
||||
func (a *Call) Equals(v Value) bool {
|
||||
|
@ -23,5 +27,15 @@ func (v *Call) IsConst() bool {
|
|||
}
|
||||
|
||||
func (v *Call) String() string {
|
||||
return fmt.Sprintf("call%v", v.Args)
|
||||
return fmt.Sprintf("%s(%v)", v.Arguments[0], v.Arguments[1:])
|
||||
}
|
||||
|
||||
func (v *Call) Type() types.Type {
|
||||
typ := v.Arguments[0].(*Function).Typ
|
||||
|
||||
if len(typ.Output) == 0 {
|
||||
return types.Void
|
||||
}
|
||||
|
||||
return typ.Output[0]
|
||||
}
|
|
@ -1,9 +1,12 @@
|
|||
package ssa
|
||||
|
||||
import "git.urbach.dev/cli/q/src/types"
|
||||
|
||||
type Function struct {
|
||||
UniqueName string
|
||||
Typ *types.Function
|
||||
Liveness
|
||||
HasToken
|
||||
Source
|
||||
}
|
||||
|
||||
func (v *Function) Dependencies() []Value {
|
||||
|
@ -27,3 +30,7 @@ func (v *Function) IsConst() bool {
|
|||
func (v *Function) String() string {
|
||||
return v.UniqueName
|
||||
}
|
||||
|
||||
func (v *Function) Type() types.Type {
|
||||
return v.Typ
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package ssa
|
||||
|
||||
import "git.urbach.dev/cli/q/src/token"
|
||||
|
||||
type HasToken struct {
|
||||
Source token.Token
|
||||
}
|
||||
|
||||
func (v *HasToken) Token() token.Token {
|
||||
return v.Source
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
package ssa
|
||||
|
||||
import "git.urbach.dev/cli/q/src/types"
|
||||
|
||||
// IR is a list of basic blocks.
|
||||
type IR struct {
|
||||
Blocks []*Block
|
||||
|
@ -39,16 +41,9 @@ func (f *IR) AppendInt(x int) *Int {
|
|||
return v
|
||||
}
|
||||
|
||||
// AppendRegister adds a new register value to the last block.
|
||||
func (f *IR) AppendRegister(index int) *Parameter {
|
||||
v := &Parameter{Index: uint8(index)}
|
||||
f.Append(v)
|
||||
return v
|
||||
}
|
||||
|
||||
// AppendFunction adds a new function value to the last block.
|
||||
func (f *IR) AppendFunction(name string) *Function {
|
||||
v := &Function{UniqueName: name}
|
||||
func (f *IR) AppendFunction(name string, typ *types.Function) *Function {
|
||||
v := &Function{UniqueName: name, Typ: typ}
|
||||
f.Append(v)
|
||||
return v
|
||||
}
|
||||
|
|
|
@ -2,12 +2,14 @@ package ssa
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.urbach.dev/cli/q/src/types"
|
||||
)
|
||||
|
||||
type Int struct {
|
||||
Int int
|
||||
Liveness
|
||||
HasToken
|
||||
Source
|
||||
}
|
||||
|
||||
func (v *Int) Dependencies() []Value {
|
||||
|
@ -31,3 +33,7 @@ func (v *Int) IsConst() bool {
|
|||
func (v *Int) String() string {
|
||||
return fmt.Sprintf("%d", v.Int)
|
||||
}
|
||||
|
||||
func (v *Int) Type() types.Type {
|
||||
return types.AnyInt
|
||||
}
|
|
@ -1,11 +1,17 @@
|
|||
package ssa
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.urbach.dev/cli/q/src/types"
|
||||
)
|
||||
|
||||
type Parameter struct {
|
||||
Index uint8
|
||||
Name string
|
||||
Typ types.Type
|
||||
Liveness
|
||||
HasToken
|
||||
Source
|
||||
}
|
||||
|
||||
func (v *Parameter) Dependencies() []Value {
|
||||
|
@ -27,5 +33,9 @@ func (v *Parameter) IsConst() bool {
|
|||
}
|
||||
|
||||
func (v *Parameter) String() string {
|
||||
return fmt.Sprintf("arg[%d]", v.Index)
|
||||
return fmt.Sprintf("in[%d]", v.Index)
|
||||
}
|
||||
|
||||
func (v *Parameter) Type() types.Type {
|
||||
return v.Typ
|
||||
}
|
|
@ -1,10 +1,14 @@
|
|||
package ssa
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.urbach.dev/cli/q/src/types"
|
||||
)
|
||||
|
||||
type Return struct {
|
||||
Arguments
|
||||
HasToken
|
||||
Source
|
||||
}
|
||||
|
||||
func (a *Return) AddUse(user Value) { panic("return is not a value") }
|
||||
|
@ -17,12 +21,12 @@ func (a *Return) Equals(v Value) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
if len(a.Args) != len(b.Args) {
|
||||
if len(a.Arguments) != len(b.Arguments) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := range a.Args {
|
||||
if !a.Args[i].Equals(b.Args[i]) {
|
||||
for i := range a.Arguments {
|
||||
if !a.Arguments[i].Equals(b.Arguments[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -35,5 +39,9 @@ func (v *Return) IsConst() bool {
|
|||
}
|
||||
|
||||
func (v *Return) String() string {
|
||||
return fmt.Sprintf("return %v", v.Args)
|
||||
return fmt.Sprintf("return %v", v.Arguments)
|
||||
}
|
||||
|
||||
func (v *Return) Type() types.Type {
|
||||
return types.Void
|
||||
}
|
13
src/ssa/Source.go
Normal file
13
src/ssa/Source.go
Normal file
|
@ -0,0 +1,13 @@
|
|||
package ssa
|
||||
|
||||
import "git.urbach.dev/cli/q/src/token"
|
||||
|
||||
type Source token.List
|
||||
|
||||
func (v Source) Start() token.Position {
|
||||
return v[0].Position
|
||||
}
|
||||
|
||||
func (v Source) End() token.Position {
|
||||
return v[len(v)-1].End()
|
||||
}
|
|
@ -1,11 +1,15 @@
|
|||
package ssa
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.urbach.dev/cli/q/src/types"
|
||||
)
|
||||
|
||||
type Syscall struct {
|
||||
Arguments
|
||||
Liveness
|
||||
HasToken
|
||||
Source
|
||||
}
|
||||
|
||||
func (a *Syscall) Equals(v Value) bool {
|
||||
|
@ -23,5 +27,9 @@ func (v *Syscall) IsConst() bool {
|
|||
}
|
||||
|
||||
func (v *Syscall) String() string {
|
||||
return fmt.Sprintf("syscall%v", v.Args)
|
||||
return fmt.Sprintf("syscall(%v)", v.Arguments)
|
||||
}
|
||||
|
||||
func (v *Syscall) Type() types.Type {
|
||||
return types.Any
|
||||
}
|
|
@ -1,13 +1,18 @@
|
|||
package ssa
|
||||
|
||||
import "git.urbach.dev/cli/q/src/token"
|
||||
import (
|
||||
"git.urbach.dev/cli/q/src/token"
|
||||
"git.urbach.dev/cli/q/src/types"
|
||||
)
|
||||
|
||||
type Value interface {
|
||||
AddUse(Value)
|
||||
Alive() int
|
||||
Dependencies() []Value
|
||||
End() token.Position
|
||||
Equals(Value) bool
|
||||
IsConst() bool
|
||||
String() string
|
||||
Token() token.Token
|
||||
Start() token.Position
|
||||
Type() types.Type
|
||||
}
|
|
@ -17,6 +17,7 @@ var (
|
|||
UInt32 = &Base{name: "uint32", size: 4}
|
||||
UInt16 = &Base{name: "uint16", size: 2}
|
||||
UInt8 = &Base{name: "uint8", size: 1}
|
||||
Void = &Base{name: "void", size: 0}
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
47
src/types/Function.go
Normal file
47
src/types/Function.go
Normal file
|
@ -0,0 +1,47 @@
|
|||
package types
|
||||
|
||||
import "strings"
|
||||
|
||||
// Function transforms inputs to new outputs.
|
||||
type Function struct {
|
||||
Input []Type
|
||||
Output []Type
|
||||
}
|
||||
|
||||
// Name returns the type name.
|
||||
func (f *Function) Name() string {
|
||||
builder := strings.Builder{}
|
||||
builder.WriteString("(")
|
||||
|
||||
for i, input := range f.Input {
|
||||
builder.WriteString(input.Name())
|
||||
|
||||
if i != len(f.Input)-1 {
|
||||
builder.WriteString(", ")
|
||||
}
|
||||
}
|
||||
|
||||
builder.WriteString(")")
|
||||
|
||||
if len(f.Output) == 0 {
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
builder.WriteString(" -> (")
|
||||
|
||||
for i, output := range f.Output {
|
||||
builder.WriteString(output.Name())
|
||||
|
||||
if i != len(f.Output)-1 {
|
||||
builder.WriteString(",")
|
||||
}
|
||||
}
|
||||
|
||||
builder.WriteString(")")
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
// Size returns the total size in bytes.
|
||||
func (f *Function) Size() int {
|
||||
return 8
|
||||
}
|
71
src/types/Parse.go
Normal file
71
src/types/Parse.go
Normal file
|
@ -0,0 +1,71 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"git.urbach.dev/cli/q/src/token"
|
||||
)
|
||||
|
||||
// Parse returns the type with the given tokens or `nil` if it doesn't exist.
|
||||
func Parse[T ~[]token.Token](tokens T, source []byte) Type {
|
||||
if tokens[0].Kind == token.Mul {
|
||||
to := tokens[1:]
|
||||
typ := Parse(to, source)
|
||||
|
||||
if typ == Any {
|
||||
return AnyPointer
|
||||
}
|
||||
|
||||
return &Pointer{To: typ}
|
||||
}
|
||||
|
||||
if len(tokens) >= 2 && tokens[0].Kind == token.ArrayStart && tokens[1].Kind == token.ArrayEnd {
|
||||
to := tokens[2:]
|
||||
typ := Parse(to, source)
|
||||
|
||||
if typ == Any {
|
||||
return AnyArray
|
||||
}
|
||||
|
||||
return &Array{Of: typ}
|
||||
}
|
||||
|
||||
if tokens[0].Kind != token.Identifier {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch tokens[0].String(source) {
|
||||
case "int":
|
||||
return Int
|
||||
case "int64":
|
||||
return Int64
|
||||
case "int32":
|
||||
return Int32
|
||||
case "int16":
|
||||
return Int16
|
||||
case "int8":
|
||||
return Int8
|
||||
case "uint":
|
||||
return UInt
|
||||
case "uint64":
|
||||
return UInt64
|
||||
case "uint32":
|
||||
return UInt32
|
||||
case "uint16":
|
||||
return UInt16
|
||||
case "uint8":
|
||||
return UInt8
|
||||
case "byte":
|
||||
return Byte
|
||||
case "bool":
|
||||
return Bool
|
||||
case "float":
|
||||
return Float
|
||||
case "float64":
|
||||
return Float64
|
||||
case "float32":
|
||||
return Float32
|
||||
case "any":
|
||||
return Any
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue