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 to a - b and not a(-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 the moonscript 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
Hover For Old
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!
Hover For Old
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"
Hover For Old
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]
Hover For Old
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!
Hover For Old
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 ImageMagick
  • web_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.