How to parse a functions argument block?

2019-07-24 06:13发布

问题:

I'd like to reorganise the arguments block in a given Rebol function to provide a more comprehensible understanding of the arguments required by the function. The arguments block in a Rebol function is a great example of the malleable data structures in Rebol:

adjoin: func [
    "Adjoins"
    series [series!] "Series to adjoin"
    joinee
    /local other
][...]

But I need something more predictable to make sense of this metadata. How would I get it to a more compliant format? An example:

[
    ; should include about value whether the about string is there or not
    about none none "Adjoins"
    argument series [series!] "Series to Adjoin"
    argument joinee none none
    option local none none
    argument other none none
]

Any thoughts to the transform method or the best way to represent the arguments content would be most helpful.

回答1:

Here is a complete rebol script which should help you out :-)

Note that the variables do not have to start with a dot, nor do the parse rules require to be surrounded in = signs. Its a quick way to separate the task of each thing within the rules. This way its much easier to identify which word does what, which is especially important when you start to build larger rules.

rebol [
    title: "func spec extractor"
]



code: {adjoin: func [
    "Adjoins"
    series [series!] "Series to adjoin"
    joinee
    /local other
][...]

append: func [
    {Appends a value to the tail of a series and returns the series head.}
    series [series! port!]
    value
    /only "Appends a block value as a block"
][ ... ]
}



code: load code

;----
; setting to a temp variable, prevents .param-str from being erased 
; if the rule doesn't match at the point the rule is used (it may be optional)
;----
=param-str=: [set .tmp string! (.param-str: .tmp)] 
=param-types=: [set .tmp into [some [word!]] (.param-types: .tmp)] 

=param=: [
    (.param-types: .tmp: .param-str: none )
    set .param-name word!
    opt =param-str= 
    opt =param-types= 
    opt =param-str=
    ( 
        append/only .param-blk .tmp: reduce [ .param-name .param-str .param-types ]
    )
]

=refinements=: [
    (.ref-str: none)
    set .refinement refinement! 
    opt [ set .ref-str string! ]
    (
        append .param-blk .refinement
        append .param-blk .ref-str
    )    
    any =param=
]

=func-rule=: [
    ; set/reset variables
    (
        func-def: context [name: none doc-str: none args: [] refinements: [] code: none]
        .tmp: .func-name: .doc-str: .param-str: none
    )

    set .func-name set-word!  
    'func into [
        opt [ set .doc-str string! ]
        ( func-def/args:  .param-blk: copy [] )
        any =param=
        ( func-def/refinements:  .param-blk: copy [] )
        any =refinements=
        here:        
    ]
    set .func-body block!
    (
        func-def/name:    .func-name
        func-def/doc-str: .doc-str
        func-def/code:    .func-body
    )
]      

funcs: []
parse code [ some [ =func-rule=  ( append funcs func-def) | skip ]]

probe funcs

here is what it prints out:

[make object! [
        name: adjoin:
        doc-str: "Adjoins"
        args: [[
                series "Series to adjoin" [series!]
            ] [
                joinee none none
            ]]
        refinements: [
            /local none [other none none]
        ]
        code: [...]
    ] make object! [
        name: append:
        doc-str: {Appends a value to the tail of a series and returns the series head.}
        args: [[
                series none [series! port!]
            ] [
                value none none
            ]]
        refinements: [
            /only "Appends a block value as a block"
        ]
        code: [...]
    ]]


标签: parsing rebol