mirror of
https://github.com/osmarks/mycorrhiza.git
synced 2024-10-30 19:56:16 +00:00
107 lines
3.0 KiB
Go
107 lines
3.0 KiB
Go
package markup
|
||
|
||
import (
|
||
"fmt"
|
||
"path"
|
||
"strconv"
|
||
"strings"
|
||
)
|
||
|
||
const xclError = -9
|
||
|
||
// Transclusion is used by markup parser to remember what hyphae shall be transcluded.
|
||
type Transclusion struct {
|
||
name string
|
||
from int // inclusive
|
||
to int // inclusive
|
||
}
|
||
|
||
// Transclude transcludes `xcl` and returns html representation.
|
||
func Transclude(xcl Transclusion, state GemParserState) (html string) {
|
||
state.recursionLevel++
|
||
tmptOk := `<section class="transclusion transclusion_ok">
|
||
<a class="transclusion__link" href="/page/%s">%s</a>
|
||
<div class="transclusion__content">%s</div>
|
||
</section>`
|
||
tmptFailed := `<section class="transclusion transclusion_failed">
|
||
<p>Failed to transclude <a href="/page/%s">%s</a></p>
|
||
</section>`
|
||
if xcl.from == xclError || xcl.to == xclError || xcl.from > xcl.to {
|
||
return fmt.Sprintf(tmptFailed, xcl.name, xcl.name)
|
||
}
|
||
|
||
rawText, binaryHtml, err := HyphaAccess(xcl.name)
|
||
if err != nil {
|
||
return fmt.Sprintf(tmptFailed, xcl.name, xcl.name)
|
||
}
|
||
xclText := Parse(lex(xcl.name, rawText), xcl.from, xcl.to, state)
|
||
return fmt.Sprintf(tmptOk, xcl.name, xcl.name, binaryHtml+xclText)
|
||
}
|
||
|
||
/* Grammar from hypha ‘transclusion’:
|
||
transclusion_line ::= transclusion_token hypha_name LWS* [":" LWS* range LWS*]
|
||
transclusion_token ::= "<=" LWS+
|
||
hypha_name ::= canonical_name | noncanonical_name
|
||
range ::= id | (from_id two_dots to_id) | (from_id two_dots) | (two_dots to_id)
|
||
two_dots ::= ".."
|
||
*/
|
||
|
||
func parseTransclusion(line, hyphaName string) (xclusion Transclusion) {
|
||
line = strings.TrimSpace(remover("<=")(line))
|
||
if line == "" {
|
||
return Transclusion{"", xclError, xclError}
|
||
}
|
||
|
||
if strings.ContainsRune(line, ':') {
|
||
parts := strings.SplitN(line, ":", 2)
|
||
xclusion.name = xclCanonicalName(hyphaName, strings.TrimSpace(parts[0]))
|
||
selector := strings.TrimSpace(parts[1])
|
||
xclusion.from, xclusion.to = parseSelector(selector)
|
||
} else {
|
||
xclusion.name = xclCanonicalName(hyphaName, strings.TrimSpace(line))
|
||
}
|
||
return xclusion
|
||
}
|
||
|
||
func xclCanonicalName(hyphaName, xclName string) string {
|
||
switch {
|
||
case strings.HasPrefix(xclName, "./"):
|
||
return canonicalName(path.Join(hyphaName, strings.TrimPrefix(xclName, "./")))
|
||
case strings.HasPrefix(xclName, "../"):
|
||
return canonicalName(path.Join(path.Dir(hyphaName), strings.TrimPrefix(xclName, "../")))
|
||
default:
|
||
return canonicalName(xclName)
|
||
}
|
||
}
|
||
|
||
// At this point:
|
||
// selector ::= id
|
||
// | from ".."
|
||
// | from ".." to
|
||
// | ".." to
|
||
// If it is not, return (xclError, xclError).
|
||
func parseSelector(selector string) (from, to int) {
|
||
if selector == "" {
|
||
return 0, 0
|
||
}
|
||
if strings.Contains(selector, "..") {
|
||
parts := strings.Split(selector, "..")
|
||
|
||
var (
|
||
fromStr = strings.TrimSpace(parts[0])
|
||
from, fromErr = strconv.Atoi(fromStr)
|
||
toStr = strings.TrimSpace(parts[1])
|
||
to, toErr = strconv.Atoi(toStr)
|
||
)
|
||
if fromStr == "" && toStr == "" {
|
||
return 0, 0
|
||
}
|
||
if fromErr == nil || toErr == nil {
|
||
return from, to
|
||
}
|
||
} else if id, err := strconv.Atoi(selector); err == nil {
|
||
return id, id
|
||
}
|
||
return xclError, xclError
|
||
}
|