1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Regression coverage for `mget m k ?? d` — one-line map-lookup with default.
//
// Older persona reports (lines 699, 705, 926 of ilo_assessment_feedback.md)
// claimed `mget m k??0` parsed as `mget(m, k??0)`, forcing a two-step
// `r=mget m k;v=r??0` bind. The arity-aware call parser landed in 06477c5
// fixed this — `mget` is registered with fixed arity 2, so the parser
// stops consuming args after the key and `??` falls out as infix
// nil-coalesce on the whole call result.
//
// These tests pin that behaviour across every engine (tree, VM,
// Cranelift) and across the variants personas actually wrote: literal
// key, variable key, path-access key, call-result key, parenthesised
// key, with both numeric and text defaults.
use std::process::Command;
fn ilo() -> Command {
Command::new(env!("CARGO_BIN_EXE_ilo"))
}
fn run_file(engine: &str, src: &str, entry: &str) -> String {
use std::sync::atomic::{AtomicU64, Ordering};
static COUNTER: AtomicU64 = AtomicU64::new(0);
let seq = COUNTER.fetch_add(1, Ordering::SeqCst);
let path = std::env::temp_dir().join(format!(
"ilo_mget_default_{}_{}.ilo",
std::process::id(),
seq
));
std::fs::write(&path, src).unwrap();
let out = ilo()
.args([path.to_str().unwrap(), engine, entry])
.output()
.expect("failed to run ilo");
assert!(
out.status.success(),
"ilo {engine} failed for `{src}`: stderr={}",
String::from_utf8_lossy(&out.stderr)
);
String::from_utf8_lossy(&out.stdout).trim().to_string()
}
// Literal key, value present — `(mget m "k") ?? 0` → 5.
const LITERAL_HIT: &str = r#"f>n;m=mset mmap "k" 5;mget m "k" ?? 0"#;
// Literal key, value missing — `(mget m "missing") ?? 99` → 99.
const LITERAL_MISS: &str = r#"f>n;m=mmap;mget m "missing" ?? 99"#;
// Variable key, value present.
const VARKEY_HIT: &str = r#"f>n;m=mset mmap "k" 5;k="k";mget m k ?? 0"#;
// Variable key, value missing (the exact shape from persona line 699).
const VARKEY_MISS: &str = r#"f>n;em=mmap;k="x";mget em k ?? 0"#;
// Text-typed default, text value present.
const TEXT_HIT: &str = r#"f>t;m=mset mmap "k" "hi";mget m "k" ?? "default""#;
// Text default reached on missing key — confirms `??` typing flows
// through `mget`'s `O T` return.
const TEXT_MISS: &str = r#"f>t;m=mmap;mget m "absent" ?? "default""#;
// Path-access key (`ks.0`) — confirms the parser doesn't greedily
// swallow `.0` or `?? 0` into the key expression.
const PATHKEY_HIT: &str = r#"f>n;m=mset mmap "k" 5;ks=["k"];mget m ks.0 ?? 0"#;
// Call-result key (`str 5`) — confirms the parser stops the key
// expression at mget's second argument boundary, not at `??`.
const CALLKEY_HIT: &str = r#"f>n;m=mset mmap "5" 7;mget m str 5 ?? 0"#;
// Parenthesised key — defensive lower bound on the precedence.
const PARENKEY_HIT: &str = r#"f>n;m=mset mmap "5" 7;mget m (str 5) ?? 0"#;
// Note: bare-chained `mget m "a" ?? mget m "b" ?? 99` does NOT parse
// today — the arity-aware call parser doesn't extend through a `??`
// boundary, so the second `mget` slurps `m "b" ?? 99` as three args.
// Bind each lookup into a temp first (`a=mget m "a";b=mget m "b";a??b??99`)
// or parenthesise. Logged as a separate adjacent finding.
fn check_all(engine: &str) {
assert_eq!(
run_file(engine, LITERAL_HIT, "f"),
"5",
"literal hit engine={engine}"
);
assert_eq!(
run_file(engine, LITERAL_MISS, "f"),
"99",
"literal miss engine={engine}"
);
assert_eq!(
run_file(engine, VARKEY_HIT, "f"),
"5",
"varkey hit engine={engine}"
);
assert_eq!(
run_file(engine, VARKEY_MISS, "f"),
"0",
"varkey miss engine={engine}"
);
assert_eq!(
run_file(engine, TEXT_HIT, "f"),
"hi",
"text hit engine={engine}"
);
assert_eq!(
run_file(engine, TEXT_MISS, "f"),
"default",
"text miss engine={engine}"
);
assert_eq!(
run_file(engine, PATHKEY_HIT, "f"),
"5",
"pathkey hit engine={engine}"
);
assert_eq!(
run_file(engine, CALLKEY_HIT, "f"),
"7",
"callkey hit engine={engine}"
);
assert_eq!(
run_file(engine, PARENKEY_HIT, "f"),
"7",
"parenkey hit engine={engine}"
);
}
#[test]
fn mget_default_tree() {
check_all("--run-tree");
}
#[test]
fn mget_default_vm() {
check_all("--run-vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn mget_default_cranelift() {
check_all("--run-cranelift");
}