Last active
June 23, 2020 05:29
-
-
Save toomasv/097b92820f3e7a9d2bbf97dbd2cb304a to your computer and use it in GitHub Desktop.
Mimick range and bound syntax
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
Red [ | |
Description: "Pre-load to mimic range/bounds syntax" | |
Date: 22-May-2020 | |
Author: @toomasv | |
] | |
context [ | |
default-start: func [stop step][ | |
case [ | |
any [percent? stop percent? :step][1%] | |
any [pair? stop pair? :step][1x1] | |
any [float? stop float? :step][1.0] | |
any [date? stop date? :step][now/date] | |
any [time? stop time? :step][now/time] | |
any [char? stop char? :step][#"A"] | |
any [tuple? st: stop tuple? st: :step][ | |
to-tuple append/dup copy "1" ".1" -1 + length? st | |
] | |
true [1] | |
] | |
] | |
set 'range function [stop /from start /by step][ | |
index: 1 | |
case/all [ | |
block? :start [index: start/1 start: none] | |
not start [start: default-start stop :step] | |
all [not block? stop number? :step negative? :step] [ | |
start: either percent? :step [ | |
to-percent (stp: to-float stop) - round/down/to stp - to-float start to-float step | |
][ | |
stop - round/down/to stop - start step | |
] | |
step: absolute step | |
] | |
block? :step [if not parse step [some scalar!][step: function [] step]] | |
not :step [ | |
step: pick [1% 1] to-logic any [percent? stop percent? start] | |
] | |
all [not function? :step zero? step] [cause-error 'user 'message ["Range doesn't advance!"]] | |
start = stop [return start] | |
not block? stop [comp: get cmp: pick either function? :step [[<= >=]][[> <]] start < stop] | |
] | |
op: get pick [+ -] to-logic any [ | |
all [not block? stop start <= stop any [not number? :step positive? step]] | |
all [not block? stop start > stop all [number? :step negative? step]] | |
block? stop | |
] | |
value: start | |
out: collect [ | |
either function? :step [ | |
bind body-of :step :range | |
;probe reduce [value cmp stop] | |
while [ | |
value: step | |
either block? stop [ | |
stop/1 >= index | |
][ | |
value comp stop | |
] | |
][ | |
keep/only value | |
index: index + 1 | |
] | |
][ | |
;probe reduce [value cmp stop] | |
until [ | |
keep value | |
value: value op step | |
index: index + 1 | |
either block? stop [ | |
stop/1 < index | |
][ | |
value comp stop | |
] | |
] | |
] | |
] | |
out | |
] | |
set 'sequence function [/to stop /from start /by step][ | |
index: 1 | |
;probe reduce [start :step stop] | |
case/all [ | |
block? :start [index: start/1 start: none] ;if start is block!, it contains starting index | |
not start [start: default-start stop :step] | |
all [stop not block? stop number? :step negative? :step] [ | |
start: either percent? :step [ | |
to-percent (stp: to-float stop) - round/down/to stp - to-float start to-float step | |
][ | |
stop - round/down/to stop - start step | |
] | |
] | |
block? :step [if not parse step [some scalar!][step: function [] step]] | |
not :step [ | |
step: pick [1% 1] to-logic any [percent? stop percent? start] | |
] | |
all [not function? :step zero? :step] [ | |
cause-error 'user 'message ["Range doesn't advance!"] | |
] | |
good-stop: all [stop not block? stop] [comp: pick [>= <=] stop >= start] | |
] | |
op: pick [+ -] to-logic any [ | |
all [good-stop start <= stop any [not number? :step positive? step]] | |
all [good-stop start > stop all [number? :step negative? step]] | |
not stop | |
block? stop | |
] | |
either function? :step [ | |
fn: none | |
stp: load mold :step | |
context compose/only/deep [ | |
step: (stp/1) (stp/2) (stp/3) | |
value: (start) | |
index: (index) | |
start: (start) | |
stop: (stop) | |
set 'fn func [/reset /ctx /local val][ | |
either reset [ | |
index: (index) value: start | |
][ | |
value: either all [ | |
value | |
val: step | |
any [ | |
not stop | |
either block? stop [ | |
stop/1 >= index ;If stop is block, it contains index to stop at | |
][ | |
stop (comp) val | |
] | |
] | |
] [also val index: index + 1][none] | |
] | |
] | |
] | |
:fn | |
][ | |
function [/reset] compose/only/deep [ | |
value: [(start)] start: (start) step: (step) stop: (stop) index: [(index)] | |
either reset [ | |
index: [(index)] value/1: start | |
][ | |
also first value value/1: either all [ | |
value/1 | |
val: value/1 (op) step | |
index/1: index/1 + 1 | |
any [ | |
not stop | |
either block? stop [ | |
;probe reduce [stop index] | |
stop/1 >= index/1 | |
][ | |
stop (comp) val | |
] | |
] | |
] [val][none] | |
] | |
] | |
] | |
] | |
set 'inbounds func [bound1 bound2 value /scope][ | |
if scope [bound2: bound1 + bound2] | |
min max bound1 bound2 max min bound1 bound2 value | |
] | |
set 'between? func [bound1 bound2 value /scope][ | |
if scope [bound2: bound1 + bound2] | |
all [value >= min bound1 bound2 value <= max bound1 bound2] | |
] | |
system/lexer/pre-load: function [src part][ | |
ws: charset " ^-^/" | |
ws+: [some ws] | |
non: union ws charset {{}"[]|} ;" | |
digit: charset "0123456789" | |
alpha: charset [#"A" - #"Z" #"a" - #"z"] | |
alnum: union union alpha digit charset "-" | |
word: [alpha any alnum] | |
char: [{#"} ["^^(" some digit ")" | opt #"^^" skip] {"}] | |
int: [opt #"-" some digit] | |
num: [int opt [#"." some digit] opt #"%"] | |
sym: complement non | |
symb: [char | some [not [3 #"." | 2 #"."] sym]] | |
idx: [#"[" some digit #"]"] | |
sep: charset ".-/" | |
is-time?: func [val][parse val [int some [#":" int]]] | |
is-date?: func [val][parse val [some digit sep [some digit | some alpha] sep some digit]] | |
parse src [ | |
any [ | |
{"..."} | |
| (d1: d2: d3: none) change [ | |
opt [copy d1 [idx | symb]] "..." | |
opt [copy d2 [idx | symb]] | |
opt [#"|" (d3: yes)];copy d3 symb] | |
] ( | |
fn: copy "sequence" args: clear [] | |
case/all [ | |
d1 [append fn "/from" append args d1] | |
d2 [append fn "/to" append args d2] | |
d3 [append fn "/by"]; append args d3] | |
] | |
;probe reduce [d1 d2 d3] | |
rejoin [fn " " form args " "] | |
) | |
| (d1: d2: d3: none) change [ | |
opt [copy d1 [idx | symb]] ".." | |
copy d2 [idx | symb] | |
opt [#"|" (d3: yes)];copy d3 symb] | |
] ( | |
fn: copy "range" args: clear [] | |
case/all [ | |
d1 [append fn "/from" append args d1] | |
d3 [append fn "/by"] ;append args d3] | |
] | |
rejoin [fn " " d2 " " form args " "] | |
) | |
| (d1: d2: d3: none) change [ | |
#"|" copy d1 symb ws+ | |
copy d2 symb ws+ | |
copy d3 symb #"|" | |
] ( | |
rejoin ["inbounds " d1 " " d2 " " d3] | |
) | |
| (d1: d2: d3: none) change [ | |
#"|" copy d1 symb ws+ | |
copy d2 symb #"|" | |
copy d3 symb | |
] ( | |
rejoin ["between? " d1 " " d2 " " d3] | |
) | |
| (d1: none) change [#"|" copy d1 [num | word] #"|"] (rejoin ["absolute " d1]) | |
| skip | |
] | |
] | |
] | |
] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
(Beware, as string contents are not currently excluded from parsing, it may give weird results if similar syntax appears in strings.)
Examples for
range
:Examples for
sequence
:And small application:
Additionally, playing with absolute, bounds-check and forcing into bounds: