1use std::clone::Clone;
2use std::collections::HashMap;
3use std::convert::AsRef;
4use std::convert::Into;
5use std::error::Error;
6use std::rc::Rc;
7
8use crate::ast::Position;
9use crate::ast::PositionedItem;
10use crate::build::ir::Val;
11use crate::error;
12
13pub fn find_in_fieldlist(target: &str, fs: &Vec<(String, Rc<Val>)>) -> Option<Rc<Val>> {
14 for (key, val) in fs.iter().cloned() {
15 if target == &key {
16 return Some(val.clone());
17 }
18 }
19 return None;
20}
21
22pub type ValueMap = HashMap<PositionedItem<String>, Rc<Val>>;
24
25#[derive(Debug, PartialEq, Clone)]
34pub struct Scope {
35 pub import_stack: Vec<String>,
36 pub env: Rc<Val>,
37 pub curr_val: Option<Rc<Val>>,
38 pub build_output: ValueMap,
39 pub search_curr_val: bool,
40 pub strict: bool,
41}
42
43impl Scope {
44 pub fn new(env: Rc<Val>) -> Self {
46 Self {
47 import_stack: Vec::new(),
48 env: env,
49 curr_val: None,
52 build_output: HashMap::new(),
53 search_curr_val: false,
54 strict: false,
55 }
56 }
57
58 pub fn use_strict(mut self) -> Self {
59 self.strict = true;
60 self
61 }
62
63 pub fn use_curr_val(mut self) -> Self {
64 self.search_curr_val = true;
65 self
66 }
67
68 pub fn spawn_child(&self) -> Self {
71 Self {
72 import_stack: self.import_stack.clone(),
73 env: self.env.clone(),
74 curr_val: None,
76 build_output: self.build_output.clone(),
77 search_curr_val: false,
78 strict: self.strict,
79 }
80 }
81
82 pub fn spawn_clean(&self) -> Self {
83 Self {
84 import_stack: self.import_stack.clone(),
85 env: self.env.clone(),
86 curr_val: None,
88 build_output: HashMap::new(),
89 search_curr_val: false,
90 strict: self.strict,
91 }
92 }
93
94 pub fn push_import<S: Into<String>>(&mut self, path: S) {
96 self.import_stack.push(path.into());
97 }
98
99 pub fn prepend_import_stack(&mut self, imports: &Vec<String>) {
100 let mut new_stack = self.import_stack.clone();
101 new_stack.append(imports.clone().as_mut());
102 self.import_stack = new_stack;
103 }
104
105 pub fn set_curr_val(mut self, val: Rc<Val>) -> Self {
107 self.curr_val = Some(val);
108 self
109 }
110
111 pub fn lookup_idx(&self, pos: &Position, idx: &Val) -> Result<Rc<Val>, Box<dyn Error>> {
113 if self.search_curr_val && self.curr_val.is_some() {
114 if let &Val::List(ref fs) = self.curr_val.as_ref().unwrap().as_ref() {
115 return Self::lookup_in_list(pos, idx, fs);
116 }
117 }
118 Err(error::BuildError::with_pos(
119 "Not a list in index lookup.",
120 error::ErrorType::TypeFail,
121 pos.clone(),
122 )
123 .to_boxed())
124 }
125
126 pub fn lookup_sym(&self, sym: &PositionedItem<String>, is_symbol: bool) -> Option<Rc<Val>> {
136 if &sym.val == "env" && is_symbol {
137 return Some(self.env.clone());
138 }
139 if &sym.val == "self" && is_symbol {
140 return self.curr_val.clone();
141 }
142 if self.search_curr_val && self.curr_val.is_some() {
143 match self.curr_val.as_ref().unwrap().as_ref() {
144 Val::Env(ref fs) => {
145 for (name, val) in fs.iter() {
146 if name == &sym.val {
147 return Some(Rc::new(Val::Str(val.clone())));
148 }
149 }
150 if !self.strict {
151 return Some(Rc::new(Val::Empty));
152 }
153 }
154 Val::Tuple(ref fs) => match Self::lookup_in_tuple(&sym.pos, &sym.val, fs) {
155 Ok(v) => return Some(v),
156 Err(_) => {
157 }
159 },
160 Val::List(ref fs) => {
161 match Self::lookup_in_list(&sym.pos, &Val::Str(sym.val.clone()), fs) {
162 Ok(v) => return Some(v),
163 Err(_) => {
164 }
166 }
167 }
168 Val::Boolean(_) | Val::Empty | Val::Float(_) | Val::Int(_) | Val::Str(_) => {
169 }
171 };
172 }
173 if self.build_output.contains_key(sym) {
174 return Some(self.build_output[sym].clone());
175 }
176 None
177 }
178
179 fn lookup_in_tuple(
180 pos: &Position,
181 field: &str,
182 fs: &Vec<(String, Rc<Val>)>,
183 ) -> Result<Rc<Val>, Box<dyn Error>> {
184 if let Some(vv) = find_in_fieldlist(&field, fs) {
185 Ok(vv)
186 } else {
187 Err(error::BuildError::with_pos(
188 format!("Unable to {} match element in tuple.", field,),
189 error::ErrorType::NoSuchSymbol,
190 pos.clone(),
191 )
192 .to_boxed())
193 }
194 }
195
196 fn lookup_in_list(
197 pos: &Position,
198 field: &Val,
199 elems: &Vec<Rc<Val>>,
200 ) -> Result<Rc<Val>, Box<dyn Error>> {
201 let idx = match field {
202 &Val::Int(i) => i as usize,
203 &Val::Str(ref s) => s.parse::<usize>()?,
204 _ => {
205 return Err(error::BuildError::with_pos(
206 format!("Invalid idx type {} for list lookup", field),
207 error::ErrorType::TypeFail,
208 pos.clone(),
209 )
210 .to_boxed());
211 }
212 };
213 if idx < elems.len() {
214 Ok(elems[idx].clone())
215 } else {
216 Err(error::BuildError::with_pos(
217 format!("idx {} out of bounds in list", idx),
218 error::ErrorType::NoSuchSymbol,
219 pos.clone(),
220 )
221 .to_boxed())
222 }
223 }
224}