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]

  • These consts are declared in class, not in some module and not globally. I want them to have private visibility and to be static as their values belong to class and not to the instance.

    My point was to talk you out of using const in another const initializer, as this case is not guaranteed to work in Monkey C as I see it.

  • Nice idea, but since it's const... it has no meaning IMHO. I can understand the private part, but not the static. Every const is kind of static, except that the optimizer will not even add it to the code in your case, because they are Number literals. Wherever you use them will be replaced by their value. If it was an Array or Dict, then it would be really added, and referenced.

    I would be very surprised if the current (6.4.2, 7.1.1) compiler would not simply ignore static in case of const. But on the other hand in the past I saw strange things, like using hidden and private resulted in different code size, So who knows. But in terms of design I don't think it makes any difference.

    It works in monkey C as expected when you use const expressions (expressions including constants) to define other constants. The only problem is when you have them in different files, when there's the "order" is problematic (it looks like the files are processed alphabetically, but I'm not sure if this is really by design or just happens so that when the compiler asks the list of the files from the OS it gets then in a "random" order, that in the test I made happend to be alphabetical) If they are in the same file you can use them, and it works. The only problem here was the function call.

  • Nice idea, but since it's const... it has no meaning IMHO

    You should be right, and this discussion has happened at least once in the forum that I can remember.

    Prior to Garmin's optimizer (and without my optimizer) they were definitely not the same - a non-static const lived on the instance, while a static const lived on the class. The *values* were the same, but clearly the memory usage was not (unless its a singleton object, of course).

    There's also the twist that adding any static member to a class creates a "shadow" class for the static members, which itself has an overhead of 18 bytes (or thereabouts - its been a while since I went through this).

    But these days, if its a const with a literal initializer, and you have the garmin optimization level set to 1 or above, the values will be substituted, and the constants themselves removed from the program. So it doesn't really matter...

  • Apparently whatever they did to improve arrays in CIQ 5 can make a huge difference in code size if done a certain way. If I have a function that returns an array as an argument to another function, then that takes a lot of code apparently. If I change the function to put the data into a public var in it's class, then assign a variable in the calling function to the public var in the other class, then use that it cuts code size a lot. I updated about a dozen functions and code went from 28k to 22k! The same code now added about 300 bytes to pre CIQ 5 devices. FWIW, the optimizer now can only crunch about 700 bytes out of the new CIQ 5 code, bringing it down to 21.3k.

    This begs the question, is it possible to pass a variable name by reference to a function in another class so that it is updated directly so the painful array copying part is avoided? Maybe by using symbols?

  • This begs the question, is it possible to pass a variable name by reference to a function in another class so that it is updated directly so the painful array copying part is avoided? Maybe by using symbols?

    I think all non-"primitives" are passed by reference, not value (similar to python and js). When you pass an array to a function, there isn't any copying that happens afaik. Correct me if I'm wrong, but you should be able to modify an element of the passed-in array, and have that change be visible to other references of the array, which would prove no copying is happening.

    e.g.

    function foo(x) {
      x[0] = 0;
    }

    function test() {
      var x = [1, 2, 3, 4, 5];
      foo(x);
      System.println(x); // should print "[0, 2, 3, 4, 5]"
    }

    Afaik, the array improvement in CIQ 5 is related to the *initialization* of an array in code.

    e.g.

    var x = [1, 2, 3, 4, 5];
    /* previously, inefficient opcodes would be generated which basically look like "x[0] = 1; x[1] = 2; ..." (a separate assignment for each array element). Now the array initialization is done as a more efficient bulk copy of data */

  • I think this (passing reference) already happens. I haven't notice this copying thing. How does your code look like?

  • Copying is clearly what is happening, because I reuse the same array in different functions:

    public var orbXY;
    
    	public function drawSunRise(rMajor, aMajor, rMinor, aMinor) {
    		orbXY = [] as Array<Float>;
    		var raySize = rMinor * 0.7;
    		for (var i = 0; i <= 11; i++) {
    			polar(:addxy, rMajor, aMajor, rMinor, aMinor);
    			polar(:addxy, rMajor, aMajor, raySize, aMinor + PI_12);
    			aMinor += PI_6;
    		}
    		return null;
    	}
    polar with the :addxy argument appends an [x,y] coordinate pair to orbXY
    on each call, so at the end you have a full polygon
    
    There is a simular function for the sunset polygon
    
    Then in the calling class:
    
    		// draw sun rise
    		orbR = astro.v_sunRiseR;
    		orb.drawSunRise(l_bigR, orbR, l_litR, PI_12);
    		_sunRiseOrb = orb.orbXY;
    
    		// draw sun set
    		orbR = astro.v_sunSetR;
    		orb.drawSunSet(l_bigR, orbR, l_litR, 0);
    		_sunSetOrb = orb.orbXY;

    Of course later on they are drawn on the dc. Each of the calling class variables has it's own data. If it was by reference, then the calling class variables would change whenever the orbXY in the called class was updated. So, it would sure be handy if I could pass a reference to the calling class variable to the called class and have it updated directly instead of having to be copied.

    The light blue polygon is the time of low tide, but you can see that they are separate.

    Previously, I would return the orbXY array and set the calling class variable to the function value. Changing it to the form in this example saves a lot of code in CIQ 5.

  • There's no copying.

    The call to drawSunRise creates a new array, assigns it to orbXY, appends a bunch of stuff to it and returns. The array is assigned to _sunRiseOrb.

    Then the call to drawSunSet creates a new array, assigns it to orbXY, appends a bunch of stuff to it, and returns. That new array is assigned to _sunSetOrb.

    You don't really need the public orbXY at all. You could just use a local, and have drawSunRise/Set return the local, and assign the result of the calls appropriately.

  • Actually I do need the class variable orbXY, or else I would have to have polar return an x,y pair and I would assign it that way. Which is what I used to do and it was also way more code bytes than using the one class variable. Where in drawSunRise do you see a new array being created, or values being returned from polar? How else could the values being calculated by polar be getting into orbXY if not being put there in that function? Like I said, I did it the traditional way of assigning the variable to the array returned by the function. Under CIQ 5 that is now the bad way.

  • Experimenting with weak references, I got this working, which doesn't look like copying, but doesn't save any bytes so I'm not seeing the benefit at the moment.

    // Was doing this
        orb.drawSmTickLines(l_tickR, l_fAngle, 0.10);
    	_smTicks = orb.orbXY;
    	
    // now doing this
    	var smRef = _smTicks.weak();
    	orb.drawSmTickLines(l_tickR, l_fAngle, 0.10, smRef);
    
    // and in drawSmTickLines
    	public function drawSmTickLines(rMajor, aMajor, rMinor, wRef as WeakReference) {
    		var wref = wRef.get() as Array< Array<Float>? >;
    // calc each tick loop
    			wref.add(tick);
    // at the end
            return null;
            

    This seems to work the same as before. Maybe it's faster, I would have to see if the profiler shows any improvement.