1use std::collections::HashMap;
2use std::sync::Arc;
3use std::sync::RwLock;
4
5use cirru_parser::Cirru;
6
7use crate::data::cirru::code_to_calcit;
8use crate::primes::{Calcit, ImportRule};
9use crate::snapshot;
10use crate::snapshot::Snapshot;
11use crate::util::string::extract_pkg_from_ns;
12
13pub type ProgramEvaledData = HashMap<Arc<str>, HashMap<Arc<str>, Calcit>>;
14
15#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct ProgramFileData {
18 pub import_map: HashMap<Arc<str>, Arc<ImportRule>>,
19 pub defs: HashMap<Arc<str>, Calcit>,
20}
21
22type ImportMapPair = (Arc<str>, Arc<ImportRule>);
23
24pub type ProgramCodeData = HashMap<Arc<str>, ProgramFileData>;
25
26lazy_static! {
27 static ref PROGRAM_EVALED_DATA_STATE: RwLock<ProgramEvaledData> = RwLock::new(HashMap::new());
29 pub static ref PROGRAM_CODE_DATA: RwLock<ProgramCodeData> = RwLock::new(HashMap::new());
31}
32
33fn extract_import_rule(nodes: &Cirru) -> Result<Vec<ImportMapPair>, String> {
34 match nodes {
35 Cirru::Leaf(_) => Err(String::from("Expected import rule in expr")),
36 Cirru::List(rule_nodes) => {
37 let mut xs = rule_nodes.to_owned();
38 match xs.get(0) {
39 Some(Cirru::Leaf(s)) if &**s == "[]" => xs = xs[1..4].to_vec(),
41 _ => (),
42 }
43 match (&xs[0], &xs[1], &xs[2]) {
44 (Cirru::Leaf(ns), x, Cirru::Leaf(alias)) if *x == Cirru::leaf(":as") => Ok(vec![(
45 (*alias.to_owned()).into(),
46 Arc::new(ImportRule::NsAs((*ns.to_owned()).into())),
47 )]),
48 (Cirru::Leaf(ns), x, Cirru::Leaf(alias)) if *x == Cirru::leaf(":default") => Ok(vec![(
49 (*alias.to_owned()).into(),
50 Arc::new(ImportRule::NsDefault((*ns.to_owned()).into())),
51 )]),
52 (Cirru::Leaf(ns), x, Cirru::List(ys)) if *x == Cirru::leaf(":refer") => {
53 let mut rules: Vec<(Arc<str>, Arc<ImportRule>)> = Vec::with_capacity(ys.len());
54 for y in ys {
55 match y {
56 Cirru::Leaf(s) if &**s == "[]" => (), Cirru::Leaf(s) => rules.push((
58 (*s.to_owned()).into(),
59 Arc::new(ImportRule::NsReferDef((*ns.to_owned()).into(), (*s.to_owned()).into())),
60 )),
61 Cirru::List(_defs) => return Err(String::from("invalid refer values")),
62 }
63 }
64 Ok(rules)
65 }
66 (_, x, _) if *x == Cirru::leaf(":as") => Err(String::from("invalid import rule")),
67 (_, x, _) if *x == Cirru::leaf(":default") => Err(String::from("invalid default rule")),
68 (_, x, _) if *x == Cirru::leaf(":refer") => Err(String::from("invalid import rule")),
69 _ if xs.len() != 3 => Err(format!("expected import rule has length 3: {}", Cirru::List(xs.to_owned()))),
70 _ => Err(String::from("unknown rule")),
71 }
72 }
73 }
74}
75
76fn extract_import_map(nodes: &Cirru) -> Result<HashMap<Arc<str>, Arc<ImportRule>>, String> {
77 match nodes {
78 Cirru::Leaf(_) => unreachable!("Expected expr for ns"),
79 Cirru::List(xs) => match (xs.get(0), xs.get(1), xs.get(2)) {
80 (Some(x), Some(Cirru::Leaf(_)), Some(Cirru::List(xs))) if *x == Cirru::leaf("ns") => {
82 if !xs.is_empty() && xs[0] == Cirru::leaf(":require") {
83 let mut ys: HashMap<Arc<str>, Arc<ImportRule>> = HashMap::with_capacity(xs.len());
84 for (idx, x) in xs.iter().enumerate() {
85 if idx > 0 {
86 let rules = extract_import_rule(x)?;
87 for (target, rule) in rules {
88 ys.insert(target, rule);
89 }
90 }
91 }
92 Ok(ys)
93 } else {
94 Ok(HashMap::new())
95 }
96 }
97 _ if xs.len() < 3 => Ok(HashMap::new()),
98 _ => Err(String::from("invalid ns form")),
99 },
100 }
101}
102
103fn extract_file_data(file: &snapshot::FileInSnapShot, ns: Arc<str>) -> Result<ProgramFileData, String> {
104 let import_map = extract_import_map(&file.ns)?;
105 let mut defs: HashMap<Arc<str>, Calcit> = HashMap::with_capacity(file.defs.len());
106 for (def, code) in &file.defs {
107 let at_def = def.to_owned();
108 defs.insert(def.to_owned(), code_to_calcit(code, ns.to_owned(), at_def)?);
109 }
110 Ok(ProgramFileData { import_map, defs })
111}
112
113pub fn extract_program_data(s: &Snapshot) -> Result<ProgramCodeData, String> {
114 let mut xs: ProgramCodeData = HashMap::with_capacity(s.files.len());
115 for (ns, file) in &s.files {
116 let file_info = extract_file_data(file, ns.to_owned())?;
117 xs.insert(ns.to_owned(), file_info);
118 }
119 Ok(xs)
120}
121
122pub fn has_def_code(ns: &str, def: &str) -> bool {
124 let program_code = { PROGRAM_CODE_DATA.read().unwrap() };
125 match program_code.get(ns) {
126 Some(v) => v.defs.contains_key(def),
127 None => false,
128 }
129}
130
131pub fn lookup_def_code(ns: &str, def: &str) -> Option<Calcit> {
132 let program_code = { PROGRAM_CODE_DATA.read().unwrap() };
133 let file = program_code.get(ns)?;
134 let data = file.defs.get(def)?;
135 Some(data.to_owned())
136}
137
138pub fn lookup_def_target_in_import(ns: &str, def: &str) -> Option<Arc<str>> {
139 let program = { PROGRAM_CODE_DATA.read().unwrap() };
140 let file = program.get(ns)?;
141 let import_rule = file.import_map.get(def)?;
142 match &**import_rule {
143 ImportRule::NsReferDef(ns, _def) => Some(ns.to_owned()),
144 ImportRule::NsAs(_ns) => None,
145 ImportRule::NsDefault(_ns) => None,
146 }
147}
148
149pub fn lookup_ns_target_in_import(ns: Arc<str>, alias: &str) -> Option<Arc<str>> {
150 let program = { PROGRAM_CODE_DATA.read().unwrap() };
151 let file = program.get(&*ns)?;
152 let import_rule = file.import_map.get(alias)?;
153 match &**import_rule {
154 ImportRule::NsReferDef(_ns, _def) => None,
155 ImportRule::NsAs(ns) => Some(ns.to_owned()),
156 ImportRule::NsDefault(_ns) => None,
157 }
158}
159
160pub fn lookup_default_target_in_import(ns: &str, alias: &str) -> Option<Arc<str>> {
162 let program = { PROGRAM_CODE_DATA.read().unwrap() };
163 let file = program.get(ns)?;
164 let import_rule = file.import_map.get(alias)?;
165 match &**import_rule {
166 ImportRule::NsReferDef(_ns, _def) => None,
167 ImportRule::NsAs(_ns) => None,
168 ImportRule::NsDefault(ns) => Some(ns.to_owned()),
169 }
170}
171
172#[allow(dead_code)]
174pub fn has_evaled_def(ns: &str, def: &str) -> bool {
175 let s2 = PROGRAM_EVALED_DATA_STATE.read().unwrap();
176 s2.contains_key(ns) && s2[ns].contains_key(def)
177}
178
179pub fn lookup_evaled_def(ns: &str, def: &str) -> Option<Calcit> {
181 let s2 = PROGRAM_EVALED_DATA_STATE.read().unwrap();
182 if s2.contains_key(ns) && s2[ns].contains_key(def) {
183 Some(s2[ns][def].to_owned())
184 } else {
185 None
187 }
188}
189
190pub fn write_evaled_def(ns: &str, def: &str, value: Calcit) -> Result<(), String> {
192 let mut program = PROGRAM_EVALED_DATA_STATE.write().unwrap();
194 if !program.contains_key(ns) {
195 (*program).insert(String::from(ns).into(), HashMap::new());
196 }
197
198 let file = program.get_mut(ns).unwrap();
199 file.insert(String::from(def).into(), value);
200
201 Ok(())
202}
203
204pub fn clone_evaled_program() -> ProgramEvaledData {
206 let program = &PROGRAM_EVALED_DATA_STATE.read().unwrap();
207
208 let mut xs: ProgramEvaledData = HashMap::new();
209 for k in program.keys() {
210 xs.insert(k.to_owned(), program[k].to_owned());
211 }
212 xs
213}
214
215pub fn apply_code_changes(changes: &snapshot::ChangesDict) -> Result<(), String> {
216 let mut program_code = { PROGRAM_CODE_DATA.write().unwrap() };
217
218 for (ns, file) in &changes.added {
219 program_code.insert(ns.to_owned(), extract_file_data(file, ns.to_owned())?);
220 }
221 for ns in &changes.removed {
222 program_code.remove(ns);
223 }
224 for (ns, info) in &changes.changed {
225 let file = program_code.get_mut(ns).unwrap();
227 if info.ns.is_some() {
228 file.import_map = extract_import_map(&info.ns.to_owned().unwrap())?;
229 }
230 for (def, code) in &info.added_defs {
231 file
232 .defs
233 .insert(def.to_owned(), code_to_calcit(code, ns.to_owned(), def.to_owned())?);
234 }
235 for def in &info.removed_defs {
236 file.defs.remove(def);
237 }
238 for (def, code) in &info.changed_defs {
239 file
240 .defs
241 .insert(def.to_owned(), code_to_calcit(code, ns.to_owned(), def.to_owned())?);
242 }
243 }
244
245 Ok(())
246}
247
248pub fn clear_all_program_evaled_defs(init_ns: Arc<str>, reload_ns: Arc<str>, reload_libs: bool) -> Result<(), String> {
250 let mut program = PROGRAM_EVALED_DATA_STATE.write().unwrap();
251 if reload_libs {
252 (*program).clear();
253 } else {
254 let init_pkg = extract_pkg_from_ns(init_ns).unwrap();
256 let reload_pkg = extract_pkg_from_ns(reload_ns).unwrap();
257 let mut to_remove: Vec<Arc<str>> = vec![];
258 for k in (*program).keys() {
259 if k == &init_pkg || k == &reload_pkg || k.starts_with(&format!("{}.", init_pkg)) || k.starts_with(&format!("{}.", reload_pkg)) {
260 to_remove.push(k.to_owned());
261 } else {
262 continue;
263 }
264 }
265 for k in to_remove {
266 (*program).remove(&k);
267 }
268 }
269 Ok(())
270}