metaprogramming/ast/manipulation.rb

tests/_examples/metaprogramming/ast/manipulation.rb in the metorex repository.

# AST Manipulation
# Demonstrates eval, parse, and runtime code generation

# 1. eval — execute code from a string
puts "=== eval ==="
puts eval("1 + 2 + 3")
x = 10
puts eval("x * 5")
# A local the eval binds belongs to the eval, so its value comes back as
# the eval's own answer rather than as a local out here.
puts eval("y = x + 20")

# 2. eval to define methods at runtime
puts ""
puts "=== eval define method ==="
eval("def double(n)\n  n * 2\nend")
puts double(7)

# 3. eval to define classes at runtime
puts ""
puts "=== eval define class ==="
eval("class Point\n  def initialize(x, y)\n    @x = x\n    @y = y\n  end\n  def to_s\n    \"(\" + @x.to_s + \", \" + @y.to_s + \")\"\n  end\nend")
p = Point.new(3, 4)
puts p.to_s

# 4. parse — get AST representation of code
puts ""
puts "=== parse ==="
ast = parse("1 + 2")
puts ast.class
puts ast.length

# 5. Code generation with eval
puts ""
puts "=== code generation ==="
eval("def add(a, b)\n  a + b\nend")
eval("def subtract(a, b)\n  a - b\nend")
eval("def multiply(a, b)\n  a * b\nend")
puts add(3, 4)
puts subtract(10, 3)
puts multiply(5, 6)

# 6. Modify class at runtime with define_method
puts ""
puts "=== runtime modification ==="
class Calculator
  def compute(a, b)
    a + b
  end
end

calc = Calculator.new()
puts calc.compute(5, 3)

Calculator.define_method("compute") do |a, b|
  a * b
end

puts calc.compute(5, 3)

Output:

=== eval ===
6
50
30

=== eval define method ===
14

=== eval define class ===
(3, 4)

=== parse ===
Array
1

=== code generation ===
7
7
30

=== runtime modification ===
8
15