--- /dev/null
+lib/edu.berkeley.sbp.jar
+wix.jar
ghc += -fglasgow-exts -cpp -hidir $(pwd)/build/hi -i$(pwd)/build/hi -odir $(pwd)/build/class/
ghclibs = $(ghcroot)/rts/HSrts.jar:$(ghcroot)/libraries/base/HSbase.jar:$(ghcroot)/libraries/stm/HSstm.jar
-java = java -Xmx800m
-java += $(profile) -cp src:$(ghclibs):$(sbp)/edu.berkeley.sbp.jar:build/class
+jvmargs = -Xmx800m -XX:ThreadStackSize=160000
+
+java = java $(jvmargs) $(profile) -cp src:$(ghclibs):$(sbp)/edu.berkeley.sbp.jar:build/class
+
+scala = JAVA_OPTS="$(jvmargs)" scala
wix = $(java) HaskellHelper
-install: build/class/Main.class build/class/Tib.class
- aklog hcoop.net || true
+install: wix.jar
aklog research.cs.berkeley.edu || true
- $(wix) ~/wix/src/ ~/wix/dest/
+ $(scala) -cp wix.jar:lib/edu.berkeley.sbp.jar Main ~/docs/wix/src/ ~/docs/wix/dest/
+
+# --delete disabled until I can keep it from clobbering GArrow.v
rsync -arL --rsync-path=/usr/sww/bin/rsync \
- --progress --verbose --delete \
- /Users/megacz/wix/dest/cs.berkeley.edu/ \
+ --progress --verbose \
+ ~/docs/wix/dest/cs.berkeley.edu/ \
login.eecs.berkeley.edu:public_html/
docs:
$(sbp)/edu.berkeley.sbp.jar: $(sbp)
cd $(sbp); make edu.berkeley.sbp.jar
-wix.jar: build/class/Main.class build/class/Tib.class lib/edu.berkeley.sbp.jar
- rm -rf tmp
- mkdir tmp
- cd build/class; for A in $(lambdavm_jars) lib/edu.berkeley.sbp.jar; \
- do jar xvf $$A; done
- echo 'Main-Class: HaskellHelper' > build/manifest
- cp src/*.g build/class/
- cd build/class; jar cvmf ../manifest ../../wix.jar .
+wix.jar: $(shell find src -name \*.java -or -name \*.scala)
+ rm -rf build
+ mkdir build
+ javac -cp lib/edu.berkeley.sbp.jar -d build `find src -name \*.java`
+ scalac -deprecation -cp lib/edu.berkeley.sbp.jar -sourcepath src -d build `find src -name \*.scala`
+ echo 'Main-Class: Main' > build/manifest
+ cp src/*.g build/
+ cd build; jar cvmf manifest ../wix.jar .
build/class/Tib.class: $(shell find src -name \*.java) lib/edu.berkeley.sbp.jar
javac -d build/class -cp lib/edu.berkeley.sbp.jar $(shell find src -name \*.java)
--- /dev/null
+ new Entity("tm", 0x2122),
+ new Entity("alef", 0x2135),
+ new Entity("leftArrow", "<--", 0x2190),
+ new Entity("rightArrow", "-->", 0x2192),
+ new Entity("leftDoubleArrow", "<==", 0x21D0),
+ new Entity("rightDoubleArrow", "==>", 0x21D2),
+ new Entity("doubleLeftRightArrow", "<==>", 0x21D4),
+ new Entity("upArrow", 0x2191),
+ new Entity("downArrow", 0x2193),
+ new Entity("upDoubleArrow", 0x21D1),
+ new Entity("downDoubleArrow", 0x21D3),
+ new Entity("forall", 0x2200),
+ new Entity("exists", 0x2203),
+ new Entity("emptySet", 0x2205),
+ new Entity("in", 0x2208),
+ new Entity("cent", 0xA2),
+ new Entity("pi", 0x220F),
+ new Entity("sigma", 0x2211),
+ new Entity("infinity", 0x221E),
+ new Entity("proportional", 0x221D),
+ new Entity("check", 0x221A),
+ new Entity("asterisk", 0x2217),
+ new Entity("minus", 0x2212),
+ new Entity("angle", 0x2220),
+ new Entity("and", 0x2227),
+ new Entity("or", 0x2228),
+ new Entity("intersection", 0x2229),
+ new Entity("union", 0x222A),
+ new Entity("integral", 0x222B),
+ new Entity("therefore", 0x2234),
+ new Entity("congruent", 0x2245),
+ new Entity("similarTo", 0x2248),
+ new Entity("identical", 0x2261),
+ new Entity("neq", 0x2260),
+ new Entity("subset", 0x2282),
+ new Entity("superset", 0x2283),
+ new Entity("notSubset", 0x2284),
+ new Entity("subsetEq", 0x2286),
+ new Entity("supersetEq", 0x2287),
+ new Entity("circlePlus", 0x2295),
+ new Entity("circleTimes", 0x2297),
+ new Entity("bottom", 0x22A5),
+ new Entity("cdot", 0x22C5),
+ new Entity("openDiamonds", 0x25CA),
+ new Entity("spade", 0x2660),
+ new Entity("clubs", 0x2663),
+ new Entity("hearts", 0x2665),
+ new Entity("diamonds", 0x2666),
+ new Entity("prime", 0x2032),
+ new Entity("reals", 0x211C),
+ new Entity("powerSet", 0x2118),
+ new Entity("overScore", 0x203E),
+ new Entity("yen", 0xA5),
+ new Entity("plusminus", 0xB1),
+ new Entity("micro", 0xB5),
+ new Entity("superScriptOne", 0xB9),
+ new Entity("superScriptTwo", 0xB2),
+ new Entity("superScriptThree", 0xB3),
+ new Entity("oneQuarter", 0xBC),
+ new Entity("oneHalf", 0xBD),
+ new Entity("threeQuarters", 0xBE),
+ new Entity("paragraphSymbol", 0xB6),
+ new Entity("times", 0xD7),
+ new Entity("daggar", 0x86),
+ new Entity("sectionSymbol", 0xA7),
+ new Entity("not", 0xAC),
+ new Entity("cr", 0x2193),
+ new Entity("dot", 0xB7),
--- /dev/null
+import edu.berkeley.sbp.scala._
+
+import Html.mapToHtml
+import Html.joinStrings
+import Html.{urlEscape,htmlEscape}
+import Html.{pre,stag,tag,stag_,tag_,stag0,link}
+
+object Doc {
+
+ def concatMap[A,B](f: A => Seq[B], s:Seq[A]) : Seq[B] =
+ concat(s.map(f))
+
+ def concat[A](s:Seq[Seq[A]]) : Seq[A] =
+ s.foldLeft(Seq[A]())(_ ++ _)
+
+ def docFromTree(t:Tree) : Doc =
+ t match { case Tree(_,Seq(_,Tree(_,a))) => new Doc(new Header(), a.map(sectionFromTree)) }
+
+ def sectionFromTree(t:Tree) : Section =
+ t match {
+ case Tree("Section", seq) =>
+ seq(0) match {
+ case Tree("SectionHeader", Seq(Tree("=",e),c)) =>
+ new Section(e.length-1, textSequenceFromTree(c), paragraphsFromTrees(seq.tail))
+ }
+ }
+
+ def textSequenceFromTree (t:Tree) : Seq[Text] =
+ t match {
+ case Tree("Word", chars ) => Seq(new Chars(stringFromTrees(chars)))
+ case Tree("Ordinal", x ) => Seq(new Command("ordinal", Seq(new Chars(stringFromTrees(x)))))
+ case Tree("Fraction", Seq(n,d) ) => Seq(new Command("fraction",Seq(new Chars(stringFromTree(n)),
+ new Chars(stringFromTree(d)))))
+ case Tree("WS", _ ) => Seq(WS)
+ case Tree("Quotes", Seq(x) ) => Seq(new Quotes(textSequenceFromTree(x)))
+ case Tree("Pars", y ) => Seq(new SubPar(paragraphsFromTrees(y)))
+ case Tree("Command", Seq(x,y) ) => Seq(new Command(stringFromTree(x), textSequenceFromTree(y)))
+ case Tree("Command", Seq(x) ) => Seq(new Command(stringFromTree(x), Seq()))
+ case Tree("Link", Seq(text,link) ) => Seq(new Link(urlFromTree(link), textSequenceFromTree(text)))
+ case Tree("Footnote", x ) => Seq(new Footnote(concatMap(textSequenceFromTree,x)))
+ case Tree("Keyword", x ) => Seq(new Keyword(concatMap(textSequenceFromTree,x)))
+ case Tree("Math", x ) => Seq(new Math(stringFromTrees(x)))
+ case Tree("Italic", Seq(x) ) => Seq(new Styled(Italic , textSequenceFromTree(x)))
+ case Tree("Bold", Seq(x) ) => Seq(new Styled(Bold , textSequenceFromTree(x)))
+ case Tree("Highlight", Seq(x) ) => Seq(new Styled(Highlight , textSequenceFromTree(x)))
+ case Tree("TT", x ) => Seq(new Styled(TT , concatMap(textSequenceFromTree,x)))
+ case Tree("Strikethrough", x ) => Seq(new Styled(Strikethrough , concatMap(textSequenceFromTree,x)))
+ case Tree("Superscript", x ) => Seq(new Styled(Superscript , concatMap(textSequenceFromTree,x)))
+ case Tree("Subscript", x ) => Seq(new Styled(Subscript , concatMap(textSequenceFromTree,x)))
+ case Tree("Underline", x ) => Seq(new Styled(Underline , concatMap(textSequenceFromTree,x)))
+ case Tree("(e)", _) => Seq(new GlyphText(Euro))
+ case Tree("(r)", _) => Seq(new GlyphText(CircleR))
+ case Tree("(c)", _) => Seq(new GlyphText(CircleC))
+ case Tree("(tm)", _) => Seq(new GlyphText(TradeMark))
+ case Tree("--", _) => Seq(new GlyphText(Emdash))
+ case Tree("<-", _) => Seq(new GlyphText(LeftArrow))
+ case Tree("<=", _) => Seq(new GlyphText(DoubleLeftArrow))
+ case Tree("=>", _) => Seq(new GlyphText(DoubleRightArrow))
+ case Tree("<=>", _) => Seq(new GlyphText(DoubleLeftRightArrow))
+ case Tree("<->", _) => Seq(new GlyphText(LeftRightArrow))
+ case Tree("^o", _) => Seq(new GlyphText(Degree))
+ case Tree("...", _) => Seq(new GlyphText(Ellipsis))
+ case Tree("Text", ts) => concat(ts.map(textSequenceFromTree))
+ case Tree("", Seq()) => Seq()
+ case t => throw new RuntimeException("unable to create [Text] from " + t)
+ }
+
+ def hostFromTree(t:Tree) : Host =
+ t match {
+ case Tree("IP", Seq(Tree(_,a),Tree(_,b),Tree(_,c),Tree(_,d))) =>
+ new HostIP(intFromTrees(a), intFromTrees(b), intFromTrees(c), intFromTrees(d))
+ case Tree("DNS", parts) =>
+ new HostDNS(parts.map( (t:Tree) => t match { case Tree(_, c) => stringFromTrees(c) }))
+ }
+
+ def urlFromTree(t:Tree) : URL =
+ t match {
+ case Tree("URL", stuff) => urlFromTrees(stuff)
+ case Tree("Email", Seq(Tree("username", un),host)) => new URLEmail(stringFromTrees(un), hostFromTree(host))
+ case Tree("Path",stuff) => new URLPath(stuff.map(fromUrlChar).foldLeft("")(_ + _))
+ }
+
+ def urlFromTrees(t:Seq[Tree]) : URL =
+ t match {
+ case Seq(Tree(_,method), login, host, port, rest @_*) =>
+ new URLNormal(stringFromTrees(method),
+ None,
+ hostFromTree(host),
+ port match { case Tree("Port",port) => {
+ val q = stringFromTrees(port)
+ if (q.equals("")) None else Some(java.lang.Integer.parseInt(q))
+ }
+ case _ => None },
+ rest match { case Seq(Tree("Path",p), x@_*) => p.map(fromUrlChar).foldLeft("")(_ + _)
+ case _ => "" },
+ rest match { case Seq(_ , Tree("Path",r), x@_*) => Some(stringFromTrees(r))
+ case _ => None })
+ }
+
+ //fromUrlChar (Tree "%" [(Tree a [] _),(Tree b [] _)] _) = chr $ (fst $ head $ readHex (a++b))
+ // FIXME: problem here is the "/" vs "%2F" issue, so we "leave urls urlencoded"
+ def fromUrlChar(t:Tree) : String =
+ t match {
+ case Tree("%", Seq(Tree(a,Seq()),Tree(b,Seq()))) => "%"+a+b
+ case Tree(x,y) =>
+ if (x.length==1) x
+ else throw new RuntimeException("could not parse as URL char: " + t)
+ }
+
+ def paragraphsFromTrees(ts:Seq[Tree]) : Seq[Paragraph] =
+ consolidate(concatMap(paragraphsFromTree,ts))
+
+ def paragraphsFromTree(t:Tree) : Seq[Paragraph] =
+ consolidate (t match {
+ case Tree("Verbatim", Seq(indent,v)) => Seq(new P( Seq(new Verbatim(unindent(indent,unverbate(v))))))
+ case Tree("TextParagraph", Seq(Tree(_,text))) => Seq(new P(concatMap(textSequenceFromTree,text)))
+ case Tree("Pars", pars ) => concatMap(paragraphsFromTree,pars)
+ case Tree("HR", _ ) => Seq(HR)
+ case Tree("OL", a ) =>
+ Seq(new OL(a.map( (t:Tree) => t match { case Tree("LI",x) => paragraphsFromTrees(x)})))
+ case Tree("UL", a ) =>
+ Seq(new UL(a.map( (t:Tree) => t match { case Tree("LI",x) => paragraphsFromTrees(x)})))
+ case Tree("", _ ) => Seq()
+ case Tree("Blockquote", pars ) => Seq(Blockquote(paragraphsFromTrees(pars)))
+ case _ => throw new RuntimeException("unable to create [Paragraph] from " + t)
+ })
+
+ def unverbate (t:Tree) : String =
+ t match {
+ case Tree("Verbatim",x) => x.map(unverbate).foldLeft("")(_ + _)
+ case Tree("VerbatimBrace",Seq(x,y)) => unverbate(x)+" "+unverbate(y)
+ case Tree(t,Seq()) => t
+ }
+
+ def unindent (t:Tree,v:String) : String =
+ t match {
+ case Tree("I", indent) => unindent_(indent.length+1, v)
+ }
+
+ private def unindent_ (i:Int,v:String) : String =
+ if (v.length==0) ""
+ else if (v.charAt(0) == '\n') "\n"+unindent_(i, drop_(i, v.substring(1)))
+ else v.charAt(0)+unindent_(i, v.substring(1))
+
+ private def drop_(n:Int, x:String) : String = {
+ val x_ : Seq[Char] = x;
+ if (n==0) x
+ else (x_ match {
+ case Seq('\n', r@_*) => x
+ case Seq() => ""
+ case Seq(a, b@_*) => drop_(n-1, x.substring(1))
+ })
+ }
+
+ def consolidate(x:Seq[Paragraph]) : Seq[Paragraph] =
+ x match {
+ case Seq() => Seq()
+ case Seq(a) => Seq(a)
+ case Seq(OL(Seq()), x@_*) => consolidate(x)
+ case Seq(UL(Seq()), x@_*) => consolidate(x)
+ case Seq(OL(a), OL(b), x@_*) => consolidate(Seq(OL(a++b))++x)
+ case Seq(UL(a), UL(b), x@_*) => consolidate(Seq(UL(a++b))++x)
+ case Seq(a, b @_*) => Seq(a)++consolidate(b)
+ }
+
+ def intFromTrees(t:Seq[Tree]) : Int =
+ java.lang.Integer.parseInt(stringFromTrees(t))
+ def stringFromTree(t:Tree) : String =
+ t match { case Tree(h,c) => h++concatMap(stringFromTree,c) }
+ def stringFromTrees(ts:Seq[Tree]) : String =
+ ts.map(stringFromTree).foldLeft("")(_ + _)
+
+}
+
+class Doc (val header:Header, val sections:Seq[Section]) extends ToHtml {
+ override def toHtml =
+ "<!-- This document was AUTOMATICALLY GENERATED from wix source -->\n"+
+ "<!-- it is probably not a wise idea to edit it directly -->\n\n"+
+ "<html>\n"+
+ "<head>\n"+
+ Html.style+
+ // FIXME: title tag
+ "</head>\n"+
+ "<body>\n"+ // tell jsmath we will escape stuff manually
+ Html.jsMath+ // FIXME: only put this in if math appears on the page
+ "<center><table><tr><td width=600>\n"+
+ mapToHtml(sections)+
+ "<br><br>\n"+
+ "<table width=100% class=footer><tr><td align=left>"+
+ "<img src='"+Html.printIconBase64+"'></td>"+
+ "<td align=right><span class='signature'>rendered from "+
+ "<a href=http://www.megacz.com/software/wix>"+
+ "W<span style='vertical-align:-20%'>I</span>X</a></span></div></td></tr></table>\n"+
+ "</td></tr></table></center>\n"+
+ "</body></html>"
+}
+
+class Header()
+
+class Section (val level:Int, val header:Seq[Text], val paragraphs:Seq[Paragraph]) extends ToHtml {
+ def toHtml = "\n<h"+((level+1))+">\n"+(mapToHtml(header))+"\n</h"+((level+1))+">\n"+(mapToHtml(paragraphs)) }
+
+abstract class Paragraph extends ToHtml
+ case class P (val body :Seq[Text] ) extends Paragraph { override def toHtml = stag_("p",body) }
+ case object HR extends Paragraph { override def toHtml = stag("hr") }
+ case class OL (val items:Seq[Seq[Paragraph]] ) extends Paragraph {
+ override def toHtml = stag0("ol", items.map( (s:Seq[Paragraph]) => stag_("li", s) ).foldLeft("")(_ + _)) }
+ case class UL (val items:Seq[Seq[Paragraph]] ) extends Paragraph {
+ override def toHtml = stag0("ul", items.map( (s:Seq[Paragraph]) => stag_("li", s) ).foldLeft("")(_ + _)) }
+ case class Blockquote (val body :Seq[Paragraph] ) extends Paragraph {
+ override def toHtml =
+ "\n<table class=blockquote border=0 cellpadding=5px>\n"+
+ "<tr><td valign=top><image src='"+Html.quoteIconBase64+"'></td>\n"+
+ "<td class=warn>\n"+
+ mapToHtml(body)+
+ "</td></tr></table>\n"
+ }
+
+abstract class Style
+ case object TT extends Style
+ case object Underline extends Style
+ case object Superscript extends Style
+ case object Subscript extends Style
+ case object Strikethrough extends Style
+ case object Italic extends Style
+ case object Bold extends Style
+ case object Highlight extends Style
+
+abstract class Text extends ToHtml
+ case object WS extends Text { def toHtml = " " }
+ case class Chars(val body:String) extends Text { def toHtml = htmlEscape(body) }
+ case class Quotes(val body:Seq[Text]) extends Text { def toHtml = "“"+mapToHtml(body)+"”" }
+ case class GlyphText(val body:Glyph) extends Text { override def toHtml = body.toHtml }
+ case class Math(val body:String) extends Text { override def toHtml = "<span class=math>" +body+ "</span>" }
+ case class Verbatim(val body:String) extends Text { override def toHtml = pre(body) }
+ case class Link(val url:URL, val body:Seq[Text]) extends Text { override def toHtml = link(url.toString, body) }
+ case class Footnote(val body:Seq[Text]) extends Text { override def toHtml = throw new Exception() }
+ case class Keyword(val body:Seq[Text]) extends Text { override def toHtml = tag_("tt", body) }
+ case class SubPar(val body:Seq[Paragraph]) extends Text { override def toHtml = stag_("p", body) }
+ case class Styled(val style:Style, val body:Seq[Text]) extends Text {
+ override def toHtml =
+ style match {
+ case Underline => tag_("u", body)
+ case TT => tag_("tt", body)
+ case Italic => tag_("i", body)
+ case Strikethrough => tag_("strike", body)
+ case Superscript => tag_("sup", body)
+ case Subscript => tag_("sub", body)
+ case Bold => tag_("b", body)
+ case Highlight => "<span class=highlight>"+mapToHtml(body)+"</span>"
+ }
+ }
+ case class Command(val command:String, val body:Seq[Text]) extends Text {
+ override def toHtml =
+ command match {
+ case "comment" => ""
+ case "url" => "<tt>"+link(mapToHtml(body),body)+"</tt>"
+ case "WiX" => "W<span style='vertical-align:-20%'>I</span>X"
+ case "TeX" => "T<span style='vertical-align:-20%'>E</span>X"
+ case "red" => "<font color=red>"+mapToHtml(body)+"</font>"
+ case "orange" => "<font color=orange>"+mapToHtml(body)+"</font>"
+ case "green" => "<font color=green>"+mapToHtml(body)+"</font>"
+ case "sc" => "<sc>"+mapToHtml(body)+"</sc>"
+ case "image" => "<img src='"+mapToHtml(body)+"'/>"
+ case "imagec" => "<center><img src='"+mapToHtml(body)+"'/></center>"
+ case "image2" => "<img width=180px src='"+mapToHtml(body)+"'/>"
+ case "image3" => "<img width=200px src='"+mapToHtml(body)+"'/>"
+ case "image4" => "<center><img width=550px src='"+mapToHtml(body)+"'/></center>"
+ case "warn" => "\n<div class=warn>\n<table border=0 cellpadding=5px>\n"+
+ "<tr><td valign=top><image src='"+Html.warnIconBase64+"'></td>\n"+
+ "<td class=warn>\n"+
+ mapToHtml(body)+
+ "</td></tr></table></div>\n"
+ case "announce" => "\n<div class=announce>\n<table border=0 cellpadding=5px>\n"+
+ "<tr><td valign=top></td>\n"+
+ "<td class=warn>\n"+
+ mapToHtml(body)+
+ "</td></tr></table></div>\n"
+ case "br" => "\n<br/>\n"
+ case "cent" => "½"
+ case "euro" => "€"
+ // gross hack
+ case "ordinal" => {
+ val x = mapToHtml(body)
+ if (x.charAt(x.length-1) == '1') x+"<sup>"+"st"+"</sup>"
+ else if (x.charAt(x.length-1) == '2') x+"<sup>"+"nd"+"</sup>"
+ else if (x.charAt(x.length-1) == '3') x+"<sup>"+"rd"+"</sup>"
+ else x+"<sup>"+"th"+"</sup>"
+ }
+ // TO DO: use "unicode vulgar fractions" here
+ // directional quotes: see http://www.dwheeler.com/essays/quotes-in-html.html
+ /* u'1/2' : u'\u00BD',
+ // u'1/4' : u'\u00BC',
+ // u'3/4' : u'\u00BE',
+ // u'1/3' : u'\u2153',
+ // u'2/3' : u'\u2154',
+ // u'1/5' : u'\u2155',
+ // u'2/5' : u'\u2156',
+ // u'3/5' : u'\u2157',
+ // u'4/5' : u'\u2158',
+ // u'1/6' : u'\u2159',
+ // u'5/6' : u'\u215A',
+ // u'1/8' : u'\u215B',
+ // u'3/8' : u'\u215C',
+ // u'5/8' : u'\u215D',
+ // u'7/8' : u'\u215E',
+ */
+ case "fraction" => "<sup>"++body(0).toHtml++"</sup>"++"/"++"<sub>"++body(1).toHtml++"</sub>"
+ case "rfc" => "<tt><a href=http://tools.ietf.org/html/rfc"+mapToHtml(body)+">RFC"+mapToHtml(body)+"</a></tt>"
+ case "keystroke:command" => "⌘"
+ case "keystroke:shift" => "⇧"
+ case "keystroke:option" => "⌥"
+ case "keystroke:control" => "⌃"
+ case "keystroke:capslock" => "⇪"
+ case "keystroke:apple" => ""
+ case _ => throw new RuntimeException("unsupported command " + command)
+ }
+ }
+
+
+abstract class Glyph extends ToHtml
+ case object Euro extends Glyph { override def toHtml = "€" }
+ case object CircleR extends Glyph { override def toHtml = "¢" }
+ case object CircleC extends Glyph { override def toHtml = "®" }
+ case object TradeMark extends Glyph { override def toHtml = "™" }
+ case object ServiceMark extends Glyph { override def toHtml = "™" }
+ case object Emdash extends Glyph { override def toHtml = "—" }
+ case object Ellipsis extends Glyph { override def toHtml = "…" /* &cdots;? */ }
+ case object Cent extends Glyph { override def toHtml = "½" }
+ case object Daggar extends Glyph { override def toHtml = "†" }
+ case object DoubleDaggar extends Glyph { override def toHtml = "‡" }
+ case object Clover extends Glyph { override def toHtml = "⌘" }
+ case object Flat extends Glyph { override def toHtml = "⋖" }
+ case object Natural extends Glyph { override def toHtml = "⋗" }
+ case object Sharp extends Glyph { override def toHtml = "⋘" }
+ case object CheckMark extends Glyph { override def toHtml = "✓" }
+ case object XMark extends Glyph { override def toHtml = "✗" }
+ case object LeftArrow extends Glyph { override def toHtml = "←" }
+ case object RightArrow extends Glyph { override def toHtml = "→" }
+ case object DoubleLeftArrow extends Glyph { override def toHtml = "&#;" /* FIXME */ }
+ case object DoubleRightArrow extends Glyph { override def toHtml = "&#;" /* FIXME */ }
+ case object DoubleLeftRightArrow extends Glyph { override def toHtml = "&#;" /* FIXME */ }
+ case object LeftRightArrow extends Glyph { override def toHtml = "&#;" /* FIXME */ }
+ case object Degree extends Glyph { override def toHtml = "&#;" /* FIXME */ }
+
+class Login(val user:String, val password:Option[String]) {
+ override def toString =
+ password match {
+ case None => user
+ case Some(x) => user+":"+urlEscape(x) }
+}
+
+abstract class URL
+ case class URLPath(val path:String) extends URL { override def toString = path }
+ case class URLEmail(val user:String, val host:Host) extends URL { override def toString = "mailto:"+user+"@"+host }
+ case class URLNormal(val method:String,
+ val login:Option[Login],
+ val host:Host,
+ val port:Option[Int],
+ val path:String,
+ val ref:Option[String]) extends URL {
+ override def toString =
+ method+"://"+
+ (login match {
+ case None => ""
+ case Some(log) => log+"@" })+
+ host+
+ "/"+
+ path+
+ (ref match {
+ case None => ""
+ case Some("") => ""
+ case Some(x) => "#"+x })
+
+ }
+
+
+abstract class Host
+ case class HostIP(val ip0:Int, val ip1:Int, val ip2:Int, val ip3:Int) extends Host {
+ override def toString = ip0+"."+ip1+"."+ip2+"."+ip3 }
+ case class HostDNS(val parts:Seq[String]) extends Host {
+ override def toString = joinStrings(parts, ".") }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
}
System.out.println(ANSI.green("copying: "+f.getPath()));
File dest_ = new File(outdir.getAbsolutePath()+File.separatorChar+suffix+"-");
+ new File(dest_.getParent()).mkdirs();
FileOutputStream fos = new FileOutputStream(dest_);
FileInputStream fis = new FileInputStream(f);
byte[] buf = new byte[1024];
Class.forName("Main").
getMethod("main", new Class[] { String[].class }).
invoke(null, new Object[] { new String[] { f.getAbsolutePath() } });
+ try {
new File(new File(outPath).getParent()).mkdirs();
PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(outPath+"+")));
pw.println(ret);
pw.close();
File dest = new File(outPath);
if (dest.exists()) {
- try {
Process p = Runtime.getRuntime().exec(new String[] {
"diff",
"-Bub",
/*else System.out.println(ANSI.blue(s));*/
}
p.waitFor();
- } catch (Exception e) {
- e.printStackTrace();
- }
}
new File(outPath+"+").renameTo(dest);
if (dest.lastModified() <= f.lastModified())
dest.setLastModified(f.lastModified()+1);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
}
}
--- /dev/null
+trait ToHtml {
+ def toHtml : String
+}
+
+object Html {
+
+ def style : String =
+ "\n<style>\n"+
+ " h1, h2, h3, h4 { font-family: 'Trebuchet MS', arial, verdana, sans-serif; width: 100% }\n"+
+ " h1 { font-size: 20pt; border-top: black 1px solid; }\n"+
+ " h2 { font-size: 16pt; border-top: silver 1px solid; }\n"+
+ " h3 { font-size: 12pt; }\n"+
+ " TH, TD, P, LI, DIV, SPAN {\n"+
+ " font-family: verdana, arial, sans-serif;\n"+
+ " font-size: 12px; \n"+
+ " text-decoration:none; \n"+
+ " }\n"+
+ " LI { margin-top: 5px; }\n"+
+ " body { color: #333333; }\n"+
+ " blockquote { font-style: italic; width: 100% }\n"+
+ " div.warn { border: 1px solid #ff; border-top: 5px solid #ff; background-color: #fbb; color: white; }\n"+
+ " td.warn { color: black; }\n"+
+ " div.announce { border: 1px solid green; background-color: #bfb; color: white; }\n"+
+ " td.announce { color: black; }\n"+
+ " div.footer {\n"+
+ " color: gray;\n"+
+ " border-top: 1px solid silver;\n"+
+ " font-size: 10px;\n"+
+ " }\n"+
+ " table.blockquote { margin: 5px; border: 1px #e6ddcb solid; background-color: #fbf2e0; width: 100% }\n"+
+ " a:link { text-decoration: none; color: blue; border-bottom:1px dotted; }\n"+
+ " a:visited { text-decoration: none; color: purple; border-bottom:1px dotted; }\n"+
+ " a:active { text-decoration: none; color: red; border-bottom:1px solid; }\n"+
+ " a:hover { text-decoration: none; border-bottom:1px solid; }\n"+
+ " table.footer { border-top: silver solid 1px; }\n"+
+ " span.signature { color: #bbb; }\n"+
+ " .signature a:link { color: #aaf; }\n"+
+ " .signature a:visited { color: #faa; }\n"+
+ " .signature a:hover { color: blue; border-bottom: 1px solid blue; }\n"+
+ " span.highlight { background: yellow; color: black; padding: 3px }\n"+
+ " div.pre {\n"+
+ " text-align: left;\n"+
+ " font-family: monospace;\n"+
+ " border-style: none;\n"+
+ " border-width: 2px 2px 2px 2px;\n"+
+ " border-color: #6666aa;\n"+
+ " color: #FFFFFF;\n"+
+ " background-color: #333333;\n"+
+ " margin-right: 25px;\n"+
+ " margin-left: 25px;\n"+
+ " padding: 10px;\n"+
+ " }\n"+
+ "</style>\n"
+
+ def quoteIconBase64 =
+ "data:image/png;base64,"+
+ "iVBORw0KGgoAAAANSUhEUgAAABAAAAAVCAYAAABPPm7SAAAABmJLR0QA/wD/AP+gvaeTAAAA"+
+ "CXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH1wQPAx0rP5obpAAAAWJJREFUOMvdlKFvg0AU"+
+ "xr8tVZ1cbU+04lUuwc2WypMX9AxiomoJ/RNYqmaaYKpb5kBys1UkWBCdKLqVs8zwFnrQmYol"+
+ "+xLC8X55H/e+wAENhWGoPM/zT6eThQ4lSWJf5EEQuAAqAJXv+95vXCkVtpwZuq4bmM1pmlrM"+
+ "LctKWzuwbTsBUNX3lph3Njfd6/WZ9vv9iHkYhsrkPa21zQ9aa9v3fRsAFovFK9cMPgIApdT7"+
+ "eDz+RB1Y1XEBwEVe54ZbXKm/N7haNwCQ5zmtVqtnE0op49lspgFgPp+/dfF/EGIPAAaDgZBS"+
+ "xgBwPB7vd7vdIwD0+/2v5ry8juNYmgY/P1GWZQ9sQER3XOcwD4fDkA2IqGiNwN8+ERVNY/Pt"+
+ "RFQIIcozg+Vy+VKW5dDcMmuz2ThFURAATKfTj7MQ1+v1Ezc7jrMVQpTmocOjOY6znUwmRSvR"+
+ "KIpknud0KfEoimSWZQ/N2jfhtb1AvGklDQAAAABJRU5ErkJggg=="
+
+ def warnIconBase64 =
+ "data:image/png;base64,"+
+ "iVBORw0KGgoAAAANSUhEUgAAABQAAAAREAYAAACN1FD9AAAABmJLR0QA/wD/AP+gvaeTAAAA"+
+ "CXBIWXMAAABIAAAASABGyWs+AAAACXZwQWcAAAAUAAAAEQDeTN6UAAAD/ElEQVRIx8WUbUyV"+
+ "ZRjHf+chxDi8yZcDa0mKh/wSuNE5eNYHZBiRwoespayEkdgx22hmjB1glTlenDYlMccAX1cb"+
+ "CDFlg+aQ1JGEazGWpixdbbweDucctPF2PNxXX3pw00gL09+X59m1+77+v+d+nueCBRJ+ECAs"+
+ "78dnAPp2nLEDtMdpPwMYZhfaf8GU1gLsixwdBRDRr7lRAHmxT0zs+UKAuKmhXgDfGleAYZVh"+
+ "lXynC/Z1AYyeX+IFiLjw+MwWAxDQuBqgNVsX+iNta/nWcnV0vD+9Ir1CndDre68AfDH42PzW"+
+ "2QDW79UFxkxh6WHp8o5a5lznXCfJ/nPX867nSYArO7AssEyOjOwE8GfGZwC84Pu3edrDLgzK"+
+ "AFj06q7DAPt/1evBK4szizMlpXy4tqO2g/MHa9sm2ibwP23M78zvFE0rBAg4U24DqFpieAqA"+
+ "6kd+cjuiAAqu6Sfn/n55/fJ6CZOc6YHpATmkNWgNWoOIZtSMmlFE1d9ae2utvDt21ZRiSpHV"+
+ "+r43LwFkBT4ysehigKgDvxcC3P5QD5qxNtU11anj8hfhg+GD4YMixipjlbFK5pjyHok8Enn3"+
+ "m7yyG2DAEdIFYLy9YMHDLoBjK/WA8fhkb7JXtcqMnJbT4tJFYlpiWmJaRKJio2KjYu8KSp9K"+
+ "UAly2ZtpSbWkql/0PrsuAJR3/2cx65cA1qujRgBV6HpWy9fy5dydOz2JPYlqQO4hwZ/gT/CL"+
+ "mB1mh9kh93Enq2tP1x7V4wo0pBnS5KehVwBmPjA7AcxtDy2mBQIYPmk/B/CDb26MxGw5teWU"+
+ "+krmIdWaak21iiR2JHYkdsi83G7KtmRb1HG9b+PHAK2lJAEQ9EDBt88C5BybGyOLQgtCC8Su"+
+ "gkesI1axzRd8Mvdk7slckeoN1RuqN8wvODsyVDNUIxaXP0SFKHlfz8l4GSDTfd+B6Tdh5QCh"+
+ "hUUvAlR8o9eDI4qSipLkJcOEqdvUzaX5HmzSNGmaNMFN+037TTvgxInzb96QKTovOo/LRmuJ"+
+ "r8QnFr3+2esAB/IX9wAEjd+3cfc+gD2euTFSv2zzss0SIdnTG6c3SqU8AFulrdJWKRJSElIS"+
+ "UiLia/Y1+5r/YcNHM9tntku1e2KFZ4VHAvXcwnyAksY5MfNvAOYdQ+8BzHytL5warDtRd2I2"+
+ "QjWNh46HqjR11LvGu0aVqUOei56LYlD7PRWeCmlWpe4p95R87h66Yb9hV+7+o71BvUHKoWyj"+
+ "jlGHSlbPOTc5N6kkFTqybWSbeksxfHb4rGqd9Q3nDOdI5lRHTXtNu6rWc/vfAJjoXHoNYOl6"+
+ "Q70CaFEpYwAZhof+m/5nvv0UoDFLW/UagCXuSQvdSzwAiTv/BGXg1AxNKyCeAAAAAElFTkSu"+
+ "QmCC"
+
+ def printIconBase64 =
+ "data:image/png;base64,"+
+ "iVBORw0KGgoAAAANSUhEUgAAAA8AAAAOCAMAAADHVLbdAAAALHRFWHRDcmVhdGlvbiBUaW1l"+
+ "AEZyaSAxOSBTZXAgMjAwMyAxODozOTozMiAtMDAwME2jAt8AAAAHdElNRQfTCRMRKABXeznM"+
+ "AAAACXBIWXMAAAsSAAALEgHS3X78AAAABGdBTUEAALGPC/xhBQAAACRQTFRF////AAAA7+/v"+
+ "3t7ezs7OtbW1ra2tlJSUnJycEBAQKSkphISEbGtEogAAAAF0Uk5TAEDm2GYAAABLSURBVHja"+
+ "nY1BEsAgCAMDKm31//81qB3l6h4Y4mYQQNvAmXNv7W/gytcTRj6pppQHNWoW6JWS13Ix86yr"+
+ "O7MEPkDWuWLPK/7hoYEOxksDsk8eppEAAAAASUVORK5CYII="
+
+ def emailIconBase64 =
+ "data:image/png;base64,"+
+ "iVBORw0KGgoAAAANSUhEUgAAABUAAAAOCAMAAAD32Kf8AAAALHRFWHRDcmVhdGlvbiBUaW1l"+
+ "AEZyaSAxOSBTZXAgMjAwMyAxODo0MjowOSAtMDAwMBDwv7IAAAAHdElNRQfTCRMRKhqYL6I0"+
+ "AAAACXBIWXMAAAsSAAALEgHS3X78AAAABGdBTUEAALGPC/xhBQAAADBQTFRF////AAAAhISE"+
+ "9/fv9/f3////CAgI7+/v1tbOxsa9tbWtpaWclJSMlJSUe3t7a2trxDv8WgAAAAF0Uk5TAEDm"+
+ "2GYAAABiSURBVHjabc9JEoAgDETRpNOKs/e/rSEYF8jfhHpQFAi6JHpHBg5VOdIj+KeXkgO9"+
+ "tUj/Bldo3WdRnktt3fbjgoWKK5EKsunkyryCn86Oxsj/UZqKEkFmNF+mvieFdSK16wFr7QK5"+
+ "tASqkwAAAABJRU5ErkJggg=="
+
+ def pdfIconBase64 =
+ "data:image/png;base64,"+
+ "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAK3RFWHRDcmVhdGlvbiBUaW1l"+
+ "AFRodSA2IE5vdiAyMDAzIDE1OjMwOjAwIC0wMDAwSwt8PwAAAAd0SU1FB9MLBg8fD1x8/t4A"+
+ "AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAEZ0FNQQAAsY8L/GEFAAAAQlBMVEX///9zc3Nra2uE"+
+ "hITGxsaUlJScnJx7e3uMjIz////39/fv7+/n5+fe3t69vb1jY2OlpaXW1ta1tbVaWlqtra3O"+
+ "zs5w48BYAAAAAXRSTlMAQObYZgAAAIdJREFUeNpNz1ESBBEMBNCRwSCJMOT+V10WW9vKz6uO"+
+ "iusa0R137ShxjImwHsnSMCbEX6dPiIje+SUKC6hav8CbAQlZD/RmJ1Tdz6oTwETMfAByK0hT"+
+ "1kgH7E1phFfjdvS2JlJpN8RAyEHMhgJgaexJTBPyA1JiGoD0BXgg2H8wcFs3/jDvPB+sOwir"+
+ "+o6iKQAAAABJRU5ErkJggg=="
+
+ def jsMath =
+ "<script> jsMath = { showFontWarnings: false } </script>\n"+
+ "<script src='/jsmath/easy/load.js'></script>\n"+
+ "<span id='tex2math_off'></span>\n"+
+ "<NOSCRIPT> <DIV STYLE='color:#CC0000; text-align:center'> "+
+ "<B>Warning: <A HREF='http://www.math.union.edu/locate/jsMath'>jsMath</A> "+
+ "requires JavaScript to process the mathematics on this page.<BR> If your "+
+ "browser supports JavaScript, be sure it is enabled.</B> </DIV> <HR> </NOSCRIPT>\n"
+
+ def urlEscape (s:Seq[Char]) =
+ s.map(urlEscapeChar).foldLeft("")(_ + _)
+
+ def htmlEscape (s:Seq[Char]) =
+ s.map(htmlEscapeChar).foldLeft("")(_ + _)
+
+ private def urlEscapeChar (c:Char) : String =
+ c match {
+ // non-alphanumerics which may appear unescaped
+ case '$' => "$"
+ case '-' => "-"
+ case '_' => "_"
+ case '.' => "."
+ case '!' => "!"
+ case '*' => "*"
+ case '\'' => "\'"
+ case '(' => "("
+ case ')' => ")"
+ case ',' => ","
+
+ // technically these aren't allowed by RFC, but we include them anyways
+ case '/' => "/"
+ case ';' => ";"
+ case '&' => "&"
+ case '=' => "="
+
+ // FIXME: this will wind up "disencoding" a %-encoded question mark
+ case '?' => "?"
+
+ case _ => {
+ if ((c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9'))
+ 'c'+""
+ else {
+ val s = "00"+java.lang.Integer.toString(c & 0xff, 16)
+ return '%'+s.substring(s.length()-1, 2)
+ }
+ }
+ }
+
+ private def htmlEscapeChar (c:Char) =
+ c match {
+ case '<' => "<"
+ case '>' => ">"
+ case '&' => "&"
+ case '\'' => "'"
+ case '\"' => """
+ case c => ""+c
+ }
+
+ def mapToHtml[A <: ToHtml](h:Seq[A]) : String =
+ h.map((s:A) => s.toHtml).foldLeft("")(_ + _)
+
+ def joinStrings(strings:Seq[String], separator:String) =
+ if (strings.length == 0) ""
+ else if (strings.length == 1) strings(0)
+ else strings(0)+strings.tail.map( (s:String) => separator+s ).foldLeft("")(_ + _)
+
+ def stag(t:String) = "\n<"+t+"></"+t+">\n"
+ def stag(t:String, body:ToHtml) = "\n<"+t+">\n"+body.toHtml+"\n</"+t+">\n"
+ def stag0(t:String, body:String) = "\n<"+t+">\n"+body+"\n</"+t+">\n"
+ def tag(t:String, body:ToHtml) = "<"+t+">"+body.toHtml+"</"+t+">"
+ def stag_(t:String, body:Seq[ToHtml]) = "\n<"+t+">\n"+mapToHtml(body)+"\n</"+t+">\n"
+ def tag_(t:String, body:Seq[ToHtml]) = "<"+t+">"+mapToHtml(body)+"</"+t+">"
+
+ def link(ref:String, body:Seq[ToHtml]) : String = {
+ val img = "style='vertical-align: text-bottom;' border=0 "
+ val icon = if (ref.endsWith(".pdf")) "<img "+img+" src='"+Html.pdfIconBase64+"'> "
+ else if (ref.startsWith("mailto:")) "<img "+img+" src='"+Html.emailIconBase64+"'> "
+ else ""
+ return "<a href='"+ref+"'>"+icon+mapToHtml(body)+"</a>"
+ }
+
+ private def pre_(c:Char) : String =
+ c match {
+ case ' ' => " "
+ case '\n' => "<br/>\n"
+ case a => htmlEscapeChar(a)
+ }
+
+ def pre(x:String) : String =
+ "\n<div class=pre style='white-space:nowrap'>"+(x.map(pre_).foldLeft("")((a,b:String) => a + b))+"\n</div>\n"
+
+}
+
--- /dev/null
+import edu.berkeley.sbp.Tree
+import edu.berkeley.sbp.scala.SBP
+
+class Main extends TreeToString {
+
+ override def run(t:edu.berkeley.sbp.Tree[String]) : String =
+ Doc.docFromTree(SBP.mkTree(t)).toHtml
+}
+
+object Main {
+
+ def main(args:Array[String]) =
+ ScalaHelper.main(args, new Main())
+
+}
--- /dev/null
+// Copyright 2011 the Contributors, as shown in the revision logs.
+// Licensed under the Apache Public Source License 2.0 ("the License").
+// You may not use this file except in compliance with the License.
+import edu.berkeley.sbp.*;
+import edu.berkeley.sbp.misc.*;
+import edu.berkeley.sbp.util.*;
+import edu.berkeley.sbp.meta.*;
+import edu.berkeley.sbp.chr.*;
+import java.io.*;
+
+public class ScalaHelper {
+ public static boolean isNull(Object o) { return o==null; }
+
+ private static CharParser parser = null;
+ static {
+ synchronized(ScalaHelper.class) {
+ if (parser == null) {
+ try {
+ // FIXME: bundle this into the jarfile
+ InputStream grammarFile = ScalaHelper.class.getClassLoader().getResourceAsStream("wix.g");
+ Tree<String> res = new CharParser(GrammarAST.getMetaGrammar())
+ .parse(grammarFile).expand1();
+ Union grammar = GrammarAST.buildFromAST(res, "s", new GrammarAST.ImportResolver() {
+ public InputStream getImportStream(String filename) {
+ return this.getClass().getClassLoader().getResourceAsStream(filename);
+ }
+ });
+ parser = new CharParser(grammar);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+
+ public static Tree parseFile(String targetFile) throws Throwable {
+ Tree ret = null;
+ try {
+ Reader r = new InputStreamReader(new FileInputStream(targetFile));
+ Input input = new CharInput(new IndentingReader(r, CharAtom.left, CharAtom.right));
+ ret = parser.parse(input).expand1();
+ } catch (Throwable e) {
+ e.printStackTrace();
+ throw e;
+ }
+ if (ret==null) throw new NullPointerException("CharParser returned null");
+ return ret;
+ }
+
+ public static void main(String[] argv, TreeToString t2s) throws Throwable {
+ if (argv.length != 2) {
+ System.out.println("usage: java -jar wix.jar [-v] <indir> <outdir>");
+ // FIXME: implement this
+ System.out.println(" | java -jar wix.jar [-v] <infile>.wix");
+ System.out.println("");
+ // FIXME: implement these
+ System.out.println(" -v print text as it is parsed (sbp.verbose=true)");
+ System.out.println(" -vv like -v, but also dump parse tree");
+ System.out.println(" -vvv like -vv, but also dump wix tree");
+ System.exit(-1);
+ return;
+ }
+ File indir = new File(argv[0]);
+ File outdir = new File(argv[1]);
+ if (!indir.isDirectory()) {
+ process(new File(indir.getParent()), indir.getName(), outdir, t2s);
+ } else {
+ process(indir, "", outdir, t2s);
+ }
+ }
+
+ private static void process(File indir, String suffix, File outdir, TreeToString t2s) throws Throwable {
+ File f = new File(indir.getAbsolutePath()+File.separatorChar+suffix);
+ //System.out.println(f+" "+indir + " " + suffix + " " + outdir);
+ if (!f.exists()) return;
+ if (f.isDirectory()) {
+ for (String s : f.list())
+ process(indir, suffix + File.separatorChar + s, outdir, t2s);
+ return;
+ }
+ if (!f.getPath().endsWith(".wix")) {
+ boolean skip = false;
+ if (f.getName().equals(".DS_Store")) skip = true;
+ if (f.getName().equals("._.DS_Store")) skip = true;
+ if (f.getName().endsWith("-")) skip = true;
+ if (!skip) {
+ File dest = new File(outdir.getAbsolutePath()+File.separatorChar+suffix);
+ if (dest.exists() && dest.lastModified()==f.lastModified() && dest.length()==f.length()) {
+ System.out.println(ANSI.yellow("no change: "+f.getPath()));
+ return;
+ }
+ System.out.println(ANSI.green("copying: "+f.getPath()));
+ File dest_ = new File(outdir.getAbsolutePath()+File.separatorChar+suffix+"-");
+ new File(dest_.getParent()).mkdirs();
+ FileOutputStream fos = new FileOutputStream(dest_);
+ FileInputStream fis = new FileInputStream(f);
+ byte[] buf = new byte[1024];
+ while(true) {
+ int numread = fis.read(buf, 0, buf.length);
+ if (numread==-1) break;
+ fos.write(buf, 0, numread);
+ }
+ fos.close();
+ fis.close();
+ dest_.renameTo(dest);
+ dest.setLastModified(f.lastModified());
+ }
+ } else {
+ String out = "== " + suffix + " ";
+ while(out.length() < 75) out+="=";
+ System.out.println(ANSI.yellow(out));
+ //System.out.println();
+ String outPath = outdir.getAbsolutePath()+File.separatorChar+suffix;
+ outPath = outPath.substring(0, outPath.length()-".wix".length())+".html";
+ if (new File(outPath).exists() && new File(outPath).lastModified() > f.lastModified()) return;
+
+ Tree tree = parseFile(f.getAbsolutePath());
+ String ret = t2s.run(tree);
+ try {
+ new File(new File(outPath).getParent()).mkdirs();
+ PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(outPath+"+")));
+ pw.println(ret);
+ pw.flush();
+ pw.close();
+ File dest = new File(outPath);
+ if (dest.exists()) {
+ Process p = Runtime.getRuntime().exec(new String[] {
+ "diff",
+ "-Bub",
+ dest.getAbsolutePath(),
+ new File(outPath+"+").getAbsolutePath()
+ });
+ BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
+ br.readLine();
+ br.readLine();
+ for(String s = br.readLine(); s != null; s = br.readLine()) {
+ if (s.startsWith("+")) System.out.println(ANSI.green(s));
+ else if (s.startsWith("-")) System.out.println(ANSI.red(s));
+ /*else System.out.println(ANSI.blue(s));*/
+ }
+ p.waitFor();
+ }
+ new File(outPath+"+").renameTo(dest);
+ if (dest.lastModified() <= f.lastModified())
+ dest.setLastModified(f.lastModified()+1);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public static Object ret;
+ public static void putBack(String o) { ret = o; }
+}
--- /dev/null
+import edu.berkeley.sbp.*;
+
+public interface TreeToString {
+ public abstract String run(Tree<String> t);
+}
\ No newline at end of file