string literal concatenation and repeating added again

This commit is contained in:
Irmen de Jong 2019-07-16 23:34:43 +02:00
parent c0e83ef8df
commit 07d8caf884
6 changed files with 75 additions and 46 deletions

View File

@ -169,7 +169,7 @@ class BinaryExpression(var left: IExpression, var operator: String, var right: I
DataType.UWORD -> Pair(DataType.UWORD, left) DataType.UWORD -> Pair(DataType.UWORD, left)
DataType.WORD -> Pair(DataType.WORD, left) DataType.WORD -> Pair(DataType.WORD, left)
DataType.FLOAT -> Pair(DataType.FLOAT, left) DataType.FLOAT -> Pair(DataType.FLOAT, left)
else -> throw FatalAstException("non-numeric datatype $rightDt") else -> Pair(leftDt, null) // non-numeric datatype
} }
} }
DataType.BYTE -> { DataType.BYTE -> {
@ -179,7 +179,7 @@ class BinaryExpression(var left: IExpression, var operator: String, var right: I
DataType.UWORD -> Pair(DataType.WORD, left) DataType.UWORD -> Pair(DataType.WORD, left)
DataType.WORD -> Pair(DataType.WORD, left) DataType.WORD -> Pair(DataType.WORD, left)
DataType.FLOAT -> Pair(DataType.FLOAT, left) DataType.FLOAT -> Pair(DataType.FLOAT, left)
else -> throw FatalAstException("non-numeric datatype $rightDt") else -> Pair(leftDt, null) // non-numeric datatype
} }
} }
DataType.UWORD -> { DataType.UWORD -> {
@ -189,7 +189,7 @@ class BinaryExpression(var left: IExpression, var operator: String, var right: I
DataType.UWORD -> Pair(DataType.UWORD, null) DataType.UWORD -> Pair(DataType.UWORD, null)
DataType.WORD -> Pair(DataType.WORD, left) DataType.WORD -> Pair(DataType.WORD, left)
DataType.FLOAT -> Pair(DataType.FLOAT, left) DataType.FLOAT -> Pair(DataType.FLOAT, left)
else -> throw FatalAstException("non-numeric datatype $rightDt") else -> Pair(leftDt, null) // non-numeric datatype
} }
} }
DataType.WORD -> { DataType.WORD -> {
@ -199,13 +199,13 @@ class BinaryExpression(var left: IExpression, var operator: String, var right: I
DataType.UWORD -> Pair(DataType.WORD, right) DataType.UWORD -> Pair(DataType.WORD, right)
DataType.WORD -> Pair(DataType.WORD, null) DataType.WORD -> Pair(DataType.WORD, null)
DataType.FLOAT -> Pair(DataType.FLOAT, left) DataType.FLOAT -> Pair(DataType.FLOAT, left)
else -> throw FatalAstException("non-numeric datatype $rightDt") else -> Pair(leftDt, null) // non-numeric datatype
} }
} }
DataType.FLOAT -> { DataType.FLOAT -> {
Pair(DataType.FLOAT, right) Pair(DataType.FLOAT, right)
} }
else -> throw FatalAstException("non-numeric datatype $leftDt") else -> Pair(leftDt, null) // non-numeric datatype
} }
} }
} }
@ -700,7 +700,12 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
fun heapId(namespace: INameScope): Int { fun heapId(namespace: INameScope): Int {
val node = namespace.lookup(nameInSource, this) ?: throw UndefinedSymbolError(this) val node = namespace.lookup(nameInSource, this) ?: throw UndefinedSymbolError(this)
return ((node as? VarDecl)?.value as? ReferenceLiteralValue)?.heapId ?: throw FatalAstException("identifier is not on the heap: $this") val value = (node as? VarDecl)?.value ?: throw FatalAstException("requires a reference value")
return when (value) {
is IdentifierReference -> value.heapId(namespace)
is ReferenceLiteralValue -> value.heapId ?: throw FatalAstException("refLv is not on the heap: $value")
else -> throw FatalAstException("requires a reference value")
}
} }
} }

View File

@ -222,20 +222,24 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
override fun visit(refLiteral: ReferenceLiteralValue): IExpression { override fun visit(refLiteral: ReferenceLiteralValue): IExpression {
if(refLiteral.parent !is VarDecl) { if(refLiteral.parent !is VarDecl) {
// a referencetype literal value that's not declared as a variable return makeIdentifierFromRefLv(refLiteral)
// we need to introduce an auto-generated variable for this to be able to refer to the value
refLiteral.addToHeap(program.heap)
val variable = VarDecl.createAuto(refLiteral, program.heap)
addVarDecl(refLiteral.definingScope(), variable)
// replace the reference literal by a identfier reference
val identifier = IdentifierReference(listOf(variable.name), variable.position)
identifier.parent = refLiteral.parent
// TODO anonymousVariablesFromHeap[variable.name] = Pair(refLiteral, variable)
return identifier
} }
return super.visit(refLiteral) return super.visit(refLiteral)
} }
private fun makeIdentifierFromRefLv(refLiteral: ReferenceLiteralValue): IdentifierReference {
// a referencetype literal value that's not declared as a variable
// we need to introduce an auto-generated variable for this to be able to refer to the value
refLiteral.addToHeap(program.heap)
val variable = VarDecl.createAuto(refLiteral, program.heap)
addVarDecl(refLiteral.definingScope(), variable)
// replace the reference literal by a identifier reference
val identifier = IdentifierReference(listOf(variable.name), variable.position)
identifier.parent = refLiteral.parent
// TODO anonymousVariablesFromHeap[variable.name] = Pair(refLiteral, variable)
return identifier
}
override fun visit(addressOf: AddressOf): IExpression { override fun visit(addressOf: AddressOf): IExpression {
// register the scoped name of the referenced identifier // register the scoped name of the referenced identifier
val variable= addressOf.identifier.targetVarDecl(program.namespace) ?: return addressOf val variable= addressOf.identifier.targetVarDecl(program.namespace) ?: return addressOf
@ -253,6 +257,38 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
return super.visit(structDecl) return super.visit(structDecl)
} }
override fun visit(expr: BinaryExpression): IExpression {
return when {
expr.left is ReferenceLiteralValue ->
processBinaryExprWithReferenceVal(expr.left as ReferenceLiteralValue, expr.right, expr)
expr.right is ReferenceLiteralValue ->
processBinaryExprWithReferenceVal(expr.right as ReferenceLiteralValue, expr.left, expr)
else -> super.visit(expr)
}
}
private fun processBinaryExprWithReferenceVal(refLv: ReferenceLiteralValue, operand: IExpression, expr: BinaryExpression): IExpression {
// expressions on strings or arrays
if(refLv.isString) {
val constvalue = operand.constValue(program)
if(constvalue!=null) {
if (expr.operator == "*") {
// repeat a string a number of times
return ReferenceLiteralValue(refLv.inferType(program),
refLv.str!!.repeat(constvalue.number.toInt()), null, null, expr.position)
}
}
if(expr.operator == "+" && operand is ReferenceLiteralValue) {
if (operand.isString) {
// concatenate two strings
return ReferenceLiteralValue(refLv.inferType(program),
"${refLv.str}${operand.str}", null, null, expr.position)
}
}
}
return expr
}
private fun addVarDecl(scope: INameScope, variable: VarDecl) { private fun addVarDecl(scope: INameScope, variable: VarDecl) {
if(scope !in vardeclsToAdd) if(scope !in vardeclsToAdd)
vardeclsToAdd[scope] = mutableListOf() vardeclsToAdd[scope] = mutableListOf()

View File

@ -172,15 +172,6 @@ class ConstExprEvaluator {
right.type == DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() + right.number.toDouble(), left.position) right.type == DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() + right.number.toDouble(), left.position)
else -> throw ExpressionError(error, left.position) else -> throw ExpressionError(error, left.position)
} }
// TODO: string concatenation
// left.isString -> when {
// right.isString -> {
// val newStr = left.strvalue!! + right.strvalue!!
// if(newStr.length > 255) throw ExpressionError("string too long", left.position)
// NumericLiteralValue(DataType.STR, strvalue = newStr, left.position)
// }
// else -> throw ExpressionError(error, left.position)
// }
else -> throw ExpressionError(error, left.position) else -> throw ExpressionError(error, left.position)
} }
} }
@ -208,11 +199,6 @@ class ConstExprEvaluator {
left.type in IntegerDatatypes -> when { left.type in IntegerDatatypes -> when {
right.type in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt() * right.number.toInt(), left.position) right.type in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt() * right.number.toInt(), left.position)
right.type == DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() * right.number.toDouble(), left.position) right.type == DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() * right.number.toDouble(), left.position)
// TODO: string multiplication
// right.isString -> {
// if(right.strvalue!!.length * left.number.toInt() > 255) throw ExpressionError("string too long", left.position)
// NumericLiteralValue(DataType.STR, strvalue = right.strvalue.repeat(left.number.toInt()), left.position)
// }
else -> throw ExpressionError(error, left.position) else -> throw ExpressionError(error, left.position)
} }
left.type == DataType.FLOAT -> when { left.type == DataType.FLOAT -> when {

View File

@ -15,7 +15,6 @@ import kotlin.math.floor
class ConstantFolding(private val program: Program) : IAstModifyingVisitor { class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
var optimizationsDone: Int = 0 var optimizationsDone: Int = 0
var errors : MutableList<AstException> = mutableListOf() var errors : MutableList<AstException> = mutableListOf()
private val reportedErrorMessages = mutableSetOf<String>() private val reportedErrorMessages = mutableSetOf<String>()
fun addError(x: AstException) { fun addError(x: AstException) {
@ -318,6 +317,10 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
override fun visit(expr: BinaryExpression): IExpression { override fun visit(expr: BinaryExpression): IExpression {
return try { return try {
super.visit(expr) super.visit(expr)
if(expr.left is ReferenceLiteralValue || expr.right is ReferenceLiteralValue)
TODO("binexpr with reference litval")
val leftconst = expr.left.constValue(program) val leftconst = expr.left.constValue(program)
val rightconst = expr.right.constValue(program) val rightconst = expr.right.constValue(program)
@ -338,12 +341,13 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
} }
// const fold when both operands are a const // const fold when both operands are a const
val evaluator = ConstExprEvaluator()
return when { return when {
leftconst != null && rightconst != null -> { leftconst != null && rightconst != null -> {
optimizationsDone++ optimizationsDone++
val evaluator = ConstExprEvaluator()
evaluator.evaluate(leftconst, expr.operator, rightconst) evaluator.evaluate(leftconst, expr.operator, rightconst)
} }
else -> expr else -> expr
} }
} catch (ax: AstException) { } catch (ax: AstException) {

View File

@ -267,6 +267,13 @@ Strings in your source code files will be encoded (translated from ASCII/UTF-8)
PETSCII is the default choice. If you need screencodes (also called 'poke' codes) instead, PETSCII is the default choice. If you need screencodes (also called 'poke' codes) instead,
you have to use the ``str_s`` variants of the string type identifier. you have to use the ``str_s`` variants of the string type identifier.
You can concatenate two string literals using '+' (not very useful though) or repeat
a string literal a given number of times using '*'::
str string1 = "first part" + "second part"
str string2 = "hello!" * 10
.. caution:: .. caution::
It's probably best that you don't change strings after they're created. It's probably best that you don't change strings after they're created.
This is because if your program exits and is restarted (without loading it again), This is because if your program exits and is restarted (without loading it again),

View File

@ -5,22 +5,13 @@
~ main { ~ main {
float[5] flarray
byte[5] barray
uword[5] uwarray
sub start() { sub start() {
c64scr.print_uw(flarray) str bla = "asfasd" + "zzz"
c64.CHROUT('=') str bla2 = "sdfsdf" * 4
c64flt.print_f(flarray[0])
c64scr.print(bla)
c64.CHROUT('\n') c64.CHROUT('\n')
c64scr.print_uw(barray) c64scr.print(bla2)
c64.CHROUT('=')
c64scr.print_b(barray[0])
c64.CHROUT('\n')
c64scr.print_uw(uwarray)
c64.CHROUT('=')
c64scr.print_uw(uwarray[0])
c64.CHROUT('\n') c64.CHROUT('\n')
} }