Big update to prettier-extension-monkeyc

I've posted about prettier-extension-monkeyc before, but I've added a bunch of new features that developers will probably like (well, I've been missing them, so maybe you have too).

The new features it implements for VSCode include:

  • Goto Definition. Point at a symbol, Ctrl/Cmd click, and it will take you to the definition. Or F12
  • Goto References. Right click on a symbol and select "Goto References". It will show you all the references. Or Shift-F12
  • Peek Definition/Peek References. Same as above, but in a popup window so you don't lose your place in the original document.
  • Rename Symbol. Right click on a local, function, class or module name, and select "Rename Symbol". It will rename all the references. It doesn't yet work for class members/methods.
  • Goto Symbol. Type Ctrl/Cmd-Shift-O and pick a symbol from the drop down (which has a hierarchical view of all symbols in the current file). This also appears as an outline across the top of the file.
  • Open Symbol By Name. Type Ctrl/Cmd-T, then start typing letters from a symbol name. A drop down will be populated with all matching symbols from anywhere in your project.

Older features include a prettier based formatter for monkeyc, and a monkeyc optimizer that will build/run/export an optimized version of your project.

[edit: My last couple of replies seem to have just disappeared, and the whole conversation seems to be in a jumbled order, so tldr: there's a new test-release at https://github.com/markw65/prettier-extension-monkeyc/releases/tag/v2.0.9 which seems to work for me on linux. I'll do more verification tomorrow, and push a proper update to the vscode store once I'm sure everything is working]

  • Note that there's and extra comma before the closing }

    Does that extra comma actually generate different byte code? (Seems unlikely to me, I could be wrong tho.)

    My guess would be that the following two dictionaries would have identical contents:

    { :foo => 42 }

    { :foo => 42, }

  • I'm pretty sure there's no difference (the .mir file is identical), but still if my original code didn't have the extra comma, then the optimizer probably shouldn't add it either.

  • That's the default prettier behavior (if an object or array is split across multiple lines, there's a trailing comma after the last item). Its also one of the few things that prettier has options to control, so in theory, setting 'trailingComma: "none"' (either via the Prettier extension's settings, or via one of prettier's many config files) should fix it - and if you're formatting the original source it does.

    But I just tried that, and it looks like when the optimizer invokes prettier to format the output, it does so in a way that skips all config files, and it just runs with the default options. There must be a way to pick up the standard options, so I'll fix that in the next release. Apart from anything else, it will make the optimized source a bit closer to the original if you're formatting the original and using non-standard options.

    But meanwhile, I'm 100% certain that the trailing comma does not change the meaning of the code in any way. One of my tests for the formatter is building all the Garmin sample projects with and without formatting the source, and verifying that the binaries don't change, and this comes up several times in those projects.

  • What does mean this numbers especial SETTINGS = 0x5e771465? Is it var symbol?

    Sorry, was just expanding on my previous statement that a .prg file appears to be a sequence of sections. Each section is a (big-endian) 32-bit int declaring the "type" of the section, followed by a (big-endian) 32-bit int giving the size of the section, followed by the actual data of the section.

    By comparing the sections and their types to the output of the compiler when you pass it the "-g" flag, I was able to determine a few of the "type" values. So no, I don't think this will help you at all

  • v2.0.29 is out.

    This fixes a bug handling unused variables declared at the top level in case statements, which could cause the optimizer to fail (it issued an obscure error, and the build failed - it never produced incorrect results).

    It also adds an icon :-)

  • I think I found  bug:

        (:memory32K)
        public function compute(info as Activity.Info) as Void {
            // other stuff
            a = strfHrZone(mConfFormat1, extHr, extZone, fractZoneStr, computeIntHR(info));
            b = strfHrZone(mConfFormat2, extHr, extZone, fractZoneStr, computeIntHR(info));
        }
    
    
        typedef AlphaNumeric as String or Number;
    
        (:inline_memory16K)
        hidden function strfHrZone(format as String, extHr as AlphaNumeric, extZone as AlphaNumeric, fractZone as String, intHr as AlphaNumeric) as String {
            var name = mName != null ? mName : "";
            return Lang.format(format, [extHr, intHr, name, extZone, fractZone]);
        }
    

    got optimized into:

          {
            var intHr =
              info != null && info.currentHeartRate != null
                ? info.currentHeartRate
                : pre_____;
            var name = mName != null ? mName : "";
            extHr = Lang.format(mConfFormat1, [
              extHr,
              intHr,
              name,
              extZone,
              fractZoneStr,
            ]);
          }
          {
            var intHr =
              info != null && info.currentHeartRate != null
                ? info.currentHeartRate
                : pre_____;
            var name = mName != null ? mName : "";
            mResult2 = Lang.format(mConfFormat2, [
              extHr,
              intHr,
              name,
              extZone,
              fractZoneStr,
            ]);
          }
    

    Or in other words: I thought the :inline_memory16K means that in memory16K it does inline it, but in memory32K it doesn't. However as you see it did inline it in memory32K

  • Hmmm, I tested it now with the current version and it seems to work. The generated code above is the current production that I compiled about 2 weeks ago. I don't know what version of the plugin I had back then... And therefore here's an idea: can you add the version of the plugin to the output? Somewhere in optimized/.... Like in the build-info.json or the beginning of the generated jungle file as a comment.

  • I tested it now with the current version and it seems to work. The generated code above is the current production that I compiled about 2 weeks ago. I don't know what version of the plugin I had back then... And therefore here's an idea: can you add the version of the plugin to the output?

    I've not posted an update for a while, and nothing has changed wrt inlining and excludeAnnotations since I flipped the sense of inline_foo (at your request) a long time ago.

    But yes, including the version number is a good idea, and I'll do that. I'm also looking at allowing you to npm install @markw65/monkeyc-optimizer@<version> in your project directory, and have the extension use *that* version (if present) rather than the one internal to the extension. That way, the optimizer version would be stored in package.json/package-lock.json (under version control), which would solve the issue brought up by earlier in the thread.

  • I'm almost certain this has been "covered" before, but is there a chance you can make the following work:

        (:inline)
        private function isInActivity() as Boolean {
            var info = Activity.getActivityInfo();
            return info != null && info.startTime != null;
        }
    
        public onStart() as Void {
            var x = Properties.getValue("x");
            if (x == 0 && isInActivity()) {
                //...
            }
        }
        
        public onStop() as Void {
            var x = Properties.getValue("x");
            x = isInActivity() ? 1 : 0;
            // ...
        }

    In both places I get a warning:
    While inlining isInActivity: This function can only be inlined in statement, assignment, or return contexts

  • The problem is that I have to translate it into monkeyc. If I could translate to monkeyc-bytecode directly, inlining arbitrary functions would be trivial [As an aside, I've actually looked at doing that, and its not *too* bad for the code itself. The problem is that then I also have to deal with all the other things that go into a monkeyc binary, including the resources, and a bunch of things that aren't even documented].

    So take the first one... I'd have to translate it to something like:

            if (x == 0) {
                var info = Activity.getActivityInfo();
                if (info != null && info.startTime != null) {
                    // ...
                }
            }

    Which is quite reasonable. But what if the original had been

            if (x == 0 && isInActivity()) {
                //...
            } else {
                // do something else
            }
    

    After my rewrite, where do I put the "else" clause? And allowing this case, but only if there's no else clause is just going to be even more confusing.

    So I think I'm going to say that I'm willing to make the following work:

            if (x == 0) {
                if (isInActivity()) {
                    //...
                }
            }
    

    ie, a function that could be inlined in assignment context can also be inlined if its the sole argument to an if clause.

    I may be able to do something similar for the conditional argument in a ?: expression that's in assignment context - but what about a conditional thats the argument to an if? Let me think about that some more...