Universal metaprogramming language to craft it yourself
npm install metaltheaA universal metaprogramming language to build it yourself!
* Metalthea is a metaprogramming language designed to generates code in-line using text manipulation and substitution at compile-time, like a macro system, for any programming language.
* Metalthea has no built-in function, nor arithmetic operations, nor control structure, you have to implement that yourself, as you want it to work, in Javascript.
* Metalthea has the parenthesized syntax of S-expressions (like the Lisp programming language).
javascript
const metalthea = require("metalthea");const { value: output, syntaxTree, errorLogs } = metalthea.compileSourceCode('(add 1 2 3)', {
add: (...args) => args.reduce((a, b) =>
${a} + ${b}),
});// output == "1 + 2 + 3"
`Program examples
A hello world (in the middle of a text)*:
`javascript
// in source code:
"you can write anything before Metalthea..."
(log (str.join "Hello" "World" "!"))
"...and after too"// in language implementation
const execCtx = {
log: function(str) {
return
console.log(${ str });;
},
str: {
join: function(...args) {
return args.join(' ');
}
}
}// the result in the output file:
"you can write anything before Metalthea..."
console.log("Hello World!");
"...and after too"
`test/:
* integration test
* unit tests cases
* execution context for unit testsWhy?
* To add macro features in your usual language.
* To use cool language features you want to work with _(strong typing, pattern matching, tuples, etc.)_.
* To transpile one source code to multiple programming language.
* To use as a codegolfing tool.
Syntax
The Metalthea syntax is close to Lisp S-expressions:
* an Expression is either an Atom or a List of Expressions.
* a List is a matched pair of parentheses, with zero or more Expressions inside it.
* an Atom is either:
* a base 10 Number
- i.e.
42, 3.14, -1.6
* a base 16 Number
- starting with 0x, can have a decimal part after a dot
- i.e. 0x2A, 0x3.2f, -0xCC.c
* a String
- between double quotes "
- i.e. "", "0", "Hello World!"
* a Boolean
- in lowercase
- i.e. true, false
* a Key
- starting with colon :
- i.e. :i, :myVar, :$_21
* a Body
- either between braces {} or between grave accents ` ``
- i.e. {1 + 1}, {n => n * n}, {)}, or ` \n `, ` substring to complete in js `
* a Function
- i.e. add, str2Int, __$log
* a Lazy Node
- a node starting with a sharp sign #
- i.e #, #(mul 2)you can check Grammar RegExp there
To separate Expressions you can use either the space character (
) or a comma (,) for the same result, in example:
`lisp
(Map :a 12, :b 36, :c 42)
`You can also chain expressions with the character
>, in example:
`lisp
((add 1 1) > sqrt)
`
is the same thing as:
`lisp
(sqrt (add 1 1))
`Installation
You need Node.js to use Metalthea, so I suggest you to setup a
package.json in your project, regardless of what are the other programming language(s), with npm:`sh
npm init
npm install --save-dev metalthea
`
or with yarn:
`sh
yarn init
yarn add --dev metalthea
`Configuration
* in
package.json:add commands in
"scripts":
`json
"metalthea": "metalthea ",
"build": "npm run metalthea --verbose && ",
"watch": "metalthea --verbose --watch"
`* in
metalthea.json:TODO (dependency management and custom setup for execution context from multiple sources)
CLI Usage
command syntax:
`
metalthea
`$3
`
--verbose, -v add logs to stdout
--syntax-tree, -t logs the intermediate syntax tree in stdout
--watch, -w watch context and source files and re-compile if any change is detected
`$3
If you have configured
package.json as recommended:
`sh
npm run metalthea --verbose --watch
`
or included in your build flow:
`sh
npm run build
`
or using a shell with Metalthea installed globally (npm install -g methathea):
`sh
metalthea src/metalthea src/js dist --verbose --watch
`
or use the Node API from your Node.js project.FAQ
* How to use loops and conditions? (_control structure_)
* Implement it yourself! The easiest way is to craft expression based ones, but you can implement statements as well.
* i.e.
(if (check :test) "yes!" "no"), (for 0,99,1 #(log))
There are missing types (list, array, character, etc.)*!
* Implement it yourself!
* i.e. (Map :x 21, :y -42)
* How to handle null / undefined ?
* You can test values in javascript, you can event add a custom Null type if you wish.
* i.e. (def :x (if (isNull :a) 0, :a))
* How to add a comment?
* For the moment you have to implement it yourself.
* i.e. (comment "blaba")
How to use arithmetic operations (+, -, , /, %)?
* Implement it yourself!
* i.e. (mul 3 (add 5 4) > sqrt)
* I need strong typing!
* Implement it yourself!
* i.e. (defTyped (int :n) 42)
* How to declare variables?
* For the global scope you can implement it yourself, outside of function scope in javascript.
i.e. (in the execution context javascript file)* const state = {};
* For the local scope you can use the keyword this in javascript functions.
* i.e. function def(varName, value) { this[varName] = value; }
How to use Lazy Node (#)*?
* as a function from javascript.
* you can store it like any variable, i.e. (def :double #(mul 2)).
* you can pass arguments while executing a Lazy Node, like for other nodes.
i.e.:
`javascript
// Metalthea program: (for 0,3,1 #(js.log) > join)// javascript context:
const execCtx = {
for: function(from, to, step, bodyLambda) {
let result = [];
for (let i = from; i < to; i += step) {
result = result.concat(bodyLambda(i, from, to));
}
return result;
},
log: function(...args) {
return
console.log(${args.join(",")});
},
join: function(arr) {
return arr.join("; ");
}
};// output ==
console.log(0,0,3); console.log(1,0,3); console.log(args: 2,0,3)
``MIT © Lucien Boudy