Name analysis for classes and affected things.
This commit is contained in:
parent
efecd6b9c8
commit
75dcca0002
@ -133,8 +133,12 @@ fn compile_expression(
|
|||||||
"__repl",
|
"__repl",
|
||||||
SourceRange::new(0, 0),
|
SourceRange::new(0, 0),
|
||||||
false,
|
false,
|
||||||
expression.type_info().clone(), // dubious
|
|
||||||
)));
|
)));
|
||||||
|
|
||||||
|
fake_function_symbol
|
||||||
|
.borrow_mut()
|
||||||
|
.set_return_type_info(expression.type_info().clone());
|
||||||
|
|
||||||
let mut ir_function = IrFunction::new(
|
let mut ir_function = IrFunction::new(
|
||||||
fake_function_symbol,
|
fake_function_symbol,
|
||||||
&[],
|
&[],
|
||||||
|
|||||||
@ -2,7 +2,8 @@ use crate::ast::field::Field;
|
|||||||
use crate::ast::function::Function;
|
use crate::ast::function::Function;
|
||||||
use crate::diagnostic::Diagnostic;
|
use crate::diagnostic::Diagnostic;
|
||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
use crate::symbol_table::SymbolTable;
|
use crate::symbol::class_symbol::ClassSymbol;
|
||||||
|
use crate::symbol_table::{SymbolInsertError, SymbolTable};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct Class {
|
pub struct Class {
|
||||||
@ -32,16 +33,142 @@ impl Class {
|
|||||||
symbol_table: &mut SymbolTable,
|
symbol_table: &mut SymbolTable,
|
||||||
) -> Result<(), Vec<Diagnostic>> {
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
// 1. insert class symbol
|
// 1. insert class symbol
|
||||||
// 2. gather fields
|
let to_insert = ClassSymbol::new(
|
||||||
// 3. gather functions
|
&self.declared_name,
|
||||||
todo!()
|
self.declared_name_source_range.clone(),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
symbol_table
|
||||||
|
.insert_class_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(),
|
||||||
|
)
|
||||||
|
.with_reporter(file!(), line!()),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// 2. push scope
|
||||||
|
symbol_table.push_class_scope(&format!("class_scope({})", self.declared_name));
|
||||||
|
|
||||||
|
// 3. gather fields
|
||||||
|
let fields_diagnostics: Vec<Diagnostic> = self
|
||||||
|
.fields
|
||||||
|
.iter_mut()
|
||||||
|
.map(|field| field.gather_declared_names(symbol_table))
|
||||||
|
.filter_map(Result::err)
|
||||||
|
.flatten()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if !fields_diagnostics.is_empty() {
|
||||||
|
return Err(fields_diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. gather functions
|
||||||
|
let functions_diagnostics: Vec<Diagnostic> = self
|
||||||
|
.functions
|
||||||
|
.iter_mut()
|
||||||
|
.map(|function| function.gather_declared_names(symbol_table))
|
||||||
|
.filter_map(Result::err)
|
||||||
|
.flatten()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if !functions_diagnostics.is_empty() {
|
||||||
|
return Err(functions_diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. pop scope
|
||||||
|
symbol_table.pop_scope();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
||||||
todo!()
|
let fields_diagnostics: Vec<Diagnostic> = self
|
||||||
|
.fields
|
||||||
|
.iter_mut()
|
||||||
|
.map(|field| field.check_name_usages(symbol_table))
|
||||||
|
.filter_map(Result::err)
|
||||||
|
.flatten()
|
||||||
|
.collect();
|
||||||
|
if !fields_diagnostics.is_empty() {
|
||||||
|
return Err(fields_diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
let functions_diagnostics: Vec<Diagnostic> = self
|
||||||
|
.functions
|
||||||
|
.iter_mut()
|
||||||
|
.map(|function| function.check_name_usages(symbol_table))
|
||||||
|
.filter_map(Result::err)
|
||||||
|
.flatten()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if functions_diagnostics.is_empty() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(functions_diagnostics)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::parser::parse_compilation_unit;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn name_analysis_no_diagnostics() {
|
||||||
|
let parse_result = parse_compilation_unit(
|
||||||
|
"
|
||||||
|
class Foo
|
||||||
|
mut bar = 42
|
||||||
|
|
||||||
|
fn baz() -> Int
|
||||||
|
bar
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Qux
|
||||||
|
fn foo() -> Foo
|
||||||
|
Foo()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
",
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut compilation_unit = match parse_result {
|
||||||
|
Ok(compilation_unit) => compilation_unit,
|
||||||
|
Err(diagnostics) => {
|
||||||
|
panic!("{:?}", diagnostics);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut symbol_table = SymbolTable::new();
|
||||||
|
match compilation_unit.gather_declared_names(&mut symbol_table) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(diagnostics) => {
|
||||||
|
panic!("{:?}", diagnostics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match compilation_unit.check_name_usages(&symbol_table) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(diagnostics) => {
|
||||||
|
panic!("{:?}", diagnostics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -4,12 +4,15 @@ use crate::diagnostic::Diagnostic;
|
|||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
use crate::symbol::function_symbol::FunctionSymbol;
|
use crate::symbol::function_symbol::FunctionSymbol;
|
||||||
use crate::symbol_table::{SymbolInsertError, SymbolTable};
|
use crate::symbol_table::{SymbolInsertError, SymbolTable};
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct ExternFunction {
|
pub struct ExternFunction {
|
||||||
declared_name: String,
|
declared_name: String,
|
||||||
declared_name_source_range: SourceRange,
|
declared_name_source_range: SourceRange,
|
||||||
parameters: Vec<Parameter>,
|
parameters: Vec<Parameter>,
|
||||||
return_type: TypeUse,
|
return_type: TypeUse,
|
||||||
|
function_symbol: Option<Rc<RefCell<FunctionSymbol>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExternFunction {
|
impl ExternFunction {
|
||||||
@ -24,6 +27,7 @@ impl ExternFunction {
|
|||||||
declared_name_source_range,
|
declared_name_source_range,
|
||||||
parameters,
|
parameters,
|
||||||
return_type,
|
return_type,
|
||||||
|
function_symbol: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +45,6 @@ impl ExternFunction {
|
|||||||
&self.declared_name,
|
&self.declared_name,
|
||||||
self.declared_name_source_range.clone(),
|
self.declared_name_source_range.clone(),
|
||||||
true,
|
true,
|
||||||
self.return_type.to_type_info(),
|
|
||||||
));
|
));
|
||||||
|
|
||||||
let function_symbol = match insert_result {
|
let function_symbol = match insert_result {
|
||||||
@ -82,6 +85,16 @@ impl ExternFunction {
|
|||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.set_parameters(parameter_symbols);
|
.set_parameters(parameter_symbols);
|
||||||
|
|
||||||
|
self.function_symbol = Some(function_symbol);
|
||||||
|
|
||||||
|
// handle return type
|
||||||
|
match self.return_type.gather_declared_names(symbol_table) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(mut type_use_diagnostics) => {
|
||||||
|
diagnostics.append(&mut type_use_diagnostics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
symbol_table.pop_scope(); // function scope
|
symbol_table.pop_scope(); // function scope
|
||||||
|
|
||||||
if diagnostics.is_empty() {
|
if diagnostics.is_empty() {
|
||||||
@ -92,14 +105,29 @@ impl ExternFunction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
||||||
let diagnostics: Vec<Diagnostic> = self
|
let mut diagnostics: Vec<Diagnostic> = self
|
||||||
.parameters
|
.parameters
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.map(|parameter| parameter.check_name_usages(symbol_table))
|
.map(|parameter| parameter.check_name_usages(symbol_table))
|
||||||
.filter_map(Result::err)
|
.filter_map(Result::err)
|
||||||
.flatten()
|
.flatten()
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
match self.return_type.check_name_usages(symbol_table) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(mut return_type_diagnostics) => {
|
||||||
|
diagnostics.append(&mut return_type_diagnostics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if diagnostics.is_empty() {
|
if diagnostics.is_empty() {
|
||||||
|
// set return type info on symbol now that its available
|
||||||
|
self.function_symbol
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.borrow_mut()
|
||||||
|
.set_return_type_info(self.return_type.to_type_info());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(diagnostics)
|
Err(diagnostics)
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
use crate::ast::expression::Expression;
|
use crate::ast::expression::Expression;
|
||||||
use crate::ast::type_use::TypeUse;
|
use crate::ast::type_use::TypeUse;
|
||||||
|
use crate::diagnostic::Diagnostic;
|
||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
|
use crate::symbol::field_symbol::FieldSymbol;
|
||||||
|
use crate::symbol_table::{SymbolInsertError, SymbolTable};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct Field {
|
pub struct Field {
|
||||||
@ -30,4 +33,51 @@ impl Field {
|
|||||||
initializer: initializer.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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -58,15 +58,15 @@ impl Function {
|
|||||||
) -> Result<(), Vec<Diagnostic>> {
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
let mut diagnostics = vec![];
|
let mut diagnostics = vec![];
|
||||||
|
|
||||||
|
if !diagnostics.is_empty() {
|
||||||
|
return Err(diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
// insert function symbol
|
// insert function symbol
|
||||||
let insert_result = symbol_table.insert_function_symbol(FunctionSymbol::new(
|
let insert_result = symbol_table.insert_function_symbol(FunctionSymbol::new(
|
||||||
self.declared_name(),
|
self.declared_name(),
|
||||||
self.declared_name_source_range.clone(),
|
self.declared_name_source_range.clone(),
|
||||||
false,
|
false,
|
||||||
self.return_type
|
|
||||||
.as_ref()
|
|
||||||
.map(|return_type| return_type.to_type_info())
|
|
||||||
.unwrap_or(TypeInfo::Void),
|
|
||||||
));
|
));
|
||||||
|
|
||||||
// get function symbol if successful
|
// get function symbol if successful
|
||||||
@ -109,6 +109,16 @@ impl Function {
|
|||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.set_parameters(parameter_symbols);
|
.set_parameters(parameter_symbols);
|
||||||
|
|
||||||
|
// return type
|
||||||
|
if let Some(type_use) = &mut self.return_type {
|
||||||
|
match type_use.gather_declared_names(symbol_table) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(mut type_use_diagnostics) => {
|
||||||
|
diagnostics.append(&mut type_use_diagnostics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
symbol_table.push_block_scope(&format!("main_block_scope({})", self.declared_name));
|
symbol_table.push_block_scope(&format!("main_block_scope({})", self.declared_name));
|
||||||
for statement in &mut self.statements {
|
for statement in &mut self.statements {
|
||||||
match statement.gather_declared_names(symbol_table) {
|
match statement.gather_declared_names(symbol_table) {
|
||||||
@ -142,6 +152,28 @@ impl Function {
|
|||||||
.collect(),
|
.collect(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// return type
|
||||||
|
if let Some(type_use) = &mut self.return_type {
|
||||||
|
match type_use.check_name_usages(symbol_table) {
|
||||||
|
Ok(_) => {
|
||||||
|
// set return type info on function symbol
|
||||||
|
self.function_symbol
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.borrow_mut()
|
||||||
|
.set_return_type_info(type_use.to_type_info());
|
||||||
|
}
|
||||||
|
Err(mut type_use_diagnostics) => {
|
||||||
|
diagnostics.append(&mut type_use_diagnostics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// we don't have a given return type, so it's void
|
||||||
|
self.function_symbol.as_mut().unwrap().borrow_mut().set_return_type_info(
|
||||||
|
TypeInfo::Void
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// statements
|
// statements
|
||||||
for statement in &mut self.statements {
|
for statement in &mut self.statements {
|
||||||
match statement.check_name_usages(symbol_table) {
|
match statement.check_name_usages(symbol_table) {
|
||||||
|
|||||||
@ -32,6 +32,7 @@ impl Parameter {
|
|||||||
&mut self,
|
&mut self,
|
||||||
symbol_table: &mut SymbolTable,
|
symbol_table: &mut SymbolTable,
|
||||||
) -> Result<Rc<RefCell<ParameterSymbol>>, Vec<Diagnostic>> {
|
) -> Result<Rc<RefCell<ParameterSymbol>>, Vec<Diagnostic>> {
|
||||||
|
// insert parameter symbol
|
||||||
let insert_result = symbol_table.insert_parameter_symbol(ParameterSymbol::new(
|
let insert_result = symbol_table.insert_parameter_symbol(ParameterSymbol::new(
|
||||||
&self.declared_name,
|
&self.declared_name,
|
||||||
self.declared_name_source_range.clone(),
|
self.declared_name_source_range.clone(),
|
||||||
@ -40,26 +41,32 @@ impl Parameter {
|
|||||||
match insert_result {
|
match insert_result {
|
||||||
Ok(parameter_symbol) => {
|
Ok(parameter_symbol) => {
|
||||||
self.parameter_symbol = Some(parameter_symbol.clone());
|
self.parameter_symbol = Some(parameter_symbol.clone());
|
||||||
Ok(parameter_symbol)
|
|
||||||
}
|
}
|
||||||
Err(symbol_insert_error) => match symbol_insert_error {
|
Err(symbol_insert_error) => {
|
||||||
SymbolInsertError::AlreadyDeclared(already_declared) => Err(vec![Diagnostic::new(
|
return match symbol_insert_error {
|
||||||
&format!(
|
SymbolInsertError::AlreadyDeclared(already_declared) => {
|
||||||
"Parameter {} already declared.",
|
Err(vec![Diagnostic::new(
|
||||||
already_declared.symbol().borrow().declared_name()
|
&format!(
|
||||||
),
|
"Parameter {} already declared.",
|
||||||
self.declared_name_source_range.start(),
|
already_declared.symbol().borrow().declared_name()
|
||||||
self.declared_name_source_range.end(),
|
),
|
||||||
)]),
|
self.declared_name_source_range.start(),
|
||||||
},
|
self.declared_name_source_range.end(),
|
||||||
|
)])
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// type use
|
||||||
|
self.type_use.gather_declared_names(symbol_table)?;
|
||||||
|
|
||||||
|
Ok(self.parameter_symbol.clone().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_name_usages(
|
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
||||||
&mut self,
|
self.type_use.check_name_usages(symbol_table)?;
|
||||||
_symbol_table: &SymbolTable,
|
Ok(())
|
||||||
) -> Result<(), Vec<Diagnostic>> {
|
|
||||||
Ok(()) // no-op for now
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn type_check(&mut self, _symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
pub fn type_check(&mut self, _symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
|||||||
@ -1,9 +1,14 @@
|
|||||||
|
use crate::diagnostic::Diagnostic;
|
||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
|
use crate::symbol::type_symbol::{PrimitiveTypeSymbol, TypeSymbol};
|
||||||
|
use crate::symbol_table::SymbolTable;
|
||||||
use crate::type_info::TypeInfo;
|
use crate::type_info::TypeInfo;
|
||||||
|
|
||||||
pub struct TypeUse {
|
pub struct TypeUse {
|
||||||
declared_name: String,
|
declared_name: String,
|
||||||
declared_name_source_range: SourceRange,
|
declared_name_source_range: SourceRange,
|
||||||
|
scope_id: Option<usize>,
|
||||||
|
type_symbol: Option<TypeSymbol>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TypeUse {
|
impl TypeUse {
|
||||||
@ -11,6 +16,8 @@ impl TypeUse {
|
|||||||
Self {
|
Self {
|
||||||
declared_name: declared_name.into(),
|
declared_name: declared_name.into(),
|
||||||
declared_name_source_range,
|
declared_name_source_range,
|
||||||
|
scope_id: None,
|
||||||
|
type_symbol: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,7 +25,47 @@ impl TypeUse {
|
|||||||
&self.declared_name
|
&self.declared_name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn gather_declared_names(
|
||||||
|
&mut self,
|
||||||
|
symbol_table: &mut SymbolTable,
|
||||||
|
) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
self.scope_id = Some(symbol_table.current_scope_id());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
|
||||||
|
let maybe_type_symbol =
|
||||||
|
symbol_table.find_type_symbol(self.scope_id.unwrap(), &self.declared_name);
|
||||||
|
if let Some(type_symbol) = maybe_type_symbol {
|
||||||
|
self.type_symbol = Some(type_symbol);
|
||||||
|
Ok(())
|
||||||
|
} else if let Some(primitive_type_symbol) =
|
||||||
|
PrimitiveTypeSymbol::try_from_declared_name(self.declared_name())
|
||||||
|
{
|
||||||
|
self.type_symbol = Some(TypeSymbol::Primitive(primitive_type_symbol));
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(vec![
|
||||||
|
Diagnostic::new(
|
||||||
|
&format!("Unable to resolve symbol {}", self.declared_name),
|
||||||
|
self.declared_name_source_range.start(),
|
||||||
|
self.declared_name_source_range.end(),
|
||||||
|
)
|
||||||
|
.with_reporter(file!(), line!()),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn to_type_info(&self) -> TypeInfo {
|
pub fn to_type_info(&self) -> TypeInfo {
|
||||||
TypeInfo::from_declared_name(self.declared_name())
|
match self.type_symbol.as_ref().unwrap() {
|
||||||
|
TypeSymbol::Class(class_symbol) => TypeInfo::Class(class_symbol.clone()),
|
||||||
|
TypeSymbol::Primitive(primitive_type) => match primitive_type {
|
||||||
|
PrimitiveTypeSymbol::Any => TypeInfo::Any,
|
||||||
|
PrimitiveTypeSymbol::Int => TypeInfo::Integer,
|
||||||
|
PrimitiveTypeSymbol::Double => TypeInfo::Double,
|
||||||
|
PrimitiveTypeSymbol::String => TypeInfo::String,
|
||||||
|
PrimitiveTypeSymbol::Void => TypeInfo::Void,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ pub struct FunctionSymbol {
|
|||||||
declared_name_source_range: SourceRange,
|
declared_name_source_range: SourceRange,
|
||||||
is_extern: bool,
|
is_extern: bool,
|
||||||
parameters: Option<Vec<Rc<RefCell<ParameterSymbol>>>>,
|
parameters: Option<Vec<Rc<RefCell<ParameterSymbol>>>>,
|
||||||
return_type: TypeInfo,
|
return_type: Option<TypeInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FunctionSymbol {
|
impl FunctionSymbol {
|
||||||
@ -18,27 +18,16 @@ impl FunctionSymbol {
|
|||||||
declared_name: &str,
|
declared_name: &str,
|
||||||
declared_name_source_range: SourceRange,
|
declared_name_source_range: SourceRange,
|
||||||
is_extern: bool,
|
is_extern: bool,
|
||||||
return_type: TypeInfo,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
declared_name: declared_name.into(),
|
declared_name: declared_name.into(),
|
||||||
declared_name_source_range,
|
declared_name_source_range,
|
||||||
is_extern,
|
is_extern,
|
||||||
parameters: None,
|
parameters: None,
|
||||||
return_type,
|
return_type: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[deprecated(note = "use declared_name")]
|
|
||||||
pub fn name(&self) -> &str {
|
|
||||||
&self.declared_name
|
|
||||||
}
|
|
||||||
|
|
||||||
#[deprecated(note = "use declared_name_owned")]
|
|
||||||
pub fn name_owned(&self) -> Rc<str> {
|
|
||||||
self.declared_name.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_parameters(&mut self, parameters: Vec<Rc<RefCell<ParameterSymbol>>>) {
|
pub fn set_parameters(&mut self, parameters: Vec<Rc<RefCell<ParameterSymbol>>>) {
|
||||||
self.parameters = Some(parameters);
|
self.parameters = Some(parameters);
|
||||||
}
|
}
|
||||||
@ -47,8 +36,12 @@ impl FunctionSymbol {
|
|||||||
self.parameters.as_ref().unwrap()
|
self.parameters.as_ref().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_return_type_info(&mut self, return_type: TypeInfo) {
|
||||||
|
self.return_type = Some(return_type);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn return_type_info(&self) -> &TypeInfo {
|
pub fn return_type_info(&self) -> &TypeInfo {
|
||||||
&self.return_type
|
self.return_type.as_ref().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_extern(&self) -> bool {
|
pub fn is_extern(&self) -> bool {
|
||||||
|
|||||||
@ -6,6 +6,7 @@ pub mod expressible_symbol;
|
|||||||
pub mod field_symbol;
|
pub mod field_symbol;
|
||||||
pub mod function_symbol;
|
pub mod function_symbol;
|
||||||
pub mod parameter_symbol;
|
pub mod parameter_symbol;
|
||||||
|
pub mod type_symbol;
|
||||||
pub mod variable_symbol;
|
pub mod variable_symbol;
|
||||||
|
|
||||||
pub trait Symbol {
|
pub trait Symbol {
|
||||||
|
|||||||
29
dmc-lib/src/symbol/type_symbol.rs
Normal file
29
dmc-lib/src/symbol/type_symbol.rs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
use crate::symbol::class_symbol::ClassSymbol;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub enum TypeSymbol {
|
||||||
|
Class(Rc<RefCell<ClassSymbol>>),
|
||||||
|
Primitive(PrimitiveTypeSymbol),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum PrimitiveTypeSymbol {
|
||||||
|
Any,
|
||||||
|
Int,
|
||||||
|
Double,
|
||||||
|
String,
|
||||||
|
Void,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimitiveTypeSymbol {
|
||||||
|
pub fn try_from_declared_name(declared_name: &str) -> Option<PrimitiveTypeSymbol> {
|
||||||
|
match declared_name {
|
||||||
|
"Any" => Some(PrimitiveTypeSymbol::Any),
|
||||||
|
"Int" => Some(PrimitiveTypeSymbol::Int),
|
||||||
|
"Double" => Some(PrimitiveTypeSymbol::Double),
|
||||||
|
"String" => Some(PrimitiveTypeSymbol::String),
|
||||||
|
"Void" => Some(PrimitiveTypeSymbol::Void),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,6 +5,7 @@ use crate::scope::function_scope::FunctionScope;
|
|||||||
use crate::scope::module_scope::ModuleScope;
|
use crate::scope::module_scope::ModuleScope;
|
||||||
use crate::symbol::Symbol;
|
use crate::symbol::Symbol;
|
||||||
use crate::symbol::expressible_symbol::ExpressibleSymbol;
|
use crate::symbol::expressible_symbol::ExpressibleSymbol;
|
||||||
|
use crate::symbol::type_symbol::TypeSymbol;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
@ -134,3 +135,25 @@ pub fn find_expressible_symbol(scope: &Scope, name: &str) -> Option<ExpressibleS
|
|||||||
Scope::Block(block_scope) => find_expressible_in_block_by_name(block_scope, name),
|
Scope::Block(block_scope) => find_expressible_in_block_by_name(block_scope, name),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn find_type_symbol_in_module(module_scope: &ModuleScope, name: &str) -> Option<TypeSymbol> {
|
||||||
|
module_scope
|
||||||
|
.class_symbols()
|
||||||
|
.get(name)
|
||||||
|
.map(|symbol| TypeSymbol::Class(symbol.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_type_symbol_in_class(class_scope: &ClassScope, name: &str) -> Option<TypeSymbol> {
|
||||||
|
class_scope
|
||||||
|
.class_symbols()
|
||||||
|
.get(name)
|
||||||
|
.map(|symbol| TypeSymbol::Class(symbol.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_type_symbol(scope: &Scope, name: &str) -> Option<TypeSymbol> {
|
||||||
|
match scope {
|
||||||
|
Scope::Module(module_scope) => find_type_symbol_in_module(module_scope, name),
|
||||||
|
Scope::Class(class_scope) => find_type_symbol_in_class(class_scope, name),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -6,13 +6,16 @@ use crate::scope::class_scope::ClassScope;
|
|||||||
use crate::scope::function_scope::FunctionScope;
|
use crate::scope::function_scope::FunctionScope;
|
||||||
use crate::scope::module_scope::ModuleScope;
|
use crate::scope::module_scope::ModuleScope;
|
||||||
use crate::symbol::Symbol;
|
use crate::symbol::Symbol;
|
||||||
|
use crate::symbol::class_symbol::ClassSymbol;
|
||||||
use crate::symbol::expressible_symbol::ExpressibleSymbol;
|
use crate::symbol::expressible_symbol::ExpressibleSymbol;
|
||||||
|
use crate::symbol::field_symbol::FieldSymbol;
|
||||||
use crate::symbol::function_symbol::FunctionSymbol;
|
use crate::symbol::function_symbol::FunctionSymbol;
|
||||||
use crate::symbol::parameter_symbol::ParameterSymbol;
|
use crate::symbol::parameter_symbol::ParameterSymbol;
|
||||||
|
use crate::symbol::type_symbol::TypeSymbol;
|
||||||
use crate::symbol::variable_symbol::VariableSymbol;
|
use crate::symbol::variable_symbol::VariableSymbol;
|
||||||
use crate::symbol_table::helpers::{
|
use crate::symbol_table::helpers::{
|
||||||
find_expressible_symbol, find_in_block_by_name, find_in_class_by_name,
|
find_expressible_symbol, find_in_block_by_name, find_in_class_by_name,
|
||||||
find_in_function_by_name, find_in_module_by_name,
|
find_in_function_by_name, find_in_module_by_name, find_type_symbol,
|
||||||
};
|
};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
@ -84,6 +87,72 @@ impl SymbolTable {
|
|||||||
&mut self.scopes[self.current_scope_id.unwrap()]
|
&mut self.scopes[self.current_scope_id.unwrap()]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn insert_class_symbol(
|
||||||
|
&mut self,
|
||||||
|
class_symbol: ClassSymbol,
|
||||||
|
) -> Result<Rc<RefCell<ClassSymbol>>, SymbolInsertError> {
|
||||||
|
let maybe_already_inserted = match self.current_scope() {
|
||||||
|
Scope::Module(module_scope) => {
|
||||||
|
find_in_module_by_name(module_scope, class_symbol.declared_name())
|
||||||
|
}
|
||||||
|
Scope::Class(class_scope) => {
|
||||||
|
find_in_class_by_name(class_scope, class_symbol.declared_name())
|
||||||
|
}
|
||||||
|
_ => panic!("Attempt to insert ClassSymbol in incompatible scope"),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(already_inserted) = maybe_already_inserted {
|
||||||
|
return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new(
|
||||||
|
already_inserted,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = class_symbol.declared_name_owned();
|
||||||
|
let as_rc = Rc::new(RefCell::new(class_symbol));
|
||||||
|
let to_return = as_rc.clone();
|
||||||
|
|
||||||
|
match self.current_scope_mut() {
|
||||||
|
Scope::Module(module_scope) => {
|
||||||
|
module_scope.class_symbols_mut().insert(name, as_rc);
|
||||||
|
}
|
||||||
|
Scope::Class(class_scope) => {
|
||||||
|
class_scope.class_symbols_mut().insert(name, as_rc);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
Ok(to_return)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_field_symbol(
|
||||||
|
&mut self,
|
||||||
|
field_symbol: FieldSymbol,
|
||||||
|
) -> Result<Rc<RefCell<FieldSymbol>>, SymbolInsertError> {
|
||||||
|
let maybe_already_inserted = match self.current_scope() {
|
||||||
|
Scope::Class(class_scope) => {
|
||||||
|
find_in_class_by_name(class_scope, field_symbol.declared_name())
|
||||||
|
}
|
||||||
|
_ => panic!("Attempt to insert FieldSymbol in incompatible scope"),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(already_inserted) = maybe_already_inserted {
|
||||||
|
return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new(
|
||||||
|
already_inserted,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = field_symbol.declared_name_owned();
|
||||||
|
let as_rc = Rc::new(RefCell::new(field_symbol));
|
||||||
|
let to_return = as_rc.clone();
|
||||||
|
|
||||||
|
match self.current_scope_mut() {
|
||||||
|
Scope::Class(class_scope) => {
|
||||||
|
class_scope.field_symbols_mut().insert(name, as_rc);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
Ok(to_return)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn insert_function_symbol(
|
pub fn insert_function_symbol(
|
||||||
&mut self,
|
&mut self,
|
||||||
function_symbol: FunctionSymbol,
|
function_symbol: FunctionSymbol,
|
||||||
@ -198,6 +267,22 @@ impl SymbolTable {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn find_type_symbol(&self, scope_id: usize, name: &str) -> Option<TypeSymbol> {
|
||||||
|
let mut maybe_scope = self.scopes.get(scope_id);
|
||||||
|
if maybe_scope.is_none() {
|
||||||
|
panic!("Invalid scope_id: {}", scope_id);
|
||||||
|
}
|
||||||
|
while let Some(scope) = maybe_scope {
|
||||||
|
let maybe_type_symbol = find_type_symbol(scope, name);
|
||||||
|
if maybe_type_symbol.is_some() {
|
||||||
|
return maybe_type_symbol;
|
||||||
|
} else {
|
||||||
|
maybe_scope = scope.parent_id().map(|id| &self.scopes[id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_variable_symbol(&self, scope_id: usize, name: &str) -> Rc<RefCell<VariableSymbol>> {
|
pub fn get_variable_symbol(&self, scope_id: usize, name: &str) -> Rc<RefCell<VariableSymbol>> {
|
||||||
match &self.scopes[scope_id] {
|
match &self.scopes[scope_id] {
|
||||||
Scope::Block(block_scope) => block_scope.variable_symbols().get(name).cloned().unwrap(),
|
Scope::Block(block_scope) => block_scope.variable_symbols().get(name).cloned().unwrap(),
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
use crate::symbol::Symbol;
|
||||||
|
use crate::symbol::class_symbol::ClassSymbol;
|
||||||
use crate::symbol::function_symbol::FunctionSymbol;
|
use crate::symbol::function_symbol::FunctionSymbol;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
@ -10,6 +12,7 @@ pub enum TypeInfo {
|
|||||||
Double,
|
Double,
|
||||||
String,
|
String,
|
||||||
Function(Rc<RefCell<FunctionSymbol>>),
|
Function(Rc<RefCell<FunctionSymbol>>),
|
||||||
|
Class(Rc<RefCell<ClassSymbol>>),
|
||||||
Void,
|
Void,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,6 +30,9 @@ impl Display for TypeInfo {
|
|||||||
}
|
}
|
||||||
write!(f, ")")
|
write!(f, ")")
|
||||||
}
|
}
|
||||||
|
TypeInfo::Class(class_symbol) => {
|
||||||
|
write!(f, "{}", class_symbol.borrow().declared_name())
|
||||||
|
}
|
||||||
TypeInfo::Void => write!(f, "Void"),
|
TypeInfo::Void => write!(f, "Void"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -34,6 +40,7 @@ impl Display for TypeInfo {
|
|||||||
|
|
||||||
impl TypeInfo {
|
impl TypeInfo {
|
||||||
// This is very naive but works for now
|
// This is very naive but works for now
|
||||||
|
#[deprecated]
|
||||||
pub fn from_declared_name(declared_name: &str) -> Self {
|
pub fn from_declared_name(declared_name: &str) -> Self {
|
||||||
match declared_name {
|
match declared_name {
|
||||||
"Any" => TypeInfo::Any,
|
"Any" => TypeInfo::Any,
|
||||||
@ -60,6 +67,15 @@ impl TypeInfo {
|
|||||||
TypeInfo::Function(_) => {
|
TypeInfo::Function(_) => {
|
||||||
unimplemented!("Type matching on Functions not yet supported.")
|
unimplemented!("Type matching on Functions not yet supported.")
|
||||||
}
|
}
|
||||||
|
TypeInfo::Class(class_symbol) => {
|
||||||
|
match other {
|
||||||
|
TypeInfo::Class(other_class_symbol) => {
|
||||||
|
class_symbol.borrow().declared_name()
|
||||||
|
== other_class_symbol.borrow().declared_name() // good enough for now
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
TypeInfo::Void => {
|
TypeInfo::Void => {
|
||||||
matches!(other, TypeInfo::Void)
|
matches!(other, TypeInfo::Void)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user