diff --git a/README.md b/README.md index b169a15..48d4079 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,45 @@ -# Stationeers Language (slang) - -This is an ambitious attempt at creating: - -- A new programming language (slang) -- A compiler to translate slang -> IC10 -- A mod to allow direct input of slang in the in-game script editor to - automatically compile to IC10 before running - -This project currently outputs 3 files: - -- A Linux CLI -- A Windows CLI -- A Windows FFI dll - - Contains a single function: `compile_from_string` - -The aim of this project is to lower the amount of time it takes to code simple -scripts in Stationeers so you can get back to engineering atmospherics or -whatever you are working on. This project is NOT meant to fully replace IC10. -Obviously hand-coded assembly written by an experienced programmer is more -optimized and smaller than something that a C compiler will spit out. This is -the same way. It WILL produce valid IC10, but for large complicated projects it -might produce over the allowed limit of lines the in-game editor supports. - -Current Unknowns - -- Should I support a configurable script line length in-game to allow larger - scripts to be saved? -- Should compilation be "behind the scenes" (in game editor will ALWAYS be what - you put in. IC10 will be IC10, slang will be slang) +# Slang Language Documentation + +Slang is a high-level programming language that compiles to IC10 assembly for [Stationeers](https://store.steampowered.com/app/544550/Stationeers/). +It provides a familiar C-like syntax while targeting the limited instruction set +of in-game IC10. + +## Quick Links + +- [Getting Started](docs/getting-started.md) - Installation and first program +- [Language Reference](docs/language-reference.md) - Complete syntax guide +- [Built-in Functions](docs/builtins.md) - System calls and math functions +- [Examples](docs/examples.md) - Real-world code samples + +## Overview + +Slang aims to reduce the time spent writing IC10 assembly by providing: + +- **Familiar syntax** - C-like declarations, control flow, and expressions +- **Device abstraction** - Named device bindings with property access +- **Automatic register allocation** - No manual register management +- **Built-in functions** - Math operations and device I/O as function calls +- **Temperature literals** - Native support for Celsius, Fahrenheit, and Kelvin + +## Example + +```rust +device gasSensor = "d0"; +device airCon = "d1"; + +const TARGET_TEMP = 20c; + +loop { + yield(); + airCon.On = gasSensor.Temperature > TARGET_TEMP; +} +``` + +This compiles to IC10 that monitors temperature and controls an air +conditioner. + +## Project Status + +Slang is under active development. It may produce suboptimal code for complex programs. +It is not a replacement for IC10, for performance-critical or large scripts, +hand-written IC10 may still be preferred. diff --git a/docs/builtins.md b/docs/builtins.md new file mode 100644 index 0000000..701684e --- /dev/null +++ b/docs/builtins.md @@ -0,0 +1,302 @@ +# Built-in Functions + + + +- [Built-in Functions](#built-in-functions) + - [System Functions](#system-functions) + - [`yield()`](#yield) + - [`sleep(ticks)`](#sleepticks) + - [`hash(prefabName)`](#hashprefabname) + - [Device I/O Functions](#device-io-functions) + - [Reading from Devices](#reading-from-devices) + - [Load from device](#load-from-device) + - [Load From Device Batched](#load-from-device-batched) + - [Load From Device Batched Named](#load-from-device-batched-named) + - [Load Slot](#load-slot) + - [Load Reagent](#load-reagent) + - [Writing to Devices](#writing-to-devices) + - [Set On Device](#set-on-device) + - [Set On Device Batched](#set-on-device-batched) + - [Set On Device Batched Named](#set-on-device-batched-named) + - [Set Slot](#set-slot) + - [Math Functions](#math-functions) + - [Trigonometric Functions](#trigonometric-functions) + - [Trig Example](#trig-example) + - [Rounding Functions](#rounding-functions) + - [Rounding Example](#rounding-example) + - [Other Math Functions](#other-math-functions) + - [Math Example](#math-example) + - [See Also](#see-also) + + +Slang provides built-in functions for device I/O and mathematical operations. +These map directly to IC10 instructions. + +## System Functions + +### `yield()` + +Pauses execution for exactly one game tick. + +```rust +yield(); +``` + +**IC10:** `yield` + +--- + +### `sleep(ticks)` + +Pauses execution for the specified number of ticks. + +```rust +sleep(10); // Sleep for 10 ticks +``` + +**IC10:** `sleep ticks` + +--- + +### `hash(prefabName)` + +Computes the in-game hash for a prefab name. The hash is computed at compile +time and no runtime code is generated. + +```rust +const AC_HASH = hash("StructureAirConditioner"); +``` + +**Note:** This is different from IC10's `hash` instruction, which computes the +hash at runtime. + +```rust +setBatched(AC_HASH, "On", 0); +``` + +**IC10:** `sb -2087593337 On 0` (no hash computation at runtime) + +--- + +## Device I/O Functions + +### Reading from Devices + +#### Load from device + +`load(device, property)` / `l(device, property)` + +Loads a property value from a device: + +```rust +let temp = load(sensor, "Temperature"); +let temp = l(sensor, "Temperature"); + +// Preferred: use dot notation +let temp = sensor.Temperature; +``` + +**IC10:** `l r? d? var` + +--- + +#### Load From Device Batched + +`loadBatched(deviceHash, property, batchMode)` / `lb(...)` + +Loads a property from all devices matching a hash, aggregated by batch mode: + +```rust +const SENSOR = hash("StructureGasSensor"); +let avgTemp = loadBatched(SENSOR, "Temperature", "Average"); +let maxTemp = lb(SENSOR, "Temperature", "Maximum"); +``` + +**Batch Modes:** `"Average"`, `"Sum"`, `"Minimum"`, `"Maximum"` + +**IC10:** `lb r? deviceHash logicType batchMode` + +--- + +#### Load From Device Batched Named + +`loadBatchedNamed(deviceHash, nameHash, property, batchMode)` / `lbn(...)` + +Loads a property from devices matching both device hash and name hash: + +```rust +const SENSOR_HASH = hash("StructureGasSensor"); +const SENSOR_NAME_HASH = hash("Outdoor Gas Sensor"); +let avgTemp = loadBatchedNamed(SENSOR_HASH, SENSOR_NAME_HASH, "Temperature", "Average"); +let maxTemp = lbn(SENSOR_HASH, SENSOR_NAME_HASH, "Temperature", "Maximum"); +``` + +**IC10:** `lbn r? deviceHash nameHash logicType batchMode` + +**Note:** This function is useful when a script interfaces with a lot of +devices, as it allows for arbitrary device access without limited to the 6 `dx` pins. + +--- + +#### Load Slot + +`loadSlot(device, slotIndex, property)` / `ls(...)` + +Loads a slot property from a device: + +```rust +let occupied = loadSlot(sorter, 0, "Occupied"); +let occupied = ls(sorter, 0, "Occupied"); +``` + +**IC10:** `ls r? d? slotIndex logicSlotType` + +--- + +#### Load Reagent + +`loadReagent(device, reagentMode, reagentHash)` / `lr(...)` + +Loads reagent information from a device: + +```rust +let amount = loadReagent(furnace, "Contents", reagentHash); +let amount = lr(furnace, "Contents", reagentHash); +``` + +**IC10:** `lr r? d? reagentMode reagentHash` + +--- + +### Writing to Devices + +#### Set On Device + +`set(device, property, value)` / `s(...)` + +Sets a property on a device: + +```rust +set(valve, "On", true); +s(valve, "On", true); + +// Preferred: use dot notation +valve.On = true; +``` + +**IC10:** `s d? logicType r?` + +--- + +#### Set On Device Batched + +`setBatched(deviceHash, property, value)` / `sb(...)` + +Sets a property on all devices matching a hash: + +```rust +const LIGHT_HASH = hash("StructureWallLight"); +setBatched(LIGHT_HASH, "On", true); +sb(LIGHT_HASH, "On", true); +``` + +**IC10:** `sb deviceHash logicType r?` + +**Note:** This function is useful when a script interfaces with a lot of devices, +as it allows for arbitrary device access without limited to the 6 `dx` pins. + +--- + +#### Set On Device Batched Named + +`setBatchedNamed(deviceHash, nameHash, property, value)` / `sbn(...)` + +Sets a property on devices matching both device hash and name hash: + +```rust +const SENSOR_HASH = hash("StructureGasSensor"); +const SENSOR_NAME_HASH = hash("Outdoor Gas Sensor"); +setBatchedNamed(SENSOR_HASH, SENSOR_NAME_HASH, "On", true); +sbn(SENSOR_HASH, SENSOR_NAME_HASH, "On", true); +``` + +**IC10:** `sbn deviceHash nameHash logicType r?` + +--- + +#### Set Slot + +`setSlot(device, slotIndex, property, value)` / `ss(...)` + +Sets a slot property on a device: + +```rust +setSlot(sorter, 0, "Open", true); +ss(sorter, 0, "Open", true); +``` + +**IC10:** `ss d? slotIndex logicSlotType r?` + +--- + +## Math Functions + +All math functions accept numbers, variables, or expressions as arguments. + +### Trigonometric Functions + +| Function | Description | IC10 | +| ------------- | ---------------------------- | ------- | +| `sin(x)` | Sine of angle in radians | `sin` | +| `cos(x)` | Cosine of angle in radians | `cos` | +| `tan(x)` | Tangent of angle in radians | `tan` | +| `asin(x)` | Arc sine, returns radians | `asin` | +| `acos(x)` | Arc cosine, returns radians | `acos` | +| `atan(x)` | Arc tangent, returns radians | `atan` | +| `atan2(y, x)` | Two-argument arc tangent | `atan2` | + +#### Trig Example + +```rust +let angle = atan2(y, x); +let sineValue = sin(angle); +``` + +### Rounding Functions + +| Function | Description | IC10 | +| ---------- | ----------------------------- | ------- | +| `ceil(x)` | Round up to nearest integer | `ceil` | +| `floor(x)` | Round down to nearest integer | `floor` | +| `trunc(x)` | Remove decimal portion | `trunc` | +| `abs(x)` | Absolute value | `abs` | + +#### Rounding Example + +```rust +let rounded = floor(3.7); // 3 +let positive = abs(-5); // 5 +``` + +### Other Math Functions + +| Function | Description | IC10 | +| ----------- | ----------------------------- | ------ | +| `sqrt(x)` | Square root | `sqrt` | +| `log(x)` | Natural logarithm | `log` | +| `max(a, b)` | Maximum of two values | `max` | +| `min(a, b)` | Minimum of two values | `min` | +| `rand()` | Random number between 0 and 1 | `rand` | + +#### Math Example + +```rust +let root = sqrt(16); // 4 +let bigger = max(a, b); +let randomVal = rand(); +``` + +## See Also + +- [Language Reference](language-reference.md) — Complete syntax guide +- [Examples](examples.md) — Real-world code samples diff --git a/docs/examples.md b/docs/examples.md new file mode 100644 index 0000000..0b6a2f9 --- /dev/null +++ b/docs/examples.md @@ -0,0 +1,254 @@ +# Examples + +Real-world Slang programs demonstrating common patterns. + +## Temperature Control + +Basic thermostat that controls an air conditioner based on room temperature: + +```rust +device ac = "db"; +device roomGasSensor = "d0"; + +const TARGET_TEMP = 22c; +const HYSTERESIS = 1; + +loop { + yield(); + let temp = roomGasSensor.Temperature; + + if (temp > TARGET_TEMP + HYSTERESIS) { + ac.On = true; + } else if (temp < TARGET_TEMP - HYSTERESIS) { + ac.On = false; + } +} +``` + +**Note:** The IC10 chip is assumed to be inserted in the air conditioner's IC slot. + +--- + +## Two-Axis Solar Panel Tracking + +Handles two-axis solar panel tracking based on the sun's position: + +```rust +device sensor = "d0"; + +const H_PANELS = hash("StructureSolarPanelDual"); + +loop { + setBatched(H_PANELS, "Horizontal", sensor.Horizontal); + setBatched(H_PANELS, "Vertical", sensor.Vertical + 90); + yield(); +} +``` + +**Note:** Assumes the daylight sensor is mounted with its port looking 90 +degrees east of the solar panel's data port, an offset can be added on the +horizontal angle if needed. + +--- + +## Day/Night Lighting + +Controls grow lights during the day and ambient lights at night: + +```rust +device greenhouseSensor = "d0"; + +const daylightSensor = hash("StructureDaylightSensor"); +const growLight = hash("StructureGrowLight"); +const wallLight = hash("StructureLightLong"); + +loop { + yield(); + let solarAngle = lb(daylightSensor, "SolarAngle", "Average"); + let isDaylight = solarAngle < 90; + + sb(growLight, "On", isDaylight); + sb(wallLight, "On", !isDaylight); +} +``` + +--- + +## Pressure Relief Valve + +Controls a volume pump based on pressure readings for emergency pressure relief: + +```rust +device volumePump = "d0"; +device pipeSensor = "d1"; + +const MAX_PRESSURE = 10_000; +const R = 8.314; + +loop { + yield(); + + let pressure = pipeSensor.Pressure; + + if (pressure > MAX_PRESSURE) { + // Use PV=nRT to calculate the amount of mols we need to move + // n = PV / RT + let molsToMove = (pressure - MAX_PRESSURE) * + pipeSensor.Volume / (R * pipeSensor.Temperature); + + // V = nRT / P + let setting = molsToMove * R * pipeSensor.Temperature / pressure; + volumePump.Setting = setting; + volumePump.On = true; + } else { + volumePump.On = false; + } +} +``` + +--- + +## Greenhouse Environment Controller + +Complete greenhouse control with pressure, temperature, and lighting: + +```rust +device self = "db"; +device emergencyRelief = "d0"; +device greenhouseSensor = "d1"; +device recycleValve = "d2"; + +const MAX_INTERIOR_PRESSURE = 80; +const MAX_INTERIOR_TEMP = 28c; +const MIN_INTERIOR_PRESSURE = 75; +const MIN_INTERIOR_TEMP = 25c; +const daylightSensor = 1076425094; +const growLight = hash("StructureGrowLight"); +const wallLight = hash("StructureLightLong"); +const lightRound = hash("StructureLightRound"); + +let shouldPurge = false; + +loop { + yield(); + let interiorPress = greenhouseSensor.Pressure; + let interiorTemp = greenhouseSensor.Temperature; + + shouldPurge = ( + interiorPress > MAX_INTERIOR_PRESSURE || + interiorTemp > MAX_INTERIOR_TEMP + ) || shouldPurge; + + emergencyRelief.On = shouldPurge; + recycleValve.On = !shouldPurge; + + if ( + shouldPurge && ( + interiorPress < MIN_INTERIOR_PRESSURE && + interiorTemp < MIN_INTERIOR_TEMP + ) + ) { + shouldPurge = false; + } + + let solarAngle = lb(daylightSensor, "SolarAngle", "Average"); + let isDaylight = solarAngle < 90; + + sb(growLight, "On", isDaylight); + sb(wallLight, "On", !isDaylight); + sb(lightRound, "On", !isDaylight); +} +``` + +--- + +## Advanced Furnace Pressure Control + +Automates multi-furnace pump control based on dial setting for pressure target: + +```rust +const FURNACE1 = 1234; +const DIAL1 = 1123; +const ANALYZER1 = 1223; + +const FURNACE2 = 1235; +const DIAL2 = 1124; +const ANALYZER2 = 1224; + +const FURNACE3 = 1236; +const DIAL3 = 1124; +const ANALYZER3 = 1225; + +const R = 8.314; + +fn handleFurnace(furnace, dial, analyzer) { + let pressure = furnace.Pressure; + let targetPressure = max(dial.Setting, 0.1) * 1000; + + if (abs(targetPressure - pressure) <= 0.1) { + furnace.On = false; + return; + } + + let molsToMove = max(furnace.TotalMoles, 1) * ( + (targetPressure / pressure) - 1 + ); + + // V = nRT / P + if (molsToMove > 0) { + // Calculate volume required + if (analyzer.Pressure == 0) { + // No more gas to add + furnace.On = false; + return; + } + let volume = molsToMove * R * analyzer.Temperature / analyzer.Pressure; + + furnace.On = true; + furnace.SettingOutput = 0; + furnace.SettingInput = volume; + + return; + } + + // Calculate volume required + let volume = (-molsToMove) * R * furnace.Temperature / pressure; + + furnace.On = true; + furnace.SettingInput = 0; + furnace.SettingOutput = volume; + + return; +} + +loop { + yield(); + + handleFurnace(FURNACE1, DIAL1, ANALYZER1); + handleFurnace(FURNACE2, DIAL2, ANALYZER2); + handleFurnace(FURNACE3, DIAL3, ANALYZER3); +} +``` + +**Note:** This example does not handle edge cases such as insufficient gas in +the input network or overfilling the furnace/pipe network. + +--- + +## Common Patterns + +### Waiting for a Condition + +```rust +fn waitForDeviceToTurnOff(device) { + while (device.On) { + yield(); + } +} +``` + +## See Also + +- [Getting Started](getting-started.md) — First steps with Slang +- [Language Reference](language-reference.md) — Complete syntax guide +- [Built-in Functions](builtins.md) — System calls and math functions diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 0000000..c120460 --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,99 @@ +# Getting Started + + + +- [Getting Started](#getting-started) + - [Program Structure](#program-structure) + - [The `yield()` Function](#the-yield-function) + - [Your First Program](#your-first-program) + - [Explanation](#explanation) + - [Comments](#comments) + - [See Also](#see-also) + + +This guide covers the basics of writing your first Slang program. + +## Program Structure + +A Slang program consists of top-level declarations and a main loop: + +```rust +// Device declarations +device self = "db"; +device sensor = "d0"; + +// Constants +const THRESHOLD = 100; + +// Variables +let counter = 0; + +// Main program loop +loop { + yield(); + // Your logic here +} +``` + +## The `yield()` Function + +IC10 programs run continuously. The `yield()` function pauses execution for one +game tick, preventing the script from consuming excessive resources. + +**Important:** You should always include `yield()` in your main loop unless you +know what you're doing. + +```rust +loop { + yield(); // Recommended! + // ... +} +``` + +## Your First Program + +Here's a simple program that turns on a light when a gas sensor detects low +pressure: + +```rust +device gasSensor = "d0"; +device light = "d1"; + +const LOW_PRESSURE = 50; + +loop { + yield(); + light.On = gasSensor.Pressure < LOW_PRESSURE; +} +``` + +### Explanation + +1. `device gasSensor = "d0"` — Binds the device at port `d0` to the name + `gasSensor` +2. `device light = "d1"` — Binds the device at port `d1` to the name `light` +3. `const LOW_PRESSURE = 50` — Defines a compile-time constant +4. `loop { ... }` — Creates an infinite loop +5. `yield()` — Pauses for one tick +6. `light.On = gasSensor.Pressure < LOW_PRESSURE` — Reads the pressure and sets + the light state + +## Comments + +Slang supports single-line comments and documentation comments: + +```rust +// This is a regular comment + +/// This is a documentation comment +/// It can span multiple lines +fn myFunction() { + // ... +} +``` + +## See Also + +- [Language Reference](language-reference.md) — Complete syntax guide +- [Built-in Functions](builtins.md) — Available system calls +- [Examples](examples.md) — Real-world programs and patterns diff --git a/docs/language-reference.md b/docs/language-reference.md new file mode 100644 index 0000000..4f68d6b --- /dev/null +++ b/docs/language-reference.md @@ -0,0 +1,339 @@ +# Language Reference + + + +- [Language Reference](#language-reference) + - [Literals](#literals) + - [Numbers](#numbers) + - [Temperature Literals](#temperature-literals) + - [Booleans](#booleans) + - [Strings](#strings) + - [Variables](#variables) + - [`let` - Mutable Variables](#let-mutable-variables) + - [`const` - Constants](#const-constants) + - [Device Declarations](#device-declarations) + - [Device Property Access](#device-property-access) + - [Device Property Assignment](#device-property-assignment) + - [Operators](#operators) + - [Arithmetic Operators](#arithmetic-operators) + - [Comparison Operators](#comparison-operators) + - [Logical Operators](#logical-operators) + - [Ternary Operator](#ternary-operator) + - [Operator Precedence](#operator-precedence) + - [Control Flow](#control-flow) + - [`if` / `else`](#if-and-else) + - [`loop`](#loop) + - [`while`](#while) + - [`break`](#break) + - [`continue`](#continue) + - [Functions](#functions) + - [Declaration](#declaration) + - [Invocation](#invocation) + - [Return Values](#return-values) + - [Parentheses for Grouping](#parentheses-for-grouping) + - [See Also](#see-also) + + +Complete syntax reference for the Slang programming language. + +## Literals + +### Numbers + +Numbers can be integers or decimals. Underscores are allowed as visual +separators: + +```rust +const integer = 42; // Integer +const decimal = 3.14; // Decimal +const million = 1_000_000; // Integer with separators +const decimalSeparators = 5_000.50; // Decimal with separators +``` + +### Temperature Literals + +Append a unit suffix to specify temperature. Values are automatically converted +to Kelvin at compile time: + +| Suffix | Unit | Example | +| ------ | ---------- | ------- | +| `c` | Celsius | `20c` | +| `f` | Fahrenheit | `68f` | +| `k` | Kelvin | `293k` | + +```rust +const ROOM_TEMP = 20c; // Converts to 293.15 Kelvin +const FREEZING = 32f; // Converts to 273.15 Kelvin +const ABSOLUTE1 = 0k; // Already in Kelvin +const ABSOLUTE2 = 0; // Assumed to be in Kelvin +``` + +### Booleans + +Booleans compile to integer values `1` and `0` in IC10. + +```rust +device ac = "d0"; + +ac.Mode = false; +ac.On = true; +``` + +### Strings + +Strings use double or single quotes. They are primarily used for prefab and +name hashes. + +```rust +const AC_HASH = hash("StructureAirConditioner"); +const AC_NAME_HASH = hash("Greenhouse Air Conditioner"); +``` + +## Variables + +### `let` Mutable Variables + +Declares a variable that can be reassigned: + +```rust +let counter = 0; +// ... +counter = counter + 1; +``` + +### `const` Constants + +Declares a compile-time constant. Constants are inlined and do not consume +registers: + +```rust +const MAX_PRESSURE = 10_000; +const DOOR_HASH = hash("StructureCompositeDoor"); +``` + +Constants support the `hash()` function for compile-time hash computation. + +## Device Declarations + +The `device` keyword binds a device port or reference ID to a named variable: + +```rust +device self = "db"; // IC housing, or device the IC is plugged into (eg. an AC) +device sensor = "d0"; // Device at port d0 +device valve = "d1"; // Device at port d1 +device ac1 = "$3FC"; // Device with reference ID $3FC (hexadecimal 1020) +device ac2 = "1020"; // Device with reference ID 1020 (decimal) +``` + +**Note:** Reference IDs can be found in-game using the Configuration cartridge. + +### Device Property Access + +Read device properties using dot notation: + +```rust +let temp = sensor.Temperature; +let pressure = sensor.Pressure; +let isOn = valve.On; +``` + +### Device Property Assignment + +Write to device properties using dot notation: + +```rust +valve.On = true; +valve.Setting = 100; +``` + +## Operators + +### Arithmetic Operators + +| Operator | Description | Example | +| -------- | -------------- | -------- | +| `+` | Addition | `a + b` | +| `-` | Subtraction | `a - b` | +| `*` | Multiplication | `a * b` | +| `/` | Division | `a / b` | +| `%` | Modulo | `a % b` | +| `**` | Exponentiation | `a ** b` | +| `-` | Negation | `-a` | + +### Comparison Operators + +| Operator | Description | Example | +| -------- | --------------------- | -------- | +| `==` | Equal | `a == b` | +| `!=` | Not equal | `a != b` | +| `<` | Less than | `a < b` | +| `>` | Greater than | `a > b` | +| `<=` | Less than or equal | `a <= b` | +| `>=` | Greater than or equal | `a >= b` | + +### Logical Operators + +| Operator | Description | Example | +| -------- | ----------- | ---------- | +| `&&` | Logical AND | `a && b` | +| `\|\|` | Logical OR | `a \|\| b` | +| `!` | Logical NOT | `!a` | + +### Ternary Operator + +Conditional expressions using `?` and `:`: + +```rust +let result = condition ? valueIfTrue : valueIfFalse; +``` + +### Operator Precedence + +Operators are evaluated in the following order, from highest to lowest +precedence: + +| Precedence | Operator(s) | Description | +| ---------- | ----------------- | -------------------------------- | +| 1 | `()` `.` | Grouping, Property access | +| 2 | `!` `-` | Logical NOT, Negation | +| 3 | `**` | Exponentiation | +| 4 | `*` `/` `%` | Multiplication, Division, Modulo | +| 5 | `+` `-` | Addition, Subtraction | +| 6 | `<` `<=` `>` `>=` | Comparison | +| 7 | `==` `!=` | Equality | +| 8 | `&&` | Logical AND | +| 9 | `\|\|` | Logical OR | +| 10 | `?:` | Ternary conditional | +| 11 | `=` | Assignment | + +Use parentheses to override precedence: + +```rust +let result = (20 + 10) * 5; +``` + +## Control Flow + +### if and else + +Conditional branching: + +```rust +if (tank.Temperature > 30c) { + ac.On = true; +} else { + ac.On = false; +} +``` + +### `loop` + +Infinite loop that runs until `break`: + +```rust +loop { + yield(); + // Loop body + if (condition) { + break; // Exit the loop + } +} +``` + +### `while` + +Conditional loop that runs while the condition is true: + +```rust +while (counter < 100) { + counter = counter + 1; + yield(); +} +``` + +### `break` + +Exits the current loop: + +```rust +loop { + yield(); + // ... + if (done) { + break; + } +} +``` + +### `continue` + +Skips to the next iteration of the current loop: + +```rust +loop { + yield(); + if (shouldSkip) { + continue; + } + // This code is skipped when shouldSkip is true + // ... +} +``` + +## Functions + +**Warning:** Functions are currently experimental and may produce suboptimal code. + +### Declaration + +```rust +fn functionName(arg1, arg2) { + // Function body + return arg1 + arg2; +} +``` + +### Invocation + +```rust +let result = functionName(10, 20); +``` + +### Return Values + +Use `return` to exit a function and optionally return a value: + +```rust +fn calculate(x) { + if (x < 0) { + return 0; // Early return + } + + return x * 2; +} + +fn doWork() { + // No return value + return; +} +``` + +## Parentheses for Grouping + +Use parentheses to control operator precedence: + +```rust +let result = (a + b) * c; + +let complex = ( + temp > 0c && + stress < 50 && + (pressure < 10_000 || temp > 20c) +); +``` + +## See Also + +- [Getting Started](getting-started.md) — First steps with Slang +- [Built-in Functions](builtins.md) — System calls and math functions +- [Examples](examples.md) — Real-world code samples diff --git a/spilling.slang b/spilling.slang deleted file mode 100644 index 515d4f5..0000000 --- a/spilling.slang +++ /dev/null @@ -1,43 +0,0 @@ -device self = "db"; -device gasSensor = "d0"; -device atmosAnal = "d1"; -device atmosValve = "d2"; -device atmosTank = "d3"; -device atmosInlet = "d4"; - -atmosInlet.Lock = true; -atmosInlet.Mode = 1; -atmosValve.On = false; -atmosValve.Lock = true; - -let isPumping = false; -let tempPressure = 0; - -loop { - yield(); - let temp = gasSensor.Temperature; - let pres = atmosAnal.Pressure; - let liqV = atmosAnal.VolumeOfLiquid; - let tempVol = atmosAnal.Volume; - - let stress = 5_000 * liqV / tempVol; - - tempPressure = isPumping ? 1_000 : 10_000; - - let shouldTurnOnInlet = ( - temp > 0c && - pres < tempPressure && - stress < 50 - ); - - isPumping = ( - !shouldTurnOnInlet && - atmosTank.Pressure < 35_000 && - atmosAnal.RatioPollutant == 0 && - atmosAnal.RatioLiquidPollutant == 0 && - atmosAnal.Pressure > 1_000 - ); - - atmosValve.On = isPumping; - atmosInlet.On = shouldTurnOnInlet; -} diff --git a/test.slang b/test.slang deleted file mode 100644 index b02f6d1..0000000 --- a/test.slang +++ /dev/null @@ -1,72 +0,0 @@ -/// Laree script V1 - -device self = "db"; -device larre = "d0"; -device exportChute = "d1"; - -const TOTAL_SLOTS = 19; -const EXPORT_CHUTE = 1; -const START_STATION = 2; - -let currentIndex = 0; - -/// Waits for the larre to be idle before continuing -fn waitForIdle() { - yield(); - while (!larre.Idle) { - yield(); - } -} - -/// Instructs the Larre to go to the chute and deposit -/// what is currently in its arm -fn deposit() { - larre.Setting = EXPORT_CHUTE; - waitForIdle(); - larre.Activate = true; - waitForIdle(); - exportChute.Open = false; -} - -/// This function is responsible for checking the plant under -/// the larre at this index, and harvesting if applicable -fn checkAndHarvest(currentIndex) { - if (currentIndex <= EXPORT_CHUTE || ls(larre, 255, "Seeding") < 1) { - return; - } - - // harvest from this device - while (ls(larre, 255, "Mature")) { - yield(); - larre.Activate = true; - } - let hasRemainingPlant = ls(larre, 255, "Occupied"); - - // move to the export chute - larre.Setting = EXPORT_CHUTE; - waitForIdle(); - deposit(); - if (hasRemainingPlant) { - deposit(); - } - - larre.Setting = currentIndex; - waitForIdle(); - - if (ls(larre, 0, "Occupied")) { - larre.Activate = true; - } - waitForIdle(); -} - -loop { - yield(); - if (!larre.Idle) { - continue; - } - let newIndex = currentIndex + 1 > TOTAL_SLOTS ? START_STATION : currentIndex + 1; - - checkAndHarvest(currentIndex); - larre.Setting = newIndex; - currentIndex = newIndex; -}