Compare commits

..

3 Commits

Author SHA1 Message Date
Jesse Brault
51c39f5f34 WIP fixing newlines/call grammar. 2025-11-03 13:41:05 -06:00
Jesse Brault
1d11a45c68 Seem to have fixed newlines and no-parentheses calls. 2025-11-03 12:16:28 -06:00
Jesse Brault
da05bb101b Better handling of global imports. 2025-11-02 13:41:00 -06:00
9 changed files with 401 additions and 66 deletions

View File

@ -87,7 +87,11 @@ pub mod build {
#[cfg(test)]
mod build_tests {
use super::*;
use crate::ast::ast_node::AstNodeRef;
use crate::ast::pretty_print::PrettyPrint;
use crate::ast::walk::walk_depth_first;
use crate::parser::DeimosParser;
use crate::util::indent_writer::IndentWriter;
use pest::Parser;
fn parse(rule: Rule, input: &str) -> Pair<Rule> {
@ -95,6 +99,14 @@ pub mod build {
parse_result.expect("parsing failed").next().unwrap()
}
fn fmt_ast(node: &dyn PrettyPrint) -> String {
let mut acc = String::new();
let mut indent_writer = IndentWriter::new(0, " ", &mut acc);
node.pretty_print(&mut indent_writer)
.expect("Pretty print failed");
acc
}
#[test]
fn boolean_literal_true() {
let pair = parse(
@ -159,6 +171,31 @@ pub mod build {
let pair = parse(Rule::Expression, "hello(42)");
let expression = build_expression(0, pair);
}
#[test]
fn multiple_expression_statements() {
let pair = parse(
Rule::Function,
"
fn test()
println 42
println 43
end
",
);
let function = build_function(0, pair);
let mut number_of_statements = 0;
walk_depth_first(&function, &mut |node| match node {
AstNodeRef::Statement(_) => {
number_of_statements += 1;
}
_ => {}
});
assert_eq!(2, number_of_statements, "Ast: {}", fmt_ast(&function));
}
}
}

View File

@ -1,8 +1,7 @@
use crate::ast::node::{
CompilationUnit, ConcreteUseStatement, ConcreteUseStatementSuffix, Function, FunctionBody
, Identifier, IdentifierOrFqn, Module, ModuleLevelDeclaration
, StarUseStatement, UseStatement,
UseStatementIdentifier, UseStatementPrefix,
CompilationUnit, ConcreteUseStatement, ConcreteUseStatementSuffix, Function, Identifier,
IdentifierOrFqn, Module, ModuleLevelDeclaration, PlatformFunction, StarUseStatement,
UseStatement, UseStatementIdentifier, UseStatementPrefix,
};
use crate::diagnostic::DmDiagnostic;
use crate::name_analysis::symbol::function_symbol::FunctionSymbol;
@ -11,10 +10,9 @@ use crate::name_analysis::symbol::module_symbol::ModuleSymbol;
use crate::name_analysis::symbol::source_definition::SourceDefinition;
use crate::name_analysis::symbol::use_symbol::{ConcreteUseSymbol, StarUseSymbol};
use crate::name_analysis::symbol_table::SymbolTable;
use crate::name_analysis::util::{
handle_insert_error, join_fqn_parts,
};
use crate::name_analysis::util::{handle_insert_error, join_fqn_parts};
use std::cell::RefCell;
use std::process::id;
use std::rc::Rc;
pub fn na_p1_compilation_unit(
@ -26,13 +24,26 @@ pub fn na_p1_compilation_unit(
if let Some(namespace) = compilation_unit.namespace() {
match namespace.identifier_or_fqn() {
IdentifierOrFqn::Identifier(identifier) => {
symbol_table.set_current_fqn(&[identifier.name()])
symbol_table.set_current_fqn(&[identifier.name()]);
}
IdentifierOrFqn::FullyQualifiedName(fqn) => {
symbol_table
.set_current_fqn(&fqn.identifiers().map(Identifier::name).collect::<Vec<_>>());
}
}
let fqn_parts = match namespace.identifier_or_fqn() {
IdentifierOrFqn::Identifier(identifier) => {
vec![identifier.name()]
}
IdentifierOrFqn::FullyQualifiedName(fqn) => {
let mut parts = vec![];
for identifier in fqn.identifiers() {
parts.push(identifier.name());
}
parts
}
};
symbol_table.register_module(&fqn_parts);
} else {
symbol_table.set_current_fqn(&[]);
}
@ -187,7 +198,9 @@ fn na_p1_module_level_declaration(
.map(|function_symbol| ModuleLevelSymbol::Function(function_symbol))
}
ModuleLevelDeclaration::PlatformFunction(platform_function) => {
todo!()
na_p1_platform_function(platform_function, symbol_table, diagnostics).map(
|platform_function_symbol| ModuleLevelSymbol::Function(platform_function_symbol),
)
}
}
}
@ -272,3 +285,45 @@ fn na_p1_function(
maybe_function_symbol
}
fn na_p1_platform_function(
platform_function: &mut PlatformFunction,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) -> Option<Rc<RefCell<FunctionSymbol>>> {
let to_insert = FunctionSymbol::new(
&symbol_table.resolve_fqn(Rc::from(platform_function.identifier().name())),
platform_function.is_public(),
true,
Some(SourceDefinition::from_identifier(
platform_function.identifier(),
)),
);
let maybe_function_symbol = match symbol_table.insert_function_symbol(to_insert) {
Ok(function_symbol) => Some(function_symbol),
Err(symbol_insert_error) => {
handle_insert_error(
symbol_insert_error,
platform_function.identifier().name(),
platform_function.identifier().file_id(),
platform_function.identifier().range(),
diagnostics,
);
None
}
};
if maybe_function_symbol.is_some() {
platform_function.set_function_symbol(maybe_function_symbol.as_ref().unwrap().clone());
}
// create a scope for later
symbol_table.push_scope(&format!(
"PlatformFunctionScope {}",
platform_function.identifier().name()
));
platform_function.set_scope_id(symbol_table.current_scope_id());
symbol_table.pop_scope();
maybe_function_symbol
}

View File

@ -46,7 +46,9 @@ pub fn analyze_names<
) -> Vec<DmDiagnostic> {
let mut diagnostics = vec![];
// gather symbols
// first pass
// generally, gather symbols from declarations
// don't go into functions
for compilation_unit in compilation_units.iter_mut() {
let file_name = files.name(compilation_unit.file_id()).unwrap();
na_p1_compilation_unit(file_name, compilation_unit, symbol_table, &mut diagnostics);
@ -56,7 +58,11 @@ pub fn analyze_names<
return diagnostics;
}
// resolve symbols
// resolve global imports
symbol_table.resolve_global_imports();
// second pass
// resolve referenced symbols and go into functions
for compilation_unit in compilation_units.iter_mut() {
na_p2_compilation_unit(compilation_unit, symbol_table, &mut diagnostics);
}
@ -318,12 +324,39 @@ mod tests {
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);
}
#[test]
fn with_std_core_source() {
let sources = HashMap::from([
(
"std/core/print.dm",
"
ns std::core
pub platform fn println(msg: Any) -> Void
pub platform fn print(msg: Any) -> Void
",
),
(
"main.dm",
"
fn main()
println('A line')
print('Not a line')
end
",
),
]);
let mut symbol_table = SymbolTable::new();
let global_star_use = symbol_table
.insert_star_use_symbol(StarUseSymbol::new(
&str_slice_to_rcs(&["std", "core"]),
None,
))
.expect("Failed to insert star use symbol.");
assert_no_diagnostics(sources, &mut symbol_table);
}

View File

@ -1,11 +1,12 @@
use crate::ast::node::{
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,
AnySpaceSuffixOperator, AssignmentStatement, BacktickString, BoundSuffixOperator, Call,
CompilationUnit, ConcreteUseStatement, ConcreteUseStatementSuffix, DString, Expression,
ExpressionList, ExpressionStatement, Function, FunctionAliasBody, FunctionBlockBody,
FunctionBody, FunctionEqualsBody, GenericParameters, Identifier, IdentifierExpression,
IdentifierOrFqn, LValue, LValueSuffix, Literal, ModuleLevelDeclaration,
NoNewlineSuffixOperator, ObjectIndex, Parameter, Parameters, PlatformFunction, PrimitiveType,
ReturnType, StarUseStatement, Statement, SuffixExpression, SuffixOperator, TypeUse, TypedArray,
UseStatement, UseStatementIdentifier, UseStatementPrefix, VariableDeclaration,
};
use crate::diagnostic::DmDiagnostic;
use crate::name_analysis::symbol::source_definition::SourceDefinition;
@ -153,7 +154,7 @@ fn na_p2_module_level_declaration(
na_p2_function(function, symbol_table, diagnostics);
}
ModuleLevelDeclaration::PlatformFunction(platform_function) => {
todo!()
na_p2_platform_function(platform_function, symbol_table, diagnostics);
}
}
}
@ -197,6 +198,44 @@ fn na_p2_function(
symbol_table.pop_scope();
}
fn na_p2_platform_function(
platform_function: &mut PlatformFunction,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
// change to this function's scope
symbol_table.set_current_scope(*platform_function.scope_id().unwrap());
// generics
na_p2_generic_parameters(platform_function.generics_mut(), symbol_table, diagnostics);
// parameters
let parameter_symbols = na_p2_parameters(
platform_function.parameters_mut(),
symbol_table,
diagnostics,
);
platform_function
.function_symbol_mut()
.unwrap()
.borrow_mut()
.set_parameter_symbols(parameter_symbols);
// return_type
let maybe_return_type_symbol = na_p2_return_type(
platform_function.return_type_mut(),
symbol_table,
diagnostics,
);
if let Some(return_type_symbol) = maybe_return_type_symbol {
platform_function
.function_symbol_mut()
.unwrap()
.borrow_mut()
.set_return_type(return_type_symbol);
}
}
fn na_p2_generic_parameters(
generic_parameters: &mut GenericParameters,
symbol_table: &mut SymbolTable,
@ -602,7 +641,7 @@ fn na_p2_expression(
na_p2_suffix_expression(suffix, symbol_table, diagnostics);
}
Expression::Literal(literal) => {
todo!()
na_p2_literal(literal, symbol_table, diagnostics);
}
Expression::Identifier(identifier_expression) => {
na_p2_identifier_expression(identifier_expression, symbol_table, diagnostics);
@ -638,20 +677,30 @@ fn na_p2_suffix_operator(
diagnostics: &mut Vec<DmDiagnostic>,
) {
match suffix_operator {
SuffixOperator::PlusPlus => {
// no-op
SuffixOperator::BoundSuffixOperator(bound_suffix) => {
match bound_suffix {
BoundSuffixOperator::PlusPlus => {
// no-op
}
BoundSuffixOperator::MinusMinus => {
// 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);
SuffixOperator::NoNewlineSuffixOperator(no_newline_suffix) => match no_newline_suffix {
NoNewlineSuffixOperator::ObjectIndex(object_index) => {
na_p2_object_index(object_index, symbol_table, diagnostics);
}
NoNewlineSuffixOperator::Call(call) => {
na_p2_call(call, symbol_table, diagnostics);
}
},
SuffixOperator::AnySpaceSuffixOperator(any_space_suffix) => {
match any_space_suffix {
AnySpaceSuffixOperator::ObjectProperty(_) => {
// no-op; this is checked during type checking
}
}
}
}
}
@ -726,3 +775,49 @@ fn na_p2_expression_list(
na_p2_expression(expression, symbol_table, diagnostics);
}
}
fn na_p2_literal(
literal: &mut Literal,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
match literal {
Literal::DString(d_string) => {
na_p2_d_string(d_string, symbol_table, diagnostics);
}
Literal::BacktickString(backtick_string) => {
na_p2_backtick_string(backtick_string, symbol_table, diagnostics);
}
_ => {
// no-op, nothing to check.
}
}
}
fn na_p2_d_string(
d_string: &mut DString,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
for d_string_expression in d_string.expressions_mut() {
na_p2_expression(
d_string_expression.expression_mut(),
symbol_table,
diagnostics,
);
}
}
fn na_p2_backtick_string(
backtick_string: &mut BacktickString,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
for d_string_expression in backtick_string.expressions_mut() {
na_p2_expression(
d_string_expression.expression_mut(),
symbol_table,
diagnostics,
);
}
}

View File

@ -10,6 +10,7 @@ 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;
use crate::name_analysis::util::join_fqn_parts;
use scope::Scope;
use std::cell::RefCell;
use std::fmt::Display;
@ -132,6 +133,45 @@ impl SymbolTable {
as_rc
}
pub fn resolve_global_imports(&mut self) {
let global_scope = &self.scopes[0];
for concrete_use_symbol in global_scope.concrete_use_symbols() {
match self
.symbol_tree
.find_usable_symbol(concrete_use_symbol.borrow().fqn_parts())
{
Ok(usable_symbol) => {
concrete_use_symbol.borrow_mut().set_resolved_symbol(usable_symbol);
}
Err(symbol_lookup_error) => {
// panic because this is definitely a compiler/configuration error
panic!(
"{:?}: Cannot resolve global import {}",
symbol_lookup_error,
join_fqn_parts(concrete_use_symbol.borrow().fqn_parts())
);
}
}
}
for star_use_symbol in global_scope.star_use_symbols() {
let mut star_use_symbol_ref_mut = star_use_symbol.borrow_mut();
match self.symbol_tree.find_all_by_base_fqn(star_use_symbol_ref_mut.fqn_parts()) {
Ok(usable_symbols) => {
star_use_symbol_ref_mut.set_resolved_symbols(usable_symbols);
}
Err(symbol_lookup_error) => {
// again, panic because this is a compiler/configuration error
panic!(
"{:?}: Cannot resolve global star import {}::*",
symbol_lookup_error,
join_fqn_parts(star_use_symbol.borrow().fqn_parts())
);
}
}
}
}
pub fn find_usable_symbols_by_base_fqn(
&self,
fqn_parts: &[Rc<str>],

View File

@ -70,6 +70,14 @@ impl Scope {
self.id
}
pub fn concrete_use_symbols(&self) -> impl Iterator<Item = &Rc<RefCell<ConcreteUseSymbol>>> {
self.concrete_use_symbols.values()
}
pub fn star_use_symbols(&self) -> impl Iterator<Item = &Rc<RefCell<StarUseSymbol>>> {
self.star_use_symbols.values()
}
pub fn insert_concrete_use_symbol(
&mut self,
symbol: ConcreteUseSymbol,

View File

@ -81,8 +81,10 @@ impl SymbolTree {
panic!("Unable to register module fqn with no parts.")
}
if fqn_parts.len() == 1 {
self.children
.insert(fqn_parts[0].clone(), SymbolTree::new(fqn_parts[0].as_ref()));
if !self.children.contains_key(&fqn_parts[0]) {
self.children
.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();
@ -154,12 +156,44 @@ impl SymbolTree {
}
}
}
pub fn find_usable_symbol(
&self,
fqn_parts: &[Rc<str>],
) -> Result<UsableSymbol, SymbolLookupError> {
match fqn_parts.len() {
0 => panic!("Cannot resolve a usable symbol with no fqn part."),
1 => self
.find_interface(fqn_parts)
.map(|interface_symbol| UsableSymbol::Interface(interface_symbol.clone()))
.or_else(|| {
self.find_class(fqn_parts)
.map(|class_symbol| UsableSymbol::Class(class_symbol.clone()))
})
.or_else(|| {
self.find_function(fqn_parts)
.map(|function_symbol| UsableSymbol::Function(function_symbol))
})
.map_or_else(
|| Err(SymbolLookupError::NoDefinition),
|usable_symbol| Ok(usable_symbol),
),
_ => {
if self.children.contains_key(fqn_parts[0].as_ref()) {
let child = self.children.get(fqn_parts[0].as_ref()).unwrap();
child.find_usable_symbol(&fqn_parts[1..])
} else {
Err(SymbolLookupError::NoSuchNamespace)
}
}
}
}
}
enum FormatAction<'a> {
PrintSymbolTree(&'a SymbolTree),
IncreaseIndent,
DecreaseIndent
DecreaseIndent,
}
impl Display for SymbolTree {
@ -181,7 +215,10 @@ impl Display for SymbolTree {
// increase for the first child
stack.push(FormatAction::IncreaseIndent);
indent_writer.writeln_indented(&format!("SymbolTree(debug_name = {})", symbol_tree.debug_name))?;
indent_writer.writeln_indented(&format!(
"SymbolTree(debug_name = {})",
symbol_tree.debug_name
))?;
indent_writer.increase_indent();
for interface in symbol_tree.interfaces.values() {

View File

@ -503,6 +503,12 @@ PlatformFunction:
- identifier
- parameters
- return_type
fields:
- function_symbol:
kind: FunctionSymbol
wrap: rc_ref_cell
- scope_id:
kind: usize
PlatformOperatorFunction:
struct:
children:
@ -1154,13 +1160,23 @@ SuffixExpression:
SuffixOperator:
tree_enum:
rules:
- PlusPlus:
child: false
- MinusMinus:
child: false
- ObjectProperty
- BoundSuffixOperator
- NoNewlineSuffixOperator
- AnySpaceSuffixOperator
BoundSuffixOperator:
leaf_enum:
rules:
- PlusPlus
- MinusMinus
NoNewlineSuffixOperator:
tree_enum:
rules:
- ObjectIndex
- Call
AnySpaceSuffixOperator:
tree_enum:
rules:
- ObjectProperty
ObjectProperty:
struct:
children:

View File

@ -188,8 +188,8 @@ FullyQualifiedName = {
}
IdentifierOrFqn = {
Identifier
| FullyQualifiedName
FullyQualifiedName
| Identifier
}
// Common lists
@ -260,7 +260,7 @@ FunctionTypeUse = {
// Generic Arguments
GenericArguments = {
GenericArguments = !{
"<"
~ TypeUseList
~ ">"
@ -735,7 +735,8 @@ MultiplicativeOperator = {
| Modulo
}
PrefixExpression = {
// Compound-atomic because all prefix operators are bound immediately (i.e., no spaces).
PrefixExpression = ${
PrefixOperator*
~ SuffixExpression
}
@ -746,19 +747,31 @@ PrefixOperator = {
| Negative
}
SuffixExpression = {
SuffixExpression = ${
PrimaryExpression
~ SuffixOperator*
}
SuffixOperator = {
SuffixOperator = ${
BoundSuffixOperator
| ( " " | "\t" )* ~ NoNewlineSuffixOperator ~ ( " " | "\t" )*
| WHITESPACE* ~ AnySpaceSuffixOperator ~ WHITESPACE*
}
BoundSuffixOperator = !{
PlusPlus
| MinusMinus
| ObjectProperty
| ObjectIndex
}
NoNewlineSuffixOperator = !{
ObjectIndex
| Call
}
AnySpaceSuffixOperator = !{
ObjectProperty
}
ObjectProperty = {
"."
~ Identifier
@ -770,10 +783,10 @@ ObjectIndex = {
~ "]"
}
PrimaryExpression = {
PrimaryExpression = !{
Literal
| IdentifierExpression
| FullyQualifiedName
| IdentifierExpression
| Closure
| ListExpression
| ParenthesizedExpression
@ -810,28 +823,29 @@ ParenthesesCall = {
~ Closure?
}
NonParenthesesCall = {
NonParenthesesCall = ${
TurboFish?
~ ( " " | "\t" )*
~ (
Closure
| ExpressionList
| ExpressionList ~ Closure
| ExpressionList ~ ( " " | "\t" )* ~ Closure
)
}
TurboFish = {
TurboFish = ${
"::"
~ GenericArguments
}
ExpressionList = {
ExpressionList = !{
Expression
~ ( "," ~ Expression )*
}
// Closure
Closure = {
Closure = !{
"{"
~ ( ClosureParameters? ~ "->" )?
~ Statement*