diff --git a/src/compiler/codegen.rs b/src/compiler/codegen.rs index f860e612f..756b7fced 100644 --- a/src/compiler/codegen.rs +++ b/src/compiler/codegen.rs @@ -24,7 +24,7 @@ use crate::compiler::inline::{replace_in_inline, synthesize_args}; use crate::compiler::lambda::lambda_codegen; use crate::compiler::prims::{primapply, primcons, primquote}; use crate::compiler::runtypes::RunFailure; -use crate::compiler::sexp::{decode_string, enlist, SExp}; +use crate::compiler::sexp::{decode_string, SExp}; use crate::compiler::srcloc::Srcloc; use crate::compiler::StartOfCodegenOptimization; use crate::compiler::{BasicCompileContext, CompileContextWrapper}; @@ -65,7 +65,7 @@ fn cons_bodyform(loc: Srcloc, left: Rc, right: Rc) -> BodyFo left, right, ], - None + None, ) } @@ -396,27 +396,22 @@ fn generate_args_code( if list.is_empty() && tail.is_none() { Ok(Rc::new(SExp::Nil(l))) } else { - let mut compiled_args: Rc = - if let Some(t) = tail.as_ref() { - generate_expr_code(context, opts.clone(), compiler, t.clone())?.1 - } else { - Rc::new(SExp::Nil(l.clone())) - }; + let mut compiled_args: Rc = if let Some(t) = tail.as_ref() { + generate_expr_code(context, opts.clone(), compiler, t.clone())?.1 + } else { + Rc::new(SExp::Nil(l.clone())) + }; for hd in list.iter().rev() { - let generated = - generate_expr_code(context, opts.clone(), compiler, hd.clone())?.1; + let generated = generate_expr_code(context, opts.clone(), compiler, hd.clone())?.1; if with_primcons { - compiled_args = Rc::new(primcons( - generated.loc(), - generated.clone(), - compiled_args - )); + compiled_args = + Rc::new(primcons(generated.loc(), generated.clone(), compiled_args)); } else { compiled_args = Rc::new(SExp::Cons( generated.loc(), generated.clone(), - compiled_args + compiled_args, )); } } @@ -466,7 +461,7 @@ fn compile_call( opts: Rc, compiler: &PrimaryCodegen, list: &[Rc], - tail: Option> + tail: Option>, ) -> Result { let arg_string_list: Vec> = list .iter() @@ -495,28 +490,28 @@ fn compile_call( process_macro_call(context, opts.clone(), compiler, l, tl, Rc::new(code)) } - Callable::CallInline(l, inline) => { - replace_in_inline(context, opts.clone(), compiler, l.clone(), &inline, l, &tl, tail) - } + Callable::CallInline(l, inline) => replace_in_inline( + context, + opts.clone(), + compiler, + l.clone(), + &inline, + l, + &tl, + tail, + ), Callable::CallDefun(l, lookup) => { - generate_args_code(context, opts.clone(), compiler, l.clone(), &tl, tail, true).and_then( - |args| { - process_defun_call( - opts.clone(), - compiler, - l.clone(), - args, - Rc::new(lookup), - ) - }, - ) + generate_args_code(context, opts.clone(), compiler, l.clone(), &tl, tail, true) + .and_then(|args| { + process_defun_call(opts.clone(), compiler, l.clone(), args, Rc::new(lookup)) + }) } - Callable::CallPrim(l, p) => generate_args_code(context, opts, compiler, l.clone(), &tl, None, false) - .map(|args| { - CompiledCode(l.clone(), Rc::new(SExp::Cons(l, Rc::new(p), args))) - }), + Callable::CallPrim(l, p) => { + generate_args_code(context, opts, compiler, l.clone(), &tl, None, false) + .map(|args| CompiledCode(l.clone(), Rc::new(SExp::Cons(l, Rc::new(p), args)))) + } Callable::EnvPath => { if tl.len() == 1 { @@ -1114,7 +1109,7 @@ pub fn hoist_body_let_binding( "@*env*".as_bytes().to_vec(), ))), ], - None + None, ) }); @@ -1139,17 +1134,19 @@ pub fn hoist_body_let_binding( new_call_list.push(new_arg); vres.append(&mut new_helper.clone()); } - let new_tail = - if let Some(t) = tail.as_ref() { - let (new_helper, new_tail_elt) = - hoist_body_let_binding(outer_context.clone(), args.clone(), t.clone())?; - vres.append(&mut new_helper.clone()); - Some(new_tail_elt) - } else { - None - }; + let new_tail = if let Some(t) = tail.as_ref() { + let (new_helper, new_tail_elt) = + hoist_body_let_binding(outer_context.clone(), args.clone(), t.clone())?; + vres.append(&mut new_helper.clone()); + Some(new_tail_elt) + } else { + None + }; - Ok((vres, Rc::new(BodyForm::Call(l.clone(), new_call_list, new_tail)))) + Ok(( + vres, + Rc::new(BodyForm::Call(l.clone(), new_call_list, new_tail)), + )) } BodyForm::Lambda(letdata) => { let new_function_args = Rc::new(SExp::Cons( @@ -1416,8 +1413,7 @@ fn finalize_env_( } if let Some(res) = c.inlines.get(v) { - let (arg_list, arg_tail) = - synthesize_args(res.args.clone()); + let (arg_list, arg_tail) = synthesize_args(res.args.clone()); return replace_in_inline( context, opts.clone(), @@ -1426,7 +1422,7 @@ fn finalize_env_( res, res.args.loc(), &arg_list, - arg_tail + arg_tail, ) .map(|x| x.1); } diff --git a/src/compiler/evaluate.rs b/src/compiler/evaluate.rs index af7c5209c..292012776 100644 --- a/src/compiler/evaluate.rs +++ b/src/compiler/evaluate.rs @@ -300,7 +300,7 @@ pub fn make_operator1(l: &Srcloc, op: String, arg: Rc) -> BodyForm { Rc::new(BodyForm::Value(SExp::atom_from_string(l.clone(), &op))), arg, ], - None + None, ) } @@ -312,7 +312,7 @@ pub fn make_operator2(l: &Srcloc, op: String, arg1: Rc, arg2: Rc) -> Rc { name: &'a [u8], args: &'a [Rc], tail: Option>, - original: Rc + original: Rc, } impl<'info> Evaluator { @@ -996,7 +996,8 @@ impl<'info> Evaluator { prog_args, )))) } else if call.name == b"com" { - let use_body = self.make_com_module(&call.loc, prog_args, arguments_to_convert[0].to_sexp()); + let use_body = + self.make_com_module(&call.loc, prog_args, arguments_to_convert[0].to_sexp()); let compiled = self.compile_code(allocator, false, use_body)?; let compiled_borrowed: &SExp = compiled.borrow(); Ok(Rc::new(BodyForm::Quoted(compiled_borrowed.clone()))) @@ -1021,7 +1022,8 @@ impl<'info> Evaluator { all_primitive = false; } - converted_args = SExp::Cons(call.loc.clone(), shrunk.to_sexp(), Rc::new(converted_args)); + converted_args = + SExp::Cons(call.loc.clone(), shrunk.to_sexp(), Rc::new(converted_args)); } if all_primitive { @@ -1034,7 +1036,11 @@ impl<'info> Evaluator { Ok(res) => Ok(res), Err(e) => { if only_inline || self.ignore_exn { - Ok(Rc::new(BodyForm::Call(call.loc.clone(), target_vec.clone(), None))) + Ok(Rc::new(BodyForm::Call( + call.loc.clone(), + target_vec.clone(), + None, + ))) } else { Err(e) } @@ -1057,7 +1063,8 @@ impl<'info> Evaluator { only_inline, ) } else { - let reformed = BodyForm::Call(call.loc.clone(), target_vec.clone(), call.tail.clone()); + let reformed = + BodyForm::Call(call.loc.clone(), target_vec.clone(), call.tail.clone()); self.chase_apply(allocator, &mut visited, Rc::new(reformed)) } } else { @@ -1132,7 +1139,7 @@ impl<'info> Evaluator { Rc::new(BodyForm::Call( iftrue.loc(), vec![apply_head.clone(), iftrue.clone(), env.clone()], - None + None, )), ); @@ -1142,7 +1149,7 @@ impl<'info> Evaluator { Rc::new(BodyForm::Call( iffalse.loc(), vec![apply_head, iffalse.clone(), env], - None + None, )), ); @@ -1158,7 +1165,7 @@ impl<'info> Evaluator { flatten_expression_to_names(surrogate_apply_true?.to_sexp()), flatten_expression_to_names(surrogate_apply_false?.to_sexp()), ], - None + None, )); return Ok(res); @@ -1231,7 +1238,10 @@ impl<'info> Evaluator { match helper { Some(HelperForm::Defmacro(mac)) => { if call.tail.is_some() { - todo!(); + return Err(CompileErr( + call.loc.clone(), + "Macros cannot use runtime rest arguments".to_string(), + )); } self.invoke_macro_expansion( allocator, @@ -1261,11 +1271,19 @@ impl<'info> Evaluator { only_inline, )?); } - return Ok(Rc::new(BodyForm::Call(call.loc.clone(), call_vec, call.tail.clone()))); + return Ok(Rc::new(BodyForm::Call( + call.loc.clone(), + call_vec, + call.tail.clone(), + ))); } - let argument_captures_untranslated = - build_argument_captures(&call.loc, arguments_to_convert, call.tail.clone(), defun.args.clone())?; + let argument_captures_untranslated = build_argument_captures( + &call.loc, + arguments_to_convert, + call.tail.clone(), + defun.args.clone(), + )?; let mut argument_captures = HashMap::new(); // Do this to protect against misalignment @@ -1503,7 +1521,7 @@ impl<'info> Evaluator { parts.iter().skip(1).cloned().collect(); match head_expr.borrow() { - BodyForm::Value(SExp::Atom(call_loc, call_name)) => self.handle_invoke( + BodyForm::Value(SExp::Atom(_call_loc, call_name)) => self.handle_invoke( allocator, &mut visited, &CallSpec { @@ -1518,7 +1536,7 @@ impl<'info> Evaluator { env, only_inline, ), - BodyForm::Value(SExp::Integer(call_loc, call_int)) => self.handle_invoke( + BodyForm::Value(SExp::Integer(_call_loc, call_int)) => self.handle_invoke( allocator, &mut visited, &CallSpec { @@ -1526,7 +1544,7 @@ impl<'info> Evaluator { name: &u8_from_number(call_int.clone()), args: parts, original: body.clone(), - tail: None + tail: None, }, prog_args, &arguments_to_convert, diff --git a/src/compiler/frontend.rs b/src/compiler/frontend.rs index ddb9cc47e..274832595 100644 --- a/src/compiler/frontend.rs +++ b/src/compiler/frontend.rs @@ -211,12 +211,18 @@ fn args_to_expression_list( } else { match body.borrow() { SExp::Cons(_l, first, rest) => { - if let SExp::Atom(fl, fname) = first.borrow() { + if let SExp::Atom(_fl, fname) = first.borrow() { if fname == b"&rest" { // Rest is a list containing one item that becomes the // tail. - let (mut args, no_tail) = - args_to_expression_list(opts, rest.clone())?; + let (args, no_tail) = args_to_expression_list(opts, rest.clone())?; + + if no_tail.is_some() { + return Err(CompileErr( + rest.loc(), + format!("only one use of &rest is allowed"), + )); + } if args.len() != 1 { return Err(CompileErr( @@ -232,15 +238,11 @@ fn args_to_expression_list( let mut result_list = Vec::new(); let f_compiled = compile_bodyform(opts.clone(), first.clone())?; result_list.push(Rc::new(f_compiled)); - let (mut args, mut tail) = - args_to_expression_list(opts, rest.clone())?; + let (mut args, tail) = args_to_expression_list(opts, rest.clone())?; result_list.append(&mut args); Ok((result_list, tail)) } - _ => Err(CompileErr( - body.loc(), - format!("Bad arg list tail {body}"), - )), + _ => Err(CompileErr(body.loc(), format!("Bad arg list tail {body}"))), } } } @@ -1054,7 +1056,7 @@ pub fn generate_type_helpers(ty: &ChiaType) -> Vec { ))), Rc::new(BodyForm::Value(SExp::Atom(m.loc.clone(), vec![b'S']))), ], - None + None, )), synthetic: Some(SyntheticType::NoInlinePreference), ty: Some(funty), diff --git a/src/compiler/inline.rs b/src/compiler/inline.rs index fb1318143..3f521fc32 100644 --- a/src/compiler/inline.rs +++ b/src/compiler/inline.rs @@ -66,15 +66,22 @@ pub fn synthesize_args(arg_: Rc) -> (Vec>, Option], tail: Option>) -> Rc { - let mut result_body = - tail.unwrap_or_else(|| Rc::new(BodyForm::Value(SExp::Nil(loc.clone())))); +fn enlist_remaining_args( + loc: Srcloc, + arg_choice: usize, + args: &[Rc], + tail: Option>, +) -> Rc { + let mut result_body = tail.unwrap_or_else(|| Rc::new(BodyForm::Value(SExp::Nil(loc.clone())))); - for (i, arg) in args.iter().enumerate().skip(arg_choice).rev() { + for arg in args.iter().skip(arg_choice).rev() { result_body = Rc::new(BodyForm::Call( loc.clone(), vec![ - Rc::new(BodyForm::Value(SExp::Integer(loc.clone(), 4_u32.to_bigint().unwrap()))), + Rc::new(BodyForm::Value(SExp::Integer( + loc.clone(), + 4_u32.to_bigint().unwrap(), + ))), arg.clone(), result_body, ], @@ -135,23 +142,27 @@ fn choose_arg_from_list_or_tail( callsite: &Srcloc, args: &[Rc], tail: Option>, - index: usize + index: usize, ) -> Result, CompileErr> { let two = 2_i32.to_bigint().unwrap(); if index >= args.len() { if let Some(t) = tail { let target_shift = index - args.len(); - let target_path = (two.clone() << target_shift) | (((two << target_shift) - bi_one()) >> 2); + let target_path = + (two.clone() << target_shift) | (((two << target_shift) - bi_one()) >> 2); return Ok(Rc::new(BodyForm::Call( callsite.clone(), vec![ - Rc::new(BodyForm::Value(SExp::Integer(t.loc(), 2_u32.to_bigint().unwrap()))), + Rc::new(BodyForm::Value(SExp::Integer( + t.loc(), + 2_u32.to_bigint().unwrap(), + ))), Rc::new(BodyForm::Value(SExp::Integer(t.loc(), target_path))), - t.clone() + t.clone(), ], // Applying a primitive. - None + None, ))); } @@ -200,15 +211,14 @@ fn arg_lookup( vec![ Rc::new(BodyForm::Value(SExp::Integer(t.loc(), two.clone()))), Rc::new(BodyForm::Value(SExp::Integer(t.loc(), tail_path))), - t.clone() + t.clone(), ], - None + None, )) }); } - let tail_list = - enlist_remaining_args(match_args.loc(), arg_choice, args, tail); + let tail_list = enlist_remaining_args(match_args.loc(), arg_choice, args, tail); return Ok(pick_value_from_arg_element( match_args.clone(), tail_list, @@ -241,7 +251,7 @@ fn make_args_for_call_from_inline( tail: Option>, callsite: Srcloc, call_args: &[Rc], - call_tail: Option> + call_tail: Option>, ) -> Result<(Vec>, Option>), CompileErr> { if call_args.len() == 0 { // This is a nil. @@ -273,23 +283,22 @@ fn make_args_for_call_from_inline( } let mut new_visited = visited_inlines.clone(); - let replaced_tail = - if let Some(t) = call_tail { - Some(replace_inline_body( - &mut new_visited, - runner, - opts, - compiler, - t.loc(), - inline, - args, - tail, - callsite.clone(), - t.clone(), - )?) - } else { - None - }; + let replaced_tail = if let Some(t) = call_tail { + Some(replace_inline_body( + &mut new_visited, + runner, + opts, + compiler, + t.loc(), + inline, + args, + tail, + callsite.clone(), + t.clone(), + )?) + } else { + None + }; Ok((new_args, replaced_tail)) } @@ -335,19 +344,18 @@ fn replace_inline_body( // Recursion only happens when the same stack encounters an inline // twice. // - let (new_args, replaced_tail) = - make_args_for_call_from_inline( - visited_inlines, - runner.clone(), - opts.clone(), - compiler, - inline, - args, - tail.clone(), - callsite.clone(), - &call_args, - call_tail.clone() - )?; + let (new_args, replaced_tail) = make_args_for_call_from_inline( + visited_inlines, + runner.clone(), + opts.clone(), + compiler, + inline, + args, + tail.clone(), + callsite.clone(), + &call_args, + call_tail.clone(), + )?; // If the called function is an inline, we'll expand it here. // This is so we can preserve the context of argument expressions @@ -473,7 +481,7 @@ pub fn replace_in_inline( inline: &InlineFunction, callsite: Srcloc, args: &[Rc], - tail: Option> + tail: Option>, ) -> Result { let mut visited = HashSet::new(); visited.insert(inline.name.clone()); diff --git a/src/compiler/optimize/bodyform.rs b/src/compiler/optimize/bodyform.rs index e6f051e6f..2d8484f16 100644 --- a/src/compiler/optimize/bodyform.rs +++ b/src/compiler/optimize/bodyform.rs @@ -250,19 +250,17 @@ where } match bf { - BodyForm::Call(l, args, tail) => { - replace_in_bodyform_inner_list( - current_path, - replacements, - args, - tail, - &BodyformPathArc::CallArgument, - &|e: &Rc| e.borrow(), - &|_w, b| Rc::new(b), - &|args, tail| BodyForm::Call(l.clone(), args, tail), - f, - ) - } + BodyForm::Call(l, args, tail) => replace_in_bodyform_inner_list( + current_path, + replacements, + args, + tail, + &BodyformPathArc::CallArgument, + &|e: &Rc| e.borrow(), + &|_w, b| Rc::new(b), + &|args, tail| BodyForm::Call(l.clone(), args, tail), + f, + ), BodyForm::Let(k, b) => { let path_idx = current_path.len(); current_path.push(BodyformPathArc::BodyOf); diff --git a/src/compiler/optimize/cse.rs b/src/compiler/optimize/cse.rs index 816160e7b..9ca9536ce 100644 --- a/src/compiler/optimize/cse.rs +++ b/src/compiler/optimize/cse.rs @@ -226,7 +226,10 @@ pub fn is_canonical_apply_parent( } else { return Err(CompileErr( root.loc(), - format!("Impossible: could not retrieve parent of existing expression (root {})", root.to_sexp()), + format!( + "Impossible: could not retrieve parent of existing expression (root {})", + root.to_sexp() + ), )); }; diff --git a/src/compiler/optimize/mod.rs b/src/compiler/optimize/mod.rs index 963d69e13..62622d5de 100644 --- a/src/compiler/optimize/mod.rs +++ b/src/compiler/optimize/mod.rs @@ -267,7 +267,7 @@ fn constant_fun_result( loc: &Srcloc, an: &[u8], forms: &[Rc], - tail: Option> + tail: Option>, ) -> Option> { if let Some(res) = opts.dialect().stepping { if res >= 23 { @@ -317,7 +317,7 @@ fn constant_fun_result( ], // Proper call: we're calling 'a' on behalf of our // single capture argument. - None + None, )), }; let optimizer = if let Ok(res) = get_optimizer(loc, opts.clone()) { @@ -338,16 +338,15 @@ fn constant_fun_result( }; // Reified args reflect the actual ABI shape with a tail if any. - let mut reified_args = - if let Some((_, t)) = optimized_tail { - if let Ok(res) = dequote(loc.clone(), t.clone()) { - res - } else { - return None; - } + let mut reified_args = if let Some((_, t)) = optimized_tail { + if let Ok(res) = dequote(loc.clone(), t.clone()) { + res } else { - Rc::new(SExp::Nil(loc.clone())) - }; + return None; + } + } else { + Rc::new(SExp::Nil(loc.clone())) + }; for (_, v) in optimized_args.iter().rev() { let unquoted = if let Ok(res) = dequote(loc.clone(), v.clone()) { res @@ -366,7 +365,7 @@ fn constant_fun_result( ], // The constructed call is proper because we're feeding something // we constructed above. - None + None, ); return Some(Rc::new(new_body)); diff --git a/src/compiler/rename.rs b/src/compiler/rename.rs index a0c647d28..423c8b320 100644 --- a/src/compiler/rename.rs +++ b/src/compiler/rename.rs @@ -243,9 +243,9 @@ fn rename_in_bodyform(namemap: &HashMap, Vec>, b: Rc) -> B } }) .collect(); - let new_tail = tail.as_ref().map(|t| { - Rc::new(rename_in_bodyform(namemap, t.clone())) - }); + let new_tail = tail + .as_ref() + .map(|t| Rc::new(rename_in_bodyform(namemap, t.clone()))); BodyForm::Call(l.clone(), new_vs, new_tail) } @@ -362,9 +362,9 @@ fn rename_args_bodyform(b: &BodyForm) -> BodyForm { .iter() .map(|a| Rc::new(rename_args_bodyform(a))) .collect(); - let new_tail = tail.as_ref().map(|t| { - Rc::new(rename_args_bodyform(t.borrow())) - }); + let new_tail = tail + .as_ref() + .map(|t| Rc::new(rename_args_bodyform(t.borrow()))); BodyForm::Call(l.clone(), new_vs, new_tail) } BodyForm::Mod(l, program) => BodyForm::Mod(l.clone(), program.clone()), diff --git a/src/compiler/typechia.rs b/src/compiler/typechia.rs index eaa5b0147..5c15017a0 100644 --- a/src/compiler/typechia.rs +++ b/src/compiler/typechia.rs @@ -530,7 +530,7 @@ fn handle_macro( form: Rc, loc: Srcloc, provided_args: &[Rc], - tail: Option> + tail: Option>, ) -> Result { // It is a macro, we need to interpret it in our way: // We'll compile and run the code itself on a @@ -658,9 +658,12 @@ fn chialisp_to_expr( chialisp_to_expr(opts, program, form_args, beta_reduced) } BodyForm::Call(l, lst, tail) => { - let mut arg_expr = tail.as_ref().map(|t| { - todo!(); - }).unwrap_or_else(|| Expr::EUnit(l.clone())); + let mut arg_expr = if let Some(t) = tail.as_ref() { + chialisp_to_expr(opts.clone(), program, form_args.clone(), t.clone())? + } else { + Expr::EUnit(l.clone()) + }; + for (_i, e) in lst.iter().enumerate().rev().take(lst.len() - 1) { let new_expr = chialisp_to_expr(opts.clone(), program, form_args.clone(), e.clone())?; diff --git a/src/tests/compiler/optimizer/bodyform.rs b/src/tests/compiler/optimizer/bodyform.rs index b0c41689c..bb09b500d 100644 --- a/src/tests/compiler/optimizer/bodyform.rs +++ b/src/tests/compiler/optimizer/bodyform.rs @@ -184,18 +184,14 @@ fn test_replace_in_bodyform() { fn make_test_case_for_visitor(program: &str) -> CompileForm { let progfile = "*test*"; let srcloc = Srcloc::start(progfile); - let parsed = parse_sexp( - srcloc.clone(), - program.bytes() - ).expect("should parse"); + let parsed = parse_sexp(srcloc.clone(), program.bytes()).expect("should parse"); let opts: Rc = Rc::new(DefaultCompilerOpts::new(progfile)); frontend(opts.clone(), &parsed).expect("should fe") } #[test] fn test_visitor_0() { - let compiled = make_test_case_for_visitor( - indoc! {" + let compiled = make_test_case_for_visitor(indoc! {" (mod () (call1 (let ((A 99) (B test)) (+ A B)) @@ -245,7 +241,7 @@ fn test_visitor_0() { }, compiled.exp.borrow(), ) - .unwrap(); + .unwrap(); assert_eq!(res.len(), 2); assert_eq!(res[0].subexp.to_sexp().to_string(), "99"); assert_eq!( @@ -268,7 +264,7 @@ fn test_visitor_0() { #[test] fn test_visitor_rest_alone() { - let compiled = make_test_case_for_visitor(indoc!{" + let compiled = make_test_case_for_visitor(indoc! {" (mod (X) (F &rest X) ) @@ -284,14 +280,14 @@ fn test_visitor_rest_alone() { }, compiled.exp.borrow(), ) - .unwrap(); + .unwrap(); assert_eq!(res.len(), 1); assert_eq!(res[0].path, vec![BodyformPathArc::CallArgument(1)]); } #[test] fn test_visitor_rest_with_list() { - let compiled = make_test_case_for_visitor(indoc!{" + let compiled = make_test_case_for_visitor(indoc! {" (mod (X) (F X &rest (F &rest X)) ) @@ -307,8 +303,14 @@ fn test_visitor_rest_with_list() { }, compiled.exp.borrow(), ) - .unwrap(); + .unwrap(); assert_eq!(res.len(), 2); assert_eq!(res[0].path, vec![BodyformPathArc::CallArgument(1)]); - assert_eq!(res[1].path, vec![BodyformPathArc::CallArgument(2), BodyformPathArc::CallArgument(1)]); + assert_eq!( + res[1].path, + vec![ + BodyformPathArc::CallArgument(2), + BodyformPathArc::CallArgument(1) + ] + ); } diff --git a/src/tests/compiler/restargs.rs b/src/tests/compiler/restargs.rs index 76cfffd71..abf909a14 100644 --- a/src/tests/compiler/restargs.rs +++ b/src/tests/compiler/restargs.rs @@ -1,5 +1,6 @@ use crate::tests::compiler::compiler::run_string; use crate::tests::compiler::repl::test_repl_outcome; +use crate::tests::compiler::types::chialisp::test_chialisp_program_typecheck; // All tests needed for rest calls: // @@ -57,7 +58,8 @@ fn test_simple_inline_toomany_args_01() { (defun-inline F (A B) (+ A B)) (F X Y Z) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7 9)".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "12"); } @@ -78,7 +80,8 @@ fn test_simple_inline_toomany_args_improper_tail_02() { (defun-inline F (A B . C) (* A B (sum C))) (F X Y 99 101 &rest Z) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7 (301 313))".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "28490"); } @@ -99,7 +102,8 @@ fn test_simple_inline_toomany_args_improper_no_tail_03() { (defun-inline F (A B . C) (* A B (sum C))) (F X Y 99 101) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7)".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "7000"); } @@ -113,7 +117,8 @@ fn test_simple_inline_exact_no_tails_04() { (defun-inline F (A B) (* A B)) (F X Y) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7)".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "35"); } @@ -127,7 +132,8 @@ fn test_simple_inline_exact_improper_tail_05() { (defun-inline F (A B . C) (* A B C)) (F X Y &rest Z) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7 9)".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "315"); } @@ -141,7 +147,8 @@ fn test_simple_inline_exact_improper_no_tail_06() { (defun-inline F (A B . C) (+ A B C)) (F X Y) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7)".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "12"); } @@ -155,7 +162,8 @@ fn test_simple_inline_exact_toofew_improper_tail_07() { (defun-inline F (A B C . D) (list A B C (f D))) (F X Y &rest Z) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7 (101 103))".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "(5 7 101 103)"); } @@ -169,7 +177,8 @@ fn test_simple_inline_exact_toofew_tail_08() { (defun-inline F (A B C) (list A B C)) (F X Y &rest Z) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7 (101 103))".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "(5 7 101)"); } @@ -183,7 +192,8 @@ fn test_simple_inline_exact_toofew_improper_no_tail_09() { (defun-inline F (A B C . D) (list A B C (f D))) (F X Y) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7)".to_string()); if let Err(e) = res { assert!(e.1.contains("Lookup for argument 3")); @@ -201,7 +211,8 @@ fn test_simple_inline_exact_toofew_tail_10() { (defun-inline F (A B C) (list A B C)) (F X Y &rest Z) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7 (101 103))".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "(5 7 101)"); } @@ -217,7 +228,8 @@ fn test_inline_inline_toomany_args_11() { (defun-inline G (X Y Z) (F X Y Z)) (G X Y Z) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7 9)".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "12"); } @@ -233,7 +245,8 @@ fn test_inline_inline_toomany_args_improper_tail_12() { (defun-inline F (A B . C) (list A B (return-list C))) (F X Y 99 101 &rest Z) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7 (301 313))".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "(5 7 (c e 301 313))"); } @@ -249,7 +262,8 @@ fn test_simple_inline_toomany_args_improper_no_tail_13() { (defun-inline F (A B . C) (list A B (return-list C))) (F X Y 99 101) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7)".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "(5 7 (c e))"); } @@ -265,7 +279,8 @@ fn test_inline_inline_exact_no_tails_14() { (defun-inline G (A B) (F A B)) (G X Y) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7)".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "35"); } @@ -281,7 +296,8 @@ fn test_inline_inline_exact_improper_tail_15() { (defun-inline G (X Y Z) (F X Y &rest Z)) (G X Y Z) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7 9)".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "315"); } @@ -297,7 +313,8 @@ fn test_inline_inline_exact_improper_no_tail_16() { (defun-inline G (X Y) (F X Y)) (G X Y) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7)".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "12"); } @@ -313,7 +330,8 @@ fn test_simple_inline_exact_toofew_improper_tail_17() { (defun-inline G (X Y Z) (F X Y &rest Z)) (G X Y Z) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7 (101 103))".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "(5 7 101 103)"); } @@ -329,7 +347,8 @@ fn test_inline_inline_exact_toofew_tail_18() { (defun-inline G (X Y Z) (F X Y &rest Z)) (G X Y Z) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7 (101 103))".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "(5 7 101)"); } @@ -345,7 +364,8 @@ fn test_inline_inline_exact_toofew_improper_no_tail_19() { (defun-inline G (X Y) (F X Y)) (G X Y) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7)".to_string()); if let Err(e) = res { assert!(e.1.contains("Lookup for argument 3")); @@ -365,7 +385,8 @@ fn test_simple_inline_exact_toofew_tail_20() { (defun-inline G (X Y Z) (F X Y &rest Z)) (G X Y Z) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7 (101 103))".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "(5 7 101)"); } @@ -381,7 +402,8 @@ fn test_ni_ni_toomany_args_21() { (defun G (X Y Z) (F X Y Z)) (G X Y Z) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7 9)".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "12"); } @@ -397,7 +419,8 @@ fn test_ni_ni_toomany_args_improper_tail_22() { (defun F (A B . C) (list A B (return-list C))) (F X Y 99 101 &rest Z) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7 (301 313))".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "(5 7 (c e 301 313))"); } @@ -413,7 +436,8 @@ fn test_simple_inline_toomany_args_improper_no_tail_23() { (defun F (A B . C) (list A B (return-list C))) (F X Y 99 101) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7)".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "(5 7 (c e))"); } @@ -429,7 +453,8 @@ fn test_ni_ni_exact_no_tails_24() { (defun G (A B) (F A B)) (G X Y) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7)".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "35"); } @@ -445,7 +470,8 @@ fn test_ni_ni_exact_improper_tail_25() { (defun G (X Y Z) (F X Y &rest Z)) (G X Y Z) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7 9)".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "315"); } @@ -481,7 +507,8 @@ fn test_ni_ni_exact_improper_no_tail_26() { (defun G (X Y) (F X Y)) (G X Y) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7)".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "12"); } @@ -497,7 +524,8 @@ fn test_simple_inline_exact_toofew_improper_tail_27() { (defun G (X Y Z) (F X Y &rest Z)) (G X Y Z) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7 (101 103))".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "(5 7 101 103)"); } @@ -513,7 +541,8 @@ fn test_ni_ni_exact_toofew_tail_28() { (defun G (X Y Z) (F X Y &rest Z)) (G X Y Z) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7 (101 103))".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "(5 7 101)"); } @@ -529,7 +558,8 @@ fn test_ni_ni_exact_toofew_improper_no_tail_29() { (defun G (X Y) (F X Y)) (G X Y) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7)".to_string()); if let Err(e) = res { assert!(e.1.contains("bad path")); @@ -549,7 +579,8 @@ fn test_ni_ni_exact_toofew_tail_30() { (defun G (X Y Z) (F X Y &rest Z)) (G X Y Z) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7 (101 103))".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "(5 7 101)"); } @@ -565,7 +596,8 @@ fn test_inline_ni_toomany_args_31() { (defun-inline G (X Y Z) (F X Y Z)) (G X Y Z) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7 9)".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "12"); } @@ -581,7 +613,8 @@ fn test_inline_ni_toomany_args_improper_tail_32() { (defun F (A B . C) (list A B (return-list C))) (F X Y 99 101 &rest Z) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7 (301 313))".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "(5 7 (c e 301 313))"); } @@ -597,7 +630,8 @@ fn test_simple_inline_toomany_args_improper_no_tail_33() { (defun F (A B . C) (list A B (return-list C))) (F X Y 99 101) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7)".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "(5 7 (c e))"); } @@ -613,7 +647,8 @@ fn test_inline_ni_exact_no_tails_34() { (defun-inline G (A B) (F A B)) (G X Y) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7)".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "35"); } @@ -629,7 +664,8 @@ fn test_inline_ni_exact_improper_tail_35() { (defun-inline G (X Y Z) (F X Y &rest Z)) (G X Y Z) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7 9)".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "315"); } @@ -656,7 +692,6 @@ fn test_simple_rest_call_inline_35() { assert_eq!(res.to_string(), "768"); } - #[test] fn test_inline_ni_exact_improper_no_tail_36() { let prog = indoc! {" @@ -668,7 +703,8 @@ fn test_inline_ni_exact_improper_no_tail_36() { (defun-inline G (X Y) (F X Y)) (G X Y) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7)".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "12"); } @@ -684,7 +720,8 @@ fn test_simple_inline_exact_toofew_improper_tail_37() { (defun-inline G (X Y Z) (F X Y &rest Z)) (G X Y Z) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7 (101 103))".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "(5 7 101 103)"); } @@ -700,7 +737,8 @@ fn test_inline_ni_exact_toofew_tail_38() { (defun-inline G (X Y Z) (F X Y &rest Z)) (G X Y Z) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7 (101 103))".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "(5 7 101)"); } @@ -716,7 +754,8 @@ fn test_inline_ni_exact_toofew_improper_no_tail_39() { (defun-inline G (X Y) (F X Y)) (G X Y) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7)".to_string()); if let Err(e) = res { assert!(e.1.contains("bad path")); @@ -736,7 +775,8 @@ fn test_inline_ni_exact_toofew_tail_40() { (defun-inline G (X Y Z) (F X Y &rest Z)) (G X Y Z) - )"}.to_string(); + )"} + .to_string(); let res = run_string(&prog, &"(5 7 (101 103))".to_string()).expect("should compile and run"); assert_eq!(res.to_string(), "(5 7 101)"); } @@ -744,15 +784,14 @@ fn test_inline_ni_exact_toofew_tail_40() { #[test] fn test_repl_01() { assert_eq!( - test_repl_outcome(vec![ - "(defun-inline F (A B) (+ A B))", - "(F 5 7 9)" - ]).unwrap().unwrap(), + test_repl_outcome(vec!["(defun-inline F (A B) (+ A B))", "(F 5 7 9)"]) + .unwrap() + .unwrap(), "(q . 12)" ); } -const repl_test_sum: &'static str = indoc!{" +const REPL_TEST_SUM: &'static str = indoc! {" (defun sum (Xs) (if Xs (+ (f Xs) (sum (r Xs))) @@ -764,10 +803,12 @@ const repl_test_sum: &'static str = indoc!{" fn test_repl_02() { assert_eq!( test_repl_outcome(vec![ - repl_test_sum, + REPL_TEST_SUM, "(defun-inline F (A B . C) (* A B (sum C)))", "(F 5 7 99 101 &rest (list 301 313))" - ]).unwrap().unwrap(), + ]) + .unwrap() + .unwrap(), "(q . 28490)" ); } @@ -776,10 +817,12 @@ fn test_repl_02() { fn test_repl_03() { assert_eq!( test_repl_outcome(vec![ - repl_test_sum, + REPL_TEST_SUM, "(defun-inline F (A B . C) (* A B (sum C)))", "(F 5 7 99 101)" - ]).unwrap().unwrap(), + ]) + .unwrap() + .unwrap(), "(q . 7000)" ); } @@ -787,10 +830,9 @@ fn test_repl_03() { #[test] fn test_repl_04() { assert_eq!( - test_repl_outcome(vec![ - "(defun F (A B) (* A B))", - "(F 5 7)" - ]).unwrap().unwrap(), + test_repl_outcome(vec!["(defun F (A B) (* A B))", "(F 5 7)"]) + .unwrap() + .unwrap(), "(q . 35)" ); } @@ -798,10 +840,9 @@ fn test_repl_04() { #[test] fn test_repl_05() { assert_eq!( - test_repl_outcome(vec![ - "(defun F (A B . C) (* A B C))", - "(F 5 7 &rest 9)" - ]).unwrap().unwrap(), + test_repl_outcome(vec!["(defun F (A B . C) (* A B C))", "(F 5 7 &rest 9)"]) + .unwrap() + .unwrap(), "(q . 315)" ); } @@ -809,10 +850,9 @@ fn test_repl_05() { #[test] fn test_repl_06() { assert_eq!( - test_repl_outcome(vec![ - "(defun F (A B . C) (+ A B C))", - "(F 5 7)" - ]).unwrap().unwrap(), + test_repl_outcome(vec!["(defun F (A B . C) (+ A B C))", "(F 5 7)"]) + .unwrap() + .unwrap(), "(q . 12)" ); } @@ -823,7 +863,9 @@ fn test_repl_07() { test_repl_outcome(vec![ "(defun F (A B C . D) (list A B C (f D)))", "(F 5 7 &rest (list 101 103))" - ]).unwrap().unwrap(), + ]) + .unwrap() + .unwrap(), "(q 5 7 101 103)" ); } @@ -834,7 +876,9 @@ fn test_repl_08() { test_repl_outcome(vec![ "(defun F (A B C) (list A B C))", "(F 5 7 &rest (list 101 103))" - ]).unwrap().unwrap(), + ]) + .unwrap() + .unwrap(), "(q 5 7 101)" ); } @@ -842,10 +886,7 @@ fn test_repl_08() { #[test] fn test_repl_09() { assert!( - test_repl_outcome(vec![ - "(defun F (A B C . D) (list A B C (f D)))", - "(F 5 7)" - ]).is_err() + test_repl_outcome(vec!["(defun F (A B C . D) (list A B C (f D)))", "(F 5 7)"]).is_err() ); } @@ -855,7 +896,48 @@ fn test_repl_10() { test_repl_outcome(vec![ "(defun F (A B C) (list A B C))", "(F 5 7 &rest (list 101 103))" - ]).unwrap().unwrap(), + ]) + .unwrap() + .unwrap(), "(q 5 7 101)" ); } + +#[test] +fn test_rest_type_good() { + let prog_good = indoc! {" +(mod (X) + (include *standard-cl-23*) + + (defun F (A B . C) : ((Pair Atom (Pair Atom (List Atom))) -> Atom) (* A B (f C))) + + (F 5 7 &rest (list 9)) + )"} + .to_string(); + + let typecheck_result = test_chialisp_program_typecheck(&prog_good, true); + + // Good result + assert!(typecheck_result.is_ok()); +} + +#[test] +fn test_rest_type_bad() { + let prog_bad = indoc! {" +(mod (X) + (include *standard-cl-23*) + + (deftype Foo ()) + + (defun F (A B . C) : ((Pair Atom (Pair Atom (List Foo))) -> Atom) (* A B (f C))) + + (F 5 7 &rest (list 9)) + )"} + .to_string(); + + let typecheck_result = test_chialisp_program_typecheck(&prog_bad, true); + + // Bad result + eprintln!("tc {typecheck_result:?}"); + assert!(typecheck_result.is_err()); +} diff --git a/src/tests/compiler/types/chialisp.rs b/src/tests/compiler/types/chialisp.rs index 45bfec965..ae6dcc36a 100644 --- a/src/tests/compiler/types/chialisp.rs +++ b/src/tests/compiler/types/chialisp.rs @@ -118,7 +118,7 @@ fn test_chialisp_context_from_args_and_type_single_arg_with_pair_type() { check_expression_against_type_with_context(&context, "X", "Atom", false); } -fn test_chialisp_program_typecheck(s: &str, flatten: bool) -> Result { +pub fn test_chialisp_program_typecheck(s: &str, flatten: bool) -> Result { let testname = "*test*".to_string(); let loc = Srcloc::start(&testname); let context = standard_type_context(); diff --git a/src/tests/compiler/types/mod.rs b/src/tests/compiler/types/mod.rs index f04ca8984..db004be64 100644 --- a/src/tests/compiler/types/mod.rs +++ b/src/tests/compiler/types/mod.rs @@ -1,2 +1,2 @@ -mod chialisp; +pub mod chialisp; pub mod types;