Giter Site home page Giter Site logo

vsedach / parenscript Goto Github PK

View Code? Open in Web Editor NEW
244.0 24.0 33.0 4.09 MB

MOVED TO https://gitlab.common-lisp.net/parenscript/parenscript

Home Page: https://gitlab.common-lisp.net/parenscript/parenscript

License: BSD 3-Clause "New" or "Revised" License

Common Lisp 99.17% Emacs Lisp 0.83%

parenscript's Introduction

Parenscript is a translator from an extended subset of Common Lisp to
JavaScript. Parenscript code can run almost identically on both the
browser (as JavaScript) and server (as Common Lisp).

Parenscript code is treated the same way as Common Lisp code, making
the full power of Lisp macros available for JavaScript. This provides
a web development environment that is unmatched in its ability to
reduce code duplication and provide advanced meta-programming
facilities to web developers.

At the same time, Parenscript is different from almost all other
"language X" to JavaScript translators in that it imposes almost no
overhead:

  No run-time dependencies:
    Any piece of Parenscript code is runnable as-is. There are no
    JavaScript files to include.
  Native types:
    Parenscript works entirely with native JavaScript data types. There
    are no new types introduced, and object prototypes are not
    touched.
  Native calling convention:
    Any JavaScript code can be called without the need for
    bindings. Likewise, Parenscript can be used to make efficient,
    self-contained JavaScript libraries.
  Readable code:
    Parenscript generates concise, formatted, idiomatic JavaScript
    code. Identifier names are preserved. This enables seamless
    use of JavaScript debuggers.
  Efficiency:
    Parenscript introduces minimal overhead for advanced Common Lisp
    features. The generated code is almost as fast as hand-written
    JavaScript.

Parenscript is available via Quicklisp:
  (ql:quickload :parenscript)

To run unit tests:
  (ql:quickload :parenscript.tests)
  (parenscript.tests:run-tests)

Contributing:
  Please send patches and bug reports to the mailing list:
    [email protected]

Documentation:
  See docs/tutorial.html and docs/reference.html
Mailing list:
  [email protected]
  https://mailman.common-lisp.net/listinfo/parenscript-devel
Web site:
  http://common-lisp.net/project/parenscript/
Source repository:
  https://gitlab.common-lisp.net/parenscript/parenscript.git
License:
  BSD-3-Clause, see the file COPYING

parenscript's People

Contributors

3b avatar agrostis avatar attila-lendvai avatar eugeneia avatar fstamour avatar gonzojive avatar gruseom avatar jasom avatar lcapello avatar lemaster avatar marijnh avatar neil-lindquist avatar pjstirling avatar puercopop avatar raymyers avatar segv avatar tatrix avatar traviscross avatar tychew avatar vsedach avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

parenscript's Issues

PS adds a semicolon after function declarations

Simple example:

CL-USER> (ps:ps (defun test ()))
"function test() {
    return null;
};"

Is this by design? It's completely useless in this case.

It's not that it bothers, since it just creates a new empty statement, but I'm wondering if it should be considered a bug.

Bitwise operation dont work

For example:

CL-USER> (ps:ps (setf foo (& 2 3)))
"foo = &(2, 3);"

Expected result is:

foo = 2 & 3

The same with other similar operators:

CL-USER> (ps:ps (setf foo (^ 2 3)))
"foo = caret(2, 3);"
CL-USER> (ps:ps (setf foo (\| 2 3)))
"foo = |(2, 3);"
CL-USER> (ps:ps (setf foo (\|\| 2 3)))
"foo = ||(2, 3);"
CL-USER> (ps:ps (setf foo (&& 2 3)))
"foo = &&(2, 3);"

Parenscript version: 2.3

New release?

I spoke with Zach about getting an updated parenscript in quicklisp, and he indicated that he typically waits for a new release. Any chance of this happening?

(defvar suppress-values?) makes trouble in clozure-cl

(defvar suppress-values?)

in clozure-cl leaves suppress-values? unbound (as documented in Hyperspec). This leads to a "non-specific warning" when going through the parenscript tutorial

I guess the desired behaviour would be to have nil as default value (this fixes the error for me):

(defvar suppress-values? nil)

Best regards,
Olaf

Bug in inner-html and attribute DOM utilities

(inner-html (chain document document-element))

wrongly expands to

document.documentElement['inner-h-t-m-l'];

when it should expand to

document.documentElement.innerHTML;

inner-html is defined as

(defpsmacro inner-html (el)
  `(@ ,el :inner-h-t-m-l))

I guess :inner-h-t-m-l should be inner-html, that is, it should not be a keyword symbol.

A similar problem and solution with the attribute macro.

Odd ps-html output

When trying ps-html from docs/introduction.lisp:

(ps
  (defun add-div (name href link-text)
    (document.write
     (ps-html ((:div :id name)
               "The link is: "
               ((:a :href href) link-text))))))

I'm getting:

"function addDiv(name, href, linkText) {
    return document.write(['<DIV ID=\"', name, '\">The link is: <A HREF=\"', href, '\">', linkText, '</A></DIV>']['join'](''));
};"

instead of what I expected:

"
function addDiv(name, href, linkText) {
  document.write('<div id=\"' + name + '\">The link is: <a href=\"'
                                + href + '\">'
                                + linkText + '</a></div>');
}
"

Don't use namespacing in variable names

Hi,

The issue with using namespacing in variable names is that it will namespace everything, including any mention I make to document, window, etc. For example:

(chain document (query-selector-all selector))

Will output:

_prefix_document._prefix_querySelectorAll(_prefix_selector);

Which is not really what I want.

Any idea of how I can do that?

The thing is, I want to include the javascript in many web pages, so I cannot afford to not have namespaces. I also want to have several files, so I need the packaging system :-)

The solution I'm using for now is something like this:

(setf (getprop window '*foo*) (or (getprop window '*foo*) (create)))
(setf (getprop (getprop window '*bar*) 'bar) #'bar) ;; export FOO.bar

Basically, using js "namespacing" manually. I have to wrap this in an IIFE manually when using ps:compile-file though, so my code ends up like this:

(defparameter *foo-ps* (concatenate
                   'string
                   ";(function(){"
                   (ps:ps-compile-file
                     (asdf:system-relative-pathname :bar "foo.ps"))
                   "}());"))

To have the js code available.

I'd love a better solution, but I don't see it yet.

Cheers,
Florian

Invalid code generated by loop (break inside a closure)

(ps:ps
(defun foo (t1 t5)
  (loop for t2 in t1 
     for t4 in t5
     collect (loop for t3 in t2 collect t3))))

generates

function foo(t1, t5) {
    __PS_MV_REG = [];
    return (function () {
        var _js54 = t1.length;
        var _js56 = t5.length;
        var collect57 = [];
        var FIRST58 = true;
        for (var _js53 = 0; _js53 < _js54; _js53 += 1) {
            (function () {
                var t2 = t1[_js53];
                var _js55 = FIRST58 ? 0 : _js55 + 1;
                if (_js55 >= _js56) {
                    break;
                };
                var t4 = t5[_js55];
                collect57.push((function () {
                    var _js60 = t2.length;
                    var collect61 = [];
                    for (var _js59 = 0; _js59 < _js60; _js59 += 1) {
                        var t3 = t2[_js59];
                        collect61.push(t3);
                    };
                    __PS_MV_REG = [];
                    return collect61;
                })());
                __PS_MV_REG = [];
                return FIRST58 = null;
            })();
        };
        __PS_MV_REG = [];
        return collect57;
    })();
};

Note that the entire outer-loop body is inside a js function but it still contains break statements.

Reverting e6489e0 fixes this issue.

This is reduced from a regression in real-world code.

eql is undefined

Attempting to pass eql as a parameter compiles fine but leads to a runtime error in the generated JavaScript. It seems like it should just work. I'm not sure what the right fix is.

Adding a simple (defun eql (x y) (eql x y)) to my code fixes this, but I don't think users should have to do that.
Also, the documentation says not to use eql as the name of a function, so that might be breaking the rules.

We could add eql to *ps-lisp-library*, but that seems weird.

The compiler could generate an anonymous function when eql appears out of operator position, but then window["eql"] would still be undefined, which I guess it shouldn't be.

I'm open to ideas and willing to implement the right solution if we can figure out what it is.

Deprecation warning for symbols with embedded dot (.) or bracket ([]) chars only triggered if symbol also contains one of -*+!?#@%/=:<>^

version

Parenscript version is 2.6 from quicklisp 2018-07-11.

steps to reproduce

In the below repl session, I would assume the intention is to also issue a deprecation warning for foo.bar and foobar[3].

CL-USER> (ps:ps foo.bar)
"foo.bar;"
CL-USER> (ps:ps foo.bar?)
WARNING: Symbol FOO.BAR? contains one of '.[]' - this compound naming convention is no longer supported by Parenscript!
"foo.barwhat;"
CL-USER> (ps:ps foobar[3])
"foobar[3];"
CL-USER> (ps:ps foo-bar[3])
WARNING: Symbol FOO-BAR[3] contains one of '.[]' - this compound naming convention is no longer supported by Parenscript!
"fooBar[3];"

description

The implementation of ps::encode-js-identifier in utils.lisp issues a deprecation warning if the user creates an identifier with an embedded dot (.) or sqaure brackets ([]); however, ps::encode-js-identifier will only issue the warning about embedded dot and/or square brackets if certain other special characters are present in the identifier name which cause Parenscript name mangling rules to kick in. I assume this is a bug, and the intention is to always warn about embedded dot/bracket characters?

Here is the relevant portion of ps::encode-js-identifier.

(let ((cache (make-hash-table :test 'equal)))
  (defun encode-js-identifier (identifier)
    "Given a string, produces to a valid JavaScript identifier by
following transformation heuristics case conversion. For example,
paren-script becomes parenScript, *some-global* becomes SOMEGLOBAL."
    (or (gethash identifier cache)
        (setf (gethash identifier cache)
              (cond ((some (lambda (c) (find c "-*+!?#@%/=:<>^")) identifier)
                     (let ((lowercase t)
                           (all-uppercase nil))
                       (when (and (not (string= identifier "[]")) ;; HACK
                                  (find-if (lambda (x) (find x '(#\. #\[ #\]))) identifier))
                         (warn "Symbol ~A contains one of '.[]' - this compound naming convention is no longer supported by Parenscript!"
                               identifier))
                         ...

If I understand correctly:

  • the cond test ensures that identifier contains at least one of the characters -*+!?#@%/=:<>^
    • therefore, the (string= identifier "[]") test can never be true
    • therefore, the warning about an identifier containing a period and/or square brackets can only be triggered if it also contains one of the characters -*+!?#@%/=:<>^

reduce ignores any "false" init argument

version

Parenscript version is 2.6 from quicklisp 2018-07-11.

steps to reproduce

(ps:ps
  (ps:lisp ps:*ps-lisp-library*)
  (reduce (lambda (acc e) (min acc e)) '(1 2) 0))

When the resulting Javascript is evaluated, the call to reduce returns 1, not 0.

description

The implementation of reduce in ps-runtime-lib.lisp looks like this:

(defun reduce (func list &optional init) ;; the use of init here is actually a bit broken wrt null
      (let* ((acc))
        (do* ((i (if init -1 0) (1+ i))
              (acc (if init init (elt list 0)) (func acc (elt list i))))
             ((>= i (1- (length list)))))
        acc))

The comment indicates that the init arg is broken w.r.t. null, but actually the init arg ends up being ignored if you pass anything Javascript considers "falsey".

Here are some examples of calling reduce from the Javascript console in Chrome dev tools. The last one is admittedly contrived.

> reduce((acc, e) => acc * e, [1,2,3,4,5], 0)
120 /* expect 0 */
> reduce((acc,e) => Math.min(acc, e), [1, 2], 0)
1 /* expect 0 */
> reduce((acc, e) => acc + e, [1.0, 2.0], NaN)
3 /* expect NaN */
> reduce((acc, e) => acc && e, [true,true,true], false)
true /* expect false */
> reduce((acc,e) => acc, ["foo", "bar"], "")
"foo" /* expect "" */

For reference, here is the corresponding Javascript version of reduce that is generated by Parenscript.

function reduce(func, list, init) {
    var acc = null;
    for (var i = init ? -1 : 0, acc = init ? init : list[0]; i < list.length - 1; i += 1, acc = func(acc, list[i])) {
    };
    return acc;
};

Of course, there is always Javascript's Array.prototype.reduce as a workaround for recent Javascript versions.

Duplicated variable initialization with multiple COLLECT clauses into same result

(ps:ps
    (loop for i from 0 to 3
          for ch = (char "abcde" i)
          for p = (char-code ch)
          collect (logand (ash p -1) 1)
          collect (logand (ash p -0) 1)))

results in

(function () {
    var collect41 = [];
    var collect41 = [];
    for (var i = 0; i <= 3; i += 1) {
        var ch = char('abcde', i);
        var p = charCode(ch);
        collect41.push(p >> 1 & 1);
        collect41.push(p >> 0 & 1);
    };
    __PS_MV_REG = [];
    return collect41;
})();

Please note the duplicated var collect41 statements.

I don't think this breaks JS, but at least it is ugly.

Package ( does not exist.

When trying to load parenscript @ 101b09e:

CL-USER> (asdf:operate 'asdf:load-op 'parenscript)
; compiling file "/home/burton/common-lisp/lib/Parenscript/src/package.lisp" (written 29 JAN 2015 09:42:45 AM):
; compiling (IN-PACKAGE #:CL)
; compiling (PUSHNEW :PARENSCRIPT ...)
; compiling (UNLESS (EDITOR-HINTS.NAMED-READTABLES:FIND-READTABLE :PARENSCRIPT) ...)
; compiling (EDITOR-HINTS.NAMED-READTABLES:IN-READTABLE :PARENSCRIPT)
; compiling |(DEFPACKAGE|
; compiling #:PARENSCRIPT
;
; caught ERROR:
; READ error during COMPILE-FILE:
;
; Package ( does not exist.
;
; Line: 14, Column: 7, File-Position: 343
;
; Stream: #<SB-SYS:FD-STREAM
; for "file /home/burton/common-lisp/lib/Parenscript/src/package.lisp"
; {10133948B3}>
; compilation aborted after 0:00:00.027
;
; compilation unit aborted
; caught 3 fatal ERROR conditions
; caught 1 ERROR condition
; Evaluation aborted on #<UIOP/LISP-BUILD:COMPILE-FILE-ERROR {10031F4823}>.

The |(DEFPACKAGE| looks very suspect but I can't seem to figure out what is wrong.

Macro expansion inside of chain.

There seems to either be an issue with macro expansion inside of chain, or I missed something big in the manual.

(chain (delay 800)
       (@ ($ "#connection") fade-out 1000))

"delay(800).at($('#connection'), fadeOut, 1000);"

this value not preserved when implicitly emitting anonymous functions

For example when an anonymous function is emitted for a let form.

The fix is to just bind the anonymous function to this; I currently haven't dug into the code to see how easy this change is to make; it seems the only time we don't want to bind the function is when we emit a function for DEFUN or LAMBDA. Am I wrong?

Parenscript 2.7.1 does not seem to compile on LispWorks

I am using LispWorks 7.1 and recently updated my Quicklisp dist. This seems to have broken Parenscript. It no longer compiles on LispWorks, while it does on Clozure CL and SBCL.

Here is the output I get:

CL-USER 1 > (ql:quickload :parenscript)
To load "parenscript":
  Load 1 ASDF system:
    parenscript
; Loading "parenscript"
..

**++++ Error between functions:
  Cannot read character U+0082 as part of a token because it has constituent trait 'invalid'.

**++++ Error between functions:
  Cannot read character U+0082 as part of a token because it has constituent trait 'invalid'.

**++++ Error between functions:
  Cannot read character U+0082 as part of a token because it has constituent trait 'invalid'.

**++++ Error between functions:
  Cannot read character U+0082 as part of a token because it has constituent trait 'invalid'.

**++++ Error between functions:
  Cannot read character U+0082 as part of a token because it has constituent trait 'invalid'.

**++++ Error between functions:
  Cannot read character U+0081 as part of a token because it has constituent trait 'invalid'.

**++++ Error between functions:
  Cannot read character U+0082 as part of a token because it has constituent trait 'invalid'.

**++++ Error between functions:
  Cannot read character U+0082 as part of a token because it has constituent trait 'invalid'.

**++++ Error between functions:
  Cannot read character U+0082 as part of a token because it has constituent trait 'invalid'.

**++++ Error between functions:
  Cannot read character U+0082 as part of a token because it has constituent trait 'invalid'.

**++++ Error between functions:
  Cannot read character U+0082 as part of a token because it has constituent trait 'invalid'.

**++++ Error between functions:
  Cannot read character U+0081 as part of a token because it has constituent trait 'invalid'.

**++++ Error between functions:
  Cannot read character U+0082 as part of a token because it has constituent trait 'invalid'.

**++++ Error between functions:
  Cannot read character U+0082 as part of a token because it has constituent trait 'invalid'.

**++++ Error between functions:
  Cannot read character U+0082 as part of a token because it has constituent trait 'invalid'.

**++++ Error between functions:
  Cannot read character U+0082 as part of a token because it has constituent trait 'invalid'.

**++++ Error between functions:
  Cannot read character U+0082 as part of a token because it has constituent trait 'invalid'.

**++++ Error between functions:
  Cannot read character U+0081 as part of a token because it has constituent trait 'invalid'.

**++++ Error between functions:
  Cannot read character U+0082 as part of a token because it has constituent trait 'invalid'.

**++++ Error between functions:
  Cannot read character U+0082 as part of a token because it has constituent trait 'invalid'.

**++++ Error between functions:
  Cannot read character U+0082 as part of a token because it has constituent trait 'invalid'.

**++++ Error between functions:
  Cannot read character U+0082 as part of a token because it has constituent trait 'invalid'.

**++++ Error between functions:
  Cannot read character U+0082 as part of a token because it has constituent trait 'invalid'.

**++++ Error between functions:
  Cannot read character U+0081 as part of a token because it has constituent trait 'invalid'.
..
; *** 24 errors detected, no fasl file produced.

Error: COMPILE-FILE-ERROR while compiling
   #<ASDF/LISP-ACTION:CL-SOURCE-FILE "parenscript" "src" "special-operators">
  1 (continue) Retry compiling
   #<ASDF/LISP-ACTION:CL-SOURCE-FILE "parenscript" "src" "special-operators">.
  2 Continue, treating compiling
   #<ASDF/LISP-ACTION:CL-SOURCE-FILE "parenscript" "src" "special-operators">
    as having been successful.
  3 Retry ASDF operation.
  4 Retry ASDF operation after resetting the configuration.
  5 Retry ASDF operation.
  6 Retry ASDF operation after resetting the configuration.
  7 (abort) Give up on "parenscript"
  8 Return to top loop level 0.

Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.

multiple-value-bind cannot return multiple values

Simple example. Note that the finally block resets __MS_MV_REG to its previous value.

CL-USER> (parenscript:ps (multiple-value-bind (a b) (values 1 2) (values 3 4)))
"(function () {
    var val1099;
    var prevMv1097 = 'undefined' === typeof __PS_MV_REG ? (__PS_MV_REG = undefined) : __PS_MV_REG;
    try {
        var a = (val1099 = 1, (val1099));
        var _db1100 = values === __PS_MV_REG['tag'] ? __PS_MV_REG['values'] : [];
        var b = _db1100[0];
        __PS_MV_REG = { 'tag' : arguments.callee, 'values' : [4] };
        return 3;
    } finally {
        __PS_MV_REG = prevMv1097;
    };
})();"

loop macro bug

These two loops generate the same javascript output:
(loop while (a) do (b)))
(loop do (b) while (a)))

=> (function () {
while (a()) {
b();
};
})();

However, they loops are not equivalent. The former tests the condition before the loop, but the latter tests the condition /after/ the loop, so (b) is called at least once.

Regards
Erik

CL-USER 781 > (ps (loop while (a) do (b)))
"(function () {
while (a()) {
b();
};
})();"

CL-USER 782 > (ps (loop do (b) while (a)))
"(function () {
while (a()) {
b();
};
})();"

Case Inversion when loading Parenscript in Allegro CL

In order to get Parenscript to load without error in a case sensitive Lisp (in my case Allegro CL), I had to remove the statement (:case :invert) from line 9 in src/package.lisp.

This was because it changed calls like (symbol-name 'nil) which are expanded to (common-lisp::symbol-name) to (COMMON-LISP::symbol-name) and COMMON-LISP is not a valid way to reference the common-lisp package.

I did manage to get the package to load with that change, but it was tricky to track down.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.