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: Transpiling Legacy Code to Modern

Product: 

Class: 

A step-by-step project to build a simple transpiler that converts a legacy scripting syntax to a modern equivalent using the uCalc Transformer.

Remarks

🚀 Project: Transpiling Legacy Code to Modern

A transpiler is a tool that reads source code written in one language and transforms it into equivalent code in another language. This project will guide you through building a simple transpiler that converts a legacy, BASIC-like syntax into a more modern, JavaScript-like format.

This is a perfect real-world demonstration of the uCalc Transformer's power. Because it is token-aware, it can safely restructure code without the risks associated with character-based tools like regular expressions.

The Goal: From Legacy BASIC to Modern Script

We'll create a transpiler that can process a script like this:

Legacy Input:

REM This is a commentLET X = 10LET Y = X * 2IF Y > 15 THEN PRINT "Value is large"

And convert it into this modern equivalent:

Modern Output:

// This is a commentvar x = 10;var y = x * 2;if (y > 15) {  console.log("Value is large");}

💡 Why uCalc Instead of Regex?

Attempting this with a series of Regex.Replace calls would be extremely fragile. A regex for PRINT "..." could accidentally match text inside another string or comment. A regex for LET X = ... would struggle with variable spacing and different expression types.

The uCalc Transformer avoids these pitfalls because it understands the code's structure. It knows the difference between the keyword PRINT and the word "PRINT" inside a string literal.


Step 1: Setting Up the Transformer

First, we need a Transformer instance. We'll also configure its tokenizer to recognize our legacy comment style (REM ...).

var t = new uCalc.Transformer();// Treat newlines as significant separatorst.DefaultRuleSet.StatementSensitive = true;// Define 'REM' comments as whitespace so they are ignored by other rules,// but we'll also have a rule to convert them.t.Tokens.Add("REM.*", TokenType.Whitespace);

Step 2: Defining the Transpilation Rules

We'll define a FromTo rule for each language construct we want to convert. The order is important: more specific rules should be defined last to give them higher precedence.

// Rule for commentst.FromTo("REM {comment}", "// {comment}");// Rule for variable assignmentt.FromTo("LET {@Alpha:var} = {val}", "var {var} = {val};");// Rule for PRINT statementst.FromTo("PRINT {output}", "console.log({output});");// Rule for IF...THEN statementst.FromTo("IF {condition} THEN {action}", "if ({condition}) {\n  {action}\n}");

Notice how we use token category matchers like {@Alpha} and variables like {condition} and {action} to capture the dynamic parts of the code.

Step 3: Handling Nested Logic with RewindOnChange

Our IF...THEN rule has a problem: if the {action} is a PRINT statement, it won't be transformed! The IF rule runs, captures the literal text PRINT "...", and finishes.

To solve this, we need to tell the transformer to re-scan the text after a replacement. This is done with the RewindOnChange property.

var ifRule = t.FromTo("IF {condition} THEN {action}", "if ({condition}) {\n  {action}\n}");ifRule.RewindOnChange = true;

Now, after the IF statement is transformed, the engine will rewind and re-scan its body, allowing the PRINT rule to match and transform the inner action.


Step 4: Putting It All Together

The complete example below combines these rules to process a sample legacy script, correctly handling comments, variable assignments, and nested statements.

This project demonstrates how a few declarative rules can build a powerful and safe code transformation pipeline, a task that would be orders of magnitude more complex and error-prone with traditional tools.

Examples

A succinct example converting a single line of legacy variable declaration syntax to a modern equivalent.
				
					using uCalcSoftware;

var uc = new uCalc();
using (var t = new uCalc.Transformer()) {
   // Rule to convert legacy variable declaration
   t.FromTo("LET {@Alpha:var} = {val}", "var {var} = {val};");
   Console.WriteLine(t.Transform("LET X = 100"));
}
				
			
var X = 100;
				
					#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
      // Rule to convert legacy variable declaration
      t.FromTo("LET {@Alpha:var} = {val}", "var {var} = {val};");
      cout << t.Transform("LET X = 100") << endl;
   }
}
				
			
var X = 100;
				
					Imports System
Imports uCalcSoftware
Public Module Program
   Public Sub Main()
      Dim uc As New uCalc()
      Using t As New uCalc.Transformer()
         '// Rule to convert legacy variable declaration
         t.FromTo("LET {@Alpha:var} = {val}", "var {var} = {val};")
         Console.WriteLine(t.Transform("LET X = 100"))
      End Using
   End Sub
End Module
				
			
var X = 100;
A practical example that transpiles a multi-line legacy script, including comments, variables, and a conditional statement with a nested action.
				
					using uCalcSoftware;

var uc = new uCalc();
using (var t = new uCalc.Transformer()) {
   // Rules must be defined in an order that allows for proper transformation.

   // 1. Comment rule
   t.FromTo("REM {comment}", "// {comment}");

   // 2. Variable assignment rule
   t.FromTo("LET {@Alpha:var} = {val}", "var {var} = {val};");

   // 3. Print statement rule
   t.FromTo("PRINT {output}", "console.log({output});");

   // 4. IF...THEN rule with RewindOnChange to handle nested statements
   var ifRule = t.FromTo("IF {condition} THEN {action}", """
if ({condition}) {
  {action}
}
""");
   ifRule.RewindOnChange = true;

   // The legacy script to transpile
   var legacyScript = """

REM Initialize variables
LET X = 10
LET Y = X * 2
IF Y > 15 THEN PRINT "Value is large"

""";

   // Run the transformation
   Console.WriteLine(t.Transform(legacyScript));
}
				
			
// Initialize variables
var X = 10;
var Y = X * 2;
if (Y > 15) {
  console.log("Value is large");
}
				
					#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
      // Rules must be defined in an order that allows for proper transformation.

      // 1. Comment rule
      t.FromTo("REM {comment}", "// {comment}");

      // 2. Variable assignment rule
      t.FromTo("LET {@Alpha:var} = {val}", "var {var} = {val};");

      // 3. Print statement rule
      t.FromTo("PRINT {output}", "console.log({output});");

      // 4. IF...THEN rule with RewindOnChange to handle nested statements
      auto ifRule = t.FromTo("IF {condition} THEN {action}", R"(if ({condition}) {
  {action}
})");
      ifRule.RewindOnChange(true);

      // The legacy script to transpile
      auto legacyScript = R"(
REM Initialize variables
LET X = 10
LET Y = X * 2
IF Y > 15 THEN PRINT "Value is large"
)";

      // Run the transformation
      cout << t.Transform(legacyScript) << endl;
   }
}
				
			
// Initialize variables
var X = 10;
var Y = X * 2;
if (Y > 15) {
  console.log("Value is large");
}
				
					Imports System
Imports uCalcSoftware
Public Module Program
   Public Sub Main()
      Dim uc As New uCalc()
      Using t As New uCalc.Transformer()
         '// Rules must be defined in an order that allows for proper transformation.
         
         '// 1. Comment rule
         t.FromTo("REM {comment}", "// {comment}")
         
         '// 2. Variable assignment rule
         t.FromTo("LET {@Alpha:var} = {val}", "var {var} = {val};")
         
         '// 3. Print statement rule
         t.FromTo("PRINT {output}", "console.log({output});")
         
         '// 4. IF...THEN rule with RewindOnChange to handle nested statements
         Dim ifRule = t.FromTo("IF {condition} THEN {action}", "if ({condition}) {
  {action}
}")
         ifRule.RewindOnChange = true
         
         '// The legacy script to transpile
         Dim legacyScript = "
REM Initialize variables
LET X = 10
LET Y = X * 2
IF Y > 15 THEN PRINT ""Value is large""
"
         
         '// Run the transformation
         Console.WriteLine(t.Transform(legacyScript))
      End Using
   End Sub
End Module
				
			
// Initialize variables
var X = 10;
var Y = X * 2;
if (Y > 15) {
  console.log("Value is large");
}