cpsa-2.2.2: src/cpsaextras/Printer.scala
/*
* A CPSA specific pretty printer
*
* Copyright (c) 2009 The MITRE Corporation
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the BSD License as published by the
* University of California.
*/
package cpsaextras
import java.io.PrintWriter
object Printer {
/** CPSA specific pretty printer.
* The pretty printer that indents a constant amount for each list. The
* top-level lists are laid out specially. Whenever some breaks
* occur, all breaks are forced. Also, breaks are only placed before
* strings and lists. CPSA protocols are handled specially. Each
* defrole is handled as are top-level lists.
*
* @param w the output writer
* @param margin the width to be filled
* @param indent the number of spaces in an indent
* @param x the S-expression to be pretty printed
*/
def pp[A](w: PrintWriter, margin: Int, indent: Int, x: SExpr[A]): Unit = {
Pretty.pr(w, margin, pretty(indent, x))
}
private def pretty[A](indent: Int, x: SExpr[A]): Pretty = {
x match {
case L(_, (x@S(_, "defprotocol")) :: xs) =>
roles(indent, xs, List(block(indent, x), Pretty.str("(")))
case x@L(_, S(_, "defmacro") :: _) =>
block(indent, x)
case x@L(_, S(_, "herald") :: _) =>
block(indent, x)
case _ =>
group(indent, x)
}
}
// Role specific pretty printer
private def roles[A](indent: Int, xs: List[SExpr[A]],
acc: List[Pretty]): Pretty = {
xs match {
case Nil =>
Pretty.grp(indent, (Pretty.str(")") :: acc).reverse)
case (x@S(_, _)) :: xs =>
roles(indent, xs, block(indent, x) :: Pretty.str(" ") :: acc)
case (x@Q(_, _)) :: xs =>
roles(indent, xs, block(indent, x) :: Pretty.brk(1) :: acc)
case (x@N(_, _)) :: xs =>
roles(indent, xs, block(indent, x) :: Pretty.str(" ") :: acc)
case (x@L(_, S(_, "defrole") :: _)) :: xs =>
roles(indent, xs, group(indent, x) :: Pretty.brk(1) :: acc)
case (x@L(_, _)) :: xs =>
roles(indent, xs, block(indent, x) :: Pretty.brk(1) :: acc)
}
}
// A pretty printer for top-level S-expressions
private def group[A](indent: Int, x: SExpr[A]): Pretty = {
x match {
case L(a, l) => grouplist(indent, l)
case _ => block(indent, x)
}
}
private def grouplist[A](indent: Int, xs: List[SExpr[A]]): Pretty = {
xs match {
case Nil => Pretty.str("()")
case x :: xs =>
grouprest(indent, xs, List(block(indent, x), Pretty.str("(")))
}
}
private def grouprest[A](indent: Int, xs: List[SExpr[A]],
acc: List[Pretty]): Pretty = {
xs match {
case Nil =>
Pretty.grp(indent, (Pretty.str(")") :: acc).reverse)
case (x@S(_, _)) :: xs =>
grouprest(indent, xs, block(indent, x) :: Pretty.str(" ") :: acc)
case (x@Q(_, _)) :: xs =>
grouprest(indent, xs, block(indent, x) :: Pretty.brk(1) :: acc)
case (x@N(_, _)) :: xs =>
grouprest(indent, xs, block(indent, x) :: Pretty.str(" ") :: acc)
case (x@L(_, _)) :: xs =>
grouprest(indent, xs, block(indent, x) :: Pretty.brk(1) :: acc)
}
}
// A pretty printer for interior lists using block style breaking.
private def block[A](indent: Int, x: SExpr[A]): Pretty = {
x match {
case L(a, l) => blocklist(indent, l)
case _ => Pretty.str(x.toString())
}
}
private def blocklist[A](indent: Int, xs: List[SExpr[A]]): Pretty = {
xs match {
case Nil => Pretty.str("()")
case x :: xs =>
blockrest(indent, xs, List(block(indent, x), Pretty.str("(")))
}
}
private def blockrest[A](indent: Int, xs: List[SExpr[A]],
acc: List[Pretty]): Pretty = {
xs match {
case Nil =>
Pretty.blo(indent, (Pretty.str(")") :: acc).reverse)
case x :: xs =>
blockrest(indent, xs, block(indent, x) :: Pretty.brk(1) :: acc)
}
}
}