Skip to main content
Full Moon is a lossless parser for Lua, meaning it preserves every single detail of your source code including comments, whitespace, and style choices. This is a fundamental feature that sets it apart from traditional parsers.

What is Lossless Parsing?

A lossless parser ensures that:
  1. Perfect Round-Trip: You can parse code to an AST and convert it back to get the exact original code
  2. Style Preservation: Your formatting choices, indentation, and spacing are maintained
  3. Comment Retention: All comments (single-line and multi-line) are kept with proper positioning
  4. Token Fidelity: Every character in your source code is accounted for in the parsed representation
With Full Moon, you can modify the AST directly and re-export it back to Lua, all while preserving the style in which you write.

Why Lossless Parsing Matters

Lossless parsing is essential for tools that need to:
  • Format code automatically (like rustfmt or prettier)
  • Refactor code en masse (like jscodeshift)
  • Provide accurate LSP features (go-to-definition, hover information)
  • Perform static analysis without losing context (like Luacheck)
  • Add or modify code while respecting existing style

How It Works

Full Moon achieves lossless parsing through a two-layer approach:

1. Tokenization Layer

The tokenizer converts source code into tokens while preserving trivia:
pub struct TokenReference {
    pub(crate) leading_trivia: Vec<Token>,
    pub(crate) token: Token,
    pub(crate) trailing_trivia: Vec<Token>,
}
Trivia includes:
  • Whitespace (spaces, tabs, newlines)
  • Single-line comments (-- comment)
  • Multi-line comments (--[[ comment ]])

2. AST Layer

The AST uses TokenReference objects instead of plain strings, ensuring every node carries its formatting information.

Example: Preserving Style

Consider this Lua code with specific formatting:
local x = 1  -- important variable
local   y   =   2  -- lots of spaces
A traditional parser might normalize this to:
local x = 1
local y = 2
But Full Moon preserves:
  • The exact spacing around =
  • The comment and its position
  • The multiple spaces in the second line

Parsing with Full Moon

use full_moon::parse;

let code = "local x = 1 -- comment";
let ast = parse(code)?;

// Convert back to string - identical to original!
assert_eq!(ast.to_string(), code);
From the source (src/lib.rs:79-94):
/// Creates an [`Ast`](ast::Ast) from Lua code.
/// Will use the most complete set of Lua versions enabled in your feature set.
///
/// # Errors
/// If the code passed cannot be tokenized, a TokenizerError will be returned.
/// If the code passed is not valid Lua 5.1 code, an AstError will be returned,
/// specifically AstError::UnexpectedToken.
///
/// ```rust
/// assert!(full_moon::parse("local x = 1").is_ok());
/// assert!(full_moon::parse("local x = ").is_err());
/// ```
pub fn parse(code: &str) -> Result<ast::Ast, Vec<Error>> {
    parse_fallible(code, LuaVersion::new()).into_result()
}

Trivia Types

Full Moon tracks these trivia types (src/tokenizer/structs.rs:242-323):
TokenType::Whitespace {
    characters: ShortString,
}

Position Tracking

Every token includes precise position information (src/tokenizer/structs.rs:852-875):
pub struct Position {
    pub(crate) bytes: usize,
    pub(crate) line: usize,
    pub(crate) character: usize,
}

impl Position {
    /// How many bytes, ignoring lines, it would take to find this position
    pub fn bytes(self) -> usize
    
    /// Index of the character on the line for this position
    pub fn character(self) -> usize
    
    /// Line the position lies on
    pub fn line(self) -> usize
}
This allows tools to:
  • Report error locations accurately
  • Implement “go to definition” features
  • Create precise source maps
  • Track changes across edits
Full Moon is heavily inspired by benjamn’s recast for JavaScript, which pioneered the lossless parsing approach for code transformation tools.

Use Cases

Lossless parsing enables these powerful applications:
  1. Code Formatters: Reformat code while preserving intentional style choices
  2. Refactoring Tools: Rename variables or restructure code without breaking formatting
  3. Static Analysis: Analyze code and report issues with exact locations
  4. Code Generation: Insert generated code that matches surrounding style
  5. Documentation Tools: Extract comments and associate them with code elements

Comparison: Lossy vs Lossless

AspectLossy ParserFull Moon (Lossless)
CommentsLostPreserved
WhitespaceNormalizedExact
Round-tripImpossiblePerfect
StyleStandardizedMaintained
Use for formattingNoYes
Use for analysisLimitedFull

Next Steps

AST Structure

Learn about the Abstract Syntax Tree structure

Tokenization

Deep dive into the tokenization process

Build docs developers (and LLMs) love