santoshrajan / lispyscript

A javascript with Lispy syntax and macros
MIT License
572 stars 57 forks source link

js loops (for, while) #69

Closed shaunlebron closed 8 years ago

shaunlebron commented 8 years ago

Lispyscript almost lets me write straight s-expression flavored JS, but it doesn't allow direct access to js loops for and while. This is perhaps a non-goal, but my use-case requires that I save on the runtime size and performance with direct loops. For now, I'm making do with the following macros, which compile to much larger forms than needed.

(macro forloop (i start n rest...)
  (loop (i) (~start)
    (if (< i ~n)
      (do
        ~rest...
        (recur (+ i 1)))
      null)))

(macro while (condition rest...)
  (loop () ()
    (if ~condition
      (do
        ~rest...
        (recur))
      null)))
darrencruse commented 8 years ago

Hi Shaun it's been a little while since I've looked at Lispyscript but I thought I could take a stab at helping a little... (the original author Santosh Rajan had started working on Lispyscript 2 btw and I'm a little out of touch on who's supporting the project now - I was for awhile but got pulled away with other priorities).

Anyway I don't mean to speak for Santosh but I'm guessing (as you hinted at when you said "this is perhaps a non-goal") that the first most obvious answer to your question is that Santosh intended Lispyscript to be a functional language whereas the classic "for" and "while" are statements. i.e. as a functional language the design of lispyscript intended everything to be an expression that returns a value. I'm guessing this is the root of why you don't see clear direct support for the classic js "for" and "while" in Lispyscript.

That aside, I'm guessing you've looked at the provided macros that's where I would start: https://github.com/santoshrajan/lispyscript/blob/master/src/macros.ls

e.g. had you'd noticed there is an "each" macro there that expands directly to the ES5 "forEach"?

Does that help you at all?

Also note where some of those macros use (javascript "...") to directly output a javascript code string in the generated output. I'm not sure if that would let you cobble together a macro that would directly output a simple "for" or "while" or not (my doubt being that I think the argument to (javascript "...") doesn't anticipate expanding arguments inside that javascript code string - but it's been a while I could be wrong, and even if that's true you might be able to break it up in multiple pieces? i.e. (javascript "while(") ~condition (javascript ") {") etc. maybe?

Lastly what I found neat about lispyscript is the code base is really tiny. If you really want this it wouldn't be that hard for you to add it yourself as not a macro but rather a keyword which is what the lispyscript code calls the "built-ins" that are translated to javascript using code.

If you look in this file: https://github.com/santoshrajan/lispyscript/blob/master/lib/ls.js

Look at some of the functions there like keywords["if"], keywords["var"], keywords["function"] etc. and you'll pretty quickly pick up the pattern for how you could do the same to add your own "for"/"while" keywords as part of the transpilation code if you wanted to (though there is that "for" macro there so you might have to compromise and call your "for" something different - oh I see you did already so yeah you could stick with "forloop" not "for" to avoid the conflict).

Take what I say with a grain of salt since I haven't fiddled with lispyscript in awhile but hope this might help a little. As I hinted above the last I knew the project wasn't super active I'm not 100% sure how much help you'll get from others (but I could be out of date I don't know).

Darren

shaunlebron commented 8 years ago

Hi Darren, thanks for such a thorough rundown! I decided to use the javascript macro to avoid forking. Auto-insertion of semicolons makes interpolation a little awkward, but the result avoids a lot of overhead. Will revisit a keyword implementation if needed.

;; alias js => javascript
(macro js (rest...) (javascript ~@rest...))

;; Example: (while (< i 10) (console.log i))
(macro while (condition rest...)
  (do
    (js "for(;") ~condition (js "){")
    ~rest...
    (js "}")
    undefined))

;; Example: (forindex i 0 10 (console.log i))
(macro forindex (idx start end rest...)
  (do
    (js "var") ~idx
    (js "for(") (set ~idx ~start) (< ~idx ~end) (js "){")
    ~rest...
    (set ~idx (+ ~idx 1))
    (js "}")
    undefined))

;; Example: (foreach e [1 2 3] (console.log e))
(macro foreach (elm arr rest...)
  ((.forEach ~arr) (function (~elm) ~rest...)))