1use crate::ast::node::Node;
2use crate::ast::program::Program;
3use crate::eval::Environment;
4use crate::functions::ExprCall;
5use crate::{ContextProvider, Error, Result, Value};
6use crate::{ExprPest, Rule};
7use pest::Parser as PestParser;
8use pest::iterators::Pairs;
9use std::fmt;
10use std::fmt::{Debug, Formatter};
11
12pub fn compile(code: &str) -> Result<Program> {
14 #[cfg(debug_assertions)]
15 pest::set_error_detail(true);
16 let pairs = ExprPest::parse(Rule::full, code).map_err(|e| Error::PestError(Box::new(e)))?;
17 validate_numeric_literals(pairs.clone())?;
18 Ok(pairs.into())
19}
20
21fn validate_numeric_literals(pairs: Pairs<'_, Rule>) -> Result<()> {
22 for pair in pairs {
23 match pair.as_rule() {
24 Rule::int => {
25 Value::parse_integer(pair.as_str()).map_err(|error| {
26 Error::ParseError(format!("invalid integer literal {}: {error}", pair.as_str()))
27 })?;
28 }
29 Rule::decimal => {
30 let value = Value::parse_float(pair.as_str()).map_err(|error| {
31 Error::ParseError(format!("invalid float literal {}: {error}", pair.as_str()))
32 })?;
33 if !value.is_finite() {
34 return Err(Error::ParseError(format!(
35 "float literal is out of range: {}",
36 pair.as_str()
37 )));
38 }
39 }
40 _ => {}
41 }
42 validate_numeric_literals(pair.into_inner())?;
43 }
44 Ok(())
45}
46
47#[cfg(test)]
48mod literal_tests {
49 use super::compile;
50
51 #[test]
52 fn rejects_malformed_integer_separators() {
53 for code in ["1__0", "1_", "0x_2A_", "0b1__0"] {
54 assert!(compile(code).is_err(), "{code} should be rejected");
55 }
56 }
57
58 #[test]
59 fn rejects_integer_overflow_without_panicking() {
60 assert!(compile("0x10000000000000000").is_err());
61 assert!(compile("9223372036854775808").is_err());
62 }
63
64 #[test]
65 fn accepts_scientific_float_literals() {
66 for code in ["1e3", "1.2e-4", ".5e+2", "1_000.5_0e-2"] {
67 assert!(compile(code).is_ok(), "{code} should be accepted");
68 }
69 }
70
71 #[test]
72 fn rejects_malformed_or_overflowing_float_literals() {
73 for code in ["1e", "1e+", "1e_2", "1e9999"] {
74 assert!(compile(code).is_err(), "{code} should be rejected");
75 }
76 }
77
78 #[test]
79 fn rejects_raw_newlines_in_interpreted_literals() {
80 for code in ["\"a\nb\"", "'a\rb'", "b\"a\nb\"", "b'a\rb'"] {
81 assert!(compile(code).is_err(), "{code:?} should be rejected");
82 }
83 assert!(compile("`a\nb`").is_ok());
84 }
85
86 #[test]
87 fn conditional_keywords_require_identifier_boundaries() {
88 assert!(compile("if true { 1 } else { 2 }").is_ok());
89 assert!(compile("ifx { 1 } else { 2 }").is_err());
90 assert!(compile("if true { 1 } elseif { 2 }").is_err());
91 }
92}
93
94#[deprecated(note = "Use `compile()` and `Environment` instead")]
105#[derive(Default)]
106pub struct Parser<'a> {
107 env: Environment<'a>,
108}
109
110#[allow(deprecated)]
111impl Debug for Parser<'_> {
112 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
113 f.debug_struct("ExprParser").finish()
114 }
115}
116
117#[allow(deprecated)]
118impl<'a> Parser<'a> {
119 pub fn new() -> Self {
121 Parser {
122 env: Environment::new(),
123 }
124 }
125
126 pub fn add_function<F>(&mut self, name: &str, f: F)
149 where
150 F: Fn(ExprCall) -> Result<Value> + 'a + Sync + Send,
151 {
152 self.env.add_function(name, Box::new(f));
153 }
154
155 pub fn compile(&self, code: &str) -> Result<Program> {
157 compile(code)
158 }
159
160 pub fn run(&self, program: &Program, ctx: &dyn ContextProvider) -> Result<Value> {
162 self.env.run(program, ctx)
163 }
164
165 pub fn eval(&self, code: &str, ctx: &dyn ContextProvider) -> Result<Value> {
176 self.env.eval(code, ctx)
177 }
178
179 pub fn eval_expr(&self, ctx: &dyn ContextProvider, node: &Node) -> Result<Value> {
180 self.env.eval_expr(ctx, node)
181 }
182}