Created
September 26, 2017 21:26
-
-
Save greggirwin/20f11d27bfe8a46075a09264a179f7d1 to your computer and use it in GitHub Desktop.
R2 `bounds` and `range` funcs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
rebol [] | |
; TBD - how to handle an unspecified end value? What to return for it? | |
; 4.. 12- | |
; Should time! values be taken as time ranges or should 3:5 be | |
; the same as [3 to 5]? | |
; Should we take a series value so we can get the length of it, | |
; or should we return a special token for the caller to apply? | |
; What about multiple range specs? [1..4 6..9 12..15] | |
; What about other separators | |
; - (dash) | |
; (0173 soft hyphen) | |
; – (0150 en dash) | |
; — (0151 em dash) | |
; Should we support issue! values for things like #1..10000 | |
; that fall outside valid tuple ranges? | |
; What about Turing-like range limits (* = upper bound) | |
; *..5 | |
; 1..* | |
; 1..*-1 | |
; *-1..* | |
bounds: func [ | |
"Returns upper and lower bounds as a block of two values" | |
[catch throw] | |
input [block! number! char! money! time! tuple!] ; a.k.a. spec | |
"A single value is the upper bound; a tuple defines both bounds; a block is a dialected value." | |
/local a b val val= a-val= b-val= ab-val= keywords | |
][ | |
keywords: [now time today date tomorrow yesterday from up back to .. ... - --] | |
val=: [ | |
set val [number! | char! | money! | time! | date!] | |
| 'now (val: now) | |
| 'time (val: now/time) | |
| ['today | 'date] (val: now/date) | |
| 'tomorrow (val: now/date + 1) | |
| 'yesterday (val: now/date - 1) | |
;| set word word! (val: get word) ; watch out, we're looking up words now | |
] | |
a-val=: ['from val= (a: val)] | |
; Will allowing a dash/minus lead to confusion? | |
b-val=: [ | |
[opt ['up | 'back] 'to | '.. | '... | '- | '-- | '– | '—] | |
[val= (b: val) | end (throw make error! "An ending boundary value must be set")] | |
] | |
ab-val=: [val= (either a [b: val] [a: val])] | |
switch/default type?/word input [ | |
block! [ | |
input: reduce/only input keywords | |
if not parse input [some [a-val= | b-val= | ab-val=]] [ | |
throw make error! "Boundary values must be one of: number! char! money! time! date!" | |
] | |
a: any [a make b either date? b [now] [1]] | |
;b: to a b | |
] | |
tuple! [ | |
if not all [3 = length? input 0 = input/2] [ | |
throw make error! "tuple! range values must be of the form m..n" | |
] | |
a: input/1 b: input/3 | |
] | |
] [a: make input 1 b: input] | |
reduce [a b] | |
] | |
comment { | |
bounds 1..255 | |
bounds 255..1 | |
bounds 10 | |
bounds [3 10] | |
bounds [4 to 11] | |
bounds [.. 4] | |
bounds [12 ..] ; ERR: An ending boundary value must be set | |
bounds [6 .. 12] | |
bounds [7 ... 2] | |
bounds [8 - 14] | |
bounds [9 -- 5] | |
bounds [to 28 from 14] | |
bounds [from 28 to 14] | |
bounds compose [.. (now + 4)] | |
bounds compose [time - (now/time + 1:0:0)] | |
bounds compose [now - (now + 10)] | |
bounds [today tomorrow] | |
; If we allow word lookup, we can do this: | |
x: 3 y: 10 | |
bounds [x y] | |
} | |
; TBD - consider how to handle 2D ranges | |
; TBD - consider smart evaluation, where dialect terms don't have | |
; to be escaped. | |
; What about multiple range specs? [1..4 6..9 12..15] | |
; TBD - consider including the bounds function here, where either | |
; a /bounds refinment would return only the boundary values, | |
; or a /fill refinement would be like range is today. | |
range: func [ | |
"Returns a block containing a range of values" | |
[catch] | |
bound [block! number! char! money! time! tuple!] | |
"A single value is the upper bound; a tuple defines both bounds; a block is a dialected value." | |
;{IMPORTANT: Don't use an upper char! bound of #"ÿ" (255)--for now | |
; FOR can't handle it and it causes an endless loop.} | |
/skip step | |
/local start end val | |
; start/end instead of low/high because they may range high to low. | |
] [ | |
if bound = #"ÿ" [throw make error! join [script invalid-arg] :bound] | |
set [start end] bounds bound | |
; If from > b, and they specify a negative step, we don't catch that | |
; and do anything smart, they just get an empty block back. | |
step: any [step 1] ;make a either date? a [now] [1] | |
if end < start [step: negate abs step] | |
collect val [for v start end step [val: :v]] | |
] | |
comment { | |
range 1..255 | |
range 255..1 | |
range 10 | |
range [3 10] | |
range [4 to 11] | |
range [6 .. 12] | |
range [8 - 14] | |
range [to 28 from 14] | |
range compose [.. (now + 4)] | |
range compose [(now/time) - (now/time + 1:0:0)] | |
range/skip compose [(now/time) - (now/time + 1:0:0)] 0:1 | |
range compose [(now) - (now + 10)] | |
range/skip compose [(now) - (now + 10)] 3 | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment