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.