You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
99 lines
2.4 KiB
99 lines
2.4 KiB
#!/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
|