1pub mod value;
8pub mod eval;
9
10pub use value::{Value, Env};
11pub use eval::{Evaluator, EvalError};
12
13use synoema_diagnostic::{Diagnostic, codes};
14use synoema_types::{TypeError, TypeErrorKind};
15
16fn parse_err(e: impl std::fmt::Display) -> Diagnostic {
19 Diagnostic::error(codes::PARSE_UNEXPECTED_TOKEN, format!("{}", e))
20}
21
22fn type_err(e: TypeError) -> Diagnostic {
23 let code = match &e.kind {
24 TypeErrorKind::Mismatch { .. } => codes::TYPE_MISMATCH,
25 TypeErrorKind::InfiniteType { .. } => codes::TYPE_INFINITE,
26 TypeErrorKind::Unbound { .. } => codes::TYPE_UNBOUND_VAR,
27 TypeErrorKind::UnboundType { .. } => codes::TYPE_UNBOUND_TYPE,
28 TypeErrorKind::ArityMismatch { .. } => codes::TYPE_ARITY,
29 TypeErrorKind::PatternMismatch { .. } => codes::TYPE_PATTERN,
30 TypeErrorKind::Other(_) => codes::TYPE_OTHER,
31 };
32 let mut diag = Diagnostic::error(code, format!("{}", e));
33 if let TypeErrorKind::Mismatch { expected, found } = &e.kind {
35 diag = diag
36 .with_note(format!("expected: {}", expected))
37 .with_note(format!("found: {}", found));
38 }
39 diag.maybe_span(e.span)
40}
41
42fn eval_err(e: EvalError) -> Diagnostic {
43 let msg = &e.message;
44 let code = if msg.contains("Undefined") || msg.contains("undefined") {
45 codes::EVAL_UNDEFINED
46 } else if msg.contains("No matching pattern") || msg.contains("no matching") {
47 codes::EVAL_NO_MATCH
48 } else if msg.contains("Division by zero") || msg.contains("division by zero") {
49 codes::EVAL_DIV_ZERO
50 } else if msg.contains("readline") || msg.contains("IO") {
51 codes::EVAL_IO
52 } else {
53 codes::EVAL_TYPE
54 };
55 Diagnostic::error(code, e.message)
56}
57
58pub fn run(source: &str) -> Result<Env, Diagnostic> {
63 let program = synoema_parser::parse(source)
64 .map_err(|e| parse_err(&e))?;
65 let program = synoema_types::resolve_modules(program);
66 let _types = synoema_types::typecheck(source)
67 .map_err(type_err)?;
68 let mut evaluator = Evaluator::new();
69 evaluator.eval_program(&program)
70 .map_err(eval_err)
71}
72
73pub fn eval_main(source: &str) -> Result<(Value, Vec<String>), Diagnostic> {
79 let source = source.to_string();
80 std::thread::Builder::new()
81 .stack_size(64 * 1024 * 1024) .spawn(move || eval_main_inner(&source))
83 .expect("Failed to spawn eval thread")
84 .join()
85 .expect("Eval thread panicked")
86}
87
88fn eval_main_inner(source: &str) -> Result<(Value, Vec<String>), Diagnostic> {
89 let program = synoema_parser::parse(source)
90 .map_err(|e| parse_err(&e))?;
91 let program = synoema_types::resolve_modules(program);
92 let _types = synoema_types::typecheck(source)
93 .map_err(type_err)?;
94 let mut evaluator = Evaluator::new();
95 let env = evaluator.eval_program(&program)
96 .map_err(eval_err)?;
97
98 let main_name = program.decls.iter().rev()
100 .find_map(|d| match d {
101 synoema_parser::Decl::Func { name, .. } => Some(name.clone()),
102 _ => None,
103 })
104 .ok_or_else(|| Diagnostic::error(codes::EVAL_UNDEFINED, "No function defined"))?;
105
106 let val = env.lookup(&main_name)
107 .cloned()
108 .ok_or_else(|| Diagnostic::error(codes::EVAL_UNDEFINED, format!("Function '{}' not found", main_name)))?;
109
110 let result = match &val {
112 Value::Func { equations, .. } if equations.iter().all(|eq| eq.pats.is_empty()) => {
113 let eq = &equations[0];
114 evaluator.eval(&env, &eq.body)
115 .map_err(eval_err)?
116 }
117 other => other.clone(),
118 };
119
120 Ok((result, evaluator.output))
121}
122
123pub fn eval_expr(source: &str) -> Result<Value, Diagnostic> {
125 let wrapped = format!("__expr = {}", source);
127 let program = synoema_parser::parse(&wrapped)
128 .map_err(|e| parse_err(&e))?;
129 let mut evaluator = Evaluator::new();
130 let env = evaluator.eval_program(&program)
131 .map_err(eval_err)?;
132
133 match env.lookup("__expr") {
134 Some(Value::Func { equations, .. }) if !equations.is_empty() => {
135 let eq = &equations[0];
136 if eq.pats.is_empty() {
137 evaluator.eval(&env, &eq.body)
138 .map_err(eval_err)
139 } else {
140 Ok(env.lookup("__expr").unwrap().clone())
141 }
142 }
143 Some(v) => Ok(v.clone()),
144 None => Err(Diagnostic::error(codes::EVAL_UNDEFINED, "Expression not found")),
145 }
146}
147
148#[cfg(test)]
149mod tests;