Implementing a Functional Language: Printing Core
This is an appendix to a series in implementing a functional language. The introduction is
here. This is a literate Haskell file - you can download the source
here. To load it into GHCi and play around, you can use the following command
(you’ll need the source for the Core.Language
module as well):
stack --resolver lts-12.2 \
ghci --package prettyprinter \
--package text \
2019-03-03-printing-core.lhs \
2019-03-03-the-core-language.lhs
{-# LANGUAGE OverloadedStrings #-}
module Core.Print (print) where
import Core.Language
import Data.Text.Prettyprint.Doc
import Prelude hiding (print)
This module exports a single function: print
. print
pretty prints a CoreProgram
,
producing a Doc a
which can be converted to a final output by the caller. Doc
is a
type provided by the Prettyprint
package.
We print a CoreProgram
by printing each of its supercombinator definitions, separating
each with a newline.
print :: CoreProgram -> Doc a
print = (vsep . map pScDefn)
We print supercombinator by printing its name and binders on one line, and printing the expression body across the rest of the line and possibly further lines below. The body will be indented to preserve the off-side rule.
pScDefn :: CoreScDefn -> Doc a
pScDefn (name, binders, expr)= pretty name <+> sepmap binders <+> equals <+> align (pExpr expr)
We print a Core expression by case analysis on its constructors.
pExpr :: CoreExpr -> Doc a
EVar v) = pretty v
pExpr (ENum n) = pretty n
pExpr (EConstr tag arity) = "Pack{" <> pretty tag <> "," <> pretty arity <> "}"
pExpr (EAp e1 e2) = pAExpr e1 <+> pAExpr e2
pExpr (ELet Recursive defns expr)
pExpr (= "letrec" <+> nest 2 (pDefns defns) <+> "in" <+> pExpr expr
ELet NonRecursive defns expr)
pExpr (= "let" <+> nest 2 (pDefns defns) <+> "in" <+> pExpr expr
ECase e alters)
pExpr (= "case" <+> pExpr e <+> "of" <+> hardline
<+> (align . vsep . punctuate semi) (map pAlter alters)
where pAlter (tag, binders, expr)
= sep $ angles (pretty tag) : map pretty binders ++ ["->", pExpr expr]
pDefns :: [(Name, Expr Name)] -> Doc a
pDefns defns= vsep $ punctuate semi (map pDefn defns)
where pDefn (name, expr) = pretty name <+> equals <+> align (pExpr expr)
pAExpr
is like pExpr
, but wraps the result in parentheses unless it is a variable or
number. This is naive but avoids ambiguity.
pAExpr :: CoreExpr -> Doc a
EVar v) = pretty v
pAExpr (ENum n) = pretty n
pAExpr (= parens (pExpr e) pAExpr e
sepmap
is a convenience function for printing each element in a list separated by spaces.
sepmap :: Pretty a => [a] -> Doc b
= sep (map pretty xs) sepmap xs
This is the entire pretty printer. Thanks to the fantastic prettyprinter
library, it’s
short and quite readable.