Skip to content

Instantly share code, notes, and snippets.

@akirill0v
Created July 19, 2012 22:10
Show Gist options
  • Save akirill0v/3147222 to your computer and use it in GitHub Desktop.
Save akirill0v/3147222 to your computer and use it in GitHub Desktop.
require File.join(File.dirname(__FILE__), 'token')
class Lexer
attr_reader :grammar_rules, :regex_pattern, :output, :buffer, :priority_hash
DEFAULT_GRAMMAR_RULES = {
:operators => %w[\+ \- \* \/ \^],
:brackets => %w[\( \)],
:operands => ['\d*\.\d+|0\.\d+|-0\.\d*[1-9]\d*', '\d+']
}
PRIORITY_HASH = {
"+" => 1,
"-" => 1,
"*" => 2,
"/" => 2,
"^" => 3
}
def initialize(args = {})
@grammar_rules = args.fetch(:grammar_rules, DEFAULT_GRAMMAR_RULES)
@regex_pattern = Regexp.new(@grammar_rules.values.join('|'))
@priority_hash = args.fetch(:priority, PRIORITY_HASH)
end
def split(formula, args = {})
formula.scan(@regex_pattern).flatten
end
def tokenize(formula)
case formula
when String then split(formula)
else formula
end.collect{|str| put_token(str)}
end
# Pice of http://goo.gl/O95cb
def sort_station(formula)
@output, @buffer = [], []
tokenized_form = tokenize(formula)
for token in tokenized_form do
# If the character is a number
if token.equal_rule?(@grammar_rules[:operands])
@output << token
end
# If the character is a bracket
if token.equal_rule?(@grammar_rules[:brackets])
if token.to_s == "(" #If opened bracket
@buffer.push(token)
else #If closed Bracket
stack_token = @buffer.pop
while(stack_token.to_s != "(" && @buffer.any?)
@output << stack_token unless stack_token.equal_rule?(@grammar_rules[:brackets])
stack_token = @buffer.pop
end
end
end
# If character is operator
if token.equal_rule?(@grammar_rules[:operators])
if @buffer.any?
while(priority(token) <= priority(@buffer.last))
@output << @buffer.pop
end
@buffer.push(token)
else
@buffer.push(token)
end
end
end
#When tokenized_form empty
while(stack_token = @buffer.pop)
@output << stack_token
end
@output.join(' ')
end
protected
def priority(token)
@priority_hash[token.to_s] || 0
end
def put_token(str)
token_type = (str =~ %r[#{@grammar_rules[:operators].join('|')}]) ? :operator : :operand
Token.new(str, token_type)
end
end
require File.join(File.dirname(__FILE__), 'lexer')
class PolishCalculator
attr_reader :lexer
def initialize(args = {})
@lexer = args.fetch(:lexer, Lexer.new)
end
def calculate(formula)
@buffer = []
tokenized_array = @lexer.tokenize(formula)
for token in tokenized_array do
if token.operator?
operator = token
op_r = @buffer.pop
op_l = @buffer.pop
if (op_l && op_r)
@buffer.push(eval("#{op_l} #{operator} #{op_r}"))
else
raise Exception.new "Operands count < 2. Can not eval operator #{token} !"
end
else
@buffer.push(token)
end
end
@buffer[0]
end
end
require File.join(File.dirname(__FILE__), 'token')
class Token
attr_reader :token
attr_accessor :kind
def initialize(str, kind = :operand)
@token, @type = str, kind
end
def to_s
@token
end
def equal_rule?(rule)
@token =~ %r[#{rule.join('|')}]
end
def operator?
@type == :operator
end
end
require 'minitest/autorun'
require File.join(File.dirname(__FILE__), '..', 'calculator', 'lexer')
class LexerTest < MiniTest::Unit::TestCase
def setup
@lexer = Lexer.new
end
def test_lexet_split
formula = "1+2*3.3/4"
parsed_array = @lexer.split(formula)
assert_equal %w[1 + 2 * 3.3 / 4], parsed_array
end
def test_tokenaize_array
formula = "1+2*3.3/4"
tokenaize_array = @lexer.tokenize(formula)
tokenaize_array.each do |token|
assert token.kind_of?(Token)
end
end
def test_tokenized_array_should_equal_string_array
formula = "1+2*3.3/4"
tokenaize_array = @lexer.tokenize(formula)
string_array = %w[1 + 2 * 3.3 / 4]
assert_equal string_array, tokenaize_array.map(&:to_s)
end
def test_sort_station_1
infix_notation = "(19 + 2.14) * (4.5 - 2 / 4.3)"
postfix_notation = "19 2.14 + 4.5 2 4.3 / - *"
converted_notation = @lexer.sort_station(infix_notation)
assert_equal postfix_notation, converted_notation
end
def test_sort_station_2
infix_notation = "2 / (3 - (4 + (5 * 6)))"
postfix_notation = "2 3 4 5 6 * + - /"
converted_notation = @lexer.sort_station(infix_notation)
assert_equal postfix_notation, converted_notation
end
def test_sort_station_3
infix_notation = "(8+2*5)/(1+3*2-4)"
postfix_notation = "8 2 5 * + 1 3 2 * + 4 - /"
converted_notation = @lexer.sort_station(infix_notation)
assert_equal postfix_notation, converted_notation
end
end
require 'minitest/autorun'
require File.join(File.dirname(__FILE__), '..', 'calculator', 'polish_calculator')
class PolishCalculatorTest < MiniTest::Unit::TestCase
def setup
@calculator = PolishCalculator.new
end
def test_calculate
result = 6
formula = "8 2 5 * + 1 3 2 * + 4 - /"
assert_equal result, @calculator.calculate(formula)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment