From 8a59e0a57f830a70f1b8698e9c911961d19bc9f2 Mon Sep 17 00:00:00 2001 From: Slavasil Date: Mon, 16 Mar 2026 14:35:49 +0300 Subject: [PATCH] add support for unary plus/minus --- lib/ruby_algebra/expression.rb | 17 --------- lib/ruby_algebra/parser.rb | 64 +++++++++++++++++++++++----------- 2 files changed, 44 insertions(+), 37 deletions(-) diff --git a/lib/ruby_algebra/expression.rb b/lib/ruby_algebra/expression.rb index bca8232..542ec5c 100644 --- a/lib/ruby_algebra/expression.rb +++ b/lib/ruby_algebra/expression.rb @@ -151,23 +151,6 @@ module RubyAlgebra 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 - - end - class Constant < Expression attr_reader :value diff --git a/lib/ruby_algebra/parser.rb b/lib/ruby_algebra/parser.rb index 6d58044..ef9d881 100644 --- a/lib/ruby_algebra/parser.rb +++ b/lib/ruby_algebra/parser.rb @@ -24,7 +24,7 @@ module RubyAlgebra end end - class OperatorToken < Token + class BinaryOperatorToken < Token attr_reader :op def initialize(op) @@ -33,6 +33,15 @@ module RubyAlgebra end end + class UnaryOperatorToken < Token + attr_reader :op + + def initialize(op) + @type = :unary_op + @op = op + end + end + class ParenthesisToken < Token attr_reader :closing @@ -54,34 +63,42 @@ module RubyAlgebra while i < e.length c = e[i] if c.match?(/\p{Alpha}/) - yield OperatorToken.new(:mult) if add_implicit_mul + 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 OperatorToken.new(:mult) if c == "(" && add_implicit_mul + yield BinaryOperatorToken.new(:mult) if c == "(" && add_implicit_mul i += 1 - add_implicit_mul = c == ")" case c when "+" - yield OperatorToken.new(:add) + if add_implicit_mul + yield BinaryOperatorToken.new(:add) + else + yield UnaryOperatorToken.new(:positive) + end when "-" - yield OperatorToken.new(:sub) + if add_implicit_mul + yield BinaryOperatorToken.new(:sub) + else + yield UnaryOperatorToken.new(:negative) + end when "*" - yield OperatorToken.new(:mult) + yield BinaryOperatorToken.new(:mult) when "/" - yield OperatorToken.new(:div) + yield BinaryOperatorToken.new(:div) when "^" - yield OperatorToken.new(:pow) + 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 OperatorToken.new(:mult) if add_implicit_mul + 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] @@ -109,16 +126,15 @@ module RubyAlgebra def parse(expr) operators = [] out_stack = [] - prev = nil tokenize(expr) do |token| + pp 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 + elsif token.type == :op || token.type == :unary_op p = _parse_op_prio token - while operators.length != 0 && operators.last.type == :op && (_parse_op_prio(operators.last) > p || (_parse_op_prio(operators.last) == p && token.op != :pow)) + 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) @@ -128,7 +144,7 @@ module RubyAlgebra _parse_make_op(operators, out_stack) end if operators.empty? || operators.last.type != :paren || operators.last.closing - puts "Missing ( in expression" + raise Exception.new else operators.pop end @@ -138,17 +154,21 @@ module RubyAlgebra elsif token.type == :id out_stack.push(Variable.new(token.name)) end - prev = token end out_stack[0] end def _parse_make_op(operators, out_stack) oper = operators.pop - raise Exception.new unless oper.type == :op - raise Exception.new unless out_stack.length >= 2 - b = out_stack.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 + + puts "make unary op #{oper.op} from (#{a})" if oper.type == :unary_op + puts "make binary op #{oper.op} from (#{a}), (#{b})" if oper.type == :op + case oper.op when :add out_stack.push Addition.new(a, b) @@ -160,6 +180,10 @@ module RubyAlgebra out_stack.push Division.new(a, b) when :pow out_stack.push Power.new(a, b) + when :positive + out_stack.push a + when :negative + out_stack.push Multiplication.new(Constant.new(-1), a) end end @@ -169,7 +193,7 @@ module RubyAlgebra 1 when :mult, :div 2 - when :pow + when :pow, :positive, :negative 3 end end