vihaco_cpu/parse_helpers.rs
1// SPDX-FileCopyrightText: 2026 The vihaco Authors
2// SPDX-License-Identifier: MIT
3
4//! Field-level parser helpers wired into `#[derive(vihaco_parser::Parse)]` via
5//! `#[parse_with = "..."]` on the `vihaco_cpu::Instruction` enum.
6//!
7//! Scope intentionally narrow: the helpers cover only the subset that
8//! `#[derive(Parse)]` can model cleanly. Three families of variants are
9//! **deferred to the Module orchestrator** (Item 4 of the migration plan):
10//!
11//! - `Const(Value::String/FunctionRef/HeapRef)` — needs a shared string
12//! interner / symbol table that `Parse` has no way to thread through.
13//! `cpu_const_value()` parses only the numeric/bool flavours.
14//! - `Branch`, `ConditionalBranch`, `Call` — use symbolic `@label` targets in
15//! real `.sst` source; the symbol table lives in the orchestrator. The
16//! `never_u32()` helper guarantees `Instruction::parser()` errors out on these
17//! mnemonics so the orchestrator can dispatch first.
18//! - Conversion from the parse-time numeric form to the orchestrator-resolved
19//! form is tracked in `~/.claude/plans/vihaco-future-rawvalue-refactor.md`.
20
21use chumsky::error::Simple;
22use chumsky::extra;
23use chumsky::prelude::*;
24use vihaco::value::{Type, Value};
25use vihaco_parser_core::Parse;
26
27type E<'src> = extra::Err<Simple<'src, char>>;
28
29/// Parses `.<typename>` and returns the matching [`Type`]. Used for the typed
30/// arithmetic/comparison variants — `add.i64`, `lt.u64`, etc.
31pub fn cpu_type<'src>() -> impl Parser<'src, &'src str, Type, E<'src>> {
32 just('.').ignore_then(choice((
33 just("i64").to(Type::I64),
34 just("u64").to(Type::U64),
35 just("u32").to(Type::U32),
36 just("f64").to(Type::F64),
37 just("bool").to(Type::Bool),
38 )))
39}
40
41/// Parses the body of `const.<type> <literal>` — without the leading `const`
42/// keyword (the derive macro emits that). Numeric and bool variants only.
43///
44/// String, FunctionRef, and HeapRef variants of `Value` are intentionally
45/// excluded: they require the orchestrator's interner/symbol tables.
46pub fn cpu_const_value<'src>() -> impl Parser<'src, &'src str, Value, E<'src>> {
47 choice((
48 just(".i64")
49 .ignore_then(text::whitespace())
50 .ignore_then(i64::parser())
51 .map(Value::I64),
52 just(".u64")
53 .ignore_then(text::whitespace())
54 .ignore_then(u64::parser())
55 .map(Value::U64),
56 just(".u32")
57 .ignore_then(text::whitespace())
58 .ignore_then(u32::parser())
59 .map(Value::U32),
60 just(".f64")
61 .ignore_then(text::whitespace())
62 .ignore_then(f64::parser())
63 .map(Value::F64),
64 just(".bool")
65 .ignore_then(text::whitespace())
66 .ignore_then(bool::parser())
67 .map(Value::Bool),
68 ))
69}
70
71/// A parser that always fails. Used for variant operands whose real form is a
72/// symbolic `@label` (`Branch`, `ConditionalBranch`, `Call`). Letting these
73/// fall through here means `Instruction::parser()` errors on their mnemonics,
74/// which is correct — the orchestrator must intercept first.
75pub fn never_u32<'src>() -> impl Parser<'src, &'src str, u32, E<'src>> {
76 empty().try_map(|_, span| Err(Simple::new(None, span)))
77}