yapyon 0.1.0__py3-none-any.whl
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.
- yapyon/__init__.py +58 -0
- yapyon/__main__.py +39 -0
- yapyon/_cli.py +46 -0
- yapyon/_xid.py +1449 -0
- yapyon/emitter.py +225 -0
- yapyon/lexer.py +1131 -0
- yapyon/loader.py +185 -0
- yapyon/multimap.py +165 -0
- yapyon/parser.py +529 -0
- yapyon/record.py +59 -0
- yapyon/resolver.py +628 -0
- yapyon/serializers.py +248 -0
- yapyon/template.py +161 -0
- yapyon-0.1.0.dist-info/METADATA +306 -0
- yapyon-0.1.0.dist-info/RECORD +18 -0
- yapyon-0.1.0.dist-info/WHEEL +5 -0
- yapyon-0.1.0.dist-info/licenses/LICENSE +674 -0
- yapyon-0.1.0.dist-info/top_level.txt +1 -0
yapyon/__init__.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""yapyon — YAMLちゃうで。やぴょんやぴょん。
|
|
2
|
+
|
|
3
|
+
Python-style typed literals with YAML-like block structure.
|
|
4
|
+
Home: https://github.com/doctorjei/yapyon
|
|
5
|
+
|
|
6
|
+
>>> import yapyon
|
|
7
|
+
>>> yapyon.loads('name: "gw"\\nbanner: y"{name} v1"\\n')
|
|
8
|
+
{'name': 'gw', 'banner': 'gw v1'}
|
|
9
|
+
|
|
10
|
+
**Conformance (SPEC §12.5): this implementation provides the full form**,
|
|
11
|
+
and therefore also loads yapyon records — `loads_record`, `load_record` and
|
|
12
|
+
`is_record` are the record surface.
|
|
13
|
+
|
|
14
|
+
**Identifier tables (GRAMMAR §G4.1): pinned to Unicode 14.0.0.** yapyon
|
|
15
|
+
ships its own table rather than asking `str.isidentifier()`, whose answer
|
|
16
|
+
depends on the interpreter, so a key means the same thing on every install.
|
|
17
|
+
`UNICODE_VERSION` is that pinned version.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from ._xid import UNICODE_VERSION as _XID_UNICODE_VERSION
|
|
21
|
+
from .lexer import Lexer, Token, tokenize, AkanError, Yakamashiwa
|
|
22
|
+
from .parser import (Mapping, MultiMap, Node, Pair, Parser, Scalar, Sequence,
|
|
23
|
+
ResolvedTemplate, YString, dump, parse,
|
|
24
|
+
parse_tokens)
|
|
25
|
+
from .resolver import Resolver, resolve
|
|
26
|
+
from .multimap import Entry, KeyView, OrderedMultimap
|
|
27
|
+
from .template import Hole, Template
|
|
28
|
+
from .loader import build, is_record, load, load_record, loads, loads_record
|
|
29
|
+
from .emitter import emit
|
|
30
|
+
|
|
31
|
+
__version__ = "0.1.0"
|
|
32
|
+
|
|
33
|
+
#: GRAMMAR §G4.1 asks every implementation to document where its identifier
|
|
34
|
+
#: tables come from. yapyon's are generated from this Unicode version by
|
|
35
|
+
#: tools/gen_xid_table.py, and do not vary with the interpreter.
|
|
36
|
+
UNICODE_VERSION = _XID_UNICODE_VERSION
|
|
37
|
+
|
|
38
|
+
__all__ = [
|
|
39
|
+
# what this install's identifier tables are (GRAMMAR §G4.1)
|
|
40
|
+
"UNICODE_VERSION",
|
|
41
|
+
# the everyday API
|
|
42
|
+
"load", "loads", "build", "Template", "Hole", "OrderedMultimap",
|
|
43
|
+
# records — the fixed, literal-only form
|
|
44
|
+
"load_record", "loads_record", "is_record",
|
|
45
|
+
# a record AST back to canonical yapyon source (python -m yapyon.record)
|
|
46
|
+
"emit",
|
|
47
|
+
# diagnostics
|
|
48
|
+
"AkanError", "Yakamashiwa",
|
|
49
|
+
# the stages, for tools that want them
|
|
50
|
+
"Lexer", "Token", "tokenize",
|
|
51
|
+
"Parser", "parse", "parse_tokens", "dump",
|
|
52
|
+
"Resolver", "resolve",
|
|
53
|
+
# AST
|
|
54
|
+
"Node", "Scalar", "YString", "ResolvedTemplate", "Pair", "Mapping",
|
|
55
|
+
"Sequence", "MultiMap",
|
|
56
|
+
# multimap internals
|
|
57
|
+
"Entry", "KeyView",
|
|
58
|
+
]
|
yapyon/__main__.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""`python -m yapyon <stage> [FILE]` — the stages, from the command line.
|
|
2
|
+
|
|
3
|
+
python -m yapyon lexer FILE # token dump
|
|
4
|
+
python -m yapyon parser FILE # AST dump
|
|
5
|
+
python -m yapyon record FILE # a record, normalized back to yapyon
|
|
6
|
+
|
|
7
|
+
Prefer this spelling over `python -m yapyon.lexer`. Both work, but the
|
|
8
|
+
per-module form re-executes a module the package has already imported, which
|
|
9
|
+
makes CPython print a `RuntimeWarning` onto the very stream the diagnostics
|
|
10
|
+
use — and, before this indirection existed, gave the stage a second copy of
|
|
11
|
+
`AkanError` that `_cli` could not catch.
|
|
12
|
+
|
|
13
|
+
Diagnostics go to stderr, output to stdout, and a shiran does not change the
|
|
14
|
+
exit status; `_cli` holds those rules.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import sys
|
|
20
|
+
|
|
21
|
+
STAGES = ("lexer", "parser", "record")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def main(argv: list[str]) -> int:
|
|
25
|
+
if len(argv) < 2 or argv[1] in ("-h", "--help"):
|
|
26
|
+
print(__doc__.strip(), file=sys.stderr)
|
|
27
|
+
return 0 if len(argv) > 1 else 2
|
|
28
|
+
stage, rest = argv[1], argv[2:]
|
|
29
|
+
if stage not in STAGES:
|
|
30
|
+
print(f"akan: no such stage {stage!r}; try one of "
|
|
31
|
+
f"{', '.join(STAGES)}", file=sys.stderr)
|
|
32
|
+
return 2
|
|
33
|
+
from importlib import import_module
|
|
34
|
+
module = import_module(f".{stage}", __package__)
|
|
35
|
+
return module.main([f"yapyon {stage}", *rest])
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
if __name__ == "__main__":
|
|
39
|
+
sys.exit(main(sys.argv))
|
yapyon/_cli.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Shared plumbing for the `python -m yapyon.*` entry points.
|
|
2
|
+
|
|
3
|
+
Three rules live here, once, because four copies of them would drift:
|
|
4
|
+
|
|
5
|
+
* **Shirans go to stderr and are shown by default.** The library keeps them
|
|
6
|
+
behind `warn=` — a caller may not want them — but a person at a terminal
|
|
7
|
+
asked to see what the tool thinks, so a CLI that swallowed them would be
|
|
8
|
+
hiding the only warning rung the format has.
|
|
9
|
+
* **A shiran never changes the exit status.** It is legal-but-suspicious, not
|
|
10
|
+
an error; a pipeline consuming stdout must not break because of one.
|
|
11
|
+
* **Diagnostics never go to stdout.** `python -m yapyon.record` writes yapyon
|
|
12
|
+
there and its output is meant to be redirected, so an akan or a shiran on
|
|
13
|
+
stdout would corrupt the file it is being written into.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import sys
|
|
19
|
+
|
|
20
|
+
from .lexer import AkanError
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def source_from(argv: list[str]) -> tuple[str, str | None]:
|
|
24
|
+
"""The file named on the command line, or stdin. The name is returned so
|
|
25
|
+
diagnostics can carry it — `f.ypy:3:9` rather than `line 3, col 9`."""
|
|
26
|
+
name = argv[1] if len(argv) > 1 else None
|
|
27
|
+
text = open(name, encoding="utf-8").read() if name else sys.stdin.read()
|
|
28
|
+
return text, name
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def run(argv: list[str], work) -> int:
|
|
32
|
+
"""`work(text, source) -> (stdout_text, warnings)`, wrapped in the rules.
|
|
33
|
+
|
|
34
|
+
Warnings are written before the output: they are diagnostics *about* the
|
|
35
|
+
input, and on a terminal a long dump would otherwise scroll them away.
|
|
36
|
+
"""
|
|
37
|
+
text, name = source_from(argv)
|
|
38
|
+
try:
|
|
39
|
+
out, warnings = work(text, name)
|
|
40
|
+
except AkanError as exc:
|
|
41
|
+
print(exc, file=sys.stderr)
|
|
42
|
+
return 1
|
|
43
|
+
for warning in warnings:
|
|
44
|
+
print(warning, file=sys.stderr)
|
|
45
|
+
sys.stdout.write(out)
|
|
46
|
+
return 0
|