usfmtc 0.2.3__tar.gz → 0.2.4__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.
- {usfmtc-0.2.3/python/lib/usfmtc.egg-info → usfmtc-0.2.4}/PKG-INFO +2 -2
- {usfmtc-0.2.3 → usfmtc-0.2.4}/pyproject.toml +1 -1
- {usfmtc-0.2.3 → usfmtc-0.2.4}/python/lib/usfmtc/__init__.py +17 -11
- {usfmtc-0.2.3 → usfmtc-0.2.4}/python/lib/usfmtc/usfmparser.py +106 -43
- {usfmtc-0.2.3 → usfmtc-0.2.4/python/lib/usfmtc.egg-info}/PKG-INFO +2 -2
- {usfmtc-0.2.3 → usfmtc-0.2.4}/LICENSE.md +0 -0
- {usfmtc-0.2.3 → usfmtc-0.2.4}/README.md +0 -0
- {usfmtc-0.2.3 → usfmtc-0.2.4}/python/README.md +0 -0
- {usfmtc-0.2.3 → usfmtc-0.2.4}/python/lib/usfmtc/diagrams.py +0 -0
- {usfmtc-0.2.3 → usfmtc-0.2.4}/python/lib/usfmtc/extension.py +0 -0
- {usfmtc-0.2.3 → usfmtc-0.2.4}/python/lib/usfmtc/grammar.py +0 -0
- {usfmtc-0.2.3 → usfmtc-0.2.4}/python/lib/usfmtc/parser.py +0 -0
- {usfmtc-0.2.3 → usfmtc-0.2.4}/python/lib/usfmtc/railroad.py +0 -0
- {usfmtc-0.2.3 → usfmtc-0.2.4}/python/lib/usfmtc/reference.py +0 -0
- {usfmtc-0.2.3 → usfmtc-0.2.4}/python/lib/usfmtc/rnc2rng/__init__.py +0 -0
- {usfmtc-0.2.3 → usfmtc-0.2.4}/python/lib/usfmtc/rnc2rng/parser.py +0 -0
- {usfmtc-0.2.3 → usfmtc-0.2.4}/python/lib/usfmtc/rnc2rng/rnctree.py +0 -0
- {usfmtc-0.2.3 → usfmtc-0.2.4}/python/lib/usfmtc/rnc2rng/serializer.py +0 -0
- {usfmtc-0.2.3 → usfmtc-0.2.4}/python/lib/usfmtc/sfmparser.py +0 -0
- {usfmtc-0.2.3 → usfmtc-0.2.4}/python/lib/usfmtc/usfmgenerate.py +0 -0
- {usfmtc-0.2.3 → usfmtc-0.2.4}/python/lib/usfmtc/usjproc.py +0 -0
- {usfmtc-0.2.3 → usfmtc-0.2.4}/python/lib/usfmtc/usx.rng +0 -0
- {usfmtc-0.2.3 → usfmtc-0.2.4}/python/lib/usfmtc/usxgrammar.py +0 -0
- {usfmtc-0.2.3 → usfmtc-0.2.4}/python/lib/usfmtc/usxmodel.py +0 -0
- {usfmtc-0.2.3 → usfmtc-0.2.4}/python/lib/usfmtc/usxparser.py +0 -0
- {usfmtc-0.2.3 → usfmtc-0.2.4}/python/lib/usfmtc/xmlutils.py +0 -0
- {usfmtc-0.2.3 → usfmtc-0.2.4}/python/lib/usfmtc.egg-info/SOURCES.txt +0 -0
- {usfmtc-0.2.3 → usfmtc-0.2.4}/python/lib/usfmtc.egg-info/dependency_links.txt +0 -0
- {usfmtc-0.2.3 → usfmtc-0.2.4}/python/lib/usfmtc.egg-info/entry_points.txt +0 -0
- {usfmtc-0.2.3 → usfmtc-0.2.4}/python/lib/usfmtc.egg-info/requires.txt +0 -0
- {usfmtc-0.2.3 → usfmtc-0.2.4}/python/lib/usfmtc.egg-info/top_level.txt +0 -0
- {usfmtc-0.2.3 → usfmtc-0.2.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: usfmtc
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: USFM reference parser
|
|
5
5
|
Author-email: USFM Technical Committee <fonts@sil.org>
|
|
6
6
|
License: Copyright © 2022-2023 by the members of the USFM/USX technical committee, listed below:
|
|
@@ -173,7 +173,7 @@ class USX:
|
|
|
173
173
|
fn(file, dat)
|
|
174
174
|
return True
|
|
175
175
|
|
|
176
|
-
def outUsx(self, file=None):
|
|
176
|
+
def outUsx(self, file=None, **kw):
|
|
177
177
|
""" Output pretty XML USX. If file is None returns string """
|
|
178
178
|
if self.xml is None:
|
|
179
179
|
return None
|
|
@@ -192,16 +192,17 @@ class USX:
|
|
|
192
192
|
return self._outwrite(file, dat)
|
|
193
193
|
return False
|
|
194
194
|
|
|
195
|
-
def outUsj(self, file=None):
|
|
195
|
+
def outUsj(self, file=None, ensure_ascii=False, **kw):
|
|
196
196
|
""" Output USJ from USX object. If file is None returns dict """
|
|
197
197
|
res = usxtousj(self.xml)
|
|
198
198
|
if file is None:
|
|
199
199
|
return res
|
|
200
200
|
else:
|
|
201
|
-
dat = json.dumps(res, indent=2)
|
|
201
|
+
dat = json.dumps(res, indent=2, ensure_ascii=ensure_ascii)
|
|
202
202
|
self._outwrite(file, dat)
|
|
203
203
|
|
|
204
|
-
def saveAs(self, outfpath, outformat=None, addesids=False, grammar=None,
|
|
204
|
+
def saveAs(self, outfpath, outformat=None, addesids=False, grammar=None,
|
|
205
|
+
gramfile=None, version=None, altparser=True, **kw):
|
|
205
206
|
""" Saves the document to a file in the appropriate format, either given
|
|
206
207
|
or inferred from the filename extension. """
|
|
207
208
|
if outformat is None:
|
|
@@ -215,9 +216,9 @@ class USX:
|
|
|
215
216
|
if outtype == "usx":
|
|
216
217
|
if addesids:
|
|
217
218
|
usxdoc.addesids()
|
|
218
|
-
self.outUsx(outfpath)
|
|
219
|
+
self.outUsx(outfpath, **kw)
|
|
219
220
|
elif outtype == "usj":
|
|
220
|
-
self.outUsj(outfpath)
|
|
221
|
+
self.outUsj(outfpath, **kw)
|
|
221
222
|
elif outtype == "usfm":
|
|
222
223
|
if grammar is None and altparser:
|
|
223
224
|
if gramfile is None:
|
|
@@ -230,7 +231,7 @@ class USX:
|
|
|
230
231
|
outtype = "usfm"
|
|
231
232
|
if version is None:
|
|
232
233
|
version = "3.0"
|
|
233
|
-
self.outUsfm(grammar, outfpath, outversion=version, altparser=altparser)
|
|
234
|
+
self.outUsfm(grammar, outfpath, outversion=version, altparser=altparser, **kw)
|
|
234
235
|
|
|
235
236
|
def canonicalise(self):
|
|
236
237
|
canonicalise(self.getroot())
|
|
@@ -262,11 +263,14 @@ def main(hookcli=None, hookusx=None):
|
|
|
262
263
|
parser.add_argument("-F","--outformat",help="Output format [usfm, usx, usj, usfm3.0]")
|
|
263
264
|
parser.add_argument("-I","--informat",help="Input format [usfm, usx, usj]")
|
|
264
265
|
parser.add_argument("-g","--grammar",help="Grammar file to use, if needed")
|
|
265
|
-
parser.add_argument("-e","--esids",action="store_true",
|
|
266
|
+
parser.add_argument("-e","--esids",action="store_true",
|
|
267
|
+
help="Add esids, vids, sids, etc. to USX output")
|
|
266
268
|
parser.add_argument("-v","--version",default=None,help="Set USFM version [3.1]")
|
|
267
|
-
parser.add_argument("-x","--extfiles",action="append",default=[],
|
|
269
|
+
parser.add_argument("-x","--extfiles",action="append",default=[],
|
|
270
|
+
help="markers.ext files to include")
|
|
268
271
|
parser.add_argument("-V","--validate",action="store_true",help="Use validating parsers for USFM")
|
|
269
272
|
parser.add_argument("-C","--canonical",action="store_true",help="Do not canonicalise")
|
|
273
|
+
parser.add_argument("-A","--ascii",action="store_true",help="Output as ASCII only in json")
|
|
270
274
|
parser.add_argument("-l","--logging",help="Set logging level to usfmxtest.log")
|
|
271
275
|
parser.add_argument("-q","--quiet",action="store_true",help="Don't say much")
|
|
272
276
|
parser.add_argument("--nooutput",action="store_true",help="Don't output any data")
|
|
@@ -353,7 +357,8 @@ def main(hookcli=None, hookusx=None):
|
|
|
353
357
|
if not args.quiet:
|
|
354
358
|
print(f"{infile} -> {outfile}" if outfile else f"{infile}")
|
|
355
359
|
try:
|
|
356
|
-
usxdoc = readFile(infile, informat=args.informat, grammar=ingrammar,
|
|
360
|
+
usxdoc = readFile(infile, informat=args.informat, grammar=ingrammar,
|
|
361
|
+
altparser=args.validate)
|
|
357
362
|
except NoParseError as e:
|
|
358
363
|
doerror(f"Failed to parse {infile}: {e}", False)
|
|
359
364
|
|
|
@@ -375,7 +380,8 @@ def main(hookcli=None, hookusx=None):
|
|
|
375
380
|
if not args.canonical:
|
|
376
381
|
usxdoc.canonicalise()
|
|
377
382
|
|
|
378
|
-
usxdoc.saveAs(outfile, outformat=args.outformat, addesids=args.esids,
|
|
383
|
+
usxdoc.saveAs(outfile, outformat=args.outformat, addesids=args.esids,
|
|
384
|
+
grammar=outgrammar, altparser=args.validate, ensure_ascii=args.ascii)
|
|
379
385
|
|
|
380
386
|
if __name__ == "__main__":
|
|
381
387
|
main()
|
|
@@ -7,12 +7,13 @@ from collections import UserDict, UserString
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class Pos:
|
|
10
|
-
def __init__(self, l, c):
|
|
10
|
+
def __init__(self, l, c, **kw):
|
|
11
11
|
self.l = l
|
|
12
12
|
self.c = c
|
|
13
|
+
self.kw = kw
|
|
13
14
|
|
|
14
15
|
class Tag(str):
|
|
15
|
-
def __new__(cls, s, l=0, c=0):
|
|
16
|
+
def __new__(cls, s, l=0, c=0, **kw):
|
|
16
17
|
isend = False
|
|
17
18
|
isplus = False
|
|
18
19
|
if s.startswith("+"):
|
|
@@ -22,6 +23,7 @@ class Tag(str):
|
|
|
22
23
|
isend = True
|
|
23
24
|
s = s[:-1]
|
|
24
25
|
res = super().__new__(cls, s)
|
|
26
|
+
res.kw = kw
|
|
25
27
|
res.isplus = isplus
|
|
26
28
|
res.isend = isend
|
|
27
29
|
return res
|
|
@@ -47,14 +49,16 @@ class Tag(str):
|
|
|
47
49
|
self.pos = pos
|
|
48
50
|
|
|
49
51
|
class AttribText(UserString):
|
|
50
|
-
def __init__(self, s, l=0, c=0):
|
|
52
|
+
def __init__(self, s, l=0, c=0, **kw):
|
|
51
53
|
super().__init__(s)
|
|
52
54
|
self.pos = Pos(l, c)
|
|
55
|
+
self.kw = kw
|
|
53
56
|
pass
|
|
54
57
|
|
|
55
58
|
class OptBreak:
|
|
56
|
-
def __init__(self, l=0, c=0):
|
|
59
|
+
def __init__(self, l=0, c=0, **kw):
|
|
57
60
|
self.pos = Pos(l, c)
|
|
61
|
+
self.kw = kw
|
|
58
62
|
|
|
59
63
|
def __repr__(self):
|
|
60
64
|
return "OptBreak(" + str(self) + ")"
|
|
@@ -62,9 +66,10 @@ class OptBreak:
|
|
|
62
66
|
return "//"
|
|
63
67
|
|
|
64
68
|
class String(UserString):
|
|
65
|
-
def __init__(self, s, l=0, c=0):
|
|
69
|
+
def __init__(self, s, l=0, c=0, **kw):
|
|
66
70
|
super().__init__(s)
|
|
67
71
|
self.pos = Pos(l, c)
|
|
72
|
+
self.kw = kw
|
|
68
73
|
|
|
69
74
|
def __add__(self, s):
|
|
70
75
|
return String(str(self) + s)
|
|
@@ -72,25 +77,28 @@ class String(UserString):
|
|
|
72
77
|
pass
|
|
73
78
|
|
|
74
79
|
class Attribs(UserDict):
|
|
75
|
-
def __init__(self, l=0, c=0):
|
|
80
|
+
def __init__(self, l=0, c=0, **kw):
|
|
76
81
|
super().__init__()
|
|
77
82
|
self.pos = Pos(l, c)
|
|
83
|
+
self.kw = kw
|
|
78
84
|
|
|
79
85
|
class Lexer:
|
|
80
86
|
|
|
81
87
|
tokenre = regex.compile(r'((?:[^\\|/]|\\[\\|\n]|/[^/])+)|([\\|\n]|//)')
|
|
82
|
-
tagre = regex.compile(r'(?:\+?[a-zA-Z_][a-zA-Z_0-9
|
|
88
|
+
tagre = regex.compile(r'(?:\+?[a-zA-Z_][a-zA-Z_0-9^-]*)?\*?')
|
|
83
89
|
attribsre = regex.compile(r'\s*([a-zA-Z_][a-zA-Z0-9_-]*)\s*=\s*"((?:\\"|[^"])*)"')
|
|
84
90
|
textrunre = regex.compile(r'(?:[^\\]|\\[^a-zA-Z_*+])*')
|
|
85
91
|
|
|
86
|
-
def __init__(self, txt):
|
|
92
|
+
def __init__(self, txt, expanded=False):
|
|
87
93
|
self.txt = txt
|
|
94
|
+
self.expanded = expanded
|
|
88
95
|
|
|
89
96
|
def __iter__(self):
|
|
90
97
|
self.nexts = []
|
|
91
98
|
self.cindex = 0
|
|
92
99
|
self.lindex = 0
|
|
93
100
|
self.lpos = 0
|
|
101
|
+
self.currxpand = None
|
|
94
102
|
return self
|
|
95
103
|
|
|
96
104
|
def __next__(self):
|
|
@@ -119,7 +127,9 @@ class Lexer:
|
|
|
119
127
|
curri = m.end() + 1
|
|
120
128
|
continue
|
|
121
129
|
else:
|
|
122
|
-
|
|
130
|
+
tagname = self.processtag(t.group(0))
|
|
131
|
+
extras = {"xp": self.currxpand} if self.expanded else {}
|
|
132
|
+
res = Tag(tagname, l=self.lindex, c=curri-self.lpos, **extras)
|
|
123
133
|
curri += t.end()
|
|
124
134
|
break
|
|
125
135
|
elif n == '|':
|
|
@@ -161,6 +171,13 @@ class Lexer:
|
|
|
161
171
|
self.cindex = m.end()
|
|
162
172
|
self.nexts.append(String(m.group(1)))
|
|
163
173
|
|
|
174
|
+
def processtag(self, t):
|
|
175
|
+
i = t.find("^")
|
|
176
|
+
if self.expanded and i >= 0:
|
|
177
|
+
self.currxpand = t[i+1:]
|
|
178
|
+
return t[:i]
|
|
179
|
+
return t
|
|
180
|
+
|
|
164
181
|
class Grammar:
|
|
165
182
|
category_markers = {
|
|
166
183
|
"attrib": "cp vp usfm ca va cat",
|
|
@@ -184,8 +201,14 @@ class Grammar:
|
|
|
184
201
|
"title": "mt1 mt2 mt3 mt4 mt",
|
|
185
202
|
"versepara": "cls nb pc pi1 pi2 pi3 pi po pr pmo pmc pmr pm ph1 ph2 ph3 ph p q1 q2 q3 q4 qc qr qm1 qm2 qm3 qm qd q b d mi1 mi2 mi3 mi4 mi m",
|
|
186
203
|
}
|
|
204
|
+
category_tags = {
|
|
205
|
+
"char": ("char", "crossreferencechar", "footnotechar", "introchar", "listchar"),
|
|
206
|
+
"para": ("header", "introduction", "list", "otherpara", "sectionpara", "title", "versepara"),
|
|
207
|
+
"ms": ("milestone", )
|
|
208
|
+
}
|
|
187
209
|
|
|
188
210
|
marker_categories = {t:k for k, v in category_markers.items() for t in v.split()}
|
|
211
|
+
marker_tags = {t:k for k, v in category_tags.items() for t in v}
|
|
189
212
|
|
|
190
213
|
attribmap = { 'jmp' : 'href', 'k' : 'key', 'qt-s': 'who', 'qt1-s': 'who', 'qt2-s': 'who',
|
|
191
214
|
'qt3-s': 'who', 'qt4-s': 'who', 'qt5-s': 'who', 'rb': 'gloss', 't-s': 'sid', 'ts-s': 'sid',
|
|
@@ -217,13 +240,15 @@ def isfirstText(e):
|
|
|
217
240
|
return True
|
|
218
241
|
|
|
219
242
|
class Node:
|
|
220
|
-
def __init__(self, parser, usxtag, tag, ispara=False, notag=False, pos=None):
|
|
243
|
+
def __init__(self, parser, usxtag, tag, ispara=False, notag=False, pos=None, **kw):
|
|
221
244
|
self.parser = parser
|
|
222
245
|
self.tag = tag
|
|
223
246
|
self.ispara = ispara
|
|
224
247
|
parent = parser.stack[-1] if len(parser.stack) else None
|
|
225
|
-
|
|
226
|
-
|
|
248
|
+
attribs = kw
|
|
249
|
+
if not notag and tag is not None:
|
|
250
|
+
attribs['style'] = tag
|
|
251
|
+
self.element = parser.factory(usxtag, attribs, parent=getattr(parent, 'element', None), pos=pos)
|
|
227
252
|
if parent:
|
|
228
253
|
parent.addNodeElement(self.element)
|
|
229
254
|
self.attribnodes = []
|
|
@@ -303,7 +328,7 @@ class USXNode(Node):
|
|
|
303
328
|
self.element.set('version', str(t).strip())
|
|
304
329
|
|
|
305
330
|
class AttribNode(Node):
|
|
306
|
-
def __init__(self, parser, parent, tag, pos=None):
|
|
331
|
+
def __init__(self, parser, parent, tag, pos=None, **kw):
|
|
307
332
|
self.parser = parser
|
|
308
333
|
self.parent = parent
|
|
309
334
|
self.parent.addAttribNode(self)
|
|
@@ -320,8 +345,8 @@ class AttribNode(Node):
|
|
|
320
345
|
self.parent.element.set(attrib, str(t).strip())
|
|
321
346
|
|
|
322
347
|
class NumberNode(Node):
|
|
323
|
-
def __init__(self, parser, usxtag, tag, ispara=False, pos=None):
|
|
324
|
-
super().__init__(parser, usxtag, tag, ispara=ispara, pos=pos)
|
|
348
|
+
def __init__(self, parser, usxtag, tag, ispara=False, pos=None, **kw):
|
|
349
|
+
super().__init__(parser, usxtag, tag, ispara=ispara, pos=pos, **kw)
|
|
325
350
|
self.hasarg = False
|
|
326
351
|
|
|
327
352
|
def appendText(self, t):
|
|
@@ -342,8 +367,8 @@ class NumberNode(Node):
|
|
|
342
367
|
self.parser.stack[-1].addNodeElement(e)
|
|
343
368
|
|
|
344
369
|
class NoteNode(Node):
|
|
345
|
-
def __init__(self, parser, usxtag, tag, pos=None):
|
|
346
|
-
super().__init__(parser, usxtag, tag, pos=pos)
|
|
370
|
+
def __init__(self, parser, usxtag, tag, pos=None, **kw):
|
|
371
|
+
super().__init__(parser, usxtag, tag, pos=pos, **kw)
|
|
347
372
|
self.hascaller = False
|
|
348
373
|
|
|
349
374
|
def appendText(self, t):
|
|
@@ -365,31 +390,16 @@ class PeriphNode(Node):
|
|
|
365
390
|
if t:
|
|
366
391
|
self.element.set('alt', str(t).strip())
|
|
367
392
|
|
|
393
|
+
class UnknownNode(Node):
|
|
394
|
+
pass
|
|
395
|
+
|
|
368
396
|
|
|
369
397
|
paratypes = ('header', 'introduction', 'list', 'otherpara', 'sectionpara', 'versepara', 'title', 'chapter', 'ident')
|
|
370
398
|
paratags = ('rem', ' table')
|
|
371
|
-
def setupParser(cls):
|
|
372
|
-
# tag closed types
|
|
373
|
-
for a in (('introchar', 'char'), ('listchar', 'char'), ('char', 'char'), ('_fig', 'figure')):
|
|
374
|
-
def maketype(c, t):
|
|
375
|
-
def dotype(self, tag):
|
|
376
|
-
if tag.isend:
|
|
377
|
-
return self.removeTag(str(tag))
|
|
378
|
-
return self.addNode(Node(self, t, tag.basestr()))
|
|
379
|
-
return dotype
|
|
380
|
-
setattr(cls, a[0], maketype(*a))
|
|
381
|
-
# implicit closed paras
|
|
382
|
-
for a in paratypes:
|
|
383
|
-
def dotype(self, tag):
|
|
384
|
-
self.removeType(paratypes, paratags)
|
|
385
|
-
return self.addNode(Node(self, 'para', str(tag), ispara=True))
|
|
386
|
-
if not hasattr(cls, a):
|
|
387
|
-
setattr(cls, a, dotype)
|
|
388
|
-
|
|
389
399
|
|
|
390
400
|
class USFMParser:
|
|
391
401
|
|
|
392
|
-
def __init__(self, txt, factory=None, grammar=None):
|
|
402
|
+
def __init__(self, txt, factory=None, grammar=None, expanded=False, strict=False):
|
|
393
403
|
if factory is None:
|
|
394
404
|
def makeel(tag, attrib, **extras):
|
|
395
405
|
attrib.update({" "+k:v for k, v in extras.items()})
|
|
@@ -399,7 +409,32 @@ class USFMParser:
|
|
|
399
409
|
grammar = Grammar()
|
|
400
410
|
self.factory = factory
|
|
401
411
|
self.grammar = grammar
|
|
402
|
-
self.
|
|
412
|
+
self._setup(expanded=expanded)
|
|
413
|
+
self.lexer = Lexer(txt, expanded=expanded)
|
|
414
|
+
|
|
415
|
+
def _setup(s, expanded=False):
|
|
416
|
+
clsself = s.__class__
|
|
417
|
+
# tag closed types
|
|
418
|
+
for a in (('introchar', 'char'), ('listchar', 'char'), ('char', 'char'), ('_fig', 'figure')):
|
|
419
|
+
def maketype(c, t):
|
|
420
|
+
def dotype(self, tag):
|
|
421
|
+
if tag.isend:
|
|
422
|
+
return self.removeTag(str(tag))
|
|
423
|
+
return self.addNode(Node(self, t, tag.basestr()))
|
|
424
|
+
return dotype
|
|
425
|
+
setattr(clsself, a[-1], maketype(*a))
|
|
426
|
+
# implicit closed paras
|
|
427
|
+
for a in paratypes:
|
|
428
|
+
if expanded:
|
|
429
|
+
def dotype(self, tag):
|
|
430
|
+
self.removeType(paratypes, paratags)
|
|
431
|
+
return self.addNode(Node(self, 'para', str(tag), ispara=True, xpand=tag.xp))
|
|
432
|
+
else:
|
|
433
|
+
def dotype(self, tag):
|
|
434
|
+
self.removeType(paratypes, paratags)
|
|
435
|
+
return self.addNode(Node(self, 'para', str(tag), ispara=True))
|
|
436
|
+
if not hasattr(clsself, a):
|
|
437
|
+
setattr(clsself, a, dotype)
|
|
403
438
|
|
|
404
439
|
def parse(self):
|
|
405
440
|
self.result = []
|
|
@@ -449,6 +484,8 @@ class USFMParser:
|
|
|
449
484
|
while len(self.stack):
|
|
450
485
|
curr = self.stack.pop()
|
|
451
486
|
if curr.tag == tag:
|
|
487
|
+
if curr.element.tag == "unk":
|
|
488
|
+
curr.element.tag = "char"
|
|
452
489
|
break
|
|
453
490
|
else:
|
|
454
491
|
self.stack = oldstack
|
|
@@ -460,12 +497,31 @@ class USFMParser:
|
|
|
460
497
|
oldstack = self.stack[:]
|
|
461
498
|
while len(self.stack):
|
|
462
499
|
curr = self.stack.pop()
|
|
500
|
+
e = curr.element
|
|
501
|
+
if e.tag == "usx":
|
|
502
|
+
self.stack.append(curr)
|
|
503
|
+
break
|
|
463
504
|
curr.close()
|
|
464
|
-
|
|
505
|
+
cat = self.grammar.marker_categories.get(e.tag, None)
|
|
506
|
+
if e.tag == "unk":
|
|
507
|
+
e.tag = self.grammar.marker_tags.get(t[0], "para")
|
|
508
|
+
if e.tag == 'para' and e.parent is not None:
|
|
509
|
+
parent = e.parent
|
|
510
|
+
while parent is not None and parent.tag not in ("usx", "para"):
|
|
511
|
+
parent = parent.parent
|
|
512
|
+
if parent.tag != "usx":
|
|
513
|
+
i, p = parent._getindex()
|
|
514
|
+
if p is not None:
|
|
515
|
+
e.parent.remove(e)
|
|
516
|
+
p.insert(i+1, e)
|
|
517
|
+
e.parent = p
|
|
518
|
+
self.removeType(t, tags=tags)
|
|
519
|
+
break
|
|
520
|
+
if curr.tag in tags or cat in t:
|
|
465
521
|
break
|
|
466
522
|
else:
|
|
467
523
|
self.stack = oldstack
|
|
468
|
-
return self.stack[-1]
|
|
524
|
+
return self.stack[-1] if len(self.stack) else None
|
|
469
525
|
|
|
470
526
|
#### Event methods
|
|
471
527
|
|
|
@@ -583,12 +639,19 @@ class USFMParser:
|
|
|
583
639
|
return self.addNode(Node(self, 'ms', tag.basestr(), pos=tag.pos))
|
|
584
640
|
|
|
585
641
|
def unknown(self, tag):
|
|
586
|
-
|
|
587
|
-
|
|
642
|
+
if strict:
|
|
643
|
+
if len(self.stack):
|
|
644
|
+
raise SyntaxError(f"Unknown tag {tag} in {self.stack[-1]}")
|
|
645
|
+
else:
|
|
646
|
+
raise SyntaxError(f"Unknown tag {tag}")
|
|
647
|
+
return None
|
|
648
|
+
if not tag.isend:
|
|
649
|
+
res = self.addNode(UnknownNode(self, 'unk', str(tag), pos=tag.pos))
|
|
650
|
+
else:
|
|
651
|
+
res = self.removeTag(str(tag))
|
|
652
|
+
res.tag = "char"
|
|
588
653
|
return res
|
|
589
654
|
|
|
590
|
-
setupParser(USFMParser)
|
|
591
|
-
|
|
592
655
|
def main():
|
|
593
656
|
import sys
|
|
594
657
|
p = USFMParser(sys.argv[1])
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: usfmtc
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: USFM reference parser
|
|
5
5
|
Author-email: USFM Technical Committee <fonts@sil.org>
|
|
6
6
|
License: Copyright © 2022-2023 by the members of the USFM/USX technical committee, listed below:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|