diff --git a/src/main/scala/millfork/compiler/AbstractStatementCompiler.scala b/src/main/scala/millfork/compiler/AbstractStatementCompiler.scala index fa37c60c..0e04f37f 100644 --- a/src/main/scala/millfork/compiler/AbstractStatementCompiler.scala +++ b/src/main/scala/millfork/compiler/AbstractStatementCompiler.scala @@ -126,20 +126,14 @@ abstract class AbstractStatementCompiler[T <: AbstractCode] { ExpressionStatement(FunctionCallExpression("+=", List(vex, one)).pos(p)).pos(p) } else { Assignment(vex, FunctionCallExpression(indexType.name, List( - SumExpression(List( - false -> FunctionCallExpression("byte", List(vex)).pos(p), - false -> LiteralExpression(1,1).pos(p), - ), decimal = false).pos(p) + FunctionCallExpression("byte", List(vex)).pos(p) #+# 1 )).pos(p)).pos(p) } val decrement = if (arithmetic) { ExpressionStatement(FunctionCallExpression("-=", List(vex, one)).pos(p)).pos(p) } else { Assignment(vex, FunctionCallExpression(indexType.name, List( - SumExpression(List( - false -> FunctionCallExpression("byte", List(vex)).pos(p), - true -> LiteralExpression(1,1).pos(p), - ), decimal = false).pos(p) + FunctionCallExpression("byte", List(vex)).pos(p) #-# 1 )).pos(p)).pos(p) } val names = Set("", "for", f.variable) @@ -213,11 +207,7 @@ abstract class AbstractStatementCompiler[T <: AbstractCode] { Assignment( vex, FunctionCallExpression("lo", List( - SumExpression(List( - false -> f.start, - false -> LiteralExpression(1, 2).pos(p)), - decimal = false - ).pos(p) + (f.start #+# LiteralExpression(1, 2).pos(p)).pos(p) )).pos(p) ).pos(p), DoWhileStatement( @@ -272,7 +262,7 @@ abstract class AbstractStatementCompiler[T <: AbstractCode] { )) case (ForDirection.DownTo, _, _) => // TODO: smarter countdown if end is not a constant - val endMinusOne = SumExpression(List(false -> f.end, true -> LiteralExpression(1, 1)), decimal = false).pos(p) + val endMinusOne = (f.end #-# 1).pos(p) compile(ctx, List( Assignment(vex, f.start).pos(p), IfStatement( diff --git a/src/main/scala/millfork/compiler/AbstractStatementPreprocessor.scala b/src/main/scala/millfork/compiler/AbstractStatementPreprocessor.scala index bd8c1389..7458b0e6 100644 --- a/src/main/scala/millfork/compiler/AbstractStatementPreprocessor.scala +++ b/src/main/scala/millfork/compiler/AbstractStatementPreprocessor.scala @@ -130,10 +130,10 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte case Assignment(target:IndexedExpression, arg) if isWordPointy(target.name) => cv = search(arg, cv) cv = search(target, cv) - Assignment(DerefExpression(SumExpression(List( - false -> FunctionCallExpression("pointer", List(VariableExpression(target.name).pos(pos))).pos(pos), - false -> FunctionCallExpression("<<", List(optimizeExpr(target.index, cv), LiteralExpression(1, 1))).pos(pos) - ), decimal = false), 0, env.getPointy(target.name).elementType).pos(pos), optimizeExpr(arg, cv)).pos(pos) -> cv + Assignment(DerefExpression( + FunctionCallExpression("pointer", List(VariableExpression(target.name).pos(pos))).pos(pos) #+# + FunctionCallExpression("<<", List(optimizeExpr(target.index, cv), LiteralExpression(1, 1))).pos(pos), + 0, env.getPointy(target.name).elementType).pos(pos), optimizeExpr(arg, cv)).pos(pos) -> cv case Assignment(target:IndexedExpression, arg) => cv = search(arg, cv) cv = search(target, cv) @@ -340,10 +340,9 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte } } // TODO: re-cast pointer type - DerefExpression(("pointer." + targetType.name) <| SumExpression(List( - false -> result, - false -> optimizeExpr(scaledIndex, Map()) - ), decimal = false), 0, targetType) + DerefExpression(("pointer." + targetType.name) <| ( + result #+# optimizeExpr(scaledIndex, Map()) + ), 0, targetType) } case _ => ctx.log.error("Not a pointer type on the left-hand side of `[`", pos) @@ -361,10 +360,9 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte case DerefExpression(inner, 0, _) => optimizeExpr(inner, currentVarValues).pos(pos) case DerefExpression(inner, offset, targetType) => - ("pointer." + targetType.name) <| SumExpression(List( - false -> ("pointer" <| optimizeExpr(inner, currentVarValues).pos(pos)), - false -> LiteralExpression(offset, 2) - ), decimal = false) + ("pointer." + targetType.name) <| ( + ("pointer" <| optimizeExpr(inner, currentVarValues).pos(pos)) #+# LiteralExpression(offset, 2) + ) case IndexedExpression(name, index) => ctx.log.fatal("Oops!") case _ => @@ -401,25 +399,19 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte case 0 => DerefExpression(inner, fieldOffset, fieldType) case 1 => - ("pointer." + fieldType.name) <| SumExpression(List( - false -> ("pointer" <| inner), - false -> LiteralExpression(fieldOffset, 2) - ), decimal = false) + ("pointer." + fieldType.name) <| ( + ("pointer" <| inner) #+# LiteralExpression(fieldOffset, 2) + ) case 2 => - SumExpression(List( - false -> ("pointer" <| inner), - false -> LiteralExpression(fieldOffset, 2) - ), decimal = false) + ("pointer" <| inner) #+# LiteralExpression(fieldOffset, 2) case 10 => - "lo" <| SumExpression(List( - false -> ("pointer" <| inner), - false -> LiteralExpression(fieldOffset, 2) - ), decimal = false) + "lo" <| ( + ("pointer" <| inner) #+# LiteralExpression(fieldOffset, 2) + ) case 11 => - "hi" <| SumExpression(List( - false -> ("pointer" <| inner), - false -> LiteralExpression(fieldOffset, 2) - ), decimal = false) + "hi" <| ( + ("pointer" <| inner) #+# LiteralExpression(fieldOffset, 2) + ) case _ => throw new IllegalStateException } } @@ -497,10 +489,9 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte case _ => "*" <| ("word" <| index, LiteralExpression(targetType.size, 1)) } } - DerefExpression(SumExpression(List( - false -> ("pointer" <| VariableExpression(name).pos(pos)), - false -> optimizeExpr(scaledIndex, Map()) - ), decimal = false), 0, pointy.elementType).pos(pos) + DerefExpression( + ("pointer" <| VariableExpression(name).pos(pos)) #+# optimizeExpr(scaledIndex, Map()), + 0, pointy.elementType).pos(pos) } case _ => expr // TODO } diff --git a/src/main/scala/millfork/compiler/mos/MosBulkMemoryOperations.scala b/src/main/scala/millfork/compiler/mos/MosBulkMemoryOperations.scala index 708c7ac0..cfee2784 100644 --- a/src/main/scala/millfork/compiler/mos/MosBulkMemoryOperations.scala +++ b/src/main/scala/millfork/compiler/mos/MosBulkMemoryOperations.scala @@ -21,11 +21,11 @@ object MosBulkMemoryOperations { ctx.env.getPointy(target.name) val sizeExpr = f.direction match { case ForDirection.DownTo => - SumExpression(List(false -> f.start, true -> f.end, false -> LiteralExpression(1, 1)), decimal = false) + f.start #-# f.end #+# 1 case ForDirection.To | ForDirection.ParallelTo => - SumExpression(List(false -> f.end, true -> f.start, false -> LiteralExpression(1, 1)), decimal = false) + f.end #-# f.start #+# 1 case ForDirection.Until | ForDirection.ParallelUntil => - SumExpression(List(false -> f.end, true -> f.start), decimal = false) + f.end #-# f.start } val reg = ctx.env.get[VariableInMemory]("__reg.loword") val w = ctx.env.get[Type]("word") @@ -37,7 +37,7 @@ object MosBulkMemoryOperations { val loadReg = if (useTwoRegs) { import millfork.assembly.mos.AddrMode._ - val first = MosExpressionCompiler.compile(ctx, SumExpression(List(false -> f.start, false -> target.index), decimal = false), Some(w -> reg), BranchSpec.None) + val first = MosExpressionCompiler.compile(ctx, f.start #+# target.index, Some(w -> reg), BranchSpec.None) first ++ (first match { case List(AssemblyLine0(LDA, Immediate, l), AssemblyLine0(LDA, ZeroPage, r0), AssemblyLine0(LDA, Immediate, h), AssemblyLine0(LDA, ZeroPage, r1)) if (r1-r0).quickSimplify.isProvably(1) => @@ -57,7 +57,7 @@ object MosBulkMemoryOperations { AssemblyLine.immediate(ADC, 0), AssemblyLine.zeropage(STA, reg, 3)) }) - }else MosExpressionCompiler.compile(ctx, SumExpression(List(false -> f.start, false -> target.index), decimal = false), Some(w -> reg), BranchSpec.None) + } else MosExpressionCompiler.compile(ctx, f.start #+# target.index, Some(w -> reg), BranchSpec.None) val loadSource = MosExpressionCompiler.compileToA(ctx, source) val loadAll = if (MosExpressionCompiler.changesZpreg(loadSource, 0) || MosExpressionCompiler.changesZpreg(loadSource, 1)) { @@ -193,7 +193,7 @@ object MosBulkMemoryOperations { val (prepareZpreg, addrMode, parameter) = env.getPointy(targetExpression.name) match { case _: VariablePointy | _:StackVariablePointy => val offsetExpr = GeneratedConstantExpression(offset, env.get[Type]("word")) - val pz = MosExpressionCompiler.compileToZReg(ctx, SumExpression(List(false -> VariableExpression(targetExpression.name), false -> offsetExpr), decimal = false)) + val pz = MosExpressionCompiler.compileToZReg(ctx, VariableExpression(targetExpression.name) #+# offsetExpr) (pz, AddrMode.IndexedY, reg.toAddress) case c: ConstantPointy => if (indexVariable.typ.size == 1) (Nil, AddrMode.AbsoluteX, c.value) diff --git a/src/main/scala/millfork/compiler/mos/PseudoregisterBuiltIns.scala b/src/main/scala/millfork/compiler/mos/PseudoregisterBuiltIns.scala index a5e2221e..5b1f92b1 100644 --- a/src/main/scala/millfork/compiler/mos/PseudoregisterBuiltIns.scala +++ b/src/main/scala/millfork/compiler/mos/PseudoregisterBuiltIns.scala @@ -686,7 +686,6 @@ object PseudoregisterBuiltIns { } def doesMemoryAccessOverlap(l1: List[AssemblyLine], l2: List[AssemblyLine]): Boolean = { - print() for{ a1 <- l1 if a1.addrMode != Immediate && a1.addrMode != Implied diff --git a/src/main/scala/millfork/compiler/z80/Z80BulkMemoryOperations.scala b/src/main/scala/millfork/compiler/z80/Z80BulkMemoryOperations.scala index 58fb9dfb..a5a5e56c 100644 --- a/src/main/scala/millfork/compiler/z80/Z80BulkMemoryOperations.scala +++ b/src/main/scala/millfork/compiler/z80/Z80BulkMemoryOperations.scala @@ -22,7 +22,7 @@ object Z80BulkMemoryOperations { def compileMemcpy(ctx: CompilationContext, target: IndexedExpression, source: IndexedExpression, f: ForStatement): List[ZLine] = { val sourceOffset = removeVariableOnce(f.variable, source.index).getOrElse(return compileForStatement(ctx, f)._1) if (!sourceOffset.isPure) return compileForStatement(ctx, f)._1 - val sourceIndexExpression = SumExpression(List(false -> sourceOffset, false -> f.start), decimal = false) + val sourceIndexExpression = sourceOffset #+# f.start val calculateSource = Z80ExpressionCompiler.calculateAddressToHL(ctx, IndexedExpression(source.name, sourceIndexExpression).pos(source.position), forWriting = false) compileMemoryBulk(ctx, target, f, useDEForTarget = true, @@ -47,18 +47,18 @@ object Z80BulkMemoryOperations { def compileForZ80(targetOffset: Expression): List[ZLine] = { val targetIndexExpression = f.direction match { - case ForDirection.DownTo => SumExpression(List(false -> targetOffset, false -> f.end), decimal = false) - case _ => SumExpression(List(false -> targetOffset, false -> f.start), decimal = false) + case ForDirection.DownTo => targetOffset #+# f.end + case _ => targetOffset #+# f.start } val array = if (target.name != f.variable) target.name else "$0000" val calculateAddress = Z80ExpressionCompiler.calculateAddressToHL(ctx, IndexedExpression(array, targetIndexExpression).pos(targetIndexExpression.position), forWriting = true) val calculateSize = f.direction match { case ForDirection.DownTo => - Z80ExpressionCompiler.stashHLIfChanged(ctx, Z80ExpressionCompiler.compileToBC(ctx, SumExpression(List(false -> f.start, true -> f.end), decimal = false))) + Z80ExpressionCompiler.stashHLIfChanged(ctx, Z80ExpressionCompiler.compileToBC(ctx, f.start #-# f.end)) case ForDirection.To | ForDirection.ParallelTo => - Z80ExpressionCompiler.stashHLIfChanged(ctx, Z80ExpressionCompiler.compileToBC(ctx, SumExpression(List(false -> f.end, true -> f.start), decimal = false))) + Z80ExpressionCompiler.stashHLIfChanged(ctx, Z80ExpressionCompiler.compileToBC(ctx, f.end #-# f.start)) case ForDirection.Until | ForDirection.ParallelUntil => - Z80ExpressionCompiler.stashHLIfChanged(ctx, Z80ExpressionCompiler.compileToBC(ctx, SumExpression(List(false -> f.end, true -> f.start, true -> LiteralExpression(1, 1)), decimal = false))) + Z80ExpressionCompiler.stashHLIfChanged(ctx, Z80ExpressionCompiler.compileToBC(ctx, f.end #-# f.start #-# 1)) } val (incOp, ldOp) = f.direction match { case ForDirection.DownTo => DEC_16 -> LDDR @@ -134,14 +134,14 @@ object Z80BulkMemoryOperations { val target1Offset = removeVariableOnce(f.variable, target2.index).getOrElse(return compileForStatement(ctx, f)._1) val target2Offset = removeVariableOnce(f.variable, target2.index).getOrElse(return compileForStatement(ctx, f)._1) val target1IndexExpression = if (c.countDownDespiteSyntax) { - SumExpression(List(false -> target1Offset, false -> f.end, true -> LiteralExpression(1, 1)), decimal = false) + target1Offset #+# f.end #-# 1 } else { - SumExpression(List(false -> target1Offset, false -> f.start), decimal = false) + target1Offset #+# f.start } val target2IndexExpression = if (c.countDownDespiteSyntax) { - SumExpression(List(false -> target2Offset, false -> f.end, true -> LiteralExpression(1, 1)), decimal = false) + target2Offset #+# f.end #-# 1 } else { - SumExpression(List(false -> target2Offset, false -> f.start), decimal = false) + target2Offset #+# f.start } val fused = target1.name == target2.name && ((ctx.env.eval(target1Offset), ctx.env.eval(target2Offset)) match { case (Some(a), Some(b)) => a == b @@ -209,7 +209,7 @@ object Z80BulkMemoryOperations { val countDownDespiteSyntax = f.direction == ForDirection.ParallelTo || f.direction == ForDirection.ParallelUntil val initC = if (useC) Z80ExpressionCompiler.compile8BitTo(ctx, f.direction match { case ForDirection.ParallelTo => f.end - case ForDirection.ParallelUntil => SumExpression(List(false -> f.end, true -> LiteralExpression(1, 1)), decimal = false) + case ForDirection.ParallelUntil => f.end #-# 1 case _ => f.start }, ZRegister.C) else Nil val nextC = if (useC) List(ZLine.register(if (countDown) DEC else INC, ZRegister.C)) else Nil @@ -397,7 +397,6 @@ object Z80BulkMemoryOperations { extraAddressCalculations: Boolean => (List[ZLine], List[ZLine]), loadA: ZOpcode.Value => List[ZLine], z80Bulk: Boolean => Option[ZOpcode.Value]): List[ZLine] = { - val one = LiteralExpression(1, 1) val targetOffset = removeVariableOnce(f.variable, target.index).getOrElse(return compileForStatement(ctx, f)._1) if (!targetOffset.isPure) return compileForStatement(ctx, f)._1 val indexVariableSize = ctx.env.get[Variable](f.variable).typ.size @@ -406,13 +405,13 @@ object Z80BulkMemoryOperations { val decreasing = f.direction == ForDirection.DownTo || decreasingDespiteSyntax val plusOne = f.direction == ForDirection.To || f.direction == ForDirection.DownTo || f.direction == ForDirection.ParallelTo val byteCountExpression = - if (f.direction == ForDirection.DownTo) SumExpression(List(false -> f.start, false -> one, true -> f.end), decimal = false) - else if (plusOne) SumExpression(List(false -> f.end, false -> one, true -> f.start), decimal = false) - else SumExpression(List(false -> f.end, true -> f.start), decimal = false) + if (f.direction == ForDirection.DownTo) f.start #+# 1 #-# f.end + else if (plusOne) f.end #+# 1 #-# f.start + else f.end #-# f.start val targetIndexExpression = if (decreasingDespiteSyntax) { - SumExpression(List(false -> targetOffset, false -> f.end, true -> one), decimal = false) + targetOffset #+# f.end #-# 1 } else { - SumExpression(List(false -> targetOffset, false -> f.start), decimal = false) + targetOffset #+# f.start } val ldr = z80Bulk(decreasing) val smallCount = indexVariableSize == 1 && (ldr.isEmpty || !ctx.options.flag(CompilationFlag.EmitZ80Opcodes)) diff --git a/src/main/scala/millfork/compiler/z80/Z80ExpressionCompiler.scala b/src/main/scala/millfork/compiler/z80/Z80ExpressionCompiler.scala index 0f3830d6..b5291826 100644 --- a/src/main/scala/millfork/compiler/z80/Z80ExpressionCompiler.scala +++ b/src/main/scala/millfork/compiler/z80/Z80ExpressionCompiler.scala @@ -752,11 +752,16 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] { case List(fp, param) => getExpressionType(ctx, fp) match { case FunctionPointerType(_, _, _, Some(pt), Some(v)) => - if (pt.size != 1) { + if (pt.size > 2 || pt.size < 1) { ctx.log.error("Invalid parameter type", param.position) compileToHL(ctx, fp) ++ compile(ctx, param, ZExpressionTarget.NOTHING) } else if (getExpressionType(ctx, param).isAssignableTo(pt)) { - compileToDE(ctx, fp) ++ stashDEIfChanged(ctx, compileToA(ctx, param)) :+ callLine + pt.size match { + case 1 => + compileToDE(ctx, fp) ++ stashDEIfChanged(ctx, compileToA(ctx, param)) :+ callLine + case 2 => + compileToDE(ctx, fp) ++ stashDEIfChanged(ctx, compileToHL(ctx, param)) :+ callLine + } } else { ctx.log.error("Invalid parameter type", param.position) compileToHL(ctx, fp) ++ compile(ctx, param, ZExpressionTarget.NOTHING) diff --git a/src/main/scala/millfork/compiler/z80/Z80Shifting.scala b/src/main/scala/millfork/compiler/z80/Z80Shifting.scala index b1d57caa..e807b03f 100644 --- a/src/main/scala/millfork/compiler/z80/Z80Shifting.scala +++ b/src/main/scala/millfork/compiler/z80/Z80Shifting.scala @@ -15,7 +15,7 @@ import scala.collection.GenTraversableOnce object Z80Shifting { private def calculateIterationCountPlus1(ctx: CompilationContext, rhs: Expression) = { - Z80ExpressionCompiler.compile8BitTo(ctx, SumExpression(List(false -> rhs, false -> LiteralExpression(1, 1)), decimal = false), ZRegister.B) + Z80ExpressionCompiler.compile8BitTo(ctx, rhs #+# 1, ZRegister.B) } private def fixAfterShiftIfNeeded(extendedOps: Boolean, left: Boolean, i: Long): List[ZLine] = diff --git a/src/main/scala/millfork/compiler/z80/Z80StatementPreprocessor.scala b/src/main/scala/millfork/compiler/z80/Z80StatementPreprocessor.scala index ef495154..edb59305 100644 --- a/src/main/scala/millfork/compiler/z80/Z80StatementPreprocessor.scala +++ b/src/main/scala/millfork/compiler/z80/Z80StatementPreprocessor.scala @@ -131,7 +131,7 @@ class Z80StatementPreprocessor(ctx: CompilationContext, statements: List[Executa Assignment( VariableExpression(newVariables(name, f.variable)), FunctionCallExpression("pointer", List( - SumExpression(List(false -> VariableExpression(name + ".addr"), false -> optStart), decimal = false) + VariableExpression(name + ".addr") #+# optStart ))) }).toList :+ ForStatement(f.variable, optStart, optimizeExpr(f.end, Map()), newDirection, optimizeStmts(newBody, Map())._1), Nil diff --git a/src/main/scala/millfork/env/Environment.scala b/src/main/scala/millfork/env/Environment.scala index f3206ac3..2d912b62 100644 --- a/src/main/scala/millfork/env/Environment.scala +++ b/src/main/scala/millfork/env/Environment.scala @@ -1168,7 +1168,6 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa case _ => ??? } if (maybeGet[Thing](name).isEmpty) { - println("registering text literal") root.registerArray(ArrayDeclarationStatement(name, None, None, "byte", None, const = true, Some(LiteralContents(literal.characters)), None, options.isBigEndian).pos(literal.position), options) } name diff --git a/src/main/scala/millfork/node/Node.scala b/src/main/scala/millfork/node/Node.scala index f0bd6086..5ff0e073 100644 --- a/src/main/scala/millfork/node/Node.scala +++ b/src/main/scala/millfork/node/Node.scala @@ -35,6 +35,15 @@ sealed trait Expression extends Node { def getPointies: Seq[String] def isPure: Boolean def getAllIdentifiers: Set[String] + + def #+#(smallInt: Int): Expression = (this #+# LiteralExpression(smallInt, 1).pos(this.position)).pos(this.position) + def #+#(that: Expression): Expression = that match { + case SumExpression(params, false) => SumExpression((false -> this) :: params, decimal = false) + case _ => SumExpression(List(false -> this, false -> that), decimal = false) + } + def #-#(smallInt: Int): Expression = (this #-# LiteralExpression(smallInt, 1).pos(this.position)).pos(this.position) + def #-#(that: Expression): Expression = SumExpression(List(false -> this, true -> that), decimal = false) + @transient var typeCache: Type = _ } @@ -99,7 +108,7 @@ case class SeparateBytesExpression(hi: Expression, lo: Expression) extends LhsEx SeparateBytesExpression( hi.replaceVariable(variable, actualParam), lo.replaceVariable(variable, actualParam)).pos(position) - override def replaceIndexedExpression(predicate: IndexedExpression => Boolean, replacement: IndexedExpression => Expression): Expression = + override def replaceIndexedExpression(predicate: IndexedExpression => Boolean, replacement: IndexedExpression => Expression): Expression = SeparateBytesExpression( hi.replaceIndexedExpression(predicate, replacement), lo.replaceIndexedExpression(predicate, replacement)).pos(position) @@ -112,12 +121,22 @@ case class SeparateBytesExpression(hi: Expression, lo: Expression) extends LhsEx case class SumExpression(expressions: List[(Boolean, Expression)], decimal: Boolean) extends Expression { override def replaceVariable(variable: String, actualParam: Expression): Expression = SumExpression(expressions.map { case (n, e) => n -> e.replaceVariable(variable, actualParam) }, decimal).pos(position) - override def replaceIndexedExpression(predicate: IndexedExpression => Boolean, replacement: IndexedExpression => Expression): Expression = + override def replaceIndexedExpression(predicate: IndexedExpression => Boolean, replacement: IndexedExpression => Expression): Expression = SumExpression(expressions.map { case (n, e) => n -> e.replaceIndexedExpression(predicate, replacement) }, decimal).pos(position) override def containsVariable(variable: String): Boolean = expressions.exists(_._2.containsVariable(variable)) override def getPointies: Seq[String] = expressions.flatMap(_._2.getPointies) override def isPure: Boolean = expressions.forall(_._2.isPure) override def getAllIdentifiers: Set[String] = expressions.map(_._2.getAllIdentifiers).fold(Set[String]())(_ ++ _) + + override def #+#(that: Expression): Expression = + if (decimal) super.#+#(that) + else that match { + case SumExpression(params, false) => SumExpression(expressions ++ params, decimal = false) + case _ => SumExpression(expressions :+ (false -> that), decimal = false) + } + override def #-#(that: Expression): Expression = + if (decimal) super.#-#(that) + else SumExpression(expressions :+ (true -> that), decimal = false) } case class FunctionCallExpression(functionName: String, expressions: List[Expression]) extends Expression {