Array access (WIP)
This commit is contained in:
parent
0c23c326ac
commit
c119a077d6
@ -12,6 +12,7 @@ The Elysium programming language.
|
|||||||
- [ ] Modules
|
- [ ] Modules
|
||||||
- [ ] Arrays
|
- [ ] Arrays
|
||||||
- [ ] Structs
|
- [ ] Structs
|
||||||
|
- [ ] Strings
|
||||||
- [X] Raw memory access
|
- [X] Raw memory access
|
||||||
- [ ] Memory allocation, Heap allocator
|
- [ ] Memory allocation, Heap allocator
|
||||||
- [ ] Garbage collector
|
- [ ] Garbage collector
|
||||||
|
100
backend_wat.go
100
backend_wat.go
@ -112,6 +112,34 @@ func (c *Compiler) getWATType(t Type) string {
|
|||||||
panic(fmt.Sprintf("type not implemented in getWATType(): %s", t))
|
panic(fmt.Sprintf("type not implemented in getWATType(): %s", t))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) getPrimitiveTypeSizeBytes(primitive PrimitiveType) int {
|
||||||
|
switch primitive {
|
||||||
|
case Primitive_I8, Primitive_U8, Primitive_Bool:
|
||||||
|
return 1
|
||||||
|
case Primitive_I16, Primitive_U16:
|
||||||
|
return 2
|
||||||
|
case Primitive_I32, Primitive_U32, Primitive_F32:
|
||||||
|
return 4
|
||||||
|
case Primitive_I64, Primitive_U64, Primitive_F64:
|
||||||
|
return 8
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(fmt.Sprintf("unhandled type in getPrimitiveTypeSizeBytes(): %s", primitive))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) getTypeSizeBytes(t Type) int {
|
||||||
|
switch t.Type {
|
||||||
|
case Type_Primitive:
|
||||||
|
return c.getPrimitiveTypeSizeBytes(t.Value.(PrimitiveType))
|
||||||
|
case Type_Named, Type_Array:
|
||||||
|
return c.getPrimitiveTypeSizeBytes(Primitive_U64)
|
||||||
|
case Type_Tuple:
|
||||||
|
panic(fmt.Sprintf("tuple type passed to getTypeSizeBytes(): %s", t))
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(fmt.Sprintf("unhandled type in getTypeSizeBytes(): %s", t))
|
||||||
|
}
|
||||||
|
|
||||||
func castPrimitiveWAT(from PrimitiveType, to PrimitiveType) (string, error) {
|
func castPrimitiveWAT(from PrimitiveType, to PrimitiveType) (string, error) {
|
||||||
if from == to {
|
if from == to {
|
||||||
return "", nil
|
return "", nil
|
||||||
@ -492,6 +520,71 @@ func (c *Compiler) compileExpressionWAT(expr Expression) (string, error) {
|
|||||||
wat += c.getWATType(raw.Type) + ".load\n" // TODO: use load8/load16(_s/u) for smaller types
|
wat += c.getWATType(raw.Type) + ".load\n" // TODO: use load8/load16(_s/u) for smaller types
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return wat, nil
|
||||||
|
case Expression_ArrayAccess:
|
||||||
|
array := expr.Value.(ArrayAccessExpression)
|
||||||
|
|
||||||
|
localArray := Local{Name: "", Type: Type{Type: Type_Primitive, Value: c.getEffectiveAddressType(), Position: unknownPosition()}, IsParameter: false, Index: len(c.CurrentFunction.Locals)}
|
||||||
|
c.CurrentFunction.Locals = append(c.CurrentFunction.Locals, localArray)
|
||||||
|
|
||||||
|
localIndex := Local{Name: "", Type: Type{Type: Type_Primitive, Value: c.getEffectiveAddressType(), Position: unknownPosition()}, IsParameter: false, Index: len(c.CurrentFunction.Locals)}
|
||||||
|
c.CurrentFunction.Locals = append(c.CurrentFunction.Locals, localIndex)
|
||||||
|
|
||||||
|
arrayWAT, err := c.compileExpressionWAT(array.Array)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
arrayWAT += "local.set $" + strconv.Itoa(localArray.Index) + "\n"
|
||||||
|
|
||||||
|
indexWAT, err := c.compileExpressionWAT(array.Index)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.Wasm64 {
|
||||||
|
cast, err := castPrimitiveWAT(Primitive_I64, Primitive_I32)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
indexWAT += cast
|
||||||
|
}
|
||||||
|
|
||||||
|
indexWAT += "local.set $" + strconv.Itoa(localIndex.Index) + "\n"
|
||||||
|
|
||||||
|
wat := arrayWAT + indexWAT
|
||||||
|
|
||||||
|
// Error if index <= 0
|
||||||
|
wat += "block\n"
|
||||||
|
wat += "local.get $" + strconv.Itoa(localIndex.Index) + "\n"
|
||||||
|
wat += c.getAddressWATType() + ".const 0\n"
|
||||||
|
wat += c.getAddressWATType() + ".gt_s\n"
|
||||||
|
wat += "br_if 0\n"
|
||||||
|
wat += "call $__builtin_panic\n"
|
||||||
|
wat += "end\n"
|
||||||
|
|
||||||
|
// Error if index >= array length
|
||||||
|
wat += "block\n"
|
||||||
|
wat += "local.get $" + strconv.Itoa(localIndex.Index) + "\n"
|
||||||
|
wat += "local.get $" + strconv.Itoa(localArray.Index) + "\n"
|
||||||
|
wat += "i32.load\n" // Load array length
|
||||||
|
wat += c.getAddressWATType() + ".lt_s\n"
|
||||||
|
wat += "br_if 0\n"
|
||||||
|
wat += "call $__builtin_panic\n"
|
||||||
|
wat += "end\n"
|
||||||
|
|
||||||
|
elementType := array.Array.ValueType.Value.(ArrayType).ElementType
|
||||||
|
wat += "local.get $" + strconv.Itoa(localIndex.Index) + "\n"
|
||||||
|
wat += c.getAddressWATType() + ".const " + strconv.Itoa(c.getTypeSizeBytes(elementType)) + "\n"
|
||||||
|
wat += c.getAddressWATType() + ".mul\n"
|
||||||
|
wat += "local.get $" + strconv.Itoa(localArray.Index) + "\n"
|
||||||
|
wat += c.getAddressWATType() + ".add\n"
|
||||||
|
wat += c.getAddressWATType() + ".const 4\n" // first 4 bytes = length
|
||||||
|
wat += c.getAddressWATType() + ".add\n"
|
||||||
|
|
||||||
|
wat += c.getWATType(elementType) + ".load\n" // TODO: use load8/load16(_s/u) for smaller types
|
||||||
|
|
||||||
return wat, nil
|
return wat, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -727,6 +820,13 @@ func (c *Compiler) compileFunctionWAT(function *ParsedFunction) (string, error)
|
|||||||
func (c *Compiler) compile() (string, error) {
|
func (c *Compiler) compile() (string, error) {
|
||||||
module := "(module (memory 0)\n"
|
module := "(module (memory 0)\n"
|
||||||
|
|
||||||
|
module += "(func $__builtin_panic\n"
|
||||||
|
module += "i32.const 0\n"
|
||||||
|
module += "i32.const 0\n"
|
||||||
|
module += "i32.div_u\n"
|
||||||
|
module += "drop\n"
|
||||||
|
module += ")\n"
|
||||||
|
|
||||||
for _, file := range c.Files {
|
for _, file := range c.Files {
|
||||||
for i := range file.Functions {
|
for i := range file.Functions {
|
||||||
wat, err := c.compileFunctionWAT(&file.Functions[i])
|
wat, err := c.compileFunctionWAT(&file.Functions[i])
|
||||||
|
@ -38,3 +38,7 @@ u64 assign(u64 a) {
|
|||||||
a += 1u64;
|
a += 1u64;
|
||||||
return raw(u64, a) += 2u64;
|
return raw(u64, a) += 2u64;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u64 test() {
|
||||||
|
return raw(u64[], 0x0u8)[0];
|
||||||
|
}
|
||||||
|
48
validator.go
48
validator.go
@ -127,6 +127,10 @@ func isPrimitiveTypeExpandableTo(from PrimitiveType, to PrimitiveType) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isSignedInt(from) && !isSignedInt(to) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
switch from {
|
switch from {
|
||||||
case Primitive_I8, Primitive_U8:
|
case Primitive_I8, Primitive_U8:
|
||||||
if to == Primitive_I16 || to == Primitive_U16 {
|
if to == Primitive_I16 || to == Primitive_U16 {
|
||||||
@ -396,11 +400,14 @@ func (v *Validator) validatePotentiallyVoidExpression(expr *Expression) []error
|
|||||||
return errors
|
return errors
|
||||||
}
|
}
|
||||||
|
|
||||||
if raw.Address.ValueType.Type != Type_Primitive || raw.Address.ValueType.Value.(PrimitiveType) != Primitive_U64 {
|
typeU64 := Type{Type: Type_Primitive, Value: Primitive_U64, Position: unknownPosition()}
|
||||||
errors = append(errors, v.createError("address must evaluate to a u64 value", expr.Position))
|
if !isTypeExpandableTo(*raw.Address.ValueType, typeU64) {
|
||||||
|
errors = append(errors, v.createError("address must be expandable to a u64 value", expr.Position))
|
||||||
return errors
|
return errors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expandExpressionToType(&raw.Address, typeU64)
|
||||||
|
|
||||||
if !v.Wasm64 {
|
if !v.Wasm64 {
|
||||||
castTo := Type{Type: Type_Primitive, Value: Primitive_U32}
|
castTo := Type{Type: Type_Primitive, Value: Primitive_U32}
|
||||||
raw.Address = Expression{Type: Expression_Cast, Value: CastExpression{Type: castTo, Value: raw.Address}, ValueType: &castTo, Position: raw.Address.Position}
|
raw.Address = Expression{Type: Expression_Cast, Value: CastExpression{Type: castTo, Value: raw.Address}, ValueType: &castTo, Position: raw.Address.Position}
|
||||||
@ -408,6 +415,43 @@ func (v *Validator) validatePotentiallyVoidExpression(expr *Expression) []error
|
|||||||
|
|
||||||
expr.ValueType = &raw.Type
|
expr.ValueType = &raw.Type
|
||||||
expr.Value = raw
|
expr.Value = raw
|
||||||
|
case Expression_ArrayAccess:
|
||||||
|
array := expr.Value.(ArrayAccessExpression)
|
||||||
|
|
||||||
|
arrayErrors := v.validateExpression(&array.Array)
|
||||||
|
if len(arrayErrors) != 0 {
|
||||||
|
errors = append(errors, arrayErrors...)
|
||||||
|
}
|
||||||
|
|
||||||
|
indexErrors := v.validateExpression(&array.Index)
|
||||||
|
if len(indexErrors) != 0 {
|
||||||
|
errors = append(errors, indexErrors...)
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(errors) != 0 {
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
|
||||||
|
if array.Array.ValueType.Type != Type_Array {
|
||||||
|
errors = append(errors, v.createError("trying to access non-array type as an array", array.Array.Position))
|
||||||
|
}
|
||||||
|
|
||||||
|
typeI64 := Type{Type: Type_Primitive, Value: Primitive_I64, Position: unknownPosition()}
|
||||||
|
if !isTypeExpandableTo(*array.Index.ValueType, typeI64) {
|
||||||
|
errors = append(errors, v.createError("array index must be expandable to an i64 value", array.Index.Position))
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
|
||||||
|
expandExpressionToType(&array.Index, typeI64)
|
||||||
|
|
||||||
|
if len(errors) != 0 {
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
|
||||||
|
elementType := array.Array.ValueType.Value.(ArrayType).ElementType
|
||||||
|
expr.ValueType = &elementType
|
||||||
|
expr.Value = array
|
||||||
default:
|
default:
|
||||||
panic("expr not implemented")
|
panic("expr not implemented")
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user