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 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 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 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}