Compare commits
No commits in common. "8779aade2437cd6a17c93ef82f4d0be920323e05" and "9ce5794c30cbb50518e5b3919799c4a51d35d427" have entirely different histories.
8779aade24
...
9ce5794c30
3 changed files with 121 additions and 168 deletions
|
|
@ -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'
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue