All Projects → bkase → Doctorpretty

bkase / Doctorpretty

Wadler's "A prettier printer" embedded pretty-printer DSL for Swift

Programming Languages

swift
15916 projects
dsl
153 projects

Projects that are alternatives of or similar to Doctorpretty

Prettier Package Json
Prettier formatter for package.json files
Stars: ✭ 86 (-53.76%)
Mutual labels:  prettier, pretty-print
Git Praise
A nicer git blame.
Stars: ✭ 24 (-87.1%)
Mutual labels:  prettier, pretty-print
Eslint Plugin Prettier
ESLint plugin for Prettier formatting
Stars: ✭ 2,228 (+1097.85%)
Mutual labels:  prettier
Eslint Config Wesbos
No-Sweat™ Eslint and Prettier Setup - with or without VS Code
Stars: ✭ 2,293 (+1132.8%)
Mutual labels:  prettier
Gr8
Customizable, functional css utilities
Stars: ✭ 169 (-9.14%)
Mutual labels:  functional
Folktale
[not actively maintained!] A standard library for functional programming in JavaScript
Stars: ✭ 1,995 (+972.58%)
Mutual labels:  functional
Chaos
The Chaos Programming Language
Stars: ✭ 171 (-8.06%)
Mutual labels:  functional
Helios
A purely functional JSON library for Kotlin built on Λrrow
Stars: ✭ 157 (-15.59%)
Mutual labels:  functional
Pretty Simple
pretty-printer for Haskell data types that have a Show instance
Stars: ✭ 183 (-1.61%)
Mutual labels:  pretty-print
Swiss
🇨🇭Functional custom elements
Stars: ✭ 169 (-9.14%)
Mutual labels:  functional
Treeify
Pretty-print a javascript object as a tree
Stars: ✭ 174 (-6.45%)
Mutual labels:  pretty-print
Tonal
A functional music theory library for Javascript
Stars: ✭ 2,156 (+1059.14%)
Mutual labels:  functional
Ddc
The Disco Discus Compiler
Stars: ✭ 164 (-11.83%)
Mutual labels:  functional
Lad
👦 Lad is the best Node.js framework. Made by a former Express TC and Koa team member.
Stars: ✭ 2,112 (+1035.48%)
Mutual labels:  prettier
Prettier Stylelint
code > prettier > stylelint > formatted code
Stars: ✭ 162 (-12.9%)
Mutual labels:  prettier
Philip2
An Elm to OCaml compiler
Stars: ✭ 182 (-2.15%)
Mutual labels:  functional
Kotlin Openapi Spring Functional Template
🍃 Kotlin Spring 5 Webflux functional application with api request validation and interactive api doc
Stars: ✭ 159 (-14.52%)
Mutual labels:  functional
Redux Zero
A lightweight state container based on Redux
Stars: ✭ 1,977 (+962.9%)
Mutual labels:  functional
Poprc
A Compiler for the Popr Language
Stars: ✭ 170 (-8.6%)
Mutual labels:  functional
Irken Compiler
Irken is a statically typed variant of Scheme. Or a lisp-like variant of ML.
Stars: ✭ 184 (-1.08%)
Mutual labels:  functional

Doctor Pretty

A Swift implementation of the A prettier printer (Wadler 2003) ported from wl-pprint-annotated

Build Status

What is this

A pretty printer is the dual of a parser -- take some arbitrary AST and output it to a String. This library is a collection of combinators and a primitive called a Doc to describe pretty printing some data much like a parser combinator library provides combinators and primitives to describe parsing test into an AST. Interestingly, this implementation efficiently finds the best pretty print. You encode your knowledge of what the best means with your use of various Doc combinators.

For example: Let's say we have some internal structured representation of this Swift code:

func aLongFunction(foo: String, bar: Int, baz: Long) -> (String, Int, Long) {
    sideEffect()
    return (foo, bar, baz)
}

With this library the description that pretty prints the above at a page width of 120 characters. Also prints:

At a page-width of 40 characters:

func aLongFunction(
    foo: String, bar: Int, baz: Long
) -> (String, Int, Long) {
    sideEffect()
    return (foo, bar, baz)
}

and at a page-width of 20 characters:

func aLongFunction(
    foo: String,
    bar: Int,
    baz: Long
) -> (
    String,
    Int,
    Long
) {
    sideEffect()
    return (
        foo,
        bar,
        baz
    )
}

See the encoding of this particular document in the testSwiftExample test case.

What would I use this for?

If you're outputting text and you care about the width of the page. Serializing to a Doc lets you capture your line-break logic and how your output string looks in one go.

Why would you output text and care about page width?

  1. You're building a gofmt-type tool for Swift (Note: gofmt doesn't pretty-print based on a width, refmt (Reason) and prettier (JavaScript) do)
  2. You're writing some sort of codegen tool to output Swift code
  3. You're building a source-to-source transpiler
  4. You're outputing help messages in a terminal window for some commandline app (I'm planning to use this for https://github.com/bkase/swift-optparse-applicative)

What is this, actually

A Swift implementation of the A prettier printer (Wadler 2003) paper (including generally accepted modern enhancements ala wl-pprint-annotated. This implementation is close to a direct port of wl-pprint-annotated with some influence from scala-optparse-applicative's Doc and a few extra Swiftisms.

Basic Usage

Doc is very composable. First of all it's a monoid with an .empty document and the .concat case which just puts two documents next to each other. We also have a primitive called grouped, which tries this document on a single line, but if it doesn't fit then breaks it up on new-lines. From there we build all high-level combinators up.

x <%> y concats x and y with a space in between if it fits, otherwise puts a line.

.text("foo") <%> .text("bar")

pretty-prints under a large page-width:

foo bar

but when the page-width is set to 5 prints:

foo
bar

Here are a few more combinators:

/// Concats x and y with a space in between
static func <+>(x: Doc, y: Doc) -> Doc

/// Behaves like `space` if the output fits the page
/// Otherwise it behaves like line
static var softline

/// Concats x and y together if it fits
/// Otherwise puts a line in between
static func <%%>(x: Doc, y: Doc) -> Doc

/// Behaves like `zero` if the output fits the page
/// Otherwise it behaves like line
static var softbreak: Doc

/// Puts a line between x and y that can be flattened to a space
static func <&>(x: Doc, y: Doc) -> Doc

/// Puts a line between x and y that can be flattened with no space
static func <&&>(x: Doc, y: Doc) -> Doc

There are also combinators for turning collections of documents into "collection"-like pretty-printed primitives such as a square-bracket separated lists:

.text("let x =") <%> [ "foo", "bar", "baz" ].map(Doc.text).list(indent: 4)

Pretty-prints at page-width 80 to:

let x = [foo, bar, baz]

and at page-width 10 to:

let x = [
    foo,
    bar,
    baz
]

See the source for more documentation, I have included descriptive doc-comments to explain all the operators (mostly taken from wl-pprint-annotated).

How do I actually pretty print my documents?

Doc has two rendering methods for now: renderPrettyDefault prints with a page-width of 100 and renderPretty lets you control the page-width.

These methods don't return Strings directly -- they return SimpleDoc a low-level IR that is close to a string, but high-enough that you can efficiently output to some other output system like stdout or a file.

For now, SimpleDoc has displayString() which outputs a String, and:

func display<M: Monoid>(readString: (String) -> M) -> M

display takes a function that can turn a string into a monoid and then smashes everything together. Because this works for any monoid, you just need to provide a monoid instance for your output formatter (to write to stdout or to a file).

Installation

With Swift Package Manager, put this inside your Package.swift:

.Package(url: "https://github.com/bkase/DoctorPretty.git",
         majorVersion: 0, minor: 5)

How does it work?

Read the A prettier printer (Wadler 2003) paper.

Doc is a recursive enum that captures text and new lines. The interesting case is .union(longerLines: Doc, shorterLines: Doc). This case reifies the notion of "try the longer lines first, then the shorter lines". We can build all sorts of high-level combinators that combine Docs in different ways that eventually reduce to a few strategic .unions.

The renderer keeps a work-list and each rule removes or adds pieces of work to the list and recurses until the list is empty. The best-fit metric proceeds greedily for now, but can be swapped out easily for a more intelligent algorithm in the future.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].