Compare commits
4 Commits
2dfd8e8a29
...
10579e2ad2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10579e2ad2 | ||
|
|
f0f377e44e | ||
|
|
b8f2620279 | ||
|
|
347a9ec9c9 |
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -119,6 +119,13 @@ dependencies = [
|
|||||||
"dvm-lib",
|
"dvm-lib",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dm-core-impl"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"dvm-lib",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dm-std-lib"
|
name = "dm-std-lib"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
members = ["dm", "dm-std-lib", "dmc-lib", "dvm-lib", "e2e-tests"]
|
members = ["dm", "dm-core-impl", "dm-std-lib", "dmc-lib", "dvm-lib", "e2e-tests"]
|
||||||
|
|||||||
7
dm-core-impl/Cargo.toml
Normal file
7
dm-core-impl/Cargo.toml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[package]
|
||||||
|
name = "dm-core-impl"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
dvm-lib = { path = "../dvm-lib" }
|
||||||
31
dm-core-impl/src/error.rs
Normal file
31
dm-core-impl/src/error.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
use std::error::Error;
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct IllegalArgumentError {
|
||||||
|
message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eventually we'll want support for source position, so we can highlight which argument(s)
|
||||||
|
// are wrong
|
||||||
|
impl IllegalArgumentError {
|
||||||
|
pub fn wrong_type(expected: &str, found: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
message: format!("Expected {} but found {}", expected, found),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wrong_number(expected: usize, found: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
message: format!("Expected {} argument(s) but found {}", expected, found),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for IllegalArgumentError {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "IllegalArgumentError: {}", self.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for IllegalArgumentError {}
|
||||||
7
dm-core-impl/src/lib.rs
Normal file
7
dm-core-impl/src/lib.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
mod error;
|
||||||
|
pub mod print;
|
||||||
|
pub mod string;
|
||||||
|
mod util;
|
||||||
|
|
||||||
|
pub use print::*;
|
||||||
|
pub use string::*;
|
||||||
56
dm-core-impl/src/print.rs
Normal file
56
dm-core-impl/src/print.rs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
use crate::error::IllegalArgumentError;
|
||||||
|
use dvm_lib::vm::value::Value;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
pub fn core_print(args: &[Value]) -> Result<Value, Box<dyn Error>> {
|
||||||
|
match args.get(0) {
|
||||||
|
None => Err(Box::new(IllegalArgumentError::wrong_number(1, 0))),
|
||||||
|
Some(msg) => {
|
||||||
|
match msg {
|
||||||
|
Value::Object(object) => {
|
||||||
|
todo!("printing objects")
|
||||||
|
}
|
||||||
|
Value::Int(i) => {
|
||||||
|
print!("{}", i);
|
||||||
|
}
|
||||||
|
Value::Double(d) => {
|
||||||
|
print!("{}", d);
|
||||||
|
}
|
||||||
|
Value::String(s) => {
|
||||||
|
print!("{}", s.deref());
|
||||||
|
}
|
||||||
|
Value::Null => {
|
||||||
|
print!("Null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Value::Null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn core_println(args: &[Value]) -> Result<Value, Box<dyn Error>> {
|
||||||
|
match args.get(0) {
|
||||||
|
None => Err(Box::new(IllegalArgumentError::wrong_number(1, 0))),
|
||||||
|
Some(msg) => {
|
||||||
|
match msg {
|
||||||
|
Value::Object(object) => {
|
||||||
|
todo!("printing objects")
|
||||||
|
}
|
||||||
|
Value::Int(i) => {
|
||||||
|
println!("{}", i);
|
||||||
|
}
|
||||||
|
Value::Double(d) => {
|
||||||
|
println!("{}", d);
|
||||||
|
}
|
||||||
|
Value::String(s) => {
|
||||||
|
println!("{}", s.deref());
|
||||||
|
}
|
||||||
|
Value::Null => {
|
||||||
|
print!("Null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Value::Null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
dm-core-impl/src/string.rs
Normal file
21
dm-core-impl/src/string.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
use crate::error::IllegalArgumentError;
|
||||||
|
use crate::util::value_to_variant_name;
|
||||||
|
use dvm_lib::vm::value::Value;
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
pub fn core_string_len(args: &[Value]) -> Result<Value, Box<dyn Error>> {
|
||||||
|
let this = args.get(0);
|
||||||
|
match this {
|
||||||
|
None => Err(Box::new(IllegalArgumentError::wrong_number(1, 0))),
|
||||||
|
Some(this) => match this {
|
||||||
|
Value::String(s) => {
|
||||||
|
let len: i64 = s.len().try_into()?;
|
||||||
|
todo!("Long Values")
|
||||||
|
}
|
||||||
|
_ => Err(Box::new(IllegalArgumentError::wrong_type(
|
||||||
|
"String",
|
||||||
|
value_to_variant_name(this),
|
||||||
|
))),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
11
dm-core-impl/src/util.rs
Normal file
11
dm-core-impl/src/util.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
use dvm_lib::vm::value::Value;
|
||||||
|
|
||||||
|
pub fn value_to_variant_name(value: &Value) -> &'static str {
|
||||||
|
match value {
|
||||||
|
Value::Object(_) => "Object", // todo: lookup class
|
||||||
|
Value::Int(_) => "Int",
|
||||||
|
Value::Double(_) => "Double",
|
||||||
|
Value::String(_) => "String",
|
||||||
|
Value::Null => "Null",
|
||||||
|
}
|
||||||
|
}
|
||||||
6
dm-core/src/lib.dm
Normal file
6
dm-core/src/lib.dm
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
mod ops
|
||||||
|
mod print
|
||||||
|
mod string
|
||||||
|
|
||||||
|
pub use print::*
|
||||||
|
pub use string::*
|
||||||
6
dm-core/src/ops.dm
Normal file
6
dm-core/src/ops.dm
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
pub trait Add
|
||||||
|
type Right = Self
|
||||||
|
type Result = Self
|
||||||
|
|
||||||
|
fn add(right: Self::Right) -> Self::Result
|
||||||
|
end
|
||||||
3
dm-core/src/print.dm
Normal file
3
dm-core/src/print.dm
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub extern fn print(msg: Any) -> Void
|
||||||
|
|
||||||
|
pub extern fn println(msg: Any) -> Void
|
||||||
5
dm-core/src/string.dm
Normal file
5
dm-core/src/string.dm
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
pub extern class String
|
||||||
|
pub extern fn len() -> Long
|
||||||
|
end
|
||||||
|
|
||||||
|
intrinsic impl<T> Add<Right = T> for String end
|
||||||
@ -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 {
|
||||||
|
|||||||
@ -5,6 +5,7 @@ use crate::ast::double_literal::DoubleLiteral;
|
|||||||
use crate::ast::identifier::Identifier;
|
use crate::ast::identifier::Identifier;
|
||||||
use crate::ast::integer_literal::IntegerLiteral;
|
use crate::ast::integer_literal::IntegerLiteral;
|
||||||
use crate::ast::negative_expression::NegativeExpression;
|
use crate::ast::negative_expression::NegativeExpression;
|
||||||
|
use crate::ast::path::Path;
|
||||||
use crate::ast::string_literal::StringLiteral;
|
use crate::ast::string_literal::StringLiteral;
|
||||||
use crate::source_range::SourceRange;
|
use crate::source_range::SourceRange;
|
||||||
|
|
||||||
@ -12,6 +13,7 @@ pub enum Expression {
|
|||||||
Binary(BinaryExpression),
|
Binary(BinaryExpression),
|
||||||
Negative(NegativeExpression),
|
Negative(NegativeExpression),
|
||||||
Call(Call),
|
Call(Call),
|
||||||
|
Path(Path),
|
||||||
Identifier(Identifier),
|
Identifier(Identifier),
|
||||||
Integer(IntegerLiteral),
|
Integer(IntegerLiteral),
|
||||||
Double(DoubleLiteral),
|
Double(DoubleLiteral),
|
||||||
@ -24,6 +26,7 @@ impl Expression {
|
|||||||
Expression::Binary(binary_expression) => binary_expression.node_id(),
|
Expression::Binary(binary_expression) => binary_expression.node_id(),
|
||||||
Expression::Negative(negative_expression) => negative_expression.node_id(),
|
Expression::Negative(negative_expression) => negative_expression.node_id(),
|
||||||
Expression::Call(call) => call.node_id(),
|
Expression::Call(call) => call.node_id(),
|
||||||
|
Expression::Path(path) => path.node_id(),
|
||||||
Expression::Identifier(identifier) => identifier.node_id(),
|
Expression::Identifier(identifier) => identifier.node_id(),
|
||||||
Expression::Integer(integer_literal) => integer_literal.node_id(),
|
Expression::Integer(integer_literal) => integer_literal.node_id(),
|
||||||
Expression::Double(double_literal) => double_literal.node_id(),
|
Expression::Double(double_literal) => double_literal.node_id(),
|
||||||
@ -36,6 +39,7 @@ impl Expression {
|
|||||||
Expression::Binary(binary_expression) => binary_expression.source_range(),
|
Expression::Binary(binary_expression) => binary_expression.source_range(),
|
||||||
Expression::Negative(negative_expression) => negative_expression.source_range(),
|
Expression::Negative(negative_expression) => negative_expression.source_range(),
|
||||||
Expression::Call(call) => call.source_range(),
|
Expression::Call(call) => call.source_range(),
|
||||||
|
Expression::Path(path) => path.source_range(),
|
||||||
Expression::Identifier(identifier) => identifier.source_range(),
|
Expression::Identifier(identifier) => identifier.source_range(),
|
||||||
Expression::Integer(integer_literal) => integer_literal.source_range(),
|
Expression::Integer(integer_literal) => integer_literal.source_range(),
|
||||||
Expression::Double(double_literal) => double_literal.source_range(),
|
Expression::Double(double_literal) => double_literal.source_range(),
|
||||||
|
|||||||
@ -16,6 +16,7 @@ pub mod integer_literal;
|
|||||||
pub mod let_statement;
|
pub mod let_statement;
|
||||||
pub mod negative_expression;
|
pub mod negative_expression;
|
||||||
pub mod parameter;
|
pub mod parameter;
|
||||||
|
pub mod path;
|
||||||
pub mod statement;
|
pub mod statement;
|
||||||
pub mod string_literal;
|
pub mod string_literal;
|
||||||
pub mod type_use;
|
pub mod type_use;
|
||||||
|
|||||||
43
dmc-lib/src/ast/path.rs
Normal file
43
dmc-lib/src/ast/path.rs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
use crate::ast::NodeId;
|
||||||
|
use crate::ast::expression::Expression;
|
||||||
|
use crate::ast::identifier::Identifier;
|
||||||
|
use crate::source_range::SourceRange;
|
||||||
|
|
||||||
|
pub struct Path {
|
||||||
|
node_id: NodeId,
|
||||||
|
source_range: SourceRange,
|
||||||
|
base: Box<Expression>,
|
||||||
|
identifier: Box<Identifier>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Path {
|
||||||
|
pub fn new(
|
||||||
|
node_id: NodeId,
|
||||||
|
source_range: SourceRange,
|
||||||
|
base: Expression,
|
||||||
|
identifier: Identifier,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
node_id,
|
||||||
|
source_range,
|
||||||
|
base: base.into(),
|
||||||
|
identifier: identifier.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn node_id(&self) -> NodeId {
|
||||||
|
self.node_id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn source_range(&self) -> &SourceRange {
|
||||||
|
&self.source_range
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn base(&self) -> &Expression {
|
||||||
|
&self.base
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn identifier(&self) -> &Identifier {
|
||||||
|
&self.identifier
|
||||||
|
}
|
||||||
|
}
|
||||||
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;
|
||||||
@ -104,7 +105,10 @@ impl SyntheticFunctionSession {
|
|||||||
Statement::Let(let_statement) => {
|
Statement::Let(let_statement) => {
|
||||||
let ir_variable_info = IrVariableInfo::new(
|
let ir_variable_info = IrVariableInfo::new(
|
||||||
let_statement.declared_name_owned(),
|
let_statement.declared_name_owned(),
|
||||||
to_ir_type_info(self.ctx.get_type_info_for_node(let_statement.node_id())),
|
to_ir_type_info(
|
||||||
|
&self.ctx,
|
||||||
|
self.ctx.get_type_info_for_node(let_statement.node_id()),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
let ir_stack_frame_variable_id = self
|
let ir_stack_frame_variable_id = self
|
||||||
.env
|
.env
|
||||||
|
|||||||
@ -70,7 +70,7 @@ pub fn lower_to_ir_synthetic_function(
|
|||||||
let type_info_id =
|
let type_info_id =
|
||||||
session.ctx.nodes_to_type_infos()[&expression_statement.expression().node_id()];
|
session.ctx.nodes_to_type_infos()[&expression_statement.expression().node_id()];
|
||||||
let type_info = &session.ctx.type_infos()[type_info_id];
|
let type_info = &session.ctx.type_infos()[type_info_id];
|
||||||
return_type_info_to_ir_type_info(type_info)
|
return_type_info_to_ir_type_info(session.ctx(), type_info)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -275,7 +275,7 @@ fn lower_to_ir_function(function: &Function, ctx: &AnalysisContext) -> IrFunctio
|
|||||||
storage_env.take_parameters(),
|
storage_env.take_parameters(),
|
||||||
storage_env.ir_stack_frame_variables.clone(),
|
storage_env.ir_stack_frame_variables.clone(),
|
||||||
storage_env.ir_free_variables.clone(),
|
storage_env.ir_free_variables.clone(),
|
||||||
return_type_info_to_ir_type_info(return_type_info),
|
return_type_info_to_ir_type_info(ctx, return_type_info),
|
||||||
blocks,
|
blocks,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -290,7 +290,7 @@ fn lower_to_ir_parameters(
|
|||||||
let parameter_type_info = &ctx.type_infos()[parameter_type_info_id];
|
let parameter_type_info = &ctx.type_infos()[parameter_type_info_id];
|
||||||
fn_ctx.storage_env_mut().new_parameter_for(
|
fn_ctx.storage_env_mut().new_parameter_for(
|
||||||
parameter.declared_name(),
|
parameter.declared_name(),
|
||||||
to_ir_type_info(parameter_type_info),
|
to_ir_type_info(ctx, parameter_type_info),
|
||||||
ctx.nodes_to_symbols()[¶meter.node_id()],
|
ctx.nodes_to_symbols()[¶meter.node_id()],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -349,7 +349,7 @@ fn lower_to_ir_let_statement(
|
|||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
fn_ctx.storage_env_mut().new_free_variable_for(
|
fn_ctx.storage_env_mut().new_free_variable_for(
|
||||||
let_statement.declared_name(),
|
let_statement.declared_name(),
|
||||||
to_ir_type_info(type_info),
|
to_ir_type_info(ctx, type_info),
|
||||||
symbol_id,
|
symbol_id,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
@ -384,7 +384,7 @@ fn lower_to_ir_expression_statement(
|
|||||||
let result_type_info = &ctx.type_infos()[result_type_info_id];
|
let result_type_info = &ctx.type_infos()[result_type_info_id];
|
||||||
let t_var_ir_variable_id = fn_ctx
|
let t_var_ir_variable_id = fn_ctx
|
||||||
.storage_env_mut()
|
.storage_env_mut()
|
||||||
.new_t_var(to_ir_type_info(result_type_info));
|
.new_t_var(to_ir_type_info(ctx, result_type_info));
|
||||||
|
|
||||||
let ir_statement = IrStatement::Assign(IrAssign::new(t_var_ir_variable_id, ir_operation));
|
let ir_statement = IrStatement::Assign(IrAssign::new(t_var_ir_variable_id, ir_operation));
|
||||||
fn_ctx.current_block_statements.push(ir_statement);
|
fn_ctx.current_block_statements.push(ir_statement);
|
||||||
@ -439,6 +439,9 @@ fn lower_expression_to_ir_operation(
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
Expression::Call(call) => IrOperation::Call(lower_to_ir_call(call, ctx, fn_ctx)),
|
Expression::Call(call) => IrOperation::Call(lower_to_ir_call(call, ctx, fn_ctx)),
|
||||||
|
Expression::Path(_path) => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
Expression::Identifier(identifier) => {
|
Expression::Identifier(identifier) => {
|
||||||
let identifier_symbol_id = ctx.nodes_to_symbols()[&identifier.node_id()];
|
let identifier_symbol_id = ctx.nodes_to_symbols()[&identifier.node_id()];
|
||||||
let identifier_ir_variable =
|
let identifier_ir_variable =
|
||||||
@ -479,7 +482,7 @@ fn lower_expression_to_ir_expression(
|
|||||||
let result_type_info = &ctx.type_infos()[result_type_info_id];
|
let result_type_info = &ctx.type_infos()[result_type_info_id];
|
||||||
let destination_ir_variable = fn_ctx
|
let destination_ir_variable = fn_ctx
|
||||||
.storage_env_mut()
|
.storage_env_mut()
|
||||||
.new_t_var(to_ir_type_info(result_type_info));
|
.new_t_var(to_ir_type_info(ctx, result_type_info));
|
||||||
|
|
||||||
// make assign statement to destination temp var
|
// make assign statement to destination temp var
|
||||||
let ir_assign = IrAssign::new(destination_ir_variable.clone(), ir_operation);
|
let ir_assign = IrAssign::new(destination_ir_variable.clone(), ir_operation);
|
||||||
@ -503,7 +506,7 @@ fn lower_expression_to_ir_expression(
|
|||||||
let result_type_info = &ctx.type_infos()[result_type_info_id];
|
let result_type_info = &ctx.type_infos()[result_type_info_id];
|
||||||
let destination_ir_variable = fn_ctx
|
let destination_ir_variable = fn_ctx
|
||||||
.storage_env_mut()
|
.storage_env_mut()
|
||||||
.new_t_var(to_ir_type_info(result_type_info));
|
.new_t_var(to_ir_type_info(ctx, result_type_info));
|
||||||
let ir_assign = IrAssign::new(destination_ir_variable.clone(), ir_operation);
|
let ir_assign = IrAssign::new(destination_ir_variable.clone(), ir_operation);
|
||||||
|
|
||||||
// push the statement which does the multiply by negative one
|
// push the statement which does the multiply by negative one
|
||||||
@ -521,7 +524,7 @@ fn lower_expression_to_ir_expression(
|
|||||||
let return_type_info = &ctx.type_infos()[return_type_info_id];
|
let return_type_info = &ctx.type_infos()[return_type_info_id];
|
||||||
let t_var_ir_variable = fn_ctx
|
let t_var_ir_variable = fn_ctx
|
||||||
.storage_env_mut()
|
.storage_env_mut()
|
||||||
.new_t_var(to_ir_type_info(return_type_info));
|
.new_t_var(to_ir_type_info(ctx, return_type_info));
|
||||||
|
|
||||||
// assign call to temp var, return temp var expression
|
// assign call to temp var, return temp var expression
|
||||||
let ir_operation = IrOperation::Call(ir_call);
|
let ir_operation = IrOperation::Call(ir_call);
|
||||||
@ -533,6 +536,9 @@ fn lower_expression_to_ir_expression(
|
|||||||
// return an expression referencing the temp var
|
// return an expression referencing the temp var
|
||||||
IrExpression::Variable(t_var_ir_variable)
|
IrExpression::Variable(t_var_ir_variable)
|
||||||
}
|
}
|
||||||
|
Expression::Path(_path) => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
Expression::Identifier(identifier) => {
|
Expression::Identifier(identifier) => {
|
||||||
let rhs_symbol_id = ctx.nodes_to_symbols()[&identifier.node_id()];
|
let rhs_symbol_id = ctx.nodes_to_symbols()[&identifier.node_id()];
|
||||||
if let Some(rhs_ir_variable) =
|
if let Some(rhs_ir_variable) =
|
||||||
@ -565,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,
|
||||||
|
|||||||
@ -1,24 +1,35 @@
|
|||||||
use crate::ir::ir_type_info::IrTypeInfo;
|
use crate::ir::ir_type_info::IrTypeInfo;
|
||||||
|
use crate::semantic_analysis::analysis_context::AnalysisContext;
|
||||||
use crate::semantic_analysis::type_info::TypeInfo;
|
use crate::semantic_analysis::type_info::TypeInfo;
|
||||||
|
|
||||||
pub fn to_ir_type_info(sa_type_info: &TypeInfo) -> IrTypeInfo {
|
pub fn to_ir_type_info(ctx: &AnalysisContext, sa_type_info: &TypeInfo) -> IrTypeInfo {
|
||||||
match sa_type_info {
|
match sa_type_info {
|
||||||
TypeInfo::String => IrTypeInfo::String,
|
TypeInfo::Instance(instance_type_info) => {
|
||||||
|
let symbol = &ctx.symbols()[instance_type_info.class_symbol_id()];
|
||||||
|
if symbol.unwrap_class_symbol().fqn() == "core::String" {
|
||||||
|
IrTypeInfo::String
|
||||||
|
} else {
|
||||||
|
todo!("Non-string Instance types")
|
||||||
|
}
|
||||||
|
}
|
||||||
TypeInfo::Int => IrTypeInfo::Int,
|
TypeInfo::Int => IrTypeInfo::Int,
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn return_type_info_to_ir_type_info(return_type_info: &TypeInfo) -> Option<IrTypeInfo> {
|
pub fn return_type_info_to_ir_type_info(
|
||||||
|
ctx: &AnalysisContext,
|
||||||
|
return_type_info: &TypeInfo,
|
||||||
|
) -> Option<IrTypeInfo> {
|
||||||
match return_type_info {
|
match return_type_info {
|
||||||
TypeInfo::String | TypeInfo::Int | TypeInfo::Double => {
|
TypeInfo::Instance(_) | TypeInfo::Int | TypeInfo::Double => {
|
||||||
Some(to_ir_type_info(return_type_info))
|
Some(to_ir_type_info(ctx, return_type_info))
|
||||||
}
|
}
|
||||||
TypeInfo::Void => None,
|
TypeInfo::Void => None,
|
||||||
_ => panic!(),
|
_ => panic!("BUG! Unknown return_type_info: {:?}", return_type_info),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,7 @@ use crate::ast::integer_literal::IntegerLiteral;
|
|||||||
use crate::ast::let_statement::LetStatement;
|
use crate::ast::let_statement::LetStatement;
|
||||||
use crate::ast::negative_expression::NegativeExpression;
|
use crate::ast::negative_expression::NegativeExpression;
|
||||||
use crate::ast::parameter::Parameter;
|
use crate::ast::parameter::Parameter;
|
||||||
|
use crate::ast::path::Path;
|
||||||
use crate::ast::statement::Statement;
|
use crate::ast::statement::Statement;
|
||||||
use crate::ast::string_literal::StringLiteral;
|
use crate::ast::string_literal::StringLiteral;
|
||||||
use crate::ast::type_use::TypeUse;
|
use crate::ast::type_use::TypeUse;
|
||||||
@ -166,21 +167,6 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn advance_until(&mut self, token_kinds: &[TokenKind]) {
|
|
||||||
while self.current.is_some() {
|
|
||||||
match &self.current {
|
|
||||||
None => {
|
|
||||||
// reached eoi
|
|
||||||
}
|
|
||||||
Some(current) => {
|
|
||||||
if token_kinds.contains(¤t.kind()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn advance(&mut self) {
|
fn advance(&mut self) {
|
||||||
fn fetch(lexer: &mut Lexer, diagnostics: &mut Diagnostics) -> Option<Token> {
|
fn fetch(lexer: &mut Lexer, diagnostics: &mut Diagnostics) -> Option<Token> {
|
||||||
let mut maybe_token: Option<Token> = None;
|
let mut maybe_token: Option<Token> = None;
|
||||||
@ -1038,6 +1024,21 @@ impl<'a> Parser<'a> {
|
|||||||
TokenKind::LeftParentheses => {
|
TokenKind::LeftParentheses => {
|
||||||
expression = Expression::Call(self.call(expression));
|
expression = Expression::Call(self.call(expression));
|
||||||
}
|
}
|
||||||
|
TokenKind::Dot => {
|
||||||
|
self.advance(); // .
|
||||||
|
if let Some(identifier) = self.identifier() {
|
||||||
|
let source_range = SourceRange::new(
|
||||||
|
expression.source_range().start(),
|
||||||
|
identifier.source_range().end(),
|
||||||
|
);
|
||||||
|
expression = Expression::Path(Path::new(
|
||||||
|
self.next_node_id(),
|
||||||
|
source_range,
|
||||||
|
expression,
|
||||||
|
identifier,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => break,
|
_ => break,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1047,6 +1048,18 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn identifier(&mut self) -> Option<Identifier> {
|
||||||
|
if let Some(identifier_token) = self.expect_advance(TokenKind::Identifier) {
|
||||||
|
Some(Identifier::new(
|
||||||
|
self.next_node_id(),
|
||||||
|
self.token_text(&identifier_token),
|
||||||
|
SourceRange::new(identifier_token.start(), identifier_token.end()),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn expression_base(&mut self) -> Option<Expression> {
|
fn expression_base(&mut self) -> Option<Expression> {
|
||||||
let current = match self.current.as_ref() {
|
let current = match self.current.as_ref() {
|
||||||
Some(current) => current,
|
Some(current) => current,
|
||||||
@ -1350,6 +1363,21 @@ mod smoke_tests {
|
|||||||
fn class_with_generic_param() {
|
fn class_with_generic_param() {
|
||||||
smoke_test("class Foo<T> end");
|
smoke_test("class Foo<T> end");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn single_member_access() {
|
||||||
|
smoke_test("fn main(foo: Foo) foo.bar end")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn double_member_access() {
|
||||||
|
smoke_test("fn main(foo: Foo) foo.bar.baz end")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn call_member() {
|
||||||
|
smoke_test("fn main(s: String) s.len() end")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -9,11 +9,10 @@ use crate::ast::function::Function;
|
|||||||
use crate::ast::identifier::Identifier;
|
use crate::ast::identifier::Identifier;
|
||||||
use crate::ast::let_statement::LetStatement;
|
use crate::ast::let_statement::LetStatement;
|
||||||
use crate::ast::negative_expression::NegativeExpression;
|
use crate::ast::negative_expression::NegativeExpression;
|
||||||
|
use crate::ast::path::Path;
|
||||||
use crate::ast::statement::Statement;
|
use crate::ast::statement::Statement;
|
||||||
use crate::semantic_analysis::analysis_context::AnalysisContext;
|
use crate::semantic_analysis::analysis_context::AnalysisContext;
|
||||||
|
|
||||||
/// N.b.: the parent_scope_id refers to the **parent** of the compilation unit scope, and must
|
|
||||||
/// be a valid (non-panicking) index of `scopes`.
|
|
||||||
pub fn collect_scopes_compilation_unit(
|
pub fn collect_scopes_compilation_unit(
|
||||||
compilation_unit: &CompilationUnit,
|
compilation_unit: &CompilationUnit,
|
||||||
ctx: &mut AnalysisContext,
|
ctx: &mut AnalysisContext,
|
||||||
@ -112,6 +111,9 @@ fn collect_scopes_expression(expression: &Expression, ctx: &mut AnalysisContext)
|
|||||||
Expression::Call(call) => {
|
Expression::Call(call) => {
|
||||||
collect_scopes_call(call, ctx);
|
collect_scopes_call(call, ctx);
|
||||||
}
|
}
|
||||||
|
Expression::Path(path) => {
|
||||||
|
collect_scopes_path(path, ctx);
|
||||||
|
}
|
||||||
Expression::Identifier(identifier) => {
|
Expression::Identifier(identifier) => {
|
||||||
collect_scopes_identifier(identifier, ctx);
|
collect_scopes_identifier(identifier, ctx);
|
||||||
}
|
}
|
||||||
@ -143,6 +145,11 @@ fn collect_scopes_call(call: &Call, ctx: &mut AnalysisContext) {
|
|||||||
collect_scopes_expression(call.callee(), ctx);
|
collect_scopes_expression(call.callee(), ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn collect_scopes_path(path: &Path, ctx: &mut AnalysisContext) {
|
||||||
|
collect_scopes_expression(path.base(), ctx);
|
||||||
|
// do not need to collect a scope for the identifier, since that is looked up via the base
|
||||||
|
}
|
||||||
|
|
||||||
fn collect_scopes_identifier(identifier: &Identifier, ctx: &mut AnalysisContext) {
|
fn collect_scopes_identifier(identifier: &Identifier, ctx: &mut AnalysisContext) {
|
||||||
ctx.associate_node_to_current_scope(identifier.node_id());
|
ctx.associate_node_to_current_scope(identifier.node_id());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -21,23 +21,30 @@ fn collect_types_function(function: &Function, ctx: &mut AnalysisContext) {
|
|||||||
|
|
||||||
let return_type_info = match function.return_type() {
|
let return_type_info = match function.return_type() {
|
||||||
None => TypeInfo::Void,
|
None => TypeInfo::Void,
|
||||||
Some(type_use) => declared_name_to_type_info(type_use.declared_name()),
|
Some(type_use) => declared_name_to_type_info(ctx, type_use.declared_name()),
|
||||||
};
|
};
|
||||||
let return_type_info_id = ctx.insert_type_info(return_type_info);
|
let return_type_info_id = ctx.insert_type_info(return_type_info);
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
ctx.associate_node_and_symbol_to_type(function.node_id(), function_type_info_id);
|
ctx.associate_node_and_symbol_to_type(function.node_id(), function_type_info_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn declared_name_to_type_info(declared_name: &str) -> TypeInfo {
|
fn declared_name_to_type_info(ctx: &AnalysisContext, declared_name: &str) -> TypeInfo {
|
||||||
match declared_name {
|
match declared_name {
|
||||||
"Any" => TypeInfo::Any,
|
"Any" => TypeInfo::Any,
|
||||||
"String" => TypeInfo::String,
|
"String" => {
|
||||||
|
// todo: resolve this like a normal instance type
|
||||||
|
let symbol_id = ctx
|
||||||
|
.find_symbol_by_fqn("core::String")
|
||||||
|
.expect("Missing core::String from ctx");
|
||||||
|
ctx.type_infos()[symbol_id].clone()
|
||||||
|
}
|
||||||
"Int" => TypeInfo::Int,
|
"Int" => TypeInfo::Int,
|
||||||
"Double" => TypeInfo::Double,
|
"Double" => TypeInfo::Double,
|
||||||
"Void" => TypeInfo::Void,
|
"Void" => TypeInfo::Void,
|
||||||
@ -51,7 +58,7 @@ fn collect_and_get_parameter_type_info_ids(
|
|||||||
) -> Vec<TypeInfoId> {
|
) -> Vec<TypeInfoId> {
|
||||||
let mut parameter_type_info_ids: Vec<TypeInfoId> = Vec::new();
|
let mut parameter_type_info_ids: Vec<TypeInfoId> = Vec::new();
|
||||||
for parameter in parameters {
|
for parameter in parameters {
|
||||||
let type_info = declared_name_to_type_info(parameter.type_use().declared_name());
|
let type_info = declared_name_to_type_info(ctx, parameter.type_use().declared_name());
|
||||||
let type_info_id = ctx.insert_type_info(type_info);
|
let type_info_id = ctx.insert_type_info(type_info);
|
||||||
parameter_type_info_ids.push(type_info_id);
|
parameter_type_info_ids.push(type_info_id);
|
||||||
|
|
||||||
@ -66,12 +73,13 @@ fn collect_types_extern_function(extern_function: &ExternFunction, ctx: &mut Ana
|
|||||||
collect_and_get_parameter_type_info_ids(extern_function.parameters(), ctx);
|
collect_and_get_parameter_type_info_ids(extern_function.parameters(), ctx);
|
||||||
|
|
||||||
let return_type_info =
|
let return_type_info =
|
||||||
declared_name_to_type_info(extern_function.return_type().declared_name());
|
declared_name_to_type_info(ctx, extern_function.return_type().declared_name());
|
||||||
let return_type_info_id = ctx.insert_type_info(return_type_info);
|
let return_type_info_id = ctx.insert_type_info(return_type_info);
|
||||||
|
|
||||||
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);
|
||||||
|
|||||||
@ -10,6 +10,7 @@ use crate::ast::identifier::Identifier;
|
|||||||
use crate::ast::let_statement::LetStatement;
|
use crate::ast::let_statement::LetStatement;
|
||||||
use crate::ast::negative_expression::NegativeExpression;
|
use crate::ast::negative_expression::NegativeExpression;
|
||||||
use crate::ast::parameter::Parameter;
|
use crate::ast::parameter::Parameter;
|
||||||
|
use crate::ast::path::Path;
|
||||||
use crate::ast::statement::Statement;
|
use crate::ast::statement::Statement;
|
||||||
use crate::diagnostic::Diagnostics;
|
use crate::diagnostic::Diagnostics;
|
||||||
use crate::semantic_analysis::analysis_context::AnalysisContext;
|
use crate::semantic_analysis::analysis_context::AnalysisContext;
|
||||||
@ -195,6 +196,9 @@ fn resolve_names_expression(
|
|||||||
Expression::Call(call) => {
|
Expression::Call(call) => {
|
||||||
resolve_names_call(call, ctx, diagnostics, phase);
|
resolve_names_call(call, ctx, diagnostics, phase);
|
||||||
}
|
}
|
||||||
|
Expression::Path(path) => {
|
||||||
|
resolve_names_path(path, ctx, diagnostics, phase);
|
||||||
|
}
|
||||||
Expression::Identifier(identifier) => {
|
Expression::Identifier(identifier) => {
|
||||||
resolve_names_identifier(identifier, ctx, diagnostics, phase);
|
resolve_names_identifier(identifier, ctx, diagnostics, phase);
|
||||||
}
|
}
|
||||||
@ -235,6 +239,18 @@ fn resolve_names_call(
|
|||||||
resolve_names_expression(call.callee(), ctx, diagnostics, phase);
|
resolve_names_expression(call.callee(), ctx, diagnostics, phase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_names_path(
|
||||||
|
path: &Path,
|
||||||
|
ctx: &mut AnalysisContext,
|
||||||
|
diagnostics: &mut Diagnostics,
|
||||||
|
phase: ExpressionResolutionPhase,
|
||||||
|
) {
|
||||||
|
// resolve base (left part)
|
||||||
|
resolve_names_expression(path.base(), ctx, diagnostics, phase);
|
||||||
|
// We cannot yet determine if the path's identifier (right part) is a valid memeber of the left,
|
||||||
|
// since we don't have type information yet. Therefore, it is deferred until type-checking.
|
||||||
|
}
|
||||||
|
|
||||||
fn resolve_names_identifier(
|
fn resolve_names_identifier(
|
||||||
identifier: &Identifier,
|
identifier: &Identifier,
|
||||||
ctx: &mut AnalysisContext,
|
ctx: &mut AnalysisContext,
|
||||||
|
|||||||
@ -10,14 +10,16 @@ use crate::ast::identifier::Identifier;
|
|||||||
use crate::ast::let_statement::LetStatement;
|
use crate::ast::let_statement::LetStatement;
|
||||||
use crate::ast::negative_expression::NegativeExpression;
|
use crate::ast::negative_expression::NegativeExpression;
|
||||||
use crate::ast::parameter::Parameter;
|
use crate::ast::parameter::Parameter;
|
||||||
|
use crate::ast::path::Path;
|
||||||
use crate::ast::statement::Statement;
|
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);
|
||||||
|
|
||||||
@ -109,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,
|
||||||
@ -138,6 +142,9 @@ fn resolve_types_expression(
|
|||||||
Expression::Call(call) => {
|
Expression::Call(call) => {
|
||||||
resolve_types_call(call, ctx, diagnostics);
|
resolve_types_call(call, ctx, diagnostics);
|
||||||
}
|
}
|
||||||
|
Expression::Path(path) => {
|
||||||
|
resolve_types_path(path, ctx, diagnostics);
|
||||||
|
}
|
||||||
Expression::Identifier(identifier) => {
|
Expression::Identifier(identifier) => {
|
||||||
resolve_types_identifier(identifier, ctx);
|
resolve_types_identifier(identifier, ctx);
|
||||||
}
|
}
|
||||||
@ -148,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(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -164,9 +177,9 @@ fn resolve_types_binary_expression(
|
|||||||
let lhs_type_info = ctx.get_type_info_for_node(binary_expression.lhs().node_id());
|
let lhs_type_info = ctx.get_type_info_for_node(binary_expression.lhs().node_id());
|
||||||
let rhs_type_info = ctx.get_type_info_for_node(binary_expression.rhs().node_id());
|
let rhs_type_info = ctx.get_type_info_for_node(binary_expression.rhs().node_id());
|
||||||
|
|
||||||
if are_binary_op_compatible(binary_expression.op(), lhs_type_info, rhs_type_info) {
|
if are_binary_op_compatible(ctx, binary_expression.op(), lhs_type_info, rhs_type_info) {
|
||||||
let result_type_info =
|
let result_type_info =
|
||||||
binary_op_result(binary_expression.op(), lhs_type_info, rhs_type_info);
|
binary_op_result(ctx, binary_expression.op(), lhs_type_info, rhs_type_info);
|
||||||
ctx.insert_type_info_for_node(result_type_info, binary_expression.node_id());
|
ctx.insert_type_info_for_node(result_type_info, binary_expression.node_id());
|
||||||
} else {
|
} else {
|
||||||
let op_name = match binary_expression.op() {
|
let op_name = match binary_expression.op() {
|
||||||
@ -181,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,
|
||||||
@ -211,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(),
|
||||||
@ -255,25 +271,26 @@ 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();
|
||||||
for i in 0..parameter_type_ids.len() {
|
if arguments.len() == parameter_type_ids.len() {
|
||||||
let argument_type_info = ctx.get_type_info_for_node(arguments[i].node_id());
|
for i in 0..parameter_type_ids.len() {
|
||||||
let parameter_type_info = ctx.get_type_info_by_id(parameter_type_ids[i]); // n.b. different method
|
let argument_type_info = ctx.get_type_info_for_node(arguments[i].node_id());
|
||||||
if !can_assign_right_to_left(parameter_type_info, argument_type_info) {
|
let parameter_type_info = ctx.get_type_info_by_id(parameter_type_ids[i]); // n.b. different method
|
||||||
let message = format!(
|
if !can_assign_right_to_left(parameter_type_info, argument_type_info, ctx) {
|
||||||
"Incompatible types: cannot assign {} to {}",
|
let parameter_type_name = get_name_for_type(parameter_type_info, ctx);
|
||||||
argument_type_info, parameter_type_info
|
let argument_type_name = get_name_for_type(argument_type_info, ctx);
|
||||||
);
|
let message = format!(
|
||||||
let diagnostic = Diagnostic::new(
|
"Incompatible types: cannot assign {} to {}",
|
||||||
&message,
|
argument_type_name, parameter_type_name
|
||||||
arguments[i].source_range().start(),
|
);
|
||||||
arguments[i].source_range().end(),
|
let diagnostic = Diagnostic::new(
|
||||||
);
|
&message,
|
||||||
diagnostics.push(diagnostic);
|
arguments[i].source_range().start(),
|
||||||
|
arguments[i].source_range().end(),
|
||||||
// do not push an error type because the result of the whole call is the return type
|
);
|
||||||
// of the function
|
diagnostics.push(diagnostic);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,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(),
|
||||||
@ -300,6 +318,73 @@ fn resolve_types_call(call: &Call, ctx: &mut AnalysisContext, diagnostics: &mut
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_types_path(path: &Path, ctx: &mut AnalysisContext, diagnostics: &mut Diagnostics) {
|
||||||
|
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::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!(
|
||||||
|
"Impossible operation: type {} does not have members to access",
|
||||||
|
base_type_name
|
||||||
|
);
|
||||||
|
let diagnostic = Diagnostic::new(
|
||||||
|
&message,
|
||||||
|
path.source_range().start(),
|
||||||
|
path.source_range().end(),
|
||||||
|
);
|
||||||
|
diagnostics.push(diagnostic);
|
||||||
|
|
||||||
|
// bubble it up
|
||||||
|
ctx.insert_type_info_for_node(TypeInfo::__Error, path.node_id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn resolve_types_identifier(identifier: &Identifier, ctx: &mut AnalysisContext) {
|
fn resolve_types_identifier(identifier: &Identifier, ctx: &mut AnalysisContext) {
|
||||||
let type_info_id = ctx.lookup_type_for_node_via_symbol(identifier.node_id());
|
let type_info_id = ctx.lookup_type_for_node_via_symbol(identifier.node_id());
|
||||||
ctx.associate_node_to_type(identifier.node_id(), type_info_id);
|
ctx.associate_node_to_type(identifier.node_id(), type_info_id);
|
||||||
|
|||||||
@ -1,13 +1,19 @@
|
|||||||
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(
|
||||||
|
ctx: &AnalysisContext,
|
||||||
|
op: &BinaryOperation,
|
||||||
|
left: &TypeInfo,
|
||||||
|
right: &TypeInfo,
|
||||||
|
) -> bool {
|
||||||
match op {
|
match op {
|
||||||
BinaryOperation::Multiply
|
BinaryOperation::Multiply
|
||||||
| BinaryOperation::Divide
|
| BinaryOperation::Divide
|
||||||
| BinaryOperation::Modulo
|
| BinaryOperation::Modulo
|
||||||
| BinaryOperation::Subtract => are_numbers(left, right),
|
| BinaryOperation::Subtract => are_numbers(left, right),
|
||||||
BinaryOperation::Add => are_numbers_or_strings(left, right),
|
BinaryOperation::Add => are_numbers_or_strings(ctx, left, right),
|
||||||
BinaryOperation::LeftShift
|
BinaryOperation::LeftShift
|
||||||
| BinaryOperation::RightShift
|
| BinaryOperation::RightShift
|
||||||
| BinaryOperation::BitwiseAnd
|
| BinaryOperation::BitwiseAnd
|
||||||
@ -24,21 +30,35 @@ fn are_numbers(t0: &TypeInfo, t1: &TypeInfo) -> bool {
|
|||||||
is_number(t0) && is_number(t1)
|
is_number(t0) && is_number(t1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn are_numbers_or_strings(t0: &TypeInfo, t1: &TypeInfo) -> bool {
|
fn are_numbers_or_strings(ctx: &AnalysisContext, t0: &TypeInfo, t1: &TypeInfo) -> bool {
|
||||||
(is_number(t0) || matches!(t0, TypeInfo::String))
|
(is_number(t0) || is_string(ctx, t0)) && (is_number(t1) || is_string(ctx, t1))
|
||||||
&& (is_number(t1) || matches!(t1, TypeInfo::String))
|
}
|
||||||
|
|
||||||
|
fn is_string(ctx: &AnalysisContext, type_info: &TypeInfo) -> bool {
|
||||||
|
match type_info {
|
||||||
|
TypeInfo::Instance(instance_type_info) => {
|
||||||
|
let class_symbol = &ctx.symbols()[instance_type_info.class_symbol_id()];
|
||||||
|
class_symbol.unwrap_class_symbol().fqn() == "core::String"
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn are_ints(t0: &TypeInfo, t1: &TypeInfo) -> bool {
|
fn are_ints(t0: &TypeInfo, t1: &TypeInfo) -> bool {
|
||||||
matches!(t0, TypeInfo::Int) && matches!(t1, TypeInfo::Int)
|
matches!(t0, TypeInfo::Int) && matches!(t1, TypeInfo::Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn binary_op_result(op: &BinaryOperation, left: &TypeInfo, right: &TypeInfo) -> TypeInfo {
|
pub fn binary_op_result(
|
||||||
|
ctx: &AnalysisContext,
|
||||||
|
op: &BinaryOperation,
|
||||||
|
left: &TypeInfo,
|
||||||
|
right: &TypeInfo,
|
||||||
|
) -> TypeInfo {
|
||||||
match op {
|
match op {
|
||||||
BinaryOperation::Multiply => numbers_binary_result(left, right),
|
BinaryOperation::Multiply => numbers_binary_result(left, right),
|
||||||
BinaryOperation::Divide => TypeInfo::Double, // okay for now
|
BinaryOperation::Divide => TypeInfo::Double, // okay for now
|
||||||
BinaryOperation::Modulo => numbers_binary_result(left, right), // same properties as multiplication
|
BinaryOperation::Modulo => numbers_binary_result(left, right), // same properties as multiplication
|
||||||
BinaryOperation::Add => add_result(left, right),
|
BinaryOperation::Add => add_result(ctx, left, right),
|
||||||
BinaryOperation::Subtract => numbers_binary_result(left, right),
|
BinaryOperation::Subtract => numbers_binary_result(left, right),
|
||||||
BinaryOperation::LeftShift
|
BinaryOperation::LeftShift
|
||||||
| BinaryOperation::RightShift
|
| BinaryOperation::RightShift
|
||||||
@ -66,22 +86,40 @@ fn numbers_binary_result(left: &TypeInfo, right: &TypeInfo) -> TypeInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_result(left: &TypeInfo, right: &TypeInfo) -> TypeInfo {
|
fn add_result(ctx: &AnalysisContext, left: &TypeInfo, right: &TypeInfo) -> TypeInfo {
|
||||||
match left {
|
match left {
|
||||||
|
TypeInfo::Instance(left_instance_type_info) => {
|
||||||
|
if is_string(ctx, left) {
|
||||||
|
TypeInfo::Instance(left_instance_type_info.clone())
|
||||||
|
} else {
|
||||||
|
todo!("Adding with non-String, non-number types")
|
||||||
|
}
|
||||||
|
}
|
||||||
TypeInfo::Int => match right {
|
TypeInfo::Int => match right {
|
||||||
|
TypeInfo::Instance(right_instance_type_info) => {
|
||||||
|
if is_string(ctx, right) {
|
||||||
|
TypeInfo::Instance(right_instance_type_info.clone())
|
||||||
|
} else {
|
||||||
|
todo!("Adding with non-String, non-number types")
|
||||||
|
}
|
||||||
|
}
|
||||||
TypeInfo::Int => TypeInfo::Int,
|
TypeInfo::Int => TypeInfo::Int,
|
||||||
TypeInfo::Double => TypeInfo::Double,
|
TypeInfo::Double => TypeInfo::Double,
|
||||||
TypeInfo::String => TypeInfo::String,
|
|
||||||
TypeInfo::__Error => TypeInfo::__Error,
|
TypeInfo::__Error => TypeInfo::__Error,
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
},
|
},
|
||||||
TypeInfo::Double => match right {
|
TypeInfo::Double => match right {
|
||||||
|
TypeInfo::Instance(right_instance_type_info) => {
|
||||||
|
if is_string(ctx, right) {
|
||||||
|
TypeInfo::Instance(right_instance_type_info.clone())
|
||||||
|
} else {
|
||||||
|
todo!("Adding with non-String, non-number types")
|
||||||
|
}
|
||||||
|
}
|
||||||
TypeInfo::Int | TypeInfo::Double => TypeInfo::Double,
|
TypeInfo::Int | TypeInfo::Double => TypeInfo::Double,
|
||||||
TypeInfo::String => TypeInfo::String,
|
|
||||||
TypeInfo::__Error => TypeInfo::__Error,
|
TypeInfo::__Error => TypeInfo::__Error,
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
},
|
},
|
||||||
TypeInfo::String => TypeInfo::String,
|
|
||||||
TypeInfo::__Error => TypeInfo::__Error,
|
TypeInfo::__Error => TypeInfo::__Error,
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
@ -103,12 +141,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