Implemented type checks
This commit is contained in:
parent
6e848774ed
commit
1b13539b22
@ -14,7 +14,7 @@ close(fd Int) -> Int {
|
|||||||
return syscall(3, fd)
|
return syscall(3, fd)
|
||||||
}
|
}
|
||||||
|
|
||||||
mmap(address Int, length Int, protection Int, flags Int) -> Int {
|
mmap(address Int, length Int, protection Int, flags Int) -> Pointer {
|
||||||
return syscall(9, address, length, protection, flags)
|
return syscall(9, address, length, protection, flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,9 +22,10 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
AllRegisters = []cpu.Register{RAX, RCX, RDX, RBX, RSP, RBP, RSI, RDI, R8, R9, R10, R11, R12, R13, R14, R15}
|
AllRegisters = []cpu.Register{RAX, RCX, RDX, RBX, RSP, RBP, RSI, RDI, R8, R9, R10, R11, R12, R13, R14, R15}
|
||||||
SyscallRegisters = []cpu.Register{RAX, RDI, RSI, RDX, R10, R8, R9}
|
SyscallInputRegisters = []cpu.Register{RAX, RDI, RSI, RDX, R10, R8, R9}
|
||||||
GeneralRegisters = []cpu.Register{RCX, RBX, RBP, R11, R12, R13, R14, R15}
|
SyscallOutputRegisters = []cpu.Register{RAX, RCX, R11}
|
||||||
CallRegisters = SyscallRegisters
|
GeneralRegisters = []cpu.Register{RCX, RBX, RBP, R11, R12, R13, R14, R15}
|
||||||
ReturnValueRegisters = SyscallRegisters
|
InputRegisters = SyscallInputRegisters
|
||||||
|
OutputRegisters = SyscallInputRegisters
|
||||||
)
|
)
|
||||||
|
@ -32,13 +32,13 @@ func (r *Result) finalize() ([]byte, []byte) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final.Call("main.main")
|
final.Call("main.main")
|
||||||
final.RegisterNumber(asm.MOVE, x64.SyscallRegisters[0], linux.Exit)
|
final.RegisterNumber(asm.MOVE, x64.SyscallInputRegisters[0], linux.Exit)
|
||||||
final.RegisterNumber(asm.MOVE, x64.SyscallRegisters[1], 0)
|
final.RegisterNumber(asm.MOVE, x64.SyscallInputRegisters[1], 0)
|
||||||
final.Syscall()
|
final.Syscall()
|
||||||
|
|
||||||
final.Label(asm.LABEL, "_crash")
|
final.Label(asm.LABEL, "_crash")
|
||||||
final.RegisterNumber(asm.MOVE, x64.SyscallRegisters[0], linux.Exit)
|
final.RegisterNumber(asm.MOVE, x64.SyscallInputRegisters[0], linux.Exit)
|
||||||
final.RegisterNumber(asm.MOVE, x64.SyscallRegisters[1], 1)
|
final.RegisterNumber(asm.MOVE, x64.SyscallInputRegisters[1], 1)
|
||||||
final.Syscall()
|
final.Syscall()
|
||||||
|
|
||||||
// This will place the main function immediately after the entry point
|
// This will place the main function immediately after the entry point
|
||||||
|
@ -15,80 +15,80 @@ import (
|
|||||||
func (f *Function) CompileCall(root *expression.Expression) (*Function, error) {
|
func (f *Function) CompileCall(root *expression.Expression) (*Function, error) {
|
||||||
var (
|
var (
|
||||||
pkg = f.Package
|
pkg = f.Package
|
||||||
nameRoot = root.Children[0]
|
nameNode = root.Children[0]
|
||||||
fn *Function
|
fn *Function
|
||||||
name string
|
name string
|
||||||
fullName string
|
fullName string
|
||||||
exists bool
|
exists bool
|
||||||
)
|
)
|
||||||
|
|
||||||
if nameRoot.IsLeaf() {
|
if nameNode.IsLeaf() {
|
||||||
name = nameRoot.Token.Text(f.File.Bytes)
|
name = nameNode.Token.Text(f.File.Bytes)
|
||||||
|
|
||||||
|
if name == "syscall" {
|
||||||
|
return nil, f.CompileSyscall(root)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
pkg = nameRoot.Children[0].Token.Text(f.File.Bytes)
|
pkg = nameNode.Children[0].Token.Text(f.File.Bytes)
|
||||||
name = nameRoot.Children[1].Token.Text(f.File.Bytes)
|
name = nameNode.Children[1].Token.Text(f.File.Bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
isSyscall := name == "syscall"
|
if pkg != f.File.Package {
|
||||||
|
if f.File.Imports == nil {
|
||||||
if !isSyscall {
|
return nil, errors.New(&errors.UnknownPackage{Name: pkg}, f.File, nameNode.Token.Position)
|
||||||
if pkg != f.File.Package {
|
|
||||||
if f.File.Imports == nil {
|
|
||||||
return nil, errors.New(&errors.UnknownPackage{Name: pkg}, f.File, nameRoot.Token.Position)
|
|
||||||
}
|
|
||||||
|
|
||||||
imp, exists := f.File.Imports[pkg]
|
|
||||||
|
|
||||||
if !exists {
|
|
||||||
return nil, errors.New(&errors.UnknownPackage{Name: pkg}, f.File, nameRoot.Token.Position)
|
|
||||||
}
|
|
||||||
|
|
||||||
imp.Used = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp := strings.Builder{}
|
imp, exists := f.File.Imports[pkg]
|
||||||
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, nameRoot.Token.Position)
|
return nil, errors.New(&errors.UnknownPackage{Name: pkg}, f.File, nameNode.Token.Position)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
imp.Used = true
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp := strings.Builder{}
|
||||||
|
tmp.WriteString(pkg)
|
||||||
|
tmp.WriteString(".")
|
||||||
|
tmp.WriteString(name)
|
||||||
|
fullName = tmp.String()
|
||||||
|
fn, exists = f.Functions[fullName]
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
return nil, errors.New(&errors.UnknownFunction{Name: name}, f.File, nameNode.Token.Position)
|
||||||
}
|
}
|
||||||
|
|
||||||
parameters := root.Children[1:]
|
parameters := root.Children[1:]
|
||||||
registers := f.CPU.Input[:len(parameters)]
|
registers := f.CPU.Input[:len(parameters)]
|
||||||
|
|
||||||
if isSyscall {
|
for i := len(parameters) - 1; i >= 0; i-- {
|
||||||
registers = f.CPU.Syscall[:len(parameters)]
|
typ, err := f.ExpressionToRegister(parameters[i], registers[i])
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if typ != fn.Parameters[i].Type {
|
||||||
|
return nil, errors.New(&errors.TypeMismatch{
|
||||||
|
Encountered: string(typ),
|
||||||
|
Expected: string(fn.Parameters[i].Type),
|
||||||
|
ParameterName: fn.Parameters[i].Name,
|
||||||
|
}, f.File, parameters[i].Token.Position)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err := f.ExpressionsToRegisters(parameters, registers)
|
for _, register := range f.CPU.Output[:len(fn.ReturnTypes)] {
|
||||||
|
f.SaveRegister(register)
|
||||||
if err != nil {
|
|
||||||
return fn, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Save all return value registers of the function
|
|
||||||
f.SaveRegister(f.CPU.Output[0])
|
|
||||||
|
|
||||||
// Push
|
|
||||||
for _, register := range f.CPU.General {
|
for _, register := range f.CPU.General {
|
||||||
if f.RegisterIsUsed(register) {
|
if f.RegisterIsUsed(register) {
|
||||||
f.Register(asm.PUSH, register)
|
f.Register(asm.PUSH, register)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call
|
f.Call(fullName)
|
||||||
if isSyscall {
|
|
||||||
f.Syscall()
|
|
||||||
} else {
|
|
||||||
f.Call(fullName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free parameter registers
|
|
||||||
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 {
|
||||||
continue
|
continue
|
||||||
@ -97,7 +97,6 @@ func (f *Function) CompileCall(root *expression.Expression) (*Function, error) {
|
|||||||
f.FreeRegister(register)
|
f.FreeRegister(register)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pop
|
|
||||||
for i := len(f.CPU.General) - 1; i >= 0; i-- {
|
for i := len(f.CPU.General) - 1; i >= 0; i-- {
|
||||||
register := f.CPU.General[i]
|
register := f.CPU.General[i]
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ func (f *Function) CompileDefinition(node *ast.Define) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
count := 0
|
count := 0
|
||||||
_, err := f.CompileCall(right)
|
called, err := f.CompileCall(right)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -54,6 +54,10 @@ func (f *Function) CompileDefinition(node *ast.Define) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if called != nil {
|
||||||
|
variable.Type = called.ReturnTypes[count]
|
||||||
|
}
|
||||||
|
|
||||||
f.RegisterRegister(asm.MOVE, variable.Register, f.CPU.Output[count])
|
f.RegisterRegister(asm.MOVE, variable.Register, f.CPU.Output[count])
|
||||||
f.AddVariable(variable)
|
f.AddVariable(variable)
|
||||||
count++
|
count++
|
||||||
|
@ -2,6 +2,8 @@ package core
|
|||||||
|
|
||||||
import (
|
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/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CompileReturn compiles a return instruction.
|
// CompileReturn compiles a return instruction.
|
||||||
@ -12,5 +14,22 @@ func (f *Function) CompileReturn(node *ast.Return) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return f.ExpressionsToRegisters(node.Values, f.CPU.Output)
|
for i := len(node.Values) - 1; i >= 0; i-- {
|
||||||
|
typ, err := f.ExpressionToRegister(node.Values[i], f.CPU.Output[i])
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if typ != types.Any && typ != f.ReturnTypes[i] {
|
||||||
|
return errors.New(&errors.TypeMismatch{
|
||||||
|
Encountered: string(typ),
|
||||||
|
Expected: string(f.ReturnTypes[i]),
|
||||||
|
ParameterName: "",
|
||||||
|
IsReturn: true,
|
||||||
|
}, f.File, node.Values[i].Token.Position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
32
src/build/core/CompileSyscall.go
Normal file
32
src/build/core/CompileSyscall.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.akyoto.dev/cli/q/src/build/expression"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CompileSyscall executes a kernel syscall.
|
||||||
|
func (f *Function) CompileSyscall(root *expression.Expression) error {
|
||||||
|
parameters := root.Children[1:]
|
||||||
|
registers := f.CPU.SyscallInput[:len(parameters)]
|
||||||
|
err := f.ExpressionsToRegisters(parameters, registers)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, register := range f.CPU.SyscallOutput {
|
||||||
|
f.SaveRegister(register)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Syscall()
|
||||||
|
|
||||||
|
for _, register := range registers {
|
||||||
|
if register == f.CPU.SyscallOutput[0] && root.Parent != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
f.FreeRegister(register)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -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.Invalid, err
|
return types.Any, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return fn.ReturnTypes[0], err
|
return fn.ReturnTypes[0], err
|
||||||
@ -72,6 +72,10 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp
|
|||||||
return types.Invalid, err
|
return types.Invalid, 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 {
|
||||||
|
typ = types.Int
|
||||||
|
}
|
||||||
|
|
||||||
err = f.Execute(node.Token, register, right)
|
err = f.Execute(node.Token, register, right)
|
||||||
|
|
||||||
if register != final {
|
if register != final {
|
||||||
|
@ -26,11 +26,12 @@ func NewFunction(pkg string, name string, file *fs.File, body []token.Token) *Fu
|
|||||||
Scopes: []*scope.Scope{{}},
|
Scopes: []*scope.Scope{{}},
|
||||||
},
|
},
|
||||||
CPU: cpu.CPU{
|
CPU: cpu.CPU{
|
||||||
All: x64.AllRegisters,
|
All: x64.AllRegisters,
|
||||||
Input: x64.CallRegisters,
|
General: x64.GeneralRegisters,
|
||||||
General: x64.GeneralRegisters,
|
Input: x64.InputRegisters,
|
||||||
Syscall: x64.SyscallRegisters,
|
Output: x64.OutputRegisters,
|
||||||
Output: x64.ReturnValueRegisters,
|
SyscallInput: x64.SyscallInputRegisters,
|
||||||
|
SyscallOutput: x64.SyscallOutputRegisters,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ func (f *Function) TokenToRegister(t token.Token, register cpu.Register) (types.
|
|||||||
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 types.Int, nil
|
return types.Pointer, nil
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return types.Invalid, errors.New(errors.InvalidExpression, f.File, t.Position)
|
return types.Invalid, errors.New(errors.InvalidExpression, f.File, t.Position)
|
||||||
|
@ -2,9 +2,10 @@ package cpu
|
|||||||
|
|
||||||
// CPU represents the processor.
|
// CPU represents the processor.
|
||||||
type CPU struct {
|
type CPU struct {
|
||||||
All []Register
|
All []Register
|
||||||
General []Register
|
General []Register
|
||||||
Syscall []Register
|
Input []Register
|
||||||
Input []Register
|
Output []Register
|
||||||
Output []Register
|
SyscallInput []Register
|
||||||
|
SyscallOutput []Register
|
||||||
}
|
}
|
||||||
|
26
src/build/errors/TypeMismatch.go
Normal file
26
src/build/errors/TypeMismatch.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package errors
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// TypeMismatch represents an error where a type requirement was not met.
|
||||||
|
type TypeMismatch struct {
|
||||||
|
Encountered string
|
||||||
|
Expected string
|
||||||
|
ParameterName string
|
||||||
|
IsReturn bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error generates the string representation.
|
||||||
|
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)
|
||||||
|
}
|
@ -229,7 +229,12 @@ func (s *Scanner) scanFile(path string, pkg string) error {
|
|||||||
function := core.NewFunction(pkg, name, file, body)
|
function := core.NewFunction(pkg, name, file, body)
|
||||||
|
|
||||||
if typeStart != -1 {
|
if typeStart != -1 {
|
||||||
function.ReturnTypes = append(function.ReturnTypes, types.New(tokens[typeStart:typeEnd]))
|
if tokens[typeStart].Kind == token.GroupStart && tokens[typeEnd-1].Kind == token.GroupEnd {
|
||||||
|
typeStart++
|
||||||
|
typeEnd--
|
||||||
|
}
|
||||||
|
|
||||||
|
function.ReturnTypes = types.NewList(tokens[typeStart:typeEnd], contents)
|
||||||
}
|
}
|
||||||
|
|
||||||
parameters := tokens[paramsStart:paramsEnd]
|
parameters := tokens[paramsStart:paramsEnd]
|
||||||
@ -241,8 +246,8 @@ func (s *Scanner) scanFile(path string, pkg string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
name := tokens[0].Text(contents)
|
name := tokens[0].Text(contents)
|
||||||
dataType := types.New(tokens[1:])
|
dataType := types.New(tokens[1:].Text(contents))
|
||||||
register := x64.CallRegisters[count]
|
register := x64.InputRegisters[count]
|
||||||
uses := token.Count(function.Body, contents, token.Identifier, name)
|
uses := token.Count(function.Body, contents, token.Identifier, name)
|
||||||
|
|
||||||
if uses == 0 {
|
if uses == 0 {
|
||||||
|
@ -47,6 +47,7 @@ func (stack *Stack) PushScope(body ast.AST, buffer []byte) *Scope {
|
|||||||
Name: v.Name,
|
Name: v.Name,
|
||||||
Register: v.Register,
|
Register: v.Register,
|
||||||
Alive: count,
|
Alive: count,
|
||||||
|
Type: v.Type,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
import "git.akyoto.dev/cli/q/src/build/token"
|
|
||||||
|
|
||||||
// New creates a new type from a list of tokens.
|
// New creates a new type from a list of tokens.
|
||||||
func New(tokens token.List) Type {
|
func New(name string) Type {
|
||||||
return Int
|
return Type(name)
|
||||||
}
|
}
|
||||||
|
19
src/build/types/NewList.go
Normal file
19
src/build/types/NewList.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.akyoto.dev/cli/q/src/build/expression"
|
||||||
|
"git.akyoto.dev/cli/q/src/build/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewList generates a list of types from comma separated tokens.
|
||||||
|
func NewList(tokens token.List, source []byte) []Type {
|
||||||
|
var list []Type
|
||||||
|
|
||||||
|
expression.EachParameter(tokens, func(parameter token.List) error {
|
||||||
|
typ := New(parameter.Text(source))
|
||||||
|
list = append(list, typ)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return list
|
||||||
|
}
|
@ -1,8 +1,10 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
type Type int
|
type Type string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Invalid Type = iota // Invalid is an invalid type.
|
Invalid = ""
|
||||||
Int
|
Any = "Any"
|
||||||
|
Int = "Int"
|
||||||
|
Pointer = "Pointer"
|
||||||
)
|
)
|
||||||
|
7
tests/errors/TypeMismatch.q
Normal file
7
tests/errors/TypeMismatch.q
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
main() {
|
||||||
|
writeToMemory(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
writeToMemory(p Pointer) {
|
||||||
|
p[0] = 'A'
|
||||||
|
}
|
@ -36,6 +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"}},
|
||||||
{"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