1
votes

Trying to write a program interactively which can take inputs from command line as an expression or attributes like -

irb : 3+2 
Should evaluate to => 5
Attribute 
irb : abc = 1

=> 1
irb : jkl(or def) = 1

=> 1
irb : abc + def

=> 2

Also the evaluation should take place once user inputs blank line.

My efforts : I created a method attr_accessor which iterates through the array of *secret passed to it, and calls define_method on each attr, creating an instance variable getter and setter for each attribute.

Part of code working : I made a success in evaluating the expressions and returning string values.

irb : 3+2 Should evaluate to => 5

irb : True => True

But still stuck with evaluation of assignment to attributes and unable to dynamically store those values in my interactive irb. Below expected results are not working : Attribute

irb : abc = 1

=> 1

irb : def = 1

=> 1

irb : abc + def

=> 2

Note - I don't want to use "require 'irb' " or " "require 'pry'". Can this be achieved with simple ruby code ?

My Solution:


class Demo
  def self.attr_accessor(*secret)
   secret.each do |attr|
     define_method(attr) { instance_variable_get("@#{attr}") }

     define_method("#{attr}=") { |val| instance_variable_set("@#{attr}", val) }
   end
   get_binding
 end

 def self.method_new(input)
   @object = attr_accessor(input)
 end

 def self.method(secret)
   @object = Regexp.new(/\A[\d+\-*\/=. ]+\z/).match(secret.to_s) ? eval(secret) : "Invalid expression"
   get_binding
 end

 def self.simple_method(secret)
   @object = secret
   get_binding
 end

 def self.get_binding
   binding
 end
end

user_input = ''
until user_input == 'q' do
 user_input = gets.chomp
 if user_input =~ /^.*=.*$/
   b2 = Demo.method_new(*user_input)
   puts eval('@object', b2)
 elsif user_input =~ /\A[\d+\-*\/=. ]+\z/
   b3 = Demo.method(user_input)
   puts eval('@object', b3)
 else
   b4 = Demo.simple_method(user_input)
   puts eval('@object', b4)
 end
end


Expected Result:

irb : 3+2
#note - each result evaluated after user enters blank line 
Should evaluate to => 5

Attributes ---

irb : abc = 1
#note - each result evaluated after user enters blank line 
=> 1

irb : def = 1
#note - each result evaluated after user enters blank line 
=> 1

irb : abc + def( or jkl)
#note - each result evaluated after user enters blank line 
=> 2

Actual Result : Output is "Invalid expression" for all other inputs except expressions and simple strings.

1
def is a keyword - steenslag
Thanks @steenslag but I was just providing an instance. If you assign some values to few attributes like a = 1 and b = 1. Then afterwards you try to perform some operation on these attributes. It gives out result as invalid expression. So my question revolves around, how to store values of these attributes and perform any kind of mathematical operation on them. - Nishant Yadav

1 Answers

0
votes

I believe, I have partly reached to the solution of above problem. Now I can store the values of attributes in a hash map. I tried accessing these values through keys and thus can easily store and display values for assignments like:

rb : x = 1  
=> 1
or 
rb : y = 1

But the part of code I have written for evaluating 'x + y' is trying to partition it on operator and then accessing value of each attribute. I am doing something wrong in line of code marked with comment #faulty. Due to which I got output like => x y

I am unable to access key values after partitioning. Can someone please advise on this piece of code alone ?

Solution:

class Module
  def initialize(args)

    args.each do |key, value|
      # the instance_variable_set method dynamically creates instance variables
      # with the key as the name and value as the assigned value
      instance_variable_set("@#{key}",value)

      # define_singleton_method creates a getter method with the same name as the
      # key and inside the block you define what it returns
      define_singleton_method(key){ value }

      #defining the setter method
      define_singleton_method("#{key}=") do |val|
        instance_variable_set("@#{key}", val)
      end
    end
  end
end

class Demo
  #var :bar
  def self.eval_binary_expr(expr)
  if expr =~ /^.*=.*$/
    obj = Module.new(:name => expr)
    @object1 = eval(obj.name)
    get_binding

  else

    obj = Module.new(:name => expr)
    l_operand, op, r_operand = (obj.name).partition(%r{[/*+-]}) #Faulty

    if op.empty?
      raise ArgumentError, "Invalid operation or no operation in expression: #{expr}"
    end
    case op
      when '/'; then @object1 = (l_operand / r_operand); get_binding
      when '*'; then @object1 = (l_operand * r_operand); get_binding
      when '+'; then @object1 = (l_operand + r_operand); get_binding
      when '-'; then @object1 = (l_operand - r_operand); get_binding
    end
    end
    end

  def self.method(secret)
    @object2 = Regexp.new(/\A[\d+\-*\/=. ]+\z/).match(secret.to_s) ? eval(secret) : "Invalid expression"
    get_binding
  end

  def self.new_method(secret)
    @object3 = secret
    get_binding
  end

  def self.get_binding
    binding
  end
end

user_input = ''
until user_input == 'q' do
 user_input = gets.chomp
 if user_input =~ /\A[\w+\-*\/=. ]+\z/
   b2 = Demo.eval_binary_expr(user_input)
   puts eval('@object1', b2)
 elsif user_input =~ /\A[\d+\-*\/=. ]+\z/
   b3 = Demo.method(user_input)
   puts eval('@object2', b3)
 else
   b4 = Demo.new_method(user_input)
   puts eval('@object3', b4)
 end
end