parse-int-values: func [
"Parses and returns integer values, each <n> chars long in a string."
input [any-string!]
spec [block!] "Dialected block of commands: <n>, skip <n>, done, char, or string"
/local
gen'd-rules ; generated rules
result ; what we return to the caller
emit emit-data-rule emit-skip-rule emit-literal-rule emit-data
digit= n= literal=
int-rule= skip-rule= literal-rule= done= build-rule=
data-rule skip-rule
][
; This is where we put the rules we build; our gernated parse rules.
gen'd-rules: copy []
; This is where we put the integer results
result: copy []
; helper functions
emit: func [rule n] [append gen'd-rules replace copy rule 'n n]
emit-data-rule: func [n] [emit data-rule n]
emit-skip-rule: func [n] [emit skip-rule n]
emit-literal-rule: func [value] [append gen'd-rules value]
emit-data: does [append result to integer! =chars]
; Rule templates; used to generate rules
;data-rule: [copy =chars n digit= (append result to integer! =chars)]
data-rule: [copy =chars n digit= (emit-data)]
skip-rule: [n skip]
; helper parse rules
digit=: charset [#"0" - #"9"]
n=: [set n integer!]
literal=: [set lit-val [char! | any-string!]]
; Rule generation helper parse rules
int-rule=: [n= (emit-data-rule n)]
skip-rule=: ['skip n= (emit-skip-rule n)]
literal-rule=: [literal= (emit-literal-rule lit-val)]
done=: ['done (append gen'd-rules [to end])]
; This generates the parse rules used against the input
build-rule=: [some [skip-rule= | int-rule= | literal-rule=] opt done=]
; We parse the spec they give us, and use that to generate the
; parse rules used against the actual input. If the spec parse
; fails, we return none (maybe we should throw an error though);
; if the data parse fails, we return false; otherwise they get
; back a block of integers. Have to decide what to do if they
; give us negative numbers as well.
either parse spec build-rule= [
either parse input gen'd-rules [result] [false]
] [none]
]
comment {
test: func [val spec] [print mold parse-int-values val spec]
test "20060228T190000" [4 2 2 skip 1 2 2 2]
test "20060228T190000" [4 2 2 #"T" 2 2 2]
test "20060228T190000Z" [4 2 2 #"T" 2 2 2 #"Z"]
test "20060228T190000Z" [4 2 2 #"T" 2 2 2 done]
test %2006021509450004.jpg [4 2 2 2 2 4 skip 4]
test %2006021509450004.jpg [4 2 2 2 2 4 done]
; tests that *should* fail
; Bad spec
test "20060228T190000Z" [4 2 x #"T" 2 2 2]
test "20060228T190000Z" [4 2 2 #"T" 2.5 2 2]
; Bad input
test "20060228T190000Z" [4 2 2 #"T" 2 2 2]
test "2000228T190000Z" [4 2 2 #"T" 2 2 2 done]
test "20060228x190000Z" [4 2 2 #"T" 2 2 2 done]
}