#!/usr/bin/ruby # encoding: utf-8 require 'antlr3/test/functional' class TestCalcParser < ANTLR3::Test::Functional inline_grammar( <<-'END' ) grammar TestCalc; options { language = Ruby; } @parser::init { @reported_errors = [] } @parser::members { attr_reader :reported_errors def emit_error_message(msg) @reported_errors << msg end } evaluate returns [result]: r=expression { $result = $r.result }; expression returns [result]: r=mult { $result = $r.result } ( '+' r2=mult { $result += $r2.result } | '-' r2=mult { $result -= $r2.result } )* ; mult returns [result]: r=log { $result = $r.result } ( '*' r2=log {$result *= $r2.result} | '/' r2=log {$result /= $r2.result} | '%' r2=log {$result \%= $r2.result} )* ; log returns [result]: 'ln' r=exp {$result = Math.log($r.result)} | r=exp {$result = $r.result} ; exp returns [result]: r=atom { $result = $r.result } ('^' r2=atom { $result **= $r2.result } )? ; atom returns [result]: n=INTEGER {$result = Integer($n.text)} | n=DECIMAL {$result = Float($n.text)} | '(' r=expression {$result = $r.result} ')' | 'PI' {$result = Math::PI} | 'E' {$result = Math::E} ; INTEGER: DIGIT+; DECIMAL: DIGIT+ '.' DIGIT+; fragment DIGIT: '0'..'9'; WS: (' ' | '\n' | '\t')+ {$channel = HIDDEN}; END def evaluate( expression ) lexer = TestCalc::Lexer.new( expression ) parser = TestCalc::Parser.new lexer value = parser.evaluate errors = parser.reported_errors return [ value, errors ] end tests = %[ 1 + 2 = 3 1 + 2 * 3 = 7 10 / 2 = 5 6 + 2*(3+1) - 4 = 10 ].strip!.split( /\n/ ).map { |line| expr, val = line.strip.split( /\s+=\s+/, 2 ) [ expr, Integer( val ) ] } tests.each do |expression, true_value| example "should parse '#{ expression }'" do parser_value, errors = evaluate( expression ) parser_value.should == true_value end end example "badly formed input" do val, errors = evaluate "6 - (2*1" errors.should have( 1 ).thing errors.first.should =~ /mismatched/ end end