-
Notifications
You must be signed in to change notification settings - Fork 0
Mapping
CDTk provides a powerful yet intuitive system for transforming Abstract Syntax Trees (ASTs) into target code. This process is the cornerstone of compilers, transpilers, and other language tools. Here, you’ll find everything you need to understand and implement mapping effectively in your projects.
- What Is Mapping?
- Key Features
- Creating and Using a MapSet
- Template-Based Code Generation
- Advanced Mapping Techniques
- Error Handling and Diagnostics
- Integrating Mapping with Compilers
- End-to-End Mapping Example
- Best Practices
- Next Steps
Mapping is the process of converting an AST into a structured output, such as source code or data, using predefined patterns.
-
Map: Defines how a specific AST node type is rendered. -
MapSet: Groups multiple map rules into one cohesive framework.
var maps = new MapSet
{
new Map("AssignmentNode", "let {variable} = {value};")
};For this AST:
{
"type": "AssignmentNode",
"fields": {
"variable": "x",
"value": 42
}
}The resulting output will be:
let x = 42;-
Template-Driven Mapping: Use simple
{fieldName}placeholders for field substitution. - Fallback Safety: Create default rules to prevent errors during unmapped cases.
- Dynamic Mapping with Context: Process runtime-specific details flexibly.
- Multi-Target Support: Generate output for multiple languages or formats simultaneously.
A MapSet holds all mappings for your project and ensures a cohesive code-generation strategy.
Define a MapSet and populate it with Map rules for specific node types:
var mapping = new MapSet
{
new Map("AssignmentNode", "let {variable} = {value};"),
new Map("BinaryOpNode", "({left} {operator} {right})"),
new Map("IfNode", "if ({condition}) {body}")
};Placeholders ({fieldName}) correspond to fields in the AST:
new Map("BinaryOpNode", "({left} {operator} {right})");Given this AST:
{
"type": "BinaryOpNode",
"fields": {
"left": "x",
"operator": "+",
"right": "y"
}
}The resulting output is:
(x + y)Templates automatically resolve nested fields in the AST:
new Map("FunctionCallNode", "{functionName}({arguments})");For AST:
{
"type": "FunctionCallNode",
"fields": {
"functionName": "print",
"arguments": "42"
}
}The output is:
print(42)Guarantee all nodes are handled by defining a fallback rule:
new Map("UnknownNode", "// TODO: Handle {type}");This prevents runtime errors and flags missing mappings.
Use separate maps for different node types:
new Map("UnaryOpNode", "{operator}{operand}");
new Map("BinaryOpNode", "({left} {operator} {right})");Introduce context-specific behaviors into templates dynamically:
new Map("VariableDeclaration", "let {name}: {type} = {value};")CDTk’s built-in diagnostics track mapping errors and provide detailed warnings.
Log errors with helpful feedback:
diagnostics.Add(
Stage.Mapping,
DiagnosticLevel.Error,
"Mapping failed at node {type}",
SourceSpan.Unknown
);Enable deduplication to reduce diagnostic clutter:
diagnostics.Deduplicate = true;Mappings plug seamlessly into the compilation pipeline:
- Single Target Compilation:
var compiler = new Compiler()
.WithTokens(tokens)
.WithRules(rules)
.WithTarget(mapSet)
.Build();
var result = compiler.Compile(inputSource);
Console.WriteLine(result.Output);- Multi-Target Compilation:
var compiler = new Compiler()
.WithTokens(tokens)
.WithRules(rules)
.WithTargets(
("LanguageA", mapA),
("LanguageB", mapB)
)
.Build();
var result = compiler.Compile(inputSource);
Console.WriteLine(result.Outputs["LanguageA"]);
Console.WriteLine(result.Outputs["LanguageB"]);Here’s a complete example of mapping a small language.
var mapSet = new MapSet
{
new Map("ProgramNode", "{statements}\n"),
new Map("AssignmentNode", "let {variable} = {value};"),
new Map("BinaryOpNode", "({left} {operator} {right})"),
new Map("IfNode", "if ({condition}) {block}"),
new Map("UnknownNode", "// Unhandled node: {type}")
};{
"type": "ProgramNode",
"fields": {
"statements": [
{
"type": "AssignmentNode",
"fields": {
"variable": "x",
"value": 10
}
},
{
"type": "IfNode",
"fields": {
"condition": "x > 0",
"block": "return x;"
}
}
]
}
}let x = 10;
if (x > 0) return x;Define a fallback to identify unmapped nodes:
new Map("UnknownNode", "// TODO: Handle {type}");Avoid overly complex templates. Break larger mappings into smaller, reusable pieces.
Write maps iteratively and test outputs with diverse inputs.
Make placeholders intuitive for better maintainability:
new Map("AssignmentNode", "let {variable} = {value};");Now that you understand mapping: