Решение на Осма задача от Милена Дренска

Обратно към всички решения

Към профила на Милена Дренска

Резултати

  • 4 точки от тестове
  • 0 бонус точки
  • 4 точки общо
  • 26 успешни тест(а)
  • 14 неуспешни тест(а)

Код

class Spreadsheet
#ERRORS
class Error < StandardError
end
class InvalidIndexNameError < Error
def initialize(cell_index)
message = "Invalid cell index '#{cell_index}'"
super(message)
end
end
class InvalidIndexError < Error
def initialize(cell_index)
message = "Cell '#{cell_index}' does not exist"
super(message)
end
end
class InvalidCommandError < Error
def initialize(command_name)
message = "Unknown function '#{command_name}'"
super(message)
end
end
class InvalidExpressionError < Error
def initialize(expression)
message = "Invalid expression '#{expression}'"
super(message)
end
end
class WrongNumberOfArguments < Error
def initialize(command_name, range, input)
message_exact = "Wrong number of arguments for '#{command_name}':"\
" expected #{range.first}, got #{input}"
message_range = "Wrong number of arguments for '#{command_name}':"\
" expected at least #{range.first}, got #{input}"
message = range.first == range.last ? message_exact : message_range
super(message)
end
end
#TABLE
class Table
attr_reader :matrix
COLUMN_PATTERN = /(?<column>[A-Z]+)(?<row>[0-9]+)/
def initialize(input = nil)
@matrix = []
parse_table(input) if input
end
def get_cell_data(cell_index)
position = get_valid_index(cell_index)
row = position[:row].to_i - 1
column = column_name_to_integer(position[:column]) - 1
position_exists = @matrix[row] && @matrix[row][column]
raise InvalidIndexError.new("#{cell_index}") unless position_exists
@matrix[row][column][:value]
end
def is_valid_cell_name?(position_name)
position_name.match(COLUMN_PATTERN) ? true : false
end
private
def parse_table(input)
rows = input.split("\n").delete_if { |line| line.strip.empty? }
rows.each_with_index do |row, row_index|
@matrix[row_index] = []
cols = row.scan(/(=[A-Z]+\([A-Z0-9,\s]+\)|[\S]+)([\s]*)/)
parse_columns(cols, row_index)
end
end
def parse_columns(cols, row_index)
cols.each do |cell|
separator = cell[1].empty? ? "\n" : cell[1]
@matrix[row_index] << {value: cell[0], separator: separator}
end
end
def column_name_to_integer(column_name)
number = 0
column_name.reverse.split('').reduce(1) do |pow, character|
number += (character.ord - 'A'.ord + 1) * pow
pow *= 26
end
number
end
def get_valid_index(cell_index)
unless is_valid_cell_name?(cell_index) then
raise InvalidIndexNameError.new(cell_index)
end
return cell_index.match(COLUMN_PATTERN)
end
end
#FORMULA
class Formula
PATTERN = /=(?<command>[A-Z]+)\((?<parameters>[A-Z0-9,\s-]+)\)/
COMMANDS = {
'DEVIDE' => {method: :/ , parameter_range:(2..2)},
'ADD' => {method: :+ , parameter_range:(2...Float::INFINITY)},
'MULTIPLY' => {method: :* , parameter_range:(2...Float::INFINITY)},
'SUBTRACT' => {method: :- , parameter_range: (2..2)},
'MOD' => {method: :% , parameter_range:(2..2)}
}
def initialize(command_name, parameters)
@command_name = command_name
self.command = command_name
self.parameters = parameters
end
def command=(command_name)
raise InvalidCommandError.new(command_name) unless COMMANDS[command_name]
@command = COMMANDS[command_name]
end
def parameters=(parameters)
input_is_valid = @command[:parameter_range].include? parameters.length
unless input_is_valid then
details = [@command_name, @command[:parameter_range], parameters.length]
raise WrongNumberOfArguments.new(*details)
end
@parameters = parameters
end
def result
float_result = @parameters.reduce(@command[:method])
float_result % 1 > 0 ? float_result.round(2).to_s : float_result.to_i.to_s
end
end
#SPREADSHEAT
attr_reader :table
NUMBER_PATTERN = /\=[-]*[0-9]+[.]*[0-9]*/
STRING_PATTERN = /^[^=].*$/
def initialize(input = nil)
@table = Table.new(input)
end
def empty?
!(@table.matrix.length > 0)
end
def cell_at(cell_index)
@table.get_cell_data(cell_index)
end
def [](cell_index)
cell_data = @table.get_cell_data(cell_index)
evaluate_cell(cell_data)
end
def to_s
result = @table.matrix.flatten.map do |cell|
"#{evaluate_cell(cell[:value])}#{cell[:separator]}"
end
result.join.strip
end
private
def evaluate_cell(expression)
case expression
when STRING_PATTERN then expression
when NUMBER_PATTERN then expression.sub!('=', '')
when Formula::PATTERN then parse_formula(expression)
when Table::COLUMN_PATTERN then self[expression.chomp('=')]
else raise InvalidExpressionError.new(expression)
end
end
def parse_formula(expression)
captured = expression.match(Formula::PATTERN)
command = captured[:command]
parameters = captured[:parameters].split(',').map do |data|
data.strip!
@table.is_valid_cell_name?(data) ? self[data].to_f : data.to_f
end
Formula.new(command, parameters).result
end
end

Лог от изпълнението

........FF.....F..F...F...FFF...FFFFF..F

Failures:

  1) Spreadsheet#to_s splits cells by two or more spaces
     Failure/Error: expect(Spreadsheet.new("foo  bar   42\nbaz    larodi  100").to_s).to eq "foo\tbar\t42\nbaz\tlarodi\t100"
       
       expected: "foo\tbar\t42\nbaz\tlarodi\t100"
            got: "foo  bar   42\nbaz    larodi  100"
       
       (compared using ==)
       
       Diff:
       @@ -1,3 +1,3 @@
       -foo	bar	42
       -baz	larodi	100
       +foo  bar   42
       +baz    larodi  100
     # /tmp/d20160121-5693-19dqqlk/spec.rb:40:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  2) Spreadsheet#to_s returns the evaluated spreadsheet as a table
     Failure/Error: expect(sheet.to_s).to eq \
     Spreadsheet::InvalidCommandError:
       Unknown function 'DIVIDE'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:129:in `command='
     # /tmp/d20160121-5693-19dqqlk/solution.rb:124:in `initialize'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:196:in `new'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:196:in `parse_formula'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:183:in `evaluate_cell'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:172:in `block in to_s'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:171:in `map'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:171:in `to_s'
     # /tmp/d20160121-5693-19dqqlk/spec.rb:50:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  3) Spreadsheet#[] returns the calculated value of formulae cells
     Failure/Error: expect(sheet['B1']).to eq 'ADD(2, 2)'
       
       expected: "ADD(2, 2)"
            got: "ADD(2,"
       
       (compared using ==)
     # /tmp/d20160121-5693-19dqqlk/spec.rb:102:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  4) Spreadsheet#[] raises an exception for less than two arguments passed to ADD
     Failure/Error: expect { Spreadsheet.new('=ADD()')['A1'] }.to raise_error(
       expected Spreadsheet::Error with message matching /Wrong number of arguments for 'ADD': expected at least 2, got 0/, got #<Spreadsheet::InvalidExpressionError: Invalid expression '=ADD()'> with backtrace:
         # /tmp/d20160121-5693-19dqqlk/solution.rb:185:in `evaluate_cell'
         # /tmp/d20160121-5693-19dqqlk/solution.rb:167:in `[]'
         # /tmp/d20160121-5693-19dqqlk/spec.rb:123:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-19dqqlk/spec.rb:123:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'
     # /tmp/d20160121-5693-19dqqlk/spec.rb:123:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  5) Spreadsheet#[] raises an exception for less than two arguments to MULTIPLY
     Failure/Error: expect { Spreadsheet.new('=MULTIPLY()')['A1'] }.to raise_error(
       expected Spreadsheet::Error with message matching /Wrong number of arguments for 'MULTIPLY': expected at least 2, got 0/, got #<Spreadsheet::InvalidExpressionError: Invalid expression '=MULTIPLY()'> with backtrace:
         # /tmp/d20160121-5693-19dqqlk/solution.rb:185:in `evaluate_cell'
         # /tmp/d20160121-5693-19dqqlk/solution.rb:167:in `[]'
         # /tmp/d20160121-5693-19dqqlk/spec.rb:153:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-19dqqlk/spec.rb:153:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'
     # /tmp/d20160121-5693-19dqqlk/spec.rb:153:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  6) Spreadsheet#[] divides two numbers with DIVIDE
     Failure/Error: expect(sheet['A1']).to eq('42')
     Spreadsheet::InvalidCommandError:
       Unknown function 'DIVIDE'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:129:in `command='
     # /tmp/d20160121-5693-19dqqlk/solution.rb:124:in `initialize'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:196:in `new'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:196:in `parse_formula'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:183:in `evaluate_cell'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:167:in `[]'
     # /tmp/d20160121-5693-19dqqlk/spec.rb:183:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  7) Spreadsheet#[] divides numbers via cell references
     Failure/Error: expect(sheet1['C1']).to eq('42')
     Spreadsheet::InvalidCommandError:
       Unknown function 'DIVIDE'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:129:in `command='
     # /tmp/d20160121-5693-19dqqlk/solution.rb:124:in `initialize'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:196:in `new'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:196:in `parse_formula'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:183:in `evaluate_cell'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:167:in `[]'
     # /tmp/d20160121-5693-19dqqlk/spec.rb:190:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  8) Spreadsheet#[] raises an exception when DIVIDE is called with a wrong number of arguments
     Failure/Error: expect { Spreadsheet.new('=DIVIDE(1)')['A1'] }.to raise_error(
       expected Spreadsheet::Error with message matching /Wrong number of arguments for 'DIVIDE': expected 2, got 1/, got #<Spreadsheet::InvalidCommandError: Unknown function 'DIVIDE'> with backtrace:
         # /tmp/d20160121-5693-19dqqlk/solution.rb:129:in `command='
         # /tmp/d20160121-5693-19dqqlk/solution.rb:124:in `initialize'
         # /tmp/d20160121-5693-19dqqlk/solution.rb:196:in `new'
         # /tmp/d20160121-5693-19dqqlk/solution.rb:196:in `parse_formula'
         # /tmp/d20160121-5693-19dqqlk/solution.rb:183:in `evaluate_cell'
         # /tmp/d20160121-5693-19dqqlk/solution.rb:167:in `[]'
         # /tmp/d20160121-5693-19dqqlk/spec.rb:195:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-19dqqlk/spec.rb:195:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'
     # /tmp/d20160121-5693-19dqqlk/spec.rb:195:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  9) Spreadsheet#[] adds floating point numbers with ADD
     Failure/Error: expect(Spreadsheet.new('10  =ADD(A1, 1.1)')['B1']).to eq '11.10'
       
       expected: "11.10"
            got: "10"
       
       (compared using ==)
     # /tmp/d20160121-5693-19dqqlk/spec.rb:229:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  10) Spreadsheet#[] subtracts floating point numbers with SUBTRACT
     Failure/Error: expect(Spreadsheet.new('10  =SUBTRACT(A1, 1.1)')['B1']).to eq '8.90'
       
       expected: "8.90"
            got: "10"
       
       (compared using ==)
     # /tmp/d20160121-5693-19dqqlk/spec.rb:234:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  11) Spreadsheet#[] multiplies floating point numbers with MULTIPLY
     Failure/Error: expect(Spreadsheet.new('10  =MULTIPLY(A1, 1.1)')['B1']).to eq '11'
       
       expected: "11"
            got: "10"
       
       (compared using ==)
     # /tmp/d20160121-5693-19dqqlk/spec.rb:239:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  12) Spreadsheet#[] divides floating point numbers with DIVIDE
     Failure/Error: expect(Spreadsheet.new('10  =DIVIDE(A1, 4)')['B1']).to eq '2.50'
     Spreadsheet::InvalidCommandError:
       Unknown function 'DIVIDE'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:129:in `command='
     # /tmp/d20160121-5693-19dqqlk/solution.rb:124:in `initialize'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:196:in `new'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:196:in `parse_formula'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:183:in `evaluate_cell'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:167:in `[]'
     # /tmp/d20160121-5693-19dqqlk/spec.rb:244:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  13) Spreadsheet#[] evaluates deeply-nested cell references
     Failure/Error: expect(Spreadsheet.new('10  =ADD(5, A1)  3  =DIVIDE(B1, C1)  =MOD(D1, 4)')['E1']).to eq '1'
     Spreadsheet::InvalidCommandError:
       Unknown function 'DIVIDE'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:129:in `command='
     # /tmp/d20160121-5693-19dqqlk/solution.rb:124:in `initialize'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:196:in `new'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:196:in `parse_formula'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:183:in `evaluate_cell'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:167:in `[]'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:194:in `block in parse_formula'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:192:in `map'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:192:in `parse_formula'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:183:in `evaluate_cell'
     # /tmp/d20160121-5693-19dqqlk/solution.rb:167:in `[]'
     # /tmp/d20160121-5693-19dqqlk/spec.rb:250:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  14) Spreadsheet#[] raises an exception for invalid expressions
     Failure/Error: expect { Spreadsheet.new('=FOO  100')['A1'] }.to raise_error(
       expected Spreadsheet::Error with message matching /Invalid expression 'FOO'/, got #<Spreadsheet::InvalidExpressionError: Invalid expression '=FOO'> with backtrace:
         # /tmp/d20160121-5693-19dqqlk/solution.rb:185:in `evaluate_cell'
         # /tmp/d20160121-5693-19dqqlk/solution.rb:167:in `[]'
         # /tmp/d20160121-5693-19dqqlk/spec.rb:266:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-19dqqlk/spec.rb:266:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'
     # /tmp/d20160121-5693-19dqqlk/spec.rb:266:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

Finished in 0.03317 seconds
40 examples, 14 failures

Failed examples:

rspec /tmp/d20160121-5693-19dqqlk/spec.rb:39 # Spreadsheet#to_s splits cells by two or more spaces
rspec /tmp/d20160121-5693-19dqqlk/spec.rb:43 # Spreadsheet#to_s returns the evaluated spreadsheet as a table
rspec /tmp/d20160121-5693-19dqqlk/spec.rb:98 # Spreadsheet#[] returns the calculated value of formulae cells
rspec /tmp/d20160121-5693-19dqqlk/spec.rb:118 # Spreadsheet#[] raises an exception for less than two arguments passed to ADD
rspec /tmp/d20160121-5693-19dqqlk/spec.rb:148 # Spreadsheet#[] raises an exception for less than two arguments to MULTIPLY
rspec /tmp/d20160121-5693-19dqqlk/spec.rb:180 # Spreadsheet#[] divides two numbers with DIVIDE
rspec /tmp/d20160121-5693-19dqqlk/spec.rb:186 # Spreadsheet#[] divides numbers via cell references
rspec /tmp/d20160121-5693-19dqqlk/spec.rb:194 # Spreadsheet#[] raises an exception when DIVIDE is called with a wrong number of arguments
rspec /tmp/d20160121-5693-19dqqlk/spec.rb:228 # Spreadsheet#[] adds floating point numbers with ADD
rspec /tmp/d20160121-5693-19dqqlk/spec.rb:233 # Spreadsheet#[] subtracts floating point numbers with SUBTRACT
rspec /tmp/d20160121-5693-19dqqlk/spec.rb:238 # Spreadsheet#[] multiplies floating point numbers with MULTIPLY
rspec /tmp/d20160121-5693-19dqqlk/spec.rb:243 # Spreadsheet#[] divides floating point numbers with DIVIDE
rspec /tmp/d20160121-5693-19dqqlk/spec.rb:249 # Spreadsheet#[] evaluates deeply-nested cell references
rspec /tmp/d20160121-5693-19dqqlk/spec.rb:265 # Spreadsheet#[] raises an exception for invalid expressions

История (1 версия и 0 коментара)

Милена обнови решението на 10.01.2016 15:20 (преди над 8 години)

+class Spreadsheet
+ #ERRORS
+ class Error < StandardError
+ end
+
+ class InvalidIndexNameError < Error
+ def initialize(cell_index)
+ message = "Invalid cell index '#{cell_index}'"
+ super(message)
+ end
+ end
+
+ class InvalidIndexError < Error
+ def initialize(cell_index)
+ message = "Cell '#{cell_index}' does not exist"
+ super(message)
+ end
+ end
+
+ class InvalidCommandError < Error
+ def initialize(command_name)
+ message = "Unknown function '#{command_name}'"
+ super(message)
+ end
+ end
+
+ class InvalidExpressionError < Error
+ def initialize(expression)
+ message = "Invalid expression '#{expression}'"
+ super(message)
+ end
+ end
+
+ class WrongNumberOfArguments < Error
+ def initialize(command_name, range, input)
+ message_exact = "Wrong number of arguments for '#{command_name}':"\
+ " expected #{range.first}, got #{input}"
+
+ message_range = "Wrong number of arguments for '#{command_name}':"\
+ " expected at least #{range.first}, got #{input}"
+
+ message = range.first == range.last ? message_exact : message_range
+ super(message)
+ end
+ end
+
+ #TABLE
+ class Table
+ attr_reader :matrix
+
+ COLUMN_PATTERN = /(?<column>[A-Z]+)(?<row>[0-9]+)/
+
+ def initialize(input = nil)
+ @matrix = []
+ parse_table(input) if input
+ end
+
+ def get_cell_data(cell_index)
+ position = get_valid_index(cell_index)
+ row = position[:row].to_i - 1
+ column = column_name_to_integer(position[:column]) - 1
+
+ position_exists = @matrix[row] && @matrix[row][column]
+ raise InvalidIndexError.new("#{cell_index}") unless position_exists
+ @matrix[row][column][:value]
+ end
+
+ def is_valid_cell_name?(position_name)
+ position_name.match(COLUMN_PATTERN) ? true : false
+ end
+
+ private
+
+ def parse_table(input)
+ rows = input.split("\n").delete_if { |line| line.strip.empty? }
+
+ rows.each_with_index do |row, row_index|
+ @matrix[row_index] = []
+ cols = row.scan(/(=[A-Z]+\([A-Z0-9,\s]+\)|[\S]+)([\s]*)/)
+ parse_columns(cols, row_index)
+ end
+ end
+
+ def parse_columns(cols, row_index)
+ cols.each do |cell|
+ separator = cell[1].empty? ? "\n" : cell[1]
+ @matrix[row_index] << {value: cell[0], separator: separator}
+ end
+ end
+
+ def column_name_to_integer(column_name)
+ number = 0
+ column_name.reverse.split('').reduce(1) do |pow, character|
+ number += (character.ord - 'A'.ord + 1) * pow
+ pow *= 26
+ end
+
+ number
+ end
+
+ def get_valid_index(cell_index)
+ unless is_valid_cell_name?(cell_index) then
+ raise InvalidIndexNameError.new(cell_index)
+ end
+
+ return cell_index.match(COLUMN_PATTERN)
+ end
+ end
+
+ #FORMULA
+
+ class Formula
+ PATTERN = /=(?<command>[A-Z]+)\((?<parameters>[A-Z0-9,\s-]+)\)/
+ COMMANDS = {
+ 'DEVIDE' => {method: :/ , parameter_range:(2..2)},
+ 'ADD' => {method: :+ , parameter_range:(2...Float::INFINITY)},
+ 'MULTIPLY' => {method: :* , parameter_range:(2...Float::INFINITY)},
+ 'SUBTRACT' => {method: :- , parameter_range: (2..2)},
+ 'MOD' => {method: :% , parameter_range:(2..2)}
+ }
+
+ def initialize(command_name, parameters)
+ @command_name = command_name
+ self.command = command_name
+ self.parameters = parameters
+ end
+
+ def command=(command_name)
+ raise InvalidCommandError.new(command_name) unless COMMANDS[command_name]
+ @command = COMMANDS[command_name]
+ end
+
+ def parameters=(parameters)
+ input_is_valid = @command[:parameter_range].include? parameters.length
+ unless input_is_valid then
+ details = [@command_name, @command[:parameter_range], parameters.length]
+ raise WrongNumberOfArguments.new(*details)
+ end
+ @parameters = parameters
+ end
+
+ def result
+ float_result = @parameters.reduce(@command[:method])
+ float_result % 1 > 0 ? float_result.round(2).to_s : float_result.to_i.to_s
+ end
+ end
+
+ #SPREADSHEAT
+ attr_reader :table
+ NUMBER_PATTERN = /\=[-]*[0-9]+[.]*[0-9]*/
+ STRING_PATTERN = /^[^=].*$/
+
+ def initialize(input = nil)
+ @table = Table.new(input)
+ end
+
+ def empty?
+ !(@table.matrix.length > 0)
+ end
+
+ def cell_at(cell_index)
+ @table.get_cell_data(cell_index)
+ end
+
+ def [](cell_index)
+ cell_data = @table.get_cell_data(cell_index)
+ evaluate_cell(cell_data)
+ end
+
+ def to_s
+ result = @table.matrix.flatten.map do |cell|
+ "#{evaluate_cell(cell[:value])}#{cell[:separator]}"
+ end
+ result.join.strip
+ end
+
+ private
+
+ def evaluate_cell(expression)
+ case expression
+ when STRING_PATTERN then expression
+ when NUMBER_PATTERN then expression.sub!('=', '')
+ when Formula::PATTERN then parse_formula(expression)
+ when Table::COLUMN_PATTERN then self[expression.chomp('=')]
+ else raise InvalidExpressionError.new(expression)
+ end
+ end
+
+ def parse_formula(expression)
+ captured = expression.match(Formula::PATTERN)
+ command = captured[:command]
+ parameters = captured[:parameters].split(',').map do |data|
+ data.strip!
+ @table.is_valid_cell_name?(data) ? self[data].to_f : data.to_f
+ end
+ Formula.new(command, parameters).result
+ end
+end