browsercms 3.1.4 → 3.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (164) hide show
  1. data/app/controllers/cms/content_block_controller.rb +2 -2
  2. data/app/controllers/cms/section_nodes_controller.rb +6 -1
  3. data/app/controllers/cms/sections_controller.rb +1 -1
  4. data/app/helpers/cms/application_helper.rb +1 -1
  5. data/app/helpers/cms/content_block_helper.rb +27 -0
  6. data/app/helpers/cms/section_nodes_helper.rb +43 -5
  7. data/app/models/abstract_file_block.rb +16 -1
  8. data/app/models/attachment.rb +17 -35
  9. data/app/models/file_block.rb +0 -12
  10. data/app/models/image_block.rb +0 -12
  11. data/app/models/link.rb +4 -21
  12. data/app/models/page.rb +31 -34
  13. data/app/models/section.rb +82 -44
  14. data/app/models/section_node.rb +39 -24
  15. data/app/models/user.rb +5 -0
  16. data/app/views/cms/blocks/index.html.erb +4 -4
  17. data/app/views/cms/file_blocks/_form.html.erb +1 -1
  18. data/app/views/cms/image_blocks/_form.html.erb +1 -1
  19. data/app/views/cms/section_nodes/_link.html.erb +6 -3
  20. data/app/views/cms/section_nodes/_node.html.erb +11 -1
  21. data/app/views/cms/section_nodes/_page.html.erb +13 -7
  22. data/app/views/cms/section_nodes/_section.html.erb +24 -8
  23. data/app/views/cms/section_nodes/index.html.erb +28 -16
  24. data/app/views/layouts/templates/default.html.erb +17 -0
  25. data/browsercms.gemspec +28 -1413
  26. data/db/migrate/20120117144039_browsercms315.rb +94 -0
  27. data/db/migrate/{20081114172307_load_seed_data.rb → 20121114172307_load_seeds.rb} +8 -1
  28. data/lib/acts_as_list.rb +1 -1
  29. data/lib/browsercms.rb +2 -0
  30. data/lib/cms/addressable.rb +83 -0
  31. data/lib/cms/behaviors/attaching.rb +44 -24
  32. data/lib/cms/behaviors/connecting.rb +2 -1
  33. data/lib/cms/behaviors/publishing.rb +12 -3
  34. data/lib/cms/behaviors/versioning.rb +83 -53
  35. data/lib/cms/content_rendering_support.rb +3 -3
  36. data/lib/cms/error_pages.rb +8 -0
  37. data/lib/cms/init.rb +5 -3
  38. data/lib/cms/version.rb +1 -1
  39. data/templates/blank.rb +2 -0
  40. data/templates/demo.rb +2 -0
  41. data/templates/module.rb +2 -0
  42. data/test/custom_assertions.rb +7 -1
  43. data/test/factories.rb +3 -1
  44. data/test/factories/sitemap_factories.rb +28 -0
  45. data/test/fixtures/connectors.yml +97 -0
  46. data/test/fixtures/content_type_groups.yml +13 -0
  47. data/test/fixtures/content_types.yml +50 -0
  48. data/test/fixtures/dynamic_view_versions.yml +26 -0
  49. data/test/fixtures/dynamic_views.yml +26 -0
  50. data/test/fixtures/group_permissions.yml +16 -0
  51. data/test/fixtures/group_sections.yml +31 -0
  52. data/test/fixtures/group_type_permissions.yml +11 -0
  53. data/test/fixtures/group_types.yml +25 -0
  54. data/test/fixtures/groups.yml +25 -0
  55. data/test/fixtures/html_block_versions.yml +67 -0
  56. data/test/fixtures/html_blocks.yml +63 -0
  57. data/test/fixtures/page_versions.yml +265 -0
  58. data/test/fixtures/pages.yml +85 -0
  59. data/test/fixtures/permissions.yml +28 -0
  60. data/test/fixtures/section_nodes.yml +46 -0
  61. data/test/fixtures/sections.yml +19 -0
  62. data/test/fixtures/sites.yml +9 -0
  63. data/test/fixtures/user_group_memberships.yml +11 -0
  64. data/test/fixtures/users.yml +15 -0
  65. data/test/functional/cms/content_controller_test.rb +6 -1
  66. data/test/functional/cms/file_blocks_controller_test.rb +1 -0
  67. data/test/functional/cms/html_blocks_controller_test.rb +1 -0
  68. data/test/functional/cms/image_blocks_controller_test.rb +39 -32
  69. data/test/functional/cms/section_nodes_controller_test.rb +48 -20
  70. data/test/functional/cms/sections_controller_test.rb +3 -1
  71. data/test/functional/tests/pretend_controller_test.rb +6 -3
  72. data/test/integration/cms/ckeditor_test.rb +5 -2
  73. data/test/integration/sitemap_performance_test.rb +26 -0
  74. data/test/selenium-core/Blank.html +7 -0
  75. data/test/selenium-core/InjectedRemoteRunner.html +8 -0
  76. data/test/selenium-core/RemoteRunner.html +110 -0
  77. data/test/selenium-core/SeleniumLog.html +109 -0
  78. data/test/selenium-core/TestPrompt.html +145 -0
  79. data/test/selenium-core/TestRunner-splash.html +55 -0
  80. data/test/selenium-core/TestRunner.hta +176 -0
  81. data/test/selenium-core/TestRunner.html +176 -0
  82. data/test/selenium-core/domviewer/butmin.gif +0 -0
  83. data/test/selenium-core/domviewer/butplus.gif +0 -0
  84. data/test/selenium-core/domviewer/domviewer.css +298 -0
  85. data/test/selenium-core/domviewer/domviewer.html +16 -0
  86. data/test/selenium-core/domviewer/selenium-domviewer.js +205 -0
  87. data/test/selenium-core/icons/all.png +0 -0
  88. data/test/selenium-core/icons/continue.png +0 -0
  89. data/test/selenium-core/icons/continue_disabled.png +0 -0
  90. data/test/selenium-core/icons/pause.png +0 -0
  91. data/test/selenium-core/icons/pause_disabled.png +0 -0
  92. data/test/selenium-core/icons/selected.png +0 -0
  93. data/test/selenium-core/icons/step.png +0 -0
  94. data/test/selenium-core/icons/step_disabled.png +0 -0
  95. data/test/selenium-core/iedoc-core.xml +1515 -0
  96. data/test/selenium-core/iedoc.xml +1469 -0
  97. data/test/selenium-core/lib/cssQuery/cssQuery-p.js +6 -0
  98. data/test/selenium-core/lib/cssQuery/src/cssQuery-level2.js +142 -0
  99. data/test/selenium-core/lib/cssQuery/src/cssQuery-level3.js +150 -0
  100. data/test/selenium-core/lib/cssQuery/src/cssQuery-standard.js +53 -0
  101. data/test/selenium-core/lib/cssQuery/src/cssQuery.js +356 -0
  102. data/test/selenium-core/lib/prototype.js +2006 -0
  103. data/test/selenium-core/lib/scriptaculous/builder.js +101 -0
  104. data/test/selenium-core/lib/scriptaculous/controls.js +815 -0
  105. data/test/selenium-core/lib/scriptaculous/dragdrop.js +915 -0
  106. data/test/selenium-core/lib/scriptaculous/effects.js +958 -0
  107. data/test/selenium-core/lib/scriptaculous/scriptaculous.js +47 -0
  108. data/test/selenium-core/lib/scriptaculous/slider.js +283 -0
  109. data/test/selenium-core/lib/scriptaculous/unittest.js +383 -0
  110. data/test/selenium-core/scripts/find_matching_child.js +69 -0
  111. data/test/selenium-core/scripts/htmlutils.js +894 -0
  112. data/test/selenium-core/scripts/injection.html +72 -0
  113. data/test/selenium-core/scripts/js2html.js +70 -0
  114. data/test/selenium-core/scripts/narcissus-defs.js +175 -0
  115. data/test/selenium-core/scripts/narcissus-exec.js +1054 -0
  116. data/test/selenium-core/scripts/narcissus-parse.js +1003 -0
  117. data/test/selenium-core/scripts/se2html.js +63 -0
  118. data/test/selenium-core/scripts/selenium-api.js +2409 -0
  119. data/test/selenium-core/scripts/selenium-browserbot.js +2203 -0
  120. data/test/selenium-core/scripts/selenium-browserdetect.js +150 -0
  121. data/test/selenium-core/scripts/selenium-commandhandlers.js +377 -0
  122. data/test/selenium-core/scripts/selenium-executionloop.js +175 -0
  123. data/test/selenium-core/scripts/selenium-logging.js +147 -0
  124. data/test/selenium-core/scripts/selenium-remoterunner.js +571 -0
  125. data/test/selenium-core/scripts/selenium-testrunner.js +1333 -0
  126. data/test/selenium-core/scripts/selenium-version.js +5 -0
  127. data/test/selenium-core/scripts/user-extensions.js +3 -0
  128. data/test/selenium-core/scripts/user-extensions.js.sample +75 -0
  129. data/test/selenium-core/scripts/xmlextras.js +153 -0
  130. data/test/selenium-core/selenium-logo.png +0 -0
  131. data/test/selenium-core/selenium-test.css +43 -0
  132. data/test/selenium-core/selenium.css +299 -0
  133. data/test/selenium-core/xpath/dom.js +428 -0
  134. data/test/selenium-core/xpath/misc.js +252 -0
  135. data/test/selenium-core/xpath/xpath.js +2223 -0
  136. data/test/selenium/_login_as_cmsadmin.rsel +4 -0
  137. data/test/selenium/dashboard.rsel +5 -0
  138. data/test/selenium/html_blocks.rsel +4 -0
  139. data/test/selenium/login/failed_login.rsel +8 -0
  140. data/test/selenium/login/successful_login.rsel +9 -0
  141. data/test/selenium/page_templates.rsel +12 -0
  142. data/test/selenium/pages/edit_properties.rsel +5 -0
  143. data/test/selenium/site/view_home_page.rsel +4 -0
  144. data/test/selenium/sitemap/move_page.rsel +9 -0
  145. data/test/selenium/sitemap/open_section.rsel +6 -0
  146. data/test/selenium/sitemap/select_page.rsel +12 -0
  147. data/test/selenium/sitemap/select_section.rsel +17 -0
  148. data/test/test_helper.rb +30 -12
  149. data/test/unit/behaviors/attaching_test.rb +4 -6
  150. data/test/unit/behaviors/connectable_test.rb +29 -0
  151. data/test/unit/behaviors/publishable_test.rb +40 -9
  152. data/test/unit/behaviors/versioning_test.rb +36 -0
  153. data/test/unit/helpers/menu_helper_test.rb +5 -2
  154. data/test/unit/helpers/page_helper_test.rb +2 -0
  155. data/test/unit/lib/cms/sitemap_test.rb +206 -0
  156. data/test/unit/models/attachment_test.rb +51 -31
  157. data/test/unit/models/file_block_test.rb +74 -55
  158. data/test/unit/models/link_test.rb +44 -0
  159. data/test/unit/models/page_test.rb +290 -224
  160. data/test/unit/models/sections_test.rb +144 -44
  161. data/test/unit/models/user_test.rb +28 -18
  162. metadata +581 -350
  163. data/app/views/cms/section_nodes/_section_node.html.erb +0 -10
  164. data/test/unit/models/section_node_test.rb +0 -92
@@ -0,0 +1,1003 @@
1
+ /* ***** BEGIN LICENSE BLOCK *****
2
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3
+ *
4
+ * The contents of this file are subject to the Mozilla Public License Version
5
+ * 1.1 (the "License"); you may not use this file except in compliance with
6
+ * the License. You may obtain a copy of the License at
7
+ * http://www.mozilla.org/MPL/
8
+ *
9
+ * Software distributed under the License is distributed on an "AS IS" basis,
10
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11
+ * for the specific language governing rights and limitations under the
12
+ * License.
13
+ *
14
+ * The Original Code is the Narcissus JavaScript engine.
15
+ *
16
+ * The Initial Developer of the Original Code is
17
+ * Brendan Eich <brendan@mozilla.org>.
18
+ * Portions created by the Initial Developer are Copyright (C) 2004
19
+ * the Initial Developer. All Rights Reserved.
20
+ *
21
+ * Contributor(s): Richard Hundt <www.plextk.org>
22
+ *
23
+ * Alternatively, the contents of this file may be used under the terms of
24
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
25
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26
+ * in which case the provisions of the GPL or the LGPL are applicable instead
27
+ * of those above. If you wish to allow use of your version of this file only
28
+ * under the terms of either the GPL or the LGPL, and not to allow others to
29
+ * use your version of this file under the terms of the MPL, indicate your
30
+ * decision by deleting the provisions above and replace them with the notice
31
+ * and other provisions required by the GPL or the LGPL. If you do not delete
32
+ * the provisions above, a recipient may use your version of this file under
33
+ * the terms of any one of the MPL, the GPL or the LGPL.
34
+ *
35
+ * ***** END LICENSE BLOCK ***** */
36
+
37
+ /*
38
+ * Narcissus - JS implemented in JS.
39
+ *
40
+ * Lexical scanner and parser.
41
+ */
42
+
43
+ // jrh
44
+ //module('JS.Parse');
45
+
46
+ // Build a regexp that recognizes operators and punctuators (except newline).
47
+ var opRegExp =
48
+ /^;|^,|^\?|^:|^\|\||^\&\&|^\||^\^|^\&|^===|^==|^=|^!==|^!=|^<<|^<=|^<|^>>>|^>>|^>=|^>|^\+\+|^\-\-|^\+|^\-|^\*|^\/|^%|^!|^~|^\.|^\[|^\]|^\{|^\}|^\(|^\)/;
49
+
50
+ // A regexp to match floating point literals (but not integer literals).
51
+ var fpRegExp = /^\d+\.\d*(?:[eE][-+]?\d+)?|^\d+(?:\.\d*)?[eE][-+]?\d+|^\.\d+(?:[eE][-+]?\d+)?/;
52
+
53
+ function Tokenizer(s, f, l) {
54
+ this.cursor = 0;
55
+ this.source = String(s);
56
+ this.tokens = [];
57
+ this.tokenIndex = 0;
58
+ this.lookahead = 0;
59
+ this.scanNewlines = false;
60
+ this.scanOperand = true;
61
+ this.filename = f || "";
62
+ this.lineno = l || 1;
63
+ }
64
+
65
+ Tokenizer.prototype = {
66
+ input : function() {
67
+ return this.source.substring(this.cursor);
68
+ },
69
+
70
+ done : function() {
71
+ return this.peek() == END;
72
+ },
73
+
74
+ token : function() {
75
+ return this.tokens[this.tokenIndex];
76
+ },
77
+
78
+ match: function (tt) {
79
+ return this.get() == tt || this.unget();
80
+ },
81
+
82
+ mustMatch: function (tt) {
83
+ if (!this.match(tt))
84
+ throw this.newSyntaxError("Missing " + this.tokens[tt].toLowerCase());
85
+ return this.token();
86
+ },
87
+
88
+ peek: function () {
89
+ var tt;
90
+ if (this.lookahead) {
91
+ tt = this.tokens[(this.tokenIndex + this.lookahead) & 3].type;
92
+ } else {
93
+ tt = this.get();
94
+ this.unget();
95
+ }
96
+ return tt;
97
+ },
98
+
99
+ peekOnSameLine: function () {
100
+ this.scanNewlines = true;
101
+ var tt = this.peek();
102
+ this.scanNewlines = false;
103
+ return tt;
104
+ },
105
+
106
+ get: function () {
107
+ var token;
108
+ while (this.lookahead) {
109
+ --this.lookahead;
110
+ this.tokenIndex = (this.tokenIndex + 1) & 3;
111
+ token = this.tokens[this.tokenIndex];
112
+ if (token.type != NEWLINE || this.scanNewlines)
113
+ return token.type;
114
+ }
115
+
116
+ for (;;) {
117
+ var input = this.input();
118
+ var rx = this.scanNewlines ? /^[ \t]+/ : /^\s+/;
119
+ var match = input.match(rx);
120
+ if (match) {
121
+ var spaces = match[0];
122
+ this.cursor += spaces.length;
123
+ var newlines = spaces.match(/\n/g);
124
+ if (newlines)
125
+ this.lineno += newlines.length;
126
+ input = this.input();
127
+ }
128
+
129
+ if (!(match = input.match(/^\/(?:\*(?:.|\n)*?\*\/|\/.*)/)))
130
+ break;
131
+ var comment = match[0];
132
+ this.cursor += comment.length;
133
+ newlines = comment.match(/\n/g);
134
+ if (newlines)
135
+ this.lineno += newlines.length
136
+ }
137
+
138
+ this.tokenIndex = (this.tokenIndex + 1) & 3;
139
+ token = this.tokens[this.tokenIndex];
140
+ if (!token)
141
+ this.tokens[this.tokenIndex] = token = {};
142
+ if (!input)
143
+ return token.type = END;
144
+ if ((match = input.match(fpRegExp))) {
145
+ token.type = NUMBER;
146
+ token.value = parseFloat(match[0]);
147
+ } else if ((match = input.match(/^0[xX][\da-fA-F]+|^0[0-7]*|^\d+/))) {
148
+ token.type = NUMBER;
149
+ token.value = parseInt(match[0]);
150
+ } else if ((match = input.match(/^((\$\w*)|(\w+))/))) {
151
+ var id = match[0];
152
+ token.type = keywords[id] || IDENTIFIER;
153
+ token.value = id;
154
+ } else if ((match = input.match(/^"(?:\\.|[^"])*"|^'(?:[^']|\\.)*'/))) {
155
+ token.type = STRING;
156
+ token.value = eval(match[0]);
157
+ } else if (this.scanOperand &&
158
+ (match = input.match(/^\/((?:\\.|[^\/])+)\/([gi]*)/))) {
159
+ token.type = REGEXP;
160
+ token.value = new RegExp(match[1], match[2]);
161
+ } else if ((match = input.match(opRegExp))) {
162
+ var op = match[0];
163
+ if (assignOps[op] && input[op.length] == '=') {
164
+ token.type = ASSIGN;
165
+ token.assignOp = GLOBAL[opTypeNames[op]];
166
+ match[0] += '=';
167
+ } else {
168
+ token.type = GLOBAL[opTypeNames[op]];
169
+ if (this.scanOperand &&
170
+ (token.type == PLUS || token.type == MINUS)) {
171
+ token.type += UNARY_PLUS - PLUS;
172
+ }
173
+ token.assignOp = null;
174
+ }
175
+ //debug('token.value => '+op+', token.type => '+token.type);
176
+ token.value = op;
177
+ } else {
178
+ throw this.newSyntaxError("Illegal token");
179
+ }
180
+
181
+ token.start = this.cursor;
182
+ this.cursor += match[0].length;
183
+ token.end = this.cursor;
184
+ token.lineno = this.lineno;
185
+ return token.type;
186
+ },
187
+
188
+ unget: function () {
189
+ if (++this.lookahead == 4) throw "PANIC: too much lookahead!";
190
+ this.tokenIndex = (this.tokenIndex - 1) & 3;
191
+ },
192
+
193
+ newSyntaxError: function (m) {
194
+ var e = new SyntaxError(m, this.filename, this.lineno);
195
+ e.source = this.source;
196
+ e.cursor = this.cursor;
197
+ return e;
198
+ }
199
+ };
200
+
201
+ function CompilerContext(inFunction) {
202
+ this.inFunction = inFunction;
203
+ this.stmtStack = [];
204
+ this.funDecls = [];
205
+ this.varDecls = [];
206
+ }
207
+
208
+ var CCp = CompilerContext.prototype;
209
+ CCp.bracketLevel = CCp.curlyLevel = CCp.parenLevel = CCp.hookLevel = 0;
210
+ CCp.ecmaStrictMode = CCp.inForLoopInit = false;
211
+
212
+ function Script(t, x) {
213
+ var n = Statements(t, x);
214
+ n.type = SCRIPT;
215
+ n.funDecls = x.funDecls;
216
+ n.varDecls = x.varDecls;
217
+ return n;
218
+ }
219
+
220
+ // Node extends Array, which we extend slightly with a top-of-stack method.
221
+ Array.prototype.top = function() {
222
+ return this.length && this[this.length-1];
223
+ }
224
+
225
+ function NarcNode(t, type) {
226
+ var token = t.token();
227
+ if (token) {
228
+ this.type = type || token.type;
229
+ this.value = token.value;
230
+ this.lineno = token.lineno;
231
+ this.start = token.start;
232
+ this.end = token.end;
233
+ } else {
234
+ this.type = type;
235
+ this.lineno = t.lineno;
236
+ }
237
+ this.tokenizer = t;
238
+ for (var i = 2; i < arguments.length; i++)
239
+ this.push(arguments[i]);
240
+ }
241
+
242
+ var Np = NarcNode.prototype = new Array();
243
+ Np.constructor = NarcNode;
244
+ Np.$length = 0;
245
+ Np.toSource = Object.prototype.toSource;
246
+
247
+ // Always use push to add operands to an expression, to update start and end.
248
+ Np.push = function (kid) {
249
+ if (kid.start < this.start)
250
+ this.start = kid.start;
251
+ if (this.end < kid.end)
252
+ this.end = kid.end;
253
+ //debug('length before => '+this.$length);
254
+ this[this.$length] = kid;
255
+ this.$length++;
256
+ //debug('length after => '+this.$length);
257
+ }
258
+
259
+ NarcNode.indentLevel = 0;
260
+
261
+ function tokenstr(tt) {
262
+ var t = tokens[tt];
263
+ return /^\W/.test(t) ? opTypeNames[t] : t.toUpperCase();
264
+ }
265
+
266
+ Np.toString = function () {
267
+ var a = [];
268
+ for (var i in this) {
269
+ if (this.hasOwnProperty(i) && i != 'type')
270
+ a.push({id: i, value: this[i]});
271
+ }
272
+ a.sort(function (a,b) { return (a.id < b.id) ? -1 : 1; });
273
+ INDENTATION = " ";
274
+ var n = ++NarcNode.indentLevel;
275
+ var s = "{\n" + INDENTATION.repeat(n) + "type: " + tokenstr(this.type);
276
+ for (i = 0; i < a.length; i++)
277
+ s += ",\n" + INDENTATION.repeat(n) + a[i].id + ": " + a[i].value;
278
+ n = --NarcNode.indentLevel;
279
+ s += "\n" + INDENTATION.repeat(n) + "}";
280
+ return s;
281
+ }
282
+
283
+ Np.getSource = function () {
284
+ return this.tokenizer.source.slice(this.start, this.end);
285
+ };
286
+
287
+ Np.filename = function () { return this.tokenizer.filename; };
288
+
289
+ String.prototype.repeat = function (n) {
290
+ var s = "", t = this + s;
291
+ while (--n >= 0)
292
+ s += t;
293
+ return s;
294
+ }
295
+
296
+ // Statement stack and nested statement handler.
297
+ function nest(t, x, node, func, end) {
298
+ x.stmtStack.push(node);
299
+ var n = func(t, x);
300
+ x.stmtStack.pop();
301
+ end && t.mustMatch(end);
302
+ return n;
303
+ }
304
+
305
+ function Statements(t, x) {
306
+ var n = new NarcNode(t, BLOCK);
307
+ x.stmtStack.push(n);
308
+ while (!t.done() && t.peek() != RIGHT_CURLY)
309
+ n.push(Statement(t, x));
310
+ x.stmtStack.pop();
311
+ return n;
312
+ }
313
+
314
+ function Block(t, x) {
315
+ t.mustMatch(LEFT_CURLY);
316
+ var n = Statements(t, x);
317
+ t.mustMatch(RIGHT_CURLY);
318
+ return n;
319
+ }
320
+
321
+ DECLARED_FORM = 0; EXPRESSED_FORM = 1; STATEMENT_FORM = 2;
322
+
323
+ function Statement(t, x) {
324
+ var i, label, n, n2, ss, tt = t.get();
325
+
326
+ // Cases for statements ending in a right curly return early, avoiding the
327
+ // common semicolon insertion magic after this switch.
328
+ switch (tt) {
329
+ case FUNCTION:
330
+ return FunctionDefinition(t, x, true,
331
+ (x.stmtStack.length > 1)
332
+ ? STATEMENT_FORM
333
+ : DECLARED_FORM);
334
+
335
+ case LEFT_CURLY:
336
+ n = Statements(t, x);
337
+ t.mustMatch(RIGHT_CURLY);
338
+ return n;
339
+
340
+ case IF:
341
+ n = new NarcNode(t);
342
+ n.condition = ParenExpression(t, x);
343
+ x.stmtStack.push(n);
344
+ n.thenPart = Statement(t, x);
345
+ n.elsePart = t.match(ELSE) ? Statement(t, x) : null;
346
+ x.stmtStack.pop();
347
+ return n;
348
+
349
+ case SWITCH:
350
+ n = new NarcNode(t);
351
+ t.mustMatch(LEFT_PAREN);
352
+ n.discriminant = Expression(t, x);
353
+ t.mustMatch(RIGHT_PAREN);
354
+ n.cases = [];
355
+ n.defaultIndex = -1;
356
+ x.stmtStack.push(n);
357
+ t.mustMatch(LEFT_CURLY);
358
+ while ((tt = t.get()) != RIGHT_CURLY) {
359
+ switch (tt) {
360
+ case DEFAULT:
361
+ if (n.defaultIndex >= 0)
362
+ throw t.newSyntaxError("More than one switch default");
363
+ // FALL THROUGH
364
+ case CASE:
365
+ n2 = new NarcNode(t);
366
+ if (tt == DEFAULT)
367
+ n.defaultIndex = n.cases.length;
368
+ else
369
+ n2.caseLabel = Expression(t, x, COLON);
370
+ break;
371
+ default:
372
+ throw t.newSyntaxError("Invalid switch case");
373
+ }
374
+ t.mustMatch(COLON);
375
+ n2.statements = new NarcNode(t, BLOCK);
376
+ while ((tt=t.peek()) != CASE && tt != DEFAULT && tt != RIGHT_CURLY)
377
+ n2.statements.push(Statement(t, x));
378
+ n.cases.push(n2);
379
+ }
380
+ x.stmtStack.pop();
381
+ return n;
382
+
383
+ case FOR:
384
+ n = new NarcNode(t);
385
+ n.isLoop = true;
386
+ t.mustMatch(LEFT_PAREN);
387
+ if ((tt = t.peek()) != SEMICOLON) {
388
+ x.inForLoopInit = true;
389
+ if (tt == VAR || tt == CONST) {
390
+ t.get();
391
+ n2 = Variables(t, x);
392
+ } else {
393
+ n2 = Expression(t, x);
394
+ }
395
+ x.inForLoopInit = false;
396
+ }
397
+ if (n2 && t.match(IN)) {
398
+ n.type = FOR_IN;
399
+ if (n2.type == VAR) {
400
+ if (n2.$length != 1) {
401
+ throw new SyntaxError("Invalid for..in left-hand side",
402
+ t.filename, n2.lineno);
403
+ }
404
+
405
+ // NB: n2[0].type == IDENTIFIER and n2[0].value == n2[0].name.
406
+ n.iterator = n2[0];
407
+ n.varDecl = n2;
408
+ } else {
409
+ n.iterator = n2;
410
+ n.varDecl = null;
411
+ }
412
+ n.object = Expression(t, x);
413
+ } else {
414
+ n.setup = n2 || null;
415
+ t.mustMatch(SEMICOLON);
416
+ n.condition = (t.peek() == SEMICOLON) ? null : Expression(t, x);
417
+ t.mustMatch(SEMICOLON);
418
+ n.update = (t.peek() == RIGHT_PAREN) ? null : Expression(t, x);
419
+ }
420
+ t.mustMatch(RIGHT_PAREN);
421
+ n.body = nest(t, x, n, Statement);
422
+ return n;
423
+
424
+ case WHILE:
425
+ n = new NarcNode(t);
426
+ n.isLoop = true;
427
+ n.condition = ParenExpression(t, x);
428
+ n.body = nest(t, x, n, Statement);
429
+ return n;
430
+
431
+ case DO:
432
+ n = new NarcNode(t);
433
+ n.isLoop = true;
434
+ n.body = nest(t, x, n, Statement, WHILE);
435
+ n.condition = ParenExpression(t, x);
436
+ if (!x.ecmaStrictMode) {
437
+ // <script language="JavaScript"> (without version hints) may need
438
+ // automatic semicolon insertion without a newline after do-while.
439
+ // See http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
440
+ t.match(SEMICOLON);
441
+ return n;
442
+ }
443
+ break;
444
+
445
+ case BREAK:
446
+ case CONTINUE:
447
+ n = new NarcNode(t);
448
+ if (t.peekOnSameLine() == IDENTIFIER) {
449
+ t.get();
450
+ n.label = t.token().value;
451
+ }
452
+ ss = x.stmtStack;
453
+ i = ss.length;
454
+ label = n.label;
455
+ if (label) {
456
+ do {
457
+ if (--i < 0)
458
+ throw t.newSyntaxError("Label not found");
459
+ } while (ss[i].label != label);
460
+ } else {
461
+ do {
462
+ if (--i < 0) {
463
+ throw t.newSyntaxError("Invalid " + ((tt == BREAK)
464
+ ? "break"
465
+ : "continue"));
466
+ }
467
+ } while (!ss[i].isLoop && (tt != BREAK || ss[i].type != SWITCH));
468
+ }
469
+ n.target = ss[i];
470
+ break;
471
+
472
+ case TRY:
473
+ n = new NarcNode(t);
474
+ n.tryBlock = Block(t, x);
475
+ n.catchClauses = [];
476
+ while (t.match(CATCH)) {
477
+ n2 = new NarcNode(t);
478
+ t.mustMatch(LEFT_PAREN);
479
+ n2.varName = t.mustMatch(IDENTIFIER).value;
480
+ if (t.match(IF)) {
481
+ if (x.ecmaStrictMode)
482
+ throw t.newSyntaxError("Illegal catch guard");
483
+ if (n.catchClauses.length && !n.catchClauses.top().guard)
484
+ throw t.newSyntaxError("Guarded catch after unguarded");
485
+ n2.guard = Expression(t, x);
486
+ } else {
487
+ n2.guard = null;
488
+ }
489
+ t.mustMatch(RIGHT_PAREN);
490
+ n2.block = Block(t, x);
491
+ n.catchClauses.push(n2);
492
+ }
493
+ if (t.match(FINALLY))
494
+ n.finallyBlock = Block(t, x);
495
+ if (!n.catchClauses.length && !n.finallyBlock)
496
+ throw t.newSyntaxError("Invalid try statement");
497
+ return n;
498
+
499
+ case CATCH:
500
+ case FINALLY:
501
+ throw t.newSyntaxError(tokens[tt] + " without preceding try");
502
+
503
+ case THROW:
504
+ n = new NarcNode(t);
505
+ n.exception = Expression(t, x);
506
+ break;
507
+
508
+ case RETURN:
509
+ if (!x.inFunction)
510
+ throw t.newSyntaxError("Invalid return");
511
+ n = new NarcNode(t);
512
+ tt = t.peekOnSameLine();
513
+ if (tt != END && tt != NEWLINE && tt != SEMICOLON && tt != RIGHT_CURLY)
514
+ n.value = Expression(t, x);
515
+ break;
516
+
517
+ case WITH:
518
+ n = new NarcNode(t);
519
+ n.object = ParenExpression(t, x);
520
+ n.body = nest(t, x, n, Statement);
521
+ return n;
522
+
523
+ case VAR:
524
+ case CONST:
525
+ n = Variables(t, x);
526
+ break;
527
+
528
+ case DEBUGGER:
529
+ n = new NarcNode(t);
530
+ break;
531
+
532
+ case REQUIRE:
533
+ n = new NarcNode(t);
534
+ n.classPath = ParenExpression(t, x);
535
+ break;
536
+
537
+ case NEWLINE:
538
+ case SEMICOLON:
539
+ n = new NarcNode(t, SEMICOLON);
540
+ n.expression = null;
541
+ return n;
542
+
543
+ default:
544
+ if (tt == IDENTIFIER && t.peek() == COLON) {
545
+ label = t.token().value;
546
+ ss = x.stmtStack;
547
+ for (i = ss.length-1; i >= 0; --i) {
548
+ if (ss[i].label == label)
549
+ throw t.newSyntaxError("Duplicate label");
550
+ }
551
+ t.get();
552
+ n = new NarcNode(t, LABEL);
553
+ n.label = label;
554
+ n.statement = nest(t, x, n, Statement);
555
+ return n;
556
+ }
557
+
558
+ n = new NarcNode(t, SEMICOLON);
559
+ t.unget();
560
+ n.expression = Expression(t, x);
561
+ n.end = n.expression.end;
562
+ break;
563
+ }
564
+
565
+ if (t.lineno == t.token().lineno) {
566
+ tt = t.peekOnSameLine();
567
+ if (tt != END && tt != NEWLINE && tt != SEMICOLON && tt != RIGHT_CURLY)
568
+ throw t.newSyntaxError("Missing ; before statement");
569
+ }
570
+ t.match(SEMICOLON);
571
+ return n;
572
+ }
573
+
574
+ function FunctionDefinition(t, x, requireName, functionForm) {
575
+ var f = new NarcNode(t);
576
+ if (f.type != FUNCTION)
577
+ f.type = (f.value == "get") ? GETTER : SETTER;
578
+ if (t.match(IDENTIFIER)) {
579
+ f.name = t.token().value;
580
+ }
581
+ else if (requireName)
582
+ throw t.newSyntaxError("Missing function identifier");
583
+
584
+ t.mustMatch(LEFT_PAREN);
585
+ f.params = [];
586
+ var tt;
587
+ while ((tt = t.get()) != RIGHT_PAREN) {
588
+ if (tt != IDENTIFIER)
589
+ throw t.newSyntaxError("Missing formal parameter");
590
+ f.params.push(t.token().value);
591
+ if (t.peek() != RIGHT_PAREN)
592
+ t.mustMatch(COMMA);
593
+ }
594
+
595
+ t.mustMatch(LEFT_CURLY);
596
+ var x2 = new CompilerContext(true);
597
+ f.body = Script(t, x2);
598
+ t.mustMatch(RIGHT_CURLY);
599
+ f.end = t.token().end;
600
+
601
+ f.functionForm = functionForm;
602
+ if (functionForm == DECLARED_FORM) {
603
+ x.funDecls.push(f);
604
+ }
605
+
606
+ return f;
607
+ }
608
+
609
+ function Variables(t, x) {
610
+ var n = new NarcNode(t);
611
+ do {
612
+ t.mustMatch(IDENTIFIER);
613
+ var n2 = new NarcNode(t);
614
+ n2.name = n2.value;
615
+ if (t.match(ASSIGN)) {
616
+ if (t.token().assignOp)
617
+ throw t.newSyntaxError("Invalid variable initialization");
618
+ n2.initializer = Expression(t, x, COMMA);
619
+ }
620
+ n2.readOnly = (n.type == CONST);
621
+ n.push(n2);
622
+ x.varDecls.push(n2);
623
+ } while (t.match(COMMA));
624
+ return n;
625
+ }
626
+
627
+ function ParenExpression(t, x) {
628
+ t.mustMatch(LEFT_PAREN);
629
+ var n = Expression(t, x);
630
+ t.mustMatch(RIGHT_PAREN);
631
+ return n;
632
+ }
633
+
634
+ var opPrecedence = {
635
+ SEMICOLON: 0,
636
+ COMMA: 1,
637
+ ASSIGN: 2, HOOK: 2, COLON: 2, CONDITIONAL: 2,
638
+ // The above all have to have the same precedence, see bug 330975.
639
+ OR: 4,
640
+ AND: 5,
641
+ BITWISE_OR: 6,
642
+ BITWISE_XOR: 7,
643
+ BITWISE_AND: 8,
644
+ EQ: 9, NE: 9, STRICT_EQ: 9, STRICT_NE: 9,
645
+ LT: 10, LE: 10, GE: 10, GT: 10, IN: 10, INSTANCEOF: 10,
646
+ LSH: 11, RSH: 11, URSH: 11,
647
+ PLUS: 12, MINUS: 12,
648
+ MUL: 13, DIV: 13, MOD: 13,
649
+ DELETE: 14, VOID: 14, TYPEOF: 14, // PRE_INCREMENT: 14, PRE_DECREMENT: 14,
650
+ NOT: 14, BITWISE_NOT: 14, UNARY_PLUS: 14, UNARY_MINUS: 14,
651
+ INCREMENT: 15, DECREMENT: 15, // postfix
652
+ NEW: 16,
653
+ DOT: 17
654
+ };
655
+
656
+ // Map operator type code to precedence.
657
+ for (i in opPrecedence)
658
+ opPrecedence[GLOBAL[i]] = opPrecedence[i];
659
+
660
+ var opArity = {
661
+ COMMA: -2,
662
+ ASSIGN: 2,
663
+ CONDITIONAL: 3,
664
+ OR: 2,
665
+ AND: 2,
666
+ BITWISE_OR: 2,
667
+ BITWISE_XOR: 2,
668
+ BITWISE_AND: 2,
669
+ EQ: 2, NE: 2, STRICT_EQ: 2, STRICT_NE: 2,
670
+ LT: 2, LE: 2, GE: 2, GT: 2, IN: 2, INSTANCEOF: 2,
671
+ LSH: 2, RSH: 2, URSH: 2,
672
+ PLUS: 2, MINUS: 2,
673
+ MUL: 2, DIV: 2, MOD: 2,
674
+ DELETE: 1, VOID: 1, TYPEOF: 1, // PRE_INCREMENT: 1, PRE_DECREMENT: 1,
675
+ NOT: 1, BITWISE_NOT: 1, UNARY_PLUS: 1, UNARY_MINUS: 1,
676
+ INCREMENT: 1, DECREMENT: 1, // postfix
677
+ NEW: 1, NEW_WITH_ARGS: 2, DOT: 2, INDEX: 2, CALL: 2,
678
+ ARRAY_INIT: 1, OBJECT_INIT: 1, GROUP: 1
679
+ };
680
+
681
+ // Map operator type code to arity.
682
+ for (i in opArity)
683
+ opArity[GLOBAL[i]] = opArity[i];
684
+
685
+ function Expression(t, x, stop) {
686
+ var n, id, tt, operators = [], operands = [];
687
+ var bl = x.bracketLevel, cl = x.curlyLevel, pl = x.parenLevel,
688
+ hl = x.hookLevel;
689
+
690
+ function reduce() {
691
+ //debug('OPERATORS => '+operators);
692
+ var n = operators.pop();
693
+ var op = n.type;
694
+ var arity = opArity[op];
695
+ if (arity == -2) {
696
+ // Flatten left-associative trees.
697
+ var left = operands.length >= 2 && operands[operands.length-2];
698
+ if (left.type == op) {
699
+ var right = operands.pop();
700
+ left.push(right);
701
+ return left;
702
+ }
703
+ arity = 2;
704
+ }
705
+
706
+ // Always use push to add operands to n, to update start and end.
707
+ var a = operands.splice(operands.length - arity, operands.length);
708
+ for (var i = 0; i < arity; i++) {
709
+ n.push(a[i]);
710
+ }
711
+
712
+ // Include closing bracket or postfix operator in [start,end).
713
+ if (n.end < t.token().end)
714
+ n.end = t.token().end;
715
+
716
+ operands.push(n);
717
+ return n;
718
+ }
719
+
720
+ loop:
721
+ while ((tt = t.get()) != END) {
722
+ //debug('TT => '+tokens[tt]);
723
+ if (tt == stop &&
724
+ x.bracketLevel == bl && x.curlyLevel == cl && x.parenLevel == pl &&
725
+ x.hookLevel == hl) {
726
+ // Stop only if tt matches the optional stop parameter, and that
727
+ // token is not quoted by some kind of bracket.
728
+ break;
729
+ }
730
+ switch (tt) {
731
+ case SEMICOLON:
732
+ // NB: cannot be empty, Statement handled that.
733
+ break loop;
734
+
735
+ case ASSIGN:
736
+ case HOOK:
737
+ case COLON:
738
+ if (t.scanOperand)
739
+ break loop;
740
+ // Use >, not >=, for right-associative ASSIGN and HOOK/COLON.
741
+ while (operators.length && opPrecedence[operators.top().type] > opPrecedence[tt] ||
742
+ (tt == COLON && operators.top().type == ASSIGN)) {
743
+ reduce();
744
+ }
745
+ if (tt == COLON) {
746
+ n = operators.top();
747
+ if (n.type != HOOK)
748
+ throw t.newSyntaxError("Invalid label");
749
+ n.type = CONDITIONAL;
750
+ --x.hookLevel;
751
+ } else {
752
+ operators.push(new NarcNode(t));
753
+ if (tt == ASSIGN)
754
+ operands.top().assignOp = t.token().assignOp;
755
+ else
756
+ ++x.hookLevel; // tt == HOOK
757
+ }
758
+ t.scanOperand = true;
759
+ break;
760
+
761
+ case IN:
762
+ // An in operator should not be parsed if we're parsing the head of
763
+ // a for (...) loop, unless it is in the then part of a conditional
764
+ // expression, or parenthesized somehow.
765
+ if (x.inForLoopInit && !x.hookLevel &&
766
+ !x.bracketLevel && !x.curlyLevel && !x.parenLevel) {
767
+ break loop;
768
+ }
769
+ // FALL THROUGH
770
+ case COMMA:
771
+ // Treat comma as left-associative so reduce can fold left-heavy
772
+ // COMMA trees into a single array.
773
+ // FALL THROUGH
774
+ case OR:
775
+ case AND:
776
+ case BITWISE_OR:
777
+ case BITWISE_XOR:
778
+ case BITWISE_AND:
779
+ case EQ: case NE: case STRICT_EQ: case STRICT_NE:
780
+ case LT: case LE: case GE: case GT:
781
+ case INSTANCEOF:
782
+ case LSH: case RSH: case URSH:
783
+ case PLUS: case MINUS:
784
+ case MUL: case DIV: case MOD:
785
+ case DOT:
786
+ if (t.scanOperand)
787
+ break loop;
788
+ while (operators.length && opPrecedence[operators.top().type] >= opPrecedence[tt])
789
+ reduce();
790
+ if (tt == DOT) {
791
+ t.mustMatch(IDENTIFIER);
792
+ operands.push(new NarcNode(t, DOT, operands.pop(), new NarcNode(t)));
793
+ } else {
794
+ operators.push(new NarcNode(t));
795
+ t.scanOperand = true;
796
+ }
797
+ break;
798
+
799
+ case DELETE: case VOID: case TYPEOF:
800
+ case NOT: case BITWISE_NOT: case UNARY_PLUS: case UNARY_MINUS:
801
+ case NEW:
802
+ if (!t.scanOperand)
803
+ break loop;
804
+ operators.push(new NarcNode(t));
805
+ break;
806
+
807
+ case INCREMENT: case DECREMENT:
808
+ if (t.scanOperand) {
809
+ operators.push(new NarcNode(t)); // prefix increment or decrement
810
+ } else {
811
+ // Use >, not >=, so postfix has higher precedence than prefix.
812
+ while (operators.length && opPrecedence[operators.top().type] > opPrecedence[tt])
813
+ reduce();
814
+ n = new NarcNode(t, tt, operands.pop());
815
+ n.postfix = true;
816
+ operands.push(n);
817
+ }
818
+ break;
819
+
820
+ case FUNCTION:
821
+ if (!t.scanOperand)
822
+ break loop;
823
+ operands.push(FunctionDefinition(t, x, false, EXPRESSED_FORM));
824
+ t.scanOperand = false;
825
+ break;
826
+
827
+ case NULL: case THIS: case TRUE: case FALSE:
828
+ case IDENTIFIER: case NUMBER: case STRING: case REGEXP:
829
+ if (!t.scanOperand)
830
+ break loop;
831
+ operands.push(new NarcNode(t));
832
+ t.scanOperand = false;
833
+ break;
834
+
835
+ case LEFT_BRACKET:
836
+ if (t.scanOperand) {
837
+ // Array initialiser. Parse using recursive descent, as the
838
+ // sub-grammar here is not an operator grammar.
839
+ n = new NarcNode(t, ARRAY_INIT);
840
+ while ((tt = t.peek()) != RIGHT_BRACKET) {
841
+ if (tt == COMMA) {
842
+ t.get();
843
+ n.push(null);
844
+ continue;
845
+ }
846
+ n.push(Expression(t, x, COMMA));
847
+ if (!t.match(COMMA))
848
+ break;
849
+ }
850
+ t.mustMatch(RIGHT_BRACKET);
851
+ operands.push(n);
852
+ t.scanOperand = false;
853
+ } else {
854
+ // Property indexing operator.
855
+ operators.push(new NarcNode(t, INDEX));
856
+ t.scanOperand = true;
857
+ ++x.bracketLevel;
858
+ }
859
+ break;
860
+
861
+ case RIGHT_BRACKET:
862
+ if (t.scanOperand || x.bracketLevel == bl)
863
+ break loop;
864
+ while (reduce().type != INDEX)
865
+ continue;
866
+ --x.bracketLevel;
867
+ break;
868
+
869
+ case LEFT_CURLY:
870
+ if (!t.scanOperand)
871
+ break loop;
872
+ // Object initialiser. As for array initialisers (see above),
873
+ // parse using recursive descent.
874
+ ++x.curlyLevel;
875
+ n = new NarcNode(t, OBJECT_INIT);
876
+ object_init:
877
+ if (!t.match(RIGHT_CURLY)) {
878
+ do {
879
+ tt = t.get();
880
+ if ((t.token().value == "get" || t.token().value == "set") &&
881
+ t.peek() == IDENTIFIER) {
882
+ if (x.ecmaStrictMode)
883
+ throw t.newSyntaxError("Illegal property accessor");
884
+ n.push(FunctionDefinition(t, x, true, EXPRESSED_FORM));
885
+ } else {
886
+ switch (tt) {
887
+ case IDENTIFIER:
888
+ case NUMBER:
889
+ case STRING:
890
+ id = new NarcNode(t);
891
+ break;
892
+ case RIGHT_CURLY:
893
+ if (x.ecmaStrictMode)
894
+ throw t.newSyntaxError("Illegal trailing ,");
895
+ break object_init;
896
+ default:
897
+ throw t.newSyntaxError("Invalid property name");
898
+ }
899
+ t.mustMatch(COLON);
900
+ n.push(new NarcNode(t, PROPERTY_INIT, id,
901
+ Expression(t, x, COMMA)));
902
+ }
903
+ } while (t.match(COMMA));
904
+ t.mustMatch(RIGHT_CURLY);
905
+ }
906
+ operands.push(n);
907
+ t.scanOperand = false;
908
+ --x.curlyLevel;
909
+ break;
910
+
911
+ case RIGHT_CURLY:
912
+ if (!t.scanOperand && x.curlyLevel != cl)
913
+ throw "PANIC: right curly botch";
914
+ break loop;
915
+
916
+ case LEFT_PAREN:
917
+ if (t.scanOperand) {
918
+ operators.push(new NarcNode(t, GROUP));
919
+ } else {
920
+ while (operators.length && opPrecedence[operators.top().type] > opPrecedence[NEW])
921
+ reduce();
922
+
923
+ // Handle () now, to regularize the n-ary case for n > 0.
924
+ // We must set scanOperand in case there are arguments and
925
+ // the first one is a regexp or unary+/-.
926
+ n = operators.top();
927
+ t.scanOperand = true;
928
+ if (t.match(RIGHT_PAREN)) {
929
+ if (n.type == NEW) {
930
+ --operators.length;
931
+ n.push(operands.pop());
932
+ } else {
933
+ n = new NarcNode(t, CALL, operands.pop(),
934
+ new NarcNode(t, LIST));
935
+ }
936
+ operands.push(n);
937
+ t.scanOperand = false;
938
+ break;
939
+ }
940
+ if (n.type == NEW)
941
+ n.type = NEW_WITH_ARGS;
942
+ else
943
+ operators.push(new NarcNode(t, CALL));
944
+ }
945
+ ++x.parenLevel;
946
+ break;
947
+
948
+ case RIGHT_PAREN:
949
+ if (t.scanOperand || x.parenLevel == pl)
950
+ break loop;
951
+ while ((tt = reduce().type) != GROUP && tt != CALL &&
952
+ tt != NEW_WITH_ARGS) {
953
+ continue;
954
+ }
955
+ if (tt != GROUP) {
956
+ n = operands.top();
957
+ if (n[1].type != COMMA)
958
+ n[1] = new NarcNode(t, LIST, n[1]);
959
+ else
960
+ n[1].type = LIST;
961
+ }
962
+ --x.parenLevel;
963
+ break;
964
+
965
+ // Automatic semicolon insertion means we may scan across a newline
966
+ // and into the beginning of another statement. If so, break out of
967
+ // the while loop and let the t.scanOperand logic handle errors.
968
+ default:
969
+ break loop;
970
+ }
971
+ }
972
+ if (x.hookLevel != hl)
973
+ throw t.newSyntaxError("Missing : after ?");
974
+ if (x.parenLevel != pl)
975
+ throw t.newSyntaxError("Missing ) in parenthetical");
976
+ if (x.bracketLevel != bl)
977
+ throw t.newSyntaxError("Missing ] in index expression");
978
+ if (t.scanOperand)
979
+ throw t.newSyntaxError("Missing operand");
980
+
981
+ // Resume default mode, scanning for operands, not operators.
982
+ t.scanOperand = true;
983
+ t.unget();
984
+
985
+ while (operators.length)
986
+ reduce();
987
+ return operands.pop();
988
+ }
989
+
990
+ function parse(s, f, l) {
991
+ var t = new Tokenizer(s, f, l);
992
+ var x = new CompilerContext(false);
993
+ var n = Script(t, x);
994
+ if (!t.done())
995
+ throw t.newSyntaxError("Syntax error");
996
+ return n;
997
+ }
998
+
999
+ debug = function(msg) {
1000
+ document.body.appendChild(document.createTextNode(msg));
1001
+ document.body.appendChild(document.createElement('br'));
1002
+ }
1003
+