Skip to content

Commit

Permalink
add bitwise operators (&, |, ^ and ~) support
Browse files Browse the repository at this point in the history
including compound assignment with binary bitwise operators (&=, |=, ^=)
  • Loading branch information
alidd committed Apr 28, 2014
1 parent b368426 commit 68b536b
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 72 deletions.
163 changes: 105 additions & 58 deletions lib/ometajs/ometa/parsers.js
Expand Up @@ -337,23 +337,30 @@
});

case "&":
switch (this.anything()) {
case "&":
return this._or(function() {
switch (this.anything()) {
case "=":
return "&&=";
return this._or(function() {
switch (this.anything()) {
case "&":
return this._or(function() {
switch (this.anything()) {
case "=":
return "&&=";

default:
throw this._fail();
}
}, function() {
return "&&";
});
default:
throw this._fail();
}
}, function() {
return "&&";
});

default:
throw this._fail();
}
case "=":
return "&=";

default:
throw this._fail();
}
}, function() {
return "&";
});

case "(":
return "(";
Expand Down Expand Up @@ -489,31 +496,54 @@
case "]":
return "]";

case "^":
return this._or(function() {
switch (this.anything()) {
case "=":
return "^=";

default:
throw this._fail();
}
}, function() {
return "^";
});

case "{":
return "{";

case "|":
switch (this.anything()) {
case "|":
return this._or(function() {
switch (this.anything()) {
case "=":
return "||=";
return this._or(function() {
switch (this.anything()) {
case "=":
return "|=";

default:
throw this._fail();
}
}, function() {
return "||";
});
case "|":
return this._or(function() {
switch (this.anything()) {
case "=":
return "||=";

default:
throw this._fail();
}
default:
throw this._fail();
}
}, function() {
return "||";
});

default:
throw this._fail();
}
}, function() {
return "|";
});

case "}":
return "}";

case "~":
return "~";

default:
throw this._fail();
}
Expand Down Expand Up @@ -577,40 +607,36 @@
});
},
asgnExpr: function() {
var $elf = this, _fromIdx = this.input.idx, e, rhs;
var $elf = this, _fromIdx = this.input.idx, e, op, rhs;
e = this._apply("condExpr");
return this._or(function() {
this._applyWithArgs("token", "=");
rhs = this._apply("asgnExpr");
return [ "set", e, rhs ];
}, function() {
this._applyWithArgs("token", "+=");
rhs = this._apply("asgnExpr");
return [ "mset", e, "+", rhs ];
}, function() {
this._applyWithArgs("token", "-=");
rhs = this._apply("asgnExpr");
return [ "mset", e, "-", rhs ];
}, function() {
this._applyWithArgs("token", "*=");
rhs = this._apply("asgnExpr");
return [ "mset", e, "*", rhs ];
}, function() {
this._applyWithArgs("token", "/=");
rhs = this._apply("asgnExpr");
return [ "mset", e, "/", rhs ];
}, function() {
this._applyWithArgs("token", "%=");
rhs = this._apply("asgnExpr");
return [ "mset", e, "%", rhs ];
}, function() {
this._applyWithArgs("token", "&&=");
rhs = this._apply("asgnExpr");
return [ "mset", e, "&&", rhs ];
}, function() {
this._applyWithArgs("token", "||=");
op = this._or(function() {
return this._applyWithArgs("token", "+=");
}, function() {
return this._applyWithArgs("token", "-=");
}, function() {
return this._applyWithArgs("token", "*=");
}, function() {
return this._applyWithArgs("token", "/=");
}, function() {
return this._applyWithArgs("token", "%=");
}, function() {
return this._applyWithArgs("token", "&&=");
}, function() {
return this._applyWithArgs("token", "||=");
}, function() {
return this._applyWithArgs("token", "&=");
}, function() {
return this._applyWithArgs("token", "^=");
}, function() {
return this._applyWithArgs("token", "|=");
});
rhs = this._apply("asgnExpr");
return [ "mset", e, "||", rhs ];
return [ "mset", e, op.slice(0, -1), rhs ];
}, function() {
this._apply("empty");
return e;
Expand Down Expand Up @@ -646,8 +672,25 @@
return this._or(function() {
x = this._apply("andExpr");
this._applyWithArgs("token", "&&");
y = this._apply("eqExpr");
y = this._apply("bitExpr");
return [ "binop", "&&", x, y ];
}, function() {
return this._apply("bitExpr");
});
},
bitExpr: function() {
var $elf = this, _fromIdx = this.input.idx, op, x, y;
return this._or(function() {
x = this._apply("bitExpr");
op = this._or(function() {
return this._applyWithArgs("token", "|");
}, function() {
return this._applyWithArgs("token", "^");
}, function() {
return this._applyWithArgs("token", "&");
});
y = this._apply("eqExpr");
return [ "binop", op, x, y ];
}, function() {
return this._apply("eqExpr");
});
Expand Down Expand Up @@ -765,6 +808,10 @@
this._applyWithArgs("token", "!");
p = this._apply("unary");
return [ "unop", "!", p ];
}, function() {
this._applyWithArgs("token", "~");
p = this._apply("unary");
return [ "unop", "~", p ];
}, function() {
this._applyWithArgs("token", "void");
p = this._apply("unary");
Expand Down
30 changes: 16 additions & 14 deletions src/bs-js-compiler.ometajs
Expand Up @@ -20,12 +20,14 @@ export ometa BSJSParser {
| '\'' (escapedChar | ~'\'' char)*:cs '\'' -> [#string, cs.join('')]
| '"' (escapedChar | ~'"' char)*:cs '"' -> [#string, cs.join('')]
| ('#' | '`') iName:n -> [#string, n],
special = ( '(' | ')' | '{' | '}' | '[' | ']' | ','
| ';' | '?' | ':' | ``!=='' | ``!='' | ``==='' | ``==''
| ``='' | ``>='' | '>' | ``<='' | '<' | ``++'' | ``+=''
| '+' | ``--'' | ``-='' | '-' | ``*='' | '*' | ``/=''
| '/' | ``%='' | '%' | ``&&='' | ``&&'' | ``||='' | ``||''
| '.' | '!' ):s -> [s, s],
special = ( '(' | ')' | '{' | '}' | '[' | ']' | ',' | ';' | '?' | ':' | '.'
| ``!=='' | ``!='' | '!' | ``==='' | ``=='' | '='
| ``>='' | '>' | ``<='' | '<'
| ``++'' | ``+='' | '+' | ``--'' | ``-='' | '-'
| ``*='' | '*' | ``/='' | '/' | ``%='' | '%'
| ``&&='' | ``&&'' | ``&='' | '&'
| ``||='' | ``||'' | ``|='' | '|'
| ``^='' | '^' | '~'):s -> [s, s],
tok = spaces (name | constant | keyword | special | number | str),
toks = token*:ts spaces end -> ts,
token :tt = tok:t ?(t[0] == tt) -> t[1],
Expand All @@ -35,21 +37,20 @@ export ometa BSJSParser {
commaExpr = commaExpr:e1 "," asgnExpr:e2 -> [#binop, ",", e1, e2]
| asgnExpr,
asgnExpr = condExpr:e ( "=" asgnExpr:rhs -> [#set, e, rhs]
| "+=" asgnExpr:rhs -> [#mset, e, "+", rhs]
| "-=" asgnExpr:rhs -> [#mset, e, "-", rhs]
| "*=" asgnExpr:rhs -> [#mset, e, "*", rhs]
| "/=" asgnExpr:rhs -> [#mset, e, "/", rhs]
| "%=" asgnExpr:rhs -> [#mset, e, "%", rhs]
| "&&=" asgnExpr:rhs -> [#mset, e, "&&", rhs]
| "||=" asgnExpr:rhs -> [#mset, e, "||", rhs]
| ( "+=" | "-=" | "*=" | "/="
| "%=" | "&&=" | "||="
| "&=" | "^=" | "|=" ):op asgnExpr:rhs -> [#mset, e, op.slice(0, -1), rhs]
| empty -> e
),

condExpr = orExpr:e ( "?" condExpr:t ":" condExpr:f -> [#condExpr, e, t, f]
| empty -> e
),
orExpr = orExpr:x "||" andExpr:y -> [#binop, "||", x, y]
| andExpr,
andExpr = andExpr:x "&&" eqExpr:y -> [#binop, "&&", x, y]
andExpr = andExpr:x "&&" bitExpr:y -> [#binop, "&&", x, y]
| bitExpr,
bitExpr = bitExpr:x ("|"|"^"|"&"):op eqExpr:y -> [#binop, op, x, y]
| eqExpr,
eqExpr = eqExpr:x ( "==" relExpr:y -> [#binop, "==", x, y]
| "!=" relExpr:y -> [#binop, "!=", x, y]
Expand All @@ -76,6 +77,7 @@ export ometa BSJSParser {
| "++" postfix:p -> [#preop, "++", p]
| "--" postfix:p -> [#preop, "--", p]
| "!" unary:p -> [#unop, "!", p]
| "~" unary:p -> [#unop, "~", p]
| "void" unary:p -> [#unop, "void", p]
| "delete" unary:p -> [#unop, "delete", p]
| "typeof" unary:p -> [#unop, "typeof", p]
Expand Down
53 changes: 53 additions & 0 deletions test/unit/bsjs-test.js
Expand Up @@ -41,3 +41,56 @@ exports['bsjstranslator should compile to js'] = function(test) {

test.done();
};

addCompilerTest = function(src, expectation) {
var ast,
code;

exports['js-compiler should compile "' + src + '"'] = function(test) {
test.doesNotThrow(function() {
ast = common.ometajs.BSJSParser.matchAll(src, 'topLevel');
code = common.ometajs.BSJSTranslator.matchAll([ast], 'trans');
});

test.ok(Array.isArray(ast), 'expected AST to be an Array');
test.ok(expectation.test(code), 'expected "' + code + '" to match: "'+ expectation + '"');

test.done();
};
};

shouldCompile = function(src) {
return {to: function(expectation) {addCompilerTest(src, expectation)}};
};

var parens = function(it) {return '\\(' + it + '\\)'}
var spaces = function(it) {return '\\s*' + it + '\\s*'}
var escape = function(it) {
shoulBeEscaped = ['*', '+', '^']
return shoulBeEscaped.indexOf(it) > -1 ? '\\' + it : it
}

testBinop = function(op1, op, op2) {
eop = escape(op)
regexp1 = new RegExp('var\\s+x' + spaces('=') + parens(parens(op1) + spaces(eop) + parens(op2)))
regexp2 = new RegExp(parens('x' + spaces(eop + '=') + parens(op1)))
shouldCompile('var x = ' + op1 + op + op2).to(regexp1)
shouldCompile('var x = ' + op1 + ' ' + op + ' ' + op2).to(regexp1)
shouldCompile('x ' + op + '= ' + op1).to(regexp2)
};

testUnary = function(op, op1) {
regexp = new RegExp('var\\s+x' + spaces('=') + parens(spaces(op) + parens(op1)))
shouldCompile('var x = ' + op + op1).to(regexp)
};

shouldCompile('var x = 1').to(/var\s+x/)

testBinop('1', '+', '2')
testBinop('1', '-', '2')

// bitweise operators
testUnary('~', '0')
testBinop('1', '&', '2')
testBinop('0', '|', '1')
testBinop('0', '^', '1')

0 comments on commit 68b536b

Please sign in to comment.