Advanced

Async Operations

Tokenizer supports async compilation and tokenization from streams, useful for processing files or network data without loading everything into memory:

C#
// Compile from a file
using var reader = new StreamReader("template.txt");
var compiled = await tokenizer.CompileAsync(reader);

// Tokenize from a stream
using var inputReader = new StreamReader("input.txt");
var result = await tokenizer.TokenizeAsync<MyClass>(
    compiled.Template, inputReader, cancellationToken);

All async methods accept a CancellationToken for cooperative cancellation:

C#
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));

try
{
    var result = await tokenizer.TokenizeAsync(template, reader, cts.Token);
}
catch (OperationCanceledException)
{
    Console.WriteLine("Tokenization timed out");
}

Safety Limits

Tokenizer v3.0 includes built-in safety limits to prevent resource exhaustion from malformed or adversarial input:

LimitDefaultDescription
MaxInputLength1,048,576 (1 MB)Maximum input text length in characters
MaxTemplateLength65,536 (64 KB)Maximum template pattern length
MaxTokenCount500Maximum number of tokens per template
MaxIterationsAuto (2× input length)Maximum engine iterations before aborting

Configure limits via options:

C#
var tokenizer = new Tokenizer(new TokenizerOptions
{
    MaxInputLength = 5_000_000,  // 5 MB
    MaxTokenCount = 100
});

Real-World Examples

WHOIS data parsing
C#
var template = @"---
TerminateOnNewLine: true
---
   Domain Name: {DomainName}
   Registrar: {Registrar}
   Updated Date: {UpdatedDate:ToDateTime('yyyy-MM-ddTHH:mm:ssZ')}
   Creation Date: {CreationDate:ToDateTime('yyyy-MM-ddTHH:mm:ssZ')}
   Registry Expiry Date: {ExpiryDate:ToDateTime('yyyy-MM-ddTHH:mm:ssZ')}
   Name Server: {NameServers*}";
Log file extraction
C#
var template = "[{Timestamp$}] [{Level$}] {Message}";
var input = "[2025-01-15 14:30:22] [ERROR] Connection refused to database server";

var result = tokenizer.Tokenize(compiled.Template, input);
// Timestamp = "2025-01-15 14:30:22", Level = "ERROR", Message = "Connection refused..."
Structured email parsing
C#
var template = @"---
TerminateOnNewLine: true
---
From: {From}
To: {To}
Subject: {Subject}
Date: {Date:ToDateTime('ddd, dd MMM yyyy HH:mm:ss')}";

Performance

Templates are compiled once and can be reused across many tokenization operations. The compilation step (parsing, AST generation, decorator resolution) is the expensive part — tokenization itself is a single-pass operation.

C#
// Compile once
var compiled = tokenizer.Compile(pattern);

// Reuse many times
foreach (var line in inputLines)
{
    var result = tokenizer.Tokenize(compiled.Template, line);
    // ...
}

For TokenMatcher, templates are compiled on registration and cached internally. The matcher itself is thread-safe for concurrent reads.

Error Handling

Tokenizer uses a specific exception hierarchy:

ExceptionWhen
LexerExceptionTemplate pattern has invalid syntax (includes line/column)
ParsingExceptionTemplate AST cannot be constructed (includes line/column)
TokenAssignmentExceptionExtracted value cannot be assigned to target property
TypeConversionExceptionTransformer cannot convert the value (includes target type)
ValidationExceptionValidator setup error (not validation failure — those are handled gracefully)
TokenMatcherExceptionError during multi-template matching

All inherit from TokenizerException. Normal validation failures (e.g., IsEmail() rejecting an invalid address) do not throw — they cause the engine to skip and continue searching.

C#
try
{
    var compiled = tokenizer.Compile(pattern);
    var result = tokenizer.Tokenize(compiled.Template, input);
}
catch (Tokens.Exceptions.LexerException ex)
{
    Console.WriteLine($"Template syntax error at line {ex.Line}, column {ex.Column}: {ex.Message}");
}
catch (Tokens.Exceptions.TokenizerException ex)
{
    Console.WriteLine($"Tokenizer error: {ex.Message}");
}