Successfully fixed implicit global println test.

This commit is contained in:
Jesse Brault 2025-11-01 21:38:52 -05:00
parent 0550df534e
commit 6bcef184eb
18 changed files with 473 additions and 134 deletions

View File

@ -11,6 +11,10 @@ use convert_case::{Case, Casing};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
fn make_result() -> TokenStream {
quote! { std::fmt::Result }
}
fn make_polymorphic_enum_loop_p2_impl(spec: &PolymorphicEnumLoopBuildSpec) -> TokenStream {
let type_ident = format_ident!("{}", spec.name());
let type_string = spec.name();
@ -44,9 +48,11 @@ fn make_polymorphic_enum_loop_p2_impl(spec: &PolymorphicEnumLoopBuildSpec) -> To
})
.collect::<Vec<_>>();
let result = make_result();
quote! {
impl PrettyPrint for #type_ident {
fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> {
fn pretty_print(&self, writer: &mut IndentWriter) -> #result {
writer.writeln_indented(#type_string)?;
writer.increase_indent();
#(#child_print_statements)*
@ -70,9 +76,11 @@ fn make_polymorphic_type_p2_impl(spec: &PolymorphicTypeBuildSpec) -> TokenStream
})
.collect::<Vec<_>>();
let result = make_result();
quote! {
impl PrettyPrint for #type_ident {
fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> {
fn pretty_print(&self, writer: &mut IndentWriter) -> #result {
match self {
#(#child_matchers,)*
}
@ -94,9 +102,11 @@ fn make_leaf_enum_p2_impl(spec: &LeafEnumBuildSpec) -> TokenStream {
})
.collect::<Vec<_>>();
let result = make_result();
quote! {
impl PrettyPrint for #type_ident {
fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> {
fn pretty_print(&self, writer: &mut IndentWriter) -> #result {
match self {
#(#child_matchers,)*
}
@ -137,9 +147,11 @@ fn make_tree_enum_p2_impl(spec: &TreeEnumBuildSpec) -> TokenStream {
.map(Option::unwrap)
.collect::<Vec<_>>();
let result = make_result();
quote! {
impl PrettyPrint for #type_ident {
fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> {
fn pretty_print(&self, writer: &mut IndentWriter) -> #result {
writer.writeln_indented(#type_str)?;
writer.increase_indent();
match self {
@ -184,9 +196,11 @@ fn make_leaf_struct_p2_impl(leaf_struct_build_spec: &LeafStructBuildSpec) -> Tok
.map(Option::unwrap)
.collect::<Vec<_>>();
let result = make_result();
quote! {
impl PrettyPrint for #type_ident {
fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> {
fn pretty_print(&self, writer: &mut IndentWriter) -> #result {
writer.writeln_indented(&format!(#format_string, #(#members),*))
}
}
@ -232,7 +246,7 @@ fn make_struct_p2_impl(struct_build_spec: &StructSpec) -> TokenStream {
})
}
},
StructChild::Special(_) => None
StructChild::Special(_) => None,
})
.filter(Option::is_some)
.map(Option::unwrap)
@ -241,9 +255,11 @@ fn make_struct_p2_impl(struct_build_spec: &StructSpec) -> TokenStream {
let type_ident = format_ident!("{}", struct_build_spec.build());
let type_string = struct_build_spec.build();
let result = make_result();
quote! {
impl PrettyPrint for #type_ident {
fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> {
fn pretty_print(&self, writer: &mut IndentWriter) -> #result {
writer.writeln_indented(#type_string)?;
writer.increase_indent();
#(#child_print_statements)*

View File

@ -166,7 +166,7 @@ pub mod pretty_print {
use crate::util::indent_writer::IndentWriter;
pub trait PrettyPrint {
fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()>;
fn pretty_print(&self, writer: &mut IndentWriter) -> std::fmt::Result;
}
include!(concat!(env!("OUT_DIR"), "/src/ast/pretty_print.rs"));

View File

@ -11,10 +11,12 @@ pub fn pretty_print_parse(path: &PathBuf) {
match parse_result {
Ok(mut pairs) => {
let compilation_unit = build_ast(0, &mut pairs);
let mut indent_writer = IndentWriter::new(0, " ", Box::new(std::io::stdout()));
let mut acc = String::new();
let mut indent_writer = IndentWriter::new(0, " ", &mut acc);
compilation_unit
.pretty_print(&mut indent_writer)
.expect("Unable to pretty-print.");
println!("{}", acc);
}
Err(e) => {
eprintln!("{}", e);

View File

@ -33,6 +33,8 @@ pub fn na_p1_compilation_unit(
.set_current_fqn(&fqn.identifiers().map(Identifier::name).collect::<Vec<_>>());
}
}
} else {
symbol_table.set_current_fqn(&[]);
}
symbol_table.push_scope(&format!("FileScope {}", file_name));

View File

@ -67,20 +67,22 @@ pub fn analyze_names<
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::ast_node::AstNodeRef;
use crate::ast::build::build_ast;
use crate::ast::pretty_print::PrettyPrint;
use crate::ast::walk::walk_depth_first;
use crate::name_analysis::symbol::use_symbol::StarUseSymbol;
use crate::name_analysis::symbol::{ExpressibleSymbol, PrimitiveTypeSymbol, TypeSymbol};
use crate::name_analysis::util::str_slice_to_rcs;
use crate::parser::{DeimosParser, Rule};
use crate::std_core::add_std_core_symbols;
use crate::util::indent_writer::IndentWriter;
use codespan_reporting::files::SimpleFiles;
use codespan_reporting::term;
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
use pest::Parser;
use std::collections::HashMap;
use std::ops::Deref;
use std::rc::Rc;
use crate::ast::ast_node::AstNodeRef;
use crate::ast::walk::walk_depth_first;
use crate::name_analysis::symbol::{LVSymbol, PrimitiveTypeSymbol, TypeSymbol};
fn parse_compilation_units<'a>(
files: &mut SimpleFiles<&'a str, &'a str>,
@ -100,7 +102,7 @@ mod tests {
let file_id = files.add(file_name, source);
compilation_units.push(build_ast(file_id, &mut pairs));
}
compilation_units
}
@ -110,13 +112,9 @@ mod tests {
number_of_diagnostics: usize,
) -> (Vec<CompilationUnit>, SimpleFiles<&'a str, &'a str>) {
let mut files = SimpleFiles::<&'a str, &'a str>::new();
let mut to_analyze = parse_compilation_units(&mut files, sources);
let diagnostics = analyze_names(
&mut to_analyze,
&files,
symbol_table
);
let mut compilation_units = parse_compilation_units(&mut files, sources);
let diagnostics = analyze_names(&mut compilation_units, &files, symbol_table);
if diagnostics.len() != number_of_diagnostics {
let writer = StandardStream::stderr(ColorChoice::Always);
@ -127,11 +125,20 @@ mod tests {
}
eprintln!("{}", symbol_table);
for compilation_unit in &compilation_units {
let mut acc = String::new();
let mut indent_writer = IndentWriter::new(0, " ", &mut acc);
compilation_unit
.pretty_print(&mut indent_writer)
.expect("Pretty print failed");
eprintln!("{}", acc);
}
}
assert_eq!(number_of_diagnostics, diagnostics.len());
(to_analyze, files)
(compilation_units, files)
}
fn assert_no_diagnostics<'a>(
@ -140,19 +147,19 @@ mod tests {
) -> (Vec<CompilationUnit>, SimpleFiles<&'a str, &'a str>) {
assert_number_of_diagnostics(sources, symbol_table, 0)
}
fn make_names_to_cus_map<'a>(
compilation_units: &'a [CompilationUnit],
files: &SimpleFiles<&'a str, &str>,
) -> HashMap<&'a str, &'a CompilationUnit> {
let mut map = HashMap::new();
for compilation_unit in compilation_units {
let file_id = compilation_unit.file_id();
let name = files.name(file_id).unwrap();
map.insert(name, compilation_unit);
}
map
}
@ -171,11 +178,11 @@ mod tests {
let (cus, files) = assert_no_diagnostics(sources, &mut symbol_table);
let results_map = make_names_to_cus_map(&cus, &files);
let main_cu = *results_map.get("main.dm").unwrap();
let mut found_main = false;
let mut found_x = false;
let mut found_args_ref = false;
walk_depth_first(main_cu, &mut |node| {
use AstNodeRef::*;
match node {
@ -209,34 +216,35 @@ mod tests {
_ => panic!("Expected a primitive type symbol for args.")
}
}
},
}
VariableDeclaration(variable_declaration) => {
if variable_declaration.identifier().name() == "x" {
found_x = true;
assert_eq!(
variable_declaration.variable_symbol().unwrap().borrow().declared_name(),
variable_declaration
.variable_symbol()
.unwrap()
.borrow()
.declared_name(),
"x"
);
}
}
VariableUse(variable_use) => {
if variable_use.identifier().name() == "args" {
IdentifierExpression(identifier_expression) => {
if identifier_expression.identifier().name() == "args" {
found_args_ref = true;
match variable_use.lv_symbol().unwrap() {
LVSymbol::Parameter(parameter_symbol) => {
assert_eq!(
parameter_symbol.borrow().declared_name(),
"args"
)
match identifier_expression.expressible_symbol().unwrap() {
ExpressibleSymbol::Parameter(parameter_symbol) => {
assert_eq!(parameter_symbol.borrow().declared_name(), "args")
}
_ => panic!("Expected args variable use to be a parameter symbol.")
_ => panic!("Expected args variable use to be a parameter symbol."),
}
}
}
_ => {}
}
});
assert!(found_main);
assert!(found_x);
assert!(found_args_ref);
@ -299,17 +307,24 @@ mod tests {
fn main(args: Array<String>)
println args
end
"
",
)]);
let mut symbol_table = SymbolTable::new();
let global_std_core_use = StarUseSymbol::new(
&[Rc::from("std"), Rc::from("core")],
None
);
symbol_table.insert_star_use_symbol(global_std_core_use)
let global_std_core_star_use_to_insert =
StarUseSymbol::new(&[Rc::from("std"), Rc::from("core")], None);
let global_std_core_star_use = symbol_table
.insert_star_use_symbol(global_std_core_star_use_to_insert)
.expect("Failed to insert star use symbol.");
add_std_core_symbols(&mut symbol_table).expect("Failed to add std/core symbols.");
let resolved_std_core_symbols = symbol_table
.find_usable_symbols_by_base_fqn(&str_slice_to_rcs(&["std", "core"]))
.unwrap();
global_std_core_star_use
.borrow_mut()
.set_resolved_symbols(resolved_std_core_symbols);
assert_no_diagnostics(sources, &mut symbol_table);
}
}

View File

@ -1,10 +1,11 @@
use crate::ast::node::{
AssignmentStatement, CompilationUnit, ConcreteUseStatement, ConcreteUseStatementSuffix,
Expression, ExpressionStatement, Function, FunctionAliasBody, FunctionBlockBody, FunctionBody,
FunctionEqualsBody, GenericParameters, Identifier, IdentifierOrFqn, LValue, LValueSuffix,
ModuleLevelDeclaration, Parameter, Parameters, PrimitiveType, ReturnType, StarUseStatement,
Statement, TypeUse, TypedArray, UseStatement, UseStatementIdentifier, UseStatementPrefix,
VariableDeclaration, VariableUse,
AssignmentStatement, Call, CompilationUnit, ConcreteUseStatement, ConcreteUseStatementSuffix,
Expression, ExpressionList, ExpressionStatement, Function, FunctionAliasBody,
FunctionBlockBody, FunctionBody, FunctionEqualsBody, GenericParameters, Identifier,
IdentifierExpression, IdentifierOrFqn, LValue, LValueSuffix, ModuleLevelDeclaration,
ObjectIndex, Parameter, Parameters, PrimitiveType, ReturnType, StarUseStatement, Statement,
SuffixExpression, SuffixOperator, TypeUse, TypedArray, UseStatement, UseStatementIdentifier,
UseStatementPrefix, VariableDeclaration,
};
use crate::diagnostic::DmDiagnostic;
use crate::name_analysis::symbol::source_definition::SourceDefinition;
@ -516,7 +517,20 @@ fn na_p2_l_value(
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
na_p2_variable_use(l_value.variable_use_mut(), symbol_table, diagnostics);
match symbol_table.lookup_lv_symbol(l_value.identifier().name()) {
Ok(lv_symbol) => {
l_value.set_lv_symbol(lv_symbol);
}
Err(symbol_lookup_error) => {
handle_lookup_error(
symbol_lookup_error,
l_value.identifier().name(),
l_value.identifier().file_id(),
l_value.identifier().range(),
diagnostics,
);
}
}
// "Name analysis" of suffixes is done during type-checking. We don't want to deal with getting
// out the different potential return types of things until that point, especially if we end up
// adding dynamic objects to the language. However, suffixes may contain things (like arguments)
@ -554,27 +568,6 @@ fn na_p2_expression_statement(
);
}
fn na_p2_variable_use(
variable_use: &mut VariableUse,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
match symbol_table.lookup_lv_symbol(variable_use.identifier().name()) {
Ok(lv_symbol) => {
variable_use.set_lv_symbol(lv_symbol);
}
Err(symbol_lookup_error) => {
handle_lookup_error(
symbol_lookup_error,
variable_use.identifier().name(),
variable_use.identifier().file_id(),
variable_use.identifier().range(),
diagnostics,
);
}
}
}
fn na_p2_expression(
expression: &mut Expression,
symbol_table: &mut SymbolTable,
@ -606,13 +599,13 @@ fn na_p2_expression(
todo!()
}
Expression::Suffix(suffix) => {
todo!()
na_p2_suffix_expression(suffix, symbol_table, diagnostics);
}
Expression::Literal(literal) => {
todo!()
}
Expression::VariableUse(variable_use) => {
na_p2_variable_use(variable_use, symbol_table, diagnostics);
Expression::Identifier(identifier_expression) => {
na_p2_identifier_expression(identifier_expression, symbol_table, diagnostics);
}
Expression::Fqn(fqn) => {
todo!()
@ -625,3 +618,111 @@ fn na_p2_expression(
}
}
}
fn na_p2_suffix_expression(
suffix_expression: &mut SuffixExpression,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
na_p2_expression(
suffix_expression.expression_mut(),
symbol_table,
diagnostics,
);
na_p2_suffix_operator(suffix_expression.operator_mut(), symbol_table, diagnostics);
}
fn na_p2_suffix_operator(
suffix_operator: &mut SuffixOperator,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
match suffix_operator {
SuffixOperator::PlusPlus => {
// no-op
}
SuffixOperator::MinusMinus => {
// no-op
}
SuffixOperator::ObjectProperty(_) => {
// no-op; props checked during type checking
}
SuffixOperator::ObjectIndex(object_index) => {
na_p2_object_index(object_index, symbol_table, diagnostics);
}
SuffixOperator::Call(call) => {
na_p2_call(call, symbol_table, diagnostics);
}
}
}
fn na_p2_object_index(
object_index: &mut ObjectIndex,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
na_p2_expression(object_index.expression_mut(), symbol_table, diagnostics);
}
fn na_p2_call(
call: &mut Call,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
// TODO: modify the ast-gen so that we don't have to match on these. They should be the same.
match call {
Call::ParenthesesCall(parentheses_call) => {
if let Some(turbo_fish) = parentheses_call.turbo_fish_mut() {
todo!()
}
if let Some(expression_list) = parentheses_call.expression_list_mut() {
na_p2_expression_list(expression_list, symbol_table, diagnostics);
}
if let Some(closure) = parentheses_call.closure_mut() {
todo!()
}
}
Call::NonParenthesesCall(non_parentheses_call) => {
if let Some(turbo_fish) = non_parentheses_call.turbo_fish_mut() {
todo!()
}
if let Some(expression_list) = non_parentheses_call.expression_list_mut() {
na_p2_expression_list(expression_list, symbol_table, diagnostics);
}
if let Some(closure) = non_parentheses_call.closure_mut() {
todo!()
}
}
}
}
fn na_p2_identifier_expression(
identifier_expression: &mut IdentifierExpression,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
match symbol_table.lookup_expressible_symbol(identifier_expression.identifier().name()) {
Ok(expressible_symbol) => {
identifier_expression.set_expressible_symbol(expressible_symbol);
}
Err(symbol_lookup_error) => {
handle_lookup_error(
symbol_lookup_error,
identifier_expression.identifier().name(),
identifier_expression.identifier().file_id(),
identifier_expression.identifier().range(),
diagnostics,
);
}
}
}
fn na_p2_expression_list(
expression_list: &mut ExpressionList,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
for expression in expression_list.expressions_mut() {
na_p2_expression(expression, symbol_table, diagnostics);
}
}

View File

@ -0,0 +1,33 @@
use crate::name_analysis::symbol::{ClassMemberSymbol, ClassSymbol, FunctionSymbol, ParameterSymbol, Symbol, VariableSymbol};
use std::cell::RefCell;
use std::rc::Rc;
pub enum ExpressibleSymbol {
Class(Rc<RefCell<ClassSymbol>>),
Function(Rc<RefCell<FunctionSymbol>>),
ClassMember(Rc<RefCell<ClassMemberSymbol>>),
Parameter(Rc<RefCell<ParameterSymbol>>),
Variable(Rc<RefCell<VariableSymbol>>),
}
impl ExpressibleSymbol {
pub fn to_symbol(self) -> Rc<RefCell<dyn Symbol>> {
match self {
ExpressibleSymbol::Class(class_symbol) => {
class_symbol as Rc<RefCell<dyn Symbol>>
}
ExpressibleSymbol::Function(function_symbol) => {
function_symbol as Rc<RefCell<dyn Symbol>>
}
ExpressibleSymbol::ClassMember(class_member_symbol) => {
class_member_symbol as Rc<RefCell<dyn Symbol>>
}
ExpressibleSymbol::Parameter(parameter_symbol) => {
parameter_symbol as Rc<RefCell<dyn Symbol>>
}
ExpressibleSymbol::Variable(variable_symbol) => {
variable_symbol as Rc<RefCell<dyn Symbol>>
}
}
}
}

View File

@ -1,5 +1,6 @@
pub mod class_member_symbol;
pub mod class_symbol;
pub mod expressible_symbol;
pub mod function_symbol;
pub mod generic_type_symbol;
pub mod interface_symbol;
@ -18,10 +19,10 @@ use crate::name_analysis::symbol::source_definition::SourceDefinition;
use std::fmt::Debug;
pub use self::{
class_member_symbol::*, class_symbol::*, function_symbol::*, function_symbol::*,
generic_type_symbol::*, interface_symbol::*, lv_symbol::*, module_level_symbol::*,
module_symbol::*, parameter_symbol::*, primitive_type_symbol::*, type_symbol::*,
usable_symbol::*, use_symbol::*, variable_symbol::*,
class_member_symbol::*, class_symbol::*, expressible_symbol::*, function_symbol::*,
function_symbol::*, generic_type_symbol::*, interface_symbol::*, lv_symbol::*,
module_level_symbol::*, module_symbol::*, parameter_symbol::*, primitive_type_symbol::*,
type_symbol::*, usable_symbol::*, use_symbol::*, variable_symbol::*,
};
pub trait Symbol: Debug {

View File

@ -51,6 +51,7 @@ impl Debug for ParameterSymbol {
f.debug_struct("ParameterSymbol")
.field("declared_name", &self.declared_name)
.field("source_definition", &self.source_definition)
.field("type_symbol", &self.type_symbol)
.finish()
}
}

View File

@ -109,9 +109,10 @@ impl Symbol for StarUseSymbol {
impl Debug for StarUseSymbol {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("StarUseStatementSymbol")
f.debug_struct("StarUseSymbol")
.field("fqn", &join_fqn_parts(&self.fqn_parts))
.field("source_definition", &self.source_definition)
.field("resolved_symbols", &self.resolved_symbols)
.finish()
}
}

View File

@ -5,7 +5,8 @@ use crate::name_analysis::symbol::parameter_symbol::ParameterSymbol;
use crate::name_analysis::symbol::type_symbol::TypeSymbol;
use crate::name_analysis::symbol::usable_symbol::UsableSymbol;
use crate::name_analysis::symbol::use_symbol::{ConcreteUseSymbol, StarUseSymbol};
use crate::name_analysis::symbol::{LVSymbol, Symbol};
use crate::name_analysis::symbol::variable_symbol::VariableSymbol;
use crate::name_analysis::symbol::{ExpressibleSymbol, LVSymbol, Symbol};
use crate::name_analysis::symbol_table::fqn_context::FqnContext;
use crate::name_analysis::symbol_table::symbol_tree::SymbolTree;
use crate::name_analysis::symbol_table::SymbolInsertError::SymbolAlreadyDefined;
@ -14,7 +15,6 @@ use std::cell::RefCell;
use std::fmt::Display;
use std::ops::Deref;
use std::rc::Rc;
use crate::name_analysis::symbol::variable_symbol::VariableSymbol;
pub(self) mod fqn_context;
mod scope;
@ -25,6 +25,7 @@ pub enum SymbolInsertError {
SymbolAlreadyDefined(Rc<RefCell<dyn Symbol>>),
}
#[derive(Debug)]
pub enum SymbolLookupError {
NoDefinition,
NoSuchNamespace,
@ -44,7 +45,7 @@ impl SymbolTable {
Self {
scopes: vec![Scope::new(None, 0, String::from("GlobalScope"))],
current_scope_id: 0,
symbol_tree: Box::new(SymbolTree::new()),
symbol_tree: Box::new(SymbolTree::new("<DEFAULT>")),
fqn_context: Box::new(FqnContext::new()),
}
}
@ -68,7 +69,7 @@ impl SymbolTable {
self.current_scope_id = parent_id;
}
}
pub fn set_current_scope(&mut self, id: usize) {
self.current_scope_id = id;
}
@ -122,6 +123,15 @@ impl SymbolTable {
self.symbol_tree.register_module_by_fqn_parts(fqn_parts);
}
pub fn register_function_symbol(
&mut self,
function_symbol: FunctionSymbol,
) -> Rc<RefCell<FunctionSymbol>> {
let as_rc = Rc::new(RefCell::new(function_symbol));
self.symbol_tree.register_function(as_rc.clone());
as_rc
}
pub fn find_usable_symbols_by_base_fqn(
&self,
fqn_parts: &[Rc<str>],
@ -231,7 +241,7 @@ impl SymbolTable {
Ok(inserted)
}
}
pub fn insert_variable_symbol(
&mut self,
variable_symbol: VariableSymbol,
@ -266,8 +276,11 @@ impl SymbolTable {
pub fn lookup_type_by_fqn(&self, fqn_parts: &[&str]) -> Result<TypeSymbol, SymbolLookupError> {
todo!()
}
pub fn lookup_function_symbol(&self, declared_name: &str) -> Result<Rc<RefCell<FunctionSymbol>>, SymbolLookupError> {
pub fn lookup_function_symbol(
&self,
declared_name: &str,
) -> Result<Rc<RefCell<FunctionSymbol>>, SymbolLookupError> {
let mut current_scope: Option<&Scope> = Some(self.current_scope());
while let Some(scope) = current_scope.take() {
if let Some(function_symbol) = scope.find_function_symbol(declared_name) {
@ -280,12 +293,29 @@ impl SymbolTable {
}
Err(SymbolLookupError::NoDefinition)
}
pub fn lookup_lv_symbol(&self, declared_name: &str) -> Result<LVSymbol, SymbolLookupError> {
let mut current_scope: Option<&Scope> = Some(self.current_scope());
while let Some(scope) = current_scope.take() {
if let Some(lv_symbol) = scope.find_lv_symbol(declared_name) {
return Ok(lv_symbol)
return Ok(lv_symbol);
} else {
current_scope = scope
.parent()
.and_then(|parent_id| self.scopes.get(parent_id));
}
}
Err(SymbolLookupError::NoDefinition)
}
pub fn lookup_expressible_symbol(
&self,
declared_name: &str,
) -> Result<ExpressibleSymbol, SymbolLookupError> {
let mut current_scope: Option<&Scope> = Some(self.current_scope());
while let Some(scope) = current_scope.take() {
if let Some(expressible_symbol) = scope.find_expressible_symbol(declared_name) {
return Ok(expressible_symbol);
} else {
current_scope = scope
.parent()
@ -302,6 +332,8 @@ impl Display for SymbolTable {
for scope in &self.scopes {
writeln!(f, "{}", scope)?;
}
writeln!(f, "---SymbolTable: SymbolTree---")?;
writeln!(f, "{}", self.symbol_tree)?;
Ok(())
}
}

View File

@ -10,6 +10,7 @@ use crate::name_analysis::symbol::parameter_symbol::ParameterSymbol;
use crate::name_analysis::symbol::type_symbol::TypeSymbol;
use crate::name_analysis::symbol::use_symbol::{ConcreteUseSymbol, StarUseSymbol};
use crate::name_analysis::symbol::variable_symbol::VariableSymbol;
use crate::name_analysis::symbol::{ExpressibleSymbol, UsableSymbol};
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt::{Display, Formatter};
@ -142,7 +143,7 @@ impl Scope {
let key = symbol.declared_name_owned();
insert_symbol!(self.parameter_symbols, symbol, key)
}
pub fn insert_variable_symbol(
&mut self,
symbol: VariableSymbol,
@ -203,11 +204,84 @@ impl Scope {
.map(|variable_symbol| LVSymbol::Variable(variable_symbol.clone()))
})
}
pub fn find_function_symbol(&self, declared_name: &str) -> Option<&Rc<RefCell<FunctionSymbol>>> {
pub fn find_function_symbol(
&self,
declared_name: &str,
) -> Option<&Rc<RefCell<FunctionSymbol>>> {
self.function_symbols.get(declared_name)
}
pub fn find_expressible_symbol(&self, declared_name: &str) -> Option<ExpressibleSymbol> {
// First, try all the usual suspects: classes, functions, members, parameters, and variables
// which are in scope. If we don't find any of those, try looking through imports; start
// with concrete imports by declared name; if none of those match, check all resolved
// symbols of the star imports until one is found or none.
// !! Eventually consider registering star-imported symbols in a scope so that we can just
// check them via the hash maps instead of looping through the star symbols.
self.class_symbols
.get(declared_name)
.map(|class_symbol| ExpressibleSymbol::Class(class_symbol.clone()))
.or_else(|| {
self.function_symbols
.get(declared_name)
.map(|function_symbol| ExpressibleSymbol::Function(function_symbol.clone()))
})
.or_else(|| {
self.class_member_symbols
.get(declared_name)
.map(|class_member_symbol| {
ExpressibleSymbol::ClassMember(class_member_symbol.clone())
})
})
.or_else(|| {
self.parameter_symbols
.get(declared_name)
.map(|parameter_symbol| ExpressibleSymbol::Parameter(parameter_symbol.clone()))
})
.or_else(|| {
self.variable_symbols
.get(declared_name)
.map(|variable_symbol| ExpressibleSymbol::Variable(variable_symbol.clone()))
})
.or_else(|| {
if let Some(concrete_use_symbol) = self.concrete_use_symbols.get(declared_name) {
return match concrete_use_symbol.borrow().resolved_symbol().unwrap() {
UsableSymbol::Interface(_) => {
None
}
UsableSymbol::Class(class_symbol) => {
Some(ExpressibleSymbol::Class(class_symbol.clone()))
}
UsableSymbol::Function(function_symbol) => {
Some(ExpressibleSymbol::Function(function_symbol.clone()))
}
}
}
None
})
.or_else(|| {
for star_use_symbol in self.star_use_symbols.values() {
for usable_symbol in star_use_symbol.borrow().resolved_symbols() {
match usable_symbol {
UsableSymbol::Class(class_symbol) => {
if class_symbol.borrow().declared_name() == declared_name {
return Some(ExpressibleSymbol::Class(class_symbol.clone()));
}
}
UsableSymbol::Function(function_symbol) => {
if function_symbol.borrow().declared_name() == declared_name {
return Some(ExpressibleSymbol::Function(function_symbol.clone()));
}
}
_ => continue,
}
}
}
None
})
}
pub fn debug_name(&self) -> &str {
&self.debug_name
}

View File

@ -4,12 +4,15 @@ use crate::name_analysis::symbol::interface_symbol::InterfaceSymbol;
use crate::name_analysis::symbol::module_symbol::ModuleSymbol;
use crate::name_analysis::symbol::usable_symbol::UsableSymbol;
use crate::name_analysis::symbol_table::SymbolLookupError;
use crate::util::indent_writer::IndentWriter;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt::{Display, Formatter};
use std::rc::Rc;
#[derive(Debug)]
pub struct SymbolTree {
debug_name: String,
children: Box<HashMap<Rc<str>, SymbolTree>>,
classes: Box<HashMap<Rc<str>, Rc<RefCell<ClassSymbol>>>>,
interfaces: Box<HashMap<Rc<str>, Rc<RefCell<InterfaceSymbol>>>>,
@ -17,8 +20,9 @@ pub struct SymbolTree {
}
impl SymbolTree {
pub fn new() -> Self {
pub fn new(debug_name: &str) -> Self {
Self {
debug_name: debug_name.to_string(),
children: Box::new(HashMap::new()),
classes: Box::new(HashMap::new()),
interfaces: Box::new(HashMap::new()),
@ -78,13 +82,13 @@ impl SymbolTree {
}
if fqn_parts.len() == 1 {
self.children
.insert(fqn_parts[0].clone(), SymbolTree::new());
.insert(fqn_parts[0].clone(), SymbolTree::new(fqn_parts[0].as_ref()));
} else {
if self.children.contains_key(fqn_parts[0].as_ref()) {
let child = self.children.get_mut(fqn_parts[0].as_ref()).unwrap();
child.recurse_register_module(&fqn_parts[1..]);
} else {
let mut child = SymbolTree::new();
let mut child = SymbolTree::new(fqn_parts[0].as_ref());
child.recurse_register_module(&fqn_parts[1..]);
self.children.insert(fqn_parts[0].clone(), child);
}
@ -105,6 +109,12 @@ impl SymbolTree {
panic!("Unable to register function fqn with no parts.");
}
if fqn_parts.len() == 1 {
if self.functions.contains_key(fqn_parts[0].as_ref()) {
panic!(
"There is already a function registered in this symbol tree with the name {}",
fqn_parts[0]
);
}
self.functions.insert(fqn_parts[0].clone(), function_symbol);
} else {
if self.children.contains_key(fqn_parts[0].as_ref()) {
@ -137,7 +147,7 @@ impl SymbolTree {
_ => {
if self.children.contains_key(fqn_parts[0].as_ref()) {
let child = self.children.get(fqn_parts[0].as_ref()).unwrap();
child.find_all_by_base_fqn(fqn_parts)
child.find_all_by_base_fqn(&fqn_parts[1..])
} else {
Err(SymbolLookupError::NoSuchNamespace)
}
@ -145,3 +155,55 @@ impl SymbolTree {
}
}
}
enum FormatAction<'a> {
PrintSymbolTree(&'a SymbolTree),
IncreaseIndent,
DecreaseIndent
}
impl Display for SymbolTree {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut acc = String::new();
let mut indent_writer = IndentWriter::new(0, " ", &mut acc);
let mut stack: Vec<FormatAction> = vec![];
stack.push(FormatAction::PrintSymbolTree(self));
while let Some(format_action) = stack.pop() {
match format_action {
FormatAction::PrintSymbolTree(symbol_tree) => {
// reverse order: start with decrease
stack.push(FormatAction::DecreaseIndent);
// add children
for child in symbol_tree.children.values() {
stack.push(FormatAction::PrintSymbolTree(child));
}
// increase for the first child
stack.push(FormatAction::IncreaseIndent);
indent_writer.writeln_indented(&format!("SymbolTree(debug_name = {})", symbol_tree.debug_name))?;
indent_writer.increase_indent();
for interface in symbol_tree.interfaces.values() {
indent_writer.writeln_indented(&format!("Interface({:?}", interface))?;
}
for class in symbol_tree.classes.values() {
indent_writer.writeln_indented(&format!("Class({:?})", class))?;
}
for function in symbol_tree.functions.values() {
indent_writer.writeln_indented(&format!("Function({:?})", function))?;
}
indent_writer.decrease_indent();
}
FormatAction::IncreaseIndent => {
indent_writer.increase_indent();
}
FormatAction::DecreaseIndent => {
indent_writer.decrease_indent();
}
}
}
writeln!(f, "{}", acc)
}
}

View File

@ -50,12 +50,12 @@ pub fn handle_lookup_error(
Label::primary(error_file_id, error_range).with_message("Symbol used here."),
);
diagnostics.push(diagnostic);
},
}
SymbolLookupError::NoSuchNamespace => {
let diagnostic = Diagnostic::error()
.with_message(format!("No such namespace '{}' found", error_symbol_name))
.with_label(
Label::primary(error_file_id, error_range).with_message("Namespace used here.")
Label::primary(error_file_id, error_range).with_message("Namespace used here."),
);
diagnostics.push(diagnostic);
}
@ -69,3 +69,7 @@ pub fn format_fqn(parts: &[&str]) -> String {
pub fn join_fqn_parts(parts: &[Rc<str>]) -> String {
format_fqn(&parts.iter().map(|part| part.as_ref()).collect::<Vec<_>>())
}
pub fn str_slice_to_rcs(str_slice: &[&str]) -> Vec<Rc<str>> {
str_slice.iter().map(|s| Rc::from(*s)).collect::<Vec<_>>()
}

View File

@ -785,25 +785,19 @@ ForStatement:
LValue:
struct:
children:
- variable_use
- identifier
- suffixes:
vec:
rule: LValueSuffix
fields:
- lv_symbol:
kind: LVSymbol
LValueSuffix:
tree_enum:
rules:
- ObjectProperty
- ObjectIndex
# VariableUse
VariableUse:
struct:
children:
- identifier
fields:
- lv_symbol:
kind: LVSymbol
# Expressions
Expression:
polymorphic_type:
@ -838,9 +832,9 @@ Expression:
- Literal:
inner:
kind: Literal
- VariableUse:
- Identifier:
inner:
kind: VariableUse
kind: IdentifierExpression
- Fqn:
inner:
kind: FullyQualifiedName
@ -1183,9 +1177,9 @@ PrimaryExpression:
- Literal:
inner:
kind: Literal
- VariableUse:
- Identifier:
inner:
kind: VariableUse
kind: IdentifierExpression
- Fqn:
inner:
kind: FullyQualifiedName
@ -1198,6 +1192,13 @@ PrimaryExpression:
- ParenthesizedExpression:
pass_through:
kind: Expression
IdentifierExpression:
struct:
children:
- identifier
fields:
- expressible_symbol:
kind: ExpressibleSymbol
ListExpression:
struct:
children:

View File

@ -615,7 +615,7 @@ ForStatement = {
// LValue
LValue = {
VariableUse
Identifier
~ LValueSuffix*
}
@ -624,12 +624,6 @@ LValueSuffix = {
| ObjectIndex
}
// Variable Use
VariableUse = {
Identifier
}
// Expressions
Expression = {
@ -778,13 +772,17 @@ ObjectIndex = {
PrimaryExpression = {
Literal
| VariableUse
| IdentifierExpression
| FullyQualifiedName
| Closure
| ListExpression
| ParenthesizedExpression
}
IdentifierExpression = {
Identifier
}
ListExpression = {
"["
~ ExpressionList?

View File

@ -26,7 +26,7 @@ pub fn add_std_core_symbols(symbol_table: &mut SymbolTable) -> Result<(), Symbol
&vec![Rc::new(RefCell::new(println_msg_symbol))],
None,
);
symbol_table.insert_function_symbol(println_symbol)?;
symbol_table.register_function_symbol(println_symbol);
Ok(())
}

View File

@ -1,15 +1,11 @@
pub struct IndentWriter {
pub struct IndentWriter<'a> {
indent_level: usize,
indent_string: String,
out: Box<dyn std::io::Write>,
out: &'a mut dyn std::fmt::Write,
}
impl IndentWriter {
pub fn new(
start_level: usize,
indent_string: &str,
out: Box<dyn std::io::Write>,
) -> IndentWriter {
impl<'a> IndentWriter<'a> {
pub fn new(start_level: usize, indent_string: &str, out: &'a mut dyn std::fmt::Write) -> Self {
IndentWriter {
indent_level: start_level,
indent_string: indent_string.to_string(),
@ -25,22 +21,22 @@ impl IndentWriter {
self.indent_level -= 1;
}
pub fn write(&mut self, s: &str) -> std::io::Result<()> {
pub fn write(&mut self, s: &str) -> std::fmt::Result {
write!(self.out, "{}", s)
}
pub fn writeln(&mut self, s: &str) -> std::io::Result<()> {
pub fn writeln(&mut self, s: &str) -> std::fmt::Result {
self.write(&format!("{}\n", s))
}
pub fn write_indented(&mut self, s: &str) -> std::io::Result<()> {
pub fn write_indented(&mut self, s: &str) -> std::fmt::Result {
for _ in 0..self.indent_level {
write!(self.out, "{}", self.indent_string)?;
}
write!(self.out, "{}", s)
}
pub fn writeln_indented(&mut self, s: &str) -> std::io::Result<()> {
pub fn writeln_indented(&mut self, s: &str) -> std::fmt::Result {
self.write_indented(&format!("{}\n", s))
}
}