Implemented structs
This commit is contained in:
parent
4609a814df
commit
03a3bd8f02
11
examples/point/point.q
Normal file
11
examples/point/point.q
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
struct Point {
|
||||||
|
x Int
|
||||||
|
y Int
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
p := new(Point)
|
||||||
|
p.x = 4
|
||||||
|
p.y = 2
|
||||||
|
delete(p)
|
||||||
|
}
|
@ -22,8 +22,8 @@ munmap(address Pointer, length Int) -> Int {
|
|||||||
return syscall(11, address, length)
|
return syscall(11, address, length)
|
||||||
}
|
}
|
||||||
|
|
||||||
nanosleep(timespec Pointer) -> Int {
|
nanosleep(duration Pointer) -> Int {
|
||||||
return syscall(35, timespec, 0)
|
return syscall(35, duration, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
clone(flags Int, stack Pointer) -> Int {
|
clone(flags Int, stack Pointer) -> Int {
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import mem
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
sleep(nanoseconds Int) {
|
sleep(nanoseconds Int) {
|
||||||
@ -8,10 +7,9 @@ sleep(nanoseconds Int) {
|
|||||||
seconds, nanoseconds = nanoseconds / 1000000000
|
seconds, nanoseconds = nanoseconds / 1000000000
|
||||||
}
|
}
|
||||||
|
|
||||||
timespec := mem.alloc(16)
|
duration := new(timespec)
|
||||||
store(timespec, 8, seconds)
|
duration.seconds = seconds
|
||||||
offset := timespec + 8
|
duration.nanoseconds = nanoseconds
|
||||||
store(offset, 8, nanoseconds)
|
sys.nanosleep(duration)
|
||||||
sys.nanosleep(timespec)
|
delete(duration)
|
||||||
mem.free(timespec, 16)
|
|
||||||
}
|
}
|
@ -2,6 +2,7 @@ package asm
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MemoryLabel operates with a memory address and a number.
|
// MemoryLabel operates with a memory address and a number.
|
||||||
@ -12,6 +13,10 @@ type MemoryLabel struct {
|
|||||||
|
|
||||||
// String returns a human readable version.
|
// String returns a human readable version.
|
||||||
func (data *MemoryLabel) String() string {
|
func (data *MemoryLabel) String() string {
|
||||||
|
if data.Address.OffsetRegister == math.MaxUint8 {
|
||||||
|
return fmt.Sprintf("%dB [%s+%d], %s", data.Address.Length, data.Address.Base, data.Address.Offset, data.Label)
|
||||||
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("%dB [%s+%s+%d], %s", data.Address.Length, data.Address.Base, data.Address.OffsetRegister, data.Address.Offset, data.Label)
|
return fmt.Sprintf("%dB [%s+%s+%d], %s", data.Address.Length, data.Address.Base, data.Address.OffsetRegister, data.Address.Offset, data.Label)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ package asm
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MemoryNumber operates with a memory address and a number.
|
// MemoryNumber operates with a memory address and a number.
|
||||||
@ -12,6 +13,10 @@ type MemoryNumber struct {
|
|||||||
|
|
||||||
// String returns a human readable version.
|
// String returns a human readable version.
|
||||||
func (data *MemoryNumber) String() string {
|
func (data *MemoryNumber) String() string {
|
||||||
|
if data.Address.OffsetRegister == math.MaxUint8 {
|
||||||
|
return fmt.Sprintf("%dB [%s+%d], %d", data.Address.Length, data.Address.Base, data.Address.Offset, data.Number)
|
||||||
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("%dB [%s+%s+%d], %d", data.Address.Length, data.Address.Base, data.Address.OffsetRegister, data.Address.Offset, data.Number)
|
return fmt.Sprintf("%dB [%s+%s+%d], %d", data.Address.Length, data.Address.Base, data.Address.OffsetRegister, data.Address.Offset, data.Number)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ package asm
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
|
|
||||||
"git.akyoto.dev/cli/q/src/cpu"
|
"git.akyoto.dev/cli/q/src/cpu"
|
||||||
)
|
)
|
||||||
@ -14,6 +15,10 @@ type MemoryRegister struct {
|
|||||||
|
|
||||||
// String returns a human readable version.
|
// String returns a human readable version.
|
||||||
func (data *MemoryRegister) String() string {
|
func (data *MemoryRegister) String() string {
|
||||||
|
if data.Address.OffsetRegister == math.MaxUint8 {
|
||||||
|
return fmt.Sprintf("%dB [%s+%d], %s", data.Address.Length, data.Address.Base, data.Address.Offset, data.Register)
|
||||||
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("%dB [%s+%s+%d], %s", data.Address.Length, data.Address.Base, data.Address.OffsetRegister, data.Address.Offset, data.Register)
|
return fmt.Sprintf("%dB [%s+%s+%d], %s", data.Address.Length, data.Address.Base, data.Address.OffsetRegister, data.Address.Offset, data.Register)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,11 +10,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Compile waits for the scan to finish and compiles all functions.
|
// Compile waits for the scan to finish and compiles all functions.
|
||||||
func Compile(files <-chan *fs.File, functions <-chan *core.Function, structs <-chan *types.Type, errs <-chan error) (Result, error) {
|
func Compile(files <-chan *fs.File, functions <-chan *core.Function, structs <-chan types.Type, errs <-chan error) (Result, error) {
|
||||||
result := Result{}
|
result := Result{}
|
||||||
allFiles := make([]*fs.File, 0, 8)
|
allFiles := make([]*fs.File, 0, 8)
|
||||||
allFunctions := map[string]*core.Function{}
|
allFunctions := map[string]*core.Function{}
|
||||||
allTypes := map[string]*types.Type{}
|
allTypes := map[string]types.Type{}
|
||||||
|
|
||||||
for functions != nil || files != nil || errs != nil {
|
for functions != nil || files != nil || errs != nil {
|
||||||
select {
|
select {
|
||||||
@ -25,6 +25,7 @@ func Compile(files <-chan *fs.File, functions <-chan *core.Function, structs <-c
|
|||||||
}
|
}
|
||||||
|
|
||||||
function.Functions = allFunctions
|
function.Functions = allFunctions
|
||||||
|
function.Types = allTypes
|
||||||
allFunctions[function.UniqueName] = function
|
allFunctions[function.UniqueName] = function
|
||||||
|
|
||||||
case typ, ok := <-structs:
|
case typ, ok := <-structs:
|
||||||
@ -33,7 +34,7 @@ func Compile(files <-chan *fs.File, functions <-chan *core.Function, structs <-c
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
allTypes[typ.Name] = typ
|
allTypes[typ.UniqueName()] = typ
|
||||||
|
|
||||||
case file, ok := <-files:
|
case file, ok := <-files:
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -26,6 +26,10 @@ func (f *Function) CompileAssign(node *ast.Assign) error {
|
|||||||
return f.Execute(operator, variable.Register, right)
|
return f.Execute(operator, variable.Register, right)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if left.Token.Kind == token.Period {
|
||||||
|
return f.CompileAssignField(node)
|
||||||
|
}
|
||||||
|
|
||||||
if left.Token.Kind == token.Array {
|
if left.Token.Kind == token.Array {
|
||||||
return f.CompileAssignArray(node)
|
return f.CompileAssignArray(node)
|
||||||
}
|
}
|
||||||
|
44
src/core/CompileAssignField.go
Normal file
44
src/core/CompileAssignField.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
|
||||||
|
"git.akyoto.dev/cli/q/src/asm"
|
||||||
|
"git.akyoto.dev/cli/q/src/ast"
|
||||||
|
"git.akyoto.dev/cli/q/src/errors"
|
||||||
|
"git.akyoto.dev/cli/q/src/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CompileAssignField compiles a memory write to a struct field.
|
||||||
|
func (f *Function) CompileAssignField(node *ast.Assign) error {
|
||||||
|
destination := node.Expression.Children[0]
|
||||||
|
value := node.Expression.Children[1]
|
||||||
|
name := destination.Children[0].Token.Text(f.File.Bytes)
|
||||||
|
fieldName := destination.Children[1].Token.Text(f.File.Bytes)
|
||||||
|
variable := f.VariableByName(name)
|
||||||
|
|
||||||
|
if variable == nil {
|
||||||
|
return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, destination.Children[0].Token.Position)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer f.UseVariable(variable)
|
||||||
|
pointer := variable.Type.(*types.Pointer)
|
||||||
|
structure := pointer.To.(*types.Struct)
|
||||||
|
|
||||||
|
for _, field := range structure.Fields {
|
||||||
|
if field.Name == fieldName {
|
||||||
|
memory := asm.Memory{
|
||||||
|
Base: variable.Register,
|
||||||
|
Offset: field.Offset,
|
||||||
|
OffsetRegister: math.MaxUint8,
|
||||||
|
Length: field.Type.TotalSize(),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := f.ExpressionToMemory(value, memory)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: unknown field error
|
||||||
|
return nil
|
||||||
|
}
|
@ -26,11 +26,20 @@ func (f *Function) CompileCall(root *expression.Expression) (*Function, error) {
|
|||||||
if nameNode.IsLeaf() {
|
if nameNode.IsLeaf() {
|
||||||
name = nameNode.Token.Text(f.File.Bytes)
|
name = nameNode.Token.Text(f.File.Bytes)
|
||||||
|
|
||||||
if name == "syscall" {
|
switch name {
|
||||||
|
case "syscall":
|
||||||
return nil, f.CompileSyscall(root)
|
return nil, f.CompileSyscall(root)
|
||||||
}
|
case "new":
|
||||||
|
return &Function{
|
||||||
if name == "store" {
|
ReturnTypes: []types.Type{
|
||||||
|
&types.Pointer{
|
||||||
|
To: f.Types[root.Children[1].Token.Text(f.File.Bytes)],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, f.CompileNew(root)
|
||||||
|
case "delete":
|
||||||
|
return nil, f.CompileDelete(root)
|
||||||
|
case "store":
|
||||||
return nil, f.CompileMemoryStore(root)
|
return nil, f.CompileMemoryStore(root)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -85,8 +94,8 @@ func (f *Function) CompileCall(root *expression.Expression) (*Function, error) {
|
|||||||
|
|
||||||
if !types.Check(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: typ.Name,
|
Encountered: typ.UniqueName(),
|
||||||
Expected: fn.Parameters[i].Type.Name,
|
Expected: fn.Parameters[i].Type.UniqueName(),
|
||||||
ParameterName: fn.Parameters[i].Name,
|
ParameterName: fn.Parameters[i].Name,
|
||||||
}, f.File, parameters[i].Token.Position)
|
}, f.File, parameters[i].Token.Position)
|
||||||
}
|
}
|
||||||
|
37
src/core/CompileDelete.go
Normal file
37
src/core/CompileDelete.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.akyoto.dev/cli/q/src/asm"
|
||||||
|
"git.akyoto.dev/cli/q/src/expression"
|
||||||
|
"git.akyoto.dev/cli/q/src/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CompileDelete compiles a `delete` function call which deallocates a struct.
|
||||||
|
func (f *Function) CompileDelete(root *expression.Expression) error {
|
||||||
|
parameters := root.Children[1:]
|
||||||
|
variableName := parameters[0].Token.Text(f.File.Bytes)
|
||||||
|
variable := f.VariableByName(variableName)
|
||||||
|
defer f.UseVariable(variable)
|
||||||
|
f.SaveRegister(f.CPU.Input[0])
|
||||||
|
f.SaveRegister(f.CPU.Input[1])
|
||||||
|
f.RegisterRegister(asm.MOVE, f.CPU.Input[0], variable.Register)
|
||||||
|
f.RegisterNumber(asm.MOVE, f.CPU.Input[1], int(variable.Type.(*types.Pointer).To.TotalSize()))
|
||||||
|
|
||||||
|
for _, register := range f.CPU.General {
|
||||||
|
if f.RegisterIsUsed(register) {
|
||||||
|
f.Register(asm.PUSH, register)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Call("mem.free")
|
||||||
|
|
||||||
|
for i := len(f.CPU.General) - 1; i >= 0; i-- {
|
||||||
|
register := f.CPU.General[i]
|
||||||
|
|
||||||
|
if f.RegisterIsUsed(register) {
|
||||||
|
f.Register(asm.POP, register)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -8,7 +8,7 @@ import (
|
|||||||
"git.akyoto.dev/cli/q/src/expression"
|
"git.akyoto.dev/cli/q/src/expression"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CompileMemoryStore ...
|
// CompileMemoryStore compiles a variable-width store to memory.
|
||||||
func (f *Function) CompileMemoryStore(root *expression.Expression) error {
|
func (f *Function) CompileMemoryStore(root *expression.Expression) error {
|
||||||
parameters := root.Children[1:]
|
parameters := root.Children[1:]
|
||||||
name := parameters[0].Token.Text(f.File.Bytes)
|
name := parameters[0].Token.Text(f.File.Bytes)
|
||||||
|
33
src/core/CompileNew.go
Normal file
33
src/core/CompileNew.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.akyoto.dev/cli/q/src/asm"
|
||||||
|
"git.akyoto.dev/cli/q/src/expression"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CompileNew compiles a `new` function call which allocates a struct.
|
||||||
|
func (f *Function) CompileNew(root *expression.Expression) error {
|
||||||
|
parameters := root.Children[1:]
|
||||||
|
structName := parameters[0].Token.Text(f.File.Bytes)
|
||||||
|
typ := f.Types[structName]
|
||||||
|
f.SaveRegister(f.CPU.Input[0])
|
||||||
|
f.RegisterNumber(asm.MOVE, f.CPU.Input[0], int(typ.TotalSize()))
|
||||||
|
|
||||||
|
for _, register := range f.CPU.General {
|
||||||
|
if f.RegisterIsUsed(register) {
|
||||||
|
f.Register(asm.PUSH, register)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Call("mem.alloc")
|
||||||
|
|
||||||
|
for i := len(f.CPU.General) - 1; i >= 0; i-- {
|
||||||
|
register := f.CPU.General[i]
|
||||||
|
|
||||||
|
if f.RegisterIsUsed(register) {
|
||||||
|
f.Register(asm.POP, register)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -27,8 +27,8 @@ func (f *Function) CompileReturn(node *ast.Return) error {
|
|||||||
|
|
||||||
if !types.Check(typ, f.ReturnTypes[i]) {
|
if !types.Check(typ, f.ReturnTypes[i]) {
|
||||||
return errors.New(&errors.TypeMismatch{
|
return errors.New(&errors.TypeMismatch{
|
||||||
Encountered: typ.Name,
|
Encountered: typ.UniqueName(),
|
||||||
Expected: f.ReturnTypes[i].Name,
|
Expected: f.ReturnTypes[i].UniqueName(),
|
||||||
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, bool, error) {
|
func (f *Function) Evaluate(expr *expression.Expression) (types.Type, cpu.Register, bool, 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)
|
||||||
|
@ -10,21 +10,21 @@ 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() {
|
if node.IsLeaf() {
|
||||||
if node.Token.Kind == token.Identifier {
|
if node.Token.Kind == token.Identifier {
|
||||||
name := node.Token.Text(f.File.Bytes)
|
name := node.Token.Text(f.File.Bytes)
|
||||||
variable, function := f.Identifier(name)
|
variable, function := f.Identifier(name)
|
||||||
|
|
||||||
if variable != nil {
|
if variable != nil {
|
||||||
f.UseVariable(variable)
|
|
||||||
f.MemoryRegister(asm.STORE, memory, variable.Register)
|
f.MemoryRegister(asm.STORE, memory, variable.Register)
|
||||||
return types.Pointer, nil
|
f.UseVariable(variable)
|
||||||
|
return types.PointerAny, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if function != nil {
|
if function != nil {
|
||||||
f.MemoryLabel(asm.STORE, memory, function.UniqueName)
|
f.MemoryLabel(asm.STORE, memory, function.UniqueName)
|
||||||
return types.Pointer, nil
|
return types.PointerAny, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, errors.New(&errors.UnknownIdentifier{Name: name}, f.File, node.Token.Position)
|
return nil, errors.New(&errors.UnknownIdentifier{Name: name}, f.File, node.Token.Position)
|
||||||
@ -39,7 +39,7 @@ func (f *Function) ExpressionToMemory(node *expression.Expression, memory asm.Me
|
|||||||
|
|
||||||
size := byte(sizeof.Signed(int64(number)))
|
size := byte(sizeof.Signed(int64(number)))
|
||||||
|
|
||||||
if size != memory.Length {
|
if size > memory.Length {
|
||||||
return nil, 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
@ -72,7 +72,7 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if typ == types.Pointer && right.Token.Kind == token.Identifier && f.VariableByName(right.Token.Text(f.File.Bytes)).Type == types.Pointer {
|
if typ == types.PointerAny && right.Token.Kind == token.Identifier && f.VariableByName(right.Token.Text(f.File.Bytes)).Type == types.PointerAny {
|
||||||
typ = types.Int
|
typ = types.Int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,8 +18,9 @@ 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
|
||||||
|
Types map[string]types.Type
|
||||||
DLLs dll.List
|
DLLs dll.List
|
||||||
Err error
|
Err error
|
||||||
deferred []func()
|
deferred []func()
|
||||||
|
@ -10,7 +10,7 @@ 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)
|
||||||
@ -26,7 +26,7 @@ func (f *Function) TokenToRegister(t token.Token, register cpu.Register) (*types
|
|||||||
if function != nil {
|
if function != nil {
|
||||||
f.SaveRegister(register)
|
f.SaveRegister(register)
|
||||||
f.RegisterLabel(asm.MOVE, register, function.UniqueName)
|
f.RegisterLabel(asm.MOVE, register, function.UniqueName)
|
||||||
return types.Pointer, nil
|
return types.PointerAny, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, errors.New(&errors.UnknownIdentifier{Name: name}, f.File, t.Position)
|
return nil, errors.New(&errors.UnknownIdentifier{Name: name}, f.File, t.Position)
|
||||||
@ -48,7 +48,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.Pointer, nil
|
return types.PointerAny, nil
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, errors.New(errors.InvalidExpression, f.File, t.Position)
|
return nil, errors.New(errors.InvalidExpression, f.File, t.Position)
|
||||||
|
@ -1,20 +1,25 @@
|
|||||||
package scanner
|
package scanner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"git.akyoto.dev/cli/q/src/config"
|
||||||
"git.akyoto.dev/cli/q/src/core"
|
"git.akyoto.dev/cli/q/src/core"
|
||||||
"git.akyoto.dev/cli/q/src/fs"
|
"git.akyoto.dev/cli/q/src/fs"
|
||||||
"git.akyoto.dev/cli/q/src/types"
|
"git.akyoto.dev/cli/q/src/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Scan scans the list of files.
|
// Scan scans the list of files.
|
||||||
func Scan(files []string) (<-chan *fs.File, <-chan *core.Function, <-chan *types.Type, <-chan error) {
|
func Scan(files []string) (<-chan *fs.File, <-chan *core.Function, <-chan types.Type, <-chan error) {
|
||||||
scanner := Scanner{
|
scanner := Scanner{
|
||||||
files: make(chan *fs.File),
|
files: make(chan *fs.File),
|
||||||
functions: make(chan *core.Function),
|
functions: make(chan *core.Function),
|
||||||
types: make(chan *types.Type),
|
types: make(chan types.Type),
|
||||||
errors: make(chan error),
|
errors: make(chan error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scanner.queueDirectory(filepath.Join(config.Library, "mem"), "mem")
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
scanner.queue(files...)
|
scanner.queue(files...)
|
||||||
scanner.group.Wait()
|
scanner.group.Wait()
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
type Scanner struct {
|
type Scanner struct {
|
||||||
files chan *fs.File
|
files chan *fs.File
|
||||||
functions chan *core.Function
|
functions chan *core.Function
|
||||||
types chan *types.Type
|
types chan types.Type
|
||||||
errors chan error
|
errors chan error
|
||||||
queued sync.Map
|
queued sync.Map
|
||||||
group sync.WaitGroup
|
group sync.WaitGroup
|
||||||
|
@ -17,7 +17,7 @@ func (s *Scanner) scanStruct(file *fs.File, tokens token.List, i int) (int, erro
|
|||||||
|
|
||||||
structName := tokens[i].Text(file.Bytes)
|
structName := tokens[i].Text(file.Bytes)
|
||||||
|
|
||||||
typ := &types.Type{
|
structure := &types.Struct{
|
||||||
Name: structName,
|
Name: structName,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,14 +39,14 @@ func (s *Scanner) scanStruct(file *fs.File, tokens token.List, i int) (int, erro
|
|||||||
fieldType := types.Parse(fieldTypeName)
|
fieldType := types.Parse(fieldTypeName)
|
||||||
i++
|
i++
|
||||||
|
|
||||||
typ.Fields = append(typ.Fields, &types.Field{
|
structure.Fields = append(structure.Fields, &types.Field{
|
||||||
Type: fieldType,
|
Type: fieldType,
|
||||||
Name: fieldName,
|
Name: fieldName,
|
||||||
Position: token.Position(fieldPosition),
|
Position: token.Position(fieldPosition),
|
||||||
Offset: typ.Size,
|
Offset: structure.Size,
|
||||||
})
|
})
|
||||||
|
|
||||||
typ.Size += fieldType.Size
|
structure.Size += fieldType.TotalSize()
|
||||||
}
|
}
|
||||||
|
|
||||||
if tokens[i].Kind == token.BlockEnd {
|
if tokens[i].Kind == token.BlockEnd {
|
||||||
@ -61,6 +61,6 @@ func (s *Scanner) scanStruct(file *fs.File, tokens token.List, i int) (int, erro
|
|||||||
return i, errors.New(errors.MissingBlockEnd, file, tokens[i].Position)
|
return i, errors.New(errors.MissingBlockEnd, file, tokens[i].Position)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.types <- typ
|
s.types <- structure
|
||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
// Variable represents a named register.
|
// Variable represents a named register.
|
||||||
type Variable struct {
|
type Variable struct {
|
||||||
Type *types.Type
|
Type types.Type
|
||||||
Name string
|
Name string
|
||||||
Alive uint8
|
Alive uint8
|
||||||
Register cpu.Register
|
Register cpu.Register
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Float64 = &Type{Name: "Float64", Size: 8}
|
Float64 = &Struct{Name: "Float64", Size: 8}
|
||||||
Float32 = &Type{Name: "Float32", Size: 4}
|
Float32 = &Struct{Name: "Float32", Size: 4}
|
||||||
Int64 = &Type{Name: "Int64", Size: 8}
|
Int64 = &Struct{Name: "Int64", Size: 8}
|
||||||
Int32 = &Type{Name: "Int32", Size: 4}
|
Int32 = &Struct{Name: "Int32", Size: 4}
|
||||||
Int16 = &Type{Name: "Int16", Size: 2}
|
Int16 = &Struct{Name: "Int16", Size: 2}
|
||||||
Int8 = &Type{Name: "Int8", Size: 1}
|
Int8 = &Struct{Name: "Int8", Size: 1}
|
||||||
Pointer = &Type{Name: "Pointer", Size: 8}
|
PointerAny = &Pointer{To: nil}
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -1,6 +1,21 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
// Check returns true if the first type can be converted into the second type.
|
// Check returns true if the encountered type `a` can be converted into the expected type `b`.
|
||||||
func Check(a *Type, b *Type) bool {
|
func Check(a Type, b Type) bool {
|
||||||
return a == nil || a == b
|
if a == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if a == b {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
aPointer, aIsPointer := a.(*Pointer)
|
||||||
|
bPointer, bIsPointer := b.(*Pointer)
|
||||||
|
|
||||||
|
if aIsPointer && bIsPointer && (bPointer.To == nil || aPointer.To == bPointer.To) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import "git.akyoto.dev/cli/q/src/token"
|
|||||||
|
|
||||||
// Field is a field in a data structure.
|
// Field is a field in a data structure.
|
||||||
type Field struct {
|
type Field struct {
|
||||||
Type *Type
|
Type Type
|
||||||
Name string
|
Name string
|
||||||
Position token.Position
|
Position token.Position
|
||||||
Offset uint8
|
Offset uint8
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
// Parse creates a new type from a list of tokens.
|
// Parse creates a new type from a list of tokens.
|
||||||
func Parse(name string) *Type {
|
func Parse(name string) Type {
|
||||||
switch name {
|
switch name {
|
||||||
case "Int":
|
case "Int":
|
||||||
return Int
|
return Int
|
||||||
@ -20,7 +20,7 @@ func Parse(name string) *Type {
|
|||||||
case "Float32":
|
case "Float32":
|
||||||
return Float32
|
return Float32
|
||||||
case "Pointer":
|
case "Pointer":
|
||||||
return Pointer
|
return PointerAny
|
||||||
default:
|
default:
|
||||||
panic("Unknown type " + name)
|
panic("Unknown type " + name)
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,8 @@ package types
|
|||||||
import "git.akyoto.dev/cli/q/src/token"
|
import "git.akyoto.dev/cli/q/src/token"
|
||||||
|
|
||||||
// ParseList generates a list of types from comma separated tokens.
|
// ParseList generates a list of types from comma separated tokens.
|
||||||
func ParseList(tokens token.List, source []byte) []*Type {
|
func ParseList(tokens token.List, source []byte) []Type {
|
||||||
var list []*Type
|
var list []Type
|
||||||
|
|
||||||
tokens.Split(func(parameter token.List) error {
|
tokens.Split(func(parameter token.List) error {
|
||||||
typ := Parse(parameter.Text(source))
|
typ := Parse(parameter.Text(source))
|
||||||
|
17
src/types/Pointer.go
Normal file
17
src/types/Pointer.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
type Pointer struct {
|
||||||
|
To Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pointer) UniqueName() string {
|
||||||
|
if p.To == nil {
|
||||||
|
return "Pointer"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Pointer:" + p.To.UniqueName()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pointer) TotalSize() uint8 {
|
||||||
|
return 8
|
||||||
|
}
|
16
src/types/Struct.go
Normal file
16
src/types/Struct.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
// Struct is a structure in memory whose regions are addressable with fields.
|
||||||
|
type Struct struct {
|
||||||
|
Name string
|
||||||
|
Fields []*Field
|
||||||
|
Size uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Struct) UniqueName() string {
|
||||||
|
return s.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Struct) TotalSize() uint8 {
|
||||||
|
return s.Size
|
||||||
|
}
|
@ -1,8 +1,6 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
// Type represents a type in the type system.
|
type Type interface {
|
||||||
type Type struct {
|
UniqueName() string
|
||||||
Name string
|
TotalSize() uint8
|
||||||
Fields []*Field
|
|
||||||
Size uint8
|
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ 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}
|
||||||
SyscallInputRegisters = []cpu.Register{RAX, RDI, RSI, RDX, R10, R8, R9}
|
SyscallInputRegisters = []cpu.Register{RAX, RDI, RSI, RDX, R10, R8, R9}
|
||||||
SyscallOutputRegisters = []cpu.Register{RAX, RCX, R11}
|
SyscallOutputRegisters = []cpu.Register{RAX, RCX, R11}
|
||||||
GeneralRegisters = []cpu.Register{RCX, RBX, RBP, R11, R12, R13, R14, R15}
|
GeneralRegisters = []cpu.Register{RCX, RBX, R11, R12, R13, R14, R15, RBP}
|
||||||
InputRegisters = SyscallInputRegisters
|
InputRegisters = SyscallInputRegisters
|
||||||
OutputRegisters = SyscallInputRegisters
|
OutputRegisters = SyscallInputRegisters
|
||||||
WindowsInputRegisters = []cpu.Register{RCX, RDX, R8, R9}
|
WindowsInputRegisters = []cpu.Register{RCX, RDX, R8, R9}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user