Skip to main content
This guide shows you how to parse Lua code into syntax trees that you can analyze and manipulate.

Creating SourceText

Before parsing, you need to load your code into a SourceText object. SourceText provides an efficient representation of source code that avoids the Large Object Heap (LOH).

From a String

The simplest way to create SourceText:
using Loretta.CodeAnalysis.Text;

var code = "local x = 10";
var sourceText = SourceText.From(code);

From a Stream

For files, use a stream to avoid loading everything into memory at once:
using Loretta.CodeAnalysis.Text;

SourceText sourceText;
using (var stream = File.OpenRead("script.lua"))
{
    sourceText = SourceText.From(stream);
}
The stream is read and can be closed immediately - SourceText stores the content internally.

With Encoding

Specify encoding for proper text handling:
using System.Text;
using Loretta.CodeAnalysis.Text;

var sourceText = SourceText.From(
    "local x = 10",
    Encoding.UTF8
);
Specifying an encoding is important for debuggability. Without it, the SourceText cannot be properly debugged.

Choosing LuaSyntaxOptions

Loretta supports multiple Lua versions and dialects through LuaSyntaxOptions presets. Choose the preset that matches your target Lua environment:

Available Presets

  • LuaSyntaxOptions.Lua51 - Lua 5.1
  • LuaSyntaxOptions.Lua52 - Lua 5.2 (adds goto, hex escapes, hex floats)
  • LuaSyntaxOptions.Lua53 - Lua 5.3 (adds bitwise operators, integers)
  • LuaSyntaxOptions.Lua54 - Lua 5.4 (adds local variable attributes)
  • LuaSyntaxOptions.LuaJIT20 - LuaJIT 2.0
  • LuaSyntaxOptions.LuaJIT21 - LuaJIT 2.1 (adds binary numbers, Unicode escapes)
  • LuaSyntaxOptions.GMod - Garry’s Mod (LuaJIT + C comments, C boolean operators)
  • LuaSyntaxOptions.Luau - Luau/Roblox (adds type annotations, compound assignment)
  • LuaSyntaxOptions.FiveM - FiveM (Lua 5.3 + backtick strings)

Special Presets

  • LuaSyntaxOptions.All - Accepts most syntax without integers
  • LuaSyntaxOptions.AllWithIntegers - Accepts most syntax with integers (excludes C comments)
The All and AllWithIntegers presets are meant for cases when the Lua version is unknown. They are not recommended for general usage as they may accept invalid syntax for your target environment.

Parsing Code

Once you have SourceText and chosen your options, parse the code into a syntax tree:
using Loretta.CodeAnalysis.Lua;
using Loretta.CodeAnalysis.Text;

var code = "local x = 10";
var sourceText = SourceText.From(code);

var parseOptions = new LuaParseOptions(LuaSyntaxOptions.Lua51);
var syntaxTree = LuaSyntaxTree.ParseText(
    sourceText,
    parseOptions,
    path: "script.lua"
);

Parsing with Error Recovery

Loretta’s parser includes error recovery - it continues parsing even when it encounters errors:
var code = "local x = " // Missing value
var syntaxTree = LuaSyntaxTree.ParseText(code, parseOptions);

// The tree is still valid and can be analyzed
var root = syntaxTree.GetRoot();
// Check for errors later
var diagnostics = syntaxTree.GetDiagnostics();

Getting the Root Node

After parsing, retrieve the root node to traverse the syntax tree:
var root = syntaxTree.GetRoot();

// For a CompilationUnit specifically:
var compilationUnit = syntaxTree.GetCompilationUnitRoot();

Complete Parsing Workflow

Here’s a complete example showing the full workflow:
1

Load the source file

using Loretta.CodeAnalysis;
using Loretta.CodeAnalysis.Lua;
using Loretta.CodeAnalysis.Text;
using System.Text;

if (!File.Exists(filePath))
{
    Console.WriteLine("File not found!");
    return;
}

SourceText sourceText;
using (var stream = File.OpenRead(filePath))
{
    sourceText = SourceText.From(stream, Encoding.UTF8);
}
2

Configure parse options

// Choose the appropriate preset for your target environment
var parseOptions = new LuaParseOptions(LuaSyntaxOptions.Lua51);
3

Parse the code

var syntaxTree = LuaSyntaxTree.ParseText(
    sourceText,
    parseOptions,
    path: filePath
);
4

Check for errors

var diagnostics = syntaxTree.GetDiagnostics();
var hasErrors = false;

foreach (var diagnostic in diagnostics)
{
    Console.WriteLine(diagnostic.ToString());
    if (diagnostic.Severity == DiagnosticSeverity.Error)
    {
        hasErrors = true;
    }
}

if (hasErrors)
{
    Console.WriteLine("Parse errors detected!");
    return;
}
5

Get the root and process

var root = syntaxTree.GetRoot();

// Now you can traverse or analyze the syntax tree
foreach (var statement in root.DescendantNodes())
{
    // Process nodes...
}

What SourceText Provides

SourceText is more than just a string wrapper:
  • Efficient memory usage - Avoids the Large Object Heap
  • Character access - Get individual characters or substrings
  • Line mapping - Convert between positions and line/column numbers
  • Change tracking - Calculate differences between versions
  • Checksums - Get file checksums for debugging
// Get a character at a position
char ch = sourceText[10];

// Get lines
var lines = sourceText.Lines;
var firstLine = lines[0];

// Map position to line/column
var linePosition = sourceText.Lines.GetLinePosition(100);
Console.WriteLine($"Line {linePosition.Line}, Column {linePosition.Character}");

Next Steps

Now that you can parse code, learn how to:

Build docs developers (and LLMs) love