2010-11-21 22:04:39 +00:00
|
|
|
/* 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_lib.c and parsing_functions.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
|
|
|
|
|
|
|
|
import (
|
2010-11-24 17:54:12 +00:00
|
|
|
"bytes"
|
2012-04-20 23:19:46 +00:00
|
|
|
"io"
|
2010-11-24 18:36:10 +00:00
|
|
|
"log"
|
2012-04-20 11:36:59 +00:00
|
|
|
"strings"
|
2010-11-21 22:04:39 +00:00
|
|
|
)
|
|
|
|
|
2012-04-29 22:55:37 +00:00
|
|
|
const (
|
|
|
|
// If you get a build error message saying that
|
|
|
|
// parserIfaceVersion_N is undefined, parser.leg.go
|
|
|
|
// either is not present or it is out of date. You should
|
|
|
|
// rebuild it using
|
|
|
|
// make nuke
|
|
|
|
// make parser
|
2013-06-12 12:17:16 +00:00
|
|
|
needParserIfaceVersion = parserIfaceVersion_17
|
2012-04-29 22:55:37 +00:00
|
|
|
)
|
|
|
|
|
2012-05-04 14:20:29 +00:00
|
|
|
// Markdown Extensions.
|
|
|
|
type Extensions struct {
|
2012-04-20 11:36:59 +00:00
|
|
|
Smart bool
|
|
|
|
Notes bool
|
|
|
|
FilterHTML bool
|
|
|
|
FilterStyles bool
|
2013-06-11 00:00:44 +00:00
|
|
|
Strike bool
|
2012-04-20 11:36:59 +00:00
|
|
|
Dlists bool
|
2010-11-24 18:56:25 +00:00
|
|
|
}
|
|
|
|
|
2012-04-28 20:57:55 +00:00
|
|
|
type Parser struct {
|
|
|
|
yy yyParser
|
|
|
|
preformatBuf *bytes.Buffer
|
|
|
|
}
|
2010-11-21 22:04:39 +00:00
|
|
|
|
2012-04-28 20:57:55 +00:00
|
|
|
// NewParser creates an instance of a parser. It can be reused
|
|
|
|
// so that stacks and buffers need not be allocated anew for
|
|
|
|
// each Markdown call.
|
2012-05-04 14:20:29 +00:00
|
|
|
func NewParser(x *Extensions) (p *Parser) {
|
2012-04-28 20:57:55 +00:00
|
|
|
p = new(Parser)
|
2012-05-04 14:20:29 +00:00
|
|
|
if x != nil {
|
|
|
|
p.yy.state.extension = *x
|
2012-04-28 20:57:55 +00:00
|
|
|
}
|
|
|
|
p.yy.Init()
|
2012-04-29 21:54:10 +00:00
|
|
|
p.yy.state.heap.init(1024)
|
2012-04-28 20:57:55 +00:00
|
|
|
p.preformatBuf = bytes.NewBuffer(make([]byte, 0, 32768))
|
|
|
|
return
|
|
|
|
}
|
2010-11-24 18:36:10 +00:00
|
|
|
|
2012-08-31 22:25:43 +00:00
|
|
|
// A Formatter is called repeatedly, one Markdown block at a time,
|
|
|
|
// while the document is parsed. At the end of a document the Finish
|
|
|
|
// method is called, which may, for example, print footnotes.
|
|
|
|
// A Formatter can be reused.
|
2012-04-28 20:57:55 +00:00
|
|
|
type Formatter interface {
|
|
|
|
FormatBlock(*element)
|
|
|
|
Finish()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Markdown parses input from an io.Reader into a tree, and sends
|
|
|
|
// parsed blocks to a Formatter
|
|
|
|
func (p *Parser) Markdown(src io.Reader, f Formatter) {
|
|
|
|
s := p.preformat(src)
|
2010-11-21 22:04:39 +00:00
|
|
|
|
2012-04-28 20:57:55 +00:00
|
|
|
p.parseRule(ruleReferences, s)
|
|
|
|
if p.yy.extension.Notes {
|
|
|
|
p.parseRule(ruleNotes, s)
|
2010-11-21 22:04:39 +00:00
|
|
|
}
|
2012-04-29 21:54:10 +00:00
|
|
|
savedPos := p.yy.state.heap.Pos()
|
2012-04-28 20:57:55 +00:00
|
|
|
|
|
|
|
for {
|
|
|
|
tree := p.parseRule(ruleDocblock, s)
|
2012-05-29 23:29:37 +00:00
|
|
|
if tree == nil {
|
|
|
|
break
|
|
|
|
}
|
2012-04-28 20:57:55 +00:00
|
|
|
s = p.yy.ResetBuffer("")
|
|
|
|
tree = p.processRawBlocks(tree)
|
|
|
|
f.FormatBlock(tree)
|
2012-04-29 21:54:10 +00:00
|
|
|
p.yy.state.heap.setPos(savedPos)
|
2012-04-28 20:57:55 +00:00
|
|
|
}
|
|
|
|
f.Finish()
|
2010-11-21 22:04:39 +00:00
|
|
|
}
|
|
|
|
|
2012-04-28 20:57:55 +00:00
|
|
|
func (p *Parser) parseRule(rule int, s string) (tree *element) {
|
2013-01-08 20:28:57 +00:00
|
|
|
old := p.yy.ResetBuffer(s)
|
|
|
|
if old != "" && strings.Trim(old, "\r\n ") != "" {
|
|
|
|
log.Fatalln("Buffer not empty", "["+old+"]")
|
2010-11-24 18:36:10 +00:00
|
|
|
}
|
2012-05-29 23:29:37 +00:00
|
|
|
err := p.yy.Parse(rule)
|
2012-04-28 20:57:55 +00:00
|
|
|
switch rule {
|
|
|
|
case ruleDoc, ruleDocblock:
|
2012-05-29 23:29:37 +00:00
|
|
|
if err == nil {
|
|
|
|
tree = p.yy.state.tree
|
|
|
|
}
|
2012-04-28 20:57:55 +00:00
|
|
|
p.yy.state.tree = nil
|
|
|
|
}
|
|
|
|
return
|
2010-11-21 22:04:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* process_raw_blocks - traverses an element list, replacing any RAW elements with
|
|
|
|
* the result of parsing them as markdown text, and recursing into the children
|
|
|
|
* of parent elements. The result should be a tree of elements without any RAWs.
|
|
|
|
*/
|
2012-04-28 20:57:55 +00:00
|
|
|
func (p *Parser) processRawBlocks(input *element) *element {
|
2010-11-21 22:04:39 +00:00
|
|
|
|
|
|
|
for current := input; current != nil; current = current.next {
|
|
|
|
if current.key == RAW {
|
|
|
|
/* \001 is used to indicate boundaries between nested lists when there
|
|
|
|
* is no blank line. We split the string by \001 and parse
|
|
|
|
* each chunk separately.
|
|
|
|
*/
|
|
|
|
current.key = LIST
|
|
|
|
current.children = nil
|
2010-11-24 17:59:08 +00:00
|
|
|
listEnd := ¤t.children
|
2011-06-30 18:52:13 +00:00
|
|
|
for _, contents := range strings.Split(current.contents.str, "\001") {
|
2012-04-28 20:57:55 +00:00
|
|
|
if list := p.parseRule(ruleDoc, contents); list != nil {
|
2010-11-25 13:33:36 +00:00
|
|
|
*listEnd = list
|
|
|
|
for list.next != nil {
|
|
|
|
list = list.next
|
|
|
|
}
|
|
|
|
listEnd = &list.next
|
2010-11-21 22:04:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
current.contents.str = ""
|
|
|
|
}
|
|
|
|
if current.children != nil {
|
2012-04-28 20:57:55 +00:00
|
|
|
current.children = p.processRawBlocks(current.children)
|
2010-11-21 22:04:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return input
|
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
|
|
|
TABSTOP = 4
|
|
|
|
)
|
|
|
|
|
|
|
|
/* preformat - allocate and copy text buffer while
|
|
|
|
* performing tab expansion.
|
|
|
|
*/
|
2012-04-28 20:57:55 +00:00
|
|
|
func (p *Parser) preformat(r io.Reader) (s string) {
|
2010-11-21 22:04:39 +00:00
|
|
|
charstotab := TABSTOP
|
2012-04-20 23:19:46 +00:00
|
|
|
buf := make([]byte, 32768)
|
|
|
|
|
2012-04-28 20:57:55 +00:00
|
|
|
b := p.preformatBuf
|
|
|
|
b.Reset()
|
2012-04-20 23:19:46 +00:00
|
|
|
for {
|
|
|
|
n, err := r.Read(buf)
|
|
|
|
if err != nil {
|
|
|
|
break
|
2010-11-21 22:04:39 +00:00
|
|
|
}
|
2012-04-20 23:19:46 +00:00
|
|
|
i0 := 0
|
2012-04-29 22:58:06 +00:00
|
|
|
for i, c := range buf[:n] {
|
|
|
|
switch c {
|
2012-04-20 23:19:46 +00:00
|
|
|
case '\t':
|
|
|
|
b.Write(buf[i0:i])
|
|
|
|
for ; charstotab > 0; charstotab-- {
|
|
|
|
b.WriteByte(' ')
|
|
|
|
}
|
|
|
|
i0 = i + 1
|
|
|
|
case '\n':
|
|
|
|
b.Write(buf[i0 : i+1])
|
|
|
|
i0 = i + 1
|
|
|
|
charstotab = TABSTOP
|
|
|
|
default:
|
|
|
|
charstotab--
|
|
|
|
}
|
|
|
|
if charstotab == 0 {
|
|
|
|
charstotab = TABSTOP
|
|
|
|
}
|
2010-11-21 22:04:39 +00:00
|
|
|
}
|
2012-04-20 23:19:46 +00:00
|
|
|
b.Write(buf[i0:n])
|
2010-11-21 22:04:39 +00:00
|
|
|
}
|
2012-04-20 23:19:46 +00:00
|
|
|
|
2012-04-29 21:47:53 +00:00
|
|
|
b.WriteString("\n\n")
|
2010-11-24 17:54:12 +00:00
|
|
|
return b.String()
|
2010-11-21 22:04:39 +00:00
|
|
|
}
|