|
|
||
|
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