How to break out of a subrule once a certain part of the rule is met?

123 Views Asked by At

Currently parsing Midi messages for the Firmata protocol in Rebol 3, and came across a situation I haven't seen before.

Basically, I have a generic rule to copy bytes between the framing bytes. However that rule is eating the framing bytes. I have reduced the code to the following:

data: #{
    F06C00010101040E7F000101010308040E7F00010101040E7F0001010103
    08040E7F000101010308040E7F00010101040E7F00010101040E7F0001010103
    08040E7F000101010308040E7F000101010308040E7F00010101040E7F000101
    01040E7F00010101020A7F00010101020A7F00010101020A7F00010101020A7F
    00010101020A06017F00010101020A06017FF7
}

sysex-start: #{F0}
sysex-end: #{F7}
capability-query: #{6B}
capability-response: #{6C}
capability-end: #{7F}

received-rule: [
    sysex-start
    capability-response-rule 
    sysex-end
]

capability-response-rule: [
    capability-response
    [
        capability-end |
        some [copy pin 1 skip]
    ]
]
parse data received-rule

The issue is that some [copy pin 1 skip] is gobbling up the sysex-end binary.

  • Is there a way I can restructure the rule (without moving sysex-end into the subrule)?

  • Is there a parse keyword that would help in this case of breaking out of the subrule?

(Note: I'm aware that I'm not interpreting the data according to the spec.)

2

There are 2 best solutions below

1
On BEST ANSWER

You'll need to break the loop when it hits sysex-end one way or another.

You either match sysex-end each time around the loop and break when it hits, or you only match against everything which is not a sysex-end.

The first option obviously brings sysex-end into the subrule, but it seems straightforward. If I recast your problem like this:

problem-rule: [
    (matched-bytes: none)
    copy matched-bytes some [skip]
]

Then the first solution using the NOT keyword might be:

alternative-1: [
    (matched-bytes: none)
    copy matched-bytes some [
        not sysex-end
        skip
    ]
]

The second alternative will not bring sysex-end into the subrule. You make a bitset! to match everything except sysex-end (247 corresponds to hex F7):

not-sysex-end: bits: make bitset! [0 - 246 248 - 255]
alternative-2: [
    (matched-bytes: none)
    copy matched-bytes some [not-sysex-end]
]

I'm not familiar enough with Rebol 3 bitsets to know if there is a better way to specify this.

The second alternative here is probably better because it meets your requirement and will likely perform much faster than the first alternative.

0
On

you could (at least in Rebol2) use

pin-set: complement charset to-string #{F7} 

received-rule: [
    sysex-start
    capability-response-rule 
    sysex-end
]

capability-response-rule: [
    capability-response
    [
        capability-end |
        some  [copy pin pin-set  ]
    ]
]
parse data received-rule

== true