十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Quarkdown 引用块解析机制详解:从 blockquote.md 测试语料到类型识别与归属提取的实现

Quarkdown 引用块解析机制详解:从 blockquote.md 测试语料到类型识别与归属提取的实现 Quarkdown 引用块解析机制详解从 blockquote.md 测试语料到类型识别与归属提取的实现【免费下载链接】quarkdown Markdown with superpowers: from ideas to papers, presentations, websites, books, and knowledge bases.项目地址: https://gitcode.com/GitHub_Trending/qu/quarkdown本文以 Quarkdown 核心解析器的引用块测试语料quarkdown-core/src/test/resources/parsing/blockquote.md为主线结合 BlockParserTest 中的断言、BlockTokenParser 的解析实现与 BlockQuote AST 节点系统讲解 Quarkdown 如何把前缀文本切分为 Token、递归解析嵌套结构、识别Note:/Tip:等类型前缀以及从单条目列表中提取引文归属attribution。读完后你将掌握 Quarkdown 引用块从词法分析到语法分析再到 AST 构建的完整链路并能在编写或调试文档引擎时复用同一套解析设计。一、测试语料blockquote.md 覆盖了哪些引用块形态blockquote.md是 BlockParserTest 中blockQuote()测试约 L315-L417的输入文件。它不是一篇可读文档而是一份精心组织的解析用例语料共 72 行、20 个样例按主题分组覆盖了引用块的全部解析分支语料位置样例考察的解析能力测试断言摘要L1 Text最基本的单行引用纯文本为TextL3 Text行首 2 空格缩进容忍0~3 空格合法同样解析为TextL5-L6 Line 1 Line 2多行引用合并为同一段落Line 1\nLine 2L8-L10用空行分隔两段引用块内部的段落切分子节点依次为Paragraph、Newline、ParagraphL12-L13 Text Inner quote嵌套引用第一个子节点为文本第二个是内层BlockQuoteInner quoteL15-L16 Text后跟无的惰性行Lazy continuation惰性行并入引用文本为Text\nwith lazy lineL18-L21嵌套引用中的惰性行与行混排递归解析中的惰性续行内层引用文本为Inner text\nwith lazy\nlinesL23-L29引用中插入# Heading与围栏代码边界中断标题与代码块不属于引用前后各自解析为 3 个独立引用均为TextL31-L32 1. A 2. B引用块内嵌有序列表首个子节点为含 2 个条目的OrderedListL34 / L36-L37 Note: A note.与 [!NOTE]A note.类型化引用两种写法等价文本剥离前缀后为A note.类型为NOTEL39-L46 Tip: ...与 [!TIP] 列表类型化引用携带列表内容类型TIP文本This is a tip!第二个子节点为UnorderedListL48-L53 Warning: ...与 [!WARNING]警告类型类型WARNING文本you should be\nmore careful.L55 / L57-L58 Something: ...与 [!SOMETHING]非类型前缀的负例类型断言为null原文完整保留L60-L61莎翁名句 单条目列表- William Shakespeare, Hamlet归属提取attribution归属为Text(William Shakespeare, Hamlet)列表不再属于正文L63-L65 Shopping list 两条列表多条目列表不触发归属归属为null列表保留在正文中L67-L69双层嵌套引用内外各带单条目列表递归归属提取内层引用归属Wayne Gretzky外层归属Emphasis(Michael Scott)L71-L72 Tip: Try Quarkdown. - iamgio类型与归属共存类型TIP文本Try Quarkdown.归属iamgio这份语料与测试断言一一对应构成了 Quarkdown 引用块解析行为的“契约”任何词法或语法改动都必须保证这 20 个用例的 AST 输出不变。二、词法层引用块 Token 是如何切出来的引用块的识别发生在块级词法器block lexer中。核心规则定义在 BaseMarkdownBlockTokenRegexPatterns.ktval blockQuote by lazy { TokenRegexPattern( name BlockQuote, wrap ::BlockQuoteToken, regex RegexBuilder(^( {0,3} ?(paragraph|[^\n]*)(?:\n|$))) .withReference(paragraph, paragraph.regex) .build(), ) }从这条正则可以读出三条词法规则^ {0,3}引用标记前最多允许 3 个空格缩进。这解释了语料第 3 行 Text为什么也能成块——与 CommonMark 的缩进限制一致而第 5 组样例4 空格则不会命中此规则会落入块级代码等其他分支。 ?之后最多跟一个空格空格会被一并吞掉。(...)的重复结构引用块由一个或多个连续的“引用行”构成其中每一行要么是整段段落引用paragraph子模式用于跨行段落识别要么是一般的非空行。整块命中后包装为 BlockQuoteToken其data.text保留了带前缀的原始多行文本——即词法器只负责“圈出引用块的范围”真正的剥离与内容解析留给语法分析层完成。三、语法层BlockTokenParser 的三步处理拿到BlockQuoteToken后BlockTokenParser 的visit(token: BlockQuoteToken)方法执行三步处理。3.1 剥离前缀var text token.data.text .replace(^ *[ \\t]?.toRegex(RegexOption.MULTILINE), ) .trim()多行正则^ *[ \t]?会删除每一行开头的若干空格加再加至多一个空白字符。注意这里删除的是“一行一个前缀”所以 Inner quote只剥掉最外层的剩下一层 Inner quote——这正是嵌套引用能够被识别的词法基础。3.2 类型前缀识别Tip / Note / Warning / Importantval type: BlockQuote.Type? BlockQuote.Type.entries.find { type - sequenceOf( ${type.name}: , // e.g. Tip:, Note:, Warning: [!${type.name}], // e.g. [!TIP], [!NOTE], [!WARNING] ).any { prefix - val (newText, found) text.removeOptionalPrefix(prefix, ignoreCase true) if (found) text newText.trimStart() found } }类型来自 BlockQuote.Type 枚举共四个值TIP、NOTE、WARNING、IMPORTANT。识别逻辑有两个要点双写法等价Note:前缀与 GitHub 风格[!NOTE]块前缀被同等对待匹配到后前缀从文本中剥离对应语料 L34 与 L36-L37 两组断言得到完全相同的type NOTE、正文A note.。官方文档 docs/quote-types.qd 也明确说明这种兼容性“the GitHub-style syntax[!NOTE],[!TIP],[!WARNING], and[!IMPORTANT]is also supported”。大小写不敏感ignoreCase true但词表封闭Something:不在枚举内因此语料 L55 断言type为null、原文完整保留。[!SOMETHING]同理它会按普通内联内容解析测试断言该段落以ReferenceLink开头不会被当成类型标记。3.3 递归解析正文与归属提取var children context.flavor.lexerFactory .newBlockLexer(source text) .tokenizeAndParse()剥离后的文本被重新送进块级词法器 解析器递归处理。这是 Quarkdown 引用块能容纳段落、列表、甚至再一层BlockQuote的机制来源——嵌套并不走特殊的“二级词法”而是同一套块解析在子文本上的递归调用语料 L12-L13、L18-L21 与 L67-L69 的断言children[1]为内层BlockQuote都直接验证了这一点。递归解析完成后归属attribution提取规则如下val attribution: InlineContent? (children.lastOrNull() as? UnorderedList) ?.children ?.singleOrNull() ?.let { it as? ListItem } ?.children ?.firstOrNull() ?.let { it as? TextNode } ?.text ?.also { children children.dropLast(1) }只有当最后一个子节点是无序列表、且该列表恰好只有一个条目时该条目的内联文本才被提升为引文的attribution同时从正文子节点中移除。对照语料可以精确看出规则边界L60-L61 莎翁例句末列表仅一条- William Shakespeare, Hamlet→ 提取为归属L63-L65 购物清单末列表有两条- Water、- Pasta→singleOrNull()不满足归属为null列表留在正文L71-L72Tip: Try Quarkdown. - iamgio类型识别与归属提取可叠加生效最终type TIP、正文Try Quarkdown.、归属iamgio。最后组装为 BlockQuote 节点class BlockQuote( val type: Type? null, val attribution: InlineContent? null, Diverge val content: ListNode, ) : NestableNode该节点实现NestableNode接口children为content attribution因此引用块本身可以作为列表项、其他容器甚至另一个引用块的子节点出现形成自由嵌套。四、类型化引用的渲染与本地化解析层只产出type字段呈现交给渲染层。BlockQuote.Type 枚举实现了RenderRepresentable接口说明类型值会作为可渲染符号参与布局渲染例如显示为带样式的标题词。据 docs/quote-types.qd 描述前缀Tip:/Note:/Warning:/Important:会被剥离并按当前主题重新样式化若文档通过.doclang设置了受支持的语言则显示本地化前缀样式随当前布局主题变化。五、边界与“非引用”行为为什么样例 8 会产生三个独立引用语料 L23-L29 是最容易被忽视的用例 Text # Heading TextCode Text测试对这段连续调用三次nodes.next()断言得到三个彼此独立的Text引用。原因在词法正则块级Heading与围栏代码是独立的块 Token它们出现在引用块中间时词法器按顺序匹配会先把第一个 Text切出一个BlockQuoteToken再切出标题与代码剩下的 Text又各自成块。换句话说Quarkdown 的引用块不跨块级中断续行——这与惰性行无前缀的普通文本可并入前一个引用形成明确对照惰性续行属于“引用块的行级延续”而标题/代码属于“块级边界”。理解这一区分是预判复杂文档解析结果的关键。六、如何验证运行引用块解析测试语料、断言与实现三者可通过测试直接闭环验证。在仓库根目录执行./gradlew :quarkdown-core:test --tests com.quarkdown.core.BlockParserTest.blockQuote该测试通过 BlockParserTest 中的blocksIterator辅助方法搭建最小管线以QuarkdownFlavor创建MutableContext、调用attachMockPipeline()再分别由flavor.lexerFactory.newBlockLexer(source)与flavor.parserFactory.newParser(context)产出词法器与解析器逐个输出顶层节点并断言类型与文本。由于blockquote.md的所有样例都以引用块为顶层节点标题与代码只是中断物测试以assertType false迭代并逐一校验内部结构。七、相关文件索引测试语料blockquote.md断言测试BlockParserTest.ktblockQuote()约 L315-L417词法模式BaseMarkdownBlockTokenRegexPatterns.ktblockQuote模式Token 定义BlockTokens.ktBlockQuoteToken解析实现BlockTokenParser.kt前缀剥离、类型识别、归属提取AST 节点BlockQuote.kt用户文档quote-types.qd类型化引用与本地化前缀的对外说明综合来看Quarkdown 的引用块解析是一个“词法圈范围、语法剥前缀、类型查词表、正文再递归”的四层设计词法正则保证 0~3 空格缩进与连续行的边界解析器把前缀、类型前缀、归属列表三类信息逐层剥离最后通过递归复用块解析器获得任意深度的嵌套能力。blockquote.md语料中 20 个用例正好逐一钉住了这四层行为及其负例非类型前缀、多条目列表、块级中断为引用块语义提供了可回归验证的完整基准。【免费下载链接】quarkdown Markdown with superpowers: from ideas to papers, presentations, websites, books, and knowledge bases.项目地址: https://gitcode.com/GitHub_Trending/qu/quarkdown创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表