diff --git a/cozo-core/src/data/expr.rs b/cozo-core/src/data/expr.rs index 5316f2a0..ef167fb0 100644 --- a/cozo-core/src/data/expr.rs +++ b/cozo-core/src/data/expr.rs @@ -88,11 +88,17 @@ pub fn eval_bytecode( ) -> Result { stack.clear(); let mut pointer = 0; + // for (i, c) in bytecodes.iter().enumerate() { + // println!("{i} {c:?}"); + // } + // println!(); loop { - if pointer >= bytecodes.len() { + // println!("{pointer} {stack:?}"); + if pointer == bytecodes.len() { break; } let current_instruction = &bytecodes[pointer]; + // println!("{current_instruction:?}"); match current_instruction { Bytecode::Binding { var, tuple_pos, .. } => match tuple_pos { None => { diff --git a/cozo-core/src/data/tests/exprs.rs b/cozo-core/src/data/tests/exprs.rs new file mode 100644 index 00000000..359f2c90 --- /dev/null +++ b/cozo-core/src/data/tests/exprs.rs @@ -0,0 +1,25 @@ +/* + * Copyright 2022, The Cozo Project Authors. + * + * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. + * If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +use crate::{DataValue, new_cozo_mem}; + +#[test] +fn expression_eval() { + let db = new_cozo_mem().unwrap(); + + + let res = db.run_script(r#" + ?[a] := a = if(2 + 3 > 1 * 99999, 190291021 + 14341234212 / 2121) + "#, Default::default()).unwrap(); + assert_eq!(res.rows[0][0], DataValue::Null); + + let res = db.run_script(r#" + ?[a] := a = if(2 + 3 > 1, true, false) + "#, Default::default()).unwrap(); + assert_eq!(res.rows[0][0].get_bool().unwrap(), true); +} \ No newline at end of file diff --git a/cozo-core/src/data/tests/mod.rs b/cozo-core/src/data/tests/mod.rs index f673829f..194902d2 100644 --- a/cozo-core/src/data/tests/mod.rs +++ b/cozo-core/src/data/tests/mod.rs @@ -11,4 +11,5 @@ mod aggrs; mod validity; mod json; mod memcmp; -mod values; \ No newline at end of file +mod values; +mod exprs; \ No newline at end of file diff --git a/cozo-core/src/parse/expr.rs b/cozo-core/src/parse/expr.rs index 867e6646..a973d100 100644 --- a/cozo-core/src/parse/expr.rs +++ b/cozo-core/src/parse/expr.rs @@ -71,18 +71,19 @@ pub(crate) fn expr2bytecode(expr: &Expr, collector: &mut Vec) { expr2bytecode(cond, collector); // -1 collector.push(Bytecode::JumpIfFalse { jump_to: 0, span: *span }); - let false_jump_amend_pos = collector.len(); + let false_jump_amend_pos = collector.len() - 1; // +1 in this branch expr2bytecode(val, collector); collector.push(Bytecode::Goto { jump_to: 0, span: *span }); - return_jump_pos.push(collector.len()); + return_jump_pos.push(collector.len() - 1); collector[false_jump_amend_pos] = Bytecode::JumpIfFalse { - jump_to: collector.len() + 1, + jump_to: collector.len(), span: *span, }; } + let total_len = collector.len(); for pos in return_jump_pos { - collector[pos] = Bytecode::Goto { jump_to: pos, span: *span } + collector[pos] = Bytecode::Goto { jump_to: total_len, span: *span } } } } @@ -267,11 +268,18 @@ fn build_term(pair: Pair<'_>, param_pool: &BTreeMap) -> Resul match ident { "cond" => { + if args.is_empty() { + #[derive(Error, Diagnostic, Debug)] + #[error("'cond' cannot have empty body")] + #[diagnostic(code(parser::empty_cond))] + struct EmptyCond(#[label] SourceSpan); + bail!(EmptyCond(span)); + } if args.len() & 1 == 1 { args.insert( args.len() - 1, Expr::Const { - val: DataValue::from(true), + val: DataValue::Null, span: args.last().unwrap().span(), }, ) @@ -280,16 +288,23 @@ fn build_term(pair: Pair<'_>, param_pool: &BTreeMap) -> Resul .chunks(2) .map(|pair| (pair[0].clone(), pair[1].clone())) .collect_vec(); - clauses.push(( - Expr::Const { - val: DataValue::from(true), - span, - }, - Expr::Const { - val: DataValue::Null, - span, - }, - )); + if let Some((cond, _)) = clauses.last() { + match cond { + Expr::Const { val: DataValue::Bool(true), ..} => {} + _ => { + clauses.push(( + Expr::Const { + val: DataValue::from(true), + span, + }, + Expr::Const { + val: DataValue::Null, + span, + }, + )); + } + } + } Expr::Cond { clauses, span } } "if" => { @@ -311,7 +326,7 @@ fn build_term(pair: Pair<'_>, param_pool: &BTreeMap) -> Resul span, }, args.next().unwrap_or(Expr::Const { - val: DataValue::from(true), + val: DataValue::Null, span, }), ));