# MoonScript: a self-hosted compiler that compiles to Lua

> A language layer over Lua that strips down the syntax while keeping the runtime, now on its last release built on LPeg before a parser rewrite, with 3,469 stars and a 158-issue backlog.

**leafo/moonscript** — :crescent_moon: A language that compiles to Lua

- Repository: https://github.com/leafo/moonscript
- Website: https://moonscript.org
- Stars: 3,472 · Forks: 199
- Language: C
- License: not declared
- Published: 2026-10-07 · Updated: 2026-10-07 · Language: en
- Canonical page: https://hysenlabs.com/projects/leafo-moonscript

## Lua's syntax minus the punctuation, not a different runtime

MoonScript describes itself as a programmer friendly language that compiles into Lua, giving you the power of the fastest scripting language combined with a richer set of features. The runtime claim is specific: it runs on Lua 5.1 and above, including alternative runtimes like LuaJIT.

That Lua 5.1 floor is the whole design. Compiling to Lua rather than to bytecode means anything that embeds Lua can run MoonScript output, and LuaJIT compatibility follows from that rather than from separate work. The trade is that you inherit Lua's performance characteristics exactly, which is usually what you want and occasionally is not.

The syntax is where the compression happens. Function calls drop the parentheses, whitespace becomes meaningful for statement termination, and there is a class syntax with an implicit self. There is an online compiler and demo at moonscript.org/compiler if you want to see the transformation without installing anything.

Editor support is spread across separate repositories rather than bundled: Vim, Textadept, a Sublime and TextMate bundle, and an Emacs mode. None of them are in this repository, so a new editor means waiting for or writing another one.

## A compiler written in the language it compiles

The self-hosted aspect is the part of this project that changes how you work on it. MoonScript is a self-hosted compiler, meaning it is written in MoonScript itself. The README's contribution guidelines follow directly from that, and the first rule is absolute: edit .moon files, never modify the alongside .lua files directly.

After changing .moon files you run the compiler to regenerate the corresponding .lua files. Both extensions are committed to the repository, and the README gives two reasons why: so users can install and use MoonScript without compiling it themselves, and so the compiler bootstrapping process works consistently.

The bootstrap risk is real and the README addresses it directly. If you break something while working, you need a working MoonScript installation to rebuild the broken one, so it helps to have a separate installation available. The suggested approach is to check the repository out in another directory, or install it with LuaRocks to have a separate working version.

The tree shows the machinery: moon/ and moonscript/ hold the compiler and runtime, bin/ holds the executables, docs/ the documentation, spec/ the tests, and there is a rockspec named moonscript-dev-1 for development installs. There is also a directory called thoughts and a gen_rockspec.sh script, which suggests the packaging is partly generated rather than maintained by hand.

## The grammar lives in two generated files, and the README names the wrong one

The parser is a pgen grammar, and this is where a small documentation inconsistency sits that will cost you time if you follow the README literally.

The README says the parser is defined as a pgen grammar in moonscript/parse/grammar.moon, compiled to a native C module at moonscript/parse/native.c and a pure Lua equivalent at moonscript/parse/slow.lua. The slow.lua file is described as unused at runtime and kept for a future Lua-only distribution. Both generated files are checked in.

But the Makefile's generate target does not reference grammar.moon at all. It runs pgen against moonscript/parse/grammar.lua, with a separate invocation to produce native.c with vendor errors from moonscript/parse/errors.lua, then runs clang-format over the generated C, then runs pgen again for the slow.lua output.

So the grammar the build actually consumes is a .lua file, not the .moon file the README names. Either the README describes an intermediate source that has since been replaced, or one of the two is out of date. Either way, if you intend to change the grammar, the Makefile is the authority and it also tells you the two tool dependencies: pgen must be installed, and clang-format is required.

The Makefile also shows how the native module is built, with gcc compiling native.c into a shared object at optimisation level 3 against lua5.1 flags pulled from pkg-config. The LUA variable defaults to lua5.1 and can be overridden, and the Lua path is derived from luarocks at build time.

## Version 0.7 is the last release on LPeg

Version 0.7.0, published in July 2026, states up front that it will be the last release using LPeg, and that future versions will use a new parser hosted in a separate repository, leafo/moonscript-parser. The rest of that release is syntax bug fixes, cleanups, a few new features and an enhanced linter.

The headline feature is destructuring of function arguments, which previously only worked on assignment. A function argument can now be written as a table literal to destructure the value passed in, and the patterns supported are the same ones as assignment, including nested patterns, numeric keys and @field targets. A constructor taking a table of coordinates can pull x and y straight out of the argument and assign them to instance fields, and a render function can accept a dimensions table alongside its other parameters.

The compiled Lua shows the cost of the convenience: the argument becomes an internal name and the fields are unpacked at the top of the function body, with any default value applied as an explicit nil check. So a pattern such as width and height is not free at runtime, it just costs one intermediate local and two assignments.

One constraint worth knowing: destructured arguments always declare fresh locals, so a name in the pattern shadows rather than assigns to an existing variable.

Version 0.6.0, published in January 2026, was the command-line overhaul. Argument parsing moved from alt_getopt to argparse for both moon and moonc, giving better help text and more robust option handling. moon gained an -e and --execute flag for running code directly. moonc gained a --transform option for custom AST transformations before compilation. Option aliases were added so -w is also --watch, -l is also --lint, and -t is also --output-to. A moon-tags script appeared for generating ctags-compatible tag files, with flags for Lapis route detection, line numbers and header suppression.

Between those two sits a decade of nothing. Version 0.5.0 was published in September 2016.

## Testing needs Busted, Loadkit and a built parser

Tests are written in MoonScript and use Busted, which means you need MoonScript itself installed plus Loadkit. From the repository root, running the test suite is one command.

```bash
busted
```

That simplicity hides the dependency chain. The Makefile's test target depends on build and on the native parser shared object, so a bare busted call works only once those exist. The target also sets LUA_PATH and LUA_CPATH explicitly, with the local directory placed first so the freshly built module wins over any installed copy.

The build target itself is three steps: run the moon and moonscript directories through bin/moonc, write a shebang line into bin/moon, then append the compiled output of bin/moon.moon to that file, finishing with a vim modeline. There is also a build_from_system target that uses an already installed moonc rather than bootstrapping, which is the recovery path when the tree's own compiler is broken.

Writing specs is described as more complicated than running them, and the README points to a spec writing guide in spec/README.md for that. The CI configuration runs a spec workflow, with AppVeyor handling the Windows build, and precompiled Windows binaries are published on the releases page under tags beginning win32-, built from a separate binaries branch.

The repository was last pushed on 2026-08-13, with 3,469 stars, 199 forks and 158 open issues. The issue count is high relative to the star count for a language whose most significant development is now happening in a different repository.

## MIT in the README, absent from the repository metadata

The README carries a full MIT licence text with copyright attributed to Leaf Corcoran, dated 2025. The repository metadata, however, records the license field as null, which is what GitHub shows when it cannot determine a licence from the files.

There is no LICENSE file in the tree, which explains the null: the terms are in the README rather than in a file the detector recognises. The README heading even spells it out as License (MIT), so there is no real ambiguity about the terms, only about why the metadata is empty. If your tooling checks licences automatically, that gap will produce a false negative, and the fix is to read the README.

A second thing worth noting is the repository description, which reads A language that compiles to Lua and carries a crescent moon emoji. That is the only place a reader meets the project's older, jokey identity. The README itself is straightforward, and the badge it displays is a picture rather than text.

What the documentation does not cover, and cannot, is how MoonScript relates to the parser rewrite. The 0.7 release notes point at a separate repository for what comes next. Anyone planning to maintain MoonScript long-term needs to watch both.

## Conclusion

MoonScript's pitch is narrow and honest: keep Lua's runtime and give up some of its punctuation. The parts worth checking before you commit are the self-hosted build, which means you edit .moon files and regenerate .lua, and the parser transition, since 0.7 is the last release on LPeg and the replacement lives in a separate repository. Start by installing from LuaRocks and running busted, then read the parse directory before you touch the grammar, because regenerating it needs pgen and clang-format.

## FAQ

### What Lua versions does MoonScript run on?

Lua 5.1 and above, including alternative runtimes such as LuaJIT. The Makefile defaults to lua5.1 for building and can be pointed at another interpreter through the LUA variable, and the project does not bundle a Lua runtime of its own.

### How do I contribute to MoonScript?

Edit .moon files and never modify the alongside .lua files directly, then run the compiler to regenerate the Lua versions. Both file types are committed, the .moon ones being the source of truth. Keep a separate working MoonScript installation available, because the compiler is self-hosted and a broken tree needs a working compiler to be rebuilt.

### What is changing in MoonScript after version 0.7?

The parser. Version 0.7.0 states it is the last release using LPeg, and that later versions will use a new parser developed in the separate leafo/moonscript-parser repository. If you depend on the parsing layer rather than only on the syntax, that transition is the thing to watch.

### How do I run the MoonScript test suite?

Tests use Busted and require MoonScript and Loadkit installed. From the repository root, run busted. The Makefile test target additionally depends on building the compiler and the native parser, and sets LUA_PATH and LUA_CPATH so the freshly built module takes priority.

### Is MoonScript still being developed?

Yes, with releases in January and July 2026, but the centre of activity is shifting. Version 0.7 shipped destructuring of function arguments and a better linter, and it was also the last LPeg release, so parser work is moving to another repository. The main repository was last pushed on 2026-08-13.

## Sources

- [Issues](https://github.com/leafo/moonscript/issues)
- [leafo/moonscript on GitHub](https://github.com/leafo/moonscript)
- [Project website](https://moonscript.org)
- [README](https://github.com/leafo/moonscript/blob/master/README.md)
- [Releases](https://github.com/leafo/moonscript/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/leafo-moonscript
