diff --git a/fine/src/compiler.rs b/fine/src/compiler.rs index 05fb34e7..6d66ecda 100644 --- a/fine/src/compiler.rs +++ b/fine/src/compiler.rs @@ -177,6 +177,13 @@ impl<'a> Compiler<'a> { where T: Into, { + // TODO: We should be looking for semantic errors and using *those* + // as the panic description and only fall back to the provided + // description if we can't find a semantic error. The idea is + // that if the compiler got confused it *might* be because + // there was actually a semantic error in the program and that + // semantic error might be a better description of what is + // wrong. let index = self.add_string(description.into()); Instruction::Panic(index) } @@ -282,7 +289,7 @@ pub fn compile(semantics: &Semantics) -> Rc { compiler.temp_functions.resize(idx + 1, None); } compiler.function = func; - compile_function(&mut compiler, fk.tree); + let _ = compile_function(&mut compiler, fk.tree); compiler.temp_functions[idx] = Some(Rc::new(compiler.function)); } @@ -310,13 +317,13 @@ fn file(c: &mut Compiler, t: TreeRef) { c.push(Instruction::Return); } -type CR = Option<()>; -const OK: CR = CR::Some(()); +type CR = Result<(), &'static str>; +const OK: CR = CR::Ok(()); fn compile_expression(c: &mut Compiler, t: TreeRef) { let tree = &c.syntax[t]; let cr = match tree.kind { - TreeKind::Error => None, + TreeKind::Error => Err("error tree"), TreeKind::Argument => compile_argument(c, tree), TreeKind::BinaryExpression => compile_binary_expression(c, t, tree), @@ -338,13 +345,13 @@ fn compile_expression(c: &mut Compiler, t: TreeRef) { _ => ice!(c, t, "{tree:?} is not an expression, cannot compile"), }; - if matches!(cr, None) { - c.push_panic(format!("panic compiling expression {:?}", tree)); + if let Err(m) = cr { + c.push_panic(format!("panic compiling expression {:?}: {m}", tree)); } } fn compile_literal(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { - let tok = tr.nth_token(0)?; + let tok = tr.nth_token(0).ok_or("no token")?; match c.semantics.type_of(t) { Type::F64 => c.push(Instruction::PushFloat( tok.as_str(c.source).parse().unwrap(), @@ -366,14 +373,14 @@ fn compile_literal(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { } fn compile_grouping(c: &mut Compiler, t: &Tree) -> CR { - compile_expression(c, t.nth_tree(1)?); + compile_expression(c, t.nth_tree(1).ok_or("unexpected tree")?); OK } fn compile_unary_operator(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { - compile_expression(c, tr.nth_tree(1)?); + compile_expression(c, tr.nth_tree(1).ok_or("no arg")?); - let tok = tr.nth_token(0)?; + let tok = tr.nth_token(0).ok_or("no op")?; match tok.kind { TokenKind::Minus => { c.push(Instruction::PushFloat(-1.0)); @@ -388,12 +395,12 @@ fn compile_unary_operator(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { } fn compile_condition_expression(c: &mut Compiler, t: &Tree) -> CR { - let condition = t.nth_tree(1)?; + let condition = t.nth_tree(1).ok_or("no cond")?; compile_expression(c, condition); let jump_else_index = c.push(Instruction::JumpFalse(0)); - let then_branch = t.nth_tree(2)?; + let then_branch = t.nth_tree(2).ok_or("no then")?; compile_expression(c, then_branch); let jump_end_index = c.push(Instruction::Jump(0)); @@ -413,9 +420,9 @@ fn compile_simple_binary_expression(c: &mut Compiler, tr: &Tree, f: T) -> CR where T: FnOnce(&mut Compiler, &Type) -> Instruction, { - compile_expression(c, tr.nth_tree(0)?); + compile_expression(c, tr.nth_tree(0).ok_or("no lhs")?); - let arg_tree = tr.nth_tree(2)?; + let arg_tree = tr.nth_tree(2).ok_or("no rhs")?; let arg_type = c.semantics.type_of(arg_tree); compile_expression(c, arg_tree); @@ -427,7 +434,7 @@ where } fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { - let op = tr.nth_token(1)?; + let op = tr.nth_token(1).ok_or("no op")?; match op.kind { TokenKind::Plus => compile_simple_binary_expression(c, tr, |c, t| match t { Type::F64 => Instruction::FloatAdd, @@ -454,7 +461,7 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { Type::F64 => Instruction::GreaterFloat, Type::String => Instruction::GreaterString, _ => c.inst_panic(format!("panic less equal {}", t)), - }); + })?; c.push(Instruction::BoolNot); OK } @@ -468,14 +475,14 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { Type::F64 => Instruction::LessFloat, Type::String => Instruction::LessString, _ => c.inst_panic(format!("panic greater equal {}", t)), - }); + })?; c.push(Instruction::BoolNot); OK } TokenKind::And => { // Compile the left hand side, it leaves a bool on the stack - compile_expression(c, tr.nth_tree(0)?); + compile_expression(c, tr.nth_tree(0).ok_or("no lhs")?); // If the first part is true (hooray!) then we need to evaluate // the right hand side, so jump around the short circuit... @@ -493,7 +500,7 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { // The right hand side leaves true or false on the stack, it's // the result of the expression. - compile_expression(c, tr.nth_tree(2)?); + compile_expression(c, tr.nth_tree(2).ok_or("no rhs")?); // (here's where you go after you leave the "false" on the stack.) c.patch(jump_end_index, |i| Instruction::Jump(i)); @@ -501,7 +508,7 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { } TokenKind::Or => { // Compile the left hand side, it leaves a bool on the stack - compile_expression(c, tr.nth_tree(0)?); + compile_expression(c, tr.nth_tree(0).ok_or("no lhs")?); // If the first part is false (boo!) then we need to evaluate the // right hand side, so jump around the short circuit... @@ -519,7 +526,7 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { // The right hand side leaves true or false on the stack, it's // the result of the expression. - compile_expression(c, tr.nth_tree(2)?); + compile_expression(c, tr.nth_tree(2).ok_or("no rhs")?); // (here's where you go after you leave "true" on the stack.) c.patch(jump_end_index, |i| Instruction::Jump(i)); @@ -542,31 +549,31 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { }) } TokenKind::Equal => { - compile_expression(c, tr.nth_tree(2)?); + compile_expression(c, tr.nth_tree(2).ok_or("no value")?); c.push(Instruction::Dup); - let lvalue = tr.nth_tree(0)?; + let lvalue = tr.nth_tree(0).ok_or("no lvalue")?; 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 TreeKind::Identifier => { - let id = ltree.nth_token(0)?.as_str(&c.source); + let id = ltree.nth_token(0).ok_or("no id")?.as_str(&c.source); environment = c.semantics.environment_of(lvalue); - environment.bind(id)? + environment.bind(id).ok_or("cannot bind destination")? } TreeKind::MemberAccess => { - let id = ltree.nth_token(2)?.as_str(&c.source); + let id = ltree.nth_token(2).ok_or("no member")?.as_str(&c.source); - let t = ltree.nth_tree(0)?; + let t = ltree.nth_tree(0).ok_or("no lhs exp")?; let typ = c.semantics.type_of(t); environment = c.semantics.member_environment(t, &typ); - environment.bind(id)? + environment.bind(id).ok_or("cannot bind field")? } - _ => return None, + _ => return Err("unsupported lval expression"), }; let instruction = match declaration { @@ -590,7 +597,7 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { Instruction::StoreModule(index) } Location::Slot => { - compile_expression(c, ltree.nth_tree(0)?); + compile_expression(c, ltree.nth_tree(0).ok_or("no obj lhs")?); Instruction::StoreSlot(index) } } @@ -614,10 +621,10 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { } } -fn compile_identifier_expression(c: &mut Compiler, t: TreeRef, tree: &Tree) -> Option<()> { - let ident = tree.nth_token(0)?.as_str(&c.source); +fn compile_identifier_expression(c: &mut Compiler, t: TreeRef, tree: &Tree) -> CR { + let ident = tree.nth_token(0).ok_or("no ident")?.as_str(&c.source); let environment = c.semantics.environment_of(t); - let declaration = environment.bind(ident)?; + let declaration = environment.bind(ident).ok_or("not found")?; compile_load_declaration(c, t, declaration) } @@ -689,22 +696,24 @@ fn compile_load_declaration(c: &mut Compiler, t: TreeRef, declaration: &Declarat OK } -fn compile_match_expression(c: &mut Compiler, tree: &Tree) -> Option<()> { - compile_expression(c, tree.nth_tree(1)?); +fn compile_match_expression(c: &mut Compiler, tree: &Tree) -> CR { + compile_expression(c, tree.nth_tree(1).ok_or("no val")?); let mut patches = Vec::new(); - let match_body = tree.child_tree_of_kind(c.syntax, TreeKind::MatchBody)?; + let match_body = tree + .child_tree_of_kind(c.syntax, TreeKind::MatchBody) + .ok_or("no body")?; for arm in match_body.children_of_kind(c.syntax, TreeKind::MatchArm) { let arm = &c.syntax[arm]; // Evaluate pattern... - compile_pattern(c, arm.nth_tree(0)?)?; + compile_pattern(c, arm.nth_tree(0).ok_or("no arm pat")?)?; // ...If false jump to next arm. let jump_next_index = c.push(Instruction::JumpFalse(0)); // ...If true run expression and jump out. - compile_expression(c, arm.nth_tree(2)?); + compile_expression(c, arm.nth_tree(2).ok_or("no arm expr")?); patches.push(c.push(Instruction::Jump(0))); c.patch(jump_next_index, |i| Instruction::JumpFalse(i)); @@ -719,13 +728,13 @@ fn compile_match_expression(c: &mut Compiler, tree: &Tree) -> Option<()> { OK } -fn compile_is_expression(c: &mut Compiler, tree: &Tree) -> Option<()> { - compile_expression(c, tree.nth_tree(0)?); +fn compile_is_expression(c: &mut Compiler, tree: &Tree) -> CR { + compile_expression(c, tree.nth_tree(0).ok_or("no val")?); - compile_pattern(c, tree.nth_tree(2)?) + compile_pattern(c, tree.nth_tree(2).ok_or("no pat")?) } -fn compile_pattern(c: &mut Compiler, t: TreeRef) -> Option<()> { +fn compile_pattern(c: &mut Compiler, t: TreeRef) -> CR { let tree = &c.syntax[t]; // Let's *try* to generate good code in the presence of a wildcard pattern.... @@ -743,8 +752,11 @@ fn compile_pattern(c: &mut Compiler, t: TreeRef) -> Option<()> { // 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))?; + let Some(declaration) = environment.bind(id) else { + ice!(c, t, "cannot bind pattern variable `{id}`"); + }; let Declaration::Variable { location: Location::Local, @@ -766,8 +778,8 @@ fn compile_pattern(c: &mut Compiler, t: TreeRef) -> Option<()> { } if !is_wildcard { - let type_expr = type_expr?; - compile_type_expr_eq(c, type_expr.nth_tree(0)?); + let type_expr = type_expr.ok_or("wild but no type")?; + compile_type_expr_eq(c, type_expr.nth_tree(0).ok_or("no type expr")?); } if let Some(and_index) = and_index { @@ -789,7 +801,7 @@ fn compile_pattern(c: &mut Compiler, t: TreeRef) -> Option<()> { Some(jump_end_index) }; - compile_expression(c, tree.nth_tree(and_index + 1)?); + compile_expression(c, tree.nth_tree(and_index + 1).ok_or("no condition")?); // If we wound up with a jump what needs patching, patch it. if let Some(jump_end_index) = jump_end_index { @@ -812,13 +824,16 @@ fn compile_type_expr_eq(c: &mut Compiler, t: TreeRef) { _ => ice!(c, t, "tree is not a type expression"), }; - if result.is_none() { - c.push_panic(format!("panic compiling type expression eq {:?}", tree)); + if let Err(m) = result { + c.push_panic(format!( + "internal error compiling type expression eq {:?}: {m}", + tree + )); } } fn compile_type_identifier_eq(c: &mut Compiler, t: TreeRef, tree: &Tree) -> CR { - let identifier = tree.nth_token(0)?.as_str(&c.source); + let identifier = tree.nth_token(0).ok_or("no id")?.as_str(&c.source); match identifier { "f64" => { c.push(Instruction::IsFloat); @@ -834,7 +849,7 @@ fn compile_type_identifier_eq(c: &mut Compiler, t: TreeRef, tree: &Tree) -> CR { } _ => { let environment = c.semantics.environment_of(t); - match environment.bind(identifier)? { + match environment.bind(identifier).ok_or("cannot bind")? { Declaration::Class { declaration, .. } => { // The runtime identifier of the class is the tree index of the // class declaration sure why not. @@ -842,7 +857,7 @@ fn compile_type_identifier_eq(c: &mut Compiler, t: TreeRef, tree: &Tree) -> CR { c.push(Instruction::IsClass(index.try_into().unwrap())); } - _ => return None, + _ => return Err("unsupported type decl"), } } }; @@ -852,7 +867,7 @@ fn compile_type_identifier_eq(c: &mut Compiler, t: TreeRef, tree: &Tree) -> CR { fn compile_type_alternate_eq(c: &mut Compiler, tree: &Tree) -> CR { // Compile the left hand side, it leaves a bool on the stack - compile_type_expr_eq(c, tree.nth_tree(0)?); + compile_type_expr_eq(c, tree.nth_tree(0).ok_or("no lhs")?); // If the first part is false (boo!) then we need to evaluate the // right hand side, so jump around the short circuit... @@ -870,7 +885,7 @@ fn compile_type_alternate_eq(c: &mut Compiler, tree: &Tree) -> CR { // The right hand side leaves true or false on the stack, it's // the result of the expression. - compile_type_expr_eq(c, tree.nth_tree(2)?); + compile_type_expr_eq(c, tree.nth_tree(2).ok_or("no rhs")?); // (here's where you go after you leave "true" on the stack.) c.patch(jump_end_index, |i| Instruction::Jump(i)); @@ -878,7 +893,9 @@ fn compile_type_alternate_eq(c: &mut Compiler, tree: &Tree) -> CR { } fn compile_call_expression(c: &mut Compiler, tree: &Tree) -> CR { - let arg_list = tree.child_tree_of_kind(&c.syntax, TreeKind::ArgumentList)?; + let arg_list = tree + .child_tree_of_kind(&c.syntax, TreeKind::ArgumentList) + .ok_or("no arglist")?; let mut args: Vec<_> = arg_list.child_trees().collect(); let arg_count = args.len(); @@ -887,7 +904,7 @@ fn compile_call_expression(c: &mut Compiler, tree: &Tree) -> CR { compile_expression(c, arg); } - let func = tree.nth_tree(0)?; + let func = tree.nth_tree(0).ok_or("no func")?; let func_type = c.semantics.type_of(func); let arg_count = match func_type { // TODO: Consider being guided by syntax here? @@ -910,14 +927,14 @@ fn compile_block_expression(c: &mut Compiler, tree: &Tree) -> CR { let last_index = tree.children.len() - if last_is_brace { 2 } else { 1 }; for i in 1..last_index { - compile_statement(c, tree.nth_tree(i)?, false); + compile_statement(c, tree.nth_tree(i).ok_or("no stat")?, false); } - compile_statement(c, tree.nth_tree(last_index)?, true); + compile_statement(c, tree.nth_tree(last_index).ok_or("no last")?, true); OK } fn compile_argument(c: &mut Compiler, tree: &Tree) -> CR { - compile_expression(c, tree.nth_tree(0)?); + compile_expression(c, tree.nth_tree(0).ok_or("no expr")?); OK } @@ -930,11 +947,13 @@ fn compile_new_object_expression(c: &mut Compiler, t: TreeRef, tree: &Tree) -> C let class = c.semantics.class_of(mid, ct); - let field_list = tree.child_tree_of_kind(&c.syntax, TreeKind::FieldList)?; + let field_list = tree + .child_tree_of_kind(&c.syntax, TreeKind::FieldList) + .ok_or("no field list")?; let mut field_bindings = HashMap::new(); for field in field_list.children_of_kind(&c.syntax, TreeKind::FieldValue) { let f = &c.syntax[field]; - let name = f.nth_token(0)?; + let name = f.nth_token(0).ok_or("no field name")?; field_bindings.insert(name.as_str(&c.source), field); } @@ -942,16 +961,23 @@ fn compile_new_object_expression(c: &mut Compiler, t: TreeRef, tree: &Tree) -> C // (stack!) we compile them in reverse order. Missing fields panic, // obviously. for field in class.fields.iter().rev() { - let binding = field_bindings.get(&*field.name)?; + let binding = field_bindings + .get(&*field.name) + .ok_or("cannot bind field")?; compile_expression(c, *binding); } // Fetch the correct constructor. // TODO: Binding this type should be done by semantics, and we should borrow it. - let type_reference = tree.child_tree_of_kind(&c.syntax, TreeKind::TypeIdentifier)?; - let identifier = type_reference.nth_token(0)?.as_str(&c.source); + let type_reference = tree + .child_tree_of_kind(&c.syntax, TreeKind::TypeIdentifier) + .ok_or("no type ref")?; + let identifier = type_reference + .nth_token(0) + .ok_or("no type id")? + .as_str(&c.source); let environment = c.semantics.environment_of(t); - match environment.bind(identifier)? { + match environment.bind(identifier).ok_or("cannot bind type")? { Declaration::Class { declaration, .. } => { let key = FunctionKey { tree: *declaration }; let index = match c.function_bindings.get(&key) { @@ -969,7 +995,7 @@ fn compile_new_object_expression(c: &mut Compiler, t: TreeRef, tree: &Tree) -> C }; c.push(Instruction::LoadFunction(index)); } - _ => return None, + _ => return Err("unsupported type for construction"), } c.push(Instruction::Call(class.fields.len())); OK @@ -978,15 +1004,15 @@ fn compile_new_object_expression(c: &mut Compiler, t: TreeRef, tree: &Tree) -> C fn compile_field_value(c: &mut Compiler, t: TreeRef, tree: &Tree) -> CR { if let Some(colon) = tree.nth_token(1) { if colon.kind == TokenKind::Colon { - compile_expression(c, tree.nth_tree(2)?); + compile_expression(c, tree.nth_tree(2).ok_or("no val")?); return OK; } } // Form 2: { x, ... } let environment = c.semantics.environment_of(t); - let id = tree.nth_token(0)?.as_str(&c.source); - let declaration = environment.bind(id)?; + let id = tree.nth_token(0).ok_or("no id")?.as_str(&c.source); + let declaration = environment.bind(id).ok_or("cannot bind")?; compile_load_declaration(c, t, declaration) } @@ -995,10 +1021,11 @@ fn compile_member_access(c: &mut Compiler, t: TreeRef, 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)?); + let lhs = tree.nth_tree(0).ok_or("no lhs")?; + compile_expression(c, lhs); - let typ = c.semantics.type_of(tree.nth_tree(0)?); - let ident = tree.nth_token(2)?.as_str(&c.source); + let typ = c.semantics.type_of(lhs); + let ident = tree.nth_token(2).ok_or("no ident")?.as_str(&c.source); let environment = match &typ { Type::Object(mid, ct, _) => { @@ -1011,15 +1038,15 @@ fn compile_member_access(c: &mut Compiler, t: TreeRef, tree: &Tree) -> CR { } _ => { c.push_panic("cannot get environment of {typ}"); - return None; + return Err("cannot get environment"); } }; - let declaration = environment.bind(ident)?; + let declaration = environment.bind(ident).ok_or("cannot bind")?; // 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_load_declaration(c, t, declaration); + compile_load_declaration(c, t, declaration)?; OK } @@ -1042,14 +1069,14 @@ fn compile_list_constructor(c: &mut Compiler, tree: &Tree) -> CR { } fn compile_list_constructor_element(c: &mut Compiler, tree: &Tree) -> CR { - compile_expression(c, tree.nth_tree(0)?); + compile_expression(c, tree.nth_tree(0).ok_or("no expr")?); OK } fn compile_statement(c: &mut Compiler, t: TreeRef, gen_value: bool) { let tree = &c.semantics.tree()[t]; let cr = match tree.kind { - TreeKind::Error => None, + TreeKind::Error => Err("parse error"), TreeKind::Import => compile_import_statement(c, gen_value), TreeKind::Block => compile_block_statement(c, t, gen_value), @@ -1064,13 +1091,17 @@ fn compile_statement(c: &mut Compiler, t: TreeRef, gen_value: bool) { _ => ice!(c, t, "unsupported statement tree kind {:?}", tree.kind), }; - if matches!(cr, None) { - c.push_panic(format!("stat {:?}", tree)); + + if let Err(e) = cr { + c.push_panic(format!( + "internal error compiling statement {:?}: {e}", + tree + )); } } fn compile_if_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR { - compile_expression(c, tree.nth_tree(0)?); + compile_expression(c, tree.nth_tree(0).ok_or("no expr")?); if !gen_value { c.push(Instruction::Discard); } @@ -1101,9 +1132,11 @@ fn compile_expression_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) } fn compile_let_statement(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_value: bool) -> CR { - compile_expression(c, tree.nth_tree(3)?); + compile_expression(c, tree.nth_tree(3).ok_or("no val")?); let environment = c.semantics.environment_of(t); - let declaration = environment.bind(tree.nth_token(1)?.as_str(&c.source))?; + let declaration = environment + .bind(tree.nth_token(1).ok_or("no id")?.as_str(&c.source)) + .ok_or("cannot bind")?; let Declaration::Variable { location, index, .. @@ -1144,9 +1177,11 @@ fn compile_function_declaration(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_v 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)?.as_str(&c.source); + let name = tree.nth_token(1).ok_or("no id")?.as_str(&c.source); - let param_list = tree.child_tree_of_kind(&c.syntax, TreeKind::ParamList)?; + let param_list = tree + .child_tree_of_kind(&c.syntax, TreeKind::ParamList) + .ok_or("no paramlist")?; let param_count = param_list.children.len() - 2; let function_index = c.temp_functions.len(); @@ -1172,7 +1207,7 @@ fn compile_class_declaration(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_valu // Classes get compiled as constructor functions which get called. let fk = FunctionKey { tree: t }; if !c.function_bindings.contains_key(&fk) { - let name = tree.nth_token(1)?.as_str(&c.source); + let name = tree.nth_token(1).ok_or("no name")?.as_str(&c.source); let field_count = tree.children.len() - 2; @@ -1198,7 +1233,9 @@ fn compile_function(c: &mut Compiler, t: TreeRef) -> CR { let tree = &c.syntax[t]; match tree.kind { TreeKind::FunctionDecl => { - let block = tree.child_of_kind(&c.syntax, TreeKind::Block)?; + let block = tree + .child_of_kind(&c.syntax, TreeKind::Block) + .ok_or("no body")?; compile_expression(c, block); } TreeKind::ClassDecl => { @@ -1209,7 +1246,7 @@ fn compile_function(c: &mut Compiler, t: TreeRef) -> CR { c.push(Instruction::LoadArgument(count - 1 - i)); } - let name = tree.nth_token(1)?.as_str(&c.source); + let name = tree.nth_token(1).ok_or("no name")?.as_str(&c.source); let name_index = c.add_string(name.to_string()); c.push(Instruction::PushString(name_index)); c.push(Instruction::PushInt(t.index().try_into().unwrap())); @@ -1233,11 +1270,11 @@ fn compile_block_statement(c: &mut Compiler, t: TreeRef, gen_value: bool) -> CR fn compile_while_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR { let start_index = c.function.instructions.len(); - compile_expression(c, tree.nth_tree(1)?); + compile_expression(c, tree.nth_tree(1).ok_or("no cond")?); let jump_end_index = c.push(Instruction::JumpFalse(0)); - compile_block_statement(c, tree.nth_tree(2)?, false); + compile_block_statement(c, tree.nth_tree(2).ok_or("no body")?, false)?; c.push(Instruction::Jump(start_index)); c.patch(jump_end_index, |i| Instruction::JumpFalse(i)); @@ -1260,11 +1297,11 @@ fn compile_return_statement(c: &mut Compiler, tree: &Tree) -> CR { fn compile_for_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR { // Figure out the variable. - let vt = tree.nth_tree(1)?; + let vt = tree.nth_tree(1).ok_or("no var")?; let var = &c.syntax[vt]; - let id = var.nth_token(0)?.as_str(&c.source); + let id = var.nth_token(0).ok_or("no id")?.as_str(&c.source); - let body = tree.nth_tree(4)?; + let body = tree.nth_tree(4).ok_or("no body")?; let env = c.semantics.environment_of(body); let Some(variable_decl) = env.bind(id) else { ice!(c, body, "Unable to bind {id} in loop body"); @@ -1287,7 +1324,7 @@ fn compile_for_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR { }; // Figure out the generator. - let iterable = tree.nth_tree(3)?; + let iterable = tree.nth_tree(3).ok_or("no generator")?; compile_expression(c, iterable); // call 'get_iterator' @@ -1299,7 +1336,7 @@ fn compile_for_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR { )); c.push(Instruction::Call(1)); } - _ => return None, // TODO: Bind and call get_iterator() on type of iterable + _ => return Err("unsupported collection"), // TODO: Bind and call get_iterator() on type of iterable } // iterate @@ -1312,7 +1349,7 @@ fn compile_for_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR { EXTERN_BUILTIN_LIST_ITERATOR_NEXT, )); } - _ => return None, // TODO: Bind and call next() on type of iterator + _ => return Err("unsupported iterator"), // TODO: Bind and call next() on type of iterator } c.push(Instruction::Call(1)); // Call 'next' 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