Skip to content

Instantly share code, notes, and snippets.

@hosiawak
Created June 12, 2011 12:53
Show Gist options
  • Select an option

  • Save hosiawak/1021515 to your computer and use it in GitHub Desktop.

Select an option

Save hosiawak/1021515 to your computer and use it in GitHub Desktop.

Revisions

  1. hosiawak revised this gist Jun 12, 2011. 1 changed file with 6 additions and 1 deletion.
    7 changes: 6 additions & 1 deletion irb_session.rb
    Original file line number Diff line number Diff line change
    @@ -167,4 +167,9 @@ def process_block_pass(line, method_send, body)
    rbx-head :007 > [1,2].map(&:to_s)
    => ["1", "2"]

    :))
    :))

    If you want to learn more read about the bytecode compiler here:

    http://rubini.us/doc/en/bytecode-compiler/

  2. hosiawak revised this gist Jun 12, 2011. 1 changed file with 7 additions and 0 deletions.
    7 changes: 7 additions & 0 deletions irb_session.rb
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,10 @@
    # The goal is to not use the regular Symbol#to_proc
    # (which is to_proc method in the Symbol class)
    # but rather use an Abstract Syntax Tree transformation to
    # transform map(&:to_s) into map {|x| x.to_s}
    # This can be achieved by using the flexible compiler architecture
    # in Rubinius and is presented below.
    #
    # First we undefine the regular Symbol#to_proc

    class Symbol
  3. hosiawak revised this gist Jun 12, 2011. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion irb_session.rb
    Original file line number Diff line number Diff line change
    @@ -154,7 +154,8 @@ def process_block_pass(line, method_send, body)
    @variable: nil
    @line: 1

    Woohoo, we can see that our map(&:to_s) AST was transformed into the desired map {|x| x.to_s} form. Does it work ?, Let's see:
    # Woohoo, we can see that our map(&:to_s) AST was transformed into the desired map {|x| x.to_s} form.
    # Does it work ?, Let's see:

    rbx-head :007 > [1,2].map(&:to_s)
    => ["1", "2"]
  4. hosiawak revised this gist Jun 12, 2011. 1 changed file with 3 additions and 8 deletions.
    11 changes: 3 additions & 8 deletions irb_session.rb
    Original file line number Diff line number Diff line change
    @@ -92,12 +92,7 @@ def process_block_pass(line, method_send, body)
    receiver = AST::LocalVariableAccess.new line, :x
    body = AST::Send.new line, receiver, body.value
    iter = AST::Iter.new line, arguments, body
    if method_send
    method_send.block = iter
    method_send
    else
    iter
    end
    super(line, method_send, iter)
    else
    super
    end
    @@ -159,9 +154,9 @@ def process_block_pass(line, method_send, body)
    @variable: nil
    @line: 1

    Woo hoo, we can see that our map(&:to_s) AST was transformed into map {|x| x.to_s} form. Does it work ?, Let's see:
    Woohoo, we can see that our map(&:to_s) AST was transformed into the desired map {|x| x.to_s} form. Does it work ?, Let's see:

    rbx-head :007 > [1,2].map(&:to_s)
    => ["1", "2"]

    Yep, it does :)
    :))
  5. hosiawak revised this gist Jun 12, 2011. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion irb_session.rb
    Original file line number Diff line number Diff line change
    @@ -34,7 +34,7 @@ def to_proc
    @value: to_s
    @line: 1

    and AST for our desired form: map {|x| x.to_s}
    # and AST for our desired form: map {|x| x.to_s}

    rbx-head :005 > Rubinius::Melbourne.parse_string("map {|x| x.to_s}").ascii_graph
    Send
  6. hosiawak created this gist Jun 12, 2011.
    167 changes: 167 additions & 0 deletions irb_session.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,167 @@
    # First we undefine the regular Symbol#to_proc

    class Symbol
    def to_proc
    raise "undefined"
    end
    end

    # Make sure it doesn't work
    rbx-head :006 > [1,2].map(&:to_s)
    ArgumentError: Unable to convert :to_s to a Proc
    from Proc.__from_block__ at kernel/common/proc.rb:12
    from { } in Object#irb_binding at (irb):6
    from Rubinius::BlockEnvironment#call_on_instance at kernel/common/block_environment.rb:72

    # OK, now to see what we need to transform in terms of AST
    # we create the AST for map(&:to_s)

    rbx-head :004 > Rubinius::Melbourne.parse_string("map(&:to_s)").ascii_graph
    Send
    @name: map
    @privately: true
    @check_for_local: false
    @vcall_style: false
    @line: 1
    @receiver: \
    Self
    @line: 1
    @block: \
    BlockPass
    @line: 1
    @body: \
    SymbolLiteral
    @value: to_s
    @line: 1

    and AST for our desired form: map {|x| x.to_s}

    rbx-head :005 > Rubinius::Melbourne.parse_string("map {|x| x.to_s}").ascii_graph
    Send
    @name: map
    @privately: true
    @check_for_local: false
    @vcall_style: false
    @line: 1
    @receiver: \
    Self
    @line: 1
    @block: \
    Iter
    @line: 1
    @arguments: \
    IterArguments
    @arity: 1
    @optional: 0
    @required_args: 1
    @splat_index: nil
    @splat: nil
    @prelude: single
    @line: 1
    @block: nil
    @arguments: \
    LocalVariableAssignment
    @value: nil
    @name: x
    @variable: nil
    @line: 1
    @body: \
    Send
    @name: to_s
    @privately: false
    @check_for_local: false
    @vcall_style: false
    @block: nil
    @line: 1
    @receiver: \
    LocalVariableAccess
    @name: x
    @variable: nil
    @line: 1

    # Then we define a custom processor for AST::BlockPass
    # which will transform map(&:to_s) into map {|x| x.to_s}
    # directly on the AST

    class Macros < Melbourne
    def process_block_pass(line, method_send, body)
    if body.class == AST::SymbolLiteral
    # Symbol#to_proc
    name = body.value
    arguments = AST::IterArguments.new(line, AST::LocalVariableAssignment.new(line, :x, nil))
    receiver = AST::LocalVariableAccess.new line, :x
    body = AST::Send.new line, receiver, body.value
    iter = AST::Iter.new line, arguments, body
    if method_send
    method_send.block = iter
    method_send
    else
    iter
    end
    else
    super
    end
    end
    end

    # We can try it out first:

    rbx-head :006 > Rubinius::Macros.parse_string("map(&:to_s)").ascii_graph
    Send
    @name: map
    @privately: true
    @check_for_local: false
    @vcall_style: false
    @line: 1
    @receiver: \
    Self
    @line: 1
    @block: \
    Iter
    @line: 1
    @arguments: \
    IterArguments
    @arity: 1
    @optional: 0
    @required_args: 1
    @splat_index: nil
    @splat: nil
    @prelude: single
    @line: 1
    @block: nil
    @arguments: \
    IterArguments
    @arity: 1
    @optional: 0
    @required_args: 1
    @splat_index: nil
    @splat: nil
    @prelude: single
    @line: 1
    @block: nil
    @arguments: \
    LocalVariableAssignment
    @value: nil
    @name: x
    @variable: nil
    @line: 1
    @body: \
    Send
    @name: to_s
    @privately: false
    @check_for_local: false
    @vcall_style: false
    @block: nil
    @line: 1
    @receiver: \
    LocalVariableAccess
    @name: x
    @variable: nil
    @line: 1

    Woo hoo, we can see that our map(&:to_s) AST was transformed into map {|x| x.to_s} form. Does it work ?, Let's see:

    rbx-head :007 > [1,2].map(&:to_s)
    => ["1", "2"]

    Yep, it does :)