npm install --global sweet.js
sjs
utility available. Let's write a macro that will swap the values of two variables, put the following code in the swap.sjs
file: macro swap { rule { $x , $y } => { var tmp = $x; $x = $y; $y = tmp } } var x = 11; var y = 12; swap x, y; swap y, x;
sjs -r ./swap.sjs
compiler sjs -r ./swap.sjs
var x = 11; var y = 12; var tmp = x; x = y; y = tmp; var tmp$2 = y; y = x; x = tmp$2;
let describe = macro { rule { $name:lit { $body ... } } => { describe($name, function () { $body ... }); } } let it = macro { rule { $name:lit { $body ... } } => { it($name, function () { $body ... }); } } describe "My functionality" { it "works!" { } }
macro name
form, we used let name = macro
- this is done in order to eliminate infinite recursion. Since the describe
and it
macros return a set of tokens with names that match the names of the macros themselves, Sweet.js will try to use the corresponding macros again and again until the stack ends. The let
form helps to avoid this, since it does not create a binding for the name of the macro inside the syntax that is returned by the macro. describe('My functionality', function () { it('works!', function () { }); });
swap
macro, which we wrote at the very beginning - it allows you to save on writing code and use syntactic constructions that are closer to the subject area. 2 + 2 should == 4 "aabbcc" should contain "bb" [1, 2] should be truthy xy() should throw
undefined has no method x
style undefined has no method x
, but will display which code exactly led to this. For example, 2 + 2 should == 5
should result in error message 2 + 2 should be equal to 5
. macro fmt { case { _ ( $val:expr ) } => { function fmt(v) { return v.map(function(x){ return x.token.inner ? x.token.value[0] + fmt(x.token.inner) + x.token.value[1] : x.token.value; }).join(''); } return [makeValue('`' + fmt(#{$val}) + '`', #{here})]; } }
fmt
function that bypasses the syntax tree and generates a line of code from it. Then we construct another syntax tree, which consists of a single node-string and return it as the result of a macro. fmt(1 + 1) // "1+1" fmt(xy(1, 2)) // "xy(1,2)"
fmt
remains as an exercise for the reader.assert
module from the standard Node.js library for the statements themselves and simply define macros that will be compiled into function calls from this module. var assert = require('assert'); macro should { rule infix { $lhs:expr | == $rhs:expr } => { assert.deepEqual( $lhs, $rhs, fmt($lhs) + " should be equal to " + fmt($rhs)); } rule infix { $lhs:expr | be truthy } => { assert.ok( $lhs, fmt($lhs) + " should be truthy"); } rule infix { $lhs:expr | contain $rhs } => { assert.ok( $lhs.indexOf($rhs) > -1, fmt($lhs) + " should contain " + fmt($rhs)); } rule infix { $lhs:expr | throw } => { assert.throws( function() { $lhs }, Error, fmt($lhs) + " should throw"); } }
rule infix
to define infix rules, the symbol |
in the template shows where the symbol of the macro name should be located, in this case should
. 2 + 2 should == 4 "aabbcc" should contain "bb" [1, 2] should be truthy xy() should throw
var assert = require('assert'); assert.deepEqual(2 + 2, 4, '`2+2`' + ' should be equal to ' + '`4`'); assert.ok('aabbcc'.indexOf('bb') > -1, '`aabbcc`' + ' should contain ' + '`bb`'); assert.ok([ 1, 2 ], '`[1,2]`' + ' should be truthy'); assert.throws(function () { xy(); }, Error, '`xy()`' + ' should throw');
% npm install --global mocha sweet.js % npm install sweet-bdd sweet-assertions
describe "additions" { it "works" { 2 + 2 should == 4 } }
% sjs -m sweet-bdd -m sweet-assertions ./specs.sjs > specs.js % mocha specs.js
function myPatterns { // Match literals case 42 => 'The meaning of life' // Tag checking for JS types using Object::toString case a @ String => 'Hello ' + a // Array destructuring case [...front, back] => back.concat(front) // Object destructuring case { foo: 'bar', x, 'y' } => x // Custom extractors case Email{ user, domain: 'foo.com' } => user // Rest arguments case (a, b, ...rest) => rest // Rest patterns (mapping a pattern over many values) case [...{ x, y }] => _.zip(x, y) // Guards case x @ Number if x > 10 => x }
Source: https://habr.com/ru/post/210454/
All Articles