Skip to main content

hara_native/
lang_harness.rs

1//! Process-local host values for the Hara-owned `std.lang` compiler pipeline.
2//!
3//! The host stores identity, immutable configuration maps, and reversible lifecycle state.
4//! It never runs grammar callbacks or emits code; those responsibilities remain in `lang.*` HAL.
5
6use crate::core::{map_entries, native_variadic_function, ExtensionValue, Value};
7use crate::lang::data::{Keyword, Map as PMap, Vector as PVector};
8use std::cell::RefCell;
9use std::collections::HashMap;
10
11const PROVIDER: &str = "std.lang";
12
13#[derive(Clone)]
14enum SnapshotKind {
15    Library,
16    Harness,
17}
18
19#[derive(Clone)]
20enum Record {
21    Immutable {
22        type_name: String,
23        data: Value,
24    },
25    Library {
26        config: Value,
27        books: HashMap<String, Value>,
28        revision: i64,
29    },
30    Snapshot {
31        kind: SnapshotKind,
32        owner: u64,
33        books: HashMap<String, Value>,
34        library_revision: i64,
35        runtime_closed: bool,
36        runtime_revision: i64,
37        harness_closed: bool,
38    },
39    Runtime {
40        config: Value,
41        closed: bool,
42        revision: i64,
43    },
44    Harness {
45        config: Value,
46        library: u64,
47        runtime: u64,
48        closed: bool,
49    },
50}
51
52#[derive(Default)]
53struct State {
54    next: u64,
55    records: HashMap<u64, Record>,
56}
57
58thread_local! {
59    static STATE: RefCell<State> = RefCell::new(State::default());
60}
61
62/// Returns one directly callable host function for an annotated `std.lang` method.
63pub fn function(type_name: &str, method: &str) -> Result<Value, String> {
64    let type_name = type_name.to_owned();
65    let method = method.to_owned();
66    let display = format!("std.lang.{type_name}/{method}");
67    Ok(native_variadic_function(&display, move |values| {
68        invoke(&type_name, &method, values)
69    }))
70}
71
72fn invoke(type_name: &str, method: &str, values: Vec<Value>) -> Result<Value, String> {
73    match type_name {
74        "BookMeta" | "BookEntry" | "BookModule" | "Book" | "Compilation" => {
75            immutable(type_name, method, values, "data")
76        }
77        "Compiler" => immutable(type_name, method, values, "config"),
78        "Library" => library(method, values),
79        "Snapshot" => snapshot(method, values),
80        "Runtime" => runtime(method, values),
81        "Harness" => harness(method, values),
82        _ => Err(failure(type_name, method, "is not installed")),
83    }
84}
85
86fn immutable(
87    type_name: &str,
88    method: &str,
89    values: Vec<Value>,
90    accessor: &str,
91) -> Result<Value, String> {
92    match method {
93        "create" => {
94            require_arity(type_name, method, &values, 1)?;
95            let data = config(&values[0], &format!("{type_name}/create"))?;
96            Ok(allocate(Record::Immutable {
97                type_name: type_name.into(),
98                data,
99            }, type_name))
100        }
101        value if value == accessor => {
102            require_arity(type_name, method, &values, 1)?;
103            let handle = handle(&values[0], type_name, method)?;
104            STATE.with(|state| match state.borrow().records.get(&handle) {
105                Some(Record::Immutable { type_name: actual, data }) if actual == type_name => {
106                    Ok(data.clone())
107                }
108                _ => Err(failure(type_name, method, "expects its matching std.lang value")),
109            })
110        }
111        _ => Err(failure(type_name, method, "is not installed")),
112    }
113}
114
115fn library(method: &str, values: Vec<Value>) -> Result<Value, String> {
116    match method {
117        "create" => {
118            require_arity("Library", method, &values, 1)?;
119            Ok(allocate(
120                Record::Library {
121                    config: config(&values[0], "Library/create")?,
122                    books: HashMap::new(),
123                    revision: 0,
124                },
125                "Library",
126            ))
127        }
128        "config" => {
129            require_arity("Library", method, &values, 1)?;
130            let handle = handle(&values[0], "Library", method)?;
131            with_library(handle, method, |config, _, _| Ok(config.clone()))
132        }
133        "install" => {
134            require_arity("Library", method, &values, 2)?;
135            let library = handle(&values[0], "Library", method)?;
136            let book = handle(&values[1], "Book", method)?;
137            let key = immutable_data(book, "Book", method).and_then(|data| book_key(&data, method))?;
138            STATE.with(|state| {
139                let mut state = state.borrow_mut();
140                let Some(Record::Library { books, revision, .. }) = state.records.get_mut(&library) else {
141                    return Err(failure("Library", method, "expects a std.lang.Library value"));
142                };
143                books.insert(key, values[1].clone());
144                *revision += 1;
145                Ok(values[0].clone())
146            })
147        }
148        "remove" => {
149            require_arity("Library", method, &values, 2)?;
150            let library = handle(&values[0], "Library", method)?;
151            let key = book_key_argument(&values[1], method)?;
152            STATE.with(|state| {
153                let mut state = state.borrow_mut();
154                let Some(Record::Library { books, revision, .. }) = state.records.get_mut(&library) else {
155                    return Err(failure("Library", method, "expects a std.lang.Library value"));
156                };
157                let removed = books.remove(&key).unwrap_or(Value::Nil);
158                if !matches!(removed, Value::Nil) {
159                    *revision += 1;
160                }
161                Ok(removed)
162            })
163        }
164        "resolve" => {
165            require_arity("Library", method, &values, 2)?;
166            let library = handle(&values[0], "Library", method)?;
167            let key = book_key_argument(&values[1], method)?;
168            with_library(library, method, |_, books, _| {
169                Ok(books.get(&key).cloned().unwrap_or(Value::Nil))
170            })
171        }
172        "books" => {
173            require_arity("Library", method, &values, 1)?;
174            let library = handle(&values[0], "Library", method)?;
175            with_library(library, method, |_, books, _| {
176                Ok(Value::Vector(PVector::from_iter(books.values().cloned())))
177            })
178        }
179        "snapshot" => {
180            require_arity("Library", method, &values, 1)?;
181            let library = handle(&values[0], "Library", method)?;
182            let (books, revision) = with_library(library, method, |_, books, revision| {
183                Ok((books.clone(), *revision))
184            })?;
185            Ok(allocate(
186                Record::Snapshot {
187                    kind: SnapshotKind::Library,
188                    owner: library,
189                    books,
190                    library_revision: revision,
191                    runtime_closed: false,
192                    runtime_revision: 0,
193                    harness_closed: false,
194                },
195                "Snapshot",
196            ))
197        }
198        "restore" => {
199            require_arity("Library", method, &values, 2)?;
200            let library = handle(&values[0], "Library", method)?;
201            let snapshot = handle(&values[1], "Snapshot", method)?;
202            let (books, revision) = snapshot_library(snapshot, library, method)?;
203            STATE.with(|state| {
204                let mut state = state.borrow_mut();
205                let Some(Record::Library { books: target, revision: target_revision, .. }) = state.records.get_mut(&library) else {
206                    return Err(failure("Library", method, "expects a std.lang.Library value"));
207                };
208                *target = books;
209                *target_revision = revision;
210                Ok(values[0].clone())
211            })
212        }
213        "reset" => {
214            require_arity("Library", method, &values, 1)?;
215            let library = handle(&values[0], "Library", method)?;
216            STATE.with(|state| {
217                let mut state = state.borrow_mut();
218                let Some(Record::Library { books, revision, .. }) = state.records.get_mut(&library) else {
219                    return Err(failure("Library", method, "expects a std.lang.Library value"));
220                };
221                books.clear();
222                *revision = 0;
223                Ok(values[0].clone())
224            })
225        }
226        "state" => {
227            require_arity("Library", method, &values, 1)?;
228            let library = handle(&values[0], "Library", method)?;
229            with_library(library, method, |_, books, revision| {
230                Ok(map(vec![
231                    ("revision", Value::Number(*revision)),
232                    ("book-count", Value::Number(books.len() as i64)),
233                    ("books", Value::Vector(PVector::from_iter(books.values().cloned()))),
234                ]))
235            })
236        }
237        _ => Err(failure("Library", method, "is not installed")),
238    }
239}
240
241fn snapshot(method: &str, values: Vec<Value>) -> Result<Value, String> {
242    if method != "data" {
243        return Err(failure("Snapshot", method, "is not installed"));
244    }
245    require_arity("Snapshot", method, &values, 1)?;
246    let snapshot = handle(&values[0], "Snapshot", method)?;
247    STATE.with(|state| match state.borrow().records.get(&snapshot) {
248        Some(Record::Snapshot { kind, books, library_revision, runtime_closed, runtime_revision, harness_closed, .. }) => Ok(map(vec![
249            ("kind", Value::Keyword(Keyword::from(match kind { SnapshotKind::Library => "library", SnapshotKind::Harness => "harness" }))),
250            ("library-revision", Value::Number(*library_revision)),
251            ("books", Value::Vector(PVector::from_iter(books.values().cloned()))),
252            ("runtime-closed?", Value::Bool(*runtime_closed)),
253            ("runtime-revision", Value::Number(*runtime_revision)),
254            ("harness-closed?", Value::Bool(*harness_closed)),
255        ])),
256        _ => Err(failure("Snapshot", method, "expects a std.lang.Snapshot value")),
257    })
258}
259
260fn runtime(method: &str, values: Vec<Value>) -> Result<Value, String> {
261    match method {
262        "create" => {
263            require_arity("Runtime", method, &values, 1)?;
264            Ok(allocate(Record::Runtime { config: config(&values[0], "Runtime/create")?, closed: false, revision: 0 }, "Runtime"))
265        }
266        "config" => runtime_config(method, values),
267        "state" => runtime_state(method, values),
268        "reset" => runtime_mutate(method, values, false, true),
269        "close" => runtime_mutate(method, values, true, false),
270        "closed?" => {
271            require_arity("Runtime", method, &values, 1)?;
272            let runtime = handle(&values[0], "Runtime", method)?;
273            with_runtime(runtime, method, |_, closed, _| Ok(Value::Bool(*closed)))
274        }
275        _ => Err(failure("Runtime", method, "is not installed")),
276    }
277}
278
279fn runtime_config(method: &str, values: Vec<Value>) -> Result<Value, String> {
280    require_arity("Runtime", method, &values, 1)?;
281    let runtime = handle(&values[0], "Runtime", method)?;
282    with_runtime(runtime, method, |config, _, _| Ok(config.clone()))
283}
284
285fn runtime_state(method: &str, values: Vec<Value>) -> Result<Value, String> {
286    require_arity("Runtime", method, &values, 1)?;
287    let runtime = handle(&values[0], "Runtime", method)?;
288    with_runtime(runtime, method, |_, closed, revision| {
289        Ok(runtime_state_value(*closed, *revision))
290    })
291}
292
293fn runtime_mutate(method: &str, values: Vec<Value>, close: bool, reset: bool) -> Result<Value, String> {
294    require_arity("Runtime", method, &values, 1)?;
295    let runtime = handle(&values[0], "Runtime", method)?;
296    STATE.with(|state| {
297        let mut state = state.borrow_mut();
298        let Some(Record::Runtime { closed, revision, .. }) = state.records.get_mut(&runtime) else {
299            return Err(failure("Runtime", method, "expects a std.lang.Runtime value"));
300        };
301        if close { *closed = true; }
302        if reset { *closed = false; *revision = 0; }
303        Ok(values[0].clone())
304    })
305}
306
307fn harness(method: &str, values: Vec<Value>) -> Result<Value, String> {
308    match method {
309        "create" => {
310            require_arity("Harness", method, &values, 1)?;
311            let config = config(&values[0], "Harness/create")?;
312            let library = optional_handle(&config, "library", "Library", method)?
313                .unwrap_or_else(|| allocate(Record::Library { config: empty_map(), books: HashMap::new(), revision: 0 }, "Library").extension_handle().unwrap());
314            let runtime = optional_handle(&config, "runtime", "Runtime", method)?
315                .unwrap_or_else(|| allocate(Record::Runtime { config: empty_map(), closed: false, revision: 0 }, "Runtime").extension_handle().unwrap());
316            Ok(allocate(Record::Harness { config, library, runtime, closed: false }, "Harness"))
317        }
318        "config" => {
319            require_arity("Harness", method, &values, 1)?;
320            let harness = handle(&values[0], "Harness", method)?;
321            with_harness(harness, method, |config, _, _, _| Ok(config.clone()))
322        }
323        "library" => {
324            require_arity("Harness", method, &values, 1)?;
325            let harness = handle(&values[0], "Harness", method)?;
326            with_harness(harness, method, |_, library, _, _| Ok(extension("Library", *library)))
327        }
328        "runtime" => {
329            require_arity("Harness", method, &values, 1)?;
330            let harness = handle(&values[0], "Harness", method)?;
331            with_harness(harness, method, |_, _, runtime, _| Ok(extension("Runtime", *runtime)))
332        }
333        "snapshot" => harness_snapshot(method, values),
334        "restore" => harness_restore(method, values),
335        "reset" => harness_reset(method, values),
336        "close" => harness_close(method, values),
337        "closed?" => {
338            require_arity("Harness", method, &values, 1)?;
339            let harness = handle(&values[0], "Harness", method)?;
340            with_harness(harness, method, |_, _, _, closed| Ok(Value::Bool(*closed)))
341        }
342        "state" => harness_state(method, values),
343        _ => Err(failure("Harness", method, "is not installed")),
344    }
345}
346
347fn harness_snapshot(method: &str, values: Vec<Value>) -> Result<Value, String> {
348    require_arity("Harness", method, &values, 1)?;
349    let harness = handle(&values[0], "Harness", method)?;
350    let (_, library, runtime, closed) = with_harness(harness, method, |config, library, runtime, closed| Ok((config.clone(), *library, *runtime, *closed)))?;
351    let (books, library_revision) = with_library(library, method, |_, books, revision| Ok((books.clone(), *revision)))?;
352    let (_, runtime_closed, runtime_revision) = with_runtime(runtime, method, |config, closed, revision| Ok((config.clone(), *closed, *revision)))?;
353    Ok(allocate(Record::Snapshot { kind: SnapshotKind::Harness, owner: harness, books, library_revision, runtime_closed, runtime_revision, harness_closed: closed }, "Snapshot"))
354}
355
356fn harness_restore(method: &str, values: Vec<Value>) -> Result<Value, String> {
357    require_arity("Harness", method, &values, 2)?;
358    let harness = handle(&values[0], "Harness", method)?;
359    let snapshot = handle(&values[1], "Snapshot", method)?;
360    let (books, library_revision, runtime_closed, runtime_revision, harness_closed) = snapshot_harness(snapshot, harness, method)?;
361    let (_, library, runtime, _) = with_harness(harness, method, |config, library, runtime, closed| Ok((config.clone(), *library, *runtime, *closed)))?;
362    restore_library(library, books, library_revision, method)?;
363    restore_runtime(runtime, runtime_closed, runtime_revision, method)?;
364    STATE.with(|state| match state.borrow_mut().records.get_mut(&harness) {
365        Some(Record::Harness { closed, .. }) => { *closed = harness_closed; Ok(values[0].clone()) }
366        _ => Err(failure("Harness", method, "expects a std.lang.Harness value")),
367    })
368}
369
370fn harness_reset(method: &str, values: Vec<Value>) -> Result<Value, String> {
371    require_arity("Harness", method, &values, 1)?;
372    let harness = handle(&values[0], "Harness", method)?;
373    let (_, library, runtime, _) = with_harness(harness, method, |config, library, runtime, closed| Ok((config.clone(), *library, *runtime, *closed)))?;
374    restore_library(library, HashMap::new(), 0, method)?;
375    restore_runtime(runtime, false, 0, method)?;
376    STATE.with(|state| match state.borrow_mut().records.get_mut(&harness) {
377        Some(Record::Harness { closed, .. }) => { *closed = false; Ok(values[0].clone()) }
378        _ => Err(failure("Harness", method, "expects a std.lang.Harness value")),
379    })
380}
381
382fn harness_close(method: &str, values: Vec<Value>) -> Result<Value, String> {
383    require_arity("Harness", method, &values, 1)?;
384    let harness = handle(&values[0], "Harness", method)?;
385    let (_, _, runtime, _) = with_harness(harness, method, |config, library, runtime, closed| Ok((config.clone(), *library, *runtime, *closed)))?;
386    restore_runtime(runtime, true, runtime_revision(runtime, method)?, method)?;
387    STATE.with(|state| match state.borrow_mut().records.get_mut(&harness) {
388        Some(Record::Harness { closed, .. }) => { *closed = true; Ok(values[0].clone()) }
389        _ => Err(failure("Harness", method, "expects a std.lang.Harness value")),
390    })
391}
392
393fn harness_state(method: &str, values: Vec<Value>) -> Result<Value, String> {
394    require_arity("Harness", method, &values, 1)?;
395    let harness = handle(&values[0], "Harness", method)?;
396    let (_, library, runtime, closed) = with_harness(harness, method, |config, library, runtime, closed| Ok((config.clone(), *library, *runtime, *closed)))?;
397    let library_value = with_library(library, method, |_, books, revision| Ok(map(vec![("revision", Value::Number(*revision)), ("book-count", Value::Number(books.len() as i64)), ("books", Value::Vector(PVector::from_iter(books.values().cloned())))])))?;
398    let runtime_value = with_runtime(runtime, method, |_, runtime_closed, revision| Ok(runtime_state_value(*runtime_closed, *revision)))?;
399    Ok(map(vec![("state", Value::Keyword(Keyword::from(if closed { "closed" } else { "ready" }))), ("library", library_value), ("runtime", runtime_value)]))
400}
401
402fn allocate(record: Record, type_name: &str) -> Value {
403    STATE.with(|state| {
404        let mut state = state.borrow_mut();
405        state.next += 1;
406        let handle = state.next;
407        state.records.insert(handle, record);
408        extension(type_name, handle)
409    })
410}
411
412fn extension(type_name: &str, handle: u64) -> Value {
413    Value::Extension(ExtensionValue { provider: PROVIDER.into(), type_name: type_name.into(), handle })
414}
415
416trait ExtensionHandle {
417    fn extension_handle(&self) -> Option<u64>;
418}
419
420impl ExtensionHandle for Value {
421    fn extension_handle(&self) -> Option<u64> {
422        match self { Value::Extension(value) if value.provider == PROVIDER => Some(value.handle), _ => None }
423    }
424}
425
426fn handle(value: &Value, expected: &str, method: &str) -> Result<u64, String> {
427    match value {
428        Value::Extension(value) if value.provider == PROVIDER && value.type_name == expected => Ok(value.handle),
429        _ => Err(failure(expected, method, &format!("expects a std.lang.{expected} value"))),
430    }
431}
432
433fn config(value: &Value, operation: &str) -> Result<Value, String> {
434    map_entries(value).map(|_| value.clone()).ok_or_else(|| format!("std.lang.{operation} expects a configuration map"))
435}
436
437fn lookup(config: &Value, name: &str) -> Option<Value> {
438    map_entries(config)?.into_iter().find_map(|(key, value)| match key {
439        Value::Keyword(key) if key.as_str() == name => Some(value),
440        _ => None,
441    })
442}
443
444fn book_key(config: &Value, method: &str) -> Result<String, String> {
445    lookup(config, "coordinate")
446        .map(|value| value.display())
447        .ok_or_else(|| failure("Library", method, "requires Book :coordinate"))
448}
449
450fn book_key_argument(value: &Value, method: &str) -> Result<String, String> {
451    if let Value::Extension(extension) = value {
452        if extension.provider == PROVIDER && extension.type_name == "Book" {
453            return immutable_data(extension.handle, "Book", method).and_then(|data| book_key(&data, method));
454        }
455    }
456    if map_entries(value).is_some() { return book_key(value, method); }
457    Ok(value.display())
458}
459
460fn immutable_data(handle: u64, expected: &str, method: &str) -> Result<Value, String> {
461    STATE.with(|state| match state.borrow().records.get(&handle) {
462        Some(Record::Immutable { type_name, data }) if type_name == expected => Ok(data.clone()),
463        _ => Err(failure(expected, method, "expects its matching std.lang value")),
464    })
465}
466
467fn with_library<T>(handle: u64, method: &str, operation: impl FnOnce(&Value, &HashMap<String, Value>, &i64) -> Result<T, String>) -> Result<T, String> {
468    STATE.with(|state| match state.borrow().records.get(&handle) {
469        Some(Record::Library { config, books, revision }) => operation(config, books, revision),
470        _ => Err(failure("Library", method, "expects a std.lang.Library value")),
471    })
472}
473
474fn with_runtime<T>(handle: u64, method: &str, operation: impl FnOnce(&Value, &bool, &i64) -> Result<T, String>) -> Result<T, String> {
475    STATE.with(|state| match state.borrow().records.get(&handle) {
476        Some(Record::Runtime { config, closed, revision }) => operation(config, closed, revision),
477        _ => Err(failure("Runtime", method, "expects a std.lang.Runtime value")),
478    })
479}
480
481fn with_harness<T>(handle: u64, method: &str, operation: impl FnOnce(&Value, &u64, &u64, &bool) -> Result<T, String>) -> Result<T, String> {
482    STATE.with(|state| match state.borrow().records.get(&handle) {
483        Some(Record::Harness { config, library, runtime, closed }) => operation(config, library, runtime, closed),
484        _ => Err(failure("Harness", method, "expects a std.lang.Harness value")),
485    })
486}
487
488fn snapshot_library(snapshot: u64, owner: u64, method: &str) -> Result<(HashMap<String, Value>, i64), String> {
489    STATE.with(|state| match state.borrow().records.get(&snapshot) {
490        Some(Record::Snapshot { kind: SnapshotKind::Library, owner: snapshot_owner, books, library_revision, .. }) if *snapshot_owner == owner => Ok((books.clone(), *library_revision)),
491        _ => Err(failure("Library", method, "requires a snapshot from the same Library")),
492    })
493}
494
495fn snapshot_harness(snapshot: u64, owner: u64, method: &str) -> Result<(HashMap<String, Value>, i64, bool, i64, bool), String> {
496    STATE.with(|state| match state.borrow().records.get(&snapshot) {
497        Some(Record::Snapshot { kind: SnapshotKind::Harness, owner: snapshot_owner, books, library_revision, runtime_closed, runtime_revision, harness_closed }) if *snapshot_owner == owner => Ok((books.clone(), *library_revision, *runtime_closed, *runtime_revision, *harness_closed)),
498        _ => Err(failure("Harness", method, "requires a snapshot from the same Harness")),
499    })
500}
501
502fn restore_library(handle: u64, books: HashMap<String, Value>, revision: i64, method: &str) -> Result<(), String> {
503    STATE.with(|state| match state.borrow_mut().records.get_mut(&handle) {
504        Some(Record::Library { books: target, revision: target_revision, .. }) => { *target = books; *target_revision = revision; Ok(()) }
505        _ => Err(failure("Library", method, "expects a std.lang.Library value")),
506    })
507}
508
509fn restore_runtime(handle: u64, closed: bool, revision: i64, method: &str) -> Result<(), String> {
510    STATE.with(|state| match state.borrow_mut().records.get_mut(&handle) {
511        Some(Record::Runtime { closed: target_closed, revision: target_revision, .. }) => { *target_closed = closed; *target_revision = revision; Ok(()) }
512        _ => Err(failure("Runtime", method, "expects a std.lang.Runtime value")),
513    })
514}
515
516fn runtime_revision(handle: u64, method: &str) -> Result<i64, String> {
517    with_runtime(handle, method, |_, _, revision| Ok(*revision))
518}
519
520fn optional_handle(config: &Value, name: &str, expected: &str, method: &str) -> Result<Option<u64>, String> {
521    lookup(config, name).map(|value| handle(&value, expected, method)).transpose()
522}
523
524fn runtime_state_value(closed: bool, revision: i64) -> Value {
525    map(vec![
526        ("state", Value::Keyword(Keyword::from(if closed { "closed" } else { "ready" }))),
527        ("revision", Value::Number(revision)),
528    ])
529}
530
531fn empty_map() -> Value { Value::Map(PMap::new()) }
532
533fn map(entries: Vec<(&str, Value)>) -> Value {
534    Value::Map(PMap::from_iter(entries.into_iter().map(|(key, value)| (Value::Keyword(Keyword::from(key)), value))))
535}
536
537fn require_arity(type_name: &str, method: &str, values: &[Value], expected: usize) -> Result<(), String> {
538    if values.len() == expected { Ok(()) } else { Err(failure(type_name, method, &format!("expects {expected} argument{}", if expected == 1 { "" } else { "s" }))) }
539}
540
541fn failure(type_name: &str, method: &str, message: &str) -> String {
542    format!("std.lang.{type_name}/{method} {message}")
543}