Compile-Time Evaluation

XXML supports compile-time evaluation, allowing expressions to be computed during compilation rather than at runtime. This enables constant folding, type-safe compile-time constants, and optimization opportunities.

Basic Syntax

Use the Compiletime keyword after Instantiate to declare a compile-time constant:

xxml
Instantiate Compiletime Integer^ As <x> = Integer::Constructor(10);
Instantiate Compiletime Bool^ As <flag> = Bool::Constructor(true);
Instantiate Compiletime String^ As <name> = String::Constructor("Hello");

Supported Types

The following built-in types are declared as Compiletime classes and support compile-time evaluation:

TypeDescription
Integer64-bit signed integers
Float32-bit floating point
Double64-bit floating point
BoolBoolean values
StringString values

Compile-Time Operations

Integer Methods

xxml
Instantiate Compiletime Integer^ As <x> = Integer::Constructor(10);
Instantiate Compiletime Integer^ As <y> = Integer::Constructor(5);

// Arithmetic
Instantiate Compiletime Integer^ As <sum> = x.add(y);        // 15
Instantiate Compiletime Integer^ As <diff> = x.subtract(y);  // 5
Instantiate Compiletime Integer^ As <prod> = x.multiply(y);  // 50
Instantiate Compiletime Integer^ As <quot> = x.divide(y);    // 2
Instantiate Compiletime Integer^ As <rem> = x.modulo(y);     // 0

// Comparisons (return Bool)
Instantiate Compiletime Bool^ As <eq> = x.equals(y);         // false
Instantiate Compiletime Bool^ As <lt> = x.lessThan(y);       // false
Instantiate Compiletime Bool^ As <gt> = x.greaterThan(y);    // true

// Properties
Instantiate Compiletime Bool^ As <isZ> = x.isZero();         // false
Instantiate Compiletime Bool^ As <isP> = x.isPositive();     // true
Instantiate Compiletime Bool^ As <isE> = x.isEven();         // true

// Conversions
Instantiate Compiletime String^ As <str> = x.toString();     // "10"

Bool Methods

xxml
Instantiate Compiletime Bool^ As <a> = Bool::Constructor(true);
Instantiate Compiletime Bool^ As <b> = Bool::Constructor(false);

// Logical operations
Instantiate Compiletime Bool^ As <notA> = a.not();           // false
Instantiate Compiletime Bool^ As <andR> = a.and(b);          // false
Instantiate Compiletime Bool^ As <orR> = a.or(b);            // true
Instantiate Compiletime Bool^ As <xorR> = a.xor(b);          // true

// Conversions
Instantiate Compiletime String^ As <str> = a.toString();     // "true"
Instantiate Compiletime Integer^ As <int> = a.toInteger();   // 1

String Methods

xxml
Instantiate Compiletime String^ As <s> = String::Constructor("Hello World");

// Properties
Instantiate Compiletime Integer^ As <len> = s.length();      // 11
Instantiate Compiletime Bool^ As <empty> = s.isEmpty();      // false

// Transformations
Instantiate Compiletime String^ As <up> = s.toUpperCase();   // "HELLO WORLD"
Instantiate Compiletime String^ As <lo> = s.toLowerCase();   // "hello world"
Instantiate Compiletime String^ As <rv> = s.reverse();       // "dlroW olleH"

// String operations
Instantiate Compiletime String^ As <cat> = s.append(String::Constructor("!"));
Instantiate Compiletime Bool^ As <has> = s.contains(String::Constructor("World"));
Instantiate Compiletime Integer^ As <idx> = s.indexOf(String::Constructor("o"));

Chained Operations

Compile-time operations can be chained:

xxml
Instantiate Compiletime Integer^ As <a> = Integer::Constructor(2);
Instantiate Compiletime Integer^ As <b> = Integer::Constructor(3);
Instantiate Compiletime Integer^ As <c> = Integer::Constructor(4);

// (a + b) * c = (2 + 3) * 4 = 20
Instantiate Compiletime Integer^ As <result> = a.add(b).multiply(c);

Note

The entire chain is evaluated at compile-time, resulting in a single constant value in the generated code.

Mixed Compile-Time and Runtime

Compile-time values can be used with runtime values:

xxml
Instantiate Compiletime Integer^ As <factor> = Integer::Constructor(10);
Instantiate Integer^ As <runtime> = Integer::Constructor(5);

// Compile-time value used in runtime expression
Instantiate Integer^ As <result> = runtime.multiply(factor);  // 50

User-Defined Compile-Time Classes

Classes can be marked as Compiletime to enable full compile-time constructor and method evaluation:

point.xxml
[ Class <Point> Compiletime Final Extends None
    [ Private <>
        Property <x> Types Integer^;
        Property <y> Types Integer^;
    ]

    [ Public <>
        Constructor = default;

        Constructor Parameters (
            Parameter <px> Types Integer^,
            Parameter <py> Types Integer^
        ) -> {
            Set x = px;
            Set y = py;
        }

        Method <getX> Returns Integer^ Parameters () -> {
            Return x;
        }

        Method <getY> Returns Integer^ Parameters () -> {
            Return y;
        }
    ]
]

[ Entrypoint
    {
        // Create Point at compile-time
        Instantiate Compiletime Point^ As <p> = Point::Constructor(
            Integer::Constructor(10),
            Integer::Constructor(20)
        );

        // These method calls are evaluated at compile-time!
        Run Console::printLine(String::Constructor("x = ").append(p.getX().toString()));
        Run Console::printLine(String::Constructor("y = ").append(p.getY().toString()));

        Exit(0);
    }
]

Generated LLVM IR

The above code generates optimized IR where method calls are completely eliminated:

llvm
; String constants directly embedded - no runtime method calls!
@.str.1 = private constant [5 x i8] c"x = \00"
@.str.2 = private constant [3 x i8] c"10\00"   ; p.getX().toString() folded to "10"
@.str.3 = private constant [5 x i8] c"y = \00"
@.str.4 = private constant [3 x i8] c"20\00"   ; p.getY().toString() folded to "20"

define i32 @main() {
  ; No Point_getX, Point_getY, or Integer_toString calls!
  %ct.str = call ptr @String_Constructor(ptr @.str.2)
  ; ...
}

Compile-Time Methods

Individual methods can be marked as compile-time:

xxml
[ Class <Math> Final Extends None
    [ Public <>
        Method <factorial> Compiletime Returns Integer^ Parameters (
            Parameter <n> Types Integer^
        ) Do {
            If (n.lessOrEqual(Integer::Constructor(1))) -> {
                Return Integer::Constructor(1);
            }
            Return n.multiply(factorial(n.subtract(Integer::Constructor(1))));
        }
    ]
]

Benefits

  • Performance: Expressions computed at compile-time have zero runtime overhead
  • Type Safety: Compile-time evaluation catches errors during compilation
  • Optimization: The compiler can optimize based on known constant values
  • Code Clarity: Clearly marks values that are constant throughout execution

Tip

Use compile-time evaluation for configuration constants, lookup tables, and any values that can be determined at build time.

Supported Statements

The following statement types are supported in compile-time method/constructor bodies:

StatementDescription
Set x = exprProperty or variable assignment
Instantiate Type As <var> = exprLocal variable declaration
Return exprReturn statement
Run exprExpression statement

Limitations

  • Compile-time values must be initialized with compile-time evaluable expressions
  • I/O operations cannot be performed at compile-time
  • Compile-time class methods must have deterministic behavior
  • Recursive compile-time methods have depth limits to prevent infinite loops
  • External/native functions cannot be called at compile-time
  • Control flow statements (If, While, For) in compile-time methods are not yet supported

Next Steps

Learn about Types for more information about XXML's type system, or explore Advanced Topics for more compiler features.