Compare commits
3 commits
9ce5794c30
...
8779aade24
| Author | SHA1 | Date | |
|---|---|---|---|
| 8779aade24 | |||
| 2ba34701ac | |||
| c4b4273115 |
3 changed files with 168 additions and 121 deletions
|
|
@ -177,6 +177,13 @@ impl<'a> Compiler<'a> {
|
||||||
where
|
where
|
||||||
T: Into<String>,
|
T: Into<String>,
|
||||||
{
|
{
|
||||||
|
// TODO: We should be looking for semantic errors and using *those*
|
||||||
|
// as the panic description and only fall back to the provided
|
||||||
|
// description if we can't find a semantic error. The idea is
|
||||||
|
// that if the compiler got confused it *might* be because
|
||||||
|
// there was actually a semantic error in the program and that
|
||||||
|
// semantic error might be a better description of what is
|
||||||
|
// wrong.
|
||||||
let index = self.add_string(description.into());
|
let index = self.add_string(description.into());
|
||||||
Instruction::Panic(index)
|
Instruction::Panic(index)
|
||||||
}
|
}
|
||||||
|
|
@ -282,7 +289,7 @@ pub fn compile(semantics: &Semantics) -> Rc<Module> {
|
||||||
compiler.temp_functions.resize(idx + 1, None);
|
compiler.temp_functions.resize(idx + 1, None);
|
||||||
}
|
}
|
||||||
compiler.function = func;
|
compiler.function = func;
|
||||||
compile_function(&mut compiler, fk.tree);
|
let _ = compile_function(&mut compiler, fk.tree);
|
||||||
compiler.temp_functions[idx] = Some(Rc::new(compiler.function));
|
compiler.temp_functions[idx] = Some(Rc::new(compiler.function));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -310,13 +317,13 @@ fn file(c: &mut Compiler, t: TreeRef) {
|
||||||
c.push(Instruction::Return);
|
c.push(Instruction::Return);
|
||||||
}
|
}
|
||||||
|
|
||||||
type CR = Option<()>;
|
type CR = Result<(), &'static str>;
|
||||||
const OK: CR = CR::Some(());
|
const OK: CR = CR::Ok(());
|
||||||
|
|
||||||
fn compile_expression(c: &mut Compiler, t: TreeRef) {
|
fn compile_expression(c: &mut Compiler, t: TreeRef) {
|
||||||
let tree = &c.syntax[t];
|
let tree = &c.syntax[t];
|
||||||
let cr = match tree.kind {
|
let cr = match tree.kind {
|
||||||
TreeKind::Error => None,
|
TreeKind::Error => Err("error tree"),
|
||||||
|
|
||||||
TreeKind::Argument => compile_argument(c, tree),
|
TreeKind::Argument => compile_argument(c, tree),
|
||||||
TreeKind::BinaryExpression => compile_binary_expression(c, t, tree),
|
TreeKind::BinaryExpression => compile_binary_expression(c, t, tree),
|
||||||
|
|
@ -338,13 +345,13 @@ fn compile_expression(c: &mut Compiler, t: TreeRef) {
|
||||||
|
|
||||||
_ => ice!(c, t, "{tree:?} is not an expression, cannot compile"),
|
_ => ice!(c, t, "{tree:?} is not an expression, cannot compile"),
|
||||||
};
|
};
|
||||||
if matches!(cr, None) {
|
if let Err(m) = cr {
|
||||||
c.push_panic(format!("panic compiling expression {:?}", tree));
|
c.push_panic(format!("panic compiling expression {:?}: {m}", tree));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_literal(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
|
fn compile_literal(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
|
||||||
let tok = tr.nth_token(0)?;
|
let tok = tr.nth_token(0).ok_or("no token")?;
|
||||||
match c.semantics.type_of(t) {
|
match c.semantics.type_of(t) {
|
||||||
Type::F64 => c.push(Instruction::PushFloat(
|
Type::F64 => c.push(Instruction::PushFloat(
|
||||||
tok.as_str(c.source).parse().unwrap(),
|
tok.as_str(c.source).parse().unwrap(),
|
||||||
|
|
@ -366,14 +373,14 @@ fn compile_literal(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_grouping(c: &mut Compiler, t: &Tree) -> CR {
|
fn compile_grouping(c: &mut Compiler, t: &Tree) -> CR {
|
||||||
compile_expression(c, t.nth_tree(1)?);
|
compile_expression(c, t.nth_tree(1).ok_or("unexpected tree")?);
|
||||||
OK
|
OK
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_unary_operator(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
|
fn compile_unary_operator(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
|
||||||
compile_expression(c, tr.nth_tree(1)?);
|
compile_expression(c, tr.nth_tree(1).ok_or("no arg")?);
|
||||||
|
|
||||||
let tok = tr.nth_token(0)?;
|
let tok = tr.nth_token(0).ok_or("no op")?;
|
||||||
match tok.kind {
|
match tok.kind {
|
||||||
TokenKind::Minus => {
|
TokenKind::Minus => {
|
||||||
c.push(Instruction::PushFloat(-1.0));
|
c.push(Instruction::PushFloat(-1.0));
|
||||||
|
|
@ -388,12 +395,12 @@ fn compile_unary_operator(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_condition_expression(c: &mut Compiler, t: &Tree) -> CR {
|
fn compile_condition_expression(c: &mut Compiler, t: &Tree) -> CR {
|
||||||
let condition = t.nth_tree(1)?;
|
let condition = t.nth_tree(1).ok_or("no cond")?;
|
||||||
compile_expression(c, condition);
|
compile_expression(c, condition);
|
||||||
|
|
||||||
let jump_else_index = c.push(Instruction::JumpFalse(0));
|
let jump_else_index = c.push(Instruction::JumpFalse(0));
|
||||||
|
|
||||||
let then_branch = t.nth_tree(2)?;
|
let then_branch = t.nth_tree(2).ok_or("no then")?;
|
||||||
compile_expression(c, then_branch);
|
compile_expression(c, then_branch);
|
||||||
|
|
||||||
let jump_end_index = c.push(Instruction::Jump(0));
|
let jump_end_index = c.push(Instruction::Jump(0));
|
||||||
|
|
@ -413,9 +420,9 @@ fn compile_simple_binary_expression<T>(c: &mut Compiler, tr: &Tree, f: T) -> CR
|
||||||
where
|
where
|
||||||
T: FnOnce(&mut Compiler, &Type) -> Instruction,
|
T: FnOnce(&mut Compiler, &Type) -> Instruction,
|
||||||
{
|
{
|
||||||
compile_expression(c, tr.nth_tree(0)?);
|
compile_expression(c, tr.nth_tree(0).ok_or("no lhs")?);
|
||||||
|
|
||||||
let arg_tree = tr.nth_tree(2)?;
|
let arg_tree = tr.nth_tree(2).ok_or("no rhs")?;
|
||||||
let arg_type = c.semantics.type_of(arg_tree);
|
let arg_type = c.semantics.type_of(arg_tree);
|
||||||
|
|
||||||
compile_expression(c, arg_tree);
|
compile_expression(c, arg_tree);
|
||||||
|
|
@ -427,7 +434,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
|
fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
|
||||||
let op = tr.nth_token(1)?;
|
let op = tr.nth_token(1).ok_or("no op")?;
|
||||||
match op.kind {
|
match op.kind {
|
||||||
TokenKind::Plus => compile_simple_binary_expression(c, tr, |c, t| match t {
|
TokenKind::Plus => compile_simple_binary_expression(c, tr, |c, t| match t {
|
||||||
Type::F64 => Instruction::FloatAdd,
|
Type::F64 => Instruction::FloatAdd,
|
||||||
|
|
@ -454,7 +461,7 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
|
||||||
Type::F64 => Instruction::GreaterFloat,
|
Type::F64 => Instruction::GreaterFloat,
|
||||||
Type::String => Instruction::GreaterString,
|
Type::String => Instruction::GreaterString,
|
||||||
_ => c.inst_panic(format!("panic less equal {}", t)),
|
_ => c.inst_panic(format!("panic less equal {}", t)),
|
||||||
});
|
})?;
|
||||||
c.push(Instruction::BoolNot);
|
c.push(Instruction::BoolNot);
|
||||||
OK
|
OK
|
||||||
}
|
}
|
||||||
|
|
@ -468,14 +475,14 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
|
||||||
Type::F64 => Instruction::LessFloat,
|
Type::F64 => Instruction::LessFloat,
|
||||||
Type::String => Instruction::LessString,
|
Type::String => Instruction::LessString,
|
||||||
_ => c.inst_panic(format!("panic greater equal {}", t)),
|
_ => c.inst_panic(format!("panic greater equal {}", t)),
|
||||||
});
|
})?;
|
||||||
c.push(Instruction::BoolNot);
|
c.push(Instruction::BoolNot);
|
||||||
OK
|
OK
|
||||||
}
|
}
|
||||||
|
|
||||||
TokenKind::And => {
|
TokenKind::And => {
|
||||||
// Compile the left hand side, it leaves a bool on the stack
|
// Compile the left hand side, it leaves a bool on the stack
|
||||||
compile_expression(c, tr.nth_tree(0)?);
|
compile_expression(c, tr.nth_tree(0).ok_or("no lhs")?);
|
||||||
|
|
||||||
// If the first part is true (hooray!) then we need to evaluate
|
// If the first part is true (hooray!) then we need to evaluate
|
||||||
// the right hand side, so jump around the short circuit...
|
// the right hand side, so jump around the short circuit...
|
||||||
|
|
@ -493,7 +500,7 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
|
||||||
|
|
||||||
// The right hand side leaves true or false on the stack, it's
|
// The right hand side leaves true or false on the stack, it's
|
||||||
// the result of the expression.
|
// the result of the expression.
|
||||||
compile_expression(c, tr.nth_tree(2)?);
|
compile_expression(c, tr.nth_tree(2).ok_or("no rhs")?);
|
||||||
|
|
||||||
// (here's where you go after you leave the "false" on the stack.)
|
// (here's where you go after you leave the "false" on the stack.)
|
||||||
c.patch(jump_end_index, |i| Instruction::Jump(i));
|
c.patch(jump_end_index, |i| Instruction::Jump(i));
|
||||||
|
|
@ -501,7 +508,7 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
|
||||||
}
|
}
|
||||||
TokenKind::Or => {
|
TokenKind::Or => {
|
||||||
// Compile the left hand side, it leaves a bool on the stack
|
// Compile the left hand side, it leaves a bool on the stack
|
||||||
compile_expression(c, tr.nth_tree(0)?);
|
compile_expression(c, tr.nth_tree(0).ok_or("no lhs")?);
|
||||||
|
|
||||||
// If the first part is false (boo!) then we need to evaluate the
|
// If the first part is false (boo!) then we need to evaluate the
|
||||||
// right hand side, so jump around the short circuit...
|
// right hand side, so jump around the short circuit...
|
||||||
|
|
@ -519,7 +526,7 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
|
||||||
|
|
||||||
// The right hand side leaves true or false on the stack, it's
|
// The right hand side leaves true or false on the stack, it's
|
||||||
// the result of the expression.
|
// the result of the expression.
|
||||||
compile_expression(c, tr.nth_tree(2)?);
|
compile_expression(c, tr.nth_tree(2).ok_or("no rhs")?);
|
||||||
|
|
||||||
// (here's where you go after you leave "true" on the stack.)
|
// (here's where you go after you leave "true" on the stack.)
|
||||||
c.patch(jump_end_index, |i| Instruction::Jump(i));
|
c.patch(jump_end_index, |i| Instruction::Jump(i));
|
||||||
|
|
@ -542,31 +549,31 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
TokenKind::Equal => {
|
TokenKind::Equal => {
|
||||||
compile_expression(c, tr.nth_tree(2)?);
|
compile_expression(c, tr.nth_tree(2).ok_or("no value")?);
|
||||||
c.push(Instruction::Dup);
|
c.push(Instruction::Dup);
|
||||||
|
|
||||||
let lvalue = tr.nth_tree(0)?;
|
let lvalue = tr.nth_tree(0).ok_or("no lvalue")?;
|
||||||
let ltree = &c.syntax[lvalue];
|
let ltree = &c.syntax[lvalue];
|
||||||
|
|
||||||
#[allow(unused_assignments)]
|
#[allow(unused_assignments)]
|
||||||
let mut environment = Environment::error();
|
let mut environment = Environment::error("??");
|
||||||
|
|
||||||
let declaration = match ltree.kind {
|
let declaration = match ltree.kind {
|
||||||
// TODO: Assign to list access
|
// TODO: Assign to list access
|
||||||
TreeKind::Identifier => {
|
TreeKind::Identifier => {
|
||||||
let id = ltree.nth_token(0)?.as_str(&c.source);
|
let id = ltree.nth_token(0).ok_or("no id")?.as_str(&c.source);
|
||||||
environment = c.semantics.environment_of(lvalue);
|
environment = c.semantics.environment_of(lvalue);
|
||||||
environment.bind(id)?
|
environment.bind(id).ok_or("cannot bind destination")?
|
||||||
}
|
}
|
||||||
TreeKind::MemberAccess => {
|
TreeKind::MemberAccess => {
|
||||||
let id = ltree.nth_token(2)?.as_str(&c.source);
|
let id = ltree.nth_token(2).ok_or("no member")?.as_str(&c.source);
|
||||||
|
|
||||||
let t = ltree.nth_tree(0)?;
|
let t = ltree.nth_tree(0).ok_or("no lhs exp")?;
|
||||||
let typ = c.semantics.type_of(t);
|
let typ = c.semantics.type_of(t);
|
||||||
environment = c.semantics.member_environment(t, &typ);
|
environment = c.semantics.member_environment(t, &typ);
|
||||||
environment.bind(id)?
|
environment.bind(id).ok_or("cannot bind field")?
|
||||||
}
|
}
|
||||||
_ => return None,
|
_ => return Err("unsupported lval expression"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let instruction = match declaration {
|
let instruction = match declaration {
|
||||||
|
|
@ -590,7 +597,7 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
|
||||||
Instruction::StoreModule(index)
|
Instruction::StoreModule(index)
|
||||||
}
|
}
|
||||||
Location::Slot => {
|
Location::Slot => {
|
||||||
compile_expression(c, ltree.nth_tree(0)?);
|
compile_expression(c, ltree.nth_tree(0).ok_or("no obj lhs")?);
|
||||||
Instruction::StoreSlot(index)
|
Instruction::StoreSlot(index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -614,10 +621,10 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_identifier_expression(c: &mut Compiler, t: TreeRef, tree: &Tree) -> Option<()> {
|
fn compile_identifier_expression(c: &mut Compiler, t: TreeRef, tree: &Tree) -> CR {
|
||||||
let ident = tree.nth_token(0)?.as_str(&c.source);
|
let ident = tree.nth_token(0).ok_or("no ident")?.as_str(&c.source);
|
||||||
let environment = c.semantics.environment_of(t);
|
let environment = c.semantics.environment_of(t);
|
||||||
let declaration = environment.bind(ident)?;
|
let declaration = environment.bind(ident).ok_or("not found")?;
|
||||||
|
|
||||||
compile_load_declaration(c, t, declaration)
|
compile_load_declaration(c, t, declaration)
|
||||||
}
|
}
|
||||||
|
|
@ -689,22 +696,24 @@ fn compile_load_declaration(c: &mut Compiler, t: TreeRef, declaration: &Declarat
|
||||||
OK
|
OK
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_match_expression(c: &mut Compiler, tree: &Tree) -> Option<()> {
|
fn compile_match_expression(c: &mut Compiler, tree: &Tree) -> CR {
|
||||||
compile_expression(c, tree.nth_tree(1)?);
|
compile_expression(c, tree.nth_tree(1).ok_or("no val")?);
|
||||||
|
|
||||||
let mut patches = Vec::new();
|
let mut patches = Vec::new();
|
||||||
let match_body = tree.child_tree_of_kind(c.syntax, TreeKind::MatchBody)?;
|
let match_body = tree
|
||||||
|
.child_tree_of_kind(c.syntax, TreeKind::MatchBody)
|
||||||
|
.ok_or("no body")?;
|
||||||
for arm in match_body.children_of_kind(c.syntax, TreeKind::MatchArm) {
|
for arm in match_body.children_of_kind(c.syntax, TreeKind::MatchArm) {
|
||||||
let arm = &c.syntax[arm];
|
let arm = &c.syntax[arm];
|
||||||
|
|
||||||
// Evaluate pattern...
|
// Evaluate pattern...
|
||||||
compile_pattern(c, arm.nth_tree(0)?)?;
|
compile_pattern(c, arm.nth_tree(0).ok_or("no arm pat")?)?;
|
||||||
|
|
||||||
// ...If false jump to next arm.
|
// ...If false jump to next arm.
|
||||||
let jump_next_index = c.push(Instruction::JumpFalse(0));
|
let jump_next_index = c.push(Instruction::JumpFalse(0));
|
||||||
|
|
||||||
// ...If true run expression and jump out.
|
// ...If true run expression and jump out.
|
||||||
compile_expression(c, arm.nth_tree(2)?);
|
compile_expression(c, arm.nth_tree(2).ok_or("no arm expr")?);
|
||||||
patches.push(c.push(Instruction::Jump(0)));
|
patches.push(c.push(Instruction::Jump(0)));
|
||||||
|
|
||||||
c.patch(jump_next_index, |i| Instruction::JumpFalse(i));
|
c.patch(jump_next_index, |i| Instruction::JumpFalse(i));
|
||||||
|
|
@ -719,13 +728,13 @@ fn compile_match_expression(c: &mut Compiler, tree: &Tree) -> Option<()> {
|
||||||
OK
|
OK
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_is_expression(c: &mut Compiler, tree: &Tree) -> Option<()> {
|
fn compile_is_expression(c: &mut Compiler, tree: &Tree) -> CR {
|
||||||
compile_expression(c, tree.nth_tree(0)?);
|
compile_expression(c, tree.nth_tree(0).ok_or("no val")?);
|
||||||
|
|
||||||
compile_pattern(c, tree.nth_tree(2)?)
|
compile_pattern(c, tree.nth_tree(2).ok_or("no pat")?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_pattern(c: &mut Compiler, t: TreeRef) -> Option<()> {
|
fn compile_pattern(c: &mut Compiler, t: TreeRef) -> CR {
|
||||||
let tree = &c.syntax[t];
|
let tree = &c.syntax[t];
|
||||||
|
|
||||||
// Let's *try* to generate good code in the presence of a wildcard pattern....
|
// Let's *try* to generate good code in the presence of a wildcard pattern....
|
||||||
|
|
@ -743,8 +752,11 @@ fn compile_pattern(c: &mut Compiler, t: TreeRef) -> Option<()> {
|
||||||
// If you have a binding, dup and store now, it is in scope.
|
// If you have a binding, dup and store now, it is in scope.
|
||||||
if let Some(binding) = tree.child_tree_of_kind(&c.syntax, TreeKind::VariableBinding) {
|
if let Some(binding) = tree.child_tree_of_kind(&c.syntax, TreeKind::VariableBinding) {
|
||||||
if let Some(variable) = binding.nth_token(0) {
|
if let Some(variable) = binding.nth_token(0) {
|
||||||
|
let id = variable.as_str(&c.source);
|
||||||
let environment = c.semantics.environment_of(t);
|
let environment = c.semantics.environment_of(t);
|
||||||
let declaration = environment.bind(variable.as_str(&c.source))?;
|
let Some(declaration) = environment.bind(id) else {
|
||||||
|
ice!(c, t, "cannot bind pattern variable `{id}`");
|
||||||
|
};
|
||||||
|
|
||||||
let Declaration::Variable {
|
let Declaration::Variable {
|
||||||
location: Location::Local,
|
location: Location::Local,
|
||||||
|
|
@ -766,8 +778,8 @@ fn compile_pattern(c: &mut Compiler, t: TreeRef) -> Option<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !is_wildcard {
|
if !is_wildcard {
|
||||||
let type_expr = type_expr?;
|
let type_expr = type_expr.ok_or("wild but no type")?;
|
||||||
compile_type_expr_eq(c, type_expr.nth_tree(0)?);
|
compile_type_expr_eq(c, type_expr.nth_tree(0).ok_or("no type expr")?);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(and_index) = and_index {
|
if let Some(and_index) = and_index {
|
||||||
|
|
@ -789,7 +801,7 @@ fn compile_pattern(c: &mut Compiler, t: TreeRef) -> Option<()> {
|
||||||
Some(jump_end_index)
|
Some(jump_end_index)
|
||||||
};
|
};
|
||||||
|
|
||||||
compile_expression(c, tree.nth_tree(and_index + 1)?);
|
compile_expression(c, tree.nth_tree(and_index + 1).ok_or("no condition")?);
|
||||||
|
|
||||||
// If we wound up with a jump what needs patching, patch it.
|
// If we wound up with a jump what needs patching, patch it.
|
||||||
if let Some(jump_end_index) = jump_end_index {
|
if let Some(jump_end_index) = jump_end_index {
|
||||||
|
|
@ -812,13 +824,16 @@ fn compile_type_expr_eq(c: &mut Compiler, t: TreeRef) {
|
||||||
_ => ice!(c, t, "tree is not a type expression"),
|
_ => ice!(c, t, "tree is not a type expression"),
|
||||||
};
|
};
|
||||||
|
|
||||||
if result.is_none() {
|
if let Err(m) = result {
|
||||||
c.push_panic(format!("panic compiling type expression eq {:?}", tree));
|
c.push_panic(format!(
|
||||||
|
"internal error compiling type expression eq {:?}: {m}",
|
||||||
|
tree
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_type_identifier_eq(c: &mut Compiler, t: TreeRef, tree: &Tree) -> CR {
|
fn compile_type_identifier_eq(c: &mut Compiler, t: TreeRef, tree: &Tree) -> CR {
|
||||||
let identifier = tree.nth_token(0)?.as_str(&c.source);
|
let identifier = tree.nth_token(0).ok_or("no id")?.as_str(&c.source);
|
||||||
match identifier {
|
match identifier {
|
||||||
"f64" => {
|
"f64" => {
|
||||||
c.push(Instruction::IsFloat);
|
c.push(Instruction::IsFloat);
|
||||||
|
|
@ -834,7 +849,7 @@ fn compile_type_identifier_eq(c: &mut Compiler, t: TreeRef, tree: &Tree) -> CR {
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let environment = c.semantics.environment_of(t);
|
let environment = c.semantics.environment_of(t);
|
||||||
match environment.bind(identifier)? {
|
match environment.bind(identifier).ok_or("cannot bind")? {
|
||||||
Declaration::Class { declaration, .. } => {
|
Declaration::Class { declaration, .. } => {
|
||||||
// The runtime identifier of the class is the tree index of the
|
// The runtime identifier of the class is the tree index of the
|
||||||
// class declaration sure why not.
|
// class declaration sure why not.
|
||||||
|
|
@ -842,7 +857,7 @@ fn compile_type_identifier_eq(c: &mut Compiler, t: TreeRef, tree: &Tree) -> CR {
|
||||||
c.push(Instruction::IsClass(index.try_into().unwrap()));
|
c.push(Instruction::IsClass(index.try_into().unwrap()));
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => return None,
|
_ => return Err("unsupported type decl"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -852,7 +867,7 @@ fn compile_type_identifier_eq(c: &mut Compiler, t: TreeRef, tree: &Tree) -> CR {
|
||||||
|
|
||||||
fn compile_type_alternate_eq(c: &mut Compiler, tree: &Tree) -> CR {
|
fn compile_type_alternate_eq(c: &mut Compiler, tree: &Tree) -> CR {
|
||||||
// Compile the left hand side, it leaves a bool on the stack
|
// Compile the left hand side, it leaves a bool on the stack
|
||||||
compile_type_expr_eq(c, tree.nth_tree(0)?);
|
compile_type_expr_eq(c, tree.nth_tree(0).ok_or("no lhs")?);
|
||||||
|
|
||||||
// If the first part is false (boo!) then we need to evaluate the
|
// If the first part is false (boo!) then we need to evaluate the
|
||||||
// right hand side, so jump around the short circuit...
|
// right hand side, so jump around the short circuit...
|
||||||
|
|
@ -870,7 +885,7 @@ fn compile_type_alternate_eq(c: &mut Compiler, tree: &Tree) -> CR {
|
||||||
|
|
||||||
// The right hand side leaves true or false on the stack, it's
|
// The right hand side leaves true or false on the stack, it's
|
||||||
// the result of the expression.
|
// the result of the expression.
|
||||||
compile_type_expr_eq(c, tree.nth_tree(2)?);
|
compile_type_expr_eq(c, tree.nth_tree(2).ok_or("no rhs")?);
|
||||||
|
|
||||||
// (here's where you go after you leave "true" on the stack.)
|
// (here's where you go after you leave "true" on the stack.)
|
||||||
c.patch(jump_end_index, |i| Instruction::Jump(i));
|
c.patch(jump_end_index, |i| Instruction::Jump(i));
|
||||||
|
|
@ -878,7 +893,9 @@ fn compile_type_alternate_eq(c: &mut Compiler, tree: &Tree) -> CR {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_call_expression(c: &mut Compiler, tree: &Tree) -> CR {
|
fn compile_call_expression(c: &mut Compiler, tree: &Tree) -> CR {
|
||||||
let arg_list = tree.child_tree_of_kind(&c.syntax, TreeKind::ArgumentList)?;
|
let arg_list = tree
|
||||||
|
.child_tree_of_kind(&c.syntax, TreeKind::ArgumentList)
|
||||||
|
.ok_or("no arglist")?;
|
||||||
let mut args: Vec<_> = arg_list.child_trees().collect();
|
let mut args: Vec<_> = arg_list.child_trees().collect();
|
||||||
let arg_count = args.len();
|
let arg_count = args.len();
|
||||||
|
|
||||||
|
|
@ -887,7 +904,7 @@ fn compile_call_expression(c: &mut Compiler, tree: &Tree) -> CR {
|
||||||
compile_expression(c, arg);
|
compile_expression(c, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
let func = tree.nth_tree(0)?;
|
let func = tree.nth_tree(0).ok_or("no func")?;
|
||||||
let func_type = c.semantics.type_of(func);
|
let func_type = c.semantics.type_of(func);
|
||||||
let arg_count = match func_type {
|
let arg_count = match func_type {
|
||||||
// TODO: Consider being guided by syntax here?
|
// TODO: Consider being guided by syntax here?
|
||||||
|
|
@ -910,14 +927,14 @@ fn compile_block_expression(c: &mut Compiler, tree: &Tree) -> CR {
|
||||||
let last_index = tree.children.len() - if last_is_brace { 2 } else { 1 };
|
let last_index = tree.children.len() - if last_is_brace { 2 } else { 1 };
|
||||||
|
|
||||||
for i in 1..last_index {
|
for i in 1..last_index {
|
||||||
compile_statement(c, tree.nth_tree(i)?, false);
|
compile_statement(c, tree.nth_tree(i).ok_or("no stat")?, false);
|
||||||
}
|
}
|
||||||
compile_statement(c, tree.nth_tree(last_index)?, true);
|
compile_statement(c, tree.nth_tree(last_index).ok_or("no last")?, true);
|
||||||
OK
|
OK
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_argument(c: &mut Compiler, tree: &Tree) -> CR {
|
fn compile_argument(c: &mut Compiler, tree: &Tree) -> CR {
|
||||||
compile_expression(c, tree.nth_tree(0)?);
|
compile_expression(c, tree.nth_tree(0).ok_or("no expr")?);
|
||||||
OK
|
OK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -930,11 +947,13 @@ fn compile_new_object_expression(c: &mut Compiler, t: TreeRef, tree: &Tree) -> C
|
||||||
|
|
||||||
let class = c.semantics.class_of(mid, ct);
|
let class = c.semantics.class_of(mid, ct);
|
||||||
|
|
||||||
let field_list = tree.child_tree_of_kind(&c.syntax, TreeKind::FieldList)?;
|
let field_list = tree
|
||||||
|
.child_tree_of_kind(&c.syntax, TreeKind::FieldList)
|
||||||
|
.ok_or("no field list")?;
|
||||||
let mut field_bindings = HashMap::new();
|
let mut field_bindings = HashMap::new();
|
||||||
for field in field_list.children_of_kind(&c.syntax, TreeKind::FieldValue) {
|
for field in field_list.children_of_kind(&c.syntax, TreeKind::FieldValue) {
|
||||||
let f = &c.syntax[field];
|
let f = &c.syntax[field];
|
||||||
let name = f.nth_token(0)?;
|
let name = f.nth_token(0).ok_or("no field name")?;
|
||||||
field_bindings.insert(name.as_str(&c.source), field);
|
field_bindings.insert(name.as_str(&c.source), field);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -942,16 +961,23 @@ fn compile_new_object_expression(c: &mut Compiler, t: TreeRef, tree: &Tree) -> C
|
||||||
// (stack!) we compile them in reverse order. Missing fields panic,
|
// (stack!) we compile them in reverse order. Missing fields panic,
|
||||||
// obviously.
|
// obviously.
|
||||||
for field in class.fields.iter().rev() {
|
for field in class.fields.iter().rev() {
|
||||||
let binding = field_bindings.get(&*field.name)?;
|
let binding = field_bindings
|
||||||
|
.get(&*field.name)
|
||||||
|
.ok_or("cannot bind field")?;
|
||||||
compile_expression(c, *binding);
|
compile_expression(c, *binding);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the correct constructor.
|
// Fetch the correct constructor.
|
||||||
// TODO: Binding this type should be done by semantics, and we should borrow it.
|
// TODO: Binding this type should be done by semantics, and we should borrow it.
|
||||||
let type_reference = tree.child_tree_of_kind(&c.syntax, TreeKind::TypeIdentifier)?;
|
let type_reference = tree
|
||||||
let identifier = type_reference.nth_token(0)?.as_str(&c.source);
|
.child_tree_of_kind(&c.syntax, TreeKind::TypeIdentifier)
|
||||||
|
.ok_or("no type ref")?;
|
||||||
|
let identifier = type_reference
|
||||||
|
.nth_token(0)
|
||||||
|
.ok_or("no type id")?
|
||||||
|
.as_str(&c.source);
|
||||||
let environment = c.semantics.environment_of(t);
|
let environment = c.semantics.environment_of(t);
|
||||||
match environment.bind(identifier)? {
|
match environment.bind(identifier).ok_or("cannot bind type")? {
|
||||||
Declaration::Class { declaration, .. } => {
|
Declaration::Class { declaration, .. } => {
|
||||||
let key = FunctionKey { tree: *declaration };
|
let key = FunctionKey { tree: *declaration };
|
||||||
let index = match c.function_bindings.get(&key) {
|
let index = match c.function_bindings.get(&key) {
|
||||||
|
|
@ -969,7 +995,7 @@ fn compile_new_object_expression(c: &mut Compiler, t: TreeRef, tree: &Tree) -> C
|
||||||
};
|
};
|
||||||
c.push(Instruction::LoadFunction(index));
|
c.push(Instruction::LoadFunction(index));
|
||||||
}
|
}
|
||||||
_ => return None,
|
_ => return Err("unsupported type for construction"),
|
||||||
}
|
}
|
||||||
c.push(Instruction::Call(class.fields.len()));
|
c.push(Instruction::Call(class.fields.len()));
|
||||||
OK
|
OK
|
||||||
|
|
@ -978,15 +1004,15 @@ fn compile_new_object_expression(c: &mut Compiler, t: TreeRef, tree: &Tree) -> C
|
||||||
fn compile_field_value(c: &mut Compiler, t: TreeRef, tree: &Tree) -> CR {
|
fn compile_field_value(c: &mut Compiler, t: TreeRef, tree: &Tree) -> CR {
|
||||||
if let Some(colon) = tree.nth_token(1) {
|
if let Some(colon) = tree.nth_token(1) {
|
||||||
if colon.kind == TokenKind::Colon {
|
if colon.kind == TokenKind::Colon {
|
||||||
compile_expression(c, tree.nth_tree(2)?);
|
compile_expression(c, tree.nth_tree(2).ok_or("no val")?);
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Form 2: { x, ... }
|
// Form 2: { x, ... }
|
||||||
let environment = c.semantics.environment_of(t);
|
let environment = c.semantics.environment_of(t);
|
||||||
let id = tree.nth_token(0)?.as_str(&c.source);
|
let id = tree.nth_token(0).ok_or("no id")?.as_str(&c.source);
|
||||||
let declaration = environment.bind(id)?;
|
let declaration = environment.bind(id).ok_or("cannot bind")?;
|
||||||
|
|
||||||
compile_load_declaration(c, t, declaration)
|
compile_load_declaration(c, t, declaration)
|
||||||
}
|
}
|
||||||
|
|
@ -995,10 +1021,11 @@ fn compile_member_access(c: &mut Compiler, t: TreeRef, tree: &Tree) -> CR {
|
||||||
// In member access; the lhs sets up the object and in theory the rhs
|
// In member access; the lhs sets up the object and in theory the rhs
|
||||||
// binds against it. ::shrug::
|
// binds against it. ::shrug::
|
||||||
//
|
//
|
||||||
compile_expression(c, tree.nth_tree(0)?);
|
let lhs = tree.nth_tree(0).ok_or("no lhs")?;
|
||||||
|
compile_expression(c, lhs);
|
||||||
|
|
||||||
let typ = c.semantics.type_of(tree.nth_tree(0)?);
|
let typ = c.semantics.type_of(lhs);
|
||||||
let ident = tree.nth_token(2)?.as_str(&c.source);
|
let ident = tree.nth_token(2).ok_or("no ident")?.as_str(&c.source);
|
||||||
|
|
||||||
let environment = match &typ {
|
let environment = match &typ {
|
||||||
Type::Object(mid, ct, _) => {
|
Type::Object(mid, ct, _) => {
|
||||||
|
|
@ -1011,15 +1038,15 @@ fn compile_member_access(c: &mut Compiler, t: TreeRef, tree: &Tree) -> CR {
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
c.push_panic("cannot get environment of {typ}");
|
c.push_panic("cannot get environment of {typ}");
|
||||||
return None;
|
return Err("cannot get environment");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let declaration = environment.bind(ident)?;
|
let declaration = environment.bind(ident).ok_or("cannot bind")?;
|
||||||
|
|
||||||
// NOTE: If this is a method call we still don't have to do anything
|
// NOTE: If this is a method call we still don't have to do anything
|
||||||
// special here, since the load of the member function will *not*
|
// special here, since the load of the member function will *not*
|
||||||
// consume the self pointer from the stack.
|
// consume the self pointer from the stack.
|
||||||
compile_load_declaration(c, t, declaration);
|
compile_load_declaration(c, t, declaration)?;
|
||||||
OK
|
OK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1042,14 +1069,14 @@ fn compile_list_constructor(c: &mut Compiler, tree: &Tree) -> CR {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_list_constructor_element(c: &mut Compiler, tree: &Tree) -> CR {
|
fn compile_list_constructor_element(c: &mut Compiler, tree: &Tree) -> CR {
|
||||||
compile_expression(c, tree.nth_tree(0)?);
|
compile_expression(c, tree.nth_tree(0).ok_or("no expr")?);
|
||||||
OK
|
OK
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_statement(c: &mut Compiler, t: TreeRef, gen_value: bool) {
|
fn compile_statement(c: &mut Compiler, t: TreeRef, gen_value: bool) {
|
||||||
let tree = &c.semantics.tree()[t];
|
let tree = &c.semantics.tree()[t];
|
||||||
let cr = match tree.kind {
|
let cr = match tree.kind {
|
||||||
TreeKind::Error => None,
|
TreeKind::Error => Err("parse error"),
|
||||||
|
|
||||||
TreeKind::Import => compile_import_statement(c, gen_value),
|
TreeKind::Import => compile_import_statement(c, gen_value),
|
||||||
TreeKind::Block => compile_block_statement(c, t, gen_value),
|
TreeKind::Block => compile_block_statement(c, t, gen_value),
|
||||||
|
|
@ -1064,13 +1091,17 @@ fn compile_statement(c: &mut Compiler, t: TreeRef, gen_value: bool) {
|
||||||
|
|
||||||
_ => ice!(c, t, "unsupported statement tree kind {:?}", tree.kind),
|
_ => ice!(c, t, "unsupported statement tree kind {:?}", tree.kind),
|
||||||
};
|
};
|
||||||
if matches!(cr, None) {
|
|
||||||
c.push_panic(format!("stat {:?}", tree));
|
if let Err(e) = cr {
|
||||||
|
c.push_panic(format!(
|
||||||
|
"internal error compiling statement {:?}: {e}",
|
||||||
|
tree
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_if_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR {
|
fn compile_if_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR {
|
||||||
compile_expression(c, tree.nth_tree(0)?);
|
compile_expression(c, tree.nth_tree(0).ok_or("no expr")?);
|
||||||
if !gen_value {
|
if !gen_value {
|
||||||
c.push(Instruction::Discard);
|
c.push(Instruction::Discard);
|
||||||
}
|
}
|
||||||
|
|
@ -1101,9 +1132,11 @@ fn compile_expression_statement(c: &mut Compiler, tree: &Tree, gen_value: bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_let_statement(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_value: bool) -> CR {
|
fn compile_let_statement(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_value: bool) -> CR {
|
||||||
compile_expression(c, tree.nth_tree(3)?);
|
compile_expression(c, tree.nth_tree(3).ok_or("no val")?);
|
||||||
let environment = c.semantics.environment_of(t);
|
let environment = c.semantics.environment_of(t);
|
||||||
let declaration = environment.bind(tree.nth_token(1)?.as_str(&c.source))?;
|
let declaration = environment
|
||||||
|
.bind(tree.nth_token(1).ok_or("no id")?.as_str(&c.source))
|
||||||
|
.ok_or("cannot bind")?;
|
||||||
|
|
||||||
let Declaration::Variable {
|
let Declaration::Variable {
|
||||||
location, index, ..
|
location, index, ..
|
||||||
|
|
@ -1144,9 +1177,11 @@ fn compile_function_declaration(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_v
|
||||||
let fk = FunctionKey { tree: t };
|
let fk = FunctionKey { tree: t };
|
||||||
if !c.function_bindings.contains_key(&fk) {
|
if !c.function_bindings.contains_key(&fk) {
|
||||||
// TODO: If this is a method the name should be different.
|
// TODO: If this is a method the name should be different.
|
||||||
let name = tree.nth_token(1)?.as_str(&c.source);
|
let name = tree.nth_token(1).ok_or("no id")?.as_str(&c.source);
|
||||||
|
|
||||||
let param_list = tree.child_tree_of_kind(&c.syntax, TreeKind::ParamList)?;
|
let param_list = tree
|
||||||
|
.child_tree_of_kind(&c.syntax, TreeKind::ParamList)
|
||||||
|
.ok_or("no paramlist")?;
|
||||||
let param_count = param_list.children.len() - 2;
|
let param_count = param_list.children.len() - 2;
|
||||||
|
|
||||||
let function_index = c.temp_functions.len();
|
let function_index = c.temp_functions.len();
|
||||||
|
|
@ -1172,7 +1207,7 @@ fn compile_class_declaration(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_valu
|
||||||
// Classes get compiled as constructor functions which get called.
|
// Classes get compiled as constructor functions which get called.
|
||||||
let fk = FunctionKey { tree: t };
|
let fk = FunctionKey { tree: t };
|
||||||
if !c.function_bindings.contains_key(&fk) {
|
if !c.function_bindings.contains_key(&fk) {
|
||||||
let name = tree.nth_token(1)?.as_str(&c.source);
|
let name = tree.nth_token(1).ok_or("no name")?.as_str(&c.source);
|
||||||
|
|
||||||
let field_count = tree.children.len() - 2;
|
let field_count = tree.children.len() - 2;
|
||||||
|
|
||||||
|
|
@ -1198,7 +1233,9 @@ fn compile_function(c: &mut Compiler, t: TreeRef) -> CR {
|
||||||
let tree = &c.syntax[t];
|
let tree = &c.syntax[t];
|
||||||
match tree.kind {
|
match tree.kind {
|
||||||
TreeKind::FunctionDecl => {
|
TreeKind::FunctionDecl => {
|
||||||
let block = tree.child_of_kind(&c.syntax, TreeKind::Block)?;
|
let block = tree
|
||||||
|
.child_of_kind(&c.syntax, TreeKind::Block)
|
||||||
|
.ok_or("no body")?;
|
||||||
compile_expression(c, block);
|
compile_expression(c, block);
|
||||||
}
|
}
|
||||||
TreeKind::ClassDecl => {
|
TreeKind::ClassDecl => {
|
||||||
|
|
@ -1209,7 +1246,7 @@ fn compile_function(c: &mut Compiler, t: TreeRef) -> CR {
|
||||||
c.push(Instruction::LoadArgument(count - 1 - i));
|
c.push(Instruction::LoadArgument(count - 1 - i));
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = tree.nth_token(1)?.as_str(&c.source);
|
let name = tree.nth_token(1).ok_or("no name")?.as_str(&c.source);
|
||||||
let name_index = c.add_string(name.to_string());
|
let name_index = c.add_string(name.to_string());
|
||||||
c.push(Instruction::PushString(name_index));
|
c.push(Instruction::PushString(name_index));
|
||||||
c.push(Instruction::PushInt(t.index().try_into().unwrap()));
|
c.push(Instruction::PushInt(t.index().try_into().unwrap()));
|
||||||
|
|
@ -1233,11 +1270,11 @@ fn compile_block_statement(c: &mut Compiler, t: TreeRef, gen_value: bool) -> CR
|
||||||
|
|
||||||
fn compile_while_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR {
|
fn compile_while_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR {
|
||||||
let start_index = c.function.instructions.len();
|
let start_index = c.function.instructions.len();
|
||||||
compile_expression(c, tree.nth_tree(1)?);
|
compile_expression(c, tree.nth_tree(1).ok_or("no cond")?);
|
||||||
|
|
||||||
let jump_end_index = c.push(Instruction::JumpFalse(0));
|
let jump_end_index = c.push(Instruction::JumpFalse(0));
|
||||||
|
|
||||||
compile_block_statement(c, tree.nth_tree(2)?, false);
|
compile_block_statement(c, tree.nth_tree(2).ok_or("no body")?, false)?;
|
||||||
c.push(Instruction::Jump(start_index));
|
c.push(Instruction::Jump(start_index));
|
||||||
|
|
||||||
c.patch(jump_end_index, |i| Instruction::JumpFalse(i));
|
c.patch(jump_end_index, |i| Instruction::JumpFalse(i));
|
||||||
|
|
@ -1260,11 +1297,11 @@ fn compile_return_statement(c: &mut Compiler, tree: &Tree) -> CR {
|
||||||
|
|
||||||
fn compile_for_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR {
|
fn compile_for_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR {
|
||||||
// Figure out the variable.
|
// Figure out the variable.
|
||||||
let vt = tree.nth_tree(1)?;
|
let vt = tree.nth_tree(1).ok_or("no var")?;
|
||||||
let var = &c.syntax[vt];
|
let var = &c.syntax[vt];
|
||||||
let id = var.nth_token(0)?.as_str(&c.source);
|
let id = var.nth_token(0).ok_or("no id")?.as_str(&c.source);
|
||||||
|
|
||||||
let body = tree.nth_tree(4)?;
|
let body = tree.nth_tree(4).ok_or("no body")?;
|
||||||
let env = c.semantics.environment_of(body);
|
let env = c.semantics.environment_of(body);
|
||||||
let Some(variable_decl) = env.bind(id) else {
|
let Some(variable_decl) = env.bind(id) else {
|
||||||
ice!(c, body, "Unable to bind {id} in loop body");
|
ice!(c, body, "Unable to bind {id} in loop body");
|
||||||
|
|
@ -1287,7 +1324,7 @@ fn compile_for_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Figure out the generator.
|
// Figure out the generator.
|
||||||
let iterable = tree.nth_tree(3)?;
|
let iterable = tree.nth_tree(3).ok_or("no generator")?;
|
||||||
compile_expression(c, iterable);
|
compile_expression(c, iterable);
|
||||||
|
|
||||||
// call 'get_iterator'
|
// call 'get_iterator'
|
||||||
|
|
@ -1299,7 +1336,7 @@ fn compile_for_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR {
|
||||||
));
|
));
|
||||||
c.push(Instruction::Call(1));
|
c.push(Instruction::Call(1));
|
||||||
}
|
}
|
||||||
_ => return None, // TODO: Bind and call get_iterator() on type of iterable
|
_ => return Err("unsupported collection"), // TODO: Bind and call get_iterator() on type of iterable
|
||||||
}
|
}
|
||||||
|
|
||||||
// iterate
|
// iterate
|
||||||
|
|
@ -1312,7 +1349,7 @@ fn compile_for_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR {
|
||||||
EXTERN_BUILTIN_LIST_ITERATOR_NEXT,
|
EXTERN_BUILTIN_LIST_ITERATOR_NEXT,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
_ => return None, // TODO: Bind and call next() on type of iterator
|
_ => return Err("unsupported iterator"), // TODO: Bind and call next() on type of iterator
|
||||||
}
|
}
|
||||||
c.push(Instruction::Call(1)); // Call 'next'
|
c.push(Instruction::Call(1)); // Call 'next'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -431,7 +431,7 @@ pub struct Environment {
|
||||||
pub location: Location,
|
pub location: Location,
|
||||||
pub next_index: usize,
|
pub next_index: usize,
|
||||||
pub declarations: HashMap<Box<str>, Declaration>,
|
pub declarations: HashMap<Box<str>, Declaration>,
|
||||||
pub is_error: bool,
|
pub error: Option<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Environment {
|
impl Environment {
|
||||||
|
|
@ -457,18 +457,22 @@ impl Environment {
|
||||||
location,
|
location,
|
||||||
next_index,
|
next_index,
|
||||||
declarations: HashMap::new(),
|
declarations: HashMap::new(),
|
||||||
is_error: false,
|
error: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn error() -> EnvironmentRef {
|
pub fn is_error(&self) -> bool {
|
||||||
|
self.error.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn error(why: &'static str) -> EnvironmentRef {
|
||||||
// TODO: Exactly once?
|
// TODO: Exactly once?
|
||||||
EnvironmentRef::new(Environment {
|
EnvironmentRef::new(Environment {
|
||||||
parent: None,
|
parent: None,
|
||||||
location: Location::Local,
|
location: Location::Local,
|
||||||
next_index: 0,
|
next_index: 0,
|
||||||
declarations: HashMap::new(),
|
declarations: HashMap::new(),
|
||||||
is_error: true,
|
error: Some(why),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1137,12 +1141,12 @@ impl Semantics {
|
||||||
let Some(pattern) = tree.child_tree_of_kind(&self.syntax_tree, TreeKind::Pattern) else {
|
let Some(pattern) = tree.child_tree_of_kind(&self.syntax_tree, TreeKind::Pattern) else {
|
||||||
// Should really have a pattern in there; otherwise there was a
|
// Should really have a pattern in there; otherwise there was a
|
||||||
// parse error, don't make more trouble.
|
// parse error, don't make more trouble.
|
||||||
return Environment::error();
|
return Environment::error("no rhs (pattern)");
|
||||||
};
|
};
|
||||||
|
|
||||||
// The left hand side of the `is` expression is used for wildcard types.
|
// The left hand side of the `is` expression is used for wildcard types.
|
||||||
let Some(lhs) = tree.nth_tree(0) else {
|
let Some(lhs) = tree.nth_tree(0) else {
|
||||||
return Environment::error();
|
return Environment::error("no lhs (value)");
|
||||||
};
|
};
|
||||||
self.environment_of_pattern(parent, pattern, lhs)
|
self.environment_of_pattern(parent, pattern, lhs)
|
||||||
}
|
}
|
||||||
|
|
@ -1159,7 +1163,7 @@ impl Semantics {
|
||||||
let Some(pattern) = tree.child_tree_of_kind(&self.syntax_tree, TreeKind::Pattern) else {
|
let Some(pattern) = tree.child_tree_of_kind(&self.syntax_tree, TreeKind::Pattern) else {
|
||||||
// Should really have a pattern in there; otherwise there was a
|
// Should really have a pattern in there; otherwise there was a
|
||||||
// parse error, don't make more trouble.
|
// parse error, don't make more trouble.
|
||||||
return Environment::error();
|
return Environment::error("arm: no lhs (pattern)");
|
||||||
};
|
};
|
||||||
|
|
||||||
// The expression in the match expression is the binding for the wildcard pattern.
|
// The expression in the match expression is the binding for the wildcard pattern.
|
||||||
|
|
@ -1180,8 +1184,8 @@ impl Semantics {
|
||||||
}
|
}
|
||||||
|
|
||||||
// The expression is the first tree child of match expression.
|
// The expression is the first tree child of match expression.
|
||||||
let Some(lhs) = tree.nth_tree(1) else {
|
let Some(lhs) = tree.nth_tree(2) else {
|
||||||
return Environment::error();
|
return Environment::error("arm: no rhs (expression)");
|
||||||
};
|
};
|
||||||
self.environment_of_pattern(parent, pattern, lhs)
|
self.environment_of_pattern(parent, pattern, lhs)
|
||||||
}
|
}
|
||||||
|
|
@ -1201,7 +1205,7 @@ impl Semantics {
|
||||||
return parent;
|
return parent;
|
||||||
};
|
};
|
||||||
let Some(variable) = binding.nth_token(0) else {
|
let Some(variable) = binding.nth_token(0) else {
|
||||||
return Environment::error();
|
return Environment::error("no variable");
|
||||||
};
|
};
|
||||||
|
|
||||||
let is_wildcard = tree
|
let is_wildcard = tree
|
||||||
|
|
@ -1218,7 +1222,7 @@ impl Semantics {
|
||||||
// match for the variable to have a value.
|
// match for the variable to have a value.
|
||||||
let Some(type_expr) = tree.child_of_kind(&self.syntax_tree, TreeKind::TypeExpression)
|
let Some(type_expr) = tree.child_of_kind(&self.syntax_tree, TreeKind::TypeExpression)
|
||||||
else {
|
else {
|
||||||
return Environment::error();
|
return Environment::error("no type expression");
|
||||||
};
|
};
|
||||||
type_expr
|
type_expr
|
||||||
};
|
};
|
||||||
|
|
@ -1658,7 +1662,7 @@ impl Semantics {
|
||||||
let tree = &self.syntax_tree[left_tree];
|
let tree = &self.syntax_tree[left_tree];
|
||||||
|
|
||||||
#[allow(unused_assignments)]
|
#[allow(unused_assignments)]
|
||||||
let mut environment = Environment::error();
|
let mut environment = Environment::error("?");
|
||||||
|
|
||||||
let declaration = match tree.kind {
|
let declaration = match tree.kind {
|
||||||
// TODO: Assign to list access
|
// TODO: Assign to list access
|
||||||
|
|
@ -1668,7 +1672,7 @@ impl Semantics {
|
||||||
match environment.bind(id) {
|
match environment.bind(id) {
|
||||||
Some(decl) => decl,
|
Some(decl) => decl,
|
||||||
None => {
|
None => {
|
||||||
if !environment.is_error {
|
if !environment.is_error() {
|
||||||
self.report_error_tree(tree, format!("cannot find value {id} here"));
|
self.report_error_tree(tree, format!("cannot find value {id} here"));
|
||||||
}
|
}
|
||||||
return Some(Type::Error);
|
return Some(Type::Error);
|
||||||
|
|
@ -1682,7 +1686,7 @@ impl Semantics {
|
||||||
match environment.bind(id) {
|
match environment.bind(id) {
|
||||||
Some(decl) => decl,
|
Some(decl) => decl,
|
||||||
None => {
|
None => {
|
||||||
if !environment.is_error {
|
if !environment.is_error() {
|
||||||
self.report_error_tree(tree, format!("'{typ}' has no member {id}"));
|
self.report_error_tree(tree, format!("'{typ}' has no member {id}"));
|
||||||
}
|
}
|
||||||
return Some(Type::Error);
|
return Some(Type::Error);
|
||||||
|
|
@ -1820,7 +1824,7 @@ impl Semantics {
|
||||||
.type_of_declaration(*tree, declaration),
|
.type_of_declaration(*tree, declaration),
|
||||||
),
|
),
|
||||||
None => {
|
None => {
|
||||||
if !environment.is_error {
|
if !environment.is_error() {
|
||||||
self.report_error_tree(tree, format!("Unrecognized type: '{token}'"));
|
self.report_error_tree(tree, format!("Unrecognized type: '{token}'"));
|
||||||
}
|
}
|
||||||
Some(Type::Error)
|
Some(Type::Error)
|
||||||
|
|
@ -2046,7 +2050,7 @@ impl Semantics {
|
||||||
|
|
||||||
let id_str = id.as_str(&self.source);
|
let id_str = id.as_str(&self.source);
|
||||||
let Some(declaration) = env.bind(id_str) else {
|
let Some(declaration) = env.bind(id_str) else {
|
||||||
if !env.is_error {
|
if !env.is_error() {
|
||||||
self.report_error_span(
|
self.report_error_span(
|
||||||
id.start(),
|
id.start(),
|
||||||
id.end(),
|
id.end(),
|
||||||
|
|
@ -2100,10 +2104,10 @@ impl Semantics {
|
||||||
}
|
}
|
||||||
EnvironmentRef::new(result)
|
EnvironmentRef::new(result)
|
||||||
}
|
}
|
||||||
Type::Error => return Environment::error(),
|
Type::Error => return Environment::error("error type has no members"),
|
||||||
_ => {
|
_ => {
|
||||||
self.report_error_tree_ref(t, format!("cannot access members of '{typ}'"));
|
self.report_error_tree_ref(t, format!("cannot access members of '{typ}'"));
|
||||||
return Environment::error();
|
return Environment::error("type has no members");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2140,7 +2144,7 @@ impl Semantics {
|
||||||
return Some(self.type_of_declaration(Some(t), declaration));
|
return Some(self.type_of_declaration(Some(t), declaration));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !environment.is_error {
|
if !environment.is_error() {
|
||||||
self.report_error_tree(tree, format!("cannot find value {id} here"));
|
self.report_error_tree(tree, format!("cannot find value {id} here"));
|
||||||
}
|
}
|
||||||
Some(Type::Error)
|
Some(Type::Error)
|
||||||
|
|
@ -2202,7 +2206,7 @@ impl Semantics {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if !environment.is_error {
|
if !environment.is_error() {
|
||||||
self.report_error_tree(tree, "`self` is only valid in methods");
|
self.report_error_tree(tree, "`self` is only valid in methods");
|
||||||
}
|
}
|
||||||
Some(Type::Error)
|
Some(Type::Error)
|
||||||
|
|
@ -2340,7 +2344,7 @@ impl Semantics {
|
||||||
let declaration = match environment.bind(id) {
|
let declaration = match environment.bind(id) {
|
||||||
Some(d) => d,
|
Some(d) => d,
|
||||||
None => {
|
None => {
|
||||||
if !environment.is_error {
|
if !environment.is_error() {
|
||||||
self.report_error_tree(tree, format!("cannot find value {id} here"));
|
self.report_error_tree(tree, format!("cannot find value {id} here"));
|
||||||
}
|
}
|
||||||
return Some(Type::Error);
|
return Some(Type::Error);
|
||||||
|
|
@ -2647,6 +2651,9 @@ impl Semantics {
|
||||||
eprintln!("\nThe environment of the tree was:");
|
eprintln!("\nThe environment of the tree was:");
|
||||||
let mut environment = Some(self.environment_of(tr));
|
let mut environment = Some(self.environment_of(tr));
|
||||||
while let Some(env) = environment {
|
while let Some(env) = environment {
|
||||||
|
if let Some(error) = env.error {
|
||||||
|
eprint!(" *** ERROR: {error}");
|
||||||
|
}
|
||||||
for (k, v) in env.declarations.iter() {
|
for (k, v) in env.declarations.iter() {
|
||||||
eprint!(" {k}: ");
|
eprint!(" {k}: ");
|
||||||
match v {
|
match v {
|
||||||
|
|
|
||||||
|
|
@ -46,13 +46,17 @@ class Monster {
|
||||||
|
|
||||||
fun print(x:string) {}
|
fun print(x:string) {}
|
||||||
|
|
||||||
fun in_range(weapon: MeleeWeapon or RangedWeapon, distance: f64) {
|
fun in_range(weapon: MeleeWeapon or RangedWeapon, distance: f64) -> bool {
|
||||||
match weapon {
|
match weapon {
|
||||||
w:RangedWeapon -> distance >= w.minRange and distance <= w.maxRange,
|
w:RangedWeapon -> distance >= w.minRange and distance <= w.maxRange,
|
||||||
_ -> distance == 1
|
_ -> distance == 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun roll_dice(x:f64) -> f64 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
fun attack(weapon: MeleeWeapon or RangedWeapon, monster: Monster, distance: f64) {
|
fun attack(weapon: MeleeWeapon or RangedWeapon, monster: Monster, distance: f64) {
|
||||||
// This is worse than Bob's final version but but it works. `is` operator
|
// This is worse than Bob's final version but but it works. `is` operator
|
||||||
// should be the same precedence as `and` and left-associative, so the
|
// should be the same precedence as `and` and left-associative, so the
|
||||||
|
|
@ -79,10 +83,10 @@ fun attack(weapon: MeleeWeapon or RangedWeapon, monster: Monster, distance: f64)
|
||||||
|
|
||||||
if monster.health <= damage {
|
if monster.health <= damage {
|
||||||
print("You kill the monster!");
|
print("You kill the monster!");
|
||||||
monster.health = 0
|
monster.health = 0;
|
||||||
} else {
|
} else {
|
||||||
print("You wound the monster.");
|
print("You wound the monster.");
|
||||||
monster.health = monster.health - damage
|
monster.health = monster.health - damage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -147,6 +151,5 @@ fun test() -> f64 {
|
||||||
// like the above.
|
// like the above.
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ignore not finished yet, still compiler bugs
|
|
||||||
// @no-errors
|
// @no-errors
|
||||||
// @eval: Float(90.0)
|
// @eval: Float(190.0)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue