(defvar js2-ENUM 161) ; for "enum" reserved word
(defvar js2-TRIPLEDOT 162) ; for rest parameter
(defvar js2-ARROW 163) ; function arrow (=>)
+(defvar js2-CLASS 164)
+(defvar js2-EXTENDS 165)
-(defconst js2-num-tokens (1+ js2-ARROW))
+(defconst js2-num-tokens (1+ js2-EXTENDS))
(defconst js2-debug-print-trees nil)
"Yield from closing generator")
;; Classes
+(js2-msg "msg.unnamed.class.stmt" ; added by js2-mode
+ "class statement requires a name")
+
+(js2-msg "msg.class.unexpected.comma" ; added by js2-mode
+ "unexpected ',' between class properties")
+
+(js2-msg "msg.missing.extends" ; added by js2-mode
+ "name is required after extends")
+
+(js2-msg "msg.no.brace.class" ; added by js2-mode
+ "missing '{' before class body")
+
(js2-msg "msg.missing.computed.rb" ; added by js2-mode
"missing ']' after computed property expression")
(insert ",")))
(insert "]"))
+(defstruct (js2-class-node
+ (:include js2-node)
+ (:constructor nil)
+ (:constructor make-js2-class-node (&key (type js2-CLASS)
+ (pos js2-ts-cursor)
+ (form 'CLASS_STATEMENT)
+ (name "")
+ extends len elems)))
+ "AST node for an class expression.
+`elems' is a list of `js2-object-prop-node', and `extends' is an
+optional `js2-expr-node'"
+ form ; CLASS_{STATEMENT|EXPRESSION}
+ name ; class name (a `js2-node-name', or nil if anonymous)
+ extends ; class heritage (a `js2-expr-node', or nil if none)
+ elems)
+
+(put 'cl-struct-js2-class-node 'js2-visitor 'js2-visit-class-node)
+(put 'cl-struct-js2-class-node 'js2-printer 'js2-print-class-node)
+
+(defun js2-visit-class-node (n v)
+ (js2-visit-ast (js2-class-node-name n) v)
+ (js2-visit-ast (js2-class-node-extends n) v)
+ (dolist (e (js2-class-node-elems n))
+ (js2-visit-ast e v)))
+
+(defun js2-print-class-node (n i)
+ (let* ((pad (js2-make-pad i))
+ (name (js2-class-node-name n))
+ (extends (js2-class-node-extends n))
+ (elems (js2-class-node-elems n)))
+ (insert pad "class")
+ (when name
+ (insert " ")
+ (js2-print-ast name 0))
+ (when extends
+ (insert " extends ")
+ (js2-print-ast extends))
+ (insert " {")
+ (dolist (elem elems)
+ (insert "\n")
+ (js2-print-ast elem (1+ i)))
+ (insert "\n" pad "}")))
+
(defstruct (js2-object-node
(:include js2-node)
(:constructor nil)
js2-CALL
js2-CATCH
js2-CATCH_SCOPE
+ js2-CLASS
js2-CONST
js2-CONTINUE
js2-DEBUGGER
(defconst js2-keywords
'(break
- case catch const continue
+ case catch class const continue
debugger default delete do
- else enum
+ else enum extends
false finally for function
if in instanceof import
let
(let ((table (make-vector js2-num-tokens nil))
(tokens
(list js2-BREAK
- js2-CASE js2-CATCH js2-CONST js2-CONTINUE
+ js2-CASE js2-CATCH js2-CLASS js2-CONST js2-CONTINUE
js2-DEBUGGER js2-DEFAULT js2-DELPROP js2-DO
- js2-ELSE
+ js2-ELSE js2-EXTENDS
js2-FALSE js2-FINALLY js2-FOR js2-FUNCTION
js2-IF js2-IN js2-INSTANCEOF js2-IMPORT
js2-LET
"Highlight function properties and external variables."
(let (leftpos name)
;; highlight vars and props assigned function values
- (when (js2-function-node-p right)
+ (when (or (js2-function-node-p right)
+ (js2-class-node-p right))
(cond
;; var foo = function() {...}
((js2-name-node-p left)
(let ((parsers (make-vector js2-num-tokens
#'js2-parse-expr-stmt)))
(aset parsers js2-BREAK #'js2-parse-break)
+ (aset parsers js2-CLASS #'js2-parse-class-stmt)
(aset parsers js2-CONST #'js2-parse-const-var)
(aset parsers js2-CONTINUE #'js2-parse-continue)
(aset parsers js2-DEBUGGER #'js2-parse-debugger)
js2-LC
js2-ERROR
js2-SEMI
+ js2-CLASS
js2-FUNCTION)
"List of tokens that don't do automatic semicolon insertion.")
tt)
(setq tt (js2-current-token-type))
(cond
+ ((= tt js2-CLASS)
+ (js2-parse-class-expr))
((= tt js2-FUNCTION)
(js2-parse-function-expr))
((= tt js2-LB)
(js2-node-add-children pn iter obj)
pn))
+(defun js2-parse-class-stmt ()
+ (let ((pos (js2-current-token-beg)))
+ (js2-must-match-name "msg.unnamed.class.stmt")
+ (js2-parse-class pos 'CLASS_STATEMENT (js2-create-name-node t))))
+
+(defun js2-parse-class-expr ()
+ (let ((pos (js2-current-token-beg))
+ name)
+ (when (js2-match-token js2-NAME)
+ (setq name (js2-create-name-node t)))
+ (js2-parse-class pos 'CLASS_EXPRESSION name)))
+
+(defun js2-parse-class (pos form name)
+ ;; class X [extends ...] {
+ (let (pn elems extends)
+ (when name
+ (js2-set-face (js2-node-pos name) (js2-node-end name)
+ 'font-lock-function-name-face 'record))
+ (if (js2-match-token js2-EXTENDS)
+ (if (= (js2-peek-token) js2-LC)
+ (js2-report-error "msg.missing.extends")
+ ;; TODO(sdh): this should be left-hand-side-expr, not assign-expr
+ (setq extends (js2-parse-assign-expr))
+ (if (not extends)
+ (js2-report-error "msg.bad.extends"))))
+ (js2-must-match js2-LC "msg.no.brace.class")
+ (setq elems (js2-parse-object-literal-elems t)
+ pn (make-js2-class-node :pos pos
+ :len (- js2-ts-cursor pos)
+ :form form
+ :name name
+ :extends extends
+ :elems elems))
+ (apply #'js2-node-add-children pn (js2-class-node-elems pn))
+ pn))
+
(defun js2-parse-object-literal ()
+ (let* ((pos (js2-current-token-beg))
+ (elems (js2-parse-object-literal-elems))
+ (result (make-js2-object-node :pos pos
+ :len (- js2-ts-cursor pos)
+ :elems elems)))
+ (apply #'js2-node-add-children result (js2-object-node-elems result))
+ result))
+
+(defun js2-parse-object-literal-elems (&optional class-p)
(let ((pos (js2-current-token-beg))
tt elems result after-comma
(continue t))
((= tt js2-NUMBER)
(setq after-comma nil)
(push (js2-parse-plain-property (make-js2-number-node)) elems))
- ;; trailing comma
- ((= tt js2-RC)
+ ;; break out of loop, trailing comma
+ ((or (= tt js2-RC)
+ (= tt js2-EOF))
(js2-unget-token)
(setq continue nil)
(if after-comma
(js2-report-error "msg.bad.prop")
(unless js2-recover-from-parse-errors
(setq continue nil)))) ; end switch
- (if (js2-match-token js2-COMMA)
- (setq after-comma (js2-current-token-end))
- (setq continue nil))) ; end loop
+ ;; handle commas, depending on class-p
+ (let ((comma (js2-match-token js2-COMMA)))
+ (if class-p
+ (if comma
+ (js2-report-error "msg.class.unexpected.comma"))
+ (if comma
+ (setq after-comma (js2-current-token-end))
+ (setq continue nil))))) ; end loop
(js2-must-match js2-RC "msg.no.brace.prop")
- (setq result (make-js2-object-node :pos pos
- :len (- js2-ts-cursor pos)
- :elems (nreverse elems)))
- (apply #'js2-node-add-children result (js2-object-node-elems result))
- result))
+ (nreverse elems)))
(defun js2-parse-named-prop (tt)
"Parse a name, string, or getter/setter object property.