uCalc API Version: 2.1.3-preview.2 Released: 6/16/2026
Warning
uCalc API Preview Release Notice:The documentation describes the intended behavior of the API. The current preview build contains incomplete features, unoptimized performance, and is subject to breaking changes.
Project: Implementing a Strict Configuration File Validator
Product:
Class:
A step-by-step project to build a static analysis tool (validator) for a custom INI-style configuration file using the uCalc Transformer.
Remarks
🛡️ Project: Implementing a Strict Configuration File Validator
This project will guide you through building a simple but powerful linter/validator for a custom INI-style configuration file. It's a perfect real-world example of how the declarative power of the uCalc.Transformer can solve complex validation problems more safely and readably than manual string parsing or traditional Regular Expressions.
The Goal
We'll create a validator that scans a configuration file and reports whether it adheres to a strict set of structural rules.
The Configuration File Format
Our linter will analyze a simple INI-style format with sections (e.g., [Server]), key-value pairs (Host = db1), and comments (lines starting with ;).
Example config.ini:
; Main server settings[Server]Host = main_serverPort = 8080The Validation Rules
Our validator must enforce the following rules:
- The file must contain exactly one
[Server]section. - The
[Server]section must contain exactly oneHostkey. - The
[Server]section must contain at least onePortkey. - Lines starting with
;are comments and should be ignored.
The Strategy: Declarative Validation
Instead of writing complex, imperative code to loop through lines and maintain state, we will use the Transformer to define our rules declaratively.
- Hierarchical Parsing: We'll use a parent Rule to find the
[Server]section and a LocalTransformer to validate the keys only within that section. - Occurrence Constraints: We'll use the Minimum and Maximum properties on our rules to enforce the "exactly one" and "at least one" constraints.
- Ignoring Comments: A simple SkipOver rule will make comments invisible to our validation logic.
Step-by-Step Implementation
Step 1: Configure the Transformer
First, we create a Transformer instance. Since INI files are multi-line, we must disable StatementSensitive to allow patterns to match across newlines. We also add a rule to ignore comments.
using (var validator = new uCalc.Transformer()) { validator.DefaultRuleSet.StatementSensitive = false; validator.SkipOver(";{line}"); // ... rules go here ...}Step 2: Validate the [Server] Section
We define a Pattern to find the [Server] section. We then chain the Minimum(1) and Maximum(1) methods to enforce that it must appear exactly once.
var serverRule = validator.Pattern("'['Server']' {body}") .SetMinimum(1) .SetMaximum(1);Step 3: Validate Keys within the Section
This is where the hierarchy comes in. We get the LocalTransformer for our serverRule. Any rules defined on this local transformer will only run on the text captured by the {body} variable of the parent rule.
var local_t = serverRule.LocalTransformer;// Rule for 'Host' key (must be exactly one)var hostRule = local_t.Pattern("Host = {@Alpha}") .SetMinimum(1) .SetMaximum(1);// Rule for 'Port' key (must be at least one)var portRule = local_t.Pattern("Port = {@Number}") .SetMinimum(1);Step 4: Run the Validation
With all rules defined, we call Find() on the transformer. After it runs, we can inspect the Matches().Count() for each rule. Because of our Minimum and Maximum constraints, a rule's match count will be 0 if its validation condition was not met, making the check simple.
⚖️ Why uCalc? (Comparative Analysis)
Without uCalc, you would write imperative code:
- Read the file line by line.
- Use
string.StartsWith()to check for comments or sections. - Use
string.Split('=')to parse key-value pairs. - Maintain manual counters and boolean flags (
foundServerSection,hostCount, etc.) to track state.
This approach is brittle, hard to read, and difficult to maintain. uCalc's declarative model is superior because you simply describe the rules of a valid file, and the engine handles the complex state management and validation logic for you.
Examples
Checks for the existence of a required header in a string.
using uCalcSoftware;
var uc = new uCalc();
using (var t = new uCalc.Transformer()) {
var text_ok = "Header: OK";
var text_fail = "Header: ERROR";
// This rule only matches if the status is "OK"
t.Pattern("Header: OK");
// Find() returns the transformer, so we can chain Matches().Count()
if (t.SetText(text_ok).Find().Matches.Count() > 0) {
Console.WriteLine("text_ok is valid.");
}
if (t.SetText(text_fail).Find().Matches.Count() == 0) {
Console.WriteLine("text_fail is invalid.");
}
}
text_ok is valid.
text_fail is invalid. using uCalcSoftware; var uc = new uCalc(); using (var t = new uCalc.Transformer()) { var text_ok = "Header: OK"; var text_fail = "Header: ERROR"; // This rule only matches if the status is "OK" t.Pattern("Header: OK"); // Find() returns the transformer, so we can chain Matches().Count() if (t.SetText(text_ok).Find().Matches.Count() > 0) { Console.WriteLine("text_ok is valid."); } if (t.SetText(text_fail).Find().Matches.Count() == 0) { Console.WriteLine("text_fail is invalid."); } }
#include
#include "uCalc.h"
using namespace std;
using namespace uCalcSoftware;
int main() {
uCalc uc;
{
uCalc::Transformer t;
t.Owned(); // Causes t to be released when it goes out of scope
auto text_ok = "Header: OK";
auto text_fail = "Header: ERROR";
// This rule only matches if the status is "OK"
t.Pattern("Header: OK");
// Find() returns the transformer, so we can chain Matches().Count()
if (t.SetText(text_ok).Find().Matches().Count() > 0) {
cout << "text_ok is valid." << endl;
}
if (t.SetText(text_fail).Find().Matches().Count() == 0) {
cout << "text_fail is invalid." << endl;
}
}
}
text_ok is valid.
text_fail is invalid. #include <iostream> #include "uCalc.h" using namespace std; using namespace uCalcSoftware; int main() { uCalc uc; { uCalc::Transformer t; t.Owned(); // Causes t to be released when it goes out of scope auto text_ok = "Header: OK"; auto text_fail = "Header: ERROR"; // This rule only matches if the status is "OK" t.Pattern("Header: OK"); // Find() returns the transformer, so we can chain Matches().Count() if (t.SetText(text_ok).Find().Matches().Count() > 0) { cout << "text_ok is valid." << endl; } if (t.SetText(text_fail).Find().Matches().Count() == 0) { cout << "text_fail is invalid." << endl; } } }
Imports System
Imports uCalcSoftware
Public Module Program
Public Sub Main()
Dim uc As New uCalc()
Using t As New uCalc.Transformer()
Dim text_ok = "Header: OK"
Dim text_fail = "Header: ERROR"
'// This rule only matches if the status is "OK"
t.Pattern("Header: OK")
'// Find() returns the transformer, so we can chain Matches().Count()
If t.SetText(text_ok).Find().Matches.Count() > 0 Then
Console.WriteLine("text_ok is valid.")
End If
If t.SetText(text_fail).Find().Matches.Count() = 0 Then
Console.WriteLine("text_fail is invalid.")
End If
End Using
End Sub
End Module
text_ok is valid.
text_fail is invalid. Imports System Imports uCalcSoftware Public Module Program Public Sub Main() Dim uc As New uCalc() Using t As New uCalc.Transformer() Dim text_ok = "Header: OK" Dim text_fail = "Header: ERROR" '// This rule only matches if the status is "OK" t.Pattern("Header: OK") '// Find() returns the transformer, so we can chain Matches().Count() If t.SetText(text_ok).Find().Matches.Count() > 0 Then Console.WriteLine("text_ok is valid.") End If If t.SetText(text_fail).Find().Matches.Count() = 0 Then Console.WriteLine("text_fail is invalid.") End If End Using End Sub End Module
Validates a configuration file format by enforcing the number of times specific keys must appear.
using uCalcSoftware;
var uc = new uCalc();
using (var validator = new uCalc.Transformer()) {
// 1. Configure the transformer
validator.DefaultRuleSet.StatementSensitive = false;
validator.SkipOver(";{line}"); // Ignore comments
// 2. Define rules with validation constraints
var serverRule = validator.Pattern("'['Server']' {body}").SetMinimum(1).SetMaximum(1);
serverRule.Description = "Server Section";
var local_t = serverRule.LocalTransformer;
var hostRule = local_t.Pattern("Host = {@Alpha}").SetMinimum(1).SetMaximum(1);
hostRule.Description = "Host Key";
var portRule = local_t.Pattern("Port = {@Number}").SetMinimum(1);
portRule.Description = "Port Key";
// --- Test Data ---
var validConfig = "[Server]\nHost = db1\nPort = 1433";
var invalidConfig = "Host = web1\nPort = 80"; // Missing [Server] section
// --- Validate validConfig ---
Console.WriteLine("--- Validating valid_config.ini ---");
validator.SetText(validConfig).Find();
Console.WriteLine($" Server section check passed: {serverRule.Matches.Count() == 1}");
Console.WriteLine($" Host key check passed: {hostRule.Matches.Count() == 1}");
Console.WriteLine($" Port key check passed: {portRule.Matches.Count() >= 1}");
Console.WriteLine("");
// --- Validate invalidConfig ---
Console.WriteLine("--- Validating invalid_config.ini ---");
validator.SetText(invalidConfig).Find();
Console.WriteLine($" Server section check passed: {serverRule.Matches.Count() == 1}");
// The host and port rules will have 0 matches because their parent rule (serverRule) failed.
Console.WriteLine($" Host key check passed: {hostRule.Matches.Count() == 1}");
Console.WriteLine($" Port key check passed: {portRule.Matches.Count() >= 1}");
}
--- Validating valid_config.ini ---
Server section check passed: True
Host key check passed: True
Port key check passed: True
--- Validating invalid_config.ini ---
Server section check passed: False
Host key check passed: False
Port key check passed: False using uCalcSoftware; var uc = new uCalc(); using (var validator = new uCalc.Transformer()) { // 1. Configure the transformer validator.DefaultRuleSet.StatementSensitive = false; validator.SkipOver(";{line}"); // Ignore comments // 2. Define rules with validation constraints var serverRule = validator.Pattern("'['Server']' {body}").SetMinimum(1).SetMaximum(1); serverRule.Description = "Server Section"; var local_t = serverRule.LocalTransformer; var hostRule = local_t.Pattern("Host = {@Alpha}").SetMinimum(1).SetMaximum(1); hostRule.Description = "Host Key"; var portRule = local_t.Pattern("Port = {@Number}").SetMinimum(1); portRule.Description = "Port Key"; // --- Test Data --- var validConfig = "[Server]\nHost = db1\nPort = 1433"; var invalidConfig = "Host = web1\nPort = 80"; // Missing [Server] section // --- Validate validConfig --- Console.WriteLine("--- Validating valid_config.ini ---"); validator.SetText(validConfig).Find(); Console.WriteLine($" Server section check passed: {serverRule.Matches.Count() == 1}"); Console.WriteLine($" Host key check passed: {hostRule.Matches.Count() == 1}"); Console.WriteLine($" Port key check passed: {portRule.Matches.Count() >= 1}"); Console.WriteLine(""); // --- Validate invalidConfig --- Console.WriteLine("--- Validating invalid_config.ini ---"); validator.SetText(invalidConfig).Find(); Console.WriteLine($" Server section check passed: {serverRule.Matches.Count() == 1}"); // The host and port rules will have 0 matches because their parent rule (serverRule) failed. Console.WriteLine($" Host key check passed: {hostRule.Matches.Count() == 1}"); Console.WriteLine($" Port key check passed: {portRule.Matches.Count() >= 1}"); }
#include
#include "uCalc.h"
using namespace std;
using namespace uCalcSoftware;
#define tf(IsTrue) ((IsTrue) ? "True" : "False")
int main() {
uCalc uc;
{
uCalc::Transformer validator;
validator.Owned(); // Causes validator to be released when it goes out of scope
// 1. Configure the transformer
validator.DefaultRuleSet().StatementSensitive(false);
validator.SkipOver(";{line}"); // Ignore comments
// 2. Define rules with validation constraints
auto serverRule = validator.Pattern("'['Server']' {body}").SetMinimum(1).SetMaximum(1);
serverRule.Description("Server Section");
auto local_t = serverRule.LocalTransformer();
auto hostRule = local_t.Pattern("Host = {@Alpha}").SetMinimum(1).SetMaximum(1);
hostRule.Description("Host Key");
auto portRule = local_t.Pattern("Port = {@Number}").SetMinimum(1);
portRule.Description("Port Key");
// --- Test Data ---
auto validConfig = "[Server]\nHost = db1\nPort = 1433";
auto invalidConfig = "Host = web1\nPort = 80"; // Missing [Server] section
// --- Validate validConfig ---
cout << "--- Validating valid_config.ini ---" << endl;
validator.SetText(validConfig).Find();
cout << " Server section check passed: " << tf(serverRule.Matches().Count() == 1) << endl;
cout << " Host key check passed: " << tf(hostRule.Matches().Count() == 1) << endl;
cout << " Port key check passed: " << tf(portRule.Matches().Count() >= 1) << endl;
cout << "" << endl;
// --- Validate invalidConfig ---
cout << "--- Validating invalid_config.ini ---" << endl;
validator.SetText(invalidConfig).Find();
cout << " Server section check passed: " << tf(serverRule.Matches().Count() == 1) << endl;
// The host and port rules will have 0 matches because their parent rule (serverRule) failed.
cout << " Host key check passed: " << tf(hostRule.Matches().Count() == 1) << endl;
cout << " Port key check passed: " << tf(portRule.Matches().Count() >= 1) << endl;
}
}
--- Validating valid_config.ini ---
Server section check passed: True
Host key check passed: True
Port key check passed: True
--- Validating invalid_config.ini ---
Server section check passed: False
Host key check passed: False
Port key check passed: False #include <iostream> #include "uCalc.h" using namespace std; using namespace uCalcSoftware; #define tf(IsTrue) ((IsTrue) ? "True" : "False") int main() { uCalc uc; { uCalc::Transformer validator; validator.Owned(); // Causes validator to be released when it goes out of scope // 1. Configure the transformer validator.DefaultRuleSet().StatementSensitive(false); validator.SkipOver(";{line}"); // Ignore comments // 2. Define rules with validation constraints auto serverRule = validator.Pattern("'['Server']' {body}").SetMinimum(1).SetMaximum(1); serverRule.Description("Server Section"); auto local_t = serverRule.LocalTransformer(); auto hostRule = local_t.Pattern("Host = {@Alpha}").SetMinimum(1).SetMaximum(1); hostRule.Description("Host Key"); auto portRule = local_t.Pattern("Port = {@Number}").SetMinimum(1); portRule.Description("Port Key"); // --- Test Data --- auto validConfig = "[Server]\nHost = db1\nPort = 1433"; auto invalidConfig = "Host = web1\nPort = 80"; // Missing [Server] section // --- Validate validConfig --- cout << "--- Validating valid_config.ini ---" << endl; validator.SetText(validConfig).Find(); cout << " Server section check passed: " << tf(serverRule.Matches().Count() == 1) << endl; cout << " Host key check passed: " << tf(hostRule.Matches().Count() == 1) << endl; cout << " Port key check passed: " << tf(portRule.Matches().Count() >= 1) << endl; cout << "" << endl; // --- Validate invalidConfig --- cout << "--- Validating invalid_config.ini ---" << endl; validator.SetText(invalidConfig).Find(); cout << " Server section check passed: " << tf(serverRule.Matches().Count() == 1) << endl; // The host and port rules will have 0 matches because their parent rule (serverRule) failed. cout << " Host key check passed: " << tf(hostRule.Matches().Count() == 1) << endl; cout << " Port key check passed: " << tf(portRule.Matches().Count() >= 1) << endl; } }
Imports System
Imports uCalcSoftware
Public Module Program
Public Sub Main()
Dim uc As New uCalc()
Using validator As New uCalc.Transformer()
'// 1. Configure the transformer
validator.DefaultRuleSet.StatementSensitive = false
validator.SkipOver(";{line}") '// Ignore comments
'// 2. Define rules with validation constraints
Dim serverRule = validator.Pattern("'['Server']' {body}").SetMinimum(1).SetMaximum(1)
serverRule.Description = "Server Section"
Dim local_t = serverRule.LocalTransformer
Dim hostRule = local_t.Pattern("Host = {@Alpha}").SetMinimum(1).SetMaximum(1)
hostRule.Description = "Host Key"
Dim portRule = local_t.Pattern("Port = {@Number}").SetMinimum(1)
portRule.Description = "Port Key"
'// --- Test Data ---
Dim validConfig = "[Server]" & vbNewLine & "Host = db1" & vbNewLine & "Port = 1433"
Dim invalidConfig = "Host = web1" & vbNewLine & "Port = 80" '// Missing [Server] section
'// --- Validate validConfig ---
Console.WriteLine("--- Validating valid_config.ini ---")
validator.SetText(validConfig).Find()
Console.WriteLine($" Server section check passed: {serverRule.Matches.Count() = 1}")
Console.WriteLine($" Host key check passed: {hostRule.Matches.Count() = 1}")
Console.WriteLine($" Port key check passed: {portRule.Matches.Count() >= 1}")
Console.WriteLine("")
'// --- Validate invalidConfig ---
Console.WriteLine("--- Validating invalid_config.ini ---")
validator.SetText(invalidConfig).Find()
Console.WriteLine($" Server section check passed: {serverRule.Matches.Count() = 1}")
'// The host and port rules will have 0 matches because their parent rule (serverRule) failed.
Console.WriteLine($" Host key check passed: {hostRule.Matches.Count() = 1}")
Console.WriteLine($" Port key check passed: {portRule.Matches.Count() >= 1}")
End Using
End Sub
End Module
--- Validating valid_config.ini ---
Server section check passed: True
Host key check passed: True
Port key check passed: True
--- Validating invalid_config.ini ---
Server section check passed: False
Host key check passed: False
Port key check passed: False Imports System Imports uCalcSoftware Public Module Program Public Sub Main() Dim uc As New uCalc() Using validator As New uCalc.Transformer() '// 1. Configure the transformer validator.DefaultRuleSet.StatementSensitive = false validator.SkipOver(";{line}") '// Ignore comments '// 2. Define rules with validation constraints Dim serverRule = validator.Pattern("'['Server']' {body}").SetMinimum(1).SetMaximum(1) serverRule.Description = "Server Section" Dim local_t = serverRule.LocalTransformer Dim hostRule = local_t.Pattern("Host = {@Alpha}").SetMinimum(1).SetMaximum(1) hostRule.Description = "Host Key" Dim portRule = local_t.Pattern("Port = {@Number}").SetMinimum(1) portRule.Description = "Port Key" '// --- Test Data --- Dim validConfig = "[Server]" & vbNewLine & "Host = db1" & vbNewLine & "Port = 1433" Dim invalidConfig = "Host = web1" & vbNewLine & "Port = 80" '// Missing [Server] section '// --- Validate validConfig --- Console.WriteLine("--- Validating valid_config.ini ---") validator.SetText(validConfig).Find() Console.WriteLine($" Server section check passed: {serverRule.Matches.Count() = 1}") Console.WriteLine($" Host key check passed: {hostRule.Matches.Count() = 1}") Console.WriteLine($" Port key check passed: {portRule.Matches.Count() >= 1}") Console.WriteLine("") '// --- Validate invalidConfig --- Console.WriteLine("--- Validating invalid_config.ini ---") validator.SetText(invalidConfig).Find() Console.WriteLine($" Server section check passed: {serverRule.Matches.Count() = 1}") '// The host and port rules will have 0 matches because their parent rule (serverRule) failed. Console.WriteLine($" Host key check passed: {hostRule.Matches.Count() = 1}") Console.WriteLine($" Port key check passed: {portRule.Matches.Count() >= 1}") End Using End Sub End Module