implementing const long

This commit is contained in:
Irmen de Jong 2024-11-13 23:42:15 +01:00
parent ea1daa97d3
commit a874aec6a1
13 changed files with 96 additions and 26 deletions

View File

@ -11,6 +11,7 @@ fun Number.toHex(): String {
// 0..15 -> "0".."15"
// 16..255 -> "$10".."$ff"
// 256..65536 -> "$0100".."$ffff"
// larger -> "$12345678"
// negative values are prefixed with '-'.
val integer = this.toInt()
if(integer<0)
@ -19,7 +20,7 @@ fun Number.toHex(): String {
in 0 until 16 -> integer.toString()
in 0 until 0x100 -> "$"+integer.toString(16).padStart(2,'0')
in 0 until 0x10000 -> "$"+integer.toString(16).padStart(4,'0')
else -> throw IllegalArgumentException("number too large for 16 bits $this")
else -> "$"+integer.toString(16).padStart(8,'0')
}
}
@ -27,11 +28,12 @@ fun UInt.toHex(): String {
// 0..15 -> "0".."15"
// 16..255 -> "$10".."$ff"
// 256..65536 -> "$0100".."$ffff"
// larger -> "$12345678"
return when (this) {
in 0u until 16u -> this.toString()
in 0u until 0x100u -> "$"+this.toString(16).padStart(2,'0')
in 0u until 0x10000u -> "$"+this.toString(16).padStart(4,'0')
else -> throw IllegalArgumentException("number too large for 16 bits $this")
else -> "$"+this.toString(16).padStart(8,'0')
}
}

View File

@ -38,9 +38,10 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
if(numLiteral.type==DataType.LONG) {
// see if LONG values may be reduced to something smaller
val smaller = NumericLiteral.optimalInteger(numLiteral.number.toInt(), numLiteral.position)
if(smaller.type!=DataType.LONG)
if(smaller.type!=DataType.LONG) {
return listOf(IAstModification.ReplaceNode(numLiteral, smaller, parent))
}
}
if(parent is Assignment) {
val iDt = parent.target.inferType(program)
@ -437,10 +438,11 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
}
if(valueDt isnot decl.datatype) {
val cast = numval.cast(decl.datatype, true)
if (cast.isValid)
if (cast.isValid) {
return listOf(IAstModification.ReplaceNode(numval, cast.valueOrZero(), decl))
}
}
}
return noModifications
}

View File

@ -717,7 +717,7 @@ internal class AstChecker(private val program: Program,
if(decl.names.size>1)
throw InternalCompilerException("vardecls with multiple names should have been converted into individual vardecls")
if(decl.datatype==DataType.LONG)
if(decl.datatype==DataType.LONG && decl.type!=VarDeclType.CONST)
errors.err("integer overflow", decl.position)
if(decl.type==VarDeclType.MEMORY) {
if (decl.datatype == DataType.BOOL || decl.datatype == DataType.ARRAY_BOOL)
@ -1691,12 +1691,14 @@ internal class AstChecker(private val program: Program,
private fun checkLongType(expression: Expression) {
if(expression.inferType(program).istype(DataType.LONG)) {
if((expression.parent as? VarDecl)?.type!=VarDeclType.CONST) {
if (expression.parent !is RepeatLoop) {
if (errors.noErrorForLine(expression.position))
errors.err("integer overflow", expression.position)
}
}
}
}
private fun checkValueTypeAndRangeString(targetDt: DataType, value: StringLiteral) : Boolean {
return if (targetDt == DataType.STR) {
@ -1830,31 +1832,38 @@ internal class AstChecker(private val program: Program,
DataType.UBYTE -> {
if(value.type==DataType.FLOAT)
err("unsigned byte value expected instead of float; possible loss of precision")
val number=value.number.toInt()
val number=value.number
if (number < 0 || number > 255)
return err("value '$number' out of range for unsigned byte")
}
DataType.BYTE -> {
if(value.type==DataType.FLOAT)
err("byte value expected instead of float; possible loss of precision")
val number=value.number.toInt()
val number=value.number
if (number < -128 || number > 127)
return err("value '$number' out of range for byte")
}
DataType.UWORD -> {
if(value.type==DataType.FLOAT)
err("unsigned word value expected instead of float; possible loss of precision")
val number=value.number.toInt()
val number=value.number
if (number < 0 || number > 65535)
return err("value '$number' out of range for unsigned word")
}
DataType.WORD -> {
if(value.type==DataType.FLOAT)
err("word value expected instead of float; possible loss of precision")
val number=value.number.toInt()
val number=value.number
if (number < -32768 || number > 32767)
return err("value '$number' out of range for word")
}
DataType.LONG -> {
if(value.type==DataType.FLOAT)
err("integer value expected instead of float; possible loss of precision")
val number=value.number
if (number < -2147483647 || number > 2147483647)
return err("value '$number' out of range for long")
}
DataType.BOOL -> {
if (value.type!=DataType.BOOL) {
err("type of value ${value.type} doesn't match target $targetDt")

View File

@ -24,6 +24,50 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
return noModifications
}
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
// check and possibly adjust value datatype vs decl datatype
val valueType = decl.value?.inferType(program)
if(valueType!=null && !valueType.istype(decl.datatype)) {
if(valueType.isUnknown) {
errors.err("value has incompatible type for ${decl.datatype}", decl.value!!.position)
return noModifications
}
val valueDt = valueType.getOr(DataType.UNDEFINED)
when(decl.type) {
VarDeclType.VAR -> {
if(decl.isArray) {
errors.err("value has incompatible type ($valueType) for the variable (${decl.datatype})", decl.value!!.position)
} else {
if (valueDt.largerThan(decl.datatype)) {
val constValue = decl.value?.constValue(program)
if (constValue != null)
errors.err("value '$constValue' out of range for ${decl.datatype}", constValue.position)
else
errors.err("value out of range for ${decl.datatype}", decl.value!!.position)
} else {
throw FatalAstException("value dt differs from decl dt ${decl.position}")
}
}
}
VarDeclType.CONST -> {
// change the vardecl type itself as well, but only if it's smaller
if(valueDt.largerThan(decl.datatype)) {
val constValue = decl.value!!.constValue(program)!!
errors.err("value '${constValue.number}' out of range for ${decl.datatype}", constValue.position)
} else {
val changed = decl.copy(valueDt)
return listOf(IAstModification.ReplaceNode(decl, changed, parent))
}
}
VarDeclType.MEMORY -> if(!valueType.isWords && !valueType.isBytes)
throw FatalAstException("value type for a memory var should be word or byte (address)")
}
}
return noModifications
}
override fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
return if(parent is IStatementContainer)
listOf(ScopeFlatten(scope, parent as IStatementContainer))

View File

@ -46,8 +46,8 @@ class TestNumbers: FunSpec({
(-50050).toHex() shouldBe "-\$c382"
(-65535).toHex() shouldBe "-\$ffff"
(-65535L).toHex() shouldBe "-\$ffff"
shouldThrow<IllegalArgumentException> { 65536.toHex() }
shouldThrow<IllegalArgumentException> { 65536L.toHex() }
(65536).toHex() shouldBe "\$00010000"
(-65536).toHex() shouldBe "-\$00010000"
}
test("testFloatToMflpt5") {

View File

@ -17,7 +17,7 @@ import prog8tests.helpers.compileText
class TestSubroutines: FunSpec({
test("string arg for byte param proper errormessage and subroutineptr in array too") {
test("string arg for byte param proper errormessage") {
val text="""
main {
sub func(ubyte bb) {
@ -26,14 +26,12 @@ class TestSubroutines: FunSpec({
sub start() {
func("abc")
uword[] commands = ["abc", 1.234]
}
}"""
val errors = ErrorReporterForTests()
compileText(C64Target(), false, text, writeAssembly = true, errors=errors) shouldBe null
errors.errors.size shouldBe 2
errors.errors.size shouldBe 1
errors.errors[0] shouldContain "type mismatch, was: STR expected: UBYTE"
errors.errors[1] shouldContain "value has incompatible type"
}
test("stringParameter") {

View File

@ -22,6 +22,19 @@ import prog8tests.helpers.compileText
class TestVariousCompilerAst: FunSpec({
context("arrays") {
test("invalid array element proper errormessage") {
val text="""
main {
sub start() {
uword[] commands = ["abc", 1.234]
}
}"""
val errors = ErrorReporterForTests()
compileText(C64Target(), false, text, writeAssembly = true, errors=errors) shouldBe null
errors.errors.size shouldBe 1
errors.errors[0] shouldContain "value has incompatible type"
}
test("array literals") {
val text="""
%zeropage basicsafe

View File

@ -293,7 +293,7 @@ class VarDecl(val type: VarDeclType,
override fun replaceChildNode(node: Node, replacement: Node) {
require(replacement is Expression && (value==null || node===value))
value = replacement
value = replacement // note: any datatype differences between the value and the decl itself, will be fixed by a separate ast walker step
replacement.parent = this
}
@ -311,10 +311,12 @@ class VarDecl(val type: VarDeclType,
throw IllegalArgumentException("attempt to get zero value for vardecl that shouldn't get it")
}
override fun copy(): VarDecl {
override fun copy(): VarDecl = copy(datatype)
fun copy(newDatatype: DataType): VarDecl {
if(names.size>1)
throw FatalAstException("should not copy a vardecl that still has multiple names")
val copy = VarDecl(type, origin, datatype, zeropage, arraysize?.copy(), name, names, value?.copy(),
val copy = VarDecl(type, origin, newDatatype, zeropage, arraysize?.copy(), name, names, value?.copy(),
sharedWithAsm, splitArray, alignment, dirty, position)
copy.allowInitializeWithZero = this.allowInitializeWithZero
return copy

View File

@ -167,7 +167,7 @@ constdecl: 'const' varinitializer ;
memoryvardecl: ADDRESS_OF varinitializer;
datatype: 'ubyte' | 'byte' | 'uword' | 'word' | 'float' | 'str' | 'bool' ;
datatype: 'ubyte' | 'byte' | 'uword' | 'word' | 'long' | 'float' | 'str' | 'bool' ;
arrayindex: '[' expression ']' ;

View File

@ -13,7 +13,7 @@
</options>
<keywords keywords="&amp;;-&gt;;@;and;as;asmsub;break;clobbers;continue;do;downto;else;extsub;false;for;goto;if;if_cc;if_cs;if_eq;if_mi;if_ne;if_neg;if_nz;if_pl;if_pos;if_vc;if_vs;if_z;in;inline;not;or;repeat;return;step;sub;to;true;unroll;until;when;while;xor;~" ignore_case="false" />
<keywords2 keywords="%address;%align;%asm;%asmbinary;%asminclude;%breakpoint;%encoding;%import;%ir;%launcher;%memtop;%option;%output;%zeropage;%zpallowed;%zpreserved;@align64;@alignpage;@alignword;@bank;@dirty;@nozp;@requirezp;@shared;@split;@zp;atascii:;cp437:;default:;iso16:;iso5:;iso:;kata:;petscii:;sc:" />
<keywords3 keywords="bool;byte;const;float;str;ubyte;uword;void;word" />
<keywords3 keywords="bool;byte;const;float;long;str;ubyte;uword;void;word" />
<keywords4 keywords="abs;call;callfar;callfar2;clamp;cmp;defer;divmod;len;lsb;max;memory;min;mkword;msb;peek;peekf;peekw;poke;pokef;pokew;rol;rol2;ror;ror2;rrestore;rrestorex;rsave;rsavex;setlsb;setmsb;sgn;sizeof;sqrt" />
</highlighting>
<extensionMap>

View File

@ -24,7 +24,7 @@
<Keywords name="Folders in comment, open"></Keywords>
<Keywords name="Folders in comment, middle"></Keywords>
<Keywords name="Folders in comment, close"></Keywords>
<Keywords name="Keywords1">void const&#x000D;&#x000A;str&#x000D;&#x000A;byte ubyte bool&#x000D;&#x000A;word uword&#x000D;&#x000A;float&#x000D;&#x000A;zp shared split requirezp nozp</Keywords>
<Keywords name="Keywords1">void const&#x000D;&#x000A;str&#x000D;&#x000A;byte ubyte bool&#x000D;&#x000A;long word uword&#x000D;&#x000A;float&#x000D;&#x000A;zp shared split requirezp nozp</Keywords>
<Keywords name="Keywords2">%address&#x000D;&#x000A;%asm&#x000D;&#x000A;%ir&#x000D;&#x000A;%asmbinary&#x000D;&#x000A;%asminclude&#x000D;&#x000A;%align&#x000D;&#x000A;%breakpoint&#x000D;&#x000A;%encoding&#x000D;&#x000A;%import&#x000D;&#x000A;%memtop&#x000D;&#x000A;%launcher&#x000D;&#x000A;%option&#x000D;&#x000A;%output&#x000D;&#x000A;%zeropage&#x000D;&#x000A;%zpreserved&#x000D;&#x000A;%zpallowed</Keywords>
<Keywords name="Keywords3">inline sub asmsub extsub&#x000D;&#x000A;clobbers&#x000D;&#x000A;asm&#x000D;&#x000A;if&#x000D;&#x000A;when else&#x000D;&#x000A;if_cc if_cs if_eq if_mi if_neg if_nz if_pl if_pos if_vc if_vs if_z&#x000D;&#x000A;for in step do while repeat unroll&#x000D;&#x000A;break continue return goto</Keywords>
<Keywords name="Keywords4">abs call callfar callfar2 clamp cmp defer divmod len lsb lsl lsr memory mkword min max msb peek peekw peekf poke pokew pokef rsave rsavex rrestore rrestorex rnd rndw rol rol2 ror ror2 setlsb setmsb sgn sizeof sqrtw</Keywords>

View File

@ -149,7 +149,7 @@ contexts:
- match: (\b\w+\.)
scope: entity.name.namespace.prog8
storage:
- match: (\b(ubyte|byte|word|uword|float|str)\b)
- match: (\b(ubyte|byte|word|uword|long|float|str)\b)
scope: storage.type.prog8
- match: (\b(const)\b)
scope: storage.modifier.prog8

View File

@ -38,7 +38,7 @@ syn match prog8Directive "\(^\|\s\)%\(zpreserved\|zpallowed\|address\|encoding\|
syn match prog8Directive "\(^\|\s\)%\(align\|asmbinary\|asminclude\|breakpoint\)\>"
syn match prog8Directive "\(^\|\s\)%\(asm\|ir\)\>"
syn match prog8Type "\<\%(u\?byte\|u\?word\|float\|str\|bool\)\>"
syn match prog8Type "\<\%(u\?byte\|u\?word\|float\|str\|bool\|long\)\>"
syn region prog8ArrayType matchgroup=prog8Type
\ start="\<\%(u\?byte\|u\?word\|float\|str\|bool\)\[" end="\]"
\ transparent