chilon::parser tutorial - part 6
The previous example will be extended to allow functions to be nested recursively. The new test data:
var _one
function command1(parameter, friend) {
var v1
function nested1(p1) {
var n1v1
}
function nested2(p2) {
function nested2nested1(p21) {}
var n2v2
function nested2nested2(p22a, p22b) {
var n2n2v1
}
}
}
var _two_two_
The new code to parse it:
typedef lexeme<
choice< char_range<a,z, A,Z>, char_<'_'> >,
many< choice<
char_range<a,z, A,Z, '0', '9'>,
char_<'_'> > > >
identifier;
typedef sequence<char_<v,a,r>, identifier> variable;
// A shortcut I like to use.
#define node_member_(N) CHILON_NODE_MEMBER(N)
// Inherting from `node' in this way allows CHILON_NODE_MEMBER to be used.
struct function : node<function> {
typedef sequence<char_<f,u,n,c,t,i,o,n>, identifier> name;
typedef joined<char_<','>, identifier> arguments;
// A function can now consist of variables and nested functions.
// `node' is used within this parser to allow `function' to refer
// to itself. It must always be used to protect parsers that would
// otherwise create a circular type dependancy and a compiler error.
// `node' has simple to follow restrictions on its use, see the
// next comment for more information.
typedef many<
choice<
node<function>,
variable
>
> elements;
stored<arguments>::type arguments_;
stored<elements>::type elements_;
range name_;
// By defining the following typedef, `function' can now be passed to
// any chilon parse function and is parsed using the corresponding type:
// Doing this also allows the type to be used with `node'.
typedef sequence<
node_member_(name),
char_<'('>,
node_member_(arguments),
char_<')'>,
char_<'{'>,
node_member_(elements),
char_<'}'>
> parser_type;
};
template <class O>
void print_args(int const indent, O& stream, function const& func) {
stream << "function " << func.name_ << "(\n";
chilon::print_indent(indent + 1, stream);
stream << "arguments: ";
chilon::print_args(indent + 1, stream, func.arguments_);
stream << "\n";
chilon::print_indent(indent + 1, stream);
stream << "elements: ";
chilon::print_args(indent + 1, stream, func.elements_);
stream << "\n";
chilon::print_indent(indent, stream);
stream << ")";
}
int main(int argc, char *argv[]) {
// function used directly in choice due to the `type' definition
// discussed in the previous comment.
return test< many< choice<
variable,
function
> > >(argc, argv);
}
The new output:
[
"_one"
function command1(
arguments: [
"parameter"
"friend"
]
elements: [
"v1"
function nested1(
arguments: [ "p1" ]
elements: [ "n1v1" ]
)
function nested2(
arguments: [ "p2" ]
elements: [
function nested2nested1(
arguments: [ "p21" ]
elements: []
)
"n2v2"
function nested2nested2(
arguments: [
"p22a"
"p22b"
]
elements: [ "n2n2v1" ]
)
]
)
]
)
"_two_two_"
]
The above source code repeated without comments and printing code:
typedef lexeme<
choice< char_range<a,z, A,Z>, char_<'_'> >,
many< choice< char_range<a,z, A,Z, '0', '9'>, char_<'_'> > > >
identifier;
typedef sequence<char_<v,a,r>, identifier> variable;
#define node_member_(N) CHILON_NODE_MEMBER(N)
struct function : node<function> {
typedef sequence<char_<f,u,n,c,t,i,o,n>, identifier> name;
typedef joined<char_<','>, identifier> arguments;
typedef many< choice< node<function>, variable > > elements;
stored<arguments>::type arguments_;
stored<elements>::type elements_;
range name_;
typedef sequence<
node_member_(name),
char_<'('>, node_member_(arguments), char_<')'>,
char_<'{'>, node_member_(elements), char_<'}'>
> type;
};
int main(int argc, char *argv[]) {
return test< many< choice< variable, function > > >(argc, argv);
}