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
String→String::from("...")(a bare"..."is a&str). - C++
std::string→std::string("..."). - Floats: write a decimal —
0.0,1.0(a whole-number float was previously truncated to0/1; that bug is fixed). On Java/C#/Kotlin a 32-bitfloatslot needs the suffix (0.0f); theirdoubleand Rust’sf32/f64take0.0. - Dynamic targets (Python/JS/Ruby/Lua/PHP) are unchanged —
"",0,0.0work.
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.