You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
wiki/link/parser.go

144 lines
2.9 KiB

package link
import (
"bytes"
"fmt"
"html/template"
"io"
"log"
"strings"
)
type Tree struct {
cur rng
children []Tree
}
func (t Tree) text(inp string) string {
return inp[t.cur.start:t.cur.end]
}
type rng struct {
start, end int
typ string
}
type Parser struct {
markers []int
ranges []rng
stack []Tree
}
func (p *Parser) pushMarker(start int) {
p.markers = append(p.markers, start)
p.stack = append(p.stack, Tree{})
}
func (p *Parser) popMarker(end int, typ string) {
start := p.markers[len(p.markers)-1]
p.markers = p.markers[:len(p.markers)-1]
cur := rng{start, end, typ}
top := p.stack[len(p.stack)-1]
top.cur = cur
p.stack = p.stack[:len(p.stack)-1]
p.stack[len(p.stack)-1].children = append(p.stack[len(p.stack)-1].children, top)
p.ranges = append(p.ranges, cur)
}
func (p *Parser) output() Tree {
return p.stack[0].children[0]
}
func ShowTree(input string, root Tree, indent int) {
if root.cur.start != root.cur.end {
fmt.Printf("%s + (%s, %d-%d) %q\n", strings.Repeat(" |", indent), root.cur.typ, root.cur.start, root.cur.end, input[root.cur.start:root.cur.end])
} else {
indent -= 1
}
for _, c := range root.children {
ShowTree(input, c, indent+1)
}
}
func formatTitle(w io.Writer, input string, root Tree, indent int) {
typ := root.cur.typ
text := input[root.cur.start:root.cur.end]
if typ == "text" {
w.Write([]byte(text))
}
if typ == "link" {
w.Write([]byte(fmt.Sprintf(`<a href="%s">`, root.children[1].text(input))))
w.Write([]byte(text))
w.Write([]byte(`</a>`))
return
}
for _, c := range root.children {
formatTitle(w, input, c, indent+1)
}
}
func findAllLinks(input string, root Tree) []string {
var links []string
typ := root.cur.typ
if typ == "link" {
links = append(links, root.children[1].text(input))
}
for _, c := range root.children {
links = append(links, findAllLinks(input, c)...)
}
return links
}
func FindAllLinks(input string) []string {
var p Parser
root := p.Parse(input)
return findAllLinks(input, root)
}
func FormatHtmlTitle(input string) template.HTML {
p := Parser{}
root := p.Parse(input)
sb := &bytes.Buffer{}
formatTitle(sb, input, root, 0)
return template.HTML(sb.String())
}
func (p *Parser) Parse(input string) Tree {
p.stack = append(p.stack, Tree{})
limit := 1000
i := 0
p.pushMarker(i)
for i < len(input) && limit > 0 {
p.pushMarker(i)
for i < len(input) && (input[i] != '[' && input[i] != ']') {
i++
limit--
}
p.popMarker(i, "text")
if i+2 <= len(input) && input[i:i+2] == "[[" {
p.pushMarker(i)
p.pushMarker(i)
i += 2
p.popMarker(i, "start link tag")
p.pushMarker(i)
} else if i+2 <= len(input) && input[i:i+2] == "]]" {
p.popMarker(i, "link text")
p.pushMarker(i)
i += 2
p.popMarker(i, "end link tag")
p.popMarker(i, "link")
}
limit--
}
p.popMarker(i, "full text")
if limit == 0 {
log.Println("LIMIT REACHED: ", input)
}
return p.output()
}