Home | History | Annotate | Download | only in parser
      1 #!/usr/bin/ruby
      2 # encoding: utf-8
      3 
      4 require 'antlr3/test/functional'
      5 
      6 class TestCalcParser < ANTLR3::Test::Functional
      7   inline_grammar( <<-'END' )
      8     grammar TestCalc;
      9     options { language = Ruby; }
     10     
     11     @parser::init {
     12       @reported_errors = []
     13     }
     14     
     15     @parser::members {
     16       attr_reader :reported_errors
     17       
     18       def emit_error_message(msg)
     19         @reported_errors << msg
     20       end
     21     }
     22     
     23     evaluate returns [result]: r=expression { $result = $r.result };
     24     
     25     expression returns [result]:
     26                r=mult { $result = $r.result }
     27         (
     28           '+' r2=mult { $result += $r2.result }
     29         | '-' r2=mult { $result -= $r2.result }
     30         )*
     31         ;
     32     
     33     mult returns [result]:
     34                r=log { $result = $r.result }
     35         (
     36           '*' r2=log {$result *= $r2.result}
     37         | '/' r2=log {$result /= $r2.result}
     38         | '%' r2=log {$result \%= $r2.result}
     39         )*
     40         ;
     41     
     42     log returns [result]: 'ln' r=exp {$result = Math.log($r.result)}
     43         | r=exp {$result = $r.result}
     44         ;
     45     
     46     exp returns [result]: r=atom { $result = $r.result } ('^' r2=atom { $result **= $r2.result } )?
     47         ;
     48     
     49     atom returns [result]:
     50         n=INTEGER {$result = Integer($n.text)}
     51       | n=DECIMAL {$result = Float($n.text)} 
     52       | '(' r=expression {$result = $r.result} ')'
     53       | 'PI' {$result = Math::PI}
     54       | 'E' {$result = Math::E}
     55       ;
     56     
     57     INTEGER: DIGIT+;
     58     
     59     DECIMAL: DIGIT+ '.' DIGIT+;
     60     
     61     fragment
     62     DIGIT: '0'..'9';
     63     
     64     WS: (' ' | '\n' | '\t')+ {$channel = HIDDEN};
     65   END
     66   
     67   def evaluate( expression )
     68     lexer  = TestCalc::Lexer.new( expression )
     69     parser = TestCalc::Parser.new lexer
     70     value = parser.evaluate
     71     errors = parser.reported_errors
     72     return [ value, errors ]
     73   end
     74   
     75   tests = %[
     76     1 + 2            = 3
     77     1 + 2 * 3        = 7
     78     10 / 2           = 5
     79     6 + 2*(3+1) - 4  = 10
     80   ].strip!.split( /\n/ ).map { |line| 
     81     expr, val = line.strip.split( /\s+=\s+/, 2 )
     82     [ expr, Integer( val ) ]
     83   }
     84   
     85   tests.each do |expression, true_value|
     86     example "should parse '#{ expression }'" do
     87       parser_value, errors = evaluate( expression )
     88       parser_value.should == true_value
     89     end
     90   end
     91   
     92   example "badly formed input" do
     93     val, errors = evaluate "6 - (2*1"
     94     
     95     errors.should have( 1 ).thing
     96     errors.first.should =~ /mismatched/
     97   end
     98 end
     99