Got rid of the "(.method-name object args)" method-calling
authorVladimir Sedach <vsedach@gmail.com>
Mon, 2 Feb 2009 21:14:21 +0000 (14:14 -0700)
committerVladimir Sedach <vsedach@gmail.com>
Mon, 2 Feb 2009 21:14:21 +0000 (14:14 -0700)
convention. It breaks package prefixing/obfuscation, and while
sometimes a handy shortcut it obscures how method calls are really
done, and introduces a redundant way of doing them which complicates
code-walkers and (future) attempts at Parenscript compiler
open-implementation facilities.

docs/reference.lisp
src/compiler.lisp
src/js-dom-symbol-exports.lisp
src/printer.lisp
src/special-forms.lisp
t/ps-tests.lisp
t/reference-tests.lisp

index 232a61b..007bc71 100644 (file)
@@ -335,22 +335,21 @@ a-variable  => aVariable
 (foobar (blorg 1 2) (blabla 3 4) (array 2 3 4))
 => foobar(blorg(1, 2), blabla(3, 4), [ 2, 3, 4 ])
 
+((slot-value this 'blorg) 1 2) => this.blorg(1, 2)
+
 ((aref foo i) 1 2) => foo[i](1, 2)
 
-;;; A method call is a function call where the function name is a
-;;; symbol and begins with a "." . In a method call, the name of the
-;;; function is append to its first argument, thus reflecting the
-;;; method call syntax of JavaScript. Please note that most method
-;;; calls can be abbreviated using the "." trick in symbol names (see
-;;; "Symbol Conversion" above).
+((slot-value (aref foobar 1) 'blorg) NIL T) => foobar[1].blorg(null, true)
 
-(.blorg this 1 2) => this.blorg(1, 2)
+;;; Note that while most method calls can be abbreviated using the "."
+;;; trick in symbol names (see "Symbol Conversion" above), this is not
+;;; advised due to the fact that "object.function" is treated as a
+;;; symbol distinct from both "object" and "function," which will
+;;; cause problems if Parenscript package prefixes or package
+;;; obfuscation is used.
 
 (this.blorg 1 2) => this.blorg(1, 2)
 
-(.blorg (aref foobar 1) NIL T)
-=> foobar[1].blorg(null, true)
-
 ;;;# Operator Expressions
 ;;;t \index{operator}
 ;;;t \index{operator expression}
index a41366c..c663635 100644 (file)
@@ -63,11 +63,6 @@ lexical block.")
        (not (op-form-p form))
        (not (ps-special-form-p form))))
 
-(defun method-call-p (form)
-  (and (funcall-form-p form)
-       (symbolp (first form))
-       (eql (char (symbol-name (first form)) 0) #\.)))
-
 ;;; macro expansion
 (eval-when (:compile-toplevel :load-toplevel :execute)
   (defun make-macro-env-dictionary ()
@@ -236,25 +231,16 @@ the form cannot be compiled to a symbol."
              (error "Attempting to use Parenscript special form ~a as variable" symbol)))
         (t (list 'js-variable symbol))))
 
-(defun compile-function-argument-forms (arg-forms)
-  "Compiles a bunch of Parenscript forms from a funcall form to an effective set of
-Javascript arguments.  The only extra processing this does is makes :keyword arguments
-into a single options argument via CREATE."
-  (let ((compiled-args (mapcar (lambda (arg) (compile-parenscript-form arg :expecting :expression))
-                               arg-forms)))
-    (do ((effective-expressions nil)
-         (expressions-subl compiled-args))
-        ((not expressions-subl) (reverse effective-expressions))
-      (let ((arg-expr (first expressions-subl)))
-        (if (keywordp arg-expr)
-            (progn (when (oddp (length expressions-subl))
-                     (error "Odd number of keyword arguments: ~A." arg-forms))
-                   (push (list 'js-object (loop for (name val) on expressions-subl by #'cddr
-                                             collect (list (list 'js-variable name) val)))
-                         effective-expressions)
-                   (setf expressions-subl nil))
-            (progn (push arg-expr effective-expressions)
-                   (setf expressions-subl (rest expressions-subl))))))))
+(defun compile-function-argument-forms (args)
+  (let ((remaining-args args))
+    (loop while remaining-args collecting
+         (if (keywordp (first remaining-args))
+             (prog2 (when (oddp (length remaining-args))
+                      (error "Odd number of keyword arguments: ~A." args))
+                 (compile-parenscript-form (cons 'create remaining-args) :expecting :expression)
+               (setf remaining-args nil))
+             (prog1 (compile-parenscript-form (first remaining-args) :expecting :expression)
+               (setf remaining-args (cdr remaining-args)))))))
 
 (defun ps-convert-op-name (op)
   (case (ensure-ps-symbol op)
@@ -276,11 +262,6 @@ into a single options argument via CREATE."
            (list 'operator
                  (ps-convert-op-name (compile-parenscript-form (first form) :expecting :symbol))
                  (mapcar (lambda (form) (compile-parenscript-form form :expecting :expression)) (rest form))))
-          ((method-call-p form)
-           (list 'js-method-call
-                 (compile-parenscript-form name :expecting :symbol)
-                 (compile-parenscript-form (first args) :expecting :expression)
-                 (compile-function-argument-forms (rest args))))
           ((funcall-form-p form)
            (list 'js-funcall
                  (compile-parenscript-form name :expecting :expression)
index cf2b50d..cc9e76f 100644 (file)
@@ -8,13 +8,17 @@
 
 (defpackage "PS-JS-SYMBOLS"
   (:export
-   #:.to-fixed
+   #:to-fixed
    #:encode-u-r-i-component
-   #:.push
-   #:.size
+   #:size
    #:*array
-   #:set-timeout
-   #:set-interval
+   #:*date
+   #:get-time
+   #:arguments
+   #:join
+   #:prototype
+   #:slice
+   #:call
    ))
 
 (defpackage "PS-DOM-SYMBOLS"
    #:left
    #:display
    #:onmousemove
-   #:.create-element
-   #:.set-attribute
-   #:.append-child
+   #:create-element
+   #:set-attribute
+   #:append-child
    #:offset-height
    #:offset-width
    #:client-height
    #:client-width
    #:scroll-height
    #:scroll-width
-   #:.insert-row
-   #:.insert-cell
+   #:insert-row
+   #:insert-cell
    #:value
    #:elements
-   #:.get-elements-by-class-name
-   #:.get-element-by-id
+   #:get-elements-by-class-name
+   #:get-element-by-id
    #:onselectstart
+   #:set-timeout
+   #:set-interval
    ))
 
 (defpackage "PS-PROTOTYPE-LIB-SYMBOLS"
   (:export
    #:*event
-   #:.observe
+   #:observe
    #:*ajax
-   #:.*request
+   #:*request
+   #:console
    ))
index 0217e90..981786d 100644 (file)
@@ -147,14 +147,6 @@ arguments, defines a printer for that form using the given body."
   (loop for idx in indices do
         (psw #\[) (ps-print idx) (psw #\])))
 
-(defprinter object-literal (&rest slot-definitions)
-  (psw #\{)
-  (loop for ((key . value) . remaining) on slot-definitions do
-        (psw (format nil "~A: " (js-translate-symbol key)))
-        (ps-print value)
-        (when remaining (psw ", ")))
-  (psw " }"))
-
 (defprinter js-variable (var)
   (psw (js-translate-symbol var)))
 
@@ -181,21 +173,11 @@ arguments, defines a printer for that form using the given body."
       (ps-print arg))
   (unless prefix (psw (format nil "~(~a~)" op))))
 
-;;; function and method calls
 (defprinter js-funcall (fun-designator args)
-  (if (member (car fun-designator) '(js-variable js-aref js-slot-value js-funcall))
-      (ps-print fun-designator)
-      (progn (psw #\() (ps-print fun-designator) (psw #\))))
-  (psw #\() (print-comma-delimited-list args) (psw #\)))
-
-(defprinter js-method-call (method object args)
-  ;; TODO: this may not be the best way to add ()'s around lambdas
-  ;; probably there is or should be a more general solution working
-  ;; in other situations involving lambdas
-  (if (or (numberp object) (and (consp object) (member (car object) '(js-lambda js-object operator js-expression-if))))
-      (parenthesize-print object)
-      (ps-print object))
-  (psw (js-translate-symbol method))
+  (funcall (if (member (car fun-designator) '(js-variable js-aref js-slot-value js-funcall))
+               #'ps-print
+               #'parenthesize-print)
+           fun-designator)
   (psw #\() (print-comma-delimited-list args) (psw #\)))
 
 (defprinter js-block (block-type statements)
@@ -227,10 +209,9 @@ arguments, defines a printer for that form using the given body."
   (psw ") ")
   (ps-print body-block))
 
-;;; object literals
 (defprinter js-object (slot-defs)
   (psw "{ ")
-  (loop for ((slot-name slot-value) . remaining) on slot-defs do
+  (loop for ((slot-name slot-value) . remaining) on slot-defs do
         (if (and (listp slot-name) (eql 'ps-quote (car slot-name)) (symbolp (second slot-name)))
             (psw (js-translate-symbol (second slot-name)))
             (ps-print slot-name))
@@ -240,7 +221,9 @@ arguments, defines a printer for that form using the given body."
   (psw " }"))
 
 (defprinter js-slot-value (obj slot)
-  (if (> (expression-precedence obj) #.(op-precedence 'js-slot-value))
+  (if (or (> (expression-precedence obj) #.(op-precedence 'js-slot-value))
+          (numberp obj)
+          (and (listp obj) (member (car obj) '(js-lambda js-object))))
       (parenthesize-print obj)
       (ps-print obj))
   (if (and (listp slot) (eql 'ps-quote (car slot)))
index 2e28c50..ab4084e 100644 (file)
                   (compile-parenscript-form form :expecting :expression))
                 coords)))
 
-(add-ps-literal '{})
-(define-ps-special-form {} (expecting &rest arrows)
-  (declare (ignore expecting))
-  (cons 'object-literal (loop for (key value) on arrows by #'cddr
-                              collect (cons key (compile-parenscript-form value :expecting :expression)))))
-
 (defpsmacro list (&rest values)
   `(array ,@values))
 
-(defpsmacro make-array (&rest inits)
-  `(new (*array ,@inits)))
+(defpsmacro make-array (&rest initial-values)
+  `(new (*array ,@initial-values)))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;; operators
@@ -455,20 +449,23 @@ lambda-list::=
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;; objects
-(define-ps-special-form create (expecting &rest args)
+(add-ps-literal '{})
+(define-ps-symbol-macro {} (create))
+
+(define-ps-special-form create (expecting &rest arrows)
   (declare (ignore expecting))
-  (list 'js-object (loop for (name val) on args by #'cddr collecting
-                         (let ((name-expr (compile-parenscript-form name :expecting :expression)))
-                           (when (keywordp name-expr)
-                             (setf name-expr (list 'js-variable name-expr)))
-                           (assert (or (stringp name-expr)
-                                       (numberp name-expr)
-                                       (and (listp name-expr)
-                                            (or (eql 'js-variable (car name-expr))
-                                                (eql 'ps-quote (car name-expr)))))
+  (list 'js-object (loop for (key-expr val-expr) on arrows by #'cddr collecting
+                         (let ((key (compile-parenscript-form key-expr :expecting :expression)))
+                           (when (keywordp key)
+                             (setf key (list 'js-variable key)))
+                           (assert (or (stringp key)
+                                       (numberp key)
+                                       (and (listp key)
+                                            (or (eq 'js-variable (car key))
+                                                (eq 'ps-quote (car key)))))
                                    ()
-                                   "Slot ~s is not one of js-variable, keyword, string or number." name-expr)
-                           (list name-expr (compile-parenscript-form val :expecting :expression))))))
+                                   "Slot key ~s is not one of js-variable, keyword, string or number." key)
+                           (cons key (compile-parenscript-form val-expr :expecting :expression))))))
 
 (define-ps-special-form %js-slot-value (expecting obj slot)
   (declare (ignore expecting))
index 7e8bedf..710b49b 100644 (file)
@@ -7,12 +7,12 @@
 (in-suite ps-tests)
 
 (test-ps-js plus-is-not-commutative
-   (setf x (+ "before" x "after"))
-   "x = 'before' + x + 'after';")
+  (setf x (+ "before" x "after"))
+  "x = 'before' + x + 'after';")
 
 (test-ps-js plus-works-if-first
-   (setf x (+ x "middle" "after"))
-   "x += 'middle' + 'after';")
+  (setf x (+ x "middle" "after"))
+  "x += 'middle' + 'after';")
 
 (test-ps-js setf-side-effects
   (progn
@@ -27,7 +27,7 @@ function sideEffect() {
   return 3;
 };
 x = 2 + sideEffect() + x + 5;")
-;; Parenscript used to optimize too much:
+;; Parenscript used to optimize incorrectly:
 ;;   var x = 10;
 ;;   function sideEffect() {
 ;;     x = 4;
@@ -38,37 +38,52 @@ x = 2 + sideEffect() + x + 5;")
 ;;   Which is 20, not 14
 
 
-(test-ps-js dot-notation-bug
-            (.match (+ "" x) "foo")
-            "('' + x).match('foo')")
+(test-ps-js method-call-op-form
+  ((@ (+ "" x) to-string))
+  "('' + x).toString()")
+
+(test-ps-js method-call-op-form-args
+  ((@ (+ "" x) to-string) 1 2 :baz 3)
+  "('' + x).toString(1, 2, { baz : 3 })")
+
+(test-ps-js method-call-number
+  ((@ 10 to-string))
+  "( 10 ).toString()")
+
+(test-ps-js method-call-string
+  ((@ "hi" to-string))
+  "'hi'.toString()")
 
-(test-ps-js method-call-op-form (.to-string (+ "" x)) "('' + x).toString()")
-(test-ps-js method-call-number (.to-string 10) "( 10 ).toString()")
-(test-ps-js method-call-string (.to-string "hi") "'hi'.toString()")
 (test-ps-js method-call-lit-object
-            (.to-string (create :to-string (lambda ()
-                                             (return "it works"))))
-            "( { toString : function () { return 'it works'; } } ).toString()")
+  ((@ (create :to-string (lambda () (return "it works"))) to-string))
+  "( { toString : function () { return 'it works'; } } ).toString()")
+
+(test-ps-js method-call-conditional
+  ((if a x y) 1)
+  "(a ? x : y)(1)")
 
 (test-ps-js method-call-variable
-            (.to-string x)
-            "x.toString()")
+  ((@ x to-string))
+  "x.toString()")
 
 (test-ps-js method-call-array
-            (.to-string (list 10 20))
-            "[ 10, 20 ].toString()")
+  ((@ (list 10 20) to-string))
+  "[ 10, 20 ].toString()")
+
 (test-ps-js method-call-fn-call
-            (.to-string (foo))
-            "foo().toString()")
+  ((@ (foo) to-string))
+  "foo().toString()")
+
 (test-ps-js method-call-lambda-fn
-            (.to-string (lambda () (alert 10)))
-            "( function () { alert(10); } ).toString()")
+  ((@ (lambda () (alert 10)) to-string))
+  "( function () { alert(10); } ).toString()")
+
 (test-ps-js method-call-lambda-call
-            (.to-string ((lambda (x) (return x)) 10))
-            "(function (x) { return x; })(10).toString()")
+  ((@ ((lambda (x) (return x)) 10) to-string))
+  "(function (x) { return x; })(10).toString()")
 
 (test no-whitespace-before-dot
-  (let* ((str (ps1* '(.to-string ((lambda (x) (return x)) 10))))
+  (let* ((str (ps1* '((@ ((lambda (x) (return x)) 10) to-string))))
          (dot-pos (position #\. str :test #'char=))
          (char-before (elt str (1- dot-pos)))
          (a-parenthesis #\)))
@@ -308,12 +323,16 @@ x = 2 + sideEffect() + x + 5;")
   (slot-value foo "bar")
   "foo['bar']")
 
+(test-ps-js slot-value-string1
+  (slot-value "bar" 'length)
+  "'bar'.length")
+
 (test-ps-js slot-value-progn
   (slot-value (progn (some-fun "abc") "123") "length")
   "(someFun('abc'), '123')['length']")
 
 (test-ps-js method-call-block
-  (.to-string (progn (some-fun "abc") "123"))
+  ((@ (progn (some-fun "abc") "123") to-string))
   "(someFun('abc'), '123').toString()")
 
 (test-ps-js create-blank
@@ -324,14 +343,6 @@ x = 2 + sideEffect() + x + 5;")
   {}
   "{ }")
 
-(test-ps-js object-literal-1
-  ({})
-  "{ }")
-
-(test-ps-js object-literal-2
-  ({} a 1 b 2)
-  "{a: 1, b: 2 }")
-
 (test-ps-js array-literal1
   []
   "[]")
@@ -673,3 +684,10 @@ try {
   (((or (@ window eval) eval)) foo nil)
   "(window.eval || eval)()(foo, null)")
 
+(test-ps-js slot-value-object-literal
+  (slot-value (create :a 1) 'a)
+  "({ a : 1 }).a")
+
+(test-ps-js slot-value-lambda
+  (slot-value (lambda ()) 'prototype)
+  "(function () { }).prototype")
index 346dfb5..1e85593 100644 (file)
   "foobar(blorg(1, 2), blabla(3, 4), [ 2, 3, 4 ])")
 
 (test-ps-js function-calls-and-method-calls-3
-  ((aref foo i) 1 2)
-  "foo[i](1, 2)")
+  ((slot-value this 'blorg) 1 2)
+  "this.blorg(1, 2)")
 
 (test-ps-js function-calls-and-method-calls-4
-  (.blorg this 1 2)
-  "this.blorg(1, 2)")
+  ((aref foo i) 1 2)
+  "foo[i](1, 2)")
 
 (test-ps-js function-calls-and-method-calls-5
-  (this.blorg 1 2)
-  "this.blorg(1, 2)")
+  ((slot-value (aref foobar 1) 'blorg) NIL T)
+  "foobar[1].blorg(null, true)")
 
 (test-ps-js function-calls-and-method-calls-6
-  (.blorg (aref foobar 1) NIL T)
-  "foobar[1].blorg(null, true)")
+  (this.blorg 1 2)
+  "this.blorg(1, 2)")
 
 (test-ps-js operator-expressions-1
   (* 1 2)