author | Alan Dipert
<alan@dipert.org> 2020-02-21 07:03:56 UTC |
committer | Alan Dipert
<alan@dipert.org> 2020-02-21 07:03:56 UTC |
parent | 497b7f881537dc973c809b90f17b5bc187c1d9ed |
paper/jacl-els-2020.tex | +54 | -60 |
diff --git a/paper/jacl-els-2020.tex b/paper/jacl-els-2020.tex index d77c3a4..92c81b7 100644 --- a/paper/jacl-els-2020.tex +++ b/paper/jacl-els-2020.tex @@ -178,15 +178,9 @@ standard Lisp atoms and list syntax, but has the potential to be stymied by extended syntax, such as that added by reader macros. As such, the pre-reader is a separate, degenerate reader that limits what's capable of being read by the underlying, full-featured -synchronous reader. - -SLip's reader is also synchronous, and its Emacs clone, Ymacs, also -includes a pre-reader as part of its Lisp interaction mode. JSCL and -SLip both demonstrate that when the underlying reader facility is -synchronous, a REPL cannot effectively be made available at run-time -because of the asynchrony of input events in JavaScript. In the best -case, the reader is only partially available through a pre-reader that -accumulates characters asynchronously. +synchronous reader. SLip's reader is also synchronous, and its Emacs +clone, Ymacs, also includes a pre-reader as part of its Lisp +interaction mode. \subsubsection{Compiler organization} @@ -195,7 +189,9 @@ JSCL compilation is performed in two stages: \begin{itemize} \item Conversion from Lisp to a JavaScript Abstract Syntax Tree (AST) represented as S-expressions. - \item Conversion from JavaScript AST to JavaScript strings. + \item Conversion from JavaScript AST to JavaScript strings. Some + code-size optimization of arithmetic expressions is also performed + in this stage. \end{itemize} The first stage, the conversion from Lisp to JavaScript Abstract @@ -221,10 +217,10 @@ More efficient ways of implementing \texttt{TAGBODY} are not hard to imagine, but the JSCL compiler does not amene itself to the implementation of this or other high-level optimizations, as JSCL lacks a sufficiently expressive intermediate representation -(IR). High-level, semantic optimizations and transformations are -important for a Lisp to JavaScript compiler to perform, because they -are exactly the kind that a JavaScript optimizer could not later -perform, provided only JavaScript code. +(IR). High-level optimizations and transformations are important for a +Lisp to JavaScript compiler to perform, because they are exactly the +kind that a JavaScript optimizer could not later perform, provided +only JavaScript code. On the other hand, the arithmetic expression code-size optimizations performed by the second, code-generation stage of the JSCL compiler @@ -246,13 +242,11 @@ Since its introduction\cite{CljsRelease}, ClojureScript has heavily promoted and prioritized the ability to produce high-performance deliverables. It has always been capable of generating JavaScript deliverables amenable to aggressive optimization by the Google Closure -Compiler. - -In this respect, ClojureScript aligns closely with the JACL project -goal of competitive application performance. In fact, experience with, -and admiration for, ClojureScript is the reason the ability to produce -high-performance deliverables is considered a crucial capability of -JACL. +Compiler. In this respect, ClojureScript aligns closely with the JACL +project goal of competitive application performance. In fact, +experience with, and admiration for, ClojureScript is the reason the +ability to produce high-performance deliverables is considered a +crucial capability of JACL. Other than the fact that JACL is a Common Lisp and ClojureScript is not, the biggest difference between the two is JACL's promotion of a @@ -296,7 +290,7 @@ features will be accessible from Lisp, perhaps as a set of implementation-dependent \emph{declaration specifiers} available in \texttt{DECLARE} expressions. -The following example demonstrates, in JavaScript, the mechanism by +The following example demonstrates, in JavaScript, the process by which the JACL reader consumes characters and produces Lisp objects. A \texttt{BufferedStream} and \texttt{Reader} are instantiated, sent characters asynchronously, and then the resulting Lisp object is @@ -342,11 +336,9 @@ analyzed, compiled, and evaluated. Concurrently, characters may be sent to the \texttt{BufferedStream} instantiated by the REPL by calling its \texttt{write()} or -\texttt{writeEach()} methods. - -Neither character input nor read object consumption impede other -JavaScript operations, so the JACL REPL model supports embedding in -applications. +\texttt{writeEach()} methods. Neither character input nor read object +consumption impede other JavaScript operations, so the JACL REPL is +suitable for embedding in applications. Because of the platform and implementation-dependent nature of JACL's reader, JACL does not support Common Lisp input streams, nor its @@ -356,10 +348,6 @@ functions. Standard interfaces for extending the reader, such as the supported. However, the JACL reader does provide an implementation-specific way to define reader macros. -JACL's reader is far from supporting all of Common Lisp syntax, but -its fundamental design is complete and unlikely to substantially -change. - \subsection{Chrome DevTools REPL} A browser-based REPL facilitates experimentation with the language by @@ -401,11 +389,10 @@ anticipated that additional host-side tools that depend on \texttt{jacl-client} will be necessary in the future to support loading source files in dependency order. -An advantage of the reader approach taken by JACL with respect to the -implementation of \texttt{jacl-client} is that the REPL client is -completely ignorant of Lisp syntax and semantics. \texttt{jacl-client} -merely transfers characters between the host machine and the Lisp -system and so is \emph{not} a pre-reader. +Unlike the pre-readers of SLip and JSCL, \texttt{jacl-client} is +completely ignorant of Lisp syntax. \texttt{jacl-client} merely +transports characters between the host machine and the Lisp system and +so is \emph{not} a pre-reader. There are a few obvious ways the JACL REPL experience could be improved. For example, \texttt{jacl-repl} is currently an @@ -475,8 +462,8 @@ syntax. There must be as many placeholders as there are arguments to In addition to \texttt{JACL:\%JS}, the JACL compiler currently supports three more special operators for interacting with the host -platform, \texttt{JACL:\%NEW}, \texttt{JACL:\%DOT} and -\texttt{JACL:\%CALL}. They perform JavaScript object instantiation, +platform: \texttt{JACL:\%NEW}, \texttt{JACL:\%DOT} and +\texttt{JACL:\%CALL}. These perform JavaScript object instantiation, field access, and function calls, respectively. Since JACL functions compile into JavaScript functions, \texttt{JACL:\%CALL} is the basis for JACL's \texttt{FUNCALL}, and for function calls generally. @@ -485,12 +472,13 @@ JACL also supplies a convenience macro, \texttt{JACL:\textbackslash.} or ``the dot macro'' for performing a series of field accesses and method calls\footnote{Strictly speaking, JavaScript ``method calls'' are normal function calls but with a particular value of - \texttt{this}.} concisely. Dot takes direct inspiration from -Clojure's \texttt{..} macro. \texttt{JACL:\textbackslash.} expands to -zero or more nested \texttt{JACL:\%DOT} or \texttt{JACL:\%CALL} -forms. Here is an example of a \texttt{JACL:\textbackslash.} form --- -equivalent to the JavaScript expression -\texttt{(123).toString().length} --- and its corresponding expansion: + \texttt{this}.} concisely. The dot macro takes direct inspiration +from Clojure's \texttt{..} macro. \texttt{JACL:\textbackslash.} +expands to zero or more nested \texttt{JACL:\%DOT} or +\texttt{JACL:\%CALL} forms. Here is an example of a +\texttt{JACL:\textbackslash.} form --- equivalent to the JavaScript +expression \texttt{(123).toString().length} --- and its corresponding +expansion: \begin{verbatim} (\. 123 (|toString|) |length|) @@ -499,7 +487,8 @@ equivalent to the JavaScript expression Note that JavaScript identifiers are case sensitive, and so case-preserving, pipe-delimited Lisp symbols must be used to refer to -JavaScript object field and method names. The dot macro also +JavaScript object field and method names. The JACL reader's +\emph{readtable case} cannot currently be modified. The dot macro also recognizes Lisp or JavaScript strings as JavaScript identifiers. \subsubsection{\texttt{TAGBODY} compilation strategy} @@ -510,9 +499,9 @@ through the combination of: \begin{itemize} \item Generating a unique identifier, the \texttt{tbidx}, for each \texttt{TAGBODY} encountered during compilation. - \item A local JavaScript \texttt{branch} variable containing the tag - of the next branch to execute. - \item Labeled \texttt{while(true) { ... }} loops each containing a + \item A local JavaScript \texttt{branch} variable in the generated + code containing the label of the next branch to execute. + \item Generated \texttt{while(true) { ... }} loops each containing a \texttt{try/catch} statement. The \texttt{catch} arm of the \texttt{try/catch} inspects the type of thrown exception, and if it is of the marker type \texttt{TagNLX} and corresponds to the @@ -554,10 +543,10 @@ them, thereby reducing generated code size. Code generated for \texttt{TAGBODY} in JACL will still generally require a \texttt{switch}, and so a slowdown relative to hand-coded JavaScript loops would not be unexpected. However, preliminary -benchmarking \ref{appendix:hypothetical} of a factorial function on a +benchmark \ref{appendix:hypothetical} of a factorial function on a recent version of Google Chrome revealed only a very small discrepancy between JACL's proposed approach and a hand-coded JavaScript -\texttt{while} loop-based approach. +\texttt{while} loop-based approach \ref{appendix:baseline}. \subsection{Delivery} @@ -567,9 +556,10 @@ implemented at all. Fortunately, since JACL development is image-based, JACL should support the traditional approach of specifying a Lisp function -entrypoint and dumping the Lisp image to native code. SBCL's -\texttt{SAVE-LISP-AND-DIE} and LispWork's \texttt{DELIVER} function -are two examples of this in other implementations. +entrypoint and dumping the Lisp image to native (JavaScript) +code. SBCL's \texttt{SAVE-LISP-AND-DIE} and LispWork's +\texttt{DELIVER} function are two examples of this in other +implementations. It is imagined that JACL's delivery function will be called in a REPL at development time by a developer, or by an automated build @@ -594,9 +584,6 @@ these definitions can be generated at delivery time. Alternatively, statically-linked code could be created automatically in addition to dynamically-linked code. -Without experience with the feature, it's hard to know which approach -to code generation will ultimately be best. - \section{Conclusions and Future Work} JACL, a new Common Lisp created to ease SPA development, was @@ -604,10 +591,15 @@ introduced. Four novel aspects of JACL were described with respect to other, comparable Lisp implementations: \begin{enumerate} - \item Asynchronous reader. - \item Analyzing compiler. - \item Chrome DevTools REPL. - \item Delivery facility design. + \item An asynchronous reader that doesn't necessitate a pre-reader and is suitable for embedded use. + \item An analyzing compiler and AST to support high-level + optimizations, such as those necessary for a high-performance + \texttt{TAGBODY} implementation. + \item \texttt{jacl-client}, the Chrome DevTools-based REPL that + complements the asynchronous reader to provide a command-line REPL + facility on the host. + \item The tentative delivery facility design, which will support the + creation of competitively small and fast application artifacts. \end{enumerate} Most of Common Lisp that JACL will be capable of supporting remains @@ -655,6 +647,7 @@ beautiful wife, Sandra Dipert, for her skillful editing. \section{Loop Performance Benchmarks} \subsection{JavaScript Baseline} +\label{appendix:baseline} This is a simple JavaScript function for computing the factorial of a positive integer as efficiently as possible. Its performance is @@ -675,6 +668,7 @@ console.log("fact1", performance.now() - start); \end{verbatim} \subsection{Equivalent Lisp} +\label{appendix:lisp} The following is an analagous Common Lisp factorial function. \texttt{TAGBODY} is the means of iteration. This function