xee_ir/
variables.rs

1use ahash::{HashMap, HashMapExt};
2use xee_interpreter::error::{self, Error};
3use xee_xpath_ast::{
4    ast::{self, Span},
5    span::Spanned,
6};
7
8use crate::{ir, Binding, Bindings};
9
10#[derive(Debug)]
11enum ContextItem {
12    Names(ir::ContextNames),
13    Absent,
14}
15
16#[derive(Debug, Default)]
17pub struct Variables {
18    counter: usize,
19    variables: HashMap<ast::Name, ir::Name>,
20    context_scope: Vec<ContextItem>,
21}
22
23impl Variables {
24    pub fn new() -> Self {
25        Self {
26            counter: 0,
27            variables: HashMap::new(),
28            context_scope: Vec::new(),
29        }
30    }
31
32    pub fn new_binding(&mut self, expr: ir::Expr, span: Span) -> Binding {
33        let name = self.new_name();
34        Binding::new(name, expr, span)
35    }
36
37    pub fn new_binding_no_span(&mut self, expr: ir::Expr) -> Binding {
38        let name = self.new_name();
39        Binding::new(name, expr, (0..0).into())
40    }
41
42    pub fn new_name(&mut self) -> ir::Name {
43        let name = format!("v{}", self.counter);
44        self.counter += 1;
45        ir::Name::new(name)
46    }
47
48    pub fn new_var_name(&mut self, name: &ast::Name) -> ir::Name {
49        self.variables.get(name).cloned().unwrap_or_else(|| {
50            let new_name = self.new_name();
51            self.variables.insert(name.clone(), new_name.clone());
52            new_name
53        })
54    }
55
56    pub fn push_context(&mut self) -> ir::ContextNames {
57        let names = ir::ContextNames {
58            item: self.new_name(),
59            position: self.new_name(),
60            last: self.new_name(),
61        };
62        self.context_scope.push(ContextItem::Names(names.clone()));
63        names
64    }
65
66    pub fn push_absent_context(&mut self) {
67        self.context_scope.push(ContextItem::Absent);
68    }
69
70    pub fn pop_context(&mut self) {
71        self.context_scope.pop();
72    }
73
74    pub fn explicit_context_names(&mut self, name: ir::Name) -> ir::ContextNames {
75        ir::ContextNames {
76            item: name,
77            position: self.new_name(),
78            last: self.new_name(),
79        }
80    }
81
82    pub fn var_ref(&mut self, name: &ast::Name, span: Span) -> error::SpannedResult<Bindings> {
83        let ir_name = self
84            .variables
85            .get(name)
86            .ok_or(Error::XPST0008.with_ast_span(span))?;
87        Ok(Bindings::new(Binding::new(
88            ir_name.clone(),
89            ir::Expr::Atom(Spanned::new(ir::Atom::Variable(ir_name.clone()), span)),
90            span,
91        )))
92    }
93
94    pub fn current_context_names(&self) -> Option<ir::ContextNames> {
95        match self.context_scope.last() {
96            Some(ContextItem::Names(names)) => Some(names.clone()),
97            Some(ContextItem::Absent) => None,
98            None => None,
99        }
100    }
101
102    // TODO: we're not using the span for error messages
103    fn context_name<F>(&mut self, get_name: F, span: Span) -> error::SpannedResult<Bindings>
104    where
105        F: Fn(&ir::ContextNames) -> ir::Name,
106    {
107        // TODO: could we get correct psna from ir_name?
108        let empty_span: Span = (0..0).into();
109        if let Some(context_scope) = self.context_scope.last() {
110            match context_scope {
111                ContextItem::Names(names) => {
112                    let ir_name = get_name(names);
113                    Ok(Bindings::new(Binding::new(
114                        ir_name.clone(),
115                        ir::Expr::Atom(Spanned::new(ir::Atom::Variable(ir_name), empty_span)),
116                        empty_span,
117                    )))
118                }
119                // we can detect statically that the context is absent if it's in
120                // a function definition
121                ContextItem::Absent => Err(Error::XPDY0002.with_ast_span(span)),
122            }
123        } else {
124            Err(Error::XPDY0002.with_ast_span(span))
125        }
126    }
127
128    pub fn context_item(&mut self, span: Span) -> error::SpannedResult<Bindings> {
129        self.context_name(|names| names.item.clone(), span)
130    }
131
132    pub fn fn_position(&mut self, span: Span) -> error::SpannedResult<Bindings> {
133        self.context_name(|names| names.position.clone(), span)
134    }
135
136    pub fn fn_last(&mut self, span: Span) -> error::SpannedResult<Bindings> {
137        self.context_name(|names| names.last.clone(), span)
138    }
139}