summaryrefslogtreecommitdiff
path: root/sources/scala/text/Document.scala
diff options
context:
space:
mode:
authorschinz <schinz@epfl.ch>2003-12-22 12:10:40 +0000
committerschinz <schinz@epfl.ch>2003-12-22 12:10:40 +0000
commit83f5597196321cbfb08fa1372b636cb36eb64d58 (patch)
tree70d66039141e42261996e861f42894e450390e18 /sources/scala/text/Document.scala
parent782063cf853062ee6467b0dc664b52a7d505ffe8 (diff)
downloadscala-83f5597196321cbfb08fa1372b636cb36eb64d58.tar.gz
scala-83f5597196321cbfb08fa1372b636cb36eb64d58.tar.bz2
scala-83f5597196321cbfb08fa1372b636cb36eb64d58.zip
- ported the code from Lindig's article
Diffstat (limited to 'sources/scala/text/Document.scala')
-rw-r--r--sources/scala/text/Document.scala119
1 files changed, 119 insertions, 0 deletions
diff --git a/sources/scala/text/Document.scala b/sources/scala/text/Document.scala
new file mode 100644
index 0000000000..40d5a3a7c4
--- /dev/null
+++ b/sources/scala/text/Document.scala
@@ -0,0 +1,119 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+// $Id$
+
+package scala.text;
+
+import java.io.PrintWriter;
+import java.io.Writer;
+
+case object DocNil extends Document;
+case object DocBreak extends Document;
+case class DocText(txt: String) extends Document;
+case class DocGroup(doc: Document) extends Document;
+case class DocNest(indent: Int, doc: Document) extends Document;
+case class DocCons(hd: Document, tl: Document) extends Document;
+
+/**
+ * A basic pretty-printing library, based on Lindig's strict version
+ * of Wadler's adaptation of Hughes' pretty-printer.
+ *
+ * @version 1.0
+ * @author Michel Schinz
+ */
+
+abstract class Document {
+ def ::(hd: Document): Document = DocCons(hd, this);
+ def ::(hd: String): Document = DocCons(DocText(hd), this);
+ def :/:(hd: Document): Document = hd :: DocBreak :: this;
+ def :/:(hd: String): Document = hd :: DocBreak :: this;
+
+ /**
+ * Format this document on WRITER and try to set line breaks so that
+ * the result fits in WIDTH columns.
+ */
+ def format(width: Int, writer: Writer): Unit = {
+ type FmtState = Triple[Int,Boolean,Document];
+
+ def fits(w: Int, state: List[FmtState]): boolean = state match {
+ case _ if w < 0 =>
+ false
+ case List() =>
+ true
+ case Triple(_, _, DocNil) :: z =>
+ fits(w, z)
+ case Triple(i, b, DocCons(h, t)) :: z =>
+ fits(w, Triple(i,b,h) :: Triple(i,b,t) :: z)
+ case Triple(_, _, DocText(t)) :: z =>
+ fits(w - t.length(), z)
+ case Triple(i, b, DocNest(ii, d)) :: z =>
+ fits(w, Triple(i + ii, b, d) :: z)
+ case Triple(_, false, DocBreak) :: z =>
+ fits(w - 1, z)
+ case Triple(_, true, DocBreak) :: z =>
+ true
+ case Triple(i, _, DocGroup(d)) :: z =>
+ fits(w, Triple(i, false, d) :: z)
+ }
+
+ def spaces(n: Int): Unit = {
+ var rem = n;
+ while (rem >= 16) { writer write " "; rem = rem - 16 };
+ if (rem >= 8) { writer write " "; rem = rem - 8 };
+ if (rem >= 4) { writer write " "; rem = rem - 4 };
+ if (rem >= 2) { writer write " "; rem = rem - 2};
+ if (rem == 1) { writer write " " };
+ }
+
+ def fmt(k: Int, state: List[FmtState]): Unit = state match {
+ case List() => ()
+ case Triple(_, _, DocNil) :: z =>
+ fmt(k, z)
+ case Triple(i, b, DocCons(h, t)) :: z =>
+ fmt(k, Triple(i, b, h) :: Triple(i, b, t) :: z)
+ case Triple(i, _, DocText(t)) :: z =>
+ writer write t;
+ fmt(k + t.length(), z)
+ case Triple(i, b, DocNest(ii, d)) :: z =>
+ fmt(k, Triple(i + ii, b, d) :: z)
+ case Triple(i, true, DocBreak) :: z =>
+ writer write "\n";
+ spaces(i);
+ fmt(i, z)
+ case Triple(i, false, DocBreak) :: z =>
+ writer write " ";
+ fmt(k + 1, z)
+ case Triple(i, b, DocGroup(d)) :: z =>
+ val fitsFlat = fits(width - k, Triple(i, false, d) :: z);
+ fmt(k, Triple(i, !fitsFlat, d) :: z)
+ }
+
+ fmt(0, Triple(0, false, DocGroup(this)) :: Nil);
+ }
+}
+
+object Document {
+ /** The empty document */
+ def empty = DocNil;
+
+ /** A break, which will either be turned into a space or a line break */
+ def break = DocBreak;
+
+ /** A document consisting of some text literal */
+ def text(s: String): Document = DocText(s);
+
+ /**
+ * A group, whose components will either be printed with all breaks
+ * rendered as spaces, or with all breaks rendered as line breaks.
+ */
+ def group(d: Document): Document = DocGroup(d);
+
+ /** A nested document, which will be indented as specified. */
+ def nest(i: Int, d: Document): Document = DocNest(i, d);
+}