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