⚠️ Warning: This is a draft ⚠️
This means it might contain formatting issues, incorrect code, conceptual problems, or other severe issues.
If you want to help to improve and eventually enable this page, please fork RosettaGit's repository and open a merge request on GitHub.
[[Category:Ruby]] Code for parsing and evaluating RPN expressions. See
- [[Parsing/RPN calculator algorithm]]
- [[Parsing/RPN to infix conversion]]
- [[Parsing/Shunting-yard algorithm]]
class RPNExpression # Set up the table of known operators Operator = Struct.new(:precedence, :associativity, :english, :ruby_operator) class Operator def left_associative?; associativity == :left; end def <(other) if left_associative? precedence <= other.precedence else precedence < other.precedence end end end Operators = { "+" => Operator.new(2, :left, "ADD", "+"), "-" => Operator.new(2, :left, "SUB", "-"), "*" => Operator.new(3, :left, "MUL", "*"), "/" => Operator.new(3, :left, "DIV", "/"), "^" => Operator.new(4, :right, "EXP", "**"), } # create a new object def initialize(str) @expression = str @infix_tree = nil @value = nil end attr_reader :expression # convert an infix expression into RPN def self.from_infix(expression) debug "\nfor Infix expression: #{expression}\nTerm\tAction\tOutput\tStack" rpn_expr = [] op_stack = [] tokens = expression.split until tokens.empty? term = tokens.shift if Operators.has_key?(term) op2 = op_stack.last if Operators.has_key?(op2) and Operators[term] < Operators[op2] rpn_expr << op_stack.pop debug "#{term}\t#{Operators[op2].english}\t#{rpn_expr}\t#{op_stack}\t#{op2} has higher precedence than #{term}" end op_stack << term debug "#{term}\tPUSH OP\t#{rpn_expr}\t#{op_stack}" elsif term == "(" op_stack << term debug "#{term}\tOPEN_P\t#{rpn_expr}\t#{op_stack}" elsif term == ")" until op_stack.last == "(" rpn_expr << op_stack.pop debug "#{term}\t#{Operators[rpn_expr.last].english}\t#{rpn_expr}\t#{op_stack}\tunwinding parenthesis" end op_stack.pop debug "#{term}\tCLOSE_P\t#{rpn_expr}\t#{op_stack}" else rpn_expr << term debug "#{term}\tPUSH V\t#{rpn_expr}\t#{op_stack}" end end until op_stack.empty? rpn_expr << op_stack.pop end obj = self.new(rpn_expr.join(" ")) debug "RPN = #{obj.to_s}" obj end # calculate the value of an RPN expression def eval return @value unless @value.nil? debug "\nfor RPN expression: #{expression}\nTerm\tAction\tStack" stack = [] expression.split.each do |term| if Operators.has_key?(term) a, b = stack.pop(2) raise ArgumentError, "not enough operands on the stack" if b.nil? a = a.to_f if term == "/" op = (term == "^" ? "**" : term) stack.push(a.method(op).call(b)) debug "#{term}\t#{Operators[term].english}\t#{stack}" else begin number = Integer(term) rescue Float(term) rescue ArgumentError raise ArgumentError, "cannot handle term: #{term}" end stack.push(number) debug "#{number}\tPUSH\t#{stack}" end end @value = stack.pop debug "Value = #@value" @value end private # convert an RPN expression into an AST def to_infix_tree return @infix_tree unless @infix_tree.nil? debug "\nfor RPN expression: #{expression}\nTerm\tAction\tStack" stack = [] expression.split.each do |term| if Operators.has_key?(term) a, b = stack.pop(2) raise ArgumentError, "not enough operands on the stack" if b.nil? op = InfixNode.new(term) op.left = a op.right = b stack.push(op) debug "#{term}\t#{Operators[term].english}\t#{stack.inspect}" else begin Integer(term) rescue Float(term) rescue ArgumentError raise ArgumentError, "cannot handle term: #{term}" end stack.push(InfixNode.new(term)) debug "#{term}\tPUSH\t#{stack.inspect}" end end @infix_tree = stack.pop end public # express the AST as a string def to_infix expr = to_infix_tree.to_s debug "Infix = #{expr}" expr end # express the AST as a string, but in a form that allows Ruby to evaluate it def to_ruby expr = to_infix_tree.to_ruby debug "Ruby = #{expr}" expr end def to_s expression end private class InfixNode def initialize(value) @value = value @left = nil @right = nil end attr_reader :value attr_accessor :left, :right def leaf? left.nil? and right.nil? end def to_s; to_string(false); end def to_ruby; to_string(true); end def to_string(to_ruby) result = [] result << display_child(left, to_ruby, (to_ruby and value == "/")) result << (to_ruby ? Operators[value].ruby_operator : value) result << display_child(right, to_ruby) result.join(" ") end def display_child(child, to_ruby, need_float = false) result = if child.leaf? child.value elsif Operators[child.value].precedence < Operators[value].precedence "( #{child.to_string(to_ruby)} )" else child.to_string(to_ruby) end result += ".to_f" if need_float result end def inspect str = "node[#{value}]" str << "<left=#{left.inspect}, right=#{right.inspect}>" unless leaf? str end end end def debug(msg) puts msg if $DEBUG end require 'test/unit' class TestRPNExpression < Test::Unit::TestCase def setup @rpn_expr = "3 4 2 * 1 5 - 2 3 ^ ^ / +" @infix_expr = "3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3" @ruby_expr = "3 + 4 * 2.to_f / ( 1 - 5 ) ** 2 ** 3" @value = 3.0001220703125 end def test_eval rpn = RPNExpression.new @rpn_expr value = rpn.eval assert_equal @value, value end def test_rpn_to_infix rpn = RPNExpression.new @rpn_expr infix = rpn.to_infix assert_equal @infix_expr, infix assert_equal @value, eval(rpn.to_ruby) end def test_infix_to_rpn rpn = RPNExpression.from_infix @infix_expr assert_equal @rpn_expr, rpn.to_s end def test_other_expressions old_debug = $DEBUG $DEBUG = false rpn = ["56 34 213.7 + * 678 -", "1 56 35 + 16 9 - / +"] infix = ["56 * ( 34 + 213.7 ) - 678", "1 + ( 56 + 35 ) / ( 16 - 9 )"] value = ["13193.2", "14.0"] [0, 1].each do |idx| obj = RPNExpression.new rpn[idx] assert_equal value[idx], "%.1f" % obj.eval assert_equal infix[idx], obj.to_infix assert_equal value[idx], "%.1f" % eval(obj.to_ruby) obj = RPNExpression.from_infix infix[idx] assert_equal rpn[idx], obj.to_s end $DEBUG = old_debug end end
Running with $DEBUG on (ruby -d rpn.rb
) gives:
Run options:
# Running tests:
for RPN expression: 3 4 2 * 1 5 - 2 3 ^ ^ / +
Term Action Stack
3 PUSH [3]
4 PUSH [3, 4]
2 PUSH [3, 4, 2]
* MUL [3, 8]
1 PUSH [3, 8, 1]
5 PUSH [3, 8, 1, 5]
- SUB [3, 8, -4]
2 PUSH [3, 8, -4, 2]
3 PUSH [3, 8, -4, 2, 3]
^ EXP [3, 8, -4, 8]
^ EXP [3, 8, 65536]
/ DIV [3, 0.0001220703125]
+ ADD [3.0001220703125]
Value = 3.0001220703125
.
for Infix expression: 3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3
Term Action Output Stack
3 PUSH V ["3"] []
+ PUSH OP ["3"] ["+"]
4 PUSH V ["3", "4"] ["+"]
* PUSH OP ["3", "4"] ["+", "*"]
2 PUSH V ["3", "4", "2"] ["+", "*"]
/ MUL ["3", "4", "2", "*"] ["+"] * has higher precedence than /
/ PUSH OP ["3", "4", "2", "*"] ["+", "/"]
( OPEN_P ["3", "4", "2", "*"] ["+", "/", "("]
1 PUSH V ["3", "4", "2", "*", "1"] ["+", "/", "("]
- PUSH OP ["3", "4", "2", "*", "1"] ["+", "/", "(", "-"]
5 PUSH V ["3", "4", "2", "*", "1", "5"] ["+", "/", "(", "-"]
) SUB ["3", "4", "2", "*", "1", "5", "-"] ["+", "/", "("] unwinding parenthesis
) CLOSE_P ["3", "4", "2", "*", "1", "5", "-"] ["+", "/"]
^ PUSH OP ["3", "4", "2", "*", "1", "5", "-"] ["+", "/", "^"]
2 PUSH V ["3", "4", "2", "*", "1", "5", "-", "2"] ["+", "/", "^"]
^ PUSH OP ["3", "4", "2", "*", "1", "5", "-", "2"] ["+", "/", "^", "^"]
3 PUSH V ["3", "4", "2", "*", "1", "5", "-", "2", "3"] ["+", "/", "^", "^"]
RPN = 3 4 2 * 1 5 - 2 3 ^ ^ / +
..
for RPN expression: 3 4 2 * 1 5 - 2 3 ^ ^ / +
Term Action Stack
3 PUSH [node[3]]
4 PUSH [node[3], node[4]]
2 PUSH [node[3], node[4], node[2]]
* MUL [node[3], node[*]<left=node[4], right=node[2]>]
1 PUSH [node[3], node[*]<left=node[4], right=node[2]>, node[1]]
5 PUSH [node[3], node[*]<left=node[4], right=node[2]>, node[1], node[5]]
- SUB [node[3], node[*]<left=node[4], right=node[2]>, node[-]<left=node[1], right=node[5]>]
2 PUSH [node[3], node[*]<left=node[4], right=node[2]>, node[-]<left=node[1], right=node[5]>, node[2]]
3 PUSH [node[3], node[*]<left=node[4], right=node[2]>, node[-]<left=node[1], right=node[5]>, node[2], node[3]]
^ EXP [node[3], node[*]<left=node[4], right=node[2]>, node[-]<left=node[1], right=node[5]>, node[^]<left=node[2], right=node[3]>]
^ EXP [node[3], node[*]<left=node[4], right=node[2]>, node[^]<left=node[-]<left=node[1], right=node[5]>, right=node[^]<left=node[2], right=node[3]>>]
/ DIV [node[3], node[/]<left=node[*]<left=node[4], right=node[2]>, right=node[^]<left=node[-]<left=node[1], right=node[5]>, right=node[^]<left=node[2], right=node[3]>>>]
+ ADD [node[+]<left=node[3], right=node[/]<left=node[*]<left=node[4], right=node[2]>, right=node[^]<left=node[-]<left=node[1], right=node[5]>, right=node[^]<left=node[2], right=node[3]>>>>]
Infix = 3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3
Ruby = 3 + 4 * 2.to_f / ( 1 - 5 ) ** 2 ** 3
.
Finished tests in 0.012002s, 333.2831 tests/s, 999.8494 assertions/s.
4 tests, 12 assertions, 0 failures, 0 errors, 0 skips