Compare commits

..

No commits in common. "8779aade2437cd6a17c93ef82f4d0be920323e05" and "9ce5794c30cbb50518e5b3919799c4a51d35d427" have entirely different histories.

3 changed files with 121 additions and 168 deletions

View file

@ -177,13 +177,6 @@ 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)
} }
@ -289,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;
let _ = compile_function(&mut compiler, fk.tree); compile_function(&mut compiler, fk.tree);
compiler.temp_functions[idx] = Some(Rc::new(compiler.function)); compiler.temp_functions[idx] = Some(Rc::new(compiler.function));
} }
@ -317,13 +310,13 @@ fn file(c: &mut Compiler, t: TreeRef) {
c.push(Instruction::Return); c.push(Instruction::Return);
} }
type CR = Result<(), &'static str>; type CR = Option<()>;
const OK: CR = CR::Ok(()); const OK: CR = CR::Some(());
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 => Err("error tree"), TreeKind::Error => None,
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),
@ -345,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 let Err(m) = cr { if matches!(cr, None) {
c.push_panic(format!("panic compiling expression {:?}: {m}", tree)); c.push_panic(format!("panic compiling expression {:?}", 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).ok_or("no token")?; let tok = tr.nth_token(0)?;
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(),
@ -373,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).ok_or("unexpected tree")?); compile_expression(c, t.nth_tree(1)?);
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).ok_or("no arg")?); compile_expression(c, tr.nth_tree(1)?);
let tok = tr.nth_token(0).ok_or("no op")?; let tok = tr.nth_token(0)?;
match tok.kind { match tok.kind {
TokenKind::Minus => { TokenKind::Minus => {
c.push(Instruction::PushFloat(-1.0)); c.push(Instruction::PushFloat(-1.0));
@ -395,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).ok_or("no cond")?; let condition = t.nth_tree(1)?;
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).ok_or("no then")?; let then_branch = t.nth_tree(2)?;
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));
@ -420,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).ok_or("no lhs")?); compile_expression(c, tr.nth_tree(0)?);
let arg_tree = tr.nth_tree(2).ok_or("no rhs")?; let arg_tree = tr.nth_tree(2)?;
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);
@ -434,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).ok_or("no op")?; let op = tr.nth_token(1)?;
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,
@ -461,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
} }
@ -475,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).ok_or("no lhs")?); compile_expression(c, tr.nth_tree(0)?);
// 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...
@ -500,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).ok_or("no rhs")?); compile_expression(c, tr.nth_tree(2)?);
// (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));
@ -508,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).ok_or("no lhs")?); compile_expression(c, tr.nth_tree(0)?);
// 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...
@ -526,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).ok_or("no rhs")?); compile_expression(c, tr.nth_tree(2)?);
// (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));
@ -549,31 +542,31 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
}) })
} }
TokenKind::Equal => { TokenKind::Equal => {
compile_expression(c, tr.nth_tree(2).ok_or("no value")?); compile_expression(c, tr.nth_tree(2)?);
c.push(Instruction::Dup); c.push(Instruction::Dup);
let lvalue = tr.nth_tree(0).ok_or("no lvalue")?; let lvalue = tr.nth_tree(0)?;
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).ok_or("no id")?.as_str(&c.source); let id = ltree.nth_token(0)?.as_str(&c.source);
environment = c.semantics.environment_of(lvalue); environment = c.semantics.environment_of(lvalue);
environment.bind(id).ok_or("cannot bind destination")? environment.bind(id)?
} }
TreeKind::MemberAccess => { TreeKind::MemberAccess => {
let id = ltree.nth_token(2).ok_or("no member")?.as_str(&c.source); let id = ltree.nth_token(2)?.as_str(&c.source);
let t = ltree.nth_tree(0).ok_or("no lhs exp")?; let t = ltree.nth_tree(0)?;
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).ok_or("cannot bind field")? environment.bind(id)?
} }
_ => return Err("unsupported lval expression"), _ => return None,
}; };
let instruction = match declaration { let instruction = match declaration {
@ -597,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).ok_or("no obj lhs")?); compile_expression(c, ltree.nth_tree(0)?);
Instruction::StoreSlot(index) Instruction::StoreSlot(index)
} }
} }
@ -621,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) -> CR { fn compile_identifier_expression(c: &mut Compiler, t: TreeRef, tree: &Tree) -> Option<()> {
let ident = tree.nth_token(0).ok_or("no ident")?.as_str(&c.source); let ident = tree.nth_token(0)?.as_str(&c.source);
let environment = c.semantics.environment_of(t); let environment = c.semantics.environment_of(t);
let declaration = environment.bind(ident).ok_or("not found")?; let declaration = environment.bind(ident)?;
compile_load_declaration(c, t, declaration) compile_load_declaration(c, t, declaration)
} }
@ -696,24 +689,22 @@ fn compile_load_declaration(c: &mut Compiler, t: TreeRef, declaration: &Declarat
OK OK
} }
fn compile_match_expression(c: &mut Compiler, tree: &Tree) -> CR { fn compile_match_expression(c: &mut Compiler, tree: &Tree) -> Option<()> {
compile_expression(c, tree.nth_tree(1).ok_or("no val")?); compile_expression(c, tree.nth_tree(1)?);
let mut patches = Vec::new(); let mut patches = Vec::new();
let match_body = tree let match_body = tree.child_tree_of_kind(c.syntax, TreeKind::MatchBody)?;
.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).ok_or("no arm pat")?)?; compile_pattern(c, arm.nth_tree(0)?)?;
// ...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).ok_or("no arm expr")?); compile_expression(c, arm.nth_tree(2)?);
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));
@ -728,13 +719,13 @@ fn compile_match_expression(c: &mut Compiler, tree: &Tree) -> CR {
OK OK
} }
fn compile_is_expression(c: &mut Compiler, tree: &Tree) -> CR { fn compile_is_expression(c: &mut Compiler, tree: &Tree) -> Option<()> {
compile_expression(c, tree.nth_tree(0).ok_or("no val")?); compile_expression(c, tree.nth_tree(0)?);
compile_pattern(c, tree.nth_tree(2).ok_or("no pat")?) compile_pattern(c, tree.nth_tree(2)?)
} }
fn compile_pattern(c: &mut Compiler, t: TreeRef) -> CR { fn compile_pattern(c: &mut Compiler, t: TreeRef) -> Option<()> {
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....
@ -752,11 +743,8 @@ fn compile_pattern(c: &mut Compiler, t: TreeRef) -> CR {
// 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 Some(declaration) = environment.bind(id) else { let declaration = environment.bind(variable.as_str(&c.source))?;
ice!(c, t, "cannot bind pattern variable `{id}`");
};
let Declaration::Variable { let Declaration::Variable {
location: Location::Local, location: Location::Local,
@ -778,8 +766,8 @@ fn compile_pattern(c: &mut Compiler, t: TreeRef) -> CR {
} }
if !is_wildcard { if !is_wildcard {
let type_expr = type_expr.ok_or("wild but no type")?; let type_expr = type_expr?;
compile_type_expr_eq(c, type_expr.nth_tree(0).ok_or("no type expr")?); compile_type_expr_eq(c, type_expr.nth_tree(0)?);
} }
if let Some(and_index) = and_index { if let Some(and_index) = and_index {
@ -801,7 +789,7 @@ fn compile_pattern(c: &mut Compiler, t: TreeRef) -> CR {
Some(jump_end_index) Some(jump_end_index)
}; };
compile_expression(c, tree.nth_tree(and_index + 1).ok_or("no condition")?); compile_expression(c, tree.nth_tree(and_index + 1)?);
// 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 {
@ -824,16 +812,13 @@ 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 let Err(m) = result { if result.is_none() {
c.push_panic(format!( c.push_panic(format!("panic compiling type expression eq {:?}", tree));
"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).ok_or("no id")?.as_str(&c.source); let identifier = tree.nth_token(0)?.as_str(&c.source);
match identifier { match identifier {
"f64" => { "f64" => {
c.push(Instruction::IsFloat); c.push(Instruction::IsFloat);
@ -849,7 +834,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).ok_or("cannot bind")? { match environment.bind(identifier)? {
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.
@ -857,7 +842,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 Err("unsupported type decl"), _ => return None,
} }
} }
}; };
@ -867,7 +852,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).ok_or("no lhs")?); compile_type_expr_eq(c, tree.nth_tree(0)?);
// 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...
@ -885,7 +870,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).ok_or("no rhs")?); compile_type_expr_eq(c, tree.nth_tree(2)?);
// (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));
@ -893,9 +878,7 @@ 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 let arg_list = tree.child_tree_of_kind(&c.syntax, TreeKind::ArgumentList)?;
.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();
@ -904,7 +887,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).ok_or("no func")?; let func = tree.nth_tree(0)?;
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?
@ -927,14 +910,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).ok_or("no stat")?, false); compile_statement(c, tree.nth_tree(i)?, false);
} }
compile_statement(c, tree.nth_tree(last_index).ok_or("no last")?, true); compile_statement(c, tree.nth_tree(last_index)?, 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).ok_or("no expr")?); compile_expression(c, tree.nth_tree(0)?);
OK OK
} }
@ -947,13 +930,11 @@ 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 let field_list = tree.child_tree_of_kind(&c.syntax, TreeKind::FieldList)?;
.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).ok_or("no field name")?; let name = f.nth_token(0)?;
field_bindings.insert(name.as_str(&c.source), field); field_bindings.insert(name.as_str(&c.source), field);
} }
@ -961,23 +942,16 @@ 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 let binding = field_bindings.get(&*field.name)?;
.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 let type_reference = tree.child_tree_of_kind(&c.syntax, TreeKind::TypeIdentifier)?;
.child_tree_of_kind(&c.syntax, TreeKind::TypeIdentifier) let identifier = type_reference.nth_token(0)?.as_str(&c.source);
.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).ok_or("cannot bind type")? { match environment.bind(identifier)? {
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) {
@ -995,7 +969,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 Err("unsupported type for construction"), _ => return None,
} }
c.push(Instruction::Call(class.fields.len())); c.push(Instruction::Call(class.fields.len()));
OK OK
@ -1004,15 +978,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).ok_or("no val")?); compile_expression(c, tree.nth_tree(2)?);
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).ok_or("no id")?.as_str(&c.source); let id = tree.nth_token(0)?.as_str(&c.source);
let declaration = environment.bind(id).ok_or("cannot bind")?; let declaration = environment.bind(id)?;
compile_load_declaration(c, t, declaration) compile_load_declaration(c, t, declaration)
} }
@ -1021,11 +995,10 @@ 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::
// //
let lhs = tree.nth_tree(0).ok_or("no lhs")?; compile_expression(c, tree.nth_tree(0)?);
compile_expression(c, lhs);
let typ = c.semantics.type_of(lhs); let typ = c.semantics.type_of(tree.nth_tree(0)?);
let ident = tree.nth_token(2).ok_or("no ident")?.as_str(&c.source); let ident = tree.nth_token(2)?.as_str(&c.source);
let environment = match &typ { let environment = match &typ {
Type::Object(mid, ct, _) => { Type::Object(mid, ct, _) => {
@ -1038,15 +1011,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 Err("cannot get environment"); return None;
} }
}; };
let declaration = environment.bind(ident).ok_or("cannot bind")?; let declaration = environment.bind(ident)?;
// 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
} }
@ -1069,14 +1042,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).ok_or("no expr")?); compile_expression(c, tree.nth_tree(0)?);
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 => Err("parse error"), TreeKind::Error => None,
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),
@ -1091,17 +1064,13 @@ 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) {
if let Err(e) = cr { c.push_panic(format!("stat {:?}", tree));
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).ok_or("no expr")?); compile_expression(c, tree.nth_tree(0)?);
if !gen_value { if !gen_value {
c.push(Instruction::Discard); c.push(Instruction::Discard);
} }
@ -1132,11 +1101,9 @@ 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).ok_or("no val")?); compile_expression(c, tree.nth_tree(3)?);
let environment = c.semantics.environment_of(t); let environment = c.semantics.environment_of(t);
let declaration = environment let declaration = environment.bind(tree.nth_token(1)?.as_str(&c.source))?;
.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, ..
@ -1177,11 +1144,9 @@ 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).ok_or("no id")?.as_str(&c.source); let name = tree.nth_token(1)?.as_str(&c.source);
let param_list = tree let param_list = tree.child_tree_of_kind(&c.syntax, TreeKind::ParamList)?;
.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();
@ -1207,7 +1172,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).ok_or("no name")?.as_str(&c.source); let name = tree.nth_token(1)?.as_str(&c.source);
let field_count = tree.children.len() - 2; let field_count = tree.children.len() - 2;
@ -1233,9 +1198,7 @@ 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 let block = tree.child_of_kind(&c.syntax, TreeKind::Block)?;
.child_of_kind(&c.syntax, TreeKind::Block)
.ok_or("no body")?;
compile_expression(c, block); compile_expression(c, block);
} }
TreeKind::ClassDecl => { TreeKind::ClassDecl => {
@ -1246,7 +1209,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).ok_or("no name")?.as_str(&c.source); let name = tree.nth_token(1)?.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()));
@ -1270,11 +1233,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).ok_or("no cond")?); compile_expression(c, tree.nth_tree(1)?);
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).ok_or("no body")?, false)?; compile_block_statement(c, tree.nth_tree(2)?, 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));
@ -1297,11 +1260,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).ok_or("no var")?; let vt = tree.nth_tree(1)?;
let var = &c.syntax[vt]; let var = &c.syntax[vt];
let id = var.nth_token(0).ok_or("no id")?.as_str(&c.source); let id = var.nth_token(0)?.as_str(&c.source);
let body = tree.nth_tree(4).ok_or("no body")?; let body = tree.nth_tree(4)?;
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");
@ -1324,7 +1287,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).ok_or("no generator")?; let iterable = tree.nth_tree(3)?;
compile_expression(c, iterable); compile_expression(c, iterable);
// call 'get_iterator' // call 'get_iterator'
@ -1336,7 +1299,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 Err("unsupported collection"), // TODO: Bind and call get_iterator() on type of iterable _ => return None, // TODO: Bind and call get_iterator() on type of iterable
} }
// iterate // iterate
@ -1349,7 +1312,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 Err("unsupported iterator"), // TODO: Bind and call next() on type of iterator _ => return None, // 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 error: Option<&'static str>, pub is_error: bool,
} }
impl Environment { impl Environment {
@ -457,22 +457,18 @@ impl Environment {
location, location,
next_index, next_index,
declarations: HashMap::new(), declarations: HashMap::new(),
error: None, is_error: false,
} }
} }
pub fn is_error(&self) -> bool { pub fn error() -> EnvironmentRef {
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(),
error: Some(why), is_error: true,
}) })
} }
@ -1141,12 +1137,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("no rhs (pattern)"); return Environment::error();
}; };
// 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("no lhs (value)"); return Environment::error();
}; };
self.environment_of_pattern(parent, pattern, lhs) self.environment_of_pattern(parent, pattern, lhs)
} }
@ -1163,7 +1159,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("arm: no lhs (pattern)"); return Environment::error();
}; };
// 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.
@ -1184,8 +1180,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(2) else { let Some(lhs) = tree.nth_tree(1) else {
return Environment::error("arm: no rhs (expression)"); return Environment::error();
}; };
self.environment_of_pattern(parent, pattern, lhs) self.environment_of_pattern(parent, pattern, lhs)
} }
@ -1205,7 +1201,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("no variable"); return Environment::error();
}; };
let is_wildcard = tree let is_wildcard = tree
@ -1222,7 +1218,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("no type expression"); return Environment::error();
}; };
type_expr type_expr
}; };
@ -1662,7 +1658,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
@ -1672,7 +1668,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);
@ -1686,7 +1682,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);
@ -1824,7 +1820,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)
@ -2050,7 +2046,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(),
@ -2104,10 +2100,10 @@ impl Semantics {
} }
EnvironmentRef::new(result) EnvironmentRef::new(result)
} }
Type::Error => return Environment::error("error type has no members"), Type::Error => return Environment::error(),
_ => { _ => {
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("type has no members"); return Environment::error();
} }
} }
} }
@ -2144,7 +2140,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)
@ -2206,7 +2202,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)
@ -2344,7 +2340,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);
@ -2651,9 +2647,6 @@ 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,17 +46,13 @@ class Monster {
fun print(x:string) {} fun print(x:string) {}
fun in_range(weapon: MeleeWeapon or RangedWeapon, distance: f64) -> bool { fun in_range(weapon: MeleeWeapon or RangedWeapon, distance: f64) {
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
@ -83,10 +79,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
} }
} }
@ -151,5 +147,6 @@ fun test() -> f64 {
// like the above. // like the above.
} }
// @ignore not finished yet, still compiler bugs
// @no-errors // @no-errors
// @eval: Float(190.0) // @eval: Float(90.0)