Array access (WIP)
This commit is contained in:
parent
0c23c326ac
commit
c119a077d6
@ -12,6 +12,7 @@ The Elysium programming language.
|
||||
- [ ] Modules
|
||||
- [ ] Arrays
|
||||
- [ ] Structs
|
||||
- [ ] Strings
|
||||
- [X] Raw memory access
|
||||
- [ ] Memory allocation, Heap allocator
|
||||
- [ ] 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))
|
||||
}
|
||||
|
||||
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) {
|
||||
if from == to {
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@ -727,6 +820,13 @@ func (c *Compiler) compileFunctionWAT(function *ParsedFunction) (string, error)
|
||||
func (c *Compiler) compile() (string, error) {
|
||||
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 i := range file.Functions {
|
||||
wat, err := c.compileFunctionWAT(&file.Functions[i])
|
||||
|
@ -38,3 +38,7 @@ u64 assign(u64 a) {
|
||||
a += 1u64;
|
||||
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
|
||||
}
|
||||
|
||||
if isSignedInt(from) && !isSignedInt(to) {
|
||||
return false
|
||||
}
|
||||
|
||||
switch from {
|
||||
case Primitive_I8, Primitive_U8:
|
||||
if to == Primitive_I16 || to == Primitive_U16 {
|
||||
@ -396,11 +400,14 @@ func (v *Validator) validatePotentiallyVoidExpression(expr *Expression) []error
|
||||
return errors
|
||||
}
|
||||
|
||||
if raw.Address.ValueType.Type != Type_Primitive || raw.Address.ValueType.Value.(PrimitiveType) != Primitive_U64 {
|
||||
errors = append(errors, v.createError("address must evaluate to a u64 value", expr.Position))
|
||||
typeU64 := Type{Type: Type_Primitive, Value: Primitive_U64, Position: unknownPosition()}
|
||||
if !isTypeExpandableTo(*raw.Address.ValueType, typeU64) {
|
||||
errors = append(errors, v.createError("address must be expandable to a u64 value", expr.Position))
|
||||
return errors
|
||||
}
|
||||
|
||||
expandExpressionToType(&raw.Address, typeU64)
|
||||
|
||||
if !v.Wasm64 {
|
||||
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}
|
||||
@ -408,6 +415,43 @@ func (v *Validator) validatePotentiallyVoidExpression(expr *Expression) []error
|
||||
|
||||
expr.ValueType = &raw.Type
|
||||
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:
|
||||
panic("expr not implemented")
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user