Skip to content

Commit

Permalink
feat(parser/renderer): support role attributes, refactor attributes a…
Browse files Browse the repository at this point in the history
…nd image type (#171)

While improving support for role, id and title in the lists, this patch
also introduces changes in the attributes which get their own type
instead of `map[string]interface{}`, which allows for utility method(s).

Also, pursuing on images, the `Macro` type that is common to `BlockImage`
and `InlineImage` is behind removed, to avoid having to deal with 2 levels
of attributes for the same element.

Fixes #151

Signed-off-by: Xavier Coulon <[email protected]>
  • Loading branch information
xcoulon authored Aug 23, 2018
1 parent 852cca4 commit d2b6e95
Show file tree
Hide file tree
Showing 35 changed files with 20,556 additions and 31,623 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ generate: prebuild-checks
.PHONY: generate-optimized
## generate the .go file based on the asciidoc grammar
generate-optimized:
@echo "generating the parser..."
@echo "generating the parser (optimized)..."
@pigeon -optimize-grammar ./pkg/parser/asciidoc-grammar.peg > ./pkg/parser/asciidoc_parser.go


Expand Down
111 changes: 45 additions & 66 deletions pkg/parser/asciidoc-grammar.peg
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ TableOfContentsMacro <- "toc::[]" NEWLINE
// ------------------------------------------
// Element Attributes
// ------------------------------------------
ElementAttribute <- attr:(ElementID / ElementTitle / AdmonitionMarkerAttribute / HorizontalLayout / AttributeGroup) WS* EOL {
ElementAttribute <- attr:(ElementID / ElementTitle / ElementRole / AdmonitionMarkerAttribute / HorizontalLayout / AttributeGroup) WS* EOL {
return attr, nil // avoid returning something like `[]interface{}{attr, EOL}`
}

Expand All @@ -136,44 +136,44 @@ InlineElementID <- "[[" id:(ID) "]]" {
return types.NewElementID(id.(string))
}

// a title attached to an element, such as a BlockImage (
// a title attached to an element, such as a BlockImage
// a title starts with a single "." followed by the value, without space in-between
ElementTitle <- "." !"." !WS title:(!NEWLINE .)+ {
return types.NewElementTitle(title.([]interface{}))
}

// a role attached to an element, such as a BlockImage
// a role starts is wrapped in "[. ]"
ElementRole <- "[." !WS role:(!NEWLINE !"]" .)+ "]"{
return types.NewElementRole(role.([]interface{}))
}

// expression for the whole admonition marker, but only retains the actual kind
AdmonitionMarkerAttribute <- "[" k:(AdmonitionKind) "]" {
return types.NewAdmonitionAttribute(k.(types.AdmonitionKind))
}

// one or more attributes. eg: [foo, key1=value1, key2=value2]other
AttributeGroup <- "[" attribute:(GenericAttribute) attributes:(OtherGenericAttribute)* "]" {
return types.NewAttributeGroup(append([]interface{}{attribute}, attributes.([]interface{})...))
// one or more attributes. eg: [foo, key1=value1, key2 = value2 , ]
AttributeGroup <- "[" !WS attributes:(GenericAttribute)* "]" {
return types.NewAttributeGroup(attributes.([]interface{}))
}

GenericAttribute <- key:(AttributeKey) "=" value:(AttributeValue) { // value is set
GenericAttribute <- key:(AttributeKey) "=" value:(AttributeValue) ","? WS* { // value is set
return types.NewGenericAttribute(key.([]interface{}), value.([]interface{}))
} / key:(AttributeKey) { // value is not set
} / key:(AttributeKey) ","? WS* { // value is not set
return types.NewGenericAttribute(key.([]interface{}), nil)
}

OtherGenericAttribute <- "," WS* key:(AttributeKey) "=" value:(AttributeValue) { // value is set
return types.NewGenericAttribute(key.([]interface{}), value.([]interface{}))
} / "," WS* key:(AttributeKey) { // value is not set
return types.NewGenericAttribute(key.([]interface{}), nil)
}

AttributeKey <- key:(!WS !"=" !"," !"]" .)+ WS* {
AttributeKey <- !VerseKind key:(!"=" !"," !"]" .)+ {
return key, nil
}

AttributeValue <- WS* value:(!WS !"=" !"]" .)* WS* {
AttributeValue <- value:(!"=" !"," !"]" .)* {
return value, nil
}

HorizontalLayout <- "[horizontal]" {
return map[string]interface{}{"layout": "horizontal"}, nil
return types.ElementAttributes{"layout": "horizontal"}, nil
}

QuoteAttributes <- "[" kind:(QuoteKind) WS* "," author:(QuoteAuthor) "," title:(QuoteTitle) "]" {
Expand Down Expand Up @@ -622,20 +622,20 @@ Link <- link:(RelativeLink / ExternalLink) {
}

ExternalLink <- url:(URL_SCHEME URL) attributes:(LinkAttributes) {
return types.NewLink(url.([]interface{}), attributes.(map[string]interface{}))
return types.NewLink(url.([]interface{}), attributes.(types.ElementAttributes))
} / url:(URL_SCHEME URL) {
return types.NewLink(url.([]interface{}), nil)
}

// url preceeding with `link:` MUST be followed by square brackets
RelativeLink <- "link:" url:(URL_SCHEME? URL) attributes:(LinkAttributes) {
return types.NewLink(url.([]interface{}), attributes.(map[string]interface{}))
return types.NewLink(url.([]interface{}), attributes.(types.ElementAttributes))
}

LinkAttributes <- "[" text:(LinkTextAttribute)
otherAttrs:(OtherGenericAttribute)* "]" {
otherAttrs:(GenericAttribute)* "]" {
return types.NewLinkAttributes(text.([]interface{}), otherAttrs.([]interface{}))
} / "[" otherAttrs:(OtherGenericAttribute)* "]" {
} / "[" otherAttrs:(GenericAttribute)* "]" {
return types.NewLinkAttributes(nil, otherAttrs.([]interface{}))
}

Expand All @@ -646,49 +646,33 @@ LinkTextAttribute <- value:(!"," !"]" .)+ {
// ------------------------------------------
// Images
// ------------------------------------------
BlockImage <- attributes:(ElementAttribute)* image:BlockImageMacro WS* EOL {
// here we can ignore the blank line in the returned element
return types.NewBlockImage(image.(types.ImageMacro), attributes.([]interface{}))
}

BlockImageMacro <- "image::" path:(URL) attributes:(ImageAttributes) {
return types.NewImageMacro(path.(string), attributes.(map[string]interface{}))
}

InlineImage <- image:InlineImageMacro {
// here we can ignore the blank line in the returned element
return types.NewInlineImage(image.(types.ImageMacro))
}

InlineImageMacro <- "image:" !":" path:(URL) attributes:(ImageAttributes) {
return types.NewImageMacro(path.(string), attributes.(map[string]interface{}))
}
BlockImage <- attributes:(ElementAttribute)* "image::" path:(URL) inlineAttributes:(ImageAttributes) WS* EOL {
return types.NewBlockImage(path.(string), attributes.([]interface{}), inlineAttributes.(types.ElementAttributes))
}

InlineImage <- "image:" !":" path:(URL) attributes:(ImageAttributes) {
return types.NewInlineImage(path.(string), attributes.(types.ElementAttributes))
}

// the 'ImageAttributes' rule could be simpler, but the grammar optimizer fails to produce a valid code :(
ImageAttributes <- "[" alt:(ImageAttribute)
width:(ImageAttribute)
height:(ImageAttribute)
otherAttrs:(GenericAttribute)* "]" {
return types.NewImageAttributes(alt.([]interface{}), width.([]interface{}), height.([]interface{}), otherAttrs.([]interface{}))
} / "[" alt:(ImageAttribute)
width:(ImageAttribute)
otherAttrs:(GenericAttribute)* "]" {
return types.NewImageAttributes(alt.([]interface{}), width.([]interface{}), nil, otherAttrs.([]interface{}))
} / "[" alt:(ImageAttribute)
otherAttrs:(GenericAttribute)* "]" {
return types.NewImageAttributes(alt.([]interface{}), nil, nil, otherAttrs.([]interface{}))
} / "[" otherAttrs:(GenericAttribute)* "]" {
return types.NewImageAttributes(nil, nil, nil, otherAttrs.([]interface{}))
}

ImageAttributes <- "[" alt:(ImageAltAttribute)
width:(ImageWidthAttribute)
height:(ImageHeightAttribute)
otherAttrs:(OtherGenericAttribute)* "]" {
return types.NewImageAttributes(alt.([]interface{}), width.([]interface{}), height.([]interface{}), otherAttrs.([]interface{}))
} / "[" alt:(ImageAltAttribute)
width:(ImageWidthAttribute)
otherAttrs:(OtherGenericAttribute)* "]" {
return types.NewImageAttributes(alt.([]interface{}), width.([]interface{}), nil, otherAttrs.([]interface{}))
} / "[" alt:(ImageAltAttribute)
otherAttrs:(OtherGenericAttribute)* "]" {
return types.NewImageAttributes(alt.([]interface{}), nil, nil, otherAttrs.([]interface{}))
} / "[" otherAttrs:(OtherGenericAttribute)* "]" {
return types.NewImageAttributes(nil, nil, nil, otherAttrs.([]interface{}))
}

ImageAltAttribute <- value:(!"," !"]" .)+ {
return value, nil
}

ImageWidthAttribute <- "," value:(!"," !"]" .)+ {
return value, nil
}

ImageHeightAttribute <- "," value:(!"," !"]" .)+ {
ImageAttribute <- value:(!"," !"=" !"]" .)+ ("," / &"]") { // attribute is followed by "," or "]" (but do not consume the latter)
return value, nil
}

Expand Down Expand Up @@ -784,9 +768,6 @@ VerseBlockAttributes <-
attribute:(VerseAttributes) WS* EOL {
return attribute, nil
}
// / attribute:(ElementAttribute) {
// return attribute, nil
// }

VerseBlockContent <- lines:(VerseBlockLine)+ {
return types.NewParagraph(lines.([]interface{}), nil)
Expand All @@ -803,7 +784,6 @@ VerseBlockLineContent <- elements:(!QuoteBlockDelimiter !EOL WS* InlineElement W
// -------------------------------------------------------------------------------------
// Tables
// -------------------------------------------------------------------------------------

Table <- attributes:(ElementAttribute)*
TableDelimiter WS* NEWLINE
header:(TableLineHeader)?
Expand Down Expand Up @@ -833,7 +813,6 @@ TableCell <- TableCellSeparator elements:(!TableCellSeparator !EOL WS* InlineEle
// -------------------------------------------------------------------------------------
// Comments
// -------------------------------------------------------------------------------------

CommentBlockDelimiter <- "////"

CommentBlock <- attributes:(ElementAttribute)* CommentBlockDelimiter WS* NEWLINE content:(CommentBlockLine)* ((CommentBlockDelimiter WS* EOL) / EOF) {
Expand Down
Loading

0 comments on commit d2b6e95

Please sign in to comment.