diff --git a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt index 4b732ccdf..5cf620232 100644 --- a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt +++ b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt @@ -171,6 +171,30 @@ internal class StatementReorderer( } override fun after(expr: BinaryExpression, parent: Node): Iterable { + // desugar chained comparisons: i < x < j ---> i leave untouched + if(expr.operator in ComparisonOperators) { + val leftBinExpr = expr.left as? BinaryExpression + val rightBinExpr = expr.right as? BinaryExpression + if(leftBinExpr!=null && !leftBinExpr.insideParentheses && leftBinExpr.operator in ComparisonOperators) { + if(!leftBinExpr.right.isSimple) { + errors.warn("possible multiple evaluation of subexpression in chained comparison, consider using a temporary variable", leftBinExpr.right.position) + } + val right = BinaryExpression(leftBinExpr.right.copy(), expr.operator, expr.right, leftBinExpr.right.position) + val desugar = BinaryExpression(leftBinExpr, "and", right, expr.position) + return listOf(IAstModification.ReplaceNode(expr, desugar, parent)) + } + else if(rightBinExpr!=null && !rightBinExpr.insideParentheses && rightBinExpr.operator in ComparisonOperators) { + if(!rightBinExpr.left.isSimple) { + errors.warn("possible multiple evaluation of subexpression in chained comparison, consider using a temporary variable", rightBinExpr.left.position) + } + val left = BinaryExpression(expr.left, expr.operator, rightBinExpr.left.copy(), rightBinExpr.left.position) + val desugar = BinaryExpression(left, "and", rightBinExpr, expr.position) + return listOf(IAstModification.ReplaceNode(expr, desugar, parent)) + } + } + + // ConstValue X --> X ConstValue // (this should be done by the ExpressionSimplifier when optimizing is enabled, // but the current assembly code generator for IF statements now also depends on it, so we do it here regardless of optimization.) diff --git a/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt b/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt index 50894bba1..2696ccaf1 100644 --- a/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt +++ b/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt @@ -423,7 +423,7 @@ private fun IntegerliteralContext.toAst(): NumericLiteralNode { } } -private fun ExpressionContext.toAst() : Expression { +private fun ExpressionContext.toAst(insideParentheses: Boolean=false) : Expression { val litval = literalvalue() if(litval!=null) { @@ -468,7 +468,7 @@ private fun ExpressionContext.toAst() : Expression { return scoped_identifier().toAst() if(bop!=null) - return BinaryExpression(left.toAst(), bop.text.trim(), right.toAst(), toPosition()) + return BinaryExpression(left.toAst(), bop.text.trim(), right.toAst(), toPosition(), insideParentheses=insideParentheses) if(prefix!=null) return PrefixExpression(prefix.text, expression(0).toAst(), toPosition()) @@ -483,7 +483,7 @@ private fun ExpressionContext.toAst() : Expression { } if(childCount==3 && children[0].text=="(" && children[2].text==")") - return expression(0).toAst() // expression within ( ) + return expression(0).toAst(insideParentheses=true) // expression within ( ) if(typecast()!=null) return TypecastExpression(expression(0).toAst(), typecast().datatype().toAst(), false, toPosition()) diff --git a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt index 5c09eaa62..ba8c53ba2 100644 --- a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt +++ b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt @@ -148,7 +148,13 @@ class PrefixExpression(val operator: String, var expression: Expression, overrid } } -class BinaryExpression(var left: Expression, var operator: String, var right: Expression, override val position: Position) : Expression() { +class BinaryExpression( + var left: Expression, + var operator: String, + var right: Expression, + override val position: Position, + val insideParentheses: Boolean = false // used in very few places to check priorities +) : Expression() { override lateinit var parent: Node override fun linkParents(parent: Node) { diff --git a/docs/source/syntaxreference.rst b/docs/source/syntaxreference.rst index 19c64b30e..f11acddf0 100644 --- a/docs/source/syntaxreference.rst +++ b/docs/source/syntaxreference.rst @@ -579,9 +579,10 @@ postfix increment and decrement: ``++`` ``--`` Syntactic sugar; ``aa++`` is equivalent to ``aa = aa + 1``, and ``aa--`` is equivalent to ``aa = aa - 1``. Because these operations are so common, we have these short forms. -comparison: ``!=`` ``<`` ``>`` ``<=`` ``>=`` +comparison: ``==`` ``!=`` ``<`` ``>`` ``<=`` ``>=`` Equality, Inequality, Less-than, Greater-than, Less-or-Equal-than, Greater-or-Equal-than comparisons. - The result is a 'boolean' value 'true' or 'false' (which in reality is just a byte value of 1 or 0). + The result is a boolean value 'true' or 'false' (1 or 0). + Note that you can chain comparisons like so: ``i < x < j``, which is the same as ``i x==true) diff --git a/examples/test.p8 b/examples/test.p8 index 377f79c4b..df10d91b2 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -3,19 +3,15 @@ main { sub start() { - cx16.r0++ - str[] names = ["irmen", "de", "jong"] - uword zz = names[1] - txt.print(names[1]) - } - - sub derp() { - cx16.r0++ - } - - asmsub hurrah() { - %ir {{ - nop - }} + ubyte x = 10 + ubyte y = 2 + txt.print_ub(5