make $8000000 a valid long integer (-2147483648)

This commit is contained in:
Irmen de Jong
2025-11-26 23:10:59 +01:00
parent e78345410f
commit b1ef863c7f
12 changed files with 118 additions and 54 deletions
+4 -1
View File
@@ -14,8 +14,11 @@ fun Number.toHex(): String {
// larger -> "$12345678"
// negative values are prefixed with '-'.
val integer = this.toLong()
if(integer<0)
if(integer<0) {
if(integer==-2147483648L)
return "$80000000" // the exception to the rule, because -$80000000 is not a valid hex number
return '-' + abs(integer).toHex()
}
return when (integer) {
in 0 until 16 -> integer.toString()
in 0 until 0x100 -> "$"+integer.toString(16).padStart(2,'0')
@@ -2375,7 +2375,7 @@ internal class AstChecker(private val program: Program,
if(value.type==BaseDataType.FLOAT)
err("integer value expected instead of float; possible loss of precision")
val number=value.number
if (number < -2147483647.0 || number > 2147483647.0)
if (number < -2147483648.0 || number > 2147483647.0)
return err("value '$number' out of range for long")
}
targetDt.isArray -> {
+34
View File
@@ -7,10 +7,13 @@ import io.kotest.matchers.doubles.plusOrMinus
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import io.kotest.matchers.string.shouldContain
import prog8.code.ast.PtAssignment
import prog8.code.ast.PtNumber
import prog8.code.core.InternalCompilerException
import prog8.code.core.toHex
import prog8.code.target.C64Target
import prog8.code.target.Mflpt5
import prog8.code.target.VMTarget
import prog8tests.helpers.ErrorReporterForTests
import prog8tests.helpers.compileText
@@ -225,4 +228,35 @@ class TestNumbers: FunSpec({
errors.errors[4] shouldContain "out of range"
errors.errors[5] shouldContain "byte doesn't match"
}
test("biggest long numbers") {
val src= $$"""
main {
sub start() {
long @shared l1, l2
l1 = $7fffffff
l2 = $80000000
l1 = -2147483648
l2 = -2147483649 ; will be truncated
l1 = $abcd1234
l2 = -$7fffffff
l1 = $80000001
l2 = -$80
}
}"""
compileText(C64Target(), false, src, outputDir) shouldNotBe null
val result = compileText(VMTarget(), false, src, outputDir)!!
val st = result.codegenAst!!.entrypoint()!!
st.children.size shouldBe 13
((st.children[4] as PtAssignment).value as PtNumber).number shouldBe 2147483647
((st.children[5] as PtAssignment).value as PtNumber).number shouldBe -2147483648
((st.children[6] as PtAssignment).value as PtNumber).number shouldBe -2147483648
((st.children[7] as PtAssignment).value as PtNumber).number shouldBe 2147483647
((st.children[8] as PtAssignment).value as PtNumber).number shouldBe -1412623820
((st.children[9] as PtAssignment).value as PtNumber).number shouldBe -2147483647
((st.children[10] as PtAssignment).value as PtNumber).number shouldBe -2147483647
((st.children[11] as PtAssignment).value as PtNumber).number shouldBe -128
}
})
@@ -347,7 +347,7 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
in -128..127 -> BaseDataType.BYTE
in 0..65535 -> BaseDataType.UWORD
in -32768..32767 -> BaseDataType.WORD
in -2147483647..2147483647 -> BaseDataType.LONG
in -2147483648L..0xffffffffL -> BaseDataType.LONG // TODO "hack" to allow unsigned long constants to be used as values for signed longs, without needing a cast (max value)
else -> BaseDataType.FLOAT
}
}
@@ -388,7 +388,7 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
}
// TODO "hack" to allow unsigned long constants to be used as values for signed longs, without needing a cast
if(integer.second.isLong && integer.first > Integer.MAX_VALUE) {
if(integer.second.isLong && integer.first > Integer.MAX_VALUE && integer.first <= 0xffffffff) {
val signedLong = integer.first.toLong().toInt()
return NumericLiteral(integer.second, signedLong.toDouble(), ctx.toPosition())
}
@@ -687,8 +687,12 @@ class NumericLiteral(val type: BaseDataType, // only numerical types allowed
in -128..127 -> NumericLiteral(BaseDataType.BYTE, dvalue, position)
in 0..65535 -> NumericLiteral(BaseDataType.UWORD, dvalue, position)
in -32768..32767 -> NumericLiteral(BaseDataType.WORD, dvalue, position)
in -2147483647..2147483647 -> NumericLiteral(BaseDataType.LONG, dvalue, position)
else -> NumericLiteral(BaseDataType.FLOAT, dvalue, position)
else -> {
if(dvalue in -2147483648.0 .. 2147483647.0)
NumericLiteral(BaseDataType.LONG, dvalue, position)
else
NumericLiteral(BaseDataType.FLOAT, dvalue, position)
}
}
}
}
+1 -1
View File
@@ -10,7 +10,7 @@ Weird Heisenbug
Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^
- make $8000000 a valid long integer (-2147483648) this is more involved than you think. To make this work: long \|= $80000000
- when implementing unsigned longs: remove the (mulitple) "TODO "hack" to allow unsigned long constants to be used as values for signed longs, without needing a cast"
- implement rest of long comparisons in IfElseAsmGen compareLongValues(): expressions operands that might clobber the R14-R15 registers... (github issue 196?)
- struct/ptr: implement the remaining TODOs in PointerAssignmentsGen.
- struct/ptr: optimize deref in PointerAssignmentsGen: optimize 'forceTemporary' to only use a temporary when the offset is >0
+1 -1
View File
@@ -225,7 +225,7 @@ value datatype
0 .. 255 ubyte
-32768 .. 32767 word
0 .. 65535 uword
-2147483647 .. 2147483647 long (there is no unsigned long right now)
-2147483648 .. 2147483647 long (there is no unsigned long right now)
========================= =================
Numeric expressions usually 'stay within their type' unless a cast is used, see :ref:`arithmetic`.
+55 -38
View File
@@ -4,55 +4,72 @@
main {
sub start() {
long @shared l
long @shared l1, l2
;l = 2147483648
l = $21111111
testdec(l)
l = $20000000
testdec(l)
l = 4026531840
testdec(l)
txt.nl()
l = $21111111
testinc(l)
l = $2fffffff
testinc(l)
}
sub testdec(long l) {
txt.print_ulhex(l, true)
l1 = $7fffffff
txt.print_ulhex(l1, true)
txt.spc()
txt.print_l(l)
txt.print_l(l1)
txt.nl()
l++
txt.print_ulhex(l, true)
l2 = $80000000
txt.print_ulhex(l2, true)
txt.spc()
txt.print_l(l)
txt.print_l(l2)
txt.nl()
l--
l--
txt.print_ulhex(l, true)
l1 = -2147483648
txt.print_ulhex(l1, true)
txt.spc()
txt.print_l(l)
txt.nl()
txt.nl()
}
sub testinc(long l) {
txt.print_ulhex(l, true)
txt.spc()
txt.print_l(l)
txt.print_l(l1)
txt.nl()
l++
txt.print_ulhex(l, true)
l2 = -2147483649 ; will be truncated
txt.print_ulhex(l2, true)
txt.spc()
txt.print_l(l)
txt.print_l(l2)
txt.nl()
l1 = $abcd1234
txt.print_ulhex(l1, true)
txt.spc()
txt.print_l(l1)
txt.nl()
l2 = -$7fffffff
txt.print_ulhex(l2, true)
txt.spc()
txt.print_l(l2)
txt.nl()
l1 = $80000001
txt.print_ulhex(l1, true)
txt.spc()
txt.print_l(l1)
txt.nl()
l2 = -$80
txt.print_ulhex(l2, true)
txt.spc()
txt.print_l(l2)
txt.nl()
l2 ^= $80000000
txt.print_ulhex(l2, true)
txt.spc()
txt.print_l(l2)
txt.nl()
l2 = $80
txt.print_ulhex(l2, true)
txt.spc()
txt.print_l(l2)
txt.nl()
l2 |= $80000000
txt.print_ulhex(l2, true)
txt.spc()
txt.print_l(l2)
txt.nl()
}
}
@@ -272,7 +272,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
dt.isBool -> constant.value.toInt().toString()
dt.isFloat -> constant.value.toString()
dt.isPointer -> TODO("constant pointer $constant")
dt.isInteger -> constant.value.toInt().toHex()
dt.isInteger -> constant.value.toLong().toHex()
else -> throw InternalCompilerException("weird dt")
}
xml.writeCharacters("${constant.typeString} ${constant.name}=$value\n")
@@ -921,7 +921,7 @@ data class IRInstruction(
when (type) {
IRDataType.BYTE -> require(immediate in -128..255) { "immediate value out of range for byte: $immediate" }
IRDataType.WORD -> require(immediate in -32768..65535) { "immediate value out of range for word: $immediate" }
IRDataType.LONG -> require(immediate in -2147483647..2147483647) { "immediate value out of range for long: $immediate" }
IRDataType.LONG -> require(immediate in -2147483648..2147483647) { "immediate value out of range for long: $immediate" }
IRDataType.FLOAT, null -> {}
}
}
+11 -5
View File
@@ -64,11 +64,11 @@ fun parseIRValue(value: String): Double {
return if(value.startsWith("-"))
-parseIRValue(value.substring(1))
else if(value.startsWith('$'))
value.substring(1).toInt(16).toDouble()
value.substring(1).toLong(16).toDouble()
else if(value.startsWith('%'))
value.substring(1).toInt(2).toDouble()
value.substring(1).toLong(2).toDouble()
else if(value.startsWith("0x"))
value.substring(2).toInt(16).toDouble()
value.substring(2).toLong(16).toDouble()
else if(value.startsWith('_'))
throw IRParseException("attempt to parse a label as numeric value")
else if(value.startsWith('&'))
@@ -154,7 +154,13 @@ fun parseIRCodeLine(line: String): Either<IRInstruction, String> {
if (immediateInt == null && immediateFp == null) {
if (type == IRDataType.FLOAT && opcode != Opcode.LOADFIELD && opcode != Opcode.STOREFIELD)
immediateFp = value
else
else if(type == IRDataType.LONG) {
val immediateLong = value.toLong()
if(immediateLong == 0x80000000L) {
immediateInt = -2147483648
} else
immediateInt = immediateLong.toInt()
} else
immediateInt = value.toInt()
} else {
address = value.toInt()
@@ -210,7 +216,7 @@ fun parseIRCodeLine(line: String): Either<IRInstruction, String> {
throw IRParseException("immediate value out of range for word: $immediateInt")
}
IRDataType.LONG -> {
if (immediateInt!=null && immediateInt < -2147483647)
if (immediateInt!=null && (immediateInt.toLong() < -2147483648L || immediateInt.toLong() > 0x7fffffffL))
throw IRParseException("immediate value out of range for long: $immediateInt")
}
IRDataType.FLOAT -> {}
@@ -348,7 +348,7 @@ class PtNumber(type: BaseDataType, val number: Double, position: Position) : PtE
BaseDataType.BYTE -> require(number in -128.0..127.0)
BaseDataType.UWORD -> require(number in 0.0..65535.0)
BaseDataType.WORD -> require(number in -32768.0..32767.0)
BaseDataType.LONG -> require(number in -2147483647.0..2147483647.0)
BaseDataType.LONG -> require(number in -2147483648.0..2147483647.0)
else -> require(type.isNumeric) { "numeric literal type should be numeric: $type" }
}
}