UNPKG

contracts-js

Version:

A contract library for JavaScript

247 lines (223 loc) 6.84 kB
var _c; let import = macro { rule { @ from $lib:lit } => { _c = <%= lib %> } rule { $rest ... } => { import $rest ... } } export import; macro stringify { case {_ ($toks ...) } => { var toks = #{$toks ...}[0].token.inner; function traverse(stx) { return stx.map(function(s) { if (s.token.inner) { return s.token.value[0] + traverse(s.token.inner) + s.token.value[1]; } return s.token.value; }).join(" "); } var toksStr = traverse(toks); letstx $str = [makeValue(toksStr, #{here})]; return #{$str} } } macro base_contract { rule { $name } => { typeof $name !== 'undefined' ? $name : _c.$name } } macroclass named_contract { rule { $name $[:] $contract:any_contract } } macro function_contract { rule { ($dom:named_contract (,) ...) this $this:object_contract -> $range:named_contract | { $guard ... } } => { _c.fun([$dom$contract (,) ...], $range$contract, { dependency: function($dom$name (,) ..., $range$name) { $guard ... }, thisContract: $this, namesStr: [$(stringify (($dom$name))) (,) ..., stringify (($range$name))], dependencyStr: stringify (($guard ...)) }) } rule { ($dom:named_contract (,) ...) -> $range:named_contract | { $guard ... } } => { _c.fun([$dom$contract (,) ...], $range$contract, { dependency: function($dom$name (,) ..., $range$name) { $guard ... }, namesStr: [$(stringify (($dom$name))) (,) ..., stringify (($range$name))], dependencyStr: stringify (($guard ...)) }) } rule { ($dom:named_contract (,) ...) this $this:object_contract -> $range:named_contract | $guard:expr } => { _c.fun([$dom$contract (,) ...], $range$contract, { dependency: function($dom$name (,) ..., $range$name) { return $guard; }, thisContract: $this, namesStr: [$(stringify (($dom$name))) (,) ..., stringify (($range$name))], dependencyStr: stringify ($guard) }) } rule { ($dom:named_contract (,) ...) -> $range:named_contract | $guard:expr } => { _c.fun([$dom$contract (,) ...], $range$contract, { dependency: function($dom$name (,) ..., $range$name) { return $guard; }, namesStr: [$(stringify (($dom$name))) (,) ..., stringify (($range$name))], dependencyStr: stringify ($guard) }) } rule { ($dom:any_contract (,) ...) -> $range:any_contract | this $[:] $this:object_contract } => { _c.fun([$dom (,) ...], $range, { thisContract: $this }) } rule { ($dom:any_contract (,) ...) -> $range:any_contract } => { _c.fun([$dom (,) ...], $range) } } macro object_contract { rule { { $($prop $[:] $contract:any_contract) (,) ... } } => { _c.object({ $($prop : $contract) (,) ... }) } // proxied objects rule { !{ $($prop $[:] $contract:any_contract) (,) ... } } => { _c.object({ $($prop : $contract) (,) ... }, {proxy: true}) } } macro array_contract { rule { [ $contracts:any_contract (,) ... ] } => { _c.array([$contracts (,) ...]) } // proxied arrays rule { ![ $contracts:any_contract (,) ... ] } => { _c.array([$contracts (,) ...], {proxy: true}) } } macro repeat_contract { rule {$[...] $contract:any_contract } => { _c.repeat($contract) } } macro optional_contract { rule { ? $contract:any_contract } => { _c.optional($contract) } } macro predicate_contract { rule { ($param) => { $pred ... } } => { _c.check(function($param) { $pred ... }, stringify (($pred ...)) ) } rule { ($param) => $pred:expr } => { _c.check(function($param) { return $pred; }, stringify ($pred) ) } } macro regex { case {_ $tok } => { var tok = #{$tok}; if (tok[0].token.type === parser.Token.RegularExpression) { return tok; } throwSyntaxCaseError("Not a regular expression"); } } macro regex_contract { rule { $re:regex } => { _c.reMatch($re) } } macro non_bin_contract { rule { $contract:regex_contract } => { $contract } rule { $contract:predicate_contract } => { $contract } rule { $contract:function_contract } => { $contract } rule { $contract:object_contract } => { $contract } rule { $contract:array_contract } => { $contract } rule { $contract:repeat_contract } => { $contract } rule { $contract:optional_contract } => { $contract } rule { $contract:base_contract } => { $contract } } macro or_contract { rule { $left:non_bin_contract or $right:any_contract } => { _c.or($left, $right) } } macro and_contract { rule { $left:non_bin_contract and $right:any_contract } => { _c.and($left, $right) } } macro any_contract { rule { $contract:or_contract } => { $contract } rule { $contract:and_contract } => { $contract } rule { $contract:non_bin_contract } => { $contract } } let @ = macro { // special casing let bound predicate contracts to get the name // from the let binding instead of doing stringify to the predicate body case {_ let $contractName = ($param) => { $pred ... } } => { return #{ _c.$contractName = _c.check(function($param) { $pred ...}, stringify (($contractName))) } } case {_ let $contractName = ($param) => $pred:expr } => { return #{ _c.$contractName = _c.check(function($param) { return $pred }, stringify (($contractName))) } } case {_ let $contractName = $contract:any_contract } => { return #{ _c.$contractName = _c.cyclic(stringify (($contractName))); _c.$contractName = _c.$contractName.closeCycle($contract); } } case {_ forall $($varName (,) ...) $contracts:function_contract function $name ($params ...) { $body ...} } => { return #{ function $name ($params ...) { $body ... } } } case {_ $contracts:function_contract function $name ($params ...) { $body ...} } => { return #{ function $name ($params ...) { $body ... } } } } export @;