lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


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