SlideShare uma empresa Scribd logo
1 de 64
Baixar para ler offline
Declarative Internal DSLs in Lua
     A Game-Changing Experience


 Alexander Gladysh, ag@logiceditor.com
   LogicEditor.com CTO, co-founder


          Lua Workshop 2011




                                         1 / 64
Outline

   Introduction

   Ad-hoc approach

   A case study

   The ”proper” solution

   Where do we use DSLs ourselves?

   Why game-changing?

   Questions?



                                     2 / 64
Internal Declarative DSL in Lua




   namespace:method "title"
   {
     data = "here";
   }




                                  3 / 64
...Without sugar




   _G["namespace"]:method(
       "title"
     ) ({
       ["data"] = "here";
     })




                             4 / 64
Na¨ implementation
  ıve



  namespace = { }

  namespace.method = function(self, name)
      return function(data)
       -- ...do something
       -- ...with name and data
    end
  end




                                            5 / 64
Hypothetical UI description language


   ui:dialog "alert"
   {
     ui:label "message";
     ui:button "OK"
     {
       on_click = function(self)
         self:close()
       end;
     };
   }




                                       6 / 64
UI description language ”implementation”, I


   function ui:label(title)
     return function(data)
       return GUI.Label:new(title, data)
     end
   end

   function ui:button(title)
     return function(data)
       return GUI.Button:new(title, data)
     end
   end




                                              7 / 64
UI description language ”implementation”, II



   function ui:dialog(title)
     return function(data)
       local dialog = GUI.Dialog:new(title)
       for i = 1, #data do
         dialog:add_child(data)
       end
       return dialog
     end
   end




                                               8 / 64
Ad-hoc approach



    + Easy to code simple stuff
  But:
    − Easily grows out of control
    − Difficult to reuse
    − Hard to handle errors
    − Hard to add new output targets




                                       9 / 64
Practical example: HTTP handler

   api:url "/reverse"
   {
     doc:description [[String reverser]]
     [[
       Takes a string and reverses it.
     ]];
     api:input { data:string "text" };
     api:output
     {
       data:node "result" { data:string "reversed" };
     };
     handler = function(param)
       return { reversed = param.text:reverse() }
     end;
   }

                                                        10 / 64
What do we want to get from that description?



      HTTP request handler itself, with:
          Input validation
          Multi-format output serialization (JSON, XML, ...)
          Handler code static checks (globals, ...)
      Documentation
      Low-level networking client code
      Smoke tests




                                                               11 / 64
Request handler: input validation



   local handler = function(checker, param)
     return
     {
       text = checker:string(param, "text");
     }
   end

   INPUT_LOADERS["/reverse.xml"] = handler
   INPUT_LOADERS["/reverse.json"] = handler




                                               12 / 64
Request handler: output serialization

   local build_formatter = function(fmt)
     return fmt:node("nil", "result")
     {
       fmt:attribute("reversed");
     }
   end

   OUTPUT["/reverse.xml"] = build_formatter(
       make_xml_formatter_builder()
     ):commit()

   OUTPUT["/reverse.json"] = build_formatter(
       make_json_formatter_builder()
     ):commit()


                                                13 / 64
Request handler: the handler itself

   -- Handler code is checked for access to illegal globals.
   -- Legal globals are aliased to locals at the top.
   -- Necessary require() calls are added automatically.

   local handler = function(param)
     return
     {
       reversed = param.text:reverse();
     }
   end

   HANDLERS["/reverse.xml"] = handler;
   HANDLERS["/reverse.json"] = handler;



                                                         14 / 64
Documentation


  /reverse.{xml, json}: String reverser
  Takes a string and reverses it.
  IN
    ?text=STRING
  OUT
  XML:
  <result reversed="STRING" />
  JSON:
  { "result": { "reversed": "STRING" } }



                                           15 / 64
Smoke tests




   test:case "/reverse.xml:smoke.ok" (function()
     local reply = assert(http.GET(
          TEST_HOST .. "/reverse.xml?text=Foo")
       ))
     assert(type(reply.result) == "table")
     assert(type(reply.result.reversed) == "string")
   end)




                                                       16 / 64
Too complicated for ad-hoc solution!




                                       17 / 64
The ”proper” solution?




      Should be easy to add a new target.
      Should be reusable.
      Should have nicer error reporting.




                                            18 / 64
The process




      Load data.
      Validate correctness.
      Generate output.




                              19 / 64
Let’s recap how our data looks like

   api:url "/reverse"
   {
     doc:description [[String reverser]]
     [[
       Takes a string and reverses it.
     ]]
     api:input { data:string "text" };
     api:output
     {
       data:node "result" { data:string "reversed" };
     };
     handler = function(param)
       return { reversed = param.text:reverse() }
     end;
   }

                                                        20 / 64
Surprise! It’s a tree!
   { id = "api:url", name = "/reverse";
     { id = "doc:description", name = "String reverser";
       text = "Takes a string and reverses it.";
     };
     { id = "api:input";
       { id = "data:string", name = "text" };
     };
     { id = "api:output";
       { id = "data:node", name = "result";
          { id = "data:string", name = "reversed" };
       };
       handler = function(param)
          return { reversed = param.text:reverse() }
       end;
     };
   }
                                                           21 / 64
We need a loader that does this: (I)




                                {
namespace:method "title"
                                    id = "namespace:method";
{                           ⇒
                                    name = "title";
  data = "here";
                                    data = "here";
}
                                }




                                                          22 / 64
We need a loader that does this: (II)




                                {
                            ⇒       id = "namespace:method";
namespace:method "title"
                                    name = "title";
                                }




                                                          23 / 64
We need a loader that does this: (III)




namespace:method                {
{                           ⇒       id = "namespace:method";
  data = "here";                    data = "here";
}                               }




                                                          24 / 64
We need a loader that does this: (IV)




                               {
                                 id = "namespace:method";
namespace:method "title"
                                 name = "title";
[[                         ⇒
                                 text = [[
   text
                                 text
]]
                               ]];
                               }




                                                       25 / 64
We need a loader that does this: (V)


   namespace:method "title" (function()
     -- do something
   end)
   ⇒
   {
       id = "namespace:method";
       name = "title";
       handler = function()
         -- do something
       end;
   }



                                          26 / 64
...And adds some debugging info for nice error messages:




                                 {
-- my_dsl.lua:                       id = "namespace:method";
42: namespace:method "title"         name = "title";
                             ⇒
43: {                                data = "here";
44:   data = "here";                 file_ = "my_dsl.lua";
45: }                                line_ = 42;
                                 }




                                                           27 / 64
Nested nodes should just... nest:
   namespace:method "title"
   {
     data = "here";
     foo:bar "baz_1";
     foo:bar "baz_2";
   }
   ⇒
   {
       id = "namespace:method";
       name = "title";
       data = "here";

       { id = "foo:bar", name = "baz_1" };
       { id = "foo:bar", name = "baz_2" };
   }
                                             28 / 64
Notes on data structure:




      Use unique objects instead of string keys to avoid name
      clashes.
      Or you may store user-supplied ”data” in a separate key.




                                                                 29 / 64
Metatable magic, I


   _G["namespace"]:method(
       "title"
     ) ({
       ["data"] = "here";
     })
   setmetatable(
       _G, -- actually, the sandbox
           -- environment for DSL code
       MAGIC_ENV_MT
     )




                                         30 / 64
Metatable magic, II


   _G["namespace"]:method(
       "title"
     ) ({
       ["data"] = "here";
     })
   MAGIC_ENV_MT.__index = function(t, k)
     return setmetatable(
         { },
         MAGIC_PROXY_MT
       )
   end




                                           31 / 64
Metatable magic, III


   _G["namespace"]:method(
       "title"
     ) ({
       ["data"] = "here";
     })
   MAGIC_PROXY_MT.__call = function(self, title)
     self.name = title
     return function(data)
       data.name = self.name
       return data
     end
   end



                                                   32 / 64
Things are somewhat more complex: (I)




      You must detect ”syntax sugar” forms (text, handler)...
          ...just watch out for types, nothing complicated.
      You have to care for single-call forms (name-only, data-only)...

          ...store all proxies after first call
          and extract data from what’s left after DSL code is executed.




                                                                          33 / 64
Things are somewhat more complex: (II)



      Error handling not shown...
          ...it is mostly argument type validation at this stage,
          but global environment protection aka strict mode is advisable.
      Debug info gathering not shown...
          ...just call debug.getinfo() in __call.
      You should keep order of top-level nodes...
          ...make a list of them at the ”name” call stage.




                                                                            34 / 64
Format-agnostic DSL loader



   Loads DSL data to the in-memory tree.
       Reusability: Works for any conforming DSL without
       modifications.
       Output targets: N/A.
       Error reporting: Does what it can, but mostly that is behind
       its scope.




                                                                      35 / 64
Bonus DSL syntax construct




   namespace:method "title"
     : modifier "text"
     : another { modifier_data = true }
   {
     data = "here";
   }




                                          36 / 64
On subnodes: DSL vs plain tables, I
   What is better?
   foo:bar "name"
   {
     subnode =
     {
       key = "value";
     };
   }
   ...Or...
   foo:bar "name"
   {
     foo:subnode
     {
       key = "value";
     };
   }
                                      37 / 64
On subnodes: DSL vs plain tables, II



   It depends on the nature of the data.
       If subnode is a genuine tree node, use foo:bar.foo:subnode
       DSL subnodes.
       But for parameters of the tree node, even when they are
       stored in a sub-table, use plain old foo:bar.subnode tables.
       When unsure, pick whichever is easier for tree traversal in
       each case.




                                                                      38 / 64
One third done




      Load data.
      Validate correctness.
      Generate output.




                              39 / 64
Validation and generation




      Trading speed for convenience (but not so much).
      Traversing the tree (or rather forest) once for validation pass
      and once for each output target.




                                                                        40 / 64
Tree traversal (hat tip to Metalua)
   namespace:method "title" { -- 3rd
      data = "here";
      foo:bar "baz_1"; -- 1st
      foo:bar "baz_2"; -- 2nd
   }
   --
   local walkers = { up = { }, down = { } }
   walkers.down["foo:bar"] = function(walkers, node, parent)
      assert(node.name == "baz_1" or node.name == "baz_2")
   end
   walkers.down["namespace:method"] = function(
        walkers, node, parent
      )
      assert(node.name == "title" and #node.data > 0)
   end
   --
   walk_tree(dsl_data, walkers)
                                                         41 / 64
Tree traversal process




       Bidirectional, depth-first: down, then up.
       If a handler for a given node.id is not found, it is considered
       a ”do nothing” function. Traversal continues.
       If down handler returns "break" string, traversal of subtree is
       aborted.
       Knowing a node parent is useful.




                                                                         42 / 64
Tree traversal hints




       Store state in walker object.
       Set metatables on up and / or down for extra power.
       In complex cases gather data in down, act in up.
       In even more complex cases break in down, and run a custom
       traversal on the node subtree.




                                                                    43 / 64
Validation

   walkers.up["foo:bar"] = function(walkers, node)
     walkers:ensure(
         "check condition A", predicate_A(node),
         node.file_, node.line_
       )
   end

   walk_tree(dsl_data, walkers)

   if not walkers:good() then
     error(
          "data validation failed: "
       .. walkers:message()
        )
   end

                                                     44 / 64
Validation notes

       Don’t skip implementing it. Even poor validation is better
       than none.
       But don’t overdo as well. Depending on the nature of the
       language, overly strict validator may harm usability. Keep
       optional things optional, and be flexible (just) enough in what
       input you accept.
       Do validation in a separate pass. In output generation assume
       data to be valid and do not clutter the code with redundant
       checks.
       Accumulate all errors before failing. This will improve
       usability. But don’t forget to teach users that errors at the
       end of the list may be bogus.
       Report full stack of wrong nodes. From the failed node up to
       the root.

                                                                        45 / 64
Almost there




      Load data.
      Validate correctness.
      Generate output.




                              46 / 64
Output generation, I

   walkers.down["namespace:method"] = function(walkers, node)
     walkers:cat
       [[<method name=]] (xml_escape(node.name)) [[>]]
   end

   walkers.up["foo:bar"] = function(walkers, node)
     walkers:cat
       [[<bar name=]] (xml_escape(node.name)) [[ />]]
   end

   walkers.up["namespace:method"] = function(walkers, node)
     walkers:cat [[</method>]]
   end



                                                         47 / 64
Output generation, II


   function walkers.cat(walkers, v)
     walkers.buf[#walkers.buf + 1] = tostring(v)
     return walkers.cat
   end

   function walkers.concat(walkers)
     return table.concat(walkers)
   end

   walk_tree(dsl_data, walkers)

   output:write(walkers:concat())



                                                   48 / 64
Output generation notes



      One tree walker per target. Otherwise make sure that your
      trusty old cheese grater is still sharp.
      Use string ropes or write directly to file. Or face GC overhead.
      You may generate run-time objects instead of strings. But
      off-line generation is much neater.
      Think! A lot of output generation problems are easier than
      they look.




                                                                        49 / 64
Validation and output generation



   ...By means of data tree traversal.
       Reusability: High. Almost everything that you have to write is
       business-logic. No low-level boilerplate code is visible.
       Output targets: Conforming targets may be added without
       changing any of existing code.
       Error reporting: You have everything you need to provide
       good error reports to user.




                                                                        50 / 64
We’re done with DSL handling




      Load data.
      Validate correctness.
      Generate output.




                               51 / 64
Where do we use internal DSLs ourselves?




   Most prominent cases:
       A HTTP webservice API DSL (which we just discussed).
       A config file format DSL family.
       A SQL DB structure DSL.
       Visual Business Logic Editor DSL family.




                                                              52 / 64
A config file format DSL family: node description language


   types:up "cfg:existing_path" (function(self, info, value)
     local _ =
       self:ensure_equals(
           "unexpected type", type(value), "string"
         ):good()
       and self:ensure(
           "path string must not be empty", value ~= ""
         ):good()
       and self:ensure(
           "path must exist", lfs.attributes(value)
         )
   end)



                                                         53 / 64
A config file format DSL family: config description
language



   Controlled by the node description language.
   cfg:node "my_tool"
   {
     cfg:existing_path "data_path";
   }




                                                   54 / 64
A config file format DSL family: the config data itself




   The data itself is the usual automagic table hierarchy.
   my_tool.data_path = "path/to/file.bin"




                                                             55 / 64
A config file format DSL family: output targets




      Data loader and validator.
      Documentation.




                                                56 / 64
A SQL DB structure DSL




  sql:table "countries"
  {
    sql:primary_key "id";
    sql:string "title" { 256 };
    --
    sql:unique_key "title" { "title" };
  }




                                          57 / 64
A SQL DB structure DSL: output targets




      Initial DB schema SQL code.
      DB schema patches (semiautomated so far).
      Full-blown backoffice web-UI for data management.
      Documentation.




                                                        58 / 64
The Logic Editor DSL family: high-level data schema DSL
   Human-friendly concepts, describing data tree structure and
   transformation rules:
   lang:enum "dow" { -- day of week
     "dow.mon", "dow.tue", "dow.wed", "dow.thu",
     "dow.fri", "dow.sat", "dow.sun";
     render:js [[Weekday]] {
       { [[Monday]] }, { [[Tuesday]] }, { [[Wednesday]] },
       { [[Thursday]] }, { [[Friday]] }, { [[Saturday]] },
       { [[Sunday]] };
     };
     render:lua { -- Matching os.date() format.
       { [[2]] }, { [[3]] }, { [[4]] }, -- MO, TU, WE
       { [[5]] }, { [[6]] }, { [[7]] }, -- TH, FR, SA
       { [[1]] }; -- SU
     };
   }
                                                                 59 / 64
The Logic Editor DSL family: low-level data schema DSL

   Machine-friendly concepts, generated from high-level DSL:
   node:variant "dow" {
     "dow.mon", "dow.tue",      "dow.wed", "dow.thu",
     "dow.fri", "dow.sat",      "dow.sun";
     render:js [[Weekday]]      { [[#{1}]] };
     render:lua { [[#{1}]]      }; }

   node:literal "dow.mon" {
     render:js { [[Monday]] };
     render:lua { [[2]] }; }
   node:literal "dow.tue" {
     render:js { [[Tuesday]] };
     render:lua { [[3]] }; }
   -- ...


                                                               60 / 64
The Logic Editor DSL family: visual DSL




                                          61 / 64
The Logic Editor DSL family: output targets



      From high-level DSL:
          low-level DSL;
          schema docs (to be implemented).
      From low-level DSL:
          schema-specific visual Editor UI;
          data validators;
          data-to-code generator;
          data upgrade code stubs to handle schema changes.




                                                              62 / 64
Why game-changing?

     Before:
          DSL is something exotic, hard to maintain.
          Not much declarative code in codebase except a few special
          places.
          All declarative code in code-base totally non-reusable ad-hoc
          lumps of spaghetti.
          Code readability suffers, bugs thrive.
     Now:
          DSLs are much easier to write and reuse.
          At least 2/3 of the new code is written in DSL of one kind or
          another. (But we heavily combine declarative DSL code with
          Lua functions embedded in it.)
          Code much more readable, less bugs in generated code.
     In future:
          A DSL to define DSLs!


                                                                          63 / 64
Questions?




   Alexander Gladysh, ag@logiceditor.com




                                           64 / 64

Mais conteúdo relacionado

Mais procurados

Big Data Everywhere Chicago: Unleash the Power of HBase Shell (Conversant)
Big Data Everywhere Chicago: Unleash the Power of HBase Shell (Conversant) Big Data Everywhere Chicago: Unleash the Power of HBase Shell (Conversant)
Big Data Everywhere Chicago: Unleash the Power of HBase Shell (Conversant) BigDataEverywhere
 
Php Development With Eclipde PDT
Php Development With Eclipde PDTPhp Development With Eclipde PDT
Php Development With Eclipde PDTBastian Feder
 
Creating Domain Specific Languages in Python
Creating Domain Specific Languages in PythonCreating Domain Specific Languages in Python
Creating Domain Specific Languages in PythonSiddhi
 
FalsyValues. Dmitry Soshnikov - ECMAScript 6
FalsyValues. Dmitry Soshnikov - ECMAScript 6FalsyValues. Dmitry Soshnikov - ECMAScript 6
FalsyValues. Dmitry Soshnikov - ECMAScript 6Dmitry Soshnikov
 
Hive Object Model
Hive Object ModelHive Object Model
Hive Object ModelZheng Shao
 
Introduction to Objective - C
Introduction to Objective - CIntroduction to Objective - C
Introduction to Objective - CJussi Pohjolainen
 
Backbone.js: Run your Application Inside The Browser
Backbone.js: Run your Application Inside The BrowserBackbone.js: Run your Application Inside The Browser
Backbone.js: Run your Application Inside The BrowserHoward Lewis Ship
 
Rich Model And Layered Architecture in SF2 Application
Rich Model And Layered Architecture in SF2 ApplicationRich Model And Layered Architecture in SF2 Application
Rich Model And Layered Architecture in SF2 ApplicationKirill Chebunin
 
Learning from other's mistakes: Data-driven code analysis
Learning from other's mistakes: Data-driven code analysisLearning from other's mistakes: Data-driven code analysis
Learning from other's mistakes: Data-driven code analysisAndreas Dewes
 
Mongoskin - Guilin
Mongoskin - GuilinMongoskin - Guilin
Mongoskin - GuilinJackson Tian
 
Code is not text! How graph technologies can help us to understand our code b...
Code is not text! How graph technologies can help us to understand our code b...Code is not text! How graph technologies can help us to understand our code b...
Code is not text! How graph technologies can help us to understand our code b...Andreas Dewes
 

Mais procurados (20)

Big Data Everywhere Chicago: Unleash the Power of HBase Shell (Conversant)
Big Data Everywhere Chicago: Unleash the Power of HBase Shell (Conversant) Big Data Everywhere Chicago: Unleash the Power of HBase Shell (Conversant)
Big Data Everywhere Chicago: Unleash the Power of HBase Shell (Conversant)
 
Php Development With Eclipde PDT
Php Development With Eclipde PDTPhp Development With Eclipde PDT
Php Development With Eclipde PDT
 
Creating Domain Specific Languages in Python
Creating Domain Specific Languages in PythonCreating Domain Specific Languages in Python
Creating Domain Specific Languages in Python
 
Field api.From d7 to d8
Field api.From d7 to d8Field api.From d7 to d8
Field api.From d7 to d8
 
Drupal 8 migrate!
Drupal 8 migrate!Drupal 8 migrate!
Drupal 8 migrate!
 
Doctrine for NoSQL
Doctrine for NoSQLDoctrine for NoSQL
Doctrine for NoSQL
 
Doctrine and NoSQL
Doctrine and NoSQLDoctrine and NoSQL
Doctrine and NoSQL
 
Multilingual drupal 7
Multilingual drupal 7Multilingual drupal 7
Multilingual drupal 7
 
FalsyValues. Dmitry Soshnikov - ECMAScript 6
FalsyValues. Dmitry Soshnikov - ECMAScript 6FalsyValues. Dmitry Soshnikov - ECMAScript 6
FalsyValues. Dmitry Soshnikov - ECMAScript 6
 
Groovy intro for OUDL
Groovy intro for OUDLGroovy intro for OUDL
Groovy intro for OUDL
 
Hive Object Model
Hive Object ModelHive Object Model
Hive Object Model
 
Introduction to Objective - C
Introduction to Objective - CIntroduction to Objective - C
Introduction to Objective - C
 
C++11 Multithreading - Futures
C++11 Multithreading - FuturesC++11 Multithreading - Futures
C++11 Multithreading - Futures
 
Dispatch in Clojure
Dispatch in ClojureDispatch in Clojure
Dispatch in Clojure
 
Backbone.js: Run your Application Inside The Browser
Backbone.js: Run your Application Inside The BrowserBackbone.js: Run your Application Inside The Browser
Backbone.js: Run your Application Inside The Browser
 
Rich Model And Layered Architecture in SF2 Application
Rich Model And Layered Architecture in SF2 ApplicationRich Model And Layered Architecture in SF2 Application
Rich Model And Layered Architecture in SF2 Application
 
Learning from other's mistakes: Data-driven code analysis
Learning from other's mistakes: Data-driven code analysisLearning from other's mistakes: Data-driven code analysis
Learning from other's mistakes: Data-driven code analysis
 
Mongoskin - Guilin
Mongoskin - GuilinMongoskin - Guilin
Mongoskin - Guilin
 
Quebec pdo
Quebec pdoQuebec pdo
Quebec pdo
 
Code is not text! How graph technologies can help us to understand our code b...
Code is not text! How graph technologies can help us to understand our code b...Code is not text! How graph technologies can help us to understand our code b...
Code is not text! How graph technologies can help us to understand our code b...
 

Destaque

Пользовательская автоматизация профессиональных веб-приложений на Lua
Пользовательская автоматизация профессиональных веб-приложений на LuaПользовательская автоматизация профессиональных веб-приложений на Lua
Пользовательская автоматизация профессиональных веб-приложений на LuaAlexander Gladysh
 
A visual DSL toolkit in Lua: Past, present and future
A visual DSL toolkit in Lua: Past, present and futureA visual DSL toolkit in Lua: Past, present and future
A visual DSL toolkit in Lua: Past, present and futureAlexander Gladysh
 
Об эффективности льгот
Об эффективности льготОб эффективности льгот
Об эффективности льготAnatol Alizar
 
ABA Information- and Communications Technologies in Austria
ABA Information- and Communications Technologies in AustriaABA Information- and Communications Technologies in Austria
ABA Information- and Communications Technologies in AustriaABA - Invest in Austria
 
Presentation2
Presentation2Presentation2
Presentation2cocolatto
 
Mamta grandini 2009-10_esercizio3
Mamta grandini 2009-10_esercizio3Mamta grandini 2009-10_esercizio3
Mamta grandini 2009-10_esercizio3pittu90
 
Proyecto de aula
Proyecto de aulaProyecto de aula
Proyecto de aulamartha
 
Patrice Krupa - Short Sale Homeowner Presentation
Patrice Krupa - Short Sale Homeowner PresentationPatrice Krupa - Short Sale Homeowner Presentation
Patrice Krupa - Short Sale Homeowner Presentationslidesharepjk
 
SGF15--e-Poster-Shevrin-3273
SGF15--e-Poster-Shevrin-3273SGF15--e-Poster-Shevrin-3273
SGF15--e-Poster-Shevrin-3273ThomsonReuters
 
Apunte dntg estructurayclasificaciondeestilos1
Apunte dntg estructurayclasificaciondeestilos1Apunte dntg estructurayclasificaciondeestilos1
Apunte dntg estructurayclasificaciondeestilos1Gabriel Soria
 
Who Are You? Branding Your Nonprofit
Who Are You? Branding Your NonprofitWho Are You? Branding Your Nonprofit
Who Are You? Branding Your NonprofitGrace Dunlap
 
Course outline
Course outlineCourse outline
Course outlinecocolatto
 
เผยความลับทุกเดือนสร้างเงินล้านออนไลน์ ฉบับสมบูรณ์
เผยความลับทุกเดือนสร้างเงินล้านออนไลน์ ฉบับสมบูรณ์เผยความลับทุกเดือนสร้างเงินล้านออนไลน์ ฉบับสมบูรณ์
เผยความลับทุกเดือนสร้างเงินล้านออนไลน์ ฉบับสมบูรณ์vr2g8er
 
Partner Training: Nonprofit Industry
Partner Training: Nonprofit IndustryPartner Training: Nonprofit Industry
Partner Training: Nonprofit IndustryGrace Dunlap
 

Destaque (20)

Пользовательская автоматизация профессиональных веб-приложений на Lua
Пользовательская автоматизация профессиональных веб-приложений на LuaПользовательская автоматизация профессиональных веб-приложений на Lua
Пользовательская автоматизация профессиональных веб-приложений на Lua
 
A visual DSL toolkit in Lua: Past, present and future
A visual DSL toolkit in Lua: Past, present and futureA visual DSL toolkit in Lua: Past, present and future
A visual DSL toolkit in Lua: Past, present and future
 
Об эффективности льгот
Об эффективности льготОб эффективности льгот
Об эффективности льгот
 
ABA Information- and Communications Technologies in Austria
ABA Information- and Communications Technologies in AustriaABA Information- and Communications Technologies in Austria
ABA Information- and Communications Technologies in Austria
 
Presentation2
Presentation2Presentation2
Presentation2
 
Mamta grandini 2009-10_esercizio3
Mamta grandini 2009-10_esercizio3Mamta grandini 2009-10_esercizio3
Mamta grandini 2009-10_esercizio3
 
Proyecto de aula
Proyecto de aulaProyecto de aula
Proyecto de aula
 
Patrice Krupa - Short Sale Homeowner Presentation
Patrice Krupa - Short Sale Homeowner PresentationPatrice Krupa - Short Sale Homeowner Presentation
Patrice Krupa - Short Sale Homeowner Presentation
 
CRIS_IR_interop_CRIS2014_slides
CRIS_IR_interop_CRIS2014_slidesCRIS_IR_interop_CRIS2014_slides
CRIS_IR_interop_CRIS2014_slides
 
Learning studio
Learning studioLearning studio
Learning studio
 
Protocolo ttemperatura
Protocolo ttemperaturaProtocolo ttemperatura
Protocolo ttemperatura
 
SGF15--e-Poster-Shevrin-3273
SGF15--e-Poster-Shevrin-3273SGF15--e-Poster-Shevrin-3273
SGF15--e-Poster-Shevrin-3273
 
Apunte dntg estructurayclasificaciondeestilos1
Apunte dntg estructurayclasificaciondeestilos1Apunte dntg estructurayclasificaciondeestilos1
Apunte dntg estructurayclasificaciondeestilos1
 
Apresentação ORCID Jornadas FCCN 2014 Évora
Apresentação ORCID Jornadas FCCN 2014 ÉvoraApresentação ORCID Jornadas FCCN 2014 Évora
Apresentação ORCID Jornadas FCCN 2014 Évora
 
Who Are You? Branding Your Nonprofit
Who Are You? Branding Your NonprofitWho Are You? Branding Your Nonprofit
Who Are You? Branding Your Nonprofit
 
Weather 1
Weather 1Weather 1
Weather 1
 
Course outline
Course outlineCourse outline
Course outline
 
เผยความลับทุกเดือนสร้างเงินล้านออนไลน์ ฉบับสมบูรณ์
เผยความลับทุกเดือนสร้างเงินล้านออนไลน์ ฉบับสมบูรณ์เผยความลับทุกเดือนสร้างเงินล้านออนไลน์ ฉบับสมบูรณ์
เผยความลับทุกเดือนสร้างเงินล้านออนไลน์ ฉบับสมบูรณ์
 
Lecture 3
Lecture 3Lecture 3
Lecture 3
 
Partner Training: Nonprofit Industry
Partner Training: Nonprofit IndustryPartner Training: Nonprofit Industry
Partner Training: Nonprofit Industry
 

Semelhante a Declarative Internal DSLs in Lua: A Game Changing Experience

HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6Dmitry Soshnikov
 
TypeScript Introduction
TypeScript IntroductionTypeScript Introduction
TypeScript IntroductionDmitry Sheiko
 
Introducing PHP Latest Updates
Introducing PHP Latest UpdatesIntroducing PHP Latest Updates
Introducing PHP Latest UpdatesIftekhar Eather
 
Spring data iii
Spring data iiiSpring data iii
Spring data iii명철 강
 
Clean code in JavaScript
Clean code in JavaScriptClean code in JavaScript
Clean code in JavaScriptMathieu Breton
 
Cascading Through Hadoop for the Boulder JUG
Cascading Through Hadoop for the Boulder JUGCascading Through Hadoop for the Boulder JUG
Cascading Through Hadoop for the Boulder JUGMatthew McCullough
 
How to Write Node.js Module
How to Write Node.js ModuleHow to Write Node.js Module
How to Write Node.js ModuleFred Chien
 
Scala4sling
Scala4slingScala4sling
Scala4slingday
 
Introduction to PHP 5.3
Introduction to PHP 5.3Introduction to PHP 5.3
Introduction to PHP 5.3guestcc91d4
 
Can't Miss Features of PHP 5.3 and 5.4
Can't Miss Features of PHP 5.3 and 5.4Can't Miss Features of PHP 5.3 and 5.4
Can't Miss Features of PHP 5.3 and 5.4Jeff Carouth
 
Marrow: A Meta-Framework for Python 2.6+ and 3.1+
Marrow: A Meta-Framework for Python 2.6+ and 3.1+Marrow: A Meta-Framework for Python 2.6+ and 3.1+
Marrow: A Meta-Framework for Python 2.6+ and 3.1+ConFoo
 
服务框架: Thrift & PasteScript
服务框架: Thrift & PasteScript服务框架: Thrift & PasteScript
服务框架: Thrift & PasteScriptQiangning Hong
 
Kerberizing spark. Spark Summit east
Kerberizing spark. Spark Summit eastKerberizing spark. Spark Summit east
Kerberizing spark. Spark Summit eastJorge Lopez-Malla
 
Fun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksFun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksMongoDB
 
Introduction to ES6 with Tommy Cresine
Introduction to ES6 with Tommy CresineIntroduction to ES6 with Tommy Cresine
Introduction to ES6 with Tommy CresineMovel
 
Building Testable PHP Applications
Building Testable PHP ApplicationsBuilding Testable PHP Applications
Building Testable PHP Applicationschartjes
 
Using Spark to Load Oracle Data into Cassandra
Using Spark to Load Oracle Data into CassandraUsing Spark to Load Oracle Data into Cassandra
Using Spark to Load Oracle Data into CassandraJim Hatcher
 
Using Spark to Load Oracle Data into Cassandra (Jim Hatcher, IHS Markit) | C*...
Using Spark to Load Oracle Data into Cassandra (Jim Hatcher, IHS Markit) | C*...Using Spark to Load Oracle Data into Cassandra (Jim Hatcher, IHS Markit) | C*...
Using Spark to Load Oracle Data into Cassandra (Jim Hatcher, IHS Markit) | C*...DataStax
 

Semelhante a Declarative Internal DSLs in Lua: A Game Changing Experience (20)

HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
 
DataMapper
DataMapperDataMapper
DataMapper
 
TypeScript Introduction
TypeScript IntroductionTypeScript Introduction
TypeScript Introduction
 
Introducing PHP Latest Updates
Introducing PHP Latest UpdatesIntroducing PHP Latest Updates
Introducing PHP Latest Updates
 
Spring data iii
Spring data iiiSpring data iii
Spring data iii
 
Clean code in JavaScript
Clean code in JavaScriptClean code in JavaScript
Clean code in JavaScript
 
Cascading Through Hadoop for the Boulder JUG
Cascading Through Hadoop for the Boulder JUGCascading Through Hadoop for the Boulder JUG
Cascading Through Hadoop for the Boulder JUG
 
How to Write Node.js Module
How to Write Node.js ModuleHow to Write Node.js Module
How to Write Node.js Module
 
Scala4sling
Scala4slingScala4sling
Scala4sling
 
Introduction to PHP 5.3
Introduction to PHP 5.3Introduction to PHP 5.3
Introduction to PHP 5.3
 
Can't Miss Features of PHP 5.3 and 5.4
Can't Miss Features of PHP 5.3 and 5.4Can't Miss Features of PHP 5.3 and 5.4
Can't Miss Features of PHP 5.3 and 5.4
 
Marrow: A Meta-Framework for Python 2.6+ and 3.1+
Marrow: A Meta-Framework for Python 2.6+ and 3.1+Marrow: A Meta-Framework for Python 2.6+ and 3.1+
Marrow: A Meta-Framework for Python 2.6+ and 3.1+
 
服务框架: Thrift & PasteScript
服务框架: Thrift & PasteScript服务框架: Thrift & PasteScript
服务框架: Thrift & PasteScript
 
Kerberizing spark. Spark Summit east
Kerberizing spark. Spark Summit eastKerberizing spark. Spark Summit east
Kerberizing spark. Spark Summit east
 
Fun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksFun Teaching MongoDB New Tricks
Fun Teaching MongoDB New Tricks
 
Introduction to ES6 with Tommy Cresine
Introduction to ES6 with Tommy CresineIntroduction to ES6 with Tommy Cresine
Introduction to ES6 with Tommy Cresine
 
Building Testable PHP Applications
Building Testable PHP ApplicationsBuilding Testable PHP Applications
Building Testable PHP Applications
 
Using Spark to Load Oracle Data into Cassandra
Using Spark to Load Oracle Data into CassandraUsing Spark to Load Oracle Data into Cassandra
Using Spark to Load Oracle Data into Cassandra
 
Using Spark to Load Oracle Data into Cassandra (Jim Hatcher, IHS Markit) | C*...
Using Spark to Load Oracle Data into Cassandra (Jim Hatcher, IHS Markit) | C*...Using Spark to Load Oracle Data into Cassandra (Jim Hatcher, IHS Markit) | C*...
Using Spark to Load Oracle Data into Cassandra (Jim Hatcher, IHS Markit) | C*...
 
Android Data Persistence
Android Data PersistenceAndroid Data Persistence
Android Data Persistence
 

Último

My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationRidwan Fadjar
 
Google AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAGGoogle AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAGSujit Pal
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationRadu Cotescu
 
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersEnhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersThousandEyes
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Igalia
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024Results
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | DelhiFULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhisoniya singh
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024The Digital Insurer
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slidevu2urc
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slidespraypatel2
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptxHampshireHUG
 
Maximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxMaximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxOnBoard
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Allon Mureinik
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j
 

Último (20)

My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 Presentation
 
Google AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAGGoogle AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAG
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersEnhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | DelhiFULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
Maximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxMaximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptx
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
 

Declarative Internal DSLs in Lua: A Game Changing Experience

  • 1. Declarative Internal DSLs in Lua A Game-Changing Experience Alexander Gladysh, ag@logiceditor.com LogicEditor.com CTO, co-founder Lua Workshop 2011 1 / 64
  • 2. Outline Introduction Ad-hoc approach A case study The ”proper” solution Where do we use DSLs ourselves? Why game-changing? Questions? 2 / 64
  • 3. Internal Declarative DSL in Lua namespace:method "title" { data = "here"; } 3 / 64
  • 4. ...Without sugar _G["namespace"]:method( "title" ) ({ ["data"] = "here"; }) 4 / 64
  • 5. Na¨ implementation ıve namespace = { } namespace.method = function(self, name) return function(data) -- ...do something -- ...with name and data end end 5 / 64
  • 6. Hypothetical UI description language ui:dialog "alert" { ui:label "message"; ui:button "OK" { on_click = function(self) self:close() end; }; } 6 / 64
  • 7. UI description language ”implementation”, I function ui:label(title) return function(data) return GUI.Label:new(title, data) end end function ui:button(title) return function(data) return GUI.Button:new(title, data) end end 7 / 64
  • 8. UI description language ”implementation”, II function ui:dialog(title) return function(data) local dialog = GUI.Dialog:new(title) for i = 1, #data do dialog:add_child(data) end return dialog end end 8 / 64
  • 9. Ad-hoc approach + Easy to code simple stuff But: − Easily grows out of control − Difficult to reuse − Hard to handle errors − Hard to add new output targets 9 / 64
  • 10. Practical example: HTTP handler api:url "/reverse" { doc:description [[String reverser]] [[ Takes a string and reverses it. ]]; api:input { data:string "text" }; api:output { data:node "result" { data:string "reversed" }; }; handler = function(param) return { reversed = param.text:reverse() } end; } 10 / 64
  • 11. What do we want to get from that description? HTTP request handler itself, with: Input validation Multi-format output serialization (JSON, XML, ...) Handler code static checks (globals, ...) Documentation Low-level networking client code Smoke tests 11 / 64
  • 12. Request handler: input validation local handler = function(checker, param) return { text = checker:string(param, "text"); } end INPUT_LOADERS["/reverse.xml"] = handler INPUT_LOADERS["/reverse.json"] = handler 12 / 64
  • 13. Request handler: output serialization local build_formatter = function(fmt) return fmt:node("nil", "result") { fmt:attribute("reversed"); } end OUTPUT["/reverse.xml"] = build_formatter( make_xml_formatter_builder() ):commit() OUTPUT["/reverse.json"] = build_formatter( make_json_formatter_builder() ):commit() 13 / 64
  • 14. Request handler: the handler itself -- Handler code is checked for access to illegal globals. -- Legal globals are aliased to locals at the top. -- Necessary require() calls are added automatically. local handler = function(param) return { reversed = param.text:reverse(); } end HANDLERS["/reverse.xml"] = handler; HANDLERS["/reverse.json"] = handler; 14 / 64
  • 15. Documentation /reverse.{xml, json}: String reverser Takes a string and reverses it. IN ?text=STRING OUT XML: <result reversed="STRING" /> JSON: { "result": { "reversed": "STRING" } } 15 / 64
  • 16. Smoke tests test:case "/reverse.xml:smoke.ok" (function() local reply = assert(http.GET( TEST_HOST .. "/reverse.xml?text=Foo") )) assert(type(reply.result) == "table") assert(type(reply.result.reversed) == "string") end) 16 / 64
  • 17. Too complicated for ad-hoc solution! 17 / 64
  • 18. The ”proper” solution? Should be easy to add a new target. Should be reusable. Should have nicer error reporting. 18 / 64
  • 19. The process Load data. Validate correctness. Generate output. 19 / 64
  • 20. Let’s recap how our data looks like api:url "/reverse" { doc:description [[String reverser]] [[ Takes a string and reverses it. ]] api:input { data:string "text" }; api:output { data:node "result" { data:string "reversed" }; }; handler = function(param) return { reversed = param.text:reverse() } end; } 20 / 64
  • 21. Surprise! It’s a tree! { id = "api:url", name = "/reverse"; { id = "doc:description", name = "String reverser"; text = "Takes a string and reverses it."; }; { id = "api:input"; { id = "data:string", name = "text" }; }; { id = "api:output"; { id = "data:node", name = "result"; { id = "data:string", name = "reversed" }; }; handler = function(param) return { reversed = param.text:reverse() } end; }; } 21 / 64
  • 22. We need a loader that does this: (I) { namespace:method "title" id = "namespace:method"; { ⇒ name = "title"; data = "here"; data = "here"; } } 22 / 64
  • 23. We need a loader that does this: (II) { ⇒ id = "namespace:method"; namespace:method "title" name = "title"; } 23 / 64
  • 24. We need a loader that does this: (III) namespace:method { { ⇒ id = "namespace:method"; data = "here"; data = "here"; } } 24 / 64
  • 25. We need a loader that does this: (IV) { id = "namespace:method"; namespace:method "title" name = "title"; [[ ⇒ text = [[ text text ]] ]]; } 25 / 64
  • 26. We need a loader that does this: (V) namespace:method "title" (function() -- do something end) ⇒ { id = "namespace:method"; name = "title"; handler = function() -- do something end; } 26 / 64
  • 27. ...And adds some debugging info for nice error messages: { -- my_dsl.lua: id = "namespace:method"; 42: namespace:method "title" name = "title"; ⇒ 43: { data = "here"; 44: data = "here"; file_ = "my_dsl.lua"; 45: } line_ = 42; } 27 / 64
  • 28. Nested nodes should just... nest: namespace:method "title" { data = "here"; foo:bar "baz_1"; foo:bar "baz_2"; } ⇒ { id = "namespace:method"; name = "title"; data = "here"; { id = "foo:bar", name = "baz_1" }; { id = "foo:bar", name = "baz_2" }; } 28 / 64
  • 29. Notes on data structure: Use unique objects instead of string keys to avoid name clashes. Or you may store user-supplied ”data” in a separate key. 29 / 64
  • 30. Metatable magic, I _G["namespace"]:method( "title" ) ({ ["data"] = "here"; }) setmetatable( _G, -- actually, the sandbox -- environment for DSL code MAGIC_ENV_MT ) 30 / 64
  • 31. Metatable magic, II _G["namespace"]:method( "title" ) ({ ["data"] = "here"; }) MAGIC_ENV_MT.__index = function(t, k) return setmetatable( { }, MAGIC_PROXY_MT ) end 31 / 64
  • 32. Metatable magic, III _G["namespace"]:method( "title" ) ({ ["data"] = "here"; }) MAGIC_PROXY_MT.__call = function(self, title) self.name = title return function(data) data.name = self.name return data end end 32 / 64
  • 33. Things are somewhat more complex: (I) You must detect ”syntax sugar” forms (text, handler)... ...just watch out for types, nothing complicated. You have to care for single-call forms (name-only, data-only)... ...store all proxies after first call and extract data from what’s left after DSL code is executed. 33 / 64
  • 34. Things are somewhat more complex: (II) Error handling not shown... ...it is mostly argument type validation at this stage, but global environment protection aka strict mode is advisable. Debug info gathering not shown... ...just call debug.getinfo() in __call. You should keep order of top-level nodes... ...make a list of them at the ”name” call stage. 34 / 64
  • 35. Format-agnostic DSL loader Loads DSL data to the in-memory tree. Reusability: Works for any conforming DSL without modifications. Output targets: N/A. Error reporting: Does what it can, but mostly that is behind its scope. 35 / 64
  • 36. Bonus DSL syntax construct namespace:method "title" : modifier "text" : another { modifier_data = true } { data = "here"; } 36 / 64
  • 37. On subnodes: DSL vs plain tables, I What is better? foo:bar "name" { subnode = { key = "value"; }; } ...Or... foo:bar "name" { foo:subnode { key = "value"; }; } 37 / 64
  • 38. On subnodes: DSL vs plain tables, II It depends on the nature of the data. If subnode is a genuine tree node, use foo:bar.foo:subnode DSL subnodes. But for parameters of the tree node, even when they are stored in a sub-table, use plain old foo:bar.subnode tables. When unsure, pick whichever is easier for tree traversal in each case. 38 / 64
  • 39. One third done Load data. Validate correctness. Generate output. 39 / 64
  • 40. Validation and generation Trading speed for convenience (but not so much). Traversing the tree (or rather forest) once for validation pass and once for each output target. 40 / 64
  • 41. Tree traversal (hat tip to Metalua) namespace:method "title" { -- 3rd data = "here"; foo:bar "baz_1"; -- 1st foo:bar "baz_2"; -- 2nd } -- local walkers = { up = { }, down = { } } walkers.down["foo:bar"] = function(walkers, node, parent) assert(node.name == "baz_1" or node.name == "baz_2") end walkers.down["namespace:method"] = function( walkers, node, parent ) assert(node.name == "title" and #node.data > 0) end -- walk_tree(dsl_data, walkers) 41 / 64
  • 42. Tree traversal process Bidirectional, depth-first: down, then up. If a handler for a given node.id is not found, it is considered a ”do nothing” function. Traversal continues. If down handler returns "break" string, traversal of subtree is aborted. Knowing a node parent is useful. 42 / 64
  • 43. Tree traversal hints Store state in walker object. Set metatables on up and / or down for extra power. In complex cases gather data in down, act in up. In even more complex cases break in down, and run a custom traversal on the node subtree. 43 / 64
  • 44. Validation walkers.up["foo:bar"] = function(walkers, node) walkers:ensure( "check condition A", predicate_A(node), node.file_, node.line_ ) end walk_tree(dsl_data, walkers) if not walkers:good() then error( "data validation failed: " .. walkers:message() ) end 44 / 64
  • 45. Validation notes Don’t skip implementing it. Even poor validation is better than none. But don’t overdo as well. Depending on the nature of the language, overly strict validator may harm usability. Keep optional things optional, and be flexible (just) enough in what input you accept. Do validation in a separate pass. In output generation assume data to be valid and do not clutter the code with redundant checks. Accumulate all errors before failing. This will improve usability. But don’t forget to teach users that errors at the end of the list may be bogus. Report full stack of wrong nodes. From the failed node up to the root. 45 / 64
  • 46. Almost there Load data. Validate correctness. Generate output. 46 / 64
  • 47. Output generation, I walkers.down["namespace:method"] = function(walkers, node) walkers:cat [[<method name=]] (xml_escape(node.name)) [[>]] end walkers.up["foo:bar"] = function(walkers, node) walkers:cat [[<bar name=]] (xml_escape(node.name)) [[ />]] end walkers.up["namespace:method"] = function(walkers, node) walkers:cat [[</method>]] end 47 / 64
  • 48. Output generation, II function walkers.cat(walkers, v) walkers.buf[#walkers.buf + 1] = tostring(v) return walkers.cat end function walkers.concat(walkers) return table.concat(walkers) end walk_tree(dsl_data, walkers) output:write(walkers:concat()) 48 / 64
  • 49. Output generation notes One tree walker per target. Otherwise make sure that your trusty old cheese grater is still sharp. Use string ropes or write directly to file. Or face GC overhead. You may generate run-time objects instead of strings. But off-line generation is much neater. Think! A lot of output generation problems are easier than they look. 49 / 64
  • 50. Validation and output generation ...By means of data tree traversal. Reusability: High. Almost everything that you have to write is business-logic. No low-level boilerplate code is visible. Output targets: Conforming targets may be added without changing any of existing code. Error reporting: You have everything you need to provide good error reports to user. 50 / 64
  • 51. We’re done with DSL handling Load data. Validate correctness. Generate output. 51 / 64
  • 52. Where do we use internal DSLs ourselves? Most prominent cases: A HTTP webservice API DSL (which we just discussed). A config file format DSL family. A SQL DB structure DSL. Visual Business Logic Editor DSL family. 52 / 64
  • 53. A config file format DSL family: node description language types:up "cfg:existing_path" (function(self, info, value) local _ = self:ensure_equals( "unexpected type", type(value), "string" ):good() and self:ensure( "path string must not be empty", value ~= "" ):good() and self:ensure( "path must exist", lfs.attributes(value) ) end) 53 / 64
  • 54. A config file format DSL family: config description language Controlled by the node description language. cfg:node "my_tool" { cfg:existing_path "data_path"; } 54 / 64
  • 55. A config file format DSL family: the config data itself The data itself is the usual automagic table hierarchy. my_tool.data_path = "path/to/file.bin" 55 / 64
  • 56. A config file format DSL family: output targets Data loader and validator. Documentation. 56 / 64
  • 57. A SQL DB structure DSL sql:table "countries" { sql:primary_key "id"; sql:string "title" { 256 }; -- sql:unique_key "title" { "title" }; } 57 / 64
  • 58. A SQL DB structure DSL: output targets Initial DB schema SQL code. DB schema patches (semiautomated so far). Full-blown backoffice web-UI for data management. Documentation. 58 / 64
  • 59. The Logic Editor DSL family: high-level data schema DSL Human-friendly concepts, describing data tree structure and transformation rules: lang:enum "dow" { -- day of week "dow.mon", "dow.tue", "dow.wed", "dow.thu", "dow.fri", "dow.sat", "dow.sun"; render:js [[Weekday]] { { [[Monday]] }, { [[Tuesday]] }, { [[Wednesday]] }, { [[Thursday]] }, { [[Friday]] }, { [[Saturday]] }, { [[Sunday]] }; }; render:lua { -- Matching os.date() format. { [[2]] }, { [[3]] }, { [[4]] }, -- MO, TU, WE { [[5]] }, { [[6]] }, { [[7]] }, -- TH, FR, SA { [[1]] }; -- SU }; } 59 / 64
  • 60. The Logic Editor DSL family: low-level data schema DSL Machine-friendly concepts, generated from high-level DSL: node:variant "dow" { "dow.mon", "dow.tue", "dow.wed", "dow.thu", "dow.fri", "dow.sat", "dow.sun"; render:js [[Weekday]] { [[#{1}]] }; render:lua { [[#{1}]] }; } node:literal "dow.mon" { render:js { [[Monday]] }; render:lua { [[2]] }; } node:literal "dow.tue" { render:js { [[Tuesday]] }; render:lua { [[3]] }; } -- ... 60 / 64
  • 61. The Logic Editor DSL family: visual DSL 61 / 64
  • 62. The Logic Editor DSL family: output targets From high-level DSL: low-level DSL; schema docs (to be implemented). From low-level DSL: schema-specific visual Editor UI; data validators; data-to-code generator; data upgrade code stubs to handle schema changes. 62 / 64
  • 63. Why game-changing? Before: DSL is something exotic, hard to maintain. Not much declarative code in codebase except a few special places. All declarative code in code-base totally non-reusable ad-hoc lumps of spaghetti. Code readability suffers, bugs thrive. Now: DSLs are much easier to write and reuse. At least 2/3 of the new code is written in DSL of one kind or another. (But we heavily combine declarative DSL code with Lua functions embedded in it.) Code much more readable, less bugs in generated code. In future: A DSL to define DSLs! 63 / 64
  • 64. Questions? Alexander Gladysh, ag@logiceditor.com 64 / 64