Professional Documents
Culture Documents
PostedAugust08,2015byleafo(@moonscript)Tags:lua
22points
Tweet
DSLs,ordomainspecificlanguages,areprogramming
languagesthataredesignedtoimplementasetoffeatures
specifictoaparticularproblemorfield.Anexamplecould
beMake,thebuildtool,whichisaspeciallydesigned
languageforcombiningcommandsandfileswhilemanaging
dependencies.
Contents
Droppingtheparenthesis
Chaining
Usingfunctionenvironments
ImplementingtheHTM Lbuilder
Closing
Alotofmodernprogramminglanguageshavesomuch
flexibilityintheirsyntaxthatitspossibletobuildlibraries
thatexposetheirownmini-languageswithinthehost
language.ThedefinitionofDSLhasbroadenedtoinclude
thesekindsoflibraries.
Inthisguidewe'llbuildaDSLforgeneratingHTML.Itlooks
likethis:
html {
body {
h1 "Welcome to my Lua site",
a{
href = "http://leafo.net",
"Go home"
}
}
}
Beforejumpingin,herearesomeDSLbuildingtechniques:
insomescenarioswhencallingfunctions.Tersenessis
importantwhenbuildingaDSL,andremovingsuperfluous
charactersisagoodwaytodothat.
Whencallingafunctionthathasasingleargumentofeither
atableliteralorastringliteral,theparenthesisareoptional.
Thissyntaxhasveryhighprecedence,thesameasifyou
wereusingparenthesis:
Chaining
Parenthesis-lessinvocationcanbechainedaslongaseach
expressionfromtheleftevaluatestoafunction(oracallable
table).Heressomeexamplesyntaxforahypotheticalweb
routingframework:
match "/post-comment" {
GET = function ()
-- render the form
end,
POST = function ()
-- save to database
end
Ifitsnotimmediatelyobviouswhatsgoingon,writingthe
parenthesisinwillclearthingsup.Theprecedenceofthe
parenthesis-lessinvocationgoesfromlefttoright,sothe
aboveisequivalentto:
match("/post-comment")({ ... })
Thepatternwewouldusetoimplementthissyntaxwould
looksomethinglikethis:
Usingarecursivefunctionconstructoritspossibletomake
chainingworkforanylength.
require .When
workingwithaDSL,itsnicetohaveallthefunctionality
availablewithouthavingtomanuallyloadanything.
Oneoptionwouldbetomakeallthefunctionsandvalues
globalvariables,butitsnotrecommendedasitmight
interferewithotherlibraries.
Afunctionenvironmentcanbeusedtochangehowa
functionresolvesglobalvariablereferenceswithinitsscope.
ThiscanbeusedtoautomaticallyexposeaDSLs
functionalitywithoutpollutingtheregularglobalscope.
ForthesakeofthisguideI'llassumethat
setfenv existsin
theversionofLuawe'reusing.Ifyou'reusing5.2orabove
you'llneedtoprovideyouownimplementation:
ImplementingsetfenvinLua5.2,5.3,andabove
Heresafunction
run_with_env thatrunsanotherfunction
withaparticularenvironment.
TheenvironmentpassedwillrepresenttheDSL:
local dsl_env = {
move = function(x,y)
print("I moved to", x, y)
end,
speak = function(message)
print("I said", message)
end
run_with_env(dsl_env, function()
move(10, 10)
speak("I am hungry!")
end)
Inthistrivialexamplethebenefitsmightnotbeobvious,but
typicallyyourDSLwouldbeimplementedinanother
module,andeachplaceyouinvokeitisnotnecessaryto
bringeachfunctionintoscopemanually,butratheractivate
thewholesscopewith
run_with_env .
Functionenvironmentsalsoletyoudynamicallygenerate
methodsonthefly.Usingthe
__index metamethod
implementedasafunction,anyvaluecanbe
programmaticallycreated.ThisishowtheHTMLbuilder
DSLwillbecreated.
html {
body {
h1 "Welcome to my Lua site",
a{
href = "http://leafo.net",
"Go home"
}
}
}
EachHTMLtagisrepresentedbyaLuafunctionthatwill
returntheHTMLstringrepresentingthattagwiththecorrect
attributeandcontentifnecessary.
Althoughitwouldbepossibletowritecodetogenerateall
theHTMLtagbuilderfunctionsaheadoftime,afunction
__index metamethodwillbeusedtogeneratethemonthefly.
InordertoruncodeinthecontextofourDSL,itmustbe
packagedintoafunction.The
render_html functionwilltake
thatfunctionandconvertittoaHTMLstring:
render_html(function()
return div {
img { src = "http://leafo.net/hi" }
}
end) -- > <div><img src="http://leafo.net/hi" /></div>
Note: The
img tagisself-closing,ithasnoseparateclosetag.
render_html mightbeimplementedlikethis:
The
build_tag functioniswhereallactualworkisdone.It
takesthenameofthetag,andtheattributesandcontentasa
singletable.
Note: Thisfunctioncouldbeoptimizedbycachingthe
generatedfunctionsintheenvironmenttable.
Thevoidelements,asmentionedabove,aredefinedasa
simpleset:
local void_tags = {
img = true,
-- etc...
}
ThemostefficientwaytoconcatenatestringsinregularLua
istoaccumulatethemintoatablethencall
Manycallsto
table.concat .
table.insert couldbeusedtoappendtothis
buffertable,butIpreferthefollowingfunctiontoallow
multiplevaluestobeappendedatonce:
Note:
anyextraallocationsbyqueryingthevarargsobjectinsteadof
creatinganew table.
Nowtheimplementationof
build_tag :
Thereareacoupleinterestingthingshere:
The
opts argumentcaneitherbeastringliteraloratable.
WhenitsatableittakesadvantageofthefactthatLuatables
arebothhashtablesandarraysatthesametime.Thehash
tableportionholdstheattributesoftheHTMLelement,and
thearrayportionholdsthecontentsoftheelement.
Checkingifthekeyina
pairs iterationisnumericisaquick
waytoapproximateisolatingarraylikeelements.Itsnot
perfect,butwillworkforthiscase.
Whenthecontentofthetagisinsertedintothebufferforthe
tablebased
opts ,thefollowinglineisused:
append_all(buffer, unpack(opts))
Luasbuiltinfunction
unpack convertsthearrayvaluesina
tabletovarargs.Thisfitsperfectlyintothe
append_all
functiondefinedabove.
Note:
Closing
ThissimpleimplementationofanHTMLbuilderthatshould
giveyouagoodintroductiontobuildingyourownDSLsin
Lua.
TheHTMLbuilderprovidedperformsnoHTMLescaping.Its
notsuitableforrenderinguntrustedinput.Ifyou'relooking
forawaytoenhancethebuilderthentryaddinghtml
escaping.Forexample:
Herearesomemoreguidestagged'lua'
Howitch.iousesCoroutinesfornon-blockingIO
PostedJune09,2016
UsingLuaRockstoinstallpackagesinthecurrentdirectory
PostedJanuary28,2016
DynamicscopinginLua
PostedJanuary24,2016
CloningafunctioninLua
PostedJuly08,2015
ImplementingsetfenvinLua5.2,5.3,andabove
PostedJuly08,2015
Anin-depthlookintotheM oonScriptclassimplementation
PostedJuly05,2015
AnintroductiontoParsingExpressionGrammarswithLPeg
PostedJuly04,2015
UsingPostgreSQLwithOpenResty
PostedJuly04,2015
4 Comments
leaf o.net
Recommend
Share
Login
Sort byBest
Jointhediscussion
mrbngle 6daysago
Thanksforthisarticle.Itwasagreatinspiration,andwellachallenge,to
me.Ireallyhadtotrythisout.Ispendacoupleofhoursandendedupwith
this:https://github.com/bungle/lua...(itisimplementedinLua).
Reply Share
DamilareAkinlaja 2monthsago
IamtryingtoimplementthistutorialusingMoonScriptbutIamgetting
error
https://gist.github.com/darmie...
Reply Share
setfenvdoesn'texistinLua5.2+,seethis:
http://leafo.net/guides/setfen...
Reply Share
daurnimat or 10 monthsago
Nicewriteup.Ididasimilarblogpostalongtimeback:
http://daurnimator.com/post/60...
Reply Share
ALSOONLEAFO.NET
1comment ayearago
6comments 5daysago
Wref Awesomefortworeasons.1)I
leaf o Itispossibletotriggertwo
couldreallyseemyselfusingthissite
formotivation.2)I'vebeenfollowing
moonscriptandlapis
thingsatonceinsideofopenresty,or
buildingsomethinginpureluathat
schedulestwo
MoonScript v0.2.3
2comments 3yearsago
leaf o Shouldbegoodnow,
accidentallyuploadedabuildofthe
manualusingoldbetaversionon
MoonScript.Thanks.(Tryahard
presentedtomePEGandPegineasy
way.:)
leafo.net2015GeneratedThuJun9 13:44:582016bySitegen