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:
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);
}
Configure parse options
// Choose the appropriate preset for your target environment
var parseOptions = new LuaParseOptions(LuaSyntaxOptions.Lua51);
Parse the code
var syntaxTree = LuaSyntaxTree.ParseText(
sourceText,
parseOptions,
path: filePath
);
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;
}
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: