mdom 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,379 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "error"
4
+ require_relative "node/inline_nodes"
5
+ require_relative "node/nodes"
6
+
7
+ module MDOM
8
+ # (2) AST1 → Ruby 源码(CSM §3.1;docs/IMPLEMENTATION.md §3)。
9
+ #
10
+ # 把一棵 AST1 翻译成**一段顶层顺序执行的 Ruby 程序**:排版节点变成 builder
11
+ # 调用,嵌入节点(块级嵌入 / 行内插值)按 +unsafe+ 决定是否原样注入。产出的
12
+ # 只是源码字符串,不绑定任何执行方式(docs/IMPLEMENTATION.md §6.2)——`eval`、
13
+ # 落盘后 `require`、交给 mruby 都可以。
14
+ #
15
+ # puts MDOM::Translator.to_ruby(MDOM::Parser.parse("# 标题\n"))
16
+ # # builder = MDOM::Builder.new
17
+ # # builder.heading(1) do
18
+ # # builder.text("标题")
19
+ # # end
20
+ # # builder.to_document
21
+ #
22
+ # 两侧的结构约束(docs/IMPLEMENTATION.md §6.3):排版段一律经由 +builder.*+
23
+ # 接收者,形式可辨识;嵌入段保持原样,边界明确(Ruby 块原文 / `interpolation`
24
+ # 的块)。这样宿主才有可能判断一段产物是否能在 mruby 上跑。
25
+ #
26
+ # == 行内内容结构化,不回吐标记
27
+ #
28
+ # 行内容器(段落、标题、单元格……)在产物里是**块形式**的 builder 调用,每个
29
+ # 行内子节点翻译成对应的 builder 行内方法:
30
+ #
31
+ # builder.paragraph do
32
+ # builder.text("正文 ")
33
+ # builder.strong do
34
+ # builder.text("粗")
35
+ # end
36
+ # end
37
+ #
38
+ # 产物因此**不经过 InlineParser**:翻译器不输出 CSM 标记文本,也就没有「产物
39
+ # 执行时再解析一次」这一步,排版段真正落在 mruby 子集里(CSM §3.1)。唯一仍
40
+ # 需要行内解析器的形态是 +inline: false+ 解析出的 AST——那时树里只有原文、没有
41
+ # 行内节点(见 #inline_block_statement)。
42
+ #
43
+ # == 插值
44
+ #
45
+ # +unsafe: true+ 时插值是 `builder.interpolation { expr }`:表达式由 Ruby 求值,
46
+ # 值按 #render 的分派表插入当前位置(CSM §3.3)。+unsafe: false+(默认)时是
47
+ # `builder.interpolation("expr")`,构造**未求值**的插值节点(CSM §3.14),交
48
+ # (6) 按独立样式输出。
49
+ #
50
+ # == 安全闸门(CSM §3.14)
51
+ #
52
+ # +unsafe+ 是**翻译期**参数,默认 +false+(安全):块级嵌入不写进产物(只留一
53
+ # 行注释占位),插值构造未求值节点,所以产物静态上不含用户代码。+unsafe: true+
54
+ # 才把嵌入原样注入。
55
+ #
56
+ # 安全模式下宿主不需要(也不应该)拿产物去 `eval`:未求值的嵌入由 (6) 直接吃
57
+ # AST1 就能按独立样式输出。求值本身只是宿主的一行 `eval`,mdom 不含求值器。
58
+ #
59
+ # 本类**不依赖 prism**:行内边界已由 InlineParser 判好,这里只做序列化。它只
60
+ # +require+ 节点定义(builder 侧)。
61
+ #
62
+ # == 转义
63
+ #
64
+ # 只有一层转义(#escape_ruby):把值编码进 Ruby 双引号字面量(`\` → `\\`、
65
+ # `"` → `\"`、`#` → `\#`)。旧版为「让 InlineParser 把纯文本当字面」而有的
66
+ # 第二层 CSM 转义(CSM §3.2 的 `\*`、`` \` ``)随行内结构化一起消失——产物里
67
+ # 不再有任何等待重新解析的 CSM 文本。
68
+ class Translator
69
+ # Ruby 双引号字面量的转义表。`#` 也转义,避免 `\#{` 里的 `#` 与随后的 `{`
70
+ # 被 Ruby 重新拼成插值。
71
+ RUBY_ESCAPES = { "\\" => "\\\\", "\"" => "\\\"", "#" => "\\#", "\n" => "\\n", "\t" => "\\t" }.freeze
72
+
73
+ # 安全模式下块级嵌入的占位注释(CSM §3.14)。
74
+ EMBED_PLACEHOLDER = "# [csm] ruby 嵌入已省略(unsafe: false)"
75
+
76
+ def self.to_ruby(node, **options)
77
+ new(**options).to_ruby(node)
78
+ end
79
+
80
+ # +indent+ 是产物每层嵌套的缩进宽度;+builder+ 是产物里 builder 变量的名字。
81
+ # +unsafe+ 为 +false+(默认)时嵌入节点不写进产物(CSM §3.14)。
82
+ def initialize(indent: 2, builder: "builder", unsafe: false)
83
+ @indent = " " * indent
84
+ @builder = builder
85
+ @unsafe = unsafe
86
+ end
87
+
88
+ # 翻译一棵树(+Node::Document+ 或任意子节点),返回 Ruby 源码字符串。
89
+ #
90
+ # 第一个 `__END__` 必须排在 `to_document` **之后**:Ruby 的 `__END__` 会截断
91
+ # 其后的程序文本,排在它后面的语句不会执行(CSM §3.1、§4)。
92
+ def to_ruby(node)
93
+ parts = [assignment] + statements(node, 0) + [finish]
94
+ marker = end_marker_of(node)
95
+ parts.concat(end_marker_statement(marker)) if marker
96
+ parts.join("\n")
97
+ end
98
+
99
+ private
100
+
101
+ def assignment = "#{@builder} = MDOM::Builder.new"
102
+
103
+ def finish = "#{@builder}.to_document"
104
+
105
+ def children_of(node)
106
+ node.type == :document ? node.children : [node]
107
+ end
108
+
109
+ def end_marker_of(node)
110
+ children_of(node).find { |child| child.type == :end_marker }
111
+ end
112
+
113
+ # -- 骨架 -----------------------------------------------------------------
114
+
115
+ def statements(node, depth)
116
+ children_of(node).reject { |child| child.type == :end_marker }
117
+ .flat_map { |child| statement(child, depth) }
118
+ end
119
+
120
+ def line(depth, text) = (@indent * depth) + text
121
+
122
+ # 逐节点分派。返回语句行数组(已含缩进)。
123
+ def statement(node, depth)
124
+ case node.type
125
+ when :heading then inline_block_statement(node, depth, "heading", heading_arguments(node))
126
+ when :paragraph then inline_block_statement(node, depth, "paragraph")
127
+ when :hrule then [line(depth, "#{@builder}.hrule")]
128
+ when :code_block then [line(depth, code_block_call(node))]
129
+ when :math_block then [line(depth, math_block_call(node))]
130
+ when :table then block_statement(node, depth, "#{@builder}.table")
131
+ when :table_row then block_statement(node, depth, "#{@builder}.row")
132
+ when :table_header_cell then inline_block_statement(node, depth, "header_cell", cell_arguments(node))
133
+ when :table_cell then inline_block_statement(node, depth, "cell", cell_arguments(node))
134
+ when :term then inline_block_statement(node, depth, "term")
135
+ when :cite then inline_block_statement(node, depth, "cite")
136
+ when :definition then block_statement(node, depth, "#{@builder}.definition")
137
+ when :list_item then block_statement(node, depth, item_head(node))
138
+ when :blockquote then block_statement(node, depth, "#{@builder}.blockquote")
139
+ when :list then block_statement(node, depth, list_head(node))
140
+ when :definition_list then block_statement(node, depth, "#{@builder}.definition_list")
141
+ when :div then block_statement(node, depth, "#{@builder}.division")
142
+ when :ruby_embed then ruby_embed_statement(node, depth)
143
+ when :end_marker then end_marker_statement(node)
144
+ else raise Error, "cannot translate #{node.type.inspect} to Ruby"
145
+ end
146
+ end
147
+
148
+ # 块级调用:无子节点时退化为单行,有子节点时用 `do ... end` 包住。
149
+ # 内容策略为 FLOW 的容器只收块级子节点(CSM 元素表),这里原样递归它们。
150
+ def block_statement(node, depth, head)
151
+ body = node.children.flat_map { |child| statement(child, depth + 1) }
152
+ return [line(depth, head)] if body.empty?
153
+
154
+ [line(depth, "#{head} do"), *body, line(depth, "end")]
155
+ end
156
+
157
+ # -- 块级节点 -------------------------------------------------------------
158
+
159
+ def heading_arguments(node)
160
+ args = [node.level.to_s]
161
+ args << "closed: true" if node.closed?
162
+ args
163
+ end
164
+
165
+ def item_head(node)
166
+ call(@builder, "item", item_arguments(node))
167
+ end
168
+
169
+ def item_arguments(node)
170
+ args = ["marker: #{ruby_literal(node.marker)}"]
171
+ args << "task: true" if node.task?
172
+ # 任务列表里的普通项:必须显式写 false,否则 builder 会从列表继承 task。
173
+ args << "task: false" if !node.task? && node.parent.is_a?(Node::List) && node.parent.task?
174
+ args << "checked: #{node.checked.inspect}" unless node.checked.nil?
175
+ args
176
+ end
177
+
178
+ def list_head(node)
179
+ args = [
180
+ "ordered: #{node.ordered?}",
181
+ "task: #{node.task?}",
182
+ "marker: #{ruby_literal(node.attributes[:marker])}"
183
+ ]
184
+ call(@builder, "list", args)
185
+ end
186
+
187
+ def code_block_call(node)
188
+ args = [ruby_literal(node.code)]
189
+ args << "lang: #{ruby_literal(node.lang)}" if node.lang
190
+ args << "params: #{ruby_literal(node.params)}" if node.params
191
+ fence = node.attributes[:fence]
192
+ args << "fence: #{fence}" unless fence.nil? || fence == 3
193
+ call(@builder, "code_block", args)
194
+ end
195
+
196
+ # 块级公式:+tex+ 按字面(CSM §1.11)。
197
+ def math_block_call(node)
198
+ "#{@builder}.math_block(#{ruby_literal(node.tex)})"
199
+ end
200
+
201
+ # 表格单元格:正文(PHRASING)由 #inline_block_statement 填充,+align+ 按需带上。
202
+ def cell_arguments(node)
203
+ node.align ? ["align: #{node.align.inspect}"] : []
204
+ end
205
+
206
+ def call(receiver, name, args)
207
+ args.empty? ? "#{receiver}.#{name}" : "#{receiver}.#{name}(#{args.join(", ")})"
208
+ end
209
+
210
+ # Ruby 嵌入块:源码原文(已剥离 N 层 CSM 缩进)按所在层级重新缩进后原样注入
211
+ # (CSM §3.6)。`begin`/`end` 标志与 `.render` 收尾都在原文里,不再改动。
212
+ # 安全模式下不写源码,只留一行注释占位(CSM §3.14)。
213
+ def ruby_embed_statement(node, depth)
214
+ return [line(depth, EMBED_PLACEHOLDER)] unless @unsafe
215
+
216
+ prefix = @indent * depth
217
+ node.source.split("\n", -1).map { |text| text.empty? ? text : prefix + text }
218
+ end
219
+
220
+ # 第一个 `__END__` 原样写入,其后内容交给 Ruby 的 `DATA`(CSM §4)。
221
+ def end_marker_statement(node)
222
+ data = node.attributes[:data]
223
+ return ["__END__"] if data.nil? || data.empty?
224
+
225
+ ["__END__", *data.split("\n", -1)]
226
+ end
227
+
228
+ # -- 行内内容 -------------------------------------------------------------
229
+
230
+ def inline?(node) = node.is_a?(Node::Inline)
231
+
232
+ def inline_nodes(node)
233
+ node.children.select { |child| inline?(child) }
234
+ end
235
+
236
+ # 行内容器(段落、标题、单元格……)与带行内子节点的行内节点:有行内子节点时
237
+ # 用 `do ... end` 逐条构造,没有时退化为单行调用。
238
+ #
239
+ # 唯一的例外是 +inline: false+ 解析出的 AST:树里只有 +attributes[:text]+ 原文,
240
+ # 没有行内节点。此时把原文作为**字符串参数**追加到调用上交给 builder 重新解析
241
+ # ——这是产物里唯一仍需要行内解析器的形态。
242
+ def inline_block_statement(node, depth, name, args = [])
243
+ nodes = inline_nodes(node)
244
+ return inline_block_lines(nodes, depth, call(@builder, name, args)) unless nodes.empty?
245
+
246
+ text = node.attributes[:text].to_s
247
+ args = text.empty? ? args : insert_text_argument(args, ruby_literal(text))
248
+ [line(depth, call(@builder, name, args))]
249
+ end
250
+
251
+ # 文本是**位置参数**,必须排在关键字参数之前(`heading(1, "x", closed: true)`)。
252
+ def insert_text_argument(args, literal)
253
+ keyword = args.index { |arg| keyword_argument?(arg) }
254
+ return args + [literal] if keyword.nil?
255
+
256
+ args.dup.insert(keyword, literal)
257
+ end
258
+
259
+ # 形如 `name: value` 的产物片段(`closed: true` / `align: :left`)。
260
+ def keyword_argument?(argument)
261
+ colon = argument.index(": ")
262
+ return false if colon.nil? || colon.zero?
263
+
264
+ argument[0...colon].each_char.all? do |char|
265
+ char == "_" || (char >= "a" && char <= "z") || (char >= "0" && char <= "9")
266
+ end
267
+ end
268
+
269
+ def inline_block_lines(nodes, depth, head)
270
+ [line(depth, "#{head} do"), *inline_statements(nodes, depth + 1), line(depth, "end")]
271
+ end
272
+
273
+ def inline_statements(nodes, depth)
274
+ nodes.flat_map { |node| inline_statement(node, depth) }
275
+ end
276
+
277
+ # 行内节点 → builder 行内方法(CSM 元素表里的每一种行内构造)。
278
+ #
279
+ # 顺序与 +MDOM::InlineParser+ 的分派一致:叶子单行,带内容的走块;`interpolation`
280
+ # 是嵌入通道,单独处理(见 #interpolation_statement)。
281
+ def inline_statement(node, depth)
282
+ case node.type
283
+ when :text then [line(depth, "#{@builder}.text(#{ruby_literal(node.text)})")]
284
+ when :code_span then [line(depth, "#{@builder}.code_span(#{ruby_literal(node.code)})")]
285
+ when :hard_break then [line(depth, "#{@builder}.hardbreak")]
286
+ when :mention then mention_statement(node, depth)
287
+ when :emphasis then inline_block_statement(node, depth, "em")
288
+ when :strong then inline_block_statement(node, depth, "strong")
289
+ when :del then inline_block_statement(node, depth, "del")
290
+ when :ins then inline_block_statement(node, depth, "ins")
291
+ when :link then inline_block_statement(node, depth, "link", link_arguments(node))
292
+ when :image then inline_block_statement(node, depth, "image", image_arguments(node))
293
+ when :inline_quote then inline_block_statement(node, depth, "inline_quote", [ruby_literal(node.uri)])
294
+ when :wiki_link then inline_block_statement(node, depth, "wiki_link", wiki_link_arguments(node))
295
+ when :meter then inline_block_statement(node, depth, "meter", bar_arguments(node))
296
+ when :progress then inline_block_statement(node, depth, "progress", bar_arguments(node))
297
+ when :math then inline_block_statement(node, depth, "math", [ruby_literal(node.tex)])
298
+ when :interpolation then interpolation_statement(node, depth)
299
+ else raise Error, "cannot translate inline #{node.type.inspect} to Ruby"
300
+ end
301
+ end
302
+
303
+ # `@user` / `@project:x` → `builder.mention("user", kind: "project")`。
304
+ def mention_statement(node, depth)
305
+ args = [ruby_literal(node.name)]
306
+ kind = node.attributes[:kind]
307
+ args << "kind: #{ruby_literal(kind)}" unless kind.nil?
308
+ [line(depth, call(@builder, "mention", args))]
309
+ end
310
+
311
+ # `[标签](uri "title")` / `<uri>`。
312
+ def link_arguments(node)
313
+ args = [ruby_literal(node.uri)]
314
+ args << "title: #{ruby_literal(node.title)}" unless node.title.nil?
315
+ args
316
+ end
317
+
318
+ # `![alt](uri "title")`:+alt+ 是替代文本原文,显示内容由子节点给出。
319
+ def image_arguments(node)
320
+ args = [ruby_literal(node.uri), "alt: #{ruby_literal(node.alt)}"]
321
+ args << "title: #{ruby_literal(node.title)}" unless node.title.nil?
322
+ args
323
+ end
324
+
325
+ # `[[target|label]]`:+label+ 省略时不传。
326
+ def wiki_link_arguments(node)
327
+ args = [ruby_literal(node.attributes[:target])]
328
+ label = node.attributes[:label]
329
+ args << ruby_literal(label) unless label.nil?
330
+ args
331
+ end
332
+
333
+ # `[=m/n]` / `[->m/n]`:+value+ / +max+ 按字面。
334
+ def bar_arguments(node)
335
+ [ruby_literal(node.value), ruby_literal(node.max)]
336
+ end
337
+
338
+ # 插值(CSM §3.13):不安全时把表达式交给 Ruby 求值(结果按 #render 的分派表
339
+ # 插入当前位置),安全时构造**未求值**的插值节点(CSM §3.14)。
340
+ def interpolation_statement(node, depth)
341
+ return [line(depth, "#{@builder}.interpolation(#{ruby_literal(node.expr)})")] unless @unsafe
342
+
343
+ expression_statements(node.expr, depth)
344
+ end
345
+
346
+ # `builder.interpolation { 表达式 }`;表达式含换行时改用 `do ... end`。
347
+ def expression_statements(expr, depth)
348
+ return [line(depth, "#{@builder}.interpolation { #{expr} }")] unless expr.include?("\n")
349
+
350
+ body = expr.split("\n", -1).map { |text| line(depth + 1, text) }
351
+ [line(depth, "#{@builder}.interpolation do"), *body, line(depth, "end")]
352
+ end
353
+
354
+ # -- 转义 -----------------------------------------------------------------
355
+
356
+ # 值 → Ruby 字面量(含引号)。
357
+ def ruby_literal(value)
358
+ return "nil" if value.nil?
359
+
360
+ "\"#{escape_ruby(value.to_s)}\""
361
+ end
362
+
363
+ # CSM 源码文本(或任意值)→ Ruby 双引号字面量的体。
364
+ def escape_ruby(text)
365
+ text.each_char.map { |char| RUBY_ESCAPES.fetch(char, char) }.join
366
+ end
367
+ end
368
+ end
369
+
370
+ module MDOM
371
+ module Node
372
+ # 便捷入口:+node.to_ruby(**options)+ ≡ +MDOM::Translator.to_ruby(node, **options)+。
373
+ class BaseNode
374
+ def to_ruby(**options)
375
+ Translator.to_ruby(self, **options)
376
+ end
377
+ end
378
+ end
379
+ end
data/lib/mdom/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MDOM
4
- VERSION = "0.2.0"
4
+ VERSION = "0.4.0"
5
5
  end
data/lib/mdom.rb CHANGED
@@ -1,16 +1,67 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "mdom/error"
3
4
  require_relative "mdom/version"
4
- require_relative "mdom/node"
5
- require_relative "mdom/nodes"
5
+ require_relative "mdom/hooks"
6
6
  require_relative "mdom/parser"
7
- require_relative "mdom/inline_parser"
8
- require_relative "mdom/serializer"
7
+ require_relative "mdom/translator"
9
8
  require_relative "mdom/builder"
9
+ require_relative "mdom/html_serializer"
10
+ require_relative "mdom/markdown_serializer"
10
11
 
12
+ # CSM(Complex Simple Markup)的 Ruby 工具链。
13
+ #
14
+ # `require "mdom"` 是**总入口**,会把两侧都加载进来(解析侧需要 `prism`)。只想在
15
+ # mruby/WASM 上构造文档时,直接 `require "mdom/builder"` 即可——那一侧零 gem 依赖。
16
+ #
17
+ # 六个转换关系见 `docs/CSM.md` §6;这里接的是其中已经实现的四段:
18
+ # (1) 解析、(2) 翻译、(3)(5) `to_markdown`、(6) HTML 序列化。(4) 执行不在 mdom
19
+ # 范围内——它就是宿主的一行 `eval`。
11
20
  module MDOM
12
- # Serialize a node (or subtree) back to a Markdown string.
13
- def self.serialize(node)
14
- Serializer.serialize(node)
21
+ # 一次 #compile 的结果。
22
+ #
23
+ # 三个阶段的产物都在这里;钩子改写过的版本就是这里看到的版本。
24
+ Compiled = Struct.new(:document, :ruby, :html, :meta, keyword_init: true)
25
+
26
+ # (1):CSM 源码 → AST1。等价于 +MDOM::Parser.parse+。
27
+ def self.parse(source, **options)
28
+ Parser.parse(source, **options)
29
+ end
30
+
31
+ # (1)(2)(6) 全链路,并在五个点上触发钩子(CSM §5.3)。
32
+ #
33
+ # 顺序:
34
+ #
35
+ # 1. `Parser.parse` → 触发 `:after_parse(doc)`
36
+ # 2. 触发 **`:before_compile(doc)`** —— 此时树仍可改写,程序化生成目录就放这里
37
+ # 3. `Translator.to_ruby` → 触发 `:after_compile(doc, ruby)`
38
+ # 4. 触发 `:before_serialize(doc)`
39
+ # 5. `HtmlSerializer.serialize` → 触发 `:after_serialize(html, meta)`
40
+ #
41
+ # 参数:
42
+ # * +hooks:+ —— 本次调用专用的临时钩子,形如 +{ before_compile: [proc] }+;
43
+ # 与类级注册表里的钩子一起执行,顺序是「全局 → 临时」;
44
+ # * +parser:+ / +translator:+ / +serializer:+ —— 分别透传给三个阶段。
45
+ #
46
+ # 返回值:+MDOM::Compiled+(含 +document+ / +ruby+ / +html+ / +meta+)。
47
+ def self.compile(source, hooks: {}, parser: {}, translator: {}, serializer: {})
48
+ document = Parser.parse(source, **parser)
49
+ run_hooks(:after_parse, hooks, document)
50
+
51
+ run_hooks(:before_compile, hooks, document)
52
+ ruby = Translator.to_ruby(document, **translator)
53
+ run_hooks(:after_compile, hooks, document, ruby)
54
+
55
+ run_hooks(:before_serialize, hooks, document)
56
+ html = HtmlSerializer.serialize(document, **serializer)
57
+ run_hooks(:after_serialize, hooks, html, document.meta)
58
+
59
+ Compiled.new(document: document, ruby: ruby, html: html, meta: document.meta)
60
+ end
61
+
62
+ # 触发一个钩子点:先全局注册表,再本次调用传入的临时钩子。
63
+ def self.run_hooks(point, hooks, *args)
64
+ Hooks.run(point, *args, extra: hooks.fetch(point, []))
15
65
  end
66
+ private_class_method :run_hooks
16
67
  end
metadata CHANGED
@@ -1,27 +1,60 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mdom
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - mdom
8
8
  bindir: bin
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
- dependencies: []
12
- description: MDOM parses Markdown into a node tree (block + inline), supports traversal,
13
- querying and mutation, and serializes back to a Markdown string.
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: prism
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0.19'
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: '2.0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: '0.19'
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: '2.0'
32
+ description: MDOM is the Ruby toolchain for CSM (Complex Simple Markup). It parses
33
+ CSM documents into an AST, translates them to safe-by-default Ruby source, re-serializes
34
+ them to CSM, and renders HTML. CSM treats Markdown as syntactic sugar for Ruby,
35
+ so a `.rb.md` document is both readable Markdown and an executable Ruby program.
14
36
  executables: []
15
37
  extensions: []
16
38
  extra_rdoc_files: []
17
39
  files:
40
+ - CHANGELOG.md
41
+ - LICENSE.txt
42
+ - README.md
43
+ - USAGE.md
18
44
  - lib/mdom.rb
45
+ - lib/mdom/block_detector.rb
19
46
  - lib/mdom/builder.rb
47
+ - lib/mdom/error.rb
48
+ - lib/mdom/hooks.rb
49
+ - lib/mdom/html_serializer.rb
20
50
  - lib/mdom/inline_parser.rb
51
+ - lib/mdom/markdown_serializer.rb
21
52
  - lib/mdom/node.rb
22
- - lib/mdom/nodes.rb
53
+ - lib/mdom/node/inline_nodes.rb
54
+ - lib/mdom/node/nodes.rb
23
55
  - lib/mdom/parser.rb
24
- - lib/mdom/serializer.rb
56
+ - lib/mdom/tokenizer.rb
57
+ - lib/mdom/translator.rb
25
58
  - lib/mdom/version.rb
26
59
  licenses:
27
60
  - MIT
@@ -43,5 +76,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
43
76
  requirements: []
44
77
  rubygems_version: 3.6.7
45
78
  specification_version: 4
46
- summary: 'A Markdown DOM: a self-contained AST for Markdown.'
79
+ summary: CSM (Complex Simple Markup) toolchain for Ruby.
47
80
  test_files: []