diff options
| -rw-r--r-- | mardown/ast.go | 17 | ||||
| -rw-r--r-- | mardown/ast_code.go | 10 | ||||
| -rw-r--r-- | mardown/ast_external.go | 12 | ||||
| -rw-r--r-- | mardown/ast_header.go | 10 | ||||
| -rw-r--r-- | mardown/ast_list.go | 4 | ||||
| -rw-r--r-- | mardown/ast_modifier.go | 5 | ||||
| -rw-r--r-- | mardown/ast_paragraph.go | 9 | ||||
| -rw-r--r-- | mardown/ast_quote.go | 4 | ||||
| -rw-r--r-- | mardown/error.go | 38 | ||||
| -rw-r--r-- | mardown/error_test.go | 26 | ||||
| -rw-r--r-- | mardown/eval.go | 16 |
11 files changed, 116 insertions, 35 deletions
diff --git a/mardown/ast.go b/mardown/ast.go index ed4d1f8..ceeec6f 100644 --- a/mardown/ast.go +++ b/mardown/ast.go @@ -11,14 +11,14 @@ import ( var ErrUnkownLexType = errors.New("unkown lex type") type block interface { - Eval() (template.HTML, error) + Eval() (template.HTML, *ParseError) } type tree struct { blocks []block } -func (t *tree) Eval() (template.HTML, error) { +func (t *tree) Eval() (template.HTML, *ParseError) { var content template.HTML for _, c := range t.blocks { ct, err := c.Eval() @@ -35,7 +35,7 @@ func (t *tree) String() string { return string(b) } -func ast(lxs *lexers) (*tree, error) { +func ast(lxs *lexers) (*tree, *ParseError) { tr := new(tree) newLine := true for lxs.Next() { @@ -53,9 +53,9 @@ func ast(lxs *lexers) (*tree, error) { return tr, nil } -func getBlock(lxs *lexers, newLine bool) (block, error) { +func getBlock(lxs *lexers, newLine bool) (block, *ParseError) { var b block - var err error + var err *ParseError switch lxs.Current().Type { case lexerHeader: if !newLine { @@ -83,7 +83,7 @@ func getBlock(lxs *lexers, newLine bool) (block, error) { } case lexerCode: if !newLine && len(lxs.Current().Value) == 3 { - return nil, ErrInvalidCodeBlockPosition + return nil, &ParseError{lxs: *lxs, internal: ErrInvalidCodeBlockPosition} } if len(lxs.Current().Value) == 1 { b, err = paragraph(lxs, false) @@ -94,7 +94,10 @@ func getBlock(lxs *lexers, newLine bool) (block, error) { b, err = paragraph(lxs, false) case lexerBreak: // do nothing default: - err = errors.Join(ErrUnkownLexType, fmt.Errorf("type received: %s", lxs.Current().Type)) + err = &ParseError{ + lxs: *lxs, + internal: errors.Join(ErrUnkownLexType, fmt.Errorf("type received: %s", lxs.Current().Type)), + } } return b, err } diff --git a/mardown/ast_code.go b/mardown/ast_code.go index 7edd6ba..f029df2 100644 --- a/mardown/ast_code.go +++ b/mardown/ast_code.go @@ -25,18 +25,18 @@ type astCode struct { codeType codeType } -func (a *astCode) Eval() (template.HTML, error) { +func (a *astCode) Eval() (template.HTML, *ParseError) { switch a.codeType { case codeOneLine: return template.HTML(fmt.Sprintf("<code>%s</code>", template.HTMLEscapeString(a.content))), nil case codeMultiLine: return template.HTML(fmt.Sprintf("<pre><code>%s</code></pre>", template.HTMLEscapeString(a.content))), nil default: - return "", ErrUnknownCodeType + return "", &ParseError{lxs: lexers{}, internal: ErrUnknownCodeType} } } -func code(lxs *lexers) (*astCode, error) { +func code(lxs *lexers) (*astCode, *ParseError) { tree := new(astCode) current := lxs.Current().Value if len(current) == 3 { @@ -44,13 +44,13 @@ func code(lxs *lexers) (*astCode, error) { } else if len(current) == 1 { tree.codeType = codeOneLine } else { - return nil, ErrInvalidCodeFormat + return nil, &ParseError{lxs: *lxs, internal: ErrInvalidCodeFormat} } started := false for lxs.Next() && lxs.Current().Value != current { if lxs.Current().Type == lexerBreak { if tree.codeType == codeOneLine { - return nil, ErrInvalidCodeFormat + return nil, &ParseError{lxs: *lxs, internal: ErrInvalidCodeFormat} } if !started { started = true diff --git a/mardown/ast_external.go b/mardown/ast_external.go index 78f12c8..98a090e 100644 --- a/mardown/ast_external.go +++ b/mardown/ast_external.go @@ -10,7 +10,7 @@ type astLink struct { href block } -func (a *astLink) Eval() (template.HTML, error) { +func (a *astLink) Eval() (template.HTML, *ParseError) { content, err := a.content.Eval() if err != nil { return "", err @@ -28,7 +28,7 @@ type astImage struct { source []*astParagraph } -func (a *astImage) Eval() (template.HTML, error) { +func (a *astImage) Eval() (template.HTML, *ParseError) { alt, err := a.alt.Eval() if err != nil { return "", err @@ -52,14 +52,14 @@ func (a *astImage) Eval() (template.HTML, error) { return template.HTML(fmt.Sprintf(`<figure><img alt="%s" src="%s"><figcaption>%s</figcaption></figure>`, alt, src, s)), nil } -func external(lxs *lexers) (block, error) { +func external(lxs *lexers) (block, *ParseError) { tp := lxs.Current().Value if !lxs.Next() { return astLiteral(tp), nil } lxs.Before() // because we call Next var b block - var err error + var err *ParseError switch tp { case "![": b, err = image(lxs) @@ -71,7 +71,7 @@ func external(lxs *lexers) (block, error) { return b, err } -func link(lxs *lexers) (block, error) { +func link(lxs *lexers) (block, *ParseError) { lk := new(astLink) start := lxs.current content, href, _, ok := parseExternal(lxs, false) @@ -83,7 +83,7 @@ func link(lxs *lexers) (block, error) { return lk, nil } -func image(lxs *lexers) (block, error) { +func image(lxs *lexers) (block, *ParseError) { img := new(astImage) start := lxs.current alt, src, source, ok := parseExternal(lxs, true) diff --git a/mardown/ast_header.go b/mardown/ast_header.go index 5b13411..0ce8a22 100644 --- a/mardown/ast_header.go +++ b/mardown/ast_header.go @@ -13,9 +13,9 @@ type astHeader struct { content *astParagraph } -func (a *astHeader) Eval() (template.HTML, error) { +func (a *astHeader) Eval() (template.HTML, *ParseError) { if a.level > 6 { - return "", ErrInvalidHeader + return "", &ParseError{lxs: lexers{}, internal: ErrInvalidCodeFormat} } var content template.HTML content, err := a.content.Eval() @@ -25,12 +25,12 @@ func (a *astHeader) Eval() (template.HTML, error) { return template.HTML(fmt.Sprintf("<h%d>%s</h%d>", a.level, trimSpace(content), a.level)), nil } -func header(lxs *lexers) (*astHeader, error) { +func header(lxs *lexers) (*astHeader, *ParseError) { b := &astHeader{level: uint(len(lxs.Current().Value))} if !lxs.Next() { - return nil, ErrInvalidHeader + return nil, &ParseError{lxs: *lxs, internal: ErrInvalidHeader} } - var err error + var err *ParseError b.content, err = paragraph(lxs, true) if err != nil { return nil, err diff --git a/mardown/ast_list.go b/mardown/ast_list.go index f95324b..39f0178 100644 --- a/mardown/ast_list.go +++ b/mardown/ast_list.go @@ -20,7 +20,7 @@ type astList struct { content []*astParagraph } -func (a *astList) Eval() (template.HTML, error) { +func (a *astList) Eval() (template.HTML, *ParseError) { var content template.HTML for _, c := range a.content { ct, err := c.Eval() @@ -32,7 +32,7 @@ func (a *astList) Eval() (template.HTML, error) { return template.HTML(fmt.Sprintf("<%s>%s</%s>", a.tag, content, a.tag)), nil } -func list(lxs *lexers) (block, error) { +func list(lxs *lexers) (block, *ParseError) { tree := new(astList) tree.tag = detectListType(lxs.Current().Value) if len(tree.tag) == 0 { diff --git a/mardown/ast_modifier.go b/mardown/ast_modifier.go index bd04b28..93a5af3 100644 --- a/mardown/ast_modifier.go +++ b/mardown/ast_modifier.go @@ -8,7 +8,6 @@ import ( ) var ( - ErrInternalError = errors.New("internal error") ErrInvalidModifier = errors.Join(ErrInvalidParagraph, errors.New("invalid modifier organization")) ErrInvalidTypeInModifier = errors.Join(ErrInvalidParagraph, errors.New("invalid type in modifier")) ) @@ -27,12 +26,12 @@ type astModifier struct { super bool } -func (a *astModifier) Eval() (template.HTML, error) { +func (a *astModifier) Eval() (template.HTML, *ParseError) { var content template.HTML for _, c := range a.content { ct, err := c.Eval() if err != nil { - return "", err + return "", &ParseError{lxs: lexers{}, internal: err} } content += ct } diff --git a/mardown/ast_paragraph.go b/mardown/ast_paragraph.go index 23d526e..15d9bb8 100644 --- a/mardown/ast_paragraph.go +++ b/mardown/ast_paragraph.go @@ -15,7 +15,7 @@ type astParagraph struct { oneLine bool } -func (a *astParagraph) Eval() (template.HTML, error) { +func (a *astParagraph) Eval() (template.HTML, *ParseError) { var content template.HTML for _, c := range a.content { ct, err := c.Eval() @@ -30,7 +30,7 @@ func (a *astParagraph) Eval() (template.HTML, error) { return template.HTML(fmt.Sprintf("<p>%s</p>", trimSpace(content))), nil } -func paragraph(lxs *lexers, oneLine bool) (*astParagraph, error) { +func paragraph(lxs *lexers, oneLine bool) (*astParagraph, *ParseError) { tree := new(astParagraph) tree.oneLine = oneLine maxBreak := 2 @@ -40,7 +40,6 @@ func paragraph(lxs *lexers, oneLine bool) (*astParagraph, error) { n := 0 lxs.current-- // because we do not use it before the next for lxs.Next() && n < maxBreak { - //println("p", strings.ReplaceAll(lxs.Current().Value, "\n", "/n")) switch lxs.Current().Type { case lexerBreak: n += len(lxs.Current().Value) @@ -62,7 +61,7 @@ func paragraph(lxs *lexers, oneLine bool) (*astParagraph, error) { n = 0 mod, err := modifier(lxs) if err != nil { - return nil, err + return nil, &ParseError{lxs: *lxs, internal: err} } tree.content = append(tree.content, mod) case lexerExternal: @@ -94,6 +93,6 @@ func paragraph(lxs *lexers, oneLine bool) (*astParagraph, error) { type astLiteral string -func (a astLiteral) Eval() (template.HTML, error) { +func (a astLiteral) Eval() (template.HTML, *ParseError) { return template.HTML(template.HTMLEscapeString(string(a))), nil } diff --git a/mardown/ast_quote.go b/mardown/ast_quote.go index f046445..ffc8e83 100644 --- a/mardown/ast_quote.go +++ b/mardown/ast_quote.go @@ -11,7 +11,7 @@ type astQuote struct { source []*astParagraph } -func (a *astQuote) Eval() (template.HTML, error) { +func (a *astQuote) Eval() (template.HTML, *ParseError) { var quote template.HTML for _, c := range a.quote { ct, err := c.Eval() @@ -36,7 +36,7 @@ func (a *astQuote) Eval() (template.HTML, error) { return template.HTML(fmt.Sprintf(`<div class="quote">%s</div>`, quote)), nil } -func quote(lxs *lexers) (*astQuote, error) { +func quote(lxs *lexers) (*astQuote, *ParseError) { tree := new(astQuote) n := 0 quoteContinue := true diff --git a/mardown/error.go b/mardown/error.go new file mode 100644 index 0000000..f85dfbb --- /dev/null +++ b/mardown/error.go @@ -0,0 +1,38 @@ +package mardown + +import "fmt" + +type ParseError struct { + internal error + lxs lexers +} + +func (e *ParseError) Error() string { + return e.internal.Error() +} + +func (e *ParseError) Pretty() string { + lxs := e.lxs + if lxs.lexers == nil { + return e.internal.Error() + } + current := lxs.current - 1 + for lxs.Before() && lxs.Current().Type != lexerBreak { + } + current -= lxs.current + contxt := "" + ind := "" + for lxs.Next() && lxs.Current().Type != lexerBreak { + contxt += lxs.Current().Value + if lxs.current < current { + if lxs.current == current-2 { + ind += "^" + } else { + for range len(lxs.Current().Value) { + ind += "~" + } + } + } + } + return fmt.Sprintf("%v\n\n%s\n%s", e, contxt, ind) +} diff --git a/mardown/error_test.go b/mardown/error_test.go new file mode 100644 index 0000000..bcf6143 --- /dev/null +++ b/mardown/error_test.go @@ -0,0 +1,26 @@ +package mardown + +import "testing" + +func TestError(t *testing.T) { + v, err := Parse("**bonsoir") + if err == nil { + t.Errorf("expected error, got %s", v) + } else { + t.Log(err.Pretty()) + } + + v, err = Parse("bo*nso**ir") + if err == nil { + t.Errorf("expected error, got %s", v) + } else { + t.Log(err.Pretty()) + } + + v, err = Parse("test ``` hehe") + if err == nil { + t.Errorf("expected error, got %s", v) + } else { + t.Log(err.Pretty()) + } +} diff --git a/mardown/eval.go b/mardown/eval.go new file mode 100644 index 0000000..c085767 --- /dev/null +++ b/mardown/eval.go @@ -0,0 +1,16 @@ +package mardown + +import "html/template" + +func Parse(s string) (template.HTML, *ParseError) { + lxs := lex(s) + tree, err := ast(lxs) + if err != nil { + return "", err + } + return tree.Eval() +} + +func ParseBytes(b []byte) (template.HTML, *ParseError) { + return Parse(string(b)) +} |
