From 80ec42d0194375c87954b4249bf5bb07406c4c51 Mon Sep 17 00:00:00 2001 From: Tanner Date: Fri, 14 Nov 2025 06:24:09 +0000 Subject: [PATCH] Print nested data types with parentheses (#22) Fixes #22 --- README.md | 2 +- src/compile/pretty.rs | 133 +++++++++++++++++++------------------ tests/script/datatype.smli | 9 +++ 3 files changed, 79 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index 451f929..d14cad7 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ val it = [4,6,8] : int list * [Change log](CHANGELOG.md) * Reading [test scripts](tests/script) can be instructive; try, for example, - [builtIn.smli](tests/script/builtIn.smli) + [built-in.smli](tests/script/built-in.smli) ## More information diff --git a/src/compile/pretty.rs b/src/compile/pretty.rs index 4233797..05dde2e 100644 --- a/src/compile/pretty.rs +++ b/src/compile/pretty.rs @@ -606,80 +606,85 @@ impl Pretty { return self .pretty1(buf, indent, line_end, depth, &args[0], value, 0, 0); } - let list = match &value { + + let mut print_wrapped = + |prefix: &str, type_arg: &Type, inner_val: &Val| { + self.pretty_raw(buf, indent, line_end, depth, prefix)?; + let need_parens = matches!(type_arg, Type::Data(..)); + + if need_parens { buf.push('('); } + self.pretty1( + buf, + indent, + line_end, + depth, + type_arg, + inner_val, + 0, + 0 + )?; + if need_parens { buf.push(')'); } + Ok(()) + }; + + match value { Val::Fn(f) => { - let name = match f { + let fname = match f { BuiltInFunction::OrderEqual => "EQUAL", BuiltInFunction::OrderGreater => "GREATER", BuiltInFunction::OrderLess => "LESS", _ => panic!("Expected list"), }; - return self.pretty_raw(buf, indent, line_end, depth, name); - } - Val::List(list) => list, - Val::Order(o) => { - return self.pretty_raw(buf, indent, line_end, depth, o.name()); - } - Val::Inl(v) => { - self.pretty_raw(buf, indent, line_end, depth, "INL ")?; - return self - .pretty1(buf, indent, line_end, depth, &args[0], v, 0, 0); - } - Val::Inr(v) => { - self.pretty_raw(buf, indent, line_end, depth, "INR ")?; - return self - .pretty1(buf, indent, line_end, depth, &args[1], v, 0, 0); - } - Val::Some(v) => { - self.pretty_raw(buf, indent, line_end, depth, "SOME ")?; - return self - .pretty1(buf, indent, line_end, depth, &args[0], v, 0, 0); - } - Val::Unit => { - if name == "option" { - return self - .pretty_raw(buf, indent, line_end, depth, "NONE"); + self.pretty_raw(buf, indent, line_end, depth, fname) + } + Val::Order(o) => + self.pretty_raw(buf, indent, line_end, depth, o.name()), + + Val::Inl(v) => + print_wrapped("INL ", &args[0], v), + Val::Inr(v) => + print_wrapped("INR ", &args[1], v), + Val::Some(v) => + print_wrapped("SOME ", &args[0], v), + + Val::Unit if name == "option" => + self.pretty_raw(buf, indent, line_end, depth, "NONE"), + + // 5. Handle Lists (Vector, Bag, or Generic Constructors) + Val::List(list) => { + if name == "vector" || name == "bag" { + if name == "vector" { buf.push('#'); } + let arg_type = args.first().unwrap(); + return self.print_list( + buf, + indent, + line_end, + depth, + arg_type, + list + ); + } + + let ty_con_name = list.first().unwrap().expect_string(); + buf.push_str(&ty_con_name); + + if let Some(arg) = list.get(1) { + buf.push(' '); + let need_parens = matches!(arg, Val::List(_)); + if need_parens { buf.push('('); } + + self.pretty2( + buf, indent, line_end, depth + 1, + &Type::Primitive(PrimitiveType::String), + arg, 0, 0 + )?; + + if need_parens { buf.push(')'); } } - panic!("Expected list") + Ok(()) } _ => panic!("Expected list"), - }; - if name == "vector" { - let arg_type = args.first().unwrap(); - buf.push('#'); - return self - .print_list(buf, indent, line_end, depth, arg_type, list); - } - if name == "bag" { - let arg_type = args.first().unwrap(); - return self - .print_list(buf, indent, line_end, depth, arg_type, list); - } - let ty_con_name = list.first().unwrap().expect_string(); - buf.push_str(&ty_con_name); - if let Some(arg) = list.get(1) { - // This is a parameterized constructor. (For example, NONE is - // not parameterized, SOME x is parameterized with x.) - buf.push(' '); - let need_parentheses = matches!(arg, Val::List(_)); - if need_parentheses { - buf.push('('); - } - self.pretty2( - buf, - indent, - line_end, - depth + 1, - &Type::Primitive(PrimitiveType::String), - arg, - 0, - 0, - )?; - if need_parentheses { - buf.push(')'); - } } - Ok(()) } fn pretty_type( diff --git a/tests/script/datatype.smli b/tests/script/datatype.smli index 4f4adda..ca237aa 100644 --- a/tests/script/datatype.smli +++ b/tests/script/datatype.smli @@ -392,4 +392,13 @@ datatype `foo bar baz` = `Foo Bar` of {a: int, b: string} | Baz of int; `Foo Bar`; > val it = fn : {a:int, b:string} -> foo bar baz +(*) Nested data types. +set("mode", "evaluate"); +> val it = () : unit +SOME (SOME 1); +> val it = SOME (SOME 1) : int option option +SOME (INL (INR 1)); +> val it = SOME (INL (INR 1)) : (('a,int) either,'b) either option +SOME [SOME (SOME 1), NONE]; +> val it = SOME [SOME (SOME 1),NONE] : int option option list option (*) End datatype.smli