djot-py 0.1.0__tar.gz
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.
- djot_py-0.1.0/PKG-INFO +85 -0
- djot_py-0.1.0/README.md +64 -0
- djot_py-0.1.0/pyproject.toml +32 -0
- djot_py-0.1.0/pyproject.toml.orig +31 -0
- djot_py-0.1.0/src/djot/__init__.py +81 -0
- djot_py-0.1.0/src/djot/ast/__init__.py +345 -0
- djot_py-0.1.0/src/djot/ast/base.py +67 -0
- djot_py-0.1.0/src/djot/ast/block.py +90 -0
- djot_py-0.1.0/src/djot/ast/doc.py +14 -0
- djot_py-0.1.0/src/djot/ast/inline.py +262 -0
- djot_py-0.1.0/src/djot/ast/list.py +125 -0
- djot_py-0.1.0/src/djot/ast/table.py +45 -0
- djot_py-0.1.0/src/djot/attributes.py +494 -0
- djot_py-0.1.0/src/djot/block/__init__.py +1 -0
- djot_py-0.1.0/src/djot/block/attribute.py +92 -0
- djot_py-0.1.0/src/djot/block/blockquote.py +86 -0
- djot_py-0.1.0/src/djot/block/caption.py +90 -0
- djot_py-0.1.0/src/djot/block/code_block.py +178 -0
- djot_py-0.1.0/src/djot/block/container.py +246 -0
- djot_py-0.1.0/src/djot/block/fenced.py +207 -0
- djot_py-0.1.0/src/djot/block/footnote.py +122 -0
- djot_py-0.1.0/src/djot/block/heading.py +140 -0
- djot_py-0.1.0/src/djot/block/list.py +294 -0
- djot_py-0.1.0/src/djot/block/para.py +84 -0
- djot_py-0.1.0/src/djot/block/parser.py +455 -0
- djot_py-0.1.0/src/djot/block/ref_def.py +148 -0
- djot_py-0.1.0/src/djot/block/state.py +154 -0
- djot_py-0.1.0/src/djot/block/table.py +415 -0
- djot_py-0.1.0/src/djot/block/thematic.py +79 -0
- djot_py-0.1.0/src/djot/common/__init__.py +23 -0
- djot_py-0.1.0/src/djot/common/align.py +8 -0
- djot_py-0.1.0/src/djot/common/list_style.py +295 -0
- djot_py-0.1.0/src/djot/common/pos.py +143 -0
- djot_py-0.1.0/src/djot/common/range.py +10 -0
- djot_py-0.1.0/src/djot/common/string.py +90 -0
- djot_py-0.1.0/src/djot/event/__init__.py +16 -0
- djot_py-0.1.0/src/djot/event/event.py +227 -0
- djot_py-0.1.0/src/djot/event/kind.py +107 -0
- djot_py-0.1.0/src/djot/experimental/lexer.py +107 -0
- djot_py-0.1.0/src/djot/inline/__init__.py +1 -0
- djot_py-0.1.0/src/djot/inline/ascii.py +55 -0
- djot_py-0.1.0/src/djot/inline/backslash.py +95 -0
- djot_py-0.1.0/src/djot/inline/backtick.py +69 -0
- djot_py-0.1.0/src/djot/inline/between.py +346 -0
- djot_py-0.1.0/src/djot/inline/brace_left.py +46 -0
- djot_py-0.1.0/src/djot/inline/bracket_left.py +39 -0
- djot_py-0.1.0/src/djot/inline/bracket_right.py +257 -0
- djot_py-0.1.0/src/djot/inline/colon.py +43 -0
- djot_py-0.1.0/src/djot/inline/hyphen.py +99 -0
- djot_py-0.1.0/src/djot/inline/lessthan.py +89 -0
- djot_py-0.1.0/src/djot/inline/matcher.py +10 -0
- djot_py-0.1.0/src/djot/inline/paren_left.py +24 -0
- djot_py-0.1.0/src/djot/inline/paren_right.py +118 -0
- djot_py-0.1.0/src/djot/inline/parser.py +325 -0
- djot_py-0.1.0/src/djot/inline/period.py +30 -0
- djot_py-0.1.0/src/djot/inline/state.py +485 -0
- djot_py-0.1.0/src/djot/input.py +543 -0
- djot_py-0.1.0/src/djot/logger.py +5 -0
- djot_py-0.1.0/src/djot/parser/__init__.py +8 -0
- djot_py-0.1.0/src/djot/parser/attr.py +103 -0
- djot_py-0.1.0/src/djot/parser/builder.py +168 -0
- djot_py-0.1.0/src/djot/parser/frame.py +327 -0
- djot_py-0.1.0/src/djot/parser/handle_attr.py +129 -0
- djot_py-0.1.0/src/djot/parser/handle_blockquote.py +20 -0
- djot_py-0.1.0/src/djot/parser/handle_default.py +209 -0
- djot_py-0.1.0/src/djot/parser/handle_div.py +23 -0
- djot_py-0.1.0/src/djot/parser/handle_footnote.py +63 -0
- djot_py-0.1.0/src/djot/parser/handle_heading.py +27 -0
- djot_py-0.1.0/src/djot/parser/handle_link.py +264 -0
- djot_py-0.1.0/src/djot/parser/handle_list.py +329 -0
- djot_py-0.1.0/src/djot/parser/handle_para.py +19 -0
- djot_py-0.1.0/src/djot/parser/handle_table.py +147 -0
- djot_py-0.1.0/src/djot/parser/handle_thematic.py +21 -0
- djot_py-0.1.0/src/djot/parser/handle_verb.py +161 -0
- djot_py-0.1.0/src/djot/parser/handler.py +64 -0
- djot_py-0.1.0/src/djot/parser/id.py +47 -0
- djot_py-0.1.0/src/djot/parser/normalize.py +35 -0
- djot_py-0.1.0/src/djot/parser/parse.py +93 -0
- djot_py-0.1.0/src/djot/parser/roman.py +53 -0
- djot_py-0.1.0/src/djot/parser/state.py +123 -0
- djot_py-0.1.0/src/djot/py.typed +0 -0
djot_py-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: djot-py
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A pure Python implementation of the Jot markup language
|
|
5
|
+
Author: Victor Nee
|
|
6
|
+
Author-email: Victor Nee <neefrankie@gmail.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
11
|
+
Classifier: Topic :: Text Processing :: Markup
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.12
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# djot-py
|
|
23
|
+
|
|
24
|
+
A Python library for parsing the light markup format [djot](https://github.com/jgm/djot).
|
|
25
|
+
|
|
26
|
+
This implementation is based on and modified from [djot.js](https://github.com/jgm/djot.js).
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install djot-py
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## API
|
|
35
|
+
|
|
36
|
+
To parse text into an AST, call `parse`:
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from djot import parse
|
|
40
|
+
|
|
41
|
+
ast = parse("hi there\nfriend\n\nnew para")
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
To parse text into events:
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from djot import EventParser
|
|
48
|
+
|
|
49
|
+
text = "hello *world*\n\nfoo"
|
|
50
|
+
|
|
51
|
+
parser = EventParser(text)
|
|
52
|
+
|
|
53
|
+
# The parse method returns an iterator of events.
|
|
54
|
+
for event in parser.parse():
|
|
55
|
+
print(event)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
There are no rendering features available currently. Will be added in the future.
|
|
59
|
+
|
|
60
|
+
## Differences from Djot.js
|
|
61
|
+
|
|
62
|
+
- Comment is not supported yet.
|
|
63
|
+
- Multi-line attributes is not supported yet.
|
|
64
|
+
|
|
65
|
+
## My personal view on the above not-yet-implemented features
|
|
66
|
+
|
|
67
|
+
### Comment
|
|
68
|
+
|
|
69
|
+
Comment should not be mixed with attributes.
|
|
70
|
+
|
|
71
|
+
I hope there's a specific comment syntax in the future. For example, there could be two types of comments:
|
|
72
|
+
|
|
73
|
+
- Line comment appeared at the end of each line, using symbols like `%%`
|
|
74
|
+
- Block comment wrapped in `%%%`, similar to code block.
|
|
75
|
+
|
|
76
|
+
### Multi-line attributes
|
|
77
|
+
|
|
78
|
+
Djot.js current implementation to support multi-line attributes involves a lot of backtracing,
|
|
79
|
+
which is in direct conflict with the philosophy of djot. To support multi-line attributes,
|
|
80
|
+
there needs a fundamental change in the line-based parsing algorithm.
|
|
81
|
+
|
|
82
|
+
### List
|
|
83
|
+
|
|
84
|
+
This is not hard to implement. However, too much choices might introduce future ambiguity.
|
|
85
|
+
I prefere simplicity rather than feature-rich. For example, Typst style simply uses `-` and `+`. Leave styling to a renderer. And block attributes could be used to convey some basic styling information.
|
djot_py-0.1.0/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# djot-py
|
|
2
|
+
|
|
3
|
+
A Python library for parsing the light markup format [djot](https://github.com/jgm/djot).
|
|
4
|
+
|
|
5
|
+
This implementation is based on and modified from [djot.js](https://github.com/jgm/djot.js).
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install djot-py
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## API
|
|
14
|
+
|
|
15
|
+
To parse text into an AST, call `parse`:
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
from djot import parse
|
|
19
|
+
|
|
20
|
+
ast = parse("hi there\nfriend\n\nnew para")
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
To parse text into events:
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
from djot import EventParser
|
|
27
|
+
|
|
28
|
+
text = "hello *world*\n\nfoo"
|
|
29
|
+
|
|
30
|
+
parser = EventParser(text)
|
|
31
|
+
|
|
32
|
+
# The parse method returns an iterator of events.
|
|
33
|
+
for event in parser.parse():
|
|
34
|
+
print(event)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
There are no rendering features available currently. Will be added in the future.
|
|
38
|
+
|
|
39
|
+
## Differences from Djot.js
|
|
40
|
+
|
|
41
|
+
- Comment is not supported yet.
|
|
42
|
+
- Multi-line attributes is not supported yet.
|
|
43
|
+
|
|
44
|
+
## My personal view on the above not-yet-implemented features
|
|
45
|
+
|
|
46
|
+
### Comment
|
|
47
|
+
|
|
48
|
+
Comment should not be mixed with attributes.
|
|
49
|
+
|
|
50
|
+
I hope there's a specific comment syntax in the future. For example, there could be two types of comments:
|
|
51
|
+
|
|
52
|
+
- Line comment appeared at the end of each line, using symbols like `%%`
|
|
53
|
+
- Block comment wrapped in `%%%`, similar to code block.
|
|
54
|
+
|
|
55
|
+
### Multi-line attributes
|
|
56
|
+
|
|
57
|
+
Djot.js current implementation to support multi-line attributes involves a lot of backtracing,
|
|
58
|
+
which is in direct conflict with the philosophy of djot. To support multi-line attributes,
|
|
59
|
+
there needs a fundamental change in the line-based parsing algorithm.
|
|
60
|
+
|
|
61
|
+
### List
|
|
62
|
+
|
|
63
|
+
This is not hard to implement. However, too much choices might introduce future ambiguity.
|
|
64
|
+
I prefere simplicity rather than feature-rich. For example, Typst style simply uses `-` and `+`. Leave styling to a renderer. And block attributes could be used to convey some basic styling information.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "djot-py"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "A pure Python implementation of the Jot markup language"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
classifiers = [
|
|
8
|
+
"Development Status :: 3 - Alpha",
|
|
9
|
+
"Intended Audience :: Developers",
|
|
10
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
11
|
+
"Topic :: Text Processing :: Markup",
|
|
12
|
+
"Operating System :: OS Independent",
|
|
13
|
+
"Programming Language :: Python :: 3",
|
|
14
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
15
|
+
"Programming Language :: Python :: 3.10",
|
|
16
|
+
"Programming Language :: Python :: 3.11",
|
|
17
|
+
"Programming Language :: Python :: 3.12",
|
|
18
|
+
"Typing :: Typed",
|
|
19
|
+
]
|
|
20
|
+
license = "MIT"
|
|
21
|
+
dependencies = []
|
|
22
|
+
|
|
23
|
+
[[project.authors]]
|
|
24
|
+
name = "Victor Nee"
|
|
25
|
+
email = "neefrankie@gmail.com"
|
|
26
|
+
|
|
27
|
+
[build-system]
|
|
28
|
+
requires = ["uv_build>=0.12.6,<0.13.0"]
|
|
29
|
+
build-backend = "uv_build"
|
|
30
|
+
|
|
31
|
+
[tool.uv.build-backend]
|
|
32
|
+
module-name = "djot"
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "djot-py"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "A pure Python implementation of the Jot markup language"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [
|
|
7
|
+
{ name = "Victor Nee", email = "neefrankie@gmail.com" }
|
|
8
|
+
]
|
|
9
|
+
requires-python = ">=3.12"
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 3 - Alpha",
|
|
12
|
+
"Intended Audience :: Developers",
|
|
13
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
14
|
+
"Topic :: Text Processing :: Markup",
|
|
15
|
+
"Operating System :: OS Independent",
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
18
|
+
"Programming Language :: Python :: 3.10",
|
|
19
|
+
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Typing :: Typed",
|
|
22
|
+
]
|
|
23
|
+
license = "MIT"
|
|
24
|
+
dependencies = []
|
|
25
|
+
|
|
26
|
+
[build-system]
|
|
27
|
+
requires = ["uv_build>=0.12.6,<0.13.0"]
|
|
28
|
+
build-backend = "uv_build"
|
|
29
|
+
|
|
30
|
+
[tool.uv.build-backend]
|
|
31
|
+
module-name = "djot"
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from .parser import (
|
|
2
|
+
parse,
|
|
3
|
+
ASTBuilder,
|
|
4
|
+
render_ast,
|
|
5
|
+
)
|
|
6
|
+
from .block import (
|
|
7
|
+
EventParser,
|
|
8
|
+
)
|
|
9
|
+
from .event import (
|
|
10
|
+
Event,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
from .common import (
|
|
14
|
+
Alignment,
|
|
15
|
+
unescape_djot,
|
|
16
|
+
Pos,
|
|
17
|
+
SourceLoc,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
from .ast import (
|
|
21
|
+
AstNode,
|
|
22
|
+
Attributes,
|
|
23
|
+
BlockNode,
|
|
24
|
+
BlockQuote,
|
|
25
|
+
BulletList,
|
|
26
|
+
BulletListStyle,
|
|
27
|
+
Caption,
|
|
28
|
+
Cell,
|
|
29
|
+
CheckboxStatus,
|
|
30
|
+
CodeBlock,
|
|
31
|
+
Definition,
|
|
32
|
+
DefinitionList,
|
|
33
|
+
DefinitionListItem,
|
|
34
|
+
Delete,
|
|
35
|
+
DisplayMath,
|
|
36
|
+
Div,
|
|
37
|
+
Doc,
|
|
38
|
+
DoubleQuoted,
|
|
39
|
+
Email,
|
|
40
|
+
Emph,
|
|
41
|
+
Footnote,
|
|
42
|
+
FootnoteReference,
|
|
43
|
+
HardBreak,
|
|
44
|
+
HasChildren,
|
|
45
|
+
HasText,
|
|
46
|
+
Heading,
|
|
47
|
+
Image,
|
|
48
|
+
InlineNode,
|
|
49
|
+
InlineMath,
|
|
50
|
+
Insert,
|
|
51
|
+
Link,
|
|
52
|
+
ListItem,
|
|
53
|
+
Mark,
|
|
54
|
+
NonBreakingSpace,
|
|
55
|
+
OrderedList,
|
|
56
|
+
OrderedListStyle,
|
|
57
|
+
Para,
|
|
58
|
+
RawBlock,
|
|
59
|
+
RawInline,
|
|
60
|
+
Reference,
|
|
61
|
+
Row,
|
|
62
|
+
Section,
|
|
63
|
+
SingleQuoted,
|
|
64
|
+
SmartPunctuation,
|
|
65
|
+
SmartPunctType,
|
|
66
|
+
SoftBreak,
|
|
67
|
+
Span,
|
|
68
|
+
Str,
|
|
69
|
+
Strong,
|
|
70
|
+
Subscript,
|
|
71
|
+
Superscript,
|
|
72
|
+
Symb,
|
|
73
|
+
Table,
|
|
74
|
+
TaskList,
|
|
75
|
+
TaskListItem,
|
|
76
|
+
Term,
|
|
77
|
+
ThematicBreak,
|
|
78
|
+
Url,
|
|
79
|
+
Verbatim,
|
|
80
|
+
Visitor,
|
|
81
|
+
)
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import TypeGuard, TypeVar
|
|
3
|
+
|
|
4
|
+
from .base import (
|
|
5
|
+
AstNode,
|
|
6
|
+
Attributes,
|
|
7
|
+
merge_attributes,
|
|
8
|
+
BlockNode,
|
|
9
|
+
InlineNode,
|
|
10
|
+
HasChildren,
|
|
11
|
+
HasText,
|
|
12
|
+
ContentBlock,
|
|
13
|
+
DefinitionBlock,
|
|
14
|
+
)
|
|
15
|
+
from .doc import Doc
|
|
16
|
+
from .block import (
|
|
17
|
+
Para,
|
|
18
|
+
Heading,
|
|
19
|
+
ThematicBreak,
|
|
20
|
+
Section,
|
|
21
|
+
Div,
|
|
22
|
+
CodeBlock,
|
|
23
|
+
RawBlock,
|
|
24
|
+
BlockQuote,
|
|
25
|
+
Footnote,
|
|
26
|
+
Reference,
|
|
27
|
+
)
|
|
28
|
+
from .list import (
|
|
29
|
+
OrderedList,
|
|
30
|
+
BulletList,
|
|
31
|
+
TaskList,
|
|
32
|
+
DefinitionList,
|
|
33
|
+
ListItem,
|
|
34
|
+
TaskListItem,
|
|
35
|
+
DefinitionListItem,
|
|
36
|
+
Term,
|
|
37
|
+
Definition,
|
|
38
|
+
BulletListStyle,
|
|
39
|
+
OrderedListStyle,
|
|
40
|
+
CheckboxStatus,
|
|
41
|
+
)
|
|
42
|
+
from .table import (
|
|
43
|
+
Table,
|
|
44
|
+
Caption,
|
|
45
|
+
Row,
|
|
46
|
+
Cell,
|
|
47
|
+
)
|
|
48
|
+
from .inline import (
|
|
49
|
+
Str,
|
|
50
|
+
SoftBreak,
|
|
51
|
+
HardBreak,
|
|
52
|
+
NonBreakingSpace,
|
|
53
|
+
Symb,
|
|
54
|
+
Verbatim,
|
|
55
|
+
RawInline,
|
|
56
|
+
InlineMath,
|
|
57
|
+
DisplayMath,
|
|
58
|
+
Url,
|
|
59
|
+
Email,
|
|
60
|
+
FootnoteReference,
|
|
61
|
+
SmartPunctuation,
|
|
62
|
+
SmartPunctType,
|
|
63
|
+
Emph,
|
|
64
|
+
Strong,
|
|
65
|
+
Link,
|
|
66
|
+
Image,
|
|
67
|
+
Span,
|
|
68
|
+
Mark,
|
|
69
|
+
Superscript,
|
|
70
|
+
Subscript,
|
|
71
|
+
Insert,
|
|
72
|
+
Delete,
|
|
73
|
+
DoubleQuoted,
|
|
74
|
+
SingleQuoted,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
C = TypeVar('C')
|
|
79
|
+
R = TypeVar('R')
|
|
80
|
+
|
|
81
|
+
class Visitor[C, R](ABC):
|
|
82
|
+
@abstractmethod
|
|
83
|
+
def doc(self, node: Doc, context: C) -> R:
|
|
84
|
+
pass
|
|
85
|
+
|
|
86
|
+
@abstractmethod
|
|
87
|
+
def para(self, node: Para, context: C) -> R:
|
|
88
|
+
pass
|
|
89
|
+
|
|
90
|
+
@abstractmethod
|
|
91
|
+
def heading(self, node: Heading, context: C) -> R:
|
|
92
|
+
pass
|
|
93
|
+
|
|
94
|
+
@abstractmethod
|
|
95
|
+
def thematic_break(self, node: ThematicBreak, context: C) -> R:
|
|
96
|
+
pass
|
|
97
|
+
|
|
98
|
+
@abstractmethod
|
|
99
|
+
def section(self, node: Section, context: C) -> R:
|
|
100
|
+
pass
|
|
101
|
+
|
|
102
|
+
@abstractmethod
|
|
103
|
+
def div(self, node: Div, context: C) -> R:
|
|
104
|
+
pass
|
|
105
|
+
|
|
106
|
+
@abstractmethod
|
|
107
|
+
def code_block(self, node: CodeBlock, context: C) -> R:
|
|
108
|
+
pass
|
|
109
|
+
|
|
110
|
+
@abstractmethod
|
|
111
|
+
def raw_block(self, node: RawBlock, context: C) -> R:
|
|
112
|
+
pass
|
|
113
|
+
|
|
114
|
+
@abstractmethod
|
|
115
|
+
def block_quote(self, node: BlockQuote, context: C) -> R:
|
|
116
|
+
pass
|
|
117
|
+
|
|
118
|
+
@abstractmethod
|
|
119
|
+
def ordered_list(self, node: OrderedList, context: C) -> R:
|
|
120
|
+
pass
|
|
121
|
+
|
|
122
|
+
@abstractmethod
|
|
123
|
+
def bullet_list(self, node: BulletList, context: C) -> R:
|
|
124
|
+
pass
|
|
125
|
+
|
|
126
|
+
@abstractmethod
|
|
127
|
+
def task_list(self, node: TaskList, context: C) -> R:
|
|
128
|
+
pass
|
|
129
|
+
|
|
130
|
+
@abstractmethod
|
|
131
|
+
def definition_list(self, node: DefinitionList, context: C) -> R:
|
|
132
|
+
pass
|
|
133
|
+
|
|
134
|
+
@abstractmethod
|
|
135
|
+
def table(self, node: Table, context: C) -> R:
|
|
136
|
+
pass
|
|
137
|
+
|
|
138
|
+
@abstractmethod
|
|
139
|
+
def str(self, node: Str, context: C) -> R:
|
|
140
|
+
pass
|
|
141
|
+
|
|
142
|
+
@abstractmethod
|
|
143
|
+
def soft_break(self, node: SoftBreak, context: C) -> R:
|
|
144
|
+
pass
|
|
145
|
+
|
|
146
|
+
@abstractmethod
|
|
147
|
+
def hard_break(self, node: HardBreak, context: C) -> R:
|
|
148
|
+
pass
|
|
149
|
+
|
|
150
|
+
@abstractmethod
|
|
151
|
+
def non_breaking_space(self, node: NonBreakingSpace, context: C) -> R:
|
|
152
|
+
pass
|
|
153
|
+
|
|
154
|
+
@abstractmethod
|
|
155
|
+
def symb(self, node: Symb, context: C) -> R:
|
|
156
|
+
pass
|
|
157
|
+
|
|
158
|
+
@abstractmethod
|
|
159
|
+
def verbatim(self, node: Verbatim, context: C) -> R:
|
|
160
|
+
pass
|
|
161
|
+
|
|
162
|
+
@abstractmethod
|
|
163
|
+
def raw_inline(self, node: RawInline, context: C) -> R:
|
|
164
|
+
pass
|
|
165
|
+
|
|
166
|
+
@abstractmethod
|
|
167
|
+
def display_math(self, node: DisplayMath, context: C) -> R:
|
|
168
|
+
pass
|
|
169
|
+
|
|
170
|
+
@abstractmethod
|
|
171
|
+
def url(self, node: Url, context: C) -> R:
|
|
172
|
+
pass
|
|
173
|
+
|
|
174
|
+
@abstractmethod
|
|
175
|
+
def email(self, node: Email, context: C) -> R:
|
|
176
|
+
pass
|
|
177
|
+
|
|
178
|
+
@abstractmethod
|
|
179
|
+
def footnote_reference(self, node: FootnoteReference, context: C) -> R:
|
|
180
|
+
pass
|
|
181
|
+
|
|
182
|
+
@abstractmethod
|
|
183
|
+
def smart_punctuation(self, node: SmartPunctuation, context: C) -> R:
|
|
184
|
+
pass
|
|
185
|
+
|
|
186
|
+
@abstractmethod
|
|
187
|
+
def emph(self, node: Emph, context: C) -> R:
|
|
188
|
+
pass
|
|
189
|
+
|
|
190
|
+
@abstractmethod
|
|
191
|
+
def strong(self, node: Strong, context: C) -> R:
|
|
192
|
+
pass
|
|
193
|
+
|
|
194
|
+
@abstractmethod
|
|
195
|
+
def link(self, node: Link, context: C) -> R:
|
|
196
|
+
pass
|
|
197
|
+
|
|
198
|
+
@abstractmethod
|
|
199
|
+
def image(self, node: Image, context: C) -> R:
|
|
200
|
+
pass
|
|
201
|
+
|
|
202
|
+
@abstractmethod
|
|
203
|
+
def span(self, node: Span, context: C) -> R:
|
|
204
|
+
pass
|
|
205
|
+
|
|
206
|
+
@abstractmethod
|
|
207
|
+
def mark(self, node: Mark, context: C) -> R:
|
|
208
|
+
pass
|
|
209
|
+
|
|
210
|
+
@abstractmethod
|
|
211
|
+
def superscript(self, node: Superscript, context: C) -> R:
|
|
212
|
+
pass
|
|
213
|
+
|
|
214
|
+
@abstractmethod
|
|
215
|
+
def subscript(self, node: Subscript, context: C) -> R:
|
|
216
|
+
pass
|
|
217
|
+
|
|
218
|
+
@abstractmethod
|
|
219
|
+
def insert(self, node: Insert, context: C) -> R:
|
|
220
|
+
pass
|
|
221
|
+
|
|
222
|
+
@abstractmethod
|
|
223
|
+
def delete(self, node: Delete, context: C) -> R:
|
|
224
|
+
pass
|
|
225
|
+
|
|
226
|
+
@abstractmethod
|
|
227
|
+
def double_quoted(self, node: DoubleQuoted, context: C) -> R:
|
|
228
|
+
pass
|
|
229
|
+
|
|
230
|
+
@abstractmethod
|
|
231
|
+
def single_quoted(self, node: SingleQuoted, context: C) -> R:
|
|
232
|
+
pass
|
|
233
|
+
|
|
234
|
+
@abstractmethod
|
|
235
|
+
def list_item(self, node: ListItem, context: C) -> R:
|
|
236
|
+
pass
|
|
237
|
+
|
|
238
|
+
@abstractmethod
|
|
239
|
+
def task_list_item(self, node: TaskListItem, context: C) -> R:
|
|
240
|
+
pass
|
|
241
|
+
|
|
242
|
+
@abstractmethod
|
|
243
|
+
def definition_list_item(self, node: DefinitionListItem, context: C) -> R:
|
|
244
|
+
pass
|
|
245
|
+
|
|
246
|
+
@abstractmethod
|
|
247
|
+
def term(self, node: Term, context: C) -> R:
|
|
248
|
+
pass
|
|
249
|
+
|
|
250
|
+
@abstractmethod
|
|
251
|
+
def definition(self, node: Definition, context: C) -> R:
|
|
252
|
+
pass
|
|
253
|
+
|
|
254
|
+
@abstractmethod
|
|
255
|
+
def row(self, node: Row, context: C) -> R:
|
|
256
|
+
pass
|
|
257
|
+
|
|
258
|
+
@abstractmethod
|
|
259
|
+
def cell(self, node: Cell, context: C) -> R:
|
|
260
|
+
pass
|
|
261
|
+
|
|
262
|
+
@abstractmethod
|
|
263
|
+
def caption(self, node: Caption, context: C) -> R:
|
|
264
|
+
pass
|
|
265
|
+
|
|
266
|
+
@abstractmethod
|
|
267
|
+
def footnote(self, node: Footnote, context: C) -> R:
|
|
268
|
+
pass
|
|
269
|
+
|
|
270
|
+
@abstractmethod
|
|
271
|
+
def reference(self, node: Reference, context: C) -> R:
|
|
272
|
+
pass
|
|
273
|
+
|
|
274
|
+
def is_block(node: AstNode) -> TypeGuard[BlockNode]:
|
|
275
|
+
match node:
|
|
276
|
+
case (
|
|
277
|
+
Para()
|
|
278
|
+
| Heading()
|
|
279
|
+
| BlockQuote()
|
|
280
|
+
| ThematicBreak()
|
|
281
|
+
| Section()
|
|
282
|
+
| Div()
|
|
283
|
+
| CodeBlock()
|
|
284
|
+
| RawBlock()
|
|
285
|
+
| BulletList()
|
|
286
|
+
| OrderedList()
|
|
287
|
+
| TaskList()
|
|
288
|
+
| DefinitionList()
|
|
289
|
+
| Table()
|
|
290
|
+
| Reference()
|
|
291
|
+
| Footnote()
|
|
292
|
+
):
|
|
293
|
+
return True
|
|
294
|
+
|
|
295
|
+
case _:
|
|
296
|
+
return False
|
|
297
|
+
|
|
298
|
+
def is_inline(node: AstNode) -> bool:
|
|
299
|
+
match node:
|
|
300
|
+
case (
|
|
301
|
+
Str()
|
|
302
|
+
| SoftBreak()
|
|
303
|
+
| HardBreak()
|
|
304
|
+
| NonBreakingSpace()
|
|
305
|
+
| Symb()
|
|
306
|
+
| Verbatim()
|
|
307
|
+
| RawInline()
|
|
308
|
+
| InlineMath()
|
|
309
|
+
| DisplayMath()
|
|
310
|
+
| Url()
|
|
311
|
+
| Email()
|
|
312
|
+
| FootnoteReference()
|
|
313
|
+
| SmartPunctuation()
|
|
314
|
+
| Emph()
|
|
315
|
+
| Strong()
|
|
316
|
+
| Link()
|
|
317
|
+
| Image()
|
|
318
|
+
| Span()
|
|
319
|
+
| Mark()
|
|
320
|
+
| Superscript()
|
|
321
|
+
| Subscript()
|
|
322
|
+
| Insert()
|
|
323
|
+
| Delete()
|
|
324
|
+
| DoubleQuoted()
|
|
325
|
+
| SingleQuoted()
|
|
326
|
+
):
|
|
327
|
+
return True
|
|
328
|
+
|
|
329
|
+
case _:
|
|
330
|
+
return False
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def is_row(node: Row | Caption) -> TypeGuard[Row]:
|
|
334
|
+
match node:
|
|
335
|
+
case Row(head=_):
|
|
336
|
+
return True
|
|
337
|
+
case _:
|
|
338
|
+
return False
|
|
339
|
+
|
|
340
|
+
def is_caption(node: Row | Caption) -> TypeGuard[Caption]:
|
|
341
|
+
match node:
|
|
342
|
+
case Row(head=_):
|
|
343
|
+
return False
|
|
344
|
+
case _:
|
|
345
|
+
return True
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
import copy
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Dict, Generic, List, Optional, TypeVar
|
|
5
|
+
|
|
6
|
+
from ..common import Pos
|
|
7
|
+
|
|
8
|
+
type Attributes = Dict[str, str]
|
|
9
|
+
|
|
10
|
+
def merge_attributes(
|
|
11
|
+
target: Optional[Attributes],
|
|
12
|
+
source: Attributes
|
|
13
|
+
) -> Attributes:
|
|
14
|
+
if not target:
|
|
15
|
+
return source.copy()
|
|
16
|
+
|
|
17
|
+
result = target.copy()
|
|
18
|
+
|
|
19
|
+
for key, value in source.items():
|
|
20
|
+
if key == 'class' and key in result:
|
|
21
|
+
result[key] = f'{result[key]} {value}'
|
|
22
|
+
else:
|
|
23
|
+
result[key] = value
|
|
24
|
+
|
|
25
|
+
return result
|
|
26
|
+
|
|
27
|
+
@dataclass(kw_only=True)
|
|
28
|
+
class AstNode(ABC):
|
|
29
|
+
attributes: Attributes | None = None
|
|
30
|
+
pos: Pos | None = None
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
@abstractmethod
|
|
34
|
+
def tag(self) -> str:
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
def merge_attributes(self, other: Attributes):
|
|
38
|
+
self.attributes = merge_attributes(self.attributes, other)
|
|
39
|
+
|
|
40
|
+
T = TypeVar("T")
|
|
41
|
+
|
|
42
|
+
@dataclass(kw_only=True)
|
|
43
|
+
class HasChildren(Generic[T], ABC):
|
|
44
|
+
children: List[T]
|
|
45
|
+
|
|
46
|
+
@dataclass(kw_only=True)
|
|
47
|
+
class HasText(ABC):
|
|
48
|
+
text: str
|
|
49
|
+
|
|
50
|
+
@dataclass(kw_only=True)
|
|
51
|
+
class InlineNode(AstNode):
|
|
52
|
+
pass
|
|
53
|
+
|
|
54
|
+
@dataclass(kw_only=True)
|
|
55
|
+
class BlockNode(AstNode, ABC):
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
# 区分常规内容块(Para, Heading 等)与 定义类块(Footnote, Reference)
|
|
59
|
+
@dataclass(kw_only=True)
|
|
60
|
+
class ContentBlock(BlockNode, ABC):
|
|
61
|
+
"""能够嵌入普通 Container(如 BlockQuote, Div, Doc)的内容块"""
|
|
62
|
+
pass
|
|
63
|
+
|
|
64
|
+
@dataclass(kw_only=True)
|
|
65
|
+
class DefinitionBlock(BlockNode, ABC):
|
|
66
|
+
"""元数据/定义类块(如 Footnote, Reference)"""
|
|
67
|
+
pass
|