Online Book Reader

Home Category

Beautiful Code [141]

By Root 5052 0
separated by ? and :. It is not an ordinary infix operator, so we need to supply its led function:

infix("?", 20, function (left) {

this.first = left;

this.second = expression(0);

advance(":");

this.third = expression(0);

this.arity = "ternary";

return this;

});

The . operator is used to select a member of an object. The token on the right must be a name, but it will be used as a literal:

infix(".", 90, function (left) {

this.first = left;

if (token.arity !== "name") {

token.error("Expected a property name.");

}

token.arity = "literal";

this.second = token;

this.arity = "binary";

advance( );

return this;

});

The [ operator is used to dynamically select a member from an object or array. The expression on the right must be followed by a closing ]:

infix("[", 90, function (left) {

this.first = left;

this.second = expression(0);

this.arity = "binary";

advance("]");

return this;

});

Those infix operators are left associative. We can also make right associative operators, such as short-circuiting logical operators, by reducing the right binding power:

var infixr = function (id, bp, led) {

var s = symbol(id, bp);

s.led = led || function (left) {

this.first = left;

this.second = expression(bp - 1);

this.arity = "binary";

return this;

};

return s;

}

The && operator returns the first operand if the first operand is falsy. Otherwise, it returns the second operand. The || operator returns the first operand if the first operand is truthy; otherwise, it returns the second operand:

infixr("&&", 40);

infixr("||", 40);

Top Down Operator Precedence > Prefix Operators

9.7. Prefix Operators

We can do a similar thing for prefix operators. Prefix operators are right associative. A prefix does not have a left binding power because it does not bind to the left. Prefix operators can sometimes be reserved words (reserved words are discussed in the section "Scope," later in this chapter):

var prefix = function (id, nud) {

var s = symbol(id);

s.nud = nud || function ( ) {

scope.reserve(this);

this.first = expression(80);

this.arity = "unary";

return this;

};

return s;

}

prefix("-");

prefix("!");

prefix("typeof");

The nud of ( will call advance(")") to match a balancing ) token. The ( token does not become part of the parse tree because the nud returns the expression:

prefix("(", function ( ) {

var e = expression(0);

advance(")");

return e;

});

Top Down Operator Precedence > Assignment Operators

9.8. Assignment Operators

We could use infixr to define our assignment operators, but we want to do two extra bits of business, so we will make a specialized assignment function. It will examine the left operand to make sure that it is a proper lvalue. We will also set an assignment flag so that we can later quickly identify assignment statements:

var assignment = function (id) {

return infixr(id, 10, function (left) {

if (left.id !== "." && left.id !== "[" &&

left.arity !== "name") {

left.error("Bad lvalue.");

}

this.first = left;

this.second = expression(9);

this.assignment = true;

this.arity = "binary";

return this;

});

};

assignment("=");

assignment("+=");

assignment("-=");

Notice that we have a sort of inheritance pattern, where assignment returns the result of calling infixr, and infixr returns the result of calling symbol.

Top Down Operator Precedence > Constants

9.9. Constants

The constant function builds constants into the language. The nud mutates a name token into a literal token:

var constant = function (s, v) {

var x = symbol(s);

x.nud = function ( ) {

scope.reserve(this);

this.value = symbol_table[this.id].value;

this.arity = "literal";

return this;

};

x.value = v;

return x;

};

constant("true", true);

constant("false", false);

constant("null", null);

constant("pi", 3.141592653589793);

Top Down Operator Precedence > Scope

9.10. Scope

We use functions such as infix

Return Main Page Previous Page Next Page

®Online Book Reader