Skip to content

Instantly share code, notes, and snippets.

@WebReflection
Last active March 21, 2019 22:05
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save WebReflection/e91b2a18dba1d72d24e0977a397b5625 to your computer and use it in GitHub Desktop.
Save WebReflection/e91b2a18dba1d72d24e0977a397b5625 to your computer and use it in GitHub Desktop.
A `lys.js` crazy non sense

lys is a programming language that produces WASM, and its design goal is to be as simple as possible, yet useful to create utilities.

I've been thinking about a subset of JavaScript that could run natively on the browser, similarly to asm.js, but with the ability, through a dedicated parser, to target another language able, on its own, to produce WASM.

The following crazy non sense works already thanks to an agglomerated of modern and deprecated JS features and it might be interesting as experiment to see if a JS to WASM compiler, through the lys indirection, could be possible.

function lys(fn) {

  /*! (c) Andrea Giammarchi */

  const window = {
    Enum,
    Fun,
    Impl,
    Struct,
    Type
  };

  Type.Variants = class extends Array {};

  Object.prototype.is = function (what) {
    return typeof what === 'function' ?
      (this instanceof what) :
      (this === what);
  };

  return Function('window', `with(window) return (${fn})();`)(window);

  function callback(name, fn) {
    const args = fn.toString().replace(
      /^[\s\S]+?\{([\s\S]*)\}\s*,\s*\{\w+\}\s*\)[\s\S]+$/,
      '$1'
    );
    return Function('window', `return function ${name}(${
      args.replace(/[\S]+:/g, '')
    }) {with(window){${
      fn.toString().replace(
        /^[\s\S]+?\)\s*\{([\s\S]*)\}\s*$/,
        '$1'
      )
    }}}`)(window);
  }

  function Constructor(name, fn) {
    const args = fn.toString().replace(
      /^[\s\S]+?\{([\s\S]*)\}\s*\)[\s\S]+$/,
      '$1'
    );
    const params = args.replace(/[\S]+:/g, '');
    return Function(`const Class = function ${name}(${params}) {
      if (!(this instanceof Class))
        return new Class(${params});
      ${args.replace(/[\S]+:\s*(\w+)\s*,?/g, 'this.$1 = $1;')}
    };
    Class.toString = () => '${name}';
    return Class;
    `)();
  }

  function Enum(definition) {
    for (const key of Object.keys(definition)) {
      const types = [];
      const sub = definition[key];
      Struct(sub);
      for (const str of Object.keys(sub)) {
        types.push(window[str]);
      }
      Type({[key]: types});
    }
  }

  function Fun(definition) {
    for (const key of Object.keys(definition)) {
      window[key] = callback(key, definition[key]);
    }
  }

  function Impl(what, how) {
    const proto = what.prototype;
    for (const method of Object.keys(how)) {
      proto[method] = callback(method, how[method]);
    }
  }

  function Struct(definition) {
    for (const name of Object.keys(definition)) {
      const current = definition[name];
      window[name] = typeof current === 'function' ?
        Constructor(name, definition[name]) :
        {
          toString: () => name,
          valueOf: () => current
        };
    }
  }

  function Type(definition) {
    for (const name of Object.keys(definition)) {
      window[name] = new Type.Variants(...definition[name]);
    }
  }

}

Above function could be used to define any sort of module, where each module could be transpiled into lys at some point in the future.

Following an example of a module with an export, fully based on current lys examples.

// example
var module = lys(() => {

  // structs, types, and functions, are defined
  // globally in this scope only

  // structs are defined through typed parameters
  // and an optional return type
  Struct({
    Node({i32:value, Tree:left, Tree:right}) {{i32}}
  });

  // if structs are static (not constructable) these
  // can simply carry a value
  Struct({Empty: void 0});

  // types are a list of structs
  Type({Tree: [Node, Empty]});

  /* all previous operations could be defined also as ...
  Enum({
    Tree: {
      Node({i32:value, Tree:left, Tree:right}) {{i32}},
      Empty: void 0
    }
  });
  */

  // enums are sugar for type and struct definition
  Enum({
    Color: {
      Red: 'r',   // static enums can have any value
      Green: 'g', // such value could be retrieved
      Blue: 'b',  // for debugging purpose via valueOf()
      Custom({i32: r, i32: g, i32: b}) {{Color}}
      // constructable structs must be defined as methods though
    }
  })

  // functions are defined through typed args, and return type
  Fun({
    isRed({Color: color}, {boolean}) {
      switch (true) {
        // all objects are enriched through an `is` method
        case color.is(Red): return true;
        case color.is(Custom):
          const {r, g, b} = color;
          return r == 255 && g == 0 && b == 0;
        default:
          return false;
      }
    }
  });

  // implementations are used to define structs (class prototype)
  Impl(Custom, {
    sum({}, {i32}) {
      return this.r + this.g + this.b;
    }
  });

  // exported utils should be simply returned
  return {
    main() {
      var redish = Custom(255, 0, 0);
      console.log(isRed(redish));
    }
  };

});

module.main();

The execution of that module.main() shold print true in console.

Theoretically everything would run in console already, but Code Pen, for some reason, doesn't work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment