Compare commits
No commits in common. "10579e2ad29ced705ccc71f93e3a03e8cc483f8e" and "2dfd8e8a295a44ce87eacfb99533d524b1ff0dcb" have entirely different histories.
10579e2ad2
...
2dfd8e8a29
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -119,13 +119,6 @@ dependencies = [
|
||||
"dvm-lib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dm-core-impl"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"dvm-lib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dm-std-lib"
|
||||
version = "0.1.0"
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
[workspace]
|
||||
resolver = "3"
|
||||
members = ["dm", "dm-core-impl", "dm-std-lib", "dmc-lib", "dvm-lib", "e2e-tests"]
|
||||
members = ["dm", "dm-std-lib", "dmc-lib", "dvm-lib", "e2e-tests"]
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
[package]
|
||||
name = "dm-core-impl"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
dvm-lib = { path = "../dvm-lib" }
|
||||
@ -1,31 +0,0 @@
|
||||
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 {}
|
||||
@ -1,7 +0,0 @@
|
||||
mod error;
|
||||
pub mod print;
|
||||
pub mod string;
|
||||
mod util;
|
||||
|
||||
pub use print::*;
|
||||
pub use string::*;
|
||||
@ -1,56 +0,0 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
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),
|
||||
))),
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
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",
|
||||
}
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
mod ops
|
||||
mod print
|
||||
mod string
|
||||
|
||||
pub use print::*
|
||||
pub use string::*
|
||||
@ -1,6 +0,0 @@
|
||||
pub trait Add
|
||||
type Right = Self
|
||||
type Result = Self
|
||||
|
||||
fn add(right: Self::Right) -> Self::Result
|
||||
end
|
||||
@ -1,3 +0,0 @@
|
||||
pub extern fn print(msg: Any) -> Void
|
||||
|
||||
pub extern fn println(msg: Any) -> Void
|
||||
@ -1,5 +0,0 @@
|
||||
pub extern class String
|
||||
pub extern fn len() -> Long
|
||||
end
|
||||
|
||||
intrinsic impl<T> Add<Right = T> for String end
|
||||
@ -4,14 +4,11 @@ use codespan_reporting::term::{Config, WriteStyle, emit_to_write_style};
|
||||
use dmc_lib::SyntheticFunctionSession;
|
||||
use dmc_lib::constants_table::ConstantsTable;
|
||||
use dmc_lib::diagnostic::Diagnostics;
|
||||
use dmc_lib::intrinsics::add_primitive_symbols_and_type_infos;
|
||||
use dmc_lib::parser::parse_statement;
|
||||
use dvm_lib::vm::constant::{Constant, StringConstant};
|
||||
use dvm_lib::vm::function::Function;
|
||||
use dvm_lib::vm::operand::Operand;
|
||||
use dvm_lib::vm::value::Value;
|
||||
use dvm_lib::vm::{CallStack, DvmContext, loop_instructions, prepare_for_instruction_loop};
|
||||
use std::error::Error;
|
||||
use std::io::{BufRead, Write};
|
||||
|
||||
pub fn repl(
|
||||
@ -29,26 +26,13 @@ pub fn repl(
|
||||
let mut session = SyntheticFunctionSession::new("__repl");
|
||||
|
||||
let analysis_context = session.ctx_mut();
|
||||
let root_scope_id = analysis_context.push_scope("__repl_root_scope");
|
||||
analysis_context.push_scope("__repl_root_scope");
|
||||
analysis_context.push_scope("__repl_function_scope");
|
||||
analysis_context.push_scope("__repl_body_scope");
|
||||
|
||||
add_primitive_symbols_and_type_infos(analysis_context, root_scope_id);
|
||||
|
||||
let mut constants_table = ConstantsTable::new();
|
||||
|
||||
let mut dvm_context = DvmContext::new();
|
||||
|
||||
// todo: move this to a core impl crate
|
||||
fn core_string_len(args: &[Value]) -> Result<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();
|
||||
|
||||
'repl: loop {
|
||||
|
||||
@ -5,7 +5,6 @@ use crate::ast::double_literal::DoubleLiteral;
|
||||
use crate::ast::identifier::Identifier;
|
||||
use crate::ast::integer_literal::IntegerLiteral;
|
||||
use crate::ast::negative_expression::NegativeExpression;
|
||||
use crate::ast::path::Path;
|
||||
use crate::ast::string_literal::StringLiteral;
|
||||
use crate::source_range::SourceRange;
|
||||
|
||||
@ -13,7 +12,6 @@ pub enum Expression {
|
||||
Binary(BinaryExpression),
|
||||
Negative(NegativeExpression),
|
||||
Call(Call),
|
||||
Path(Path),
|
||||
Identifier(Identifier),
|
||||
Integer(IntegerLiteral),
|
||||
Double(DoubleLiteral),
|
||||
@ -26,7 +24,6 @@ impl Expression {
|
||||
Expression::Binary(binary_expression) => binary_expression.node_id(),
|
||||
Expression::Negative(negative_expression) => negative_expression.node_id(),
|
||||
Expression::Call(call) => call.node_id(),
|
||||
Expression::Path(path) => path.node_id(),
|
||||
Expression::Identifier(identifier) => identifier.node_id(),
|
||||
Expression::Integer(integer_literal) => integer_literal.node_id(),
|
||||
Expression::Double(double_literal) => double_literal.node_id(),
|
||||
@ -39,7 +36,6 @@ impl Expression {
|
||||
Expression::Binary(binary_expression) => binary_expression.source_range(),
|
||||
Expression::Negative(negative_expression) => negative_expression.source_range(),
|
||||
Expression::Call(call) => call.source_range(),
|
||||
Expression::Path(path) => path.source_range(),
|
||||
Expression::Identifier(identifier) => identifier.source_range(),
|
||||
Expression::Integer(integer_literal) => integer_literal.source_range(),
|
||||
Expression::Double(double_literal) => double_literal.source_range(),
|
||||
|
||||
@ -16,7 +16,6 @@ pub mod integer_literal;
|
||||
pub mod let_statement;
|
||||
pub mod negative_expression;
|
||||
pub mod parameter;
|
||||
pub mod path;
|
||||
pub mod statement;
|
||||
pub mod string_literal;
|
||||
pub mod type_use;
|
||||
|
||||
@ -1,43 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -1,51 +0,0 @@
|
||||
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,7 +20,6 @@ pub mod constants_table;
|
||||
pub mod diagnostic;
|
||||
mod diagnostic_factories;
|
||||
mod error_codes;
|
||||
pub mod intrinsics;
|
||||
pub mod ir;
|
||||
pub mod lexer;
|
||||
pub mod lowering;
|
||||
@ -105,10 +104,7 @@ impl SyntheticFunctionSession {
|
||||
Statement::Let(let_statement) => {
|
||||
let ir_variable_info = IrVariableInfo::new(
|
||||
let_statement.declared_name_owned(),
|
||||
to_ir_type_info(
|
||||
&self.ctx,
|
||||
self.ctx.get_type_info_for_node(let_statement.node_id()),
|
||||
),
|
||||
to_ir_type_info(self.ctx.get_type_info_for_node(let_statement.node_id())),
|
||||
);
|
||||
let ir_stack_frame_variable_id = self
|
||||
.env
|
||||
|
||||
@ -70,7 +70,7 @@ pub fn lower_to_ir_synthetic_function(
|
||||
let type_info_id =
|
||||
session.ctx.nodes_to_type_infos()[&expression_statement.expression().node_id()];
|
||||
let type_info = &session.ctx.type_infos()[type_info_id];
|
||||
return_type_info_to_ir_type_info(session.ctx(), type_info)
|
||||
return_type_info_to_ir_type_info(type_info)
|
||||
}
|
||||
};
|
||||
|
||||
@ -275,7 +275,7 @@ fn lower_to_ir_function(function: &Function, ctx: &AnalysisContext) -> IrFunctio
|
||||
storage_env.take_parameters(),
|
||||
storage_env.ir_stack_frame_variables.clone(),
|
||||
storage_env.ir_free_variables.clone(),
|
||||
return_type_info_to_ir_type_info(ctx, return_type_info),
|
||||
return_type_info_to_ir_type_info(return_type_info),
|
||||
blocks,
|
||||
)
|
||||
}
|
||||
@ -290,7 +290,7 @@ fn lower_to_ir_parameters(
|
||||
let parameter_type_info = &ctx.type_infos()[parameter_type_info_id];
|
||||
fn_ctx.storage_env_mut().new_parameter_for(
|
||||
parameter.declared_name(),
|
||||
to_ir_type_info(ctx, parameter_type_info),
|
||||
to_ir_type_info(parameter_type_info),
|
||||
ctx.nodes_to_symbols()[¶meter.node_id()],
|
||||
);
|
||||
}
|
||||
@ -349,7 +349,7 @@ fn lower_to_ir_let_statement(
|
||||
.unwrap_or_else(|| {
|
||||
fn_ctx.storage_env_mut().new_free_variable_for(
|
||||
let_statement.declared_name(),
|
||||
to_ir_type_info(ctx, type_info),
|
||||
to_ir_type_info(type_info),
|
||||
symbol_id,
|
||||
)
|
||||
});
|
||||
@ -384,7 +384,7 @@ fn lower_to_ir_expression_statement(
|
||||
let result_type_info = &ctx.type_infos()[result_type_info_id];
|
||||
let t_var_ir_variable_id = fn_ctx
|
||||
.storage_env_mut()
|
||||
.new_t_var(to_ir_type_info(ctx, result_type_info));
|
||||
.new_t_var(to_ir_type_info(result_type_info));
|
||||
|
||||
let ir_statement = IrStatement::Assign(IrAssign::new(t_var_ir_variable_id, ir_operation));
|
||||
fn_ctx.current_block_statements.push(ir_statement);
|
||||
@ -439,9 +439,6 @@ fn lower_expression_to_ir_operation(
|
||||
))
|
||||
}
|
||||
Expression::Call(call) => IrOperation::Call(lower_to_ir_call(call, ctx, fn_ctx)),
|
||||
Expression::Path(_path) => {
|
||||
todo!()
|
||||
}
|
||||
Expression::Identifier(identifier) => {
|
||||
let identifier_symbol_id = ctx.nodes_to_symbols()[&identifier.node_id()];
|
||||
let identifier_ir_variable =
|
||||
@ -482,7 +479,7 @@ fn lower_expression_to_ir_expression(
|
||||
let result_type_info = &ctx.type_infos()[result_type_info_id];
|
||||
let destination_ir_variable = fn_ctx
|
||||
.storage_env_mut()
|
||||
.new_t_var(to_ir_type_info(ctx, result_type_info));
|
||||
.new_t_var(to_ir_type_info(result_type_info));
|
||||
|
||||
// make assign statement to destination temp var
|
||||
let ir_assign = IrAssign::new(destination_ir_variable.clone(), ir_operation);
|
||||
@ -506,7 +503,7 @@ fn lower_expression_to_ir_expression(
|
||||
let result_type_info = &ctx.type_infos()[result_type_info_id];
|
||||
let destination_ir_variable = fn_ctx
|
||||
.storage_env_mut()
|
||||
.new_t_var(to_ir_type_info(ctx, result_type_info));
|
||||
.new_t_var(to_ir_type_info(result_type_info));
|
||||
let ir_assign = IrAssign::new(destination_ir_variable.clone(), ir_operation);
|
||||
|
||||
// push the statement which does the multiply by negative one
|
||||
@ -524,7 +521,7 @@ fn lower_expression_to_ir_expression(
|
||||
let return_type_info = &ctx.type_infos()[return_type_info_id];
|
||||
let t_var_ir_variable = fn_ctx
|
||||
.storage_env_mut()
|
||||
.new_t_var(to_ir_type_info(ctx, return_type_info));
|
||||
.new_t_var(to_ir_type_info(return_type_info));
|
||||
|
||||
// assign call to temp var, return temp var expression
|
||||
let ir_operation = IrOperation::Call(ir_call);
|
||||
@ -536,9 +533,6 @@ fn lower_expression_to_ir_expression(
|
||||
// return an expression referencing the temp var
|
||||
IrExpression::Variable(t_var_ir_variable)
|
||||
}
|
||||
Expression::Path(_path) => {
|
||||
todo!()
|
||||
}
|
||||
Expression::Identifier(identifier) => {
|
||||
let rhs_symbol_id = ctx.nodes_to_symbols()[&identifier.node_id()];
|
||||
if let Some(rhs_ir_variable) =
|
||||
@ -571,29 +565,11 @@ fn lower_to_ir_call(
|
||||
let callee_symbol = &ctx.symbols()[callee_symbol_id];
|
||||
match callee_symbol {
|
||||
Symbol::Function(function_symbol) => {
|
||||
let mut arguments = Vec::new();
|
||||
if function_symbol.is_method() {
|
||||
let this = match call.callee() {
|
||||
Expression::Path(path) => {
|
||||
lower_expression_to_ir_expression(path.base(), ctx, fn_ctx)
|
||||
}
|
||||
Expression::Identifier(identifier) => {
|
||||
// A self object, like:
|
||||
// class Foo
|
||||
// fn bar() "hello" end
|
||||
// fn greet() bar() end
|
||||
// end
|
||||
todo!()
|
||||
}
|
||||
_ => panic!(
|
||||
"Cannot determine method's this expression when not Path or Identifier"
|
||||
),
|
||||
};
|
||||
arguments.push(this);
|
||||
}
|
||||
for expression in call.arguments() {
|
||||
arguments.push(lower_expression_to_ir_expression(expression, ctx, fn_ctx));
|
||||
}
|
||||
let arguments = call
|
||||
.arguments()
|
||||
.iter()
|
||||
.map(|e| lower_expression_to_ir_expression(e, ctx, fn_ctx))
|
||||
.collect();
|
||||
IrCall::new(
|
||||
function_symbol.fqn_owned(),
|
||||
arguments,
|
||||
|
||||
@ -1,35 +1,24 @@
|
||||
use crate::ir::ir_type_info::IrTypeInfo;
|
||||
use crate::semantic_analysis::analysis_context::AnalysisContext;
|
||||
use crate::semantic_analysis::type_info::TypeInfo;
|
||||
|
||||
pub fn to_ir_type_info(ctx: &AnalysisContext, sa_type_info: &TypeInfo) -> IrTypeInfo {
|
||||
pub fn to_ir_type_info(sa_type_info: &TypeInfo) -> IrTypeInfo {
|
||||
match sa_type_info {
|
||||
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::String => IrTypeInfo::String,
|
||||
TypeInfo::Int => IrTypeInfo::Int,
|
||||
TypeInfo::Double => IrTypeInfo::Double,
|
||||
TypeInfo::Void => IrTypeInfo::Void,
|
||||
_ => {
|
||||
panic!("BUG! Unknown sa_type_info: {:?}", sa_type_info);
|
||||
panic!("BUG! Unknown sa_type_info: {}", sa_type_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn return_type_info_to_ir_type_info(
|
||||
ctx: &AnalysisContext,
|
||||
return_type_info: &TypeInfo,
|
||||
) -> Option<IrTypeInfo> {
|
||||
pub fn return_type_info_to_ir_type_info(return_type_info: &TypeInfo) -> Option<IrTypeInfo> {
|
||||
match return_type_info {
|
||||
TypeInfo::Instance(_) | TypeInfo::Int | TypeInfo::Double => {
|
||||
Some(to_ir_type_info(ctx, return_type_info))
|
||||
TypeInfo::String | TypeInfo::Int | TypeInfo::Double => {
|
||||
Some(to_ir_type_info(return_type_info))
|
||||
}
|
||||
TypeInfo::Void => None,
|
||||
_ => panic!("BUG! Unknown return_type_info: {:?}", return_type_info),
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,7 +18,6 @@ use crate::ast::integer_literal::IntegerLiteral;
|
||||
use crate::ast::let_statement::LetStatement;
|
||||
use crate::ast::negative_expression::NegativeExpression;
|
||||
use crate::ast::parameter::Parameter;
|
||||
use crate::ast::path::Path;
|
||||
use crate::ast::statement::Statement;
|
||||
use crate::ast::string_literal::StringLiteral;
|
||||
use crate::ast::type_use::TypeUse;
|
||||
@ -167,6 +166,21 @@ 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 fetch(lexer: &mut Lexer, diagnostics: &mut Diagnostics) -> Option<Token> {
|
||||
let mut maybe_token: Option<Token> = None;
|
||||
@ -1024,21 +1038,6 @@ impl<'a> Parser<'a> {
|
||||
TokenKind::LeftParentheses => {
|
||||
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,
|
||||
}
|
||||
}
|
||||
@ -1048,18 +1047,6 @@ 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> {
|
||||
let current = match self.current.as_ref() {
|
||||
Some(current) => current,
|
||||
@ -1363,21 +1350,6 @@ mod smoke_tests {
|
||||
fn class_with_generic_param() {
|
||||
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)]
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
pub mod helpers;
|
||||
|
||||
use crate::ast::NodeId;
|
||||
use crate::diagnostic::Diagnostic;
|
||||
use crate::diagnostic_factories::symbol_not_found;
|
||||
@ -46,6 +44,14 @@ impl AnalysisContext {
|
||||
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] {
|
||||
&self.symbols
|
||||
}
|
||||
@ -80,11 +86,10 @@ impl AnalysisContext {
|
||||
fqn.join("::")
|
||||
}
|
||||
|
||||
pub fn push_scope(&mut self, _debug_name: &str) -> ScopeId {
|
||||
pub fn push_scope(&mut self, _debug_name: &str) {
|
||||
let scope = Scope::new(self.current_scope_id);
|
||||
self.scopes.push(scope);
|
||||
self.current_scope_id = Some(self.scopes.len() - 1);
|
||||
self.current_scope_id.unwrap()
|
||||
}
|
||||
|
||||
fn get_scope(&self, scope_id: ScopeId) -> &Scope {
|
||||
@ -120,11 +125,6 @@ impl AnalysisContext {
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts the given symbol into the Scope corresponding to the given ScopeId. **This is a low
|
||||
/// level operation and alternate methods should be preferred.**
|
||||
///
|
||||
/// Danger: If a symbol already exists in the corresponding Scope with the same declared name,
|
||||
/// it will be overwritten, leading to dangling symbols/symbol ids.
|
||||
fn insert_symbol_in_scope(&mut self, scope_id: ScopeId, symbol: Symbol) -> SymbolId {
|
||||
// get declared name
|
||||
let declared_name = symbol.declared_name_owned();
|
||||
@ -159,14 +159,14 @@ impl AnalysisContext {
|
||||
&mut self,
|
||||
to_insert: Symbol,
|
||||
scope_id: ScopeId,
|
||||
) -> Result<SymbolId, Diagnostic> {
|
||||
) -> Result<(), Diagnostic> {
|
||||
let scope = self.get_scope(scope_id);
|
||||
if let Some(already_declared) = self.get_symbol_in_scope(scope, to_insert.declared_name()) {
|
||||
Err(symbol_already_declared(already_declared, &to_insert))
|
||||
} else {
|
||||
let symbol_id = self.insert_symbol_in_scope(scope_id, to_insert);
|
||||
self.insert_symbol_in_scope(scope_id, to_insert);
|
||||
// no associated node, so no need to update self.nodes_to_symbols
|
||||
Ok(symbol_id)
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -261,16 +261,12 @@ impl AnalysisContext {
|
||||
.insert(node_id, self.type_infos.len() - 1);
|
||||
}
|
||||
|
||||
pub fn associate_symbol_to_type(&mut self, symbol_id: SymbolId, type_info_id: TypeInfoId) {
|
||||
self.symbols_to_type_infos.insert(symbol_id, type_info_id);
|
||||
}
|
||||
|
||||
pub fn associate_node_and_symbol_to_type(&mut self, node_id: NodeId, type_info_id: TypeInfoId) {
|
||||
let symbol_id = self
|
||||
.nodes_to_symbols
|
||||
.get(&node_id)
|
||||
.expect(&format!("node_id {} has no associated symbol", node_id));
|
||||
self.associate_symbol_to_type(*symbol_id, type_info_id);
|
||||
self.symbols_to_type_infos.insert(*symbol_id, type_info_id);
|
||||
self.nodes_to_type_infos.insert(node_id, type_info_id);
|
||||
}
|
||||
|
||||
@ -309,25 +305,4 @@ impl AnalysisContext {
|
||||
));
|
||||
*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
|
||||
}
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
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."),
|
||||
}
|
||||
}
|
||||
@ -9,10 +9,11 @@ use crate::ast::function::Function;
|
||||
use crate::ast::identifier::Identifier;
|
||||
use crate::ast::let_statement::LetStatement;
|
||||
use crate::ast::negative_expression::NegativeExpression;
|
||||
use crate::ast::path::Path;
|
||||
use crate::ast::statement::Statement;
|
||||
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(
|
||||
compilation_unit: &CompilationUnit,
|
||||
ctx: &mut AnalysisContext,
|
||||
@ -111,9 +112,6 @@ fn collect_scopes_expression(expression: &Expression, ctx: &mut AnalysisContext)
|
||||
Expression::Call(call) => {
|
||||
collect_scopes_call(call, ctx);
|
||||
}
|
||||
Expression::Path(path) => {
|
||||
collect_scopes_path(path, ctx);
|
||||
}
|
||||
Expression::Identifier(identifier) => {
|
||||
collect_scopes_identifier(identifier, ctx);
|
||||
}
|
||||
@ -145,11 +143,6 @@ fn collect_scopes_call(call: &Call, ctx: &mut AnalysisContext) {
|
||||
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) {
|
||||
ctx.associate_node_to_current_scope(identifier.node_id());
|
||||
}
|
||||
|
||||
@ -38,7 +38,6 @@ fn collect_symbols_function(
|
||||
Some(function.declared_name_source_range()),
|
||||
fqn,
|
||||
false,
|
||||
false,
|
||||
));
|
||||
|
||||
// insert
|
||||
@ -71,7 +70,6 @@ fn collect_symbols_extern_function(
|
||||
Some(extern_function.declared_name_source_range()),
|
||||
fqn,
|
||||
true,
|
||||
false,
|
||||
));
|
||||
|
||||
// insert function symbol
|
||||
|
||||
@ -21,30 +21,23 @@ fn collect_types_function(function: &Function, ctx: &mut AnalysisContext) {
|
||||
|
||||
let return_type_info = match function.return_type() {
|
||||
None => TypeInfo::Void,
|
||||
Some(type_use) => declared_name_to_type_info(ctx, type_use.declared_name()),
|
||||
Some(type_use) => declared_name_to_type_info(type_use.declared_name()),
|
||||
};
|
||||
let return_type_info_id = ctx.insert_type_info(return_type_info);
|
||||
|
||||
let function_type_info = TypeInfo::Function(FunctionTypeInfo::new(
|
||||
parameter_type_info_ids,
|
||||
return_type_info_id,
|
||||
None,
|
||||
));
|
||||
let function_type_info_id = ctx.insert_type_info(function_type_info);
|
||||
|
||||
ctx.associate_node_and_symbol_to_type(function.node_id(), function_type_info_id);
|
||||
}
|
||||
|
||||
fn declared_name_to_type_info(ctx: &AnalysisContext, declared_name: &str) -> TypeInfo {
|
||||
fn declared_name_to_type_info(declared_name: &str) -> TypeInfo {
|
||||
match declared_name {
|
||||
"Any" => TypeInfo::Any,
|
||||
"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()
|
||||
}
|
||||
"String" => TypeInfo::String,
|
||||
"Int" => TypeInfo::Int,
|
||||
"Double" => TypeInfo::Double,
|
||||
"Void" => TypeInfo::Void,
|
||||
@ -58,7 +51,7 @@ fn collect_and_get_parameter_type_info_ids(
|
||||
) -> Vec<TypeInfoId> {
|
||||
let mut parameter_type_info_ids: Vec<TypeInfoId> = Vec::new();
|
||||
for parameter in parameters {
|
||||
let type_info = declared_name_to_type_info(ctx, parameter.type_use().declared_name());
|
||||
let type_info = declared_name_to_type_info(parameter.type_use().declared_name());
|
||||
let type_info_id = ctx.insert_type_info(type_info);
|
||||
parameter_type_info_ids.push(type_info_id);
|
||||
|
||||
@ -73,13 +66,12 @@ fn collect_types_extern_function(extern_function: &ExternFunction, ctx: &mut Ana
|
||||
collect_and_get_parameter_type_info_ids(extern_function.parameters(), ctx);
|
||||
|
||||
let return_type_info =
|
||||
declared_name_to_type_info(ctx, extern_function.return_type().declared_name());
|
||||
declared_name_to_type_info(extern_function.return_type().declared_name());
|
||||
let return_type_info_id = ctx.insert_type_info(return_type_info);
|
||||
|
||||
let function_type_info = TypeInfo::Function(FunctionTypeInfo::new(
|
||||
parameter_type_info_ids,
|
||||
return_type_info_id,
|
||||
None,
|
||||
));
|
||||
|
||||
let function_type_info_id = ctx.insert_type_info(function_type_info);
|
||||
|
||||
@ -10,7 +10,6 @@ use crate::ast::identifier::Identifier;
|
||||
use crate::ast::let_statement::LetStatement;
|
||||
use crate::ast::negative_expression::NegativeExpression;
|
||||
use crate::ast::parameter::Parameter;
|
||||
use crate::ast::path::Path;
|
||||
use crate::ast::statement::Statement;
|
||||
use crate::diagnostic::Diagnostics;
|
||||
use crate::semantic_analysis::analysis_context::AnalysisContext;
|
||||
@ -196,9 +195,6 @@ fn resolve_names_expression(
|
||||
Expression::Call(call) => {
|
||||
resolve_names_call(call, ctx, diagnostics, phase);
|
||||
}
|
||||
Expression::Path(path) => {
|
||||
resolve_names_path(path, ctx, diagnostics, phase);
|
||||
}
|
||||
Expression::Identifier(identifier) => {
|
||||
resolve_names_identifier(identifier, ctx, diagnostics, phase);
|
||||
}
|
||||
@ -239,18 +235,6 @@ fn resolve_names_call(
|
||||
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(
|
||||
identifier: &Identifier,
|
||||
ctx: &mut AnalysisContext,
|
||||
|
||||
@ -10,16 +10,14 @@ use crate::ast::identifier::Identifier;
|
||||
use crate::ast::let_statement::LetStatement;
|
||||
use crate::ast::negative_expression::NegativeExpression;
|
||||
use crate::ast::parameter::Parameter;
|
||||
use crate::ast::path::Path;
|
||||
use crate::ast::statement::Statement;
|
||||
use crate::diagnostic::{Diagnostic, Diagnostics};
|
||||
use crate::error_codes::BINARY_INCOMPATIBLE_TYPES;
|
||||
use crate::semantic_analysis::analysis_context::AnalysisContext;
|
||||
use crate::semantic_analysis::analysis_context::helpers::get_name_for_type;
|
||||
use crate::semantic_analysis::resolve_types::type_analysis::{
|
||||
are_binary_op_compatible, binary_op_result, can_assign_right_to_left, can_negate, negate_result,
|
||||
};
|
||||
use crate::semantic_analysis::type_info::{InstanceTypeInfo, TypeInfo};
|
||||
use crate::semantic_analysis::type_info::TypeInfo;
|
||||
|
||||
pub struct ResolveTypesResult(pub Diagnostics);
|
||||
|
||||
@ -111,12 +109,10 @@ fn resolve_types_assign_statement(
|
||||
let destination_type_info =
|
||||
ctx.get_type_info_for_node(assign_statement.destination().node_id());
|
||||
|
||||
if !can_assign_right_to_left(destination_type_info, value_type_info, 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);
|
||||
if !can_assign_right_to_left(destination_type_info, value_type_info) {
|
||||
let message = format!(
|
||||
"Incompatible types: cannot assign {} from {}",
|
||||
destination_type_name, value_type_name
|
||||
destination_type_info, value_type_info
|
||||
);
|
||||
let diagnostic = Diagnostic::new(
|
||||
&message,
|
||||
@ -142,9 +138,6 @@ fn resolve_types_expression(
|
||||
Expression::Call(call) => {
|
||||
resolve_types_call(call, ctx, diagnostics);
|
||||
}
|
||||
Expression::Path(path) => {
|
||||
resolve_types_path(path, ctx, diagnostics);
|
||||
}
|
||||
Expression::Identifier(identifier) => {
|
||||
resolve_types_identifier(identifier, ctx);
|
||||
}
|
||||
@ -155,13 +148,7 @@ fn resolve_types_expression(
|
||||
ctx.insert_type_info_for_node(TypeInfo::Double, double_literal.node_id());
|
||||
}
|
||||
Expression::String(string_literal) => {
|
||||
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(),
|
||||
);
|
||||
ctx.insert_type_info_for_node(TypeInfo::String, string_literal.node_id());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -177,9 +164,9 @@ fn resolve_types_binary_expression(
|
||||
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());
|
||||
|
||||
if are_binary_op_compatible(ctx, binary_expression.op(), lhs_type_info, rhs_type_info) {
|
||||
if are_binary_op_compatible(binary_expression.op(), lhs_type_info, rhs_type_info) {
|
||||
let result_type_info =
|
||||
binary_op_result(ctx, binary_expression.op(), lhs_type_info, rhs_type_info);
|
||||
binary_op_result(binary_expression.op(), lhs_type_info, rhs_type_info);
|
||||
ctx.insert_type_info_for_node(result_type_info, binary_expression.node_id());
|
||||
} else {
|
||||
let op_name = match binary_expression.op() {
|
||||
@ -194,11 +181,9 @@ fn resolve_types_binary_expression(
|
||||
BinaryOperation::BitwiseXor => "bitwise xor",
|
||||
BinaryOperation::BitwiseOr => "bitwise or",
|
||||
};
|
||||
let lhs_type_name = get_name_for_type(lhs_type_info, ctx);
|
||||
let rhs_type_name = get_name_for_type(rhs_type_info, ctx);
|
||||
let message = format!(
|
||||
"Incompatible types: cannot {} {} and {}",
|
||||
op_name, lhs_type_name, rhs_type_name
|
||||
op_name, lhs_type_info, rhs_type_info
|
||||
);
|
||||
let diagnostic = Diagnostic::new(
|
||||
&message,
|
||||
@ -226,8 +211,7 @@ fn resolve_types_negative_expression(
|
||||
let result_type_info = negate_result(operand_type_info);
|
||||
ctx.insert_type_info_for_node(result_type_info, negative_expression.node_id());
|
||||
} else {
|
||||
let operand_type_name = get_name_for_type(operand_type_info, ctx);
|
||||
let message = format!("Incompatible type: cannot negate {}", operand_type_name);
|
||||
let message = format!("Incompatible type: cannot negate {}", operand_type_info);
|
||||
let diagnostic = Diagnostic::new(
|
||||
&message,
|
||||
negative_expression.source_range().start(),
|
||||
@ -271,26 +255,25 @@ fn resolve_types_call(call: &Call, ctx: &mut AnalysisContext, diagnostics: &mut
|
||||
// of the function
|
||||
}
|
||||
|
||||
// check argument types, but only if we have the right number of arguments
|
||||
// check argument types
|
||||
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() {
|
||||
let argument_type_info = ctx.get_type_info_for_node(arguments[i].node_id());
|
||||
let parameter_type_info = ctx.get_type_info_by_id(parameter_type_ids[i]); // n.b. different method
|
||||
if !can_assign_right_to_left(parameter_type_info, argument_type_info, ctx) {
|
||||
let parameter_type_name = get_name_for_type(parameter_type_info, ctx);
|
||||
let argument_type_name = get_name_for_type(argument_type_info, ctx);
|
||||
let message = format!(
|
||||
"Incompatible types: cannot assign {} to {}",
|
||||
argument_type_name, parameter_type_name
|
||||
);
|
||||
let diagnostic = Diagnostic::new(
|
||||
&message,
|
||||
arguments[i].source_range().start(),
|
||||
arguments[i].source_range().end(),
|
||||
);
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
for i in 0..parameter_type_ids.len() {
|
||||
let argument_type_info = ctx.get_type_info_for_node(arguments[i].node_id());
|
||||
let parameter_type_info = ctx.get_type_info_by_id(parameter_type_ids[i]); // n.b. different method
|
||||
if !can_assign_right_to_left(parameter_type_info, argument_type_info) {
|
||||
let message = format!(
|
||||
"Incompatible types: cannot assign {} to {}",
|
||||
argument_type_info, parameter_type_info
|
||||
);
|
||||
let diagnostic = Diagnostic::new(
|
||||
&message,
|
||||
arguments[i].source_range().start(),
|
||||
arguments[i].source_range().end(),
|
||||
);
|
||||
diagnostics.push(diagnostic);
|
||||
|
||||
// do not push an error type because the result of the whole call is the return type
|
||||
// of the function
|
||||
}
|
||||
}
|
||||
|
||||
@ -303,8 +286,7 @@ fn resolve_types_call(call: &Call, ctx: &mut AnalysisContext, diagnostics: &mut
|
||||
ctx.insert_type_info_for_node(TypeInfo::__Error, call.node_id());
|
||||
}
|
||||
_ => {
|
||||
let callee_type_name = get_name_for_type(callee_type_info, ctx);
|
||||
let message = format!("Incompatible type: cannot call type {}", callee_type_name);
|
||||
let message = format!("Incompatible type: cannot call type {}", callee_type_info);
|
||||
let diagnostic = Diagnostic::new(
|
||||
&message,
|
||||
call.callee().source_range().start(),
|
||||
@ -318,73 +300,6 @@ 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) {
|
||||
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);
|
||||
|
||||
@ -1,19 +1,13 @@
|
||||
use crate::ast::binary_expression::BinaryOperation;
|
||||
use crate::semantic_analysis::analysis_context::AnalysisContext;
|
||||
use crate::semantic_analysis::type_info::TypeInfo;
|
||||
|
||||
pub fn are_binary_op_compatible(
|
||||
ctx: &AnalysisContext,
|
||||
op: &BinaryOperation,
|
||||
left: &TypeInfo,
|
||||
right: &TypeInfo,
|
||||
) -> bool {
|
||||
pub fn are_binary_op_compatible(op: &BinaryOperation, left: &TypeInfo, right: &TypeInfo) -> bool {
|
||||
match op {
|
||||
BinaryOperation::Multiply
|
||||
| BinaryOperation::Divide
|
||||
| BinaryOperation::Modulo
|
||||
| BinaryOperation::Subtract => are_numbers(left, right),
|
||||
BinaryOperation::Add => are_numbers_or_strings(ctx, left, right),
|
||||
BinaryOperation::Add => are_numbers_or_strings(left, right),
|
||||
BinaryOperation::LeftShift
|
||||
| BinaryOperation::RightShift
|
||||
| BinaryOperation::BitwiseAnd
|
||||
@ -30,35 +24,21 @@ fn are_numbers(t0: &TypeInfo, t1: &TypeInfo) -> bool {
|
||||
is_number(t0) && is_number(t1)
|
||||
}
|
||||
|
||||
fn are_numbers_or_strings(ctx: &AnalysisContext, t0: &TypeInfo, t1: &TypeInfo) -> bool {
|
||||
(is_number(t0) || is_string(ctx, t0)) && (is_number(t1) || is_string(ctx, t1))
|
||||
}
|
||||
|
||||
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_numbers_or_strings(t0: &TypeInfo, t1: &TypeInfo) -> bool {
|
||||
(is_number(t0) || matches!(t0, TypeInfo::String))
|
||||
&& (is_number(t1) || matches!(t1, TypeInfo::String))
|
||||
}
|
||||
|
||||
fn are_ints(t0: &TypeInfo, t1: &TypeInfo) -> bool {
|
||||
matches!(t0, TypeInfo::Int) && matches!(t1, TypeInfo::Int)
|
||||
}
|
||||
|
||||
pub fn binary_op_result(
|
||||
ctx: &AnalysisContext,
|
||||
op: &BinaryOperation,
|
||||
left: &TypeInfo,
|
||||
right: &TypeInfo,
|
||||
) -> TypeInfo {
|
||||
pub fn binary_op_result(op: &BinaryOperation, left: &TypeInfo, right: &TypeInfo) -> TypeInfo {
|
||||
match op {
|
||||
BinaryOperation::Multiply => numbers_binary_result(left, right),
|
||||
BinaryOperation::Divide => TypeInfo::Double, // okay for now
|
||||
BinaryOperation::Modulo => numbers_binary_result(left, right), // same properties as multiplication
|
||||
BinaryOperation::Add => add_result(ctx, left, right),
|
||||
BinaryOperation::Add => add_result(left, right),
|
||||
BinaryOperation::Subtract => numbers_binary_result(left, right),
|
||||
BinaryOperation::LeftShift
|
||||
| BinaryOperation::RightShift
|
||||
@ -86,40 +66,22 @@ fn numbers_binary_result(left: &TypeInfo, right: &TypeInfo) -> TypeInfo {
|
||||
}
|
||||
}
|
||||
|
||||
fn add_result(ctx: &AnalysisContext, left: &TypeInfo, right: &TypeInfo) -> TypeInfo {
|
||||
fn add_result(left: &TypeInfo, right: &TypeInfo) -> TypeInfo {
|
||||
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::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::Double => TypeInfo::Double,
|
||||
TypeInfo::String => TypeInfo::String,
|
||||
TypeInfo::__Error => TypeInfo::__Error,
|
||||
_ => panic!(),
|
||||
},
|
||||
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::String => TypeInfo::String,
|
||||
TypeInfo::__Error => TypeInfo::__Error,
|
||||
_ => panic!(),
|
||||
},
|
||||
TypeInfo::String => TypeInfo::String,
|
||||
TypeInfo::__Error => TypeInfo::__Error,
|
||||
_ => panic!(),
|
||||
}
|
||||
@ -141,18 +103,12 @@ pub fn negate_result(operand: &TypeInfo) -> TypeInfo {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn can_assign_right_to_left(left: &TypeInfo, right: &TypeInfo, ctx: &AnalysisContext) -> bool {
|
||||
pub fn can_assign_right_to_left(left: &TypeInfo, right: &TypeInfo) -> bool {
|
||||
match left {
|
||||
TypeInfo::Any => true,
|
||||
TypeInfo::Function(_) => {
|
||||
panic!()
|
||||
}
|
||||
TypeInfo::Instance(left_instance_type_info) => match right {
|
||||
TypeInfo::Instance(right_instance_type_info) => {
|
||||
left_instance_type_info.can_assign_from(right_instance_type_info, ctx)
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
TypeInfo::String => match right {
|
||||
TypeInfo::String => true,
|
||||
_ => false,
|
||||
|
||||
@ -1,14 +1,10 @@
|
||||
use crate::semantic_analysis::analysis_context::AnalysisContext;
|
||||
use crate::source_range::SourceRange;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub type SymbolId = usize;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Symbol {
|
||||
Class(ClassSymbol),
|
||||
Field(FieldSymbol),
|
||||
Function(FunctionSymbol),
|
||||
Parameter(ParameterSymbol),
|
||||
Variable(VariableSymbol),
|
||||
@ -17,8 +13,6 @@ pub enum Symbol {
|
||||
impl Symbol {
|
||||
pub fn declared_name(&self) -> &str {
|
||||
match self {
|
||||
Symbol::Class(class_symbol) => class_symbol.declared_name(),
|
||||
Symbol::Field(field_symbol) => &field_symbol.declared_name(),
|
||||
Symbol::Function(function_symbol) => function_symbol.declared_name(),
|
||||
Symbol::Parameter(parameter_symbol) => parameter_symbol.declared_name(),
|
||||
Symbol::Variable(variable_symbol) => variable_symbol.declared_name(),
|
||||
@ -27,8 +21,6 @@ impl Symbol {
|
||||
|
||||
pub fn declared_name_owned(&self) -> Rc<str> {
|
||||
match self {
|
||||
Symbol::Class(class_symbol) => class_symbol.declared_name_owned(),
|
||||
Symbol::Field(field_symbol) => field_symbol.declared_name_owned(),
|
||||
Symbol::Function(function_symbol) => function_symbol.declared_name_owned(),
|
||||
Symbol::Parameter(parameter_symbol) => parameter_symbol.declared_name_owned(),
|
||||
Symbol::Variable(variable_symbol) => variable_symbol.declared_name_owned(),
|
||||
@ -37,105 +29,11 @@ impl Symbol {
|
||||
|
||||
pub fn source_range(&self) -> Option<&SourceRange> {
|
||||
match self {
|
||||
Symbol::Class(class_symbol) => class_symbol.source_range(),
|
||||
Symbol::Field(field_symbol) => field_symbol.source_range(),
|
||||
Symbol::Function(function_symbol) => function_symbol.source_range(),
|
||||
Symbol::Parameter(parameter_symbol) => parameter_symbol.source_range(),
|
||||
Symbol::Variable(variable_symbol) => variable_symbol.source_range(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwrap_class_symbol(&self) -> &ClassSymbol {
|
||||
match self {
|
||||
Symbol::Class(class_symbol) => class_symbol,
|
||||
_ => panic!("Attempt to unwrap {:?} as ClassSymbol", self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ClassSymbol {
|
||||
declared_name: Rc<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)]
|
||||
@ -144,7 +42,6 @@ pub struct FunctionSymbol {
|
||||
declared_name_source_range: Option<SourceRange>,
|
||||
fqn: Rc<str>,
|
||||
is_extern: bool,
|
||||
is_method: bool,
|
||||
}
|
||||
|
||||
impl FunctionSymbol {
|
||||
@ -153,14 +50,12 @@ impl FunctionSymbol {
|
||||
declared_name_source_range: Option<SourceRange>,
|
||||
fqn: Rc<str>,
|
||||
is_extern: bool,
|
||||
is_method: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
declared_name,
|
||||
declared_name_source_range,
|
||||
fqn,
|
||||
is_extern,
|
||||
is_method,
|
||||
}
|
||||
}
|
||||
|
||||
@ -187,10 +82,6 @@ impl FunctionSymbol {
|
||||
pub fn is_extern(&self) -> bool {
|
||||
self.is_extern
|
||||
}
|
||||
|
||||
pub fn is_method(&self) -> bool {
|
||||
self.is_method
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
use crate::semantic_analysis::analysis_context::AnalysisContext;
|
||||
use crate::semantic_analysis::symbol::SymbolId;
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
pub type TypeInfoId = usize;
|
||||
|
||||
@ -7,7 +6,6 @@ pub type TypeInfoId = usize;
|
||||
pub enum TypeInfo {
|
||||
Any,
|
||||
Function(FunctionTypeInfo),
|
||||
Instance(InstanceTypeInfo),
|
||||
String,
|
||||
Int,
|
||||
Double,
|
||||
@ -15,23 +13,35 @@ pub enum TypeInfo {
|
||||
__Error,
|
||||
}
|
||||
|
||||
impl Display for TypeInfo {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
TypeInfo::Any => "Any",
|
||||
TypeInfo::Function(_) => "Function",
|
||||
TypeInfo::String => "String",
|
||||
TypeInfo::Int => "Int",
|
||||
TypeInfo::Double => "Double",
|
||||
TypeInfo::Void => "Void",
|
||||
TypeInfo::__Error => panic!("This should never be shown to a user."),
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FunctionTypeInfo {
|
||||
parameter_type_info_ids: Vec<TypeInfoId>,
|
||||
return_type_info_id: TypeInfoId,
|
||||
self_type_info_id: Option<TypeInfoId>,
|
||||
}
|
||||
|
||||
impl FunctionTypeInfo {
|
||||
pub fn new(
|
||||
parameter_type_info_ids: Vec<TypeInfoId>,
|
||||
return_type_info_id: TypeInfoId,
|
||||
self_type_info_id: Option<TypeInfoId>,
|
||||
) -> Self {
|
||||
pub fn new(parameter_type_info_ids: Vec<TypeInfoId>, return_type_info_id: TypeInfoId) -> Self {
|
||||
Self {
|
||||
parameter_type_info_ids,
|
||||
return_type_info_id,
|
||||
self_type_info_id,
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,29 +52,4 @@ impl FunctionTypeInfo {
|
||||
pub fn return_type_id(&self) -> TypeInfoId {
|
||||
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,12 +3,10 @@ mod e2e_tests {
|
||||
use dmc_lib::compile_compilation_unit;
|
||||
use dmc_lib::constants_table::ConstantsTable;
|
||||
use dmc_lib::diagnostic::{Diagnostic, Diagnostics};
|
||||
use dmc_lib::intrinsics::add_primitive_symbols_and_type_infos;
|
||||
use dmc_lib::semantic_analysis::analysis_context::AnalysisContext;
|
||||
use dvm_lib::vm::constant::{Constant, StringConstant};
|
||||
use dvm_lib::vm::value::Value;
|
||||
use dvm_lib::vm::{DvmContext, call};
|
||||
use std::error::Error;
|
||||
use std::rc::Rc;
|
||||
|
||||
const REGISTER_COUNT: usize = 16;
|
||||
@ -27,9 +25,6 @@ mod e2e_tests {
|
||||
|
||||
fn prepare_context(input: &str) -> Result<DvmContext, Diagnostics> {
|
||||
let mut ctx = AnalysisContext::new();
|
||||
let global_scope_id = ctx.push_scope("global");
|
||||
add_primitive_symbols_and_type_infos(&mut ctx, global_scope_id);
|
||||
|
||||
let mut constants_table = ConstantsTable::new();
|
||||
|
||||
let compile_compilation_unit_result =
|
||||
@ -229,31 +224,4 @@ mod e2e_tests {
|
||||
assert_eq!(o.fields()[0].unwrap_int(), 42);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_len() -> Result<(), Diagnostics> {
|
||||
fn core_string_len(args: &[Value]) -> Result<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