Skip to main content

Overview

Full Moon supports multiple Lua versions and dialects through Cargo feature flags. You can parse Lua 5.1, 5.2, 5.3, 5.4, Luau (Roblox), LuaJIT, and CFX Lua (FiveM).

Feature Flags

Enable Lua versions in your Cargo.toml:
[dependencies]
full_moon = { version = "2.1", features = ["lua54"] }

Available Features

lua52
feature
Enables Lua 5.2 syntax support including:
  • goto statements
  • Labels (::)
  • Hexadecimal float literals
lua53
feature
Enables Lua 5.3 syntax (includes lua52):
  • Bitwise operators (&, |, ~, <<, >>)
  • Integer division (//)
  • Floor division
lua54
feature
Enables Lua 5.4 syntax (includes lua53):
  • Attributes in local declarations (local x <const> = 1)
  • Close variables (<close>)
  • To-be-closed variables
luau
feature
Enables Luau (Roblox Lua) syntax:
  • Type annotations
  • Generics
  • String interpolation
  • If expressions
  • Continue statements
  • Compound assignments
luajit
feature
Enables LuaJIT-specific syntax:
  • goto statements (like Lua 5.2)
  • Label support
cfxlua
feature
Enables CFX Lua (FiveM) syntax:
  • Set constructor syntax (.a in tables)
  • Compound assignments (includes lua54)

Version Relationships

Enabling lua54 automatically enables lua53 and lua52. Similarly, cfxlua includes lua54 features.

Selecting a Version at Runtime

Use LuaVersion to control which version to parse:
use full_moon::{parse_fallible, LuaVersion};

// Automatic version selection (based on features)
let auto_version = LuaVersion::new();
let ast = parse_fallible(code, auto_version);

Explicit Version Selection

use full_moon::LuaVersion;

// Parse as Lua 5.1
let lua51 = LuaVersion::lua51();

// Parse as Lua 5.2
#[cfg(feature = "lua52")]
let lua52 = LuaVersion::lua52();

// Parse as Lua 5.3
#[cfg(feature = "lua53")]
let lua53 = LuaVersion::lua53();

// Parse as Lua 5.4
#[cfg(feature = "lua54")]
let lua54 = LuaVersion::lua54();

// Parse as Luau
#[cfg(feature = "luau")]
let luau = LuaVersion::luau();

Version-Specific Syntax

Lua 5.2: Goto and Labels

::start::
print("hello")
goto start
#[cfg(any(feature = "lua52", feature = "luajit"))]
use full_moon::ast::{Stmt, Goto, Label};

#[cfg(any(feature = "lua52", feature = "luajit"))]
fn handle_goto(stmt: &Stmt) {
    match stmt {
        Stmt::Goto(goto_stmt) => {
            println!("Goto: {}", goto_stmt.label_name());
        }
        Stmt::Label(label) => {
            println!("Label: {}", label.name());
        }
        _ => {}
    }
}

Lua 5.4: Attributes

local x <const> = 10
local file <close> = io.open("file.txt")
#[cfg(feature = "lua54")]
use full_moon::ast::Attribute;

#[cfg(feature = "lua54")]
fn handle_attributes(local: &LocalAssignment) {
    for attr in local.attributes() {
        if let Some(attribute) = attr {
            println!("Attribute: {}", attribute.name());
        }
    }
}

Luau: Type Annotations

function add(a: number, b: number): number
    return a + b
end

type Point = { x: number, y: number }
#[cfg(feature = "luau")]
use full_moon::ast::{
    TypeSpecifier,
    TypeDeclaration,
    TypeInfo,
};

#[cfg(feature = "luau")]
fn handle_types(body: &FunctionBody) {
    // Check parameter types
    for type_spec in body.type_specifiers() {
        if let Some(spec) = type_spec {
            println!("Parameter type: {}", spec.type_info());
        }
    }
    
    // Check return type
    if let Some(return_type) = body.return_type() {
        println!("Return type: {}", return_type.type_info());
    }
}

Luau: String Interpolation

local name = "World"
print(`Hello, {name}!`)
#[cfg(feature = "luau")]
use full_moon::ast::InterpolatedString;

#[cfg(feature = "luau")]
fn handle_interpolation(expr: &Expression) {
    if let Expression::InterpolatedString(interp) = expr {
        for segment in interp.segments() {
            println!("Segment: {}", segment);
        }
    }
}

Luau: If Expressions

local result = if condition then value1 else value2
#[cfg(feature = "luau")]
use full_moon::ast::IfExpression;

#[cfg(feature = "luau")]
fn handle_if_expr(expr: &Expression) {
    if let Expression::IfExpression(if_expr) = expr {
        println!("Condition: {}", if_expr.condition());
        println!("True branch: {}", if_expr.if_expression());
        println!("False branch: {}", if_expr.else_expression());
    }
}

Compound Assignments (Luau & CFX Lua)

x += 5
y *= 2
z //= 3
#[cfg(any(feature = "luau", feature = "cfxlua"))]
use full_moon::ast::{CompoundAssignment, CompoundOp};

#[cfg(any(feature = "luau", feature = "cfxlua"))]
fn handle_compound(stmt: &Stmt) {
    if let Stmt::CompoundAssignment(compound) = stmt {
        match compound.compound_operator() {
            CompoundOp::PlusEqual(_) => println!("+="),
            CompoundOp::MinusEqual(_) => println!("-="),
            CompoundOp::StarEqual(_) => println!("*="),
            CompoundOp::SlashEqual(_) => println!("/="),
            _ => println!("Other compound op"),
        }
    }
}

CFX Lua: Set Constructors

local set = { .a, .b, .c }
-- Equivalent to: { a = true, b = true, c = true }
#[cfg(feature = "cfxlua")]
use full_moon::ast::Field;

#[cfg(feature = "cfxlua")]
fn handle_set_constructor(table: &TableConstructor) {
    for field in table.fields() {
        if let Field::SetConstructor { dot, name } = field {
            println!("Set field: {}", name);
        }
    }
}

Multi-Version Support

If you need to support multiple versions in one binary:
[dependencies]
full_moon = { version = "2.1", features = ["lua54", "luau"] }
use full_moon::{parse_fallible, LuaVersion};

enum TargetVersion {
    Lua51,
    Lua54,
    Luau,
}

fn parse_for_version(code: &str, target: TargetVersion) -> full_moon::ast::Ast {
    let version = match target {
        TargetVersion::Lua51 => LuaVersion::lua51(),
        #[cfg(feature = "lua54")]
        TargetVersion::Lua54 => LuaVersion::lua54(),
        #[cfg(feature = "luau")]
        TargetVersion::Luau => LuaVersion::luau(),
        #[cfg(not(feature = "lua54"))]
        TargetVersion::Lua54 => panic!("lua54 feature not enabled"),
        #[cfg(not(feature = "luau"))]
        TargetVersion::Luau => panic!("luau feature not enabled"),
    };
    
    parse_fallible(code, version).into_ast()
}

Feature Detection

Check which features are enabled at compile time:
// Check for Lua 5.2+
#[cfg(feature = "lua52")]
fn has_goto_support() -> bool {
    true
}

#[cfg(not(feature = "lua52"))]
fn has_goto_support() -> bool {
    false
}

// Check for Luau
#[cfg(feature = "luau")]
fn has_type_support() -> bool {
    true
}

#[cfg(not(feature = "luau"))]
fn has_type_support() -> bool {
    false
}

Lua 5.1 Only

full_moon = "2.1"
Minimal features, most compatible

Modern Lua

full_moon = { version = "2.1", features = ["lua54"] }
Full Lua 5.4 support with all modern features

Roblox/Luau

full_moon = { version = "2.1", features = ["luau"] }
Type annotations and Roblox-specific syntax

Game Development

full_moon = { version = "2.1", features = ["cfxlua"] }
FiveM/CFX Lua with compound assignments

Version-Specific AST Nodes

Different features enable different AST node types:
Node TypeFeature RequiredDescription
Gotolua52 or luajitGoto statements
Labellua52 or luajitLabel declarations
Attributelua54Variable attributes
TypeDeclarationluauType definitions
TypeAssertionluauType assertions
IfExpressionluauIf expressions
InterpolatedStringluauString interpolation
CompoundAssignmentluau or cfxluaCompound operators
SetConstructor (Field)cfxluaSet syntax in tables

Best Practices

1

Enable Only What You Need

Each feature increases compile time. Only enable versions you actually support.
2

Use Conditional Compilation

Wrap version-specific code in #[cfg(feature = "...")] blocks.
3

Document Version Requirements

Clearly document which Lua versions your tool supports.
4

Test Against Multiple Versions

If supporting multiple versions, test your code against each one.

Next Steps

Parsing Guide

Learn how to parse with different versions

API Reference

Explore version-specific APIs

Build docs developers (and LLMs) love