зеркало из https://github.com/mozilla/pjs.git
224 строки
9.4 KiB
Common Lisp
Executable File
224 строки
9.4 KiB
Common Lisp
Executable File
|
|
(defun js-state-transition (action-results)
|
|
(assert-type action-results (tuple t bool))
|
|
(values action-results (if (second action-results) '($re) '($div))))
|
|
|
|
(defun js-metaparse (string &key trace)
|
|
(lexer-metaparse *ll* string :initial-state '($re) :state-transition #'js-state-transition :trace trace))
|
|
|
|
(defun js-pmetaparse (string &key (stream t) trace)
|
|
(lexer-pmetaparse *ll* string :initial-state '($re) :state-transition #'js-state-transition :stream stream :trace trace))
|
|
|
|
|
|
(defun translate-number (data)
|
|
(cond
|
|
((or (float64? data) (float32? data)) data)
|
|
((eq (first data) 'l:long) (list (world-find-symbol *jw* 'long) (second data)))
|
|
((eq (first data) 'l:u-long) (list (world-find-symbol *jw* 'u-long) (second data)))
|
|
(t (error "Bad number token: ~S" data))))
|
|
|
|
|
|
; Convert the results of the lexer's actions into a token suitable for the parser.
|
|
(defun js-lexer-results-to-token (token-value line-break)
|
|
(cond
|
|
((eq token-value :end-of-input)
|
|
(values *end-marker* nil))
|
|
((eq token-value :negated-min-long)
|
|
(values (if line-break (terminal-lf-terminal '$negated-min-long) '$negated-min-long) nil))
|
|
(t
|
|
(let ((data (second token-value)))
|
|
(multiple-value-bind (token token-arg)
|
|
(ecase (first token-value)
|
|
(l:identifier (values '$identifier data))
|
|
((l:keyword l:punctuator) (values (intern (string-upcase data)) nil))
|
|
(l:number-token (values '$number (translate-number data)))
|
|
(l:string-token (values '$string data))
|
|
(l:regular-expression (values '$regular-expression (rest token-value))))
|
|
(when line-break
|
|
(setq token (terminal-lf-terminal token)))
|
|
(values token token-arg))))))
|
|
|
|
|
|
; Lex and parse the input-string of tokens to produce a list of action results.
|
|
; If trace is:
|
|
; nil, don't print trace information
|
|
; :code, print trace information, including action code
|
|
; :lexer, print lexer trace information
|
|
; :lexer-code print lexer trace information, including action code
|
|
; other print trace information
|
|
; Return three values:
|
|
; the list of action results;
|
|
; the list of action results' types;
|
|
; the list of processed tokens.
|
|
(defun js-parse (input-string &key (lexer *ll*) (grammar *jg*) trace)
|
|
(let ((lexer-classifier (lexer-classifier lexer))
|
|
(lexer-metagrammar (lexer-metagrammar lexer))
|
|
(lexer-trace (cdr (assoc trace '((:lexer t) (:lexer-code :code)))))
|
|
(state-stack (list (grammar-start-state grammar)))
|
|
(value-stack nil)
|
|
(type-stack nil)
|
|
(prev-number-token nil)
|
|
(input (append (coerce input-string 'list) '($end)))
|
|
(token nil)
|
|
(token-arg nil)
|
|
(token2 nil)
|
|
(token2-arg nil)
|
|
(token-history nil))
|
|
(flet
|
|
((get-next-token-value (lexer-state)
|
|
(multiple-value-bind (results in-rest)
|
|
(action-metaparse lexer-metagrammar lexer-classifier (cons lexer-state input) :trace lexer-trace)
|
|
(assert-true (null (cdr results)))
|
|
(setq input in-rest)
|
|
(car results))))
|
|
|
|
(loop
|
|
(let* ((state (car state-stack))
|
|
(transition (state-only-transition state)))
|
|
(unless transition
|
|
(unless token
|
|
(if token2
|
|
(setq token token2
|
|
token-arg token2-arg
|
|
token2 nil
|
|
token2-arg nil)
|
|
(let* ((lexer-state (cond
|
|
(prev-number-token '$num)
|
|
((or (state-transition state '/) (state-transition state '/=)) '$div)
|
|
(t '$re)))
|
|
(token-value (get-next-token-value lexer-state))
|
|
(line-break nil))
|
|
(when (eq token-value :line-break)
|
|
(when (eq lexer-state '$num)
|
|
(setq lexer-state '$div))
|
|
(setq token-value (get-next-token-value lexer-state))
|
|
(setq line-break t))
|
|
(setq prev-number-token (or (eq token-value :negated-min-long)
|
|
(and (consp token-value) (eq (car token-value) 'l:number-token))))
|
|
(multiple-value-setq (token token-arg) (js-lexer-results-to-token token-value line-break)))))
|
|
(setq transition (state-transition state token))
|
|
(unless transition
|
|
(when (lf-terminal? token)
|
|
(setq transition (state-transition state '$virtual-semicolon)))
|
|
(if transition
|
|
(progn
|
|
(when trace
|
|
(format *trace-output* "Inserted virtual semicolon~@:_"))
|
|
(setq token2 token
|
|
token2-arg token-arg
|
|
token '$virtual-semicolon
|
|
token-arg nil))
|
|
(syntax-error "Parse error on ~S followed by ~S ..." token (coerce (butlast (ldiff input (nthcdr 31 input))) 'string)))))
|
|
|
|
(when trace
|
|
(format *trace-output* "S~D: ~@_" (state-number state))
|
|
(print-values (reverse value-stack) (reverse type-stack) *trace-output*)
|
|
(pprint-newline :mandatory *trace-output*))
|
|
|
|
(ecase (transition-kind transition)
|
|
(:shift
|
|
(push (if token-arg (cons token token-arg) token) token-history)
|
|
(when trace
|
|
(format *trace-output* " shift ~W ~W~:@_" token token-arg)
|
|
(dolist (action-signature (grammar-symbol-signature grammar token))
|
|
(push (cdr action-signature) type-stack)))
|
|
(dolist (action-function-binding (gethash token (grammar-terminal-actions grammar)))
|
|
(push (funcall (cdr action-function-binding) token-arg) value-stack))
|
|
(push (transition-state transition) state-stack)
|
|
(setq token nil))
|
|
|
|
(:reduce
|
|
(let ((production (transition-production transition)))
|
|
(when trace
|
|
(write-string " reduce " *trace-output*)
|
|
(if (eq trace :code)
|
|
(write production :stream *trace-output* :pretty t)
|
|
(print-production production *trace-output*))
|
|
(pprint-newline :mandatory *trace-output*))
|
|
(setq state-stack (nthcdr (production-rhs-length production) state-stack)
|
|
state (assert-non-null
|
|
(cdr (assoc (production-lhs production) (state-gotos (car state-stack)) :test *grammar-symbol-=*)))
|
|
value-stack (funcall (production-evaluator production) value-stack))
|
|
(push state state-stack)
|
|
(when trace
|
|
(setq type-stack (nthcdr (production-n-action-args production) type-stack))
|
|
(dolist (action-signature (grammar-symbol-signature grammar (production-lhs production)))
|
|
(push (cdr action-signature) type-stack)))))
|
|
|
|
(:accept
|
|
(when trace
|
|
(format *trace-output* " accept~:@_"))
|
|
(return (values
|
|
(nreverse value-stack)
|
|
(if trace
|
|
(nreverse type-stack)
|
|
(grammar-user-start-action-types grammar))
|
|
(nreverse token-history)))))
|
|
(when trace
|
|
(format *trace-output* "!")))))))
|
|
|
|
|
|
(defun object-to-source (o)
|
|
(funcall (world-find-symbol *jw* 'object-to-source) o))
|
|
|
|
|
|
; Simple JS2 read-eval-print loop.
|
|
(defun rep (&key print-tokens break-on-error)
|
|
(loop
|
|
(let ((s (read-line *terminal-io* t)))
|
|
;(format *terminal-io* "<~S>~%" s)
|
|
(block success
|
|
(handler-case
|
|
(flet ((eval-and-print ()
|
|
(multiple-value-bind (results types tokens) (js-parse s)
|
|
(declare (ignore types))
|
|
(assert-true (= (length results) 1))
|
|
(when print-tokens
|
|
(write tokens :stream *terminal-io* :pretty t)
|
|
(terpri *terminal-io*))
|
|
(write-string (object-to-source (first results)) *terminal-io*)
|
|
(terpri *terminal-io*))))
|
|
(if break-on-error
|
|
(eval-and-print)
|
|
(let ((exception
|
|
(catch :semantic-exception
|
|
(eval-and-print)
|
|
(return-from success))))
|
|
(format *terminal-io* "Exception: ~A~%" (object-to-source exception)))))
|
|
(syntax-error (condition)
|
|
(format *terminal-io* "~A~%" condition)))))))
|
|
|
|
|
|
#|
|
|
(js-parse "1+2*/4*/
|
|
32")
|
|
(js-parse "1+2
|
|
+3" :trace t)
|
|
(js-parse "3
|
|
++" :trace t)
|
|
(js-parse "1+2
|
|
true
|
|
false")
|
|
(js-parse "32+abc//23e-a4*7e-2 3 id4 4ef;")
|
|
|
|
(js-parse "if (1) 5 else 8")
|
|
(js-parse "0x20")
|
|
(js-parse "2b")
|
|
(js-parse " 3.75" :trace t)
|
|
(js-parse "25" :trace :code)
|
|
(js-parse "32+abc//23e-a4*7e-2 3 id4 4ef;")
|
|
(js-parse "32+abc//23e-a4*7e-2 3 id4 4ef;
|
|
")
|
|
(js-parse "32+abc/ /23e-a4*7e-2 3 /*id4 4*-/ef;
|
|
|
|
fjds*/y//z")
|
|
(js-parse "3a+in'a+b\\147\"de'\"'\"")
|
|
(js-parse "3*/regexp*///x")
|
|
(js-parse "/regexp*///x")
|
|
(js-parse "if \\x69f \\u0069f")
|
|
(js-parse "if \\x69f z\\x20z")
|
|
(js-parse "3lbs,3in,3 in 3_in,3_lbs")
|
|
(js-parse "3a+b in'a+b\\040\\077\\700\\150\\15A\\69\"de'\"'\"")
|
|
|#
|
|
|