diff --git a/libflux/flux-core/src/doc/mod.rs b/libflux/flux-core/src/doc/mod.rs index 714ef064fe..b9c47f0541 100644 --- a/libflux/flux-core/src/doc/mod.rs +++ b/libflux/flux-core/src/doc/mod.rs @@ -1548,7 +1548,7 @@ mod test { description: None, required: true, } ], - flux_type: "(x:A) => int".to_string(), + flux_type: "(x: A) => int".to_string(), is_option: false, source_location: loc.get(49,9,49,21), examples: vec![], @@ -1674,7 +1674,7 @@ foo.a description: Some("This is a description of x.".to_string()), required: true, } ], - flux_type: "(x:A) => int".to_string(), + flux_type: "(x: A) => int".to_string(), is_option: false, source_location: loc.get(49,9,49,21), examples: vec![Example { @@ -1806,7 +1806,7 @@ foo.a description: Some("This is a description of x.".to_string()), required: true, } ], - flux_type: "(x:A) => int".to_string(), + flux_type: "(x: A) => int".to_string(), is_option: false, source_location: loc.get(36,9,36,21), examples: vec![], @@ -1955,7 +1955,7 @@ foo.a required: false, } ], - flux_type: "(<-p:A, x:A) => A where A: Addable".to_string(), + flux_type: "(<-p: A, x: A) => A where A: Addable".to_string(), is_option: false, source_location: loc.get(15,9,15,30), examples: vec![], @@ -2022,7 +2022,7 @@ foo.a required: true, }, ], - flux_type: "(a:A, b:B, c:C) => int".to_string(), + flux_type: "(a: A, b: B, c: C) => int".to_string(), is_option: false, source_location: loc.get(14,9,14,27), examples: vec![], @@ -2122,7 +2122,7 @@ foo.a description: Some("Y has a long description too.".to_string()), required: true, }], - flux_type: "(x:A, y:A) => A where A: Addable".to_string(), + flux_type: "(x: A, y: A) => A where A: Addable".to_string(), is_option: false, source_location: loc.get(20,9,20,27), examples: vec![], @@ -2183,7 +2183,7 @@ foo.a description: Some("Y has a long description too.".to_string()), required: false, }], - flux_type: "(x:A, y:A) => A where A: Addable".to_string(), + flux_type: "(x: A, y: A) => A where A: Addable".to_string(), is_option: false, source_location: loc.get(20,9,20,27), examples: vec![], @@ -2236,7 +2236,7 @@ foo.a headline: "f is a function.".to_string(), description: None, parameters: vec![], - flux_type: "(x:A) => A".to_string(), + flux_type: "(x: A) => A".to_string(), is_option: false, source_location: loc.get(6, 9, 6, 21), examples: vec![], @@ -2283,7 +2283,7 @@ foo.a description: None, required: true, }], - flux_type: "(x:A, y:A) => A where A: Addable".to_string(), + flux_type: "(x: A, y: A) => A where A: Addable".to_string(), is_option: false, source_location: loc.get(9, 9, 9, 29), examples: vec![], @@ -2330,7 +2330,7 @@ foo.a description: None, required: true, }], - flux_type: "(<-y:A, x:A) => A where A: Addable".to_string(), + flux_type: "(<-y: A, x: A) => A where A: Addable".to_string(), is_option: false, source_location: loc.get(9, 9, 9, 32), examples: vec![], @@ -2377,7 +2377,7 @@ foo.a description: None, required: true, }], - flux_type: "(x:A, ?y:A) => A where A: Addable".to_string(), + flux_type: "(x: A, ?y: A) => A where A: Addable".to_string(), is_option: false, source_location: loc.get(9, 9, 9, 31), examples: vec![], diff --git a/libflux/flux-core/src/formatter/mod.rs b/libflux/flux-core/src/formatter/mod.rs index 9cc7ffdd46..d704476b88 100644 --- a/libflux/flux-core/src/formatter/mod.rs +++ b/libflux/flux-core/src/formatter/mod.rs @@ -70,17 +70,16 @@ fn format_item_list<'doc>( ) } -fn comma_list_with<'doc, I>( +pub(crate) fn comma_list_with<'doc, I>( arena: &'doc Arena<'doc>, docs: impl IntoIterator, IntoIter = I>, line: Doc<'doc>, ) -> Doc<'doc> where - I: ExactSizeIterator>, + I: Iterator>, { - let docs = docs.into_iter(); - let len = docs.len(); - let trailing_comma = if len == 0 { + let mut docs = docs.into_iter().peekable(); + let trailing_comma = if docs.peek().is_none() { arena.nil() } else { arena.text(",").flat_alt(arena.nil()) diff --git a/libflux/flux-core/src/lib.rs b/libflux/flux-core/src/lib.rs index 29e39a9777..b9f0953c66 100644 --- a/libflux/flux-core/src/lib.rs +++ b/libflux/flux-core/src/lib.rs @@ -18,6 +18,10 @@ #[macro_use] extern crate serde_derive; +#[macro_use] +#[cfg(test)] +extern crate pretty_assertions; + // Only include the doc module if the feature is enabled. // The code has lots of dependencies we do not want as part of the crate by default. #[cfg(feature = "doc")] diff --git a/libflux/flux-core/src/semantic/formatter/mod.rs b/libflux/flux-core/src/semantic/formatter/mod.rs index 2233f0af26..56efbd935b 100644 --- a/libflux/flux-core/src/semantic/formatter/mod.rs +++ b/libflux/flux-core/src/semantic/formatter/mod.rs @@ -34,6 +34,14 @@ pub fn format_node(node: walk::Node) -> Result { formatter.output() } +/// Format a `MonoType` +pub fn format_monotype(typ: &MonoType) -> String { + let arena = Arena::default(); + let mut formatter = DocFormatter { arena: &arena }; + let doc = formatter.format_monotype(typ); + doc.pretty(120).to_string() +} + /// Struct to hold data related to formatting such as formatted code, /// options, and errors. /// Provides methods for formatting files and strings of source code. @@ -292,7 +300,7 @@ impl Formatter { } fn format_text_part(&mut self, n: &semantic::nodes::TextPart) { - let escaped_string = self.escape_string(&n.value); + let escaped_string = escape_string(&n.value); self.write_string(&escaped_string); } @@ -744,26 +752,12 @@ impl Formatter { } // Write out escaped string value self.write_rune('"'); - let escaped_string = self.escape_string(&n.value); + let escaped_string = escape_string(&n.value); self.write_string(&escaped_string); self.write_rune('"'); // self.write_string(&format!(":{}", MonoType::String.to_string())); } - fn escape_string(&mut self, s: &str) -> String { - if !(s.contains('\"') || s.contains('\\')) { - return s.to_string(); - } - let mut escaped = String::with_capacity(s.len() * 2); - for r in s.chars() { - if r == '"' || r == '\\' { - escaped.push('\\') - } - escaped.push(r) - } - escaped - } - fn format_boolean_literal(&mut self, n: &semantic::nodes::BooleanLit) { let s: &str = if n.value { "true" } else { "false" }; self.write_string(s); @@ -890,6 +884,190 @@ impl Formatter { } } +use pretty::{docs, DocAllocator}; + +use crate::formatter::comma_list_with; + +type Arena<'doc> = pretty::Arena<'doc>; +type Doc<'doc> = pretty::DocBuilder<'doc, Arena<'doc>, ()>; + +const MULTILINE: usize = 4; +const INDENT: isize = INDENT_BYTES.len() as isize; + +struct DocFormatter<'doc> { + arena: &'doc Arena<'doc>, +} + +impl<'doc> DocFormatter<'doc> { + fn base_multiline(&self, base: &ast::BaseNode) -> Doc<'doc> { + self.multiline(base.is_multiline()) + } + + fn multiline(&self, multiline: bool) -> Doc<'doc> { + if multiline { + self.arena.hardline() + } else { + self.arena.line() + } + } + + fn base_multiline_(&self, base: &ast::BaseNode) -> Doc<'doc> { + self.multiline_(base.is_multiline()) + } + + fn multiline_(&self, multiline: bool) -> Doc<'doc> { + if multiline { + self.arena.hardline() + } else { + self.arena.line_() + } + } + + fn format_monotype(&self, n: &'doc MonoType) -> Doc<'doc> { + let arena = self.arena; + match n { + MonoType::Error => arena.text(""), + MonoType::Var(tv) => docs![arena, "#", tv.to_string()], + MonoType::BoundVar(tv) => arena.text(tv.to_string()), + MonoType::Builtin(nt) => arena.text(nt.to_string()), + MonoType::Collection(col) => match col.collection { + CollectionType::Array => { + docs![arena, "[", self.format_monotype(&col.arg), "]",] + } + CollectionType::Vector => { + docs![arena, "v[", self.format_monotype(&col.arg), "]",] + } + CollectionType::Stream => { + docs![arena, "stream[", self.format_monotype(&col.arg), "]",] + } + }, + MonoType::Dict(dict) => { + docs![ + arena, + "[", + self.format_monotype(&dict.key), + ":", + self.format_monotype(&dict.val), + "]", + ] + } + MonoType::Record(n) => { + let multiline = n.fields().count() > MULTILINE; + let line = self.multiline(multiline); + let line_ = self.multiline_(multiline); + + let mut fields = n.fields(); + + let fields_doc = comma_list_with( + arena, + fields.by_ref().map(|p| { + docs![arena, p.k.to_string(), ": ", self.format_monotype(&p.v),].group() + }), + line, + ); + + docs![ + arena, + "{", + docs![ + arena, + line_.clone(), + if let Some(typ) = fields.tail() { + docs![ + arena, + docs![arena, self.format_monotype(typ), arena.line(), "with",] + .group(), + arena.line(), + ] + } else { + arena.nil() + }, + fields_doc, + ] + .nest(INDENT), + line_, + "}", + ] + } + MonoType::Fun(n) => { + let multiline = n.parameters_len() > MULTILINE; + let line = self.multiline(multiline); + let line_ = self.multiline_(multiline); + + docs![ + arena, + "(", + docs![ + arena, + line_.clone(), + comma_list_with( + arena, + n.pipe + .iter() + .map(|p| { + docs![ + arena, + if p.k == "<-" { + docs![arena, &p.k] + } else { + docs![arena, "<-", &p.k] + }, + ": ", + self.format_monotype(&p.v), + ] + }) + .chain(n.req.iter().map(|(k, v)| { + docs![arena, k.as_str(), ": ", self.format_monotype(v),] + })) + .chain(n.opt.iter().map(|(name, argument)| { + docs![ + arena, + "?", + name.as_str(), + ": ", + self.format_monotype(&argument.typ), + match &argument.default { + Some(default) => + docs![arena, " = ", self.format_monotype(default)], + None => arena.nil(), + } + ] + })), + line, + ), + ] + .nest(INDENT), + line_.clone(), + ")", + " => ", + self.format_monotype(&n.retn), + ] + } + MonoType::Label(label) => self.format_string_literal(label), + } + .group() + } + + fn format_string_literal(&self, value: &str) -> Doc<'doc> { + let arena = self.arena; + docs![arena, "\"", escape_string(value), "\""] + } +} + +fn escape_string(s: &str) -> String { + if !(s.contains('\"') || s.contains('\\')) { + return s.to_string(); + } + let mut escaped = String::with_capacity(s.len() * 2); + for r in s.chars() { + if r == '"' || r == '\\' { + escaped.push('\\') + } + escaped.push(r) + } + escaped +} + fn get_precedence(node: &walk::Node) -> u32 { match node { walk::Node::BinaryExpr(p) => Operator::new(&p.operator).get_precedence(), diff --git a/libflux/flux-core/src/semantic/formatter/tests.rs b/libflux/flux-core/src/semantic/formatter/tests.rs index 41fb94b3df..7f5919e084 100644 --- a/libflux/flux-core/src/semantic/formatter/tests.rs +++ b/libflux/flux-core/src/semantic/formatter/tests.rs @@ -63,7 +63,7 @@ fn array_lit() { d = [1s, 2m, 3h]:[duration] e = [2019-10-31T00:00:00Z]:[time] f = [/a/, /b/, /c/]:[regexp] - g = [{a: 0, b: 0.0}:{a:int, b:float}, {a: 1, b: 1.1}:{a:int, b:float}]:[{a:int, b:float}]"#]], + g = [{a: 0, b: 0.0}:{a: int, b: float}, {a: 1, b: 1.1}:{a: int, b: float}]:[{a: int, b: float}]"#]], ) } @@ -115,15 +115,15 @@ fn format_function_expression() { package main (a) => { return a:#A - }:(a:#A) => #A + }:(a: #A) => #A f = (a, b=1) => { return a:A +:A b:A - }:(a:A, ?b:A) => A - x = f:(a:int, ?b:int) => int(a: 2):int - y = f:(a:int, ?b:int) => int(a: x:int, b: f:(a:int, ?b:int) => int(a: x:int):int):int + }:(a: A, ?b: A) => A + x = f:(a: int, ?b: int) => int(a: 2):int + y = f:(a: int, ?b: int) => int(a: x:int, b: f:(a: int, ?b: int) => int(a: x:int):int):int g = (t) => { return t:A - }:(<-t:A) => A"##]], + }:(<-t: A) => A"##]], ) } @@ -185,7 +185,7 @@ fn format_object_expression() { script, expect![[r#" package main - {a: 1, b: "2"}:{a:int, b:string}"#]], + {a: 1, b: "2"}:{a: int, b: string}"#]], ) } @@ -200,8 +200,8 @@ fn format_member_expression() { script, expect![[r#" package main - o = {temp: 30.0, loc: "FL"}:{temp:float, loc:string} - t = o:{temp:float, loc:string}.temp:float"#]], + o = {temp: 30.0, loc: "FL"}:{temp: float, loc: string} + t = o:{temp: float, loc: string}.temp:float"#]], ) } @@ -267,6 +267,6 @@ fn format_block_statement() { (r) => { v = (if r:#A <:bool 0 then -r:#A:#A else r:#A):#A return v:#A *:#A v:#A - }:(r:#A) => #A"##]], + }:(r: #A) => #A"##]], ) } diff --git a/libflux/flux-core/src/semantic/tests.rs b/libflux/flux-core/src/semantic/tests.rs index 5686035bb0..ed0d32ad8d 100644 --- a/libflux/flux-core/src/semantic/tests.rs +++ b/libflux/flux-core/src/semantic/tests.rs @@ -3631,7 +3631,7 @@ test_error_msg! { "#, // Location points to second interpolated expression expect: expect![[r#" - error: {b:float, a:int} (record) is not Stringable + error: {b: float, a: int} (record) is not Stringable ┌─ main:4:35 │ 4 │ "Hey ${bob} it's me ${joe}!" @@ -3740,7 +3740,7 @@ test_error_msg! { "#, // Location points to the identifier a expect: expect![[r#" - error: expected {A with x:B} (record) but found [int] (array) + error: expected {A with x: B} (record) but found [int] (array) ┌─ main:3:13 │ 3 │ a.x diff --git a/libflux/flux-core/src/semantic/tests/vectorize.rs b/libflux/flux-core/src/semantic/tests/vectorize.rs index 020268a80b..cc86f7e2b4 100644 --- a/libflux/flux-core/src/semantic/tests/vectorize.rs +++ b/libflux/flux-core/src/semantic/tests/vectorize.rs @@ -50,8 +50,8 @@ fn vectorize_field_access() -> anyhow::Result<()> { expect_test::expect![[r##" (r) => { - return {a: r:{#F with b:v[#B], a:v[#D]}.a:v[#D], b: r:{#F with b:v[#B], a:v[#D]}.b:v[#B]}:{a:v[#D], b:v[#B]} - }:(r:{#F with b:v[#B], a:v[#D]}) => {a:v[#D], b:v[#B]}"##]].assert_eq(&crate::semantic::formatter::format_node( + return {a: r:{#F with b: v[#B], a: v[#D]}.a:v[#D], b: r:{#F with b: v[#B], a: v[#D]}.b:v[#B]}:{a: v[#D], b: v[#B]} + }:(r: {#F with b: v[#B], a: v[#D]}) => {a: v[#D], b: v[#B]}"##]].assert_eq(&crate::semantic::formatter::format_node( Node::FunctionExpr(function), )?); @@ -66,8 +66,8 @@ fn vectorize_with_construction() -> anyhow::Result<()> { expect_test::expect![[r##" (r) => { - return {r:{#C with a:v[#B]} with b: r:{#C with a:v[#B]}.a:v[#B]}:{#C with b:v[#B], a:v[#B]} - }:(r:{#C with a:v[#B]}) => {#C with b:v[#B], a:v[#B]}"##]] + return {r:{#C with a: v[#B]} with b: r:{#C with a: v[#B]}.a:v[#B]}:{#C with b: v[#B], a: v[#B]} + }:(r: {#C with a: v[#B]}) => {#C with b: v[#B], a: v[#B]}"##]] .assert_eq(&crate::semantic::formatter::format_node( Node::FunctionExpr(function), )?); @@ -88,8 +88,8 @@ fn vectorize_even_when_another_function_fails_to_vectorize() -> anyhow::Result<( expect_test::expect![[r##" (r) => { - return {r:{#I with a:v[#G], b:v[#G]} with x: r:{#I with a:v[#G], b:v[#G]}.a:v[#G] +:v[#G] r:{#I with a:v[#G], b:v[#G]}.b:v[#G]}:{#I with x:v[#G], a:v[#G], b:v[#G]} - }:(r:{#I with a:v[#G], b:v[#G]}) => {#I with x:v[#G], a:v[#G], b:v[#G]}"##]] + return {r:{#I with a: v[#G], b: v[#G]} with x: r:{#I with a: v[#G], b: v[#G]}.a:v[#G] +:v[#G] r:{#I with a: v[#G], b: v[#G]}.b:v[#G]}:{#I with x: v[#G], a: v[#G], b: v[#G]} + }:(r: {#I with a: v[#G], b: v[#G]}) => {#I with x: v[#G], a: v[#G], b: v[#G]}"##]] .assert_eq(&crate::semantic::formatter::format_node( Node::FunctionExpr(function), )?); @@ -117,8 +117,8 @@ fn vectorize_addition_operator() -> anyhow::Result<()> { expect_test::expect![[r##" (r) => { - return {x: r:{#F with a:v[#D], b:v[#D]}.a:v[#D] +:v[#D] r:{#F with a:v[#D], b:v[#D]}.b:v[#D]}:{x:v[#D]} - }:(r:{#F with a:v[#D], b:v[#D]}) => {x:v[#D]}"##]].assert_eq(&crate::semantic::formatter::format_node( + return {x: r:{#F with a: v[#D], b: v[#D]}.a:v[#D] +:v[#D] r:{#F with a: v[#D], b: v[#D]}.b:v[#D]}:{x: v[#D]} + }:(r: {#F with a: v[#D], b: v[#D]}) => {x: v[#D]}"##]].assert_eq(&crate::semantic::formatter::format_node( Node::FunctionExpr(function), )?); @@ -133,8 +133,8 @@ fn vectorize_subtraction_operator() -> anyhow::Result<()> { expect_test::expect![[r##" (r) => { - return {x: r:{#F with a:v[#D], b:v[#D]}.a:v[#D] -:v[#D] r:{#F with a:v[#D], b:v[#D]}.b:v[#D]}:{x:v[#D]} - }:(r:{#F with a:v[#D], b:v[#D]}) => {x:v[#D]}"##]].assert_eq(&crate::semantic::formatter::format_node( + return {x: r:{#F with a: v[#D], b: v[#D]}.a:v[#D] -:v[#D] r:{#F with a: v[#D], b: v[#D]}.b:v[#D]}:{x: v[#D]} + }:(r: {#F with a: v[#D], b: v[#D]}) => {x: v[#D]}"##]].assert_eq(&crate::semantic::formatter::format_node( Node::FunctionExpr(function), )?); diff --git a/libflux/flux-core/src/semantic/types.rs b/libflux/flux-core/src/semantic/types.rs index 84f86c7dd4..6a0e292bf2 100644 --- a/libflux/flux-core/src/semantic/types.rs +++ b/libflux/flux-core/src/semantic/types.rs @@ -4,7 +4,7 @@ use std::{ borrow::Cow, cmp, collections::{BTreeMap, BTreeSet}, - fmt::{self, Write}, + fmt, hash::Hash, str::FromStr, }; @@ -17,6 +17,7 @@ use crate::{ errors::{Errors, Located}, map::HashMap, semantic::{ + formatter, fresh::Fresher, nodes::Symbol, sub::{ @@ -572,37 +573,28 @@ pub enum BuiltinType { /// Represents a Flux type. The type may be unknown, represented as a type variable, /// or may be a known concrete type. -#[derive(Debug, Display, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq)] #[allow(missing_docs)] pub enum MonoType { - #[display(fmt = "")] Error, - - #[display(fmt = "{}", _0)] Builtin(BuiltinType), - - #[display(fmt = "\"{}\"", _0)] Label(Label), - #[display(fmt = "#{}", _0)] Var(Tvar), - /// A type variable that is bound to to a `PolyType` that this variable is contained in. - #[display(fmt = "{}", _0)] BoundVar(Tvar), - - #[display(fmt = "{}", _0)] Collection(Ptr), - - #[display(fmt = "{}", _0)] Dict(Ptr), - - #[display(fmt = "{}", _0)] Record(Ptr), - - #[display(fmt = "{}", _0)] Fun(Ptr), } +impl fmt::Display for MonoType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let s = formatter::format_monotype(self); + f.write_str(&s) + } +} + impl Serialize for MonoType { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where @@ -668,22 +660,6 @@ pub struct Collection { pub arg: MonoType, } -impl fmt::Display for Collection { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.collection { - CollectionType::Array => { - write!(f, "[{}]", self.arg) - } - CollectionType::Vector => { - write!(f, "v[{}]", self.arg) - } - CollectionType::Stream => { - write!(f, "stream[{}]", self.arg) - } - } - } -} - #[derive(Debug, Clone, Copy, Eq, PartialEq)] #[allow(missing_docs)] pub enum CollectionType { @@ -1277,8 +1253,7 @@ impl Collection { } /// A key-value data structure. -#[derive(Debug, Display, Clone, Eq, PartialEq, Serialize)] -#[display(fmt = "[{}:{}]", key, val)] +#[derive(Debug, Clone, Eq, PartialEq, Serialize)] pub struct Dictionary { /// Type of key. pub key: MonoType, @@ -1343,18 +1318,7 @@ impl fmt::Debug for Record { impl fmt::Display for Record { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str("{")?; - let mut s = String::new(); - let tvar = self.format(&mut s)?; - if let Some(tv) = tvar { - write!(f, "{} with ", tv)?; - } - if s.len() > 2 { - // remove trailing ', ' delimiter - s.truncate(s.len() - 2); - } - f.write_str(s.as_str())?; - f.write_str("}") + MonoType::from(self.clone()).fmt(f) } } @@ -1590,14 +1554,6 @@ impl Record { } } - fn format(&self, f: &mut String) -> Result, fmt::Error> { - let mut fields = self.fields(); - for head in &mut fields { - write!(f, "{}, ", head)?; - } - Ok(fields.tail()) - } - /// Returns an iterator over the fields in the record pub fn fields(&self) -> FieldIter<'_> { FieldIter::Record(self) @@ -1874,8 +1830,7 @@ impl Label { } /// A key-value pair representing a property type in a record. -#[derive(Debug, Display, Clone, Eq, PartialEq, Serialize)] -#[display(fmt = "{}:{}", k, v)] +#[derive(Debug, Clone, Eq, PartialEq, Serialize)] #[allow(missing_docs)] pub struct Property { pub k: K, @@ -1902,19 +1857,6 @@ pub struct Argument { pub typ: T, } -impl fmt::Display for Argument -where - T: fmt::Display, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.typ)?; - if let Some(default) = &self.default { - write!(f, " = {}", default)?; - } - Ok(()) - } -} - impl From for Argument { fn from(typ: T) -> Self { Self { default: None, typ } @@ -1950,55 +1892,7 @@ pub struct Function { impl fmt::Display for Function { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let required = self - .req - .iter() - // Sort args with BTree - .collect::>() - .iter() - .map(|(&k, &v)| Property { - k: k.clone(), - v: v.clone(), - }) - .collect::>(); - - let optional = self - .opt - .iter() - // Sort args with BTree - .collect::>() - .iter() - .map(|(&k, &v)| Property { - k: String::from("?") + k, - v: v.clone(), - }) - .collect::>(); - - let pipe = match &self.pipe { - Some(pipe) => { - if pipe.k == "<-" { - vec![pipe.clone()] - } else { - vec![Property { - k: String::from("<-") + &pipe.k, - v: pipe.v.clone(), - }] - } - } - None => vec![], - }; - - write!( - f, - "({}) => {}", - pipe.iter() - .map(|x| x.to_string()) - .chain(required.iter().map(|x| x.to_string())) - .chain(optional.iter().map(|x| x.to_string())) - .collect::>() - .join(", "), - self.retn - ) + MonoType::from(self.clone()).fmt(f) } } @@ -2051,6 +1945,10 @@ impl Substitutable for Function { } impl Function { + pub(crate) fn parameters_len(&self) -> usize { + self.opt.len() + self.req.len() + self.pipe.is_some() as usize + } + pub(crate) fn map(self, mut f: impl FnMut(T) -> U) -> Function { let Self { opt, @@ -2492,7 +2390,7 @@ mod tests { #[test] fn display_type_record() { assert_eq!( - "{A with a:int, b:string}", + "{A with a: int, b: string}", Record::new( [ Property { @@ -2509,7 +2407,7 @@ mod tests { .to_string() ); assert_eq!( - "{a:int, b:string}", + "{a: int, b: string}", Record::new( [ Property { @@ -2539,7 +2437,7 @@ mod tests { .to_string() ); assert_eq!( - "(<-:int) => int", + "(<-: int) => int", Function { req: MonoTypeMap::new(), opt: MonoTypeMap::new(), @@ -2552,7 +2450,7 @@ mod tests { .to_string() ); assert_eq!( - "(<-a:int) => int", + "(<-a: int) => int", Function { req: MonoTypeMap::new(), opt: MonoTypeMap::new(), @@ -2565,7 +2463,7 @@ mod tests { .to_string() ); assert_eq!( - "(<-:int, a:int, b:int) => int", + "(<-: int, a: int, b: int) => int", Function { req: semantic_map! { String::from("a") => MonoType::INT, @@ -2581,7 +2479,7 @@ mod tests { .to_string() ); assert_eq!( - "(<-:int, ?a:int, ?b:int) => int", + "(<-: int, ?a: int, ?b: int) => int", Function { req: MonoTypeMap::new(), opt: semantic_map! { @@ -2597,7 +2495,13 @@ mod tests { .to_string() ); assert_eq!( - "(<-:int, a:int, b:int, ?c:int, ?d:int) => int", + r#"( + <-: int, + a: int, + b: int, + ?c: int, + ?d: int, +) => int"#, Function { req: semantic_map! { String::from("a") => MonoType::INT, @@ -2616,7 +2520,7 @@ mod tests { .to_string() ); assert_eq!( - "(a:int, ?b:bool) => int", + "(a: int, ?b: bool) => int", Function { req: semantic_map! { String::from("a") => MonoType::INT, @@ -2630,7 +2534,7 @@ mod tests { .to_string() ); assert_eq!( - "(<-a:int, b:int, c:int, ?d:bool) => int", + "(<-a: int, b: int, c: int, ?d: bool) => int", Function { req: semantic_map! { String::from("b") => MonoType::INT, @@ -2661,7 +2565,7 @@ mod tests { .to_string(), ); assert_eq!( - "(x:A) => A", + "(x: A) => A", PolyType { vars: vec![Tvar(0)], cons: TvarKinds::new(), @@ -2677,7 +2581,7 @@ mod tests { .to_string(), ); assert_eq!( - "(x:A, y:B) => {x:A, y:B}", + "(x: A, y: B) => {x: A, y: B}", PolyType { vars: vec![Tvar(0), Tvar(1)], cons: TvarKinds::new(), @@ -2706,7 +2610,7 @@ mod tests { .to_string(), ); assert_eq!( - "(a:A, b:A) => A where A: Addable", + "(a: A, b: A) => A where A: Addable", PolyType { vars: vec![Tvar(0)], cons: semantic_map! {Tvar(0) => vec![Kind::Addable]}, @@ -2723,7 +2627,7 @@ mod tests { .to_string(), ); assert_eq!( - "(x:A, y:B) => {x:A, y:B} where A: Addable, B: Divisible", + "(x: A, y: B) => {x: A, y: B} where A: Addable, B: Divisible", PolyType { vars: vec![Tvar(0), Tvar(1)], cons: semantic_map! { @@ -2755,7 +2659,7 @@ mod tests { .to_string(), ); assert_eq!( - "(x:A, y:B) => {x:A, y:B} where A: Comparable + Equatable, B: Addable + Divisible", + "(x: A, y: B) => {x: A, y: B} where A: Comparable + Equatable, B: Addable + Divisible", PolyType { vars: vec![Tvar(0), Tvar(1)], cons: semantic_map! { diff --git a/libflux/flux/src/cffi.rs b/libflux/flux/src/cffi.rs index 4b2d4a3363..63212d7cf3 100644 --- a/libflux/flux/src/cffi.rs +++ b/libflux/flux/src/cffi.rs @@ -753,11 +753,28 @@ mod tests { )); assert_eq!( format!("{}", ty), - "{t4972 with a:t4949, b:t4949, e:t4957, f:t4957, g:t4957}" + r#"{ + t4972 with + a: t4949, + b: t4949, + e: t4957, + f: t4957, + g: t4957, +}"#, ); let mut v = MonoTypeNormalizer::new(); v.normalize(&mut ty); - assert_eq!(format!("{}", ty), "{C with a:A, b:A, e:B, f:B, g:B}"); + assert_eq!( + format!("{}", ty), + r#"{ + C with + a: A, + b: A, + e: B, + f: B, + g: B, +}"# + ); } #[test] @@ -774,7 +791,10 @@ vstr = v.str + "hello" let mut t = find_var_type(&pkg, "v".into()).expect("Should be able to get a MonoType."); let mut v = MonoTypeNormalizer::new(); v.normalize(&mut t); - assert_eq!(format!("{}", t), "{B with int:int, sweet:A, str:string}"); + assert_eq!( + format!("{}", t), + r#"{B with int: int, sweet: A, str: string}"# + ); expect_test::expect![[r#" { @@ -843,7 +863,7 @@ p = o.ethan let mut t = find_var_type(&pkg, "v".into()).expect("Should be able to get a MonoType."); let mut v = MonoTypeNormalizer::new(); v.normalize(&mut t); - assert_eq!(format!("{}", t), "{B with int:int, ethan:A}"); + assert_eq!(format!("{}", t), r#"{B with int: int, ethan: A}"#); expect_test::expect![[r#" { @@ -893,7 +913,7 @@ from(bucket: v.bucket) v.normalize(&mut ty); assert_eq!( format!("{}", ty), - "{D with measurement:A, timeRangeStart:B, timeRangeStop:C, bucket:string}" + "{D with measurement: A, timeRangeStart: B, timeRangeStop: C, bucket: string}" ); } diff --git a/libflux/go/libflux/buildinfo.gen.go b/libflux/go/libflux/buildinfo.gen.go index 73cb6073e8..ead12f201a 100644 --- a/libflux/go/libflux/buildinfo.gen.go +++ b/libflux/go/libflux/buildinfo.gen.go @@ -22,10 +22,10 @@ var sourceHashes = map[string]string{ "libflux/flux-core/src/bin/fluxc.rs": "bf275289e690236988049fc0a07cf832dbac25bb5739c02135b069dcdfab4d0f", "libflux/flux-core/src/bin/fluxdoc.rs": "354a8dfd262f223412ee491a9947962f993e43c5467f8ee37c4f15528dc5b571", "libflux/flux-core/src/doc/example.rs": "35cd959719ed501f7da781eccf3a0edfd598431b7aa4d01d5b2e35619197ca6e", - "libflux/flux-core/src/doc/mod.rs": "f67a43c7b6f7ceec322b269546af97bc6925394e8da12d67bf28d18b830123e5", + "libflux/flux-core/src/doc/mod.rs": "764b5a2802558aae1ce2457e2d0f5107e155abbc0de79e13771ee0053e3358ce", "libflux/flux-core/src/errors.rs": "b1d8a992686f20a41ea5996ac272625743b2770e3382f8113c4260f33cebf917", - "libflux/flux-core/src/formatter/mod.rs": "5a90cfc05244b91ae4c326c4cf5aee4de8745d990ddf3c87d991c72f78bdc288", - "libflux/flux-core/src/lib.rs": "443aed16dd600eecc1ffbee2d2dead6788e796cd5a278eb5dafb123843b8959e", + "libflux/flux-core/src/formatter/mod.rs": "3554398ed5b18979585220f575ea98471f9ddba4d87f7c38ba2fa06db1df455d", + "libflux/flux-core/src/lib.rs": "b2bb87383e602bcadf2dfcf30874d1f78f6e16751eb9eddeffa303e1dceed471", "libflux/flux-core/src/map.rs": "342c1cc111d343f01b97f38be10a9f1097bdd57cdc56f55e92fd3ed5028e6973", "libflux/flux-core/src/parser/mod.rs": "a2b043f5103ea487822f17c30e775e49d6bf3572684b13af677a9cfd1057cff6", "libflux/flux-core/src/parser/strconv.rs": "84d24110f8af4a40ff7584cb0a41e8a1f8949d19c1ec329719057c5302f7615d", @@ -42,7 +42,7 @@ var sourceHashes = map[string]string{ "libflux/flux-core/src/semantic/flatbuffers/mod.rs": "270671ffdcb1eb5308f9bbab0431c9464df264070a2deb05c526d182a6ec5585", "libflux/flux-core/src/semantic/flatbuffers/semantic_generated.rs": "beaaa6b08d8b56dba81153a58e440bbdc430b4eca0201a3431e90b793f1adbce", "libflux/flux-core/src/semantic/flatbuffers/types.rs": "029d51104eb4a0bbfc8d82a470e0f2915a27d7cb00c08a3f35e638b5a3105fc2", - "libflux/flux-core/src/semantic/formatter/mod.rs": "44af48f9fd3d6e297572b5ede5b5a292ea66d091ccd62dfc0148960d0c9d91c7", + "libflux/flux-core/src/semantic/formatter/mod.rs": "f39a1d127900ea409af7ad5d02b646c66471f31c90bb49359c48ea2bb3bbd5c1", "libflux/flux-core/src/semantic/fresh.rs": "97238fbc317e7c51836a6ba3441d641d9f4f8c7f637bde4bccbd0e09146129d0", "libflux/flux-core/src/semantic/fs.rs": "f7f609bc8149769d99b737150e184a2d54029c0b768365dbcf08ff193b0e1f6f", "libflux/flux-core/src/semantic/import.rs": "184e955211db1ceb1be782b4daf75584b86907b1428e50015497909cfc2dd89a", @@ -50,7 +50,7 @@ var sourceHashes = map[string]string{ "libflux/flux-core/src/semantic/mod.rs": "3cec76c645e411592898a3e153d571787157bdfdf8552c21748c5e8c5e41d48d", "libflux/flux-core/src/semantic/nodes.rs": "300de1a0f78f303b97736add050d9c8e5d90ad9554eaed6d24b79f9c39991f67", "libflux/flux-core/src/semantic/sub.rs": "a989e50c113ca899fe02f8d49a4744a420580a3f803f656db25beb2d0c2a247b", - "libflux/flux-core/src/semantic/types.rs": "dc444143773bf261fb11520543964f6e03dadcec4a8f4b239e151a37710a5b1a", + "libflux/flux-core/src/semantic/types.rs": "ed414b695e925f18f74984ec88bba652ef8dd8c9e905cb9e8fa19b101a4601b4", "libflux/flux-core/src/semantic/vectorize.rs": "6ce2dc4e6ff572abc0146a220291322ea88557ce674ae16220a2d67420e9fa0d", "libflux/flux-core/src/semantic/walk/_walk.rs": "198e6c546f73f78b1de4b963084033a06a6d71e72b0294b16c5e4de874f97a38", "libflux/flux-core/src/semantic/walk/mod.rs": "a0eab3f225dc294569217da295d44f31073e643073e6b00fec5846cde2382ade", @@ -59,7 +59,7 @@ var sourceHashes = map[string]string{ "libflux/flux/Cargo.toml": "9fae0ee886a5c5278ad6b4606c36d9e929ab9529995856a333a618caae35c210", "libflux/flux/FLUXDOC.md": "92e6dd8043bd87b4924e09aa28fb5346630aee1214de28ea2c8fc0687cad0785", "libflux/flux/build.rs": "6f2a4da51744a174ab13a1ebcb18ea39c0acfc2c720e7a61ea3e326fb0649529", - "libflux/flux/src/cffi.rs": "598412e4d6faa5aa7858dad228b09999c89f2f46f42b5a062a2202be3e5676e1", + "libflux/flux/src/cffi.rs": "49fe2c55dbe091ebd56deaec6873164227d37bb87e46f0c998877d07a2431cdf", "libflux/flux/src/lib.rs": "3cd7dfcf7491f5797d501a647ee92a3f66b5790f6df7ed2238f1969b4bd929ed", "libflux/flux/templates/base.html": "a818747b9621828bb96b94291c60922db54052bbe35d5e354f8e589d2a4ebd02", "libflux/flux/templates/home.html": "f9927514dd42ca7271b4817ad1ca33ec79c03a77a783581b4dcafabd246ebf3f",