convert
Convert a single file between C# and Calor.
calor convert <input> [options]
Overview
The convert command performs bidirectional conversion between C# and Calor:
- C# → Calor: Convert
.csfiles to Calor syntax - Calor → C#: Convert
.calrfiles to generated C#
The conversion direction is automatically detected from the input file extension.
Quick Start
# Convert C# to Calor
calor convert MyService.cs
# Convert Calor to C#
calor convert MyService.calr
# Specify output path
calor convert MyService.cs --output src/MyService.calr
# Include benchmark comparison
calor convert MyService.cs --benchmark
Options
| Option | Short | Default | Description |
|---|---|---|---|
--output | -o | Auto-detected | Output file path |
--benchmark | -b | false | Include benchmark metrics comparison |
--verbose | -v | false | Enable verbose output |
--explain | -e | false | Explain unsupported features in detail |
--no-fallback | — | false | Fail on unsupported constructs instead of emitting fallback TODOs |
--validate | — | false | Parse generated Calor before writing and report errors |
--passthrough | — | false | Widen the set of C# members preserved as §CSHARP interop blocks. Since 0.16 calor convert already rescues a member it cannot convert this way; see What Changed in 0.16 |
--timeout | -t | 0 | Conversion timeout in seconds; 0 disables it |
--explicit-call-closers | — | false | Emit explicit §/C for every §C call (v0.6.0-compatible output). |
--format | — | text | text or schema 2.0 json; there is no -f alias |
Auto-Detected Output Paths
If --output is not specified:
| Input | Output |
|---|---|
MyFile.cs | MyFile.calr |
MyFile.calr | MyFile.g.cs |
Supported Constructs
The converter validates generated C# before claiming a fully successful
conversion. It reports semantic losses with file:line locations and exposes
the same list as data.lossCount and data.losses[] in JSON. Unknown
operators, patterns, compound assignments, namespace handling, and local
functions no longer silently substitute a different construct.
When native conversion is impossible, the containing member is preserved as a
§CSHARP interop block and counted — on the command line this happens by
default, without --passthrough, which widens it further. "Conversion
successful" is printed only when there are no recorded losses and requested
validation passed.
| C# Construct | Calor Equivalent |
|---|---|
namespace | §M{id:Name} module |
class | §CL{id:Name:vis} class |
class method | §MT{id:Name:vis} method |
property | §PROP{id:Name:vis:type} property |
field | §FLD{id:type:name} field |
if/else if/else | §IF{id} / §EI / §EL branches (end at dedent) |
for loop | §L{id:var:from:to:step} |
while loop | §WH{id} |
What Changed in 0.17
0.16 got all 364 modules to parse. 0.17 is about what happens next: a module that parses can still stop at name binding, before any of the effect checking runs. At the 0.16 branch cut, 60 of the 364 stopped there. Now 40 do, and the number of modules the effect checker reaches went from 304 to 324.
Most of that came from two fixes to how the compiler resolves calls.
Overload resolution learned the conversions C# already allows. Deciding which
overload you meant was comparing parameter types too literally. Passing a
PropertyEnricher to a parameter typed ILogEventEnricher — an interface the
class implements — was reported as "no overload matches", and so was passing a
generic parameter T to a parameter typed as one of T's own constraints.
Member lookup learned about inheritance. Looking up a method asked the
receiver type for its own declarations only — nothing about base classes, base
interfaces, generic constraints, or System.Object. IValidator.GetType() was
"no such member" because an interface has no base type to walk; so were
MethodInfo.GetParameters(), declared on MethodBase, and IList<T>.Add(item),
declared on ICollection<T>. Across the same three projects, the share of call
sites the compiler can resolve went from 92.8% to 95.9%.
Four converter and emitter defects are fixed alongside them. A class using §EXT
emitted unqualified calls to module functions, which the C# compiler then
rejected; the same function referenced as a method group rather than called had
the same problem; a var bound to a method call lost its type, and with it every
member access on the result; and a named argument matching no parameter crashed
the binder outright.
What Changed in 0.16
Converted code is only useful if the compiler can read it back. We test that on three real open-source projects — MediatR, Serilog, and FluentValidation — which come to 364 modules.
In 0.15, 59 of those 364 modules came out of the converter as Calor the compiler could not parse. Long lambdas were written at the wrong indent, calls split across lines came out wrong, and a few other patterns tripped it. In 0.16 all 364 parse.
One more thing is fixed alongside those: when the converter could not work out a
lambda parameter's type, it used to write ?, which the compiler cannot read —
it gave up on the rest of that method. It now treats the parameter as untyped,
and reads the type from the Func<...> or Action<...> the lambda is handed
to. Between that fix and the parsing fixes above, the number of modules where
the converter crashed part-way through dropped from 73 to 1 across the same
three projects.
There is one change to what you get back. calor convert now often rescues a
member it could not convert into working Calor, by wrapping the original C# in a
§CSHARP block rather than sinking the whole file. That means converted output
can contain §CSHARP blocks even when you did not pass --passthrough, and the
report tells you how many. Partly converted beats nothing converted, but treat
each block as work still to do.
The rescue is not a guarantee. It fires when the generated Calor fails to parse
or fails to round-trip back to C#; output that parses but that Calor's own
compiler then rejects still fails the conversion, with nothing written and exit
1. --passthrough does not change that outcome.
Benchmark Comparison
Use --benchmark to see how the Calor version compares to C#:
calor convert PaymentService.cs --benchmark
This is a local source-pair comparison, not the published 217-program dashboard. Keep its metric output with the conversion report rather than assuming it reproduces the website headline.
Exit Codes
| Code | Meaning |
|---|---|
0 | Conversion completed; inspect the loss report to distinguish fully native from preserved interop |
1 | Missing input, unsupported route without fallback, timeout, or conversion failure |
See Also
- calor migrate - Convert entire projects
- calor assess - Find best conversion candidates
- calor benchmark - Detailed metrics comparison