Commit 1914aec8 authored by Jens Korinth's avatar Jens Korinth

Fix broken main man page and update

* man tapasco page is broken, was too optimistic that man would be able
  to parse the ASCII format directly (works on Darwin)
* replaced hard-coded strings with mini-markup language
* Formatters can produce any kind of representation
* same code is used to produce CLI output and man pages
* will simplify the updates in the future
parent 3959bfb3
This diff is collapsed.
......@@ -45,7 +45,7 @@ private object BasicParsers {
val quotedString: Parser[String] =
(quote ~/ CharPred(!quoteChars.contains(_)).rep.! ~ quote)
.opaque("quoted string")
val qstring: Parser[String] =
(string | quotedString)
.opaque("quoted or unquoted string")
......@@ -68,7 +68,7 @@ private object BasicParsers {
val double: Parser[Double] = dblstr.map(_.toDouble)
.opaque("floating point number")
val frequency: Parser[Frequency] = double.opaque("frequency in MHz")
implicit def toPath(s: String): Path = Paths.get(s)
......
package de.tu_darmstadt.cs.esa.tapasco.parser
import scala.language.implicitConversions
sealed trait FormatObject {
def /(other: FormatObject) = Concat(this, other)
def concat(other: FormatObject) = Concat(this, other)
def ~(other: FormatObject) = Join(this, other)
def join(other: FormatObject) = Join(this, other)
def &(other: FormatObject) = Break(this, other)
def break(other: FormatObject) = Break(this, other)
}
final case class Header(title: String, section: ManSection, date: String, source: String) extends FormatObject
sealed class Section(val name: String, val content: FormatObject) extends FormatObject
object Section { def apply(name: String, content: FormatObject): FormatObject = new Section(name, content) }
final case class URL(url: String, text: String, trailer: String = "") extends FormatObject
final case class T(text: String) extends FormatObject
final case class Arg(arg: FormatObject, desc: FormatObject) extends FormatObject
final case class B(fo: FormatObject) extends FormatObject
final case class BI(fo: FormatObject) extends FormatObject
final case class BR(fo: FormatObject) extends FormatObject
final case class I(fo: FormatObject) extends FormatObject
final case class IB(fo: FormatObject) extends FormatObject
final case class IR(fo: FormatObject) extends FormatObject
final case class RB(fo: FormatObject) extends FormatObject
final case class RI(fo: FormatObject) extends FormatObject
final case class SB(fo: FormatObject) extends FormatObject
final case class SM(fo: FormatObject) extends FormatObject
final case class Block(fo: FormatObject, width: Int = 80) extends FormatObject
final case class Indent(fo: FormatObject, depth: Int = 2) extends FormatObject
final case class Concat(fo1: FormatObject, fo2: FormatObject) extends FormatObject
final case class Join(fo1: FormatObject, fo2: FormatObject) extends FormatObject
final case class Break(fo1: FormatObject, fo2: FormatObject) extends FormatObject
final case class Name(program: String, onelineDesc: String) extends Section("Name", T(s"$program - $onelineDesc"))
final case class Synopsis(shortUsage: FormatObject) extends Section("Synopsis", shortUsage)
object FormatObject {
implicit def toFormatObject(s: String): FormatObject = T(s)
implicit def toString(fo: FormatObject)(implicit formatter: Formatter[String]): String =
formatter(fo)
}
package de.tu_darmstadt.cs.esa.tapasco.parser
import scala.util.Properties.{lineSeparator => NL}
/** Generic formatter for FormatObjects. */
trait Formatter[A] extends Function[FormatObject, A] {
def format(header: Header): A
def format(section: Section): A
def format(url: URL): A
def format(text: T): A
def format(arg: Arg): A
def format(b: B): A
def format(bi: BI): A
def format(br: BR): A
def format(i: I): A
def format(ib: IB): A
def format(ir: IR): A
def format(rb: RB): A
def format(ri: RI): A
def format(sb: SB): A
def format(sb: SM): A
def format(b: Block): A
def format(in: Indent): A
def format(p: Concat): A
def format(p: Join): A
def format(p: Break): A
def apply(fo: FormatObject): A = fo match {
case x: Header => format(x)
case x: Section => format(x)
case x: URL => format(x)
case x: T => format(x)
case x: Arg => format(x)
case x: B => format(x)
case x: BI => format(x)
case x: BR => format(x)
case x: I => format(x)
case x: IB => format(x)
case x: IR => format(x)
case x: RB => format(x)
case x: RI => format(x)
case x: SB => format(x)
case x: SM => format(x)
case x: Block => format(x)
case x: Indent => format(x)
case x: Concat => format(x)
case x: Join => format(x)
case x: Break => format(x)
}
}
/** Formatter producing strings; used for CLI output. */
class StringFormatter extends Formatter[String] {
protected val ARG_WIDTH = 80
protected val ARG_LEFT = 29
protected val ARG_RIGHT = 50
def isWhitespace(c: Char): Boolean = " \t\n".contains(c)
private def wordwrap(s: String, line: String)(width: Int): Seq[String] = s.length match {
case 0 => Seq(line)
case _ if line.length >= width => line +: wordwrap(s, "")(width)
case _ =>
val rest = s.dropWhile(line.length == 0 && isWhitespace(_))
val word = rest.takeWhile(c => if (isWhitespace(rest(0))) isWhitespace(c) else !isWhitespace(c))
if (line.length + word.length > width) {
line +: wordwrap(rest, "")(width)
} else {
wordwrap(s.drop(word.length), line ++ word)(width)
}
}
def extend(l: String, width: Int): String = l ++ (" " * (width - l.length))
def format(header: Header): String = ""
def format(name: Name): String = s"${name.program} - ${name.onelineDesc}"
def format(section: Section): String = "%s%s%s%s".format(NL, section.name.toUpperCase, NL, apply(Indent(section.content)))
def format(url: URL): String = s"${url.url} ${url.trailer}"
def format(t: T): String = t.text
def format(a: Arg): String = {
val l = apply(Block(a.arg, ARG_LEFT)).split(NL)
val r = apply(Block(a.desc, ARG_RIGHT)).split(NL)
val m = Seq(l.length, r.length).max
val le = l ++ (0 until m - l.length map (_ => ""))
val re = r ++ (0 until m - r.length map (_ => ""))
(le zip re).map { case (l, r) => extend(l.take(ARG_LEFT), ARG_LEFT) ++ " " ++ r.take(ARG_RIGHT) }
.mkString(NL)
}
def format(b: B): String = "**%s**".format(apply(b.fo))
def format(bi: BI): String = apply(B(bi.fo))
def format(br: BR): String = apply(B(br.fo))
def format(i: I): String = "_%s_".format(apply(i.fo))
def format(ib: IB): String = apply(I(B(ib.fo)))
def format(ir: IR): String = apply(I(ir.fo))
def format(rb: RB): String = apply(B(rb.fo))
def format(ri: RI): String = apply(I(ri.fo))
def format(sb: SB): String = apply(SM(sb.fo))
def format(sb: SM): String = "%s".format(apply(sb.fo).toLowerCase)
def format(b: Block): String = wordwrap(apply(b.fo), "")(b.width) mkString NL
def format(in: Indent): String = apply(in.fo).split(NL).map(l => "%s%s".format(" " * in.depth, l)).mkString(NL)
def format(p: Concat): String = Seq(apply(p.fo1), apply(p.fo2)) mkString
def format(p: Join): String = Seq(apply(p.fo1), apply(p.fo2)) mkString " "
def format(p: Break): String = Seq(apply(p.fo1), apply(p.fo2)).mkString(NL)
}
/** Formatter producing man page format. */
class ManPageFormatter extends StringFormatter {
override def format(header: Header) =
s".TH ${header.title} ${header.section: Int} ${header.source} ${header.section.manual}"
override def format(section: Section): String =
Seq(s".SH ${section.name.toUpperCase}",
apply(section.content)) mkString NL
override def format(indent: Indent): String =
Seq(".RS", apply(indent.fo), ".RE") mkString NL
override def format(a: Arg): String = apply(Indent(a.arg & Indent(a.desc)))
override def format(b: B): String = Seq(".B %s".format(apply(b.fo)), "") mkString NL
override def format(b: BI): String = Seq(".BI %s".format(apply(b.fo)), "") mkString NL
override def format(b: BR): String = Seq(".BR %s".format(apply(b.fo)), "") mkString NL
override def format(i: I): String = Seq(".I %s".format(apply(i.fo)), "") mkString NL
override def format(i: IB): String = Seq(".IB %s".format(apply(i.fo)), "") mkString NL
override def format(i: IR): String = Seq(".IR %s".format(apply(i.fo)), "") mkString NL
override def format(r: RB): String = Seq(".RB %s".format(apply(r.fo)), "") mkString NL
override def format(r: RI): String = Seq(".RI %s".format(apply(r.fo)), "") mkString NL
override def format(s: SB): String = Seq(".SB %s".format(apply(s.fo)), "") mkString NL
override def format(s: SM): String = Seq(".SM %s".format(apply(s.fo)), "") mkString NL
override def format(t: T): String =
super.format(t).replace("-", """\-""")
}
object StringFormatter extends StringFormatter
object ManPageFormatter extends ManPageFormatter
......@@ -30,7 +30,7 @@ private object GlobalOptions {
def help: Parser[(String, String)] =
(longShortOption("h", "help") | IgnoreCase("help") | IgnoreCase("usage")) ~
(ws1 ~ string).? map { case (h, topic) => { Usage(topic); ("Help", Usage()) } }
(ws1 ~ string).? map { case (h, topic) => { Usage.topic(topic); ("Help", Usage()) } }
def verbose: Parser[(String, String)] =
(longShortOption("v", "verbose", Some("Verbose")) ~/ (ws1 ~ quotedString.opaque("verbose mode as quoted string")).? ~ ws)
......@@ -77,7 +77,7 @@ private object GlobalOptions {
def globalOptionsSeq: Parser[Seq[(String, _)]] =
ws ~ (help | verbose | dirs | inputFiles | slurm | parallel | dryRun | maxThreads).rep
def globalOptions: Parser[Configuration] =
globalOptionsSeq map (as => mkConfig(as))
......
......@@ -17,7 +17,7 @@ private object ImportParser {
private def zip: Parser[Path] = path.opaque("path to .zip file containing IP-XACT core")
private val jobid = identity[ImportJob] _
private def options: Parser[ImportJob => ImportJob] =
(description | avgClockCycles | architectures | platforms).rep map (opts =>
(opts map (applyOption _) fold jobid) (_ andThen _))
......
package de.tu_darmstadt.cs.esa.tapasco.parser
import scala.language.implicitConversions
sealed abstract class ManSection(private val n: Int, _manual: Option[String] = None) {
require(n > 0 && n <= 9, "invalid section number, use 1-9")
lazy val manual: String = _manual getOrElse "MAN(%d)".format(n)
}
final case object GeneralCommands extends ManSection(1)
final case object SystemCalls extends ManSection(2)
final case object LibraryFunctions extends ManSection(3)
final case object SpecialFiles extends ManSection(4)
final case object FileFormatsConventions extends ManSection(5)
final case object GamesAndScreensavers extends ManSection(6)
final case object Miscellanea extends ManSection(7)
final case object SysAdminCommands extends ManSection(8)
object ManSection {
private lazy val numMap: Map[Int, ManSection] = all map (s => (s: Int) -> s) toMap
lazy val all: Seq[ManSection] = Seq(
GeneralCommands,
SystemCalls,
LibraryFunctions,
SpecialFiles,
FileFormatsConventions,
GamesAndScreensavers,
Miscellanea,
SysAdminCommands
)
def apply(n: Int) = numMap(n)
implicit def toManSection(n: Int): ManSection = apply(n)
implicit def toInt(s: ManSection): Int = s.n
}
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment