comparison .elisp/js2.el @ 27:614a83a1c5dd

Add js2-mode.
author Augie Fackler <durin42@gmail.com>
date Tue, 30 Dec 2008 13:25:23 -0600
parents
children
comparison
equal deleted inserted replaced
26:895bcf0db86a 27:614a83a1c5dd
1 ;;; js2.el -- an improved JavaScript editing mode
2 ;;;
3 ;;; This file was auto-generated on Mon Jun 16 01:46:45 2008 from files:
4 ;;; js2-vars.el
5 ;;; js2-util.el
6 ;;; js2-scan.el
7 ;;; js2-messages.el
8 ;;; js2-ast.el
9 ;;; js2-highlight.el
10 ;;; js2-browse.el
11 ;;; js2-parse.el
12 ;;; js2-indent.el
13 ;;; js2-mode.el
14
15 ;;; js2-mode.el --- an improved JavaScript editing mode
16
17 ;; Author: Steve Yegge (steve.yegge@gmail.com)
18 ;; Version: 20080616
19 ;; Keywords: javascript languages
20
21 ;; This program is free software; you can redistribute it and/or
22 ;; modify it under the terms of the GNU General Public License as
23 ;; published by the Free Software Foundation; either version 2 of
24 ;; the License, or (at your option) any later version.
25
26 ;; This program is distributed in the hope that it will be
27 ;; useful, but WITHOUT ANY WARRANTY; without even the implied
28 ;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
29 ;; PURPOSE. See the GNU General Public License for more details.
30
31 ;; You should have received a copy of the GNU General Public
32 ;; License along with this program; if not, write to the Free
33 ;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
34 ;; MA 02111-1307 USA
35
36 ;;; Commentary:
37
38 ;; This JavaScript editing mode supports:
39 ;;
40 ;; - the full JavaScript language through version 1.7
41 ;; - support for most Rhino and SpiderMonkey extensions from 1.5 to 1.7
42 ;; - accurate syntax highlighting using a recursive-descent parser
43 ;; - syntax-error and strict-mode warning reporting
44 ;; - "bouncing" line indentation to choose among alternate indentation points
45 ;; - smart line-wrapping within comments (Emacs 22+) and strings
46 ;; - code folding:
47 ;; - show some or all function bodies as {...}
48 ;; - show some or all block comments as /*...*/
49 ;; - context-sensitive menu bar and popup menus
50 ;; - code browsing using the `imenu' package
51 ;; - typing helpers (e.g. inserting matching braces/parens)
52 ;; - many customization options
53 ;;
54 ;; It is only compatible with GNU Emacs versions 21 and higher (not XEmacs).
55 ;;
56 ;; Installation:
57 ;;
58 ;; - put `js2.el' somewhere in your emacs load path
59 ;; - M-x byte-compile-file RET <path-to-js2.el> RET
60 ;; Note: it will refuse to run unless byte-compiled
61 ;; - add these lines to your .emacs file:
62 ;; (autoload 'js2-mode "js2" nil t)
63 ;; (add-to-list 'auto-mode-alist '("\\.js$" . js2-mode))
64 ;;
65 ;; To customize how it works:
66 ;; M-x customize-group RET js2-mode RET
67 ;;
68 ;; The variable `js2-mode-version' is a date stamp. When you upgrade
69 ;; to a newer version, you must byte-compile the file again.
70 ;;
71 ;; Notes:
72 ;;
73 ;; This mode is different in many ways from standard Emacs language editing
74 ;; modes, inasmuch as it attempts to be more like an IDE. If this drives
75 ;; you crazy, it IS possible to customize it to be more like other Emacs
76 ;; editing modes. Please customize the group `js2-mode' to see all of the
77 ;; configuration options.
78 ;;
79 ;; Some of the functionality does not work in Emacs 21 -- upgrading to
80 ;; Emacs 22 or higher will get you better results. If you byte-compiled
81 ;; js2.el with Emacs 21, you should re-compile it for Emacs 22.
82 ;;
83 ;; Unlike cc-engine based language modes, js2-mode's line-indentation is not
84 ;; customizable. It is a surprising amount of work to support customizable
85 ;; indentation. The current compromise is that the tab key lets you cycle among
86 ;; various likely indentation points, similar to the behavior of python-mode.
87 ;;
88 ;; This mode does not yet work with "multi-mode" modes such as mmm-mode
89 ;; and mumamo, although it could possibly be made to do so with some effort.
90 ;; This means that js2-mode is currently only useful for editing JavaScript
91 ;; files, and not for editing JavaScript within <script> tags or templates.
92 ;;
93 ;; This code is part of a larger project, in progress, to enable writing
94 ;; Emacs customizations in JavaScript.
95 ;;
96 ;; Please email bug reports and suggestions to the author, or submit them
97 ;; at http://code.google.com/p/js2-mode/issues
98
99 ;; TODO:
100 ;; - add unreachable-code warning (error?) using the inconsistent-return analysis
101 ;; - labeled stmt length is now 1
102 ;; - "anonymous function does not always return a value" - use getter/setter name
103 ;; - extend js2-missing-semi-one-line-override to handle catch (e) {return x}
104 ;; - set a text prop on autoinserted delimiters and don't biff user-entered ones
105 ;; - when inserting magic curlies, look for matching close-curly before inserting
106 ;; - get more use out of the symbol table:
107 ;; - jump to declaration (put hyperlinks on all non-decl var usages?)
108 ;; - rename variable/function
109 ;; - warn on unused var
110 ;; - add some dabbrev-expansions for built-in keywords like finally, function
111 ;; - add at least some completion support, e.g. for built-ins
112 ;; - code formatting
113
114 ;;; Code:
115 ;;; js2-vars.el -- byte-compiler support for js2-mode
116
117 ;; Author: Steve Yegge (steve.yegge@gmail.com)
118 ;; Keywords: javascript languages
119
120 ;;; Code:
121
122 (eval-when-compile
123 (require 'cl))
124
125 (eval-and-compile
126 (require 'cc-mode) ; (only) for `c-populate-syntax-table'
127 (require 'cc-langs) ; it's here in Emacs 21...
128 (require 'cc-engine)) ; for `c-paragraph-start' et. al.
129
130 (defvar js2-emacs22 (>= emacs-major-version 22))
131
132 (defcustom js2-highlight-level 2
133 "Amount of syntax highlighting to perform.
134 nil, zero or negative means none.
135 1 adds basic syntax highlighting.
136 2 adds highlighting of some Ecma built-in properties.
137 3 adds highlighting of many Ecma built-in functions."
138 :type 'integer
139 :group 'js2-mode)
140
141 (defvar js2-mode-dev-mode-p nil
142 "Non-nil if running in development mode. Normally nil.")
143
144 (defgroup js2-mode nil
145 "An improved JavaScript mode."
146 :group 'languages)
147
148 (defcustom js2-basic-offset (if (and (boundp 'c-basic-offset)
149 (numberp c-basic-offset))
150 c-basic-offset
151 2)
152 "Number of spaces to indent nested statements.
153 Similar to `c-basic-offset'."
154 :group 'js2-mode
155 :type 'integer)
156 (make-variable-buffer-local 'js2-basic-offset)
157
158 (defcustom js2-cleanup-whitespace t
159 "Non-nil to invoke `delete-trailing-whitespace' before saves."
160 :type 'boolean
161 :group 'js2-mode)
162
163 (defcustom js2-move-point-on-right-click t
164 "Non-nil to move insertion point when you right-click.
165 This makes right-click context menu behavior a bit more intuitive,
166 since menu operations generally apply to the point. The exception
167 is if there is a region selection, in which case the point does -not-
168 move, so cut/copy/paste etc. can work properly.
169
170 Note that IntelliJ moves the point, and Eclipse leaves it alone,
171 so this behavior is customizable."
172 :group 'js2-mode
173 :type 'boolean)
174
175 (defcustom js2-mirror-mode t
176 "Non-nil to insert closing brackets, parens, etc. automatically."
177 :group 'js2-mode
178 :type 'boolean)
179
180 (defcustom js2-auto-indent-flag t
181 "Automatic indentation with punctuation characters. If non-nil, the
182 current line is indented when certain punctuations are inserted."
183 :group 'js2-mode
184 :type 'boolean)
185
186 (defcustom js2-bounce-indent-flag t
187 "Non-nil to have indent-line function choose among alternatives.
188 If nil, the indent-line function will indent to a predetermined column
189 based on heuristic guessing. If non-nil, then if the current line is
190 already indented to that predetermined column, indenting will choose
191 another likely column and indent to that spot. Repeated invocation of
192 the indent-line function will cycle among the computed alternatives.
193 See the function `js2-bounce-indent' for details."
194 :type 'boolean
195 :group 'js2-mode)
196
197 (defcustom js2-indent-on-enter-key nil
198 "Non-nil to have Enter/Return key indent the line.
199 This is unusual for Emacs modes but common in IDEs like Eclipse."
200 :type 'boolean
201 :group 'js2-mode)
202
203 (defcustom js2-enter-indents-newline t
204 "Non-nil to have Enter/Return key indent the newly-inserted line.
205 This is unusual for Emacs modes but common in IDEs like Eclipse."
206 :type 'boolean
207 :group 'js2-mode)
208
209 (defcustom js2-rebind-eol-bol-keys t
210 "Non-nil to rebind beginning-of-line and end-of-line keys.
211 If non-nil, bounce between bol/eol and first/last non-whitespace char."
212 :group 'js2-mode
213 :type 'boolean)
214
215 (defcustom js2-electric-keys '("{" "}" "(" ")" "[" "]" ":" ";" "," "*")
216 "Keys that auto-indent when `js2-auto-indent-flag' is non-nil.
217 Each value in the list is passed to `define-key'."
218 :type 'list
219 :group 'js2-mode)
220
221 (defcustom js2-idle-timer-delay 0.2
222 "Delay in secs before re-parsing after user makes changes.
223 Multiplied by `js2-dynamic-idle-timer-adjust', which see."
224 :type 'number
225 :group 'js2-mode)
226 (make-variable-buffer-local 'js2-idle-timer-delay)
227
228 (defcustom js2-dynamic-idle-timer-adjust 0
229 "Positive to adjust `js2-idle-timer-delay' based on file size.
230 The idea is that for short files, parsing is faster so we can be
231 more responsive to user edits without interfering with editing.
232 The buffer length in characters (typically bytes) is divided by
233 this value and used to multiply `js2-idle-timer-delay' for the
234 buffer. For example, a 21k file and 10k adjust yields 21k/10k
235 == 2, so js2-idle-timer-delay is multiplied by 2.
236 If `js2-dynamic-idle-timer-adjust' is 0 or negative,
237 `js2-idle-timer-delay' is not dependent on the file size."
238 :type 'number
239 :group 'js2-mode)
240
241 (defcustom js2-mode-escape-quotes t
242 "Non-nil to disable automatic quote-escaping inside strings."
243 :type 'boolean
244 :group 'js2-mode)
245
246 (defcustom js2-mode-squeeze-spaces t
247 "Non-nil to normalize whitespace when filling in comments.
248 Multiple runs of spaces are converted to a single space."
249 :type 'boolean
250 :group 'js2-mode)
251
252 (defcustom js2-mode-show-parse-errors t
253 "True to highlight parse errors."
254 :type 'boolean
255 :group 'js2-mode)
256
257 (defcustom js2-mode-show-strict-warnings t
258 "Non-nil to emit Ecma strict-mode warnings.
259 Some of the warnings can be individually disabled by other flags,
260 even if this flag is non-nil."
261 :type 'boolean
262 :group 'js2-mode)
263
264 (defcustom js2-strict-trailing-comma-warning t
265 "Non-nil to warn about trailing commas in array literals.
266 Ecma-262 forbids them, but many browsers permit them. IE is the
267 big exception, and can produce bugs if you have trailing commas."
268 :type 'boolean
269 :group 'js2-mode)
270
271 (defcustom js2-strict-missing-semi-warning t
272 "Non-nil to warn about semicolon auto-insertion after statement.
273 Technically this is legal per Ecma-262, but some style guides disallow
274 depending on it."
275 :type 'boolean
276 :group 'js2-mode)
277
278 (defcustom js2-missing-semi-one-line-override nil
279 "Non-nil to permit missing semicolons in one-line functions.
280 In one-liner functions such as `function identity(x) {return x}'
281 people often omit the semicolon for a cleaner look. If you are
282 such a person, you can suppress the missing-semicolon warning
283 by setting this variable to t."
284 :type 'boolean
285 :group 'js2-mode)
286
287 (defcustom js2-strict-inconsistent-return-warning t
288 "Non-nil to warn about mixing returns with value-returns.
289 It's perfectly legal to have a `return' and a `return foo' in the
290 same function, but it's often an indicator of a bug, and it also
291 interferes with type inference (in systems that support it.)"
292 :type 'boolean
293 :group 'js2-mode)
294
295 (defcustom js2-strict-cond-assign-warning t
296 "Non-nil to warn about expressions like if (a = b).
297 This often should have been '==' instead of '='. If the warning
298 is enabled, you can suppress it on a per-expression basis by
299 parenthesizing the expression, e.g. if ((a = b)) ..."
300 :type 'boolean
301 :group 'js2-mode)
302
303 (defcustom js2-strict-cond-assign-warning t
304 "Non-nil to warn about expressions like if (a = b).
305 This often should have been '==' instead of '='. If the warning
306 is enabled, you can suppress it on a per-expression basis by
307 parenthesizing the expression, e.g. if ((a = b)) ..."
308 :type 'boolean
309 :group 'js2-mode)
310
311 (defcustom js2-strict-var-redeclaration-warning t
312 "Non-nil to warn about redeclaring variables in a script or function."
313 :type 'boolean
314 :group 'js2-mode)
315
316 (defcustom js2-strict-var-hides-function-arg-warning t
317 "Non-nil to warn about a var decl hiding a function argument."
318 :type 'boolean
319 :group 'js2-mode)
320
321 (defcustom js2-skip-preprocessor-directives nil
322 "Non-nil to treat lines beginning with # as comments.
323 Useful for viewing Mozilla JavaScript source code."
324 :type 'boolean
325 :group 'js2-mode)
326
327 (defcustom js2-basic-offset c-basic-offset
328 "Functions like `c-basic-offset' in js2-mode buffers."
329 :type 'integer
330 :group 'js2-mode)
331 (make-variable-buffer-local 'js2-basic-offset)
332
333 (defcustom js2-language-version 170
334 "Configures what JavaScript language version to recognize.
335 Currently only 150, 160 and 170 are supported, corresponding
336 to JavaScript 1.5, 1.6 and 1.7, respectively. In a nutshell,
337 1.6 adds E4X support, and 1.7 adds let, yield, and Array
338 comprehensions."
339 :type 'integer
340 :group 'js2-mode)
341
342 (defcustom js2-allow-keywords-as-property-names t
343 "If non-nil, you can use JavaScript keywords as object property names.
344 Examples:
345
346 var foo = {int: 5, while: 6, continue: 7};
347 foo.return = 8;
348
349 Ecma-262 forbids this syntax, but many browsers support it."
350 :type 'boolean
351 :group 'js2-mode)
352
353 (defcustom js2-instanceof-has-side-effects nil
354 "If non-nil, treats the instanceof operator as having side effects.
355 This is useful for xulrunner apps."
356 :type 'boolean
357 :group 'js2-mode)
358
359 (defcustom js2-allow-rhino-new-expr-initializer nil
360 "Non-nil to support a Rhino's experimental syntactic construct.
361
362 Rhino supports the ability to follow a `new' expression with an object
363 literal, which is used to set additional properties on the new object
364 after calling its constructor. Syntax:
365
366 new <expr> [ ( arglist ) ] [initializer]
367
368 Hence, this expression:
369
370 new Object {a: 1, b: 2}
371
372 results in an Object with properties a=1 and b=2. This syntax is
373 apparently not configurable in Rhino - it's currently always enabled,
374 as of Rhino version 1.7R2."
375 :type 'boolean
376 :group 'js2-mode)
377
378 (defcustom js2-allow-member-expr-as-function-name nil
379 "Non-nil to support experimental Rhino syntax for function names.
380
381 Rhino supports an experimental syntax configured via the Rhino Context
382 setting `allowMemberExprAsFunctionName'. The experimental syntax is:
383
384 function <member-expr> ( [ arg-list ] ) { <body> }
385
386 Where member-expr is a non-parenthesized 'member expression', which
387 is anything at the grammar level of a new-expression or lower, meaning
388 any expression that does not involve infix or unary operators.
389
390 When <member-expr> is not a simple identifier, then it is syntactic
391 sugar for assigning the anonymous function to the <member-expr>. Hence,
392 this code:
393
394 function a.b().c[2] (x, y) { ... }
395
396 is rewritten as:
397
398 a.b().c[2] = function(x, y) {...}
399
400 which doesn't seem particularly useful, but Rhino permits it."
401 :type 'boolean
402 :group 'js2-mode)
403
404 (defvar js2-mode-version 20080616
405 "Release number for `js2-mode'.")
406
407 ;; scanner variables
408
409 ;; We record the start and end position of each token.
410 (defvar js2-token-beg 1)
411 (make-variable-buffer-local 'js2-token-beg)
412 (defvar js2-token-end -1)
413 (make-variable-buffer-local 'js2-token-end)
414
415 (defvar js2-EOF_CHAR -1
416 "Represents end of stream. Distinct from js2-EOF token type.")
417
418 ;; I originally used symbols to represent tokens, but Rhino uses
419 ;; ints and then sets various flag bits in them, so ints it is.
420 ;; The upshot is that we need a `js2-' prefix in front of each name.
421 (defvar js2-ERROR -1)
422 (defvar js2-EOF 0)
423 (defvar js2-EOL 1)
424 (defvar js2-ENTERWITH 2) ; begin interpreter bytecodes
425 (defvar js2-LEAVEWITH 3)
426 (defvar js2-RETURN 4)
427 (defvar js2-GOTO 5)
428 (defvar js2-IFEQ 6)
429 (defvar js2-IFNE 7)
430 (defvar js2-SETNAME 8)
431 (defvar js2-BITOR 9)
432 (defvar js2-BITXOR 10)
433 (defvar js2-BITAND 11)
434 (defvar js2-EQ 12)
435 (defvar js2-NE 13)
436 (defvar js2-LT 14)
437 (defvar js2-LE 15)
438 (defvar js2-GT 16)
439 (defvar js2-GE 17)
440 (defvar js2-LSH 18)
441 (defvar js2-RSH 19)
442 (defvar js2-URSH 20)
443 (defvar js2-ADD 21) ; infix plus
444 (defvar js2-SUB 22) ; infix minus
445 (defvar js2-MUL 23)
446 (defvar js2-DIV 24)
447 (defvar js2-MOD 25)
448 (defvar js2-NOT 26)
449 (defvar js2-BITNOT 27)
450 (defvar js2-POS 28) ; unary plus
451 (defvar js2-NEG 29) ; unary minus
452 (defvar js2-NEW 30)
453 (defvar js2-DELPROP 31)
454 (defvar js2-TYPEOF 32)
455 (defvar js2-GETPROP 33)
456 (defvar js2-GETPROPNOWARN 34)
457 (defvar js2-SETPROP 35)
458 (defvar js2-GETELEM 36)
459 (defvar js2-SETELEM 37)
460 (defvar js2-CALL 38)
461 (defvar js2-NAME 39) ; an identifier
462 (defvar js2-NUMBER 40)
463 (defvar js2-STRING 41)
464 (defvar js2-NULL 42)
465 (defvar js2-THIS 43)
466 (defvar js2-FALSE 44)
467 (defvar js2-TRUE 45)
468 (defvar js2-SHEQ 46) ; shallow equality (===)
469 (defvar js2-SHNE 47) ; shallow inequality (!==)
470 (defvar js2-REGEXP 48)
471 (defvar js2-BINDNAME 49)
472 (defvar js2-THROW 50)
473 (defvar js2-RETHROW 51) ; rethrow caught exception: catch (e if ) uses it
474 (defvar js2-IN 52)
475 (defvar js2-INSTANCEOF 53)
476 (defvar js2-LOCAL_LOAD 54)
477 (defvar js2-GETVAR 55)
478 (defvar js2-SETVAR 56)
479 (defvar js2-CATCH_SCOPE 57)
480 (defvar js2-ENUM_INIT_KEYS 58)
481 (defvar js2-ENUM_INIT_VALUES 59)
482 (defvar js2-ENUM_INIT_ARRAY 60)
483 (defvar js2-ENUM_NEXT 61)
484 (defvar js2-ENUM_ID 62)
485 (defvar js2-THISFN 63)
486 (defvar js2-RETURN_RESULT 64) ; to return previously stored return result
487 (defvar js2-ARRAYLIT 65) ; array literal
488 (defvar js2-OBJECTLIT 66) ; object literal
489 (defvar js2-GET_REF 67) ; *reference
490 (defvar js2-SET_REF 68) ; *reference = something
491 (defvar js2-DEL_REF 69) ; delete reference
492 (defvar js2-REF_CALL 70) ; f(args) = something or f(args)++
493 (defvar js2-REF_SPECIAL 71) ; reference for special properties like __proto
494 (defvar js2-YIELD 72) ; JS 1.7 yield pseudo keyword
495
496 ;; XML support
497 (defvar js2-DEFAULTNAMESPACE 73)
498 (defvar js2-ESCXMLATTR 74)
499 (defvar js2-ESCXMLTEXT 75)
500 (defvar js2-REF_MEMBER 76) ; Reference for x.@y, x..y etc.
501 (defvar js2-REF_NS_MEMBER 77) ; Reference for x.ns::y, x..ns::y etc.
502 (defvar js2-REF_NAME 78) ; Reference for @y, @[y] etc.
503 (defvar js2-REF_NS_NAME 79) ; Reference for ns::y, @ns::y@[y] etc.
504
505 (defvar js2-first-bytecode js2-ENTERWITH)
506 (defvar js2-last-bytecode js2-REF_NS_NAME)
507
508 (defvar js2-TRY 80)
509 (defvar js2-SEMI 81) ; semicolon
510 (defvar js2-LB 82) ; left and right brackets
511 (defvar js2-RB 83)
512 (defvar js2-LC 84) ; left and right curly-braces
513 (defvar js2-RC 85)
514 (defvar js2-LP 86) ; left and right parens
515 (defvar js2-RP 87)
516 (defvar js2-COMMA 88) ; comma operator
517
518 (defvar js2-ASSIGN 89) ; simple assignment (=)
519 (defvar js2-ASSIGN_BITOR 90) ; |=
520 (defvar js2-ASSIGN_BITXOR 91) ; ^=
521 (defvar js2-ASSIGN_BITAND 92) ; &=
522 (defvar js2-ASSIGN_LSH 93) ; <<=
523 (defvar js2-ASSIGN_RSH 94) ; >>=
524 (defvar js2-ASSIGN_URSH 95) ; >>>=
525 (defvar js2-ASSIGN_ADD 96) ; +=
526 (defvar js2-ASSIGN_SUB 97) ; -=
527 (defvar js2-ASSIGN_MUL 98) ; *=
528 (defvar js2-ASSIGN_DIV 99) ; /=
529 (defvar js2-ASSIGN_MOD 100) ; %=
530
531 (defvar js2-first-assign js2-ASSIGN)
532 (defvar js2-last-assign js2-ASSIGN_MOD)
533
534 (defvar js2-HOOK 101) ; conditional (?:)
535 (defvar js2-COLON 102)
536 (defvar js2-OR 103) ; logical or (||)
537 (defvar js2-AND 104) ; logical and (&&)
538 (defvar js2-INC 105) ; increment/decrement (++ --)
539 (defvar js2-DEC 106)
540 (defvar js2-DOT 107) ; member operator (.)
541 (defvar js2-FUNCTION 108) ; function keyword
542 (defvar js2-EXPORT 109) ; export keyword
543 (defvar js2-IMPORT 110) ; import keyword
544 (defvar js2-IF 111) ; if keyword
545 (defvar js2-ELSE 112) ; else keyword
546 (defvar js2-SWITCH 113) ; switch keyword
547 (defvar js2-CASE 114) ; case keyword
548 (defvar js2-DEFAULT 115) ; default keyword
549 (defvar js2-WHILE 116) ; while keyword
550 (defvar js2-DO 117) ; do keyword
551 (defvar js2-FOR 118) ; for keyword
552 (defvar js2-BREAK 119) ; break keyword
553 (defvar js2-CONTINUE 120) ; continue keyword
554 (defvar js2-VAR 121) ; var keyword
555 (defvar js2-WITH 122) ; with keyword
556 (defvar js2-CATCH 123) ; catch keyword
557 (defvar js2-FINALLY 124) ; finally keyword
558 (defvar js2-VOID 125) ; void keyword
559 (defvar js2-RESERVED 126) ; reserved keywords
560
561 (defvar js2-EMPTY 127)
562
563 ;; Types used for the parse tree - never returned by scanner.
564
565 (defvar js2-BLOCK 128) ; statement block
566 (defvar js2-LABEL 129) ; label
567 (defvar js2-TARGET 130)
568 (defvar js2-LOOP 131)
569 (defvar js2-EXPR_VOID 132) ; expression statement in functions
570 (defvar js2-EXPR_RESULT 133) ; expression statement in scripts
571 (defvar js2-JSR 134)
572 (defvar js2-SCRIPT 135) ; top-level node for entire script
573 (defvar js2-TYPEOFNAME 136) ; for typeof(simple-name)
574 (defvar js2-USE_STACK 137)
575 (defvar js2-SETPROP_OP 138) ; x.y op= something
576 (defvar js2-SETELEM_OP 139) ; x[y] op= something
577 (defvar js2-LOCAL_BLOCK 140)
578 (defvar js2-SET_REF_OP 141) ; *reference op= something
579
580 ;; For XML support:
581 (defvar js2-DOTDOT 142) ; member operator (..)
582 (defvar js2-COLONCOLON 143) ; namespace::name
583 (defvar js2-XML 144) ; XML type
584 (defvar js2-DOTQUERY 145) ; .() -- e.g., x.emps.emp.(name == "terry")
585 (defvar js2-XMLATTR 146) ; @
586 (defvar js2-XMLEND 147)
587
588 ;; Optimizer-only tokens
589 (defvar js2-TO_OBJECT 148)
590 (defvar js2-TO_DOUBLE 149)
591
592 (defvar js2-GET 150) ; JS 1.5 get pseudo keyword
593 (defvar js2-SET 151) ; JS 1.5 set pseudo keyword
594 (defvar js2-LET 152) ; JS 1.7 let pseudo keyword
595 (defvar js2-CONST 153)
596 (defvar js2-SETCONST 154)
597 (defvar js2-SETCONSTVAR 155)
598 (defvar js2-ARRAYCOMP 156)
599 (defvar js2-LETEXPR 157)
600 (defvar js2-WITHEXPR 158)
601 (defvar js2-DEBUGGER 159)
602
603 (defvar js2-COMMENT 160) ; not yet in Rhino
604
605 (defvar js2-num-tokens (1+ js2-COMMENT))
606
607 (defconst js2-debug-print-trees nil)
608
609 ;; Rhino accepts any string or stream as input.
610 ;; Emacs character processing works best in buffers, so we'll
611 ;; assume the input is a buffer. JavaScript strings can be
612 ;; copied into temp buffers before scanning them.
613
614 (defmacro deflocal (name value comment)
615 `(progn
616 (defvar ,name ,value ,comment)
617 (make-variable-buffer-local ',name)))
618
619 ;; Buffer-local variables yield much cleaner code than using `defstruct'.
620 ;; They're the Emacs equivalent of instance variables, more or less.
621
622 (deflocal js2-ts-dirty-line nil
623 "Token stream buffer-local variable.
624 Indicates stuff other than whitespace since start of line.")
625
626 (deflocal js2-ts-regexp-flags nil
627 "Token stream buffer-local variable.")
628
629 (deflocal js2-ts-string ""
630 "Token stream buffer-local variable.
631 Last string scanned.")
632
633 (deflocal js2-ts-number nil
634 "Token stream buffer-local variable.
635 Last literal number scanned.")
636
637 (deflocal js2-ts-hit-eof nil
638 "Token stream buffer-local variable.")
639
640 (deflocal js2-ts-line-start 0
641 "Token stream buffer-local variable.")
642
643 (deflocal js2-ts-lineno 1
644 "Token stream buffer-local variable.")
645
646 (deflocal js2-ts-line-end-char -1
647 "Token stream buffer-local variable.")
648
649 (deflocal js2-ts-cursor 1 ; emacs buffers are 1-indexed
650 "Token stream buffer-local variable.
651 Current scan position.")
652
653 (deflocal js2-ts-is-xml-attribute nil
654 "Token stream buffer-local variable.")
655
656 (deflocal js2-ts-xml-is-tag-content nil
657 "Token stream buffer-local variable.")
658
659 (deflocal js2-ts-xml-open-tags-count 0
660 "Token stream buffer-local variable.")
661
662 (deflocal js2-ts-string-buffer nil
663 "Token stream buffer-local variable.
664 List of chars built up while scanning various tokens.")
665
666 (deflocal js2-ts-comment-type nil
667 "Token stream buffer-local variable.")
668
669 ;;; Parser variables
670
671 (defvar js2-parsed-errors nil
672 "List of errors produced during scanning/parsing.")
673 (make-variable-buffer-local 'js2-parsed-errors)
674
675 (defvar js2-parsed-warnings nil
676 "List of warnings produced during scanning/parsing.")
677 (make-variable-buffer-local 'js2-parsed-warnings)
678
679 (defvar js2-recover-from-parse-errors t
680 "Non-nil to continue parsing after a syntax error.
681
682 In recovery mode, the AST will be built in full, and any error
683 nodes will be flagged with appropriate error information. If
684 this flag is nil, a syntax error will result in an error being
685 signaled.
686
687 The variable is automatically buffer-local, because different
688 modes that use the parser will need different settings.")
689 (make-variable-buffer-local 'js2-recover-from-parse-errors)
690
691 (defvar js2-parse-hook nil
692 "List of callbacks for receiving parsing progress.")
693 (make-variable-buffer-local 'js2-parse-hook)
694
695 (defvar js2-parse-finished-hook nil
696 "List of callbacks to notify when parsing finishes.
697 Not called if parsing was interrupted.")
698
699 (defvar js2-is-eval-code nil
700 "True if we're evaluating code in a string.
701 If non-nil, the tokenizer will record the token text, and the AST nodes
702 will record their source text. Off by default for IDE modes, since the
703 text is available in the buffer.")
704 (make-variable-buffer-local 'js2-is-eval-code)
705
706 (defvar js2-parse-ide-mode t
707 "Non-nil if the parser is being used for `js2-mode'.
708 If non-nil, the parser will set text properties for fontification
709 and the syntax-table. The value should be nil when using the
710 parser as a frontend to an interpreter or byte compiler.")
711
712 ;;; Parser instance variables (buffer-local vars for js2-parse)
713
714 (defconst js2-clear-ti-mask #xFFFF
715 "Mask to clear token information bits.")
716
717 (defconst js2-ti-after-eol (lsh 1 16)
718 "Flag: first token of the source line.")
719
720 (defconst js2-ti-check-label (lsh 1 17)
721 "Flag: indicates to check for label.")
722
723 ;; Inline Rhino's CompilerEnvirons vars as buffer-locals.
724
725 (defvar js2-compiler-generate-debug-info t)
726 (make-variable-buffer-local 'js2-compiler-generate-debug-info)
727
728 (defvar js2-compiler-use-dynamic-scope nil)
729 (make-variable-buffer-local 'js2-compiler-use-dynamic-scope)
730
731 (defvar js2-compiler-reserved-keywords-as-identifier nil)
732 (make-variable-buffer-local 'js2-compiler-reserved-keywords-as-identifier)
733
734 (defvar js2-compiler-xml-available t)
735 (make-variable-buffer-local 'js2-compiler-xml-available)
736
737 (defvar js2-compiler-optimization-level 0)
738 (make-variable-buffer-local 'js2-compiler-optimization-level)
739
740 (defvar js2-compiler-generating-source t)
741 (make-variable-buffer-local 'js2-compiler-generating-source)
742
743 (defvar js2-compiler-strict-mode nil)
744 (make-variable-buffer-local 'js2-compiler-strict-mode)
745
746 (defvar js2-compiler-report-warning-as-error nil)
747 (make-variable-buffer-local 'js2-compiler-report-warning-as-error)
748
749 (defvar js2-compiler-generate-observer-count nil)
750 (make-variable-buffer-local 'js2-compiler-generate-observer-count)
751
752 (defvar js2-compiler-activation-names nil)
753 (make-variable-buffer-local 'js2-compiler-activation-names)
754
755 ;; SKIP: sourceURI
756
757 ;; There's a compileFunction method in Context.java - may need it.
758 (defvar js2-called-by-compile-function nil
759 "True if `js2-parse' was called by `js2-compile-function'.
760 Will only be used when we finish implementing the interpreter.")
761 (make-variable-buffer-local 'js2-called-by-compile-function)
762
763 ;; SKIP: ts (we just call `js2-init-scanner' and use its vars)
764
765 (defvar js2-current-flagged-token js2-EOF)
766 (make-variable-buffer-local 'js2-current-flagged-token)
767
768 (defvar js2-current-token js2-EOF)
769 (make-variable-buffer-local 'js2-current-token)
770
771 ;; SKIP: node factory - we're going to just call functions directly,
772 ;; and eventually go to a unified AST format.
773
774 (defvar js2-nesting-of-function 0)
775 (make-variable-buffer-local 'js2-nesting-of-function)
776
777 (defvar js2-recorded-assignments nil)
778 (make-variable-buffer-local 'js2-assignments-from-parse)
779
780 ;; SKIP: decompiler
781 ;; SKIP: encoded-source
782
783 ;;; These variables are per-function and should be saved/restored
784 ;;; during function parsing.
785
786 (defvar js2-current-script-or-fn nil)
787 (make-variable-buffer-local 'js2-current-script-or-fn)
788
789 (defvar js2-current-scope nil)
790 (make-variable-buffer-local 'js2-current-scope)
791
792 (defvar js2-nesting-of-with 0)
793 (make-variable-buffer-local 'js2-nesting-of-with)
794
795 (defvar js2-label-set nil
796 "An alist mapping label names to nodes.")
797 (make-variable-buffer-local 'js2-label-set)
798
799 (defvar js2-loop-set nil)
800 (make-variable-buffer-local 'js2-loop-set)
801
802 (defvar js2-loop-and-switch-set nil)
803 (make-variable-buffer-local 'js2-loop-and-switch-set)
804
805 (defvar js2-has-return-value nil)
806 (make-variable-buffer-local 'js2-has-return-value)
807
808 (defvar js2-end-flags 0)
809 (make-variable-buffer-local 'js2-end-flags)
810
811 ;;; end of per function variables
812
813 ;; Without 2-token lookahead, labels are a problem.
814 ;; These vars store the token info of the last matched name,
815 ;; iff it wasn't the last matched token. Only valid in some contexts.
816 (defvar js2-prev-name-token-start nil)
817 (defvar js2-prev-name-token-string nil)
818
819 (defsubst js2-save-name-token-data (pos name)
820 (setq js2-prev-name-token-start pos
821 js2-prev-name-token-string name))
822
823 ;; These flags enumerate the possible ways a statement/function can
824 ;; terminate. These flags are used by endCheck() and by the Parser to
825 ;; detect inconsistent return usage.
826 ;;
827 ;; END_UNREACHED is reserved for code paths that are assumed to always be
828 ;; able to execute (example: throw, continue)
829 ;;
830 ;; END_DROPS_OFF indicates if the statement can transfer control to the
831 ;; next one. Statement such as return dont. A compound statement may have
832 ;; some branch that drops off control to the next statement.
833 ;;
834 ;; END_RETURNS indicates that the statement can return (without arguments)
835 ;; END_RETURNS_VALUE indicates that the statement can return a value.
836 ;;
837 ;; A compound statement such as
838 ;; if (condition) {
839 ;; return value;
840 ;; }
841 ;; Will be detected as (END_DROPS_OFF | END_RETURN_VALUE) by endCheck()
842
843 (defconst js2-end-unreached #x0)
844 (defconst js2-end-drops-off #x1)
845 (defconst js2-end-returns #x2)
846 (defconst js2-end-returns-value #x4)
847 (defconst js2-end-yields #x8)
848
849 ;; Rhino awkwardly passes a statementLabel parameter to the
850 ;; statementHelper() function, the main statement parser, which
851 ;; is then used by quite a few of the sub-parsers. We just make
852 ;; it a buffer-local variable and make sure it's cleaned up properly.
853 (defvar js2-labeled-stmt nil) ; type `js2-labeled-stmt-node'
854 (make-variable-buffer-local 'js2-labeled-stmt)
855
856 ;; Similarly, Rhino passes an inForInit boolean through about half
857 ;; the expression parsers. We use a dynamically-scoped variable,
858 ;; which makes it easier to funcall the parsers individually without
859 ;; worrying about whether they take the parameter or not.
860 (defvar js2-in-for-init nil)
861 (make-variable-buffer-local 'js2-in-for-init)
862
863 (defvar js2-temp-name-counter 0)
864 (make-variable-buffer-local 'js2-temp-name-counter)
865
866 (defvar js2-parse-stmt-count 0)
867 (make-variable-buffer-local 'js2-parse-stmt-count)
868
869 (defsubst js2-get-next-temp-name ()
870 (format "$%d" (incf js2-temp-name-counter)))
871
872 (defvar js2-parse-interruptable-p t
873 "Set this to nil to force parse to continue until finished.
874 This will mostly be useful for interpreters.")
875
876 (defvar js2-statements-per-pause 50
877 "Pause after this many statements to check for user input.
878 If user input is pending, stop the parse and discard the tree.
879 This makes for a smoother user experience for large files.
880 You may have to wait a second or two before the highlighting
881 and error-reporting appear, but you can always type ahead if
882 you wish. This appears to be more or less how Eclipse, IntelliJ
883 and other editors work.")
884
885 (defvar js2-record-comments t
886 "Instructs the scanner to record comments in `js2-scanned-comments'.")
887 (make-variable-buffer-local 'js2-record-comments)
888
889 (defvar js2-scanned-comments nil
890 "List of all comments from the current parse.")
891 (make-variable-buffer-local 'js2-scanned-comments)
892
893 (defun js2-underline-color (color)
894 "Return a legal value for the :underline face attribute based on COLOR."
895 ;; In XEmacs the :underline attribute can only be a boolean.
896 ;; In GNU it can be the name of a colour.
897 (if (featurep 'xemacs)
898 (if color t nil)
899 color))
900
901 (defcustom js2-mode-indent-inhibit-undo nil
902 "Non-nil to disable collection of Undo information when indenting lines.
903 Some users have requested this behavior. It's nil by default because
904 other Emacs modes don't work this way."
905 :type 'boolean
906 :group 'js2-mode)
907
908 (defcustom js2-mode-indent-ignore-first-tab nil
909 "If non-nil, ignore first TAB keypress if we look indented properly.
910 It's fairly common for users to navigate to an already-indented line
911 and press TAB for reassurance that it's been indented. For this class
912 of users, we want the first TAB press on a line to be ignored if the
913 line is already indented to one of the precomputed alternatives.
914
915 This behavior is only partly implemented. If you TAB-indent a line,
916 navigate to another line, and then navigate back, it fails to clear
917 the last-indented variable, so it thinks you've already hit TAB once,
918 and performs the indent. A full solution would involve getting on the
919 point-motion hooks for the entire buffer. If we come across another
920 use cases that requires watching point motion, I'll consider doing it.
921
922 If you set this variable to nil, then the TAB key will always change
923 the indentation of the current line, if more than one alternative
924 indentation spot exists."
925 :type 'boolean
926 :group 'js2-mode)
927
928 (defvar js2-indent-hook nil
929 "A hook for user-defined indentation rules.
930
931 Functions on this hook should expect two arguments: (LIST INDEX)
932 The LIST argument is the list of computed indentation points for
933 the current line. INDEX is the list index of the indentation point
934 that `js2-bounce-indent' plans to use. If INDEX is nil, then the
935 indent function is not going to change the current line indentation.
936
937 If a hook function on this list returns a non-nil value, then
938 `js2-bounce-indent' assumes the hook function has performed its own
939 indentation, and will do nothing. If all hook functions on the list
940 return nil, then `js2-bounce-indent' will use its computed indentation
941 and reindent the line.
942
943 When hook functions on this hook list are called, the variable
944 `js2-mode-ast' may or may not be set, depending on whether the
945 parse tree is available. If the variable is nil, you can pass a
946 callback to `js2-mode-wait-for-parse', and your callback will be
947 called after the new parse tree is built. This can take some time
948 in large files.")
949
950 (defface js2-warning-face
951 `((((class color) (background light))
952 (:underline ,(js2-underline-color "orange")))
953 (((class color) (background dark))
954 (:underline ,(js2-underline-color "orange")))
955 (t (:underline t)))
956 "Face for JavaScript warnings."
957 :group 'js2-mode)
958
959 (defface js2-error-face
960 `((((class color) (background light))
961 (:foreground "red"))
962 (((class color) (background dark))
963 (:foreground "red"))
964 (t (:foreground "red")))
965 "Face for JavaScript errors."
966 :group 'js2-mode)
967
968 (defface js2-jsdoc-tag-face
969 '((t :foreground "SlateGray"))
970 "Face used to highlight @whatever tags in jsdoc comments."
971 :group 'js2-mode)
972
973 (defface js2-jsdoc-type-face
974 '((t :foreground "SteelBlue"))
975 "Face used to highlight {FooBar} types in jsdoc comments."
976 :group 'js2-mode)
977
978 (defface js2-jsdoc-value-face
979 '((t :foreground "PeachPuff3"))
980 "Face used to highlight tag values in jsdoc comments."
981 :group 'js2-mode)
982
983 (defface js2-function-param-face
984 '((t :foreground "SeaGreen"))
985 "Face used to highlight function parameters in javascript."
986 :group 'js2-mode)
987
988 (defface js2-instance-member-face
989 '((t :foreground "DarkOrchid"))
990 "Face used to highlight instance variables in javascript.
991 Not currently used."
992 :group 'js2-mode)
993
994 (defface js2-private-member-face
995 '((t :foreground "PeachPuff3"))
996 "Face used to highlight calls to private methods in javascript.
997 Not currently used."
998 :group 'js2-mode)
999
1000 (defface js2-private-function-call-face
1001 '((t :foreground "goldenrod"))
1002 "Face used to highlight calls to private functions in javascript.
1003 Not currently used."
1004 :group 'js2-mode)
1005
1006 (defface js2-jsdoc-html-tag-name-face
1007 (if js2-emacs22
1008 '((((class color) (min-colors 88) (background light))
1009 (:foreground "rosybrown"))
1010 (((class color) (min-colors 8) (background dark))
1011 (:foreground "yellow"))
1012 (((class color) (min-colors 8) (background light))
1013 (:foreground "magenta")))
1014 '((((type tty pc) (class color) (background light))
1015 (:foreground "magenta"))
1016 (((type tty pc) (class color) (background dark))
1017 (:foreground "yellow"))
1018 (t (:foreground "RosyBrown"))))
1019 "Face used to highlight jsdoc html tag names"
1020 :group 'js2-mode)
1021
1022 (defface js2-jsdoc-html-tag-delimiter-face
1023 (if js2-emacs22
1024 '((((class color) (min-colors 88) (background light))
1025 (:foreground "dark khaki"))
1026 (((class color) (min-colors 8) (background dark))
1027 (:foreground "green"))
1028 (((class color) (min-colors 8) (background light))
1029 (:foreground "green")))
1030 '((((type tty pc) (class color) (background light))
1031 (:foreground "green"))
1032 (((type tty pc) (class color) (background dark))
1033 (:foreground "green"))
1034 (t (:foreground "dark khaki"))))
1035 "Face used to highlight brackets in jsdoc html tags."
1036 :group 'js2-mode)
1037
1038 (defface js2-external-variable-face
1039 '((t :foreground "orange"))
1040 "Face used to highlight assignments to undeclared variables.
1041 An undeclared variable is any variable not declared with var or let
1042 in the current scope or any lexically enclosing scope. If you assign
1043 to such a variable, then you are either expecting it to originate from
1044 another file, or you've got a potential bug."
1045 :group 'js2-mode)
1046
1047 (defcustom js2-highlight-external-variables t
1048 "Non-nil to higlight assignments to undeclared variables."
1049 :type 'boolean
1050 :group 'js2-mode)
1051
1052 (defvar js2-mode-map
1053 (let ((map (make-sparse-keymap))
1054 keys)
1055 (define-key map [mouse-1] #'js2-mode-show-node)
1056 (define-key map "\C-m" #'js2-enter-key)
1057 (when js2-rebind-eol-bol-keys
1058 (define-key map "\C-a" #'js2-beginning-of-line)
1059 (define-key map "\C-e" #'js2-end-of-line))
1060 (define-key map "\C-c\C-e" #'js2-mode-hide-element)
1061 (define-key map "\C-c\C-s" #'js2-mode-show-element)
1062 (define-key map "\C-c\C-a" #'js2-mode-show-all)
1063 (define-key map "\C-c\C-f" #'js2-mode-toggle-hide-functions)
1064 (define-key map "\C-c\C-t" #'js2-mode-toggle-hide-comments)
1065 (define-key map "\C-c\C-o" #'js2-mode-toggle-element)
1066 (define-key map "\C-c\C-w" #'js2-mode-toggle-warnings-and-errors)
1067 (define-key map (kbd "C-c C-'") #'js2-next-error)
1068 ;; also define user's preference for next-error, if available
1069 (if (setq keys (where-is-internal #'next-error))
1070 (define-key map (car keys) #'js2-next-error))
1071 (define-key map (or (car (where-is-internal #'mark-defun))
1072 (kbd "M-C-h"))
1073 #'js2-mark-defun)
1074 (define-key map (or (car (where-is-internal #'narrow-to-defun))
1075 (kbd "C-x nd"))
1076 #'js2-narrow-to-defun)
1077 (define-key map [down-mouse-3] #'js2-mouse-3)
1078 (when js2-auto-indent-flag
1079 (mapc (lambda (key)
1080 (define-key map key #'js2-insert-and-indent))
1081 js2-electric-keys))
1082
1083 (define-key map [menu-bar javascript]
1084 (cons "JavaScript" (make-sparse-keymap "JavaScript")))
1085
1086 (define-key map [menu-bar javascript customize-js2-mode]
1087 '(menu-item "Customize js2-mode" js2-mode-customize
1088 :help "Customize the behavior of this mode"))
1089
1090 (define-key map [menu-bar javascript js2-force-refresh]
1091 '(menu-item "Force buffer refresh" js2-mode-reset
1092 :help "Re-parse the buffer from scratch"))
1093
1094 (define-key map [menu-bar javascript separator-2]
1095 '("--"))
1096
1097 (define-key map [menu-bar javascript next-error]
1098 '(menu-item "Next warning or error" js2-next-error
1099 :enabled (and js2-mode-ast
1100 (or (js2-ast-root-errors js2-mode-ast)
1101 (js2-ast-root-warnings js2-mode-ast)))
1102 :help "Move to next warning or error"))
1103
1104 (define-key map [menu-bar javascript display-errors]
1105 '(menu-item "Show errors and warnings" js2-mode-display-warnings-and-errors
1106 :visible (not js2-mode-show-parse-errors)
1107 :help "Turn on display of warnings and errors"))
1108
1109 (define-key map [menu-bar javascript hide-errors]
1110 '(menu-item "Hide errors and warnings" js2-mode-hide-warnings-and-errors
1111 :visible js2-mode-show-parse-errors
1112 :help "Turn off display of warnings and errors"))
1113
1114 (define-key map [menu-bar javascript separator-1]
1115 '("--"))
1116
1117 (define-key map [menu-bar javascript js2-toggle-function]
1118 '(menu-item "Show/collapse element" js2-mode-toggle-element
1119 :help "Hide or show function body or comment"))
1120
1121 (define-key map [menu-bar javascript show-comments]
1122 '(menu-item "Show block comments" js2-mode-toggle-hide-comments
1123 :visible js2-mode-comments-hidden
1124 :help "Expand all hidden block comments"))
1125
1126 (define-key map [menu-bar javascript hide-comments]
1127 '(menu-item "Hide block comments" js2-mode-toggle-hide-comments
1128 :visible (not js2-mode-comments-hidden)
1129 :help "Show block comments as /*...*/"))
1130
1131 (define-key map [menu-bar javascript show-all-functions]
1132 '(menu-item "Show function bodies" js2-mode-toggle-hide-functions
1133 :visible js2-mode-functions-hidden
1134 :help "Expand all hidden function bodies"))
1135
1136 (define-key map [menu-bar javascript hide-all-functions]
1137 '(menu-item "Hide function bodies" js2-mode-toggle-hide-functions
1138 :visible (not js2-mode-functions-hidden)
1139 :help "Show {...} for all top-level function bodies"))
1140
1141 map)
1142 "Keymap used in `js2-mode' buffers.")
1143
1144 (defconst js2-mode-identifier-re "[a-zA-Z_$][a-zA-Z0-9_$]*")
1145
1146 (defvar js2-mode-//-comment-re "^\\(\\s-*\\)//.+"
1147 "Matches a //-comment line. Must be first non-whitespace on line.
1148 First match-group is the leading whitespace.")
1149
1150 (defvar js2-mode-ast nil "Private variable.")
1151 (make-variable-buffer-local 'js2-mode-ast)
1152
1153 (defvar js2-mode-hook nil)
1154
1155 (defvar js2-mode-parse-timer nil "Private variable.")
1156 (make-variable-buffer-local 'js2-mode-parse-timer)
1157
1158 (defvar js2-mode-buffer-dirty-p nil "Private variable.")
1159 (make-variable-buffer-local 'js2-mode-buffer-dirty-p)
1160
1161 (defvar js2-mode-parsing nil "Private variable.")
1162 (make-variable-buffer-local 'js2-mode-parsing)
1163
1164 (defvar js2-mode-node-overlay nil)
1165 (make-variable-buffer-local 'js2-mode-node-overlay)
1166
1167 (defvar js2-mode-show-overlay js2-mode-dev-mode-p
1168 "Debug: Non-nil to highlight AST nodes on mouse-down.")
1169
1170 (defvar js2-mode-fontifications nil "Private variable")
1171 (make-variable-buffer-local 'js2-mode-fontifications)
1172
1173 (defvar js2-mode-deferred-properties nil "Private variable")
1174 (make-variable-buffer-local 'js2-mode-deferred-properties)
1175
1176 (defvar js2-imenu-recorder nil "Private variable")
1177 (make-variable-buffer-local 'js2-imenu-recorder)
1178
1179 (defvar js2-imenu-function-map nil "Private variable")
1180 (make-variable-buffer-local 'js2-imenu-function-map)
1181
1182 (defvar js2-paragraph-start
1183 "\\(@[a-zA-Z]+\\>\\|$\\)")
1184
1185 ;; Note that we also set a 'c-in-sws text property in html comments,
1186 ;; so that `c-forward-sws' and `c-backward-sws' work properly.
1187 (defvar js2-syntactic-ws-start
1188 "\\s \\|/[*/]\\|[\n\r]\\|\\\\[\n\r]\\|\\s!\\|<!--\\|^\\s-*-->")
1189
1190 (defvar js2-syntactic-ws-end
1191 "\\s \\|[\n\r/]\\|\\s!")
1192
1193 (defvar js2-syntactic-eol
1194 (concat "\\s *\\(/\\*[^*\n\r]*"
1195 "\\(\\*+[^*\n\r/][^*\n\r]*\\)*"
1196 "\\*+/\\s *\\)*"
1197 "\\(//\\|/\\*[^*\n\r]*"
1198 "\\(\\*+[^*\n\r/][^*\n\r]*\\)*$"
1199 "\\|\\\\$\\|$\\)")
1200 "Copied from java-mode. Needed for some cc-engine functions.")
1201
1202 (defvar js2-comment-prefix-regexp
1203 "//+\\|\\**")
1204
1205 (defvar js2-comment-start-skip
1206 "\\(//+\\|/\\*+\\)\\s *")
1207
1208 (defvar js2-mode-verbose-parse-p js2-mode-dev-mode-p
1209 "Non-nil to emit status messages during parsing.")
1210
1211 (defvar js2-mode-functions-hidden nil "private variable")
1212 (defvar js2-mode-comments-hidden nil "private variable")
1213
1214 (defvar js2-mode-syntax-table
1215 (let ((table (make-syntax-table)))
1216 (c-populate-syntax-table table)
1217 table)
1218 "Syntax table used in js2-mode buffers.")
1219
1220 (defvar js2-mode-abbrev-table nil
1221 "Abbrev table in use in `js2-mode' buffers.")
1222 (define-abbrev-table 'js2-mode-abbrev-table ())
1223
1224 (defvar js2-mode-must-byte-compile (not js2-mode-dev-mode-p)
1225 "Non-nil to have `js2-mode' signal an error if not byte-compiled.")
1226
1227 (defvar js2-mode-pending-parse-callbacks nil
1228 "List of functions waiting to be notified that parse is finished.")
1229
1230 (defvar js2-mode-last-indented-line -1)
1231
1232 (eval-when-compile
1233 (defvar c-paragraph-start nil)
1234 (defvar c-paragraph-separate nil)
1235 (defvar c-syntactic-ws-start nil)
1236 (defvar c-syntactic-ws-end nil)
1237 (defvar c-syntactic-eol nil)
1238 (defvar running-xemacs nil)
1239 (defvar font-lock-mode nil)
1240 (defvar font-lock-keywords nil))
1241
1242 (eval-when-compile
1243 (if (< emacs-major-version 22)
1244 (defun c-setup-paragraph-variables () nil)))
1245
1246 (provide 'js2-vars)
1247
1248 ;;; js2-vars.el ends here
1249 ;;; js2-util.el -- JavaScript utilities
1250
1251 ;; Author: Steve Yegge (steve.yegge@gmail.com)
1252 ;; Keywords: javascript languages
1253
1254 ;;; Code
1255
1256 (eval-when-compile
1257 (require 'cl))
1258
1259
1260 ;; Emacs21 compatibility, plus some stuff to avoid runtime dependency on CL
1261
1262 (unless (fboundp #'looking-back)
1263 (defun looking-back (regexp &optional limit greedy)
1264 "Return non-nil if text before point matches regular expression REGEXP.
1265 Like `looking-at' except matches before point, and is slower.
1266 LIMIT if non-nil speeds up the search by specifying a minimum
1267 starting position, to avoid checking matches that would start
1268 before LIMIT.
1269
1270 If GREEDY is non-nil, extend the match backwards as far as possible,
1271 stopping when a single additional previous character cannot be part
1272 of a match for REGEXP."
1273 (let ((start (point))
1274 (pos
1275 (save-excursion
1276 (and (re-search-backward (concat "\\(?:" regexp "\\)\\=") limit t)
1277 (point)))))
1278 (if (and greedy pos)
1279 (save-restriction
1280 (narrow-to-region (point-min) start)
1281 (while (and (> pos (point-min))
1282 (save-excursion
1283 (goto-char pos)
1284 (backward-char 1)
1285 (looking-at (concat "\\(?:" regexp "\\)\\'"))))
1286 (setq pos (1- pos)))
1287 (save-excursion
1288 (goto-char pos)
1289 (looking-at (concat "\\(?:" regexp "\\)\\'")))))
1290 (not (null pos)))))
1291
1292 (unless (fboundp #'copy-overlay)
1293 (defun copy-overlay (o)
1294 "Return a copy of overlay O."
1295 (let ((o1 (make-overlay (overlay-start o) (overlay-end o)
1296 ;; FIXME: there's no easy way to find the
1297 ;; insertion-type of the two markers.
1298 (overlay-buffer o)))
1299 (props (overlay-properties o)))
1300 (while props
1301 (overlay-put o1 (pop props) (pop props)))
1302 o1)))
1303
1304 (unless (fboundp #'remove-overlays)
1305 (defun remove-overlays (&optional beg end name val)
1306 "Clear BEG and END of overlays whose property NAME has value VAL.
1307 Overlays might be moved and/or split.
1308 BEG and END default respectively to the beginning and end of buffer."
1309 (unless beg (setq beg (point-min)))
1310 (unless end (setq end (point-max)))
1311 (if (< end beg)
1312 (setq beg (prog1 end (setq end beg))))
1313 (save-excursion
1314 (dolist (o (overlays-in beg end))
1315 (when (eq (overlay-get o name) val)
1316 ;; Either push this overlay outside beg...end
1317 ;; or split it to exclude beg...end
1318 ;; or delete it entirely (if it is contained in beg...end).
1319 (if (< (overlay-start o) beg)
1320 (if (> (overlay-end o) end)
1321 (progn
1322 (move-overlay (copy-overlay o)
1323 (overlay-start o) beg)
1324 (move-overlay o end (overlay-end o)))
1325 (move-overlay o (overlay-start o) beg))
1326 (if (> (overlay-end o) end)
1327 (move-overlay o end (overlay-end o))
1328 (delete-overlay o))))))))
1329
1330 ;; we don't want a runtime dependency on the CL package, so define
1331 ;; our own versions of these functions.
1332
1333 (defun js2-delete-if (predicate list)
1334 "Remove all items satisfying PREDICATE in LIST."
1335 (loop for item in list
1336 if (not (funcall predicate item))
1337 collect item))
1338
1339 (defun js2-position (element list)
1340 "Find 0-indexed position of ELEMENT in LIST comparing with `eq'.
1341 Returns nil if element is not found in the list."
1342 (let ((count 0)
1343 found)
1344 (while (and list (not found))
1345 (if (eq element (car list))
1346 (setq found t)
1347 (setq count (1+ count)
1348 list (cdr list))))
1349 (if found count)))
1350
1351 (defun js2-find-if (predicate list)
1352 "Find first item satisfying PREDICATE in LIST."
1353 (let (result)
1354 (while (and list (not result))
1355 (if (funcall predicate (car list))
1356 (setq result (car list)))
1357 (setq list (cdr list)))
1358 result))
1359
1360 ;;; end Emacs 21 compat
1361
1362 (defmacro js2-time (form)
1363 "Evaluate FORM, discard result, and return elapsed time in sec"
1364 (let ((beg (make-symbol "--js2-time-beg--"))
1365 (delta (make-symbol "--js2-time-end--")))
1366 `(let ((,beg (current-time))
1367 ,delta)
1368 ,form
1369 (/ (truncate (* (- (float-time (current-time))
1370 (float-time ,beg)))
1371 10000)
1372 10000.0))))
1373
1374 (def-edebug-spec js2-time t)
1375
1376 (defsubst neq (expr1 expr2)
1377 "Return (not (eq expr1 expr2))."
1378 (not (eq expr1 expr2)))
1379
1380 (defsubst js2-same-line (pos)
1381 "Return t if POS is on the same line as current point."
1382 (and (>= pos (point-at-bol))
1383 (<= pos (point-at-eol))))
1384
1385 (defsubst js2-same-line-2 (p1 p2)
1386 "Return t if p1 is on the same line as p2."
1387 (save-excursion
1388 (goto-char p1)
1389 (js2-same-line p2)))
1390
1391 (defun js2-code-bug ()
1392 "Signal an error when we encounter an unexpected code path."
1393 (error "failed assertion"))
1394
1395 ;; I'd like to associate errors with nodes, but for now the
1396 ;; easiest thing to do is get the context info from the last token.
1397 (defsubst js2-record-parse-error (msg &optional arg pos len)
1398 (push (list (list msg arg)
1399 (or pos js2-token-beg)
1400 (or len (- js2-token-end js2-token-beg)))
1401 js2-parsed-errors))
1402
1403 (defsubst js2-report-error (msg &optional msg-arg pos len)
1404 "Signal a syntax error or record a parse error."
1405 (if js2-recover-from-parse-errors
1406 (js2-record-parse-error msg msg-arg pos len)
1407 (signal 'js2-syntax-error
1408 (list msg
1409 js2-ts-lineno
1410 (save-excursion
1411 (goto-char js2-ts-cursor)
1412 (current-column))
1413 js2-ts-hit-eof))))
1414
1415 (defsubst js2-report-warning (msg &optional msg-arg pos len)
1416 (if js2-compiler-report-warning-as-error
1417 (js2-report-error msg msg-arg pos len)
1418 (push (list (list msg msg-arg)
1419 (or pos js2-token-beg)
1420 (or len (- js2-token-end js2-token-beg)))
1421 js2-parsed-warnings)))
1422
1423 (defsubst js2-add-strict-warning (msg-id &optional msg-arg beg end)
1424 (if js2-compiler-strict-mode
1425 (js2-report-warning msg-id msg-arg beg
1426 (and beg end (- end beg)))))
1427
1428 (put 'js2-syntax-error 'error-conditions
1429 '(error syntax-error js2-syntax-error))
1430 (put 'js2-syntax-error 'error-message "Syntax error")
1431
1432 (put 'js2-parse-error 'error-conditions
1433 '(error parse-error js2-parse-error))
1434 (put 'js2-parse-error 'error-message "Parse error")
1435
1436 (defmacro js2-clear-flag (flags flag)
1437 `(setq ,flags (logand ,flags (lognot ,flag))))
1438
1439 (defmacro js2-set-flag (flags flag)
1440 "Logical-or FLAG into FLAGS."
1441 `(setq ,flags (logior ,flags ,flag)))
1442
1443 (defsubst js2-flag-set-p (flags flag)
1444 (/= 0 (logand flags flag)))
1445
1446 (defsubst js2-flag-not-set-p (flags flag)
1447 (zerop (logand flags flag)))
1448
1449 ;; Stolen shamelessly from James Clark's nxml-mode.
1450 (defmacro js2-with-unmodifying-text-property-changes (&rest body)
1451 "Evaluate BODY without any text property changes modifying the buffer.
1452 Any text properties changes happen as usual but the changes are not treated as
1453 modifications to the buffer."
1454 (let ((modified (make-symbol "modified")))
1455 `(let ((,modified (buffer-modified-p))
1456 (inhibit-read-only t)
1457 (inhibit-modification-hooks t)
1458 (buffer-undo-list t)
1459 (deactivate-mark nil)
1460 ;; Apparently these avoid file locking problems.
1461 (buffer-file-name nil)
1462 (buffer-file-truename nil))
1463 (unwind-protect
1464 (progn ,@body)
1465 (unless ,modified
1466 (restore-buffer-modified-p nil))))))
1467
1468 (put 'js2-with-unmodifying-text-property-changes 'lisp-indent-function 0)
1469 (def-edebug-spec js2-with-unmodifying-text-property-changes t)
1470
1471 (defmacro js2-with-underscore-as-word-syntax (&rest body)
1472 "Evaluate BODY with the _ character set to be word-syntax."
1473 (let ((old-syntax (make-symbol "old-syntax")))
1474 `(let ((,old-syntax (string (char-syntax ?_))))
1475 (unwind-protect
1476 (progn
1477 (modify-syntax-entry ?_ "w" js2-mode-syntax-table)
1478 ,@body)
1479 (modify-syntax-entry ?_ ,old-syntax js2-mode-syntax-table)))))
1480
1481 (put 'js2-with-underscore-as-word-syntax 'lisp-indent-function 0)
1482 (def-edebug-spec js2-with-underscore-as-word-syntax t)
1483
1484 (defmacro with-buffer (buf form)
1485 "Executes FORM in buffer BUF.
1486 BUF can be a buffer name or a buffer object.
1487 If the buffer doesn't exist, it's created."
1488 `(let ((buffer (gentemp)))
1489 (setq buffer
1490 (if (stringp ,buf)
1491 (get-buffer-create ,buf)
1492 ,buf))
1493 (save-excursion
1494 (set-buffer buffer)
1495 ,form)))
1496
1497 (defsubst char-is-uppercase (c)
1498 "Return t if C is an uppercase character.
1499 Handles unicode and latin chars properly."
1500 (/= c (downcase c)))
1501
1502 (defsubst char-is-lowercase (c)
1503 "Return t if C is an uppercase character.
1504 Handles unicode and latin chars properly."
1505 (/= c (upcase c)))
1506
1507 (put 'with-buffer 'lisp-indent-function 1)
1508 (def-edebug-spec with-buffer t)
1509
1510 (provide 'js2-util)
1511
1512 ;;; js2-util.el ends here
1513 ;;; js2-scan.el --- JavaScript scanner
1514
1515 ;; Author: Steve Yegge (steve.yegge@gmail.com)
1516 ;; Keywords: javascript languages
1517
1518 ;;; Commentary:
1519
1520 ;; A port of Mozilla Rhino's scanner.
1521 ;; Corresponds to Rhino files Token.java and TokenStream.java.
1522
1523 ;;; Code:
1524
1525
1526 (eval-when-compile
1527 (require 'cl))
1528
1529 (defvar js2-tokens nil
1530 "List of all defined token names.") ; intialized below
1531
1532 (defvar js2-token-names
1533 (let* ((names (make-vector js2-num-tokens -1))
1534 (case-fold-search nil) ; only match js2-UPPER_CASE
1535 (syms (apropos-internal "^js2-\\(?:[A-Z_]+\\)")))
1536 (loop for sym in syms
1537 for i from 0
1538 do
1539 (unless (or (memq sym '(js2-EOF_CHAR js2-ERROR))
1540 (not (boundp sym)))
1541 (aset names (symbol-value sym) ; code, e.g. 152
1542 (substring (symbol-name sym) 4)) ; name, e.g. "LET"
1543 (push sym js2-tokens)))
1544 names)
1545 "Vector mapping int values to token string names, sans `js2-' prefix.")
1546
1547 (defun js2-token-name (tok)
1548 "Return a string name for TOK, a token symbol or code.
1549 Signals an error if it's not a recognized token."
1550 (let ((code tok))
1551 (if (symbolp tok)
1552 (setq code (symbol-value tok)))
1553 (if (eq code -1)
1554 "ERROR"
1555 (if (and (numberp code)
1556 (not (minusp code))
1557 (< code js2-num-tokens))
1558 (aref js2-token-names code)
1559 (error "Invalid token: %s" code)))))
1560
1561 (defsubst js2-token-sym (tok)
1562 "Return symbol for TOK given its code, e.g. 'js2-LP for code 86."
1563 (intern (js2-token-name tok)))
1564
1565 (defvar js2-token-codes
1566 (let ((table (make-hash-table :test 'eq :size 256)))
1567 (loop for name across js2-token-names
1568 for sym = (intern (concat "js2-" name))
1569 do
1570 (puthash sym (symbol-value sym) table))
1571 ;; clean up a few that are "wrong" in Rhino's token codes
1572 (puthash 'js2-DELETE js2-DELPROP table)
1573 table)
1574 "Hashtable mapping token symbols to their bytecodes.")
1575
1576 (defsubst js2-token-code (sym)
1577 "Return code for token symbol SYM, e.g. 86 for 'js2-LP."
1578 (or (gethash sym js2-token-codes)
1579 (error "Invalid token symbol: %s " sym))) ; signal code bug
1580
1581 (defsubst js2-report-scan-error (msg &optional no-throw beg len)
1582 (setq js2-token-end js2-ts-cursor)
1583 (js2-report-error msg nil
1584 (or beg js2-token-beg)
1585 (or len (- js2-token-end js2-token-beg)))
1586 (unless no-throw
1587 (throw 'return js2-ERROR)))
1588
1589 (defsubst js2-get-string-from-buffer ()
1590 "Reverse the char accumulator and return it as a string."
1591 (setq js2-token-end js2-ts-cursor)
1592 (if js2-ts-string-buffer
1593 (apply #'string (nreverse js2-ts-string-buffer))
1594 ""))
1595
1596 ;; TODO: could potentially avoid a lot of consing by allocating a
1597 ;; char buffer the way Rhino does.
1598 (defsubst js2-add-to-string (c)
1599 (push c js2-ts-string-buffer))
1600
1601 ;; Note that when we "read" the end-of-file, we advance js2-ts-cursor
1602 ;; to (1+ (point-max)), which lets the scanner treat end-of-file like
1603 ;; any other character: when it's not part of the current token, we
1604 ;; unget it, allowing it to be read again by the following call.
1605 (defsubst js2-unget-char ()
1606 (decf js2-ts-cursor))
1607
1608 ;; Rhino distinguishes \r and \n line endings. We don't need to
1609 ;; because we only scan from Emacs buffers, which always use \n.
1610 (defsubst js2-get-char ()
1611 "Read and return the next character from the input buffer.
1612 Increments `js2-ts-lineno' if the return value is a newline char.
1613 Updates `js2-ts-cursor' to the point after the returned char.
1614 Returns `js2-EOF_CHAR' if we hit the end of the buffer.
1615 Also updates `js2-ts-hit-eof' and `js2-ts-line-start' as needed."
1616 (let (c)
1617 ;; check for end of buffer
1618 (if (>= js2-ts-cursor (point-max))
1619 (setq js2-ts-hit-eof t
1620 js2-ts-cursor (1+ js2-ts-cursor)
1621 c js2-EOF_CHAR) ; return value
1622
1623 ;; otherwise read next char
1624 (setq c (char-before (incf js2-ts-cursor)))
1625
1626 ;; if we read a newline, update counters
1627 (if (= c ?\n)
1628 (setq js2-ts-line-start js2-ts-cursor
1629 js2-ts-lineno (1+ js2-ts-lineno)))
1630
1631 ;; TODO: skip over format characters
1632 c)))
1633
1634 (defsubst js2-read-unicode-escape ()
1635 "Read a \\uNNNN sequence from the input.
1636 Assumes the ?\ and ?u have already been read.
1637 Returns the unicode character, or nil if it wasn't a valid character.
1638 Doesn't change the values of any scanner variables."
1639 ;; I really wish I knew a better way to do this, but I can't
1640 ;; find the Emacs function that takes a 16-bit int and converts
1641 ;; it to a Unicode/utf-8 character. So I basically eval it with (read).
1642 ;; Have to first check that it's 4 hex characters or it may stop
1643 ;; the read early.
1644 (ignore-errors
1645 (let ((s (buffer-substring-no-properties js2-ts-cursor
1646 (+ 4 js2-ts-cursor))))
1647 (if (string-match "[a-zA-Z0-9]\\{4\\}" s)
1648 (read (concat "?\\u" s))))))
1649
1650 (defsubst js2-match-char (test)
1651 "Consume and return next character if it matches TEST, a character.
1652 Returns nil and consumes nothing if TEST is not the next character."
1653 (let ((c (js2-get-char)))
1654 (if (eq c test)
1655 t
1656 (js2-unget-char)
1657 nil)))
1658
1659 (defsubst js2-peek-char ()
1660 (prog1
1661 (js2-get-char)
1662 (js2-unget-char)))
1663
1664 (defsubst js2-java-identifier-start-p (c)
1665 (or
1666 (memq c '(?$ ?_))
1667 (char-is-uppercase c)
1668 (char-is-lowercase c)))
1669
1670 (defsubst js2-java-identifier-part-p (c)
1671 "Implementation of java.lang.Character.isJavaIdentifierPart()"
1672 ;; TODO: make me Unicode-friendly. See comments above.
1673 (or
1674 (memq c '(?$ ?_))
1675 (char-is-uppercase c)
1676 (char-is-lowercase c)
1677 (and (>= c ?0) (<= c ?9))))
1678
1679 (defsubst js2-alpha-p (c)
1680 ;; Use 'Z' < 'a'
1681 (if (<= c ?Z)
1682 (<= ?A c)
1683 (and (<= ?a c)
1684 (<= c ?z))))
1685
1686 (defsubst js2-digit-p (c)
1687 (and (<= ?0 c) (<= c ?9)))
1688
1689 (defsubst js2-js-space-p (c)
1690 (if (<= c 127)
1691 (memq c '(#x20 #x9 #xC #xB))
1692 (or
1693 (eq c #xA0)
1694 ;; TODO: change this nil to check for Unicode space character
1695 nil)))
1696
1697 (defsubst js2-skip-line ()
1698 "Skip to end of line"
1699 (let (c)
1700 (while (and (/= js2-EOF_CHAR (setq c (js2-get-char)))
1701 (/= c ?\n)))
1702 (js2-unget-char)
1703 (setq js2-token-end js2-ts-cursor)))
1704
1705 (defun js2-init-scanner (&optional buf line)
1706 "Create token stream for BUF starting on LINE.
1707 BUF defaults to current-buffer and line defaults to 1.
1708
1709 A buffer can only have one scanner active at a time, which yields
1710 dramatically simpler code than using a defstruct. If you need to
1711 have simultaneous scanners in a buffer, copy the regions to scan
1712 into temp buffers."
1713 (save-excursion
1714 (when buf
1715 (set-buffer buf))
1716 (setq js2-ts-dirty-line nil
1717 js2-ts-regexp-flags nil
1718 js2-ts-string ""
1719 js2-ts-number nil
1720 js2-ts-hit-eof nil
1721 js2-ts-line-start 0
1722 js2-ts-lineno (or line 1)
1723 js2-ts-line-end-char -1
1724 js2-ts-cursor (point-min)
1725 js2-ts-is-xml-attribute nil
1726 js2-ts-xml-is-tag-content nil
1727 js2-ts-xml-open-tags-count 0
1728 js2-ts-string-buffer nil)))
1729
1730 ;; This function uses the cached op, string and number fields in
1731 ;; TokenStream; if getToken has been called since the passed token
1732 ;; was scanned, the op or string printed may be incorrect.
1733 (defun js2-token-to-string (token)
1734 ;; Not sure where this function is used in Rhino. Not tested.
1735 (if (not js2-debug-print-trees)
1736 ""
1737 (let ((name (js2-token-name token)))
1738 (cond
1739 ((memq token (list js2-STRING js2-REGEXP js2-NAME))
1740 (concat name " `" js2-ts-string "'"))
1741 ((eq token js2-NUMBER)
1742 (format "NUMBER %g" js2-ts-number))
1743 (t
1744 name)))))
1745
1746 (defconst js2-keywords
1747 '(break
1748 case catch const continue
1749 debugger default delete do
1750 else enum
1751 false finally for function
1752 if in instanceof import
1753 let
1754 new null
1755 return
1756 switch
1757 this throw true try typeof
1758 var void
1759 while with
1760 yield))
1761
1762 ;; Token names aren't exactly the same as the keywords, unfortunately.
1763 ;; E.g. enum isn't in the tokens, and delete is js2-DELPROP.
1764 (defconst js2-kwd-tokens
1765 (let ((table (make-vector js2-num-tokens nil))
1766 (tokens
1767 (list js2-BREAK
1768 js2-CASE js2-CATCH js2-CONST js2-CONTINUE
1769 js2-DEBUGGER js2-DEFAULT js2-DELPROP js2-DO
1770 js2-ELSE
1771 js2-FALSE js2-FINALLY js2-FOR js2-FUNCTION
1772 js2-IF js2-IN js2-INSTANCEOF js2-IMPORT
1773 js2-LET
1774 js2-NEW js2-NULL
1775 js2-RETURN
1776 js2-SWITCH
1777 js2-THIS js2-THROW js2-TRUE js2-TRY js2-TYPEOF
1778 js2-VAR
1779 js2-WHILE js2-WITH
1780 js2-YIELD)))
1781 (dolist (i tokens)
1782 (aset table i 'font-lock-keyword-face))
1783 (aset table js2-STRING 'font-lock-string-face)
1784 (aset table js2-REGEXP 'font-lock-string-face)
1785 (aset table js2-COMMENT 'font-lock-comment-face)
1786 (aset table js2-THIS 'font-lock-builtin-face)
1787 (aset table js2-VOID 'font-lock-constant-face)
1788 (aset table js2-NULL 'font-lock-constant-face)
1789 (aset table js2-TRUE 'font-lock-constant-face)
1790 (aset table js2-FALSE 'font-lock-constant-face)
1791 table)
1792 "Vector whose values are non-nil for tokens that are keywords.
1793 The values are default faces to use for highlighting the keywords.")
1794
1795 (defconst js2-reserved-words
1796 '(abstract
1797 boolean byte
1798 char class
1799 double
1800 enum extends
1801 final float
1802 goto
1803 implements int interface
1804 long
1805 native
1806 package private protected public
1807 short static super synchronized
1808 throws transient
1809 volatile))
1810
1811 (defconst js2-keyword-names
1812 (let ((table (make-hash-table :test 'equal)))
1813 (loop for k in js2-keywords
1814 do (puthash
1815 (symbol-name k) ; instanceof
1816 (intern (concat "js2-"
1817 (upcase (symbol-name k)))) ; js2-INSTANCEOF
1818 table))
1819 table)
1820 "JavaScript keywords by name, mapped to their symbols.")
1821
1822 (defconst js2-reserved-word-names
1823 (let ((table (make-hash-table :test 'equal)))
1824 (loop for k in js2-reserved-words
1825 do
1826 (puthash (symbol-name k) 'js2-RESERVED table))
1827 table)
1828 "JavaScript reserved words by name, mapped to 'js2-RESERVED.")
1829
1830 (defsubst js2-collect-string (buf)
1831 "Convert BUF, a list of chars, to a string.
1832 Reverses BUF before converting."
1833 (cond
1834 ((stringp buf)
1835 buf)
1836 ((null buf) ; for emacs21 compat
1837 "")
1838 (t
1839 (if buf
1840 (apply #'string (nreverse buf))
1841 ""))))
1842
1843 (defun js2-string-to-keyword (s)
1844 "Return token for S, a string, if S is a keyword or reserved word.
1845 Returns a symbol such as 'js2-BREAK, or nil if not keyword/reserved."
1846 (or (gethash s js2-keyword-names)
1847 (gethash s js2-reserved-word-names)))
1848
1849 (defsubst js2-ts-set-char-token-bounds ()
1850 "Used when next token is one character."
1851 (setq js2-token-beg (1- js2-ts-cursor)
1852 js2-token-end js2-ts-cursor))
1853
1854 (defsubst js2-ts-return (token)
1855 "Return an N-character TOKEN from `js2-get-token'.
1856 Updates `js2-token-end' accordingly."
1857 (setq js2-token-end js2-ts-cursor)
1858 (throw 'return token))
1859
1860 (defsubst js2-x-digit-to-int (c accumulator)
1861 "Build up a hex number.
1862 If C is a hexadecimal digit, return ACCUMULATOR * 16 plus
1863 corresponding number. Otherwise return -1."
1864 (catch 'return
1865 (catch 'check
1866 ;; Use 0..9 < A..Z < a..z
1867 (cond
1868 ((<= c ?9)
1869 (decf c ?0)
1870 (if (<= 0 c)
1871 (throw 'check nil)))
1872 ((<= c ?F)
1873 (when (<= ?A c)
1874 (decf c (- ?A 10))
1875 (throw 'check nil)))
1876 ((<= c ?f)
1877 (when (<= ?a c)
1878 (decf c (- ?a 10))
1879 (throw 'check nil))))
1880 (throw 'return -1))
1881 (logior c (lsh accumulator 4))))
1882
1883 (defun js2-get-token ()
1884 "Return next JavaScript token, an int such as js2-RETURN."
1885 (let (c
1886 c1
1887 identifier-start
1888 is-unicode-escape-start
1889 contains-escape
1890 escape-val
1891 escape-start
1892 str
1893 result
1894 base
1895 is-integer
1896 quote-char
1897 val
1898 look-for-slash
1899 continue)
1900 (catch 'return
1901 (while t
1902 ;; Eat whitespace, possibly sensitive to newlines.
1903 (setq continue t)
1904 (while continue
1905 (setq c (js2-get-char))
1906 (cond
1907 ((eq c js2-EOF_CHAR)
1908 (js2-ts-set-char-token-bounds)
1909 (throw 'return js2-EOF))
1910 ((eq c ?\n)
1911 (js2-ts-set-char-token-bounds)
1912 (setq js2-ts-dirty-line nil)
1913 (throw 'return js2-EOL))
1914 ((not (js2-js-space-p c))
1915 (if (/= c ?-) ; in case end of HTML comment
1916 (setq js2-ts-dirty-line t))
1917 (setq continue nil))))
1918
1919 ;; Assume the token will be 1 char - fixed up below.
1920 (js2-ts-set-char-token-bounds)
1921
1922 (when (eq c ?@)
1923 (throw 'return js2-XMLATTR))
1924
1925 ;; identifier/keyword/instanceof?
1926 ;; watch out for starting with a <backslash>
1927 (cond
1928 ((eq c ?\\)
1929 (setq c (js2-get-char))
1930 (if (eq c ?u)
1931 (setq identifier-start t
1932 is-unicode-escape-start t
1933 js2-ts-string-buffer nil)
1934 (setq identifier-start nil)
1935 (js2-unget-char)
1936 (setq c ?\\)))
1937 (t
1938 (when (setq identifier-start (js2-java-identifier-start-p c))
1939 (setq js2-ts-string-buffer nil)
1940 (js2-add-to-string c))))
1941
1942 (when identifier-start
1943 (setq contains-escape is-unicode-escape-start)
1944 (catch 'break
1945 (while t
1946 (if is-unicode-escape-start
1947 ;; strictly speaking we should probably push-back
1948 ;; all the bad characters if the <backslash>uXXXX
1949 ;; sequence is malformed. But since there isn't a
1950 ;; correct context(is there?) for a bad Unicode
1951 ;; escape sequence in an identifier, we can report
1952 ;; an error here.
1953 (progn
1954 (setq escape-val 0)
1955 (dotimes (i 4)
1956 (setq c (js2-get-char)
1957 escape-val (js2-x-digit-to-int c escape-val))
1958 ;; Next check takes care of c < 0 and bad escape
1959 (if (minusp escape-val)
1960 (throw 'break nil)))
1961 (if (minusp escape-val)
1962 (js2-report-scan-error "msg.invalid.escape" t))
1963 (js2-add-to-string escape-val)
1964 (setq is-unicode-escape-start nil))
1965 (setq c (js2-get-char))
1966 (cond
1967 ((eq c ?\\)
1968 (setq c (js2-get-char))
1969 (if (eq c ?u)
1970 (setq is-unicode-escape-start t
1971 contains-escape t)
1972 (js2-report-scan-error "msg.illegal.character" t)))
1973 (t
1974 (if (or (eq c js2-EOF_CHAR)
1975 (not (js2-java-identifier-part-p c)))
1976 (throw 'break nil))
1977 (js2-add-to-string c))))))
1978 (js2-unget-char)
1979
1980 (setq str (js2-get-string-from-buffer))
1981 (unless contains-escape
1982 ;; OPT we shouldn't have to make a string (object!) to
1983 ;; check if it's a keyword.
1984
1985 ;; Return the corresponding token if it's a keyword
1986 (when (setq result (js2-string-to-keyword str))
1987 (if (and (< js2-language-version 170)
1988 (memq result '(js2-LET js2-YIELD)))
1989 ;; LET and YIELD are tokens only in 1.7 and later
1990 (setq result 'js2-NAME))
1991 (if (neq result js2-RESERVED)
1992 (throw 'return (js2-token-code result)))
1993 (js2-report-warning "msg.reserved.keyword" str)))
1994
1995 ;; If we want to intern these as Rhino does, just use (intern str)
1996 (setq js2-ts-string str)
1997 (throw 'return js2-NAME)) ; end identifier/kwd check
1998
1999 ;; is it a number?
2000 (when (or (js2-digit-p c)
2001 (and (eq c ?.) (js2-digit-p (js2-peek-char))))
2002 (setq js2-ts-string-buffer nil
2003 base 10)
2004 (when (eq c ?0)
2005 (setq c (js2-get-char))
2006 (cond
2007 ((or (eq c ?x) (eq c ?X))
2008 (setq base 16)
2009 (setq c (js2-get-char)))
2010 ((js2-digit-p c)
2011 (setq base 8))
2012 (t
2013 (js2-add-to-string ?0))))
2014
2015 (if (eq base 16)
2016 (while (<= 0 (js2-x-digit-to-int c 0))
2017 (js2-add-to-string c)
2018 (setq c (js2-get-char)))
2019 (while (and (<= ?0 c) (<= c ?9))
2020 ;; We permit 08 and 09 as decimal numbers, which
2021 ;; makes our behavior a superset of the ECMA
2022 ;; numeric grammar. We might not always be so
2023 ;; permissive, so we warn about it.
2024 (when (and (eq base 8) (>= c ?8))
2025 (js2-report-warning "msg.bad.octal.literal"
2026 (if (eq c ?8) "8" "9"))
2027 (setq base 10))
2028 (js2-add-to-string c)
2029 (setq c (js2-get-char))))
2030
2031 (setq is-integer t)
2032
2033 (when (and (eq base 10) (memq c '(?. ?e ?E)))
2034 (setq is-integer nil)
2035 (when (eq c ?.)
2036 (loop do
2037 (js2-add-to-string c)
2038 (setq c (js2-get-char))
2039 while (js2-digit-p c)))
2040 (when (memq c '(?e ?E))
2041 (js2-add-to-string c)
2042 (setq c (js2-get-char))
2043 (when (memq c '(?+ ?-))
2044 (js2-add-to-string c)
2045 (setq c (js2-get-char)))
2046 (unless (js2-digit-p c)
2047 (js2-report-scan-error "msg.missing.exponent" t))
2048 (loop do
2049 (js2-add-to-string c)
2050 (setq c (js2-get-char))
2051 while (js2-digit-p c))))
2052
2053 (js2-unget-char)
2054 (setq js2-ts-string (js2-get-string-from-buffer)
2055 js2-ts-number
2056 (if (and (eq base 10) (not is-integer))
2057 (string-to-number js2-ts-string)
2058 ;; TODO: call runtime number-parser. Some of it is in
2059 ;; js2-util.el, but I need to port ScriptRuntime.stringToNumber.
2060 (string-to-number js2-ts-string)))
2061 (throw 'return js2-NUMBER))
2062
2063 ;; is it a string?
2064 (when (memq c '(?\" ?\'))
2065 ;; We attempt to accumulate a string the fast way, by
2066 ;; building it directly out of the reader. But if there
2067 ;; are any escaped characters in the string, we revert to
2068 ;; building it out of a string buffer.
2069 (setq quote-char c
2070 js2-ts-string-buffer nil
2071 c (js2-get-char))
2072 (catch 'break
2073 (while (/= c quote-char)
2074 (catch 'continue
2075 (when (or (eq c ?\n) (eq c js2-EOF_CHAR))
2076 (js2-unget-char)
2077 (setq js2-token-end js2-ts-cursor)
2078 (js2-report-error "msg.unterminated.string.lit")
2079 (throw 'return js2-STRING))
2080
2081 (when (eq c ?\\)
2082 ;; We've hit an escaped character
2083 (setq c (js2-get-char))
2084 (case c
2085 (?b (setq c ?\b))
2086 (?f (setq c ?\f))
2087 (?n (setq c ?\n))
2088 (?r (setq c ?\r))
2089 (?t (setq c ?\t))
2090 (?v (setq c ?\v))
2091 (?u
2092 (setq c1 (js2-read-unicode-escape))
2093 (if js2-parse-ide-mode
2094 (if c1
2095 (progn
2096 ;; just copy the string in IDE-mode
2097 (js2-add-to-string ?\\)
2098 (js2-add-to-string ?u)
2099 (dotimes (i 3)
2100 (js2-add-to-string (js2-get-char)))
2101 (setq c (js2-get-char))) ; added at end of loop
2102 ;; flag it as an invalid escape
2103 (js2-report-warning "msg.invalid.escape"
2104 nil (- js2-ts-cursor 2) 6))
2105 ;; Get 4 hex digits; if the u escape is not
2106 ;; followed by 4 hex digits, use 'u' + the
2107 ;; literal character sequence that follows.
2108 (js2-add-to-string ?u)
2109 (setq escape-val 0)
2110 (dotimes (i 4)
2111 (setq c (js2-get-char)
2112 escape-val (js2-x-digit-to-int c escape-val))
2113 (if (minusp escape-val)
2114 (throw 'continue nil))
2115 (js2-add-to-string c))
2116 ;; prepare for replace of stored 'u' sequence by escape value
2117 (setq js2-ts-string-buffer (nthcdr 5 js2-ts-string-buffer)
2118 c escape-val)))
2119 (?x
2120 ;; Get 2 hex digits, defaulting to 'x'+literal
2121 ;; sequence, as above.
2122 (setq c (js2-get-char)
2123 escape-val (js2-x-digit-to-int c 0))
2124 (if (minusp escape-val)
2125 (progn
2126 (js2-add-to-string ?x)
2127 (throw 'continue nil))
2128 (setq c1 c
2129 c (js2-get-char)
2130 escape-val (js2-x-digit-to-int c escape-val))
2131 (if (minusp escape-val)
2132 (progn
2133 (js2-add-to-string ?x)
2134 (js2-add-to-string c1)
2135 (throw 'continue nil))
2136 ;; got 2 hex digits
2137 (setq c escape-val))))
2138 (?\n
2139 ;; Remove line terminator after escape to follow
2140 ;; SpiderMonkey and C/C++
2141 (setq c (js2-get-char))
2142 (throw 'continue nil))
2143 (t
2144 (when (and (<= ?0 c) (< c ?8))
2145 (setq val (- c ?0)
2146 c (js2-get-char))
2147 (when (and (<= ?0 c) (< c ?8))
2148 (setq val (- (+ (* 8 val) c) ?0)
2149 c (js2-get-char))
2150 (when (and (<= ?0 c)
2151 (< c ?8)
2152 (< val #o37))
2153 ;; c is 3rd char of octal sequence only
2154 ;; if the resulting val <= 0377
2155 (setq val (- (+ (* 8 val) c) ?0)
2156 c (js2-get-char))))
2157 (js2-unget-char)
2158 (setq c val)))))
2159 (js2-add-to-string c)
2160 (setq c (js2-get-char)))))
2161 (setq js2-ts-string (js2-get-string-from-buffer))
2162 (throw 'return js2-STRING))
2163
2164 (case c
2165 (?\;
2166 (throw 'return js2-SEMI))
2167 (?\[
2168 (throw 'return js2-LB))
2169 (?\]
2170 (throw 'return js2-RB))
2171 (?{
2172 (throw 'return js2-LC))
2173 (?}
2174 (throw 'return js2-RC))
2175 (?\(
2176 (throw 'return js2-LP))
2177 (?\)
2178 (throw 'return js2-RP))
2179 (?,
2180 (throw 'return js2-COMMA))
2181 (??
2182 (throw 'return js2-HOOK))
2183 (?:
2184 (if (js2-match-char ?:)
2185 (js2-ts-return js2-COLONCOLON)
2186 (throw 'return js2-COLON)))
2187 (?.
2188 (if (js2-match-char ?.)
2189 (js2-ts-return js2-DOTDOT)
2190 (if (js2-match-char ?\()
2191 (js2-ts-return js2-DOTQUERY)
2192 (throw 'return js2-DOT))))
2193 (?|
2194 (if (js2-match-char ?|)
2195 (throw 'return js2-OR)
2196 (if (js2-match-char ?=)
2197 (js2-ts-return js2-ASSIGN_BITOR)
2198 (throw 'return js2-BITOR))))
2199 (?^
2200 (if (js2-match-char ?=)
2201 (js2-ts-return js2-ASSIGN_BITOR)
2202 (throw 'return js2-BITXOR)))
2203 (?&
2204 (if (js2-match-char ?&)
2205 (throw 'return js2-AND)
2206 (if (js2-match-char ?=)
2207 (js2-ts-return js2-ASSIGN_BITAND)
2208 (throw 'return js2-BITAND))))
2209 (?=
2210 (if (js2-match-char ?=)
2211 (if (js2-match-char ?=)
2212 (js2-ts-return js2-SHEQ)
2213 (throw 'return js2-EQ))
2214 (throw 'return js2-ASSIGN)))
2215 (?!
2216 (if (js2-match-char ?=)
2217 (if (js2-match-char ?=)
2218 (js2-ts-return js2-SHNE)
2219 (js2-ts-return js2-NE))
2220 (throw 'return js2-NOT)))
2221 (?<
2222 ;; NB:treat HTML begin-comment as comment-till-eol
2223 (when (js2-match-char ?!)
2224 (when (js2-match-char ?-)
2225 (when (js2-match-char ?-)
2226 (js2-skip-line)
2227 (setq js2-ts-comment-type 'html)
2228 (throw 'return js2-COMMENT)))
2229 (js2-unget-char))
2230
2231 (if (js2-match-char ?<)
2232 (if (js2-match-char ?=)
2233 (js2-ts-return js2-ASSIGN_LSH)
2234 (js2-ts-return js2-LSH))
2235 (if (js2-match-char ?=)
2236 (js2-ts-return js2-LE)
2237 (throw 'return js2-LT))))
2238 (?>
2239 (if (js2-match-char ?>)
2240 (if (js2-match-char ?>)
2241 (if (js2-match-char ?=)
2242 (js2-ts-return js2-ASSIGN_URSH)
2243 (js2-ts-return js2-URSH))
2244 (if (js2-match-char ?=)
2245 (js2-ts-return js2-ASSIGN_RSH)
2246 (js2-ts-return js2-RSH)))
2247 (if (js2-match-char ?=)
2248 (js2-ts-return js2-GE)
2249 (throw 'return js2-GT))))
2250 (?*
2251 (if (js2-match-char ?=)
2252 (js2-ts-return js2-ASSIGN_MUL)
2253 (throw 'return js2-MUL)))
2254
2255 (?/
2256 ;; is it a // comment?
2257 (when (js2-match-char ?/)
2258 (setq js2-token-beg (- js2-ts-cursor 2))
2259 (js2-skip-line)
2260 (setq js2-ts-comment-type 'line)
2261 (throw 'return js2-COMMENT))
2262
2263 ;; is it a /* comment?
2264 (when (js2-match-char ?*)
2265 (setq look-for-slash nil
2266 js2-token-beg (- js2-ts-cursor 2)
2267 js2-ts-comment-type
2268 (if (js2-match-char ?*)
2269 (progn
2270 (setq look-for-slash t)
2271 'jsdoc)
2272 'block))
2273 (while t
2274 (setq c (js2-get-char))
2275 (cond
2276 ((eq c js2-EOF_CHAR)
2277 (setq js2-token-end (1- js2-ts-cursor))
2278 (js2-report-error "msg.unterminated.comment")
2279 (throw 'return js2-COMMENT))
2280 ((eq c ?*)
2281 (setq look-for-slash t))
2282 ((eq c ?/)
2283 (if look-for-slash
2284 (js2-ts-return js2-COMMENT)))
2285 (t
2286 (setq look-for-slash nil
2287 js2-token-end js2-ts-cursor)))))
2288
2289 (if (js2-match-char ?=)
2290 (js2-ts-return js2-ASSIGN_DIV)
2291 (throw 'return js2-DIV)))
2292
2293 (?#
2294 (when js2-skip-preprocessor-directives
2295 (js2-skip-line)
2296 (setq js2-ts-comment-type 'preprocessor
2297 js2-token-end js2-ts-cursor)
2298 (throw 'return js2-COMMENT))
2299 (throw 'return js2-ERROR))
2300
2301 (?%
2302 (if (js2-match-char ?=)
2303 (js2-ts-return js2-ASSIGN_MOD)
2304 (throw 'return js2-MOD)))
2305 (?~
2306 (throw 'return js2-BITNOT))
2307 (?+
2308 (if (js2-match-char ?=)
2309 (js2-ts-return js2-ASSIGN_ADD)
2310 (if (js2-match-char ?+)
2311 (js2-ts-return js2-INC)
2312 (throw 'return js2-ADD))))
2313 (?-
2314 (cond
2315 ((js2-match-char ?=)
2316 (setq c js2-ASSIGN_SUB))
2317 ((js2-match-char ?-)
2318 (unless js2-ts-dirty-line
2319 ;; treat HTML end-comment after possible whitespace
2320 ;; after line start as comment-until-eol
2321 (when (js2-match-char ?>)
2322 (js2-skip-line)
2323 (setq js2-ts-comment-type 'html)
2324 (throw 'return js2-COMMENT)))
2325 (setq c js2-DEC))
2326 (t
2327 (setq c js2-SUB)))
2328 (setq js2-ts-dirty-line t)
2329 (js2-ts-return c))
2330
2331 (otherwise
2332 (js2-report-scan-error "msg.illegal.character")))))))
2333
2334 (defun js2-read-regexp (start-token)
2335 "Called by parser when it gets / or /= in literal context."
2336 (let (c
2337 err
2338 in-class ; inside a '[' .. ']' character-class
2339 flags
2340 (continue t))
2341 (setq js2-token-beg js2-ts-cursor
2342 js2-ts-string-buffer nil
2343 js2-ts-regexp-flags nil)
2344
2345 (if (eq start-token js2-ASSIGN_DIV)
2346 ;; mis-scanned /=
2347 (js2-add-to-string ?=)
2348 (if (neq start-token js2-DIV)
2349 (error "failed assertion")))
2350
2351 (while (and (not err)
2352 (or (/= (setq c (js2-get-char)) ?/)
2353 in-class))
2354 (cond
2355 ((or (= c ?\n)
2356 (= c js2-EOF_CHAR))
2357 (setq js2-token-end (1- js2-ts-cursor)
2358 err t
2359 js2-ts-string (js2-collect-string js2-ts-string-buffer))
2360 (js2-report-error "msg.unterminated.re.lit"))
2361 (t (cond
2362 ((= c ?\\)
2363 (js2-add-to-string c)
2364 (setq c (js2-get-char)))
2365
2366 ((= c ?\[)
2367 (setq in-class t))
2368
2369 ((= c ?\])
2370 (setq in-class nil)))
2371 (js2-add-to-string c))))
2372
2373 (unless err
2374 (while continue
2375 (cond
2376 ((js2-match-char ?g)
2377 (push ?g flags))
2378 ((js2-match-char ?i)
2379 (push ?i flags))
2380 ((js2-match-char ?m)
2381 (push ?m flags))
2382 (t
2383 (setq continue nil))))
2384 (if (js2-alpha-p (js2-peek-char))
2385 (js2-report-scan-error "msg.invalid.re.flag" t
2386 js2-ts-cursor 1))
2387 (setq js2-ts-string (js2-collect-string js2-ts-string-buffer)
2388 js2-ts-regexp-flags (js2-collect-string flags)
2389 js2-token-end js2-ts-cursor)
2390 ;; tell `parse-partial-sexp' to ignore this range of chars
2391 (put-text-property js2-token-beg js2-token-end 'syntax-class '(2)))))
2392
2393 (defun js2-get-first-xml-token ()
2394 (setq js2-ts-xml-open-tags-count 0
2395 js2-ts-is-xml-attribute nil
2396 js2-ts-xml-is-tag-content nil)
2397 (js2-unget-char)
2398 (js2-get-next-xml-token))
2399
2400 (defsubst js2-xml-discard-string ()
2401 "Throw away the string in progress and flag an XML parse error."
2402 (setq js2-ts-string-buffer nil
2403 js2-ts-string nil)
2404 (js2-report-scan-error "msg.XML.bad.form" t))
2405
2406 (defun js2-get-next-xml-token ()
2407 (setq js2-ts-string-buffer nil ; for recording the XML
2408 js2-token-beg js2-ts-cursor)
2409 (let (c result)
2410 (setq result
2411 (catch 'return
2412 (while t
2413 (setq c (js2-get-char))
2414 (cond
2415 ((= c js2-EOF_CHAR)
2416 (throw 'return js2-ERROR))
2417
2418 (js2-ts-xml-is-tag-content
2419 (case c
2420 (?>
2421 (js2-add-to-string c)
2422 (setq js2-ts-xml-is-tag-content nil
2423 js2-ts-is-xml-attribute nil))
2424 (?/
2425 (js2-add-to-string c)
2426 (when (eq ?> (js2-peek-char))
2427 (setq c (js2-get-char))
2428 (js2-add-to-string c)
2429 (setq js2-ts-xml-is-tag-content nil)
2430 (decf js2-ts-xml-open-tags-count)))
2431 (?{
2432 (js2-unget-char)
2433 (setq js2-ts-string (js2-get-string-from-buffer))
2434 (throw 'return js2-XML))
2435 ((?\' ?\")
2436 (js2-add-to-string c)
2437 (unless (js2-read-quoted-string c)
2438 (throw 'return js2-ERROR)))
2439 (?=
2440 (js2-add-to-string c)
2441 (setq js2-ts-is-xml-attribute t))
2442 ((? ?\t ?\r ?\n)
2443 (js2-add-to-string c))
2444 (t
2445 (js2-add-to-string c)
2446 (setq js2-ts-is-xml-attribute nil)))
2447 (when (and (not js2-ts-xml-is-tag-content)
2448 (zerop js2-ts-xml-open-tags-count))
2449 (setq js2-ts-string (js2-get-string-from-buffer))
2450 (throw 'return js2-XMLEND)))
2451
2452 (t
2453 ;; else not tag content
2454 (case c
2455 (?<
2456 (js2-add-to-string c)
2457 (setq c (js2-peek-char))
2458 (case c
2459 (?!
2460 (setq c (js2-get-char)) ;; skip !
2461 (js2-add-to-string c)
2462 (setq c (js2-peek-char))
2463 (case c
2464 (?-
2465 (setq c (js2-get-char)) ;; skip -
2466 (js2-add-to-string c)
2467 (if (eq c ?-)
2468 (progn
2469 (js2-add-to-string c)
2470 (unless (js2-read-xml-comment)
2471 (throw 'return js2-ERROR)))
2472 (js2-xml-discard-string)
2473 (throw 'return js2-ERROR)))
2474 (?\[
2475 (setq c (js2-get-char)) ;; skip [
2476 (js2-add-to-string c)
2477 (if (and (= (js2-get-char) ?C)
2478 (= (js2-get-char) ?D)
2479 (= (js2-get-char) ?A)
2480 (= (js2-get-char) ?T)
2481 (= (js2-get-char) ?A)
2482 (= (js2-get-char) ?\[))
2483 (progn
2484 (js2-add-to-string ?C)
2485 (js2-add-to-string ?D)
2486 (js2-add-to-string ?A)
2487 (js2-add-to-string ?T)
2488 (js2-add-to-string ?A)
2489 (js2-add-to-string ?\[)
2490 (unless (js2-read-cdata)
2491 (throw 'return js2-ERROR)))
2492 (js2-xml-discard-string)
2493 (throw 'return js2-ERROR)))
2494 (t
2495 (unless (js2-read-entity)
2496 (throw 'return js2-ERROR)))))
2497 (??
2498 (setq c (js2-get-char)) ;; skip ?
2499 (js2-add-to-string c)
2500 (unless (js2-read-PI)
2501 (throw 'return js2-ERROR)))
2502 (?/
2503 ;; end tag
2504 (setq c (js2-get-char)) ;; skip /
2505 (js2-add-to-string c)
2506 (when (zerop js2-ts-xml-open-tags-count)
2507 (js2-xml-discard-string)
2508 (throw 'return js2-ERROR))
2509 (setq js2-ts-xml-is-tag-content t)
2510 (decf js2-ts-xml-open-tags-count))
2511 (t
2512 ;; start tag
2513 (setq js2-ts-xml-is-tag-content t)
2514 (incf js2-ts-xml-open-tags-count))))
2515 (?{
2516 (js2-unget-char)
2517 (setq js2-ts-string (js2-get-string-from-buffer))
2518 (throw 'return js2-XML))
2519 (t
2520 (js2-add-to-string c))))))))
2521 (setq js2-token-end js2-ts-cursor)
2522 result))
2523
2524 (defun js2-read-quoted-string (quote)
2525 (let (c)
2526 (catch 'return
2527 (while (/= (setq c (js2-get-char)) js2-EOF_CHAR)
2528 (js2-add-to-string c)
2529 (if (eq c quote)
2530 (throw 'return t)))
2531 (js2-xml-discard-string) ;; throw away string in progress
2532 nil)))
2533
2534 (defun js2-read-xml-comment ()
2535 (let ((c (js2-get-char)))
2536 (catch 'return
2537 (while (/= c js2-EOF_CHAR)
2538 (catch 'continue
2539 (js2-add-to-string c)
2540 (when (and (eq c ?-) (eq ?- (js2-peek-char)))
2541 (setq c (js2-get-char))
2542 (js2-add-to-string c)
2543 (if (eq (js2-peek-char) ?>)
2544 (progn
2545 (setq c (js2-get-char)) ;; skip >
2546 (js2-add-to-string c)
2547 (throw 'return t))
2548 (throw 'continue nil)))
2549 (setq c (js2-get-char))))
2550 (js2-xml-discard-string)
2551 nil)))
2552
2553 (defun js2-read-cdata ()
2554 (let ((c (js2-get-char)))
2555 (catch 'return
2556 (while (/= c js2-EOF_CHAR)
2557 (catch 'continue
2558 (js2-add-to-string c)
2559 (when (and (eq c ?\]) (eq (js2-peek-char) ?\]))
2560 (setq c (js2-get-char))
2561 (js2-add-to-string c)
2562 (if (eq (js2-peek-char) ?>)
2563 (progn
2564 (setq c (js2-get-char)) ;; Skip >
2565 (js2-add-to-string c)
2566 (throw 'return t))
2567 (throw 'continue nil)))
2568 (setq c (js2-get-char))))
2569 (js2-xml-discard-string)
2570 nil)))
2571
2572 (defun js2-read-entity ()
2573 (let ((decl-tags 1)
2574 c)
2575 (catch 'return
2576 (while (/= js2-EOF_CHAR (setq c (js2-get-char)))
2577 (js2-add-to-string c)
2578 (case c
2579 (?<
2580 (incf decl-tags))
2581 (?>
2582 (decf decl-tags)
2583 (if (zerop decl-tags)
2584 (throw 'return t)))))
2585 (js2-xml-discard-string)
2586 nil)))
2587
2588 (defun js2-read-PI ()
2589 "Scan an XML processing instruction."
2590 (let (c)
2591 (catch 'return
2592 (while (/= js2-EOF_CHAR (setq c (js2-get-char)))
2593 (js2-add-to-string c)
2594 (when (and (eq c ??) (eq (js2-peek-char) ?>))
2595 (setq c (js2-get-char)) ;; Skip >
2596 (js2-add-to-string c)
2597 (throw 'return t)))
2598 (js2-xml-discard-string)
2599 nil)))
2600
2601 (defun js2-scanner-get-line ()
2602 "Return the text of the current scan line."
2603 (buffer-substring (point-at-bol) (point-at-eol)))
2604
2605 (provide 'js2-scan)
2606
2607 ;;; js2-scan.el ends here
2608 ;;; js2-messages: localizable messages for js2-mode
2609
2610 ;; Author: Steve Yegge (steve.yegge@gmail.com)
2611 ;; Keywords: javascript languages
2612
2613 ;;; Commentary:
2614
2615 ;; Messages are copied from Rhino's Messages.properties.
2616 ;; Many of the Java-specific messages have been elided.
2617 ;; Add any js2-specific ones at the end, so we can keep
2618 ;; this file synced with changes to Rhino's.
2619 ;;
2620 ;; TODO:
2621 ;; - move interpreter messages into separate file
2622
2623 ;;; Code:
2624
2625 (defvar js2-message-table
2626 (make-hash-table :test 'equal :size 250)
2627 "Contains localized messages for js2-mode.")
2628
2629 ;; TODO: construct this hashtable at compile-time.
2630 (defmacro js2-msg (key &rest strings)
2631 `(puthash ,key (funcall #'concat ,@strings)
2632 js2-message-table))
2633
2634 (defun js2-get-msg (msg-key)
2635 "Look up a localized message.
2636 MSG-KEY is a list of (MSG ARGS). If the message takes parameters,
2637 the correct number of ARGS must be provided."
2638 (let* ((key (if (listp msg-key) (car msg-key) msg-key))
2639 (args (if (listp msg-key) (cdr msg-key)))
2640 (msg (gethash key js2-message-table)))
2641 (if msg
2642 (apply #'format msg args)
2643 key))) ; default to showing the key
2644
2645 (js2-msg "msg.dup.parms"
2646 "Duplicate parameter name '%s'.")
2647
2648 (js2-msg "msg.too.big.jump"
2649 "Program too complex: jump offset too big.")
2650
2651 (js2-msg "msg.too.big.index"
2652 "Program too complex: internal index exceeds 64K limit.")
2653
2654 (js2-msg "msg.while.compiling.fn"
2655 "Encountered code generation error while compiling function '%s': %s")
2656
2657 (js2-msg "msg.while.compiling.script"
2658 "Encountered code generation error while compiling script: %s")
2659
2660 ;; Context
2661 (js2-msg "msg.ctor.not.found"
2662 "Constructor for '%s' not found.")
2663
2664 (js2-msg "msg.not.ctor"
2665 "'%s' is not a constructor.")
2666
2667 ;; FunctionObject
2668 (js2-msg "msg.varargs.ctor"
2669 "Method or constructor '%s' must be static "
2670 "with the signature (Context cx, Object[] args, "
2671 "Function ctorObj, boolean inNewExpr) "
2672 "to define a variable arguments constructor.")
2673
2674 (js2-msg "msg.varargs.fun"
2675 "Method '%s' must be static with the signature "
2676 "(Context cx, Scriptable thisObj, Object[] args, Function funObj) "
2677 "to define a variable arguments function.")
2678
2679 (js2-msg "msg.incompat.call"
2680 "Method '%s' called on incompatible object.")
2681
2682 (js2-msg "msg.bad.parms"
2683 "Unsupported parameter type '%s' in method '%s'.")
2684
2685 (js2-msg "msg.bad.method.return"
2686 "Unsupported return type '%s' in method '%s'.")
2687
2688 (js2-msg "msg.bad.ctor.return"
2689 "Construction of objects of type '%s' is not supported.")
2690
2691 (js2-msg "msg.no.overload"
2692 "Method '%s' occurs multiple times in class '%s'.")
2693
2694 (js2-msg "msg.method.not.found"
2695 "Method '%s' not found in '%s'.")
2696
2697 ;; IRFactory
2698
2699 (js2-msg "msg.bad.for.in.lhs"
2700 "Invalid left-hand side of for..in loop.")
2701
2702 (js2-msg "msg.mult.index"
2703 "Only one variable allowed in for..in loop.")
2704
2705 (js2-msg "msg.bad.for.in.destruct"
2706 "Left hand side of for..in loop must be an array of "
2707 "length 2 to accept key/value pair.")
2708
2709 (js2-msg "msg.cant.convert"
2710 "Can't convert to type '%s'.")
2711
2712 (js2-msg "msg.bad.assign.left"
2713 "Invalid assignment left-hand side.")
2714
2715 (js2-msg "msg.bad.decr"
2716 "Invalid decerement operand.")
2717
2718 (js2-msg "msg.bad.incr"
2719 "Invalid increment operand.")
2720
2721 (js2-msg "msg.bad.yield"
2722 "yield must be in a function.")
2723
2724 (js2-msg "msg.yield.parenthesized"
2725 "yield expression must be parenthesized.")
2726
2727 ;; NativeGlobal
2728 (js2-msg "msg.cant.call.indirect"
2729 "Function '%s' must be called directly, and not by way of a "
2730 "function of another name.")
2731
2732 (js2-msg "msg.eval.nonstring"
2733 "Calling eval() with anything other than a primitive "
2734 "string value will simply return the value. "
2735 "Is this what you intended?")
2736
2737 (js2-msg "msg.eval.nonstring.strict"
2738 "Calling eval() with anything other than a primitive "
2739 "string value is not allowed in strict mode.")
2740
2741 (js2-msg "msg.bad.destruct.op"
2742 "Invalid destructuring assignment operator")
2743
2744 ;; NativeCall
2745 (js2-msg "msg.only.from.new"
2746 "'%s' may only be invoked from a `new' expression.")
2747
2748 (js2-msg "msg.deprec.ctor"
2749 "The '%s' constructor is deprecated.")
2750
2751 ;; NativeFunction
2752 (js2-msg "msg.no.function.ref.found"
2753 "no source found to decompile function reference %s")
2754
2755 (js2-msg "msg.arg.isnt.array"
2756 "second argument to Function.prototype.apply must be an array")
2757
2758 ;; NativeGlobal
2759 (js2-msg "msg.bad.esc.mask"
2760 "invalid string escape mask")
2761
2762 ;; NativeRegExp
2763 (js2-msg "msg.bad.quant"
2764 "Invalid quantifier %s")
2765
2766 (js2-msg "msg.overlarge.backref"
2767 "Overly large back reference %s")
2768
2769 (js2-msg "msg.overlarge.min"
2770 "Overly large minimum %s")
2771
2772 (js2-msg "msg.overlarge.max"
2773 "Overly large maximum %s")
2774
2775 (js2-msg "msg.zero.quant"
2776 "Zero quantifier %s")
2777
2778 (js2-msg "msg.max.lt.min"
2779 "Maximum %s less than minimum")
2780
2781 (js2-msg "msg.unterm.quant"
2782 "Unterminated quantifier %s")
2783
2784 (js2-msg "msg.unterm.paren"
2785 "Unterminated parenthetical %s")
2786
2787 (js2-msg "msg.unterm.class"
2788 "Unterminated character class %s")
2789
2790 (js2-msg "msg.bad.range"
2791 "Invalid range in character class.")
2792
2793 (js2-msg "msg.trail.backslash"
2794 "Trailing \\ in regular expression.")
2795
2796 (js2-msg "msg.re.unmatched.right.paren"
2797 "unmatched ) in regular expression.")
2798
2799 (js2-msg "msg.no.regexp"
2800 "Regular expressions are not available.")
2801
2802 (js2-msg "msg.bad.backref"
2803 "back-reference exceeds number of capturing parentheses.")
2804
2805 (js2-msg "msg.bad.regexp.compile"
2806 "Only one argument may be specified if the first "
2807 "argument to RegExp.prototype.compile is a RegExp object.")
2808
2809 ;; Parser
2810 (js2-msg "msg.got.syntax.errors"
2811 "Compilation produced %s syntax errors.")
2812
2813 (js2-msg "msg.var.redecl"
2814 "TypeError: redeclaration of var %s.")
2815
2816 (js2-msg "msg.const.redecl"
2817 "TypeError: redeclaration of const %s.")
2818
2819 (js2-msg "msg.let.redecl"
2820 "TypeError: redeclaration of variable %s.")
2821
2822 (js2-msg "msg.parm.redecl"
2823 "TypeError: redeclaration of formal parameter %s.")
2824
2825 (js2-msg "msg.fn.redecl"
2826 "TypeError: redeclaration of function %s.")
2827
2828 (js2-msg "msg.let.decl.not.in.block"
2829 "SyntaxError: let declaration not directly within block")
2830
2831 ;; NodeTransformer
2832 (js2-msg "msg.dup.label"
2833 "duplicated label")
2834
2835 (js2-msg "msg.undef.label"
2836 "undefined label")
2837
2838 (js2-msg "msg.bad.break"
2839 "unlabelled break must be inside loop or switch")
2840
2841 (js2-msg "msg.continue.outside"
2842 "continue must be inside loop")
2843
2844 (js2-msg "msg.continue.nonloop"
2845 "continue can only use labels of iteration statements")
2846
2847 (js2-msg "msg.bad.throw.eol"
2848 "Line terminator is not allowed between the throw "
2849 "keyword and throw expression.")
2850
2851 (js2-msg "msg.no.paren.parms"
2852 "missing ( before function parameters.")
2853
2854 (js2-msg "msg.no.parm"
2855 "missing formal parameter")
2856
2857 (js2-msg "msg.no.paren.after.parms"
2858 "missing ) after formal parameters")
2859
2860 (js2-msg "msg.no.brace.body"
2861 "missing '{' before function body")
2862
2863 (js2-msg "msg.no.brace.after.body"
2864 "missing } after function body")
2865
2866 (js2-msg "msg.no.paren.cond"
2867 "missing ( before condition")
2868
2869 (js2-msg "msg.no.paren.after.cond"
2870 "missing ) after condition")
2871
2872 (js2-msg "msg.no.semi.stmt"
2873 "missing ; before statement")
2874
2875 (js2-msg "msg.missing.semi"
2876 "missing ; after statement")
2877
2878 (js2-msg "msg.no.name.after.dot"
2879 "missing name after . operator")
2880
2881 (js2-msg "msg.no.name.after.coloncolon"
2882 "missing name after :: operator")
2883
2884 (js2-msg "msg.no.name.after.dotdot"
2885 "missing name after .. operator")
2886
2887 (js2-msg "msg.no.name.after.xmlAttr"
2888 "missing name after .@")
2889
2890 (js2-msg "msg.no.bracket.index"
2891 "missing ] in index expression")
2892
2893 (js2-msg "msg.no.paren.switch"
2894 "missing ( before switch expression")
2895
2896 (js2-msg "msg.no.paren.after.switch"
2897 "missing ) after switch expression")
2898
2899 (js2-msg "msg.no.brace.switch"
2900 "missing '{' before switch body")
2901
2902 (js2-msg "msg.bad.switch"
2903 "invalid switch statement")
2904
2905 (js2-msg "msg.no.colon.case"
2906 "missing : after case expression")
2907
2908 (js2-msg "msg.double.switch.default"
2909 "double default label in the switch statement")
2910
2911 (js2-msg "msg.no.while.do"
2912 "missing while after do-loop body")
2913
2914 (js2-msg "msg.no.paren.for"
2915 "missing ( after for")
2916
2917 (js2-msg "msg.no.semi.for"
2918 "missing ; after for-loop initializer")
2919
2920 (js2-msg "msg.no.semi.for.cond"
2921 "missing ; after for-loop condition")
2922
2923 (js2-msg "msg.in.after.for.name"
2924 "missing in after for")
2925
2926 (js2-msg "msg.no.paren.for.ctrl"
2927 "missing ) after for-loop control")
2928
2929 (js2-msg "msg.no.paren.with"
2930 "missing ( before with-statement object")
2931
2932 (js2-msg "msg.no.paren.after.with"
2933 "missing ) after with-statement object")
2934
2935 (js2-msg "msg.no.paren.after.let"
2936 "missing ( after let")
2937
2938 (js2-msg "msg.no.paren.let"
2939 "missing ) after variable list")
2940
2941 (js2-msg "msg.no.curly.let"
2942 "missing } after let statement")
2943
2944 (js2-msg "msg.bad.return"
2945 "invalid return")
2946
2947 (js2-msg "msg.no.brace.block"
2948 "missing } in compound statement")
2949
2950 (js2-msg "msg.bad.label"
2951 "invalid label")
2952
2953 (js2-msg "msg.bad.var"
2954 "missing variable name")
2955
2956 (js2-msg "msg.bad.var.init"
2957 "invalid variable initialization")
2958
2959 (js2-msg "msg.no.colon.cond"
2960 "missing : in conditional expression")
2961
2962 (js2-msg "msg.no.paren.arg"
2963 "missing ) after argument list")
2964
2965 (js2-msg "msg.no.bracket.arg"
2966 "missing ] after element list")
2967
2968 (js2-msg "msg.bad.prop"
2969 "invalid property id")
2970
2971 (js2-msg "msg.no.colon.prop"
2972 "missing : after property id")
2973
2974 (js2-msg "msg.no.brace.prop"
2975 "missing } after property list")
2976
2977 (js2-msg "msg.no.paren"
2978 "missing ) in parenthetical")
2979
2980 (js2-msg "msg.reserved.id"
2981 "identifier is a reserved word")
2982
2983 (js2-msg "msg.no.paren.catch"
2984 "missing ( before catch-block condition")
2985
2986 (js2-msg "msg.bad.catchcond"
2987 "invalid catch block condition")
2988
2989 (js2-msg "msg.catch.unreachable"
2990 "any catch clauses following an unqualified catch are unreachable")
2991
2992 (js2-msg "msg.no.brace.try"
2993 "missing '{' before try block")
2994
2995 (js2-msg "msg.no.brace.catchblock"
2996 "missing '{' before catch-block body")
2997
2998 (js2-msg "msg.try.no.catchfinally"
2999 "'try' without 'catch' or 'finally'")
3000
3001 (js2-msg "msg.no.return.value"
3002 "function %s does not always return a value")
3003
3004 (js2-msg "msg.anon.no.return.value"
3005 "anonymous function does not always return a value")
3006
3007 (js2-msg "msg.return.inconsistent"
3008 "return statement is inconsistent with previous usage")
3009
3010 (js2-msg "msg.generator.returns"
3011 "TypeError: generator function '%s' returns a value")
3012
3013 (js2-msg "msg.anon.generator.returns"
3014 "TypeError: anonymous generator function returns a value")
3015
3016 (js2-msg "msg.syntax"
3017 "syntax error")
3018
3019 (js2-msg "msg.unexpected.eof"
3020 "Unexpected end of file")
3021
3022 (js2-msg "msg.XML.bad.form"
3023 "illegally formed XML syntax")
3024
3025 (js2-msg "msg.XML.not.available"
3026 "XML runtime not available")
3027
3028 (js2-msg "msg.too.deep.parser.recursion"
3029 "Too deep recursion while parsing")
3030
3031 (js2-msg "msg.no.side.effects"
3032 "Code has no side effects")
3033
3034 (js2-msg "msg.extra.trailing.comma"
3035 "Trailing comma is not legal in an ECMA-262 object initializer")
3036
3037 (js2-msg "msg.array.trailing.comma"
3038 "Trailing comma yields different behavior across browsers")
3039
3040 (js2-msg "msg.equal.as.assign"
3041 (concat "Test for equality (==) mistyped as assignment (=)?"
3042 " (parenthesize to suppress warning)"))
3043
3044 (js2-msg "msg.var.hides.arg"
3045 "Variable %s hides argument")
3046
3047 (js2-msg "msg.destruct.assign.no.init"
3048 "Missing = in destructuring declaration")
3049
3050 ;; ScriptRuntime
3051 (js2-msg "msg.no.properties"
3052 "%s has no properties.")
3053
3054 (js2-msg "msg.invalid.iterator"
3055 "Invalid iterator value")
3056
3057 (js2-msg "msg.iterator.primitive"
3058 "__iterator__ returned a primitive value")
3059
3060 (js2-msg "msg.assn.create.strict"
3061 "Assignment to undeclared variable %s")
3062
3063 (js2-msg "msg.ref.undefined.prop"
3064 "Reference to undefined property '%s'")
3065
3066 (js2-msg "msg.prop.not.found"
3067 "Property %s not found.")
3068
3069 (js2-msg "msg.invalid.type"
3070 "Invalid JavaScript value of type %s")
3071
3072 (js2-msg "msg.primitive.expected"
3073 "Primitive type expected (had %s instead)")
3074
3075 (js2-msg "msg.namespace.expected"
3076 "Namespace object expected to left of :: (found %s instead)")
3077
3078 (js2-msg "msg.null.to.object"
3079 "Cannot convert null to an object.")
3080
3081 (js2-msg "msg.undef.to.object"
3082 "Cannot convert undefined to an object.")
3083
3084 (js2-msg "msg.cyclic.value"
3085 "Cyclic %s value not allowed.")
3086
3087 (js2-msg "msg.is.not.defined"
3088 "'%s' is not defined.")
3089
3090 (js2-msg "msg.undef.prop.read"
3091 "Cannot read property '%s' from %s")
3092
3093 (js2-msg "msg.undef.prop.write"
3094 "Cannot set property '%s' of %s to '%s'")
3095
3096 (js2-msg "msg.undef.prop.delete"
3097 "Cannot delete property '%s' of %s")
3098
3099 (js2-msg "msg.undef.method.call"
3100 "Cannot call method '%s' of %s")
3101
3102 (js2-msg "msg.undef.with"
3103 "Cannot apply 'with' to %s")
3104
3105 (js2-msg "msg.isnt.function"
3106 "%s is not a function, it is %s.")
3107
3108 (js2-msg "msg.isnt.function.in"
3109 "Cannot call property %s in object %s. "
3110 "It is not a function, it is '%s'.")
3111
3112 (js2-msg "msg.function.not.found"
3113 "Cannot find function %s.")
3114
3115 (js2-msg "msg.function.not.found.in"
3116 "Cannot find function %s in object %s.")
3117
3118 (js2-msg "msg.isnt.xml.object"
3119 "%s is not an xml object.")
3120
3121 (js2-msg "msg.no.ref.to.get"
3122 "%s is not a reference to read reference value.")
3123
3124 (js2-msg "msg.no.ref.to.set"
3125 "%s is not a reference to set reference value to %s.")
3126
3127 (js2-msg "msg.no.ref.from.function"
3128 "Function %s can not be used as the left-hand "
3129 "side of assignment or as an operand of ++ or -- operator.")
3130
3131 (js2-msg "msg.bad.default.value"
3132 "Object's getDefaultValue() method returned an object.")
3133
3134 (js2-msg "msg.instanceof.not.object"
3135 "Can't use instanceof on a non-object.")
3136
3137 (js2-msg "msg.instanceof.bad.prototype"
3138 "'prototype' property of %s is not an object.")
3139
3140 (js2-msg "msg.bad.radix"
3141 "illegal radix %s.")
3142
3143 ;; ScriptableObject
3144 (js2-msg "msg.default.value"
3145 "Cannot find default value for object.")
3146
3147 (js2-msg "msg.zero.arg.ctor"
3148 "Cannot load class '%s' which has no zero-parameter constructor.")
3149
3150 (js2-msg "msg.ctor.multiple.parms"
3151 "Can't define constructor or class %s since more than "
3152 "one constructor has multiple parameters.")
3153
3154 (js2-msg "msg.extend.scriptable"
3155 "%s must extend ScriptableObject in order to define property %s.")
3156
3157 (js2-msg "msg.bad.getter.parms"
3158 "In order to define a property, getter %s must have zero "
3159 "parameters or a single ScriptableObject parameter.")
3160
3161 (js2-msg "msg.obj.getter.parms"
3162 "Expected static or delegated getter %s to take "
3163 "a ScriptableObject parameter.")
3164
3165 (js2-msg "msg.getter.static"
3166 "Getter and setter must both be static or neither be static.")
3167
3168 (js2-msg "msg.setter.return"
3169 "Setter must have void return type: %s")
3170
3171 (js2-msg "msg.setter2.parms"
3172 "Two-parameter setter must take a ScriptableObject as "
3173 "its first parameter.")
3174
3175 (js2-msg "msg.setter1.parms"
3176 "Expected single parameter setter for %s")
3177
3178 (js2-msg "msg.setter2.expected"
3179 "Expected static or delegated setter %s to take two parameters.")
3180
3181 (js2-msg "msg.setter.parms"
3182 "Expected either one or two parameters for setter.")
3183
3184 (js2-msg "msg.setter.bad.type"
3185 "Unsupported parameter type '%s' in setter '%s'.")
3186
3187 (js2-msg "msg.add.sealed"
3188 "Cannot add a property to a sealed object: %s.")
3189
3190 (js2-msg "msg.remove.sealed"
3191 "Cannot remove a property from a sealed object: %s.")
3192
3193 (js2-msg "msg.modify.sealed"
3194 "Cannot modify a property of a sealed object: %s.")
3195
3196 (js2-msg "msg.modify.readonly"
3197 "Cannot modify readonly property: %s.")
3198
3199 ;; TokenStream
3200 (js2-msg "msg.missing.exponent"
3201 "missing exponent")
3202
3203 (js2-msg "msg.caught.nfe"
3204 "number format error")
3205
3206 (js2-msg "msg.unterminated.string.lit"
3207 "unterminated string literal")
3208
3209 (js2-msg "msg.unterminated.comment"
3210 "unterminated comment")
3211
3212 (js2-msg "msg.unterminated.re.lit"
3213 "unterminated regular expression literal")
3214
3215 (js2-msg "msg.invalid.re.flag"
3216 "invalid flag after regular expression")
3217
3218 (js2-msg "msg.no.re.input.for"
3219 "no input for %s")
3220
3221 (js2-msg "msg.illegal.character"
3222 "illegal character")
3223
3224 (js2-msg "msg.invalid.escape"
3225 "invalid Unicode escape sequence")
3226
3227 (js2-msg "msg.bad.namespace"
3228 "not a valid default namespace statement. "
3229 "Syntax is: default xml namespace = EXPRESSION;")
3230
3231 ;; TokensStream warnings
3232 (js2-msg "msg.bad.octal.literal"
3233 "illegal octal literal digit %s; "
3234 "interpreting it as a decimal digit")
3235
3236 (js2-msg "msg.reserved.keyword"
3237 "illegal usage of future reserved keyword %s; "
3238 "interpreting it as ordinary identifier")
3239
3240 (js2-msg "msg.script.is.not.constructor"
3241 "Script objects are not constructors.")
3242
3243 ;; Arrays
3244 (js2-msg "msg.arraylength.bad"
3245 "Inappropriate array length.")
3246
3247 ;; Arrays
3248 (js2-msg "msg.arraylength.too.big"
3249 "Array length %s exceeds supported capacity limit.")
3250
3251 ;; URI
3252 (js2-msg "msg.bad.uri"
3253 "Malformed URI sequence.")
3254
3255 ;; Number
3256 (js2-msg "msg.bad.precision"
3257 "Precision %s out of range.")
3258
3259 ;; NativeGenerator
3260 (js2-msg "msg.send.newborn"
3261 "Attempt to send value to newborn generator")
3262
3263 (js2-msg "msg.already.exec.gen"
3264 "Already executing generator")
3265
3266 (js2-msg "msg.StopIteration.invalid"
3267 "StopIteration may not be changed to an arbitrary object.")
3268
3269 ;; Interpreter
3270 (js2-msg "msg.yield.closing"
3271 "Yield from closing generator")
3272
3273 (provide 'js2-messages)
3274 ;;; js2-ast.el --- JavaScript syntax tree node definitions
3275
3276 ;; Author: Steve Yegge (steve.yegge@gmail.com)
3277 ;; Keywords: javascript languages
3278
3279 ;;; Code:
3280
3281 (eval-and-compile
3282 (require 'cl))
3283
3284
3285 ;; flags for ast node property 'member-type (used for e4x operators)
3286 (defvar js2-property-flag #x1 "property access: element is valid name")
3287 (defvar js2-attribute-flag #x2 "x.@y or x..@y")
3288 (defvar js2-descendants-flag #x4 "x..y or x..@i")
3289
3290 (defsubst js2-relpos (pos anchor)
3291 "Convert POS to be relative to ANCHOR.
3292 If POS is nil, returns nil."
3293 (and pos (- pos anchor)))
3294
3295 (defsubst js2-make-pad (indent)
3296 (if (zerop indent)
3297 ""
3298 (make-string (* indent js2-basic-offset) ? )))
3299
3300 (defsubst js2-visit-ast (node callback)
3301 "Visit every node in ast NODE with visitor CALLBACK.
3302
3303 CALLBACK is a function that takes two arguments: (NODE END-P). It is
3304 called twice: once to visit the node, and again after all the node's
3305 children have been processed. The END-P argument is nil on the first
3306 call and non-nil on the second call. The return value of the callback
3307 affects the traversal: if non-nil, the children of NODE are processed.
3308 If the callback returns nil, or if the node has no children, then the
3309 callback is called immediately with a non-nil END-P argument.
3310
3311 The node traversal is approximately lexical-order, although there
3312 are currently no guarantees around this."
3313 (let ((vfunc (get (aref node 0) 'js2-visitor)))
3314 ;; visit the node
3315 (when (funcall callback node nil)
3316 ;; visit the kids
3317 (cond
3318 ((eq vfunc 'js2-visit-none)
3319 nil) ; don't even bother calling it
3320 ;; Each AST node type has to define a `js2-visitor' function
3321 ;; that takes a node and a callback, and calls `js2-visit-ast'
3322 ;; on each child of the node.
3323 (vfunc
3324 (funcall vfunc node callback))
3325 (t
3326 (error "%s does not define a visitor-traversal function"
3327 (aref node 0)))))
3328 ;; call the end-visit
3329 (funcall callback node t)))
3330
3331 (defstruct (js2-node
3332 (:constructor nil)) ; abstract
3333 "Base AST node type."
3334 (type -1) ; token type
3335 (pos -1) ; start position of this AST node in parsed input
3336 (len 1) ; num characters spanned by the node
3337 props ; optional node property list (an alist)
3338 parent) ; link to parent node; null for root
3339
3340 (defsubst js2-node-get-prop (node prop &optional default)
3341 (or (cadr (assoc prop (js2-node-props node))) default))
3342
3343 (defsubst js2-node-set-prop (node prop value)
3344 (setf (js2-node-props node)
3345 (cons (list prop value) (js2-node-props node))))
3346
3347 (defsubst js2-fixup-starts (n nodes)
3348 "Adjust the start positions of NODES to be relative to N.
3349 Any node in the list may be nil, for convenience."
3350 (dolist (node nodes)
3351 (when node
3352 (setf (js2-node-pos node) (- (js2-node-pos node)
3353 (js2-node-pos n))))))
3354
3355 (defsubst js2-node-add-children (parent &rest nodes)
3356 "Set parent node of NODES to PARENT, and return PARENT.
3357 Does nothing if we're not recording parent links.
3358 If any given node in NODES is nil, doesn't record that link."
3359 (js2-fixup-starts parent nodes)
3360 (dolist (node nodes)
3361 (and node
3362 (setf (js2-node-parent node) parent))))
3363
3364 ;; Non-recursive since it's called a frightening number of times.
3365 (defsubst js2-node-abs-pos (n)
3366 (let ((pos (js2-node-pos n)))
3367 (while (setq n (js2-node-parent n))
3368 (setq pos (+ pos (js2-node-pos n))))
3369 pos))
3370
3371 (defsubst js2-node-abs-end (n)
3372 "Return absolute buffer position of end of N."
3373 (+ (js2-node-abs-pos n) (js2-node-len n)))
3374
3375 ;; It's important to make sure block nodes have a lisp list for the
3376 ;; child nodes, to limit printing recursion depth in an AST that
3377 ;; otherwise consists of defstruct vectors. Emacs will crash printing
3378 ;; a sufficiently large vector tree.
3379
3380 (defstruct (js2-block-node
3381 (:include js2-node)
3382 (:constructor nil)
3383 (:constructor make-js2-block-node (&key (type js2-BLOCK)
3384 (pos js2-token-beg)
3385 len
3386 props
3387 kids)))
3388 "A block of statements."
3389 kids) ; a lisp list of the child statement nodes
3390
3391 (put 'cl-struct-js2-block-node 'js2-visitor 'js2-visit-block)
3392 (put 'cl-struct-js2-block-node 'js2-printer 'js2-print-block)
3393
3394 (defsubst js2-visit-block (ast callback)
3395 "Visit the `js2-block-node' children of AST."
3396 (dolist (kid (js2-block-node-kids ast))
3397 (js2-visit-ast kid callback)))
3398
3399 (defun js2-print-block (n i)
3400 (let ((pad (js2-make-pad i)))
3401 (insert pad "{\n")
3402 (dolist (kid (js2-block-node-kids n))
3403 (js2-print-ast kid (1+ i)))
3404 (insert pad "}")))
3405
3406 (defstruct (js2-scope
3407 (:include js2-block-node)
3408 (:constructor nil)
3409 (:constructor make-js2-scope (&key (type js2-BLOCK)
3410 (pos js2-token-beg)
3411 len
3412 kids)))
3413 ;; The symbol-table is a LinkedHashMap<String,Symbol> in Rhino.
3414 ;; I don't have one of those handy, so I'll use an alist for now.
3415 ;; It's as fast as an emacs hashtable for up to about 50 elements,
3416 ;; and is much lighter-weight to construct (both CPU and mem).
3417 ;; The keys are interned strings (symbols) for faster lookup.
3418 ;; Should switch to hybrid alist/hashtable eventually.
3419 symbol-table ; an alist of (symbol . js2-symbol)
3420 parent-scope ; a `js2-scope'
3421 top) ; top-level `js2-scope' (script/function)
3422
3423 (put 'cl-struct-js2-scope 'js2-visitor 'js2-visit-none)
3424 (put 'cl-struct-js2-scope 'js2-printer 'js2-print-none)
3425
3426 (defun js2-scope-set-parent-scope (scope parent)
3427 (setf (js2-scope-parent-scope scope) parent
3428 (js2-scope-top scope) (if (null parent)
3429 scope
3430 (js2-scope-top parent))))
3431
3432 (defun js2-node-get-enclosing-scope (node)
3433 "Return the innermost `js2-scope' node surrounding NODE.
3434 Returns nil if there is no enclosing scope node."
3435 (let ((parent (js2-node-parent node)))
3436 (while (not (js2-scope-p parent))
3437 (setq parent (js2-node-parent parent)))
3438 parent))
3439
3440 (defun js2-get-defining-scope (scope name)
3441 "Search up scope chain from SCOPE looking for NAME, a string or symbol.
3442 Returns `js2-scope' in which NAME is defined, or nil if not found."
3443 (let ((sym (if (symbolp name)
3444 name
3445 (intern name)))
3446 table
3447 result
3448 (continue t))
3449 (while (and scope continue)
3450 (if (and (setq table (js2-scope-symbol-table scope))
3451 (assq sym table))
3452 (setq continue nil
3453 result scope)
3454 (setq scope (js2-scope-parent-scope scope))))
3455 result))
3456
3457 (defsubst js2-scope-get-symbol (scope name)
3458 "Return symbol table entry for NAME in SCOPE.
3459 NAME can be a string or symbol. Returns a `js2-symbol' or nil if not found."
3460 (and (js2-scope-symbol-table scope)
3461 (cdr (assq (if (symbolp name)
3462 name
3463 (intern name))
3464 (js2-scope-symbol-table scope)))))
3465
3466 (defsubst js2-scope-put-symbol (scope name symbol)
3467 "Enter SYMBOL into symbol-table for SCOPE under NAME.
3468 NAME can be a lisp symbol or string. SYMBOL is a `js2-symbol'."
3469 (let* ((table (js2-scope-symbol-table scope))
3470 (sym (if (symbolp name) name (intern name)))
3471 (entry (assq sym table)))
3472 (if entry
3473 (setcdr entry symbol)
3474 (push (cons sym symbol)
3475 (js2-scope-symbol-table scope)))))
3476
3477 (defstruct (js2-symbol
3478 (:constructor nil)
3479 (:constructor make-js2-symbol (decl-type name &optional ast-node)))
3480 "A symbol table entry."
3481 ;; One of js2-FUNCTION, js2-LP (for parameters), js2-VAR,
3482 ;; js2-LET, or js2-CONST
3483 decl-type
3484 name ; string
3485 ast-node) ; a `js2-node'
3486
3487 (defstruct (js2-error-node
3488 (:include js2-node)
3489 (:constructor nil) ; silence emacs21 byte-compiler
3490 (:constructor make-js2-error-node (&key (type js2-ERROR)
3491 (pos js2-token-beg)
3492 len)))
3493 "AST node representing a parse error.")
3494
3495 (put 'cl-struct-js2-error-node 'js2-visitor 'js2-visit-none)
3496 (put 'cl-struct-js2-error-node 'js2-printer 'js2-print-none)
3497
3498 (defstruct (js2-script-node
3499 (:include js2-scope)
3500 (:constructor nil)
3501 (:constructor make-js2-script-node (&key (type js2-SCRIPT)
3502 (pos js2-token-beg)
3503 len
3504 var-decls
3505 fun-decls)))
3506 functions ; lisp list of nested functions
3507 regexps ; lisp list of (string . flags)
3508 symbols ; alist (every symbol gets unique index)
3509 (param-count 0)
3510 var-names ; vector of string names
3511 consts ; bool-vector matching var-decls
3512 (temp-number 0)) ; for generating temp variables
3513
3514 (put 'cl-struct-js2-script-node 'js2-visitor 'js2-visit-block)
3515 (put 'cl-struct-js2-script-node 'js2-printer 'js2-print-script)
3516
3517 (defun js2-print-script (node indent)
3518 (dolist (kid (js2-block-node-kids node))
3519 (js2-print-ast kid indent)))
3520
3521 (defstruct (js2-ast-root
3522 (:include js2-script-node)
3523 (:constructor nil)
3524 (:constructor make-js2-ast-root (&key (type js2-SCRIPT)
3525 (pos js2-token-beg)
3526 len
3527 buffer)))
3528 "The root node of a js2 AST."
3529 buffer ; the source buffer from which the code was parsed
3530 comments ; a lisp list of comments, ordered by start position
3531 errors ; a lisp list of errors found during parsing
3532 warnings ; a lisp list of warnings found during parsing
3533 node-count) ; number of nodes in the tree, including the root
3534
3535 (put 'cl-struct-js2-ast-root 'js2-visitor 'js2-visit-ast-root)
3536 (put 'cl-struct-js2-ast-root 'js2-printer 'js2-print-script)
3537
3538 (defun js2-visit-ast-root (ast callback)
3539 (dolist (kid (js2-ast-root-kids ast))
3540 (js2-visit-ast kid callback))
3541 (dolist (comment (js2-ast-root-comments ast))
3542 (js2-visit-ast comment callback)))
3543
3544 (defstruct (js2-comment-node
3545 (:include js2-node)
3546 (:constructor nil)
3547 (:constructor make-js2-comment-node (&key (type js2-COMMENT)
3548 (pos js2-token-beg)
3549 len
3550 (format js2-ts-comment-type))))
3551 format) ; 'line, 'block, 'jsdoc or 'html
3552
3553 (put 'cl-struct-js2-comment-node 'js2-visitor 'js2-visit-none)
3554 (put 'cl-struct-js2-comment-node 'js2-printer 'js2-print-comment)
3555
3556 (defun js2-print-comment (n i)
3557 ;; We really ought to link end-of-line comments to their nodes.
3558 ;; Or maybe we could add a new comment type, 'endline.
3559 (insert (js2-make-pad i)
3560 (js2-node-string n)))
3561
3562 (defstruct (js2-expr-stmt-node
3563 (:include js2-node)
3564 (:constructor nil)
3565 (:constructor make-js2-expr-stmt-node (&key (type js2-EXPR_VOID)
3566 (pos js2-ts-cursor)
3567 len
3568 expr)))
3569 "An expression statement."
3570 expr)
3571
3572 (defsubst js2-expr-stmt-node-set-has-result (node)
3573 "Change the node type to `js2-EXPR_RESULT'. Used for code generation."
3574 (setf (js2-node-type node) js2-EXPR_RESULT))
3575
3576 (put 'cl-struct-js2-expr-stmt-node 'js2-visitor 'js2-visit-expr-stmt-node)
3577 (put 'cl-struct-js2-expr-stmt-node 'js2-printer 'js2-print-expr-stmt-node)
3578
3579 (defun js2-visit-expr-stmt-node (n v)
3580 (js2-visit-ast (js2-expr-stmt-node-expr n) v))
3581
3582 (defun js2-print-expr-stmt-node (n indent)
3583 (js2-print-ast (js2-expr-stmt-node-expr n) indent)
3584 (insert ";\n"))
3585
3586 (defstruct (js2-loop-node
3587 (:include js2-scope)
3588 (:constructor nil))
3589 "Abstract supertype of loop nodes."
3590 body ; a `js2-block-node'
3591 lp ; position of left-paren, nil if omitted
3592 rp) ; position of right-paren, nil if omitted
3593
3594 (defstruct (js2-do-node
3595 (:include js2-loop-node)
3596 (:constructor nil)
3597 (:constructor make-js2-do-node (&key (type js2-DO)
3598 (pos js2-token-beg)
3599 len
3600 body
3601 condition
3602 while-pos
3603 lp
3604 rp)))
3605 "AST node for do-loop."
3606 condition ; while (expression)
3607 while-pos) ; buffer position of 'while' keyword
3608
3609 (put 'cl-struct-js2-do-node 'js2-visitor 'js2-visit-do-node)
3610 (put 'cl-struct-js2-do-node 'js2-printer 'js2-print-do-node)
3611
3612 (defun js2-visit-do-node (n v)
3613 (js2-visit-ast (js2-do-node-body n) v)
3614 (js2-visit-ast (js2-do-node-condition n) v))
3615
3616 (defun js2-print-do-node (n i)
3617 (let ((pad (js2-make-pad i)))
3618 (insert pad "do {\n")
3619 (dolist (kid (js2-block-node-kids (js2-do-node-body n)))
3620 (js2-print-ast kid (1+ i)))
3621 (insert pad "} while (")
3622 (js2-print-ast (js2-do-node-condition n) 0)
3623 (insert ");\n")))
3624
3625 (defstruct (js2-while-node
3626 (:include js2-loop-node)
3627 (:constructor nil)
3628 (:constructor make-js2-while-node (&key (type js2-WHILE)
3629 (pos js2-token-beg)
3630 len
3631 body
3632 condition
3633 lp
3634 rp)))
3635 "AST node for while-loop."
3636 condition) ; while-condition
3637
3638 (put 'cl-struct-js2-while-node 'js2-visitor 'js2-visit-while-node)
3639 (put 'cl-struct-js2-while-node 'js2-printer 'js2-print-while-node)
3640
3641 (defun js2-visit-while-node (n v)
3642 (js2-visit-ast (js2-while-node-condition n) v)
3643 (js2-visit-ast (js2-while-node-body n) v))
3644
3645 (defun js2-print-while-node (n i)
3646 (let ((pad (js2-make-pad i)))
3647 (insert pad "while (")
3648 (js2-print-ast (js2-while-node-condition n) 0)
3649 (insert ") {\n")
3650 (js2-print-body (js2-while-node-body n) (1+ i))
3651 (insert pad "}\n")))
3652
3653 (defstruct (js2-for-node
3654 (:include js2-loop-node)
3655 (:constructor nil)
3656 (:constructor make-js2-for-node (&key (type js2-FOR)
3657 (pos js2-ts-cursor)
3658 len
3659 body
3660 init
3661 condition
3662 update
3663 lp
3664 rp)))
3665 "AST node for a C-style for-loop."
3666 init ; initialization expression
3667 condition ; loop condition
3668 update) ; update clause
3669
3670 (put 'cl-struct-js2-for-node 'js2-visitor 'js2-visit-for-node)
3671 (put 'cl-struct-js2-for-node 'js2-printer 'js2-print-for-node)
3672
3673 (defun js2-visit-for-node (n v)
3674 (js2-visit-ast (js2-for-node-init n) v)
3675 (js2-visit-ast (js2-for-node-condition n) v)
3676 (js2-visit-ast (js2-for-node-update n) v)
3677 (js2-visit-ast (js2-for-node-body n) v))
3678
3679 (defun js2-print-for-node (n i)
3680 (let ((pad (js2-make-pad i)))
3681 (insert pad "for (")
3682 (js2-print-ast (js2-for-node-init n) 0)
3683 (insert "; ")
3684 (js2-print-ast (js2-for-node-condition n) 0)
3685 (insert "; ")
3686 (js2-print-ast (js2-for-node-update n) 0)
3687 (insert ") {\n")
3688 (js2-print-body (js2-for-node-body n) (1+ i))
3689 (insert pad "}\n")))
3690
3691 (defstruct (js2-for-in-node
3692 (:include js2-loop-node)
3693 (:constructor nil)
3694 (:constructor make-js2-for-in-node (&key (type js2-FOR)
3695 (pos js2-ts-cursor)
3696 len
3697 body
3698 iterator
3699 object
3700 in-pos
3701 each-pos
3702 foreach-p
3703 lp
3704 rp)))
3705 "AST node for a for..in loop."
3706 iterator ; [var] foo in ...
3707 object ; object over which we're iterating
3708 in-pos ; buffer position of 'in' keyword
3709 each-pos ; buffer position of 'each' keyword, if foreach-p
3710 foreach-p) ; t if it's a for-each loop
3711
3712 (put 'cl-struct-js2-for-in-node 'js2-visitor 'js2-visit-for-in-node)
3713 (put 'cl-struct-js2-for-in-node 'js2-printer 'js2-print-for-in-node)
3714
3715 (defun js2-visit-for-in-node (n v)
3716 (js2-visit-ast (js2-for-in-node-iterator n) v)
3717 (js2-visit-ast (js2-for-in-node-object n) v)
3718 (js2-visit-ast (js2-for-in-node-body n) v))
3719
3720 (defun js2-print-for-in-node (n i)
3721 (let ((pad (js2-make-pad i))
3722 (foreach (js2-for-in-node-foreach-p n)))
3723 (insert pad "for ")
3724 (if foreach
3725 (insert "each "))
3726 (insert "(")
3727 (js2-print-ast (js2-for-in-node-iterator n) 0)
3728 (insert " in ")
3729 (js2-print-ast (js2-for-in-node-object n) 0)
3730 (insert ") {\n")
3731 (js2-print-body (js2-for-in-node-body n) (1+ i))
3732 (insert pad "}\n")))
3733
3734 (defstruct (js2-return-node
3735 (:include js2-node)
3736 (:constructor nil)
3737 (:constructor make-js2-return-node (&key (type js2-RETURN)
3738 (pos js2-ts-cursor)
3739 len
3740 retval)))
3741 "AST node for a return statement."
3742 retval) ; expression to return, or 'undefined
3743
3744 (put 'cl-struct-js2-return-node 'js2-visitor 'js2-visit-return-node)
3745 (put 'cl-struct-js2-return-node 'js2-printer 'js2-print-return-node)
3746
3747 (defun js2-visit-return-node (n v)
3748 (if (js2-return-node-retval n)
3749 (js2-visit-ast (js2-return-node-retval n) v)))
3750
3751 (defun js2-print-return-node (n i)
3752 (insert (js2-make-pad i) "return")
3753 (when (js2-return-node-retval n)
3754 (insert " ")
3755 (js2-print-ast (js2-return-node-retval n) 0))
3756 (insert ";\n"))
3757
3758 (defstruct (js2-if-node
3759 (:include js2-node)
3760 (:constructor nil)
3761 (:constructor make-js2-if-node (&key (type js2-IF)
3762 (pos js2-ts-cursor)
3763 len
3764 condition
3765 then-part
3766 else-pos
3767 else-part
3768 lp
3769 rp)))
3770 "AST node for an if-statement."
3771 condition ; expression
3772 then-part ; statement or block
3773 else-pos ; optional buffer position of 'else' keyword
3774 else-part ; optional statement or block
3775 lp ; position of left-paren, nil if omitted
3776 rp) ; position of right-paren, nil if omitted
3777
3778 (put 'cl-struct-js2-if-node 'js2-visitor 'js2-visit-if-node)
3779 (put 'cl-struct-js2-if-node 'js2-printer 'js2-print-if-node)
3780
3781 (defun js2-visit-if-node (n v)
3782 (js2-visit-ast (js2-if-node-condition n) v)
3783 (js2-visit-ast (js2-if-node-then-part n) v)
3784 (if (js2-if-node-else-part n)
3785 (js2-visit-ast (js2-if-node-else-part n) v)))
3786
3787 (defun js2-print-if-node (n i)
3788 (let ((pad (js2-make-pad i))
3789 (then-part (js2-if-node-then-part n))
3790 (else-part (js2-if-node-else-part n)))
3791 (insert pad "if (")
3792 (js2-print-ast (js2-if-node-condition n) 0)
3793 (insert ") {\n")
3794 (js2-print-body then-part (1+ i))
3795 (insert pad "}")
3796 (cond
3797 ((not else-part)
3798 (insert "\n"))
3799 ((js2-if-node-p else-part)
3800 (insert " else ")
3801 (js2-print-body else-part i))
3802 (t
3803 (insert " else {\n")
3804 (js2-print-body else-part (1+ i))
3805 (insert pad "}\n")))))
3806
3807 (defstruct (js2-try-node
3808 (:include js2-node)
3809 (:constructor nil)
3810 (:constructor make-js2-try-node (&key (type js2-TRY)
3811 (pos js2-ts-cursor)
3812 len
3813 try-block
3814 catch-clauses
3815 finally-block)))
3816 "AST node for a try-statement."
3817 try-block
3818 catch-clauses ; a lisp list of `js2-catch-node'
3819 finally-block) ; a `js2-finally-node'
3820
3821 (put 'cl-struct-js2-try-node 'js2-visitor 'js2-visit-try-node)
3822 (put 'cl-struct-js2-try-node 'js2-printer 'js2-print-try-node)
3823
3824 (defun js2-visit-try-node (n v)
3825 (js2-visit-ast (js2-try-node-try-block n) v)
3826 (dolist (clause (js2-try-node-catch-clauses n))
3827 (js2-visit-ast clause v))
3828 (if (js2-try-node-finally-block n)
3829 (js2-visit-ast (js2-try-node-finally-block n) v)))
3830
3831 (defun js2-print-try-node (n i)
3832 (let ((pad (js2-make-pad i))
3833 (catches (js2-try-node-catch-clauses n))
3834 (finally (js2-try-node-finally-block n)))
3835 (insert pad "try {\n")
3836 (js2-print-body (js2-try-node-try-block n) (1+ i))
3837 (insert pad "}")
3838 (when catches
3839 (dolist (catch catches)
3840 (js2-print-ast catch i)))
3841 (if finally
3842 (js2-print-ast finally i)
3843 (insert "\n"))))
3844
3845 (defstruct (js2-catch-node
3846 (:include js2-node)
3847 (:constructor nil)
3848 (:constructor make-js2-catch-node (&key (type js2-CATCH)
3849 (pos js2-ts-cursor)
3850 len
3851 var-name
3852 guard-kwd
3853 guard-expr
3854 block
3855 lp
3856 rp)))
3857 "AST node for a catch clause."
3858 var-name ; a `js2-name-node'
3859 guard-kwd ; relative buffer position of "if" in "catch (x if ...)"
3860 guard-expr ; catch condition, a `js2-node'
3861 block ; statements, a `js2-block-node'
3862 lp ; buffer position of left-paren, nil if omitted
3863 rp) ; buffer position of right-paren, nil if omitted
3864
3865 (put 'cl-struct-js2-catch-node 'js2-visitor 'js2-visit-catch-node)
3866 (put 'cl-struct-js2-catch-node 'js2-printer 'js2-print-catch-node)
3867
3868 (defun js2-visit-catch-node (n v)
3869 (js2-visit-ast (js2-catch-node-var-name n) v)
3870 (when (js2-catch-node-guard-kwd n)
3871 (js2-visit-ast (js2-catch-node-guard-expr n) v))
3872 (js2-visit-ast (js2-catch-node-block n) v))
3873
3874 (defun js2-print-catch-node (n i)
3875 (let ((pad (js2-make-pad i))
3876 (guard-kwd (js2-catch-node-guard-kwd n))
3877 (guard-expr (js2-catch-node-guard-expr n)))
3878 (insert " catch (")
3879 (js2-print-ast (js2-catch-node-var-name n) 0)
3880 (when guard-kwd
3881 (insert " if ")
3882 (js2-print-ast guard-expr 0))
3883 (insert ") {\n")
3884 (js2-print-body (js2-catch-node-block n) (1+ i))
3885 (insert pad "}")))
3886
3887 (defstruct (js2-finally-node
3888 (:include js2-node)
3889 (:constructor nil)
3890 (:constructor make-js2-finally-node (&key (type js2-FINALLY)
3891 (pos js2-ts-cursor)
3892 len
3893 body)))
3894 "AST node for a finally clause."
3895 body) ; a `js2-node', often but not always a block node
3896
3897 (put 'cl-struct-js2-finally-node 'js2-visitor 'js2-visit-finally-node)
3898 (put 'cl-struct-js2-finally-node 'js2-printer 'js2-print-finally-node)
3899
3900 (defun js2-visit-finally-node (n v)
3901 (js2-visit-ast (js2-finally-node-body n) v))
3902
3903 (defun js2-print-finally-node (n i)
3904 (let ((pad (js2-make-pad i)))
3905 (insert " finally {\n")
3906 (js2-print-body (js2-finally-node-body n) (1+ i))
3907 (insert pad "}\n")))
3908
3909 (defstruct (js2-switch-node
3910 (:include js2-node)
3911 (:constructor nil)
3912 (:constructor make-js2-switch-node (&key (type js2-SWITCH)
3913 (pos js2-ts-cursor)
3914 len
3915 discriminant
3916 cases
3917 lp
3918 rp)))
3919 "AST node for a switch statement."
3920 discriminant ; a `js2-node' (switch expression)
3921 cases ; a lisp list of `js2-case-node'
3922 lp ; position of open-paren for discriminant, nil if omitted
3923 rp) ; position of close-paren for discriminant, nil if omitted
3924
3925 (put 'cl-struct-js2-switch-node 'js2-visitor 'js2-visit-switch-node)
3926 (put 'cl-struct-js2-switch-node 'js2-printer 'js2-print-switch-node)
3927
3928 (defun js2-visit-switch-node (n v)
3929 (js2-visit-ast (js2-switch-node-discriminant n) v)
3930 (dolist (c (js2-switch-node-cases n))
3931 (js2-visit-ast c v)))
3932
3933 (defun js2-print-switch-node (n i)
3934 (let ((pad (js2-make-pad i))
3935 (cases (js2-switch-node-cases n)))
3936 (insert pad "switch (")
3937 (js2-print-ast (js2-switch-node-discriminant n) 0)
3938 (insert ") {\n")
3939 (dolist (case cases)
3940 (js2-print-ast case i))
3941 (insert pad "}\n")))
3942
3943 (defstruct (js2-case-node
3944 (:include js2-block-node)
3945 (:constructor nil)
3946 (:constructor make-js2-case-node (&key (type js2-CASE)
3947 (pos js2-ts-cursor)
3948 len
3949 kids
3950 expr)))
3951 "AST node for a case clause of a switch statement."
3952 expr) ; the case expression (nil for default)
3953
3954 (put 'cl-struct-js2-case-node 'js2-visitor 'js2-visit-case-node)
3955 (put 'cl-struct-js2-case-node 'js2-printer 'js2-print-case-node)
3956
3957 (defun js2-visit-case-node (n v)
3958 (if (js2-case-node-expr n) ; nil for default: case
3959 (js2-visit-ast (js2-case-node-expr n) v))
3960 (js2-visit-block n v))
3961
3962 (defun js2-print-case-node (n i)
3963 (let ((pad (js2-make-pad i))
3964 (expr (js2-case-node-expr n)))
3965 (insert pad)
3966 (if (null expr)
3967 (insert "default:\n")
3968 (insert "case ")
3969 (js2-print-ast expr 0)
3970 (insert ":\n"))
3971 (dolist (kid (js2-case-node-kids n))
3972 (js2-print-ast kid (1+ i)))))
3973
3974 (defstruct (js2-throw-node
3975 (:include js2-node)
3976 (:constructor nil)
3977 (:constructor make-js2-throw-node (&key (type js2-THROW)
3978 (pos js2-ts-cursor)
3979 len
3980 expr)))
3981 "AST node for a throw statement."
3982 expr) ; the expression to throw
3983
3984 (put 'cl-struct-js2-throw-node 'js2-visitor 'js2-visit-throw-node)
3985 (put 'cl-struct-js2-throw-node 'js2-printer 'js2-print-throw-node)
3986
3987 (defun js2-visit-throw-node (n v)
3988 (js2-visit-ast (js2-throw-node-expr n) v))
3989
3990 (defun js2-print-throw-node (n i)
3991 (insert (js2-make-pad i) "throw ")
3992 (js2-print-ast (js2-throw-node-expr n) 0)
3993 (insert ";\n"))
3994
3995 (defstruct (js2-with-node
3996 (:include js2-node)
3997 (:constructor nil)
3998 (:constructor make-js2-with-node (&key (type js2-WITH)
3999 (pos js2-ts-cursor)
4000 len
4001 object
4002 body
4003 lp
4004 rp)))
4005 "AST node for a with-statement."
4006 object
4007 body
4008 lp ; buffer position of left-paren around object, nil if omitted
4009 rp) ; buffer position of right-paren around object, nil if omitted
4010
4011 (put 'cl-struct-js2-with-node 'js2-visitor 'js2-visit-with-node)
4012 (put 'cl-struct-js2-with-node 'js2-printer 'js2-print-with-node)
4013
4014 (defun js2-visit-with-node (n v)
4015 (js2-visit-ast (js2-with-node-object n) v)
4016 (js2-visit-ast (js2-with-node-body n) v))
4017
4018 (defun js2-print-with-node (n i)
4019 (let ((pad (js2-make-pad i)))
4020 (insert pad "with (")
4021 (js2-print-ast (js2-with-node-object n) 0)
4022 (insert ") {\n")
4023 (js2-print-body (js2-with-node-body n) (1+ i))
4024 (insert pad "}\n")))
4025
4026 (defstruct (js2-label-node
4027 (:include js2-node)
4028 (:constructor nil)
4029 (:constructor make-js2-label-node (&key (type js2-LABEL)
4030 (pos js2-ts-cursor)
4031 len
4032 name)))
4033 "AST node for a statement label or case label."
4034 name ; a string
4035 loop) ; for validating and code-generating continue-to-label
4036
4037 (put 'cl-struct-js2-label-node 'js2-visitor 'js2-visit-none)
4038 (put 'cl-struct-js2-label-node 'js2-printer 'js2-print-label)
4039
4040 (defun js2-print-label (n i)
4041 (insert (js2-make-pad i)
4042 (js2-label-node-name n)
4043 ":\n"))
4044
4045 (defstruct (js2-labeled-stmt-node
4046 (:include js2-node)
4047 (:constructor nil)
4048 ;; type needs to be in `js2-side-effecting-tokens' to avoid spurious
4049 ;; no-side-effects warnings, hence js2-EXPR_RESULT.
4050 (:constructor make-js2-labeled-stmt-node (&key (type js2-EXPR_RESULT)
4051 (pos js2-ts-cursor)
4052 len
4053 labels
4054 stmt)))
4055 "AST node for a statement with one or more labels.
4056 Multiple labels for a statement are collapsed into the labels field."
4057 labels ; lisp list of `js2-label-node'
4058 stmt) ; the statement these labels are for
4059
4060 (put 'cl-struct-js2-labeled-stmt-node 'js2-visitor 'js2-visit-labeled-stmt)
4061 (put 'cl-struct-js2-labeled-stmt-node 'js2-printer 'js2-print-labeled-stmt)
4062
4063 (defun js2-get-label-by-name (lbl-stmt name)
4064 "Return a `js2-label-node' by NAME from LBL-STMT's labels list.
4065 Returns nil if no such label is in the list."
4066 (let ((label-list (js2-labeled-stmt-node-labels lbl-stmt))
4067 result)
4068 (while (and label-list (not result))
4069 (if (string= (js2-label-node-name (car label-list)) name)
4070 (setq result (car label-list))
4071 (setq label-list (cdr label-list))))
4072 result))
4073
4074 (defun js2-visit-labeled-stmt (n v)
4075 (dolist (label (js2-labeled-stmt-node-labels n))
4076 (js2-visit-ast label v))
4077 (js2-visit-ast (js2-labeled-stmt-node-stmt n) v))
4078
4079 (defun js2-print-labeled-stmt (n i)
4080 (dolist (label (js2-labeled-stmt-node-labels n))
4081 (js2-print-ast label i))
4082 (js2-print-ast (js2-labeled-stmt-node-stmt n) (1+ i)))
4083
4084 (defun js2-labeled-stmt-node-contains (node label)
4085 "Return t if NODE contains LABEL in its label set.
4086 NODE is a `js2-labels-node'. LABEL is an identifier."
4087 (loop for nl in (js2-labeled-stmt-node-labels node)
4088 if (string= label (js2-label-node-name nl))
4089 return t
4090 finally return nil))
4091
4092 (defsubst js2-labeled-stmt-node-add-label (node label)
4093 "Add a `js2-label-node' to the label set for this statement."
4094 (setf (js2-labeled-stmt-node-labels node)
4095 (nconc (js2-labeled-stmt-node-labels node) (list label))))
4096
4097 (defstruct (js2-jump-node
4098 (:include js2-node)
4099 (:constructor nil))
4100 "Abstract supertype of break and continue nodes."
4101 label ; `js2-name-node' for location of label identifier, if present
4102 target) ; target js2-labels-node or loop/switch statement
4103
4104 (defun js2-visit-jump-node (n v)
4105 ;; we don't visit the target, since it's a back-link
4106 (if (js2-jump-node-label n)
4107 (js2-visit-ast (js2-jump-node-label n) v)))
4108
4109 (defstruct (js2-break-node
4110 (:include js2-jump-node)
4111 (:constructor nil)
4112 (:constructor make-js2-break-node (&key (type js2-BREAK)
4113 (pos js2-ts-cursor)
4114 len
4115 label
4116 target)))
4117 "AST node for a break statement.
4118 The label field is a `js2-name-node', possibly nil, for the named label
4119 if provided. E.g. in 'break foo', it represents 'foo'. The target field
4120 is the target of the break - a label node or enclosing loop/switch statement.")
4121
4122 (put 'cl-struct-js2-break-node 'js2-visitor 'js2-visit-jump-node)
4123 (put 'cl-struct-js2-break-node 'js2-printer 'js2-print-break-node)
4124
4125 (defun js2-print-break-node (n i)
4126 (insert (js2-make-pad i) "break")
4127 (when (js2-break-node-label n)
4128 (insert " ")
4129 (js2-print-ast (js2-break-node-label n) 0))
4130 (insert ";\n"))
4131
4132 (defstruct (js2-continue-node
4133 (:include js2-jump-node)
4134 (:constructor nil)
4135 (:constructor make-js2-continue-node (&key (type js2-CONTINUE)
4136 (pos js2-ts-cursor)
4137 len
4138 label
4139 target)))
4140 "AST node for a continue statement.
4141 The label field is the user-supplied enclosing label name, a `js2-name-node'.
4142 It is nil if continue specifies no label. The target field is the jump target:
4143 a `js2-label-node' or the innermost enclosing loop.")
4144
4145 (put 'cl-struct-js2-continue-node 'js2-visitor 'js2-visit-jump-node)
4146 (put 'cl-struct-js2-continue-node 'js2-printer 'js2-print-continue-node)
4147
4148 (defun js2-print-continue-node (n i)
4149 (insert (js2-make-pad i) "continue")
4150 (when (js2-continue-node-label n)
4151 (insert " ")
4152 (js2-print-ast (js2-continue-node-label n) 0))
4153 (insert ";\n"))
4154
4155 (defstruct (js2-function-node
4156 (:include js2-script-node)
4157 (:constructor nil)
4158 (:constructor make-js2-function-node (&key (type js2-FUNCTION)
4159 (pos js2-ts-cursor)
4160 len
4161 (ftype 'FUNCTION)
4162 (form 'FUNCTION_STATEMENT)
4163 (name "")
4164 params
4165 body
4166 lp
4167 rp)))
4168 "AST node for a function declaration.
4169 The `params' field is a lisp list of nodes. Each node is either a simple
4170 `js2-name-node', or if it's a destructuring-assignment parameter, a
4171 `js2-array-node' or `js2-object-node'."
4172 ftype ; FUNCTION, GETTER or SETTER
4173 form ; FUNCTION_{STATEMENT|EXPRESSION|EXPRESSION_STATEMENT}
4174 name ; function name (a `js2-name-node', or nil if anonymous)
4175 params ; a lisp list of destructuring forms or simple name nodes
4176 body ; a `js2-block-node'
4177 lp ; position of arg-list open-paren, or nil if omitted
4178 rp ; position of arg-list close-paren, or nil if omitted
4179 ignore-dynamic ; ignore value of the dynamic-scope flag (interpreter only)
4180 needs-activation ; t if we need an activation object for this frame
4181 is-generator ; t if this function contains a yield
4182 member-expr) ; nonstandard Ecma extension from Rhino
4183
4184 (put 'cl-struct-js2-function-node 'js2-visitor 'js2-visit-function-node)
4185 (put 'cl-struct-js2-function-node 'js2-printer 'js2-print-function-node)
4186
4187 (defun js2-visit-function-node (n v)
4188 (if (js2-function-node-name n)
4189 (js2-visit-ast (js2-function-node-name n) v))
4190 (dolist (p (js2-function-node-params n))
4191 (js2-visit-ast p v))
4192 (js2-visit-ast (js2-function-node-body n) v))
4193
4194 (defun js2-print-function-node (n i)
4195 (let ((pad (js2-make-pad i))
4196 (getter (js2-node-get-prop n 'GETTER_SETTER))
4197 (name (js2-function-node-name n))
4198 (params (js2-function-node-params n))
4199 (body (js2-function-node-body n))
4200 (expr (eq (js2-function-node-form n) 'FUNCTION_EXPRESSION)))
4201 (unless getter
4202 (insert pad "function"))
4203 (when name
4204 (insert " ")
4205 (js2-print-ast name 0))
4206 (insert "(")
4207 (loop with len = (length params)
4208 for param in params
4209 for count from 1
4210 do
4211 (js2-print-ast param 0)
4212 (if (< count len)
4213 (insert ", ")))
4214 (insert ") {")
4215 (unless expr
4216 (insert "\n"))
4217 ;; TODO: fix this to be smarter about indenting, etc.
4218 (js2-print-body body (1+ i))
4219 (insert pad "}")
4220 (unless expr
4221 (insert "\n"))))
4222
4223 (defsubst js2-function-name (node)
4224 "Return function name for NODE, a `js2-function-node', or nil if anonymous."
4225 (and (js2-function-node-name node)
4226 (js2-name-node-name (js2-function-node-name node))))
4227
4228 ;; Having this be an expression node makes it more flexible.
4229 ;; There are IDE contexts, such as indentation in a for-loop initializer,
4230 ;; that work better if you assume it's an expression. Whenever we have
4231 ;; a standalone var/const declaration, we just wrap with an expr stmt.
4232 ;; Eclipse apparently screwed this up and now has two versions, expr and stmt.
4233 (defstruct (js2-var-decl-node
4234 (:include js2-node)
4235 (:constructor nil)
4236 (:constructor make-js2-var-decl-node (&key (type js2-VAR)
4237 (pos js2-token-beg)
4238 len
4239 kids
4240 decl-type)))
4241 "AST node for a variable declaration list (VAR, CONST or LET).
4242 The node bounds differ depending on the declaration type. For VAR or
4243 CONST declarations, the bounds include the var/const keyword. For LET
4244 declarations, the node begins at the position of the first child."
4245 kids ; a lisp list of `js2-var-init-node' structs.
4246 decl-type) ; js2-VAR, js2-CONST or js2-LET
4247
4248 (put 'cl-struct-js2-var-decl-node 'js2-visitor 'js2-visit-var-decl)
4249 (put 'cl-struct-js2-var-decl-node 'js2-printer 'js2-print-var-decl)
4250
4251 (defun js2-visit-var-decl (n v)
4252 (dolist (kid (js2-var-decl-node-kids n))
4253 (js2-visit-ast kid v)))
4254
4255 (defun js2-print-var-decl (n i)
4256 (let ((pad (js2-make-pad i))
4257 (tt (js2-var-decl-node-decl-type n)))
4258 (insert pad)
4259 (insert (cond
4260 ((= tt js2-VAR) "var ")
4261 ((= tt js2-LET) "") ; handled by parent let-{expr/stmt}
4262 ((= tt js2-CONST) "const ")
4263 (t
4264 (error "malformed var-decl node"))))
4265 (loop with kids = (js2-var-decl-node-kids n)
4266 with len = (length kids)
4267 for kid in kids
4268 for count from 1
4269 do
4270 (js2-print-ast kid 0)
4271 (if (< count len)
4272 (insert ", ")))))
4273
4274 (defstruct (js2-var-init-node
4275 (:include js2-node)
4276 (:constructor nil)
4277 (:constructor make-js2-var-init-node (&key (type js2-VAR)
4278 (pos js2-ts-cursor)
4279 len
4280 target
4281 initializer)))
4282 "AST node for a variable declaration.
4283 The type field will be js2-CONST for a const decl."
4284 target ; `js2-name-node', `js2-object-node', or `js2-array-node'
4285 initializer) ; initializer expression, a `js2-node'
4286
4287 (put 'cl-struct-js2-var-init-node 'js2-visitor 'js2-visit-var-init-node)
4288 (put 'cl-struct-js2-var-init-node 'js2-printer 'js2-print-var-init-node)
4289
4290 (defun js2-visit-var-init-node (n v)
4291 (js2-visit-ast (js2-var-init-node-target n) v)
4292 (if (js2-var-init-node-initializer n)
4293 (js2-visit-ast (js2-var-init-node-initializer n) v)))
4294
4295 (defun js2-print-var-init-node (n i)
4296 (let ((pad (js2-make-pad i))
4297 (name (js2-var-init-node-target n))
4298 (init (js2-var-init-node-initializer n)))
4299 (insert pad)
4300 (js2-print-ast name 0)
4301 (when init
4302 (insert " = ")
4303 (js2-print-ast init 0))))
4304
4305 (defstruct (js2-cond-node
4306 (:include js2-node)
4307 (:constructor nil)
4308 (:constructor make-js2-cond-node (&key (type js2-HOOK)
4309 (pos js2-ts-cursor)
4310 len
4311 test-expr
4312 true-expr
4313 false-expr
4314 q-pos
4315 c-pos)))
4316 "AST node for the ternary operator"
4317 test-expr
4318 true-expr
4319 false-expr
4320 q-pos ; buffer position of ?
4321 c-pos) ; buffer position of :
4322
4323 (put 'cl-struct-js2-cond-node 'js2-visitor 'js2-visit-cond-node)
4324 (put 'cl-struct-js2-cond-node 'js2-printer 'js2-print-cond-node)
4325
4326 (defun js2-visit-cond-node (n v)
4327 (js2-visit-ast (js2-cond-node-test-expr n) v)
4328 (js2-visit-ast (js2-cond-node-true-expr n) v)
4329 (js2-visit-ast (js2-cond-node-false-expr n) v))
4330
4331 (defun js2-print-cond-node (n i)
4332 (let ((pad (js2-make-pad i)))
4333 (insert pad)
4334 (js2-print-ast (js2-cond-node-test-expr n) 0)
4335 (insert " ? ")
4336 (js2-print-ast (js2-cond-node-true-expr n) 0)
4337 (insert " : ")
4338 (js2-print-ast (js2-cond-node-false-expr n) 0)))
4339
4340 (defstruct (js2-infix-node
4341 (:include js2-node)
4342 (:constructor nil)
4343 (:constructor make-js2-infix-node (&key type
4344 (pos js2-ts-cursor)
4345 len
4346 op-pos
4347 left
4348 right)))
4349 "Represents infix expressions.
4350 Includes assignment ops like `|=', and the comma operator.
4351 The type field inherited from `js2-node' holds the operator."
4352 op-pos ; buffer position where operator begins
4353 left ; any `js2-node'
4354 right) ; any `js2-node'
4355
4356 (put 'cl-struct-js2-infix-node 'js2-visitor 'js2-visit-infix-node)
4357 (put 'cl-struct-js2-infix-node 'js2-printer 'js2-print-infix-node)
4358
4359 (defun js2-visit-infix-node (n v)
4360 (when (js2-infix-node-left n)
4361 (js2-visit-ast (js2-infix-node-left n) v))
4362 (when (js2-infix-node-right n)
4363 (js2-visit-ast (js2-infix-node-right n) v)))
4364
4365 (defconst js2-operator-tokens
4366 (let ((table (make-hash-table :test 'eq))
4367 (tokens
4368 (list (cons js2-IN "in")
4369 (cons js2-TYPEOF "typeof")
4370 (cons js2-INSTANCEOF "instanceof")
4371 (cons js2-DELPROP "delete")
4372 (cons js2-COMMA ",")
4373 (cons js2-COLON ":")
4374 (cons js2-OR "||")
4375 (cons js2-AND "&&")
4376 (cons js2-INC "++")
4377 (cons js2-DEC "--")
4378 (cons js2-BITOR "|")
4379 (cons js2-BITXOR "^")
4380 (cons js2-BITAND "&")
4381 (cons js2-EQ "==")
4382 (cons js2-NE "!=")
4383 (cons js2-LT "<")
4384 (cons js2-LE "<=")
4385 (cons js2-GT ">")
4386 (cons js2-GE ">=")
4387 (cons js2-LSH "<<")
4388 (cons js2-RSH ">>")
4389 (cons js2-URSH ">>>")
4390 (cons js2-ADD "+") ; infix plus
4391 (cons js2-SUB "-") ; infix minus
4392 (cons js2-MUL "*")
4393 (cons js2-DIV "/")
4394 (cons js2-MOD "%")
4395 (cons js2-NOT "!")
4396 (cons js2-BITNOT "~")
4397 (cons js2-POS "+") ; unary plus
4398 (cons js2-NEG "-") ; unary minus
4399 (cons js2-SHEQ "===") ; shallow equality
4400 (cons js2-SHNE "!==") ; shallow inequality
4401 (cons js2-ASSIGN "=")
4402 (cons js2-ASSIGN_BITOR "|=")
4403 (cons js2-ASSIGN_BITXOR "^=")
4404 (cons js2-ASSIGN_BITAND "&=")
4405 (cons js2-ASSIGN_LSH "<<=")
4406 (cons js2-ASSIGN_RSH ">>=")
4407 (cons js2-ASSIGN_URSH ">>>=")
4408 (cons js2-ASSIGN_ADD "+=")
4409 (cons js2-ASSIGN_SUB "-=")
4410 (cons js2-ASSIGN_MUL "*=")
4411 (cons js2-ASSIGN_DIV "/=")
4412 (cons js2-ASSIGN_MOD "%="))))
4413 (loop for (k . v) in tokens do
4414 (puthash k v table))
4415 table))
4416
4417 (defun js2-print-infix-node (n i)
4418 (let* ((tt (js2-node-type n))
4419 (op (gethash tt js2-operator-tokens)))
4420 (unless op
4421 (error "unrecognized infix operator %s" (js2-node-type n)))
4422 (insert (js2-make-pad i))
4423 (js2-print-ast (js2-infix-node-left n) 0)
4424 (unless (= tt js2-COMMA)
4425 (insert " "))
4426 (insert op)
4427 (insert " ")
4428 (js2-print-ast (js2-infix-node-right n) 0)))
4429
4430 (defstruct (js2-assign-node
4431 (:include js2-infix-node)
4432 (:constructor nil)
4433 (:constructor make-js2-assign-node (&key type
4434 (pos js2-ts-cursor)
4435 len
4436 op-pos
4437 left
4438 right)))
4439 "Represents any assignment.
4440 The type field holds the actual assignment operator.")
4441
4442 (put 'cl-struct-js2-assign-node 'js2-visitor 'js2-visit-infix-node)
4443 (put 'cl-struct-js2-assign-node 'js2-printer 'js2-print-infix-node)
4444
4445 (defstruct (js2-unary-node
4446 (:include js2-node)
4447 (:constructor nil)
4448 (:constructor make-js2-unary-node (&key type ; required
4449 (pos js2-ts-cursor)
4450 len
4451 operand)))
4452 "AST node type for unary operator nodes.
4453 The type field can be NOT, BITNOT, POS, NEG, INC, DEC,
4454 TYPEOF, or DELPROP. For INC or DEC, a 'postfix node
4455 property is added if the operator follows the operand."
4456 operand) ; a `js2-node' expression
4457
4458 (put 'cl-struct-js2-unary-node 'js2-visitor 'js2-visit-unary-node)
4459 (put 'cl-struct-js2-unary-node 'js2-printer 'js2-print-unary-node)
4460
4461 (defun js2-visit-unary-node (n v)
4462 (js2-visit-ast (js2-unary-node-operand n) v))
4463
4464 (defun js2-print-unary-node (n i)
4465 (let* ((tt (js2-node-type n))
4466 (op (gethash tt js2-operator-tokens))
4467 (postfix (js2-node-get-prop n 'postfix)))
4468 (unless op
4469 (error "unrecognized unary operator %s" tt))
4470 (insert (js2-make-pad i))
4471 (unless postfix
4472 (insert op))
4473 (if (or (= tt js2-TYPEOF)
4474 (= tt js2-DELPROP))
4475 (insert " "))
4476 (js2-print-ast (js2-unary-node-operand n) 0)
4477 (when postfix
4478 (insert op))))
4479
4480 (defstruct (js2-let-node
4481 (:include js2-scope)
4482 (:constructor nil)
4483 (:constructor make-js2-let-node (&key (type js2-LETEXPR)
4484 (pos js2-token-beg)
4485 len
4486 vars
4487 body
4488 lp
4489 rp)))
4490 "AST node for a let expression or a let statement.
4491 Note that a let declaration such as let x=6, y=7 is a `js2-var-decl-node'."
4492 vars ; a `js2-var-decl-node'
4493 body ; a `js2-node' representing the expression or body block
4494 lp
4495 rp)
4496
4497 (put 'cl-struct-js2-let-node 'js2-visitor 'js2-visit-let-node)
4498 (put 'cl-struct-js2-let-node 'js2-printer 'js2-print-let-node)
4499
4500 (defun js2-visit-let-node (n v)
4501 (when (js2-let-node-vars n)
4502 (js2-visit-ast (js2-let-node-vars n) v))
4503 (when (js2-let-node-body n)
4504 (js2-visit-ast (js2-let-node-body n) v)))
4505
4506 (defun js2-print-let-node (n i)
4507 (insert (js2-make-pad i) "let (")
4508 (js2-print-ast (js2-let-node-vars n) 0)
4509 (insert ") ")
4510 (js2-print-ast (js2-let-node-body n) i))
4511
4512 (defstruct (js2-keyword-node
4513 (:include js2-node)
4514 (:constructor nil)
4515 (:constructor make-js2-keyword-node (&key type
4516 (pos js2-token-beg)
4517 (len (- js2-ts-cursor pos)))))
4518 "AST node representing a literal keyword such as `null'.
4519 Used for `null', `this', `true', `false' and `debugger'.
4520 The node type is set to js2-NULL, js2-THIS, etc.")
4521
4522 (put 'cl-struct-js2-keyword-node 'js2-visitor 'js2-visit-none)
4523 (put 'cl-struct-js2-keyword-node 'js2-printer 'js2-print-keyword-node)
4524
4525 (defun js2-print-keyword-node (n i)
4526 (insert (js2-make-pad i)
4527 (let ((tt (js2-node-type n)))
4528 (cond
4529 ((= tt 'js2-THIS) "this")
4530 ((= tt 'js2-NULL) "null")
4531 ((= tt 'js2-TRUE) "true")
4532 ((= tt 'js2-FALSE) "false")
4533 ((= tt 'js2-DEBUGGER) "debugger")
4534 (t (error "Invalid keyword literal type: %d" tt))))))
4535
4536 (defsubst js2-this-node-p (node)
4537 "Return t if this node is a `js2-literal-node' of type js2-THIS."
4538 (eq (js2-node-type node) js2-THIS))
4539
4540 (defstruct (js2-new-node
4541 (:include js2-node)
4542 (:constructor nil)
4543 (:constructor make-js2-new-node (&key (type js2-NEW)
4544 (pos js2-token-beg)
4545 len
4546 target
4547 args
4548 initializer
4549 lp
4550 rp)))
4551 "AST node for new-expression such as new Foo()."
4552 target ; an identifier or reference
4553 args ; a lisp list of argument nodes
4554 lp ; position of left-paren, nil if omitted
4555 rp ; position of right-paren, nil if omitted
4556 initializer) ; experimental Rhino syntax: optional `js2-object-node'
4557
4558 (put 'cl-struct-js2-new-node 'js2-visitor 'js2-visit-new-node)
4559 (put 'cl-struct-js2-new-node 'js2-printer 'js2-print-new-node)
4560
4561 (defun js2-visit-new-node (n v)
4562 (js2-visit-ast (js2-new-node-target n) v)
4563 (dolist (arg (js2-new-node-args n))
4564 (js2-visit-ast arg v))
4565 (when (js2-new-node-initializer n)
4566 (js2-visit-ast (js2-new-node-initializer n) v)))
4567
4568 (defun js2-print-new-node (n i)
4569 (insert (js2-make-pad i) "new ")
4570 (js2-print-ast (js2-new-node-target n))
4571 (insert "(")
4572 (js2-print-list (js2-new-node-args n))
4573 (insert ")")
4574 (when (js2-new-node-initializer n)
4575 (insert " ")
4576 (js2-print-ast (js2-new-node-initializer n))))
4577
4578 (defstruct (js2-name-node
4579 (:include js2-node)
4580 (:constructor nil)
4581 (:constructor make-js2-name-node (&key (type js2-NAME)
4582 (pos js2-token-beg)
4583 (len (- js2-ts-cursor
4584 js2-token-beg))
4585 (name js2-ts-string))))
4586 "AST node for a JavaScript identifier"
4587 name ; a string
4588 scope) ; a `js2-scope' (optional, used for codegen)
4589
4590 (put 'cl-struct-js2-name-node 'js2-visitor 'js2-visit-none)
4591 (put 'cl-struct-js2-name-node 'js2-printer 'js2-print-name-node)
4592
4593 (defun js2-print-name-node (n i)
4594 (insert (js2-make-pad i)
4595 (js2-name-node-name n)))
4596
4597 (defsubst js2-name-node-length (node)
4598 "Return identifier length of NODE, a `js2-name-node'.
4599 Returns 0 if NODE is nil or its identifier field is nil."
4600 (if node
4601 (length (js2-name-node-name node))
4602 0))
4603
4604 (defstruct (js2-number-node
4605 (:include js2-node)
4606 (:constructor nil)
4607 (:constructor make-js2-number-node (&key (type js2-NUMBER)
4608 (pos js2-token-beg)
4609 (len (- js2-ts-cursor
4610 js2-token-beg))
4611 (value js2-ts-string)
4612 (num-value js2-ts-number))))
4613 "AST node for a number literal."
4614 value ; the original string, e.g. "6.02e23"
4615 num-value) ; the parsed number value
4616
4617 (put 'cl-struct-js2-number-node 'js2-visitor 'js2-visit-none)
4618 (put 'cl-struct-js2-number-node 'js2-printer 'js2-print-number-node)
4619
4620 (defun js2-print-number-node (n i)
4621 (insert (js2-make-pad i)
4622 (number-to-string (js2-number-node-value n))))
4623
4624 (defstruct (js2-regexp-node
4625 (:include js2-node)
4626 (:constructor nil)
4627 (:constructor make-js2-regexp-node (&key (type js2-REGEXP)
4628 (pos js2-token-beg)
4629 (len (- js2-ts-cursor
4630 js2-token-beg))
4631 value
4632 flags)))
4633 "AST node for a regular expression literal."
4634 value ; the regexp string, without // delimiters
4635 flags) ; a string of flags, e.g. `mi'.
4636
4637 (put 'cl-struct-js2-regexp-node 'js2-visitor 'js2-visit-none)
4638 (put 'cl-struct-js2-regexp-node 'js2-printer 'js2-print-regexp)
4639
4640 (defun js2-print-regexp (n i)
4641 (insert (js2-make-pad i)
4642 "/"
4643 (js2-regexp-node-value n)
4644 "/")
4645 (if (js2-regexp-node-flags n)
4646 (insert (js2-regexp-node-flags n))))
4647
4648 (defstruct (js2-string-node
4649 (:include js2-node)
4650 (:constructor nil)
4651 (:constructor make-js2-string-node (&key (type js2-STRING)
4652 (pos js2-token-beg)
4653 (len (- js2-ts-cursor
4654 js2-token-beg))
4655 (value js2-ts-string))))
4656 "String literal.
4657 Escape characters are not evaluated; e.g. \n is 2 chars in value field.
4658 You can tell the quote type by looking at the first character."
4659 value) ; the characters of the string, including the quotes
4660
4661 (put 'cl-struct-js2-string-node 'js2-visitor 'js2-visit-none)
4662 (put 'cl-struct-js2-string-node 'js2-printer 'js2-print-string-node)
4663
4664 (defun js2-print-string-node (n i)
4665 (insert (js2-make-pad i)
4666 (js2-node-string n)))
4667
4668 (defstruct (js2-array-node
4669 (:include js2-node)
4670 (:constructor nil)
4671 (:constructor make-js2-array-node (&key (type js2-ARRAYLIT)
4672 (pos js2-ts-cursor)
4673 len
4674 elems)))
4675 "AST node for an array literal."
4676 elems) ; list of expressions. [foo,,bar] yields a nil middle element.
4677
4678 (put 'cl-struct-js2-array-node 'js2-visitor 'js2-visit-array-node)
4679 (put 'cl-struct-js2-array-node 'js2-printer 'js2-print-array-node)
4680
4681 (defun js2-visit-array-node (n v)
4682 (dolist (e (js2-array-node-elems n))
4683 (when e ; can be nil, e.g. [a, ,b]
4684 (js2-visit-ast e v))))
4685
4686 (defun js2-print-array-node (n i)
4687 (insert (js2-make-pad i) "[")
4688 (js2-print-list (js2-array-node-elems n))
4689 (insert "]"))
4690
4691 (defstruct (js2-object-node
4692 (:include js2-node)
4693 (:constructor nil)
4694 (:constructor make-js2-object-node (&key (type js2-OBJECTLIT)
4695 (pos js2-ts-cursor)
4696 len
4697 elems)))
4698 "AST node for an object literal expression."
4699 elems) ; a lisp list of `js2-object-prop-node'
4700
4701 (put 'cl-struct-js2-object-node 'js2-visitor 'js2-visit-object-node)
4702 (put 'cl-struct-js2-object-node 'js2-printer 'js2-print-object-node)
4703
4704 (defun js2-visit-object-node (n v)
4705 (dolist (e (js2-object-node-elems n))
4706 (js2-visit-ast e v)))
4707
4708 (defun js2-print-object-node (n i)
4709 (insert (js2-make-pad i) "{")
4710 (js2-print-list (js2-object-node-elems n))
4711 (insert "}"))
4712
4713 (defstruct (js2-object-prop-node
4714 (:include js2-infix-node)
4715 (:constructor nil)
4716 (:constructor make-js2-object-prop-node (&key (type js2-COLON)
4717 (pos js2-ts-cursor)
4718 len
4719 left
4720 right
4721 op-pos)))
4722 "AST node for an object literal prop:value entry.
4723 The `left' field is the property: a name node, string node or number node.
4724 The `right' field is a `js2-node' representing the initializer value.")
4725
4726 (put 'cl-struct-js2-object-prop-node 'js2-visitor 'js2-visit-infix-node)
4727 (put 'cl-struct-js2-object-prop-node 'js2-printer 'js2-print-object-prop-node)
4728
4729 (defun js2-print-object-prop-node (n i)
4730 (insert (js2-make-pad i))
4731 (js2-print-ast (js2-object-prop-node-left n) 0)
4732 (insert ":")
4733 (js2-print-ast (js2-object-prop-node-right n) 0))
4734
4735 (defstruct (js2-getter-setter-node
4736 (:include js2-infix-node)
4737 (:constructor nil)
4738 (:constructor make-js2-getter-setter-node (&key type ; GET or SET
4739 (pos js2-ts-cursor)
4740 len
4741 left
4742 right)))
4743 "AST node for a getter/setter property in an object literal.
4744 The `left' field is the `js2-name-node' naming the getter/setter prop.
4745 The `right' field is always an anonymous `js2-function-node' with a node
4746 property `GETTER_SETTER' set to js2-GET or js2-SET. ")
4747
4748 (put 'cl-struct-js2-getter-setter-node 'js2-visitor 'js2-visit-infix-node)
4749 (put 'cl-struct-js2-getter-setter-node 'js2-printer 'js2-print-getter-setter)
4750
4751 (defun js2-print-getter-setter (n i)
4752 (let ((pad (js2-make-pad i))
4753 (left (js2-getter-setter-node-left n))
4754 (right (js2-getter-setter-node-right n)))
4755 (insert pad)
4756 (insert (if (= (js2-node-type n) js2-GET) "get " "set "))
4757 (js2-print-ast left 0)
4758 (js2-print-ast right 0)))
4759
4760 (defstruct (js2-prop-get-node
4761 (:include js2-infix-node)
4762 (:constructor nil)
4763 (:constructor make-js2-prop-get-node (&key (type js2-GETPROP)
4764 (pos js2-ts-cursor)
4765 len
4766 left
4767 right)))
4768 "AST node for a dotted property reference, e.g. foo.bar or foo().bar")
4769
4770 (put 'cl-struct-js2-prop-get-node 'js2-visitor 'js2-visit-prop-get-node)
4771 (put 'cl-struct-js2-prop-get-node 'js2-printer 'js2-print-prop-get-node)
4772
4773 (defun js2-visit-prop-get-node (n v)
4774 (when (js2-prop-get-node-left n)
4775 (js2-visit-ast (js2-prop-get-node-left n) v))
4776 (when (js2-prop-get-node-right n)
4777 (js2-visit-ast (js2-prop-get-node-right n) v)))
4778
4779 (defun js2-print-prop-get-node (n i)
4780 (insert (js2-make-pad i))
4781 (js2-print-ast (js2-prop-get-node-left n) 0)
4782 (insert ".")
4783 (js2-print-ast (js2-prop-get-node-right n) 0))
4784
4785 (defstruct (js2-elem-get-node
4786 (:include js2-node)
4787 (:constructor nil)
4788 (:constructor make-js2-elem-get-node (&key (type js2-GETELEM)
4789 (pos js2-ts-cursor)
4790 len
4791 target
4792 element
4793 lb
4794 rb)))
4795 "AST node for an array index expression such as foo[bar]."
4796 target ; a `js2-node' - the expression preceding the "."
4797 element ; a `js2-node' - the expression in brackets
4798 lb ; position of left-bracket, nil if omitted
4799 rb) ; position of right-bracket, nil if omitted
4800
4801 (put 'cl-struct-js2-elem-get-node 'js2-visitor 'js2-visit-elem-get-node)
4802 (put 'cl-struct-js2-elem-get-node 'js2-printer 'js2-print-elem-get-node)
4803
4804 (defun js2-visit-elem-get-node (n v)
4805 (when (js2-elem-get-node-target n)
4806 (js2-visit-ast (js2-elem-get-node-target n) v))
4807 (when (js2-elem-get-node-element n)
4808 (js2-visit-ast (js2-elem-get-node-element n) v)))
4809
4810 (defun js2-print-elem-get-node (n i)
4811 (insert (js2-make-pad i))
4812 (js2-print-ast (js2-elem-get-node-target n) 0)
4813 (insert "[")
4814 (js2-print-ast (js2-elem-get-node-element n) 0)
4815 (insert "]"))
4816
4817 (defstruct (js2-call-node
4818 (:include js2-node)
4819 (:constructor nil)
4820 (:constructor make-js2-call-node (&key (type js2-CALL)
4821 (pos js2-ts-cursor)
4822 len
4823 target
4824 args
4825 lp
4826 rp)))
4827 "AST node for a JavaScript function call."
4828 target ; a `js2-node' evaluating to the function to call
4829 args ; a lisp list of `js2-node' arguments
4830 lp ; position of open-paren, or nil if missing
4831 rp) ; position of close-paren, or nil if missing
4832
4833 (put 'cl-struct-js2-call-node 'js2-visitor 'js2-visit-call-node)
4834 (put 'cl-struct-js2-call-node 'js2-printer 'js2-print-call-node)
4835
4836 (defun js2-visit-call-node (n v)
4837 (js2-visit-ast (js2-call-node-target n) v)
4838 (dolist (arg (js2-call-node-args n))
4839 (js2-visit-ast arg v)))
4840
4841 (defun js2-print-call-node (n i)
4842 (insert (js2-make-pad i))
4843 (js2-print-ast (js2-call-node-target n) 0)
4844 (insert "(")
4845 (js2-print-list (js2-call-node-args n))
4846 (insert ")"))
4847
4848 (defstruct (js2-yield-node
4849 (:include js2-node)
4850 (:constructor nil)
4851 (:constructor make-js2-yield-node (&key (type js2-YIELD)
4852 (pos js2-ts-cursor)
4853 len
4854 value)))
4855 "AST node for yield statement or expression."
4856 value) ; optional: value to be yielded
4857
4858 (put 'cl-struct-js2-yield-node 'js2-visitor 'js2-visit-yield-node)
4859 (put 'cl-struct-js2-yield-node 'js2-printer 'js2-print-yield-node)
4860
4861 (defun js2-visit-yield-node (n v)
4862 (js2-visit-ast (js2-yield-node-value n) v))
4863
4864 (defun js2-print-yield-node (n i)
4865 (insert (js2-make-pad i))
4866 (insert "yield")
4867 (when (js2-yield-node-value n)
4868 (insert " ")
4869 (js2-print-ast (js2-yield-node-value n) 0)))
4870
4871 (defstruct (js2-paren-node
4872 (:include js2-node)
4873 (:constructor nil)
4874 (:constructor make-js2-paren-node (&key (type js2-LP)
4875 (pos js2-ts-cursor)
4876 len
4877 expr)))
4878 "AST node for a parenthesized expression.
4879 In particular, used when the parens are syntactically optional,
4880 as opposed to required parens such as those enclosing an if-conditional."
4881 expr) ; `js2-node'
4882
4883 (put 'cl-struct-js2-paren-node 'js2-visitor 'js2-visit-paren-node)
4884 (put 'cl-struct-js2-paren-node 'js2-printer 'js2-print-paren-node)
4885
4886 (defun js2-visit-paren-node (n v)
4887 (js2-visit-ast (js2-paren-node-expr n) v))
4888
4889 (defun js2-print-paren-node (n i)
4890 (insert (js2-make-pad i))
4891 (insert "(")
4892 (js2-print-ast (js2-paren-node-expr n) 0)
4893 (insert ")"))
4894
4895 (defstruct (js2-array-comp-node
4896 (:include js2-scope)
4897 (:constructor nil)
4898 (:constructor make-js2-array-comp-node (&key (type js2-ARRAYCOMP)
4899 (pos js2-ts-cursor)
4900 len
4901 result
4902 loops
4903 filter
4904 if-pos
4905 lp
4906 rp)))
4907 "AST node for an Array comprehension such as [[x,y] for (x in foo) for (y in bar)]."
4908 result ; result expression (just after left-bracket)
4909 loops ; a lisp list of `js2-array-comp-loop-node'
4910 filter ; guard/filter expression
4911 if-pos ; buffer pos of 'if' keyword, if present, else nil
4912 lp ; buffer position of if-guard left-paren, or nil if not present
4913 rp) ; buffer position of if-guard right-paren, or nil if not present
4914
4915 (put 'cl-struct-js2-array-comp-node 'js2-visitor 'js2-visit-array-comp-node)
4916 (put 'cl-struct-js2-array-comp-node 'js2-printer 'js2-print-array-comp-node)
4917
4918 (defun js2-visit-array-comp-node (n v)
4919 (js2-visit-ast (js2-array-comp-node-result n) v)
4920 (dolist (l (js2-array-comp-node-loops n))
4921 (js2-visit-ast l v))
4922 (if (js2-array-comp-node-filter n)
4923 (js2-visit-ast (js2-array-comp-node-filter n) v)))
4924
4925 (defun js2-print-array-comp-node (n i)
4926 (let ((pad (js2-make-pad i))
4927 (result (js2-array-comp-node-result n))
4928 (loops (js2-array-comp-node-loops n))
4929 (filter (js2-array-comp-node-filter n)))
4930 (insert pad "[")
4931 (js2-print-ast result 0)
4932 (dolist (l loops)
4933 (insert " ")
4934 (js2-print-ast l 0))
4935 (when filter
4936 (insert " if (")
4937 (js2-print-ast filter 0))
4938 (insert ")]")))
4939
4940 (defstruct (js2-array-comp-loop-node
4941 (:include js2-for-in-node)
4942 (:constructor nil)
4943 (:constructor make-js2-array-comp-loop-node (&key (type js2-FOR)
4944 (pos js2-ts-cursor)
4945 len
4946 iterator
4947 object
4948 in-pos
4949 foreach-p
4950 each-pos
4951 lp
4952 rp)))
4953 "AST subtree for each 'for (foo in bar)' loop in an array comprehension.")
4954
4955 (put 'cl-struct-js2-array-comp-loop-node 'js2-visitor 'js2-visit-array-comp-loop)
4956 (put 'cl-struct-js2-array-comp-loop-node 'js2-printer 'js2-print-array-comp-loop)
4957
4958 (defun js2-visit-array-comp-loop (n v)
4959 (js2-visit-ast (js2-array-comp-loop-node-iterator n) v)
4960 (js2-visit-ast (js2-array-comp-loop-node-object n) v))
4961
4962 (defun js2-print-array-comp-loop (n i)
4963 (insert "for (")
4964 (js2-print-ast (js2-array-comp-loop-node-iterator n) 0)
4965 (insert " in ")
4966 (js2-print-ast (js2-array-comp-loop-node-object n) 0)
4967 (insert ")"))
4968
4969 (defstruct (js2-empty-expr-node
4970 (:include js2-node)
4971 (:constructor nil)
4972 (:constructor make-js2-empty-expr-node (&key (type js2-EMPTY)
4973 (pos js2-token-beg)
4974 len)))
4975 "AST node for an empty expression.")
4976
4977 (put 'cl-struct-js2-empty-expr-node 'js2-visitor 'js2-visit-none)
4978 (put 'cl-struct-js2-empty-expr-node 'js2-printer 'js2-print-none)
4979
4980 (defstruct (js2-xml-node
4981 (:include js2-block-node)
4982 (:constructor nil)
4983 (:constructor make-js2-xml-node (&key (type js2-XML)
4984 (pos js2-token-beg)
4985 len
4986 kids)))
4987 "AST node for initial parse of E4X literals.
4988 The kids field is a list of XML fragments, each a `js2-string-node' or
4989 a `js2-xml-js-expr-node'. Equivalent to Rhino's XmlLiteral node.")
4990
4991 (put 'cl-struct-js2-xml-node 'js2-visitor 'js2-visit-block)
4992 (put 'cl-struct-js2-xml-node 'js2-printer 'js2-print-xml-node)
4993
4994 (defun js2-print-xml-node (n i)
4995 (dolist (kid (js2-xml-node-kids n))
4996 (js2-print-ast kid i)))
4997
4998 (defstruct (js2-xml-js-expr-node
4999 (:include js2-xml-node)
5000 (:constructor nil)
5001 (:constructor make-js2-xml-js-expr-node (&key (type js2-XML)
5002 (pos js2-ts-cursor)
5003 len
5004 expr)))
5005 "AST node for an embedded JavaScript {expression} in an E4X literal.
5006 The start and end fields correspond to the curly-braces."
5007 expr) ; a `js2-expr-node' of some sort
5008
5009 (put 'cl-struct-js2-xml-js-expr-node 'js2-visitor 'js2-visit-xml-js-expr)
5010 (put 'cl-struct-js2-xml-js-expr-node 'js2-printer 'js2-print-xml-js-expr)
5011
5012 (defun js2-visit-xml-js-expr (n v)
5013 (js2-visit-ast (js2-xml-js-expr-node-expr n) v))
5014
5015 (defun js2-print-xml-js-expr (n i)
5016 (insert (js2-make-pad i))
5017 (insert "{")
5018 (js2-print-ast (js2-xml-js-expr-node-expr n) 0)
5019 (insert "}"))
5020
5021 (defstruct (js2-xml-dot-query-node
5022 (:include js2-infix-node)
5023 (:constructor nil)
5024 (:constructor make-js2-xml-dot-query-node (&key (type js2-DOTQUERY)
5025 (pos js2-ts-cursor)
5026 op-pos
5027 len
5028 left
5029 right
5030 rp)))
5031 "AST node for an E4X foo.(bar) filter expression.
5032 Note that the left-paren is automatically the character immediately
5033 following the dot (.) in the operator. No whitespace is permitted
5034 between the dot and the lp by the scanner."
5035 rp)
5036
5037 (put 'cl-struct-js2-xml-dot-query-node 'js2-visitor 'js2-visit-infix-node)
5038 (put 'cl-struct-js2-xml-dot-query-node 'js2-printer 'js2-print-xml-dot-query)
5039
5040 (defun js2-print-xml-dot-query (n i)
5041 (insert (js2-make-pad i))
5042 (js2-print-ast (js2-xml-dot-query-node-left n) 0)
5043 (insert ".(")
5044 (js2-print-ast (js2-xml-dot-query-node-right n) 0)
5045 (insert ")"))
5046
5047 (defstruct (js2-xml-ref-node
5048 (:include js2-node)
5049 (:constructor nil)) ; abstract
5050 "Base type for E4X XML attribute-access or property-get expressions.
5051 Such expressions can take a variety of forms. The general syntax has
5052 three parts:
5053
5054 - (optional) an @ (specifying an attribute access)
5055 - (optional) a namespace (a `js2-name-node') and double-colon
5056 - (required) either a `js2-name-node' or a bracketed [expression]
5057
5058 The property-name expressions (examples: ns::name, @name) are
5059 represented as `js2-xml-prop-ref' nodes. The bracketed-expression
5060 versions (examples: ns::[name], @[name]) become `js2-xml-elem-ref' nodes.
5061
5062 This node type (or more specifically, its subclasses) will sometimes
5063 be the right-hand child of a `js2-prop-get-node' or a
5064 `js2-infix-node' of type `js2-DOTDOT', the .. xml-descendants operator.
5065 The `js2-xml-ref-node' may also be a standalone primary expression with
5066 no explicit target, which is valid in certain expression contexts such as
5067
5068 company..employee.(@id < 100)
5069
5070 in this case, the @id is a `js2-xml-ref' that is part of an infix '<'
5071 expression whose parent is a `js2-xml-dot-query-node'."
5072 namespace
5073 at-pos
5074 colon-pos)
5075
5076 (defsubst js2-xml-ref-node-attr-access-p (node)
5077 "Return non-nil if this expression began with an @-token."
5078 (and (numberp (js2-xml-ref-node-at-pos node))
5079 (plusp (js2-xml-ref-node-at-pos node))))
5080
5081 (defstruct (js2-xml-prop-ref-node
5082 (:include js2-xml-ref-node)
5083 (:constructor nil)
5084 (:constructor make-js2-xml-prop-ref-node (&key (type js2-REF_NAME)
5085 (pos js2-token-beg)
5086 len
5087 propname
5088 namespace
5089 at-pos
5090 colon-pos)))
5091 "AST node for an E4X XML [expr] property-ref expression.
5092 The JavaScript syntax is an optional @, an optional ns::, and a name.
5093
5094 [ '@' ] [ name '::' ] name
5095
5096 Examples include name, ns::name, ns::*, *::name, *::*, @attr, @ns::attr,
5097 @ns::*, @*::attr, @*::*, and @*.
5098
5099 The node starts at the @ token, if present. Otherwise it starts at the
5100 namespace name. The node bounds extend through the closing right-bracket,
5101 or if it is missing due to a syntax error, through the end of the index
5102 expression."
5103 propname)
5104
5105 (put 'cl-struct-js2-xml-prop-ref-node 'js2-visitor 'js2-visit-xml-prop-ref-node)
5106 (put 'cl-struct-js2-xml-prop-ref-node 'js2-printer 'js2-print-xml-prop-ref-node)
5107
5108 (defun js2-visit-xml-prop-ref-node (n v)
5109 (if (js2-xml-prop-ref-node-namespace n)
5110 (js2-visit-ast (js2-xml-prop-ref-node-namespace n) v))
5111 (if (js2-xml-prop-ref-node-propname n)
5112 (js2-visit-ast (js2-xml-prop-ref-node-propname n) v)))
5113
5114 (defun js2-print-xml-prop-ref-node (n i)
5115 (insert (js2-make-pad i))
5116 (if (js2-xml-ref-node-attr-access-p n)
5117 (insert "@"))
5118 (when (js2-xml-prop-ref-node-namespace n)
5119 (js2-print-ast (js2-xml-prop-ref-node-namespace n) 0)
5120 (insert "::"))
5121 (if (js2-xml-prop-ref-node-propname n)
5122 (js2-print-ast (js2-xml-prop-ref-node-propname n) 0)))
5123
5124 (defstruct (js2-xml-elem-ref-node
5125 (:include js2-xml-ref-node)
5126 (:constructor nil)
5127 (:constructor make-js2-xml-elem-ref-node (&key (type js2-REF_MEMBER)
5128 (pos js2-token-beg)
5129 len
5130 expr
5131 lb
5132 rb
5133 namespace
5134 at-pos
5135 colon-pos)))
5136 "AST node for an E4X XML [expr] member-ref expression.
5137 Syntax:
5138
5139 [ '@' ] [ name '::' ] '[' expr ']'
5140
5141 Examples include ns::[expr], @ns::[expr], @[expr], *::[expr] and @*::[expr].
5142
5143 Note that the form [expr] (i.e. no namespace or attribute-qualifier)
5144 is not a legal E4X XML element-ref expression, since it's already used
5145 for standard JavaScript element-get array indexing. Hence, a
5146 `js2-xml-elem-ref-node' always has either the attribute-qualifier, a
5147 non-nil namespace node, or both.
5148
5149 The node starts at the @ token, if present. Otherwise it starts
5150 at the namespace name. The node bounds extend through the closing
5151 right-bracket, or if it is missing due to a syntax error, through the
5152 end of the index expression."
5153 expr ; the bracketed index expression
5154 lb
5155 rb)
5156
5157 (put 'cl-struct-js2-xml-elem-ref-node 'js2-visitor 'js2-visit-xml-elem-ref-node)
5158 (put 'cl-struct-js2-xml-elem-ref-node 'js2-printer 'js2-print-xml-elem-ref-node)
5159
5160 (defun js2-visit-xml-elem-ref-node (n v)
5161 (if (js2-xml-elem-ref-node-namespace n)
5162 (js2-visit-ast (js2-xml-elem-ref-node-namespace n) v))
5163 (if (js2-xml-elem-ref-node-expr n)
5164 (js2-visit-ast (js2-xml-elem-ref-node-expr n) v)))
5165
5166 (defun js2-print-xml-elem-ref-node (n i)
5167 (insert (js2-make-pad i))
5168 (if (js2-xml-ref-node-attr-access-p n)
5169 (insert "@"))
5170 (when (js2-xml-elem-ref-node-namespace n)
5171 (js2-print-ast (js2-xml-elem-ref-node-namespace n) 0)
5172 (insert "::"))
5173 (insert "[")
5174 (if (js2-xml-elem-ref-node-expr n)
5175 (js2-print-ast (js2-xml-elem-ref-node-expr n) 0))
5176 (insert "]"))
5177
5178 ;;; Placeholder nodes for when we try parsing the XML literals structurally.
5179
5180 (defstruct (js2-xml-start-tag-node
5181 (:include js2-xml-node)
5182 (:constructor nil)
5183 (:constructor make-js2-xml-start-tag-node (&key (type js2-XML)
5184 (pos js2-ts-cursor)
5185 len
5186 name
5187 attrs
5188 kids
5189 empty-p)))
5190 "AST node for an XML start-tag. Not currently used.
5191 The `kids' field is a lisp list of child content nodes."
5192 name ; a `js2-xml-name-node'
5193 attrs ; a lisp list of `js2-xml-attr-node'
5194 empty-p) ; t if this is an empty element such as <foo bar="baz"/>
5195
5196 (put 'cl-struct-js2-xml-start-tag-node 'js2-visitor 'js2-visit-xml-start-tag)
5197 (put 'cl-struct-js2-xml-start-tag-node 'js2-printer 'js2-print-xml-start-tag)
5198
5199 (defun js2-visit-xml-start-tag (n v)
5200 (js2-visit-ast (js2-xml-start-tag-node-name n) v)
5201 (dolist (attr (js2-xml-start-tag-node-attrs n))
5202 (js2-visit-ast attr v))
5203 (js2-visit-block n v))
5204
5205 (defun js2-print-xml-start-tag (n i)
5206 (insert (js2-make-pad i) "<")
5207 (js2-print-ast (js2-xml-start-tag-node-name n) 0)
5208 (when (js2-xml-start-tag-node-attrs n)
5209 (insert " ")
5210 (js2-print-list (js2-xml-start-tag-node-attrs n) " "))
5211 (insert ">"))
5212
5213 ;; I -think- I'm going to make the parent node the corresponding start-tag,
5214 ;; and add the end-tag to the kids list of the parent as well.
5215 (defstruct (js2-xml-end-tag-node
5216 (:include js2-xml-node)
5217 (:constructor nil)
5218 (:constructor make-js2-xml-end-tag-node (&key (type js2-XML)
5219 (pos js2-ts-cursor)
5220 len
5221 name)))
5222 "AST node for an XML end-tag. Not currently used."
5223 name) ; a `js2-xml-name-node'
5224
5225 (put 'cl-struct-js2-xml-end-tag-node 'js2-visitor 'js2-visit-xml-end-tag)
5226 (put 'cl-struct-js2-xml-end-tag-node 'js2-printer 'js2-print-xml-end-tag)
5227
5228 (defun js2-visit-xml-end-tag (n v)
5229 (js2-visit-ast (js2-xml-end-tag-node-name n) v))
5230
5231 (defun js2-print-xml-end-tag (n i)
5232 (insert (js2-make-pad i))
5233 (insert "</")
5234 (js2-print-ast (js2-xml-end-tag-node-name n) 0)
5235 (insert ">"))
5236
5237 (defstruct (js2-xml-name-node
5238 (:include js2-xml-node)
5239 (:constructor nil)
5240 (:constructor make-js2-xml-name-node (&key (type js2-XML)
5241 (pos js2-ts-cursor)
5242 len
5243 namespace
5244 kids)))
5245 "AST node for an E4X XML name. Not currently used.
5246 Any XML name can be qualified with a namespace, hence the namespace field.
5247 Further, any E4X name can be comprised of arbitrary JavaScript {} expressions.
5248 The kids field is a list of `js2-name-node' and `js2-xml-js-expr-node'.
5249 For a simple name, the kids list has exactly one node, a `js2-name-node'."
5250 namespace) ; a `js2-string-node'
5251
5252 (put 'cl-struct-js2-xml-name-node 'js2-visitor 'js2-visit-xml-name-node)
5253 (put 'cl-struct-js2-xml-name-node 'js2-printer 'js2-print-xml-name-node)
5254
5255 (defun js2-visit-xml-name-node (n v)
5256 (js2-visit-ast (js2-xml-name-node-namespace n) v))
5257
5258 (defun js2-print-xml-name-node (n i)
5259 (insert (js2-make-pad i))
5260 (when (js2-xml-name-node-namespace n)
5261 (js2-print-ast (js2-xml-name-node-namespace n) 0)
5262 (insert "::"))
5263 (dolist (kid (js2-xml-name-node-kids n))
5264 (js2-print-ast kid 0)))
5265
5266 (defstruct (js2-xml-pi-node
5267 (:include js2-xml-node)
5268 (:constructor nil)
5269 (:constructor make-js2-xml-pi-node (&key (type js2-XML)
5270 (pos js2-ts-cursor)
5271 len
5272 name
5273 attrs)))
5274 "AST node for an E4X XML processing instruction. Not currently used."
5275 name ; a `js2-xml-name-node'
5276 attrs) ; a list of `js2-xml-attr-node'
5277
5278 (put 'cl-struct-js2-xml-pi-node 'js2-visitor 'js2-visit-xml-pi-node)
5279 (put 'cl-struct-js2-xml-pi-node 'js2-printer 'js2-print-xml-pi-node)
5280
5281 (defun js2-visit-xml-pi-node (n v)
5282 (js2-visit-ast (js2-xml-pi-node-name n) v)
5283 (dolist (attr (js2-xml-pi-node-attrs n))
5284 (js2-visit-ast attr v)))
5285
5286 (defun js2-print-xml-pi-node (n i)
5287 (insert (js2-make-pad i) "<?")
5288 (js2-print-ast (js2-xml-pi-node-name n))
5289 (when (js2-xml-pi-node-attrs n)
5290 (insert " ")
5291 (js2-print-list (js2-xml-pi-node-attrs n)))
5292 (insert "?>"))
5293
5294 (defstruct (js2-xml-cdata-node
5295 (:include js2-xml-node)
5296 (:constructor nil)
5297 (:constructor make-js2-xml-cdata-node (&key (type js2-XML)
5298 (pos js2-ts-cursor)
5299 len
5300 content)))
5301 "AST node for a CDATA escape section. Not currently used."
5302 content) ; a `js2-string-node' with node-property 'quote-type 'cdata
5303
5304 (put 'cl-struct-js2-xml-cdata-node 'js2-visitor 'js2-visit-xml-cdata-node)
5305 (put 'cl-struct-js2-xml-cdata-node 'js2-printer 'js2-print-xml-cdata-node)
5306
5307 (defun js2-visit-xml-cdata-node (n v)
5308 (js2-visit-ast (js2-xml-cdata-node-content n) v))
5309
5310 (defun js2-print-xml-cdata-node (n i)
5311 (insert (js2-make-pad i))
5312 (js2-print-ast (js2-xml-cdata-node-content n)))
5313
5314 (defstruct (js2-xml-attr-node
5315 (:include js2-xml-node)
5316 (:constructor nil)
5317 (:constructor make-js2-attr-node (&key (type js2-XML)
5318 (pos js2-ts-cursor)
5319 len
5320 name
5321 value
5322 eq-pos
5323 quote-type)))
5324 "AST node representing a foo='bar' XML attribute value. Not yet used."
5325 name ; a `js2-xml-name-node'
5326 value ; a `js2-xml-name-node'
5327 eq-pos ; buffer position of "=" sign
5328 quote-type) ; 'single or 'double
5329
5330 (put 'cl-struct-js2-xml-attr-node 'js2-visitor 'js2-visit-xml-attr-node)
5331 (put 'cl-struct-js2-xml-attr-node 'js2-printer 'js2-print-xml-attr-node)
5332
5333 (defun js2-visit-xml-attr-node (n v)
5334 (js2-visit-ast (js2-xml-attr-node-name n) v)
5335 (js2-visit-ast (js2-xml-attr-node-value n) v))
5336
5337 (defun js2-print-xml-attr-node (n i)
5338 (let ((quote (if (eq (js2-xml-attr-node-quote-type n) 'single)
5339 "'"
5340 "\"")))
5341 (insert (js2-make-pad i))
5342 (js2-print-ast (js2-xml-attr-node-name n) 0)
5343 (insert "=" quote)
5344 (js2-print-ast (js2-xml-attr-node-value n) 0)
5345 (insert quote)))
5346
5347 (defstruct (js2-xml-text-node
5348 (:include js2-xml-node)
5349 (:constructor nil)
5350 (:constructor make-js2-text-node (&key (type js2-XML)
5351 (pos js2-ts-cursor)
5352 len
5353 content)))
5354 "AST node for an E4X XML text node. Not currently used."
5355 content) ; a lisp list of `js2-string-node' and `js2-xml-js-expr-node'
5356
5357 (put 'cl-struct-js2-xml-text-node 'js2-visitor 'js2-visit-xml-text-node)
5358 (put 'cl-struct-js2-xml-text-node 'js2-printer 'js2-print-xml-text-node)
5359
5360 (defun js2-visit-xml-text-node (n v)
5361 (js2-visit-ast (js2-xml-text-node-content n) v))
5362
5363 (defun js2-print-xml-text-node (n i)
5364 (insert (js2-make-pad i))
5365 (dolist (kid (js2-xml-text-node-content n))
5366 (js2-print-ast kid)))
5367
5368 (defstruct (js2-xml-comment-node
5369 (:include js2-xml-node)
5370 (:constructor nil)
5371 (:constructor make-js2-xml-comment-node (&key (type js2-XML)
5372 (pos js2-ts-cursor)
5373 len)))
5374 "AST node for E4X XML comment. Not currently used.")
5375
5376 (put 'cl-struct-js2-xml-comment-node 'js2-visitor 'js2-visit-none)
5377 (put 'cl-struct-js2-xml-comment-node 'js2-printer 'js2-print-xml-comment)
5378
5379 (defun js2-print-xml-comment (n i)
5380 (insert (js2-make-pad i)
5381 (js2-node-string n)))
5382
5383 ;;; Node utilities
5384
5385 (defsubst js2-node-line (n)
5386 "Fetch the source line number at the start of node N.
5387 This is O(n) in the length of the source buffer; use prudently."
5388 (1+ (count-lines (point-min) (js2-node-abs-pos n))))
5389
5390 (defsubst js2-block-node-kid (n i)
5391 "Return child I of node N, or nil if there aren't that many."
5392 (nth i (js2-block-node-kids n)))
5393
5394 (defsubst js2-block-node-first (n)
5395 "Return first child of block node N, or nil if there is none."
5396 (first (js2-block-node-kids n)))
5397
5398 (defun js2-node-root (n)
5399 "Return the root of the AST containing N.
5400 If N has no parent pointer, returns N."
5401 (let ((parent (js2-node-parent n)))
5402 (if parent
5403 (js2-node-root parent)
5404 n)))
5405
5406 (defun js2-node-position-in-parent (node &optional parent)
5407 "Return the position of NODE in parent's block-kids list.
5408 PARENT can be supplied if known. Positioned returned is zero-indexed.
5409 Returns 0 if NODE is not a child of a block statement, or if NODE
5410 is not a statement node."
5411 (let ((p (or parent (js2-node-parent node)))
5412 (i 0))
5413 (if (not (js2-block-node-p p))
5414 i
5415 (or (js2-position node (js2-block-node-kids p))
5416 0))))
5417
5418 (defsubst js2-node-short-name (n)
5419 "Return the short name of node N as a string, e.g. `js2-if-node'."
5420 (substring (symbol-name (aref n 0))
5421 (length "cl-struct-")))
5422
5423 (defsubst js2-node-child-list (node)
5424 "Return the child list for NODE, a lisp list of nodes.
5425 Works for block nodes, array nodes, obj literals, funarg lists,
5426 var decls and try nodes (for catch clauses). Note that you should call
5427 `js2-block-node-kids' on the function body for the body statements.
5428 Returns nil for zero-length child lists or unsupported nodes."
5429 (cond
5430 ((js2-function-node-p node)
5431 (js2-function-node-params node))
5432 ((js2-block-node-p node)
5433 (js2-block-node-kids node))
5434 ((js2-try-node-p node)
5435 (js2-try-node-catch-clauses node))
5436 ((js2-array-node-p node)
5437 (js2-array-node-elems node))
5438 ((js2-object-node-p node)
5439 (js2-object-node-elems node))
5440 ((js2-call-node-p node)
5441 (js2-call-node-args node))
5442 ((js2-new-node-p node)
5443 (js2-new-node-args node))
5444 ((js2-var-decl-node-p node)
5445 (js2-var-decl-node-kids node))
5446 (t
5447 nil)))
5448
5449 (defsubst js2-node-set-child-list (node kids)
5450 "Set the child list for NODE to KIDS."
5451 (cond
5452 ((js2-function-node-p node)
5453 (setf (js2-function-node-params node) kids))
5454 ((js2-block-node-p node)
5455 (setf (js2-block-node-kids node) kids))
5456 ((js2-try-node-p node)
5457 (setf (js2-try-node-catch-clauses node) kids))
5458 ((js2-array-node-p node)
5459 (setf (js2-array-node-elems node) kids))
5460 ((js2-object-node-p node)
5461 (setf (js2-object-node-elems node) kids))
5462 ((js2-call-node-p node)
5463 (setf (js2-call-node-args node) kids))
5464 ((js2-new-node-p node)
5465 (setf (js2-new-node-args node) kids))
5466 ((js2-var-decl-node-p node)
5467 (setf (js2-var-decl-node-kids node) kids))
5468 (t
5469 (error "Unsupported node type: %s" (js2-node-short-name node))))
5470 kids)
5471
5472 ;; All because Common Lisp doesn't support multiple inheritance for defstructs.
5473 (defconst js2-paren-expr-nodes
5474 '(cl-struct-js2-array-comp-loop-node
5475 cl-struct-js2-array-comp-node
5476 cl-struct-js2-call-node
5477 cl-struct-js2-catch-node
5478 cl-struct-js2-do-node
5479 cl-struct-js2-elem-get-node
5480 cl-struct-js2-for-in-node
5481 cl-struct-js2-for-node
5482 cl-struct-js2-function-node
5483 cl-struct-js2-if-node
5484 cl-struct-js2-let-node
5485 cl-struct-js2-new-node
5486 cl-struct-js2-paren-node
5487 cl-struct-js2-switch-node
5488 cl-struct-js2-while-node
5489 cl-struct-js2-with-node
5490 cl-struct-js2-xml-dot-query-node)
5491 "Node types that can have a parenthesized child expression.
5492 In particular, nodes that respond to `js2-node-lp' and `js2-node-rp'.")
5493
5494 (defsubst js2-paren-expr-node-p (node)
5495 "Return t for nodes that typically have a parenthesized child expression.
5496 Useful for computing the indentation anchors for arg-lists and conditions.
5497 Note that it may return a false positive, for instance when NODE is
5498 a `js2-new-node' and there are no arguments or parentheses."
5499 (memq (aref node 0) js2-paren-expr-nodes))
5500
5501 ;; Fake polymorphism... yech.
5502 (defsubst js2-node-lp (node)
5503 "Return relative left-paren position for NODE, if applicable.
5504 For `js2-elem-get-node' structs, returns left-bracket position.
5505 Note that the position may be nil in the case of a parse error."
5506 (cond
5507 ((js2-elem-get-node-p node)
5508 (js2-elem-get-node-lb node))
5509 ((js2-loop-node-p node)
5510 (js2-loop-node-lp node))
5511 ((js2-function-node-p node)
5512 (js2-function-node-lp node))
5513 ((js2-if-node-p node)
5514 (js2-if-node-lp node))
5515 ((js2-new-node-p node)
5516 (js2-new-node-lp node))
5517 ((js2-call-node-p node)
5518 (js2-call-node-lp node))
5519 ((js2-paren-node-p node)
5520 (js2-node-pos node))
5521 ((js2-switch-node-p node)
5522 (js2-switch-node-lp node))
5523 ((js2-catch-node-p node)
5524 (js2-catch-node-lp node))
5525 ((js2-let-node-p node)
5526 (js2-let-node-lp node))
5527 ((js2-array-comp-node-p node)
5528 (js2-array-comp-node-lp node))
5529 ((js2-with-node-p node)
5530 (js2-with-node-lp node))
5531 ((js2-xml-dot-query-node-p node)
5532 (1+ (js2-infix-node-op-pos node)))
5533 (t
5534 (error "Unsupported node type: %s" (js2-node-short-name node)))))
5535
5536 ;; Fake polymorphism... blech.
5537 (defsubst js2-node-rp (node)
5538 "Return relative right-paren position for NODE, if applicable.
5539 For `js2-elem-get-node' structs, returns right-bracket position.
5540 Note that the position may be nil in the case of a parse error."
5541 (cond
5542 ((js2-elem-get-node-p node)
5543 (js2-elem-get-node-lb node))
5544 ((js2-loop-node-p node)
5545 (js2-loop-node-rp node))
5546 ((js2-function-node-p node)
5547 (js2-function-node-rp node))
5548 ((js2-if-node-p node)
5549 (js2-if-node-rp node))
5550 ((js2-new-node-p node)
5551 (js2-new-node-rp node))
5552 ((js2-call-node-p node)
5553 (js2-call-node-rp node))
5554 ((js2-paren-node-p node)
5555 (+ (js2-node-pos node) (js2-node-len node)))
5556 ((js2-switch-node-p node)
5557 (js2-switch-node-rp node))
5558 ((js2-catch-node-p node)
5559 (js2-catch-node-rp node))
5560 ((js2-let-node-p node)
5561 (js2-let-node-rp node))
5562 ((js2-array-comp-node-p node)
5563 (js2-array-comp-node-rp node))
5564 ((js2-with-node-p node)
5565 (js2-with-node-rp node))
5566 ((js2-xml-dot-query-node-p node)
5567 (1+ (js2-xml-dot-query-node-rp node)))
5568 (t
5569 (error "Unsupported node type: %s" (js2-node-short-name node)))))
5570
5571 (defsubst js2-node-first-child (node)
5572 "Returns the first element of `js2-node-child-list' for NODE."
5573 (car (js2-node-child-list node)))
5574
5575 (defsubst js2-node-last-child (node)
5576 "Returns the last element of `js2-node-last-child' for NODE."
5577 (car (last (js2-node-child-list node))))
5578
5579 (defun js2-node-prev-sibling (node)
5580 "Return the previous statement in parent.
5581 Works for parents supported by `js2-node-child-list'.
5582 Returns nil if NODE is not in the parent, or PARENT is
5583 not a supported node, or if NODE is the first child."
5584 (let* ((p (js2-node-parent node))
5585 (kids (js2-node-child-list p))
5586 (sib (car kids)))
5587 (while (and kids
5588 (neq node (cadr kids)))
5589 (setq kids (cdr kids)
5590 sib (car kids)))
5591 sib))
5592
5593 (defun js2-node-next-sibling (node)
5594 "Return the next statement in parent block.
5595 Returns nil if NODE is not in the block, or PARENT is not
5596 a block node, or if NODE is the last statement."
5597 (let* ((p (js2-node-parent node))
5598 (kids (js2-node-child-list p)))
5599 (while (and kids
5600 (neq node (car kids)))
5601 (setq kids (cdr kids)))
5602 (cadr kids)))
5603
5604 (defun js2-node-find-child-before (pos parent &optional after)
5605 "Find the last child that starts before POS in parent.
5606 If AFTER is non-nil, returns first child starting after POS.
5607 POS is an absolute buffer position. PARENT is any node
5608 supported by `js2-node-child-list'.
5609 Returns nil if no applicable child is found."
5610 (let ((kids (if (js2-function-node-p parent)
5611 (js2-block-node-kids (js2-function-node-body parent))
5612 (js2-node-child-list parent)))
5613 (beg (if (js2-function-node-p parent)
5614 (js2-node-abs-pos (js2-function-node-body parent))
5615 (js2-node-abs-pos parent)))
5616 kid
5617 result
5618 fn
5619 (continue t))
5620 (setq fn (if after '> '<))
5621 (while (and kids continue)
5622 (setq kid (car kids))
5623 (if (funcall fn (+ beg (js2-node-pos kid)) pos)
5624 (setq result kid
5625 continue (if after nil t))
5626 (setq continue (if after t nil)))
5627 (setq kids (cdr kids)))
5628 result))
5629
5630 (defun js2-node-find-child-after (pos parent)
5631 "Find first child that starts after POS in parent.
5632 POS is an absolute buffer position. PARENT is any node
5633 supported by `js2-node-child-list'.
5634 Returns nil if no applicable child is found."
5635 (js2-node-find-child-before pos parent 'after))
5636
5637 (defun js2-node-replace-child (pos parent new-node)
5638 "Replace node at index POS in PARENT with NEW-NODE.
5639 Only works for parents supported by `js2-node-child-list'."
5640 (let ((kids (js2-node-child-list parent))
5641 (i 0))
5642 (while (< i pos)
5643 (setq kids (cdr kids)
5644 i (1+ i)))
5645 (setcar kids new-node)
5646 (js2-node-add-children parent new-node)))
5647
5648 (defun js2-node-buffer (n)
5649 "Return the buffer associated with AST N.
5650 Returns nil if the buffer is not set as a property on the root
5651 node, or if parent links were not recorded during parsing."
5652 (let ((root (js2-node-root n)))
5653 (and root
5654 (js2-ast-root-p root)
5655 (js2-ast-root-buffer root))))
5656
5657 (defsubst js2-block-node-push (n kid)
5658 "Push js2-node KID onto the end of js2-block-node N's child list.
5659 KID is always added to the -end- of the kids list.
5660 Function also calls `js2-node-add-children' to add the parent link."
5661 (let ((kids (js2-node-child-list n)))
5662 (if kids
5663 (setcdr kids (nconc (cdr kids) (list kid)))
5664 (js2-node-set-child-list n (list kid)))
5665 (js2-node-add-children n kid)))
5666
5667 (defun js2-node-string (node)
5668 (let ((buf (js2-node-buffer node))
5669 pos)
5670 (unless buf
5671 (error "No buffer available for node %s" node))
5672 (save-excursion
5673 (set-buffer buf)
5674 (buffer-substring-no-properties (setq pos (js2-node-abs-pos node))
5675 (+ pos (js2-node-len node))))))
5676
5677 ;; Container for storing the node we're looking for in a traversal.
5678 (defvar js2-discovered-node nil)
5679 (make-variable-buffer-local 'js2-discovered-node)
5680
5681 ;; Keep track of absolute node position during traversals.
5682 (defvar js2-visitor-offset nil)
5683 (make-variable-buffer-local 'js2-visitor-offset)
5684
5685 (defvar js2-node-search-point nil)
5686 (make-variable-buffer-local 'js2-node-search-point)
5687
5688 (when js2-mode-dev-mode-p
5689 (defun js2-find-node-at-point ()
5690 (interactive)
5691 (let ((node (js2-node-at-point)))
5692 (message "%s" (or node "No node found at point"))))
5693 (defun js2-node-name-at-point ()
5694 (interactive)
5695 (let ((node (js2-node-at-point)))
5696 (message "%s" (if node
5697 (js2-node-short-name node)
5698 "No node found at point.")))))
5699
5700 (defun js2-node-at-point (&optional pos skip-comments)
5701 "Return AST node at POS, a buffer position, defaulting to current point.
5702 The `js2-mode-ast' variable must be set to the current parse tree.
5703 Signals an error if the AST (`js2-mode-ast') is nil.
5704 Always returns a node - if it can't find one, it returns the root.
5705 If SKIP-COMMENTS is non-nil, comment nodes are ignored."
5706 (let ((ast js2-mode-ast)
5707 result)
5708 (unless ast
5709 (error "No JavaScript AST available"))
5710 ;; Look through comments first, since they may be inside nodes that
5711 ;; would otherwise report a match.
5712 (setq pos (or pos (point))
5713 result (if (> pos (js2-node-abs-end ast))
5714 ast
5715 (if (not skip-comments)
5716 (js2-comment-at-point pos))))
5717 (unless result
5718 (setq js2-discovered-node nil
5719 js2-visitor-offset 0
5720 js2-node-search-point pos)
5721 (unwind-protect
5722 (catch 'js2-visit-done
5723 (js2-visit-ast ast #'js2-node-at-point-visitor))
5724 (setq js2-visitor-offset nil
5725 js2-node-search-point nil))
5726 (setq result js2-discovered-node))
5727 ;; may have found a comment beyond end of last child node,
5728 ;; since visiting the ast-root looks at the comment-list last.
5729 (if (and skip-comments
5730 (js2-comment-node-p result))
5731 (setq result nil))
5732 (or result js2-mode-ast)))
5733
5734 (defun js2-node-at-point-visitor (node end-p)
5735 (let ((rel-pos (js2-node-pos node))
5736 abs-pos
5737 abs-end
5738 (point js2-node-search-point))
5739 (cond
5740 (end-p
5741 ;; this evaluates to a non-nil return value, even if it's zero
5742 (decf js2-visitor-offset rel-pos))
5743 ;; we already looked for comments before visiting, and don't want them now
5744 ((js2-comment-node-p node)
5745 nil)
5746 (t
5747 (setq abs-pos (incf js2-visitor-offset rel-pos)
5748 ;; we only want to use the node if the point is before
5749 ;; the last character position in the node, so we decrement
5750 ;; the absolute end by 1.
5751 abs-end (+ abs-pos (js2-node-len node) -1))
5752 (cond
5753 ;; If this node starts after search-point, stop the search.
5754 ((> abs-pos point)
5755 (throw 'js2-visit-done nil))
5756 ;; If this node ends before the search-point, don't check kids.
5757 ((> point abs-end)
5758 nil)
5759 (t
5760 ;; Otherwise point is within this node, possibly in a child.
5761 (setq js2-discovered-node node)
5762 t)))))) ; keep processing kids to look for more specific match
5763
5764 (defsubst js2-block-comment-p (node)
5765 "Return non-nil if NODE is a comment node of format `jsdoc' or `block'."
5766 (and (js2-comment-node-p node)
5767 (memq (js2-comment-node-format node) '(jsdoc block))))
5768
5769 ;; TODO: put the comments in a vector and binary-search them instead
5770 (defun js2-comment-at-point (&optional pos)
5771 "Look through scanned comment nodes for one containing POS.
5772 POS is a buffer position that defaults to current point.
5773 Function returns nil if POS was not in any comment node."
5774 (let ((ast js2-mode-ast)
5775 (x (or pos (point)))
5776 beg
5777 end)
5778 (unless ast
5779 (error "No JavaScript AST available"))
5780 (catch 'done
5781 ;; Comments are stored in lexical order.
5782 (dolist (comment (js2-ast-root-comments ast) nil)
5783 (setq beg (js2-node-abs-pos comment)
5784 end (+ beg (js2-node-len comment)))
5785 (if (and (>= x beg)
5786 (<= x end))
5787 (throw 'done comment))))))
5788
5789 (defun js2-mode-find-parent-fn (node)
5790 "Find function enclosing NODE.
5791 Returns nil if NODE is not inside a function."
5792 (setq node (js2-node-parent node))
5793 (while (and node (not (js2-function-node-p node)))
5794 (setq node (js2-node-parent node)))
5795 (and (js2-function-node-p node) node))
5796
5797 (defun js2-mode-find-enclosing-fn (node)
5798 "Find function or root enclosing NODE."
5799 (if (js2-ast-root-p node)
5800 node
5801 (setq node (js2-node-parent node))
5802 (while (not (or (js2-ast-root-p node)
5803 (js2-function-node-p node)))
5804 (setq node (js2-node-parent node)))
5805 node))
5806
5807 (defun js2-mode-find-enclosing-node (beg end)
5808 "Find script or function fully enclosing BEG and END."
5809 (let ((node (js2-node-at-point beg))
5810 pos
5811 (continue t))
5812 (while continue
5813 (if (or (js2-ast-root-p node)
5814 (and (js2-function-node-p node)
5815 (<= (setq pos (js2-node-abs-pos node)) beg)
5816 (>= (+ pos (js2-node-len node)) end)))
5817 (setq continue nil)
5818 (setq node (js2-node-parent node))))
5819 node))
5820
5821 (defun js2-node-parent-script-or-fn (node)
5822 "Find script or function immediately enclosing NODE.
5823 If NODE is the ast-root, returns nil."
5824 (if (js2-ast-root-p node)
5825 nil
5826 (setq node (js2-node-parent node))
5827 (while (and node (not (or (js2-function-node-p node)
5828 (js2-script-node-p node))))
5829 (setq node (js2-node-parent node)))
5830 node))
5831
5832 (defsubst js2-nested-function-p (node)
5833 "Return t if NODE is a nested function, or is inside a nested function."
5834 (js2-function-node-p (if (js2-function-node-p node)
5835 (js2-node-parent-script-or-fn node)
5836 (js2-node-parent-script-or-fn
5837 (js2-node-parent-script-or-fn node)))))
5838
5839 (defsubst js2-mode-shift-kids (kids start offset)
5840 (dolist (kid kids)
5841 (if (> (js2-node-pos kid) start)
5842 (incf (js2-node-pos kid) offset))))
5843
5844 (defsubst js2-mode-shift-children (parent start offset)
5845 "Update start-positions of all children of PARENT beyond START."
5846 (let ((root (js2-node-root parent)))
5847 (js2-mode-shift-kids (js2-node-child-list parent) start offset)
5848 (js2-mode-shift-kids (js2-ast-root-comments root) start offset)))
5849
5850 (defsubst js2-node-is-descendant (node ancestor)
5851 "Return t if NODE is a descendant of ANCESTOR."
5852 (while (and node
5853 (neq node ancestor))
5854 (setq node (js2-node-parent node)))
5855 node)
5856
5857 ;;; visitor infrastructure
5858
5859 (defun js2-visit-none (node callback)
5860 "Visitor for AST node that have no node children."
5861 nil)
5862
5863 (defun js2-print-none (node indent)
5864 "Visitor for AST node with no printed representation.")
5865
5866 (defun js2-print-body (node indent)
5867 "Print a statement, or a block without braces."
5868 (if (js2-block-node-p node)
5869 (dolist (kid (js2-block-node-kids node))
5870 (js2-print-ast kid indent))
5871 (js2-print-ast node indent)))
5872
5873 (defun js2-print-list (args &optional delimiter)
5874 (loop with len = (length args)
5875 for arg in args
5876 for count from 1
5877 do
5878 (js2-print-ast arg 0)
5879 (if (< count len)
5880 (insert (or delimiter ", ")))))
5881
5882 (defun js2-print-tree (ast)
5883 "Prints an AST to the current buffer.
5884 Makes `js2-ast-parent-nodes' available to the printer functions."
5885 (let ((max-lisp-eval-depth (max max-lisp-eval-depth 1500)))
5886 (js2-print-ast ast)))
5887
5888 (defun js2-print-ast (node &optional indent)
5889 "Helper function for printing AST nodes.
5890 Requires `js2-ast-parent-nodes' to be non-nil.
5891 You should use `js2-print-tree' instead of this function."
5892 (let ((printer (get (aref node 0) 'js2-printer))
5893 (i (or indent 0))
5894 (pos (js2-node-abs-pos node)))
5895 ;; TODO: wedge comments in here somewhere
5896 (if printer
5897 (funcall printer node i))))
5898
5899 (defconst js2-side-effecting-tokens
5900 (let ((tokens (make-bool-vector js2-num-tokens nil)))
5901 (dolist (tt (list js2-ASSIGN
5902 js2-ASSIGN_ADD
5903 js2-ASSIGN_BITAND
5904 js2-ASSIGN_BITOR
5905 js2-ASSIGN_BITXOR
5906 js2-ASSIGN_DIV
5907 js2-ASSIGN_LSH
5908 js2-ASSIGN_MOD
5909 js2-ASSIGN_MUL
5910 js2-ASSIGN_RSH
5911 js2-ASSIGN_SUB
5912 js2-ASSIGN_URSH
5913 js2-BLOCK
5914 js2-BREAK
5915 js2-CALL
5916 js2-CATCH
5917 js2-CATCH_SCOPE
5918 js2-CONST
5919 js2-CONTINUE
5920 js2-DEBUGGER
5921 js2-DEC
5922 js2-DELPROP
5923 js2-DEL_REF
5924 js2-DO
5925 js2-ELSE
5926 js2-EMPTY
5927 js2-ENTERWITH
5928 js2-EXPORT
5929 js2-EXPR_RESULT
5930 js2-FINALLY
5931 js2-FOR
5932 js2-FUNCTION
5933 js2-GOTO
5934 js2-IF
5935 js2-IFEQ
5936 js2-IFNE
5937 js2-IMPORT
5938 js2-INC
5939 js2-JSR
5940 js2-LABEL
5941 js2-LEAVEWITH
5942 js2-LET
5943 js2-LETEXPR
5944 js2-LOCAL_BLOCK
5945 js2-LOOP
5946 js2-NEW
5947 js2-REF_CALL
5948 js2-RETHROW
5949 js2-RETURN
5950 js2-RETURN_RESULT
5951 js2-SEMI
5952 js2-SETELEM
5953 js2-SETELEM_OP
5954 js2-SETNAME
5955 js2-SETPROP
5956 js2-SETPROP_OP
5957 js2-SETVAR
5958 js2-SET_REF
5959 js2-SET_REF_OP
5960 js2-SWITCH
5961 js2-TARGET
5962 js2-THROW
5963 js2-TRY
5964 js2-VAR
5965 js2-WHILE
5966 js2-WITH
5967 js2-WITHEXPR
5968 js2-YIELD))
5969 (aset tokens tt t))
5970 (if js2-instanceof-has-side-effects
5971 (aset tokens js2-INSTANCEOF t))
5972 tokens))
5973
5974 (defun js2-node-has-side-effects (node)
5975 "Return t if NODE has side effects."
5976 (when node ; makes it easier to handle malformed expressions
5977 (let ((tt (js2-node-type node)))
5978 (cond
5979 ;; This doubtless needs some work, since EXPR_VOID is used
5980 ;; in several ways in Rhino, and I may not have caught them all.
5981 ;; I'll wait for people to notice incorrect warnings.
5982 ((and (= tt js2-EXPR_VOID)
5983 (js2-expr-stmt-node-p node)) ; but not if EXPR_RESULT
5984 (js2-node-has-side-effects (js2-expr-stmt-node-expr node)))
5985
5986 ((= tt js2-COMMA)
5987 (js2-node-has-side-effects (js2-infix-node-right node)))
5988
5989 ((or (= tt js2-AND)
5990 (= tt js2-OR))
5991 (or (js2-node-has-side-effects (js2-infix-node-right node))
5992 (js2-node-has-side-effects (js2-infix-node-left node))))
5993
5994 ((= tt js2-HOOK)
5995 (and (js2-node-has-side-effects (js2-cond-node-true-expr node))
5996 (js2-node-has-side-effects (js2-cond-node-false-expr node))))
5997
5998 ((js2-paren-node-p node)
5999 (js2-node-has-side-effects (js2-paren-node-expr node)))
6000
6001 ((= tt js2-ERROR) ; avoid cascaded error messages
6002 nil)
6003 (t
6004 (aref js2-side-effecting-tokens tt))))))
6005
6006 (defun js2-member-expr-leftmost-name (node)
6007 "For an expr such as foo.bar.baz, return leftmost node foo.
6008 NODE is any `js2-node' object. If it represents a member expression,
6009 which is any sequence of property gets, element-gets, function calls,
6010 or xml descendants/filter operators, then we look at the lexically
6011 leftmost (first) node in the chain. If it is a name-node we return it.
6012 Note that NODE can be a raw name-node and it will be returned as well.
6013 If NODE is not a name-node or member expression, or if it is a member
6014 expression whose leftmost target is not a name node, returns nil."
6015 (let ((continue t)
6016 result)
6017 (while (and continue (not result))
6018 (cond
6019 ((js2-name-node-p node)
6020 (setq result node))
6021 ((js2-prop-get-node-p node)
6022 (setq node (js2-prop-get-node-left node)))
6023 ;; TODO: handle call-nodes, xml-nodes, others?
6024 (t
6025 (setq continue nil))))
6026 result))
6027
6028 (defconst js2-stmt-node-types
6029 (list js2-BLOCK
6030 js2-BREAK
6031 js2-CONTINUE
6032 js2-DEFAULT ; e4x "default xml namespace" statement
6033 js2-DO
6034 js2-EXPR_RESULT
6035 js2-EXPR_VOID
6036 js2-FOR
6037 js2-IF
6038 js2-RETURN
6039 js2-SWITCH
6040 js2-THROW
6041 js2-TRY
6042 js2-WHILE
6043 js2-WITH)
6044 "Node types that only appear in statement contexts.
6045 The list does not include nodes that always appear as the child
6046 of another specific statement type, such as switch-cases,
6047 catch and finally blocks, and else-clauses. The list also excludes
6048 nodes like yield, let and var, which may appear in either expression
6049 or statement context, and in the latter context always have a
6050 `js2-expr-stmt-node' parent. Finally, the list does not include
6051 functions or scripts, which are treated separately from statements
6052 by the JavaScript parser and runtime.")
6053
6054 (defun js2-stmt-node-p (node)
6055 "Heuristic for figuring out if NODE is a statement.
6056 Some node types can appear in either an expression context or a
6057 statement context, e.g. let-nodes, yield-nodes, and var-decl nodes.
6058 For these node types in a statement context, the parent will be a
6059 `js2-expr-stmt-node'.
6060 Functions aren't included in the check."
6061 (memq (js2-node-type node) js2-stmt-node-types))
6062
6063 (defsubst js2-mode-find-first-stmt (node)
6064 "Search upward starting from NODE looking for a statement.
6065 For purposes of this function, a `js2-function-node' counts."
6066 (while (not (or (js2-stmt-node-p node)
6067 (js2-function-node-p node)))
6068 (setq node (js2-node-parent node)))
6069 node)
6070
6071 (defun js2-node-parent-stmt (node)
6072 "Return the node's first ancestor that is a statement.
6073 Returns nil if NODE is a `js2-ast-root'. Note that any expression
6074 appearing in a statement context will have a parent that is a
6075 `js2-expr-stmt-node' that will be returned by this function."
6076 (let ((parent (js2-node-parent node)))
6077 (if (or (null parent)
6078 (js2-stmt-node-p parent)
6079 (and (js2-function-node-p parent)
6080 (neq (js2-function-node-form parent) 'FUNCTION_EXPRESSION)))
6081 parent
6082 (js2-node-parent-stmt parent))))
6083
6084 ;; Roshan James writes:
6085 ;; Does consistent-return analysis on the function body when strict mode is
6086 ;; enabled.
6087 ;;
6088 ;; function (x) { return (x+1) }
6089 ;;
6090 ;; is ok, but
6091 ;;
6092 ;; function (x) { if (x < 0) return (x+1); }
6093 ;;
6094 ;; is not because the function can potentially return a value when the
6095 ;; condition is satisfied and if not, the function does not explicitly
6096 ;; return a value.
6097 ;;
6098 ;; This extends to checking mismatches such as "return" and "return <value>"
6099 ;; used in the same function. Warnings are not emitted if inconsistent
6100 ;; returns exist in code that can be statically shown to be unreachable.
6101 ;; Ex.
6102 ;; function (x) { while (true) { ... if (..) { return value } ... } }
6103 ;;
6104 ;; emits no warning. However if the loop had a break statement, then a
6105 ;; warning would be emitted.
6106 ;;
6107 ;; The consistency analysis looks at control structures such as loops, ifs,
6108 ;; switch, try-catch-finally blocks, examines the reachable code paths and
6109 ;; warns the user about an inconsistent set of termination possibilities.
6110 ;;
6111 ;; These flags enumerate the possible ways a statement/function can
6112 ;; terminate. These flags are used by endCheck() and by the Parser to
6113 ;; detect inconsistent return usage.
6114 ;;
6115 ;; END_UNREACHED is reserved for code paths that are assumed to always be
6116 ;; able to execute (example: throw, continue)
6117 ;;
6118 ;; END_DROPS_OFF indicates if the statement can transfer control to the
6119 ;; next one. Statement such as return dont. A compound statement may have
6120 ;; some branch that drops off control to the next statement.
6121 ;;
6122 ;; END_RETURNS indicates that the statement can return with no value.
6123 ;; END_RETURNS_VALUE indicates that the statement can return a value.
6124 ;;
6125 ;; A compound statement such as
6126 ;; if (condition) {
6127 ;; return value;
6128 ;; }
6129 ;; Will be detected as (END_DROPS_OFF | END_RETURN_VALUE) by endCheck()
6130
6131 (defconst js2-END_UNREACHED 0)
6132 (defconst js2-END_DROPS_OFF 1)
6133 (defconst js2-END_RETURNS 2)
6134 (defconst js2-END_RETURNS_VALUE 4)
6135 (defconst js2-END_YIELDS 8)
6136
6137 (defun js2-has-consistent-return-usage (node)
6138 "Check that every return usage in a function body is consistent.
6139 Returns t if the function satisfies strict mode requirement."
6140 (let ((n (js2-end-check node)))
6141 ;; either it doesn't return a value in any branch...
6142 (or (js2-flag-not-set-p n js2-END_RETURNS_VALUE)
6143 ;; or it returns a value (or is unreached) at every branch
6144 (js2-flag-not-set-p n (logior js2-END_DROPS_OFF
6145 js2-END_RETURNS
6146 js2-END_YIELDS)))))
6147
6148 (defun js2-end-check-if (node)
6149 "Returns in the then and else blocks must be consistent with each other.
6150 If there is no else block, then the return statement can fall through.
6151 Returns logical OR of END_* flags"
6152 (let ((th (js2-if-node-then-part node))
6153 (el (js2-if-node-else-part node)))
6154 (if (null th)
6155 js2-END_UNREACHED
6156 (logior (js2-end-check th) (if el
6157 (js2-end-check el)
6158 js2-END_DROPS_OFF)))))
6159
6160 (defun js2-end-check-switch (node)
6161 "Consistency of return statements is checked between the case statements.
6162 If there is no default, then the switch can fall through. If there is a
6163 default, we check to see if all code paths in the default return or if
6164 there is a code path that can fall through.
6165 Returns logical OR of END_* flags."
6166 (let ((rv js2-END_UNREACHED)
6167 default-case)
6168 ;; examine the cases
6169 (catch 'break
6170 (dolist (c (js2-switch-node-cases node))
6171 (if (js2-case-node-expr c)
6172 (js2-set-flag rv (js2-end-check-block c))
6173 (setq default-case c)
6174 (throw 'break nil))))
6175
6176 ;; we don't care how the cases drop into each other
6177 (js2-clear-flag rv js2-END_DROPS_OFF)
6178
6179 ;; examine the default
6180 (js2-set-flag rv (if default-case
6181 (js2-end-check default-case)
6182 js2-END_DROPS_OFF))
6183 rv))
6184
6185 (defun js2-end-check-try (node)
6186 "If the block has a finally, return consistency is checked in the
6187 finally block. If all code paths in the finally return, then the
6188 returns in the try-catch blocks don't matter. If there is a code path
6189 that does not return or if there is no finally block, the returns
6190 of the try and catch blocks are checked for mismatch.
6191 Returns logical OR of END_* flags."
6192 (let ((finally (js2-try-node-finally-block node))
6193 rv)
6194 ;; check the finally if it exists
6195 (setq rv (if finally
6196 (js2-end-check (js2-finally-node-body finally))
6197 js2-END_DROPS_OFF))
6198
6199 ;; If the finally block always returns, then none of the returns
6200 ;; in the try or catch blocks matter.
6201 (when (js2-flag-set-p rv js2-END_DROPS_OFF)
6202 (js2-clear-flag rv js2-END_DROPS_OFF)
6203
6204 ;; examine the try block
6205 (js2-set-flag rv (js2-end-check (js2-try-node-try-block node)))
6206
6207 ;; check each catch block
6208 (dolist (cb (js2-try-node-catch-clauses node))
6209 (js2-set-flag rv (js2-end-check (js2-catch-node-block cb)))))
6210 rv))
6211
6212 (defun js2-end-check-loop (node)
6213 "Return statement in the loop body must be consistent. The default
6214 assumption for any kind of a loop is that it will eventually terminate.
6215 The only exception is a loop with a constant true condition. Code that
6216 follows such a loop is examined only if one can statically determine
6217 that there is a break out of the loop.
6218
6219 for(... ; ... ; ...) {}
6220 for(... in ... ) {}
6221 while(...) { }
6222 do { } while(...)
6223
6224 Returns logical OR of END_* flags."
6225 (let ((rv (js2-end-check (js2-loop-node-body node)))
6226 (condition (cond
6227 ((js2-while-node-p node)
6228 (js2-while-node-condition node))
6229 ((js2-do-node-p node)
6230 (js2-do-node-condition node))
6231 ((js2-for-node-p node)
6232 (js2-for-node-condition node)))))
6233
6234 ;; check to see if the loop condition is always true
6235 (if (and condition
6236 (eq (js2-always-defined-boolean-p condition) 'ALWAYS_TRUE))
6237 (js2-clear-flag rv js2-END_DROPS_OFF))
6238
6239 ;; look for effect of breaks
6240 (js2-set-flag rv (js2-node-get-prop node
6241 'CONTROL_BLOCK_PROP
6242 js2-END_UNREACHED))
6243 rv))
6244
6245 (defun js2-end-check-block (node)
6246 "A general block of code is examined statement by statement.
6247 If any statement (even a compound one) returns in all branches, then
6248 subsequent statements are not examined.
6249 Returns logical OR of END_* flags."
6250 (let* ((rv js2-END_DROPS_OFF)
6251 (kids (js2-block-node-kids node))
6252 (n (car kids)))
6253 ;; Check each statment. If the statement can continue onto the next
6254 ;; one (i.e. END_DROPS_OFF is set), then check the next statement.
6255 (while (and n (js2-flag-set-p rv js2-END_DROPS_OFF))
6256 (js2-clear-flag rv js2-END_DROPS_OFF)
6257 (js2-set-flag rv (js2-end-check n))
6258 (setq kids (cdr kids)
6259 n (car kids)))
6260 rv))
6261
6262 (defun js2-end-check-label (node)
6263 "A labeled statement implies that there may be a break to the label.
6264 The function processes the labeled statement and then checks the
6265 CONTROL_BLOCK_PROP property to see if there is ever a break to the
6266 particular label.
6267 Returns logical OR of END_* flags."
6268 (let ((rv (js2-end-check (js2-labeled-stmt-node-stmt node))))
6269 (logior rv (js2-node-get-prop node
6270 'CONTROL_BLOCK_PROP
6271 js2-END_UNREACHED))))
6272
6273 (defun js2-end-check-break (node)
6274 "When a break is encountered annotate the statement being broken
6275 out of by setting its CONTROL_BLOCK_PROP property.
6276 Returns logical OR of END_* flags."
6277 (and (js2-break-node-target node)
6278 (js2-node-set-prop (js2-break-node-target node)
6279 'CONTROL_BLOCK_PROP
6280 js2-END_DROPS_OFF))
6281 js2-END_UNREACHED)
6282
6283 (defun js2-end-check (node)
6284 "Examine the body of a function, doing a basic reachability analysis.
6285 Returns a combination of flags END_* flags that indicate
6286 how the function execution can terminate. These constitute only the
6287 pessimistic set of termination conditions. It is possible that at
6288 runtime certain code paths will never be actually taken. Hence this
6289 analysis will flag errors in cases where there may not be errors.
6290 Returns logical OR of END_* flags"
6291 (let (kid)
6292 (cond
6293 ((js2-break-node-p node)
6294 (js2-end-check-break node))
6295
6296 ((js2-expr-stmt-node-p node)
6297 (if (setq kid (js2-expr-stmt-node-expr node))
6298 (js2-end-check kid)
6299 js2-END_DROPS_OFF))
6300
6301 ((or (js2-continue-node-p node)
6302 (js2-throw-node-p node))
6303 js2-END_UNREACHED)
6304
6305 ((js2-return-node-p node)
6306 (if (setq kid (js2-return-node-retval node))
6307 js2-END_RETURNS_VALUE
6308 js2-END_RETURNS))
6309
6310 ((js2-loop-node-p node)
6311 (js2-end-check-loop node))
6312
6313 ((js2-switch-node-p node)
6314 (js2-end-check-switch node))
6315
6316 ((js2-labeled-stmt-node-p node)
6317 (js2-end-check-label node))
6318
6319 ((js2-if-node-p node)
6320 (js2-end-check-if node))
6321
6322 ((js2-try-node-p node)
6323 (js2-end-check-try node))
6324
6325 ((js2-block-node-p node)
6326 (if (null (js2-block-node-kids node))
6327 js2-END_DROPS_OFF
6328 (js2-end-check-block node)))
6329
6330 ((js2-yield-node-p node)
6331 js2-END_YIELDS)
6332
6333 (t
6334 js2-END_DROPS_OFF))))
6335
6336 (defun js2-always-defined-boolean-p (node)
6337 "Check if NODE always evaluates to true or false in boolean context.
6338 Returns 'ALWAYS_TRUE, 'ALWAYS_FALSE, or nil if it's neither always true
6339 nor always false."
6340 (let ((tt (js2-node-type node))
6341 num)
6342 (cond
6343 ((or (= tt js2-FALSE) (= tt js2-NULL))
6344 'ALWAYS_FALSE)
6345 ((= tt js2-TRUE)
6346 'ALWAYS_TRUE)
6347 ((= tt js2-NUMBER)
6348 (setq num (js2-number-node-num-value node))
6349 (if (and (not (eq num 0.0e+NaN))
6350 (not (zerop num)))
6351 'ALWAYS_TRUE
6352 'ALWAYS_FALSE))
6353 (t
6354 nil))))
6355
6356 (provide 'js2-ast)
6357
6358 ;;; js2-ast.el ends here
6359 ;;; js2-highlight.el --- JavaScript syntax coloring support
6360
6361 ;; Author: Steve Yegge (steve.yegge@gmail.com)
6362 ;; Keywords: javascript languages
6363
6364 ;;; Code:
6365
6366
6367 (defsubst js2-set-face (beg end face &optional record)
6368 "Fontify a region. If RECORD is non-nil, record for later."
6369 (when (plusp js2-highlight-level)
6370 (setq beg (min (point-max) beg)
6371 beg (max (point-min) beg)
6372 end (min (point-max) end)
6373 end (max (point-min) end))
6374 (if record
6375 (push (list beg end face) js2-mode-fontifications)
6376 (put-text-property beg end 'face face))))
6377
6378 (defsubst js2-set-kid-face (pos kid len face)
6379 "Set-face on a child node.
6380 POS is absolute buffer position of parent.
6381 KID is the child node.
6382 LEN is the length to fontify.
6383 FACE is the face to fontify with."
6384 (js2-set-face (+ pos (js2-node-pos kid))
6385 (+ pos (js2-node-pos kid) (js2-node-len kid))
6386 face))
6387
6388 (defsubst js2-fontify-kwd (start length)
6389 (js2-set-face start (+ start length) 'font-lock-keyword-face))
6390
6391 (defsubst js2-clear-face (beg end)
6392 (remove-text-properties beg end '(face nil
6393 help-echo nil
6394 point-entered nil
6395 c-in-sws nil)))
6396
6397 (defsubst js2-record-text-property (beg end prop value)
6398 "Record a text property to set when parsing finishes."
6399 (push (list beg end prop value) js2-mode-deferred-properties))
6400
6401 (defconst js2-ecma-global-props
6402 (concat "^"
6403 (regexp-opt
6404 '("Infinity" "NaN" "undefined" "arguments") t)
6405 "$")
6406 "Value properties of the Ecma-262 Global Object.
6407 Shown at or above `js2-highlight-level' 2.")
6408
6409 ;; might want to add the name "arguments" to this list?
6410 (defconst js2-ecma-object-props
6411 (concat "^"
6412 (regexp-opt
6413 '("prototype" "__proto__" "__parent__") t)
6414 "$")
6415 "Value properties of the Ecma-262 Object constructor.
6416 Shown at or above `js2-highlight-level' 2.")
6417
6418 (defconst js2-ecma-global-funcs
6419 (concat
6420 "^"
6421 (regexp-opt
6422 '("decodeURI" "decodeURIComponent" "encodeURI" "encodeURIComponent"
6423 "eval" "isFinite" "isNaN" "parseFloat" "parseInt") t)
6424 "$")
6425 "Function properties of the Ecma-262 Global object.
6426 Shown at or above `js2-highlight-level' 2.")
6427
6428 (defconst js2-ecma-number-props
6429 (concat "^"
6430 (regexp-opt '("MAX_VALUE" "MIN_VALUE" "NaN"
6431 "NEGATIVE_INFINITY"
6432 "POSITIVE_INFINITY") t)
6433 "$")
6434 "Properties of the Ecma-262 Number constructor.
6435 Shown at or above `js2-highlight-level' 2.")
6436
6437 (defconst js2-ecma-date-props "^\\(parse\\|UTC\\)$"
6438 "Properties of the Ecma-262 Date constructor.
6439 Shown at or above `js2-highlight-level' 2.")
6440
6441
6442 (defconst js2-ecma-math-props
6443 (concat "^"
6444 (regexp-opt
6445 '("E" "LN10" "LN2" "LOG2E" "LOG10E" "PI" "SQRT1_2" "SQRT2")
6446 t)
6447 "$")
6448 "Properties of the Ecma-262 Math object.
6449 Shown at or above `js2-highlight-level' 2.")
6450
6451
6452 (defconst js2-ecma-math-funcs
6453 (concat "^"
6454 (regexp-opt
6455 '("abs" "acos" "asin" "atan" "atan2" "ceil" "cos" "exp" "floor"
6456 "log" "max" "min" "pow" "random" "round" "sin" "sqrt" "tan") t)
6457 "$")
6458 "Function properties of the Ecma-262 Math object.
6459 Shown at or above `js2-highlight-level' 2.")
6460
6461 (defconst js2-ecma-function-props
6462 (concat
6463 "^"
6464 (regexp-opt
6465 '(;; properties of the Object prototype object
6466 "hasOwnProperty" "isPrototypeOf" "propertyIsEnumerable"
6467 "toLocaleString" "toString" "valueOf"
6468 ;; properties of the Function prototype object
6469 "apply" "call"
6470 ;; properties of the Array prototype object
6471 "concat" "join" "pop" "push" "reverse" "shift" "slice" "sort"
6472 "splice" "unshift"
6473 ;; properties of the String prototype object
6474 "charAt" "charCodeAt" "fromCharCode" "indexOf" "lastIndexOf"
6475 "localeCompare" "match" "replace" "search" "split" "substring"
6476 "toLocaleLowerCase" "toLocaleUpperCase" "toLowerCase"
6477 "toUpperCase"
6478 ;; properties of the Number prototype object
6479 "toExponential" "toFixed" "toPrecision"
6480 ;; properties of the Date prototype object
6481 "getDate" "getDay" "getFullYear" "getHours" "getMilliseconds"
6482 "getMinutes" "getMonth" "getSeconds" "getTime"
6483 "getTimezoneOffset" "getUTCDate" "getUTCDay" "getUTCFullYear"
6484 "getUTCHours" "getUTCMilliseconds" "getUTCMinutes" "getUTCMonth"
6485 "getUTCSeconds" "setDate" "setFullYear" "setHours"
6486 "setMilliseconds" "setMinutes" "setMonth" "setSeconds" "setTime"
6487 "setUTCDate" "setUTCFullYear" "setUTCHours" "setUTCMilliseconds"
6488 "setUTCMinutes" "setUTCMonth" "setUTCSeconds" "toDateString"
6489 "toLocaleDateString" "toLocaleString" "toLocaleTimeString"
6490 "toTimeString" "toUTCString"
6491 ;; properties of the RegExp prototype object
6492 "exec" "test"
6493 ;; SpiderMonkey/Rhino extensions, versions 1.5+
6494 "toSource" "__defineGetter__" "__defineSetter__"
6495 "__lookupGetter__" "__lookupSetter__" "__noSuchMethod__"
6496 "every" "filter" "forEach" "lastIndexOf" "map" "some")
6497 t)
6498 "$")
6499 "Built-in functions defined by Ecma-262 and SpiderMonkey extensions.
6500 Shown at or above `js2-highlight-level' 3.")
6501
6502 (defsubst js2-parse-highlight-prop-get (parent target prop call-p)
6503 (let ((target-name (and target
6504 (js2-name-node-p target)
6505 (js2-name-node-name target)))
6506 (prop-name (if prop (js2-name-node-name prop)))
6507 (level1 (>= js2-highlight-level 1))
6508 (level2 (>= js2-highlight-level 2))
6509 (level3 (>= js2-highlight-level 3))
6510 pos
6511 face)
6512 (when level2
6513 (if call-p
6514 (cond
6515 ((and target prop)
6516 (cond
6517 ((and level3 (string-match js2-ecma-function-props prop-name))
6518 (setq face 'font-lock-builtin-face))
6519 ((and target-name prop)
6520 (cond
6521 ((string= target-name "Date")
6522 (if (string-match js2-ecma-date-props prop-name)
6523 (setq face 'font-lock-builtin-face)))
6524 ((string= target-name "Math")
6525 (if (string-match js2-ecma-math-funcs prop-name)
6526 (setq face 'font-lock-builtin-face)))))))
6527 (prop
6528 (if (string-match js2-ecma-global-funcs prop-name)
6529 (setq face 'font-lock-builtin-face))))
6530 (cond
6531 ((and target prop)
6532 (cond
6533 ((string= target-name "Number")
6534 (if (string-match js2-ecma-number-props prop-name)
6535 (setq face 'font-lock-constant-face)))
6536 ((string= target-name "Math")
6537 (if (string-match js2-ecma-math-props prop-name)
6538 (setq face 'font-lock-constant-face)))))
6539 (prop
6540 (if (string-match js2-ecma-object-props prop-name)
6541 (setq face 'font-lock-constant-face)))))
6542 (when face
6543 (js2-set-face (setq pos (+ (js2-node-pos parent) ; absolute
6544 (js2-node-pos prop))) ; relative
6545 (+ pos (js2-node-len prop))
6546 face)))))
6547
6548 (defun js2-parse-highlight-member-expr-node (node)
6549 "Perform syntax highlighting of EcmaScript built-in properties.
6550 The variable `js2-highlight-level' governs this highighting."
6551 (let (face target prop name pos end parent call-p callee)
6552 (cond
6553 ;; case 1: simple name, e.g. foo
6554 ((js2-name-node-p node)
6555 (setq name (js2-name-node-name node))
6556 ;; possible for name to be nil in rare cases - saw it when
6557 ;; running js2-mode on an elisp buffer. Might as well try to
6558 ;; make it so js2-mode never barfs.
6559 (when name
6560 (setq face (if (string-match js2-ecma-global-props name)
6561 'font-lock-constant-face))
6562 (when face
6563 (setq pos (js2-node-pos node)
6564 end (+ pos (js2-node-len node)))
6565 (js2-set-face pos end face))))
6566
6567 ;; case 2: property access or function call
6568 ((or (js2-prop-get-node-p node)
6569 ;; highlight function call if expr is a prop-get node
6570 ;; or a plain name (i.e. unqualified function call)
6571 (and (setq call-p (js2-call-node-p node))
6572 (setq callee (js2-call-node-target node)) ; separate setq!
6573 (or (js2-prop-get-node-p callee)
6574 (js2-name-node-p callee))))
6575 (setq parent node
6576 node (if call-p callee node))
6577 (if (and call-p (js2-name-node-p callee))
6578 (setq prop callee)
6579 (setq target (js2-prop-get-node-left node)
6580 prop (js2-prop-get-node-right node)))
6581 (cond
6582 ((js2-name-node-p target)
6583 (if (js2-name-node-p prop)
6584 ;; case 2a: simple target, simple prop name, e.g. foo.bar
6585 (js2-parse-highlight-prop-get parent target prop call-p)
6586 ;; case 2b: simple target, complex name, e.g. foo.x[y]
6587 (js2-parse-highlight-prop-get parent target nil call-p)))
6588 ((js2-name-node-p prop)
6589 ;; case 2c: complex target, simple name, e.g. x[y].bar
6590 (js2-parse-highlight-prop-get parent target prop call-p)))))))
6591
6592 (defun js2-parse-highlight-member-expr-fn-name (expr)
6593 "Highlight the `baz' in function foo.bar.baz(args) {...}.
6594 This is experimental Rhino syntax. EXPR is the foo.bar.baz member expr.
6595 We currently only handle the case where the last component is a prop-get
6596 of a simple name. Called before EXPR has a parent node."
6597 (let (pos
6598 (name (and (js2-prop-get-node-p expr)
6599 (js2-prop-get-node-right expr))))
6600 (when (js2-name-node-p name)
6601 (js2-set-face (setq pos (+ (js2-node-pos expr) ; parent is absolute
6602 (js2-node-pos name)))
6603 (+ pos (js2-node-len name))
6604 'font-lock-function-name-face
6605 'record))))
6606
6607 ;; source: http://jsdoc.sourceforge.net/
6608 ;; Note - this syntax is for Google's enhanced jsdoc parser that
6609 ;; allows type specifications, and needs work before entering the wild.
6610
6611 (defconst js2-jsdoc-param-tag-regexp
6612 (concat "^\\s-*\\*+\\s-*\\(@"
6613 "\\(?:param\\|argument\\)"
6614 "\\)"
6615 "\\s-*\\({[^}]+}\\)?" ; optional type
6616 "\\s-*\\([a-zA-Z0-9_$]+\\)?" ; name
6617 "\\>")
6618 "Matches jsdoc tags with optional type and optional param name.")
6619
6620 (defconst js2-jsdoc-typed-tag-regexp
6621 (concat "^\\s-*\\*+\\s-*\\(@\\(?:"
6622 (regexp-opt
6623 '("requires" "return" "returns" "throw" "throws"))
6624 "\\)\\)\\s-*\\({[^}]+}\\)?")
6625 "Matches jsdoc tags with optional type.")
6626
6627 (defconst js2-jsdoc-arg-tag-regexp
6628 (concat "^\\s-*\\*+\\s-*\\(@\\(?:"
6629 (regexp-opt
6630 '("base" "extends" "member" "type" "version"))
6631 "\\)\\)\\s-+\\([^ \t]+\\)")
6632 "Matches jsdoc tags with a single argument.")
6633
6634 (defconst js2-jsdoc-empty-tag-regexp
6635 (concat "^\\s-*\\*+\\s-*\\(@\\(?:"
6636 (regexp-opt
6637 '("addon" "author" "class" "constructor" "deprecated" "exec"
6638 "exception" "fileoverview" "final" "ignore" "private"))
6639 "\\)\\)\\s-*")
6640 "Matches empty jsdoc tags.")
6641
6642 (defconst js2-jsdoc-link-tag-regexp
6643 "{\\(@link\\)\\s-+\\([^#}\n]+\\)\\(#.+\\)?}"
6644 "Matches a jsdoc link tag.")
6645
6646 (defconst js2-jsdoc-see-tag-regexp
6647 "^\\s-*\\*+\\s-*\\(@see\\)\\s-+\\([^#}\n]+\\)\\(#.+\\)?"
6648 "Matches a jsdoc @see tag.")
6649
6650 (defconst js2-jsdoc-html-tag-regexp
6651 "\\(</?\\)\\([a-zA-Z]+\\)\\s-*\\(/?>\\)"
6652 "Matches a simple (no attributes) html start- or end-tag.")
6653
6654 (defsubst js2-jsdoc-highlight-helper ()
6655 (js2-set-face (match-beginning 1)
6656 (match-end 1)
6657 'js2-jsdoc-tag-face)
6658 (if (match-beginning 2)
6659 (if (save-excursion
6660 (goto-char (match-beginning 2))
6661 (= (char-after) ?{))
6662 (js2-set-face (1+ (match-beginning 2))
6663 (1- (match-end 2))
6664 'js2-jsdoc-type-face)
6665 (js2-set-face (match-beginning 2)
6666 (match-end 2)
6667 'js2-jsdoc-value-face)))
6668 (if (match-beginning 3)
6669 (js2-set-face (match-beginning 3)
6670 (match-end 3)
6671 'js2-jsdoc-value-face)))
6672
6673 (defun js2-highlight-jsdoc (ast)
6674 "Highlight doc comment tags."
6675 (let ((comments (js2-ast-root-comments ast))
6676 beg end)
6677 (save-excursion
6678 (dolist (node comments)
6679 (when (eq (js2-comment-node-format node) 'jsdoc)
6680 (setq beg (js2-node-abs-pos node)
6681 end (+ beg (js2-node-len node)))
6682 (save-restriction
6683 (narrow-to-region beg end)
6684 (dolist (re (list js2-jsdoc-param-tag-regexp
6685 js2-jsdoc-typed-tag-regexp
6686 js2-jsdoc-arg-tag-regexp
6687 js2-jsdoc-link-tag-regexp
6688 js2-jsdoc-see-tag-regexp
6689 js2-jsdoc-empty-tag-regexp))
6690 (goto-char beg)
6691 (while (re-search-forward re nil t)
6692 (js2-jsdoc-highlight-helper)))
6693 ;; simple highlighting for html tags
6694 (goto-char beg)
6695 (while (re-search-forward js2-jsdoc-html-tag-regexp nil t)
6696 (js2-set-face (match-beginning 1)
6697 (match-end 1)
6698 'js2-jsdoc-html-tag-delimiter-face)
6699 (js2-set-face (match-beginning 2)
6700 (match-end 2)
6701 'js2-jsdoc-html-tag-name-face)
6702 (js2-set-face (match-beginning 3)
6703 (match-end 3)
6704 'js2-jsdoc-html-tag-delimiter-face))))))))
6705
6706 (defun js2-highlight-assign-targets (node left right)
6707 "Highlight function properties and external variables."
6708 (let (leftpos end name)
6709 ;; highlight vars and props assigned function values
6710 (when (js2-function-node-p right)
6711 (cond
6712 ;; var foo = function() {...}
6713 ((js2-name-node-p left)
6714 (setq name left))
6715
6716 ;; foo.bar.baz = function() {...}
6717 ((and (js2-prop-get-node-p left)
6718 (js2-name-node-p (js2-prop-get-node-right left)))
6719 (setq name (js2-prop-get-node-right left))))
6720
6721 (when name
6722 (js2-set-face (setq leftpos (js2-node-abs-pos name))
6723 (+ leftpos (js2-node-len name))
6724 'font-lock-function-name-face
6725 'record)))
6726
6727 ;; save variable assignments so we can check for undeclared later
6728 ;; (can't do it here since var decls can come at end of script)
6729 (when (and js2-highlight-external-variables
6730 (setq name (js2-member-expr-leftmost-name left)))
6731 (push (list name js2-current-scope
6732 (setq leftpos (js2-node-abs-pos name))
6733 (setq end (+ leftpos (js2-node-len name))))
6734 js2-recorded-assignments))))
6735
6736 (defun js2-highlight-undeclared-vars ()
6737 "After entire parse is finished, look for undeclared variable assignments.
6738 Have to wait until entire buffer is parsed, since JavaScript permits var
6739 decls to occur after they're used.
6740
6741 We currently use a simple heuristic to rule out complaining about built-ins:
6742 if the name is capitalized we don't highlight it. This could be improved a
6743 bit by declaring all the Ecma global object, constructor and function names
6744 in a hashtable, but we'd still wind up complaining about all the DHTML
6745 builtins, the Mozilla builtins, etc."
6746 (let (name first-char)
6747 (dolist (entry js2-recorded-assignments)
6748 (destructuring-bind (name-node scope pos end) entry
6749 (setq name (js2-name-node-name name-node)
6750 first-char (aref name 0))
6751 (unless (or (and (>= first-char ?A) (<= first-char ?Z))
6752 (js2-get-defining-scope scope name))
6753 (js2-set-face pos end 'js2-external-variable-face 'record)
6754 (js2-record-text-property pos end 'help-echo "Undeclared variable")
6755 (js2-record-text-property pos end 'point-entered #'js2-echo-help))))
6756 (setq js2-recorded-assignments nil)))
6757
6758 (provide 'js2-highlight)
6759
6760 ;;; js2-highlight.el ends here
6761 ;;; js2-browse.el --- browsing/hierarchy support for js2-mode
6762
6763 ;; Author: Steve Yegge (steve.yegge@gmail.com)
6764 ;; Keywords: javascript languages
6765
6766 ;; Commentary:
6767 ;;
6768 ;; We currently only support imenu, but eventually should support speedbar and
6769 ;; possibly other browsing mechanisms.
6770 ;;
6771 ;; The basic strategy is to identify function assignment targets of the form
6772 ;; `foo.bar.baz', convert them to (list foo bar baz <position>), and push the
6773 ;; list into `js2-imenu-recorder'. The lists are merged into a trie-like tree
6774 ;; for imenu after parsing is finished.
6775 ;;
6776 ;; A `foo.bar.baz' assignment target may be expressed in many ways in
6777 ;; JavaScript, and the general problem is undecidable. However, several forms
6778 ;; are readily recognizable at parse-time; the forms we attempt to recognize
6779 ;; include:
6780 ;;
6781 ;; function foo() -- function declaration
6782 ;; foo = function() -- function expression assigned to variable
6783 ;; foo.bar.baz = function() -- function expr assigned to nested property-get
6784 ;; foo = {bar: function()} -- fun prop in object literal assigned to var
6785 ;; foo = {bar: {baz: function()}} -- inside nested object literal
6786 ;; foo.bar = {baz: function()}} -- obj lit assigned to nested prop get
6787 ;; a.b = {c: {d: function()}} -- nested obj lit assigned to nested prop get
6788 ;; foo = {get bar() {...}} -- getter/setter in obj literal
6789 ;; function foo() {function bar() {...}} -- nested function
6790 ;; foo['a'] = function() -- fun expr assigned to deterministic element-get
6791 ;;
6792 ;; This list boils down to a few forms that can be combined recursively.
6793 ;; Top-level named function declarations include both the left-hand (name)
6794 ;; and the right-hand (function value) expressions needed to produce an imenu
6795 ;; entry. The other "right-hand" forms we need to look for are:
6796 ;; - functions declared as props/getters/setters in object literals
6797 ;; - nested named function declarations
6798 ;; The "left-hand" expressions that functions can be assigned to include:
6799 ;; - local/global variables
6800 ;; - nested property-get expressions like a.b.c.d
6801 ;; - element gets like foo[10] or foo['bar'] where the index
6802 ;; expression can be trivially converted to a property name. They
6803 ;; effectively then become property gets.
6804 ;;
6805 ;; All the different definition types are canonicalized into the form
6806 ;; foo.bar.baz = position-of-function-keyword
6807 ;;
6808 ;; We need to build a trie-like structure for imenu. As an example,
6809 ;; consider the following JavaScript code:
6810 ;;
6811 ;; a = function() {...} // function at position 5
6812 ;; b = function() {...} // function at position 25
6813 ;; foo = function() {...} // function at position 100
6814 ;; foo.bar = function() {...} // function at position 200
6815 ;; foo.bar.baz = function() {...} // function at position 300
6816 ;; foo.bar.zab = function() {...} // function at position 400
6817 ;;
6818 ;; During parsing we accumulate an entry for each definition in
6819 ;; the variable `js2-imenu-recorder', like so:
6820 ;;
6821 ;; '((a 5)
6822 ;; (b 25)
6823 ;; (foo 100)
6824 ;; (foo bar 200)
6825 ;; (foo bar baz 300)
6826 ;; (foo bar zab 400))
6827 ;;
6828 ;; After parsing these entries are merged into this alist-trie:
6829 ;;
6830 ;; '((a . 1)
6831 ;; (b . 2)
6832 ;; (foo (<definition> . 3)
6833 ;; (bar (<definition> . 6)
6834 ;; (baz . 100)
6835 ;; (zab . 200))))
6836 ;;
6837 ;; Note the wacky need for a <definition> name. The token can be anything
6838 ;; that isn't a valid JavaScript identifier, because you might make foo
6839 ;; a function and then start setting properties on it that are also functions.
6840
6841 ;;; Code:
6842
6843
6844 (defsubst js2-prop-node-name (node)
6845 "Return the name of a node that may be a property-get/property-name.
6846 If NODE is not a valid name-node, string-node or integral number-node,
6847 returns nil. Otherwise returns the string name/value of the node."
6848 (cond
6849 ((js2-name-node-p node)
6850 (js2-name-node-name node))
6851 ((js2-string-node-p node)
6852 (js2-string-node-value node))
6853 ((and (js2-number-node-p node)
6854 (string-match "^[0-9]+$" (js2-number-node-value node)))
6855 (js2-number-node-value node))
6856 ((js2-this-node-p node)
6857 "this")))
6858
6859 (defsubst js2-node-qname-component (node)
6860 "Test function: return the name of this node, if it contributes to a qname.
6861 Returns nil if the node doesn't contribute."
6862 (copy-sequence
6863 (or (js2-prop-node-name node)
6864 (if (and (js2-function-node-p node)
6865 (js2-function-node-name node))
6866 (js2-name-node-name (js2-function-node-name node))))))
6867
6868 (defsubst js2-record-function-qname (fn-node qname)
6869 "Associate FN-NODE with its QNAME for later lookup.
6870 This is used in postprocessing the chain list. When we find a chain
6871 whose first element is a js2-THIS keyword node, we look up the parent
6872 function and see (using this map) whether it is the tail of a chain.
6873 If so, we replace the this-node with a copy of the parent's qname."
6874 (unless js2-imenu-function-map
6875 (setq js2-imenu-function-map (make-hash-table :test 'eq)))
6876 (puthash fn-node qname js2-imenu-function-map))
6877
6878 (defun js2-record-imenu-functions (node &optional var)
6879 "Record function definitions for imenu.
6880 NODE is a function node or an object literal.
6881 VAR, if non-nil, is the expression that NODE is being assigned to."
6882 (when js2-parse-ide-mode
6883 (let ((fun-p (js2-function-node-p node))
6884 qname left fname-node pos)
6885 (cond
6886 ;; non-anonymous function declaration?
6887 ((and fun-p
6888 (not var)
6889 (setq fname-node (js2-function-node-name node)))
6890 (push (setq qname (list fname-node (js2-node-pos node)))
6891 js2-imenu-recorder)
6892 (js2-record-function-qname node qname))
6893
6894 ;; for remaining forms, compute left-side tree branch first
6895 ((and var (setq qname (js2-compute-nested-prop-get var)))
6896 (cond
6897 ;; foo.bar.baz = function
6898 (fun-p
6899 (push (nconc qname (list (js2-node-pos node)))
6900 js2-imenu-recorder)
6901 (js2-record-function-qname node qname))
6902 ;; foo.bar.baz = object-literal
6903 ;; look for nested functions: {a: {b: function() {...} }}
6904 ((js2-object-node-p node)
6905 (js2-record-object-literal node qname))))))))
6906
6907 (defun js2-compute-nested-prop-get (node)
6908 "If NODE is of form foo.bar.baz, return component nodes as a list.
6909 Otherwise returns nil. Element-gets can be treated as property-gets
6910 if the index expression is a name, a string, or a positive integer."
6911 (let (left right head)
6912 (cond
6913 ((or (js2-name-node-p node)
6914 (js2-this-node-p node))
6915 (list node))
6916 ;; foo.bar.baz is parenthesized as (foo.bar).baz => right operand is a leaf
6917 ((js2-prop-get-node-p node) ; includes elem-get nodes
6918 (setq left (js2-prop-get-node-left node)
6919 right (js2-prop-get-node-right node))
6920 (if (and (or (js2-prop-get-node-p left) ; left == foo.bar
6921 (js2-name-node-p left)
6922 (js2-this-node-p left)) ; or left == foo
6923 (or (js2-name-node-p right) ; .bar
6924 (js2-string-node-p right) ; ['bar']
6925 (and (js2-number-node-p right) ; [10]
6926 (string-match "^[0-9]+$"
6927 (js2-number-node-value right)))))
6928 (if (setq head (js2-compute-nested-prop-get left))
6929 (nconc head (list right))))))))
6930
6931 (defun js2-record-object-literal (node qname)
6932 "Recursively process an object literal looking for functions.
6933 NODE is an object literal that is the right-hand child of an assignment
6934 expression. QNAME is a list of nodes representing the assignment target,
6935 e.g. for foo.bar.baz = {...}, QNAME is (foo-node bar-node baz-node).
6936 We do a depth-first traversal of NODE. Any functions we find are prefixed
6937 with QNAME plus the property name of the function and appended to the
6938 variable `js2-imenu-recorder'."
6939 ;; Elements are relative to parent position, which is still absolute,
6940 ;; since the parser passes the assignment target and value expressions
6941 ;; to us before they are added as children of the assignment node.
6942 (let ((pos (js2-node-pos node))
6943 left right)
6944 (dolist (e (js2-object-node-elems node)) ; e is a `js2-object-prop-node'
6945 (setq left (js2-infix-node-left e))
6946 (cond
6947 ;; foo: function() {...}
6948 ((js2-function-node-p (setq right (js2-infix-node-right e)))
6949 (when (js2-prop-node-name left)
6950 ;; As a policy decision, we record the position of the property,
6951 ;; not the position of the `function' keyword, since the property
6952 ;; is effectively the name of the function.
6953 (push (append qname (list left) (list (+ pos (js2-node-pos e))))
6954 js2-imenu-recorder)
6955 (js2-record-function-qname right qname)))
6956 ;; foo: {object-literal} -- add foo to qname and recurse
6957 ((js2-object-node-p right)
6958 (js2-record-object-literal right
6959 (append qname (list (js2-infix-node-left e)))))))))
6960
6961 (defsubst js2-node-top-level-decl-p (node)
6962 "Return t if NODE's name is defined in the top-level scope.
6963 Also returns t if NODE's name is not defined in any scope, since it implies
6964 that it's an external variable, which must also be in the top-level scope."
6965 (let* ((name (js2-prop-node-name node))
6966 (this-scope (js2-node-get-enclosing-scope node))
6967 defining-scope)
6968 (cond
6969 ((js2-this-node-p node)
6970 nil)
6971 ((null this-scope)
6972 t)
6973 ((setq defining-scope (js2-get-defining-scope this-scope name))
6974 (js2-ast-root-p defining-scope))
6975 (t t))))
6976
6977 (defun js2-browse-postprocess-chains (chains)
6978 "Modify function-declaration name chains after parsing finishes.
6979 Some of the information is only available after the parse tree is complete.
6980 For instance, following a 'this' reference requires a parent function node."
6981 (let (result head fn parent-chain p elem)
6982 (dolist (chain chains)
6983 ;; examine the head of each node to get its defining scope
6984 (setq head (car chain))
6985 (cond
6986 ;; if top-level/external, keep as-is
6987 ((js2-node-top-level-decl-p head)
6988 (push chain result))
6989 ;; check for a this-reference
6990 ((eq (js2-node-type head) js2-THIS)
6991 (setq fn (js2-node-parent-script-or-fn head))
6992 ;; if there is no parent function, or if the parent function
6993 ;; is nested, discard the head node and keep the rest of the chain.
6994 (if (or (null fn) (js2-nested-function-p fn))
6995 (push (cdr chain) result)
6996 ;; else look up parent in function-map. If not found, discard chain.
6997 (when (setq parent-chain (and js2-imenu-function-map
6998 (gethash fn js2-imenu-function-map)))
6999 ;; else discard head node and prefix parent fn qname, which is
7000 ;; the parent-chain sans tail, to this chain.
7001 (push (append (butlast parent-chain) (cdr chain)) result))))))
7002 ;; finally replace each node in each chain with its name.
7003 (dolist (chain result)
7004 (setq p chain)
7005 (while p
7006 (if (js2-node-p (setq elem (car p)))
7007 (setcar p (js2-node-qname-component elem)))
7008 (setq p (cdr p))))
7009 result))
7010
7011 ;; Merge name chains into a trie-like tree structure of nested lists.
7012 ;; To simplify construction of the trie, we first build it out using the rule
7013 ;; that the trie consists of lists of pairs. Each pair is a 2-element array:
7014 ;; [key, num-or-list]. The second element can be a number; if so, this key
7015 ;; is a leaf-node with only one value. (I.e. there is only one declaration
7016 ;; associated with the key at this level.) Otherwise the second element is
7017 ;; a list of pairs, with the rule applied recursively. This symmetry permits
7018 ;; a simple recursive formulation.
7019 ;;
7020 ;; js2-mode is building the data structure for imenu. The imenu documentation
7021 ;; claims that it's the structure above, but in practice it wants the children
7022 ;; at the same list level as the key for that level, which is how I've drawn
7023 ;; the "Expected final result" above. We'll postprocess the trie to remove the
7024 ;; list wrapper around the children at each level.
7025 ;;
7026 ;; A completed nested imenu-alist entry looks like this:
7027 ;; '(("foo"
7028 ;; ("<definition>" . 7)
7029 ;; ("bar"
7030 ;; ("a" . 40)
7031 ;; ("b" . 60))))
7032 ;;
7033 ;; In particular, the documentation for `imenu--index-alist' says that
7034 ;; a nested sub-alist element looks like (INDEX-NAME SUB-ALIST).
7035 ;; The sub-alist entries immediately follow INDEX-NAME, the head of the list.
7036
7037 (defsubst js2-treeify (lst)
7038 "Convert (a b c d) to (a ((b ((c d)))))"
7039 (if (null (cddr lst)) ; list length <= 2
7040 lst
7041 (list (car lst) (list (js2-treeify (cdr lst))))))
7042
7043 (defun js2-build-alist-trie (chains trie)
7044 "Merge declaration name chains into a trie-like alist structure for imenu.
7045 CHAINS is the qname chain list produced during parsing. TRIE is a
7046 list of elements built up so far."
7047 (let (head tail pos branch kids)
7048 (dolist (chain chains)
7049 (setq head (car chain)
7050 tail (cdr chain)
7051 pos (if (numberp (car tail)) (car tail))
7052 branch (js2-find-if (lambda (n)
7053 (string= (car n) head))
7054 trie)
7055 kids (second branch))
7056 (cond
7057 ;; case 1: this key isn't in the trie yet
7058 ((null branch)
7059 (if trie
7060 (setcdr (last trie) (list (js2-treeify chain)))
7061 (setq trie (list (js2-treeify chain)))))
7062
7063 ;; case 2: key is present with a single number entry: replace w/ list
7064 ;; ("a1" 10) + ("a1" 20) => ("a1" (("<definition>" 10)
7065 ;; ("<definition>" 20)))
7066 ((numberp kids)
7067 (setcar (cdr branch)
7068 (list (list "<definition-1>" kids)
7069 (if pos
7070 (list "<definition-2>" pos)
7071 (js2-treeify tail)))))
7072
7073 ;; case 3: key is there (with kids), and we're a number entry
7074 (pos
7075 (setcdr (last kids)
7076 (list
7077 (list (format "<definition-%d>"
7078 (1+ (loop for kid in kids
7079 count (eq ?< (aref (car kid) 0)))))
7080 pos))))
7081
7082 ;; case 4: key is there with kids, need to merge in our chain
7083 (t
7084 (js2-build-alist-trie (list tail) kids))))
7085 trie))
7086
7087 (defun js2-flatten-trie (trie)
7088 "Convert TRIE to imenu-format.
7089 Recurses through nodes, and for each one whose second element is a list,
7090 appends the list's flattened elements to the current element. Also
7091 changes the tails into conses. For instance, this pre-flattened trie
7092
7093 '(a ((b 20)
7094 (c ((d 30)
7095 (e 40)))))
7096
7097 becomes
7098
7099 '(a (b . 20)
7100 (c (d . 30)
7101 (e . 40)))
7102
7103 Note that the root of the trie has no key, just a list of chains.
7104 This is also true for the value of any key with multiple children,
7105 e.g. key 'c' in the example above."
7106 (cond
7107 ((listp (car trie))
7108 (mapcar #'js2-flatten-trie trie))
7109 (t
7110 (if (numberp (second trie))
7111 (cons (car trie) (second trie))
7112 ;; else pop list and append its kids
7113 (apply #'append (list (car trie)) (js2-flatten-trie (cdr trie)))))))
7114
7115 (defun js2-build-imenu-index ()
7116 "Turn `js2-imenu-recorder' into an imenu data structure."
7117 (unless (eq js2-imenu-recorder 'empty)
7118 (let* ((chains (js2-browse-postprocess-chains js2-imenu-recorder))
7119 (result (js2-build-alist-trie chains nil)))
7120 (js2-flatten-trie result))))
7121
7122 (defun js2-test-print-chains (chains)
7123 "Print a list of qname chains.
7124 Each element of CHAINS is a list of the form (NODE [NODE *] pos);
7125 i.e. one or more nodes, and an integer position as the list tail."
7126 (mapconcat (lambda (chain)
7127 (concat "("
7128 (mapconcat (lambda (elem)
7129 (if (js2-node-p elem)
7130 (or (js2-node-qname-component elem)
7131 "nil")
7132 (number-to-string elem)))
7133 chain
7134 " ")
7135 ")"))
7136 chains
7137 "\n"))
7138
7139
7140 (provide 'js2-browse)
7141
7142 ;;; js2-browse.el ends here
7143 ;;; js2-parse.el --- JavaScript parser
7144
7145 ;; Author: Steve Yegge (steve.yegge@gmail.com)
7146 ;; Keywords: javascript languages
7147
7148 ;; Commentary:
7149
7150 ;; This is based on Rhino's parser and tries to follow its code
7151 ;; structure as closely as practical, so that changes to the Rhino
7152 ;; parser can easily be propagated into this code. However, Rhino
7153 ;; does not currently generate a usable AST representation, at least
7154 ;; from an IDE perspective, so we build our own more suitable AST.
7155
7156 ;; The AST node structures are defined in `js2-ast.el'.
7157 ;; Every parser function that creates and returns an AST node has
7158 ;; the following responsibilities:
7159
7160 ;; 1) set the node start to the absolute buffer start position
7161 ;; 2) set the node length to include any closing chars (RC, SEMI)
7162 ;; 3) fix up any child-node starts to be relative to this node
7163 ;; 4) set any field positions (e.g. keywords) relative to this node
7164 ;; 5) report any child nodes with `js2-node-add-children'
7165 ;; (note that this call fixes up start positions by default)
7166
7167 ;; The resulting AST has all node start positions relative to the
7168 ;; parent nodes; only the root has an absolute start position.
7169
7170 ;; Note: fontification is done inline while parsing. It used to be
7171 ;; done in a second pass over the AST, but doing it inline is about
7172 ;; twice as fast. Most of the fontification happens when tokens are
7173 ;; scanned, and the parser has a few spots that perform extra
7174 ;; fontification. In addition to speed, a second benefit of inline
7175 ;; parsing is that if a long parse is interrupted, everything parsed
7176 ;; so far is still fontified.
7177
7178 ;; The editing mode that uses this parser, `js2-mode', directs the
7179 ;; parser to check periodically for user input. If user input
7180 ;; arrives, the parse is abandoned, except for the highlighting that
7181 ;; has occurred so far, and a re-parse is rescheduled for when Emacs
7182 ;; becomes idle again. This works pretty well, but could be better.
7183 ;; In particular, when the user input has not resulted in changes to
7184 ;; the buffer (for instance, navigation input), the parse tree built
7185 ;; so far should not be discarded, and the parse should continue where
7186 ;; it left off. It will be some work to create what amounts to a
7187 ;; continuation, but it should not be unreasonably difficult.
7188
7189 ;; TODO:
7190 ;; - make non-editing input restart parse at previous continuation
7191 ;; - in Eclipse, sibling nodes never overlap start/end ranges
7192 ;; - for getters, prop name and function nodes overlap
7193 ;; - should write a debug tree visitor to look for overlaps
7194 ;; - mark array and object literals as "destructuring" (node prop?)
7195 ;; so we can syntax-highlight them properly.
7196 ;; - figure out a way not to store value in string/name nodes
7197 ;; - needs a solution for synthetic nodes
7198
7199 ;;; Code
7200
7201 (eval-and-compile
7202 (require 'cl)) ; for delete-if
7203
7204
7205 (defconst js2-version "1.7.0"
7206 "Version of JavaScript supported, plus minor js2 version.")
7207
7208 (defmacro js2-record-face (face)
7209 "Record a style run of FACE for the current token."
7210 `(js2-set-face js2-token-beg js2-token-end ,face 'record))
7211
7212 (defsubst js2-node-end (n)
7213 "Computes the absolute end of node N.
7214 Use with caution! Assumes `js2-node-pos' is -absolute-, which
7215 is only true until the node is added to its parent; i.e., while parsing."
7216 (+ (js2-node-pos n)
7217 (js2-node-len n)))
7218
7219 (defsubst js2-record-comment ()
7220 (push (make-js2-comment-node :len (- js2-token-end js2-token-beg)
7221 :format js2-ts-comment-type)
7222 js2-scanned-comments)
7223 (when js2-parse-ide-mode
7224 (js2-record-face 'font-lock-comment-face)
7225 (when (memq js2-ts-comment-type '(html preprocessor))
7226 ;; Tell cc-engine the bounds of the comment.
7227 (put-text-property js2-token-beg (1- js2-token-end) 'c-in-sws t))))
7228
7229 ;; This function is called depressingly often, so it should be fast.
7230 ;; Most of the time it's looking at the same token it peeked before.
7231 (defsubst js2-peek-token ()
7232 "Returns the next token without consuming it.
7233 If previous token was consumed, calls scanner to get new token.
7234 If previous token was -not- consumed, returns it (idempotent).
7235
7236 This function will not return a newline (js2-EOL) - instead, it
7237 gobbles newlines until it finds a non-newline token, and flags
7238 that token as appearing just after a newline.
7239
7240 This function will also not return a js2-COMMENT. Instead, it
7241 records comments found in `js2-scanned-comments'. If the token
7242 returned by this function immediately follows a jsdoc comment,
7243 the token is flagged as such.
7244
7245 Note that this function always returned the un-flagged token!
7246 The flags, if any, are saved in `js2-current-flagged-token'."
7247 (if (/= js2-current-flagged-token js2-EOF) ; last token not consumed
7248 js2-current-token ; most common case - return already-peeked token
7249 (let ((tt (js2-get-token)) ; call scanner
7250 saw-eol
7251 face)
7252 ;; process comments and whitespace
7253 (while (or (= tt js2-EOL)
7254 (= tt js2-COMMENT))
7255 (if (= tt js2-EOL)
7256 (setq saw-eol t)
7257 (setq saw-eol nil)
7258 (if js2-record-comments
7259 (js2-record-comment)))
7260 (setq tt (js2-get-token))) ; call scanner
7261
7262 (setq js2-current-token tt
7263 js2-current-flagged-token (if saw-eol
7264 (logior tt js2-ti-after-eol)
7265 tt))
7266 ;; perform lexical fontification as soon as token is scanned
7267 (when js2-parse-ide-mode
7268 (cond
7269 ((minusp tt)
7270 (js2-record-face 'js2-error-face))
7271 ((setq face (aref js2-kwd-tokens tt))
7272 (js2-record-face face))
7273 ((and (= tt js2-NAME)
7274 (equal js2-ts-string "undefined"))
7275 (js2-record-face 'font-lock-constant-face))))
7276 tt))) ; return unflagged token
7277
7278 (defsubst js2-peek-flagged-token ()
7279 "Returns the current token along with any flags set for it."
7280 (js2-peek-token)
7281 js2-current-flagged-token)
7282
7283 (defsubst js2-consume-token ()
7284 (setq js2-current-flagged-token js2-EOF))
7285
7286 (defsubst js2-next-token ()
7287 (prog1
7288 (js2-peek-token)
7289 (js2-consume-token)))
7290
7291 (defsubst js2-next-flagged-token ()
7292 (js2-peek-token)
7293 (prog1 js2-current-flagged-token
7294 (js2-consume-token)))
7295
7296 (defsubst js2-match-token (match)
7297 "Consume and return t if next token matches MATCH, a bytecode.
7298 Returns nil and consumes nothing if MATCH is not the next token."
7299 (if (/= (js2-peek-token) match)
7300 nil
7301 (js2-consume-token)
7302 t))
7303
7304 (defsubst js2-valid-prop-name-token (tt)
7305 (or (= tt js2-NAME)
7306 (and js2-allow-keywords-as-property-names
7307 (plusp tt)
7308 (aref js2-kwd-tokens tt))))
7309
7310 (defsubst js2-match-prop-name ()
7311 "Consume token and return t if next token is a valid property name.
7312 It's valid if it's a js2-NAME, or `js2-allow-keywords-as-property-names'
7313 is non-nil and it's a keyword token."
7314 (if (js2-valid-prop-name-token (js2-peek-token))
7315 (progn
7316 (js2-consume-token)
7317 t)
7318 nil))
7319
7320 (defsubst js2-must-match-prop-name (msg-id &optional pos len)
7321 (if (js2-match-prop-name)
7322 t
7323 (js2-report-error msg-id nil pos len)
7324 nil))
7325
7326 (defsubst js2-peek-token-or-eol ()
7327 "Return js2-EOL if the current token immediately follows a newline.
7328 Else returns the current token. Used in situations where we don't
7329 consider certain token types valid if they are preceded by a newline.
7330 One example is the postfix ++ or -- operator, which has to be on the
7331 same line as its operand."
7332 (let ((tt (js2-peek-token)))
7333 ;; Check for last peeked token flags
7334 (if (js2-flag-set-p js2-current-flagged-token js2-ti-after-eol)
7335 js2-EOL
7336 tt)))
7337
7338 (defsubst js2-set-check-for-label ()
7339 (assert (= (logand js2-current-flagged-token js2-clear-ti-mask) js2-NAME))
7340 (js2-set-flag js2-current-flagged-token js2-ti-check-label))
7341
7342 (defsubst js2-must-match (token msg-id &optional pos len)
7343 "Match next token to token code TOKEN, or record a syntax error.
7344 MSG-ID is the error message to report if the match fails.
7345 Returns t on match, nil if no match."
7346 (if (js2-match-token token)
7347 t
7348 (js2-report-error msg-id nil pos len)
7349 nil))
7350
7351 (defsubst js2-inside-function ()
7352 (plusp js2-nesting-of-function))
7353
7354 (defsubst js2-set-requires-activation ()
7355 (if (js2-function-node-p js2-current-script-or-fn)
7356 (setf (js2-function-node-needs-activation js2-current-script-or-fn) t)))
7357
7358 (defsubst js2-check-activation-name (name token)
7359 (when (js2-inside-function)
7360 ;; skip language-version 1.2 check from Rhino
7361 (if (or (string= "arguments" name)
7362 (and js2-compiler-activation-names ; only used in codegen
7363 (gethash name js2-compiler-activation-names)))
7364 (js2-set-requires-activation))))
7365
7366 (defsubst js2-set-is-generator ()
7367 (if (js2-function-node-p js2-current-script-or-fn)
7368 (setf (js2-function-node-is-generator js2-current-script-or-fn) t)))
7369
7370 (defsubst js2-must-have-xml ()
7371 (unless js2-compiler-xml-available
7372 (js2-report-error "msg.XML.not.available")))
7373
7374 (defsubst js2-push-scope (scope)
7375 "Push SCOPE, a `js2-scope', onto the lexical scope chain."
7376 (assert (js2-scope-p scope))
7377 (assert (null (js2-scope-parent-scope scope)))
7378 (assert (neq js2-current-scope scope))
7379 (setf (js2-scope-parent-scope scope) js2-current-scope
7380 js2-current-scope scope))
7381
7382 (defsubst js2-pop-scope ()
7383 (setq js2-current-scope
7384 (js2-scope-parent-scope js2-current-scope)))
7385
7386 (defsubst js2-enter-loop (loop-node)
7387 (push loop-node js2-loop-set)
7388 (push loop-node js2-loop-and-switch-set)
7389 (js2-push-scope loop-node)
7390 ;; Tell the current labeled statement (if any) its statement,
7391 ;; and set the jump target of the first label to the loop.
7392 ;; These are used in `js2-parse-continue' to verify that the
7393 ;; continue target is an actual labeled loop. (And for codegen.)
7394 (when js2-labeled-stmt
7395 (setf (js2-labeled-stmt-node-stmt js2-labeled-stmt) loop-node
7396 (js2-label-node-loop (car (js2-labeled-stmt-node-labels
7397 js2-labeled-stmt))) loop-node)))
7398
7399 (defsubst js2-exit-loop ()
7400 (pop js2-loop-set)
7401 (pop js2-loop-and-switch-set)
7402 (js2-pop-scope))
7403
7404 (defsubst js2-enter-switch (switch-node)
7405 (push switch-node js2-loop-and-switch-set))
7406
7407 (defsubst js2-exit-switch ()
7408 (pop js2-loop-and-switch-set))
7409
7410 (defun js2-parse (&optional buf cb)
7411 "Tells the js2 parser to parse a region of JavaScript.
7412
7413 BUF is a buffer or buffer name containing the code to parse.
7414 Call `narrow-to-region' first to parse only part of the buffer.
7415
7416 The returned AST root node is given some additional properties:
7417 `node-count' - total number of nodes in the AST
7418 `buffer' - BUF. The buffer it refers to may change or be killed,
7419 so the value is not necessarily reliable.
7420
7421 An optional callback CB can be specified to report parsing
7422 progress. If `(functionp CB)' returns t, it will be called with
7423 the current line number once before parsing begins, then again
7424 each time the lexer reaches a new line number.
7425
7426 CB can also be a list of the form `(symbol cb ...)' to specify
7427 multiple callbacks with different criteria. Each symbol is a
7428 criterion keyword, and the following element is the callback to
7429 call
7430
7431 :line - called whenever the line number changes
7432 :token - called for each new token consumed
7433
7434 The list of criteria could be extended to include entering or
7435 leaving a statement, an expression, or a function definition."
7436 (if (and cb (not (functionp cb)))
7437 (error "criteria callbacks not yet implemented"))
7438 (let ((inhibit-point-motion-hooks t)
7439 (js2-compiler-xml-available (>= js2-language-version 160))
7440 ;; This is a recursive-descent parser, so give it a big stack.
7441 (max-lisp-eval-depth (max max-lisp-eval-depth 3000))
7442 (max-specpdl-size (max max-specpdl-size 3000))
7443 (case-fold-search nil)
7444 ast)
7445 (or buf (setq buf (current-buffer)))
7446 (save-excursion
7447 (set-buffer buf)
7448 (setq js2-scanned-comments nil
7449 js2-parsed-errors nil
7450 js2-parsed-warnings nil
7451 js2-imenu-recorder nil
7452 js2-imenu-function-map nil
7453 js2-label-set nil)
7454 (js2-init-scanner)
7455 (setq ast (js2-with-unmodifying-text-property-changes
7456 (js2-do-parse)))
7457 (unless js2-ts-hit-eof
7458 (js2-report-error "msg.got.syntax.errors" (length js2-parsed-errors)))
7459 (setf (js2-ast-root-errors ast) js2-parsed-errors
7460 (js2-ast-root-warnings ast) js2-parsed-warnings)
7461 ;; if we didn't find any declarations, put a dummy in this list so we
7462 ;; don't end up re-parsing the buffer in `js2-mode-create-imenu-index'
7463 (unless js2-imenu-recorder
7464 (setq js2-imenu-recorder 'empty))
7465 (run-hooks 'js2-parse-finished-hook)
7466 ast)))
7467
7468 ;; Corresponds to Rhino's Parser.parse() method.
7469 (defun js2-do-parse ()
7470 "Parse current buffer starting from current point.
7471 Scanner should be initialized."
7472 (let ((pos js2-ts-cursor)
7473 (end js2-ts-cursor) ; in case file is empty
7474 root n tt)
7475 ;; initialize buffer-local parsing vars
7476 (setf root (make-js2-ast-root :buffer (buffer-name) :pos pos)
7477 js2-current-script-or-fn root
7478 js2-current-scope root
7479 js2-current-flagged-token js2-EOF
7480 js2-nesting-of-function 0
7481 js2-labeled-stmt nil
7482 js2-recorded-assignments nil) ; for js2-highlight
7483
7484 (while (/= (setq tt (js2-peek-token)) js2-EOF)
7485 (if (= tt js2-FUNCTION)
7486 (progn
7487 (js2-consume-token)
7488 (setq n (js2-parse-function (if js2-called-by-compile-function
7489 'FUNCTION_EXPRESSION
7490 'FUNCTION_STATEMENT)))
7491 (js2-record-imenu-functions n))
7492 ;; not a function - parse a statement
7493 (setq n (js2-parse-statement)))
7494 ;; add function or statement to script
7495 (setq end (js2-node-end n))
7496 (js2-block-node-push root n))
7497
7498 ;; add comments to root in lexical order
7499 (when js2-scanned-comments
7500 ;; if we find a comment beyond end of normal kids, use its end
7501 (setq end (max end (js2-node-end (first js2-scanned-comments))))
7502 (dolist (comment js2-scanned-comments)
7503 (push comment (js2-ast-root-comments root))
7504 (js2-node-add-children root comment)))
7505
7506 (setf (js2-node-len root) (- end pos))
7507 (js2-highlight-undeclared-vars)
7508 root))
7509
7510 (defun js2-function-parser ()
7511 (js2-consume-token)
7512 (js2-parse-function 'FUNCTION_EXPRESSION_STATEMENT))
7513
7514 (defun js2-parse-function-body (fn-node)
7515 (js2-must-match js2-LC "msg.no.brace.body")
7516 (let ((pos js2-token-beg) ; LC position
7517 (pn (make-js2-block-node)) ; starts at LC position
7518 tt
7519 end)
7520 (incf js2-nesting-of-function)
7521 (unwind-protect
7522 (while (not (or (= (setq tt (js2-peek-token)) js2-ERROR)
7523 (= tt js2-EOF)
7524 (= tt js2-RC)))
7525 (js2-block-node-push pn (if (/= tt js2-FUNCTION)
7526 (js2-parse-statement)
7527 (js2-consume-token)
7528 (js2-parse-function 'FUNCTION_STATEMENT))))
7529 (decf js2-nesting-of-function))
7530 (setq end js2-token-end) ; assume no curly and leave at current token
7531 (if (js2-must-match js2-RC "msg.no.brace.after.body" pos)
7532 (setq end js2-token-end))
7533 (setf (js2-node-pos pn) pos
7534 (js2-node-len pn) (- end pos))
7535 (setf (js2-function-node-body fn-node) pn)
7536 (js2-node-add-children fn-node pn)
7537 pn))
7538
7539 (defun js2-parse-function-params (fn-node pos)
7540 (if (js2-match-token js2-RP)
7541 (setf (js2-function-node-rp fn-node) (- js2-token-beg pos))
7542 (let (params len param)
7543 (loop for tt = (js2-peek-token)
7544 do
7545 (cond
7546 ;; destructuring param
7547 ((or (= tt js2-LB) (= tt js2-LC))
7548 (push (js2-parse-primary-expr) params))
7549 ;; simple name
7550 (t
7551 (js2-must-match js2-NAME "msg.no.parm")
7552 (js2-record-face 'js2-function-param-face)
7553 (setq param (js2-create-name-node))
7554 (js2-define-symbol js2-LP js2-ts-string param)
7555 (push param params)))
7556 while
7557 (js2-match-token js2-COMMA))
7558 (if (js2-must-match js2-RP "msg.no.paren.after.parms")
7559 (setf (js2-function-node-rp fn-node) (- js2-token-beg pos)))
7560 (dolist (p params)
7561 (js2-node-add-children fn-node p)
7562 (push p (js2-function-node-params fn-node))))))
7563
7564 (defsubst js2-check-inconsistent-return-warning (fn-node name)
7565 "Possibly show inconsistent-return warning.
7566 Last token scanned is the close-curly for the function body."
7567 (when (and js2-mode-show-strict-warnings
7568 js2-strict-inconsistent-return-warning
7569 (not (js2-has-consistent-return-usage
7570 (js2-function-node-body fn-node))))
7571 ;; Have it extend from close-curly to bol or beginning of block.
7572 (let ((pos (save-excursion
7573 (goto-char js2-token-end)
7574 (max (js2-node-abs-pos (js2-function-node-body fn-node))
7575 (point-at-bol))))
7576 (end js2-token-end))
7577 (if (plusp (js2-name-node-length name))
7578 (js2-add-strict-warning "msg.no.return.value"
7579 (js2-name-node-name name) pos end)
7580 (js2-add-strict-warning "msg.anon.no.return.value" nil pos end)))))
7581
7582 (defun js2-parse-function (function-type)
7583 "Function parser. FUNCTION-TYPE is a symbol."
7584 (let ((pos js2-token-beg) ; start of 'function' keyword
7585 name
7586 name-beg
7587 name-end
7588 fn-node
7589 lp
7590 (synthetic-type function-type)
7591 member-expr-node)
7592
7593 ;; parse function name, expression, or non-name (anonymous)
7594 (cond
7595 ;; function foo(...)
7596 ((js2-match-token js2-NAME)
7597 (setq name (js2-create-name-node t)
7598 name-beg js2-token-beg
7599 name-end js2-token-end)
7600 (unless (js2-match-token js2-LP)
7601 (when js2-allow-member-expr-as-function-name
7602 ;; function foo.bar(...)
7603 (setq member-expr-node name
7604 name nil
7605 member-expr-node (js2-parse-member-expr-tail
7606 nil member-expr-node)))
7607 (js2-must-match js2-LP "msg.no.paren.parms")))
7608
7609 ((js2-match-token js2-LP)
7610 nil) ; anonymous function: leave name as null
7611
7612 (t
7613 ;; function random-member-expr(...)
7614 (when js2-allow-member-expr-as-function-name
7615 ;; Note that memberExpr can not start with '(' like
7616 ;; in function (1+2).toString(), because 'function (' already
7617 ;; processed as anonymous function
7618 (setq member-expr-node (js2-parse-member-expr)))
7619 (js2-must-match js2-LP "msg.no.paren.parms")))
7620
7621 (if (= js2-current-token js2-LP) ; eventually matched LP?
7622 (setq lp js2-token-beg))
7623
7624 (if member-expr-node
7625 (progn
7626 (setq synthetic-type 'FUNCTION_EXPRESSION)
7627 (js2-parse-highlight-member-expr-fn-name member-expr-node))
7628 (if name
7629 (js2-set-face name-beg name-end
7630 'font-lock-function-name-face 'record)))
7631
7632 (if (and (neq synthetic-type 'FUNCTION_EXPRESSION)
7633 (plusp (js2-name-node-length name)))
7634 ;; Function statements define a symbol in the enclosing scope
7635 (js2-define-symbol js2-FUNCTION (js2-name-node-name name) fn-node))
7636
7637 (setf fn-node (make-js2-function-node :pos pos
7638 :name name
7639 :form function-type
7640 :lp (if lp (- lp pos))))
7641
7642 (if (or (js2-inside-function) (plusp js2-nesting-of-with))
7643 ;; 1. Nested functions are not affected by the dynamic scope flag
7644 ;; as dynamic scope is already a parent of their scope.
7645 ;; 2. Functions defined under the with statement also immune to
7646 ;; this setup, in which case dynamic scope is ignored in favor
7647 ;; of the with object.
7648 (setf (js2-function-node-ignore-dynamic fn-node) t))
7649
7650 ;; dynamically bind all the per-function variables
7651 (let ((js2-current-script-or-fn fn-node)
7652 (js2-current-scope fn-node)
7653 (js2-nesting-of-with 0)
7654 (js2-end-flags 0)
7655 js2-label-set
7656 js2-loop-set
7657 js2-loop-and-switch-set)
7658
7659 ;; parse params and function body
7660 (js2-parse-function-params fn-node pos)
7661 (js2-parse-function-body fn-node)
7662 (if name
7663 (js2-node-add-children fn-node name))
7664
7665 (js2-check-inconsistent-return-warning fn-node name)
7666
7667 ;; Function expressions define a name only in the body of the
7668 ;; function, and only if not hidden by a parameter name
7669 (if (and name
7670 (eq synthetic-type 'FUNCTION_EXPRESSION)
7671 (null (js2-scope-get-symbol js2-current-scope
7672 (js2-name-node-name name))))
7673 (js2-define-symbol js2-FUNCTION
7674 (js2-name-node-name name)
7675 fn-node))
7676 (if (and name
7677 (eq function-type 'FUNCTION_EXPRESSION_STATEMENT))
7678 (js2-record-imenu-functions fn-node)))
7679
7680 (setf (js2-node-len fn-node) (- js2-ts-cursor pos)
7681 (js2-function-node-member-expr fn-node) member-expr-node) ; may be nil
7682
7683 ;; Rhino doesn't do this, but we need it for finding undeclared vars.
7684 ;; We wait until after parsing the function to set its parent scope,
7685 ;; since `js2-define-symbol' needs the defining-scope check to stop
7686 ;; at the function boundary when checking for redeclarations.
7687 (setf (js2-scope-parent-scope fn-node) js2-current-scope)
7688
7689 fn-node))
7690
7691 (defun js2-parse-statements (&optional parent)
7692 "Parse a statement list. Last token consumed must be js2-LC.
7693
7694 PARENT can be a `js2-block-node', in which case the statements are
7695 appended to PARENT. Otherwise a new `js2-block-node' is created
7696 and returned.
7697
7698 This function does not match the closing js2-RC: the caller
7699 matches the RC so it can provide a suitable error message if not
7700 matched. This means it's up to the caller to set the length of
7701 the node to include the closing RC. The node start pos is set to
7702 the absolute buffer start position, and the caller should fix it
7703 up to be relative to the parent node. All children of this block
7704 node are given relative start positions and correct lengths."
7705 (let ((pn (or parent (make-js2-block-node)))
7706 tt)
7707 (setf (js2-node-pos pn) js2-token-beg)
7708 (while (and (> (setq tt (js2-peek-token)) js2-EOF)
7709 (/= tt js2-RC))
7710 (js2-block-node-push pn (js2-parse-statement)))
7711 pn))
7712
7713 (defun js2-parse-statement ()
7714 (let (tt pn beg end)
7715
7716 ;; coarse-grained user-interrupt check - needs work
7717 (and js2-parse-interruptable-p
7718 (zerop (% (incf js2-parse-stmt-count)
7719 js2-statements-per-pause))
7720 (input-pending-p)
7721 (throw 'interrupted t))
7722
7723 (setq pn (js2-statement-helper))
7724
7725 ;; no-side-effects warning check
7726 (unless (js2-node-has-side-effects pn)
7727 (setq end (js2-node-end pn))
7728 (save-excursion
7729 (goto-char end)
7730 (setq beg (max (js2-node-pos pn) (point-at-bol))))
7731 (js2-add-strict-warning "msg.no.side.effects" nil beg end))
7732
7733 pn))
7734
7735 ;; These correspond to the switch cases in Parser.statementHelper
7736 (defconst js2-parsers
7737 (let ((parsers (make-vector js2-num-tokens
7738 #'js2-parse-expr-stmt)))
7739 (aset parsers js2-BREAK #'js2-parse-break)
7740 (aset parsers js2-CONST #'js2-parse-const-var)
7741 (aset parsers js2-CONTINUE #'js2-parse-continue)
7742 (aset parsers js2-DEBUGGER #'js2-parse-debugger)
7743 (aset parsers js2-DEFAULT #'js2-parse-default-xml-namespace)
7744 (aset parsers js2-DO #'js2-parse-do)
7745 (aset parsers js2-FOR #'js2-parse-for)
7746 (aset parsers js2-FUNCTION #'js2-function-parser)
7747 (aset parsers js2-IF #'js2-parse-if)
7748 (aset parsers js2-LC #'js2-parse-block)
7749 (aset parsers js2-LET #'js2-parse-let-stmt)
7750 (aset parsers js2-NAME #'js2-parse-name-or-label)
7751 (aset parsers js2-RETURN #'js2-parse-ret-yield)
7752 (aset parsers js2-SEMI #'js2-parse-semi)
7753 (aset parsers js2-SWITCH #'js2-parse-switch)
7754 (aset parsers js2-THROW #'js2-parse-throw)
7755 (aset parsers js2-TRY #'js2-parse-try)
7756 (aset parsers js2-VAR #'js2-parse-const-var)
7757 (aset parsers js2-WHILE #'js2-parse-while)
7758 (aset parsers js2-WITH #'js2-parse-with)
7759 (aset parsers js2-YIELD #'js2-parse-ret-yield)
7760 parsers)
7761 "A vector mapping token types to parser functions.")
7762
7763 (defsubst js2-parse-warn-missing-semi (beg end)
7764 (and js2-mode-show-strict-warnings
7765 js2-strict-missing-semi-warning
7766 (js2-add-strict-warning
7767 "msg.missing.semi" nil
7768 ;; back up to beginning of statement or line
7769 (max beg (save-excursion
7770 (goto-char end)
7771 (point-at-bol)))
7772 end)))
7773
7774 (defconst js2-no-semi-insertion
7775 (list js2-IF
7776 js2-SWITCH
7777 js2-WHILE
7778 js2-DO
7779 js2-FOR
7780 js2-TRY
7781 js2-WITH
7782 js2-LC
7783 js2-ERROR
7784 js2-SEMI
7785 js2-FUNCTION)
7786 "List of tokens that don't do automatic semicolon insertion.")
7787
7788 (defconst js2-autoinsert-semi-and-warn
7789 (list js2-ERROR js2-EOF js2-RC))
7790
7791 (defun js2-statement-helper ()
7792 (let* ((tt (js2-peek-token))
7793 (first-tt tt)
7794 (beg js2-token-beg)
7795 (parser (if (= tt js2-ERROR)
7796 #'js2-parse-semi
7797 (aref js2-parsers tt)))
7798 pn
7799 tt-flagged)
7800 ;; If the statement is set, then it's been told its label by now.
7801 (and js2-labeled-stmt
7802 (js2-labeled-stmt-node-stmt js2-labeled-stmt)
7803 (setq js2-labeled-stmt nil))
7804
7805 (setq pn (funcall parser)
7806 tt-flagged (js2-peek-flagged-token)
7807 tt (logand tt-flagged js2-clear-ti-mask))
7808
7809 ;; Don't do auto semi insertion for certain statement types.
7810 (unless (or (memq first-tt js2-no-semi-insertion)
7811 (js2-labeled-stmt-node-p pn))
7812 (cond
7813 ((= tt js2-SEMI)
7814 ;; Consume ';' as a part of expression
7815 (js2-consume-token)
7816 ;; extend the node bounds to include the semicolon.
7817 (setf (js2-node-len pn) (- js2-token-end beg)))
7818 ((memq tt js2-autoinsert-semi-and-warn)
7819 ;; Autoinsert ;
7820 (js2-parse-warn-missing-semi beg (js2-node-end pn)))
7821 (t
7822 (if (js2-flag-not-set-p tt-flagged js2-ti-after-eol)
7823 ;; Report error if no EOL or autoinsert ';' otherwise
7824 (js2-report-error "msg.no.semi.stmt")
7825 (js2-parse-warn-missing-semi beg (js2-node-end pn))))))
7826 pn))
7827
7828 (defun js2-parse-condition ()
7829 "Parse a parenthesized boolean expression, e.g. in an if- or while-stmt.
7830 The parens are discarded and the expression node is returned.
7831 The `pos' field of the return value is set to an absolute position
7832 that must be fixed up by the caller.
7833 Return value is a list (EXPR LP RP), with absolute paren positions."
7834 (let (pn lp rp)
7835 (if (js2-must-match js2-LP "msg.no.paren.cond")
7836 (setq lp js2-token-beg))
7837 (setq pn (js2-parse-expr))
7838 (if (js2-must-match js2-RP "msg.no.paren.after.cond")
7839 (setq rp js2-token-beg))
7840 ;; Report strict warning on code like "if (a = 7) ..."
7841 (if (and js2-strict-cond-assign-warning
7842 (js2-assign-node-p pn))
7843 (js2-add-strict-warning "msg.equal.as.assign" nil
7844 (js2-node-pos pn)
7845 (+ (js2-node-pos pn)
7846 (js2-node-len pn))))
7847 (list pn lp rp)))
7848
7849 (defun js2-parse-if ()
7850 "Parser for if-statement. Last matched token must be js2-IF."
7851 (let ((pos js2-token-beg)
7852 cond
7853 if-true
7854 if-false
7855 else-pos
7856 end
7857 pn)
7858 (js2-consume-token)
7859 (setq cond (js2-parse-condition)
7860 if-true (js2-parse-statement)
7861 if-false (if (js2-match-token js2-ELSE)
7862 (progn
7863 (setq else-pos (- js2-token-beg pos))
7864 (js2-parse-statement)))
7865 end (js2-node-end (or if-false if-true))
7866 pn (make-js2-if-node :pos pos
7867 :len (- end pos)
7868 :condition (car cond)
7869 :then-part if-true
7870 :else-part if-false
7871 :else-pos else-pos
7872 :lp (js2-relpos (second cond) pos)
7873 :rp (js2-relpos (third cond) pos)))
7874 (js2-node-add-children pn (car cond) if-true if-false)
7875 pn))
7876
7877 (defun js2-parse-switch ()
7878 "Parser for if-statement. Last matched token must be js2-SWITCH."
7879 (let ((pos js2-token-beg)
7880 tt
7881 pn
7882 discriminant
7883 has-default
7884 case-expr
7885 case-node
7886 case-pos
7887 cases
7888 stmt
7889 lp
7890 rp)
7891 (js2-consume-token)
7892 (if (js2-must-match js2-LP "msg.no.paren.switch")
7893 (setq lp js2-token-beg))
7894 (setq discriminant (js2-parse-expr)
7895 pn (make-js2-switch-node :discriminant discriminant
7896 :pos pos
7897 :lp (js2-relpos lp pos)))
7898 (js2-node-add-children pn discriminant)
7899 (js2-enter-switch pn)
7900 (unwind-protect
7901 (progn
7902 (if (js2-must-match js2-RP "msg.no.paren.after.switch")
7903 (setf (js2-switch-node-rp pn) (- js2-token-beg pos)))
7904 (js2-must-match js2-LC "msg.no.brace.switch")
7905 (catch 'break
7906 (while t
7907 (setq tt (js2-next-token)
7908 case-pos js2-token-beg)
7909 (cond
7910 ((= tt js2-RC)
7911 (setf (js2-node-len pn) (- js2-token-end pos))
7912 (throw 'break nil)) ; done
7913
7914 ((= tt js2-CASE)
7915 (setq case-expr (js2-parse-expr))
7916 (js2-must-match js2-COLON "msg.no.colon.case"))
7917
7918 ((= tt js2-DEFAULT)
7919 (if has-default
7920 (js2-report-error "msg.double.switch.default"))
7921 (setq has-default t
7922 case-expr nil)
7923 (js2-must-match js2-COLON "msg.no.colon.case"))
7924
7925 (t
7926 (js2-report-error "msg.bad.switch")
7927 (throw 'break nil)))
7928
7929 (setq case-node (make-js2-case-node :pos case-pos
7930 :len (- js2-token-end case-pos)
7931 :expr case-expr))
7932 (js2-node-add-children case-node case-expr)
7933 (while (and (/= (setq tt (js2-peek-token)) js2-RC)
7934 (/= tt js2-CASE)
7935 (/= tt js2-DEFAULT)
7936 (/= tt js2-EOF))
7937 (setf stmt (js2-parse-statement)
7938 (js2-node-len case-node) (- (js2-node-end stmt) case-pos))
7939 (js2-block-node-push case-node stmt))
7940 (push case-node cases)))
7941 ;; add cases last, as pushing reverses the order to be correct
7942 (dolist (kid cases)
7943 (js2-node-add-children pn kid)
7944 (push kid (js2-switch-node-cases pn)))
7945 pn) ; return value
7946 (js2-exit-switch))))
7947
7948 (defun js2-parse-while ()
7949 "Parser for while-statement. Last matched token must be js2-WHILE."
7950 (let ((pos js2-token-beg)
7951 (pn (make-js2-while-node))
7952 cond
7953 body)
7954 (js2-consume-token)
7955 (js2-enter-loop pn)
7956 (unwind-protect
7957 (progn
7958 (setf cond (js2-parse-condition)
7959 (js2-while-node-condition pn) (car cond)
7960 body (js2-parse-statement)
7961 (js2-while-node-body pn) body
7962 (js2-node-len pn) (- (js2-node-end body) pos)
7963 (js2-while-node-lp pn) (js2-relpos (second cond) pos)
7964 (js2-while-node-rp pn) (js2-relpos (third cond) pos))
7965 (js2-node-add-children pn body (car cond)))
7966 (js2-exit-loop))
7967 pn))
7968
7969 (defun js2-parse-do ()
7970 "Parser for do-statement. Last matched token must be js2-DO."
7971 (let ((pos js2-token-beg)
7972 (pn (make-js2-do-node))
7973 cond
7974 body
7975 end)
7976 (js2-consume-token)
7977 (js2-enter-loop pn)
7978 (unwind-protect
7979 (progn
7980 (setq body (js2-parse-statement))
7981 (js2-must-match js2-WHILE "msg.no.while.do")
7982 (setf (js2-do-node-while-pos pn) (- js2-token-beg pos)
7983 cond (js2-parse-condition)
7984 (js2-do-node-condition pn) (car cond)
7985 (js2-do-node-body pn) body
7986 end js2-ts-cursor
7987 (js2-do-node-lp pn) (js2-relpos (second cond) pos)
7988 (js2-do-node-rp pn) (js2-relpos (third cond) pos))
7989 (js2-node-add-children pn (car cond) body))
7990 (js2-exit-loop))
7991 ;; Always auto-insert semicolon to follow SpiderMonkey:
7992 ;; It is required by ECMAScript but is ignored by the rest of
7993 ;; world; see bug 238945
7994 (if (js2-match-token js2-SEMI)
7995 (setq end js2-ts-cursor))
7996 (setf (js2-node-len pn) (- end pos))
7997 pn))
7998
7999 (defun js2-parse-for ()
8000 "Parser for for-statement. Last matched token must be js2-FOR.
8001 Parses for, for-in, and for each-in statements."
8002 (let ((for-pos js2-token-beg)
8003 pn
8004 is-for-each
8005 is-for-in
8006 in-pos
8007 each-pos
8008 tmp-pos
8009 init ; Node init is also foo in 'foo in object'
8010 cond ; Node cond is also object in 'foo in object'
8011 incr ; 3rd section of for-loop initializer
8012 body
8013 tt
8014 lp
8015 rp)
8016 (js2-consume-token)
8017 ;; See if this is a for each () instead of just a for ()
8018 (when (js2-match-token js2-NAME)
8019 (if (string= "each" js2-ts-string)
8020 (progn
8021 (setq is-for-each t
8022 each-pos (- js2-token-beg for-pos)) ; relative
8023 (js2-record-face 'font-lock-keyword-face))
8024 (js2-report-error "msg.no.paren.for")))
8025
8026 (if (js2-must-match js2-LP "msg.no.paren.for")
8027 (setq lp (- js2-token-beg for-pos)))
8028 (setq tt (js2-peek-token))
8029
8030 ;; parse init clause
8031 (let ((js2-in-for-init t)) ; set as dynamic variable
8032 (cond
8033 ((= tt js2-SEMI)
8034 (setq init (make-js2-empty-expr-node)))
8035 ((or (= tt js2-VAR) (= tt js2-LET))
8036 (js2-consume-token)
8037 (setq init (js2-parse-variables tt js2-token-beg)))
8038 (t
8039 (setq init (js2-parse-expr)))))
8040
8041 (if (js2-match-token js2-IN)
8042 (setq is-for-in t
8043 in-pos (- js2-token-beg for-pos)
8044 cond (js2-parse-expr)) ; object over which we're iterating
8045 ;; else ordinary for loop - parse cond and incr
8046 (js2-must-match js2-SEMI "msg.no.semi.for")
8047 (setq cond (if (= (js2-peek-token) js2-SEMI)
8048 (make-js2-empty-expr-node) ; no loop condition
8049 (js2-parse-expr)))
8050 (js2-must-match js2-SEMI "msg.no.semi.for.cond")
8051 (setq tmp-pos js2-token-end
8052 incr (if (= (js2-peek-token) js2-RP)
8053 (make-js2-empty-expr-node :pos tmp-pos)
8054 (js2-parse-expr))))
8055
8056 (if (js2-must-match js2-RP "msg.no.paren.for.ctrl")
8057 (setq rp (- js2-token-beg for-pos)))
8058 (if (not is-for-in)
8059 (setq pn (make-js2-for-node :init init
8060 :condition cond
8061 :update incr
8062 :lp lp
8063 :rp rp))
8064 ;; cond could be null if 'in obj' got eaten by the init node.
8065 (if (js2-infix-node-p init)
8066 ;; it was (foo in bar) instead of (var foo in bar)
8067 (setq cond (js2-infix-node-right init)
8068 init (js2-infix-node-left init))
8069 (if (and (js2-var-decl-node-p init)
8070 (> (length (js2-var-decl-node-kids init)) 1))
8071 (js2-report-error "msg.mult.index")))
8072
8073 (setq pn (make-js2-for-in-node :iterator init
8074 :object cond
8075 :in-pos in-pos
8076 :foreach-p is-for-each
8077 :each-pos each-pos
8078 :lp lp
8079 :rp rp)))
8080 (unwind-protect
8081 (progn
8082 (js2-enter-loop pn)
8083 ;; We have to parse the body -after- creating the loop node,
8084 ;; so that the loop node appears in the js2-loop-set, allowing
8085 ;; break/continue statements to find the enclosing loop.
8086 (setf body (js2-parse-statement)
8087 (js2-loop-node-body pn) body
8088 (js2-node-pos pn) for-pos
8089 (js2-node-len pn) (- (js2-node-end body) for-pos))
8090 (js2-node-add-children pn init cond incr body))
8091 ;; finally
8092 (js2-exit-loop))
8093 pn))
8094
8095 (defun js2-parse-try ()
8096 "Parser for try-statement. Last matched token must be js2-TRY."
8097 (let ((try-pos js2-token-beg)
8098 try-end
8099 try-block
8100 catch-blocks
8101 finally-block
8102 saw-default-catch
8103 peek
8104 var-name
8105 catch-cond
8106 catch-node
8107 guard-kwd
8108 catch-pos
8109 finally-pos
8110 pn
8111 block
8112 lp
8113 rp)
8114 (js2-consume-token)
8115 (if (/= (js2-peek-token) js2-LC)
8116 (js2-report-error "msg.no.brace.try"))
8117 (setq try-block (js2-parse-statement)
8118 try-end (js2-node-end try-block)
8119 peek (js2-peek-token))
8120 (cond
8121 ((= peek js2-CATCH)
8122 (while (js2-match-token js2-CATCH)
8123 (setq catch-pos js2-token-beg
8124 guard-kwd nil
8125 catch-cond nil
8126 lp nil
8127 rp nil)
8128 (if saw-default-catch
8129 (js2-report-error "msg.catch.unreachable"))
8130 (if (js2-must-match js2-LP "msg.no.paren.catch")
8131 (setq lp (- js2-token-beg catch-pos)))
8132
8133 (js2-must-match js2-NAME "msg.bad.catchcond")
8134 (setq var-name (js2-create-name-node))
8135
8136 (if (js2-match-token js2-IF)
8137 (setq guard-kwd (- js2-token-beg catch-pos)
8138 catch-cond (js2-parse-expr))
8139 (setq saw-default-catch t))
8140
8141 (if (js2-must-match js2-RP "msg.bad.catchcond")
8142 (setq rp (- js2-token-beg catch-pos)))
8143 (js2-must-match js2-LC "msg.no.brace.catchblock")
8144
8145 (setq block (js2-parse-statements)
8146 try-end (js2-node-end block)
8147 catch-node (make-js2-catch-node :pos catch-pos
8148 :var-name var-name
8149 :guard-expr catch-cond
8150 :guard-kwd guard-kwd
8151 :block block
8152 :lp lp
8153 :rp rp))
8154 (if (js2-must-match js2-RC "msg.no.brace.after.body")
8155 (setq try-end js2-token-beg))
8156 (setf (js2-node-len block) (- try-end (js2-node-pos block))
8157 (js2-node-len catch-node) (- try-end catch-pos))
8158 (js2-node-add-children catch-node var-name catch-cond block)
8159 (push catch-node catch-blocks)))
8160
8161 ((/= peek js2-FINALLY)
8162 (js2-must-match js2-FINALLY "msg.try.no.catchfinally"
8163 (js2-node-pos try-block)
8164 (- (setq try-end (js2-node-end try-block))
8165 (js2-node-pos try-block)))))
8166
8167 (when (js2-match-token js2-FINALLY)
8168 (setq finally-pos js2-token-beg
8169 block (js2-parse-statement)
8170 try-end (js2-node-end block)
8171 finally-block (make-js2-finally-node :pos finally-pos
8172 :len (- try-end finally-pos)
8173 :body block))
8174 (js2-node-add-children finally-block block))
8175
8176 (setq pn (make-js2-try-node :pos try-pos
8177 :len (- try-end try-pos)
8178 :try-block try-block
8179 :finally-block finally-block))
8180 (js2-node-add-children pn try-block finally-block)
8181
8182 ;; push them onto the try-node, which reverses and corrects their order
8183 (dolist (cb catch-blocks)
8184 (js2-node-add-children pn cb)
8185 (push cb (js2-try-node-catch-clauses pn)))
8186 pn))
8187
8188 (defun js2-parse-throw ()
8189 "Parser for throw-statement. Last matched token must be js2-THROW."
8190 (let ((pos js2-token-beg)
8191 expr
8192 pn)
8193 (js2-consume-token)
8194 (if (= (js2-peek-token-or-eol) js2-EOL)
8195 ;; ECMAScript does not allow new lines before throw expression,
8196 ;; see bug 256617
8197 (js2-report-error "msg.bad.throw.eol"))
8198 (setq expr (js2-parse-expr)
8199 pn (make-js2-throw-node :pos pos
8200 :len (- (js2-node-end expr) pos)
8201 :expr expr))
8202 (js2-node-add-children pn expr)
8203 pn))
8204
8205 (defsubst js2-match-jump-label-name (label-name)
8206 "If break/continue specified a label, return that label's labeled stmt.
8207 Returns the corresponding `js2-labeled-stmt-node', or if LABEL-NAME
8208 does not match an existing label, reports an error and returns nil."
8209 (let ((bundle (cdr (assoc label-name js2-label-set))))
8210 (if (null bundle)
8211 (js2-report-error "msg.undef.label"))
8212 bundle))
8213
8214 (defun js2-parse-break ()
8215 "Parser for break-statement. Last matched token must be js2-BREAK."
8216 (let ((pos js2-token-beg)
8217 (end js2-token-end)
8218 break-target ; statement to break from
8219 break-label ; in "break foo", name-node representing the foo
8220 labels ; matching labeled statement to break to
8221 pn)
8222 (js2-consume-token) ; `break'
8223 (when (eq (js2-peek-token-or-eol) js2-NAME)
8224 (js2-consume-token)
8225 (setq break-label (js2-create-name-node)
8226 end (js2-node-end break-label)
8227 ;; matchJumpLabelName only matches if there is one
8228 labels (js2-match-jump-label-name js2-ts-string)
8229 break-target (if labels (car (js2-labeled-stmt-node-labels labels)))))
8230
8231 (unless (or break-target break-label)
8232 ;; no break target specified - try for innermost enclosing loop/switch
8233 (if (null js2-loop-and-switch-set)
8234 (unless break-label
8235 (js2-report-error "msg.bad.break" nil pos (length "break")))
8236 (setq break-target (car js2-loop-and-switch-set))))
8237
8238 (setq pn (make-js2-break-node :pos pos
8239 :len (- end pos)
8240 :label break-label
8241 :target break-target))
8242 (js2-node-add-children pn break-label) ; but not break-target
8243 pn))
8244
8245 (defun js2-parse-continue ()
8246 "Parser for continue-statement. Last matched token must be js2-CONTINUE."
8247 (let ((pos js2-token-beg)
8248 (end js2-token-end)
8249 label ; optional user-specified label, a `js2-name-node'
8250 labels ; current matching labeled stmt, if any
8251 target ; the `js2-loop-node' target of this continue stmt
8252 pn)
8253 (js2-consume-token) ; `continue'
8254 (when (= (js2-peek-token-or-eol) js2-NAME)
8255 (js2-consume-token)
8256 (setq label (js2-create-name-node)
8257 end (js2-node-end label)
8258 ;; matchJumpLabelName only matches if there is one
8259 labels (js2-match-jump-label-name js2-ts-string)))
8260 (cond
8261 ((null labels) ; no current label to go to
8262 (if (null js2-loop-set) ; no loop to continue to
8263 (js2-report-error "msg.continue.outside" nil pos
8264 (length "continue"))
8265 (setq target (car js2-loop-set)))) ; innermost enclosing loop
8266 (t
8267 (if (js2-loop-node-p (js2-labeled-stmt-node-stmt labels))
8268 (setq target (js2-labeled-stmt-node-stmt labels))
8269 (js2-report-error "msg.continue.nonloop" nil pos (- end pos)))))
8270
8271 (setq pn (make-js2-continue-node :pos pos
8272 :len (- end pos)
8273 :label label
8274 :target target))
8275 (js2-node-add-children pn label) ; but not target - it's not our child
8276 pn))
8277
8278 (defun js2-parse-with ()
8279 "Parser for with-statement. Last matched token must be js2-WITH."
8280 (js2-consume-token)
8281 (let ((pos js2-token-beg)
8282 obj body pn lp rp)
8283
8284 (if (js2-must-match js2-LP "msg.no.paren.with")
8285 (setq lp js2-token-beg))
8286
8287 (setq obj (js2-parse-expr))
8288
8289 (if (js2-must-match js2-RP "msg.no.paren.after.with")
8290 (setq rp js2-token-beg))
8291
8292 (let ((js2-nesting-of-with (1+ js2-nesting-of-with)))
8293 (setq body (js2-parse-statement)))
8294
8295 (setq pn (make-js2-with-node :pos pos
8296 :len (- (js2-node-end body) pos)
8297 :object obj
8298 :body body
8299 :lp (js2-relpos lp pos)
8300 :rp (js2-relpos rp pos)))
8301 (js2-node-add-children pn obj body)
8302 pn))
8303
8304 (defun js2-parse-const-var ()
8305 "Parser for var- or const-statement.
8306 Last matched token must be js2-CONST or js2-VAR."
8307 (let ((tt (js2-peek-token))
8308 (pos js2-token-beg)
8309 expr
8310 pn)
8311 (js2-consume-token)
8312 (setq expr (js2-parse-variables tt js2-token-beg)
8313 pn (make-js2-expr-stmt-node :pos pos
8314 :len (- (js2-node-end expr) pos)
8315 :expr expr))
8316 (js2-node-add-children pn expr)
8317 pn))
8318
8319 (defsubst js2-wrap-with-expr-stmt (pos expr &optional add-child)
8320 (let ((pn (make-js2-expr-stmt-node :pos pos
8321 :len (js2-node-len expr)
8322 :type (if (js2-inside-function)
8323 js2-EXPR_VOID
8324 js2-EXPR_RESULT)
8325 :expr expr)))
8326 (if add-child
8327 (js2-node-add-children pn expr))
8328 pn))
8329
8330 (defun js2-parse-let-stmt ()
8331 "Parser for let-statement. Last matched token must be js2-LET."
8332 (js2-consume-token)
8333 (let ((pos js2-token-beg)
8334 expr
8335 pn)
8336 (if (= (js2-peek-token) js2-LP)
8337 ;; let expression in statement context
8338 (setq expr (js2-parse-let pos 'statement)
8339 pn (js2-wrap-with-expr-stmt pos expr t))
8340 ;; else we're looking at a statement like let x=6, y=7;
8341 (setf expr (js2-parse-variables js2-LET pos)
8342 pn (js2-wrap-with-expr-stmt pos expr t)
8343 (js2-node-type pn) js2-EXPR_RESULT))
8344 pn))
8345
8346 (defun js2-parse-ret-yield ()
8347 (js2-parse-return-or-yield (js2-peek-token) nil))
8348
8349 (defconst js2-parse-return-stmt-enders
8350 (list js2-SEMI js2-RC js2-EOF js2-EOL js2-ERROR js2-RB js2-RP js2-YIELD))
8351
8352 (defsubst js2-now-all-set (before after mask)
8353 "Return whether or not the bits in the mask have changed to all set.
8354 BEFORE is bits before change, AFTER is bits after change, and MASK is
8355 the mask for bits. Returns t if all the bits in the mask are set in AFTER
8356 but not BEFORE."
8357 (and (/= (logand before mask) mask)
8358 (= (logand after mask) mask)))
8359
8360 (defun js2-parse-return-or-yield (tt expr-context)
8361 (let ((pos js2-token-beg)
8362 (end js2-token-end)
8363 (before js2-end-flags)
8364 (inside-function (js2-inside-function))
8365 e
8366 ret
8367 name)
8368 (unless inside-function
8369 (js2-report-error (if (eq tt js2-RETURN)
8370 "msg.bad.return"
8371 "msg.bad.yield")))
8372 (js2-consume-token)
8373 ;; This is ugly, but we don't want to require a semicolon.
8374 (unless (memq (js2-peek-token-or-eol) js2-parse-return-stmt-enders)
8375 (setq e (js2-parse-expr)
8376 end (js2-node-end e)))
8377 (cond
8378 ((eq tt js2-RETURN)
8379 (js2-set-flag js2-end-flags (if (null e)
8380 js2-end-returns
8381 js2-end-returns-value))
8382 (setq ret (make-js2-return-node :pos pos
8383 :len (- end pos)
8384 :retval e))
8385 (js2-node-add-children ret e)
8386 ;; See if we need a strict mode warning.
8387 ;; TODO: The analysis done by `js2-has-consistent-return-usage' is
8388 ;; more thorough and accurate than this before/after flag check.
8389 ;; E.g. if there's a finally-block that always returns, we shouldn't
8390 ;; show a warning generated by inconsistent returns in the catch blocks.
8391 ;; Basically `js2-has-consistent-return-usage' needs to keep more state,
8392 ;; so we know which returns/yields to highlight, and we should get rid of
8393 ;; all the checking in `js2-parse-return-or-yield'.
8394 (if (and js2-strict-inconsistent-return-warning
8395 (js2-now-all-set before js2-end-flags
8396 (logior js2-end-returns js2-end-returns-value)))
8397 (js2-add-strict-warning "msg.return.inconsistent" nil pos end)))
8398 (t
8399 (unless (js2-inside-function)
8400 (js2-report-error "msg.bad.yield"))
8401 (js2-set-flag js2-end-flags js2-end-yields)
8402 (setq ret (make-js2-yield-node :pos pos
8403 :len (- end pos)
8404 :value e))
8405 (js2-node-add-children ret e)
8406 (unless expr-context
8407 (setq e ret
8408 ret (js2-wrap-with-expr-stmt pos e t))
8409 (js2-set-requires-activation)
8410 (js2-set-is-generator))))
8411
8412 ;; see if we are mixing yields and value returns.
8413 (when (and inside-function
8414 (js2-now-all-set before js2-end-flags
8415 (logior js2-end-yields js2-end-returns-value)))
8416 (setq name (js2-function-name js2-current-script-or-fn))
8417 (if (zerop (length name))
8418 (js2-report-error "msg.anon.generator.returns" nil pos (- end pos))
8419 (js2-report-error "msg.generator.returns" name pos (- end pos))))
8420
8421 ret))
8422
8423 (defun js2-parse-debugger ()
8424 (js2-consume-token)
8425 (make-js2-keyword-node :type js2-DEBUGGER))
8426
8427 (defun js2-parse-block ()
8428 "Parser for a curly-delimited statement block.
8429 Last token matched must be js2-LC."
8430 (let ((pos js2-token-beg)
8431 (pn (make-js2-scope)))
8432 (js2-consume-token)
8433 (js2-push-scope pn)
8434 (unwind-protect
8435 (progn
8436 (js2-parse-statements pn)
8437 (js2-must-match js2-RC "msg.no.brace.block")
8438 (setf (js2-node-len pn) (- js2-token-end pos)))
8439 (js2-pop-scope))
8440 pn))
8441
8442 ;; for js2-ERROR too, to have a node for error recovery to work on
8443 (defun js2-parse-semi ()
8444 "Parse a statement or handle an error.
8445 Last matched token is js-SEMI or js-ERROR."
8446 (let ((tt (js2-peek-token)) pos len)
8447 (js2-consume-token)
8448 (if (eq tt js2-SEMI)
8449 (make-js2-empty-expr-node :len 1)
8450 (setq pos js2-token-beg
8451 len (- js2-token-beg pos))
8452 (js2-report-error "msg.syntax" nil pos len)
8453 (make-js2-error-node :pos pos :len len))))
8454
8455 (defun js2-parse-default-xml-namespace ()
8456 "Parse a `default xml namespace = <expr>' e4x statement."
8457 (let ((pos js2-token-beg)
8458 end len expr unary es)
8459 (js2-consume-token)
8460 (js2-must-have-xml)
8461 (js2-set-requires-activation)
8462 (setq len (- js2-ts-cursor pos))
8463 (unless (and (js2-match-token js2-NAME)
8464 (string= js2-ts-string "xml"))
8465 (js2-report-error "msg.bad.namespace" nil pos len))
8466 (unless (and (js2-match-token js2-NAME)
8467 (string= js2-ts-string "namespace"))
8468 (js2-report-error "msg.bad.namespace" nil pos len))
8469 (unless (js2-match-token js2-ASSIGN)
8470 (js2-report-error "msg.bad.namespace" nil pos len))
8471 (setq expr (js2-parse-expr)
8472 end (js2-node-end expr)
8473 unary (make-js2-unary-node :type js2-DEFAULTNAMESPACE
8474 :pos pos
8475 :len (- end pos)
8476 :operand expr))
8477 (js2-node-add-children unary expr)
8478 (make-js2-expr-stmt-node :pos pos
8479 :len (- end pos)
8480 :expr unary)))
8481
8482 (defun js2-record-label (label bundle)
8483 ;; current token should be colon that `js2-parse-primary-expr' left untouched
8484 (js2-consume-token)
8485 (let ((name (js2-label-node-name label))
8486 labeled-stmt
8487 dup)
8488 (when (setq labeled-stmt (cdr (assoc name js2-label-set)))
8489 ;; flag both labels if possible when used in editing mode
8490 (if (and js2-parse-ide-mode
8491 (setq dup (js2-get-label-by-name labeled-stmt name)))
8492 (js2-report-error "msg.dup.label" nil
8493 (js2-node-abs-pos dup) (js2-node-len dup)))
8494 (js2-report-error "msg.dup.label" nil
8495 (js2-node-pos label) (js2-node-len label)))
8496 (js2-labeled-stmt-node-add-label bundle label)
8497 (js2-node-add-children bundle label)
8498 ;; Add one reference to the bundle per label in `js2-label-set'
8499 (push (cons name bundle) js2-label-set)))
8500
8501 (defun js2-parse-name-or-label ()
8502 "Parser for identifier or label. Last token matched must be js2-NAME.
8503 Called when we found a name in a statement context. If it's a label, we gather
8504 up any following labels and the next non-label statement into a
8505 `js2-labeled-stmt-node' bundle and return that. Otherwise we parse an
8506 expression and return it wrapped in a `js2-expr-stmt-node'."
8507 (let ((pos js2-token-beg)
8508 (end js2-token-end)
8509 expr
8510 stmt
8511 pn
8512 bundle
8513 (continue t))
8514 ;; set check for label and call down to `js2-parse-primary-expr'
8515 (js2-set-check-for-label)
8516 (setq expr (js2-parse-expr))
8517
8518 (if (/= (js2-node-type expr) js2-LABEL)
8519 ;; Parsed non-label expression - wrap with expression stmt.
8520 (setq pn (js2-wrap-with-expr-stmt pos expr t))
8521
8522 ;; else parsed a label
8523 (setq bundle (make-js2-labeled-stmt-node :pos pos))
8524 (js2-record-label expr bundle)
8525
8526 ;; look for more labels
8527 (while (and continue (= (js2-peek-token) js2-NAME))
8528 (js2-set-check-for-label)
8529 (setq expr (js2-parse-expr))
8530 (if (/= (js2-node-type expr) js2-LABEL)
8531 (setq stmt (js2-wrap-with-expr-stmt pos expr t)
8532 continue nil)
8533 (js2-record-label expr bundle)))
8534
8535 ;; no more labels; now parse the labeled statement
8536 (unwind-protect
8537 (unless stmt
8538 (let ((js2-labeled-stmt bundle)) ; bind dynamically
8539 (setq stmt (js2-statement-helper))))
8540 ;; remove the labels for this statement from the global set
8541 (dolist (label (js2-labeled-stmt-node-labels bundle))
8542 (setq js2-label-set (remove label js2-label-set))))
8543
8544 (setf (js2-labeled-stmt-node-stmt bundle) stmt)
8545 (js2-node-add-children bundle stmt)
8546 bundle)))
8547
8548 (defun js2-parse-expr-stmt ()
8549 "Default parser in statement context, if no recognized statement found."
8550 (js2-wrap-with-expr-stmt js2-token-beg (js2-parse-expr) t))
8551
8552 (defun js2-parse-variables (decl-type pos)
8553 "Parse a comma-separated list of variable declarations.
8554 Could be a 'var', 'const' or 'let' expression, possibly in a for-loop initializer.
8555
8556 DECL-TYPE is a token value: either VAR, CONST, or LET depending on context.
8557 For 'var' or 'const', the keyword should be the token last scanned.
8558
8559 POS is the position where the node should start. It's sometimes the
8560 var/const/let keyword, and other times the beginning of the first token
8561 in the first variable declaration.
8562
8563 Returns the parsed `js2-var-decl-node' expression node."
8564 (let* ((result (make-js2-var-decl-node :decl-type decl-type
8565 :pos pos))
8566 destructuring
8567 kid-pos
8568 tt
8569 init
8570 name
8571 end
8572 nbeg nend
8573 vi
8574 (continue t))
8575 ;; Example:
8576 ;; var foo = {a: 1, b: 2}, bar = [3, 4];
8577 ;; var {b: s2, a: s1} = foo, x = 6, y, [s3, s4] = bar;
8578 (while continue
8579 (setq destructuring nil
8580 name nil
8581 tt (js2-peek-token)
8582 kid-pos js2-token-beg
8583 end js2-token-end
8584 init nil)
8585 (if (or (= tt js2-LB) (= tt js2-LC))
8586 ;; Destructuring assignment, e.g., var [a, b] = ...
8587 (setq destructuring (js2-parse-primary-expr)
8588 end (js2-node-end destructuring))
8589 ;; Simple variable name
8590 (when (js2-must-match js2-NAME "msg.bad.var")
8591 (setq name (js2-create-name-node)
8592 nbeg js2-token-beg
8593 nend js2-token-end
8594 end nend)
8595 (js2-define-symbol decl-type js2-ts-string name js2-in-for-init)))
8596
8597 (when (js2-match-token js2-ASSIGN)
8598 (setq init (js2-parse-assign-expr)
8599 end (js2-node-end init))
8600 (if (and js2-parse-ide-mode
8601 (or (js2-object-node-p init)
8602 (js2-function-node-p init)))
8603 (js2-record-imenu-functions init name)))
8604
8605 (when name
8606 (js2-set-face nbeg nend (if (js2-function-node-p init)
8607 'font-lock-function-name-face
8608 'font-lock-variable-name-face)
8609 'record))
8610
8611 (setq vi (make-js2-var-init-node :pos kid-pos
8612 :len (- end kid-pos)
8613 :type decl-type))
8614 (if destructuring
8615 (progn
8616 (if (and (null init) (not js2-in-for-init))
8617 (js2-report-error "msg.destruct.assign.no.init"))
8618 (setf (js2-var-init-node-target vi) destructuring))
8619 (setf (js2-var-init-node-target vi) name))
8620 (setf (js2-var-init-node-initializer vi) init)
8621 (js2-node-add-children vi name destructuring init)
8622
8623 (js2-block-node-push result vi)
8624 (unless (js2-match-token js2-COMMA)
8625 (setq continue nil)))
8626
8627 (setf (js2-node-len result) (- end pos))
8628 result))
8629
8630 (defun js2-parse-let (pos &optional stmt-p)
8631 "Parse a let expression or statement.
8632 A let-expression is of the form `let (vars) expr'.
8633 A let-statment is of the form `let (vars) {statements}'.
8634 The third form of let is a variable declaration list, handled
8635 by `js2-parse-variables'."
8636 (let ((pn (make-js2-let-node :pos pos))
8637 beg vars body)
8638 (if (js2-must-match js2-LP "msg.no.paren.after.let")
8639 (setf (js2-let-node-lp pn) (- js2-token-beg pos)))
8640 (js2-push-scope pn)
8641 (unwind-protect
8642 (progn
8643 (setq vars (js2-parse-variables js2-LET js2-token-beg))
8644 (if (js2-must-match js2-RP "msg.no.paren.let")
8645 (setf (js2-let-node-rp pn) (- js2-token-beg pos)))
8646 (if (and stmt-p (eq (js2-peek-token) js2-LC))
8647 ;; let statement
8648 (progn
8649 (js2-consume-token)
8650 (setf beg js2-token-beg ; position stmt at LC
8651 body (js2-parse-statements))
8652 (js2-must-match js2-RC "msg.no.curly.let")
8653 (setf (js2-node-len body) (- js2-token-end beg)
8654 (js2-node-len pn) (- js2-token-end pos)
8655 (js2-let-node-body pn) body
8656 (js2-node-type pn) js2-LET))
8657 ;; let expression
8658 (setf body (js2-parse-expr)
8659 (js2-node-len pn) (- (js2-node-end body) pos)
8660 (js2-let-node-body pn) body))
8661 (js2-node-add-children pn vars body))
8662 (js2-pop-scope))
8663 pn))
8664
8665 (defsubst js2-define-new-symbol (decl-type name node)
8666 (js2-scope-put-symbol js2-current-scope
8667 name
8668 (make-js2-symbol decl-type name node)))
8669
8670 (defun js2-define-symbol (decl-type name &optional node ignore-not-in-block)
8671 "Define a symbol in the current scope.
8672 If NODE is non-nil, it is the AST node associated with the symbol."
8673 (let* ((defining-scope (js2-get-defining-scope js2-current-scope name))
8674 (symbol (if defining-scope
8675 (js2-scope-get-symbol defining-scope name)))
8676 (sdt (if symbol (js2-symbol-decl-type symbol) -1)))
8677 (cond
8678 ((and symbol ; already defined
8679 (or (= sdt js2-CONST) ; old version is const
8680 (= decl-type js2-CONST) ; new version is const
8681 ;; two let-bound vars in this block have same name
8682 (and (= sdt js2-LET)
8683 (eq defining-scope js2-current-scope))))
8684 (js2-report-error
8685 (cond
8686 ((= sdt js2-CONST) "msg.const.redecl")
8687 ((= sdt js2-LET) "msg.let.redecl")
8688 ((= sdt js2-VAR) "msg.var.redecl")
8689 ((= sdt js2-FUNCTION) "msg.function.redecl")
8690 (t "msg.parm.redecl"))
8691 name))
8692
8693 ((= decl-type js2-LET)
8694 (if (and (not ignore-not-in-block)
8695 (or (= (js2-node-type js2-current-scope) js2-IF)
8696 (js2-loop-node-p js2-current-scope)))
8697 (js2-report-error "msg.let.decl.not.in.block")
8698 (js2-define-new-symbol decl-type name node)))
8699
8700 ((or (= decl-type js2-VAR)
8701 (= decl-type js2-CONST)
8702 (= decl-type js2-FUNCTION))
8703 (if symbol
8704 (if (and js2-strict-var-redeclaration-warning (= sdt js2-VAR))
8705 (js2-add-strict-warning "msg.var.redecl" name)
8706 (if (and js2-strict-var-hides-function-arg-warning (= sdt js2-LP))
8707 (js2-add-strict-warning "msg.var.hides.arg" name)))
8708 (js2-define-new-symbol decl-type name node)))
8709
8710 ((= decl-type js2-LP)
8711 (if symbol
8712 ;; must be duplicate parameter. Second parameter hides the
8713 ;; first, so go ahead and add the second pararameter
8714 (js2-report-warning "msg.dup.parms" name))
8715 (js2-define-new-symbol decl-type name node))
8716
8717 (t (js2-code-bug)))))
8718
8719 (defun js2-parse-expr ()
8720 (let* ((pn (js2-parse-assign-expr))
8721 (pos (js2-node-pos pn))
8722 left
8723 right
8724 op-pos)
8725 (while (js2-match-token js2-COMMA)
8726 (setq op-pos (- js2-token-beg pos)) ; relative
8727 (if (= (js2-peek-token) js2-YIELD)
8728 (js2-report-error "msg.yield.parenthesized"))
8729 (setq right (js2-parse-assign-expr)
8730 left pn
8731 pn (make-js2-infix-node :type js2-COMMA
8732 :pos pos
8733 :len (- js2-ts-cursor pos)
8734 :op-pos op-pos
8735 :left left
8736 :right right))
8737 (js2-node-add-children pn left right))
8738 pn))
8739
8740 (defun js2-parse-assign-expr ()
8741 (let ((tt (js2-peek-token))
8742 (pos js2-token-beg)
8743 pn
8744 left
8745 right
8746 op-pos)
8747 (if (= tt js2-YIELD)
8748 (js2-parse-return-or-yield tt t)
8749 ;; not yield - parse assignment expression
8750 (setq pn (js2-parse-cond-expr)
8751 tt (js2-peek-token))
8752 (when (and (<= js2-first-assign tt)
8753 (<= tt js2-last-assign))
8754 (js2-consume-token)
8755 (setq op-pos (- js2-token-beg pos) ; relative
8756 left pn
8757 right (js2-parse-assign-expr)
8758 pn (make-js2-assign-node :type tt
8759 :pos pos
8760 :len (- (js2-node-end right) pos)
8761 :op-pos op-pos
8762 :left left
8763 :right right))
8764 (when js2-parse-ide-mode
8765 (js2-highlight-assign-targets pn left right)
8766 (if (or (js2-function-node-p right)
8767 (js2-object-node-p right))
8768 (js2-record-imenu-functions right left)))
8769 ;; do this last so ide checks above can use absolute positions
8770 (js2-node-add-children pn left right))
8771 pn)))
8772
8773 (defun js2-parse-cond-expr ()
8774 (let ((pos js2-token-beg)
8775 (pn (js2-parse-or-expr))
8776 test-expr
8777 if-true
8778 if-false
8779 q-pos
8780 c-pos)
8781 (when (js2-match-token js2-HOOK)
8782 (setq q-pos (- js2-token-beg pos)
8783 if-true (js2-parse-assign-expr))
8784 (js2-must-match js2-COLON "msg.no.colon.cond")
8785 (setq c-pos (- js2-token-beg pos)
8786 if-false (js2-parse-assign-expr)
8787 test-expr pn
8788 pn (make-js2-cond-node :pos pos
8789 :len (- (js2-node-end if-false) pos)
8790 :test-expr test-expr
8791 :true-expr if-true
8792 :false-expr if-false
8793 :q-pos q-pos
8794 :c-pos c-pos))
8795 (js2-node-add-children pn test-expr if-true if-false))
8796 pn))
8797
8798 (defun js2-make-binary (type left parser)
8799 "Helper for constructing a binary-operator AST node.
8800 LEFT is the left-side-expression, already parsed, and the
8801 binary operator should have just been matched.
8802 PARSER is a function to call to parse the right operand,
8803 or a `js2-node' struct if it has already been parsed."
8804 (let* ((pos (js2-node-pos left))
8805 (op-pos (- js2-token-beg pos))
8806 (right (if (js2-node-p parser)
8807 parser
8808 (funcall parser)))
8809 (pn (make-js2-infix-node :type type
8810 :pos pos
8811 :len (- (js2-node-end right) pos)
8812 :op-pos op-pos
8813 :left left
8814 :right right)))
8815 (js2-node-add-children pn left right)
8816 pn))
8817
8818 (defun js2-parse-or-expr ()
8819 (let ((pn (js2-parse-and-expr)))
8820 (when (js2-match-token js2-OR)
8821 (setq pn (js2-make-binary js2-OR
8822 pn
8823 'js2-parse-or-expr)))
8824 pn))
8825
8826 (defun js2-parse-and-expr ()
8827 (let ((pn (js2-parse-bit-or-expr)))
8828 (when (js2-match-token js2-AND)
8829 (setq pn (js2-make-binary js2-AND
8830 pn
8831 'js2-parse-and-expr)))
8832 pn))
8833
8834 (defun js2-parse-bit-or-expr ()
8835 (let ((pn (js2-parse-bit-xor-expr)))
8836 (while (js2-match-token js2-BITOR)
8837 (setq pn (js2-make-binary js2-BITOR
8838 pn
8839 'js2-parse-bit-xor-expr)))
8840 pn))
8841
8842 (defun js2-parse-bit-xor-expr ()
8843 (let ((pn (js2-parse-bit-and-expr)))
8844 (while (js2-match-token js2-BITXOR)
8845 (setq pn (js2-make-binary js2-BITXOR
8846 pn
8847 'js2-parse-bit-and-expr)))
8848 pn))
8849
8850 (defun js2-parse-bit-and-expr ()
8851 (let ((pn (js2-parse-eq-expr)))
8852 (while (js2-match-token js2-BITAND)
8853 (setq pn (js2-make-binary js2-BITAND
8854 pn
8855 'js2-parse-eq-expr)))
8856 pn))
8857
8858 (defconst js2-parse-eq-ops
8859 (list js2-EQ js2-NE js2-SHEQ js2-SHNE))
8860
8861 (defun js2-parse-eq-expr ()
8862 (let ((pn (js2-parse-rel-expr))
8863 tt)
8864 (while (memq (setq tt (js2-peek-token)) js2-parse-eq-ops)
8865 (js2-consume-token)
8866 (setq pn (js2-make-binary tt
8867 pn
8868 'js2-parse-rel-expr)))
8869 pn))
8870
8871 (defconst js2-parse-rel-ops
8872 (list js2-IN js2-INSTANCEOF js2-LE js2-LT js2-GE js2-GT))
8873
8874 (defun js2-parse-rel-expr ()
8875 (let ((pn (js2-parse-shift-expr))
8876 (continue t)
8877 tt)
8878 (while continue
8879 (setq tt (js2-peek-token))
8880 (cond
8881 ((and js2-in-for-init (= tt js2-IN))
8882 (setq continue nil))
8883 ((memq tt js2-parse-rel-ops)
8884 (js2-consume-token)
8885 (setq pn (js2-make-binary tt pn 'js2-parse-shift-expr)))
8886 (t
8887 (setq continue nil))))
8888 pn))
8889
8890 (defconst js2-parse-shift-ops
8891 (list js2-LSH js2-URSH js2-RSH))
8892
8893 (defun js2-parse-shift-expr ()
8894 (let ((pn (js2-parse-add-expr))
8895 tt
8896 (continue t))
8897 (while continue
8898 (setq tt (js2-peek-token))
8899 (if (memq tt js2-parse-shift-ops)
8900 (progn
8901 (js2-consume-token)
8902 (setq pn (js2-make-binary tt pn 'js2-parse-add-expr)))
8903 (setq continue nil)))
8904 pn))
8905
8906 (defun js2-parse-add-expr ()
8907 (let ((pn (js2-parse-mul-expr))
8908 tt
8909 (continue t))
8910 (while continue
8911 (setq tt (js2-peek-token))
8912 (if (or (= tt js2-ADD) (= tt js2-SUB))
8913 (progn
8914 (js2-consume-token)
8915 (setq pn (js2-make-binary tt pn 'js2-parse-mul-expr)))
8916 (setq continue nil)))
8917 pn))
8918
8919 (defconst js2-parse-mul-ops
8920 (list js2-MUL js2-DIV js2-MOD))
8921
8922 (defun js2-parse-mul-expr ()
8923 (let ((pn (js2-parse-unary-expr))
8924 tt
8925 (continue t))
8926 (while continue
8927 (setq tt (js2-peek-token))
8928 (if (memq tt js2-parse-mul-ops)
8929 (progn
8930 (js2-consume-token)
8931 (setq pn (js2-make-binary tt pn 'js2-parse-unary-expr)))
8932 (setq continue nil)))
8933 pn))
8934
8935 (defsubst js2-make-unary (type parser &rest args)
8936 "Make a unary node of type TYPE.
8937 PARSER is either a node (for postfix operators) or a function to call
8938 to parse the operand (for prefix operators)."
8939 (let* ((pos js2-token-beg)
8940 (postfix (js2-node-p parser))
8941 (expr (if postfix
8942 parser
8943 (apply parser args)))
8944 end
8945 pn)
8946 (if postfix ; e.g. i++
8947 (setq pos (js2-node-pos expr)
8948 end js2-token-end)
8949 (setq end (js2-node-end expr)))
8950 (setq pn (make-js2-unary-node :type type
8951 :pos pos
8952 :len (- end pos)
8953 :operand expr))
8954 (js2-node-add-children pn expr)
8955 pn))
8956
8957 (defconst js2-incrementable-node-types
8958 (list js2-NAME js2-GETPROP js2-GETELEM js2-GET_REF js2-CALL)
8959 "Node types that can be the operand of a ++ or -- operator.")
8960
8961 (defsubst js2-check-bad-inc-dec (tt beg end unary)
8962 (unless (memq (js2-node-type (js2-unary-node-operand unary))
8963 js2-incrementable-node-types)
8964 (js2-report-error (if (= tt js2-INC)
8965 "msg.bad.incr"
8966 "msg.bad.decr")
8967 nil beg (- end beg))))
8968
8969 (defun js2-parse-unary-expr ()
8970 (let ((tt (js2-peek-token))
8971 pn expr beg end)
8972 (cond
8973 ((or (= tt js2-VOID)
8974 (= tt js2-NOT)
8975 (= tt js2-BITNOT)
8976 (= tt js2-TYPEOF))
8977 (js2-consume-token)
8978 (js2-make-unary tt 'js2-parse-unary-expr))
8979
8980 ((= tt js2-ADD)
8981 (js2-consume-token)
8982 ;; Convert to special POS token in decompiler and parse tree
8983 (js2-make-unary js2-POS 'js2-parse-unary-expr))
8984
8985 ((= tt js2-SUB)
8986 (js2-consume-token)
8987 ;; Convert to special NEG token in decompiler and parse tree
8988 (js2-make-unary js2-NEG 'js2-parse-unary-expr))
8989
8990 ((or (= tt js2-INC)
8991 (= tt js2-DEC))
8992 (js2-consume-token)
8993 (prog1
8994 (setq beg js2-token-beg
8995 end js2-token-end
8996 expr (js2-make-unary tt 'js2-parse-member-expr t))
8997 (js2-check-bad-inc-dec tt beg end expr)))
8998
8999 ((= tt js2-DELPROP)
9000 (js2-consume-token)
9001 (js2-make-unary js2-DELPROP 'js2-parse-unary-expr))
9002
9003 ((= tt js2-ERROR)
9004 (js2-consume-token)
9005 (make-js2-error-node)) ; try to continue
9006
9007 ((and (= tt js2-LT)
9008 js2-compiler-xml-available)
9009 ;; XML stream encountered in expression.
9010 (js2-consume-token)
9011 (js2-parse-member-expr-tail t (js2-parse-xml-initializer)))
9012 (t
9013 (setq pn (js2-parse-member-expr t)
9014 ;; Don't look across a newline boundary for a postfix incop.
9015 tt (js2-peek-token-or-eol))
9016 (when (or (= tt js2-INC) (= tt js2-DEC))
9017 (js2-consume-token)
9018 (setf expr pn
9019 pn (js2-make-unary tt expr))
9020 (js2-node-set-prop pn 'postfix t)
9021 (js2-check-bad-inc-dec tt js2-token-beg js2-token-end pn))
9022 pn))))
9023
9024 (defun js2-parse-xml-initializer ()
9025 "Parse an E4X XML initializer.
9026 I'm parsing it the way Rhino parses it, but without the tree-rewriting.
9027 Then I'll postprocess the result, depending on whether we're in IDE
9028 mode or codegen mode, and generate the appropriate rewritten AST.
9029 IDE mode uses a rich AST that models the XML structure. Codegen mode
9030 just concatenates everything and makes a new XML or XMLList out of it."
9031 (let ((tt (js2-get-first-xml-token))
9032 pn-xml
9033 pn
9034 expr
9035 kids
9036 expr-pos
9037 (continue t)
9038 (first-token t))
9039 (when (not (or (= tt js2-XML) (= tt js2-XMLEND)))
9040 (js2-report-error "msg.syntax"))
9041 (setq pn-xml (make-js2-xml-node))
9042 (while continue
9043 (if first-token
9044 (setq first-token nil)
9045 (setq tt (js2-get-next-xml-token)))
9046 (cond
9047 ;; js2-XML means we found a {expr} in the XML stream.
9048 ;; The js2-ts-string is the XML up to the left-curly.
9049 ((= tt js2-XML)
9050 (push (make-js2-string-node :pos js2-token-beg
9051 :len (- js2-ts-cursor js2-token-beg))
9052 kids)
9053 (js2-must-match js2-LC "msg.syntax")
9054 (setq expr-pos js2-ts-cursor
9055 expr (if (eq (js2-peek-token) js2-RC)
9056 (make-js2-empty-expr-node :pos expr-pos)
9057 (js2-parse-expr)))
9058 (js2-must-match js2-RC "msg.syntax")
9059 (setq pn (make-js2-xml-js-expr-node :pos (js2-node-pos expr)
9060 :len (js2-node-len expr)
9061 :expr expr))
9062 (js2-node-add-children pn expr)
9063 (push pn kids))
9064
9065 ;; a js2-XMLEND token means we hit the final close-tag.
9066 ((= tt js2-XMLEND)
9067 (push (make-js2-string-node :pos js2-token-beg
9068 :len (- js2-ts-cursor js2-token-beg))
9069 kids)
9070 (dolist (kid (nreverse kids))
9071 (js2-block-node-push pn-xml kid))
9072 (setf (js2-node-len pn-xml) (- js2-ts-cursor
9073 (js2-node-pos pn-xml))
9074 continue nil))
9075 (t
9076 (js2-report-error "msg.syntax")
9077 (setq continue nil))))
9078 pn-xml))
9079
9080
9081 (defun js2-parse-argument-list ()
9082 "Parse an argument list and return it as a lisp list of nodes.
9083 Returns the list in reverse order. Consumes the right-paren token."
9084 (let (result)
9085 (unless (js2-match-token js2-RP)
9086 (loop do
9087 (if (= (js2-peek-token) js2-YIELD)
9088 (js2-report-error "msg.yield.parenthesized"))
9089 (push (js2-parse-assign-expr) result)
9090 while
9091 (js2-match-token js2-COMMA))
9092 (js2-must-match js2-RP "msg.no.paren.arg")
9093 result)))
9094
9095 (defun js2-parse-member-expr (&optional allow-call-syntax)
9096 (let ((tt (js2-peek-token))
9097 pn
9098 pos
9099 target
9100 args
9101 beg
9102 end
9103 init
9104 tail)
9105 (if (/= tt js2-NEW)
9106 (setq pn (js2-parse-primary-expr))
9107 ;; parse a 'new' expression
9108 (js2-consume-token)
9109 (setq pos js2-token-beg
9110 beg pos
9111 target (js2-parse-member-expr)
9112 end (js2-node-end target)
9113 pn (make-js2-new-node :pos pos
9114 :target target
9115 :len (- end pos)))
9116 (js2-node-add-children pn target)
9117 (when (js2-match-token js2-LP)
9118 ;; Add the arguments to pn, if any are supplied.
9119 (setf beg pos ; start of "new" keyword
9120 pos js2-token-beg
9121 args (nreverse (js2-parse-argument-list))
9122 (js2-new-node-args pn) args
9123 end js2-token-end
9124 (js2-new-node-lp pn) (- pos beg)
9125 (js2-new-node-rp pn) (- end 1 beg))
9126 (apply #'js2-node-add-children pn args))
9127
9128 (when (and js2-allow-rhino-new-expr-initializer
9129 (js2-match-token js2-LC))
9130 (setf init (js2-parse-object-literal)
9131 end (js2-node-end init)
9132 (js2-new-node-initializer pn) init)
9133 (js2-node-add-children pn init))
9134
9135 (setf (js2-node-len pn) (- beg pos))) ; end outer if
9136
9137 (js2-parse-member-expr-tail allow-call-syntax pn)))
9138
9139 (defun js2-parse-member-expr-tail (allow-call-syntax pn)
9140 "Parse a chain of property/array accesses or function calls.
9141 Includes parsing for E4X operators like `..' and `.@'.
9142 If ALLOW-CALL-SYNTAX is nil, stops when we encounter a left-paren.
9143 Returns an expression tree that includes PN, the parent node."
9144 (let ((beg (js2-node-pos pn))
9145 tt
9146 (continue t))
9147 (while continue
9148 (setq tt (js2-peek-token))
9149 (cond
9150 ((or (= tt js2-DOT) (= tt js2-DOTDOT))
9151 (setq pn (js2-parse-property-access tt pn)))
9152
9153 ((= tt js2-DOTQUERY)
9154 (setq pn (js2-parse-dot-query pn)))
9155
9156 ((= tt js2-LB)
9157 (setq pn (js2-parse-element-get pn)))
9158
9159 ((= tt js2-LP)
9160 (if allow-call-syntax
9161 (setq pn (js2-parse-function-call pn))
9162 (setq continue nil)))
9163 (t
9164 (setq continue nil))))
9165 (if (>= js2-highlight-level 2)
9166 (js2-parse-highlight-member-expr-node pn))
9167 pn))
9168
9169 (defun js2-parse-dot-query (pn)
9170 "Parse a dot-query expression, e.g. foo.bar.(@name == 2)
9171 Last token parsed must be `js2-DOTQUERY'."
9172 (let ((pos (js2-node-pos pn))
9173 op-pos
9174 expr
9175 end)
9176 (js2-consume-token)
9177 (js2-must-have-xml)
9178 (js2-set-requires-activation)
9179 (setq op-pos js2-token-beg
9180 expr (js2-parse-expr)
9181 end (js2-node-end expr)
9182 pn (make-js2-xml-dot-query-node :left pn
9183 :pos pos
9184 :op-pos op-pos
9185 :right expr))
9186 (js2-node-add-children pn
9187 (js2-xml-dot-query-node-left pn)
9188 (js2-xml-dot-query-node-right pn))
9189 (if (js2-must-match js2-RP "msg.no.paren")
9190 (setf (js2-xml-dot-query-node-rp pn) js2-token-beg
9191 end js2-token-end))
9192 (setf (js2-node-len pn) (- end pos))
9193 pn))
9194
9195 (defun js2-parse-element-get (pn)
9196 "Parse an element-get expression, e.g. foo[bar].
9197 Last token parsed must be `js2-RB'."
9198 (let ((lb js2-token-beg)
9199 (pos (js2-node-pos pn))
9200 rb
9201 expr)
9202 (js2-consume-token)
9203 (setq expr (js2-parse-expr))
9204 (if (js2-must-match js2-RB "msg.no.bracket.index")
9205 (setq rb js2-token-beg))
9206 (setq pn (make-js2-elem-get-node :target pn
9207 :pos pos
9208 :element expr
9209 :lb (js2-relpos lb pos)
9210 :rb (js2-relpos rb pos)
9211 :len (- js2-token-end pos)))
9212 (js2-node-add-children pn
9213 (js2-elem-get-node-target pn)
9214 (js2-elem-get-node-element pn))
9215 pn))
9216
9217 (defun js2-parse-function-call (pn)
9218 (let (args
9219 (pos (js2-node-pos pn)))
9220 (js2-consume-token)
9221 (setq pn (make-js2-call-node :pos pos
9222 :target pn
9223 :lp (- js2-token-beg pos)))
9224 (js2-node-add-children pn (js2-call-node-target pn))
9225
9226 ;; Add the arguments to pn, if any are supplied.
9227 (setf args (nreverse (js2-parse-argument-list))
9228 (js2-call-node-rp pn) (- js2-token-beg pos)
9229 (js2-call-node-args pn) args)
9230 (apply #'js2-node-add-children pn args)
9231
9232 (setf (js2-node-len pn) (- js2-ts-cursor pos))
9233 pn))
9234
9235 (defun js2-parse-property-access (tt pn)
9236 "Parse a property access, XML descendants access, or XML attr access."
9237 (let ((member-type-flags 0)
9238 (dot-pos js2-token-beg)
9239 (dot-len (if (= tt js2-DOTDOT) 2 1))
9240 name
9241 ref ; right side of . or .. operator
9242 result)
9243 (js2-consume-token)
9244 (when (= tt js2-DOTDOT)
9245 (js2-must-have-xml)
9246 (setq member-type-flags js2-descendants-flag))
9247 (if (not js2-compiler-xml-available)
9248 (progn
9249 (js2-must-match-prop-name "msg.no.name.after.dot")
9250 (setq name (js2-create-name-node t js2-GETPROP)
9251 result (make-js2-prop-get-node :left pn
9252 :pos js2-token-beg
9253 :right name
9254 :len (- js2-token-end
9255 js2-token-beg)))
9256 (js2-node-add-children result pn name)
9257 result)
9258 ;; otherwise look for XML operators
9259 (setf result (if (= tt js2-DOT)
9260 (make-js2-prop-get-node)
9261 (make-js2-infix-node :type js2-DOTDOT))
9262 (js2-node-pos result) (js2-node-pos pn)
9263 (js2-infix-node-op-pos result) dot-pos
9264 (js2-infix-node-left result) pn ; do this after setting position
9265 tt (js2-next-token))
9266 (cond
9267 ;; needed for generator.throw()
9268 ((= tt js2-THROW)
9269 (js2-save-name-token-data js2-token-beg "throw")
9270 (setq ref (js2-parse-property-name nil js2-ts-string member-type-flags)))
9271
9272 ;; handles: name, ns::name, ns::*, ns::[expr]
9273 ((js2-valid-prop-name-token tt)
9274 (setq ref (js2-parse-property-name -1 js2-ts-string member-type-flags)))
9275
9276 ;; handles: *, *::name, *::*, *::[expr]
9277 ((= tt js2-MUL)
9278 (js2-save-name-token-data js2-token-beg "*")
9279 (setq ref (js2-parse-property-name nil "*" member-type-flags)))
9280
9281 ;; handles: '@attr', '@ns::attr', '@ns::*', '@ns::[expr]', etc.
9282 ((= tt js2-XMLATTR)
9283 (setq result (js2-parse-attribute-access)))
9284
9285 (t
9286 (js2-report-error "msg.no.name.after.dot" nil dot-pos dot-len)))
9287
9288 (if ref
9289 (setf (js2-node-len result) (- (js2-node-end ref)
9290 (js2-node-pos result))
9291 (js2-infix-node-right result) ref))
9292 (if (js2-infix-node-p result)
9293 (js2-node-add-children result
9294 (js2-infix-node-left result)
9295 (js2-infix-node-right result)))
9296 result)))
9297
9298 (defun js2-parse-attribute-access ()
9299 "Parse an E4X XML attribute expression.
9300 This includes expressions of the forms:
9301
9302 @attr @ns::attr @ns::*
9303 @* @*::attr @*::*
9304 @[expr] @*::[expr] @ns::[expr]
9305
9306 Called if we peeked an '@' token."
9307 (let ((tt (js2-next-token))
9308 (at-pos js2-token-beg))
9309 (cond
9310 ;; handles: @name, @ns::name, @ns::*, @ns::[expr]
9311 ((js2-valid-prop-name-token tt)
9312 (js2-parse-property-name at-pos js2-ts-string 0))
9313
9314 ;; handles: @*, @*::name, @*::*, @*::[expr]
9315 ((= tt js2-MUL)
9316 (js2-save-name-token-data js2-token-beg "*")
9317 (js2-parse-property-name js2-token-beg "*" 0))
9318
9319 ;; handles @[expr]
9320 ((= tt js2-LB)
9321 (js2-parse-xml-elem-ref at-pos))
9322
9323 (t
9324 (js2-report-error "msg.no.name.after.xmlAttr")
9325 ;; Avoid cascaded errors that happen if we make an error node here.
9326 (js2-save-name-token-data js2-token-beg "")
9327 (js2-parse-property-name js2-token-beg "" 0)))))
9328
9329 (defun js2-parse-property-name (at-pos s member-type-flags)
9330 "Check if :: follows name in which case it becomes qualified name.
9331
9332 AT-POS is a natural number if we just read an '@' token, else nil.
9333 S is the name or string that was matched: an identifier, 'throw' or '*'.
9334 MEMBER-TYPE-FLAGS is a bit set tracking whether we're a '.' or '..' child.
9335
9336 Returns a `js2-xml-ref-node' if it's an attribute access, a child of a '..'
9337 operator, or the name is followed by ::. For a plain name, returns a
9338 `js2-name-node'. Returns a `js2-error-node' for malformed XML expressions."
9339 (let ((pos (or at-pos js2-token-beg))
9340 colon-pos
9341 (name (js2-create-name-node t js2-current-token))
9342 ns
9343 tt
9344 ref
9345 pn)
9346 (catch 'return
9347 (when (js2-match-token js2-COLONCOLON)
9348 (setq ns name
9349 colon-pos js2-token-beg
9350 tt (js2-next-token))
9351 (cond
9352 ;; handles name::name
9353 ((js2-valid-prop-name-token tt)
9354 (setq name (js2-create-name-node)))
9355
9356 ;; handles name::*
9357 ((= tt js2-MUL)
9358 (js2-save-name-token-data js2-token-beg "*")
9359 (setq name (js2-create-name-node)))
9360
9361 ;; handles name::[expr]
9362 ((= tt js2-LB)
9363 (throw 'return (js2-parse-xml-elem-ref at-pos ns colon-pos)))
9364
9365 (t
9366 (js2-report-error "msg.no.name.after.coloncolon"))))
9367
9368 (if (and (null ns) (zerop member-type-flags))
9369 name
9370 (prog1
9371 (setq pn
9372 (make-js2-xml-prop-ref-node :pos pos
9373 :len (- (js2-node-end name) pos)
9374 :at-pos at-pos
9375 :colon-pos colon-pos
9376 :propname name))
9377 (js2-node-add-children pn name))))))
9378
9379 (defun js2-parse-xml-elem-ref (at-pos &optional namespace colon-pos)
9380 "Parse the [expr] portion of an xml element reference.
9381 For instance, @[expr], @*::[expr], or ns::[expr]."
9382 (let* ((lb js2-token-beg)
9383 (pos (or at-pos lb))
9384 rb
9385 (expr (js2-parse-expr))
9386 (end (js2-node-end expr))
9387 pn)
9388 (if (js2-must-match js2-RB "msg.no.bracket.index")
9389 (setq rb js2-token-beg
9390 end js2-token-end))
9391 (prog1
9392 (setq pn
9393 (make-js2-xml-elem-ref-node :pos pos
9394 :len (- end pos)
9395 :namespace namespace
9396 :colon-pos colon-pos
9397 :at-pos at-pos
9398 :expr expr
9399 :lb (js2-relpos lb pos)
9400 :rb (js2-relpos rb pos)))
9401 (js2-node-add-children pn namespace expr))))
9402
9403 (defun js2-parse-primary-expr ()
9404 "Parses a literal (leaf) expression of some sort.
9405 Includes complex literals such as functions, object-literals,
9406 array-literals, array comprehensions and regular expressions."
9407 (let ((tt-flagged (js2-next-flagged-token))
9408 pn ; parent node (usually return value)
9409 tt
9410 px-pos ; paren-expr pos
9411 len
9412 flags ; regexp flags
9413 expr)
9414 (setq tt js2-current-token)
9415 (cond
9416 ((= tt js2-FUNCTION)
9417 (js2-parse-function 'FUNCTION_EXPRESSION))
9418
9419 ((= tt js2-LB)
9420 (js2-parse-array-literal))
9421
9422 ((= tt js2-LC)
9423 (js2-parse-object-literal))
9424
9425 ((= tt js2-LET)
9426 (js2-parse-let js2-token-beg))
9427
9428 ((= tt js2-LP)
9429 (setq px-pos js2-token-beg
9430 expr (js2-parse-expr))
9431 (js2-must-match js2-RP "msg.no.paren")
9432 (setq pn (make-js2-paren-node :pos px-pos
9433 :expr expr
9434 :len (- js2-token-end px-pos)))
9435 (js2-node-add-children pn (js2-paren-node-expr pn))
9436 pn)
9437
9438 ((= tt js2-XMLATTR)
9439 (js2-must-have-xml)
9440 (js2-parse-attribute-access))
9441
9442 ((= tt js2-NAME)
9443 (js2-parse-name tt-flagged tt))
9444
9445 ((= tt js2-NUMBER)
9446 (make-js2-number-node))
9447
9448 ((= tt js2-STRING)
9449 (prog1
9450 (make-js2-string-node)
9451 (js2-record-face 'font-lock-string-face)))
9452
9453 ((or (= tt js2-DIV) (= tt js2-ASSIGN_DIV))
9454 ;; Got / or /= which in this context means a regexp literal
9455 (setq px-pos js2-token-beg)
9456 (js2-read-regexp tt)
9457 (setq flags js2-ts-regexp-flags
9458 js2-ts-regexp-flags nil)
9459 (prog1
9460 (make-js2-regexp-node :pos px-pos
9461 :len (- js2-ts-cursor px-pos)
9462 :value js2-ts-string
9463 :flags flags)
9464 (js2-set-face px-pos js2-ts-cursor 'font-lock-string-face 'record)))
9465
9466 ((or (= tt js2-NULL)
9467 (= tt js2-THIS)
9468 (= tt js2-FALSE)
9469 (= tt js2-TRUE))
9470 (make-js2-keyword-node :type tt))
9471
9472 ((= tt js2-RESERVED)
9473 (js2-report-error "msg.reserved.id")
9474 (make-js2-name-node))
9475
9476 ((= tt js2-ERROR)
9477 ;; the scanner or one of its subroutines reported the error.
9478 (make-js2-error-node))
9479
9480 ((= tt js2-EOF)
9481 (setq px-pos (point-at-bol)
9482 len (- js2-ts-cursor px-pos))
9483 (js2-report-error "msg.unexpected.eof" nil px-pos len)
9484 (make-js2-error-node :pos px-pos :len len))
9485
9486 (t
9487 (js2-report-error "msg.syntax")
9488 (make-js2-error-node)))))
9489
9490 (defun js2-parse-name (tt-flagged tt)
9491 (let ((name js2-ts-string)
9492 (name-pos js2-token-beg))
9493 (if (and (js2-flag-set-p tt-flagged js2-ti-check-label)
9494 (= (js2-peek-token) js2-COLON))
9495 (prog1
9496 ;; Do not consume colon, it is used as unwind indicator
9497 ;; to return to statementHelper.
9498 (make-js2-label-node :pos name-pos
9499 :len (- js2-token-end name-pos)
9500 :name name)
9501 (js2-set-face name-pos
9502 js2-token-end
9503 'font-lock-variable-name-face 'record))
9504 ;; Otherwise not a label, just a name. Unfortunately peeking
9505 ;; the next token to check for a colon has biffed js2-token-beg
9506 ;; and js2-token-end. We store the name's bounds in buffer vars
9507 ;; and `js2-create-name-node' uses them.
9508 (js2-save-name-token-data name-pos name)
9509 (if js2-compiler-xml-available
9510 (js2-parse-property-name nil name 0)
9511 (js2-create-name-node 'check-activation)))))
9512
9513 (defsubst js2-parse-warn-trailing-comma (msg pos elems comma-pos)
9514 (js2-add-strict-warning
9515 msg nil
9516 ;; back up from comma to beginning of line or array/objlit
9517 (max (if elems
9518 (js2-node-pos (car elems))
9519 pos)
9520 (save-excursion
9521 (goto-char comma-pos)
9522 (back-to-indentation)
9523 (point)))
9524 comma-pos))
9525
9526 (defun js2-parse-array-literal ()
9527 (let ((pos js2-token-beg)
9528 (end js2-token-end)
9529 (after-lb-or-comma t)
9530 after-comma
9531 tt
9532 elems
9533 pn
9534 (continue t))
9535 (while continue
9536 (setq tt (js2-peek-token))
9537 (cond
9538 ;; comma
9539 ((= tt js2-COMMA)
9540 (js2-consume-token)
9541 (setq after-comma js2-token-end)
9542 (if (not after-lb-or-comma)
9543 (setq after-lb-or-comma t)
9544 (push nil elems)))
9545
9546 ;; end of array
9547 ((or (= tt js2-RB)
9548 (= tt js2-EOF)) ; prevent infinite loop
9549 (if (= tt js2-EOF)
9550 (js2-report-error "msg.no.bracket.arg" nil pos)
9551 (js2-consume-token))
9552 (setq continue nil
9553 end js2-token-end
9554 pn (make-js2-array-node :pos pos
9555 :len (- js2-ts-cursor pos)
9556 :elems (nreverse elems)))
9557 (apply #'js2-node-add-children pn (js2-array-node-elems pn))
9558 (when after-comma
9559 (js2-parse-warn-trailing-comma "msg.array.trailing.comma"
9560 pos elems after-comma)))
9561
9562 ;; array comp
9563 ((and (>= js2-language-version 170)
9564 (= tt js2-FOR) ; check for array comprehension
9565 (not after-lb-or-comma) ; "for" can't follow a comma
9566 elems ; must have at least 1 element
9567 (not (cdr elems))) ; but no 2nd element
9568 (setf continue nil
9569 pn (js2-parse-array-comprehension (car elems) pos)))
9570
9571 ;; another element
9572 (t
9573 (unless after-lb-or-comma
9574 (js2-report-error "msg.no.bracket.arg"))
9575 (push (js2-parse-assign-expr) elems)
9576 (setq after-lb-or-comma nil
9577 after-comma nil))))
9578 pn))
9579
9580 (defun js2-parse-array-comprehension (expr pos)
9581 "Parse a JavaScript 1.7 Array Comprehension.
9582 EXPR is the first expression after the opening left-bracket.
9583 POS is the beginning of the LB token preceding EXPR.
9584 We should have just parsed the 'for' keyword before calling this function."
9585 (let (loops
9586 filter
9587 if-pos
9588 result)
9589 (while (= (js2-peek-token) js2-FOR)
9590 (push (js2-parse-array-comp-loop) loops))
9591 (when (= (js2-peek-token) js2-IF)
9592 (js2-consume-token)
9593 (setq if-pos (- js2-token-beg pos) ; relative
9594 filter (js2-parse-condition)))
9595 (js2-must-match js2-RB "msg.no.bracket.arg" pos)
9596 (setq result (make-js2-array-comp-node :pos pos
9597 :len (- js2-ts-cursor pos)
9598 :result expr
9599 :loops (nreverse loops)
9600 :filter (car filter)
9601 :lp (js2-relpos (second filter) pos)
9602 :rp (js2-relpos (third filter) pos)
9603 :if-pos if-pos))
9604 (apply #'js2-node-add-children result expr (car filter)
9605 (js2-array-comp-node-loops result))
9606 result))
9607
9608 (defun js2-parse-array-comp-loop ()
9609 "Parse a 'for [each] (foo in bar)' expression in an Array comprehension.
9610 Last token peeked should be the initial FOR."
9611 (let ((pos js2-token-beg)
9612 (pn (make-js2-array-comp-loop-node))
9613 tt
9614 iter
9615 obj
9616 foreach-p
9617 in-pos
9618 each-pos
9619 lp
9620 rp)
9621 (assert (= (js2-next-token) js2-FOR)) ; consumes token
9622 (js2-push-scope pn)
9623 (unwind-protect
9624 (progn
9625 (when (js2-match-token js2-NAME)
9626 (if (string= js2-ts-string "each")
9627 (progn
9628 (setq foreach-p t
9629 each-pos (- js2-token-beg pos)) ; relative
9630 (js2-record-face 'font-lock-keyword-face))
9631 (js2-report-error "msg.no.paren.for")))
9632
9633 (if (js2-must-match js2-LP "msg.no.paren.for")
9634 (setq lp (- js2-token-beg pos)))
9635
9636 (setq tt (js2-peek-token))
9637 (cond
9638 ((or (= tt js2-LB)
9639 (= tt js2-LC))
9640 ;; handle destructuring assignment
9641 (setq iter (js2-parse-primary-expr)))
9642
9643 ((js2-valid-prop-name-token tt)
9644 (js2-consume-token)
9645 (setq iter (js2-create-name-node)))
9646
9647 (t
9648 (js2-report-error "msg.bad.var")))
9649
9650 ;; Define as a let since we want the scope of the variable to
9651 ;; be restricted to the array comprehension
9652 (if (js2-name-node-p iter)
9653 (js2-define-symbol js2-LET (js2-name-node-name iter) pn t))
9654
9655 (if (js2-must-match js2-IN "msg.in.after.for.name")
9656 (setq in-pos (- js2-token-beg pos)))
9657
9658 (setq obj (js2-parse-expr))
9659 (if (js2-must-match js2-RP "msg.no.paren.for.ctrl")
9660 (setq rp (- js2-token-beg pos)))
9661
9662 (setf (js2-node-pos pn) pos
9663 (js2-node-len pn) (- js2-ts-cursor pos)
9664 (js2-array-comp-loop-node-iterator pn) iter
9665 (js2-array-comp-loop-node-object pn) obj
9666 (js2-array-comp-loop-node-in-pos pn) in-pos
9667 (js2-array-comp-loop-node-each-pos pn) each-pos
9668 (js2-array-comp-loop-node-foreach-p pn) foreach-p
9669 (js2-array-comp-loop-node-lp pn) lp
9670 (js2-array-comp-loop-node-rp pn) rp)
9671 (js2-node-add-children pn iter obj))
9672 (js2-pop-scope))
9673 pn))
9674
9675 (defun js2-parse-object-literal ()
9676 (let ((pos js2-token-beg)
9677 tt
9678 elems
9679 result
9680 after-comma
9681 (continue t))
9682 (while continue
9683 (setq tt (js2-peek-token))
9684 (cond
9685 ;; {foo: ...}, {'foo': ...}, {get foo() {...}}, or {set foo(x) {...}}
9686 ((or (js2-valid-prop-name-token tt)
9687 (= tt js2-STRING))
9688 (setq after-comma nil
9689 result (js2-parse-named-prop tt))
9690 (if (and (null result)
9691 (not js2-recover-from-parse-errors))
9692 (setq continue nil)
9693 (push result elems)))
9694
9695 ;; {12: x} or {10.7: x}
9696 ((= tt js2-NUMBER)
9697 (js2-consume-token)
9698 (setq after-comma nil)
9699 (push (js2-parse-plain-property (make-js2-number-node)) elems))
9700
9701 ;; trailing comma
9702 ((= tt js2-RC)
9703 (setq continue nil)
9704 (if after-comma
9705 (js2-parse-warn-trailing-comma "msg.extra.trailing.comma"
9706 pos elems after-comma)))
9707 (t
9708 (js2-report-error "msg.bad.prop")
9709 (unless js2-recover-from-parse-errors
9710 (setq continue nil)))) ; end switch
9711
9712 (if (js2-match-token js2-COMMA)
9713 (setq after-comma js2-token-end)
9714 (setq continue nil))) ; end loop
9715
9716 (js2-must-match js2-RC "msg.no.brace.prop")
9717 (setq result (make-js2-object-node :pos pos
9718 :len (- js2-ts-cursor pos)
9719 :elems (nreverse elems)))
9720 (apply #'js2-node-add-children result (js2-object-node-elems result))
9721 result))
9722
9723 (defun js2-parse-named-prop (tt)
9724 "Parse a name, string, or getter/setter object property."
9725 (js2-consume-token)
9726 (let ((string-prop (and (= tt js2-STRING)
9727 (make-js2-string-node)))
9728 expr
9729 (ppos js2-token-beg)
9730 (pend js2-token-end)
9731 (name (js2-create-name-node))
9732 (prop js2-ts-string))
9733
9734 (if (and (= tt js2-NAME)
9735 (= (js2-peek-token) js2-NAME)
9736 (or (string= prop "get")
9737 (string= prop "set")))
9738 (progn
9739 ;; getter/setter prop
9740 (js2-consume-token)
9741 (js2-set-face ppos pend 'font-lock-keyword-face 'record) ; get/set
9742 (js2-record-face 'font-lock-function-name-face) ; for peeked name
9743 (setq name (js2-create-name-node)) ; discard get/set & use peeked name
9744 (js2-parse-getter-setter-prop ppos name (string= prop "get")))
9745
9746 ;; regular prop
9747 (prog1
9748 (setq expr (js2-parse-plain-property (or string-prop name)))
9749 (js2-set-face ppos pend
9750 (if (js2-function-node-p
9751 (js2-object-prop-node-right expr))
9752 'font-lock-function-name-face
9753 'font-lock-variable-name-face)
9754 'record)))))
9755
9756 (defun js2-parse-plain-property (prop)
9757 "Parse a non-getter/setter property in an object literal.
9758 PROP is the node representing the property: a number, name or string."
9759 (js2-must-match js2-COLON "msg.no.colon.prop")
9760 (let* ((pos (js2-node-pos prop))
9761 (colon (- js2-token-beg pos))
9762 (expr (js2-parse-assign-expr))
9763 (result (make-js2-object-prop-node
9764 :pos pos
9765 ;; don't include last consumed token in length
9766 :len (- (+ (js2-node-pos expr)
9767 (js2-node-len expr))
9768 pos)
9769 :left prop
9770 :right expr
9771 :op-pos colon)))
9772 (js2-node-add-children result prop expr)
9773 result))
9774
9775 (defun js2-parse-getter-setter-prop (pos prop get-p)
9776 "Parse getter or setter property in an object literal.
9777 JavaScript syntax is:
9778
9779 { get foo() {...}, set foo(x) {...} }
9780
9781 POS is the start position of the `get' or `set' keyword.
9782 PROP is the `js2-name-node' representing the property name.
9783 GET-P is non-nil if the keyword was `get'."
9784 (let ((type (if get-p js2-GET js2-SET))
9785 result
9786 end
9787 (fn (js2-parse-function 'FUNCTION_EXPRESSION)))
9788
9789 ;; it has to be an anonymous function, as we already parsed the name
9790 (if (/= (js2-node-type fn) js2-FUNCTION)
9791 (js2-report-error "msg.bad.prop")
9792 (if (plusp (length (js2-function-name fn)))
9793 (js2-report-error "msg.bad.prop")))
9794
9795 (js2-node-set-prop fn 'GETTER_SETTER type) ; for codegen
9796 (setq end (js2-node-end fn)
9797 result (make-js2-getter-setter-node :type type
9798 :pos pos
9799 :len (- end pos)
9800 :left prop
9801 :right fn))
9802 (js2-node-add-children result prop fn)
9803 result))
9804
9805 (defun js2-create-name-node (&optional check-activation-p token)
9806 "Create a name node using the token info from last scanned name.
9807 In some cases we need to either synthesize a name node, or we lost
9808 the name token information by peeking. If the TOKEN parameter is
9809 not `js2-NAME', then we use the token info saved in instance vars."
9810 (let ((beg js2-token-beg)
9811 (s js2-ts-string)
9812 name)
9813 (when (/= js2-current-token js2-NAME)
9814 (setq beg (or js2-prev-name-token-start js2-ts-cursor)
9815 s js2-prev-name-token-string
9816 js2-prev-name-token-start nil
9817 js2-prev-name-token-string nil))
9818 (setq name (make-js2-name-node :pos beg
9819 :name s
9820 :len (length s)))
9821 (if check-activation-p
9822 (js2-check-activation-name s (or token js2-NAME)))
9823 name))
9824
9825 (provide 'js2-parse)
9826
9827 ;;; js2-parse.el ends here
9828 ;;; js2-indent.el --- indentation for js2-mode
9829 ;;
9830 ;; Copyright (C) 2008 Steve Yegge
9831 ;; Author: Steve Yegge (steve.yegge@gmail.com)
9832 ;; Maintainer: Steve Yegge (steve.yegge@gmail.com)
9833
9834 ;; Commentary:
9835 ;;
9836 ;; This indenter is based on Karl Landström's "javascript.el" indenter.
9837 ;; Karl cleverly deduces that the desired indentation level is often a
9838 ;; function of paren/bracket/brace nesting depth, which can be determined
9839 ;; quickly via the built-in `parse-partial-sexp' function. His indenter
9840 ;; then does some equally clever checks to see if we're in the context of a
9841 ;; substatement of a possibly braceless statement keyword such as if, while,
9842 ;; or finally. This approach yields pretty good results.
9843 ;;
9844 ;; The indenter is often "wrong", however, and needs to be overridden.
9845 ;; The right long-term solution is probably to emulate (or modify)
9846 ;; cc-engine, but it's thousands upon thousands of lines of code. Even
9847 ;; if you were to assume the accurate parse tree from `js2-parse' is
9848 ;; present, indentation is still thousands of lines of code (I've been
9849 ;; down that path) to handle every possible syntactic edge case, and in
9850 ;; any case, relying on the parse tree is undesirable because parsing is
9851 ;; slow. So you might as well go the cc-engine approach, but it's a
9852 ;; huge pile of work that I'm just not up for any time soon.
9853 ;;
9854 ;; In the meantime, the compromise solution is that we offer a
9855 ;; "bounce indenter", configured with `js2-bounce-indent-flag', which
9856 ;; cycles the current line indent among various likely guess points.
9857 ;; This approach is far from perfect, but should at least make it
9858 ;; slightly easier to move the line towards its desired indentation
9859 ;; when manually overriding Karl's heuristic nesting guesser.
9860 ;;
9861 ;; I've made miscellaneous tweaks to Karl's code to handle some Ecma
9862 ;; extensions such as `let' and Array comprehensions, and will likely
9863 ;; make further tweaks to it, but major kudos to Karl for coming up with
9864 ;; the initial approach, which packs a lot of punch for so little code.
9865
9866 ;;; Code:
9867
9868 (defconst js-possibly-braceless-keyword-re
9869 (regexp-opt
9870 '("catch" "do" "else" "finally" "for" "if" "try" "while" "with" "let")
9871 'words)
9872 "Regular expression matching keywords that are optionally
9873 followed by an opening brace.")
9874
9875 (defconst js-indent-operator-re
9876 (concat "[-+*/%<>=&^|?:.]\\([^-+*/]\\|$\\)\\|"
9877 (regexp-opt '("in" "instanceof") 'words))
9878 "Regular expression matching operators that affect indentation
9879 of continued expressions.")
9880
9881 ;; This function has horrible results if you're typing an array
9882 ;; such as [[1, 2], [3, 4], [5, 6]]. Bounce indenting -really- sucks
9883 ;; in conjunction with electric-indent, so just disabling it.
9884 (defsubst js2-code-at-bol-p ()
9885 "Return t if the first character on line is non-whitespace."
9886 nil)
9887 ;; (not (memq (char-after (point-at-bol))
9888 ;; '(? ?\t)))))
9889
9890 (defun js2-insert-and-indent (key)
9891 "Run command bound to key and indent current line. Runs the command
9892 bound to KEY in the global keymap and indents the current line."
9893 (interactive (list (this-command-keys)))
9894 (let ((cmd (lookup-key (current-global-map) key)))
9895 (if (commandp cmd)
9896 (call-interactively cmd)))
9897 ;; don't do the electric keys inside comments or strings,
9898 ;; and don't do bounce-indent with them.
9899 (let ((parse-state (parse-partial-sexp (point-min) (point)))
9900 (js2-bounce-indent-flag (js2-code-at-bol-p)))
9901 (unless (or (nth 3 parse-state)
9902 (nth 4 parse-state))
9903 (indent-according-to-mode))))
9904
9905 (defun js-re-search-forward-inner (regexp &optional bound count)
9906 "Auxiliary function for `js-re-search-forward'."
9907 (let ((parse)
9908 (saved-point (point-min)))
9909 (while (> count 0)
9910 (re-search-forward regexp bound)
9911 (setq parse (parse-partial-sexp saved-point (point)))
9912 (cond ((nth 3 parse)
9913 (re-search-forward
9914 (concat "\\([^\\]\\|^\\)" (string (nth 3 parse)))
9915 (save-excursion (end-of-line) (point)) t))
9916 ((nth 7 parse)
9917 (forward-line))
9918 ((or (nth 4 parse)
9919 (and (eq (char-before) ?\/) (eq (char-after) ?\*)))
9920 (re-search-forward "\\*/"))
9921 (t
9922 (setq count (1- count))))
9923 (setq saved-point (point))))
9924 (point))
9925
9926 (defun js-re-search-forward (regexp &optional bound noerror count)
9927 "Search forward but ignore strings and comments. Invokes
9928 `re-search-forward' but treats the buffer as if strings and
9929 comments have been removed."
9930 (let ((saved-point (point))
9931 (search-expr
9932 (cond ((null count)
9933 '(js-re-search-forward-inner regexp bound 1))
9934 ((< count 0)
9935 '(js-re-search-backward-inner regexp bound (- count)))
9936 ((> count 0)
9937 '(js-re-search-forward-inner regexp bound count)))))
9938 (condition-case err
9939 (eval search-expr)
9940 (search-failed
9941 (goto-char saved-point)
9942 (unless noerror
9943 (error (error-message-string err)))))))
9944
9945 (defun js-re-search-backward-inner (regexp &optional bound count)
9946 "Auxiliary function for `js-re-search-backward'."
9947 (let ((parse)
9948 (saved-point (point-min)))
9949 (while (> count 0)
9950 (re-search-backward regexp bound)
9951 (setq parse (parse-partial-sexp saved-point (point)))
9952 (cond ((nth 3 parse)
9953 (re-search-backward
9954 (concat "\\([^\\]\\|^\\)" (string (nth 3 parse)))
9955 (save-excursion (beginning-of-line) (point)) t))
9956 ((nth 7 parse)
9957 (goto-char (nth 8 parse)))
9958 ((or (nth 4 parse)
9959 (and (eq (char-before) ?/) (eq (char-after) ?*)))
9960 (re-search-backward "/\\*"))
9961 (t
9962 (setq count (1- count))))))
9963 (point))
9964
9965 (defun js-re-search-backward (regexp &optional bound noerror count)
9966 "Search backward but ignore strings and comments. Invokes
9967 `re-search-backward' but treats the buffer as if strings and
9968 comments have been removed."
9969 (let ((saved-point (point))
9970 (search-expr
9971 (cond ((null count)
9972 '(js-re-search-backward-inner regexp bound 1))
9973 ((< count 0)
9974 '(js-re-search-forward-inner regexp bound (- count)))
9975 ((> count 0)
9976 '(js-re-search-backward-inner regexp bound count)))))
9977 (condition-case err
9978 (eval search-expr)
9979 (search-failed
9980 (goto-char saved-point)
9981 (unless noerror
9982 (error (error-message-string err)))))))
9983
9984 (defun js-looking-at-operator-p ()
9985 "Return non-nil if text after point is an operator (that is not
9986 a comma)."
9987 (save-match-data
9988 (and (looking-at js-indent-operator-re)
9989 (or (not (looking-at ":"))
9990 (save-excursion
9991 (and (js-re-search-backward "[?:{]\\|\\<case\\>" nil t)
9992 (looking-at "?")))))))
9993
9994 (defun js-continued-expression-p ()
9995 "Returns non-nil if the current line continues an expression."
9996 (save-excursion
9997 (back-to-indentation)
9998 (or (js-looking-at-operator-p)
9999 (and (js-re-search-backward "\n" nil t)
10000 (progn
10001 (skip-chars-backward " \t")
10002 (backward-char)
10003 (and (js-looking-at-operator-p)
10004 (and (progn (backward-char)
10005 (not (looking-at "\\*\\|++\\|--\\|/[/*]"))))))))))
10006
10007 (defun js-end-of-do-while-loop-p ()
10008 "Returns non-nil if word after point is `while' of a do-while
10009 statement, else returns nil. A braceless do-while statement
10010 spanning several lines requires that the start of the loop is
10011 indented to the same column as the current line."
10012 (interactive)
10013 (save-excursion
10014 (save-match-data
10015 (when (looking-at "\\s-*\\<while\\>")
10016 (if (save-excursion
10017 (skip-chars-backward "[ \t\n]*}")
10018 (looking-at "[ \t\n]*}"))
10019 (save-excursion
10020 (backward-list) (backward-word 1) (looking-at "\\<do\\>"))
10021 (js-re-search-backward "\\<do\\>" (point-at-bol) t)
10022 (or (looking-at "\\<do\\>")
10023 (let ((saved-indent (current-indentation)))
10024 (while (and (js-re-search-backward "^[ \t]*\\<" nil t)
10025 (/= (current-indentation) saved-indent)))
10026 (and (looking-at "[ \t]*\\<do\\>")
10027 (not (js-re-search-forward
10028 "\\<while\\>" (point-at-eol) t))
10029 (= (current-indentation) saved-indent)))))))))
10030
10031 (defun js-ctrl-statement-indentation ()
10032 "Returns the proper indentation of the current line if it
10033 starts the body of a control statement without braces, else
10034 returns nil."
10035 (let (forward-sexp-function) ; temporarily unbind it
10036 (save-excursion
10037 (back-to-indentation)
10038 (when (save-excursion
10039 (and (not (js2-same-line (point-min)))
10040 (not (looking-at "{"))
10041 (js-re-search-backward "[[:graph:]]" nil t)
10042 (not (looking-at "[{([]"))
10043 (progn
10044 (forward-char)
10045 ;; scan-sexps sometimes throws an error
10046 (ignore-errors (backward-sexp))
10047 (when (looking-at "(") (backward-word 1))
10048 (and (save-excursion
10049 (skip-chars-backward " \t}" (point-at-bol))
10050 (bolp))
10051 (looking-at js-possibly-braceless-keyword-re)
10052 (not (js-end-of-do-while-loop-p))))))
10053 (save-excursion
10054 (goto-char (match-beginning 0))
10055 (+ (current-indentation) js2-basic-offset))))))
10056
10057 (defun js2-indent-in-array-comp (parse-status)
10058 "Return non-nil if we think we're in an array comprehension.
10059 In particular, return the buffer position of the first `for' kwd."
10060 (let ((end (point)))
10061 (when (nth 1 parse-status)
10062 (save-excursion
10063 (goto-char (nth 1 parse-status))
10064 (when (looking-at "\\[")
10065 (forward-char 1)
10066 (js2-forward-sws)
10067 (if (looking-at "[[{]")
10068 (let (forward-sexp-function) ; use lisp version
10069 (forward-sexp) ; skip destructuring form
10070 (js2-forward-sws)
10071 (if (and (/= (char-after) ?,) ; regular array
10072 (looking-at "for"))
10073 (match-beginning 0)))
10074 ;; to skip arbitrary expressions we need the parser,
10075 ;; so we'll just guess at it.
10076 (if (re-search-forward "[^,]* \\(for\\) " end t)
10077 (match-beginning 1))))))))
10078
10079 (defun js2-array-comp-indentation (parse-status for-kwd)
10080 (if (js2-same-line for-kwd)
10081 ;; first continuation line
10082 (save-excursion
10083 (goto-char (nth 1 parse-status))
10084 (forward-char 1)
10085 (skip-chars-forward " \t")
10086 (current-column))
10087 (save-excursion
10088 (goto-char for-kwd)
10089 (current-column))))
10090
10091 (defun js-proper-indentation (parse-status)
10092 "Return the proper indentation for the current line."
10093 (save-excursion
10094 (back-to-indentation)
10095 (let ((ctrl-stmt-indent (js-ctrl-statement-indentation))
10096 (same-indent-p (looking-at "[]})]\\|\\<case\\>\\|\\<default\\>"))
10097 (continued-expr-p (js-continued-expression-p))
10098 (bracket (nth 1 parse-status))
10099 beg)
10100 (cond
10101 ;; indent array comprehension continuation lines specially
10102 ((and bracket
10103 (not (js2-same-line bracket))
10104 (setq beg (js2-indent-in-array-comp parse-status))
10105 (>= (point) (save-excursion
10106 (goto-char beg)
10107 (point-at-bol)))) ; at or after first loop?
10108 (js2-array-comp-indentation parse-status beg))
10109
10110 (ctrl-stmt-indent)
10111
10112 (bracket
10113 (goto-char bracket)
10114 (cond
10115 ((looking-at "[({[][ \t]*\\(/[/*]\\|$\\)")
10116 (let ((p (parse-partial-sexp (point-at-bol) (point))))
10117 (when (save-excursion (skip-chars-backward " \t)")
10118 (looking-at ")"))
10119 (backward-list))
10120 (if (nth 1 p)
10121 (progn (goto-char (1+ (nth 1 p)))
10122 (skip-chars-forward " \t"))
10123 (back-to-indentation))
10124 (cond (same-indent-p
10125 (current-column))
10126 (continued-expr-p
10127 (+ (current-column) (* 2 js2-basic-offset)))
10128 (t
10129 (+ (current-column) js2-basic-offset)))))
10130 (t
10131 (unless same-indent-p
10132 (forward-char)
10133 (skip-chars-forward " \t"))
10134 (current-column))))
10135
10136 (continued-expr-p js2-basic-offset)
10137 (t 0)))))
10138
10139 (defun js2-lineup-comment (parse-status)
10140 "Indent a multi-line block comment continuation line."
10141 (let* ((beg (nth 8 parse-status))
10142 (first-line (js2-same-line beg))
10143 (offset (save-excursion
10144 (goto-char beg)
10145 (if (looking-at "/\\*")
10146 (+ 1 (current-column))
10147 0))))
10148 (unless first-line
10149 (indent-line-to offset))))
10150
10151 (defun js2-backward-sws ()
10152 "Move backward through whitespace and comments."
10153 (interactive)
10154 (while (forward-comment -1)))
10155
10156 (defun js2-forward-sws ()
10157 "Move forward through whitespace and comments."
10158 (interactive)
10159 (while (forward-comment 1)))
10160
10161 (defsubst js2-current-indent (&optional pos)
10162 "Return column of indentation on current line.
10163 If POS is non-nil, go to that point and return indentation for that line."
10164 (save-excursion
10165 (if pos
10166 (goto-char pos))
10167 (back-to-indentation)
10168 (current-column)))
10169
10170 (defsubst js2-arglist-close ()
10171 "Return non-nil if we're on a line beginning with a close-paren/brace."
10172 (save-match-data
10173 (save-excursion
10174 (goto-char (point-at-bol))
10175 (js2-forward-sws)
10176 (looking-at "[])}]"))))
10177
10178 (defsubst js2-indent-looks-like-label-p ()
10179 (goto-char (point-at-bol))
10180 (js2-forward-sws)
10181 (looking-at (concat js2-mode-identifier-re ":")))
10182
10183 (defun js2-indent-in-objlit-p (parse-status)
10184 "Return non-nil if this looks like an object-literal entry."
10185 (let ((start (nth 1 parse-status)))
10186 (and
10187 start
10188 (save-excursion
10189 (and (zerop (forward-line -1))
10190 (not (< (point) start)) ; crossed a {} boundary
10191 (js2-indent-looks-like-label-p)))
10192 (save-excursion
10193 (js2-indent-looks-like-label-p)))))
10194
10195 ;; if prev line looks like foobar({ then we're passing an object
10196 ;; literal to a function call, and people pretty much always want to
10197 ;; de-dent back to the previous line, so move the 'basic-offset'
10198 ;; position to the front.
10199 (defsubst js2-indent-objlit-arg-p (parse-status)
10200 (save-excursion
10201 (back-to-indentation)
10202 (js2-backward-sws)
10203 (and (eq (1- (point)) (nth 1 parse-status))
10204 (eq (char-before) ?{)
10205 (progn
10206 (forward-char -1)
10207 (skip-chars-backward " \t")
10208 (eq (char-before) ?\()))))
10209
10210 (defsubst js2-indent-case-block-p ()
10211 (save-excursion
10212 (back-to-indentation)
10213 (js2-backward-sws)
10214 (goto-char (point-at-bol))
10215 (skip-chars-forward " \t")
10216 (save-match-data
10217 (looking-at "case\\s-.+:"))))
10218
10219 (defsubst js2-syntax-bol ()
10220 "Return the point at the first non-whitespace char on the line.
10221 Returns `point-at-bol' if the line is empty."
10222 (save-excursion
10223 (beginning-of-line)
10224 (skip-chars-forward " \t")
10225 (point)))
10226
10227 (defun js2-bounce-indent (normal-col parse-status)
10228 "Cycle among alternate computed indentation positions.
10229 PARSE-STATUS is the result of `parse-partial-sexp' from the beginning
10230 of the buffer to the current point. NORMAL-COL is the indentation
10231 column computed by the heuristic guesser based on current paren,
10232 bracket, brace and statement nesting."
10233 (let ((cur-indent (js2-current-indent))
10234 (old-buffer-undo-list buffer-undo-list)
10235 ;; Emacs 21 only has `count-lines', not `line-number-at-pos'
10236 (current-line (save-excursion
10237 (forward-line 0) ; move to bol
10238 (1+ (count-lines (point-min) (point)))))
10239 positions
10240 pos
10241 anchor
10242 arglist-cont
10243 same-indent
10244 prev-line-col
10245 basic-offset
10246 computed-pos)
10247 ;; temporarily don't record undo info, if user requested this
10248 (if js2-mode-indent-inhibit-undo
10249 (setq buffer-undo-list t))
10250 (unwind-protect
10251 (progn
10252 ;; first likely point: indent from beginning of previous code line
10253 (push (setq basic-offset
10254 (+ (save-excursion
10255 (back-to-indentation)
10256 (js2-backward-sws)
10257 (back-to-indentation)
10258 (setq prev-line-col (current-column)))
10259 js2-basic-offset))
10260 positions)
10261
10262 ;; second likely point: indent from assign-expr RHS. This
10263 ;; is just a crude guess based on finding " = " on the previous
10264 ;; line containing actual code.
10265 (setq pos (save-excursion
10266 (save-match-data
10267 (forward-line -1)
10268 (goto-char (point-at-bol))
10269 (when (re-search-forward "\\s-+\\(=\\)\\s-+"
10270 (point-at-eol) t)
10271 (goto-char (match-end 1))
10272 (skip-chars-forward " \t\r\n")
10273 (current-column)))))
10274 (when pos
10275 (incf pos js2-basic-offset)
10276 (unless (member pos positions)
10277 (push pos positions)))
10278
10279 ;; third likely point: same indent as previous line of code.
10280 ;; Make it the first likely point if we're not on an
10281 ;; arglist-close line and previous line ends in a comma, or
10282 ;; both this line and prev line look like object-literal
10283 ;; elements.
10284 (setq pos (save-excursion
10285 (goto-char (point-at-bol))
10286 (js2-backward-sws)
10287 (back-to-indentation)
10288 (prog1
10289 (current-column)
10290 ;; while we're here, look for trailing comma
10291 (if (save-excursion
10292 (goto-char (point-at-eol))
10293 (js2-backward-sws)
10294 (eq (char-before) ?,))
10295 (setq arglist-cont (1- (point)))))))
10296 (when pos
10297 (if (and (or arglist-cont
10298 (js2-indent-in-objlit-p parse-status))
10299 (not (js2-arglist-close)))
10300 (setq same-indent pos))
10301 (unless (member pos positions)
10302 (push pos positions)))
10303
10304 ;; fourth likely position: first preceding code with less indentation
10305 ;; than the immediately preceding code line.
10306 (setq pos (save-excursion
10307 (js2-backward-sws)
10308 (back-to-indentation)
10309 (setq anchor (current-column))
10310 (while (and (zerop (forward-line -1))
10311 (>= (progn
10312 (back-to-indentation)
10313 (current-column))
10314 anchor)))
10315 (setq pos (current-column))))
10316 (unless (member pos positions)
10317 (push pos positions))
10318
10319 ;; put nesting-heuristic position first in list, sort rest
10320 (setq positions (nreverse (sort positions '<)))
10321 (setq positions (cons normal-col (delete normal-col positions)))
10322
10323 ;; comma-list continuation lines: prev line indent takes precedence
10324 (if same-indent
10325 (setq positions
10326 (cons same-indent
10327 (sort (delete same-indent positions) '<))))
10328
10329 ;; common special cases where we want to indent in from previous line
10330 (if (or (js2-indent-case-block-p)
10331 (js2-indent-objlit-arg-p parse-status))
10332 (setq positions
10333 (cons basic-offset
10334 (delete basic-offset positions))))
10335
10336 ;; record whether we're already sitting on one of the alternatives
10337 (setq pos (member cur-indent positions))
10338 (cond
10339 ;; case 0: we're one one of the alternatives and this is the
10340 ;; first time they've pressed TAB on this line (best-guess).
10341 ((and js2-mode-indent-ignore-first-tab
10342 pos
10343 ;; first time pressing TAB on this line?
10344 (not (eq js2-mode-last-indented-line current-line)))
10345 ;; do nothing
10346 (setq computed-pos nil))
10347 ;; case 1: only one computed position => use it
10348 ((null (cdr positions))
10349 (setq computed-pos 0))
10350 ;; case 2: not on any of the computed spots => use main spot
10351 ((not pos)
10352 (setq computed-pos 0))
10353 ;; case 3: on last position: cycle to first position
10354 ((null (cdr pos))
10355 (setq computed-pos 0))
10356 ;; case 4: on intermediate position: cycle to next position
10357 (t
10358 (setq computed-pos (js2-position (second pos) positions))))
10359
10360 ;; see if any hooks want to indent; otherwise we do it
10361 (loop with result = nil
10362 for hook in js2-indent-hook
10363 while (null result)
10364 do
10365 (setq result (funcall hook positions computed-pos))
10366 finally do
10367 (unless (or result (null computed-pos))
10368 (indent-line-to (nth computed-pos positions)))))
10369
10370 ;; finally
10371 (if js2-mode-indent-inhibit-undo
10372 (setq buffer-undo-list old-buffer-undo-list))
10373 ;; see commentary for `js2-mode-last-indented-line'
10374 (setq js2-mode-last-indented-line current-line))))
10375
10376 (defsubst js2-1-line-comment-continuation-p ()
10377 "Return t if we're in a 1-line comment continuation.
10378 If so, we don't ever want to use bounce-indent."
10379 (save-excursion
10380 (save-match-data
10381 (and (progn
10382 (forward-line 0)
10383 (looking-at "\\s-*//"))
10384 (progn
10385 (forward-line -1)
10386 (forward-line 0)
10387 (when (looking-at "\\s-*$")
10388 (js2-backward-sws)
10389 (forward-line 0))
10390 (looking-at "\\s-*//"))))))
10391
10392 (defun js2-indent-line ()
10393 "Indent the current line as JavaScript source text."
10394 (interactive)
10395 (let (parse-status
10396 current-indent
10397 offset
10398 indent-col
10399 moved
10400 ;; don't whine about errors/warnings when we're indenting.
10401 ;; This has to be set before calling parse-partial-sexp below.
10402 (inhibit-point-motion-hooks t))
10403 (setq parse-status (save-excursion
10404 (parse-partial-sexp (point-min)
10405 (point-at-bol)))
10406 offset (- (point) (save-excursion
10407 (back-to-indentation)
10408 (setq current-indent (current-column))
10409 (point))))
10410 (js2-with-underscore-as-word-syntax
10411 (if (nth 4 parse-status)
10412 (js2-lineup-comment parse-status)
10413 (setq indent-col (js-proper-indentation parse-status))
10414 ;; see comments below about js2-mode-last-indented-line
10415 (when
10416 (cond
10417 ;; bounce-indenting is disabled during electric-key indent.
10418 ;; It doesn't work well on first line of buffer.
10419 ((and js2-bounce-indent-flag
10420 (not (js2-same-line (point-min)))
10421 (not (js2-1-line-comment-continuation-p)))
10422 (js2-bounce-indent indent-col parse-status)
10423 (setq moved t))
10424 ;; just indent to the guesser's likely spot
10425 ((/= current-indent indent-col)
10426 (indent-line-to indent-col)
10427 (setq moved t)))
10428 (when (and moved (plusp offset))
10429 (forward-char offset)))))))
10430
10431 (defun js2-indent-region (start end)
10432 "Indent the region, but don't use bounce indenting."
10433 (let ((js2-bounce-indent-flag nil)
10434 (indent-region-function nil))
10435 (indent-region start end nil))) ; nil for byte-compiler
10436
10437 (provide 'js2-indent)
10438
10439 ;;; js2-indent.el ends here
10440
10441 (eval-when-compile
10442 (require 'cl))
10443
10444 (require 'imenu)
10445 (require 'cc-cmds) ; for `c-fill-paragraph'
10446
10447
10448 ;;;###autoload (add-to-list 'auto-mode-alist '("\\.js$" . js2-mode))
10449
10450 ;;;###autoload
10451 (defun js2-mode ()
10452 "Major mode for editing JavaScript code."
10453 (interactive)
10454 (js2-mode-check-compat)
10455 (kill-all-local-variables)
10456 (set-syntax-table js2-mode-syntax-table)
10457 (use-local-map js2-mode-map)
10458 (setq major-mode 'js2-mode
10459 mode-name "JavaScript-IDE"
10460 comment-start "//" ; used by comment-region; don't change it
10461 comment-end "")
10462 (setq local-abbrev-table js2-mode-abbrev-table)
10463 (set (make-local-variable 'max-lisp-eval-depth)
10464 (max max-lisp-eval-depth 3000))
10465 (set (make-local-variable 'indent-line-function) #'js2-indent-line)
10466 (set (make-local-variable 'indent-region-function) #'js2-indent-region)
10467 (set (make-local-variable 'fill-paragraph-function) #'js2-fill-paragraph)
10468 (set (make-local-variable 'before-save-hook) #'js2-before-save)
10469 (set (make-local-variable 'next-error-function) #'js2-next-error)
10470 (set (make-local-variable 'beginning-of-defun-function) #'js2-beginning-of-defun)
10471 (set (make-local-variable 'end-of-defun-function) #'js2-end-of-defun)
10472 ;; We un-confuse `parse-partial-sexp' by setting syntax-table properties
10473 ;; for characters inside regexp literals.
10474 (set (make-local-variable 'parse-sexp-lookup-properties) t)
10475 ;; this is necessary to make `show-paren-function' work properly
10476 (set (make-local-variable 'parse-sexp-ignore-comments) t)
10477 ;; needed for M-x rgrep, among other things
10478 (put 'js2-mode 'find-tag-default-function #'js2-mode-find-tag)
10479
10480 ;; some variables needed by cc-engine for paragraph-fill, etc.
10481 (setq c-buffer-is-cc-mode t
10482 c-comment-prefix-regexp js2-comment-prefix-regexp
10483 c-paragraph-start js2-paragraph-start
10484 c-paragraph-separate "$"
10485 comment-start-skip js2-comment-start-skip
10486 c-syntactic-ws-start js2-syntactic-ws-start
10487 c-syntactic-ws-end js2-syntactic-ws-end
10488 c-syntactic-eol js2-syntactic-eol)
10489 (if js2-emacs22
10490 (c-setup-paragraph-variables))
10491
10492 ;; We do our own syntax highlighting based on the parse tree.
10493 ;; However, we want minor modes that add keywords to highlight properly
10494 ;; (examples: doxymacs, column-marker). We do this by not letting
10495 ;; font-lock unfontify anything, and telling it to fontify after we
10496 ;; re-parse and re-highlight the buffer. (We currently don't do any
10497 ;; work with regions other than the whole buffer.)
10498 (dolist (var '(font-lock-unfontify-buffer-function
10499 font-lock-unfontify-region-function))
10500 (set (make-local-variable var) (lambda (&rest args) t)))
10501
10502 ;; Don't let font-lock do syntactic (string/comment) fontification.
10503 (set (make-local-variable #'font-lock-syntactic-face-function)
10504 (lambda (state) nil))
10505
10506 ;; Experiment: make reparse-delay longer for longer files.
10507 (if (plusp js2-dynamic-idle-timer-adjust)
10508 (setq js2-idle-timer-delay
10509 (* js2-idle-timer-delay
10510 (/ (point-max) js2-dynamic-idle-timer-adjust))))
10511
10512 (add-hook 'change-major-mode-hook #'js2-mode-exit nil t)
10513 (add-hook 'after-change-functions #'js2-mode-edit nil t)
10514 (setq imenu-create-index-function #'js2-mode-create-imenu-index)
10515 (imenu-add-to-menubar (concat "IM-" mode-name))
10516 (when js2-mirror-mode
10517 (js2-enter-mirror-mode))
10518 (add-to-invisibility-spec '(js2-outline . t))
10519 (set (make-local-variable 'line-move-ignore-invisible) t)
10520 (set (make-local-variable 'forward-sexp-function) #'js2-mode-forward-sexp)
10521 (setq js2-mode-functions-hidden nil
10522 js2-mode-comments-hidden nil
10523 js2-mode-buffer-dirty-p t
10524 js2-mode-parsing nil)
10525 (js2-reparse)
10526 (run-hooks 'js2-mode-hook))
10527
10528 (defun js2-mode-check-compat ()
10529 "Signal an error if we can't run with this version of Emacs."
10530 (if (and js2-mode-must-byte-compile
10531 (not (byte-code-function-p (symbol-function 'js2-mode))))
10532 (error "You must byte-compile js2-mode before using it."))
10533 (if (and (boundp 'running-xemacs)
10534 running-xemacs)
10535 (error "js2-mode is not compatible with XEmacs"))
10536 (unless (>= emacs-major-version 21)
10537 (error "js2-mode requires GNU Emacs version 21 or higher")))
10538
10539 (defun js2-mode-exit ()
10540 (interactive)
10541 (when js2-mode-node-overlay
10542 (delete-overlay js2-mode-node-overlay)
10543 (setq js2-mode-node-overlay nil))
10544 (js2-remove-overlays)
10545 (setq js2-mode-ast nil)
10546 (remove-hook 'change-major-mode-hook #'js2-mode-exit t)
10547 (remove-from-invisibility-spec '(js2-outline . t))
10548 (js2-mode-show-all)
10549 (js2-with-unmodifying-text-property-changes
10550 (js2-clear-face (point-min) (point-max))))
10551
10552 (defun js2-before-save ()
10553 "Clean up whitespace before saving file.
10554 You can disable this by customizing `js2-cleanup-whitespace'."
10555 (when js2-cleanup-whitespace
10556 (let ((col (current-column)))
10557 (delete-trailing-whitespace)
10558 ;; don't change trailing whitespace on current line
10559 (unless (eq (current-column) col)
10560 (indent-to col)))))
10561
10562 (defsubst js2-mode-reset-timer ()
10563 (if js2-mode-parse-timer
10564 (cancel-timer js2-mode-parse-timer))
10565 (setq js2-mode-parsing nil)
10566 (setq js2-mode-parse-timer
10567 (run-with-idle-timer js2-idle-timer-delay nil #'js2-reparse)))
10568
10569 (defun js2-mode-edit (beg end len)
10570 "Schedule a new parse after buffer is edited."
10571 (setq js2-mode-buffer-dirty-p t)
10572 (js2-mode-hide-overlay)
10573 (js2-mode-reset-timer))
10574
10575 (defun js2-mode-run-font-lock ()
10576 "Run `font-lock-fontify-buffer' after parsing/highlighting.
10577 This is intended to allow modes that install their own font-lock keywords
10578 to work with js2-mode. In practice it never seems to work for long.
10579 Hopefully the Emacs maintainers can help figure out a way to make it work."
10580 (when (and (boundp 'font-lock-keywords)
10581 font-lock-keywords
10582 (boundp 'font-lock-mode)
10583 font-lock-mode)
10584 ;; TODO: font-lock and jit-lock really really REALLY don't want to
10585 ;; play nicely with js2-mode. They go out of their way to fail to
10586 ;; provide any option for saying "look, fontify the goddamn buffer
10587 ;; with just the keywords already". Argh.
10588 (setq font-lock-defaults (list font-lock-keywords 'keywords-only))
10589 (let (font-lock-verbose)
10590 (font-lock-default-fontify-buffer))))
10591
10592 (defun js2-reparse (&optional force)
10593 "Re-parse current buffer after user finishes some data entry.
10594 If we get any user input while parsing, including cursor motion,
10595 we discard the parse and reschedule it. If FORCE is nil, then the
10596 buffer will only rebuild its `js2-mode-ast' if the buffer is dirty."
10597 (let (time
10598 interrupted-p
10599 (js2-compiler-strict-mode js2-mode-show-strict-warnings))
10600 (unless js2-mode-parsing
10601 (setq js2-mode-parsing t)
10602 (unwind-protect
10603 (when (or js2-mode-buffer-dirty-p force)
10604 (js2-remove-overlays)
10605 (js2-with-unmodifying-text-property-changes
10606 (setq js2-mode-buffer-dirty-p nil
10607 js2-mode-fontifications nil
10608 js2-mode-deferred-properties nil)
10609 (if js2-mode-verbose-parse-p
10610 (message "parsing..."))
10611 (setq time
10612 (js2-time
10613 (setq interrupted-p
10614 (catch 'interrupted
10615 (setq js2-mode-ast (js2-parse))
10616 (js2-mode-fontify-regions)
10617 (js2-mode-remove-suppressed-warnings)
10618 (js2-mode-show-warnings)
10619 (js2-mode-show-errors)
10620 (js2-mode-run-font-lock) ; note: doesn't work
10621 (if (>= js2-highlight-level 1)
10622 (js2-highlight-jsdoc js2-mode-ast))
10623 nil))))
10624 (if interrupted-p
10625 (progn
10626 ;; unfinished parse => try again
10627 (setq js2-mode-buffer-dirty-p t)
10628 (js2-mode-reset-timer))
10629 (if js2-mode-verbose-parse-p
10630 (message "Parse time: %s" time)))))
10631 ;; finally
10632 (setq js2-mode-parsing nil)
10633 (unless interrupted-p
10634 (setq js2-mode-parse-timer nil))))))
10635
10636 (defun js2-mode-show-node ()
10637 "Debugging aid: highlight selected AST node on mouse click."
10638 (interactive)
10639 (let ((node (js2-node-at-point))
10640 beg
10641 end)
10642 (when js2-mode-show-overlay
10643 (if (null node)
10644 (message "No node found at location %s" (point))
10645 (setq beg (js2-node-abs-pos node)
10646 end (+ beg (js2-node-len node)))
10647 (if js2-mode-node-overlay
10648 (move-overlay js2-mode-node-overlay beg end)
10649 (setq js2-mode-node-overlay (make-overlay beg end))
10650 (overlay-put js2-mode-node-overlay 'face 'highlight))
10651 (js2-with-unmodifying-text-property-changes
10652 (put-text-property beg end 'point-left #'js2-mode-hide-overlay))
10653 (message "%s, parent: %s"
10654 (js2-node-short-name node)
10655 (if (js2-node-parent node)
10656 (js2-node-short-name (js2-node-parent node))
10657 "nil"))))))
10658
10659 (defun js2-mode-hide-overlay (&optional p1 p2)
10660 "Remove the debugging overlay when the point moves."
10661 (when js2-mode-node-overlay
10662 (let ((beg (overlay-start js2-mode-node-overlay))
10663 (end (overlay-end js2-mode-node-overlay)))
10664 ;; Sometimes we're called spuriously.
10665 (unless (and p2
10666 (>= p2 beg)
10667 (<= p2 end))
10668 (js2-with-unmodifying-text-property-changes
10669 (remove-text-properties beg end '(point-left nil)))
10670 (delete-overlay js2-mode-node-overlay)
10671 (setq js2-mode-node-overlay nil)))))
10672
10673 (defun js2-mode-reset ()
10674 "Debugging helper; resets everything."
10675 (interactive)
10676 (js2-mode-exit)
10677 (js2-mode))
10678
10679 (defsubst js2-mode-show-warn-or-err (e face)
10680 "Highlight a warning or error E with FACE.
10681 E is a list of ((MSG-KEY MSG-ARG) BEG END)."
10682 (let* ((key (first e))
10683 (beg (second e))
10684 (end (+ beg (third e)))
10685 ;; Don't inadvertently go out of bounds.
10686 (beg (max (point-min) (min beg (point-max))))
10687 (end (max (point-min) (min end (point-max))))
10688 (js2-highlight-level 3) ; so js2-set-face is sure to fire
10689 (ovl (make-overlay beg end)))
10690 (overlay-put ovl 'face face)
10691 (overlay-put ovl 'js2 t)
10692 (put-text-property beg end 'help-echo (js2-get-msg key))
10693 (put-text-property beg end 'point-entered #'js2-echo-error)))
10694
10695 (defun js2-remove-overlays ()
10696 "Remove overlays from buffer that have a `js2' property."
10697 (let ((beg (point-min))
10698 (end (point-max)))
10699 (save-excursion
10700 (dolist (o (overlays-in beg end))
10701 (when (overlay-get o 'js2)
10702 (delete-overlay o))))))
10703
10704 (defun js2-mode-fontify-regions ()
10705 "Apply fontifications recorded during parsing."
10706 ;; We defer clearing faces as long as possible to eliminate flashing.
10707 (js2-clear-face (point-min) (point-max))
10708 ;; have to reverse the recorded fontifications so that errors and
10709 ;; warnings overwrite the normal fontifications
10710 (dolist (f (nreverse js2-mode-fontifications))
10711 (put-text-property (first f) (second f) 'face (third f)))
10712 (setq js2-mode-fontifications nil)
10713 (dolist (p js2-mode-deferred-properties)
10714 (apply #'put-text-property p))
10715 (setq js2-mode-deferred-properties nil))
10716
10717 (defun js2-mode-show-errors ()
10718 "Highlight syntax errors."
10719 (when js2-mode-show-parse-errors
10720 (dolist (e (js2-ast-root-errors js2-mode-ast))
10721 (js2-mode-show-warn-or-err e 'js2-error-face))))
10722
10723 (defun js2-mode-remove-suppressed-warnings ()
10724 "Take suppressed warnings out of the AST warnings list.
10725 This ensures that the counts and `next-error' are correct."
10726 (setf (js2-ast-root-warnings js2-mode-ast)
10727 (js2-delete-if
10728 (lambda (e)
10729 (let ((key (caar e)))
10730 (or
10731 (and (not js2-strict-trailing-comma-warning)
10732 (string-match "trailing\\.comma" key))
10733 (and (not js2-strict-cond-assign-warning)
10734 (string= key "msg.equal.as.assign"))
10735 (and js2-missing-semi-one-line-override
10736 (string= key "msg.missing.semi")
10737 (let* ((beg (second e))
10738 (node (js2-node-at-point beg))
10739 (fn (js2-mode-find-parent-fn node))
10740 (body (and fn (js2-function-node-body fn)))
10741 (lc (and body (js2-node-abs-pos body)))
10742 (rc (and lc (+ lc (js2-node-len body)))))
10743 (and fn
10744 (or (null body)
10745 (save-excursion
10746 (goto-char beg)
10747 (and (js2-same-line lc)
10748 (js2-same-line rc))))))))))
10749 (js2-ast-root-warnings js2-mode-ast))))
10750
10751 (defun js2-mode-show-warnings ()
10752 "Highlight strict-mode warnings."
10753 (when js2-mode-show-strict-warnings
10754 (dolist (e (js2-ast-root-warnings js2-mode-ast))
10755 (js2-mode-show-warn-or-err e 'js2-warning-face))))
10756
10757 (defun js2-echo-error (old-point new-point)
10758 "Called by point-motion hooks."
10759 (let ((msg (get-text-property new-point 'help-echo)))
10760 (if msg
10761 (message msg))))
10762
10763 (defalias #'js2-echo-help #'js2-echo-error)
10764
10765 (defun js2-enter-key ()
10766 "Handle user pressing the Enter key."
10767 (interactive)
10768 (let ((parse-status (save-excursion
10769 (parse-partial-sexp (point-min) (point)))))
10770 (cond
10771 ;; check if we're inside a string
10772 ((nth 3 parse-status)
10773 (js2-mode-split-string parse-status))
10774 ;; check if inside a block comment
10775 ((nth 4 parse-status)
10776 (js2-mode-extend-comment))
10777 (t
10778 ;; should probably figure out what the mode-map says we should do
10779 (if js2-indent-on-enter-key
10780 (let ((js2-bounce-indent-flag nil))
10781 (js2-indent-line)))
10782 (insert "\n")
10783 (if js2-enter-indents-newline
10784 (let ((js2-bounce-indent-flag nil))
10785 (js2-indent-line)))))))
10786
10787 (defun js2-mode-split-string (parse-status)
10788 "Turn a newline in mid-string into a string concatenation."
10789 (let* ((col (current-column))
10790 (quote-char (nth 3 parse-status))
10791 (quote-string (string quote-char))
10792 (string-beg (nth 8 parse-status))
10793 (indent (save-match-data
10794 (or
10795 (save-excursion
10796 (back-to-indentation)
10797 (if (looking-at "\\+")
10798 (current-column)))
10799 (save-excursion
10800 (goto-char string-beg)
10801 (if (looking-back "\\+\\s-+")
10802 (goto-char (match-beginning 0)))
10803 (current-column))))))
10804 (insert quote-char "\n")
10805 (indent-to indent)
10806 (insert "+ " quote-string)
10807 (when (eolp)
10808 (insert quote-string)
10809 (backward-char 1))))
10810
10811 (defun js2-mode-extend-comment ()
10812 "When inside a comment block, add comment prefix."
10813 (let (star single col first-line needs-close)
10814 (save-excursion
10815 (back-to-indentation)
10816 (cond
10817 ((looking-at "\\*[^/]")
10818 (setq star t
10819 col (current-column)))
10820 ((looking-at "/\\*")
10821 (setq star t
10822 first-line t
10823 col (1+ (current-column))))
10824 ((looking-at "//")
10825 (setq single t
10826 col (current-column)))))
10827 ;; Heuristic for whether we need to close the comment:
10828 ;; if we've got a parse error here, assume it's an unterminated
10829 ;; comment.
10830 (setq needs-close
10831 (or
10832 (eq (get-text-property (1- (point)) 'point-entered)
10833 'js2-echo-error)
10834 ;; The heuristic above doesn't work well when we're
10835 ;; creating a comment and there's another one downstream,
10836 ;; as our parser thinks this one ends at the end of the
10837 ;; next one. (You can have a /* inside a js block comment.)
10838 ;; So just close it if the next non-ws char isn't a *.
10839 (and first-line
10840 (eolp)
10841 (save-excursion
10842 (skip-syntax-forward " ")
10843 (not (eq (char-after) ?*))))))
10844 (insert "\n")
10845 (cond
10846 (star
10847 (indent-to col)
10848 (insert "* ")
10849 (if (and first-line needs-close)
10850 (save-excursion
10851 (insert "\n")
10852 (indent-to col)
10853 (insert "*/"))))
10854 (single
10855 (when (save-excursion
10856 (and (zerop (forward-line 1))
10857 (looking-at "\\s-*//")))
10858 (indent-to col)
10859 (insert "// "))))))
10860
10861 (defun js2-fill-string (beg quote)
10862 "Line-wrap a single-line string into a multi-line string.
10863 BEG is the string beginning, QUOTE is the quote char."
10864 (let* ((squote (string quote))
10865 (end (if (re-search-forward (concat "[^\\]" squote)
10866 (point-at-eol) t)
10867 (1+ (match-beginning 0))
10868 (point-at-eol)))
10869 (tag (make-marker))
10870 (fill-column (- fill-column 4))) ; make room
10871 (unwind-protect
10872 (progn
10873 (move-marker tag end)
10874 (fill-paragraph nil)
10875 (goto-char beg)
10876 (while (not (js2-same-line tag))
10877 (goto-char (point-at-eol))
10878 (insert squote)
10879 (when (zerop (forward-line 1))
10880 (back-to-indentation)
10881 (if (looking-at (concat squote "\\s-*$"))
10882 (progn
10883 (setq end (point-at-eol))
10884 (forward-line -1)
10885 (delete-region (point-at-eol) end))
10886 (insert "+ " squote)))))
10887 (move-marker tag nil))))
10888
10889 (defun js2-fill-paragraph (arg)
10890 "Fill paragraph after point. Prefix ARG means justify as well.
10891 Has special handling for filling in comments and strings."
10892 (let* ((parse-status (save-excursion
10893 (parse-partial-sexp (point-min) (point))))
10894 (quote-char (or (nth 3 parse-status)
10895 (save-match-data
10896 (if (looking-at "[\"\']")
10897 (char-after))))))
10898 (cond
10899 (quote-char
10900 (js2-fill-string (or (nth 8 parse-status)
10901 (point))
10902 quote-char)
10903 t) ; or fill-paragraph does evil things afterwards
10904 ((nth 4 parse-status) ; in block comment?
10905 (js2-fill-comment parse-status arg))
10906 (t
10907 (fill-paragraph arg)))))
10908
10909 (defun js2-fill-comment (parse-status arg)
10910 "Fill-paragraph in a block comment."
10911 (let* ((beg (nth 8 parse-status))
10912 (end (save-excursion
10913 (goto-char beg)
10914 (re-search-forward "[^\\]\\*/" nil t)))
10915 indent
10916 end-marker)
10917 (when end
10918 (setq end-marker (make-marker))
10919 (move-marker end-marker end))
10920 (when (and end js2-mode-squeeze-spaces)
10921 (save-excursion
10922 (save-restriction
10923 (narrow-to-region beg end)
10924 (goto-char (point-min))
10925 (while (re-search-forward "[ \t][ \t]+" nil t)
10926 (replace-match " ")))))
10927 ;; `c-fill-paragraph' doesn't indent the continuation stars properly
10928 ;; if the comment isn't left-justified. They align to the first star
10929 ;; on the first continuation line after the comment-open, so we make
10930 ;; sure the first continuation line has the proper indentation.
10931 (save-excursion
10932 (goto-char beg)
10933 (setq indent (1+ (current-column)))
10934 (goto-char (point-at-eol))
10935 (skip-chars-forward " \t\r\n")
10936 (indent-line-to indent)
10937
10938 ;; Invoke `c-fill-paragraph' from the first continuation line,
10939 ;; since it provides better results. Otherwise if you're on the
10940 ;; last line, it doesn't prefix with stars the way you'd expect.
10941 ;; TODO: write our own fill function that works in Emacs 21
10942 (c-fill-paragraph arg))
10943
10944 ;; last line is typically indented wrong, so fix it
10945 (when end-marker
10946 (save-excursion
10947 (goto-char end-marker)
10948 (js2-indent-line)))))
10949
10950 (defun js2-beginning-of-line ()
10951 "Toggles point between bol and first non-whitespace char in line.
10952 Also moves past comment delimiters when inside comments."
10953 (interactive)
10954 (let (node beg)
10955 (cond
10956 ((bolp)
10957 (back-to-indentation))
10958 ((looking-at "//")
10959 (skip-chars-forward "/ \t"))
10960 ((and (eq (char-after) ?*)
10961 (setq node (js2-comment-at-point))
10962 (memq (js2-comment-node-format node) '(jsdoc block))
10963 (save-excursion
10964 (skip-chars-backward " \t")
10965 (bolp)))
10966 (skip-chars-forward "\* \t"))
10967 (t
10968 (goto-char (point-at-bol))))))
10969
10970 (defun js2-end-of-line ()
10971 "Toggles point between eol and last non-whitespace char in line."
10972 (interactive)
10973 (if (eolp)
10974 (skip-chars-backward " \t")
10975 (goto-char (point-at-eol))))
10976
10977 (defun js2-enter-mirror-mode()
10978 "Turns on mirror mode, where quotes, brackets etc are mirrored automatically
10979 on insertion."
10980 (interactive)
10981 (define-key js2-mode-map (read-kbd-macro "{") 'js2-mode-match-curly)
10982 (define-key js2-mode-map (read-kbd-macro "}") 'js2-mode-magic-close-paren)
10983 (define-key js2-mode-map (read-kbd-macro "\"") 'js2-mode-match-double-quote)
10984 (define-key js2-mode-map (read-kbd-macro "'") 'js2-mode-match-single-quote)
10985 (define-key js2-mode-map (read-kbd-macro "(") 'js2-mode-match-paren)
10986 (define-key js2-mode-map (read-kbd-macro ")") 'js2-mode-magic-close-paren)
10987 (define-key js2-mode-map (read-kbd-macro "[") 'js2-mode-match-bracket)
10988 (define-key js2-mode-map (read-kbd-macro "]") 'js2-mode-magic-close-paren))
10989
10990 (defun js2-leave-mirror-mode()
10991 "Turns off mirror mode."
10992 (interactive)
10993 (dolist (key '("{" "\"" "'" "(" ")" "[" "]"))
10994 (define-key js2-mode-map (read-kbd-macro key) 'self-insert-command)))
10995
10996 (defsubst js2-mode-inside-string ()
10997 "Return non-nil if inside a string.
10998 Actually returns the quote character that begins the string."
10999 (let ((parse-state (save-excursion
11000 (parse-partial-sexp (point-min) (point)))))
11001 (nth 3 parse-state)))
11002
11003 (defsubst js2-mode-inside-comment-or-string ()
11004 "Return non-nil if inside a comment or string."
11005 (or
11006 (let ((comment-start
11007 (save-excursion
11008 (goto-char (point-at-bol))
11009 (if (re-search-forward "//" (point-at-eol) t)
11010 (match-beginning 0)))))
11011 (and comment-start
11012 (<= comment-start (point))))
11013 (let ((parse-state (save-excursion
11014 (parse-partial-sexp (point-min) (point)))))
11015 (or (nth 3 parse-state)
11016 (nth 4 parse-state)))))
11017
11018 (defun js2-mode-match-curly (arg)
11019 "Insert matching curly-brace."
11020 (interactive "p")
11021 (insert "{")
11022 (if current-prefix-arg
11023 (save-excursion
11024 (insert "}"))
11025 (unless (or (not (looking-at "\\s-*$"))
11026 (js2-mode-inside-comment-or-string))
11027 (undo-boundary)
11028
11029 ;; absolutely mystifying bug: when inserting the next "\n",
11030 ;; the buffer-undo-list is given two new entries: the inserted range,
11031 ;; and the incorrect position of the point. It's recorded incorrectly
11032 ;; as being before the opening "{", not after it. But it's recorded
11033 ;; as the correct value if you're debugging `js2-mode-match-curly'
11034 ;; in edebug. I have no idea why it's doing this, but incrementing
11035 ;; the inserted position fixes the problem, so that the undo takes us
11036 ;; back to just after the user-inserted "{".
11037 (insert "\n")
11038 (ignore-errors
11039 (incf (cadr buffer-undo-list)))
11040
11041 (js2-indent-line)
11042 (save-excursion
11043 (insert "\n}")
11044 (let ((js2-bounce-indent-flag (js2-code-at-bol-p)))
11045 (js2-indent-line))))))
11046
11047 (defun js2-mode-match-bracket ()
11048 "Insert matching bracket."
11049 (interactive)
11050 (insert "[")
11051 (unless (or (not (looking-at "\\s-*$"))
11052 (js2-mode-inside-comment-or-string))
11053 (save-excursion
11054 (insert "]"))
11055 (when js2-auto-indent-flag
11056 (let ((js2-bounce-indent-flag (js2-code-at-bol-p)))
11057 (js2-indent-line)))))
11058
11059 (defun js2-mode-match-paren ()
11060 "Insert matching paren unless already inserted."
11061 (interactive)
11062 (insert "(")
11063 (unless (or (not (looking-at "\\s-*$"))
11064 (js2-mode-inside-comment-or-string))
11065 (save-excursion
11066 (insert ")"))
11067 (when js2-auto-indent-flag
11068 (let ((js2-bounce-indent-flag (js2-code-at-bol-p)))
11069 (js2-indent-line)))))
11070
11071 (defsubst js2-match-quote (quote-string)
11072 (let ((start-quote (js2-mode-inside-string)))
11073 (cond
11074 ;; inside a comment - don't do quote-matching, since we can't
11075 ;; reliably figure out if we're in a string inside the comment
11076 ((js2-comment-at-point)
11077 (insert quote-string))
11078 ((not start-quote)
11079 ;; not in string => insert matched quotes
11080 (insert quote-string)
11081 ;; exception: if we're just before a word, don't double it.
11082 (unless (looking-at "[^ \t\r\n]")
11083 (save-excursion
11084 (insert quote-string))))
11085 ((looking-at quote-string)
11086 (if (looking-back "[^\\]\\\\")
11087 (insert quote-string)
11088 (forward-char 1)))
11089 ((and js2-mode-escape-quotes
11090 (save-excursion
11091 (save-match-data
11092 (re-search-forward quote-string (point-at-eol) t))))
11093 ;; inside terminated string, escape quote (unless already escaped)
11094 (insert (if (looking-back "[^\\]\\\\")
11095 quote-string
11096 (concat "\\" quote-string))))
11097 (t
11098 (insert quote-string))))) ; else terminate the string
11099
11100 (defun js2-mode-match-single-quote ()
11101 "Insert matching single-quote."
11102 (interactive)
11103 (let ((parse-status (parse-partial-sexp (point-min) (point))))
11104 ;; don't match inside comments, since apostrophe is more common
11105 (if (nth 4 parse-status)
11106 (insert "'")
11107 (js2-match-quote "'"))))
11108
11109 (defun js2-mode-match-double-quote ()
11110 "Insert matching double-quote."
11111 (interactive)
11112 (js2-match-quote "\""))
11113
11114 (defun js2-mode-magic-close-paren ()
11115 "Skip over close-paren rather than inserting, where appropriate.
11116 Uses some heuristics to try to figure out the right thing to do."
11117 (interactive)
11118 (let* ((parse-status (parse-partial-sexp (point-min) (point)))
11119 (open-pos (nth 1 parse-status))
11120 (close last-input-char)
11121 (open (cond
11122 ((eq close 41) ; close-paren
11123 40) ; open-paren
11124 ((eq close 93) ; close-bracket
11125 91) ; open-bracket
11126 ((eq close ?})
11127 ?{)
11128 (t nil))))
11129 (if (and (looking-at (string close))
11130 (eq open (char-after open-pos))
11131 (js2-same-line open-pos))
11132 (forward-char 1)
11133 (insert (string close)))
11134 (blink-matching-open)))
11135
11136 (defun js2-mode-wait-for-parse (callback)
11137 "Invoke CALLBACK when parsing is finished.
11138 If parsing is already finished, calls CALLBACK immediately."
11139 (if (not js2-mode-buffer-dirty-p)
11140 (funcall callback)
11141 (push callback js2-mode-pending-parse-callbacks)
11142 (add-hook 'js2-parse-finished-hook #'js2-mode-parse-finished)))
11143
11144 (defun js2-mode-parse-finished ()
11145 "Invoke callbacks in `js2-mode-pending-parse-callbacks'."
11146 ;; We can't let errors propagate up, since it prevents the
11147 ;; `js2-parse' method from completing normally and returning
11148 ;; the ast, which makes things mysteriously not work right.
11149 (unwind-protect
11150 (dolist (cb js2-mode-pending-parse-callbacks)
11151 (condition-case err
11152 (funcall cb)
11153 (error (message "%s" err))))
11154 (setq js2-mode-pending-parse-callbacks nil)))
11155
11156 (defun js2-mode-flag-region (from to flag)
11157 "Hide or show text from FROM to TO, according to FLAG.
11158 If FLAG is nil then text is shown, while if FLAG is t the text is hidden.
11159 Returns the created overlay if FLAG is non-nil."
11160 (remove-overlays from to 'invisible 'js2-outline)
11161 (when flag
11162 (let ((o (make-overlay from to)))
11163 (overlay-put o 'invisible 'js2-outline)
11164 (overlay-put o 'isearch-open-invisible
11165 'js2-isearch-open-invisible)
11166 o)))
11167
11168 ;; Function to be set as an outline-isearch-open-invisible' property
11169 ;; to the overlay that makes the outline invisible (see
11170 ;; `js2-mode-flag-region').
11171 (defun js2-isearch-open-invisible (overlay)
11172 ;; We rely on the fact that isearch places point on the matched text.
11173 (js2-mode-show-element))
11174
11175 (defun js2-mode-invisible-overlay-bounds (&optional pos)
11176 "Return cons cell of bounds of folding overlay at POS.
11177 Returns nil if not found."
11178 (let ((overlays (overlays-at (or pos (point))))
11179 o)
11180 (while (and overlays
11181 (not o))
11182 (if (overlay-get (car overlays) 'invisible)
11183 (setq o (car overlays))
11184 (setq overlays (cdr overlays))))
11185 (if o
11186 (cons (overlay-start o) (overlay-end o)))))
11187
11188 (defun js2-mode-function-at-point (&optional pos)
11189 "Return the innermost function node enclosing current point.
11190 Returns nil if point is not in a function."
11191 (let ((node (js2-node-at-point pos)))
11192 (while (and node (not (js2-function-node-p node)))
11193 (setq node (js2-node-parent node)))
11194 (if (js2-function-node-p node)
11195 node)))
11196
11197 (defun js2-mode-toggle-element ()
11198 "Hide or show the foldable element at the point."
11199 (interactive)
11200 (let (comment fn pos)
11201 (save-excursion
11202 (save-match-data
11203 (cond
11204 ;; /* ... */ comment?
11205 ((js2-block-comment-p (setq comment (js2-comment-at-point)))
11206 (if (js2-mode-invisible-overlay-bounds
11207 (setq pos (+ 3 (js2-node-abs-pos comment))))
11208 (progn
11209 (goto-char pos)
11210 (js2-mode-show-element))
11211 (js2-mode-hide-element)))
11212
11213 ;; //-comment?
11214 ((save-excursion
11215 (back-to-indentation)
11216 (looking-at js2-mode-//-comment-re))
11217 (js2-mode-toggle-//-comment))
11218
11219 ;; function?
11220 ((setq fn (js2-mode-function-at-point))
11221 (setq pos (and (js2-function-node-body fn)
11222 (js2-node-abs-pos (js2-function-node-body fn))))
11223 (goto-char (1+ pos))
11224 (if (js2-mode-invisible-overlay-bounds)
11225 (js2-mode-show-element)
11226 (js2-mode-hide-element)))
11227 (t
11228 (message "Nothing at point to hide or show")))))))
11229
11230 (defun js2-mode-hide-element ()
11231 "Fold/hide contents of a block, showing ellipses.
11232 Show the hidden text with \\[js2-mode-show-element]."
11233 (interactive)
11234 (if js2-mode-buffer-dirty-p
11235 (js2-mode-wait-for-parse #'js2-mode-hide-element))
11236 (let (node body beg end)
11237 (cond
11238 ((js2-mode-invisible-overlay-bounds)
11239 (message "already hidden"))
11240 (t
11241 (setq node (js2-node-at-point))
11242 (cond
11243 ((js2-block-comment-p node)
11244 (js2-mode-hide-comment node))
11245 (t
11246 (while (and node (not (js2-function-node-p node)))
11247 (setq node (js2-node-parent node)))
11248 (if (and node
11249 (setq body (js2-function-node-body node)))
11250 (progn
11251 (setq beg (js2-node-abs-pos body)
11252 end (+ beg (js2-node-len body)))
11253 (js2-mode-flag-region (1+ beg) (1- end) 'hide))
11254 (message "No collapsable element found at point"))))))))
11255
11256 (defun js2-mode-show-element ()
11257 "Show the hidden element at current point."
11258 (interactive)
11259 (let ((bounds (js2-mode-invisible-overlay-bounds)))
11260 (if bounds
11261 (js2-mode-flag-region (car bounds) (cdr bounds) nil)
11262 (message "Nothing to un-hide"))))
11263
11264 (defun js2-mode-show-all ()
11265 "Show all of the text in the buffer."
11266 (interactive)
11267 (js2-mode-flag-region (point-min) (point-max) nil))
11268
11269 (defun js2-mode-toggle-hide-functions ()
11270 (interactive)
11271 (if js2-mode-functions-hidden
11272 (js2-mode-show-functions)
11273 (js2-mode-hide-functions)))
11274
11275 (defun js2-mode-hide-functions ()
11276 "Hides all non-nested function bodies in the buffer.
11277 Use \\[js2-mode-show-all] to reveal them, or \\[js2-mode-show-element]
11278 to open an individual entry."
11279 (interactive)
11280 (if js2-mode-buffer-dirty-p
11281 (js2-mode-wait-for-parse #'js2-mode-hide-functions))
11282 (if (null js2-mode-ast)
11283 (message "Oops - parsing failed")
11284 (setq js2-mode-functions-hidden t)
11285 (js2-visit-ast js2-mode-ast #'js2-mode-function-hider)))
11286
11287 (defun js2-mode-function-hider (n endp)
11288 (when (not endp)
11289 (let ((tt (js2-node-type n))
11290 body beg end)
11291 (cond
11292 ((and (= tt js2-FUNCTION)
11293 (setq body (js2-function-node-body n)))
11294 (setq beg (js2-node-abs-pos body)
11295 end (+ beg (js2-node-len body)))
11296 (js2-mode-flag-region (1+ beg) (1- end) 'hide)
11297 nil) ; don't process children of function
11298 (t
11299 t))))) ; keep processing other AST nodes
11300
11301 (defun js2-mode-show-functions ()
11302 "Un-hide any folded function bodies in the buffer."
11303 (interactive)
11304 (setq js2-mode-functions-hidden nil)
11305 (save-excursion
11306 (goto-char (point-min))
11307 (while (/= (goto-char (next-overlay-change (point)))
11308 (point-max))
11309 (dolist (o (overlays-at (point)))
11310 (when (and (overlay-get o 'invisible)
11311 (not (overlay-get o 'comment)))
11312 (js2-mode-flag-region (overlay-start o) (overlay-end o) nil))))))
11313
11314 (defun js2-mode-hide-comment (n)
11315 (let* ((head (if (eq (js2-comment-node-format n) 'jsdoc)
11316 3 ; /**
11317 2)) ; /*
11318 (beg (+ (js2-node-abs-pos n) head))
11319 (end (- (+ beg (js2-node-len n)) head 2))
11320 (o (js2-mode-flag-region beg end 'hide)))
11321 (overlay-put o 'comment t)))
11322
11323 (defun js2-mode-toggle-hide-comments ()
11324 "Folds all block comments in the buffer.
11325 Use \\[js2-mode-show-all] to reveal them, or \\[js2-mode-show-element]
11326 to open an individual entry."
11327 (interactive)
11328 (if js2-mode-comments-hidden
11329 (js2-mode-show-comments)
11330 (js2-mode-hide-comments)))
11331
11332 (defun js2-mode-hide-comments ()
11333 (interactive)
11334 (if js2-mode-buffer-dirty-p
11335 (js2-mode-wait-for-parse #'js2-mode-hide-comments))
11336 (if (null js2-mode-ast)
11337 (message "Oops - parsing failed")
11338 (setq js2-mode-comments-hidden t)
11339 (dolist (n (js2-ast-root-comments js2-mode-ast))
11340 (let ((format (js2-comment-node-format n)))
11341 (when (js2-block-comment-p n)
11342 (js2-mode-hide-comment n))))
11343 (js2-mode-hide-//-comments)))
11344
11345 (defsubst js2-mode-extend-//-comment (direction)
11346 "Find start or end of a block of similar //-comment lines.
11347 DIRECTION is -1 to look back, 1 to look forward.
11348 INDENT is the indentation level to match.
11349 Returns the end-of-line position of the furthest adjacent
11350 //-comment line with the same indentation as the current line.
11351 If there is no such matching line, returns current end of line."
11352 (let ((pos (point-at-eol))
11353 (indent (current-indentation)))
11354 (save-excursion
11355 (save-match-data
11356 (while (and (zerop (forward-line direction))
11357 (looking-at js2-mode-//-comment-re)
11358 (eq indent (length (match-string 1))))
11359 (setq pos (point-at-eol)))
11360 pos))))
11361
11362 (defun js2-mode-hide-//-comments ()
11363 "Fold adjacent 1-line comments, showing only snippet of first one."
11364 (let (beg end)
11365 (save-excursion
11366 (save-match-data
11367 (goto-char (point-min))
11368 (while (re-search-forward js2-mode-//-comment-re nil t)
11369 (setq beg (point)
11370 end (js2-mode-extend-//-comment 1))
11371 (unless (eq beg end)
11372 (overlay-put (js2-mode-flag-region beg end 'hide)
11373 'comment t))
11374 (goto-char end)
11375 (forward-char 1))))))
11376
11377 (defun js2-mode-toggle-//-comment ()
11378 "Fold or un-fold any multi-line //-comment at point.
11379 Caller should have determined that this line starts with a //-comment."
11380 (let* ((beg (point-at-eol))
11381 (end beg))
11382 (save-excursion
11383 (goto-char end)
11384 (if (js2-mode-invisible-overlay-bounds)
11385 (js2-mode-show-element)
11386 ;; else hide the comment
11387 (setq beg (js2-mode-extend-//-comment -1)
11388 end (js2-mode-extend-//-comment 1))
11389 (unless (eq beg end)
11390 (overlay-put (js2-mode-flag-region beg end 'hide)
11391 'comment t))))))
11392
11393 (defun js2-mode-show-comments ()
11394 "Un-hide any hidden comments, leaving other hidden elements alone."
11395 (interactive)
11396 (setq js2-mode-comments-hidden nil)
11397 (save-excursion
11398 (goto-char (point-min))
11399 (while (/= (goto-char (next-overlay-change (point)))
11400 (point-max))
11401 (dolist (o (overlays-at (point)))
11402 (when (overlay-get o 'comment)
11403 (js2-mode-flag-region (overlay-start o) (overlay-end o) nil))))))
11404
11405 (defun js2-mode-display-warnings-and-errors ()
11406 "Turn on display of warnings and errors."
11407 (interactive)
11408 (setq js2-mode-show-parse-errors t
11409 js2-mode-show-strict-warnings t)
11410 (js2-reparse 'force))
11411
11412 (defun js2-mode-hide-warnings-and-errors ()
11413 "Turn off display of warnings and errors."
11414 (interactive)
11415 (setq js2-mode-show-parse-errors nil
11416 js2-mode-show-strict-warnings nil)
11417 (js2-reparse 'force))
11418
11419 (defun js2-mode-toggle-warnings-and-errors ()
11420 "Toggle the display of warnings and errors.
11421 Some users don't like having warnings/errors reported while they type."
11422 (interactive)
11423 (setq js2-mode-show-parse-errors (not js2-mode-show-parse-errors)
11424 js2-mode-show-strict-warnings (not js2-mode-show-strict-warnings))
11425 (if (interactive-p)
11426 (message "warnings and errors %s"
11427 (if js2-mode-show-parse-errors
11428 "enabled"
11429 "disabled")))
11430 (js2-reparse 'force))
11431
11432 (defun js2-mode-customize ()
11433 (interactive)
11434 (customize-group 'js2-mode))
11435
11436 (defun js2-mode-forward-sexp (&optional arg)
11437 "Move forward across one statement or balanced expression.
11438 With ARG, do it that many times. Negative arg -N means
11439 move backward across N balanced expressions."
11440 (interactive "p")
11441 (setq arg (or arg 1))
11442 (if js2-mode-buffer-dirty-p
11443 (js2-mode-wait-for-parse #'js2-mode-forward-sexp))
11444 (let (node end (start (point)))
11445 (cond
11446 ;; backward-sexp
11447 ;; could probably make this "better" for some cases:
11448 ;; - if in statement block (e.g. function body), go to parent
11449 ;; - infix exprs like (foo in bar) - maybe go to beginning
11450 ;; of infix expr if in the right-side expression?
11451 ((and arg (minusp arg))
11452 (dotimes (i (- arg))
11453 (js2-backward-sws)
11454 (forward-char -1) ; enter the node we backed up to
11455 (setq node (js2-node-at-point (point) t))
11456 (goto-char (if node
11457 (js2-node-abs-pos node)
11458 (point-min)))))
11459 (t
11460 ;; forward-sexp
11461 (js2-forward-sws)
11462 (dotimes (i arg)
11463 (js2-forward-sws)
11464 (setq node (js2-node-at-point (point) t)
11465 end (if node (+ (js2-node-abs-pos node)
11466 (js2-node-len node))))
11467 (goto-char (or end (point-max))))))))
11468
11469 (defun js2-next-error (&optional arg reset)
11470 "Move to next parse error.
11471 Typically invoked via \\[next-error].
11472 ARG is the number of errors, forward or backward, to move.
11473 RESET means start over from the beginning."
11474 (interactive "p")
11475 (if (or (null js2-mode-ast)
11476 (and (null (js2-ast-root-errors js2-mode-ast))
11477 (null (js2-ast-root-warnings js2-mode-ast))))
11478 (message "No errors")
11479 (when reset
11480 (goto-char (point-min)))
11481 (let* ((errs (copy-sequence
11482 (append (js2-ast-root-errors js2-mode-ast)
11483 (js2-ast-root-warnings js2-mode-ast))))
11484 (continue t)
11485 (start (point))
11486 (count (or arg 1))
11487 (backward (minusp count))
11488 (sorter (if backward '> '<))
11489 (stopper (if backward '< '>))
11490 (count (abs count))
11491 all-errs
11492 err)
11493 ;; sort by start position
11494 (setq errs (sort errs (lambda (e1 e2)
11495 (funcall sorter (second e1) (second e2))))
11496 all-errs errs)
11497 ;; find nth error with pos > start
11498 (while (and errs continue)
11499 (when (funcall stopper (cadar errs) start)
11500 (setq err (car errs))
11501 (if (zerop (decf count))
11502 (setq continue nil)))
11503 (setq errs (cdr errs)))
11504 (if err
11505 (goto-char (second err))
11506 ;; wrap around to first error
11507 (goto-char (second (car all-errs)))
11508 ;; if we were already on it, echo msg again
11509 (if (= (point) start)
11510 (js2-echo-error (point) (point)))))))
11511
11512 (defun js2-mouse-3 ()
11513 "Make right-click move the point to the click location.
11514 This makes right-click context menu operations a bit more intuitive.
11515 The point will not move if the region is active, however, to avoid
11516 destroying the region selection."
11517 (interactive)
11518 (when (and js2-move-point-on-right-click
11519 (not mark-active))
11520 (let ((e last-input-event))
11521 (ignore-errors
11522 (goto-char (cadadr e))))))
11523
11524 (defun js2-mode-create-imenu-index ()
11525 "Return an alist for `imenu--index-alist'."
11526 ;; This is built up in `js2-parse-record-imenu' during parsing.
11527 (when js2-mode-ast
11528 ;; if we have an ast but no recorder, they're requesting a rescan
11529 (unless js2-imenu-recorder
11530 (js2-reparse 'force))
11531 (prog1
11532 (js2-build-imenu-index)
11533 (setq js2-imenu-recorder nil
11534 js2-imenu-function-map nil))))
11535
11536 (defun js2-mode-find-tag ()
11537 "Replacement for `find-tag-default'.
11538 `find-tag-default' returns a ridiculous answer inside comments."
11539 (let (beg end)
11540 (js2-with-underscore-as-word-syntax
11541 (save-excursion
11542 (if (and (not (looking-at "[A-Za-z0-9_$]"))
11543 (looking-back "[A-Za-z0-9_$]"))
11544 (setq beg (progn (forward-word -1) (point))
11545 end (progn (forward-word 1) (point)))
11546 (setq beg (progn (forward-word 1) (point))
11547 end (progn (forward-word -1) (point))))
11548 (replace-regexp-in-string
11549 "[\"']" ""
11550 (buffer-substring-no-properties beg end))))))
11551
11552 (defun js2-mode-forward-sibling ()
11553 "Move to the end of the sibling following point in parent.
11554 Returns non-nil if successful, or nil if there was no following sibling."
11555 (let* ((node (js2-node-at-point))
11556 (parent (js2-mode-find-enclosing-fn node))
11557 sib)
11558 (when (setq sib (js2-node-find-child-after (point) parent))
11559 (goto-char (+ (js2-node-abs-pos sib)
11560 (js2-node-len sib))))))
11561
11562 (defun js2-mode-backward-sibling ()
11563 "Move to the beginning of the sibling node preceding point in parent.
11564 Parent is defined as the enclosing script or function."
11565 (let* ((node (js2-node-at-point))
11566 (parent (js2-mode-find-enclosing-fn node))
11567 sib)
11568 (when (setq sib (js2-node-find-child-before (point) parent))
11569 (goto-char (js2-node-abs-pos sib)))))
11570
11571 (defun js2-beginning-of-defun ()
11572 "Go to line on which current function starts, and return non-nil.
11573 If we're not in a function, go to beginning of previous script-level element."
11574 (interactive)
11575 (let ((parent (js2-node-parent-script-or-fn (js2-node-at-point)))
11576 pos sib)
11577 (cond
11578 ((and (js2-function-node-p parent)
11579 (not (eq (point) (setq pos (js2-node-abs-pos parent)))))
11580 (goto-char pos))
11581 (t
11582 (js2-mode-backward-sibling)))))
11583
11584 (defun js2-end-of-defun ()
11585 "Go to the char after the last position of the current function.
11586 If we're not in a function, skips over the next script-level element."
11587 (interactive)
11588 (let ((parent (js2-node-parent-script-or-fn (js2-node-at-point))))
11589 (if (not (js2-function-node-p parent))
11590 ;; punt: skip over next script-level element beyond point
11591 (js2-mode-forward-sibling)
11592 (goto-char (+ 1 (+ (js2-node-abs-pos parent)
11593 (js2-node-len parent)))))))
11594
11595 (defun js2-mark-defun (&optional allow-extend)
11596 "Put mark at end of this function, point at beginning.
11597 The function marked is the one that contains point.
11598
11599 Interactively, if this command is repeated,
11600 or (in Transient Mark mode) if the mark is active,
11601 it marks the next defun after the ones already marked."
11602 (interactive "p")
11603 (let (extended)
11604 (when (and allow-extend
11605 (or (and (eq last-command this-command) (mark t))
11606 (and transient-mark-mode mark-active)))
11607 (let ((sib (save-excursion
11608 (goto-char (mark))
11609 (if (js2-mode-forward-sibling)
11610 (point))))
11611 node)
11612 (if sib
11613 (progn
11614 (set-mark sib)
11615 (setq extended t))
11616 ;; no more siblings - try extending to enclosing node
11617 (goto-char (mark t)))))
11618 (when (not extended)
11619 (let ((node (js2-node-at-point (point) t)) ; skip comments
11620 ast fn stmt parent beg end)
11621 (when (js2-ast-root-p node)
11622 (setq ast node
11623 node (or (js2-node-find-child-after (point) node)
11624 (js2-node-find-child-before (point) node))))
11625 ;; only mark whole buffer if we can't find any children
11626 (if (null node)
11627 (setq node ast))
11628 (if (js2-function-node-p node)
11629 (setq parent node)
11630 (setq fn (js2-mode-find-enclosing-fn node)
11631 stmt (if (or (null fn)
11632 (js2-ast-root-p fn))
11633 (js2-mode-find-first-stmt node))
11634 parent (or stmt fn)))
11635 (setq beg (js2-node-abs-pos parent)
11636 end (+ beg (js2-node-len parent)))
11637 (push-mark beg)
11638 (goto-char end)
11639 (exchange-point-and-mark)))))
11640
11641 (defun js2-narrow-to-defun ()
11642 "Narrow to the function enclosing point."
11643 (interactive)
11644 (let* ((node (js2-node-at-point (point) t)) ; skip comments
11645 (fn (if (js2-script-node-p node)
11646 node
11647 (js2-mode-find-enclosing-fn node)))
11648 (beg (js2-node-abs-pos fn)))
11649 (unless (js2-ast-root-p fn)
11650 (narrow-to-region beg (+ beg (js2-node-len fn))))))
11651
11652 (defalias 'js2r 'js2-mode-reset)
11653
11654 (provide 'js2-mode)
11655
11656 ;;; js2-mode.el ends here
11657
11658
11659 ;;; js2.el ends here