diff --git a/Cargo.lock b/Cargo.lock index dc2cefb741..ce333ccd38 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1143,7 +1143,7 @@ dependencies = [ "itertools", "log 0.4.8", "ordered-float", - "pretty 0.9.0", + "pretty", "pretty_assertions", "quick-error", "serde", @@ -1180,7 +1180,7 @@ dependencies = [ "gluon_parser", "itertools", "log 0.4.8", - "pretty 0.9.0", + "pretty", "pretty_assertions", "quick-error", "rpds", @@ -1245,7 +1245,7 @@ dependencies = [ "lazy_static", "log 0.4.8", "opener", - "pretty 0.5.2", + "pretty", "pulldown-cmark 0.6.0", "rayon", "regex 1.3.1", @@ -1268,7 +1268,7 @@ dependencies = [ "gluon_base", "itertools", "log 0.4.8", - "pretty 0.9.0", + "pretty", "pretty_assertions", ] @@ -1288,7 +1288,7 @@ dependencies = [ "lalrpop-util", "log 0.4.8", "ordered-float", - "pretty 0.5.2", + "pretty", "pretty_assertions", "quick-error", ] @@ -1349,7 +1349,7 @@ dependencies = [ "ordered-float", "parking_lot 0.9.0", "petgraph", - "pretty 0.9.0", + "pretty", "pretty_assertions", "quick-error", "regex 1.3.1", @@ -2260,20 +2260,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" -[[package]] -name = "pretty" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60c0d9f6fc88ecdd245d90c1920ff76a430ab34303fc778d33b1d0a4c3bf6d3" -dependencies = [ - "typed-arena 1.7.0", -] - [[package]] name = "pretty" version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1891757f8427ce41706957fde1fec1b86aee3e335bd50320257705061507a24c" dependencies = [ "arrayvec 0.5.1", "typed-arena 2.0.1", diff --git a/Cargo.toml b/Cargo.toml index ff829340f3..daa8f1c0c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -148,3 +148,6 @@ debug = 1 [profile.release] debug = 1 + +[patch.crates-io] +pretty = { path = "../pretty.rs", version = "0.9.0" } diff --git a/README.md b/README.md index 0ccbcb2d32..d2d5543b04 100644 --- a/README.md +++ b/README.md @@ -93,8 +93,14 @@ let { (*>), (<*), wrap } = import! std.applicative let { for } = import! std.traversable -type Op = | Add | Sub | Div | Mul -type Expr = | Int Int | Binop Expr Op Expr +type Op = + | Add + | Sub + | Div + | Mul +type Expr = + | Int Int + | Binop Expr Op Expr let parse : String -> Result String Expr = // Gluon has a small parser combinator library which makes it easy to define an expression parser @@ -110,7 +116,9 @@ let parse : String -> Result String Expr = lazy_parser, chainl1, (), - ? } = import! std.parser + ? + } = + import! std.parser let { (<|>) } = import! std.alternative let lex x = x <* spaces @@ -123,13 +131,14 @@ let parse : String -> Result String Expr = | Err _ -> parser.fail "Unable to parse integer" let operator = - satisfy_map (\c -> - match c with - | '*' -> Some Mul - | '+' -> Some Add - | '-' -> Some Sub - | '/' -> Some Div - | _ -> None) + satisfy_map + (\c -> + match c with + | '*' -> Some Mul + | '+' -> Some Add + | '-' -> Some Sub + | '/' -> Some Div + | _ -> None) "operator" rec @@ -145,9 +154,6 @@ let parse : String -> Result String Expr = let expr _ = binop () in - - // Gluon makes it possible to partially apply functions which we use here to scope all parser functions - // inside the `let parse` binding above. let parse : String -> Result String Expr = parser.parse (expr () <* spaces) parse @@ -175,7 +181,9 @@ let eval expr : Expr -> Int = | Mul -> (*) f (eval l) (eval r) -do digits = +do +digits += let gen_digit = random.thread_rng.gen_int_range 1 10 do a = gen_digit do b = gen_digit @@ -183,7 +191,9 @@ do digits = do d = gen_digit wrap [a, b, c, d] -let print_digits = for digits (\d -> +let print_digits = for + digits + (\d -> seq io.print " " io.print (show d)) seq io.print "Four digits:" *> print_digits *> io.println "" @@ -199,8 +209,7 @@ let guess_loop _ = | Ok expr -> if validate digits expr then let result = eval expr - if result == 24 - then io.println "Correct!" + if result == 24 then io.println "Correct!" else io.println ("Incorrect, " <> int.show.show result <> " != 24") *> guess_loop () else io.println diff --git a/base/src/error.rs b/base/src/error.rs index 0d13c93edf..2be273f997 100644 --- a/base/src/error.rs +++ b/base/src/error.rs @@ -216,19 +216,18 @@ impl InFile { self.error } - pub fn emit_string(&self, code_map: &::codespan::CodeMap) -> io::Result + pub fn emit_string(&self) -> io::Result where E: AsDiagnostic, { let mut output = Vec::new(); self.emit( &mut ::codespan_reporting::termcolor::NoColor::new(&mut output), - code_map, )?; Ok(String::from_utf8(output).unwrap()) } - pub fn emit(&self, writer: &mut W, code_map: &::codespan::CodeMap) -> io::Result<()> + pub fn emit(&self, writer: &mut W) -> io::Result<()> where W: ?Sized + ::codespan_reporting::termcolor::WriteColor, E: AsDiagnostic, @@ -242,7 +241,7 @@ impl InFile { if i != 0 { writeln!(writer)?; } - ::codespan_reporting::emit(&mut *writer, &code_map, &diagnostic)?; + ::codespan_reporting::emit(&mut *writer, &self.source, &diagnostic)?; } Ok(()) } @@ -254,7 +253,7 @@ impl fmt::Display for InFile { { let mut writer = ::codespan_reporting::termcolor::NoColor::new(&mut buffer); - self.emit(&mut writer, &self.source) + self.emit(&mut writer) .map_err(|_| fmt::Error)?; } diff --git a/base/src/lib.rs b/base/src/lib.rs index 8b23757ffe..79de1368cf 100644 --- a/base/src/lib.rs +++ b/base/src/lib.rs @@ -73,8 +73,9 @@ macro_rules! type_cache { } } +#[macro_export] macro_rules! chain { - ($alloc: expr; $first: expr, $($rest: expr),+) => {{ + ($alloc: expr; $first: expr, $($rest: expr),+ $(,)?) => {{ let mut doc = ::pretty::DocBuilder($alloc, $first.into()); $( doc = doc.append($rest); diff --git a/base/src/source.rs b/base/src/source.rs index ad59cf443e..20c0f7c53e 100644 --- a/base/src/source.rs +++ b/base/src/source.rs @@ -159,7 +159,7 @@ impl<'a> DoubleEndedIterator for CommentIter<'a> { .src .trim_end_matches(|c: char| c.is_whitespace() && c != '\n'); if self.src.ends_with('\n') { - let comment_line = self.src[..self.src.len() - 1].lines().next_back().unwrap(); + let comment_line = self.src[..self.src.len() - 1].lines().next_back()?; let trimmed = comment_line.trim_start(); let newline_len = if self.src.ends_with("\r\n") { 2 } else { 1 }; diff --git a/base/src/types/mod.rs b/base/src/types/mod.rs index 1a2f355f9d..6541bb16fa 100644 --- a/base/src/types/mod.rs +++ b/base/src/types/mod.rs @@ -1208,6 +1208,17 @@ where } } + fn is_simple_constructor(&self) -> bool { + let mut typ = self; + while let Some((_, ret)) = typ.as_function() { + typ = ret; + } + match typ { + Type::Opaque => true, + _ => false, + } + } + pub fn as_function(&self) -> Option<(&T, &T)> { self.as_function_with_type().map(|t| (t.1, t.2)) } @@ -2499,17 +2510,6 @@ where } } -#[macro_export] -macro_rules! chain { - ($alloc: expr; $first: expr, $($rest: expr),+) => {{ - let mut doc = ::pretty::DocBuilder($alloc, $first.into()); - $( - doc = doc.append($rest); - )* - doc - }} -} - const INDENT: isize = 4; impl<'a, I, T> DisplayType<'a, T> @@ -2518,6 +2518,22 @@ where I: AsRef + 'a, { pub fn pretty(&self, printer: &Printer<'a, I, A>) -> DocBuilder<'a, Arena<'a, A>, A> + where + A: Clone, + { + let typ = self.typ; + + let doc = self.pretty_(printer); + match **typ { + Type::ExtendRow { .. } | Type::Variant(..) | Type::Function(..) => doc, + _ => { + let comment = printer.comments_before(typ.span().start()); + comment.append(doc) + } + } + } + + fn pretty_(&self, printer: &Printer<'a, I, A>) -> DocBuilder<'a, Arena<'a, A>, A> where A: Clone, { @@ -2526,7 +2542,7 @@ where let p = self.prec; let typ = self.typ; - let doc = match **typ { + match **typ { Type::Hole => arena.text("_"), Type::Error => arena.text("!"), Type::Opaque => arena.text(""), @@ -2535,14 +2551,14 @@ where chain![arena; "forall ", arena.concat(args.iter().map(|arg| { - arena.text(arg.id.as_ref()).append(arena.space()) + arena.text(arg.id.as_ref()).append(arena.line()) })), "." ].group(), - arena.space(), + arena.line(), top(typ).pretty(printer) ]; - p.enclose(Prec::Function, arena, doc) + p.enclose(Prec::Function, arena, doc).group() } Type::Variable(ref var) => arena.text(format!("{}", var.id)), Type::Skolem(ref skolem) => chain![ @@ -2556,11 +2572,11 @@ where Type::App(ref t, ref args) => match self.typ.as_function() { Some(_) => self.pretty_function(printer).nest(INDENT), None => { - let doc = dt(Prec::Top, t).pretty(printer); + let doc = dt(Prec::Top, t).pretty_(printer); let arg_doc = arena.concat(args.iter().map(|arg| { - arena - .space() - .append(dt(Prec::Constructor, arg).pretty(printer)) + printer + .space_before(arg.span().start()) + .append(dt(Prec::Constructor, arg).pretty_(printer)) })); let doc = doc.append(arg_doc.nest(INDENT)); p.enclose(Prec::Constructor, arena, doc).group() @@ -2580,7 +2596,6 @@ where .. } => { doc = doc.append(arena.concat(fields.iter().map(|field| { - let typ = remove_forall(&field.typ); chain![arena; if first { first = false; @@ -2588,32 +2603,37 @@ where } else { arena.hardline() }, - "| ", - field.name.as_ref(), - if typ.as_function().is_some() { - arena.concat(arg_iter(typ).map(|arg| { - chain![arena; - " ", - dt(Prec::Constructor, arg).pretty(printer) - ] - })) - } else { - arena.concat(row_iter(typ).map(|field| { + chain![arena; + "| ", + field.name.as_ref(), + if field.typ.is_simple_constructor() { + arena.concat(arg_iter(&field.typ).map(|arg| { + chain![arena; + " ", + dt(Prec::Constructor, arg).pretty(printer) + ] + })) + } else { chain![arena; - " ", - dt(Prec::Constructor, &field.typ).pretty(printer) - ] - })) - } + " :", + arena.line(), + top(&field.typ).pretty(printer), + ].nest(INDENT) + } + ] + .group() ] - .group() }))); rest } _ => { doc = chain![arena; doc, - arena.hardline(), + if first { + arena.nil() + } else { + arena.line() + }, ".. ", top(row).pretty(printer) ]; @@ -2678,17 +2698,6 @@ where .intersperse(arena.text(".")), ), Type::Alias(ref alias) => printer.symbol(&alias.name), - }; - match **typ { - Type::App(..) - | Type::ExtendRow { .. } - | Type::ExtendTypeRow { .. } - | Type::Variant(..) - | Type::Function(..) => doc, - _ => { - let comment = printer.comments_before(typ.span().start()); - comment.append(doc) - } } } @@ -2709,7 +2718,7 @@ where let hardline = if forced_hardline { arena.hardline() } else { - arena.space() + arena.line() }; let mut doc = arena.text(open); @@ -2724,7 +2733,7 @@ where .append(top(row).pretty_row(open, printer, pretty_field)) .nest(INDENT), _ => doc - .append(arena.space()) + .append(arena.line()) .append("| ") .append(top(row).pretty(printer)) .nest(INDENT), @@ -2765,7 +2774,7 @@ where let hardline = if forced_hardline { arena.hardline() } else { - arena.space() + arena.line() }; let print_any_field = fields @@ -2784,9 +2793,9 @@ where let f = chain![arena; field.name.as_ref(), - arena.space(), + arena.line(), arena.concat(field.typ.params().iter().map(|param| { - arena.text(param.id.as_ref()).append(arena.space()) + arena.text(param.id.as_ref()).append(arena.line()) })), arena.text("= "), if filter == Filter::RetainKey { @@ -2794,7 +2803,7 @@ where } else { top(&field.typ.typ).pretty(printer) }, - if i + 1 != types_len || print_any_field { + if i + 1 != types_len || print_any_field { arena.text(",") } else { arena.nil() @@ -2867,7 +2876,7 @@ where match **typ { Type::EmptyRow => doc, _ => doc - .append(arena.space()) + .append(arena.line()) .append("| ") .append(top(typ).pretty(printer)), } @@ -2891,9 +2900,11 @@ where let arena = printer.arena; match self.typ.as_function_with_type() { Some((arg_type, arg, ret)) => chain![arena; - if arg_type == ArgType::Implicit { "[" } else { "" }, - dt(Prec::Function, arg).pretty(printer), - if arg_type == ArgType::Implicit { "]" } else { "" }, + chain![arena; + if arg_type == ArgType::Implicit { arena.text("[") } else { arena.nil() }, + dt(Prec::Function, arg).pretty(printer), + if arg_type == ArgType::Implicit { arena.text("]") } else { arena.nil() }, + ].group(), printer.space_after(arg.span().end()), "-> ", top(ret).pretty_function_(printer) diff --git a/base/src/types/pretty_print.rs b/base/src/types/pretty_print.rs index f492bdcb94..03f17f6d14 100644 --- a/base/src/types/pretty_print.rs +++ b/base/src/types/pretty_print.rs @@ -212,11 +212,9 @@ impl<'a, I, A> Printer<'a, I, A> { } pub fn space_before(&self, pos: BytePos) -> DocBuilder<'a, Arena<'a, A>, A> { - let (doc, comments) = self.comments_before_(pos); + let doc = self.comments_before(pos); if let Doc::Nil = *doc.1 { - self.arena.space() - } else if comments { - self.arena.space().append(doc).append(self.arena.space()) + self.arena.line() } else { doc } @@ -226,86 +224,137 @@ impl<'a, I, A> Printer<'a, I, A> { let arena = self.arena; let doc = self.comments_after(end); if let Doc::Nil = *doc.1 { - arena.space() + arena.line() } else { - arena.space().append(doc) + doc } } - pub fn comments_before(&self, pos: BytePos) -> DocBuilder<'a, Arena<'a, A>, A> { - let (doc, comments) = self.comments_before_(pos); - if comments { - doc.append(self.arena.space()) + pub fn nilline_before(&self, pos: BytePos) -> DocBuilder<'a, Arena<'a, A>, A> { + let doc = self.comments_before(pos); + if let Doc::Nil = *doc.1 { + self.arena.line_() } else { doc } } - fn comments_before_(&self, pos: BytePos) -> (DocBuilder<'a, Arena<'a, A>, A>, bool) { + pub fn nilline_after(&self, end: BytePos) -> DocBuilder<'a, Arena<'a, A>, A> { let arena = self.arena; - - if pos == 0.into() { - return (arena.nil(), false); + let doc = self.comments_after(end); + if let Doc::Nil = *doc.1 { + arena.line_() + } else { + doc } + } - let mut doc = arena.nil(); - let mut comments = 0; - for comment in self - .source - .comments_between(Span::new(self.source.span().start(), pos)) - .rev() - { - let x = if comment.is_empty() { - arena.hardline() - } else if comment.starts_with("//") { - arena.text(comment).append(arena.hardline()) - } else { - comments += 1; - arena.text(comment) - }; - doc = x.append(doc); + pub fn comments_before(&self, pos: BytePos) -> DocBuilder<'a, Arena<'a, A>, A> { + if pos == BytePos::none() { + return self.arena.nil(); } - (doc, comments != 0) + let (comments_after, mut doc, comments_before) = self.make_comments_doc( + pos, + self.source + .comments_between(Span::new(self.source.span().start(), pos)) + .rev(), + |l, r| r.append(l), + ); + doc = match comments_before { + CommentLike::Line | CommentLike::Block => self.arena.line().append(doc), + CommentLike::Empty => doc, + }; + doc = match comments_after { + CommentLike::Block => doc.append(self.arena.line()), + CommentLike::Line | CommentLike::Empty => doc, + }; + doc } pub fn comments_after(&self, end: BytePos) -> DocBuilder<'a, Arena<'a, A>, A> { - let (doc, block_comments, _) = - self.comments_count(Span::new(end, self.source.span().end())); - if block_comments == 0 { - doc - } else { - let arena = self.arena; - chain![arena; - doc, - arena.space() - ] + let (doc, _) = self.comments_count(Span::new(end, self.source.span().end())); + doc + } + + pub fn comments_count(&self, span: Span) -> (DocBuilder<'a, Arena<'a, A>, A>, usize) { + if span.start() == BytePos::none() || span.end() == BytePos::none() { + return (self.arena.nil(), 0); + } + + let mut comments = 0; + let (comments_before, mut doc, comments_after) = self.make_comments_doc( + span.start(), + self.source.comments_between(span).inspect(|comment| { + if !comment.is_empty() { + comments += 1 + } + }), + |l, r| l.append(r), + ); + + match comments_before { + CommentLike::Line | CommentLike::Block => { + // Avoid inserting an extra space before comments at the start of the file + if self.source.span().start() != span.start() { + doc = self.arena.line().append(doc) + } + } + CommentLike::Empty => (), } + doc = match comments_after { + CommentLike::Block => doc.append(self.arena.line()), + CommentLike::Line | CommentLike::Empty => doc, + }; + (doc, comments) } - pub fn comments_count( + fn make_comments_doc( &self, - span: Span, - ) -> (DocBuilder<'a, Arena<'a, A>, A>, usize, bool) { + pos: BytePos, + iterable: impl IntoIterator, + mut append: impl FnMut( + DocBuilder<'a, Arena<'a, A>, A>, + DocBuilder<'a, Arena<'a, A>, A>, + ) -> DocBuilder<'a, Arena<'a, A>, A>, + ) -> (CommentLike, DocBuilder<'a, Arena<'a, A>, A>, CommentLike) { let arena = self.arena; - if span.start() == 0.into() { - return (arena.nil(), 0, false); + if pos == 0.into() { + return (CommentLike::Empty, arena.nil(), CommentLike::Empty); } - let mut comments = 0; - let mut ends_with_hardline = false; - let doc = arena.concat(self.source.comments_between(span).map(|comment| { - ends_with_hardline = false; - if comment.is_empty() { - ends_with_hardline = true; + let mut doc = arena.nil(); + let mut comments_before = CommentLike::Empty; + let mut comments_after = CommentLike::Empty; + for (i, comment) in iterable.into_iter().enumerate() { + let x = if comment.is_empty() { + comments_after = CommentLike::Empty; arena.hardline() } else if comment.starts_with("//") { + if i == 0 { + comments_before = CommentLike::Line; + } + comments_after = CommentLike::Line; + arena.text(comment).append(arena.hardline()) } else { - comments += 1; + if i == 0 { + comments_before = CommentLike::Block; + } + comments_after = CommentLike::Block; + arena.text(comment) - } - })); - (doc, comments, ends_with_hardline) + }; + doc = append(doc, x); + } + + (comments_before, doc, comments_after) } } + +#[derive(Debug)] +enum CommentLike { + Block, + Line, + Empty, +} diff --git a/doc/Cargo.toml b/doc/Cargo.toml index da14a16d6f..09da076cb8 100644 --- a/doc/Cargo.toml +++ b/doc/Cargo.toml @@ -21,7 +21,7 @@ itertools = "0.8" lazy_static = "1" log = "0.4" opener = "0.4" -pretty = "0.5" +pretty = "0.9" pulldown-cmark = "0.6" rayon = "1" regex = "1" diff --git a/doc/src/lib.rs b/doc/src/lib.rs index 24cada3606..d42c56cfd9 100644 --- a/doc/src/lib.rs +++ b/doc/src/lib.rs @@ -99,7 +99,7 @@ impl pretty::Render for SymbolLinkRenderer { } } -impl pretty::RenderAnnotated for SymbolLinkRenderer { +impl pretty::RenderAnnotated<'_, String> for SymbolLinkRenderer { fn push_annotation(&mut self, annotation: &String) -> StdResult<(), Self::Error> { self.flush_to_escaped(); self.escaped @@ -124,7 +124,7 @@ fn print_type(current_module: &str, typ: &ArcType) -> String { .pretty(&arena); match **typ { Type::Record(_) => (), - Type::Variant(_) => doc = arena.newline().append(doc).nest(4), + Type::Variant(_) => doc = arena.hardline().append(doc).nest(4), _ => { doc = doc.nest(4); } diff --git a/examples/24.glu b/examples/24.glu index 446847db73..a1ddfa4d94 100644 --- a/examples/24.glu +++ b/examples/24.glu @@ -44,8 +44,14 @@ let { (*>), (<*), wrap } = import! std.applicative let { for } = import! std.traversable -type Op = | Add | Sub | Div | Mul -type Expr = | Int Int | Binop Expr Op Expr +type Op = + | Add + | Sub + | Div + | Mul +type Expr = + | Int Int + | Binop Expr Op Expr let parse : String -> Result String Expr = // Gluon has a small parser combinator library which makes it easy to define an expression parser @@ -61,7 +67,9 @@ let parse : String -> Result String Expr = lazy_parser, chainl1, (), - ? } = import! std.parser + ? + } = + import! std.parser let { (<|>) } = import! std.alternative let lex x = x <* spaces @@ -74,13 +82,14 @@ let parse : String -> Result String Expr = | Err _ -> parser.fail "Unable to parse integer" let operator = - satisfy_map (\c -> - match c with - | '*' -> Some Mul - | '+' -> Some Add - | '-' -> Some Sub - | '/' -> Some Div - | _ -> None) + satisfy_map + (\c -> + match c with + | '*' -> Some Mul + | '+' -> Some Add + | '-' -> Some Sub + | '/' -> Some Div + | _ -> None) "operator" rec @@ -96,9 +105,6 @@ let parse : String -> Result String Expr = let expr _ = binop () in - - // Gluon makes it possible to partially apply functions which we use here to scope all parser functions - // inside the `let parse` binding above. let parse : String -> Result String Expr = parser.parse (expr () <* spaces) parse @@ -126,7 +132,9 @@ let eval expr : Expr -> Int = | Mul -> (*) f (eval l) (eval r) -do digits = +do +digits += let gen_digit = random.thread_rng.gen_int_range 1 10 do a = gen_digit do b = gen_digit @@ -134,7 +142,9 @@ do digits = do d = gen_digit wrap [a, b, c, d] -let print_digits = for digits (\d -> +let print_digits = for + digits + (\d -> seq io.print " " io.print (show d)) seq io.print "Four digits:" *> print_digits *> io.println "" @@ -150,8 +160,7 @@ let guess_loop _ = | Ok expr -> if validate digits expr then let result = eval expr - if result == 24 - then io.println "Correct!" + if result == 24 then io.println "Correct!" else io.println ("Incorrect, " <> int.show.show result <> " != 24") *> guess_loop () else io.println diff --git a/format/src/pretty_print.rs b/format/src/pretty_print.rs index 1f2ea983e8..d77d0eba6b 100644 --- a/format/src/pretty_print.rs +++ b/format/src/pretty_print.rs @@ -1,23 +1,27 @@ use std::{iter, ops}; -use codespan::{ByteOffset, RawOffset}; -use itertools::{Either, Itertools}; -use pretty::{Arena, Doc, DocAllocator, DocBuilder}; +use { + codespan::{ByteOffset, RawOffset}, + itertools::{Either, Itertools}, + pretty::{Arena, Doc, DocAllocator, DocBuilder}, +}; use self::types::pretty_print as pretty_types; -use base::ast::{ - Do, Expr, Literal, Pattern, PatternField, SpannedExpr, SpannedPattern, ValueBinding, - ValueBindings, +use base::{ + ast::{ + Do, Expr, Literal, Pattern, PatternField, SpannedExpr, SpannedPattern, ValueBinding, + ValueBindings, + }, + kind::Kind, + metadata::Attribute, + pos::{self, BytePos, HasSpan, Span, Spanned}, + source, + types::{self, ArgType, Prec, Type}, }; -use base::kind::Kind; -use base::metadata::Attribute; -use base::pos::{self, BytePos, HasSpan, Span, Spanned}; -use base::source; -use base::types::{self, ArgType, Prec, Type}; const INDENT: isize = 4; -macro_rules! hardlines_iter { +macro_rules! newlines_iter { ($self_:ident, $iterable:expr) => { $iterable .into_iter() @@ -26,7 +30,7 @@ macro_rules! hardlines_iter { }; } -macro_rules! rev_hardlines_iter { +macro_rules! rev_newlines_iter { ($self_:ident, $iterable:expr) => { $iterable .into_iter() @@ -35,14 +39,6 @@ macro_rules! rev_hardlines_iter { }; } -fn is_hardline<'a, A>(doc: &DocBuilder<'a, Arena<'a, A>, A>) -> bool { - if let Doc::BorrowedText("\n") = *doc.1 { - true - } else { - false - } -} - fn is_nil<'a, A>(doc: &DocBuilder<'a, Arena<'a, A>, A>) -> bool { if let Doc::Nil = *doc.1 { true @@ -51,6 +47,10 @@ fn is_nil<'a, A>(doc: &DocBuilder<'a, Arena<'a, A>, A>) -> bool { } } +fn trailing_comma<'a, A>(arena: &'a Arena<'a, A>) -> DocBuilder<'a, Arena<'a, A>, A> { + arena.text(",").flat_alt(arena.nil()) +} + pub(super) struct Printer<'a, I: 'a, A: 'a> { printer: pretty_types::Printer<'a, I, A>, formatter: crate::Formatter, @@ -58,7 +58,9 @@ pub(super) struct Printer<'a, I: 'a, A: 'a> { impl<'a, I, A> Printer<'a, I, A> where - I: AsRef, + I: AsRef + std::fmt::Debug + 'a, + A: std::fmt::Debug, + A: 'a, { pub(super) fn new( arena: &'a Arena<'a, A>, @@ -75,9 +77,8 @@ where where A: Clone, { - self.pretty_expr(expr) - .1 - .pretty(width) + let doc = self.pretty_expr(expr).1; + doc.pretty(width) .to_string() .lines() .map(|s| format!("{}{}", s.trim_end(), hardline)) @@ -88,8 +89,10 @@ where where A: Clone, { - self.pretty_expr_with_shebang_line(expr) - .append(self.comments(Span::new(expr.span.end(), self.source.span().end()))) + let e = self + .pretty_expr_with_shebang_line(expr) + .append(self.comments(Span::new(expr.span.end(), self.source.span().end()))); + e } fn pretty_expr_with_shebang_line( @@ -131,7 +134,7 @@ where while expr.span.start() == 0.into() { expr = match expr.value { Expr::TypeBindings(_, ref expr) | Expr::LetBindings(_, ref expr) => expr, - _ => expr, + _ => break, }; } } @@ -140,7 +143,8 @@ where let pretty = |next: &'a SpannedExpr<_>| self.pretty_expr_(next.span.start(), next); - let comments = self.comments(Span::new(previous_end, expr.span.start())); + let span = Span::new(previous_end, expr.span.start()); + let comments = self.comments(span); let doc = match expr.value { Expr::App { ref implicit_args, @@ -173,7 +177,7 @@ where .exprs .iter() .map(|elem| pretty(elem)) - .intersperse(arena.text(",").append(arena.space())), + .intersperse(arena.text(",").append(arena.line())), ), ) .append("]") @@ -201,21 +205,7 @@ where Expr::Ident(ref id) => pretty_types::ident(arena, id.name.as_ref()), - Expr::IfElse(ref body, ref if_true, ref if_false) => { - let space = hardline(arena, expr); - chain![arena; - chain![arena; - "if ", - pretty(body), - arena.space(), - "then" - ].group(), - space.clone().append(pretty(if_true)).nest(INDENT).group(), - space.clone(), - "else", - self.pretty_else_expr(space, if_false) - ] - } + Expr::IfElse(..) => self.pretty_if_expr(expr), Expr::Infix { ref lhs, @@ -230,12 +220,8 @@ where " ", pretty(rhs).group() ].nest(INDENT) - ], - - Expr::Lambda(_) => { - let (arguments, body) = self.pretty_lambda(previous_end, expr); - arguments.group().append(body) - } + ] + .group(), Expr::LetBindings(ref binds, ref body) => { let binding = |bind: &'a ValueBinding| { @@ -258,17 +244,20 @@ where ].group(), match bind.typ { None => arena.nil(), - Some(ref typ) => arena.text(": ") - .append(types::pretty_print(self, typ)) - .append(self.space_after(typ.span().end())) - .nest(INDENT), + Some(ref typ) => { + arena.text(": ") + .append(types::pretty_print(self, typ)) + .append(self.space_after(typ.span().end())) + .nest(INDENT) + }, }, "=" - ]; + ] + .group(); chain![arena; pretty_types::doc_comment(arena, bind.metadata.comment.as_ref()), self.pretty_attributes(&bind.metadata.attributes), - self.hang(decl, &bind.expr).group(), + self.hang(decl, (self.space_before(bind.expr.span.start()), true), &bind.expr).group(), if self.formatter.expanded { arena.hardline() } else { @@ -282,14 +271,14 @@ where }; chain![arena; if is_recursive { - arena.text("rec").append(if binds.len() == 1 { arena.space() } else { arena.hardline() }) + arena.text("rec").append(if binds.len() == 1 { arena.softline() } else { arena.hardline() }) } else { arena.nil() }, arena.concat( binds.iter() .map(|bind| binding(bind)) - .interleave(hardlines_iter!(self, binds.iter().map(|bind| bind.span()))) + .interleave(newlines_iter!(self, binds.iter().map(|bind| bind.span()))) ), if is_recursive { match body.value { @@ -344,7 +333,7 @@ where "| ", self.pretty_pattern(&alt.pattern), " ->", - self.hang(arena.nil(), &alt.expr).group() + self.hang(arena.nil(), (self.space_before(alt.expr.span.start()), true), &alt.expr).group() ] }).intersperse(arena.hardline())) ], @@ -355,33 +344,8 @@ where pretty_types::ident(arena, field.as_ref()) ], - Expr::Record { .. } => { - let (x, y) = self.pretty_lambda(previous_end, expr); - x.append(y).group() - } - - Expr::Tuple { ref elems, .. } => { - let inner = chain![arena; - arena.softline(), - arena.concat( - self.comma_sep_paren( - elems - .iter() - .map(|elem| pos::spanned(elem.span, pretty(elem))), - |spanned| spanned.value, - ), - ) - ]; - chain![arena; - "(", - if elems.len() == 1 { - inner - } else { - inner.nest(INDENT).append(arena.softline()) - }, - ")" - ] - .group() + Expr::Record { .. } | Expr::Tuple { .. } | Expr::Lambda(_) => { + self.hang(arena.nil(), (arena.nil(), false), expr) } Expr::TypeBindings(ref binds, ref body) => { @@ -398,7 +362,7 @@ where self.pretty_attributes(&binds.first().unwrap().metadata.attributes), if is_recursive && binds.len() == 1 { - arena.text("rec").append(arena.space()) + arena.text("rec").append(arena.line()) } else { arena.nil() }, @@ -415,6 +379,14 @@ where Type::Record(_) | Type::Variant(_) => (), _ => type_doc = type_doc.nest(INDENT), } + let variant = + match &**typ { + Type::Variant(row) => match &**row { + Type::ExtendRow { fields, .. } => !fields.is_empty(), + _ => false, + } + _ => false, + }; chain![arena; if i != 0 { chain![arena; @@ -436,33 +408,30 @@ where "(", arg.id.as_ref(), " :", - arena.space(), + arena.line(), pretty_kind(arena, Prec::Top, &arg.kind).group(), ")" ].group() } else { arena.text(arg.id.as_ref()) }, - arena.space() + arena.line() ] })).group(), - match **typ { - Type::Variant(_) => { - chain![arena; - "=", - arena.hardline(), - type_doc - ].nest(INDENT) - } - _ => { - chain![arena; - "= ", - type_doc - ].group() - } + "=", + if variant { + chain![arena; + arena.hardline(), + type_doc + ].nest(INDENT) + } else { + chain![arena; + arena.space(), + type_doc + ].group() } ].group() - }).interleave(hardlines_iter!(self, binds.iter().map(|bind| bind.span())))), + }).interleave(newlines_iter!(self, binds.iter().map(|bind| bind.span())))), if is_recursive { arena.hardline().append(arena.text("in")) } else { @@ -482,31 +451,31 @@ where ref bound, ref body, .. - }) => chain![arena; + }) => { + let from = match id { + Some(pattern) => chain![arena; + "do", + self.space_before(pattern.span.start()), + self.pretty_pattern(pattern), + self.space_after(pattern.span.end()), + "=" + ], + None => arena.text("seq"), + }; chain![arena; - match id { - Some(pattern) => chain![arena; - "do", - self.space_before(pattern.span.start()), - self.pretty_pattern(pattern), - self.space_after(pattern.span.end()), - "=" - ], - None => arena.text("seq"), - }, - self.hang(arena.nil(), bound).group() - ].group(), - self.pretty_expr_(bound.span.end(), body) - ], + self.hang(from, (self.space_before(bound.span.start()), true), bound), + self.pretty_expr_(bound.span.end(), body) + ] + } Expr::MacroExpansion { ref original, .. } => { return self.pretty_expr_(previous_end, original); } Expr::Annotated(ref expr, ref typ) => chain![ arena; pretty(expr).group(), - arena.space(), + arena.line(), ":", - arena.space(), + arena.line(), types::pretty_print(self, typ) ], Expr::Error(_) => arena.text(""), @@ -515,7 +484,7 @@ where } fn space(&self, span: Span) -> DocBuilder<'a, Arena<'a, A>, A> { - self.whitespace(span, self.arena.space()) + self.whitespace(span, self.arena.line()) } fn whitespace( @@ -523,50 +492,56 @@ where span: Span, default: DocBuilder<'a, Arena<'a, A>, A>, ) -> DocBuilder<'a, Arena<'a, A>, A> { - let arena = self.arena; - let (doc, count, ends_with_hardline) = self.comments_count(span); + let (doc, _) = self.comments_count(span); if let Doc::Nil = *doc.1 { default - } else if count == 0 { - // No comments, only hardlines from the iterator - doc - } else if ends_with_hardline { - arena.space().append(doc) } else { - arena.space().append(doc).append(arena.space()) + doc } } - fn pretty_else_expr( - &self, - space: DocBuilder<'a, Arena<'a, A>, A>, - if_false: &'a SpannedExpr, - ) -> DocBuilder<'a, Arena<'a, A>, A> + fn pretty_if_expr(&self, mut expr: &'a SpannedExpr) -> DocBuilder<'a, Arena<'a, A>, A> where A: Clone, { let pretty = |next: &'a SpannedExpr<_>| self.pretty_expr_(next.span.start(), next); let arena = self.arena; - match if_false.value { - Expr::IfElse(ref body, ref if_true, ref if_false) => chain![arena; + + let mut doc = arena.nil(); + let mut prefix: Option, A>> = None; + while let Expr::IfElse(body, if_true, if_false) = &expr.value { + let next = chain![arena; chain![arena; - " if ", + prefix.map(|prefix| prefix.append(" ")).unwrap_or_else(|| arena.nil()), + "if ", pretty(body), - arena.space(), + arena.line(), "then" ].group(), - space.clone().append(pretty(if_true)).nest(INDENT).group(), - space.clone(), - "else", - self.pretty_else_expr(space, if_false) - ], - _ => space.append(pretty(if_false)).nest(INDENT).group(), + arena.line().append(pretty(if_true)).nest(INDENT).group(), + ] + .group(); + doc = doc.append(next).append(arena.line()); + prefix = Some(arena.text("else")); + expr = if_false; } + chain![arena; + doc, + chain![arena; + prefix.unwrap(), + arena.line(), + pretty(expr), + ].nest(INDENT).group(), + ] } fn pretty_lambda( &self, - previous_end: BytePos, + decls: &mut Vec<( + (DocBuilder<'a, Arena<'a, A>, A>, bool), + DocBuilder<'a, Arena<'a, A>, A>, + )>, + body_spacing: (DocBuilder<'a, Arena<'a, A>, A>, bool), expr: &'a SpannedExpr, ) -> ( DocBuilder<'a, Arena<'a, A>, A>, @@ -578,21 +553,20 @@ where let arena = self.arena; match expr.value { Expr::Lambda(ref lambda) => { - let decl = chain![arena; + let from = chain![arena; "\\", arena.concat(lambda.args.iter().map(|arg| { arena.text(arg.name.value.name.as_ref()).append(" ") })), "->" ]; - let (next_lambda, body) = - self.pretty_lambda(lambda.body.span.start(), &lambda.body); - if let Doc::Nil = *next_lambda.1 { - let decl = decl.append(self.space_before(lambda.body.span.start())); - (decl, body) - } else { - (decl.append(arena.space()).append(next_lambda), body) - } + decls.push((body_spacing, from)); + let (body, trailer) = self.pretty_lambda( + decls, + (self.space_before(lambda.body.span.start()), true), + &lambda.body, + ); + (body, trailer) } Expr::Record { ref types, @@ -600,6 +574,9 @@ where ref base, .. } => { + decls.push((body_spacing, arena.text("{"))); + decls.push(((arena.nil(), false), arena.nil())); + let ordered_iter = || expr.value.field_iter(); let spans = || { ordered_iter().map(|x| { @@ -618,26 +595,26 @@ where }; // Preserve comments before and after the comma - let hardlines = hardlines_iter!(self, spans()) - .zip(rev_hardlines_iter!(self, spans())) + let newlines = newlines_iter!(self, spans()) + .zip(rev_newlines_iter!(self, spans())) .collect::>(); let mut line = hardline(arena, expr); // If there are any explicit line breaks then we need put each field on a separate // line - let hardline_in_fields = hardlines + let newline_in_fields = newlines .iter() .any(|&(ref l, ref r)| !is_nil(l) || !is_nil(r)); - let hardline_from_doc_comment = expr.value.field_iter().any(|either| { + let newline_from_doc_comment = expr.value.field_iter().any(|either| { either .either(|f| &f.metadata, |f| &f.metadata) .comment .is_some() }); - let hardline_in_base = base + let newline_in_base = base .as_ref() .map_or(false, |base| !is_nil(&self.space_before(base.span.start()))); - if hardline_in_fields || hardline_in_base | hardline_from_doc_comment { + if newline_in_fields || newline_in_base | newline_from_doc_comment { line = arena.hardline(); } @@ -661,11 +638,11 @@ where match r.value { Some(ref expr) => { let x = chain![arena; - id, - self.space_after(r.name.span.end()), - "=" - ]; - self.hang(x, expr) + id, + self.space_after(r.name.span.end()), + "=" + ]; + self.hang(x, (self.space_before(expr.span.start()), true), expr).group() } None => id, } @@ -675,13 +652,11 @@ where }), |spanned| spanned.value, )) - .append( - if (!exprs.is_empty() || !types.is_empty()) && is_hardline(&line) { - arena.text(",") - } else { - arena.nil() - }, - ) + .append(if !types.is_empty() || !exprs.is_empty() { + trailing_comma(arena) + } else { + arena.nil() + }) .append(match *base { Some(ref base) => { let comments = self.comments_after(last_field_end); @@ -702,11 +677,68 @@ where .append( self.whitespace(Span::new(last_element_end, expr.span.end()), line.clone()), ) - .group() - .append("}"); - (arena.text("{"), record) + .group(); + + (record, arena.text("}")) + } + + Expr::Tuple { + elems: [ref inner], .. + } => { + let decl = arena.text("("); + + decls.push((body_spacing, decl)); + + let (body, end) = self.pretty_lambda( + decls, + (self.comments_before(inner.span.start()), true), + inner, + ); + + (body, end.append(")")) + } + + Expr::Tuple { ref elems, .. } => { + let pretty = |next: &'a SpannedExpr<_>| self.pretty_expr_(next.span.start(), next); + + decls.push((body_spacing, arena.text("("))); + decls.push(((arena.nil(), true), arena.nil())); + + let inner = arena.concat( + self.comma_sep_paren( + elems + .iter() + .map(|elem| pos::spanned(elem.span, pretty(elem))), + |spanned| spanned.value, + ), + ); + let tuple = chain![arena; + self.nilline_after(expr.span.start() + ByteOffset::from(1)), + inner, + trailing_comma(arena), + ] + .group(); + + let end = if elems.is_empty() { + arena.nil() + } else { + self.nilline_before(expr.span.end() - ByteOffset::from(1)) + }; + (tuple, end.append(arena.text(")"))) + } + + _ => { + let body_spacing = match &*(body_spacing.0).1 { + Doc::Nil if forced_new_line(expr) => (arena.hardline(), body_spacing.1), + Doc::FlatAlt( + pretty::RefDoc(Doc::Line), + pretty::RefDoc(Doc::BorrowedText(" ")), + ) if forced_new_line(expr) => (arena.hardline(), body_spacing.1), + _ => body_spacing, + }; + decls.push((body_spacing, arena.nil())); + (self.pretty_expr_(expr.span.start(), expr), arena.nil()) } - _ => (arena.nil(), self.pretty_expr_(previous_end, expr)), } } @@ -829,7 +861,7 @@ where if fields.is_empty() && implicit_import.is_none() { arena.nil() } else { - arena.space() + arena.line() }, "}" ] @@ -854,66 +886,86 @@ where fn hang( &self, from: DocBuilder<'a, Arena<'a, A>, A>, + body_spacing: (DocBuilder<'a, Arena<'a, A>, A>, bool), expr: &'a SpannedExpr, ) -> DocBuilder<'a, Arena<'a, A>, A> where A: Clone, { + let mut decls = Vec::new(); + decls.push(((self.arena.nil(), false), from)); + + let (body, end) = self.pretty_lambda(&mut decls, body_spacing, expr); + + self.hang_parts(&decls, body, end).group() + } + + fn hang_parts( + &self, + from: &[( + (DocBuilder<'a, Arena<'a, A>, A>, bool), + DocBuilder<'a, Arena<'a, A>, A>, + )], + body: DocBuilder<'a, Arena<'a, A>, A>, + trailer: DocBuilder<'a, Arena<'a, A>, A>, + ) -> DocBuilder<'a, Arena<'a, A>, A> + where + A: Clone, + { + assert!(from.len() >= 1); let arena = self.arena; - let (arguments, body) = self.pretty_lambda(expr.span.start(), expr); - match expr.value { - Expr::Record { .. } => { - let opening = arguments; - let spaces = self.space_before(expr.span.start()); - // If there are just spaces between `=`/`->` and the opening brace (`{`) we hang - // the opening brace next to the `=` and only indent the body of the record - // let x = { - // y, - // } - // Instead of - // let x = - // { - // y, - // } - let needs_indent = if let Doc::BorrowedText(" ") = *spaces.1 { - false - } else { - true - }; - let doc = chain![arena; - chain![arena; - from, - spaces, - opening - ].group(), - body - ] - .group(); - if needs_indent { - doc.nest(INDENT) - } else { - doc - } - } - _ => { - let mut spaces = self.space_before(expr.span.start()); - if let Doc::BorrowedText(" ") = *spaces.1 { - match expr.value { - Expr::Lambda(..) => (), - _ => spaces = hardline(arena, expr), + + let mut iter = (1..from.len()).filter_map(|i| { + let (before, after) = from.split_at(i); + + let before = before + .iter() + .rev() + .cloned() + .fold(arena.nil(), |next, ((body_spacing, _), from)| { + body_spacing.append(from).append(next).group() + }); + + let after = after.iter().rev().cloned().fold( + body.clone(), + |next, ((body_spacing, nest), from)| { + let doc = body_spacing.append(from).append(next); + if nest { + doc.nest(INDENT) + } else { + doc } - } + }, + ); - from.append( - chain![arena; - spaces, - arguments - ] - .group() - .append(body) - .nest(INDENT), - ) + Some( + if i != 1 { + arena + .text( + (0..20) + .map(|_| format!(" {}", i)) + .collect::(), + ) + // .flat_alt(arena.as_string(i)) // FIXME + .flat_alt(arena.nil()) + } else { + // The last one should always apply + arena.nil() + } + .append(before) .group() + .append(after) + .append(trailer.clone()) + .group(), + ) + }); + + match iter.next() { + None => unreachable!(), + Some(first) => { + let x = iter.fold(first, |a, b| b.union(a)); + + x } } } @@ -989,10 +1041,11 @@ where impl<'a, 'e, F, I, J, T, U, A> Iterator for CommaSeparated<'a, 'e, F, I, J, U, A> where - I: AsRef, + I: AsRef + std::fmt::Debug, F: FnMut(T) -> DocBuilder<'a, Arena<'a, A>, A>, J: Iterator, T: ::std::borrow::Borrow>, + A: std::fmt::Debug, { type Item = DocBuilder<'a, Arena<'a, A>, A>; @@ -1035,7 +1088,7 @@ fn pretty_kind<'a, A>( Kind::Function(ref a, ref r) => { let doc = chain![arena; pretty_kind(arena, Prec::Function, a), - arena.space(), + arena.line(), "-> ", pretty_kind(arena, Prec::Top, r) ]; @@ -1051,7 +1104,7 @@ fn hardline<'a, Id, A>( if forced_new_line(expr) { arena.hardline() } else { - arena.space() + arena.line() } } diff --git a/format/tests/pretty_print.rs b/format/tests/pretty_print.rs index dcb814f6ff..3d5cedf81d 100644 --- a/format/tests/pretty_print.rs +++ b/format/tests/pretty_print.rs @@ -1,15 +1,10 @@ -#[macro_use(assert_diff)] -extern crate difference; #[macro_use] extern crate pretty_assertions; extern crate gluon_base as base; extern crate gluon_format as format; -use std::env; -use std::fs::File; -use std::io::{Read, Write}; -use std::path::Path; +use difference::assert_diff; use gluon::{RootedThread, ThreadExt, VmBuilder}; @@ -29,130 +24,6 @@ fn format_expr_expanded(expr: &str) -> gluon::Result { thread.format_expr(&mut format::Formatter { expanded: true }, "test", expr) } -fn test_format(filename: &str) { - let _ = env_logger::try_init(); - - let mut contents = String::new(); - File::open(Path::new("../").join(filename)) - .or_else(|_| File::open(filename)) - .unwrap() - .read_to_string(&mut contents) - .unwrap(); - - let name = base::filename_to_module(filename); - - let thread = new_vm(); - let out_str = thread - .format_expr(&mut format::Formatter::default(), &name, &contents) - .unwrap_or_else(|err| panic!("{}", err)); - - if contents != out_str { - let args: Vec<_> = env::args().collect(); - let out_path = Path::new(&args[0][..]) - .parent() - .and_then(|p| p.parent()) - .expect("folder") - .join(Path::new(filename).file_name().unwrap()); - File::create(out_path) - .unwrap() - .write_all(out_str.as_bytes()) - .unwrap(); - - assert_diff!(&contents, &out_str, " ", 0); - } -} - -#[test] -fn bool() { - test_format("std/bool.glu"); -} - -#[test] -fn char() { - test_format("std/char.glu"); -} - -#[test] -fn function() { - test_format("std/function.glu"); -} - -#[test] -fn map() { - test_format("std/map.glu"); -} - -#[test] -fn option() { - test_format("std/option.glu"); -} - -#[test] -fn prelude() { - test_format("std/prelude.glu"); -} - -#[test] -fn result() { - test_format("std/result.glu"); -} - -#[test] -fn state() { - test_format("std/state.glu"); -} - -#[test] -fn stream() { - test_format("std/stream.glu"); -} - -#[test] -fn string() { - test_format("std/string.glu"); -} - -#[ignore] -#[test] -fn test() { - test_format("std/test.glu"); -} - -#[test] -fn types() { - test_format("std/types.glu"); -} - -#[test] -fn unit() { - test_format("std/unit.glu"); -} - -#[test] -fn writer() { - test_format("std/writer.glu"); -} - -#[test] -fn parser() { - test_format("std/parser.glu"); -} - -#[test] -fn random() { - test_format("std/random.glu"); -} - -#[test] -fn repl() { - test_format("repl/src/repl.glu"); -} - -#[test] -fn json_de() { - test_format("std/json/de.glu"); -} - #[test] fn dont_add_newline_for_let_literal() { let expr = r#" @@ -190,13 +61,15 @@ fn long_tuple() { let expr = r#" (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb) "#; - assert_eq!(&format_expr(expr).unwrap(), -r#" + assert_eq!( + &format_expr(expr).unwrap(), + r#" ( aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, - bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, ) -"#); +"# + ); } #[test] @@ -218,7 +91,7 @@ type Test = Int 1 // test4 "#; - assert_diff!(&format_expr(expr).unwrap(), expr, " ", 0); + assert_diff!(&format_expr(expr).unwrap(), expr, "\n", 0); } #[test] @@ -231,7 +104,7 @@ fn preserve_whitespace_in_record() { bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbby = 2, } "#; - assert_diff!(&format_expr(expr).unwrap(), expr, " ", 0); + assert_diff!(&format_expr(expr).unwrap(), expr, "\n", 0); } #[test] @@ -286,18 +159,25 @@ let { test 123 "#; - assert_eq!(&format_expr(expr).unwrap(), expr); + assert_diff!(&format_expr(expr).unwrap(), expr, "\n", 0); } #[test] fn preserve_comments_in_function_types() { let expr = r#"#!/bin/gluon -let x : /* first */ Int /* Int */ -> +let x : + /* first */ + Int + /* Int */ + -> // Float - Float /* last */ = () + Float + /* last */ + = + () x "#; - assert_diff!(&format_expr(expr).unwrap(), expr, " ", 0); + assert_diff!(&format_expr(expr).unwrap(), expr, "\n", 0); } #[test] @@ -308,7 +188,18 @@ let x : Test /* first */ Int Float /* last */ = () x "#; - assert_diff!(&format_expr(expr).unwrap(), expr, " ", 0); + let expected = r#"#!/bin/gluon +let x : Test + /* first */ + Int + // middle + Float + /* last */ + = + () +x +"#; + assert_diff!(&format_expr(expr).unwrap(), expected, "\n", 0); } #[test] @@ -324,7 +215,7 @@ type Test = { } x "#; - assert_diff!(&format_expr(expr).unwrap(), expr, " ", 0); + assert_diff!(&format_expr(expr).unwrap(), expr, "\n", 0); } #[test] @@ -336,7 +227,7 @@ fn doc_comment_in_record_expr() { field1 = 1, } "#; - assert_diff!(&format_expr(expr).unwrap(), expr, " ", 0); + assert_diff!(&format_expr(expr).unwrap(), expr, "\n", 0); } #[test] @@ -346,7 +237,7 @@ fn preserve_comments_in_empty_record() { // 123 } "#; - assert_diff!(&format_expr(expr).unwrap(), expr, " ", 0); + assert_diff!(&format_expr(expr).unwrap(), expr, "\n", 0); } #[test] @@ -360,7 +251,15 @@ fn preserve_comments_in_record_base() { /* x */ } "#; - assert_diff!(&format_expr(expr).unwrap(), expr, " ", 0); + assert_diff!(&format_expr(expr).unwrap(), expr, "\n", 0); +} + +#[test] +fn starting_comment() { + let expr = r#"// 123 +() +"#; + assert_diff!(&format_expr(expr).unwrap(), expr, "\n", 0); } #[test] @@ -370,7 +269,12 @@ let semigroup = { append } () "#; - assert_diff!(&format_expr(expr).unwrap(), expr, " ", 0); + let format = r#" +let semigroup = + { append } +() +"#; + assert_diff!(&format_expr(expr).unwrap(), format, "\n", 0); } #[test] @@ -380,31 +284,61 @@ do /* x1 */ x /* x2 */ = Some 1 // test test abc 1232 "" "#; - assert_diff!(&format_expr(expr).unwrap(), expr, " ", 0); + assert_diff!(&format_expr(expr).unwrap(), expr, "\n", 0); } #[test] -fn if_else_multiple() { +fn hang_lambda_arg() { + let expr = r#" +function + (\arg -> + let x = 1 + x) +"#; + assert_diff!(&format_expr(expr).unwrap(), expr, "\n", 0); +} + +#[test] +fn hang_record() { + let expr = r#" +let x = { + // abc + field = 1, +} +() +"#; + assert_diff!(&format_expr(expr).unwrap(), expr, "\n", 0); +} + +#[test] +fn if_else_basic() { + let expr = r#" +if x then y +else 0 +"#; + assert_diff!(&format_expr(expr).unwrap(), expr, "\n", 0); +} + +#[test] +fn if_else_multiple_basic() { let expr = r#" if x then y else if z then w else 0 "#; - assert_diff!(&format_expr(expr).unwrap(), expr, " ", 0); + assert_diff!(&format_expr(expr).unwrap(), expr, "\n", 0); } #[test] fn if_else_multiple_let_multiline_1() { let expr = r#" -if x then - f 123 483 -else if z then - "12312" +if x then f 123 483 +else if z then "12312" else do x = 1 x "#; - assert_diff!(&format_expr(expr).unwrap(), expr, " ", 0); + assert_diff!(&format_expr(expr).unwrap(), expr, "\n", 0); } #[test] @@ -420,7 +354,7 @@ else let x = 1 x "#; - assert_diff!(&format_expr(expr).unwrap(), expr, " ", 0); + assert_diff!(&format_expr(expr).unwrap(), expr, "\n", 0); } #[test] @@ -430,13 +364,13 @@ let traverse_with_key f m x : [Ord k] -> Applicative t -> (k -> a -> t b) -> Map k a - -> () + -> LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONG -> () = () () "#; - assert_diff!(&format_expr(expr).unwrap(), expr, " ", 0); + assert_diff!(&format_expr(expr).unwrap(), expr, "\n", 0); } #[test] @@ -452,7 +386,7 @@ test 123 abc "" // test2 "#; - assert_diff!(&format_expr(expr).unwrap(), expr, " ", 0); + assert_diff!(&format_expr(expr).unwrap(), expr, "\n", 0); } #[test] @@ -467,7 +401,7 @@ type Handler a = -> IO Response () "#; - assert_diff!(&format_expr(expr).unwrap(), expr, " ", 0); + assert_diff!(&format_expr(expr).unwrap(), expr, "\n", 0); } #[test] @@ -481,7 +415,7 @@ type TestCase a = assert_diff!( &format_expr(expr).unwrap_or_else(|err| panic!("{}", err)), expr, - " ", + "\n", 0 ); } @@ -494,7 +428,7 @@ let x = "abc " x "#; - assert_diff!(&format_expr(expr).unwrap(), expr, " ", 0); + assert_diff!(&format_expr(expr).unwrap(), expr, "\n", 0); } #[test] @@ -505,7 +439,7 @@ type Test = | Test Test "#; - assert_diff!(&format_expr(expr).unwrap(), expr, " ", 0); + assert_diff!(&format_expr(expr).unwrap(), expr, "\n", 0); } #[test] @@ -516,7 +450,7 @@ type Test a = | Test a Test 1 "#; - assert_diff!(&format_expr(expr).unwrap(), expr, " ", 0); + assert_diff!(&format_expr(expr).unwrap(), expr, "\n", 0); } #[test] @@ -665,9 +599,8 @@ rec let deserialize_Record : Deserialize Record = let { map } = import! std.functor let { (<*>) } = import! std.applicative let { (<|>) } = import! std.alternative - let deserializer : ValueDeserializer Record - = map (\x y -> { x, y }) (field "x" deserializer) - <*> field "y" deserializer + let deserializer : ValueDeserializer Record = + map (\x y -> { x, y }) (field "x" deserializer) <*> field "y" deserializer { deserializer = deserializer } () "#; @@ -728,3 +661,110 @@ rec let serialize_Variant : Serialize Variant = "#; assert_diff!(&format_expr_expanded(expr).unwrap(), expected, "\n", 0); } + +#[test] +fn let_lambda() { + let expr = r#" +let flat_map_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA = \state -> + let PATTERN_BINDING = BBBBBBB + CCCCC + +{ } +"#; + assert_diff!(&format_expr(expr).unwrap(), expr, "\n", 0); +} + +#[test] +fn let_tuple() { + let expr = r#" +let flat_map_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA = (BBBBBBBBBBBBBBBBBBBBB, CCCCCCCCCCCCCCCCCCC) + +1 +"#; + let expected = r#" +let flat_map_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA = ( + BBBBBBBBBBBBBBBBBBBBB, + CCCCCCCCCCCCCCCCCCC, +) + +1 +"#; + assert_diff!(&format_expr(expr).unwrap(), expected, "\n", 0); +} + +#[test] +fn let_app() { + let expr = r#" +let flat_map_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA = BBBBBBBBBBBBBBBBBBBBB CCCCCCCCCCCCCCCCCCC + +1 +"#; + let expected = r#" +let flat_map_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA = + BBBBBBBBBBBBBBBBBBBBB CCCCCCCCCCCCCCCCCCC + +1 +"#; + assert_diff!(&format_expr(expr).unwrap(), expected, "\n", 0); +} + +#[test] +fn function_type_dont_split_app() { + let expr = r#" +let run_interruptible_io : + IO String + -> LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONG + = + () + +{ } +"#; + assert_diff!(&format_expr(expr).unwrap(), expr, "\n", 0); +} + +#[test] +fn inside_parens_idempotent() { + let expr = r#" +let rest x = + ( + do f = op + rest) + <|> wrap + +{ } +"#; + assert_diff!(&format_expr(expr).unwrap(), expr, "\n", 0); +} + +#[test] +fn comment_in_lambda() { + let expr = r#" +(\settings -> + // Comment + { }) +"#; + assert_diff!(&format_expr(expr).unwrap(), expr, "\n", 0); +} + +#[test] +fn long_type() { + let expr = r#" +let assert_throws : forall e . + [Show a] -> Eff [| error : Error e, writer : Test | r |] a -> Eff [| writer : Test | r |] () + = + run_error >> flat_map assert_err + +1 +"#; + assert_diff!(&format_expr(expr).unwrap(), expr, "\n", 0); +} + +#[test] +fn open_variant() { + let expr = r#" +type OpenVariant r a = .. r + +1 +"#; + assert_diff!(&format_expr(expr).unwrap(), expr, "\n", 0); +} diff --git a/format/tests/std.rs b/format/tests/std.rs new file mode 100644 index 0000000000..ff5e967f46 --- /dev/null +++ b/format/tests/std.rs @@ -0,0 +1,140 @@ +extern crate gluon_base as base; +extern crate gluon_format as format; + +use std::{ + env, + fs::File, + io::{Read, Write}, + path::Path, +}; + +use difference::assert_diff; + +use gluon::{RootedThread, ThreadExt, VmBuilder}; + +fn new_vm() -> RootedThread { + VmBuilder::new() + .import_paths(Some(vec![".".into(), "..".into()])) + .build() +} + +fn test_format(name: &str) { + let _ = env_logger::try_init(); + + let mut contents = String::new(); + File::open(Path::new("../").join(name)) + .or_else(|_| File::open(name)) + .unwrap() + .read_to_string(&mut contents) + .unwrap(); + + let thread = new_vm(); + let out_str = thread + .format_expr(&mut format::Formatter::default(), name, &contents) + .unwrap_or_else(|err| panic!("{}", err)); + + if contents != out_str { + let args: Vec<_> = env::args().collect(); + let out_path = Path::new(&args[0][..]) + .parent() + .and_then(|p| p.parent()) + .expect("folder") + .join(Path::new(name).file_name().unwrap()); + File::create(out_path) + .unwrap() + .write_all(out_str.as_bytes()) + .unwrap(); + + assert_diff!(&contents, &out_str, "\n", 0); + } +} + +#[test] +fn bool() { + test_format("std/bool.glu"); +} + +#[test] +fn char() { + test_format("std/char.glu"); +} + +#[test] +fn function() { + test_format("std/function.glu"); +} + +#[test] +fn map() { + test_format("std/map.glu"); +} + +#[test] +fn option() { + test_format("std/option.glu"); +} + +#[test] +fn prelude() { + test_format("std/prelude.glu"); +} + +#[test] +fn result() { + test_format("std/result.glu"); +} + +#[test] +fn state() { + test_format("std/state.glu"); +} + +#[test] +fn stream() { + test_format("std/stream.glu"); +} + +#[test] +fn string() { + test_format("std/string.glu"); +} + +#[test] +fn test() { + test_format("std/test.glu"); +} + +#[test] +fn types() { + test_format("std/types.glu"); +} + +#[test] +fn unit() { + test_format("std/unit.glu"); +} + +#[test] +fn writer() { + test_format("std/writer.glu"); +} + +#[test] +fn parser() { + test_format("std/parser.glu"); +} + +#[test] +fn random() { + test_format("std/random.glu"); +} + +#[test] +fn repl() { + test_format("repl/src/repl.glu"); +} + +#[test] +fn json_de() { + test_format("std/json/de.glu"); +} diff --git a/parser/Cargo.toml b/parser/Cargo.toml index 302bdce50f..9fd8139247 100644 --- a/parser/Cargo.toml +++ b/parser/Cargo.toml @@ -23,7 +23,7 @@ itertools = "0.8" quick-error = "1.0.0" lalrpop-util = "0.17.2" log = "0.4" -pretty = "0.5" +pretty = "0.9" gluon_base = { path = "../base", version = "0.13.1" } # GLUON ordered-float = "1" codespan = "0.3" diff --git a/parser/src/grammar.lalrpop b/parser/src/grammar.lalrpop index dbaf01f2ec..767782386e 100644 --- a/parser/src/grammar.lalrpop +++ b/parser/src/grammar.lalrpop @@ -531,7 +531,7 @@ Type: AstType<'ast, Id> = { ArgType: (ArgType, AstType<'ast, Id>) = { => (ArgType::Explicit, <>), - "[" "]" => + "[" "]" => (ArgType::Implicit, AstType::new(arena, pos::spanned2(l, r, typ))), }; diff --git a/repl/src/main.rs b/repl/src/main.rs index 59211e5a0d..8a4b11465f 100644 --- a/repl/src/main.rs +++ b/repl/src/main.rs @@ -286,7 +286,7 @@ async fn main() { Error::VM(VMError::Message(_)) => eprintln!("{}\n{}", err, vm.context().stacktrace(0)), _ => { let mut stderr = termcolor::StandardStream::stderr(color.into()); - if let Err(err) = err.emit(&mut stderr, &vm.get_database().code_map()) { + if let Err(err) = err.emit(&mut stderr) { eprintln!("{}", err); } else { eprintln!(""); diff --git a/repl/src/repl.glu b/repl/src/repl.glu index b4852f16d8..8c8da79fd7 100644 --- a/repl/src/repl.glu +++ b/repl/src/repl.glu @@ -80,7 +80,8 @@ let commands : Commands = | Err x -> io.println x let commands = ref [] - let cmds : Array Cmd = [{ + let cmds : Array Cmd = + [{ name = "quit", alias = "q", info = "Quit the REPL", @@ -88,41 +89,41 @@ let commands : Commands = action = let x : a -> Eff (ReplEffect r) ReplAction = \_ -> wrap Quit x, - }, { + }, + { name = "type", alias = "t", info = "Prints the type with an expression", action = \arg -> - (lift (repl_prim.type_of_expr arg) >>= print_result) - *> wrap Continue, + (lift (repl_prim.type_of_expr arg) >>= print_result) *> wrap Continue, }, { name = "info", alias = "i", info = "Prints information about the given name", action - = \arg -> - (lift (repl_prim.find_info arg) >>= print_result) - *> wrap Continue, + = + \arg -> + (lift (repl_prim.find_info arg) >>= print_result) *> wrap Continue, }, { name = "kind", alias = "k", info = "Prints the kind with the given type", action - = \arg -> - (lift (repl_prim.find_kind arg) >>= print_result) - *> wrap Continue, + = + \arg -> + (lift (repl_prim.find_kind arg) >>= print_result) *> wrap Continue, }, { name = "load", alias = "l", info = "Loads the file at \'folder/module.ext\' and stores it at \'module\'", action - = \arg -> - (load_file arg >>= io.println) - *> wrap Continue, + = + \arg -> + (load_file arg >>= io.println) *> wrap Continue, }, { name = "script", @@ -135,12 +136,16 @@ let commands : Commands = alias = "p", info = "Sets the prompt", action = \prompt -> - seq modify (\settings -> - // TODO Should be able infer this before the `..` splatting - let settings : Settings = settings - { prompt, - .. settings - }) + seq + modify + (\settings -> + // TODO Should be able infer this before the `..` splatting + let settings : Settings = settings + { + prompt, + .. + settings + }) wrap Continue, }, { @@ -151,12 +156,15 @@ let commands : Commands = seq match repl_prim.parse_color color with | Ok color -> - modify (\settings -> - // TODO Should be able infer this before the `..` splatting - let settings : Settings = settings - { color, - .. settings - }) + modify + (\settings -> + // TODO Should be able infer this before the `..` splatting + let settings : Settings = settings + { + color, + .. + settings + }) | Err msg -> io.println msg wrap Continue, }, @@ -165,9 +173,9 @@ let commands : Commands = alias = "d", info = "Switch debug level", action - = \arg -> - (lift (repl_prim.switch_debug_level arg) >>= print_result) - *> wrap Continue, + = + \arg -> + (lift (repl_prim.switch_debug_level arg) >>= print_result) *> wrap Continue, }, { name = "help", @@ -198,7 +206,9 @@ let cmd_parser : Parser { cmd : String, arg : String } = skip_many1, token, spaces, - letter } = import! std.parser + letter + } = + import! std.parser let { (<|>) } = import! std.alternative let word = recognize (skip_many1 letter) @@ -216,18 +226,15 @@ let do_command commands line : Commands -> String -> Eff (ReplEffect r) ReplActi | Some command -> command.action arg | None -> io.println ("Unknown command \'" ++ cmd ++ "\'") *> wrap Continue | Err err -> - io.println "Expected a command such as `:h`" - *> wrap Continue + io.println "Expected a command such as `:h`" *> wrap Continue let loop _ : () -> Eff (ReplEffect r) () = do repl = ask do settings = get let run_line line = - if string.is_empty (string.trim line) then - wrap Continue - else if string.starts_with line ":" then - do_command repl.commands line + if string.is_empty (string.trim line) then wrap Continue + else if string.starts_with line ":" then do_command repl.commands line else let action = do eval_thread = thread.new_thread () diff --git a/repl/src/repl.rs b/repl/src/repl.rs index 6ecabaef7c..d9c88ce48e 100644 --- a/repl/src/repl.rs +++ b/repl/src/repl.rs @@ -332,7 +332,7 @@ fn eval_line( Ok(x) => IO::Value(x), Err(err) => { let mut stderr = termcolor::StandardStream::stderr(color.into()); - if let Err(err) = err.emit(&mut stderr, &vm.get_database().code_map()) { + if let Err(err) = err.emit(&mut stderr) { eprintln!("{}", err); } IO::Value(()) @@ -632,9 +632,7 @@ pub async fn run( .use_standard_lib(use_std_lib) .run_io(true); - compile_repl(&vm) - .await - .map_err(|err| err.emit_string(&vm.get_database().code_map()).unwrap())?; + compile_repl(&vm).await?; let mut repl: OwnedFunction _> = vm.get_global("repl")?; debug!("Starting repl"); diff --git a/src/lib.rs b/src/lib.rs index acf30b0ecb..db0871592a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -243,29 +243,28 @@ impl From> for Error { } impl Error { - pub fn emit_string(&self, code_map: &::codespan::CodeMap) -> ::std::io::Result { + pub fn emit_string(&self) -> ::std::io::Result { let mut output = Vec::new(); self.emit( &mut ::codespan_reporting::termcolor::NoColor::new(&mut output), - code_map, )?; Ok(String::from_utf8(output).unwrap()) } - pub fn emit(&self, writer: &mut W, code_map: &::codespan::CodeMap) -> ::std::io::Result<()> + pub fn emit(&self, writer: &mut W) -> ::std::io::Result<()> where W: ?Sized + ::codespan_reporting::termcolor::WriteColor, { match *self { - Error::Parse(ref err) => err.emit(writer, code_map), - Error::Typecheck(ref err) => err.emit(writer, code_map), + Error::Parse(ref err) => err.emit(writer), + Error::Typecheck(ref err) => err.emit(writer), Error::IO(ref err) => write!(writer, "{}", err), Error::VM(ref err) => write!(writer, "{}", err), - Error::Macro(ref err) => err.emit(writer, code_map), + Error::Macro(ref err) => err.emit(writer), Error::Other(ref err) => write!(writer, "{}", err), Error::Multiple(ref errors) => { for err in errors { - err.emit(writer, code_map)?; + err.emit(writer)?; } Ok(()) } diff --git a/std/alternative.glu b/std/alternative.glu index d1b6a43eb0..0a084cdd8d 100644 --- a/std/alternative.glu +++ b/std/alternative.glu @@ -11,7 +11,7 @@ type Alternative f = { /// An associative binary operation. /// /// Evaluates to the first argument if it is not `empty`, otherwise evaluates to the second argument. - or : forall a . f a -> f a -> f a, + or : forall a . f a -> f a -> f a } let empty ?alt : [Alternative f] -> f a = alt.empty @@ -26,5 +26,7 @@ let (<|>) : [Alternative f] -> f a -> f a -> f a = or { Alternative, - empty, or, (<|>), + empty, + or, + (<|>), } diff --git a/std/applicative.glu b/std/applicative.glu index 7971f56f75..5a351a2757 100644 --- a/std/applicative.glu +++ b/std/applicative.glu @@ -51,11 +51,13 @@ let (<*) l r : [Applicative f] -> f a -> f b -> f a = map (\x _ -> x) l <*> r let (*>) l r : [Applicative f] -> f a -> f b -> f b = map (\_ x -> x) l <*> r /// Maps over two actions -let map2 fn a b : [Applicative f] -> (a -> b -> c) +let map2 fn a b : [Applicative f] + -> (a -> b -> c) -> f a -> f b -> f c - = map fn a <*> b + = + map fn a <*> b /// Maps over three actions let map3 fn a b c : [Applicative f] -> (a -> b -> c -> d) -> f a -> f b -> f c -> f d = @@ -83,15 +85,17 @@ let map10 fn a b c d e f g h i j : [Applicative f] -> _ = map9 fn a b c d e f g h i <*> j let when b m : [Applicative m] -> Bool -> m () -> m () = - if b then - m - else - wrap () + if b then m + else wrap () { Applicative, - apply, wrap, (<*>), (<*), (*>), - + apply, + wrap, + (<*>), + (<*), + (*>), + map2, map3, map4, @@ -101,6 +105,6 @@ let when b m : [Applicative m] -> Bool -> m () -> m () = map8, map9, map10, - + when, } diff --git a/std/array.glu b/std/array.glu index cdc5d7c6c8..bb5943d4d5 100644 --- a/std/array.glu +++ b/std/array.glu @@ -19,18 +19,15 @@ let cons l r = prim.append [l] r let eq ?eq : [Eq a] -> Eq (Array a) = let array_eq l r = - if prim.len l /= prim.len r then - False + if prim.len l /= prim.len r then False else let len = prim.len l - rec - let array_eq_ i = + rec let array_eq_ i = if i < len then let x = prim.index l i let y = prim.index r i eq.(==) x y && array_eq_ (i + 1) - else - True + else True array_eq_ 0 { (==) = array_eq } @@ -44,8 +41,7 @@ let ord ?ord : [Ord a] -> Ord (Array a) = match ord.compare x y with | EQ -> array_cmp_ (i + 1) | o -> o - else - compare (prim.len l) (prim.len r) + else compare (prim.len l) (prim.len r) array_cmp_ 0 { eq, compare = array_cmp } @@ -54,15 +50,13 @@ let show ?d : [Show a] -> Show (Array a) = let show xs = let len = prim.len xs - if len == 0 then - "[]" + if len == 0 then "[]" else rec let show_elems i = if i < len then let x = prim.index xs i ", " ++ d.show x ++ show_elems (i + 1) - else - "" + else "" "[" ++ d.show (prim.index xs 0) ++ show_elems 1 ++ "]" { show } @@ -73,8 +67,7 @@ let functor : Functor Array = if i < prim.len xs then let y = prim.index xs i cons (f y) (map_ (i + 1)) - else - [] + else [] map_ 0 { map } @@ -82,8 +75,7 @@ let foldable : Foldable Array = let foldr f y xs = let len = prim.len xs rec let foldr_ i y = - if i == 0 then - y + if i == 0 then y else let x = prim.index xs (i - 1) foldr_ (i - 1) (f x y) @@ -95,8 +87,7 @@ let foldable : Foldable Array = if i < len then let x = prim.index xs i foldl_ (i + 1) (f y x) - else - y + else y foldl_ 0 y diff --git a/std/bool.glu b/std/bool.glu index 63d2cc068f..9664d9d571 100644 --- a/std/bool.glu +++ b/std/bool.glu @@ -17,7 +17,7 @@ let xor x y : Bool -> Bool -> Bool = if x then not y else y let conjunctive = let semigroup : Semigroup Bool = { - append = \x y -> x && y + append = \x y -> x && y, } let monoid : Monoid Bool = { @@ -29,7 +29,7 @@ let conjunctive = let disjunctive = let semigroup : Semigroup Bool = { - append = \x y -> x || y + append = \x y -> x || y, } let monoid : Monoid Bool = { @@ -55,15 +55,16 @@ let exclusive = { semigroup, monoid, group } let eq : Eq Bool = { - (==) = \l r -> if l then r else not r + (==) = \l r -> if l then r else not r, } let ord : Ord Bool = { - eq = eq, compare = \l r -> if l then if r then EQ else GT else LT + eq = eq, + compare = \l r -> if l then if r then EQ else GT else LT, } let show : Show Bool = { - show = \x -> if x then "True" else "False" + show = \x -> if x then "True" else "False", } { diff --git a/std/byte.glu b/std/byte.glu index c3503c24eb..dbd2c09992 100644 --- a/std/byte.glu +++ b/std/byte.glu @@ -4,7 +4,7 @@ let { Semigroup, Monoid, Group, Eq, Ord, Ordering, Num, Show } = import! std.pre let additive = let semigroup : Semigroup Byte = { - append = \x y -> x #Byte+ y + append = \x y -> x #Byte+ y, } let monoid : Monoid Byte = { @@ -21,7 +21,7 @@ let additive = let multiplicative = let semigroup : Semigroup Byte = { - append = \x y -> x #Byte* y + append = \x y -> x #Byte* y, } let monoid : Monoid Byte = { @@ -32,7 +32,7 @@ let multiplicative = { semigroup, monoid } let eq : Eq Byte = { - (==) = \l r -> l #Byte== r + (==) = \l r -> l #Byte== r, } let ord : Ord Byte = { @@ -50,7 +50,7 @@ let num : Num Byte = { } let show : Show Byte = { - show = (import! std.prim).show_byte + show = (import! std.prim).show_byte, } { diff --git a/std/category.glu b/std/category.glu index f3dd5786dd..6e195ac0f8 100644 --- a/std/category.glu +++ b/std/category.glu @@ -20,7 +20,8 @@ let (>>) f g : forall a b c . [Category cat] -> cat a b -> cat b c -> cat a c = { Category, - id, compose, + id, + compose, (<<), (>>), diff --git a/std/cmp.glu b/std/cmp.glu index 603beb1254..df154f8b29 100644 --- a/std/cmp.glu +++ b/std/cmp.glu @@ -9,7 +9,7 @@ let { Monoid } = import! std.monoid #[implicit] type Eq a = { /// Tests whether the values are equal. - (==) : a -> a -> Bool, + (==) : a -> a -> Bool } #[infix(left, 4)] @@ -24,7 +24,7 @@ let (/=) ?eq l r : [Eq a] -> a -> a -> Bool = if (eq.(==) l r) then False else T type Ord a = { eq : Eq a, /// Compares two values and returns wheter the first is less than, equal or greater than the second. - compare : a -> a -> Ordering, + compare : a -> a -> Ordering } let compare ?ord : [Ord a] -> a -> a -> Ordering = ord.compare @@ -62,10 +62,12 @@ let (>=) l r : [Ord a] -> a -> a -> Bool = | GT -> True let min l r : [Ord a] -> a -> a -> a = - if l <= r then l else r + if l <= r then l + else r let max l r : [Ord a] -> a -> a -> a = - if r >= l then r else l + if r >= l then r + else l let semigroup : Semigroup Ordering = { append = \x y -> @@ -81,12 +83,19 @@ let monoid : Monoid Ordering = { { Eq, - (==), (/=), + (==), + (/=), Bool, Ord, - compare, (<), (<=), (>=), (>), min, max, + compare, + (<), + (<=), + (>=), + (>), + min, + max, Ordering, diff --git a/std/disposable.glu b/std/disposable.glu index b98d2e157e..f49b71d41a 100644 --- a/std/disposable.glu +++ b/std/disposable.glu @@ -12,10 +12,9 @@ type Disposable a = { /// Disposes of `a`, releasing the resources it manages. Calling this function /// a second time (or more) has no effect. dispose : a -> IO (), - /// Indicates if `a` has been disposed, meaning that `dispose` has been called /// at least once. - is_disposed : a -> Bool, + is_disposed : a -> Bool } let dispose ?disposable : [Disposable a] -> a -> IO () = disposable.dispose diff --git a/std/effect/alt.glu b/std/effect/alt.glu index 172bb0b6f0..c6d06ffe4f 100644 --- a/std/effect/alt.glu +++ b/std/effect/alt.glu @@ -8,19 +8,23 @@ let { (<<), id } = import! std.function /// The `Alt` effect lets `Eff` implement `Alternative` type Alt r a = - | Empty - .. r + | Empty .. r let extract_alt x : forall s . [| alt : Alt | r |] a -> Alt r a = convert_variant! x let send_alt f : Alt r a -> Eff [| alt : Alt | r |] a = Impure (convert_effect! alt f) Pure -let run_alt_inner transform fail eff_1 eff_2 : (a -> b) -> (() -> Eff [| | s |] b) -> Eff [| alt : Alt | r |] a -> Eff [| alt : Alt | r |] a -> Eff [| | s |] b = +let run_alt_inner transform fail eff_1 eff_2 : (a -> b) + -> (() -> Eff [| | s |] b) + -> Eff [| alt : Alt | r |] a + -> Eff [| alt : Alt | r |] a + -> Eff [| | s |] b + = let loop next ve : (() -> Eff [| | s |] b) -> Eff [| alt : Alt | r |] a -> _ = match ve with | Pure value -> wrap (transform value) | Impure e f -> - match extract_alt e with + match extract_alt e with | Empty -> next () | rest -> @@ -38,7 +42,7 @@ let alternative : Alternative (Eff [| alt : Alt | r |]) = { } /// Eliminates the `Alt` effect returning `None` if the `Alt` effect is `empty`, otherwise returns `Some a` with the value -/// +/// /// ``` /// let { assert_eq, ? } = import! std.test /// let alt @ { ? } = import! std.effect.alt @@ -48,14 +52,17 @@ let alternative : Alternative (Eff [| alt : Alt | r |]) = { /// let { Eff, run_pure, ? } = import! std.effect /// /// let incr = state.modify (\x -> x + 1) -/// +/// /// seq assert_eq (run_pure (state.exec_state 0 (alt.run_alt incr incr))) (1) /// seq assert_eq (run_pure (state.exec_state 0 (alt.run_alt (incr *> incr) incr))) (2) /// seq assert_eq (run_pure (state.exec_state 0 (alt.run_alt (incr *> empty *> incr) incr))) (2) /// assert_eq (run_pure (state.exec_state 0 (alt.run_alt empty incr))) (1) -/// +/// /// ``` -let run_alt eff_1 eff_2 : Eff [| alt : Alt | r |] a -> Eff [| alt : Alt | r |] a -> Eff [| | r |] (Option a) = +let run_alt eff_1 eff_2 : Eff [| alt : Alt | r |] a + -> Eff [| alt : Alt | r |] a + -> Eff [| | r |] (Option a) + = let fail _ = wrap None run_alt_inner Some fail eff_1 eff_2 diff --git a/std/effect/error.glu b/std/effect/error.glu index 347c26bba2..8ab1b5b897 100644 --- a/std/effect/error.glu +++ b/std/effect/error.glu @@ -8,10 +8,10 @@ let { wrap } = import! std.applicative /// The `Error` effects adds "exceptions" to the `Eff` monad type Error e r a = - | Error : e -> Error e r a - .. r + | Error e .. r -let send_error f : Error e r a -> Eff [| error : Error e | r |] a = Impure (convert_effect! error f) Pure +let send_error f : Error e r a -> Eff [| error : Error e | r |] a = + Impure (convert_effect! error f) Pure let extract_error x : forall e . [| error : Error e | r |] a -> Error e r a = convert_variant! x @@ -35,7 +35,7 @@ let run_error eff : forall e . Eff [| error : Error e | r |] a -> Eff [| | r |] match ve with | Pure v -> wrap (Ok v) | Impure e f -> - match extract_error e with + match extract_error e with | Error err -> wrap (Err err) | rest -> @@ -43,12 +43,20 @@ let run_error eff : forall e . Eff [| error : Error e | r |] a -> Eff [| | r |] loop eff /// Catches an "exception", allowing the effect to continue executing -let catch eff handler : forall e . Eff [| error : Error e | r |] a -> (e -> Eff [| error : Error e | r |] a) -> Eff [| error : Error e | r |] a = +let catch eff handler : forall e . + + + + + Eff [| error : Error e | r |] a + -> (e -> Eff [| error : Error e | r |] a) + -> Eff [| error : Error e | r |] a + = let loop ve : Eff [| error : Error e | r |] a -> Eff [| error : Error e | r |] a = match ve with | Pure v -> wrap v | Impure e f -> - match extract_error e with + match extract_error e with | Error err -> handler err | rest -> diff --git a/std/effect/io.glu b/std/effect/io.glu index 2593573cf0..2cb4739f3f 100644 --- a/std/effect/io.glu +++ b/std/effect/io.glu @@ -1,2 +1,2 @@ let { lift } = import! std.effect.lift -lift_io! lift (import! std.io) +lift_io! lift diff --git a/std/effect/io/write.glu b/std/effect/io/write.glu index d0ec3fea46..359dfa23e3 100644 --- a/std/effect/io/write.glu +++ b/std/effect/io/write.glu @@ -1,3 +1,3 @@ let { lift } = import! std.effect.lift let { Write } = import! std.io.write -lift_io! lift (import! std.io.write) +lift_io! lift diff --git a/std/effect/lift.glu b/std/effect/lift.glu index d3a52e7de1..c46010ba09 100644 --- a/std/effect/lift.glu +++ b/std/effect/lift.glu @@ -7,14 +7,14 @@ let { Monad, flat_map } = import! std.monad let { (<<) } = import! std.function /// The `Lift` effect allows a regular monad (usually `IO`) to be used in an `Eff` monad -type Lift m r a = | Lift (m a) .. r +type Lift m r a = + | Lift (m a) .. r let send_lift f : Lift m r a -> Eff [| lift : Lift m | r |] a = Impure (convert_effect! lift f) Pure let extract_state x : forall m . [| lift : Lift m | r |] a -> Lift m r a = convert_variant! x /// "Lifts" a monadic action into the `Eff` monad. Since monads do not compose this can only be -// done once and `run_lift` must be the outermost effect eliminator let lift m : m a -> Eff [| lift : Lift m | r |] a = send_lift (Lift m) /// Eliminates the lifted monad `m`. Can only be used once all other effects have been eliminated @@ -23,10 +23,10 @@ let run_lift eff : [Monad m] -> Eff [| lift : Lift m |] a -> m a = match ve with | Pure v -> wrap v | Impure e f -> - match extract_state e with + match extract_state e with | Lift m -> do a = m - loop (f a) + loop (f a) | _ -> error "Impossible: Lift should always be the only remaining variant" loop eff diff --git a/std/effect/reader.glu b/std/effect/reader.glu index 748f671233..c5a8557d11 100644 --- a/std/effect/reader.glu +++ b/std/effect/reader.glu @@ -6,12 +6,12 @@ let { (<<) } = import! std.function /// The `Reader` effects provides a shared, immutable environment for the effectful functions using it type Reader s r a = - | Ask : Reader s r s - .. r + | Ask : Reader s r s .. r let extract_reader x : forall s . [| reader : Reader s | r |] a -> Reader s r a = convert_variant! x -let send_reader f : Reader s r a -> Eff [| reader : Reader s | r |] a = Impure (convert_effect! reader f) Pure +let send_reader f : Reader s r a -> Eff [| reader : Reader s | r |] a = + Impure (convert_effect! reader f) Pure /// Retrieve the value from the environment let ask : forall s . Eff [| reader : Reader s | r |] s = @@ -22,14 +22,17 @@ let asks f : forall s . (s -> a) -> Eff [| reader : Reader s | r |] a = map f ask /// Runs a computation in a modified environment. -let local f eff : forall s . (s -> s) -> Eff [| reader : Reader s | r |] a -> Eff [| reader : Reader s | r |] a = +let local f eff : forall s . + (s -> s) -> Eff [| reader : Reader s | r |] a -> Eff [| reader : Reader s | r |] a + = do s = asks f - let s : s = s // FIXME Remove after this does not affect inference + let s : s = s + // FIXME Remove after this does not affect inference let loop ve : Eff [| reader : Reader s | r |] a -> Eff [| reader : Reader s | r |] a = match ve with | Pure value -> wrap value | Impure e f -> - match extract_reader e with + match extract_reader e with | Ask -> loop (f s) | rest -> @@ -42,7 +45,7 @@ let run_reader s eff : forall s . s -> Eff [| reader : Reader s | r |] a -> Eff match ve with | Pure value -> wrap value | Impure e f -> - match extract_reader e with + match extract_reader e with | Ask -> loop reader (f reader) | rest -> diff --git a/std/effect/st.glu b/std/effect/st.glu index 0c0072f648..99731e1586 100644 --- a/std/effect/st.glu +++ b/std/effect/st.glu @@ -21,7 +21,8 @@ type State s r a = let extract_state x : forall s . [| st : State s | r |] a -> State s r a = convert_variant! x #[inline(never)] -let send_state f : forall s . State s r a -> Eff [| st : State s | r |] a = Impure (convert_effect! st f) Pure +let send_state f : forall s . State s r a -> Eff [| st : State s | r |] a = + Impure (convert_effect! st f) Pure /// Creates a new mutable reference that contains `a`. let new_ref a : forall s . a -> Eff [| st : State s | r |] (STRef s a) = send_state (New a) @@ -29,10 +30,11 @@ let new_ref a : forall s . a -> Eff [| st : State s | r |] (STRef s a) = send_st let make_call = Call /// Reads the values stored in the reference. -let read_ref ref : forall s . STRef s a -> Eff [| st : State s | r |] a = send_state (Read ref) +let read_ref ref : forall s . STRef s a -> Eff [| st : State s | r |] a = send_state (Read ref) /// Writes a new value into the reference. -let write_ref a ref : forall s . a -> STRef s a -> Eff [| st : State s | r |] () = send_state (Write a ref) +let write_ref a ref : forall s . a -> STRef s a -> Eff [| st : State s | r |] () = + send_state (Write a ref) /// Eliminates the `State` effect let run_state eff : (forall s . Eff [| st : State s | r |] a) -> Eff [| | r |] a = @@ -40,7 +42,7 @@ let run_state eff : (forall s . Eff [| st : State s | r |] a) -> Eff [| | r |] a match ve with | Pure value -> wrap value | Impure e f -> - match extract_state e with + match extract_state e with | New a -> loop (f { __ref = ref a }) | Read r -> @@ -58,7 +60,7 @@ let run_state eff : (forall s . Eff [| st : State s | r |] a) -> Eff [| | r |] a { State, - + send_state, new_ref, diff --git a/std/effect/st/string.glu b/std/effect/st/string.glu index d41c3a28c4..107a7f77b1 100644 --- a/std/effect/st/string.glu +++ b/std/effect/st/string.glu @@ -2,16 +2,16 @@ let { Eff, ? } = import! std.effect let { State, send_state, make_call } = import! std.effect.st let prim @ { StringBuf } = import! std.effect.st.string.prim -let new : forall s . Eff [| st : State s | r | ] (StringBuf s) = +let new : forall s . Eff [| st : State s | r |] (StringBuf s) = send_state (make_call prim.new) -let len buf : StringBuf s -> Eff [| st : State s | r | ] Int = +let len buf : StringBuf s -> Eff [| st : State s | r |] Int = send_state (make_call (\_ -> prim.len buf)) -let push_str buf str : StringBuf s -> String -> Eff [| st : State s | r | ] () = +let push_str buf str : StringBuf s -> String -> Eff [| st : State s | r |] () = send_state (make_call (\_ -> prim.push_str buf str)) -let slice buf start end : StringBuf s -> Int -> Int -> Eff [| st : State s | r | ] String = +let slice buf start end : StringBuf s -> Int -> Int -> Eff [| st : State s | r |] String = send_state (make_call (\_ -> prim.slice buf start end)) /// ``` @@ -29,7 +29,7 @@ let slice buf start end : StringBuf s -> Int -> Int -> Eff [| st : State s | r | /// string_buf.read buf /// assert_eq (run_pure (st.run_state action)) "field: 123" /// ``` -let read buf : StringBuf s -> Eff [| st : State s | r | ] String = +let read buf : StringBuf s -> Eff [| st : State s | r |] String = do l = len buf slice buf 0 l diff --git a/std/effect/state.glu b/std/effect/state.glu index f0c6ad5247..1eec680002 100644 --- a/std/effect/state.glu +++ b/std/effect/state.glu @@ -13,7 +13,8 @@ type State s r a = let extract_state x : forall s . [| state : State s | r |] a -> State s r a = convert_variant! x -let send_state f : State s r a -> Eff [| state : State s | r |] a = Impure (convert_effect! state f) Pure +let send_state f : State s r a -> Eff [| state : State s | r |] a = + Impure (convert_effect! state f) Pure /// Retreive the current value. let get : forall s . Eff [| state : State s | r |] s = @@ -33,12 +34,17 @@ let modify f : (s -> s) -> Eff [| state : State s | r |] () = put (f s) /// Eliminate the `State` effect and return the state and the computed value -let run_state s eff : forall s . s -> Eff [| state : State s | r |] a -> Eff [| | r |] { state : s, value : a} = - let loop state ve : s -> Eff [| state : State s | r |] a -> Eff [| | r |] { state : s, value : a } = +let run_state s eff : forall s . + s -> Eff [| state : State s | r |] a -> Eff [| | r |] { state : s, value : a } + = + let loop state ve : s + -> Eff [| state : State s | r |] a + -> Eff [| | r |] { state : s, value : a } + = match ve with | Pure value -> wrap { state, value } | Impure e f -> - match extract_state e with + match extract_state e with | Get -> loop state (f state) | Put state -> diff --git a/std/effect/writer.glu b/std/effect/writer.glu index 4ddfe7b140..fc10a02d97 100644 --- a/std/effect/writer.glu +++ b/std/effect/writer.glu @@ -8,14 +8,14 @@ let { (<<) } = import! std.function /// The `Writer` effect allows the computations to output values of type `s` type Writer s r a = - | Tell : s -> Writer s r () - .. r + | Tell : s -> Writer s r () .. r #[inline(never)] let extract_writer x : forall s . [| writer : Writer s | r |] a -> Writer s r a = convert_variant! x #[inline(never)] -let send_writer f : Writer s r a -> Eff [| writer : Writer s | r |] a = Impure (convert_effect! writer f) Pure +let send_writer f : Writer s r a -> Eff [| writer : Writer s | r |] a = + Impure (convert_effect! writer f) Pure /// Outputs `s` let tell s : forall s . s -> Eff [| writer : Writer s | r |] () = @@ -23,12 +23,14 @@ let tell s : forall s . s -> Eff [| writer : Writer s | r |] () = /// Eliminates `Writer`, returning the output and computed value. Each output through `tell` are /// joined via its `Monoid` instance -let run_writer eff : forall s . [Monoid s] -> Eff [| writer : Writer s | r |] a -> Eff [| | r |] { value : a, writer : s } = +let run_writer eff : forall s . + [Monoid s] -> Eff [| writer : Writer s | r |] a -> Eff [| | r |] { value : a, writer : s } + = let loop writer ve : s -> Eff [| writer : Writer s | r |] a -> Eff [| | r |] _ = match ve with | Pure value -> wrap { value, writer } | Impure e f -> - match extract_writer e with + match extract_writer e with | Tell w -> loop (writer <> w) (f ()) | rest -> diff --git a/std/function.glu b/std/function.glu index 499a2bba9b..de59a433a6 100644 --- a/std/function.glu +++ b/std/function.glu @@ -9,7 +9,8 @@ let { Monad } = import! std.monad let { Applicative } = import! std.applicative let semigroup s : Semigroup b -> Semigroup (a -> b) = { - append = \f g x -> s.append (f x) (g x) } + append = \f g x -> s.append (f x) (g x), +} let monoid m : Monoid b -> Monoid (a -> b) = { semigroup = semigroup m.semigroup, diff --git a/std/json/de.glu b/std/json/de.glu index faa56cae3a..7ba81bf46e 100644 --- a/std/json/de.glu +++ b/std/json/de.glu @@ -29,18 +29,22 @@ let error_msg = id let deserializer : Deserializer i a -> Deserializer i a = id let functor : Functor (Deserializer i) = { - map = \f m -> deserializer (\input -> - do a = deserializer m input - Ok { value = f a.value, input = a.input }) + map = \f m -> + deserializer + (\input -> + do a = deserializer m input + Ok { value = f a.value, input = a.input }), } let applicative : Applicative (Deserializer i) = { functor, - apply = \f m -> deserializer (\input -> - do g = deserializer f input - do a = deserializer m g.input - Ok { value = g.value a.value, input = a.input }), + apply = \f m -> + deserializer + (\input -> + do g = deserializer f input + do a = deserializer m g.input + Ok { value = g.value a.value, input = a.input }), wrap = \value -> deserializer (\input -> Ok { value, input }), } @@ -50,19 +54,22 @@ let alternative : Alternative (Deserializer i) = { empty = deserializer (\stream -> Err (error_msg "empty")), - or = \l r -> deserializer (\stream -> - match deserializer l stream with - | Ok a -> Ok a - | Err _ -> deserializer r stream), + or = \l r -> + deserializer + (\stream -> + match deserializer l stream with + | Ok a -> Ok a + | Err _ -> deserializer r stream), } let monad : Monad (Deserializer i) = { applicative, flat_map = \f m -> - deserializer (\input -> - do a = deserializer m input - deserializer (f a.value) a.input), + deserializer + (\input -> + do a = deserializer m input + deserializer (f a.value) a.input), } /// Deserializes a `Bool` @@ -222,8 +229,7 @@ let map a : ValueDeserializer a -> ValueDeserializer (Map String a) = \input -> | _ -> Err (error_msg "Expected map") /// Deserializes a `Value` -let value : ValueDeserializer Value = \input -> - Ok { value = input, input } +let value : ValueDeserializer Value = \input -> Ok { value = input, input } #[implicit] type Deserialize a = { deserializer : ValueDeserializer a } diff --git a/std/map.glu b/std/map.glu index f902989fc7..be8c55e2df 100644 --- a/std/map.glu +++ b/std/map.glu @@ -87,7 +87,9 @@ let foldl_with_key f z m : [Ord k] -> (a -> k -> b -> a) -> a -> Map k b -> a = | Bin k x l r -> foldl_with_key f (f (foldl_with_key f z l) k x) r /// Performs a traverse over the `Map` where the key gets passed to the function in addition to the value. -let traverse_with_key f m : [Ord k] -> [Applicative t] -> (k -> a -> t b) +let traverse_with_key f m : [Ord k] + -> [Applicative t] + -> (k -> a -> t b) -> Map k a -> t (Map k b) = @@ -96,8 +98,7 @@ let traverse_with_key f m : [Ord k] -> [Applicative t] -> (k -> a -> t b) let go m = match m with | Tip -> wrap Tip - | Bin k v l r -> - map3 (flip (Bin k)) (go l) (f k v) (go r) + | Bin k v l r -> map3 (flip (Bin k)) (go l) (f k v) (go r) go m diff --git a/std/option.glu b/std/option.glu index 5d07260bee..49f0dd4881 100644 --- a/std/option.glu +++ b/std/option.glu @@ -75,16 +75,15 @@ let eq ?a : [Eq a] -> Eq (Option a) = { | _ -> False, } -let ord ?a : [Ord a] -> Ord (Option a) = - { - eq = eq, - compare = \l r -> - match (l, r) with - | (Some l_val, Some r_val) -> a.compare l_val r_val - | (None, Some _) -> LT - | (Some _, None) -> GT - | (None, None) -> EQ, - } +let ord ?a : [Ord a] -> Ord (Option a) = { + eq = eq, + compare = \l r -> + match (l, r) with + | (Some l_val, Some r_val) -> a.compare l_val r_val + | (None, Some _) -> LT + | (Some _, None) -> GT + | (None, None) -> EQ, +} let functor : Functor Option = { map = \f x -> diff --git a/std/parser.glu b/std/parser.glu index 7e7d08eea1..73e7321710 100644 --- a/std/parser.glu +++ b/std/parser.glu @@ -19,16 +19,19 @@ type ParseResult a = Result Error { value : a, buffer : OffsetString } /// `Parser` is a monad which parses a `String` into structured values type Parser a = - OffsetString -> ParseResult a + OffsetString + -> ParseResult a let parser : Parser a -> Parser a = id let functor : Functor Parser = { - map = \f m -> parser (\buffer -> - let result = parser m buffer - match result with - | Ok a -> Ok { value = f a.value, buffer = a.buffer } - | Err err -> Err err) + map = \f m -> + parser + (\buffer -> + let result = parser m buffer + match result with + | Ok a -> Ok { value = f a.value, buffer = a.buffer } + | Err err -> Err err), } let { map } = functor @@ -36,15 +39,17 @@ let { map } = functor let applicative : Applicative Parser = { functor, - apply = \f m -> parser (\buffer -> - let result1 = parser f buffer - match result1 with - | Ok g -> - let result2 = parser m g.buffer - match result2 with - | Ok a -> Ok { value = g.value a.value, buffer = a.buffer } - | Err err -> Err err - | Err err -> Err err), + apply = \f m -> + parser + (\buffer -> + let result1 = parser f buffer + match result1 with + | Ok g -> + let result2 = parser m g.buffer + match result2 with + | Ok a -> Ok { value = g.value a.value, buffer = a.buffer } + | Err err -> Err err + | Err err -> Err err), wrap = \value -> parser (\buffer -> Ok { value, buffer }), } @@ -56,10 +61,12 @@ let alternative : Alternative Parser = { empty = parser (\stream -> Err { position = stream.start, message = "empty" }), - or = \l r -> parser (\stream -> - match parser l stream with - | Ok a -> Ok a - | Err _ -> parser r stream), + or = \l r -> + parser + (\stream -> + match parser l stream with + | Ok a -> Ok a + | Err _ -> parser r stream), } let { (<|>) } = import! std.alternative @@ -67,10 +74,12 @@ let { (<|>) } = import! std.alternative let monad : Monad Parser = { applicative, - flat_map = \f m -> parser (\buffer -> - match parser m buffer with - | Ok a -> parser (f a.value) a.buffer - | Err err -> Err err), + flat_map = \f m -> + parser + (\buffer -> + match parser m buffer with + | Ok a -> parser (f a.value) a.buffer + | Err err -> Err err), } let { flat_map } = import! std.monad @@ -81,7 +90,8 @@ let uncons stream : OffsetString -> Option { char : Char, rest : OffsetString } else let c = string.char_at stream.buffer stream.start let char_len = char.len_utf8 c - Some { + Some + { char = c, rest = { start = stream.start + char_len, @@ -90,29 +100,29 @@ let uncons stream : OffsetString -> Option { char : Char, rest : OffsetString } }, } -let update_position c position : Char -> Position -> Position = - position + char.len_utf8 c +let update_position c position : Char -> Position -> Position = position + char.len_utf8 c /// Returns `message` as what was expected by `p` #[infix(left, 0)] let () p message : Parser a -> String -> Parser a = - parser (\stream -> - match p stream with - | Ok x -> Ok x - | Err _ -> Err { position = stream.start, message }) + parser + (\stream -> + match p stream with + | Ok x -> Ok x + | Err _ -> Err { position = stream.start, message }) /// Parses any character. Only errors if the stream is out of input let any : Parser Char = - parser (\stream -> - match uncons stream with - | Some record -> - let { char, rest } = record - Ok { value = char, buffer = rest } - | None -> Err { position = stream.start, message = "End of stream" }) + parser + (\stream -> + match uncons stream with + | Some record -> + let { char, rest } = record + Ok { value = char, buffer = rest } + | None -> Err { position = stream.start, message = "End of stream" }) /// Fails the parser with `message` as the cause -let fail message : String -> Parser a = - parser (\stream -> Err { position = stream.start, message }) +let fail message : String -> Parser a = parser (\stream -> Err { position = stream.start, message }) /// Succeeds if `predicate` returns `Some`, fails if `None` is returned let satisfy_map predicate : (Char -> Option a) -> Parser a = @@ -127,8 +137,7 @@ let satisfy predicate : (Char -> Bool) -> Parser Char = satisfy_map (\c -> if predicate c then Some c else None) /// Succeeds if the next token is `expected` -let token expected : Char -> Parser Char = - satisfy (\c -> expected == c) +let token expected : Char -> Parser Char = satisfy (\c -> expected == c) /// Succeds if the next token is a letter let letter : Parser Char = satisfy char.is_alphabetic "letter" @@ -148,54 +157,57 @@ let tab : Parser Char = token '\t' /// Parses one or more tokens passing `predicate` and returns the `String` between the start and /// end of those tokens let take1 predicate : (Char -> Bool) -> Parser String = - parser (\stream -> - let take_ stream2 = - match uncons stream2 with - | Some record -> - if predicate record.char then take_ record.rest - else if stream.start == stream2.start then - Err { position = stream.start, message = "Unexpected token" } - else Ok { - value = string.slice stream.buffer stream.start stream2.start, + parser + (\stream -> + let take_ stream2 = + match uncons stream2 with + | Some record -> + if predicate record.char then take_ record.rest + else if stream.start == stream2.start then + Err { position = stream.start, message = "Unexpected token" } + else + Ok + { + value = string.slice stream.buffer stream.start stream2.start, + buffer = stream2, + } + | None -> + Ok + { + value = string.slice stream.buffer stream.start stream.end, buffer = stream2, } - | None -> Ok { - value = string.slice stream.buffer stream.start stream.end, - buffer = stream2, - } - take_ stream) + take_ stream) /// Parses zero or more tokens passing `predicate` and returns the `String` between the start and /// end of those tokens -let take predicate : (Char -> Bool) -> Parser String = - take1 predicate <|> wrap "" +let take predicate : (Char -> Bool) -> Parser String = take1 predicate <|> wrap "" /// Parses using `p` and returns the `String` between the start and of what `p` parsed let recognize p : Parser a -> Parser String = - parser (\stream -> - match parser p stream with - | Ok a -> - Ok { - value = string.slice stream.buffer stream.start a.buffer.start, - buffer = a.buffer, - } - | Err err -> Err err) + parser + (\stream -> + match parser p stream with + | Ok a -> + Ok + { + value = string.slice stream.buffer stream.start a.buffer.start, + buffer = a.buffer, + } + | Err err -> Err err) /// Skips over whitespace characters let spaces = take char.is_whitespace /// Creates a parser from a factory function. Useful to prevent mutually recursive parser from looping forever -let lazy_parser f : (() -> Parser a) -> Parser a = - parser (\stream -> f () stream) +let lazy_parser f : (() -> Parser a) -> Parser a = parser (\stream -> f () stream) /// Parses `x` between `l` and `r`, returning the result of `x` -let between l r x : Parser a -> Parser b -> Parser c -> Parser c = - l *> x <* r +let between l r x : Parser a -> Parser b -> Parser c -> Parser c = l *> x <* r rec /// Parses with `p` zero or more times -let many p : Parser a -> Parser (List a) = - many1 p <|> wrap Nil +let many p : Parser a -> Parser (List a) = many1 p <|> wrap Nil /// Parses with `p` one or more times let many1 p : Parser a -> Parser (List a) = @@ -204,8 +216,7 @@ let many1 p : Parser a -> Parser (List a) = in rec /// Parses with `p` zero or more times, ignoring the result of the parser -let skip_many p : Parser a -> Parser () = - skip_many1 p <|> wrap () +let skip_many p : Parser a -> Parser () = skip_many1 p <|> wrap () /// Parses with `p` one or more times, ignoring the result of the parser let skip_many1 p : Parser a -> Parser () = seq p @@ -213,16 +224,17 @@ let skip_many1 p : Parser a -> Parser () = in /// Parses one of the characters of `s` let one_of s : String -> Parser Char = - satisfy (\first -> - let len = string.len s - let one_of_ i = - if i == len then - False - else - let c = string.char_at s i - if first == c then True - else one_of_ (i + char.len_utf8 c) - one_of_ 0) + satisfy + (\first -> + let len = string.len s + let one_of_ i = + if i == len then + False + else + let c = string.char_at s i + if first == c then True + else one_of_ (i + char.len_utf8 c) + one_of_ 0) <|> fail ("Expected one of `" <> s <> "`") @@ -233,22 +245,21 @@ let sep_by1 parser sep : Parser a -> Parser b -> Parser (List a) = wrap (Cons x xs) /// Parses `parser` separated by `sep` -let sep_by parser sep : Parser a -> Parser b -> Parser (List a) = - sep_by1 parser sep <|> wrap Nil +let sep_by parser sep : Parser a -> Parser b -> Parser (List a) = sep_by1 parser sep <|> wrap Nil /// Like `sep_by1` but applies the function returned by `op` on the left fold of successive parses let chainl1 p op : Parser a -> Parser (a -> a -> a) -> Parser a = do l = p let rest x = ( - do f = op - do r = p - rest (f x r)) <|> wrap x + do f = op + do r = p + rest (f x r)) + <|> wrap x rest l /// Like `sep_by` but applies the function returned by `op` on the left fold of successive parses -let chainl p op v : Parser a -> Parser (a -> a -> a) -> a -> Parser a = - chainl1 p op <|> wrap v +let chainl p op v : Parser a -> Parser (a -> a -> a) -> a -> Parser a = chainl1 p op <|> wrap v /// Parses `input` using `p` @@ -258,9 +269,15 @@ let parse p input : Parser a -> String -> Result String a = | Err err -> Err (int.show.show err.position <> ":" <> err.message) { - Position, Error, ParseResult, Parser, + Position, + Error, + ParseResult, + Parser, - functor, applicative, alternative, monad, + functor, + applicative, + alternative, + monad, parser, diff --git a/std/prelude.glu b/std/prelude.glu index e2c1ec17b8..5e584019fc 100644 --- a/std/prelude.glu +++ b/std/prelude.glu @@ -36,13 +36,18 @@ let { flat_map } = import! std.monad Group, Eq, - (==), (/=), + (==), + (/=), Ord, - (<), (<=), (>=), (>), + (<), + (<=), + (>=), + (>), Category, - id, compose, + id, + compose, Functor, Applicative, @@ -50,7 +55,11 @@ let { flat_map } = import! std.monad Monad, Num, - (+), (-), (*), (/), negate, + (+), + (-), + (*), + (/), + negate, Show, show, diff --git a/std/random.glu b/std/random.glu index 7dfb94bd0d..792bb0fb66 100644 --- a/std/random.glu +++ b/std/random.glu @@ -8,7 +8,7 @@ type RandomGen g = { next : g -> { value : Int, gen : g } } let xor_shift_rng = let random_gen : RandomGen XorShiftRng = { - next = prim.xor_shift_next + next = prim.xor_shift_next, } { diff --git a/std/state.glu b/std/state.glu index d81c9b222d..fc60d7f439 100644 --- a/std/state.glu +++ b/std/state.glu @@ -5,14 +5,16 @@ let { Applicative, Functor, Monad } = import! std.prelude type State s a = s -> { value : a, state : s } let functor : Functor (State s) = - rec let map f m : (a -> b) -> State s a -> State s b = \state -> + rec + let map f m : (a -> b) -> State s a -> State s b = \state -> let { value, state } = m state { value = f value, state = state } { map } let applicative : Applicative (State s) = - rec let apply mf n : State s (a -> b) -> State s a -> State s b = \state -> + rec + let apply mf n : State s (a -> b) -> State s a -> State s b = \state -> let { value, state } = mf state functor.map value n state in @@ -21,7 +23,8 @@ let applicative : Applicative (State s) = { functor, apply, wrap } let monad : Monad (State s) = - rec let flat_map f m : (a -> State s b) -> State s a -> State s b = \state -> + rec + let flat_map f m : (a -> State s b) -> State s a -> State s b = \state -> let { value, state } = m state let m2 = f value m2 state diff --git a/std/test.glu b/std/test.glu index 48e485b617..78de6287cc 100644 --- a/std/test.glu +++ b/std/test.glu @@ -75,8 +75,7 @@ let assert_success : [Show e] let assert_throws : forall e . [Show a] -> Eff [| error : Error e, writer : Test | r |] a -> Eff [| writer : Test | r |] () = - run_error - >> flat_map assert_err + run_error >> flat_map assert_err rec let run_raw test : Eff [| writer : Test | r |] a -> Eff [| | r |] (List String) = do test = run_writer test @@ -101,14 +100,14 @@ rec let run_io test : TestEffIO r a -> IO () = group, assert, - + assert_eq, assert_neq, assert_lt, assert_lte, assert_gt, assert_gte, - + assert_ok, assert_err, assert_success, diff --git a/std/writer.glu b/std/writer.glu index 235f7c71c1..3ea74368cc 100644 --- a/std/writer.glu +++ b/std/writer.glu @@ -9,7 +9,7 @@ let { Identity } = import! std.identity type Writer w a = { value : a, writer : w } let functor : [Monoid w] -> Functor (Writer w) = { - map = \f m -> { value = f m.value, writer = m.writer } + map = \f m -> { value = f m.value, writer = m.writer }, } let applicative : [Monoid w] -> Applicative (Writer w) = {