Improved type system
This commit is contained in:
parent
d624a5f895
commit
5d9be01a85
@ -22,7 +22,7 @@ munmap(address Pointer, length Int) -> Int {
|
|||||||
return syscall(11, address, length)
|
return syscall(11, address, length)
|
||||||
}
|
}
|
||||||
|
|
||||||
clone(flags Int, stack Pointer) -> ThreadID {
|
clone(flags Int, stack Pointer) -> Int {
|
||||||
return syscall(56, flags, stack)
|
return syscall(56, flags, stack)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.akyoto.dev/cli/q/src/asm"
|
"git.akyoto.dev/cli/q/src/asm"
|
||||||
"git.akyoto.dev/cli/q/src/errors"
|
"git.akyoto.dev/cli/q/src/errors"
|
||||||
"git.akyoto.dev/cli/q/src/expression"
|
"git.akyoto.dev/cli/q/src/expression"
|
||||||
|
"git.akyoto.dev/cli/q/src/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CompileCall executes a function call.
|
// CompileCall executes a function call.
|
||||||
@ -18,7 +17,6 @@ func (f *Function) CompileCall(root *expression.Expression) (*Function, error) {
|
|||||||
nameNode = root.Children[0]
|
nameNode = root.Children[0]
|
||||||
fn *Function
|
fn *Function
|
||||||
name string
|
name string
|
||||||
fullName string
|
|
||||||
exists bool
|
exists bool
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -47,12 +45,7 @@ func (f *Function) CompileCall(root *expression.Expression) (*Function, error) {
|
|||||||
imp.Used = true
|
imp.Used = true
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp := strings.Builder{}
|
fn, exists = f.Functions[pkg+"."+name]
|
||||||
tmp.WriteString(pkg)
|
|
||||||
tmp.WriteString(".")
|
|
||||||
tmp.WriteString(name)
|
|
||||||
fullName = tmp.String()
|
|
||||||
fn, exists = f.Functions[fullName]
|
|
||||||
|
|
||||||
if !exists {
|
if !exists {
|
||||||
return nil, errors.New(&errors.UnknownFunction{Name: name}, f.File, nameNode.Token.Position)
|
return nil, errors.New(&errors.UnknownFunction{Name: name}, f.File, nameNode.Token.Position)
|
||||||
@ -68,10 +61,10 @@ func (f *Function) CompileCall(root *expression.Expression) (*Function, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if typ != fn.Parameters[i].Type {
|
if !types.Check(typ, fn.Parameters[i].Type) {
|
||||||
return nil, errors.New(&errors.TypeMismatch{
|
return nil, errors.New(&errors.TypeMismatch{
|
||||||
Encountered: string(typ),
|
Encountered: typ.Name,
|
||||||
Expected: string(fn.Parameters[i].Type),
|
Expected: fn.Parameters[i].Type.Name,
|
||||||
ParameterName: fn.Parameters[i].Name,
|
ParameterName: fn.Parameters[i].Name,
|
||||||
}, f.File, parameters[i].Token.Position)
|
}, f.File, parameters[i].Token.Position)
|
||||||
}
|
}
|
||||||
@ -87,7 +80,7 @@ func (f *Function) CompileCall(root *expression.Expression) (*Function, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
f.Call(fullName)
|
f.Call(fn.UniqueName)
|
||||||
|
|
||||||
for _, register := range registers {
|
for _, register := range registers {
|
||||||
if register == f.CPU.Output[0] && root.Parent != nil {
|
if register == f.CPU.Output[0] && root.Parent != nil {
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"git.akyoto.dev/cli/q/src/ast"
|
"git.akyoto.dev/cli/q/src/ast"
|
||||||
"git.akyoto.dev/cli/q/src/errors"
|
"git.akyoto.dev/cli/q/src/errors"
|
||||||
"git.akyoto.dev/cli/q/src/expression"
|
"git.akyoto.dev/cli/q/src/expression"
|
||||||
"git.akyoto.dev/cli/q/src/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CompileDefinition compiles a variable definition.
|
// CompileDefinition compiles a variable definition.
|
||||||
@ -28,7 +27,7 @@ func (f *Function) CompileDefinition(node *ast.Define) error {
|
|||||||
|
|
||||||
variable.Type = typ
|
variable.Type = typ
|
||||||
|
|
||||||
if variable.Type == types.Invalid {
|
if variable.Type == nil {
|
||||||
return errors.New(errors.UnknownType, f.File, node.Expression.Token.End())
|
return errors.New(errors.UnknownType, f.File, node.Expression.Token.End())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,10 +21,10 @@ func (f *Function) CompileReturn(node *ast.Return) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if typ != types.Any && typ != f.ReturnTypes[i] {
|
if !types.Check(typ, f.ReturnTypes[i]) {
|
||||||
return errors.New(&errors.TypeMismatch{
|
return errors.New(&errors.TypeMismatch{
|
||||||
Encountered: string(typ),
|
Encountered: typ.Name,
|
||||||
Expected: string(f.ReturnTypes[i]),
|
Expected: f.ReturnTypes[i].Name,
|
||||||
ParameterName: "",
|
ParameterName: "",
|
||||||
IsReturn: true,
|
IsReturn: true,
|
||||||
}, f.File, node.Values[i].Token.Position)
|
}, f.File, node.Values[i].Token.Position)
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// 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) (types.Type, 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)
|
||||||
|
@ -9,18 +9,18 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// 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) (types.Type, 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 types.Invalid, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
size := byte(sizeof.Signed(int64(number)))
|
size := byte(sizeof.Signed(int64(number)))
|
||||||
|
|
||||||
if size != memory.Length {
|
if size != memory.Length {
|
||||||
return types.Invalid, errors.New(&errors.NumberExceedsBounds{Number: number, ExpectedSize: memory.Length, Size: size}, f.File, node.Token.Position)
|
return nil, 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)
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// 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) (types.Type, 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 {
|
||||||
@ -31,7 +31,7 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp
|
|||||||
}
|
}
|
||||||
|
|
||||||
if fn == nil || len(fn.ReturnTypes) == 0 {
|
if fn == nil || len(fn.ReturnTypes) == 0 {
|
||||||
return types.Any, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return fn.ReturnTypes[0], err
|
return fn.ReturnTypes[0], err
|
||||||
@ -46,7 +46,7 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp
|
|||||||
|
|
||||||
if len(node.Children) == 1 {
|
if len(node.Children) == 1 {
|
||||||
if !node.Token.IsUnaryOperator() {
|
if !node.Token.IsUnaryOperator() {
|
||||||
return types.Invalid, errors.New(errors.MissingOperand, f.File, node.Token.End())
|
return nil, errors.New(errors.MissingOperand, f.File, node.Token.End())
|
||||||
}
|
}
|
||||||
|
|
||||||
typ, err := f.ExpressionToRegister(node.Children[0], register)
|
typ, err := f.ExpressionToRegister(node.Children[0], register)
|
||||||
@ -69,10 +69,10 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp
|
|||||||
typ, err := f.ExpressionToRegister(left, register)
|
typ, err := f.ExpressionToRegister(left, register)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return types.Invalid, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if typ == types.Pointer && (node.Token.Kind == token.Add || node.Token.Kind == token.Sub) && right.Token.Kind == token.Identifier && f.VariableByName(right.Token.Text(f.File.Bytes)).Type == types.Pointer {
|
if typ == types.Pointer && right.Token.Kind == token.Identifier && f.VariableByName(right.Token.Text(f.File.Bytes)).Type == types.Pointer {
|
||||||
typ = types.Int
|
typ = types.Int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ type Function struct {
|
|||||||
File *fs.File
|
File *fs.File
|
||||||
Body token.List
|
Body token.List
|
||||||
Parameters []*scope.Variable
|
Parameters []*scope.Variable
|
||||||
ReturnTypes []types.Type
|
ReturnTypes []*types.Type
|
||||||
Functions map[string]*Function
|
Functions map[string]*Function
|
||||||
Err error
|
Err error
|
||||||
deferred []func()
|
deferred []func()
|
||||||
|
@ -10,14 +10,14 @@ import (
|
|||||||
|
|
||||||
// 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) (types.Type, 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 types.Invalid, errors.New(&errors.UnknownIdentifier{Name: name}, f.File, t.Position)
|
return nil, errors.New(&errors.UnknownIdentifier{Name: name}, f.File, t.Position)
|
||||||
}
|
}
|
||||||
|
|
||||||
f.UseVariable(variable)
|
f.UseVariable(variable)
|
||||||
@ -29,7 +29,7 @@ func (f *Function) TokenToRegister(t token.Token, register cpu.Register) (types.
|
|||||||
number, err := f.Number(t)
|
number, err := f.Number(t)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return types.Invalid, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
f.SaveRegister(register)
|
f.SaveRegister(register)
|
||||||
@ -45,6 +45,6 @@ func (f *Function) TokenToRegister(t token.Token, register cpu.Register) (types.
|
|||||||
return types.Pointer, nil
|
return types.Pointer, nil
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return types.Invalid, errors.New(errors.InvalidExpression, f.File, t.Position)
|
return nil, errors.New(errors.InvalidExpression, f.File, t.Position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -233,7 +233,7 @@ func (s *Scanner) scanFile(path string, pkg string) error {
|
|||||||
typeEnd--
|
typeEnd--
|
||||||
}
|
}
|
||||||
|
|
||||||
function.ReturnTypes = types.NewList(tokens[typeStart:typeEnd], contents)
|
function.ReturnTypes = types.ParseList(tokens[typeStart:typeEnd], contents)
|
||||||
}
|
}
|
||||||
|
|
||||||
parameters := tokens[paramsStart:paramsEnd]
|
parameters := tokens[paramsStart:paramsEnd]
|
||||||
@ -245,7 +245,7 @@ func (s *Scanner) scanFile(path string, pkg string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
name := tokens[0].Text(contents)
|
name := tokens[0].Text(contents)
|
||||||
dataType := types.New(tokens[1:].Text(contents))
|
dataType := types.Parse(tokens[1:].Text(contents))
|
||||||
register := x64.InputRegisters[count]
|
register := x64.InputRegisters[count]
|
||||||
uses := token.Count(function.Body, contents, token.Identifier, name)
|
uses := token.Count(function.Body, contents, token.Identifier, name)
|
||||||
|
|
||||||
|
@ -7,8 +7,8 @@ import (
|
|||||||
|
|
||||||
// Variable represents a named register.
|
// Variable represents a named register.
|
||||||
type Variable struct {
|
type Variable struct {
|
||||||
|
Type *types.Type
|
||||||
Name string
|
Name string
|
||||||
Type types.Type
|
|
||||||
Alive uint8
|
Alive uint8
|
||||||
Register cpu.Register
|
Register cpu.Register
|
||||||
}
|
}
|
||||||
|
16
src/types/Base.go
Normal file
16
src/types/Base.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
var (
|
||||||
|
Float64 = &Type{Name: "Float64", Size: 8}
|
||||||
|
Float32 = &Type{Name: "Float32", Size: 4}
|
||||||
|
Int64 = &Type{Name: "Int64", Size: 8}
|
||||||
|
Int32 = &Type{Name: "Int32", Size: 4}
|
||||||
|
Int16 = &Type{Name: "Int16", Size: 2}
|
||||||
|
Int8 = &Type{Name: "Int8", Size: 1}
|
||||||
|
Pointer = &Type{Name: "Pointer", Size: 8}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Float = Float64
|
||||||
|
Int = Int64
|
||||||
|
)
|
6
src/types/Check.go
Normal file
6
src/types/Check.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
// Check returns true if the first type can be converted into the second type.
|
||||||
|
func Check(a *Type, b *Type) bool {
|
||||||
|
return a == nil || a == b
|
||||||
|
}
|
11
src/types/Field.go
Normal file
11
src/types/Field.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
import "git.akyoto.dev/cli/q/src/token"
|
||||||
|
|
||||||
|
// Field is a field in a data structure.
|
||||||
|
type Field struct {
|
||||||
|
Type *Type
|
||||||
|
Name string
|
||||||
|
Position token.Position
|
||||||
|
Offset uint8
|
||||||
|
}
|
@ -1,6 +0,0 @@
|
|||||||
package types
|
|
||||||
|
|
||||||
// New creates a new type from a list of tokens.
|
|
||||||
func New(name string) Type {
|
|
||||||
return Type(name)
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
package types
|
|
||||||
|
|
||||||
import "git.akyoto.dev/cli/q/src/token"
|
|
||||||
|
|
||||||
// NewList generates a list of types from comma separated tokens.
|
|
||||||
func NewList(tokens token.List, source []byte) []Type {
|
|
||||||
var list []Type
|
|
||||||
|
|
||||||
tokens.Split(func(parameter token.List) error {
|
|
||||||
typ := New(parameter.Text(source))
|
|
||||||
list = append(list, typ)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
return list
|
|
||||||
}
|
|
27
src/types/Parse.go
Normal file
27
src/types/Parse.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
// Parse creates a new type from a list of tokens.
|
||||||
|
func Parse(name string) *Type {
|
||||||
|
switch name {
|
||||||
|
case "Int":
|
||||||
|
return Int
|
||||||
|
case "Int64":
|
||||||
|
return Int64
|
||||||
|
case "Int32":
|
||||||
|
return Int32
|
||||||
|
case "Int16":
|
||||||
|
return Int16
|
||||||
|
case "Int8":
|
||||||
|
return Int8
|
||||||
|
case "Float":
|
||||||
|
return Float
|
||||||
|
case "Float64":
|
||||||
|
return Float64
|
||||||
|
case "Float32":
|
||||||
|
return Float32
|
||||||
|
case "Pointer":
|
||||||
|
return Pointer
|
||||||
|
default:
|
||||||
|
panic("Unknown type " + name)
|
||||||
|
}
|
||||||
|
}
|
16
src/types/ParseList.go
Normal file
16
src/types/ParseList.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
import "git.akyoto.dev/cli/q/src/token"
|
||||||
|
|
||||||
|
// ParseList generates a list of types from comma separated tokens.
|
||||||
|
func ParseList(tokens token.List, source []byte) []*Type {
|
||||||
|
var list []*Type
|
||||||
|
|
||||||
|
tokens.Split(func(parameter token.List) error {
|
||||||
|
typ := Parse(parameter.Text(source))
|
||||||
|
list = append(list, typ)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return list
|
||||||
|
}
|
@ -1,10 +1,8 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
type Type string
|
// Type represents a type in the type system.
|
||||||
|
type Type struct {
|
||||||
const (
|
Name string
|
||||||
Invalid = ""
|
Fields []*Field
|
||||||
Any = "Any"
|
Size uint8
|
||||||
Int = "Int"
|
}
|
||||||
Pointer = "Pointer"
|
|
||||||
)
|
|
||||||
|
@ -36,7 +36,7 @@ var errs = []struct {
|
|||||||
{"MissingOperand.q", errors.MissingOperand},
|
{"MissingOperand.q", errors.MissingOperand},
|
||||||
{"MissingOperand2.q", errors.MissingOperand},
|
{"MissingOperand2.q", errors.MissingOperand},
|
||||||
{"VariableAlreadyExists.q", &errors.VariableAlreadyExists{Name: "x"}},
|
{"VariableAlreadyExists.q", &errors.VariableAlreadyExists{Name: "x"}},
|
||||||
{"TypeMismatch.q", &errors.TypeMismatch{Expected: "Pointer", Encountered: "Int", ParameterName: "p"}},
|
{"TypeMismatch.q", &errors.TypeMismatch{Expected: "Pointer", Encountered: "Int64", ParameterName: "p"}},
|
||||||
{"UnknownFunction.q", &errors.UnknownFunction{Name: "unknown"}},
|
{"UnknownFunction.q", &errors.UnknownFunction{Name: "unknown"}},
|
||||||
{"UnknownFunction2.q", &errors.UnknownFunction{Name: "f"}},
|
{"UnknownFunction2.q", &errors.UnknownFunction{Name: "f"}},
|
||||||
{"UnknownIdentifier.q", &errors.UnknownIdentifier{Name: "x"}},
|
{"UnknownIdentifier.q", &errors.UnknownIdentifier{Name: "x"}},
|
||||||
|
Loading…
Reference in New Issue
Block a user