now correctly accepts "xxx" * constexpr (where constexpr is not just a single const number)

This commit is contained in:
Irmen de Jong 2022-05-05 22:30:52 +02:00
parent 09d3451d9d
commit 348b3036ff
6 changed files with 47 additions and 64 deletions

View File

@ -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<IAstModification> {
val modifications = mutableListOf<IAstModification>()
val leftconst = expr.left.constValue(program)
val rightconst = expr.right.constValue(program)
val modifications = mutableListOf<IAstModification>()
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

View File

@ -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)

View File

@ -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<IAstModification> {
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<IAstModification> {
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
}
}
}

View File

@ -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) {

View File

@ -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.

View File

@ -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