pub_just/
assignment_resolver.rs

1use {super::*, CompileErrorKind::*};
2
3pub struct AssignmentResolver<'src: 'run, 'run> {
4  assignments: &'run Table<'src, Assignment<'src>>,
5  stack: Vec<&'src str>,
6  evaluated: BTreeSet<&'src str>,
7}
8
9impl<'src: 'run, 'run> AssignmentResolver<'src, 'run> {
10  pub fn resolve_assignments(
11    assignments: &'run Table<'src, Assignment<'src>>,
12  ) -> CompileResult<'src> {
13    let mut resolver = Self {
14      stack: Vec::new(),
15      evaluated: BTreeSet::new(),
16      assignments,
17    };
18
19    for name in assignments.keys() {
20      resolver.resolve_assignment(name)?;
21    }
22
23    Ok(())
24  }
25
26  fn resolve_assignment(&mut self, name: &'src str) -> CompileResult<'src> {
27    if self.evaluated.contains(name) {
28      return Ok(());
29    }
30
31    self.stack.push(name);
32
33    if let Some(assignment) = self.assignments.get(name) {
34      for variable in assignment.value.variables() {
35        let name = variable.lexeme();
36
37        if self.evaluated.contains(name) || constants().contains_key(name) {
38          continue;
39        }
40
41        if self.stack.contains(&name) {
42          self.stack.push(name);
43          return Err(
44            self.assignments[name]
45              .name
46              .error(CircularVariableDependency {
47                variable: name,
48                circle: self.stack.clone(),
49              }),
50          );
51        } else if self.assignments.contains_key(name) {
52          self.resolve_assignment(name)?;
53        } else {
54          return Err(variable.error(UndefinedVariable { variable: name }));
55        }
56      }
57      self.evaluated.insert(name);
58    } else {
59      let message = format!("attempted to resolve unknown assignment `{name}`");
60      let token = Token {
61        src: "",
62        offset: 0,
63        line: 0,
64        column: 0,
65        length: 0,
66        kind: TokenKind::Unspecified,
67        path: "".as_ref(),
68      };
69      return Err(CompileError::new(token, Internal { message }));
70    }
71
72    self.stack.pop();
73
74    Ok(())
75  }
76}
77
78#[cfg(test)]
79mod tests {
80  use super::*;
81
82  analysis_error! {
83    name:   circular_variable_dependency,
84    input:   "a := b\nb := a",
85    offset:  0,
86    line:   0,
87    column: 0,
88    width:  1,
89    kind:   CircularVariableDependency{variable: "a", circle: vec!["a", "b", "a"]},
90  }
91
92  analysis_error! {
93    name:   self_variable_dependency,
94    input:  "a := a",
95    offset: 0,
96    line:   0,
97    column: 0,
98    width:  1,
99    kind:   CircularVariableDependency{variable: "a", circle: vec!["a", "a"]},
100  }
101
102  analysis_error! {
103    name:   unknown_expression_variable,
104    input:  "x := yy",
105    offset: 5,
106    line:   0,
107    column: 5,
108    width:  2,
109    kind:   UndefinedVariable{variable: "yy"},
110  }
111
112  analysis_error! {
113    name:   unknown_function_parameter,
114    input:  "x := env_var(yy)",
115    offset:  13,
116    line:   0,
117    column: 13,
118    width:  2,
119    kind:   UndefinedVariable{variable: "yy"},
120  }
121
122  analysis_error! {
123    name:   unknown_function_parameter_binary_first,
124    input:  "x := env_var_or_default(yy, 'foo')",
125    offset:  24,
126    line:   0,
127    column: 24,
128    width:  2,
129    kind:   UndefinedVariable{variable: "yy"},
130  }
131
132  analysis_error! {
133    name:   unknown_function_parameter_binary_second,
134    input:  "x := env_var_or_default('foo', yy)",
135    offset:  31,
136    line:   0,
137    column: 31,
138    width:  2,
139    kind:   UndefinedVariable{variable: "yy"},
140  }
141}