Implemented struct pointer types
This commit is contained in:
parent
85568949a2
commit
5d38a4980a
@ -7,10 +7,19 @@ struct Point {
|
||||
}
|
||||
|
||||
main() {
|
||||
p := construct()
|
||||
print(p)
|
||||
delete(p)
|
||||
}
|
||||
|
||||
construct() -> *Point {
|
||||
p := new(Point)
|
||||
p.x = 1
|
||||
p.y = 2
|
||||
return p
|
||||
}
|
||||
|
||||
print(p *Point) {
|
||||
out := mem.alloc(8)
|
||||
out[0] = 'x'
|
||||
out[1] = ' '
|
||||
@ -21,7 +30,5 @@ main() {
|
||||
out[6] = '0' + p.y
|
||||
out[7] = '\n'
|
||||
sys.write(1, out, 8)
|
||||
mem.free(out)
|
||||
|
||||
delete(p)
|
||||
mem.free(out, 8)
|
||||
}
|
@ -14,7 +14,18 @@ func Compile(files <-chan *fs.File, functions <-chan *core.Function, structs <-c
|
||||
result := Result{}
|
||||
allFiles := make([]*fs.File, 0, 8)
|
||||
allFunctions := map[string]*core.Function{}
|
||||
allTypes := map[string]types.Type{}
|
||||
|
||||
allTypes := map[string]types.Type{
|
||||
"Int": types.Int,
|
||||
"Int64": types.Int64,
|
||||
"Int32": types.Int32,
|
||||
"Int16": types.Int16,
|
||||
"Int8": types.Int8,
|
||||
"Float": types.Float,
|
||||
"Float64": types.Float64,
|
||||
"Float32": types.Float32,
|
||||
"Pointer": types.PointerAny,
|
||||
}
|
||||
|
||||
for functions != nil || files != nil || errs != nil {
|
||||
select {
|
||||
@ -54,6 +65,15 @@ func Compile(files <-chan *fs.File, functions <-chan *core.Function, structs <-c
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve the types
|
||||
for _, function := range allFunctions {
|
||||
err := function.ResolveTypes()
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
}
|
||||
|
||||
// Start parallel compilation
|
||||
CompileFunctions(allFunctions)
|
||||
|
||||
|
@ -31,9 +31,11 @@ func (f *Function) CompileCall(root *expression.Expression) (*Function, error) {
|
||||
return nil, f.CompileSyscall(root)
|
||||
case "new":
|
||||
return &Function{
|
||||
ReturnTypes: []types.Type{
|
||||
&types.Pointer{
|
||||
To: f.Types[root.Children[1].Token.Text(f.File.Bytes)],
|
||||
Output: []*Output{
|
||||
{
|
||||
Type: &types.Pointer{
|
||||
To: f.Types[root.Children[1].Token.Text(f.File.Bytes)],
|
||||
},
|
||||
},
|
||||
},
|
||||
}, f.CompileNew(root)
|
||||
@ -92,16 +94,16 @@ func (f *Function) CompileCall(root *expression.Expression) (*Function, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !types.Is(typ, fn.Parameters[i].Type) {
|
||||
if !types.Is(typ, fn.Input[i].Type) {
|
||||
return nil, errors.New(&errors.TypeMismatch{
|
||||
Encountered: typ.Name(),
|
||||
Expected: fn.Parameters[i].Type.Name(),
|
||||
ParameterName: fn.Parameters[i].Name,
|
||||
Expected: fn.Input[i].Type.Name(),
|
||||
ParameterName: fn.Input[i].Name,
|
||||
}, f.File, parameters[i].Token.Position)
|
||||
}
|
||||
}
|
||||
|
||||
for _, register := range f.CPU.Output[:len(fn.ReturnTypes)] {
|
||||
for _, register := range f.CPU.Output[:len(fn.Output)] {
|
||||
f.SaveRegister(register)
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ func (f *Function) CompileDefinition(node *ast.Define) error {
|
||||
variable.Type = typ
|
||||
|
||||
if variable.Type == nil {
|
||||
return errors.New(errors.UnknownType, f.File, node.Expression.Token.End())
|
||||
return errors.New(errors.CouldNotInferType, f.File, node.Expression.Token.End())
|
||||
}
|
||||
|
||||
f.AddVariable(variable)
|
||||
@ -54,7 +54,7 @@ func (f *Function) CompileDefinition(node *ast.Define) error {
|
||||
}
|
||||
|
||||
if called != nil {
|
||||
variable.Type = called.ReturnTypes[count]
|
||||
variable.Type = called.Output[count].Type
|
||||
}
|
||||
|
||||
f.RegisterRegister(asm.MOVE, variable.Register, f.CPU.Output[count])
|
||||
|
@ -14,8 +14,8 @@ func (f *Function) CompileReturn(node *ast.Return) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(node.Values) != len(f.ReturnTypes) {
|
||||
return errors.New(&errors.ReturnCountMismatch{Count: len(node.Values), ExpectedCount: len(f.ReturnTypes)}, f.File, node.Values[0].Token.Position)
|
||||
if len(node.Values) != len(f.Output) {
|
||||
return errors.New(&errors.ReturnCountMismatch{Count: len(node.Values), ExpectedCount: len(f.Output)}, f.File, node.Values[0].Token.Position)
|
||||
}
|
||||
|
||||
for i := len(node.Values) - 1; i >= 0; i-- {
|
||||
@ -25,10 +25,10 @@ func (f *Function) CompileReturn(node *ast.Return) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if !types.Is(typ, f.ReturnTypes[i]) {
|
||||
if !types.Is(typ, f.Output[i].Type) {
|
||||
return errors.New(&errors.TypeMismatch{
|
||||
Encountered: typ.Name(),
|
||||
Expected: f.ReturnTypes[i].Name(),
|
||||
Expected: f.Output[i].Type.Name(),
|
||||
ParameterName: "",
|
||||
IsReturn: true,
|
||||
}, f.File, node.Values[i].Token.Position)
|
||||
|
@ -30,11 +30,11 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp
|
||||
f.RegisterRegister(asm.MOVE, register, f.CPU.Output[0])
|
||||
}
|
||||
|
||||
if fn == nil || len(fn.ReturnTypes) == 0 {
|
||||
if fn == nil || len(fn.Output) == 0 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return fn.ReturnTypes[0], err
|
||||
return fn.Output[0].Type, err
|
||||
}
|
||||
|
||||
if node.Token.Kind == token.Array {
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"git.akyoto.dev/cli/q/src/dll"
|
||||
"git.akyoto.dev/cli/q/src/fs"
|
||||
"git.akyoto.dev/cli/q/src/register"
|
||||
"git.akyoto.dev/cli/q/src/scope"
|
||||
"git.akyoto.dev/cli/q/src/token"
|
||||
"git.akyoto.dev/cli/q/src/types"
|
||||
)
|
||||
@ -12,19 +11,19 @@ import (
|
||||
// Function represents the smallest unit of code.
|
||||
type Function struct {
|
||||
register.Machine
|
||||
Package string
|
||||
Name string
|
||||
UniqueName string
|
||||
File *fs.File
|
||||
Body token.List
|
||||
Parameters []*scope.Variable
|
||||
ReturnTypes []types.Type
|
||||
Functions map[string]*Function
|
||||
Types map[string]types.Type
|
||||
DLLs dll.List
|
||||
Err error
|
||||
deferred []func()
|
||||
count counter
|
||||
Package string
|
||||
Name string
|
||||
UniqueName string
|
||||
File *fs.File
|
||||
Body token.List
|
||||
Input []*Input
|
||||
Output []*Output
|
||||
Functions map[string]*Function
|
||||
Types map[string]types.Type
|
||||
DLLs dll.List
|
||||
Err error
|
||||
deferred []func()
|
||||
count counter
|
||||
}
|
||||
|
||||
// counter stores how often a certain statement appeared so we can generate a unique label from it.
|
||||
|
16
src/core/Input.go
Normal file
16
src/core/Input.go
Normal file
@ -0,0 +1,16 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"git.akyoto.dev/cli/q/src/token"
|
||||
"git.akyoto.dev/cli/q/src/types"
|
||||
)
|
||||
|
||||
type Input struct {
|
||||
Name string
|
||||
Type types.Type
|
||||
tokens token.List
|
||||
}
|
||||
|
||||
func NewInput(tokens token.List) *Input {
|
||||
return &Input{tokens: tokens}
|
||||
}
|
15
src/core/Output.go
Normal file
15
src/core/Output.go
Normal file
@ -0,0 +1,15 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"git.akyoto.dev/cli/q/src/token"
|
||||
"git.akyoto.dev/cli/q/src/types"
|
||||
)
|
||||
|
||||
type Output struct {
|
||||
Type types.Type
|
||||
tokens token.List
|
||||
}
|
||||
|
||||
func NewOutput(tokens token.List) *Output {
|
||||
return &Output{tokens: tokens}
|
||||
}
|
45
src/core/ResolveTypes.go
Normal file
45
src/core/ResolveTypes.go
Normal file
@ -0,0 +1,45 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"git.akyoto.dev/cli/q/src/errors"
|
||||
"git.akyoto.dev/cli/q/src/scope"
|
||||
"git.akyoto.dev/cli/q/src/token"
|
||||
"git.akyoto.dev/cli/q/src/x64"
|
||||
)
|
||||
|
||||
// ResolveTypes parses the input and output types.
|
||||
func (f *Function) ResolveTypes() error {
|
||||
for i, param := range f.Input {
|
||||
param.Name = param.tokens[0].Text(f.File.Bytes)
|
||||
typeName := param.tokens[1:].Text(f.File.Bytes)
|
||||
param.Type = f.TypeByName(typeName)
|
||||
|
||||
if param.Type == nil {
|
||||
return errors.New(&errors.UnknownType{Name: typeName}, f.File, param.tokens[1].Position)
|
||||
}
|
||||
|
||||
uses := token.Count(f.Body, f.File.Bytes, token.Identifier, param.Name)
|
||||
|
||||
if uses == 0 && param.Name != "_" {
|
||||
return errors.New(&errors.UnusedVariable{Name: param.Name}, f.File, param.tokens[0].Position)
|
||||
}
|
||||
|
||||
f.AddVariable(&scope.Variable{
|
||||
Name: param.Name,
|
||||
Type: param.Type,
|
||||
Register: x64.InputRegisters[i],
|
||||
Alive: uses,
|
||||
})
|
||||
}
|
||||
|
||||
for _, param := range f.Output {
|
||||
typeName := param.tokens.Text(f.File.Bytes)
|
||||
param.Type = f.TypeByName(typeName)
|
||||
|
||||
if param.Type == nil {
|
||||
return errors.New(&errors.UnknownType{Name: typeName}, f.File, param.tokens[1].Position)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
17
src/core/TypeByName.go
Normal file
17
src/core/TypeByName.go
Normal file
@ -0,0 +1,17 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"git.akyoto.dev/cli/q/src/types"
|
||||
)
|
||||
|
||||
// TypeByName returns the type with the given name or `nil` if it doesn't exist.
|
||||
func (f *Function) TypeByName(name string) types.Type {
|
||||
if strings.HasPrefix(name, "*") {
|
||||
to := strings.TrimPrefix(name, "*")
|
||||
return &types.Pointer{To: f.TypeByName(to)}
|
||||
}
|
||||
|
||||
return f.Types[name]
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package errors
|
||||
|
||||
var (
|
||||
CouldNotInferType = &Base{"Couldn't infer type"}
|
||||
EmptySwitch = &Base{"Empty switch"}
|
||||
ExpectedFunctionName = &Base{"Expected function name"}
|
||||
ExpectedFunctionParameters = &Base{"Expected function parameters"}
|
||||
@ -21,5 +22,4 @@ var (
|
||||
MissingParameter = &Base{"Missing parameter"}
|
||||
MissingType = &Base{"Missing type"}
|
||||
NotImplemented = &Base{"Not implemented"}
|
||||
UnknownType = &Base{"Unknown type"}
|
||||
)
|
||||
|
18
src/errors/UnknownType.go
Normal file
18
src/errors/UnknownType.go
Normal file
@ -0,0 +1,18 @@
|
||||
package errors
|
||||
|
||||
import "fmt"
|
||||
|
||||
// UnknownType represents unknown types.
|
||||
type UnknownType struct {
|
||||
Name string
|
||||
CorrectName string
|
||||
}
|
||||
|
||||
// Error generates the string representation.
|
||||
func (err *UnknownType) Error() string {
|
||||
if err.CorrectName != "" {
|
||||
return fmt.Sprintf("Unknown type '%s', did you mean '%s'?", err.Name, err.CorrectName)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Unknown type '%s'", err.Name)
|
||||
}
|
@ -4,10 +4,7 @@ import (
|
||||
"git.akyoto.dev/cli/q/src/core"
|
||||
"git.akyoto.dev/cli/q/src/errors"
|
||||
"git.akyoto.dev/cli/q/src/fs"
|
||||
"git.akyoto.dev/cli/q/src/scope"
|
||||
"git.akyoto.dev/cli/q/src/token"
|
||||
"git.akyoto.dev/cli/q/src/types"
|
||||
"git.akyoto.dev/cli/q/src/x64"
|
||||
)
|
||||
|
||||
// scanFunction scans a function.
|
||||
@ -157,11 +154,19 @@ func (s *Scanner) scanFunction(file *fs.File, tokens token.List, i int) (int, er
|
||||
typeEnd--
|
||||
}
|
||||
|
||||
function.ReturnTypes = types.ParseList(tokens[typeStart:typeEnd], file.Bytes)
|
||||
outputTokens := tokens[typeStart:typeEnd]
|
||||
|
||||
err := outputTokens.Split(func(tokens token.List) error {
|
||||
function.Output = append(function.Output, core.NewOutput(tokens))
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
}
|
||||
|
||||
parameters := tokens[paramsStart:paramsEnd]
|
||||
count := 0
|
||||
|
||||
err := parameters.Split(func(tokens token.List) error {
|
||||
if len(tokens) == 0 {
|
||||
@ -172,25 +177,7 @@ func (s *Scanner) scanFunction(file *fs.File, tokens token.List, i int) (int, er
|
||||
return errors.New(errors.MissingType, file, tokens[0].End())
|
||||
}
|
||||
|
||||
name := tokens[0].Text(file.Bytes)
|
||||
dataType := types.Parse(tokens[1:].Text(file.Bytes))
|
||||
register := x64.InputRegisters[count]
|
||||
uses := token.Count(function.Body, file.Bytes, token.Identifier, name)
|
||||
|
||||
if uses == 0 && name != "_" {
|
||||
return errors.New(&errors.UnusedVariable{Name: name}, file, tokens[0].Position)
|
||||
}
|
||||
|
||||
variable := &scope.Variable{
|
||||
Name: name,
|
||||
Type: dataType,
|
||||
Register: register,
|
||||
Alive: uses,
|
||||
}
|
||||
|
||||
function.Parameters = append(function.Parameters, variable)
|
||||
function.AddVariable(variable)
|
||||
count++
|
||||
function.Input = append(function.Input, core.NewInput(tokens))
|
||||
return nil
|
||||
})
|
||||
|
||||
|
@ -33,7 +33,12 @@ func (s *Scanner) scanStruct(file *fs.File, tokens token.List, i int) (int, erro
|
||||
fieldName := tokens[i].Text(file.Bytes)
|
||||
i++
|
||||
fieldTypeName := tokens[i].Text(file.Bytes)
|
||||
fieldType := types.Parse(fieldTypeName)
|
||||
fieldType := types.Int
|
||||
|
||||
if fieldTypeName != "Int" {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
i++
|
||||
|
||||
structure.AddField(&types.Field{
|
||||
|
@ -1,27 +0,0 @@
|
||||
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 PointerAny
|
||||
default:
|
||||
panic("Unknown type " + name)
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
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
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user