diff --git a/src/bits.rs b/src/bits.rs index e0289a80..cf72e039 100644 --- a/src/bits.rs +++ b/src/bits.rs @@ -495,16 +495,18 @@ pub fn deserialized_term(bits: &BitVec) -> Option { // A Rule pub fn serialize_rule(rule: &Rule, bits: &mut BitVec, names: &mut Names) { - serialize_term(&rule.lhs, bits, names); + serialize_list(serialize_term, &rule.lhs, bits, names); serialize_term(&rule.rhs, bits, names); } pub fn deserialize_rule(bits: &BitVec, index: &mut u128, names: &mut Names) -> Option { - let lhs = deserialize_term(bits, index, names)?; + let lhs = deserialize_list(deserialize_term, bits, index, names)?; let rhs = deserialize_term(bits, index, names)?; Some(Rule{lhs, rhs}) } +// A lhs + // A Func pub fn serialize_func(func: &Func, bits: &mut BitVec, names: &mut Names) { diff --git a/src/cli.rs b/src/cli.rs index 336b2342..361b7fca 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -390,9 +390,8 @@ pub fn get_info(kind: GetKind) -> Result<(), String> { GetKind::Fn { name, stat } => match stat { GetFnKind::Code => todo!(), GetFnKind::State => { - let state = run_async_blocking(client.get_function_state(name)); - // TODO: Display trait on `Term` - println!("{:?}", state); + let state = run_async_blocking(client.get_function_state(name))?; + println!("{}", state); Ok(()) } GetFnKind::Slots => todo!(), diff --git a/src/hvm.rs b/src/hvm.rs index bac85fa4..5757cdc9 100644 --- a/src/hvm.rs +++ b/src/hvm.rs @@ -330,7 +330,7 @@ pub type Map = util::U128Map; /// A rewrite rule, or equation, in the shape of `left_hand_side = right_hand_side`. #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Rule { - pub lhs: Term, + pub lhs: Vec, pub rhs: Term, } @@ -1453,11 +1453,12 @@ impl Heap { let mut i = 0; while i < serial.file.len() { let fnid = serial.file[i + 0]; + let name = Name(fnid); let size = serial.file[i + 1]; let buff = &serial.file[i + 2 .. i + 2 + size as usize]; let func = &bits::deserialized_func(&bit_vec::BitVec::from_bytes(&util::u128s_to_u8s(&buff))).unwrap(); - let func = compile_func(func, false).unwrap(); - self.write_file(Name(fnid), Arc::new(func)); + let func = compile_func(func, false, &name).unwrap(); + self.write_file(name, Arc::new(func)); i = i + 2 + size as usize; } // Deserializes Arits @@ -2034,7 +2035,7 @@ impl Runtime { if !self.check_func(&func) { return error(self, "fun", format!("Invalid function {}.", name)); } - let func = compile_func(func, true); + let func = compile_func(func, true, name); if func.is_none() { return error(self, "fun", format!("Invalid function {}.", name)); } @@ -2132,7 +2133,7 @@ impl Runtime { pub fn check_func(&self, func: &Func) -> bool { for rule in &func.rules { - if !self.check_term(&rule.lhs) || !self.check_term(&rule.rhs) { + if !rule.lhs.iter().all(|term| self.check_term(&term)) || !self.check_term(&rule.rhs) { return false; } } @@ -3054,7 +3055,7 @@ pub fn create_term(rt: &mut Runtime, term: &Term, loc: u128, vars_data: &mut Map } /// Given a Func (a vector of rules, lhs/rhs pairs), builds the CompFunc object -pub fn compile_func(func: &Func, debug: bool) -> Option { +pub fn compile_func(func: &Func, debug: bool, func_name: &Name) -> Option { let rules = &func.rules; // If there are no rules, return none @@ -3066,15 +3067,7 @@ pub fn compile_func(func: &Func, debug: bool) -> Option { } // Find the function arity - let arity; - if let Term::Fun { args, .. } = &rules[0].lhs { - arity = args.len() as u128; - } else { - if debug { - println!(" - failed to build function: left-hand side must be !(Fun ...)"); - } - return None; - } + let arity = rules[0].lhs.len() as u128; // The resulting vector let mut comp_rules = Vec::new(); @@ -3104,80 +3097,70 @@ pub fn compile_func(func: &Func, debug: bool) -> Option { let mut cond = Vec::new(); let mut vars = Vec::new(); let mut eras = Vec::new(); + let args = &rule.lhs; - // If the lhs is a Fun - if let Term::Fun { ref name, ref args } = rule.lhs { - - // If there is an arity mismatch, return None - if args.len() as u128 != arity { - if debug { - println!(" - failed to build function: arity mismatch on rule {}", rule_index); - } - return None; + // If there is an arity mismatch, return None + if args.len() as u128 != arity { + if debug { + println!(" - failed to build function: arity mismatch on rule {}", rule_index); } + return None; + } - // For each lhs argument - for i in 0 .. args.len() as u128 { - - match &args[i as usize] { - // If it is a constructor... - Term::Ctr { name: arg_name, args: arg_args } => { - strict[i as usize] = true; - cond.push(Ctr(**arg_name, 0)); // adds its matching condition - eras.push((i, arg_args.len() as u128)); // marks its index and arity for freeing - // For each of its fields... - for j in 0 .. arg_args.len() as u128 { - // If it is a variable... - if let Term::Var { name } = arg_args[j as usize] { - if !check_var(*name, &rule.rhs, &mut seen) { - if debug { - println!(" - failed to build function: non-linear variable '{}', on rule {}, argument {}:\n {} = {}", name, rule_index, i, view_term(&rule.lhs), view_term(&rule.rhs)); - } - return None; - } else { - vars.push(Var { name, param: i, field: Some(j), erase: name == Name::NONE }); // add its location - } - // Otherwise.. - } else { + // For each lhs argument + for i in 0 .. args.len() as u128 { + + match &args[i as usize] { + // If it is a constructor... + Term::Ctr { name: arg_name, args: arg_args } => { + strict[i as usize] = true; + cond.push(Ctr(**arg_name, 0)); // adds its matching condition + eras.push((i, arg_args.len() as u128)); // marks its index and arity for freeing + // For each of its fields... + for j in 0 .. arg_args.len() as u128 { + // If it is a variable... + if let Term::Var { name } = arg_args[j as usize] { + if !check_var(*name, &rule.rhs, &mut seen) { if debug { - println!(" - failed to build function: nested match on rule {}, argument {}:\n {} = {}", rule_index, i, view_term(&rule.lhs), view_term(&rule.rhs)); + println!(" - failed to build function: non-linear variable '{}', on rule {}, argument {}:\n {} = {}", name, rule_index, i, view_lhs(func_name, &rule.lhs), view_term(&rule.rhs)); } - return None; // return none, because we don't allow nested matches + return None; + } else { + vars.push(Var { name, param: i, field: Some(j), erase: name == Name::NONE }); // add its location } - } - } - // If it is a number... - Term::Num { numb: arg_numb } => { - strict[i as usize] = true; - cond.push(Num(**arg_numb)); // adds its matching condition - } - // If it is a variable... - Term::Var { name: arg_name } => { - if !check_var(**arg_name, &rule.rhs, &mut seen) { + // Otherwise.. + } else { if debug { - println!(" - failed to build function: non-linear variable '{}', on rule {}, argument {}:\n {} = {}", arg_name, rule_index, i, view_term(&rule.lhs), view_term(&rule.rhs)); + println!(" - failed to build function: nested match on rule {}, argument {}:\n {} = {}", rule_index, i, view_lhs(func_name, &rule.lhs), view_term(&rule.rhs)); } - return None; - } else { - vars.push(Var { name: *arg_name, param: i, field: None, erase: *arg_name == Name::NONE }); // add its location - cond.push(Var(0)); // it has no matching condition + return None; // return none, because we don't allow nested matches } } - _ => { + } + // If it is a number... + Term::Num { numb: arg_numb } => { + strict[i as usize] = true; + cond.push(Num(**arg_numb)); // adds its matching condition + } + // If it is a variable... + Term::Var { name: arg_name } => { + if !check_var(**arg_name, &rule.rhs, &mut seen) { if debug { - println!(" - failed to build function: unsupported match on rule {}, argument {}:\n {} = {}", rule_index, i, view_term(&rule.lhs), view_term(&rule.rhs)); + println!(" - failed to build function: non-linear variable '{}', on rule {}, argument {}:\n {} = {}", arg_name, rule_index, i, view_lhs(func_name, &rule.lhs), view_term(&rule.rhs)); } return None; + } else { + vars.push(Var { name: *arg_name, param: i, field: None, erase: *arg_name == Name::NONE }); // add its location + cond.push(Var(0)); // it has no matching condition } } + _ => { + if debug { + println!(" - failed to build function: unsupported match on rule {}, argument {}:\n {} = {}", rule_index, i, view_lhs(func_name, &rule.lhs), view_term(&rule.rhs)); + } + return None; + } } - - // If lhs isn't a Ctr, return None - } else { - if debug { - println!(" - failed to build function: left-hand side isn't a constructor, on rule {}:\n {} = {}", rule_index, view_term(&rule.lhs), view_term(&rule.rhs)); - } - return None; } // Creates the rhs body @@ -4526,7 +4509,7 @@ pub fn u128_to_name(num: u128) -> String { name.chars().rev().collect() } -pub fn read_until(code: &str, stop: char, read: fn(&str) -> ParseResult) -> ParseResult> { +pub fn read_until ParseResult>(code: &str, stop: char, read: F) -> ParseResult> { let mut elems = Vec::new(); let mut code = code; while code.len() > 0 && head(skip(code)) != stop { @@ -4701,32 +4684,35 @@ pub fn read_oper(in_code: &str) -> (&str, Option) { } } -pub fn read_rule(code: &str) -> ParseResult { - // TODO: custom parser for lhs - let (code, lhs) = read_term(code)?; - let (code, ()) = read_char(code, '=')?; - let (code, rhs) = read_term(code)?; - return Ok((code, Rule{lhs, rhs})); -} - -pub fn read_rules(code: &str) -> ParseResult> { - let (code, rules) = read_until(code, '\0', read_rule)?; +pub fn read_rules(func_name: Name, code: &str) -> ParseResult> { + let mut rules = Vec::new(); + let mut code = code; + while code.len() > 0 && head(skip(code)) != '}' { + let (new_code, lhs) = read_lhs(func_name, code)?; + let (new_code, ()) = read_char(new_code, '=')?; + let (new_code, rhs) = read_term(new_code)?; + code = new_code; + rules.push(Rule { lhs, rhs }); + } + code = tail(skip(code)); return Ok((code, rules)); } -pub fn read_func(code: &str) -> ParseResult { - let (code, rules) = read_until(code, '\0', read_rule)?; - let func = Func { rules }; - if let Some(func) = compile_func(&func, false) { - return Ok((code, func)); - } else { - return Err(ParseErr { - code: code.to_string(), - erro: "Couldn't parse function".to_string() - }); +pub fn read_lhs(func_name: Name, code: &str) -> ParseResult> { + let (code, ()) = read_char(code, '(')?; + let (code, name) = read_name(code)?; + if func_name != name { + return Err(ParseErr::new(code, format!("Function rule with different name in function {}", func_name))) } + let (code, args) = read_until(code, ')', read_term)?; + Ok((code, args)) } +// pub fn read_rules(code: &str) -> ParseResult> { +// let (code, rules) = read_until(code, '\0', read_rule)?; +// return Ok((code, rules)); +// } + pub fn read_sign(code: &str) -> ParseResult> { let code = skip(code); if let ('s','i','g','n') = (nth(code,0), nth(code,1), nth(code,2), nth(code,3)) { @@ -4755,7 +4741,7 @@ pub fn read_statement(code: &str) -> ParseResult { let (code, name) = read_name(code)?; let (code, args) = read_until(code, ')', read_name)?; let (code, unit) = read_char(code, '{')?; - let (code, ruls) = read_until(code, '}', read_rule)?; + let (code, ruls) = read_rules(name, code)?; let code = skip(code); let (code, init) = if let ('w','i','t','h') = (nth(code,0), nth(code,1), nth(code,2), nth(code,3)) { let code = drop(code,4); @@ -4945,6 +4931,11 @@ pub fn view_oper(oper: &Oper) -> String { }.to_string() } +pub fn view_lhs(name: &Name, args: &Vec) -> String { + let args = args.iter().map(view_term).collect::>(); + format!("({} {})", name, args.join(" ")) +} + pub fn view_statement(statement: &Statement) -> String { fn view_sign(sign: &Option) -> String { fn format_sign(sign: &crypto::Signature) -> String { @@ -4964,7 +4955,7 @@ pub fn view_statement(statement: &Statement) -> String { } match statement { Statement::Fun { name, args, func, init, sign } => { - let func = func.rules.iter().map(|x| format!("\n {} = {}", view_term(&x.lhs), view_term(&x.rhs))); + let func = func.rules.iter().map(|x| format!("\n {} = {}", view_lhs(&name, &x.lhs), view_term(&x.rhs))); let func = func.collect::>().join(""); let args = args.iter().map(|x| x.to_string()).collect::>().join(" "); let init = view_term(init); diff --git a/src/test/hvm.rs b/src/test/hvm.rs index f38468ba..b4aeafe5 100644 --- a/src/test/hvm.rs +++ b/src/test/hvm.rs @@ -1,14 +1,16 @@ use crate::{ bits::{deserialized_func, serialized_func}, hvm::{ - init_map, init_runtime, name_to_u128_unsafe, read_statements, readback_term, show_term, - u128_to_name, view_statements, view_term, Name, Rollback, Runtime, StatementInfo, Term, U120, + init_map, init_runtime, name_to_u128_unsafe, read_statements, + readback_term, show_term, u128_to_name, view_statements, view_term, Name, + Rollback, Runtime, StatementInfo, Term, U120, }, test::{ strategies::{func, heap, name, op2, statement, term}, util::{ - advance, rollback, rollback_path, rollback_simple, run_term_and, run_term_from_code_and, - temp_dir, test_heap_checksum, view_rollback_ticks, RuntimeStateTest, TempDir, + advance, rollback, rollback_path, rollback_simple, run_term_and, + run_term_from_code_and, temp_dir, test_heap_checksum, + view_rollback_ticks, RuntimeStateTest, TempDir, }, }, }; @@ -23,10 +25,20 @@ use rstest_reuse::{apply, template}; #[case(&["Count", "Store", "Sub", "Add"], PRE_COUNTER, COUNTER)] #[case(&["Bank", "Random", "AddAcc", "AddEq", "AddChild"], PRE_BANK, BANK)] #[case(&["End", "B0", "B1", "IncBit", "ToNum", "CountBit"], PRE_BIT_COUNTER, BIT_COUNTER)] -fn hvm_cases(#[case] fn_names: &[&str], #[case] pre_code: &str, #[case] code: &str) {} +fn hvm_cases( + #[case] fn_names: &[&str], + #[case] pre_code: &str, + #[case] code: &str, +) { +} #[apply(hvm_cases)] -pub fn simple_rollback(fn_names: &[&str], pre_code: &str, code: &str, temp_dir: TempDir) { +pub fn simple_rollback( + fn_names: &[&str], + pre_code: &str, + code: &str, + temp_dir: TempDir, +) { assert!(rollback_simple(pre_code, code, fn_names, 1000, 1, &temp_dir.path)); } @@ -77,11 +89,22 @@ pub fn advanced_rollback_run_fail( temp_dir: TempDir, ) { let path = [2, 1, 2, 1, 2, 1]; - assert!(rollback_path(PRE_COUNTER, COUNTER, &fn_names, &path, &temp_dir.path)); + assert!(rollback_path( + PRE_COUNTER, + COUNTER, + &fn_names, + &path, + &temp_dir.path + )); } #[apply(hvm_cases)] -pub fn stack_overflow(fn_names: &[&str], pre_code: &str, code: &str, temp_dir: TempDir) { +pub fn stack_overflow( + fn_names: &[&str], + pre_code: &str, + code: &str, + temp_dir: TempDir, +) { // caused by compute_at function let mut rt = init_runtime(Some(&temp_dir.path)); rt.run_statements_from_code(pre_code, true, true); @@ -144,7 +167,11 @@ fn one_hundred_snapshots(temp_dir: TempDir) { let mut rt = init_runtime(Some(&temp_dir.path)); for i in 0..100000 { rt.tick(); - println!(" - tick: {}, - rollback: {}", rt.get_tick(), view_rollback_ticks(&rt)); + println!( + " - tick: {}, - rollback: {}", + rt.get_tick(), + view_rollback_ticks(&rt) + ); } } @@ -191,18 +218,28 @@ fn dupped_state_test(temp_dir: TempDir) { expected_original_readback: &str, expected_other_readback: &str, ) { - let original_state = rt.read_disk(Name::try_from("Original").unwrap()).unwrap(); + let original_state = + rt.read_disk(Name::try_from("Original").unwrap()).unwrap(); let other_state = rt.read_disk(Name::try_from("Other").unwrap()).unwrap(); println!(); println!("original ptr: {}", original_state); println!("original: {}", show_term(&rt, original_state, None)); - println!("original readback: {}", view_term(&readback_term(&rt, original_state))); - assert_eq!(expected_original_readback, view_term(&readback_term(&rt, original_state))); + println!( + "original readback: {}", + view_term(&readback_term(&rt, original_state)) + ); + assert_eq!( + expected_original_readback, + view_term(&readback_term(&rt, original_state)) + ); println!(); println!("other ptr: {}", other_state); println!("other: {}", show_term(&rt, other_state, None)); println!("other readback: {}", view_term(&readback_term(&rt, other_state))); - assert_eq!(expected_other_readback, view_term(&readback_term(&rt, other_state))); + assert_eq!( + expected_other_readback, + view_term(&readback_term(&rt, other_state)) + ); println!(); } @@ -240,7 +277,11 @@ fn dupped_state_test(temp_dir: TempDir) { #[case("dup a ~ = @x dup ~ b = x; b; a", "@x0 x0")] #[case("dup a ~ = dup b ~ = @x (+ x #2); b; a", "@x0 (+ x0 #2)")] #[case("dup a ~ = dup b ~ = @x (+ #2 x); b; a", "@x0 (+ #2 x0)")] -fn readback(#[case] code: &str, #[case] expected_readback: &str, temp_dir: TempDir) { +fn readback( + #[case] code: &str, + #[case] expected_readback: &str, + temp_dir: TempDir, +) { // initialize runtime let mut rt = init_runtime(Some(&temp_dir.path)); // declare used constructors @@ -274,6 +315,7 @@ proptest! { #[test] fn parser(statements in vec(statement(), 0..10)) { + println!("{}\n=================", statements.iter().map(crate::hvm::view_statement).collect::>().join("\n")); let str = view_statements(&statements); let (.., s1) = read_statements(&str).unwrap(); assert_eq!(statements, s1); diff --git a/src/test/strategies.rs b/src/test/strategies.rs index 021f57e8..a190adb5 100644 --- a/src/test/strategies.rs +++ b/src/test/strategies.rs @@ -89,7 +89,7 @@ fn fun() -> impl Strategy { // generate rules pub fn rule() -> impl Strategy { - (fun(), term()).prop_map(|(lhs, rhs)| Rule { lhs, rhs }) + (vec(term(), 0..10), term()).prop_map(|(lhs, rhs)| Rule { lhs, rhs }) } pub fn func() -> impl Strategy {