String len method working!
This commit is contained in:
parent
347a9ec9c9
commit
b8f2620279
@ -4,11 +4,14 @@ use codespan_reporting::term::{Config, WriteStyle, emit_to_write_style};
|
|||||||
use dmc_lib::SyntheticFunctionSession;
|
use dmc_lib::SyntheticFunctionSession;
|
||||||
use dmc_lib::constants_table::ConstantsTable;
|
use dmc_lib::constants_table::ConstantsTable;
|
||||||
use dmc_lib::diagnostic::Diagnostics;
|
use dmc_lib::diagnostic::Diagnostics;
|
||||||
|
use dmc_lib::intrinsics::add_primitive_symbols_and_type_infos;
|
||||||
use dmc_lib::parser::parse_statement;
|
use dmc_lib::parser::parse_statement;
|
||||||
use dvm_lib::vm::constant::{Constant, StringConstant};
|
use dvm_lib::vm::constant::{Constant, StringConstant};
|
||||||
use dvm_lib::vm::function::Function;
|
use dvm_lib::vm::function::Function;
|
||||||
use dvm_lib::vm::operand::Operand;
|
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 dvm_lib::vm::{CallStack, DvmContext, loop_instructions, prepare_for_instruction_loop};
|
||||||
|
use std::error::Error;
|
||||||
use std::io::{BufRead, Write};
|
use std::io::{BufRead, Write};
|
||||||
|
|
||||||
pub fn repl(
|
pub fn repl(
|
||||||
@ -26,13 +29,26 @@ pub fn repl(
|
|||||||
let mut session = SyntheticFunctionSession::new("__repl");
|
let mut session = SyntheticFunctionSession::new("__repl");
|
||||||
|
|
||||||
let analysis_context = session.ctx_mut();
|
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_function_scope");
|
||||||
analysis_context.push_scope("__repl_body_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 constants_table = ConstantsTable::new();
|
||||||
|
|
||||||
let mut dvm_context = DvmContext::new();
|
let mut dvm_context = DvmContext::new();
|
||||||
|
|
||||||
|
// todo: move this to a core impl crate
|
||||||
|
fn core_string_len(args: &[Value]) -> Result<Value, Box<dyn Error>> {
|
||||||
|
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<Operand> = Vec::new();
|
let mut repl_fn_stack_locals: Vec<Operand> = Vec::new();
|
||||||
|
|
||||||
'repl: loop {
|
'repl: loop {
|
||||||
|
|||||||
51
dmc-lib/src/intrinsics.rs
Normal file
51
dmc-lib/src/intrinsics.rs
Normal file
@ -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);
|
||||||
|
}
|
||||||
@ -20,6 +20,7 @@ pub mod constants_table;
|
|||||||
pub mod diagnostic;
|
pub mod diagnostic;
|
||||||
mod diagnostic_factories;
|
mod diagnostic_factories;
|
||||||
mod error_codes;
|
mod error_codes;
|
||||||
|
pub mod intrinsics;
|
||||||
pub mod ir;
|
pub mod ir;
|
||||||
pub mod lexer;
|
pub mod lexer;
|
||||||
pub mod lowering;
|
pub mod lowering;
|
||||||
|
|||||||
@ -571,11 +571,29 @@ fn lower_to_ir_call(
|
|||||||
let callee_symbol = &ctx.symbols()[callee_symbol_id];
|
let callee_symbol = &ctx.symbols()[callee_symbol_id];
|
||||||
match callee_symbol {
|
match callee_symbol {
|
||||||
Symbol::Function(function_symbol) => {
|
Symbol::Function(function_symbol) => {
|
||||||
let arguments = call
|
let mut arguments = Vec::new();
|
||||||
.arguments()
|
if function_symbol.is_method() {
|
||||||
.iter()
|
let this = match call.callee() {
|
||||||
.map(|e| lower_expression_to_ir_expression(e, ctx, fn_ctx))
|
Expression::Path(path) => {
|
||||||
.collect();
|
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(
|
IrCall::new(
|
||||||
function_symbol.fqn_owned(),
|
function_symbol.fqn_owned(),
|
||||||
arguments,
|
arguments,
|
||||||
|
|||||||
@ -8,7 +8,7 @@ pub fn to_ir_type_info(sa_type_info: &TypeInfo) -> IrTypeInfo {
|
|||||||
TypeInfo::Double => IrTypeInfo::Double,
|
TypeInfo::Double => IrTypeInfo::Double,
|
||||||
TypeInfo::Void => IrTypeInfo::Void,
|
TypeInfo::Void => IrTypeInfo::Void,
|
||||||
_ => {
|
_ => {
|
||||||
panic!("BUG! Unknown sa_type_info: {}", sa_type_info);
|
panic!("BUG! Unknown sa_type_info: {:?}", sa_type_info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
21
dmc-lib/src/semantic_analysis/analysis_context/helpers.rs
Normal file
21
dmc-lib/src/semantic_analysis/analysis_context/helpers.rs
Normal file
@ -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."),
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,3 +1,5 @@
|
|||||||
|
pub mod helpers;
|
||||||
|
|
||||||
use crate::ast::NodeId;
|
use crate::ast::NodeId;
|
||||||
use crate::diagnostic::Diagnostic;
|
use crate::diagnostic::Diagnostic;
|
||||||
use crate::diagnostic_factories::symbol_not_found;
|
use crate::diagnostic_factories::symbol_not_found;
|
||||||
@ -44,14 +46,6 @@ impl AnalysisContext {
|
|||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scopes(&self) -> &[Scope] {
|
|
||||||
&self.scopes
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn scopes_mut(&mut self) -> &mut Vec<Scope> {
|
|
||||||
&mut self.scopes
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn symbols(&self) -> &[Symbol] {
|
pub fn symbols(&self) -> &[Symbol] {
|
||||||
&self.symbols
|
&self.symbols
|
||||||
}
|
}
|
||||||
@ -86,10 +80,11 @@ impl AnalysisContext {
|
|||||||
fqn.join("::")
|
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);
|
let scope = Scope::new(self.current_scope_id);
|
||||||
self.scopes.push(scope);
|
self.scopes.push(scope);
|
||||||
self.current_scope_id = Some(self.scopes.len() - 1);
|
self.current_scope_id = Some(self.scopes.len() - 1);
|
||||||
|
self.current_scope_id.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_scope(&self, scope_id: ScopeId) -> &Scope {
|
fn get_scope(&self, scope_id: ScopeId) -> &Scope {
|
||||||
@ -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 {
|
fn insert_symbol_in_scope(&mut self, scope_id: ScopeId, symbol: Symbol) -> SymbolId {
|
||||||
// get declared name
|
// get declared name
|
||||||
let declared_name = symbol.declared_name_owned();
|
let declared_name = symbol.declared_name_owned();
|
||||||
@ -159,14 +159,14 @@ impl AnalysisContext {
|
|||||||
&mut self,
|
&mut self,
|
||||||
to_insert: Symbol,
|
to_insert: Symbol,
|
||||||
scope_id: ScopeId,
|
scope_id: ScopeId,
|
||||||
) -> Result<(), Diagnostic> {
|
) -> Result<SymbolId, Diagnostic> {
|
||||||
let scope = self.get_scope(scope_id);
|
let scope = self.get_scope(scope_id);
|
||||||
if let Some(already_declared) = self.get_symbol_in_scope(scope, to_insert.declared_name()) {
|
if let Some(already_declared) = self.get_symbol_in_scope(scope, to_insert.declared_name()) {
|
||||||
Err(symbol_already_declared(already_declared, &to_insert))
|
Err(symbol_already_declared(already_declared, &to_insert))
|
||||||
} else {
|
} else {
|
||||||
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
|
// 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);
|
.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) {
|
pub fn associate_node_and_symbol_to_type(&mut self, node_id: NodeId, type_info_id: TypeInfoId) {
|
||||||
let symbol_id = self
|
let symbol_id = self
|
||||||
.nodes_to_symbols
|
.nodes_to_symbols
|
||||||
.get(&node_id)
|
.get(&node_id)
|
||||||
.expect(&format!("node_id {} has no associated symbol", node_id));
|
.expect(&format!("node_id {} has no associated symbol", node_id));
|
||||||
self.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);
|
self.nodes_to_type_infos.insert(node_id, type_info_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,4 +309,25 @@ impl AnalysisContext {
|
|||||||
));
|
));
|
||||||
*symbol_type_info_id
|
*symbol_type_info_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn find_symbol_by_fqn(&self, fqn: &str) -> Option<SymbolId> {
|
||||||
|
// This is quite naive and probably very slow; we will certainly use trees of HashMaps (or
|
||||||
|
// 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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -38,6 +38,7 @@ fn collect_symbols_function(
|
|||||||
Some(function.declared_name_source_range()),
|
Some(function.declared_name_source_range()),
|
||||||
fqn,
|
fqn,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
));
|
));
|
||||||
|
|
||||||
// insert
|
// insert
|
||||||
@ -70,6 +71,7 @@ fn collect_symbols_extern_function(
|
|||||||
Some(extern_function.declared_name_source_range()),
|
Some(extern_function.declared_name_source_range()),
|
||||||
fqn,
|
fqn,
|
||||||
true,
|
true,
|
||||||
|
false,
|
||||||
));
|
));
|
||||||
|
|
||||||
// insert function symbol
|
// insert function symbol
|
||||||
|
|||||||
@ -28,6 +28,7 @@ fn collect_types_function(function: &Function, ctx: &mut AnalysisContext) {
|
|||||||
let function_type_info = TypeInfo::Function(FunctionTypeInfo::new(
|
let function_type_info = TypeInfo::Function(FunctionTypeInfo::new(
|
||||||
parameter_type_info_ids,
|
parameter_type_info_ids,
|
||||||
return_type_info_id,
|
return_type_info_id,
|
||||||
|
None,
|
||||||
));
|
));
|
||||||
let function_type_info_id = ctx.insert_type_info(function_type_info);
|
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(
|
let function_type_info = TypeInfo::Function(FunctionTypeInfo::new(
|
||||||
parameter_type_info_ids,
|
parameter_type_info_ids,
|
||||||
return_type_info_id,
|
return_type_info_id,
|
||||||
|
None,
|
||||||
));
|
));
|
||||||
|
|
||||||
let function_type_info_id = ctx.insert_type_info(function_type_info);
|
let function_type_info_id = ctx.insert_type_info(function_type_info);
|
||||||
|
|||||||
@ -15,10 +15,11 @@ use crate::ast::statement::Statement;
|
|||||||
use crate::diagnostic::{Diagnostic, Diagnostics};
|
use crate::diagnostic::{Diagnostic, Diagnostics};
|
||||||
use crate::error_codes::BINARY_INCOMPATIBLE_TYPES;
|
use crate::error_codes::BINARY_INCOMPATIBLE_TYPES;
|
||||||
use crate::semantic_analysis::analysis_context::AnalysisContext;
|
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::{
|
use crate::semantic_analysis::resolve_types::type_analysis::{
|
||||||
are_binary_op_compatible, binary_op_result, can_assign_right_to_left, can_negate, negate_result,
|
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);
|
pub struct ResolveTypesResult(pub Diagnostics);
|
||||||
|
|
||||||
@ -110,10 +111,12 @@ fn resolve_types_assign_statement(
|
|||||||
let destination_type_info =
|
let destination_type_info =
|
||||||
ctx.get_type_info_for_node(assign_statement.destination().node_id());
|
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!(
|
let message = format!(
|
||||||
"Incompatible types: cannot assign {} from {}",
|
"Incompatible types: cannot assign {} from {}",
|
||||||
destination_type_info, value_type_info
|
destination_type_name, value_type_name
|
||||||
);
|
);
|
||||||
let diagnostic = Diagnostic::new(
|
let diagnostic = Diagnostic::new(
|
||||||
&message,
|
&message,
|
||||||
@ -152,7 +155,13 @@ fn resolve_types_expression(
|
|||||||
ctx.insert_type_info_for_node(TypeInfo::Double, double_literal.node_id());
|
ctx.insert_type_info_for_node(TypeInfo::Double, double_literal.node_id());
|
||||||
}
|
}
|
||||||
Expression::String(string_literal) => {
|
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::BitwiseXor => "bitwise xor",
|
||||||
BinaryOperation::BitwiseOr => "bitwise or",
|
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!(
|
let message = format!(
|
||||||
"Incompatible types: cannot {} {} and {}",
|
"Incompatible types: cannot {} {} and {}",
|
||||||
op_name, lhs_type_info, rhs_type_info
|
op_name, lhs_type_name, rhs_type_name
|
||||||
);
|
);
|
||||||
let diagnostic = Diagnostic::new(
|
let diagnostic = Diagnostic::new(
|
||||||
&message,
|
&message,
|
||||||
@ -215,7 +226,8 @@ fn resolve_types_negative_expression(
|
|||||||
let result_type_info = negate_result(operand_type_info);
|
let result_type_info = negate_result(operand_type_info);
|
||||||
ctx.insert_type_info_for_node(result_type_info, negative_expression.node_id());
|
ctx.insert_type_info_for_node(result_type_info, negative_expression.node_id());
|
||||||
} else {
|
} 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(
|
let diagnostic = Diagnostic::new(
|
||||||
&message,
|
&message,
|
||||||
negative_expression.source_range().start(),
|
negative_expression.source_range().start(),
|
||||||
@ -259,15 +271,18 @@ fn resolve_types_call(call: &Call, ctx: &mut AnalysisContext, diagnostics: &mut
|
|||||||
// of the function
|
// 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();
|
let parameter_type_ids = function_type_info.parameter_type_ids();
|
||||||
|
if arguments.len() == parameter_type_ids.len() {
|
||||||
for i in 0..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 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
|
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) {
|
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!(
|
let message = format!(
|
||||||
"Incompatible types: cannot assign {} to {}",
|
"Incompatible types: cannot assign {} to {}",
|
||||||
argument_type_info, parameter_type_info
|
argument_type_name, parameter_type_name
|
||||||
);
|
);
|
||||||
let diagnostic = Diagnostic::new(
|
let diagnostic = Diagnostic::new(
|
||||||
&message,
|
&message,
|
||||||
@ -275,9 +290,7 @@ fn resolve_types_call(call: &Call, ctx: &mut AnalysisContext, diagnostics: &mut
|
|||||||
arguments[i].source_range().end(),
|
arguments[i].source_range().end(),
|
||||||
);
|
);
|
||||||
diagnostics.push(diagnostic);
|
diagnostics.push(diagnostic);
|
||||||
|
}
|
||||||
// do not push an error type because the result of the whole call is the return type
|
|
||||||
// of the function
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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());
|
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(
|
let diagnostic = Diagnostic::new(
|
||||||
&message,
|
&message,
|
||||||
call.callee().source_range().start(),
|
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);
|
resolve_types_expression(path.base(), ctx, diagnostics);
|
||||||
let base_type_info = ctx.get_type_info_for_node(path.base().node_id());
|
let base_type_info = ctx.get_type_info_for_node(path.base().node_id());
|
||||||
match base_type_info {
|
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 => {
|
TypeInfo::__Error => {
|
||||||
// bubble it up
|
// bubble it up
|
||||||
ctx.insert_type_info_for_node(TypeInfo::__Error, path.node_id());
|
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!(
|
let message = format!(
|
||||||
"Incompatible type: cannot access a field on {}",
|
"Impossible operation: type {} does not have members to access",
|
||||||
base_type_info
|
base_type_name
|
||||||
);
|
);
|
||||||
let diagnostic = Diagnostic::new(
|
let diagnostic = Diagnostic::new(
|
||||||
&message,
|
&message,
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
use crate::ast::binary_expression::BinaryOperation;
|
use crate::ast::binary_expression::BinaryOperation;
|
||||||
|
use crate::semantic_analysis::analysis_context::AnalysisContext;
|
||||||
use crate::semantic_analysis::type_info::TypeInfo;
|
use crate::semantic_analysis::type_info::TypeInfo;
|
||||||
|
|
||||||
pub fn are_binary_op_compatible(op: &BinaryOperation, left: &TypeInfo, right: &TypeInfo) -> bool {
|
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 {
|
match left {
|
||||||
TypeInfo::Any => true,
|
TypeInfo::Any => true,
|
||||||
TypeInfo::Function(_) => {
|
TypeInfo::Function(_) => {
|
||||||
panic!()
|
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 => match right {
|
||||||
TypeInfo::String => true,
|
TypeInfo::String => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
|
|||||||
@ -1,10 +1,14 @@
|
|||||||
|
use crate::semantic_analysis::analysis_context::AnalysisContext;
|
||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub type SymbolId = usize;
|
pub type SymbolId = usize;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Symbol {
|
pub enum Symbol {
|
||||||
|
Class(ClassSymbol),
|
||||||
|
Field(FieldSymbol),
|
||||||
Function(FunctionSymbol),
|
Function(FunctionSymbol),
|
||||||
Parameter(ParameterSymbol),
|
Parameter(ParameterSymbol),
|
||||||
Variable(VariableSymbol),
|
Variable(VariableSymbol),
|
||||||
@ -13,6 +17,8 @@ pub enum Symbol {
|
|||||||
impl Symbol {
|
impl Symbol {
|
||||||
pub fn declared_name(&self) -> &str {
|
pub fn declared_name(&self) -> &str {
|
||||||
match self {
|
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::Function(function_symbol) => function_symbol.declared_name(),
|
||||||
Symbol::Parameter(parameter_symbol) => parameter_symbol.declared_name(),
|
Symbol::Parameter(parameter_symbol) => parameter_symbol.declared_name(),
|
||||||
Symbol::Variable(variable_symbol) => variable_symbol.declared_name(),
|
Symbol::Variable(variable_symbol) => variable_symbol.declared_name(),
|
||||||
@ -21,6 +27,8 @@ impl Symbol {
|
|||||||
|
|
||||||
pub fn declared_name_owned(&self) -> Rc<str> {
|
pub fn declared_name_owned(&self) -> Rc<str> {
|
||||||
match self {
|
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::Function(function_symbol) => function_symbol.declared_name_owned(),
|
||||||
Symbol::Parameter(parameter_symbol) => parameter_symbol.declared_name_owned(),
|
Symbol::Parameter(parameter_symbol) => parameter_symbol.declared_name_owned(),
|
||||||
Symbol::Variable(variable_symbol) => variable_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> {
|
pub fn source_range(&self) -> Option<&SourceRange> {
|
||||||
match self {
|
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::Function(function_symbol) => function_symbol.source_range(),
|
||||||
Symbol::Parameter(parameter_symbol) => parameter_symbol.source_range(),
|
Symbol::Parameter(parameter_symbol) => parameter_symbol.source_range(),
|
||||||
Symbol::Variable(variable_symbol) => variable_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<str>,
|
||||||
|
source_range: Option<SourceRange>,
|
||||||
|
fqn: Rc<str>,
|
||||||
|
is_extern: bool,
|
||||||
|
field_symbol_ids: HashMap<Rc<str>, SymbolId>,
|
||||||
|
method_symbol_ids: HashMap<Rc<str>, SymbolId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClassSymbol {
|
||||||
|
pub fn new(
|
||||||
|
declared_name: Rc<str>,
|
||||||
|
source_range: Option<SourceRange>,
|
||||||
|
fqn: Rc<str>,
|
||||||
|
is_extern: bool,
|
||||||
|
field_symbol_ids: HashMap<Rc<str>, SymbolId>,
|
||||||
|
method_symbol_ids: HashMap<Rc<str>, 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<str> {
|
||||||
|
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<Rc<str>, SymbolId> {
|
||||||
|
&self.field_symbol_ids
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn method_symbol_ids(&self) -> &HashMap<Rc<str>, SymbolId> {
|
||||||
|
&self.method_symbol_ids
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct FieldSymbol {
|
||||||
|
declared_name: Rc<str>,
|
||||||
|
declared_name_source_range: Option<SourceRange>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FieldSymbol {
|
||||||
|
pub fn new(declared_name: Rc<str>, source_range: Option<SourceRange>) -> 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<str> {
|
||||||
|
self.declared_name.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn source_range(&self) -> Option<&SourceRange> {
|
||||||
|
self.declared_name_source_range.as_ref()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -42,6 +144,7 @@ pub struct FunctionSymbol {
|
|||||||
declared_name_source_range: Option<SourceRange>,
|
declared_name_source_range: Option<SourceRange>,
|
||||||
fqn: Rc<str>,
|
fqn: Rc<str>,
|
||||||
is_extern: bool,
|
is_extern: bool,
|
||||||
|
is_method: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FunctionSymbol {
|
impl FunctionSymbol {
|
||||||
@ -50,12 +153,14 @@ impl FunctionSymbol {
|
|||||||
declared_name_source_range: Option<SourceRange>,
|
declared_name_source_range: Option<SourceRange>,
|
||||||
fqn: Rc<str>,
|
fqn: Rc<str>,
|
||||||
is_extern: bool,
|
is_extern: bool,
|
||||||
|
is_method: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
declared_name,
|
declared_name,
|
||||||
declared_name_source_range,
|
declared_name_source_range,
|
||||||
fqn,
|
fqn,
|
||||||
is_extern,
|
is_extern,
|
||||||
|
is_method,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,6 +187,10 @@ impl FunctionSymbol {
|
|||||||
pub fn is_extern(&self) -> bool {
|
pub fn is_extern(&self) -> bool {
|
||||||
self.is_extern
|
self.is_extern
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_method(&self) -> bool {
|
||||||
|
self.is_method
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|||||||
@ -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;
|
pub type TypeInfoId = usize;
|
||||||
|
|
||||||
@ -6,6 +7,7 @@ pub type TypeInfoId = usize;
|
|||||||
pub enum TypeInfo {
|
pub enum TypeInfo {
|
||||||
Any,
|
Any,
|
||||||
Function(FunctionTypeInfo),
|
Function(FunctionTypeInfo),
|
||||||
|
Instance(InstanceTypeInfo),
|
||||||
String,
|
String,
|
||||||
Int,
|
Int,
|
||||||
Double,
|
Double,
|
||||||
@ -13,35 +15,23 @@ pub enum TypeInfo {
|
|||||||
__Error,
|
__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)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct FunctionTypeInfo {
|
pub struct FunctionTypeInfo {
|
||||||
parameter_type_info_ids: Vec<TypeInfoId>,
|
parameter_type_info_ids: Vec<TypeInfoId>,
|
||||||
return_type_info_id: TypeInfoId,
|
return_type_info_id: TypeInfoId,
|
||||||
|
self_type_info_id: Option<TypeInfoId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FunctionTypeInfo {
|
impl FunctionTypeInfo {
|
||||||
pub fn new(parameter_type_info_ids: Vec<TypeInfoId>, return_type_info_id: TypeInfoId) -> Self {
|
pub fn new(
|
||||||
|
parameter_type_info_ids: Vec<TypeInfoId>,
|
||||||
|
return_type_info_id: TypeInfoId,
|
||||||
|
self_type_info_id: Option<TypeInfoId>,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
parameter_type_info_ids,
|
parameter_type_info_ids,
|
||||||
return_type_info_id,
|
return_type_info_id,
|
||||||
|
self_type_info_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,4 +42,29 @@ impl FunctionTypeInfo {
|
|||||||
pub fn return_type_id(&self) -> TypeInfoId {
|
pub fn return_type_id(&self) -> TypeInfoId {
|
||||||
self.return_type_info_id
|
self.return_type_info_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn self_type_id(&self) -> Option<TypeInfoId> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,10 +3,12 @@ mod e2e_tests {
|
|||||||
use dmc_lib::compile_compilation_unit;
|
use dmc_lib::compile_compilation_unit;
|
||||||
use dmc_lib::constants_table::ConstantsTable;
|
use dmc_lib::constants_table::ConstantsTable;
|
||||||
use dmc_lib::diagnostic::{Diagnostic, Diagnostics};
|
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 dmc_lib::semantic_analysis::analysis_context::AnalysisContext;
|
||||||
use dvm_lib::vm::constant::{Constant, StringConstant};
|
use dvm_lib::vm::constant::{Constant, StringConstant};
|
||||||
use dvm_lib::vm::value::Value;
|
use dvm_lib::vm::value::Value;
|
||||||
use dvm_lib::vm::{DvmContext, call};
|
use dvm_lib::vm::{DvmContext, call};
|
||||||
|
use std::error::Error;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
const REGISTER_COUNT: usize = 16;
|
const REGISTER_COUNT: usize = 16;
|
||||||
@ -25,6 +27,9 @@ mod e2e_tests {
|
|||||||
|
|
||||||
fn prepare_context(input: &str) -> Result<DvmContext, Diagnostics> {
|
fn prepare_context(input: &str) -> Result<DvmContext, Diagnostics> {
|
||||||
let mut ctx = AnalysisContext::new();
|
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 mut constants_table = ConstantsTable::new();
|
||||||
|
|
||||||
let compile_compilation_unit_result =
|
let compile_compilation_unit_result =
|
||||||
@ -224,4 +229,31 @@ mod e2e_tests {
|
|||||||
assert_eq!(o.fields()[0].unwrap_int(), 42);
|
assert_eq!(o.fields()[0].unwrap_int(), 42);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn string_len() -> Result<(), Diagnostics> {
|
||||||
|
fn core_string_len(args: &[Value]) -> Result<Value, Box<dyn Error>> {
|
||||||
|
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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user