[fine] Evaluate is expressions, SO MANY BUG FIXES
This commit is contained in:
parent
b5b56b49a9
commit
198dc5bdb3
6 changed files with 215 additions and 42 deletions
|
|
@ -26,7 +26,7 @@ pub enum Instruction {
|
|||
FloatSubtract,
|
||||
Jump(usize),
|
||||
JumpFalse(usize),
|
||||
JumpTrue(usize),
|
||||
JumpTrue(usize), // TODO: Only one of these, and use BoolNot?
|
||||
LoadArgument(usize),
|
||||
LoadExternFunction(usize), // NOTE: FUNKY, might want to indirect this index.
|
||||
LoadFunction(usize),
|
||||
|
|
@ -44,6 +44,9 @@ pub enum Instruction {
|
|||
StoreLocal(usize),
|
||||
StoreModule(usize),
|
||||
StringAdd,
|
||||
|
||||
IsClass(i64),
|
||||
PushInt(i64),
|
||||
}
|
||||
|
||||
pub enum Export {
|
||||
|
|
@ -286,19 +289,22 @@ fn compile_expression(c: &mut Compiler, t: TreeRef) {
|
|||
let tree = &c.syntax[t];
|
||||
let cr = match tree.kind {
|
||||
TreeKind::Error => None,
|
||||
TreeKind::LiteralExpression => compile_literal(c, t, tree),
|
||||
TreeKind::GroupingExpression => compile_grouping(c, tree),
|
||||
TreeKind::UnaryExpression => compile_unary_operator(c, t, tree),
|
||||
TreeKind::ConditionalExpression => compile_condition_expression(c, tree),
|
||||
TreeKind::BinaryExpression => compile_binary_expression(c, t, tree),
|
||||
TreeKind::Identifier => compile_identifier_expression(c, t, tree),
|
||||
TreeKind::CallExpression => compile_call_expression(c, tree),
|
||||
TreeKind::Block => compile_block_expression(c, tree),
|
||||
|
||||
TreeKind::Argument => compile_argument(c, tree),
|
||||
TreeKind::NewObjectExpression => compile_new_object_expression(c, t, tree),
|
||||
TreeKind::BinaryExpression => compile_binary_expression(c, t, tree),
|
||||
TreeKind::Block => compile_block_expression(c, tree),
|
||||
TreeKind::CallExpression => compile_call_expression(c, tree),
|
||||
TreeKind::ConditionalExpression => compile_condition_expression(c, tree),
|
||||
TreeKind::FieldValue => compile_field_value(c, t, tree),
|
||||
TreeKind::GroupingExpression => compile_grouping(c, tree),
|
||||
TreeKind::Identifier => compile_identifier_expression(c, t, tree),
|
||||
TreeKind::IsExpression => compile_is_expression(c, t, tree),
|
||||
TreeKind::LiteralExpression => compile_literal(c, t, tree),
|
||||
TreeKind::MemberAccess => compile_member_access(c, tree),
|
||||
TreeKind::NewObjectExpression => compile_new_object_expression(c, t, tree),
|
||||
TreeKind::SelfReference => compile_self_reference(c),
|
||||
TreeKind::UnaryExpression => compile_unary_operator(c, t, tree),
|
||||
|
||||
_ => ice!(c, t, "{tree:?} is not an expression, cannot compile"),
|
||||
};
|
||||
if matches!(cr, None) {
|
||||
|
|
@ -374,15 +380,16 @@ fn compile_condition_expression(c: &mut Compiler, t: &Tree) -> CR {
|
|||
let then_branch = t.nth_tree(2)?;
|
||||
compile_expression(c, then_branch);
|
||||
|
||||
if let Some(else_branch) = t.nth_tree(4) {
|
||||
let jump_end_index = c.push(Instruction::Jump(0));
|
||||
c.patch(jump_else_index, |i| Instruction::JumpFalse(i));
|
||||
let jump_end_index = c.push(Instruction::Jump(0));
|
||||
c.patch(jump_else_index, |i| Instruction::JumpFalse(i));
|
||||
|
||||
if let Some(else_branch) = t.nth_tree(4) {
|
||||
compile_expression(c, else_branch);
|
||||
c.patch(jump_end_index, |i| Instruction::Jump(i));
|
||||
} else {
|
||||
c.patch(jump_else_index, |i| Instruction::JumpFalse(i));
|
||||
c.push(Instruction::PushNothing);
|
||||
}
|
||||
|
||||
c.patch(jump_end_index, |i| Instruction::Jump(i));
|
||||
OK
|
||||
}
|
||||
|
||||
|
|
@ -420,30 +427,54 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR {
|
|||
compile_simple_binary_expression(c, tr, |_, _| Instruction::FloatDivide)
|
||||
}
|
||||
TokenKind::And => {
|
||||
// Compile the left hand side, it leaves a bool on the stack
|
||||
compile_expression(c, tr.nth_tree(0)?);
|
||||
let jump_false_index = c.push(Instruction::JumpFalse(0));
|
||||
|
||||
c.push(Instruction::PushTrue);
|
||||
// If the first part is true (hooray!) then we need to evaluate
|
||||
// the right hand side, so jump around the short circuit...
|
||||
let jump_true_index = c.push(Instruction::JumpTrue(0));
|
||||
|
||||
// ...but if the first part is false then we stop here. We need
|
||||
// to leave a value on the stack (it was consumed by jump above)
|
||||
// so we push an extra False here, and jump to the end.
|
||||
c.push(Instruction::PushFalse);
|
||||
let jump_end_index = c.push(Instruction::Jump(0));
|
||||
|
||||
c.patch(jump_false_index, |i| Instruction::JumpFalse(i));
|
||||
// Here we are, we consumed the `true` off the stack now time to
|
||||
// do the right hand side.
|
||||
c.patch(jump_true_index, |i| Instruction::JumpTrue(i));
|
||||
|
||||
// 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)?);
|
||||
|
||||
// (here's where you go after you leave the "false" on the stack.)
|
||||
c.patch(jump_end_index, |i| Instruction::Jump(i));
|
||||
OK
|
||||
}
|
||||
TokenKind::Or => {
|
||||
// Compile the left hand side, it leaves a bool on the stack
|
||||
compile_expression(c, tr.nth_tree(0)?);
|
||||
let jump_true_index = c.push(Instruction::JumpTrue(0));
|
||||
|
||||
// If the first part is false (boo!) then we need to evaluate the
|
||||
// right hand side, so jump around the short circuit...
|
||||
let jump_false_index = c.push(Instruction::JumpFalse(0));
|
||||
|
||||
// ...but if the first part os true then we stop here. We need to
|
||||
// leave a value on the stack (it was consumed by jump above) so
|
||||
// we push an extra True here and jump to the end.
|
||||
c.push(Instruction::PushTrue);
|
||||
let jump_end_index = c.push(Instruction::Jump(0));
|
||||
|
||||
c.patch(jump_true_index, |i| Instruction::JumpTrue(i));
|
||||
// Here we are, we consumed the `false` off the stack now time to
|
||||
// do the right hand side.
|
||||
c.patch(jump_false_index, |i| Instruction::JumpFalse(i));
|
||||
|
||||
// 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)?);
|
||||
|
||||
// (here's where you go after you leave "true" on the stack.)
|
||||
c.patch(jump_end_index, |i| Instruction::Jump(i));
|
||||
OK
|
||||
}
|
||||
|
|
@ -589,6 +620,112 @@ fn compile_load_declaration(c: &mut Compiler, t: TreeRef, declaration: &Declarat
|
|||
OK
|
||||
}
|
||||
|
||||
fn compile_is_expression(c: &mut Compiler, t: TreeRef, tree: &Tree) -> Option<()> {
|
||||
compile_expression(c, tree.nth_tree(0)?);
|
||||
|
||||
// If you have a binding, dup and store now, it is in scope.
|
||||
let type_expr =
|
||||
if let Some(binding) = tree.child_tree_of_kind(c.syntax, TreeKind::VariableBinding) {
|
||||
if let Some(variable) = binding.nth_token(0) {
|
||||
let environment = c.semantics.environment_of(t);
|
||||
let declaration = environment.bind(variable)?;
|
||||
|
||||
let Declaration::Variable {
|
||||
location: Location::Local,
|
||||
index,
|
||||
..
|
||||
} = declaration
|
||||
else {
|
||||
ice!(c, t, "is cannot make a non-local, non-variable declaration")
|
||||
};
|
||||
|
||||
c.push(Instruction::Dup);
|
||||
c.push(Instruction::StoreLocal(*index));
|
||||
}
|
||||
|
||||
binding.child_tree_of_kind(c.syntax, TreeKind::TypeExpression)?
|
||||
} else {
|
||||
tree.child_tree_of_kind(c.syntax, TreeKind::TypeExpression)?
|
||||
};
|
||||
|
||||
compile_type_expr_eq(c, type_expr.nth_tree(0)?);
|
||||
|
||||
if let Some(tok) = tree.nth_token(3) {
|
||||
if tok.kind == TokenKind::And {
|
||||
let jump_true_index = c.push(Instruction::JumpTrue(0));
|
||||
|
||||
c.push(Instruction::PushFalse);
|
||||
let jump_end_index = c.push(Instruction::Jump(0));
|
||||
|
||||
c.patch(jump_true_index, |i| Instruction::JumpTrue(i));
|
||||
|
||||
compile_expression(c, tree.nth_tree(4)?);
|
||||
c.patch(jump_end_index, |i| Instruction::Jump(i));
|
||||
}
|
||||
}
|
||||
|
||||
OK
|
||||
}
|
||||
|
||||
fn compile_type_expr_eq(c: &mut Compiler, t: TreeRef) {
|
||||
let tree = &c.syntax[t];
|
||||
let result = match tree.kind {
|
||||
TreeKind::TypeIdentifier => compile_type_identifier_eq(c, t, tree),
|
||||
TreeKind::AlternateType => compile_type_alternate_eq(c, tree),
|
||||
_ => ice!(c, t, "tree is not a type expression"),
|
||||
};
|
||||
|
||||
if result.is_none() {
|
||||
c.push(inst_panic!("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)?;
|
||||
let environment = c.semantics.environment_of(t);
|
||||
match environment.bind(identifier)? {
|
||||
Declaration::Class { declaration, .. } => {
|
||||
// The runtime identifier of the class is the tree index of the
|
||||
// class declaration sure why not.
|
||||
let index = declaration.index();
|
||||
c.push(Instruction::IsClass(index.try_into().unwrap()));
|
||||
}
|
||||
|
||||
// TODO: enforce that type identifier binds to class in `is`
|
||||
// expresion, we don't support RTTI for other types yet.
|
||||
_ => return None,
|
||||
}
|
||||
|
||||
OK
|
||||
}
|
||||
|
||||
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)?);
|
||||
|
||||
// If the first part is false (boo!) then we need to evaluate the
|
||||
// right hand side, so jump around the short circuit...
|
||||
let jump_false_index = c.push(Instruction::JumpFalse(0));
|
||||
|
||||
// ...but if the first part is true then we stop here. We need to
|
||||
// leave a value on the stack (it was consumed by jump above) so
|
||||
// we push an extra True here and jump to the end.
|
||||
c.push(Instruction::PushTrue);
|
||||
let jump_end_index = c.push(Instruction::Jump(0));
|
||||
|
||||
// Here we are, we consumed the `false` off the stack now time to
|
||||
// do the right hand side.
|
||||
c.patch(jump_false_index, |i| Instruction::JumpFalse(i));
|
||||
|
||||
// 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)?);
|
||||
|
||||
// (here's where you go after you leave "true" on the stack.)
|
||||
c.patch(jump_end_index, |i| Instruction::Jump(i));
|
||||
OK
|
||||
}
|
||||
|
||||
fn compile_call_expression(c: &mut Compiler, tree: &Tree) -> CR {
|
||||
let arg_list = tree.child_tree_of_kind(c.syntax, TreeKind::ArgumentList)?;
|
||||
let mut args: Vec<_> = arg_list.child_trees().collect();
|
||||
|
|
@ -883,6 +1020,7 @@ fn compile_function(c: &mut Compiler, t: TreeRef) -> CR {
|
|||
let name = tree.nth_token(1)?.as_str();
|
||||
let name_index = c.add_string(name.to_string());
|
||||
c.push(Instruction::PushString(name_index));
|
||||
c.push(Instruction::PushInt(t.index().try_into().unwrap()));
|
||||
c.push(Instruction::NewObject(count));
|
||||
}
|
||||
_ => ice!(c, t, "what is this tree doing in compile_function?"),
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ pub enum Type {
|
|||
// TODO: Numeric literals should be implicitly convertable, unlike other
|
||||
// types. Maybe just "numeric literal" type?
|
||||
F64,
|
||||
I64,
|
||||
String,
|
||||
Bool,
|
||||
|
||||
|
|
@ -172,6 +173,7 @@ impl fmt::Display for Type {
|
|||
Assignment(_) => write!(f, "assignment"),
|
||||
Nothing => write!(f, "()"),
|
||||
F64 => write!(f, "f64"),
|
||||
I64 => write!(f, "i64"),
|
||||
String => write!(f, "string"),
|
||||
Bool => write!(f, "bool"),
|
||||
Function(args, ret) => {
|
||||
|
|
@ -831,6 +833,9 @@ impl<'a> Semantics<'a> {
|
|||
return Environment::error();
|
||||
};
|
||||
|
||||
// TODO: This binding should be un-assignable! Don't assign to this!
|
||||
// (UNLESS VERY SPECIFIC CIRCUMSTANCES; WHICH ARE COMPLEX)
|
||||
|
||||
let mut env = Environment::new(Some(parent), Location::Local);
|
||||
env.insert(variable, type_expr);
|
||||
return EnvironmentRef::new(env);
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ type Result<T> = std::result::Result<T, VMErrorCode>;
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct Object {
|
||||
name: Rc<str>,
|
||||
class_id: i64,
|
||||
values: Box<[StackValue]>,
|
||||
}
|
||||
|
||||
|
|
@ -68,6 +69,7 @@ pub enum StackValue {
|
|||
Nothing,
|
||||
Bool(bool),
|
||||
Float(f64),
|
||||
Int(i64),
|
||||
String(Rc<str>),
|
||||
Function(Rc<Function>),
|
||||
ExternFunction(usize),
|
||||
|
|
@ -146,6 +148,13 @@ impl Frame {
|
|||
}
|
||||
}
|
||||
|
||||
fn pop_int(&mut self) -> Result<i64> {
|
||||
match self.pop_value()? {
|
||||
StackValue::Int(v) => Ok(v),
|
||||
v => Err(VMErrorCode::StackTypeMismatch(v, Type::I64)),
|
||||
}
|
||||
}
|
||||
|
||||
fn push_value(&mut self, v: StackValue) {
|
||||
self.stack.push(v)
|
||||
}
|
||||
|
|
@ -178,6 +187,10 @@ impl Frame {
|
|||
self.push_value(StackValue::Object(v));
|
||||
}
|
||||
|
||||
fn push_int(&mut self, v: i64) {
|
||||
self.push_value(StackValue::Int(v));
|
||||
}
|
||||
|
||||
fn get_argument(&self, i: usize) -> Result<StackValue> {
|
||||
self.args
|
||||
.get(i)
|
||||
|
|
@ -434,6 +447,7 @@ fn eval_one(
|
|||
}
|
||||
|
||||
Instruction::NewObject(slots) => {
|
||||
let class_id = f.pop_int()?;
|
||||
let name = f.pop_string()?;
|
||||
let mut values = Vec::with_capacity(slots);
|
||||
for _ in 0..slots {
|
||||
|
|
@ -442,6 +456,7 @@ fn eval_one(
|
|||
|
||||
let object = Object {
|
||||
name,
|
||||
class_id,
|
||||
values: values.into(),
|
||||
};
|
||||
|
||||
|
|
@ -451,6 +466,18 @@ fn eval_one(
|
|||
let obj = f.pop_object()?;
|
||||
f.push_value(obj.get_slot(slot)?);
|
||||
}
|
||||
Instruction::IsClass(id) => {
|
||||
let value = f.pop_value()?;
|
||||
match value {
|
||||
StackValue::Object(o) => {
|
||||
f.push_bool(o.class_id == id);
|
||||
}
|
||||
_ => f.push_bool(false),
|
||||
}
|
||||
}
|
||||
Instruction::PushInt(v) => {
|
||||
f.push_int(v);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Flow::Continue)
|
||||
|
|
@ -469,16 +496,16 @@ pub fn eval(
|
|||
let instructions = f.func.instructions();
|
||||
let instruction = instructions[index];
|
||||
|
||||
// {
|
||||
// eprint!("{index}: {instruction:?} [");
|
||||
// for val in f.stack.iter().rev().take(3) {
|
||||
// eprint!("{val:?} ");
|
||||
// }
|
||||
// if f.stack.len() > 3 {
|
||||
// eprint!("...");
|
||||
// }
|
||||
// eprintln!("]");
|
||||
// }
|
||||
{
|
||||
eprint!("{index}: {instruction:?} [");
|
||||
for val in f.stack.iter().rev().take(3) {
|
||||
eprint!("{val:?} ");
|
||||
}
|
||||
if f.stack.len() > 3 {
|
||||
eprint!("...");
|
||||
}
|
||||
eprintln!("]");
|
||||
}
|
||||
|
||||
index += 1;
|
||||
|
||||
|
|
|
|||
|
|
@ -13,16 +13,16 @@ fun test() -> bool {
|
|||
// | strings (0):
|
||||
// | code (15):
|
||||
// | 0: PushTrue
|
||||
// | 1: JumpFalse(4)
|
||||
// | 2: PushTrue
|
||||
// | 1: JumpTrue(4)
|
||||
// | 2: PushFalse
|
||||
// | 3: Jump(5)
|
||||
// | 4: PushFalse
|
||||
// | 5: JumpTrue(8)
|
||||
// | 5: JumpFalse(8)
|
||||
// | 6: PushTrue
|
||||
// | 7: Jump(14)
|
||||
// | 8: PushFalse
|
||||
// | 9: JumpFalse(12)
|
||||
// | 10: PushTrue
|
||||
// | 9: JumpTrue(12)
|
||||
// | 10: PushFalse
|
||||
// | 11: Jump(14)
|
||||
// | 12: PushTrue
|
||||
// | 13: BoolNot
|
||||
|
|
|
|||
|
|
@ -38,21 +38,23 @@ fun test() -> f64 {
|
|||
// | function Point (6 args, 0 locals):
|
||||
// | strings (1):
|
||||
// | 0: Point
|
||||
// | code (5):
|
||||
// | code (6):
|
||||
// | 0: LoadArgument(1)
|
||||
// | 1: LoadArgument(0)
|
||||
// | 2: PushString(0)
|
||||
// | 3: NewObject(2)
|
||||
// | 4: Return
|
||||
// | 3: PushInt(37)
|
||||
// | 4: NewObject(2)
|
||||
// | 5: Return
|
||||
// | function Line (4 args, 0 locals):
|
||||
// | strings (1):
|
||||
// | 0: Line
|
||||
// | code (5):
|
||||
// | code (6):
|
||||
// | 0: LoadArgument(1)
|
||||
// | 1: LoadArgument(0)
|
||||
// | 2: PushString(0)
|
||||
// | 3: NewObject(2)
|
||||
// | 4: Return
|
||||
// | 3: PushInt(44)
|
||||
// | 4: NewObject(2)
|
||||
// | 5: Return
|
||||
// | function test (0 args, 3 locals):
|
||||
// | strings (0):
|
||||
// | code (27):
|
||||
|
|
|
|||
|
|
@ -10,9 +10,10 @@ fun test() -> f64 {
|
|||
result = result + 1;
|
||||
}
|
||||
if b is c:Foo and c.a == 24 {
|
||||
result = result + 1;
|
||||
result = result + 10;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
// @no-errors
|
||||
// @eval: Float(1.0)
|
||||
Loading…
Add table
Add a link
Reference in a new issue