diff --git a/fine/src/compiler.rs b/fine/src/compiler.rs index 671d7a91..ba4300a8 100644 --- a/fine/src/compiler.rs +++ b/fine/src/compiler.rs @@ -15,23 +15,18 @@ pub enum Instruction { BoolNot, Call(usize), + CompareBool, + CompareFloat, + CompareString, Discard, Dup, - EqBool, - EqFloat, - EqString, FloatAdd, FloatDivide, FloatMultiply, FloatSubtract, - GreaterFloat, - GreaterString, - IsClass(i64), Jump(usize), JumpFalse(usize), JumpTrue(usize), // TODO: Only one of these, and use BoolNot? - LessFloat, - LessString, LoadArgument(usize), LoadExternFunction(usize), // NOTE: FUNKY, might want to indirect this index. LoadFunction(usize), @@ -41,7 +36,6 @@ pub enum Instruction { NewObject(usize), PushFalse, PushFloat(f64), - PushInt(i64), PushNothing, PushString(usize), PushTrue, @@ -50,6 +44,9 @@ pub enum Instruction { StoreLocal(usize), StoreModule(usize), StringAdd, + + IsClass(i64), + PushInt(i64), } pub enum Export { @@ -414,8 +411,7 @@ where } fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { - let op = tr.nth_token(1)?; - match op.kind { + match tr.nth_token(1)?.kind { TokenKind::Plus => compile_simple_binary_expression(c, tr, |_, t| match t { Type::F64 => Instruction::FloatAdd, Type::String => Instruction::StringAdd, @@ -430,36 +426,6 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { TokenKind::Slash => { compile_simple_binary_expression(c, tr, |_, _| Instruction::FloatDivide) } - - TokenKind::Less => compile_simple_binary_expression(c, tr, |_, t| match t { - Type::F64 => Instruction::LessFloat, - Type::String => Instruction::LessString, - _ => inst_panic!("panic less {}", t), - }), - TokenKind::LessEqual => { - compile_simple_binary_expression(c, tr, |_, t| match t { - Type::F64 => Instruction::GreaterFloat, - Type::String => Instruction::GreaterString, - _ => inst_panic!("panic less equal {}", t), - }); - c.push(Instruction::BoolNot); - OK - } - TokenKind::Greater => compile_simple_binary_expression(c, tr, |_, t| match t { - Type::F64 => Instruction::GreaterFloat, - Type::String => Instruction::GreaterString, - _ => inst_panic!("panic greater {}", t), - }), - TokenKind::GreaterEqual => { - compile_simple_binary_expression(c, tr, |_, t| match t { - Type::F64 => Instruction::LessFloat, - Type::String => Instruction::LessString, - _ => inst_panic!("panic greater equal {}", t), - }); - c.push(Instruction::BoolNot); - OK - } - TokenKind::And => { // Compile the left hand side, it leaves a bool on the stack compile_expression(c, tr.nth_tree(0)?); @@ -520,9 +486,9 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { Instruction::PushTrue } else { match arg_type { - Type::F64 => Instruction::EqFloat, - Type::String => Instruction::EqString, - Type::Bool => Instruction::EqBool, // ? + Type::F64 => Instruction::CompareFloat, + Type::String => Instruction::CompareString, + Type::Bool => Instruction::CompareBool, // ? _ => inst_panic!("panic comparing {}", arg_type), } } @@ -579,7 +545,7 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { } OK } - _ => ice!(c, t, "Unsupported binary expression '{op}'"), + _ => ice!(c, t, "Unsupported binary expression"), } } @@ -932,15 +898,13 @@ fn compile_self_reference(c: &mut Compiler) -> CR { fn compile_statement(c: &mut Compiler, t: TreeRef, gen_value: bool) { let tree = &c.semantics.tree()[t]; let cr = match tree.kind { - TreeKind::Block => compile_block_statement(c, t, gen_value), - TreeKind::ClassDecl => compile_class_declaration(c, t, tree, gen_value), - TreeKind::ExpressionStatement => compile_expression_statement(c, tree, gen_value), TreeKind::FunctionDecl => compile_function_declaration(c, t, tree, gen_value), - TreeKind::IfStatement => compile_if_statement(c, tree, gen_value), + TreeKind::ClassDecl => compile_class_declaration(c, t, tree, gen_value), TreeKind::LetStatement => compile_let_statement(c, t, tree, gen_value), - TreeKind::WhileStatement => compile_while_statement(c, tree, gen_value), - - _ => ice!(c, t, "unsupported statement tree kind {:?}", tree.kind), + TreeKind::ExpressionStatement => compile_expression_statement(c, tree, gen_value), + TreeKind::IfStatement => compile_if_statement(c, tree, gen_value), + TreeKind::Block => compile_block_statement(c, t, gen_value), + _ => ice!(c, t, "unsupported tree kind {:?}", tree.kind), }; if matches!(cr, None) { c.push(inst_panic!("stat {:?}", tree)); @@ -1112,20 +1076,3 @@ fn compile_block_statement(c: &mut Compiler, t: TreeRef, gen_value: bool) -> CR OK } - -fn compile_while_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR { - let start_index = c.function.instructions.len(); - compile_expression(c, tree.nth_tree(1)?); - - let jump_end_index = c.push(Instruction::JumpFalse(0)); - - compile_block_statement(c, tree.nth_tree(2)?, false); - c.push(Instruction::Jump(start_index)); - - c.patch(jump_end_index, |i| Instruction::JumpFalse(i)); - if gen_value { - c.push(Instruction::PushNothing); - } - - OK -} diff --git a/fine/src/parser.rs b/fine/src/parser.rs index cebede90..fd0619b0 100644 --- a/fine/src/parser.rs +++ b/fine/src/parser.rs @@ -143,14 +143,10 @@ pub enum TreeKind { ListConstructor, ListConstructorElement, LiteralExpression, - MatchArm, - MatchBody, - MatchExpression, MemberAccess, NewObjectExpression, ParamList, Parameter, - Pattern, ReturnStatement, ReturnType, SelfParameter, @@ -160,8 +156,13 @@ pub enum TreeKind { TypeParameter, TypeParameterList, UnaryExpression, + + Pattern, VariableBinding, - WhileStatement, + + MatchExpression, + MatchBody, + MatchArm, WildcardPattern, } @@ -780,7 +781,6 @@ const STATEMENT_RECOVERY: &[TokenKind] = &[ TokenKind::Return, TokenKind::For, TokenKind::Class, - TokenKind::While, ]; fn block(p: &mut CParser) { @@ -813,8 +813,6 @@ fn statement(p: &mut CParser) -> bool { // require a semicolon at the end if it's all by itself. TokenKind::If => statement_if(p), - TokenKind::While => statement_while(p), - _ => { if p.at(TokenKind::Semicolon) || p.at_any(EXPRESSION_FIRST) { statement_expression(p) @@ -836,24 +834,6 @@ fn statement_if(p: &mut CParser) { p.end(m, TreeKind::IfStatement); } -fn statement_while(p: &mut CParser) { - let m = p.start(); - - p.expect_start(TokenKind::While); - if p.at_any(EXPRESSION_FIRST) { - expression(p); - } else { - p.error("expected an expression for the loop condition"); - } - if p.at(TokenKind::LeftBrace) { - block(p); - } else { - p.error("expected a block for the loop body"); - } - - p.end(m, TreeKind::WhileStatement); -} - fn statement_let(p: &mut CParser) { let m = p.start(); diff --git a/fine/src/semantics.rs b/fine/src/semantics.rs index 4823adf6..1450eec8 100644 --- a/fine/src/semantics.rs +++ b/fine/src/semantics.rs @@ -171,7 +171,7 @@ impl fmt::Display for Type { Error => write!(f, "<< INTERNAL ERROR >>"), Unreachable => write!(f, "<< UNREACHABLE >>"), Assignment(_) => write!(f, "assignment"), - Nothing => write!(f, "nothing"), + Nothing => write!(f, "()"), F64 => write!(f, "f64"), I64 => write!(f, "i64"), String => write!(f, "string"), @@ -1139,9 +1139,9 @@ impl<'a> Semantics<'a> { TreeKind::Block => self.type_of_block(tree), TreeKind::CallExpression => self.type_of_call(tree), TreeKind::ClassDecl => self.type_of_class_decl(t, tree), + TreeKind::FieldDecl => self.type_of_field_decl(tree), TreeKind::ConditionalExpression => self.type_of_conditional(tree), TreeKind::ExpressionStatement => self.type_of_expression_statement(tree), - TreeKind::FieldDecl => self.type_of_field_decl(tree), TreeKind::FieldValue => self.type_of_field_value(t, tree), TreeKind::ForStatement => Some(Type::Nothing), TreeKind::FunctionDecl => self.type_of_function_decl(tree), @@ -1153,9 +1153,6 @@ impl<'a> Semantics<'a> { TreeKind::ListConstructor => self.type_of_list_constructor(t, tree), TreeKind::ListConstructorElement => self.type_of_list_constructor_element(tree), TreeKind::LiteralExpression => self.type_of_literal(tree), - TreeKind::MatchArm => self.type_of_match_arm(tree), - TreeKind::MatchBody => self.type_of_match_body(tree), - TreeKind::MatchExpression => self.type_of_match_expression(tree), TreeKind::MemberAccess => self.type_of_member_access(tree), TreeKind::NewObjectExpression => self.type_of_new_object_expression(tree), TreeKind::Parameter => self.type_of_parameter(tree), @@ -1167,7 +1164,10 @@ impl<'a> Semantics<'a> { TreeKind::TypeIdentifier => self.type_of_type_identifier(t, tree), TreeKind::TypeParameter => self.type_of_type_parameter(tree), TreeKind::UnaryExpression => self.type_of_unary(tree), - TreeKind::WhileStatement => Some(Type::Nothing), + + TreeKind::MatchExpression => self.type_of_match_expression(tree), + TreeKind::MatchBody => self.type_of_match_body(tree), + TreeKind::MatchArm => self.type_of_match_arm(tree), _ => self.internal_compiler_error(Some(t), "asking for a nonsense type"), }; @@ -1241,16 +1241,6 @@ impl<'a> Semantics<'a> { (TokenKind::EqualEqual, Type::Bool, Type::Bool) => Some(Type::Bool), (TokenKind::EqualEqual, Type::Nothing, Type::Nothing) => Some(Type::Bool), - (TokenKind::Less, Type::F64, Type::F64) => Some(Type::Bool), - (TokenKind::LessEqual, Type::F64, Type::F64) => Some(Type::Bool), - (TokenKind::Greater, Type::F64, Type::F64) => Some(Type::Bool), - (TokenKind::GreaterEqual, Type::F64, Type::F64) => Some(Type::Bool), - - (TokenKind::Less, Type::String, Type::String) => Some(Type::Bool), - (TokenKind::LessEqual, Type::String, Type::String) => Some(Type::Bool), - (TokenKind::Greater, Type::String, Type::String) => Some(Type::Bool), - (TokenKind::GreaterEqual, Type::String, Type::String) => Some(Type::Bool), - // This is dumb and should be punished, probably. (_, _, Type::Unreachable) => { self.report_error( @@ -1363,7 +1353,7 @@ impl<'a> Semantics<'a> { "f64" => Some(Type::F64), "string" => Some(Type::String), "bool" => Some(Type::Bool), - "nothing" => Some(Type::Nothing), + "()" => Some(Type::Nothing), "list" => { let args = tree.child_tree_of_kind(self.syntax_tree, TreeKind::TypeParameterList)?; @@ -2051,8 +2041,6 @@ pub fn check(s: &Semantics) { TreeKind::MatchArm => {} TreeKind::MatchBody => check_match_body(s, t, tree), TreeKind::MatchExpression => {} - - TreeKind::WhileStatement => check_while_statement(s, tree), } } } @@ -2252,7 +2240,6 @@ fn check_match_body(s: &Semantics, t: TreeRef, _tree: &Tree) { let _ = s.type_of(t); // Checks arm count and compatibility. // TODO: completeness checks - // https://doc.rust-lang.org/nightly/nightly-rustc/rustc_pattern_analysis/usefulness/index.html // let arms: Vec<_> = tree // .children_of_kind(s.syntax_tree, TreeKind::MatchArm) @@ -2266,18 +2253,6 @@ fn check_match_body(s: &Semantics, t: TreeRef, _tree: &Tree) { // } } -fn check_while_statement(s: &Semantics, tree: &Tree) { - if let Some(expr) = tree.nth_tree(1) { - let expr_type = s.type_of(expr); - if !s.can_convert(&expr_type, &Type::Bool) { - s.report_error_tree_ref( - expr, - "the condition of the while loop must produce a boolean", - ); - } - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/fine/src/vm.rs b/fine/src/vm.rs index 33de826d..96895258 100644 --- a/fine/src/vm.rs +++ b/fine/src/vm.rs @@ -430,41 +430,21 @@ fn eval_one( f.push_string(new_string.into()); } - Instruction::EqBool => { + Instruction::CompareBool => { let x = f.pop_bool()?; let y = f.pop_bool()?; f.push_bool(x == y); } - Instruction::EqFloat => { + Instruction::CompareFloat => { let x = f.pop_float()?; let y = f.pop_float()?; f.push_bool(x == y); } - Instruction::EqString => { + Instruction::CompareString => { let x = f.pop_string()?; let y = f.pop_string()?; f.push_bool(x == y); } - Instruction::GreaterFloat => { - let x = f.pop_float()?; - let y = f.pop_float()?; - f.push_bool(y > x); - } - Instruction::GreaterString => { - let x = f.pop_string()?; - let y = f.pop_string()?; - f.push_bool(y > x); - } - Instruction::LessFloat => { - let x = f.pop_float()?; - let y = f.pop_float()?; - f.push_bool(y < x); - } - Instruction::LessString => { - let x = f.pop_string()?; - let y = f.pop_string()?; - f.push_bool(y < x); - } Instruction::NewObject(slots) => { let class_id = f.pop_int()?; @@ -516,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; diff --git a/fine/tests/expression/alternates.fine b/fine/tests/expression/alternates.fine index 8b922480..97bb3007 100644 --- a/fine/tests/expression/alternates.fine +++ b/fine/tests/expression/alternates.fine @@ -46,7 +46,7 @@ fun attack(weapon: MeleeWeapon or RangedWeapon, monster: Monster, distance: f64) // 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) { - print("You are out of range"); + print("You are out of range") return } @@ -72,7 +72,7 @@ fun attack(weapon: MeleeWeapon or RangedWeapon, monster: Monster, distance: f64) } } -fun more_examples(weapon: MeleeWeapon or RangedWeapon) -> f64 or nothing { +fun more_examples(weapon: MeleeWeapon or RangedWeapon) -> f64 or () { if weapon is w: RangedWeapon and w.maxRange > 10 { // w is still in scope here; the `and` is bound into a predicate expression // and breaks exhaustivity @@ -115,7 +115,7 @@ fun test() -> f64 { // Unroll by hand... let it = new Iterator { current: 0 }; - while true { + loop { if it.next() is v: f64 { sum = sum + v; } else { diff --git a/fine/tests/expression/block.fine b/fine/tests/expression/block.fine index c6785fa2..6b9f348d 100644 --- a/fine/tests/expression/block.fine +++ b/fine/tests/expression/block.fine @@ -16,7 +16,7 @@ fun test() { // | 1: Return // | // @eval: Nothing -// @type: 15 nothing +// @type: 15 () // @concrete: // | File // | FunctionDecl diff --git a/fine/tests/expression/errors/if_requires_else.fine b/fine/tests/expression/errors/if_requires_else.fine index 153134b8..bdc5a229 100644 --- a/fine/tests/expression/errors/if_requires_else.fine +++ b/fine/tests/expression/errors/if_requires_else.fine @@ -1,4 +1,4 @@ if (if false { true }) { 32 } else { 23 } // @expect-errors: -// | 1:4: the type of the 'then' branch ('bool') must match the type of the 'else' branch ('nothing') +// | 1:4: the type of the 'then' branch ('bool') must match the type of the 'else' branch ('()') diff --git a/fine/tests/expression/errors/while_not_bool.fine b/fine/tests/expression/errors/while_not_bool.fine deleted file mode 100644 index edd47dc8..00000000 --- a/fine/tests/expression/errors/while_not_bool.fine +++ /dev/null @@ -1,6 +0,0 @@ -fun test() { - while 12 {} -} - -// @expect-errors: -// | 2:8: the condition of the while loop must produce a boolean diff --git a/fine/tests/expression/loops.fine b/fine/tests/expression/loops.fine deleted file mode 100644 index d0b67c55..00000000 --- a/fine/tests/expression/loops.fine +++ /dev/null @@ -1,12 +0,0 @@ -fun test() -> f64 { - let result = 1; - let i = 0; - while i < 10 { - result = result * 2; - i = i + 1; - } - result -} - -// @no-errors -// @eval: Float(1024.0) \ No newline at end of file