[fine] Support assignment to member, loops with iterators
Hmm it's starting to look like something.
This commit is contained in:
parent
3415b1a3f6
commit
239e859eaf
7 changed files with 360 additions and 171 deletions
|
|
@ -422,26 +422,34 @@ fn set_logical_parents(
|
|||
}
|
||||
}
|
||||
}
|
||||
TreeKind::MemberAccess => {
|
||||
// The LHS has the environment of my parent, because I set up an
|
||||
// environment containing only members of the type of the LHS. The
|
||||
// RHS has that one.
|
||||
if let Some(lhs) = tree.nth_tree(0) {
|
||||
set_logical_parents(parents, syntax_tree, lhs, parent);
|
||||
}
|
||||
if let Some(rhs) = tree.nth_tree(2) {
|
||||
set_logical_parents(parents, syntax_tree, rhs, Some(t));
|
||||
TreeKind::ConditionalExpression => {
|
||||
// Special case! The parent of the `then` clause is the
|
||||
// condition, so any variable bound by the condition is valid in
|
||||
// the `then` clause. The `else` clause and the condition itself
|
||||
// do not have the bindings in scope, obviously.
|
||||
let body_parent = if let Some(is_condition) = tree.nth_tree(1) {
|
||||
Some(is_condition)
|
||||
} else {
|
||||
Some(t)
|
||||
};
|
||||
|
||||
let then_body = tree.nth_tree(2);
|
||||
for child in &tree.children {
|
||||
match child {
|
||||
Child::Token(_) => (),
|
||||
Child::Tree(ct) => {
|
||||
if Some(*ct) == then_body {
|
||||
set_logical_parents(parents, syntax_tree, *ct, body_parent);
|
||||
} else {
|
||||
set_logical_parents(parents, syntax_tree, *ct, Some(t));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TreeKind::ConditionalExpression => {
|
||||
// Special case! If the condition expression is a `is` expression
|
||||
// then it is the parent for the body of the `then` clause so
|
||||
// that any variable bindings it makes remain in scope. The
|
||||
// `else` clause and the condition itself do not have the
|
||||
// bindings in scope, obviously.
|
||||
let body_parent = if let Some(is_condition) =
|
||||
tree.child_of_kind(syntax_tree, TreeKind::IsExpression)
|
||||
{
|
||||
TreeKind::WhileStatement => {
|
||||
// Just like `if`, bindings in the condition are valid in the body.
|
||||
let body_parent = if let Some(is_condition) = tree.nth_tree(1) {
|
||||
Some(is_condition)
|
||||
} else {
|
||||
Some(t)
|
||||
|
|
@ -640,7 +648,6 @@ impl<'a> Semantics<'a> {
|
|||
TreeKind::IsExpression => self.environment_of_is_expression(parent, tree),
|
||||
TreeKind::LetStatement => self.environment_of_let(parent, tree),
|
||||
TreeKind::MatchArm => self.environment_of_match_arm(parent, t, tree),
|
||||
TreeKind::MemberAccess => self.environment_of_member_access(tree),
|
||||
TreeKind::ParamList => self.environment_of_paramlist(parent, tree),
|
||||
|
||||
_ => parent,
|
||||
|
|
@ -820,35 +827,6 @@ impl<'a> Semantics<'a> {
|
|||
parent
|
||||
}
|
||||
|
||||
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 Environment::error();
|
||||
};
|
||||
let Some(op) = tree.nth_token(1) else {
|
||||
return Environment::error();
|
||||
};
|
||||
let typ = self.type_of(lhs);
|
||||
match &typ {
|
||||
Type::Object(ct, _) => {
|
||||
let class = self.class_of(*ct);
|
||||
class.env.clone()
|
||||
}
|
||||
Type::Class(ct, _) => {
|
||||
let class = self.class_of(*ct);
|
||||
class.static_env.clone()
|
||||
}
|
||||
Type::Error => Environment::error(),
|
||||
_ => {
|
||||
// TODO: This is probably wrong, yeah?
|
||||
self.report_error(op.start, format!("cannot access members of '{typ}'"));
|
||||
Environment::error()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn environment_of_is_expression(&self, parent: EnvironmentRef, tree: &Tree) -> EnvironmentRef {
|
||||
assert_eq!(tree.kind, TreeKind::IsExpression);
|
||||
|
||||
|
|
@ -1156,7 +1134,7 @@ impl<'a> Semantics<'a> {
|
|||
TreeKind::MatchArm => self.type_of_match_arm(tree),
|
||||
TreeKind::MatchBody => self.type_of_match_body(tree),
|
||||
TreeKind::MatchExpression => self.type_of_match_expression(tree),
|
||||
TreeKind::MemberAccess => self.type_of_member_access(tree),
|
||||
TreeKind::MemberAccess => self.type_of_member_access(t, tree),
|
||||
TreeKind::NewObjectExpression => self.type_of_new_object_expression(tree),
|
||||
TreeKind::Parameter => self.type_of_parameter(tree),
|
||||
TreeKind::ReturnStatement => Some(Type::Unreachable),
|
||||
|
|
@ -1293,40 +1271,69 @@ impl<'a> Semantics<'a> {
|
|||
op: &Token,
|
||||
) -> Option<Type> {
|
||||
// Ensure the left tree is an lvalue
|
||||
let environment = self.environment_of(left_tree);
|
||||
let tree = &self.syntax_tree[left_tree];
|
||||
|
||||
#[allow(unused_assignments)]
|
||||
let mut environment = Environment::error();
|
||||
|
||||
let declaration = match tree.kind {
|
||||
// TODO: Assign to member access or list access
|
||||
TreeKind::Identifier => environment.bind(tree.nth_token(0)?),
|
||||
_ => None,
|
||||
};
|
||||
match declaration {
|
||||
Some(d) => match d {
|
||||
Declaration::Variable { .. } => (),
|
||||
Declaration::ExternFunction { .. } | Declaration::Function { .. } => {
|
||||
self.report_error_tree_ref(
|
||||
left_tree,
|
||||
"cannot assign a new value to a function declaration",
|
||||
);
|
||||
return Some(Type::Error);
|
||||
// TODO: Assign to list access
|
||||
TreeKind::Identifier => {
|
||||
let id = tree.nth_token(0)?;
|
||||
environment = self.environment_of(left_tree);
|
||||
match environment.bind(id) {
|
||||
Some(decl) => decl,
|
||||
None => {
|
||||
if !environment.is_error {
|
||||
self.report_error_tree(tree, format!("cannot find value {id} here"));
|
||||
}
|
||||
return Some(Type::Error);
|
||||
}
|
||||
}
|
||||
Declaration::Class { .. } => {
|
||||
self.report_error_tree_ref(
|
||||
left_tree,
|
||||
"cannot assign a new value to a class declaration",
|
||||
);
|
||||
return Some(Type::Error);
|
||||
}
|
||||
TreeKind::MemberAccess => {
|
||||
let id = tree.nth_token(2)?;
|
||||
let typ = self.type_of(tree.nth_tree(0)?);
|
||||
environment = self.member_environment(left_tree, &typ);
|
||||
match environment.bind(id) {
|
||||
Some(decl) => decl,
|
||||
None => {
|
||||
if !environment.is_error {
|
||||
self.report_error_tree(tree, format!("'{typ}' has no member {id}"));
|
||||
}
|
||||
return Some(Type::Error);
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {
|
||||
}
|
||||
_ => {
|
||||
self.report_error_tree_ref(
|
||||
left_tree,
|
||||
"cannot assign a value to this expression, it is not a place you can store things",
|
||||
);
|
||||
return Some(Type::Error);
|
||||
}
|
||||
};
|
||||
|
||||
match declaration {
|
||||
Declaration::Variable { .. } => (),
|
||||
Declaration::ExternFunction { .. } | Declaration::Function { .. } => {
|
||||
self.report_error_tree_ref(
|
||||
left_tree,
|
||||
"cannot assign a new value to a function declaration",
|
||||
);
|
||||
return Some(Type::Error);
|
||||
}
|
||||
Declaration::Class { .. } => {
|
||||
self.report_error_tree_ref(
|
||||
left_tree,
|
||||
"cannot assign a new value to a class declaration",
|
||||
);
|
||||
return Some(Type::Error);
|
||||
}
|
||||
}
|
||||
|
||||
let _ = environment;
|
||||
|
||||
let left_type = match left_type {
|
||||
Type::Assignment(x) => *x,
|
||||
t => t,
|
||||
|
|
@ -1629,11 +1636,43 @@ impl<'a> Semantics<'a> {
|
|||
Some(result)
|
||||
}
|
||||
|
||||
fn type_of_member_access(&self, tree: &Tree) -> Option<Type> {
|
||||
fn type_of_member_access(&self, t: TreeRef, tree: &Tree) -> Option<Type> {
|
||||
assert_eq!(tree.kind, TreeKind::MemberAccess);
|
||||
|
||||
// Type of member access is the type of the RHS.
|
||||
Some(self.type_of(tree.nth_tree(2)?))
|
||||
let lhs = tree.nth_tree(0)?;
|
||||
let typ = self.type_of(lhs);
|
||||
let env = self.member_environment(lhs, &typ);
|
||||
let id = tree.nth_token(2)?;
|
||||
if id.kind != TokenKind::Identifier {
|
||||
return Some(Type::Error);
|
||||
}
|
||||
|
||||
let Some(declaration) = env.bind(id) else {
|
||||
if !env.is_error {
|
||||
self.report_error(id.start, format!("'{typ}' has no member {id}"));
|
||||
}
|
||||
return Some(Type::Error);
|
||||
};
|
||||
|
||||
Some(self.type_of_declaration(t, declaration))
|
||||
}
|
||||
|
||||
pub fn member_environment(&self, t: TreeRef, typ: &Type) -> EnvironmentRef {
|
||||
match &typ {
|
||||
Type::Object(ct, _) => {
|
||||
let class = self.class_of(*ct);
|
||||
class.env.clone()
|
||||
}
|
||||
Type::Class(ct, _) => {
|
||||
let class = self.class_of(*ct);
|
||||
class.static_env.clone()
|
||||
}
|
||||
Type::Error => return Environment::error(),
|
||||
_ => {
|
||||
self.report_error_tree_ref(t, format!("cannot access members of '{typ}'"));
|
||||
return Environment::error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn type_of_expression_statement(&self, tree: &Tree) -> Option<Type> {
|
||||
|
|
@ -1665,17 +1704,7 @@ impl<'a> Semantics<'a> {
|
|||
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, .. } => self.type_of(*declaration),
|
||||
Declaration::Function { declaration, .. } => self.type_of(*declaration),
|
||||
Declaration::ExternFunction {
|
||||
declaration_type, ..
|
||||
} => declaration_type.clone(),
|
||||
Declaration::Class { declaration, .. } => match self.type_of(*declaration) {
|
||||
Type::Object(cd, name) => Type::Class(cd, name.clone()),
|
||||
_ => self.internal_compiler_error(Some(t), "bound to a class not understood"),
|
||||
},
|
||||
});
|
||||
return Some(self.type_of_declaration(t, declaration));
|
||||
}
|
||||
|
||||
if !environment.is_error {
|
||||
|
|
@ -1684,6 +1713,20 @@ impl<'a> Semantics<'a> {
|
|||
Some(Type::Error)
|
||||
}
|
||||
|
||||
fn type_of_declaration(&self, t: TreeRef, declaration: &Declaration) -> Type {
|
||||
match declaration {
|
||||
Declaration::Variable { declaration, .. } => self.type_of(*declaration),
|
||||
Declaration::Function { declaration, .. } => self.type_of(*declaration),
|
||||
Declaration::ExternFunction {
|
||||
declaration_type, ..
|
||||
} => declaration_type.clone(),
|
||||
Declaration::Class { declaration, .. } => match self.type_of(*declaration) {
|
||||
Type::Object(cd, name) => Type::Class(cd, name.clone()),
|
||||
_ => self.internal_compiler_error(Some(t), "bound to a class not understood"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn type_of_self_parameter(&self, tree: &Tree) -> Option<Type> {
|
||||
let pl = tree.parent?;
|
||||
let param_list = &self.syntax_tree[pl];
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue