Archived messages from: gitter.im/red/parse from year: 2021

gltewalt
07:46Percy the Parse :mouse2: - finding his way to end of mazes
07:47:mouse:

gltewalt
22:47So getting back acquainted with parse (not that I was especially acquainted before), how to parse out all the words in a string that come before "=>" ?
Using collect/into

"hello => some text..."
23:01Oh, probably just [copy my-word to "=>"] ?
greggirwin
23:14to works well in simple cases, but may skip things you didn't intend if your data isn't regular.
gltewalt
23:38given txt: "Hello => some text... World => other text"
23:40parse txt [some [collect [keep to "=>"]]]

Not quite
23:41looking for ["Hello" "World"]
23:44I'm sure I've got things out of order..

gltewalt
00:52Dag nab it...
01:06What I really need is Between whitespace and =>
01:07Sortof
03:12
>> words: copy []
== []
>> parse txt [any [collect into words [keep to "=>" thru lf]]]
== true
03:12Like so?
toomasv
04:40
>> parse txt [s: collect any [ahead [some #" " "=>"] :s keep to #" " some #" " "=>" | #" " s: | skip]]
== ["Hello" "World"]
gltewalt
04:53Thanks but I cant make sense of that yet
05:09If you were collecting the word values from help-string action! it would be the same?
greggirwin
05:12
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
	]
]
05:16Oh for a fixed pitch font in Gitter. I think I have those lined up OK now.
05:18The trick is that 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.
toomasv
05:21Thanks @greggirwin , I started explaining it too, and then changed the rule a bit, and here it is with explanation:
parse txt [s: collect any [some #" " "=>" e: :s keep to #" " :e | #" " s: | skip]]

Explained:
[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
]]
greggirwin
05:22Great minds. :^)
toomasv
05:25:smile_cat:
05:48But it is still too brittle. Suppose you have text where "=>" may have no spaces before it. Here is solution:
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 !"
05:54:point_up: [January 26, 2021 7:09 AM](https://gitter.im/red/parse?at=600fa40bdfdbc1437fb1cbfc)
>> 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}
gltewalt
06:03Thanks ghys.
06:03Guys
toomasv
06:03To make it even more robust, use 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 !"

gltewalt
06:04Things to play with tomorrow
06:06ws: charset reduce [space tab cr lf]
toomasv

GiuseppeChillemi
10:06Could a code act as a rule? I want to create a function that does a custom check and then returns true or false. Parse would fail or have success accordingly.
rebolek
10:10Yes, you can do something like parse data [.... (result: my-custom-check) result].
hiiamboris
10:26[... if (code)...] ?
rebolek
10:26right, that's easier

gltewalt
01:31Are there examples somewhere for parse/trace?
01:32I'm not sure how to use it. parse/trace "stuff" [some rule] func, but the func part is the part I'm not sure about.
zentrog:matrix.org
05:03@gltewalt: check out parse-trace, which has a built in callback method and might be all you need
toomasv
09:28Yes, look at result of parse-trace and compare it to [this function](https://github.com/red/red/blob/master/environment/functions.red#L311) used to create the result.
GalenIvanov
13:13@toomasv Maybe this is too easy for you, but it could be a nice exercise for parse: [Evaluate left-or-right
](https://codegolf.stackexchange.com/questions/218618/evaluate-left-or-right)
toomasv
14:42@GalenIvanov Interesting exercise, thanks! And nice solution.
GalenIvanov
14:45@toomasv Thank you, it's iteresting indeed.
toomasv
15:23@GalenIvanov Working on your solution I got it 15 chars shorter :smile:
func[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]
15:29But for given patterns the shortest possible is just <.
GalenIvanov
17:15@toomasv That's very nice! Btw, the question allows for the input to be entirely enlosed in parens ><> -> '(><>)`. This way your solution can be even shorter:
17:15
func[s][a: charset"<>"until[parse s[any[t: change["("a"<"a")"](t/2)| change["("a">"a")"](t/4)| skip]]2 > length? s]s]
hiiamboris
17:35func[s][r:["<"|">"]until[not parse s[to change[opt"("x:[r"<"r | r">"x: r]opt")"](x/1)to end]]]
17:35as far as obfuscation goes.. 94b
17:38who needs spaces anyway
toomasv
17:50@hiiamboris Wohoo! Still 22 shorter than @GalenIvanov 's latest! :clap:
GalenIvanov
19:05@hiiamboris Wow! That's great! After seeing yours and @toomasv solutions, they look obvious - too bad I didn't find something similar by myself. Thank you for your lessons!
hiiamboris
19:13we can probably cut 2 more bytes by using while:
func[s][r:["<"|">"]while[parse s[to change[opt"("x:[r"<"r | r">"x: r]opt")"](x/1)to end]]][]
:D
GalenIvanov
19:16:+1:

GalenIvanov
08:22@hiiamboris Can I use your code in Stack Exchange's Code Golf & Coding Challenges (with credits)?
toomasv
09:12@hiiamboris You probably meant:
func[s][r:["<"|">"]while[parse s[to change[opt"("x:[r"<"r | r">"x: r]opt")"](x/1)to end]][]]
Typo in the end?
GalenIvanov
09:16 I observed the misplaced ] too
09:18and using the clarification of the rules, opts are not necessary, just plain "(" / ")"
toomasv
09:22Indeed!
hiiamboris
09:40Yep!
GalenIvanov
09:45@hiiamboris Thank you!
hiiamboris
09:56func[s][r:["<"|">"]while[parse s[to change[opt"("x: r["<"r |">"x: r]opt")"](x/1)to end]][]] 91b
GalenIvanov
10:00@hiiamboris Great - a new improvement!
toomasv
10:02Without opt's 85 ;)
GalenIvanov
10:05[86 bytes](https://tio.run/##ZVLLbsMgELz3KxAnOFW9Wqv5iF4RhyjGTarUiXCq5tB/d/eB7Vj1YWXYYR4LtfTze@lTfhm6efgej2nKqXbJk//18PnndL6UdDvUqbgp3a/ueDqMHyX54B@dq4KrTpCyyj76HB6vb5GBZexzTnnK83Ct5XA8uckl9@L48wB5@yMKBIptFQDEtRe4R4hcKEZadwMYRpEb4O4OT9wjpmB80C4XRQrFel4W0lMa09cFn1tRrGEMaoBbDd42@dzGJxpWxC5XQ0J5yULELZPBLeoiDLFDFuzJBQvvXKMpaKpNH2qTGgZPGWDDkzCE/75hltFsUjOPJxTIgDAamPHYpoZNLbR5tnvRYryGJ@yVaR2NRNc/MlrseWm9IqXCMmwsw9YE@2kIk56SG1uyyZ2qstxGTu5Wz@Pdpa/rpee36bvOu8HV8nk9j/K@ZY/fc3Z5/gM ) - I add a s to make it work iin TIO

qtxie
03:47Why this parse rule works in Rebol but not in Red?
https://gist.github.com/qtxie/e321ac4d1c737fc6e5d843f67cbb7de4
toomasv
07:57Strange indeed! For some reason it stops parsing after #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?
With said insertion, halt replaced by () and blk changed, both REBOL and Red produce this:
>> do %parse-test.r
LIBREDRT-file : "libRedRT.dll"
src: [
    print "libRedRT.dll"
]
08:03Although blk seems not to affect the result.
gltewalt
22:57I have severa rules, and I get the result i want by doing multiple passes - parse input [rule], repeat, but i dont get the results i want if i try to do one pass.
22:58Do i need to add in the backtrack? [rule | rule2] etc?
22:59Does order matter?

greggirwin
00:30Have to see the rules to know. Do you have a some or any to repeat over the rule?
gltewalt
greggirwin
01:04Order does matter. You need to put the longest matching rules first.
gltewalt
01:06This is 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:

https://gist.github.com/gltewalt/4bdba9588bec79d111bb4056fc209e41
greggirwin
01:18Will have to wait for time to dig in, but a key thing to remember is that 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.
gltewalt
01:20What should i use in place of to?
greggirwin
01:20To handle all your rules in this case, you would need |, but then you'll run up against to skipping over things, based on the order your check your rules in.
gltewalt
01:21Most of these are reated patterns, but... little steps
greggirwin
01:21| skip. So you check your known values to match, then skip one char if none of them do.
gltewalt
01:21Related patterns
greggirwin
01:28e.g.
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 ss
gltewalt
01:30Dont need the anys?
greggirwin
01:33The outer some could be any instead, which will handle all the sub rules.
qtxie
02:12@toomasv Thanks. So maybe a bug in parse?
toomasv
04:38@qtxie Yes, I thought so too.
08:53@gltewalt On from @greggirwin :
level-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
	]
]


But I am not sure it works for all funcs.
Oldes
17:52Currently there is:
>> parse "a" [#a]
== false
>> parse "#a" [#a]
== false

Should not be there an error instead as it looks that issue! is not supported.
toomasv
17:57Datatypes and typed data in general are not supported in string-parsing.
Oldes
18:00Really?
>> parse "http://aaa b c@d <e>" [http://aaa sp "b" sp c@d sp <e>]
== true
18:02Also:
>> parse "a" [%a]
== true

But I'm not sure if I like this result... but it's compatible with Rebol.
18:09It is consistent with:
>> parse "oldes" [@oldes]
== true

But I have a feeling that it would be more useful if it would count also the @ char... having true = parse "@oldes" [@oldes]
gltewalt
18:49Do I have to use that copy trick and moving around the index for this?
; 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]
18:50> Thanks @greggirwin , I started explaining it too, and then changed the rule a bit, and here it is with explanation:
>
> parse txt [s: collect any [some #" " "=>" e: :s keep to #" " :e | #" " s: | skip]]
>

> Explained:
>
> [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
> ]]
>


Similar to that
toomasv
19:19@gltewalt These are all any-string! types (string! file! url! tag! email! ref!), but issue! does not belong to these.
gltewalt
19:55Its just another string if you split it, get the value, then rejoin
greggirwin
20:02@Oldes there is limited support for typed parsing from strings. We should consider it experimental, and maybe not as useful now with transcode/trace available. I believe it helps to think in terms of text, which may be anything, versus Red data.
Oldes
20:04@toomasv I know that issue! is not in any-string!... I was asking if there should not be error like in Rebol instead of silently ignoring it.
20:06On the other side.. there are no errors for unsupported types... like:
>> parse "aa" [%1]
== false
gltewalt
20:58I swear there was an error on my linux version - cant check until after work, now

toomasv
06:00@Oldes It is just the same as you showed before:
>> parse "aa" [%aa]
== true
>> parse "1" [%1]
== true

As it belongs to any-string! typset it is supported. Just not matching.
06:41@gltewalt Yes, copy and juggle indexes:
>> 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>^/}
06:42Alternatively, without jumping around:
>> 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>^/}
Oldes
09:37@toomasv ah... my fault... I wanted to write:
>> parse "1" [1%]
== false ;<-- no error when used `percent!` (or any other unsupported) datatype as a rule.
>> parse "1%" [1%]
== false
toomasv
09:41I agree that error would be useful if trying to match non-any-string rules on any-string input. Like:
>> parse "1" [integer!]
*** Script Error: PARSE - matching by datatype not supported for any-string! input

Or even
>> parse "1" [string!]
*** Script Error: PARSE - matching by datatype not supported for any-string! input
hiiamboris
12:52That's strange considering:
>> parse to #{} "1" [integer!]
== true
12:52I think it's a bug
13:01OK crash is a different issue, I'll report it myself
gltewalt
18:18@toomasv I had this before you responded


level-3-rule: [change "==== " {<li><a href="#_} copy s to newline insert {">} insert (rejoin [ s {</a></li>}])]
toomasv
19:18If there are single words only in headings and lowercase doesn't matter, then why not.
gltewalt
19:25Yeah

gltewalt
03:48The doc says you can match by datatype for utf-8 encoded values if you use to binary!
03:53parse to binary! "1" [integer!] == true
03:55https://imgur.com/a/Jod9rmB

gltewalt
00:31Can you copy more than one thing?
00:31
s: "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 ":"]

00:34The whole string should go in between td class="content"> and . No text after
00:36Tried this but 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]
00:37I swear ... I'm never going to learn this
02:01Close, but need the text after 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 ":"]
02:21Oh well
toomasv
08:25@gltewalt Several things about latest:
* s is used for original string but reused inside rule
* change rule starts with empty option - it will never check further alternatives
* x is referred before it has got value

Here is one possibility, assuming there is no more text (which there probably would be):
str: 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>}
gltewalt
15:52Empty option was typo
greggirwin
18:36R2
parse [1] [any [1 1 1]]
;== true
val: 1
;== 1
parse [1] [any [1 1 val]]
;== true

Red:
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:


Do others agree this is a bug?
toomasv
18:43But in Red you can do this:
>> parse [1] [any [1 1 1 1 1 1 quote 1]]
== true

Not in R2:
>> parse [1] [any [1 1 1 1 1 1 quote 1]]
== false

Not that it is useful :confused:
18:46Plain integer is **always** a rule in Red
>> val: [quote 1] parse [1] [any [1 1 val]]
== true
hiiamboris
18:50I agree, a bug.
greggirwin
19:05How to/thru handle ints is another wrinkle. Today they don't. But R2 didn't either.
19:07parse [1] [any [1 1 1 1 1 1 quote 1]] melted my brain.
19:13I can use the val: [quote 1] trick (thanks @toomasv) for my experiment.
toomasv
19:33But there *may* be situations where this matters, e.g. Red:
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

R2:
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
hiiamboris
19:35oh wow
greggirwin
19:43Naming things helps sooo much. :^) Saved my melted brain from leaking out entirely.
toomasv
19:45:point_up: [February 12, 2021 9:05 PM](https://gitter.im/red/parse?at=6026d19532e01b4f71904f32) What about 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
greggirwin
19:50Indeed. I mixed something up somewhere. Thanks for the sanity check.
19:54The nested range "feature" will surely help us win code obfuscation contests. One of those things that makes logical sense when teased apart, but was totally opaque to me with the unnamed 1 example.
19:55I bet @toomasv cheated and used DiaGrammar. ;^)
19:55[![image.png](https://files.gitter.im/5b147b8fd73408ce4f9bc4b4/WFBw/thumb/image.png)](https://files.gitter.im/5b147b8fd73408ce4f9bc4b4/WFBw/image.png)
toomasv
20:03:smile: I'd like to have had that idea!

gltewalt
20:14How come the index of the next position to match is one beyond the match with strings, but is at the match with blocks? Or is it not supposed to be that way?
20:16https://imgur.com/a/PoWNrAx
hiiamboris
20:20both look identical to me
20:202 = after '7'
gltewalt
20:25Am I just roasted tired?
https://imgur.com/a/IWRIYEa
hiiamboris
20:31OK here it looks weird
20:32Is this a bug in parse/trace or parse-trace?
gltewalt
20:33Might be
20:33Im running to the other job right now so i cant try to check
hiiamboris
20:34You'll hunt it down eventually ;)
gltewalt
20:37Pulling from on-parse-event

gltewalt
00:58longer example
https://imgur.com/a/qIGWrVg

gltewalt
05:59Fixed it. Not an issue with on-parse-events. I was the issue.

GiuseppeChillemi
23:00How could I trigger 2 opposite actions on a rule, one if it succeeds and one if it does not succeeds? For example: if the element I have found is a string I want to set a word and do some code, if it is not a string I want to write "expected a string"
greggirwin
23:15
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])
	]
]
GiuseppeChillemi
23:25I can't actually test, is | set v not string! possible?
greggirwin
23:38Not 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])
    ]
]

But then you have to ask if that's clearer. You could also create a custom typeset, and match against that. e.g.
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])
    ]
]
GiuseppeChillemi
23:50Thank you, I will work on it. It is a technique that I thought could be useful in situations like function's argument type checking, so you exactly know that has gone wrong and where.
greggirwin
23:53That's an interesting idea. Datatypes for func specs have always been seen as inclusive (what you want) rather than exclusive (what you don't want). The latter may be more informative in some cases, and also more automatically extensible (pros and cons there). Puts a new twist on how you think about testing too.

GiuseppeChillemi
00:07Until now we have given meaningfulness to the domain of what is matched, but when something has not matched, another parallel domain arises and it is the domain of errors. Think when you parse a communication protocol and its rule fails: you want your code either to process the successful event but also do something like storing the position and content, for unmatched one; also, for other rules, you may want to collect all unmatching elements in a block...
greggirwin
00:12Yes, you'll see more of my thoughts on this in the near future.
GiuseppeChillemi
00:13Waiting to read all your thoughts! Goodnight!
00:43Can't sleep:

Its simpler that we think:

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]) 
    ]
]


Expressed linearly the alternate rule matches everything which has not been matched in the first rule.
greggirwin
00:44Ummm, isn't that what I posted originally? ;^)
GiuseppeChillemi
00:45Yes, we just miss: not-string!: exclude any-type! make typeset! [string!]
greggirwin
00:46:point_up: [here](https://gitter.im/red/parse?at=603d75a695e23446e4074b3c)
GiuseppeChillemi
00:46And please, I feel ashamed to ask this simple thing but why you check the type with get notation? type? :v
00:46> :point_up: [here](https://gitter.im/red/parse?at=603d75a695e23446e4074b3c)

Ok, I NEED to sleep! Bye!
greggirwin
00:47Since it's a block it could contain "active" values like functions, which we don't want to evaluate in this scenario.
toomasv
09:07It's even simpler:
some [
        set s string! (print 'do-do-do) 
        | v: (print ["Expected a string, but saw a(n)" type? :v/1])
    ]
09:09BTW in ahead not ahead is superfluous. Use just not if you need negative lookahead.
09:15> why you check the type with get notation?

You might have a function in input, e.g.:
>> 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
GiuseppeChillemi
10:26Thanks you, I make this error often because of a phenomenon of expansion of the concepts I was using to the other elements: I was thinking about inactive values that get reduced, then I also see the functions as inactive values that get reduced, forgetting they are active ones. I have learned that's part of the way I work, so I ask for an explanation because I know that simple thing sometime could block me and I should not be ashamed to ask. O:-)
greggirwin
16:25Thanks for the addenda and errata @toomasv. :+1:
16:25Never be ashamed. :^)
toomasv
16:29:flushed:
GiuseppeChillemi
17:24> Never be ashamed. :^)

Never, ever!
gltewalt
18:05I'm frequently ashamed :-)
GiuseppeChillemi
19:14Programmers are often sensitive people because higher intelligence is associated with higher perceptions and reactions, but we are not so good at managing them!

gltewalt
00:19I dont consider myself a "real" programmer
02:02I think this is ready for others to test out . If you're willing, I'd be happy to get the feedback.
https://github.com/gltewalt/parse-caddy
toomasv
05:06@gltewalt Nice work, congratulations!
greggirwin
05:48Thanks for posting @gltewalt ! Nice mascot. :^) I will check it out soon.
gltewalt
06:05:blush: I hope it's ok for completed v1
hiiamboris
09:20I'd like such tool to remember start and end of every match. Then when I select one char/value or more, it would highlight in green parts of the rule that matched this whole selection, and in red - parts that tried to match it but failed. Or smth like that :)
gltewalt
18:16like, copy the selected input and pass it to parse/trace?
18:19I need to learn rich-text . Right now the highlight color when text is selected isn't showing itself, for some reason
ne1uno
18:58a log area might be nice, push results and traces
gltewalt
23:01Log files, or just text to a panel?
ne1uno
23:25just to an area that could be copied from. I usually add a limiter so the log does't keep growing
23:25log-area/text: copy/part rejoin [form count etc other-f/text "**" newline log-area/text] 28000

bubnenkoff
09:44I have got next data structure:
data: [
	id: 123
	lots: [
		lot: [name: "apple"]
		lot: [name: "bananas"]
	]
]


I need to make every child know 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"]
	]
]


At first step I want to learn how to collect root 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]]


But it's seems that 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]]


But it's also not working.
09:54Wow! This rule is working:
parse data rule: [some [ set b quote id: ahead [ set b integer! ] | ahead block! into rule | skip]]

Am I right understand that ahead with subrule do collect value?
rebolek
09:59ahead 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.
10:00OTOH I can imagine situation where it makes sense.
bubnenkoff
10:01Is it's good idea to use skip (set b skip ):
parse data rule: [some [ set b quote id: ahead [ set b skip ] | ahead block! into rule | skip]]

if it's does not matter what type I should collect?
rebolek
10:02Yes, I don’t see a problem there. If you know what to expect there and don’t need to check for validity, then skip is good enough (and faster).
bubnenkoff
10:38How to insert value in block?
>> b: []
== []
>> parse [b] [insert 44 block!]


I am expecting to get b: [44]

rebolek
10:53
>> parse reduce [b] [into [p: insert (44)]]
== true
>> b
== [44]
bubnenkoff
10:55is it possible to do without reduce? I will need to mix insert with code above....
toomasv
10:56This
parse [b] [set s word! (if block? s: get/any s [insert s 44])]

or this
parse [b] [ahead word! s: skip (if block? s': get/any s/1 [insert s' 44])]
bubnenkoff
10:56oh thank!
toomasv
10:58better
parse [b] [set s word! (if block? s: attempt [get s] [insert s 44])]
bubnenkoff
10:59but if I am sure that it's block I can drop checking?
toomasv
11:01Then you can do even
parse [b] [set s word! (insert get s 44)]
bubnenkoff
11:01cool! thanks!
toomasv
11:01or
parse [b] [s: skip (insert get s/1 44)]
bubnenkoff
11:13@toomasv I tried to mixed all code together but it do not work:
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


I expected that it will insert 44 in every new block, but iit does not
toomasv
11:13
parse data [
	quote id: set id skip (probe id)
	quote lots: into [some [quote lot: s: skip (insert s/1 compose [id: (id)])]]
]

or
parse data [
	any [
		quote id: set id skip
	|	set-word! ahead block! into [
			any [set-word! set block block! (insert block compose [id: (id)])]
		] 	
	]
]
bubnenkoff
11:15wow!!! Thanks!!!
toomasv
11:15:smile:
pekr
11:17Now I will study the above few lines of code for one week and hope I get a clue, what it does :-)
bubnenkoff
11:20Yeah!! I mixed Toomas solution with my (I understand my code better):
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"]
    ]
]
11:21It's interesting that adding 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
11:35@toomasv could you explain what are you doing with this:
set-word! set block block!
toomasv
11:40After a set-word! it sets a word block to the following block! value.
Ok, here's another
parse data rule: [
    some [s: 
	  quote id: set b skip 
	| if (head? s) insert (quote id:) insert (b) 
	| ahead block! into rule 
	| skip
	]
]
bubnenkoff
11:51thanks! Could you also explain why with skip it's process only first level child and do not go deeper (code above)
toomasv
11:51In your rule 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

So, following rule does it without unnecessary clutter:
quote id: set b skip | ahead block! s: (insert s/1 compose [id: (b)]) into rule | skip
bubnenkoff
11:53> After a set-word! it sets a word block to the following block! value.
> Ok, here's another
>
> parse data rule: [
>     some [s: 
> 	  quote id: set b skip 
> 	| if (head? s) insert (quote id:) insert (b) 
> 	| ahead block! into rule 
> 	| skip
> 	]
> ]     
>


this look very cool!
18:27Why 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
    ]
]
hiiamboris
18:29It does for me
bubnenkoff
18:32Oh, sorry, strange. One moment I will check in fresh console
18:33yes, sorry! I did not understand the reason I got this issue
18:41
data: [
    id: 123 
    lots: [id: 123 
        lot: [id: 123 name: "apple"] 
        lot: [id: 123  name: "bananas" ]
    ]
]


with this data it's not 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
    ]
]

toomasv
18:49It doesn't print "Hello" because you never happen to be in head of block with these data.
Oldes
18:50@bubnenkoff because you have skip in the first rule
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")
    ]
]
greggirwin
18:51@gltewalt nice work on Parse Caddy. It's very close to what I'm working on now, as an interactive approach. I do get this error when clicking Parse Block Values:
*** Script Error: reset-field has no value
*** Where: act
*** Stack: context view do-events do-actor do-safe

Should it consider success that any rule matches, or based on parse's result? e.g. with "a,b,c" as input to #"," passes but some [thru #","] doesn't.
Oldes
18:56@bubnenkoff why you want to do something like that? If it would be processed, you would get into infinite loop!
You may try it by replacing (head? s) with (2 = length? s). But you will have to kill the console!!!
bubnenkoff
19:01Oh! Infinity loop! I am just experimented!
gltewalt
20:39@greggirwin ill look at it this evening. As far as tirn green on a match vs green on parse returning true... im not sure.
20:54Turning green

gltewalt
00:22@greggirwin that's weird, because some [thru #","] does match. It just doesn't turn green. some [to #","]` does turn green.
greggirwin
00:41Right.
gltewalt
00:57Oh, I see why
01:35Could do it this way
https://imgur.com/a/NKn2qoc
01:44Or highlight the match position in the Input field, and turn color in the rule field.
01:45Speaking for myself as a parse novice - I'm more interested in seeing if something matches and then doing something with that match, than I am interested in whether parse reaches the end of the input or not
03:42One known issue is that if you use insert, it inserts until match? is false - which means the whole time you're hitting space or typing the next part of the rule
03:47https://imgur.com/a/GhpkEvM
03:48Not sure how to fix that, but I fixed the other stuff
hiiamboris
08:47One more thing. Parse is often expressed as a set of named rules. Such tool should be able to help trace in which rule the match stuck or never succeeded.
gltewalt
15:53My intent was to have a tool that helps you see what is going on for "a" parse rule.
15:54Beginners in mind.
15:57Something for me to think about, though

gltewalt
02:34Not exactly safe input, but making a little more progress.
https://imgur.com/a/x10MYK7
02:52https://imgur.com/a/gF1VXZy

bubnenkoff
11:49@toomasv is there any nice way to measure nesting level?
data: [
	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
    ]
]

For example I want to add id only on third level. But common task is measuring of levels
toomasv
12:39@bubnenkoff E.g.:
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
    ]
]

If you don't need to insert into single level, use just level = 3 or level = id-level.
If you need ranges of levels, then you need to adjust comparison.
bubnenkoff
12:55thank!
12:57I was quite near! But stuck with this line:
ahead block! (level: level + 1) into rule (level: level - 1)

I did not expect that code after into rule will work, because I thought it will make jump exactly in this moment
toomasv
12:59Yes, it continues after exiting from rule, i.e. after into rule.
bubnenkoff
13:14Oh! I understood!
13:17So we do into rule and after it do decrement, right?
toomasv
bubnenkoff
18:55Why exclamation mark is printing only once?
parse [11 aa 11 bb cc] [some [ahead word! | skip (print "!") | 'cc]]
>!
toomasv
18:59Think it through:
>> 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

Remember, that ahead does not advance.
bubnenkoff
19:09At first step we are checking it word! is ahead, than we are doing skiping.
No we have: [aa 11 bb cc]
And first rule is success but input do not advice? Right?
toomasv
19:14Yes, on 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.

bubnenkoff
10:33Here I am trying to collect/print all set-words
parse [a: 1 b: 2 name: "Mike" c: 3] [ ahead [set-word!] set w set-word! (probe w) fail | skip  ]

And I can't understand why it's do not work.
ahead -- look ahead
set w set-word! set w to next value (next value is set-word)
fail should advance input to next rule.

Where I am wrong?


hiiamboris
10:45aren't you forgetting a loop?
bubnenkoff
10:45ohhh!
10:48still stop at first set-word:
>> parse [a: 1 b: 2 name: "Mike" c: 3] [some [ ahead [set-word!] set w set-word! (probe w) fail | skip  ] ]
a:
10:55I dropped 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  ] ]
rebolek
11:06Yes: fail : force current rule to fail and backtrack.
hiiamboris
11:12@bubnenkoff [fail] might work in place of fail.
Put simply, fail and reject are a dubious design and best avoided.
rebolek
11:42@hiiamboris do you have a better design idea?
hiiamboris
11:49https://github.com/red/red/issues/3478#issuecomment-406884362
11:54specifically for fail: equivalence with not none or end skip is proposed
12:04like here https://en.wikibooks.org/wiki/Rebol_Programming/Language_Features/Parse/Parse_expressions#Parse_idioms
rebolek
12:19Thanks, I take a look. I’m not using fail or reject very much but break is extremely important.
toomasv
13:51@bubnenkoff Neither is ahead needed here:
[some [ set w set-word! (probe w) | skip ] ]
Oldes
14:08@hiiamboris I just FAILed reading the 3478 issue, because my brain REJECTed to understand all of mentioned cases and I must take a BREAK now ;-)
hiiamboris
toomasv
14:10@bubnenkoff 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 ] ]

Same could be achieved in different ways:
parse [a: 1 b: 2 name: "Mike" c: 3] [some [ not quote b: set w set-word! (probe w) | skip ] ]

or
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)]]]

Oldes
14:13or:
parse [a: 1 b: 2 name: "Mike" c: 3] [some [ quote b: | set w set-word! (probe w) | skip ] ]
14:20@hiiamboris what took my attention is yours note:
> parse haystack [to needle to end] But assume that there's no end (maybe we're parsing continuous stream of data).

It makes me think, if there is any way how to exit from parse with success in such a case without use of the to end? Because I must say that I use it very often.
14:24In R3 it could be:
>> parse "ab" ["a" return (true)]
== true
rebolek
14:25BTW, just now I’ve updated my [HTML parser](https://gitlab.com/rebolek/castr/-/blob/master/html.red) so now it can handle even more buggy HTML than before. Stuff like startinner

end
and other monstrosities can be parsed without a problem.
Oldes
14:26But return (true) is in R3 14x slower than to end and longer:/
rebolek
14:2714x slower? wow
Oldes
14:29Maybe 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:)
bubnenkoff
14:43Am I wrong or the was discussion about then should be remove from parser?
hiiamboris
14:45There's no then in Red as far as I know
bubnenkoff
14:49https://ungaretti.gitbooks.io/red-language-notebook/content/parse.html

" then Regardless of failure or success of what follows, skip the next alternate rule."
hiiamboris
14:49maybe it was there 2-3 years ago but was removed
14:53> It makes me think, if there is any way how to exit from parse with success in such a case without use of the to end? Because I must say that I use it very often.

> Maybe 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:)

That's an interesting consideration, but probably should be left to a separate REP? ;)
We don't have infinite data (yet), but to exit parse asap, I recall I used exit at least once. Otherwise I could write loop 1 [parse ... [ .. (break)..]]. But that's a workaround alright.

Oldes
15:46I'm used to 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.
rebolek
16:49then was extremely confusing, no one could figure out how to use it properly and I believe it was buggy anyway.
bubnenkoff
18:26@toomasv I did it! Thanks for examples! I will learn them. You was totally right that I do not need ahead before set
data: [
    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     
    ]
]


This code will print all set-words
greggirwin
18:31If someone would like to track this chat and pull useful topics and examples into https://github.com/red/red/wiki/Parse-Cookbook, that would be fantastic. Such great information, but it will fade as chat continues. Of course, I'm selfish and this is also to keep me from having to ask the same questions later. :^) @bubnenkoff would you be up for that?
bubnenkoff
18:33I will to add examples later, I am doing small collections of good solutions
toomasv
18:35@bubnenkoff :+1:
hiiamboris
18:38@bubnenkoff FYI there's another way to do the same task: https://gitlab.com/hiiamboris/red-mezz-warehouse/-/blob/master/collect-set-words.red#L9
In Red there's always another way :D
bubnenkoff
18:44@hiiamboris hah! Funny!
greggirwin
18:49@hiiamboris the 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.
18:50Thanks @bubnenkoff ! You've been doing a lot of parse work.
18:53@hiiamboris your set-word collector is "sufficiently advanced technology", as Mr. Clarke would say. :^)
19:03Very clever leverage of function.
hiiamboris
19:06Had to google it :D Turns out it's one of the guys who wrote 2001
toomasv
19:30Yes, @hiiamboris , funny func! :)
hiiamboris
19:32Thanks (:
toomasv
20:53@hiiamboris, I wanted to compare your witchcraft with a more traditional method:
collect-setwords: function [block [any-list!]][
    setwords: [any [keep set-word! | ahead any-list! into setwords | skip]] 
    unique parse block [collect setwords]
]

and tried both on %red-object-browser.red:
>> rob: load %red-object-browser.red
>> length? c1: collect-setwords rob
== 42
>> length? c2: collect-set-words rob
== 46

Hey, you gather more words!
>> exclude c2 c1
== [i: item: w: ctl:]

Ha, these are foreach words probably, i.e. you gather some words that are not set-words! *per se*.
But your performance is much better:
>> 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]
hiiamboris
20:56Good catch on foreach!
21:00I'll note this in the header
toomasv
21:00:+1:
greggirwin
21:01Hey @rebolek, when we revisit Gritter at some point, it could be really cool to find messages with code.
rebolek
21:26@greggirwin I believe there’s a function for it in Gritter, I need to take a look.
21:27Yup, there is: https://github.com/rebolek/gritter/blob/master/gitter-tools.red#L209
greggirwin
22:29Nice!

bubnenkoff
08:38How to change type during set?
parse [foo:] [set x set-word!]

now x become set-word! but I want to make it simply word!
08:39is it good:
parse [foo:] [set x set-word!  (x: to word! x) ]

?
Oldes
08:49Yes... that is good. Question is, it you need to convert it.
08:52Do you know, that you may use any-word! and any-word? in many situations?
bubnenkoff
08:55Thanks for tips! I did not know about them!
10:35With great help I mixed all examples and wrote interesting code. It can advance value to nested blocks. It check it parent name and insert value if it in list. The only problem I can't find issue why it do not process top level item: 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 data
10:58fix: if (not unset? set-word-name) --> if (value? 'set-word-name)
toomasv
12:50@bubnenkoff Good work, congratulations!
Here is a bit reorganized rule that works for lots too
set-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
    ]
]

Con you point out now why your rule didn't work for lots?
bubnenkoff
13:17thanks! That's very funny that my last version is very similar with your (I dropped a lot of aheads), but my did not worked because few small but important difference!) Thanks!
18:39I want to do something like this. It should be possible, but I do not understand what I am doing wrong:
needle: 'id
parse [id: 1] compose [(to-set-word needle) integer!]
hiiamboris
18:40use probe to find out
bubnenkoff
18:42Probe show that needle was right evalauted, but result of parsing is false
hiiamboris
18:44what do you think id: means for parse in it's rule?
bubnenkoff
18:45Oh, it seems that quote should be used: parse [id: 1] compose [quote (to-set-word needle) integer!]
hiiamboris
18:45exactly :+1:

bubnenkoff
09:18Why Insert do not insert noting in follow code:
a: [foo: 123]
parse [a] compose [quote (needle) set x integer! insert [id: (x)] ]
a
hiiamboris
09:24[a]?
bubnenkoff
09:41Yes, [a], what's wrong?
hiiamboris
09:55that's a block with one word!
bubnenkoff
09:57"with word and value" right?
hiiamboris
09:59nope
bubnenkoff
10:01oh my bad! Brackets!
10:47This code is working:
needle: 'id
a: [id: 123]
parse a compose/deep [ quote (to-set-word needle) set x skip ]
x
>> 123


But here I am getting 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

hiiamboris
10:57([(print x)])
10:59or use https://gitlab.com/hiiamboris/red-mezz-warehouse/-/blob/master/reshape.md better suited for that
toomasv
11:02Or try to wrap your head around this convoluted example:
parse a compose [ 
    quote (n: to-set-word needle) 
    set x skip (quote (print x)) 
    insert (to-paren reduce ['compose reduce [n quote (x)]])
]
bubnenkoff
11:04Oh magic! I do not know how I would read it after few weeks after writing)))
toomasv
11:08(Corrected example: quote (x) in the end)
bubnenkoff
11:09this work for me:
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]
toomasv
11:11Did you try it in fresh console? ;)
bubnenkoff
11:17hah! In fresh do not work)
toomasv
11:20What do you think compose/deep [[(reduce [(x)])]] would do?
bubnenkoff
11:25at result it will try to evaluate x
11:26I checked and it will print: [[1]] is x is setted
toomasv
11:29But you need not to evaluate x at compose-time, only at parse-time...
bubnenkoff
12:47Still hard to realize this magic.
So:
needle: 'id
a: [id: 123]
parse a compose/deep [ quote (to-set-word needle) set x skip insert [ (to-set-word needle) ([(x)])  ] ]

output:
[id: 123 id: (x)]

so I need compose it's once again to get result, right?
12:50But honestly I am not understanding why: ([(x)]) is evaluate to (x). it should be: [x]
hiiamboris
12:53
>> append [1 2] ([(x)])
== [1 2 (x)]
toomasv
12:59> I am not understanding...

Imagine it this way that compose/deep stops going deeper if it meets block after (some consecutive) parens
>> compose/deep [((([((x))])))]
== [((x))]

But there is another problem in your rule - insert will not evaluate block, but you need to evaluate (x).
bubnenkoff
13:15So insert and append evaluate block that inserting/appending? Because this:
>> append [1 2] ([(x)])
== [1 2 (x)]
toomasv
bubnenkoff
13:17then where [] ?
hiiamboris
13:18
>> append [1 2] [3 4]
== [1 2 3 4]
>> append [1 2] ((([3 4])))
== [1 2 3 4]
>> ((([3 4])))
== [3 4]
bubnenkoff
13:26I used not correct word. Not evaluate, but "right part is value or block content". Right?
hiiamboris
13:27more or less
toomasv
13:29I would add "or a result of evaluation"
>> append [a b](1 2 3 'c)
== [a b c]

Which is some kind of value of course
bubnenkoff
14:42@hiiamboris "This mezz was made to counter the limitations of COMPOSE." so is there any plans to improve compose? I look at your code too, but it look like black magic for me. How to say in human words what limitation is? Something like: "ability to not evaluate impty values"?
hiiamboris
14:54you've just encountered that limitation yourself
bubnenkoff
14:58So in theory there is should be something like compose/lazy that do not try to evaluate value if it has no value?
hiiamboris
15:04https://gitlab.com/hiiamboris/red-mezz-warehouse/-/blob/master/reshape.md#an-overview-of-the-previous-designs first part is dedicated to the limitations of compose
second point is about your problem:
> It \[compose\] uses parens, so if one wants to also use parens in the code, code gets uglified.

You want some parens to be ignored by compose above, otherwise you uglify your code with ([(..)]) thing
15:06reshape ignores () and only processes @() and !(), thus helping you
15:08but your last x is of course available only after parse, so you'll have to resort to compose again there

bubnenkoff
11:53Hot to check if 1 is next to set word?
>> parse [a:] [set-word!]
== true
>> parse [a:1] [set-word! 1]
== false
hiiamboris
11:55quote it
11:55a:1 is not a set-word though
bubnenkoff
11:57Thank! I got it!

bubnenkoff
09:28word in my case can be:
none
integer!
string!


Is the any common type for them?
rebolek
09:33No, but you can make a typeset:
>> whatiwant!: make typeset! [integer! none! string!]
== make typeset! [none! string! integer!]
>> parse [1] [whatiwant!]
== true
>> parse ["a"] [whatiwant!]
== true
>> parse [#[none]] [whatiwant!]
== true
hiiamboris
09:33until evaluated, it's just words though
bubnenkoff
09:44Why false?
>> parse [a: none] [ set-word! [string! | integer! | none! ] ]
== false
09:45
>> parse [a: 1] [ set-word! [string! | integer! | none! ] ]
== true
09:53though evaluation x as 'none is working:
>>  parse [a: none] [ set-word! 'none ]
== true
toomasv
11:04How many nones are in your input? None.
greggirwin
19:01@bubnenkoff sometimes it can help to use other funcs to explore your data. e.g. foreach ... print type? ...] or a general parse rule that accepts any value and dumps info about it.

bubnenkoff
12:49How to split parsing in two step. On first to process words that have values as value. And on second to process only words with block.
I know about recursion, but I am not sure that it should be used here
parse [a: "Hello" b: [ foo: 123 bar: [ x: "name" ]] c: "world" ] rule: [  ]


12:50I want to process first level, that to move to second and then move to third
hiiamboris
13:03why separate?
bubnenkoff
13:15I want to make from mext structure:

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"
]

Something like this:
[
	[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 ]  ]]
 ]


I started with follow code:


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 result


But when I am going into recursion I am loosing child/parent name


hiiamboris
13:18make a function with arguments: child-name and parent-name, and recurse into it

rseger
21:33am I understanding to 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)?
hiiamboris
21:35Exactly. This is called backtracking.
rseger
21:38excellent, makes sense. Love it when that happens :)
hiiamboris
21:39you can hack it to work your way :)
21:39read this? https://www.red-lang.org/2013/11/041-introducing-parse.html
21:40there's word: an :word things in there of particular interest ;)
21:41but I don't recommend hacking to
21:41well.. if only to later discover how you were making your life harder
rseger
21:42constantly :)
most of the examples seem to assume transparent data. Meaning you can know what's before and after the nugget you're trying to parse. I'm having a hard time translating things for opaque data
21:42learning a ton, but struggling mightily for it :)
gltewalt
21:46I struggle too
hiiamboris
21:46@rseger when stuck, post your code snippet here, someone will offer an advice ;)
rseger
21:51sanitizing data isn't so wildly out of parse's use case that I'm running at a dead end, right? I have a semi-functioning horrific version working with regexes that I can no longer maintain because I can no longer read the gd regexes. Parse is like a goldmine for readability and I'm really hoping I can figure out how to use it this way without losing that
hiiamboris
21:58you'll learn it ;)
21:58sanitizing works just as well as anything else really
greggirwin
22:07Almost anything can be done with 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.

As @hiiamboris if you post sample data and goals, you'll often get help here.
rseger
22:13it has to work against intentionally degenerate data, unfortunately. Both the nugget and the surrounding opaque data are available to the attacker to manipulate in any way they like before it all gets passed through my engine. The data has to come out squeaky clean without destroying the good stuff in the process
23:10bah, my to hack works but it is painfully slow :sob: why you gotta be right @hiiamboris?
23:15I gotta take a break for a bit but if anyone is bored and interested in a concrete example, this works save being painfully slow
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 bad

hiiamboris
08:05Haha. I wonder if this code has O(n^4) complexity or near that (:
toomasv
09:14@rseger Maybe post a small sample of your input and state what you want as an output, so we can hopefully offer some advice how to go about it with parse?
rseger
14:56I don't have a problem sharing it buuut, it's malware. It's inert, in that you would have to copy it into a php file and then have the php engine run it before it was at all harmful, but I can see how it would make a lot of people nervous.
14:57had some ideas in twilight while I was drifting to sleep last night - love it when that happens and hopefully can get that knocked down from O(n^4) (It's definitely somewhere around there)
toomasv
15:53No need for actual code, just something that would satisfy your constraints, e.g. following:
whatever 
$x = $y 
$a = 123 
$b = xyz
$c = $b(blah-blah)
$c();
gnah 
^ $a 
whatever

Should this be sanitized into:
whatever 
$x = $y 
gnah 
^ $a 
whatever

hiiamboris
15:24@pekr I've implemented your idea of a [parse tool](https://gitlab.com/hiiamboris/red-cli/-/tree/master/mockups/parse) (how I imagined it) so check it out ;)
Can we improve it?
15:32I'm also interested in real world use cases if you guys have any
toomasv
15:51@hiiamboris Seems really nice! :+1:
pekr
16:20wut? In just two days? How's that? :-)
16:20I will try to run the examples, thanks for that!
hiiamboris
16:22in 4 hrs ;)
greggirwin
16:23Look at how little code it takes @pekr, and everyone please say what you think of his CLI approach. We've had that waiting in the wings for quite some time.

Nice work @hiiamboris. :+1:

As far as real world use, making parse an option in our own future AWK-like tool would be a superpower.
pekr
16:59The only thing I might be concerned about, is the Red's parse speed. I do remember my R2 old days comparison to the CA-Visual objects lang, which compiled down to the native language. Rebol was faster and everybody was like - how could that be? It's just an interpreter. I do remember some examples with Red, which was lot slower, than in R2, but it is an old story, so I will have to re-try.
17:00@greggirwin I do remember some blog post, talking about the CLI. What was that? Is that some concept, like to create some CLI apps using Red and @hiiamboris just used that?
greggirwin
17:28We can profile it @pekr, what's your worst case scenario? But then, to be fair, we have to show solutions in the faster lang or tool, e.g. the consecutive vowel or mixed case examples. :^)

@hiiamboris wrote this CLI dialect. It's quite different from what I originally proposed, and I admit it's still different enough that until you use it a few times it may seem strange. If you look at the [parse tool code](https://gitlab.com/hiiamboris/red-cli/-/blob/master/mockups/parse/parse.red), you'll see a func called 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.

Look again at the parse-tool func. Look at its spec. Look at the --help output for the app.
17:29The details for it are [here](https://gitlab.com/hiiamboris/red-cli).

rseger
19:43I've been trying to learn to incorporate 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 removals
19:43anyone able to point me in the right direction?
hiiamboris
20:18like this?
>> parse s: "abcdef" [p: 4 skip remove p] s
== "ef"
20:21tried to [remove [bigger rule]]?
rseger
20:22remove here is removing everything from p: to the current index when it's called?
20:23and you can change that current index with :other-spot (assuming it's set, of course), right?
hiiamboris
20:25be careful though that all your indexes after start of remove will move too (relative to the content that's left I mean)
rseger
20:26good to know
20:27(and thank you - that is exactly what I was hoping to learn!)
hiiamboris
20:27I added --write to the Parse tool to batch edit files ;)
20:27With ports it will kill sed ;)
GiuseppeChillemi
Oldes
21:44Can anybody explain what's goin on in this piece of Rebol2 parse code?
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
	]
]
21:45It works in R2, but not in Red (matching by datatype not supported) nor in R3.
21:46It's trimmed from [this Red's compiler function](https://github.com/red/red/blob/master/system/loader.r#L222).
hiiamboris
22:02what do you mean by "matching by datatype not supported"?
22:05ah I see
22:05try changing into blk with ahead any-block! into blk?
Oldes
22:08This is the problematic part: into [some [defs | skip]]
hiiamboris
22:09I don't see a problem there
Oldes
22:10It's the combination of into and skip
hiiamboris
22:11into applies the following rule some [defs | skip] to the path
Oldes
22:13Where happens the:
*** Script Error: PARSE - matching by datatype not supported for any-string! input
9214
22:14@Oldes try this in R2, R3, and Red.
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.
Oldes
22:22AH.. so this is the magic.. parse ["R/S"] [into blk] is false in R2, but error in R3 and Red.

rseger
02:05is there a clean/concise way to tell parse "if you've gone past here, fail"? Using markers, changing them to index integers, and comparing those works it's just not very elegant
9214
02:09
text
>> parse [forbidden zone]['forbidden 'zone [fail] | (print 'fallback!)]
fallback!
== false
rseger
02:10I was thinking more "if you've gone past the next newline, bail"
03:10am I right that [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?
03:19nm - skip is super greedy so it won't work for what I was thinking anyway
hiiamboris
08:28not "greedy" :) skip just means accept any token. Parse rules do not look ahead to see what rule is next.
rseger
17:08fair, still stuck in regex land a bit :flushed:
17:14I just went through a chunk of my code from a week ago. Two things became apparent: I have actually learned a lot, my original cut.. :sob:
hiiamboris
18:15maybe you can start a "Parse for Regex users" wiki entry ;)
rseger
19:02lol. I feel like maybe I should figure that out a little first :)
greggirwin
19:19@rseger look at how much you've done in a short time, even with a few stumbles. Progress indeed.

Even thinking about how we use terms, or are used to their use in use cases where you hadn't had a case to use them, can be confusing. Is 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?

I'm still amazed at how easy 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. :^)
rseger
20:50heh, I've fought that urge a few times "I could just do XYZ with a regex and this would work". Problem is, regexes at this level of complexity are absolutely unreadable (if you can get them to work at all). Like it or not they are simply not an option - I need something more powerful. Parse is that, with an ice cold bucket of new perspective on the side :)
20:58lol, right into an ice bucket
>> parse "abc" [to "b" break]
== false

but the intro to parse page is saying break : break out of a matching loop, returning success.
Is there a new command for that or am I totally misunderstanding again?
greggirwin
21:07> an ice cold bucket of new perspective

Fair warning. I'm going to steal that line. :^)
21:08https://github.com/red/docs/blob/master/en/parse.adoc#break is a better reference. Intro to parse was written a long time ago, and is likely stale in some descriptions.
hiiamboris
21:08@rseger I suggest [raising an issue](https://github.com/red/red/issues/new?template=bug_report.md) about that. 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.

A more in-depth material https://w.red-lang.org/en/parse/ says that for Parse to return true it requires
> Full match of top-level rule and input exhaustion

"Input exhaustion" is what's missing. But to me it looks like a special case where one shouldn't be.
21:09In any case it's worth careful consideration and documenting.
greggirwin
21:11Docs say "Forces enclosing block! rule to instantly succeed. " but only repetition based examples are used. I agree that this needs to be clarified.

The great thing about new people is that they don't know what to tiptoe around, reminding us of traps we automatically avoid.
hiiamboris
21:11True ;)
rseger
21:29:boom: <- my true skillset ;)
hiiamboris
21:32Dangerous! :D
greggirwin
21:35Save that codepoint for function name annotations. We can blow Hungarian Notation out of the water, almost literally.
rseger
21:35to end works, similar to any skip - I was just looking for a more efficient "you're good buddy, thank you"
greggirwin
21:38*Finally* someone who is kind to their constructs. :^)

9214
02:17@rseger, 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.
gltewalt
02:43Why did they decide on returning 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.
02:45I know it's an expression based language, and has to return something
rseger
02:46I appreciate the heads up!
02:48I don't know that it makes sense beyond my use case but a word which halted everything and returned true, just the opposite value but how I assume fail returns, would be cool.
greggirwin
02:49@gletewalt, how can it know when *you* think it's done? If you don't care after finding a match, just add 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?
gltewalt
02:50It knows by Stern Scowl
rseger
9214
02:50@rseger that depends on what you mean by "halted everything and returned true". Do you mean stop parsing and force parse to return true?
rseger
02:50yep :)
9214
02:51Well, the only way to do so is to consume all the input, so I think you know the answer.
rseger
02:53right now, yeah. It's not super efficient when I've got MBs of input left but I already found the thing I wanted
9214
02:54@rseger you can mark the tail of the input in advance and then simply use :mark, for example. Should be marginally faster than combing thru megabytes of data with to or thru.
02:58Or you can rephrase the problem: why do you need parse to return true? You can signal that the thing you wanted is found the other way, e.g. [thing (found?: yes)].
greggirwin
02:58@9214, what's your view on using throw for that case?
accept: [(throw true)]
catch [parse [a b c]['a accept]]
rseger
02:58I really like the sound of that. How do you get the input to the end in order to set the mark:?
9214
02:59@rseger mark: tail input? In pure Red, that is. IIRC Parse docs have some examples that show that approach.
rseger
02:59that's true. Right now I have a really slick little 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 in
03:01looking to an external variable for success/failure could certainly work, and I may need to go that route if that turns out to be a considerable efficiency gain, but using parse's return value directly is really nice
03:03forgive the newbie question but you can set marks in/on input outside of parse?
03:04or is tail a special word in parse?
9214
03:05@rseger no, 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?
03:07Does that make sense?
>> 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
rseger
03:07well, I thought I was :)
evidently there are some subtleties there I'm missing.
03:07let me noodle on that a sec :)
03:13find is returning a pointer into that series, effectively?
:c is inserting that pointer as the current parse index. Then :e is doing the same.
the final skip is eating that last 'e'.
03:13am I reading that right?
9214
03:15@rseger yes, conceptually that's correct. Series is a heterogenous (i.e. it can contain any value regardless of its type) array with boundary checking + an offset (or rather a 1-based index, but internally it's a 0-based offset). Kind like Go's slices, except that the length cannot be modified.
03:17So, in the example above, there's a single array (aka data buffer) a b c d e and two indices into it, c and e.
rseger
03:18wow. Evidently I understood almost nothing about how marks/find, all of it, really works. This is incredibly helpful, thank you!
9214
03:19@rseger [Rebol/Core](http://www.rebol.com/docs/core23/rebolcore-6.html) user guide has some pretty pictures that might help you.
rseger
03:20yeah, those were helpful. What I didn't put together was their relationship with parse. I thought the two were entirely independent ways of working with series
03:21seems I can do some pre-processing on the series before entering parse and (conceptually) pull that information in during processing. Amazing.
03:22is there a way to have parse process the series in reverse?
03:23conceptually that might be hard for me to wrangle right now but it would be incredibly useful for my use case
9214
03:25Well, you'll have to either reverse the input and write all the rules in Da Vinci's mirror writing, or do a lot of scaffolding.
03:26There was some talk in the past about adding extra modes to Parse (reverse, Packrat, etc), but no one knows when it will happen, or happen at all.
toomasv
06:16@rseger I use backward parsing a lot. Here is main logic of it:
>> 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
08:03Here is a bit more involved example of finding needle in possibly nested block by backward parsing:
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]
hiiamboris
08:46this is highly inefficient though, you would be better off with find/reverse
09:36@toomasv for the record, what's your use case(s)?
toomasv
09:39I use backward parsing with draw blocks (which can be deeply nested) to match and extract or edit latest (i.e. visually front) shapes or transformations, often identified by mouse offset only.
The above snippet was just made up. I don't think I've used exactly same form before.
rseger
14:45back? 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?

more importantly, thank you!
toomasv
15:08@rseger look at ? skip
15:12
>> bin: tail append/dup #{} #{00} 1000
== #{}
>> head? skip bin -1000
== true
rseger
15:17you can skip negative numbers?!
toomasv
15:17Sure
rseger
15:17so much love
hiiamboris
15:18It looks like some people come here not because of Red, but because of Parse alone ;)
@rseger how did you find out about Parse?
rseger
15:20somewhat, kind of - yeah, very guilty. Happy coincidence is that I'm really digging Red as well. I realized the other day that I very rarely have syntax errors while coding Red and that's quite unusual for me, particularly when learning a new language.
hiiamboris
15:22So you've been digging Red a bit and realized Parse is what you need and started digging it instead?
rseger
15:22I ran into the limits of regex, thought about creating my own one-off solution, and remembered that there's no way in the world I'm the first person to have this problem. Took some aggressive googling but I did finally find an alternative. Originally my goal was effectively just a subset of regex with great readability. I found a superset, just need to totally upend the way I look at the problem, lol
hiiamboris
15:23I see. Do you happen to recall what webpage told you that there's Parse? ;)
15:24I'm just curious because like you say I imagine it'll require some really aggressive googling to find it..
rseger
15:24I solved 50% of this problem (it was 100% then, the problem is ever-evolving) about 6mo ago with pure regex. When I returned to pick up that extra 50% I realized I was going to have to re-write the original regexes because I couldn't read them anymore (and I'm fairly experienced with regexes)
15:25let me see if I can't recreate the searches real quick
15:25it definitely wasn't a "oh, here's your perfect answer" situation :)
hiiamboris
rseger
15:39alright, here's the high level. I'm not sure whether google's algorithm is lying to me now or not. I recall digging through 10s of pages of results for the last step and now it's only the fourth down but ultimately, here's the ark
search "alternatives to regex" -> "regex is the best, you're just dumb" and "somebody made an ABNF parse in javascript".

search "BNF parser" -> 100's of effectively specialized solutions similar to what I was considering making.

search "alternatives to regex" -> chase the results. This sounds quick but I read through a lot of garbage to finally get to this nugget You 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 :)
hiiamboris
15:44Thanks! That's quite a lot to skip thru to finally find what's you're looking for..
I imagine Parse being useful on it's own should have more active advocacy agenda (something for @greggirwin to consider).
Kinda speaks though, of a power of a language at whole, if just one small dialect of it draws so much attention.
15:44@bubnenkoff should you come around, please tell us how did you discover Parse? ;)
rseger
15:47Every new language is always looking for its "killer app", most of them don't really have anything except novel syntax and making XYZ for the specific problem(s) they were designed around easier. Since those problems technically can be solved in any other language, the new language basically just languishes at the fringes. I love them because they represent new ways of thinking about problems but that's as far as I get with most.

Red has the "killer app" already, parse is ridiculous. Just needs some marketing :)
hiiamboris
15:57That's what @pekr says too. And what the experiment of [Parse tool](https://gitlab.com/hiiamboris/red-cli/-/tree/master/mockups/parse) is all about. But I guess we need to gather some real world use cases to make the tool solid. I don't really have anything myself except occasional type file | find "string" filter.
greggirwin
16:00Thanks for the info @rseger. Helpful indeed. And yes, @hiiamboris, we need more parse advocacy, which is one of the goals of https://www.redlake-tech.com/products/diagrammar-for-windows/.
rseger
16:59the other thing that would be really helpful, something I hope to help with, is a more robust set of string-centric examples. A lot of the more robust examples are very focused on block parsing. It's certainly possible to do the translation but much harder when coming from a regex background.
greggirwin
17:00Agreed.
hiiamboris
17:02FWIW these pages have some potential for highlighting Parse and Red :
https://rosettacode.org/wiki/Category:Text_processing
https://rosettacode.org/wiki/Parsing
And others there... Red is often missing or contains solutions without wow-effect.
greggirwin
17:05We could make notes specifically for that on https://github.com/red/red/wiki/Advocacy.
17:06And put things here too: https://github.com/red/red/wiki/Parse-Cookbook
bubnenkoff
18:36Is the re
> @bubnenkoff should you come around, please tell us how did you discover Parse? ;)

I will answer tomorrow.
hiiamboris
18:37:+1:
bubnenkoff
18:38I need to check if data have some values that are not blocks. For simplification I check 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.

Is there any ideas how to do it in better way?
hiiamboris
18:41why not just parse [aa: [] cc: 1 bb: []] [to integer! to end]?
18:45or find [aa: [] cc: 1 bb: []] integer!

bubnenkoff
08:12> @bubnenkoff should you come around, please tell us how did you discover Parse? ;)

Be honestly I knew somthing about Rebol from Kaj de Vos who was co-developer of Syllable. I take part in it's community, but at that time I was not programmer. After long time I become programmer, and re-discovered Red. For few years I have not time\task for it. And I started from Parse. It's take around 2-3 months to getting *minimal* experience. With incredible help from community. And it take for me around one year before I was able to write some more o less working. It took a long time, but algorithms have never been my strong suit. So only every day practice.
hiiamboris
08:38Aha. Thanks.
GalenIvanov
08:58Speaking of alternatives of regex, has someone tried [Bracmat](https://github.com/BartJongejan/Bracmat)? It's about pattern matching in general, not only in text :
08:58> Bracmat is a computer programming language designed for analysis and manipulation of complex data, be it evaluation of algebraic expressions, parsing natural language, validation of HTML or automatic chaining multi-facetted webservices into a workflow. In several projects, Bracmat has been combined with other programming languages to take care of high level activities that otherwise would be very time consuming to implement.
09:02I spent some time reading the docs, but the terse symbolic notation turned out to be beyond my grasp and my array languages experience.
rseger
15:45this quote 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"

that said, I was saddened to see it. Their goals and ideas seem to be very solid. I just wish they were in a more maintainable package.
hiiamboris
16:28Perl is [one of the best](http://99-bottles-of-beer.net/language-perl-737.html). I wonder if such code is possible in Bracmat.
rseger
17:19it truly is a marvel
GalenIvanov
17:49@rseger Yes, I agree!
17:51@hiiamboris I don't know Perl, but I think Raku (formerly Perl 6) have taken steps in the right direction - at least the (golfed) code I have seen in Code Golf & Coding Challenges on StackExchange looks much better than Perl.
greggirwin
18:10That Perl stuff is astounding. True artistry. Bracmat is an interesting find. Skimming the docs, it brings to my mind how important foundational principles are. His are quite specific and lead to design choices and a user experience that make it interesting (e.g. no subtraction, no named fields). If it empowers people, that's what DSLs are for, but it isn't clicking for me at a glance.

There's an informal grammar in the PDF, but I can't copy it out to see how hard it might be to normalize. Not worth downloading and trying locally in PDFElement right now. If @GalenIvanov wants to reach out and see if the author can use the free version of DG to make a diagram for it, we'll render him one without watermarks.
GalenIvanov
18:26@greggirwin That's an interesting idea - yes, I'll try to contact him with your suggestion.
greggirwin
18:44:+1:
toomasv
19:51Bracmat grammar (for visualisation only):
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"
greggirwin
19:55Thanks @toomasv !
toomasv
greggirwin
19:59[![bracmat.png](https://files.gitter.im/5b147b8fd73408ce4f9bc4b4/JfTY/thumb/bracmat.png)](https://files.gitter.im/5b147b8fd73408ce4f9bc4b4/JfTY/bracmat.png)
20:00@GalenIvanov feel free to pass that along to the author. If they want to use it, ask if they'll give us credit and point to https://www.redlake-tech.com/products/diagrammar-for-windows/. You can send them the working grammar from @toomasv as well.
hiiamboris
20:31the image doesn't zoom for me (says files.gitter.im inaccessible, but ping works) :/
greggirwin
23:10Technology is hard it seems. :^\

cloutiy
14:46Is diagrammar available for linux?
greggirwin
17:40Not at this time.

bubnenkoff
18:38Could anybody help me to adopt this code to output result in single quotes: like: '.
I tied to write big hack with 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]


The result should look like as SQL insert VALUES. They are single quoted.
greggirwin
18:43You should just be able to add the single quotes at the head and tail of each string.
bubnenkoff
18:44Do not understand moment where it should be added(
greggirwin
18:48I don't remember SQL rules for embedded quotes. What should the "some string..." look like when done?
bubnenkoff
18:48VALUES(1 'aa', 'some "name"', 123, NULL)
the quotes should look like this
greggirwin
18:52If just enclosing each string works, this should do it:
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]
bubnenkoff
18:53Wow! Thanks! I need to check it on real data!
19:00
block-to-comma-list ["25.22.11" "aa aaa aaaaa" "1.00" none]

== "('25.22.11', 'aa aaa aaaaa', '1.00', )"


it loosing none (that should be renamed to NULL)
greggirwin
19:02So you'll need to add a rule for | none! as an alternate. I missed that when changing to the string! check.
19:02Then you can remove your last replace call.
bubnenkoff
19:04I put none! in wrong place?
rule: [collect any [set s string! keep (enclose s {'}) [end | keep (", ") | none!]]]
19:05After one year this string is look magic for me(
greggirwin
19:07Well, you're not doing anything when you see the none. You want to keep NULL in that case, right?
bubnenkoff
19:09yes
19:09To get:
== "('25.22.11', 'aa aaa aaaaa', '1.00',  NULL)"
greggirwin
19:14So what do you think you need to do when you hit the none! rule? (hint: look at the string! rule)
bubnenkoff
19:19I tried different combination of keep none! and keep 'none do not work. Rule hard for me
greggirwin
19:29
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]
bubnenkoff
19:36It do not add , 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]
greggirwin
19:38I leave the details to you. :^)
bubnenkoff
19:38Ok! Thanks!

bubnenkoff
08:16@greggirwin It's seems that I did it. The yesterday rule was to hard for me and I was not able to fix it. So I did next:
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 ")"

output:
== {(NULL, '25.22.11', 44, 'aa aaa aaaaa', 'some string with quotes "Microsoft", "Apple", "HP"', '1.00', NULL)}
toomasv
08:52@bubnenkoff
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)}

GalenIvanov
08:54@toomasv I was wondering isn't it possible to change head insert next copy "()" with mold to-paren?
08:56Apparently not in this particular case :smile:
toomasv
08:58As to-paren loads string, so yes, i.e. no.
GalenIvanov
08:58:+1:
Oldes
09:03@toomasv your solution will fail once there will be some string like "it's fail"
toomasv
09:06@Oldes I'm only giving pointers, not trying to develop THE solution :)
greggirwin
20:00@bubnenkoff *not* using parse is a great option at times.

ldci
17:11Hello everybody: looking for a parse rule for deleting punctuation marks in a text in order to keep only words.
GalenIvanov
17:38@ldci Here is some simple rule (you can add another punctuation marks to punct):
17:39
punct: charset ".,;!?"
remove-punct: [any [remove punct | skip]]
text: "This, that? No! Some ... "
parse text remove-punct
text
== "This that No Some  "
ldci
17:49@GalenIvanov Thanks a lot!
GalenIvanov
17:50@ldci You're welcome! Don't forget that it changes the source string in place.
ldci
17:51@GalenIvanov Better. than using trim/with. Perfect:)
GalenIvanov
17:52:+1:
ldci
17:59[![image.png](https://files.gitter.im/5b147b8fd73408ce4f9bc4b4/REQ1/thumb/image.png)](https://files.gitter.im/5b147b8fd73408ce4f9bc4b4/REQ1/image.png)
GalenIvanov
18:04@ldci Nice! Is this part of a bigger project?
ldci
18:07@GalenIvanov Nope. Just testing Red. BTW I really appreciate your contribution for L-System and graphics stuff:)
GalenIvanov
18:09Thank you! I hope there will be more to come :smile:
ldci
18:10@GalenIvanov We have to connect your work and redCV. :)
GalenIvanov
18:14Yes, I need to get familiar with redCV and stop reinventing things :smile:
ldci
18:17@GalenIvanov Please feel free to contribute. You’re welcome.
GalenIvanov
18:24@ldci Thanks, I'll be glad to contribute!

ldci
06:46@GalenIvanov Solution with trim is 2x faster than with parse. Probably because with parse this is char by char.
GalenIvanov
06:55@ldci I don't know why - I'm not aware of the implementation details. Btw, have you tried remove-each?
06:57
>> remove-each c text[find ".,;!?" c]
>> text
== "This that No Some  "
ldci
06:58@GalenIvanov I’ll test :)
08:24@GalenIvanov 3 methods tested: trim, remove-each and parse. They are equivalent: less than 150 ms to count 2615 words in classical Cinderella text.
GalenIvanov
09:14@ldci Thanks!
greggirwin
19:31Trim is low-level R/S code, which can spin through strings as characters very quickly. You trade speed for flexibility.
dsunanda
19:51@ldci Stupid trick possible with TRIM - specify only the characters you want to keep. Everything else gets removed:
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"
ldci
23:49@dsunanda Very elegant!

ldci
00:34I’m really impressed about string processing with Red: powerful and elegant
trim/with aTxt rejoin [",.;:!?-(){}'" form #"^ » »]
00:34Fabulous
abdllhygt
14:39hi!
which one is logic?
a: complement charset "<>"
b: ["<" copy a to ">"]
14:40
a: complement charset "<>"
b: ["<" copy a ">"]
rebolek
15:19second one. if you have charset, you don’t need to. But it would be copy some a.
abdllhygt
15:31@rebolek thank you!
21:21
any [
            !satır
            | !işaret
            | !işlev
            | !an
] ;any

is this enough to make loop?
21:21or, do i have to add this:
script: any [
             !satır
            | !işaret
            | !işlev
            | !an
            | script
] ;any
greggirwin
22:28You don't have to name rules unless you want to reference them by name, e.g. from multiple locations.
abdllhygt
22:33i didn't understand, can you give me an example?
greggirwin
22:47parse series [any ['a | 'b | ['satir | 'an]]] vs
my-rule: ['satir | 'an]
parse series [any ['a | 'b | my-rule]]
abdllhygt
22:53i see, but i was talking about putting a block into itself
22:54
blok: [1 | 2 | 3 | 4 | blok]

like a loop
i was asking that do we still need this if we use any. but now i know the answer, thank you! @greggirwin
greggirwin
23:03Yes, if you want to use a rule recursively you have to name it.

abdllhygt
20:58hello!
20:59how to use parse/trace? i was using it before, but i couldn't use now.
21:02ah it is parse-trace, i really forgot. long time i don't write Red.
21:08is there a better method for reading result of parse-trace?

gltewalt
21:17parse-trace is a wrapper for parse/trace, you can view the source and craft your own - source parse-trace, or modify parse/trace itself.
Depends on what you mean by a better method for reading result
21:30You can play with this, but I haven't touched it in weeks. Use a nightly build for your Red version.

https://github.com/gltewalt/parse-caddy
gtzip
23:54If parse-trace output is too long, there is a /part refinement that may be all you need to play with

abdllhygt
18:10thank you

giesse
09:54Bug?
parse ['keep/only] ['keep/only]
09:54(returns true)
09:54(there used to be the same bug for lit-words)
hiiamboris
13:26The bug was never fixed, so it likely affects paths too.
13:27If you wish to leave a comment: https://github.com/red/red/issues/3029 https://github.com/red/red/issues/3554

GiuseppeChillemi
21:54Could Parse in Block catch carriage returns?
22:31*Parse for Blocks
greggirwin
22:34Yes.
blk: [a b 
c d
e f]
parse blk [
	some [
		mark: (if new-line? mark [print mold mark])
		skip
	]
]
GiuseppeChillemi
22:37Ok, it is needed for data rows ending with new-line, thank you.

ne1uno
21:54.gse python for
21:54^H^H^H

greggirwin
22:07For reference, on binary parser work: https://project-everest.github.io/everparse/

XANOZOID
20:34> not rule : invert the result of the sub-rule.

By result, does it mean it makes a "true" match reject??
20:34I was expecting something like parse blok [ any [not word!]] to succeed on any block that has no words
20:34But it just fails
20:36[![image.png](https://files.gitter.im/5b147b8fd73408ce4f9bc4b4/8Ymk/thumb/image.png)](https://files.gitter.im/5b147b8fd73408ce4f9bc4b4/8Ymk/image.png)
20:37the one that passes clearly shows there's only numbers and spaces . . . so why not pass when there's anything that isn't a word?
hiiamboris
20:38https://w.red-lang.org/en/parse/#not
XANOZOID
20:40Okay so based on that, this should pass as true
parse [1 2 3 4] [any [not word! | skip]]
But it does not
hiiamboris
20:40not word! succeeds
20:41any stops because it doesn't advance
20:41end of parse block, input is not exhausted
20:41=> false
20:42https://w.red-lang.org/en/parse/#any
greggirwin
20:42The key being that not (or ahead) never advance. Think of them as peeking ahead.
XANOZOID
20:46Thanks guys! Also nice to see there's a docs page now; I've just been referring to the blog post. I guess I was thinking a "true with no pass" would attempt to use the other rules but that wouldn't make sense - this helps!

lylgithub2021
01:27I want to convert "%abc.txt.def.txt" to "%abc.txt.def" — that is to say, only keep the part before the last dot. How can I achieve

it by parse? Or in common, how to get part of content before the last given symbol(here is dot in this example)?
dsunanda
01:46@lylgithub2021 Here's one way:
str: "%abc.txt.def.txt"
copy/part str find/last str "."
== "%abc.txt.def"
lylgithub2021
01:54@dsunanda Many thanks. According to your code find/last str "." which gives ".txt", then `copy/part str ".txt" gives wrong message, why?
dsunanda
02:00@lylgithub2021
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
lylgithub2021
02:06How does 13 comes since find/last str "." only returns ".txt"?
dsunanda
02:14find/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.
Series can take a while to get the hang of.
Red's daddy - Rebol - probably still has the better documentation for explaining it -- see 7.5 here, for example:
http://www.rebol.com/docs/core23/rebolcore-6.html
lylgithub2021
02:27The above reference seems not explain why the result of find can be regarded as length that can be used as argument of other function(i.e. copy).
dsunanda
02:36It may help to realize that str: "%abc.txt.def.txt" and xxx: ".txt" are completely separate series.

So 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 str
str: "%abc.txt.def.txt"
 xxx: ".txt"
 index? find/last str "."
== 13
index? xxx
== 1
lylgithub2021
02:52But 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?
dsunanda
02:54I'd agree that's confusing - it's behavior copied over from Rebol, and it may take a deeper expert to explain why it is the best, rather than a mistake..... Might want to ask again in the Help room -- we're off topic for parse.
lylgithub2021
03:06Many thanks again!! Then, is there a way to achieve it by parse?
gltewalt
03:21Because find/last returns . through the end of the string. Effectively takes it, or "chops it off"
03:23
>> str
== "%abc.txt.def.txt"
>> find/last str "."
== ".txt"

03:24>> copy/part str find/last str "." is telling it to copy up to ".txt"
03:28Including index? will give the latter result.
copy/part str index? find/last str "."
lylgithub2021
04:25index? 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.
toomasv
04:37If you give a number, it is interpreted as offset (i.e. length to be copied), but result of find (i.e. same series with different index position) is interpreted as target position until which to copy.
greggirwin
05:32The doc string for /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"

lylgithub2021
07:04 I see. Thank you @all
07:13So, here, how to achieve it by parse?
rebolek
07:17@lylgithub2021 for example:
file: %abc.txt.def.txt
rejoin parse file [collect some [keep to #"." ahead [skip to #"."] keep skip]]
07:20If you don’t want to use rejoin, collectdirectly into string!:
out: ""
parse file [collect into out some [keep to #"." ahead [skip to #"."] keep skip]]
toomasv
08:30Some more:
; 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"

Oldes
12:26
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
12:30@lylgithub2021 when the series is used for the _part_, than it is zero-based index and it makes sense. You want s: "abc" copy/part s s to be "" and not "a".

lylgithub2021
00:50Thank you all. I'm learning them.
gltewalt
02:46lol
>> reverse remove/part reverse str 4 
== "%abc.txt.def"
lylgithub2021
06:39Why do codes with 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

When I compiled this script by red -r -t MSDOS fortestparse.red, it faild with wrong message: undefined word aa. Why and how to deal with it?
rebolek
06:40insert a line aa: none before the parse. Compiler can't determine words from parse, they need to be initialized manually. Interpreter is smarter in this case.
lylgithub2021
06:41Is it a bug of compiler?
rebolek
06:42I won't say "bug". Maybe a missing feature?
lylgithub2021
06:43Does that mean all word used in parse has to be preclaimed when compiling needed?
06:46Any help Doc mention this?
rebolek
06:47Yes, you need to define all used words. There may be a list of compiler limitations in the docs, I'll take a look.

hiiamboris
16:17More targeted repost from red/red:

Hi! If you feel like expanding mental horizons, here's an experiment I'd love feedback on.

## [MORPH DSL - A dialect for efficient local series conversion](https://gitlab.com/hiiamboris/red-mezz-warehouse/-/blob/master/morph.md)

A few teaser examples (more on that page):
>> 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"

rebolek
05:54Nice :)

justinthesmith
16:03Hey folks, I've run into a problem with unexpected case sensitivity with parse in Red 0.6.4:

parse [a] ['A]

This returns true in Rebol 2, Rebol 3, and older versions of Red (0.6.3). But Red 0.6.4 returns false.

This is problematic as it's preventing construction of a case insensitive but case preservative dialect using block parsing mode. I've not enabled case-sensitive mode with the /case refinement. Any idea what's up?
rebolek
16:25@justinthesmith sounds like a bug
ne1uno
20:44@justinthesmith try with latest, 064 is old
justinthesmith
22:53I get the same unexpected case-senstivity with latest, Red 0.6.4 for Windows built 13-Nov-2021/2:17:19-08:00 commit #5f09829
Where do I file a bug report?

greggirwin
16:14https://github.com/red/red/issues
16:17See also: https://github.com/red/red/issues/3554 and https://github.com/red/red/issues/3029
justinthesmith
17:09Thanks! Will file an issue.

One interesting thing that's arisen looking back, is that the /case refinement in Rebol's parse doesn't seem to apply in block parsing mode. For example, this is what I would expect:
>> parse/case [a]['A]
== false

This returns 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?

hiiamboris
07:38Of course it should.
justinthesmith
17:47Thanks for the comment on the ticket, @hiiamboris . I was hoping Red could solve problem with a dialect, but I'm not sure my vision is possible with this bug.
hiiamboris
17:59Haven't I provided you with a workaround?
18:00I don't see how that can impair your dialect ;)
18:00Just produce the rules that work.
justinthesmith
19:55The goal is to produce a dialect that's human readable, human writable, and human maintainable. "Just produce the rules that work" is easier said than done--and seems to stand in sharp contrast with the philosophy of building the the language to suit the problem.

Consider this dialect example:
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 rule

All three tests are true in Rebol, but test2 and test3 return false in Red with the case sensitivity bug in parse.

Rewriting the rule with your tip:
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!]
]

Now all three tests are 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.

To me, this bug is a critical issue as it severely hinders one of the key features of the language.
hiiamboris
19:58Ah, so them others would need to modify your dialect then. I see.
19:59Well, you can fix the bug and PR the solution then ;)
20:06https://github.com/red/red/issues/3029#issuecomment-436438710 here it was reported as a regression
20:07here's commit that caused it https://github.com/red/red/blame/5f09829edc80342c70b65f69defc4d05c2d081b8/runtime/parse.reds#L190
20:08And there's a better workaround for you: quote buy | qoute sell
justinthesmith
20:09Thanks for finding that! I'll take a look
greggirwin
20:09Words should not be treated case sensitively by default. That's the main bug. Whether /case should apply to them is separate. It is supported elsewhere.
justinthesmith
20:18Makes sense to me, Gregg.

I have a few old builds saved locally prior and confirm things worked as expected prior to that commit @hiiamboris referenced. I don't have any experience with Red/system but please let me know if there's anything else I can do to help address this bug.
greggirwin
20:26:+1: This is probably one we want the core team to tackle.

bubnenkoff
10:17I need to extract letter from string if digits. I tried to use 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
rebolek
10:19First, you are parsing a string, so it doesn’t make sense to put it into block, just use string parsing.
10:19Then it’s pretty straightforward.
10:21
>> numbers: charset "0123456789"
== make bitset! #{000000000000FFC0}
>> parse "81128A24826" [collect some [keep numbers | skip]]
== [#"8" #"1" #"1" #"2" #"8" #"2" #"4" #"8" #"2" #"6"]
bubnenkoff
10:24So I should do something like:
>> parse "81128A24826" [collect some [ numbers | set x skip ]]
== []
>> x
== #"A"

To extract letter
rebolek
10:24either that or you can turn it around and define letters charset
dsunanda
10:26Just for variety, a non-parse way:
trim/with "81128A24826" "0123456789"
== "A"
rebolek
10:26:+1:
bubnenkoff
10:38hah! Cool!

lylgithub2021
02:45A string like this: "a.b.c.d.e.f.g"(the number of "." and the content of string are both unknown in advance). How to get "a.b.c.d.e.f" by parse? That is to say, all things before the last dot.
toomasv
04:42If it should always get string just before the last dot, then e.g.:
head clear find/last copy "a.b.c.d.e.f.g" dot
== "a.b.c.d.e.f"

If it is rather everything until 6th dot, then maybe:
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"

Or with parse:
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"
05:19With hypothetical new splitit might be
first split "a.b.c.d.e.f.g" [last dot]
== "a.b.c.d.e.f"

Or maybe
first split/last "a.b.c.d.e.f.g" dot
== "a.b.c.d.e.f"

Which would you prefer?
lylgithub2021
05:49Many thank! @toomasv ! THe version of ypothetical new split is more readable. When is it usable in Red?
07:20I think 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?
toomasv
07:41You are welcome!

> If I need to store the returned value... what shall I do?

Use set-word. You can record both tail of your string and head of it, e.g.
head-of-string: head tail-of-string: clear find/last copy "a.b.c.d.e.f.g" dot

Or, if you don't need it at once, but later, you might set a word to it at once when copying:
clear find/last str: copy "a.b.c.d.e.f.g" dot 
...
str
== "a.b.c.d.e.f"

lylgithub2021
13:06I have a string whch contains some numbers. How can I get all numbers from the string(and all strings in the string). For example, str: " 1.5yuan ^/ 3 yuan ^- -2.3dollar"
hiiamboris
13:16use [Parse](https://www.red-lang.org/2013/11/041-introducing-parse.html)
toomasv
13:17
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"]
pekr
13:20
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])
   ]
]

My code is surely complicated and not efficient in comparisong to parse gurus here :-)
toomasv
13:29Also
parse str [any [digit not digit insert space | skip]] new-line/all load str no
== [1.5 yuan 3 yuan -2.3 dollar]

lylgithub2021
02:22Good solutions! Thank you all.
03:07@toomasv I modified your first method with this:
>> parse trim/all str [collect any [keep some digit | keep to [digit | end]]]
== ["1.5" "yuan" #"3" "yuan" "-2.3" "dollar"]

Then I tried to change #"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?
toomasv
05:56@lylgithub2021 No, I did not load a char, bc it cannot be loaded. You can load strings.
07:28That's why I used 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:)
07:42You can use keep copy _ some digit in your rule to get all elements as strings.
lylgithub2021
13:49Thank you @toomasv so much!
toomasv
15:33You are welcome!