Metal Hurlant (metal_hurlant) wrote in lj_dev,
Metal Hurlant
metal_hurlant
lj_dev

  • Mood:

Compiling S2 to Javascript

For some reason, the perspective of having S2 code compile to javascript seemed strangely appealing to me, so I wrote just that.
It could be a useful piece as part of a mechanism to allow end-users to have some sort of client-side scripting on their journal.



Here's the S2 file I was coding with. It tests a few things, but definitely not everything.
layerinfo "type" = "layout";

property int test_prop {
  des = "Stuff";
  min = 5;
  max = 50;
}

property string string_prop {
}

class Sample {
  var int a;
}


class Child extends Sample {
  var int b;
  function new_stuff;
}

function function(string a1, int a2) {
  """testing $a1 and $a2""";
}

function function(string c): int 
""" function overloading AND identifier overloading. exciting. """
{
  return 3*size $c;
}

function function(string{} d)
{
  print size $d;
}

function function(string{}[]{} e, int[]{}[] f):int[]{}
{
  print "yeah whateva.";
  return { "a" => [ 0, 1], "b" => [2,3] };
}

function Child::new_stuff
""" doc stuff. PH34R. (totally discarded for now.)"""
{
  print "in new_stuff";
  print "a = $.a and b= $.b";
}

function Sample::lay_doStuff(int a, int b):string
{
  return "SAMPLE:blah=["+$a+","+$b+"];";

}

function Child::lay_doStuff(int a, int b):string
{
  return "CHILD:blah=["+$a+","+$b+"];";
}

function string::lay_len():int
{
  "computing size of $this :";
  return size $this;
}

function test() {

  function("test", 0);
  var Sample s = new Child;
  foreach var int a (1..3) {
    foreach var int b (5..7) {
      print $a+$b;
    }
  }
  ""+$s.a;
  print $s->lay_doStuff(4,5);

  var Child t = new Child;
  $t.a=37;

  $t->new_stuff();

  print function("test");

  var string test = "Metal";
  foreach var string j ($test) {
    "gimme a "+$j;
  }
  var bool T = true;
  print $T?"true":"false";

  var int[] ar1 = [ 1, 2, 3];
  var int[] ar2 = reverse $ar1;

  foreach $s.a ($ar2) {
    print "ar2 contains $s.a";
  }

  var string{} ha1 = { "a" => "65", "b" => "66", "c" => "67" };
  print "size of ha1 = "+size $ha1;

  foreach $test ($ha1) {
    print "ha1["+$test+"] = "+$ha1{$test};
  }

  if (not $T) { "1"; } elseif (2==3) { "2"; } else { "3"; }

  function($ha1);
  print safe size function({"a"=>[{"b"=>"c"}]},[{"d"=>[1,2]}]);

  $*test_prop=3;

  $*string_prop ="Whatever";

  print $*string_prop->lay_len();

}

And here is the resulting javascript for it:
// auto-generated Javascript code from input S2 code

function $S2P_range(a,b) {
  var r=[];
  for (var i=a;i<=b;i++) r[r.length]=i;
  return r;
}
function $S2P_keys(a) {
  var r=[];
  for (var i in a) { r[r.length]=i; }
  return r;
}
function $S2P_print(s) {
  document.writeln(s.toString().split('<').join("<"));
}
var $S2G_PROPS = {};
    
$S2G_PROPS.test_prop = 0;
$S2G_PROPS.string_prop = "";
$S2C_Sample = function($$null) {
    this.a = 0;
};
$S2C_Child = function($$null) {
    this.b = 0;
};
$S2C_Child.prototype = new $S2C_Sample();
$S2_function_string_int = function ($a1, $a2) {
    $S2P_print(("testing " + $a1 + " and " + $a2));
}
$S2_function_string = function ($c) {
    return 3 * ($c).length;
}
$S2_function_string$H = function ($d) {
    $S2P_print(($S2P_keys($d)).length);
}
$S2_function_string$H$A$H_int$A$H$A = function ($e, $f) {
    $S2P_print("yeah whateva.");
    return {
        "a" : [
            0,
            1,
        ],
        "b" : [
            2,
            3,
        ],
    };
}
$S2C_Child.prototype.$S2_new_stuff = function () {
    $S2P_print("in new_stuff");
    $S2P_print(("a = " + this.a + " and b= " + this.b));
}
$S2C_Sample.prototype.$S2_lay_doStuff_int_int = function ($a, $b) {
    return "SAMPLE:blah=[" + $a + "," + $b + "];";
}
$S2C_Child.prototype.$S2_lay_doStuff_int_int = function ($a, $b) {
    return "CHILD:blah=[" + $a + "," + $b + "];";
}
String.prototype.$S2_lay_len = function () {
    $S2P_print(("computing size of " + this + " :"));
    return (this).length;
}
$S2_test = function () {
    $S2_function_string_int("test", 0);
    var $s = new $S2C_Child();
    var $$tmp1 = ($S2P_range(1, 3));
    for (var $$tmp2=0; $$tmp2<$$tmp1.length;$$tmp2++) {
        var $a = $$tmp1[$$tmp2];
        var $$tmp3 = ($S2P_range(5, 7));
        for (var $$tmp4=0; $$tmp4<$$tmp3.length;$$tmp4++) {
            var $b = $$tmp3[$$tmp4];
            $S2P_print($a + $b);
        }
    }
    $S2P_print("" + $s.a);
    $S2P_print($s.$S2_lay_doStuff_int_int(4, 5));
    var $t = new $S2C_Child();
    $t.a = 37;
    $t.$S2_new_stuff();
    $S2P_print($S2_function_string("test"));
    var $test = "Metal";
    var $$tmp5 = ($test).split('');
    for (var $$tmp6=0; $$tmp6<$$tmp5.length;$$tmp6++) {
        var $j = $$tmp5[$$tmp6];
        $S2P_print("gimme a " + $j);
    }
    var $T = 1;
    $S2P_print(($T ? "true" : "false"));
    var $ar1 = [
        1,
        2,
        3,
    ];
    var $ar2 = ($ar1).reverse();
    var $$tmp7 = ($ar2);
    for (var $$tmp8=0; $$tmp8<$$tmp7.length;$$tmp8++) {
        $s.a = $$tmp7[$$tmp8];
        $S2P_print(("ar2 contains " + $s.a));
    }
    var $ha1 = {
        "a" : "65",
        "b" : "66",
        "c" : "67",
    };
    $S2P_print("size of ha1 = " + ($S2P_keys($ha1)).length);
    for ($test in $ha1) {
        $S2P_print("ha1[" + $test + "] = " + $ha1[$test]);
    }
    if (! $T) {
        $S2P_print("1");
    } else if (2 == 3) {
        $S2P_print("2");
    } else {
        $S2P_print("3");
    }
    $S2_function_string$H($ha1);
    $S2P_print(($S2P_keys($S2_function_string$H$A$H_int$A$H$A({
        "a" : [
            {
                "b" : "c",
            },
        ],
    }, [
        {
            "d" : [
                1,
                2,
            ],
        },
    ]))).length);
    $S2G_PROPS.test_prop = 3;
    $S2G_PROPS.string_prop = "Whatever";
    $S2P_print($S2G_PROPS.string_prop.$S2_lay_len());
}
// end.

A few notes in no particular order:
- the resulting javascript seems to parse and run correctly.
- one S2 object = one javascript object. This seems to work so far.
- prototyping is used as the mechanism to resolve S2 class inheritance
- there isn't a good equivalent for foreach in javascript, so 2 times out of 3, this gets implemented with a for(;;) and a couple of temporary variables.
- some S2 concept are not very relevant on the client-side, so property support is minimal, and things like builtins and readonly are non-existent.
- the current implementation is alpha and has many rough edges, just to set expectations a bit.

This would need some additional methods to allow safe outputing of html (right now, printing won't let any html through.), and some hooks into the javascript event model before starting to be useful.

Alrighty, flame away.
Subscribe

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your reply will be screened

    Your IP address will be recorded 

  • 17 comments