rsyntaxtree 2.1.0 → 2.3.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.
- checksums.yaml +4 -4
- data/.dockerignore +5 -0
- data/CHANGELOG.md +46 -10
- data/CODE_OF_CONDUCT.md +85 -0
- data/CONTRIBUTING.md +89 -0
- data/README.md +11 -17
- data/bin/rsyntaxtree +3 -6
- data/lib/rsyntaxtree/notation_examples.md +42 -16
- data/lib/rsyntaxtree/string_parser.rb +85 -10
- data/lib/rsyntaxtree/version.rb +1 -1
- data/lib/rsyntaxtree.rb +172 -14
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8c57f743f27cc6830495cfe9da3ce4483fec13491af8e80d4437357cc9c37968
|
|
4
|
+
data.tar.gz: d051b212a5abe64711fd42622bce06cea77c1e8bfc78a8315dfc1246f0dc22d0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e214718246e31cf0d42a03f91ee4b7e714cf1576f3f60fb70b6d0fb9d2d22a24e2b04e832d9e8be2df0b664b5506bf8a0994f659384a85469a4023535409c615
|
|
7
|
+
data.tar.gz: 5f7638450414b9cb53ac9fe2cdc08e7776af24ea50a0de75166dfbb3d47b9d4a27ab1d45a026012ef6bd9fdcc76400b1cf88f7df5095dac20cd78c629c3ccc33
|
data/.dockerignore
ADDED
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.3.0] - 2026-09-01
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `RSTError::CODES` lists every error code the library can report. A program
|
|
7
|
+
keyed by code — one that translates the diagnosis, or tallies what kinds of
|
|
8
|
+
mistake a writer makes — needs the whole set rather than the codes it has
|
|
9
|
+
happened to see, and until now that set could only be recovered by reading
|
|
10
|
+
five files and the repair table. The list only grows: a published code is
|
|
11
|
+
not renamed or removed.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
- Whitespace alone is an empty input rather than a defect in the library. It
|
|
15
|
+
reached the drawing, which walked off the end of an empty tree and reported
|
|
16
|
+
`internal_error`, telling the caller that their own fixable input was the
|
|
17
|
+
library's fault.
|
|
18
|
+
- A bracketless input of a single character draws. The tokenizer counted its
|
|
19
|
+
cursor as exhausted one character early, so `A`, and `<>` once it is one
|
|
20
|
+
whitespace block, produced no tokens at all — though the manual has always
|
|
21
|
+
said a label on its own draws as one leaf.
|
|
22
|
+
|
|
23
|
+
## [2.2.0] - 2026-09-01
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
- Validation reports every error of a stage, not just the first one found.
|
|
27
|
+
An input with three bad labels lists all three in the `errors` array of
|
|
28
|
+
the JSON diagnosis, so one round of fixes covers them. The stages are
|
|
29
|
+
ordered — options, bracket structure, labels, whole-tree checks — and a
|
|
30
|
+
mistake stops the later stages, with a `note` saying that fixing what is
|
|
31
|
+
listed may reveal more; nothing is reported whose appearance is an
|
|
32
|
+
artifact of an earlier mistake. Duplicate failures collapse to one entry
|
|
33
|
+
and the list stops at twenty. The schema is unchanged
|
|
34
|
+
(`rsyntaxtree.error/1`): the same array, more of it.
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
- `RSGenerator.diagnose(text, options)` returns the full diagnosis as a
|
|
38
|
+
hash — what `--validate` prints — for programmatic callers.
|
|
39
|
+
`check_data` keeps its contract of raising the first error.
|
|
40
|
+
|
|
3
41
|
## [2.1.0] - 2026-08-29
|
|
4
42
|
|
|
5
43
|
### Changed
|
|
@@ -79,9 +117,8 @@ looking at what was left. Every figure in the gallery draws exactly as it did.
|
|
|
79
117
|
|
|
80
118
|
### Added
|
|
81
119
|
- `shear`: the drawn figure tilts by the given angle (degrees, positive
|
|
82
|
-
leaning the top to the right) and lies on a plane drawn behind it,
|
|
83
|
-
|
|
84
|
-
whole picture shears as one affine piece — layout untouched, region
|
|
120
|
+
leaning the top to the right) and lies on a plane drawn behind it, which
|
|
121
|
+
shows the tilt as a surface. The whole picture shears as one affine piece — layout untouched, region
|
|
85
122
|
shades coming out parallelograms on their own, and nothing able to newly
|
|
86
123
|
touch or cross. The plane is fill without an edge — a region shade is
|
|
87
124
|
bounded because it marks one part of a figure off from the rest, and a
|
|
@@ -227,9 +264,8 @@ looking at what was left. Every figure in the gallery draws exactly as it did.
|
|
|
227
264
|
- Derivations. A derivation puts the words first and the result last, and
|
|
228
265
|
joins what each step combines with one rule drawn across all of it rather
|
|
229
266
|
than with a line to each daughter. `derivation` draws the rules and
|
|
230
|
-
`direction: btt` turns the tree over; together they
|
|
231
|
-
|
|
232
|
-
spans of an ordinary tree. The name of each step rides in the label after a
|
|
267
|
+
`direction: btt` turns the tree over; together they draw a CCG derivation,
|
|
268
|
+
and `derivation` on its own marks the spans of an ordinary tree. The name of each step rides in the label after a
|
|
233
269
|
column break and is set beside the end of its rule. A derivation runs down
|
|
234
270
|
the page and is drawn with its rules, so `direction: ltr` and hiding the
|
|
235
271
|
default connectors are refused rather than approximated.
|
|
@@ -361,15 +397,15 @@ RMagick dependency.
|
|
|
361
397
|
rule that work never uses.
|
|
362
398
|
- A matrix nested in a label, written between `#(` and `#)`. The value of an
|
|
363
399
|
attribute can be another attribute-value matrix, to any depth, which is what
|
|
364
|
-
a feature path such as SYNSEM | LOCAL | CATEGORY | HEAD needs and what
|
|
365
|
-
|
|
400
|
+
a feature path such as SYNSEM | LOCAL | CATEGORY | HEAD needs, and what
|
|
401
|
+
HPSG and LFG are written in. The nested matrix draws its own brackets and
|
|
366
402
|
lays out its own columns, and the rows after it clear its full height.
|
|
367
403
|
- `\t` in a label cuts the line into cells. Every line is cut at the same
|
|
368
404
|
points and each column is drawn at the width of its widest cell, so the
|
|
369
405
|
parts line up down the label. Together with the bracket enclosure and the
|
|
370
406
|
horizontal rule this gives attribute-value matrices — the feature structures
|
|
371
|
-
of HPSG,
|
|
372
|
-
gallery's HPSG example used to do it.
|
|
407
|
+
of HPSG, LFG and the like — without spacing each row by hand, which is how
|
|
408
|
+
the gallery's HPSG example used to do it.
|
|
373
409
|
- `color: gray`, a scheme that keeps node and leaf labels black and draws the
|
|
374
410
|
connectors, triangles and movement paths in grey. It is for diagrams whose
|
|
375
411
|
links outnumber their labels — an ontology, a network of constructions —
|
data/CODE_OF_CONDUCT.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
|
|
2
|
+
# Contributor Covenant Code of Conduct
|
|
3
|
+
|
|
4
|
+
## Our Pledge
|
|
5
|
+
|
|
6
|
+
We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.
|
|
7
|
+
|
|
8
|
+
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
|
|
9
|
+
|
|
10
|
+
## Our Standards
|
|
11
|
+
|
|
12
|
+
Examples of behavior that contributes to a positive environment for our community include:
|
|
13
|
+
|
|
14
|
+
* Demonstrating empathy and kindness toward other people
|
|
15
|
+
* Being respectful of differing opinions, viewpoints, and experiences
|
|
16
|
+
* Giving and gracefully accepting constructive feedback
|
|
17
|
+
* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
|
|
18
|
+
* Focusing on what is best not just for us as individuals, but for the overall community
|
|
19
|
+
|
|
20
|
+
Examples of unacceptable behavior include:
|
|
21
|
+
|
|
22
|
+
* The use of sexualized language or imagery, and sexual attention or advances of any kind
|
|
23
|
+
* Trolling, insulting or derogatory comments, and personal or political attacks
|
|
24
|
+
* Public or private harassment
|
|
25
|
+
* Publishing others' private information, such as a physical or email address, without their explicit permission
|
|
26
|
+
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
|
27
|
+
|
|
28
|
+
## Enforcement Responsibilities
|
|
29
|
+
|
|
30
|
+
Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
|
|
31
|
+
|
|
32
|
+
Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
|
|
33
|
+
|
|
34
|
+
## Scope
|
|
35
|
+
|
|
36
|
+
This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
|
|
37
|
+
|
|
38
|
+
## Enforcement
|
|
39
|
+
|
|
40
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at <yohasebe@gmail.com>. All complaints will be reviewed and investigated promptly and fairly.
|
|
41
|
+
|
|
42
|
+
All community leaders are obligated to respect the privacy and security of the reporter of any incident.
|
|
43
|
+
|
|
44
|
+
## Enforcement Guidelines
|
|
45
|
+
|
|
46
|
+
Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
|
|
47
|
+
|
|
48
|
+
### 1. Correction
|
|
49
|
+
|
|
50
|
+
**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
|
|
51
|
+
|
|
52
|
+
**Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
|
|
53
|
+
|
|
54
|
+
### 2. Warning
|
|
55
|
+
|
|
56
|
+
**Community Impact**: A violation through a single incident or series of actions.
|
|
57
|
+
|
|
58
|
+
**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
|
|
59
|
+
|
|
60
|
+
### 3. Temporary Ban
|
|
61
|
+
|
|
62
|
+
**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
|
|
63
|
+
|
|
64
|
+
**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
|
|
65
|
+
|
|
66
|
+
### 4. Permanent Ban
|
|
67
|
+
|
|
68
|
+
**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
|
|
69
|
+
|
|
70
|
+
**Consequence**: A permanent ban from any sort of public interaction within the community.
|
|
71
|
+
|
|
72
|
+
## Attribution
|
|
73
|
+
|
|
74
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
|
75
|
+
|
|
76
|
+
Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
|
77
|
+
|
|
78
|
+
For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations].
|
|
79
|
+
|
|
80
|
+
[homepage]: https://www.contributor-covenant.org
|
|
81
|
+
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
|
82
|
+
[Mozilla CoC]: https://github.com/mozilla/diversity
|
|
83
|
+
[FAQ]: https://www.contributor-covenant.org/faq
|
|
84
|
+
[translations]: https://www.contributor-covenant.org/translations
|
|
85
|
+
|
data/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Contributing to RSyntaxTree
|
|
2
|
+
|
|
3
|
+
This page explains how to report a problem, propose a change, and get a
|
|
4
|
+
development environment running.
|
|
5
|
+
|
|
6
|
+
## Reporting bugs
|
|
7
|
+
|
|
8
|
+
Open an issue at <https://github.com/yohasebe/rsyntaxtree/issues> with:
|
|
9
|
+
|
|
10
|
+
- the input text (the bracket notation) and the options you used
|
|
11
|
+
- what you expected and what you got — for drawing problems, attach the image
|
|
12
|
+
- your platform and how you run RSyntaxTree (gem, Docker, or the
|
|
13
|
+
[web interface](https://yohasebe.com/rsyntaxtree))
|
|
14
|
+
|
|
15
|
+
If the input fails to draw, include the output of:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
rsyntaxtree --validate "your input here"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
It reports the problem in a structured form that is usually enough to
|
|
22
|
+
diagnose the issue.
|
|
23
|
+
|
|
24
|
+
## Suggesting features
|
|
25
|
+
|
|
26
|
+
Open an issue describing what you want to draw and, ideally, a hand-drawn or
|
|
27
|
+
published example of the intended output. The
|
|
28
|
+
[gallery](https://yohasebe.github.io/rsyntaxtree/examples) shows the range of
|
|
29
|
+
figures the notation covers today; a suggestion that names the linguistic
|
|
30
|
+
framework it serves is easier to evaluate than one described purely in terms
|
|
31
|
+
of graphics.
|
|
32
|
+
|
|
33
|
+
Not every good idea belongs in RSyntaxTree. The project deliberately keeps
|
|
34
|
+
the notation small, so proposals are weighed against what they add for
|
|
35
|
+
users who do not need them.
|
|
36
|
+
|
|
37
|
+
## Setting up for development
|
|
38
|
+
|
|
39
|
+
System libraries first (Pango for text measurement, librsvg for rasterizing):
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Debian/Ubuntu
|
|
43
|
+
apt install libpango1.0-dev librsvg2-dev libgirepository1.0-dev gobject-introspection
|
|
44
|
+
|
|
45
|
+
# macOS
|
|
46
|
+
brew install pkg-config pango librsvg gobject-introspection
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The tests measure text with the Noto fonts, so install them too — the
|
|
50
|
+
[Fonts](https://yohasebe.github.io/rsyntaxtree/documentation#fonts) section
|
|
51
|
+
of the manual lists the package names for each platform.
|
|
52
|
+
|
|
53
|
+
Then:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
git clone https://github.com/yohasebe/rsyntaxtree.git
|
|
57
|
+
cd rsyntaxtree
|
|
58
|
+
bundle install
|
|
59
|
+
bundle exec rake test
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The tests should pass before and after your change.
|
|
63
|
+
|
|
64
|
+
## Making changes
|
|
65
|
+
|
|
66
|
+
- Every behavioral change needs a test that fails without it.
|
|
67
|
+
- The committed gallery figures are generated inside Docker
|
|
68
|
+
(`rake docker_build && rake docker_generate`) so that font differences
|
|
69
|
+
between machines do not rewrite them. Do not regenerate them with a local
|
|
70
|
+
`rake generate`; if your change intentionally alters figures, say so in
|
|
71
|
+
the pull request and the maintainer will regenerate them.
|
|
72
|
+
- The manual lives in `docs/documentation.md` (English) and
|
|
73
|
+
`docs/documentation_ja.md` (Japanese). If your change affects the
|
|
74
|
+
notation or an option, update at least the English manual; the reference
|
|
75
|
+
the gem installs (`lib/rsyntaxtree/notation_core.md`) and the LLM payload
|
|
76
|
+
(`rake llm_payload`) must be regenerated when examples or the manual
|
|
77
|
+
change — the test suite tells you when they are stale.
|
|
78
|
+
|
|
79
|
+
## Pull requests
|
|
80
|
+
|
|
81
|
+
Fork, branch, and open a pull request against `master`. A good pull request
|
|
82
|
+
explains the problem before the solution and stays small enough to review.
|
|
83
|
+
CI runs the test suite on Linux and an installation check on macOS; both
|
|
84
|
+
must pass.
|
|
85
|
+
|
|
86
|
+
## Questions
|
|
87
|
+
|
|
88
|
+
For anything that does not fit an issue, contact the maintainer:
|
|
89
|
+
Yoichiro Hasebe <yohasebe@gmail.com>.
|
data/README.md
CHANGED
|
@@ -135,23 +135,17 @@ See [Fonts](https://yohasebe.github.io/rsyntaxtree/documentation#install-fonts-f
|
|
|
135
135
|
gem install rsyntaxtree
|
|
136
136
|
```
|
|
137
137
|
|
|
138
|
-
### macOS
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
```bash
|
|
151
|
-
gem install rsyntaxtree
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
Alternatively, use the [Docker image](https://hub.docker.com/r/yohasebe/rsyntaxtree) or the [web interface](https://yohasebe.com/rsyntaxtree).
|
|
138
|
+
### macOS
|
|
139
|
+
|
|
140
|
+
With the libraries in place (`brew install pkg-config pango librsvg
|
|
141
|
+
gobject-introspection`), a plain `gem install rsyntaxtree` builds the native
|
|
142
|
+
extensions without further options — CI verifies this on every change. If an
|
|
143
|
+
older setup still fails to build `gobject-introspection`, `cairo-gobject` or
|
|
144
|
+
`gio2`, install those three first with
|
|
145
|
+
`-- --with-ldflags="-Wl,-undefined,dynamic_lookup"` appended, then install
|
|
146
|
+
RSyntaxTree; or skip the build entirely with the
|
|
147
|
+
[Docker image](https://hub.docker.com/r/yohasebe/rsyntaxtree) or the
|
|
148
|
+
[web interface](https://yohasebe.com/rsyntaxtree).
|
|
155
149
|
|
|
156
150
|
## Usage
|
|
157
151
|
|
data/bin/rsyntaxtree
CHANGED
|
@@ -214,12 +214,9 @@ data = RSyntaxTree::FormatConverter.to_bracket(data) if data
|
|
|
214
214
|
# Validate without drawing or writing anything: the diagnosis goes to stdout
|
|
215
215
|
# as JSON and the exit code carries the verdict.
|
|
216
216
|
if opts[:validate]
|
|
217
|
-
diagnosis =
|
|
218
|
-
RSyntaxTree::RSGenerator.
|
|
219
|
-
|
|
220
|
-
rescue RSTError => e
|
|
221
|
-
{ "schema" => "rsyntaxtree.error/1" }.merge(e.to_h)
|
|
222
|
-
end
|
|
217
|
+
diagnosis = { "schema" => "rsyntaxtree.error/1" }.merge(
|
|
218
|
+
RSyntaxTree::RSGenerator.diagnose(data, string_opts)
|
|
219
|
+
)
|
|
223
220
|
puts JSON.pretty_generate(diagnosis)
|
|
224
221
|
exit(diagnosis["ok"] ? 0 : 1)
|
|
225
222
|
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
RSyntaxTree examples:
|
|
1
|
+
RSyntaxTree examples: 81 trees, every one verified to draw.
|
|
2
2
|
|
|
3
3
|
Each is the input behind a figure in the gallery at https://yohasebe.github.io/rsyntaxtree/examples.
|
|
4
4
|
The settings line names the options the gallery records for that figure;
|
|
@@ -2586,30 +2586,56 @@ VAL\t#(SPR\t⟨<>⟩#)#)#)
|
|
|
2586
2586
|
VAL\t#(SPR\t⟨<>*NP*<>⟩#)#)#) [barks]]]
|
|
2587
2587
|
```
|
|
2588
2588
|
|
|
2589
|
-
## 085 — A tree on a tilted plane
|
|
2589
|
+
## 085 — A tree on a tilted plane, with a movement path
|
|
2590
2590
|
|
|
2591
2591
|
Category: General
|
|
2592
2592
|
Settings: fontstyle=noto-serif shear=20 vheight=1.0
|
|
2593
2593
|
|
|
2594
2594
|
```
|
|
2595
|
-
[
|
|
2596
|
-
[NP
|
|
2597
|
-
|
|
2598
|
-
[
|
|
2599
|
-
|
|
2600
|
-
[VP
|
|
2601
|
-
[V put]
|
|
2602
|
-
[NP
|
|
2603
|
-
[D the]
|
|
2604
|
-
[N book]
|
|
2605
|
-
]
|
|
2606
|
-
[PP
|
|
2607
|
-
[P on]
|
|
2595
|
+
[CP
|
|
2596
|
+
[NP what+>1]
|
|
2597
|
+
[C'
|
|
2598
|
+
[C did]
|
|
2599
|
+
[TP
|
|
2608
2600
|
[NP
|
|
2609
2601
|
[D the]
|
|
2610
|
-
[N
|
|
2602
|
+
[N man]
|
|
2603
|
+
]
|
|
2604
|
+
[VP
|
|
2605
|
+
[V'
|
|
2606
|
+
[V put]
|
|
2607
|
+
[NP *t*+1]
|
|
2608
|
+
]
|
|
2609
|
+
[PP
|
|
2610
|
+
[P on]
|
|
2611
|
+
[NP
|
|
2612
|
+
[D the]
|
|
2613
|
+
[N table]
|
|
2614
|
+
]
|
|
2615
|
+
]
|
|
2611
2616
|
]
|
|
2612
2617
|
]
|
|
2613
2618
|
]
|
|
2614
2619
|
]
|
|
2615
2620
|
```
|
|
2621
|
+
|
|
2622
|
+
## 086 — Feature structures on a tilted plane
|
|
2623
|
+
|
|
2624
|
+
Category: General
|
|
2625
|
+
Settings: color=none fontstyle=noto-serif leafstyle=nothing shear=20 vheight=1.5
|
|
2626
|
+
|
|
2627
|
+
```
|
|
2628
|
+
[#(*phrase*\
|
|
2629
|
+
---\
|
|
2630
|
+
SYN\t#(HEAD\t|1|#)#)
|
|
2631
|
+
[#(*word*\
|
|
2632
|
+
---\
|
|
2633
|
+
SYN\t#(HEAD\t#(*noun*\
|
|
2634
|
+
---\
|
|
2635
|
+
AGR\t|2|#(NUM\t*pl*#)#)#)#) [dogs]]
|
|
2636
|
+
[#(*word*\
|
|
2637
|
+
---\
|
|
2638
|
+
SYN\t#(HEAD\t|1|#(*verb*\
|
|
2639
|
+
---\
|
|
2640
|
+
AGR\t|2|#)#)#) [bark]]]
|
|
2641
|
+
```
|
|
@@ -15,9 +15,23 @@ require_relative 'utils'
|
|
|
15
15
|
module RSyntaxTree
|
|
16
16
|
class StringParser
|
|
17
17
|
attr_accessor :data, :elist, :pos, :id, :level
|
|
18
|
+
attr_reader :collected_errors
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
# In collect mode, reporting stops here but the walk does not: labels
|
|
21
|
+
# past this many failures are still checked (the tree must stay whole)
|
|
22
|
+
# without adding to the list.
|
|
23
|
+
COLLECTED_ERRORS_LIMIT = 20
|
|
24
|
+
|
|
25
|
+
# What stands in for a label that would not parse, when collecting.
|
|
26
|
+
# Any label that always parses will do; what matters is that it keeps
|
|
27
|
+
# the tree's shape.
|
|
28
|
+
PLACEHOLDER_LABEL = "x"
|
|
29
|
+
|
|
30
|
+
def initialize(str, fontset, fontsize, global, collect_errors: false)
|
|
20
31
|
@global = global
|
|
32
|
+
@collect_errors = collect_errors
|
|
33
|
+
@collected_errors = []
|
|
34
|
+
@more_errors = false
|
|
21
35
|
# Clean up the data a little to make processing easier
|
|
22
36
|
# repeated newlines => a newline
|
|
23
37
|
string = str.gsub(/[\n\r]+/m, "\n")
|
|
@@ -50,7 +64,12 @@ module RSyntaxTree
|
|
|
50
64
|
end
|
|
51
65
|
|
|
52
66
|
def self.valid?(data)
|
|
53
|
-
|
|
67
|
+
# Whitespace alone is an empty input, not a tree with one blank label:
|
|
68
|
+
# nothing survives the parse, and what used to happen is that the
|
|
69
|
+
# drawing walked off the end of an empty element list and reported a
|
|
70
|
+
# defect in the library for what the caller could see and fix. A space
|
|
71
|
+
# meant as a label is written <>, which is not whitespace here.
|
|
72
|
+
raise RSTError.new(+"Error: input text is empty", code: :empty_input, retryable: false) if data.strip.empty?
|
|
54
73
|
|
|
55
74
|
if /\[\s*\]/m =~ data
|
|
56
75
|
raise RSTError.new(+"Error: inside the brackets is empty", code: :empty_brackets,
|
|
@@ -113,6 +132,41 @@ module RSyntaxTree
|
|
|
113
132
|
restore_rule_names_without_a_rule
|
|
114
133
|
end
|
|
115
134
|
|
|
135
|
+
def more_errors?
|
|
136
|
+
@more_errors
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# One element, or its recorded failure. Outside collect mode this is
|
|
140
|
+
# exactly the Element.new it wraps. In collect mode an RSTError is
|
|
141
|
+
# recorded and a placeholder element stands in, so the walk continues
|
|
142
|
+
# and every bad label is seen. The placeholder keeps the tree's shape —
|
|
143
|
+
# parenthood, childlessness, level — which is all a later label's
|
|
144
|
+
# verdict can depend on; what it cannot keep (a path marker the broken
|
|
145
|
+
# label carried) only matters to the whole-tree checks, and those do
|
|
146
|
+
# not run while collected errors stand.
|
|
147
|
+
def element_or_recorded_failure(id, parent, level, names_a_rule = false)
|
|
148
|
+
yield
|
|
149
|
+
rescue RSTError => e
|
|
150
|
+
raise unless @collect_errors
|
|
151
|
+
|
|
152
|
+
record_failure(e)
|
|
153
|
+
Element.new(id, parent, PLACEHOLDER_LABEL, level, @fontset, @fontsize, @global, names_a_rule)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# The same mistake in two copies of a label is one thing to fix, so a
|
|
157
|
+
# failure is recorded once per (code, label, position). Past the limit
|
|
158
|
+
# the list stops growing and only the fact that there was more is kept.
|
|
159
|
+
def record_failure(error)
|
|
160
|
+
key = [error.code, error.label, error.position]
|
|
161
|
+
return if @collected_errors.any? { |c| [c.code, c.label, c.position] == key }
|
|
162
|
+
|
|
163
|
+
if @collected_errors.length >= COLLECTED_ERRORS_LIMIT
|
|
164
|
+
@more_errors = true
|
|
165
|
+
else
|
|
166
|
+
@collected_errors << error
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
116
170
|
# A rule name names the step that produced a node from its daughters. A node
|
|
117
171
|
# with no daughters is the product of no step, so what looked like a name is
|
|
118
172
|
# a column of the label like any other, and it goes back.
|
|
@@ -128,8 +182,17 @@ module RSyntaxTree
|
|
|
128
182
|
next unless e.children.empty?
|
|
129
183
|
next if e.label_with_rule_name.nil?
|
|
130
184
|
|
|
131
|
-
|
|
132
|
-
|
|
185
|
+
# In collect mode the failed restoration is recorded and the element
|
|
186
|
+
# kept as it is; the tree stays whole either way.
|
|
187
|
+
restored = begin
|
|
188
|
+
Element.new(e.id, e.parent, e.label_with_rule_name,
|
|
189
|
+
e.level, @fontset, @fontsize, @global)
|
|
190
|
+
rescue RSTError => err
|
|
191
|
+
raise unless @collect_errors
|
|
192
|
+
|
|
193
|
+
record_failure(err)
|
|
194
|
+
next
|
|
195
|
+
end
|
|
133
196
|
restored.children = e.children
|
|
134
197
|
restored.type = e.type
|
|
135
198
|
@elist.elements[i] = restored
|
|
@@ -146,7 +209,11 @@ module RSyntaxTree
|
|
|
146
209
|
token = ""
|
|
147
210
|
i = 0
|
|
148
211
|
|
|
149
|
-
|
|
212
|
+
# The cursor is exhausted when it reaches the end, not one before it:
|
|
213
|
+
# counting from @pos + 1 skipped the last character, so an input of a
|
|
214
|
+
# single character — "A", or "<>" once it is one whitespace block —
|
|
215
|
+
# produced no tokens at all and drew from an empty element list.
|
|
216
|
+
return "" if @pos >= data.length
|
|
150
217
|
|
|
151
218
|
escape = false
|
|
152
219
|
while ((@pos + i) < data.length) && !gottoken
|
|
@@ -226,7 +293,9 @@ module RSyntaxTree
|
|
|
226
293
|
# Check for escaped square brackets
|
|
227
294
|
if token =~ /\A\\\[/ || token =~ /\A\\\]/
|
|
228
295
|
# Treat escaped brackets as regular text
|
|
229
|
-
element =
|
|
296
|
+
element = element_or_recorded_failure(@id, parent, @level) do
|
|
297
|
+
Element.new(@id, parent, token, @level, @fontset, @fontsize, @global)
|
|
298
|
+
end
|
|
230
299
|
@id += 1
|
|
231
300
|
@elist.add(element)
|
|
232
301
|
else
|
|
@@ -241,7 +310,7 @@ module RSyntaxTree
|
|
|
241
310
|
tl = token_r.length
|
|
242
311
|
parts[1] = token_r[spaceat, tl - spaceat].join
|
|
243
312
|
|
|
244
|
-
element =
|
|
313
|
+
element = element_or_recorded_failure(@id, parent, @level, true) do
|
|
245
314
|
Element.new(@id, parent, parts[0], @level, @fontset, @fontsize, @global, true)
|
|
246
315
|
rescue RSTError => e
|
|
247
316
|
# The first raw space splits a token into the node's label
|
|
@@ -277,11 +346,15 @@ module RSyntaxTree
|
|
|
277
346
|
@elist.add(element)
|
|
278
347
|
newparent = element.id
|
|
279
348
|
|
|
280
|
-
element =
|
|
349
|
+
element = element_or_recorded_failure(@id, @id - 1, @level + 1) do
|
|
350
|
+
Element.new(@id, @id - 1, parts[1], @level + 1, @fontset, @fontsize, @global)
|
|
351
|
+
end
|
|
281
352
|
@id += 1
|
|
282
353
|
else
|
|
283
354
|
joined = token_r.join
|
|
284
|
-
element =
|
|
355
|
+
element = element_or_recorded_failure(@id, parent, @level, true) do
|
|
356
|
+
Element.new(@id, parent, joined, @level, @fontset, @fontsize, @global, true)
|
|
357
|
+
end
|
|
285
358
|
@id += 1
|
|
286
359
|
newparent = element.id
|
|
287
360
|
end
|
|
@@ -291,7 +364,9 @@ module RSyntaxTree
|
|
|
291
364
|
end
|
|
292
365
|
else
|
|
293
366
|
if token.strip != ""
|
|
294
|
-
element =
|
|
367
|
+
element = element_or_recorded_failure(@id, parent, @level) do
|
|
368
|
+
Element.new(@id, parent, token, @level, @fontset, @fontsize, @global)
|
|
369
|
+
end
|
|
295
370
|
@id += 1
|
|
296
371
|
@elist.add(element)
|
|
297
372
|
end
|
data/lib/rsyntaxtree/version.rb
CHANGED
data/lib/rsyntaxtree.rb
CHANGED
|
@@ -133,20 +133,70 @@ class RSTError < StandardError
|
|
|
133
133
|
super(msg.gsub(WHITESPACE_BLOCK, "<>"))
|
|
134
134
|
end
|
|
135
135
|
|
|
136
|
+
# Every code an error from this library can carry. Consumers dispatch on
|
|
137
|
+
# these — a translation keyed by code, a harness counting what kinds of
|
|
138
|
+
# mistake occur — and both need to know the whole set, not only the codes
|
|
139
|
+
# they have happened to see: a kind of mistake nobody made is not the same
|
|
140
|
+
# as a kind that cannot happen. Until this list existed the set was only
|
|
141
|
+
# discoverable by reading five files and a repair table.
|
|
142
|
+
#
|
|
143
|
+
# Written out rather than computed, because it is a contract and a contract
|
|
144
|
+
# should be readable at a glance and visible in a diff. A test keeps it
|
|
145
|
+
# honest by finding the codes the library actually raises and comparing.
|
|
146
|
+
#
|
|
147
|
+
# The list grows and does not churn: a code once published is not renamed
|
|
148
|
+
# or removed, so a consumer keyed by code — a translation table, a tally of
|
|
149
|
+
# what kinds of mistake a writer makes — only ever has entries to add.
|
|
150
|
+
#
|
|
151
|
+
# Eight are named by the repair table, which diagnoses a label by rewriting
|
|
152
|
+
# it and asking the parser whether the rewrite reads: angle_brackets,
|
|
153
|
+
# bare_hyphen, incomplete_path, invalid_color, rule_name_without_derivation,
|
|
154
|
+
# stray_triangle, unclosed_markup, unclosed_matrix. invalid_markup is where
|
|
155
|
+
# a label lands whose mistake no single rewrite fits, which in practice
|
|
156
|
+
# means more than one. unknown_color and label_split are mistakes inside a
|
|
157
|
+
# label too, found before the repair table is reached.
|
|
158
|
+
CODES = %i[
|
|
159
|
+
angle_brackets
|
|
160
|
+
bare_hyphen
|
|
161
|
+
empty_brackets
|
|
162
|
+
empty_input
|
|
163
|
+
incomplete_path
|
|
164
|
+
internal_error
|
|
165
|
+
invalid_color
|
|
166
|
+
invalid_markup
|
|
167
|
+
invalid_option
|
|
168
|
+
label_split
|
|
169
|
+
path_multiple_ends
|
|
170
|
+
path_single_end
|
|
171
|
+
result_too_big
|
|
172
|
+
rule_name_without_derivation
|
|
173
|
+
stray_triangle
|
|
174
|
+
unbalanced_brackets
|
|
175
|
+
unclosed_markup
|
|
176
|
+
unclosed_matrix
|
|
177
|
+
unknown_color
|
|
178
|
+
].freeze
|
|
179
|
+
|
|
136
180
|
# Where the notation is written down. A hint repairs the mistake in front of
|
|
137
181
|
# it and says nothing about the rest, which is enough for a reader who knows
|
|
138
182
|
# the notation and not enough for one who was guessing at it. Given once, at
|
|
139
183
|
# the top, rather than repeated on every error.
|
|
140
184
|
REFERENCE = "rsyntaxtree --notation, or https://yohasebe.github.io/rsyntaxtree/llms-full.txt"
|
|
141
185
|
|
|
186
|
+
# One error as the hash the JSON diagnosis carries; to_h wraps a single
|
|
187
|
+
# one and diagnose collects many.
|
|
188
|
+
def error_entry
|
|
189
|
+
{ "code" => code.to_s,
|
|
190
|
+
"message" => message,
|
|
191
|
+
"label" => label,
|
|
192
|
+
"position" => position,
|
|
193
|
+
"hint" => hint,
|
|
194
|
+
"retryable" => retryable }.compact
|
|
195
|
+
end
|
|
196
|
+
|
|
142
197
|
def to_h
|
|
143
198
|
{ "ok" => false,
|
|
144
|
-
"errors" => [
|
|
145
|
-
"message" => message,
|
|
146
|
-
"label" => label,
|
|
147
|
-
"position" => position,
|
|
148
|
-
"hint" => hint,
|
|
149
|
-
"retryable" => retryable }.compact],
|
|
199
|
+
"errors" => [error_entry],
|
|
150
200
|
"reference" => REFERENCE }
|
|
151
201
|
end
|
|
152
202
|
end
|
|
@@ -185,13 +235,13 @@ module RSyntaxTree
|
|
|
185
235
|
|
|
186
236
|
if REMOVED_OPTIONS.key?(key)
|
|
187
237
|
raise RSTError.new(+"Error: option '#{key}' was removed in RSyntaxTree 2.0",
|
|
188
|
-
code: :invalid_option,
|
|
238
|
+
code: :invalid_option, label: key.to_s,
|
|
189
239
|
hint: "Use #{REMOVED_OPTIONS[key]} instead.",
|
|
190
240
|
retryable: false)
|
|
191
241
|
end
|
|
192
242
|
if OPTION_VALUES.key?(key) && !OPTION_VALUES[key].include?(value.to_s)
|
|
193
243
|
raise RSTError.new(+"Error: invalid value for option '#{key}': #{value.inspect}",
|
|
194
|
-
code: :invalid_option,
|
|
244
|
+
code: :invalid_option, label: key.to_s,
|
|
195
245
|
hint: "'#{key}' must be one of: #{OPTION_VALUES[key].join(', ')}.",
|
|
196
246
|
retryable: false)
|
|
197
247
|
end
|
|
@@ -202,14 +252,14 @@ module RSyntaxTree
|
|
|
202
252
|
if NUMERIC_RANGES.key?(key) && !value.is_a?(Numeric) &&
|
|
203
253
|
value.to_s.strip !~ /\A-?(\d+(\.\d+)?|\.\d+)\z/
|
|
204
254
|
raise RSTError.new(+"Error: invalid value for option '#{key}': #{value.inspect}",
|
|
205
|
-
code: :invalid_option,
|
|
255
|
+
code: :invalid_option, label: key.to_s,
|
|
206
256
|
hint: "'#{key}' takes a number.",
|
|
207
257
|
retryable: false)
|
|
208
258
|
end
|
|
209
259
|
if NUMERIC_RANGES.key?(key) && !NUMERIC_RANGES[key].cover?(value.to_f)
|
|
210
260
|
range = NUMERIC_RANGES[key]
|
|
211
261
|
raise RSTError.new(+"Error: invalid value for option '#{key}': #{value.inspect}",
|
|
212
|
-
code: :invalid_option,
|
|
262
|
+
code: :invalid_option, label: key.to_s,
|
|
213
263
|
hint: "'#{key}' must be in the range of #{range.begin}-#{range.end}.",
|
|
214
264
|
retryable: false)
|
|
215
265
|
end
|
|
@@ -288,7 +338,7 @@ module RSyntaxTree
|
|
|
288
338
|
else
|
|
289
339
|
unless COLOR_NAMES.include?(v.downcase) || v =~ /\A#(\h{3}|\h{6})\z/
|
|
290
340
|
raise RSTError.new(+"Error: invalid value for option 'shear_plane': #{value.inspect}",
|
|
291
|
-
code: :invalid_option,
|
|
341
|
+
code: :invalid_option, label: "shear_plane",
|
|
292
342
|
hint: "'shear_plane' is on, off, a colour name, " \
|
|
293
343
|
"or a hex colour of 3 or 6 digits.",
|
|
294
344
|
retryable: false)
|
|
@@ -333,7 +383,7 @@ module RSyntaxTree
|
|
|
333
383
|
# the combination is refused rather than approximated.
|
|
334
384
|
if @params[:derivation] == true && @params[:direction] == "ltr"
|
|
335
385
|
raise RSTError.new(+"Error: a derivation cannot be drawn left to right",
|
|
336
|
-
code: :invalid_option,
|
|
386
|
+
code: :invalid_option, label: "derivation",
|
|
337
387
|
hint: "A derivation runs down the page. Use direction ttb or btt, " \
|
|
338
388
|
"or turn derivation off.",
|
|
339
389
|
retryable: false)
|
|
@@ -346,7 +396,7 @@ module RSyntaxTree
|
|
|
346
396
|
# them. Refused for the same reason as left to right.
|
|
347
397
|
if @params[:derivation] == true && @params[:hide_default_connectors] == true
|
|
348
398
|
raise RSTError.new(+"Error: a derivation's rules cannot be hidden",
|
|
349
|
-
code: :invalid_option,
|
|
399
|
+
code: :invalid_option, label: "derivation",
|
|
350
400
|
hint: "The rules are what a derivation is drawn with, not a " \
|
|
351
401
|
"connector added to it. Turn off hide default connectors, " \
|
|
352
402
|
"or turn derivation off.",
|
|
@@ -429,8 +479,21 @@ module RSyntaxTree
|
|
|
429
479
|
!OFF.include?(value.to_s.strip.downcase)
|
|
430
480
|
end
|
|
431
481
|
|
|
482
|
+
# Whether the input is nothing to draw. Whitespace alone counts: it
|
|
483
|
+
# leaves no label behind, and reporting it as a defect in the library
|
|
484
|
+
# told the caller their own fixable input was our fault. Asked without
|
|
485
|
+
# strip when the bytes are not valid UTF-8, because strip raises there
|
|
486
|
+
# and that input is not blank — it is malformed, which the caller
|
|
487
|
+
# learns further down.
|
|
488
|
+
def self.blank?(text)
|
|
489
|
+
s = text.to_s
|
|
490
|
+
return true if s.empty?
|
|
491
|
+
|
|
492
|
+
s.valid_encoding? && s.strip.empty?
|
|
493
|
+
end
|
|
494
|
+
|
|
432
495
|
def self.check_data(text, params = {})
|
|
433
|
-
raise RSTError.new(+"Error: input text is empty", code: :empty_input, retryable: false) if text
|
|
496
|
+
raise RSTError.new(+"Error: input text is empty", code: :empty_input, retryable: false) if blank?(text)
|
|
434
497
|
|
|
435
498
|
begin
|
|
436
499
|
StringParser.valid?(text)
|
|
@@ -448,6 +511,101 @@ module RSyntaxTree
|
|
|
448
511
|
end
|
|
449
512
|
end
|
|
450
513
|
|
|
514
|
+
# Every option error at once. The constructor is the only judge of an
|
|
515
|
+
# option, and its rules are not written out a second time here: it
|
|
516
|
+
# stops at its first complaint, so it is asked again with the option
|
|
517
|
+
# it complained about set aside, until it accepts what is left or
|
|
518
|
+
# names nothing to set aside. Each round removes one option, so the
|
|
519
|
+
# loop is as bounded as the option list.
|
|
520
|
+
def self.option_errors(params)
|
|
521
|
+
remaining = params.reject { |k, _| k.to_sym == :data }
|
|
522
|
+
errors = []
|
|
523
|
+
loop do
|
|
524
|
+
begin
|
|
525
|
+
new(remaining.merge(data: "[A a]"))
|
|
526
|
+
break
|
|
527
|
+
rescue RSTError => e
|
|
528
|
+
errors << e
|
|
529
|
+
key = e.label
|
|
530
|
+
break if key.nil? || remaining.keys.none? { |k| k.to_s == key }
|
|
531
|
+
|
|
532
|
+
remaining = remaining.reject { |k, _| k.to_s == key }
|
|
533
|
+
end
|
|
534
|
+
end
|
|
535
|
+
errors
|
|
536
|
+
end
|
|
537
|
+
|
|
538
|
+
# Parse in collect mode: every label that will not parse, and every
|
|
539
|
+
# rule name that turns out to have no rule behind it, recorded instead
|
|
540
|
+
# of raised. The walk is the real one — the same tokens, the same
|
|
541
|
+
# judgments — so what this reports and what drawing rejects cannot
|
|
542
|
+
# drift apart. Returns the errors and whether the list was cut short.
|
|
543
|
+
def collect_input_errors
|
|
544
|
+
sp = StringParser.new(@params[:data].gsub('&', '&'), @params[:fontset],
|
|
545
|
+
@params[:fontsize], @global, collect_errors: true)
|
|
546
|
+
sp.parse
|
|
547
|
+
[sp.collected_errors, sp.more_errors?]
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
NOTE_OPTIONS = "Not every option could be read, so the input itself has " \
|
|
551
|
+
"not been checked yet; fixing the options may reveal more."
|
|
552
|
+
NOTE_STRUCTURE = "The bracket structure could not be read, so the labels " \
|
|
553
|
+
"have not been checked yet; fixing it may reveal more."
|
|
554
|
+
NOTE_LABELS = "Whole-tree checks (paths, output limits) run only once " \
|
|
555
|
+
"every label reads, so fixing these may reveal more."
|
|
556
|
+
NOTE_TRUNCATED = " Only the first #{StringParser::COLLECTED_ERRORS_LIMIT} " \
|
|
557
|
+
"problems are listed."
|
|
558
|
+
|
|
559
|
+
# The whole diagnosis at once, as the hash the CLI prints: every error
|
|
560
|
+
# of the first stage that finds any, not just the first error found.
|
|
561
|
+
# Stages are ordered so that nothing is reported whose appearance is an
|
|
562
|
+
# artifact of an earlier mistake — a missing bracket shifts every token
|
|
563
|
+
# after it, an unreadable hyphen option changes what counts as markup —
|
|
564
|
+
# and when a stage stops the walk, the note says that fixing what is
|
|
565
|
+
# listed may reveal more. check_data keeps its contract (raise the
|
|
566
|
+
# first error) for callers that want a verdict rather than a list.
|
|
567
|
+
def self.diagnose(text, params = {})
|
|
568
|
+
errors = []
|
|
569
|
+
note = nil
|
|
570
|
+
if blank?(text)
|
|
571
|
+
errors << RSTError.new(+"Error: input text is empty", code: :empty_input, retryable: false)
|
|
572
|
+
else
|
|
573
|
+
begin
|
|
574
|
+
errors = option_errors(params)
|
|
575
|
+
if errors.any?
|
|
576
|
+
note = NOTE_OPTIONS
|
|
577
|
+
else
|
|
578
|
+
StringParser.valid?(text)
|
|
579
|
+
gen = new(params.merge(data: text))
|
|
580
|
+
collected, truncated = gen.collect_input_errors
|
|
581
|
+
if collected.any?
|
|
582
|
+
errors = collected
|
|
583
|
+
note = NOTE_LABELS
|
|
584
|
+
note += NOTE_TRUNCATED if truncated
|
|
585
|
+
else
|
|
586
|
+
gen.validate!
|
|
587
|
+
end
|
|
588
|
+
end
|
|
589
|
+
rescue RSTError => e
|
|
590
|
+
errors << e
|
|
591
|
+
note = NOTE_STRUCTURE if %i[empty_brackets unbalanced_brackets].include?(e.code)
|
|
592
|
+
rescue StandardError => e
|
|
593
|
+
# The same promise check_data makes: a defect anywhere in here —
|
|
594
|
+
# the option probing included — is still a verdict of "no", in
|
|
595
|
+
# the same shape, not a backtrace.
|
|
596
|
+
errors << RSTError.new(+"Error: input could not be processed (#{e.class})",
|
|
597
|
+
code: :internal_error, retryable: false)
|
|
598
|
+
end
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
return { "ok" => true } if errors.empty?
|
|
602
|
+
|
|
603
|
+
result = { "ok" => false, "errors" => errors.map(&:error_entry) }
|
|
604
|
+
result["note"] = note if note
|
|
605
|
+
result["reference"] = RSTError::REFERENCE
|
|
606
|
+
result
|
|
607
|
+
end
|
|
608
|
+
|
|
451
609
|
# Generate, and throw the result away. Parsing alone leaves out the
|
|
452
610
|
# checks that only happen once the tree is laid out — a movement path
|
|
453
611
|
# with one end, a line with three — so validation that stopped at the
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rsyntaxtree
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yoichiro Hasebe
|
|
@@ -115,8 +115,11 @@ executables:
|
|
|
115
115
|
extensions: []
|
|
116
116
|
extra_rdoc_files: []
|
|
117
117
|
files:
|
|
118
|
+
- ".dockerignore"
|
|
118
119
|
- CHANGELOG.md
|
|
119
120
|
- CITATION.cff
|
|
121
|
+
- CODE_OF_CONDUCT.md
|
|
122
|
+
- CONTRIBUTING.md
|
|
120
123
|
- LICENSE
|
|
121
124
|
- README.md
|
|
122
125
|
- bin/rsyntaxtree
|