Compare commits

..

3 commits

Author SHA1 Message Date
8779aade24 [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.
2024-03-24 08:15:33 -07:00
2ba34701ac [fine] Note about panics and error messages 2024-03-24 07:48:23 -07:00
c4b4273115 [fine] Compiler flows error descriptions on tree errors
Like, even when the tree is malformed lets try to talk about what we
found. This is still not right, but it's better maybe.
2024-03-24 07:45:34 -07:00
3 changed files with 168 additions and 121 deletions

View file

@ -177,6 +177,13 @@ impl<'a> Compiler<'a> {
where where
T: Into<String>, T: Into<String>,
{ {
// 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()); let index = self.add_string(description.into());
Instruction::Panic(index) Instruction::Panic(index)
} }
@ -282,7 +289,7 @@ pub fn compile(semantics: &Semantics) -> Rc<Module> {
compiler.temp_functions.resize(idx + 1, None); compiler.temp_functions.resize(idx + 1, None);
} }
compiler.function = func; 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)); compiler.temp_functions[idx] = Some(Rc::new(compiler.function));
} }
@ -310,13 +317,13 @@ fn file(c: &mut Compiler, t: TreeRef) {
c.push(Instruction::Return); c.push(Instruction::Return);
} }
type CR = Option<()>; type CR = Result<(), &'static str>;
const OK: CR = CR::Some(()); const OK: CR = CR::Ok(());
fn compile_expression(c: &mut Compiler, t: TreeRef) { fn compile_expression(c: &mut Compiler, t: TreeRef) {
let tree = &c.syntax[t]; let tree = &c.syntax[t];
let cr = match tree.kind { let cr = match tree.kind {
TreeKind::Error => None, TreeKind::Error => Err("error tree"),
TreeKind::Argument => compile_argument(c, tree), TreeKind::Argument => compile_argument(c, tree),
TreeKind::BinaryExpression => compile_binary_expression(c, t, 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"), _ => ice!(c, t, "{tree:?} is not an expression, cannot compile"),
}; };
if matches!(cr, None) { if let Err(m) = cr {
c.push_panic(format!("panic compiling expression {:?}", tree)); c.push_panic(format!("panic compiling expression {:?}: {m}", tree));
} }
} }
fn compile_literal(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { 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) { match c.semantics.type_of(t) {
Type::F64 => c.push(Instruction::PushFloat( Type::F64 => c.push(Instruction::PushFloat(
tok.as_str(c.source).parse().unwrap(), 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 { 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 OK
} }
fn compile_unary_operator(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { 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 { match tok.kind {
TokenKind::Minus => { TokenKind::Minus => {
c.push(Instruction::PushFloat(-1.0)); 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 { 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); compile_expression(c, condition);
let jump_else_index = c.push(Instruction::JumpFalse(0)); 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); compile_expression(c, then_branch);
let jump_end_index = c.push(Instruction::Jump(0)); let jump_end_index = c.push(Instruction::Jump(0));
@ -413,9 +420,9 @@ fn compile_simple_binary_expression<T>(c: &mut Compiler, tr: &Tree, f: T) -> CR
where where
T: FnOnce(&mut Compiler, &Type) -> Instruction, 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); let arg_type = c.semantics.type_of(arg_tree);
compile_expression(c, arg_tree); compile_expression(c, arg_tree);
@ -427,7 +434,7 @@ where
} }
fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { 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 { match op.kind {
TokenKind::Plus => compile_simple_binary_expression(c, tr, |c, t| match t { TokenKind::Plus => compile_simple_binary_expression(c, tr, |c, t| match t {
Type::F64 => Instruction::FloatAdd, 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::F64 => Instruction::GreaterFloat,
Type::String => Instruction::GreaterString, Type::String => Instruction::GreaterString,
_ => c.inst_panic(format!("panic less equal {}", t)), _ => c.inst_panic(format!("panic less equal {}", t)),
}); })?;
c.push(Instruction::BoolNot); c.push(Instruction::BoolNot);
OK OK
} }
@ -468,14 +475,14 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
Type::F64 => Instruction::LessFloat, Type::F64 => Instruction::LessFloat,
Type::String => Instruction::LessString, Type::String => Instruction::LessString,
_ => c.inst_panic(format!("panic greater equal {}", t)), _ => c.inst_panic(format!("panic greater equal {}", t)),
}); })?;
c.push(Instruction::BoolNot); c.push(Instruction::BoolNot);
OK OK
} }
TokenKind::And => { TokenKind::And => {
// Compile the left hand side, it leaves a bool on the stack // 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 // If the first part is true (hooray!) then we need to evaluate
// the right hand side, so jump around the short circuit... // 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 right hand side leaves true or false on the stack, it's
// the result of the expression. // 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.) // (here's where you go after you leave the "false" on the stack.)
c.patch(jump_end_index, |i| Instruction::Jump(i)); 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 => { TokenKind::Or => {
// Compile the left hand side, it leaves a bool on the stack // 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 // If the first part is false (boo!) then we need to evaluate the
// right hand side, so jump around the short circuit... // 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 right hand side leaves true or false on the stack, it's
// the result of the expression. // 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.) // (here's where you go after you leave "true" on the stack.)
c.patch(jump_end_index, |i| Instruction::Jump(i)); 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 => { TokenKind::Equal => {
compile_expression(c, tr.nth_tree(2)?); compile_expression(c, tr.nth_tree(2).ok_or("no value")?);
c.push(Instruction::Dup); 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]; 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
TreeKind::Identifier => { 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 = c.semantics.environment_of(lvalue);
environment.bind(id)? environment.bind(id).ok_or("cannot bind destination")?
} }
TreeKind::MemberAccess => { 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); let typ = c.semantics.type_of(t);
environment = c.semantics.member_environment(t, &typ); 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 { let instruction = match declaration {
@ -590,7 +597,7 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
Instruction::StoreModule(index) Instruction::StoreModule(index)
} }
Location::Slot => { Location::Slot => {
compile_expression(c, ltree.nth_tree(0)?); compile_expression(c, ltree.nth_tree(0).ok_or("no obj lhs")?);
Instruction::StoreSlot(index) 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<()> { fn compile_identifier_expression(c: &mut Compiler, t: TreeRef, tree: &Tree) -> CR {
let ident = tree.nth_token(0)?.as_str(&c.source); let ident = tree.nth_token(0).ok_or("no ident")?.as_str(&c.source);
let environment = c.semantics.environment_of(t); 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) compile_load_declaration(c, t, declaration)
} }
@ -689,22 +696,24 @@ fn compile_load_declaration(c: &mut Compiler, t: TreeRef, declaration: &Declarat
OK OK
} }
fn compile_match_expression(c: &mut Compiler, tree: &Tree) -> Option<()> { fn compile_match_expression(c: &mut Compiler, tree: &Tree) -> CR {
compile_expression(c, tree.nth_tree(1)?); compile_expression(c, tree.nth_tree(1).ok_or("no val")?);
let mut patches = Vec::new(); 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) { for arm in match_body.children_of_kind(c.syntax, TreeKind::MatchArm) {
let arm = &c.syntax[arm]; let arm = &c.syntax[arm];
// Evaluate pattern... // 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. // ...If false jump to next arm.
let jump_next_index = c.push(Instruction::JumpFalse(0)); let jump_next_index = c.push(Instruction::JumpFalse(0));
// ...If true run expression and jump out. // ...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))); patches.push(c.push(Instruction::Jump(0)));
c.patch(jump_next_index, |i| Instruction::JumpFalse(i)); c.patch(jump_next_index, |i| Instruction::JumpFalse(i));
@ -719,13 +728,13 @@ fn compile_match_expression(c: &mut Compiler, tree: &Tree) -> Option<()> {
OK OK
} }
fn compile_is_expression(c: &mut Compiler, tree: &Tree) -> Option<()> { fn compile_is_expression(c: &mut Compiler, tree: &Tree) -> CR {
compile_expression(c, tree.nth_tree(0)?); 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 tree = &c.syntax[t];
// Let's *try* to generate good code in the presence of a wildcard pattern.... // 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 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.bind(variable.as_str(&c.source))?; let Some(declaration) = environment.bind(id) else {
ice!(c, t, "cannot bind pattern variable `{id}`");
};
let Declaration::Variable { let Declaration::Variable {
location: Location::Local, location: Location::Local,
@ -766,8 +778,8 @@ fn compile_pattern(c: &mut Compiler, t: TreeRef) -> Option<()> {
} }
if !is_wildcard { if !is_wildcard {
let type_expr = type_expr?; let type_expr = type_expr.ok_or("wild but no type")?;
compile_type_expr_eq(c, type_expr.nth_tree(0)?); compile_type_expr_eq(c, type_expr.nth_tree(0).ok_or("no type expr")?);
} }
if let Some(and_index) = and_index { if let Some(and_index) = and_index {
@ -789,7 +801,7 @@ fn compile_pattern(c: &mut Compiler, t: TreeRef) -> Option<()> {
Some(jump_end_index) 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 we wound up with a jump what needs patching, patch it.
if let Some(jump_end_index) = jump_end_index { 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"), _ => ice!(c, t, "tree is not a type expression"),
}; };
if result.is_none() { if let Err(m) = result {
c.push_panic(format!("panic compiling type expression eq {:?}", tree)); 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 { 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 { match identifier {
"f64" => { "f64" => {
c.push(Instruction::IsFloat); 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); let environment = c.semantics.environment_of(t);
match environment.bind(identifier)? { match environment.bind(identifier).ok_or("cannot bind")? {
Declaration::Class { declaration, .. } => { Declaration::Class { declaration, .. } => {
// The runtime identifier of the class is the tree index of the // The runtime identifier of the class is the tree index of the
// class declaration sure why not. // 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())); 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 { fn compile_type_alternate_eq(c: &mut Compiler, tree: &Tree) -> CR {
// Compile the left hand side, it leaves a bool on the stack // 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 // If the first part is false (boo!) then we need to evaluate the
// right hand side, so jump around the short circuit... // 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 right hand side leaves true or false on the stack, it's
// the result of the expression. // 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.) // (here's where you go after you leave "true" on the stack.)
c.patch(jump_end_index, |i| Instruction::Jump(i)); 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 { 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 mut args: Vec<_> = arg_list.child_trees().collect();
let arg_count = args.len(); let arg_count = args.len();
@ -887,7 +904,7 @@ fn compile_call_expression(c: &mut Compiler, tree: &Tree) -> CR {
compile_expression(c, arg); 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 func_type = c.semantics.type_of(func);
let arg_count = match func_type { let arg_count = match func_type {
// TODO: Consider being guided by syntax here? // 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 }; let last_index = tree.children.len() - if last_is_brace { 2 } else { 1 };
for i in 1..last_index { 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 OK
} }
fn compile_argument(c: &mut Compiler, tree: &Tree) -> CR { 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 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 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(); let mut field_bindings = HashMap::new();
for field in field_list.children_of_kind(&c.syntax, TreeKind::FieldValue) { for field in field_list.children_of_kind(&c.syntax, TreeKind::FieldValue) {
let f = &c.syntax[field]; 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); 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, // (stack!) we compile them in reverse order. Missing fields panic,
// obviously. // obviously.
for field in class.fields.iter().rev() { 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); compile_expression(c, *binding);
} }
// Fetch the correct constructor. // Fetch the correct constructor.
// TODO: Binding this type should be done by semantics, and we should borrow it. // 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 type_reference = tree
let identifier = type_reference.nth_token(0)?.as_str(&c.source); .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); let environment = c.semantics.environment_of(t);
match environment.bind(identifier)? { match environment.bind(identifier).ok_or("cannot bind type")? {
Declaration::Class { declaration, .. } => { Declaration::Class { declaration, .. } => {
let key = FunctionKey { tree: *declaration }; let key = FunctionKey { tree: *declaration };
let index = match c.function_bindings.get(&key) { 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)); c.push(Instruction::LoadFunction(index));
} }
_ => return None, _ => return Err("unsupported type for construction"),
} }
c.push(Instruction::Call(class.fields.len())); c.push(Instruction::Call(class.fields.len()));
OK 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 { fn compile_field_value(c: &mut Compiler, t: TreeRef, tree: &Tree) -> CR {
if let Some(colon) = tree.nth_token(1) { if let Some(colon) = tree.nth_token(1) {
if colon.kind == TokenKind::Colon { 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; return OK;
} }
} }
// Form 2: { x, ... } // Form 2: { x, ... }
let environment = c.semantics.environment_of(t); let environment = c.semantics.environment_of(t);
let id = tree.nth_token(0)?.as_str(&c.source); let id = tree.nth_token(0).ok_or("no id")?.as_str(&c.source);
let declaration = environment.bind(id)?; let declaration = environment.bind(id).ok_or("cannot bind")?;
compile_load_declaration(c, t, declaration) 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 // In member access; the lhs sets up the object and in theory the rhs
// binds against it. ::shrug:: // 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 typ = c.semantics.type_of(lhs);
let ident = tree.nth_token(2)?.as_str(&c.source); let ident = tree.nth_token(2).ok_or("no ident")?.as_str(&c.source);
let environment = match &typ { let environment = match &typ {
Type::Object(mid, ct, _) => { 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}"); 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 // 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* // special here, since the load of the member function will *not*
// consume the self pointer from the stack. // consume the self pointer from the stack.
compile_load_declaration(c, t, declaration); compile_load_declaration(c, t, declaration)?;
OK 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 { 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 OK
} }
fn compile_statement(c: &mut Compiler, t: TreeRef, gen_value: bool) { fn compile_statement(c: &mut Compiler, t: TreeRef, gen_value: bool) {
let tree = &c.semantics.tree()[t]; let tree = &c.semantics.tree()[t];
let cr = match tree.kind { let cr = match tree.kind {
TreeKind::Error => None, TreeKind::Error => Err("parse error"),
TreeKind::Import => compile_import_statement(c, gen_value), TreeKind::Import => compile_import_statement(c, gen_value),
TreeKind::Block => compile_block_statement(c, t, 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), _ => 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 { 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 { if !gen_value {
c.push(Instruction::Discard); 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 { 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 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 { let Declaration::Variable {
location, index, .. location, index, ..
@ -1144,9 +1177,11 @@ fn compile_function_declaration(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_v
let fk = FunctionKey { tree: t }; let fk = FunctionKey { tree: t };
if !c.function_bindings.contains_key(&fk) { if !c.function_bindings.contains_key(&fk) {
// TODO: If this is a method the name should be different. // 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 param_count = param_list.children.len() - 2;
let function_index = c.temp_functions.len(); 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. // Classes get compiled as constructor functions which get called.
let fk = FunctionKey { tree: t }; let fk = FunctionKey { tree: t };
if !c.function_bindings.contains_key(&fk) { 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; 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]; let tree = &c.syntax[t];
match tree.kind { match tree.kind {
TreeKind::FunctionDecl => { 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); compile_expression(c, block);
} }
TreeKind::ClassDecl => { TreeKind::ClassDecl => {
@ -1209,7 +1246,7 @@ fn compile_function(c: &mut Compiler, t: TreeRef) -> CR {
c.push(Instruction::LoadArgument(count - 1 - i)); 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()); let name_index = c.add_string(name.to_string());
c.push(Instruction::PushString(name_index)); c.push(Instruction::PushString(name_index));
c.push(Instruction::PushInt(t.index().try_into().unwrap())); 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 { fn compile_while_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR {
let start_index = c.function.instructions.len(); 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)); 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.push(Instruction::Jump(start_index));
c.patch(jump_end_index, |i| Instruction::JumpFalse(i)); 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 { fn compile_for_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR {
// Figure out the variable. // 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 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 env = c.semantics.environment_of(body);
let Some(variable_decl) = env.bind(id) else { let Some(variable_decl) = env.bind(id) else {
ice!(c, body, "Unable to bind {id} in loop body"); 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. // Figure out the generator.
let iterable = tree.nth_tree(3)?; let iterable = tree.nth_tree(3).ok_or("no generator")?;
compile_expression(c, iterable); compile_expression(c, iterable);
// call 'get_iterator' // 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)); 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 // iterate
@ -1312,7 +1349,7 @@ fn compile_for_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR {
EXTERN_BUILTIN_LIST_ITERATOR_NEXT, 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' c.push(Instruction::Call(1)); // Call 'next'

View file

@ -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 {

View file

@ -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)