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 := `
%s
%s
` tmptFailed := `

Failed to transclude %s

` 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 }