DZone Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

Snippets has posted 5883 posts at DZone. View Full User Profile

Parse Multiple, Independently Fixed Length, Integer Values From A String

02.15.2006
| 3211 views |
  • submit to reddit
        
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]
}