1pub mod ast;
49pub mod builtins;
50pub mod error;
51pub mod interpreter;
52pub mod lexer;
53pub mod parser;
54
55use indexmap::IndexMap;
56
57pub use ast::{Expression, Program, Statement};
59pub use builtins::BuiltinFn;
60pub use error::{FiddlerError, LexError, ParseError, RuntimeError};
61pub use interpreter::Interpreter;
62pub use lexer::{Lexer, Token};
63pub use parser::Parser;
64
65pub fn parse(source: &str) -> Result<Program, FiddlerError> {
85 let mut lexer = Lexer::new(source);
86 let tokens = lexer.tokenize()?;
87 let mut parser = Parser::new(tokens);
88 let program = parser.parse()?;
89 Ok(program)
90}
91
92pub fn check(source: &str) -> Result<(), FiddlerError> {
98 parse(source).map(|_| ())
99}
100
101#[derive(Debug, Clone)]
103pub enum Value {
104 Integer(i64),
106 Float(f64),
108 String(String),
110 Boolean(bool),
112 Bytes(Vec<u8>),
114 Array(Vec<Value>),
116 Dictionary(IndexMap<String, Value>),
118 Null,
120}
121
122impl PartialEq for Value {
124 fn eq(&self, other: &Self) -> bool {
125 match (self, other) {
126 (Value::Integer(a), Value::Integer(b)) => a == b,
127 (Value::Float(a), Value::Float(b)) => a == b, (Value::Integer(a), Value::Float(b)) => (*a as f64) == *b,
129 (Value::Float(a), Value::Integer(b)) => *a == (*b as f64),
130 (Value::String(a), Value::String(b)) => a == b,
131 (Value::Boolean(a), Value::Boolean(b)) => a == b,
132 (Value::Bytes(a), Value::Bytes(b)) => a == b,
133 (Value::Array(a), Value::Array(b)) => a == b,
134 (Value::Dictionary(a), Value::Dictionary(b)) => a == b,
135 (Value::Null, Value::Null) => true,
136 _ => false,
137 }
138 }
139}
140
141impl std::fmt::Display for Value {
142 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
143 match self {
144 Value::Integer(n) => write!(f, "{}", n),
145 Value::Float(fl) => {
146 if fl.is_nan() {
147 write!(f, "NaN")
148 } else if fl.is_infinite() {
149 if fl.is_sign_positive() {
150 write!(f, "Infinity")
151 } else {
152 write!(f, "-Infinity")
153 }
154 } else if fl.fract() == 0.0 && fl.abs() < 1e15 {
155 write!(f, "{:.1}", fl)
157 } else {
158 let s = format!("{:.10}", fl);
160 let s = s.trim_end_matches('0').trim_end_matches('.');
161 write!(f, "{}", s)
162 }
163 }
164 Value::String(s) => write!(f, "{}", s),
165 Value::Boolean(b) => write!(f, "{}", b),
166 Value::Bytes(bytes) => write!(f, "<bytes: {} bytes>", bytes.len()),
167 Value::Array(arr) => {
168 write!(f, "[")?;
169 let mut first = true;
170 for v in arr {
171 if !first {
172 write!(f, ", ")?;
173 }
174 write!(f, "{}", v)?;
175 first = false;
176 }
177 write!(f, "]")
178 }
179 Value::Dictionary(dict) => {
180 write!(f, "{{")?;
181 let mut first = true;
182 for (k, v) in dict {
183 if !first {
184 write!(f, ", ")?;
185 }
186 write!(f, "{}: {}", k, v)?;
187 first = false;
188 }
189 write!(f, "}}")
190 }
191 Value::Null => write!(f, "null"),
192 }
193 }
194}
195
196impl Value {
197 pub fn to_bytes(&self) -> Vec<u8> {
199 match self {
200 Value::Integer(n) => n.to_string().into_bytes(),
201 Value::Float(f) => f.to_string().into_bytes(),
202 Value::String(s) => s.as_bytes().to_vec(),
203 Value::Boolean(b) => b.to_string().into_bytes(),
204 Value::Bytes(bytes) => bytes.clone(),
205 Value::Array(_) | Value::Dictionary(_) => self.to_string().into_bytes(),
206 Value::Null => Vec::new(),
207 }
208 }
209
210 pub fn is_number(&self) -> bool {
212 matches!(self, Value::Integer(_) | Value::Float(_))
213 }
214
215 pub fn as_f64(&self) -> Option<f64> {
217 match self {
218 Value::Integer(n) => Some(*n as f64),
219 Value::Float(f) => Some(*f),
220 _ => None,
221 }
222 }
223
224 pub fn as_i64(&self) -> Option<i64> {
226 match self {
227 Value::Integer(n) => Some(*n),
228 Value::Float(f) => Some(*f as i64),
229 _ => None,
230 }
231 }
232
233 pub fn from_bytes(bytes: Vec<u8>) -> Self {
235 Value::Bytes(bytes)
236 }
237
238 pub fn as_string_lossy(&self) -> Result<String, RuntimeError> {
244 match self {
245 Value::String(s) => Ok(s.clone()),
246 Value::Bytes(b) => Ok(String::from_utf8_lossy(b).into_owned()),
247 _ => Err(RuntimeError::invalid_argument(
248 "Expected string or bytes value",
249 )),
250 }
251 }
252
253 pub fn is_array(&self) -> bool {
255 matches!(self, Value::Array(_))
256 }
257
258 pub fn is_dictionary(&self) -> bool {
260 matches!(self, Value::Dictionary(_))
261 }
262
263 pub fn as_array(&self) -> Option<&Vec<Value>> {
265 match self {
266 Value::Array(arr) => Some(arr),
267 _ => None,
268 }
269 }
270
271 pub fn as_dictionary(&self) -> Option<&IndexMap<String, Value>> {
273 match self {
274 Value::Dictionary(dict) => Some(dict),
275 _ => None,
276 }
277 }
278}
279
280#[cfg(test)]
281mod lint_tests {
282 use super::*;
283
284 #[test]
285 fn parse_valid_source() {
286 let program = parse("let x = 10; let y = x + 1;").unwrap();
287 assert!(!program.statements.is_empty());
288 }
289
290 #[test]
291 fn parse_returns_lex_error_for_bad_char() {
292 let err = parse("let x = @;").unwrap_err();
293 assert!(matches!(err, FiddlerError::Lex(_)));
294 }
295
296 #[test]
297 fn parse_returns_lex_error_for_unterminated_string() {
298 let err = parse(r#"let x = "hello;"#).unwrap_err();
299 assert!(matches!(err, FiddlerError::Lex(_)));
300 }
301
302 #[test]
303 fn parse_returns_parse_error_for_missing_semicolon() {
304 let err = parse("let x = 10\nlet y = 20;").unwrap_err();
305 assert!(matches!(err, FiddlerError::Parse(_)));
306 }
307
308 #[test]
309 fn parse_does_not_execute() {
310 let result = parse("let x = undefined_var + 1;");
313 assert!(result.is_ok());
314 }
315
316 #[test]
317 fn check_valid_source() {
318 assert!(check("let x = 10;").is_ok());
319 }
320
321 #[test]
322 fn check_invalid_source() {
323 assert!(check("let x = ;").is_err());
324 }
325}