mirror of
https://github.com/irmen/prog8.git
synced 2024-11-25 19:31:36 +00:00
arrays are now a datatype as well
fixed array initializer checks
This commit is contained in:
parent
3933fdab13
commit
e928997193
@ -211,6 +211,7 @@ Various examples::
|
||||
str name = "my name is Irmen"
|
||||
word address = #counter
|
||||
byte[5] values = [11, 22, 33, 44, 55]
|
||||
byte[5] values = 255 ; initialize with five 255 bytes
|
||||
byte[5][6] empty_matrix
|
||||
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
memory word freadstr_arg = $22 ; argument for FREADSTR
|
||||
|
||||
c64.init_system()
|
||||
c64.VMCSB |= 2 ; lowercase charset
|
||||
c64.VMCSB |= 2 ; activate lowercase charset
|
||||
|
||||
; greeting
|
||||
c64scr.print_string("Enter your name: ")
|
||||
@ -30,7 +30,7 @@
|
||||
c64.FADDH() ; add 0.5..
|
||||
c64.FADDH() ; and again, so +1 total
|
||||
AY = c64flt.GETADRAY()
|
||||
secretnumber=A
|
||||
secretnumber = A
|
||||
;A=math.randbyte()
|
||||
;A+=c64.RASTER
|
||||
;A-=c64.TIME_LO
|
||||
@ -51,8 +51,8 @@ ask_guess:
|
||||
c64.FREADSTR(A)
|
||||
AY = c64flt.GETADRAY()
|
||||
if(A==secretnumber) {
|
||||
c64scr.print_string("\nThat's my number, impressive!\n")
|
||||
goto goodbye
|
||||
c64scr.print_string("\nThat's my number, impressive!\n")
|
||||
goto goodbye
|
||||
}
|
||||
c64scr.print_string("That is too ")
|
||||
if(A > secretnumber)
|
||||
@ -62,8 +62,9 @@ ask_guess:
|
||||
|
||||
attempts_left--
|
||||
if(attempts_left>0) goto ask_guess
|
||||
; more efficient: if_nz goto ask_guess
|
||||
|
||||
game_over:
|
||||
; game over.
|
||||
c64scr.print_string("\nToo bad! It was: ")
|
||||
c64scr.print_byte_decimal(secretnumber)
|
||||
c64.CHROUT("\n")
|
||||
|
@ -16,9 +16,10 @@ enum class DataType {
|
||||
STR,
|
||||
STR_P,
|
||||
STR_S,
|
||||
STR_PS
|
||||
|
||||
// TODO arrays (of byte, word) and matrix (of byte) should have their own datatype as well?
|
||||
STR_PS,
|
||||
ARRAY,
|
||||
ARRAY_W,
|
||||
MATRIX
|
||||
}
|
||||
|
||||
enum class Register {
|
||||
@ -74,7 +75,7 @@ open class ExpressionException(message: String, val position: Position?) : AstEx
|
||||
}
|
||||
}
|
||||
|
||||
class UndefinedSymbolException(val symbol: IdentifierReference)
|
||||
class UndefinedSymbolException(symbol: IdentifierReference)
|
||||
: ExpressionException("undefined symbol: ${symbol.nameInSource.joinToString(".")}", symbol.position)
|
||||
|
||||
|
||||
@ -503,13 +504,26 @@ enum class VarDeclType {
|
||||
}
|
||||
|
||||
class VarDecl(val type: VarDeclType,
|
||||
val datatype: DataType,
|
||||
declaredDatatype: DataType,
|
||||
val arrayspec: ArraySpec?,
|
||||
val name: String,
|
||||
var value: IExpression?) : IStatement {
|
||||
override var position: Position? = null
|
||||
override lateinit var parent: Node
|
||||
val datatype: DataType
|
||||
|
||||
init {
|
||||
datatype = when {
|
||||
arrayspec!=null -> // it's not a scalar, adjust the datatype
|
||||
when(declaredDatatype) {
|
||||
DataType.BYTE -> DataType.ARRAY
|
||||
DataType.WORD -> DataType.ARRAY_W
|
||||
DataType.MATRIX -> TODO()
|
||||
else -> throw FatalAstException("invalid vardecl array datatype $declaredDatatype at $position")
|
||||
}
|
||||
else -> declaredDatatype
|
||||
}
|
||||
}
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
arrayspec?.linkParents(this)
|
||||
@ -518,9 +532,6 @@ class VarDecl(val type: VarDeclType,
|
||||
|
||||
override fun process(processor: IAstProcessor) = processor.process(this)
|
||||
|
||||
val isScalar = arrayspec==null // TODO replace with actual array/matrix datatype itself?
|
||||
val isArray = arrayspec!=null && arrayspec.y==null
|
||||
val isMatrix = arrayspec?.y != null
|
||||
val scopedname: List<String> by lazy { makeScopedName(name) }
|
||||
|
||||
fun arraySizeX(namespace: INameScope) : Int? {
|
||||
|
@ -25,7 +25,7 @@ fun Module.checkValid(globalNamespace: INameScope, compilerOptions: CompilationO
|
||||
* todo check subroutine return values against the call's result assignments
|
||||
*/
|
||||
|
||||
class AstChecker(private val globalNamespace: INameScope, val compilerOptions: CompilationOptions) : IAstProcessor {
|
||||
class AstChecker(private val globalNamespace: INameScope, private val compilerOptions: CompilationOptions) : IAstProcessor {
|
||||
private val checkResult: MutableList<SyntaxError> = mutableListOf()
|
||||
|
||||
fun result(): List<SyntaxError> {
|
||||
@ -132,9 +132,7 @@ class AstChecker(private val globalNamespace: INameScope, val compilerOptions: C
|
||||
|
||||
if(assignment.value is LiteralValue) {
|
||||
val targetDatatype = assignment.target.determineDatatype(globalNamespace, assignment)
|
||||
if(checkValueType(targetDatatype, assignment.value as LiteralValue, assignment.position)) {
|
||||
checkValueRange(targetDatatype, assignment.value as LiteralValue, assignment.position)
|
||||
}
|
||||
checkValueTypeAndRange(targetDatatype, null, assignment.value as LiteralValue, assignment.position)
|
||||
}
|
||||
return super.process(assignment)
|
||||
}
|
||||
@ -160,20 +158,17 @@ class AstChecker(private val globalNamespace: INameScope, val compilerOptions: C
|
||||
|
||||
when(decl.type) {
|
||||
VarDeclType.VAR, VarDeclType.CONST -> {
|
||||
when {
|
||||
decl.value == null ->
|
||||
when(decl.value) {
|
||||
null -> {
|
||||
err("var/const declaration needs a compile-time constant initializer value")
|
||||
decl.value !is LiteralValue ->
|
||||
err("var/const declaration needs a compile-time constant initializer value, found: ${decl.value!!::class.simpleName}")
|
||||
decl.isScalar -> {
|
||||
if(checkValueType(decl, decl.value as LiteralValue, decl.position)) {
|
||||
checkValueRange(decl.datatype, decl.value as LiteralValue, decl.position)
|
||||
}
|
||||
return super.process(decl)
|
||||
}
|
||||
decl.isArray || decl.isMatrix -> {
|
||||
checkConstInitializerValueArray(decl)
|
||||
!is LiteralValue -> {
|
||||
err("var/const declaration needs a compile-time constant initializer value, found: ${decl.value!!::class.simpleName}")
|
||||
return super.process(decl)
|
||||
}
|
||||
}
|
||||
checkValueTypeAndRange(decl.datatype, decl.arrayspec, decl.value as LiteralValue, decl.position)
|
||||
}
|
||||
VarDeclType.MEMORY -> {
|
||||
if(decl.value !is LiteralValue) {
|
||||
@ -277,45 +272,6 @@ class AstChecker(private val globalNamespace: INameScope, val compilerOptions: C
|
||||
return super.process(literalValue)
|
||||
}
|
||||
|
||||
private fun checkConstInitializerValueArray(decl: VarDecl) {
|
||||
val value = decl.value as LiteralValue
|
||||
// init value should either be a scalar or an array with the same dimensions as the arrayspec.
|
||||
|
||||
if(decl.isArray) {
|
||||
if(value.arrayvalue==null) {
|
||||
checkValueRange(decl.datatype, value.constValue(globalNamespace)!!, value.position)
|
||||
}
|
||||
else {
|
||||
val expected = decl.arraySizeX(globalNamespace)
|
||||
if (value.arrayvalue.size != expected)
|
||||
checkResult.add(SyntaxError("initializer array size mismatch (expecting $expected, got ${value.arrayvalue.size})", decl.position))
|
||||
else {
|
||||
for(v in value.arrayvalue) {
|
||||
if(!checkValueRange(decl.datatype, v.constValue(globalNamespace)!!, v.position))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(decl.isMatrix) {
|
||||
if(value.arrayvalue==null) {
|
||||
checkValueRange(decl.datatype, value.constValue(globalNamespace)!!, value.position)
|
||||
}
|
||||
else {
|
||||
val expected = decl.arraySizeX(globalNamespace)!! * decl.arraySizeY(globalNamespace)!!
|
||||
if (value.arrayvalue.size != expected)
|
||||
checkResult.add(SyntaxError("initializer array size mismatch (expecting $expected, got ${value.arrayvalue.size})", decl.position))
|
||||
else {
|
||||
for(v in value.arrayvalue) {
|
||||
if(!checkValueRange(decl.datatype, v.constValue(globalNamespace)!!, v.position))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun process(range: RangeExpr): IExpression {
|
||||
fun err(msg: String) {
|
||||
checkResult.add(SyntaxError(msg, range.position))
|
||||
@ -391,7 +347,7 @@ class AstChecker(private val globalNamespace: INameScope, val compilerOptions: C
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkValueRange(datatype: DataType, value: LiteralValue, position: Position?) : Boolean {
|
||||
private fun checkValueTypeAndRange(datatype: DataType, arrayspec: ArraySpec?, value: LiteralValue, position: Position?) : Boolean {
|
||||
fun err(msg: String) : Boolean {
|
||||
checkResult.add(SyntaxError(msg, position))
|
||||
return false
|
||||
@ -421,45 +377,49 @@ class AstChecker(private val globalNamespace: INameScope, val compilerOptions: C
|
||||
if (str.isEmpty() || str.length > 65535)
|
||||
return err("string length must be 1..65535")
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
DataType.ARRAY -> {
|
||||
// value may be either a single byte, or a byte array
|
||||
if(value.isArray) {
|
||||
for (av in value.arrayvalue!!) {
|
||||
val number = (av as LiteralValue).intvalue
|
||||
?: return err("array must be all bytes")
|
||||
|
||||
private fun checkValueType(vardecl: VarDecl, value: LiteralValue, position: Position?) : Boolean {
|
||||
fun err(msg: String) : Boolean {
|
||||
checkResult.add(SyntaxError(msg, position))
|
||||
return false
|
||||
}
|
||||
when {
|
||||
vardecl.isScalar -> checkValueType(vardecl.datatype, value, position)
|
||||
vardecl.isArray -> if(value.arrayvalue==null) return err("array value expected")
|
||||
vardecl.isMatrix -> TODO()
|
||||
}
|
||||
return true
|
||||
}
|
||||
val expectedSize = arrayspec?.x?.constValue(globalNamespace)?.intvalue
|
||||
if (value.arrayvalue.size != expectedSize)
|
||||
return err("initializer array size mismatch (expecting $expectedSize, got ${value.arrayvalue.size})")
|
||||
|
||||
private fun checkValueType(targetType: DataType, value: LiteralValue, position: Position?) : Boolean {
|
||||
fun err(msg: String) : Boolean {
|
||||
checkResult.add(SyntaxError(msg, position))
|
||||
return false
|
||||
}
|
||||
when(targetType) {
|
||||
DataType.FLOAT -> {
|
||||
if (value.floatvalue == null)
|
||||
return err("floating point value expected")
|
||||
if (number < 0 || number > 255)
|
||||
return err("value '$number' in byte array is out of range for unsigned byte")
|
||||
}
|
||||
} else {
|
||||
val number = value.intvalue
|
||||
?: return err("byte integer value expected")
|
||||
if (number < 0 || number > 255)
|
||||
return err("value '$number' out of range for unsigned byte")
|
||||
}
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
if (value.intvalue == null)
|
||||
return err("byte integer value expected")
|
||||
}
|
||||
DataType.WORD -> {
|
||||
if (value.intvalue == null)
|
||||
return err("word integer value expected")
|
||||
}
|
||||
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> {
|
||||
if (value.strvalue == null)
|
||||
return err("string value expected")
|
||||
DataType.ARRAY_W -> {
|
||||
// value may be either a single word, or a word array
|
||||
if(value.isArray) {
|
||||
for (av in value.arrayvalue!!) {
|
||||
val number = (av as LiteralValue).intvalue
|
||||
?: return err("array must be all words")
|
||||
|
||||
val expectedSize = arrayspec?.x?.constValue(globalNamespace)?.intvalue
|
||||
if (value.arrayvalue.size != expectedSize)
|
||||
return err("initializer array size mismatch (expecting $expectedSize, got ${value.arrayvalue.size})")
|
||||
|
||||
if (number < 0 || number > 65535)
|
||||
return err("value '$number' in word array is out of range for unsigned word")
|
||||
}
|
||||
} else {
|
||||
val number = value.intvalue
|
||||
?: return err("word integer value expected")
|
||||
if (number < 0 || number > 65535)
|
||||
return err("value '$number' out of range for unsigned word")
|
||||
}
|
||||
}
|
||||
DataType.MATRIX -> TODO()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -9,43 +9,43 @@ class StatementReorderer: IAstProcessor {
|
||||
// -- the remaining statements then follow in their original order.
|
||||
// - the 'start' subroutine in the 'main' block will be moved to the top immediately following the directives.
|
||||
|
||||
val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%address", "%option")
|
||||
private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%address", "%option")
|
||||
|
||||
override fun process(node: Module) {
|
||||
val mainBlock = node.statements.single { it is Block && it.name=="main" }
|
||||
node.statements.remove(mainBlock)
|
||||
node.statements.add(0, mainBlock)
|
||||
val varDecls = node.statements.filter { it is VarDecl }
|
||||
node.statements.removeAll(varDecls)
|
||||
node.statements.addAll(0, varDecls)
|
||||
val directives = node.statements.filter {it is Directive && directivesToMove.contains(it.directive)}
|
||||
node.statements.removeAll(directives)
|
||||
node.statements.addAll(0, directives)
|
||||
super.process(node)
|
||||
override fun process(module: Module) {
|
||||
val mainBlock = module.statements.single { it is Block && it.name=="main" }
|
||||
module.statements.remove(mainBlock)
|
||||
module.statements.add(0, mainBlock)
|
||||
val varDecls = module.statements.filter { it is VarDecl }
|
||||
module.statements.removeAll(varDecls)
|
||||
module.statements.addAll(0, varDecls)
|
||||
val directives = module.statements.filter {it is Directive && directivesToMove.contains(it.directive)}
|
||||
module.statements.removeAll(directives)
|
||||
module.statements.addAll(0, directives)
|
||||
super.process(module)
|
||||
}
|
||||
|
||||
override fun process(node: Block): IStatement {
|
||||
val startSub = node.statements.singleOrNull {it is Subroutine && it.name=="start"}
|
||||
override fun process(block: Block): IStatement {
|
||||
val startSub = block.statements.singleOrNull {it is Subroutine && it.name=="start"}
|
||||
if(startSub!=null) {
|
||||
node.statements.remove(startSub)
|
||||
node.statements.add(0, startSub)
|
||||
block.statements.remove(startSub)
|
||||
block.statements.add(0, startSub)
|
||||
}
|
||||
val varDecls = node.statements.filter { it is VarDecl }
|
||||
node.statements.removeAll(varDecls)
|
||||
node.statements.addAll(0, varDecls)
|
||||
val directives = node.statements.filter {it is Directive && directivesToMove.contains(it.directive)}
|
||||
node.statements.removeAll(directives)
|
||||
node.statements.addAll(0, directives)
|
||||
return super.process(node)
|
||||
val varDecls = block.statements.filter { it is VarDecl }
|
||||
block.statements.removeAll(varDecls)
|
||||
block.statements.addAll(0, varDecls)
|
||||
val directives = block.statements.filter {it is Directive && directivesToMove.contains(it.directive)}
|
||||
block.statements.removeAll(directives)
|
||||
block.statements.addAll(0, directives)
|
||||
return super.process(block)
|
||||
}
|
||||
|
||||
override fun process(node: Subroutine): IStatement {
|
||||
val varDecls = node.statements.filter { it is VarDecl }
|
||||
node.statements.removeAll(varDecls)
|
||||
node.statements.addAll(0, varDecls)
|
||||
val directives = node.statements.filter {it is Directive && directivesToMove.contains(it.directive)}
|
||||
node.statements.removeAll(directives)
|
||||
node.statements.addAll(0, directives)
|
||||
return super.process(node)
|
||||
override fun process(subroutine: Subroutine): IStatement {
|
||||
val varDecls = subroutine.statements.filter { it is VarDecl }
|
||||
subroutine.statements.removeAll(varDecls)
|
||||
subroutine.statements.addAll(0, varDecls)
|
||||
val directives = subroutine.statements.filter {it is Directive && directivesToMove.contains(it.directive)}
|
||||
subroutine.statements.removeAll(directives)
|
||||
subroutine.statements.addAll(0, directives)
|
||||
return super.process(subroutine)
|
||||
}
|
||||
}
|
||||
|
@ -488,7 +488,7 @@ class Program (prog: MutableList<Instruction>,
|
||||
Opcode.SYSCALL -> {
|
||||
val syscallparts = args!!.split(' ')
|
||||
val call = Syscall.valueOf(syscallparts[0])
|
||||
val callValue = if(parts.size==2) getArgValue(syscallparts[1]) else null
|
||||
val callValue = if(syscallparts.size==2) getArgValue(syscallparts[1]) else null
|
||||
val callValues = if(callValue==null) emptyList() else listOf(callValue)
|
||||
Instruction(opcode, Value(DataType.BYTE, call.callNr), callValues)
|
||||
}
|
||||
@ -690,7 +690,7 @@ class StackVm(val traceOutputFile: String?) {
|
||||
}
|
||||
}
|
||||
|
||||
fun dispatch(ins: Instruction) : Instruction {
|
||||
private fun dispatch(ins: Instruction) : Instruction {
|
||||
traceOutput?.println("\n$ins")
|
||||
when (ins.opcode) {
|
||||
Opcode.NOP -> {}
|
||||
|
Loading…
Reference in New Issue
Block a user