Migrating to v4.5.0

4.5.0 makes framec honor the verbatim passthrough contract uniformly: type annotations and initializer values reach the generated code exactly as you write them, with no Frame→target translation and no value wrapping. Two things that used to be papered over now require native spellings.

If your sources already use your target language’s own type names and init values, no change is needed — output is identical to 4.4.x.

1. Type annotations → native type names

framec no longer maps Frame-canonical names to native ones. Write the type name your target compiler expects. On many targets the canonical name already is native, so only the rows where they differ need changes — chiefly str (everywhere) and int/float (Rust).

The native spellings, per target (this is the same table the test corpus uses):

Target int string float (whole-number init example)
rust i32 / i64 String f32 / f64 = 0.0
python int str float = 0.0
typescript / javascript number string number = 0.0
c int char* float / double = 0.0
cpp int std::string double = 0.0
csharp int string double = 0.0 (32-bit float = 0.0f)
java int String double = 0.0 (32-bit float = 0.0f)
go int string float64 = 0.0
kotlin Int String Double = 0.0 (Float = 0.0f)
swift Int String Double / Float = 0.0
php int string float = 0.0
ruby Integer String Float = 0.0
lua number string number = 0.0
erlang integer string float = 0.0
dart int String double = 0.0
gdscript int String float = 0.0
# before (relied on the removed alias table) — Rust target
domain: n: int = 0   x: float = 1.5   s: str = ""

# after — native Rust names
domain: n: i64 = 0   x: f64 = 1.5   s: String = String::from("")

GDScript and Dart are statically-typed enough that : str/: list/: dict (GDScript) and : str/: float (Dart) were being aliased too — write String/Array/Dictionary and String/double respectively.

2. State-variable init values → native values

State-var initializers are now emitted verbatim, exactly like domain fields. The small “portable literal” wrapping (""String::from("") on Rust, etc.) is gone. Write the value your target accepts for the declared type:

# before — relied on portable-init wrapping (Rust)
$Idle { $.buffer: String = "" }

# after — native Rust init value
$Idle { $.buffer: String = String::from("") }
  • Rust StringString::from("...") (a bare "..." is a &str).
  • C++ std::stringstd::string("...").
  • Floats: write a decimal — 0.0, 1.0 (a whole-number float was previously truncated to 0/1; that bug is fixed). On Java/C#/Kotlin a 32-bit float slot needs the suffix (0.0f); their double and Rust’s f32/f64 take 0.0.
  • Dynamic targets (Python/JS/Ruby/Lua/PHP) are unchanged — "", 0, 0.0 work.

3. Comments → native leaders

framec no longer rewrites a // comment leader to the target’s native one. Section comments emit verbatim, like everything else. If you wrote // structural comments in a source targeting a #/--/% language, switch to that language’s leader — the same one you already use inside handler bodies:

# before — relied on //→native translation (Python target)
// reset the counter

# after — write Python's own comment leader
# reset the counter

Targets whose native leader is already // (Rust, C, C++, C#, Java, Kotlin, Swift, Go, Dart, JS, TS, PHP) are unaffected. A # in a structural section no longer throws E002 — it passes through verbatim like any native text.

Quick check

Compile your tree to each target you ship and let the native compiler flag the spots: an unknown str/int type, or a &str-into-String mismatch, points exactly at an annotation/value that needs its native spelling.