bend/imp/
order_kwargs.rs

1use crate::{
2  fun::{parser::ParseBook, Name},
3  imp::{Definition, Expr, Stmt},
4};
5use indexmap::IndexMap;
6
7impl Definition {
8  /// Traverses the program's definitions and adjusts the order of keyword arguments
9  /// in call/constructor expressions to match the order specified in the function or constructor definition.
10  pub fn order_kwargs(&mut self, book: &ParseBook) -> Result<(), String> {
11    let use_map = &mut IndexMap::new();
12    self.body.order_kwargs(book, use_map).map_err(|e| format!("In function '{}':\n  {}", self.name, e))
13  }
14}
15
16impl Stmt {
17  fn order_kwargs(&mut self, book: &ParseBook, use_map: &mut IndexMap<Name, Name>) -> Result<(), String> {
18    match self {
19      Stmt::LocalDef { def, nxt } => {
20        def.order_kwargs(book)?;
21        nxt.order_kwargs(book, use_map)?;
22      }
23      Stmt::Assign { val, nxt, .. } => {
24        val.order_kwargs(book, use_map)?;
25        if let Some(nxt) = nxt {
26          nxt.order_kwargs(book, use_map)?;
27        }
28      }
29      Stmt::Ask { val, nxt, .. } => {
30        val.order_kwargs(book, use_map)?;
31        if let Some(nxt) = nxt {
32          nxt.order_kwargs(book, use_map)?;
33        }
34      }
35      Stmt::InPlace { val, nxt, .. } => {
36        val.order_kwargs(book, use_map)?;
37        nxt.order_kwargs(book, use_map)?;
38      }
39      Stmt::If { cond, then, otherwise, nxt } => {
40        cond.order_kwargs(book, use_map)?;
41        then.order_kwargs(book, use_map)?;
42        otherwise.order_kwargs(book, use_map)?;
43        if let Some(nxt) = nxt {
44          nxt.order_kwargs(book, use_map)?;
45        }
46      }
47      Stmt::Match { arg, arms, nxt, .. } => {
48        arg.order_kwargs(book, use_map)?;
49        for arm in arms {
50          arm.rgt.order_kwargs(book, use_map)?;
51        }
52        if let Some(nxt) = nxt {
53          nxt.order_kwargs(book, use_map)?;
54        }
55      }
56      Stmt::Switch { arg, arms, nxt, .. } => {
57        arg.order_kwargs(book, use_map)?;
58        for arm in arms {
59          arm.order_kwargs(book, use_map)?;
60        }
61        if let Some(nxt) = nxt {
62          nxt.order_kwargs(book, use_map)?;
63        }
64      }
65      Stmt::Fold { arg, arms, nxt, .. } => {
66        arg.order_kwargs(book, use_map)?;
67        for arm in arms {
68          arm.rgt.order_kwargs(book, use_map)?;
69        }
70        if let Some(nxt) = nxt {
71          nxt.order_kwargs(book, use_map)?;
72        }
73      }
74      Stmt::Bend { bnd: _, arg, cond, step, base, nxt } => {
75        for arg in arg {
76          arg.order_kwargs(book, use_map)?;
77        }
78        cond.order_kwargs(book, use_map)?;
79        step.order_kwargs(book, use_map)?;
80        base.order_kwargs(book, use_map)?;
81        if let Some(nxt) = nxt {
82          nxt.order_kwargs(book, use_map)?;
83        }
84      }
85      Stmt::With { typ: _, bod, nxt } => {
86        bod.order_kwargs(book, use_map)?;
87        if let Some(nxt) = nxt {
88          nxt.order_kwargs(book, use_map)?;
89        }
90      }
91      Stmt::Open { typ: _, var: _, nxt } => {
92        nxt.order_kwargs(book, use_map)?;
93      }
94      Stmt::Use { nam, val: bod, nxt } => {
95        if let Expr::Var { nam: bod } = bod.as_ref() {
96          use_map.insert(nam.clone(), bod.clone());
97          nxt.order_kwargs(book, use_map)?;
98          use_map.pop();
99        } else {
100          bod.order_kwargs(book, use_map)?;
101          nxt.order_kwargs(book, use_map)?;
102        }
103      }
104      Stmt::Return { term } => term.order_kwargs(book, use_map)?,
105      Stmt::Err => {}
106    }
107    Ok(())
108  }
109}
110
111impl Expr {
112  fn order_kwargs(&mut self, book: &ParseBook, use_map: &mut IndexMap<Name, Name>) -> Result<(), String> {
113    match self {
114      // Named arguments are only allowed when directly calling a named function.
115      Expr::Call { fun, args, kwargs } => {
116        if !kwargs.is_empty() {
117          if let Expr::Var { nam } = fun.as_ref() {
118            if let Some(names) = get_args_def_or_ctr(nam, book, use_map) {
119              go_order_kwargs(&names, args, kwargs)?;
120            } else {
121              return Err(format!(
122                "Named args are only allowed when calling a named function, not when calling variable '{nam}'."
123              ));
124            }
125          } else {
126            // TODO: Print expression
127            return Err(
128              "Named args are only allowed when calling a named function, not when calling an expression."
129                .to_string(),
130            );
131          }
132        }
133        fun.order_kwargs(book, use_map)?;
134        for arg in args {
135          arg.order_kwargs(book, use_map)?;
136        }
137        for (_, arg) in kwargs {
138          arg.order_kwargs(book, use_map)?;
139        }
140      }
141      Expr::Lam { bod, .. } => bod.order_kwargs(book, use_map)?,
142      Expr::Opr { lhs, rhs, .. } => {
143        lhs.order_kwargs(book, use_map)?;
144        rhs.order_kwargs(book, use_map)?;
145      }
146      Expr::Lst { els } | Expr::Tup { els } | Expr::Sup { els } => {
147        for el in els {
148          el.order_kwargs(book, use_map)?;
149        }
150      }
151      Expr::LstMap { term, iter, cond, .. } => {
152        term.order_kwargs(book, use_map)?;
153        iter.order_kwargs(book, use_map)?;
154        if let Some(cond) = cond {
155          cond.order_kwargs(book, use_map)?;
156        }
157      }
158      Expr::Ctr { name, args, kwargs } => match get_args_def_or_ctr(name, book, use_map) {
159        Some(names) => {
160          go_order_kwargs(&names, args, kwargs)?;
161          for arg in args {
162            arg.order_kwargs(book, use_map)?;
163          }
164        }
165        _ => return Err(format!("Constructor '{name}' not found.")),
166      },
167      Expr::Map { entries } => {
168        for entry in entries {
169          entry.1.order_kwargs(book, use_map)?;
170        }
171      }
172      Expr::MapGet { nam: _, key } => {
173        key.order_kwargs(book, use_map)?;
174      }
175      Expr::TreeNode { left, right } => {
176        left.order_kwargs(book, use_map)?;
177        right.order_kwargs(book, use_map)?;
178      }
179      Expr::TreeLeaf { val } => {
180        val.order_kwargs(book, use_map)?;
181      }
182      Expr::Era | Expr::Var { .. } | Expr::Chn { .. } | Expr::Num { .. } | Expr::Str { .. } => {}
183    }
184    Ok(())
185  }
186}
187
188fn go_order_kwargs(
189  names: &[Name],
190  args: &mut Vec<Expr>,
191  kwargs: &mut Vec<(Name, Expr)>,
192) -> Result<(), String> {
193  if args.len() + kwargs.len() != names.len() {
194    return Err(
195      "Named args are only allowed when calling a function with the exact number of arguments.".to_string(),
196    );
197  }
198  let mut kwargs: IndexMap<Name, Expr> = IndexMap::from_iter(kwargs.drain(..));
199  let remaining_names = &names[args.len()..];
200  for name in remaining_names {
201    if let Some(arg) = kwargs.shift_remove(name) {
202      args.push(arg);
203    } else {
204      return Err(format!("Named arg '{name}' is missing."));
205    }
206  }
207  if let Some(name) = kwargs.keys().next() {
208    return Err(format!("Unexpected named arg in function call {}.", name));
209  }
210  Ok(())
211}
212
213fn get_args_def_or_ctr(name: &Name, book: &ParseBook, use_map: &IndexMap<Name, Name>) -> Option<Vec<Name>> {
214  let name = use_map.get(name).unwrap_or(name);
215
216  #[allow(clippy::manual_map)]
217  if let Some(adt_nam) = book.ctrs.get(name) {
218    Some(book.adts[adt_nam].ctrs[name].fields.iter().map(|f| f.nam.clone()).collect())
219  } else if let Some(def) = book.fun_defs.get(name) {
220    Some(def.rules[0].pats.iter().flat_map(|p| p.binds().flatten().cloned()).collect())
221  } else {
222    None
223  }
224}