mirror of
https://github.com/irmen/prog8.git
synced 2024-11-22 15:33:02 +00:00
check assignment targets
This commit is contained in:
parent
f23808eaae
commit
1a60119fde
@ -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:
|
||||||
|
@ -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)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user