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 $ message2.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 // 302.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 | _ | -12.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)