deimos-lang/dmc-lib/src/ast/field.rs
2026-03-11 22:15:23 -05:00

84 lines
2.8 KiB
Rust

use crate::ast::expression::Expression;
use crate::ast::type_use::TypeUse;
use crate::diagnostic::Diagnostic;
use crate::source_range::SourceRange;
use crate::symbol::field_symbol::FieldSymbol;
use crate::symbol_table::{SymbolInsertError, SymbolTable};
use std::rc::Rc;
pub struct Field {
declared_name: Rc<str>,
declared_name_source_range: SourceRange,
is_public: bool,
is_mut: bool,
declared_type: Option<Box<TypeUse>>,
initializer: Option<Box<Expression>>,
}
impl Field {
pub fn new(
declared_name: &str,
declared_name_source_range: SourceRange,
is_public: bool,
is_mut: bool,
declared_type: Option<TypeUse>,
initializer: Option<Expression>,
) -> Self {
Self {
declared_name: declared_name.into(),
declared_name_source_range,
is_public,
is_mut,
declared_type: declared_type.map(Box::new),
initializer: initializer.map(Box::new),
}
}
pub fn gather_declared_names(
&mut self,
symbol_table: &mut SymbolTable,
) -> Result<(), Vec<Diagnostic>> {
// 1. insert field symbol
let to_insert =
FieldSymbol::new(&self.declared_name, self.declared_name_source_range.clone());
symbol_table
.insert_field_symbol(to_insert)
.map_err(|e| match e {
SymbolInsertError::AlreadyDeclared(already_declared) => {
vec![Diagnostic::new(
&format!(
"Symbol {} already declared in current scope.",
already_declared.symbol().borrow().declared_name()
),
self.declared_name_source_range.start(),
self.declared_name_source_range.end(),
)]
}
})?;
// 2. gather initializer, if present
if let Some(initializer) = &mut self.initializer {
initializer.gather_declared_names(symbol_table)?;
}
Ok(())
}
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
if let Some(type_use) = &mut self.declared_type {
type_use.check_name_usages(symbol_table)?;
}
// This is going to get hairy, because users might attempt to use a field in an initializer
// (for either this field, or another one) before it's actually initialized. As such, we
// need a way to prevent lookup of current class' fields in the initializer.
// For now, the following is okay so long as we don't start referencing things in the
// initializers.
if let Some(initializer) = self.initializer.as_mut() {
initializer.check_name_usages(symbol_table)?;
}
Ok(())
}
}