1use crate::{
5 builtin::*, diag::*, eval::*, model::*, rc::*, resolve::*, syntax::*, tree_display::*,
6};
7
8pub struct EvalContext {
12 root: Symbol,
14 sources: Sources,
16 pub(super) stack: Stack,
18 output: Box<dyn Output>,
20 exporters: ExporterRegistry,
22 importers: ImporterRegistry,
24 pub diag: DiagHandler,
26}
27
28impl EvalContext {
29 pub fn new(
31 resolve_context: ResolveContext,
32 output: Box<dyn Output>,
33 exporters: ExporterRegistry,
34 importers: ImporterRegistry,
35 ) -> Self {
36 log::debug!("Creating evaluation context");
37
38 Self {
39 root: resolve_context.root,
40 sources: resolve_context.sources,
41 diag: resolve_context.diag,
42 output,
43 exporters,
44 importers,
45 ..Default::default()
46 }
47 }
48
49 pub(crate) fn current_symbol(&self) -> Symbol {
51 self.stack.current_symbol().expect("Some symbol")
52 }
53
54 pub fn from_source(
56 root: Rc<SourceFile>,
57 builtin: Option<Symbol>,
58 search_paths: &[impl AsRef<std::path::Path>],
59 output: Box<dyn Output>,
60 exporters: ExporterRegistry,
61 importers: ImporterRegistry,
62 line_offset: usize,
63 ) -> EvalResult<Self> {
64 Ok(Self::new(
65 ResolveContext::create(root, search_paths, builtin, DiagHandler::new(line_offset))?,
66 output,
67 exporters,
68 importers,
69 ))
70 }
71
72 pub fn output(&self) -> Option<String> {
74 self.output.output()
75 }
76
77 pub fn print(&mut self, what: String) {
79 self.output.print(what).expect("could not write to output");
80 }
81
82 pub fn eval(&mut self) -> EvalResult<Option<Model>> {
84 if self.diag.error_count() > 0 {
85 log::error!("Aborting evaluation because of prior resolve errors!");
86 return Err(EvalError::ResolveFailed);
87 }
88 let model: Model = self.sources.root().eval(self)?;
89 log::trace!("Post-evaluation context:\n{self:?}");
90 log::trace!("Evaluated Model:\n{}", FormatTree(&model));
91
92 let unused = self
93 .root
94 .unused_private()
95 .iter()
96 .map(|symbol| {
97 (
98 match self.sources.get_code(&symbol) {
99 Ok(id) => id,
100 Err(_) => symbol.id().to_string(),
101 },
102 symbol.src_ref(),
103 )
104 })
105 .collect::<indexmap::IndexMap<_, _>>();
107
108 unused.into_iter().try_for_each(|(id, src_ref)| {
109 self.warning(&src_ref, EvalError::UnusedGlobalSymbol(id))
110 })?;
111
112 if model.has_no_output() {
113 Ok(None)
115 } else {
116 Ok(Some(model))
117 }
118 }
119
120 pub(super) fn scope<T>(
122 &mut self,
123 stack_frame: StackFrame,
124 f: impl FnOnce(&mut EvalContext) -> T,
125 ) -> T {
126 self.open(stack_frame);
127 let result = f(self);
128 let mut unused: Vec<_> = if let Some(frame) = &self.stack.current_frame() {
129 if let Some(locals) = frame.locals() {
130 locals
131 .iter()
132 .filter(|(_, symbol)| !symbol.is_used())
133 .filter(|(id, _)| !id.ignore())
134 .filter(|(_, symbol)| !symbol.src_ref().is_none())
135 .map(|(id, _)| id.clone())
136 .collect()
137 } else {
138 vec![]
139 }
140 } else {
141 vec![]
142 };
143 unused.sort();
144
145 unused
146 .iter()
147 .try_for_each(|id| self.warning(id, EvalError::UnusedLocal(id.clone())))
148 .expect("diag error");
149
150 self.close();
151 result
152 }
153
154 pub fn exporters(&self) -> &ExporterRegistry {
156 &self.exporters
157 }
158
159 pub fn search_paths(&self) -> &Vec<std::path::PathBuf> {
161 self.sources.search_paths()
162 }
163
164 pub(super) fn get_property(&self, id: &Identifier) -> EvalResult<Value> {
166 match self.get_model() {
167 Ok(model) => {
168 if let Some(value) = model.get_property(id) {
169 Ok(value.clone())
170 } else {
171 Err(EvalError::PropertyNotFound(id.clone()))
172 }
173 }
174 Err(err) => Err(err),
175 }
176 }
177
178 pub(super) fn init_property(&self, id: Identifier, value: Value) -> EvalResult<()> {
182 match self.get_model() {
183 Ok(model) => {
184 if let Some(previous_value) = model.borrow_mut().set_property(id.clone(), value) {
185 if !previous_value.is_invalid() {
186 return Err(EvalError::ValueAlreadyDefined {
187 location: id.src_ref(),
188 name: id.clone(),
189 value: previous_value.to_string(),
190 previous_location: id.src_ref(),
191 });
192 }
193 }
194 Ok(())
195 }
196 Err(err) => Err(err),
197 }
198 }
199
200 pub(super) fn is_init(&mut self) -> bool {
202 matches!(self.stack.current_frame(), Some(StackFrame::Init(_)))
203 }
204
205 pub(super) fn is_within_function(&self) -> bool {
206 self.stack.is_within_function()
207 }
208
209 fn lookup_property(&self, name: &QualifiedName) -> EvalResult<Symbol> {
211 log::trace!(
212 "{lookup} for property {name:?}",
213 lookup = crate::mark!(LOOKUP)
214 );
215 self.root.deny_super(name)?;
216
217 if self.stack.current_workbench_name().is_some() {
218 if let Some(id) = name.single_identifier() {
219 match self.get_property(id) {
220 Ok(value) => {
221 log::trace!("{found} property '{name:?}'", found = crate::mark!(FOUND));
222 return Ok(Symbol::new(
223 SymbolDef::Constant(Visibility::Public, id.clone(), value),
224 None,
225 ));
226 }
227 Err(err) => return Err(err),
228 }
229 }
230 }
231 log::trace!(
232 "{not_found} Property '{name:?}'",
233 not_found = crate::mark!(NOT_FOUND)
234 );
235 Err(EvalError::NoPropertyId(name.clone()))
236 }
237
238 fn lookup_workbench(
239 &self,
240 name: &QualifiedName,
241 target: LookupTarget,
242 ) -> ResolveResult<Symbol> {
243 if let Some(workbench) = &self.stack.current_workbench_name() {
244 log::trace!(
245 "{lookup} for symbol '{name:?}' in current workbench '{workbench:?}'",
246 lookup = crate::mark!(LOOKUP)
247 );
248 self.deny_super(name)?;
249 match self.root.lookup_within_name(name, workbench, target) {
250 Ok(symbol) => {
251 log::trace!(
252 "{found} symbol in current module: {symbol:?}",
253 found = crate::mark!(FOUND),
254 );
255 Ok(symbol)
256 }
257 Err(err) => {
258 log::trace!(
259 "{not_found} symbol '{name:?}': {err}",
260 not_found = crate::mark!(NOT_FOUND)
261 );
262 Err(err)
263 }
264 }
265 } else {
266 log::trace!(
267 "{not_found} No current workbench",
268 not_found = crate::mark!(NOT_FOUND)
269 );
270 Err(ResolveError::SymbolNotFound(name.clone()))
271 }
272 }
273
274 fn is_code(&self) -> bool {
276 !matches!(self.stack.current_frame(), Some(StackFrame::Module(..)))
277 }
278
279 pub(crate) fn is_module(&self) -> bool {
281 matches!(
282 self.stack.current_frame(),
283 Some(StackFrame::Module(..) | StackFrame::Source(..))
284 )
285 }
286
287 fn lookup_within(&self, name: &QualifiedName, target: LookupTarget) -> ResolveResult<Symbol> {
288 self.root.lookup_within(
289 name,
290 &self.root.search(&self.stack.current_module_name(), false)?,
291 target,
292 )
293 }
294
295 pub fn root(&self) -> &Symbol {
297 &self.root
298 }
299}
300
301impl UseLocally for EvalContext {
302 fn use_symbol(&mut self, name: &QualifiedName, id: Option<Identifier>) -> EvalResult<Symbol> {
303 log::debug!("Using symbol {name:?}");
304
305 let symbol = self.lookup(name, LookupTarget::Any)?;
306 if self.is_code() {
307 self.stack.put_local(id, symbol.clone())?;
308 log::trace!("Local Stack:\n{:?}", self.stack);
309 }
310
311 Ok(symbol)
312 }
313
314 fn use_symbols_of(&mut self, name: &QualifiedName) -> EvalResult<Symbol> {
315 log::debug!("Using all symbols in {name:?}");
316
317 let symbol = self.lookup(name, LookupTarget::Any)?;
318 if symbol.is_empty() {
319 Err(EvalError::NoSymbolsToUse(symbol.full_name()))
320 } else {
321 if self.is_code() {
322 symbol.try_children(|(id, symbol)| {
323 self.stack.put_local(Some(id.clone()), symbol.clone())
324 })?;
325 log::trace!("Local Stack:\n{:?}", self.stack);
326 }
327 Ok(symbol)
328 }
329 }
330}
331
332impl Locals for EvalContext {
333 fn set_local_value(&mut self, id: Identifier, value: Value) -> EvalResult<()> {
334 self.stack.set_local_value(id, value)
335 }
336
337 fn get_local_value(&self, id: &Identifier) -> EvalResult<Value> {
338 self.stack.get_local_value(id)
339 }
340
341 fn open(&mut self, frame: StackFrame) {
342 self.stack.open(frame);
343 }
344
345 fn close(&mut self) -> StackFrame {
346 self.stack.close()
347 }
348
349 fn fetch_symbol(&self, id: &Identifier) -> EvalResult<Symbol> {
350 self.stack.fetch_symbol(id)
351 }
352
353 fn get_model(&self) -> EvalResult<Model> {
354 self.stack.get_model()
355 }
356
357 fn current_name(&self) -> QualifiedName {
358 self.stack.current_name()
359 }
360}
361
362impl Default for EvalContext {
363 fn default() -> Self {
364 Self {
365 root: Default::default(),
366 sources: Default::default(),
367 stack: Default::default(),
368 output: Stdout::new(),
369 exporters: Default::default(),
370 importers: Default::default(),
371 diag: Default::default(),
372 }
373 }
374}
375
376impl Lookup<EvalError> for EvalContext {
377 fn lookup(&self, name: &QualifiedName, target: LookupTarget) -> EvalResult<Symbol> {
378 log::debug!("Lookup {target} '{name:?}' (at line {:?}):", name.src_ref());
379
380 log::trace!("- lookups -------------------------------------------------------");
381 let results = [
383 ("local", { self.stack.lookup(name, target) }),
384 ("global", {
385 self.lookup_within(name, target).map_err(|err| err.into())
386 }),
387 ("property", { self.lookup_property(name) }),
388 ("workbench", {
389 self.lookup_workbench(name, target)
390 .map_err(|err| err.into())
391 }),
392 ]
393 .into_iter();
394
395 log::trace!("- lookup results ------------------------------------------------");
396 let results = results.inspect(|(from, result)| log::trace!("{from}: {:?}", result));
397
398 let (found, mut ambiguities, mut errors) = results.fold(
400 (vec![], vec![], vec![]),
401 |(mut oks, mut ambiguities, mut errors), (origin, result)| {
402 match result {
403 Ok(symbol) => oks.push((origin, symbol)),
404 Err(EvalError::AmbiguousSymbol( ambiguous, others)) => {
405 ambiguities.push((origin, EvalError::AmbiguousSymbol ( ambiguous, others )))
406 }
407 Err(
408 EvalError::SymbolNotFound(_)
410 | EvalError::LocalNotFound(_)
412 | EvalError::NoModelInWorkbench
414 | EvalError::PropertyNotFound(_)
415 | EvalError::NoPropertyId(_)
416 | EvalError::ResolveError(ResolveError::SymbolNotFound(_))
418 | EvalError::ResolveError(ResolveError::ExternalPathNotFound(_))
419 | EvalError::ResolveError(ResolveError::SymbolIsPrivate(_))
420 | EvalError::ResolveError(ResolveError::NulHash)
421 | EvalError::ResolveError(ResolveError::WrongTarget),
422 ) => (),
423 Err(err) => errors.push((origin, err)),
424 }
425 (oks, ambiguities, errors)
426 },
427 );
428
429 if !errors.is_empty() {
431 log::error!("Unexpected errors while lookup symbol '{name:?}':");
432 errors
433 .iter()
434 .for_each(|(origin, err)| log::error!("Lookup ({origin}) error: {err}"));
435
436 return Err(errors.remove(0).1);
437 }
438
439 if !ambiguities.is_empty() {
441 log::debug!(
442 "{ambiguous} Symbol '{name:?}':\n{}",
443 ambiguities
444 .iter()
445 .map(|(origin, err)| format!("{origin}: {err}"))
446 .collect::<Vec<_>>()
447 .join("\n"),
448 ambiguous = crate::mark!(AMBIGUOUS)
449 );
450 return Err(ambiguities.remove(0).1);
451 }
452
453 let found: Vec<_> = found
455 .iter()
456 .filter(|(_, symbol)| target.matches(symbol))
457 .collect();
458
459 match found.first() {
461 Some((origin, symbol)) => {
462 if found.iter().all(|(_, x)| x == symbol) {
464 log::debug!(
465 "{found} symbol '{name:?}' in {origin}",
466 found = crate::mark!(FOUND!)
467 );
468 symbol.set_used();
469 Ok(symbol.clone())
470 } else {
471 let others: QualifiedNames =
472 found.iter().map(|(_, symbol)| symbol.full_name()).collect();
473 log::debug!(
474 "{ambiguous} symbol '{name:?}' in {others:?}:\n{self:?}",
475 ambiguous = crate::mark!(AMBIGUOUS),
476 );
477 Err(EvalError::AmbiguousSymbol(name.clone(), others))
478 }
479 }
480 None => {
481 log::debug!(
482 "{not_found} Symbol '{name:?}'",
483 not_found = crate::mark!(NOT_FOUND!)
484 );
485 Err(EvalError::SymbolNotFound(name.clone()))
486 }
487 }
488 }
489
490 fn ambiguity_error(ambiguous: QualifiedName, others: QualifiedNames) -> EvalError {
491 EvalError::AmbiguousSymbol(ambiguous, others)
492 }
493}
494
495impl Diag for EvalContext {
496 fn fmt_diagnosis(&self, f: &mut dyn std::fmt::Write) -> std::fmt::Result {
497 self.diag.pretty_print(f, self)
498 }
499
500 fn warning_count(&self) -> u32 {
501 self.diag.warning_count()
502 }
503
504 fn error_count(&self) -> u32 {
505 self.diag.error_count()
506 }
507
508 fn error_lines(&self) -> std::collections::HashSet<usize> {
509 self.diag.error_lines()
510 }
511
512 fn warning_lines(&self) -> std::collections::HashSet<usize> {
513 self.diag.warning_lines()
514 }
515}
516
517impl PushDiag for EvalContext {
518 fn push_diag(&mut self, diag: Diagnostic) -> DiagResult<()> {
519 let result = self.diag.push_diag(diag);
520 log::trace!("Error Context:\n{self:?}");
521 #[cfg(debug_assertions)]
522 if std::env::var("MICROCAD_ERROR_PANIC").is_ok() {
523 eprintln!("{}", self.diagnosis());
524 panic!("MICROCAD_ERROR_PANIC")
525 }
526 result
527 }
528}
529
530impl GetSourceByHash for EvalContext {
531 fn get_by_hash(&self, hash: u64) -> ResolveResult<Rc<SourceFile>> {
532 self.sources.get_by_hash(hash)
533 }
534}
535
536impl std::fmt::Debug for EvalContext {
537 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
538 if let Ok(model) = self.get_model() {
539 write!(f, "\nModel:\n")?;
540 model.tree_print(f, TreeState::new_debug(4))?;
541 }
542 writeln!(f, "\nCurrent: {:?}", self.stack.current_name())?;
543 writeln!(f, "\nModule: {:?}", self.stack.current_module_name())?;
544 write!(f, "\nLocals Stack:\n{:?}", self.stack)?;
545 writeln!(f, "\nCall Stack:")?;
546 self.stack.pretty_print_call_trace(f, &self.sources)?;
547
548 writeln!(f, "\nSources:\n")?;
549 write!(f, "{:?}", &self.sources)?;
550
551 write!(f, "\nSymbol Table:\n")?;
552 self.root.tree_print(f, TreeState::new_debug(0))?;
553
554 match self.error_count() {
555 0 => write!(f, "No errors")?,
556 1 => write!(f, "1 error")?,
557 _ => write!(f, "{} errors", self.error_count())?,
558 };
559 match self.warning_count() {
560 0 => writeln!(
561 f,
562 ", no warnings{}",
563 if self.error_count() > 0 { ":" } else { "." }
564 )?,
565 1 => writeln!(f, ", 1 warning:")?,
566 _ => writeln!(f, ", {} warnings:", self.warning_count())?,
567 };
568 self.fmt_diagnosis(f)?;
569 Ok(())
570 }
571}
572
573impl ImporterRegistryAccess for EvalContext {
574 type Error = EvalError;
575
576 fn import(
577 &mut self,
578 arg_map: &Tuple,
579 search_paths: &[std::path::PathBuf],
580 ) -> Result<Value, Self::Error> {
581 match self.importers.import(arg_map, search_paths) {
582 Ok(value) => Ok(value),
583 Err(err) => {
584 self.error(arg_map, err)?;
585 Ok(Value::None)
586 }
587 }
588 }
589}
590
591impl ExporterAccess for EvalContext {
592 fn exporter_by_id(&self, id: &crate::Id) -> Result<Rc<dyn Exporter>, ExportError> {
593 self.exporters.exporter_by_id(id)
594 }
595
596 fn exporter_by_filename(
597 &self,
598 filename: &std::path::Path,
599 ) -> Result<Rc<dyn Exporter>, ExportError> {
600 self.exporters.exporter_by_filename(filename)
601 }
602}