wasm_wave/
untyped.rs

1//! Untyped value
2
3use std::borrow::Cow;
4
5use crate::{Parser, WasmValue, ast::Node, lex::Keyword, parser::ParserError};
6
7/// An UntypedValue is a parsed but not type-checked WAVE value.
8#[derive(Clone, Debug)]
9pub struct UntypedValue<'source> {
10    source: Cow<'source, str>,
11    node: Node,
12}
13
14impl<'source> UntypedValue<'source> {
15    pub(crate) fn new(source: impl Into<Cow<'source, str>>, node: Node) -> Self {
16        Self {
17            source: source.into(),
18            node,
19        }
20    }
21
22    /// Parses an untyped value from WAVE.
23    pub fn parse(source: &'source str) -> Result<Self, ParserError> {
24        let mut parser = Parser::new(source);
25        let val = parser.parse_raw_value()?;
26        parser.finish()?;
27        Ok(val)
28    }
29
30    /// Creates an owned value, copying the entire source string if necessary.
31    pub fn into_owned(self) -> UntypedValue<'static> {
32        UntypedValue::new(self.source.into_owned(), self.node)
33    }
34
35    /// Returns the source this value was parsed from.
36    pub fn source(&self) -> &str {
37        &self.source
38    }
39
40    /// Returns this value's root node.
41    pub fn node(&self) -> &Node {
42        &self.node
43    }
44
45    /// Converts this untyped value into the given typed value.
46    pub fn to_wasm_value<V: WasmValue>(&self, ty: &V::Type) -> Result<V, ParserError> {
47        self.node.to_wasm_value(ty, &self.source)
48    }
49}
50
51impl std::fmt::Display for UntypedValue<'_> {
52    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53        fmt_node(f, &self.node, &self.source)
54    }
55}
56
57/// An UntypedFuncCall is a parsed but not type-checked WAVE function call.
58///
59/// WAVE function calls have the form `<name>(<params...>)`.
60pub struct UntypedFuncCall<'source> {
61    source: Cow<'source, str>,
62    name: Node,
63    params: Option<Node>,
64}
65
66impl<'source> UntypedFuncCall<'source> {
67    pub(crate) fn new(
68        source: impl Into<Cow<'source, str>>,
69        name: Node,
70        params: Option<Node>,
71    ) -> Self {
72        Self {
73            source: source.into(),
74            name,
75            params,
76        }
77    }
78
79    /// Parses an untyped function call from WAVE.
80    pub fn parse(source: &'source str) -> Result<Self, ParserError> {
81        let mut parser = Parser::new(source);
82        let call = parser.parse_raw_func_call()?;
83        parser.finish()?;
84        Ok(call)
85    }
86
87    /// Creates an owned function call, copying the entire source string if necessary.
88    pub fn into_owned(self) -> UntypedFuncCall<'static> {
89        UntypedFuncCall::new(
90            self.source.into_owned(),
91            self.name.clone(),
92            self.params.clone(),
93        )
94    }
95
96    /// Returns the source this function call was parsed from.
97    pub fn source(&self) -> &str {
98        &self.source
99    }
100
101    /// Returns the function name node.
102    pub fn name_node(&self) -> &Node {
103        &self.name
104    }
105
106    /// Returns the function parameters node.
107    ///
108    /// Returns `None` if the function call has no parameters.
109    pub fn params_node(&self) -> Option<&Node> {
110        self.params.as_ref()
111    }
112
113    /// Returns the function name.
114    pub fn name(&self) -> &str {
115        self.name.slice(&self.source)
116    }
117
118    /// Converts the untyped parameters into the given types.
119    ///
120    /// Any number of trailing option-typed values may be omitted; those will
121    /// be returned as `none` values.
122    pub fn to_wasm_params<'types, V: WasmValue + 'static>(
123        &self,
124        types: impl IntoIterator<Item = &'types V::Type>,
125    ) -> Result<Vec<V>, ParserError> {
126        match &self.params {
127            Some(params) => params.to_wasm_params(types, self.source()),
128            None => Ok(vec![]),
129        }
130    }
131}
132
133impl std::fmt::Display for UntypedFuncCall<'_> {
134    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135        f.write_str(self.name.slice(&self.source))?;
136        match &self.params {
137            Some(params) => fmt_node(f, params, &self.source),
138            None => f.write_str("()"),
139        }
140    }
141}
142
143fn fmt_node(f: &mut impl std::fmt::Write, node: &Node, src: &str) -> std::fmt::Result {
144    use crate::ast::NodeType::*;
145    match node.ty() {
146        BoolTrue | BoolFalse | Number | Char | String | MultilineString | Label => {
147            f.write_str(node.slice(src))
148        }
149        Tuple => fmt_sequence(f, '(', ')', node.as_tuple()?, src),
150        List => fmt_sequence(f, '[', ']', node.as_list()?, src),
151        Record => {
152            let fields = node.as_record(src)?;
153            if fields.len() == 0 {
154                return f.write_str("{:}");
155            }
156            f.write_char('{')?;
157            for (idx, (name, value)) in node.as_record(src)?.enumerate() {
158                if idx != 0 {
159                    f.write_str(", ")?;
160                }
161                write!(f, "{name}: ")?;
162                fmt_node(f, value, src)?;
163            }
164            f.write_char('}')
165        }
166        VariantWithPayload => {
167            let (label, payload) = node.as_variant(src)?;
168            if Keyword::decode(label).is_some() {
169                f.write_char('%')?;
170            }
171            fmt_variant(f, label, payload, src)
172        }
173        OptionSome => fmt_variant(f, "some", node.as_option()?, src),
174        OptionNone => fmt_variant(f, "none", None, src),
175        ResultOk => fmt_variant(f, "ok", node.as_result()?.unwrap(), src),
176        ResultErr => fmt_variant(f, "err", node.as_result()?.unwrap_err(), src),
177        Flags => {
178            f.write_char('{')?;
179            for (idx, flag) in node.as_flags(src)?.enumerate() {
180                if idx != 0 {
181                    f.write_str(", ")?;
182                }
183                f.write_str(flag)?;
184            }
185            f.write_char('}')
186        }
187    }
188}
189
190fn fmt_sequence<'a>(
191    f: &mut impl std::fmt::Write,
192    open: char,
193    close: char,
194    nodes: impl Iterator<Item = &'a Node>,
195    src: &str,
196) -> std::fmt::Result {
197    f.write_char(open)?;
198    for (idx, node) in nodes.enumerate() {
199        if idx != 0 {
200            f.write_str(", ")?;
201        }
202        fmt_node(f, node, src)?;
203    }
204    f.write_char(close)
205}
206
207fn fmt_variant(
208    f: &mut impl std::fmt::Write,
209    case: &str,
210    payload: Option<&Node>,
211    src: &str,
212) -> std::fmt::Result {
213    f.write_str(case)?;
214    if let Some(node) = payload {
215        f.write_char('(')?;
216        fmt_node(f, node, src)?;
217        f.write_char(')')?;
218    }
219    Ok(())
220}
221
222impl From<ParserError> for std::fmt::Error {
223    fn from(_: ParserError) -> Self {
224        Self
225    }
226}
227
228#[cfg(test)]
229mod tests {
230    use super::*;
231
232    #[test]
233    fn round_trips() {
234        for src in [
235            "true",
236            "18446744073709551616",
237            "-9223372036854775808",
238            "[-3.1415, 0, inf, nan, -inf]",
239            "['☃', '\\n']",
240            r#""☃☃☃""#,
241            "(1, false)",
242            "{:}",
243            "{code: red}",
244            "left(1)",
245            "[some(1), none]",
246            "[ok(1), err(2)]",
247            "[ok, err]",
248            "%inf(inf)",
249            "%some",
250            "%none(none)",
251        ] {
252            let val = UntypedValue::parse(src).unwrap();
253            let encoded = val.to_string();
254            assert_eq!(encoded, src);
255        }
256    }
257}