collect/into"hello => some text..."[copy my-word to "=>"] ?to works well in simple cases, but may skip things you didn't intend if your data isn't regular.txt: "Hello => some text... World => other text"["Hello" "World"]Between whitespace and =>>> parse txt [s: collect any [ahead [some #" " "=>"] :s keep to #" " some #" " "=>" | #" " s: | skip]] == ["Hello" "World"]
help-string action! it would be the same?parse txt [ s: collect any [ ; 1) look ahead, but don't move, to see if the next thing in ; the input is " =>". If so, 2) set the input to the position ; s from the next rule. Then 3) collect text until you get ; to the next space. Finally 4) eat the " =>" marker. ; 1 2 3 4 ahead [some #" " "=>"] :s keep to #" " some #" " "=>" ; Mark where you found a space. | #" " s: ; Otherwise it's text and you can move forward. | skip ] ]
s: sets the position just past the last known space, and part 2 (:s) moves the input *backwards* to that position, so it can copy the last word before the => marker.parse txt [s: collect any [some #" " "=>" e: :s keep to #" " :e | #" " s: | skip]]
[s: ; record start of the text for case the word we want is in the very beginning
collect any [ ; it doesn't really matter if it is `any` or `some`
; Principal alternative:
some #" " "=>" ; we hit some spaces and "=>" (let's call it "separator")
e: ; record position after separator
:s ; go back to last recorded position before separator ...
keep to #" " ; ... and keep word between that position and next space
:e ; now jump to position after separator and continue from there
; Second alternative:
| #" " s: ; we meet a space and record position after that, so we remember last position before we hit separator.
; Notice that this rule *has* to be after the first one, otherwise we'll hit spaces before "=>" first
; Third alternative:
| skip ; just skip everything else
]]txt: "Hello => some text... World=> other text ! =>" form parse txt [s: collect any [ahead [any #" " "=>" e:] b: keep (copy/part s b) :e | #" " s: | skip]] ;== "Hello World !"
>> txt: help-string find
== {USAGE:^/ FIND series value^/^/DESCRIPTION: ^/ Returns the series where a value is found, or ...
>> form parse txt [s: collect any [ahead [any #" " "=>" e:] b: keep (copy/part s b) :e | #" " s: | skip]]
== {/part /only /case /same /any /with /skip /last /reverse /tail /match}ws instead of #" ".ws: charset " ^-^/" rule: [s: collect any [ahead [any ws "=>" e:] b: keep (copy/part s b) :e | ws s: | skip]] print txt: "Hello => some text ...^/World=>other text^-!^/=>end" ;Hello => some text ... ;World=>other text ! ;=>end form parse txt rule ;== "Hello World !"
ws: charset reduce [space tab cr lf]parse data [.... (result: my-custom-check) result].[... if (code)...] ?parse/trace?parse/trace "stuff" [some rule] func, but the func part is the part I'm not sure about.parse-trace, which has a built in callback method and might be all you needparse-trace and compare it to [this function](https://github.com/red/red/blob/master/environment/functions.red#L311) used to create the result.parse: [Evaluate left-or-rightfunc[s][a: charset"<>"until[parse s[any[t: change[a"<"a](t/1)| change[a">"a](t/3)| change["("a")"](t/2)| skip]]2 > length? s]s]<.><> -> '(><>)`. This way your solution can be even shorter:func[s][a: charset"<>"until[parse s[any[t: change["("a"<"a")"](t/2)| change["("a">"a")"](t/4)| skip]]2 > length? s]s]func[s][r:["<"|">"]until[not parse s[to change[opt"("x:[r"<"r | r">"x: r]opt")"](x/1)to end]]]while: func[s][r:["<"|">"]while[parse s[to change[opt"("x:[r"<"r | r">"x: r]opt")"](x/1)to end]]][]func[s][r:["<"|">"]while[parse s[to change[opt"("x:[r"<"r | r">"x: r]opt")"](x/1)to end]][]] ] tooopts are not necessary, just plain "(" / ")"func[s][r:["<"|">"]while[parse s[to change[opt"("x: r["<"r |">"x: r]opt")"](x/1)to end]][]] 91bopt's 85 ;)s to make it work iin TIO#switch alternative, exiting parser with false. If forced (insert (parse s rule-blk) in the end of line 60) then it works. Also, Red doesn't like halt in the end. And there is undefined blk on line 62. Should be rule-blk?halt replaced by () and blk changed, both REBOL and Red produce this:>> do %parse-test.r
LIBREDRT-file : "libRedRT.dll"
src: [
print "libRedRT.dll"
]blk seems not to affect the result.parse input [rule], repeat, but i dont get the results i want if i try to do one pass.[rule | rule2] etc?very deliberate and basic, down to the simplest I can make it while I stumble through it - so... don't make fun of me :sunglasses: to can skip over a lot of things to get to its match, so tends to be less useful when the format is less strict.to?|, but then you'll run up against to skipping over things, based on the order your check your rules in.| skip. So you check your known values to match, then skip one char if none of them do.Red []
level-3-rule: [change "====" {<a href="#Lnk">} to "^/" insert "</a><p>"]
level-2-rule: [change "===" "<h3>" to "^/" insert "</h3><p>"]
level-1-rule: [change "==" "<h2>" to "^/" insert "</h2><p>"]
level-0-rule: [change "=" "<h1>" to "^/" insert "</h1>"]
main-rule: [
some [
level-3-rule
| level-2-rule
| level-1-rule
| level-0-rule
;...
| skip
]
]
s: {
= Parse dialect
== Overview
Parse dialect is an embedded domain-specific language (DSL) of Red that allows concise processing of _input_ series with grammar _rules_. Parse's common use-cases are:
* *Search:* locate specific patterns;
* *Validation*: check the conformity of input to some specification;
=== Usage
Parse is invoked through the `parse` function using a simple default syntax (see <<Extra functions>> section for more details):
----
parse <input> <rules>
<input> : any series! value except for image! and vector!
<rules> : a block! value with valid Parse dialect content (top-level rule)
----
}
parse ss: copy s main-rule
print sslevel-0-rule: [change "= " "<h1>" to newline insert "</h1>" newline]
level-1-rule: [change "== " "<h2>" to newline insert "</h2><p>" newline]
level-2-rule: [change "=== " "<h3>" to newline insert "</h3><p>" newline]
level-3-rule: [change "==== " {<a href="#Lnk">} to newline insert "</a><p>" newline]
list-item: [if (not in-bold?) change "* " "<li>" (in-list-item?: yes)]
end-list-item: [if (in-list-item?) ahead newline insert "</li>" (in-list-item?: no) newline]
escape-input-tag: [change "<" "<" | change ">" ">"]
code-rule: [ahead ["----" | "`"] [
if (in-pre?) change "----" "</pre>" (in-pre?: false)
| change "----" "<pre>" (in-pre?: true)
| if (in-code?) change "`" "</code>" (in-code?: false)
| change "`" "<code>" (in-code?: true)
]
]
italics-rule: [ahead "_" [
if (in-italics?) change "_" "</i>" (in-italics?: false)
| change "_" "<i>" (in-italics?: true)
]
]
bold-rule: [ahead "*" [
if (in-bold?) change "*" "</b>" (in-bold?: false)
| change "*" "<b>" (in-bold?: true)
]
]
in-pre?: in-code?: in-italics?: in-bold?: in-list-item?: false
parse r [
any [
level-0-rule
| level-1-rule
| level-2-rule
| level-3-rule
| list-item
| end-list-item
| code-rule
| escape-input-tag
| italics-rule
| bold-rule
| skip
]
]>> parse "a" [#a] == false >> parse "#a" [#a] == false
issue! is not supported.>> parse "a" [%a] == true
>> parse "oldes" [@oldes] == true
@ char... having true = parse "@oldes" [@oldes]; capture word after "==== " replace Lnk with word, lowercase. Example: "==== Usage" {<a href="#usage>"}
level-3-rule: [change "==== " {<a href="#Lnk">} to newline insert "</a><p>" newline]> parse txt [s: collect any [some #" " "=>" e: :s keep to #" " :e | #" " s: | skip]] >
> [s: ; record start of the text for case the word we want is in the very beginning > collect any [ ; it doesn't really matter if it is `any` or `some` > ; Principal alternative: > some #" " "=>" ; we hit some spaces and "=>" (let's call it "separator") > e: ; record position after separator > :s ; go back to last recorded position before separator ... > keep to #" " ; ... and keep word between that position and next space > :e ; now jump to position after separator and continue from there > ; Second alternative: > | #" " s: ; we meet a space and record position after that, so we remember last position before we hit separator. > ; Notice that this rule *has* to be after the first one, otherwise we'll hit spaces before "=>" first > ; Third alternative: > | skip ; just skip everything else > ]] >
any-string! types (string! file! url! tag! email! ref!), but issue! does not belong to these.transcode/trace available. I believe it helps to think in terms of text, which may be anything, versus Red data. issue! is not in any-string!... I was asking if there should not be error like in Rebol instead of silently ignoring it.>> parse "aa" [%1] == false
>> parse "aa" [%aa] == true >> parse "1" [%1] == true
any-string! typset it is supported. Just not matching.>> level-3-rule: [s: change "==== " {<a href="#Lnk">} copy lnk to newline :s thru "#" change "Lnk" (lowercase replace/all copy lnk #" " #"-") to newline insert "</a><p>" newline]
== [s: change "==== " {<a href="#Lnk">} copy lnk to newline :s thru "#" change "Lnk" (lowercase re...
>> parse str: "==== Usage^/" level-3-rule str
== {<a href="#usage">Usage</a><p>^/}
>> parse str: "==== Usage example^/" level-3-rule str
== {<a href="#usage-example">Usage example</a><p>^/}>> level-3-rule': [change "==== " {<a href="#} ahead [copy lnk to newline] insert (lowercase replace/all copy lnk #" " #"-") insert {">} lnk insert "</a><p>" newline]
== [change "==== " {<a href="#} ahead [copy lnk to newline] insert (lowercase replace/all copy ...
>> parse str: "==== Usage example^/" level-3-rule' str
== {<a href="#usage-example">Usage example</a><p>^/}>> parse "1" [1%] == false ;<-- no error when used `percent!` (or any other unsupported) datatype as a rule. >> parse "1%" [1%] == false
>> parse "1" [integer!] *** Script Error: PARSE - matching by datatype not supported for any-string! input
>> parse "1" [string!] *** Script Error: PARSE - matching by datatype not supported for any-string! input
level-3-rule: [change "==== " {<li><a href="#_} copy s to newline insert {">} insert (rejoin [ s {</a></li>}])]to binary!parse to binary! "1" [integer!] == trues: "CAUTION: Ensure that..."
hmmm: [change [| "CAUTION:" | "WARNING:" | "IMPORTANT:"] (rejoin [
{<table><tr><td class="icon"><div class="title">} x {</div></td><td class="content">ALL OF S GOES HERE</td></tr></table>}]
) copy x to ":"]y has no value.hmmm: [change [| "CAUTION:" | "WARNING:" | "IMPORTANT:"] (rejoin [
{<table><tr><td class="icon"><div class="title">} x {</div></td><td class="content">} y {</td></tr></table>}]
) copy x to ":" skip copy y to newline] gone.s: copy "CAUTION: Ensure that..."
hmmm: [s: change [| "CAUTION:" | "WARNING:" | "IMPORTANT:"] (rejoin [
{<table><tr><td class="icon"><div class="title">} x {</div></td><td class="content">} s {</td></tr></table>}]
) copy x to ":"]s is used for original string but reused inside rulechange rule starts with empty option - it will never check further alternativesx is referred before it has got valuestr: copy "CAUTION: Ensure that..."
hmmm: [ahead [copy x ["CAUTION" | "WARNING" | "IMPORTANT"] #":"] insert (rejoin [
{<table><tr><td class="icon"><div class="title">} x {</div></td><td class="content">}]) to end insert ({</td></tr></table>})
]
parse str hmmm probe str
{<table><tr><td class="icon"><div class="title">CAUTION</div></td><td class="content">CAUTION: Ensure that...</td></tr></table>}parse [1] [any [1 1 1]] ;== true val: 1 ;== 1 parse [1] [any [1 1 val]] ;== true
parse [1] [any [1 1 1]] ;*** Script Error: PARSE - invalid rule or usage of rule: 1 ;*** Where: parse ;*** Stack: parse [1] [any [1 1 quote 1]] ;== true val: 1 ;== 1 parse [1] [any [1 1 quote val]] ;== false parse [1] [any [1 1 val]] ;*** Script Error: PARSE - invalid rule or usage of rule: 1 ;*** Where: parse ;*** Stack:
>> parse [1] [any [1 1 1 1 1 1 quote 1]] == true
>> parse [1] [any [1 1 1 1 1 1 quote 1]] == false
to/thru handle ints is another wrinkle. Today they don't. But R2 didn't either.parse [1] [any [1 1 1 1 1 1 quote 1]] melted my brain.val: [quote 1] trick (thanks @toomasv) for my experiment.outer-min: 1 outer-max: 2 inner-min: 2 inner-max: 3 parse [1 2 3 4 5 6][outer-min outer-max inner-min inner-max integer!] == true
outer-min: 1 outer-max: 2 inner-min: 2 inner-max: 3 parse [1 2 3 4 5 6][outer-min outer-max inner-min inner-max integer!] == false
to/thru? Seems it treats integers as expected:>> parse [a 1 2 b]['a thru 2 3 [quote 1 | quote 2 | quote 3] 'b] == true
1 example.blk: ["Yay!" #boo "Yay!" #{0B00}]
parse blk [
some [
set s string! (print 'do-do-do)
| set v any-type! (print ["Expected a string, but saw a(n)" type? :v])
]
] | set v not string! possible?Not is great, but can be trickier to think about. You have to remember that it never advances the input. Docs also have a note that says set sets the word to none if the rule doesn't advance the input, so you can't chain set v not ... together and get the *next* thing. You can do it other ways, like so:blk: ["Yay!" #boo "Yay!" #{0B00}]
parse blk [
some [
set s string! (print 'do-do-do)
| ahead not string! set v skip (print ["Expected a string, but saw a(n)" type? :v])
]
]not-string!: exclude any-type! make typeset! [string!]
blk: ["Yay!" #boo "Yay!" #{0B00}]
parse blk [
some [
set s string! (print 'do-do-do)
| set v not-string! (print ["Expected a string, but saw a(n)" type? :v])
]
]blk: ["Yay!" #boo "Yay!" #{0B00}]
parse blk [
some [
set s string! (print 'do-do-do)
| set v any-type! (print ["Expected a string, but saw a(n)" type? :v])
]
]not-string!: exclude any-type! make typeset! [string!]type? :vsome [
set s string! (print 'do-do-do)
| v: (print ["Expected a string, but saw a(n)" type? :v/1])
]>> parse reduce [func [a][print a]] [set v skip (print type? :v)] function == true >> parse reduce [func [a][print a]] [set v skip (print type? v)] *** Script Error: v is missing its a argument
log-area/text: copy/part rejoin [form count etc other-f/text "**" newline log-area/text] 28000data: [ id: 123 lots: [ lot: [name: "apple"] lot: [name: "bananas"] ] ]
id of it's parent. So result should be like:data: [ id: 123 lots: [ id: 123 lot: [id: 123 name: "apple"] lot: [id: 123 name: "bananas"] ] ]
id. But I can't understand how to take it's value with parse. I tried:parse data rule: [some [ set b if (b = quote id:) ahead set c integer! | ahead block! into rule | skip]]
ahead not suitable for. Also I tried to do some magic with checking value skiping it and reasigning b:parse data rule: [some [ set b if (b = quote id:) skip set b integer! | ahead block! into rule | skip]]
parse data rule: [some [ set b quote id: ahead [ set b integer! ] | ahead block! into rule | skip]]
ahead checks the rule and rewinds. set, copy and keep work as usual. You can argue that they shouldn’t and I probably would agree with you.skip (set b skip ):parse data rule: [some [ set b quote id: ahead [ set b skip ] | ahead block! into rule | skip]]
skip is good enough (and faster).>> b: [] == [] >> parse [b] [insert 44 block!]
b: [44]parse [b] [set s word! (if block? s: get/any s [insert s 44])]
parse [b] [ahead word! s: skip (if block? s': get/any s/1 [insert s' 44])]
data: [ id: 123 lots: [ lot: [name: "apple"] lot: [name: "bananas"] ] ] parse data rule: [ some [ set b quote id: ahead [ set b skip ] | ahead block! s: skip (insert get s/1 44) into rule | skip] ] probe data
parse data [ quote id: set id skip (probe id) quote lots: into [some [quote lot: s: skip (insert s/1 compose [id: (id)])]] ]
parse data [ any [ quote id: set id skip | set-word! ahead block! into [ any [set-word! set block block! (insert block compose [id: (id)])] ] ] ]
data: [
id: 123
lots: [
lot: [name: "apple"]
lot: [name: "bananas"]
]
]
parse data rule: [
some
[ set b quote id: ahead [ set b skip ] | ahead block! s: (insert s/1 compose [id: (b)]) into rule | skip]
]
probe data
>> probe data
[
id: 123
lots: [id: 123
lot: [id: 123 name: "apple"]
lot: [id: 123 name: "bananas"]
]
]skip insert id only in first level of child:data: [ id: 123 lots: [ lot: [name: "apple"] lot: [name: "bananas"] ] ] parse data rule: [ some [ set b quote id: ahead [ set b skip ] | ahead block! s: skip (insert s/1 compose [id: (b)]) into rule | skip] ] probe data
set-word! it sets a word block to the following block! value.parse data rule: [
some [s:
quote id: set b skip
| if (head? s) insert (quote id:) insert (b)
| ahead block! into rule
| skip
]
]skip it's process only first level child and do not go deeper (code above)set b quote id: ; `set b` is unnecessary here ahead [ set b skip ] ; `ahead` is unnecessary here | ahead block! s: skip (insert s/1 compose [id: (b)]) ; here you skip over block and fail to enter it into rule | skip
quote id: set b skip | ahead block! s: (insert s/1 compose [id: (b)]) into rule | skip
set-word! it sets a word block to the following block! value.> parse data rule: [ > some [s: > quote id: set b skip > | if (head? s) insert (quote id:) insert (b) > | ahead block! into rule > | skip > ] > ] >
Hello never don't printing?parse data rule: [
some [s:
quote id: set b skip
| if (head? s) insert (quote id:) insert (b) (print "Hello")
| ahead block! into rule
| skip
]
]data: [
id: 123
lots: [id: 123
lot: [id: 123 name: "apple"]
lot: [id: 123 name: "bananas" ]
]
]parse data rule: [
some [s:
quote id: set b skip
| if (head? s) insert (quote id:) insert (b) (print "Hello")
| ahead block! into rule
| skip
]
]parse data rule: [
some [
(print "will set s") s: quote id: set b skip (print "RULE1 DONE")
| if (head? s) insert (quote id:) insert (b) (print "RULE2 NEVER DONE")
| ahead block! into rule (print "RULE3 DONE")
| skip (print "RULE4 DONE")
]
]*** Script Error: reset-field has no value *** Where: act *** Stack: context view do-events do-actor do-safe
parse's result? e.g. with "a,b,c" as input to #"," passes but some [thru #","] doesn't.(head? s) with (2 = length? s). But you will have to kill the console!!!some [thru #","] does match. It just doesn't turn green. some [to #","]` does turn green.match? is false - which means the whole time you're hitting space or typing the next part of the ruledata: [
id: 123
lots: [
lot: [name: "apple"]
lot: [
name: "bananas"
obj: [ price: 44 ]
]
]
]
; count: 0
parse data rule: [
some [s:
quote id: set b skip
| if (head? s) insert (quote id:) insert (b)
| ahead block! into rule
| skip
]
]level: 1
id-level: [2 3] ; or just <int>
parse data rule: [
some [s:
quote id: set b skip
| if (all [find id-level level head? s]) insert (quote id:) insert (b)
| ahead block! (level: level + 1) into rule (level: level - 1)
| skip
]
]level = 3 or level = id-level. ahead block! (level: level + 1) into rule (level: level - 1)
into rule will work, because I thought it will make jump exactly in this momentinto rule and after it do decrement, right? parse [11 aa 11 bb cc] [some [ahead word! | skip (print "!") | 'cc]] >!
>> parse [11 aa 11 bb cc] [some [s: (probe s) ahead word! | skip (print "!") | 'cc]] [11 aa 11 bb cc] ! [aa 11 bb cc] == false
ahead does not advance.word! is ahead, than we are doing skiping. [aa 11 bb cc] 11 first subrule fails, second subrule advances and prints !; in next round, parsing stops at aa, because subrule succeeds but does not advance. If it would not stop it would be in infinite loop.parse [a: 1 b: 2 name: "Mike" c: 3] [ ahead [set-word!] set w set-word! (probe w) fail | skip ]
ahead -- look aheadset w set-word! set w to next value (next value is set-word)fail should advance input to next rule.>> parse [a: 1 b: 2 name: "Mike" c: 3] [some [ ahead [set-word!] set w set-word! (probe w) fail | skip ] ] a:
fail and it's begin to work. Could you explain why? fail interrup some?parse [a: 1 b: 2 name: "Mike" c: 3] [some [ ahead [set-word!] set w set-word! (probe w) | skip ] ]
fail : force current rule to fail and backtrack.[fail] might work in place of fail. fail and reject are a dubious design and best avoided.fail or reject very much but break is extremely important.fail works ok in some situations, e.g. let's say you want to probe set-words except b: parse [a: 1 b: 2 name: "Mike" c: 3] [some [ set w set-word! [if (w = quote b:) fail | (probe w) ] | skip ] ]
parse [a: 1 b: 2 name: "Mike" c: 3] [some [ not quote b: set w set-word! (probe w) | skip ] ]
parse [a: 1 b: 2 name: "Mike" c: 3] [some [ s: skip opt [if (all [set-word? s/1 s/1 <> quote b: ]) (probe s/1)]]]
parse haystack [to needle to end] But assume that there's no end (maybe we're parsing continuous stream of data).to end? Because I must say that I use it very often.parse "ab" ["a" accept] should return true instead of false in (R3).. there is no accept in Red. It could be added into the red/red#3478 to make it even more complicated:)then should be remove from parser? then in Red as far as I know then Regardless of failure or success of what follows, skip the next alternate rule."to end? Because I must say that I use it very often.parse "ab" ["a" accept] should return true instead of false in (R3).. there is no accept in Red. It could be added into the red/red#3478 to make it even more complicated:)exit at least once. Otherwise I could write loop 1 [parse ... [ .. (break)..]]. But that's a workaround alright.to end for now, but it is right, that if we would have streaming parse one day, there could be some other way how to end and report a success. But hard to say how to implement the streaming parse.. it would require more thoughs.then was extremely confusing, no one could figure out how to use it properly and I believe it was buggy anyway.ahead before setdata: [
foo: 33
bar: 66
id: 123
lots: [
lot: [name: "blackberry"]
lot: [name: "apple"]
lot: [
name: "bananas"
number: 43
obj: [ price: 44 ]
obj: [ price: 23 ]
]
]
]
parse data rule: [
some [
set w set-word! (probe w) | not block! skip
| ahead block! into rule
]
]loop+break trick is neat, but yes it's a workaround, as is accept: [to end]. As noted, the latter won't work for streaming cases.parse work.function.2001collect-setwords: function [block [any-list!]][
setwords: [any [keep set-word! | ahead any-list! into setwords | skip]]
unique parse block [collect setwords]
]>> rob: load %red-object-browser.red >> length? c1: collect-setwords rob == 42 >> length? c2: collect-set-words rob == 46
>> exclude c2 c1 == [i: item: w: ctl:]
foreach words probably, i.e. you gather some words that are not set-words! *per se*. >> profile/show/count [[collect-setwords rob][collect-set-words rob]] 1000 Count: 1000 Time | Time (Per) | Memory | Code 0:00:00.148 | 0:00:00 | 20184284 | [collect-set-words rob] 0:00:00.279 | 0:00:00 | 2020596 | [collect-setwords rob]
set?parse [foo:] [set x set-word!]
x become set-word! but I want to make it simply word!lots (but perfect work with lot) :data: [
id: 123
foo: 33
bar: 66
lots: [ lot: [name: "blackberry"]
lot: [name: "apple"]
lot: [
name: "bananas"
number: 43
obj: [ price: 44 ]
obj: [ price: 44 some: [ age: 20 bar: [] ] ]
]
]
]
tags: [lots obj bar]
parse data rule: [
some [
s: ahead [set-word! block!] set set-word-name set-word! (set-word-name: to word! set-word-name) (probe set-word-name)
| quote id: set b skip
| if (not unset? set-word-name) if (find tags set-word-name) if (head? s) insert (quote id:) insert (b)
| not block! skip
| ahead block! into rule
]
]
probe datalots tooset-word-name: none
parse data rule: [
some [
s: quote id: set b skip
| if (all [find tags set-word-name head? s]) insert (compose [id: (b)])
| ahead [set-word! block!] set set-word-name set-word! into rule
| skip
]
]lots?aheads), but my did not worked because few small but important difference!) Thanks!needle: 'id parse [id: 1] compose [(to-set-word needle) integer!]
probe to find out id: means for parse in it's rule?quote should be used: parse [id: 1] compose [quote (to-set-word needle) integer!]a: [foo: 123] parse [a] compose [quote (needle) set x integer! insert [id: (x)] ] a
[a]?[a], what's wrong?word!needle: 'id a: [id: 123] parse a compose/deep [ quote (to-set-word needle) set x skip ] x >> 123
x has no value. It's seems that compose try to evaluate all before last x will get value. How to solve it?needle: 'id a: [id: 123] parse a compose/deep [ quote (to-set-word needle) set x skip (print x) insert [(to-set-word needle) (x)] ] >> x has no value
([(print x)])parse a compose [
quote (n: to-set-word needle)
set x skip (quote (print x))
insert (to-paren reduce ['compose reduce [n quote (x)]])
]quote (x) in the end)needle: 'id a: [id: 123] parse a compose/deep [ quote (to-set-word needle) set x skip insert [(to-set-word needle) (reduce [(x)] ) ] ] >> a == [id: 123 id: 123]
compose/deep [[(reduce [(x)])]] would do?x x at compose-time, only at parse-time...needle: 'id a: [id: 123] parse a compose/deep [ quote (to-set-word needle) set x skip insert [ (to-set-word needle) ([(x)]) ] ]
[id: 123 id: (x)]
compose/deep stops going deeper if it meets block after (some consecutive) parens>> compose/deep [((([((x))])))] == [((x))]
insert will not evaluate block, but you need to evaluate (x).insert and append evaluate block that inserting/appending? Because this:>> append [1 2] ([(x)]) == [1 2 (x)]
[] ?>> append [1 2] [3 4] == [1 2 3 4] >> append [1 2] ((([3 4]))) == [1 2 3 4] >> ((([3 4]))) == [3 4]
>> append [a b](1 2 3 'c) == [a b c]
compose/lazy that do not try to evaluate value if it has no value?composecompose above, otherwise you uglify your code with ([(..)]) thingx is of course available only after parse, so you'll have to resort to compose again there1 is next to set word? >> parse [a:] [set-word!] == true >> parse [a:1] [set-word! 1] == false
quote ita:1 is not a set-word though>> whatiwant!: make typeset! [integer! none! string!] == make typeset! [none! string! integer!] >> parse [1] [whatiwant!] == true >> parse ["a"] [whatiwant!] == true >> parse [#[none]] [whatiwant!] == true
foreach ... print type? ...] or a general parse rule that accepts any value and dumps info about it.parse [a: "Hello" b: [ foo: 123 bar: [ x: "name" ]] c: "world" ] rule: [ ]
data: [
purchaseNumber: 848838383
region: none
lots: [
objects: [
object: [
OKPD_code: "25.22.11"
OKPD_name: {aaaaaaaaaa}
quantity: "1.00"
isMedicalProduct: none
]
object: [
OKPD_code: "83.44.66"
OKPD_name: {bbbbbbbbb}
quantity: "2.00"
isMedicalProduct: none
]
some: "Test message"
]
]
tag: "is_lots"
][
[parent-name: "mainTable" key: [purchaseNumber region tag] value: [[848838383 none "is_lots"]] ]
[parent-name: "objects" key: [some] value: [ ["Test message"] ]]
[parent-name: "object" key: [OKPD_code OKPD_name quantity isMedicalProduct] value: [ ["25.22.11" {aaaaaaaaaa} "1.00" none ] ["83.44.66" {bbbbbbbbb} "2.00" none ] ]]
]root?: true
root-name: "mainTable"
block-name: none
inside-parent?: true
result: copy []
key-value: copy/deep [parent-name: none key: [] value: []]
parse data rule: [
some [
( if root? [ key-value/parent-name: root-name ] )
ahead [set-word! [string! | integer! | 'none ] ] set set-word-name set-word! set set-word-value [string! | integer! | 'none ]
(append key-value/key to-word set-word-name append/only key-value/value set-word-value)
(root?: false)
| skip
]
]
print block-name
probe key-value
probe resultto rule correctly: even if rule advances the input considerably before it ultimately fails, to will dutifully continue from where it began testing (and not where the input was left when things failed)?to parse, though it's not *always* the best choice. Sanitizing data is a perfect use case for it though. How complicated things get all depends on your data, and how well specified the rules are. You can use the free version of DiaGrammar (link at the bottom of https://www.redlake-tech.com/products/diagrammar-for-windows/) to see if visualizations help you think through it. to hack works but it is painfully slow :sob: why you gotta be right @hiiamboris? xor: [to [ to "$" begin-xor: copy first-var to [whitespace | "="] ; find the first var assignment where,
ahead [to [{^^} any whitespace first-var]] ; that variable is ever ^ed (xor)
to "$" copy second-var to [whitespace | "="] ; and the next variable
thru [to "$" copy third-var to [whitespace | "="] ; is called, with the results assigned
any whitespace "=" any whitespace second-var "("]
thru [third-var "();"] end-xor: ; to a third variable, itself called
(change/part begin-xor "" end-xor)] any skip] ; remove all the badparse?whatever $x = $y $a = 123 $b = xyz $c = $b(blah-blah) $c(); gnah ^ $a whatever
whatever $x = $y gnah ^ $a whatever
parse an option in our own future AWK-like tool would be a superpower.parse-tool, and then a [line](https://gitlab.com/hiiamboris/red-cli/-/blob/master/mockups/parse/parse.red#L73) that processes the CLI. The rest is magic.parse-tool func. Look at its spec. Look at the --help output for the app.remove into my parsers but I'm having a hard time solving for conditional removal. Meaning I want to remove something starting from the beginning of the parse rule but only if the whole rule ultimately matches. To complicate that, I need to scan through the whole file to find if the rule matches anywhere.remove to [bigger rule]does the opposite in that it effectively destroys everything I want to keep.to remove [bigger rule] isn't valid syntax. And thus far all of my efforts to embed remove into that bigger rule have resulted in partial removalsto [remove [bigger rule]]?p: to the current index when it's called?:other-spot (assuming it's set, of course), right?--write to the Parse tool to batch edit files ;)sed ;)defs: [<nothing>] line-rule: [ s: #L set line integer! e: ( s: remove/part s 2 new-line s yes ) :s ] src: [ R/S [ #L 2 T: "R/S" #L 3 A: "o" #L 4 ] ] parse src blk: [ some [ line-rule | p: [path! | set-path!] :p into [some [defs | skip]] ;-- process macros in paths | s: [into blk | block! | paren!] ;-- black magic... s: (print "END?" ? src halt) | skip ] ]
into [some [defs | skip]]*** Script Error: PARSE - matching by datatype not supported for any-string! input
parse [""][into [none!]]
into blk tries to match "R/S" because it's a series. The other into does some pre-checking (poor man's ahead) for paths and avoids that.text >> parse [forbidden zone]['forbidden 'zone [fail] | (print 'fallback!)] fallback! == false
[0 1234 skip "a"]would be more efficient than thru "a" when there is no "a" in the input, and the input is longer than 1234 characters?skip just means accept any token. Parse rules do not look ahead to see what rule is next.skip greedy in the sense that it matches any value, or is repetition greedy because it will consume all it can up to a point? Is to end the ultimate in terms of greed? parse is to use for many things, given how powerful it is. That makes these clarifications important because it's easy to wade into dangerous territory without realizing it. Suddenly you're lost and the urge to run will not serve you well. :^)>> parse "abc" [to "b" break] == false
break : break out of a matching loop, returning success.break, fail and reject designs are fundamentally flawed IMO. There was a discussion about them previously in https://github.com/red/red/issues/3478 , but unfortunately I don't know if Nenad saw it.true it requires to end works, similar to any skip - I was just looking for a more efficient "you're good buddy, thank you"break forces enclosing block to succeed, but also breaks looping constructs (i.e. any, some, while and integer repetition), if any. Control-flow keywords in Parse are bugged though, see https://github.com/red/red/issues/4193 for potential pitfalls. Your example is a bit of a special case: break "breaks" top-level rule, so it should return true, but then it did not actually exhaust the input, so it's false.true when input is exhausted? Seems to me that people are primarily concerned with whether something matches or otherwise succeeds along the way to the end of the input.fail returns, would be cool.to end, which also makes your code more self-documenting. But also consider that you may be collecting data or executing commands in a dialect. If you want to scan an input without actions, you can still tell if it's valid. Another use case might be a dispatcher that takes inputs, scans them fast without actions, and sends them on to handlers based on the match. If you only do partial matches, you may not be able to distinguish two inputs that start the same, which comes back again to how does it know what you want?parse to return true?:mark, for example. Should be marginally faster than combing thru megabytes of data with to or thru.parse to return true? You can signal that the thing you wanted is found the other way, e.g. [thing (found?: yes)].throw for that case?accept: [(throw true)] catch [parse [a b c]['a accept]]
mark:?mark: tail input? In pure Red, that is. IIRC Parse docs have some examples that show that approach.foreach loop running through all of my parses. Each one of them is executed in a while loop. So, while they are finding things they get sent back out to keep trying. As soon as they are unable to find what they are looking for, the next parser is called intail a special word in parse?tail is a Red native that sets series' index to a, well, tail. Just like head sets it to a head. Are you familiar with how series work in Red though?>> block: [a b c d e] == [a b c d e] >> c: find block 'c == [c d e] >> e: find block 'e == [e] >> parse block ['a 'b 'nope | :c mark: (probe mark) :e mark: (probe mark) skip] [c d e] [e] == true
:c is inserting that pointer as the current parse index. Then :e is doing the same.a b c d e and two indices into it, c and e.reverse the input and write all the rules in Da Vinci's mirror writing, or do a lot of scaffolding.>> block: tail [a b c d e f g] == [] >> parse block [[any [s: (probe s) if (head? s) break | (s: back s) :s]] :block] [] [g] [f g] [e f g] [d e f g] [c d e f g] [b c d e f g] [a b c d e f g] == true
block: [a b c [d a g f e] g h]
needle: 'a
parse block rule: [
(mark: none)
s: (s: tail s) :s
[any [
s: (done: tail s) ahead needle mark: :done
| ahead block! into rule s: [
if (mark) thru end
| (s: back s) fail
]
| if (head? s) [
if (s = block) fail
| :done
]
| (s: back s) :s
]]
]
mark
== [a g f e]
needle: ['g 'f] parse block rule mark
== [g f e]
needle: 'g parse block rule mark
== [g h]back? I didn't know about that at all. Is there a way to go backward in larger chunks? Say I wanted to back up by 1K bytes?? skipYou may want to look at the RebolLanguage. It has parsing built into the language, using a syntax that looks like the one you described in a message thread on wiki.c2.com. Rebol gets you to Red pretty quickly :)type file | find "string" filter.parse advocacy, which is one of the goals of https://www.redlake-tech.com/products/diagrammar-for-windows/. integer only. I wrote next code and not sure that it's optimal (it's working):if value? 'at-least-one-integer [ unset 'at-least-one-integer ] parse [aa: [] cc: 1 bb: []] [ some [ set at-least-one-integer integer! | skip ] if (value? 'at-least-one-integer) to end | [fail] ]
On: [aa: [] cc: [] bb: []]` answer should be false.parse [aa: [] cc: 1 bb: []] [to integer! to end]?find [aa: [] cc: 1 bb: []] integer!beats Perl in terms of line noise was what turned me off it. Line noise is the wrong sin, in my oh so humble opinion, to be focused on with perl. And you can see the results in the syntax they've chosen. They seem to be under the inaccurate impression that optimizing character count is somehow a good thing. Those days have long since passed. The correct optimization is for the maintainer of a codebase. You can certainly swing too verbose but the last thing I think when reading perl is "there's too much information in this line of code"input: [opt expression opt [";" input]]
expression: [
white-space expression white-space
| opt prefixes "(" expression ")"
| leaf
| expression binop expression
]
leaf: [opt prefixes atom-or-nil]
atom-or-nil: [atom | nil]
atom: [{"} string {"} | string]
string: [character opt string]
character: compose [(charset [not #"\" #"^""]) | spec]
spec: compose ["\" (charset {abtnvfr"\})]
nil: [none]
binop: compose [(charset "=.,|&:+*^'$_") | white-space | "\L" | "\D"]
prefixes: [prefix opt prefixes]
prefix: compose [(charset "[~/#<>%@`?!") | "!!"]
white-space: charset " ^-^/^L"'.replace "{" but it produce a lot of errors.block-to-comma-list: function[str-blk] [
str: none
rule: [collect any [s: skip keep (mold s/1) [end | keep (", ")]]]
str: head insert next copy "()" rejoin parse str-blk rule
str: replace/all str "none" "NULL"
print "-------"
probe str
]
block-to-comma-list ["25.22.11" "aa aaa aaaaa" {some string with quotes "Microsoft", "Apple", "HP"} "1.00" none]enclose: func [
"Returns a string with leading and trailing values added."
str [any-string!] "(modified)"
val [char! series!] "A single value can be used in the series"
][
rejoin either char? val [ [val str val] ][
[form first val str last val]
]
]
;enbrace: func [s] [enclose s "{}"]
;enbracket: func [s] [enclose s "[]"]
;enparen: func [s] [enclose s "()"]
;enquote: func [s] [enclose s {"}]
;entag: func [s] [enclose s "<>"]
block-to-comma-list: function[str-blk] [
str: none
rule: [collect any [set s string! keep (enclose s {'}) [end | keep (", ")]]]
str: head insert next copy "()" rejoin parse str-blk rule
str: replace/all str "none" "NULL"
print "-------"
probe str
]
block-to-comma-list ["25.22.11" "aa aaa aaaaa" {some string with quotes "Microsoft", "Apple", "HP"} "1.00" none]block-to-comma-list ["25.22.11" "aa aaa aaaaa" "1.00" none]
== "('25.22.11', 'aa aaa aaaaa', '1.00', )"| none! as an alternate. I missed that when changing to the string! check.replace call.none! in wrong place?rule: [collect any [set s string! keep (enclose s {'}) [end | keep (", ") | none!]]]none! rule? (hint: look at the string! rule)enclose: func [
"Returns a string with leading and trailing values added."
str [any-string!] "(modified)"
val [char! series!] "A single value can be used in the series"
][
rejoin either char? val [ [val str val] ][
[form first val str last val]
]
]
;enbrace: func [s] [enclose s "{}"]
;enbracket: func [s] [enclose s "[]"]
;enparen: func [s] [enclose s "()"]
;enquote: func [s] [enclose s {"}]
;entag: func [s] [enclose s "<>"]
block-to-comma-list: function[str-blk] [
str: none
rule: [
collect any [
set s string! keep (enclose s {'})
| 'none keep (" NULL") ; change to none! if it's a real none! value
[end | keep (", ")]
]
]
str: head insert next copy "()" rejoin parse str-blk rule
str: replace/all str "none" "NULL"
print "-------"
probe str
]
block-to-comma-list ["25.22.11" "aa aaa aaaaa" {some string with quotes "Microsoft", "Apple", "HP"} "1.00" none], before last NULL, if to make it (", NULL") it would issue on first none block-to-comma-list [none "25.22.11" "aa aaa aaaaa" {some string with quotes "Microsoft", "Apple", "HP"} "1.00" none]enclose: func [
"Returns a string with leading and trailing values added."
str [any-string!] "(modified)"
val [char! series!] "A single value can be used in the series"
][
rejoin either char? val [ [val str val] ][
[form first val str last val]
]
]
data: [none "25.22.11" 44 "aa aaa aaaaa" {some string with quotes "Microsoft", "Apple", "HP"} "1.00" none]
result: ""
count: 0
foreach el data [
count: count + 1
if (string? el) [el: enclose el [{'}] "," ]
if (el = 'none) [el: "NULL"]
append result el
if (count < length? data) [ ; prevent adding at end
append result ", "
]
]
insert result "("
append result ")"== {(NULL, '25.22.11', 44, 'aa aaa aaaaa', 'some string with quotes "Microsoft", "Apple", "HP"', '1.00', NULL)}SQLify: func [data][
head insert next copy "()" rejoin parse data [
collect [
any [[
['none | none!] keep ("NULL")
| ahead string! keep ("'") keep skip keep ("'")
| keep skip]
[end | keep (", ")]
]]]]
>> SQLify data
== {(NULL, '25.22.11', 44, 'aa aaa aaaaa', 'some string with quotes "Microsoft", "Apple", "HP"', '1.00', NULL)}head insert next copy "()" with mold to-paren?to-paren loads string, so yes, i.e. no."it's fail" parse is a great option at times. punct): punct: charset ".,;!?" remove-punct: [any [remove punct | skip]] text: "This, that? No! Some ... " parse text remove-punct text == "This that No Some "
remove-each? Trim is low-level R/S code, which can spin through strings as characters very quickly. You trade speed for flexibility. good-chars: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ -" text: "This! is (,not,).... a [good] example hyphen-ated" trim/with text trim/with copy text good-chars == "This is not a good example hyphen-ated"
trim/with aTxt rejoin [",.;:!?-(){}'" form #"^ » »]parse series [any ['a | 'b | ['satir | 'an]]] vsmy-rule: ['satir | 'an] parse series [any ['a | 'b | my-rule]]
blok: [1 | 2 | 3 | 4 | blok]
any. but now i know the answer, thank you! @greggirwin parse/trace? i was using it before, but i couldn't use now.parse-trace?parse-trace is a wrapper for parse/trace, you can view the source and craft your own - source parse-trace, or modify parse/trace itself. Parse in Block catch carriage returns?new-line, thank you.parse blok [ any [not word!]] to succeed on any block that has no wordsparse [1 2 3 4] [any [not word! | skip]] not word! succeedsany stops because it doesn't advanceparse? Or in common, how to get part of content before the last given symbol(here is dot in this example)?str: "%abc.txt.def.txt" copy/part str find/last str "." == "%abc.txt.def"
find/last str "." which gives ".txt", then `copy/part str ".txt" gives wrong message, why?copy/part str ".txt" ; is not the same as: copy/part str find/last str "." ; my code is the equivalent of copy/part str 13
find/last str "." only returns ".txt"?find/last str "." returns the str series at the 13th position (ie at the last period). That happens to look like ".txt" but is subtly different - it's effectively str offset by 13.find can be regarded as length that can be used as argument of other function(i.e. copy).str: "%abc.txt.def.txt" and xxx: ".txt" are completely separate series.copy/part str xxx is meaningless in this instance, while copy/part str find/last str ".txt" means copy str up to the position found within strstr: "%abc.txt.def.txt" xxx: ".txt" index? find/last str "." == 13 index? xxx == 1
copy/part str find/last str "." gives "abc.txt.def", and copy/part str 13 gives "abc.txt.def.". I am confused. Why does the former have not the last dot,but the latter have?parse.parse?find/last returns . through the end of the string. Effectively takes it, or "chops it off">> copy/part str find/last str "." is telling it to copy up to ".txt"index? is more readable, thanks. And I am still confused why the result of find can be used as length argument of copy/part. I don't find any explanation from help of Rebol or Red./part was shortened from R2, which was "Limits the search to a given length or position." so it's confusing as it is. If we change "length" to "extent" that helps, but the R2 doc string might be best. We can also add details to the length arg, and keep the /part doc-string short. The fact that the arg is called length makes it a bit confusing too. And just reiterating the types in a doc string isn't much more informative./part => Limit the extent of the search.
length [number! series!] "Length or series position"parse?file: %abc.txt.def.txt rejoin parse file [collect some [keep to #"." ahead [skip to #"."] keep skip]]
collectdirectly into string!:out: "" parse file [collect into out some [keep to #"." ahead [skip to #"."] keep skip]]
; Simplest >> first parse "%abc.txt.def.txt" [collect keep to [".txt" end]] == "%abc.txt.def" ; If you know the structure ahead >> first parse "%abc.txt.def.txt" [collect keep [2 thru dot to dot]] == "%abc.txt.def" ; Interesting :) >> first parse tail "%abc.txt.def.txt" [collect any [dot keep (copy/part head s s) fail | s: (s: back s) :s]] == "%abc.txt.def"
file: %abc.txt.def.txt ext: find/last file #"." ;== %.txt copy/part file ext ;== %abc.txt.def rejoin [copy/part file ext ext] ;== %abc.txt.def.txt clear ext ;== %"" file ;== %abc.txt.def
s: "abc" copy/part s s to be "" and not "a".parse fail to complie? I have a red script named "fortestparse.red" which only contains: Red [] parse "abc.def.ghi.jkl" [copy aa thru "." to "."] probe aa
red -r -t MSDOS fortestparse.red, it faild with wrong message: undefined word aa. Why and how to deal with it?aa: none before the parse. Compiler can't determine words from parse, they need to be initialized manually. Interpreter is smarter in this case.parse has to be preclaimed when compiling needed?>> morph [1 2 3 4] ['x 'y ...] ['y 'x ...] == [2 1 4 3] >> morph/auto [1 2 3 4] ['x ? even? x | skip ...] ['x ...] == [2 4] >> morph/auto/into [1 2 3 4] ['x ...] ['x (not 'x | " ") ...] "" == "1 2 3 4"
parse in Red 0.6.4:parse [a] ['A]
true in Rebol 2, Rebol 3, and older versions of Red (0.6.3). But Red 0.6.4 returns false./case refinement. Any idea what's up?>> parse/case [a]['A] == false
false in Red latest as I expected, but this is true in Rebol. Is Red's behavior here a bug? Should /case apply to word values in block parsing mode?test1: {sell 100 shares acme now}
test2: {Sell 100 Shares Acme Now}
test3: {Sell 100 Shares Acme 20-Nov-2021/11:45:00-08:00}
rule: [
set order ['buy | 'sell]
set amount integer!
opt 'shares
set ticker word!
['now (set 'when now) | set when date!]
]
parse load test1 rule
parse load test2 rule
parse load test3 ruletrue in Rebol, but test2 and test3 return false in Red with the case sensitivity bug in parse.rule: [ set order [set w word! if (w = 'buy) | set w word! if (w = 'sell)] set amount integer! opt set w word! if (w = 'shares) set ticker word! [set w word! if (w = 'now) (set 'when now) | set when date!] ]
true in Red (but all are false in Rebol). But the added complexity is the problem. Going to be very difficult to scale to a more complex dialect--especially if I expect people who haven't been using parse for decades to be able to write rules as well. I can't present a solution like this to my manager, let alone others who don't know me as well.quote buy | qoute sell/case should apply to them is separate. It is supported elsewhere.char! datatype to determinate symbol, but it's seems that every element in string is char! so the code do not work:>> parse ["81128A24826"] [ to char! s: (print s) ] == false
>> numbers: charset "0123456789"
== make bitset! #{000000000000FFC0}
>> parse "81128A24826" [collect some [keep numbers | skip]]
== [#"8" #"1" #"1" #"2" #"8" #"2" #"4" #"8" #"2" #"6"]>> parse "81128A24826" [collect some [ numbers | set x skip ]] == [] >> x == #"A"
head clear find/last copy "a.b.c.d.e.f.g" dot == "a.b.c.d.e.f"
count: 0 string: copy "a.b.c.d.e.f.g.h.i" until [string: next string if dot = first string [count: count + 1] all [count > 5 string: head clear string]] == "a.b.c.d.e.f"
count: 0 parse string: copy "a.b.c.d.e.f.g.h.i" [any [s: dot if (5 < count: count + 1) (clear s) | skip]] string == "a.b.c.d.e.f"
splitit might be first split "a.b.c.d.e.f.g" [last dot] == "a.b.c.d.e.f"
first split/last "a.b.c.d.e.f.g" dot == "a.b.c.d.e.f"
find/last copy "a.b.c.d.e.f.g" dot returns a pointer which is used by head clear. If I need to store the returned value in a variable which will not be affected by the further movement of the pointer, what shall I do?head-of-string: head tail-of-string: clear find/last copy "a.b.c.d.e.f.g" dot
clear find/last str: copy "a.b.c.d.e.f.g" dot ... str == "a.b.c.d.e.f"
str: " 1.5yuan ^/ 3 yuan ^- -2.3dollar"digit: charset "0123456789.-" parse trim/all str [collect any [copy num some digit keep (load num) | keep to [digit | end]]] == [1.5 "yuan" 3 "yuan" -2.3 "dollar"]
str: " 1.5yuan ^/ 3 yuan ^- -2.3dollar"
number: charset "0123456789."
string: charset complement number
parse str [
any [
copy num some number (print ["Number: " num])
|
copy str some string (print ["String:" str])
]
]parse str [any [digit not digit insert space | skip]] new-line/all load str no == [1.5 yuan 3 yuan -2.3 dollar]
>> parse trim/all str [collect any [keep some digit | keep to [digit | end]]] == ["1.5" "yuan" #"3" "yuan" "-2.3" "dollar"]
#"3" into 3 by load #"3" as you did in parse but failed. I also tried with do #"3", and get #"3". So how to change a digit char to number? And why does it work by load in parse but fail outside parse?copy. If you have char, then you can do e.g.load to-string #"3" or to-integer to-string #"3". (Or (to integer! #"3") - 48:)keep copy _ some digit in your rule to get all elements as strings.