Skip to content

Instantly share code, notes, and snippets.

@greggirwin
Created September 26, 2017 21:26
Show Gist options
  • Save greggirwin/20f11d27bfe8a46075a09264a179f7d1 to your computer and use it in GitHub Desktop.
Save greggirwin/20f11d27bfe8a46075a09264a179f7d1 to your computer and use it in GitHub Desktop.
R2 `bounds` and `range` funcs
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