fix: for loop over array literal no longer crashes the compiler

This commit is contained in:
Irmen de Jong 2021-12-28 17:21:41 +01:00
parent b2876b0a03
commit 7a9e5afb93
5 changed files with 140 additions and 11 deletions

View File

@ -91,7 +91,8 @@ internal fun Program.checkIdentifiers(errors: IErrorReporter, program: Program,
transforms.applyModifications()
val lit2decl = LiteralsToAutoVars(this)
lit2decl.visit(this)
lit2decl.applyModifications()
if(errors.noErrors())
lit2decl.applyModifications()
}
}

View File

@ -1,6 +1,5 @@
package prog8.compiler.astprocessing
import prog8.ast.IStatementContainer
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.DataType
@ -28,26 +27,24 @@ internal class LiteralsToAutoVars(private val program: Program) : AstWalker() {
override fun after(array: ArrayLiteralValue, parent: Node): Iterable<IAstModification> {
val vardecl = array.parent as? VarDecl
if(vardecl!=null) {
// adjust the datatype of the array (to an educated guess)
// adjust the datatype of the array (to an educated guess from the vardecl type)
val arrayDt = array.type
if(arrayDt isnot vardecl.datatype) {
val cast = array.cast(vardecl.datatype)
if (cast != null && cast !== array)
if(cast!=null && cast !== array)
return listOf(IAstModification.ReplaceNode(vardecl.value!!, cast, vardecl))
}
} else {
val arrayDt = array.guessDatatype(program)
if(arrayDt.isKnown) {
// this array literal is part of an expression, turn it into an identifier reference
// turn the array literal it into an identifier reference
val litval2 = array.cast(arrayDt.getOr(DataType.UNDEFINED))
if(litval2!=null) {
if(array.parent !is IStatementContainer)
return noModifications
val vardecl2 = VarDecl.createAuto(litval2)
val identifier = IdentifierReference(listOf(vardecl2.name), vardecl2.position)
return listOf(
IAstModification.ReplaceNode(array, identifier, parent),
IAstModification.InsertFirst(vardecl2, array.parent as IStatementContainer)
IAstModification.ReplaceNode(array, identifier, parent),
IAstModification.InsertFirst(vardecl2, array.definingScope)
)
}
}

View File

@ -301,4 +301,68 @@ class TestCompilerOnRanges: FunSpec({
forloop.iterable shouldBe instanceOf<RangeExpr>()
(forloop.iterable as RangeExpr).step shouldBe NumericLiteralValue(DataType.UBYTE, -2.0, Position.DUMMY)
}
test("for statement on all possible iterable expressions") {
compileText(C64Target, false, """
main {
sub start() {
ubyte xx
str name = "irmen"
ubyte[] values = [1,2,3,4,5,6,7]
for xx in name {
xx++
}
for xx in values {
xx++
}
for xx in 10 to 20 step 3 {
xx++
}
for xx in "abcdef" {
xx++
}
for xx in [2,4,6,8] {
xx++
}
}
}""").assertSuccess()
}
// TODO enable this after this if-syntax is implemented
xtest("if containment check on all possible iterable expressions") {
compileText(C64Target, false, """
main {
sub start() {
ubyte xx
str name = "irmen"
ubyte[] values = [1,2,3,4,5,6,7]
if xx in name {
xx++
}
if xx in values {
xx++
}
if xx in 10 to 20 step 3 {
xx++
}
if xx in "abcdef" {
xx++
}
if xx in [2,4,6,8] {
xx++
}
}
}""").assertSuccess()
}
})

View File

@ -6,6 +6,7 @@ For next compiler release (7.6)
add "if X in [1,2,3] {...}" syntax , as an alternative to when X { 1,2,3-> {...} }
if the array is not a literal, do a normal containment test instead in an array or string or range
change "consider using when statement..." to "consider using if X in [..] or when statement..."
also add to the docs!
Blocked by an official Commander-x16 v39 release

View File

@ -5,10 +5,76 @@
main {
sub start() {
ubyte @shared xx
str name = "irmen"
ubyte[] values = [1,2,3,4,5,6,7]
if xx<100 {
foobar()
for xx in name {
txt.chrout(xx)
txt.spc()
}
txt.nl()
for xx in values {
txt.print_ub(xx)
txt.spc()
}
txt.nl()
for xx in 10 to 20 step 3 {
txt.print_ub(xx)
txt.spc()
}
txt.nl()
for xx in "abcdef" {
txt.print_ub(xx)
txt.spc()
}
txt.nl()
for xx in [2,4,6,8] {
txt.print_ub(xx)
txt.spc()
}
txt.nl()
; if xx in 100 { ; TODO error
; xx++
; }
;
; if xx in 'a' { ; TODO error
; xx++
; }
;
; if xx in "abc" { ; TODO containment test via when
; xx++
; }
;
; if xx in [1,2,3,4,5] { ; TODO containment test via when
; xx++
; }
;
; if xx in [1,2,3,4,5,6,7,8,9,10] { ; TODO containment test via loop?
; xx++
; }
;
; if xx in name { ; TODO containment test via loop
; xx++
; }
;
; if xx in values { ; TODO containment test via loop
; xx++
; }
;
; if xx in 10 to 20 step 2 { ; TODO
;
; }
; TODO const optimizing of the containment tests
; TODO also with (u)word and floats
if xx==9 or xx==10 or xx==11 or xx==12 or xx==13 {
txt.print("9 10 11\n")