[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,
|
FloatSubtract,
|
||||||
Jump(usize),
|
Jump(usize),
|
||||||
JumpFalse(usize),
|
JumpFalse(usize),
|
||||||
JumpTrue(usize),
|
JumpTrue(usize), // TODO: Only one of these, and use BoolNot?
|
||||||
LoadArgument(usize),
|
LoadArgument(usize),
|
||||||
LoadExternFunction(usize), // NOTE: FUNKY, might want to indirect this index.
|
LoadExternFunction(usize), // NOTE: FUNKY, might want to indirect this index.
|
||||||
LoadFunction(usize),
|
LoadFunction(usize),
|
||||||
|
|
@ -44,6 +44,9 @@ pub enum Instruction {
|
||||||
StoreLocal(usize),
|
StoreLocal(usize),
|
||||||
StoreModule(usize),
|
StoreModule(usize),
|
||||||
StringAdd,
|
StringAdd,
|
||||||
|
|
||||||
|
IsClass(i64),
|
||||||
|
PushInt(i64),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Export {
|
pub enum Export {
|
||||||
|
|
@ -286,19 +289,22 @@ 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 => 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::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::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::MemberAccess => compile_member_access(c, tree),
|
||||||
|
TreeKind::NewObjectExpression => compile_new_object_expression(c, t, tree),
|
||||||
TreeKind::SelfReference => compile_self_reference(c),
|
TreeKind::SelfReference => compile_self_reference(c),
|
||||||
|
TreeKind::UnaryExpression => compile_unary_operator(c, t, tree),
|
||||||
|
|
||||||
_ => 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 matches!(cr, None) {
|
||||||
|
|
@ -374,15 +380,16 @@ fn compile_condition_expression(c: &mut Compiler, t: &Tree) -> CR {
|
||||||
let then_branch = t.nth_tree(2)?;
|
let then_branch = t.nth_tree(2)?;
|
||||||
compile_expression(c, then_branch);
|
compile_expression(c, then_branch);
|
||||||
|
|
||||||
if let Some(else_branch) = t.nth_tree(4) {
|
let jump_end_index = c.push(Instruction::Jump(0));
|
||||||
let jump_end_index = c.push(Instruction::Jump(0));
|
c.patch(jump_else_index, |i| Instruction::JumpFalse(i));
|
||||||
c.patch(jump_else_index, |i| Instruction::JumpFalse(i));
|
|
||||||
|
|
||||||
|
if let Some(else_branch) = t.nth_tree(4) {
|
||||||
compile_expression(c, else_branch);
|
compile_expression(c, else_branch);
|
||||||
c.patch(jump_end_index, |i| Instruction::Jump(i));
|
|
||||||
} else {
|
} else {
|
||||||
c.patch(jump_else_index, |i| Instruction::JumpFalse(i));
|
c.push(Instruction::PushNothing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.patch(jump_end_index, |i| Instruction::Jump(i));
|
||||||
OK
|
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)
|
compile_simple_binary_expression(c, tr, |_, _| Instruction::FloatDivide)
|
||||||
}
|
}
|
||||||
TokenKind::And => {
|
TokenKind::And => {
|
||||||
|
// 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)?);
|
||||||
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));
|
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)?);
|
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));
|
c.patch(jump_end_index, |i| Instruction::Jump(i));
|
||||||
OK
|
OK
|
||||||
}
|
}
|
||||||
TokenKind::Or => {
|
TokenKind::Or => {
|
||||||
|
// 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)?);
|
||||||
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);
|
c.push(Instruction::PushTrue);
|
||||||
let jump_end_index = c.push(Instruction::Jump(0));
|
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)?);
|
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));
|
c.patch(jump_end_index, |i| Instruction::Jump(i));
|
||||||
OK
|
OK
|
||||||
}
|
}
|
||||||
|
|
@ -589,6 +620,112 @@ fn compile_load_declaration(c: &mut Compiler, t: TreeRef, declaration: &Declarat
|
||||||
OK
|
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 {
|
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)?;
|
||||||
let mut args: Vec<_> = arg_list.child_trees().collect();
|
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 = tree.nth_token(1)?.as_str();
|
||||||
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::NewObject(count));
|
c.push(Instruction::NewObject(count));
|
||||||
}
|
}
|
||||||
_ => ice!(c, t, "what is this tree doing in compile_function?"),
|
_ => 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
|
// TODO: Numeric literals should be implicitly convertable, unlike other
|
||||||
// types. Maybe just "numeric literal" type?
|
// types. Maybe just "numeric literal" type?
|
||||||
F64,
|
F64,
|
||||||
|
I64,
|
||||||
String,
|
String,
|
||||||
Bool,
|
Bool,
|
||||||
|
|
||||||
|
|
@ -172,6 +173,7 @@ impl fmt::Display for Type {
|
||||||
Assignment(_) => write!(f, "assignment"),
|
Assignment(_) => write!(f, "assignment"),
|
||||||
Nothing => write!(f, "()"),
|
Nothing => write!(f, "()"),
|
||||||
F64 => write!(f, "f64"),
|
F64 => write!(f, "f64"),
|
||||||
|
I64 => write!(f, "i64"),
|
||||||
String => write!(f, "string"),
|
String => write!(f, "string"),
|
||||||
Bool => write!(f, "bool"),
|
Bool => write!(f, "bool"),
|
||||||
Function(args, ret) => {
|
Function(args, ret) => {
|
||||||
|
|
@ -831,6 +833,9 @@ impl<'a> Semantics<'a> {
|
||||||
return Environment::error();
|
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);
|
let mut env = Environment::new(Some(parent), Location::Local);
|
||||||
env.insert(variable, type_expr);
|
env.insert(variable, type_expr);
|
||||||
return EnvironmentRef::new(env);
|
return EnvironmentRef::new(env);
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ type Result<T> = std::result::Result<T, VMErrorCode>;
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Object {
|
pub struct Object {
|
||||||
name: Rc<str>,
|
name: Rc<str>,
|
||||||
|
class_id: i64,
|
||||||
values: Box<[StackValue]>,
|
values: Box<[StackValue]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,6 +69,7 @@ pub enum StackValue {
|
||||||
Nothing,
|
Nothing,
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
Float(f64),
|
Float(f64),
|
||||||
|
Int(i64),
|
||||||
String(Rc<str>),
|
String(Rc<str>),
|
||||||
Function(Rc<Function>),
|
Function(Rc<Function>),
|
||||||
ExternFunction(usize),
|
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) {
|
fn push_value(&mut self, v: StackValue) {
|
||||||
self.stack.push(v)
|
self.stack.push(v)
|
||||||
}
|
}
|
||||||
|
|
@ -178,6 +187,10 @@ impl Frame {
|
||||||
self.push_value(StackValue::Object(v));
|
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> {
|
fn get_argument(&self, i: usize) -> Result<StackValue> {
|
||||||
self.args
|
self.args
|
||||||
.get(i)
|
.get(i)
|
||||||
|
|
@ -434,6 +447,7 @@ fn eval_one(
|
||||||
}
|
}
|
||||||
|
|
||||||
Instruction::NewObject(slots) => {
|
Instruction::NewObject(slots) => {
|
||||||
|
let class_id = f.pop_int()?;
|
||||||
let name = f.pop_string()?;
|
let name = f.pop_string()?;
|
||||||
let mut values = Vec::with_capacity(slots);
|
let mut values = Vec::with_capacity(slots);
|
||||||
for _ in 0..slots {
|
for _ in 0..slots {
|
||||||
|
|
@ -442,6 +456,7 @@ fn eval_one(
|
||||||
|
|
||||||
let object = Object {
|
let object = Object {
|
||||||
name,
|
name,
|
||||||
|
class_id,
|
||||||
values: values.into(),
|
values: values.into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -451,6 +466,18 @@ fn eval_one(
|
||||||
let obj = f.pop_object()?;
|
let obj = f.pop_object()?;
|
||||||
f.push_value(obj.get_slot(slot)?);
|
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)
|
Ok(Flow::Continue)
|
||||||
|
|
@ -469,16 +496,16 @@ pub fn eval(
|
||||||
let instructions = f.func.instructions();
|
let instructions = f.func.instructions();
|
||||||
let instruction = instructions[index];
|
let instruction = instructions[index];
|
||||||
|
|
||||||
// {
|
{
|
||||||
// eprint!("{index}: {instruction:?} [");
|
eprint!("{index}: {instruction:?} [");
|
||||||
// for val in f.stack.iter().rev().take(3) {
|
for val in f.stack.iter().rev().take(3) {
|
||||||
// eprint!("{val:?} ");
|
eprint!("{val:?} ");
|
||||||
// }
|
}
|
||||||
// if f.stack.len() > 3 {
|
if f.stack.len() > 3 {
|
||||||
// eprint!("...");
|
eprint!("...");
|
||||||
// }
|
}
|
||||||
// eprintln!("]");
|
eprintln!("]");
|
||||||
// }
|
}
|
||||||
|
|
||||||
index += 1;
|
index += 1;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,16 +13,16 @@ fun test() -> bool {
|
||||||
// | strings (0):
|
// | strings (0):
|
||||||
// | code (15):
|
// | code (15):
|
||||||
// | 0: PushTrue
|
// | 0: PushTrue
|
||||||
// | 1: JumpFalse(4)
|
// | 1: JumpTrue(4)
|
||||||
// | 2: PushTrue
|
// | 2: PushFalse
|
||||||
// | 3: Jump(5)
|
// | 3: Jump(5)
|
||||||
// | 4: PushFalse
|
// | 4: PushFalse
|
||||||
// | 5: JumpTrue(8)
|
// | 5: JumpFalse(8)
|
||||||
// | 6: PushTrue
|
// | 6: PushTrue
|
||||||
// | 7: Jump(14)
|
// | 7: Jump(14)
|
||||||
// | 8: PushFalse
|
// | 8: PushFalse
|
||||||
// | 9: JumpFalse(12)
|
// | 9: JumpTrue(12)
|
||||||
// | 10: PushTrue
|
// | 10: PushFalse
|
||||||
// | 11: Jump(14)
|
// | 11: Jump(14)
|
||||||
// | 12: PushTrue
|
// | 12: PushTrue
|
||||||
// | 13: BoolNot
|
// | 13: BoolNot
|
||||||
|
|
|
||||||
|
|
@ -38,21 +38,23 @@ fun test() -> f64 {
|
||||||
// | function Point (6 args, 0 locals):
|
// | function Point (6 args, 0 locals):
|
||||||
// | strings (1):
|
// | strings (1):
|
||||||
// | 0: Point
|
// | 0: Point
|
||||||
// | code (5):
|
// | code (6):
|
||||||
// | 0: LoadArgument(1)
|
// | 0: LoadArgument(1)
|
||||||
// | 1: LoadArgument(0)
|
// | 1: LoadArgument(0)
|
||||||
// | 2: PushString(0)
|
// | 2: PushString(0)
|
||||||
// | 3: NewObject(2)
|
// | 3: PushInt(37)
|
||||||
// | 4: Return
|
// | 4: NewObject(2)
|
||||||
|
// | 5: Return
|
||||||
// | function Line (4 args, 0 locals):
|
// | function Line (4 args, 0 locals):
|
||||||
// | strings (1):
|
// | strings (1):
|
||||||
// | 0: Line
|
// | 0: Line
|
||||||
// | code (5):
|
// | code (6):
|
||||||
// | 0: LoadArgument(1)
|
// | 0: LoadArgument(1)
|
||||||
// | 1: LoadArgument(0)
|
// | 1: LoadArgument(0)
|
||||||
// | 2: PushString(0)
|
// | 2: PushString(0)
|
||||||
// | 3: NewObject(2)
|
// | 3: PushInt(44)
|
||||||
// | 4: Return
|
// | 4: NewObject(2)
|
||||||
|
// | 5: Return
|
||||||
// | function test (0 args, 3 locals):
|
// | function test (0 args, 3 locals):
|
||||||
// | strings (0):
|
// | strings (0):
|
||||||
// | code (27):
|
// | code (27):
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,10 @@ fun test() -> f64 {
|
||||||
result = result + 1;
|
result = result + 1;
|
||||||
}
|
}
|
||||||
if b is c:Foo and c.a == 24 {
|
if b is c:Foo and c.a == 24 {
|
||||||
result = result + 1;
|
result = result + 10;
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
// @no-errors
|
// @no-errors
|
||||||
|
// @eval: Float(1.0)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue