Permalink
Browse files

support groff-mm output format

  • Loading branch information...
1 parent ab2227c commit 46e3fb8ecdfcb783a24d7991da7d7a51a7372a86 @knieriem committed Aug 31, 2012
View
@@ -10,8 +10,8 @@ by peg-markdown.
[peg]: https://github.com/pointlander/peg
[Go]: http://golang.org/
-Support for HTML output is implemented, but Groff and LaTeX
-output have not been ported. The output is identical
+Support for HTML and groff mm output is implemented, but LaTeX
+output has not been ported. The output is identical
to that of peg-markdown.
I try to keep the grammar in sync with the C version, by
View
@@ -9,6 +9,8 @@ import (
"os"
)
+var format = flag.String("t", "html", "output format")
+
func main() {
var opt markdown.Extensions
flag.BoolVar(&opt.Notes, "notes", false, "turn on footnote syntax")
@@ -37,6 +39,12 @@ func main() {
defer stopPProf()
w := bufio.NewWriter(os.Stdout)
- p.Markdown(r, markdown.ToHTML(w))
+
+ switch *format {
+ case "groff-mm":
+ p.Markdown(r, markdown.ToGroffMM(w))
+ default:
+ p.Markdown(r, markdown.ToHTML(w))
+ }
w.Flush()
}
View
@@ -25,45 +25,50 @@ func runDirTests(dir string, t *testing.T) {
t.Fatal(err)
}
+ var buf bytes.Buffer
+ fHTML := ToHTML(&buf)
+ fGroff := ToGroffMM(&buf)
p := NewParser(nil)
for _, name := range names {
if filepath.Ext(name) != ".text" {
continue
}
- if err = compareOutput(filepath.Join(dirPath, name), p); err != nil {
+ if err = compareOutput(&buf, fHTML, ".html", filepath.Join(dirPath, name), p); err != nil {
+ t.Error(err)
+ }
+ if err = compareOutput(&buf, fGroff, ".mm", filepath.Join(dirPath, name), p); err != nil {
t.Error(err)
}
}
}
// Compare the output of the C-based peg-markdown, which
-// is, for each test, available in a .html file accompanying the
-// .text file, with the output of this package's Markdown processor.
-func compareOutput(textPath string, p *Parser) (err error) {
- var bOrig, bThis bytes.Buffer
+// is, for each test, available in either a .html or a .mm file accompanying
+// the .text file, with the output of this package's Markdown processor.
+func compareOutput(w *bytes.Buffer, f Formatter, ext string, textPath string, p *Parser) (err error) {
+ var bOrig bytes.Buffer
r, err := os.Open(textPath)
if err != nil {
return
}
defer r.Close()
- bThis.Reset()
- out := ToHTML(&bThis)
- p.Markdown(r, out)
+ w.Reset()
+ p.Markdown(r, f)
- // replace .text extension by .html
+ // replace .text extension by `ext'
base := textPath[:len(textPath)-len(".text")]
- htmlPath := base + ".html"
+ refPath := base + ext
- r, err = os.Open(htmlPath)
+ r, err = os.Open(refPath)
if err != nil {
return
}
defer r.Close()
bOrig.ReadFrom(r)
- if bytes.Compare(bOrig.Bytes(), bThis.Bytes()) != 0 {
- err = fmt.Errorf("test %q failed", base)
+ if bytes.Compare(bOrig.Bytes(), w.Bytes()) != 0 {
+ err = fmt.Errorf("test %q failed", refPath)
}
return
}
View
@@ -0,0 +1,196 @@
+/* Original C version https://github.com/jgm/peg-markdown/
+ * Copyright 2008 John MacFarlane (jgm at berkeley dot edu).
+ *
+ * Modifications and translation from C into Go
+ * based on markdown_output.c
+ * Copyright 2010 Michael Teichgräber (mt at wmipf dot de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License or the MIT
+ * license. See LICENSE for details.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+package markdown
+
+// groff mm output functions
+
+import (
+ "log"
+ "strings"
+)
+
+type troffOut struct {
+ baseWriter
+ inListItem bool
+ escape *strings.Replacer
+}
+
+// Returns a formatter that writes the document in groff mm format.
+func ToGroffMM(w Writer) Formatter {
+ f := new(troffOut)
+ f.baseWriter = baseWriter{w, 2}
+ f.escape = strings.NewReplacer(`\`, `\e`)
+ return f
+}
+func (f *troffOut) FormatBlock(tree *element) {
+ f.elist(tree)
+}
+func (f *troffOut) Finish() {
+ f.WriteByte('\n')
+ f.padded = 2
+}
+
+func (h *troffOut) sp() *troffOut {
+ h.pad(2)
+ return h
+}
+func (h *troffOut) br() *troffOut {
+ h.pad(1)
+ return h
+}
+
+func (h *troffOut) skipPadding() *troffOut {
+ h.padded = 2
+ return h
+}
+
+// write a string
+func (w *troffOut) s(s string) *troffOut {
+ w.WriteString(s)
+ return w
+}
+
+// write string, escape '\'
+func (w *troffOut) str(s string) *troffOut {
+ w.escape.WriteString(w, s)
+ return w
+}
+
+func (w *troffOut) children(el *element) *troffOut {
+ return w.elist(el.children)
+}
+func (w *troffOut) inline(pfx string, el *element, sfx string) *troffOut {
+ return w.s(pfx).children(el).s(sfx)
+}
+
+func (w *troffOut) req(name string) *troffOut {
+ return w.br().s(".").s(name)
+}
+
+// write a list of elements
+func (w *troffOut) elist(list *element) *troffOut {
+ for i := 0; list != nil; i++ {
+ w.elem(list, i == 0)
+ list = list.next
+ }
+ return w
+}
+
+func (w *troffOut) elem(elt *element, isFirst bool) *troffOut {
+ var s string
+
+ switch elt.key {
+ case SPACE:
+ s = elt.contents.str
+ case LINEBREAK:
+ w.req("br\n")
+ case STR:
+ w.str(elt.contents.str)
+ case ELLIPSIS:
+ s = "..."
+ case EMDASH:
+ s = `\[em]`
+ case ENDASH:
+ s = `\[en]`
+ case APOSTROPHE:
+ s = "'"
+ case SINGLEQUOTED:
+ w.inline("`", elt, "'")
+ case DOUBLEQUOTED:
+ w.inline(`\[lq]`, elt, `\[rq]`)
+ case CODE:
+ w.s(`\fC`).str(elt.contents.str).s(`\fR`)
+ case HTML:
+ /* don't print HTML */
+ case LINK:
+ link := elt.contents.link
+ w.elist(link.label)
+ w.s(" (").s(link.url).s(")")
+ case IMAGE:
+ w.s("[IMAGE: ").elist(elt.contents.link.label).s("]")
+ /* not supported */
+ case EMPH:
+ w.inline(`\fI`, elt, `\fR`)
+ case STRONG:
+ w.inline(`\fB`, elt, `\fR`)
+ case LIST:
+ w.children(elt)
+ case RAW:
+ /* Shouldn't occur - these are handled by process_raw_blocks() */
+ log.Fatalf("RAW")
+ case H1, H2, H3, H4, H5, H6:
+ h := ".H " + string('1'+elt.key-H1) + ` "` /* assumes H1 ... H6 are in order */
+ w.br().inline(h, elt, `"`)
+ case PLAIN:
+ w.br().children(elt)
+ case PARA:
+ if !w.inListItem || !isFirst {
+ w.req("P\n").children(elt)
+ } else {
+ w.br().children(elt)
+ }
+ case HRULE:
+ w.br().s(`\l'\n(.lu*8u/10u'`)
+ case HTMLBLOCK:
+ /* don't print HTML block */
+ case VERBATIM:
+ w.req("VERBON 2\n")
+ w.str(elt.contents.str)
+ w.s(".VERBOFF")
+ case BULLETLIST:
+ w.req("BL").children(elt).req("LE 1")
+ case ORDEREDLIST:
+ w.req("AL").children(elt).req("LE 1")
+ case DEFINITIONLIST:
+ w.req(`BVL \\n(Pin`).children(elt).req("LE 1")
+ case DEFTITLE:
+ w.req(`DLI "`).children(elt).s(`"`)
+ case DEFDATA:
+ w.children(elt)
+ w.req("br")
+ case LISTITEM:
+ w.req("LI\n")
+ w.inListItem = true
+ w.skipPadding()
+ w.children(elt)
+ w.inListItem = false
+ case BLOCKQUOTE:
+ w.req("DS I\n")
+ w.skipPadding()
+ w.children(elt)
+ w.req("DE")
+ case NOTE:
+ /* if contents.str == 0, then print note; else ignore, since this
+ * is a note block that has been incorporated into the notes list */
+ if elt.contents.str == "" {
+ w.s("\\*F\n")
+ w.s(".FS\n")
+ w.skipPadding()
+ w.children(elt)
+ w.req("FE")
+ }
+ case REFERENCE:
+ /* Nonprinting */
+ default:
+ log.Fatalf("troffOut.elem encountered unknown element key = %d\n", elt.key)
+ }
+ if s != "" {
+ w.s(s)
+ }
+ return w
+}
View
@@ -3,4 +3,5 @@ pmd=../../,,pmd/markdown
for (i in *.text) {
stem=`{echo $i | sed 's,.text$,,'}
$pmd < $i >$"stem.html
+ $pmd -t groff-mm < $i >$"stem.mm
}
@@ -0,0 +1,18 @@
+.P
+AT&T has an ampersand in their name.
+.P
+ATT is another way to write it.
+.P
+This & that.
+.P
+4 < 5.
+.P
+6 > 5.
+.P
+Here's a link (http://example.com/?foo=1&bar=2) with an ampersand in the URL.
+.P
+Here's a link with an amersand in the link text: AT&T (http://att.com/).
+.P
+Here's an inline link (/script?foo=1&bar=2).
+.P
+Here's an inline link (/script?foo=1&bar=2).
@@ -0,0 +1,21 @@
+.P
+Link: http://example.com/ (http://example.com/).
+.P
+With an ampersand: http://example.com/?foo=1&bar=2 (http://example.com/?foo=1&bar=2)
+.BL
+.LI
+In a list?
+.LI
+http://example.com/ (http://example.com/)
+.LI
+It should.
+.LE 1
+.DS I
+.P
+Blockquoted: http://example.com/ (http://example.com/)
+.DE
+.P
+Auto-links should not occur here: \fC<http://example.com/>\fR
+.VERBON 2
+or here: <http://example.com/>
+.VERBOFF
Oops, something went wrong.

0 comments on commit 46e3fb8

Please sign in to comment.