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 152186f5b..9035695a1 100644 --- a/shared/src/main/scala/io/kaitai/struct/languages/PythonCompiler.scala +++ b/shared/src/main/scala/io/kaitai/struct/languages/PythonCompiler.scala @@ -475,7 +475,7 @@ class PythonCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) out.puts out.puts(s"class ${type2class(enumName)}(IntEnum):") out.inc - enumColl.foreach { case (id: Long, label: String) => out.puts(s"$label = ${translator.doIntLiteral(id)}") } + enumColl.foreach { case (id: Long, label: String) => out.puts(s"${escapePythonKeyword(label)} = ${translator.doIntLiteral(id)}") } out.dec } @@ -498,7 +498,7 @@ class PythonCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) override def publicMemberName(id: Identifier): String = id match { - case InstanceIdentifier(name) => name + case InstanceIdentifier(name) => escapePythonKeyword(name) case _ => idToStr(id) } @@ -546,10 +546,12 @@ object PythonCompiler extends LanguageCompilerStatic config: RuntimeConfig ): LanguageCompiler = new PythonCompiler(tp, config) + override def type2class(name: String): String = escapePythonKeyword(super.type2class(name)) + def idToStr(id: Identifier): String = id match { - case SpecialIdentifier(name) => name - case NamedIdentifier(name) => name + case SpecialIdentifier(name) => escapePythonKeyword(name) + case NamedIdentifier(name) => escapePythonKeyword(name) case NumberedIdentifier(idx) => s"_${NumberedIdentifier.TEMPLATE}$idx" case InstanceIdentifier(name) => s"_m_$name" case RawIdentifier(innerId) => s"_raw_${idToStr(innerId)}" @@ -584,4 +586,51 @@ object PythonCompiler extends LanguageCompilerStatic } ) } + + // Python reserved keywords that need to be escaped + // https://docs.python.org/3/reference/lexical_analysis.html#keywords + private val KEYWORDS = Set( + "and", + "as", + "assert", + "async", + "await", + "break", + "class", + "continue", + "def", + "del", + "elif", + "else", + "except", + "False", + "finally", + "for", + "from", + "global", + "if", + "import", + "in", + "is", + "lambda", + "None", + "nonlocal", + "not", + "or", + "pass", + "raise", + "return", + "True", + "try", + "while", + "with", + "yield", + ) + + def escapePythonKeyword(name: String): String = + if (KEYWORDS.contains(name)) { + s"${name}_" + } else { + name + } } diff --git a/shared/src/main/scala/io/kaitai/struct/translators/PythonTranslator.scala b/shared/src/main/scala/io/kaitai/struct/translators/PythonTranslator.scala index 7e4eaf151..0e4b5d866 100644 --- a/shared/src/main/scala/io/kaitai/struct/translators/PythonTranslator.scala +++ b/shared/src/main/scala/io/kaitai/struct/translators/PythonTranslator.scala @@ -100,7 +100,7 @@ class PythonTranslator(provider: TypeProvider, importList: ImportList, config: R } } override def doName(s: String) = - s + PythonCompiler.escapePythonKeyword(s) override def doInternalName(id: Identifier): String = PythonCompiler.privateMemberName(id) @@ -109,7 +109,7 @@ class PythonTranslator(provider: TypeProvider, importList: ImportList, config: R if (isExternal) { PythonCompiler.externalTypeDeclaration(ExternalEnum(enumSpec), importList, config) } - s"${PythonCompiler.types2class(enumSpec.name, isExternal)}.$label" + s"${PythonCompiler.types2class(enumSpec.name, isExternal)}.${doName(label)}" } override def doEnumById(enumSpec: EnumSpec, id: String): String = s"${PythonCompiler.kstreamName}.resolve_enum(${PythonCompiler.types2class(enumSpec.name, enumSpec.isExternal(provider.nowClass))}, $id)"