use std::path::PathBuf; use comemo::Track; use crate::model::{eval, Module, Route, Tracer, Value}; use crate::syntax::{ast, LinkedNode, Source, SyntaxKind}; use crate::util::PathExt; use crate::World; /// Try to determine a set of possible values for an expression. pub fn analyze_expr(world: &(dyn World + 'static), node: &LinkedNode) -> Vec { match node.cast::() { Some(ast::Expr::None(_)) => vec![Value::None], Some(ast::Expr::Auto(_)) => vec![Value::Auto], Some(ast::Expr::Bool(v)) => vec![Value::Bool(v.get())], Some(ast::Expr::Int(v)) => vec![Value::Int(v.get())], Some(ast::Expr::Float(v)) => vec![Value::Float(v.get())], Some(ast::Expr::Numeric(v)) => vec![Value::numeric(v.get())], Some(ast::Expr::Str(v)) => vec![Value::Str(v.get().into())], Some(ast::Expr::FieldAccess(access)) => { let Some(child) = node.children().next() else { return vec![] }; analyze_expr(world, &child) .into_iter() .filter_map(|target| target.field(&access.field()).ok()) .collect() } Some(_) => { if let Some(parent) = node.parent() { if parent.kind() == SyntaxKind::FieldAccess && node.index() > 0 { return analyze_expr(world, parent); } } let span = node.span(); let source = world.source(span.source()); let route = Route::default(); let mut tracer = Tracer::new(Some(span)); eval(world.track(), route.track(), tracer.track_mut(), source).ok(); tracer.finish() } _ => vec![], } } /// Try to load a module from the current source file. pub fn analyze_import( world: &(dyn World + 'static), source: &Source, path: &str, ) -> Option { let full: PathBuf = if let Some(path) = path.strip_prefix('/') { world.root().join(path).normalize() } else if let Some(dir) = source.path().parent() { dir.join(path).normalize() } else { path.into() }; let route = Route::default(); let mut tracer = Tracer::default(); let id = world.resolve(&full).ok()?; let source = world.source(id); eval(world.track(), route.track(), tracer.track_mut(), source).ok() }