Beautiful Code [143]
advance( );
scope.reserve(n);
return n.std( );
}
v = expression(0);
if (!v.assignment && v.id !== "(") {
v.error("Bad expression statement.");
}
advance(";");
return v;
};
The statements function parses statements until it sees (end) or } signaling the end of a block. It returns a statement, an array of statements, or (if there were no statements present) simply null:
var statements = function ( ) {
var a = [], s;
while (true) {
if (token.id === "}" || token.id === "(end)") {
break;
}
s = statement( );
if (s) {
a.push(s);
}
}
return a.length === 0 ? null : a.length === 1 ? a[0] : a;
};
The stmt function is used to add statements to the symbol table. It takes a statement id and an std function:
var stmt = function (s, f) {
var x = symbol(s);
x.std = f;
return x;
};
The block statement wraps a pair of curly braces around a list of statements, giving them a new scope:
stmt("{", function ( ) {
new_scope( );
var a = statements( );
advance("}");
scope.pop( );
return a;
});
The block function parses a block:
var block = function ( ) {
var t = token;
advance("{");
return t.std( );
};
The var statement defines one or more variables in the current block. Each name can optionally be followed by = and an expression:
Code View: Scroll / Show All
stmt("var", function ( ) {
var a = [], n, t;
while (true) {
n = token;
if (n.arity !== "name") {
n.error("Expected a new variable name.");
}
scope.define(n);
advance( );
if (token.id === "=") {
t = token;
advance("=");
t.first = n;
t.second = expression(0);
t.arity = "binary";
a.push(t);
}
if (token.id !== ",") {
break;
}
advance(",");
}
advance(";");
return a.length === 0 ? null : a.length === 1 ? a[0] : a;
});
The while statement defines a loop. It contains an expression in parentheses and a block:
stmt("while", function ( ) {
advance("(");
this.first = expression(0);
advance(")");
this.second = block( );
this.arity = "statement";
return this;
});
The if statement allows for conditional execution. If we see the else symbol after the block, we parse the next block or if statement:
stmt("if", function ( ) {
advance("(");
this.first = expression(0);
advance(")");
this.second = block( );
if (token.id === "else") {
scope.reserve(token);
advance("else");
this.third = token.id === "if" ? statement() : block( );
}
this.arity = "statement";
return this;
});
The break statement is used to break out of loops. We make sure that the next symbol is }:
stmt("break", function ( ) {
advance(";");
if (token.id !== "}") {
token.error("Unreachable statement.");
}
this.arity = "statement";
return this;
});
The return statement is used to return from functions. It can return an optional expression:
stmt("return", function ( ) {
if (token.id !== ";") {
this.first = expression(0);
}
advance(";");
if (token.id !== "}") {
token.error("Unreachable statement.");
}
this.arity = "statement";
return this;
});
Top Down Operator Precedence > Functions
9.12. Functions
Functions are executable object values. A function has an optional name (so that it can call itself recursively), a list of parameter names wrapped in parentheses, and a body that is a list of statements wrapped in curly braces. A function has its own scope:
Code View: Scroll / Show All
prefix("function", function ( ) {
var a = [];
scope = new_scope( );
if (token.arity === "name") {
scope.define(token);
this.name = token.value;
advance( );
}
advance("(");
if (token.id !== ")") {
while (true) {
if (token.arity !== "name") {
token.error("Expected a parameter name.");
}
scope.define(token);
a.push(token);
advance( );
if (token.id !== ",") {
break;
}
advance(",");
}
}
this.first = a;
advance(")");
advance("{");
this.second = statements( );