Skip to main content

2  Quickstart, Build & Tour

2.1 Host requirements and compiler build

Edva targets Linux hosts and cross-compiles to AArch64 and WebAssembly (wasm32). Building the compiler requires libLLVM-18, clang, and Nushell (nu) on PATH.

Task Command Behavior
Build compiler make edva Incrementally builds ./edva (bootstraps from seed/ir/ via clang if ./edva is absent)
Full build + test suite make check Verifies generated tables, builds ./edva, and runs the complete test suite
2-stage self-host verify make fixed-point Compiles Stage 1 and Stage 2 IR, asserts byte-identical fixed point, and promotes edva_stage1 to ./edva
Compile and run ./edva main.dva -r Compiles main.dva to a temporary binary, executes it immediately, and removes the binary
Compile executable ./edva main.dva -o app Compiles and links main.dva into ./app
Cross-compile ./edva main.dva -t aarch64-linux-gnu Emits code and links for the specified LLVM target triple
#use io

message = 10 > 5 | "greater" | "smaller"
io::out $ message

2.2 Conventional keyword to Edva symbolic mapping

Edva has no reserved keywords. Every control-flow, declaration, and error-handling construct is expressed through operators and # directives:

Construct Conventional Keyword Edva Syntax Example
Immutable binding let / const = x = 42
Rebinding / local mutation let mut / var Trailing prime ' x' = 0; x' = x' + 1
Mutable module global static mut :: prefix ::count = 0
Function signature fn f(a: T) -> R : and => add: Int, Int => Int
Function definition fn / def / lambda => (or !=> for 0-arg) add = a, b => a + b
Conditional branch if ... else Choice | x > 0 | "pos" | "non-pos"
Pattern match / switch match / switch Guarded choice [...] x [0] "zero" [> 0] "pos" | "neg"
Loop / iteration for / while Cycle @ 0..10 @ i => io::out(i)
Loop break break / break 'lbl --> / -->N done | --> / done | -->2
Loop continue continue --^ / --^N skip | --^
Loop value escape loop { break val; } >-- (returns <T|>) xs @ x => ok(x) | --> >-- x
Function return return val; / return; Tail expr or --| --| 0 / bare --|
Ram unit early return if c { return false; } |-> |+> |--> |++> x < 0 |--> (returns <-->)
Boolean true / false true / false <+> / <-> (type <>) active = <+>
Option / Result values Some(v) / None / Ok / Err <+ v +> / <--> / <+ v -> / <- e +> res = <+ 42 ->
Unwrap or abort .unwrap() Postfix !! val = res!!
Unwrap or propagate ? (Rust) Postfix --! or $>> fd = open(p)--!
Type definition type / struct / enum #type #type Point (x: Int, y: Int)
Module import / export import / pub / extern #use / #public / #foreign / #export #use io

2.3 Language tour by subsystem

2.3.1 1. Bindings, primes, and separate signatures

Bindings created with = are immutable (E3033 on reassignment). Step-by-step updates or mutable accumulators append prime marks ('). Function parameter types are written on a separate signature line above the definition; unannotated functions infer types via static unification without ever defaulting unknown types to Int.

#use io

greet: String, Int => String
greet = name, count =>
   total' = count
   total'' = total' * 2
   total'' > 0 | "Hello, ${name} (${total''})" | "Nobody"

io::out $ greet("Alice", 3)

2.3.2 2. Pipelines and application ($, $>, $>>)

Operator Associativity Tier Lowering
f(a, b) Left Postfix (LBP 170) Direct call (no whitespace before (, E2032)
f! Left Postfix (LBP 170) Invokes 0-argument function defined with !=>
x $> f(y) Left Feed (LBP 40) Prepends x as first argument: f(x, y)
f(x) $ y Right Apply (LBP 30) Appends y as final argument: f(x, y)
m $>> cont Left Feed (LBP 40) Unpacks positive ram payload into ram-returning cont
#use io

add = a, b => a + b
mul = a, b => a * b

io::out $ 5 $> add(3) $> mul(2)   // 16
io::out $ add(10) $ 20            // 30

2.3.3 3. Choices and cycles (|, [...], @, -->, --^, >--)

Choices evaluate guarded branches [...] in order, with | providing the fallback or positional ram branch. Cycles iterate over counts, ranges, conditions, or collections with @ and can yield < T | > via >--.

#use io

classify: Int => String
classify =
   [< 0]     "negative"
   [0]       "zero"
   [1..4]    "small"
   |         "large"

io::out $ classify(2)

found = 50..100 @ n =>
   n == 56 | -->
>--
   n

io::out $ found | _ | -1

2.3.4 4. Strings and Builder ({cap}, +=, +b)

String values are immutable { len, data } descriptors. Builder ({cap}) accumulates bytes, runes, strings, and interpolated blocks in place before freezing into a String in \(O(1)\) with unary +:

Operator Syntax Semantics
{cap} b = {64} Allocate mutable Builder with capacity cap
+= b += "text" / b += 'λ' / b += 65 Append string, UTF-8 rune, byte, or tuple (a, b) in place
+ (unary) s = +b Freeze Builder into String in \(O(1)\) and reset b
? / ?? ?s, ??s, ?b Byte length (?) or UTF-8 codepoint count (??)
& (unary) &s, &b Raw buffer Addr pointer (&s is guaranteed NUL-terminated)
#use io

b = {64}
b += ("Welcome, ", "Edva", '!')
s = +b
io::out $ "${s} (${?s} bytes)"

2.3.5 5. Composite types, containers, and ramifications

Shape Type Syntax Construction Access / Matching
Record / Tuple #type R (x: Int, y: Int) R(10, 20) or (10, 20) r.x, r.0 (definite init enforced, E3130)
Growable Array #type A Int{} xs = A(); xs += 10 xs(i) \(\to\) < Int | Error >; ?xs length
Slice Int[] view = xs(0..2) view(i) \(\to\) Int; non-owning { len, data }
Map #type M String ^ Int m = M(); m ^ "k" = 1 m ^ "k" \(\to\) < Int | Error >; m ^? "k" \(\to\) <>
Ramification <>, <T|>, <|E>, <T|E> <+>, <+ v +>, <+ v -> r | pos | neg, r!!, r--!
Enum #type E < A(Int), B > E(A, 5), E(B) / B e [A x] x [B] 0 (exhaustiveness enforced, E3102)
#use io

#type Shape < Circle(Float), Point >
#type Ints Int{}
#type Config String ^ Int

area: Shape => Float
area = [Circle r] 3.14159 * r * r [Point] 0.0

xs = Ints()
xs += 10
xs += 20

cfg = Config()
cfg ^ "port" = 8080

io::out $ area(Shape(Circle, 2.0))
io::out $ xs(0)!! + (cfg ^ "port" | _ | 0)