|
For creating a DSL (Domain Specific Language), you have choice between writing your own parser or extending a language. Since its origin, Lua is well suited for data oriented DSL. But for declarative DSL, the mandatory use of parenthesis for arguments function (except in case of a single string or table) is a big limitation. As example, consider the 2 following versions of the same code (which come from my current work on <http://github.com/fperrad/lua-Coat/> and it is not really a DSL and David Manura already send me some good suggestions in pure Lua) -- "classic" Lua class 'Point' has( 'x', { is = 'rw', isa = 'number', default = 0 } ) has( 'y', { is = 'rw', isa = 'number', default = 0 } ) overload( '__tostring', function (self) return '(' .. self:x() .. ', ' .. self:y() .. ')' end ) method( 'draw', function (self) return "drawing " .. self._CLASS .. tostring(self) end ) and -- "patched" Lua class 'Point' has 'x' => { is = 'rw', isa = 'number', default = 0 } has 'y' => { is = 'rw', isa = 'number', default = 0 } overload '__tostring' => function (self) return '(' .. self:x() .. ', ' .. self:y() .. ')' end method 'draw' => function (self) return "drawing " .. self._CLASS .. tostring(self) end A seasoned Lua developper could make abstraction of extra parenthesis. But a DSL user cannot because usually he doesn't known Lua. My first patch allows multiple arguments without parenthesis if the first argument is a string. It also introduces the token `=>' named FAT_COMMA. The grammar modifications are : funcargs -> ( [explist] ) | tableconstructor |STRING { expsep expr } expsep -> `,' | `=>' Some examples : > print 'a', 2, 'c', 4 a 2 c 4 > print('a', 2, 'c', 4) -- equiv a 2 c 4 nested call is possible > print 'a', 2, print 'c', 4 c 4 a 2 > print( 'a', 2, print( 'c', 4)) -- equiv c 4 a 2 semicolon forces the end of a level > print 'a', 2, print 'c';, 4 c a 2 nil 4 > print('a', 2, print('c'), 4) -- equiv c a 2 nil 4 This patch is not backward compatible with Lua, for example it breaks the following code : function printf(...) io.write(string.format(...)) end printf("Hello %s from %s\n", os.getenv "USER", _VERSION) it must be rewritten : printf("Hello %s from %s\n", os.getenv("USER"), _VERSION) My second patch allows multiple arguments without parenthesis in all case (except if the first argument is a name) The grammar modifications are : funcargs -> ( [explist] ) | expr { expsep expr } expsep -> `,' | `=>' The syntax becomes ambiguous in many case. I parse the indentation (like Python) in order to solve this issue, and the lexer generates extra semicol. This second patch is very experimental. Now with it, I could write : subtype 'Natural' => as 'number'; => where function (n) return n > 0 end; instead of : subtype( 'Natural', as( 'number' ), where( function (n) return n > 0 end ) ) I hope that Lua will become the best choice for building DSL. Francois Perrad
Attachment:
0001-function-call-allows-arguments-after-a-initial-strin.patch
Description: Binary data
Attachment:
0001-function-call-allows-multiple-arguments-without-pare.patch
Description: Binary data