aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mardown/ast.go6
-rw-r--r--mardown/ast_external.go141
-rw-r--r--mardown/ast_external_test.go31
-rw-r--r--mardown/ast_modifier.go6
-rw-r--r--mardown/ast_paragraph.go7
5 files changed, 187 insertions, 4 deletions
diff --git a/mardown/ast.go b/mardown/ast.go
index 3794ff1..ed4d1f8 100644
--- a/mardown/ast.go
+++ b/mardown/ast.go
@@ -37,7 +37,7 @@ func (t *tree) String() string {
func ast(lxs *lexers) (*tree, error) {
tr := new(tree)
- newLine := false
+ newLine := true
for lxs.Next() {
b, err := getBlock(lxs, newLine)
if err != nil {
@@ -64,8 +64,8 @@ func getBlock(lxs *lexers, newLine bool) (block, error) {
b, err = header(lxs)
}
case lexerExternal:
- if newLine && lxs.Current().Value == "!" {
- //TODO: handle
+ if newLine && lxs.Current().Value == "![" {
+ b, err = external(lxs)
} else {
b, err = paragraph(lxs, false)
}
diff --git a/mardown/ast_external.go b/mardown/ast_external.go
new file mode 100644
index 0000000..87fb880
--- /dev/null
+++ b/mardown/ast_external.go
@@ -0,0 +1,141 @@
+package mardown
+
+import (
+ "fmt"
+ "html/template"
+)
+
+type astLink struct {
+ content block
+ href block
+}
+
+func (a *astLink) Eval() (template.HTML, error) {
+ content, err := a.content.Eval()
+ if err != nil {
+ return "", err
+ }
+ href, err := a.href.Eval()
+ if err != nil {
+ return "", err
+ }
+ return template.HTML(fmt.Sprintf(`<a href="%s">%s</a>`, href, content)), nil
+}
+
+type astImage struct {
+ alt block
+ src block
+ source *astParagraph
+}
+
+func (a *astImage) Eval() (template.HTML, error) {
+ alt, err := a.alt.Eval()
+ if err != nil {
+ return "", err
+ }
+ src, err := a.src.Eval()
+ if err != nil {
+ return "", err
+ }
+ if a.source == nil {
+ return template.HTML(fmt.Sprintf(`<figure><img alt="%s" src="%s"></figure>`, alt, src)), nil
+ }
+ source, err := a.source.Eval()
+ if err != nil {
+ return "", err
+ }
+ return template.HTML(fmt.Sprintf(`<figure><img alt="%s" src="%s"><figcaption>%s</figcaption></figure>`, alt, src, source)), nil
+}
+
+func external(lxs *lexers) (block, error) {
+ tp := lxs.Current().Value
+ if !lxs.Next() {
+ return astLiteral(tp), nil
+ }
+ lxs.Before() // because we call Next
+ var b block
+ var err error
+ switch tp {
+ case "![":
+ b, err = image(lxs)
+ case "[":
+ b, err = link(lxs)
+ default:
+ b = astLiteral(tp)
+ }
+ return b, err
+}
+
+func link(lxs *lexers) (block, error) {
+ lk := new(astLink)
+ start := lxs.current
+ content, href, _, ok := parseExternal(lxs, 1)
+ if !ok {
+ return reset(lxs, start), nil
+ }
+ lk.content = astLiteral(content)
+ lk.href = astLiteral(href)
+ return lk, nil
+}
+
+func image(lxs *lexers) (block, error) {
+ img := new(astImage)
+ start := lxs.current
+ alt, src, _, ok := parseExternal(lxs, 2)
+ if !ok {
+ return reset(lxs, start), nil
+ }
+ img.alt = astLiteral(alt)
+ img.src = astLiteral(src)
+ //img.source = astLiteral(source)
+ return img, nil
+}
+
+func parseExternal(lxs *lexers, maxBreak int) (string, string, string, bool) {
+ next := false
+ var s string
+ var first string
+ var end string
+ n := 0
+ for lxs.Next() && n < maxBreak {
+ switch lxs.Current().Type {
+ case lexerBreak:
+ n++
+ case lexerExternal:
+ if n > 0 && (first == "" || end == "") {
+ return "", "", "", false
+ }
+ n = 0
+ if !next {
+ if lxs.Current().Value != "](" || !lxs.Next() {
+ return "", "", "", false
+ }
+ lxs.Before() // because we called Next
+ first = s
+ s = ""
+ next = true
+ } else {
+ if lxs.Current().Value != ")" {
+ return "", "", "", false
+ }
+ if maxBreak == 1 {
+ return first, s, "", true
+ }
+ end = s
+ s = ""
+ }
+ default:
+ n = 0
+ s += lxs.Current().Value
+ }
+ }
+ if maxBreak == 1 {
+ return "", "", "", false
+ }
+ return first, end, s, true
+}
+
+func reset(lxs *lexers, start int) block {
+ lxs.current = start
+ return astLiteral(lxs.Current().Value)
+}
diff --git a/mardown/ast_external_test.go b/mardown/ast_external_test.go
new file mode 100644
index 0000000..fc0597d
--- /dev/null
+++ b/mardown/ast_external_test.go
@@ -0,0 +1,31 @@
+package mardown
+
+import "testing"
+
+func TestExternal(t *testing.T) {
+ lxs := lex("[content](href)")
+ tree, err := ast(lxs)
+ if err != nil {
+ t.Fatal(err)
+ }
+ got, err := tree.Eval()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if string(got) != `<p><a href="href">content</a></p>` {
+ t.Errorf("invalid value, got %s", got)
+ }
+
+ lxs = lex("![image alt](image src)")
+ tree, err = ast(lxs)
+ if err != nil {
+ t.Fatal(err)
+ }
+ got, err = tree.Eval()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if string(got) != `<figure><img alt="image alt" src="image src"></figure>` {
+ t.Errorf("invalid value, got %s", got)
+ }
+}
diff --git a/mardown/ast_modifier.go b/mardown/ast_modifier.go
index 13cd91d..bd04b28 100644
--- a/mardown/ast_modifier.go
+++ b/mardown/ast_modifier.go
@@ -117,6 +117,12 @@ func modifier(lxs *lexers) (*astModifier, error) {
case lexerExternal:
if lxs.Current().Value == "!" {
s += lxs.Current().Value
+ } else {
+ ext, err := external(lxs)
+ if err != nil {
+ return nil, err
+ }
+ mod.content = append(mod.content, ext)
}
default:
return nil, ErrInvalidTypeInModifier
diff --git a/mardown/ast_paragraph.go b/mardown/ast_paragraph.go
index ee71794..44b5dd9 100644
--- a/mardown/ast_paragraph.go
+++ b/mardown/ast_paragraph.go
@@ -68,8 +68,13 @@ func paragraph(lxs *lexers, oneLine bool) (*astParagraph, error) {
n = 0
if lxs.Current().Value == "!" {
tree.content = append(tree.content, astLiteral(lxs.Current().Value))
+ } else {
+ ext, err := external(lxs)
+ if err != nil {
+ return nil, err
+ }
+ tree.content = append(tree.content, ext)
}
- //TODO: handle
case lexerCode:
n = 0
b, err := code(lxs)