mirror of
https://github.com/irmen/prog8.git
synced 2025-08-11 17:26:01 +00:00
fixed some type checks
This commit is contained in:
@@ -4,46 +4,13 @@
|
|||||||
|
|
||||||
~ main {
|
~ main {
|
||||||
|
|
||||||
byte mainb = 44
|
byte[2] barray = 0
|
||||||
|
byte[2] barray2 =0
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
test()
|
X=barray[0]
|
||||||
test()
|
|
||||||
}
|
|
||||||
|
|
||||||
sub test() {
|
|
||||||
|
|
||||||
; @todo test assigning values to array vars
|
|
||||||
; @todo test creating const array vars
|
|
||||||
|
|
||||||
|
|
||||||
byte subb=42
|
|
||||||
|
|
||||||
for byte i in 0 to 2 {
|
|
||||||
float ff=3.3
|
|
||||||
byte bb=99
|
|
||||||
word ww=1999
|
|
||||||
|
|
||||||
_vm_write_num(ff)
|
|
||||||
_vm_write_char('\n')
|
|
||||||
_vm_write_num(bb)
|
|
||||||
_vm_write_char('\n')
|
|
||||||
_vm_write_num(ww)
|
|
||||||
_vm_write_char('\n')
|
|
||||||
_vm_write_num(subb)
|
|
||||||
_vm_write_char('\n')
|
|
||||||
_vm_write_num(mainb)
|
|
||||||
_vm_write_char('\n')
|
|
||||||
_vm_write_char('\n')
|
|
||||||
|
|
||||||
ff += 3
|
|
||||||
bb += 3
|
|
||||||
ww += 3
|
|
||||||
subb += 3
|
|
||||||
mainb += 3
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -254,9 +254,19 @@ class AstChecker(private val namespace: INameScope,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// it is not possible to assign a new array to something.
|
||||||
|
// currently, it's also not possible to assign a new string to a string variable.
|
||||||
|
when(assignment.value.resultingDatatype(namespace, heap)) {
|
||||||
|
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS ->
|
||||||
|
checkResult.add(SyntaxError("it's not possible to reassign a string", assignment.position)) // todo allow reassigning strings
|
||||||
|
DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F, DataType.MATRIX ->
|
||||||
|
checkResult.add(SyntaxError("it's not possible to reassign an array", assignment.position))
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
|
||||||
if(assignment.aug_op!=null) {
|
if(assignment.aug_op!=null) {
|
||||||
// check augmented assignment:
|
// check augmented assignment:
|
||||||
// A /= 3 -> check is if it was A = A / 3
|
// A /= 3 -> check as if it was A = A / 3
|
||||||
val target: IExpression =
|
val target: IExpression =
|
||||||
if(assignment.target.register!=null)
|
if(assignment.target.register!=null)
|
||||||
RegisterExpr(assignment.target.register!!, assignment.target.position)
|
RegisterExpr(assignment.target.register!!, assignment.target.position)
|
||||||
@@ -278,7 +288,13 @@ class AstChecker(private val namespace: INameScope,
|
|||||||
if(targetDatatype!=null) {
|
if(targetDatatype!=null) {
|
||||||
val constVal = assignment.value.constValue(namespace, heap)
|
val constVal = assignment.value.constValue(namespace, heap)
|
||||||
if(constVal!=null) {
|
if(constVal!=null) {
|
||||||
checkValueTypeAndRange(targetDatatype, null, constVal, heap)
|
val arrayspec = if(assignment.target.identifier!=null) {
|
||||||
|
val targetVar = namespace.lookup(assignment.target.identifier!!.nameInSource, assignment) as? VarDecl
|
||||||
|
targetVar?.arrayspec
|
||||||
|
} else null
|
||||||
|
checkValueTypeAndRange(targetDatatype,
|
||||||
|
arrayspec ?: ArraySpec(LiteralValue.optimalInteger(-1, assignment.position), null, assignment.position),
|
||||||
|
constVal, heap)
|
||||||
} else {
|
} else {
|
||||||
val sourceDatatype: DataType? = assignment.value.resultingDatatype(namespace, heap)
|
val sourceDatatype: DataType? = assignment.value.resultingDatatype(namespace, heap)
|
||||||
if(sourceDatatype==null) {
|
if(sourceDatatype==null) {
|
||||||
@@ -312,6 +328,12 @@ class AstChecker(private val namespace: INameScope,
|
|||||||
err("recursive var declaration")
|
err("recursive var declaration")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CONST can only occur on simple types (byte, word, float)
|
||||||
|
if(decl.type==VarDeclType.CONST) {
|
||||||
|
if (decl.datatype != DataType.BYTE && decl.datatype != DataType.WORD && decl.datatype != DataType.FLOAT)
|
||||||
|
err("const modifier can only be used on simple types (byte, word, float)")
|
||||||
|
}
|
||||||
|
|
||||||
when(decl.type) {
|
when(decl.type) {
|
||||||
VarDeclType.VAR, VarDeclType.CONST -> {
|
VarDeclType.VAR, VarDeclType.CONST -> {
|
||||||
if (decl.value == null) {
|
if (decl.value == null) {
|
||||||
@@ -328,7 +350,9 @@ class AstChecker(private val namespace: INameScope,
|
|||||||
}
|
}
|
||||||
when {
|
when {
|
||||||
decl.value is RangeExpr -> checkValueTypeAndRange(decl.datatype, decl.arrayspec, decl.value as RangeExpr)
|
decl.value is RangeExpr -> checkValueTypeAndRange(decl.datatype, decl.arrayspec, decl.value as RangeExpr)
|
||||||
decl.value is LiteralValue -> checkValueTypeAndRange(decl.datatype, decl.arrayspec, decl.value as LiteralValue, heap)
|
decl.value is LiteralValue -> checkValueTypeAndRange(decl.datatype,
|
||||||
|
decl.arrayspec ?: ArraySpec(LiteralValue.optimalInteger(-2, decl.position), null, decl.position),
|
||||||
|
decl.value as LiteralValue, heap)
|
||||||
else -> {
|
else -> {
|
||||||
err("var/const declaration needs a compile-time constant initializer value, or range, instead found: ${decl.value!!::class.simpleName}")
|
err("var/const declaration needs a compile-time constant initializer value, or range, instead found: ${decl.value!!::class.simpleName}")
|
||||||
return super.process(decl)
|
return super.process(decl)
|
||||||
@@ -421,7 +445,9 @@ class AstChecker(private val namespace: INameScope,
|
|||||||
if(!compilerOptions.floats && literalValue.type==DataType.FLOAT) {
|
if(!compilerOptions.floats && literalValue.type==DataType.FLOAT) {
|
||||||
checkResult.add(SyntaxError("floating point value used, but floating point is not enabled via options", literalValue.position))
|
checkResult.add(SyntaxError("floating point value used, but floating point is not enabled via options", literalValue.position))
|
||||||
}
|
}
|
||||||
checkValueTypeAndRange(literalValue.type, null, literalValue, heap)
|
checkValueTypeAndRange(literalValue.type,
|
||||||
|
ArraySpec(LiteralValue.optimalInteger(-3, literalValue.position), null, literalValue.position),
|
||||||
|
literalValue, heap)
|
||||||
|
|
||||||
val lv = super.process(literalValue)
|
val lv = super.process(literalValue)
|
||||||
when(lv.type) {
|
when(lv.type) {
|
||||||
@@ -632,7 +658,7 @@ class AstChecker(private val namespace: INameScope,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkValueTypeAndRange(targetDt: DataType, arrayspec: ArraySpec?, value: LiteralValue, heap: HeapValues) : Boolean {
|
private fun checkValueTypeAndRange(targetDt: DataType, arrayspec: ArraySpec, value: LiteralValue, heap: HeapValues) : Boolean {
|
||||||
fun err(msg: String) : Boolean {
|
fun err(msg: String) : Boolean {
|
||||||
checkResult.add(ExpressionError(msg, value.position))
|
checkResult.add(ExpressionError(msg, value.position))
|
||||||
return false
|
return false
|
||||||
@@ -673,7 +699,8 @@ class AstChecker(private val namespace: INameScope,
|
|||||||
// value may be either a single byte, or a byte array (of all constant values)
|
// value may be either a single byte, or a byte array (of all constant values)
|
||||||
if(value.type==DataType.ARRAY) {
|
if(value.type==DataType.ARRAY) {
|
||||||
val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).array!!.size
|
val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).array!!.size
|
||||||
if(arrayspec!=null) {
|
val arraySpecSize = arrayspec.size()
|
||||||
|
if(arraySpecSize!=null && arraySpecSize>0) {
|
||||||
val constX = arrayspec.x.constValue(namespace, heap)
|
val constX = arrayspec.x.constValue(namespace, heap)
|
||||||
if(constX?.asIntegerValue==null)
|
if(constX?.asIntegerValue==null)
|
||||||
return err("array size specifier must be constant integer value")
|
return err("array size specifier must be constant integer value")
|
||||||
@@ -696,7 +723,8 @@ class AstChecker(private val namespace: INameScope,
|
|||||||
// value may be either a single word, or a word array
|
// value may be either a single word, or a word array
|
||||||
if(value.type==DataType.ARRAY || value.type==DataType.ARRAY_W) {
|
if(value.type==DataType.ARRAY || value.type==DataType.ARRAY_W) {
|
||||||
val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).array!!.size
|
val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).array!!.size
|
||||||
if(arrayspec!=null) {
|
val arraySpecSize = arrayspec.size()
|
||||||
|
if(arraySpecSize!=null && arraySpecSize>0) {
|
||||||
// arrayspec is not always known when checking
|
// arrayspec is not always known when checking
|
||||||
val constX = arrayspec.x.constValue(namespace, heap)
|
val constX = arrayspec.x.constValue(namespace, heap)
|
||||||
if(constX?.asIntegerValue==null)
|
if(constX?.asIntegerValue==null)
|
||||||
@@ -717,8 +745,9 @@ class AstChecker(private val namespace: INameScope,
|
|||||||
DataType.ARRAY_F -> {
|
DataType.ARRAY_F -> {
|
||||||
// value may be either a single float, or a float array
|
// value may be either a single float, or a float array
|
||||||
if(value.type==DataType.ARRAY || value.type==DataType.ARRAY_W || value.type==DataType.ARRAY_F) {
|
if(value.type==DataType.ARRAY || value.type==DataType.ARRAY_W || value.type==DataType.ARRAY_F) {
|
||||||
val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).doubleArray!!.size
|
val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).arraysize
|
||||||
if(arrayspec!=null) {
|
val arraySpecSize = arrayspec.size()
|
||||||
|
if(arraySpecSize!=null && arraySpecSize>0) {
|
||||||
// arrayspec is not always known when checking
|
// arrayspec is not always known when checking
|
||||||
val constX = arrayspec.x.constValue(namespace, heap)
|
val constX = arrayspec.x.constValue(namespace, heap)
|
||||||
if(constX?.asIntegerValue==null)
|
if(constX?.asIntegerValue==null)
|
||||||
@@ -728,16 +757,18 @@ class AstChecker(private val namespace: INameScope,
|
|||||||
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)")
|
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val number = value.asNumericValue!!.toDouble()
|
val number = value.asNumericValue?.toDouble()
|
||||||
if (number < FLOAT_MAX_NEGATIVE || number > FLOAT_MAX_POSITIVE)
|
if(number==null)
|
||||||
|
return err("expected numerical value")
|
||||||
|
else if (number < FLOAT_MAX_NEGATIVE || number > FLOAT_MAX_POSITIVE)
|
||||||
return err("value '$number' out of range for mfplt5 floating point")
|
return err("value '$number' out of range for mfplt5 floating point")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.MATRIX -> {
|
DataType.MATRIX -> {
|
||||||
// value can only be a single byte, or a byte array (which represents the matrix)
|
// value can only be a single byte, or a byte array (which represents the matrix)
|
||||||
if(value.type==DataType.ARRAY || value.type==DataType.MATRIX) {
|
if(value.type==DataType.ARRAY || value.type==DataType.MATRIX) {
|
||||||
if(arrayspec!=null) {
|
val arraySpecSize = arrayspec.size()
|
||||||
// arrayspec is not always known when checking
|
if(arraySpecSize!=null && arraySpecSize>0) {
|
||||||
val constX = arrayspec.x.constValue(namespace, heap)
|
val constX = arrayspec.x.constValue(namespace, heap)
|
||||||
val constY = arrayspec.y!!.constValue(namespace, heap)
|
val constY = arrayspec.y!!.constValue(namespace, heap)
|
||||||
if (constX?.asIntegerValue == null || constY?.asIntegerValue == null)
|
if (constX?.asIntegerValue == null || constY?.asIntegerValue == null)
|
||||||
|
@@ -66,6 +66,8 @@ class HeapValues {
|
|||||||
result = 31 * result + (doubleArray?.let { Arrays.hashCode(it) } ?: 0)
|
result = 31 * result + (doubleArray?.let { Arrays.hashCode(it) } ?: 0)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val arraysize: Int = array?.size ?: doubleArray?.size ?: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
private val heap = mutableListOf<HeapValue>()
|
private val heap = mutableListOf<HeapValue>()
|
||||||
@@ -1204,10 +1206,10 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
|
|||||||
else -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt")
|
else -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// todo: maybe if you assign byte or word to array/matrix, clear it with that value?
|
||||||
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt")
|
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt")
|
||||||
DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F, DataType.MATRIX -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt")
|
DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F, DataType.MATRIX -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt")
|
||||||
null -> throw CompilerException("could not determine targetdt")
|
null -> throw CompilerException("could not determine targetdt")
|
||||||
// todo: maybe if you assign byte or word to array/matrix, clear it with that value?
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -105,14 +105,17 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* replace identifiers that refer to const value, with the value itself
|
* replace identifiers that refer to const value, with the value itself (if it's a simple type)
|
||||||
*/
|
*/
|
||||||
override fun process(identifier: IdentifierReference): IExpression {
|
override fun process(identifier: IdentifierReference): IExpression {
|
||||||
return try {
|
return try {
|
||||||
val cval = identifier.constValue(namespace, heap) ?: return identifier
|
val cval = identifier.constValue(namespace, heap) ?: return identifier
|
||||||
val copy = LiteralValue(cval.type, cval.bytevalue, cval.wordvalue, cval.floatvalue, cval.strvalue, cval.arrayvalue, position=identifier.position)
|
return if(cval.isNumeric) {
|
||||||
|
val copy = LiteralValue(cval.type, cval.bytevalue, cval.wordvalue, cval.floatvalue, cval.strvalue, cval.arrayvalue, position = identifier.position)
|
||||||
copy.parent = identifier.parent
|
copy.parent = identifier.parent
|
||||||
return copy
|
copy
|
||||||
|
} else
|
||||||
|
identifier
|
||||||
} catch (ax: AstException) {
|
} catch (ax: AstException) {
|
||||||
addError(ax)
|
addError(ax)
|
||||||
identifier
|
identifier
|
||||||
|
@@ -233,7 +233,7 @@ Special types: const and memory-mapped
|
|||||||
When using ``const``, the value of the 'variable' can no longer be changed.
|
When using ``const``, the value of the 'variable' can no longer be changed.
|
||||||
You'll have to specify the initial value expression. This value is then used
|
You'll have to specify the initial value expression. This value is then used
|
||||||
by the compiler everywhere you refer to the constant (and no storage is allocated
|
by the compiler everywhere you refer to the constant (and no storage is allocated
|
||||||
for the constant itself).
|
for the constant itself). This is only valid for the simple numeric types (byte, word, float).
|
||||||
|
|
||||||
When using ``memory``, the variable will point to specific location in memory,
|
When using ``memory``, the variable will point to specific location in memory,
|
||||||
rather than being newly allocated. The initial value (mandatory) must be a valid
|
rather than being newly allocated. The initial value (mandatory) must be a valid
|
||||||
|
@@ -288,11 +288,9 @@ Constants
|
|||||||
|
|
||||||
All variables can be assigned new values unless you use the ``const`` keyword.
|
All variables can be assigned new values unless you use the ``const`` keyword.
|
||||||
The initial value will now be evaluated at compile time (it must be a compile time constant expression).
|
The initial value will now be evaluated at compile time (it must be a compile time constant expression).
|
||||||
Storage is allocated only for the constant values that remain a the end of this process and that
|
This is only valid for the simple numeric types (byte, word, float)::
|
||||||
require it (floats, strings, arrays, matrices)::
|
|
||||||
|
|
||||||
const byte max_age = 99
|
const byte max_age = 99
|
||||||
const str someName = "Peter"
|
|
||||||
|
|
||||||
|
|
||||||
Reserved names
|
Reserved names
|
||||||
|
Reference in New Issue
Block a user