uCalc API Version: 2.1.3-preview.2 Released: 6/17/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: Building a Markdown to HTML Converter

Product: 

Class: 

A step-by-step guide to building a simple Markdown to HTML converter using the declarative rules of the uCalc Transformer.

Remarks

📝 Project: Building a Markdown to HTML Converter

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.

The Goal

We'll create a transformer that can convert basic Markdown syntax into the corresponding HTML tags:

  • Headers: # A Header<h1>A Header</h1>
  • List Items: * An item<li>An item</li>
  • Bold Text: **bold**<b>bold</b>
  • Italic Text: *italic*<i>italic</i>

🤔 Why uCalc Instead of Regex?

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.


Step 1: Setting Up the Transformer

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.

using (var t = new uCalc.Transformer()) {    t.DefaultRuleSet.RewindOnChange = true;    // ... rules go here ...}

Step 2: Defining the Rules

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.

// Headers: Match a '#' at the start of a line, capture the rest.// The implicit newline at the end of the line will stop the {line} capture.t.FromTo("#{@Whitespace}{line}", "<h1>{line}</h1>");// List Items: Match a '*' at the start of a line.t.FromTo("*{@Whitespace}{line}", "<li>{line}</li>");

Now for the inline elements. This is where rule order becomes critical.

// Rule for Italic textt.FromTo("*{text}*", "<i>{text}</i>");// Rule for Bold textt.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 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 Together

The 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> Tags

You'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 system.

Examples

A complete, single-pass transformer that converts headers, list items, bold, and italic Markdown syntax to HTML.
				
					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));
}
				
			
<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;
   }
}
				
			
<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
				
			
<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>.