midirebolrebol3firmata

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


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.

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


Solution

  • 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.