From e20acb05fd33b8fbcfafee4b72734224093c2d20 Mon Sep 17 00:00:00 2001 From: Anhgelus Morhtuuzh Date: Tue, 30 Sep 2025 20:55:02 +0200 Subject: feat(markdown): ast supports modifier --- mardown/ast_header.go | 2 +- mardown/ast_modifier.go | 104 +++++++++++++++++++++++++++++++++++++++++++++++ mardown/ast_paragraph.go | 12 +++++- 3 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 mardown/ast_modifier.go (limited to 'mardown') diff --git a/mardown/ast_header.go b/mardown/ast_header.go index 07d7e1c..1526efa 100644 --- a/mardown/ast_header.go +++ b/mardown/ast_header.go @@ -25,7 +25,7 @@ func (a *astHeader) Eval() (template.HTML, error) { return template.HTML(fmt.Sprintf("%s", a.level, content, a.level)), nil } -func header(lxs lexers) (block, error) { +func header(lxs lexers) (*astHeader, error) { b := &astHeader{level: uint(len(lxs.Current().Value))} var err error b.content, err = paragraph(lxs, true) diff --git a/mardown/ast_modifier.go b/mardown/ast_modifier.go new file mode 100644 index 0000000..e505aef --- /dev/null +++ b/mardown/ast_modifier.go @@ -0,0 +1,104 @@ +package mardown + +import ( + "errors" + "fmt" + "html/template" +) + +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")) +) + +type modifierTag string + +const ( + boldTag modifierTag = "b" + emTag modifierTag = "em" + codeTag modifierTag = "code" +) + +type astModifier struct { + symbols string + tag modifierTag + content []block + parent *astModifier +} + +func (a *astModifier) Eval() (template.HTML, error) { + var content template.HTML + for _, c := range a.content { + ct, err := c.Eval() + if err != nil { + return "", err + } + content += ct + } + return template.HTML(fmt.Sprintf("<%s>%s", a.tag, content, a.tag)), nil +} + +func modifier(lxs lexers) (*astModifier, error) { + current := lxs.Current().Value + mod := modifierDetect(current) + modInside := mod + if len(mod.content) > 0 { + var ok bool + modInside, ok = mod.content[0].(*astModifier) + if modInside.content != nil && !ok { + return nil, ErrInternalError + } + // getting the last modifier + for len(modInside.content) > 0 { + modInside, ok = modInside.content[0].(*astModifier) + if modInside.content != nil && !ok { + return nil, ErrInternalError + } + } + } + n := len(modInside.symbols) + var s string + for lxs.Next() { + switch lxs.Current().Type { + case lexerLiteral: + s += lxs.Current().Value + case lexerModifier: + if len(lxs.Current().Value) < n { + return nil, ErrInvalidModifier + } + if lxs.Current().Value[:n] != modInside.symbols { + return nil, ErrInvalidModifier + } + modInside.content = append(modInside.content, astLiteral(s)) + s = "" + modInside = modInside.parent + default: + return nil, ErrInvalidTypeInModifier + } + } + return mod, nil +} + +func modifierDetect(val string) *astModifier { + mod := new(astModifier) + if len(val) == 1 { + mod.symbols = val + if val == "`" { + mod.tag = codeTag + } else { + mod.tag = emTag + } + return mod + } + if val[:2] == "**" || val[:2] == "__" { + mod.symbols = val + mod.tag = boldTag + } else { + mod = modifierDetect(val[:1]) + next := modifierDetect(val[1:]) + next.parent = mod + mod.content = append(mod.content, next) + } + return mod +} diff --git a/mardown/ast_paragraph.go b/mardown/ast_paragraph.go index 04f2086..3c8f985 100644 --- a/mardown/ast_paragraph.go +++ b/mardown/ast_paragraph.go @@ -47,7 +47,11 @@ func paragraph(lxs lexers, oneLine bool) (*astParagraph, error) { case lexerLiteral, lexerHeader: tree.content = append(tree.content, astLiteral(lxs.Current().Value)) case lexerModifier: - //TODO: handle + mod, err := modifier(lxs) + if err != nil { + return nil, err + } + tree.content = append(tree.content, mod) case lexerQuote: //TODO: handle case lexerEscape: @@ -62,7 +66,11 @@ func paragraph(lxs lexers, oneLine bool) (*astParagraph, error) { lxs.current-- // because we do not use it before the next return tree, nil } - //TODO: handle + mod, err := modifier(lxs) + if err != nil { + return nil, err + } + tree.content = append(tree.content, mod) } } return tree, nil -- cgit v1.2.3