Domain-Specific Languages for Games

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
User avatar
ejmr
Party member
Posts: 302
Joined: Fri Jun 01, 2012 7:45 am
Location: South Carolina, U.S.A.
Contact:

Domain-Specific Languages for Games

Post by ejmr »

Does anyone here have experience creating domain-specific languages (DSL) within Lua? I am building one for a visual novel engine, which is itself part of a larger game project. This howto document shows what my DSL currently looks like:

https://github.com/ejmr/LNVL/blob/master/docs/Howto.md

I would be grateful if anyone has experience or links to resources online about DSLs in Lua, particularly with regard to game development.
ejmr :: Programming and Game-Dev Blog, GitHub
南無妙法蓮華經
User avatar
Inny
Party member
Posts: 652
Joined: Fri Jan 30, 2009 3:41 am
Location: New York

Re: Domain-Specific Languages for Games

Post by Inny »

Lua technically is a DSL, as it was born as one (when PETROBRAS hired TeCGraf at PUC-Rio to write a DSL for them). There are two interesting features of Lua's syntax that makes this possible:
1. You can pass strings or tables into functions without needing parenthesis
2. You can set metatables on tables to make it behave as a function

So this code is valid:

Code: Select all

Hallway = Room {
  description = "a long hallway",
  fits = 1,
  smells = "great",
}
The trick is as I said, metatables:

Code: Select all

Room = setmetatable({}, {
  __call = function(original, created)
    -- manipulate the new room here
    return created
  end
})
This is basically how most OOP frameworks are constructed.
User avatar
kikito
Inner party member
Posts: 3153
Joined: Sat Oct 03, 2009 5:22 pm
Location: Madrid, Spain
Contact:

Re: Domain-Specific Languages for Games

Post by kikito »

Inny wrote:1. You can pass strings or tables into functions without needing parenthesis
2. You can set metatables on tables to make it behave as a function
I would add a couple more to that list:
3. Functions are first-class language members. This means that you can define them and pass them as parameters when you need to.
4. The "environment" of a function is a table, and it's manipulable.

Functions are great for providing callbacks ("do this when this condition is matched"), and scopes. For example, in busted, the testing framework I currently use for Lua, most of the commands it provides accept a function:

Code: Select all

describe("A number in Lua", function()
  it("can be even or uneven, but never both", function()
    assert.equals(0,4%2)
    assert.equals(1,5%2)
  end)
end)
Point [4] is a bit more advanced. In Lua 5.1 it's done with getfenv and setfenv, and you probably don't need it right away. It also changes in Lua 5.2.
When I write def I mean function.
User avatar
ejmr
Party member
Posts: 302
Joined: Fri Jun 01, 2012 7:45 am
Location: South Carolina, U.S.A.
Contact:

Re: Domain-Specific Languages for Games

Post by ejmr »

Thanks Inny and kikito for the ideas. And the link to 'busted', useful-looking framework.
ejmr :: Programming and Game-Dev Blog, GitHub
南無妙法蓮華經
kclanc
Citizen
Posts: 89
Joined: Sun Jan 29, 2012 6:39 pm

Re: Domain-Specific Languages for Games

Post by kclanc »

This is a really well documented and implemented project. Lua has a lot of potential as a platform for internal DSLs. In my opinion, this potential is not tapped often enough.

I have two ideas for this project. I'm not convinced that they are things that you should implement; however, I'm posting them because I think they are interesting.

Idea 1:

An alternative way to implement something like this would be to use coroutines. Instead of representing a scene as a sequence of expressions, you would represent it using a function. As with the current implementation, you would still be able to associate extra data, such as background images, with the scene by adding values into a table.

Example 4 might look something like this:

Code: Select all

local END = LNVL.Scene()
function END.script()
	Jeff “I hate you. I hate you so much.”
	...
end

local NOT_GUILTY = NOT_GUILTY.Scene()
function NOT_GUILTY.script()
	Eric “Not Guilty, your honor!”
	...
end

local THE_TRUTH = LNVL.Scene()
function THE_TRUTH.script()
	Eric “Ok, so we infringed on copyrighted material...”
	…
	LNVL:jumpTo( END() )
end

local START = LNVL.Scene()
START.background = “img/courtroom.png”
function START.script()
	Jeff “Isn't this a copyright infringement again?”
	Eric “Would you just shut up...”
	Judge “I have reviewed the evidence. How do you plead?”

	if LNVL.Menu(“Not Guilty”, “Obviously Guilty”) == “Not Guilty” then
		LNVL:jumpTo( NOT_GUILTY() )
	else
		LNVL:jumpTo( THE_TRUTH() )
	end
end
With this approach, I think it would be sensible to give the programmer two options for
scene transititions. One would be to call a function which sets up the scene and then executes its events.

Using this, you could organize your novel hierarchically as follows.

Code: Select all

function Start.script()
	Beginning()
	Middle()
	End()
end
The other option would be to set up a transition which occurs in a non-hierarchical manner after the current scene has finished executing.

Code: Select all

local Start = LNVL.NewScene()
Start.nextScene = “Beginning”
function Start.script ()
...
end
There would be pros and cons to this approach.

Pros:

-Debugger Friendly
-Scenes can be composed in a hierarchical fashion. (Actually this is an iffy pro—a screenwriter would never do this, so why would someone writing a visual novel?)
-Programmer can take advantage of constructs such as local variable definitions, if statements, and parameterized function calls

Cons:

-Less ability to reflect on the runtime data that represents scenes
-Function definitions are ugly in lua
-Probably a lot of other things that I didn't think of

Idea 2:

Another idea that I have is an alternative notation for monologues. If one chose to use the coroutine approach, it would be debugger friendly. If not, the notation *might* be slightly preferable, but I think the current notation works fine.

The new notation would look like this:

Code: Select all

SomeDude 
	“Hello reader.”
	“I am SomeDude.”
SomeDude's call metamethod would take one string argument. However, the call metamethod would return SomeDude, allowing the programmer to supply additional lines for a monologue.

Finally, responding to your original question, I imagine that you have already read the paper Building Domain-Specific Languages over a Language Framework by the lua creators.

BTW. I can't run most of the examples because the image resources that they depend on do not seem to have been included.

Keep up the good work.
User avatar
ejmr
Party member
Posts: 302
Joined: Fri Jun 01, 2012 7:45 am
Location: South Carolina, U.S.A.
Contact:

Re: Domain-Specific Languages for Games

Post by ejmr »

kclanc wrote:I have two ideas for this project. I'm not convinced that they are things that you should implement; however, I'm posting them because I think they are interesting….
Thank you for the ideas. I agree both are interesting, and worth considering. I would like to use co-routines to make debugging easier, as you mention, but I have not been able to find a way to make their use feel ‘pedestrian’ enough, for lack of a better term.
Finally, responding to your original question, I imagine that you have already read the paper Building Domain-Specific Languages over a Language Framework by the lua creators.
Not in many years, to the point where I had forgotten about it, heh. Thanks for the reminder. :)
BTW. I can't run most of the examples because the image resources that they depend on do not seem to have been included.
Ah true; I need to fix that eventually. The project is in lockstep development with another and is currently using art assets from that which I don’t want to permanently include. But at some point I will change them around to use some other assets, either something custom or public-domain.
ejmr :: Programming and Game-Dev Blog, GitHub
南無妙法蓮華經
mlepage
Prole
Posts: 10
Joined: Sat Jun 30, 2012 9:26 pm

Re: Domain-Specific Languages for Games

Post by mlepage »

Inny wrote:Lua technically is a DSL, as it was born as one (when PETROBRAS hired TeCGraf at PUC-Rio to write a DSL for them). There are
The trick is as I said, metatables:

Code: Select all

Room = setmetatable({}, {
  __call = function(original, created)
    -- manipulate the new room here
    return created
  end
})
In this case, you can simplify the table+metatable to just a function:

Code: Select all

Room = function(original, created)
  -- manipulate the new room here
  return created
end
mlepage
Prole
Posts: 10
Joined: Sat Jun 30, 2012 9:26 pm

Re: Domain-Specific Languages for Games

Post by mlepage »

I've been playing around with Lua internal DSLs as well.

Take something like this:

Code: Select all

local END = LNVL.Scene()
function END.script()
   Jeff “I hate you. I hate you so much.”
   ...
end

local NOT_GUILTY = NOT_GUILTY.Scene()
function NOT_GUILTY.script()
   Eric “Not Guilty, your honor!”
   ...
end
Now assume your scenes are not going to be nested within one another. That is, you're only ever building one (the "context") at a time.

Now, instead of assigning the scene directly into a local, and function into a member variable of the scene, you can rewrite the above like this:

Code: Select all

scene 'END'
function script()
   Jeff “I hate you. I hate you so much.”
   ...
end

scene 'NOT_GUILTY'
function script()
   Eric “Not Guilty, your honor!”
   ...
end
The scene function takes a string, creates a scene (the "context"), and stashes it away. The function is assigned to a global variable called 'script'. What you do is configure the environment when loading the DSL so that creating a global named 'script' checks that it is a function, and instead of creating a global it stores it in the context scene.

If you have additional functions besides 'script' per scene, you just give them different names. If you have simple properties, you can make them functions taking a string and configuring the context scene appropriately. So your scene could have setting 'outside' and time 'night'.

I've tried this and it makes for quite a readable DSL.

As for where your scenes get stashed, you can create global variables for them if they are uniquely named (they probably are), automatically in the scene creator function. Also remember, this "global" environment can still be unique to loading the DSL and so not actually trample the "real" global environment.

Using globals allows you to write a function that just uses variable NOT_GUILTY as a scene, instead of having to do something like getScene('NOT_GUILTY'). This works (even in the script function for the END scene) because when the function is compiled, the NOT_GUILTY scene doesn't have to have been made yet; the global reference is only resolved when the script function is called. Locals work too, but you probably don't need them.
kclanc
Citizen
Posts: 89
Joined: Sun Jan 29, 2012 6:39 pm

Re: Domain-Specific Languages for Games

Post by kclanc »

Interesting idea, mlepage. Declaring a single function with the same name multiple times might be a little confusing from the perspective of default lua semantices, but it does use less syntax and is more readable.
User avatar
ejmr
Party member
Posts: 302
Joined: Fri Jun 01, 2012 7:45 am
Location: South Carolina, U.S.A.
Contact:

Re: Domain-Specific Languages for Games

Post by ejmr »

Neat idea mlepage, and something that would have never crossed my mind. Many thanks for sharing.
ejmr :: Programming and Game-Dev Blog, GitHub
南無妙法蓮華經
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot] and 1 guest