| 1 | (in-package :ps) |
| 2 | ;;;# Introduction |
| 3 | ;;; |
| 4 | ;;; ParenScript is a simple language that looks a lot like Lisp, but |
| 5 | ;;; actually is JavaScript in disguise. Actually, it is JavaScript |
| 6 | ;;; embedded in a host Lisp. This way, JavaScript programs can be |
| 7 | ;;; seamlessly integrated in a Lisp web application. The programmer |
| 8 | ;;; doesn't have to resort to a different syntax, and JavaScript code |
| 9 | ;;; can easily be generated without having to resort to complicated |
| 10 | ;;; string generation or `FORMAT' expressions. |
| 11 | ;;; |
| 12 | ;;; An example is worth more than a thousand words. The following Lisp |
| 13 | ;;; expression is a call to the ParenScript "compiler". The |
| 14 | ;;; ParenScript "compiler" transforms the expression in ParenScript |
| 15 | ;;; into an equivalent, human-readable expression in JavaScript. |
| 16 | |
| 17 | (ps |
| 18 | (defun foobar (a b) |
| 19 | (return (+ a b)))) |
| 20 | |
| 21 | ;;; The resulting javascript is: |
| 22 | " |
| 23 | function foobar(a, b) { |
| 24 | return a + b; |
| 25 | } |
| 26 | " |
| 27 | ;;; Great care has been given to the indentation and overall |
| 28 | ;;; readability of the generated JavaScript code. |
| 29 | |
| 30 | ;;;# Features |
| 31 | ;;; |
| 32 | ;;; ParenScript supports all the statements and expressions defined by |
| 33 | ;;; the EcmaScript 262 standard. Lisp symbols are converted to |
| 34 | ;;; camelcase, javascript-compliant syntax. This idea is taken from |
| 35 | ;;; Linj by Antonio Menezes Leitao. Here are a few examples of Lisp |
| 36 | ;;; symbol to JavaScript name conversion: |
| 37 | |
| 38 | (js-to-string 'foobar) => "foobar" |
| 39 | (js-to-string 'foo-bar) => "fooBar" |
| 40 | (js-to-string 'foo-b-@-r) => "fooBAtR" |
| 41 | (js-to-string 'foo-b@r) => "fooBatr" |
| 42 | (js-to-string '*array) => "Array" |
| 43 | (js-to-string '*math.floor) => "Math.floor" |
| 44 | |
| 45 | ;;; It also supports additional iteration constructs, relieving the |
| 46 | ;;; programmer of the burden of iterating over arrays. |
| 47 | ;;; `for' loops can be written using the customary `DO*' syntax. |
| 48 | |
| 49 | (ps |
| 50 | (do* ((i 0 (incf i)) |
| 51 | (j (aref arr i) (aref arr i))) |
| 52 | ((>= i 10)) |
| 53 | (alert (+ "i is " i " and j is " j)))) |
| 54 | |
| 55 | ;; compiles to |
| 56 | " |
| 57 | for (var i = 0, j = arr[i]; i < 10; i = ++i, j = arr[i]) { |
| 58 | alert('i is ' + i + ' and j is ' + j); |
| 59 | }; |
| 60 | " |
| 61 | ;;; ParenScript uses the Lisp reader, allowing for reader macros. It |
| 62 | ;;; also comes with its own macro environment, allowing host Lisp |
| 63 | ;;; macros and ParenScript macros to coexist without interfering with |
| 64 | ;;; each other. For example, the `1+' construct is implemented using |
| 65 | ;;; a ParenScript macro: |
| 66 | |
| 67 | (defpsmacro 1+ (form) |
| 68 | `(+ ,form 1)) |
| 69 | |
| 70 | ;;; ParenScript allows the creation of JavaScript objects in a Lispy |
| 71 | ;;; way, using keyword arguments. |
| 72 | |
| 73 | (ps |
| 74 | (create :foo "foo" |
| 75 | :bla "bla")) |
| 76 | |
| 77 | ;; compiles to |
| 78 | " |
| 79 | { foo : 'foo', |
| 80 | bla : 'bla' } |
| 81 | " |
| 82 | ;;; ParenScript features a HTML generator. Using the same syntax as |
| 83 | ;;; the HTMLGEN package of Franz, Inc., it can generate JavaScript |
| 84 | ;;; string expressions. This allows for a clean integration of HTML in |
| 85 | ;;; ParenScript code, instead of writing the tedious and error-prone |
| 86 | ;;; string generation code generally found in JavaScript. |
| 87 | |
| 88 | (ps |
| 89 | (defun add-div (name href link-text) |
| 90 | (document.write |
| 91 | (ps-html ((:div :id name) |
| 92 | "The link is: " |
| 93 | ((:a :href href) link-text)))))) |
| 94 | |
| 95 | ;; compiles to |
| 96 | " |
| 97 | function addDiv(name, href, linkText) { |
| 98 | document.write('<div id=\"' + name + '\">The link is: <a href=\"' |
| 99 | + href + '\">' |
| 100 | + linkText + '</a></div>'); |
| 101 | } |
| 102 | " |
| 103 | ;;; In order to have a complete web application framework available in |
| 104 | ;;; Lisp, ParenScript also provides a sexp-based syntax for CSS |
| 105 | ;;; stylesheets. Thus, a complete web application featuring HTML, CSS |
| 106 | ;;; and JavaScript documents can be generated using Lisp syntax, |
| 107 | ;;; allowing the programmer to use Lisp macros to factor out the |
| 108 | ;;; redundancies and complexities of Web syntax. For example, to |
| 109 | ;;; generate a CSS inline node in a HTML document using the |
| 110 | ;;; AllegroServe HTMLGEN library: |
| 111 | |
| 112 | (html-stream *standard-output* |
| 113 | (html |
| 114 | (:html |
| 115 | (:head |
| 116 | (css (* :border "1px solid black") |
| 117 | (div.bl0rg :font-family "serif") |
| 118 | (("a:active" "a:hoover") :color "black" :size "200%")))))) |
| 119 | |
| 120 | ;; which produces |
| 121 | |
| 122 | <html><head><style type="text/css"> |
| 123 | <!-- |
| 124 | * { |
| 125 | border:1px solid black; |
| 126 | } |
| 127 | |
| 128 | div.bl0rg { |
| 129 | font-family:serif; |
| 130 | } |
| 131 | |
| 132 | a:active,a:hoover { |
| 133 | color:black; |
| 134 | size:200%; |
| 135 | } |
| 136 | |
| 137 | --> |
| 138 | </style> |
| 139 | </head> |
| 140 | </html> |