size: 4 KiB

1--- @module 'djot'
2--- Parse and render djot light markup format. See https://djot.net.
3---
4--- @usage
5--- local djot = require("djot")
6--- local input = "This is *djot*"
7--- local doc = djot.parse(input)
8--- -- render as HTML:
9--- print(djot.render_html(doc))
10---
11--- -- render as AST:
12--- print(djot.render_ast_pretty(doc))
13---
14--- -- or in JSON:
15--- print(djot.render_ast_json(doc))
16---
17--- -- alter the AST with a filter:
18--- local src = "return { str = function(e) e.text = e.text:upper() end }"
19--- -- subordinate modules like filter can be accessed as fields
20--- -- and are lazily loaded.
21--- local filter = djot.filter.load_filter(src)
22--- djot.filter.apply_filter(doc, filter)
23---
24--- -- streaming parser:
25--- for startpos, endpos, annotation in djot.parse_events("*hello there*") do
26--- print(startpos, endpos, annotation)
27--- end
28
29local unpack = unpack or table.unpack
30local Parser = require("djot.block").Parser
31local ast = require("djot.ast")
32local html = require("djot.html")
33local json = require("djot.json")
34local filter = require("djot.filter")
35
36--- @class StringHandle
37local StringHandle = {}
38
39--- @return (StringHandle)
40function StringHandle:new()
41 local buffer = {}
42 setmetatable(buffer, StringHandle)
43 StringHandle.__index = StringHandle
44 return buffer
45end
46
47--- @param s (string)
48function StringHandle:write(s)
49 self[#self + 1] = s
50end
51
52--- @return (string)
53function StringHandle:flush()
54 return table.concat(self)
55end
56
57--- Parse a djot text and construct an abstract syntax tree (AST)
58--- representing the document.
59--- @param input (string) input string
60--- @param sourcepos (boolean) if true, source positions are included in the AST
61--- @param warn (function) function that processes a warning, accepting a warning
62--- object with `pos` and `message` fields.
63--- @return (AST)
64local function parse(input, sourcepos, warn)
65 local parser = Parser:new(input, warn)
66 return ast.to_ast(parser, sourcepos)
67end
68
69--- Parses a djot text and returns an iterator over events, consisting
70--- of a start position (bytes), and an position (bytes), and an
71--- annotation.
72--- @param input (string) input string
73--- @param warn (function) function that processes a warning, accepting a warning
74--- object with `pos` and `message` fields.
75--- @return integer, integer, string an iterator over events.
76---
77--- for startpos, endpos, annotation in djot.parse_events("hello *world") do
78--- ...
79--- end
80local function parse_events(input, warn)
81 return Parser:new(input):events()
82end
83
84--- Render a document's AST in human-readable form.
85--- @param doc (AST) the AST
86--- @return (string) rendered AST
87local function render_ast_pretty(doc)
88 local handle = StringHandle:new()
89 ast.render(doc, handle)
90 return handle:flush()
91end
92
93--- Render a document's AST in JSON.
94--- @param doc (AST) the AST
95--- @return (string) rendered AST (JSON string)
96local function render_ast_json(doc)
97 return json.encode(doc) .. "\n"
98end
99
100--- Render a document as HTML.
101--- @param doc (AST) the AST
102--- @return (string) rendered document (HTML string)
103local function render_html(doc)
104 local handle = StringHandle:new()
105 local renderer = html.Renderer:new()
106 renderer:render(doc, handle)
107 return handle:flush()
108end
109
110--- Render an event as a JSON array.
111--- @param startpos (integer) starting byte position
112--- @param endpos (integer) ending byte position
113--- @param annotation (string) annotation of event
114--- @return (string) rendered event (JSON string)
115local function render_event(startpos, endpos, annotation)
116 return string.format("[%q,%d,%d]", annotation, startpos, endpos)
117end
118
119--- Parse a document and render as a JSON array of events.
120--- @param input (string) the djot document
121--- @param warn (function) function that emits warnings, taking as argumnet
122--- an object with fields 'message' and 'pos'
123--- @return (string) rendered events (JSON string)
124local function parse_and_render_events(input, warn)
125 local handle = StringHandle:new()
126 local idx = 0
127 for startpos, endpos, annotation in parse_events(input, warn) do
128 idx = idx + 1
129 if idx == 1 then
130 handle:write("[")
131 else
132 handle:write(",")
133 end
134 handle:write(render_event(startpos, endpos, annotation) .. "\n")
135 end
136 handle:write("]\n")
137 return handle:flush()
138end
139
140--- djot version (string)
141local version = "0.2.1"
142
143--- @export
144local G = {
145 parse = parse,
146 parse_events = parse_events,
147 parse_and_render_events = parse_and_render_events,
148 render_html = render_html,
149 render_ast_pretty = render_ast_pretty,
150 render_ast_json = render_ast_json,
151 render_event = render_event,
152 version = version
153}
154
155-- Lazily load submodules, e.g. djot.filter
156setmetatable(G,{ __index = function(t,name)
157 local mod = require("djot." .. name)
158 rawset(t,name,mod)
159 return t[name]
160 end })
161
162return G