# HG changeset patch # User wenzelm # Date 1444836261 -7200 # Node ID 20ff1d5c74e1884ea2c63e9bbd7eb947d851ce1b # Parent 8626c2fed037dad8033f550f38c01e298cbc844a minimal support for Markdown documents; diff -r 8626c2fed037 -r 20ff1d5c74e1 src/Pure/ROOT --- a/src/Pure/ROOT Wed Oct 14 17:24:03 2015 +0200 +++ b/src/Pure/ROOT Wed Oct 14 17:24:21 2015 +0200 @@ -229,6 +229,7 @@ "System/system_channel.ML" "Thy/html.ML" "Thy/latex.ML" + "Thy/markdown.ML" "Thy/present.ML" "Thy/term_style.ML" "Thy/thy_header.ML" diff -r 8626c2fed037 -r 20ff1d5c74e1 src/Pure/Thy/markdown.ML --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Pure/Thy/markdown.ML Wed Oct 14 17:24:21 2015 +0200 @@ -0,0 +1,105 @@ +(* Title: Pure/Thy/markdown.ML + Author: Makarius + +Minimal support for Markdown documents (see also http://commonmark.org). +*) + +signature MARKDOWN = +sig + type line + val read: Input.source -> line list list +end; + +structure Markdown: MARKDOWN = +struct + +(* line with optional item marker *) + +datatype kind = Itemize | Enumerate | Description; + +datatype line = + Line of + {content: Antiquote.text_antiquote list, + is_empty: bool, + indent: int, + marker: (kind * Position.T) option}; + +fun line_content (Line {content, ...}) = content; +fun line_is_empty (Line {is_empty, ...}) = is_empty; +fun line_indent (Line {indent, ...}) = indent; +fun line_marker (Line {marker, ...}) = marker; + +local + +fun bad_blank ((s, _): Symbol_Pos.T) = Symbol.is_ascii_blank s andalso s <> Symbol.space; +val bad_blanks = maps (fn Antiquote.Text ss => filter bad_blank ss | _ => []); + +fun check_blanks content = + (case bad_blanks content of + [] => () + | (c, pos) :: _ => + error ("Bad blank character " ^ quote (ML_Syntax.print_char c) ^ Position.here pos)); + +fun is_space ((s, _): Symbol_Pos.T) = s = Symbol.space; +val is_empty = forall (fn Antiquote.Text ss => forall is_space ss | _ => false); + +fun line_kind (Antiquote.Text ss :: _) = + let + val (spaces, rest) = take_prefix is_space ss; + fun make_marker kind = + (case rest of + [(_, pos)] => (kind, pos) + | (_, pos) :: (" ", _) :: _ => (kind, pos) + | (_, pos) :: _ => error ("Missing space after item marker" ^ Position.here pos)); + val marker = + (case rest of + ("\<^item>", _) :: _ => SOME (make_marker Itemize) + | ("\<^enum>", _) :: _ => SOME (make_marker Enumerate) + | ("\<^descr>", _) :: _ => SOME (make_marker Description) + | _ => NONE); + in (length spaces, marker) end + | line_kind _ = (0, NONE); + +in + +fun make_line content = + let + val _ = check_blanks content; + val (indent, marker) = line_kind content; + in Line {content = content, is_empty = is_empty content, indent = indent, marker = marker} end; + +end; + + +(* spans of related lines *) + +local + +val eof = make_line [Antiquote.Text [(Symbol.eof, Position.none)]]; +val stopper = Scan.stopper (K eof) (fn line => line = eof); + +fun item_line line = is_some (line_marker line); +fun plain_line line = is_none (line_marker line) andalso line <> eof; + +val parse_span = + Scan.one item_line -- Scan.many plain_line >> op :: || + Scan.many1 plain_line || + Scan.many1 line_is_empty; + +in + +fun read_spans lines = + the_default [] (Scan.read stopper (Scan.repeat parse_span) lines); + +end; + + +(* document structure *) + +fun read input = + Antiquote.read input + |> Antiquote.split_lines + |> map make_line + |> read_spans; + +end;