# frozen_string_literal: true module RubyAlgebra module Parser class Token attr_reader :type end class NumberToken < Token attr_reader :value def initialize(value) @type = :num @value = value end end class IdentifierToken < Token attr_reader :name def initialize(name) @type = :id @name = name end end class OperatorToken < Token attr_reader :op def initialize(op) @type = :op @op = op end end class ParenthesisToken < Token attr_reader :closing def initialize(closing) @type = :paren @closing = closing end end class EndToken < Token def initialize() @type = :end end end def tokenize(e) i = 0 add_implicit_mul = false while i < e.length c = e[i] if c.match?(/\p{Alpha}/) yield OperatorToken.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 i += 1 add_implicit_mul = c == ")" case c when "+" yield OperatorToken.new(:add) when "-" yield OperatorToken.new(:sub) when "*" yield OperatorToken.new(:mult) when "/" yield OperatorToken.new(:div) when "^" yield OperatorToken.new(:pow) when "(" yield ParenthesisToken.new(false) when ")" yield ParenthesisToken.new(true) end elsif c.match?(/\p{Digit}|\./) yield OperatorToken.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) else yield NumberToken.new(s.to_f) end i = j elsif c == " " i += 1 else puts "Unrecognized character at pos #{i}" i += 1 end end yield EndToken.new end def parse(expr) operators = [] out_stack = [] prev = nil 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 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)) _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 puts "Missing ( in expression" else operators.pop end else operators.push token end 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 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) end end def _parse_op_prio(op_token) case op_token.op when :add, :sub 1 when :mult, :div 2 when :pow 3 end end end end