1use super::adapter::Adapter;
7use super::node::Node;
8use crate::func;
9use crate::lang::args::should_append_stdin;
10use crate::lang::error::Error;
11use crate::lang::error::ErrorKind;
12use crate::lang::label::Label;
13use crate::lang::machine::Machine;
14use crate::lang::node::pipe_from_nodes;
15use crate::lang::parse;
16use crate::value::Value;
17use crate::value::keys::Keys;
18use crate::value::list::List;
19use crate::value::map::Map;
20use crate::value::operation::Operation;
21use crate::value::operation::fold_map_value;
22use crate::value::operation::get_value;
23use crate::value::operation::insert_map_value;
24use crate::value::span::Span;
25use crate::value::tracer::Segment;
26use crate::value::tracer::Tracer;
27use either::Either;
28use std::borrow::Cow;
29
30#[derive(Debug, Default)]
31pub struct Eval<T> {
32 pub result: T,
33 #[doc(hidden)]
34 pub ops: Vec<Operation>,
35}
36
37impl<T: Into<Value>> From<T> for Eval<Value> {
38 fn from(value: T) -> Self {
39 Self {
40 result: value.into(),
41 ops: Vec::default(),
42 }
43 }
44}
45
46impl<T: Into<Keys>> From<T> for Eval<Keys> {
47 fn from(value: T) -> Self {
48 Self {
49 result: value.into(),
50 ops: Vec::default(),
51 }
52 }
53}
54
55pub(crate) fn eval(
56 adapter: &impl Adapter,
57 map: &mut Map,
58 span: Span,
59 node: Node,
60) -> Result<Eval<Value>, Error> {
61 let mut machine = Machine::new(map, span);
62
63 eval_machine(adapter, &mut machine, node)
64}
65
66fn eval_machine(
67 adapter: &impl Adapter,
68 machine: &mut Machine<'_>,
69 node: Node,
70) -> Result<Eval<Value>, Error> {
71 let result = eval_node(adapter, machine, None, node)?;
72
73 Ok(Eval {
74 result: result.unwrap_or_default(),
75 ops: core::mem::take(&mut machine.ops),
76 })
77}
78
79pub(crate) fn eval_keys(
80 adapter: &impl Adapter,
81 map: &mut Map,
82 span: Span,
83 node: Node,
84) -> Result<Option<Eval<Keys>>, Error> {
85 let mut machine = Machine::new(map, span);
86 let result = keys_from_node(adapter, &mut machine, node)?;
87
88 Ok(result.map(|result| Eval {
89 ops: machine.ops,
90 result,
91 }))
92}
93
94pub(crate) fn eval_node(
95 adapter: &impl Adapter,
96 machine: &mut Machine<'_>,
97 stdin: Option<&Value>,
98 node: Node,
99) -> Result<Option<Value>, Error> {
100 match node {
101 Node::Bytes(s, n) => Ok(Some(eval_bytes(adapter, s, n))),
102 Node::Tag(s, n) => Ok(Some(eval_tag(adapter, s, n))),
103 Node::Label(..) => eval_context_label(adapter, machine, stdin, &node).map(Some),
104 Node::List(..) => eval_list(adapter, machine, stdin, node),
105 Node::Map(..) => eval_map(adapter, machine, stdin, node),
106 Node::Pipe(..) => eval_node_list(adapter, machine, stdin, node),
107 Node::Paren(..) => eval_node_list(adapter, machine, stdin, node),
108 Node::Bracket(..) => eval_node_list(adapter, machine, stdin, node),
109 Node::Semicolon(..) => eval_node_list(adapter, machine, stdin, node),
110 Node::And(..) => eval_node_list(adapter, machine, stdin, node),
111 Node::Or(..) => eval_node_list(adapter, machine, stdin, node),
112 Node::Thunk(..) => eval_node_list(adapter, machine, stdin, node),
113 Node::Comma(_) => Err(eval_error(adapter, machine, "unexpected comma")),
114 Node::Space(_) => Ok(None),
115 }
116}
117
118pub(crate) fn eval_node_list(
119 adapter: &impl Adapter,
120 machine: &mut Machine<'_>,
121 stdin: Option<&Value>,
122 mut node: Node,
123) -> Result<Option<Value>, Error> {
124 let head = node.list().first().cloned();
125
126 if node.list().is_empty() {
127 return Ok(None);
128 }
129
130 if let Some(Node::Label(..)) = &head {
131 match eval_func(adapter, machine, stdin, node)? {
132 Either::Right(v) => return Ok(v),
133 Either::Left(n) => node = n,
134 }
135 }
136
137 if node.list().len() > 1 && !has_space(&node) {
138 if let Some(Node::List(..)) = &head {
139 return eval_list_getter(adapter, machine, stdin, node);
140 } else if let Some(Node::Map(..)) = &head {
141 return eval_map_getter(adapter, machine, stdin, node);
142 }
143 }
144
145 if node.list().len() > 1 && !has_pipe(&node) && (has_tag(&node) || has_space(&node)) {
146 return eval_concat(adapter, machine, stdin, &node);
147 }
148
149 if let Some(Node::Label(..)) = &head {
150 return eval_context_getter(adapter, machine, stdin, node);
151 }
152
153 let mut skip_to_pipe = false;
154 let mut stdin = stdin.cloned();
155
156 for node in node.into_list().unwrap_or_default() {
157 if skip_to_pipe {
158 match node {
159 Node::Semicolon(..) => skip_to_pipe = false,
160 Node::Pipe(..) => skip_to_pipe = false,
161 Node::Or(..) => skip_to_pipe = false,
162 _ => continue,
163 }
164 }
165
166 stdin = match node {
167 Node::Label(_, n) => {
168 return Err(eval_error_data(
169 adapter,
170 machine,
171 "unexpected label",
172 n.as_bytes(),
173 ));
174 }
175 Node::Thunk(..) => {
176 return Err(thunk_error(adapter, machine));
177 }
178 Node::Comma(_) => {
179 return Err(eval_error(adapter, machine, "unexpected comma"));
180 }
181 Node::And(..) if stdin.as_ref().is_some_and(|v| !v.is_empty()) => {
182 eval_node_list(adapter, machine, None, node)?
183 }
184 Node::Or(..) if stdin.as_ref().is_none_or(Value::is_empty) => {
185 eval_node_list(adapter, machine, None, node)?
186 }
187 Node::And(..) => {
188 skip_to_pipe = true;
189 stdin
190 }
191 Node::Or(..) => {
192 skip_to_pipe = true;
193 stdin
194 }
195 Node::Bytes(s, n) => Some(eval_bytes(adapter, s, n)),
196 Node::Tag(s, n) => Some(eval_tag(adapter, s, n)),
197 Node::List(..) => eval_list(adapter, machine, stdin.as_ref(), node)?,
198 Node::Map(..) => eval_map(adapter, machine, stdin.as_ref(), node)?,
199 Node::Pipe(..) => eval_node_list(adapter, machine, stdin.as_ref(), node)?,
200 Node::Paren(..) => eval_node_list(adapter, machine, stdin.as_ref(), node)?,
201 Node::Bracket(..) => eval_node_list(adapter, machine, stdin.as_ref(), node)?,
202 Node::Semicolon(..) => eval_node_list(adapter, machine, None, node)?,
203 Node::Space(_) => stdin,
204 };
205 }
206
207 Ok(stdin)
208}
209
210fn eval_func(
211 adapter: &impl Adapter,
212 machine: &mut Machine<'_>,
213 stdin: Option<&Value>,
214 mut node: Node,
215) -> Result<Either<Node, Option<Value>>, Error> {
216 let Some(head) = node.list().first().and_then(Node::as_label).cloned() else {
217 return Err(eval_error(adapter, machine, "expected label"));
218 };
219
220 let prefix = head.as_prefix();
221
222 if prefix == Some(b"fn") {
223 return match func::call(adapter, machine, stdin, node, head.as_name())? {
224 Either::Right(p) => Ok(Either::Right(Some(p))),
225 Either::Left(_) => Err(eval_error_data(
226 adapter,
227 machine,
228 "unknown func",
229 head.as_name(),
230 )),
231 };
232 }
233
234 if prefix.is_none() && !machine.map.contains_key(head.as_name()) {
235 match func::call(adapter, machine, stdin, node, head.as_name())? {
236 Either::Right(p) => return Ok(Either::Right(Some(p))),
237 Either::Left(l) => node = l,
238 }
239 }
240
241 if let Some(prefix) = prefix.filter(|p| !p.is_empty()) {
242 return Err(eval_error_data(adapter, machine, "unknown prefix", prefix));
243 }
244
245 Ok(Either::Left(node))
246}
247
248fn eval_bytes(adapter: &impl Adapter, span: Span, node: Vec<u8>) -> Value {
249 let tracer = Tracer::default()
250 .with_source(adapter.template_source().unwrap_or_default())
251 .with_span(span);
252
253 Value::from(node).with_tracer(tracer)
254}
255
256fn eval_tag(adapter: &impl Adapter, span: Span, node: Vec<u8>) -> Value {
257 eval_bytes(adapter, span, node).with_safe()
258}
259
260fn eval_list(
261 adapter: &impl Adapter,
262 machine: &mut Machine<'_>,
263 stdin: Option<&Value>,
264 node: Node,
265) -> Result<Option<Value>, Error> {
266 let tracer = Tracer::default()
267 .with_source(adapter.template_source().unwrap_or_default())
268 .with_span(Span::from(&node));
269
270 let list = node
271 .into_list()
272 .unwrap_or_default()
273 .into_iter()
274 .filter_map(|n| match n {
275 Node::Bytes(s, n) => Some(Ok(Some(eval_bytes(adapter, s, n)))),
276 Node::Tag(s, n) => Some(Ok(Some(eval_tag(adapter, s, n)))),
277 Node::Label(..) => Some(eval_context_label(adapter, machine, stdin, &n).map(Some)),
278 Node::List(..) => Some(eval_list(adapter, machine, stdin, n)),
279 Node::Map(..) => Some(eval_map(adapter, machine, stdin, n)),
280 Node::Pipe(..) => Some(eval_node_list(adapter, machine, stdin, n)),
281 Node::Paren(..) => Some(eval_node_list(adapter, machine, stdin, n)),
282 Node::Bracket(..) => Some(eval_node_list(adapter, machine, stdin, n)),
283 Node::And(..) => Some(eval_node_list(adapter, machine, stdin, n)),
284 Node::Or(..) => Some(eval_node_list(adapter, machine, stdin, n)),
285 Node::Semicolon(..) => Some(eval_node_list(adapter, machine, stdin, n)),
286 Node::Thunk(..) => Some(eval_node_list(adapter, machine, stdin, n)),
287 Node::Space(_) => Some(Err(eval_error(adapter, machine, "unexpected space"))),
288 Node::Comma(_) => None,
289 })
290 .collect::<Result<Vec<_>, _>>()?
291 .into_iter()
292 .map(Option::unwrap_or_default)
293 .collect::<List>();
294
295 Ok(Some(Value::from(list).with_tracer(tracer)))
296}
297
298fn eval_map(
299 adapter: &impl Adapter,
300 machine: &mut Machine<'_>,
301 stdin: Option<&Value>,
302 node: Node,
303) -> Result<Option<Value>, Error> {
304 let tracer = Tracer::default()
305 .with_source(adapter.template_source().unwrap_or_default())
306 .with_span(Span::from(&node));
307
308 let list = node.into_list().unwrap_or_default();
309 let mut split = Vec::with_capacity(list.len());
310 let mut map = Map::default();
311 let mut key = None;
312
313 for node in list {
315 if let Some(l) = node.as_label() {
316 if let Some(p) = l.as_prefix().filter(|l| !l.is_empty()) {
317 if let Some(s) = l.as_suffix().filter(|l| !l.is_empty()) {
318 split.push(parse::parse(
319 adapter.template_source(),
320 Span::from(&node),
321 p,
322 )?);
323 split.push(parse::parse(
324 adapter.template_source(),
325 Span::from(&node),
326 s,
327 )?);
328 continue;
329 }
330 }
331 }
332 split.push(node);
333 }
334
335 for node in split {
337 match (core::mem::take(&mut key), node) {
338 (None, Node::Comma(..)) => {}
339 (None, n) => key = Some(n),
340 (Some(k), Node::Comma(..)) => map.fold_key(
341 keys_from_node_default(adapter, machine, k.clone())?.join(),
342 eval_node(adapter, machine, stdin, k)?,
343 ),
344 (Some(k), n) => {
345 map.fold_key(
346 keys_from_node_default(adapter, machine, k.clone())?.join(),
347 eval_node(adapter, machine, stdin, n)?,
348 );
349 }
350 }
351 }
352
353 if let Some(k) = key {
355 match k {
356 Node::Bytes(..) | Node::Label(..) => map.fold_key(
357 keys_from_node_default(adapter, machine, k.clone())?.join(),
358 eval_node(adapter, machine, stdin, k)?,
359 ),
360 _ => return Err(eval_error(adapter, machine, "bad map syntax")),
361 }
362 }
363
364 Ok(Some(Value::from(map).with_tracer(tracer)))
365}
366
367pub(crate) fn eval_concat(
368 adapter: &impl Adapter,
369 machine: &mut Machine<'_>,
370 stdin: Option<&Value>,
371 node: &Node,
372) -> Result<Option<Value>, Error> {
373 let mut values = eval_spaced(adapter, machine, stdin, false, node)?;
374
375 if values.len() == 1 {
376 return Ok(values.remove(0));
377 }
378
379 let value = values.into_iter().flatten().collect::<List>();
380 let value = Value::from(value).with_meta("internal-format", "concat");
381
382 Ok(Some(value))
383}
384
385pub(crate) fn eval_spaced(
386 adapter: &impl Adapter,
387 machine: &mut Machine<'_>,
388 stdin: Option<&Value>,
389 stdin_append: bool,
390 node: &Node,
391) -> Result<Vec<Option<Value>>, Error> {
392 let groups = group_spaced_nodes(node.list());
393
394 let mut values = groups
395 .into_iter()
396 .filter(|n| !n.is_empty())
397 .map(pipe_from_nodes)
398 .map(|n| eval_node_list(adapter, machine, stdin, n))
399 .collect::<Result<Vec<_>, _>>()?;
400
401 if stdin_append && stdin.is_some() && should_append_stdin(node) {
402 values.push(stdin.cloned());
403 }
404
405 Ok(values)
406}
407
408fn group_spaced_nodes(list: &[Node]) -> Vec<Vec<Node>> {
409 let mut groups = Vec::with_capacity(list.len());
410 let mut group = Vec::default();
411
412 let mut push = |group: &mut Vec<Node>| {
413 if !group.is_empty() {
414 groups.push(core::mem::take(group));
415 }
416 };
417
418 for node in list {
419 match node {
420 Node::Space(_) => {
421 push(&mut group);
422 }
423 node @ Node::Tag(..) => {
424 push(&mut group);
425 group.push(node.clone());
426 push(&mut group);
427 }
428 node => {
429 group.push(node.clone());
430 }
431 }
432 }
433
434 push(&mut group);
435 groups
436}
437
438fn eval_list_getter(
439 adapter: &impl Adapter,
440 machine: &mut Machine<'_>,
441 stdin: Option<&Value>,
442 mut node: Node,
443) -> Result<Option<Value>, Error> {
444 let Some(literal) = node.try_remove(0) else {
445 return Ok(None);
446 };
447
448 let literal = eval_list(adapter, machine, stdin, literal)?;
449 eval_literal_getter(adapter, machine, literal.as_ref(), node)
450}
451
452fn eval_map_getter(
453 adapter: &impl Adapter,
454 machine: &mut Machine<'_>,
455 stdin: Option<&Value>,
456 mut node: Node,
457) -> Result<Option<Value>, Error> {
458 let Some(literal) = node.try_remove(0) else {
459 return Ok(None);
460 };
461
462 let literal = eval_map(adapter, machine, stdin, literal)?;
463 eval_literal_getter(adapter, machine, literal.as_ref(), node)
464}
465
466fn eval_literal_getter(
467 adapter: &impl Adapter,
468 machine: &mut Machine<'_>,
469 literal: Option<&Value>,
470 node: Node,
471) -> Result<Option<Value>, Error> {
472 if node.list().is_empty() {
473 return Err(eval_error(adapter, machine, "missing keys"));
474 }
475
476 let node = node.into_list().unwrap_or_default();
477
478 let Some(keys) = keys_from_node_list(adapter, machine, node)? else {
479 return Ok(None);
480 };
481
482 Ok(match literal {
483 Some(l) => get_value(l, keys.as_slice()).cloned(),
484 None => get_value(&Value::default(), keys.as_slice()).cloned(),
485 })
486}
487
488pub(crate) fn eval_context_getter(
489 adapter: &impl Adapter,
490 machine: &mut Machine<'_>,
491 stdin: Option<&Value>,
492 node: Node,
493) -> Result<Option<Value>, Error> {
494 let node = node.into_list().unwrap_or_default();
495
496 let Some(keys) = keys_from_node_list(adapter, machine, node)? else {
497 return Ok(None);
498 };
499
500 trace_getter(adapter, machine, stdin, keys).map(Some)
501}
502
503fn eval_context_label(
504 adapter: &impl Adapter,
505 machine: &mut Machine<'_>,
506 stdin: Option<&Value>,
507 node: &Node,
508) -> Result<Value, Error> {
509 let Some(keys) = node.as_label().map(Label::as_keys) else {
510 return Err(eval_error(adapter, machine, "expected keys from label"));
511 };
512
513 trace_getter(adapter, machine, stdin, keys)
514}
515
516fn trace_getter(
517 adapter: &impl Adapter,
518 machine: &mut Machine<'_>,
519 stdin: Option<&Value>,
520 mut keys: Keys,
521) -> Result<Value, Error> {
522 let mut stdin = stdin.map(Cow::Borrowed);
523
524 if keys.is_placeholder() {
525 return Ok(stdin.map(Cow::into_owned).unwrap_or_default());
526 }
527
528 if keys.is_context_spread() {
529 return Ok(machine.map.clone().into());
530 }
531
532 if keys.has_leading_placeholder() {
533 keys = keys.without_first();
534 stdin = stdin
535 .map(Cow::into_owned)
536 .map(Value::into_map)
537 .map(Value::from)
538 .map(Cow::Owned);
539 }
540
541 let value = match stdin.as_deref() {
542 Some(Value::Map(stdin)) => adapter.value_get(stdin, &keys),
543 Some(Value::List(stdin)) => keys.as_usize().and_then(|k| stdin.get(k)).cloned(),
544 Some(Value::Bytes(_)) => None,
545 None => None,
546 };
547
548 let mut value = value
549 .or_else(|| adapter.value_get(machine.map, &keys))
550 .unwrap_or_default();
551
552 let tracer = value
553 .take_tracer()
554 .with_source(adapter.template_source().unwrap_or_default())
555 .with_keys(keys.clone());
556 let value = value.with_tracer(tracer);
557
558 match adapter.record_getter(&value) {
559 Err(e) => return Err(eval_error(adapter, machine, &e)),
560 Ok(Some(o)) => machine.ops.push(o),
561 Ok(None) => {}
562 }
563
564 Ok(value)
565}
566
567pub(crate) fn record_setter(
568 adapter: &impl Adapter,
569 machine: &mut Machine<'_>,
570 keys: &Keys,
571 mut value: Value,
572) -> Result<Value, Error> {
573 let tracer = value
574 .take_tracer()
575 .with_source(adapter.template_source().unwrap_or_default())
576 .with_segment(Segment::Setter)
577 .with_keys(keys.clone());
578
579 let value = value.with_tracer(tracer);
580 fold_map_value(machine.map, keys, value.clone());
581
582 if let Some(op) = adapter
583 .record_setter(&value)
584 .map_err(|e| eval_error(adapter, machine, &e))?
585 {
586 machine.ops.push(op);
587 }
588
589 Ok(value)
590}
591
592pub(crate) fn replace_operation(
593 adapter: &impl Adapter,
594 machine: &mut Machine<'_>,
595 keys: &Keys,
596 prev: &Value,
597 next: &Value,
598) -> Result<(), Error> {
599 debug_assert_eq!(prev.tracer(), next.tracer());
600 insert_map_value(machine.map, keys, next.clone());
601
602 let ops = adapter
603 .erase_operation(prev)
604 .map_err(|e| eval_error(adapter, machine, &e))?;
605
606 for op in ops {
607 adapter
608 .record_operation(&op.with_value(next.clone()))
609 .map_err(|e| eval_error(adapter, machine, &e))?;
610 }
611
612 Ok(())
613}
614
615pub(crate) fn keys_from_node(
616 adapter: &impl Adapter,
617 machine: &mut Machine<'_>,
618 node: Node,
619) -> Result<Option<Keys>, Error> {
620 match node {
621 Node::Space(_) => Err(eval_error(adapter, machine, "unexpected space")),
622 Node::Comma(_) => Err(eval_error(adapter, machine, "unexpected comma")),
623 Node::List(..) => Err(eval_error(adapter, machine, "unexpected list")),
624 Node::Map(..) => Err(eval_error(adapter, machine, "unexpected map")),
625 Node::Bytes(_, n) => Ok(Some(Keys::parse(n.as_slice()))),
626 Node::Tag(_, n) => Ok(Some(Keys::parse(n.as_slice()))),
627 Node::Label(_, n) => Ok(Some(n.as_keys())),
628 Node::Pipe(_, n) => Ok(keys_from_node_list(adapter, machine, n)?),
629 Node::Paren(_, n) => Ok(keys_from_node_list(adapter, machine, n)?),
630 Node::Bracket(_, n) => Ok(keys_from_node_list(adapter, machine, n)?),
631 Node::And(_, n) => Ok(keys_from_node_list(adapter, machine, n)?),
632 Node::Or(_, n) => Ok(keys_from_node_list(adapter, machine, n)?),
633 Node::Semicolon(_, n) => Ok(keys_from_node_list(adapter, machine, n)?),
634 Node::Thunk(_, n) => Ok(keys_from_node_list(adapter, machine, n)?),
635 }
636}
637
638pub(crate) fn keys_from_node_list(
639 adapter: &impl Adapter,
640 machine: &mut Machine<'_>,
641 list: Vec<Node>,
642) -> Result<Option<Keys>, Error> {
643 let node = list.into_iter().map(|n| match n {
644 Node::Space(_) => Err(eval_error(adapter, machine, "unexpected space")),
645 Node::List(..) => Err(eval_error(adapter, machine, "unexpected list")),
646 Node::Map(..) => Err(eval_error(adapter, machine, "unexpected map")),
647 Node::Thunk(..) => Err(eval_error(adapter, machine, "unexpected thunk")),
648 Node::Bytes(_, n) => Ok(Keys::parse(n.as_slice())),
649 Node::Label(_, n) => Ok(n.as_keys()),
650 _ if n.list().is_empty() => Err(eval_error(adapter, machine, "empty keys")),
651 _ => {
652 let mut e = eval_machine(adapter, machine, n)?;
653 machine.ops.append(&mut e.ops);
654 Ok(Keys::new(e.result.join()))
655 }
656 });
657
658 match Keys::try_from(node.collect::<Result<Vec<_>, _>>()?) {
659 Ok(keys) => Ok(Some(keys)),
660 Err(_) => Ok(None),
661 }
662}
663
664fn keys_from_node_default(
665 adapter: &impl Adapter,
666 machine: &mut Machine<'_>,
667 node: Node,
668) -> Result<Keys, Error> {
669 Ok(keys_from_node(adapter, machine, node)?.unwrap_or_default())
670}
671
672fn has_space(node: &Node) -> bool {
673 node.list().iter().any(|n| matches!(n, Node::Space(..)))
674}
675
676fn has_tag(node: &Node) -> bool {
677 node.list().iter().any(|n| matches!(n, Node::Tag(..)))
678}
679
680fn has_pipe(node: &Node) -> bool {
681 node.list().iter().any(Node::is_pipe)
682}
683
684fn eval_error_data<T: Into<String>>(
685 adapter: &impl Adapter,
686 machine: &Machine<'_>,
687 message: T,
688 data: &[u8],
689) -> Error {
690 eval_error(
691 adapter,
692 machine,
693 format!("{}: {}", message.into(), String::from_utf8_lossy(data)),
694 )
695}
696
697fn thunk_error(adapter: &impl Adapter, machine: &Machine<'_>) -> Error {
698 eval_error(
699 adapter,
700 machine,
701 "first-class functions not yet implemented",
702 )
703}
704
705fn eval_error<T: Into<String>>(adapter: &impl Adapter, machine: &Machine<'_>, message: T) -> Error {
706 Error {
707 kind: ErrorKind::Eval,
708 source: adapter.template_source().map(String::from),
709 span: machine.span,
710 message: message.into(),
711 }
712}