[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.
This commit is contained in:
John Doty 2024-03-24 07:45:34 -07:00
parent 9ce5794c30
commit c4b4273115

View file

@ -282,7 +282,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 +310,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 +338,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 +366,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 +388,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 +413,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 +427,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 +454,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 +468,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 +493,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 +501,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 +519,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,10 +542,10 @@ 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)]
@ -554,19 +554,19 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
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 +590,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 +614,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 +689,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 +721,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....
@ -744,7 +746,9 @@ fn compile_pattern(c: &mut Compiler, t: TreeRef) -> Option<()> {
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 environment = c.semantics.environment_of(t); let environment = c.semantics.environment_of(t);
let declaration = environment.bind(variable.as_str(&c.source))?; let declaration = environment
.bind(variable.as_str(&c.source))
.ok_or("not bound")?;
let Declaration::Variable { let Declaration::Variable {
location: Location::Local, location: Location::Local,
@ -766,8 +770,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 +793,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 +816,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 +841,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 +849,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 +859,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 +877,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 +885,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 +896,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 +919,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 +939,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 +953,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 +987,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 +996,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 +1013,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 +1030,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 +1061,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 +1083,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 +1124,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 +1169,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 +1199,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 +1225,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 +1238,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 +1262,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 +1289,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 +1316,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 +1328,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 +1341,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'