Skip to content

digital-fabric/sirop

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

47 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Sirop

Sirop is a Ruby gem for manipulating Ruby source code. Sirop is very young, so the following information might be incomplete, out of date, or simply wrong!

Use Cases

Some of the use cases addressed by Sirop are:

  • Compile DSLs into optimized Ruby code. This is especially interesting for HTML templating DSLs in libraries like Phlex, Papercraft etc. Example
  • Get the source of a given block or method.
  • Rewrite parts of Ruby code, for implementing Ruby macros (and why not?).

Limitations

  • Sirop supports Ruby 3.2 or newer.
  • Sirop can be used only on blocks and methods defined in a file, so cannot really be used on dynamically eval'd Ruby code, or in an IRB/Pry session.

Getting the AST/source of a Ruby proc or method

To get the AST of a proc or a method, use Sirop.to_ast:

# for a proc
mul = ->(x, y) { x * y }
Sirop.to_ast(mul) #=> ...

# for a method
def foo; :bar; end
Sirop.to_ast(method(:foo)) #=> ...

To get the source of a proc or a method, use Sirop.to_source:

mul = ->(x, y) { x * y }
Sirop.to_source(mul) #=> "->(x, y) { x * y }"

def foo; :bar; end
Sirop.to_source(method(:foo)) #=> "def foo; :bar; end"

Rewriting Ruby code

You can consult the DSL compiler example. This example intercepts method calls by defining a visit_call_node method:

# Annotated with some explanations
def visit_call_node(node)
  # don't rewrite if the call has a receiver
  return super if node.receiver

  # set HTML location start
  @html_location_start ||= node.location
  # get method arguments...
  inner_text, attrs = tag_args(node)
  # and block
  block = node.block

  # emit HTML tag according to given arguments
  if inner_text
    emit_tag_open(node, attrs)
    emit_tag_inner_text(inner_text)
    emit_tag_close(node)
  elsif block
    emit_tag_open(node, attrs)
    visit(block.body)
    emit_tag_close(node)
  else
    emit_tag_open_close(node, attrs)
  end
  # set HTML location end
  @html_location_end = node.location
end

Future directions

  • Implement a macro expander with support for quote/unquote:

    trace_macro = Sirop.macro do |ast|
      source = Sirop.to_source(ast)
      quote do
        result = unquote(ast)
        puts "The result of #{source} is: #{result}"
        result
      end
    end
    
    def add(x, y)
      trace(x + y)
    end
    
    Sirop.expand_macros(method(:add), trace: trace_macro)
  • Implement a DSL compiler with hooks for easier usage in DSL libraries.

Contributing

We gladly welcome contributions from anyone! Some areas that need work currently are:

Please feel free to contribute PR's and issues