r3wp [groups: 83 posts: 189283]
  • Home
  • Script library
  • AltME Archive
  • Mailing list
  • Articles Index
  • Site search
 

World: r3wp

[Parse] Discussion of PARSE dialect

Anton
5-Sep-2010
[5137]
rule: [ "text" (var: "local") ]
var: "global"

f: func [ /local var ] [var: "funclocal" parse "text" rule  return 
var ]
f ;== "funclocal"
var ;== "local"
Ladislav
5-Sep-2010
[5138]
It is quite hard to decipher what actually Micha meant, I suppose, 
he wanted this?

rule: ["text" (var: "local")]

var: "global"

f: func [/local var] [parse "text" bind rule 'var return var]
Micha
5-Sep-2010
[5139x2]
ok thanx
this code  good works
Anton
5-Sep-2010
[5141x2]
Nicolas, I refer to your first example.

The error is that FUNC binds the words in its body block to its context, 
but this binding does not extend to reaching inside the block referred 
to by the RULE word.

This error might have arisen because of a small typo, parsing "text", 
not "test", which RULE matches.
("arisen" -> "survived")
BrianH
5-Sep-2010
[5143x4]
Fork, I didn't know about the paren-in-a-block form of the DO parameter. 
That is weird. I can't figure out why that form would be supported 
- it wasn't in the proposal.
Is there a foundational reason for DO not being available for string 
parsing, or is it just not implemented?

There are a lot of things that you can do in block parsing that you 
can't in string parsing. In this case, the result of DO is compared 
directly as a REBOL value. Strings don't directly contain REBOL values 
the way that blocks do. Even if you tried to limit the result types 
of the expression and trigger an error if they don't match, what 
you are left with isn't useful enough to justify adding it, imo. 
For instance, in your example it was a bad idea to use DO. We'll 
see though.
Micha, there was a direct solution proposed for this in the parse 
proposals, specifically to deal with local variables in recursive 
parse rules. However, it turns out that PARSE isn't really recursive: 
It fakes it. So there was no way to support this feature in a parse 
directive. The best way to do the local variables is to put the PARSE 
call and the rules in a function, and if you have to use recursive 
rules, recursively call that function in an IF (...) operation. It 
really works well, in a roundabout sort of way.
Nicolas, in R3 especially it is better to directly put the rules 
in the function, rather than refer to external rules. The DECODE-URL 
method is really crappy in R3 because it isn't recursion-safe or 
task-safe. (Reminder: We must fix that for R3 when we go over the 
mezzanines for task-safety.)
Maxim
5-Sep-2010
[5147x2]
micha, you can also just push and pop values in a block you use like 
a stack.  you push before setting to a variable, you pop after the 
rule.

you just have to make sure to only push/pop once a complete rule 
is matched.  meaning you handle that in a paren at the END of the 
rule.
there are a few example floating around, you should find one if you 
google it.
Anton
6-Sep-2010
[5149]
Remember that Micha's English isn't good. I don't think he can understand 
what you guys are saying without a lot of effort in translation. 
It might be better to try to make your points in code.
Fork
6-Sep-2010
[5150]
@BrianH: If you're string parsing, couldn't it run to-string on the 
result of DO?
Ladislav
8-Sep-2010
[5151]
The best way to do the local variables is to put the PARSE call and 
the rules in a function, and if you have to use recursive rules, 
recursively call that function in an IF (...) operation. It really 
works well, in a roundabout sort of way.
 - this is too much of a roundabout for most cases, I have to add
BrianH
10-Sep-2010
[5152x2]
Fork, that won't work on some return types, and that would lead to 
runtime errors. But in theory, yes.
Ladislav, true. But since PARSE doesn't really recurse, the only 
direct way to have local variables would be to BIND/copy the parse 
rules for each level of recursion. Doing the function recursion method 
is actually more efficient and easier than that.
Ladislav
11-Sep-2010
[5154]
I guess, that it is the time to propose a reasonable and efficient 
method
Ladislav
13-Sep-2010
[5155]
I defined a USE-RULE function yielding a rule with local variables. 
Now I wonder where to publish it.
Gregg
13-Sep-2010
[5156]
REBOL.org, or perhaps your own site for now? Or as a wiki page linked 
from PARSE docs?
Ladislav
13-Sep-2010
[5157]
http://en.wikibooks.org/w/index.php?title=REBOL_Programming/Language_Features/Parse&stable=0#USE_rule
Gregg
13-Sep-2010
[5158]
Thanks Ladislav. 

What do 'fni and 'fnii stand for?


I would certainly add a comment or doc string that USE-RULE is recursive/thread 
safe, which is why it's not much simpler.
Ladislav
13-Sep-2010
[5159]
FNI is just "fixed guts" of the CONTEXT-FN put in the CONTEXT-FN 
function body as a function to not intefere with the context. FNII 
is "dynamic guts" of CONTEXT-FN. By assigning different functions 
to the FNII variable we adjust what the CONTEXT-FN is actually doing.
Gregg
13-Sep-2010
[5160]
Sorry, I meant to ask what the words were abbreviations for.
Ladislav
13-Sep-2010
[5161]
FN - internals (aka "guts")
Gregg
13-Sep-2010
[5162x3]
Ah, thanks.
So, something like 'inner-body.
And 'inner-inner-body. :-)
Ladislav
13-Sep-2010
[5165x2]
yes, I wanted to have a FNII variable referring to a function having 
access to the CONTEXT-FNs context without being influenced directly 
by the context
...and yet influencing what the CONTEXT-FN is actually doing
Gregg
13-Sep-2010
[5167]
It's another great example of bending REBOL to your will with a great 
deal of control.
Ladislav
13-Sep-2010
[5168x2]
If I wanted to do it just in R2, it could have been simpler, but 
R3 is more picky about what it allows
(in R2 you can directly modify the CONTEXT-FN, which is protected 
in R3)
Gregg
13-Sep-2010
[5170x2]
Does it look like I understand it, based on these comments?
set 'use-rule func [

    "Create a recursion and thread-safe parse rule with local variables. 
    R2/R3 compatible."
    words [block!] "Local word(s) to the parse rule"
    rule  [block!] "Parse rule"
] [
    make object! [

        ; Create a new function context. 'Inner-body refers to a function 

        ; with access to CONTEXT-FN's context without being influenced 
        ; directly by the context.
        spec: copy [/local]
        append spec words
        inner-body: func ['word] [inner-inner-body word]
        context-fn: func spec reduce [:inner-body first words]
        

        ; Bind the rule the caller gave us to the new context we just created.
        inner-inner-body: func [word] [bind/copy rule word]
        bound-rule: context-fn
        

        ; Now define the use rule. Because this is an "active" rule,
        ; with state we need to include some state variables used
        ; by the internal PARSE call ('pos and 'success).
        pos: none
        success: none
        inner-inner-body: func [word] [

            ; If the parse of the rule succeeds, we set the parse position

            ; to the where the rule match ended, otherwise we don't change

            ; the parse position and use [end skip] to return a false 
            ; result (for R2 compatibility).
            success: either parse pos [bound-rule pos: to end] [
                [:pos]
            ] [
                [end skip]
            ]
        ]
        set 'rule copy/deep [pos: (context-fn) success]
    ]
    rule
]
Ladislav
13-Sep-2010
[5172]
Quite precise, except for the fact, that SUCCESS and POS are mainly 
used to "transfer" the inner parse state to the "outer parse"
Gregg
13-Sep-2010
[5173]
Thanks. I'll note that. I usually have to analyze your code bit by 
bit to understand it. :-)
Ladislav
13-Sep-2010
[5174]
I am glad you added your notes. I hope you do not mind me using your 
comments to make the code more understandable for others?
Gregg
13-Sep-2010
[5175x4]
I don't mind at all. :-)
set 'use-rule func [

    "Create a recursion and thread-safe parse rule with local variables. 
    R2/R3 compatible."
    words [block!] "Local word(s) to the parse rule"
    rule  [block!] "Parse rule"
] [
    make object! [

        ; Create a new function context. 'Inner-body refers to a function 

        ; with access to CONTEXT-FN's context without being influenced 
        ; directly by the context.
        spec: copy [/local]
        append spec words
        inner-body: func ['word] [inner-inner-body word]
        context-fn: func spec reduce [:inner-body first words]
        

        ; Bind the rule the caller gave us to the new context we just created.
        inner-inner-body: func [word] [bind/copy rule word]
        bound-rule: context-fn
        

        ; Now define the use rule. Because this is an "active" rule,
        ; with state we need to include some state variables used

        ; by the internal PARSE call ('pos and 'success). They are used to 
        ; "transfer" the inner parse state to the "outer parse".
        pos: none
        success: none
        inner-inner-body: func [word] [

            ; If the parse of the rule succeeds, we set the parse position

            ; to the where the rule match ended, otherwise we don't change

            ; the parse position and use [end skip] to return a false 
            ; result (for R2 compatibility).
            success: either parse pos [bound-rule pos: to end] [
                [:pos]
            ] [
                [end skip]
            ]
        ]
        set 'rule copy/deep [pos: (context-fn) success]
    ]
    rule
]
Typos in comments. Just a minute.
set 'use-rule func [

    "Create a recursion and thread-safe parse rule with local variables. 
    R2/R3 compatible."
    words [block!] "Local word(s) to the parse rule"
    rule  [block!] "Parse rule"
] [
    make object! [

        ; Create a new function context. 'Inner-body refers to a function 

        ; with access to CONTEXT-FN's context without being influenced 
        ; directly by the context.
        spec: copy [/local]
        append spec words
        inner-body: func ['word] [inner-inner-body word]
        context-fn: func spec reduce [:inner-body first words]
        

        ; Bind the rule the caller gave us to the new context we just created.
        inner-inner-body: func [word] [bind/copy rule word]
        bound-rule: context-fn
        

        ; Now define the use rule. Because this is an "active" rule,

        ; with state, we need to include some state variables used

        ; by the internal PARSE call ('pos and 'success). They are used to 
        ; "transfer" the inner parse state to the "outer parse".
        pos: none
        success: none
        inner-inner-body: func [word] [

            ; If the parse of the rule succeeds, we set the parse position

            ; to the point where the rule match ended, otherwise we don't 

            ; change the parse position and use [end skip] to return a false 
            ; result (for R2 compatibility).
            success: either parse pos [bound-rule pos: to end] [
                [:pos]
            ] [
                [end skip]
            ]
        ]
        set 'rule copy/deep [pos: (context-fn) success]
    ]
    rule
]
Ladislav
14-Sep-2010
[5179x3]
fine, I used your text, and added some more
Brian, do you think, that a more "seamless" way how to do the above 
in parse shall be considered, or that this approach is good enough 
as it stands now?
Reposting the link:

http://www.rebol.org/view-script.r?script=use-rule.r
Graham
14-Sep-2010
[5182x2]
Very useful ...
( that was a pun :) )
Pekr
15-Sep-2010
[5184x2]
hmm, I thought that in R3, parse variables are safe for recursion 
rules?
ah, there was USE 1 (BrianH) and USE 2 proposal (Peta). USE 1 was 
assigned for implementation, but deferred along with LIMIT, OF, REVERSE 
and some other proposals ...
Ladislav
15-Sep-2010
[5186]
Pekr, there are no "parse variables", as far as I know. My above 
code was inspired by BrianH and USE2