aboutsummaryrefslogtreecommitdiff
path: root/stdlib/source/lux/control/contract.lux
diff options
context:
space:
mode:
Diffstat (limited to 'stdlib/source/lux/control/contract.lux')
-rw-r--r--stdlib/source/lux/control/contract.lux37
1 files changed, 37 insertions, 0 deletions
diff --git a/stdlib/source/lux/control/contract.lux b/stdlib/source/lux/control/contract.lux
new file mode 100644
index 000000000..2f347dfa5
--- /dev/null
+++ b/stdlib/source/lux/control/contract.lux
@@ -0,0 +1,37 @@
+(;module:
+ lux
+ (lux (control monad)
+ (data text/format)
+ [compiler #+ Monad<Lux>]
+ (macro [ast]
+ ["s" syntax #+ syntax:])))
+
+(def: #export (assert! message test)
+ (-> Text Bool [])
+ (if test
+ []
+ (error! message)))
+
+(syntax: #export (@pre test expr)
+ {#;doc (doc "Pre-conditions."
+ "Given a test and an expression to run, only runs the expression if the test passes."
+ "Otherwise, an error is raised."
+ (@pre (i.= 4 (i.+ 2 2))
+ (foo 123 456 789)))}
+ (wrap (list (` (exec (assert! (~ (ast;text (format "Pre-condition failed: " (%ast test))))
+ (~ test))
+ (~ expr))))))
+
+(syntax: #export (@post test expr)
+ {#;doc (doc "Post-conditions."
+ "Given a predicate and an expression to run, evaluates the expression and then tests the output with the predicate."
+ "If the predicate returns true, returns the value of the expression."
+ "Otherwise, an error is raised."
+ (@post i.even?
+ (i.+ 2 2)))}
+ (do @
+ [g!output (compiler;gensym "")]
+ (wrap (list (` (let [(~ g!output) (~ expr)]
+ (exec (assert! (~ (ast;text (format "Post-condition failed: " (%ast test))))
+ ((~ test) (~ g!output)))
+ (~ g!output))))))))