MoonScript v0.2.4
I'm happy to announce MoonScript version 0.2.4, the CoffeeScript inspired language that compiles to Lua. It’s been about 5 months since the last release.
As always, if you've got any questions or want to tell me about how you are using MoonScript you can email me or contact me on twitter.
Changes
- The way the subtraction operator works has changed. There was always a little
confusion as to the rules regarding whitespace around it and it was
recommended to always add whitespace around the operator when doing
subtraction. Not anymore. Hopefully it now works how you would
expect. (
a-b
compiles toa - b
and nota(-b)
anymore). - The
moon
library is no longer sets a global variable and instead returns the module. Your code should now be:
moon = require "moon"
- Generated code will reuse local variables when appropriate. Local variables are guaranteed to not have side effects when being accessed as opposed to expressions and global variables. MoonScript will now take advantage of this and reuse those variable without creating and copying to a temporary name. See the examples below.
- Reduced the creation of anonymous functions that are called immediately. MoonScript uses this technique to convert a series of statements into a single expression. It’s inefficient because it allocates a new function object and has to do a function call. It also obfuscates stack traces. MoonScript will flatten these functions into the current scope in a lot of situations now. See the examples below.
- Reduced the amount of code generated for classes. Parent class code it left out if there is no parent. See the examples below.
New Things
- You can now put line breaks inside of string literals. It will be replaced
with
\n
in the generated code.
x = "hello
world"
- Added
moonscript.base
module. It’s a way of including themoonscript
module without automatically installing the moonloader. - You are free to use any whitespace around the name list in an import statement. It has the same rules as an array table, meaning you can delimit names with line breaks.
import a, b
c, d from z
- Added significantly better tests. Previously the testing suite would only verify that code compiled to an expected string. Now there are unit tests that execute the code as well. This will make it easier to change the generated output while still guaranteeing the semantics are the same.
Bug Fixes
b
is not longer treated as self assign in{ a : b }
- load functions will return
nil
instead of throwing error, as described in documentation - fixed an issue with
moon.mixin
where it did not work as described
Code Generation Changes
As mentioned above a lot has changed about the generated code. Here’s a quick overview with examples. All of these address special cases, if it’s not possible to write the optimized form the original form will be generated.
Reusing locals:
input = { }
{ :a, :b, d: { one, two, } } = input
local input = { }
local a, b, one, two
do
local _obj_0 = input
a, b, one, two = _obj_0.a, _obj_0.b, _obj_0.d[1], _obj_0.d[2]
end
local input = { }
local a, b, one, two
a, b, one, two = input.a, input.b, input.d[1], input.d[2]
No unnecessary with
variable:
with thing = "hello!"
print \upper!
do
local _with_0 = "hello!"
local thing = _with_0
print(_with_0:upper())
end
do
local thing = "hello!"
print(thing:upper())
end
Remove unnecessary inheritance code with simple classes:
class Hello
new: => print "hello"
local Hello
do
local _parent_0 = nil
local _base_0 = { }
_base_0.__index = _base_0
if _parent_0 then
setmetatable(_base_0, _parent_0.__base)
end
local _class_0 = setmetatable({
__init = function(self)
return print("hello")
end,
__base = _base_0,
__name = "Hello",
__parent = _parent_0
}, {
__index = function(cls, name)
local val = rawget(_base_0, name)
if val == nil and _parent_0 then
return _parent_0[name]
else
return val
end
end,
__call = function(cls, ...)
local _self_0 = setmetatable({}, _base_0)
cls.__init(_self_0, ...)
return _self_0
end
})
_base_0.__class = _class_0
if _parent_0 and _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0)
end
Hello = _class_0
end
local Hello
do
local _base_0 = { }
_base_0.__index = _base_0
local _class_0 = setmetatable({
__init = function(self)
return print("hello")
end,
__base = _base_0,
__name = "Hello"
}, {
__index = _base_0,
__call = function(cls, ...)
local _self_0 = setmetatable({}, _base_0)
cls.__init(_self_0, ...)
return _self_0
end
})
_base_0.__class = _class_0
Hello = _class_0
end
Comprehensions reuse local, don’t make temporary function:
things = {}
x = [a for a in *things]
local things = { }
local x = (function()
local _accum_0 = { }
local _len_0 = 1
local _list_0 = things
for _index_0 = 1, #_list_0 do
a = _list_0[_index_0]
_accum_0[_len_0] = a
_len_0 = _len_0 + 1
end
return _accum_0
end)()
local things = { }
local x
do
local _accum_0 = { }
local _len_0 = 1
for _index_0 = 1, #things do
a = things[_index_0]
_accum_0[_len_0] = a
_len_0 = _len_0 + 1
end
x = _accum_0
end
Accumulated loops also don’t use temporary function:
y = while check_something!
math.random!
local y = (function()
local _accum_0 = { }
local _len_0 = 1
while check_something() do
_accum_0[_len_0] = math.random()
_len_0 = _len_0 + 1
end
return _accum_0
end)()
local y
do
local _accum_0 = { }
local _len_0 = 1
while check_something() do
_accum_0[_len_0] = math.random()
_len_0 = _len_0 + 1
end
y = _accum_0
end
Other Stuff
Libraries
Some updates for libraries written in MoonScript:
- Lapis, the MoonScript powered web framework has come out with version 0.0.2.
magick
, LuaJIT FFI bindings to ImageMagickweb_sanitize
, HTML sanitization
Games
Ludum Dare happened again, and I wrote another game in MoonScript:
Thanks
Thanks to everyone who provided feedback for this release. See you next time.