diff --git a/dm/src/repl.rs b/dm/src/repl.rs index c0d75b3..33da1fe 100644 --- a/dm/src/repl.rs +++ b/dm/src/repl.rs @@ -4,11 +4,14 @@ use codespan_reporting::term::{Config, WriteStyle, emit_to_write_style}; use dmc_lib::SyntheticFunctionSession; use dmc_lib::constants_table::ConstantsTable; use dmc_lib::diagnostic::Diagnostics; +use dmc_lib::intrinsics::add_primitive_symbols_and_type_infos; use dmc_lib::parser::parse_statement; use dvm_lib::vm::constant::{Constant, StringConstant}; use dvm_lib::vm::function::Function; use dvm_lib::vm::operand::Operand; +use dvm_lib::vm::value::Value; use dvm_lib::vm::{CallStack, DvmContext, loop_instructions, prepare_for_instruction_loop}; +use std::error::Error; use std::io::{BufRead, Write}; pub fn repl( @@ -26,13 +29,26 @@ pub fn repl( let mut session = SyntheticFunctionSession::new("__repl"); let analysis_context = session.ctx_mut(); - analysis_context.push_scope("__repl_root_scope"); + let root_scope_id = analysis_context.push_scope("__repl_root_scope"); analysis_context.push_scope("__repl_function_scope"); analysis_context.push_scope("__repl_body_scope"); + add_primitive_symbols_and_type_infos(analysis_context, root_scope_id); + let mut constants_table = ConstantsTable::new(); let mut dvm_context = DvmContext::new(); + + // todo: move this to a core impl crate + fn core_string_len(args: &[Value]) -> Result> { + let this = args[0].unwrap_string(); + let len = this.len(); + Ok(Value::Int(len as i32)) + } + dvm_context + .platform_functions_mut() + .insert("core::String::len".into(), core_string_len); + let mut repl_fn_stack_locals: Vec = Vec::new(); 'repl: loop { diff --git a/dmc-lib/src/intrinsics.rs b/dmc-lib/src/intrinsics.rs new file mode 100644 index 0000000..c492a85 --- /dev/null +++ b/dmc-lib/src/intrinsics.rs @@ -0,0 +1,51 @@ +use crate::semantic_analysis::analysis_context::AnalysisContext; +use crate::semantic_analysis::scope::ScopeId; +use crate::semantic_analysis::symbol::{ClassSymbol, FunctionSymbol, Symbol}; +use crate::semantic_analysis::type_info::{FunctionTypeInfo, InstanceTypeInfo, TypeInfo}; +use std::collections::HashMap; + +pub fn add_primitive_symbols_and_type_infos(ctx: &mut AnalysisContext, global_scope_id: ScopeId) { + let string_len_symbol = Symbol::Function(FunctionSymbol::new( + "len".into(), + None, + "core::String::len".into(), + true, + true, + )); + let string_len_symbol_id = ctx + .try_insert_symbol_in_scope(string_len_symbol, global_scope_id) + .unwrap(); + + let mut string_class_methods = HashMap::new(); + string_class_methods.insert("len".into(), string_len_symbol_id); + + let string_class_symbol = Symbol::Class(ClassSymbol::new( + "String".into(), + None, + "core::String".into(), + true, + HashMap::new(), + string_class_methods, + )); + let string_class_symbol_id = ctx + .try_insert_symbol_in_scope(string_class_symbol, global_scope_id) + .unwrap(); + + let string_instance_type_info_id = ctx.insert_type_info(TypeInfo::Instance( + InstanceTypeInfo::new(string_class_symbol_id), + )); + + ctx.associate_symbol_to_type(string_class_symbol_id, string_instance_type_info_id); + + let string_len_return_type_info_id = ctx.insert_type_info(TypeInfo::Int); + + let string_len_type_info = TypeInfo::Function(FunctionTypeInfo::new( + vec![], + string_len_return_type_info_id, + Some(string_instance_type_info_id), + )); + + let string_len_type_info_id = ctx.insert_type_info(string_len_type_info); + + ctx.associate_symbol_to_type(string_len_symbol_id, string_len_type_info_id); +} diff --git a/dmc-lib/src/lib.rs b/dmc-lib/src/lib.rs index 7826411..e0acdea 100644 --- a/dmc-lib/src/lib.rs +++ b/dmc-lib/src/lib.rs @@ -20,6 +20,7 @@ pub mod constants_table; pub mod diagnostic; mod diagnostic_factories; mod error_codes; +pub mod intrinsics; pub mod ir; pub mod lexer; pub mod lowering; diff --git a/dmc-lib/src/lowering/mod.rs b/dmc-lib/src/lowering/mod.rs index 624409d..05ed512 100644 --- a/dmc-lib/src/lowering/mod.rs +++ b/dmc-lib/src/lowering/mod.rs @@ -571,11 +571,29 @@ fn lower_to_ir_call( let callee_symbol = &ctx.symbols()[callee_symbol_id]; match callee_symbol { Symbol::Function(function_symbol) => { - let arguments = call - .arguments() - .iter() - .map(|e| lower_expression_to_ir_expression(e, ctx, fn_ctx)) - .collect(); + let mut arguments = Vec::new(); + if function_symbol.is_method() { + let this = match call.callee() { + Expression::Path(path) => { + lower_expression_to_ir_expression(path.base(), ctx, fn_ctx) + } + Expression::Identifier(identifier) => { + // A self object, like: + // class Foo + // fn bar() "hello" end + // fn greet() bar() end + // end + todo!() + } + _ => panic!( + "Cannot determine method's this expression when not Path or Identifier" + ), + }; + arguments.push(this); + } + for expression in call.arguments() { + arguments.push(lower_expression_to_ir_expression(expression, ctx, fn_ctx)); + } IrCall::new( function_symbol.fqn_owned(), arguments, diff --git a/dmc-lib/src/lowering/util.rs b/dmc-lib/src/lowering/util.rs index 00ac153..37b1ff8 100644 --- a/dmc-lib/src/lowering/util.rs +++ b/dmc-lib/src/lowering/util.rs @@ -8,7 +8,7 @@ pub fn to_ir_type_info(sa_type_info: &TypeInfo) -> IrTypeInfo { TypeInfo::Double => IrTypeInfo::Double, TypeInfo::Void => IrTypeInfo::Void, _ => { - panic!("BUG! Unknown sa_type_info: {}", sa_type_info); + panic!("BUG! Unknown sa_type_info: {:?}", sa_type_info); } } } diff --git a/dmc-lib/src/semantic_analysis/analysis_context/helpers.rs b/dmc-lib/src/semantic_analysis/analysis_context/helpers.rs new file mode 100644 index 0000000..91c7813 --- /dev/null +++ b/dmc-lib/src/semantic_analysis/analysis_context/helpers.rs @@ -0,0 +1,21 @@ +use crate::semantic_analysis::analysis_context::AnalysisContext; +use crate::semantic_analysis::type_info::TypeInfo; + +pub fn get_name_for_type<'ctx>(type_info: &TypeInfo, ctx: &'ctx AnalysisContext) -> &'ctx str { + match type_info { + TypeInfo::Any => "Any", + TypeInfo::Function(_function_type_info) => { + todo!() + } + TypeInfo::Instance(instance_type_info) => { + let class_symbol_id = instance_type_info.class_symbol_id(); + let class_symbol = &ctx.symbols()[class_symbol_id]; + class_symbol.declared_name() + } + TypeInfo::String => "String", + TypeInfo::Int => "Int", + TypeInfo::Double => "Double", + TypeInfo::Void => "Void", + TypeInfo::__Error => panic!("TypeInfo::__Error should never be shown to the user."), + } +} diff --git a/dmc-lib/src/semantic_analysis/analysis_context.rs b/dmc-lib/src/semantic_analysis/analysis_context/mod.rs similarity index 85% rename from dmc-lib/src/semantic_analysis/analysis_context.rs rename to dmc-lib/src/semantic_analysis/analysis_context/mod.rs index 5b8712c..5c25ae2 100644 --- a/dmc-lib/src/semantic_analysis/analysis_context.rs +++ b/dmc-lib/src/semantic_analysis/analysis_context/mod.rs @@ -1,3 +1,5 @@ +pub mod helpers; + use crate::ast::NodeId; use crate::diagnostic::Diagnostic; use crate::diagnostic_factories::symbol_not_found; @@ -44,14 +46,6 @@ impl AnalysisContext { todo!() } - pub fn scopes(&self) -> &[Scope] { - &self.scopes - } - - pub fn scopes_mut(&mut self) -> &mut Vec { - &mut self.scopes - } - pub fn symbols(&self) -> &[Symbol] { &self.symbols } @@ -86,10 +80,11 @@ impl AnalysisContext { fqn.join("::") } - pub fn push_scope(&mut self, _debug_name: &str) { + 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 { @@ -125,6 +120,11 @@ impl AnalysisContext { ); } + /// Inserts the given symbol into the Scope corresponding to the given ScopeId. **This is a low + /// level operation and alternate methods should be preferred.** + /// + /// Danger: If a symbol already exists in the corresponding Scope with the same declared name, + /// it will be overwritten, leading to dangling symbols/symbol ids. fn insert_symbol_in_scope(&mut self, scope_id: ScopeId, symbol: Symbol) -> SymbolId { // get declared name let declared_name = symbol.declared_name_owned(); @@ -159,14 +159,14 @@ impl AnalysisContext { &mut self, to_insert: Symbol, scope_id: ScopeId, - ) -> Result<(), Diagnostic> { + ) -> Result { let scope = self.get_scope(scope_id); if let Some(already_declared) = self.get_symbol_in_scope(scope, to_insert.declared_name()) { Err(symbol_already_declared(already_declared, &to_insert)) } else { - self.insert_symbol_in_scope(scope_id, to_insert); + let symbol_id = self.insert_symbol_in_scope(scope_id, to_insert); // no associated node, so no need to update self.nodes_to_symbols - Ok(()) + Ok(symbol_id) } } @@ -261,12 +261,16 @@ impl AnalysisContext { .insert(node_id, self.type_infos.len() - 1); } + 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); + } + pub fn associate_node_and_symbol_to_type(&mut self, node_id: NodeId, type_info_id: TypeInfoId) { let symbol_id = self .nodes_to_symbols .get(&node_id) .expect(&format!("node_id {} has no associated symbol", node_id)); - self.symbols_to_type_infos.insert(*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); } @@ -305,4 +309,25 @@ impl AnalysisContext { )); *symbol_type_info_id } + + pub fn find_symbol_by_fqn(&self, fqn: &str) -> Option { + // 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. + for (id, symbol) in self.symbols.iter().enumerate() { + match symbol { + Symbol::Class(class_symbol) => { + if class_symbol.fqn() == fqn { + return Some(id); + } + } + Symbol::Function(function_symbol) => { + if function_symbol.fqn() == fqn { + return Some(id); + } + } + _ => {} + } + } + None + } } diff --git a/dmc-lib/src/semantic_analysis/collect_symbols.rs b/dmc-lib/src/semantic_analysis/collect_symbols.rs index 2aa3cb9..cde4c54 100644 --- a/dmc-lib/src/semantic_analysis/collect_symbols.rs +++ b/dmc-lib/src/semantic_analysis/collect_symbols.rs @@ -38,6 +38,7 @@ fn collect_symbols_function( Some(function.declared_name_source_range()), fqn, false, + false, )); // insert @@ -70,6 +71,7 @@ fn collect_symbols_extern_function( Some(extern_function.declared_name_source_range()), fqn, true, + false, )); // insert function symbol diff --git a/dmc-lib/src/semantic_analysis/collect_types.rs b/dmc-lib/src/semantic_analysis/collect_types.rs index 3e798a8..113d242 100644 --- a/dmc-lib/src/semantic_analysis/collect_types.rs +++ b/dmc-lib/src/semantic_analysis/collect_types.rs @@ -28,6 +28,7 @@ fn collect_types_function(function: &Function, ctx: &mut AnalysisContext) { let function_type_info = TypeInfo::Function(FunctionTypeInfo::new( parameter_type_info_ids, return_type_info_id, + None, )); let function_type_info_id = ctx.insert_type_info(function_type_info); @@ -72,6 +73,7 @@ fn collect_types_extern_function(extern_function: &ExternFunction, ctx: &mut Ana let function_type_info = TypeInfo::Function(FunctionTypeInfo::new( parameter_type_info_ids, return_type_info_id, + None, )); let function_type_info_id = ctx.insert_type_info(function_type_info); diff --git a/dmc-lib/src/semantic_analysis/resolve_types/mod.rs b/dmc-lib/src/semantic_analysis/resolve_types/mod.rs index c8d3342..66df52e 100644 --- a/dmc-lib/src/semantic_analysis/resolve_types/mod.rs +++ b/dmc-lib/src/semantic_analysis/resolve_types/mod.rs @@ -15,10 +15,11 @@ use crate::ast::statement::Statement; use crate::diagnostic::{Diagnostic, Diagnostics}; use crate::error_codes::BINARY_INCOMPATIBLE_TYPES; use crate::semantic_analysis::analysis_context::AnalysisContext; +use crate::semantic_analysis::analysis_context::helpers::get_name_for_type; use crate::semantic_analysis::resolve_types::type_analysis::{ are_binary_op_compatible, binary_op_result, can_assign_right_to_left, can_negate, negate_result, }; -use crate::semantic_analysis::type_info::TypeInfo; +use crate::semantic_analysis::type_info::{InstanceTypeInfo, TypeInfo}; pub struct ResolveTypesResult(pub Diagnostics); @@ -110,10 +111,12 @@ fn resolve_types_assign_statement( let destination_type_info = ctx.get_type_info_for_node(assign_statement.destination().node_id()); - if !can_assign_right_to_left(destination_type_info, value_type_info) { + if !can_assign_right_to_left(destination_type_info, value_type_info, ctx) { + let destination_type_name = get_name_for_type(destination_type_info, ctx); + let value_type_name = get_name_for_type(value_type_info, ctx); let message = format!( "Incompatible types: cannot assign {} from {}", - destination_type_info, value_type_info + destination_type_name, value_type_name ); let diagnostic = Diagnostic::new( &message, @@ -152,7 +155,13 @@ fn resolve_types_expression( ctx.insert_type_info_for_node(TypeInfo::Double, double_literal.node_id()); } Expression::String(string_literal) => { - ctx.insert_type_info_for_node(TypeInfo::String, string_literal.node_id()); + let string_class_symbol_id = ctx + .find_symbol_by_fqn("core::String") + .expect("core::String is missing from ctx."); + ctx.insert_type_info_for_node( + TypeInfo::Instance(InstanceTypeInfo::new(string_class_symbol_id)), + string_literal.node_id(), + ); } } } @@ -185,9 +194,11 @@ fn resolve_types_binary_expression( BinaryOperation::BitwiseXor => "bitwise xor", BinaryOperation::BitwiseOr => "bitwise or", }; + let lhs_type_name = get_name_for_type(lhs_type_info, ctx); + let rhs_type_name = get_name_for_type(rhs_type_info, ctx); let message = format!( "Incompatible types: cannot {} {} and {}", - op_name, lhs_type_info, rhs_type_info + op_name, lhs_type_name, rhs_type_name ); let diagnostic = Diagnostic::new( &message, @@ -215,7 +226,8 @@ fn resolve_types_negative_expression( let result_type_info = negate_result(operand_type_info); ctx.insert_type_info_for_node(result_type_info, negative_expression.node_id()); } else { - let message = format!("Incompatible type: cannot negate {}", operand_type_info); + let operand_type_name = get_name_for_type(operand_type_info, ctx); + let message = format!("Incompatible type: cannot negate {}", operand_type_name); let diagnostic = Diagnostic::new( &message, negative_expression.source_range().start(), @@ -259,25 +271,26 @@ fn resolve_types_call(call: &Call, ctx: &mut AnalysisContext, diagnostics: &mut // of the function } - // check argument types + // check argument types, but only if we have the right number of arguments let parameter_type_ids = function_type_info.parameter_type_ids(); - for i in 0..parameter_type_ids.len() { - let argument_type_info = ctx.get_type_info_for_node(arguments[i].node_id()); - let parameter_type_info = ctx.get_type_info_by_id(parameter_type_ids[i]); // n.b. different method - if !can_assign_right_to_left(parameter_type_info, argument_type_info) { - let message = format!( - "Incompatible types: cannot assign {} to {}", - argument_type_info, parameter_type_info - ); - let diagnostic = Diagnostic::new( - &message, - arguments[i].source_range().start(), - arguments[i].source_range().end(), - ); - diagnostics.push(diagnostic); - - // do not push an error type because the result of the whole call is the return type - // of the function + if arguments.len() == parameter_type_ids.len() { + for i in 0..parameter_type_ids.len() { + let argument_type_info = ctx.get_type_info_for_node(arguments[i].node_id()); + let parameter_type_info = ctx.get_type_info_by_id(parameter_type_ids[i]); // n.b. different method + if !can_assign_right_to_left(parameter_type_info, argument_type_info, ctx) { + let parameter_type_name = get_name_for_type(parameter_type_info, ctx); + let argument_type_name = get_name_for_type(argument_type_info, ctx); + let message = format!( + "Incompatible types: cannot assign {} to {}", + argument_type_name, parameter_type_name + ); + let diagnostic = Diagnostic::new( + &message, + arguments[i].source_range().start(), + arguments[i].source_range().end(), + ); + diagnostics.push(diagnostic); + } } } @@ -290,7 +303,8 @@ fn resolve_types_call(call: &Call, ctx: &mut AnalysisContext, diagnostics: &mut ctx.insert_type_info_for_node(TypeInfo::__Error, call.node_id()); } _ => { - let message = format!("Incompatible type: cannot call type {}", callee_type_info); + let callee_type_name = get_name_for_type(callee_type_info, ctx); + let message = format!("Incompatible type: cannot call type {}", callee_type_name); let diagnostic = Diagnostic::new( &message, call.callee().source_range().start(), @@ -308,15 +322,55 @@ fn resolve_types_path(path: &Path, ctx: &mut AnalysisContext, diagnostics: &mut resolve_types_expression(path.base(), ctx, diagnostics); let base_type_info = ctx.get_type_info_for_node(path.base().node_id()); match base_type_info { - // TypeInfo::ClassInstance => {} todo + TypeInfo::Instance(instance_type_info) => { + let class_symbol_id = instance_type_info.class_symbol_id(); + let class_symbol = ctx.symbols()[class_symbol_id].unwrap_class_symbol(); + // try methods and then fields + let maybe_symbol_id = class_symbol + .field_symbol_ids() + .get(path.identifier().name()) + .or_else(|| { + class_symbol + .method_symbol_ids() + .get(path.identifier().name()) + }); + match maybe_symbol_id { + Some(member_symbol_id) => { + // here, we can perform logic such as visibility checking + // for now, we set the node's symbol and type to the appropriate values + let type_info_id = ctx.symbols_to_type_infos()[member_symbol_id]; + let type_info = ctx.get_type_info_by_id(type_info_id).clone(); + ctx.associate_node_to_symbol(path.node_id(), *member_symbol_id); + ctx.insert_type_info_for_node(type_info, path.node_id()); + } + None => { + let base_type_name = get_name_for_type(base_type_info, ctx); + let message = format!( + "Type {} has no such member: {}", + base_type_name, + path.identifier().name() + ); + let diagnostic = Diagnostic::new( + &message, + path.identifier().source_range().start(), + path.identifier().source_range().end(), + ); + diagnostics.push(diagnostic); + + // bubble it up: + ctx.insert_type_info_for_node(TypeInfo::__Error, path.node_id()); + } + } + } TypeInfo::__Error => { // bubble it up ctx.insert_type_info_for_node(TypeInfo::__Error, path.node_id()); } _ => { + let base_type_name = get_name_for_type(base_type_info, ctx); let message = format!( - "Incompatible type: cannot access a field on {}", - base_type_info + "Impossible operation: type {} does not have members to access", + base_type_name ); let diagnostic = Diagnostic::new( &message, diff --git a/dmc-lib/src/semantic_analysis/resolve_types/type_analysis.rs b/dmc-lib/src/semantic_analysis/resolve_types/type_analysis.rs index 9004eea..8937476 100644 --- a/dmc-lib/src/semantic_analysis/resolve_types/type_analysis.rs +++ b/dmc-lib/src/semantic_analysis/resolve_types/type_analysis.rs @@ -1,4 +1,5 @@ use crate::ast::binary_expression::BinaryOperation; +use crate::semantic_analysis::analysis_context::AnalysisContext; use crate::semantic_analysis::type_info::TypeInfo; pub fn are_binary_op_compatible(op: &BinaryOperation, left: &TypeInfo, right: &TypeInfo) -> bool { @@ -103,12 +104,18 @@ pub fn negate_result(operand: &TypeInfo) -> TypeInfo { } } -pub fn can_assign_right_to_left(left: &TypeInfo, right: &TypeInfo) -> bool { +pub fn can_assign_right_to_left(left: &TypeInfo, right: &TypeInfo, ctx: &AnalysisContext) -> bool { match left { TypeInfo::Any => true, TypeInfo::Function(_) => { panic!() } + TypeInfo::Instance(left_instance_type_info) => match right { + TypeInfo::Instance(right_instance_type_info) => { + left_instance_type_info.can_assign_from(right_instance_type_info, ctx) + } + _ => false, + }, TypeInfo::String => match right { TypeInfo::String => true, _ => false, diff --git a/dmc-lib/src/semantic_analysis/symbol.rs b/dmc-lib/src/semantic_analysis/symbol.rs index 5f994d9..ac88b49 100644 --- a/dmc-lib/src/semantic_analysis/symbol.rs +++ b/dmc-lib/src/semantic_analysis/symbol.rs @@ -1,10 +1,14 @@ +use crate::semantic_analysis::analysis_context::AnalysisContext; use crate::source_range::SourceRange; +use std::collections::HashMap; use std::rc::Rc; pub type SymbolId = usize; #[derive(Debug)] pub enum Symbol { + Class(ClassSymbol), + Field(FieldSymbol), Function(FunctionSymbol), Parameter(ParameterSymbol), Variable(VariableSymbol), @@ -13,6 +17,8 @@ pub enum Symbol { impl Symbol { pub fn declared_name(&self) -> &str { match self { + Symbol::Class(class_symbol) => class_symbol.declared_name(), + Symbol::Field(field_symbol) => &field_symbol.declared_name(), Symbol::Function(function_symbol) => function_symbol.declared_name(), Symbol::Parameter(parameter_symbol) => parameter_symbol.declared_name(), Symbol::Variable(variable_symbol) => variable_symbol.declared_name(), @@ -21,6 +27,8 @@ impl Symbol { pub fn declared_name_owned(&self) -> Rc { match self { + Symbol::Class(class_symbol) => class_symbol.declared_name_owned(), + Symbol::Field(field_symbol) => field_symbol.declared_name_owned(), Symbol::Function(function_symbol) => function_symbol.declared_name_owned(), Symbol::Parameter(parameter_symbol) => parameter_symbol.declared_name_owned(), Symbol::Variable(variable_symbol) => variable_symbol.declared_name_owned(), @@ -29,11 +37,105 @@ impl Symbol { pub fn source_range(&self) -> Option<&SourceRange> { match self { + Symbol::Class(class_symbol) => class_symbol.source_range(), + Symbol::Field(field_symbol) => field_symbol.source_range(), Symbol::Function(function_symbol) => function_symbol.source_range(), Symbol::Parameter(parameter_symbol) => parameter_symbol.source_range(), Symbol::Variable(variable_symbol) => variable_symbol.source_range(), } } + + pub fn unwrap_class_symbol(&self) -> &ClassSymbol { + match self { + Symbol::Class(class_symbol) => class_symbol, + _ => panic!("Attempt to unwrap {:?} as ClassSymbol", self), + } + } +} + +#[derive(Debug)] +pub struct ClassSymbol { + declared_name: Rc, + source_range: Option, + fqn: Rc, + is_extern: bool, + field_symbol_ids: HashMap, SymbolId>, + method_symbol_ids: HashMap, SymbolId>, +} + +impl ClassSymbol { + pub fn new( + declared_name: Rc, + source_range: Option, + fqn: Rc, + is_extern: bool, + field_symbol_ids: HashMap, SymbolId>, + method_symbol_ids: HashMap, SymbolId>, + ) -> Self { + Self { + declared_name, + source_range, + fqn, + is_extern, + field_symbol_ids, + method_symbol_ids, + } + } + + pub fn declared_name(&self) -> &str { + &self.declared_name + } + + pub fn declared_name_owned(&self) -> Rc { + self.declared_name.clone() + } + + pub fn source_range(&self) -> Option<&SourceRange> { + self.source_range.as_ref() + } + + pub fn fqn(&self) -> &str { + &self.fqn + } + + pub fn can_assign_from(&self, right: &ClassSymbol, _ctx: &AnalysisContext) -> bool { + self.fqn == right.fqn // eventually, this will check inheritance logic, etc. + } + + pub fn field_symbol_ids(&self) -> &HashMap, SymbolId> { + &self.field_symbol_ids + } + + pub fn method_symbol_ids(&self) -> &HashMap, SymbolId> { + &self.method_symbol_ids + } +} + +#[derive(Debug)] +pub struct FieldSymbol { + declared_name: Rc, + declared_name_source_range: Option, +} + +impl FieldSymbol { + pub fn new(declared_name: Rc, source_range: Option) -> Self { + Self { + declared_name, + declared_name_source_range: source_range, + } + } + + pub fn declared_name(&self) -> &str { + &self.declared_name + } + + pub fn declared_name_owned(&self) -> Rc { + self.declared_name.clone() + } + + pub fn source_range(&self) -> Option<&SourceRange> { + self.declared_name_source_range.as_ref() + } } #[derive(Debug)] @@ -42,6 +144,7 @@ pub struct FunctionSymbol { declared_name_source_range: Option, fqn: Rc, is_extern: bool, + is_method: bool, } impl FunctionSymbol { @@ -50,12 +153,14 @@ impl FunctionSymbol { declared_name_source_range: Option, fqn: Rc, is_extern: bool, + is_method: bool, ) -> Self { Self { declared_name, declared_name_source_range, fqn, is_extern, + is_method, } } @@ -82,6 +187,10 @@ impl FunctionSymbol { pub fn is_extern(&self) -> bool { self.is_extern } + + pub fn is_method(&self) -> bool { + self.is_method + } } #[derive(Debug)] diff --git a/dmc-lib/src/semantic_analysis/type_info.rs b/dmc-lib/src/semantic_analysis/type_info.rs index e025d09..ab8dbb3 100644 --- a/dmc-lib/src/semantic_analysis/type_info.rs +++ b/dmc-lib/src/semantic_analysis/type_info.rs @@ -1,4 +1,5 @@ -use std::fmt::{Display, Formatter}; +use crate::semantic_analysis::analysis_context::AnalysisContext; +use crate::semantic_analysis::symbol::SymbolId; pub type TypeInfoId = usize; @@ -6,6 +7,7 @@ pub type TypeInfoId = usize; pub enum TypeInfo { Any, Function(FunctionTypeInfo), + Instance(InstanceTypeInfo), String, Int, Double, @@ -13,35 +15,23 @@ pub enum TypeInfo { __Error, } -impl Display for TypeInfo { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - match self { - TypeInfo::Any => "Any", - TypeInfo::Function(_) => "Function", - TypeInfo::String => "String", - TypeInfo::Int => "Int", - TypeInfo::Double => "Double", - TypeInfo::Void => "Void", - TypeInfo::__Error => panic!("This should never be shown to a user."), - } - ) - } -} - #[derive(Clone, Debug)] pub struct FunctionTypeInfo { parameter_type_info_ids: Vec, return_type_info_id: TypeInfoId, + self_type_info_id: Option, } impl FunctionTypeInfo { - pub fn new(parameter_type_info_ids: Vec, return_type_info_id: TypeInfoId) -> Self { + pub fn new( + parameter_type_info_ids: Vec, + return_type_info_id: TypeInfoId, + self_type_info_id: Option, + ) -> Self { Self { parameter_type_info_ids, return_type_info_id, + self_type_info_id, } } @@ -52,4 +42,29 @@ impl FunctionTypeInfo { pub fn return_type_id(&self) -> TypeInfoId { self.return_type_info_id } + + pub fn self_type_id(&self) -> Option { + self.self_type_info_id + } +} + +#[derive(Clone, Debug)] +pub struct InstanceTypeInfo { + class_symbol_id: SymbolId, +} + +impl InstanceTypeInfo { + pub fn new(class_symbol_id: SymbolId) -> Self { + Self { class_symbol_id } + } + + pub fn class_symbol_id(&self) -> SymbolId { + self.class_symbol_id + } + + pub fn can_assign_from(&self, right: &InstanceTypeInfo, ctx: &AnalysisContext) -> bool { + let self_class_symbol = ctx.symbols()[self.class_symbol_id].unwrap_class_symbol(); + let right_class_symbol = ctx.symbols()[right.class_symbol_id].unwrap_class_symbol(); + self_class_symbol.can_assign_from(right_class_symbol, ctx) + } } diff --git a/e2e-tests/src/lib.rs b/e2e-tests/src/lib.rs index 4b31e76..1761f24 100644 --- a/e2e-tests/src/lib.rs +++ b/e2e-tests/src/lib.rs @@ -3,10 +3,12 @@ mod e2e_tests { use dmc_lib::compile_compilation_unit; use dmc_lib::constants_table::ConstantsTable; use dmc_lib::diagnostic::{Diagnostic, Diagnostics}; + use dmc_lib::intrinsics::add_primitive_symbols_and_type_infos; use dmc_lib::semantic_analysis::analysis_context::AnalysisContext; use dvm_lib::vm::constant::{Constant, StringConstant}; use dvm_lib::vm::value::Value; use dvm_lib::vm::{DvmContext, call}; + use std::error::Error; use std::rc::Rc; const REGISTER_COUNT: usize = 16; @@ -25,6 +27,9 @@ mod e2e_tests { fn prepare_context(input: &str) -> Result { let mut ctx = AnalysisContext::new(); + let global_scope_id = ctx.push_scope("global"); + add_primitive_symbols_and_type_infos(&mut ctx, global_scope_id); + let mut constants_table = ConstantsTable::new(); let compile_compilation_unit_result = @@ -224,4 +229,31 @@ mod e2e_tests { assert_eq!(o.fields()[0].unwrap_int(), 42); Ok(()) } + + #[test] + fn string_len() -> Result<(), Diagnostics> { + fn core_string_len(args: &[Value]) -> Result> { + let this = args[0].unwrap_string(); + let len = this.len(); + Ok(Value::Int(len.try_into()?)) + } + + let mut context = prepare_context( + " + fn main() -> Int + \"Hello, World!\".len() + end + ", + )?; + + context + .platform_functions_mut() + .insert("core::String::len".into(), core_string_len); + let result = get_result(&context, "main", &vec![]); + + assert!(result.is_some()); + let value = result.unwrap(); + assert!(matches!(value, Value::Int(13))); + Ok(()) + } }