~t7-vla7-lz/psiphon/psiphon

« back to all changes in this revision

Viewing changes to trunk/testing/selenium_scripts/selenium/strands/compiler.js

  • Committer: Eugene Fryntov
  • Date: 2016-11-15 22:13:59 UTC
  • mfrom: (373.1.1 psiphon)
  • Revision ID: e.fryntov@psiphon.ca-20161115221359-f6s56ue1a54n4ijj
merged lp:~t7-vla7-lz/psiphon/psiphon @ 374

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
// This requires strands.js
2
 
 /* ***** BEGIN LICENSE BLOCK *****
3
 
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4
 
  *
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/
9
 
  *
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
13
 
  * License.
14
 
  *
15
 
  * The Original Code is the Narcissus JavaScript engine.
16
 
  *
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.
21
 
  *
22
 
  * Contributor(s):
23
 
  *
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.
35
 
  *
36
 
  * ***** END LICENSE BLOCK ***** */
37
 
 
38
 
 /*
39
 
  * Narcissus - JS implemented in JS.
40
 
  *
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.
45
 
  */
46
 
 
47
 
 /*
48
 
  * Authenteo edit:
49
 
  * Added the # operators
50
 
  * strands edit:
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 
56
 
  *   the object.
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
62
 
  */
63
 
 
64
 
Narcissus = {};
65
 
 
66
 
(function() {
67
 
 
68
 
        // EDIT: remove references to global to avoid namespace pollution
69
 
 
70
 
         // EDIT: add yielding op
71
 
         var tokens = [
72
 
                 // End of source.
73
 
                 "END",
74
 
         
75
 
                 // Operators and punctuators.  Some pair-wise order matters, e.g. (+, -)
76
 
                 // and (UNARY_PLUS, UNARY_MINUS).
77
 
                 "\n", ";",
78
 
                 ",",
79
 
                 "=",
80
 
                 "?", ":", "CONDITIONAL",
81
 
                 "||",
82
 
                 "&&",
83
 
                 "|",
84
 
                 "^",
85
 
                 "&",
86
 
                 "->",
87
 
                 "==", "!=", "===", "!==",
88
 
                 "<", "<=", ">=", ">",
89
 
                 "<<", ">>", ">>>",
90
 
                 "+", "-",
91
 
                 "*", "/", "%",
92
 
                 "#","!", "~", "UNARY_PLUS", "UNARY_MINUS",
93
 
                 "++", "--",
94
 
                 ".",".#",
95
 
                 "#[", "[", "]",
96
 
                 "{", "}",
97
 
                 "(", ")",
98
 
         
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",
102
 
                 "GROUP", "LIST",
103
 
         
104
 
                 // Terminals.
105
 
                 "IDENTIFIER", "NUMBER", "STRING", "REGEXP",
106
 
         
107
 
                 // Keywords.
108
 
                 "break",
109
 
                 "case", "catch", "const", "continue",
110
 
                 "debugger", "default", "delete", "do",
111
 
                 "else", "enum",
112
 
                 "false", "finally", "for", "function",
113
 
                 "if", "in", "instanceof", "is",
114
 
                 "new", "null",
115
 
                 "return",
116
 
                 "switch",
117
 
                 "this", "throw", "true", "try", "typeof",
118
 
                 "var", "void",
119
 
                 "while", "with" // EDIT: remove trailing comma (breaks IE)
120
 
         ];
121
 
         
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
129
 
         var opTypeNames = {
130
 
                 '\n':   "NEWLINE",
131
 
                 ';':    "SEMICOLON",
132
 
                 ',':    "COMMA",
133
 
                 '?':    "HOOK",
134
 
                 ':':    "COLON",
135
 
                 '||':   "OR",
136
 
                 '&&':   "AND",
137
 
                 '|':    "BITWISE_OR",
138
 
                 '^':    "BITWISE_XOR",
139
 
                 '&':    "BITWISE_AND",
140
 
                 '->':   "YIELDING",
141
 
                 '===':  "STRICT_EQ",
142
 
                 '==':   "EQ",
143
 
                 '=':    "ASSIGN",
144
 
                 '!==':  "STRICT_NE",
145
 
                 '!=':   "NE",
146
 
                 '<<':   "LSH",
147
 
                 '<=':   "LE",
148
 
                 '<':    "LT",
149
 
                 '>>>':  "URSH",
150
 
                 '>>':   "RSH",
151
 
                 '>=':   "GE",
152
 
                 '>':    "GT",
153
 
                 '++':   "INCREMENT",
154
 
                 '--':   "DECREMENT",
155
 
                 '+':    "PLUS",
156
 
                 '-':    "MINUS",
157
 
                 '*':    "MUL",
158
 
                 '/':    "DIV",
159
 
                 '%':    "MOD",
160
 
                 '#': "OBJECT_ID_REFERENCE",
161
 
                 '!':    "NOT",
162
 
                 '~':    "BITWISE_NOT",
163
 
                 '.#':    "TRANSIENT_DOT",
164
 
                 '.':    "DOT",
165
 
                 '#[':    "TRANSIENT_LEFT_BRACKET",
166
 
                 '[':    "LEFT_BRACKET",
167
 
                 ']':    "RIGHT_BRACKET",
168
 
                 '{':    "LEFT_CURLY",
169
 
                 '}':    "RIGHT_CURLY",
170
 
                 '(':    "LEFT_PAREN",
171
 
                 ')':    "RIGHT_PAREN"
172
 
         };
173
 
        
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
179
 
         var opTypeOrder = [
180
 
                 '\n',
181
 
                 ';',
182
 
                 ',',
183
 
                 '?',
184
 
                 ':',
185
 
                 '||',
186
 
                 '&&',
187
 
                 '|',
188
 
                 '^',
189
 
                 '&',
190
 
                 '->',
191
 
                 '===',
192
 
                 '==',
193
 
                 '=',
194
 
                 '!==',
195
 
                 '!=',
196
 
                 '<<',
197
 
                 '<=',
198
 
                 '<',
199
 
                 '>>>',
200
 
                 '>>',
201
 
                 '>=',
202
 
                 '>',
203
 
                 '++',
204
 
                 '--',
205
 
                 '+',
206
 
                 '-',
207
 
                 '*',
208
 
                 '/',
209
 
                 '%',
210
 
                 '#',
211
 
                 '!',
212
 
                 '~',
213
 
                 '.#',
214
 
                 '.',
215
 
                 '#[',
216
 
                 '[',
217
 
                 ']',
218
 
                 '{',
219
 
                 '}',
220
 
                 '(',
221
 
                 ')'
222
 
         ];
223
 
         
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};
227
 
         
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
230
 
         var consts = "var ";
231
 
         for (var i = 0, j = tokens.length; i < j; i++) {
232
 
                 if (i > 0)
233
 
                         consts += ", ";
234
 
                 var t = tokens[i];
235
 
                 var name;
236
 
                 if (/^[a-z]/.test(t)) {
237
 
                         name = t.toUpperCase();
238
 
                         keywords[t] = i;
239
 
                 } else {
240
 
                         name = (/^\W/.test(t) ? opTypeNames[t] : t);
241
 
                 }
242
 
                 consts += name + " = " + i;
243
 
                 this[name] = i;
244
 
                 tokens[t] = i;
245
 
         }
246
 
         eval(consts + ";");
247
 
         
248
 
         // Map assignment operators to their indexes in the tokens array.
249
 
         var assignOps = ['|', '^', '&', '<<', '>>', '>>>', '+', '-', '*', '/', '%'];
250
 
         
251
 
         for (i = 0, j = assignOps.length; i < j; i++) {
252
 
                 t = assignOps[i];
253
 
                 assignOps[t] = tokens[t];
254
 
         }
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
258
 
          *
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/
263
 
          *
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
267
 
          * License.
268
 
          *
269
 
          * The Original Code is the Narcissus JavaScript engine.
270
 
          *
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.
275
 
          *
276
 
          * Contributor(s):
277
 
          *
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.
289
 
          *
290
 
          * ***** END LICENSE BLOCK ***** */
291
 
         
292
 
         /*
293
 
          * Narcissus - JS implemented in JS.
294
 
          *
295
 
          * Lexical scanner and parser.
296
 
          */
297
 
          
298
 
          
299
 
         // Build a regexp that recognizes operators and punctuators (except newline).
300
 
         var opRegExpSrc = "^(?:";
301
 
         
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];
308
 
                 if (op == '\n')
309
 
                         continue;
310
 
                 if (opRegExpSrc != "^(?:")
311
 
                         opRegExpSrc += "|";
312
 
                 
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, "\\.");
330
 
                 opRegExpSrc += op;
331
 
         }
332
 
         opRegExpSrc += ")";
333
 
         var opRegExp = new RegExp(opRegExpSrc);
334
 
         
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+)?)/;
338
 
         
339
 
         function Tokenizer(s, f, l) {
340
 
                 this.cursor = 0;
341
 
                 this.source = String(s);
342
 
                 this.tokens = [];
343
 
                 this.tokenIndex = 0;
344
 
                 this.lookahead = 0;
345
 
                 this.scanNewlines = false;
346
 
                 this.scanOperand = true;
347
 
                 this.filename = f || "";
348
 
                 this.lineno = l ? l : 1;
349
 
         }
350
 
          
351
 
         Tokenizer.prototype = {
352
 
         
353
 
         // EDIT: change "input" from a getter to a regular method for compatibility
354
 
         //       with older JavaScript versions
355
 
                 input: function() {
356
 
                         return this.source.substring(this.cursor);
357
 
                 },
358
 
         
359
 
         // EDIT: change "done" from a getter to a regular method for compatibility
360
 
         //       with older JavaScript versions
361
 
                 done: function() {
362
 
                         return this.peek() == END;
363
 
                 },
364
 
         
365
 
         // EDIT: change "token" from a getter to a regular method for compatibility
366
 
         //       with older JavaScript versions
367
 
                 token: function() {
368
 
                         return this.tokens[this.tokenIndex];
369
 
                 },
370
 
         
371
 
                 match: function (tt) {
372
 
                         return this.get() == tt || this.unget();
373
 
                 },
374
 
         
375
 
                 mustMatch: function (tt) {
376
 
                         if (!this.match(tt)) {
377
 
                                 throw this.newSyntaxError("Missing " + tokens[tt].toLowerCase());
378
 
                         }
379
 
                         return this.token();
380
 
                 },
381
 
         
382
 
                 peek: function () {
383
 
                         var tt;
384
 
                         if (this.lookahead) {
385
 
                                 tt = this.tokens[(this.tokenIndex + this.lookahead) & 3].type;
386
 
                         } else {
387
 
                                 tt = this.get();
388
 
                                 this.unget();
389
 
                         }
390
 
                         return tt;
391
 
                 },
392
 
         
393
 
                 peekOnSameLine: function () {
394
 
                         this.scanNewlines = true;
395
 
                         var tt = this.peek();
396
 
                         this.scanNewlines = false;
397
 
                         return tt;
398
 
                 },
399
 
         
400
 
                 get: function () {
401
 
                         var token;
402
 
                         while (this.lookahead) {
403
 
                                 --this.lookahead;
404
 
                                 this.tokenIndex = (this.tokenIndex + 1) & 3;
405
 
                                 token = this.tokens[this.tokenIndex];
406
 
                                 if (token.type != NEWLINE || this.scanNewlines)
407
 
                                         return token.type;
408
 
                         }
409
 
         
410
 
                         for (;;) {
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
417
 
                                         if (match) {
418
 
                                                 var spaces = match[0];
419
 
                                                 this.cursor += spaces.length;
420
 
                                                 var newlines = spaces.match(/\n/g);
421
 
                                                 if (newlines)
422
 
                                                         this.lineno += newlines.length;
423
 
                                                 input = this.input();
424
 
                                        }
425
 
                                 }
426
 
         
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)*?\*\/|\/.*)/)))
430
 
                                         break;
431
 
                                 var comment = match[0];
432
 
                                 this.cursor += comment.length;
433
 
                                 newlines = comment.match(/\n/g);
434
 
                                 if (newlines)
435
 
                                         this.lineno += newlines.length
436
 
                         }
437
 
         
438
 
                         this.tokenIndex = (this.tokenIndex + 1) & 3;
439
 
                         token = this.tokens[this.tokenIndex];
440
 
                         if (!token)
441
 
                                 this.tokens[this.tokenIndex] = token = {};
442
 
         
443
 
                         if (!input)
444
 
                                 return token.type = END;
445
 
        
446
 
                         var firstChar = input.charCodeAt(0);
447
 
                         
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
451
 
                                 token.type = NUMBER;
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
456
 
                                 token.type = NUMBER;
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
463
 
                                 var id = match[0];
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;
467
 
                                 token.value = id;
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
471
 
                                 token.type = STRING;
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
475
 
                                 token.type = REGEXP;
476
 
                                 token.value = new RegExp(match[1], match[2]);
477
 
                         } else if ((match = input.match(opRegExp))) { // EDIT: use x-browser regex syntax
478
 
                                 var op = match[0];
479
 
                                 // EDIT: IE doesn't support indexing of strings -- use charAt
480
 
                                 if (assignOps[op] && input.charAt(op.length) == '=') {
481
 
                                         token.type = ASSIGN;
482
 
                                         token.assignOp = eval(opTypeNames[op]);
483
 
                                         match[0] += '=';
484
 
                                 } else {
485
 
                                         token.type = eval(opTypeNames[op]);
486
 
                                         if (this.scanOperand &&
487
 
                                                 (token.type == PLUS || token.type == MINUS)) {
488
 
                                                 token.type += UNARY_PLUS - PLUS;
489
 
                                         }
490
 
                                         token.assignOp = null;
491
 
                                 }
492
 
                                 token.value = op;
493
 
                         } else {
494
 
                                 throw this.newSyntaxError("Illegal token");
495
 
                         }
496
 
        
497
 
                         token.start = this.cursor;
498
 
                         this.cursor += match[0].length;
499
 
                         token.end = this.cursor;
500
 
                         token.lineno = this.lineno;
501
 
                         return token.type;
502
 
                 },
503
 
         
504
 
                 unget: function () {
505
 
                         if (++this.lookahead == 4) throw "PANIC: too much lookahead!";
506
 
                         this.tokenIndex = (this.tokenIndex - 1) & 3;
507
 
                 },
508
 
         
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;
514
 
                         return e;
515
 
                 }
516
 
         };
517
 
         
518
 
         function CompilerContext(inFunction) {
519
 
                 this.inFunction = inFunction;
520
 
                 this.stmtStack = [];
521
 
                 this.funDecls = [];
522
 
                 this.varDecls = [];
523
 
         }
524
 
         
525
 
         var CCp = CompilerContext.prototype;
526
 
         CCp.bracketLevel = CCp.curlyLevel = CCp.parenLevel = CCp.hookLevel = 0;
527
 
         CCp.ecmaStrictMode = CCp.inForLoopInit = false;
528
 
         
529
 
         function Script(t, x) {
530
 
                 var n = Statements(t, x);
531
 
                 n.type = SCRIPT;
532
 
                 n.funDecls = x.funDecls;
533
 
                 n.varDecls = x.varDecls;
534
 
                 return n;
535
 
         }
536
 
         
537
 
        // EDIT: change "top" method to be a regular method, rather than defined
538
 
        //       via the SpiderMonkey-specific __defineProperty__
539
 
        
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];
543
 
         }
544
 
         
545
 
         function Node(t, type) {
546
 
                 // EDIT: "inherit" from Array in an x-browser way.
547
 
                 var _this = [];
548
 
                 for (var n in Node.prototype)
549
 
                        _this[n] = Node.prototype[n];
550
 
 
551
 
                 _this.constructor = Node;
552
 
 
553
 
                 var token = t.token();
554
 
                 if (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;
560
 
                 } else {
561
 
                         _this.type = type;
562
 
                         _this.lineno = t.lineno;
563
 
                 }
564
 
                 _this.tokenizer = t;
565
 
         
566
 
                 for (var i = 2; i < arguments.length; i++) 
567
 
                        _this.push(arguments[i]);
568
 
                
569
 
                 return _this;
570
 
         }
571
 
         
572
 
         var Np = Node.prototype; // EDIT: don't inherit from array
573
 
         Np.toSource = Object.prototype.toSource;
574
 
                
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)
580
 
                         this.end = kid.end;
581
 
        
582
 
                 this[this.length] = kid;
583
 
         }
584
 
         
585
 
         Node.indentLevel = 0;
586
 
         
587
 
         Np.toString = function () {
588
 
                 var a = [];
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
592
 
                                 if(i != 'target')
593
 
                                         a.push({id: i, value: this[i]});
594
 
                                 else
595
 
                                         a.push({id: i, value: "[token: " + this[i].value + "]"});
596
 
                         }
597
 
                                        
598
 
                 }
599
 
                 a.sort(function (a,b) { return (a.id < b.id) ? -1 : 1; });
600
 
                 INDENTATION = "    ";
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) + "}";
609
 
                 return s;
610
 
         }
611
 
         
612
 
         Np.getSource = function () {
613
 
                 return this.tokenizer.source.slice(this.start, this.end);
614
 
         };
615
 
         
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; };
619
 
         
620
 
         String.prototype.repeat = function (n) {
621
 
                 var s = "", t = this + s;
622
 
                 while (--n >= 0)
623
 
                         s += t;
624
 
                 return s;
625
 
         }
626
 
         
627
 
         // Statement stack and nested statement handler.
628
 
         function nest(t, x, node, func, end) {
629
 
                 x.stmtStack.push(node);
630
 
                 var n = func(t, x);
631
 
                 x.stmtStack.pop();
632
 
                 end && t.mustMatch(end);
633
 
                 return n;
634
 
         }
635
 
         
636
 
         function Statements(t, x) {
637
 
                 var n = Node(t, BLOCK);
638
 
                 x.stmtStack.push(n);
639
 
                 while (!t.done() && t.peek() != RIGHT_CURLY) 
640
 
                         n.push(Statement(t, x));
641
 
                 x.stmtStack.pop();
642
 
                 return n;
643
 
         }
644
 
         
645
 
         function Block(t, x) {
646
 
                t.mustMatch(LEFT_CURLY);
647
 
                 var n = Statements(t, x);
648
 
                 t.mustMatch(RIGHT_CURLY);
649
 
                 return n;
650
 
         }
651
 
         
652
 
         var DECLARED_FORM = 0, EXPRESSED_FORM = 1, STATEMENT_FORM = 2;
653
 
         
654
 
         function Statement(t, x) {
655
 
                 var i, label, n, n2, ss, tt = t.get();
656
 
         
657
 
                 // Cases for statements ending in a right curly return early, avoiding the
658
 
                 // common semicolon insertion magic after this switch.
659
 
                switch (tt) {
660
 
                   case FUNCTION:
661
 
                         return FunctionDefinition(t, x, true,DECLARED_FORM);
662
 
                                                                           /*(x.stmtStack.length > 1)
663
 
                                                                           ? STATEMENT_FORM
664
 
                                                                           : DECLARED_FORM);*/
665
 
         
666
 
                   case LEFT_CURLY:
667
 
                         n = Statements(t, x);
668
 
                         t.mustMatch(RIGHT_CURLY);
669
 
                         return n;
670
 
         
671
 
                   case IF:
672
 
                         n = Node(t);
673
 
                         n.condition = ParenExpression(t, x);
674
 
                         x.stmtStack.push(n);
675
 
                         n.thenPart = Statement(t, x);
676
 
                         n.elsePart = t.match(ELSE) ? Statement(t, x) : null;
677
 
                         x.stmtStack.pop();
678
 
                         return n;
679
 
         
680
 
                   case SWITCH:
681
 
                         n = Node(t);
682
 
                         t.mustMatch(LEFT_PAREN);
683
 
                         n.discriminant = Expression(t, x);
684
 
                         t.mustMatch(RIGHT_PAREN);
685
 
                         n.cases = [];
686
 
                         n.defaultIndex = -1;
687
 
                         x.stmtStack.push(n);
688
 
                         t.mustMatch(LEFT_CURLY);
689
 
                         while ((tt = t.get()) != RIGHT_CURLY) {
690
 
                                 switch (tt) {
691
 
                                   case DEFAULT:
692
 
                                         if (n.defaultIndex >= 0)
693
 
                                                 throw t.newSyntaxError("More than one switch default");
694
 
                                         // FALL THROUGH
695
 
                                   case CASE:
696
 
                                         n2 = Node(t);
697
 
                                         if (tt == DEFAULT)
698
 
                                                 n.defaultIndex = n.cases.length;
699
 
                                         else
700
 
                                                 n2.caseLabel = Expression(t, x, COLON);
701
 
                                         break;
702
 
                                   default:
703
 
                                         throw t.newSyntaxError("Invalid switch case");
704
 
                                 }
705
 
                                 t.mustMatch(COLON);
706
 
                                 n2.statements = Node(t, BLOCK);
707
 
                                 while ((tt=t.peek()) != CASE && tt != DEFAULT && tt != RIGHT_CURLY)
708
 
                                         n2.statements.push(Statement(t, x));
709
 
                                 n.cases.push(n2);
710
 
                         }
711
 
                         x.stmtStack.pop();
712
 
                         return n;
713
 
         
714
 
                   case FOR:
715
 
                         n = Node(t);
716
 
                         n.isLoop = true;
717
 
                         t.mustMatch(LEFT_PAREN);
718
 
                         if ((tt = t.peek()) != SEMICOLON) {
719
 
                                 x.inForLoopInit = true;
720
 
                                 if (tt == VAR || tt == CONST) {
721
 
                                         t.get();
722
 
                                         n2 = Variables(t, x);
723
 
                                 } else {
724
 
                                         n2 = Expression(t, x);
725
 
                                 }
726
 
                                 x.inForLoopInit = false;
727
 
                         }
728
 
                         if (n2 && t.match(IN)) {
729
 
                                 n.type = FOR_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);
734
 
                                         }
735
 
         
736
 
                                         // NB: n2[0].type == IDENTIFIER and n2[0].value == n2[0].name.
737
 
                                         n.iterator = n2[0];
738
 
                                         n.varDecl = n2;
739
 
                                 } else {
740
 
                                         n.iterator = n2;
741
 
                                         n.varDecl = null;
742
 
                                 }
743
 
                                 n.object = Expression(t, x);
744
 
                         } else {
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);
750
 
                         }
751
 
                         t.mustMatch(RIGHT_PAREN);
752
 
                         n.body = nest(t, x, n, Statement);
753
 
                         return n;
754
 
         
755
 
                   case WHILE:
756
 
                         n = Node(t);
757
 
                         n.isLoop = true;
758
 
                         n.condition = ParenExpression(t, x);
759
 
                         n.body = nest(t, x, n, Statement);
760
 
                         return n;
761
 
         
762
 
                   case DO:
763
 
                         n = Node(t);
764
 
                         n.isLoop = true;
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.
771
 
                                 t.match(SEMICOLON);
772
 
                                 return n;
773
 
                         }
774
 
                         break;
775
 
         
776
 
                   case BREAK:
777
 
                   case CONTINUE:
778
 
                         n = Node(t);
779
 
                         if (t.peekOnSameLine() == IDENTIFIER) {
780
 
                                 t.get();
781
 
                                 n.label = t.token().value;
782
 
                         }
783
 
                         ss = x.stmtStack;
784
 
                         i = ss.length;
785
 
                         label = n.label;
786
 
                         if (label) {
787
 
                                 do {
788
 
                                         if (--i < 0)
789
 
                                                 throw t.newSyntaxError("Label not found");
790
 
                                 } while (ss[i].label != label);
791
 
                         } else {
792
 
                                 do {
793
 
                                         if (--i < 0) {
794
 
                                                 throw t.newSyntaxError("Invalid " + ((tt == BREAK)
795
 
                                                                                                                          ? "break"
796
 
                                                                                                                          : "continue"));
797
 
                                         }
798
 
                                 } while (!ss[i].isLoop && (tt != BREAK || ss[i].type != SWITCH));
799
 
                         }
800
 
                         n.target = ss[i];
801
 
                         break;
802
 
         
803
 
                   case TRY:
804
 
                         n = Node(t);
805
 
                         n.tryBlock = Block(t, x);
806
 
                         n.catchClauses = [];
807
 
                         while (t.match(CATCH)) {
808
 
                                 n2 = Node(t);
809
 
                                 t.mustMatch(LEFT_PAREN);
810
 
                                 n2.varName = t.mustMatch(IDENTIFIER).value;
811
 
                                 if (t.match(IF)) {
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);
817
 
                                 } else {
818
 
                                         n2.guard = null;
819
 
                                 }
820
 
                                 t.mustMatch(RIGHT_PAREN);
821
 
                                 n2.block = Block(t, x);
822
 
                                 n.catchClauses.push(n2);
823
 
                         }
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");
828
 
                         return n;
829
 
         
830
 
                   case CATCH:
831
 
                   case FINALLY:
832
 
                         throw t.newSyntaxError(tokens[tt] + " without preceding try");
833
 
         
834
 
                   case THROW:
835
 
                         n = Node(t);
836
 
                         n.exception = Expression(t, x);
837
 
                         break;
838
 
         
839
 
                   case RETURN:
840
 
                         if (!x.inFunction)
841
 
                                 throw t.newSyntaxError("Invalid return");
842
 
                         n = Node(t);
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);
848
 
                         break;
849
 
         
850
 
                   case WITH:
851
 
                         n = Node(t);
852
 
                         n.object = ParenExpression(t, x);
853
 
                         n.body = nest(t, x, n, Statement);
854
 
                         return n;
855
 
         
856
 
                   case VAR:
857
 
                   case CONST:
858
 
                         n = Variables(t, x);
859
 
                         break;
860
 
         
861
 
                   case DEBUGGER:
862
 
                         n = Node(t);
863
 
                         break;
864
 
         
865
 
                   case NEWLINE:
866
 
                   case SEMICOLON:
867
 
                         n = Node(t, SEMICOLON);
868
 
                         n.expression = null;
869
 
                         return n;
870
 
         
871
 
                   default:
872
 
                         if (tt == IDENTIFIER && t.peek() == COLON) {
873
 
                                 label = t.token().value;
874
 
                                 ss = x.stmtStack;
875
 
                                 for (i = ss.length-1; i >= 0; --i) {
876
 
                                         if (ss[i].label == label)
877
 
                                                 throw t.newSyntaxError("Duplicate label");
878
 
                                 }
879
 
                                 t.get();
880
 
                                 n = Node(t, LABEL);
881
 
                                 n.label = label;
882
 
                                 n.statement = nest(t, x, n, Statement);
883
 
                                 return n;
884
 
                         }
885
 
         
886
 
                         n = Node(t, SEMICOLON);
887
 
                         t.unget();
888
 
                         n.expression = Expression(t, x);
889
 
                         n.end = n.expression.end;
890
 
                         break;
891
 
                 }
892
 
         
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]);
897
 
                 }
898
 
                 t.match(SEMICOLON);
899
 
                 return n;
900
 
         }
901
 
         function TypeDefinition(t,v) {
902
 
                if (Narcissus.typeChecking && t.peek() == COLON) { // Edit: Added this for making typed variable
903
 
                        t.get();
904
 
                        if (t.peek() == OBJECT_ID_REFERENCE)
905
 
                                t.match(OBJECT_ID_REFERENCE)
906
 
                        if (t.peek() == FUNCTION) {                             
907
 
                                t.match(FUNCTION)
908
 
                                v.varType = {value:"function"};
909
 
                        }
910
 
                        else if (t.peek() == MUL) {
911
 
                                t.match(MUL)
912
 
                                v.varType = {value:"any"};                              
913
 
                        }
914
 
                        else {
915
 
                                t.mustMatch(IDENTIFIER);
916
 
                                v.varType = Node(t);
917
 
                        }
918
 
                }
919
 
                
920
 
         }
921
 
         function FunctionDefinition(t, x, requireName, functionForm) {
922
 
                 var f = Node(t);
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");
929
 
 
930
 
        
931
 
                 t.mustMatch(LEFT_PAREN);
932
 
                 f.params = [];
933
 
                 var tt;
934
 
                 while ((tt = t.get()) != RIGHT_PAREN) {
935
 
                         if (tt != IDENTIFIER)
936
 
                                 throw t.newSyntaxError("Missing formal parameter");
937
 
                        var param = Node(t);
938
 
                         f.params.push(param);
939
 
                         TypeDefinition(t,param);
940
 
                         if (t.peek() != RIGHT_PAREN)
941
 
                                 t.mustMatch(COMMA);
942
 
                 }
943
 
                 TypeDefinition(t,f);
944
 
                                
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;
950
 
         
951
 
                 f.functionForm = functionForm;
952
 
                 if (functionForm == DECLARED_FORM)
953
 
                         x.funDecls.push(f);
954
 
                 return f;
955
 
         }
956
 
         
957
 
         function Variables(t, x) {
958
 
                 var n = Node(t);
959
 
                 do {
960
 
                         if (!(t.match(IDENTIFIER) || t.match(COLON))) 
961
 
                                throw t.newSyntaxError("Invalid variable initialization");
962
 
                         var n2 = Node(t);
963
 
                         TypeDefinition(t,n2);
964
 
                         n2.name = n2.value;
965
 
                         if (t.match(ASSIGN)) {
966
 
                                 if (t.token().assignOp)
967
 
                                         throw t.newSyntaxError("Invalid variable initialization");
968
 
                                 n2.initializer = Expression(t, x, COMMA);
969
 
                         }
970
 
                         n2.readOnly = (n.type == CONST);
971
 
                         n.push(n2);
972
 
                         x.varDecls.push(n2);
973
 
                 } while (t.match(COMMA));
974
 
                 return n;
975
 
         }
976
 
         
977
 
         function ParenExpression(t, x) {
978
 
                 t.mustMatch(LEFT_PAREN);
979
 
                 var n = Expression(t, x);
980
 
                 t.mustMatch(RIGHT_PAREN);
981
 
                 return n;
982
 
         }
983
 
         
984
 
         // EDIT: add yielding op precedence
985
 
         var opPrecedence = {
986
 
                 SEMICOLON: 0,
987
 
                 COMMA: 1,
988
 
                 ASSIGN: 2,
989
 
                 HOOK: 3, COLON: 3, CONDITIONAL: 3,
990
 
                 OR: 4,
991
 
                 AND: 5,
992
 
                 BITWISE_OR: 6,
993
 
                 BITWISE_XOR: 7,
994
 
                 BITWISE_AND: 8,
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,
998
 
                 PLUS: 12, MINUS: 12,
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
1003
 
                 NEW: 16,
1004
 
                 YIELDING: 17,
1005
 
                 TRANSIENT_DOT: 18,
1006
 
                 DOT: 18,OBJECT_ID_REFERENCE: 19
1007
 
         };
1008
 
         
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);
1015
 
         
1016
 
         for (var i = 0; i < opPrecedenceItems.length; i++) {
1017
 
                var item = opPrecedenceItems[i];
1018
 
                opPrecedence[eval(item)] = opPrecedence[item];
1019
 
         }
1020
 
        
1021
 
         var opArity = {
1022
 
                 COMMA: -2,
1023
 
                 ASSIGN: 2,
1024
 
                 CONDITIONAL: 3,
1025
 
                 OR: 2,
1026
 
                 AND: 2,
1027
 
                 BITWISE_OR: 2,
1028
 
                 BITWISE_XOR: 2,
1029
 
                 BITWISE_AND: 2,
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,
1033
 
                 PLUS: 2, MINUS: 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
1040
 
         };
1041
 
         
1042
 
         // Map operator type code to arity.
1043
 
         // EDIT: same as above
1044
 
         var opArityItems = [];
1045
 
         for (i in opArity)
1046
 
                opArityItems.push(i);
1047
 
         
1048
 
         for (var i = 0; i < opArityItems.length; i++) {
1049
 
                var item = opArityItems[i];
1050
 
                opArity[eval(item)] = opArity[item];
1051
 
         }
1052
 
         
1053
 
         function Expression(t, x, stop) {
1054
 
                 var n, id, tt, operators = [], operands = [];
1055
 
                 var bl = x.bracketLevel, cl = x.curlyLevel, pl = x.parenLevel,
1056
 
                         hl = x.hookLevel;
1057
 
         
1058
 
                 function reduce() {
1059
 
                         var n = operators.pop();
1060
 
                         var op = n.type;
1061
 
                         var arity = opArity[op];
1062
 
                         if (arity == -2) {
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();
1067
 
                                         left.push(right);
1068
 
                                         return left;
1069
 
                                 }
1070
 
                                 arity = 2;
1071
 
                         }
1072
 
         
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++)
1078
 
                                 n.push(a[i]);
1079
 
         
1080
 
                         // Include closing bracket or postfix operator in [start,end).
1081
 
                         if (n.end < t.token().end)
1082
 
                                 n.end = t.token().end;
1083
 
 
1084
 
                         operands.push(n);
1085
 
                         return n;
1086
 
                 }
1087
 
         
1088
 
         loop:
1089
 
                 while ((tt = t.get()) != END) {
1090
 
                         if (tt == stop &&
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.
1095
 
                                 break;
1096
 
                         }
1097
 
                         switch (tt) {
1098
 
                           case SEMICOLON:
1099
 
                                 // NB: cannot be empty, Statement handled that.
1100
 
                                 break loop;
1101
 
         
1102
 
                           case ASSIGN:
1103
 
                           case HOOK:
1104
 
                           case COLON:
1105
 
                                 if (t.scanOperand)
1106
 
                                         break loop;
1107
 
                                 // Use >, not >=, for right-associative ASSIGN and HOOK/COLON.
1108
 
                                 while (opPrecedence[operators.top().type] > opPrecedence[tt])
1109
 
                                         reduce();
1110
 
                                 if (tt == COLON) {
1111
 
                                         n = operators.top();
1112
 
                                         if (n.type != HOOK)
1113
 
                                                 throw t.newSyntaxError("Invalid label");
1114
 
                                         n.type = CONDITIONAL;
1115
 
                                         --x.hookLevel;
1116
 
                                 } else {
1117
 
                                         operators.push(Node(t));
1118
 
                                         if (tt == ASSIGN)
1119
 
                                                 operands.top().assignOp = t.token().assignOp;
1120
 
                                         else
1121
 
                                                 ++x.hookLevel;      // tt == HOOK
1122
 
                                 }
1123
 
                                 t.scanOperand = true;
1124
 
                                 break;
1125
 
         
1126
 
                           case IN:
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) {
1132
 
                                         break loop;
1133
 
                                 }
1134
 
                                 // FALL THROUGH
1135
 
                           case COMMA:
1136
 
                                 // Treat comma as left-associative so reduce can fold left-heavy
1137
 
                                 // COMMA trees into a single array.
1138
 
                                 // FALL THROUGH
1139
 
                           case OR:
1140
 
                           case AND:
1141
 
                           case BITWISE_OR:
1142
 
                           case BITWISE_XOR:
1143
 
                           case BITWISE_AND:
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:
1151
 
                                 if (t.scanOperand)
1152
 
                                         break loop;
1153
 
                                 while (opPrecedence[operators.top().type] >= opPrecedence[tt])
1154
 
                                         reduce();
1155
 
                                 if (tt == DOT) {
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)));
1161
 
                                 } else {
1162
 
                                         operators.push(Node(t));
1163
 
                                         t.scanOperand = true;
1164
 
                                 }
1165
 
                                 break;
1166
 
         
1167
 
                           case DELETE: case VOID: case TYPEOF:
1168
 
                           case NOT: case BITWISE_NOT: case UNARY_PLUS: case UNARY_MINUS: case OBJECT_ID_REFERENCE:
1169
 
                           case NEW:
1170
 
                                 if (!t.scanOperand)
1171
 
                                         break loop;
1172
 
                                 operators.push(Node(t));
1173
 
                                 break;
1174
 
         
1175
 
                           case INCREMENT: case DECREMENT:
1176
 
                                 if (t.scanOperand) {
1177
 
                                         operators.push(Node(t));  // prefix increment or decrement
1178
 
                                 } else {
1179
 
                                         // Use >, not >=, so postfix has higher precedence than prefix.
1180
 
                                         while (opPrecedence[operators.top().type] > opPrecedence[tt])
1181
 
                                                 reduce();
1182
 
                                         n = Node(t, tt, operands.pop());
1183
 
                                         n.postfix = true;
1184
 
                                         operands.push(n);
1185
 
                                 }
1186
 
                                 break;
1187
 
         
1188
 
                           case FUNCTION:
1189
 
                                 if (!t.scanOperand)
1190
 
                                         break loop;
1191
 
                                 operands.push(FunctionDefinition(t, x, false, EXPRESSED_FORM));
1192
 
                                 t.scanOperand = false;
1193
 
                                 break;
1194
 
         
1195
 
                           case NULL: case THIS: case TRUE: case FALSE:
1196
 
                           case IDENTIFIER: case NUMBER: case STRING: case REGEXP:
1197
 
                                 if (!t.scanOperand)
1198
 
                                         break loop;
1199
 
                                 operands.push(Node(t));
1200
 
                                 t.scanOperand = false;
1201
 
                                 break;
1202
 
         
1203
 
                           case LEFT_BRACKET:
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) {
1209
 
                                                 if (tt == COMMA) {
1210
 
                                                         t.get();
1211
 
                                                         n.push(null);
1212
 
                                                         continue;
1213
 
                                                 }
1214
 
                                                 n.push(Expression(t, x, COMMA));
1215
 
                                                 if (!t.match(COMMA))
1216
 
                                                         break;
1217
 
                                         }
1218
 
                                         t.mustMatch(RIGHT_BRACKET);
1219
 
                                         operands.push(n);
1220
 
                                         t.scanOperand = false;
1221
 
                                 } else {
1222
 
                                         // Property indexing operator.
1223
 
                                         operators.push(Node(t, INDEX));
1224
 
                                         t.scanOperand = true;
1225
 
                                         ++x.bracketLevel;
1226
 
                                 }
1227
 
                                 break;
1228
 
                           case TRANSIENT_LEFT_BRACKET:
1229
 
                                 // Property indexing operator.
1230
 
                                 operators.push(new Node(t, TRANSIENT_INDEX));
1231
 
                                 t.scanOperand = true;
1232
 
                                 ++x.bracketLevel;
1233
 
                                 break;                    
1234
 
                           case RIGHT_BRACKET:
1235
 
                                 if (t.scanOperand || x.bracketLevel == bl)
1236
 
                                         break loop;
1237
 
                                 do {
1238
 
                                        var type = reduce().type;
1239
 
                                 }
1240
 
                                 while (type != INDEX && type != TRANSIENT_INDEX)
1241
 
                                 --x.bracketLevel;
1242
 
                                 break;
1243
 
         
1244
 
                           case LEFT_CURLY:
1245
 
                                 if (!t.scanOperand)
1246
 
                                         break loop;
1247
 
                                 // Object initialiser.  As for array initialisers (see above),
1248
 
                                 // parse using recursive descent.
1249
 
                                 ++x.curlyLevel;
1250
 
                                 n = Node(t, OBJECT_INIT);
1251
 
                           object_init:
1252
 
                                 if (!t.match(RIGHT_CURLY)) {
1253
 
                                         do {
1254
 
                                                 tt = t.get();
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));
1260
 
                                                 } else {
1261
 
                                                         switch (tt) {
1262
 
                                                           case IDENTIFIER:
1263
 
                                                           case NUMBER:
1264
 
                                                           case STRING:
1265
 
                                                                 id = Node(t);
1266
 
                                                                 break;
1267
 
                                                           case RIGHT_CURLY:
1268
 
                                                                 if (x.ecmaStrictMode)
1269
 
                                                                         throw t.newSyntaxError("Illegal trailing ,");
1270
 
                                                                 break object_init;
1271
 
                                                           default:
1272
 
                                                                 throw t.newSyntaxError("Invalid property name");
1273
 
                                                         }
1274
 
                                                         t.mustMatch(COLON);
1275
 
                                                         n.push(Node(t, PROPERTY_INIT, id,
1276
 
                                                                                         Expression(t, x, COMMA)));
1277
 
                                                 }
1278
 
                                         } while (t.match(COMMA));
1279
 
                                         t.mustMatch(RIGHT_CURLY);
1280
 
                                 }
1281
 
                                 operands.push(n);
1282
 
                                 t.scanOperand = false;
1283
 
                                 --x.curlyLevel;
1284
 
                                 break;
1285
 
         
1286
 
                           case RIGHT_CURLY:
1287
 
                                 if (!t.scanOperand && x.curlyLevel != cl)
1288
 
                                         throw "PANIC: right curly botch";
1289
 
                                 break loop;
1290
 
         
1291
 
                           case YIELDING:
1292
 
                                 while (opPrecedence[operators.top().type] > opPrecedence[YIELDING])
1293
 
                                         reduce();
1294
 
                                 t.mustMatch(LEFT_PAREN);
1295
 
                                 var yielding = true;
1296
 
                                 // FALL THROUGH
1297
 
                                 
1298
 
                           case LEFT_PAREN:
1299
 
                                 if (t.scanOperand) {
1300
 
                                         operators.push(Node(t, GROUP));
1301
 
                                 } else {
1302
 
                                         while (opPrecedence[operators.top().type] > opPrecedence[NEW])
1303
 
                                                 reduce();
1304
 
         
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;
1313
 
                                                         --operators.length;
1314
 
                                                         n.push(operands.pop());
1315
 
                                                 } else {
1316
 
                                                         n = Node(t, CALL, operands.pop(),
1317
 
                                                                                  Node(t, LIST));
1318
 
                                                 }
1319
 
                                                 operands.push(n);
1320
 
                                                 t.scanOperand = false;
1321
 
                                                 n.yielding = yielding || false;
1322
 
                                                 break;
1323
 
                                         }
1324
 
                                         if (n.type == NEW) {
1325
 
                                                 n.type = NEW_WITH_ARGS;
1326
 
                                         } else {
1327
 
                                                 n = Node(t, CALL);
1328
 
                                                 operators.push(n);
1329
 
                                         }
1330
 
                                         n.yielding = yielding || false;
1331
 
                                 }
1332
 
                                 ++x.parenLevel;
1333
 
                                 break;
1334
 
         
1335
 
                           case RIGHT_PAREN:
1336
 
                                 if (t.scanOperand || x.parenLevel == pl)
1337
 
                                         break loop;
1338
 
                                 while ((tt = reduce().type) != GROUP && tt != CALL &&
1339
 
                                                tt != NEW_WITH_ARGS) {
1340
 
                                         continue;
1341
 
                                 }
1342
 
                                 if (tt != GROUP) {
1343
 
                                         n = operands.top();
1344
 
                                         if (n[1].type != COMMA)
1345
 
                                                 n[1] = Node(t, LIST, n[1]);
1346
 
                                         else
1347
 
                                                 n[1].type = LIST;
1348
 
                                 }
1349
 
                                 --x.parenLevel;
1350
 
                                 break;
1351
 
         
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.
1355
 
                           default:
1356
 
                                 break loop;
1357
 
                         }
1358
 
                 }
1359
 
         
1360
 
                 if (x.hookLevel != hl)
1361
 
                         throw t.newSyntaxError("Missing : after ?");
1362
 
                 if (t.scanOperand)
1363
 
                         throw t.newSyntaxError("Missing operand");
1364
 
         
1365
 
                 // Resume default mode, scanning for operands, not operators.
1366
 
                 t.scanOperand = true;
1367
 
                 t.unget();
1368
 
                 while (operators.length)
1369
 
                         reduce();
1370
 
                 return operands.pop();
1371
 
         }
1372
 
         
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);
1377
 
                 if (!t.done())
1378
 
                         throw t.newSyntaxError("Syntax error");
1379
 
                 return n;
1380
 
         }
1381
 
         
1382
 
         // make stuff visible to StrandsCompiler
1383
 
         this.parse      = parse;
1384
 
         this.Node       = Node;
1385
 
         this.tokens     = tokens;
1386
 
         this.consts     = consts;
1387
 
         
1388
 
}).call(Narcissus);
1389
 
/* ***** BEGIN LICENSE BLOCK *****
1390
 
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
1391
 
 *
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/
1396
 
 *
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
1400
 
 * License.
1401
 
 *
1402
 
 * The Original Code is the Narrative JavaScript compiler.
1403
 
 *
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.
1408
 
 *
1409
 
 * Contributor(s):
1410
 
 *
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.
1422
 
 *
1423
 
 * ***** END LICENSE BLOCK ***** */
1424
 
 
1425
 
NJS_CODE = Narcissus.REGEXP; // fake node type for strands code segment
1426
 
 
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);
1433
 
}
1434
 
 
1435
 
var strandscp = StrandsCompiler.prototype;
1436
 
 
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];
1444
 
                } else {
1445
 
                        if (typeof(value) == "string")
1446
 
                                value = value.toLowerCase();
1447
 
        
1448
 
                        options[name] = value == "yes" || value == "true" || value == "on" || value == "1";
1449
 
                }
1450
 
        }
1451
 
}
1452
 
 
1453
 
strandscp.compile = function (/*string*/ code, /*string*/ scriptName,thisObject,scopeObject) {
1454
 
        if (!_frm)
1455
 
                var frame = {_cp:0};
1456
 
        else
1457
 
                var frame = _frm(this,arguments,[],[]);
1458
 
        if (!frame._cp) {
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 || {};
1467
 
                frame._cp = 1;  
1468
 
        }
1469
 
        if (_frm) {
1470
 
                try {
1471
 
                        var errors = strandscp.currentError = {};
1472
 
                        frame.n = this.assemble(frame.n, frame.resolver, this.options);
1473
 
                } catch (e) {
1474
 
                        if (strands && e == strands.Suspension) // this is a continuation escape, suspend so we can try again later
1475
 
                                return frame._s();
1476
 
                        throw e; // this is a real error
1477
 
                }
1478
 
        }
1479
 
        else {
1480
 
                var errors = strandscp.currentError = {};
1481
 
                frame.n = this.assemble(frame.n, frame.resolver, this.options);
1482
 
        }
1483
 
        if (errors.next)
1484
 
                throw errors.next;
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();
1490
 
}
1491
 
 
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++) {
1498
 
                        n[i] = n.cases[i];
1499
 
                }
1500
 
                n.length = n.cases.length;
1501
 
        }
1502
 
        
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];
1507
 
                }
1508
 
        }
1509
 
        
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.
1513
 
        if( n.treeified )
1514
 
                return;
1515
 
 
1516
 
        n.treeified = true;
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;
1519
 
        for (var i in n) {
1520
 
                if (n[i] != null &&
1521
 
                    typeof(n[i]) == 'object' &&
1522
 
                    n[i].constructor == Narcissus.Node &&
1523
 
                        i != "target" &&
1524
 
                        i != "parent") 
1525
 
                {
1526
 
                        var child = n[i];
1527
 
 
1528
 
                        // set this before treeification so that our parent is available
1529
 
                        // in the post-treeification below.
1530
 
                        child.parent = n;
1531
 
 
1532
 
                        this.treeify(child);
1533
 
                        
1534
 
                        if(child.yielding && child.type != Narcissus.FUNCTION)
1535
 
                                n.yielding = true;
1536
 
                }
1537
 
        }
1538
 
 
1539
 
        if (n.type == Narcissus.TRY) {
1540
 
                n = n.parent;
1541
 
                while (n != null && n.type != Narcissus.FUNCTION)
1542
 
                        n = n.parent;
1543
 
                
1544
 
                if (n != null)
1545
 
                        n.hasTry = true;
1546
 
        }
1547
 
}
1548
 
 
1549
 
strandscp.noderize = function(opts) {
1550
 
        var n = new Array();
1551
 
        n.nodeID = this.nodeSequence++;
1552
 
        n.toString = Narcissus.Node.prototype.toString;
1553
 
        if (opts) {
1554
 
                for (var i in opts) {
1555
 
                        n[i] = opts[i];
1556
 
                }
1557
 
        }
1558
 
        n.isNode = true;
1559
 
        return n;
1560
 
}
1561
 
 
1562
 
strandscp.assemble = function(root, scopeResolver, options) {
1563
 
        function ObjectType(object,field) {             
1564
 
                this.object = object;
1565
 
                this.field = field;
1566
 
        }
1567
 
        ObjectType.prototype = {
1568
 
                getValue : function() {
1569
 
                        if (this.value)
1570
 
                                return this.value;
1571
 
                        else if (this.object && this.field)
1572
 
                                exceptingGet(this.object,this.field);
1573
 
                },
1574
 
                getType : function() {
1575
 
                        if (this.object && this.field)
1576
 
                                return exceptingGet(exceptingGet(this.object,"structure"), this.field);
1577
 
                }
1578
 
        }
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;
1584
 
        
1585
 
        var codePtrSequence = 0;
1586
 
        var executed = [];
1587
 
        
1588
 
        var stack = [];
1589
 
        var exPtrStack = [];
1590
 
                
1591
 
        var statements = [];
1592
 
        while (root.length) {
1593
 
                tailFrame = null;
1594
 
                statements.push(execute(root.shift()));
1595
 
        }
1596
 
 
1597
 
        for( var i = 0; i < statements.length; i++ ) {
1598
 
                if (statements[i])
1599
 
                        root.push(statements[i]);
1600
 
        }
1601
 
        var lineno;
1602
 
        return root;
1603
 
        
1604
 
        function execute(node) {
1605
 
                if (node == null)
1606
 
                        return null;
1607
 
 
1608
 
                stack.push(node);
1609
 
                lineno = node.lineno;
1610
 
                switch(node.type) {
1611
 
                  case SCRIPT:
1612
 
                        throw new Error("what's a script doing in a statement?");
1613
 
 
1614
 
                  case FUNCTION:
1615
 
                        if (scopeResolver.isYielding() && node.name) {
1616
 
                                node.scoped = true;
1617
 
                                node.name = scopeResolver.addSymbol(node.name,FUNCTION_TYPE);                           
1618
 
                        }
1619
 
                        if (isYielding(node.body)) {
1620
 
                                scopeResolver.push(node.body, true);
1621
 
                                var vars = '';
1622
 
                                var scopedVars = scopeResolver.scopes[scopeResolver.scopes.length-1];
1623
 
                                for (var i in scopedVars) {
1624
 
                                        vars += ',"' + i + '"';
1625
 
                                }
1626
 
                                vars = vars.substring(1);
1627
 
                                var params = '';
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);
1631
 
                                }
1632
 
                                params = params.substring(1);
1633
 
                                var openNodes = [
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,[~],[~])){',
1635
 
                                             params,vars),
1636
 
                                    codeNode('while(1){'),
1637
 
                                    (!options.exceptions ?
1638
 
                                                codeNode("$_noex=1;") :
1639
 
                                        node.hasTry ?
1640
 
                                          codeNode('try{') :
1641
 
                                          codeNode('')),
1642
 
                                    codeNode('switch(_cp){case 0:')
1643
 
                                ];
1644
 
 
1645
 
                                while (openNodes.length)
1646
 
                                        node.body.unshift(openNodes.pop());
1647
 
 
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("}}"));
1652
 
 
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();
1656
 
                        } else {
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;
1660
 
                                }
1661
 
                                
1662
 
                                scopeResolver.push(node.body, false);
1663
 
                                assemble(node.body, scopeResolver, options);
1664
 
                                scopeResolver.pop();
1665
 
                        }
1666
 
                        break;
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({
1672
 
                                                                type: LIST,
1673
 
                                                                0: node[0][0],
1674
 
                                                                1: codeNode('"' + propName + '"')});
1675
 
                                else {
1676
 
                                        node[1] = noderize({
1677
 
                                                type: LIST,
1678
 
                                                0: node[0][0],
1679
 
                                                1: codeNode('"' + propName + '"'),
1680
 
                                                2: node[1]});
1681
 
                                }
1682
 
                                node.type=CALL;
1683
 
                                node[0] = codeNode("_p");
1684
 
                        }
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({
1689
 
                                                                type: LIST,
1690
 
                                                                0: node[0][0],
1691
 
                                                                1: index});
1692
 
                                else {
1693
 
                                        node[1] = noderize({
1694
 
                                                type: LIST,
1695
 
                                                0: node[0][0],
1696
 
                                                1: index,
1697
 
                                                2: node[1]});
1698
 
                                        }
1699
 
                                node.type=CALL;
1700
 
                                node[0] = codeNode("_p");
1701
 
                        }
1702
 
                        else {
1703
 
                                for (var i = 0; i < node.length; i++) {
1704
 
                                        node[i] = execute(node[i]);
1705
 
                                }
1706
 
                                if (node.type != DELETE)
1707
 
                                        typeCheck(node[0],node[1]);
1708
 
                                break;
1709
 
                        }
1710
 
                        // fall through
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)
1714
 
                                node[0].type     = DOT;
1715
 
                        if (node[0].type == TRANSIENT_INDEX) 
1716
 
                                node[0].type = INDEX;
1717
 
                        
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;
1722
 
                                break;
1723
 
                        }
1724
 
                        var varType;
1725
 
                        if (node.type == NEW)
1726
 
                                varType = node[0].varType;
1727
 
                        if (!isYielding(node) || !withinYielding(node))
1728
 
                                break;
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]);
1733
 
                        }
1734
 
                        
1735
 
                        codePtrSequence++;
1736
 
                        
1737
 
                        // update our code pointer
1738
 
                        addCode("_cp=~;",
1739
 
                                        codePtrSequence);
1740
 
 
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,
1745
 
                                                                                        yielding: true,
1746
 
                                                                                        0 : codeNode("_new"),
1747
 
                                                                                        1 : noderize({type: LIST,
1748
 
                                                                                                                                  0:node[0]})
1749
 
                                });
1750
 
                                if (newArgs)
1751
 
                                        node[1][1] = newArgs;
1752
 
                        }                                                        
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
1758
 
                                replaceNode(null);
1759
 
                        } else {
1760
 
                                var newNode = codeNode("_r.v~", 
1761
 
                                                                         codePtrSequence);
1762
 
                                newNode.varType = varType;
1763
 
                                replaceNode(newNode);
1764
 
                        }
1765
 
                        
1766
 
                        var checkRetNode = noderize({
1767
 
                                type: IF,
1768
 
                                condition: noderize({
1769
 
                                        type: STRICT_EQ,
1770
 
                                        0: noderize({
1771
 
                                                type: GROUP,
1772
 
                                                0: noderize({
1773
 
                                                        type: ASSIGN,
1774
 
                                                        0: codeNode("_r.v~",
1775
 
                                                                                codePtrSequence),
1776
 
                                                        1: node
1777
 
                                                })
1778
 
                                        }),
1779
 
                                        1: codeNode("_S")
1780
 
                                }),
1781
 
                                thenPart: noderize({
1782
 
                                        type: SEMICOLON,
1783
 
                                        expression: codeNode("return _s()")
1784
 
                                })
1785
 
                        });
1786
 
                        
1787
 
                        // add our call as a statement at the top level
1788
 
                        statements.push(checkRetNode);
1789
 
                        
1790
 
                        break;
1791
 
 
1792
 
                  case IF:
1793
 
                        node.condition = execute(node.condition);
1794
 
                        if (isYielding(node.thenPart) ||
1795
 
                                (node.elsePart && isYielding(node.elsePart)))
1796
 
                        {                               
1797
 
                                var thenPtr = ++codePtrSequence;
1798
 
                                if (node.elsePart)
1799
 
                                        var elsePtr = ++codePtrSequence;
1800
 
                                var endPtr = ++codePtrSequence;
1801
 
                                newConditional(node.condition, thenPtr, elsePtr || endPtr);
1802
 
                                newCodeSegment(thenPtr);
1803
 
                                
1804
 
                                // thenPart
1805
 
                                execBlock(node.thenPart);
1806
 
                                gotoCodeSegment(endPtr);
1807
 
 
1808
 
                                // elsePart
1809
 
                                if (node.elsePart) {
1810
 
                                        newCodeSegment(elsePtr);
1811
 
                                        execBlock(node.elsePart);
1812
 
                                        gotoCodeSegment(endPtr);
1813
 
                                }
1814
 
                                
1815
 
                                // end if
1816
 
                                newCodeSegment(endPtr);
1817
 
 
1818
 
                                replaceNode(null);
1819
 
                        } else {
1820
 
                                // make sure we catch any breaks or continues
1821
 
                                node.thenPart = execute(node.thenPart);
1822
 
                                if (node.elsePart)
1823
 
                                        node.elsePart = execute(node.elsePart);
1824
 
                        }
1825
 
                        break;
1826
 
 
1827
 
                  case FOR_IN: // varDecl/iterator, object, body
1828
 
                        if (node.varDecl == null) {
1829
 
                                node.iterator = execute(node.iterator);
1830
 
                        } else {
1831
 
                                node.varDecl = execute(node.varDecl);
1832
 
                        }
1833
 
                        node.object = execute(node.object);
1834
 
                        
1835
 
/*                      if (!isYielding(node.body)) { // we will always do it so they we can weed out dont-enums.
1836
 
                                node.body = execute(node.body);
1837
 
                                break;
1838
 
                        }*/
1839
 
                        
1840
 
                        // grab all items from the object and stick them in a local array
1841
 
                        var iterId = codePtrSequence;
1842
 
                        statements.push(noderize({
1843
 
                                type: NJS_CODE, 
1844
 
                                value: subst("_r.iter~=_keys(", iterId),
1845
 
                                lineno: node.object.lineno
1846
 
                        }));
1847
 
                        statements.push(node.object);
1848
 
                        addCode(");");
1849
 
 
1850
 
                        // change the FOR_IN into a regular FOR
1851
 
                        node.type = FOR;
1852
 
                        node.setup = codeNode("_r.ctr~=0;", iterId);
1853
 
                        node.condition = codeNode("_r.ctr~<_r.iter~.length",
1854
 
                                                      iterId, iterId);
1855
 
                        node.update = codeNode("_r.ctr~++", iterId);
1856
 
                        var initializer = codeNode("_r.iter~[_r.ctr~]",
1857
 
                                                        iterId, iterId);
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});
1861
 
 
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}));
1868
 
                        } else {
1869
 
                                // varDecl -- use the initializer
1870
 
                                node.varDecl[0].initializer = initializer;
1871
 
                                node.body.unshift(node.varDecl);
1872
 
                        }
1873
 
                        node.iterator = null;
1874
 
                        node.varDecl = null;
1875
 
                        // FALL THROUGH
1876
 
                        
1877
 
                  case FOR:
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);
1883
 
                                break;
1884
 
                        }
1885
 
                        
1886
 
                        // turn it into a WHILE statement
1887
 
                        node.type = WHILE;
1888
 
                        
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});
1892
 
 
1893
 
                        statements.push(node.setup);
1894
 
                        node.setup = null;
1895
 
 
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});
1899
 
 
1900
 
                        node.updatePtr = ++codePtrSequence;
1901
 
                        node.body.push(newCodeSegmentNode(node.updatePtr));
1902
 
                        
1903
 
                        // make sure the proper update happens in the block
1904
 
                        node.body.push(noderize({type: SEMICOLON, expression: node.update}));
1905
 
                        node.update = null;                             
1906
 
 
1907
 
                        // FALL THROUGH
1908
 
 
1909
 
                  case WHILE:
1910
 
                        if (isYielding(node)) {
1911
 
                                node.continuePtr = ++codePtrSequence;
1912
 
                                newCodeSegment(node.continuePtr);
1913
 
                                node.condition = execute(node.condition);
1914
 
 
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);
1922
 
 
1923
 
                                replaceNode(null);
1924
 
                        } else {
1925
 
                                node.condition = execute(node.condition);
1926
 
                                node.body = execute(node.body);
1927
 
                        }
1928
 
                        break;
1929
 
                  
1930
 
                  case DO:
1931
 
                        if (isYielding(node)) {
1932
 
                                node.continuePtr = ++codePtrSequence;
1933
 
                                node.breakPtr = ++codePtrSequence;
1934
 
                                newCodeSegment(node.continuePtr);
1935
 
                                execBlock(node.body);
1936
 
 
1937
 
                                newConditional(execute(node.condition), node.continuePtr, node.breakPtr);
1938
 
                                newCodeSegment(node.breakPtr);
1939
 
 
1940
 
                                replaceNode(null);
1941
 
                        } else {
1942
 
                                node.condition = execute(node.condition);
1943
 
                                node.body = execute(node.body);
1944
 
                        }
1945
 
                    break;
1946
 
                  
1947
 
                  case BREAK:
1948
 
                        if (node.target.breakPtr != null) {
1949
 
                                replaceNode(codeNode("_cp=~;break;", 
1950
 
                                                         node.target.breakPtr));
1951
 
                        }
1952
 
                        break;
1953
 
                        
1954
 
                  case CONTINUE:
1955
 
                        if (node.target.continuePtr != null) {
1956
 
                                replaceNode(codeNode("_cp=~;break;", 
1957
 
                                                         node.target.updatePtr || node.target.continuePtr));
1958
 
                        }
1959
 
                        break;
1960
 
                  
1961
 
                  case SWITCH:
1962
 
                        if (!isYielding(node))
1963
 
                                break;
1964
 
                        
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);
1970
 
                        } else {
1971
 
                                conditional = codeNode(node.breakPtr);
1972
 
                        }
1973
 
                        
1974
 
                        for (var i = node.length - 1; i >= 0; i--) {
1975
 
                                if (i == node.defaultIndex)
1976
 
                                        continue;
1977
 
                                
1978
 
                                // adjust the line numbering of the case label nodes
1979
 
                                removeLineNumbers(node[i].caseLabel);
1980
 
                                
1981
 
                                node[i].codePtr = ++codePtrSequence;
1982
 
                                conditional = noderize({
1983
 
                                        type: CONDITIONAL,
1984
 
                                        0: noderize({
1985
 
                                                type: EQ,
1986
 
                                                0: node.discriminant,
1987
 
                                                1: node[i].caseLabel
1988
 
                                        }),
1989
 
                                        1: codeNode(node[i].codePtr),
1990
 
                                        2: conditional
1991
 
                                });
1992
 
                        }
1993
 
                        
1994
 
                        statements.push(noderize({
1995
 
                                type: SEMICOLON,
1996
 
                                expression: noderize({
1997
 
                                        type: ASSIGN,
1998
 
                                        0: codeNode("_cp"),
1999
 
                                        1: execute(conditional)
2000
 
                                })
2001
 
                        }));
2002
 
                        statements.push(codeNode("break;"));
2003
 
                        
2004
 
                        for (var i = 0; i < node.length; i++) {
2005
 
                                newCodeSegment(node[i].codePtr);
2006
 
                                execBlock(node[i].statements);
2007
 
                        }
2008
 
                        
2009
 
                        newCodeSegment(node.breakPtr);
2010
 
                        
2011
 
                        replaceNode(null);
2012
 
                        break;
2013
 
                        
2014
 
                  case WITH:
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++)
2022
 
                                if (!body[i]) {
2023
 
                                        body.splice(i--,1);
2024
 
                                }
2025
 
                        statements = oldStatements;
2026
 
                        
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]);
2032
 
                                        }
2033
 
                                }
2034
 
                        codePtrSequence++;
2035
 
                        node.body.push(codeNode('_cp=~}break;',codePtrSequence));
2036
 
                        statements.push(node);
2037
 
                        replaceNode(null);
2038
 
                        newCodeSegment(codePtrSequence);
2039
 
                    }
2040
 
                        break;
2041
 
 
2042
 
                  case TRY:
2043
 
                        if (!isYielding(node))
2044
 
                                break;
2045
 
                        
2046
 
                        if (!options.exceptions)
2047
 
                                throw new Error("yielding within try/catch/finally not allowed when the exceptions are turned off in the compiler");
2048
 
 
2049
 
                        //   set codeptr for catches, finally, endptr
2050
 
                        for (var i = 0; i < node.catchClauses.length; i++) {
2051
 
                                node.catchClauses[i].codePtr = ++codePtrSequence;
2052
 
                        }
2053
 
                        
2054
 
                        if (node.finallyBlock)
2055
 
                                node.finallyBlock.codePtr = ++codePtrSequence;
2056
 
 
2057
 
                        var endPtr = ++codePtrSequence;
2058
 
                        
2059
 
                        // set exception codePtr
2060
 
                        var exCodePtr = node.catchClauses.length ?
2061
 
                                        node.catchClauses[0].codePtr :
2062
 
                                        node.finallyBlock.codePtr;
2063
 
                        
2064
 
                        addCode("_r.ecp=~;", exCodePtr);
2065
 
                        exPtrStack.push(exCodePtr);
2066
 
                        execBlock(node.tryBlock);
2067
 
                        node.finallyBlock ? gotoCodeSegment(node.finallyBlock.codePtr) :
2068
 
                                            gotoCodeSegment(endPtr);
2069
 
                        exPtrStack.pop();
2070
 
 
2071
 
                        for (var i = 0; i < node.catchClauses.length; i++) {
2072
 
                                var clause = node.catchClauses[i];
2073
 
                                newCodeSegment(clause.codePtr);
2074
 
 
2075
 
                                if (i == 0) {
2076
 
                                        // first catch block
2077
 
                                        // set exception codePtr appropriately
2078
 
                                        addCode("_r.ecp=~;", 
2079
 
                                                                node.finallyBlock ? 
2080
 
                                                                node.finallyBlock.codePtr :
2081
 
                                                                (exPtrStack.top() || "null"));
2082
 
                                        // reset throwing flag to prevent infinite loopage
2083
 
                                        addCode("$_thr=false;");
2084
 
                                }
2085
 
 
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 
2089
 
                                // lazy right now.
2090
 
                                scopeResolver.addSymbol(clause.varName,ANY_TYPE)
2091
 
                                addCode("~ = $_ex;", scopeResolver.getSymbol(clause.varName));
2092
 
 
2093
 
                                if (clause.guard) {
2094
 
                                        clause.guard = execute(clause.guard);
2095
 
                                        statements.push(noderize({
2096
 
                                                type: NJS_CODE,
2097
 
                                                value: "if(!(",
2098
 
                                                lineno: clause.guard.lineno
2099
 
                                        }));
2100
 
                                        statements.push(clause.guard);
2101
 
                                        addCode(")) {");
2102
 
 
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());
2110
 
                                        } else {
2111
 
                                                addCode("throw ~;", scopeResolver.getSymbol(clause.varName));
2112
 
                                        }
2113
 
                                        
2114
 
                                        addCode("}");
2115
 
                                }
2116
 
                        
2117
 
                                if (node.finallyBlock)
2118
 
                                        exPtrStack.push(node.finallyBlock.codePtr);
2119
 
                                execBlock(clause.block);
2120
 
                                if (node.finallyBlock)
2121
 
                                        exPtrStack.pop();
2122
 
                                
2123
 
                                // handle successful execution of catch clause
2124
 
                                if (node.finallyBlock) {
2125
 
                                        gotoCodeSegment(node.finallyBlock.codePtr);
2126
 
                                } else {
2127
 
                                        gotoCodeSegment(endPtr);
2128
 
                                }
2129
 
                        }
2130
 
                        
2131
 
                        if (node.finallyBlock) {
2132
 
                                newCodeSegment(node.finallyBlock.codePtr);
2133
 
 
2134
 
                                // set the exception code pointer
2135
 
                                addCode("_r.ecp=~;", 
2136
 
                                                                    exPtrStack.top() || "null");
2137
 
 
2138
 
                                execBlock(node.finallyBlock);
2139
 
 
2140
 
                                // if we're throwing, rethrow, otherwise goto endPtr
2141
 
                                addCode("if($_thr){");
2142
 
                                if (exPtrStack.length) {
2143
 
                                        gotoCodeSegment(exPtrStack.top());
2144
 
                                } else {
2145
 
                                        addCode("$_except($_ex);return;");
2146
 
                                }
2147
 
                                addCode("}else{");
2148
 
                                gotoCodeSegment(endPtr);
2149
 
                                addCode("}");
2150
 
                        }
2151
 
 
2152
 
                        newCodeSegment(endPtr);
2153
 
                        
2154
 
                        replaceNode(null);
2155
 
                        break;
2156
 
                  case TRUE: case FALSE: node.varType = "Boolean"; break;
2157
 
                  case NUMBER: 
2158
 
                        node.varType = "Number"; 
2159
 
                  break;
2160
 
                  case STRING: 
2161
 
                        node.varType = "String";
2162
 
                        break;
2163
 
                        
2164
 
                  case THIS: 
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:
2170
 
                        // nothing to do
2171
 
                        break;
2172
 
                
2173
 
                  case IDENTIFIER:
2174
 
                        node.value = scopeResolver.getSymbol(node.value);
2175
 
                    node.initializer = execute(node.initializer);
2176
 
                    if (node.initializer)
2177
 
                            typeCheck(node,node.initializer);
2178
 
                        break;
2179
 
                        
2180
 
                  case THROW:
2181
 
                        node.exception = execute(node.exception);
2182
 
                        break;
2183
 
 
2184
 
                  case RETURN:
2185
 
                  case SEMICOLON:
2186
 
                        node.expression = execute(node.expression);
2187
 
                        break;
2188
 
                  
2189
 
                  case OR: case AND:
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.
2193
 
                        var left = node[0];
2194
 
                        var right = node[1];
2195
 
                        
2196
 
                        node[0] = left = execute(left);
2197
 
                        if (!isYielding(right)) {
2198
 
                                node[1] = execute(right);
2199
 
                                break;
2200
 
                        }
2201
 
 
2202
 
                        var condVar = "_c" + codePtrSequence;
2203
 
                        
2204
 
                        // put the left in it's own assign statement
2205
 
                        statements.push(noderize({
2206
 
                                type: SEMICOLON,
2207
 
                                expression: noderize({
2208
 
                                        type: ASSIGN,
2209
 
                                        0: codeNode("var ~", condVar),
2210
 
                                        1: left
2211
 
                                })
2212
 
                        }));
2213
 
                        
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) {
2218
 
                                cond = noderize({
2219
 
                                        type: NOT,
2220
 
                                        value: "!",
2221
 
                                        0: cond
2222
 
                                });
2223
 
                        }
2224
 
                        
2225
 
                        // create an if node that checks the guarded value and executes
2226
 
                        // the right if appropriate
2227
 
                        var guard = noderize({
2228
 
                                type: IF,
2229
 
                                condition: cond,
2230
 
                                thenPart: noderize({
2231
 
                                        type: SEMICOLON,
2232
 
                                        expression: noderize({
2233
 
                                                type: ASSIGN,
2234
 
                                                0: codeNode(condVar),
2235
 
                                                1: right,
2236
 
                                                yielding: true
2237
 
                                        }),
2238
 
                                        parent: node,
2239
 
                                        yielding: true
2240
 
                                }),
2241
 
                                yielding: true
2242
 
                        });
2243
 
                        // execute the if node as if it were top-level
2244
 
                        var tmpStack = stack;
2245
 
                        stack = [];
2246
 
                        statements.push(execute(guard));
2247
 
                        stack = tmpStack;
2248
 
                        
2249
 
                        // finally, hand back the result of the guarding process
2250
 
                        node.type = NJS_CODE;
2251
 
                        node.value = condVar;
2252
 
                        break;
2253
 
                  case VAR:
2254
 
                        if(scopeResolver.isYielding())
2255
 
                                node.scoped = true;
2256
 
                        for (var i = 0; i < node.length; i++) {
2257
 
                                node[i] = execute(node[i]);
2258
 
                        }
2259
 
                        break;
2260
 
                  
2261
 
 
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]);
2268
 
                        }
2269
 
                        break;
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
2276
 
                        }
2277
 
                        break;
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]);
2282
 
                        }
2283
 
                        break;
2284
 
                  
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
2288
 
                        }
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";
2293
 
                        break;
2294
 
                  case CONDITIONAL:
2295
 
                        // pull out comparisons with right-side yields into their own
2296
 
                        // statements and transform them separately.
2297
 
                        var cond = node[0];
2298
 
                        var left = node[1];
2299
 
                        var right = node[2];
2300
 
                                                
2301
 
                        node[0] = cond = execute(cond);
2302
 
                        if (!isYielding(left) && !isYielding(right)) {
2303
 
                                node[1] = execute(left);
2304
 
                                node[2] = execute(right);
2305
 
                                break;
2306
 
                        }
2307
 
 
2308
 
                        var condVar = "_c" + codePtrSequence;
2309
 
                        
2310
 
                        // put the left in it's own assign statement
2311
 
                        statements.push(noderize({
2312
 
                                type: SEMICOLON,
2313
 
                                expression: codeNode("var ~", condVar)
2314
 
                        }));
2315
 
                        
2316
 
                        // create an if node that checks the guarded value and executes
2317
 
                        // the right if appropriate
2318
 
                        var guard = noderize({
2319
 
                                type: IF,
2320
 
                                condition: cond,
2321
 
                                thenPart: noderize({
2322
 
                                        type: SEMICOLON,
2323
 
                                        expression: noderize({
2324
 
                                                type: ASSIGN,
2325
 
                                                0: codeNode(condVar),
2326
 
                                                1: left,
2327
 
                                                yielding: true
2328
 
                                        }),
2329
 
                                        parent: node,
2330
 
                                        yielding: true
2331
 
                                }),
2332
 
                                elsePart: noderize({
2333
 
                                        type: SEMICOLON,
2334
 
                                        expression: noderize({
2335
 
                                                type: ASSIGN,
2336
 
                                                0: codeNode(condVar),
2337
 
                                                1: right,
2338
 
                                                yielding: true
2339
 
                                        }),
2340
 
                                        parent: node,
2341
 
                                        yielding: true
2342
 
                                }),
2343
 
                                yielding: true
2344
 
                        });
2345
 
                        // execute the if node as if it were top-level
2346
 
                        var tmpStack = stack;
2347
 
                        stack = [];
2348
 
                        statements.push(execute(guard));
2349
 
                        stack = tmpStack;
2350
 
                        
2351
 
                        // finally, hand back the result of the guarding process
2352
 
                        node.type = NJS_CODE;
2353
 
                        node.value = condVar;
2354
 
                        break;
2355
 
                  case BITWISE_OR: case BITWISE_XOR: case BITWISE_AND:
2356
 
                  case BITWISE_NOT:
2357
 
                  case VOID:              
2358
 
                  case IN: 
2359
 
                  case COMMA: 
2360
 
                  case LIST:
2361
 
                  case GROUP: case BLOCK:
2362
 
                        for (var i = 0; i < node.length; i++) {
2363
 
                                node[i] = execute(node[i]);
2364
 
                        }
2365
 
                        break;
2366
 
                  case OBJECT_ID_REFERENCE:
2367
 
                        var get = noderize({
2368
 
                                                type: CALL,
2369
 
                                                parent: node.parent,
2370
 
                                                yielding: true,
2371
 
                                                0: codeNode("_ref"),
2372
 
                                                1: noderize({
2373
 
                                                        type:LIST,
2374
 
                                                        0:node[0]})});
2375
 
                        get = execute(get);
2376
 
                        node.type = get.type;
2377
 
                        node.value = get.value;
2378
 
                        node.varType = new ObjectType(scopeResolver.scopeObject,node[0].value);
2379
 
                        break;
2380
 
                  case IS:
2381
 
                        node.type = CALL;
2382
 
                        node[1] = execute(noderize({type: LIST,
2383
 
                                                                                                                        0: node[0],
2384
 
                                                                                                                        1: node[1]}));
2385
 
                        node[0] = codeNode("_is");
2386
 
                        break;
2387
 
                  case INDEX: 
2388
 
                        if (options.persistence && withinYielding(node) && node.parent.type != CALL) {
2389
 
                                var get = noderize({
2390
 
                                                        type: CALL,
2391
 
                                                        parent: node.parent,
2392
 
                                                        yielding: true,
2393
 
                                                        0: codeNode("_g"),
2394
 
                                                        1: noderize({
2395
 
                                                                type:LIST,
2396
 
                                                                0:node[0],
2397
 
                                                                1:node[1]})});
2398
 
                                get = execute(get);
2399
 
                                node.type = get.type;
2400
 
                                node.value = get.value;
2401
 
                                break;
2402
 
                        }
2403
 
                        // else fall through
2404
 
                  case TRANSIENT_INDEX:
2405
 
                                for (var i = 0; i < node.length; i++) {
2406
 
                                        node[i] = execute(node[i]);
2407
 
                                }
2408
 
                                node.type = INDEX;
2409
 
                                break;
2410
 
                  case DOT:
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({
2413
 
                                                        type: CALL,
2414
 
                                                        parent: node.parent,
2415
 
                                                        yielding: true,
2416
 
                                                        0: codeNode("_g"),
2417
 
                                                        1: noderize({
2418
 
                                                                type:LIST,
2419
 
                                                                0:node[0],
2420
 
                                                                1:codeNode('"' + node[1].value + '"')})});
2421
 
                                get = execute(get);
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);
2426
 
                                break;
2427
 
                        }
2428
 
                        // else fall through to TRANSIENT_DOT
2429
 
                  case TRANSIENT_DOT:
2430
 
//                              if (!options.persistence)
2431
 
        //                              throw new Error("direct reference syntax (.#) not supported without persevere");
2432
 
                                node[0] = execute(node[0]);
2433
 
                                node.type = DOT;
2434
 
                                break;
2435
 
 
2436
 
                  case PROPERTY_INIT:
2437
 
                        // don't execute n[0] because it might resolve to a scoped var
2438
 
                        node[1] = execute(node[1]);
2439
 
                        break;
2440
 
 
2441
 
                  default:
2442
 
                        throw new Error("PANIC: unknown node type " + Narcissus.tokens[node.type]);
2443
 
                }
2444
 
                
2445
 
                return stack.pop();
2446
 
        }
2447
 
        
2448
 
        function subst(str /*, ... */) {
2449
 
                for(var i = 1; i < arguments.length; i++) {
2450
 
                        str = str.replace("~", arguments[i]);
2451
 
                }
2452
 
                return str;
2453
 
        }
2454
 
 
2455
 
        function replaceNode(node) {
2456
 
                stack.pop();
2457
 
                stack.push(node);
2458
 
        }
2459
 
        
2460
 
        function execBlock(set) {
2461
 
                if (set.type == BLOCK) {
2462
 
 
2463
 
                        for (var i = 0; i < set.length; i++) {
2464
 
                                statements.push(execute(set[i]));
2465
 
                        }
2466
 
                        return set;
2467
 
                } else {
2468
 
                        set = execute(set);
2469
 
                        statements.push(set);
2470
 
                        return set;
2471
 
                }
2472
 
        }
2473
 
        
2474
 
        function newCodeSegment(id) {
2475
 
                var newNode = newCodeSegmentNode(id);
2476
 
                newNode.codeSegmentId = id;
2477
 
                statements.push(newNode);
2478
 
        }
2479
 
        
2480
 
        function newCodeSegmentNode(id) {
2481
 
                return codeNode("case ~:", id)
2482
 
        }
2483
 
        
2484
 
        function gotoCodeSegment(id) {
2485
 
                addCode("_cp=~;break;",id);
2486
 
        }
2487
 
 
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({
2492
 
                        type: NJS_CODE,
2493
 
                        value: subst("_cp=("),
2494
 
                        lineno: node.lineno
2495
 
                }));
2496
 
                statements.push(node);
2497
 
                addCode(")?~:~;break;", thenPtr, elsePtr);
2498
 
        }
2499
 
        
2500
 
        function addCode(str/*, ...*/) {
2501
 
                statements.push(codeNode.apply(this, arguments));
2502
 
        }
2503
 
        
2504
 
        function codeNode(str/*, ...*/) {
2505
 
                return noderize({
2506
 
                        type: NJS_CODE,
2507
 
                        value: subst.apply(this, arguments)
2508
 
                })
2509
 
        }
2510
 
 
2511
 
        function isYielding(node) {
2512
 
                return node != null && node.yielding;
2513
 
        }
2514
 
        function withinYielding(node) {
2515
 
                var parentNode = node;
2516
 
                while (parentNode) {
2517
 
                        if (parentNode.type == FUNCTION)
2518
 
                                return parentNode.yielding;
2519
 
                        parentNode = parentNode.parent;
2520
 
                }
2521
 
                return false;
2522
 
        }
2523
 
                
2524
 
        function removeLineNumbers(node) {
2525
 
                delete node.lineno;
2526
 
                for (n in node) {
2527
 
                        if (node[n] != null 
2528
 
                            && typeof(node[n]) == "object" 
2529
 
                            && n != "parent"
2530
 
                            && node[n].isNode) 
2531
 
                        {
2532
 
                                removeLineNumbers(node[n]);
2533
 
                        }
2534
 
                }
2535
 
        }
2536
 
        function exceptingGet(object,field) {
2537
 
                var value = pjs.get(object,field);
2538
 
                if (value == strands.Suspension)
2539
 
                        throw value;
2540
 
                return value;
2541
 
        }
2542
 
        function typeCheck(variable, value) {
2543
 
                if (!options.typeChecking)
2544
 
                        return value;
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;
2548
 
                if (!valueType)
2549
 
                        valueType = scopeResolver.getType(value.value);
2550
 
                if (!valueType)
2551
 
                        valueType = ANY_TYPE;
2552
 
                if (!variable || variable == ANY_TYPE)
2553
 
                        return value;
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);
2560
 
                        if (variableType)
2561
 
                                variableType.value = value.varType.value;
2562
 
                }
2563
 
                return value;
2564
 
        }
2565
 
        function addError(message) {
2566
 
                strandscp.currentError = strandscp.currentError.next ={message:message, lineNumber:lineno || 0};
2567
 
        }
2568
 
}
2569
 
var FUNCTION_TYPE = {value:"function"};
2570
 
var ANY_TYPE = {toString:function() {return "any"}};    
2571
 
 
2572
 
 
2573
 
function StrandsScopeResolver() {
2574
 
        this.scopes = [];
2575
 
        this.yieldingStatus = [];
2576
 
}
2577
 
 
2578
 
var nsrp = StrandsScopeResolver.prototype;
2579
 
nsrp.addError = function(message) {
2580
 
        strandscp.currentError = strandscp.currentError.next ={message:message, lineNumber:lineno || 0};
2581
 
}
2582
 
nsrp.push = function(n, isYielding) {
2583
 
        this.scopes.push({});
2584
 
        this.yieldingStatus.push(isYielding);
2585
 
        if(n.varDecls) {
2586
 
                for(var i = 0; i < n.varDecls.length; i++) {
2587
 
                        this.addSymbol(n.varDecls[i].value,n.varDecls[i].varType||ANY_TYPE);
2588
 
                }
2589
 
        }
2590
 
        if(n.funDecls) {
2591
 
                for(var i = 0; i < n.funDecls.length; i++) {
2592
 
                        this.addSymbol(n.funDecls[i].name,FUNCTION_TYPE);
2593
 
                }
2594
 
        }
2595
 
}
2596
 
nsrp.pop = function() {
2597
 
        this.yieldingStatus.pop();
2598
 
        return this.scopes.pop();
2599
 
}
2600
 
 
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);
2607
 
}
2608
 
nsrp.getType = function(name) {
2609
 
        var object = this.getSymbolObject(name);
2610
 
        if (object && object.type=="any")
2611
 
                return ANY_TYPE;
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;
2617
 
        }
2618
 
        if (object && object.type)
2619
 
                return object.type;
2620
 
        return ANY_TYPE;
2621
 
}
2622
 
nsrp.getSymbolObject = function(name) {
2623
 
        for (var i = this.scopes.length; i > 0;) {
2624
 
                i--;
2625
 
                if (this.scopes[i][name]) 
2626
 
                        return this.scopes[i][name];
2627
 
        }
2628
 
        return;
2629
 
}
2630
 
 
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])
2635
 
                return name;
2636
 
        for (var i = this.scopes.length; i > 0;) {
2637
 
                i--;
2638
 
                if (this.scopes[i][name]) 
2639
 
                        return name;
2640
 
        }
2641
 
        this.addError("The identifier " + name + " was not found");     
2642
 
        return name;
2643
 
}
2644
 
nsrp.getCurrentFrame = function() {
2645
 
        var id = this.scopes.length - 1;
2646
 
        if (id < 0) {
2647
 
                throw new Error("compiler error: empty scope resolver");
2648
 
        }
2649
 
        return "njf" + id;
2650
 
}
2651
 
nsrp.isYielding = function() {
2652
 
        if(this.scopes.length == 0)
2653
 
                return false;
2654
 
        
2655
 
        return this.yieldingStatus.top();
2656
 
}
2657
 
 
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]) 
2662
 
                        list += n + ", ";
2663
 
 
2664
 
                print(list);
2665
 
        }
2666
 
}
2667
 
 
2668
 
/* ***** BEGIN LICENSE BLOCK *****
2669
 
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
2670
 
 *
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/
2675
 
 *
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
2679
 
 * License.
2680
 
 *
2681
 
 * The Original Code is the Narrative JavaScript compiler.
2682
 
 *
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.
2687
 
 *
2688
 
 * Contributor(s):
2689
 
 *
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.
2701
 
 *
2702
 
 * ***** END LICENSE BLOCK ***** */
2703
 
 
2704
 
function StrandsScriptWriter() {
2705
 
        this.lines = [];
2706
 
        this.infix_lookup = {};
2707
 
        for( var i = 0; i < this.infix_operators.length; i++ ) {
2708
 
                this.infix_lookup[this.infix_operators[i]] = true;
2709
 
        }
2710
 
        this.prefix_lookup = {};
2711
 
        for( var i = 0; i < this.prefix_operators.length; i++ ) {
2712
 
                this.prefix_lookup[this.prefix_operators[i]] = true;
2713
 
        }
2714
 
        this.simple_lookup = {};
2715
 
        for( var i = 0; i < this.simple_tokens.length; i++ ) {
2716
 
                this.simple_lookup[this.simple_tokens[i]] = true;
2717
 
        }
2718
 
}
2719
 
 
2720
 
StrandsScriptWriter.dump = function(n) {
2721
 
        var o = new StrandsScriptWriter();
2722
 
        o.add(n);
2723
 
        print(o);
2724
 
}
2725
 
 
2726
 
var strandsswp = StrandsScriptWriter.prototype;
2727
 
 
2728
 
strandsswp.infix_operators = [
2729
 
         ',',
2730
 
         '||',
2731
 
         '&&',
2732
 
         '|',
2733
 
         '^',
2734
 
         '&',
2735
 
         '===',
2736
 
         '==',
2737
 
         '!==',
2738
 
         '!=',
2739
 
         '<<',
2740
 
         '<=',
2741
 
         '<',
2742
 
         '>>>',
2743
 
         '>>',
2744
 
         '>=',
2745
 
         '>',
2746
 
         '-',
2747
 
         '+',
2748
 
         '*',
2749
 
         '/',
2750
 
         '%',
2751
 
         '.',
2752
 
         '.#',
2753
 
         '='
2754
 
];
2755
 
 
2756
 
strandsswp.prefix_operators = [
2757
 
        '!',
2758
 
        '~',
2759
 
        'unary_plus',
2760
 
        'unary_minus'
2761
 
];
2762
 
 
2763
 
strandsswp.simple_tokens = [
2764
 
        "identifier",
2765
 
        "number",
2766
 
        "regexp",
2767
 
        "true",
2768
 
        "false",
2769
 
        "null",
2770
 
        "this"
2771
 
];
2772
 
 
2773
 
strandsswp.add = function(n) {
2774
 
        if( n == null ) 
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] ) {
2781
 
                this[method](n);
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);
2788
 
        } else {
2789
 
                throw new Error("ScriptWriter Error: unknown type: " + Narcissus.tokens[n.type]);
2790
 
        }
2791
 
}
2792
 
 
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) {
2797
 
                this.add(n);
2798
 
        } else {
2799
 
                this.write(n, "{");
2800
 
                this.add(n);
2801
 
                this.write(null, "}");
2802
 
        }
2803
 
}
2804
 
 
2805
 
strandsswp.write = function(n, text) {
2806
 
        if (text == null) 
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] || [];
2810
 
        line.push(text);
2811
 
        this.lines[lineno] = line;
2812
 
}
2813
 
 
2814
 
strandsswp.last = function() {
2815
 
        return this.lines.top().top();
2816
 
}
2817
 
 
2818
 
strandsswp.pop = function() {
2819
 
        return this.lines.top().pop();
2820
 
}
2821
 
 
2822
 
strandsswp.toString = function() {
2823
 
        var output = [];
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]);
2831
 
                        }
2832
 
                }
2833
 
                else {
2834
 
                        if (this.debug && this.sourceLines[i-1])
2835
 
                                output.push("/*" + this.sourceLines[i-1].replace(/\*\//g,'* ') + "\t\t\t\t\t\t*/");
2836
 
                }
2837
 
                output.push("\n");
2838
 
        }
2839
 
        return output.join("");
2840
 
}
2841
 
 
2842
 
strandsswp.write_script = function(n,output) {
2843
 
        for (var i = 0; i < n.length; i++) {
2844
 
                this.add(n[i]);
2845
 
        }
2846
 
}
2847
 
 
2848
 
strandsswp.write_infix_operator = function(n) {
2849
 
        this.add(n[0]);
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
2853
 
        this.add(n[1]);
2854
 
}
2855
 
 
2856
 
strandsswp.write_prefix_operator = function(n) {
2857
 
        this.write(n, n.value);
2858
 
        this.add(n[0]);
2859
 
}
2860
 
 
2861
 
strandsswp.write_function = function(n) {
2862
 
        if(n.scoped) {
2863
 
                this.write(n, n.name);
2864
 
                this.write(n, " = ");
2865
 
        }
2866
 
        this.write(n, "function");
2867
 
        if(n.name && !n.scoped) {
2868
 
                this.write(n, " ");
2869
 
                this.write(n, n.name);
2870
 
        }
2871
 
        this.write(n, "(");
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, "){");
2877
 
        this.add(n.body);
2878
 
        this.write(null, "}");
2879
 
        if(n.scoped) {
2880
 
                this.write(null, ";");
2881
 
        }
2882
 
}
2883
 
 
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);
2891
 
                }
2892
 
                if( i == n.length - 1 ) {
2893
 
                        this.write(null, ";");
2894
 
                } else {
2895
 
                        this.write(n[i], ",");
2896
 
                }
2897
 
        }
2898
 
}
2899
 
 
2900
 
strandsswp["write_;"] = function(n) {
2901
 
        if(!n.expression) 
2902
 
                return;
2903
 
        this.add(n.expression);
2904
 
        this.write(null, ";");
2905
 
}
2906
 
 
2907
 
 
2908
 
strandsswp.write_conditional = function(n) {
2909
 
        this.add(n[0]);
2910
 
        this.write(null, "?");
2911
 
        this.add(n[1]);
2912
 
        this.write(null, ":");
2913
 
        this.add(n[2]);
2914
 
}
2915
 
 
2916
 
strandsswp["write_++"] = function(n) {
2917
 
        if( n.postfix ) {
2918
 
                this.add(n[0]);
2919
 
                this.write(n, "++");
2920
 
        } else {
2921
 
                this.write(n, "++");
2922
 
                this.add(n[0]);
2923
 
        }
2924
 
}
2925
 
 
2926
 
strandsswp["write_--"] = function(n) {
2927
 
        if( n.postfix ) {
2928
 
                this.add(n[0]);
2929
 
                this.write(n, "--");
2930
 
        } else {
2931
 
                this.write(n, "--");
2932
 
                this.add(n[0]);
2933
 
        }
2934
 
}
2935
 
 
2936
 
strandsswp.write_index = function(n) {
2937
 
        this.add(n[0]);
2938
 
        this.write(null, '[');
2939
 
        this.add(n[1]);
2940
 
        this.write(null, ']');
2941
 
}
2942
 
 
2943
 
strandsswp.write_array_init = function(n) {
2944
 
        this.write(n, '[');
2945
 
        for( var i = 0; i < n.length; i++ ) {
2946
 
                if (i > 0) {
2947
 
                        this.write(null, ",");
2948
 
                }
2949
 
                this.add(n[i]);
2950
 
        }
2951
 
        this.write(null, ']');
2952
 
}
2953
 
 
2954
 
strandsswp.write_object_init = function(n) {
2955
 
        this.write(n, '{');
2956
 
        for(var i = 0; i < n.length; i++) {
2957
 
                this.add(n[i]);
2958
 
                if( i != n.length - 1 ) {
2959
 
                        this.write(n[i], ',');
2960
 
                }
2961
 
        }
2962
 
        this.write(null, '}');
2963
 
}
2964
 
 
2965
 
strandsswp.write_property_init = function(n) {
2966
 
        this.add(n[0]);
2967
 
        this.write(n[0], ':');
2968
 
        this.add(n[1]);
2969
 
}
2970
 
 
2971
 
strandsswp.write_block = function(n) {
2972
 
        this.write(n, '{');
2973
 
        for( var i = 0; i < n.length; i++ ) {
2974
 
                this.add(n[i]);
2975
 
        }
2976
 
        this.write(null, "}");
2977
 
}
2978
 
 
2979
 
strandsswp.write_group = function(n) {
2980
 
        this.write(n, '(');
2981
 
        for( var i = 0; i < n.length; i++ ) {
2982
 
                this.add(n[i]);
2983
 
        }
2984
 
        this.write(null, ")");
2985
 
}
2986
 
 
2987
 
strandsswp.write_list = function(n) {
2988
 
        this.write(null, '(');
2989
 
        for( var i = 0; i < n.length; i++ ) {
2990
 
                this.add(n[i]);
2991
 
                if( i != n.length - 1 ) {
2992
 
                        this.write(null, ",");
2993
 
                }
2994
 
        }
2995
 
        this.write(n, ')');
2996
 
}
2997
 
 
2998
 
strandsswp.write_label = function(n) {
2999
 
        this.write(n, n.label);
3000
 
        this.write(n, ":");
3001
 
        this.add(n.statement);
3002
 
}
3003
 
 
3004
 
strandsswp.write_for = function(n) {
3005
 
        this.write(n, "for(");
3006
 
        this.add(n.setup);
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, ";");
3013
 
        this.add(n.update);
3014
 
        this.write(null, ")");
3015
 
        this.add(n.body);
3016
 
}
3017
 
 
3018
 
strandsswp.write_call = function(n) {
3019
 
        this.add(n[0]);
3020
 
        this.add(n[1]);
3021
 
}
3022
 
 
3023
 
strandsswp.write_new_with_args = function(n) {
3024
 
        this.write(n, "new ");
3025
 
        this.add(n[0]);
3026
 
        if (n[1])
3027
 
                this.add(n[1]);
3028
 
}
3029
 
 
3030
 
strandsswp.write_new = function(n) {
3031
 
        this.write(n, "new ");
3032
 
        this.add(n[0]);
3033
 
        this.write(null, "()");
3034
 
}
3035
 
 
3036
 
strandsswp.write_string = function(n) {
3037
 
        var value = n.value.replace(/(\\|")/g, "\\$1");
3038
 
        value = value.replace(/\n/g, "\\n");
3039
 
        this.write(n, '"');
3040
 
        this.write(n, value);
3041
 
        this.write(n, '"');
3042
 
}
3043
 
 
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]);
3050
 
        }
3051
 
        this.write(null, "}");
3052
 
}
3053
 
 
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);
3059
 
}
3060
 
 
3061
 
strandsswp.write_default = function(n) {
3062
 
        this.write(n, "default:");
3063
 
        this.add(n.statements);
3064
 
}
3065
 
 
3066
 
strandsswp.write_delete = function(n) {
3067
 
        this.write(n, "delete ");
3068
 
        for( var i = 0; i < n.length; i++ ) {
3069
 
                this.add(n[i]);
3070
 
        }
3071
 
}
3072
 
 
3073
 
strandsswp.write_while = function(n) {
3074
 
        this.write(n, "while(");
3075
 
        this.add(n.condition);
3076
 
        this.write(null, ")");
3077
 
        this.add(n.body);
3078
 
}
3079
 
 
3080
 
strandsswp.write_do = function(n) {
3081
 
        this.write(n, "do");
3082
 
        this.add(n.body);
3083
 
        this.write(n.condition, " while(");
3084
 
        this.add(n.condition);
3085
 
        this.write(null, ");");
3086
 
}
3087
 
 
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);
3096
 
        }
3097
 
}
3098
 
 
3099
 
strandsswp.write_typeof = function(n) {
3100
 
        this.write(n, "typeof ");
3101
 
        this.add(n[0]);
3102
 
}
3103
 
strandsswp.write_instanceof = function(n) {
3104
 
        this.add(n[0]);
3105
 
        this.write(n, " instanceof ");
3106
 
        this.add(n[1]);
3107
 
}
3108
 
 
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);
3116
 
                if (clause.guard) {
3117
 
                        this.write(null, " if(");
3118
 
                        this.add(clause.guard);
3119
 
                        this.write(null, ")");
3120
 
                }
3121
 
                this.write(null, ")");
3122
 
                this.add(clause.block);
3123
 
        }
3124
 
        if( n.finallyBlock != null ) {
3125
 
                this.write(n.finallyBlock, " finally ");
3126
 
                this.add(n.finallyBlock);
3127
 
        }
3128
 
}
3129
 
 
3130
 
strandsswp.write_throw = function(n) {
3131
 
        this.write(n, "throw(");
3132
 
        this.add(n.exception);
3133
 
        this.write(n, ");");
3134
 
}
3135
 
 
3136
 
strandsswp.write_for_in = function(n) {
3137
 
        this.write(n, "for(");
3138
 
        if( n.varDecl == null ) {
3139
 
                this.add(n.iterator);
3140
 
        } else {
3141
 
                this.add(n.varDecl);
3142
 
                // variable writes automatically add a semicolon,
3143
 
                // we need to remove it.
3144
 
                this.pop();
3145
 
        }
3146
 
        this.write(null, " in ");
3147
 
        this.add(n.object);
3148
 
        this.write(null, ")");
3149
 
        this.add(n.body);
3150
 
}
3151
 
 
3152
 
strandsswp.write_with = function(n) {
3153
 
        this.write(n, "with(");
3154
 
        this.add(n.object);
3155
 
        this.write(null, ")");
3156
 
        this.add(n.body);
3157
 
}
3158
 
 
3159
 
strandsswp.write_void = function(n) {
3160
 
        this.write(n, "void ");
3161
 
        this.add(n[0]);
3162
 
}
3163
 
 
3164
 
strandsswp.write_break = function(n) {
3165
 
        this.write(n, "break;");
3166
 
}
3167
 
 
3168
 
strandsswp.write_continue = function(n) {
3169
 
        this.write(n, "continue;");
3170
 
}
3171
 
 
3172
 
strandsswp.write_debugger = function(n) {
3173
 
        this.write(n, "debugger;");
3174
 
}
3175
 
 
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);
3181
 
        }
3182
 
        this.write(null, ";");
3183
 
}
3184
 
 
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);
3191
 
}
3192
 
strands.compiler.Function = function(source,thisObject,scopeObject,runAt) {
3193
 
        with(_frm(this,arguments,['source','thisObject','scopeObject','runAt'],[])) {
3194
 
                if (!source)
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") {
3200
 
                        func = function() {
3201
 
                                persevere.serverCall(this,arguments);
3202
 
                        }
3203
 
                        func._psv15 = func.toString();
3204
 
                }
3205
 
                else
3206
 
                        func._psv15 = code.substring(5,code.length-3);
3207
 
                func['function'] = source;
3208
 
                if (runAt)
3209
 
                        func.runAt = runAt;
3210
 
                return func;
3211
 
                }
3212
 
}
3213
 
 
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");
3222
 
                }
3223
 
                var xhr = getXMLHttpRequest();
3224
 
                frame.future = new Future();
3225
 
                var ajaxDataReader = function () {
3226
 
                        if (xhr.readyState == 4) {
3227
 
                        // only if "OK"
3228
 
                        var loaded;
3229
 
                        try {
3230
 
                                var status = xhr.status;
3231
 
                                loaded = xhr.responseText.length > 0;//firefox can throw an exception right here
3232
 
                        } catch(e) {}
3233
 
                        if (loaded) 
3234
 
                                frame.future.fulfill(xhr.responseText);                         
3235
 
                                else
3236
 
                                        frame.future.interrupt();
3237
 
                        xhr = null; // This is to correct for IE memory leak
3238
 
                        }
3239
 
                }
3240
 
                frame._cp = 1;
3241
 
            xhr.open(method || "POST", url, true); 
3242
 
                xhr.onreadystatechange = ajaxDataReader;
3243
 
            xhr.send(postData);
3244
 
        }
3245
 
        var result = frame.future.result();
3246
 
        if (result == frame._S) frame._s();
3247
 
        return result;
3248
 
}
3249
 
strands.compiler.tryCatchCompile = function(source,name,persistence,debug) {
3250
 
        try {
3251
 
                this.options.persistence = persistence;
3252
 
                this.options.debug = debug;
3253
 
                return this.compile(source);
3254
 
        }
3255
 
        catch (e) {
3256
 
                return "alert('ERROR in " + name.replace(/'/g,'') + ": line " + e.lineNumber + ": " + e.message + "');";
3257
 
        }
3258
 
}
3259