From 33d99ef2f0eb82860bfccdd38cc28e00e7b3e1f9 Mon Sep 17 00:00:00 2001
From: Mingun <Alexander_Sergey@mail.ru>
Date: Mon, 13 Jan 2020 23:23:34 +0500
Subject: [PATCH] #22: Forward attribute indexes to language compilers

---
 .../io/kaitai/struct/ClassCompiler.scala      | 26 +++++++++----------
 .../io/kaitai/struct/GoClassCompiler.scala    |  4 +--
 .../io/kaitai/struct/RustClassCompiler.scala  |  4 +--
 .../struct/languages/CSharpCompiler.scala     |  4 +--
 .../kaitai/struct/languages/CppCompiler.scala |  4 +--
 .../kaitai/struct/languages/GoCompiler.scala  |  4 +--
 .../struct/languages/JavaCompiler.scala       |  4 +--
 .../struct/languages/JavaScriptCompiler.scala |  4 +--
 .../kaitai/struct/languages/LuaCompiler.scala |  4 +--
 .../kaitai/struct/languages/PHPCompiler.scala |  4 +--
 .../struct/languages/PerlCompiler.scala       |  4 +--
 .../struct/languages/PythonCompiler.scala     |  4 +--
 .../struct/languages/RubyCompiler.scala       |  4 +--
 .../struct/languages/RustCompiler.scala       |  4 +--
 .../components/LanguageCompiler.scala         |  4 +--
 15 files changed, 41 insertions(+), 41 deletions(-)

diff --git a/shared/src/main/scala/io/kaitai/struct/ClassCompiler.scala b/shared/src/main/scala/io/kaitai/struct/ClassCompiler.scala
index 46fb67659..aeabd1fef 100644
--- a/shared/src/main/scala/io/kaitai/struct/ClassCompiler.scala
+++ b/shared/src/main/scala/io/kaitai/struct/ClassCompiler.scala
@@ -91,14 +91,14 @@ class ClassCompiler(
     compileInstances(curClass)
 
     // Attributes declarations and readers
-    val allAttrs: List[MemberSpec] =
-      curClass.seq ++
-      curClass.params ++
-      List(
+    val allAttrs: List[(MemberSpec, Int)] =
+      curClass.seq.zipWithIndex ++
+      curClass.params.zipWithIndex ++
+      (List(
         AttrSpec(List(), RootIdentifier, CalcUserType(topClassName, None)),
         AttrSpec(List(), ParentIdentifier, curClass.parentType)
       ) ++
-      ExtraAttrs.forClassSpec(curClass, lang)
+      ExtraAttrs.forClassSpec(curClass, lang)).zipWithIndex
     compileAttrDeclarations(allAttrs)
     compileAttrReaders(allAttrs)
 
@@ -184,28 +184,28 @@ class ClassCompiler(
   }
 
   /**
-    * Iterates over a given list of attributes and generates attribute
+    * Iterates over a given list of attributes and their indexes and generates attribute
     * declarations for each of them.
     * @param attrs attribute list to traverse
     */
-  def compileAttrDeclarations(attrs: List[MemberSpec]): Unit = {
-    attrs.foreach { (attr) =>
+  def compileAttrDeclarations(attrs: List[(MemberSpec, Int)]): Unit = {
+    attrs.foreach { case (attr, i) =>
       val isNullable = if (lang.switchBytesOnlyAsRaw) {
         attr.isNullableSwitchRaw
       } else {
         attr.isNullable
       }
-      lang.attributeDeclaration(attr, isNullable)
+      lang.attributeDeclaration(attr, i, isNullable)
     }
   }
 
   /**
-    * Iterates over a given list of attributes and generates attribute
+    * Iterates over a given list of attributes and their indexes and generates attribute
     * readers (AKA getters) for each of them.
     * @param attrs attribute list to traverse
     */
-  def compileAttrReaders(attrs: List[MemberSpec]): Unit = {
-    attrs.foreach { (attr) =>
+  def compileAttrReaders(attrs: List[(MemberSpec, Int)]): Unit = {
+    attrs.foreach { case (attr, i) =>
       // FIXME: Python should have some form of attribute docs too
       if (!attr.doc.isEmpty && !lang.innerDocstrings)
         lang.attributeDoc(attr.id, attr.doc)
@@ -214,7 +214,7 @@ class ClassCompiler(
       } else {
         attr.isNullable
       }
-      lang.attributeReader(attr, isNullable)
+      lang.attributeReader(attr, i, isNullable)
     }
   }
 
diff --git a/shared/src/main/scala/io/kaitai/struct/GoClassCompiler.scala b/shared/src/main/scala/io/kaitai/struct/GoClassCompiler.scala
index 4efebc6b1..a6b074b9f 100644
--- a/shared/src/main/scala/io/kaitai/struct/GoClassCompiler.scala
+++ b/shared/src/main/scala/io/kaitai/struct/GoClassCompiler.scala
@@ -30,7 +30,7 @@ class GoClassCompiler(
 
     // Basic struct declaration
     lang.classHeader(curClass.name)
-    compileAttrDeclarations(curClass.seq ++ extraAttrs)
+    compileAttrDeclarations(curClass.seq.zipWithIndex ++ extraAttrs.zipWithIndex)
     curClass.instances.foreach { case (instName, instSpec) =>
       compileInstanceDeclaration(instName, instSpec)
     }
@@ -41,7 +41,7 @@ class GoClassCompiler(
 
     compileInstances(curClass)
 
-    compileAttrReaders(curClass.seq ++ extraAttrs)
+    compileAttrReaders(curClass.seq.zipWithIndex ++ extraAttrs.zipWithIndex)
 
     // Recursive types
     compileSubclasses(curClass)
diff --git a/shared/src/main/scala/io/kaitai/struct/RustClassCompiler.scala b/shared/src/main/scala/io/kaitai/struct/RustClassCompiler.scala
index 3c75039e2..152d33d84 100644
--- a/shared/src/main/scala/io/kaitai/struct/RustClassCompiler.scala
+++ b/shared/src/main/scala/io/kaitai/struct/RustClassCompiler.scala
@@ -30,7 +30,7 @@ class RustClassCompiler(
     // Basic struct declaration
     lang.classHeader(curClass.name)
 
-    compileAttrDeclarations(curClass.seq ++ extraAttrs)
+    compileAttrDeclarations(curClass.seq.zipWithIndex ++ extraAttrs.zipWithIndex)
     curClass.instances.foreach { case (instName, instSpec) =>
       compileInstanceDeclaration(instName, instSpec)
     }
@@ -40,7 +40,7 @@ class RustClassCompiler(
 
     compileInstances(curClass)
 
-    compileAttrReaders(curClass.seq ++ extraAttrs)
+    compileAttrReaders(curClass.seq.zipWithIndex ++ extraAttrs.zipWithIndex)
     lang.classFooter(curClass.name)
 
     compileEnums(curClass)
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 85be3b05c..ab71d6235 100644
--- a/shared/src/main/scala/io/kaitai/struct/languages/CSharpCompiler.scala
+++ b/shared/src/main/scala/io/kaitai/struct/languages/CSharpCompiler.scala
@@ -152,11 +152,11 @@ class CSharpCompiler(val typeProvider: ClassTypeProvider, config: RuntimeConfig)
 
   override def readFooter(): Unit = fileFooter("")
 
-  override def attributeDeclaration(attr: MemberSpec, isNullable: Boolean): Unit = {
+  override def attributeDeclaration(attr: MemberSpec, index: Int, isNullable: Boolean): Unit = {
     out.puts(s"private ${kaitaiType2NativeTypeNullable(attr.dataTypeComposite, isNullable)} ${privateMemberName(attr.id)};")
   }
 
-  override def attributeReader(attr: MemberSpec, isNullable: Boolean): Unit = {
+  override def attributeReader(attr: MemberSpec, index: Int, isNullable: Boolean): Unit = {
     out.puts(s"public ${kaitaiType2NativeTypeNullable(attr.dataTypeComposite, isNullable)} ${publicMemberName(attr.id)} { get { return ${privateMemberName(attr.id)}; } }")
   }
 
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 4db6c433c..9af9930ee 100644
--- a/shared/src/main/scala/io/kaitai/struct/languages/CppCompiler.scala
+++ b/shared/src/main/scala/io/kaitai/struct/languages/CppCompiler.scala
@@ -297,7 +297,7 @@ class CppCompiler(
     ensureMode(PublicAccess)
   }
 
-  override def attributeDeclaration(attr: MemberSpec, isNullable: Boolean): Unit = {
+  override def attributeDeclaration(attr: MemberSpec, index: Int, isNullable: Boolean): Unit = {
     ensureMode(PrivateAccess)
     outHdr.puts(s"${kaitaiType2NativeType(attr.dataTypeComposite)} ${privateMemberName(attr.id)};")
     declareNullFlag(attr.id, isNullable)
@@ -316,7 +316,7 @@ class CppCompiler(
     }
   }
 
-  override def attributeReader(attr: MemberSpec, isNullable: Boolean): Unit = {
+  override def attributeReader(attr: MemberSpec, index: Int, isNullable: Boolean): Unit = {
     ensureMode(PublicAccess)
     outHdr.puts(s"${kaitaiType2NativeType(attr.dataTypeComposite.asNonOwning)} ${publicMemberName(attr.id)}() const { return ${nonOwningPointer(attr.id, attr.dataTypeComposite)}; }")
   }
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 6413627b2..e24a53284 100644
--- a/shared/src/main/scala/io/kaitai/struct/languages/GoCompiler.scala
+++ b/shared/src/main/scala/io/kaitai/struct/languages/GoCompiler.scala
@@ -136,12 +136,12 @@ class GoCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
     universalFooter
   }
 
-  override def attributeDeclaration(attr: MemberSpec, isNullable: Boolean): Unit = {
+  override def attributeDeclaration(attr: MemberSpec, index: Int, isNullable: Boolean): Unit = {
     out.puts(s"${idToStr(attr.id)} ${kaitaiType2NativeType(attr.dataTypeComposite)}")
     translator.returnRes = None
   }
 
-  override def attributeReader(attr: MemberSpec, isNullable: Boolean): Unit = {}
+  override def attributeReader(attr: MemberSpec, index: Int, isNullable: Boolean): Unit = {}
 
   override def universalDoc(doc: DocSpec): Unit = {
     out.puts
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 917d096f4..4925ec999 100644
--- a/shared/src/main/scala/io/kaitai/struct/languages/JavaCompiler.scala
+++ b/shared/src/main/scala/io/kaitai/struct/languages/JavaCompiler.scala
@@ -197,11 +197,11 @@ class JavaCompiler(val typeProvider: ClassTypeProvider, config: RuntimeConfig)
 
   override def readFooter(): Unit = universalFooter
 
-  override def attributeDeclaration(attr: MemberSpec, isNullable: Boolean): Unit = {
+  override def attributeDeclaration(attr: MemberSpec, index: Int, isNullable: Boolean): Unit = {
     out.puts(s"private ${kaitaiType2JavaType(attr.dataTypeComposite, isNullable)} ${idToStr(attr.id)};")
   }
 
-  override def attributeReader(attr: MemberSpec, isNullable: Boolean): Unit = {
+  override def attributeReader(attr: MemberSpec, index: Int, isNullable: Boolean): Unit = {
     out.puts(s"public ${kaitaiType2JavaType(attr.dataTypeComposite, isNullable)} ${idToStr(attr.id)}() { return ${idToStr(attr.id)}; }")
   }
 
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 07a08cc2d..860c4781f 100644
--- a/shared/src/main/scala/io/kaitai/struct/languages/JavaScriptCompiler.scala
+++ b/shared/src/main/scala/io/kaitai/struct/languages/JavaScriptCompiler.scala
@@ -150,9 +150,9 @@ class JavaScriptCompiler(val typeProvider: ClassTypeProvider, config: RuntimeCon
     out.puts("}")
   }
 
-  override def attributeDeclaration(attr: MemberSpec, isNullable: Boolean): Unit = {}
+  override def attributeDeclaration(attr: MemberSpec, index: Int, isNullable: Boolean): Unit = {}
 
-  override def attributeReader(attr: MemberSpec, isNullable: Boolean): Unit = {}
+  override def attributeReader(attr: MemberSpec, index: Int, isNullable: Boolean): Unit = {}
 
   override def universalDoc(doc: DocSpec): Unit = {
     // JSDoc docstring style: http://usejsdoc.org/about-getting-started.html
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 94caf4a94..b4d947f00 100644
--- a/shared/src/main/scala/io/kaitai/struct/languages/LuaCompiler.scala
+++ b/shared/src/main/scala/io/kaitai/struct/languages/LuaCompiler.scala
@@ -132,9 +132,9 @@ class LuaCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
     out.puts
   }
 
-  override def attributeDeclaration(attr: MemberSpec, isNullable: Boolean): Unit =
+  override def attributeDeclaration(attr: MemberSpec, index: Int, isNullable: Boolean): Unit =
     {}
-  override def attributeReader(attr: MemberSpec, isNullable: Boolean): Unit =
+  override def attributeReader(attr: MemberSpec, index: Int, isNullable: Boolean): Unit =
     {}
 
   override def attrParseHybrid(leProc: () => Unit, beProc: () => Unit): Unit = {
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 0e6f4b4e9..3a4414ae1 100644
--- a/shared/src/main/scala/io/kaitai/struct/languages/PHPCompiler.scala
+++ b/shared/src/main/scala/io/kaitai/struct/languages/PHPCompiler.scala
@@ -142,10 +142,10 @@ class PHPCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
 
   override def readFooter(): Unit = universalFooter
 
-  override def attributeDeclaration(attr: MemberSpec, isNullable: Boolean): Unit
+  override def attributeDeclaration(attr: MemberSpec, index: Int, isNullable: Boolean): Unit
     = declaration(attr.id)
 
-  override def attributeReader(attr: MemberSpec, isNullable: Boolean): Unit = {
+  override def attributeReader(attr: MemberSpec, index: Int, isNullable: Boolean): Unit = {
     attr.id match {
       case ParentIdentifier | RootIdentifier =>
         // just ignore it for now
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 48d87a752..48c4edc2e 100644
--- a/shared/src/main/scala/io/kaitai/struct/languages/PerlCompiler.scala
+++ b/shared/src/main/scala/io/kaitai/struct/languages/PerlCompiler.scala
@@ -132,9 +132,9 @@ class PerlCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
 
   override def readFooter(): Unit = universalFooter
 
-  override def attributeDeclaration(attr: MemberSpec, isNullable: Boolean): Unit = {}
+  override def attributeDeclaration(attr: MemberSpec, index: Int, isNullable: Boolean): Unit = {}
 
-  override def attributeReader(attr: MemberSpec, isNullable: Boolean): Unit = {
+  override def attributeReader(attr: MemberSpec, index: Int, isNullable: Boolean): Unit = {
     attr.id match {
       case RootIdentifier | ParentIdentifier =>
         // ignore, they are already defined in KaitaiStruct class
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 668701f7a..6c6d88264 100644
--- a/shared/src/main/scala/io/kaitai/struct/languages/PythonCompiler.scala
+++ b/shared/src/main/scala/io/kaitai/struct/languages/PythonCompiler.scala
@@ -135,9 +135,9 @@ class PythonCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
 
   override def readFooter() = universalFooter
 
-  override def attributeDeclaration(attr: MemberSpec, isNullable: Boolean): Unit = {}
+  override def attributeDeclaration(attr: MemberSpec, index: Int, isNullable: Boolean): Unit = {}
 
-  override def attributeReader(attr: MemberSpec, isNullable: Boolean): Unit = {}
+  override def attributeReader(attr: MemberSpec, index: Int, isNullable: Boolean): Unit = {}
 
   override def universalDoc(doc: DocSpec): Unit = {
     val docStr = doc.summary match {
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 54882aba7..83a56cb36 100644
--- a/shared/src/main/scala/io/kaitai/struct/languages/RubyCompiler.scala
+++ b/shared/src/main/scala/io/kaitai/struct/languages/RubyCompiler.scala
@@ -137,9 +137,9 @@ class RubyCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
     universalFooter
   }
 
-  override def attributeDeclaration(attr: MemberSpec, isNullable: Boolean): Unit = {}
+  override def attributeDeclaration(attr: MemberSpec, index: Int, isNullable: Boolean): Unit = {}
 
-  override def attributeReader(attr: MemberSpec, isNullable: Boolean): Unit = {
+  override def attributeReader(attr: MemberSpec, index: Int, isNullable: Boolean): Unit = {
     attr.id match {
       case RootIdentifier | ParentIdentifier =>
         // ignore, they are already added in Kaitai::Struct::Struct
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 847c658a1..bb924a493 100644
--- a/shared/src/main/scala/io/kaitai/struct/languages/RustCompiler.scala
+++ b/shared/src/main/scala/io/kaitai/struct/languages/RustCompiler.scala
@@ -134,7 +134,7 @@ class RustCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
     out.puts("}")
   }
 
-  override def attributeDeclaration(attr: MemberSpec, isNullable: Boolean): Unit = {
+  override def attributeDeclaration(attr: MemberSpec, index: Int, isNullable: Boolean): Unit = {
     attr.id match {
       case ParentIdentifier | RootIdentifier | IoIdentifier =>
         // just ignore it for now
@@ -145,7 +145,7 @@ class RustCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
     }
   }
 
-  override def attributeReader(attr: MemberSpec, isNullable: Boolean): Unit = {
+  override def attributeReader(attr: MemberSpec, index: Int, isNullable: Boolean): Unit = {
 
   }
 
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 d0646e145..7b8d51bf7 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
@@ -86,8 +86,8 @@ abstract class LanguageCompiler(
   def readHeader(endian: Option[FixedEndian], isEmpty: Boolean): Unit
   def readFooter(): Unit
 
-  def attributeDeclaration(attr: MemberSpec, isNullable: Boolean): Unit
-  def attributeReader(attr: MemberSpec, isNullable: Boolean): Unit
+  def attributeDeclaration(attr: MemberSpec, index: Int, isNullable: Boolean): Unit
+  def attributeReader(attr: MemberSpec, index: Int, isNullable: Boolean): Unit
   def attributeDoc(id: Identifier, doc: DocSpec): Unit = {}
 
   def attrParse(attr: AttrLikeSpec, id: Identifier, defEndian: Option[Endianness]): Unit