Added types

This commit is contained in:
Eduard Urbach 2024-08-06 15:12:07 +02:00
parent 67a37cdb26
commit 6e848774ed
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
19 changed files with 122 additions and 60 deletions

View File

@ -25,7 +25,7 @@ func (f *Function) Compare(comparison *expression.Expression) error {
} }
if ast.IsFunctionCall(left) && right.IsLeaf() { if ast.IsFunctionCall(left) && right.IsLeaf() {
err := f.CompileCall(left) _, err := f.CompileCall(left)
if err != nil { if err != nil {
return err return err
@ -35,7 +35,7 @@ func (f *Function) Compare(comparison *expression.Expression) error {
} }
tmp := f.NewRegister() tmp := f.NewRegister()
err := f.ExpressionToRegister(left, tmp) _, err := f.ExpressionToRegister(left, tmp)
if err != nil { if err != nil {
return err return err

View File

@ -17,7 +17,8 @@ func (f *Function) CompileASTNode(node ast.Node) error {
case *ast.Call: case *ast.Call:
f.Fold(node.Expression) f.Fold(node.Expression)
return f.CompileCall(node.Expression) _, err := f.CompileCall(node.Expression)
return err
case *ast.Define: case *ast.Define:
f.Fold(node.Expression) f.Fold(node.Expression)

View File

@ -33,5 +33,6 @@ func (f *Function) CompileAssignArray(node *ast.Assign) error {
Length: byte(1), Length: byte(1),
} }
return f.ExpressionToMemory(right, memory) _, err = f.ExpressionToMemory(right, memory)
return err
} }

View File

@ -29,7 +29,7 @@ func (f *Function) CompileAssignDivision(node *ast.Assign) error {
} }
dividend := right.Children[0] dividend := right.Children[0]
dividendRegister, err := f.Evaluate(dividend) _, dividendRegister, err := f.Evaluate(dividend)
if err != nil { if err != nil {
return err return err

View File

@ -12,12 +12,14 @@ import (
// All call registers must hold the correct parameter values before the function invocation. // All call registers must hold the correct parameter values before the function invocation.
// Registers that are in use must be saved if they are modified by the function. // Registers that are in use must be saved if they are modified by the function.
// After the function call, they must be restored in reverse order. // After the function call, they must be restored in reverse order.
func (f *Function) CompileCall(root *expression.Expression) error { func (f *Function) CompileCall(root *expression.Expression) (*Function, error) {
var ( var (
pkg = f.Package pkg = f.Package
nameRoot = root.Children[0] nameRoot = root.Children[0]
fn *Function
name string name string
fullName string fullName string
exists bool
) )
if nameRoot.IsLeaf() { if nameRoot.IsLeaf() {
@ -32,13 +34,13 @@ func (f *Function) CompileCall(root *expression.Expression) error {
if !isSyscall { if !isSyscall {
if pkg != f.File.Package { if pkg != f.File.Package {
if f.File.Imports == nil { if f.File.Imports == nil {
return errors.New(&errors.UnknownPackage{Name: pkg}, f.File, nameRoot.Token.Position) return nil, errors.New(&errors.UnknownPackage{Name: pkg}, f.File, nameRoot.Token.Position)
} }
imp, exists := f.File.Imports[pkg] imp, exists := f.File.Imports[pkg]
if !exists { if !exists {
return errors.New(&errors.UnknownPackage{Name: pkg}, f.File, nameRoot.Token.Position) return nil, errors.New(&errors.UnknownPackage{Name: pkg}, f.File, nameRoot.Token.Position)
} }
imp.Used = true imp.Used = true
@ -49,10 +51,10 @@ func (f *Function) CompileCall(root *expression.Expression) error {
tmp.WriteString(".") tmp.WriteString(".")
tmp.WriteString(name) tmp.WriteString(name)
fullName = tmp.String() fullName = tmp.String()
_, exists := f.Functions[fullName] fn, exists = f.Functions[fullName]
if !exists { if !exists {
return errors.New(&errors.UnknownFunction{Name: name}, f.File, nameRoot.Token.Position) return nil, errors.New(&errors.UnknownFunction{Name: name}, f.File, nameRoot.Token.Position)
} }
} }
@ -66,7 +68,7 @@ func (f *Function) CompileCall(root *expression.Expression) error {
err := f.ExpressionsToRegisters(parameters, registers) err := f.ExpressionsToRegisters(parameters, registers)
if err != nil { if err != nil {
return err return fn, err
} }
// TODO: Save all return value registers of the function // TODO: Save all return value registers of the function
@ -104,5 +106,5 @@ func (f *Function) CompileCall(root *expression.Expression) error {
} }
} }
return nil return fn, nil
} }

View File

@ -5,6 +5,7 @@ import (
"git.akyoto.dev/cli/q/src/build/ast" "git.akyoto.dev/cli/q/src/build/ast"
"git.akyoto.dev/cli/q/src/build/errors" "git.akyoto.dev/cli/q/src/build/errors"
"git.akyoto.dev/cli/q/src/build/expression" "git.akyoto.dev/cli/q/src/build/expression"
"git.akyoto.dev/cli/q/src/build/types"
) )
// CompileDefinition compiles a variable definition. // CompileDefinition compiles a variable definition.
@ -19,9 +20,20 @@ func (f *Function) CompileDefinition(node *ast.Define) error {
return err return err
} }
err = f.ExpressionToRegister(right, variable.Register) typ, err := f.ExpressionToRegister(right, variable.Register)
if err != nil {
return err
}
variable.Type = typ
if variable.Type == types.Invalid {
return errors.New(errors.UnknownType, f.File, node.Expression.Token.End())
}
f.AddVariable(variable) f.AddVariable(variable)
return err return nil
} }
if !ast.IsFunctionCall(right) { if !ast.IsFunctionCall(right) {
@ -29,7 +41,7 @@ func (f *Function) CompileDefinition(node *ast.Define) error {
} }
count := 0 count := 0
err := f.CompileCall(right) _, err := f.CompileCall(right)
if err != nil { if err != nil {
return err return err

View File

@ -4,21 +4,22 @@ import (
"git.akyoto.dev/cli/q/src/build/cpu" "git.akyoto.dev/cli/q/src/build/cpu"
"git.akyoto.dev/cli/q/src/build/expression" "git.akyoto.dev/cli/q/src/build/expression"
"git.akyoto.dev/cli/q/src/build/token" "git.akyoto.dev/cli/q/src/build/token"
"git.akyoto.dev/cli/q/src/build/types"
) )
// Evaluate evaluates an expression and returns a register that contains the value of the expression. // Evaluate evaluates an expression and returns a register that contains the value of the expression.
func (f *Function) Evaluate(expr *expression.Expression) (cpu.Register, error) { func (f *Function) Evaluate(expr *expression.Expression) (types.Type, cpu.Register, error) {
if expr.Token.Kind == token.Identifier { if expr.Token.Kind == token.Identifier {
name := expr.Token.Text(f.File.Bytes) name := expr.Token.Text(f.File.Bytes)
variable := f.VariableByName(name) variable := f.VariableByName(name)
if variable.Alive == 1 { if variable.Alive == 1 {
f.UseVariable(variable) f.UseVariable(variable)
return variable.Register, nil return variable.Type, variable.Register, nil
} }
} }
tmp := f.NewRegister() tmp := f.NewRegister()
err := f.ExpressionToRegister(expr, tmp) typ, err := f.ExpressionToRegister(expr, tmp)
return tmp, err return typ, tmp, err
} }

View File

@ -14,7 +14,7 @@ func (f *Function) Execute(operation token.Token, register cpu.Register, value *
} }
if ast.IsFunctionCall(value) { if ast.IsFunctionCall(value) {
err := f.CompileCall(value) _, err := f.CompileCall(value)
if err != nil { if err != nil {
return err return err
@ -26,7 +26,7 @@ func (f *Function) Execute(operation token.Token, register cpu.Register, value *
tmp := f.NewRegister() tmp := f.NewRegister()
defer f.FreeRegister(tmp) defer f.FreeRegister(tmp)
err := f.ExpressionToRegister(value, tmp) _, err := f.ExpressionToRegister(value, tmp)
if err != nil { if err != nil {
return err return err

View File

@ -31,7 +31,8 @@ func (f *Function) ExecuteLeaf(operation token.Token, register cpu.Register, ope
case token.String: case token.String:
if operation.Kind == token.Assign { if operation.Kind == token.Assign {
return f.TokenToRegister(operand, register) _, err := f.TokenToRegister(operand, register)
return err
} }
} }

View File

@ -5,30 +5,31 @@ import (
"git.akyoto.dev/cli/q/src/build/errors" "git.akyoto.dev/cli/q/src/build/errors"
"git.akyoto.dev/cli/q/src/build/expression" "git.akyoto.dev/cli/q/src/build/expression"
"git.akyoto.dev/cli/q/src/build/sizeof" "git.akyoto.dev/cli/q/src/build/sizeof"
"git.akyoto.dev/cli/q/src/build/types"
) )
// ExpressionToMemory puts the result of an expression into the specified memory address. // ExpressionToMemory puts the result of an expression into the specified memory address.
func (f *Function) ExpressionToMemory(node *expression.Expression, memory asm.Memory) error { func (f *Function) ExpressionToMemory(node *expression.Expression, memory asm.Memory) (types.Type, error) {
if node.IsLeaf() && node.Token.IsNumeric() { if node.IsLeaf() && node.Token.IsNumeric() {
number, err := f.Number(node.Token) number, err := f.Number(node.Token)
if err != nil { if err != nil {
return err return types.Invalid, err
} }
size := byte(sizeof.Signed(int64(number))) size := byte(sizeof.Signed(int64(number)))
if size != memory.Length { if size != memory.Length {
return errors.New(&errors.NumberExceedsBounds{Number: number, ExpectedSize: memory.Length, Size: size}, f.File, node.Token.Position) return types.Invalid, errors.New(&errors.NumberExceedsBounds{Number: number, ExpectedSize: memory.Length, Size: size}, f.File, node.Token.Position)
} }
f.MemoryNumber(asm.STORE, memory, number) f.MemoryNumber(asm.STORE, memory, number)
return nil return types.Int, nil
} }
tmp := f.NewRegister() tmp := f.NewRegister()
defer f.FreeRegister(tmp) defer f.FreeRegister(tmp)
err := f.ExpressionToRegister(node, tmp) typ, err := f.ExpressionToRegister(node, tmp)
f.MemoryRegister(asm.STORE, memory, tmp) f.MemoryRegister(asm.STORE, memory, tmp)
return err return typ, err
} }

View File

@ -7,15 +7,16 @@ import (
"git.akyoto.dev/cli/q/src/build/errors" "git.akyoto.dev/cli/q/src/build/errors"
"git.akyoto.dev/cli/q/src/build/expression" "git.akyoto.dev/cli/q/src/build/expression"
"git.akyoto.dev/cli/q/src/build/token" "git.akyoto.dev/cli/q/src/build/token"
"git.akyoto.dev/cli/q/src/build/types"
) )
// ExpressionToRegister puts the result of an expression into the specified register. // ExpressionToRegister puts the result of an expression into the specified register.
func (f *Function) ExpressionToRegister(node *expression.Expression, register cpu.Register) error { func (f *Function) ExpressionToRegister(node *expression.Expression, register cpu.Register) (types.Type, error) {
f.SaveRegister(register) f.SaveRegister(register)
if node.IsFolded { if node.IsFolded {
f.RegisterNumber(asm.MOVE, register, node.Value) f.RegisterNumber(asm.MOVE, register, node.Value)
return nil return types.Int, nil
} }
if node.IsLeaf() { if node.IsLeaf() {
@ -23,34 +24,38 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp
} }
if ast.IsFunctionCall(node) { if ast.IsFunctionCall(node) {
err := f.CompileCall(node) fn, err := f.CompileCall(node)
if register != f.CPU.Output[0] { if register != f.CPU.Output[0] {
f.RegisterRegister(asm.MOVE, register, f.CPU.Output[0]) f.RegisterRegister(asm.MOVE, register, f.CPU.Output[0])
} }
return err if fn == nil || len(fn.ReturnTypes) == 0 {
return types.Invalid, err
}
return fn.ReturnTypes[0], err
} }
if node.Token.Kind == token.Array { if node.Token.Kind == token.Array {
array := f.VariableByName(node.Children[0].Token.Text(f.File.Bytes)) array := f.VariableByName(node.Children[0].Token.Text(f.File.Bytes))
offset, err := f.Number(node.Children[1].Token) offset, err := f.Number(node.Children[1].Token)
f.MemoryRegister(asm.LOAD, asm.Memory{Base: array.Register, Offset: byte(offset), Length: 1}, register) f.MemoryRegister(asm.LOAD, asm.Memory{Base: array.Register, Offset: byte(offset), Length: 1}, register)
return err return types.Int, err
} }
if len(node.Children) == 1 { if len(node.Children) == 1 {
if !node.Token.IsUnaryOperator() { if !node.Token.IsUnaryOperator() {
return errors.New(errors.MissingOperand, f.File, node.Token.End()) return types.Invalid, errors.New(errors.MissingOperand, f.File, node.Token.End())
} }
err := f.ExpressionToRegister(node.Children[0], register) typ, err := f.ExpressionToRegister(node.Children[0], register)
if err != nil { if err != nil {
return err return typ, err
} }
return f.ExecuteRegister(node.Token, register) return typ, f.ExecuteRegister(node.Token, register)
} }
left := node.Children[0] left := node.Children[0]
@ -61,10 +66,10 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp
register = f.NewRegister() register = f.NewRegister()
} }
err := f.ExpressionToRegister(left, register) typ, err := f.ExpressionToRegister(left, register)
if err != nil { if err != nil {
return err return types.Invalid, err
} }
err = f.Execute(node.Token, register, right) err = f.Execute(node.Token, register, right)
@ -74,5 +79,5 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp
f.FreeRegister(register) f.FreeRegister(register)
} }
return err return typ, err
} }

View File

@ -8,7 +8,7 @@ import (
// ExpressionsToRegisters moves multiple expressions into the specified registers. // ExpressionsToRegisters moves multiple expressions into the specified registers.
func (f *Function) ExpressionsToRegisters(expressions []*expression.Expression, registers []cpu.Register) error { func (f *Function) ExpressionsToRegisters(expressions []*expression.Expression, registers []cpu.Register) error {
for i := len(expressions) - 1; i >= 0; i-- { for i := len(expressions) - 1; i >= 0; i-- {
err := f.ExpressionToRegister(expressions[i], registers[i]) _, err := f.ExpressionToRegister(expressions[i], registers[i])
if err != nil { if err != nil {
return err return err

View File

@ -3,21 +3,25 @@ package core
import ( import (
"git.akyoto.dev/cli/q/src/build/fs" "git.akyoto.dev/cli/q/src/build/fs"
"git.akyoto.dev/cli/q/src/build/register" "git.akyoto.dev/cli/q/src/build/register"
"git.akyoto.dev/cli/q/src/build/scope"
"git.akyoto.dev/cli/q/src/build/token" "git.akyoto.dev/cli/q/src/build/token"
"git.akyoto.dev/cli/q/src/build/types"
) )
// Function represents the smallest unit of code. // Function represents the smallest unit of code.
type Function struct { type Function struct {
register.Machine register.Machine
Package string Package string
Name string Name string
UniqueName string UniqueName string
File *fs.File File *fs.File
Body []token.Token Body token.List
Functions map[string]*Function Parameters []*scope.Variable
Err error ReturnTypes []types.Type
deferred []func() Functions map[string]*Function
count counter Err error
deferred []func()
count counter
} }
// counter stores how often a certain statement appeared so we can generate a unique label from it. // counter stores how often a certain statement appeared so we can generate a unique label from it.

View File

@ -5,35 +5,36 @@ import (
"git.akyoto.dev/cli/q/src/build/cpu" "git.akyoto.dev/cli/q/src/build/cpu"
"git.akyoto.dev/cli/q/src/build/errors" "git.akyoto.dev/cli/q/src/build/errors"
"git.akyoto.dev/cli/q/src/build/token" "git.akyoto.dev/cli/q/src/build/token"
"git.akyoto.dev/cli/q/src/build/types"
) )
// TokenToRegister moves a token into a register. // TokenToRegister moves a token into a register.
// It only works with identifiers, numbers and strings. // It only works with identifiers, numbers and strings.
func (f *Function) TokenToRegister(t token.Token, register cpu.Register) error { func (f *Function) TokenToRegister(t token.Token, register cpu.Register) (types.Type, error) {
switch t.Kind { switch t.Kind {
case token.Identifier: case token.Identifier:
name := t.Text(f.File.Bytes) name := t.Text(f.File.Bytes)
variable := f.VariableByName(name) variable := f.VariableByName(name)
if variable == nil { if variable == nil {
return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, t.Position) return types.Invalid, errors.New(&errors.UnknownIdentifier{Name: name}, f.File, t.Position)
} }
f.UseVariable(variable) f.UseVariable(variable)
f.SaveRegister(register) f.SaveRegister(register)
f.RegisterRegister(asm.MOVE, register, variable.Register) f.RegisterRegister(asm.MOVE, register, variable.Register)
return nil return variable.Type, nil
case token.Number, token.Rune: case token.Number, token.Rune:
number, err := f.Number(t) number, err := f.Number(t)
if err != nil { if err != nil {
return err return types.Invalid, err
} }
f.SaveRegister(register) f.SaveRegister(register)
f.RegisterNumber(asm.MOVE, register, number) f.RegisterNumber(asm.MOVE, register, number)
return nil return types.Int, nil
case token.String: case token.String:
data := t.Bytes(f.File.Bytes) data := t.Bytes(f.File.Bytes)
@ -41,9 +42,9 @@ func (f *Function) TokenToRegister(t token.Token, register cpu.Register) error {
label := f.AddBytes(data) label := f.AddBytes(data)
f.SaveRegister(register) f.SaveRegister(register)
f.RegisterLabel(asm.MOVE, register, label) f.RegisterLabel(asm.MOVE, register, label)
return nil return types.Int, nil
default: default:
return errors.New(errors.InvalidExpression, f.File, t.Position) return types.Invalid, errors.New(errors.InvalidExpression, f.File, t.Position)
} }
} }

View File

@ -19,6 +19,7 @@ var (
MissingOperand = &Base{"Missing operand"} MissingOperand = &Base{"Missing operand"}
MissingType = &Base{"Missing type"} MissingType = &Base{"Missing type"}
NotImplemented = &Base{"Not implemented"} NotImplemented = &Base{"Not implemented"}
UnknownType = &Base{"Unknown type"}
) )
// Base is the base class for errors that have no parameters. // Base is the base class for errors that have no parameters.

View File

@ -1,7 +1,6 @@
package scanner package scanner
import ( import (
"fmt"
"os" "os"
"path/filepath" "path/filepath"
@ -13,6 +12,7 @@ import (
"git.akyoto.dev/cli/q/src/build/fs" "git.akyoto.dev/cli/q/src/build/fs"
"git.akyoto.dev/cli/q/src/build/scope" "git.akyoto.dev/cli/q/src/build/scope"
"git.akyoto.dev/cli/q/src/build/token" "git.akyoto.dev/cli/q/src/build/token"
"git.akyoto.dev/cli/q/src/build/types"
) )
// scanFile scans a single file. // scanFile scans a single file.
@ -42,6 +42,8 @@ func (s *Scanner) scanFile(path string, pkg string) error {
paramsStart = -1 paramsStart = -1
paramsEnd = -1 paramsEnd = -1
bodyStart = -1 bodyStart = -1
typeStart = -1
typeEnd = -1
) )
for { for {
@ -156,9 +158,13 @@ func (s *Scanner) scanFile(path string, pkg string) error {
// Return type // Return type
if i < len(tokens) && tokens[i].Kind == token.ReturnType { if i < len(tokens) && tokens[i].Kind == token.ReturnType {
typeStart = i + 1
for i < len(tokens) && tokens[i].Kind != token.BlockStart { for i < len(tokens) && tokens[i].Kind != token.BlockStart {
i++ i++
} }
typeEnd = i
} }
// Function definition // Function definition
@ -221,6 +227,11 @@ func (s *Scanner) scanFile(path string, pkg string) error {
name := tokens[nameStart].Text(contents) name := tokens[nameStart].Text(contents)
body := tokens[bodyStart:i] body := tokens[bodyStart:i]
function := core.NewFunction(pkg, name, file, body) function := core.NewFunction(pkg, name, file, body)
if typeStart != -1 {
function.ReturnTypes = append(function.ReturnTypes, types.New(tokens[typeStart:typeEnd]))
}
parameters := tokens[paramsStart:paramsEnd] parameters := tokens[paramsStart:paramsEnd]
count := 0 count := 0
@ -230,8 +241,7 @@ func (s *Scanner) scanFile(path string, pkg string) error {
} }
name := tokens[0].Text(contents) name := tokens[0].Text(contents)
dataType := tokens[1].Text(contents) dataType := types.New(tokens[1:])
fmt.Println(dataType)
register := x64.CallRegisters[count] register := x64.CallRegisters[count]
uses := token.Count(function.Body, contents, token.Identifier, name) uses := token.Count(function.Body, contents, token.Identifier, name)
@ -241,10 +251,12 @@ func (s *Scanner) scanFile(path string, pkg string) error {
variable := &scope.Variable{ variable := &scope.Variable{
Name: name, Name: name,
Type: dataType,
Register: register, Register: register,
Alive: uses, Alive: uses,
} }
function.Parameters = append(function.Parameters, variable)
function.AddVariable(variable) function.AddVariable(variable)
count++ count++
return nil return nil
@ -258,6 +270,8 @@ func (s *Scanner) scanFile(path string, pkg string) error {
nameStart = -1 nameStart = -1
paramsStart = -1 paramsStart = -1
bodyStart = -1 bodyStart = -1
typeStart = -1
typeEnd = -1
i++ i++
} }
} }

View File

@ -2,11 +2,13 @@ package scope
import ( import (
"git.akyoto.dev/cli/q/src/build/cpu" "git.akyoto.dev/cli/q/src/build/cpu"
"git.akyoto.dev/cli/q/src/build/types"
) )
// Variable represents a named register. // Variable represents a named register.
type Variable struct { type Variable struct {
Name string Name string
Type types.Type
Alive uint8 Alive uint8
Register cpu.Register Register cpu.Register
} }

8
src/build/types/New.go Normal file
View File

@ -0,0 +1,8 @@
package types
import "git.akyoto.dev/cli/q/src/build/token"
// New creates a new type from a list of tokens.
func New(tokens token.List) Type {
return Int
}

8
src/build/types/Type.go Normal file
View File

@ -0,0 +1,8 @@
package types
type Type int
const (
Invalid Type = iota // Invalid is an invalid type.
Int
)