Skip to content

Commit e93be01

Browse files
committed
adding predicate contracts
1 parent 0fe7324 commit e93be01

File tree

6 files changed

+225
-25
lines changed

6 files changed

+225
-25
lines changed

macros/disabled.js

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,7 @@ let import = macro {
431431
Void: check(function (val) {
432432
return null == val;
433433
}, 'Null'),
434+
check: check,
434435
fun: fun,
435436
or: or,
436437
repeat: repeat,
@@ -472,6 +473,10 @@ macro base_contract {
472473
rule { $name } => { _c.$name }
473474
}
474475

476+
macroclass named_contract {
477+
rule { $name $[:] $contract:any_contract }
478+
}
479+
475480
macro function_contract {
476481
rule { ($dom:named_contract (,) ...) -> $range:named_contract | { $guard ... } } => {
477482
_c.fun([$dom$contract (,) ...], $range$contract, {
@@ -542,6 +547,31 @@ macro optional_contract {
542547
}
543548
}
544549

550+
macro predicate_contract {
551+
rule {
552+
($param) => { $pred ... }
553+
} => {
554+
_c.check(function($param) { $pred ... }, stringify (($pred ...)) )
555+
}
556+
557+
rule {
558+
($param) => $pred:expr
559+
} => {
560+
_c.check(function($param) { return $pred; }, stringify ($pred) )
561+
}
562+
}
563+
564+
565+
macro non_or_contract {
566+
rule { $contract:predicate_contract } => { $contract }
567+
rule { $contract:function_contract } => { $contract }
568+
rule { $contract:object_contract } => { $contract }
569+
rule { $contract:array_contract } => { $contract }
570+
rule { $contract:repeat_contract } => { $contract }
571+
rule { $contract:optional_contract } => { $contract }
572+
rule { $contract:base_contract } => { $contract }
573+
}
574+
545575
macro non_or_contract {
546576
rule { $contract:function_contract } => { $contract }
547577
rule { $contract:object_contract } => { $contract }
@@ -564,6 +594,24 @@ macro any_contract {
564594

565595

566596
let @ = macro {
597+
// special casing let bound predicate contracts to get the name
598+
// from the let binding instead of doing stringify to the predicate body
599+
case {_
600+
let $contractName = ($param) => { $pred ... }
601+
} => {
602+
return #{
603+
_c.$contractName = _c.check(function($param) { $pred ...},
604+
stringify (($contractName)))
605+
}
606+
}
607+
case {_
608+
let $contractName = ($param) => $pred:expr
609+
} => {
610+
return #{
611+
_c.$contractName = _c.check(function($param) { return $pred },
612+
stringify (($contractName)))
613+
}
614+
}
567615
case {_
568616
let $contractName = $contract:any_contract
569617
} => {
@@ -572,13 +620,25 @@ let @ = macro {
572620
}
573621
}
574622

623+
case {_
624+
forall $($varName (,) ...)
625+
$contracts:function_contract
626+
function $name ($params ...) { $body ...}
627+
} => {
628+
return #{
629+
function $name ($params ...) {
630+
$body ...
631+
}
632+
}
633+
}
634+
575635
case {_
576636
$contracts:function_contract
577637
function $name ($params ...) { $body ...}
578638
} => {
579639
return #{
580640
function $name ($params ...) {
581-
return $body ...
641+
$body ...
582642
}
583643
}
584644
}

macros/index.js

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,7 @@ let import = macro {
431431
Void: check(function (val) {
432432
return null == val;
433433
}, 'Null'),
434+
check: check,
434435
fun: fun,
435436
or: or,
436437
repeat: repeat,
@@ -546,14 +547,29 @@ macro optional_contract {
546547
}
547548
}
548549

550+
macro predicate_contract {
551+
rule {
552+
($param) => { $pred ... }
553+
} => {
554+
_c.check(function($param) { $pred ... }, stringify (($pred ...)) )
555+
}
556+
557+
rule {
558+
($param) => $pred:expr
559+
} => {
560+
_c.check(function($param) { return $pred; }, stringify ($pred) )
561+
}
562+
}
563+
549564

550565
macro non_or_contract {
551-
rule { $contract:function_contract } => { $contract }
552-
rule { $contract:object_contract } => { $contract }
553-
rule { $contract:array_contract } => { $contract }
554-
rule { $contract:repeat_contract } => { $contract }
555-
rule { $contract:optional_contract } => { $contract }
556-
rule { $contract:base_contract } => { $contract }
566+
rule { $contract:predicate_contract } => { $contract }
567+
rule { $contract:function_contract } => { $contract }
568+
rule { $contract:object_contract } => { $contract }
569+
rule { $contract:array_contract } => { $contract }
570+
rule { $contract:repeat_contract } => { $contract }
571+
rule { $contract:optional_contract } => { $contract }
572+
rule { $contract:base_contract } => { $contract }
557573
}
558574

559575
macro or_contract {
@@ -567,11 +583,26 @@ macro any_contract {
567583
rule { $contract:non_or_contract } => { $contract }
568584
}
569585

570-
// macro ident_list {
571-
// rule { $p (,) ... }
572-
// }
573586

574587
let @ = macro {
588+
// special casing let bound predicate contracts to get the name
589+
// from the let binding instead of doing stringify to the predicate body
590+
case {_
591+
let $contractName = ($param) => { $pred ... }
592+
} => {
593+
return #{
594+
_c.$contractName = _c.check(function($param) { $pred ...},
595+
stringify (($contractName)))
596+
}
597+
}
598+
case {_
599+
let $contractName = ($param) => $pred:expr
600+
} => {
601+
return #{
602+
_c.$contractName = _c.check(function($param) { return $pred },
603+
stringify (($contractName)))
604+
}
605+
}
575606
case {_
576607
let $contractName = $contract:any_contract
577608
} => {

src/contracts.js

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -540,15 +540,7 @@
540540
Undefined: check(function(val) { return void 0 === val; }, "Null"),
541541
Void: check(function(val) { return null == val; }, "Null"),
542542

543-
// "type" variables
544-
// a: seal("a"),
545-
// b: seal("b"),
546-
// c: seal("c"),
547-
// d: seal("d"),
548-
// e: seal("e"),
549-
// f: seal("f"),
550-
// g: seal("g"),
551-
543+
check: check,
552544
fun: fun,
553545
or: or,
554546
repeat: repeat,

src/macros-disabled.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,30 @@ macro optional_contract {
106106
}
107107
}
108108

109+
macro predicate_contract {
110+
rule {
111+
($param) => { $pred ... }
112+
} => {
113+
_c.check(function($param) { $pred ... }, stringify (($pred ...)) )
114+
}
115+
116+
rule {
117+
($param) => $pred:expr
118+
} => {
119+
_c.check(function($param) { return $pred; }, stringify ($pred) )
120+
}
121+
}
122+
123+
124+
macro non_or_contract {
125+
rule { $contract:predicate_contract } => { $contract }
126+
rule { $contract:function_contract } => { $contract }
127+
rule { $contract:object_contract } => { $contract }
128+
rule { $contract:array_contract } => { $contract }
129+
rule { $contract:repeat_contract } => { $contract }
130+
rule { $contract:optional_contract } => { $contract }
131+
rule { $contract:base_contract } => { $contract }
132+
}
109133

110134
macro non_or_contract {
111135
rule { $contract:function_contract } => { $contract }
@@ -129,6 +153,24 @@ macro any_contract {
129153

130154

131155
let @ = macro {
156+
// special casing let bound predicate contracts to get the name
157+
// from the let binding instead of doing stringify to the predicate body
158+
case {_
159+
let $contractName = ($param) => { $pred ... }
160+
} => {
161+
return #{
162+
_c.$contractName = _c.check(function($param) { $pred ...},
163+
stringify (($contractName)))
164+
}
165+
}
166+
case {_
167+
let $contractName = ($param) => $pred:expr
168+
} => {
169+
return #{
170+
_c.$contractName = _c.check(function($param) { return $pred },
171+
stringify (($contractName)))
172+
}
173+
}
132174
case {_
133175
let $contractName = $contract:any_contract
134176
} => {

src/macros.js

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,29 @@ macro optional_contract {
106106
}
107107
}
108108

109+
macro predicate_contract {
110+
rule {
111+
($param) => { $pred ... }
112+
} => {
113+
_c.check(function($param) { $pred ... }, stringify (($pred ...)) )
114+
}
115+
116+
rule {
117+
($param) => $pred:expr
118+
} => {
119+
_c.check(function($param) { return $pred; }, stringify ($pred) )
120+
}
121+
}
122+
109123

110124
macro non_or_contract {
111-
rule { $contract:function_contract } => { $contract }
112-
rule { $contract:object_contract } => { $contract }
113-
rule { $contract:array_contract } => { $contract }
114-
rule { $contract:repeat_contract } => { $contract }
115-
rule { $contract:optional_contract } => { $contract }
116-
rule { $contract:base_contract } => { $contract }
125+
rule { $contract:predicate_contract } => { $contract }
126+
rule { $contract:function_contract } => { $contract }
127+
rule { $contract:object_contract } => { $contract }
128+
rule { $contract:array_contract } => { $contract }
129+
rule { $contract:repeat_contract } => { $contract }
130+
rule { $contract:optional_contract } => { $contract }
131+
rule { $contract:base_contract } => { $contract }
117132
}
118133

119134
macro or_contract {
@@ -129,6 +144,24 @@ macro any_contract {
129144

130145

131146
let @ = macro {
147+
// special casing let bound predicate contracts to get the name
148+
// from the let binding instead of doing stringify to the predicate body
149+
case {_
150+
let $contractName = ($param) => { $pred ... }
151+
} => {
152+
return #{
153+
_c.$contractName = _c.check(function($param) { $pred ...},
154+
stringify (($contractName)))
155+
}
156+
}
157+
case {_
158+
let $contractName = ($param) => $pred:expr
159+
} => {
160+
return #{
161+
_c.$contractName = _c.check(function($param) { return $pred },
162+
stringify (($contractName)))
163+
}
164+
}
132165
case {_
133166
let $contractName = $contract:any_contract
134167
} => {

test/test_contracts.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,48 @@ in: in the type variable a of
643643
(a) -> a
644644
function inc_if_odd guarded at line: 629
645645
blaming: function inc_if_odd
646+
`
647+
});
648+
649+
it("should allow you to define predicate contracts", function() {
650+
@ ((x) => typeof x === 'number') -> Num
651+
function id(x) { return x; }
652+
653+
(id(42)).should.equal(42);
654+
blame of {
655+
id("foo")
656+
} should be `id: contract violation
657+
expected: typeof x === number
658+
given: 'foo'
659+
in: the 1st argument of
660+
(typeof x === number) -> Num
661+
function id guarded at line: 651
662+
blaming: (calling context for id)
663+
`
664+
665+
})
666+
667+
it("should allow you to let bind complex predicate contracts", function() {
668+
@ let MyNum = (x) => {
669+
if (typeof x === "number") {
670+
return true;
671+
}
672+
return false;
673+
}
674+
675+
@ (MyNum) -> MyNum
676+
function id(x) { return x; }
677+
678+
(id(42)).should.equal(42);
679+
blame of {
680+
id("foo")
681+
} should be `id: contract violation
682+
expected: MyNum
683+
given: 'foo'
684+
in: the 1st argument of
685+
(MyNum) -> MyNum
686+
function id guarded at line: 676
687+
blaming: (calling context for id)
646688
`
647689
})
648690

0 commit comments

Comments
 (0)