1
// This requires strands.js
2
/* ***** BEGIN LICENSE BLOCK *****
3
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
5
* The contents of this file are subject to the Mozilla Public License Version
6
* 1.1 (the "License"); you may not use this file except in compliance with
7
* the License. You may obtain a copy of the License at
8
* http://www.mozilla.org/MPL/
10
* Software distributed under the License is distributed on an "AS IS" basis,
11
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12
* for the specific language governing rights and limitations under the
15
* The Original Code is the Narcissus JavaScript engine.
17
* The Initial Developer of the Original Code is
18
* Brendan Eich <brendan@mozilla.org>.
19
* Portions created by the Initial Developer are Copyright (C) 2004
20
* the Initial Developer. All Rights Reserved.
24
* Alternatively, the contents of this file may be used under the terms of
25
* either the GNU General Public License Version 2 or later (the "GPL"), or
26
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27
* in which case the provisions of the GPL or the LGPL are applicable instead
28
* of those above. If you wish to allow use of your version of this file only
29
* under the terms of either the GPL or the LGPL, and not to allow others to
30
* use your version of this file under the terms of the MPL, indicate your
31
* decision by deleting the provisions above and replace them with the notice
32
* and other provisions required by the GPL or the LGPL. If you do not delete
33
* the provisions above, a recipient may use your version of this file under
34
* the terms of any one of the MPL, the GPL or the LGPL.
36
* ***** END LICENSE BLOCK ***** */
39
* Narcissus - JS implemented in JS.
41
* Well-known constants and lookup tables. Many consts are generated from the
42
* tokens table via eval to minimize redundancy, so consumers must be compiled
43
* separately to take advantage of the simple switch-case constant propagation
44
* done by SpiderMonkey.
49
* Added the # operators
51
* - Neil: combine jsdefs.js and jsparse.js into a single file as part of an
52
* effort to reduce namespace pollution.
53
* - Neil: make opTypeName order explicit for env compatibility. The original
54
* source relied on a SpiderMonkey specific behavior where object key
55
* iteration occurs in the same order in which the keys were defined in
57
�* - Neil: perf optimizations for OOM+ parse speedup
58
�* - Neil: make code x-env-compatible
59
�* - chocolateboy 2006-06-01: add support for $ in identifiers and remove support for ` as the first character as per:
60
�* � http://www.mozilla.org/js/language/es4/formal/lexer-semantics.html#N-InitialIdentifierCharacter and
61
�* � http://www.mozilla.org/js/language/es4/formal/lexer-semantics.html#N-ContinuingIdentifierCharacter
68
// EDIT: remove references to global to avoid namespace pollution
70
// EDIT: add yielding op
75
// Operators and punctuators. Some pair-wise order matters, e.g. (+, -)
76
// and (UNARY_PLUS, UNARY_MINUS).
80
"?", ":", "CONDITIONAL",
87
"==", "!=", "===", "!==",
92
"#","!", "~", "UNARY_PLUS", "UNARY_MINUS",
99
// Nonterminal tree node type codes.
100
"SCRIPT", "BLOCK", "LABEL", "FOR_IN", "CALL", "NEW_WITH_ARGS", "INDEX", "TRANSIENT_INDEX",
101
"ARRAY_INIT", "OBJECT_INIT", "PROPERTY_INIT", "GETTER", "SETTER",
105
"IDENTIFIER", "NUMBER", "STRING", "REGEXP",
109
"case", "catch", "const", "continue",
110
"debugger", "default", "delete", "do",
112
"false", "finally", "for", "function",
113
"if", "in", "instanceof", "is",
117
"this", "throw", "true", "try", "typeof",
119
"while", "with" // EDIT: remove trailing comma (breaks IE)
122
// Operator and punctuator mapping from token to tree node type name.
123
// NB: superstring tokens (e.g., ++) must come before their substring token
124
// counterparts (+ in the example), so that the opRegExp regular expression
125
// synthesized from this list makes the longest possible match.
126
// EDIT: NB comment above indicates reliance on SpiderMonkey-specific
127
// behavior in the ordering of key iteration -- see EDIT below.
128
// EDIT: add yeilding op
160
'#': "OBJECT_ID_REFERENCE",
163
'.#': "TRANSIENT_DOT",
165
'#[': "TRANSIENT_LEFT_BRACKET",
167
']': "RIGHT_BRACKET",
174
// EDIT: created separate opTypeOrder array to indicate the order in which
175
// to evaluate opTypeNames. (Apparently, SpiderMonkey must iterate
176
// hash keys in the order in which they are defined, an implementation
177
// detail which the original narcissus code relied on.)
178
// EDIT: add yielding op
224
// Hash of keyword identifier to tokens index. NB: we must null __proto__ to
225
// avoid toString, etc. namespace pollution.
226
var keywords = {__proto__: null};
228
// Define const END, etc., based on the token names. Also map name to index.
229
// EDIT: use "var " prefix to make definitions local to this function
231
for (var i = 0, j = tokens.length; i < j; i++) {
236
if (/^[a-z]/.test(t)) {
237
name = t.toUpperCase();
240
name = (/^\W/.test(t) ? opTypeNames[t] : t);
242
consts += name + " = " + i;
248
// Map assignment operators to their indexes in the tokens array.
249
var assignOps = ['|', '^', '&', '<<', '>>', '>>>', '+', '-', '*', '/', '%'];
251
for (i = 0, j = assignOps.length; i < j; i++) {
253
assignOps[t] = tokens[t];
255
/* vim: set sw=4 ts=8 et tw=80: */
256
/* ***** BEGIN LICENSE BLOCK *****
257
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
259
* The contents of this file are subject to the Mozilla Public License Version
260
* 1.1 (the "License"); you may not use this file except in compliance with
261
* the License. You may obtain a copy of the License at
262
* http://www.mozilla.org/MPL/
264
* Software distributed under the License is distributed on an "AS IS" basis,
265
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
266
* for the specific language governing rights and limitations under the
269
* The Original Code is the Narcissus JavaScript engine.
271
* The Initial Developer of the Original Code is
272
* Brendan Eich <brendan@mozilla.org>.
273
* Portions created by the Initial Developer are Copyright (C) 2004
274
* the Initial Developer. All Rights Reserved.
278
* Alternatively, the contents of this file may be used under the terms of
279
* either the GNU General Public License Version 2 or later (the "GPL"), or
280
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
281
* in which case the provisions of the GPL or the LGPL are applicable instead
282
* of those above. If you wish to allow use of your version of this file only
283
* under the terms of either the GPL or the LGPL, and not to allow others to
284
* use your version of this file under the terms of the MPL, indicate your
285
* decision by deleting the provisions above and replace them with the notice
286
* and other provisions required by the GPL or the LGPL. If you do not delete
287
* the provisions above, a recipient may use your version of this file under
288
* the terms of any one of the MPL, the GPL or the LGPL.
290
* ***** END LICENSE BLOCK ***** */
293
* Narcissus - JS implemented in JS.
295
* Lexical scanner and parser.
299
// Build a regexp that recognizes operators and punctuators (except newline).
300
var opRegExpSrc = "^(?:";
302
// EDIT: change for loop from iterating through opTypeNames keys to using
303
// opTypeOrder array so that we're not dependent on SpiderMonkey's
304
// key order default behavior.
305
// EDIT: change regex structure for OOM perf improvement
306
for (var i = 0; i < opTypeOrder.length; i++) {
307
var op = opTypeOrder[i];
310
if (opRegExpSrc != "^(?:")
313
// EDIT: expand out this regexp for environments that don't support $&
314
//opRegExpSrc += op.replace(/[?|^&(){}\[\]+\-*\/\.]/g, "\\$&");
315
op = op.replace(/\?/g, "\\?");
316
op = op.replace(/\|/g, "\\|");
317
op = op.replace(/\^/g, "\\^");
318
op = op.replace(/\&/g, "\\&");
319
op = op.replace(/\(/g, "\\(");
320
op = op.replace(/\)/g, "\\)");
321
op = op.replace(/\{/g, "\\{");
322
op = op.replace(/\}/g, "\\}");
323
op = op.replace(/\[/g, "\\[");
324
op = op.replace(/\]/g, "\\]");
325
op = op.replace(/\+/g, "\\+");
326
op = op.replace(/\-/g, "\\-");
327
op = op.replace(/\*/g, "\\*");
328
op = op.replace(/\//g, "\\/");
329
op = op.replace(/\./g, "\\.");
333
var opRegExp = new RegExp(opRegExpSrc);
335
// A regexp to match floating point literals (but not integer literals).
336
// EDIT: change regex structure for OOM perf improvement
337
var fpRegExp = /^(?:\d+\.\d*(?:[eE][-+]?\d+)?|\d+(?:\.\d*)?[eE][-+]?\d+|\.\d+(?:[eE][-+]?\d+)?)/;
339
function Tokenizer(s, f, l) {
341
this.source = String(s);
345
this.scanNewlines = false;
346
this.scanOperand = true;
347
this.filename = f || "";
348
this.lineno = l ? l : 1;
351
Tokenizer.prototype = {
353
// EDIT: change "input" from a getter to a regular method for compatibility
354
// with older JavaScript versions
356
return this.source.substring(this.cursor);
359
// EDIT: change "done" from a getter to a regular method for compatibility
360
// with older JavaScript versions
362
return this.peek() == END;
365
// EDIT: change "token" from a getter to a regular method for compatibility
366
// with older JavaScript versions
368
return this.tokens[this.tokenIndex];
371
match: function (tt) {
372
return this.get() == tt || this.unget();
375
mustMatch: function (tt) {
376
if (!this.match(tt)) {
377
throw this.newSyntaxError("Missing " + tokens[tt].toLowerCase());
384
if (this.lookahead) {
385
tt = this.tokens[(this.tokenIndex + this.lookahead) & 3].type;
393
peekOnSameLine: function () {
394
this.scanNewlines = true;
395
var tt = this.peek();
396
this.scanNewlines = false;
402
while (this.lookahead) {
404
this.tokenIndex = (this.tokenIndex + 1) & 3;
405
token = this.tokens[this.tokenIndex];
406
if (token.type != NEWLINE || this.scanNewlines)
411
var input = this.input();
412
var firstChar = input.charCodeAt(0);
413
// EDIT: check first char, then use regex
414
// valid regex whitespace includes char codes: 9 10 11 12 13 32
415
if(firstChar == 32 || (firstChar >= 9 && firstChar <= 13)) {
416
var match = input.match(this.scanNewlines ? /^[ \t]+/ : /^\s+/); // EDIT: use x-browser regex syntax
418
var spaces = match[0];
419
this.cursor += spaces.length;
420
var newlines = spaces.match(/\n/g);
422
this.lineno += newlines.length;
423
input = this.input();
427
// EDIT: improve perf by checking first string char before proceeding to regex,
428
// use x-browser regex syntax
429
if (input.charCodeAt(0) != 47 || !(match = input.match(/^\/(?:\*(?:.|\n)*?\*\/|\/.*)/)))
431
var comment = match[0];
432
this.cursor += comment.length;
433
newlines = comment.match(/\n/g);
435
this.lineno += newlines.length
438
this.tokenIndex = (this.tokenIndex + 1) & 3;
439
token = this.tokens[this.tokenIndex];
441
this.tokens[this.tokenIndex] = token = {};
444
return token.type = END;
446
var firstChar = input.charCodeAt(0);
448
// EDIT: guard by checking char codes before going to regex
449
if ((firstChar == 46 || (firstChar > 47 && firstChar < 58)) &&
450
(match = input.match(fpRegExp))) { // EDIT: use x-browser regex syntax
452
token.value = parseFloat(match[0]);
453
} else if ((firstChar > 47 && firstChar < 58) &&
454
(match = input.match(/^(?:0[xX][\da-fA-F]+|0[0-7]*|\d+)/))) { // EDIT: change regex structure for OOM perf improvement,
455
// use x-browser regex syntax
457
token.value = parseInt(match[0]);
458
} else if (((firstChar > 47 && firstChar < 58) || // EDIT: add guards to check before using regex
459
(firstChar > 64 && firstChar < 91) ||
460
(firstChar > 96 && firstChar < 123) || // EDIT: exclude `
461
(firstChar == 36 || firstChar == 95)) && // EDIT: allow $ + mv _ here
462
(match = input.match(/^[$\w]+/))) { // EDIT: allow $, use x-browser regex syntax
464
// EDIT: check the type of the value in the keywords hash, as different envs
465
// expose implicit Object properties that SpiderMonkey does not.
466
token.type = typeof(keywords[id]) == "number" ? keywords[id] : IDENTIFIER;
468
} else if ((firstChar == 34 || firstChar == 39) &&
469
(match = input.match(/^(?:"(?:\\.|[^"])*"|'(?:[^']|\\.)*')/))) { //"){ // EDIT: change regex structure for OOM perf improvement,
470
// use x-browser regex syntax
472
token.value = eval(match[0]);
473
} else if (this.scanOperand && firstChar == 47 && // EDIT: improve perf by guarding with first char check
474
(match = input.match(/^\/((?:\\.|[^\/])+)\/([gi]*)/))) { // EDIT: use x-browser regex syntax
476
token.value = new RegExp(match[1], match[2]);
477
} else if ((match = input.match(opRegExp))) { // EDIT: use x-browser regex syntax
479
// EDIT: IE doesn't support indexing of strings -- use charAt
480
if (assignOps[op] && input.charAt(op.length) == '=') {
482
token.assignOp = eval(opTypeNames[op]);
485
token.type = eval(opTypeNames[op]);
486
if (this.scanOperand &&
487
(token.type == PLUS || token.type == MINUS)) {
488
token.type += UNARY_PLUS - PLUS;
490
token.assignOp = null;
494
throw this.newSyntaxError("Illegal token");
497
token.start = this.cursor;
498
this.cursor += match[0].length;
499
token.end = this.cursor;
500
token.lineno = this.lineno;
505
if (++this.lookahead == 4) throw "PANIC: too much lookahead!";
506
this.tokenIndex = (this.tokenIndex - 1) & 3;
509
newSyntaxError: function (m) {
510
var e = new SyntaxError(m, this.filename, this.lineno);
511
e.lineNumber = this.lineno; // EDIT: x-browser exception handling
512
e.source = this.source;
513
e.cursor = this.cursor;
518
function CompilerContext(inFunction) {
519
this.inFunction = inFunction;
525
var CCp = CompilerContext.prototype;
526
CCp.bracketLevel = CCp.curlyLevel = CCp.parenLevel = CCp.hookLevel = 0;
527
CCp.ecmaStrictMode = CCp.inForLoopInit = false;
529
function Script(t, x) {
530
var n = Statements(t, x);
532
n.funDecls = x.funDecls;
533
n.varDecls = x.varDecls;
537
// EDIT: change "top" method to be a regular method, rather than defined
538
// via the SpiderMonkey-specific __defineProperty__
540
// Node extends Array, which we extend slightly with a top-of-stack method.
541
Array.prototype.top = function() {
542
return this.length && this[this.length-1];
545
function Node(t, type) {
546
// EDIT: "inherit" from Array in an x-browser way.
548
for (var n in Node.prototype)
549
_this[n] = Node.prototype[n];
551
_this.constructor = Node;
553
var token = t.token();
555
_this.type = type || token.type;
556
_this.value = token.value;
557
_this.lineno = token.lineno;
558
_this.start = token.start;
559
_this.end = token.end;
562
_this.lineno = t.lineno;
566
for (var i = 2; i < arguments.length; i++)
567
_this.push(arguments[i]);
572
var Np = Node.prototype; // EDIT: don't inherit from array
573
Np.toSource = Object.prototype.toSource;
575
// Always use push to add operands to an expression, to update start and end.
576
Np.push = function (kid) {
577
if (kid.start < this.start)
578
this.start = kid.start;
579
if (this.end < kid.end)
582
this[this.length] = kid;
585
Node.indentLevel = 0;
587
Np.toString = function () {
589
for (var i in this) {
590
if (this.hasOwnProperty(i) && i != 'type' && i != 'parent' && typeof(this[i]) != 'function') {
591
// EDIT,BUG: add check for 'target' to prevent infinite recursion
593
a.push({id: i, value: this[i]});
595
a.push({id: i, value: "[token: " + this[i].value + "]"});
599
a.sort(function (a,b) { return (a.id < b.id) ? -1 : 1; });
601
var n = ++Node.indentLevel;
602
var t = tokens[this.type];
603
var s = "{\n" + INDENTATION.repeat(n) +
604
"type: " + (/^\W/.test(t) ? opTypeNames[t] : t.toUpperCase());
605
for (i = 0; i < a.length; i++) {
606
s += ",\n" + INDENTATION.repeat(n) + a[i].id + ": " + a[i].value;}
607
n = --Node.indentLevel;
608
s += "\n" + INDENTATION.repeat(n) + "}";
612
Np.getSource = function () {
613
return this.tokenizer.source.slice(this.start, this.end);
616
// EDIT: change "filename" method to be a regular method, rather than defined
617
// via the SpiderMonkey-specific __defineGetter__
618
Np.filename = function () { return this.tokenizer.filename; };
620
String.prototype.repeat = function (n) {
621
var s = "", t = this + s;
627
// Statement stack and nested statement handler.
628
function nest(t, x, node, func, end) {
629
x.stmtStack.push(node);
632
end && t.mustMatch(end);
636
function Statements(t, x) {
637
var n = Node(t, BLOCK);
639
while (!t.done() && t.peek() != RIGHT_CURLY)
640
n.push(Statement(t, x));
645
function Block(t, x) {
646
t.mustMatch(LEFT_CURLY);
647
var n = Statements(t, x);
648
t.mustMatch(RIGHT_CURLY);
652
var DECLARED_FORM = 0, EXPRESSED_FORM = 1, STATEMENT_FORM = 2;
654
function Statement(t, x) {
655
var i, label, n, n2, ss, tt = t.get();
657
// Cases for statements ending in a right curly return early, avoiding the
658
// common semicolon insertion magic after this switch.
661
return FunctionDefinition(t, x, true,DECLARED_FORM);
662
/*(x.stmtStack.length > 1)
667
n = Statements(t, x);
668
t.mustMatch(RIGHT_CURLY);
673
n.condition = ParenExpression(t, x);
675
n.thenPart = Statement(t, x);
676
n.elsePart = t.match(ELSE) ? Statement(t, x) : null;
682
t.mustMatch(LEFT_PAREN);
683
n.discriminant = Expression(t, x);
684
t.mustMatch(RIGHT_PAREN);
688
t.mustMatch(LEFT_CURLY);
689
while ((tt = t.get()) != RIGHT_CURLY) {
692
if (n.defaultIndex >= 0)
693
throw t.newSyntaxError("More than one switch default");
698
n.defaultIndex = n.cases.length;
700
n2.caseLabel = Expression(t, x, COLON);
703
throw t.newSyntaxError("Invalid switch case");
706
n2.statements = Node(t, BLOCK);
707
while ((tt=t.peek()) != CASE && tt != DEFAULT && tt != RIGHT_CURLY)
708
n2.statements.push(Statement(t, x));
717
t.mustMatch(LEFT_PAREN);
718
if ((tt = t.peek()) != SEMICOLON) {
719
x.inForLoopInit = true;
720
if (tt == VAR || tt == CONST) {
722
n2 = Variables(t, x);
724
n2 = Expression(t, x);
726
x.inForLoopInit = false;
728
if (n2 && t.match(IN)) {
730
if (n2.type == VAR) {
731
if (n2.length != 1) {
732
throw new SyntaxError("Invalid for..in left-hand side",
733
t.filename, n2.lineno);
736
// NB: n2[0].type == IDENTIFIER and n2[0].value == n2[0].name.
743
n.object = Expression(t, x);
745
n.setup = n2 || null;
746
t.mustMatch(SEMICOLON);
747
n.condition = (t.peek() == SEMICOLON) ? null : Expression(t, x);
748
t.mustMatch(SEMICOLON);
749
n.update = (t.peek() == RIGHT_PAREN) ? null : Expression(t, x);
751
t.mustMatch(RIGHT_PAREN);
752
n.body = nest(t, x, n, Statement);
758
n.condition = ParenExpression(t, x);
759
n.body = nest(t, x, n, Statement);
765
n.body = nest(t, x, n, Statement, WHILE);
766
n.condition = ParenExpression(t, x);
767
if (!x.ecmaStrictMode) {
768
// <script language="JavaScript"> (without version hints) may need
769
// automatic semicolon insertion without a newline after do-while.
770
// See http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
779
if (t.peekOnSameLine() == IDENTIFIER) {
781
n.label = t.token().value;
789
throw t.newSyntaxError("Label not found");
790
} while (ss[i].label != label);
794
throw t.newSyntaxError("Invalid " + ((tt == BREAK)
798
} while (!ss[i].isLoop && (tt != BREAK || ss[i].type != SWITCH));
805
n.tryBlock = Block(t, x);
807
while (t.match(CATCH)) {
809
t.mustMatch(LEFT_PAREN);
810
n2.varName = t.mustMatch(IDENTIFIER).value;
812
if (x.ecmaStrictMode)
813
throw t.newSyntaxError("Illegal catch guard");
814
if (n.catchClauses.length && !n.catchClauses.top().guard)
815
throw t.newSyntaxError("Guarded catch after unguarded");
816
n2.guard = Expression(t, x);
820
t.mustMatch(RIGHT_PAREN);
821
n2.block = Block(t, x);
822
n.catchClauses.push(n2);
824
if (t.match(FINALLY))
825
n.finallyBlock = Block(t, x);
826
if (!n.catchClauses.length && !n.finallyBlock)
827
throw t.newSyntaxError("Invalid try statement");
832
throw t.newSyntaxError(tokens[tt] + " without preceding try");
836
n.exception = Expression(t, x);
841
throw t.newSyntaxError("Invalid return");
843
tt = t.peekOnSameLine();
844
// EDIT,BUG?: rather that set n.value (which already has meaning for
845
// nodes), set n.expression
846
if (tt != END && tt != NEWLINE && tt != SEMICOLON && tt != RIGHT_CURLY)
847
n.expression = Expression(t, x);
852
n.object = ParenExpression(t, x);
853
n.body = nest(t, x, n, Statement);
867
n = Node(t, SEMICOLON);
872
if (tt == IDENTIFIER && t.peek() == COLON) {
873
label = t.token().value;
875
for (i = ss.length-1; i >= 0; --i) {
876
if (ss[i].label == label)
877
throw t.newSyntaxError("Duplicate label");
882
n.statement = nest(t, x, n, Statement);
886
n = Node(t, SEMICOLON);
888
n.expression = Expression(t, x);
889
n.end = n.expression.end;
893
if (t.lineno == t.token().lineno) {
894
tt = t.peekOnSameLine();
895
if (tt != END && tt != NEWLINE && tt != SEMICOLON && tt != RIGHT_CURLY)
896
throw t.newSyntaxError("Missing ; before statement " + tokens[tt]);
901
function TypeDefinition(t,v) {
902
if (Narcissus.typeChecking && t.peek() == COLON) { // Edit: Added this for making typed variable
904
if (t.peek() == OBJECT_ID_REFERENCE)
905
t.match(OBJECT_ID_REFERENCE)
906
if (t.peek() == FUNCTION) {
908
v.varType = {value:"function"};
910
else if (t.peek() == MUL) {
912
v.varType = {value:"any"};
915
t.mustMatch(IDENTIFIER);
921
function FunctionDefinition(t, x, requireName, functionForm) {
923
if (f.type != FUNCTION)
924
f.type = (f.value == "get") ? GETTER : SETTER;
925
if (t.match(IDENTIFIER))
926
f.name = t.token().value;
927
else if (requireName)
928
throw t.newSyntaxError("Missing function identifier");
931
t.mustMatch(LEFT_PAREN);
934
while ((tt = t.get()) != RIGHT_PAREN) {
935
if (tt != IDENTIFIER)
936
throw t.newSyntaxError("Missing formal parameter");
938
f.params.push(param);
939
TypeDefinition(t,param);
940
if (t.peek() != RIGHT_PAREN)
945
t.mustMatch(LEFT_CURLY);
946
var x2 = new CompilerContext(true);
947
f.body = Script(t, x2);
948
t.mustMatch(RIGHT_CURLY);
949
f.end = t.token().end;
951
f.functionForm = functionForm;
952
if (functionForm == DECLARED_FORM)
957
function Variables(t, x) {
960
if (!(t.match(IDENTIFIER) || t.match(COLON)))
961
throw t.newSyntaxError("Invalid variable initialization");
963
TypeDefinition(t,n2);
965
if (t.match(ASSIGN)) {
966
if (t.token().assignOp)
967
throw t.newSyntaxError("Invalid variable initialization");
968
n2.initializer = Expression(t, x, COMMA);
970
n2.readOnly = (n.type == CONST);
973
} while (t.match(COMMA));
977
function ParenExpression(t, x) {
978
t.mustMatch(LEFT_PAREN);
979
var n = Expression(t, x);
980
t.mustMatch(RIGHT_PAREN);
984
// EDIT: add yielding op precedence
989
HOOK: 3, COLON: 3, CONDITIONAL: 3,
995
EQ: 9, NE: 9, STRICT_EQ: 9, STRICT_NE: 9,
996
LT: 10, LE: 10, GE: 10, GT: 10, IN: 10, INSTANCEOF: 10, IS: 10,
997
LSH: 11, RSH: 11, URSH: 11,
999
MUL: 13, DIV: 13, MOD: 13,
1000
DELETE: 14, VOID: 14, TYPEOF: 14, // PRE_INCREMENT: 14, PRE_DECREMENT: 14,
1001
NOT: 14, BITWISE_NOT: 14, UNARY_PLUS: 14, UNARY_MINUS: 14,
1002
INCREMENT: 15, DECREMENT: 15, // postfix
1006
DOT: 18,OBJECT_ID_REFERENCE: 19
1009
// Map operator type code to precedence.
1010
// EDIT: slurp opPrecence items into array first, because IE includes
1011
// modified hash items in iterator when modified during iteration
1012
var opPrecedenceItems = [];
1013
for (i in opPrecedence)
1014
opPrecedenceItems.push(i);
1016
for (var i = 0; i < opPrecedenceItems.length; i++) {
1017
var item = opPrecedenceItems[i];
1018
opPrecedence[eval(item)] = opPrecedence[item];
1030
EQ: 2, NE: 2, STRICT_EQ: 2, STRICT_NE: 2,
1031
LT: 2, LE: 2, GE: 2, GT: 2, IN: 2, INSTANCEOF: 2, IS: 2,
1032
LSH: 2, RSH: 2, URSH: 2,
1034
MUL: 2, DIV: 2, MOD: 2,
1035
DELETE: 1, VOID: 1, TYPEOF: 1, OBJECT_ID_REFERENCE: 1,// PRE_INCREMENT: 1, PRE_DECREMENT: 1,
1036
NOT: 1, BITWISE_NOT: 1, UNARY_PLUS: 1, UNARY_MINUS: 1,
1037
INCREMENT: 1, DECREMENT: 1, // postfix
1038
NEW: 1, NEW_WITH_ARGS: 2, DOT: 2, TRANSIENT_DOT: 2, INDEX: 2, TRANSIENT_INDEX: 2, CALL: 2, YIELDING: 3,
1039
ARRAY_INIT: 1, OBJECT_INIT: 1, GROUP: 1
1042
// Map operator type code to arity.
1043
// EDIT: same as above
1044
var opArityItems = [];
1046
opArityItems.push(i);
1048
for (var i = 0; i < opArityItems.length; i++) {
1049
var item = opArityItems[i];
1050
opArity[eval(item)] = opArity[item];
1053
function Expression(t, x, stop) {
1054
var n, id, tt, operators = [], operands = [];
1055
var bl = x.bracketLevel, cl = x.curlyLevel, pl = x.parenLevel,
1059
var n = operators.pop();
1061
var arity = opArity[op];
1063
// Flatten left-associative trees.
1064
var left = operands.length >= 2 && operands[operands.length-2];
1065
if (left.type == op) {
1066
var right = operands.pop();
1073
// Always use push to add operands to n, to update start and end.
1074
// EDIT: provide second argument to splice or IE won't work.
1075
var index = operands.length - arity;
1076
var a = operands.splice(index, operands.length - index);
1077
for (var i = 0; i < arity; i++)
1080
// Include closing bracket or postfix operator in [start,end).
1081
if (n.end < t.token().end)
1082
n.end = t.token().end;
1089
while ((tt = t.get()) != END) {
1091
x.bracketLevel == bl && x.curlyLevel == cl && x.parenLevel == pl &&
1092
x.hookLevel == hl) {
1093
// Stop only if tt matches the optional stop parameter, and that
1094
// token is not quoted by some kind of bracket.
1099
// NB: cannot be empty, Statement handled that.
1107
// Use >, not >=, for right-associative ASSIGN and HOOK/COLON.
1108
while (opPrecedence[operators.top().type] > opPrecedence[tt])
1111
n = operators.top();
1113
throw t.newSyntaxError("Invalid label");
1114
n.type = CONDITIONAL;
1117
operators.push(Node(t));
1119
operands.top().assignOp = t.token().assignOp;
1121
++x.hookLevel; // tt == HOOK
1123
t.scanOperand = true;
1127
// An in operator should not be parsed if we're parsing the head of
1128
// a for (...) loop, unless it is in the then part of a conditional
1129
// expression, or parenthesized somehow.
1130
if (x.inForLoopInit && !x.hookLevel &&
1131
!x.bracketLevel && !x.curlyLevel && !x.parenLevel) {
1136
// Treat comma as left-associative so reduce can fold left-heavy
1137
// COMMA trees into a single array.
1144
case EQ: case NE: case STRICT_EQ: case STRICT_NE:
1145
case LT: case LE: case GE: case GT:
1146
case INSTANCEOF: case IS:
1147
case LSH: case RSH: case URSH:
1148
case PLUS: case MINUS:
1149
case MUL: case DIV: case MOD:
1150
case DOT: case TRANSIENT_DOT:
1153
while (opPrecedence[operators.top().type] >= opPrecedence[tt])
1156
t.mustMatch(IDENTIFIER);
1157
operands.push(Node(t, DOT, operands.pop(), Node(t)));
1158
} else if (tt == TRANSIENT_DOT) {
1159
t.mustMatch(IDENTIFIER);
1160
operands.push(Node(t, TRANSIENT_DOT, operands.pop(), Node(t)));
1162
operators.push(Node(t));
1163
t.scanOperand = true;
1167
case DELETE: case VOID: case TYPEOF:
1168
case NOT: case BITWISE_NOT: case UNARY_PLUS: case UNARY_MINUS: case OBJECT_ID_REFERENCE:
1172
operators.push(Node(t));
1175
case INCREMENT: case DECREMENT:
1176
if (t.scanOperand) {
1177
operators.push(Node(t)); // prefix increment or decrement
1179
// Use >, not >=, so postfix has higher precedence than prefix.
1180
while (opPrecedence[operators.top().type] > opPrecedence[tt])
1182
n = Node(t, tt, operands.pop());
1191
operands.push(FunctionDefinition(t, x, false, EXPRESSED_FORM));
1192
t.scanOperand = false;
1195
case NULL: case THIS: case TRUE: case FALSE:
1196
case IDENTIFIER: case NUMBER: case STRING: case REGEXP:
1199
operands.push(Node(t));
1200
t.scanOperand = false;
1204
if (t.scanOperand) {
1205
// Array initialiser. Parse using recursive descent, as the
1206
// sub-grammar here is not an operator grammar.
1207
n = Node(t, ARRAY_INIT);
1208
while ((tt = t.peek()) != RIGHT_BRACKET) {
1214
n.push(Expression(t, x, COMMA));
1215
if (!t.match(COMMA))
1218
t.mustMatch(RIGHT_BRACKET);
1220
t.scanOperand = false;
1222
// Property indexing operator.
1223
operators.push(Node(t, INDEX));
1224
t.scanOperand = true;
1228
case TRANSIENT_LEFT_BRACKET:
1229
// Property indexing operator.
1230
operators.push(new Node(t, TRANSIENT_INDEX));
1231
t.scanOperand = true;
1235
if (t.scanOperand || x.bracketLevel == bl)
1238
var type = reduce().type;
1240
while (type != INDEX && type != TRANSIENT_INDEX)
1247
// Object initialiser. As for array initialisers (see above),
1248
// parse using recursive descent.
1250
n = Node(t, OBJECT_INIT);
1252
if (!t.match(RIGHT_CURLY)) {
1255
if ((t.token().value == "get" || t.token().value == "set") &&
1256
t.peek() == IDENTIFIER) {
1257
if (x.ecmaStrictMode)
1258
throw t.newSyntaxError("Illegal property accessor");
1259
n.push(FunctionDefinition(t, x, true, EXPRESSED_FORM));
1268
if (x.ecmaStrictMode)
1269
throw t.newSyntaxError("Illegal trailing ,");
1272
throw t.newSyntaxError("Invalid property name");
1275
n.push(Node(t, PROPERTY_INIT, id,
1276
Expression(t, x, COMMA)));
1278
} while (t.match(COMMA));
1279
t.mustMatch(RIGHT_CURLY);
1282
t.scanOperand = false;
1287
if (!t.scanOperand && x.curlyLevel != cl)
1288
throw "PANIC: right curly botch";
1292
while (opPrecedence[operators.top().type] > opPrecedence[YIELDING])
1294
t.mustMatch(LEFT_PAREN);
1295
var yielding = true;
1299
if (t.scanOperand) {
1300
operators.push(Node(t, GROUP));
1302
while (opPrecedence[operators.top().type] > opPrecedence[NEW])
1305
// Handle () now, to regularize the n-ary case for n > 0.
1306
// We must set scanOperand in case there are arguments and
1307
// the first one is a regexp or unary+/-.
1308
n = operators.top();
1309
t.scanOperand = true;
1310
if (t.match(RIGHT_PAREN)) {
1311
if (n.type == NEW) {
1312
n.type = NEW_WITH_ARGS;
1314
n.push(operands.pop());
1316
n = Node(t, CALL, operands.pop(),
1320
t.scanOperand = false;
1321
n.yielding = yielding || false;
1324
if (n.type == NEW) {
1325
n.type = NEW_WITH_ARGS;
1330
n.yielding = yielding || false;
1336
if (t.scanOperand || x.parenLevel == pl)
1338
while ((tt = reduce().type) != GROUP && tt != CALL &&
1339
tt != NEW_WITH_ARGS) {
1344
if (n[1].type != COMMA)
1345
n[1] = Node(t, LIST, n[1]);
1352
// Automatic semicolon insertion means we may scan across a newline
1353
// and into the beginning of another statement. If so, break out of
1354
// the while loop and let the t.scanOperand logic handle errors.
1360
if (x.hookLevel != hl)
1361
throw t.newSyntaxError("Missing : after ?");
1363
throw t.newSyntaxError("Missing operand");
1365
// Resume default mode, scanning for operands, not operators.
1366
t.scanOperand = true;
1368
while (operators.length)
1370
return operands.pop();
1373
function parse(s, f, l) {
1374
var t = new Tokenizer(s, f, l);
1375
var x = new CompilerContext(false);
1376
var n = Script(t, x);
1378
throw t.newSyntaxError("Syntax error");
1382
// make stuff visible to StrandsCompiler
1385
this.tokens = tokens;
1386
this.consts = consts;
1389
/* ***** BEGIN LICENSE BLOCK *****
1390
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
1392
* The contents of this file are subject to the Mozilla Public License Version
1393
* 1.1 (the "License"); you may not use this file except in compliance with
1394
* the License. You may obtain a copy of the License at
1395
* http://www.mozilla.org/MPL/
1397
* Software distributed under the License is distributed on an "AS IS" basis,
1398
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
1399
* for the specific language governing rights and limitations under the
1402
* The Original Code is the Narrative JavaScript compiler.
1404
* The Initial Developer of the Original Code is
1405
* Neil Mix (neilmix -at- gmail -dot- com).
1406
* Portions created by the Initial Developer are Copyright (C) 2006
1407
* the Initial Developer. All Rights Reserved.
1411
* Alternatively, the contents of this file may be used under the terms of
1412
* either the GNU General Public License Version 2 or later (the "GPL"), or
1413
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
1414
* in which case the provisions of the GPL or the LGPL are applicable instead
1415
* of those above. If you wish to allow use of your version of this file only
1416
* under the terms of either the GPL or the LGPL, and not to allow others to
1417
* use your version of this file under the terms of the MPL, indicate your
1418
* decision by deleting the provisions above and replace them with the notice
1419
* and other provisions required by the GPL or the LGPL. If you do not delete
1420
* the provisions above, a recipient may use your version of this file under
1421
* the terms of any one of the MPL, the GPL or the LGPL.
1423
* ***** END LICENSE BLOCK ***** */
1425
NJS_CODE = Narcissus.REGEXP; // fake node type for strands code segment
1427
// declare it this way so that when it is evaled it gets put in the global scope
1428
StrandsCompiler = function (options) {
1429
this.nodeSequence = 0;
1430
this.options = options || {};
1431
this.parseBooleanOptions("exceptions", true);
1432
this.parseBooleanOptions("defaultYield", true);
1435
var strandscp = StrandsCompiler.prototype;
1437
strandscp.parseBooleanOptions = function (/*...*/) {
1438
var options = this.options;
1439
for (var i = 0; i < arguments.length; i += 2) {
1440
var name = arguments[i];
1441
var value = options[name];
1442
if (value == null) {
1443
options[name] = arguments[i+1];
1445
if (typeof(value) == "string")
1446
value = value.toLowerCase();
1448
options[name] = value == "yes" || value == "true" || value == "on" || value == "1";
1453
strandscp.compile = function (/*string*/ code, /*string*/ scriptName,thisObject,scopeObject) {
1455
var frame = {_cp:0};
1457
var frame = _frm(this,arguments,[],[]);
1459
Narcissus.typeChecking = this.options.typeChecking;
1460
frame.n = Narcissus.parse(code, scriptName, 1);
1461
this.treeify(frame.n);
1462
var resolver = frame.resolver = new StrandsScopeResolver();
1463
resolver.push(frame.n, false);
1464
resolver.globalChecking = !(!thisObject);
1465
resolver.thisObject = thisObject;
1466
resolver.scopeObject = scopeObject || {};
1471
var errors = strandscp.currentError = {};
1472
frame.n = this.assemble(frame.n, frame.resolver, this.options);
1474
if (strands && e == strands.Suspension) // this is a continuation escape, suspend so we can try again later
1476
throw e; // this is a real error
1480
var errors = strandscp.currentError = {};
1481
frame.n = this.assemble(frame.n, frame.resolver, this.options);
1485
var writer = new StrandsScriptWriter();
1486
if (writer.debug = this.options.debug)
1487
writer.sourceLines = code.split('\n');
1488
writer.add(frame.n);
1489
return writer.toString();
1492
strandscp.treeify = function(n) {
1493
if (n.type == Narcissus.SWITCH) {
1494
// hack. *sigh* n.cases is an array, not a Node. So we have
1495
// to fool our treeification process into thinking this node
1496
// has Node children.
1497
for (var i = 0; i < n.cases.length; i++) {
1500
n.length = n.cases.length;
1503
if (n.type == Narcissus.TRY) {
1504
// another hack. catchClauses is a regular array.
1505
for (var i = 0; i < n.catchClauses.length; i++) {
1506
n["catch" + i] = n.catchClauses[i];
1510
// for-in constructs will use the identifier node within its varDecl
1511
// as the iterator value, which means it may get operated on twice
1512
// during the treeification process.
1517
if (this.options.defaultYield && (n.type==Narcissus.CALL || n.type==Narcissus.NEW || n.type==Narcissus.NEW_WITH_ARGS || n.type==Narcissus.INDEX || n.type==Narcissus.DOT))
1518
n.yielding = !n.yielding;
1521
typeof(n[i]) == 'object' &&
1522
n[i].constructor == Narcissus.Node &&
1528
// set this before treeification so that our parent is available
1529
// in the post-treeification below.
1532
this.treeify(child);
1534
if(child.yielding && child.type != Narcissus.FUNCTION)
1539
if (n.type == Narcissus.TRY) {
1541
while (n != null && n.type != Narcissus.FUNCTION)
1549
strandscp.noderize = function(opts) {
1550
var n = new Array();
1551
n.nodeID = this.nodeSequence++;
1552
n.toString = Narcissus.Node.prototype.toString;
1554
for (var i in opts) {
1562
strandscp.assemble = function(root, scopeResolver, options) {
1563
function ObjectType(object,field) {
1564
this.object = object;
1567
ObjectType.prototype = {
1568
getValue : function() {
1571
else if (this.object && this.field)
1572
exceptingGet(this.object,this.field);
1574
getType : function() {
1575
if (this.object && this.field)
1576
return exceptingGet(exceptingGet(this.object,"structure"), this.field);
1579
// make consts names available to execute()
1580
eval(Narcissus.consts);
1581
// declare these locally so they can be used within closures
1582
var noderize = StrandsCompiler.prototype.noderize;
1583
var assemble = StrandsCompiler.prototype.assemble;
1585
var codePtrSequence = 0;
1589
var exPtrStack = [];
1591
var statements = [];
1592
while (root.length) {
1594
statements.push(execute(root.shift()));
1597
for( var i = 0; i < statements.length; i++ ) {
1599
root.push(statements[i]);
1604
function execute(node) {
1609
lineno = node.lineno;
1612
throw new Error("what's a script doing in a statement?");
1615
if (scopeResolver.isYielding() && node.name) {
1617
node.name = scopeResolver.addSymbol(node.name,FUNCTION_TYPE);
1619
if (isYielding(node.body)) {
1620
scopeResolver.push(node.body, true);
1622
var scopedVars = scopeResolver.scopes[scopeResolver.scopes.length-1];
1623
for (var i in scopedVars) {
1624
vars += ',"' + i + '"';
1626
vars = vars.substring(1);
1628
for (var i = 0; i < node.params.length; i++) {
1629
params += ',"' + node.params[i].value + '"';
1630
scopeResolver.addSymbol(node.params[i].value,node.params[i].varType||ANY_TYPE);
1632
params = params.substring(1);
1634
codeNode('var _scope=_frm(this,arguments,[~],[~]);with(_scope){', // we could make it more compact in non debug mode with: codeNode(options.debug?'var _scope=_frm(this,arguments,[~],[~]);with(_scope){':'with(_frm(this,arguments,[~],[~])){',
1636
codeNode('while(1){'),
1637
(!options.exceptions ?
1638
codeNode("$_noex=1;") :
1642
codeNode('switch(_cp){case 0:')
1645
while (openNodes.length)
1646
node.body.unshift(openNodes.pop());
1648
node.body.push(codeNode('return;' + (options.persistent?'case -1:return _s()}':'}')));
1649
if (options.exceptions && node.hasTry)
1650
node.body.push(codeNode("}catch(ex){_s(ex)}"));
1651
node.body.push(codeNode("}}"));
1653
assemble(node.body, scopeResolver, options);
1654
node.params = []; // get rid of the parameters so that the JS compression doesn't eliminate them.
1655
scopeResolver.pop();
1657
for (var i = 0; i < node.params.length; i++) {
1658
scopeResolver.addSymbol(node.params[i].value,node.params[i].varType||ANY_TYPE);
1659
node.params[i] = node.params[i].value;
1662
scopeResolver.push(node.body, false);
1663
assemble(node.body, scopeResolver, options);
1664
scopeResolver.pop();
1667
case ASSIGN: case DELETE:
1668
var propName = node[0].value;
1669
if (options.persistence && node[0].type == DOT && withinYielding(node)) {
1670
if (node.type == DELETE)
1671
node[1] = noderize({
1674
1: codeNode('"' + propName + '"')});
1676
node[1] = noderize({
1679
1: codeNode('"' + propName + '"'),
1683
node[0] = codeNode("_p");
1685
else if (options.persistence && node[0].type == INDEX && withinYielding(node)) {
1686
var index = node[0][1];
1687
if (node.type == DELETE)
1688
node[1] = noderize({
1693
node[1] = noderize({
1700
node[0] = codeNode("_p");
1703
for (var i = 0; i < node.length; i++) {
1704
node[i] = execute(node[i]);
1706
if (node.type != DELETE)
1707
typeCheck(node[0],node[1]);
1711
case NEW: case NEW_WITH_ARGS: case CALL:
1712
// If it is persistent, we can go right to the transients on function calls
1713
if (node[0].type == TRANSIENT_DOT)
1715
if (node[0].type == TRANSIENT_INDEX)
1716
node[0].type = INDEX;
1718
// execute our identifier and args *first*
1719
node[0] = execute(node[0]);
1720
if (node[0].value == "spawn" && node[0].type == IDENTIFIER) {
1721
node[1].yielding = false;
1725
if (node.type == NEW)
1726
varType = node[0].varType;
1727
if (!isYielding(node) || !withinYielding(node))
1729
if (node[1]) {// new (without args) doesn't have a list
1730
if (node.type == NEW_WITH_ARGS)
1731
node[1].type = ARRAY_INIT;
1732
var newArgs = execute(node[1]);
1737
// update our code pointer
1741
// set up our re-entry point
1742
newCodeSegment(codePtrSequence);
1743
if (node.type!=CALL) { // This is the persistent JavaScript extension to do news on objects
1744
node = noderize({type: CALL,
1746
0 : codeNode("_new"),
1747
1 : noderize({type: LIST,
1751
node[1][1] = newArgs;
1753
// remove the call node from the stack,
1754
// replace it with rv if necessary
1755
if (stack.length > 1 && stack[stack.length-2].type == SEMICOLON) {
1756
// simple semicolon expression. don't bother
1757
// with retval -- there aren't any dependencies
1760
var newNode = codeNode("_r.v~",
1762
newNode.varType = varType;
1763
replaceNode(newNode);
1766
var checkRetNode = noderize({
1768
condition: noderize({
1774
0: codeNode("_r.v~",
1781
thenPart: noderize({
1783
expression: codeNode("return _s()")
1787
// add our call as a statement at the top level
1788
statements.push(checkRetNode);
1793
node.condition = execute(node.condition);
1794
if (isYielding(node.thenPart) ||
1795
(node.elsePart && isYielding(node.elsePart)))
1797
var thenPtr = ++codePtrSequence;
1799
var elsePtr = ++codePtrSequence;
1800
var endPtr = ++codePtrSequence;
1801
newConditional(node.condition, thenPtr, elsePtr || endPtr);
1802
newCodeSegment(thenPtr);
1805
execBlock(node.thenPart);
1806
gotoCodeSegment(endPtr);
1809
if (node.elsePart) {
1810
newCodeSegment(elsePtr);
1811
execBlock(node.elsePart);
1812
gotoCodeSegment(endPtr);
1816
newCodeSegment(endPtr);
1820
// make sure we catch any breaks or continues
1821
node.thenPart = execute(node.thenPart);
1823
node.elsePart = execute(node.elsePart);
1827
case FOR_IN: // varDecl/iterator, object, body
1828
if (node.varDecl == null) {
1829
node.iterator = execute(node.iterator);
1831
node.varDecl = execute(node.varDecl);
1833
node.object = execute(node.object);
1835
/* if (!isYielding(node.body)) { // we will always do it so they we can weed out dont-enums.
1836
node.body = execute(node.body);
1840
// grab all items from the object and stick them in a local array
1841
var iterId = codePtrSequence;
1842
statements.push(noderize({
1844
value: subst("_r.iter~=_keys(", iterId),
1845
lineno: node.object.lineno
1847
statements.push(node.object);
1850
// change the FOR_IN into a regular FOR
1852
node.setup = codeNode("_r.ctr~=0;", iterId);
1853
node.condition = codeNode("_r.ctr~<_r.iter~.length",
1855
node.update = codeNode("_r.ctr~++", iterId);
1856
var initializer = codeNode("_r.iter~[_r.ctr~]",
1858
// make sure our body is a block so we can add a statement to it
1859
if (node.body.type != BLOCK)
1860
node.body = noderize({type: BLOCK, yielding: true, 0: node.body});
1862
if (node.varDecl == null) {
1863
// iterator -- create an assignment
1864
var assign = noderize({type: ASSIGN});
1865
assign.push(node.iterator);
1866
assign.push(initializer);
1867
node.body.unshift(noderize({type: SEMICOLON, expression: assign}));
1869
// varDecl -- use the initializer
1870
node.varDecl[0].initializer = initializer;
1871
node.body.unshift(node.varDecl);
1873
node.iterator = null;
1874
node.varDecl = null;
1878
node.setup = execute(node.setup);
1879
if (!isYielding(node.body) && !isYielding(node.update)) {
1880
node.condition = execute(node.condition);
1881
node.update = execute(node.update);
1882
node.body = execute(node.body);
1886
// turn it into a WHILE statement
1889
// move the setup before the while
1890
if(node.setup.type != VAR && node.setup.type != NJS_CODE)
1891
node.setup = noderize({type: SEMICOLON, expression: node.setup});
1893
statements.push(node.setup);
1896
// make sure our body is a block so we can add a statement to it
1897
if (node.body.type != BLOCK)
1898
node.body = noderize({type: BLOCK, 0: node.body});
1900
node.updatePtr = ++codePtrSequence;
1901
node.body.push(newCodeSegmentNode(node.updatePtr));
1903
// make sure the proper update happens in the block
1904
node.body.push(noderize({type: SEMICOLON, expression: node.update}));
1910
if (isYielding(node)) {
1911
node.continuePtr = ++codePtrSequence;
1912
newCodeSegment(node.continuePtr);
1913
node.condition = execute(node.condition);
1915
var bodyPtr = ++codePtrSequence;
1916
node.breakPtr = ++codePtrSequence;
1917
newConditional(node.condition, bodyPtr, node.breakPtr);
1918
newCodeSegment(bodyPtr);
1919
execBlock(node.body);
1920
gotoCodeSegment(node.continuePtr);
1921
newCodeSegment(node.breakPtr);
1925
node.condition = execute(node.condition);
1926
node.body = execute(node.body);
1931
if (isYielding(node)) {
1932
node.continuePtr = ++codePtrSequence;
1933
node.breakPtr = ++codePtrSequence;
1934
newCodeSegment(node.continuePtr);
1935
execBlock(node.body);
1937
newConditional(execute(node.condition), node.continuePtr, node.breakPtr);
1938
newCodeSegment(node.breakPtr);
1942
node.condition = execute(node.condition);
1943
node.body = execute(node.body);
1948
if (node.target.breakPtr != null) {
1949
replaceNode(codeNode("_cp=~;break;",
1950
node.target.breakPtr));
1955
if (node.target.continuePtr != null) {
1956
replaceNode(codeNode("_cp=~;break;",
1957
node.target.updatePtr || node.target.continuePtr));
1962
if (!isYielding(node))
1965
node.breakPtr = ++codePtrSequence;
1966
var conditional = null;
1967
if (node.defaultIndex >= 0) {
1968
node[node.defaultIndex].codePtr = ++codePtrSequence;
1969
conditional = codeNode(node[node.defaultIndex].codePtr);
1971
conditional = codeNode(node.breakPtr);
1974
for (var i = node.length - 1; i >= 0; i--) {
1975
if (i == node.defaultIndex)
1978
// adjust the line numbering of the case label nodes
1979
removeLineNumbers(node[i].caseLabel);
1981
node[i].codePtr = ++codePtrSequence;
1982
conditional = noderize({
1986
0: node.discriminant,
1987
1: node[i].caseLabel
1989
1: codeNode(node[i].codePtr),
1994
statements.push(noderize({
1996
expression: noderize({
1999
1: execute(conditional)
2002
statements.push(codeNode("break;"));
2004
for (var i = 0; i < node.length; i++) {
2005
newCodeSegment(node[i].codePtr);
2006
execBlock(node[i].statements);
2009
newCodeSegment(node.breakPtr);
2015
if (isYielding(node)) {
2016
//throw new Error("yielding within " + Narcissus.tokens[node.type].toUpperCase() + " not supported");
2017
var oldStatements = statements;
2018
var innerStatements = statements = [];
2019
node.body.unshift(codeNode('switch(_cp){case ~:',codePtrSequence));
2020
var body = execute(node.body);
2021
for (var i = 0; i < body.length; i++)
2025
statements = oldStatements;
2027
for( var i = 0; i < innerStatements.length; i++ ) {
2028
if (innerStatements[i]) {
2029
if (innerStatements[i].codeSegmentId)
2030
newCodeSegment(innerStatements[i].codeSegmentId);
2031
node.body.push(innerStatements[i]);
2035
node.body.push(codeNode('_cp=~}break;',codePtrSequence));
2036
statements.push(node);
2038
newCodeSegment(codePtrSequence);
2043
if (!isYielding(node))
2046
if (!options.exceptions)
2047
throw new Error("yielding within try/catch/finally not allowed when the exceptions are turned off in the compiler");
2049
// set codeptr for catches, finally, endptr
2050
for (var i = 0; i < node.catchClauses.length; i++) {
2051
node.catchClauses[i].codePtr = ++codePtrSequence;
2054
if (node.finallyBlock)
2055
node.finallyBlock.codePtr = ++codePtrSequence;
2057
var endPtr = ++codePtrSequence;
2059
// set exception codePtr
2060
var exCodePtr = node.catchClauses.length ?
2061
node.catchClauses[0].codePtr :
2062
node.finallyBlock.codePtr;
2064
addCode("_r.ecp=~;", exCodePtr);
2065
exPtrStack.push(exCodePtr);
2066
execBlock(node.tryBlock);
2067
node.finallyBlock ? gotoCodeSegment(node.finallyBlock.codePtr) :
2068
gotoCodeSegment(endPtr);
2071
for (var i = 0; i < node.catchClauses.length; i++) {
2072
var clause = node.catchClauses[i];
2073
newCodeSegment(clause.codePtr);
2076
// first catch block
2077
// set exception codePtr appropriately
2078
addCode("_r.ecp=~;",
2080
node.finallyBlock.codePtr :
2081
(exPtrStack.top() || "null"));
2082
// reset throwing flag to prevent infinite loopage
2083
addCode("$_thr=false;");
2086
// set our exception var. This will override any masked
2087
// variables with the same name. Technically this is
2088
// incorrect behavior. I should fix this, but I'm too
2090
scopeResolver.addSymbol(clause.varName,ANY_TYPE)
2091
addCode("~ = $_ex;", scopeResolver.getSymbol(clause.varName));
2094
clause.guard = execute(clause.guard);
2095
statements.push(noderize({
2098
lineno: clause.guard.lineno
2100
statements.push(clause.guard);
2103
// handle missed guard clause carefully
2104
if (i < node.catchClauses.length - 1) {
2105
gotoCodeSegment(node.catchClauses[i+1].codePtr);
2106
} else if (node.finallyBlock) {
2107
gotoCodeSegment(node.finallyBlock.codePtr);
2108
} else if (exPtrStack.length) {
2109
gotoCodeSegment(exPtrStack.top());
2111
addCode("throw ~;", scopeResolver.getSymbol(clause.varName));
2117
if (node.finallyBlock)
2118
exPtrStack.push(node.finallyBlock.codePtr);
2119
execBlock(clause.block);
2120
if (node.finallyBlock)
2123
// handle successful execution of catch clause
2124
if (node.finallyBlock) {
2125
gotoCodeSegment(node.finallyBlock.codePtr);
2127
gotoCodeSegment(endPtr);
2131
if (node.finallyBlock) {
2132
newCodeSegment(node.finallyBlock.codePtr);
2134
// set the exception code pointer
2135
addCode("_r.ecp=~;",
2136
exPtrStack.top() || "null");
2138
execBlock(node.finallyBlock);
2140
// if we're throwing, rethrow, otherwise goto endPtr
2141
addCode("if($_thr){");
2142
if (exPtrStack.length) {
2143
gotoCodeSegment(exPtrStack.top());
2145
addCode("$_except($_ex);return;");
2148
gotoCodeSegment(endPtr);
2152
newCodeSegment(endPtr);
2156
case TRUE: case FALSE: node.varType = "Boolean"; break;
2158
node.varType = "Number";
2161
node.varType = "String";
2165
node.varType = new ObjectType();
2166
node.varType.value = scopeResolver.thisObject;
2167
node.varType.type = scopeResolver.thisObject;
2168
case DEBUGGER: case LABEL: case NULL:
2169
case REGEXP: case NJS_CODE:
2174
node.value = scopeResolver.getSymbol(node.value);
2175
node.initializer = execute(node.initializer);
2176
if (node.initializer)
2177
typeCheck(node,node.initializer);
2181
node.exception = execute(node.exception);
2186
node.expression = execute(node.expression);
2190
// because of the "guarding" nature of boolean comparisons, we need to
2191
// pull out comparisons with right-side yields into their own
2192
// statements and transform them separately.
2194
var right = node[1];
2196
node[0] = left = execute(left);
2197
if (!isYielding(right)) {
2198
node[1] = execute(right);
2202
var condVar = "_c" + codePtrSequence;
2204
// put the left in it's own assign statement
2205
statements.push(noderize({
2207
expression: noderize({
2209
0: codeNode("var ~", condVar),
2214
// create a boolean node that indicates whether or not the left guards
2215
// against execution of the right
2216
var cond = codeNode(condVar);
2217
if (node.type == OR) {
2225
// create an if node that checks the guarded value and executes
2226
// the right if appropriate
2227
var guard = noderize({
2230
thenPart: noderize({
2232
expression: noderize({
2234
0: codeNode(condVar),
2243
// execute the if node as if it were top-level
2244
var tmpStack = stack;
2246
statements.push(execute(guard));
2249
// finally, hand back the result of the guarding process
2250
node.type = NJS_CODE;
2251
node.value = condVar;
2254
if(scopeResolver.isYielding())
2256
for (var i = 0; i < node.length; i++) {
2257
node[i] = execute(node[i]);
2262
case EQ: case NE: case STRICT_EQ: case STRICT_NE:
2263
case LT: case LE: case GE: case GT:
2264
case TYPEOF: case NOT: case INSTANCEOF:
2265
node.varType = "Boolean";
2266
for (var i = 0; i < node.length; i++) {
2267
node[i] = execute(node[i]);
2270
case UNARY_PLUS: case UNARY_MINUS: case INCREMENT: case DECREMENT:
2271
case LSH: case RSH: case URSH:
2272
case MINUS: case MUL: case DIV: case MOD:
2273
node.varType = "Number";
2274
for (var i = 0; i < node.length; i++) {
2275
node[i] = execute(node[i]); // should do a type check here
2278
case ARRAY_INIT: case OBJECT_INIT:
2279
node.varType = "Object";
2280
for (var i = 0; i < node.length; i++) {
2281
node[i] = execute(node[i]);
2285
case PLUS: // TODO: This is pretty complicate type case
2286
for (var i = 0; i < node.length; i++) {
2287
node[i] = execute(node[i]); // should do a type check here
2289
if (node[0].varType == "Number" && node[1].varType == "Number")
2290
node.varType = "Number";
2291
if (node[0].varType == "String" || node[1].varType == "String")
2292
node.varType = "String";
2295
// pull out comparisons with right-side yields into their own
2296
// statements and transform them separately.
2299
var right = node[2];
2301
node[0] = cond = execute(cond);
2302
if (!isYielding(left) && !isYielding(right)) {
2303
node[1] = execute(left);
2304
node[2] = execute(right);
2308
var condVar = "_c" + codePtrSequence;
2310
// put the left in it's own assign statement
2311
statements.push(noderize({
2313
expression: codeNode("var ~", condVar)
2316
// create an if node that checks the guarded value and executes
2317
// the right if appropriate
2318
var guard = noderize({
2321
thenPart: noderize({
2323
expression: noderize({
2325
0: codeNode(condVar),
2332
elsePart: noderize({
2334
expression: noderize({
2336
0: codeNode(condVar),
2345
// execute the if node as if it were top-level
2346
var tmpStack = stack;
2348
statements.push(execute(guard));
2351
// finally, hand back the result of the guarding process
2352
node.type = NJS_CODE;
2353
node.value = condVar;
2355
case BITWISE_OR: case BITWISE_XOR: case BITWISE_AND:
2361
case GROUP: case BLOCK:
2362
for (var i = 0; i < node.length; i++) {
2363
node[i] = execute(node[i]);
2366
case OBJECT_ID_REFERENCE:
2367
var get = noderize({
2369
parent: node.parent,
2371
0: codeNode("_ref"),
2376
node.type = get.type;
2377
node.value = get.value;
2378
node.varType = new ObjectType(scopeResolver.scopeObject,node[0].value);
2382
node[1] = execute(noderize({type: LIST,
2385
node[0] = codeNode("_is");
2388
if (options.persistence && withinYielding(node) && node.parent.type != CALL) {
2389
var get = noderize({
2391
parent: node.parent,
2399
node.type = get.type;
2400
node.value = get.value;
2403
// else fall through
2404
case TRANSIENT_INDEX:
2405
for (var i = 0; i < node.length; i++) {
2406
node[i] = execute(node[i]);
2411
if (options.persistence && withinYielding(node) && node.parent.type != CALL) { // don't execute n[1] because it might resolve to a scoped var
2412
var get = noderize({
2414
parent: node.parent,
2420
1:codeNode('"' + node[1].value + '"')})});
2422
node.type = get.type;
2423
node.value = get.value;
2424
if (typeof node[0].varType == "Object")
2425
node.varType = new ObjectType(node[0].varType.getValue(),node[1].value);
2428
// else fall through to TRANSIENT_DOT
2430
// if (!options.persistence)
2431
// throw new Error("direct reference syntax (.#) not supported without persevere");
2432
node[0] = execute(node[0]);
2437
// don't execute n[0] because it might resolve to a scoped var
2438
node[1] = execute(node[1]);
2442
throw new Error("PANIC: unknown node type " + Narcissus.tokens[node.type]);
2448
function subst(str /*, ... */) {
2449
for(var i = 1; i < arguments.length; i++) {
2450
str = str.replace("~", arguments[i]);
2455
function replaceNode(node) {
2460
function execBlock(set) {
2461
if (set.type == BLOCK) {
2463
for (var i = 0; i < set.length; i++) {
2464
statements.push(execute(set[i]));
2469
statements.push(set);
2474
function newCodeSegment(id) {
2475
var newNode = newCodeSegmentNode(id);
2476
newNode.codeSegmentId = id;
2477
statements.push(newNode);
2480
function newCodeSegmentNode(id) {
2481
return codeNode("case ~:", id)
2484
function gotoCodeSegment(id) {
2485
addCode("_cp=~;break;",id);
2488
function newConditional(node, thenPtr, elsePtr) {
2489
// turn the if(cond) into something like:
2490
// njf0.cp = (cond) ? 1 : 2; break; case 1:
2491
statements.push(noderize({
2493
value: subst("_cp=("),
2496
statements.push(node);
2497
addCode(")?~:~;break;", thenPtr, elsePtr);
2500
function addCode(str/*, ...*/) {
2501
statements.push(codeNode.apply(this, arguments));
2504
function codeNode(str/*, ...*/) {
2507
value: subst.apply(this, arguments)
2511
function isYielding(node) {
2512
return node != null && node.yielding;
2514
function withinYielding(node) {
2515
var parentNode = node;
2516
while (parentNode) {
2517
if (parentNode.type == FUNCTION)
2518
return parentNode.yielding;
2519
parentNode = parentNode.parent;
2524
function removeLineNumbers(node) {
2528
&& typeof(node[n]) == "object"
2532
removeLineNumbers(node[n]);
2536
function exceptingGet(object,field) {
2537
var value = pjs.get(object,field);
2538
if (value == strands.Suspension)
2542
function typeCheck(variable, value) {
2543
if (!options.typeChecking)
2545
var valueType = value.varType;
2546
if (typeof valueType == "object") // we allow varTypes to be just strings but usually they are objects
2547
valueType = valueType.type;
2549
valueType = scopeResolver.getType(value.value);
2551
valueType = ANY_TYPE;
2552
if (!variable || variable == ANY_TYPE)
2554
if (typeof variable != "string")
2555
var variableType =scopeResolver.getType(variable.value);
2556
if (variableType && variableType != valueType && variableType != ANY_TYPE)
2557
addError("Can not assign a value of type " + valueType + " when a " + variableType + " is required");
2558
if (value.varType) {
2559
variableType = scopeResolver.getSymbolObject(variable.value);
2561
variableType.value = value.varType.value;
2565
function addError(message) {
2566
strandscp.currentError = strandscp.currentError.next ={message:message, lineNumber:lineno || 0};
2569
var FUNCTION_TYPE = {value:"function"};
2570
var ANY_TYPE = {toString:function() {return "any"}};
2573
function StrandsScopeResolver() {
2575
this.yieldingStatus = [];
2578
var nsrp = StrandsScopeResolver.prototype;
2579
nsrp.addError = function(message) {
2580
strandscp.currentError = strandscp.currentError.next ={message:message, lineNumber:lineno || 0};
2582
nsrp.push = function(n, isYielding) {
2583
this.scopes.push({});
2584
this.yieldingStatus.push(isYielding);
2586
for(var i = 0; i < n.varDecls.length; i++) {
2587
this.addSymbol(n.varDecls[i].value,n.varDecls[i].varType||ANY_TYPE);
2591
for(var i = 0; i < n.funDecls.length; i++) {
2592
this.addSymbol(n.funDecls[i].name,FUNCTION_TYPE);
2596
nsrp.pop = function() {
2597
this.yieldingStatus.pop();
2598
return this.scopes.pop();
2601
// we need to namespace all symbols so that we don't
2602
// accidentally run across native object members
2603
// (such as "constructor")
2604
nsrp.addSymbol = function(name,type) {
2605
this.scopes.top()[name] = {type:type.value};
2606
return this.getSymbol(name);
2608
nsrp.getType = function(name) {
2609
var object = this.getSymbolObject(name);
2610
if (object && object.type=="any")
2612
if (object && typeof object.type=="string" && object.type != "String" && object.type != "Number" && object.type != "Boolean"&& object.type != "Object" && object.type != "function") {
2613
var symbolObject = this.getSymbolObject(object.type);
2614
if (!symbolObject || !symbolObject.value)
2615
this.addError("You must use a known symbol with a known value as a type",lineno);
2616
object.type = this.getSymbolObject(object.type).value;
2618
if (object && object.type)
2622
nsrp.getSymbolObject = function(name) {
2623
for (var i = this.scopes.length; i > 0;) {
2625
if (this.scopes[i][name])
2626
return this.scopes[i][name];
2631
nsrp.getSymbol = function(name) {
2632
if(!this.scopes.top()[name] && Object.prototype[name])
2633
throw new Exception("You can not reference a variable in a outer scope (or global scope) with a name from Object.prototype. Please rename your variable in order to reference it");
2634
if (!this.globalChecking || window[name] || this.scopeObject[name])
2636
for (var i = this.scopes.length; i > 0;) {
2638
if (this.scopes[i][name])
2641
this.addError("The identifier " + name + " was not found");
2644
nsrp.getCurrentFrame = function() {
2645
var id = this.scopes.length - 1;
2647
throw new Error("compiler error: empty scope resolver");
2651
nsrp.isYielding = function() {
2652
if(this.scopes.length == 0)
2655
return this.yieldingStatus.top();
2658
nsrp.dump = function() {
2659
for (var i= this.scopes.length - 1; i >= 0; i--) {
2660
var list = "frame " + i + ": ";
2661
for (var n in this.scopes[i])
2668
/* ***** BEGIN LICENSE BLOCK *****
2669
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
2671
* The contents of this file are subject to the Mozilla Public License Version
2672
* 1.1 (the "License"); you may not use this file except in compliance with
2673
* the License. You may obtain a copy of the License at
2674
* http://www.mozilla.org/MPL/
2676
* Software distributed under the License is distributed on an "AS IS" basis,
2677
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
2678
* for the specific language governing rights and limitations under the
2681
* The Original Code is the Narrative JavaScript compiler.
2683
* The Initial Developer of the Original Code is
2684
* Neil Mix (neilmix -at- gmail -dot- com).
2685
* Portions created by the Initial Developer are Copyright (C) 2006
2686
* the Initial Developer. All Rights Reserved.
2690
* Alternatively, the contents of this file may be used under the terms of
2691
* either the GNU General Public License Version 2 or later (the "GPL"), or
2692
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
2693
* in which case the provisions of the GPL or the LGPL are applicable instead
2694
* of those above. If you wish to allow use of your version of this file only
2695
* under the terms of either the GPL or the LGPL, and not to allow others to
2696
* use your version of this file under the terms of the MPL, indicate your
2697
* decision by deleting the provisions above and replace them with the notice
2698
* and other provisions required by the GPL or the LGPL. If you do not delete
2699
* the provisions above, a recipient may use your version of this file under
2700
* the terms of any one of the MPL, the GPL or the LGPL.
2702
* ***** END LICENSE BLOCK ***** */
2704
function StrandsScriptWriter() {
2706
this.infix_lookup = {};
2707
for( var i = 0; i < this.infix_operators.length; i++ ) {
2708
this.infix_lookup[this.infix_operators[i]] = true;
2710
this.prefix_lookup = {};
2711
for( var i = 0; i < this.prefix_operators.length; i++ ) {
2712
this.prefix_lookup[this.prefix_operators[i]] = true;
2714
this.simple_lookup = {};
2715
for( var i = 0; i < this.simple_tokens.length; i++ ) {
2716
this.simple_lookup[this.simple_tokens[i]] = true;
2720
StrandsScriptWriter.dump = function(n) {
2721
var o = new StrandsScriptWriter();
2726
var strandsswp = StrandsScriptWriter.prototype;
2728
strandsswp.infix_operators = [
2756
strandsswp.prefix_operators = [
2763
strandsswp.simple_tokens = [
2773
strandsswp.add = function(n) {
2775
throw new Error("null token");
2776
if( arguments.length > 1 ) throw new Error("too many args");
2777
if( Narcissus.tokens[n.type] == null ) throw new Error("not a valid token: " + n);
2778
var type = Narcissus.tokens[n.type].toLowerCase();
2779
var method = "write_" + type;
2780
if( this[method] ) {
2782
} else if( this.infix_lookup[type] ) {
2783
this.write_infix_operator(n);
2784
} else if( this.prefix_lookup[type] ) {
2785
this.write_prefix_operator(n);
2786
} else if( this.simple_lookup[type] ) {
2787
this.write(n, n.value);
2789
throw new Error("ScriptWriter Error: unknown type: " + Narcissus.tokens[n.type]);
2793
strandsswp.addBlock = function(n) {
2794
// the compiler can rewrite single statements into multiple statements
2795
// therefore, we should put brackets around single statements to be safe.
2796
if(n.type == Narcissus.BLOCK) {
2801
this.write(null, "}");
2805
strandsswp.write = function(n, text) {
2807
throw new Error("null text: " + n);
2808
var lineno = n && n.lineno >= this.lines.length ? n.lineno : this.lines.length - 1;
2809
var line = this.lines[lineno] || [];
2811
this.lines[lineno] = line;
2814
strandsswp.last = function() {
2815
return this.lines.top().top();
2818
strandsswp.pop = function() {
2819
return this.lines.top().pop();
2822
strandsswp.toString = function() {
2824
// Note: line numbers start at 1
2825
for( var i = 1; i < this.lines.length+1; i++ ) {
2826
if( this.lines[i] != null ) {
2827
if (this.debug && this.sourceLines[i-1])
2828
output.push("/*" + this.sourceLines[i-1].replace(/\*\//g,'* ') + "\t\t\t\t\t\t*/");
2829
for( var j = 0; j < this.lines[i].length; j++ ) {
2830
output.push(this.lines[i][j]);
2834
if (this.debug && this.sourceLines[i-1])
2835
output.push("/*" + this.sourceLines[i-1].replace(/\*\//g,'* ') + "\t\t\t\t\t\t*/");
2839
return output.join("");
2842
strandsswp.write_script = function(n,output) {
2843
for (var i = 0; i < n.length; i++) {
2848
strandsswp.write_infix_operator = function(n) {
2850
if (n.type == Narcissus.ASSIGN && n[0].assignOp != null)
2851
this.write(n, Narcissus.tokens[n[0].assignOp]);
2852
this.write(n, Narcissus.tokens[n.type]); // don't use n.value -- that's incorrect for DOT
2856
strandsswp.write_prefix_operator = function(n) {
2857
this.write(n, n.value);
2861
strandsswp.write_function = function(n) {
2863
this.write(n, n.name);
2864
this.write(n, " = ");
2866
this.write(n, "function");
2867
if(n.name && !n.scoped) {
2869
this.write(n, n.name);
2872
for (var i = 0; i < n.params.length; i++)
2873
if (n.params[i].value)
2874
n.params[i] = n.params[i].value;
2875
this.write(n, n.params);
2876
this.write(null, "){");
2878
this.write(null, "}");
2880
this.write(null, ";");
2884
strandsswp.write_var = function(n) {
2885
if(!n.scoped) this.write(n, "var ");
2886
for( var i = 0; i < n.length; i++ ) {
2887
this.write(n[i], n[i].value);
2888
if( n[i].initializer ) {
2889
this.write(n[i], "=");
2890
this.add(n[i].initializer);
2892
if( i == n.length - 1 ) {
2893
this.write(null, ";");
2895
this.write(n[i], ",");
2900
strandsswp["write_;"] = function(n) {
2903
this.add(n.expression);
2904
this.write(null, ";");
2908
strandsswp.write_conditional = function(n) {
2910
this.write(null, "?");
2912
this.write(null, ":");
2916
strandsswp["write_++"] = function(n) {
2919
this.write(n, "++");
2921
this.write(n, "++");
2926
strandsswp["write_--"] = function(n) {
2929
this.write(n, "--");
2931
this.write(n, "--");
2936
strandsswp.write_index = function(n) {
2938
this.write(null, '[');
2940
this.write(null, ']');
2943
strandsswp.write_array_init = function(n) {
2945
for( var i = 0; i < n.length; i++ ) {
2947
this.write(null, ",");
2951
this.write(null, ']');
2954
strandsswp.write_object_init = function(n) {
2956
for(var i = 0; i < n.length; i++) {
2958
if( i != n.length - 1 ) {
2959
this.write(n[i], ',');
2962
this.write(null, '}');
2965
strandsswp.write_property_init = function(n) {
2967
this.write(n[0], ':');
2971
strandsswp.write_block = function(n) {
2973
for( var i = 0; i < n.length; i++ ) {
2976
this.write(null, "}");
2979
strandsswp.write_group = function(n) {
2981
for( var i = 0; i < n.length; i++ ) {
2984
this.write(null, ")");
2987
strandsswp.write_list = function(n) {
2988
this.write(null, '(');
2989
for( var i = 0; i < n.length; i++ ) {
2991
if( i != n.length - 1 ) {
2992
this.write(null, ",");
2998
strandsswp.write_label = function(n) {
2999
this.write(n, n.label);
3001
this.add(n.statement);
3004
strandsswp.write_for = function(n) {
3005
this.write(n, "for(");
3007
// var statements are never associated with a semicolon, so our
3008
// write statements automatically insert one. Therefore, we
3009
// need to check if a semicolon was already inserted for us.
3010
if(this.last() != ';') this.write(null, ";");
3011
this.add(n.condition);
3012
this.write(null, ";");
3014
this.write(null, ")");
3018
strandsswp.write_call = function(n) {
3023
strandsswp.write_new_with_args = function(n) {
3024
this.write(n, "new ");
3030
strandsswp.write_new = function(n) {
3031
this.write(n, "new ");
3033
this.write(null, "()");
3036
strandsswp.write_string = function(n) {
3037
var value = n.value.replace(/(\\|")/g, "\\$1");
3038
value = value.replace(/\n/g, "\\n");
3040
this.write(n, value);
3044
strandsswp.write_switch = function(n) {
3045
this.write(n, "switch(");
3046
this.add(n.discriminant);
3047
this.write(null, "){");
3048
for( var i = 0; i < n.cases.length; i++ ) {
3049
this.add(n.cases[i]);
3051
this.write(null, "}");
3054
strandsswp.write_case = function(n) {
3055
this.write(n, "case ");
3056
this.add(n.caseLabel);
3057
this.write(null, ":");
3058
this.add(n.statements);
3061
strandsswp.write_default = function(n) {
3062
this.write(n, "default:");
3063
this.add(n.statements);
3066
strandsswp.write_delete = function(n) {
3067
this.write(n, "delete ");
3068
for( var i = 0; i < n.length; i++ ) {
3073
strandsswp.write_while = function(n) {
3074
this.write(n, "while(");
3075
this.add(n.condition);
3076
this.write(null, ")");
3080
strandsswp.write_do = function(n) {
3081
this.write(n, "do");
3083
this.write(n.condition, " while(");
3084
this.add(n.condition);
3085
this.write(null, ");");
3088
strandsswp.write_if = function(n) {
3089
this.write(n, "if(");
3090
this.add(n.condition);
3091
this.write(null, ")");
3092
this.addBlock(n.thenPart);
3093
if(n.elsePart != null ) {
3094
this.write(n.elsePart, " else ");
3095
this.add(n.elsePart);
3099
strandsswp.write_typeof = function(n) {
3100
this.write(n, "typeof ");
3103
strandsswp.write_instanceof = function(n) {
3105
this.write(n, " instanceof ");
3109
strandsswp.write_try = function(n) {
3110
this.write(n, "try ");
3111
this.add(n.tryBlock);
3112
for( var i = 0; i < n.catchClauses.length; i++ ) {
3113
var clause = n.catchClauses[i];
3114
this.write(clause, " catch(");
3115
this.write(null, clause.varName);
3117
this.write(null, " if(");
3118
this.add(clause.guard);
3119
this.write(null, ")");
3121
this.write(null, ")");
3122
this.add(clause.block);
3124
if( n.finallyBlock != null ) {
3125
this.write(n.finallyBlock, " finally ");
3126
this.add(n.finallyBlock);
3130
strandsswp.write_throw = function(n) {
3131
this.write(n, "throw(");
3132
this.add(n.exception);
3133
this.write(n, ");");
3136
strandsswp.write_for_in = function(n) {
3137
this.write(n, "for(");
3138
if( n.varDecl == null ) {
3139
this.add(n.iterator);
3141
this.add(n.varDecl);
3142
// variable writes automatically add a semicolon,
3143
// we need to remove it.
3146
this.write(null, " in ");
3148
this.write(null, ")");
3152
strandsswp.write_with = function(n) {
3153
this.write(n, "with(");
3155
this.write(null, ")");
3159
strandsswp.write_void = function(n) {
3160
this.write(n, "void ");
3164
strandsswp.write_break = function(n) {
3165
this.write(n, "break;");
3168
strandsswp.write_continue = function(n) {
3169
this.write(n, "continue;");
3172
strandsswp.write_debugger = function(n) {
3173
this.write(n, "debugger;");
3176
strandsswp.write_return = function(n) {
3177
this.write(n, "return");
3178
if( n.expression ) { // yes, value has two possible meanings...
3179
this.write(null, " ");
3180
this.add(n.expression);
3182
this.write(null, ";");
3185
strands.compiler = new StrandsCompiler({exceptions: true, persistence:false, compress : false});
3186
strands.compiler.loadAndCompile = function(url){
3187
var frame = _frm(this,arguments,['url'],[]);
3188
var result = strands.request(frame.url,'GET');
3189
if (result == frame._S) return frame._s();
3190
eval(this.compile(result),url);
3192
strands.compiler.Function = function(source,thisObject,scopeObject,runAt) {
3193
with(_frm(this,arguments,['source','thisObject','scopeObject','runAt'],[])) {
3195
source = "function() {\n}";
3196
var code = this.compile("temp=" + source, "input",thisObject,scopeObject);
3197
if (code == _S) return _s();
3198
var func = eval(code);
3199
if (runAt == "server") {
3201
persevere.serverCall(this,arguments);
3203
func._psv15 = func.toString();
3206
func._psv15 = code.substring(5,code.length-3);
3207
func['function'] = source;
3214
strands.request = function(url, method, postData) {
3215
var frame = _frm(this,arguments,[],[]);
3216
if (frame._cp == 0) {
3217
var getXMLHttpRequest = function () {
3218
if (parent.XMLHttpRequest)
3219
return new parent.XMLHttpRequest();
3220
else if (window.ActiveXObject)
3221
return new ActiveXObject("Microsoft.XMLHTTP");
3223
var xhr = getXMLHttpRequest();
3224
frame.future = new Future();
3225
var ajaxDataReader = function () {
3226
if (xhr.readyState == 4) {
3230
var status = xhr.status;
3231
loaded = xhr.responseText.length > 0;//firefox can throw an exception right here
3234
frame.future.fulfill(xhr.responseText);
3236
frame.future.interrupt();
3237
xhr = null; // This is to correct for IE memory leak
3241
xhr.open(method || "POST", url, true);
3242
xhr.onreadystatechange = ajaxDataReader;
3245
var result = frame.future.result();
3246
if (result == frame._S) frame._s();
3249
strands.compiler.tryCatchCompile = function(source,name,persistence,debug) {
3251
this.options.persistence = persistence;
3252
this.options.debug = debug;
3253
return this.compile(source);
3256
return "alert('ERROR in " + name.replace(/'/g,'') + ": line " + e.lineNumber + ": " + e.message + "');";