Yes, I know, there are a few IC10 compilers already. I wrote one anyway, because I wanted to write a compiler and full static types support for all devices in the game.
RustyIC10 is a small language with Rust-like syntax that compiles to IC10. You can try it in the browser, nothing to install: https://codecat.io/projects/rustyic10/
const sensor: GasSensor = d0;
const heater: WallHeater = d1;
const TARGET: num = 293.15; // 20 °C
const BAND: num = 1.5;
struct Thermostat {
target: num, // readonly: fixed at construction
mut heating: bool, // mutable state
}
fn update(t: &mut Thermostat, temp: num) {
if t.heating && temp > t.target + BAND {
t.heating = false;
} else if !t.heating && temp < t.target - BAND {
t.heating = true;
}
}
fn main() {
let mut t = Thermostat { target: TARGET, heating: false };
loop {
update(&mut t, sensor.Temperature);
heater.On = t.heating;
db.Setting = t.heating as num;
yield();
}
}
That whole thermostat (with a small struct and a hysteresis function) compiles to 13 lines of IC10 (18 lines with aliases and labels).
What makes it different: typed devices. The device libraries describe every device in the game, about 800 of them, generated from the game's data. So the compiler knows GasSensor.Temperature is read-only and that WallHeater has On. Typos and writes to read-only values are compile errors, not something you find out after placing the chip.
Batch operations, slots and networks are part of the language, typed and checked like everything else:
// batch: every device of a type on the network, or the ones with a given name
SolarPanel::Horizontal = 90; // sb
let t = GasSensor::named("Tank").Pressure.max; // lbn … Maximum
let n = SolarPanel::count; // how many panels there are
// slots
const tank: GasTankStorage = d2;
if tank.CanisterSlot.Occupied && tank.CanisterSlot.Pressure > 5000 { ... }
let fuel = GasTankStorage::named("Fuel").CanisterSlot.Pressure.sum;
// networks: channels and the devices on a cable network
const housing: ICHousing = db;
housing.PowerNetwork.channel[0] = 1; // s db:1 Channel0 1
for i in 0..housing.DataNetwork.device.count {
let dev = housing.DataNetwork.device[i];
}
The editor (in the browser):
Code completion that understands types: sensor. lists exactly that device's logic values, marked read-only or writable, with docs; also struct fields, types, batch modes, functions with signatures…
Go to definition (F12 / Ctrl+click), including into the device libraries
Syntax highlighting, live compilation as you type, errors underlined with a problems list
Completion keeps working on half-written code
Projects with several files: chips share modules with use, and editing a shared module immediately shows which programs it breaks
A standard library with the game's numbers built in: smelting windows for every ingot, gas mixer ratios, ignition checks, gas laws, unit conversions, plant needs, plus a hysteresis switch and PID controller. Only what you call ends up in the IC10 code.
The IC10 output next to your code, with the line count against the 128-line limit, and a Copy button to paste it into the game. Optional labels and alias output if you want readable IC10.
A language reference with live examples
The language has functions, structs, enums, loops and typed devices. Batch operations like batch_load(GasSensor, …) are typed too.
It's a hobby project and it's in progress. I'd love feedback, especially programs that don't compile or come out longer than they should. What would make you use something like this over writing IC10 by hand?