Skip to main content

Overview

Loretta supports multiple Lua versions and dialects through LuaSyntaxOptions. Each preset configures the parser to accept syntax features specific to that Lua version or dialect.

Available Presets

Loretta provides the following built-in presets:
  • Lua51 - Lua 5.1
  • Lua52 - Lua 5.2
  • Lua53 - Lua 5.3
  • Lua54 - Lua 5.4
  • LuaJIT20 - LuaJIT 2.0
  • LuaJIT21 - LuaJIT 2.1-beta3
  • GMod - Garry’s Mod Lua (GLua)
  • Luau - Luau (Roblox Lua)
  • FiveM - FiveM Lua
  • All - Accepts all syntax features (without integer parsing)
  • AllWithIntegers - Accepts all syntax features with integer parsing

Using Presets

using Loretta.CodeAnalysis.Lua;

// Parse with Lua 5.4 syntax
var tree = LuaSyntaxTree.ParseText(
    "local x <const> = 10",
    new LuaParseOptions(LuaSyntaxOptions.Lua54)
);

// Parse with Luau syntax
var tree = LuaSyntaxTree.ParseText(
    "local x: number = 10",
    new LuaParseOptions(LuaSyntaxOptions.Luau)
);

// Parse with GLua syntax
var tree = LuaSyntaxTree.ParseText(
    "// C-style comment\nlocal x = 10",
    new LuaParseOptions(LuaSyntaxOptions.GMod)
);

Preset Details

Lua 5.1

Baseline Lua version with minimal features:
var options = LuaSyntaxOptions.Lua51;
Features:
  • Basic Lua 5.1 syntax
  • Shebang support (#!/usr/bin/env lua)
  • Invalid escape sequences accepted (Lua 5.1 bug compatibility)
Not supported:
  • Goto statements
  • Hexadecimal escapes in strings
  • Empty statements
  • Bitwise operators
  • And many modern features

Lua 5.2

Builds on Lua 5.1:
var options = LuaSyntaxOptions.Lua52;
Adds:
  • Goto statements and labels
  • Empty statements (bare semicolons)
  • Hexadecimal escapes in strings (\x00)
  • Hexadecimal float literals
  • Whitespace escape (\z)
  • Nesting of long strings
Example:
var code = @"
::start::
local x = 0x1.8p3  -- Hex float
if x > 0 then
    goto start
end
";

var tree = LuaSyntaxTree.ParseText(
    code,
    new LuaParseOptions(LuaSyntaxOptions.Lua52)
);

Lua 5.3

Builds on Lua 5.2:
var options = LuaSyntaxOptions.Lua53;
Adds:
  • Bitwise operators (&, |, ~, <<, >>, ~)
  • Integer division (//)
  • Unicode escapes (\u{XXXX})
  • Integer literals (64-bit)
Example:
var code = @"
local a = 0xFF
local b = 0x10
local c = a & b     -- Bitwise AND
local d = 10 // 3   -- Integer division
local e = "\u{1F600}"  -- Unicode emoji
";

var tree = LuaSyntaxTree.ParseText(
    code,
    new LuaParseOptions(LuaSyntaxOptions.Lua53)
);

Lua 5.4

Builds on Lua 5.3:
var options = LuaSyntaxOptions.Lua54;
Adds:
  • Variable attributes (<const>, <close>)
Example:
var code = @"
local x <const> = 10
local file <close> = io.open('test.txt')
";

var tree = LuaSyntaxTree.ParseText(
    code,
    new LuaParseOptions(LuaSyntaxOptions.Lua54)
);

LuaJIT 2.0

LuaJIT-specific features:
var options = LuaSyntaxOptions.LuaJIT20;
Features:
  • Based on Lua 5.1 with select Lua 5.2 features
  • Goto statements (from Lua 5.2)
  • Hexadecimal escapes and floats
  • LuaJIT identifier rules (accepts characters >= 0xF7)
  • Number suffixes (LL, ULL, i)
  • Nesting of long strings
Example:
var code = @"
local x = 10LL     -- LuaJIT number suffix
local y = 0x1.8p3  -- Hex float
goto skip
print('skipped')
::skip::
";

var tree = LuaSyntaxTree.ParseText(
    code,
    new LuaParseOptions(LuaSyntaxOptions.LuaJIT20)
);

LuaJIT 2.1

Builds on LuaJIT 2.0:
var options = LuaSyntaxOptions.LuaJIT21;
Adds:
  • Binary number literals (0b1010)
  • Unicode escapes
Example:
var code = @"
local binary = 0b1010
local unicode = "\u{1F600}"
";

var tree = LuaSyntaxTree.ParseText(
    code,
    new LuaParseOptions(LuaSyntaxOptions.LuaJIT21)
);

GMod (Garry’s Mod)

Based on LuaJIT 2.0 with additional features:
var options = LuaSyntaxOptions.GMod;
Adds:
  • C-style comments (// and /* */)
  • C-style boolean operators (&&, ||, !, !=)
  • Continue statement (keyword style)
Example:
var code = @"
// C++ style comment
/* Multi-line
   comment */

local x = 10
if x > 5 && x < 15 then  // C-style AND
    print('in range')
end

for i = 1, 10 do
    if i == 5 then
        continue  -- Continue keyword
    end
    print(i)
end
";

var tree = LuaSyntaxTree.ParseText(
    code,
    new LuaParseOptions(LuaSyntaxOptions.GMod)
);

Luau (Roblox)

Roblox’s Luau dialect:
var options = LuaSyntaxOptions.Luau;
Features:
  • Type annotations
  • Compound assignments (+=, -=, etc.)
  • If expressions
  • Continue statement (contextual keyword)
  • Interpolated strings with backticks
  • Underscores in number literals
  • Binary number literals
  • Floor division
Example:
var code = @"
local x: number = 10
local y: string = 'hello'

-- Type annotations on functions
local function add(a: number, b: number): number
    return a + b
end

-- Compound assignment
x += 5

-- If expression
local result = if x > 10 then 'big' else 'small'

-- Interpolated strings
local name = 'World'
local message = `Hello, {name}!`

-- Continue in loops
for i = 1, 10 do
    if i == 5 then
        continue
    end
    print(i)
end
";

var tree = LuaSyntaxTree.ParseText(
    code,
    new LuaParseOptions(LuaSyntaxOptions.Luau)
);

FiveM

Builds on Lua 5.3:
var options = LuaSyntaxOptions.FiveM;
Adds:
  • Backtick strings for hash literals (for GTA V natives)
Example:
var code = @"
-- Backtick strings compile to hash values
local hash = `VEHICLE_HASH`
";

var tree = LuaSyntaxTree.ParseText(
    code,
    new LuaParseOptions(LuaSyntaxOptions.FiveM)
);

Feature Comparison Table

Feature5.15.25.35.4JIT20JIT21GModLuauFiveM
Goto
Empty statements
Hex escapes
Hex floats
Bitwise operators
Unicode escapes
Floor division
Variable attributes
Binary literals
C comments
C boolean ops
Continue
Type annotations
Compound assignment
If expressions
Interpolated strings
Hash strings

Customizing Syntax Options

You can customize any preset using the .With() method:
// Start with Lua 5.1 but add binary numbers
var customOptions = LuaSyntaxOptions.Lua51.With(
    acceptBinaryNumbers: true
);

var tree = LuaSyntaxTree.ParseText(
    "local x = 0b1010",
    new LuaParseOptions(customOptions)
);

Common Customizations

Enable Specific Features

// Lua 5.1 with goto and hex floats
var options = LuaSyntaxOptions.Lua51.With(
    acceptGoto: true,
    acceptHexFloatLiterals: true
);

Accept Multiple Dialects

// Accept both C comments and Lua comments
var options = LuaSyntaxOptions.Lua54.With(
    acceptCCommentSyntax: true
);

Create Strict Subset

// Lua 5.4 but without goto
var options = LuaSyntaxOptions.Lua54.With(
    acceptGoto: false
);

Integer Format Options

Some Lua versions parse integer literals differently:
public enum IntegerFormats
{
    NotSupported,  // Parse as double
    Int64,         // Parse as 64-bit integer
    Double         // Parse as double (Luau behavior)
}
Example:
// Lua 5.3 parses integers as Int64
var lua53 = LuaSyntaxOptions.Lua53;
Console.WriteLine(lua53.HexIntegerFormat);  // Int64

// Luau parses integers as Double
var luau = LuaSyntaxOptions.Luau;
Console.WriteLine(luau.HexIntegerFormat);  // Double

When to Use Which Preset

Use Lua51 when:

  • Targeting standard Lua 5.1
  • Maximum compatibility with old scripts
  • Not using any modern features

Use Lua52/53/54 when:

  • Targeting specific official Lua versions
  • Need standard Lua features from those versions
  • Writing portable Lua code

Use LuaJIT20/21 when:

  • Using LuaJIT runtime
  • Need LuaJIT-specific features
  • Performance is critical

Use GMod when:

  • Developing Garry’s Mod addons
  • Need C-style comments and operators
  • Using continue statements

Use Luau when:

  • Developing for Roblox
  • Using type annotations
  • Need Luau-specific features like if expressions

Use FiveM when:

  • Developing FiveM resources
  • Using GTA V native hashes

Use All/AllWithIntegers when:

  • Building a general-purpose Lua tool
  • Need to parse any Lua dialect
  • Not concerned about strict version compliance

Complete Example

using Loretta.CodeAnalysis.Lua;

class LuaVersionExample
{
    static void TestVersion(string code, LuaSyntaxOptions options, string versionName)
    {
        Console.WriteLine($"\nTesting with {versionName}:");
        
        var tree = LuaSyntaxTree.ParseText(
            code,
            new LuaParseOptions(options)
        );
        
        var diagnostics = tree.GetDiagnostics()
            .Where(d => d.Severity == DiagnosticSeverity.Error)
            .ToList();
        
        if (diagnostics.Any())
        {
            Console.WriteLine($"  ❌ {diagnostics.Count} error(s)");
            foreach (var diagnostic in diagnostics.Take(3))
            {
                Console.WriteLine($"     {diagnostic.GetMessage()}");
            }
        }
        else
        {
            Console.WriteLine("  ✅ Parsed successfully");
        }
    }
    
    static void Main()
    {
        // Test different syntax features
        var codes = new[]
        {
            ("Goto", "goto label\n::label::"),
            ("Hex float", "local x = 0x1.8p3"),
            ("Bitwise", "local x = 5 & 3"),
            ("Attributes", "local x <const> = 10"),
            ("Type annotation", "local x: number = 10"),
            ("If expression", "local x = if true then 1 else 2"),
            ("C comment", "// comment\nlocal x = 10")
        };
        
        var versions = new[]
        {
            ("Lua 5.1", LuaSyntaxOptions.Lua51),
            ("Lua 5.2", LuaSyntaxOptions.Lua52),
            ("Lua 5.3", LuaSyntaxOptions.Lua53),
            ("Lua 5.4", LuaSyntaxOptions.Lua54),
            ("LuaJIT 2.1", LuaSyntaxOptions.LuaJIT21),
            ("GMod", LuaSyntaxOptions.GMod),
            ("Luau", LuaSyntaxOptions.Luau)
        };
        
        foreach (var (featureName, code) in codes)
        {
            Console.WriteLine($"\n{'=', 50}");
            Console.WriteLine($"Feature: {featureName}");
            Console.WriteLine($"Code: {code}");
            Console.WriteLine($"{'=', 50}");
            
            foreach (var (versionName, options) in versions)
            {
                TestVersion(code, options, versionName);
            }
        }
    }
}

See Also

Build docs developers (and LLMs) love