1use crate::{
5 aim::{ContextLike, WriterLike, Writer},
6 expressions::{Conditional, ExpressionLike, Primary, parse_conditional, parse_arc_identifier},
7 values::Value,
8};
9use anyhow::Result;
10use nom::{
11 IResult, Parser,
12 bytes::tag,
13 character::complete::{char, multispace0},
14 sequence::{delimited, separated_pair},
15};
16use std::{
17 fmt,
18 sync::Arc,
19};
20
21#[derive(Debug, Clone, PartialEq)]
28pub enum Closure {
29 One(Arc<str>, Conditional),
30 Two(Arc<str>, Arc<str>, Conditional),
31 Primary(Box<Primary>),
33}
34
35impl Closure {
36 pub fn invoke(&self, context: &mut dyn ContextLike) -> Result<Arc<Value>> {
38 match self {
39 Closure::Two(one, two, conditional) => {
40 context.set_key(0, one.clone());
41 context.set_key(1, two.clone());
42 conditional.evaluate(context)
43 }
44 Closure::One(one, conditional) => {
45 context.set_key(0, one.clone());
46 conditional.evaluate(context)
47 }
48 Closure::Primary(primary) => primary.evaluate(context),
49 }
50 }
51
52 pub fn call(&self, context: &mut dyn ContextLike, arg: Arc<Value>) -> Result<Arc<Value>> {
59 if arg.is_error() {
60 return Ok(arg);
61 }
62
63 let stack = context.start_closure();
64
65 match &*arg {
66 Value::Literal(_) | Value::Errata(_) | Value::Empty => {
67 context.set_parameter(0, arg);
68 }
69 Value::Array(array) => {
70 if !array.is_empty() {
71 context.set_parameter(0, array[0].clone());
72 if array.len() > 1 {
73 context.set_parameter(1, array[1].clone());
74 }
75 }
76 }
77 _ => {}
78 }
79
80 let result = self.invoke(context);
81 context.end_closure(stack);
82 result
83 }
84
85 pub fn print(&self, writer: &mut Writer) {
87 match self {
88 Closure::One(one, conditional) => {
89 writer.write_str(one);
90 writer.write_str(" => ");
91 conditional.print(writer);
92 }
93 Closure::Two(one, two, conditional) => {
94 writer.write_char('(');
95 writer.write_str(one);
96 writer.write_str(", ");
97 writer.write_str(two);
98 writer.write_str(") => ");
99 conditional.print(writer);
100 }
101 Closure::Primary(primary) => primary.print(writer),
102 }
103 }
104}
105
106fn single_closure(input: &str) -> IResult<&str, Closure> {
108 let (input, (one, _, conditional)) = (
109 parse_arc_identifier,
110 delimited(multispace0, tag("=>"), multispace0),
111 parse_conditional,
112 ).parse(input)?;
113 Ok((input, Closure::One(one, conditional)))
114}
115
116fn double_closure(input: &str) -> IResult<&str, Closure> {
118 let (input, ((one, two), _, conditional)) = (delimited(
119 char('('),
120 separated_pair(
121 parse_arc_identifier,
122 delimited(multispace0, char(','), multispace0),
123 parse_arc_identifier,
124 ),
125 char(')'),
126 ),
127 delimited(multispace0, tag("=>"), multispace0),
128 parse_conditional,
129 ).parse(input)?;
130 Ok((input, Closure::Two(one, two, conditional)))
131}
132
133pub fn parse_closure(input: &str) -> IResult<&str, Closure> {
137 if input.starts_with('(') {
138 if let Ok((input, closure)) = double_closure(input) {
139 return Ok((input, closure));
140 }
141 } else if let Ok((input, closure)) = single_closure(input) {
142 return Ok((input, closure));
143 }
144 parse_closure_conditional(input)
145}
146
147pub fn parse_closure_conditional(input: &str) -> IResult<&str, Closure> {
149 let (input, conditional) = parse_conditional(input)?;
150 let closure = match conditional {
151 Conditional::Primary(primary) => Closure::Primary(primary),
152 _ => Closure::Primary(Box::new(Primary::Conditional(conditional))),
153 };
154 Ok((input, closure))
155}
156
157impl ExpressionLike for Closure {
158 fn evaluate(&self, context: &mut dyn ContextLike) -> Result<Arc<Value>> {
163 match self {
164 Closure::Primary(primary) => primary.evaluate(context),
165 _ => Ok(Arc::new(Value::Closure(Arc::new(self.clone())))),
167 }
168 }
169
170 fn to_formula(&self) -> String {
172 let mut writer = Writer::formulizer();
173 self.print(&mut writer);
174 writer.finish()
175 }
176}
177
178impl WriterLike for Closure {
179 fn write(&self, writer: &mut Writer) {
180 self.print(writer);
181 }
182}
183
184impl fmt::Display for Closure {
185 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186 write!(f, "{}", self.to_stringized())
187 }
188}