ryan/parser/
literal.rs

1use indexmap::IndexMap;
2use pest::iterators::Pairs;
3use std::fmt::Display;
4use std::rc::Rc;
5
6use crate::rc_world;
7
8use super::value::Value;
9use super::ErrorLogger;
10use super::Rule;
11use super::State;
12
13/// A literal Ryan value.
14#[derive(Debug, Clone, PartialEq)]
15pub enum Literal {
16    /// The value `null`.
17    Null,
18    /// An integer.
19    Integer(i64),
20    /// A float.
21    Float(f64),
22    /// A boolean.
23    Bool(bool),
24    /// An utf-8 encoded string.
25    Text(String),
26    /// An identifier, i.e., the name of a variable, a type or a pattern.
27    Identifier(Rc<str>),
28}
29
30impl Default for Literal {
31    fn default() -> Self {
32        Literal::Integer(0)
33    }
34}
35
36impl Display for Literal {
37    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38        match self {
39            Self::Null => write!(f, "null"),
40            Self::Integer(int) => write!(f, "{int}"),
41            Self::Float(float) => write!(f, "{float}"),
42            Self::Bool(b) => write!(f, "{b}"),
43            Self::Text(text) => write!(f, "{text:?}"),
44            Self::Identifier(id) => write!(f, "{id}"),
45        }
46    }
47}
48
49impl Literal {
50    pub(super) fn parse(logger: &mut ErrorLogger, mut pairs: Pairs<'_, Rule>) -> Self {
51        let pair = pairs.next().expect("there is always a token in a literal");
52
53        let literal = match pair.as_rule() {
54            Rule::null => Literal::Null,
55            Rule::number => logger.absorb(
56                &pair,
57                pair.as_str()
58                    .replace('_', "")
59                    .parse::<i64>()
60                    .map(|int| Literal::Integer(int))
61                    .or_else(|_| {
62                        pair.as_str()
63                            .replace('_', "")
64                            .parse::<f64>()
65                            .map(|float| Literal::Float(float))
66                    }),
67            ),
68            Rule::bool => match pair.as_str() {
69                "true" => Literal::Bool(true),
70                "false" => Literal::Bool(false),
71                _ => unreachable!(),
72            },
73            Rule::text => {
74                Literal::Text(logger.absorb(&pair, crate::utils::unescape(pair.as_str())))
75            }
76            Rule::identifier => Literal::Identifier(rc_world::str_to_rc(pair.as_str())),
77            _ => unreachable!(),
78        };
79
80        literal
81    }
82
83    #[must_use]
84    pub(super) fn capture(
85        &self,
86        state: &mut State<'_>,
87        provided: &[Rc<str>],
88        values: &mut IndexMap<Rc<str>, Value>,
89    ) -> Option<()> {
90        if let Self::Identifier(id) = self {
91            match state.try_get(id) {
92                Ok(cap) => {
93                    values.insert(id.clone(), cap.clone());
94                }
95                Err(err) => {
96                    if !provided.contains(&id) {
97                        state.absorb(Err(err))?;
98                    }
99                }
100            }
101        }
102
103        Some(())
104    }
105
106    pub(super) fn eval(&self, state: &mut State<'_>) -> Option<Value> {
107        let value = match self {
108            Self::Null => Value::Null,
109            Self::Bool(b) => Value::Bool(*b),
110            Self::Integer(int) => Value::Integer(*int),
111            Self::Float(float) => Value::Float(*float),
112            Self::Text(text) => Value::Text(rc_world::str_to_rc(&text)),
113            Self::Identifier(id) => state.get(id)?,
114        };
115
116        Some(value)
117    }
118}