#!/usr/bin/perl -w use strict; use Parse::RecDescent; use Data::Dumper; use vars qw(%VARIABLE); # Enable warnings within the Parse::RecDescent module. $::RD_ERRORS = 1; # Make sure the parser dies when it encounters an error $::RD_WARN = 1; # Enable warnings. This will warn on unused rules &c. $::RD_HINT = 1; # Give out hints to help fix problems. my $grammar = <<'_EOGRAMMAR_'; # Terminals (macros that can't expand further) # OP : m([-+*/%]) # Mathematical operators INTEGER : /[-+]?\d+/ # Signed integers VARIABLE : /\w[a-z0-9_]*/i # Variable expression : INTEGER OP expression { return &::expression(@item) } | VARIABLE OP expression { return &::expression(@item) } | INTEGER | VARIABLE { return $::VARIABLE{$item{VARIABLE}} } #### print_instruction : /print/i expression { use Data::Dumper; print Dumper \%item; print $item{expression}."\n" } assign_instruction : VARIABLE "=" expression { $::VARIABLE{$item{VARIABLE}} = $item{expression} } myprint_instruction: 'print(' expression ')' # I want print(1+1) to work { print $item{expression}."\n" } range: '(' INTEGER '..' INTEGER ')' { print "Gosh darn, $item{INTEGER}\n"; } instruction : myprint_instruction | assign_instruction | print_instruction startrule: instruction(s /;/) # multiple instructions, separated by ; _EOGRAMMAR_ sub expression { shift; my ($lhs,$op,$rhs) = @_; $lhs = $VARIABLE{$lhs} if $lhs=~/[^-+0-9]/; return eval "$lhs $op $rhs"; } my $parser = Parse::RecDescent->new($grammar); print "a=2\n"; $parser->startrule("a=2"); print "a=1+3\n"; $parser->startrule("a=1+3"); print "print 5*7\n"; $parser->startrule("print 5*7"); print "print 2/4\n"; $parser->startrule("print 2/4"); print "print 2+2/4\n"; $parser->startrule("print 2+2/4"); print "print 2+-2/4\n"; $parser->startrule("print 2+-2/4"); print "a = 5 ; print a\n"; $parser->startrule("a = 5 ; print a"); print "a = 69 ; print(a)\n"; $parser->startrule("a = 69 ; print(a)"); # WTF??? print "a = 69 ; print a ; print(a)\n"; $parser->startrule("a = 69 ; print a ; print(a)"); # WTF??? print "print( 6 + 9 )\n"; $parser->myprint_instruction("print( 6 + 9 )"); # WTF?? print "print(69)\n"; $parser->myprint_instruction("print(69)"); # ok print "print(69)\n"; $parser->startrule("print(69)"); # ok print "(4..9)\n"; $parser->range("(4..4)"); # ok print Dumper $parser; __END__ a=2 a=1+3 print 5*7 $VAR1 = { 'expression' => 35, '__PATTERN1__' => 'print', '__RULE__' => 'print_instruction' }; 35 print 2/4 $VAR1 = { 'expression' => '0.5', '__PATTERN1__' => 'print', '__RULE__' => 'print_instruction' }; 0.5 print 2+2/4 $VAR1 = { 'expression' => '2.5', '__PATTERN1__' => 'print', '__RULE__' => 'print_instruction' }; 2.5 print 2+-2/4 $VAR1 = { 'expression' => '1.5', '__PATTERN1__' => 'print', '__RULE__' => 'print_instruction' }; 1.5 a = 5 ; print a $VAR1 = { 'expression' => '5', '__PATTERN1__' => 'print', '__RULE__' => 'print_instruction' }; 5 a = 69 ; print(a) a = 69 ; print a ; print(a) $VAR1 = { 'expression' => '69', '__PATTERN1__' => 'print', '__RULE__' => 'print_instruction' }; 69 print( 6 + 9 ) print(69) 69 print(69) 69 (4..9) God damn, 4 #### $VAR1 = bless( { '_AUTOTREE' => undef, 'rules' => { 'assign_instruction' => bless( { 'impcount' => 0, 'line' => '24', 'prods' => [ bless( { 'dircount' => 0, 'uncommit' => undef, 'patcount' => 0, 'strcount' => 1, 'number' => 0, 'error' => undef, 'line' => undef, 'items' => [ bless( { 'line' => '24', 'subrule' => 'VARIABLE', 'argcode' => undef, 'implicit' => undef, 'matchrule' => 0, 'lookahead' => 0 }, 'Parse::RecDescent::Subrule' ), bless( { 'description' => '\'=\'', 'line' => '24', 'pattern' => '=', 'hashname' => '__STRING1__', 'lookahead' => 0 }, 'Parse::RecDescent::InterpLit' ), bless( { 'line' => '24', 'subrule' => 'expression', 'argcode' => undef, 'implicit' => undef, 'matchrule' => 0, 'lookahead' => 0 }, 'Parse::RecDescent::Subrule' ), bless( { 'line' => '25', 'code' => '{ $::VARIABLE{$item{VARIABLE}} = $item{expression} }', 'hashname' => '__ACTION1__', 'lookahead' => 0 }, 'Parse::RecDescent::Action' ) ], 'actcount' => 1 }, 'Parse::RecDescent::Production' ) ], 'calls' => [ 'VARIABLE', 'expression' ], 'opcount' => 0, 'changed' => 0, 'vars' => '', 'name' => 'assign_instruction' }, 'Parse::RecDescent::Rule' ), 'instruction' => bless( { 'impcount' => 0, 'line' => '36', 'prods' => [ bless( { 'dircount' => 0, 'uncommit' => undef, 'patcount' => 0, 'strcount' => 0, 'number' => 0, 'error' => undef, 'line' => undef, 'items' => [ bless( { 'line' => '36', 'subrule' => 'myprint_instruction', 'argcode' => undef, 'implicit' => undef, 'matchrule' => 0, 'lookahead' => 0 }, 'Parse::RecDescent::Subrule' ) ], 'actcount' => 0 }, 'Parse::RecDescent::Production' ), bless( { 'dircount' => 0, 'uncommit' => undef, 'patcount' => 0, 'strcount' => 0, 'number' => 1, 'error' => undef, 'line' => '37', 'items' => [ bless( { 'line' => '37', 'subrule' => 'assign_instruction', 'argcode' => undef, 'implicit' => undef, 'matchrule' => 0, 'lookahead' => 0 }, 'Parse::RecDescent::Subrule' ) ], 'actcount' => 0 }, 'Parse::RecDescent::Production' ), bless( { 'dircount' => 0, 'uncommit' => undef, 'patcount' => 0, 'strcount' => 0, 'number' => 2, 'error' => undef, 'line' => '38', 'items' => [ bless( { 'line' => '38', 'subrule' => 'print_instruction', 'argcode' => undef, 'implicit' => undef, 'matchrule' => 0, 'lookahead' => 0 }, 'Parse::RecDescent::Subrule' ) ], 'actcount' => 0 }, 'Parse::RecDescent::Production' ) ], 'calls' => [ 'myprint_instruction', 'assign_instruction', 'print_instruction' ], 'opcount' => 0, 'changed' => 0, 'vars' => '', 'name' => 'instruction' }, 'Parse::RecDescent::Rule' ), 'OP' => bless( { 'impcount' => 0, 'line' => '3', 'prods' => [ bless( { 'dircount' => 0, 'uncommit' => undef, 'patcount' => 1, 'strcount' => 0, 'number' => 0, 'error' => undef, 'line' => undef, 'items' => [ bless( { 'description' => 'm([-+*/%])', 'pattern' => '[-+*/%]', 'mod' => '', 'hashname' => '__PATTERN1__', 'lookahead' => 0, 'ldelim' => '(', 'line' => '5', 'rdelim' => ')' }, 'Parse::RecDescent::Token' ) ], 'actcount' => 0 }, 'Parse::RecDescent::Production' ) ], 'calls' => [], 'opcount' => 0, 'changed' => 0, 'vars' => '', 'name' => 'OP' }, 'Parse::RecDescent::Rule' ), 'startrule' => bless( { 'impcount' => 0, 'line' => '40', 'prods' => [ bless( { 'dircount' => 1, 'uncommit' => undef, 'op' => [], 'patcount' => 1, 'strcount' => 0, 'number' => 0, 'error' => undef, 'line' => undef, 'items' => [ bless( { 'expected' => '', 'rightarg' => bless( { 'line' => '40', 'subrule' => 'instruction', 'argcode' => undef, 'implicit' => undef, 'matchrule' => 0, 'lookahead' => 0 }, 'Parse::RecDescent::Subrule' ), 'min' => 1, 'max' => 100000000, 'op' => bless( { 'description' => '/;/', 'pattern' => ';', 'mod' => '', 'hashname' => '__PATTERN1__', 'lookahead' => 0, 'ldelim' => '/', 'line' => '40', 'rdelim' => '/' }, 'Parse::RecDescent::Token' ), 'hashname' => '__DIRECTIVE1__', 'leftarg' => bless( { 'line' => '40', 'subrule' => 'instruction', 'argcode' => undef, 'implicit' => undef, 'matchrule' => 0, 'lookahead' => 0 }, 'Parse::RecDescent::Subrule' ), 'type' => 'leftop' }, 'Parse::RecDescent::Operator' ) ], 'actcount' => 0 }, 'Parse::RecDescent::Production' ) ], 'calls' => [ 'instruction' ], 'opcount' => 0, 'changed' => 0, 'vars' => '', 'name' => 'startrule' }, 'Parse::RecDescent::Rule' ), 'VARIABLE' => bless( { 'impcount' => 0, 'line' => '6', 'prods' => [ bless( { 'dircount' => 0, 'uncommit' => undef, 'patcount' => 1, 'strcount' => 0, 'number' => 0, 'error' => undef, 'line' => undef, 'items' => [ bless( { 'description' => '/\\\\w[a-z0-9_]*/i', 'pattern' => '\\w[a-z0-9_]*', 'mod' => 'i', 'hashname' => '__PATTERN1__', 'lookahead' => 0, 'ldelim' => '/', 'line' => '7', 'rdelim' => '/' }, 'Parse::RecDescent::Token' ) ], 'actcount' => 0 }, 'Parse::RecDescent::Production' ) ], 'calls' => [], 'opcount' => 0, 'changed' => 0, 'vars' => '', 'name' => 'VARIABLE' }, 'Parse::RecDescent::Rule' ), 'print_instruction' => bless( { 'impcount' => 0, 'line' => '17', 'prods' => [ bless( { 'dircount' => 0, 'uncommit' => undef, 'patcount' => 1, 'strcount' => 0, 'number' => 0, 'error' => undef, 'line' => undef, 'items' => [ bless( { 'description' => '/print/i', 'pattern' => 'print', 'mod' => 'i', 'hashname' => '__PATTERN1__', 'lookahead' => 0, 'ldelim' => '/', 'line' => '17', 'rdelim' => '/' }, 'Parse::RecDescent::Token' ), bless( { 'line' => '17', 'subrule' => 'expression', 'argcode' => undef, 'implicit' => undef, 'matchrule' => 0, 'lookahead' => 0 }, 'Parse::RecDescent::Subrule' ), bless( { 'line' => '18', 'code' => '{ use Data::Dumper; print Dumper \\%item; print $item{expression}."\\n" }', 'hashname' => '__ACTION1__', 'lookahead' => 0 }, 'Parse::RecDescent::Action' ) ], 'actcount' => 1 }, 'Parse::RecDescent::Production' ) ], 'calls' => [ 'expression' ], 'opcount' => 0, 'changed' => 0, 'vars' => '', 'name' => 'print_instruction' }, 'Parse::RecDescent::Rule' ), 'myprint_instruction' => bless( { 'impcount' => 0, 'line' => '26', 'prods' => [ bless( { 'dircount' => 0, 'uncommit' => undef, 'patcount' => 0, 'strcount' => 2, 'number' => 0, 'error' => undef, 'line' => undef, 'items' => [ bless( { 'description' => '\'print(\'', 'line' => '26', 'pattern' => 'print(', 'hashname' => '__STRING1__', 'lookahead' => 0 }, 'Parse::RecDescent::Literal' ), bless( { 'line' => '26', 'subrule' => 'expression', 'argcode' => undef, 'implicit' => undef, 'matchrule' => 0, 'lookahead' => 0 }, 'Parse::RecDescent::Subrule' ), bless( { 'description' => '\')\'', 'line' => '26', 'pattern' => ')', 'hashname' => '__STRING2__', 'lookahead' => 0 }, 'Parse::RecDescent::Literal' ), bless( { 'line' => '26', 'code' => '{ print $item{expression}."\\n" }', 'hashname' => '__ACTION1__', 'lookahead' => 0 }, 'Parse::RecDescent::Action' ) ], 'actcount' => 1 }, 'Parse::RecDescent::Production' ) ], 'calls' => [ 'expression' ], 'opcount' => 0, 'changed' => 0, 'vars' => '', 'name' => 'myprint_instruction' }, 'Parse::RecDescent::Rule' ), 'expression' => bless( { 'impcount' => 0, 'line' => '7', 'prods' => [ bless( { 'dircount' => 0, 'uncommit' => undef, 'patcount' => 0, 'strcount' => 0, 'number' => 0, 'error' => undef, 'line' => undef, 'items' => [ bless( { 'line' => '9', 'subrule' => 'INTEGER', 'argcode' => undef, 'implicit' => undef, 'matchrule' => 0, 'lookahead' => 0 }, 'Parse::RecDescent::Subrule' ), bless( { 'line' => '9', 'subrule' => 'OP', 'argcode' => undef, 'implicit' => undef, 'matchrule' => 0, 'lookahead' => 0 }, 'Parse::RecDescent::Subrule' ), bless( { 'line' => '9', 'subrule' => 'expression', 'argcode' => undef, 'implicit' => undef, 'matchrule' => 0, 'lookahead' => 0 }, 'Parse::RecDescent::Subrule' ), bless( { 'line' => '10', 'code' => '{ return &::expression(@item) }', 'hashname' => '__ACTION1__', 'lookahead' => 0 }, 'Parse::RecDescent::Action' ) ], 'actcount' => 1 }, 'Parse::RecDescent::Production' ), bless( { 'dircount' => 0, 'uncommit' => undef, 'patcount' => 0, 'strcount' => 0, 'number' => 1, 'error' => undef, 'line' => '11', 'items' => [ bless( { 'line' => '11', 'subrule' => 'VARIABLE', 'argcode' => undef, 'implicit' => undef, 'matchrule' => 0, 'lookahead' => 0 }, 'Parse::RecDescent::Subrule' ), bless( { 'line' => '11', 'subrule' => 'OP', 'argcode' => undef, 'implicit' => undef, 'matchrule' => 0, 'lookahead' => 0 }, 'Parse::RecDescent::Subrule' ), bless( { 'line' => '11', 'subrule' => 'expression', 'argcode' => undef, 'implicit' => undef, 'matchrule' => 0, 'lookahead' => 0 }, 'Parse::RecDescent::Subrule' ), bless( { 'line' => '12', 'code' => '{ return &::expression(@item) }', 'hashname' => '__ACTION1__', 'lookahead' => 0 }, 'Parse::RecDescent::Action' ) ], 'actcount' => 1 }, 'Parse::RecDescent::Production' ), bless( { 'dircount' => 0, 'uncommit' => undef, 'patcount' => 0, 'strcount' => 0, 'number' => 2, 'error' => undef, 'line' => '13', 'items' => [ bless( { 'line' => '13', 'subrule' => 'INTEGER', 'argcode' => undef, 'implicit' => undef, 'matchrule' => 0, 'lookahead' => 0 }, 'Parse::RecDescent::Subrule' ) ], 'actcount' => 0 }, 'Parse::RecDescent::Production' ), bless( { 'dircount' => 0, 'uncommit' => undef, 'patcount' => 0, 'strcount' => 0, 'number' => 3, 'error' => undef, 'line' => '14', 'items' => [ bless( { 'line' => '14', 'subrule' => 'VARIABLE', 'argcode' => undef, 'implicit' => undef, 'matchrule' => 0, 'lookahead' => 0 }, 'Parse::RecDescent::Subrule' ), bless( { 'line' => '15', 'code' => '{ return $::VARIABLE{$item{VARIABLE}} }', 'hashname' => '__ACTION1__', 'lookahead' => 0 }, 'Parse::RecDescent::Action' ) ], 'actcount' => 1 }, 'Parse::RecDescent::Production' ) ], 'calls' => [ 'INTEGER', 'OP', 'expression', 'VARIABLE' ], 'opcount' => 0, 'changed' => 0, 'vars' => '', 'name' => 'expression' }, 'Parse::RecDescent::Rule' ), 'range' => bless( { 'impcount' => 0, 'line' => '31', 'prods' => [ bless( { 'dircount' => 0, 'uncommit' => undef, 'patcount' => 0, 'strcount' => 3, 'number' => 0, 'error' => undef, 'line' => undef, 'items' => [ bless( { 'description' => '\'(\'', 'line' => '31', 'pattern' => '(', 'hashname' => '__STRING1__', 'lookahead' => 0 }, 'Parse::RecDescent::Literal' ), bless( { 'line' => '31', 'subrule' => 'INTEGER', 'argcode' => undef, 'implicit' => undef, 'matchrule' => 0, 'lookahead' => 0 }, 'Parse::RecDescent::Subrule' ), bless( { 'description' => '\'..\'', 'line' => '31', 'pattern' => '..', 'hashname' => '__STRING2__', 'lookahead' => 0 }, 'Parse::RecDescent::Literal' ), bless( { 'line' => '31', 'subrule' => 'INTEGER', 'argcode' => undef, 'implicit' => undef, 'matchrule' => 0, 'lookahead' => 0 }, 'Parse::RecDescent::Subrule' ), bless( { 'description' => '\')\'', 'line' => '31', 'pattern' => ')', 'hashname' => '__STRING3__', 'lookahead' => 0 }, 'Parse::RecDescent::Literal' ), bless( { 'line' => '32', 'code' => '{ print "God damn, $item{INTEGER}\\n"; }', 'hashname' => '__ACTION1__', 'lookahead' => 0 }, 'Parse::RecDescent::Action' ) ], 'actcount' => 1 }, 'Parse::RecDescent::Production' ) ], 'calls' => [ 'INTEGER' ], 'opcount' => 0, 'changed' => 0, 'vars' => '', 'name' => 'range' }, 'Parse::RecDescent::Rule' ), 'INTEGER' => bless( { 'impcount' => 0, 'line' => '5', 'prods' => [ bless( { 'dircount' => 0,