nreplops_tool/clojure/
result_ir.rs

1// clojure/result_ir.rs
2// Copyright 2024 Matti Hänninen
3//
4// Licensed under the Apache License, Version 2.0 (the "License"); you may not
5// use this file except in compliance with the License. You may obtain a copy of
6// the License at
7//
8//     http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13// License for the specific language governing permissions and limitations under
14// the License.
15
16//! Data structures and tools for our internal representation of Clojure result
17//! values.
18
19// XXX(soija) This whole thing is a hot mess.  Redesign it BUT NOT BEFORE trying
20//            to use it for splitting and rewriting the forms that are sent to
21//            the nREPL server.
22
23use super::lex::Lexeme;
24
25#[derive(Debug)]
26pub enum Value<'a> {
27  Nil,
28  Number {
29    literal: &'a str,
30  },
31  String {
32    literal: &'a str,
33  },
34  Boolean {
35    value: bool,
36  },
37  SymbolicValue {
38    literal: &'a str,
39  },
40  Symbol {
41    namespace: Option<&'a str>,
42    name: &'a str,
43  },
44  Keyword {
45    namespace: Option<&'a str>,
46    name: &'a str,
47    alias: bool,
48  },
49  TaggedLiteral {
50    namespace: Option<&'a str>,
51    name: &'a str,
52    value: Box<Value<'a>>,
53  },
54  VarQuoted {
55    namespace: Option<&'a str>,
56    name: &'a str,
57  },
58  List {
59    values: Box<[Value<'a>]>,
60  },
61
62  Vector {
63    values: Box<[Value<'a>]>,
64  },
65
66  Set {
67    values: Box<[Value<'a>]>,
68  },
69  Map {
70    entries: Box<[MapEntry<'a>]>,
71  },
72}
73
74#[derive(Debug)]
75pub struct MapEntry<'a> {
76  pub key: Value<'a>,
77  pub value: Value<'a>,
78}
79
80#[derive(Debug)]
81pub enum BuildError {
82  TooFewTopLevelValues,
83  TooManyTopLevelValues,
84  RunawayCollection,
85  InconsistentCollections,
86  IncompleteMapEntry,
87  ExpectedTagForTaggedLiteral,
88  ExpectedValueForTaggedLiteral,
89  IncompleteTaggedLiteral,
90  UnexpectedLiteralTag,
91  ExpectedSymbolForLiteralTag,
92  ExpectedSymbolForVarQuote,
93}
94
95pub fn build<'a>(lexemes: &[Lexeme<'a>]) -> Result<Value<'a>, BuildError> {
96  use Lexeme::*;
97  let mut b = Builder::new();
98
99  for lexeme in lexemes {
100    let mut composite_ready = match lexeme {
101      Whitespace { .. } | SymbolicValuePrefix { .. } => false, // ignore
102      Nil { .. } => b.add_to_composite(Value::Nil)?,
103      Boolean { value, .. } => {
104        b.add_to_composite(Value::Boolean { value: *value })?
105      }
106      Numeric { source, .. } => {
107        b.add_to_composite(Value::Number { literal: source })?
108      }
109      String { source, .. } => {
110        b.add_to_composite(Value::String { literal: source })?
111      }
112      SymbolicValue { source, .. } => {
113        b.add_to_composite(Value::SymbolicValue { literal: source })?
114      }
115      // NB: The tagged literal builder expects that the tag is passed on as a
116      //     symbol.  This way we don't need to add a separate "tag" value that
117      //     would stick out of the enum like a sore thumb.
118      Symbol {
119        namespace, name, ..
120      }
121      | Tag {
122        namespace, name, ..
123      } => b.add_to_composite(Value::Symbol {
124        name,
125        namespace: *namespace,
126      })?,
127      Keyword {
128        alias,
129        namespace,
130        name,
131        ..
132      } => b.add_to_composite(Value::Keyword {
133        name,
134        namespace: *namespace,
135        alias: *alias,
136      })?,
137      StartList { .. } => b.start(CompositeType::List)?,
138      EndList { .. } => b.end(CompositeType::List)?,
139      StartSet { .. } => b.start(CompositeType::Set)?,
140      EndSet { .. } => b.end(CompositeType::Set)?,
141      StartVector { .. } => b.start(CompositeType::Vector)?,
142      EndVector { .. } => b.end(CompositeType::Vector)?,
143      StartMap { .. } => b.start(CompositeType::Map)?,
144      EndMap { .. } => b.end(CompositeType::Map)?,
145      TaggedLiteral { .. } => b.start(CompositeType::TaggedLiteral)?,
146      VarQuote { .. } => b.start(CompositeType::VarQuoted)?,
147      unhandled => todo!("Missing rule for:\n{:#?}", unhandled),
148    };
149
150    while composite_ready {
151      composite_ready = b.build_composite()?;
152    }
153  }
154
155  b.build_top_level()
156}
157
158struct Builder<'a> {
159  stack: Vec<CompositeBuilder<'a>>,
160}
161
162impl<'a> Builder<'a> {
163  fn new() -> Self {
164    Self {
165      stack: vec![CompositeBuilder::new(CompositeType::TopLevel)],
166    }
167  }
168
169  fn start(
170    &mut self,
171    composite_type: CompositeType,
172  ) -> Result<bool, BuildError> {
173    self.stack.push(CompositeBuilder::new(composite_type));
174    Ok(false)
175  }
176
177  fn end(&mut self, composite_type: CompositeType) -> Result<bool, BuildError> {
178    if self.stack.last().unwrap().composite_type() == composite_type {
179      Ok(true)
180    } else {
181      Err(BuildError::InconsistentCollections)
182    }
183  }
184
185  fn add_to_composite(&mut self, value: Value<'a>) -> Result<bool, BuildError> {
186    self.stack.last_mut().unwrap().add(value)
187  }
188
189  fn build_composite(&mut self) -> Result<bool, BuildError> {
190    let b = self.stack.pop().unwrap();
191    self.add_to_composite(b.build()?)
192  }
193
194  fn build_top_level(mut self) -> Result<Value<'a>, BuildError> {
195    // We can unwrap safely ⇐ last one is a top-level builder and we have
196    // asserted the type of the other ones when popping them out of the
197    // builder stack.
198    let b = self.stack.pop().unwrap();
199    if b.composite_type() == CompositeType::TopLevel {
200      b.build()
201    } else {
202      Err(BuildError::RunawayCollection)
203    }
204  }
205}
206
207trait BuildComposite<'a> {
208  fn composite_type(&self) -> CompositeType;
209
210  /// Adds a contained value to the value being built.  Returns `true` if value
211  /// being built is complete and should be popped out.
212  fn add(&mut self, value: Value<'a>) -> Result<bool, BuildError>;
213
214  fn build(self) -> Result<Value<'a>, BuildError>;
215}
216
217/// The types of comosite values we recognize at the level of syntax.
218#[derive(Clone, Copy, Debug, PartialEq, Eq)]
219enum CompositeType {
220  List,
221  Vector,
222  Set,
223  Map,
224  /// A tagged literal
225  ///
226  /// The tag is guaranteed to be a symbol whereas the value can be any value.
227  TaggedLiteral,
228  /// A varquoted symbol
229  VarQuoted,
230  /// The top-level of the program (or the result)
231  ///
232  /// Currently limited to hold no more nor less than a single value.
233  TopLevel,
234}
235
236// The reason we have this intermediate builder instead of using `Box<dyn
237// BuildContainer<'a> + a>` is that moving the contained values out of turned
238// out to be surprisingly clunky. The extra boilerplate introduced by this
239// is annoying but at least it is concentrated in one place.
240//
241// XXX(soija) Okay, this keeps growing and is getting unbearably clunky.
242//
243enum CompositeBuilder<'a> {
244  TopLevel(TopLevelBuilder<'a>),
245  Seq(SeqBuilder<'a>),
246  Map(MapBuilder<'a>),
247  TaggedLiteral(TaggedLiteralBuilder<'a>),
248  VarQuoted(VarQuotedBuilder<'a>),
249}
250
251impl<'a> CompositeBuilder<'a> {
252  fn new(composite_type: CompositeType) -> Self {
253    use CompositeType as T;
254    match composite_type {
255      T::TopLevel => Self::TopLevel(TopLevelBuilder::new()),
256      T::List => Self::Seq(SeqBuilder::new(SeqType::List)),
257      T::Vector => Self::Seq(SeqBuilder::new(SeqType::Vector)),
258      T::Set => Self::Seq(SeqBuilder::new(SeqType::Set)),
259      T::Map => Self::Map(Default::default()),
260      T::TaggedLiteral => Self::TaggedLiteral(Default::default()),
261      T::VarQuoted => Self::VarQuoted(Default::default()),
262    }
263  }
264}
265
266impl<'a> BuildComposite<'a> for CompositeBuilder<'a> {
267  fn composite_type(&self) -> CompositeType {
268    match self {
269      CompositeBuilder::Seq(b) => b.composite_type(),
270      CompositeBuilder::Map(b) => b.composite_type(),
271      CompositeBuilder::TopLevel(b) => b.composite_type(),
272      CompositeBuilder::TaggedLiteral(b) => b.composite_type(),
273      CompositeBuilder::VarQuoted(b) => b.composite_type(),
274    }
275  }
276
277  fn add(&mut self, value: Value<'a>) -> Result<bool, BuildError> {
278    match self {
279      CompositeBuilder::Seq(b) => b.add(value),
280      CompositeBuilder::Map(b) => b.add(value),
281      CompositeBuilder::TopLevel(b) => b.add(value),
282      CompositeBuilder::TaggedLiteral(b) => b.add(value),
283      CompositeBuilder::VarQuoted(b) => b.add(value),
284    }
285  }
286
287  fn build(self) -> Result<Value<'a>, BuildError> {
288    match self {
289      CompositeBuilder::Seq(b) => b.build(),
290      CompositeBuilder::Map(b) => b.build(),
291      CompositeBuilder::TopLevel(b) => b.build(),
292      CompositeBuilder::TaggedLiteral(b) => b.build(),
293      CompositeBuilder::VarQuoted(b) => b.build(),
294    }
295  }
296}
297
298struct SeqBuilder<'a> {
299  seq_type: SeqType,
300  values: Vec<Value<'a>>,
301}
302
303impl<'a> SeqBuilder<'a> {
304  fn new(seq_type: SeqType) -> Self {
305    SeqBuilder {
306      seq_type,
307      values: Vec::new(),
308    }
309  }
310}
311
312impl<'a> BuildComposite<'a> for SeqBuilder<'a> {
313  fn composite_type(&self) -> CompositeType {
314    match self.seq_type {
315      SeqType::List => CompositeType::List,
316      SeqType::Vector => CompositeType::Vector,
317      SeqType::Set => CompositeType::Set,
318    }
319  }
320
321  fn add(&mut self, value: Value<'a>) -> Result<bool, BuildError> {
322    self.values.push(value);
323    Ok(false)
324  }
325
326  fn build(mut self) -> Result<Value<'a>, BuildError> {
327    self.values.shrink_to_fit();
328    let values = self.values.into_boxed_slice();
329    Ok(match self.seq_type {
330      SeqType::List => Value::List { values },
331      SeqType::Vector => Value::Vector { values },
332      SeqType::Set => Value::Set { values },
333    })
334  }
335}
336
337enum SeqType {
338  List,
339  Vector,
340  Set,
341}
342
343#[derive(Default)]
344enum TaggedLiteralBuilder<'a> {
345  #[default]
346  Empty,
347  WithTag {
348    namespace: Option<&'a str>,
349    name: &'a str,
350  },
351  WithTagAndValue {
352    namespace: Option<&'a str>,
353    name: &'a str,
354    value: Value<'a>,
355  },
356  Invalid,
357}
358
359impl<'a> BuildComposite<'a> for TaggedLiteralBuilder<'a> {
360  fn composite_type(&self) -> CompositeType {
361    CompositeType::TaggedLiteral
362  }
363
364  fn add(&mut self, value: Value<'a>) -> Result<bool, BuildError> {
365    use TaggedLiteralBuilder as B;
366    match std::mem::replace(self, B::Invalid) {
367      B::Empty => match value {
368        Value::Symbol { namespace, name } => {
369          *self = B::WithTag { namespace, name };
370          Ok(false)
371        }
372        _ => Err(BuildError::ExpectedSymbolForLiteralTag),
373      },
374      B::WithTag { namespace, name } => {
375        *self = B::WithTagAndValue {
376          namespace,
377          name,
378          value,
379        };
380        Ok(true)
381      }
382      // XXX(soija) This is probably a wrong error here.  We should probably
383      //            panic (unreachable) here.  FIXME: Write a test case and
384      //            figure this out.
385      _ => Err(BuildError::ExpectedValueForTaggedLiteral),
386    }
387  }
388
389  fn build(self) -> Result<Value<'a>, BuildError> {
390    if let TaggedLiteralBuilder::WithTagAndValue {
391      namespace,
392      name,
393      value,
394    } = self
395    {
396      Ok(Value::TaggedLiteral {
397        namespace,
398        name,
399        value: value.into(),
400      })
401    } else {
402      // XXX(soija) This is probably a wrong error here.  FIXME: Write a test
403      //            case and figure this out.
404      Err(BuildError::IncompleteTaggedLiteral)
405    }
406  }
407}
408
409#[derive(Default)]
410enum VarQuotedBuilder<'a> {
411  #[default]
412  Empty,
413  WithSymbol {
414    namespace: Option<&'a str>,
415    name: &'a str,
416  },
417  Invalid,
418}
419
420impl<'a> BuildComposite<'a> for VarQuotedBuilder<'a> {
421  fn composite_type(&self) -> CompositeType {
422    CompositeType::VarQuoted
423  }
424
425  fn add(&mut self, value: Value<'a>) -> Result<bool, BuildError> {
426    use VarQuotedBuilder as B;
427    match std::mem::replace(self, VarQuotedBuilder::Invalid) {
428      B::Empty => match value {
429        Value::Symbol { namespace, name } => {
430          *self = VarQuotedBuilder::WithSymbol { namespace, name };
431          Ok(true)
432        }
433        _ => Err(BuildError::ExpectedSymbolForVarQuote),
434      },
435      _ => unreachable!("should have triggered error or build"),
436    }
437  }
438
439  fn build(self) -> Result<Value<'a>, BuildError> {
440    if let VarQuotedBuilder::WithSymbol { namespace, name } = self {
441      Ok(Value::VarQuoted { namespace, name })
442    } else {
443      // But what if we ran out of lexemes?
444      unreachable!("should not have been triggered unless ready");
445    }
446  }
447}
448
449#[derive(Default)]
450struct MapBuilder<'a> {
451  key: Option<Value<'a>>,
452  entries: Vec<MapEntry<'a>>,
453}
454
455impl<'a> BuildComposite<'a> for MapBuilder<'a> {
456  fn composite_type(&self) -> CompositeType {
457    CompositeType::Map
458  }
459
460  fn add(&mut self, value: Value<'a>) -> Result<bool, BuildError> {
461    if let Some(key) = self.key.take() {
462      self.entries.push(MapEntry { key, value });
463    } else {
464      self.key = Some(value);
465    }
466    Ok(false)
467  }
468
469  fn build(mut self) -> Result<Value<'a>, BuildError> {
470    if self.key.is_none() {
471      self.entries.shrink_to_fit();
472      Ok(Value::Map {
473        entries: self.entries.into_boxed_slice(),
474      })
475    } else {
476      Err(BuildError::IncompleteMapEntry)
477    }
478  }
479}
480
481#[derive(Default)]
482struct TopLevelBuilder<'a> {
483  value: Option<Value<'a>>,
484}
485
486impl<'a> TopLevelBuilder<'a> {
487  fn new() -> Self {
488    Default::default()
489  }
490}
491
492impl<'a> BuildComposite<'a> for TopLevelBuilder<'a> {
493  fn composite_type(&self) -> CompositeType {
494    CompositeType::TopLevel
495  }
496
497  fn add(&mut self, value: Value<'a>) -> Result<bool, BuildError> {
498    if self.value.is_none() {
499      self.value = Some(value);
500      // XXX(soija) Actually, it would be consistent to return `true` in here
501      //            but the way the main building loop is implemented requires
502      //            that we return here false.  FIXME: do it right.
503      Ok(false)
504    } else {
505      Err(BuildError::TooManyTopLevelValues)
506    }
507  }
508
509  fn build(self) -> Result<Value<'a>, BuildError> {
510    self.value.ok_or(BuildError::TooFewTopLevelValues)
511  }
512}