asciidoc-pubkit 0.1.1 → 0.1.2
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +99 -4
- data/data/review-rules.ja.yml +123 -0
- data/lib/asciidoc_pubkit/cli.rb +1 -0
- data/lib/asciidoc_pubkit/rule_set.rb +52 -0
- data/lib/asciidoc_pubkit/rules.rb +26 -34
- data/lib/asciidoc_pubkit/settings.rb +5 -2
- data/lib/asciidoc_pubkit.rb +2 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: de07af1758fdba08a7099b9e66f63b14084d0dad6cba8437498f0ddd0577b425
|
|
4
|
+
data.tar.gz: 8474d883c10c23f1221ff85cbd023d781c84f93dd55436a542c0d304c0a8f424
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6983524a7967131b469360bebd417125d7eea14cdf8054d14c928940528d685ee50fd0c402ca354073a55e643217ed931ddda3ca3e402d252d02e4030b7b3a9e
|
|
7
|
+
data.tar.gz: 8e8111fea8d32dfb1449c48cfc8d8f2df22e58dec14a44a55c6dcb6596a6bd3e87aa86fbd596cfc332a6ca49c9d1452f5dd34b23126137d9359a081585e1ae56
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.2 — Released
|
|
4
|
+
|
|
5
|
+
- Package default Japanese review rules as a validated UTF-8 YAML file.
|
|
6
|
+
- Support custom rule files through `review scan --rules` and `review.rules`.
|
|
7
|
+
- Save resolved rules in sessions for reproducible prompts and verification.
|
|
8
|
+
- Add concept, distinction, and negative meaning review candidates.
|
|
9
|
+
- Add a Make target for running a local checkout with arbitrary CLI arguments.
|
|
10
|
+
- Require a new review session when upgrading from earlier tool versions.
|
|
11
|
+
|
|
3
12
|
## 0.1.1 — Released
|
|
4
13
|
|
|
5
14
|
- Add contextual phrases, compound nouns, and potential and causative verb forms.
|
data/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|

|
|
2
2
|

|
|
3
3
|
[](https://rubygems.org/gems/asciidoc-pubkit)
|
|
4
|
+
[](https://github.com/cybergarage/asciidoc-pubkit/actions/workflows/test.yml)
|
|
4
5
|
|
|
5
6
|
# asciidoc-pubkit
|
|
6
7
|
|
|
@@ -59,7 +60,7 @@ Add the gem to your project's `Gemfile` to manage its version with Bundler:
|
|
|
59
60
|
|
|
60
61
|
```ruby
|
|
61
62
|
source 'https://rubygems.org'
|
|
62
|
-
gem 'asciidoc-pubkit', '~> 0.1.
|
|
63
|
+
gem 'asciidoc-pubkit', '~> 0.1.2'
|
|
63
64
|
```
|
|
64
65
|
|
|
65
66
|
Then install dependencies and run the CLI through Bundler:
|
|
@@ -81,7 +82,7 @@ git clone https://github.com/cybergarage/asciidoc-pubkit.git
|
|
|
81
82
|
cd asciidoc-pubkit
|
|
82
83
|
bundle install
|
|
83
84
|
gem build asciidoc-pubkit.gemspec
|
|
84
|
-
gem install ./asciidoc-pubkit-0.1.
|
|
85
|
+
gem install ./asciidoc-pubkit-0.1.2.gem
|
|
85
86
|
asciidoc-pubkit --version
|
|
86
87
|
```
|
|
87
88
|
|
|
@@ -92,6 +93,34 @@ For a small trial, use `examples/book.adoc` as the scan input. Its Japanese
|
|
|
92
93
|
paragraphs deliberately contain review candidates; its code block must remain
|
|
93
94
|
unchanged.
|
|
94
95
|
|
|
96
|
+
## Run from a local checkout
|
|
97
|
+
|
|
98
|
+
Use the `run` Make target to execute the checkout without installing the
|
|
99
|
+
asciidoc-pubkit gem. Ruby dependencies and, for the default tokenizer, MeCab and
|
|
100
|
+
UTF-8 IPADIC must already be installed.
|
|
101
|
+
|
|
102
|
+
```sh
|
|
103
|
+
make run ARGS="--version"
|
|
104
|
+
make run ARGS="review scan examples/book.adoc"
|
|
105
|
+
make run ARGS='review scan "manuscripts/my book.adoc"'
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
With no `ARGS`, `make run` displays CLI help. Set `RUBY` to choose another Ruby
|
|
109
|
+
executable. `ARGS` is shell command-line text; quote paths containing spaces and
|
|
110
|
+
use only trusted arguments.
|
|
111
|
+
|
|
112
|
+
To run from a manuscript directory, select the checkout's Makefile with `-f`.
|
|
113
|
+
Relative manuscript paths and output paths remain relative to your current
|
|
114
|
+
working directory:
|
|
115
|
+
|
|
116
|
+
```sh
|
|
117
|
+
make -f "$HOME/Src/asciidoc-pubkit/Makefile" run ARGS="review scan book.adoc"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Use `ARGS` instead of `make review scan book.adoc`: Make interprets positional
|
|
121
|
+
words as build targets, not CLI arguments. Avoid `make -C` when manuscript paths
|
|
122
|
+
should remain relative to the current directory, because it changes directories.
|
|
123
|
+
|
|
95
124
|
## Install the morphological analyzer (0.1.1 and later)
|
|
96
125
|
|
|
97
126
|
On macOS with Homebrew:
|
|
@@ -298,12 +327,12 @@ reports analyzer changes instead of treating results from different dictionaries
|
|
|
298
327
|
as directly comparable. Prompt generation uses saved evidence and does not need
|
|
299
328
|
MeCab. Changed rules or dictionary settings require a new scan.
|
|
300
329
|
|
|
301
|
-
Sessions from
|
|
330
|
+
Sessions from earlier tool versions are not compatible with 0.1.2. Keep the original baseline for
|
|
302
331
|
an ongoing review and finish it with the original version, or start a new review
|
|
303
332
|
pass in a different directory:
|
|
304
333
|
|
|
305
334
|
```sh
|
|
306
|
-
asciidoc-pubkit review scan book.adoc --output .pubkit/review-0.1.
|
|
335
|
+
asciidoc-pubkit review scan book.adoc --output .pubkit/review-0.1.2
|
|
307
336
|
```
|
|
308
337
|
|
|
309
338
|
Severity describes review priority, not proof of an error. There is no AI-authorship
|
|
@@ -341,3 +370,69 @@ GitHub Actions is configured for Ruby 3.2, 3.3, 3.4, and 4.0 on Linux.
|
|
|
341
370
|
Copyright 2026 CyberGarage.
|
|
342
371
|
|
|
343
372
|
Licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE).
|
|
373
|
+
|
|
374
|
+
## Customize review rules
|
|
375
|
+
|
|
376
|
+
The UTF-8 YAML file [`data/review-rules.ja.yml`](data/review-rules.ja.yml) is
|
|
377
|
+
included in the gem and loaded by default. Edit that file when running a local
|
|
378
|
+
checkout, or copy it to a project-owned file for custom rules. No Ruby changes
|
|
379
|
+
are required to update candidate terms.
|
|
380
|
+
|
|
381
|
+
```sh
|
|
382
|
+
asciidoc-pubkit review scan book.adoc --rules ./review-rules.yml
|
|
383
|
+
make run ARGS="review scan book.adoc --rules ./review-rules.yml"
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
For an installed gem, copy the default file with:
|
|
387
|
+
|
|
388
|
+
```sh
|
|
389
|
+
ruby -rasciidoc_pubkit -e 'puts File.read(AsciidocPubkit::RuleSet::DEFAULT_PATH)' > review-rules.yml
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
Alternatively, configure a path in `.asciidoc-pubkit.yml`:
|
|
393
|
+
|
|
394
|
+
```yaml
|
|
395
|
+
review:
|
|
396
|
+
rules: review-rules.yml
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
Precedence is `--rules`, then `review.rules`, then the packaged default.
|
|
400
|
+
CLI paths are relative to the current working directory; configuration paths
|
|
401
|
+
are relative to the configuration file. A custom file replaces the entire rule
|
|
402
|
+
set; it is not merged with defaults. Start by copying the standard file.
|
|
403
|
+
|
|
404
|
+
### Rule file format (schema version 1)
|
|
405
|
+
|
|
406
|
+
All top-level fields below are required. Unknown keys and invalid types are
|
|
407
|
+
rejected. YAML aliases and object tags are not supported.
|
|
408
|
+
|
|
409
|
+
| Field | Format and behavior |
|
|
410
|
+
| --- | --- |
|
|
411
|
+
| `schema_version` | Integer `1` |
|
|
412
|
+
| `terms` | Mapping containing all five categories listed below |
|
|
413
|
+
| `verbs` | Mapping from canonical verb forms to nonempty arrays of MeCab/IPADIC dictionary forms; each dictionary form belongs to only one canonical form |
|
|
414
|
+
| `sahen` | Array of nouns matched with a following `する` verb |
|
|
415
|
+
| `negative_only` | Array of canonical predicates restricted to negative forms; use the noun plus `する` for sahen predicates |
|
|
416
|
+
| `compound_nouns` | Array of terms matched across contiguous noun tokens; each must also appear in `abstract-reference.terms` |
|
|
417
|
+
|
|
418
|
+
Each `terms` category must contain `terms` (an array of unique nonempty strings)
|
|
419
|
+
and `question` (a nonempty review instruction string). The required categories
|
|
420
|
+
are `abstract-reference`, `weak-predicate`, `vague-degree`, `contextual-phrase`,
|
|
421
|
+
and `generic-framing`. Empty term arrays disable that category's literal
|
|
422
|
+
candidates. Empty `verbs`, `sahen`, and `compound_nouns` collections disable their
|
|
423
|
+
respective morphological matchers. Keep `negative_only` consistent with the
|
|
424
|
+
configured predicates.
|
|
425
|
+
|
|
426
|
+
In literal mode, all category term lists use exact phrase matching. In MeCab
|
|
427
|
+
mode, abstract nouns and degree adjectives use dictionary forms, while predicates
|
|
428
|
+
use `verbs` and `sahen`; add a predicate's desired literal surface to
|
|
429
|
+
`weak-predicate.terms` as well if literal mode should detect it. Contextual and
|
|
430
|
+
generic framing phrases use literal matching in both modes. Questions apply to
|
|
431
|
+
both detectors. Glossary, style, repeated-ending checks, inline exclusions, and
|
|
432
|
+
morphological suffix handling remain implemented in Ruby.
|
|
433
|
+
|
|
434
|
+
A scan saves the resolved rule contents and source path in `manifest.json`.
|
|
435
|
+
Prompt generation and verification use the saved contents, even if the original
|
|
436
|
+
YAML file is subsequently edited or removed. Start a new session to apply rule
|
|
437
|
+
changes. Older sessions without a rule snapshot fall back to the currently
|
|
438
|
+
installed default file; start a new session for reproducible custom-rule reviews.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
---
|
|
2
|
+
schema_version: 1
|
|
3
|
+
terms:
|
|
4
|
+
abstract-reference:
|
|
5
|
+
terms:
|
|
6
|
+
- コスト
|
|
7
|
+
- 境界
|
|
8
|
+
- 契約
|
|
9
|
+
- 観点
|
|
10
|
+
- 土台
|
|
11
|
+
- 橋渡し
|
|
12
|
+
- 入口
|
|
13
|
+
- 記述
|
|
14
|
+
- 場所
|
|
15
|
+
- 意図
|
|
16
|
+
- 役割
|
|
17
|
+
- 一続き
|
|
18
|
+
- 根拠
|
|
19
|
+
- 部品
|
|
20
|
+
- 開発者
|
|
21
|
+
- 概念
|
|
22
|
+
question: Identify the concrete referent, components, or measurable work. Keep
|
|
23
|
+
established technical meanings.
|
|
24
|
+
weak-predicate:
|
|
25
|
+
terms:
|
|
26
|
+
- 利用します
|
|
27
|
+
- 整理します
|
|
28
|
+
- 扱います
|
|
29
|
+
- 示します
|
|
30
|
+
- 変わります
|
|
31
|
+
- 把握します
|
|
32
|
+
- 分けられます
|
|
33
|
+
- そろえます
|
|
34
|
+
- まとまっています
|
|
35
|
+
- 加えます
|
|
36
|
+
- 探します
|
|
37
|
+
- 到達しません
|
|
38
|
+
- 扱いません
|
|
39
|
+
- そろいます
|
|
40
|
+
- 選べます
|
|
41
|
+
- 成り立たせています
|
|
42
|
+
- 確かめます
|
|
43
|
+
- 書き換える
|
|
44
|
+
- 絞れます
|
|
45
|
+
- 渡します
|
|
46
|
+
- あります
|
|
47
|
+
- 意味しません
|
|
48
|
+
question: Check whether the purpose, operation, or result is clear from the surrounding
|
|
49
|
+
paragraph. Preserve negation and conditions.
|
|
50
|
+
vague-degree:
|
|
51
|
+
terms:
|
|
52
|
+
- 浅い
|
|
53
|
+
- 深い
|
|
54
|
+
question: Identify the concrete depth, level, scope, or comparison. Keep literal
|
|
55
|
+
measurements and established technical meanings.
|
|
56
|
+
contextual-phrase:
|
|
57
|
+
terms:
|
|
58
|
+
- これらを
|
|
59
|
+
- であることだけでは
|
|
60
|
+
- あるものとします
|
|
61
|
+
- わけではありません
|
|
62
|
+
- 別です
|
|
63
|
+
question: Check the referent, assumption, or qualification against the surrounding
|
|
64
|
+
explanation. Preserve conditions and negation.
|
|
65
|
+
generic-framing:
|
|
66
|
+
terms:
|
|
67
|
+
- 重要なのは
|
|
68
|
+
- ポイントは
|
|
69
|
+
- 本章では
|
|
70
|
+
- ここでは
|
|
71
|
+
- まとめると
|
|
72
|
+
question: Check whether this framing adds useful scope or information instead
|
|
73
|
+
of repeating the explanation.
|
|
74
|
+
verbs:
|
|
75
|
+
扱う:
|
|
76
|
+
- 扱う
|
|
77
|
+
示す:
|
|
78
|
+
- 示す
|
|
79
|
+
変わる:
|
|
80
|
+
- 変わる
|
|
81
|
+
分ける:
|
|
82
|
+
- 分ける
|
|
83
|
+
そろえる:
|
|
84
|
+
- そろえる
|
|
85
|
+
- 揃える
|
|
86
|
+
まとまる:
|
|
87
|
+
- まとまる
|
|
88
|
+
- 纏まる
|
|
89
|
+
加える:
|
|
90
|
+
- 加える
|
|
91
|
+
探す:
|
|
92
|
+
- 探す
|
|
93
|
+
そろう:
|
|
94
|
+
- そろう
|
|
95
|
+
- 揃う
|
|
96
|
+
選ぶ:
|
|
97
|
+
- 選ぶ
|
|
98
|
+
- 選べる
|
|
99
|
+
成り立つ:
|
|
100
|
+
- 成り立つ
|
|
101
|
+
確かめる:
|
|
102
|
+
- 確かめる
|
|
103
|
+
書き換える:
|
|
104
|
+
- 書き換える
|
|
105
|
+
絞る:
|
|
106
|
+
- 絞る
|
|
107
|
+
- 絞れる
|
|
108
|
+
渡す:
|
|
109
|
+
- 渡す
|
|
110
|
+
ある:
|
|
111
|
+
- ある
|
|
112
|
+
sahen:
|
|
113
|
+
- 利用
|
|
114
|
+
- 整理
|
|
115
|
+
- 把握
|
|
116
|
+
- 到達
|
|
117
|
+
- 意味
|
|
118
|
+
negative_only:
|
|
119
|
+
- 到達する
|
|
120
|
+
- 意味する
|
|
121
|
+
compound_nouns:
|
|
122
|
+
- 一続き
|
|
123
|
+
- 開発者
|
data/lib/asciidoc_pubkit/cli.rb
CHANGED
|
@@ -36,6 +36,7 @@ module AsciidocPubkit
|
|
|
36
36
|
if command == 'scan'
|
|
37
37
|
opts.on('--only FILE', 'Review one included file in the book context') { |v| options[:only] = v }
|
|
38
38
|
opts.on('--config FILE', 'Use an explicit YAML configuration') { |v| options[:config] = v }
|
|
39
|
+
opts.on('--rules FILE', 'Replace default review rules with a YAML rule set') { |v| options[:rules] = v }
|
|
39
40
|
opts.on('--base-dir DIR', 'Set the Asciidoctor base directory') { |v| options[:base_dir] = v }
|
|
40
41
|
opts.on('--lang LANG', 'Prose language (ja only)') { |v| options[:language] = v }
|
|
41
42
|
opts.on('--tokenizer NAME', 'mecab (default) or literal (limited phrase matching)') { |v| options[:tokenizer] = v }
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AsciidocPubkit
|
|
4
|
+
class RuleSet
|
|
5
|
+
DEFAULT_PATH = File.expand_path('../../data/review-rules.ja.yml', __dir__)
|
|
6
|
+
CATEGORIES = %w[abstract-reference weak-predicate vague-degree contextual-phrase generic-framing].freeze
|
|
7
|
+
KEYS = %w[schema_version terms verbs sahen negative_only compound_nouns].freeze
|
|
8
|
+
|
|
9
|
+
def self.load(path = DEFAULT_PATH)
|
|
10
|
+
validate(YAML.safe_load(AsciidocPubkit.read_text(path), permitted_classes: [], aliases: false))
|
|
11
|
+
rescue Psych::Exception => e
|
|
12
|
+
raise Error, "Invalid rule YAML in #{path}: #{e.message}"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.mapping(value, keys, label)
|
|
16
|
+
unless value.is_a?(Hash) && (value.keys - keys).empty? && (keys - value.keys).empty?
|
|
17
|
+
raise Error, "#{label} must contain exactly these keys: #{keys.join(', ')}."
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.strings(value, label)
|
|
22
|
+
unless value.is_a?(Array) && value.all? { |s| s.is_a?(String) && !s.strip.empty? } && value.uniq == value
|
|
23
|
+
raise Error, "#{label} must be an array of unique nonempty strings."
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.validate(data)
|
|
28
|
+
mapping(data, KEYS, 'Rule set')
|
|
29
|
+
raise Error, 'Rule schema_version must be integer 1.' unless data['schema_version'].is_a?(Integer) && data['schema_version'] == 1
|
|
30
|
+
mapping(data['terms'], CATEGORIES, 'Rule terms')
|
|
31
|
+
data['terms'].each do |category, entry|
|
|
32
|
+
mapping(entry, %w[terms question], category)
|
|
33
|
+
strings(entry['terms'], "#{category}.terms")
|
|
34
|
+
raise Error, "#{category}.question must be a nonempty string." unless entry['question'].is_a?(String) && !entry['question'].strip.empty?
|
|
35
|
+
end
|
|
36
|
+
verbs = data['verbs']
|
|
37
|
+
raise Error, 'verbs must be a mapping of canonical forms to lemma arrays.' unless verbs.is_a?(Hash)
|
|
38
|
+
verbs.each do |canonical, forms|
|
|
39
|
+
raise Error, 'Verb canonical forms must be nonempty strings.' unless canonical.is_a?(String) && !canonical.strip.empty?
|
|
40
|
+
strings(forms, "verbs.#{canonical}")
|
|
41
|
+
raise Error, "verbs.#{canonical} must contain at least one lemma." if forms.empty?
|
|
42
|
+
end
|
|
43
|
+
aliases = verbs.values.flatten
|
|
44
|
+
raise Error, 'Verb lemmas must have only one canonical form.' unless aliases.uniq == aliases
|
|
45
|
+
%w[sahen negative_only compound_nouns].each { |key| strings(data[key], key) }
|
|
46
|
+
canonical_forms = verbs.keys + data['sahen'].map { |noun| noun + 'する' }
|
|
47
|
+
raise Error, 'negative_only must refer to configured canonical predicates.' unless (data['negative_only'] - canonical_forms).empty?
|
|
48
|
+
raise Error, 'compound_nouns must also appear in abstract-reference terms.' unless (data['compound_nouns'] - data['terms']['abstract-reference']['terms']).empty?
|
|
49
|
+
data
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -3,30 +3,14 @@
|
|
|
3
3
|
module AsciidocPubkit
|
|
4
4
|
class Rules
|
|
5
5
|
INLINE = /`[^`\n]*`|\+\+\+.*?\+\+\+|\+\+[^\n]*?\+\+|(?<!\w)\+[^+\n]+\+|「[^」\n]*」|『[^』\n]*』|\{[^}\n]+\}|<<[^>\n]+>>|\[\[[^\]\n]+\]\]|(?:link|xref|image|footnote|pass):[^\s\[]*\[[^\]\n]*\]|https?:\/\/[^\s\[\]<>]+(?:\[[^\]\n]*\])?/m
|
|
6
|
-
TERMS = {
|
|
7
|
-
'abstract-reference' => [%w[コスト 境界 契約 観点 土台 橋渡し 入口 記述 場所 意図 役割 一続き 根拠 部品 開発者], 'Identify the concrete referent, components, or measurable work. Keep established technical meanings.'],
|
|
8
|
-
'weak-predicate' => [%w[利用します 整理します 扱います 示します 変わります 把握します 分けられます そろえます まとまっています 加えます 探します 到達しません 扱いません そろいます 選べます 成り立たせています 確かめます 書き換える 絞れます 渡します あります], 'Check whether the purpose, operation, or result is clear from the surrounding paragraph. Preserve negation and conditions.'],
|
|
9
|
-
'vague-degree' => [%w[浅い 深い], 'Identify the concrete depth, level, scope, or comparison. Keep literal measurements and established technical meanings.'],
|
|
10
|
-
'contextual-phrase' => [%w[これらを であることだけでは あるものとします わけではありません], 'Check the referent, assumption, or qualification against the surrounding explanation. Preserve conditions and negation.'],
|
|
11
|
-
'generic-framing' => [%w[重要なのは ポイントは 本章では ここでは まとめると], 'Check whether this framing adds useful scope or information instead of repeating the explanation.']
|
|
12
|
-
}.freeze
|
|
13
|
-
VERBS = {
|
|
14
|
-
'扱う' => %w[扱う], '示す' => %w[示す], '変わる' => %w[変わる],
|
|
15
|
-
'分ける' => %w[分ける], 'そろえる' => %w[そろえる 揃える],
|
|
16
|
-
'まとまる' => %w[まとまる 纏まる], '加える' => %w[加える],
|
|
17
|
-
'探す' => %w[探す], 'そろう' => %w[そろう 揃う],
|
|
18
|
-
'選ぶ' => %w[選ぶ 選べる], '成り立つ' => %w[成り立つ],
|
|
19
|
-
'確かめる' => %w[確かめる], '書き換える' => %w[書き換える],
|
|
20
|
-
'絞る' => %w[絞る 絞れる], '渡す' => %w[渡す], 'ある' => %w[ある]
|
|
21
|
-
}.freeze
|
|
22
|
-
SAHEN = %w[利用 整理 把握 到達].freeze
|
|
23
|
-
|
|
24
6
|
def self.mask(text)
|
|
25
7
|
# Keep character offsets stable while excluding common inline constructs.
|
|
26
8
|
text.gsub(INLINE) { |match| match.gsub(/[^\n]/, ' ') }
|
|
27
9
|
end
|
|
28
10
|
|
|
29
11
|
def self.scan(paragraphs, settings, tokenizer: nil)
|
|
12
|
+
rules = RuleSet.validate(settings.fetch('rules') { RuleSet.load })
|
|
13
|
+
terms = rules.fetch('terms').transform_values { |entry| entry.values_at('terms', 'question') }
|
|
30
14
|
findings = []
|
|
31
15
|
if settings.fetch('tokenizer', 'mecab') == 'mecab'
|
|
32
16
|
tokenizer ||= Morphology.new(settings)
|
|
@@ -34,8 +18,8 @@ module AsciidocPubkit
|
|
|
34
18
|
token_groups = tokenizer ? tokenize_paragraphs(paragraphs, tokenizer) : []
|
|
35
19
|
paragraphs.each_with_index do |paragraph, index|
|
|
36
20
|
text = mask(paragraph.fetch('text'))
|
|
37
|
-
scan_morphemes(findings, paragraph, text, token_groups[index], settings) if tokenizer
|
|
38
|
-
|
|
21
|
+
scan_morphemes(findings, paragraph, text, token_groups[index], settings, rules, terms) if tokenizer
|
|
22
|
+
terms.each do |rule, (terms, question)|
|
|
39
23
|
next if tokenizer && !%w[generic-framing contextual-phrase].include?(rule)
|
|
40
24
|
terms.each do |term|
|
|
41
25
|
next if settings.fetch('allows').include?(term)
|
|
@@ -90,8 +74,8 @@ module AsciidocPubkit
|
|
|
90
74
|
end
|
|
91
75
|
end
|
|
92
76
|
|
|
93
|
-
def self.scan_morphemes(findings, paragraph, text, tokens, settings)
|
|
94
|
-
phrase_ranges =
|
|
77
|
+
def self.scan_morphemes(findings, paragraph, text, tokens, settings, rules, terms)
|
|
78
|
+
phrase_ranges = terms['contextual-phrase'][0].flat_map do |phrase|
|
|
95
79
|
text.to_enum(:scan, Regexp.new(Regexp.escape(phrase))).map do
|
|
96
80
|
match = Regexp.last_match
|
|
97
81
|
match.begin(0)...match.end(0)
|
|
@@ -102,26 +86,34 @@ module AsciidocPubkit
|
|
|
102
86
|
lemma = token['lemma']
|
|
103
87
|
rule = nil
|
|
104
88
|
finish_index = index
|
|
105
|
-
compound =
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
89
|
+
compound = rules.fetch('compound_nouns').find do |term|
|
|
90
|
+
surface = +''
|
|
91
|
+
cursor = index
|
|
92
|
+
while (member = tokens[cursor]) && member['pos'] == '名詞' && !member['unknown']
|
|
93
|
+
break if cursor > index && tokens[cursor - 1]['end_offset'] != member['offset']
|
|
94
|
+
surface << member['surface']
|
|
95
|
+
break unless term.start_with?(surface)
|
|
96
|
+
if surface == term
|
|
97
|
+
finish_index = cursor
|
|
98
|
+
break
|
|
99
|
+
end
|
|
100
|
+
cursor += 1
|
|
101
|
+
end
|
|
102
|
+
surface == term
|
|
110
103
|
end
|
|
111
104
|
if compound
|
|
112
105
|
lemma = compound
|
|
113
|
-
finish_index = index + 1
|
|
114
106
|
rule = 'abstract-reference'
|
|
115
|
-
elsif token['pos'] == '名詞' &&
|
|
107
|
+
elsif token['pos'] == '名詞' && terms['abstract-reference'][0].include?(lemma)
|
|
116
108
|
rule = 'abstract-reference'
|
|
117
|
-
elsif token['pos'] == '形容詞' &&
|
|
109
|
+
elsif token['pos'] == '形容詞' && terms['vague-degree'][0].include?(lemma)
|
|
118
110
|
rule = 'vague-degree'
|
|
119
111
|
finish_index = predicate_end(tokens, index, text)
|
|
120
|
-
elsif token['pos'] == '動詞' && (entry =
|
|
112
|
+
elsif token['pos'] == '動詞' && (entry = rules.fetch('verbs').find { |_canonical, forms| forms.include?(lemma) })
|
|
121
113
|
lemma = entry[0]
|
|
122
114
|
rule = 'weak-predicate'
|
|
123
115
|
finish_index = predicate_end(tokens, index, text)
|
|
124
|
-
elsif token['pos'] == '名詞' && token['pos_detail'] == 'サ変接続' &&
|
|
116
|
+
elsif token['pos'] == '名詞' && token['pos_detail'] == 'サ変接続' && rules.fetch('sahen').include?(lemma)
|
|
125
117
|
following = tokens[index + 1]
|
|
126
118
|
next unless following && following['pos'] == '動詞' && following['lemma'] == 'する' && adjacent?(token, following, text)
|
|
127
119
|
lemma += 'する'
|
|
@@ -131,11 +123,11 @@ module AsciidocPubkit
|
|
|
131
123
|
next unless rule
|
|
132
124
|
members = tokens[index..finish_index]
|
|
133
125
|
negative = members.any? { |member| member['pos'] == '助動詞' && %w[ない ぬ ん].include?(member['lemma']) }
|
|
134
|
-
next if
|
|
126
|
+
next if rules.fetch('negative_only').include?(lemma) && !negative
|
|
135
127
|
surface = text[token['offset']...tokens[finish_index]['end_offset']]
|
|
136
128
|
allows = settings.fetch('allows')
|
|
137
129
|
next if [lemma, token['lemma'], surface, surface + '。'].any? { |form| allows.include?(form) }
|
|
138
|
-
add(findings, paragraph, text, token['offset'], surface, rule, 'hint',
|
|
130
|
+
add(findings, paragraph, text, token['offset'], surface, rule, 'hint', terms.fetch(rule)[1])
|
|
139
131
|
findings.last.merge!('lemma' => lemma, 'part_of_speech' => token['pos'], 'negative' => negative,
|
|
140
132
|
'detector' => 'mecab-ipadic')
|
|
141
133
|
end
|
|
@@ -23,13 +23,16 @@ module AsciidocPubkit
|
|
|
23
23
|
reject_keys(config, ['review'], 'configuration')
|
|
24
24
|
review = config.fetch('review', {})
|
|
25
25
|
raise Error, 'review must be a mapping.' unless review.is_a?(Hash)
|
|
26
|
-
reject_keys(review, %w[language style glossary exclude allows attributes base_dir tokenizer mecab_command mecab_dictionary], 'review')
|
|
26
|
+
reject_keys(review, %w[language style glossary exclude allows attributes base_dir tokenizer mecab_command mecab_dictionary rules], 'review')
|
|
27
27
|
raise Error, 'attributes must be a mapping.' unless review.fetch('attributes', {}).is_a?(Hash)
|
|
28
|
-
%w[base_dir glossary mecab_command mecab_dictionary].each do |key|
|
|
28
|
+
%w[base_dir glossary mecab_command mecab_dictionary rules].each do |key|
|
|
29
29
|
raise Error, "#{key} must be a nonempty path string." if review.key?(key) && (!review[key].is_a?(String) || review[key].empty?)
|
|
30
30
|
end
|
|
31
31
|
base = @path ? File.dirname(@path) : File.dirname(File.expand_path(entry))
|
|
32
|
+
rules_path = options[:rules] ? File.expand_path(options[:rules]) : (review['rules'] ? File.expand_path(review['rules'], base) : RuleSet::DEFAULT_PATH)
|
|
32
33
|
@data = {
|
|
34
|
+
'rules' => RuleSet.load(rules_path),
|
|
35
|
+
'rules_path' => rules_path,
|
|
33
36
|
'language' => options[:language] || review.fetch('language', 'ja'),
|
|
34
37
|
'style' => options[:style] || review.fetch('style', 'preserve'),
|
|
35
38
|
'exclude' => review.fetch('exclude', []),
|
data/lib/asciidoc_pubkit.rb
CHANGED
|
@@ -9,7 +9,7 @@ require 'pathname'
|
|
|
9
9
|
require 'optparse'
|
|
10
10
|
|
|
11
11
|
module AsciidocPubkit
|
|
12
|
-
VERSION = '0.1.
|
|
12
|
+
VERSION = '0.1.2'
|
|
13
13
|
class Error < StandardError; end
|
|
14
14
|
|
|
15
15
|
def self.hash_text(text)
|
|
@@ -23,6 +23,7 @@ module AsciidocPubkit
|
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
+
require_relative 'asciidoc_pubkit/rule_set'
|
|
26
27
|
require_relative 'asciidoc_pubkit/settings'
|
|
27
28
|
require_relative 'asciidoc_pubkit/document'
|
|
28
29
|
require_relative 'asciidoc_pubkit/morphology'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: asciidoc-pubkit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- CyberGarage
|
|
@@ -47,12 +47,14 @@ files:
|
|
|
47
47
|
- CHANGELOG.md
|
|
48
48
|
- LICENSE
|
|
49
49
|
- README.md
|
|
50
|
+
- data/review-rules.ja.yml
|
|
50
51
|
- examples/book.adoc
|
|
51
52
|
- exe/asciidoc-pubkit
|
|
52
53
|
- lib/asciidoc_pubkit.rb
|
|
53
54
|
- lib/asciidoc_pubkit/cli.rb
|
|
54
55
|
- lib/asciidoc_pubkit/document.rb
|
|
55
56
|
- lib/asciidoc_pubkit/morphology.rb
|
|
57
|
+
- lib/asciidoc_pubkit/rule_set.rb
|
|
56
58
|
- lib/asciidoc_pubkit/rules.rb
|
|
57
59
|
- lib/asciidoc_pubkit/session.rb
|
|
58
60
|
- lib/asciidoc_pubkit/settings.rb
|