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(&mut 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 &mut 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 register_constant(&mut self, constant: Constant) {
118 let name = constant.name.clone();
119 if let Some(prev) = self.constants.get(&name) {
120 self.errors.push(SemanticAnalysisError::SymbolConflict {
121 span: constant.span,
122 prev_span: prev.span,
123 });
124 } else {
125 self.constants.insert(name, constant);
126 }
127 }
128
129 pub fn simplify_constants(&mut self) {
133 let constants = self.constants.keys().cloned().collect::<Vec<_>>();
134
135 for constant in constants.iter() {
136 let expr = ConstantExpr::Var(Span::new(
137 constant.span(),
138 PathBuf::from(constant.clone()).into(),
139 ));
140 match crate::ast::constants::eval::expr(&expr, self) {
141 Ok(value) => {
142 self.constants.get_mut(constant).unwrap().value = value;
143 },
144 Err(err) => {
145 self.errors.push(err);
146 },
147 }
148 }
149 }
150
151 pub fn get_constant(&self, name: &Ident) -> Result<&ConstantExpr, SemanticAnalysisError> {
155 if let Some(expr) = self.constants.get(name) {
156 Ok(&expr.value)
157 } else {
158 Err(SemanticAnalysisError::SymbolResolutionError(Box::new(
159 SymbolResolutionError::undefined(name.span(), &self.source_manager),
160 )))
161 }
162 }
163
164 pub fn error(&mut self, diagnostic: SemanticAnalysisError) {
165 self.errors.push(diagnostic);
166 }
167
168 pub fn has_errors(&self) -> bool {
169 if self.warnings_as_errors() {
170 return !self.errors.is_empty();
171 }
172 self.errors
173 .iter()
174 .any(|err| matches!(err.severity().unwrap_or(Severity::Error), Severity::Error))
175 }
176
177 pub fn has_failed(&mut self) -> Result<(), SyntaxError> {
178 if self.has_errors() {
179 Err(SyntaxError {
180 source_file: self.source_file.clone(),
181 errors: core::mem::take(&mut self.errors),
182 })
183 } else {
184 Ok(())
185 }
186 }
187
188 pub fn into_result(self) -> Result<(), SyntaxError> {
189 if self.has_errors() {
190 Err(SyntaxError {
191 source_file: self.source_file.clone(),
192 errors: self.errors,
193 })
194 } else {
195 self.emit_warnings();
196 Ok(())
197 }
198 }
199
200 #[cfg(feature = "std")]
201 fn emit_warnings(self) {
202 use crate::diagnostics::Report;
203
204 if !self.errors.is_empty() {
205 let warning = Report::from(super::errors::SyntaxWarning {
207 source_file: self.source_file,
208 errors: self.errors,
209 });
210 std::eprintln!("{warning}");
211 }
212 }
213
214 #[cfg(not(feature = "std"))]
215 fn emit_warnings(self) {}
216}