jbr / sibilant

Just another compile-to-js LISP-like language
https://sibilant.org
MIT License
386 stars 47 forks source link

Experimental idea: Support target languages other than js #87

Open jbr opened 8 years ago

jbr commented 8 years ago

Wacky idea: Provide macros that compile to another language with similar semantics to js, which I think is pretty much just first class lambdas, no type system, and a similar notion of scope. This would potentially include ruby and python initially.

If sibilant were able to bootstrap compile on several vms/languages, it could provide a cross language meta language, which would be snazzy. Post- #86, sibilant will have a powerful-enough macro system designed specifically for outputting arbitrary source code and a small enough language core that this should be possible. Initially I'd use the js version to compile a ruby version probably.

Differences between languages limits the power of this idea for generating idiomatic code

riatzukiza commented 8 years ago

I noticed this was possible as soon as I looked at it. Seems you've created a markdown syntax for generating any arbitrary programming language.

I like the fact that sibilant is idiomatic to javascript, but the only reason I like that is because it is compileing to javascript, and I am ultimately still working with javascript. If I were to use this atop another language, I think I would want it to be idiomatic to the underlying language, rather then attempting to be idiomatic to javascript across the board.

Doing it this way allows for a language to maintain all of its various strengths and weaknesses, with sibilant just being a light, but powerful, functional lispy macro layer on top of the language, with the base AST of the sibilant markup being as close as possible in structure to the AST of the underlying language.

A quick example of what I mean assumeing C as the underlying language Here is the fibonacci sequence in C from this link: http://www.programmingsimplified.com/c-program-generate-fibonacci-series

#include<stdio.h>

int main()
{
   int n, first = 0, second = 1, next, c;

   printf("Enter the number of terms\n");
   scanf("%d",&n);

   printf("First %d terms of Fibonacci series are :-\n",n);

   for ( c = 0 ; c < n ; c++ )
   {
      if ( c <= 1 )
         next = c;
      else
      {
         next = first + second;
         first = second;
         second = next;
      }
      printf("%d\n",next);
   }

   return 0;
}

Same program now, but with a hypothetical Sibilant macro layer

(#include 'stdio.h)
(int main ()
    (int n null
          first 0
          second 1
          next null
          c null)
    (printf "Enter the number of terms\n")
    (scanf "%d",&n)
    (printf "First %d terms of Fibonacci series are :-\n",n)
    (for (assign c 0) (< c n) (post-increment c)
          (if (<= c 1)
              (assign next c)
              (do
                (assign next (+ first second))
                (assign first second)
                (assign second next)))
          (printf "%d\n" next))
    (return 0))

In doing this, I kept the keywords exactly the same in the lisp layer as in the C program, as a result, this is a lisp language that any one who uses C regularly would be able to pick up with no effort at all.

riatzukiza commented 8 years ago

Of course, since there are macros, over top of this you could still have the exact semantics of sibilant as it is also implemented in addition to the compile target specific keywords. There are some languages this would be a challenge to do, but given the nature of the macroing system, I believe it is entirely possible to do for just about any underlying language.

riatzukiza commented 8 years ago

How ever you end up wanting to do it, the above is just a bunch my thoughts on the matter. I just picked up sibilant and am really enjoying it. Once I get really comfortable in the environment I would love to help you further this project in what ever way I am able to.

riatzukiza commented 8 years ago

I have created a gist of a macro language that can almost reproduce the example I gave above for C. I am having a difficult time properly placing semicolons. Also note that there are some differences in the languages syntax than the example I gave, I wrote the example quite quickly and hadn't considered too precisely the implementation at the time.

If a given language can be generated through macros like described, then it should also be possible to implement the rest of the sibilant language to compile to the target.

This will result in two languages being implemented for any given target language

Meaning that for if one wanted to make code that would execute on any platform that sibilant can target, you should just need to stick to whats in the sibilant docs.

Optionally, for any given target language, there are specific optimizations that can be made by directly using language features that belong to the underlying language, so in the case of typed languages this gives sibilant an optional type system.

jc00ke commented 8 years ago

I think it would be neat to target some intermediate representation like LLVM IR and then let it kick out compiled native code to whatever arch you want, even WebAssembly (at some point)

riatzukiza commented 8 years ago

As the code is right now, it is a macro layer that does text manipulation intermingled with ast manipulation.

I mean, if you wanted to set it up to output binary buffers instead of strings and manipulate the code as a mixture of byte code and symbols, go ahead.

But the reason for the descriptions above is because, since the environment is already built to built strings from arbitrary AST's, you could just return a bunch of C or C++ strings out, the compile those, and you get all the benefits of those environments. Not only are you getting the optimized byte code represenation, but you are also getting access to what ever other constructs are available in the underlieing language. So all the C++ libraries, templates (which as clunky as they are, are actually a rather powerful meta programming tool them selves), types, etc.

Basicly, follow the ancient rule, don't remake what you can beg borrow or steal. In this case, just tag on a bunch of macros and steal the entire C++ langauge.

As I demonstrated above, this would not actually be very hard at all to do. The sibilant macro system is very powerful, though there is kind of a lack of examples for them.

To give an example in terms of javascript, I was able to implement two ES6 features in sibilant that it did not have a concept of before, very quickly. The class syntax and the arrow syntax.

(import-namespace core) ;;need to do this to get access to the ^do macro. 
;;Arrow function
(macro => (args ...body)
       ["(" ...(interleave ", "args.contents) ") => {\n\t"  (indent (apply ^do body)) "\n}"])

;;class syntax
(macro class (name filler type ...rest)
       (def specialMethod (type name args ...body)
         [
         type.token " " name.token "(" ...(interleave ", " (map args.contents (# (c) c.token)))  ") {"
         (indent (apply ^do body))
         "}"
         ])
       (def methodStatement (name args ...body)
         (if (or (= name.token "static")
                 (= name.token "get")
                 (= name.token "set"))
             (specialMethod name args ...body)
             [ name.token "("
             ...(interleave ", " (map args.contents
                                      (# (c) c.token)))
             ") {"
             (indent (apply ^do body))
             "}\n" ])
         )
       (if (= filler.token  "extends")
           ["class " name " extends " type "{\n"
            ...(map rest (# (m)
                            (var method (apply methodStatement m.contents))
                            (indent method)))"}"]
         ["class " name  "{\n"
          ...(map [filler type ...rest] (# (m)
                          (var method (apply methodStatement m.contents))
                          (indent method)))"}"]))

I am not totaly sure I needed to use ^do, but when looking for the code that generated other function types in the source, this is what I found was used.