Mercurial > doc-thingy
changeset 3:0c1314b3ea79
Removed narcissus, since it doesn't support JS 1.7 syntax.
author | Atul Varma <varmaa@toolness.com> |
---|---|
date | Mon, 05 Jan 2009 16:44:08 -0800 |
parents | 4a813cef41b1 |
children | ccc558eb70db |
files | js.js jsdefs.js jsexec.js jsparse.js thingy.html thingy.js |
diffstat | 6 files changed, 5 insertions(+), 2222 deletions(-) [+] |
line wrap: on
line diff
--- a/js.js Mon Jan 05 16:04:40 2009 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,73 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is the Narcissus JavaScript engine. - * - * The Initial Developer of the Original Code is - * Brendan Eich <brendan@mozilla.org>. - * Portions created by the Initial Developer are Copyright (C) 2004 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * Narcissus - JS implemented in JS. - * - * Native objects and classes implemented metacircularly: - * the global object (singleton) - * eval - * function objects, Function - * - * SpiderMonkey extensions used: - * catch guards - * const declarations - * get and set functions in object initialisers - * Object.prototype.__defineGetter__ - * Object.prototype.__defineSetter__ - * Object.prototype.__defineProperty__ - * Object.prototype.__proto__ - * filename and line number arguments to *Error constructors - * callable regular expression objects - * - * SpiderMonkey extensions supported metacircularly: - * catch guards - * const declarations - * get and set functions in object initialisers - */ - -/* - * Loads a file relative to the calling script's (our) source directory, and not - * the directory that the executing shell is being run out of. - */ -function my_load(filename) { - evaluate(snarf(filename), filename, 1); -} - -my_load('jsdefs.js'); -my_load('jsparse.js'); -my_load('jsexec.js'); -
--- a/jsdefs.js Mon Jan 05 16:04:40 2009 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,170 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is the Narcissus JavaScript engine. - * - * The Initial Developer of the Original Code is - * Brendan Eich <brendan@mozilla.org>. - * Portions created by the Initial Developer are Copyright (C) 2004 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * Narcissus - JS implemented in JS. - * - * Well-known constants and lookup tables. Many consts are generated from the - * tokens table via eval to minimize redundancy, so consumers must be compiled - * separately to take advantage of the simple switch-case constant propagation - * done by SpiderMonkey. - */ -const GLOBAL = this; - -var tokens = [ - // End of source. - "END", - - // Operators and punctuators. Some pair-wise order matters, e.g. (+, -) - // and (UNARY_PLUS, UNARY_MINUS). - "\n", ";", - ",", - "=", - "?", ":", "CONDITIONAL", - "||", - "&&", - "|", - "^", - "&", - "==", "!=", "===", "!==", - "<", "<=", ">=", ">", - "<<", ">>", ">>>", - "+", "-", - "*", "/", "%", - "!", "~", "UNARY_PLUS", "UNARY_MINUS", - "++", "--", - ".", - "[", "]", - "{", "}", - "(", ")", - - // Nonterminal tree node type codes. - "SCRIPT", "BLOCK", "LABEL", "FOR_IN", "CALL", "NEW_WITH_ARGS", "INDEX", - "ARRAY_INIT", "OBJECT_INIT", "PROPERTY_INIT", "GETTER", "SETTER", - "GROUP", "LIST", - - // Terminals. - "IDENTIFIER", "NUMBER", "STRING", "REGEXP", - - // Keywords. - "break", - "case", "catch", "const", "continue", - "debugger", "default", "delete", "do", - "else", "enum", - "false", "finally", "for", "function", - "if", "in", "instanceof", - "new", "null", - "return", - "switch", - "this", "throw", "true", "try", "typeof", - "var", "void", - "while", "with", - "let" -]; - -// Operator and punctuator mapping from token to tree node type name. -// NB: superstring tokens (e.g., ++) must come before their substring token -// counterparts (+ in the example), so that the opRegExp regular expression -// synthesized from this list makes the longest possible match. -var opTypeNames = { - '\n': "NEWLINE", - ';': "SEMICOLON", - ',': "COMMA", - '?': "HOOK", - ':': "COLON", - '||': "OR", - '&&': "AND", - '|': "BITWISE_OR", - '^': "BITWISE_XOR", - '&': "BITWISE_AND", - '===': "STRICT_EQ", - '==': "EQ", - '=': "ASSIGN", - '!==': "STRICT_NE", - '!=': "NE", - '<<': "LSH", - '<=': "LE", - '<': "LT", - '>>>': "URSH", - '>>': "RSH", - '>=': "GE", - '>': "GT", - '++': "INCREMENT", - '--': "DECREMENT", - '+': "PLUS", - '-': "MINUS", - '*': "MUL", - '/': "DIV", - '%': "MOD", - '!': "NOT", - '~': "BITWISE_NOT", - '.': "DOT", - '[': "LEFT_BRACKET", - ']': "RIGHT_BRACKET", - '{': "LEFT_CURLY", - '}': "RIGHT_CURLY", - '(': "LEFT_PAREN", - ')': "RIGHT_PAREN" -}; - -// Hash of keyword identifier to tokens index. NB: we must null __proto__ to -// avoid toString, etc. namespace pollution. -var keywords = {__proto__: null}; - -// Define const END, etc., based on the token names. Also map name to index. -var consts = "const "; -for (var i = 0, j = tokens.length; i < j; i++) { - if (i > 0) - consts += ", "; - var t = tokens[i]; - if (/^[a-z]/.test(t)) { - consts += t.toUpperCase(); - keywords[t] = i; - } else { - consts += (/^\W/.test(t) ? opTypeNames[t] : t); - } - consts += " = " + i; - tokens[t] = i; -} -eval(consts + ";"); - -// Map assignment operators to their indexes in the tokens array. -var assignOps = ['|', '^', '&', '<<', '>>', '>>>', '+', '-', '*', '/', '%']; - -for (i = 0, j = assignOps.length; i < j; i++) { - t = assignOps[i]; - assignOps[t] = tokens[t]; -}
--- a/jsexec.js Mon Jan 05 16:04:40 2009 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,944 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * vim: set ts=4 sw=4 et tw=80: - * - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is the Narcissus JavaScript engine. - * - * The Initial Developer of the Original Code is - * Brendan Eich <brendan@mozilla.org>. - * Portions created by the Initial Developer are Copyright (C) 2004 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * Narcissus - JS implemented in JS. - * - * Execution of parse trees. - * - * Standard classes except for eval, Function, Array, and String are borrowed - * from the host JS environment. Function is metacircular. Array and String - * are reflected via wrapping the corresponding native constructor and adding - * an extra level of prototype-based delegation. - */ - -const GLOBAL_CODE = 0, EVAL_CODE = 1, FUNCTION_CODE = 2; - -function ExecutionContext(type) { - this.type = type; -} - -var global = { - // Value properties. - NaN: NaN, Infinity: Infinity, undefined: undefined, - - // Function properties. - eval: function eval(s) { - if (typeof s != "string") - return s; - - var x = ExecutionContext.current; - var x2 = new ExecutionContext(EVAL_CODE); - x2.thisObject = x.thisObject; - x2.caller = x.caller; - x2.callee = x.callee; - x2.scope = x.scope; - ExecutionContext.current = x2; - try { - execute(parse(s), x2); - } catch (e if e == THROW) { - x.result = x2.result; - throw e; - } finally { - ExecutionContext.current = x; - } - return x2.result; - }, - parseInt: parseInt, parseFloat: parseFloat, - isNaN: isNaN, isFinite: isFinite, - decodeURI: decodeURI, encodeURI: encodeURI, - decodeURIComponent: decodeURIComponent, - encodeURIComponent: encodeURIComponent, - - // Class constructors. Where ECMA-262 requires C.length == 1, we declare - // a dummy formal parameter. - Object: Object, - Function: function Function(dummy) { - var p = "", b = "", n = arguments.length; - if (n) { - var m = n - 1; - if (m) { - p += arguments[0]; - for (var k = 1; k < m; k++) - p += "," + arguments[k]; - } - b += arguments[m]; - } - - // XXX We want to pass a good file and line to the tokenizer. - // Note the anonymous name to maintain parity with Spidermonkey. - var t = new Tokenizer("anonymous(" + p + ") {" + b + "}"); - - // NB: Use the STATEMENT_FORM constant since we don't want to push this - // function onto the null compilation context. - var f = FunctionDefinition(t, null, false, STATEMENT_FORM); - var s = {object: global, parent: null}; - return new FunctionObject(f, s); - }, - Array: function Array(dummy) { - // Array when called as a function acts as a constructor. - return GLOBAL.Array.apply(this, arguments); - }, - String: function String(s) { - // Called as function or constructor: convert argument to string type. - s = arguments.length ? "" + s : ""; - if (this instanceof String) { - // Called as constructor: save the argument as the string value - // of this String object and return this object. - this.value = s; - return this; - } - return s; - }, - Boolean: Boolean, Number: Number, Date: Date, RegExp: RegExp, - Error: Error, EvalError: EvalError, RangeError: RangeError, - ReferenceError: ReferenceError, SyntaxError: SyntaxError, - TypeError: TypeError, URIError: URIError, - - // Other properties. - Math: Math, - - // Extensions to ECMA. - snarf: snarf, evaluate: evaluate, - load: function load(s) { - if (typeof s != "string") - return s; - - evaluate(snarf(s), s, 1) - }, - print: print, version: null -}; - -// Helper to avoid Object.prototype.hasOwnProperty polluting scope objects. -function hasDirectProperty(o, p) { - return Object.prototype.hasOwnProperty.call(o, p); -} - -// Reflect a host class into the target global environment by delegation. -function reflectClass(name, proto) { - var gctor = global[name]; - gctor.__defineProperty__('prototype', proto, true, true, true); - proto.__defineProperty__('constructor', gctor, false, false, true); - return proto; -} - -// Reflect Array -- note that all Array methods are generic. -reflectClass('Array', new Array); - -// Reflect String, overriding non-generic methods. -var gSp = reflectClass('String', new String); -gSp.toSource = function () { return this.value.toSource(); }; -gSp.toString = function () { return this.value; }; -gSp.valueOf = function () { return this.value; }; -global.String.fromCharCode = String.fromCharCode; - -var XCp = ExecutionContext.prototype; -ExecutionContext.current = XCp.caller = XCp.callee = null; -XCp.scope = {object: global, parent: null}; -XCp.thisObject = global; -XCp.result = undefined; -XCp.target = null; -XCp.ecmaStrictMode = false; - -function Reference(base, propertyName, node) { - this.base = base; - this.propertyName = propertyName; - this.node = node; -} - -Reference.prototype.toString = function () { return this.node.getSource(); } - -function getValue(v) { - if (v instanceof Reference) { - if (!v.base) { - throw new ReferenceError(v.propertyName + " is not defined", - v.node.filename, v.node.lineno); - } - return v.base[v.propertyName]; - } - return v; -} - -function putValue(v, w, vn) { - if (v instanceof Reference) - return (v.base || global)[v.propertyName] = w; - throw new ReferenceError("Invalid assignment left-hand side", - vn.filename, vn.lineno); -} - -function isPrimitive(v) { - var t = typeof v; - return (t == "object") ? v === null : t != "function"; -} - -function isObject(v) { - var t = typeof v; - return (t == "object") ? v !== null : t == "function"; -} - -// If r instanceof Reference, v == getValue(r); else v === r. If passed, rn -// is the node whose execute result was r. -function toObject(v, r, rn) { - switch (typeof v) { - case "boolean": - return new global.Boolean(v); - case "number": - return new global.Number(v); - case "string": - return new global.String(v); - case "function": - return v; - case "object": - if (v !== null) - return v; - } - var message = r + " (type " + (typeof v) + ") has no properties"; - throw rn ? new TypeError(message, rn.filename, rn.lineno) - : new TypeError(message); -} - -function execute(n, x) { - var a, f, i, j, r, s, t, u, v; - - switch (n.type) { - case FUNCTION: - if (n.functionForm != DECLARED_FORM) { - if (!n.name || n.functionForm == STATEMENT_FORM) { - v = new FunctionObject(n, x.scope); - if (n.functionForm == STATEMENT_FORM) - x.scope.object.__defineProperty__(n.name, v, true); - } else { - t = new Object; - x.scope = {object: t, parent: x.scope}; - try { - v = new FunctionObject(n, x.scope); - t.__defineProperty__(n.name, v, true, true); - } finally { - x.scope = x.scope.parent; - } - } - } - break; - - case SCRIPT: - t = x.scope.object; - a = n.funDecls; - for (i = 0, j = a.length; i < j; i++) { - s = a[i].name; - f = new FunctionObject(a[i], x.scope); - t.__defineProperty__(s, f, x.type != EVAL_CODE); - } - a = n.varDecls; - for (i = 0, j = a.length; i < j; i++) { - u = a[i]; - s = u.name; - if (u.readOnly && hasDirectProperty(t, s)) { - throw new TypeError("Redeclaration of const " + s, - u.filename, u.lineno); - } - if (u.readOnly || !hasDirectProperty(t, s)) { - t.__defineProperty__(s, undefined, x.type != EVAL_CODE, - u.readOnly); - } - } - // FALL THROUGH - - case BLOCK: - for (i = 0, j = n.length; i < j; i++) - execute(n[i], x); - break; - - case IF: - if (getValue(execute(n.condition, x))) - execute(n.thenPart, x); - else if (n.elsePart) - execute(n.elsePart, x); - break; - - case SWITCH: - s = getValue(execute(n.discriminant, x)); - a = n.cases; - var matchDefault = false; - switch_loop: - for (i = 0, j = a.length; ; i++) { - if (i == j) { - if (n.defaultIndex >= 0) { - i = n.defaultIndex - 1; // no case matched, do default - matchDefault = true; - continue; - } - break; // no default, exit switch_loop - } - t = a[i]; // next case (might be default!) - if (t.type == CASE) { - u = getValue(execute(t.caseLabel, x)); - } else { - if (!matchDefault) // not defaulting, skip for now - continue; - u = s; // force match to do default - } - if (u === s) { - for (;;) { // this loop exits switch_loop - if (t.statements.length) { - try { - execute(t.statements, x); - } catch (e if e == BREAK && x.target == n) { - break switch_loop; - } - } - if (++i == j) - break switch_loop; - t = a[i]; - } - // NOT REACHED - } - } - break; - - case FOR: - n.setup && getValue(execute(n.setup, x)); - // FALL THROUGH - case WHILE: - while (!n.condition || getValue(execute(n.condition, x))) { - try { - execute(n.body, x); - } catch (e if e == BREAK && x.target == n) { - break; - } catch (e if e == CONTINUE && x.target == n) { - continue; - } - n.update && getValue(execute(n.update, x)); - } - break; - - case FOR_IN: - u = n.varDecl; - if (u) - execute(u, x); - r = n.iterator; - s = execute(n.object, x); - v = getValue(s); - - // ECMA deviation to track extant browser JS implementation behavior. - t = (v == null && !x.ecmaStrictMode) ? v : toObject(v, s, n.object); - a = []; - for (i in t) - a.push(i); - for (i = 0, j = a.length; i < j; i++) { - putValue(execute(r, x), a[i], r); - try { - execute(n.body, x); - } catch (e if e == BREAK && x.target == n) { - break; - } catch (e if e == CONTINUE && x.target == n) { - continue; - } - } - break; - - case DO: - do { - try { - execute(n.body, x); - } catch (e if e == BREAK && x.target == n) { - break; - } catch (e if e == CONTINUE && x.target == n) { - continue; - } - } while (getValue(execute(n.condition, x))); - break; - - case BREAK: - case CONTINUE: - x.target = n.target; - throw n.type; - - case TRY: - try { - execute(n.tryBlock, x); - } catch (e if e == THROW && (j = n.catchClauses.length)) { - e = x.result; - x.result = undefined; - for (i = 0; ; i++) { - if (i == j) { - x.result = e; - throw THROW; - } - t = n.catchClauses[i]; - x.scope = {object: {}, parent: x.scope}; - x.scope.object.__defineProperty__(t.varName, e, true); - try { - if (t.guard && !getValue(execute(t.guard, x))) - continue; - execute(t.block, x); - break; - } finally { - x.scope = x.scope.parent; - } - } - } finally { - if (n.finallyBlock) - execute(n.finallyBlock, x); - } - break; - - case THROW: - x.result = getValue(execute(n.exception, x)); - throw THROW; - - case RETURN: - x.result = getValue(execute(n.value, x)); - throw RETURN; - - case WITH: - r = execute(n.object, x); - t = toObject(getValue(r), r, n.object); - x.scope = {object: t, parent: x.scope}; - try { - execute(n.body, x); - } finally { - x.scope = x.scope.parent; - } - break; - - case VAR: - case CONST: - for (i = 0, j = n.length; i < j; i++) { - u = n[i].initializer; - if (!u) - continue; - t = n[i].name; - for (s = x.scope; s; s = s.parent) { - if (hasDirectProperty(s.object, t)) - break; - } - u = getValue(execute(u, x)); - if (n.type == CONST) - s.object.__defineProperty__(t, u, x.type != EVAL_CODE, true); - else - s.object[t] = u; - } - break; - - case DEBUGGER: - throw "NYI: " + tokens[n.type]; - - case SEMICOLON: - if (n.expression) - x.result = getValue(execute(n.expression, x)); - break; - - case LABEL: - try { - execute(n.statement, x); - } catch (e if e == BREAK && x.target == n) { - } - break; - - case COMMA: - for (i = 0, j = n.length; i < j; i++) - v = getValue(execute(n[i], x)); - break; - - case ASSIGN: - r = execute(n[0], x); - t = n[0].assignOp; - if (t) - u = getValue(r); - v = getValue(execute(n[1], x)); - if (t) { - switch (t) { - case BITWISE_OR: v = u | v; break; - case BITWISE_XOR: v = u ^ v; break; - case BITWISE_AND: v = u & v; break; - case LSH: v = u << v; break; - case RSH: v = u >> v; break; - case URSH: v = u >>> v; break; - case PLUS: v = u + v; break; - case MINUS: v = u - v; break; - case MUL: v = u * v; break; - case DIV: v = u / v; break; - case MOD: v = u % v; break; - } - } - putValue(r, v, n[0]); - break; - - case HOOK: - v = getValue(execute(n[0], x)) ? getValue(execute(n[1], x)) - : getValue(execute(n[2], x)); - break; - - case OR: - v = getValue(execute(n[0], x)) || getValue(execute(n[1], x)); - break; - - case AND: - v = getValue(execute(n[0], x)) && getValue(execute(n[1], x)); - break; - - case BITWISE_OR: - v = getValue(execute(n[0], x)) | getValue(execute(n[1], x)); - break; - - case BITWISE_XOR: - v = getValue(execute(n[0], x)) ^ getValue(execute(n[1], x)); - break; - - case BITWISE_AND: - v = getValue(execute(n[0], x)) & getValue(execute(n[1], x)); - break; - - case EQ: - v = getValue(execute(n[0], x)) == getValue(execute(n[1], x)); - break; - - case NE: - v = getValue(execute(n[0], x)) != getValue(execute(n[1], x)); - break; - - case STRICT_EQ: - v = getValue(execute(n[0], x)) === getValue(execute(n[1], x)); - break; - - case STRICT_NE: - v = getValue(execute(n[0], x)) !== getValue(execute(n[1], x)); - break; - - case LT: - v = getValue(execute(n[0], x)) < getValue(execute(n[1], x)); - break; - - case LE: - v = getValue(execute(n[0], x)) <= getValue(execute(n[1], x)); - break; - - case GE: - v = getValue(execute(n[0], x)) >= getValue(execute(n[1], x)); - break; - - case GT: - v = getValue(execute(n[0], x)) > getValue(execute(n[1], x)); - break; - - case IN: - v = getValue(execute(n[0], x)) in getValue(execute(n[1], x)); - break; - - case INSTANCEOF: - t = getValue(execute(n[0], x)); - u = getValue(execute(n[1], x)); - if (isObject(u) && typeof u.__hasInstance__ == "function") - v = u.__hasInstance__(t); - else - v = t instanceof u; - break; - - case LSH: - v = getValue(execute(n[0], x)) << getValue(execute(n[1], x)); - break; - - case RSH: - v = getValue(execute(n[0], x)) >> getValue(execute(n[1], x)); - break; - - case URSH: - v = getValue(execute(n[0], x)) >>> getValue(execute(n[1], x)); - break; - - case PLUS: - v = getValue(execute(n[0], x)) + getValue(execute(n[1], x)); - break; - - case MINUS: - v = getValue(execute(n[0], x)) - getValue(execute(n[1], x)); - break; - - case MUL: - v = getValue(execute(n[0], x)) * getValue(execute(n[1], x)); - break; - - case DIV: - v = getValue(execute(n[0], x)) / getValue(execute(n[1], x)); - break; - - case MOD: - v = getValue(execute(n[0], x)) % getValue(execute(n[1], x)); - break; - - case DELETE: - t = execute(n[0], x); - v = !(t instanceof Reference) || delete t.base[t.propertyName]; - break; - - case VOID: - getValue(execute(n[0], x)); - break; - - case TYPEOF: - t = execute(n[0], x); - if (t instanceof Reference) - t = t.base ? t.base[t.propertyName] : undefined; - v = typeof t; - break; - - case NOT: - v = !getValue(execute(n[0], x)); - break; - - case BITWISE_NOT: - v = ~getValue(execute(n[0], x)); - break; - - case UNARY_PLUS: - v = +getValue(execute(n[0], x)); - break; - - case UNARY_MINUS: - v = -getValue(execute(n[0], x)); - break; - - case INCREMENT: - case DECREMENT: - t = execute(n[0], x); - u = Number(getValue(t)); - if (n.postfix) - v = u; - putValue(t, (n.type == INCREMENT) ? ++u : --u, n[0]); - if (!n.postfix) - v = u; - break; - - case DOT: - r = execute(n[0], x); - t = getValue(r); - u = n[1].value; - v = new Reference(toObject(t, r, n[0]), u, n); - break; - - case INDEX: - r = execute(n[0], x); - t = getValue(r); - u = getValue(execute(n[1], x)); - v = new Reference(toObject(t, r, n[0]), String(u), n); - break; - - case LIST: - // Curse ECMA for specifying that arguments is not an Array object! - v = {}; - for (i = 0, j = n.length; i < j; i++) { - u = getValue(execute(n[i], x)); - v.__defineProperty__(i, u, false, false, true); - } - v.__defineProperty__('length', i, false, false, true); - break; - - case CALL: - r = execute(n[0], x); - a = execute(n[1], x); - f = getValue(r); - if (isPrimitive(f) || typeof f.__call__ != "function") { - throw new TypeError(r + " is not callable", - n[0].filename, n[0].lineno); - } - t = (r instanceof Reference) ? r.base : null; - if (t instanceof Activation) - t = null; - v = f.__call__(t, a, x); - break; - - case NEW: - case NEW_WITH_ARGS: - r = execute(n[0], x); - f = getValue(r); - if (n.type == NEW) { - a = {}; - a.__defineProperty__('length', 0, false, false, true); - } else { - a = execute(n[1], x); - } - if (isPrimitive(f) || typeof f.__construct__ != "function") { - throw new TypeError(r + " is not a constructor", - n[0].filename, n[0].lineno); - } - v = f.__construct__(a, x); - break; - - case ARRAY_INIT: - v = []; - for (i = 0, j = n.length; i < j; i++) { - if (n[i]) - v[i] = getValue(execute(n[i], x)); - } - v.length = j; - break; - - case OBJECT_INIT: - v = {}; - for (i = 0, j = n.length; i < j; i++) { - t = n[i]; - if (t.type == PROPERTY_INIT) { - v[t[0].value] = getValue(execute(t[1], x)); - } else { - f = new FunctionObject(t, x.scope); - u = (t.type == GETTER) ? '__defineGetter__' - : '__defineSetter__'; - v[u](t.name, thunk(f, x)); - } - } - break; - - case NULL: - v = null; - break; - - case THIS: - v = x.thisObject; - break; - - case TRUE: - v = true; - break; - - case FALSE: - v = false; - break; - - case IDENTIFIER: - for (s = x.scope; s; s = s.parent) { - if (n.value in s.object) - break; - } - v = new Reference(s && s.object, n.value, n); - break; - - case NUMBER: - case STRING: - case REGEXP: - v = n.value; - break; - - case GROUP: - v = execute(n[0], x); - break; - - default: - throw "PANIC: unknown operation " + n.type + ": " + uneval(n); - } - - return v; -} - -function Activation(f, a) { - for (var i = 0, j = f.params.length; i < j; i++) - this.__defineProperty__(f.params[i], a[i], true); - this.__defineProperty__('arguments', a, true); -} - -// Null Activation.prototype's proto slot so that Object.prototype.* does not -// pollute the scope of heavyweight functions. Also delete its 'constructor' -// property so that it doesn't pollute function scopes. But first, we must -// copy __defineProperty__ down from Object.prototype. - -Activation.prototype.__defineProperty__ = Object.prototype.__defineProperty__; -Activation.prototype.__proto__ = null; -delete Activation.prototype.constructor; - -function FunctionObject(node, scope) { - this.node = node; - this.scope = scope; - this.__defineProperty__('length', node.params.length, true, true, true); - var proto = {}; - this.__defineProperty__('prototype', proto, true); - proto.__defineProperty__('constructor', this, false, false, true); -} - -var FOp = FunctionObject.prototype = { - // Internal methods. - __call__: function (t, a, x) { - var x2 = new ExecutionContext(FUNCTION_CODE); - x2.thisObject = t || global; - x2.caller = x; - x2.callee = this; - a.__defineProperty__('callee', this, false, false, true); - var f = this.node; - x2.scope = {object: new Activation(f, a), parent: this.scope}; - - ExecutionContext.current = x2; - try { - execute(f.body, x2); - } catch (e if e == RETURN) { - return x2.result; - } catch (e if e == THROW) { - x.result = x2.result; - throw THROW; - } finally { - ExecutionContext.current = x; - } - return undefined; - }, - - __construct__: function (a, x) { - var o = new Object; - var p = this.prototype; - if (isObject(p)) - o.__proto__ = p; - // else o.__proto__ defaulted to Object.prototype - - var v = this.__call__(o, a, x); - if (isObject(v)) - return v; - return o; - }, - - __hasInstance__: function (v) { - if (isPrimitive(v)) - return false; - var p = this.prototype; - if (isPrimitive(p)) { - throw new TypeError("'prototype' property is not an object", - this.node.filename, this.node.lineno); - } - var o; - while ((o = v.__proto__)) { - if (o == p) - return true; - v = o; - } - return false; - }, - - // Standard methods. - toString: function () { - return this.node.getSource(); - }, - - apply: function (t, a) { - // Curse ECMA again! - if (typeof this.__call__ != "function") { - throw new TypeError("Function.prototype.apply called on" + - " uncallable object"); - } - - if (t === undefined || t === null) - t = global; - else if (typeof t != "object") - t = toObject(t, t); - - if (a === undefined || a === null) { - a = {}; - a.__defineProperty__('length', 0, false, false, true); - } else if (a instanceof Array) { - var v = {}; - for (var i = 0, j = a.length; i < j; i++) - v.__defineProperty__(i, a[i], false, false, true); - v.__defineProperty__('length', i, false, false, true); - a = v; - } else if (!(a instanceof Object)) { - // XXX check for a non-arguments object - throw new TypeError("Second argument to Function.prototype.apply" + - " must be an array or arguments object", - this.node.filename, this.node.lineno); - } - - return this.__call__(t, a, ExecutionContext.current); - }, - - call: function (t) { - // Curse ECMA a third time! - var a = Array.prototype.splice.call(arguments, 1); - return this.apply(t, a); - } -}; - -// Connect Function.prototype and Function.prototype.constructor in global. -reflectClass('Function', FOp); - -// Help native and host-scripted functions be like FunctionObjects. -var Fp = Function.prototype; -var REp = RegExp.prototype; - -if (!('__call__' in Fp)) { - Fp.__defineProperty__('__call__', function (t, a, x) { - // Curse ECMA yet again! - a = Array.prototype.splice.call(a, 0, a.length); - return this.apply(t, a); - }, true, true, true); - - REp.__defineProperty__('__call__', function (t, a, x) { - a = Array.prototype.splice.call(a, 0, a.length); - return this.exec.apply(this, a); - }, true, true, true); - - Fp.__defineProperty__('__construct__', function (a, x) { - a = Array.prototype.splice.call(a, 0, a.length); - return this.__applyConstructor__(a); - }, true, true, true); - - // Since we use native functions such as Date along with host ones such - // as global.eval, we want both to be considered instances of the native - // Function constructor. - Fp.__defineProperty__('__hasInstance__', function (v) { - return v instanceof Function || v instanceof global.Function; - }, true, true, true); -} - -function thunk(f, x) { - return function () { return f.__call__(this, arguments, x); }; -} - -function evaluate(s, f, l) { - if (typeof s != "string") - return s; - - var x = ExecutionContext.current; - var x2 = new ExecutionContext(GLOBAL_CODE); - ExecutionContext.current = x2; - try { - execute(parse(s, f, l), x2); - } catch (e if e == THROW) { - if (x) { - x.result = x2.result; - throw THROW; - } - throw x2.result; - } finally { - ExecutionContext.current = x; - } - return x2.result; -}
--- a/jsparse.js Mon Jan 05 16:04:40 2009 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1020 +0,0 @@ -/* vim: set sw=4 ts=8 et tw=78: */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is the Narcissus JavaScript engine. - * - * The Initial Developer of the Original Code is - * Brendan Eich <brendan@mozilla.org>. - * Portions created by the Initial Developer are Copyright (C) 2004 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * Narcissus - JS implemented in JS. - * - * Lexical scanner and parser. - */ - -// Build a regexp that recognizes operators and punctuators (except newline). -var opRegExpSrc = "^"; -for (i in opTypeNames) { - if (i == '\n') - continue; - if (opRegExpSrc != "^") - opRegExpSrc += "|^"; - opRegExpSrc += i.replace(/[?|^&(){}\[\]+\-*\/\.]/g, "\\$&"); -} -var opRegExp = new RegExp(opRegExpSrc); - -// A regexp to match floating point literals (but not integer literals). -var fpRegExp = /^\d+\.\d*(?:[eE][-+]?\d+)?|^\d+(?:\.\d*)?[eE][-+]?\d+|^\.\d+(?:[eE][-+]?\d+)?/; - -// A regexp to match regexp literals. -var reRegExp = /^\/((?:\\.|\[(?:\\.|[^\]])*\]|[^\/])+)\/([gimy]*)/; - -function Tokenizer(s, f, l) { - this.cursor = 0; - this.source = String(s); - this.tokens = []; - this.tokenIndex = 0; - this.lookahead = 0; - this.scanNewlines = false; - this.scanOperand = true; - this.filename = f || ""; - this.lineno = l || 1; - this.comments = []; -} - -Tokenizer.prototype = { - get input() { - return this.source.substring(this.cursor); - }, - - get done() { - return this.peek() == END; - }, - - get token() { - return this.tokens[this.tokenIndex]; - }, - - match: function (tt) { - return this.get() == tt || this.unget(); - }, - - mustMatch: function (tt) { - if (!this.match(tt)) - throw this.newSyntaxError("Missing " + tokens[tt].toLowerCase()); - return this.token; - }, - - peek: function () { - var tt, next; - if (this.lookahead) { - next = this.tokens[(this.tokenIndex + this.lookahead) & 3]; - if (this.scanNewlines && next.lineno != this.lineno) - tt = NEWLINE; - else - tt = next.type; - } else { - tt = this.get(); - this.unget(); - } - return tt; - }, - - peekOnSameLine: function () { - this.scanNewlines = true; - var tt = this.peek(); - this.scanNewlines = false; - return tt; - }, - - get: function () { - var token; - while (this.lookahead) { - --this.lookahead; - this.tokenIndex = (this.tokenIndex + 1) & 3; - token = this.tokens[this.tokenIndex]; - if (token.type != NEWLINE || this.scanNewlines) - return token.type; - } - - for (;;) { - var input = this.input; - var match = (this.scanNewlines ? /^[ \t]+/ : /^\s+/)(input); - if (match) { - var spaces = match[0]; - this.cursor += spaces.length; - var newlines = spaces.match(/\n/g); - if (newlines) - this.lineno += newlines.length; - input = this.input; - } - - if (!(match = /^\/(?:\*(?:.|\n)*?\*\/|\/.*)/(input))) - break; - var comment = match[0]; - this.comments.push({lineno: this.lineno, - cursor: this.cursor, - text: comment}); - this.cursor += comment.length; - newlines = comment.match(/\n/g); - if (newlines) - this.lineno += newlines.length - } - - this.tokenIndex = (this.tokenIndex + 1) & 3; - token = this.tokens[this.tokenIndex]; - if (!token) - this.tokens[this.tokenIndex] = token = {}; - - if (!input) - return token.type = END; - - if ((match = fpRegExp(input))) { - token.type = NUMBER; - token.value = parseFloat(match[0]); - } else if ((match = /^0[xX][\da-fA-F]+|^0[0-7]*|^\d+/(input))) { - token.type = NUMBER; - token.value = parseInt(match[0]); - } else if ((match = /^[$_\w]+/(input))) { // FIXME no ES3 unicode - var id = match[0]; - token.type = keywords[id] || IDENTIFIER; - token.value = id; - } else if ((match = /^"(?:\\.|[^"])*"|^'(?:\\.|[^'])*'/(input))) { //"){ - token.type = STRING; - token.value = eval(match[0]); - } else if (this.scanOperand && (match = reRegExp(input))) { - token.type = REGEXP; - token.value = new RegExp(match[1], match[2]); - } else if ((match = opRegExp(input))) { - var op = match[0]; - if (assignOps[op] && input[op.length] == '=') { - token.type = ASSIGN; - token.assignOp = GLOBAL[opTypeNames[op]]; - match[0] += '='; - } else { - token.type = GLOBAL[opTypeNames[op]]; - if (this.scanOperand && - (token.type == PLUS || token.type == MINUS)) { - token.type += UNARY_PLUS - PLUS; - } - token.assignOp = null; - } - token.value = op; - } else if (this.scanNewlines && (match = /^\n/(input))) { - token.type = NEWLINE; - } else { - throw this.newSyntaxError("Illegal token"); - } - - token.start = this.cursor; - this.cursor += match[0].length; - token.end = this.cursor; - token.lineno = this.lineno; - return token.type; - }, - - unget: function () { - if (++this.lookahead == 4) throw "PANIC: too much lookahead!"; - this.tokenIndex = (this.tokenIndex - 1) & 3; - }, - - newSyntaxError: function (m) { - var e = new SyntaxError(m, this.filename, this.lineno); - e.source = this.source; - e.cursor = this.cursor; - return e; - } -}; - -function CompilerContext(inFunction) { - this.inFunction = inFunction; - this.stmtStack = []; - this.funDecls = []; - this.varDecls = []; -} - -var CCp = CompilerContext.prototype; -CCp.bracketLevel = CCp.curlyLevel = CCp.parenLevel = CCp.hookLevel = 0; -CCp.ecmaStrictMode = CCp.inForLoopInit = false; - -function Script(t, x) { - var n = Statements(t, x); - n.type = SCRIPT; - n.funDecls = x.funDecls; - n.varDecls = x.varDecls; - return n; -} - -// Node extends Array, which we extend slightly with a top-of-stack method. -Array.prototype.__defineProperty__( - 'top', - function () { - return this.length && this[this.length-1]; - }, - false, false, true -); - -function Node(t, type) { - var token = t.token; - if (token) { - this.type = type || token.type; - this.value = token.value; - this.lineno = token.lineno; - this.start = token.start; - this.end = token.end; - } else { - this.type = type; - this.lineno = t.lineno; - } - this.tokenizer = t; - - for (var i = 2; i < arguments.length; i++) - this.push(arguments[i]); -} - -var Np = Node.prototype = new Array; -Np.constructor = Node; -Np.toSource = Object.prototype.toSource; - -// Always use push to add operands to an expression, to update start and end. -Np.push = function (kid) { - if (kid.start < this.start) - this.start = kid.start; - if (this.end < kid.end) - this.end = kid.end; - return Array.prototype.push.call(this, kid); -} - -Node.indentLevel = 0; - -function tokenstr(tt) { - var t = tokens[tt]; - return /^\W/.test(t) ? opTypeNames[t] : t.toUpperCase(); -} - -Np.toString = function () { - var a = []; - for (var i in this) { - if (this.hasOwnProperty(i) && i != 'type' && i != 'target') - a.push({id: i, value: this[i]}); - } - a.sort(function (a,b) { return (a.id < b.id) ? -1 : 1; }); - const INDENTATION = " "; - var n = ++Node.indentLevel; - var s = "{\n" + INDENTATION.repeat(n) + "type: " + tokenstr(this.type); - for (i = 0; i < a.length; i++) - s += ",\n" + INDENTATION.repeat(n) + a[i].id + ": " + a[i].value; - n = --Node.indentLevel; - s += "\n" + INDENTATION.repeat(n) + "}"; - return s; -} - -Np.getSource = function () { - return this.tokenizer.source.slice(this.start, this.end); -}; - -Np.__defineGetter__('filename', - function () { return this.tokenizer.filename; }); - -String.prototype.__defineProperty__( - 'repeat', - function (n) { - var s = "", t = this + s; - while (--n >= 0) - s += t; - return s; - }, - false, false, true -); - -// Statement stack and nested statement handler. -function nest(t, x, node, func, end) { - x.stmtStack.push(node); - var n = func(t, x); - x.stmtStack.pop(); - end && t.mustMatch(end); - return n; -} - -function Statements(t, x) { - var n = new Node(t, BLOCK); - x.stmtStack.push(n); - while (!t.done && t.peek() != RIGHT_CURLY) - n.push(Statement(t, x)); - x.stmtStack.pop(); - return n; -} - -function Block(t, x) { - t.mustMatch(LEFT_CURLY); - var n = Statements(t, x); - t.mustMatch(RIGHT_CURLY); - return n; -} - -const DECLARED_FORM = 0, EXPRESSED_FORM = 1, STATEMENT_FORM = 2; - -function Statement(t, x) { - var i, label, n, n2, ss, tt = t.get(); - - // Cases for statements ending in a right curly return early, avoiding the - // common semicolon insertion magic after this switch. - switch (tt) { - case FUNCTION: - return FunctionDefinition(t, x, true, - (x.stmtStack.length > 1) - ? STATEMENT_FORM - : DECLARED_FORM); - - case LEFT_CURLY: - n = Statements(t, x); - t.mustMatch(RIGHT_CURLY); - return n; - - case IF: - n = new Node(t); - n.condition = ParenExpression(t, x); - x.stmtStack.push(n); - n.thenPart = Statement(t, x); - n.elsePart = t.match(ELSE) ? Statement(t, x) : null; - x.stmtStack.pop(); - return n; - - case SWITCH: - n = new Node(t); - t.mustMatch(LEFT_PAREN); - n.discriminant = Expression(t, x); - t.mustMatch(RIGHT_PAREN); - n.cases = []; - n.defaultIndex = -1; - x.stmtStack.push(n); - t.mustMatch(LEFT_CURLY); - while ((tt = t.get()) != RIGHT_CURLY) { - switch (tt) { - case DEFAULT: - if (n.defaultIndex >= 0) - throw t.newSyntaxError("More than one switch default"); - // FALL THROUGH - case CASE: - n2 = new Node(t); - if (tt == DEFAULT) - n.defaultIndex = n.cases.length; - else - n2.caseLabel = Expression(t, x, COLON); - break; - default: - throw t.newSyntaxError("Invalid switch case"); - } - t.mustMatch(COLON); - n2.statements = new Node(t, BLOCK); - while ((tt=t.peek()) != CASE && tt != DEFAULT && tt != RIGHT_CURLY) - n2.statements.push(Statement(t, x)); - n.cases.push(n2); - } - x.stmtStack.pop(); - return n; - - case FOR: - n = new Node(t); - n.isLoop = true; - t.mustMatch(LEFT_PAREN); - if ((tt = t.peek()) != SEMICOLON) { - x.inForLoopInit = true; - if (tt == VAR || tt == CONST || tt == LET) { - t.get(); - n2 = Variables(t, x); - } else { - n2 = Expression(t, x); - } - x.inForLoopInit = false; - } - if (n2 && t.match(IN)) { - n.type = FOR_IN; - if (n2.type == VAR || n2.type == LET) { - if (n2.length != 1) { - throw new SyntaxError("Invalid for..in left-hand side", - t.filename, n2.lineno); - } - - // NB: n2[0].type == IDENTIFIER and n2[0].value == n2[0].name. - n.iterator = n2[0]; - n.varDecl = n2; - } else { - n.iterator = n2; - n.varDecl = null; - } - n.object = Expression(t, x); - } else { - n.setup = n2 || null; - t.mustMatch(SEMICOLON); - n.condition = (t.peek() == SEMICOLON) ? null : Expression(t, x); - t.mustMatch(SEMICOLON); - n.update = (t.peek() == RIGHT_PAREN) ? null : Expression(t, x); - } - t.mustMatch(RIGHT_PAREN); - n.body = nest(t, x, n, Statement); - return n; - - case WHILE: - n = new Node(t); - n.isLoop = true; - n.condition = ParenExpression(t, x); - n.body = nest(t, x, n, Statement); - return n; - - case DO: - n = new Node(t); - n.isLoop = true; - n.body = nest(t, x, n, Statement, WHILE); - n.condition = ParenExpression(t, x); - if (!x.ecmaStrictMode) { - // <script language="JavaScript"> (without version hints) may need - // automatic semicolon insertion without a newline after do-while. - // See http://bugzilla.mozilla.org/show_bug.cgi?id=238945. - t.match(SEMICOLON); - return n; - } - break; - - case BREAK: - case CONTINUE: - n = new Node(t); - if (t.peekOnSameLine() == IDENTIFIER) { - t.get(); - n.label = t.token.value; - } - ss = x.stmtStack; - i = ss.length; - label = n.label; - if (label) { - do { - if (--i < 0) - throw t.newSyntaxError("Label not found"); - } while (ss[i].label != label); - } else { - do { - if (--i < 0) { - throw t.newSyntaxError("Invalid " + ((tt == BREAK) - ? "break" - : "continue")); - } - } while (!ss[i].isLoop && (tt != BREAK || ss[i].type != SWITCH)); - } - n.target = ss[i]; - break; - - case TRY: - n = new Node(t); - n.tryBlock = Block(t, x); - n.catchClauses = []; - while (t.match(CATCH)) { - n2 = new Node(t); - t.mustMatch(LEFT_PAREN); - n2.varName = t.mustMatch(IDENTIFIER).value; - if (t.match(IF)) { - if (x.ecmaStrictMode) - throw t.newSyntaxError("Illegal catch guard"); - if (n.catchClauses.length && !n.catchClauses.top().guard) - throw t.newSyntaxError("Guarded catch after unguarded"); - n2.guard = Expression(t, x); - } else { - n2.guard = null; - } - t.mustMatch(RIGHT_PAREN); - n2.block = Block(t, x); - n.catchClauses.push(n2); - } - if (t.match(FINALLY)) - n.finallyBlock = Block(t, x); - if (!n.catchClauses.length && !n.finallyBlock) - throw t.newSyntaxError("Invalid try statement"); - return n; - - case CATCH: - case FINALLY: - throw t.newSyntaxError(tokens[tt] + " without preceding try"); - - case THROW: - n = new Node(t); - n.exception = Expression(t, x); - break; - - case RETURN: - if (!x.inFunction) - throw t.newSyntaxError("Invalid return"); - n = new Node(t); - tt = t.peekOnSameLine(); - if (tt != END && tt != NEWLINE && tt != SEMICOLON && tt != RIGHT_CURLY) - n.value = Expression(t, x); - break; - - case WITH: - n = new Node(t); - n.object = ParenExpression(t, x); - n.body = nest(t, x, n, Statement); - return n; - - case VAR: - case CONST: - case LET: - n = Variables(t, x); - break; - - case DEBUGGER: - n = new Node(t); - break; - - case NEWLINE: - case SEMICOLON: - n = new Node(t, SEMICOLON); - n.expression = null; - return n; - - default: - if (tt == IDENTIFIER) { - t.scanOperand = false; - tt = t.peek(); - t.scanOperand = true; - if (tt == COLON) { - label = t.token.value; - ss = x.stmtStack; - for (i = ss.length-1; i >= 0; --i) { - if (ss[i].label == label) - throw t.newSyntaxError("Duplicate label"); - } - t.get(); - n = new Node(t, LABEL); - n.label = label; - n.statement = nest(t, x, n, Statement); - return n; - } - } - - n = new Node(t, SEMICOLON); - t.unget(); - n.expression = Expression(t, x); - n.end = n.expression.end; - break; - } - - if (t.lineno == t.token.lineno) { - tt = t.peekOnSameLine(); - if (tt != END && tt != NEWLINE && tt != SEMICOLON && tt != RIGHT_CURLY) - throw t.newSyntaxError("Missing ; before statement"); - } - t.match(SEMICOLON); - return n; -} - -function FunctionDefinition(t, x, requireName, functionForm) { - var f = new Node(t); - if (f.type != FUNCTION) - f.type = (f.value == "get") ? GETTER : SETTER; - if (t.match(IDENTIFIER)) - f.name = t.token.value; - else if (requireName) - throw t.newSyntaxError("Missing function identifier"); - - t.mustMatch(LEFT_PAREN); - f.params = []; - var tt; - while ((tt = t.get()) != RIGHT_PAREN) { - if (tt != IDENTIFIER) - throw t.newSyntaxError("Missing formal parameter"); - f.params.push(t.token.value); - if (t.peek() != RIGHT_PAREN) - t.mustMatch(COMMA); - } - - t.mustMatch(LEFT_CURLY); - var x2 = new CompilerContext(true); - f.body = Script(t, x2); - t.mustMatch(RIGHT_CURLY); - f.end = t.token.end; - - f.functionForm = functionForm; - if (functionForm == DECLARED_FORM) - x.funDecls.push(f); - return f; -} - -function Variables(t, x) { - var n = new Node(t); - do { - t.mustMatch(IDENTIFIER); - var n2 = new Node(t); - n2.name = n2.value; - if (t.match(ASSIGN)) { - if (t.token.assignOp) - throw t.newSyntaxError("Invalid variable initialization"); - n2.initializer = Expression(t, x, COMMA); - } - n2.readOnly = (n.type == CONST); - n.push(n2); - x.varDecls.push(n2); - } while (t.match(COMMA)); - return n; -} - -function ParenExpression(t, x) { - t.mustMatch(LEFT_PAREN); - var n = Expression(t, x); - t.mustMatch(RIGHT_PAREN); - return n; -} - -var opPrecedence = { - SEMICOLON: 0, - COMMA: 1, - ASSIGN: 2, HOOK: 2, COLON: 2, - // The above all have to have the same precedence, see bug 330975. - OR: 4, - AND: 5, - BITWISE_OR: 6, - BITWISE_XOR: 7, - BITWISE_AND: 8, - EQ: 9, NE: 9, STRICT_EQ: 9, STRICT_NE: 9, - LT: 10, LE: 10, GE: 10, GT: 10, IN: 10, INSTANCEOF: 10, - LSH: 11, RSH: 11, URSH: 11, - PLUS: 12, MINUS: 12, - MUL: 13, DIV: 13, MOD: 13, - DELETE: 14, VOID: 14, TYPEOF: 14, // PRE_INCREMENT: 14, PRE_DECREMENT: 14, - NOT: 14, BITWISE_NOT: 14, UNARY_PLUS: 14, UNARY_MINUS: 14, - INCREMENT: 15, DECREMENT: 15, // postfix - NEW: 16, - DOT: 17 -}; - -// Map operator type code to precedence. -for (i in opPrecedence) - opPrecedence[GLOBAL[i]] = opPrecedence[i]; - -var opArity = { - COMMA: -2, - ASSIGN: 2, - HOOK: 3, - OR: 2, - AND: 2, - BITWISE_OR: 2, - BITWISE_XOR: 2, - BITWISE_AND: 2, - EQ: 2, NE: 2, STRICT_EQ: 2, STRICT_NE: 2, - LT: 2, LE: 2, GE: 2, GT: 2, IN: 2, INSTANCEOF: 2, - LSH: 2, RSH: 2, URSH: 2, - PLUS: 2, MINUS: 2, - MUL: 2, DIV: 2, MOD: 2, - DELETE: 1, VOID: 1, TYPEOF: 1, // PRE_INCREMENT: 1, PRE_DECREMENT: 1, - NOT: 1, BITWISE_NOT: 1, UNARY_PLUS: 1, UNARY_MINUS: 1, - INCREMENT: 1, DECREMENT: 1, // postfix - NEW: 1, NEW_WITH_ARGS: 2, DOT: 2, INDEX: 2, CALL: 2, - ARRAY_INIT: 1, OBJECT_INIT: 1, GROUP: 1 -}; - -// Map operator type code to arity. -for (i in opArity) - opArity[GLOBAL[i]] = opArity[i]; - -function Expression(t, x, stop) { - var n, id, tt, operators = [], operands = []; - var bl = x.bracketLevel, cl = x.curlyLevel, pl = x.parenLevel, - hl = x.hookLevel; - - function reduce() { - var n = operators.pop(); - var op = n.type; - var arity = opArity[op]; - if (arity == -2) { - // Flatten left-associative trees. - var left = operands.length >= 2 && operands[operands.length-2]; - if (left.type == op) { - var right = operands.pop(); - left.push(right); - return left; - } - arity = 2; - } - - // Always use push to add operands to n, to update start and end. - var a = operands.splice(operands.length - arity); - for (var i = 0; i < arity; i++) - n.push(a[i]); - - // Include closing bracket or postfix operator in [start,end). - if (n.end < t.token.end) - n.end = t.token.end; - - operands.push(n); - return n; - } - -loop: - while ((tt = t.get()) != END) { - if (tt == stop && - x.bracketLevel == bl && x.curlyLevel == cl && x.parenLevel == pl && - x.hookLevel == hl) { - // Stop only if tt matches the optional stop parameter, and that - // token is not quoted by some kind of bracket. - break; - } - switch (tt) { - case SEMICOLON: - // NB: cannot be empty, Statement handled that. - break loop; - - case ASSIGN: - case HOOK: - case COLON: - if (t.scanOperand) - break loop; - // Use >, not >=, for right-associative ASSIGN and HOOK/COLON. - while (opPrecedence[operators.top().type] > opPrecedence[tt] || - (tt == COLON && operators.top().type == ASSIGN)) { - reduce(); - } - if (tt == COLON) { - n = operators.top(); - if (n.type != HOOK) - throw t.newSyntaxError("Invalid label"); - --x.hookLevel; - } else { - operators.push(new Node(t)); - if (tt == ASSIGN) - operands.top().assignOp = t.token.assignOp; - else - ++x.hookLevel; // tt == HOOK - } - t.scanOperand = true; - break; - - case IN: - // An in operator should not be parsed if we're parsing the head of - // a for (...) loop, unless it is in the then part of a conditional - // expression, or parenthesized somehow. - if (x.inForLoopInit && !x.hookLevel && - !x.bracketLevel && !x.curlyLevel && !x.parenLevel) { - break loop; - } - // FALL THROUGH - case COMMA: - // Treat comma as left-associative so reduce can fold left-heavy - // COMMA trees into a single array. - // FALL THROUGH - case OR: - case AND: - case BITWISE_OR: - case BITWISE_XOR: - case BITWISE_AND: - case EQ: case NE: case STRICT_EQ: case STRICT_NE: - case LT: case LE: case GE: case GT: - case INSTANCEOF: - case LSH: case RSH: case URSH: - case PLUS: case MINUS: - case MUL: case DIV: case MOD: - case DOT: - if (t.scanOperand) - break loop; - while (opPrecedence[operators.top().type] >= opPrecedence[tt]) - reduce(); - if (tt == DOT) { - t.mustMatch(IDENTIFIER); - operands.push(new Node(t, DOT, operands.pop(), new Node(t))); - } else { - operators.push(new Node(t)); - t.scanOperand = true; - } - break; - - case DELETE: case VOID: case TYPEOF: - case NOT: case BITWISE_NOT: case UNARY_PLUS: case UNARY_MINUS: - case NEW: - if (!t.scanOperand) - break loop; - operators.push(new Node(t)); - break; - - case INCREMENT: case DECREMENT: - if (t.scanOperand) { - operators.push(new Node(t)); // prefix increment or decrement - } else { - // Don't cross a line boundary for postfix {in,de}crement. - if (t.tokens[(t.tokenIndex + t.lookahead - 1) & 3].lineno != - t.lineno) { - break loop; - } - - // Use >, not >=, so postfix has higher precedence than prefix. - while (opPrecedence[operators.top().type] > opPrecedence[tt]) - reduce(); - n = new Node(t, tt, operands.pop()); - n.postfix = true; - operands.push(n); - } - break; - - case FUNCTION: - if (!t.scanOperand) - break loop; - operands.push(FunctionDefinition(t, x, false, EXPRESSED_FORM)); - t.scanOperand = false; - break; - - case NULL: case THIS: case TRUE: case FALSE: - case IDENTIFIER: case NUMBER: case STRING: case REGEXP: - if (!t.scanOperand) - break loop; - operands.push(new Node(t)); - t.scanOperand = false; - break; - - case LEFT_BRACKET: - if (t.scanOperand) { - // Array initialiser. Parse using recursive descent, as the - // sub-grammar here is not an operator grammar. - n = new Node(t, ARRAY_INIT); - while ((tt = t.peek()) != RIGHT_BRACKET) { - if (tt == COMMA) { - t.get(); - n.push(null); - continue; - } - n.push(Expression(t, x, COMMA)); - if (!t.match(COMMA)) - break; - } - t.mustMatch(RIGHT_BRACKET); - operands.push(n); - t.scanOperand = false; - } else { - // Property indexing operator. - operators.push(new Node(t, INDEX)); - t.scanOperand = true; - ++x.bracketLevel; - } - break; - - case RIGHT_BRACKET: - if (t.scanOperand || x.bracketLevel == bl) - break loop; - while (reduce().type != INDEX) - continue; - --x.bracketLevel; - break; - - case LEFT_CURLY: - if (!t.scanOperand) - break loop; - // Object initialiser. As for array initialisers (see above), - // parse using recursive descent. - ++x.curlyLevel; - n = new Node(t, OBJECT_INIT); - object_init: - if (!t.match(RIGHT_CURLY)) { - do { - tt = t.get(); - if ((t.token.value == "get" || t.token.value == "set") && - t.peek() == IDENTIFIER) { - if (x.ecmaStrictMode) - throw t.newSyntaxError("Illegal property accessor"); - n.push(FunctionDefinition(t, x, true, EXPRESSED_FORM)); - } else { - switch (tt) { - case IDENTIFIER: - case NUMBER: - case STRING: - id = new Node(t); - break; - case RIGHT_CURLY: - if (x.ecmaStrictMode) - throw t.newSyntaxError("Illegal trailing ,"); - break object_init; - default: - throw t.newSyntaxError("Invalid property name"); - } - t.mustMatch(COLON); - n.push(new Node(t, PROPERTY_INIT, id, - Expression(t, x, COMMA))); - } - } while (t.match(COMMA)); - t.mustMatch(RIGHT_CURLY); - } - operands.push(n); - t.scanOperand = false; - --x.curlyLevel; - break; - - case RIGHT_CURLY: - if (!t.scanOperand && x.curlyLevel != cl) - throw "PANIC: right curly botch"; - break loop; - - case LEFT_PAREN: - if (t.scanOperand) { - operators.push(new Node(t, GROUP)); - } else { - while (opPrecedence[operators.top().type] > opPrecedence[NEW]) - reduce(); - - // Handle () now, to regularize the n-ary case for n > 0. - // We must set scanOperand in case there are arguments and - // the first one is a regexp or unary+/-. - n = operators.top(); - t.scanOperand = true; - if (t.match(RIGHT_PAREN)) { - if (n.type == NEW) { - --operators.length; - n.push(operands.pop()); - } else { - n = new Node(t, CALL, operands.pop(), - new Node(t, LIST)); - } - operands.push(n); - t.scanOperand = false; - break; - } - if (n.type == NEW) - n.type = NEW_WITH_ARGS; - else - operators.push(new Node(t, CALL)); - } - ++x.parenLevel; - break; - - case RIGHT_PAREN: - if (t.scanOperand || x.parenLevel == pl) - break loop; - while ((tt = reduce().type) != GROUP && tt != CALL && - tt != NEW_WITH_ARGS) { - continue; - } - if (tt != GROUP) { - n = operands.top(); - if (n[1].type != COMMA) - n[1] = new Node(t, LIST, n[1]); - else - n[1].type = LIST; - } - --x.parenLevel; - break; - - // Automatic semicolon insertion means we may scan across a newline - // and into the beginning of another statement. If so, break out of - // the while loop and let the t.scanOperand logic handle errors. - default: - break loop; - } - } - - if (x.hookLevel != hl) - throw t.newSyntaxError("Missing : after ?"); - if (x.parenLevel != pl) - throw t.newSyntaxError("Missing ) in parenthetical"); - if (x.bracketLevel != bl) - throw t.newSyntaxError("Missing ] in index expression"); - if (t.scanOperand) - throw t.newSyntaxError("Missing operand"); - - // Resume default mode, scanning for operands, not operators. - t.scanOperand = true; - t.unget(); - while (operators.length) - reduce(); - return operands.pop(); -} - -function parse(s, f, l) { - var t = new Tokenizer(s, f, l); - var x = new CompilerContext(false); - var n = Script(t, x); - if (!t.done) - throw t.newSyntaxError("Syntax error"); - return n; -}
--- a/thingy.html Mon Jan 05 16:04:40 2009 -0800 +++ b/thingy.html Mon Jan 05 16:44:08 2009 -0800 @@ -8,17 +8,7 @@ <body> <div id="content"></div> </body> -<script> -// Hack found at: -// http://www.nabble.com/Re%3A-__defineProperty__-by-default-in-SpiderMonkey-p2836854.html - -Object.prototype.__defineProperty__ = function(property, value) { - this[property] = value; -} -</script> <script src="jquery-1.2.6.min.js"></script> -<script src="jsdefs.js"></script> -<script src="jsparse.js"></script> <script src="wikicreole.js"></script> <script src="thingy.js"></script> </html>
--- a/thingy.js Mon Jan 05 16:04:40 2009 -0800 +++ b/thingy.js Mon Jan 05 16:44:08 2009 -0800 @@ -7,18 +7,18 @@ }; App.processCode = function processCode(code) { - var statements = parse(code, App.FILENAME, 1); + var lines = code.split('\n'); var block = ""; jQuery.each( - statements.tokenizer.comments, + lines, function(i) { - var comment = this; - var startIndex = comment.text.indexOf("//"); + var line = this; + var startIndex = line.indexOf("//"); if (startIndex == -1) // This is a /* */-style comment, skip it. return; - var text = comment.text.slice(startIndex + 3); + var text = line.slice(startIndex + 3); block += text + "\n"; });