Based on perlop(1) and the note at the end of apocalypse 3, here's a start on a Parse::RecDescent grammar for Perl 6 expressions. It does not handle some variables; in particular, qq/${"foo"}/ won't fly. It should handle precedence and hyping when adding new operators in the "right way". To add a new operator with the same precedence as binary '+', you can do something like
addsub_op: '=#$%' and get the hyped '^=#$%' operator for free. If you don't want it to be hyped, you can do this: _addsub_op: '=#$%' If you want it to be unary prefix, but with the same precedence, you can do this: addsub: '=#$%' muldiv Fire away! /s use Parse::RecDescent; use Data::Dumper; use strict; $Data::Dumper::Terse = 1; $Data::Dumper::Indent = 1; use Term::ReadLine; my $grammar = <<'END'; { use Regexp::Common; } <autotree> # Literals: literal: lit_int | lit_real | lit_string lit_int: /$RE{num}{int}/ lit_real: /$RE{num}{real}/ lit_string: <perl_quotelike> # Variables: variable: sv | av | hv sv: '$' name av: '@' name hv: '%' name cv: '&' name name: '{' name '}' | sv | /[\/\\^%&*\$\#@!_0-9]/ | ('::')(?) <leftop: /[\w_]+/ '::' /[\w_]+/> # operators/expressions hype: ('^')(?) term: '(' expr ')' | literal | variable left_list: left_list_op(?) term left_list_op: 'left_list' apply: <leftop: left_list _apply_op left_list> _apply_op: hype apply_op apply_op: '.' incr: apply _incr_op | _incr_op apply | apply _incr_op: hype incr_op incr_op: '++' | '--' pow: <rightop: incr _pow_op incr> _pow_op: hype pow_op pow_op: '**' misc_unary: _misc_unary_op(?) pow _misc_unary_op: hype misc_unary_op misc_unary_op: '!' | '~' | '\\' | '+' | '-' match: <leftop: misc_unary _match_op misc_unary> _match_op: hype match_op match_op: '=~' | '!~' muldiv: <leftop: match _muldiv_op match> _muldiv_op: hype muldiv_op muldiv_op: '*' | '/' | '%' | 'x' addsub: <leftop: muldiv _addsub_op muldiv> _addsub_op: hype addsub_op addsub_op: '+' | '-' | '_' bitshift: <leftop: addsub _bitshift_op addsub> _bitshift_op: hype bitshift_op bitshift_op: '<<' | '>>' named_unary: named_unary_op(s?) bitshift named_unary_op: 'named_unary' compare: <leftop: named_unary _compare_op named_unary> _compare_op: hype compare_op compare_op: '==' | '!=' | '<' | '>' | '<=' | '>=' | '<=>' | 'eq' | 'ne' | 'lt' | 'gt' | 'le' | 'ge' | 'cmp' bitand: <leftop: compare _bitand_op compare> _bitand_op: hype bitand_op bitand_op: '&' bitor: <leftop: bitand _bitor_op bitand> _bitor_op: hype bitor_op bitor_op: '|' | '^' logand: <leftop: bitor _logand_op bitor> _logand_op: hype logand_op logand_op: '&' logor: <leftop: logand _logor_op logand> _logor_op: hype logor_op logor_op: '|' | '^' range: logor (range_op logor)(?) range_op: '...' | '..' ternary: range ('?' ternary ':' ternary)(?) assign: <rightop: ternary _assign_op ternary> _assign_op: '^' assign_op assign_op: assignable_op(?) '=' assignable_op: logand_op | logor_op | bitand_op | bitor_op | bitshift_op | addsub_op | muldiv_op | pow_op | '!' comma: <leftop: assign comma_op assign> comma_op: ',' | '=>' right_list: right_list_op(s?) comma right_list_op: 'not' | 'right_list' log_AND: <leftop: right_list _log_AND_op right_list> _log_AND_op: hype log_AND_op log_AND_op: 'and' log_OR: <leftop: log_AND _log_OR_op log_AND> _log_OR_op: hype log_OR_op log_OR_op: 'or' | 'xor' expr: log_OR END my $parser = new Parse::RecDescent $grammar or die $!; sub simplify { my $self = shift; if (!ref $self) { return $self; } elsif (ref $self eq 'ARRAY') { return join ' ', map { simplify($_) } @$self; } elsif (exists $self->{__done__}) { return ''; } else { $self->{__done__} = 1; if (exists $self->{__VALUE__}) { return $self->{__VALUE__}; } if ($self->{__RULE__} eq 'hype') { return $self->{"'^'"}[0]; } my @things = grep /\S/, map { simplify($self->{$_}) } grep !/__(?:RULE|done)__/, keys %$self; if (@things == 1) { return $things[0]; } else { return "($self->{__RULE__} ".(join ' ', @things).')'; } } } my $term = new Term::ReadLine; while (defined(local $_ = $term->readline('> '))) { if (/^:(.*)/) { print eval $1; } else { my $result = $parser->expr($_); if ($::USE_DUMPER) { print Dumper $result; } else { if ($result) { print simplify($result), "\n"; } else { print "parse error\n"; } } } print "\n"; }