Skip to content

compiles scheme-like s-expressions to javascript/ecmascript

License

Notifications You must be signed in to change notification settings

sph-mn/sescript

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

94 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

  • a simpler ecmascript/javascript syntax. ecmascript written as scheme-like s-expressions
  • the output, after formatting, is supposed to be as if originally written in ecmascript
  • supports all es5
  • command-line compiler and scheme library
  • possibly also useful as an intermediate language for applications that want to compile to ecmascript
  • status: should work, been around for a while, easy to maintain and extend
  • license: gpl3+. does not apply to generated code. generated code has your license
  • you can try it out here

examples

echo "(define (test x y) (return #t))" | ses
->
function test(x, y){return(true)};
(declare d e f)

(define myobject
  (object
    a 1
    b (object c 2)))

(set
  d 1
  e 3
  f 9)

accessors

myobject.a.b
(get myobject "a" "b")
(get myarray 0 1)

extensions

  • adds return statement to the last expression in a function. lambda just feels wrong without this behaviour
  • let, let*

command-line application

$ ses --help
parameters
  options ... paths ...
description
  compile sescript to ecmascript. read from files or standard input,
  and write to a file or standard output depending on if paths are given as arguments.
  input/output depends on the number of given paths:
  none: read from standard input and write to standard output
  one: read from file and write to standard output
  two: read from the first file and write to the second
  more: read from all files except the last one and write to the last one
options
  --compress | -c  compress output with uglifyjs
  --format | -f  format output with uglifyjs
  --help | -h
  --interface

dependencies

installation

run the install script, "./exe/install". it supports one optional argument, an install path prefix.

alternatively

  • copy everything under modules/ into a directory that is in guiles default load path or $GUILE_LOAD_PATH
  • copy exe/ses to /usr/bin or wherever executables are to be installed

try to run ses --help to see if it works

usage from scheme

(import (sph lang sescript))

examples

(sescript->ecmascript-string (quote (begin (define a 1) (set a 2))))
(sescript->ecmascript (quote ((define a 1) (set a 2))) (current-output-port))

(define scheme-value 8)

(define code
  (quasiquote
    (begin
      (define a 1)
      (set a (unquote scheme-value)))))

(sescript->ecmascript-string code)

extension

how to add your own syntax: this is currently only possible when compiling with scheme and not via the sescript command-line interface. create a scheme file with new bindings, example below, and then use sescript->ecmascript-string or similar as usual

(import (sph lang sescript) (rnrs hashtables))

(hashtable-set! ses-descend-sescript (quote myprefix)
  (lambda (a compile)
    "(any:sescript-argument ...) procedure:recurse -> any:sescript
    create sescript that will be parsed again"
    (list (quote if) (car a) #t #f)))

(hashtable-set! ses-descend-ecmascript (quote myprefix2)
  (lambda (a compile)
    "(any:sescript-argument ...) procedure:recurse -> string:ecmascript
     create final ecmascript strings directly"
     (string-join (map compile a) "\n")))

(define code (quote (myprefix "test")))

(sescript->ecmascript-string code)

other

  • filename extension .sjs or .ses
  • --format and --compress option on the command-line if uglifyjs is installed
  • js-beautify is another recommended auto formatter
  • sescript only outputs valid ecmascript syntax
  • a benefit of using sescript is that editor modes for scheme syntax and structural editing can be used
  • other languages that compile to javascript

syntax reference

ses expression and the ecmascript result. taken from the automated tests

(array #\a)
->
["a"];

(array "\"")
->
["\""];

(array #t)
->
[true];

(begin a->b a-b a! a& a?)
->
a_to_b;a_b;a_x;a_ampersand;a_p;

(case a ((b c d) #t))
->
switch(a){case b:case c:case d:true;break};

(begin 1 a #t #\a "1")
->
1;a;true;"a";"1";

(begin 1 (begin 2 3))
->
1;2;3;

(case (+ 1 2) (2 #t #f) (3 #t) (else 4 5))
->
switch(1+2){case 2:true;false;break;case 3:true;break;default:4;5;break};

(= 1 2 3)
->
(1===2)&&(2===3);

(and 1 2 3)
->
(1&&2&&3);

(array 1 2 3)
->
[1,2,3];

(chain c (chain a b) 1 2 "d")
->
b.a().c(1,2,"d");

(cond (a b) ((= c 3) #f #t) (else #t #f))
->
if(a){b}else if(c===3){false;true;}else{true;false;};

(cond (a b))
->
if(a){b};

(declare a)
->
var a;

(declare a b c)
->
var a,b,c;

(define a 1)
->
var a=1;

(define a 1 b 2)
->
var a=1,b=2;

(define a (lambda (a b) 1 2 3))
->
var a=(function(a,b){1;2;return(3);});

(define (a b c-d) 1 2 3)
->
function a(b,c_d){1;2;return(3);};

(for ((set index 0) (< index len) (set index (+ 1 index))) #t)
->
for(index=0;(index<len);index=(1+index)){true};

(for (((set a 0) (set b 1)) (< index len) ((set a (+ 1 a)) (set b (+ 2 b)))) #t)
->
for(a=0,b=1;(index<len);a=(1+a),b=(2+b)){true};

(for ((begin a b) (< c d) (begin e f)) #t)
->
for(a,b;(c<d);e,f){true};

(get a 1)
->
a[1];

(get a 1 2 3)
->
a[1][2][3];

(get (get a 1) 2)
->
a[1][2];

(if* a (if* b c d) e)
->
(a?(b?c:d):e);

(if* a (if* (if* b c) d) e)
->
(a?((b?c:undefined)?d:undefined):e);

(if* 1 (lambda () #t) 2)
->
(1?(function(){return(true)}):2);

(if* (not 1) a b)
->
(!1?a:b);

(lambda (a) 1)
->
(function(a){return(1)});

(lambda () (if* a b c))
->
(function(){return(a?b:c)});

((lambda (a) 1))
->
(function(a){return(1)})();

(let (a 1) 2)
->
(function(a){return(2)})(1);

(let ((a 1) (b 2)) #t)
->
(function(a,b){return(true)})(1,2);

(let* ((a 1) (b 2) (c 3)) 4)
->
(function(a){var b=2;var c=3;return(4);})(1);

(make-regexp "[^a-b0-9]" "g")
->
/[^a-b0-9]/g;

(new obj)
->
new obj();

(new obj 3 5)
->
new obj(3,5);

(not 1)
->
!1;

(object)
->
{};

(object a 2 b-c 3 "d" 4)
->
{a:2,b_c:3,"d":4};

(object* a b c)
->
{a:a,b:b,c:c};

(return)
->
return;

(return 1 2)
->
return(1,2);

(ses-comment "test" "comment")
->
/* test
  comment */

(ses-insert "var a = 3")
->
var a = 3;

(set a 1)
->
a=1;

(set a 1 b 2 c 3)
->
a=1;b=2;c=3;

possible enhancements and ideas

  • support docstrings as is already implemented in sc
  • port the macro system from sc
  • es6 syntax: support new syntax (class, string templates, default parameters, object destructuring, async/await, import/export, const, let) and/or replace old syntax with new one (arrow functions for lambdas, still need a way to use the function keyword, coffeescript has -> => for that)
  • translate scheme comments. scheme comments dont appear in the output, only (ses-comment "comment string") or ses-insert can be used. could use guile-reader but because that is a difficult to handle dependency it might be better to preprocess and replace all comments with ses-comment expressions
  • add a command-line option to load custom syntax extensions from a file
  • more syntax checks and error messages

About

compiles scheme-like s-expressions to javascript/ecmascript

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published