-
Notifications
You must be signed in to change notification settings - Fork 0
FAQ
CDTk (Compiler Description Toolkit) is a .NET library for building compilers, transpilers, and code generators. It provides a unified API centered around three components:
- TokenSet (for tokens)
- RuleSet (for grammar rules)
- MapSet (for code generation)
Starting with Version 7, CDTk introduces a strongly-typed API, enhancing safety, clarity, and IDE support.
CDTk is ideal for:
- Building domain-specific languages (DSLs)
- Creating code generators and transpilers
- Prototyping new programming languages
- Teaching compiler construction principles
- Transforming data between formats (e.g., JSON to XML, SQL to NoSQL)
CDTk targets .NET 10.0 and above.
Here’s a minimal example using the fully-typed API:
// Define token types
class Number { }
class Identifier { }
// Define tokens
var tokens = new TokenSet
{
new Token<Number>(@"\d+"),
new Token<Identifier>(@"[A-Za-z_][A-Za-z0-9_]*")
};
// Define grammar rules
class AssignmentNode { }
var rules = new RuleSet
{
new Rule<AssignmentNode>("variable:@Identifier '=' value:@Number")
.Returns<AssignmentNode>("variable", "value")
};
// Define code generation
var mapping = new MapSet
{
new Map<AssignmentNode>("let {variable} = {value};")
};
// Build and compile
var compiler = new Compiler()
.WithTokens(tokens)
.WithRules(rules)
.WithTarget(mapping)
.Build();
var result = compiler.Compile("x = 42;");
Console.WriteLine(result.Output); // Output: let x = 42;For more detailed examples, see Examples.md.
See Examples.md for:
- Calculators
- Mini languages
- JSON parsers
- Multi-target transpilers
- Domain-specific languages (DSLs)
Check token order. Tokens are processed in the order they are defined. The first matching token is selected:
// Wrong: Identifier can match keywords
new Token<Identifier>(@"[A-Za-z_][A-Za-z0-9_]*"), // Too generic
new Token<Keyword>(@"if|else|while"), // Never gets matched
// Correct: Define keywords first
new Token<Keyword>(@"if|else|while"),
new Token<Identifier>(@"[A-Za-z_][A-Za-z0-9_]*")Use .Ignore() on tokens:
class Whitespace { }
new Token<Whitespace>(@"\s+").Ignore();Use RegexOptions.Singleline for patterns like /* comments */:
class BlockComment { }
new Token<BlockComment>(@"/\*.*?\*/", RegexOptions.Singleline).Ignore();class StringLiteral { }
new Token<StringLiteral>(@"""([^""\\]|\\.)*""");This supports escape sequences like \n, \t, and \".
-
Token references should use
@:// Wrong new Rule<Assignment>("Identifier '=' Number") // Correct new Rule<Assignment>("variable:@Identifier '=' value:@Number")
-
Verify literal formatting: Use
'='for exact matches. -
Simplify debugging: Start with minimal rules and incrementally add complexity.
Use separate rules for each precedence level:
class ExprNode { }
class TermNode { }
class FactorNode { }
new Rule<ExprNode>("Term (('+' | '-') Term)*")
new Rule<TermNode>("Factor (('*' | '/') Factor)*")
new Rule<FactorNode>("@Number | '(' ExprNode ')'")Left recursion causes infinite loops:
// BAD: Left recursive
new Rule<Expression>("Expression '+' Term")
// GOOD: Iterative
new Rule<Expression>("Term ('+' Term)*")Use ? for optional components:
class VarDeclNode { }
new Rule<VarDeclNode>("@Type @Identifier ('=' Expression)?")
.Returns<VarDeclNode>("type", "name", "init");Use * (zero or more) or + (one or more):
class BlockNode { }
new Rule<BlockNode>("'{' Statement* '}'");
class ArgList { }
new Rule<ArgList>("Expression (',' Expression)*");-
Check node type names: Node types in your
Mapmust match.Returns()declarations. -
Verify placeholder names: Ensure placeholders in templates (
{fieldName}) match field names defined in.Returns().
Use specific node types in your rules and mappings:
class IntNode { }
class StringNode { }
new Map<IntNode>("int32_t {variable}")
new Map<StringNode>("char* {variable}")Yes, use Validate or advanced custom rules:
class ValidatedNode { }
new Map<ValidatedNode>("Validated Output");Alternatively, map different nodes using specific cases like:
Class ComplexRule future context handling wanceled {"QUICK WALKTHRU"}