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
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());
Instruction::Panic(index)
}
@ -289,7 +282,7 @@ pub fn compile(semantics: &Semantics) -> Rc<Module> {
compiler.temp_functions.resize(idx + 1, None);
}
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));
}
@ -317,13 +310,13 @@ fn file(c: &mut Compiler, t: TreeRef) {
c.push(Instruction::Return);
}
type CR = Result<(), &'static str>;
const OK: CR = CR::Ok(());
type CR = Option<()>;
const OK: CR = CR::Some(());
fn compile_expression(c: &mut Compiler, t: TreeRef) {
let tree = &c.syntax[t];
let cr = match tree.kind {
TreeKind::Error => Err("error tree"),
TreeKind::Error => None,
TreeKind::Argument => compile_argument(c, 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"),
};
if let Err(m) = cr {
c.push_panic(format!("panic compiling expression {:?}: {m}", tree));
if matches!(cr, None) {
c.push_panic(format!("panic compiling expression {:?}", tree));
}
}
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) {
Type::F64 => c.push(Instruction::PushFloat(
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 {
compile_expression(c, t.nth_tree(1).ok_or("unexpected tree")?);
compile_expression(c, t.nth_tree(1)?);
OK
}
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 {
TokenKind::Minus => {
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 {
let condition = t.nth_tree(1).ok_or("no cond")?;
let condition = t.nth_tree(1)?;
compile_expression(c, condition);
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);
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
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);
compile_expression(c, arg_tree);
@ -434,7 +427,7 @@ where
}
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 {
TokenKind::Plus => compile_simple_binary_expression(c, tr, |c, t| match t {
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::String => Instruction::GreaterString,
_ => c.inst_panic(format!("panic less equal {}", t)),
})?;
});
c.push(Instruction::BoolNot);
OK
}
@ -475,14 +468,14 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
Type::F64 => Instruction::LessFloat,
Type::String => Instruction::LessString,
_ => c.inst_panic(format!("panic greater equal {}", t)),
})?;
});
c.push(Instruction::BoolNot);
OK
}
TokenKind::And => {
// Compile the left hand side, it leaves a bool on the stack
compile_expression(c, tr.nth_tree(0).ok_or("no lhs")?);
compile_expression(c, tr.nth_tree(0)?);
// If the first part is true (hooray!) then we need to evaluate
// 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 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.)
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 => {
// 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
// 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 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.)
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 => {
compile_expression(c, tr.nth_tree(2).ok_or("no value")?);
compile_expression(c, tr.nth_tree(2)?);
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];
#[allow(unused_assignments)]
let mut environment = Environment::error("??");
let mut environment = Environment::error();
let declaration = match ltree.kind {
// TODO: Assign to list access
TreeKind::Identifier => {
let id = ltree.nth_token(0).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.bind(id).ok_or("cannot bind destination")?
environment.bind(id)?
}
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);
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 {
@ -597,7 +590,7 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
Instruction::StoreModule(index)
}
Location::Slot => {
compile_expression(c, ltree.nth_tree(0).ok_or("no obj lhs")?);
compile_expression(c, ltree.nth_tree(0)?);
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 {
let ident = tree.nth_token(0).ok_or("no ident")?.as_str(&c.source);
fn compile_identifier_expression(c: &mut Compiler, t: TreeRef, tree: &Tree) -> Option<()> {
let ident = tree.nth_token(0)?.as_str(&c.source);
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)
}
@ -696,24 +689,22 @@ fn compile_load_declaration(c: &mut Compiler, t: TreeRef, declaration: &Declarat
OK
}
fn compile_match_expression(c: &mut Compiler, tree: &Tree) -> CR {
compile_expression(c, tree.nth_tree(1).ok_or("no val")?);
fn compile_match_expression(c: &mut Compiler, tree: &Tree) -> Option<()> {
compile_expression(c, tree.nth_tree(1)?);
let mut patches = Vec::new();
let match_body = tree
.child_tree_of_kind(c.syntax, TreeKind::MatchBody)
.ok_or("no body")?;
let match_body = tree.child_tree_of_kind(c.syntax, TreeKind::MatchBody)?;
for arm in match_body.children_of_kind(c.syntax, TreeKind::MatchArm) {
let arm = &c.syntax[arm];
// Evaluate pattern...
compile_pattern(c, arm.nth_tree(0).ok_or("no arm pat")?)?;
compile_pattern(c, arm.nth_tree(0)?)?;
// ...If false jump to next arm.
let jump_next_index = c.push(Instruction::JumpFalse(0));
// ...If true run expression and jump out.
compile_expression(c, arm.nth_tree(2).ok_or("no arm expr")?);
compile_expression(c, arm.nth_tree(2)?);
patches.push(c.push(Instruction::Jump(0)));
c.patch(jump_next_index, |i| Instruction::JumpFalse(i));
@ -728,13 +719,13 @@ fn compile_match_expression(c: &mut Compiler, tree: &Tree) -> CR {
OK
}
fn compile_is_expression(c: &mut Compiler, tree: &Tree) -> CR {
compile_expression(c, tree.nth_tree(0).ok_or("no val")?);
fn compile_is_expression(c: &mut Compiler, tree: &Tree) -> Option<()> {
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'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 let Some(binding) = tree.child_tree_of_kind(&c.syntax, TreeKind::VariableBinding) {
if let Some(variable) = binding.nth_token(0) {
let id = variable.as_str(&c.source);
let environment = c.semantics.environment_of(t);
let Some(declaration) = environment.bind(id) else {
ice!(c, t, "cannot bind pattern variable `{id}`");
};
let declaration = environment.bind(variable.as_str(&c.source))?;
let Declaration::Variable {
location: Location::Local,
@ -778,8 +766,8 @@ fn compile_pattern(c: &mut Compiler, t: TreeRef) -> CR {
}
if !is_wildcard {
let type_expr = type_expr.ok_or("wild but no type")?;
compile_type_expr_eq(c, type_expr.nth_tree(0).ok_or("no type expr")?);
let type_expr = type_expr?;
compile_type_expr_eq(c, type_expr.nth_tree(0)?);
}
if let Some(and_index) = and_index {
@ -801,7 +789,7 @@ fn compile_pattern(c: &mut Compiler, t: TreeRef) -> CR {
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 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"),
};
if let Err(m) = result {
c.push_panic(format!(
"internal error compiling type expression eq {:?}: {m}",
tree
));
if result.is_none() {
c.push_panic(format!("panic compiling type expression eq {:?}", tree));
}
}
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 {
"f64" => {
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);
match environment.bind(identifier).ok_or("cannot bind")? {
match environment.bind(identifier)? {
Declaration::Class { declaration, .. } => {
// The runtime identifier of the class is the tree index of the
// 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()));
}
_ => 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 {
// 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
// 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 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.)
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 {
let arg_list = tree
.child_tree_of_kind(&c.syntax, TreeKind::ArgumentList)
.ok_or("no arglist")?;
let arg_list = tree.child_tree_of_kind(&c.syntax, TreeKind::ArgumentList)?;
let mut args: Vec<_> = arg_list.child_trees().collect();
let arg_count = args.len();
@ -904,7 +887,7 @@ fn compile_call_expression(c: &mut Compiler, tree: &Tree) -> CR {
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 arg_count = match func_type {
// 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 };
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
}
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
}
@ -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 field_list = tree
.child_tree_of_kind(&c.syntax, TreeKind::FieldList)
.ok_or("no field list")?;
let field_list = tree.child_tree_of_kind(&c.syntax, TreeKind::FieldList)?;
let mut field_bindings = HashMap::new();
for field in field_list.children_of_kind(&c.syntax, TreeKind::FieldValue) {
let f = &c.syntax[field];
let name = f.nth_token(0).ok_or("no field name")?;
let name = f.nth_token(0)?;
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,
// obviously.
for field in class.fields.iter().rev() {
let binding = field_bindings
.get(&*field.name)
.ok_or("cannot bind field")?;
let binding = field_bindings.get(&*field.name)?;
compile_expression(c, *binding);
}
// Fetch the correct constructor.
// TODO: Binding this type should be done by semantics, and we should borrow it.
let type_reference = tree
.child_tree_of_kind(&c.syntax, TreeKind::TypeIdentifier)
.ok_or("no type ref")?;
let identifier = type_reference
.nth_token(0)
.ok_or("no type id")?
.as_str(&c.source);
let type_reference = tree.child_tree_of_kind(&c.syntax, TreeKind::TypeIdentifier)?;
let identifier = type_reference.nth_token(0)?.as_str(&c.source);
let environment = c.semantics.environment_of(t);
match environment.bind(identifier).ok_or("cannot bind type")? {
match environment.bind(identifier)? {
Declaration::Class { declaration, .. } => {
let key = FunctionKey { tree: *declaration };
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));
}
_ => return Err("unsupported type for construction"),
_ => return None,
}
c.push(Instruction::Call(class.fields.len()));
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 {
if let Some(colon) = tree.nth_token(1) {
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;
}
}
// Form 2: { x, ... }
let environment = c.semantics.environment_of(t);
let id = tree.nth_token(0).ok_or("no id")?.as_str(&c.source);
let declaration = environment.bind(id).ok_or("cannot bind")?;
let id = tree.nth_token(0)?.as_str(&c.source);
let declaration = environment.bind(id)?;
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
// binds against it. ::shrug::
//
let lhs = tree.nth_tree(0).ok_or("no lhs")?;
compile_expression(c, lhs);
compile_expression(c, tree.nth_tree(0)?);
let typ = c.semantics.type_of(lhs);
let ident = tree.nth_token(2).ok_or("no ident")?.as_str(&c.source);
let typ = c.semantics.type_of(tree.nth_tree(0)?);
let ident = tree.nth_token(2)?.as_str(&c.source);
let environment = match &typ {
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}");
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
// special here, since the load of the member function will *not*
// consume the self pointer from the stack.
compile_load_declaration(c, t, declaration)?;
compile_load_declaration(c, t, declaration);
OK
}
@ -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 {
compile_expression(c, tree.nth_tree(0).ok_or("no expr")?);
compile_expression(c, tree.nth_tree(0)?);
OK
}
fn compile_statement(c: &mut Compiler, t: TreeRef, gen_value: bool) {
let tree = &c.semantics.tree()[t];
let cr = match tree.kind {
TreeKind::Error => Err("parse error"),
TreeKind::Error => None,
TreeKind::Import => compile_import_statement(c, 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),
};
if let Err(e) = cr {
c.push_panic(format!(
"internal error compiling statement {:?}: {e}",
tree
));
if matches!(cr, None) {
c.push_panic(format!("stat {:?}", tree));
}
}
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 {
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 {
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 declaration = environment
.bind(tree.nth_token(1).ok_or("no id")?.as_str(&c.source))
.ok_or("cannot bind")?;
let declaration = environment.bind(tree.nth_token(1)?.as_str(&c.source))?;
let Declaration::Variable {
location, index, ..
@ -1177,11 +1144,9 @@ fn compile_function_declaration(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_v
let fk = FunctionKey { tree: t };
if !c.function_bindings.contains_key(&fk) {
// TODO: If this is a method the name should be different.
let name = tree.nth_token(1).ok_or("no id")?.as_str(&c.source);
let name = tree.nth_token(1)?.as_str(&c.source);
let param_list = tree
.child_tree_of_kind(&c.syntax, TreeKind::ParamList)
.ok_or("no paramlist")?;
let param_list = tree.child_tree_of_kind(&c.syntax, TreeKind::ParamList)?;
let param_count = param_list.children.len() - 2;
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.
let fk = FunctionKey { tree: t };
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;
@ -1233,9 +1198,7 @@ fn compile_function(c: &mut Compiler, t: TreeRef) -> CR {
let tree = &c.syntax[t];
match tree.kind {
TreeKind::FunctionDecl => {
let block = tree
.child_of_kind(&c.syntax, TreeKind::Block)
.ok_or("no body")?;
let block = tree.child_of_kind(&c.syntax, TreeKind::Block)?;
compile_expression(c, block);
}
TreeKind::ClassDecl => {
@ -1246,7 +1209,7 @@ fn compile_function(c: &mut Compiler, t: TreeRef) -> CR {
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());
c.push(Instruction::PushString(name_index));
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 {
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));
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.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 {
// 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 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 Some(variable_decl) = env.bind(id) else {
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.
let iterable = tree.nth_tree(3).ok_or("no generator")?;
let iterable = tree.nth_tree(3)?;
compile_expression(c, iterable);
// 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));
}
_ => 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
@ -1349,7 +1312,7 @@ fn compile_for_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR {
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'

View file

@ -431,7 +431,7 @@ pub struct Environment {
pub location: Location,
pub next_index: usize,
pub declarations: HashMap<Box<str>, Declaration>,
pub error: Option<&'static str>,
pub is_error: bool,
}
impl Environment {
@ -457,22 +457,18 @@ impl Environment {
location,
next_index,
declarations: HashMap::new(),
error: None,
is_error: false,
}
}
pub fn is_error(&self) -> bool {
self.error.is_some()
}
pub fn error(why: &'static str) -> EnvironmentRef {
pub fn error() -> EnvironmentRef {
// TODO: Exactly once?
EnvironmentRef::new(Environment {
parent: None,
location: Location::Local,
next_index: 0,
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 {
// Should really have a pattern in there; otherwise there was a
// 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.
let Some(lhs) = tree.nth_tree(0) else {
return Environment::error("no lhs (value)");
return Environment::error();
};
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 {
// Should really have a pattern in there; otherwise there was a
// 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.
@ -1184,8 +1180,8 @@ impl Semantics {
}
// The expression is the first tree child of match expression.
let Some(lhs) = tree.nth_tree(2) else {
return Environment::error("arm: no rhs (expression)");
let Some(lhs) = tree.nth_tree(1) else {
return Environment::error();
};
self.environment_of_pattern(parent, pattern, lhs)
}
@ -1205,7 +1201,7 @@ impl Semantics {
return parent;
};
let Some(variable) = binding.nth_token(0) else {
return Environment::error("no variable");
return Environment::error();
};
let is_wildcard = tree
@ -1222,7 +1218,7 @@ impl Semantics {
// match for the variable to have a value.
let Some(type_expr) = tree.child_of_kind(&self.syntax_tree, TreeKind::TypeExpression)
else {
return Environment::error("no type expression");
return Environment::error();
};
type_expr
};
@ -1662,7 +1658,7 @@ impl Semantics {
let tree = &self.syntax_tree[left_tree];
#[allow(unused_assignments)]
let mut environment = Environment::error("?");
let mut environment = Environment::error();
let declaration = match tree.kind {
// TODO: Assign to list access
@ -1672,7 +1668,7 @@ impl Semantics {
match environment.bind(id) {
Some(decl) => decl,
None => {
if !environment.is_error() {
if !environment.is_error {
self.report_error_tree(tree, format!("cannot find value {id} here"));
}
return Some(Type::Error);
@ -1686,7 +1682,7 @@ impl Semantics {
match environment.bind(id) {
Some(decl) => decl,
None => {
if !environment.is_error() {
if !environment.is_error {
self.report_error_tree(tree, format!("'{typ}' has no member {id}"));
}
return Some(Type::Error);
@ -1824,7 +1820,7 @@ impl Semantics {
.type_of_declaration(*tree, declaration),
),
None => {
if !environment.is_error() {
if !environment.is_error {
self.report_error_tree(tree, format!("Unrecognized type: '{token}'"));
}
Some(Type::Error)
@ -2050,7 +2046,7 @@ impl Semantics {
let id_str = id.as_str(&self.source);
let Some(declaration) = env.bind(id_str) else {
if !env.is_error() {
if !env.is_error {
self.report_error_span(
id.start(),
id.end(),
@ -2104,10 +2100,10 @@ impl Semantics {
}
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}'"));
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));
}
if !environment.is_error() {
if !environment.is_error {
self.report_error_tree(tree, format!("cannot find value {id} here"));
}
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");
}
Some(Type::Error)
@ -2344,7 +2340,7 @@ impl Semantics {
let declaration = match environment.bind(id) {
Some(d) => d,
None => {
if !environment.is_error() {
if !environment.is_error {
self.report_error_tree(tree, format!("cannot find value {id} here"));
}
return Some(Type::Error);
@ -2651,9 +2647,6 @@ impl Semantics {
eprintln!("\nThe environment of the tree was:");
let mut environment = Some(self.environment_of(tr));
while let Some(env) = environment {
if let Some(error) = env.error {
eprint!(" *** ERROR: {error}");
}
for (k, v) in env.declarations.iter() {
eprint!(" {k}: ");
match v {

View file

@ -46,17 +46,13 @@ class Monster {
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 {
w:RangedWeapon -> distance >= w.minRange and distance <= w.maxRange,
_ -> distance == 1
}
}
fun roll_dice(x:f64) -> f64 {
0
}
fun attack(weapon: MeleeWeapon or RangedWeapon, monster: Monster, distance: f64) {
// This is worse than Bob's final version but but it works. `is` operator
// should be the same precedence as `and` and left-associative, so the
@ -83,10 +79,10 @@ fun attack(weapon: MeleeWeapon or RangedWeapon, monster: Monster, distance: f64)
if monster.health <= damage {
print("You kill the monster!");
monster.health = 0;
monster.health = 0
} else {
print("You wound the monster.");
monster.health = monster.health - damage;
monster.health = monster.health - damage
}
}
@ -151,5 +147,6 @@ fun test() -> f64 {
// like the above.
}
// @ignore not finished yet, still compiler bugs
// @no-errors
// @eval: Float(190.0)
// @eval: Float(90.0)