1#!/usr/bin/ruby 2# encoding: utf-8 3 4require 'antlr3/test/functional' 5 6class 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 98end 99