RUNTIME VALUE CONVERSIONS

Runtime conversion and C use

Runtime value conversion generates C11 routines for Python values that are created, shared, and changed while the generated program runs.

Select runtime conversion

Open all required source documents, select Transpile → Runtime, then click the Transpile button or press Ctrl+Return. The checked selection is remembered; selecting it alone does not run conversion. Both modes produce normal .c files saved with Save Generated C. The terminal equivalent is:

pycforge convert input.py --runtime-values --output generated.c

For the Python API, use the complete runtime request options shown in Runtime value requests. Runtime conversion uses its own C value representation. It provides validated ForgeLens emission mappings and independent output/provenance checks; the Typed converter's C-IR semantic proof pipeline does not apply.

Organize the source

Place functions, classes, immutable constants, and explicit imports at module level. Include at least one top-level function in the bundle; constants-only and empty companions are accepted. See Runtime modules for imports and initialization order. Functions support nested definitions and closures and decorators. Nested classes, class decorators, general module execution, and writable globals are unsupported.

def greeting(name: str) -> str:
    return "Hello, " + name + "!"

def main() -> int:
    print(greeting("café"))
    return 0

This program prints Hello, café! when compiled and run externally.

Values and built-in calls

FormSupported use
None, bool, int, float, strScalar values, compatible arithmetic/comparisons, and truth tests.
len(value)Strings, lists, tuples, ranges, dictionaries, sets, frozen sets, and dictionary views.
bool(value), bool()Truth conversion for admitted runtime values; the empty call returns False.
int(value, base), float(value)Numeric and string conversions. The optional int base is 0 or 2–36 and also accepts a keyword. Empty int/float calls return zero.
str(value), str()Strings, integers, floats (including inf, -inf and nan), Booleans, and None. The empty call returns an empty string. Nested containers and dictionary views also render as Python-style text. Object/function/class/module/range/iterator/file-handle representations are outside this profile.
list(value), tuple(value)Convert a supported string, list, tuple, range, dictionary, set, frozen set, dictionary view, admitted iterator, or readable file handle. An omitted argument creates an empty container, like [] or ().
dict(source, name=value)Create an ordered mutable dictionary from another dictionary or key/value pairs, plus optional explicit keywords. Source is optional. See Dictionary operations.
set(iterable), frozenset(iterable)Create a mutable or immutable set of hashable values. Omit the iterable for an empty set. See Set operations.
range(stop), range(start, stop, step)Integer ranges, including a calculated nonzero step. Values are generated as needed during iteration.
isinstance, issubclass, super, objectClass relationships, cooperative method lookup, and the root class. See Class calls and inheritance for supported forms.
print(value, ...)Strings, integers, floats (including inf, -inf and nan), Booleans, and None. Optional sep and end keywords accept strings or None. Nested containers and dictionary views print with Python-style quoting and cycle markers. Object/function/class/module/range/iterator/file-handle printing and file/flush options are unsupported.

Everyday builtins

FormBehavior
sum(iterable, start=0)Accumulate admitted values. Strings are rejected; numeric sums use checked integers and Python 3.12-style compensated floating addition.
min, maxOne iterable or multiple positional values; optional key function. An empty iterable may use default=. Equal keys keep the first value.
abs(value), round(number, ndigits=None)Numeric absolute value and ties-to-even rounding. Integer overflow is reported; decimal rounding retains the actual binary64 input.
any(iterable), all(iterable)Short-circuit truth tests; empty input returns False for any and True for all.
sorted(iterable, key=None, reverse=False)New list in stable order. Keys are evaluated once per element. Numeric, string, and compatible list/tuple keys are supported.
enumerate(iterable, start=0), zip(*iterables, strict=False)Lazy, single-use iterators. Zip accepts separate iterable arguments or an expanded collection of iterables. Strict zip reports unequal lengths during consumption.
iter(iterable), next(iterator, default)Share and consume iterator state. The next default is optional; exhaustion without it reports StopIteration through the C error channel.
reversed(value)A single-use reverse iterator over a list, tuple, string, range, dictionary, or dictionary view. Dictionary entries follow reverse insertion order.
repr(value), ascii(value), format(value, spec)Representation and bounded formatting; see formatting details.
def main() -> int:
    values = [-3, 2, -1, 2]
    print(sum(values), min(values), max(values), round(2.675, 2))
    print(sorted(values, key=abs))
    for position, pair in enumerate(zip("ab", [10, 20]), start=1):
        print(position, pair)
    return 0

Iterator inputs remain live and iterators retain their consumption position. They have no length or indexing. General generators and custom iterator protocols are not supported. An enumerate index outside int64 reports overflow. Builtins are function values, so key=abs and key=len work.

Exhaustion belongs to each input iterator. A list, tuple, range, string, dictionary, or set iterator stays exhausted once it has reported the end, even if its container later grows. A readable file can resume after a successful rewind, including through enumerate or zip. Catching an iteration error does not turn it into an end-of-input result.

zip reads its inputs from left to right on every attempt. An incomplete row can consume earlier inputs. With strict=True, retrying after a length mismatch can consume another unmatched value and raise again:

def main() -> int:
    pairs = zip([1], [10, 20, 30], strict=True)
    print(next(pairs))
    for attempt in range(3):
        try:
            print(next(pairs, "finished"))
        except ValueError:
            print("length mismatch")
    return 0

This prints (1, 10), two length mismatch lines, and finished. A next default applies only to exhaustion; it does not suppress other errors. Usually handle a strict length mismatch as the end of that operation and create fresh iterators when retrying the complete workflow.

Numeric operations

Integers and their results must fit signed 64-bit values. Floating operations use finite binary64 values. Overflow and division by zero report errors through the C context.

FormBehavior
+, -, *, /Checked arithmetic; true division returns a float.
//, %, divmod(a, b)Integer or floating floor quotient and remainder, including Python's sign and signed-zero rules. Divmod returns both results.
**, pow(base, exp, mod=None)Nonnegative integer powers remain integers; negative exponents or floating operands produce floats. A nonzero integer modulus enables modular power, including negative exponents when the base is invertible. Pow accepts its named keywords.
&, |, ^, ~, <<, >>Integer bit operations with Python's negative-value semantics. Negative shift counts are errors; left shifts check overflow. Binary Boolean bit operations return Booleans.

Augmented assignments use the same numeric rules. Complex results and arbitrary-size integers are outside the numeric domain. Floating power uses the target's math library; its final rounding may vary across platforms.

Convert numeric text

def main() -> int:
    print(int("1_250"), int("ff", base=16), int("0b101", 0))
    print(float("  -1.25e2  "), float("١.٥"))
    print(str(1.5), str(-0.0), str(1e20))
    return 0

Expected output:

1250 255 5
-125.0 1.5
1.5 -0.0 1e+20

Integer parsing accepts an optional sign, surrounding numeric whitespace, Unicode decimal digits, valid underscore separators, and prefixes consistent with the selected base. Base 0 detects 0b, 0o, or 0x; a nonzero decimal value with a leading zero is invalid in that form. An explicit base requires a string. Values must fit signed 64-bit integers.

Float parsing accepts finite decimal text, optional exponent/sign, surrounding numeric whitespace, Unicode decimal digits, and underscores between digits. Invalid text and embedded NULs fail. NaN, infinity, hexadecimal float text, and non-finite results are outside the numeric domain. Underflow may produce a subnormal or signed zero.

Float-to-string conversion uses a shortest round-trip representation with Python's fixed/scientific notation rules, including -0.0. Integer conversion truncates a finite float toward zero and checks the int64 range. The generated C caller must keep a numeric locale whose decimal point is .; decimal parsing reports an error if the host selects a different separator.

Branches and loops

Use if/elif/else, while, and for over strings, lists, tuples, ranges, dictionaries, sets, frozen sets, dictionary views, admitted iterators, or readable files. break, continue, and loop else follow the usual Python rules: else runs after normal exhaustion, including zero iterations, and is skipped by break.

Conditional expressions, chained comparisons, and short-circuit and/or preserve evaluation order and return the selected operand. Assign locals before reading them; a loop may leave its target unassigned if it never runs. Comprehensions have their own scope, described in List, dictionary, and set comprehensions.

Use file-processing workflows to connect UTF-8 reads and writes with these operations. File contexts can nest inside loops and call imported processing helpers.

Use try/except/else/finally to recover from selected runtime errors and run cleanup. Literal raises and nested handlers are supported; resource-policy failures remain terminal.

Call generated functions from C

Generated values use the PV type. A zero-initialized PC context owns all allocations. Obtain a function value with pv_top_function using its generated symbol from the conversion summary. This initializes module definitions, defaults, and decorators once in the context and returns the final decorated value. Call it through pv_call with a positional argument count and PV array; optional parameters may be omitted. For keywords, pv_call_kw additionally takes a keyword count, name array, and value array. Use pv_i for integer arguments and pv_string for UTF-8 bytes with an explicit byte length.

Use the same context for connected calls, shared lists, dictionaries, objects, and closures. Check context.error after each call; context.error_source and context.error_line identify its logical source file and Python statement. On failure, discard the result. Always finish with pv_destroy(&context), including after an error.

Returned values and captured variables remain valid until their context is destroyed. Do not free individual values, use them afterward, move them to another context, or share one context concurrently between threads. Intermediate allocations remain in the context until destruction.

Complete runtime C caller

Save the greeting/main Python example above as runtime-example.py and convert it in Runtime mode to runtime-example.c. In this exact example greeting has generated symbol pv_fn1; confirm the symbol in your own output because changing definitions can change it. Save this caller as runtime-caller.c beside the generated C.

#include "runtime-example.c"

int main(void)
{
    PC context = {0};
    int status = 1;
    PV function = pv_top_function(&context, pv_fn1);
    if (context.error) goto cleanup;
    PV arguments[] = {
        pv_string(&context, (const unsigned char *)"Ada", 3)
    };
    if (context.error) goto cleanup;
    PV result = pv_call(&context, function, 1, arguments);
    if (context.error) goto cleanup;
    pv_print(&context, 1, &result);
    if (!context.error) status = 0;

cleanup:
    if (context.error) {
        fprintf(stderr, "Python line %d: %s\n",
                context.error_line, context.error);
    }
    pv_destroy(&context);
    return status;
}
gcc -std=c11 runtime-caller.c -o runtime-caller -lm

Run ./runtime-caller on Linux/macOS or .\runtime-caller.exe in Windows PowerShell. It prints Hello, Ada!. This caller supplies its own main; omit PYCF_RUNTIME_MAIN and compile only runtime-caller.c. Error checks cover definition setup, string allocation, the call, and printing, and the context is destroyed on every exit path.

Build a standalone program

If the primary module defines or imports a main() callable without arguments, including one whose parameters all have defaults, an external C build can enable the supplied entry wrapper. For the example above, with a compatible 64-bit GCC toolchain:

gcc -std=c11 -DPYCF_RUNTIME_MAIN generated.c -o greeting -lm
./greeting

In Windows PowerShell, use .\greeting.exe to run the program. The command assumes a compatible GCC toolchain is already installed; PyCForge does not install one.

Integer returns from 0 to 255 become process exit statuses; other integers map to 255, and None maps to 0. A runtime failure prints its error and exits with status 1. Other return types are invalid for this wrapper.

The emitted program requires a 64-bit host with binary64 double. It includes its value routines and does not require Python, PySide6, NumPy, Numba, or Ting to run. Build through Compile C or an external toolchain; run the resulting program separately.

Register conversion imports

Open Tools → Register Conversion Imports… or use the direct icon beside Tools. The same dialog lists registered imports with namespace, version, author, date and status. Select an entry for its description, license, original file and verification producer. Register a .pcfplugin file or a folder, enable or disable entries, or remove a registration. Removing an entry leaves its original file intact; the bundled math and NumPy entries can be disabled. Both are included in the installation.

The SDK performs conversion-import verification. PyCForge reads that reported result and checks package compatibility and integrity. An unusable import stays visible as Cannot use, with a reason and guidance to resolve it in the SDK. Registration never runs the SDK test suite or executes a package entrypoint. The first supported package profile contains scalar declarations only. A failed replacement preserves the prior usable registration. Changes to registered imports make existing C output stale until a new conversion completes.

In the source editor, registered *import namespaces and their aliases support completion and positional call hints from this same catalogue. Only enabled, usable package exports are offered. See workspace assistance for shortcuts and limits.

Bundled math extension

Choose Runtime conversion and declare *import math or *import math as m in the module import preamble. The marker explicitly selects a bundled conversion extension. Ordinary import math still refers to an explicitly supplied source module.

*import math as m

def main():
    print(m.sqrt(9), m.copysign(1, -0.0))
    print(m.isfinite(m.pi), m.isnan(m.nan))
    return 0

The bundled math 0.3.0 import supplies 36 functions: fabs, sqrt, cbrt, copysign, isfinite, isinf, isnan, isclose; sin, cos, tan, asin, acos, atan, atan2; sinh, cosh, tanh, asinh, acosh, atanh; exp, exp2, expm1, log, log2, log10, log1p; pow, fmod, remainder, erf, erfc; and floor, ceil, trunc. Constants are pi, e, tau, inf and nan.

Calls accept Boolean, int64 and float64 values. atan2(y, x), copysign(x, y), pow(x, y), fmod(x, y) and remainder(x, y) take two positional arguments. log(x, base) takes an optional positional base: omission returns the natural logarithm, while a supplied base computes log(x)/log(base). Base one raises ZeroDivisionError, nonpositive inputs raise ValueError, and explicit None raises TypeError. isclose(a, b, *, rel_tol=1e-9, abs_tol=0.0) accepts a and b by position or name and tolerances only by name. Other exports take one positional argument. Domain failures raise ValueError, nonnumeric inputs raise TypeError and finite-input overflow raises OverflowError. Underflow may produce a subnormal value or signed zero. All supplied expressions evaluate once, positional expressions then keyword expressions in their source order, before binding to declaration slots.

floor, ceil and trunc return bounded int64 integers. Existing int64 inputs are preserved exactly, including the endpoints. Float results outside the int64 range raise OverflowError; NaN raises ValueError and infinity raises OverflowError. Arbitrary-size Python integer results and custom numeric protocols are outside this profile. The other numeric functions return float64; classification functions return bool.

Nonfinite values can be printed or converted with str; they render as inf, -inf and nan. These functions use C11 math.h and require binary64, default rounding and no fast-math; link libm on POSIX. NaN payloads and bitwise equality across different C libraries are not guaranteed.

Other math members, undeclared keywords, expanded arguments, first-class extension function values, alias rebinding/shadowing and namespace re-export are unsupported. Each consuming module declares its own extension import. Typed conversion reports PYC4703 instead of switching modes. Run Python blocks actual extension declarations before launch; comments and strings containing the marker do not count. The selected source and explicitly supplied execution companions are checked; unrelated editor tabs are not scanned.

Optional bases and named tolerances

Negative tolerances raise ValueError even for equal operands. Equal infinities are close; other infinite pairs are not. NaN operands are never close. Finite differences are tested against both relative bounds and the absolute bound. Tolerances at least one and positive infinity are accepted, following CPython behavior; NaN tolerances participate in ordinary comparisons. There is no array broadcasting.

*import math as m
def main():
    print(m.log(8, 2))
    print(m.isclose(b=1.0000000001, a=1.0))
    print(m.isclose(0.0, 0.000001, abs_tol=0.00001))
    return 0

The output is 3.0, True, True. A named base such as m.log(8, base=2) is rejected because math.log is positional-only. Existing registered math overrides keep their own version and signatures; disable an older override to select bundled math or register the current package explicitly.

Memory and work limits

The default context limit is 64 MiB of allocations, one million counted iteration, hashing, lookup, comparison, materialization, and type-check steps, and 128 call levels. These are workload limits, not a wall-clock timer. PYCF_RUNTIME_BYTES and PYCF_RUNTIME_STEPS may be configured by the external C build. See Conversion and input limits for source limits and failure handling.

For registered packages, see conversion import documentation and the Import Help action.