Skip to content

Commit

Permalink
feat(parser): add support for meta-elements: ID, link and title
Browse files Browse the repository at this point in the history
Support meta-elements alone, they shall be associated with their
target element later in the process.

Signed-off-by: Xavier Coulon <[email protected]>
  • Loading branch information
xcoulon committed Jun 24, 2017
1 parent b53b3a2 commit c08a7f3
Show file tree
Hide file tree
Showing 3 changed files with 272 additions and 38 deletions.
62 changes: 53 additions & 9 deletions parser/asciidoc-grammar.peg
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,26 @@ Document <- lines:Line* EOF {
return types.NewDocument(lines.([]interface{}))
}

Line <- line:(Heading / ListItem / BlockImage / Inline / EmptyLine) {
Line <- line:(Heading / ListItem / BlockImage / MetaElement / Inline / EmptyLine) {
return line, nil
}

Heading <- level:("="+) WS+ content:Inline {
return types.NewHeading(level, content.(*types.InlineContent))
}

// ---------------------
// Lists
// ---------------------
//TODO: Blank lines are required before and after a list
//TODO: Additionally, blank lines are permitted, but not required, between list items.
ListItem <- WS* ('*' / '-') WS+ content:(Inline) {
return types.NewListItem(content.(*types.InlineContent))
}

Inline <- !NEWLINE elements:(BoldQuote / ExternalLink / BlockImage / Word / WS)+ NEWLINE? {
return types.NewInlineContent(elements.([]interface{}))
}

// ---------------------
// Quotes
// ---------------------
BoldQuote <- '*' content:(BoldContent) '*' {
return types.NewBoldQuote(content)
}
Expand All @@ -39,18 +41,59 @@ BoldContent <- (BoldContentWord WS+)* BoldContentWord {
return string(c.text), nil
}

BoldContentWord <- (!NEWLINE !WS !'*' .)+ {
return string(c.text), nil
}

// ---------------------
// Links
// ---------------------
ExternalLink <- url:(URL_SCHEME URL) text:('[' (URL_TEXT)* ']')? {
if text != nil {
return types.NewExternalLink(url.([]interface{}), text.([]interface{}))
}
return types.NewExternalLink(url.([]interface{}), nil)

}

BlockImage <- "image::" path:(URL) altText:('[' (URL_TEXT)* ']') {
// ---------------------
// Images
// ---------------------
BlockImage <- "image::" path:(URL) altText:('[' (URL_TEXT)* ']') (NEWLINE/EOF) {
return types.NewBlockImage(path.(string), altText.([]interface{}))
}

// ---------------------
// Inline content
// ---------------------
Inline <- !NEWLINE elements:(BoldQuote / ExternalLink / Word / WS)+ (NEWLINE/EOF) {
return types.NewInlineContent(elements.([]interface{}))
}

// ---------------------
// meta-element types
// ---------------------
MetaElement <- meta:(ElementLink / ElementID / ElementTitle) {
return meta, nil
}

// a link attached to an element, such as a BlockImage
ElementLink <- "[" WS* "link" WS* "=" WS* path:URL WS* "]" (NEWLINE/EOF) {
return types.NewElementLink(path.(string))
}

// an id attached to an element, such as a BlockImage
ElementID <- "[" WS* id:(ID) WS* "]" (NEWLINE/EOF) {
return types.NewElementID(id.(string))
}

// a title attached to an element, such as a BlockImage
ElementTitle <- "." !WS title:(!NEWLINE .)+ (NEWLINE/EOF) {
return types.NewElementTitle(title.([]interface{}))
}

// ---------------------
// Base types
// ---------------------
Word <- (!NEWLINE !WS .)+ {
return string(c.text), nil
}
Expand All @@ -59,14 +102,15 @@ URL <- (!NEWLINE !WS !'[' !']' .)+ {
return string(c.text), nil
}

URL_TEXT <- (!NEWLINE !'[' !']' .)+ {
ID <- '#' (!NEWLINE !WS !'[' !']' .)+ {
return string(c.text), nil
}

BoldContentWord <- (!NEWLINE !WS !'*' .)+ {
URL_TEXT <- (!NEWLINE !'[' !']' .)+ {
return string(c.text), nil
}


EmptyLine <- NEWLINE {
return types.NewEmptyLine()
}
Expand Down
152 changes: 146 additions & 6 deletions parser/asciidoc_parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func TestHeadingOnly(t *testing.T) {
assert.EqualValues(t, expectedDocument, actualDocument)
}

func TestInvalidHeading1(t *testing.T) {
func TestHeadingInvalid1(t *testing.T) {
// given an invalid heading (missing space after '=')
actualDocument, errs := ParseString("=a heading")
require.Nil(t, errs)
Expand All @@ -56,7 +56,7 @@ func TestInvalidHeading1(t *testing.T) {
log.Debugf("expected document: %s", expectedDocument.String())
assert.EqualValues(t, expectedDocument, actualDocument)
}
func TestInvalidHeading2(t *testing.T) {
func TestHeadingInvalid2(t *testing.T) {
// given an invalid heading (extra space before '=')
actualDocument, errs := ParseString(" = a heading")
require.Nil(t, errs)
Expand Down Expand Up @@ -535,7 +535,7 @@ func TestBlockImageWithAltText(t *testing.T) {
assert.EqualValues(t, expectedDocument, actualDocument)
}

func TestBlockImageWithIDAndTitleAndDimensions(t *testing.T) {
func TestBlockImageWithDimensionsAndIDLinkTitleMeta(t *testing.T) {
// given an inline with an external lin
actualDocument, errs := ParseString(`[#img-foobar]
.A title to foobar
Expand All @@ -549,9 +549,9 @@ image::images/foo.png[the foo.png image,600,400]`)
height := "400"
expectedDocument := &types.Document{
Elements: []types.DocElement{
&types.InlineContent{Elements: []types.DocElement{&types.StringElement{Content: "[#img-foobar]"}}},
&types.InlineContent{Elements: []types.DocElement{&types.StringElement{Content: ".A title to foobar"}}},
&types.InlineContent{Elements: []types.DocElement{&types.StringElement{Content: "[link=http://foo.bar]"}}},
&types.ElementID{ID: "#img-foobar"},
&types.ElementTitle{Content: "A title to foobar"},
&types.ElementLink{Path: "http://foo.bar"},
&types.BlockImage{
Path: "images/foo.png",
AltText: &altText,
Expand All @@ -563,3 +563,143 @@ image::images/foo.png[the foo.png image,600,400]`)
log.Debugf("expected document: %s", expectedDocument.String())
assert.EqualValues(t, expectedDocument, actualDocument)
}

func TestElementLink(t *testing.T) {
// given an inline with an external lin
actualDocument, errs := ParseString(`[link=http://foo.bar]`)
require.Nil(t, errs)
log.Debugf("actual document: %s", actualDocument.String())
// then
expectedDocument := &types.Document{
Elements: []types.DocElement{
&types.ElementLink{Path: "http://foo.bar"},
},
}
log.Debugf("expected document: %s", expectedDocument.String())
assert.EqualValues(t, expectedDocument, actualDocument)
}

func TestElementLinkWithSpaces(t *testing.T) {
// given an inline with an element link
actualDocument, errs := ParseString(`[ link = http://foo.bar ]`)
require.Nil(t, errs)
log.Debugf("actual document: %s", actualDocument.String())
// then
expectedDocument := &types.Document{
Elements: []types.DocElement{
&types.ElementLink{Path: "http://foo.bar"},
},
}
log.Debugf("expected document: %s", expectedDocument.String())
assert.EqualValues(t, expectedDocument, actualDocument)
}

func TestElementLinkInvalid(t *testing.T) {
// given an inline with an element link with missing ']'
actualDocument, errs := ParseString(`[ link = http://foo.bar`)
require.Nil(t, errs)
log.Debugf("actual document: %s", actualDocument.String())
// then
expectedDocument := &types.Document{
Elements: []types.DocElement{
&types.InlineContent{
Elements: []types.DocElement{
&types.StringElement{Content: "[ link = "},
&types.ExternalLink{URL: "http://foo.bar"},
},
},
},
}
log.Debugf("expected document: %s", expectedDocument.String())
assert.EqualValues(t, expectedDocument, actualDocument)
}

func TestElementID(t *testing.T) {
// given an inline with an element ID
actualDocument, errs := ParseString(`[#img-foobar]`)
require.Nil(t, errs)
log.Debugf("actual document: %s", actualDocument.String())
// then
expectedDocument := &types.Document{
Elements: []types.DocElement{
&types.ElementID{ID: "#img-foobar"},
},
}
log.Debugf("expected document: %s", expectedDocument.String())
assert.EqualValues(t, expectedDocument, actualDocument)
}

func TestElementIDWithSpaces(t *testing.T) {
// given an inline with an element ID
actualDocument, errs := ParseString("[ #img-foobar ]")
require.Nil(t, errs)
log.Debugf("actual document: %s", actualDocument.String())
// then
expectedDocument := &types.Document{
Elements: []types.DocElement{
&types.ElementID{ID: "#img-foobar"},
},
}
log.Debugf("expected document: %s", expectedDocument.String())
assert.EqualValues(t, expectedDocument, actualDocument)
}

func TestElementIDInvalid(t *testing.T) {
// given an inline with an element ID with missing ']'
actualDocument, errs := ParseString(`[#img-foobar`)
require.Nil(t, errs)
log.Debugf("actual document: %s", actualDocument.String())
// then
expectedDocument := &types.Document{
Elements: []types.DocElement{
&types.InlineContent{Elements: []types.DocElement{&types.StringElement{Content: "[#img-foobar"}}},
},
}
log.Debugf("expected document: %s", expectedDocument.String())
assert.EqualValues(t, expectedDocument, actualDocument)
}

func TestElementTitle(t *testing.T) {
// given an inline with an element title
actualDocument, errs := ParseString(`.a title`)
require.Nil(t, errs)
log.Debugf("actual document: %s", actualDocument.String())
// then
expectedDocument := &types.Document{
Elements: []types.DocElement{
&types.ElementTitle{Content: "a title"},
},
}
log.Debugf("expected document: %s", expectedDocument.String())
assert.EqualValues(t, expectedDocument, actualDocument)
}

func TestElementTitleInvalid1(t *testing.T) {
// given an inline with an element title with extra space after '.'
actualDocument, errs := ParseString(". a title")
require.Nil(t, errs)
log.Debugf("actual document: %s", actualDocument.String())
// then
expectedDocument := &types.Document{
Elements: []types.DocElement{
&types.InlineContent{Elements: []types.DocElement{&types.StringElement{Content: ". a title"}}},
},
}
log.Debugf("expected document: %s", expectedDocument.String())
assert.EqualValues(t, expectedDocument, actualDocument)
}

func TestElementTitleInvalid2(t *testing.T) {
// given an inline with an element ID with missing '.' as first character
actualDocument, errs := ParseString(`!a title`)
require.Nil(t, errs)
log.Debugf("actual document: %s", actualDocument.String())
// then
expectedDocument := &types.Document{
Elements: []types.DocElement{
&types.InlineContent{Elements: []types.DocElement{&types.StringElement{Content: "!a title"}}},
},
}
log.Debugf("expected document: %s", expectedDocument.String())
assert.EqualValues(t, expectedDocument, actualDocument)
}
Loading

0 comments on commit c08a7f3

Please sign in to comment.