Search code examples
parsingrebolred-lang

How to use `parse` to get all lines with all search strings in Red language


I am trying to extract from a list of lines which have all of the strings from a list of search strings. I am trying following code using and keyword (as mentioned on http://rebol.com/r3/docs/concepts/parsing-summary.html ) to include only those lines that have all strings of srchstr block:

lines: [ 
    "this is first (1st) line"
    "second line"
    "third line"
    "this is second sentence"
    "first line or sentence"
    "third sentence"    ]

srchstr: ["line" "first"]

probe parse lines [and reduce srchstr]
probe parse lines [and srchstr]  

There is no error message but the output is only:

false
false

I also tried:

foreach line lines 
    [parse line [and srchstr]
        (print line)]

But this prints all lines.

Desired output is:

    [ 
    "this is first (1st) line"
    "first line or sentence"
    ]

It will be obvious from these codes that I am very new to this, though I have tried to read about it.

Where is the problem and how can I get the desired output?


Solution

  • To match whole words, both versions will use a parse pre-pass to extract said words:

    extract-words: func [series [string!]][
        letters: charset [#"0" - #"9" #"a" - #"z"]
        series: parse series [collect any [keep some letters | skip]]
    ]
    

    Using FIND

    This creates a block of find results and confirms compliance with all:

    contains-all: func [series [string!] words [block!] /local word][
        series: extract-words series
    
        all collect [
            foreach word words [
                keep find series word 
            ]
            keep true
        ]
    ]
    

    Then you can loop through your strings to find matches:

    collect [
        foreach line [ 
            "this is first (1st) line"
            "second line"
            "third line"
            "this is second sentence"
            "first line or sentence"
            "third sentence"
        ][
            if contains-all line ["line" "first"][keep line]
        ]
    ]
    

    Using PARSE

    This version creates a parse rule that will match against sorted words:

    contains-all: function [series [string!] words [block!]][
        parse sort extract-words series collect [
            foreach word sort copy words [
                keep 'thru
                keep word
            ]
            keep [to end]
        ]
    ]