check assignment targets

This commit is contained in:
Irmen de Jong 2018-09-06 01:02:36 +02:00
parent f23808eaae
commit 1a60119fde
3 changed files with 95 additions and 38 deletions

View File

@ -80,7 +80,6 @@
equalQQ = foo(33) equalQQ = foo(33)
equalQQ = main.foo(33) equalQQ = main.foo(33)
XY = hopla*2+hopla1 XY = hopla*2+hopla1
A = "derp" * %000100
byte equalWW = 4==4 byte equalWW = 4==4
const byte equalWW2 = (4+hopla)>0 const byte equalWW2 = (4+hopla)>0
@ -123,6 +122,14 @@
main.foo(1,2,3) main.foo(1,2,3)
sub start () -> () {
word dinges = 0
dinges=round(blerp1)
A=round(blerp1)
return
}
mega: mega:
X += 1 X += 1
cool: cool:

View File

@ -17,6 +17,8 @@ enum class DataType {
STR_P, STR_P,
STR_S, STR_S,
STR_PS STR_PS
// TODO arrays (of byte, word) and matrix (of byte) should have their own datatype as well?
} }
enum class Register { enum class Register {
@ -25,12 +27,7 @@ enum class Register {
Y, Y,
AX, AX,
AY, AY,
XY, XY
PC,
PI,
PZ,
PN,
PV
} }
enum class Statusflag { enum class Statusflag {
@ -168,6 +165,12 @@ interface IAstProcessor {
fun process(literalValue: LiteralValue): LiteralValue { fun process(literalValue: LiteralValue): LiteralValue {
return literalValue return literalValue
} }
fun process(assignment: Assignment): IStatement {
assignment.target = assignment.target.process(this)
assignment.value = assignment.value.process(this)
return assignment
}
} }
@ -362,7 +365,7 @@ private class GlobalNamespace(override val name: String,
override var statements: MutableList<IStatement>, override var statements: MutableList<IStatement>,
override val position: Position?) : INameScope { override val position: Position?) : INameScope {
private val scopedNamesUsed: MutableSet<String> = mutableSetOf("main") // main is always used private val scopedNamesUsed: MutableSet<String> = mutableSetOf("main", "main.start") // main and main.start are always used
override fun usedNames(): Set<String> = scopedNamesUsed override fun usedNames(): Set<String> = scopedNamesUsed
@ -515,7 +518,7 @@ 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 val isScalar = arrayspec==null // TODO replace with actual array/matrix datatype itself?
val isArray = arrayspec!=null && arrayspec.y==null val isArray = arrayspec!=null && arrayspec.y==null
val isMatrix = arrayspec?.y != null val isMatrix = arrayspec?.y != null
val scopedname: List<String> by lazy { makeScopedName(name) } val scopedname: List<String> by lazy { makeScopedName(name) }
@ -543,11 +546,7 @@ class Assignment(var target: AssignTarget, val aug_op : String?, var value: IExp
value.linkParents(this) value.linkParents(this)
} }
override fun process(processor: IAstProcessor): IStatement { override fun process(processor: IAstProcessor) = processor.process(this)
target = target.process(processor)
value = value.process(processor)
return this
}
} }
data class AssignTarget(val register: Register?, val identifier: IdentifierReference?) : Node { data class AssignTarget(val register: Register?, val identifier: IdentifierReference?) : Node {
@ -560,6 +559,18 @@ data class AssignTarget(val register: Register?, val identifier: IdentifierRefer
} }
fun process(processor: IAstProcessor) = this fun process(processor: IAstProcessor) = this
fun determineDatatype(namespace: INameScope, stmt: IStatement): DataType {
if(register!=null)
return when(register){
Register.A, Register.X, Register.Y -> DataType.BYTE
Register.AX, Register.AY, Register.XY -> DataType.WORD
}
val symbol = namespace.lookup(identifier!!.nameInSource, stmt)
if(symbol is VarDecl) return symbol.datatype
throw FatalAstException("cannot determine datatype of assignment target $this")
}
} }
@ -597,9 +608,8 @@ class BinaryExpression(var left: IExpression, val operator: String, var right: I
right.linkParents(this) right.linkParents(this)
} }
override fun constValue(namespace: INameScope): LiteralValue? { // binary expression should actually have been optimized away into a single value, before const value was requested...
throw FatalAstException("binary expression should have been optimized away into a single value, before const value was requested (this error is often caused by another) pos=$position") override fun constValue(namespace: INameScope): LiteralValue? = null
}
override fun process(processor: IAstProcessor) = processor.process(this) override fun process(processor: IAstProcessor) = processor.process(this)
override fun referencesIdentifier(name: String) = left.referencesIdentifier(name) || right.referencesIdentifier(name) override fun referencesIdentifier(name: String) = left.referencesIdentifier(name) || right.referencesIdentifier(name)
@ -813,7 +823,7 @@ class FunctionCallStatement(override var target: IdentifierReference, override v
override fun process(processor: IAstProcessor) = processor.process(this) override fun process(processor: IAstProcessor) = processor.process(this)
override fun toString(): String { override fun toString(): String {
return "FunctionCall(target=$target, targetStmt=$targetStatement, pos=$position)" return "FunctionCall(target=$target, pos=$position)"
} }
} }

View File

@ -41,6 +41,13 @@ class AstChecker(private val globalNamespace: INameScope, val compilerOptions: C
entry.value.mapTo(checkResult) { SyntaxError("directive can just occur once", it.position) } entry.value.mapTo(checkResult) { SyntaxError("directive can just occur once", it.position) }
} }
} }
// there must be a 'main' block with a 'start' subroutine for the program entry point.
val mainBlock = module.statements.singleOrNull { it is Block && it.name=="main" } as? Block?
val startSub = mainBlock?.subScopes()?.get("start")
if(startSub==null) {
checkResult.add(SyntaxError("missing program entrypoint ('start' subroutine in 'main' block)", module.position))
}
} }
override fun process(jump: Jump): IStatement { override fun process(jump: Jump): IStatement {
@ -107,6 +114,31 @@ class AstChecker(private val globalNamespace: INameScope, val compilerOptions: C
return subroutine return subroutine
} }
/**
* Assignment target must be register, or a variable name
* for constant-value assignments, check the datatype as well
*/
override fun process(assignment: Assignment): IStatement {
if(assignment.target.identifier!=null) {
val targetSymbol = globalNamespace.lookup(assignment.target.identifier!!.nameInSource, assignment)
if(targetSymbol !is VarDecl) {
checkResult.add(SyntaxError("assignment LHS must be register or variable", assignment.position))
return super.process(assignment)
} else if(targetSymbol.type==VarDeclType.CONST) {
checkResult.add(SyntaxError("cannot assign new value to a constant", assignment.position))
return super.process(assignment)
}
}
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)
}
}
return super.process(assignment)
}
/** /**
* Check the variable declarations (values within range etc) * Check the variable declarations (values within range etc)
*/ */
@ -399,28 +431,36 @@ class AstChecker(private val globalNamespace: INameScope, val compilerOptions: C
return false return false
} }
when { when {
vardecl.isScalar -> when (vardecl.datatype) { vardecl.isScalar -> checkValueType(vardecl.datatype, value, position)
DataType.FLOAT -> { vardecl.isArray -> if(value.arrayvalue==null) return err("array value expected")
if (value.floatvalue == null)
return err("floating point value expected")
}
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")
}
}
vardecl.isArray -> if(value.arrayvalue==null)
return err("array value expected")
vardecl.isMatrix -> TODO() vardecl.isMatrix -> TODO()
} }
return true return true
} }
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")
}
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")
}
}
return true
}
} }