miden_assembly_syntax/sema/
context.rs1use alloc::{
2 boxed::Box,
3 collections::{BTreeMap, BTreeSet},
4 sync::Arc,
5 vec::Vec,
6};
7
8use miden_debug_types::{SourceFile, SourceManager, SourceSpan, Span, Spanned};
9use miden_utils_diagnostics::{Diagnostic, Severity};
10
11use super::{SemanticAnalysisError, SyntaxError};
12use crate::ast::{
13 constants::{ConstEvalError, eval::CachedConstantValue},
14 *,
15};
16
17pub struct AnalysisContext {
19 constants: BTreeMap<Ident, Constant>,
20 imported: BTreeSet<Ident>,
21 procedures: BTreeSet<ProcedureName>,
22 errors: Vec<SemanticAnalysisError>,
23 source_file: Arc<SourceFile>,
24 source_manager: Arc<dyn SourceManager>,
25 warnings_as_errors: bool,
26}
27
28impl constants::ConstEnvironment for AnalysisContext {
29 type Error = SemanticAnalysisError;
30
31 fn get_source_file_for(&self, span: SourceSpan) -> Option<Arc<SourceFile>> {
32 if span.source_id() == self.source_file.id() {
33 Some(self.source_file.clone())
34 } else {
35 None
36 }
37 }
38 #[inline]
39 fn get(&self, name: &Ident) -> Result<Option<CachedConstantValue<'_>>, Self::Error> {
40 if let Some(constant) = self.constants.get(name) {
41 Ok(Some(CachedConstantValue::Miss(&constant.value)))
42 } else if self.imported.contains(name) {
43 Ok(None)
45 } else {
46 Err(ConstEvalError::UndefinedSymbol {
47 symbol: name.clone(),
48 source_file: self.get_source_file_for(name.span()),
49 }
50 .into())
51 }
52 }
53 #[inline(always)]
54 fn get_by_path(
55 &self,
56 path: Span<&Path>,
57 ) -> Result<Option<CachedConstantValue<'_>>, Self::Error> {
58 if let Some(name) = path.as_ident() {
59 self.get(&name)
60 } else {
61 Ok(None)
62 }
63 }
64}
65
66impl AnalysisContext {
67 pub fn new(source_file: Arc<SourceFile>, source_manager: Arc<dyn SourceManager>) -> Self {
68 Self {
69 constants: Default::default(),
70 imported: Default::default(),
71 procedures: Default::default(),
72 errors: Default::default(),
73 source_file,
74 source_manager,
75 warnings_as_errors: false,
76 }
77 }
78
79 pub fn set_warnings_as_errors(&mut self, yes: bool) {
80 self.warnings_as_errors = yes;
81 }
82
83 #[inline(always)]
84 pub fn warnings_as_errors(&self) -> bool {
85 self.warnings_as_errors
86 }
87
88 #[inline(always)]
89 pub fn source_manager(&self) -> Arc<dyn SourceManager> {
90 self.source_manager.clone()
91 }
92
93 pub fn register_procedure_name(&mut self, name: ProcedureName) {
94 self.procedures.insert(name);
95 }
96
97 pub fn register_imported_name(&mut self, name: Ident) {
98 self.imported.insert(name);
99 }
100
101 pub fn define_constant(&mut self, module: &mut Module, constant: Constant) {
105 if let Err(err) = module.define_constant(constant.clone()) {
106 self.errors.push(err);
107 } else {
108 let name = constant.name.clone();
109 self.constants.insert(name, constant);
110 }
111 }
112
113 pub fn simplify_constants(&mut self) {
117 let constants = self.constants.keys().cloned().collect::<Vec<_>>();
118
119 for constant in constants.iter() {
120 let expr = ConstantExpr::Var(Span::new(
121 constant.span(),
122 PathBuf::from(constant.clone()).into(),
123 ));
124 match crate::ast::constants::eval::expr(&expr, self) {
125 Ok(value) => {
126 self.constants.get_mut(constant).unwrap().value = value;
127 },
128 Err(err) => {
129 self.errors.push(err);
130 },
131 }
132 }
133 }
134
135 pub fn get_constant(&self, name: &Ident) -> Result<&ConstantExpr, SemanticAnalysisError> {
139 if let Some(expr) = self.constants.get(name) {
140 Ok(&expr.value)
141 } else {
142 Err(SemanticAnalysisError::SymbolResolutionError(Box::new(
143 SymbolResolutionError::undefined(name.span(), &self.source_manager),
144 )))
145 }
146 }
147
148 pub fn error(&mut self, diagnostic: SemanticAnalysisError) {
149 self.errors.push(diagnostic);
150 }
151
152 pub fn has_errors(&self) -> bool {
153 if self.warnings_as_errors() {
154 return !self.errors.is_empty();
155 }
156 self.errors
157 .iter()
158 .any(|err| matches!(err.severity().unwrap_or(Severity::Error), Severity::Error))
159 }
160
161 pub fn has_failed(&mut self) -> Result<(), SyntaxError> {
162 if self.has_errors() {
163 Err(SyntaxError {
164 source_file: self.source_file.clone(),
165 errors: core::mem::take(&mut self.errors),
166 })
167 } else {
168 Ok(())
169 }
170 }
171
172 pub fn into_result(self) -> Result<(), SyntaxError> {
173 if self.has_errors() {
174 Err(SyntaxError {
175 source_file: self.source_file.clone(),
176 errors: self.errors,
177 })
178 } else {
179 self.emit_warnings();
180 Ok(())
181 }
182 }
183
184 #[cfg(feature = "std")]
185 fn emit_warnings(self) {
186 use crate::diagnostics::Report;
187
188 if !self.errors.is_empty() {
189 let warning = Report::from(super::errors::SyntaxWarning {
191 source_file: self.source_file,
192 errors: self.errors,
193 });
194 std::eprintln!("{warning}");
195 }
196 }
197
198 #[cfg(not(feature = "std"))]
199 fn emit_warnings(self) {}
200}