[fine] Report errors with environments, fix match bug
I'm worried that this will turn into something I always have to do instead of something I just do sometimes: always provide the provenance of some error type or another. Link error types to diagnostics, etc.
This commit is contained in:
parent
2ba34701ac
commit
8779aade24
3 changed files with 41 additions and 30 deletions
|
|
@ -556,7 +556,7 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
|
||||||
let ltree = &c.syntax[lvalue];
|
let ltree = &c.syntax[lvalue];
|
||||||
|
|
||||||
#[allow(unused_assignments)]
|
#[allow(unused_assignments)]
|
||||||
let mut environment = Environment::error();
|
let mut environment = Environment::error("??");
|
||||||
|
|
||||||
let declaration = match ltree.kind {
|
let declaration = match ltree.kind {
|
||||||
// TODO: Assign to list access
|
// TODO: Assign to list access
|
||||||
|
|
@ -752,10 +752,11 @@ fn compile_pattern(c: &mut Compiler, t: TreeRef) -> CR {
|
||||||
// If you have a binding, dup and store now, it is in scope.
|
// If you have a binding, dup and store now, it is in scope.
|
||||||
if let Some(binding) = tree.child_tree_of_kind(&c.syntax, TreeKind::VariableBinding) {
|
if let Some(binding) = tree.child_tree_of_kind(&c.syntax, TreeKind::VariableBinding) {
|
||||||
if let Some(variable) = binding.nth_token(0) {
|
if let Some(variable) = binding.nth_token(0) {
|
||||||
|
let id = variable.as_str(&c.source);
|
||||||
let environment = c.semantics.environment_of(t);
|
let environment = c.semantics.environment_of(t);
|
||||||
let declaration = environment
|
let Some(declaration) = environment.bind(id) else {
|
||||||
.bind(variable.as_str(&c.source))
|
ice!(c, t, "cannot bind pattern variable `{id}`");
|
||||||
.ok_or("not bound")?;
|
};
|
||||||
|
|
||||||
let Declaration::Variable {
|
let Declaration::Variable {
|
||||||
location: Location::Local,
|
location: Location::Local,
|
||||||
|
|
|
||||||
|
|
@ -431,7 +431,7 @@ pub struct Environment {
|
||||||
pub location: Location,
|
pub location: Location,
|
||||||
pub next_index: usize,
|
pub next_index: usize,
|
||||||
pub declarations: HashMap<Box<str>, Declaration>,
|
pub declarations: HashMap<Box<str>, Declaration>,
|
||||||
pub is_error: bool,
|
pub error: Option<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Environment {
|
impl Environment {
|
||||||
|
|
@ -457,18 +457,22 @@ impl Environment {
|
||||||
location,
|
location,
|
||||||
next_index,
|
next_index,
|
||||||
declarations: HashMap::new(),
|
declarations: HashMap::new(),
|
||||||
is_error: false,
|
error: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn error() -> EnvironmentRef {
|
pub fn is_error(&self) -> bool {
|
||||||
|
self.error.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn error(why: &'static str) -> EnvironmentRef {
|
||||||
// TODO: Exactly once?
|
// TODO: Exactly once?
|
||||||
EnvironmentRef::new(Environment {
|
EnvironmentRef::new(Environment {
|
||||||
parent: None,
|
parent: None,
|
||||||
location: Location::Local,
|
location: Location::Local,
|
||||||
next_index: 0,
|
next_index: 0,
|
||||||
declarations: HashMap::new(),
|
declarations: HashMap::new(),
|
||||||
is_error: true,
|
error: Some(why),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1137,12 +1141,12 @@ impl Semantics {
|
||||||
let Some(pattern) = tree.child_tree_of_kind(&self.syntax_tree, TreeKind::Pattern) else {
|
let Some(pattern) = tree.child_tree_of_kind(&self.syntax_tree, TreeKind::Pattern) else {
|
||||||
// Should really have a pattern in there; otherwise there was a
|
// Should really have a pattern in there; otherwise there was a
|
||||||
// parse error, don't make more trouble.
|
// parse error, don't make more trouble.
|
||||||
return Environment::error();
|
return Environment::error("no rhs (pattern)");
|
||||||
};
|
};
|
||||||
|
|
||||||
// The left hand side of the `is` expression is used for wildcard types.
|
// The left hand side of the `is` expression is used for wildcard types.
|
||||||
let Some(lhs) = tree.nth_tree(0) else {
|
let Some(lhs) = tree.nth_tree(0) else {
|
||||||
return Environment::error();
|
return Environment::error("no lhs (value)");
|
||||||
};
|
};
|
||||||
self.environment_of_pattern(parent, pattern, lhs)
|
self.environment_of_pattern(parent, pattern, lhs)
|
||||||
}
|
}
|
||||||
|
|
@ -1159,7 +1163,7 @@ impl Semantics {
|
||||||
let Some(pattern) = tree.child_tree_of_kind(&self.syntax_tree, TreeKind::Pattern) else {
|
let Some(pattern) = tree.child_tree_of_kind(&self.syntax_tree, TreeKind::Pattern) else {
|
||||||
// Should really have a pattern in there; otherwise there was a
|
// Should really have a pattern in there; otherwise there was a
|
||||||
// parse error, don't make more trouble.
|
// parse error, don't make more trouble.
|
||||||
return Environment::error();
|
return Environment::error("arm: no lhs (pattern)");
|
||||||
};
|
};
|
||||||
|
|
||||||
// The expression in the match expression is the binding for the wildcard pattern.
|
// The expression in the match expression is the binding for the wildcard pattern.
|
||||||
|
|
@ -1180,8 +1184,8 @@ impl Semantics {
|
||||||
}
|
}
|
||||||
|
|
||||||
// The expression is the first tree child of match expression.
|
// The expression is the first tree child of match expression.
|
||||||
let Some(lhs) = tree.nth_tree(1) else {
|
let Some(lhs) = tree.nth_tree(2) else {
|
||||||
return Environment::error();
|
return Environment::error("arm: no rhs (expression)");
|
||||||
};
|
};
|
||||||
self.environment_of_pattern(parent, pattern, lhs)
|
self.environment_of_pattern(parent, pattern, lhs)
|
||||||
}
|
}
|
||||||
|
|
@ -1201,7 +1205,7 @@ impl Semantics {
|
||||||
return parent;
|
return parent;
|
||||||
};
|
};
|
||||||
let Some(variable) = binding.nth_token(0) else {
|
let Some(variable) = binding.nth_token(0) else {
|
||||||
return Environment::error();
|
return Environment::error("no variable");
|
||||||
};
|
};
|
||||||
|
|
||||||
let is_wildcard = tree
|
let is_wildcard = tree
|
||||||
|
|
@ -1218,7 +1222,7 @@ impl Semantics {
|
||||||
// match for the variable to have a value.
|
// match for the variable to have a value.
|
||||||
let Some(type_expr) = tree.child_of_kind(&self.syntax_tree, TreeKind::TypeExpression)
|
let Some(type_expr) = tree.child_of_kind(&self.syntax_tree, TreeKind::TypeExpression)
|
||||||
else {
|
else {
|
||||||
return Environment::error();
|
return Environment::error("no type expression");
|
||||||
};
|
};
|
||||||
type_expr
|
type_expr
|
||||||
};
|
};
|
||||||
|
|
@ -1658,7 +1662,7 @@ impl Semantics {
|
||||||
let tree = &self.syntax_tree[left_tree];
|
let tree = &self.syntax_tree[left_tree];
|
||||||
|
|
||||||
#[allow(unused_assignments)]
|
#[allow(unused_assignments)]
|
||||||
let mut environment = Environment::error();
|
let mut environment = Environment::error("?");
|
||||||
|
|
||||||
let declaration = match tree.kind {
|
let declaration = match tree.kind {
|
||||||
// TODO: Assign to list access
|
// TODO: Assign to list access
|
||||||
|
|
@ -1668,7 +1672,7 @@ impl Semantics {
|
||||||
match environment.bind(id) {
|
match environment.bind(id) {
|
||||||
Some(decl) => decl,
|
Some(decl) => decl,
|
||||||
None => {
|
None => {
|
||||||
if !environment.is_error {
|
if !environment.is_error() {
|
||||||
self.report_error_tree(tree, format!("cannot find value {id} here"));
|
self.report_error_tree(tree, format!("cannot find value {id} here"));
|
||||||
}
|
}
|
||||||
return Some(Type::Error);
|
return Some(Type::Error);
|
||||||
|
|
@ -1682,7 +1686,7 @@ impl Semantics {
|
||||||
match environment.bind(id) {
|
match environment.bind(id) {
|
||||||
Some(decl) => decl,
|
Some(decl) => decl,
|
||||||
None => {
|
None => {
|
||||||
if !environment.is_error {
|
if !environment.is_error() {
|
||||||
self.report_error_tree(tree, format!("'{typ}' has no member {id}"));
|
self.report_error_tree(tree, format!("'{typ}' has no member {id}"));
|
||||||
}
|
}
|
||||||
return Some(Type::Error);
|
return Some(Type::Error);
|
||||||
|
|
@ -1820,7 +1824,7 @@ impl Semantics {
|
||||||
.type_of_declaration(*tree, declaration),
|
.type_of_declaration(*tree, declaration),
|
||||||
),
|
),
|
||||||
None => {
|
None => {
|
||||||
if !environment.is_error {
|
if !environment.is_error() {
|
||||||
self.report_error_tree(tree, format!("Unrecognized type: '{token}'"));
|
self.report_error_tree(tree, format!("Unrecognized type: '{token}'"));
|
||||||
}
|
}
|
||||||
Some(Type::Error)
|
Some(Type::Error)
|
||||||
|
|
@ -2046,7 +2050,7 @@ impl Semantics {
|
||||||
|
|
||||||
let id_str = id.as_str(&self.source);
|
let id_str = id.as_str(&self.source);
|
||||||
let Some(declaration) = env.bind(id_str) else {
|
let Some(declaration) = env.bind(id_str) else {
|
||||||
if !env.is_error {
|
if !env.is_error() {
|
||||||
self.report_error_span(
|
self.report_error_span(
|
||||||
id.start(),
|
id.start(),
|
||||||
id.end(),
|
id.end(),
|
||||||
|
|
@ -2100,10 +2104,10 @@ impl Semantics {
|
||||||
}
|
}
|
||||||
EnvironmentRef::new(result)
|
EnvironmentRef::new(result)
|
||||||
}
|
}
|
||||||
Type::Error => return Environment::error(),
|
Type::Error => return Environment::error("error type has no members"),
|
||||||
_ => {
|
_ => {
|
||||||
self.report_error_tree_ref(t, format!("cannot access members of '{typ}'"));
|
self.report_error_tree_ref(t, format!("cannot access members of '{typ}'"));
|
||||||
return Environment::error();
|
return Environment::error("type has no members");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2140,7 +2144,7 @@ impl Semantics {
|
||||||
return Some(self.type_of_declaration(Some(t), declaration));
|
return Some(self.type_of_declaration(Some(t), declaration));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !environment.is_error {
|
if !environment.is_error() {
|
||||||
self.report_error_tree(tree, format!("cannot find value {id} here"));
|
self.report_error_tree(tree, format!("cannot find value {id} here"));
|
||||||
}
|
}
|
||||||
Some(Type::Error)
|
Some(Type::Error)
|
||||||
|
|
@ -2202,7 +2206,7 @@ impl Semantics {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if !environment.is_error {
|
if !environment.is_error() {
|
||||||
self.report_error_tree(tree, "`self` is only valid in methods");
|
self.report_error_tree(tree, "`self` is only valid in methods");
|
||||||
}
|
}
|
||||||
Some(Type::Error)
|
Some(Type::Error)
|
||||||
|
|
@ -2340,7 +2344,7 @@ impl Semantics {
|
||||||
let declaration = match environment.bind(id) {
|
let declaration = match environment.bind(id) {
|
||||||
Some(d) => d,
|
Some(d) => d,
|
||||||
None => {
|
None => {
|
||||||
if !environment.is_error {
|
if !environment.is_error() {
|
||||||
self.report_error_tree(tree, format!("cannot find value {id} here"));
|
self.report_error_tree(tree, format!("cannot find value {id} here"));
|
||||||
}
|
}
|
||||||
return Some(Type::Error);
|
return Some(Type::Error);
|
||||||
|
|
@ -2647,6 +2651,9 @@ impl Semantics {
|
||||||
eprintln!("\nThe environment of the tree was:");
|
eprintln!("\nThe environment of the tree was:");
|
||||||
let mut environment = Some(self.environment_of(tr));
|
let mut environment = Some(self.environment_of(tr));
|
||||||
while let Some(env) = environment {
|
while let Some(env) = environment {
|
||||||
|
if let Some(error) = env.error {
|
||||||
|
eprint!(" *** ERROR: {error}");
|
||||||
|
}
|
||||||
for (k, v) in env.declarations.iter() {
|
for (k, v) in env.declarations.iter() {
|
||||||
eprint!(" {k}: ");
|
eprint!(" {k}: ");
|
||||||
match v {
|
match v {
|
||||||
|
|
|
||||||
|
|
@ -46,13 +46,17 @@ class Monster {
|
||||||
|
|
||||||
fun print(x:string) {}
|
fun print(x:string) {}
|
||||||
|
|
||||||
fun in_range(weapon: MeleeWeapon or RangedWeapon, distance: f64) {
|
fun in_range(weapon: MeleeWeapon or RangedWeapon, distance: f64) -> bool {
|
||||||
match weapon {
|
match weapon {
|
||||||
w:RangedWeapon -> distance >= w.minRange and distance <= w.maxRange,
|
w:RangedWeapon -> distance >= w.minRange and distance <= w.maxRange,
|
||||||
_ -> distance == 1
|
_ -> distance == 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun roll_dice(x:f64) -> f64 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
fun attack(weapon: MeleeWeapon or RangedWeapon, monster: Monster, distance: f64) {
|
fun attack(weapon: MeleeWeapon or RangedWeapon, monster: Monster, distance: f64) {
|
||||||
// This is worse than Bob's final version but but it works. `is` operator
|
// This is worse than Bob's final version but but it works. `is` operator
|
||||||
// should be the same precedence as `and` and left-associative, so the
|
// should be the same precedence as `and` and left-associative, so the
|
||||||
|
|
@ -79,10 +83,10 @@ fun attack(weapon: MeleeWeapon or RangedWeapon, monster: Monster, distance: f64)
|
||||||
|
|
||||||
if monster.health <= damage {
|
if monster.health <= damage {
|
||||||
print("You kill the monster!");
|
print("You kill the monster!");
|
||||||
monster.health = 0
|
monster.health = 0;
|
||||||
} else {
|
} else {
|
||||||
print("You wound the monster.");
|
print("You wound the monster.");
|
||||||
monster.health = monster.health - damage
|
monster.health = monster.health - damage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -147,6 +151,5 @@ fun test() -> f64 {
|
||||||
// like the above.
|
// like the above.
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ignore not finished yet, still compiler bugs
|
|
||||||
// @no-errors
|
// @no-errors
|
||||||
// @eval: Float(90.0)
|
// @eval: Float(190.0)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue