wisp-lang / wisp

A little Clojure-like LISP in JavaScript
https://gozala.github.io/wisp/
Other
982 stars 68 forks source link

how emit javascript code? #158

Closed francescoagati closed 6 years ago

francescoagati commented 6 years ago

hi, is possible from a macro emit custom js in raw mode? like coffeescript that use `` for inject javascript raw code inside the code

Thanks

chr15m commented 6 years ago

@francescoagati I can't figure out any way to do this but will merge a PR to do it if somebody supplies one.

francescoagati commented 6 years ago

Is because wisp can have differenti targets that Theresa isnt a RAW emitter of js? Shoild be nice emittenti new js for es6 async await generatore typescript ecc...

chr15m commented 6 years ago

@francescoagati I am not 100% sure what you mean but you can generate raw Javascript by doing:

wisp < myfile.wisp > myfile.js

francescoagati commented 6 years ago

for example this in sibilant js https://github.com/jbr/sibilant/blob/master/src/macros/lambda.sibilant#L56

chr15m commented 6 years ago

I'm afraid this code is beyond me. I do not know of a way in Wisp to inject raw Javascript. It may be possible but I am not sure. Perhaps @Gozala can advise?

Gozala commented 6 years ago

I'm afraid this code is beyond me. I do not know of a way in Wisp to inject raw Javascript. It may be possible but I am not sure. Perhaps @Gozala can advise?

It's being a while since I have looked at the code base so there's a risk I may get some things wrong, but I'll try to below.

Is because wisp can have differenti targets that Theresa isnt a RAW emitter of js?

Short answer is yes. More elaborate follows below

for example this in sibilant js https://github.com/jbr/sibilant/blob/master/src/macros/lambda.sibilant#L56

I'm getting an impression that sibilant macros emit JS source, which are at some point concatenated and evaluated. This is also how originally wisp worked when it got forked from lispyscript. Later on I have changed design as I had an ambition to have many different back-ends it could compile to, which is why right now wisp compilation consists of following phases:

  1. Read - Palin text is converted to forms (lists, and atoms)
  2. Analyze - Analyzer performs all the macro & syntax expansions and transforms forms into AST node of an expressions.
  3. Generate - Takes Wisp AST and generates final representation. Generators are in theory plugeable, as in I was planning to have one to generate Lua code and other to generate Swift. In practice though I only have ever created one for JS which currently translates Wisp AST to JS AST and uses escodegen to generate actual JS code. If I were to do it toady would have used babel instead & in fact one could write alternative generator that would do that instead.

Shoild be nice emittenti new js for es6 async await generatore typescript ecc...

I agree! That being said you do not really need to emit raw JS for that. To make it work you'd need to do following:

  1. Extend analyzer with new forms like await, have some form for async functions and include that info AST node of the function. Same with generators and whatever else you might support.
  2. Extend generator to emit JS corresponding to those forms. (I suspect you'd probably need to swap escodegen for babel but that's likely isn't going to a big problem as JS AST is compatible).

It's worth pointing out that while it does seem more difficult than just emitting JS strings it has few advantages:

  1. Everything is composeable. If macro just emit JS code it's no longer that case as you can't plug expansion of it into other arbitrary form, or more likely compiler might emit invalid JS.
  2. You can have different backend targets like I've mentioned earlier.

If above advantages is not something you care about there is also a possibility to do something along the lines of what sibilant does, by doing following:

  1. Introduce some special form say js so you could write things like (js "async funciton() {" ...))` which reader will be able to understand.
  2. Extend analyzer by introducing special AST node representing raw JS code.
  3. Extend generator which will recognize new raw JS AST nodes and incorporate them into the output (although you might have to hack your way around the escodegen, possibly by parsing that raw JS into JS AST and splicing into the actual AST you pass to escodegen).

With this option you'd be able to get what sibilant does but I'd recommend former approach instead.

chr15m commented 6 years ago

@Gozala thank you so much for taking the time to write this great info up.

I think it makes sense to merge a PR which can inline raw JS.

@francescoagati since Wisp can require native JS directly could that be an option and then pass the result through browserify or similar? You could put your native parts into a separate .js file.

francescoagati commented 6 years ago

@chr15m no i think that in this case using inline raw js is not compatible with the philosophy of wisp. is better modify the target for add new features of es6