Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(parser): support document attributes in links #441

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ generate: prebuild-checks
generate-optimized:
@echo "generating the parser (optimized)..."
@pigeon -optimize-parser \
-alternate-entrypoints DraftAsciidocDocument,DraftAsciidocDocumentWithinDelimitedBlock,DraftTextDocument,DocumentBlock,InlineElementsWithoutSubtitution,FileLocation,IncludedFileLine \
-alternate-entrypoints AsciidocDocument,AsciidocDocumentWithinDelimitedBlock,TextDocument,DocumentBlock,InlineElementsWithoutSubtitution,FileLocation,IncludedFileLine,InlineLinks \
-o ./pkg/parser/parser.go ./pkg/parser/parser.peg

.PHONY: test
Expand Down
12 changes: 5 additions & 7 deletions pkg/parser/document_attributes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -906,9 +906,7 @@ a paragraph written by {author}.`
Attributes: types.ElementAttributes{},
Lines: []types.InlineElements{
{
types.StringElement{Content: "a paragraph written by "},
types.DocumentAttributeSubstitution{Name: "author"},
types.StringElement{Content: "."},
types.StringElement{Content: "a paragraph written by Xavier."},
},
},
},
Expand Down Expand Up @@ -936,9 +934,7 @@ a paragraph written by {author}.`
Attributes: types.ElementAttributes{},
Lines: []types.InlineElements{
{
types.StringElement{Content: "a paragraph written by "},
types.DocumentAttributeSubstitution{Name: "author"},
types.StringElement{Content: "."},
types.StringElement{Content: "a paragraph written by Xavier."},
},
},
},
Expand Down Expand Up @@ -1127,7 +1123,9 @@ a paragraph with *bold content*`
types.StringElement{Content: ":@date: 2017-01-01"},
},
{
types.StringElement{Content: ":{author}: Xavier"},
types.StringElement{Content: ":"},
types.DocumentAttributeSubstitution{Name: "author"},
types.StringElement{Content: ": Xavier"},
},
},
},
Expand Down
6 changes: 3 additions & 3 deletions pkg/parser/document_preprocessing.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const LevelOffset ContextKey = "leveloffset"

// ParseDraftDocument parses a document's content and applies the preprocessing directives (file inclusions)
func ParseDraftDocument(filename string, r io.Reader, opts ...Option) (types.DraftDocument, error) {
opts = append(opts, Entrypoint("DraftAsciidocDocument"))
opts = append(opts, Entrypoint("AsciidocDocument"))
return parseDraftDocument(filename, r, []levelOffset{}, opts...)
}

Expand Down Expand Up @@ -54,7 +54,7 @@ func parseElements(filename string, elements []interface{}, attrs types.Document
case types.DelimitedBlock:
elmts, err := parseElements(filename, e.Elements, attrs, levelOffsets,
// use a new var to avoid overridding the current one which needs to stay as-is for the rest of the doc parsing
append(opts, Entrypoint("DraftAsciidocDocumentWithinDelimitedBlock"))...)
append(opts, Entrypoint("AsciidocDocumentWithinDelimitedBlock"))...)
if err != nil {
return nil, err
}
Expand All @@ -69,7 +69,7 @@ func parseElements(filename string, elements []interface{}, attrs types.Document
offset.apply(&e)
// replace the absolute when the first section is processed with a relative offset
// which is based on the actual level offset that resulted in the application of the absolute offset
if offset.absolute {
if offset.absolute {
levelOffsets = []levelOffset{
relativeOffset(e.Level - oldLevel),
}
Expand Down
95 changes: 65 additions & 30 deletions pkg/parser/document_processing.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"io"
"reflect"
"strconv"
"strings"

"github.com/bytesparadise/libasciidoc/pkg/types"

Expand All @@ -22,8 +23,13 @@ func ParseDocument(filename string, r io.Reader, opts ...Option) (types.Document
log.Debug("draft document")
spew.Dump(draftDoc)
}
// apply document attribute substitutions and re-parse paragraphs that were affected
blocks, err := applyDocumentAttributeSubstitutions(draftDoc.Blocks)
if err != nil {
return types.Document{}, err
}
// now, merge list items into proper lists
blocks, err := rearrangeListItems(draftDoc.Blocks, false)
blocks, err = rearrangeListItems(blocks, false)
if err != nil {
return types.Document{}, err
}
Expand All @@ -38,9 +44,65 @@ func ParseDocument(filename string, r io.Reader, opts ...Option) (types.Document
doc.Attributes[k] = v
}
}
if log.IsLevelEnabled(log.DebugLevel) {
log.Debug("final document")
spew.Dump(doc)
}
return doc, nil
}

// applyAttributeSubstitutions applies the document attribute substitutions
// and re-parse the paragraphs that were affected
func applyDocumentAttributeSubstitutions(blocks []interface{}) ([]interface{}, error) {
// the document attributes, as they are resolved while processing the blocks
attrs := make(map[string]string)
result := make([]interface{}, 0, len(blocks)) // maximum capacity cannot exceed initial input
for _, b := range blocks {
switch b := b.(type) {
case types.DocumentAttributeDeclaration:
attrs[b.Name] = b.Value
case types.DocumentAttributeReset:
delete(attrs, b.Name)
case types.Paragraph:
for i, line := range b.Lines {
if line, found := line.ApplyDocumentAttributeSubstitutions(attrs); found {
// reparse the string elements, looking for links
elements := make(types.InlineElements, 0, 2*len(line))
for _, element := range line {
switch element := element.(type) {
case types.StringElement:
r, err := parseInlineLinks(element)
if err != nil {
return []interface{}{}, errors.Wrap(err, "unable to process attribute substitutions")
}
elements = append(elements, r...)
default:
elements = append(elements, element)
}
}
b.Lines[i] = elements
} else {
b.Lines[i] = line
}
}
}
result = append(result, b)
}

return result, nil
}

func parseInlineLinks(element types.StringElement) (types.InlineElements, error) {
log.Debugf("parsing '%+v'", element.Content)
elements, err := ParseReader("", strings.NewReader(element.Content), Entrypoint("InlineLinks"))
if err != nil {
return types.InlineElements{}, errors.Wrap(err, "error while parsing content for inline links")
}

log.Debugf(" giving '%+v'", elements)
return elements.(types.InlineElements), nil
}

// rearrangeListItems moves the list items into lists, and nested lists if needed
func rearrangeListItems(blocks []interface{}, withinDelimitedBlock bool) ([]interface{}, error) {
// log.Debugf("rearranging list items in %d blocks...", len(blocks))
Expand Down Expand Up @@ -116,33 +178,11 @@ func rearrangeListItems(blocks []interface{}, withinDelimitedBlock bool) ([]inte
}
// also when all is done, process the remaining pending list items
if len(lists) > 0 {
// if log.IsLevelEnabled(log.DebugLevel) {
// log.Debugf("processing the remaining %d lists", len(lists))
// spew.Dump(lists)
// }
// // add the top-level list *value* (not the pointer)
// if reflect.ValueOf(l[0]).Kind() == reflect.Ptr { // TODO: factorize duplicate code
// tl := reflect.Indirect(reflect.ValueOf(l[0])).Interface()
// result = append(result, tl) // just add the top-level list
// }
log.Debugf("processing the remaining %d lists...", len(lists))
for _, list := range pruneLists(lists, 0) {
result = append(result, unPtr(list))
// switch list := list.(type) {
// case *types.OrderedList:
// result = append(result, *list)
// case *types.UnorderedList:
// result = append(result, *list)
// case *types.LabeledList:
// result = append(result, *list)
// }
}
}
if log.IsLevelEnabled(log.DebugLevel) {
log.Debugf("rearranged list items in %d blocks", len(blocks))
spew.Dump(result)
}

return result, nil
}

Expand Down Expand Up @@ -342,11 +382,11 @@ func rearrangeSections(blocks []interface{}) (types.Document, error) {
previous = &e // pointer to new current parent
} else {
if previous == nil {
log.Debugf("adding element of type %T as a top-level element", element)
// log.Debugf("adding element of type %T as a top-level element", element)
tle = append(tle, element)
} else {
parentSection := &(sections[len(sections)-1])
log.Debugf("adding element of type %T as a child of section with level %d", element, parentSection.Level)
// log.Debugf("adding element of type %T as a child of section with level %d", element, parentSection.Level)
(*parentSection).AddElement(element)
}
}
Expand All @@ -364,11 +404,6 @@ func rearrangeSections(blocks []interface{}) (types.Document, error) {
}
}
// process the remaining sections
if log.IsLevelEnabled(log.DebugLevel) {
log.Debugf("processing the remaining sections...")
spew.Dump(sections)
}

sections = pruneSections(sections, 1)
if len(sections) > 0 {
tle = append(tle, sections[0])
Expand Down
2 changes: 1 addition & 1 deletion pkg/parser/file_inclusion.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func parseFileToInclude(filename string, incl types.FileInclusion, attrs types.D
}
// use a simpler/different grammar for non-asciidoc files.
if !IsAsciidoc(absPath) {
opts = append(opts, Entrypoint("DraftTextDocument"))
opts = append(opts, Entrypoint("TextDocument"))
}
return parseDraftDocument(absPath, content, levelOffsets, opts...)
}
Expand Down
Loading