Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

Sydrogen is a statically typed systems programming language that compiles to native machine code.

Sydrogen source files use the .anvil extension. If you drop one on the floor, it will not make a sound.

The compiler is Furnace.

Status: Sydrogen Alpha-7
Compiler: Furnace
Implementation: Rust
Parser: pest
Code generation: Direct x86-64 path or Cranelift
Output: ELF64/PE32+ executable or native object code

What Sydrogen Is

Sydrogen is a statically typed programming language whose compiled programs do not require a virtual machine or interpreter at runtime. Furnace either writes an ELF64 Linux or PE32+ Windows executable directly, or uses the system linker for the Cranelift path.

What Furnace Is

Furnace is the name of the Sydrogen compiler. The current generation is written in Rust and uses pest for parsing, a direct x86-64 code path and Cranelift for code generation, Rayon for parallel semantic analysis, and cc for the typed path's linking step.

Current Version

This documentation describes Alpha-7 of Sydrogen and the corresponding release of Furnace.

Source Files

Sydrogen source files use the .anvil extension. Furnace checks that input files use this extension before processing them.

Compiler Pipeline

flowchart LR
    A(Sydrogen source) --> B(pest)
    B --> C(AST)
    C --> D(Semantic analysis)
    D --> E(Direct native path or Cranelift path)
    E --> F(Executable)

Furnace is divided into several stages:

  • pest parses the source into an AST.
  • Semantic analysis validates the AST and rejects invalid programs.
  • The direct native path writes x86-64 instructions and an ELF64 or PE32+ executable.
  • Cranelift generates a native object file for programs that need the typed path.
  • cc (the system C compiler) links that object file into an executable.

Alpha-7 uses:

  • Rust for the compiler
  • pest for parsing
  • Cranelift for code generation
  • Rayon for parallel semantic analysis
  • cc for linking

How to Read This Book

If you are new to Sydrogen, start with Hello World and Program Structure.

The Language section describes individual language features.

The Compiler section describes how Furnace is organized.

The Reference sections provide quick lookup tables.

The Project Status section explains what is currently supported, what is not, and what is planned.


Next →

CLI Reference

This page documents the commands exposed by the furnace binary.

Update

The update command installs the latest tagged release of Furnace from its source repository.

furnace update

The command:

  1. Checks that git and cargo are available.
  2. Clones the Furnace repository into a temporary directory.
  3. Fetches tags and selects the latest tag sorted by version.
  4. Checks out that tag.
  5. Runs cargo install --path . --locked in the checked-out tree.
  6. Replaces the existing furnace binary on the PATH.

The repository URL is defined as FURNACE_REPO in Cli/commands/update.rs.

Example output:

Downloading source...
Latest tag: alpha-7
Installing Furnace...
Furnace updated successfully.

Failure modes:

  • git or cargo is missing from the PATH.
  • The repository cannot be cloned.
  • No tags are found in the repository.
  • cargo install fails to build or link.

On failure the command exits with a non-zero code and prints an error message.

Other Commands

Usage:
    furnace build <project>.blower [--windows] [--backend native|cranelift]
    furnace compile <file>.anvil [linux|windows] [--windows] [--backend native|cranelift]
    furnace run <file>.anvil|<project>.blower [--windows] [--backend native|cranelift]
    furnace backend <native|cranelift>
    furnace new <APP_TYPE> -n <NAME>
    furnace update
    furnace -version
    furnace -help

Build

furnace build project.blower loads a Sydrogen project, discovers the source files declared by its Files.location entries, and writes the named executable to the project's build/ directory. Use --backend native or --backend cranelift to select a backend.

build accepts only .blower targets. Compile an independent .anvil file with furnace compile file.anvil (or the equivalent explicit Linux target, furnace compile file.anvil linux).

Windows Target

Linux x86-64 and ELF64 are the default native target and executable format. Use either form below to cross-compile a Windows x86-64 PE32+ executable:

furnace compile main.anvil --windows
furnace compile main.anvil windows

Both commands select the direct native backend and write main.exe. The same --windows target option is accepted by build, which adds .exe to the project output name. Cross-compilation works when Furnace is running on Linux and does not require a Windows linker or SDK.

The Windows backend currently supports integer and Boolean computation, Weld value passing, variables, control flow, and Sydrogen function calls. It does not yet support Print, Input, Program.Stop(), floats, collections, Data, or objects. Those features produce an explicit compiler error because their native runtime or typed fallback is currently Linux-only. Cranelift Windows linking is also unsupported; selecting --backend cranelift with a Windows target is an error.

run --windows is recognized, but Furnace cannot execute the cross-compiled file on a non-Windows host; use compile or build for cross-compilation.

Persistent Backend Selection

The existing backend command saves the default used by future commands:

furnace backend native
furnace backend cranelift

The selected backend is automatically used by furnace build, furnace compile, and furnace run, including later Furnace processes. With no saved configuration, Cranelift remains the built-in default.

On Unix, Furnace stores the setting in $XDG_CONFIG_HOME/furnace/config, or ~/.config/furnace/config when XDG_CONFIG_HOME is unset. On Windows it uses %APPDATA%\furnace\config. The file contains, for example:

backend = "native"

An existing --backend native|cranelift option overrides the saved preference for one build, compile, or run command. Resolution priority is the command override, saved preference, then built-in default. Malformed configuration is reported rather than silently ignored.

Hello World

A minimal Sydrogen program is:

Open Nunction Main()
{
    Print("Hello, World!");
}

Main is the entry point of the program.

Print writes a value to standard output.

The Open keyword controls visibility, and Nunction declares a function that does not return a value. Together they describe a public function called Main that contains the body of the program.

A Sydrogen source file containing only the program above is a complete executable program. It can be saved with a .anvil extension and compiled by Furnace.


← Previous Next →

Program Structure

A Sydrogen program consists of declarations and statements.

A simple program looks like:

Open Nunction Main()
{
    Int I = 0;

    While (I < 10)
    {
        Print(\V"{I}");
        I = I + 1;
    }
}

Sydrogen uses braces {} to delimit function and control-flow bodies.

Statements are terminated with ;.

Top-Level Declarations

Top-level declarations can include:

  • Function definitions
  • Variable declarations
  • Data declarations
  • Import statements

The exact behavior of some declarations depends on the current compiler implementation. Use and Using imports are resolved before semantic analysis and work with either backend. Data and object code generation still has limitations; see Current Limitations for details.

Statement Terminators

Every statement ends with a semicolon. This includes variable declarations, assignments, function calls, control-flow statements, and the Stop statement. The semicolon tells the parser where one statement ends and the next begins.

Braces

Braces group statements into a single block. They are used for:

  • Function bodies
  • If / Else If / Else branches
  • While loop bodies
  • For loop bodies
  • Data declaration bodies
  • Object initializer bodies

← Previous Next →

Compilation

This page covers the practical steps of building Furnace and using it to compile a Sydrogen program.

Build Furnace

Furnace is built using Cargo:

cargo build --release

The release compiler is located at:

target/release/furnace

You can also install Furnace using Cargo:

cd Sydrogen
cargo install --path .

After installation, Cargo places furnace in its executable path, allowing you to invoke it directly without specifying target/release/furnace.

For example:

furnace build Project.blower
furnace compile main.anvil linux
furnace compile main.anvil --windows
furnace run main.anvil
furnace backend native
furnace backend cranelift
furnace new console -n Project
furnace update
furnace -help
furnace --help
furnace -version
furnace --version

This means you can use furnace directly from any directory, provided Cargo's binary directory is available in your PATH.

Documentation will use cargo build --release instead of cargo install --path .

Compile a Sydrogen Program

For a project, use its .blower configuration:

./target/debug/furnace build Project.blower

Project sources come from Files.location patterns relative to the project file. The executable is named by Project.Name and written to build/.

For one standalone source, use:

Linux/ELF64 is the default, so either of these commands produces main:

./target/debug/furnace compile main.anvil linux
./target/debug/furnace compile main.anvil

The compile process:

  1. Checks that the input file uses the .anvil extension.
  2. Reads the source file.
  3. Parses the source.
  4. Builds the AST.
  5. Performs semantic analysis.
  6. Selects the direct native path or the Cranelift path.
  7. Generates functions and calls.
  8. Writes an ELF64 or PE32+ executable directly, or creates an object file and invokes the Linux platform linker.
  9. Produces the executable.

Example output:

Compiling main.anvil...
Linking...
Build successful!
Output: ./main

The CLI uses the Platform enum in Cli/platform.rs.

The supported direct-native targets are:

linux (default): x86-64 ELF64
windows: x86-64 PE32+

To cross-compile from Linux to Windows without an external linker or Windows SDK, run:

furnace compile main.anvil --windows

The result is main.exe. furnace compile main.anvil windows is equivalent, and furnace build Project.blower --windows writes build/<Project.Name>.exe.

Windows output currently excludes Print, Input, Program.Stop(), floats, collections, Data, and objects. Furnace reports these as unsupported instead of falling back to an ELF executable. Windows also requires --backend native; the flag selects it automatically when no backend is explicitly supplied.

Run a Sydrogen Program

The CLI can compile and execute a program directly:

./target/debug/furnace run main.anvil

This command:

  1. Compiles the source.
  2. Writes the direct executable or links the generated object.
  3. Executes the resulting binary.
  4. Forwards the program's standard output and standard error.
  5. Returns the child process exit code.

Select the Default Backend

Use the backend command to persist the default backend:

./target/debug/furnace backend native
./target/debug/furnace backend cranelift

Available backends:

  • native writes a direct x86-64 ELF64 or PE32+ executable.
  • cranelift writes a native object file and links it with cc.

The selection is reused by build, compile, and run in later Furnace processes. If no preference has been saved, Cranelift remains the built-in default. Furnace stores the preference in the platform configuration directory: $XDG_CONFIG_HOME/furnace/config or ~/.config/furnace/config on Unix, and %APPDATA%\furnace\config on Windows.

Use --backend with build, compile, or run to override the saved backend for only that command:

./target/debug/furnace build Project.blower --backend native
./target/debug/furnace compile main.anvil linux --backend native
./target/debug/furnace run main.anvil --backend cranelift
./target/debug/furnace compile main.anvil --windows

Version and Help

Furnace exposes its version through centralized compiler metadata.

The current version is:

#![allow(unused)]
fn main() {
pub const VERSION: &str = "Alpha-7";
}

Version information can be requested with:

./target/debug/furnace -version

Help can be requested with:

./target/debug/furnace -help

Example version output:

Furnace Alpha-7

Usage:

Usage:
    Furnace compile <file>.anvil [linux|windows] [--windows] [--backend native|cranelift]
    Furnace run <file>.anvil [--windows] [--backend native|cranelift]
    Furnace backend <native|cranelift>
    Furnace new <APP_TYPE> -n <NAME>
    Furnace -version
    Furnace -help

Available backends:
    native: direct x86-64 ELF64 or PE32+ executable
    cranelift: native object file linked with cc

Create a Project

Create a console project with:

./target/debug/furnace new console -n Project

The command creates Project/Project.blower and Project/src/Main.anvil. The project file declares Name = "Project" and location = "src/*.anvil", so it can immediately be built with furnace build Project/Project.blower.

The generated source contains:

Open Nunction Main()
{
}

The supported application type is console. Furnace rejects unknown types, empty names, and existing project directories.

Furnace produces a native object file that can be linked separately:

cc main.o -o main -lm

The exact libraries required may depend on the generated program and target platform.

Run the Resulting Executable

The resulting executable can be started normally:

./main

← Previous Next →

.blower Projects

A .blower file is the Sydrogen project configuration format. It names the project output and declares exactly which .anvil files belong to the project.

Project
{
    Name = "PlaceHolder";
}

Files
{
    location = "src/*.anvil";
    location = "tests/*.anvil";
}

Project and Name

The Project section contains project metadata. Project.Name is required and becomes the executable filename. The example above produces build/PlaceHolder on platforms that do not add an executable suffix.

Names may contain letters, numbers, underscores, and hyphens.

Files and location

The Files section contains one or more location entries. Only .anvil sources matched by these entries belong to the project. Entries are evaluated relative to the directory containing the .blower file, regardless of the shell's current directory.

* matches within one path component, ? matches one character, and ** matches directories recursively. For example:

Files
{
    location = "src/*.anvil";
    location = "src/**/*.anvil";
}

Files matched by more than one entry are compiled once. Furnace orders source paths deterministically.

Building a project

Build a project through its .blower file:

furnace build project.blower
furnace build project.blower --backend native
furnace build project.blower --backend cranelift
furnace build project.blower --windows

furnace backend native or furnace backend cranelift saves the default used when --backend is omitted. The saved choice persists across Furnace invocations.

Furnace parses all matched sources as one project, performs project-wide semantic analysis, and writes the executable to build/ beside the .blower file. Both backends use the same output path derived from Project.Name.

For a standalone source file, continue to use:

furnace compile file.anvil linux
furnace compile file.anvil --windows

The compile command retains its existing single-file output behavior; it does not redirect standalone files through project builds.

Creating a project

furnace new console -n MyProject

This creates an immediately buildable layout:

MyProject/
├── MyProject.blower
└── src/
    └── Main.anvil

From MyProject/, run furnace build MyProject.blower.

To compile and immediately run all sources in a project without writing its normal build/ output, use furnace run MyProject.blower.

Functions

Sydrogen functions are declared with either:

  • Nunction for a function that does not return a value
  • A return type, such as Int, Float, Weld, Bool, Ore, or Materials, for a function that returns data

Furnace compiles each user-defined function as an independent native function. Calls use the function's declared parameter and return types.

Nunction

A Nunction is a function that does not return a value.

Nunction Tick()
{
    Print("tick");
}

It can be called with:

Tick();

Nunction calls do not return a value. They can take parameters and can call other functions.

Returning Functions

A returning function declares its return type before its name. It sends a value back to its caller with the Return keyword.

For example, this function declares an Int return type:

Int Add(Int A, Int B)
{
    Return A + B;
}

A returned value can be used like this:

Int Result = Add(10, 20);

The returned value can be assigned to a compatible variable or used directly in another expression.

Returning functions can also return collection data. The declared shape and element types must match the returned value:

Ore(Int Number, Weld Name) MakePerson()
{
    Return {14, "Den"};
}

The Return Keyword

Return ends the current function call and sends its expression back to the caller:

Weld Greeting()
{
    Return "hello";
}

Return is case-sensitive and must be written with a capital R.

A Nunction cannot return data because it has no return type. This is an error:

Nunction Bad()
{
    Return 42;
}

Furnace reports Void function Bad cannot return a value.

The returned expression must also be compatible with the function's declared return type. Returning a mismatched data type is an error:

Int Bad()
{
    Return "wrong";
}

Furnace reports Return type mismatch in Bad: expected Int, got Weld.

Function Parameters

The grammar accepts parameter declarations:

Nunction PrintNumber(Int Value)
{
    Print(\V"{Value}");
}

A call can be written as:

PrintNumber(42);

Furnace checks argument count and argument types during semantic analysis. Type aliases such as String and Weld, and Boolean and Bool, are treated as equivalent.

Native Function Calls

For example:

Nunction Tick()
{
    Print("tick");
}

Open Nunction Main()
{
    Tick();
}

The compiler compiles Tick as an independent Cranelift function and emits a call from Main.

The same calling convention supports parameterized functions, return values, calls inside If, While, and For, and recursive or mutually recursive functions.

See Function Calls for details on how calls are written, and Recursion for the current state of recursive functions.


← Previous Next →

The Main Function

Every executable Sydrogen program requires a Main entry point.

The standard form is:

Open Nunction Main()
{
    // program
}

Open controls visibility.

Main is used as the root of executable code generation.

Furnace searches for a function named Main.

If a program does not contain a Main function, Furnace cannot produce an executable. The semantic analyzer checks that the Main function has the correct shape before code generation begins.

The Main function is treated specially: the Stop statement is not allowed inside it, since there is no enclosing loop or conditional from which to break early. To terminate a program early, use Program.Stop().


← Previous Next →

Variables

Variables are declared using a type, a name, and optionally an initial value.

Example:

Int I = 0;

A variable can later be assigned:

I = 10;

Variables can be modified multiple times:

I = I + 1;
I = I + 5;

Declaration

General form:

Type Name = Value;

Examples:

Int Age = 14;
Float Height = 181.0;
Weld Name = "Sydrogen";
Ore[3] Numbers = [10, 20, 30,];
Materials Int List = (1, 2, 3,);

A trailing comma is allowed in array and list literals.

Assignment

General form:

Name = Value;

Example:

Age = Age + 1;

The assigned value must be compatible with the variable's type. The semantic analyzer rejects assignments whose right-hand side does not match the declared type. For example, assigning a Weld value to a Bool variable produces a type error.

For more details on the available types, see Types.


← Previous Next →

Types

Alpha-7 currently includes primitive types, arrays, tuples, lists, and a limited generic type.

Primitive Types

The primary primitive types are:

TypePurpose
NumberNumeric type category
IntInteger values
FloatFloating-point values
WeldCanonical string type
StringAlias of Weld
BoolBoolean values
BooleanBoolean values

Bool and Boolean refer to the same type and can be used interchangeably.

Int and Float are distinct concrete types in the Number category. Variable declarations use the concrete type directly, such as Int Count = 0; or Float Ratio = 1.5;.

Sydrogen uses static type checking.

Arrays

Arrays use the Ore keyword followed by a size.

A fixed-size array:

Ore[3] FixedNums = [10, 20, 30,];

An array with an inferred size:

Ore[EMPTY] InferredNums = [100, 200, 300, 400,];

Array literals use square brackets.

A trailing comma is allowed.

Elements can be accessed by index:

Print(FixedNums[0]);

Elements can be assigned by index:

FixedNums[1] = 55;

Arrays expose .Length:

Print(FixedNums.Length);

The semantic analyzer checks that an explicitly declared array size matches the initializer count.

Arrays have a fixed number of elements after creation.

Your array will not spontaneously grow because it has decided that four elements are not enough. That job belongs to Materials.

Tuples

Tuples use Ore followed by named fields.

Example:

Ore(Int Number1, Int Number2) TwoNumbers = {1, 2};

A tuple containing different field types:

Ore(Int Age, Weld Name) Person = {14, "Den"};

Fields are accessed by name:

Print(Person.Age);
Print(Person.Name);

Fields can be assigned:

Person.Age = 15;

The semantic analyzer checks the number and types of tuple initializer values.

Lists

Lists use the Materials keyword.

A list initialized with values:

Materials Int Numbers = (10, 20, 30,);

An empty list:

Materials Int new EmptyList;

An empty list starts with zero elements and an initial capacity of four.

Elements can be read and assigned by index:

Print(Numbers[0]);
Numbers[1] = 50;

Lists expose .Length and .Len:

Print(Numbers.Length);

Available methods include:

MethodDescription
.Add(value)Adds an element to the list
.Remove(index)Removes an element and shifts later elements
.RemoveAt(index)Alias for .Remove(index)

Example:

Numbers.Add(40);

Print(Numbers[3]);
Print(Numbers.Length);

Numbers.Remove(0);

Print(Numbers[0]);
Print(Numbers.Length);

Generic Types

Generic can be used as the element type of a list.

Example:

Materials Generic new Items;

Items.Add(999);
Items.Add(1234);

Print(Items[0]);
Print(Items[1]);

The current implementation stores generic list elements as integers internally.

Mixed-type generic storage is not currently implemented as a type-checked feature.

Booleans

Booleans are declared with Bool or Boolean. The two keywords are identical.

Bool IsOpen = true;
Boolean IsClosed = false;

A Bool variable holds exactly one of two values: true or false. These are recognized as Boolean literals by the lexer and parser, and are represented internally as Boolean values.

A Bool variable can be reassigned:

Bool Flag = true;
Flag = false;
Flag = true;

A Bool variable may only contain true, false, or another Bool variable. The compiler rejects assignments of integers, floats, or strings to Bool variables.

Bool Value = 1;

produces:

error: Type error: cannot assign Int to Bool variable 'Value'

Similarly:

Bool IsOpen = true;
IsOpen = "true";

produces:

error: Type error: cannot assign Weld to Bool variable 'IsOpen'

Print outputs true or false for Boolean values.

Open Nunction Main()
{
    Bool Flag = true;
    Print(Flag);
    Flag = false;
    Print(Flag);
}

This program outputs:

true
false

Boolean variables can be used directly in conditions:

Bool Running = true;

If (Running)
{
    Print("running");
}

The value true is treated as a true condition and false is treated as a false condition.


← Previous Next →

Integers

Integer values use:

Int

Example:

Int Counter = 0;

Integer arithmetic supports:

Int A = 10;
Int B = 5;

Int Add = A + B;
Int Subtract = A - B;
Int Multiply = A * B;
Int Divide = A / B;

Integer variables can be modified:

Counter = Counter + 1;

Integer values are also used as the underlying storage for Generic list elements in the current implementation.

See Operators for the full list of arithmetic, power, and bitwise operations.


← Previous Next →

Floating-Point Numbers

Floating-point values use:

Float

Example:

Float Temperature = 21.5;

Floating-point arithmetic supports the standard arithmetic operators:

Float A = 10.5;
Float B = 2.0;

Float Result = A + B;

A floating-point value can be read from standard input using Input(Float):

Float Value = Input(Float);

See Input and Operators for more details.


← Previous Next →

Strings

Weld is the canonical string type. String is an alias for Weld; both names resolve to the same internal type.

Example:

Weld Name = "Sydrogen";
String Alias = Name;

String literals use double quotes:

"Hello"

Strings can be passed to Print:

Print("Hello, World!");

String Escapes

String literals support:

SequenceMeaning
\nNewline
\tTab
\rCarriage return
\0Null byte
\\Backslash
\"Double quote

Example:

Weld Line = "Hello\nWorld";

See String Interpolation for embedding values into strings.


← Previous Next →

String Interpolation

Sydrogen supports interpolation using the \V string form.

Example:

Int I = 42;

Print(\V"{I}");

The value of I is inserted into the string.

Multiple values can be used:

Int A = 10;
Int B = 20;

Print(\V"A = {A}, B = {B}");

Member access is also supported:

Ore(Int Age, Weld Name) Person = {14, "Den"};

Print(\V"{Person.Name} is {Person.Age}");

The interpolation form is recognized by the lexer through the \V prefix. Inside the string, expressions enclosed in { } are evaluated and rendered as their string representation.


← Previous Next →

Input

Sydrogen provides input through Input.

Integer Input

Int Value = Input(Int);

This reads input as a string and parses it as an integer. An input that cannot be parsed as an integer produces an input error.

Floating-Point Input

Float Value = Input(Float);

This reads input as a string and parses it as a floating-point value. An input that cannot be parsed as a floating-point value produces an input error.

String Input

Weld Value = Input();

This reads a string from standard input.

The current implementation uses standard C input facilities internally.


← Previous Next →

Operators

Alpha-7 supports arithmetic, comparison, bitwise, unary, and loop increment operators.

Arithmetic

OperatorOperation
+Addition
-Subtraction
*Multiplication
/Division
%Modulo
**Power

Example:

Int A = 10;
Int B = 5;

Int C = A + B;
Int D = A - B;
Int E = A * B;
Int F = A / B;
Int G = A % B;

% requires integer operands and returns the integer remainder.

Power

The ** operator performs exponentiation.

Int Result = 2 ** 8;

Power expressions are right-associative.

For example:

A ** B ** C

is interpreted as:

A ** (B ** C)

The current implementation lowers power operations through the C pow function.

Increment and Decrement

++ and -- are postfix operators currently used in the increment section of a For loop.

Example:

For (Int I = 0; I < 10; I++)
{
    Print(\V"{I}");
}

Decrementing is also supported:

For (Int I = 10; I > 0; I--)
{
    Print(\V"{I}");
}

See Unary Operators, Comparisons, and Logical Operators for the related operator families.


← Previous Next →

Unary Operators

Alpha-7 supports unary negation and unary plus.

Example:

Int Value = -10;

Unary negation can also be applied to an expression:

Int Result = -(A + B);

Unary plus is a no-op:

Int Value = +42;

It exists because sometimes a language designer looks at unary minus and thinks, "why should minus get all the attention?"

Precedence

Unary operators bind looser than **. Therefore:

-2 ** 2

is interpreted as:

-(2 ** 2)

which produces:

-4

This is part of the documented language rule that unary operators bind looser than the power operator.


← Previous Next →

Comparisons

Comparison operators can be used in conditions and loops.

OperatorMeaning
<Less than
>Greater than
<=Less than or equal
>=Greater than or equal
==Equal
!=Not equal

Example:

If (A < B)
{
    Print("A is smaller");
}

Comparison expressions are evaluated to a Boolean condition. They appear in If / Else If conditions, While loop conditions, and the condition position of a For loop.

Boolean values can also be used directly in conditions, since true and false are themselves valid conditions. See Types for details.


← Previous Next →

Logical Operators

Sydrogen currently provides And, Or, and Xor as bitwise operators for integer values.

OperatorOperation
AndBitwise AND
OrBitwise OR
XorBitwise XOR

Precedence is:

And > Or > Xor

Example:

Int A = 12;
Int B = 10;

Int C = A And B;
Int D = A Or B;
Int E = A Xor B;

These operators currently operate on integer operands. They are not yet a general-purpose boolean logic system for non-integer values.


← Previous Next →

Conditionals

Sydrogen supports:

  • If
  • Else If
  • Else

If

If (I < 10)
{
    Print("Less than ten");
}

Else

If (I < 10)
{
    Print("Small");
}
Else
{
    Print("Large");
}

Else If

If (I < 10)
{
    Print("Small");
}
Else If (I < 100)
{
    Print("Medium");
}
Else
{
    Print("Large");
}

Conditions must evaluate to a valid condition for the current compiler. In other words, a condition must be a type the compiler can evaluate as truthy or falsey in the current backend.

For selecting among several exact values, see Switch Statements.


← Previous Next →

Switch Statements

Switch selects one branch by comparing a value with the cases in source order:

Switch (X) {
    Case 1 {
        Print("One");
    }

    Case 2 or 3 {
        Print("Two or three");
    }

    Base {
        Print("Other");
    }
}

The expression in Switch (...) is evaluated exactly once. The compiler then compares that retained result with each Case value. Only the first matching branch runs, and execution continues after the whole Switch when its body finishes. Cases never fall through, so no Break or other terminator is required.

Switch values and Case values currently support Int, Float, and Bool. Every value in a Case must have exactly the selector's type; Furnace does not insert numeric conversions to make a Case match. Float Switch statements use the Linux typed backend and are not currently available for Windows output.

Case forms

A Case can use a block:

Case 1 {
    Print("One");
}

For one statement, -> provides a compact form:

Case 1 -> Print("One");

The alternate arrow spelling places the arrow before the values and a colon before the action:

Case -> 1: Print("One");

Both arrow forms have exactly the same behavior as a one-statement block.

Lowercase or gives one Case multiple values:

Case 2 or 3 {
    Print("Two or three");
}

Case 4 or 5 -> Print("Four or five");

Repeated constant values are rejected, including repetitions hidden in an or list or a later Case.

Base fallback

Base runs when no Case matches. It supports block and arrow bodies:

Base {
    Print("Other");
}

Base -> Print("Other");

A Switch may omit Base; if nothing matches, it simply does nothing. At most one Base is allowed, and it must be the final branch.

Switch and errors

Switch uses ordinary compiler control flow, so typed errors retain their usual behavior:

Do {
    Switch (Value) {
        Case 0 -> Throw Error.InvalidValue("Zero is invalid");
        Base -> Print("Valid");
    }
}
Catch Error.InvalidValue => ERROR {
    Print(ERROR);
}
Finally {
    Print("Finished");
}

-> is only the compact Switch action operator. => remains the distinct Catch binding operator.

Switch control flow is supported by the direct native ELF64 and PE32+ paths and by Cranelift. The usual backend restrictions still apply to statements inside a branch—for example, Windows output cannot currently use Print.


← Previous Next →

While Loops

Sydrogen supports While loops.

Basic syntax:

While (Condition)
{
    // body
}

Example:

Int I = 0;

While (I < 10)
{
    I = I + 1;
}

The condition is evaluated before every iteration.

Nested Loops

While loops can be nested:

Int I = 0;
Int V = 0;

While (I < 100)
{
    V = 0;

    While (V < 100)
    {
        V = V + 1;
    }

    I = I + 1;
}

Nested loops are compiled to native control flow.

Normal Sydrogen While loops execute on one thread.

The compiler using multiple CPU cores for semantic analysis does not make the generated loop multicore. The compiler cannot simply yell "parallel!" at a loop and hope for the best.


← Previous Next →

For Loops

Sydrogen supports C-style For loops.

Basic syntax:

For (Init; Condition; Increment)
{
    // body
}

Example:

For (Int I = 0; I < 10; I++)
{
    Print(\V"{I}");
}

A For loop has three parts:

  1. Init - runs once before the loop.
  2. Condition - is evaluated before each iteration.
  3. Increment - runs after each iteration.

The increment expression currently uses ++ or --.

Example:

For (Int I = 10; I > 0; I--)
{
    Print(\V"{I}");
}

The increment variable must be a numeric variable. The loop variable is not in scope while its initializer is being evaluated.

Skip continues with the next iteration of the loop. Stop exits the current loop. Skip can only be used inside a loop.

A For loop without braces produces an error.


← Previous Next →

ForEach Loops

Sydrogen supports ForEach loops that iterate over a collection.

Basic syntax:

ForEach (type item in collection)
{
    // body
}

Example:

Array[Int] nums = {1, 2, 3, 4, 5};

ForEach (Int num in nums)
{
    Print(num);
}

A ForEach loop has two parts:

  1. type item - declares the loop variable and its type.
  2. collection - the collection being iterated.

The loop variable is in scope only inside the loop body. It is not in scope while the collection expression is being evaluated.

The collection must be an Array or List. A type mismatch produces a semantic error.

Skip continues with the next iteration of the loop. Stop exits the current loop. Skip can only be used inside a loop.

A ForEach loop without braces produces an error.


← Previous Next →

The Stop Statement

Stop provides an early exit from a loop or conditional block.

Example:

While (I < 10)
{
    If (I == 5)
    {
        Stop;
    }

    I = I + 1;
}

The semantic analyzer currently enforces these rules:

  • Stop cannot be used inside Main
  • Stop must be inside a loop or If statement

In the compiler, Stop is represented as Statement::Stop in src/ast.rs.

The semantic checks are implemented in src/semantic.rs.

Code generation is handled by FunctionCompiler::compile_statement in src/codegen.rs.

Stop is not an exception mechanism. It represents a structured early exit.


← Previous Next →

The Program Namespace

The Program namespace provides runtime operations.

The currently implemented member is:

Program.Stop();

This terminates the program by calling the C exit function with a status of 0.

Program.Stop() is treated as a namespace operation rather than a normal user-defined function call.

The semantic analyzer checks that:

  • It is used inside a function
  • It has zero arguments
  • The namespace and operation are valid

Example:

Open Nunction Main()
{
    Print("before");
    Program.Stop();
    Print("after");
}

The second Print is never reached.


← Previous Next →

Function Calls

Functions are called using their name followed by parentheses.

A zero-argument call:

Tick();

A call with arguments:

PrintNumber(42);

A call with multiple arguments:

Add(10, 20);

Furnace compiles calls as native calls to independently compiled Sydrogen functions.

Semantic analysis checks the function name, argument count, and argument types before code generation.

See Functions and Recursion for the current implementation status.


← Previous Next →

Recursion

The grammar can represent recursive functions.

Example:

Nunction Countdown(Int I)
{
    If (I > 0)
    {
        Print(\V"{I}");
        Countdown(I - 1);
    }
}

Recursive calls use the same native function call path as other calls. A recursive function can have parameters and can return a value.


← Previous Next →

Scope

Variables declared inside a block are intended to belong to that block.

Example:

Open Nunction Main()
{
    Int I = 10;

    If (I > 0)
    {
        Int V = 20;
    }
}

V is declared inside the If block.

The AST represents nested blocks, while the current backend uses a function-level variable map.

Full lexical scope, shadowing, and capture rules are still under development.

This is an example of a feature that can be represented at the AST level without being fully executable in the backend yet.


← Previous Next →

Error Handling

Sydrogen provides typed, structured errors through Do, Catch, Finally, and Throw. Error propagation is part of the language runtime; it does not use Rust panics, C++ exceptions, or Windows SEH.

Throwing an error

Throw constructs an error with a built-in type and a Weld message, stops the current block, and begins propagation:

Throw Error.InvalidValue("Value cannot be negative");

The initial built-in hierarchy is:

  • Error, the general base type
  • Error.InvalidValue
  • Error.DivisionByZero
  • Error.Overflow
  • Error.IO

Integer division or remainder by zero automatically throws Error.DivisionByZero in the direct native backend.

Do and Catch

Do protects a block. It must be followed by at least one Catch or a Finally block:

Do {
    Throw Error.InvalidValue("bad input");
}
Catch Error.InvalidValue => Err {
    Print(Err);
}

The name after => is user-defined and exists only in that catch block. Printing it writes the error's human-readable message.

Catches are tested from top to bottom and only the first match runs. A general Catch Error matches every Sydrogen error and must appear last:

Do {
    DangerousOperation();
}
Catch Error.DivisionByZero => DivisionError {
    Print(DivisionError);
}
Catch Error.IO => IOError {
    Print(IOError);
}
Catch Error => OtherError {
    Print(OtherError);
}

Furnace rejects duplicate catches and catches placed after Catch Error.

Finally and propagation

Finally is optional and always runs after its protected sequence: after success, after a caught error, and while an unmatched error propagates. A return from the protected body also runs Finally before returning.

Do {
    Int Result = Divide(10, 0);
    Print(Result);
}
Catch Error.DivisionByZero => ERROR {
    Print(ERROR);
}
Catch Error => ERROR {
    Print(ERROR);
}
Finally {
    Print("Operation finished");
}

Nested handlers propagate to the next enclosing matching catch. An inner handler that only catches Error.IO, for example, does not consume an Error.InvalidValue; its Finally runs and the outer search continues.

On native Linux, an error that reaches the program entry point prints its type and message and exits with status 1:

Error.InvalidValue: Value cannot be negative

Backend status

The direct native backend implements error propagation for ELF64/Linux and for PE32+/Windows code, including nested handlers and Finally. Linux also implements the top-level uncaught-error diagnostic.

Current limitations:

  • The Cranelift backend rejects programs using error handling with an explicit diagnostic; use --backend native.
  • Windows PE32+ cannot yet print an uncaught error because Windows console I/O is not implemented. The image returns to the loader cleanly, but the Linux type-and-message diagnostic is unavailable.
  • Stop and Skip leaving a protected block do not yet run Finally; return and error propagation do.
  • Built-in overflow and I/O errors currently require an explicit Throw.

← Previous Next →

Comments

Sydrogen supports single-line comments using //.

Example:

// This is a comment

Int I = 0;

Comments are ignored by the compiler.


← Previous Next →

Visibility

Sydrogen uses visibility modifiers to control declaration visibility.

The current visibility keywords are:

  • Open
  • Closed
  • Showcase

Example:

Open Nunction Main()
{
}

The complete module and visibility system is still under development.

Showcase

Showcase is a third visibility modifier.

It is currently parsed and stored in the AST but does not affect code generation.

It is reserved for future functionality related to exported declarations, documentation, or REPL introspection.

Example:

Showcase Nunction Helper()
{
    Print("helper");
}

← Previous Next →

Data Declarations

Sydrogen supports the Data keyword for declaring structured types.

Example:

Data Person
{
    Int Age;
    Weld Name;
}

A Data declaration contains typed fields.

The Data declaration can use a visibility modifier:

Open Data Point
{
    Int X;
    Int Y;
}

Data declarations are currently parsed and represented in the AST.

The current backend does not generate executable code for Data declarations.


← Previous Next →

Object Instantiation

Instances of Data types use object declaration syntax.

Example:

Person Den
{
    Age = 14;
    Name = "Den";
}

Members can also be separated using commas:

Person Den { Age = 14, Name = "Den"; }

Nested member paths are supported by the parser:

Person Den { Address.City = "Den", Age = 14; }

Object declarations are represented as Statement::ObjectDecl.

The current backend does not generate executable code for object declarations.

They are currently checked for structural validity.


← Previous Next →

Imports

Sydrogen provides Use and Using for importing modules and symbols. Imports are resolved before semantic analysis and work with both Native and Cranelift.

Use

Use File;

Open Nunction Main()
{
    File.Function1();
}

Use imports a module while preserving its namespace. It does not place the module's functions directly into the importing scope, so Function1() alone is not made valid by Use File;.

Using

Using File: Function1;

Open Nunction Main()
{
    Function1();
}

Using imports one specific symbol directly into the current module's scope. It does not expose the rest of the module.

The difference is:

Use File;
File.Function1();

Using File: Function1;
Function1();

Module discovery

During a .blower build, the module table contains only .anvil files matched by the project's Files.location entries. A file that exists on disk but is excluded from the project cannot be imported. Each module name initially comes from its filename: src/File.anvil defines module File. Duplicate filenames that would create the same module name are rejected.

During standalone compilation, imports are loaded relative to the importing source file, not the shell's working directory. For example, Use File; in Main.anvil loads the sibling File.anvil.

Qualified paths are structural and may identify nested source paths:

Use System.Math;

For standalone compilation this resolves System/Math.anvil beside the importing file. In a project it must match a declared project source whose path ends in System/Math.anvil. This does not imply that a standard-library module exists.

Visibility and conflicts

Only Open declarations can be imported across a module boundary. Private or Closed declarations remain usable inside their own module but cannot be reached through Use or Using. Functions are callable through either import form; existing named declarations such as Data types can be selected with Using where their normal language syntax permits their use.

Repeated identical imports are idempotent. Furnace rejects selective imports that give two different symbols the same local name, imports that conflict with a local declaration, and ambiguous duplicate module names.

Missing modules, missing symbols, and inaccessible symbols are reported before code generation. Imports must appear at module top level.

Dependency graph

Furnace builds one dependency graph for the loaded modules. Nested dependencies are resolved once and keep their scope: importing File does not re-export symbols that File imported internally. Direct and longer cycles are rejected, for example:

error: circular import detected: A -> B -> C -> A

Imports do not download packages or resolve external registries. All imported modules must be local standalone sources or declared .blower project members.


← Previous Next →

Semantic Analysis

Furnace performs semantic analysis before code generation.

The semantic stage checks the parsed AST for invalid programs.

It currently handles checks including:

  • Undefined variables
  • Invalid function calls
  • Incorrect function argument counts
  • Invalid Main parameters
  • Invalid shared-member access
  • Forbidden shared-member mutation
  • Invalid Stop usage
  • Invalid Program.Stop() usage
  • Unknown collection methods
  • Array size mismatches
  • Tuple field count mismatches
  • List element type mismatches
  • Invalid empty-list declarations
  • Boolean type compatibility for Bool and Boolean variables
  • Other language-level errors

Semantic analysis happens before either code generation path.

This keeps invalid programs from being passed directly to the backend.

Parallel Semantic Analysis

Furnace uses Rayon for parallel semantic analysis.

Independent portions of the AST can be analyzed concurrently.

For example, independent function declarations can be processed concurrently.

This applies to compiler analysis only.

A Sydrogen program containing:

While (Condition)
{
    // work
}

still executes that loop on one thread unless future language features explicitly introduce parallel execution.


← Previous Next →

Compiler Architecture

Furnace is divided into several stages.

Parser

The parser uses pest, a PEG parser generator for Rust.

It converts Sydrogen source into the AST.

The grammar uses explicit precedence rules rather than left-recursive expression rules.

Operator precedence, from highest to lowest, is:

flowchart LR
    A(primary) --> B(postfix)
    B --> C(power)
    C --> D(unary)
    D --> E(multiplicative)
    E --> F(additive)
    F --> G(comparison)
    G --> H(and)
    H --> I(or)
    I --> J(xor)

A documented language rule is that unary operators bind looser than **.

Therefore:

-2 ** 2

is interpreted as:

-(2 ** 2)

which produces:

-4

AST

The AST is represented using Rust structures.

It acts as the representation shared between parsing, semantic analysis, and code generation.

The compiler works with the AST rather than passing raw source text between compiler stages.

Semantic Analysis

Before semantic analysis, project or standalone source loading builds a module table and import dependency graph. Use and Using are resolved into uniquely named declaration references, cycles and visibility errors are diagnosed, and the backends receive one already-resolved program without import statements.

src/semantic.rs validates the AST before code generation.

This stage handles language-level checks such as:

  • Type compatibility
  • Variable lookup
  • Function lookup
  • Function argument counts
  • Collection operations
  • Scope-related checks
  • Control-flow restrictions

Code Generation

Furnace has two code generation paths. The direct native path lowers supported programs to the internal representation in src/ir.rs, writes x86-64 instruction bytes, and wraps them as either an ELF64 Linux executable or a PE32+ Windows executable. The other path in src/codegen.rs converts programs that need typed features to Cranelift IR and produces an object file.

The paths share the AST and semantic analysis. The compiler selects the path after semantic analysis based on the types and statements used by the program. See Native Code Generation for the direct path.

Function Calls

The direct path writes each Sydrogen function as a separate block of machine code. Calls use the System V x86-64 argument registers, and return values use RAX.

The Cranelift path declares each Sydrogen function as an independent Cranelift function and emits calls from the caller.

Collection Layout

The current backend uses malloc for heap allocation of arrays, tuples, and lists.

Their layouts are:

Array (Ore)

OffsetContents
0Length
8Element size
16 onwardElement data

Each element currently occupies 8 bytes.

List (Materials)

OffsetContents
0Length
8Capacity
16Buffer pointer

Tuple (Ore with named fields)

Fields are stored starting at offset 0 in declaration order.

Each field currently occupies 8 bytes.

These layouts are implementation details of the current backend and may change in future compiler versions.

Linking

The direct native path creates its ELF64 or PE32+ executable itself, so it does not call a linker. Target selection is centralized in src/backend/mod.rs; format-specific layout stays in src/backend/elf.rs and src/backend/pe.rs.

The Cranelift path generates a native object file and uses the system C compiler as the linker.

The object file is linked using the system C compiler.

For example:

cc main.o -o main -lm

The linker produces the final executable.


← Previous Next →

Native Code Generation

Furnace has a direct native code path for a supported subset of Sydrogen. This path writes x86-64 instructions and creates either an ELF64 Linux executable or a PE32+ Windows executable without first creating an object file or calling an external linker.

The compiler chooses the path after parsing and semantic analysis:

flowchart TD
    A(Sydrogen source) --> B(Parser)
    B --> C(AST)
    C --> D(Semantic analysis)
    D --> E(Direct native path)
    E --> F(Internal IR)
    F --> G(x86-64 bytes)
    G --> H{Target format}
    H --> J(ELF64 executable)
    H --> K(PE32+ executable)
    D --> I(Cranelift path)
    I --> M(Object file)
    M --> N(cc)
    N --> O(Executable)

The direct path is used when a program does not need the types that still depend on the Cranelift path. It currently handles integer, Boolean, and Weld values, integer input, strings used by printing, arithmetic, comparisons, branches, loops, function calls, and Program.Stop().

Programs that use floats, arrays, tuples, lists, or other typed features are sent to the Cranelift path. That path remains available while the direct path grows.

1. Lowering the AST

src/lowering.rs converts the checked AST into the internal representation in src/ir.rs.

The internal representation contains:

  • virtual registers for temporary values
  • named stack variables
  • basic blocks with labels
  • arithmetic and comparison instructions
  • function calls and parameters
  • print and input instructions
  • jumps, conditional branches, and returns
  • typed error status, message, and type-name operations
  • an error-propagation terminator

The lowerer turns If, Switch, While, and For statements into basic blocks. Switch evaluates its selector once, emits ordered equality checks, and directs every completed Case to one shared exit block. Each branch gets a label, and each loop has a condition block and an exit block. At this stage the code still uses virtual registers and does not contain x86-64 instruction bytes.

2. Placing values

src/backend/x86_64/mod.rs assigns each virtual register to one of five callee-saved registers: RBX, R12, R13, R14, or R15.

If there are more live values than available registers, the allocator gives the extra values stack slots. Function variables also receive stack slots. Each function gets a stack frame with space for saved registers, spilled values, and variables.

Function arguments use the first six integer registers from the System V x86-64 calling convention:

RDI, RSI, RDX, RCX, R8, R9

Function results are returned in RAX. Native Sydrogen calls additionally use RDX as an error status and preserve the error message and type-name pointers in R8 and R9 while propagating. A zero status means success. This compact language-level ABI lets one shared propagation mechanism serve ELF64 and PE32+ without relying on platform exception facilities.

3. Writing instructions

src/backend/x86_64/encoder.rs writes instruction bytes into a Vec<u8>.

The encoder handles the instructions needed by the current direct path, including:

  • moving constants and values between registers and stack slots
  • integer addition, subtraction, multiplication, division, and remainder
  • bitwise operations
  • comparisons and Boolean results
  • calls and returns
  • conditional and unconditional jumps
  • function prologues and epilogues
  • Linux system calls used by the executable startup code

The encoder is specific to x86-64. It does not ask another compiler to produce these instructions.

4. Fixing addresses

Function calls and jumps may refer to code that has not been placed yet. Furnace writes a temporary four-byte relative offset and records its position.

After all functions have been written, Furnace knows every function and block offset. It then patches:

  • calls to Sydrogen functions
  • jumps between basic blocks
  • calls to printing, input, and exponentiation helpers
  • pointers to embedded string data

This lets functions call one another without requiring symbol tables or a separate linker for the direct path.

5. Building the executable file

Linux is the default target. src/backend/elf.rs writes:

  1. an ELF64 header
  2. one loadable program header
  3. a startup stub
  4. the generated function code and helper code
  5. string data used by the program

The startup stub is the _start entry point. It calls Main; on success it moves the return value into the Linux exit-status register, while an uncaught error is formatted as its type and message and exits with status 1.

For --windows, src/backend/pe.rs writes the DOS header, PE signature, AMD64 COFF header, PE32+ optional header, and an aligned executable .text section. Its entry stub reserves the Windows x64 shadow space, calls the same generated Main function, restores the stack, and returns to the Windows loader. The image uses 4096-byte section alignment and 512-byte file alignment.

The command-line compiler writes these bytes directly to the requested output file and marks the file executable. The direct path does not need cc.

6. The Cranelift path

The direct path is not used for every Sydrogen type. When src/backend/mod.rs finds a float, array, tuple, list, or another type that needs the typed path, it calls src/codegen.rs instead.

That path:

  1. converts the program to Cranelift IR
  2. writes a native object file
  3. writes the C runtime helpers
  4. calls cc to make the final executable

Both paths share parsing, the AST, and semantic analysis. The difference begins after the program has been checked.

7. Current limits

The direct native path currently has these general limits:

  • x86-64 instruction output only
  • at most six integer function arguments
  • integer, Boolean, and Weld function values
  • integer input only
  • no direct float, array, tuple, or list code generation
  • no direct Data or object code generation

Imports are resolved before backend selection, so imported functions work through the direct path without backend-specific import instructions. The other limits describe the direct path. A construct can be parsed and checked before code generation rejects it or sends it to the other path.

The Windows target has additional limits: Print, Input, and Program.Stop() still rely on Linux system calls, while floats, collections, Data, and objects use a Linux-only typed/linker fallback. Furnace rejects these constructs for Windows. Integer, Boolean, and Weld values, variables, arithmetic, comparisons, branches, loops, and direct Sydrogen function calls are supported. Integer and Boolean Switch statements use this same shared control-flow lowering on both executable formats.

Typed error dispatch, nested propagation, and Finally control flow are lowered into ordinary IR blocks and supported by the direct backend on both targets. Windows currently lacks the console runtime needed to print an uncaught top-level error. The Cranelift backend reports error handling as unsupported instead of generating incomplete behavior.

← Previous Next →

Code Generation

src/codegen.rs is the Cranelift code generation path. The direct native path is described in Native Code Generation.

Cranelift handles:

  • Instruction selection
  • Register allocation
  • Machine code generation
  • Target-specific code generation
  • Object-file generation

The Cranelift path is:

flowchart LR
    A(Sydrogen source) --> B(pest)
    B --> C(AST)
    C --> D(Module resolution)
    D --> E(Semantic analysis)
    E --> F(Cranelift)
    F --> G(Native object code)
    G --> H(System linker)
    H --> I(Executable)

Supported Constructs

Currently supported Cranelift code generation includes the subset of the language that can be lowered to an object file, such as:

  • variables and assignments
  • primitive arithmetic
  • loops and conditionals
  • native calls to user-defined functions
  • collection access for supported layouts

Unsupported and Planned Constructs

The backend does not currently generate executable code for:

  • Data declarations
  • object instantiation
  • full lexical scope handling

Parsed constructs that are not listed above may still be rejected during semantic analysis or code generation. Programs that use the direct x86-64 path do not pass through this chapter's Cranelift path.

Module imports are not a backend construct: Furnace resolves them before semantic analysis, and Cranelift receives ordinary uniquely named declarations and calls.

See Current Limitations for the full status matrix.


← Previous Next →

Linking

The Cranelift path generates a native object file. The direct native path creates an ELF64 or PE32+ executable and does not use this step. PE32+ cross-compilation therefore does not require a Windows linker or SDK.

The object file is linked using the system C compiler.

For example:

cc main.o -o main -lm

The linker produces the final executable.

This is the last stage of the Cranelift path. Once the object file is generated, it becomes a native binary after the system C compiler links it.

The currently supported target is linux.

Additional targets can be added as compiler support is implemented.


← Previous Next →

Types Reference

This page collects the core Sydrogen types and points to the detailed language sections.

TypeDescriptionDetails
NumberNumeric type categorySee Types
IntInteger valueSee Integers
FloatFloating-point valueSee Floating-Point Numbers
WeldString valueSee Strings
StringAlias of WeldSee Strings
Bool / BooleanBoolean valueSee Types
Ore[...]Fixed-size arraySee Types
Ore(...)Tuple with named fieldsSee Types
Materials TList typeSee Types
GenericGeneric list element typeSee Types

Notes

  • Bool and Boolean are treated as the same type.
  • Arrays use Ore with a fixed size.
  • Lists use Materials and support methods such as .Add(...), .Remove(...), and .RemoveAt(...).
  • The current implementation is still limited: generic storage is constrained, and data/object declarations are not yet executable in the backend.

← Previous Next →

Operators Reference

This is a compact reference for the operators described in the language chapters.

Arithmetic

OperatorMeaning
+Addition
-Subtraction
*Multiplication
/Division
%Modulo
**Exponentiation

Comparison

OperatorMeaning
<Less than
>Greater than
<=Less than or equal
>=Greater than or equal
==Equal
!=Not equal

Logical / Bitwise

OperatorMeaning
AndBitwise AND
OrBitwise OR
XorBitwise XOR

Unary

OperatorMeaning
-Negation
+Unary plus, a no-op
++Postfix increment
--Postfix decrement

See Operators, Unary Operators, Comparisons, and Logical Operators for the full discussion and caveats.


← Previous Next →

Keywords Reference

This page summarizes the keywords and reserved terms that appear in the current Sydrogen documentation.

KeywordMeaning
NunctionFunction that does not return a value
functionFunction with a dynamic return type
ReturnEnds a returning function and sends a value to its caller
OpenVisibility modifier
ClosedVisibility modifier
ShowcaseVisibility modifier currently parsed but not code-generated
NumberInternal category for numeric types
IntInteger type
FloatFloating-point type
WeldString type
StringAlias of the canonical Weld type
Bool / BooleanBoolean type
OreArray or tuple type indicator
MaterialsList type
GenericGeneric list element type
IfConditional statement
ElseConditional fallback
SwitchEvaluates one value and selects the first matching Case
CaseIntroduces one or more values in a Switch
BaseFinal fallback branch in a Switch
WhileWhile loop
ForFor loop
DoIntroduces a protected error-handling block
CatchHandles the first matching typed error
FinallyRuns cleanup after a protected block
ThrowConstructs and propagates a typed error
ErrorRoot of the built-in error hierarchy
StopStructured early exit
ProgramRuntime namespace
UseModule import path
UsingImported symbol from a module
DataStructured data declaration

A number of these keywords are present in the parser or AST, but not yet fully implemented in every backend. See Switch Statements, Error Handling, Current Limitations, and the language chapters for the current status.


← Previous Next →

Complete Example

The following program demonstrates several features available in Alpha-7:

  • Nunction
  • Variables
  • Arrays
  • Tuples
  • Lists
  • While
  • For
  • If
  • Else
  • Arithmetic
  • String interpolation
  • Stop
  • Bitwise operations
  • Zero-argument function calls
  • Boolean values
Nunction Tick()
{
    Print("tick");
}

Open Nunction Main()
{
    // Array
    Ore[3] FixedNums = [10, 20, 30,];

    Print(FixedNums[0]);

    FixedNums[1] = 55;

    Print(FixedNums[1]);
    Print(FixedNums.Length);

    // Tuple
    Ore(Int Age, Weld Name) Person = {14, "Den"};

    Print(Person.Age);
    Print(Person.Name);

    Person.Age = 15;

    Print(Person.Age);

    // List
    Materials Int Numbers = (10, 20, 30,);

    Print(Numbers[0]);
    Print(Numbers.Length);

    Numbers.Add(40);

    Print(Numbers[3]);

    Numbers.Remove(0);

    Print(Numbers[0]);

    // While loop
    Int I = 0;

    While (I < 5)
    {
        Print(\V"{I}");
        I = I + 1;
    }

    // For loop
    For (Int J = 0; J < 3; J++)
    {
        Print(\V"J = {J}");
    }

    // Boolean
    Bool Flag = true;
    Print(Flag);
    Flag = false;
    Print(Flag);

    // Stop inside a loop
    Int K = 0;

    While (K < 10)
    {
        If (K == 3)
        {
            Stop;
        }

        Print(\V"{K}");
        K = K + 1;
    }

    // Zero-argument Nunction call
    Tick();

    // Bitwise operations
    Int A = 12;
    Int B = 10;

    Print(A And B);
    Print(A Or B);
    Print(A Xor B);
}

This example represents the current Alpha-7 subset. Some features are still planned or parser-only, so the example shows what the compiler currently handles.


← Previous Next →

Current Limitations

Alpha-7 is an early development release.

The following features are not currently fully implemented in the backend:

  • Data declaration code generation
  • Object instantiation code generation
  • Deal remains reserved for possible future pattern-matching syntax; implemented Switch statements use Case and Base
  • Error handling in the Cranelift backend
  • Windows top-level uncaught-error printing
  • Finally handling for Stop and Skip
  • Package and dependency management
  • Complete lexical scope handling
  • Multicore Sydrogen program execution
  • Garbage collection
  • Self-hosting Furnace
  • Complete systems-level standard library

Some of these features are already represented in the grammar or AST.

There is an important distinction:

Parsed means Furnace can recognize the syntax.

Semantically checked means Furnace can inspect the construct and report certain errors.

Code generated means Furnace can produce executable native code for the construct.

A feature being parsed does not mean that it can currently be used in an executable program.

This distinction saves everyone from discovering that the compiler supports something only after the compiler politely refuses to compile it.


← Previous Next →

Alpha-7 Roadmap

Alpha-7 continues development of the Rust-based Furnace compiler.

Short Term

Switch Follow-up

Switch, Case, multi-value or patterns, and Base are implemented without fallthrough on both native executable formats and in Cranelift. Future pattern matching may assign a separate role to the reserved Deal keyword.

Error Handling Follow-up

Do, Catch, Finally, and Throw are implemented by the direct native backend. Follow-up work includes Cranelift support, Windows uncaught-error printing, automatic overflow and I/O conversion, and Finally handling for Stop and Skip.

Generic Data Types

Generic is currently limited.

Future versions are planned to support generic data types and generic function parameters.

Mid Term

Module Packaging

Sydrogen now has local and .blower project imports based around:

Use
Using

The implemented import system applies Open and Closed visibility. Future module work can extend standard-library and package integration and define the role of:

Showcase

visibility rules.

Additional Function Features

Parameterized functions and return-value functions are supported by the current native function call path.

Future work includes clearer diagnostics for control-flow paths that do not return and broader support for generic function types.

Multicore Runtime

Alpha-7 uses Rayon for compiler-side parallel analysis.

Future versions are planned to provide mechanisms for Sydrogen programs to execute work on multiple CPU cores.

Possible constructs include:

Spawn
Join

The exact syntax and safety rules are not final.

The compiler's parallel analysis and a program's parallel execution are separate features.

Long-Term Plans

Scrap

Scrap is planned as an optional garbage collector for Sydrogen.

It is intended to be disabled by default.

The default memory model is intended to keep memory management explicit.

Scrap would provide another memory-management option without making garbage collection mandatory.

Ironwork

Ironwork is planned as the Sydrogen package manager.

Its planned responsibilities include:

  • Package management
  • Dependency resolution
  • Library distribution
  • Project management
  • Sydrogen package integration

Self-Hosting

A long-term goal is to rewrite Furnace in Sydrogen itself.

This is targeted for the 2.0 generation of Sydrogen.

The compiler will need sufficient language features, standard library support, and tooling before this becomes practical.


← Previous Next →

Implementation Notes

Alpha-7 uses a different compiler implementation from the earlier experimental versions of Sydrogen.

Earlier versions used:

Alpha 1.x and 2.x used:

Python
ANTLR
LLVM

Alpha-7 uses:

Rust
pest
Direct x86-64 code path
Cranelift typed path

The current compiler pipeline is:

flowchart LR
    A(Sydrogen source) --> B(pest)
    B --> C(AST)
    C --> D(Semantic analysis)
    D --> E(Direct native path or Cranelift path)
    E --> F(Executable)

The compiler is written in Rust and produces native executables through the two paths described above.

Alpha-7 should not be treated as a finished language specification.

Some syntax exists before its backend implementation.

Some AST structures exist before their code generation.

Some planned language features are already represented in the parser even though the compiler cannot execute them yet.

That is normal for a compiler under active development.

For now, Furnace can compile a growing subset of Sydrogen to native code while the rest of the language catches up.


← Previous