[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];
|
||||
|
||||
#[allow(unused_assignments)]
|
||||
let mut environment = Environment::error();
|
||||
let mut environment = Environment::error("??");
|
||||
|
||||
let declaration = match ltree.kind {
|
||||
// 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 let Some(binding) = tree.child_tree_of_kind(&c.syntax, TreeKind::VariableBinding) {
|
||||
if let Some(variable) = binding.nth_token(0) {
|
||||
let id = variable.as_str(&c.source);
|
||||
let environment = c.semantics.environment_of(t);
|
||||
let declaration = environment
|
||||
.bind(variable.as_str(&c.source))
|
||||
.ok_or("not bound")?;
|
||||
let Some(declaration) = environment.bind(id) else {
|
||||
ice!(c, t, "cannot bind pattern variable `{id}`");
|
||||
};
|
||||
|
||||
let Declaration::Variable {
|
||||
location: Location::Local,
|
||||
|
|
|
|||
|
|
@ -431,7 +431,7 @@ pub struct Environment {
|
|||
pub location: Location,
|
||||
pub next_index: usize,
|
||||
pub declarations: HashMap<Box<str>, Declaration>,
|
||||
pub is_error: bool,
|
||||
pub error: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl Environment {
|
||||
|
|
@ -457,18 +457,22 @@ impl Environment {
|
|||
location,
|
||||
next_index,
|
||||
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?
|
||||
EnvironmentRef::new(Environment {
|
||||
parent: None,
|
||||
location: Location::Local,
|
||||
next_index: 0,
|
||||
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 {
|
||||
// Should really have a pattern in there; otherwise there was a
|
||||
// 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.
|
||||
let Some(lhs) = tree.nth_tree(0) else {
|
||||
return Environment::error();
|
||||
return Environment::error("no lhs (value)");
|
||||
};
|
||||
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 {
|
||||
// Should really have a pattern in there; otherwise there was a
|
||||
// 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.
|
||||
|
|
@ -1180,8 +1184,8 @@ impl Semantics {
|
|||
}
|
||||
|
||||
// The expression is the first tree child of match expression.
|
||||
let Some(lhs) = tree.nth_tree(1) else {
|
||||
return Environment::error();
|
||||
let Some(lhs) = tree.nth_tree(2) else {
|
||||
return Environment::error("arm: no rhs (expression)");
|
||||
};
|
||||
self.environment_of_pattern(parent, pattern, lhs)
|
||||
}
|
||||
|
|
@ -1201,7 +1205,7 @@ impl Semantics {
|
|||
return parent;
|
||||
};
|
||||
let Some(variable) = binding.nth_token(0) else {
|
||||
return Environment::error();
|
||||
return Environment::error("no variable");
|
||||
};
|
||||
|
||||
let is_wildcard = tree
|
||||
|
|
@ -1218,7 +1222,7 @@ impl Semantics {
|
|||
// match for the variable to have a value.
|
||||
let Some(type_expr) = tree.child_of_kind(&self.syntax_tree, TreeKind::TypeExpression)
|
||||
else {
|
||||
return Environment::error();
|
||||
return Environment::error("no type expression");
|
||||
};
|
||||
type_expr
|
||||
};
|
||||
|
|
@ -1658,7 +1662,7 @@ impl Semantics {
|
|||
let tree = &self.syntax_tree[left_tree];
|
||||
|
||||
#[allow(unused_assignments)]
|
||||
let mut environment = Environment::error();
|
||||
let mut environment = Environment::error("?");
|
||||
|
||||
let declaration = match tree.kind {
|
||||
// TODO: Assign to list access
|
||||
|
|
@ -1668,7 +1672,7 @@ impl Semantics {
|
|||
match environment.bind(id) {
|
||||
Some(decl) => decl,
|
||||
None => {
|
||||
if !environment.is_error {
|
||||
if !environment.is_error() {
|
||||
self.report_error_tree(tree, format!("cannot find value {id} here"));
|
||||
}
|
||||
return Some(Type::Error);
|
||||
|
|
@ -1682,7 +1686,7 @@ impl Semantics {
|
|||
match environment.bind(id) {
|
||||
Some(decl) => decl,
|
||||
None => {
|
||||
if !environment.is_error {
|
||||
if !environment.is_error() {
|
||||
self.report_error_tree(tree, format!("'{typ}' has no member {id}"));
|
||||
}
|
||||
return Some(Type::Error);
|
||||
|
|
@ -1820,7 +1824,7 @@ impl Semantics {
|
|||
.type_of_declaration(*tree, declaration),
|
||||
),
|
||||
None => {
|
||||
if !environment.is_error {
|
||||
if !environment.is_error() {
|
||||
self.report_error_tree(tree, format!("Unrecognized type: '{token}'"));
|
||||
}
|
||||
Some(Type::Error)
|
||||
|
|
@ -2046,7 +2050,7 @@ impl Semantics {
|
|||
|
||||
let id_str = id.as_str(&self.source);
|
||||
let Some(declaration) = env.bind(id_str) else {
|
||||
if !env.is_error {
|
||||
if !env.is_error() {
|
||||
self.report_error_span(
|
||||
id.start(),
|
||||
id.end(),
|
||||
|
|
@ -2100,10 +2104,10 @@ impl Semantics {
|
|||
}
|
||||
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}'"));
|
||||
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));
|
||||
}
|
||||
|
||||
if !environment.is_error {
|
||||
if !environment.is_error() {
|
||||
self.report_error_tree(tree, format!("cannot find value {id} here"));
|
||||
}
|
||||
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");
|
||||
}
|
||||
Some(Type::Error)
|
||||
|
|
@ -2340,7 +2344,7 @@ impl Semantics {
|
|||
let declaration = match environment.bind(id) {
|
||||
Some(d) => d,
|
||||
None => {
|
||||
if !environment.is_error {
|
||||
if !environment.is_error() {
|
||||
self.report_error_tree(tree, format!("cannot find value {id} here"));
|
||||
}
|
||||
return Some(Type::Error);
|
||||
|
|
@ -2647,6 +2651,9 @@ impl Semantics {
|
|||
eprintln!("\nThe environment of the tree was:");
|
||||
let mut environment = Some(self.environment_of(tr));
|
||||
while let Some(env) = environment {
|
||||
if let Some(error) = env.error {
|
||||
eprint!(" *** ERROR: {error}");
|
||||
}
|
||||
for (k, v) in env.declarations.iter() {
|
||||
eprint!(" {k}: ");
|
||||
match v {
|
||||
|
|
|
|||
|
|
@ -46,13 +46,17 @@ class Monster {
|
|||
|
||||
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 {
|
||||
w:RangedWeapon -> distance >= w.minRange and distance <= w.maxRange,
|
||||
_ -> distance == 1
|
||||
}
|
||||
}
|
||||
|
||||
fun roll_dice(x:f64) -> f64 {
|
||||
0
|
||||
}
|
||||
|
||||
fun attack(weapon: MeleeWeapon or RangedWeapon, monster: Monster, distance: f64) {
|
||||
// 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
|
||||
|
|
@ -79,10 +83,10 @@ fun attack(weapon: MeleeWeapon or RangedWeapon, monster: Monster, distance: f64)
|
|||
|
||||
if monster.health <= damage {
|
||||
print("You kill the monster!");
|
||||
monster.health = 0
|
||||
monster.health = 0;
|
||||
} else {
|
||||
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.
|
||||
}
|
||||
|
||||
// @ignore not finished yet, still compiler bugs
|
||||
// @no-errors
|
||||
// @eval: Float(90.0)
|
||||
// @eval: Float(190.0)
|
||||
Loading…
Add table
Add a link
Reference in a new issue