replace expressions with polynomials, add interpreter command class, rewrite parser
This commit is contained in:
@@ -1,9 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'ruby_algebra/version'
|
||||
require_relative 'ruby_algebra/expression'
|
||||
require_relative 'ruby_algebra/command'
|
||||
require_relative 'ruby_algebra/polynomial'
|
||||
require_relative 'ruby_algebra/parser'
|
||||
|
||||
module RubyAlgebra
|
||||
end
|
||||
|
||||
46
lib/ruby_algebra/command.rb
Normal file
46
lib/ruby_algebra/command.rb
Normal file
@@ -0,0 +1,46 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RubyAlgebra
|
||||
# Команда интерпретатора
|
||||
class Command
|
||||
end
|
||||
|
||||
# Команда присвоения
|
||||
class AssignmentCommand < Command
|
||||
attr_reader :lhs, :operation, :operand1, :operand2
|
||||
|
||||
def initialize(lhs, operation, operand1, operand2)
|
||||
@lhs = lhs
|
||||
@operation = operation
|
||||
@operand1 = operand1
|
||||
@operand2 = operand2
|
||||
end
|
||||
|
||||
def to_s
|
||||
result = @lhs.is_a?(Array) ? @lhs.join(', ') : @lhs.to_s
|
||||
result += ' := '
|
||||
result += @operand1.to_s
|
||||
case @operation
|
||||
when :add
|
||||
result += ' + '
|
||||
when :sub
|
||||
result += ' - '
|
||||
when :mult, :scale
|
||||
result += ' * '
|
||||
when :div
|
||||
result += ' / '
|
||||
end
|
||||
result += @operand2.to_s if @operation != :assign
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
# Команда вывода на экран
|
||||
class DisplayCommand < Command
|
||||
attr_reader :polynomial
|
||||
|
||||
def initialize(polynomial)
|
||||
@polynomial = polynomial
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,375 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RubyAlgebra
|
||||
class Expression
|
||||
def to_s
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def type
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def op_priority
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def op_assoc_type
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def diff(v)
|
||||
raise NotImplementedError, "#{self.class}#diff not implemented"
|
||||
end
|
||||
|
||||
def evaluate
|
||||
raise NotImplementedError, "#{self.class}#evaluate not implemented"
|
||||
end
|
||||
|
||||
def constant?
|
||||
raise NotImplementedError, "#{self.class}#constant? not implemented"
|
||||
end
|
||||
end
|
||||
|
||||
class Addition < Expression
|
||||
attr_reader :lhs, :rhs
|
||||
|
||||
def initialize(lhs, rhs)
|
||||
raise ArgumentError unless lhs.is_a?(Expression) && rhs.is_a?(Expression)
|
||||
|
||||
@lhs = lhs
|
||||
@rhs = rhs
|
||||
end
|
||||
|
||||
def to_s
|
||||
need_parentheses_left = lhs.op_priority < op_priority || (lhs.op_priority == op_priority && op_assoc_type == :right)
|
||||
need_parentheses_right = rhs.op_priority < op_priority || (rhs.op_priority == op_priority && op_assoc_type == :left)
|
||||
|
||||
result = need_parentheses_left ? "(#{lhs}) + " : "#{lhs} + "
|
||||
result + (need_parentheses_right ? "(#{rhs})" : rhs.to_s)
|
||||
end
|
||||
|
||||
def type
|
||||
:add
|
||||
end
|
||||
|
||||
def op_priority
|
||||
10
|
||||
end
|
||||
|
||||
def op_assoc_type
|
||||
:left
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
return false if other.type != type
|
||||
|
||||
lhs == other.lhs && rhs == other.rhs
|
||||
end
|
||||
|
||||
def diff(v)
|
||||
Addition.new(@lhs.diff(v), @rhs.diff(v))
|
||||
end
|
||||
|
||||
def evaluate
|
||||
lhs_val = @lhs.evaluate
|
||||
rhs_val = @rhs.evaluate
|
||||
return nil if lhs_val.nil? || rhs_val.nil?
|
||||
|
||||
lhs_val + rhs_val
|
||||
end
|
||||
|
||||
def constant?
|
||||
@lhs.constant? && @rhs.constant?
|
||||
end
|
||||
end
|
||||
|
||||
class Subtraction < Addition
|
||||
attr_reader :lhs, :rhs
|
||||
|
||||
def to_s
|
||||
need_parentheses_left = lhs.op_priority < op_priority || (lhs.op_priority == op_priority && op_assoc_type == :right)
|
||||
need_parentheses_right = rhs.op_priority < op_priority || (rhs.op_priority == op_priority && op_assoc_type == :left)
|
||||
|
||||
result = need_parentheses_left ? "(#{lhs}) - " : "#{lhs} - "
|
||||
result + (need_parentheses_right ? "(#{rhs})" : rhs.to_s)
|
||||
end
|
||||
|
||||
def type
|
||||
:sub
|
||||
end
|
||||
|
||||
def diff(v)
|
||||
Subtraction.new(@lhs.diff(v), @rhs.diff(v))
|
||||
end
|
||||
|
||||
def evaluate
|
||||
lhs_val = @lhs.evaluate
|
||||
rhs_val = @rhs.evaluate
|
||||
return nil if lhs_val.nil? || rhs_val.nil?
|
||||
|
||||
lhs_val - rhs_val
|
||||
end
|
||||
|
||||
def constant?
|
||||
@lhs.constant? && @rhs.constant?
|
||||
end
|
||||
end
|
||||
|
||||
class Multiplication < Expression
|
||||
attr_reader :lhs, :rhs
|
||||
|
||||
def initialize(lhs, rhs)
|
||||
raise ArgumentError unless lhs.is_a?(Expression) && rhs.is_a?(Expression)
|
||||
|
||||
@lhs = lhs
|
||||
@rhs = rhs
|
||||
end
|
||||
|
||||
def to_s
|
||||
need_parentheses_left = lhs.op_priority < op_priority || (lhs.op_priority == op_priority && op_assoc_type == :right)
|
||||
need_parentheses_right = rhs.op_priority < op_priority || (rhs.op_priority == op_priority && op_assoc_type == :left)
|
||||
|
||||
if lhs.type == :constant && lhs.value == 1
|
||||
need_parentheses_right ? "(#{rhs})" : rhs.to_s
|
||||
elsif lhs.type == :constant && lhs.value == -1
|
||||
need_parentheses_right ? "-(#{rhs})" : "-#{rhs}"
|
||||
elsif (need_parentheses_right || rhs.type == :variable) && (need_parentheses_left || lhs.type == :mult || lhs.type == :constant)
|
||||
result = need_parentheses_left ? "(#{lhs})" : lhs.to_s
|
||||
result += ' ' if lhs.type == :variable && rhs.type == :variable && lhs.single_letter? && rhs.single_letter?
|
||||
result + (need_parentheses_right ? "(#{rhs})" : rhs.to_s)
|
||||
else
|
||||
result = need_parentheses_left ? "(#{lhs}) * " : "#{lhs} * "
|
||||
result + (need_parentheses_right ? "(#{rhs})" : rhs.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
def type
|
||||
:mult
|
||||
end
|
||||
|
||||
def op_priority
|
||||
20
|
||||
end
|
||||
|
||||
def op_assoc_type
|
||||
:left
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
return false if other.type != type
|
||||
|
||||
lhs == other.lhs && rhs == other.rhs
|
||||
end
|
||||
|
||||
def diff(v)
|
||||
u_prime = @lhs.diff(v)
|
||||
v_prime = @rhs.diff(v)
|
||||
term1 = Multiplication.new(u_prime, @rhs)
|
||||
term2 = Multiplication.new(@lhs, v_prime)
|
||||
Addition.new(term1, term2)
|
||||
end
|
||||
|
||||
def evaluate
|
||||
lhs_val = @lhs.evaluate
|
||||
rhs_val = @rhs.evaluate
|
||||
return nil if lhs_val.nil? || rhs_val.nil?
|
||||
|
||||
lhs_val * rhs_val
|
||||
end
|
||||
|
||||
def constant?
|
||||
@lhs.constant? && @rhs.constant?
|
||||
end
|
||||
end
|
||||
|
||||
class Division < Multiplication
|
||||
def to_s
|
||||
need_parentheses_left = lhs.op_priority < op_priority || (lhs.op_priority == op_priority && op_assoc_type == :right)
|
||||
need_parentheses_right = rhs.op_priority < op_priority || (rhs.op_priority == op_priority && op_assoc_type == :left)
|
||||
|
||||
result = need_parentheses_left ? "(#{lhs}) / " : "#{lhs} / "
|
||||
result + (need_parentheses_right ? "(#{rhs})" : rhs.to_s)
|
||||
end
|
||||
|
||||
def type
|
||||
:div
|
||||
end
|
||||
|
||||
def diff(v)
|
||||
u_prime = @lhs.diff(v)
|
||||
v_prime = @rhs.diff(v)
|
||||
|
||||
numerator = Subtraction.new(
|
||||
Multiplication.new(u_prime, @rhs),
|
||||
Multiplication.new(@lhs, v_prime)
|
||||
)
|
||||
denominator = Power.new(@rhs, Constant.new(2))
|
||||
Division.new(numerator, denominator)
|
||||
end
|
||||
|
||||
def evaluate
|
||||
lhs_val = @lhs.evaluate
|
||||
rhs_val = @rhs.evaluate
|
||||
return nil if lhs_val.nil? || rhs_val.nil?
|
||||
return nil if rhs_val == 0
|
||||
|
||||
lhs_val.to_f / rhs_val
|
||||
end
|
||||
|
||||
def constant?
|
||||
@lhs.constant? && @rhs.constant?
|
||||
end
|
||||
end
|
||||
|
||||
class Power < Expression
|
||||
attr_reader :base, :exponent
|
||||
|
||||
def initialize(base, exponent)
|
||||
raise ArgumentError unless base.is_a?(Expression) && exponent.is_a?(Expression)
|
||||
|
||||
@base = base
|
||||
@exponent = exponent
|
||||
end
|
||||
|
||||
def to_s
|
||||
need_parentheses_left = base.op_priority < op_priority || (base.op_priority == op_priority && op_assoc_type == :right)
|
||||
need_parentheses_right = exponent.op_priority < op_priority || (exponent.op_priority == op_priority && op_assoc_type == :left)
|
||||
|
||||
result = need_parentheses_left ? "(#{base}) ^ " : "#{base} ^ "
|
||||
result + (need_parentheses_right ? "(#{exponent})" : exponent.to_s)
|
||||
end
|
||||
|
||||
def type
|
||||
:pow
|
||||
end
|
||||
|
||||
def op_priority
|
||||
30
|
||||
end
|
||||
|
||||
def op_assoc_type
|
||||
:right
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
return false if other.type != type
|
||||
|
||||
base == other.base && exponent == other.exponent
|
||||
end
|
||||
|
||||
def diff(v)
|
||||
unless @exponent.is_a?(Constant)
|
||||
raise NotImplementedError, 'Дифференцирование степени с неконстантным показателем не реализовано'
|
||||
end
|
||||
|
||||
n = @exponent.value
|
||||
# (u^n)' = n * u^(n-1) * u'
|
||||
base_diff = @base.diff(v)
|
||||
new_power = Power.new(@base, Constant.new(n - 1))
|
||||
coeff = Constant.new(n)
|
||||
Multiplication.new(coeff, Multiplication.new(new_power, base_diff))
|
||||
end
|
||||
|
||||
def evaluate
|
||||
base_val = @base.evaluate
|
||||
exp_val = @exponent.evaluate
|
||||
return nil if base_val.nil? || exp_val.nil?
|
||||
|
||||
base_val**exp_val
|
||||
end
|
||||
|
||||
def constant?
|
||||
@base.constant? && @exponent.constant?
|
||||
end
|
||||
end
|
||||
|
||||
class Constant < Expression
|
||||
attr_reader :value
|
||||
|
||||
def initialize(value)
|
||||
@value = value
|
||||
end
|
||||
|
||||
def to_s
|
||||
@value.to_s
|
||||
end
|
||||
|
||||
def type
|
||||
:constant
|
||||
end
|
||||
|
||||
def op_priority
|
||||
1000
|
||||
end
|
||||
|
||||
def op_assoc_type
|
||||
nil
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
return false if other.type != type
|
||||
|
||||
value == other.value
|
||||
end
|
||||
|
||||
def diff(v)
|
||||
Constant.new(0)
|
||||
end
|
||||
|
||||
def evaluate
|
||||
@value
|
||||
end
|
||||
|
||||
def constant?
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
class Variable < Expression
|
||||
attr_reader :symbol
|
||||
|
||||
def initialize(symbol)
|
||||
@symbol = symbol
|
||||
@is_single_letter = symbol.length == 1
|
||||
end
|
||||
|
||||
def to_s
|
||||
@symbol
|
||||
end
|
||||
|
||||
def type
|
||||
:variable
|
||||
end
|
||||
|
||||
def op_priority
|
||||
1000
|
||||
end
|
||||
|
||||
def op_assoc_type
|
||||
nil
|
||||
end
|
||||
|
||||
def single_letter?
|
||||
@is_single_letter
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
return false if other.type != type
|
||||
|
||||
symbol == other.symbol
|
||||
end
|
||||
|
||||
def diff(v)
|
||||
@symbol == v ? Constant.new(1) : Constant.new(0)
|
||||
end
|
||||
|
||||
def evaluate
|
||||
nil
|
||||
end
|
||||
|
||||
def constant?
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -2,199 +2,332 @@
|
||||
|
||||
module RubyAlgebra
|
||||
module Parser
|
||||
# Токен выражения или команды
|
||||
class Token
|
||||
attr_reader :type
|
||||
|
||||
def initialize; end
|
||||
end
|
||||
|
||||
class NumberToken < Token
|
||||
attr_reader :value
|
||||
|
||||
def initialize(value)
|
||||
@type = :num
|
||||
@value = value
|
||||
# Знак сложения
|
||||
class PlusToken < Token
|
||||
def initialize
|
||||
super()
|
||||
@type = :plus
|
||||
end
|
||||
end
|
||||
|
||||
class IdentifierToken < Token
|
||||
attr_reader :name
|
||||
|
||||
def initialize(name)
|
||||
@type = :id
|
||||
@name = name
|
||||
# Знак вычитания
|
||||
class MinusToken < Token
|
||||
def initialize
|
||||
super()
|
||||
@type = :minus
|
||||
end
|
||||
end
|
||||
|
||||
class BinaryOperatorToken < Token
|
||||
attr_reader :op
|
||||
|
||||
def initialize(op)
|
||||
@type = :op
|
||||
@op = op
|
||||
# Знак умножения
|
||||
class MultiplyToken < Token
|
||||
def initialize
|
||||
super()
|
||||
@type = :mult
|
||||
end
|
||||
end
|
||||
|
||||
class UnaryOperatorToken < Token
|
||||
attr_reader :op
|
||||
|
||||
def initialize(op)
|
||||
@type = :unary_op
|
||||
@op = op
|
||||
# Знак деления
|
||||
class DivideToken < Token
|
||||
def initialize
|
||||
super()
|
||||
@type = :div
|
||||
end
|
||||
end
|
||||
|
||||
# Знак возведения в степень
|
||||
class PowerToken < Token
|
||||
def initialize
|
||||
super()
|
||||
@type = :pow
|
||||
end
|
||||
end
|
||||
|
||||
# Скобка
|
||||
class ParenthesisToken < Token
|
||||
attr_reader :closing
|
||||
|
||||
def initialize(closing)
|
||||
super()
|
||||
@type = :paren
|
||||
@closing = closing
|
||||
end
|
||||
end
|
||||
|
||||
# Знак присвоения :=
|
||||
class AssignmentToken < Token
|
||||
def initialize
|
||||
super()
|
||||
@type = :assign
|
||||
end
|
||||
end
|
||||
|
||||
# Запятая
|
||||
class CommaToken < Token
|
||||
def initialize
|
||||
super()
|
||||
@type = :comma
|
||||
end
|
||||
end
|
||||
|
||||
# Имя переменной или функции
|
||||
class IdentifierToken < Token
|
||||
attr_reader :symbol
|
||||
|
||||
def initialize(symbol)
|
||||
super()
|
||||
@type = :id
|
||||
@symbol = symbol
|
||||
end
|
||||
|
||||
def variable?
|
||||
@symbol.match?(/^\p{Ll}/)
|
||||
end
|
||||
end
|
||||
|
||||
# Целое или дробное число
|
||||
class NumberToken < Token
|
||||
attr_reader :value
|
||||
|
||||
def initialize(value)
|
||||
super()
|
||||
@type = :num
|
||||
@value = value
|
||||
end
|
||||
end
|
||||
|
||||
# Специальный токен, означающий конец ввода
|
||||
class EndToken < Token
|
||||
def initialize()
|
||||
def initialize
|
||||
super()
|
||||
@type = :end
|
||||
end
|
||||
end
|
||||
|
||||
def self.tokenize(e)
|
||||
i = 0
|
||||
add_implicit_mul = false
|
||||
while i < e.length
|
||||
c = e[i]
|
||||
if c.match?(/\p{Alpha}/)
|
||||
yield BinaryOperatorToken.new(:mult) if add_implicit_mul
|
||||
j = i + 1
|
||||
j += 1 while j < e.length && e[j].match?(/\p{Alnum}/)
|
||||
add_implicit_mul = true
|
||||
yield IdentifierToken.new(e[i...j])
|
||||
i = j
|
||||
elsif c.match?(/[+\-*\/^()]/)
|
||||
yield BinaryOperatorToken.new(:mult) if c == "(" && add_implicit_mul
|
||||
i += 1
|
||||
case c
|
||||
when "+"
|
||||
if add_implicit_mul
|
||||
yield BinaryOperatorToken.new(:add)
|
||||
else
|
||||
yield UnaryOperatorToken.new(:positive)
|
||||
# Класс для разбора выражений на токены
|
||||
class Tokenizer
|
||||
attr_reader :lookahead
|
||||
|
||||
def initialize(expr)
|
||||
@expr = expr
|
||||
@i = 0
|
||||
@lookahead = _next_token
|
||||
end
|
||||
|
||||
def next_token
|
||||
token = @lookahead
|
||||
@lookahead = _next_token unless token.type == :end
|
||||
token
|
||||
end
|
||||
|
||||
def _next_token
|
||||
puts '_next_token'
|
||||
|
||||
loop do
|
||||
return EndToken.new unless @i < @expr.length
|
||||
puts "i = #{@i}"
|
||||
c = @expr[@i]
|
||||
puts "c = #{c}"
|
||||
if c.match?(/\p{Alpha}/)
|
||||
puts 'letter'
|
||||
j = @i + 1
|
||||
j += 1 while j < @expr.length && @expr[j].match?(/\p{Alnum}/)
|
||||
token = IdentifierToken.new(@expr[@i...j])
|
||||
@i = j
|
||||
return token
|
||||
elsif c.match(%r{[+\-*/^():]})
|
||||
puts 'operator'
|
||||
token = nil
|
||||
case c
|
||||
when '+'
|
||||
token = PlusToken.new
|
||||
when '-'
|
||||
token = MinusToken.new
|
||||
when '*'
|
||||
token = MultiplyToken.new
|
||||
when '/'
|
||||
token = DivideToken.new
|
||||
when '^'
|
||||
token = PowerToken.new
|
||||
when '('
|
||||
token = ParenthesisToken.new(false)
|
||||
when ')'
|
||||
token = ParenthesisToken.new(true)
|
||||
end
|
||||
when "-"
|
||||
if add_implicit_mul
|
||||
yield BinaryOperatorToken.new(:sub)
|
||||
else
|
||||
yield UnaryOperatorToken.new(:negative)
|
||||
@i += 1
|
||||
if c == ':' && @expr[@i] == '='
|
||||
token = AssignmentToken.new
|
||||
@i += 1
|
||||
end
|
||||
when "*"
|
||||
yield BinaryOperatorToken.new(:mult)
|
||||
when "/"
|
||||
yield BinaryOperatorToken.new(:div)
|
||||
when "^"
|
||||
yield BinaryOperatorToken.new(:pow)
|
||||
when "("
|
||||
yield ParenthesisToken.new(false)
|
||||
when ")"
|
||||
yield ParenthesisToken.new(true)
|
||||
end
|
||||
add_implicit_mul = c == ")"
|
||||
elsif c.match?(/\p{Digit}|\./)
|
||||
yield BinaryOperatorToken.new(:mult) if add_implicit_mul
|
||||
j = i + 1
|
||||
j += 1 while j < e.length && e[j].match?(/\p{Digit}|\./)
|
||||
s = e[i...j]
|
||||
fl = s.count "."
|
||||
if fl > 1
|
||||
raise Exception.new
|
||||
end
|
||||
add_implicit_mul = true
|
||||
if fl == 0
|
||||
yield NumberToken.new(s.to_i)
|
||||
return token
|
||||
elsif c.match?(/\p{Digit}|\./)
|
||||
puts 'digit'
|
||||
j = @i + 1
|
||||
j += 1 while j < @expr.length && @expr[j].match?(/\p{Digit}|\./)
|
||||
s = @expr[@i...j]
|
||||
@i = j
|
||||
puts "ssss: #{s}"
|
||||
fl = s.count '.'
|
||||
raise StandardError, 'malformed number' if fl > 1
|
||||
return fl.zero? ? NumberToken.new(s.to_i) : NumberToken.new(s.to_f)
|
||||
elsif c == ' '
|
||||
puts 'whitespace'
|
||||
@i += 1
|
||||
else
|
||||
yield NumberToken.new(s.to_f)
|
||||
puts 'unknown'
|
||||
@i += 1
|
||||
end
|
||||
i = j
|
||||
elsif c == " "
|
||||
i += 1
|
||||
else
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
yield EndToken.new
|
||||
end
|
||||
|
||||
def self.parse(expr)
|
||||
operators = []
|
||||
out_stack = []
|
||||
tokenize(expr) do |token|
|
||||
if token.type == :num
|
||||
out_stack.push(Constant.new(token.value))
|
||||
elsif token.type == :end
|
||||
_parse_make_op(operators, out_stack) until operators.empty?
|
||||
elsif token.type == :op || token.type == :unary_op
|
||||
p = _parse_op_prio token
|
||||
while operators.length != 0 && (operators.last.type == :op || operators.last.type == :unary_op) && (_parse_op_prio(operators.last) > p || (_parse_op_prio(operators.last) == p && token.op != :pow && token.op != :positive && token.op != :negative))
|
||||
_parse_make_op(operators, out_stack)
|
||||
end
|
||||
operators.push(token)
|
||||
elsif token.type == :paren
|
||||
if token.closing
|
||||
until operators.empty? || (operators.last.type == :paren && !operators.last.closing)
|
||||
_parse_make_op(operators, out_stack)
|
||||
end
|
||||
if operators.empty? || operators.last.type != :paren || operators.last.closing
|
||||
raise Exception.new
|
||||
else
|
||||
operators.pop
|
||||
end
|
||||
def self.parse_command(expr)
|
||||
tokenizer = Tokenizer.new(expr)
|
||||
n = tokenizer.lookahead
|
||||
if n.type == :id && !n.variable?
|
||||
parse_assignment_command(tokenizer)
|
||||
else
|
||||
parse_display_command(tokenizer)
|
||||
end
|
||||
end
|
||||
|
||||
def self.parse_assignment_command(tokenizer)
|
||||
left_hand_side = tokenizer.next_token
|
||||
n = tokenizer.next_token
|
||||
if n.type == :assign
|
||||
operand1 = tokenizer.lookahead
|
||||
unless operand1.type == :id && !operand1.variable?
|
||||
return AssignmentCommand.new(left_hand_side.symbol, :assign, parse_polynomial(tokenizer), nil)
|
||||
end
|
||||
tokenizer.next_token
|
||||
operator = tokenizer.next_token
|
||||
raise StandardError, "unexpected token #{operator.type}, expected +, -, * or /" unless [:plus, :minus, :mult, :div].include?(operator.type)
|
||||
|
||||
operand2 = tokenizer.next_token
|
||||
unless ([:plus, :minus, :mult].include?(operator.type) && operand2.type == :id && !operand2.variable?) ||
|
||||
([:mult, :div].include?(operator.type) && operand2.type == :num)
|
||||
raise StandardError, 'unsupported operation or invalid syntax'
|
||||
end
|
||||
if operand2.type == :num
|
||||
if operator.type == :mult
|
||||
return AssignmentCommand.new(left_hand_side.symbol, :scale, operand1.symbol, operand2.value)
|
||||
else
|
||||
operators.push token
|
||||
return AssignmentCommand.new(left_hand_side.symbol, :scale, operand1.symbol, 1.0 / operand2.value)
|
||||
end
|
||||
elsif operand2.type == :id
|
||||
case operator.type
|
||||
when :plus
|
||||
return AssignmentCommand.new(left_hand_side.symbol, :add, operand1.symbol, operand2.symbol)
|
||||
when :minus
|
||||
return AssignmentCommand.new(left_hand_side.symbol, :sub, operand1.symbol, operand2.symbol)
|
||||
when :mult
|
||||
return AssignmentCommand.new(left_hand_side.symbol, :mult, operand1.symbol, operand2.symbol)
|
||||
end
|
||||
elsif token.type == :id
|
||||
out_stack.push(Variable.new(token.name))
|
||||
end
|
||||
end
|
||||
out_stack[0]
|
||||
end
|
||||
|
||||
def self._parse_make_op(operators, out_stack)
|
||||
oper = operators.pop
|
||||
unless (oper.type == :op && out_stack.length >= 2) || (oper.type == :unary_op && out_stack.length >= 1)
|
||||
raise Exception.new
|
||||
end
|
||||
b = out_stack.pop if oper.type == :op
|
||||
a = out_stack.pop
|
||||
|
||||
case oper.op
|
||||
when :add
|
||||
out_stack.push Addition.new(a, b)
|
||||
when :sub
|
||||
out_stack.push Subtraction.new(a, b)
|
||||
when :mult
|
||||
out_stack.push Multiplication.new(a, b)
|
||||
when :div
|
||||
out_stack.push Division.new(a, b)
|
||||
when :pow
|
||||
out_stack.push Power.new(a, b)
|
||||
when :positive
|
||||
out_stack.push a
|
||||
when :negative
|
||||
if a.type == :constant
|
||||
out_stack.push Constant.new(-a.value)
|
||||
else
|
||||
out_stack.push Multiplication.new(Constant.new(-1), a)
|
||||
elsif n.type == :comma
|
||||
second_lhs = tokenizer.next_token
|
||||
unless second_lhs.type == :id && !second_lhs.variable?
|
||||
raise StandardError, "unexpected token #{second_lhs.type}, expected polynomial variable"
|
||||
end
|
||||
unless tokenizer.next_token.type == :assign
|
||||
raise StandardError, "unexpected token, expected :="
|
||||
end
|
||||
operand1 = tokenizer.next_token
|
||||
unless operand1.type == :id && !operand1.variable?
|
||||
raise StandardError, "unexpected token #{operand1.type}, expected polynomial variable"
|
||||
end
|
||||
unless tokenizer.next_token.type == :div
|
||||
raise StandardError, "unexpected token, expected /"
|
||||
end
|
||||
operand2 = tokenizer.next_token
|
||||
unless operand2.type == :id && !operand2.variable?
|
||||
raise StandardError, "unexpected token #{operand2.type}, expected polynomial variable"
|
||||
end
|
||||
return AssignmentCommand.new([left_hand_side.symbol, second_lhs.symbol], :div, operand1.symbol)
|
||||
else
|
||||
raise StandardError, "unexpected token #{n.type}, expected ':=' or ','"
|
||||
end
|
||||
end
|
||||
|
||||
def self._parse_op_prio(op_token)
|
||||
case op_token.op
|
||||
when :add, :sub
|
||||
1
|
||||
when :mult, :div
|
||||
2
|
||||
when :pow, :positive, :negative
|
||||
3
|
||||
def self.parse_display_command(tokenizer)
|
||||
DisplayCommand.new(parse_polynomial(tokenizer))
|
||||
end
|
||||
|
||||
def self.parse_polynomial(tokenizer)
|
||||
puts "parse_polynomial <lookahead #{tokenizer.lookahead.type}>"
|
||||
terms = [parse_term(tokenizer)]
|
||||
while [:plus, :minus].include?(tokenizer.lookahead.type)
|
||||
sign = tokenizer.next_token.type == :plus ? +1 : -1
|
||||
terms << parse_term(tokenizer) * sign
|
||||
end
|
||||
puts 'parse_polynomial end'
|
||||
Polynomial.new(terms)
|
||||
end
|
||||
|
||||
def self.parse_term(tokenizer)
|
||||
puts "parse_term <lookahead #{tokenizer.lookahead.type}>"
|
||||
n = tokenizer.lookahead
|
||||
unless n.type == :id && n.variable? || n.type == :num
|
||||
raise StandardError, "unexpected token #{n.type}, expected number or variable"
|
||||
end
|
||||
if n.type == :num
|
||||
puts "eat coeff"
|
||||
tokenizer.next_token
|
||||
coeff = n.value
|
||||
variables = {}
|
||||
else
|
||||
coeff = 1
|
||||
v = parse_variable_factor(tokenizer)
|
||||
variables = {v[0] => v[1]}
|
||||
end
|
||||
loop do
|
||||
n = tokenizer.lookahead
|
||||
puts "<lookahead #{n.type}>"
|
||||
unless n.type == :mult || n.type == :id && n.variable?
|
||||
break
|
||||
end
|
||||
v = parse_variable_factor(tokenizer)
|
||||
variables[v[0]] = v[1]
|
||||
end
|
||||
puts 'parse_term end'
|
||||
pp [coeff, variables]
|
||||
Term.new(coeff, variables)
|
||||
end
|
||||
|
||||
def self.parse_variable_factor(tokenizer)
|
||||
puts "parse_term <lookahead #{tokenizer.lookahead.type}>"
|
||||
n = tokenizer.lookahead
|
||||
tokenizer.next_token
|
||||
puts 'eat variable'
|
||||
#n = tokenizer.next_token if n.type == :mult
|
||||
if n.type == :mult
|
||||
n = tokenizer.next_token
|
||||
puts 'eat *'
|
||||
end
|
||||
unless n.type == :id && n.variable?
|
||||
raise StandardError, "unexpected token #{n.type}, expected variable"
|
||||
end
|
||||
variable = n.symbol
|
||||
if tokenizer.lookahead.type == :pow
|
||||
tokenizer.next_token
|
||||
puts 'eat ^'
|
||||
unless tokenizer.lookahead.type == :num
|
||||
raise StandardError, "unexpected token #{n.type}, expected number"
|
||||
end
|
||||
power = tokenizer.next_token.value
|
||||
puts 'eat exponent'
|
||||
else
|
||||
power = 1
|
||||
end
|
||||
puts 'parse_variable_factor end'
|
||||
pp [variable, power]
|
||||
[variable, power]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -171,7 +171,9 @@ module RubyAlgebra
|
||||
end
|
||||
|
||||
def *(other)
|
||||
return Term.new if @coeff.zero? || other.coeff.zero?
|
||||
return Term.new if zero? || other.zero?
|
||||
|
||||
return Term.new(coeff * other, variables) unless other.is_a? Term
|
||||
|
||||
new_variables = @variables.clone
|
||||
other.variables.each do |symbol, power|
|
||||
@@ -205,11 +207,19 @@ module RubyAlgebra
|
||||
unless @variables.empty?
|
||||
first = true
|
||||
@variables.each do |var, power|
|
||||
result += if first
|
||||
"#{var}^#{power}"
|
||||
else
|
||||
" * #{var}^#{power}"
|
||||
end
|
||||
if first
|
||||
unless power == 1
|
||||
result += "#{var}^#{power}"
|
||||
else
|
||||
result += "#{var}"
|
||||
end
|
||||
else
|
||||
unless power == 1
|
||||
result += " * #{var}^#{power}"
|
||||
else
|
||||
result += " * #{var}"
|
||||
end
|
||||
end
|
||||
first = false
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user