Harper 语料库解析:this / that / these / those 消歧测试文档与快照测试机制
【免费下载链接】harperOffline, privacy-first grammar checker. Fast, open-source, Rust-powered项目地址: https://gitcode.com/GitHub_Trending/har/harper
本文以 this and that.md 这份核心测试语料为主体,完整讲解它如何通过分组例句覆盖指示代词(demonstrative)的语法歧义,以及 Harper 如何用快照(snapshot)测试体系消费这份语料——读者读完后既能理解英语指示词消歧的测试设计思路,也能掌握在 harper-core 中运行与扩展该类快照测试的具体方法。
一、这是一份什么文件:测试语料而非项目文档
this and that.md位于harper-core/tests/text/目录下,它不是给人阅读的项目文档,而是一份刻意设计的歧义语料。文件开头的自述说明了一切:
"This" and "that" are common and fulfill multiple purposes in everyday English. As such, disambiguating them is necessary. This document contains various sentences that use "this", "that", "these", and "those" in different contexts with a lot of edge cases.
它的用途是喂给 harper-core 的两条快照测试流水线:词性标注快照与 lint 快照。文件中的每个句子都被精心挑选,用以压测分词器(tokenizer)与词典元数据在 "this/that/these/those" 上的词性不确定性建模能力——这是 Harper 这类基于规则表达式(Expr)做语法检查的引擎能否正确工作的前提。
二、语料全览:句子分组与消歧点
以下例句完整保留自语料文件 this and that.md,按其消歧目标可分为四组。
2.1 指示限定词 vs 独立代词(第一组)
This triangle is nice. This is nice. That triangle is nice. That is nice. These triangles are nice. These are nice. Those triangles are nice. Those are nice.这组对照了四种指示词的两种句法角色:作限定词(后接名词短语,如 "This triangle")与独立作主语(如 "This is nice")。对词性标注器而言,前者应体现为「代词/限定词」二义性,后者则要求名词短语成分信息缺失时仍能正确切分。
2.2 同形异词 "massage/massages"(第二组)
This massage is nice. That massage is nice. These massages are nice. Those massages are nice. This massages well. That massages well. These massage well. Those massage well.这组是刻意加入的强歧义陷阱:massages既可是名词复数("These massages are nice")也可是动词第三人称单数("This massages well");而massage既可是单数名词也可是动词原形("These massage well")。在基于词法元数据的 lint 规则中,"massage 到底是名词还是动词"直接决定后续规则是否命中,因此这类同形异词是必须覆盖的边界情况。
2.3 "that" 的双重身份:指示词 vs 关系代词(第三组)
That could be a solution. Find all candidates that could be a solution. This is all that I have. This is all that solutions can do. That solution can do."that" 既可作指示词("That could be a solution" 中的主语),也可作关系代词("all candidatesthatcould be a solution")。两句中 "that could be a solution" 的局部结构完全相同,差别只在上下文——这是关系代词消歧的典型难点。Harper 的多个 lint 规则(如 it_looks_like_that.rs)正是围绕「that 是冗余的关系代词还是合法的指示词」做启发式判断,本组例句即为该类规则提供了正反例基础。
2.4 祈使与口语边缘用例(第四组)
We can do this! I can do this and that. We unite to stand united in unity.感叹号结尾的 "We can do this!" 考察非句末句号对句子边界的影响;"this and that" 考察两个指示词经并列连词连接的短语切分;末句 "We unite to stand united in unity" 则在同一句中密集出现 unite / united / unity 三个同根词(动词、形容词/过去分词、名词),用于验证词根相同的词之间不会互相污染元数据。
三、文档在测试管线中如何被消费
tests/text/下的语料由一套自研快照框架驱动,核心在 snapshot.rs:
- snapshot_all_text_files() 遍历
tests/text/下所有.md/.txt文件,用 rayon 并行处理; - tag_file() 对每个文件执行「生成快照 → 与既有快照比对」:若快照不存在则写入并报
No snapshot!,若不一致则重写并报Snapshot mismatches!,最终使测试失败; - 方言支持:try_get_dialect_override() 会从文件名中解析方言缩写(如 Spell.US.md 中的
.US.)作为方言覆盖,否则回退自动猜测。
这意味着 CI 中一旦词性标注器或任意 lint 规则的行为发生变化而快照未同步,构建就会失败——快照文件本身就是回归基线。
3.1 词性标注快照(pos_tags.rs)
linters.rs 同目录的 pos_tags.rs 中,test_pos_tagger 用FstDictionary::curated()词典构建Document,将每个 token 按其TokenKind与词典元数据渲染为紧凑标签,逐行对齐写在原文下方,输出到tests/text/tagged/。该文档对应的真实快照是 tagged/this and that.md,例如第一组例句的标注结果:
> This triangle is nice . # I/Ddem NSg VL3 NPr/J . > That is nice . # I/C/Ddem+ VL3 NPr/J . > These triangles are nice . # I/Ddem NPl VLB NPr/J . > That massage is nice . # I/C/Ddem NSg/VB+ VL3 NPr/J . > This massages well . # I/Ddem+ NPl/V3+ NSg/VB/J/R .标签约定在 pos_tags.rs 的文件头注释 中有完整定义,与本文相关的核心符号如下表(灵感来自 Penn Treebank 词性集):
| 标签 | 含义 | 语料中的实例 |
|---|---|---|
I | 代词(pronoun),Sg/Pl标记单复数 | This→I/Ddem |
D | 限定词(determiner),dem为指示性,q为数量词 | that→I/C/Ddem |
C | 连词(conjunction) | that的第三重身份 |
NSg/NPl | 可数名词单数 / 复数 | triangle、triangles |
VB | 动词原形(lemma) | massage→NSg/VB+ |
V3 | 动词第三人称单数现在时 | massages→NPl/V3+ |
VL3 | 系动词第三人称单数 | is→VL3 |
VXB | 助动词原形 | could→VXB |
+ | 名词短语成员 | I/C/Ddem+中的+ |
/ | 一词多标签,表示标注器承认的不确定性 | NPl/V3+ |
从快照可以清楚看到语料设计意图与实现结果的吻合:
That稳定标注为I/C/Ddem——代词 / 连词 / 指示限定词三重身份并存,正是 2.3 节要求的歧义建模;This在 "This is nice."(独立代词)中标为I/Ddem+,而在 "This triangle is nice."(作限定词)中标为I/Ddem,+(名词短语成员)标记随句法角色变化;massages在名词句中为NPl/V3+、在动词句中同样保留NPl/V3+,massage在名词句中为NSg/VB+——词法层面保持双义,交由上下文规则消解。
3.2 Lint 快照(linters.rs)
test_most_lints 对同一批语料运行全部精选 lint 规则(LintGroup::new_curated),按 span 排序后连同 lint 类型、优先级、消息与修改建议,以带行号上下文和下划线高亮的格式写入tests/text/linters/下的.snap.yml文件。
对该文档而言,其快照 this and that.snap.yml为空文件——这表明整份语料在所有精选规则下零告警。这是符合设计预期的:语料中每个句子都是合法英语,该文件的价值在于验证"歧义结构不产生误报",而 lint 快照框架保证任何规则改动若在这份语料上引入新的告警(即误报),CI 会立即失败。
顺带说明,仓库还存在另一条测试管线 run_tests.rs:它针对tests/test_sources/目录,用create_test!宏断言"某文档恰好产生 N 个 lint",与tests/text/的快照式全量快照互为补充。
四、语料对应的消歧机制:源码证据
4.1 词典元数据层的指示词标记
歧义建模的根基在词典元数据。dict_word_metadata.rs 中定义了限定词专用结构:
pub struct DeterminerData { pub is_demonstrative: Option<bool>, pub is_possessive: Option<bool>, pub is_quantifier: Option<bool>, }三个字段均为Option<bool>,配合or()合并逻辑,允许同形词的多条词典条目各自声明部分属性后取并集。单元测试 this_is_demonstrative_determiner 直接断言了本文主角:
assert!(md("this").is_demonstrative_determiner());4.2 TokenKind 上的快捷谓词
harper-core/src/token_kind.rs 基于上述元数据自动生成了is_demonstrative_determiner()等谓词,供所有基于SequenceExpr的规则直接调用。例如 possessive_noun.rs 就用SequenceExpr::unless(|tok, _| tok.kind.is_demonstrative_determiner())把指示词从"疑似所有格名词"中排除——这正是语料第一组("This triangle...")要守护的行为:指示词限定名词时不应触发所有格误报。
4.3 真实用例:ItLooksLikeThat 规则
it_looks_like_that.rs 是 "that" 指示词/关系代词消歧的完整实例。该规则匹配it looks like that ...,并在 "that" 之后取一个词做启发式判断,决定 "that" 是否为冗余的关系代词:
let is_subj = kind.is_subject_pronoun(); let is_ing = kind.is_verb_progressive_form(); let is_definitely_rel_pron = is_subj || is_ing; let is_v3psgpres = kind.is_verb_third_person_singular_present_form(); let is_vmodal_or_aux = kind.is_auxiliary_verb(); let is_vpret = kind.is_verb_simple_past_form(); let is_noun = kind.is_noun(); let is_oov = kind.is_oov(); let maybe_demonstrative_or_determiner = is_v3psgpres || is_vmodal_or_aux || is_vpret || is_noun || is_oov; is_definitely_rel_pron || !maybe_demonstrative_or_determiner逻辑是:若 "that" 后紧跟主格代词或进行时动词,几乎必然是关系代词(报冗余);若其后是第三人称单数动词、情态/助动词、过去式、名词或词典外词,则可能是合法指示词,倾向放行。语料第三组中 "That could be a solution." 的 "could"(快照中标为VXB助动词)正是落入is_vmodal_or_aux白名单、从而被识别为合法指示词用法的典型——这解释了为什么该句不产生告警。类似地,nominal_wants.rs 的注释也明确写出:"That" can act as two kinds of pronoun: demonstrative and relative,规则须先区分再判定。
五、如何复现与验证
在仓库根目录下运行 harper-core 的集成测试即可复现全部快照行为:
# 词性标注快照(输出到 tests/text/tagged/) cargo test --test pos_tags # lint 快照(输出到 tests/text/linters/) cargo test --test linters适用前提:快照与当前词法/规则行为一致时测试通过;若你在开发中改动了标注器或 lint 规则导致输出变化,测试会先自动重写快照文件再失败,此时需人工审查 diff 确认变化符合预期。若要新增一份歧义语料,流程与 pos_tags.rs 文件头 的说明一致:把文档放入tests/text/,运行上述测试,快照会自动生成——文件名中带方言缩写(如X.US.md)可强制指定方言。
六、这份语料体现的测试设计要点
- 对照成对设计:每组例句都以"限定词 + 名词"与"独立代词"、"名词用法"与"动词用法"成对出现,使快照 diff 能直接暴露单一角色的回归;
- 歧义不消除、只建模:从快照可见 Harper 不在词法层强行裁决同形词(
massages始终保留NPl/V3双标签),而把裁决推迟到带上下文的规则层,这与 4.3 节规则的启发式结构一致; - 空快照即断言:this and that.snap.yml 的"空"本身就是可执行的回归断言——任何规则若把合法指示词用法误报为错误,CI 立刻拦截;
- 快照即文档:tagged/this and that.md 让标注器对每个歧义句的实际判断以人类可读方式固化在仓库中,既是测试基线,也是行为文档。
综上,this and that.md虽只是一页英语句子,却是 Harper 语法检查引擎消歧能力的最小可验证载体:上游有DeterminerData元数据与TokenKind谓词支撑,下游有双快照管线持续回归,构成从词典、标注到规则、测试的完整闭环。
【免费下载链接】harperOffline, privacy-first grammar checker. Fast, open-source, Rust-powered项目地址: https://gitcode.com/GitHub_Trending/har/harper
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考