View in color | License | Download script | History |
30-Apr 10:50 UTC
[0.068] 15.487k
[0.068] 15.487k
contract.rREBOL [
Title: "Software engineering: design by contract"
Date: 18-May-2001/9:21:57+2:00
Version: 1.0.1
File: %contract.r
Author: "Maarten Koopmans"
Purpose: "Facilitates design by contract"
Email: %m--koopmans2--chello--nl
library: [
level: 'advanced
platform: 'all
type: [Tool module]
domain: [UI user-interface]
tested-under: none
support: none
license: none
see-also: none
]
]
system/error/user: make system/error/user [ pre-error: [ "The precondition " :arg1 " was not met" ] ]
system/error/user: make system/error/user [ post-error: [ "The postcondition " :arg1 " was not met" ] ]
block-all: func [ { Block variant on all. Evaluates al netsed blocks as conditions.} a [any-block!]][
;Are we @ the tail? Then we have evaluated all the conditions succesfully. Return true.
either tail? a
; We are at the end of the conditions, return true
[ return true]
[
; Is the block empty or does it contain none
either any [ empty? first a none? first first a]
[
;yes, skip and do the next condition
block-all next a
]
[
;Continue... we have a valid condition
;If the first condition is true, recursively call block-all on the next
either do first a
[ block-all next a]
[return false]
];either any
]
]
find-false: func [ {Finds the first false block in a block of blocks and return at the start of it.} a [any-block!] ]
[
;Initialize. Skip all empty and none! conditions
;until [ either any [ empty? first a none? first first a] [a: next a ] [true ] ]
while [all [(not tail? a) (do first a)] ]
[
;go to the next element and skip empty ones and ones of type none!
until [ either any [ empty? first a none? first first a] [a: next a false] [a: next a true ] ]
]
return copy a
]
contract: func [ {Contracts are functions that support pre and post conditions, aka design by contract.
Note that your code should return a value (at least none) for this to work.}
args [any-block!] {Function arguments.}
conditions [any-block!] { conditions in the format: [ pre [ [cond1] [cond2]] post [[cond3] ..]}
locals [any-block!] {Local variables to the function.}
body [any-block!] {The body of the function, should ALWAYS return a value (at least none).}
/local pre-cond post-cond pre-code post-code func-args func-body
cond-block do-func inner-func do-body
]
[
pre-code: copy []
post-code: copy []
;Find the pre conditions
pre-cond: select conditions 'pre
if (not none? pre-cond)
[
;Pre-code is the code for the precondition.
pre-code: copy compose/deep [ if not block-all compose/deep [(pre-cond)]]
;Append some code. We need to split the compose because we use a compose again in the resulting code :)
append cond-block: copy compose/deep [ cond: mold first find-false compose/deep [(pre-cond)]] [ make error! compose [ user pre-error (cond)]]
;And append the cond-block to pre-code. Now we have our pre-code ready.
append/only pre-code cond-block
]
post-cond: select conditions 'post
;Find the pre conditions
if (not none? post-cond)
[
;Pre-code is the code for the precondition.
post-code: copy compose/deep [ if not block-all compose/deep [(post-cond)]]
;Append and compose some code. We need to split the compose because we use a compose again in the resulting code :)
append cond-block: copy compose/deep [ cond: mold first find-false compose/deep [(post-cond)]] [ make error! compose [ user post-error (cond)]]
;And append the cond-block to pre-code. Now we have our pre-code ready.
append/only post-code cond-block
]
;Append the local variables to the argument block
append func-args: copy args /local
append func-args [ __return __ret_err]
append func-args locals
;if the body is empty, make sure it returns none
if body = []
[
body: copy [ none ]
]
;We evaluate the body as an anonymous function with access to all or locals
do-body: copy compose/deep [ __innerfunc: func [] [(:body)]]
; Change the function body to include the conditions
func-body: copy []
; we at least return none
insert func-body copy [ __return: none ]
append func-body copy pre-code
append func-body do-body
append func-body copy [ __return: __innerfunc ]
append func-body copy post-code
append func-body copy [ __return ]
;Create and return the function
return func func-args func-body
] Notes
|