Decoding TileDissectorMethod.jstmp from ProtoTilesPen - Minecraftian14/JShortyOutlet GitHub Wiki

Diving into a big example.

Target

Well... let's begin by having a look at what this example is...

{
  "package": "mcxiv.dissector",
  "name": "Create Dissector Method",
  "identities": [
    {
      "name": "Create Dissector Method",
      "regex": "\\n([ ]*createDissectorMethod\\(\"(\\w+)\"\\)([^;]+);)",
      "template": {
        "type": "lines",
        "content": [
          "    /**",
          "<<     * @param PD:NAME PD:DOCS.>>",
          "     * @param fade If true, fades the image about the cuts by fadeLength.",
          "     * @param fadeLength The length by which the fade takes effect.",
          "     * @param raw If it's null, a new array is returned based on the original pixels taken in by the constructor. If it's not null, the effect os applied on this array.",
          "     * @param interpolator The interpolator to be used to fade the pixels.",
          "     * @param clone If true returns a completely new array and doesn't disturbs values in the given array (raw).",
          "     * @param deNull If true changes all null values to transparent colors.",
          "     * @return <<RD:DOCS>>",
          "     * The dimensions remain the same, ie, the remaining pixels are Null or Transparent.",
          "     */",
          "    public static Color[][] <<MN:NAME>>(<<int PD:NAME, >>boolean fade, int fadeLength, Color[][] raw, FloatFunction interpolator, boolean clone, boolean deNull) {",
          "        raw = arrayPreCheck(raw, clone);",
          "        <<VAR_WIN>>",
          "        <<VAR_HIN>>",
          "        <<VAR_WFL>>",
          "        <<VAR_HFL>>",
          "        <<VAR_WIN_2>>",
          "        <<VAR_HIN_2>>",
          "        <<VAR_WFL_2>>",
          "        <<VAR_HFL_2>>",
          "        <<VAR_EL>>",
          "<<        ArrUtil.retainByPredicate(raw, (x, y) -> CROP:EXPR);>>",
          "        if (fade) {",
          "<<            ArrUtil.fade(raw, interpolator, FADE:PARAMS);>>",
          "        }",
          "        if (deNull) ArrUtil.deNull(raw, Color.INVISIBLE);",
          "        return raw;",
          "    }"
        ]
      }
    }
  ],
  "elements": [
    {
      "name": "Method Name",
      "docs": "",
      "tag": "MN",
      "references": {
        "NAME from PARENT": 2
      }
    },
    {
      "name": "Parameter Definition",
      "docs": "",
      "regex": "\\.parameter\\(\"(.*)\", ([^)]+)\\)",
      "tag": "PD",
      "references": {
        "NAME": 1,
        "DOCS": 2
      }
    },
    {
      "name": "Returns Definition",
      "docs": "",
      "regex": "\\.description\\(\"\"\"\\n[ ]+([^)]+)\\n[ ]*\"\"\"\\)",
      "tag": "RD",
      "references": {
        "DOCS": 1
      }
    },
    {
      "name": "Crop Method Call",
      "docs": "",
      "regex": "\\.crop\\(([^)]+)\\)",
      "tag": "CROP",
      "references": {
        "EXPR": 1
      }
    },
    {
      "name": "Fade Method Call",
      "docs": "",
      "regex": "\\.fade\\(([^)]+)\\)",
      "tag": "FADE",
      "references": {
        "PARAMS": 1
      }
    }
  ],
  "expressions": [
    {
      "name": "Width stored as an integer",
      "regex": "w_in[^_]",
      "tag": "VAR_WIN",
      "expression": "int w_in = raw.length;"
    },
    {
      "name": "Height stored as an integer",
      "regex": "h_in[^_]",
      "tag": "VAR_HIN",
      "expression": "int h_in = raw[0].length;"
    },
    {
      "name": "Width stored as a float",
      "regex": "w_fl[^_]",
      "tag": "VAR_WFL",
      "expression": "float w_fl = raw.length;"
    },
    {
      "name": "Height stored as a float",
      "regex": "h_fl[^_]",
      "tag": "VAR_HFL",
      "expression": "float h_fl = raw[0].length;"
    },
    {
      "name": "Half of Width stored as an integer",
      "regex": "w_in_2",
      "tag": "VAR_WIN_2",
      "expression": "int w_in_2 = raw.length / 2;"
    },
    {
      "name": "Half of Height stored as an integer",
      "regex": "h_in_2",
      "tag": "VAR_HIN_2",
      "expression": "int h_in_2 = raw[0].length / 2;"
    },
    {
      "name": "Half of Width stored as a float",
      "regex": "w_fl_2",
      "tag": "VAR_WFL_2",
      "expression": "float w_fl_2 = raw.length / 2f;"
    },
    {
      "name": "Half of Height stored as a float",
      "regex": "h_fl_2",
      "tag": "VAR_HFL_2",
      "expression": "float h_fl_2 = raw[0].length / 2f;"
    },
    {
      "name": "Effective Length",
      "regex": "effectiveLength",
      "tag": "VAR_EL",
      "expression": "int effectiveLength = (int) (fadeLength * AppUtil.ROOT_2);"
    }
  ],
  "example": "..."
}

Okayyy that's quite a big chunk of mysterious code, but if you look at it after creating a few segments it's actually very easy and small.

The java doc declaration

Let's begin from the content. It starts with a big chunk of JavaDocs as

    /**,
<<   * @param PD:NAME PD:DOCS.>>,
     * @param fade If true...by fadeLength.,
     * @param fadeLength T... takes effect.,
     * @param raw If it's ...on this array.,
     * @param interpolator...de the pixels.,
     * @param clone If tru...n array (raw).,
     * @param deNull If tr...parent colors.,
     * @return <<RD:DOCS>>,
     * The dimensions rema...r Transparent.,
     */",

Wow! There are actually just three references.

The first reference is << * @param PD:NAME PD:DOCS.>>. If it repeats, it will copy " * @param " every time, and pick a suitable replacement for PD:NAME and PD:DOCS.

To clean up this segment, let's assume PD:NAME and PD:DOCS are the arrays [A, B, C] and [1, 2, 3]

Therefore, it will generate this:

   * @param A 1.
   * @param B 2.
   * @param C 3.

About the last reference, ie, RD:DOCS it doesn't necessarily repeats and does a simple job of inserting some text.

The method declaration

Next comes the method declaration segment:

"    public static Color[][] <<MN:NAME>>(<<int PD:NAME, >>boolean fade, int fadeLength, Color[][] raw, FloatFunction interpolator, boolean clone, boolean deNull) {",
"        raw = arrayPreCheck(raw, clone);",
"        <<VAR_WIN>>",
"        <<VAR_HIN>>",
"        <<VAR_WFL>>",
"        <<VAR_HFL>>",
"        <<VAR_WIN_2>>",
"        <<VAR_HIN_2>>",
"        <<VAR_WFL_2>>",
"        <<VAR_HFL_2>>",
"        <<VAR_EL>>",
"<<        ArrUtil.retainByPredicate(raw, (x, y) -> CROP:EXPR);>>",
"        if (fade) {",
"<<            ArrUtil.fade(raw, interpolator, FADE:PARAMS);>>",
"        }",
"        if (deNull) ArrUtil.deNull(raw, Color.INVISIBLE);",
"        return raw;",
"    }"

Okay, so to begin, wherever you are reading it, say out loud with me, "The heck!"

This place contains a lot of references. Let us first focus on only on the first line - the method signature defination.

Here we have a 2 variables, the MN:NAME and PD:NAME. MN:NAME doesnt necessarily repeats, but PD:NAME does. Proceeding with the assumption from before, we the code generated will be:

(Let MN:NAME be [name])

public static Color[][] name(int A, int B, int C, boolean fade, int fadeLength, Color[][] raw, FloatFunction interpolator, boolean clone, boolean deNull) {

Easy right?

Next we have a bunch of Expressions which will be replaced with some code only if they satisfy a condition. So there isnt much to discuss except explaining the expressions itself, which I'll cover soon.

Finally, the method ends with some fairly length references like << ArrUtil.retainByPredicate(raw, (x, y) -> CROP:EXPR);>>. But once again, they dont do anything super special except repeating the ArrUtil.retainByPredicate part and replacing CROP:EXPR for every element provided.

With this, I hope it gave a fairly good idea, what we really want to generate.

Just giving an example for one such final product:

/**
     * @param h_abv """height_above: The height above the middle of original image to be summed with h_btw and be used as the height of the rectangle to be cropped.""".
     * @param h_blw """height_below: The height below the middle of original image to be summed with h_abv and be used as the height of the rectangle to be cropped.""".
     * @param fade If true, fades the image about the cuts by fadeLength.
     * @param fadeLength The length by which the fade takes effect.
     * @param raw If it's null, a new array is returned based on the original pixels taken in by the constructor. If it's not null, the effect os applied on this array.
     * @param interpolator The interpolator to be used to fade the pixels.
     * @param clone If true returns a completely new array and doesn't disturbs values in the given array (raw).
     * @param deNull If true changes all null values to transparent colors.
     * @return Returns the middle part of original pixels cropped like a rectangle with full width.
     * The dimensions remain the same, ie, the remaining pixels are Null or Transparent.
     */
    public static Color[][] getHorRect(int h_abv, int h_blw, boolean fade, int fadeLength, Color[][] raw, FloatFunction interpolator, boolean clone, boolean deNull) {
        raw = arrayPreCheck(raw, clone);
        int h_in = raw[0].length;
        float h_fl = h_in;
        int h_in_2 = (int) (h_fl / 2);
        float h_fl_2 = h_fl / 2;
        ArrUtil.retainByPredicate(raw, (x, y) -> y > h_fl_2 - h_abv - fadeLength && y < h_fl_2 + h_blw + fadeLength);
        if (fade) {
            ArrUtil.fade(raw, interpolator, 0, h_in_2 - h_abv, 0, h_in_2 - h_abv - fadeLength);
            ArrUtil.fade(raw, interpolator, 0, h_in_2 + h_blw, 0, h_in_2 + h_blw + fadeLength);
        }
        if (deNull) ArrUtil.deNull(raw, Color.INVISIBLE);
        return raw;
    }

Now, before we discuss the regex for this identity, we need to clear up, how we would like to define all those variables as mentioned above.

Because I love Java, I will be creating a syntax similar to Java only. Let's go with this:

createDissectorMethod("nameOfTheMethod")
   .parameter("h_abv", """some docs about param1""")
   .parameter("h_blw", """some docs about param2""")
   .description("""
       A doc about what the method returns.
   """)
   .crop(y > h_fl_2 - h_abv - fadeLength && y < h_fl_2 + h_blw + fadeLength)
   .fade(0, h_in_2 - h_abv, 0, h_in_2 - h_abv - fadeLength)
   .fade(0, h_in_2 + h_blw, 0, h_in_2 + h_blw + fadeLength);

As you can see, h_abv and h_blw are two such parameters we define and h_abv_2 and h_blw_2 are their halves respectively.

Inside of crop it's just a complicated boolean expression, we shouldnt be bothered about what exactly it is.

Similarly, inside of fade we have a pair of coords given as 4 parameters. Once again, we can ignore what exactly it is.

What maters here is:

  • Create more variables like h_blw_2 only if required.
  • Feeding 1 boolean expression to crop.
  • Feeding 4 parameters to fade.

Let's first handle the regex for our identity. Note, that it shouldnt bother about the different method calls, we just need to grab this whole block.

\\n([ ]*createDissectorMethod\\(\"(\\w+)\"\\)([^;]+);)

Note, that \n is outside of the first regex group, therefore it wont be replaced: that is, everything inside of the first group will be replaced.

This regex starts simply by matching the method call, ie, createDissectorMethod and matches until a ; is reached.

Here,

  • Group 1 represents the whole method call.
  • Parenthesis starts.
  • Group 2 represents some word inside a pair of double quotes.
  • Group 3 anything after group 2 until/except ;.

Thus the identity part ends here.

Next we should move on to elements.

Elements

TODO

⚠️ **GitHub.com Fallback** ⚠️