deimos-lang/src/name_analysis/second_pass.rs

824 lines
27 KiB
Rust

use crate::ast::node::{
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;
use crate::name_analysis::symbol::variable_symbol::VariableSymbol;
use crate::name_analysis::symbol::{
GenericTypeSymbol, ParameterSymbol, PrimitiveTypeSymbol, TypeSymbol,
};
use crate::name_analysis::symbol_table::{SymbolLookupError, SymbolTable};
use crate::name_analysis::util::{
format_fqn, handle_insert_error, handle_lookup_error, join_fqn_parts,
};
use std::cell::RefCell;
use std::rc::Rc;
pub fn na_p2_compilation_unit(
compilation_unit: &mut CompilationUnit,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
for use_statement in compilation_unit.use_statements_mut() {
na_p2_use_statement(use_statement, symbol_table, diagnostics);
}
for declaration in compilation_unit.module_level_declarations_mut() {
na_p2_module_level_declaration(declaration, symbol_table, diagnostics);
}
}
fn na_p2_use_statement(
use_statement: &mut UseStatement,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
match use_statement {
UseStatement::ConcreteUseStatement(concrete_use_statement) => {
na_p2_concrete_use_statement(concrete_use_statement, symbol_table, diagnostics);
}
UseStatement::StarUseStatement(star_use_statement) => {
na_p2_star_use_statement(star_use_statement, symbol_table, diagnostics);
}
}
}
fn na_p2_concrete_use_statement(
concrete_use_statement: &mut ConcreteUseStatement,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
let base_fqn_parts = concrete_use_statement
.prefixes()
.map(UseStatementPrefix::identifier)
.map(Identifier::name)
.map(Rc::from)
.collect::<Vec<Rc<str>>>();
match concrete_use_statement.suffix_mut() {
ConcreteUseStatementSuffix::UseStatementIdentifier(use_statement_identifier) => {
handle_concrete_use_statement_identifier(
&base_fqn_parts,
use_statement_identifier,
symbol_table,
diagnostics,
);
}
ConcreteUseStatementSuffix::UseList(use_list) => {
for use_statement_identifier in use_list.identifiers_mut() {
handle_concrete_use_statement_identifier(
&base_fqn_parts,
use_statement_identifier,
symbol_table,
diagnostics,
);
}
}
}
}
fn handle_concrete_use_statement_identifier(
base_fqn_parts: &[Rc<str>],
use_statement_identifier: &mut UseStatementIdentifier,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
let fqn_parts = {
let mut all_parts = base_fqn_parts.to_vec();
all_parts.push(Rc::from(use_statement_identifier.identifier().name()));
all_parts
};
let maybe_usable_symbol = symbol_table.find_usable_symbol(&fqn_parts);
if let Some(usable_symbol) = maybe_usable_symbol {
use_statement_identifier
.symbol_mut()
.expect("Should have set the symbol on the use statement already.")
.borrow_mut()
.set_resolved_symbol(usable_symbol);
} else {
handle_lookup_error(
SymbolLookupError::NoDefinition,
&join_fqn_parts(&fqn_parts),
use_statement_identifier.identifier().file_id(),
use_statement_identifier.identifier().range(),
diagnostics,
);
}
}
fn na_p2_star_use_statement(
star_use_statement: &mut StarUseStatement,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
let mut symbol_ref_mut = star_use_statement.symbol().unwrap().borrow_mut();
match symbol_table.find_usable_symbols_by_base_fqn(symbol_ref_mut.fqn_parts()) {
Ok(usable_symbols) => {
symbol_ref_mut.set_resolved_symbols(usable_symbols);
}
Err(symbol_lookup_error) => {
handle_lookup_error(
symbol_lookup_error,
&join_fqn_parts(symbol_ref_mut.fqn_parts()),
star_use_statement.file_id(),
star_use_statement.range(),
diagnostics,
);
}
}
}
fn na_p2_module_level_declaration(
module_level_declaration: &mut ModuleLevelDeclaration,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
match module_level_declaration {
ModuleLevelDeclaration::Module(module_declaration) => {
todo!()
}
ModuleLevelDeclaration::Interface(interface) => {
todo!()
}
ModuleLevelDeclaration::Class(class) => {
todo!()
}
ModuleLevelDeclaration::Function(function) => {
na_p2_function(function, symbol_table, diagnostics);
}
ModuleLevelDeclaration::PlatformFunction(platform_function) => {
na_p2_platform_function(platform_function, symbol_table, diagnostics);
}
}
}
fn na_p2_function(
function: &mut Function,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
// change to this function's scope
symbol_table.set_current_scope(*function.scope_id().unwrap());
// generics
na_p2_generic_parameters(function.generics_mut(), symbol_table, diagnostics);
// parameters
let parameter_symbols = na_p2_parameters(function.parameters_mut(), symbol_table, diagnostics);
function
.function_symbol()
.unwrap()
.borrow_mut()
.set_parameter_symbols(parameter_symbols);
// return type
let maybe_return_type_symbol =
na_p2_return_type(function.return_type_mut(), symbol_table, diagnostics);
if let Some(return_type_symbol) = maybe_return_type_symbol {
function
.function_symbol()
.unwrap()
.borrow_mut()
.set_return_type(return_type_symbol);
}
// push a scope for the body and check the body
symbol_table.push_scope(&format!(
"FunctionBodyScope {}",
function.identifier().name()
));
na_p2_function_body(function.function_body_mut(), symbol_table, diagnostics);
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,
diagnostics: &mut Vec<DmDiagnostic>,
) {
for identifier in generic_parameters.identifier_list().identifiers() {
let generic_type_symbol = GenericTypeSymbol::new(
identifier.name(),
Some(SourceDefinition::from_identifier(identifier)),
);
match symbol_table.insert_generic_type_symbol(generic_type_symbol) {
Ok(_) => {}
Err(symbol_insert_error) => {
handle_insert_error(
symbol_insert_error,
identifier.name(),
identifier.file_id(),
identifier.range(),
diagnostics,
);
}
}
}
}
fn na_p2_parameters(
parameters: &mut Parameters,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) -> Vec<Rc<RefCell<ParameterSymbol>>> {
parameters
.parameters_mut()
.map(|parameter| na_p2_parameter(parameter, symbol_table, diagnostics))
.filter(Option::is_some)
.map(Option::unwrap)
.collect()
}
fn na_p2_parameter(
parameter: &mut Parameter,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) -> Option<Rc<RefCell<ParameterSymbol>>> {
let parameter_type_symbol = na_p2_type_use(parameter.type_use_mut(), symbol_table, diagnostics);
let to_insert = ParameterSymbol::new(
parameter.identifier().name(),
Some(SourceDefinition::from_identifier(parameter.identifier())),
parameter_type_symbol,
);
match symbol_table.insert_parameter_symbol(to_insert) {
Ok(parameter_symbol) => Some(parameter_symbol),
Err(symbol_insert_error) => {
handle_insert_error(
symbol_insert_error,
parameter.identifier().name(),
parameter.identifier().file_id(),
parameter.identifier().range(),
diagnostics,
);
None
}
}
}
fn na_p2_return_type(
return_type: &mut ReturnType,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) -> Option<TypeSymbol> {
na_p2_type_use(return_type.type_use_mut(), symbol_table, diagnostics)
}
fn na_p2_type_use(
type_use: &mut TypeUse,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) -> Option<TypeSymbol> {
match type_use {
TypeUse::PrimitiveType(primitive_type) => {
Some(TypeSymbol::Primitive(match primitive_type {
PrimitiveType::Byte => PrimitiveTypeSymbol::Byte,
PrimitiveType::Short => PrimitiveTypeSymbol::Short,
PrimitiveType::Char => PrimitiveTypeSymbol::Char,
PrimitiveType::Int => PrimitiveTypeSymbol::Int,
PrimitiveType::Long => PrimitiveTypeSymbol::Long,
PrimitiveType::Double => PrimitiveTypeSymbol::Double,
PrimitiveType::Bool => PrimitiveTypeSymbol::Boolean,
PrimitiveType::String => PrimitiveTypeSymbol::String,
PrimitiveType::TypedArray(typed_array) => {
na_p2_typed_array(typed_array, symbol_table, diagnostics)
}
PrimitiveType::Any => PrimitiveTypeSymbol::Any,
PrimitiveType::Void => PrimitiveTypeSymbol::Void,
}))
}
TypeUse::InterfaceOrClassTypeUse(interface_or_class_type) => {
match interface_or_class_type.identifier_or_fqn() {
IdentifierOrFqn::Identifier(identifier) => {
match symbol_table.lookup_type(identifier.name()) {
Ok(type_symbol) => {
interface_or_class_type.set_type_symbol(type_symbol.clone());
Some(type_symbol)
}
Err(symbol_lookup_error) => {
handle_lookup_error(
symbol_lookup_error,
identifier.name(),
identifier.file_id(),
identifier.range(),
diagnostics,
);
None
}
}
}
IdentifierOrFqn::FullyQualifiedName(fqn) => {
let fqn_parts = fqn
.identifiers()
.map(Identifier::name)
.collect::<Vec<&str>>();
match symbol_table.lookup_type_by_fqn(&fqn_parts) {
Ok(type_symbol) => {
interface_or_class_type.set_type_symbol(type_symbol.clone());
Some(type_symbol)
}
Err(symbol_lookup_error) => {
handle_lookup_error(
symbol_lookup_error,
&format_fqn(&fqn_parts),
fqn.file_id(),
fqn.range(),
diagnostics,
);
None
}
}
}
}
}
TypeUse::TupleTypeUse(tuple_type) => {
todo!()
}
TypeUse::FunctionTypeUse(function_type) => {
todo!()
}
}
}
fn na_p2_typed_array(
typed_array: &mut TypedArray,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) -> PrimitiveTypeSymbol {
let inner_type_use = typed_array
.generic_arguments_mut()
.type_use_list_mut()
.type_uses_mut()
.next()
.unwrap();
let inner_type_symbol = na_p2_type_use(inner_type_use, symbol_table, diagnostics);
PrimitiveTypeSymbol::TypedArray {
inner_type: inner_type_symbol.map(Box::from),
}
}
/// **Note:** caller needs to push a scope before calling.
fn na_p2_function_body(
function_body: &mut FunctionBody,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
match function_body {
FunctionBody::FunctionAliasBody(alias_body) => {
na_p2_function_alias_body(alias_body, symbol_table, diagnostics);
}
FunctionBody::FunctionEqualsBody(equals_body) => {
na_p2_function_equals_body(equals_body, symbol_table, diagnostics);
}
FunctionBody::FunctionBlockBody(block_body) => {
na_p2_function_block_body(block_body, symbol_table, diagnostics);
}
}
}
fn na_p2_function_alias_body(
function_alias_body: &mut FunctionAliasBody,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
let maybe_function_symbol =
symbol_table.lookup_function_symbol(function_alias_body.identifier().name());
match maybe_function_symbol {
Ok(function_symbol) => {
function_alias_body.set_resolved_function_symbol(function_symbol);
}
Err(symbol_lookup_error) => {
handle_lookup_error(
symbol_lookup_error,
function_alias_body.identifier().name(),
function_alias_body.identifier().file_id(),
function_alias_body.identifier().range(),
diagnostics,
);
}
}
}
fn na_p2_function_equals_body(
function_equals_body: &mut FunctionEqualsBody,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
na_p2_expression(
function_equals_body.expression_mut(),
symbol_table,
diagnostics,
);
}
fn na_p2_function_block_body(
function_block_body: &mut FunctionBlockBody,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
for statement in function_block_body.statements_mut() {
na_p2_statement(statement, symbol_table, diagnostics);
}
}
fn na_p2_statement(
statement: &mut Statement,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
match statement {
Statement::VariableDeclaration(variable_declaration) => {
na_p2_variable_declaration(variable_declaration, symbol_table, diagnostics);
}
Statement::AssignmentStatement(assignment_statement) => {
na_p2_assignment_statement(assignment_statement, symbol_table, diagnostics);
}
Statement::ExpressionStatement(expression_statement) => {
na_p2_expression_statement(expression_statement, symbol_table, diagnostics);
}
Statement::UseStatement(use_statement) => {
todo!()
}
Statement::IfStatement(if_statement) => {
todo!()
}
Statement::WhileStatement(while_statement) => {
todo!()
}
Statement::ForStatement(for_statement) => {
todo!()
}
}
}
fn na_p2_variable_declaration(
variable_declaration: &mut VariableDeclaration,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
// handle variable itself
let to_insert = VariableSymbol::new(
variable_declaration.identifier().name(),
variable_declaration.is_mut(),
Some(SourceDefinition::from_identifier(
variable_declaration.identifier(),
)),
);
match symbol_table.insert_variable_symbol(to_insert) {
Ok(variable_symbol) => {
variable_declaration.set_variable_symbol(variable_symbol);
}
Err(symbol_insert_error) => {
handle_insert_error(
symbol_insert_error,
variable_declaration.identifier().name(),
variable_declaration.identifier().file_id(),
variable_declaration.identifier().range(),
diagnostics,
);
}
}
// type-use
if let Some(type_use) = variable_declaration.type_use_mut() {
na_p2_type_use(type_use, symbol_table, diagnostics);
}
// initializer
if let Some(expression) = variable_declaration.expression_mut() {
na_p2_expression(expression, symbol_table, diagnostics);
}
}
fn na_p2_assignment_statement(
assignment_statement: &mut AssignmentStatement,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
na_p2_l_value(
assignment_statement.l_value_mut(),
symbol_table,
diagnostics,
);
na_p2_expression(
assignment_statement.expression_mut(),
symbol_table,
diagnostics,
);
}
fn na_p2_l_value(
l_value: &mut LValue,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
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)
// which we do need to recurse into.
for l_value_suffix in l_value.suffixes_mut() {
na_p2_l_value_suffix(l_value_suffix, symbol_table, diagnostics);
}
}
fn na_p2_l_value_suffix(
l_value_suffix: &mut LValueSuffix,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
match l_value_suffix {
LValueSuffix::ObjectProperty(_) => {
// no-op; properties are checked during type-checking for soundness
}
LValueSuffix::ObjectIndex(object_index) => {
// check inner expression
na_p2_expression(object_index.expression_mut(), symbol_table, diagnostics);
}
}
}
fn na_p2_expression_statement(
expression_statement: &mut ExpressionStatement,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
na_p2_expression(
expression_statement.expression_mut(),
symbol_table,
diagnostics,
);
}
fn na_p2_expression(
expression: &mut Expression,
symbol_table: &mut SymbolTable,
diagnostics: &mut Vec<DmDiagnostic>,
) {
match expression {
Expression::Ternary(ternary) => {
todo!()
}
Expression::Or(or) => {
todo!()
}
Expression::And(and) => {
todo!()
}
Expression::Comparison(comparison) => {
todo!()
}
Expression::Shift(shift) => {
todo!()
}
Expression::Additive(additive) => {
todo!()
}
Expression::Multiplicative(multiplicative) => {
todo!()
}
Expression::Prefix(prefix) => {
todo!()
}
Expression::Suffix(suffix) => {
na_p2_suffix_expression(suffix, symbol_table, diagnostics);
}
Expression::Literal(literal) => {
na_p2_literal(literal, symbol_table, diagnostics);
}
Expression::Identifier(identifier_expression) => {
na_p2_identifier_expression(identifier_expression, symbol_table, diagnostics);
}
Expression::Fqn(fqn) => {
todo!()
}
Expression::Closure(closure) => {
todo!()
}
Expression::List(list) => {
todo!()
}
}
}
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::BoundSuffixOperator(bound_suffix) => {
match bound_suffix {
BoundSuffixOperator::PlusPlus => {
// no-op
}
BoundSuffixOperator::MinusMinus => {
// no-op
}
}
}
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
}
}
}
}
}
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);
}
}
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,
);
}
}