From a874aec6a1e7490f7af15a3543764f115a3d4fae Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Wed, 13 Nov 2024 23:42:15 +0100 Subject: [PATCH] implementing const long --- codeCore/src/prog8/code/core/Conversions.kt | 6 ++- .../optimizer/ConstantFoldingOptimizer.kt | 6 ++- .../compiler/astprocessing/AstChecker.kt | 25 +++++++---- .../compiler/astprocessing/VariousCleanups.kt | 44 +++++++++++++++++++ compiler/test/TestNumbers.kt | 4 +- compiler/test/TestSubroutines.kt | 6 +-- compiler/test/ast/TestVariousCompilerAst.kt | 13 ++++++ .../src/prog8/ast/statements/AstStatements.kt | 8 ++-- parser/antlr/Prog8ANTLR.g4 | 2 +- syntax-files/IDEA/Prog8.xml | 2 +- syntax-files/NotepadPlusPlus/Prog8.xml | 2 +- syntax-files/SublimeText/Prog8.sublime-syntax | 2 +- syntax-files/Vim/prog8.vim | 2 +- 13 files changed, 96 insertions(+), 26 deletions(-) diff --git a/codeCore/src/prog8/code/core/Conversions.kt b/codeCore/src/prog8/code/core/Conversions.kt index 9befdf9e1..e1d4d45af 100644 --- a/codeCore/src/prog8/code/core/Conversions.kt +++ b/codeCore/src/prog8/code/core/Conversions.kt @@ -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') } } diff --git a/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt b/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt index 73e9eeee8..1cdce064c 100644 --- a/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt +++ b/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt @@ -38,8 +38,9 @@ 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) { @@ -437,8 +438,9 @@ 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 diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index 6dac009df..c6fe32000 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -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,9 +1691,11 @@ internal class AstChecker(private val program: Program, private fun checkLongType(expression: Expression) { if(expression.inferType(program).istype(DataType.LONG)) { - if(expression.parent !is RepeatLoop) { - if (errors.noErrorForLine(expression.position)) - errors.err("integer overflow", expression.position) + if((expression.parent as? VarDecl)?.type!=VarDeclType.CONST) { + if (expression.parent !is RepeatLoop) { + if (errors.noErrorForLine(expression.position)) + errors.err("integer overflow", expression.position) + } } } } @@ -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") diff --git a/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt b/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt index 898023082..3b2e6fd78 100644 --- a/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt +++ b/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt @@ -24,6 +24,50 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter, return noModifications } + override fun after(decl: VarDecl, parent: Node): Iterable { + // 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 { return if(parent is IStatementContainer) listOf(ScopeFlatten(scope, parent as IStatementContainer)) diff --git a/compiler/test/TestNumbers.kt b/compiler/test/TestNumbers.kt index f033f6074..e769d1501 100644 --- a/compiler/test/TestNumbers.kt +++ b/compiler/test/TestNumbers.kt @@ -46,8 +46,8 @@ class TestNumbers: FunSpec({ (-50050).toHex() shouldBe "-\$c382" (-65535).toHex() shouldBe "-\$ffff" (-65535L).toHex() shouldBe "-\$ffff" - shouldThrow { 65536.toHex() } - shouldThrow { 65536L.toHex() } + (65536).toHex() shouldBe "\$00010000" + (-65536).toHex() shouldBe "-\$00010000" } test("testFloatToMflpt5") { diff --git a/compiler/test/TestSubroutines.kt b/compiler/test/TestSubroutines.kt index 4a651038d..ba63570dc 100644 --- a/compiler/test/TestSubroutines.kt +++ b/compiler/test/TestSubroutines.kt @@ -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") { diff --git a/compiler/test/ast/TestVariousCompilerAst.kt b/compiler/test/ast/TestVariousCompilerAst.kt index c172eb631..573bab68e 100644 --- a/compiler/test/ast/TestVariousCompilerAst.kt +++ b/compiler/test/ast/TestVariousCompilerAst.kt @@ -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 diff --git a/compilerAst/src/prog8/ast/statements/AstStatements.kt b/compilerAst/src/prog8/ast/statements/AstStatements.kt index eb6b6fd1b..4a96aee1a 100644 --- a/compilerAst/src/prog8/ast/statements/AstStatements.kt +++ b/compilerAst/src/prog8/ast/statements/AstStatements.kt @@ -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 diff --git a/parser/antlr/Prog8ANTLR.g4 b/parser/antlr/Prog8ANTLR.g4 index ba15984ea..06d91c3ec 100644 --- a/parser/antlr/Prog8ANTLR.g4 +++ b/parser/antlr/Prog8ANTLR.g4 @@ -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 ']' ; diff --git a/syntax-files/IDEA/Prog8.xml b/syntax-files/IDEA/Prog8.xml index 350333a07..3b19d468d 100644 --- a/syntax-files/IDEA/Prog8.xml +++ b/syntax-files/IDEA/Prog8.xml @@ -13,7 +13,7 @@ - + diff --git a/syntax-files/NotepadPlusPlus/Prog8.xml b/syntax-files/NotepadPlusPlus/Prog8.xml index 5270cebb8..40d4d1b76 100644 --- a/syntax-files/NotepadPlusPlus/Prog8.xml +++ b/syntax-files/NotepadPlusPlus/Prog8.xml @@ -24,7 +24,7 @@ - void const str byte ubyte bool word uword float zp shared split requirezp nozp + void const str byte ubyte bool long word uword float zp shared split requirezp nozp %address %asm %ir %asmbinary %asminclude %align %breakpoint %encoding %import %memtop %launcher %option %output %zeropage %zpreserved %zpallowed inline sub asmsub extsub clobbers asm if when else if_cc if_cs if_eq if_mi if_neg if_nz if_pl if_pos if_vc if_vs if_z for in step do while repeat unroll break continue return goto 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 diff --git a/syntax-files/SublimeText/Prog8.sublime-syntax b/syntax-files/SublimeText/Prog8.sublime-syntax index ee6d8148d..34d18244c 100644 --- a/syntax-files/SublimeText/Prog8.sublime-syntax +++ b/syntax-files/SublimeText/Prog8.sublime-syntax @@ -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 diff --git a/syntax-files/Vim/prog8.vim b/syntax-files/Vim/prog8.vim index f376a7c7b..34a7f8aca 100644 --- a/syntax-files/Vim/prog8.vim +++ b/syntax-files/Vim/prog8.vim @@ -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