1use crate::{
5 builtin::*, diag::*, eval::*, model::*, rc::*, resolve::*, syntax::*, tree_display::*,
6};
7
8pub struct EvalContext {
12 symbol_table: SymbolTable,
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 symbol_table: resolve_context.symbol_table,
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 self.symbol_table
93 .unused_private()
94 .iter()
95 .try_for_each(|symbol| {
96 self.warning(
97 &symbol.src_ref(),
98 EvalError::UnusedGlobalSymbol(match self.sources.get_code(&symbol) {
99 Ok(id) => id,
100 Err(_) => symbol.id().to_string(),
101 }),
102 )
103 })?;
104
105 if model.has_no_output() {
106 Ok(None)
108 } else {
109 Ok(Some(model))
110 }
111 }
112
113 pub(super) fn scope<T>(
115 &mut self,
116 stack_frame: StackFrame,
117 f: impl FnOnce(&mut EvalContext) -> T,
118 ) -> T {
119 self.open(stack_frame);
120 let result = f(self);
121 let mut unused: Vec<_> = if let Some(frame) = &self.stack.current_frame() {
122 if let Some(locals) = frame.locals() {
123 locals
124 .iter()
125 .filter(|(_, symbol)| !symbol.is_used())
126 .filter(|(id, _)| !id.ignore())
127 .filter(|(_, symbol)| !symbol.src_ref().is_none())
128 .map(|(id, _)| id.clone())
129 .collect()
130 } else {
131 vec![]
132 }
133 } else {
134 vec![]
135 };
136 unused.sort();
137
138 unused
139 .iter()
140 .try_for_each(|id| self.warning(id, EvalError::UnusedLocal(id.clone())))
141 .expect("diag error");
142
143 self.close();
144 result
145 }
146
147 pub fn exporters(&self) -> &ExporterRegistry {
149 &self.exporters
150 }
151
152 pub fn search_paths(&self) -> &Vec<std::path::PathBuf> {
154 self.sources.search_paths()
155 }
156
157 pub(super) fn get_property(&self, id: &Identifier) -> EvalResult<Value> {
159 match self.get_model() {
160 Ok(model) => {
161 if let Some(value) = model.get_property(id) {
162 Ok(value.clone())
163 } else {
164 Err(EvalError::PropertyNotFound(id.clone()))
165 }
166 }
167 Err(err) => Err(err),
168 }
169 }
170
171 pub(super) fn init_property(&self, id: Identifier, value: Value) -> EvalResult<()> {
175 match self.get_model() {
176 Ok(model) => {
177 if let Some(previous_value) = model.borrow_mut().set_property(id.clone(), value) {
178 if !previous_value.is_invalid() {
179 return Err(EvalError::ValueAlreadyDefined(
180 id.clone(),
181 previous_value.to_string(),
182 id.src_ref(),
183 ));
184 }
185 }
186 Ok(())
187 }
188 Err(err) => Err(err),
189 }
190 }
191
192 pub(super) fn is_init(&mut self) -> bool {
194 matches!(self.stack.current_frame(), Some(StackFrame::Init(_)))
195 }
196
197 pub(super) fn is_within_function(&self) -> bool {
198 self.stack.is_within_function()
199 }
200
201 fn lookup_property(&self, name: &QualifiedName) -> EvalResult<Symbol> {
203 log::trace!(
204 "{lookup} for property {name:?}",
205 lookup = crate::mark!(LOOKUP)
206 );
207 self.symbol_table.deny_super(name)?;
208
209 if self.stack.current_workbench_name().is_some() {
210 if let Some(id) = name.single_identifier() {
211 match self.get_property(id) {
212 Ok(value) => {
213 log::trace!(
214 "{found} property '{name:?}'",
215 found = crate::mark!(FOUND_INTERIM)
216 );
217 return Ok(Symbol::new(
218 SymbolDef::Constant(Visibility::Public, id.clone(), value),
219 None,
220 ));
221 }
222 Err(err) => return Err(err),
223 }
224 }
225 }
226 log::trace!(
227 "{not_found} Property '{name:?}'",
228 not_found = crate::mark!(NOT_FOUND_INTERIM)
229 );
230 Err(EvalError::NoPropertyId(name.clone()))
231 }
232
233 fn lookup_workbench(
234 &self,
235 name: &QualifiedName,
236 target: LookupTarget,
237 ) -> ResolveResult<Symbol> {
238 if let Some(workbench) = &self.stack.current_workbench_name() {
239 log::trace!(
240 "{lookup} for symbol '{name:?}' in current workbench '{workbench:?}'",
241 lookup = crate::mark!(LOOKUP)
242 );
243 self.deny_super(name)?;
244 match self
245 .symbol_table
246 .lookup_within_name(name, workbench, target)
247 {
248 Ok(symbol) => {
249 log::trace!(
250 "{found} symbol in current module: {symbol:?}",
251 found = crate::mark!(FOUND_INTERIM),
252 );
253 Ok(symbol)
254 }
255 Err(err) => {
256 log::trace!(
257 "{not_found} symbol '{name:?}': {err}",
258 not_found = crate::mark!(NOT_FOUND_INTERIM)
259 );
260 Err(err)
261 }
262 }
263 } else {
264 log::trace!(
265 "{not_found} No current workbench",
266 not_found = crate::mark!(NOT_FOUND_INTERIM)
267 );
268 Err(ResolveError::SymbolNotFound(name.clone()))
269 }
270 }
271
272 fn is_code(&self) -> bool {
274 !matches!(self.stack.current_frame(), Some(StackFrame::Module(..)))
275 }
276
277 pub(crate) fn is_module(&self) -> bool {
279 matches!(
280 self.stack.current_frame(),
281 Some(StackFrame::Module(..) | StackFrame::Source(..))
282 )
283 }
284
285 fn lookup_within(&self, name: &QualifiedName, target: LookupTarget) -> ResolveResult<Symbol> {
286 self.symbol_table.lookup_within(
287 name,
288 &self
289 .symbol_table
290 .search(&self.stack.current_module_name(), false)?,
291 target,
292 )
293 }
294
295 pub fn symbol_table(&self) -> &SymbolTable {
297 &self.symbol_table
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 symbol_table: 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{:?}", self.symbol_table)?;
552 match self.error_count() {
553 0 => write!(f, "No errors")?,
554 1 => write!(f, "1 error")?,
555 _ => write!(f, "{} errors", self.error_count())?,
556 };
557 match self.warning_count() {
558 0 => writeln!(
559 f,
560 ", no warnings{}",
561 if self.error_count() > 0 { ":" } else { "." }
562 )?,
563 1 => writeln!(f, ", 1 warning:")?,
564 _ => writeln!(f, ", {} warnings:", self.warning_count())?,
565 };
566 self.fmt_diagnosis(f)?;
567 Ok(())
568 }
569}
570
571impl ImporterRegistryAccess for EvalContext {
572 type Error = EvalError;
573
574 fn import(
575 &mut self,
576 arg_map: &Tuple,
577 search_paths: &[std::path::PathBuf],
578 ) -> Result<Value, Self::Error> {
579 match self.importers.import(arg_map, search_paths) {
580 Ok(value) => Ok(value),
581 Err(err) => {
582 self.error(arg_map, err)?;
583 Ok(Value::None)
584 }
585 }
586 }
587}
588
589impl ExporterAccess for EvalContext {
590 fn exporter_by_id(&self, id: &crate::Id) -> Result<Rc<dyn Exporter>, ExportError> {
591 self.exporters.exporter_by_id(id)
592 }
593
594 fn exporter_by_filename(
595 &self,
596 filename: &std::path::Path,
597 ) -> Result<Rc<dyn Exporter>, ExportError> {
598 self.exporters.exporter_by_filename(filename)
599 }
600}