mirror of
https://github.com/irmen/prog8.git
synced 2026-04-20 11:17:01 +00:00
fix const evaluation of bitwise logical expressions (&, |, ^, <<, >>) of signed operands
This commit is contained in:
@@ -5,10 +5,7 @@ import prog8.ast.FatalAstException
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.expressions.FunctionCallExpression
|
||||
import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.code.core.BaseDataType
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.core.isInteger
|
||||
import prog8.code.core.isIntegerOrBool
|
||||
import prog8.code.core.*
|
||||
import kotlin.math.*
|
||||
|
||||
|
||||
@@ -70,52 +67,45 @@ class ConstExprEvaluator {
|
||||
}
|
||||
|
||||
private fun bitwiseXor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||
if(left.type==BaseDataType.UBYTE || left.type==BaseDataType.BOOL) {
|
||||
if(right.type.isIntegerOrBool) {
|
||||
return NumericLiteral(BaseDataType.UBYTE, (left.number.toInt() xor (right.number.toInt() and 255)).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type==BaseDataType.UWORD) {
|
||||
if(right.type.isInteger) {
|
||||
return NumericLiteral(BaseDataType.UWORD, (left.number.toInt() xor right.number.toInt() and 65535).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type==BaseDataType.LONG) {
|
||||
if(right.type.isInteger) {
|
||||
return NumericLiteral.optimalNumeric((left.number.toInt() xor right.number.toInt()).toDouble(), left.position)
|
||||
if(right.type.isIntegerOrBool) {
|
||||
val leftDt = left.type
|
||||
if(leftDt.isByteOrBool)
|
||||
return NumericLiteral(BaseDataType.UBYTE, ((left.number.toInt() xor right.number.toInt()) and 255).toDouble(), left.position)
|
||||
else if(leftDt.isInteger) {
|
||||
if (leftDt == BaseDataType.UWORD || leftDt == BaseDataType.WORD)
|
||||
return NumericLiteral(BaseDataType.UWORD, ((left.number.toInt() xor right.number.toInt()) and 65535).toDouble(), left.position)
|
||||
else if (leftDt == BaseDataType.LONG)
|
||||
return NumericLiteral.optimalNumeric((left.number.toInt() xor right.number.toInt()).toDouble(), left.position)
|
||||
}
|
||||
}
|
||||
|
||||
throw ExpressionError("cannot calculate $left ^ $right", left.position)
|
||||
}
|
||||
|
||||
private fun bitwiseOr(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||
if(left.type==BaseDataType.UBYTE || left.type==BaseDataType.BOOL) {
|
||||
if(right.type.isIntegerOrBool) {
|
||||
return NumericLiteral(BaseDataType.UBYTE, (left.number.toInt() or (right.number.toInt() and 255)).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type==BaseDataType.UWORD) {
|
||||
if(right.type.isInteger) {
|
||||
return NumericLiteral(BaseDataType.UWORD, (left.number.toInt() or right.number.toInt() and 65535).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type==BaseDataType.LONG) {
|
||||
if(right.type.isInteger) {
|
||||
return NumericLiteral.optimalNumeric((left.number.toInt() or right.number.toInt()).toDouble(), left.position)
|
||||
if(right.type.isIntegerOrBool) {
|
||||
val leftDt = left.type
|
||||
if(leftDt.isByteOrBool)
|
||||
return NumericLiteral(BaseDataType.UBYTE, ((left.number.toInt() or right.number.toInt()) and 255).toDouble(), left.position)
|
||||
else if(leftDt.isInteger) {
|
||||
if (leftDt == BaseDataType.UWORD || leftDt == BaseDataType.WORD)
|
||||
return NumericLiteral(BaseDataType.UWORD, ((left.number.toInt() or right.number.toInt()) and 65535).toDouble(), left.position)
|
||||
else if (leftDt == BaseDataType.LONG)
|
||||
return NumericLiteral.optimalNumeric((left.number.toInt() or right.number.toInt()).toDouble(), left.position)
|
||||
}
|
||||
}
|
||||
throw ExpressionError("cannot calculate $left | $right", left.position)
|
||||
}
|
||||
|
||||
private fun bitwiseAnd(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||
if(left.type==BaseDataType.UBYTE || left.type==BaseDataType.BOOL) {
|
||||
if(right.type.isIntegerOrBool) {
|
||||
return NumericLiteral(BaseDataType.UBYTE, (left.number.toInt() and (right.number.toInt() and 255)).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type==BaseDataType.UWORD) {
|
||||
if(right.type.isInteger) {
|
||||
return NumericLiteral(BaseDataType.UWORD, (left.number.toInt() and right.number.toInt() and 65535).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type==BaseDataType.LONG) {
|
||||
if(right.type.isInteger) {
|
||||
return NumericLiteral.optimalNumeric((left.number.toInt() and right.number.toInt()).toDouble(), left.position)
|
||||
if(right.type.isIntegerOrBool) {
|
||||
val leftDt = left.type
|
||||
if(leftDt.isByteOrBool)
|
||||
return NumericLiteral(BaseDataType.UBYTE, ((left.number.toInt() and right.number.toInt()) and 255).toDouble(), left.position)
|
||||
else if(leftDt.isInteger) {
|
||||
if (leftDt == BaseDataType.UWORD || leftDt == BaseDataType.WORD)
|
||||
return NumericLiteral(BaseDataType.UWORD, ((left.number.toInt() and right.number.toInt()) and 65535).toDouble(), left.position)
|
||||
else if (leftDt == BaseDataType.LONG)
|
||||
return NumericLiteral.optimalNumeric((left.number.toInt() and right.number.toInt()).toDouble(), left.position)
|
||||
}
|
||||
}
|
||||
throw ExpressionError("cannot calculate $left & $right", left.position)
|
||||
|
||||
@@ -185,6 +185,16 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if shifts have a positive integer shift type
|
||||
if(expr.operator=="<<" || expr.operator==">>") {
|
||||
if(rightDt.isInteger) {
|
||||
val rconst = expr.right.constValue(program)
|
||||
if(rconst!=null && rconst.number<0)
|
||||
errors.err("can only shift by a positive amount", expr.right.position)
|
||||
} else
|
||||
errors.err("right operand of bit shift must be an integer", expr.right.position)
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
|
||||
@@ -406,4 +406,66 @@ main {
|
||||
errors.errors[0] shouldContain(":4:37: no cast available")
|
||||
errors.errors[1] shouldContain(":5:37: no cast available")
|
||||
}
|
||||
|
||||
test("const evaluation of signed bitwise operations") {
|
||||
val src="""
|
||||
%import textio
|
||||
main {
|
||||
sub start() {
|
||||
byte @shared a = -1
|
||||
byte @shared b = -15
|
||||
ubyte @shared ub = 2
|
||||
const byte ca = -1
|
||||
const byte cb = -15
|
||||
const ubyte cub = 2
|
||||
|
||||
txt.print_ub( a & b )
|
||||
txt.spc()
|
||||
txt.print_ub( a | b )
|
||||
txt.spc()
|
||||
txt.print_ub( a ^ b )
|
||||
txt.spc()
|
||||
txt.print_b( a << ub )
|
||||
txt.spc()
|
||||
txt.print_b( a >> ub )
|
||||
txt.nl()
|
||||
txt.print_ub( ca & cb )
|
||||
txt.spc()
|
||||
txt.print_ub( ca | cb )
|
||||
txt.spc()
|
||||
txt.print_ub( ca ^ cb )
|
||||
txt.spc()
|
||||
txt.print_b( ca << cub )
|
||||
txt.spc()
|
||||
txt.print_b( ca >> cub )
|
||||
txt.nl()
|
||||
|
||||
word @shared aw = -1
|
||||
word @shared bw = -15
|
||||
uword @shared uw = 2
|
||||
const word caw = -1
|
||||
const word cbw = -15
|
||||
const uword cuw = 2
|
||||
|
||||
txt.print_uw( aw & bw )
|
||||
txt.spc()
|
||||
txt.print_uw( aw | bw )
|
||||
txt.spc()
|
||||
txt.print_uw( aw ^ bw )
|
||||
txt.nl()
|
||||
txt.print_uw( caw & cbw )
|
||||
txt.spc()
|
||||
txt.print_uw( caw | cbw )
|
||||
txt.spc()
|
||||
txt.print_uw( caw ^ cbw )
|
||||
txt.nl()
|
||||
txt.print_w( cbw << cuw )
|
||||
txt.spc()
|
||||
txt.print_w( cbw >> cuw )
|
||||
txt.nl()
|
||||
}
|
||||
}"""
|
||||
compileText(C64Target(), false, src, outputDir, writeAssembly = false) shouldNotBe null
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
@@ -923,6 +923,8 @@ arithmetic: ``+`` ``-`` ``*`` ``/`` ``%``
|
||||
bitwise arithmetic: ``&`` ``|`` ``^`` ``~`` ``<<`` ``>>``
|
||||
``&`` is bitwise and, ``|`` is bitwise or, ``^`` is bitwise xor, ``~`` is bitwise invert (this one is an unary operator)
|
||||
``<<`` is bitwise left shift and ``>>`` is bitwise right shift (both will not change the datatype of the value)
|
||||
While the operands can be signed integers (the expression will just consider the underlying bit patterns),
|
||||
the result value of a bitwise expression is always unsigned.
|
||||
|
||||
assignment: ``=``
|
||||
Sets the target on the LHS (left hand side) of the operator to the value of the expression on the RHS (right hand side).
|
||||
|
||||
+52
-4
@@ -1,9 +1,57 @@
|
||||
%zeropage basicsafe
|
||||
%import textio
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
cx16.r0 = foo()
|
||||
}
|
||||
byte @shared a = -1
|
||||
byte @shared b = -15
|
||||
ubyte @shared ub = 2
|
||||
const byte ca = -1
|
||||
const byte cb = -15
|
||||
const ubyte cub = 2
|
||||
|
||||
extsub $f000 = foo() clobbers(X) -> uword @AY
|
||||
txt.print_ub( a & b )
|
||||
txt.spc()
|
||||
txt.print_ub( a | b )
|
||||
txt.spc()
|
||||
txt.print_ub( a ^ b )
|
||||
txt.spc()
|
||||
txt.print_b( a << ub )
|
||||
txt.spc()
|
||||
txt.print_b( a >> ub )
|
||||
txt.nl()
|
||||
txt.print_ub( ca & cb )
|
||||
txt.spc()
|
||||
txt.print_ub( ca | cb )
|
||||
txt.spc()
|
||||
txt.print_ub( ca ^ cb )
|
||||
txt.spc()
|
||||
txt.print_b( ca << cub )
|
||||
txt.spc()
|
||||
txt.print_b( ca >> cub )
|
||||
txt.nl()
|
||||
|
||||
word @shared aw = -1
|
||||
word @shared bw = -15
|
||||
uword @shared uw = 2
|
||||
const word caw = -1
|
||||
const word cbw = -15
|
||||
const uword cuw = 2
|
||||
|
||||
txt.print_uw( aw & bw )
|
||||
txt.spc()
|
||||
txt.print_uw( aw | bw )
|
||||
txt.spc()
|
||||
txt.print_uw( aw ^ bw )
|
||||
txt.nl()
|
||||
txt.print_uw( caw & cbw )
|
||||
txt.spc()
|
||||
txt.print_uw( caw | cbw )
|
||||
txt.spc()
|
||||
txt.print_uw( caw ^ cbw )
|
||||
txt.nl()
|
||||
txt.print_w( cbw << cuw )
|
||||
txt.spc()
|
||||
txt.print_w( cbw >> cuw )
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user