1

There is an example in Ragel manual 6.5 Semantic conditions, which demonstrates how to write a grammar for variable size structures, using when clause.

action rec_num { i = 0; n = getnumber(); }
action test_len { i++ < n }
data_fields = (
’d’
[0-9]+ %rec_num
’:’
( [a-z] when test_len )*
)**;

It works fine for small structures, however for bigger structures it slows down, because parser tries to evaluate condition on every character.

What I am trying to do is to skip scanning and just copy data into the buffer, for a grammar like this (note any*):

action rec_num { i = 0; n = getnumber(); }
action test_len { i++ < n }
data_fields = (
’d’
[0-9]+ %rec_num
’:’
( any* when test_len )*
)**;

So I want to copy buffer of length n straight away without iteration. How can I do this without leaving parser context?

Andrew Selivanov
  • 1,306
  • 15
  • 22

1 Answers1

1

You probably need to take matters into your own hand. The ragel user guide mentions that you can alter the fpc/p variable within the machine so this should be safe enough. This assumes you're processing all your data in one chunk (ie, the data field won't be broken up)

machine foo;

action rec_num { i = 0; n = getnumber(); }
action test_len { i++ < n }
action buffer_data_field {
  /* p is still pointing to ':' at this point. */
  if (p + 1 + n >= pe) { fgoto *foo_error; }
  buffer(p + 1, n);
  p += n;
}
action buffer_data_field_eof {
  /* check for eof while data was expected */
  /* p is pointing past the ':' at this point */
  if (n) { fgoto *foo_error; }
}

data_fields = (
'd'
[0-9]+ %rec_num
':' 
$buffer_data_field
$eof(buffer_data_field_eof)

)**;

If data is chunked up, you could split the buffering out:

buffer_data :=
    any+
    $eof{ fnext *foo_error; }
    ${
        size_t avail = pe - p;
        if (avail >= n) {
            buffer(p, n);
            p += n - 1;
            fnext data_fields;
        } else {
            buffer_partial(p, avail);
            n -= avail;
            p += avail - 1;
        }
    }
    ;


data_fields = (
    'd'
    [0-9]+ %rec_num
    ':'  ${ if (n) fnext buffer_data; }
    )**;
Kelvin Sherlock
  • 825
  • 7
  • 8