Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Give a more natural names to the Enum-related AST nodes #224

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -133,12 +133,12 @@ class ExpressionsSpec extends AnyFunSpec {

// Enums
it("parses port::http") {
Expressions.parse("port::http") should be (EnumByLabel(identifier("port"), identifier("http")))
Expressions.parse("port::http") should be (EnumVariant(identifier("port"), identifier("http")))
}

it("parses some_type::port::http") {
Expressions.parse("some_type::port::http") should be (
EnumByLabel(
EnumVariant(
identifier("port"),
identifier("http"),
typeId(absolute = false, Seq("some_type"))
Expand All @@ -148,7 +148,7 @@ class ExpressionsSpec extends AnyFunSpec {

it("parses parent_type::child_type::port::http") {
Expressions.parse("parent_type::child_type::port::http") should be (
EnumByLabel(
EnumVariant(
identifier("port"),
identifier("http"),
typeId(absolute = false, Seq("parent_type", "child_type"))
Expand All @@ -158,7 +158,7 @@ class ExpressionsSpec extends AnyFunSpec {

it("parses ::parent_type::child_type::port::http") {
Expressions.parse("::parent_type::child_type::port::http") should be (
EnumByLabel(
EnumVariant(
identifier("port"),
identifier("http"),
typeId(absolute = true, Seq("parent_type", "child_type"))
Expand All @@ -171,7 +171,7 @@ class ExpressionsSpec extends AnyFunSpec {
Compare(
BinOp(
Attribute(
EnumByLabel(identifier("port"),identifier("http")),
EnumVariant(identifier("port"),identifier("http")),
identifier("to_i")
),
Add,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -345,10 +345,10 @@ class GraphvizClassCompiler(classSpecs: ClassSpecs, topClass: ClassSpec) extends
affectedVars(left) ++ affectedVars(right)
case Ast.expr.IntNum(_) | Ast.expr.FloatNum(_) | Ast.expr.Str(_) | Ast.expr.Bool(_) =>
List()
case _: Ast.expr.EnumByLabel =>
case _: Ast.expr.EnumVariant =>
List()
case Ast.expr.EnumById(_, id, _) =>
affectedVars(id)
case Ast.expr.EnumCast(_, value, _) =>
affectedVars(value)
case Ast.expr.Attribute(value, attr) =>
if (attr.name == Identifier.SIZEOF) {
val vars = value match {
Expand Down
17 changes: 15 additions & 2 deletions shared/src/main/scala/io/kaitai/struct/exprlang/Ast.scala
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,21 @@ object Ast {
case class FloatNum(n: BigDecimal) extends expr
case class Str(s: String) extends expr
case class Bool(n: Boolean) extends expr
case class EnumByLabel(enumName: identifier, label: identifier, inType: typeId = EmptyTypeId) extends expr
case class EnumById(enumName: identifier, id: expr, inType: typeId = EmptyTypeId) extends expr
/**
* Reference to the enum variant `variant` in the enumeration `enumName`,
* defined in the type `inType`. In expression language represented as
* `enumName::variant` or `<type-path>::enumName::variant` where `<type-path>`
* can be an absolute or a relative path to the type, defined in the current
* KSY file.
*/
case class EnumVariant(enumName: identifier, variant: identifier, inType: typeId = EmptyTypeId) extends expr
/**
* Transformation of the `value` expression into the `enumName` type,
* defined in the type `inType`. Unlike other nodes this node never
* parsed from the expression language, because at parse time any
* identifier can represent an enum and actual resolution performed later
*/
case class EnumCast(enumName: identifier, value: expr, inType: typeId = EmptyTypeId) extends expr

case class Attribute(value: expr, attr: identifier) extends expr
case class CastToType(value: expr, typeName: typeId) extends expr
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ object Expressions {
"(" ~ test ~ ")" |
"[" ~ list ~ "]" |
// "{" ~ dictorsetmaker ~ "}" |
enumByName |
enumVariant |
byteSizeOfType |
bitSizeOfType |
fstring |
Expand Down Expand Up @@ -167,17 +167,18 @@ object Expressions {

def testlist1[$: P]: P[Seq[Ast.expr]] = P( test.rep(1, sep = ",") )

def enumByName[$: P]: P[Ast.expr.EnumByLabel] = P("::".!.? ~ NAME.rep(2, "::")).map {
/** Parses reference to an enumeration variant. */
def enumVariant[$: P]: P[Ast.expr.EnumVariant] = P("::".!.? ~ NAME.rep(2, "::")).map {
case (first, names: Seq[Ast.identifier]) =>
val isAbsolute = first.nonEmpty
val (enumName, enumLabel) = names.takeRight(2) match {
val (enumName, variant) = names.takeRight(2) match {
case Seq(a, b) => (a, b)
}
val typePath = names.dropRight(2)
if (typePath.isEmpty) {
Ast.expr.EnumByLabel(enumName, enumLabel, Ast.EmptyTypeId)
Ast.expr.EnumVariant(enumName, variant, Ast.EmptyTypeId)
} else {
Ast.expr.EnumByLabel(enumName, enumLabel, Ast.typeId(isAbsolute, typePath.map(_.name)))
Ast.expr.EnumVariant(enumName, variant, Ast.typeId(isAbsolute, typePath.map(_.name)))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@ object InstanceSpec {
// value instance
ParseUtils.ensureLegalKeys(srcMap, LEGAL_KEYS_VALUE_INST, path, Some("value instance"))

// Wrap everything in EnumById if "enum" is used
// Wrap everything in EnumCast if "enum" is used
val value2 = ParseUtils.getOptValueStr(srcMap, "enum", path) match {
case None =>
value
case Some(enumName) =>
Ast.expr.EnumById(Ast.identifier(enumName), value)
Ast.expr.EnumCast(Ast.identifier(enumName), value)
}

val ifExpr = ParseUtils.getOptValueExpression(srcMap, "if", path)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -587,10 +587,10 @@ class JavaCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
// Java is very specific about what can be used as "condition" in "case
// condition:".
val condStr = condition match {
case enumByLabel: Ast.expr.EnumByLabel =>
case variant: Ast.expr.EnumVariant =>
// If switch is over a enum, only literal enum values are supported,
// and they must be written as "MEMBER", not "SomeEnum.MEMBER".
value2Const(enumByLabel.label.name)
value2Const(variant.variant.name)
case _ =>
expression(condition)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ trait EveryReadIsExpression
val expr = translator.bytesToStr(parseExprBytes(t.bytes, io), t.encoding)
handleAssignment(id, expr, rep, isRaw)
case t: EnumType =>
val expr = translator.doEnumById(t.enumSpec.get, parseExpr(t.basedOn, t.basedOn, io, defEndian))
val expr = translator.doEnumCast(t.enumSpec.get, parseExpr(t.basedOn, t.basedOn, io, defEndian))
handleAssignment(id, expr, rep, isRaw)
case _ =>
val expr = parseExpr(dataType, assignType, io, defEndian)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ trait GoReads extends CommonReads with ObjectOrientedLanguage with GoSwitchOps {
case t: EnumType =>
val r1 = translator.outVarCheckRes(parseExpr(t.basedOn, io, defEndian))
val enumSpec = t.enumSpec.get
val expr = translator.trEnumById(enumSpec.name, translator.resToStr(r1))
val expr = translator.trEnumCast(enumSpec.name, translator.resToStr(r1))
handleAssignment(id, expr, rep, isRaw)
case _: BitsType1 =>
val expr = parseExpr(dataType, io, defEndian)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,12 @@ abstract class BaseTranslator(val provider: TypeProvider)
doInterpolatedStringLiteral(s)
case Ast.expr.Bool(n) =>
doBoolLiteral(n)
case Ast.expr.EnumById(enumType, id, inType) =>
case Ast.expr.EnumCast(enumType, value, inType) =>
val enumSpec = provider.resolveEnum(inType, enumType.name)
doEnumById(enumSpec, translate(id))
case Ast.expr.EnumByLabel(enumType, label, inType) =>
doEnumCast(enumSpec, translate(value))
case Ast.expr.EnumVariant(enumType, variant, inType) =>
val enumSpec = provider.resolveEnum(inType, enumType.name)
doEnumByLabel(enumSpec, label.name)
doEnumVariant(enumSpec, variant.name)
case Ast.expr.Name(name: Ast.identifier) =>
if (name.name == Identifier.SIZEOF) {
byteSizeOfClassSpec(provider.nowClass)
Expand Down Expand Up @@ -186,8 +186,24 @@ abstract class BaseTranslator(val provider: TypeProvider)
def kaitaiStructField(value: Ast.expr, name: String): String =
anyField(value, name)

def doEnumByLabel(enumSpec: EnumSpec, label: String): String
def doEnumById(enumSpec: EnumSpec, id: String): String
/**
* Translates reference to the enum variant into target language
*
* @param enumSpec An enum definition
* @param variant Enum variant
*
* @return String in the target language with reference to the enum variant
*/
def doEnumVariant(enumSpec: EnumSpec, variant: String): String
/**
* Translates cast of expression to the enumeration type.
*
* @param enumSpec An enum definition
* @param value Translated expression which should have enum type
*
* @return String in the target language transformation of the expression result into enumeration type
*/
def doEnumCast(enumSpec: EnumSpec, value: String): String

// Predefined methods of various types
def strConcat(left: Ast.expr, right: Ast.expr, extPrec: Int) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,10 @@ class CSharpTranslator(provider: TypeProvider, importList: ImportList) extends B
override def doInternalName(id: Identifier): String =
CSharpCompiler.privateMemberName(id)

override def doEnumByLabel(enumSpec: EnumSpec, label: String): String =
s"${enumClass(enumSpec.name)}.${Utils.upperCamelCase(label)}"
override def doEnumById(enumSpec: EnumSpec, id: String): String =
s"((${enumClass(enumSpec.name)}) $id)"
override def doEnumVariant(enumSpec: EnumSpec, variant: String): String =
s"${enumClass(enumSpec.name)}.${Utils.upperCamelCase(variant)}"
override def doEnumCast(enumSpec: EnumSpec, value: String): String =
s"((${enumClass(enumSpec.name)}) $value)"

def enumClass(enumTypeAbs: List[String]): String = {
val enumTypeRel = Utils.relClass(enumTypeAbs, provider.nowClass.name)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ class ConstructTranslator(provider: TypeProvider, importList: ImportList) extend
}
}

override def doEnumByLabel(enumSpec: EnumSpec, label: String): String =
s"'$label'"
override def doEnumVariant(enumSpec: EnumSpec, variant: String): String =
s"'$variant'"

override def kaitaiStreamSize(value: Ast.expr): String =
s"stream_size(${translate(value)})"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,16 +142,16 @@ class CppTranslator(provider: TypeProvider, importListSrc: CppImportList, import
override def doInternalName(id: Identifier): String =
CppCompiler.privateMemberName(id)

override def doEnumByLabel(enumSpec: EnumSpec, label: String): String = {
override def doEnumVariant(enumSpec: EnumSpec, variant: String): String = {
val isExternal = enumSpec.isExternal(provider.nowClass)
if (isExternal) {
importListHdr.addLocal(CppCompiler.outFileNameHeader(enumSpec.name.head))
}
CppCompiler.types2class(enumSpec.name.dropRight(1)) + "::" +
Utils.upperUnderscoreCase(enumSpec.name.last + "_" + label)
Utils.upperUnderscoreCase(enumSpec.name.last + "_" + variant)
}
override def doEnumById(enumSpec: EnumSpec, id: String): String =
s"static_cast<${CppCompiler.types2class(enumSpec.name)}>($id)"
override def doEnumCast(enumSpec: EnumSpec, value: String): String =
s"static_cast<${CppCompiler.types2class(enumSpec.name)}>($value)"

override def doStrCompareOp(left: Ast.expr, op: Ast.cmpop, right: Ast.expr) = {
if (op == Ast.cmpop.Eq) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ class ExpressionValidator(val provider: TypeProvider)
_: Ast.expr.FloatNum |
_: Ast.expr.Str |
_: Ast.expr.Bool => // all simple literals are good and valid
case Ast.expr.EnumById(enumType, id, inType) =>
case Ast.expr.EnumCast(enumType, value, inType) =>
provider.resolveEnum(inType, enumType.name)
validate(id)
case Ast.expr.EnumByLabel(enumType, label, inType) =>
validate(value)
case Ast.expr.EnumVariant(enumType, variant, inType) =>
val enumSpec = provider.resolveEnum(inType, enumType.name)
if (!enumSpec.map.values.exists(_.name == label.name)) {
throw new EnumMemberNotFoundError(label.name, enumType.name, enumSpec.path.mkString("/"))
if (!enumSpec.map.values.exists(_.name == variant.name)) {
throw new EnumMemberNotFoundError(variant.name, enumType.name, enumSpec.path.mkString("/"))
}
case Ast.expr.Name(name: Ast.identifier) =>
if (name.name == Identifier.SIZEOF) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@ class GoTranslator(out: StringLanguageOutputWriter, provider: TypeProvider, impo
trInterpolatedStringLiteral(s)
case Ast.expr.Bool(n) =>
trBoolLiteral(n)
case Ast.expr.EnumById(enumType, id, inType) =>
case Ast.expr.EnumCast(enumType, value, inType) =>
val enumSpec = provider.resolveEnum(inType, enumType.name)
trEnumById(enumSpec.name, translate(id))
case Ast.expr.EnumByLabel(enumType, label, inType) =>
trEnumCast(enumSpec.name, translate(value))
case Ast.expr.EnumVariant(enumType, variant, inType) =>
val enumSpec = provider.resolveEnum(inType, enumType.name)
trEnumByLabel(enumSpec.name, label.name)
trEnumVariant(enumSpec.name, variant.name)
case Ast.expr.Name(name: Ast.identifier) =>
if (name.name == Identifier.SIZEOF) {
byteSizeOfClassSpec(provider.nowClass)
Expand Down Expand Up @@ -253,10 +253,10 @@ class GoTranslator(out: StringLanguageOutputWriter, provider: TypeProvider, impo
ResultLocalVar(v1)
}

def trEnumByLabel(enumTypeAbs: List[String], label: String) =
ResultString(GoCompiler.enumToStr(enumTypeAbs, label))
def trEnumById(enumTypeAbs: List[String], id: String) =
ResultString(s"${types2class(enumTypeAbs)}($id)")
def trEnumVariant(enumTypeAbs: List[String], variant: String) =
ResultString(GoCompiler.enumToStr(enumTypeAbs, variant))
def trEnumCast(enumTypeAbs: List[String], value: String) =
ResultString(s"${types2class(enumTypeAbs)}($value)")

override def doBytesCompareOp(left: Ast.expr, op: Ast.cmpop, right: Ast.expr): String = {
op match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,17 +56,17 @@ class JavaScriptTranslator(provider: TypeProvider, importList: ImportList) exten
override def doInternalName(id: Identifier): String =
JavaScriptCompiler.privateMemberName(id)

override def doEnumByLabel(enumSpec: EnumSpec, label: String): String = {
override def doEnumVariant(enumSpec: EnumSpec, variant: String): String = {
val isExternal = enumSpec.isExternal(provider.nowClass)
if (isExternal) {
val className = JavaScriptCompiler.type2class(enumSpec.name.head)
importList.add(s"./$className")
}
s"${JavaScriptCompiler.types2class(enumSpec.name, isExternal)}.${Utils.upperUnderscoreCase(label)}"
s"${JavaScriptCompiler.types2class(enumSpec.name, isExternal)}.${Utils.upperUnderscoreCase(variant)}"
}
override def doEnumById(enumSpec: EnumSpec, id: String): String =
override def doEnumCast(enumSpec: EnumSpec, value: String): String =
// Just an integer, without any casts / resolutions - one would have to look up constants manually
id
value

override def doBytesCompareOp(left: Ast.expr, op: Ast.cmpop, right: Ast.expr): String =
s"(${JavaScriptCompiler.kstreamName}.byteArrayCompare(${translate(left)}, ${translate(right)}) ${cmpOp(op)} 0)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,10 @@ class JavaTranslator(provider: TypeProvider, importList: ImportList) extends Bas
override def doInternalName(id: Identifier): String =
JavaCompiler.privateMemberName(id)

override def doEnumByLabel(enumSpec: EnumSpec, label: String): String =
s"${enumClass(enumSpec.name)}.${Utils.upperUnderscoreCase(label)}"
override def doEnumById(enumSpec: EnumSpec, id: String): String =
s"${enumClass(enumSpec.name)}.byId($id)"
override def doEnumVariant(enumSpec: EnumSpec, variant: String): String =
s"${enumClass(enumSpec.name)}.${Utils.upperUnderscoreCase(variant)}"
override def doEnumCast(enumSpec: EnumSpec, value: String): String =
s"${enumClass(enumSpec.name)}.byId($value)"

def enumClass(enumTypeAbs: List[String]): String = {
val enumTypeRel = Utils.relClass(enumTypeAbs, provider.nowClass.name)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,10 @@ class LuaTranslator(provider: TypeProvider, importList: ImportList) extends Base
override def doInternalName(id: Identifier): String =
LuaCompiler.privateMemberName(id)

override def doEnumByLabel(enumSpec: EnumSpec, label: String): String =
s"${LuaCompiler.types2class(enumSpec.name)}.$label"
override def doEnumById(enumSpec: EnumSpec, id: String): String =
s"${LuaCompiler.types2class(enumSpec.name)}($id)"
override def doEnumVariant(enumSpec: EnumSpec, variant: String): String =
s"${LuaCompiler.types2class(enumSpec.name)}.$variant"
override def doEnumCast(enumSpec: EnumSpec, value: String): String =
s"${LuaCompiler.types2class(enumSpec.name)}($value)"

override def strConcat(left: Ast.expr, right: Ast.expr, extPrec: Int): String =
s"${translate(left)} .. ${translate(right)}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ class NimTranslator(provider: TypeProvider, importList: ImportList) extends Base
override def bytesToStr(bytesExpr: String, encoding: String): String = {
s"""encode($bytesExpr, ${doStringLiteral(encoding)})"""
}
override def doEnumById(enumSpec: EnumSpec, id: String): String = s"${namespaced(enumSpec.name)}($id)"
// override def doEnumByLabel(enumSpec: EnumSpec, label: String): String = s"${namespaced(enumSpec.name)}($label)"
override def doEnumByLabel(enumSpec: EnumSpec, label: String): String = s"${enumSpec.name.head}.$label"
override def doEnumCast(enumSpec: EnumSpec, value: String): String = s"${namespaced(enumSpec.name)}($value)"
// override def doEnumVariant(enumSpec: EnumSpec, variant: String): String = s"${namespaced(enumSpec.name)}($variant)"
override def doEnumVariant(enumSpec: EnumSpec, variant: String): String = s"${enumSpec.name.head}.$variant"
override def doName(s: String): String =
s match {
case Identifier.ROOT => "root"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,13 @@ class PHPTranslator(provider: TypeProvider, config: RuntimeConfig) extends BaseT
override def doInternalName(id: Identifier): String =
PHPCompiler.privateMemberName(id)

override def doEnumByLabel(enumSpec: EnumSpec, label: String): String = {
override def doEnumVariant(enumSpec: EnumSpec, variant: String): String = {
val enumClass = types2classAbs(enumSpec.name)
s"$enumClass::${Utils.upperUnderscoreCase(label)}"
s"$enumClass::${Utils.upperUnderscoreCase(variant)}"
}
override def doEnumById(enumSpec: EnumSpec, id: String): String =
override def doEnumCast(enumSpec: EnumSpec, value: String): String =
// Just an integer, without any casts / resolutions - one would have to look up constants manually
id
value

override def arraySubscript(container: expr, idx: expr): String =
s"${translate(container)}[${translate(idx)}]"
Expand Down
Loading
Loading