arrays are now a datatype as well

fixed array initializer checks
This commit is contained in:
Irmen de Jong 2018-09-06 22:28:37 +02:00
parent 3933fdab13
commit e928997193
6 changed files with 108 additions and 135 deletions

View File

@ -211,6 +211,7 @@ Various examples::
str name = "my name is Irmen" str name = "my name is Irmen"
word address = #counter word address = #counter
byte[5] values = [11, 22, 33, 44, 55] byte[5] values = [11, 22, 33, 44, 55]
byte[5] values = 255 ; initialize with five 255 bytes
byte[5][6] empty_matrix byte[5][6] empty_matrix

View File

@ -11,7 +11,7 @@
memory word freadstr_arg = $22 ; argument for FREADSTR memory word freadstr_arg = $22 ; argument for FREADSTR
c64.init_system() c64.init_system()
c64.VMCSB |= 2 ; lowercase charset c64.VMCSB |= 2 ; activate lowercase charset
; greeting ; greeting
c64scr.print_string("Enter your name: ") c64scr.print_string("Enter your name: ")
@ -30,7 +30,7 @@
c64.FADDH() ; add 0.5.. c64.FADDH() ; add 0.5..
c64.FADDH() ; and again, so +1 total c64.FADDH() ; and again, so +1 total
AY = c64flt.GETADRAY() AY = c64flt.GETADRAY()
secretnumber=A secretnumber = A
;A=math.randbyte() ;A=math.randbyte()
;A+=c64.RASTER ;A+=c64.RASTER
;A-=c64.TIME_LO ;A-=c64.TIME_LO
@ -62,8 +62,9 @@ ask_guess:
attempts_left-- attempts_left--
if(attempts_left>0) goto ask_guess 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_string("\nToo bad! It was: ")
c64scr.print_byte_decimal(secretnumber) c64scr.print_byte_decimal(secretnumber)
c64.CHROUT("\n") c64.CHROUT("\n")

View File

@ -16,9 +16,10 @@ enum class DataType {
STR, STR,
STR_P, STR_P,
STR_S, STR_S,
STR_PS STR_PS,
ARRAY,
// TODO arrays (of byte, word) and matrix (of byte) should have their own datatype as well? ARRAY_W,
MATRIX
} }
enum class Register { 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) : ExpressionException("undefined symbol: ${symbol.nameInSource.joinToString(".")}", symbol.position)
@ -503,13 +504,26 @@ enum class VarDeclType {
} }
class VarDecl(val type: VarDeclType, class VarDecl(val type: VarDeclType,
val datatype: DataType, declaredDatatype: DataType,
val arrayspec: ArraySpec?, val arrayspec: ArraySpec?,
val name: String, val name: String,
var value: IExpression?) : IStatement { var value: IExpression?) : IStatement {
override var position: Position? = null override var position: Position? = null
override lateinit var parent: Node 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) { override fun linkParents(parent: Node) {
this.parent = parent this.parent = parent
arrayspec?.linkParents(this) arrayspec?.linkParents(this)
@ -518,9 +532,6 @@ class VarDecl(val type: VarDeclType,
override fun process(processor: IAstProcessor) = processor.process(this) 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) } val scopedname: List<String> by lazy { makeScopedName(name) }
fun arraySizeX(namespace: INameScope) : Int? { fun arraySizeX(namespace: INameScope) : Int? {

View File

@ -25,7 +25,7 @@ fun Module.checkValid(globalNamespace: INameScope, compilerOptions: CompilationO
* todo check subroutine return values against the call's result assignments * 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() private val checkResult: MutableList<SyntaxError> = mutableListOf()
fun result(): List<SyntaxError> { fun result(): List<SyntaxError> {
@ -132,9 +132,7 @@ class AstChecker(private val globalNamespace: INameScope, val compilerOptions: C
if(assignment.value is LiteralValue) { if(assignment.value is LiteralValue) {
val targetDatatype = assignment.target.determineDatatype(globalNamespace, assignment) val targetDatatype = assignment.target.determineDatatype(globalNamespace, assignment)
if(checkValueType(targetDatatype, assignment.value as LiteralValue, assignment.position)) { checkValueTypeAndRange(targetDatatype, null, assignment.value as LiteralValue, assignment.position)
checkValueRange(targetDatatype, assignment.value as LiteralValue, assignment.position)
}
} }
return super.process(assignment) return super.process(assignment)
} }
@ -160,20 +158,17 @@ class AstChecker(private val globalNamespace: INameScope, val compilerOptions: C
when(decl.type) { when(decl.type) {
VarDeclType.VAR, VarDeclType.CONST -> { VarDeclType.VAR, VarDeclType.CONST -> {
when { when(decl.value) {
decl.value == null -> null -> {
err("var/const declaration needs a compile-time constant initializer value") err("var/const declaration needs a compile-time constant initializer value")
decl.value !is LiteralValue -> return super.process(decl)
}
!is LiteralValue -> {
err("var/const declaration needs a compile-time constant initializer value, found: ${decl.value!!::class.simpleName}") err("var/const declaration needs a compile-time constant initializer value, found: ${decl.value!!::class.simpleName}")
decl.isScalar -> { return super.process(decl)
if(checkValueType(decl, decl.value as LiteralValue, decl.position)) {
checkValueRange(decl.datatype, decl.value as LiteralValue, decl.position)
}
}
decl.isArray || decl.isMatrix -> {
checkConstInitializerValueArray(decl)
} }
} }
checkValueTypeAndRange(decl.datatype, decl.arrayspec, decl.value as LiteralValue, decl.position)
} }
VarDeclType.MEMORY -> { VarDeclType.MEMORY -> {
if(decl.value !is LiteralValue) { if(decl.value !is LiteralValue) {
@ -277,45 +272,6 @@ class AstChecker(private val globalNamespace: INameScope, val compilerOptions: C
return super.process(literalValue) 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 { override fun process(range: RangeExpr): IExpression {
fun err(msg: String) { fun err(msg: String) {
checkResult.add(SyntaxError(msg, range.position)) 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 { fun err(msg: String) : Boolean {
checkResult.add(SyntaxError(msg, position)) checkResult.add(SyntaxError(msg, position))
return false return false
@ -421,46 +377,50 @@ class AstChecker(private val globalNamespace: INameScope, val compilerOptions: C
if (str.isEmpty() || str.length > 65535) if (str.isEmpty() || str.length > 65535)
return err("string length must be 1..65535") return err("string length must be 1..65535")
} }
} DataType.ARRAY -> {
return true // 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 { val expectedSize = arrayspec?.x?.constValue(globalNamespace)?.intvalue
fun err(msg: String) : Boolean { if (value.arrayvalue.size != expectedSize)
checkResult.add(SyntaxError(msg, position)) return err("initializer array size mismatch (expecting $expectedSize, got ${value.arrayvalue.size})")
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
}
private fun checkValueType(targetType: DataType, value: LiteralValue, position: Position?) : Boolean { if (number < 0 || number > 255)
fun err(msg: String) : Boolean { return err("value '$number' in byte array is out of range for unsigned byte")
checkResult.add(SyntaxError(msg, position))
return false
} }
when(targetType) { } else {
DataType.FLOAT -> { val number = value.intvalue
if (value.floatvalue == null) ?: return err("byte integer value expected")
return err("floating point 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 -> { DataType.ARRAY_W -> {
if (value.intvalue == null) // value may be either a single word, or a word array
return err("word integer value expected") 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")
} }
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> { } else {
if (value.strvalue == null) val number = value.intvalue
return err("string value expected") ?: 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 return true
} }
} }

View File

@ -9,43 +9,43 @@ class StatementReorderer: IAstProcessor {
// -- the remaining statements then follow in their original order. // -- 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. // - 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) { override fun process(module: Module) {
val mainBlock = node.statements.single { it is Block && it.name=="main" } val mainBlock = module.statements.single { it is Block && it.name=="main" }
node.statements.remove(mainBlock) module.statements.remove(mainBlock)
node.statements.add(0, mainBlock) module.statements.add(0, mainBlock)
val varDecls = node.statements.filter { it is VarDecl } val varDecls = module.statements.filter { it is VarDecl }
node.statements.removeAll(varDecls) module.statements.removeAll(varDecls)
node.statements.addAll(0, varDecls) module.statements.addAll(0, varDecls)
val directives = node.statements.filter {it is Directive && directivesToMove.contains(it.directive)} val directives = module.statements.filter {it is Directive && directivesToMove.contains(it.directive)}
node.statements.removeAll(directives) module.statements.removeAll(directives)
node.statements.addAll(0, directives) module.statements.addAll(0, directives)
super.process(node) super.process(module)
} }
override fun process(node: Block): IStatement { override fun process(block: Block): IStatement {
val startSub = node.statements.singleOrNull {it is Subroutine && it.name=="start"} val startSub = block.statements.singleOrNull {it is Subroutine && it.name=="start"}
if(startSub!=null) { if(startSub!=null) {
node.statements.remove(startSub) block.statements.remove(startSub)
node.statements.add(0, startSub) block.statements.add(0, startSub)
} }
val varDecls = node.statements.filter { it is VarDecl } val varDecls = block.statements.filter { it is VarDecl }
node.statements.removeAll(varDecls) block.statements.removeAll(varDecls)
node.statements.addAll(0, varDecls) block.statements.addAll(0, varDecls)
val directives = node.statements.filter {it is Directive && directivesToMove.contains(it.directive)} val directives = block.statements.filter {it is Directive && directivesToMove.contains(it.directive)}
node.statements.removeAll(directives) block.statements.removeAll(directives)
node.statements.addAll(0, directives) block.statements.addAll(0, directives)
return super.process(node) return super.process(block)
} }
override fun process(node: Subroutine): IStatement { override fun process(subroutine: Subroutine): IStatement {
val varDecls = node.statements.filter { it is VarDecl } val varDecls = subroutine.statements.filter { it is VarDecl }
node.statements.removeAll(varDecls) subroutine.statements.removeAll(varDecls)
node.statements.addAll(0, varDecls) subroutine.statements.addAll(0, varDecls)
val directives = node.statements.filter {it is Directive && directivesToMove.contains(it.directive)} val directives = subroutine.statements.filter {it is Directive && directivesToMove.contains(it.directive)}
node.statements.removeAll(directives) subroutine.statements.removeAll(directives)
node.statements.addAll(0, directives) subroutine.statements.addAll(0, directives)
return super.process(node) return super.process(subroutine)
} }
} }

View File

@ -488,7 +488,7 @@ class Program (prog: MutableList<Instruction>,
Opcode.SYSCALL -> { Opcode.SYSCALL -> {
val syscallparts = args!!.split(' ') val syscallparts = args!!.split(' ')
val call = Syscall.valueOf(syscallparts[0]) 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) val callValues = if(callValue==null) emptyList() else listOf(callValue)
Instruction(opcode, Value(DataType.BYTE, call.callNr), callValues) 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") traceOutput?.println("\n$ins")
when (ins.opcode) { when (ins.opcode) {
Opcode.NOP -> {} Opcode.NOP -> {}