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"
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

View File

@ -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")

View File

@ -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? {

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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 -> {}