Often your Ruby code will contain a variable-length list of items, like parameters to a method call, items in an array, or statements inside a block. Ripper doesn’t use variable-length events. Instead, every event has a fixed number of items.
In order to keep the number of parameters fixed for each event, Ripper often creates an empty item and then adds to it. For instance, here is how array literals are parsed by Ripper::SexpBuilder
:
2.7.1 :001 > pp Ripper.sexp_raw("[1, 2, 3, 4, 5]")
[:program,
[:stmts_add,
[:stmts_new],
[:array,
[:args_add,
[:args_add,
[:args_add,
[:args_add,
[:args_add, [:args_new], [:@int, "1", [1, 1]]],
[:@int, "2", [1, 4]]],
[:@int, "3", [1, 7]]],
[:@int, "4", [1, 10]]],
[:@int, "5", [1, 13]]]]]]
Notice that args_new has no additional parameters, and the other array elements are added one at a time using args_add.
Other events, such as qsymbols_new/qsymbols_add work very much like args_new/args_add. Here’s what qsymbols_new/qsymbols_add looks like in Ripper::SexpBuilder
:
2.7.1 :001 > pp Ripper.sexp_raw("%i[one two three]")
[:program,
[:stmts_add,
[:stmts_new],
[:array,
[:qsymbols_add,
[:qsymbols_add,
[:qsymbols_add, [:qsymbols_new], [:@tstring_content, "one", [1, 3]]],
[:@tstring_content, "two", [1, 7]]],
[:@tstring_content, "three", [1, 11]]]]]]
One of the differences between the Ripper::SexpBuilder
and Ripper::SexpBuilderPP
classes is that it replaces *_new
/*_add
chains with a Ruby array, such as stmts_new/stmts_add being replaced here:
2.7.1 :001 > pp Ripper.sexp("[1, 2, 3, 4, 5]")
[:program,
[[:array,
[[:@int, "1", [1, 1]],
[:@int, "2", [1, 4]],
[:@int, "3", [1, 7]],
[:@int, "4", [1, 10]],
[:@int, "5", [1, 13]]]]]]
The accomplish this same behavior in your own Ripper parser, you can duplicate the approach that Ripper::SexpBuilderPP
takes by handling *_new
events with a new array and *_add
methods by adding onto that array, like here.
The convention to use *_new
/*_add
events to build lists is used in many places in the Ripper parser. The complete list is given below:
%i
array literal parts%w
array literal parts%I
array literal parts%W
array literal%W
array literal parts%x
command string literal parts