Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
cf4b7cd
Document `_currentIteratorType` and `_currentSwitchType` fields
Mingun Sep 15, 2024
2ec3394
Raise human-readable error in case of incorrect usage of `_on` or `_`…
Mingun Nov 25, 2021
55b3fd4
Perl: Remove excess set of `_currentSwitchType`, it is already set in…
Mingun Sep 15, 2024
0f1dc55
Move set of type of `_` from each compiler implementation to the comm…
Mingun Sep 15, 2024
09b803f
Rust: Remove unnecessary set of type of `_` variable, it is already s…
Mingun Sep 15, 2024
425ad40
Rust: no need to check that type of `_` is defined, it MUST be define…
Mingun Sep 15, 2024
0a2c4a1
Set type of `_` for type validation only in `repeat-until` loops wher…
Mingun Sep 15, 2024
2d19bcd
Rename _currentIteratorType -> _typeOfUnderscore
Mingun Sep 15, 2024
81023ab
Use Identifier.ITERATOR instead of "_"
Mingun Sep 15, 2024
502cd44
Remove unused parameters from `condRepeatUntilHeader` and `condRepeat…
Mingun Sep 15, 2024
34e8cde
Document `condRepeatUntilHeader` and `condRepeatUntilFooter` methods
Mingun Sep 15, 2024
ee79a98
Fix inconsistency in values of `_index` variable in loops
Mingun Sep 15, 2024
6a25ca2
Always calculate count of iterations only once in `repeat-expr` loops
Mingun Sep 15, 2024
1746704
Remove unused parameters from `condRepeatExprHeader` and document `co…
Mingun Sep 15, 2024
9ef6085
Perl: declare `_index` variable in `repeat: eos` loops
Mingun Sep 15, 2024
d44726f
Go, Rust: Fix usage of incorrect stream in `repeat: eos` loops when `…
Mingun Sep 15, 2024
3fb5790
Remove unused parameters from `condRepeatEosHeader` and document `con…
Mingun Sep 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 20 additions & 7 deletions shared/src/main/scala/io/kaitai/struct/ClassTypeProvider.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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.<case>` 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)
Expand All @@ -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.<case>' expressions")
}
case Identifier.INDEX =>
CalcIntType
case Identifier.SIZEOF =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -212,7 +213,8 @@ class GraphvizClassCompiler(classSpecs: ClassSpecs, topClass: ClassSpec) extends
expression(ex, s"$currentTable:$portName", STYLE_EDGE_REPEAT) +
" times</TD></TR>")
case RepeatUntil(ex) =>
provider._currentIteratorType = Some(dataType)
// Set the type of the `_` variable in expression
provider._typeOfUnderscore = Some(dataType)
out.puts("<TR><TD COLSPAN=\"4\" PORT=\"" + portName + "\">repeat until " +
expression(ex, s"$currentTable:$portName", STYLE_EDGE_REPEAT) +
"</TD></TR>")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;")
Expand All @@ -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
}
Expand All @@ -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
}

Expand All @@ -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 =
Expand Down
28 changes: 15 additions & 13 deletions shared/src/main/scala/io/kaitai/struct/languages/CppCompiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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;")
Expand All @@ -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
}

Expand All @@ -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
}

Expand Down Expand Up @@ -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 = {
Expand Down
23 changes: 14 additions & 9 deletions shared/src/main/scala/io/kaitai/struct/languages/GoCompiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
}

Expand All @@ -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 = {
Expand Down
26 changes: 15 additions & 11 deletions shared/src/main/scala/io/kaitai/struct/languages/JavaCompiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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;")
Expand All @@ -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
}

Expand All @@ -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
}

Expand All @@ -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 =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}

Expand All @@ -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
}

Expand All @@ -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 = {
Expand Down
Loading
Loading