From 348b3036ffd1dd5b00d8a68030ec42322df16598 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Thu, 5 May 2022 22:30:52 +0200 Subject: [PATCH] now correctly accepts "xxx" * constexpr (where constexpr is not just a single const number) --- .../optimizer/ConstantFoldingOptimizer.kt | 25 +++++++- .../compiler/astprocessing/AstChecker.kt | 5 +- .../astprocessing/AstVariousTransforms.kt | 57 ------------------- .../astprocessing/LiteralsToAutoVars.kt | 3 +- docs/source/todo.rst | 1 - examples/test.p8 | 20 ++++++- 6 files changed, 47 insertions(+), 64 deletions(-) diff --git a/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt b/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt index 4ec7cd7c6..ad8457ba7 100644 --- a/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt +++ b/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt @@ -108,9 +108,32 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() { * (X + c1) - c2 -> X + (c1-c2) */ override fun after(expr: BinaryExpression, parent: Node): Iterable { + val modifications = mutableListOf() val leftconst = expr.left.constValue(program) val rightconst = expr.right.constValue(program) - val modifications = mutableListOf() + + if(expr.left.inferType(program) istype DataType.STR) { + if(expr.operator=="+" && expr.left is StringLiteral && expr.right is StringLiteral) { + // concatenate two strings. + val leftString = expr.left as StringLiteral + val rightString = expr.right as StringLiteral + val concatenated = if(leftString.encoding==rightString.encoding) { + leftString.value + rightString.value + } else { + program.encoding.decodeString( + program.encoding.encodeString(leftString.value, leftString.encoding) + program.encoding.encodeString(rightString.value, rightString.encoding), + leftString.encoding) + } + val concatStr = StringLiteral(concatenated, leftString.encoding, expr.position) + return listOf(IAstModification.ReplaceNode(expr, concatStr, parent)) + } + else if(expr.operator=="*" && rightconst!=null && expr.left is StringLiteral) { + // mutiply a string. + val part = expr.left as StringLiteral + val newStr = StringLiteral(part.value.repeat(rightconst.number.toInt()), part.encoding, expr.position) + return listOf(IAstModification.ReplaceNode(expr, newStr, parent)) + } + } if(expr.operator=="==" && rightconst!=null) { val leftExpr = expr.left as? BinaryExpression diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index 3e2f9e21a..ca5240684 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -649,6 +649,9 @@ internal class AstChecker(private val program: Program, if(parameter==null) err("string var must be initialized with a string literal") } + + if(decl.value !is StringLiteral) + err("string var must be initialized with a string literal") } if(compilerOptions.zeropage==ZeropageType.DONTUSE && decl.zeropage == ZeropageWish.REQUIRE_ZEROPAGE) @@ -886,7 +889,7 @@ internal class AstChecker(private val program: Program, if(leftDt!=rightDt) { if(leftDt==DataType.STR && rightDt in IntegerDatatypes) { // only exception allowed: str * constvalue - if(expr.right.constValue(program)!=null) + if(expr.right.constValue(program)==null) errors.err("can only use string repeat with a constant number value", expr.left.position) } else { errors.err("left and right operands aren't the same type", expr.left.position) diff --git a/compiler/src/prog8/compiler/astprocessing/AstVariousTransforms.kt b/compiler/src/prog8/compiler/astprocessing/AstVariousTransforms.kt index 3e53609d1..09033b56d 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstVariousTransforms.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstVariousTransforms.kt @@ -5,7 +5,6 @@ import prog8.ast.Program import prog8.ast.expressions.ArrayIndexedExpression import prog8.ast.expressions.BinaryExpression import prog8.ast.expressions.DirectMemoryRead -import prog8.ast.expressions.StringLiteral import prog8.ast.statements.AssignTarget import prog8.ast.statements.DirectMemoryWrite import prog8.ast.statements.Subroutine @@ -39,65 +38,9 @@ internal class AstVariousTransforms(private val program: Program) : AstWalker() return noModifications } - override fun after(expr: BinaryExpression, parent: Node): Iterable { - val leftStr = expr.left as? StringLiteral - val rightStr = expr.right as? StringLiteral - if(expr.operator == "+") { - val concatenatedString = concatString(expr) - if(concatenatedString!=null) - return listOf(IAstModification.ReplaceNode(expr, concatenatedString, parent)) - } - else if(expr.operator == "*") { - if (leftStr!=null) { - val amount = expr.right.constValue(program) - if(amount!=null) { - val string = leftStr.value.repeat(amount.number.toInt()) - val strval = StringLiteral(string, leftStr.encoding, expr.position) - return listOf(IAstModification.ReplaceNode(expr, strval, parent)) - } - } - else if (rightStr!=null) { - val amount = expr.right.constValue(program) - if(amount!=null) { - val string = rightStr.value.repeat(amount.number.toInt()) - val strval = StringLiteral(string, rightStr.encoding, expr.position) - return listOf(IAstModification.ReplaceNode(expr, strval, parent)) - } - } - } - - return noModifications - } - override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable { return replacePointerVarIndexWithMemreadOrMemwrite(program, arrayIndexedExpression, parent) } - - private fun concatString(expr: BinaryExpression): StringLiteral? { - val rightStrval = expr.right as? StringLiteral - val leftStrval = expr.left as? StringLiteral - return when { - expr.operator!="+" -> null - expr.left is BinaryExpression && rightStrval!=null -> { - val subStrVal = concatString(expr.left as BinaryExpression) - if(subStrVal==null) - null - else - StringLiteral("${subStrVal.value}${rightStrval.value}", subStrVal.encoding, rightStrval.position) - } - expr.right is BinaryExpression && leftStrval!=null -> { - val subStrVal = concatString(expr.right as BinaryExpression) - if(subStrVal==null) - null - else - StringLiteral("${leftStrval.value}${subStrVal.value}", subStrVal.encoding, leftStrval.position) - } - leftStrval!=null && rightStrval!=null -> { - StringLiteral("${leftStrval.value}${rightStrval.value}", leftStrval.encoding, leftStrval.position) - } - else -> null - } - } } diff --git a/compiler/src/prog8/compiler/astprocessing/LiteralsToAutoVars.kt b/compiler/src/prog8/compiler/astprocessing/LiteralsToAutoVars.kt index 70f628927..e4cc8c8af 100644 --- a/compiler/src/prog8/compiler/astprocessing/LiteralsToAutoVars.kt +++ b/compiler/src/prog8/compiler/astprocessing/LiteralsToAutoVars.kt @@ -4,6 +4,7 @@ import prog8.ast.IFunctionCall import prog8.ast.Node import prog8.ast.Program import prog8.ast.expressions.ArrayLiteral +import prog8.ast.expressions.BinaryExpression import prog8.ast.expressions.IdentifierReference import prog8.ast.expressions.StringLiteral import prog8.ast.statements.VarDecl @@ -26,7 +27,7 @@ internal class LiteralsToAutoVars(private val program: Program, errors.err("compilation target doesn't support this text encoding", string.position) return noModifications } - if(string.parent !is VarDecl && string.parent !is WhenChoice) { + if(string.parent !is VarDecl && string.parent !is WhenChoice && string.parent !is BinaryExpression) { // replace the literal string by an identifier reference to the interned string val parentFunc = (string.parent as? IFunctionCall)?.target if(parentFunc!=null) { diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 9ff4391b2..6372e4a54 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -4,7 +4,6 @@ TODO For next release ^^^^^^^^^^^^^^^^ - pipe operator: allow non-unary function calls in the pipe that specify the other argument(s) in the calls. -- allow "xxx" * constexpr (where constexpr is not a number literal), now gives expression error not same type - make it possible to inline non-asmsub routines that just contain a single statement (return, functioncall, assignment) but this requires all identifiers in the inlined expression to be changed to fully scoped names. If we can do that why not perhaps also able to inline multi-line subroutines? Why would it be limited to just 1 line? Maybe to protect against code size bloat. diff --git a/examples/test.p8 b/examples/test.p8 index e1d1f45dd..6199152d7 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -7,9 +7,23 @@ main { sub start() { - ubyte xx=10 - xx = xx>9 - txt.print_ub(xx) + const ubyte times=3 + ; expected output: aaabbb aaa bbb + txt.print("aaa"+"bbb"+"ccc") + txt.nl() +; txt.print("aaa") +; txt.nl() +; txt.print("bbb") +; txt.nl() + ; expected output: xxx xxxxxx xxxxxxxxx xxx +; txt.print("xxx"*(times-2)) +; txt.nl() +; txt.print("xxx"*(times-1)) +; txt.nl() + txt.print("xxx"*times) + txt.nl() +; txt.print("xxx") +; txt.nl() sys.exit(42) ; floats.print_f(-42.42) ; float f1 = 1.2345