Skip to main content
SyntaxFactory provides factory methods to programmatically construct Lua syntax trees. This is essential for code generation, refactoring, and transformation scenarios.

Overview

The SyntaxFactory class contains static methods to create:
  • Trivia: Comments, whitespace, end-of-line markers
  • Tokens: Keywords, identifiers, literals, operators
  • Syntax Nodes: Statements, expressions, and other structural elements

Common Trivia

Predefined Trivia

public static SyntaxTrivia Space { get; }
public static SyntaxTrivia Tab { get; }
public static SyntaxTrivia LineFeed { get; }
public static SyntaxTrivia CarriageReturn { get; }
public static SyntaxTrivia CarriageReturnLineFeed { get; }
Commonly used trivia instances:
var token = SyntaxFactory.Token(
    leading: SyntaxFactory.TriviaList(SyntaxFactory.Space, SyntaxFactory.Space),
    kind: SyntaxKind.LocalKeyword,
    trailing: SyntaxFactory.TriviaList(SyntaxFactory.Space)
);

Elastic Trivia

public static SyntaxTrivia ElasticSpace { get; }
public static SyntaxTrivia ElasticLineFeed { get; }
public static SyntaxTrivia ElasticMarker { get; }
Elastic trivia are placeholders that formatters can replace with appropriate whitespace.

Custom Trivia

var comment = SyntaxFactory.Comment("-- This is a comment");
var whitespace = SyntaxFactory.Whitespace("    "); // 4 spaces
var eol = SyntaxFactory.EndOfLine("\n");

Tokens

Keywords and Operators

var localKeyword = SyntaxFactory.Token(SyntaxKind.LocalKeyword);
var functionKeyword = SyntaxFactory.Token(SyntaxKind.FunctionKeyword);
var equals = SyntaxFactory.Token(SyntaxKind.EqualsToken);
var plus = SyntaxFactory.Token(SyntaxKind.PlusToken);
With trivia:
var localKeyword = SyntaxFactory.Token(
    leading: SyntaxFactory.TriviaList(SyntaxFactory.Comment("-- variable")),
    kind: SyntaxKind.LocalKeyword,
    trailing: SyntaxFactory.TriviaList(SyntaxFactory.Space)
);

Identifiers

var identifier = SyntaxFactory.Identifier("myVariable");

// With trivia
var identifier = SyntaxFactory.Identifier(
    leading: SyntaxFactory.TriviaList(),
    text: "myVariable",
    trailing: SyntaxFactory.TriviaList(SyntaxFactory.Space)
);

Literals

Numeric Literals

// Integer
var num1 = SyntaxFactory.Literal(42);
var num2 = SyntaxFactory.Literal("0xFF", 255L);

// Floating point
var num3 = SyntaxFactory.Literal(3.14);
var num4 = SyntaxFactory.Literal("3.14e-10", 3.14e-10);

// Complex (imaginary)
var imag = SyntaxFactory.Literal(Complex.ImaginaryOne * 5);

String Literals

var str1 = SyntaxFactory.Literal("hello"); // Auto-escapes
var str2 = SyntaxFactory.Literal("\"hello\"", "hello"); // Custom raw text

Hash Literals (FiveM)

var hash1 = SyntaxFactory.HashLiteral("WEAPON_PISTOL"); // Calculates hash
var hash2 = SyntaxFactory.HashLiteral("`WEAPON_PISTOL`", 0x1B06D571);

Expressions

Literals

var nilLiteral = SyntaxFactory.NilLiteralExpression();
var trueLiteral = SyntaxFactory.TrueLiteralExpression();
var falseLiteral = SyntaxFactory.FalseLiteralExpression();
var number = SyntaxFactory.NumericalLiteralExpression(SyntaxFactory.Literal(42));
var string = SyntaxFactory.StringLiteralExpression(SyntaxFactory.Literal("hello"));

Identifiers and Variables

var name = SyntaxFactory.IdentifierName("x");
var vararg = SyntaxFactory.VarArgExpression();

Binary Expressions

var left = SyntaxFactory.IdentifierName("a");
var right = SyntaxFactory.IdentifierName("b");

var add = SyntaxFactory.BinaryExpression(
    SyntaxKind.AddExpression,
    left,
    SyntaxFactory.Token(SyntaxKind.PlusToken),
    right
);

var equals = SyntaxFactory.BinaryExpression(
    SyntaxKind.EqualsExpression,
    SyntaxFactory.IdentifierName("x"),
    SyntaxFactory.Token(SyntaxKind.EqualsEqualsToken),
    SyntaxFactory.NumericalLiteralExpression(SyntaxFactory.Literal(1))
);

Unary Expressions

var not = SyntaxFactory.UnaryExpression(
    SyntaxKind.LogicalNotExpression,
    SyntaxFactory.Token(SyntaxKind.NotKeyword),
    SyntaxFactory.IdentifierName("flag")
);

var negate = SyntaxFactory.UnaryExpression(
    SyntaxKind.UnaryMinusExpression,
    SyntaxFactory.Token(SyntaxKind.MinusToken),
    SyntaxFactory.IdentifierName("x")
);

Function Calls

var call = SyntaxFactory.FunctionCallExpression(
    SyntaxFactory.IdentifierName("print"),
    SyntaxFactory.ExpressionListFunctionArgument(
        SyntaxFactory.Token(SyntaxKind.OpenParenthesisToken),
        SyntaxFactory.SeparatedList<ExpressionSyntax>(new[]
        {
            SyntaxFactory.StringLiteralExpression(SyntaxFactory.Literal("Hello, World!"))
        }),
        SyntaxFactory.Token(SyntaxKind.CloseParenthesisToken)
    )
);

Table Constructors

var table = SyntaxFactory.TableConstructorExpression(
    SyntaxFactory.Token(SyntaxKind.OpenBraceToken),
    SyntaxFactory.SeparatedList<TableFieldSyntax>(new TableFieldSyntax[]
    {
        SyntaxFactory.UnkeyedTableField(
            SyntaxFactory.NumericalLiteralExpression(SyntaxFactory.Literal(1))
        ),
        SyntaxFactory.IdentifierKeyedTableField(
            SyntaxFactory.Identifier("x"),
            SyntaxFactory.Token(SyntaxKind.EqualsToken),
            SyntaxFactory.NumericalLiteralExpression(SyntaxFactory.Literal(10))
        )
    }),
    SyntaxFactory.Token(SyntaxKind.CloseBraceToken)
);
// Result: { 1, x = 10 }

Statements

Local Variable Declaration

var localDecl = SyntaxFactory.LocalVariableDeclarationStatement(
    SyntaxFactory.Token(SyntaxKind.LocalKeyword),
    SyntaxFactory.SeparatedList<LocalDeclarationNameSyntax>(new[]
    {
        SyntaxFactory.LocalDeclarationName(SyntaxFactory.Identifier("x"))
    }),
    SyntaxFactory.EqualsValuesClause(
        SyntaxFactory.Token(SyntaxKind.EqualsToken),
        SyntaxFactory.SeparatedList<ExpressionSyntax>(new[]
        {
            SyntaxFactory.NumericalLiteralExpression(SyntaxFactory.Literal(1))
        })
    ),
    SyntaxFactory.Token(SyntaxKind.None) // Optional semicolon
);
// Result: local x = 1

Assignment

var assignment = SyntaxFactory.AssignmentStatement(
    SyntaxFactory.SeparatedList<PrefixExpressionSyntax>(new[]
    {
        SyntaxFactory.IdentifierName("x")
    }),
    SyntaxFactory.EqualsValuesClause(
        SyntaxFactory.Token(SyntaxKind.EqualsToken),
        SyntaxFactory.SeparatedList<ExpressionSyntax>(new[]
        {
            SyntaxFactory.NumericalLiteralExpression(SyntaxFactory.Literal(2))
        })
    ),
    SyntaxFactory.Token(SyntaxKind.None)
);
// Result: x = 2

If Statement

var ifStmt = SyntaxFactory.IfStatement(
    SyntaxFactory.Token(SyntaxKind.IfKeyword),
    SyntaxFactory.IdentifierName("condition"),
    SyntaxFactory.Token(SyntaxKind.ThenKeyword),
    SyntaxFactory.StatementList(
        SyntaxFactory.ExpressionStatement(
            SyntaxFactory.FunctionCallExpression(
                SyntaxFactory.IdentifierName("print"),
                SyntaxFactory.ExpressionListFunctionArgument(
                    SyntaxFactory.Token(SyntaxKind.OpenParenthesisToken),
                    SyntaxFactory.SeparatedList<ExpressionSyntax>(new[]
                    {
                        SyntaxFactory.StringLiteralExpression(SyntaxFactory.Literal("true"))
                    }),
                    SyntaxFactory.Token(SyntaxKind.CloseParenthesisToken)
                )
            ),
            SyntaxFactory.Token(SyntaxKind.None)
        )
    ),
    SyntaxFactory.List<ElseIfClauseSyntax>(),
    null, // No else clause
    SyntaxFactory.Token(SyntaxKind.EndKeyword),
    SyntaxFactory.Token(SyntaxKind.None)
);

While Loop

var whileStmt = SyntaxFactory.WhileStatement(
    SyntaxFactory.Token(SyntaxKind.WhileKeyword),
    SyntaxFactory.TrueLiteralExpression(),
    SyntaxFactory.Token(SyntaxKind.DoKeyword),
    SyntaxFactory.StatementList(
        SyntaxFactory.BreakStatement(
            SyntaxFactory.Token(SyntaxKind.BreakKeyword),
            SyntaxFactory.Token(SyntaxKind.None)
        )
    ),
    SyntaxFactory.Token(SyntaxKind.EndKeyword),
    SyntaxFactory.Token(SyntaxKind.None)
);
// Result: while true do break end

Function Declaration

var funcDecl = SyntaxFactory.FunctionDeclarationStatement(
    SyntaxFactory.Token(SyntaxKind.FunctionKeyword),
    SyntaxFactory.SimpleFunctionName(SyntaxFactory.Identifier("greet")),
    null, // No type parameters
    SyntaxFactory.ParameterList(
        SyntaxFactory.Token(SyntaxKind.OpenParenthesisToken),
        SyntaxFactory.SeparatedList<ParameterSyntax>(new ParameterSyntax[]
        {
            SyntaxFactory.NamedParameter(SyntaxFactory.Identifier("name"), null)
        }),
        SyntaxFactory.Token(SyntaxKind.CloseParenthesisToken)
    ),
    null, // No return type
    SyntaxFactory.StatementList(
        SyntaxFactory.ExpressionStatement(
            SyntaxFactory.FunctionCallExpression(
                SyntaxFactory.IdentifierName("print"),
                SyntaxFactory.ExpressionListFunctionArgument(
                    SyntaxFactory.Token(SyntaxKind.OpenParenthesisToken),
                    SyntaxFactory.SeparatedList<ExpressionSyntax>(new[]
                    {
                        SyntaxFactory.IdentifierName("name")
                    }),
                    SyntaxFactory.Token(SyntaxKind.CloseParenthesisToken)
                )
            ),
            SyntaxFactory.Token(SyntaxKind.None)
        )
    ),
    SyntaxFactory.Token(SyntaxKind.EndKeyword),
    SyntaxFactory.Token(SyntaxKind.None)
);
// Result: function greet(name) print(name) end

Lists and Collections

SyntaxList

// Empty list
var empty = SyntaxFactory.List<StatementSyntax>();

// Single item
var single = SyntaxFactory.SingletonList<StatementSyntax>(statement);

// Multiple items
var list = SyntaxFactory.List<StatementSyntax>(new[]
{
    statement1,
    statement2,
    statement3
});

SeparatedSyntaxList

For comma-separated lists:
// Empty
var empty = SyntaxFactory.SeparatedList<ExpressionSyntax>();

// Single item (no separator)
var single = SyntaxFactory.SingletonSeparatedList(expression);

// Multiple items (auto-adds commas)
var list = SyntaxFactory.SeparatedList<ExpressionSyntax>(new[]
{
    expr1,
    expr2,
    expr3
});

// Custom separators
var customList = SyntaxFactory.SeparatedList<ExpressionSyntax>(
    nodes: new[] { expr1, expr2 },
    separators: new[] { SyntaxFactory.Token(SyntaxKind.CommaToken) }
);

Parsing Helpers

Parse Expression

var expr = SyntaxFactory.ParseExpression("x + y * 2");

Parse Statement

var stmt = SyntaxFactory.ParseStatement("local x = 1");

Parse Compilation Unit

var tree = SyntaxFactory.ParseCompilationUnit(@"
local function test()
    print('hello')
end
");

Complete Example

Generating a complete Lua script:
using Loretta.CodeAnalysis.Lua;
using Loretta.CodeAnalysis.Lua.Syntax;

// Create: local x = 10
var localDecl = SyntaxFactory.LocalVariableDeclarationStatement(
    SyntaxFactory.Token(SyntaxKind.LocalKeyword),
    SyntaxFactory.SeparatedList<LocalDeclarationNameSyntax>(new[]
    {
        SyntaxFactory.LocalDeclarationName(SyntaxFactory.Identifier("x"))
    }),
    SyntaxFactory.EqualsValuesClause(
        SyntaxFactory.Token(SyntaxKind.EqualsToken),
        SyntaxFactory.SeparatedList<ExpressionSyntax>(new[]
        {
            SyntaxFactory.NumericalLiteralExpression(SyntaxFactory.Literal(10))
        })
    ),
    SyntaxFactory.Token(SyntaxKind.None)
);

// Create: print(x)
var printCall = SyntaxFactory.ExpressionStatement(
    SyntaxFactory.FunctionCallExpression(
        SyntaxFactory.IdentifierName("print"),
        SyntaxFactory.ExpressionListFunctionArgument(
            SyntaxFactory.Token(SyntaxKind.OpenParenthesisToken),
            SyntaxFactory.SeparatedList<ExpressionSyntax>(new[]
            {
                SyntaxFactory.IdentifierName("x")
            }),
            SyntaxFactory.Token(SyntaxKind.CloseParenthesisToken)
        )
    ),
    SyntaxFactory.Token(SyntaxKind.None)
);

// Create compilation unit
var compilationUnit = SyntaxFactory.CompilationUnit(
    SyntaxFactory.StatementList(localDecl, printCall),
    SyntaxFactory.Token(SyntaxKind.EndOfFileToken)
);

// Convert to string
var code = compilationUnit.ToFullString();
Console.WriteLine(code);
// Output:
// local x = 10
// print(x)

Best Practices

Use Trivia Wisely: Add appropriate whitespace and newlines for readable generated code:
var localKeyword = SyntaxFactory.Token(
    SyntaxFactory.TriviaList(),
    SyntaxKind.LocalKeyword,
    SyntaxFactory.TriviaList(SyntaxFactory.Space)
);
Reuse Tokens: Cache commonly used tokens:
var space = SyntaxFactory.Space;
var comma = SyntaxFactory.Token(SyntaxKind.CommaToken);
Token Text Matters: When creating tokens with custom text, ensure the text matches the token kind:
// Correct
SyntaxFactory.Token(SyntaxKind.LocalKeyword) // Text is "local"

// Wrong - don't do this
SyntaxFactory.Token(
    SyntaxFactory.TriviaList(),
    SyntaxKind.LocalKeyword,
    "LOCAL", // Wrong casing!
    "local",
    SyntaxFactory.TriviaList()
)

See Also

Build docs developers (and LLMs) love