diff --git a/shared/src/main/scala/io/kaitai/struct/ClassTypeProvider.scala b/shared/src/main/scala/io/kaitai/struct/ClassTypeProvider.scala index b544dc6a8..467de141c 100644 --- a/shared/src/main/scala/io/kaitai/struct/ClassTypeProvider.scala +++ b/shared/src/main/scala/io/kaitai/struct/ClassTypeProvider.scala @@ -4,17 +4,24 @@ import io.kaitai.struct.datatype.DataType import io.kaitai.struct.datatype.DataType._ import io.kaitai.struct.exprlang.Ast import io.kaitai.struct.format._ -import io.kaitai.struct.precompile.{EnumNotFoundError, FieldNotFoundError, TypeNotFoundError, TypeUndecidedError} +import io.kaitai.struct.precompile.{EnumNotFoundError, ExpressionError, FieldNotFoundError, TypeNotFoundError, TypeUndecidedError} import io.kaitai.struct.translators.TypeProvider class ClassTypeProvider(classSpecs: ClassSpecs, var topClass: ClassSpec) extends TypeProvider { var nowClass = topClass val allClasses: ClassSpecs = classSpecs - var _currentIteratorType: Option[DataType] = None + /** + * Type of the `_` variable in the expression. That variable is defined in + * `repeat-until` and `valid: expr` contexts and refers to the attribute + * just parsed. + */ + var _typeOfUnderscore: Option[DataType] = None + /** + * Type of the `_on` variable in the expression. That variable is defined in + * `cases.` contexts and refers to the value of `switch-on` expression. + */ var _currentSwitchType: Option[DataType] = None - def currentIteratorType: DataType = _currentIteratorType.get - def currentSwitchType: DataType = _currentSwitchType.get override def determineType(attrName: String): DataType = { determineType(nowClass, attrName) @@ -30,14 +37,20 @@ class ClassTypeProvider(classSpecs: ClassSpecs, var topClass: ClassSpec) extends topClass.toDataType case Identifier.PARENT => if (inClass.parentClass == UnknownClassSpec) - throw new RuntimeException(s"Unable to derive ${Identifier.PARENT} type in ${inClass.name.mkString("::")}") + throw new ExpressionError(s"Unable to derive '${Identifier.PARENT}' type in '${inClass.nameAsStr}'") inClass.parentClass.toDataType case Identifier.IO => KaitaiStreamType case Identifier.ITERATOR => - currentIteratorType + _typeOfUnderscore match { + case Some(value) => value + case None => throw new ExpressionError(s"Context variable '$attrName' is available only in the 'repeat-until' and 'valid/expr' attributes") + } case Identifier.SWITCH_ON => - currentSwitchType + _currentSwitchType match { + case Some(value) => value + case None => throw new ExpressionError(s"Context variable '$attrName' is available only in the 'cases.' expressions") + } case Identifier.INDEX => CalcIntType case Identifier.SIZEOF => diff --git a/shared/src/main/scala/io/kaitai/struct/ConstructClassCompiler.scala b/shared/src/main/scala/io/kaitai/struct/ConstructClassCompiler.scala index 4637773bd..26f75f74f 100644 --- a/shared/src/main/scala/io/kaitai/struct/ConstructClassCompiler.scala +++ b/shared/src/main/scala/io/kaitai/struct/ConstructClassCompiler.scala @@ -87,7 +87,8 @@ class ConstructClassCompiler(classSpecs: ClassSpecs, topClass: ClassSpec) extend case RepeatExpr(expr) => s"Array(${translator.translate(expr)}, $typeStr)" case RepeatUntil(expr) => - provider._currentIteratorType = Some(dataType) + // Set the type of the `_` variable in expression + provider._typeOfUnderscore = Some(dataType) s"RepeatUntil(lambda obj_, list_, this: ${translator.translate(expr)}, $typeStr)" case RepeatEos => s"GreedyRange($typeStr)" diff --git a/shared/src/main/scala/io/kaitai/struct/GraphvizClassCompiler.scala b/shared/src/main/scala/io/kaitai/struct/GraphvizClassCompiler.scala index 4bf1ebcc0..d2fba3694 100644 --- a/shared/src/main/scala/io/kaitai/struct/GraphvizClassCompiler.scala +++ b/shared/src/main/scala/io/kaitai/struct/GraphvizClassCompiler.scala @@ -196,7 +196,8 @@ class GraphvizClassCompiler(classSpecs: ClassSpecs, topClass: ClassSpec) extends case ValidationInEnum() => "must be defined in the enum" case ValidationExpr(expr) => - provider._currentIteratorType = Some(dataType) + // Set the type of the `_` variable in expression + provider._typeOfUnderscore = Some(dataType) s"must satisfy ${expression(expr, fullPortName, STYLE_EDGE_VALID)}" } case None => return @@ -212,7 +213,8 @@ class GraphvizClassCompiler(classSpecs: ClassSpecs, topClass: ClassSpec) extends expression(ex, s"$currentTable:$portName", STYLE_EDGE_REPEAT) + " times") case RepeatUntil(ex) => - provider._currentIteratorType = Some(dataType) + // Set the type of the `_` variable in expression + provider._typeOfUnderscore = Some(dataType) out.puts("repeat until " + expression(ex, s"$currentTable:$portName", STYLE_EDGE_REPEAT) + "") diff --git a/shared/src/main/scala/io/kaitai/struct/languages/CSharpCompiler.scala b/shared/src/main/scala/io/kaitai/struct/languages/CSharpCompiler.scala index e7cc734f1..00d2eac04 100644 --- a/shared/src/main/scala/io/kaitai/struct/languages/CSharpCompiler.scala +++ b/shared/src/main/scala/io/kaitai/struct/languages/CSharpCompiler.scala @@ -272,7 +272,7 @@ class CSharpCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) out.puts(s"${privateMemberName(id)} = new ${kaitaiType2NativeType(ArrayTypeInStream(dataType))}();") } - override def condRepeatEosHeader(id: Identifier, io: String, dataType: DataType): Unit = { + override def condRepeatEosHeader(io: String): Unit = { out.puts("{") out.inc out.puts("var i = 0;") @@ -292,8 +292,8 @@ class CSharpCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) out.puts("}") } - override def condRepeatExprHeader(id: Identifier, io: String, dataType: DataType, repeatExpr: expr): Unit = { - out.puts(s"for (var i = 0; i < ${expression(repeatExpr)}; i++)") + override def condRepeatExprHeader(countExpr: expr): Unit = { + out.puts(s"for (var i = 0, _end = ${expression(countExpr)}; i < _end; ++i)") out.puts("{") out.inc } @@ -303,12 +303,12 @@ class CSharpCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) override def condRepeatExprFooter: Unit = fileFooter(null) - override def condRepeatUntilHeader(id: Identifier, io: String, dataType: DataType, untilExpr: expr): Unit = { + override def condRepeatUntilHeader(itemType: DataType): Unit = { out.puts("{") out.inc out.puts("var i = 0;") - out.puts(s"${kaitaiType2NativeType(dataType)} ${translator.doName("_")};") - out.puts("do {") + out.puts(s"${kaitaiType2NativeType(itemType)} ${translator.doName(Identifier.ITERATOR)};") + out.puts("while (true) {") out.inc } @@ -322,13 +322,17 @@ class CSharpCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) out.puts(s"${privateMemberName(id)}.Add($tempVar);") } - override def condRepeatUntilFooter(id: Identifier, io: String, dataType: DataType, untilExpr: expr): Unit = { - typeProvider._currentIteratorType = Some(dataType) - out.puts("i++;") - out.dec - out.puts(s"} while (!(${expression(untilExpr)}));") + override def condRepeatUntilFooter(untilExpr: expr): Unit = { + out.puts(s"if (${expression(untilExpr)}) {") + out.inc + out.puts("break;") out.dec out.puts("}") + out.puts("++i;") + out.dec + out.puts("}") // close while (true) + out.dec + out.puts("}") // close scope of i and _ variables } override def handleAssignmentSimple(id: Identifier, expr: String): Unit = diff --git a/shared/src/main/scala/io/kaitai/struct/languages/CppCompiler.scala b/shared/src/main/scala/io/kaitai/struct/languages/CppCompiler.scala index dece57247..3ee94dfaa 100644 --- a/shared/src/main/scala/io/kaitai/struct/languages/CppCompiler.scala +++ b/shared/src/main/scala/io/kaitai/struct/languages/CppCompiler.scala @@ -563,7 +563,7 @@ class CppCompiler( outSrc.puts(s"${privateMemberName(id)} = ${newVector(dataType)};") } - override def condRepeatEosHeader(id: Identifier, io: String, dataType: DataType): Unit = { + override def condRepeatEosHeader(io: String): Unit = { outSrc.puts("{") outSrc.inc outSrc.puts("int i = 0;") @@ -583,10 +583,8 @@ class CppCompiler( outSrc.puts("}") } - override def condRepeatExprHeader(id: Identifier, io: String, dataType: DataType, repeatExpr: Ast.expr): Unit = { - val lenVar = s"l_${idToStr(id)}" - outSrc.puts(s"const int $lenVar = ${expression(repeatExpr)};") - outSrc.puts(s"for (int i = 0; i < $lenVar; i++) {") + override def condRepeatExprHeader(countExpr: Ast.expr): Unit = { + outSrc.puts(s"for (int i = 0, _end = ${expression(countExpr)}; i < _end; ++i) {") outSrc.inc } @@ -598,12 +596,12 @@ class CppCompiler( outSrc.puts("}") } - override def condRepeatUntilHeader(id: Identifier, io: String, dataType: DataType, untilExpr: expr): Unit = { + override def condRepeatUntilHeader(itemType: DataType): Unit = { outSrc.puts("{") outSrc.inc outSrc.puts("int i = 0;") - outSrc.puts(s"${kaitaiType2NativeType(dataType.asNonOwning())} ${translator.doName("_")};") - outSrc.puts("do {") + outSrc.puts(s"${kaitaiType2NativeType(itemType.asNonOwning())} ${translator.doName(Identifier.ITERATOR)};") + outSrc.puts("while (true) {") outSrc.inc } @@ -632,13 +630,17 @@ class CppCompiler( outSrc.puts(s"${privateMemberName(id)}->push_back($wrappedTempVar);") } - override def condRepeatUntilFooter(id: Identifier, io: String, dataType: DataType, untilExpr: expr): Unit = { - typeProvider._currentIteratorType = Some(dataType) - outSrc.puts("i++;") - outSrc.dec - outSrc.puts(s"} while (!(${expression(untilExpr)}));") + override def condRepeatUntilFooter(untilExpr: expr): Unit = { + outSrc.puts(s"if (${expression(untilExpr)}) {") + outSrc.inc + outSrc.puts("break;") outSrc.dec outSrc.puts("}") + outSrc.puts("++i;") + outSrc.dec + outSrc.puts("}") // close while (true) + outSrc.dec + outSrc.puts("}") // close scope of i and _ variables } override def handleAssignmentSimple(id: Identifier, expr: String): Unit = { diff --git a/shared/src/main/scala/io/kaitai/struct/languages/GoCompiler.scala b/shared/src/main/scala/io/kaitai/struct/languages/GoCompiler.scala index 514f88d9a..94d3c3707 100644 --- a/shared/src/main/scala/io/kaitai/struct/languages/GoCompiler.scala +++ b/shared/src/main/scala/io/kaitai/struct/languages/GoCompiler.scala @@ -277,12 +277,12 @@ class GoCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) // function works even on `nil` slices (https://go.dev/tour/moretypes/15) } - override def condRepeatEosHeader(id: Identifier, io: String, dataType: DataType): Unit = { + override def condRepeatEosHeader(io: String): Unit = { out.puts(s"for i := 0;; i++ {") out.inc val eofVar = translator.allocateLocalVar() - out.puts(s"${translator.localVarName(eofVar)}, err := this._io.EOF()") + out.puts(s"${translator.localVarName(eofVar)}, err := $io.EOF()") translator.outAddErrCheck() out.puts(s"if ${translator.localVarName(eofVar)} {") out.inc @@ -297,8 +297,8 @@ class GoCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) out.puts(s"$name = append($name, $expr)") } - override def condRepeatExprHeader(id: Identifier, io: String, dataType: DataType, repeatExpr: Ast.expr): Unit = { - out.puts(s"for i := 0; i < int(${expression(repeatExpr)}); i++ {") + override def condRepeatExprHeader(countExpr: Ast.expr): Unit = { + out.puts(s"for i, _end := 0, int(${expression(countExpr)}); i < _end; i++ {") out.inc // FIXME: Go throws a fatal compile error when the `i` variable is not used (unused variables // can only use the blank identifier `_`, see https://go.dev/doc/effective_go#blank), so we have @@ -311,8 +311,11 @@ class GoCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) override def handleAssignmentRepeatExpr(id: Identifier, r: TranslatorResult): Unit = handleAssignmentRepeatEos(id, r) - override def condRepeatUntilHeader(id: Identifier, io: String, dataType: DataType, untilExpr: Ast.expr): Unit = { - out.puts(s"for i := 1;; i++ {") + override def condRepeatUntilHeader(itemType: DataType): Unit = { + out.puts("{") + out.inc + out.puts("i := 0") + out.puts(s"for {") out.inc } @@ -323,15 +326,17 @@ class GoCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) out.puts(s"${privateMemberName(id)} = append(${privateMemberName(id)}, $tempVar)") } - override def condRepeatUntilFooter(id: Identifier, io: String, dataType: DataType, untilExpr: Ast.expr): Unit = { - typeProvider._currentIteratorType = Some(dataType) + override def condRepeatUntilFooter(untilExpr: Ast.expr): Unit = { out.puts(s"if ${expression(untilExpr)} {") out.inc out.puts("break") out.dec out.puts("}") + out.puts("i++;") out.dec - out.puts("}") + out.puts("}") // close for + out.dec + out.puts("}") // close scope of i variable } private def castToType(r: TranslatorResult, dataType: DataType): TranslatorResult = { diff --git a/shared/src/main/scala/io/kaitai/struct/languages/JavaCompiler.scala b/shared/src/main/scala/io/kaitai/struct/languages/JavaCompiler.scala index dd23e9234..d1b845a3f 100644 --- a/shared/src/main/scala/io/kaitai/struct/languages/JavaCompiler.scala +++ b/shared/src/main/scala/io/kaitai/struct/languages/JavaCompiler.scala @@ -560,7 +560,7 @@ class JavaCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) out.puts(s"${privateMemberName(id)} = new ArrayList<${kaitaiType2JavaTypeBoxed(dataType)}>();") } - override def condRepeatEosHeader(id: Identifier, io: String, dataType: DataType): Unit = { + override def condRepeatEosHeader(io: String): Unit = { out.puts("{") out.inc out.puts("int i = 0;") @@ -580,8 +580,8 @@ class JavaCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) out.puts("}") } - override def condRepeatExprHeader(id: Identifier, io: String, dataType: DataType, repeatExpr: expr): Unit = { - out.puts(s"for (int i = 0; i < ${expression(repeatExpr)}; i++) {") + override def condRepeatExprHeader(countExpr: expr): Unit = { + out.puts(s"for (int i = 0, _end = ${expression(countExpr)}; i < _end; ++i) {") out.inc } @@ -594,12 +594,12 @@ class JavaCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) override def handleAssignmentRepeatExpr(id: Identifier, expr: String): Unit = handleAssignmentRepeatEos(id, expr) - override def condRepeatUntilHeader(id: Identifier, io: String, dataType: DataType, untilExpr: expr): Unit = { + override def condRepeatUntilHeader(itemType: DataType): Unit = { out.puts("{") out.inc - out.puts(s"${kaitaiType2JavaType(dataType)} ${translator.doName(Identifier.ITERATOR)};") + out.puts(s"${kaitaiType2JavaType(itemType)} ${translator.doName(Identifier.ITERATOR)};") out.puts("int i = 0;") - out.puts("do {") + out.puts("while (true) {") out.inc } @@ -613,13 +613,17 @@ class JavaCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) out.puts(s"${privateMemberName(id)}.add($tempVar);") } - override def condRepeatUntilFooter(id: Identifier, io: String, dataType: DataType, untilExpr: expr): Unit = { - typeProvider._currentIteratorType = Some(dataType) - out.puts("i++;") - out.dec - out.puts(s"} while (!(${expression(untilExpr)}));") + override def condRepeatUntilFooter(untilExpr: expr): Unit = { + out.puts(s"if (${expression(untilExpr)}) {") + out.inc + out.puts("break;") out.dec out.puts("}") + out.puts("++i;") + out.dec + out.puts("}") // close while (true) + out.dec + out.puts("}") // close scope of i and _ variables } override def handleAssignmentSimple(id: Identifier, expr: String): Unit = diff --git a/shared/src/main/scala/io/kaitai/struct/languages/JavaScriptCompiler.scala b/shared/src/main/scala/io/kaitai/struct/languages/JavaScriptCompiler.scala index b4f2c74f8..c59c42946 100644 --- a/shared/src/main/scala/io/kaitai/struct/languages/JavaScriptCompiler.scala +++ b/shared/src/main/scala/io/kaitai/struct/languages/JavaScriptCompiler.scala @@ -293,7 +293,7 @@ class JavaScriptCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) override def condRepeatInitAttr(id: Identifier, dataType: DataType): Unit = out.puts(s"${privateMemberName(id)} = [];") - override def condRepeatEosHeader(id: Identifier, io: String, dataType: DataType): Unit = { + override def condRepeatEosHeader(io: String): Unit = { out.puts("var i = 0;") out.puts(s"while (!$io.isEof()) {") out.inc @@ -309,8 +309,8 @@ class JavaScriptCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) out.puts("}") } - override def condRepeatExprHeader(id: Identifier, io: String, dataType: DataType, repeatExpr: expr): Unit = { - out.puts(s"for (var i = 0; i < ${expression(repeatExpr)}; i++) {") + override def condRepeatExprHeader(countExpr: expr): Unit = { + out.puts(s"for (var i = 0, _end = ${expression(countExpr)}; i < _end; ++i) {") out.inc } @@ -322,9 +322,10 @@ class JavaScriptCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) out.puts("}") } - override def condRepeatUntilHeader(id: Identifier, io: String, dataType: DataType, untilExpr: expr): Unit = { + override def condRepeatUntilHeader(itemType: DataType): Unit = { + // "var" variables in any case have a scope of surrounding function, no need a scope to isolate them out.puts("var i = 0;") - out.puts("do {") + out.puts("while (true) {") out.inc } @@ -334,11 +335,15 @@ class JavaScriptCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) out.puts(s"${privateMemberName(id)}.push($tmpName);") } - override def condRepeatUntilFooter(id: Identifier, io: String, dataType: DataType, untilExpr: expr): Unit = { - typeProvider._currentIteratorType = Some(dataType) - out.puts("i++;") + override def condRepeatUntilFooter(untilExpr: expr): Unit = { + out.puts(s"if (${expression(untilExpr)}) {") + out.inc + out.puts("break;") + out.dec + out.puts("}") + out.puts("++i;") out.dec - out.puts(s"} while (!(${expression(untilExpr)}));") + out.puts("}") // close while (true) } override def handleAssignmentSimple(id: Identifier, expr: String): Unit = { diff --git a/shared/src/main/scala/io/kaitai/struct/languages/LuaCompiler.scala b/shared/src/main/scala/io/kaitai/struct/languages/LuaCompiler.scala index bd6a3f01f..0310650f1 100644 --- a/shared/src/main/scala/io/kaitai/struct/languages/LuaCompiler.scala +++ b/shared/src/main/scala/io/kaitai/struct/languages/LuaCompiler.scala @@ -166,7 +166,7 @@ class LuaCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) out.puts(s"${privateMemberName(id)} = {}") } - override def condRepeatEosHeader(id: Identifier, io: String, dataType: DataType): Unit = { + override def condRepeatEosHeader(io: String): Unit = { out.puts("local i = 0") out.puts(s"while not $io:is_eof() do") out.inc @@ -177,8 +177,8 @@ class LuaCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) out.puts("end") } - override def condRepeatExprHeader(id: Identifier, io: String, dataType: DataType, repeatExpr: Ast.expr): Unit = { - out.puts(s"for i = 0, ${expression(repeatExpr)} - 1 do") + override def condRepeatExprHeader(countExpr: Ast.expr): Unit = { + out.puts(s"for i = 0, ${expression(countExpr)} - 1 do") out.inc } override def condRepeatExprFooter: Unit = { @@ -186,13 +186,13 @@ class LuaCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) out.puts("end") } - override def condRepeatUntilHeader(id: Identifier, io: String, dataType: DataType, untilExpr: Ast.expr): Unit = { + override def condRepeatUntilHeader(itemType: DataType): Unit = { + // Lua allows shadowing of variables, no need a scope to isolate them out.puts("local i = 0") out.puts("while true do") out.inc } - override def condRepeatUntilFooter(id: Identifier, io: String, dataType: DataType, untilExpr: Ast.expr): Unit = { - typeProvider._currentIteratorType = Some(dataType) + override def condRepeatUntilFooter(untilExpr: Ast.expr): Unit = { out.puts(s"if ${expression(untilExpr)} then") out.inc out.puts("break") diff --git a/shared/src/main/scala/io/kaitai/struct/languages/NimCompiler.scala b/shared/src/main/scala/io/kaitai/struct/languages/NimCompiler.scala index efc0fc5eb..6d17376c5 100644 --- a/shared/src/main/scala/io/kaitai/struct/languages/NimCompiler.scala +++ b/shared/src/main/scala/io/kaitai/struct/languages/NimCompiler.scala @@ -149,7 +149,7 @@ class NimCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) // empty sequences (see https://narimiran.github.io/nim-basics/#_result_variable) } - override def condRepeatEosHeader(id: Identifier, io: String, dataType: DataType): Unit = { + override def condRepeatEosHeader(io: String): Unit = { out.puts("block:") out.inc out.puts("var i: int") @@ -161,19 +161,18 @@ class NimCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) out.dec out.dec } - override def condRepeatExprHeader(id: Identifier, io: String, dataType: DataType, repeatExpr: Ast.expr): Unit = { - out.puts(s"for i in 0 ..< int(${expression(repeatExpr)}):") + override def condRepeatExprHeader(countExpr: Ast.expr): Unit = { + out.puts(s"for i in 0 ..< int(${expression(countExpr)}):") out.inc } - override def condRepeatUntilHeader(id: Identifier, io: String, dataType: DataType, untilExpr: Ast.expr): Unit = { + override def condRepeatUntilHeader(itemType: DataType): Unit = { out.puts("block:") out.inc out.puts("var i: int") out.puts("while true:") out.inc } - override def condRepeatUntilFooter(id: Identifier, io: String, dataType: DataType, untilExpr: Ast.expr): Unit = { - typeProvider._currentIteratorType = Some(dataType) + override def condRepeatUntilFooter(untilExpr: Ast.expr): Unit = { out.puts(s"if ${expression(untilExpr)}:") out.inc out.puts("break") diff --git a/shared/src/main/scala/io/kaitai/struct/languages/PHPCompiler.scala b/shared/src/main/scala/io/kaitai/struct/languages/PHPCompiler.scala index 8515eb1d5..482dcce99 100644 --- a/shared/src/main/scala/io/kaitai/struct/languages/PHPCompiler.scala +++ b/shared/src/main/scala/io/kaitai/struct/languages/PHPCompiler.scala @@ -274,7 +274,7 @@ class PHPCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) override def condRepeatInitAttr(id: Identifier, dataType: DataType): Unit = out.puts(s"${privateMemberName(id)} = [];") - override def condRepeatEosHeader(id: Identifier, io: String, dataType: DataType): Unit = { + override def condRepeatEosHeader(io: String): Unit = { out.puts("$i = 0;") out.puts(s"while (!$io->isEof()) {") out.inc @@ -289,8 +289,8 @@ class PHPCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) super.condRepeatEosFooter } - override def condRepeatExprHeader(id: Identifier, io: String, dataType: DataType, repeatExpr: Ast.expr): Unit = { - out.puts(s"$$n = ${expression(repeatExpr)};") + override def condRepeatExprHeader(countExpr: Ast.expr): Unit = { + out.puts(s"$$n = ${expression(countExpr)};") out.puts("for ($i = 0; $i < $n; $i++) {") out.inc } @@ -298,9 +298,9 @@ class PHPCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) override def handleAssignmentRepeatExpr(id: Identifier, expr: String): Unit = handleAssignmentRepeatEos(id, expr) - override def condRepeatUntilHeader(id: Identifier, io: String, dataType: DataType, untilExpr: Ast.expr): Unit = { + override def condRepeatUntilHeader(itemType: DataType): Unit = { out.puts("$i = 0;") - out.puts("do {") + out.puts("while (1) {") out.inc } @@ -310,11 +310,17 @@ class PHPCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) out.puts(s"${privateMemberName(id)}[] = $tmpName;") } - override def condRepeatUntilFooter(id: Identifier, io: String, dataType: DataType, untilExpr: Ast.expr): Unit = { - typeProvider._currentIteratorType = Some(dataType) + override def condRepeatUntilFooter(untilExpr: Ast.expr): Unit = { + out.puts(s"if ${expression(untilExpr)} {") + out.inc + out.puts("break;") + out.dec + out.puts("}") out.puts("$i++;") out.dec - out.puts(s"} while (!(${expression(untilExpr)}));") + out.puts("}") // close while (1) + out.dec + out.puts("}") // close scope of i variable } override def handleAssignmentSimple(id: Identifier, expr: String): Unit = { diff --git a/shared/src/main/scala/io/kaitai/struct/languages/PerlCompiler.scala b/shared/src/main/scala/io/kaitai/struct/languages/PerlCompiler.scala index ed58d9337..ff65d22bd 100644 --- a/shared/src/main/scala/io/kaitai/struct/languages/PerlCompiler.scala +++ b/shared/src/main/scala/io/kaitai/struct/languages/PerlCompiler.scala @@ -237,7 +237,9 @@ class PerlCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) override def condRepeatInitAttr(id: Identifier, dataType: DataType): Unit = out.puts(s"${privateMemberName(id)} = [];") - override def condRepeatEosHeader(id: Identifier, io: String, dataType: DataType): Unit = { + override def condRepeatEosHeader(io: String): Unit = { + // Perl allows shadowing of variables, no need a scope to isolate them + out.puts("my $i = 0;") out.puts(s"while (!$io->is_eof()) {") out.inc } @@ -245,20 +247,24 @@ class PerlCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) override def handleAssignmentRepeatEos(id: Identifier, expr: String): Unit = out.puts(s"push @{${privateMemberName(id)}}, $expr;") - override def condRepeatExprHeader(id: Identifier, io: String, dataType: DataType, repeatExpr: expr): Unit = { - val nVar = s"$$n_${idToStr(id)}" - out.puts(s"my $nVar = ${expression(repeatExpr)};") - out.puts(s"for (my $$i = 0; $$i < $nVar; $$i++) {") + override def condRepeatEosFooter: Unit = { + out.puts("$i++;") + out.dec + out.puts("}") + } + + override def condRepeatExprHeader(countExpr: expr): Unit = { + out.puts(s"for (my $$i = 0, $$_end = ${expression(countExpr)}; $$i < $$_end; ++$$i) {") out.inc } override def handleAssignmentRepeatExpr(id: Identifier, expr: String): Unit = handleAssignmentRepeatEos(id, expr) - override def condRepeatUntilHeader(id: Identifier, io: String, dataType: DataType, untilExpr: expr): Unit = { - blockScopeHeader - out.puts(s"my ${translator.doName("_")};") - out.puts("do {") + override def condRepeatUntilHeader(itemType: DataType): Unit = { + // Perl allows shadowing of variables, no need a scope to isolate them + out.puts("my $i = 0;") + out.puts("while (1) {") out.inc } @@ -272,11 +278,11 @@ class PerlCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) out.puts(s"push @{${privateMemberName(id)}}, $tmpName;") } - override def condRepeatUntilFooter(id: Identifier, io: String, dataType: DataType, untilExpr: expr): Unit = { - typeProvider._currentIteratorType = Some(dataType) + override def condRepeatUntilFooter(untilExpr: expr): Unit = { + out.puts(s"last if (${expression(untilExpr)});") + out.puts("$i++;") out.dec - out.puts(s"} until (${expression(untilExpr)});") - blockScopeFooter + out.puts("}") // close while (1) } override def handleAssignmentSimple(id: Identifier, expr: String): Unit = @@ -379,7 +385,6 @@ class PerlCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) override def switchRequiresIfs(onType: DataType): Boolean = true override def switchIfStart(id: Identifier, on: Ast.expr, onType: DataType): Unit = { - typeProvider._currentSwitchType = Some(translator.detectType(on)) out.puts(s"my $$_on = ${expression(on)};") } diff --git a/shared/src/main/scala/io/kaitai/struct/languages/PythonCompiler.scala b/shared/src/main/scala/io/kaitai/struct/languages/PythonCompiler.scala index d9a28bd37..00b2f7399 100644 --- a/shared/src/main/scala/io/kaitai/struct/languages/PythonCompiler.scala +++ b/shared/src/main/scala/io/kaitai/struct/languages/PythonCompiler.scala @@ -526,7 +526,7 @@ class PythonCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) override def condRepeatInitAttr(id: Identifier, dataType: DataType): Unit = out.puts(s"${privateMemberName(id)} = []") - override def condRepeatEosHeader(id: Identifier, io: String, dataType: DataType): Unit = { + override def condRepeatEosHeader(io: String): Unit = { out.puts("i = 0") out.puts(s"while not $io.is_eof():") out.inc @@ -539,8 +539,8 @@ class PythonCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) universalFooter } - override def condRepeatExprHeader(id: Identifier, io: String, dataType: DataType, repeatExpr: expr): Unit = { - out.puts(s"for i in range(${expression(repeatExpr)}):") + override def condRepeatExprHeader(countExpr: expr): Unit = { + out.puts(s"for i in range(${expression(countExpr)}):") out.inc } @@ -556,7 +556,7 @@ class PythonCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) override def handleAssignmentRepeatExpr(id: Identifier, expr: String): Unit = handleAssignmentRepeatEos(id, expr) - override def condRepeatUntilHeader(id: Identifier, io: String, dataType: DataType, untilExpr: expr): Unit = { + override def condRepeatUntilHeader(itemType: DataType): Unit = { out.puts("i = 0") out.puts("while True:") out.inc @@ -568,8 +568,7 @@ class PythonCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) out.puts(s"${privateMemberName(id)}.append($tmpName)") } - override def condRepeatUntilFooter(id: Identifier, io: String, dataType: DataType, untilExpr: expr): Unit = { - typeProvider._currentIteratorType = Some(dataType) + override def condRepeatUntilFooter(untilExpr: expr): Unit = { out.puts(s"if ${expression(untilExpr)}:") out.inc out.puts("break") diff --git a/shared/src/main/scala/io/kaitai/struct/languages/RubyCompiler.scala b/shared/src/main/scala/io/kaitai/struct/languages/RubyCompiler.scala index 51669b288..631b30d42 100644 --- a/shared/src/main/scala/io/kaitai/struct/languages/RubyCompiler.scala +++ b/shared/src/main/scala/io/kaitai/struct/languages/RubyCompiler.scala @@ -298,7 +298,7 @@ class RubyCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) override def condRepeatInitAttr(id: Identifier, dataType: DataType): Unit = out.puts(s"${privateMemberName(id)} = []") - override def condRepeatEosHeader(id: Identifier, io: String, dataType: DataType): Unit = { + override def condRepeatEosHeader(io: String): Unit = { out.puts("i = 0") out.puts(s"while not $io.eof?") out.inc @@ -311,8 +311,8 @@ class RubyCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) super.condRepeatEosFooter } - override def condRepeatExprHeader(id: Identifier, io: String, dataType: DataType, repeatExpr: expr): Unit = { - out.puts(s"(${expression(repeatExpr)}).times { |i|") + override def condRepeatExprHeader(countExpr: expr): Unit = { + out.puts(s"(${expression(countExpr)}).times { |i|") out.inc } override def handleAssignmentRepeatExpr(id: Identifier, expr: String): Unit = @@ -323,9 +323,9 @@ class RubyCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) out.puts("}") } - override def condRepeatUntilHeader(id: Identifier, io: String, dataType: DataType, untilExpr: expr): Unit = { + override def condRepeatUntilHeader(itemType: DataType): Unit = { out.puts("i = 0") - out.puts("begin") + out.puts("while true") out.inc } @@ -335,11 +335,11 @@ class RubyCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) out.puts(s"${privateMemberName(id)} << $tmpName") } - override def condRepeatUntilFooter(id: Identifier, io: String, dataType: DataType, untilExpr: expr): Unit = { - typeProvider._currentIteratorType = Some(dataType) + override def condRepeatUntilFooter(untilExpr: expr): Unit = { + out.puts(s"break if ${expression(untilExpr)}") out.puts("i += 1") out.dec - out.puts(s"end until ${expression(untilExpr)}") + out.puts("end") } override def handleAssignmentSimple(id: Identifier, expr: String): Unit = diff --git a/shared/src/main/scala/io/kaitai/struct/languages/RustCompiler.scala b/shared/src/main/scala/io/kaitai/struct/languages/RustCompiler.scala index 35dada301..3731594e5 100644 --- a/shared/src/main/scala/io/kaitai/struct/languages/RustCompiler.scala +++ b/shared/src/main/scala/io/kaitai/struct/languages/RustCompiler.scala @@ -272,19 +272,14 @@ class RustCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) out.inc } - override def condRepeatInitAttr(id: Identifier, dataType: DataType): Unit = { - // this line required for handleAssignmentRepeatUntil - typeProvider._currentIteratorType = Some(dataType) + override def condRepeatInitAttr(id: Identifier, dataType: DataType): Unit = { out.puts(s"*${RustCompiler.privateMemberName(id, writeAccess = true)} = Vec::new();") } - override def condRepeatEosHeader(id: Identifier, - io: String, - dataType: DataType): Unit = { - out.puts("{") - out.inc + override def condRepeatEosHeader(io: String): Unit = { + // Rust allows shadowing of variables, no need a scope to isolate them out.puts(s"let mut _i = 0;") - out.puts(s"while !_io.is_eof() {") + out.puts(s"while !$io.is_eof() {") out.inc } @@ -296,28 +291,18 @@ class RustCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) out.puts("_i += 1;") out.dec out.puts("}") - out.dec - out.puts("}") } - override def condRepeatExprHeader(id: Identifier, - io: String, - dataType: DataType, - repeatExpr: Ast.expr): Unit = { - val lenVar = s"l_${idToStr(id)}" - out.puts(s"let $lenVar = ${expression(repeatExpr)};") - out.puts(s"for _i in 0..$lenVar {") + override def condRepeatExprHeader(countExpr: Ast.expr): Unit = { + out.puts(s"let _end = ${expression(countExpr)};") + out.puts(s"for _i in 0.._end {") out.inc } - override def condRepeatUntilHeader(id: Identifier, - io: String, - dataType: DataType, - repeatExpr: Ast.expr): Unit = { - out.puts("{") - out.inc + override def condRepeatUntilHeader(itemType: DataType): Unit = { + // Rust allows shadowing of variables, no need a scope to isolate them out.puts("let mut _i = 0;") - out.puts("while {") + out.puts("loop {") out.inc } @@ -330,7 +315,8 @@ class RustCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) isRaw: Boolean): Unit = { out.puts(s"${RustCompiler.privateMemberName(id, writeAccess = true)}.push($expr);") var copy_type = "" - if (typeProvider._currentIteratorType.isDefined && translator.is_copy_type(typeProvider._currentIteratorType.get)) { + // typeProvider._typeOfUnderscore is set in CommonReads.attrParse0 + if (translator.is_copy_type(typeProvider._typeOfUnderscore.get)) { copy_type = "*" } val t = localTemporaryName(id) @@ -338,19 +324,15 @@ class RustCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) out.puts(s"let ${translator.doLocalName(Identifier.ITERATOR)} = $copy_type$t.last().unwrap();") } - override def condRepeatUntilFooter(id: Identifier, - io: String, - dataType: DataType, - repeatExpr: Ast.expr): Unit = { - // this line required by kaitai code - typeProvider._currentIteratorType = Some(dataType) - out.puts("_i += 1;") - out.puts(s"let x = !(${expression(repeatExpr)});") - out.puts("x") - out.dec - out.puts("} {}") + override def condRepeatUntilFooter(untilExpr: Ast.expr): Unit = { + out.puts(s"if ${expression(untilExpr)} {") + out.inc + out.puts("break") out.dec out.puts("}") + out.puts("_i += 1;") + out.dec + out.puts("}") // close loop } def getRawIdExpr(varName: Identifier, rep: RepeatSpec): String = { diff --git a/shared/src/main/scala/io/kaitai/struct/languages/ZigCompiler.scala b/shared/src/main/scala/io/kaitai/struct/languages/ZigCompiler.scala index 9fba224a5..906b2ebc1 100644 --- a/shared/src/main/scala/io/kaitai/struct/languages/ZigCompiler.scala +++ b/shared/src/main/scala/io/kaitai/struct/languages/ZigCompiler.scala @@ -300,7 +300,7 @@ class ZigCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) case _ => expr } - override def condRepeatEosHeader(id: Identifier, io: String, dataType: DataType): Unit = { + override def condRepeatEosHeader(io: String): Unit = { out.puts("{") out.inc out.puts("var i: usize = 0;") @@ -319,8 +319,8 @@ class ZigCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) out.puts("}") } - override def condRepeatExprHeader(id: Identifier, io: String, dataType: DataType, repeatExpr: expr): Unit = { - out.puts(s"for (0..${expression(repeatExpr)}) |i| {") + override def condRepeatExprHeader(countExpr: expr): Unit = { + out.puts(s"for (0..${expression(countExpr)}) |i| {") out.inc // NOTE: Zig would refuse to compile the code with an "error: unused capture" if the `i` // variable wasn't used in any way. In hand-written code, it's easy to deal with that by @@ -341,7 +341,7 @@ class ZigCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) override def handleAssignmentRepeatExpr(id: Identifier, expr: String): Unit = handleAssignmentRepeatEos(id, expr) - override def condRepeatUntilHeader(id: Identifier, io: String, dataType: DataType, untilExpr: expr): Unit = { + override def condRepeatUntilHeader(itemType: DataType): Unit = { out.puts("{") out.inc out.puts("var i: usize = 0;") @@ -355,8 +355,7 @@ class ZigCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) out.puts(s"try ${privateMemberName(id)}.append(self._allocator(), $tmpName);") } - override def condRepeatUntilFooter(id: Identifier, io: String, dataType: DataType, untilExpr: expr): Unit = { - typeProvider._currentIteratorType = Some(dataType) + override def condRepeatUntilFooter(untilExpr: expr): Unit = { out.puts(s"if (${expression(untilExpr)}) {") out.inc out.puts("break;") diff --git a/shared/src/main/scala/io/kaitai/struct/languages/components/CommonReads.scala b/shared/src/main/scala/io/kaitai/struct/languages/components/CommonReads.scala index 4c9ed3085..46f518be2 100644 --- a/shared/src/main/scala/io/kaitai/struct/languages/components/CommonReads.scala +++ b/shared/src/main/scala/io/kaitai/struct/languages/components/CommonReads.scala @@ -64,11 +64,11 @@ trait CommonReads extends LanguageCompiler { (ExtraAttrs.forAttr(attr, this) ++ List(attr)).foreach(a => condRepeatInitAttr(a.id, a.dataType)) attr.cond.repeat match { case RepeatEos => - condRepeatEosHeader(id, io, attr.dataType) - case RepeatExpr(repeatExpr: Ast.expr) => - condRepeatExprHeader(id, io, attr.dataType, repeatExpr) - case RepeatUntil(untilExpr: Ast.expr) => - condRepeatUntilHeader(id, io, attr.dataType, untilExpr) + condRepeatEosHeader(io) + case RepeatExpr(countExpr: Ast.expr) => + condRepeatExprHeader(countExpr) + case RepeatUntil(_) => + condRepeatUntilHeader(attr.dataType) case NoRepeat => } @@ -88,7 +88,9 @@ trait CommonReads extends LanguageCompiler { case _: RepeatExpr => condRepeatExprFooter case RepeatUntil(untilExpr: Ast.expr) => - condRepeatUntilFooter(id, io, attr.dataType, untilExpr) + // Set the type of the `_` variable in expression + typeProvider._typeOfUnderscore = Some(attr.dataType) + condRepeatUntilFooter(untilExpr) case NoRepeat => } } diff --git a/shared/src/main/scala/io/kaitai/struct/languages/components/GenericChecks.scala b/shared/src/main/scala/io/kaitai/struct/languages/components/GenericChecks.scala index 084abe82d..2b0686947 100644 --- a/shared/src/main/scala/io/kaitai/struct/languages/components/GenericChecks.scala +++ b/shared/src/main/scala/io/kaitai/struct/languages/components/GenericChecks.scala @@ -224,7 +224,8 @@ trait GenericChecks extends LanguageCompiler with EveryReadIsExpression { def attrValidCheck(attr: AttrLikeSpec, shouldDependOnIo: Option[Boolean]): Unit = { attr.valid.foreach { (valid) => - typeProvider._currentIteratorType = Some(attr.dataType) + // Set the type of the `_` variable in expression + typeProvider._typeOfUnderscore = Some(attr.dataType) if (shouldDependOnIo.map(shouldDepend => validDependsOnIo(valid) == shouldDepend).getOrElse(true)) { attrValidate(attr, valid, false) } @@ -515,7 +516,8 @@ trait GenericChecks extends LanguageCompiler with EveryReadIsExpression { } def attrAssertUntilCond(id: Identifier, dataType: DataType, repUntil: RepeatUntil, shouldDependOnIo: Option[Boolean]): Unit = { - typeProvider._currentIteratorType = Some(dataType) + // Set the type of the `_` variable in expression + typeProvider._typeOfUnderscore = Some(dataType) if (shouldDependOnIo.map(shouldDepend => userExprDependsOnIo(repUntil.expr) != shouldDepend).getOrElse(false)) return blockScopeHeader diff --git a/shared/src/main/scala/io/kaitai/struct/languages/components/LanguageCompiler.scala b/shared/src/main/scala/io/kaitai/struct/languages/components/LanguageCompiler.scala index 7f09b1606..579e1d929 100644 --- a/shared/src/main/scala/io/kaitai/struct/languages/components/LanguageCompiler.scala +++ b/shared/src/main/scala/io/kaitai/struct/languages/components/LanguageCompiler.scala @@ -150,14 +150,47 @@ abstract class LanguageCompiler( def condRepeatInitAttr(id: Identifier, dataType: DataType): Unit - def condRepeatEosHeader(id: Identifier, io: String, dataType: DataType): Unit + /** + * Generates start of loop until end-of-stream is reached. + * + * @param io rendered expression that evaluates to reference of a stream which + * should be checked + */ + def condRepeatEosHeader(io: String): Unit + /** + * Generates end of loop until end-of-stream is reached. + */ def condRepeatEosFooter: Unit - def condRepeatExprHeader(id: Identifier, io: String, dataType: DataType, repeatExpr: Ast.expr): Unit + /** + * Generates start of "for 0.." loop which loops specified count of times. + * + * @param countExpr expression that evaluates into number of repetitions. + * That expression should be evaluated only once before loop is started + */ + def condRepeatExprHeader(countExpr: Ast.expr): Unit + /** + * Generates end of "for 0.." loop. + */ def condRepeatExprFooter: Unit - def condRepeatUntilHeader(id: Identifier, io: String, dataType: DataType, untilExpr: Ast.expr): Unit - def condRepeatUntilFooter(id: Identifier, io: String, dataType: DataType, untilExpr: Ast.expr): Unit + /** + * Generates start of "repeat ... until" loop which parses element of type `itemType` + * on each iteration. + * + * @param itemType Type of the element that can be used to declare variable for + * holding that element so it will accessible in the condition in the footer. + * In most languages condition in special "repeat ... until" loop cannot + * access variables, defined in body of loop + */ + def condRepeatUntilHeader(itemType: DataType): Unit + /** + * Generates end of "repeat ... until" loop which checks the specified condition. + * + * @param untilExpr condition that evaluates to boolean value where `true` means + * that loop will be finished + */ + def condRepeatUntilFooter(untilExpr: Ast.expr): Unit def condRepeatCommonHeader(id: Identifier, io: String, dataType: DataType): Unit = {} def condRepeatCommonFooter: Unit = {} diff --git a/shared/src/main/scala/io/kaitai/struct/languages/components/ValidateOps.scala b/shared/src/main/scala/io/kaitai/struct/languages/components/ValidateOps.scala index a771d1d72..b4d593584 100644 --- a/shared/src/main/scala/io/kaitai/struct/languages/components/ValidateOps.scala +++ b/shared/src/main/scala/io/kaitai/struct/languages/components/ValidateOps.scala @@ -55,7 +55,8 @@ trait ValidateOps extends ExceptionNames { ) case ValidationExpr(expr) => blockScopeHeader - typeProvider._currentIteratorType = Some(attr.dataType) + // Set the type of the `_` variable in expression + typeProvider._typeOfUnderscore = Some(attr.dataType) handleAssignmentTempVar( attr.dataType, translator.translate(Ast.expr.Name(Ast.identifier(Identifier.ITERATOR))), diff --git a/shared/src/main/scala/io/kaitai/struct/precompile/Exceptions.scala b/shared/src/main/scala/io/kaitai/struct/precompile/Exceptions.scala index 1ac6035bb..3a5a9e6e3 100644 --- a/shared/src/main/scala/io/kaitai/struct/precompile/Exceptions.scala +++ b/shared/src/main/scala/io/kaitai/struct/precompile/Exceptions.scala @@ -8,7 +8,7 @@ import io.kaitai.struct.translators.MethodArgType * Base class for all expression-related errors, not localized to a certain path * in source file. */ -sealed abstract class ExpressionError(msg: String) extends RuntimeException(msg) +sealed class ExpressionError(msg: String) extends RuntimeException(msg) class TypeMismatchError(msg: String) extends ExpressionError(msg) class TypeUndecidedError(msg: String) extends ExpressionError(msg) class WrongMethodCall(val dataType: MethodArgType, val methodName: String, val expectedSigs: Iterable[String], val actualSig: String) diff --git a/shared/src/main/scala/io/kaitai/struct/precompile/TypeValidator.scala b/shared/src/main/scala/io/kaitai/struct/precompile/TypeValidator.scala index e3e1b6dd1..4536324c7 100644 --- a/shared/src/main/scala/io/kaitai/struct/precompile/TypeValidator.scala +++ b/shared/src/main/scala/io/kaitai/struct/precompile/TypeValidator.scala @@ -70,11 +70,12 @@ class TypeValidator(specs: ClassSpecs) extends PrecompileStep { checkAssert[BooleanType](ifExpr, "boolean", path, "if") ) - provider._currentIteratorType = Some(attr.dataType) val problemsRepeat: Iterable[CompilationProblem] = attr.cond.repeat match { case RepeatExpr(expr) => checkAssert[IntType](expr, "integer", path, "repeat-expr") case RepeatUntil(expr) => + // Set the type of the `_` variable in expression + provider._typeOfUnderscore = Some(attr.dataType) checkAssert[BooleanType](expr, "boolean", path, "repeat-until") case RepeatEos | NoRepeat => None