是否有一种不错的方法来编写输出ES6类定义的Parenscript类宏?
如果类定义如下所示:
class Person {
sayHello() {
alert('hello');
}
walk() {
alert('I am walking!');
}
}
class Student extends Person {
sayGoodBye() {
alert('goodBye');
}
sayHello() {
alert('hi, I am a student');
}
}
我想在Parenscript中写出这样的内容:
(def-class -person ()
(say-hello () (alert "hello"))
(walk () (alert "I am walking!")))
(def-class -student (-person)
(say-good-bye () (alert "goodBye"))
(say-hello () (alert "hi, I am a student")))
我尝试了几种方法 - 附在下面作为答案 - 但它们都不是完全令人满意的。有没有更好的解决方案,不涉及重新设计Parenscript?
答案 0 :(得分:4)
解决方案1:
(defpsmacro def-class (name (&optional extends) &body body)
(multiple-value-bind (constructor others)
(gadgets:splitfilter (lambda (x) (string-equal (car x) 'constructor)) body)
(let ((constructor (case (length constructor)
(0 nil)
(1 (car constructor))
(otherwise
(error "Class can't have more than one constructor"))))
(const-lambda-list nil)
(const-body nil))
(when constructor
(setf const-lambda-list (second constructor))
(set const-body (cddr constructor)))
`(progn
(defun ,name ,const-lambda-list
,@const-body)
,@(mapcar
(lambda (item)
`(setf (@ ,name prototype ,(car item))
(lambda ,(second item) ,@(cddr item))))
others)
,@(when extends
`((setf (@ ,name prototype) (chain -object (create (@ ,name prototype))))
(setf (@ ,name prototype constructor) ,name)))))))
问题:
答案 1 :(得分:3)
解决方案2:
(defpsmacro def-class-es6 (name (&optional extends) &body body)
(let ((output nil))
(push (format nil "class ~a~a {~&"
(symbol-to-js-string name)
(if extends
(format nil " extends ~a" (symbol-to-js-string extends))
""))
output)
(dolist (itm body)
(when (atom itm)
(error "Non-list item found in class body."))
(let ((staticp nil))
(when (eq (car itm) :static)
(setf itm (if (listp (cadr itm)) (cadr itm) (cdr itm)))
(setf staticp t))
(destructuring-bind (mname lambda-list . statements) itm
(push
(format
nil "~a~a (~{~a~^ ~}) {~a}~&"
(if staticp "static " "")
(symbol-to-js-string mname)
(mapcar #'symbol-to-js-string lambda-list)
;; Use parenscript to express a method body, then peel the
;; block contents out and put them in our own block.
(let ((fbody (eval `(ps (lambda ,lambda-list ,@statements)))))
(subseq fbody
(1+ (position #\{ fbody))
(position #\} fbody :from-end t))))
output))))
(push "}" output)
`(lisp-raw ,(apply #'concatenate 'string (nreverse output)))))
此宏输出ES6代码,但是: