From 2ce6bc59465998a5bacb4c3bbd40f6755996de88 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 11 Aug 2019 14:02:53 +0200 Subject: [PATCH] fix strlen --- compiler/src/prog8/ast/base/Errors.kt | 6 +-- .../prog8/ast/expressions/AstExpressions.kt | 4 +- .../src/prog8/ast/processing/AstChecker.kt | 13 +++++- .../ast/processing/AstIdentifiersChecker.kt | 9 +++- .../VarInitValueAndAddressOfCreator.kt | 3 +- .../c64/codegen2/BuiltinFunctionsAsmGen.kt | 14 +++++- examples/test.p8 | 46 +++++++++++++++---- 7 files changed, 78 insertions(+), 17 deletions(-) diff --git a/compiler/src/prog8/ast/base/Errors.kt b/compiler/src/prog8/ast/base/Errors.kt index 14ffe0e21..18dc2e0e7 100644 --- a/compiler/src/prog8/ast/base/Errors.kt +++ b/compiler/src/prog8/ast/base/Errors.kt @@ -10,13 +10,13 @@ class SyntaxError(override var message: String, val position: Position) : AstExc override fun toString() = "$position Syntax error: $message" } -class NameError(override var message: String, val position: Position) : AstException(message) { +open class NameError(override var message: String, val position: Position) : AstException(message) { override fun toString() = "$position Name error: $message" } -open class ExpressionError(message: String, val position: Position) : AstException(message) { +class ExpressionError(message: String, val position: Position) : AstException(message) { override fun toString() = "$position Error: $message" } class UndefinedSymbolError(symbol: IdentifierReference) - : ExpressionError("undefined symbol: ${symbol.nameInSource.joinToString(".")}", symbol.position) + : NameError("undefined symbol: ${symbol.nameInSource.joinToString(".")}", symbol.position) diff --git a/compiler/src/prog8/ast/expressions/AstExpressions.kt b/compiler/src/prog8/ast/expressions/AstExpressions.kt index c12202af5..7646eed5c 100644 --- a/compiler/src/prog8/ast/expressions/AstExpressions.kt +++ b/compiler/src/prog8/ast/expressions/AstExpressions.kt @@ -326,7 +326,7 @@ class NumericLiteralValue(val type: DataType, // only numerical types allowed operator fun compareTo(other: NumericLiteralValue): Int = number.toDouble().compareTo(other.number.toDouble()) - fun cast(targettype: DataType): NumericLiteralValue? { + fun cast(targettype: DataType): NumericLiteralValue { if(type==targettype) return this val numval = number.toDouble() @@ -381,7 +381,7 @@ class NumericLiteralValue(val type: DataType, // only numerical types allowed } else -> {} } - return null // invalid type conversion from $this to $targettype + throw ExpressionError("can't cast $type into $targettype", position) } } diff --git a/compiler/src/prog8/ast/processing/AstChecker.kt b/compiler/src/prog8/ast/processing/AstChecker.kt index 91dd1bdbc..c347df79f 100644 --- a/compiler/src/prog8/ast/processing/AstChecker.kt +++ b/compiler/src/prog8/ast/processing/AstChecker.kt @@ -848,9 +848,12 @@ internal class AstChecker(private val program: Program, if(args.size!=func.parameters.size) checkResult.add(SyntaxError("invalid number of arguments", position)) else { + val paramTypesForAddressOf = PassByReferenceDatatypes + DataType.UWORD for (arg in args.withIndex().zip(func.parameters)) { val argDt=arg.first.value.inferType(program) - if(argDt!=null && !(argDt isAssignableTo arg.second.possibleDatatypes)) { + if (argDt != null + && !(argDt isAssignableTo arg.second.possibleDatatypes) + && (argDt != DataType.UWORD || arg.second.possibleDatatypes.intersect(paramTypesForAddressOf).isEmpty())) { checkResult.add(ExpressionError("builtin function '${target.name}' argument ${arg.first.index + 1} has invalid type $argDt, expected ${arg.second.possibleDatatypes}", position)) } } @@ -867,6 +870,14 @@ internal class AstChecker(private val program: Program, else if(dt1 !in NumericDatatypes) checkResult.add(ExpressionError("swap requires args of numerical type", position)) } + else if(target.name=="all" || target.name=="any") { + if((args[0] as? AddressOf)?.identifier?.targetVarDecl(program.namespace)?.datatype in StringDatatypes) { + checkResult.add(ExpressionError("any/all on a string is useless (is always true unless the string is empty)", position)) + } + if(args[0].inferType(program) in StringDatatypes) { + checkResult.add(ExpressionError("any/all on a string is useless (is always true unless the string is empty)", position)) + } + } } } else if(target is Subroutine) { if(args.size!=target.parameters.size) diff --git a/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt b/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt index 55cfdf066..229eec62b 100644 --- a/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt +++ b/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt @@ -381,7 +381,14 @@ internal fun fixupArrayDatatype(array: ReferenceLiteralValue, vardecl: VarDecl, val arrayDt = array.type if(arrayDt!=vardecl.datatype) { // fix the datatype of the array (also on the heap) to match the vardecl - val litval2 = array.cast(vardecl.datatype)!! + val litval2 = + try { + array.cast(vardecl.datatype)!! + } catch(x: ExpressionError) { + // couldn't cast permanently. + // instead, simply adjust the array type and trust the AstChecker to report the exact error + ReferenceLiteralValue(vardecl.datatype, null, array.array, array.heapId, array.position) + } vardecl.value = litval2 litval2.linkParents(vardecl) litval2.addToHeap(heap) // TODO is the previous array discarded from the resulting asm code? diff --git a/compiler/src/prog8/ast/processing/VarInitValueAndAddressOfCreator.kt b/compiler/src/prog8/ast/processing/VarInitValueAndAddressOfCreator.kt index 653e33a9f..f84bd17ef 100644 --- a/compiler/src/prog8/ast/processing/VarInitValueAndAddressOfCreator.kt +++ b/compiler/src/prog8/ast/processing/VarInitValueAndAddressOfCreator.kt @@ -135,10 +135,11 @@ internal class VarInitValueAndAddressOfCreator(private val program: Program): IA } private fun addAddressOfExprIfNeededForBuiltinFuncs(signature: FunctionSignature, args: MutableList, parent: Statement) { + // val paramTypesForAddressOf = PassByReferenceDatatypes + DataType.UWORD for(arg in args.withIndex().zip(signature.parameters)) { val argvalue = arg.first.value val argDt = argvalue.inferType(program) - if(DataType.UWORD in arg.second.possibleDatatypes && argDt in PassByReferenceDatatypes) { + if(argDt in PassByReferenceDatatypes && DataType.UWORD in arg.second.possibleDatatypes) { if(argvalue !is IdentifierReference) throw CompilerException("pass-by-reference parameter isn't an identifier? $argvalue") val addrOf = AddressOf(argvalue, argvalue.position) diff --git a/compiler/src/prog8/compiler/target/c64/codegen2/BuiltinFunctionsAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen2/BuiltinFunctionsAsmGen.kt index 8b1044758..0c6e7a396 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen2/BuiltinFunctionsAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen2/BuiltinFunctionsAsmGen.kt @@ -11,6 +11,7 @@ import prog8.ast.statements.AssignTarget import prog8.ast.statements.FunctionCallStatement import prog8.compiler.CompilationOptions import prog8.compiler.Zeropage +import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_HEX import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_PLUS1_HEX import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_HEX import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_PLUS1_HEX @@ -79,7 +80,18 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, asmgen.assignFromEvalResult(firstTarget) asmgen.assignFromEvalResult(secondTarget) } - // TODO: any(f), all(f), max(f), min(f), sum(f) + "strlen" -> { + val identifierName = asmgen.asmIdentifierName(fcall.arglist[0] as IdentifierReference) + asmgen.out(""" + lda #<$identifierName + sta $ESTACK_LO_HEX,x + lda #>$identifierName + sta $ESTACK_HI_HEX,x + dex + jsr prog8_lib.func_strlen + """) + } + // TODO: any(f), all(f), max(f), min(f), sum(f), avg(f) "sin", "cos", "tan", "atan", "ln", "log2", "sqrt", "rad", "deg", "round", "floor", "ceil", diff --git a/examples/test.p8 b/examples/test.p8 index 922317925..58a082749 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,17 +1,47 @@ %import c64lib %import c64utils +%import c64flt +%zeropage dontuse main { sub start() { - A = testsub(33) - } + byte[] barr = [-100, 0, 99, -122, 22] + ubyte[] ubarr = [100, 0, 99, 199, 22] + word[] warr = [-1000, 0, 999, -4444, 222] + uword[] uwarr = [1000, 0, 222, 4444, 999] + float[] farr = [-1000.1, 0, 999.9, -4444.4, 222.2] + str name = "irmen" + ubyte ub + byte bb + word ww + uword uw + float ff - asmsub testsub(ubyte foo @stack) -> ubyte @stack { - %asm {{ - Y=44 - rts - }} - } + ; LEN/STRLEN + ubyte length = len(name) + if(length!=5) c64scr.print("error len1\n") + length = len(uwarr) + if(length!=5) c64scr.print("error len2\n") + length=strlen(name) + if(length!=5) c64scr.print("error strlen1\n") + name[3] = 0 + length=strlen(name) + if(length!=3) c64scr.print("error strlen2\n") + ; MAX +; ub = max(ubarr) +; bb = max(barr) +; ww = max(warr) +; uw = max(uwarr) +; ff = max(farr) + +; word ww = sum(barr) +; uword uw = sum(ubarr) +; ww = sum(warr) +; uw = sum(uwarr) +; float ff = sum(farr) + + c64scr.print("\nyou should see no errors above.") + } }