usfmtc 0.2__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.
- usfmtc/__init__.py +379 -0
- usfmtc/diagrams.py +247 -0
- usfmtc/extension.py +79 -0
- usfmtc/grammar.py +461 -0
- usfmtc/parser.py +300 -0
- usfmtc/railroad.py +1320 -0
- usfmtc/reference.py +302 -0
- usfmtc/rnc2rng/__init__.py +13 -0
- usfmtc/rnc2rng/parser.py +741 -0
- usfmtc/rnc2rng/rnctree.py +17 -0
- usfmtc/rnc2rng/serializer.py +255 -0
- usfmtc/sfmparser.py +345 -0
- usfmtc/usfmgenerate.py +156 -0
- usfmtc/usfmparser.py +590 -0
- usfmtc/usjproc.py +88 -0
- usfmtc/usx.rng +2182 -0
- usfmtc/usxgrammar.py +36 -0
- usfmtc/usxmodel.py +520 -0
- usfmtc/usxparser.py +568 -0
- usfmtc/xmlutils.py +139 -0
- usfmtc-0.2.dist-info/LICENSE.md +20 -0
- usfmtc-0.2.dist-info/METADATA +61 -0
- usfmtc-0.2.dist-info/RECORD +26 -0
- usfmtc-0.2.dist-info/WHEEL +5 -0
- usfmtc-0.2.dist-info/entry_points.txt +2 -0
- usfmtc-0.2.dist-info/top_level.txt +1 -0
usfmtc/reference.py
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
from typing import Optional, List, Tuple
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
import re
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class MarkerRef:
|
|
9
|
+
mrkr: str
|
|
10
|
+
index: Optional[int] = None
|
|
11
|
+
word: Optional[int] = None
|
|
12
|
+
char: Optional[str] = None
|
|
13
|
+
|
|
14
|
+
def __eq__(self, context:'MarkerRef'):
|
|
15
|
+
if self.mrkr != context.mrkr:
|
|
16
|
+
return False
|
|
17
|
+
elif self.index != context.index:
|
|
18
|
+
return False
|
|
19
|
+
elif self.word != context.word:
|
|
20
|
+
return False
|
|
21
|
+
elif self.char != context.char:
|
|
22
|
+
return False
|
|
23
|
+
|
|
24
|
+
def __str__(self):
|
|
25
|
+
return self.str(force=True)
|
|
26
|
+
|
|
27
|
+
def str(self, context: Optional['MarkerRef']=None, force: bool=False):
|
|
28
|
+
res = []
|
|
29
|
+
if force or context is None or context.mrkr != self.mrkr or context.index != self.index:
|
|
30
|
+
res.append("!"+self.mrkr)
|
|
31
|
+
if self.index is not None:
|
|
32
|
+
res.append("[" + str(self.index) + "]")
|
|
33
|
+
if self.word is not None and (force or context is None or context.word != self.word):
|
|
34
|
+
if len(res):
|
|
35
|
+
res.append("!")
|
|
36
|
+
res.append(str(self.word))
|
|
37
|
+
if self.char is not None and (force or context is None or context.char != self.char):
|
|
38
|
+
if len(res):
|
|
39
|
+
res.append("+")
|
|
40
|
+
res.append(str(self.char))
|
|
41
|
+
return "".join(res)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
_regexes = {
|
|
45
|
+
"book": r"""(?P<transid>(?:[a-z0-9_-]*[+])*)
|
|
46
|
+
(?P<book>(?:[A-Z][A-Z0-9][A-Z0-9]|[0-9](?:[A-Z][A-Z]|[0-9][A-Z]|[A-Z][0-9])))
|
|
47
|
+
\s*{chap}""",
|
|
48
|
+
"id": r"(?:[a-z][a-z0-9_-]*[a-z0-9]?)",
|
|
49
|
+
"charref": r"(?:(?:[0-9]|end)[+]?)",
|
|
50
|
+
"wordrefanon": r"",
|
|
51
|
+
"mrkref": r"(?:\!{id}(?:\[[0-9]\])?(?:\!(?:[0-9|end)(?:[+]{charref})?)?)",
|
|
52
|
+
"wordrefonly": r"(?P<word>[0-9]|end)(?P<char>[+]{charref})?",
|
|
53
|
+
"wordref1": r"""(?:(?: \!(?P<word1>[0-9]|end)(?:[+](?P<char1>{charref}))?
|
|
54
|
+
|(?P<mrk1>{mrkref}))
|
|
55
|
+
(?P<mrkn1>{mrkref}*))""",
|
|
56
|
+
"wordref2": r"""(?:(?: \!(?P<word2>[0-9]|end)(?:[+](?P<char2>{charref}))?
|
|
57
|
+
|(?P<mrk2>{mrkref}))
|
|
58
|
+
(?P<mrkn2>{mrkref}*))""",
|
|
59
|
+
"verse1": r"(?P<verse1>[0-9]+)(?P<subv1>[a-z]?|end)",
|
|
60
|
+
"verse2": r"(?P<verse2>[0-9]+)(?P<subv2>[a-z]?|end)",
|
|
61
|
+
"chapter": r"(?:(?P<chap>[0-9]+))",
|
|
62
|
+
"chap": r"{chapter}(?:[:.]{verse1})?{wordref1}?",
|
|
63
|
+
"context": r"""(?:{chap}
|
|
64
|
+
| {verse2}{wordref2}? | {wordrefonly}(?P<mrkn3>{mrkref}*)
|
|
65
|
+
| (?P<char3>{charref})(?P<mrkn4>{mrkref}*))(?=[-;,\s]|$)"""
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
regexes = _regexes
|
|
69
|
+
for i in range(3):
|
|
70
|
+
regexes = {k: v.format(**regexes) for k, v in regexes.items()}
|
|
71
|
+
|
|
72
|
+
def intend(s: str) -> Optional[int]:
|
|
73
|
+
if not s:
|
|
74
|
+
return None
|
|
75
|
+
elif s == "end":
|
|
76
|
+
return -1
|
|
77
|
+
return int(s)
|
|
78
|
+
|
|
79
|
+
def strend(s: int) -> str:
|
|
80
|
+
if s == -1:
|
|
81
|
+
return "end"
|
|
82
|
+
else:
|
|
83
|
+
return str(s)
|
|
84
|
+
|
|
85
|
+
def parse_wordref(s: str) -> Tuple[int, Optional[str]]:
|
|
86
|
+
b = s.split("+", 1)
|
|
87
|
+
word = int(b[0])
|
|
88
|
+
char = "+" + b[1] if len(b) > 1 else None
|
|
89
|
+
return (word, char)
|
|
90
|
+
|
|
91
|
+
_reindex = re.compile(r"([0-9a-z_-]+)(?:\[([0-9]+\]))?")
|
|
92
|
+
def asmarkers(s: str, t: str) -> List[MarkerRef]:
|
|
93
|
+
res = []
|
|
94
|
+
if s is None or not len(s):
|
|
95
|
+
return res
|
|
96
|
+
b = s.split("!")
|
|
97
|
+
b += t.split("!")
|
|
98
|
+
i = 0
|
|
99
|
+
while i < len(b):
|
|
100
|
+
if not len(b[i]):
|
|
101
|
+
i += 1
|
|
102
|
+
continue
|
|
103
|
+
if not (m := _reindex.match(b[i])):
|
|
104
|
+
raise SyntaxError("Badly formed marker reference {} in {}".format(b, s))
|
|
105
|
+
mrkr = m.group(1)
|
|
106
|
+
ind = int(m.group(2)) if m.group(2) else None
|
|
107
|
+
if i < len(b) - 1 and len(b[i+1]) and b[i+1][0] in "0123456789":
|
|
108
|
+
word, charref = parse_wordref(b[i+1])
|
|
109
|
+
i += 1
|
|
110
|
+
else:
|
|
111
|
+
word = None
|
|
112
|
+
charref = None
|
|
113
|
+
res.append(MarkerRef(mrkr, index=ind, word=word, char=charref))
|
|
114
|
+
i += 1
|
|
115
|
+
return res if len(res) else None
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class Ref:
|
|
119
|
+
rebook = re.compile(regexes["book"], re.X)
|
|
120
|
+
recontext = re.compile(regexes["context"], re.X)
|
|
121
|
+
|
|
122
|
+
def __init__(self, string: Optional[str] = None,
|
|
123
|
+
context: Optional['Ref'] = None, start: int = 0, **kw):
|
|
124
|
+
parmlist = ('product', 'book', 'chapter', 'verse', 'subverse', 'word', 'char', 'mrkrs')
|
|
125
|
+
if string is not None:
|
|
126
|
+
self.parse(string, context=context, start=start)
|
|
127
|
+
elif context is not None:
|
|
128
|
+
for a in parmlist:
|
|
129
|
+
setattr(self, a, kw.get(a, getattr(context, a, None)))
|
|
130
|
+
else:
|
|
131
|
+
for a in parmlist:
|
|
132
|
+
setattr(self, a, kw.get(a, None))
|
|
133
|
+
|
|
134
|
+
def parse(self, s: str, context: Optional['Ref'] = None, start: int = 0):
|
|
135
|
+
p = {}
|
|
136
|
+
if m := self.rebook.match(s, pos=start):
|
|
137
|
+
p['product'] = m.group('transid') or None
|
|
138
|
+
p['book'] = m.group('book')
|
|
139
|
+
p['chapter'] = int(m.group('chap')) if m.group('chap') else None
|
|
140
|
+
p['verse'] = intend(m.group('verse1'))
|
|
141
|
+
p['subverse'] = m.group('subv1') or None
|
|
142
|
+
p['word'] = intend(m.group('word1'))
|
|
143
|
+
p['char'] = intend(m.group('char1'))
|
|
144
|
+
p['mrkrs'] = asmarkers(m.group("mrk1"), m.group("mrkn1"))
|
|
145
|
+
elif not (m:= self.recontext.match(s, pos=start)):
|
|
146
|
+
raise SyntaxError("Cannot parse {}".format(s))
|
|
147
|
+
elif m.group('verse1'): # we know we have a chapter and verse
|
|
148
|
+
p['chapter'] = int(m.group('chap'))
|
|
149
|
+
p['verse'] = intend(m.group('verse1'))
|
|
150
|
+
p['subverse'] = m.group('subv1') or None
|
|
151
|
+
p['word'] = intend(m.group('word1'))
|
|
152
|
+
p['char'] = intend(m.group('char1'))
|
|
153
|
+
p['mrkrs'] = asmarkers(m.group("mrk1"), m.group("mrkn1"))
|
|
154
|
+
elif m.group('subv2'):
|
|
155
|
+
p['verse'] = intend(m.group('verse2'))
|
|
156
|
+
p['subverse'] = m.group('subv2') or None
|
|
157
|
+
p['word'] = intend(m.group('word2'))
|
|
158
|
+
p['char'] = intend(m.group('char2'))
|
|
159
|
+
p['mrkrs'] = asmarkers(m.group("mrk2"), m.group("mrkn2"))
|
|
160
|
+
elif m.group('word'):
|
|
161
|
+
p['word'] = intend(m.group('word'))
|
|
162
|
+
p['char'] = intend(m.group('char'))
|
|
163
|
+
p['mrkrs'] = asmarkers("", m.group('mrkn3'))
|
|
164
|
+
elif m.group('char3'):
|
|
165
|
+
p['char'] = intend(m.group('char3'))
|
|
166
|
+
p['mrkrs'] = asmarkers("", m.group('mrkn4'))
|
|
167
|
+
elif not m.group('chap') and m.group('word1'):
|
|
168
|
+
p['word'] = intend(m.group('word1'))
|
|
169
|
+
p['char'] = intend(m.group('char1'))
|
|
170
|
+
p['mrkrs'] = asmarkers(m.group('mrk1'), m.group('mrkn1'))
|
|
171
|
+
elif context is not None and context.char:
|
|
172
|
+
p['char'] = intend(m.group('chap'))
|
|
173
|
+
elif context is not None and context.word:
|
|
174
|
+
p['word'] = intend(m.group('chap'))
|
|
175
|
+
p['char'] = intend(m.group('char1'))
|
|
176
|
+
elif context is not None and context.verse:
|
|
177
|
+
p['verse'] = intend(m.group('chap'))
|
|
178
|
+
p['word'] = intend(m.group('word1'))
|
|
179
|
+
p['char'] = intend(m.group('char1'))
|
|
180
|
+
else:
|
|
181
|
+
p['chapter'] = int(m.group('chap'))
|
|
182
|
+
p['word'] = intend(m.group('word1'))
|
|
183
|
+
p['char'] = intend(m.group('char1'))
|
|
184
|
+
self.strend = m.end(0)
|
|
185
|
+
self.__init__(None, context, **p)
|
|
186
|
+
|
|
187
|
+
def __str__(self):
|
|
188
|
+
return self.str()
|
|
189
|
+
|
|
190
|
+
def str(self, context: Optional['Ref'] = None, force: bool = False):
|
|
191
|
+
if context is None:
|
|
192
|
+
context = Ref()
|
|
193
|
+
res = []
|
|
194
|
+
sep = ''
|
|
195
|
+
if context.product != self.product:
|
|
196
|
+
res.append(self.product)
|
|
197
|
+
res.append('.')
|
|
198
|
+
force = True
|
|
199
|
+
if force or context.book != self.book:
|
|
200
|
+
res.append(self.book)
|
|
201
|
+
res.append(' ')
|
|
202
|
+
force = True
|
|
203
|
+
if force or context.chapter != self.chapter:
|
|
204
|
+
res.append(str(self.chapter))
|
|
205
|
+
sep = ':'
|
|
206
|
+
if self.verse is not None and (force or context.verse != self.verse):
|
|
207
|
+
if len(res):
|
|
208
|
+
res.append(sep)
|
|
209
|
+
res.append(strend(self.verse))
|
|
210
|
+
res.append(self.subverse or "")
|
|
211
|
+
sep = "!"
|
|
212
|
+
if self.word is not None and (force or context.word != self.word):
|
|
213
|
+
if len(res):
|
|
214
|
+
res.append(sep)
|
|
215
|
+
res.append(strend(self.word))
|
|
216
|
+
sep = "+"
|
|
217
|
+
if self.char is not None and (force or context.char != self.char):
|
|
218
|
+
if len(res):
|
|
219
|
+
res.append(sep)
|
|
220
|
+
res.append(strend(self.char))
|
|
221
|
+
sep = "!"
|
|
222
|
+
if self.mrkrs is not None and len(self.mrkrs):
|
|
223
|
+
forcemkr = False
|
|
224
|
+
for i, m in enumerate(self.mrkrs):
|
|
225
|
+
if not forcemkr and context is not None and i < len(context.mrkrs):
|
|
226
|
+
if context.mrkrs[i] != m:
|
|
227
|
+
res.append(m.str(context=contex.mrkrs[i], force=force or len(res)))
|
|
228
|
+
forcemkr = True
|
|
229
|
+
elif force or forcemkr:
|
|
230
|
+
res.append(m.str(force=True))
|
|
231
|
+
return "".join(res)
|
|
232
|
+
|
|
233
|
+
@property
|
|
234
|
+
def first(self):
|
|
235
|
+
return self
|
|
236
|
+
|
|
237
|
+
@property
|
|
238
|
+
def last(self):
|
|
239
|
+
return self
|
|
240
|
+
|
|
241
|
+
class RefRange:
|
|
242
|
+
def __init__(self, first: Optional[Ref] = None, last: Optional[Ref] = None):
|
|
243
|
+
self.first = first
|
|
244
|
+
self.last = last
|
|
245
|
+
|
|
246
|
+
def str(self, context: Optional[Ref] = None, force: bool = False):
|
|
247
|
+
res = [self.first.str(context, force=force)]
|
|
248
|
+
res.append("-")
|
|
249
|
+
res.append(self.last.str(self.first, force=force))
|
|
250
|
+
return "".join(res)
|
|
251
|
+
|
|
252
|
+
def __str__(self):
|
|
253
|
+
return self.str()
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
class RefList(List):
|
|
257
|
+
def __init__(self, content: str | List[Ref | RefRange], context: Optional[Ref] = None, start: int = 0):
|
|
258
|
+
if isinstance(content, list):
|
|
259
|
+
super().__init__(content)
|
|
260
|
+
else:
|
|
261
|
+
self.parse(content, context, start=start)
|
|
262
|
+
|
|
263
|
+
def parse(self, s: str, context: Optional[Ref] = None, start: int = 0):
|
|
264
|
+
bits = re.split(r"\s*[,;]\s*", s[start:])
|
|
265
|
+
res = []
|
|
266
|
+
for b in bits:
|
|
267
|
+
first = Ref(b, context=context)
|
|
268
|
+
nextstart = first.strend
|
|
269
|
+
if not (m := re.match(r"\s*-\s*", s[nextstart:])):
|
|
270
|
+
if nextstart < len(b):
|
|
271
|
+
raise SyntaxError("Bad range in {}".format(s[start:]))
|
|
272
|
+
else:
|
|
273
|
+
res.append(first)
|
|
274
|
+
context = first
|
|
275
|
+
else:
|
|
276
|
+
nextstart += m.end()
|
|
277
|
+
last = Ref(b, context=first, start=nextstart)
|
|
278
|
+
res.append(RefRange(first, last))
|
|
279
|
+
context = last
|
|
280
|
+
self.__init__(res)
|
|
281
|
+
|
|
282
|
+
def __str__(self):
|
|
283
|
+
return "; ".join(str(s) for s in self)
|
|
284
|
+
|
|
285
|
+
def str(self, context: Optional[Ref] = None, force: bool = False):
|
|
286
|
+
res = []
|
|
287
|
+
for r in self:
|
|
288
|
+
res.append(r.str(context, force=force))
|
|
289
|
+
context = r.last
|
|
290
|
+
return "; ".join(res)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def main():
|
|
294
|
+
import sys
|
|
295
|
+
|
|
296
|
+
if len(sys.argv) > 1:
|
|
297
|
+
s = " ".join(sys.argv[1:])
|
|
298
|
+
res = RefList(s)
|
|
299
|
+
print(res.str(force=True))
|
|
300
|
+
|
|
301
|
+
if __name__ == "__main__":
|
|
302
|
+
main()
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from . import parser, serializer
|
|
2
|
+
|
|
3
|
+
def load(f):
|
|
4
|
+
return parser.parse(f=f)
|
|
5
|
+
|
|
6
|
+
def loads(src):
|
|
7
|
+
return parser.parse(src)
|
|
8
|
+
|
|
9
|
+
def dump(root, f, indent=None):
|
|
10
|
+
f.write(serializer.XMLSerializer(indent).toxml(root))
|
|
11
|
+
|
|
12
|
+
def dumps(root, indent=None):
|
|
13
|
+
return serializer.XMLSerializer(indent).toxml(root)
|