mirror of
https://github.com/irmen/prog8.git
synced 2025-02-02 19:32:21 +00:00
fixed some type checks
This commit is contained in:
parent
30b58c8567
commit
34d26e42e1
@ -4,46 +4,13 @@
|
||||
|
||||
~ main {
|
||||
|
||||
byte mainb = 44
|
||||
byte[2] barray = 0
|
||||
byte[2] barray2 =0
|
||||
|
||||
sub start() {
|
||||
test()
|
||||
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
|
||||
}
|
||||
|
||||
X=barray[0]
|
||||
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) {
|
||||
// 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 =
|
||||
if(assignment.target.register!=null)
|
||||
RegisterExpr(assignment.target.register!!, assignment.target.position)
|
||||
@ -278,7 +288,13 @@ class AstChecker(private val namespace: INameScope,
|
||||
if(targetDatatype!=null) {
|
||||
val constVal = assignment.value.constValue(namespace, heap)
|
||||
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 {
|
||||
val sourceDatatype: DataType? = assignment.value.resultingDatatype(namespace, heap)
|
||||
if(sourceDatatype==null) {
|
||||
@ -312,6 +328,12 @@ class AstChecker(private val namespace: INameScope,
|
||||
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) {
|
||||
VarDeclType.VAR, VarDeclType.CONST -> {
|
||||
if (decl.value == null) {
|
||||
@ -328,7 +350,9 @@ class AstChecker(private val namespace: INameScope,
|
||||
}
|
||||
when {
|
||||
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 -> {
|
||||
err("var/const declaration needs a compile-time constant initializer value, or range, instead found: ${decl.value!!::class.simpleName}")
|
||||
return super.process(decl)
|
||||
@ -421,7 +445,9 @@ class AstChecker(private val namespace: INameScope,
|
||||
if(!compilerOptions.floats && literalValue.type==DataType.FLOAT) {
|
||||
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)
|
||||
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 {
|
||||
checkResult.add(ExpressionError(msg, value.position))
|
||||
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)
|
||||
if(value.type==DataType.ARRAY) {
|
||||
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)
|
||||
if(constX?.asIntegerValue==null)
|
||||
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
|
||||
if(value.type==DataType.ARRAY || value.type==DataType.ARRAY_W) {
|
||||
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
|
||||
val constX = arrayspec.x.constValue(namespace, heap)
|
||||
if(constX?.asIntegerValue==null)
|
||||
@ -717,8 +745,9 @@ class AstChecker(private val namespace: INameScope,
|
||||
DataType.ARRAY_F -> {
|
||||
// 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) {
|
||||
val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).doubleArray!!.size
|
||||
if(arrayspec!=null) {
|
||||
val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).arraysize
|
||||
val arraySpecSize = arrayspec.size()
|
||||
if(arraySpecSize!=null && arraySpecSize>0) {
|
||||
// arrayspec is not always known when checking
|
||||
val constX = arrayspec.x.constValue(namespace, heap)
|
||||
if(constX?.asIntegerValue==null)
|
||||
@ -728,16 +757,18 @@ class AstChecker(private val namespace: INameScope,
|
||||
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)")
|
||||
}
|
||||
} else {
|
||||
val number = value.asNumericValue!!.toDouble()
|
||||
if (number < FLOAT_MAX_NEGATIVE || number > FLOAT_MAX_POSITIVE)
|
||||
val number = value.asNumericValue?.toDouble()
|
||||
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")
|
||||
}
|
||||
}
|
||||
DataType.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(arrayspec!=null) {
|
||||
// arrayspec is not always known when checking
|
||||
val arraySpecSize = arrayspec.size()
|
||||
if(arraySpecSize!=null && arraySpecSize>0) {
|
||||
val constX = arrayspec.x.constValue(namespace, heap)
|
||||
val constY = arrayspec.y!!.constValue(namespace, heap)
|
||||
if (constX?.asIntegerValue == null || constY?.asIntegerValue == null)
|
||||
|
@ -66,6 +66,8 @@ class HeapValues {
|
||||
result = 31 * result + (doubleArray?.let { Arrays.hashCode(it) } ?: 0)
|
||||
return result
|
||||
}
|
||||
|
||||
val arraysize: Int = array?.size ?: doubleArray?.size ?: 0
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
// 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.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")
|
||||
// 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 {
|
||||
return try {
|
||||
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)
|
||||
copy.parent = identifier.parent
|
||||
return copy
|
||||
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
|
||||
} else
|
||||
identifier
|
||||
} catch (ax: AstException) {
|
||||
addError(ax)
|
||||
identifier
|
||||
|
@ -233,7 +233,7 @@ Special types: const and memory-mapped
|
||||
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
|
||||
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,
|
||||
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.
|
||||
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
|
||||
require it (floats, strings, arrays, matrices)::
|
||||
This is only valid for the simple numeric types (byte, word, float)::
|
||||
|
||||
const byte max_age = 99
|
||||
const str someName = "Peter"
|
||||
|
||||
|
||||
Reserved names
|
||||
|
Loading…
x
Reference in New Issue
Block a user