SyntaxFactory provides factory methods to programmatically construct Lua syntax trees. This is essential for code generation, refactoring, and transformation scenarios.
Overview
TheSyntaxFactory 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; }
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; }
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);
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
- LuaSyntaxTree - Parse text into syntax trees
- Syntax Nodes Overview - Understanding the syntax tree structure
- Code Generation Guide - Generating Lua code programmatically