Home

Awesome

Md Logo Kantox ❤ OSS  Test  Dialyzer

Stream markup parser, extendable, flexible, blazingly fast, with callbacks and more, ready for markdown…


Main Focus

This library is not yet another markdown parser, rather it’s a highly configurable and extendable parser for any custom markdown-like markup. It has been created mostly to allow custom markdown syntax, like ^foo^ for superscript, or ⇓bar⇓ for subscript. It also supports custom parsers for anything that cannot be handled with generic parsers, inspired by markdown (something more complex than standard markdown provides.)

The library provides callbacks for all the default syntax handlers, as well as for custom handlers, allowing the on-fly modification of what’s currently being processed.

Md parses the incoming stream once and keeps the state, producing an AST of the input document. It has an ability to recover from errors collecting them.

It currently does not support (and I frankly doubt it ever will) lists with embedded quotes, and other contrived syntax. If one needs to perfectly parse the common markdown, Md is probably not the correct choice.

But if one wants to easily extend syntax almost without limits, Md might be good.

Markup Handling

There are several different syntax patterns recognizable by Md. Those are:

Syntax description

The syntax must be configured at compile time (because parse/2 handlers are generated in compile time.) It is a map, having settings key

settings: %{
  outer: :p,
  span: :span,
  empty_tags: ~w|img hr br|a
}

and key ⇒ list_of_tuples key-values, providing a text markup representation and its handling rules. Here is the excerpt from the default parser for braces

  brace: %{
    "*" => %{tag: :b},
    "_" => %{tag: :i},
    "**" => %{tag: :strong, attributes: %{class: "nota-bene"}},
    "__" => %{tag: :em},
    "~" => %{tag: :s},
    "~~" => %{tag: :del},
    "`" => %{tag: :code, mode: :raw, attributes: %{class: "code-inline"}}
  }

For more examples of what properties are allowed for each kind of handlers, see the sources (ATM.)

Predefined parsers

Md comes with a generic predefined parser Md.Parser.Default, which includes all the markup currently supported by Md.

Custom parser definition would be usually based on Md.Parser.Syntax.Void syntax as shown below

defmodule MyParser do
  use Md.Parser

  alias Md.Parser.Syntax.Void

  @default_syntax Map.put(Void.syntax(), :settings, Void.settings())
  @syntax @default_syntax |> Map.merge(%{
    comment: [{"<!--", %{closing: "-->"}}],
    paragraph: [
      {"##", %{tag: :h2}},
      {"###", %{tag: :h3}},
      {">", %{tag: :blockquote}}
    ],
    list:
      [
        {"- ", %{tag: :li, outer: :ul}},
        {"+ ", %{tag: :li, outer: :ol}}
      ]
    brace: [
      {"*", %{tag: :b}},
      {"_", %{tag: :i}},
      {"~", %{tag: :s}},
      {"`", %{tag: :code, mode: :raw, attributes: %{class: "code-inline"}}}
    ]
  })
end

@syntax module attribute must be declared, or DSL used as shown below (declarations), or an argument in a call to use Md.Parser. The separate declarations will be collected and merged.

defmodule MyDSLParser do
  @my_syntax %{brace: [{"***", %{tag: "u"}}]}
  
  use Md.Parser, syntax: @my_syntax
  import Md.Parser.DSL

  comment "<!--", %{closing: "-->"}
  ...
end

Instead of @syntax module attribute, one might use


Changelog

Installation

def deps do
  [
    {:md, "~> 0.1"}
  ]
end

Documentation