diff --git a/fine/src/compiler.rs b/fine/src/compiler.rs index 8fd1d3a8..6d66ecda 100644 --- a/fine/src/compiler.rs +++ b/fine/src/compiler.rs @@ -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, diff --git a/fine/src/semantics.rs b/fine/src/semantics.rs index 578c3232..dee3f1de 100644 --- a/fine/src/semantics.rs +++ b/fine/src/semantics.rs @@ -431,7 +431,7 @@ pub struct Environment { pub location: Location, pub next_index: usize, pub declarations: HashMap, 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 { diff --git a/fine/tests/expression/alternates.fine b/fine/tests/expression/alternates.fine index 27aae472..fce7272f 100644 --- a/fine/tests/expression/alternates.fine +++ b/fine/tests/expression/alternates.fine @@ -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) \ No newline at end of file +// @eval: Float(190.0) \ No newline at end of file