uCalc API Version: 5.7.0-preview.1 Released: 7/30/2026
Warning
uCalc API Preview Release Notice:This preview documentation describes the intended behavior of the API. It is not fully accurate or complete.The current preview build contains incomplete features, unoptimized performance, and is subject to breaking changes.Use of the preview version in your production code is not recommended.
A step-by-step guide to building a simple Markdown to HTML converter using the declarative rules of the uCalc Transformer.
This project will guide you through building a simple, functional Markdown to HTML converter. It's a perfect real-world example of how the declarative power of the uCalc.Transformer can solve complex text-structuring problems more safely and readably than traditional tools like Regular Expressions.
We'll create a transformer that can convert basic Markdown syntax into the corresponding HTML tags:
# A Header → <h1>A Header</h1>* An item → <li>An item</li>**bold** → <b>bold</b>*italic* → <i>italic</i>While you could attempt this with a series of Regex.Replace calls, you would quickly run into problems, especially with inline styles. A regex for italic text (\*(.*?)\*) would incorrectly match bold text (**text**), leading to broken tags. Handling this requires complex negative lookarounds that are difficult to write and maintain.
The uCalc Transformer avoids this by being token-aware and using a clear rule precedence system, making the solution both more robust and easier to understand.
First, we need a Transformer instance. We will also enable RewindOnChange for all rules. This is crucial because it tells the transformer to re-scan the text after a replacement. Without it, after converting a list item line like * text with **bold** into <li>text with **bold**</li>, the engine would not re-scan the new text to find and convert the **bold** part.
csharp
using (var t = new uCalc.Transformer()) {
t.DefaultRuleSet.RewindOnChange = true;
// ... rules go here ...
}
Next, we'll define our conversion logic using FromTo() rules. We'll start with block-level elements (headers and lists), which operate on whole lines.
Now for the inline elements. This is where rule order becomes critical.```csharp
// Rule for Italic text
t.FromTo("*{text}*", "<i>{text}</i>");
// Rule for Bold text
t.FromTo("**{text}**", "<b>{text}</b>");
```## Step 3: Rule Order is Crucial! (LIFO Precedence)In the code above, why did we define the rule for italics *before* the rule for bold? Because the [Transformer](/Reference/uCalcBase/Transformer/Constructor) checks rules in a **LIFO (Last-In, First-Out)** order. The **last rule defined is checked first**.* The pattern `**{text}**` is more specific than `*{text}*`.* If the italic rule were defined last, it would match `**bold**` as `*` + `*bold` + `*`, resulting in `<i>*bold</i>*`—incorrect HTML!* By defining the bold rule last, we give it higher precedence. The transformer will check for `**...**` first. If it matches, great. If not, it will then check for `*...*`.This is a fundamental concept for writing correct transformation logic.## Step 4: Putting It All TogetherThe full example below combines these rules to process a sample Markdown document. The transformer will correctly handle both block-level tags and nested inline tags in a single pass.## Next Steps: Handling `<ul>` TagsYou'll notice this simple converter creates `<li>` tags but doesn't wrap them in the required `<ul>...</ul>` container. This is a classic multi-pass transformation problem. To solve it, you could:1. Run the first pass (as we did here) to generate the `<li>` tags.2. Run a second pass with a new rule that finds consecutive blocks of `<li>` tags and wraps them in `<ul>...</ul>`.This demonstrates how you can build sophisticated pipelines by chaining transformers or using the [Pass](/Reference/uCalcBase/Transformer/Pass) system.ID: 1404
using uCalcSoftware;
var uc = new uCalc();
using (var t = new uCalc.Transformer()) {
t.DefaultRuleSet.RewindOnChange = true;
// 2. Define Rules (General rules first, specific rules last for LIFO precedence)
// -- Inline rules --
// Italic is defined before Bold, giving Bold higher precedence.
t.FromTo("*{text}*", "{text}");
t.FromTo("**{text}**", "{text}");
// -- Block-level rules --
t.FromTo("#{@Whitespace}{line}", "{line}
");
t.FromTo("*{@Whitespace}{line}", " {line} ");
t.FromTo("{@nl}{@nl}", "{@nl}{@nl}"); // {@nl} = NewLine
t.FromTo("{@nl}{@nl}*{@Whitespace}", "{@nl}{@nl}* ");
// 3. Define the input Markdown text
var markdown = """
# Main Header
* First list item
* Second list item with **bold** text.
* Third list item with *italic* text.
Another paragraph with **bold** and *italic*.
""";
// 4. Run the transformation and print the result
Console.WriteLine(t.Transform(markdown));
}
<h1>Main Header</h1>
<ul>
<li>First list item</li>
<li>Second list item with <b>bold</b> text.</li>
<li>Third list item with <i>italic</i> text.</li>
</ul>
Another paragraph with <b>bold</b> and <i>italic</i>. using uCalcSoftware; var uc = new uCalc(); using (var t = new uCalc.Transformer()) { t.DefaultRuleSet.RewindOnChange = true; // 2. Define Rules (General rules first, specific rules last for LIFO precedence) // -- Inline rules -- // Italic is defined before Bold, giving Bold higher precedence. t.FromTo("*{text}*", "<i>{text}</i>"); t.FromTo("**{text}**", "<b>{text}</b>"); // -- Block-level rules -- t.FromTo("#{@Whitespace}{line}", "<h1>{line}</h1>"); t.FromTo("*{@Whitespace}{line}", "<li>{line}</li>"); t.FromTo("</li>{@nl}{@nl}", "</li>{@nl}</ul>{@nl}"); // {@nl} = NewLine t.FromTo("{@nl}{@nl}*{@Whitespace}", "{@nl}<ul>{@nl}* "); // 3. Define the input Markdown text var markdown = """ # Main Header * First list item * Second list item with **bold** text. * Third list item with *italic* text. Another paragraph with **bold** and *italic*. """; // 4. Run the transformation and print the result Console.WriteLine(t.Transform(markdown)); }
#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
t.DefaultRuleSet().RewindOnChange(true);
// 2. Define Rules (General rules first, specific rules last for LIFO precedence)
// -- Inline rules --
// Italic is defined before Bold, giving Bold higher precedence.
t.FromTo("*{text}*", "{text}");
t.FromTo("**{text}**", "{text}");
// -- Block-level rules --
t.FromTo("#{@Whitespace}{line}", "{line}
");
t.FromTo("*{@Whitespace}{line}", "{line} ");
t.FromTo("{@nl}{@nl}", "{@nl}{@nl}"); // {@nl} = NewLine
t.FromTo("{@nl}{@nl}*{@Whitespace}", "{@nl}{@nl}* ");
// 3. Define the input Markdown text
auto markdown = R"(
# Main Header
* First list item
* Second list item with **bold** text.
* Third list item with *italic* text.
Another paragraph with **bold** and *italic*.
)";
// 4. Run the transformation and print the result
cout << t.Transform(markdown) << endl;
}
}
<h1>Main Header</h1>
<ul>
<li>First list item</li>
<li>Second list item with <b>bold</b> text.</li>
<li>Third list item with <i>italic</i> text.</li>
</ul>
Another paragraph with <b>bold</b> and <i>italic</i>. #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 t.DefaultRuleSet().RewindOnChange(true); // 2. Define Rules (General rules first, specific rules last for LIFO precedence) // -- Inline rules -- // Italic is defined before Bold, giving Bold higher precedence. t.FromTo("*{text}*", "<i>{text}</i>"); t.FromTo("**{text}**", "<b>{text}</b>"); // -- Block-level rules -- t.FromTo("#{@Whitespace}{line}", "<h1>{line}</h1>"); t.FromTo("*{@Whitespace}{line}", "<li>{line}</li>"); t.FromTo("</li>{@nl}{@nl}", "</li>{@nl}</ul>{@nl}"); // {@nl} = NewLine t.FromTo("{@nl}{@nl}*{@Whitespace}", "{@nl}<ul>{@nl}* "); // 3. Define the input Markdown text auto markdown = R"( # Main Header * First list item * Second list item with **bold** text. * Third list item with *italic* text. Another paragraph with **bold** and *italic*. )"; // 4. Run the transformation and print the result cout << t.Transform(markdown) << endl; } }
Imports System
Imports uCalcSoftware
Public Module Program
Public Sub Main()
Dim uc As New uCalc()
Using t As New uCalc.Transformer()
t.DefaultRuleSet.RewindOnChange = true
'// 2. Define Rules (General rules first, specific rules last for LIFO precedence)
'// -- Inline rules --
'// Italic is defined before Bold, giving Bold higher precedence.
t.FromTo("*{text}*", "{text}")
t.FromTo("**{text}**", "{text}")
'// -- Block-level rules --
t.FromTo("#{@Whitespace}{line}", "{line}
")
t.FromTo("*{@Whitespace}{line}", " {line} ")
t.FromTo("{@nl}{@nl}", "{@nl}{@nl}") '// {@nl} = NewLine
t.FromTo("{@nl}{@nl}*{@Whitespace}", "{@nl}{@nl}* ")
'// 3. Define the input Markdown text
Dim markdown = "
# Main Header
* First list item
* Second list item with **bold** text.
* Third list item with *italic* text.
Another paragraph with **bold** and *italic*.
"
'// 4. Run the transformation and print the result
Console.WriteLine(t.Transform(markdown))
End Using
End Sub
End Module
<h1>Main Header</h1>
<ul>
<li>First list item</li>
<li>Second list item with <b>bold</b> text.</li>
<li>Third list item with <i>italic</i> text.</li>
</ul>
Another paragraph with <b>bold</b> and <i>italic</i>. Imports System Imports uCalcSoftware Public Module Program Public Sub Main() Dim uc As New uCalc() Using t As New uCalc.Transformer() t.DefaultRuleSet.RewindOnChange = true '// 2. Define Rules (General rules first, specific rules last for LIFO precedence) '// -- Inline rules -- '// Italic is defined before Bold, giving Bold higher precedence. t.FromTo("*{text}*", "<i>{text}</i>") t.FromTo("**{text}**", "<b>{text}</b>") '// -- Block-level rules -- t.FromTo("#{@Whitespace}{line}", "<h1>{line}</h1>") t.FromTo("*{@Whitespace}{line}", "<li>{line}</li>") t.FromTo("</li>{@nl}{@nl}", "</li>{@nl}</ul>{@nl}") '// {@nl} = NewLine t.FromTo("{@nl}{@nl}*{@Whitespace}", "{@nl}<ul>{@nl}* ") '// 3. Define the input Markdown text Dim markdown = " # Main Header * First list item * Second list item with **bold** text. * Third list item with *italic* text. Another paragraph with **bold** and *italic*. " '// 4. Run the transformation and print the result Console.WriteLine(t.Transform(markdown)) End Using End Sub End Module