{"id":422,"date":"2013-11-17T15:31:40","date_gmt":"2013-11-17T20:31:40","guid":{"rendered":"http:\/\/www.virtualroadside.com\/blog\/?p=422"},"modified":"2013-11-17T15:32:10","modified_gmt":"2013-11-17T20:32:10","slug":"tool-to-implement-ruby-inspired-dsls-in-python","status":"publish","type":"post","link":"https:\/\/www.virtualroadside.com\/blog\/index.php\/2013\/11\/17\/tool-to-implement-ruby-inspired-dsls-in-python\/","title":{"rendered":"Tool to implement ruby-inspired DSLs in python"},"content":{"rendered":"<p>This weekend, I created library to aid developers in creating neat little embedded DSLs when using python, without having to do any complex parsing or anything like that. The resulting DSLs look a bit more like english than python does.<\/p>\n<p>The idea for this was inspired by some ruby stuff that I&#8217;ve been using lately. I&#8217;ve been using ruby quite a bit lately, and while I am still not a huge fan of the language, I do like the idea of easy to code up DSLs that I can use to populate objects without too much effort. Since I wanted a DSL for a python project I was working on, I played with a few ideas and ended up with this tool.<!--more-->First, create some very basic python objects, and a file with the DSL to pass to dsltool. Here&#8217;s a sample recipe DSL for baking yummy cookies.<\/p>\n<pre>\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0author = 'Mom'\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0level = 'Easy'\r\n\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0with Instructions('Yummy cookies'):\r\n\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 preheat_oven = '375F'\r\n\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 with Ingredients:\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 add += '2 1\/4 cups', 'flour'\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 add += '1 tsp', 'baking soda'\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 add += '1 tsp', 'salt'\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 add += '1 cup', 'butter'\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 add += '3\/4 cup', 'sugar'\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 add += '3\/4 cup', 'brown sugar'\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 add += '1 tsp', 'vanilla extract'\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 add += '2', 'eggs'\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 add += '2 cups', 'chocolate chips'\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 add += '1 cup', 'nuts'<\/pre>\n<p>And the implementation to parse that dsl is super simple:<\/p>\n<pre>\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0import dsltool\r\n\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0class Ingredients(object):\r\n\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 def __init__(self):\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 self._items = []\r\n\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 @property\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 def add(self):\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 return dsltool.add_to_list(self._items)\r\n\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 @add.setter\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 def add(self, value):\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 self._items = value\r\n\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0class Instructions(object):\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 preheat_oven = None\r\n\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 ingredients = Ingredients\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 steps = Steps\r\n\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 def __init__(self, name):\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 self.name = name\r\n\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0class Recipe(object):\r\n\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 author = None\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 level = None\r\n\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 def __init__(self):\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 self.instructions = Instructions\r\n\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0if __name__ == '__main__':\r\n\u00c2\u00a0\u00c2\u00a0 \u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0 obj = dsltool.parse_dsl_file('cookies.pydsl', Recipe)<\/pre>\n<p>The source distribution comes with an example recipe DSL that you can play with to make tasty cookies. Submit bug reports\/pull requests on <a title=\"Github site for pydsltool\" href=\"https:\/\/github.com\/virtuald\/pydsltool\">github<\/a>, or you can download it from <a title=\"Pypi site for pydsltool\" href=\"https:\/\/pypi.python.org\/pypi\/pydsltool\">pypi<\/a>, or even via pip:<\/p>\n<pre>pip install pydsltool<\/pre>\n<p>Let me know if you find it useful!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This weekend, I created library to aid developers in creating neat little embedded DSLs when using python, without having to do any complex parsing or anything like that. The resulting DSLs look a bit more like english than python does. The idea for this was inspired by some ruby stuff that I&#8217;ve been using lately. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"_links":{"self":[{"href":"https:\/\/www.virtualroadside.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/422"}],"collection":[{"href":"https:\/\/www.virtualroadside.com\/blog\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.virtualroadside.com\/blog\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.virtualroadside.com\/blog\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.virtualroadside.com\/blog\/index.php\/wp-json\/wp\/v2\/comments?post=422"}],"version-history":[{"count":2,"href":"https:\/\/www.virtualroadside.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/422\/revisions"}],"predecessor-version":[{"id":424,"href":"https:\/\/www.virtualroadside.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/422\/revisions\/424"}],"wp:attachment":[{"href":"https:\/\/www.virtualroadside.com\/blog\/index.php\/wp-json\/wp\/v2\/media?parent=422"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.virtualroadside.com\/blog\/index.php\/wp-json\/wp\/v2\/categories?post=422"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.virtualroadside.com\/blog\/index.php\/wp-json\/wp\/v2\/tags?post=422"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}