Skip to content
This repository was archived by the owner on Jun 5, 2020. It is now read-only.
Daniel Wirtz edited this page Jan 15, 2014 · 33 revisions

Welcome to the MetaScript wiki! For now, the wiki contains a few non-trivial insights on writing meta.

Short statements

//? if (NODE)
buffer.writeInt8(offset, value);
//? else
view.setInt8(value, offset);

This will actually work without using curly braces as long as there is just one line following, because each source line is wrapped by a dedicated write(...) call, resulting in the following meta program:

if (NODE)
  write('buffer.writeInt8(offset, value);\n');
else
  write('view.setInt8(value, offset);\n');

Local vs. global variables, functions or macros

In default notation, variables and functions are local to all files. So, if you intend to declare a global variable or function, just do it explicitly:

Variables:

//? var NODE = true;     // Local
//? NODE = true;         // Global

Functions:

//? function doit() {}   // Local
//? doit = function() {} // Global

This might feel strange initially, but that's how JavaScript intends it to be.

White-spaces and line breaks

The MetaScript compiler makes sure to retain any white spaces and line breaks with one important exception: If a meta line or block, which is not a ?= expression, is the only contents of a line, the entire line will be skipped. Let's modify the example from above to demonstrate why this is useful:

if (true) {
    //? if (NODE)
    buffer.writeInt8(offset, value);
    //? else
    view.setInt8(value, offset);
}

The result of the above will not contain any white spaces or new lines where the meta statements are, like for NODE=true:

if (true) {
    buffer.writeInt8(offset, value);
}

Which looks much cleaner.

If you explicitly require indentation, you may use either a ?= expression, the __ variable or even call the indent(str:string, indent:string|number):string utility manually. Example:

// This will be indented (it's a ?= expression):
    //?= 'var i=0;'
// Just like this (it uses manual indentation):
    //? write(indent('var j=0;\n', 4));
// Or this (it prepends __):
    //? write(__+'var k=0;\n');
// But this will not:
    //? write('var k=0;\n');

Results in:

// This will be indented (it's a ?= expression):
    var i=0;
// Just like this (it uses manual indentation):
    var j=0;
// Or this (it prepends __):
    var k=0;
// But this will not:
var k=0;

The __ variable

When inspecting the generated meta program, you will notice that the variable __ is used quite frequently. It stores the indentation level of the last meta block processed. For example ...

//? if (NODE)
    //? include("node-stuff.js");
//? else
    //? include("browser-stuff.js");

... will indent the contents of the included files by '    ' (four spaces), just like before //?, while ...

/*? if (NODE)
    include("node-stuff.js");
else
    include("browser-stuff.js"); */

will not, just like before /*?. When creating custom utility functions, it's of course safe to use this variable on your own.

The __out variable

This variable contains the so far generated output of the final document as an array. The meta program pushes additional output to it as it becomes available. Messing with __out directly isn't a good idea usually (use write(str:string) instead), but if you have something special in mind, well, then you know that it exists.

JavaScript meta programming at compile time vs. runtime

JavaScripts surely allows a developer to do lots, if not all, of the work at runtime, switching to different fallbacks in varying environments. We all know this from making stuff working in multiple browsers and in lots of cases this is absolutely legit because there are simply no alternatives.

However, since technology like node.js came up, what we understand by different environments has changed a bit: Imagine you have a library that, for maximum performance, uses ArrayBuffers in the browser and Buffers, which are much faster than ArrayBuffers but not available in the browser, on node. Or node modules vs. polyfills and stuff like that. At worst, the result would be a heavily bloated library that implements lots of the functionality multiple times for the different APIs or at best multiple layers of wrappers around the fundamental types. Now imagine that this library is also meant to be used in the browser and that you do not want the redundant node-functionality to be downloaded every time a user visits it. Or that every microsecond counts because you are writing a realtime game. You see, it will work but is not optimal and you might consider writing some meta that produces a node and a browser version of your library from a single source tree.

Clone this wiki locally