Pursuit of a DSL
Published on , 524 words, 2 minutes to read
A project we have been working on is Tetra. It is an extended services package in Go with Lua and Moonscript extensions. While writing Tetra, I have found out how to create a Domain Specific Language, and I would like to recommend Moonscript as a toolkit for creating DSL's.
Moonscript is a high level wrapper around Lua designed to make programming easier. We have used Moonscript heavily in Tetra because of how easy it is to make very idiomatic code in it.
Here is some example code from the Tetra codebase for making a command:
require "lib/elfs"
Command "NAMEGEN", ->
"> #{elfs.GenName!\upper!}"
That's it. That creates a command named NAMEGEN
that uses lib/elfs
to
generate goofy heroku-like application names based on names from Pokemon Vietnamese Crystal.
In fact, because this is so simple and elegant, you can document code like this inline.
Command Tutorial
In this file we describe an example command TEST
. TEST
will return some
information about the place the command is used as well as explain the
arguments involved.
Because Tetra is a polyglot of Lua, Moonscript and Go, the relevant Go objects will have their type definitions linked to on godoc
Declaring commands is done with the Command
macro. It takes in two arguments.
- The command verb
- The command function
It also can take in 3 arguments if the command needs to be restricted to IRCops only.
- The command verb
true
- The command function
The command function can have up to 3 arguments set when it is called. These are:
- The Client that originated the command call.
- The Destination or where the command was sent to. This will be a Client if the target is an internal client or a Channel if the target is a channel.
- The command arguments as a string array.
Command "TEST", (source, destination, args) ->
All scripts have client
pointing to the pseudoclient that the script is
spawned in. If the script name is chatbot/8ball
, the value of client
will
point to the chatbot
pseudoclient.
client.Notice source, "Hello there!"
This will send a NOTICE
to the source of the command saying "Hello there!".
client.Notice source, "You are #{source.Nick} sending this to #{destination.Target!} with #{#args} arguments"
All command must return a string with a message to the user. This is a good place to do things like summarize the output of the command or if it worked or not. If the command is oper-only, this will be the message logged to the services snoop channel.
"End of TEST output"
See? That easy.
Command "TEST", ->
"Hello!"
This is much better than Cod's
#All modules have a name and description
NAME="Test module"
DESC="Small example to help you get started"
def initModule(cod):
cod.addBotCommand("TEST", testbotCommand)
def destroyModule(cod):
cod.delBotCommand("TEST")
def testbotCommand(cod, line, splitline, source, destination):
"A simple test command"
return "Hello!"
Facts and circumstances may have changed since publication. Please contact me before jumping to conclusions if something seems wrong or unclear.
Tags: