[fine] Methods!
This commit is contained in:
parent
2839b43f6d
commit
0c69758b11
5 changed files with 352 additions and 95 deletions
|
|
@ -291,6 +291,7 @@ fn compile_expression(c: &mut Compiler, t: TreeRef) {
|
|||
TreeKind::NewObjectExpression => compile_new_object_expression(c, t, tree),
|
||||
TreeKind::FieldValue => compile_field_value(c, t, tree),
|
||||
TreeKind::MemberAccess => compile_member_access(c, tree),
|
||||
TreeKind::SelfReference => compile_self_reference(c),
|
||||
_ => ice!(c, t, "{tree:?} is not an expression, cannot compile"),
|
||||
};
|
||||
if matches!(cr, None) {
|
||||
|
|
@ -553,11 +554,19 @@ fn compile_load_declaration(c: &mut Compiler, t: TreeRef, declaration: &Declarat
|
|||
let tree = &c.syntax[*declaration];
|
||||
compiler_assert_eq!(c, t, tree.kind, TreeKind::FunctionDecl);
|
||||
|
||||
compile_function_declaration(c, t, tree, false)?;
|
||||
compile_function_declaration(c, *declaration, tree, false)?;
|
||||
|
||||
*c.function_bindings
|
||||
.get(&key)
|
||||
.expect("did not compile the function!")
|
||||
match c.function_bindings.get(&key) {
|
||||
Some(index) => *index,
|
||||
None => {
|
||||
ice!(
|
||||
c,
|
||||
t,
|
||||
"did not compile the function with key {:?}!",
|
||||
declaration
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
Instruction::LoadFunction(index)
|
||||
|
|
@ -584,6 +593,12 @@ fn compile_call_expression(c: &mut Compiler, tree: &Tree) -> CR {
|
|||
}
|
||||
|
||||
let func = tree.nth_tree(0)?;
|
||||
let func_type = c.semantics.type_of(func);
|
||||
let arg_count = match func_type {
|
||||
// TODO: Consider being guided by syntax here?
|
||||
Type::Method(..) => arg_count + 1,
|
||||
_ => arg_count,
|
||||
};
|
||||
compile_expression(c, func);
|
||||
|
||||
c.push(Instruction::Call(arg_count));
|
||||
|
|
@ -682,11 +697,21 @@ fn compile_field_value(c: &mut Compiler, t: TreeRef, tree: &Tree) -> CR {
|
|||
fn compile_member_access(c: &mut Compiler, tree: &Tree) -> CR {
|
||||
// In member access; the lhs sets up the object and in theory the rhs
|
||||
// binds against it. ::shrug::
|
||||
//
|
||||
compile_expression(c, tree.nth_tree(0)?);
|
||||
|
||||
// NOTE: If this is a method call we still don't have to do anything
|
||||
// special here, since the load of the member function will *not*
|
||||
// consume the self pointer from the stack.
|
||||
compile_expression(c, tree.nth_tree(2)?);
|
||||
OK
|
||||
}
|
||||
|
||||
fn compile_self_reference(c: &mut Compiler) -> CR {
|
||||
c.push(Instruction::LoadArgument(0));
|
||||
OK
|
||||
}
|
||||
|
||||
fn compile_statement(c: &mut Compiler, t: TreeRef, gen_value: bool) {
|
||||
let tree = &c.semantics.tree()[t];
|
||||
let cr = match tree.kind {
|
||||
|
|
@ -777,6 +802,7 @@ fn compile_function_declaration(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_v
|
|||
// if we have no unbound type variables.
|
||||
let fk = FunctionKey { tree: t };
|
||||
if !c.function_bindings.contains_key(&fk) {
|
||||
// TODO: If this is a method the name should be different.
|
||||
let name = tree.nth_token(1)?;
|
||||
|
||||
let param_list = tree.child_tree_of_kind(c.syntax, TreeKind::ParamList)?;
|
||||
|
|
|
|||
|
|
@ -62,10 +62,19 @@ pub struct FieldDecl {
|
|||
pub field_type: Type,
|
||||
}
|
||||
|
||||
pub struct MethodDecl {
|
||||
pub name: Rc<str>,
|
||||
pub decl_type: Type,
|
||||
pub declaration: TreeRef,
|
||||
}
|
||||
|
||||
pub struct ClassDecl {
|
||||
pub name: Rc<str>,
|
||||
pub fields: Vec<FieldDecl>,
|
||||
pub methods: Vec<MethodDecl>,
|
||||
pub decl_tree: TreeRef,
|
||||
|
||||
pub env: EnvironmentRef,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
@ -115,6 +124,12 @@ pub enum Type {
|
|||
Bool,
|
||||
|
||||
Function(Vec<Box<Type>>, Box<Type>),
|
||||
|
||||
// A method is like a function except that it takes a self parameter.
|
||||
// This is how we signal the difference. We do *not* count them as the
|
||||
// same.
|
||||
Method(Box<Type>, Vec<Box<Type>>, Box<Type>),
|
||||
|
||||
List(Box<Type>),
|
||||
|
||||
// Classes need to be fetched explicitly from the semantics; they are
|
||||
|
|
@ -161,15 +176,30 @@ impl fmt::Display for Type {
|
|||
write!(f, ") -> {ret}")
|
||||
}
|
||||
|
||||
// TODO: Better names
|
||||
TypeVariable(_) => write!(f, "$_"),
|
||||
Method(self_type, args, ret) => {
|
||||
write!(f, "method of {self_type} (")?;
|
||||
let mut first = true;
|
||||
for arg in args.iter() {
|
||||
if !first {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{arg}")?;
|
||||
first = false;
|
||||
}
|
||||
write!(f, ") -> {ret}")
|
||||
}
|
||||
|
||||
TypeVariable(_) => {
|
||||
// TODO: Better names for type variable
|
||||
write!(f, "$_")
|
||||
}
|
||||
List(t) => write!(f, "list<{t}>"),
|
||||
Class(_, name) => write!(f, "class {}", name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Location {
|
||||
Argument,
|
||||
Local,
|
||||
|
|
@ -213,6 +243,7 @@ pub struct Environment {
|
|||
pub location: Location,
|
||||
pub next_index: usize,
|
||||
pub declarations: HashMap<Box<str>, Declaration>,
|
||||
pub is_error: bool,
|
||||
}
|
||||
|
||||
impl Environment {
|
||||
|
|
@ -238,9 +269,21 @@ impl Environment {
|
|||
location,
|
||||
next_index,
|
||||
declarations: HashMap::new(),
|
||||
is_error: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn error() -> EnvironmentRef {
|
||||
// TODO: Exactly once?
|
||||
EnvironmentRef::new(Environment {
|
||||
parent: None,
|
||||
location: Location::Local,
|
||||
next_index: 0,
|
||||
declarations: HashMap::new(),
|
||||
is_error: true,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, token: &Token, t: Type) -> Option<Declaration> {
|
||||
self.insert_name(token.as_str().into(), t)
|
||||
}
|
||||
|
|
@ -557,7 +600,7 @@ impl<'a> Semantics<'a> {
|
|||
|
||||
TreeKind::ForStatement => self.environment_of_for(parent, tree),
|
||||
|
||||
TreeKind::MemberAccess => self.environment_of_member_access(parent, tree),
|
||||
TreeKind::MemberAccess => self.environment_of_member_access(tree),
|
||||
|
||||
_ => parent,
|
||||
};
|
||||
|
|
@ -665,16 +708,12 @@ impl<'a> Semantics<'a> {
|
|||
None => Type::Error,
|
||||
};
|
||||
|
||||
// TODO: Variables cannot be of type <method>?
|
||||
|
||||
let location = match parent.location {
|
||||
Location::Local => Location::Local,
|
||||
Location::Module => Location::Module,
|
||||
Location::Argument => Location::Local,
|
||||
|
||||
// TODO: Wait can I... do wild stuff? e.g.:
|
||||
//
|
||||
// foo.{let y = 23; bar}
|
||||
//
|
||||
// ???
|
||||
Location::Slot => Location::Local,
|
||||
};
|
||||
|
||||
|
|
@ -688,25 +727,41 @@ impl<'a> Semantics<'a> {
|
|||
assert!(tree.kind == TreeKind::ParamList);
|
||||
|
||||
let mut environment = Environment::new(Some(parent), Location::Argument);
|
||||
for child in tree.children.iter() {
|
||||
for (i, child) in tree.children.iter().enumerate() {
|
||||
let Child::Tree(ct) = child else {
|
||||
continue;
|
||||
};
|
||||
let param = &self.syntax_tree[*ct];
|
||||
if param.kind != TreeKind::Parameter {
|
||||
continue;
|
||||
}
|
||||
match param.kind {
|
||||
TreeKind::SelfParameter => {
|
||||
let param_name = param.nth_token(0).unwrap();
|
||||
let declaration_type = self.type_of(*ct);
|
||||
if environment.insert(param_name, declaration_type).is_some() {
|
||||
self.report_error_tree(
|
||||
param,
|
||||
format!("duplicate definition of self parameter"),
|
||||
);
|
||||
} else if i != 1 {
|
||||
self.report_error_tree(
|
||||
param,
|
||||
"self parameter must be the first parameter in the list",
|
||||
);
|
||||
}
|
||||
}
|
||||
TreeKind::Parameter => {
|
||||
let Some(param_name) = param.nth_token(0) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let Some(param_name) = param.nth_token(0) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let declaration_type = self.type_of(*ct);
|
||||
if environment.insert(param_name, declaration_type).is_some() {
|
||||
self.report_error_tree(
|
||||
param,
|
||||
format!("duplicate definition of parameter '{param_name}'"),
|
||||
);
|
||||
let declaration_type = self.type_of(*ct);
|
||||
if environment.insert(param_name, declaration_type).is_some() {
|
||||
self.report_error_tree(
|
||||
param,
|
||||
format!("duplicate definition of parameter '{param_name}'"),
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -736,41 +791,27 @@ impl<'a> Semantics<'a> {
|
|||
EnvironmentRef::new(environment)
|
||||
}
|
||||
|
||||
fn environment_of_member_access(&self, parent: EnvironmentRef, tree: &Tree) -> EnvironmentRef {
|
||||
// TODO: Build an "error environment" and then anything that tried to
|
||||
// bind in it would know to give up and not report a new error.
|
||||
|
||||
fn environment_of_member_access(&self, tree: &Tree) -> EnvironmentRef {
|
||||
assert_eq!(tree.kind, TreeKind::MemberAccess);
|
||||
|
||||
// Build environment out of members of type of lhs
|
||||
let Some(lhs) = tree.nth_tree(0) else {
|
||||
return parent;
|
||||
return Environment::error();
|
||||
};
|
||||
let Some(op) = tree.nth_token(1) else {
|
||||
return parent;
|
||||
return Environment::error();
|
||||
};
|
||||
let typ = self.type_of(lhs);
|
||||
match &typ {
|
||||
Type::Class(ct, _) => {
|
||||
// NOTE: The parent here is None! environment search after a dot
|
||||
// is constrained to the scope of the type, obviously!
|
||||
let class = self.class_of(*ct);
|
||||
let mut env = Environment::new(None, Location::Slot);
|
||||
|
||||
// TODO: Cache environment of type.
|
||||
// NOTE: It is important that this go in order! The location
|
||||
// index is the slot index!
|
||||
for field in class.fields.iter() {
|
||||
env.insert_name((&*field.name).into(), field.field_type.clone());
|
||||
}
|
||||
|
||||
EnvironmentRef::new(env)
|
||||
class.env.clone()
|
||||
}
|
||||
Type::Error => parent, // TODO: WRONG
|
||||
Type::Error => Environment::error(),
|
||||
_ => {
|
||||
// TODO: This is probably wrong, yeah?
|
||||
self.report_error(op.start, format!("cannot access members of '{typ}'"));
|
||||
parent
|
||||
Environment::error()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -796,6 +837,8 @@ impl<'a> Semantics<'a> {
|
|||
assert_eq!(tree.kind, TreeKind::ClassDecl);
|
||||
|
||||
let name = tree.nth_token(1).map(|t| t.as_str()).unwrap_or("<??>");
|
||||
|
||||
// Fields
|
||||
let mut fields = Vec::new();
|
||||
for field in tree.children_of_kind(self.syntax_tree, TreeKind::FieldDecl) {
|
||||
let f = &self.syntax_tree[field];
|
||||
|
|
@ -811,10 +854,53 @@ impl<'a> Semantics<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
// Methods
|
||||
let mut methods = Vec::new();
|
||||
for method in tree.children_of_kind(self.syntax_tree, TreeKind::FunctionDecl) {
|
||||
let m = &self.syntax_tree[method];
|
||||
if let Some(method_name) = m.nth_token(1) {
|
||||
methods.push(MethodDecl {
|
||||
name: method_name.as_str().into(),
|
||||
decl_type: self.type_of(method),
|
||||
declaration: method,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Build into an environment
|
||||
let mut env = Environment::new(None, Location::Slot);
|
||||
for (index, field) in fields.iter().enumerate() {
|
||||
env.declarations.insert(
|
||||
(&*field.name).into(),
|
||||
Declaration::Variable {
|
||||
index,
|
||||
declaration_type: field.field_type.clone(),
|
||||
location: Location::Slot,
|
||||
},
|
||||
);
|
||||
}
|
||||
for method in methods.iter() {
|
||||
let existing = env.declarations.insert(
|
||||
(&*method.name).into(),
|
||||
Declaration::Function {
|
||||
declaration_type: method.decl_type.clone(),
|
||||
declaration: method.declaration,
|
||||
},
|
||||
);
|
||||
if existing.is_some() {
|
||||
self.report_error_tree_ref(
|
||||
method.declaration,
|
||||
format!("duplicate definition of method '{}'", method.name),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let result = ClassRef::new(ClassDecl {
|
||||
name: name.into(),
|
||||
fields: fields.into(),
|
||||
methods: methods.into(),
|
||||
decl_tree: t,
|
||||
env: EnvironmentRef::new(env),
|
||||
});
|
||||
|
||||
self.classes.borrow_mut()[t.index()] = Incremental::Complete(result.clone());
|
||||
|
|
@ -881,35 +967,34 @@ impl<'a> Semantics<'a> {
|
|||
|
||||
let result = match tree.kind {
|
||||
TreeKind::Error => Some(Type::Error),
|
||||
TreeKind::UnaryExpression => self.type_of_unary(tree),
|
||||
|
||||
TreeKind::Argument => self.type_of_argument(tree),
|
||||
TreeKind::BinaryExpression => self.type_of_binary(tree),
|
||||
TreeKind::Block => self.type_of_block(tree),
|
||||
TreeKind::CallExpression => self.type_of_call(tree),
|
||||
TreeKind::ClassDecl => self.type_of_class_decl(t, tree),
|
||||
TreeKind::ConditionalExpression => self.type_of_conditional(tree),
|
||||
TreeKind::ExpressionStatement => self.type_of_expression_statement(tree),
|
||||
TreeKind::FieldValue => self.type_of_field_value(t, tree),
|
||||
TreeKind::ForStatement => Some(Type::Nothing),
|
||||
TreeKind::FunctionDecl => self.type_of_function_decl(tree),
|
||||
TreeKind::GroupingExpression => self.type_of_grouping(tree),
|
||||
TreeKind::Identifier => self.type_of_identifier(t, tree),
|
||||
TreeKind::IfStatement => self.type_of_if_statement(tree),
|
||||
TreeKind::LetStatement => Some(Type::Nothing),
|
||||
TreeKind::ListConstructor => self.type_of_list_constructor(t, tree),
|
||||
TreeKind::ListConstructorElement => self.type_of_list_constructor_element(tree),
|
||||
TreeKind::LiteralExpression => self.type_of_literal(tree),
|
||||
TreeKind::MemberAccess => self.type_of_member_access(tree),
|
||||
TreeKind::NewObjectExpression => self.type_of_new_object_expression(tree),
|
||||
TreeKind::Parameter => self.type_of_parameter(tree),
|
||||
TreeKind::ReturnStatement => Some(Type::Unreachable),
|
||||
TreeKind::ReturnType => self.type_of_return_type(tree),
|
||||
TreeKind::SelfParameter => self.type_of_self_parameter(tree),
|
||||
TreeKind::SelfReference => self.type_of_self_reference(t, tree),
|
||||
TreeKind::TypeExpression => self.type_of_type_expr(t, tree),
|
||||
TreeKind::TypeParameter => self.type_of_type_parameter(tree),
|
||||
TreeKind::Block => self.type_of_block(tree),
|
||||
TreeKind::LiteralExpression => self.type_of_literal(tree),
|
||||
TreeKind::GroupingExpression => self.type_of_grouping(tree),
|
||||
TreeKind::ConditionalExpression => self.type_of_conditional(tree),
|
||||
TreeKind::CallExpression => self.type_of_call(tree),
|
||||
TreeKind::Argument => self.type_of_argument(tree),
|
||||
TreeKind::MemberAccess => self.type_of_member_access(tree),
|
||||
|
||||
TreeKind::LetStatement => Some(Type::Nothing),
|
||||
TreeKind::ReturnStatement => Some(Type::Unreachable),
|
||||
TreeKind::ForStatement => Some(Type::Nothing),
|
||||
TreeKind::ExpressionStatement => self.type_of_expression_statement(tree),
|
||||
TreeKind::IfStatement => self.type_of_if_statement(tree),
|
||||
TreeKind::Identifier => self.type_of_identifier(t, tree),
|
||||
|
||||
TreeKind::FunctionDecl => self.type_of_function_decl(tree),
|
||||
TreeKind::ReturnType => self.type_of_return_type(tree),
|
||||
TreeKind::Parameter => self.type_of_parameter(tree),
|
||||
|
||||
TreeKind::ListConstructorElement => self.type_of_list_constructor_element(tree),
|
||||
TreeKind::ListConstructor => self.type_of_list_constructor(t, tree),
|
||||
|
||||
TreeKind::NewObjectExpression => self.type_of_new_object_expression(tree),
|
||||
TreeKind::FieldValue => self.type_of_field_value(t, tree),
|
||||
TreeKind::ClassDecl => self.type_of_class_decl(t, tree),
|
||||
TreeKind::UnaryExpression => self.type_of_unary(tree),
|
||||
|
||||
_ => self.internal_compiler_error(Some(t), "asking for a nonsense type"),
|
||||
};
|
||||
|
|
@ -1122,7 +1207,9 @@ impl<'a> Semantics<'a> {
|
|||
Some(Type::Error)
|
||||
}
|
||||
None => {
|
||||
self.report_error_tree(tree, format!("Unrecognized type: '{token}'"));
|
||||
if !environment.is_error {
|
||||
self.report_error_tree(tree, format!("Unrecognized type: '{token}'"));
|
||||
}
|
||||
Some(Type::Error)
|
||||
}
|
||||
}
|
||||
|
|
@ -1297,6 +1384,37 @@ impl<'a> Semantics<'a> {
|
|||
|
||||
Some(*ret.clone())
|
||||
}
|
||||
|
||||
Type::Method(_, params, ret) => {
|
||||
let mut any_errors = false;
|
||||
|
||||
// For the purposes of type checking ignore the self type.
|
||||
if params.len() != arg_types.len() {
|
||||
// TODO: Augment with function name if known
|
||||
self.report_error_tree(tree, format!("expected {} parameters", params.len()));
|
||||
any_errors = true;
|
||||
}
|
||||
|
||||
for (i, ((t, a), p)) in arg_types.iter().zip(params.iter()).enumerate() {
|
||||
if !self.type_compat(&a, p) {
|
||||
self.report_error_tree_ref(
|
||||
*t,
|
||||
format!(
|
||||
"parameter {i} has an incompatible type: expected {} but got {}",
|
||||
p, a
|
||||
),
|
||||
);
|
||||
any_errors = true;
|
||||
}
|
||||
}
|
||||
|
||||
if any_errors {
|
||||
return Some(Type::Error);
|
||||
}
|
||||
|
||||
Some(*ret.clone())
|
||||
}
|
||||
|
||||
_ => {
|
||||
self.report_error_tree_ref(f_ref, format!("expected a function type, got: {f}"));
|
||||
Some(Type::Error)
|
||||
|
|
@ -1364,15 +1482,71 @@ impl<'a> Semantics<'a> {
|
|||
});
|
||||
}
|
||||
|
||||
self.report_error_tree(tree, format!("cannot find value {id} here"));
|
||||
if !environment.is_error {
|
||||
self.report_error_tree(tree, format!("cannot find value {id} here"));
|
||||
}
|
||||
Some(Type::Error)
|
||||
}
|
||||
|
||||
fn type_of_self_parameter(&self, tree: &Tree) -> Option<Type> {
|
||||
let pl = tree.parent?;
|
||||
let param_list = &self.syntax_tree[pl];
|
||||
|
||||
let fd = param_list.parent?;
|
||||
let function_decl = &self.syntax_tree[fd];
|
||||
|
||||
let cd = function_decl.parent?;
|
||||
let class_decl = &self.syntax_tree[cd];
|
||||
|
||||
if class_decl.kind != TreeKind::ClassDecl {
|
||||
self.report_error_tree(tree, "self parameter only allowed in methods");
|
||||
Some(Type::Error)
|
||||
} else {
|
||||
Some(self.type_of(cd))
|
||||
}
|
||||
}
|
||||
|
||||
fn type_of_self_reference(&self, t: TreeRef, tree: &Tree) -> Option<Type> {
|
||||
assert_eq!(tree.kind, TreeKind::SelfReference);
|
||||
|
||||
let id = tree.nth_token(0)?;
|
||||
let environment = self.environment_of(t);
|
||||
if let Some(declaration) = environment.bind(id) {
|
||||
return Some(match declaration {
|
||||
Declaration::Variable {
|
||||
declaration_type, ..
|
||||
} => declaration_type.clone(),
|
||||
_ => self.internal_compiler_error(
|
||||
Some(t),
|
||||
"how did I bind something other than a variable to self?",
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
if !environment.is_error {
|
||||
self.report_error_tree(tree, "`self` is only valid in methods");
|
||||
}
|
||||
Some(Type::Error)
|
||||
}
|
||||
|
||||
fn type_of_function_decl(&self, tree: &Tree) -> Option<Type> {
|
||||
let param_list = tree.child_tree_of_kind(self.syntax_tree, TreeKind::ParamList)?;
|
||||
|
||||
// NOTE: The methodness here is determined by the presence of a self
|
||||
// parameter, even if that parameter is incorrect (e.g., this
|
||||
// declaration is not nested in a class, or it is not the first
|
||||
// parameter.) We could have also chosen to signal it by our
|
||||
// nesting but we want to extract the self parameter to a
|
||||
// distinguished place in the function type.
|
||||
let mut self_type = None;
|
||||
let mut parameter_types = Vec::new();
|
||||
for p in param_list.child_trees() {
|
||||
parameter_types.push(Box::new(self.type_of(p)));
|
||||
let p_type = Box::new(self.type_of(p));
|
||||
if self.syntax_tree[p].kind == TreeKind::SelfParameter {
|
||||
self_type = Some(p_type);
|
||||
} else {
|
||||
parameter_types.push(p_type);
|
||||
}
|
||||
}
|
||||
|
||||
let return_type = match tree.child_of_kind(self.syntax_tree, TreeKind::ReturnType) {
|
||||
|
|
@ -1380,7 +1554,11 @@ impl<'a> Semantics<'a> {
|
|||
None => Type::Nothing,
|
||||
};
|
||||
let return_type = Box::new(return_type);
|
||||
Some(Type::Function(parameter_types, return_type))
|
||||
|
||||
Some(match self_type {
|
||||
Some(self_type) => Type::Method(self_type, parameter_types, return_type),
|
||||
None => Type::Function(parameter_types, return_type),
|
||||
})
|
||||
}
|
||||
|
||||
fn type_of_parameter(&self, tree: &Tree) -> Option<Type> {
|
||||
|
|
@ -1452,7 +1630,9 @@ impl<'a> Semantics<'a> {
|
|||
let declaration = match environment.bind(id) {
|
||||
Some(d) => d,
|
||||
None => {
|
||||
self.report_error_tree(tree, format!("cannot find value {id} here"));
|
||||
if !environment.is_error {
|
||||
self.report_error_tree(tree, format!("cannot find value {id} here"));
|
||||
}
|
||||
return Some(Type::Error);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -102,6 +102,18 @@ impl Frame {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn func(&self) -> Rc<Function> {
|
||||
self.func.clone()
|
||||
}
|
||||
|
||||
pub fn args(&self) -> &[StackValue] {
|
||||
&self.args
|
||||
}
|
||||
|
||||
pub fn pc(&self) -> usize {
|
||||
self.pc
|
||||
}
|
||||
|
||||
fn pop_value(&mut self) -> Result<StackValue> {
|
||||
self.stack.pop().ok_or_else(|| VMErrorCode::StackUnderflow)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -263,7 +263,7 @@ fn assert_eval_ok(tree: &SyntaxTree, lines: &Lines, expected: &str) {
|
|||
let semantics = Semantics::new(tree, lines);
|
||||
|
||||
let module = compile(&semantics);
|
||||
let mut context = Context::new(module);
|
||||
let mut context = Context::new(module.clone());
|
||||
context.init().expect("Unable to initialize module");
|
||||
|
||||
match eval_export_fn(&mut context, "test", &[]) {
|
||||
|
|
@ -278,7 +278,24 @@ fn assert_eval_ok(tree: &SyntaxTree, lines: &Lines, expected: &str) {
|
|||
);
|
||||
}
|
||||
Err(e) => {
|
||||
semantic_panic!(&semantics, None, "error occurred while running: {:?}", e);
|
||||
semantics.dump_compiler_state(None);
|
||||
|
||||
let mut actual = String::new();
|
||||
let _ = dump_module(&mut actual, &module);
|
||||
|
||||
eprintln!("{actual}");
|
||||
|
||||
for frame in e.stack.iter() {
|
||||
eprintln!("{:?}", frame.func());
|
||||
eprint!(" (");
|
||||
for arg in frame.args().iter() {
|
||||
eprint!("{:?},", arg);
|
||||
}
|
||||
eprintln!(") @ {}", frame.pc());
|
||||
}
|
||||
eprintln!();
|
||||
|
||||
panic!("error occurred while running: {:?}", e.code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ class Point {
|
|||
// 12
|
||||
// }
|
||||
|
||||
// fun square_length(self) -> f64 {
|
||||
// self.x * self.x + self.y * self.y
|
||||
// }
|
||||
fun square_length(self) -> f64 {
|
||||
self.x * self.x + self.y * self.y
|
||||
}
|
||||
}
|
||||
|
||||
class Line {
|
||||
|
|
@ -18,24 +18,24 @@ class Line {
|
|||
|
||||
fun test() -> f64 {
|
||||
let line = new Line {
|
||||
start: new Point { x: 7, y: 23 },
|
||||
start: new Point { y: 23, x: 7 },
|
||||
end: new Point { x: 999, y: 99 },
|
||||
};
|
||||
|
||||
let z = line.start.x;// + pt.square_length() + Point::something_static();
|
||||
let pt = line.start;
|
||||
let z = line.start.x + pt.square_length();// + Point::something_static();
|
||||
z
|
||||
}
|
||||
|
||||
/// @ignore WIP: Methods
|
||||
// @no-errors
|
||||
// @eval: Float(7.0)
|
||||
// @eval: Float(585.0)
|
||||
// @compiles-to:
|
||||
// | function << module >> (0 args, 0 locals):
|
||||
// | strings (0):
|
||||
// | code (2):
|
||||
// | 0: PushNothing
|
||||
// | 1: Return
|
||||
// | function Point (4 args, 0 locals):
|
||||
// | function Point (5 args, 0 locals):
|
||||
// | strings (1):
|
||||
// | 0: Point
|
||||
// | code (5):
|
||||
|
|
@ -53,9 +53,9 @@ fun test() -> f64 {
|
|||
// | 2: PushString(0)
|
||||
// | 3: NewObject(2)
|
||||
// | 4: Return
|
||||
// | function test (0 args, 2 locals):
|
||||
// | function test (0 args, 3 locals):
|
||||
// | strings (0):
|
||||
// | code (17):
|
||||
// | code (24):
|
||||
// | 0: PushFloat(99.0)
|
||||
// | 1: PushFloat(999.0)
|
||||
// | 2: LoadFunction(1)
|
||||
|
|
@ -69,8 +69,30 @@ fun test() -> f64 {
|
|||
// | 10: StoreLocal(0)
|
||||
// | 11: LoadLocal(0)
|
||||
// | 12: LoadSlot(0)
|
||||
// | 13: LoadSlot(0)
|
||||
// | 14: StoreLocal(1)
|
||||
// | 15: LoadLocal(1)
|
||||
// | 16: Return
|
||||
// | 13: StoreLocal(1)
|
||||
// | 14: LoadLocal(0)
|
||||
// | 15: LoadSlot(0)
|
||||
// | 16: LoadSlot(0)
|
||||
// | 17: LoadLocal(1)
|
||||
// | 18: LoadFunction(4)
|
||||
// | 19: Call(1)
|
||||
// | 20: FloatAdd
|
||||
// | 21: StoreLocal(2)
|
||||
// | 22: LoadLocal(2)
|
||||
// | 23: Return
|
||||
// | function square_length (1 args, 0 locals):
|
||||
// | strings (0):
|
||||
// | code (12):
|
||||
// | 0: LoadArgument(0)
|
||||
// | 1: LoadSlot(0)
|
||||
// | 2: LoadArgument(0)
|
||||
// | 3: LoadSlot(0)
|
||||
// | 4: FloatMultiply
|
||||
// | 5: LoadArgument(0)
|
||||
// | 6: LoadSlot(1)
|
||||
// | 7: LoadArgument(0)
|
||||
// | 8: LoadSlot(1)
|
||||
// | 9: FloatMultiply
|
||||
// | 10: FloatAdd
|
||||
// | 11: Return
|
||||
// |
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue