Overview

AetherShell is a functional-first, typed shell language with AST-based evaluation and Hindley-Milner type inference. Every construct is an expression that returns a value.

Key Principle: In AetherShell, data flows as structured Value types (Int, Float, String, Array, Record, Lambda), not raw text.

Literals

Numbers

Type Syntax Examples
Integer digits 42, 0, -123
Float digits.digits 3.14, 0.5, -2.718

Strings

Form Syntax Example
Plain "text" "Hello, world!"
Interpolated "text ${expr} text" "Result: ${x + 1}"
Escapes \n \t \\ \" "Line 1\nLine 2"

Booleans & Null

true      // Boolean true
false     // Boolean false
null      // Null value

Arrays

[]                    // Empty array
[1, 2, 3]            // Integer array
["a", "b", "c"]      // String array
[1, "two", 3.0]      // Heterogeneous array
[[1,2], [3,4]]       // Nested arrays

Records (Objects)

{}                           // Empty record
{x: 10}                      // Single field
{name: "Alice", age: 30}     // Multiple fields
{x: 1, nested: {y: 2}}       // Nested records

Variables

Immutable Bindings

x = 10           // Immutable - cannot reassign
name = "Alice"   // Immutable string
list = [1,2,3]   // Immutable reference

// x = 20  // ERROR: Cannot reassign immutable binding

Mutable Bindings

y := 10          // Mutable - can reassign
y = 20           // OK
y = y + 5        // OK

counter := 0
counter = counter + 1  // OK
Important: Use = for immutable (default), := for mutable bindings.

Operators

Arithmetic Operators

Operator Description Example Result
+ Addition 10 + 5 15
- Subtraction 10 - 5 5
* Multiplication 10 * 5 50
/ Division 10 / 5 2
% Modulo 10 % 3 1

Comparison Operators

Operator Description Example Result
== Equal 10 == 10 true
!= Not equal 10 != 5 true
< Less than 5 < 10 true
> Greater than 10 > 5 true
<= Less or equal 10 <= 10 true
>= Greater or equal 10 >= 10 true

Logical Operators

Operator Description Example Result
&& Logical AND true && false false
|| Logical OR true || false true
! Logical NOT !true false

String Operators

// Concatenation
"Hello" + " " + "World"  // "Hello World"

// Interpolation
name = "Alice"
"Hello, ${name}!"        // "Hello, Alice!"

Functions (Lambdas)

Lambda Syntax

// General form: fn(params) => expression

// No parameters
get_pi = fn() => 3.14159

// Single parameter
double = fn(x) => x * 2

// Multiple parameters
add = fn(a, b) => a + b
multiply = fn(x, y, z) => x * y * z

// Block body
complex = fn(x) => {
    temp = x * 2
    result = temp + 10
    result
}

Function Application

// Direct call
double = fn(x) => x * 2
result = double(5)  // 10

// In pipeline
[1,2,3] | map(fn(x) => x * 2)

// As argument
apply = fn(f, x) => f(x)
apply(double, 5)  // 10

Closures

// Functions capture outer scope
make_adder = fn(n) => fn(x) => x + n

add5 = make_adder(5)
add5(3)   // 8
add5(10)  // 15

Pipeline Operator

Basic Pipeline

// Syntax: expr | function
value | print
[1,2,3] | len

// Chaining
[1,2,3] | map(fn(x) => x * 2) | print

Multi-line Pipelines

[1, 2, 3, 4, 5, 6]
| where(fn(x) => x % 2 == 0)    // [2, 4, 6]
| map(fn(x) => x * x)            // [4, 16, 36]
| reduce(fn(a, b) => a + b, 0)  // 56
| print

Pipeline Context

The value on the left is passed as the first argument to the function on the right.

// These are equivalent:
[1,2,3] | map(fn(x) => x * 2)
map([1,2,3], fn(x) => x * 2)

// Explicit vs piped
len([1,2,3])      // Direct call
[1,2,3] | len     // Piped call

Control Flow

If Expression

// Syntax: if condition { expr } else { expr }

x = 10
result = if x > 5 {
    "large"
} else {
    "small"
}
// result = "large"

// Nested if
status = if x > 10 {
    "high"
} else if x > 5 {
    "medium"
} else {
    "low"
}

Match Expression

// Pattern matching
value = 2
result = match value {
    1 => "one",
    2 => "two",
    3 => "three",
    _ => "other"
}
// result = "two"

// Match with guards (future feature)
// match x {
//     n if n > 0 => "positive",
//     0 => "zero",
//     _ => "negative"
// }

While Loop

// Syntax: while condition { statements }

i := 0
sum := 0
while (i < 10) {
    sum = sum + i
    i = i + 1
}
// sum = 45
Note: Prefer functional approaches (map, reduce, filter) over loops when possible.

Record Access

Field Access

person = {name: "Alice", age: 30}

// Dot notation
person.name  // "Alice"
person.age   // 30

// Nested access
data = {user: {name: "Bob", id: 123}}
data.user.name  // "Bob"

Array Indexing

arr = [10, 20, 30, 40]

// Zero-indexed
arr[0]   // 10
arr[2]   // 30
arr[-1]  // 40 (last element)

// Nested
matrix = [[1,2], [3,4]]
matrix[0][1]  // 2

Comments

// Single-line comment

x = 10  // Inline comment

// Multi-line comments:
// Line 1
// Line 2
// Line 3

/* Block comments (if supported)
   Multi-line
   Comment block
*/

Blocks & Scope

Block Expressions

// Block evaluates to last expression
result = {
    x = 10
    y = 20
    x + y
}
// result = 30

Lexical Scoping

x = 10
{
    x = 20  // Shadows outer x
    print(x)  // 20
}
print(x)  // Still 10 (outer scope unchanged with =)

Mutable Scoping

x := 10
{
    x = 20  // Modifies outer x (mutable)
    print(x)  // 20
}
print(x)  // 20 (outer x was modified)

Type Annotations (Future)

Note: Type inference is automatic. Explicit annotations may be added in future versions.
// Current (inferred)
add = fn(a, b) => a + b

// Future possibility (annotated)
// add: (Int, Int) => Int = fn(a, b) => a + b
// name: Str = "Alice"
// items: [Int] = [1, 2, 3]

Reserved Keywords

Keyword Usage
fn Lambda definition
if Conditional
else Alternative branch
match Pattern matching
while Loop
true Boolean literal
false Boolean literal
null Null literal

Grammar Summary (EBNF-style)

Program    = Statement*
Statement  = VarDecl | Expression

VarDecl    = Ident "=" Expression          // Immutable
           | Ident ":=" Expression         // Mutable

Expression = Literal
           | Ident
           | Lambda
           | Pipeline
           | BinaryOp
           | UnaryOp
           | IfExpr
           | MatchExpr
           | WhileExpr
           | Block
           | Call
           | FieldAccess
           | IndexAccess

Literal    = Int | Float | String | Bool | Null | Array | Record

Lambda     = "fn" "(" Params? ")" "=>" (Expression | Block)

Pipeline   = Expression "|" Expression

BinaryOp   = Expression Operator Expression
Operator   = "+" | "-" | "*" | "/" | "%" | "==" | "!=" | "<" | ">" | "<=" | ">=" | "&&" | "||"

UnaryOp    = "!" Expression | "-" Expression

IfExpr     = "if" Expression Block ("else" (Block | IfExpr))?

MatchExpr  = "match" Expression "{" MatchArm+ "}"
MatchArm   = Pattern "=>" Expression ","

WhileExpr  = "while" "(" Expression ")" Block

Block      = "{" Statement* Expression? "}"

Call       = Expression "(" Args? ")"

FieldAccess = Expression "." Ident
IndexAccess = Expression "[" Expression "]"

Operator Precedence

Precedence Operator Associativity
1 (Highest) . [] () Left
2 ! - (unary) Right
3 * / % Left
4 + - Left
5 < > <= >= Left
6 == != Left
7 && Left
8 || Left
9 | (pipeline) Left
10 (Lowest) = := Right

Quick Reference Card

Essential Syntax

// Variables
x = 10              // Immutable
y := 20             // Mutable

// Functions
fn(x) => x * 2
fn(a, b) => a + b

// Pipelines
[1,2,3] | map(fn(x) => x * 2)

// Arrays & Records
[1, 2, 3]
{name: "Alice", age: 30}

// Control flow
if x > 0 { "pos" } else { "neg" }
match x { 1 => "one", _ => "other" }
while (i < 10) { i = i + 1 }

// Operators
+ - * / %           // Arithmetic
== != < > <= >=     // Comparison
&& || !             // Logical
|                   // Pipeline
Next: Check out Built-in Functions for all available operations, or Type System to understand type inference.