Skip to content
chibash edited this page Oct 22, 2019 · 17 revisions

Yadriggy -- an embedded DSL framework for Ruby

Ruby is known as a cradle of embedded DSLs. Although the code of typical DSLs embedded in Ruby is executed by the Ruby VM, this framework helps the embedded DSL code run by its dedicated execution engine such as an interpreter or native hardware. Yadriggy provides a method for reifying a lambda expression or a method body. You can write a program that receives a lambda expression or a method, reify it to obtain its abstract syntax tree, and execute it as DSL code with semantics different from Ruby.

Yadriggy reads the source code from a file when it constructs an abstract syntax tree. It works even when the source code is written on Pry or IRuby unless a syntax error occurs.

Sample DSLs

This framework comes with example DSLs.

A Python-like DSL embedded in Ruby. It is a front-end of PyCall Ruby for exploiting a Python library from Ruby.

A C-like DSL embedded in Ruby, which is for computation offloading by translating the DSL code into C or OpenCL code.

A power-assert library built with Yadriggy.

A syntax checker for DSLs implemented with Yadriggy.

A tree walker for the abstract syntax trees (ASTs) of Yadriggy, or an implementation of Visitor pattern.

API

The following methods are the entry point of Yadriggy.

- Yadriggy::reify(proc=nil)

This gets the abstract syntax tree (AST) of the given procedure proc. If proc is nil, the block argument is converted into an abstract syntax tree. The returned ASTree object is a container holding not only the AST but also other data. To get an AST, call ASTree#tree on the ASTree object.

require 'yadriggy'

f = ->(x) { x + 1 }
p Yadriggy::reify(f)
p Yadriggy::reify { 1 + 2 }

- Yadriggy::ASTree#reify(proc=nil)

This gets the abstract syntax tree (AST) as Yadriggy::reify does. However, it memoises all the ASTs obtained before and avoids constructing multiple copies of an AST. reify always returns a unique copy of AST for each procedure given to reify. This method does not receive a block argument.

require 'yadriggy'

ast = Yadriggy::reify { 1 + 2 }
f = ->(x) { x + 1 }
ast2 = ast.reify(f)
ast3 = ast.reify(f)
p ast2 == ast3

- Yadriggy::reset_pry

This discards the code given to Pry before. This is effective only when Ruby is running with Pry or Jupyter IRuby.

Yadriggy stops working after the code with a syntax error is given to Pry or IRuby. To restart Yadriggy, you have to call Yadriggy::reset_pry.

Reference

The overview of Yadribby-Py is found here:

  • Shigeru Chiba, Foreign language interfaces by code migration, Proc. of the 18th ACM SIGPLAN International Conference on Generative Programming: Concepts and Experiences (GPCE 2019), pp. 1-13, ACM, 2019.

The idea of the reification provided by Yadriggy was proposed here:

  • Shigeru Chiba, YungYu Zhuang, Maximilian Scherr, "Deeply Reifying Running Code for Constructing a Domain-Specific Language", PPPJ'16, Article No. 1, ACM, August 2016.