New type analysis WIP.
This commit is contained in:
parent
d07135b3c6
commit
21fa082b44
@ -10,7 +10,7 @@ pub fn get_name_for_type<'ctx>(type_info: &TypeInfo, ctx: &'ctx AnalysisContext)
|
|||||||
TypeInfo::Constructor(_constructor_type_info) => {
|
TypeInfo::Constructor(_constructor_type_info) => {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
TypeInfo::TypeConstructor(type_constructor_info) => {
|
TypeInfo::Class(type_constructor_info) => {
|
||||||
let class_symbol = &ctx.symbols()[type_constructor_info.class_symbol_id()];
|
let class_symbol = &ctx.symbols()[type_constructor_info.class_symbol_id()];
|
||||||
class_symbol.declared_name()
|
class_symbol.declared_name()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,12 +6,13 @@ use crate::diagnostic_factories::symbol_not_found;
|
|||||||
use crate::semantic_analysis::diagnostic_helpers::symbol_already_declared;
|
use crate::semantic_analysis::diagnostic_helpers::symbol_already_declared;
|
||||||
use crate::semantic_analysis::scope::{Scope, ScopeId};
|
use crate::semantic_analysis::scope::{Scope, ScopeId};
|
||||||
use crate::semantic_analysis::symbol::{Symbol, SymbolId};
|
use crate::semantic_analysis::symbol::{Symbol, SymbolId};
|
||||||
use crate::semantic_analysis::type_info::{TypeInfo, TypeInfoId};
|
use crate::semantic_analysis::type_info::{ClassTypeInfo, TypeInfo, TypeInfoId};
|
||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct AnalysisContext {
|
#[derive(Debug, Clone)]
|
||||||
|
struct AnalysisState {
|
||||||
fqn_stack: Vec<Rc<str>>,
|
fqn_stack: Vec<Rc<str>>,
|
||||||
scopes: Vec<Scope>,
|
scopes: Vec<Scope>,
|
||||||
current_scope_id: Option<ScopeId>,
|
current_scope_id: Option<ScopeId>,
|
||||||
@ -23,8 +24,8 @@ pub struct AnalysisContext {
|
|||||||
nodes_to_type_infos: HashMap<NodeId, TypeInfoId>,
|
nodes_to_type_infos: HashMap<NodeId, TypeInfoId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnalysisContext {
|
impl Default for AnalysisState {
|
||||||
pub fn new() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
fqn_stack: Vec::new(),
|
fqn_stack: Vec::new(),
|
||||||
scopes: Vec::new(),
|
scopes: Vec::new(),
|
||||||
@ -37,101 +38,129 @@ impl AnalysisContext {
|
|||||||
nodes_to_type_infos: HashMap::new(),
|
nodes_to_type_infos: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AnalysisContext {
|
||||||
|
state: Box<AnalysisState>,
|
||||||
|
previous_state: Option<Box<AnalysisState>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnalysisContext {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
state: AnalysisState::default().into(),
|
||||||
|
previous_state: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn commit(&mut self) {
|
pub fn commit(&mut self) {
|
||||||
todo!()
|
self.previous_state = Some(self.state.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rollback(&mut self) {
|
pub fn rollback(&mut self) {
|
||||||
todo!()
|
self.state = self.previous_state.take().unwrap_or_default();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
pub fn symbols(&self) -> &[Symbol] {
|
pub fn symbols(&self) -> &[Symbol] {
|
||||||
&self.symbols
|
&self.state.symbols
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
pub fn nodes_to_symbols(&self) -> &HashMap<NodeId, SymbolId> {
|
pub fn nodes_to_symbols(&self) -> &HashMap<NodeId, SymbolId> {
|
||||||
&self.nodes_to_symbols
|
&self.state.nodes_to_symbols
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
pub fn type_infos(&self) -> &[TypeInfo] {
|
pub fn type_infos(&self) -> &[TypeInfo] {
|
||||||
&self.type_infos
|
&self.state.type_infos
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
pub fn symbols_to_type_infos(&self) -> &HashMap<SymbolId, TypeInfoId> {
|
pub fn symbols_to_type_infos(&self) -> &HashMap<SymbolId, TypeInfoId> {
|
||||||
&self.symbols_to_type_infos
|
&self.state.symbols_to_type_infos
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
pub fn nodes_to_type_infos(&self) -> &HashMap<NodeId, TypeInfoId> {
|
pub fn nodes_to_type_infos(&self) -> &HashMap<NodeId, TypeInfoId> {
|
||||||
&self.nodes_to_type_infos
|
&self.state.nodes_to_type_infos
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_fqn_part(&mut self, part: Rc<str>) {
|
pub fn push_fqn_part(&mut self, part: Rc<str>) {
|
||||||
self.fqn_stack.push(part);
|
self.state.fqn_stack.push(part);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop_fqn_part(&mut self) {
|
pub fn pop_fqn_part(&mut self) {
|
||||||
self.fqn_stack.pop();
|
self.state.fqn_stack.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_fqn(&self, suffix: &str) -> String {
|
pub fn resolve_fqn(&self, suffix: &str) -> String {
|
||||||
let mut fqn = self.fqn_stack.clone();
|
let mut fqn = self.state.fqn_stack.clone();
|
||||||
fqn.push(suffix.into());
|
fqn.push(suffix.into());
|
||||||
fqn.join("::")
|
fqn.join("::")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_scope(&mut self, _debug_name: &str) -> ScopeId {
|
|
||||||
let scope = Scope::new(self.current_scope_id);
|
|
||||||
self.scopes.push(scope);
|
|
||||||
self.current_scope_id = Some(self.scopes.len() - 1);
|
|
||||||
self.current_scope_id.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_scope(&self, scope_id: ScopeId) -> &Scope {
|
fn get_scope(&self, scope_id: ScopeId) -> &Scope {
|
||||||
self.scopes.get(scope_id).expect(&format!(
|
self.state.scopes.get(scope_id).expect(&format!(
|
||||||
"scope_id {} is not a valid index of self.scopes",
|
"scope_id {} is not a valid index of self.scopes",
|
||||||
scope_id
|
scope_id
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_scope_mut(&mut self, scope_id: ScopeId) -> &mut Scope {
|
fn get_scope_mut(&mut self, scope_id: ScopeId) -> &mut Scope {
|
||||||
self.scopes.get_mut(scope_id).expect(&format!(
|
self.state.scopes.get_mut(scope_id).expect(&format!(
|
||||||
"scope_id {} is not a valid index of self.scopes",
|
"scope_id {} is not a valid index of self.scopes",
|
||||||
scope_id
|
scope_id
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_current_scope(&self) -> &Scope {
|
/// Pushes a new `Scope` on top of the scope stack, returning the `ScopeId` for the newly pushed
|
||||||
self.get_scope(self.current_scope_id.expect("current_scope_id is None"))
|
/// scope.
|
||||||
}
|
pub fn push_scope(&mut self, _debug_name: &str) -> ScopeId {
|
||||||
|
let scope = Scope::new(self.state.current_scope_id);
|
||||||
fn get_current_scope_mut(&mut self) -> &mut Scope {
|
self.state.scopes.push(scope);
|
||||||
self.get_scope_mut(self.current_scope_id.expect("current_scope_id is None"))
|
self.state.current_scope_id = Some(self.state.scopes.len() - 1);
|
||||||
|
self.state.current_scope_id.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop_scope(&mut self) {
|
pub fn pop_scope(&mut self) {
|
||||||
self.current_scope_id = self.get_current_scope().parent_id();
|
self.state.current_scope_id = self
|
||||||
|
.state
|
||||||
|
.current_scope_id
|
||||||
|
.and_then(|current_scope_id| self.state.scopes.get(current_scope_id))
|
||||||
|
.and_then(|current_scope| current_scope.parent_id())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Designates the given `node_id` as belonging to the current scope.
|
||||||
pub fn associate_node_to_current_scope(&mut self, node_id: NodeId) {
|
pub fn associate_node_to_current_scope(&mut self, node_id: NodeId) {
|
||||||
self.nodes_to_scopes.insert(
|
self.state.nodes_to_scopes.insert(
|
||||||
node_id,
|
node_id,
|
||||||
self.current_scope_id.expect("current_scope_id is None"),
|
self.state
|
||||||
|
.current_scope_id
|
||||||
|
.expect("current_scope_id is None"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inserts the given symbol into the Scope corresponding to the given ScopeId. **This is a low
|
/// Inserts the given symbol into the Scope corresponding to the given ScopeId.
|
||||||
/// level operation and alternate methods should be preferred.**
|
|
||||||
///
|
///
|
||||||
/// Danger: If a symbol already exists in the corresponding Scope with the same declared name,
|
/// ## Panics
|
||||||
/// it will be overwritten, leading to dangling symbols/symbol ids.
|
/// - If the scope identified by `scope_id` already contains a symbol with the `declared_name`
|
||||||
|
/// obtained from the given `Symbol`.
|
||||||
fn insert_symbol_in_scope(&mut self, scope_id: ScopeId, symbol: Symbol) -> SymbolId {
|
fn insert_symbol_in_scope(&mut self, scope_id: ScopeId, symbol: Symbol) -> SymbolId {
|
||||||
// get declared name
|
// get declared name
|
||||||
let declared_name = symbol.declared_name_owned();
|
let declared_name = symbol.declared_name_owned();
|
||||||
|
|
||||||
|
if self.get_scope(scope_id).has_symbol(symbol.declared_name()) {
|
||||||
|
panic!(
|
||||||
|
"Symbol with declared_name {} already exists in scope_id {}",
|
||||||
|
symbol.declared_name(),
|
||||||
|
scope_id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// push the symbol
|
// push the symbol
|
||||||
self.symbols.push(symbol);
|
self.state.symbols.push(symbol);
|
||||||
let symbol_id = self.symbols.len() - 1;
|
let symbol_id = self.state.symbols.len() - 1;
|
||||||
|
|
||||||
// put it in scope
|
// put it in scope
|
||||||
self.get_scope_mut(scope_id)
|
self.get_scope_mut(scope_id)
|
||||||
@ -140,28 +169,46 @@ impl AnalysisContext {
|
|||||||
symbol_id
|
symbol_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the symbol identified by `symbol_id` for the node identified by `node_id`.
|
||||||
pub fn associate_node_to_symbol(&mut self, node_id: NodeId, symbol_id: SymbolId) {
|
pub fn associate_node_to_symbol(&mut self, node_id: NodeId, symbol_id: SymbolId) {
|
||||||
self.nodes_to_symbols.insert(node_id, symbol_id);
|
self.state.nodes_to_symbols.insert(node_id, symbol_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_symbol_in_scope(&self, scope: &Scope, declared_name: &str) -> Option<&Symbol> {
|
/// Finds a symbol with a matching name to `declared_name` in the provided `Scope`. Returns an
|
||||||
|
/// `Option` maybe containing a reference to that `Symbol`.
|
||||||
|
///
|
||||||
|
/// ## Panics
|
||||||
|
/// - If the `symbol_id` for the matching `Symbol` is not a valid index of `self.state.symbols`.
|
||||||
|
fn find_symbol_in_scope(&self, scope: &Scope, declared_name: &str) -> Option<&Symbol> {
|
||||||
scope.get_symbol_id(declared_name).map(|symbol_id| {
|
scope.get_symbol_id(declared_name).map(|symbol_id| {
|
||||||
self.symbols.get(symbol_id).expect(&format!(
|
self.state.symbols.get(symbol_id).expect(&format!(
|
||||||
"symbol_id {} is not a valid index of self.symbols",
|
"symbol_id {} is not a valid index of self.symbols",
|
||||||
symbol_id
|
symbol_id
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Use this function only when there is no node associated with the symbol. Otherwise use
|
/// Inserts the given `Symbol` into the scope identified by `scope_id`.
|
||||||
/// `try_insert_symbol_in_node_scope`.
|
///
|
||||||
|
/// ## Examples
|
||||||
|
/// ```rust
|
||||||
|
/// use dmc_lib::semantic_analysis::analysis_context::AnalysisContext;
|
||||||
|
/// use dmc_lib::semantic_analysis::symbol::{Symbol, VariableSymbol};
|
||||||
|
///
|
||||||
|
/// let mut analysis_context = AnalysisContext::new();
|
||||||
|
/// let scope_id = analysis_context.push_scope("some scope");
|
||||||
|
/// let symbol = Symbol::Variable(VariableSymbol::new("my_variable".into(), None, false));
|
||||||
|
/// let result = analysis_context.try_insert_symbol_in_scope(symbol, scope_id);
|
||||||
|
/// assert!(matches!(result, Ok(_)));
|
||||||
|
/// ```
|
||||||
pub fn try_insert_symbol_in_scope(
|
pub fn try_insert_symbol_in_scope(
|
||||||
&mut self,
|
&mut self,
|
||||||
to_insert: Symbol,
|
to_insert: Symbol,
|
||||||
scope_id: ScopeId,
|
scope_id: ScopeId,
|
||||||
) -> Result<SymbolId, Diagnostic> {
|
) -> Result<SymbolId, Diagnostic> {
|
||||||
let scope = self.get_scope(scope_id);
|
let scope = self.get_scope(scope_id);
|
||||||
if let Some(already_declared) = self.get_symbol_in_scope(scope, to_insert.declared_name()) {
|
if let Some(already_declared) = self.find_symbol_in_scope(scope, to_insert.declared_name())
|
||||||
|
{
|
||||||
Err(symbol_already_declared(already_declared, &to_insert))
|
Err(symbol_already_declared(already_declared, &to_insert))
|
||||||
} else {
|
} else {
|
||||||
let symbol_id = self.insert_symbol_in_scope(scope_id, to_insert);
|
let symbol_id = self.insert_symbol_in_scope(scope_id, to_insert);
|
||||||
@ -170,18 +217,39 @@ impl AnalysisContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Inserts the given symbol into the scope belonging to the given node_id, and sets the node's
|
||||||
|
/// associated symbol id to the resultant symbol id. Returns `Ok(SymbolId)` if the insert was
|
||||||
|
/// successful, else `Err(Diagnostic)`.
|
||||||
|
///
|
||||||
|
/// ## Examples
|
||||||
|
/// ```rust
|
||||||
|
/// use dmc_lib::semantic_analysis::analysis_context::AnalysisContext;
|
||||||
|
/// use dmc_lib::semantic_analysis::symbol::{Symbol, VariableSymbol};
|
||||||
|
///
|
||||||
|
/// let mut analysis_context = AnalysisContext::new();
|
||||||
|
/// analysis_context.push_scope("some scope");
|
||||||
|
/// let node_id = 0;
|
||||||
|
/// analysis_context.associate_node_to_current_scope(node_id);
|
||||||
|
/// let symbol = Symbol::Variable(VariableSymbol::new("my_variable".into(), None, false));
|
||||||
|
/// let result = analysis_context.try_insert_associate_symbol_in_node_scope(symbol, node_id);
|
||||||
|
/// assert!(matches!(result, Ok(_)));
|
||||||
|
/// ```
|
||||||
pub fn try_insert_associate_symbol_in_node_scope(
|
pub fn try_insert_associate_symbol_in_node_scope(
|
||||||
&mut self,
|
&mut self,
|
||||||
to_insert: Symbol,
|
to_insert: Symbol,
|
||||||
owner_node_id: NodeId,
|
owner_node_id: NodeId,
|
||||||
) -> Result<SymbolId, Diagnostic> {
|
) -> Result<SymbolId, Diagnostic> {
|
||||||
let node_scope_id = self.nodes_to_scopes.get(&owner_node_id).expect(&format!(
|
let node_scope_id = self
|
||||||
"owner_node_id {} is not in self.nodes_to_scopes",
|
.state
|
||||||
|
.nodes_to_scopes
|
||||||
|
.get(&owner_node_id)
|
||||||
|
.expect(&format!(
|
||||||
|
"owner_node_id {} is not in self.state.nodes_to_scopes",
|
||||||
owner_node_id
|
owner_node_id
|
||||||
));
|
));
|
||||||
let node_scope = self.get_scope(*node_scope_id);
|
let node_scope = self.get_scope(*node_scope_id);
|
||||||
if let Some(already_declared) =
|
if let Some(already_declared) =
|
||||||
self.get_symbol_in_scope(node_scope, to_insert.declared_name())
|
self.find_symbol_in_scope(node_scope, to_insert.declared_name())
|
||||||
{
|
{
|
||||||
Err(symbol_already_declared(already_declared, &to_insert))
|
Err(symbol_already_declared(already_declared, &to_insert))
|
||||||
} else {
|
} else {
|
||||||
@ -191,12 +259,15 @@ impl AnalysisContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the `SymbolId` of the node with the declared name in the containing scope of the node
|
||||||
|
/// with the given `NodeId`.
|
||||||
fn get_symbol_id_by_declared_name_in_node_scope(
|
fn get_symbol_id_by_declared_name_in_node_scope(
|
||||||
&self,
|
&self,
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
declared_name: &str,
|
declared_name: &str,
|
||||||
) -> Option<SymbolId> {
|
) -> Option<SymbolId> {
|
||||||
let scope_id = self
|
let scope_id = self
|
||||||
|
.state
|
||||||
.nodes_to_scopes
|
.nodes_to_scopes
|
||||||
.get(&node_id)
|
.get(&node_id)
|
||||||
.expect(&format!("node_id {} has no associated scope", node_id));
|
.expect(&format!("node_id {} has no associated scope", node_id));
|
||||||
@ -204,6 +275,8 @@ impl AnalysisContext {
|
|||||||
scope.get_symbol_id(declared_name)
|
scope.get_symbol_id(declared_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Finds the symbol for the node with the given node id and declared name and sets the node's
|
||||||
|
/// symbol to that result.
|
||||||
pub fn associate_node_with_self_symbol(&mut self, node_id: NodeId, declared_name: &str) {
|
pub fn associate_node_with_self_symbol(&mut self, node_id: NodeId, declared_name: &str) {
|
||||||
let symbol_id = self
|
let symbol_id = self
|
||||||
.get_symbol_id_by_declared_name_in_node_scope(node_id, declared_name)
|
.get_symbol_id_by_declared_name_in_node_scope(node_id, declared_name)
|
||||||
@ -221,6 +294,7 @@ impl AnalysisContext {
|
|||||||
node_source_range: &SourceRange,
|
node_source_range: &SourceRange,
|
||||||
) -> Result<(), Diagnostic> {
|
) -> Result<(), Diagnostic> {
|
||||||
let scope_id = self
|
let scope_id = self
|
||||||
|
.state
|
||||||
.nodes_to_scopes
|
.nodes_to_scopes
|
||||||
.get(&node_id)
|
.get(&node_id)
|
||||||
.expect(&format!("node_id {} has no associated scope", node_id));
|
.expect(&format!("node_id {} has no associated scope", node_id));
|
||||||
@ -250,37 +324,43 @@ impl AnalysisContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
pub fn insert_type_info(&mut self, type_info: TypeInfo) -> TypeInfoId {
|
pub fn insert_type_info(&mut self, type_info: TypeInfo) -> TypeInfoId {
|
||||||
self.type_infos.push(type_info);
|
self.state.type_infos.push(type_info);
|
||||||
self.type_infos.len() - 1
|
self.state.type_infos.len() - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
pub fn insert_type_info_for_node(
|
pub fn insert_type_info_for_node(
|
||||||
&mut self,
|
&mut self,
|
||||||
type_info: TypeInfo,
|
type_info: TypeInfo,
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
) -> TypeInfoId {
|
) -> TypeInfoId {
|
||||||
self.type_infos.push(type_info);
|
self.state.type_infos.push(type_info);
|
||||||
let type_info_id = self.type_infos.len() - 1;
|
let type_info_id = self.state.type_infos.len() - 1;
|
||||||
self.nodes_to_type_infos.insert(node_id, type_info_id);
|
self.state.nodes_to_type_infos.insert(node_id, type_info_id);
|
||||||
type_info_id
|
type_info_id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn associate_symbol_to_type(&mut self, symbol_id: SymbolId, type_info_id: TypeInfoId) {
|
pub fn associate_symbol_to_type(&mut self, symbol_id: SymbolId, type_info_id: TypeInfoId) {
|
||||||
self.symbols_to_type_infos.insert(symbol_id, type_info_id);
|
self.state
|
||||||
|
.symbols_to_type_infos
|
||||||
|
.insert(symbol_id, type_info_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn associate_node_and_symbol_to_type(&mut self, node_id: NodeId, type_info_id: TypeInfoId) {
|
pub fn associate_node_and_symbol_to_type(&mut self, node_id: NodeId, type_info_id: TypeInfoId) {
|
||||||
let symbol_id = self
|
let symbol_id = self
|
||||||
|
.state
|
||||||
.nodes_to_symbols
|
.nodes_to_symbols
|
||||||
.get(&node_id)
|
.get(&node_id)
|
||||||
.expect(&format!("node_id {} has no associated symbol", node_id));
|
.expect(&format!("node_id {} has no associated symbol", node_id));
|
||||||
self.associate_symbol_to_type(*symbol_id, type_info_id);
|
self.associate_symbol_to_type(*symbol_id, type_info_id);
|
||||||
self.nodes_to_type_infos.insert(node_id, type_info_id);
|
self.state.nodes_to_type_infos.insert(node_id, type_info_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_type_info_id_for_node(&self, node_id: NodeId) -> TypeInfoId {
|
pub fn get_type_info_id_for_node(&self, node_id: NodeId) -> TypeInfoId {
|
||||||
*self
|
*self
|
||||||
|
.state
|
||||||
.nodes_to_type_infos
|
.nodes_to_type_infos
|
||||||
.get(&node_id)
|
.get(&node_id)
|
||||||
.expect(&format!("node_id {} has no associated type", node_id))
|
.expect(&format!("node_id {} has no associated type", node_id))
|
||||||
@ -288,27 +368,34 @@ impl AnalysisContext {
|
|||||||
|
|
||||||
pub fn get_type_info_for_node(&self, node_id: NodeId) -> &TypeInfo {
|
pub fn get_type_info_for_node(&self, node_id: NodeId) -> &TypeInfo {
|
||||||
let type_info_id = self.get_type_info_id_for_node(node_id);
|
let type_info_id = self.get_type_info_id_for_node(node_id);
|
||||||
self.type_infos
|
self.state
|
||||||
|
.type_infos
|
||||||
.get(type_info_id)
|
.get(type_info_id)
|
||||||
.expect(&format!("invalid type_info_id {}", type_info_id))
|
.expect(&format!("invalid type_info_id {}", type_info_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_type_info_by_id(&self, type_info_id: TypeInfoId) -> &TypeInfo {
|
pub fn get_type_info_by_id(&self, type_info_id: TypeInfoId) -> &TypeInfo {
|
||||||
self.type_infos
|
self.state
|
||||||
|
.type_infos
|
||||||
.get(type_info_id)
|
.get(type_info_id)
|
||||||
.expect(&format!("invalid type_info_id {}", type_info_id))
|
.expect(&format!("invalid type_info_id {}", type_info_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn associate_node_to_type(&mut self, node_id: NodeId, type_info_id: TypeInfoId) {
|
pub fn associate_node_to_type(&mut self, node_id: NodeId, type_info_id: TypeInfoId) {
|
||||||
self.nodes_to_type_infos.insert(node_id, type_info_id);
|
self.state.nodes_to_type_infos.insert(node_id, type_info_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lookup_type_for_node_via_symbol(&self, node_id: NodeId) -> TypeInfoId {
|
pub fn lookup_type_for_node_via_symbol(&self, node_id: NodeId) -> TypeInfoId {
|
||||||
let symbol_id = self
|
let symbol_id = self
|
||||||
|
.state
|
||||||
.nodes_to_symbols
|
.nodes_to_symbols
|
||||||
.get(&node_id)
|
.get(&node_id)
|
||||||
.expect(&format!("node_id {} has no associated symbol", node_id));
|
.expect(&format!("node_id {} has no associated symbol", node_id));
|
||||||
let symbol_type_info_id = self.symbols_to_type_infos.get(symbol_id).expect(&format!(
|
let symbol_type_info_id = self
|
||||||
|
.state
|
||||||
|
.symbols_to_type_infos
|
||||||
|
.get(symbol_id)
|
||||||
|
.expect(&format!(
|
||||||
"symbol_id {} has no associated type_info",
|
"symbol_id {} has no associated type_info",
|
||||||
symbol_id
|
symbol_id
|
||||||
));
|
));
|
||||||
@ -318,7 +405,7 @@ impl AnalysisContext {
|
|||||||
pub fn find_symbol_by_fqn(&self, fqn: &str) -> Option<SymbolId> {
|
pub fn find_symbol_by_fqn(&self, fqn: &str) -> Option<SymbolId> {
|
||||||
// This is quite naive and probably very slow; we will certainly use trees of HashMaps (or
|
// This is quite naive and probably very slow; we will certainly use trees of HashMaps (or
|
||||||
// structs with a HashMap and children) to represent the fqn hierarchy.
|
// structs with a HashMap and children) to represent the fqn hierarchy.
|
||||||
for (id, symbol) in self.symbols.iter().enumerate() {
|
for (id, symbol) in self.state.symbols.iter().enumerate() {
|
||||||
match symbol {
|
match symbol {
|
||||||
Symbol::Class(class_symbol) => {
|
Symbol::Class(class_symbol) => {
|
||||||
if class_symbol.fqn() == fqn {
|
if class_symbol.fqn() == fqn {
|
||||||
@ -335,4 +422,43 @@ impl AnalysisContext {
|
|||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_symbol_id(&self, node_id: NodeId) -> Option<SymbolId> {
|
||||||
|
self.state.nodes_to_scopes.get(&node_id).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_class_type(
|
||||||
|
&mut self,
|
||||||
|
class_symbol_id: SymbolId,
|
||||||
|
constructor_symbol_id: Option<SymbolId>,
|
||||||
|
) -> TypeInfoId {
|
||||||
|
if let Some(type_info_id) = self.state.symbols_to_type_infos.get(&class_symbol_id) {
|
||||||
|
*type_info_id
|
||||||
|
} else {
|
||||||
|
let type_info =
|
||||||
|
TypeInfo::Class(ClassTypeInfo::new(class_symbol_id, constructor_symbol_id));
|
||||||
|
self.state.type_infos.push(type_info);
|
||||||
|
let type_info_id = self.state.type_infos.len() - 1;
|
||||||
|
self.state
|
||||||
|
.symbols_to_type_infos
|
||||||
|
.insert(class_symbol_id, type_info_id);
|
||||||
|
type_info_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_type_info_id(&mut self, node_id: NodeId, declared_name: &str) -> TypeInfoId {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn int_type(&mut self) -> &TypeInfoId {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn double_type(&mut self) -> &TypeInfoId {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn type_by_fqn(&mut self, fqn: &str) -> &TypeInfoId {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,8 +7,7 @@ use crate::ast::parameter::Parameter;
|
|||||||
use crate::semantic_analysis::analysis_context::AnalysisContext;
|
use crate::semantic_analysis::analysis_context::AnalysisContext;
|
||||||
use crate::semantic_analysis::symbol::SymbolId;
|
use crate::semantic_analysis::symbol::SymbolId;
|
||||||
use crate::semantic_analysis::type_info::{
|
use crate::semantic_analysis::type_info::{
|
||||||
ConstructorTypeInfo, FunctionTypeInfo, InstanceTypeInfo, TypeConstructorInfo, TypeInfo,
|
ClassTypeInfo, ConstructorTypeInfo, FunctionTypeInfo, InstanceTypeInfo, TypeInfo, TypeInfoId,
|
||||||
TypeInfoId,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn collect_types(compilation_unit: &CompilationUnit, ctx: &mut AnalysisContext) {
|
pub fn collect_types(compilation_unit: &CompilationUnit, ctx: &mut AnalysisContext) {
|
||||||
@ -148,7 +147,7 @@ fn collect_types_class(class: &Class, ctx: &mut AnalysisContext) {
|
|||||||
// todo: fields, methods
|
// todo: fields, methods
|
||||||
|
|
||||||
// Self type constructor
|
// Self type constructor
|
||||||
let type_constructor_type_info = TypeInfo::TypeConstructor(TypeConstructorInfo::new(
|
let type_constructor_type_info = TypeInfo::Class(ClassTypeInfo::new(
|
||||||
ctx.nodes_to_symbols()[&class.node_id()],
|
ctx.nodes_to_symbols()[&class.node_id()],
|
||||||
constructor_type_info_id,
|
constructor_type_info_id,
|
||||||
));
|
));
|
||||||
|
|||||||
@ -255,7 +255,7 @@ fn resolve_types_call(call: &Call, ctx: &mut AnalysisContext, diagnostics: &mut
|
|||||||
let callee_type_info = ctx.get_type_info_for_node(call.callee().node_id());
|
let callee_type_info = ctx.get_type_info_for_node(call.callee().node_id());
|
||||||
|
|
||||||
match callee_type_info {
|
match callee_type_info {
|
||||||
TypeInfo::TypeConstructor(type_constructor_info) => {
|
TypeInfo::Class(type_constructor_info) => {
|
||||||
let class_symbol =
|
let class_symbol =
|
||||||
ctx.symbols()[type_constructor_info.class_symbol_id()].unwrap_class_symbol();
|
ctx.symbols()[type_constructor_info.class_symbol_id()].unwrap_class_symbol();
|
||||||
match class_symbol.constructor_symbol_id() {
|
match class_symbol.constructor_symbol_id() {
|
||||||
|
|||||||
@ -150,7 +150,7 @@ pub fn can_assign_right_to_left(left: &TypeInfo, right: &TypeInfo, ctx: &Analysi
|
|||||||
TypeInfo::Constructor(_) => {
|
TypeInfo::Constructor(_) => {
|
||||||
panic!()
|
panic!()
|
||||||
}
|
}
|
||||||
TypeInfo::TypeConstructor(_) => {
|
TypeInfo::Class(_) => {
|
||||||
panic!()
|
panic!()
|
||||||
}
|
}
|
||||||
TypeInfo::Instance(left_instance_type_info) => match right {
|
TypeInfo::Instance(left_instance_type_info) => match right {
|
||||||
|
|||||||
@ -4,6 +4,7 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
pub type ScopeId = usize;
|
pub type ScopeId = usize;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct Scope {
|
pub struct Scope {
|
||||||
parent_id: Option<ScopeId>,
|
parent_id: Option<ScopeId>,
|
||||||
symbols: HashMap<Rc<str>, SymbolId>,
|
symbols: HashMap<Rc<str>, SymbolId>,
|
||||||
@ -21,6 +22,10 @@ impl Scope {
|
|||||||
self.parent_id
|
self.parent_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_symbol(&self, declared_name: &str) -> bool {
|
||||||
|
self.symbols.contains_key(declared_name)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn insert_symbol(&mut self, declared_name: Rc<str>, symbol_id: SymbolId) {
|
pub fn insert_symbol(&mut self, declared_name: Rc<str>, symbol_id: SymbolId) {
|
||||||
self.symbols.insert(declared_name, symbol_id);
|
self.symbols.insert(declared_name, symbol_id);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
pub type SymbolId = usize;
|
pub type SymbolId = usize;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Symbol {
|
pub enum Symbol {
|
||||||
Class(ClassSymbol),
|
Class(ClassSymbol),
|
||||||
Field(FieldSymbol),
|
Field(FieldSymbol),
|
||||||
@ -60,7 +60,7 @@ impl Symbol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ClassSymbol {
|
pub struct ClassSymbol {
|
||||||
declared_name: Rc<str>,
|
declared_name: Rc<str>,
|
||||||
source_range: Option<SourceRange>,
|
source_range: Option<SourceRange>,
|
||||||
@ -129,7 +129,7 @@ impl ClassSymbol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct FieldSymbol {
|
pub struct FieldSymbol {
|
||||||
declared_name: Rc<str>,
|
declared_name: Rc<str>,
|
||||||
declared_name_source_range: Option<SourceRange>,
|
declared_name_source_range: Option<SourceRange>,
|
||||||
@ -156,7 +156,7 @@ impl FieldSymbol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum FunctionType {
|
pub enum FunctionType {
|
||||||
Function,
|
Function,
|
||||||
Constructor,
|
Constructor,
|
||||||
@ -164,7 +164,7 @@ pub enum FunctionType {
|
|||||||
StaticMethod,
|
StaticMethod,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct FunctionSymbol {
|
pub struct FunctionSymbol {
|
||||||
declared_name: Rc<str>,
|
declared_name: Rc<str>,
|
||||||
declared_name_source_range: Option<SourceRange>,
|
declared_name_source_range: Option<SourceRange>,
|
||||||
@ -219,7 +219,7 @@ impl FunctionSymbol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ParameterSymbol {
|
pub struct ParameterSymbol {
|
||||||
name: Rc<str>,
|
name: Rc<str>,
|
||||||
source_range: Option<SourceRange>,
|
source_range: Option<SourceRange>,
|
||||||
@ -243,7 +243,7 @@ impl ParameterSymbol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct VariableSymbol {
|
pub struct VariableSymbol {
|
||||||
name: Rc<str>,
|
name: Rc<str>,
|
||||||
source_range: Option<SourceRange>,
|
source_range: Option<SourceRange>,
|
||||||
|
|||||||
@ -10,16 +10,14 @@ pub fn collect_types(compilation_unit: &CompilationUnit, ctx: &mut AnalysisConte
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn collect_types_class(class: &Class, ctx: &mut AnalysisContext) {
|
fn collect_types_class(class: &Class, ctx: &mut AnalysisContext) {
|
||||||
let class_symbol_id = *ctx
|
let maybe_constructor_type_info_id = class
|
||||||
.nodes_to_symbols()
|
.constructor()
|
||||||
.get(&class.node_id())
|
.and_then(|constructor| ctx.get_symbol_id(constructor.node_id()));
|
||||||
.expect(&format!(
|
|
||||||
"Could not find class symbol for {} {}",
|
let class_symbol_id = ctx.get_symbol_id(class.node_id()).expect(&format!(
|
||||||
|
"Could not find symbol for {} {}",
|
||||||
class.node_id(),
|
class.node_id(),
|
||||||
class.declared_name()
|
class.declared_name()
|
||||||
));
|
));
|
||||||
|
ctx.make_class_type(class_symbol_id, maybe_constructor_type_info_id);
|
||||||
let self_instance_type_info = TypeInfo::Instance(InstanceTypeInfo::new(class_symbol_id));
|
|
||||||
let self_instance_type_info_id = ctx.insert_type_info(self_instance_type_info);
|
|
||||||
ctx.associate_symbol_to_type(class_symbol_id, self_instance_type_info_id)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,13 @@
|
|||||||
|
use crate::ast::compilation_unit::CompilationUnit;
|
||||||
|
use crate::semantic_analysis::analysis_context::AnalysisContext;
|
||||||
|
use crate::semantic_analysis::type_analysis::collect::collect_types;
|
||||||
|
use crate::semantic_analysis::type_analysis::resolve::resolve_types;
|
||||||
|
|
||||||
mod collect;
|
mod collect;
|
||||||
mod infer;
|
mod infer;
|
||||||
mod resolve;
|
mod resolve;
|
||||||
|
|
||||||
|
pub fn analyze_types(compilation_unit: &CompilationUnit, analysis_context: &mut AnalysisContext) {
|
||||||
|
collect_types(compilation_unit, analysis_context);
|
||||||
|
resolve_types(compilation_unit, analysis_context);
|
||||||
|
}
|
||||||
|
|||||||
@ -1 +1,31 @@
|
|||||||
|
use crate::ast::compilation_unit::CompilationUnit;
|
||||||
|
use crate::ast::function::Function;
|
||||||
|
use crate::ast::parameter::Parameter;
|
||||||
|
use crate::ast::type_use::TypeUse;
|
||||||
|
use crate::semantic_analysis::analysis_context::AnalysisContext;
|
||||||
|
|
||||||
|
pub fn resolve_types(compilation_unit: &CompilationUnit, ctx: &mut AnalysisContext) {
|
||||||
|
for function in compilation_unit.functions() {
|
||||||
|
resolve_types_function(function, ctx);
|
||||||
|
}
|
||||||
|
for extern_function in compilation_unit.extern_functions() {}
|
||||||
|
for class in compilation_unit.classes() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_types_function(function: &Function, ctx: &mut AnalysisContext) {
|
||||||
|
for parameter in function.parameters() {
|
||||||
|
resolve_types_parameter(parameter, ctx);
|
||||||
|
}
|
||||||
|
if let Some(return_type) = function.return_type() {
|
||||||
|
resolve_types_type_use(return_type, ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_types_parameter(parameter: &Parameter, ctx: &mut AnalysisContext) {
|
||||||
|
resolve_types_type_use(parameter.type_use(), ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_types_type_use(type_use: &TypeUse, ctx: &mut AnalysisContext) {
|
||||||
|
let type_info_id = ctx.resolve_type_info_id(type_use.node_id(), type_use.declared_name());
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|||||||
@ -6,9 +6,9 @@ pub type TypeInfoId = usize;
|
|||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum TypeInfo {
|
pub enum TypeInfo {
|
||||||
Any,
|
Any,
|
||||||
Function(FunctionTypeInfo),
|
Class(ClassTypeInfo),
|
||||||
Constructor(ConstructorTypeInfo),
|
Constructor(ConstructorTypeInfo),
|
||||||
TypeConstructor(TypeConstructorInfo),
|
Function(FunctionTypeInfo),
|
||||||
Instance(InstanceTypeInfo),
|
Instance(InstanceTypeInfo),
|
||||||
Int,
|
Int,
|
||||||
Double,
|
Double,
|
||||||
@ -24,9 +24,9 @@ impl TypeInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unwrap_type_constructor(&self) -> &TypeConstructorInfo {
|
pub fn unwrap_class(&self) -> &ClassTypeInfo {
|
||||||
match self {
|
match self {
|
||||||
TypeInfo::TypeConstructor(type_constructor_info) => type_constructor_info,
|
TypeInfo::Class(type_constructor_info) => type_constructor_info,
|
||||||
_ => panic!("Attempt to unwrap {:?} as TypeConstructor", self),
|
_ => panic!("Attempt to unwrap {:?} as TypeConstructor", self),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -89,16 +89,16 @@ impl ConstructorTypeInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct TypeConstructorInfo {
|
pub struct ClassTypeInfo {
|
||||||
class_symbol_id: SymbolId,
|
class_symbol_id: SymbolId,
|
||||||
constructor_type_info_id: Option<TypeInfoId>,
|
constructor_symbol_id: Option<SymbolId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TypeConstructorInfo {
|
impl ClassTypeInfo {
|
||||||
pub fn new(class_symbol_id: SymbolId, constructor_type_info_id: Option<TypeInfoId>) -> Self {
|
pub fn new(class_symbol_id: SymbolId, constructor_symbol_id: Option<SymbolId>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
class_symbol_id,
|
class_symbol_id,
|
||||||
constructor_type_info_id,
|
constructor_symbol_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,8 +106,8 @@ impl TypeConstructorInfo {
|
|||||||
self.class_symbol_id
|
self.class_symbol_id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn constructor_type_info_id(&self) -> Option<TypeInfoId> {
|
pub fn constructor_type_info_id(&self) -> Option<SymbolId> {
|
||||||
self.constructor_type_info_id
|
self.constructor_symbol_id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user