diff --git a/fine/src/compiler.rs b/fine/src/compiler.rs index 166beb8a..05fb34e7 100644 --- a/fine/src/compiler.rs +++ b/fine/src/compiler.rs @@ -17,7 +17,7 @@ pub const EXTERN_USER_FIRST: usize = 100000; // But I'm not cool. #[derive(Debug, Clone, Copy)] pub enum Instruction { - Panic, + Panic(usize), BoolNot, Call(usize), @@ -173,6 +173,22 @@ impl<'a> Compiler<'a> { index } + fn inst_panic(&mut self, description: T) -> Instruction + where + T: Into, + { + let index = self.add_string(description.into()); + Instruction::Panic(index) + } + + fn push_panic(&mut self, description: T) -> usize + where + T: Into, + { + let instruction = self.inst_panic(description); + self.push(instruction) + } + fn patch(&mut self, i: usize, f: impl FnOnce(usize) -> Instruction) { let index = self.function.instructions.len(); self.function.instructions[i] = f(index); @@ -226,12 +242,12 @@ macro_rules! ice { }} } -macro_rules! inst_panic { - ($($t:tt)+) => {{ - // eprintln!($($t)*); - Instruction::Panic - }}; -} +// macro_rules! inst_panic { +// ($($t:tt)+) => {{ +// // eprintln!($($t)*); +// Instruction::Panic +// }}; +// } // macro_rules! ice { // ($compiler:expr, $tr:expr, $($t:tt)*) => {{}}; @@ -318,11 +334,12 @@ fn compile_expression(c: &mut Compiler, t: TreeRef) { TreeKind::NewObjectExpression => compile_new_object_expression(c, t, tree), TreeKind::SelfReference => compile_self_reference(c), TreeKind::UnaryExpression => compile_unary_operator(c, t, tree), + TreeKind::MatchExpression => compile_match_expression(c, tree), _ => ice!(c, t, "{tree:?} is not an expression, cannot compile"), }; if matches!(cr, None) { - c.push(inst_panic!("panic compiling expression {:?}", tree)); + c.push_panic(format!("panic compiling expression {:?}", tree)); } } @@ -342,7 +359,7 @@ fn compile_literal(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { let index = c.add_string(result); c.push(Instruction::PushString(index)) } - Type::Error => c.push(inst_panic!("compiling literal {:?}", tr)), + Type::Error => c.push_panic(format!("compiling literal {:?}", tr)), _ => ice!(c, t, "unsupported literal type: {t:?}"), }; OK @@ -412,10 +429,10 @@ where fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { let op = tr.nth_token(1)?; match op.kind { - TokenKind::Plus => compile_simple_binary_expression(c, tr, |_, t| match t { + TokenKind::Plus => compile_simple_binary_expression(c, tr, |c, t| match t { Type::F64 => Instruction::FloatAdd, Type::String => Instruction::StringAdd, - _ => inst_panic!("panic adding {}", t), + _ => c.inst_panic(format!("panic adding {}", t)), }), TokenKind::Minus => { compile_simple_binary_expression(c, tr, |_, _| Instruction::FloatSubtract) @@ -427,30 +444,30 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { compile_simple_binary_expression(c, tr, |_, _| Instruction::FloatDivide) } - TokenKind::Less => compile_simple_binary_expression(c, tr, |_, t| match t { + TokenKind::Less => compile_simple_binary_expression(c, tr, |c, t| match t { Type::F64 => Instruction::LessFloat, Type::String => Instruction::LessString, - _ => inst_panic!("panic less {}", t), + _ => c.inst_panic(format!("panic less {}", t)), }), TokenKind::LessEqual => { - compile_simple_binary_expression(c, tr, |_, t| match t { + compile_simple_binary_expression(c, tr, |c, t| match t { Type::F64 => Instruction::GreaterFloat, Type::String => Instruction::GreaterString, - _ => inst_panic!("panic less equal {}", t), + _ => c.inst_panic(format!("panic less equal {}", t)), }); c.push(Instruction::BoolNot); OK } - TokenKind::Greater => compile_simple_binary_expression(c, tr, |_, t| match t { + TokenKind::Greater => compile_simple_binary_expression(c, tr, |c, t| match t { Type::F64 => Instruction::GreaterFloat, Type::String => Instruction::GreaterString, - _ => inst_panic!("panic greater {}", t), + _ => c.inst_panic(format!("panic greater {}", t)), }), TokenKind::GreaterEqual => { - compile_simple_binary_expression(c, tr, |_, t| match t { + compile_simple_binary_expression(c, tr, |c, t| match t { Type::F64 => Instruction::LessFloat, Type::String => Instruction::LessString, - _ => inst_panic!("panic greater equal {}", t), + _ => c.inst_panic(format!("panic greater equal {}", t)), }); c.push(Instruction::BoolNot); OK @@ -519,7 +536,7 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { Type::F64 => Instruction::EqFloat, Type::String => Instruction::EqString, Type::Bool => Instruction::EqBool, // ? - _ => inst_panic!("panic comparing {}", arg_type), + _ => c.inst_panic(format!("panic comparing {}", arg_type)), } } }) @@ -579,11 +596,11 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { } } - Declaration::ExternFunction { .. } => inst_panic!("store ext"), - Declaration::Function { .. } => inst_panic!("store func"), - Declaration::Class { .. } => inst_panic!("store class"), - Declaration::ImportedModule { .. } => inst_panic!("store import"), - Declaration::ImportedDeclaration { .. } => inst_panic!("store import decl"), + Declaration::ExternFunction { .. } => c.inst_panic("store ext"), + Declaration::Function { .. } => c.inst_panic("store func"), + Declaration::Class { .. } => c.inst_panic("store class"), + Declaration::ImportedModule { .. } => c.inst_panic("store import"), + Declaration::ImportedDeclaration { .. } => c.inst_panic("store import decl"), }; c.push(instruction); OK @@ -672,6 +689,36 @@ fn compile_load_declaration(c: &mut Compiler, t: TreeRef, declaration: &Declarat OK } +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)?; + 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)?)?; + + // ...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)?); + patches.push(c.push(Instruction::Jump(0))); + + c.patch(jump_next_index, |i| Instruction::JumpFalse(i)); + } + c.push_panic("Fell through all match arms"); + + // Patch the jumps to the end of the match expression. + for patch in patches { + c.patch(patch, |i| Instruction::Jump(i)); + } + + OK +} + fn compile_is_expression(c: &mut Compiler, tree: &Tree) -> Option<()> { compile_expression(c, tree.nth_tree(0)?); @@ -766,7 +813,7 @@ fn compile_type_expr_eq(c: &mut Compiler, t: TreeRef) { }; if result.is_none() { - c.push(inst_panic!("panic compiling type expression eq {:?}", tree)); + c.push_panic(format!("panic compiling type expression eq {:?}", tree)); } } @@ -877,7 +924,7 @@ fn compile_argument(c: &mut Compiler, tree: &Tree) -> CR { fn compile_new_object_expression(c: &mut Compiler, t: TreeRef, tree: &Tree) -> CR { // We pass in the arguments.... by... field order? let Type::Object(mid, ct, _) = c.semantics.type_of(t) else { - c.push(inst_panic!("new obj not ob")); + c.push_panic("new obj not ob"); return OK; }; @@ -963,7 +1010,7 @@ fn compile_member_access(c: &mut Compiler, t: TreeRef, tree: &Tree) -> CR { class.static_env.clone() } _ => { - c.push(inst_panic!("cannot get environment of {typ}")); + c.push_panic("cannot get environment of {typ}"); return None; } }; @@ -1018,7 +1065,7 @@ fn compile_statement(c: &mut Compiler, t: TreeRef, gen_value: bool) { _ => ice!(c, t, "unsupported statement tree kind {:?}", tree.kind), }; if matches!(cr, None) { - c.push(inst_panic!("stat {:?}", tree)); + c.push_panic(format!("stat {:?}", tree)); } } diff --git a/fine/src/vm.rs b/fine/src/vm.rs index 91660a03..032a8c7e 100644 --- a/fine/src/vm.rs +++ b/fine/src/vm.rs @@ -10,8 +10,8 @@ use thiserror::Error; #[derive(Error, Debug)] pub enum VMErrorCode { - #[error("code panic (syntax or semantic error)")] - Panic, + #[error("code panic (syntax or semantic error): {0}")] + Panic(Rc), #[error("internal error: stack underflow")] StackUnderflow, #[error("internal error: stack type mismatch: {0:?} is not {1:?}")] @@ -394,7 +394,12 @@ fn eval_one( stack: &mut Vec, ) -> Result { match instruction { - Instruction::Panic => return Err(VMErrorCode::Panic), + Instruction::Panic(index) => { + let v = f + .get_string(index) + .unwrap_or_else(|_| "!!panic string out of range!!".into()); + return Err(VMErrorCode::Panic(v)); + } Instruction::BoolNot => { let value = f.pop_bool()?; f.push_bool(!value); diff --git a/fine/tests/expression/alternates.fine b/fine/tests/expression/alternates.fine index 43eb5faf..27aae472 100644 --- a/fine/tests/expression/alternates.fine +++ b/fine/tests/expression/alternates.fine @@ -59,7 +59,7 @@ fun attack(weapon: MeleeWeapon or RangedWeapon, monster: Monster, distance: f64) // `and` that follows the declaration in the `is` still has the variables // from the `is` binding in scope. if weapon is MeleeWeapon and distance > 1 or - weapon is w : RangedWeapon and (distance < w.minRange or distance > w.maxRange) { + weapon is w:RangedWeapon and (distance < w.minRange or distance > w.maxRange) { print("You are out of range"); return } @@ -127,6 +127,12 @@ fun test() -> f64 { sum = sum + v; } + let it = new Iterator { current: 0 }; + sum = sum + match it.next() { + v:f64 -> 100, + _ -> 1000, + }; + // Unroll by hand... let it = new Iterator { current: 0 }; while true { @@ -141,5 +147,6 @@ fun test() -> f64 { // like the above. } -// @ignore never finished compiling `match` -// @no-errors \ No newline at end of file +// @ignore not finished yet, still compiler bugs +// @no-errors +// @eval: Float(90.0) \ No newline at end of file