Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

Remove redundant local variables and allow passing parameters via function. #7

wants to merge 2 commits into


None yet
2 participants

Page- commented Jul 19, 2011

I've updated the ometa compiler to use an object for this.locals instead of an array to have unique keys and then use Object.keys to create an array that only contains unique values, resulting in the generated javascript not having redundant declarations. I've recompiled all the ometa sources including the compiler itself using the updated compiler.

I've also added a change so that if you were to declare:

ometa OmetaTest {
test1 = testJS('x'),
test2 = testJS

OmetaTest.testJS = function(param) {
console.log('No param passed');

Then in test1 'x' will be passed to testJS as the function parameter, but for test2 no parameter will be passed, and because we aren't using the "anything" rule to pick up parameters it means we won't eat into the input stream when no parameter is passed. This allows for calling into custom javascript that is written in a more standard way and using optional parameters without having to resort to embedding javascript directly into the ometa source using {}

Page- added some commits Jul 18, 2011

Allow passing arguments using _applyWithArgs and _superApplyWithArgs …
…to the rule using standard javascript function passing, this makes writing custom rules in javascript more straightforward (can use the familiar parameter construct) and also allows for optional parameters.

alexwarth commented Jul 20, 2011

Hi Page,

Thanks, this is definitely good stuff.

About the first part -- removing redundant... -- please see 453430a

I'm sorry that I couldn't just accept your pull request, but I'm still not a very experienced git user, and the fact that bs-ometa-compiler.js and bs-js-compiler.js are automatically generated and also required by the rest of the system makes things a little bit trickier than usual.

I really like the second part of your pull request, and think that it could prove to be pretty useful. My only reservation is that parameterized rule applications are already pretty expensive, and having to pay the additional cost of creating and filling in an array of arguments every time may have really bad effects on the performance of the system. Do you have any ideas on how we may be able to avoid that?

Thanks again, and I hope we can figure out a way to put this into the system with a minimal performance hit :)


@alexwarth alexwarth closed this Jul 20, 2011


Page- commented Jul 20, 2011

Thanks for pulling the first part - I had wanted to send them as separate requests but I'm also fairly new to git so wasn't sure how to manage it. The main thing is to get the changes into your repository :) Thanks for crediting it to me in your commit message though, it's appreciated.

I agree with the performance, I haven't run any tests but just thinking out loud it seems to me that it could arguably be a very small performance hit or potentially a gain (in the hit case).
My reasoning for this is Function.length is a native and should hopefully be optimised to be fairly quick, along with Math.min so the effects of those should hopefully be mostly neglible. I'm not sure how quick args = [] would be so that could add some weight but it's a fairly common operation so likely to be highly optimised (potentially optimised away altogether if nothing gets added to it).
For the hit case (the rule we are applying has parameters) then we no longer need to call _prependInput as many times (or potentially at all) which looks like it quite an expensive operation, so avoiding that would be a performance benefit.
For the miss case it's a single if which should hopefully be minimal.

Anyway this is all just speculation, I'll have to get some sort of benchmark going and check out the effects.
If there is a performance benefit for the standard function parameter method then I will see about making ometa generate functions with parameters to take advantage of this. If this turns out to be the case do you have any thoughts/input on the syntax to use for the function parameter method (maybe :x before the = are used as parameters, or maybe add a new construct to declare parameters?)


alexwarth commented Jul 20, 2011

Ok, I've got a pretty good idea how I can do this w/o slowing down the implementation -- it involves changing the code generation a little bit and taking advantage of Javascript's arguments array. But there's still something that's not quite right about this idea. Suppose we write the following grammar:

ometa G {
aRule = aJavascriptFunction(42) 'x'
G.aJavascriptFunction = function(arg) { alert("hello world " + arg) }

So far so good, but consider what happens when I try to use it:

G.matchAll("x", "aRule")

You'll see an alert window come up, and it will say "hello world 42", just as we expected, but the match will fail. This is because aJavascriptFunction's argument was not only passed to it the usual Javascripty way, it was also prepended to the input stream. So when we try to match the 'x' character, we'll find a 42 instead!

Granted, we could make this problem go away by changing the function's implementation to:

G.aJavascriptFunction = function(arg) { alert("hello world " + arg); this._apply("anything") }

so that the argument is consumed, but IMO this is really ugly -- remember, the intent of your change was to allow OMeta and Javascript to mix more cleanly, and this work-around requires that the Javascript function have intimate knowledge of the OMeta implementation. This is really too bad, because I really liked your idea.

(Please let me know if I misunderstood your idea and/or this doesn't make any sense!)



Page- commented Jul 20, 2011

I already accounted for this in the patch I sent - any argument that is passed through the standard Javascript way will not be passed using _prependInput, so it's either one way or the other, preferring the standard Javascript method if available and bypassing the need to have the _prependInput, meaning that the 'x' will still match.
The other benefit of this was that if you have:
aRule2 = aJavascriptFunction 'x'

Then the response would be "hello world undefined" and the stream would remain as 'x', using the current method for parameter passing you would get "hello world x" and the 'x' in aRule would not match as the stream would have ended.

So the current implementation already takes this potential problem into account and uses the arguments "array" (it's not technically an array and as such I could not use a splice to grab the elements I wanted and instead had to result to copying the arguments into a new array) and then Function.apply to pass them. I'm not sure how else you would use the arguments array? For the code generation if we could specify ometa to use standard parameters for the most part then I feel this method would speed it up. Anyway I'm going to look at benchmarking this now, have been at a workshop most of the day.


Page- commented Jul 21, 2011

Tests so far haven't shown any difference in performance (once I changed args.push() to args[args.length-1] =), I'll try and get them posted in the morning.


alexwarth commented Jul 21, 2011

Oh, ok! I hadn't noticed that you were using the rule-function's length property -- that's pretty clever! So I stand corrected. This is great stuff :)

Now that it all makes sense to me, I can think about that diferent code generation scheme (for rule applications) that I was talking about before. But I've got a lot of other stuff going on (I've got a big deadline coming up for work, and also have to prepare to give a talk at ECOOP next week) so unfortunately this will have to wait a little bit.

In the meantime, I'd love to see the results of your benchmark!

BTW, I would like to re-open this pull request, but it would be better if we had a version that didn't include the other commit, which I've already incorporated into the main repository.



Page- commented Jul 21, 2011

I'll merge in your latest changes and commit the change I made for performance (removing the args.push() call) and post a new pull request in a moment.
Anyway here are my results.
Over 5 runs per browser I got these averages Chrome/IE9/FF5
Testing the case we call a function with JS params: 909.8ms/238.2ms/332.2ms
Testing the case we call a function without JS params: 1323.2ms/470.4ms/647ms
Testing without the patch: 1278.2ms/435ms/621.2ms

The performance hit for the case where we call without JS params actually seems to come from the _apply call to fetch the parameters, rather than anywhere else.

Here's the code I used, I had to run the test in a loop in order to get it to take enough time such that the clock inaccuracy wouldn't put the error too high: (not quite sure what's with the weird highlighting, had to manually escape the html as github doesn't seem to it? I was quite surprised by that)

<script src="lib.js"></script>
<script src="ometa-base.js"></script>
<script src="ApplyWithArgs.js"></script>
<textarea id="resultsArea" rows="15" cols="100"></textarea><br />
<input type="button" value="Test Hit" onClick="runTests(ApplyWithArgsTest,'testHit',1,3,10000);return false">
<input type="button" value="Test Miss" onClick="runTests(ApplyWithArgsTest,'testMiss',1,3,10000);return false">
var resultsArea = document.getElementById('resultsArea');
function log(value) {
resultsArea.value += value + "\n";

        function runTests(ometaObj,rule,startIdx,endIdx,repeat) {
            var start = new Date().getTime();
            for(var j=0;j&lt;repeat;j++) {
                for(var i=startIdx;i&lt;=endIdx;i++) {
            var end = new Date().getTime();
            var time = end - start;


ometa ApplyWithArgsTest {
testHit1 = oneArgJS(2000),
testHit2 = twoArgJS(2000,1),
testHit3 = threeArgJS(2000,1,2),

testMiss1           = oneArg(2000),
testMiss2           = twoArg(2000,1),
testMiss3           = threeArg(2000,2,3),

oneArg           :x = ?(x>0) | oneArg(x-1),
twoArg        :x :y = ?(x>0) | twoArg(x-1,y),
threeArg   :x :y :z = ?(x>0) | threeArg(x-1,y,z)


ApplyWithArgsTest.oneArgJS = function(a1) {

ApplyWithArgsTest.twoArgJS = function(a1,a2) {

ApplyWithArgsTest.threeArgJS = function(a1,a2,a3) {


alexwarth commented Jul 21, 2011

The problem with your benchmark is that these test grammars are not representative of real-world OMeta grammars... maybe you could try running the OMeta compiler on its own source code. Here's how you would do this:

s = ... // source code from OMeta compiler, i.e., the contents of bs-ometa-compiler.txt
tree = BSOMetaJSParser.matchAll(s, "topLevel")
code = BSOMetaJSTranslator.match(tree, "trans")



Page- commented Jul 22, 2011

I agree it's not representative, it was just to test very heavily the impact of the changes, unfortunately I had a bug in my code that meant the results were not even useful in the manner I had intended them to be used :(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment