error when doing txt.print('@') where "@" was intended (byte for string parameter)

This commit is contained in:
Irmen de Jong 2023-08-14 19:25:26 +02:00
parent bc8126eb16
commit f98ee326b4
7 changed files with 93 additions and 77 deletions

View File

@ -220,7 +220,7 @@ internal class AstChecker(private val program: Program,
override fun visit(jump: Jump) {
val ident = jump.identifier
if(ident!=null) {
val targetStatement = checkFunctionOrLabelExists(ident, jump)
val targetStatement = ident.checkFunctionOrLabelExists(program, jump, errors)
if(targetStatement!=null) {
if(targetStatement is BuiltinFunctionPlaceholder)
errors.err("can't jump to a builtin function", jump.position)
@ -1068,7 +1068,7 @@ internal class AstChecker(private val program: Program,
val stmtOfExpression = findParentNode<Statement>(functionCallExpr)
?: throw FatalAstException("cannot determine statement scope of function call expression at ${functionCallExpr.position}")
val targetStatement = checkFunctionOrLabelExists(functionCallExpr.target, stmtOfExpression)
val targetStatement = functionCallExpr.target.checkFunctionOrLabelExists(program, stmtOfExpression, errors)
if(targetStatement!=null)
checkFunctionCall(targetStatement, functionCallExpr.args, functionCallExpr.position)
@ -1120,7 +1120,7 @@ internal class AstChecker(private val program: Program,
}
override fun visit(functionCallStatement: FunctionCallStatement) {
val targetStatement = checkFunctionOrLabelExists(functionCallStatement.target, functionCallStatement)
val targetStatement = functionCallStatement.target.checkFunctionOrLabelExists(program, functionCallStatement, errors)
if(targetStatement!=null) {
checkFunctionCall(targetStatement, functionCallStatement.args, functionCallStatement.position)
checkUnusedReturnValues(functionCallStatement, targetStatement, errors)
@ -1421,25 +1421,6 @@ internal class AstChecker(private val program: Program,
errors.err("%asm containing IR code cannot be translated to 6502 assembly", inlineAssembly.position)
}
private fun checkFunctionOrLabelExists(target: IdentifierReference, statement: Statement): Statement? {
when (val targetStatement = target.targetStatement(program)) {
is Label, is Subroutine, is BuiltinFunctionPlaceholder -> return targetStatement
is VarDecl -> {
if(statement is Jump) {
if (targetStatement.datatype == DataType.UWORD)
return targetStatement
else
errors.err("wrong address variable datatype, expected uword", target.position)
}
else
errors.err("cannot call that: ${target.nameInSource.joinToString(".")}", target.position)
}
null -> errors.undefined(target.nameInSource, target.position)
else -> errors.err("cannot call that: ${target.nameInSource.joinToString(".")}", target.position)
}
return null
}
private fun checkValueTypeAndRangeString(targetDt: DataType, value: StringLiteral) : Boolean {
return if (targetDt == DataType.STR) {
when {

View File

@ -5,10 +5,7 @@ import prog8.ast.Program
import prog8.ast.expressions.CharLiteral
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.Directive
import prog8.ast.statements.InlineAssembly
import prog8.ast.statements.Subroutine
import prog8.ast.statements.VarDeclOrigin
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.*
@ -151,4 +148,25 @@ internal fun Subroutine.hasRtsInAsm(): Boolean {
.asSequence()
.filterIsInstance<InlineAssembly>()
.any { it.hasReturnOrRts() }
}
internal fun IdentifierReference.checkFunctionOrLabelExists(program: Program, statement: Statement, errors: IErrorReporter): Statement? {
when (val targetStatement = this.targetStatement(program)) {
is Label, is Subroutine, is BuiltinFunctionPlaceholder -> return targetStatement
is VarDecl -> {
if(statement is Jump) {
if (targetStatement.datatype == DataType.UWORD)
return targetStatement
else
errors.err("wrong address variable datatype, expected uword", this.position)
}
else
errors.err("cannot call that: ${this.nameInSource.joinToString(".")}", this.position)
}
null -> {
errors.undefined(this.nameInSource, this.position)
}
else -> errors.err("cannot call that: ${this.nameInSource.joinToString(".")}", this.position)
}
return null
}

View File

@ -1,9 +1,12 @@
package prog8.compiler.astprocessing
import prog8.ast.IFunctionCall
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.FatalAstException
import prog8.ast.base.SyntaxError
import prog8.ast.expressions.*
import prog8.ast.findParentNode
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
@ -201,4 +204,42 @@ class AstPreprocessor(val program: Program,
return noModifications
}
override fun after(bfc: BuiltinFunctionCall, parent: Node): Iterable<IAstModification> {
val stmtOfExpression = findParentNode<Statement>(bfc)
?: throw FatalAstException("cannot determine statement scope of function call expression at ${bfc.position}")
checkStringParam(bfc as IFunctionCall, stmtOfExpression)
return noModifications
}
override fun after(bfcs: BuiltinFunctionCallStatement, parent: Node): Iterable<IAstModification> {
checkStringParam(bfcs as IFunctionCall, bfcs)
return noModifications
}
override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
val stmtOfExpression = findParentNode<Statement>(functionCallExpr)
?: throw FatalAstException("cannot determine statement scope of function call expression at ${functionCallExpr.position}")
checkStringParam(functionCallExpr as IFunctionCall, stmtOfExpression)
return noModifications
}
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
checkStringParam(functionCallStatement as IFunctionCall, functionCallStatement)
return noModifications
}
private fun checkStringParam(call: IFunctionCall, stmt: Statement) {
val targetStatement = call.target.checkFunctionOrLabelExists(program, stmt, errors)
if(targetStatement!=null) {
if(targetStatement is Subroutine) {
for(arg in call.args.zip(targetStatement.parameters)) {
if(arg.first.inferType(program).isBytes && arg.second.type==DataType.STR) {
errors.err("cannot use byte value for string parameter", arg.first.position)
}
}
}
}
}
}

View File

@ -351,5 +351,20 @@ main
compileText(VMTarget(), optimize=false, src, writeAssembly=false) shouldNotBe null
}
test("char as str param is error") {
val src = """
main {
sub start() {
print('@')
}
sub print(str message) {
}
}"""
val errors = ErrorReporterForTests()
compileText(VMTarget(), optimize=false, src, writeAssembly=false, errors = errors) shouldBe null
errors.errors.single() shouldContain "cannot use byte value"
}
})

View File

@ -954,7 +954,15 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
override fun copy() = IdentifierReference(nameInSource, position)
override fun constValue(program: Program): NumericLiteral? {
val node = definingScope.lookup(nameInSource) ?: throw UndefinedSymbolError(this)
val node = definingScope.lookup(nameInSource)
if(node==null) {
// maybe not a statement but perhaps a subroutine parameter?
(definingScope as? Subroutine)?.let { sub ->
if(sub.parameters.any { it.name==nameInSource.last() })
return null
}
throw UndefinedSymbolError(this)
}
val vardecl = node as? VarDecl
if(vardecl==null) {
return null

View File

@ -1,7 +1,6 @@
TODO
====
- don't allow txt.print('@') if possible, don't cast up a byte to str
- [on branch:] investigate McCarthy evaluation again? this may also reduce code size perhaps for things like if a>4 or a<2 ....
- IR: reduce the number of branch instructions such as BEQ, BEQR, etc (gradually), replace with CMP(I) + status branch instruction
- IR: reduce amount of CMP/CMPI after instructions that set the status bits correctly (LOADs? INC? etc), but only after setting the status bits is verified!

View File

@ -1,54 +1,8 @@
%import textio
%zeropage basicsafe
cbm2 {
sub SETTIM(ubyte a, ubyte b, ubyte c) {
}
sub RDTIM16() -> uword {
return 0
}
}
main {
sub start() {
uword wvalue
uword wvalue2
print('@')
}
txt.print("word square..")
cbm.SETTIM(0,0,0)
repeat 200 {
for wvalue in 0 to 200 {
cx16.r0 = wvalue*wvalue
}
}
txt.print_uw(cbm.RDTIM16())
txt.nl()
txt.print("word square via multiply new..")
cbm.SETTIM(0,0,0)
wvalue2 = wvalue
repeat 200 {
for wvalue in 0 to 200 {
cx16.r0 = wvalue*wvalue2
}
}
txt.print_uw(cbm.RDTIM16())
txt.nl()
txt.print("word square verify..")
for wvalue in 0 to 200 {
wvalue2 = wvalue
if wvalue*wvalue != wvalue*wvalue2 {
txt.print("different! ")
txt.print_uw(wvalue)
txt.spc()
txt.spc()
txt.print_uw(wvalue*wvalue)
txt.spc()
txt.print_uw(wvalue*wvalue2)
sys.exit(1)
}
}
txt.nl()
sub print(str message) {
}
}