Skip to content

Commit

Permalink
Implements the bitwise operators.
Browse files Browse the repository at this point in the history
  • Loading branch information
ndreynolds committed Nov 19, 2012
1 parent 273ab17 commit 3c6f1b6
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 10 deletions.
31 changes: 28 additions & 3 deletions src/eval.c
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,6 @@ fh_prefix_exp(js_val *ctx, ast_node *node)
fh_eval(ctx, node->e1);
return JSUNDEF();
}

if (STREQ(op, "+"))
return TO_NUM(fh_eval(ctx, node->e1));
if (STREQ(op, "!"))
Expand All @@ -667,9 +666,10 @@ fh_prefix_exp(js_val *ctx, ast_node *node)
return JSNUM(-1 * x->number.val);
}

// Increment and decrement.
js_val *old_val = TO_NUM(fh_eval(ctx, node->e1));
js_val *new_val;

// Increment and decrement.
if (STREQ(op, "++")) {
new_val = fh_add(old_val, JSNUM(1));
fh_set_rec(ctx, node->e1->sval, new_val);
Expand All @@ -681,6 +681,13 @@ fh_prefix_exp(js_val *ctx, ast_node *node)
return new_val;
}

// Bitwise NOT
if (STREQ(op, "~")) {
old_val = fh_to_int32(old_val);
int old_val_int32 = (int)old_val->number.val;
return JSNUM(~old_val_int32);
}

UNREACHABLE();
}

Expand Down Expand Up @@ -745,10 +752,28 @@ fh_bin_exp(js_val *ctx, ast_node *node)
fh_error(NULL, E_TYPE, "Expecting a function in 'instanceof' check");
return fh_has_instance(b, a);
}
if (STREQ(op, "in"))
if (STREQ(op, "in")) {
if (!IS_OBJ(b))
fh_error(NULL, E_TYPE, "Expecting an object with 'in' operator");
return fh_has_property(b, TO_STR(a)->string.ptr);
}

int a_int32 = fh_to_int32(a)->number.val;
int b_int32 = fh_to_int32(b)->number.val;

// Bitwise Logical
if (STREQ(op, "&")) return JSNUM(a_int32 & b_int32);
if (STREQ(op, "^")) return JSNUM(a_int32 ^ b_int32);
if (STREQ(op, "|")) return JSNUM(a_int32 | b_int32);

unsigned a_uint32 = fh_to_uint32(a)->number.val;
unsigned b_uint32 = fh_to_uint32(b)->number.val;
unsigned shift_cnt = b_uint32 & 0x1F;

// Bitwise Shift
if (STREQ(op, "<<")) return JSNUM(a_int32 << shift_cnt);
if (STREQ(op, ">>")) return JSNUM(a_int32 >> shift_cnt);
if (STREQ(op, ">>>")) return JSNUM(a_uint32 >> shift_cnt);

UNREACHABLE();
}
Expand Down
26 changes: 26 additions & 0 deletions src/flathead.c
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,32 @@ fh_to_number(js_val *val)
return val;
}

js_val *
fh_to_int32(js_val *val)
{
long long int32_bit = fh_to_uint32(val)->number.val;

if (int32_bit >= pow(2, 31))
return JSNUM(int32_bit - pow(2, 32));
return JSNUM(int32_bit);
}

js_val *
fh_to_uint32(js_val *val)
{
long long pos_int, int32_bit;

val = fh_to_number(val);
if (IS_NAN(val) || IS_INF(val) || val->number.val == 0)
return JSNUM(0);

int sign = val->number.val < 0 ? -1 : 1;
pos_int = sign * floor(abs(val->number.val));
int32_bit = fmod(pos_int, pow(2, 32));

return JSNUM(int32_bit);
}

js_val *
fh_to_string(js_val *val)
{
Expand Down
2 changes: 2 additions & 0 deletions src/flathead.h
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,8 @@ js_val * fh_try_get_proto(char *);
bool fh_is_callable(js_val *);
js_val * fh_to_primitive(js_val *, js_type);
js_val * fh_to_number(js_val *);
js_val * fh_to_int32(js_val *);
js_val * fh_to_uint32(js_val *);
js_val * fh_to_string(js_val *);
js_val * fh_to_boolean(js_val *);
js_val * fh_to_object(js_val *);
Expand Down
6 changes: 3 additions & 3 deletions src/grammar.y
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@
%token<val> AND OR
%token<val> PLUSPLUS MINUSMINUS VOID DELETE TYPEOF
%token<val> EQEQ GTE LTE NE STEQ STNE INSTANCEOF
%token<val> LSHIFT RSHIFT ULSHIFT URSHIFT
%token<val> LSHIFT RSHIFT URSHIFT
%token<val> PLUSEQ MINUSEQ MULTEQ DIVEQ MODEQ
%token<val> LSHIFTEQ RSHIFTEQ URSHIFTEQ
%token<val> ANDEQ XOREQ OREQ
Expand Down Expand Up @@ -726,8 +726,6 @@ ShiftExpression : AdditiveExpression
{ $$ = NEW_EXP($1, $3, "<<"); }
| ShiftExpression RSHIFT AdditiveExpression
{ $$ = NEW_EXP($1, $3, ">>"); }
| ShiftExpression ULSHIFT AdditiveExpression
{ $$ = NEW_EXP($1, $3, "<<<"); }
| ShiftExpression URSHIFT AdditiveExpression
{ $$ = NEW_EXP($1, $3, ">>>"); }
;
Expand Down Expand Up @@ -768,6 +766,8 @@ UnaryExpression : PostfixExpression
{ $$ = NEW_UNPRE($2, "-"); }
| '!' UnaryExpression
{ $$ = NEW_UNPRE($2, "!"); }
| '~' UnaryExpression
{ $$ = NEW_UNPRE($2, "~"); }
;

PostfixExpression : LeftHandSideExpression
Expand Down
7 changes: 3 additions & 4 deletions src/lexer.l
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,6 @@ L?\/[^\ \/]+\/([imgy]{0,4}) { /* FIXME: This is very incomplete */
">>" return OP(RSHIFT);
"<<=" return OP(LSHIFTEQ);
">>=" return OP(RSHIFTEQ);
"<<<" return OP(ULSHIFT);
">>>" return OP(URSHIFT);
"===" return OP(STEQ);
"!==" return OP(STNE);
Expand All @@ -168,9 +167,9 @@ L?\/[^\ \/]+\/([imgy]{0,4}) { /* FIXME: This is very incomplete */

/* SINGLE-CHARACTER PUNCTUATORS & OPERATORS */

[-+()\[\]=*/%<>,.:`;?!{}] {
TOKEN("LITERAL", 0);
return *yytext;
[-+()\[\]=*/%<>,.:`;?!{}~&|^] {
TOKEN("LITERAL", 0);
return *yytext;
}


Expand Down
69 changes: 69 additions & 0 deletions test/test_bitwise.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// test_bitwise.js
// ---------------

var assert = console.assert;

var assertEquals = function(a, b) {
if (a !== b)
console.log(a + ' !== ' + b);
assert(a === b);
};

var test = function(name, f) {
f();
};


test('& Bitwise AND', function() {
var x = 2600822924;
assertEquals(0, 1 & 0);
assertEquals(1, 1 & 1);
assertEquals(1, -1 & 1);
assertEquals(8, 14 & 9);
assertEquals(0, undefined & 1);
assertEquals(0, 1 & undefined);
assertEquals(46512102 & x, 46512102 & 2600822924);
assertEquals(1600822924 & x, 1600822924 & 2600822924);
assertEquals(2600822924 & x, 2600822924 & 2600822924);
});

test('| Bitwise OR', function() {
assertEquals(15, 14 | 9);
assertEquals(1, undefined | 1);
assertEquals(1, 1 | undefined);
});

test('^ Bitwise XOR', function() {
assertEquals(7, 14 ^ 9);
assertEquals(1, undefined ^ 1);
assertEquals(1, 1 ^ undefined);
});

test('~ Bitwise NOT', function() {
assertEquals(-10, ~9);
assertEquals(-1, ~0);
assertEquals(-2, ~1);
assertEquals(0, ~-1);
assertEquals(-101, ~100);
assertEquals(-1073741825, ~0x40000000);
});

test('<< Bitwise Left Shift', function() {
assertEquals(36, 9 << 2);
assertEquals(0, undefined << 1);
assertEquals(1, 1 << undefined);
});

test('>> Bitwise Right Shift', function() {
assertEquals(2, 9 >> 2);
assertEquals(-3, -9 >> 2);
assertEquals(0, undefined >> 1);
assertEquals(1, 1 >> undefined);
});

test('>>> Bitwise Right Unsigned Shift', function() {
assertEquals(2, 9 >>> 2);
assertEquals(1073741821, -9 >>> 2);
assertEquals(0, undefined >>> 1);
assertEquals(1, 1 >>> undefined);
});

0 comments on commit 3c6f1b6

Please sign in to comment.