Writing a DSL with commands split by space
We want to have a DSL where we can write statements separated with space. Like this:
1 2 3 4 5 6 |
Robot.new do
a b c
d e
f g h i
j
end.run
|
outputs
1 2 3 4 5 6 7 8 9 10 |
Go to a
Go to b
Go to c
Go to d
Go to e
Go to f
Go to g
Go to h
Go to i
Go to j
|
Since separating by space means method invocation in Ruby, we need to hack into method_missing
.
I am just posting the codes and will not give it further explanation. Look into the details yourself.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
class Robot
attr_accessor :movements
def initialize &block
@movements = []
@last_length = 0
instance_eval &block if block
rearrange
end
def method_missing name, *args
case args.size
when 0
rearrange
@movements.push result = Movement.new(name)
@last_length += 1
result
when 1
@movements.push result = Movement.new(name)
@last_length += 1
result
else
super
end
end
def rearrange
last_movements = @movements.last @last_length
@movements[@movements.size - @last_length..] = last_movements.reverse
@last_length = 0
end
def run
puts @movements
end
class Movement
attr_accessor :target
def initialize target
@target = target
end
def to_s
"Go to #@target"
end
end
end
|
Similar tricks are used in my other Ruby project alda-rb. The codes can be found on GitHub.