Precedence and associativity in a pretty-printer

Hi, Suppose we have some algebraic datatype describing an expression language containing the usual suspects (various binary arithmetic operators such as addition, subtraction, multiplication, division, exponentiation, function abstraction and application, etc.) each with their own precendence (multiplication binds stronger than addition) and associativity (function application is left associative, exponentiation is right associative, addition is associative, etc.) Is there a nice way to pretty-print such an expression with the minimal number of brackets? I can come up with something, but I'm sure somebody thought hard about this problem before and came up with a really nice solution :) Any hints or pointers would be appreciated, Thanks, Edsko

Hi Edsko,
On Jan 22, 2008 7:34 PM, Edsko de Vries
Is there a nice way to pretty-print such an expression with the minimal number of brackets? I can come up with something, but I'm sure somebody thought hard about this problem before and came up with a really nice solution :)
Take a look at how Haskell's derived Show instances do it? :-) http://www.haskell.org/onlinereport/derived.html#sect10.4 - Benja

On 1/22/08, Benja Fallenstein
Take a look at how Haskell's derived Show instances do it? :-)
I hate how Haskell handles precedence: 1) Arbitrary magic numbers for precedence, which isn't very natural. 2) Impossible to define operators that have higher precedence than function application. 3) Impossible to define unary operators. 4) Because of (1), impossible to define an operator with higher precedence than + but lower precedence than *, or similar. When I was working through SPJ's "functional languages" book, (see http://research.microsoft.com/~simonpj/Papers/pj-lester-book/) I was determined to do a better job. It's not that hard to define a partial ordering between operators instead of using fixed categories for precedences, so that's what I did. So, given an environment which gives you a partial ordering between operators and fixty declarations (left, right, or nofix), you can write out expressions pretty easily: type Name = String data Operator = OpFunAp | Op Name data PartialOrdering = PoLT | PoGT | PoEQ | PoNC deriving Eq data Fixity = FixLeft | FixRight | FixNone deriving Eq -- type FixityEnv = exercise for the reader -- with operations: -- contains :: FixityEnv -> Operator -> Bool -- comparePrec :: FixityEnv -> Operator -> Operator -> PartialOrdering -- fixity :: FixityEnv -> Operator -> Fixity -- unary operators are also an exercise for the reader -- you could also improve this to use ShowS and (.) -- instead of String and (++) to avoid quadratic slowdown. data FixityContext = FcNone | FcLeft Operator | FcRight Operator parens :: Bool -> String -> String parens False s = s parens True s = "(" ++ s ++ ")" print :: FixityEnv -> FixityContext -> Exp -> String print env ctx (Ap (Ap (Sym operator) left) right) | env `contains` (Op operator) = printBinOp env ctxt operator left right print env ctx (Ap left right) = printAp env ctxt left right print env ctx (Sym name) = parens (env `contains` Op name) name printBinOp :: FixityEnv -> FixityContext -> Name -> Exp -> Exp -> String printBinOp env ctxt op left right = parens (needsParens env ctxt (Op op)) $ concat [ print env (FcLeft $ Op op) left, " " ++ op ++ " ", print env (FcRight $ Op op) right ] printAp :: FixityEnv -> FixityContext -> Exp -> Exp -> String printAp env ctxt op left right = parens (needsParens env ctxt OpFunAp) $ concat [ print env (FcLeft OpFunAp) left, " ", print env (FcRight OpFunAp) right ] needsParens :: FixityEnv -> FixityContext -> Operator -> Bool needsParens _ FcNone _ = False needsParens env (FcLeft ctxt) op | comparePrec env ctxt op == PoLT = False | comparePrec env ctxt op == PoGT = True | comparePrec env ctxt op == PoNC = True -- otherwise the two operators have the same precedence | fixity ctxt /= fixity op = True | fixity ctxt == FixLeft = False | otherwise = True needsParens env (FcRight ctxt) op | comparePrec env ctxt op == PoLT = False | comparePrec env ctxt op == PoGT = True | comparePrec env ctxt op == PoNC = True -- otherwise the two operators have the same precedence | fixity ctxt /= fixity op = True | fixity ctxt == FixRight = False | otherwise = True

Edsko de Vries wrote:
Hi,
Is there a nice way to pretty-print such an expression with the minimal number of brackets? I can come up with something, but I'm sure somebody thought hard about this problem before and came up with a really nice solution :)
Any hints or pointers would be appreciated,
Thanks,
You might find Norman Ramsey's paper on unparsing helpful: http://citeseer.ist.psu.edu/194913.html or google for Norman Ramsey Unparsing regards allan
participants (5)
-
Allan Clark
-
Benja Fallenstein
-
devriese@cs.tcd.ie
-
Edsko de Vries
-
Ryan Ingram