dala/
lib.rs

1use core::fmt;
2use pest::Span;
3use std::collections::HashMap;
4
5mod interpreter;
6use interpreter::expr::eval_visitor::EvalVisitor;
7use interpreter::{ast::create_ast, parser::parse_dala};
8
9/// The result of a successful evaluation of a `DalaExpression`.
10/// It can be either a `String`, a `f64` or a `bool`.
11#[derive(Debug, Clone)]
12pub enum DalaValue {
13    Str(String),
14    Num(f64),
15    Boolean(bool),
16}
17
18impl fmt::Display for DalaValue {
19    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
20        match self {
21            DalaValue::Str(value) => write!(f, "\"{}\"", value),
22            DalaValue::Num(value) => write!(f, "{}", value),
23            DalaValue::Boolean(value) => write!(f, "{}", value.to_string().to_uppercase()),
24        }
25    }
26}
27
28/// Contains the position of a `DalaExpression` in the source code.
29#[derive(Debug, Clone)]
30pub struct Position {
31    pub start: usize,
32    pub end: usize,
33}
34
35impl Position {
36    pub fn new(pair: Span) -> Self {
37        Self {
38            start: pair.start(),
39            end: pair.end(),
40        }
41    }
42}
43
44impl fmt::Display for Position {
45    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
46        write!(f, "{}, {}", self.start, self.end)
47    }
48}
49
50/// The result of an unsuccessful evaluation of a `DalaExpression`.
51#[derive(Debug, Clone)]
52pub enum DalaError {
53    BuildError(BuildError),
54    RuntimeError(RuntimeError),
55    ParseError(ParseError),
56}
57
58impl fmt::Display for DalaError {
59    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
60        match self {
61            DalaError::BuildError(err) => write!(f, "{}", err),
62            DalaError::RuntimeError(err) => write!(f, "{}", err),
63            DalaError::ParseError(err) => write!(f, "{}", err),
64        }
65    }
66}
67
68/// An error that occurs during the evaluation of a `DalaExpression`.
69#[derive(Debug, Clone)]
70pub struct RuntimeError {
71    pub pos: Position,
72    pub message: String,
73}
74
75impl RuntimeError {
76    pub fn new(pos: Position, message: String) -> Self {
77        Self { pos, message }
78    }
79}
80
81impl fmt::Display for RuntimeError {
82    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
83        write!(f, "Runtime error: {}, at: {}", self.message, self.pos)
84    }
85}
86
87/// An error that occurs when processing the a `DalaExpression`, before its evaluation.
88#[derive(Debug, Clone)]
89pub struct BuildError {
90    pub pos: Position,
91    pub message: String,
92}
93
94impl BuildError {
95    pub fn new(pos: Position, message: String) -> Self {
96        Self { pos, message }
97    }
98}
99
100impl fmt::Display for BuildError {
101    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
102        write!(f, "Build error: {}, at: {}", self.message, self.pos)
103    }
104}
105
106/// An error that occurs when parsing a `DalaExpression`.
107#[derive(Debug, Clone)]
108pub struct ParseError {
109    pub message: String,
110}
111
112impl ParseError {
113    pub fn new(message: String) -> Self {
114        Self { message }
115    }
116}
117
118impl fmt::Display for ParseError {
119    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
120        write!(f, "Parse error: {}", self.message)
121    }
122}
123
124/// Evaluates a `DalaExpression` and returns a `DalaValue` if the evaluation is successful or a `DalaError` if it is not.
125///
126/// # Examples
127///
128/// ```
129/// use dala::{eval, DalaValue};
130///
131/// let result = eval("CONCAT(\"Hello\", \" \", \"World\")");
132/// let DalaValue::Str(value) = result[0].as_ref().unwrap() else { panic!("Not a string") };
133/// assert_eq!(value, "Hello World");
134/// ```
135pub fn eval(str: &str) -> Vec<Result<DalaValue, DalaError>> {
136    let parsed = parse_dala(str);
137    if parsed.is_err() {
138        return vec![Err(parsed.unwrap_err())];
139    }
140
141    create_ast(parsed.unwrap(), &HashMap::new())
142        .into_iter()
143        .map(|expr| expr.and_then(|expr| expr.eval()))
144        .collect()
145}
146
147/// Evaluates a `DalaExpression` with the provied dataset and returns a `DalaValue` if the evaluation is successful or a `DalaError` if it is not.
148///
149/// # Examples
150///
151/// ```
152/// use std::collections::HashMap;
153/// use dala::{eval_with_data, DalaValue};
154///
155/// let result = eval_with_data(
156///     "CONCAT($var1, $2)",
157///     &HashMap::from([
158///         ("$var1", &DalaValue::Str("hello".to_string())),
159///         ("$2", &DalaValue::Str("world".to_string())),
160///     ]),
161/// );
162/// let DalaValue::Str(value) = result[0].as_ref().unwrap() else { panic!("Not a string") };
163/// assert_eq!(value, "helloworld");
164/// ```
165pub fn eval_with_data(
166    str: &str,
167    data: &HashMap<&str, &DalaValue>,
168) -> Vec<Result<DalaValue, DalaError>> {
169    let parsed = parse_dala(str);
170    if parsed.is_err() {
171        return vec![Err(parsed.unwrap_err())];
172    }
173
174    create_ast(parsed.unwrap(), data)
175        .into_iter()
176        .map(|expr| expr.and_then(|expr| expr.eval()))
177        .collect()
178}