phext 0.3.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.
phext/__init__.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "0.3.0"
phext/coordinate.py ADDED
@@ -0,0 +1,154 @@
1
+ from dataclasses import dataclass
2
+ from functools import total_ordering
3
+
4
+ @total_ordering
5
+ @dataclass
6
+ class Coordinate:
7
+ library: int
8
+ shelf: int
9
+ series: int
10
+ collection: int
11
+ volume: int
12
+ book: int
13
+ chapter: int
14
+ section: int
15
+ scroll: int
16
+
17
+ @classmethod
18
+ def from_string(ignored, coord_str: str):
19
+ try:
20
+ parts = coord_str.strip().split("/")
21
+ if len(parts) != 3:
22
+ raise ValueError("Coordinate must conform to Phext Standard Addressing")
23
+
24
+ nums = []
25
+ for part in parts:
26
+ nums.extend(int(x) for x in part.split("."))
27
+
28
+ if len(nums) != 9:
29
+ raise ValueError("Coordinate must have exactly 9 numeric parts")
30
+
31
+ if any(n < 1 or n > 999 for n in nums):
32
+ raise ValueError("All coordinate values must be between 1 and 999")
33
+
34
+ return Coordinate(nums[0], nums[1], nums[2], nums[3], nums[4], nums[5], nums[6], nums[7], nums[8])
35
+
36
+ except Exception as e:
37
+ raise ValueError(f"Invalid coordinate string: {coord_str} {e}") from e
38
+
39
+ def libraryBreak(self):
40
+ self.library += 1
41
+ self.shelf = 1
42
+ self.series = 1
43
+ self.collection = 1
44
+ self.volume = 1
45
+ self.book = 1
46
+ self.chapter = 1
47
+ self.section = 1
48
+ self.scroll = 1
49
+
50
+ def shelfBreak(self):
51
+ self.shelf += 1
52
+ self.series = 1
53
+ self.collection = 1
54
+ self.volume = 1
55
+ self.book = 1
56
+ self.chapter = 1
57
+ self.section = 1
58
+ self.scroll = 1
59
+
60
+ def seriesBreak(self):
61
+ self.series += 1
62
+ self.collection = 1
63
+ self.volume = 1
64
+ self.book = 1
65
+ self.chapter = 1
66
+ self.section = 1
67
+ self.scroll = 1
68
+
69
+ def collectionBreak(self):
70
+ self.collection += 1
71
+ self.volume = 1
72
+ self.book = 1
73
+ self.chapter = 1
74
+ self.section = 1
75
+ self.scroll = 1
76
+
77
+ def volumeBreak(self):
78
+ self.volume += 1
79
+ self.book = 1
80
+ self.chapter = 1
81
+ self.section = 1
82
+ self.scroll = 1
83
+
84
+ def bookBreak(self):
85
+ self.book += 1
86
+ self.chapter = 1
87
+ self.section = 1
88
+ self.scroll = 1
89
+
90
+ def chapterBreak(self):
91
+ self.chapter += 1
92
+ self.section = 1
93
+ self.scroll = 1
94
+
95
+ def sectionBreak(self):
96
+ self.section += 1
97
+ self.scroll = 1
98
+
99
+ def scrollBreak(self):
100
+ self.scroll += 1
101
+
102
+ def __init__(self, lb, sf, sr, cn, vm, bk, ch, sn, sc):
103
+ self.library = int(lb)
104
+ self.shelf = int(sf)
105
+ self.series = int(sr)
106
+ self.collection = int(cn)
107
+ self.volume = int(vm)
108
+ self.book = int(bk)
109
+ self.chapter = int(ch)
110
+ self.section = int(sn)
111
+ self.scroll = int(sc)
112
+
113
+ def copy_coordinate(self, other):
114
+ self.library = int(other.library)
115
+ self.shelf = int(other.shelf)
116
+ self.series = int(other.series)
117
+ self.collection = int(other.collection)
118
+ self.volume = int(other.volume)
119
+ self.book = int(other.book)
120
+ self.chapter = int(other.chapter)
121
+ self.section = int(other.section)
122
+ self.scroll = int(other.scroll)
123
+
124
+ def __str__(self) -> str:
125
+ return f"{self.library}.{self.shelf}.{self.series}/" \
126
+ f"{self.collection}.{self.volume}.{self.book}/" \
127
+ f"{self.chapter}.{self.section}.{self.scroll}"
128
+
129
+ def urlencoded(self) -> str:
130
+ return f"{self.library}.{self.shelf}.{self.series};" \
131
+ f"{self.collection}.{self.volume}.{self.book};" \
132
+ f"{self.chapter}.{self.section}.{self.scroll}"
133
+
134
+ def __eq__(self, other):
135
+ return isinstance(self, Coordinate) and isinstance(other, Coordinate) and self.library == other.library and self.shelf == other.shelf and self.series == other.series and self.collection == other.collection and self.volume == other.volume and self.book == other.book and self.chapter == other.chapter and self.section == other.section and self.scroll == other.scroll
136
+
137
+ def __lt__(self, other):
138
+ if not isinstance(other, Coordinate):
139
+ return NotImplemented
140
+ return self.as_tuple() < other.as_tuple()
141
+
142
+ def as_tuple(self) -> tuple:
143
+ return (
144
+ self.library, self.shelf, self.series,
145
+ self.collection, self.volume, self.book,
146
+ self.chapter, self.section, self.scroll
147
+ )
148
+
149
+ def __hash__(self):
150
+ return hash((
151
+ self.library, self.shelf, self.series,
152
+ self.collection, self.volume, self.book,
153
+ self.chapter, self.section, self.scroll
154
+ ))
phext/phext.py ADDED
@@ -0,0 +1,546 @@
1
+ import copy
2
+ import xxhash
3
+
4
+ from dataclasses import dataclass
5
+ from typing import List
6
+
7
+ from phext.coordinate import Coordinate
8
+ from phext.positionedScroll import PositionedScroll
9
+ from phext.range import Range
10
+
11
+ @dataclass
12
+ class SubspaceBeacon:
13
+ start: int
14
+ end: int
15
+ best: Coordinate
16
+
17
+ @dataclass
18
+ class Phext:
19
+ location: Coordinate
20
+
21
+ LIBRARY_BREAK = "\x01"
22
+ STX_BREAK = "\x02"
23
+ ETX_BREAK = "\x03"
24
+ MORE_COWBELL = "\x07"
25
+ SHELF_BREAK = "\x1F"
26
+ SERIES_BREAK = "\x1E"
27
+ COLLECTION_BREAK = "\x1D"
28
+ VOLUME_BREAK = "\x1C"
29
+ BOOK_BREAK = "\x1A"
30
+ CHAPTER_BREAK = "\x19"
31
+ SECTION_BREAK = "\x18"
32
+ SCROLL_BREAK = "\x17"
33
+ LINE_BREAK = "\x0a"
34
+
35
+ def defaultCoordinate(self) -> Coordinate:
36
+ return Coordinate(1,1,1, 1,1,1, 1,1,1)
37
+
38
+ def __init__(self):
39
+ location = self.defaultCoordinate()
40
+
41
+ def fetch(self, buffer:str, coord:Coordinate) -> dict[Coordinate, str]:
42
+ stack = self.phokenize(buffer)
43
+ for ps in stack:
44
+ if ps.coord == coord:
45
+ return ps.text
46
+ return ""
47
+
48
+ def isPhextBreak(self, byte:str):
49
+ return byte == self.LINE_BREAK or \
50
+ byte == self.SCROLL_BREAK or \
51
+ byte == self.SECTION_BREAK or \
52
+ byte == self.CHAPTER_BREAK or \
53
+ byte == self.BOOK_BREAK or \
54
+ byte == self.VOLUME_BREAK or \
55
+ byte == self.COLLECTION_BREAK or \
56
+ byte == self.SERIES_BREAK or \
57
+ byte == self.SHELF_BREAK or \
58
+ byte == self.LIBRARY_BREAK
59
+
60
+ def phokenize(self, buffer:str) -> List[PositionedScroll]:
61
+ result = []
62
+ location = self.defaultCoordinate()
63
+ next = self.defaultCoordinate()
64
+ temp = ""
65
+ # decoded = buffer.decode("utf-8")
66
+ for ch in buffer:
67
+ dimension_break = False
68
+ if ch == self.LIBRARY_BREAK:
69
+ next.libraryBreak()
70
+ dimension_break = True
71
+ if ch == self.SHELF_BREAK:
72
+ next.shelfBreak()
73
+ dimension_break = True
74
+ if ch == self.SERIES_BREAK:
75
+ next.seriesBreak()
76
+ dimension_break = True
77
+ if ch == self.COLLECTION_BREAK:
78
+ next.collectionBreak()
79
+ dimension_break = True
80
+ if ch == self.VOLUME_BREAK:
81
+ next.volumeBreak()
82
+ dimension_break = True
83
+ if ch == self.BOOK_BREAK:
84
+ next.bookBreak()
85
+ dimension_break = True
86
+ if ch == self.CHAPTER_BREAK:
87
+ next.chapterBreak()
88
+ dimension_break = True
89
+ if ch == self.SECTION_BREAK:
90
+ next.sectionBreak()
91
+ dimension_break = True
92
+ if ch == self.SCROLL_BREAK:
93
+ next.scrollBreak()
94
+ dimension_break = True
95
+
96
+ if dimension_break == True:
97
+ if len(temp) > 0:
98
+ item = PositionedScroll(location, temp)
99
+ result.append(item)
100
+ temp = ""
101
+ else:
102
+ temp += str(ch)
103
+ location = copy.deepcopy(next)
104
+
105
+ if len(temp) > 0:
106
+ item = PositionedScroll(location, temp)
107
+ result.append(item)
108
+
109
+ return result
110
+
111
+ def append_scroll(self, entry: PositionedScroll, location: Coordinate) -> PositionedScroll:
112
+ output = ""
113
+ compare = entry.coord
114
+ while location < entry.coord:
115
+ if location.library < compare.library:
116
+ output += Phext.LIBRARY_BREAK
117
+ location.libraryBreak()
118
+ continue
119
+ if location.shelf < compare.shelf:
120
+ output += Phext.SHELF_BREAK
121
+ location.shelfBreak()
122
+ continue
123
+ if location.series < compare.series:
124
+ output += Phext.SERIES_BREAK
125
+ location.seriesBreak()
126
+ continue
127
+ if location.collection < compare.collection:
128
+ output += Phext.COLLECTION_BREAK
129
+ location.collectionBreak()
130
+ continue
131
+ if location.volume < compare.volume:
132
+ output += Phext.VOLUME_BREAK
133
+ location.volumeBreak()
134
+ continue
135
+ if location.book < compare.book:
136
+ output += Phext.BOOK_BREAK
137
+ location.bookBreak()
138
+ continue
139
+ if location.chapter < compare.chapter:
140
+ output += Phext.CHAPTER_BREAK
141
+ location.chapterBreak()
142
+ continue
143
+ if location.section < compare.section:
144
+ output += Phext.SECTION_BREAK
145
+ location.sectionBreak()
146
+ continue
147
+ if location.scroll < compare.scroll:
148
+ output += Phext.SCROLL_BREAK
149
+ location.scrollBreak()
150
+ continue
151
+ if len(entry.text) > 0:
152
+ output += entry.text
153
+ result = PositionedScroll(location, output)
154
+ return result
155
+
156
+ def dephokenize(self, stack: list[PositionedScroll]) -> str:
157
+ result = ""
158
+ location = self.defaultCoordinate()
159
+ for ps in stack:
160
+ next = self.append_scroll(ps, location)
161
+ result += next.text
162
+ location = next.coord
163
+ return result
164
+
165
+ def normalize(self, buffer:str) -> str:
166
+ arr = self.phokenize(buffer)
167
+ return self.dephokenize(arr)
168
+
169
+ def update(self, buffer:str, coord:Coordinate, scroll:str, overwrite:bool) -> str:
170
+ items = self.phokenize(buffer)
171
+ result = []
172
+ next = PositionedScroll(coord, scroll)
173
+ appended = False
174
+ for ps in items:
175
+ if (ps.coord > coord) and (appended == False):
176
+ result.append(next)
177
+ appended = True
178
+ if (ps.coord == coord) and (appended == False):
179
+ if overwrite == False:
180
+ next.text = ps.text + next.text
181
+ result.append(next)
182
+ appended = True
183
+ continue
184
+ result.append(ps)
185
+ if appended == False:
186
+ result.append(next)
187
+ appended = True
188
+
189
+ serialized = self.dephokenize(result)
190
+ return serialized
191
+
192
+ def insert(self, buffer:str, coord:Coordinate, scroll:str) -> str:
193
+ return self.update(buffer, coord, scroll, False)
194
+
195
+ def replace(self, buffer:str, coord:Coordinate, scroll:str) -> str:
196
+ return self.update(buffer, coord, scroll, True)
197
+
198
+ def remove(self, buffer:str, coord:Coordinate) -> str:
199
+ intermediate = self.update(buffer, coord, "", True)
200
+ return self.normalize(intermediate)
201
+
202
+ def range_replace(self, buffer:str, range:Range, text:str) -> str:
203
+ stack = self.phokenize(buffer)
204
+ result = []
205
+ appended = False
206
+ for ps in stack:
207
+ if ps.coord < range.start and len(ps.text) > 0:
208
+ result.append(ps)
209
+ if ps.coord >= range.start and ps.coord <= range.end:
210
+ if appended == False and len(text) > 0:
211
+ next = PositionedScroll(ps.coord, text)
212
+ result.append(next)
213
+ appended = True
214
+ if ps.coord > range.end and len(ps.text) > 0:
215
+ result.append(ps)
216
+ return self.dephokenize(result)
217
+
218
+ def next_scroll(self, buffer:str, coord:Coordinate) -> List[PositionedScroll]:
219
+ stack = self.phokenize(buffer)
220
+ found = False
221
+ result = []
222
+ for ps in stack:
223
+ if found == True:
224
+ result.append(ps)
225
+ return result
226
+ if ps.coord >= coord:
227
+ result.append(ps)
228
+ found = True
229
+ return result
230
+
231
+ def get_subspace_coordinates(self, buffer:str, target:Coordinate):
232
+ walker = self.defaultCoordinate()
233
+ best = self.defaultCoordinate()
234
+ subspace_index = 0
235
+ start = 0
236
+ end = 0
237
+ stage = 0
238
+ max = len(buffer)
239
+
240
+ while subspace_index < max:
241
+ next = buffer[subspace_index]
242
+ compare = next
243
+
244
+ if stage == 0:
245
+ if walker == target:
246
+ stage = 1
247
+ start = subspace_index
248
+ best = copy.deepcopy(walker)
249
+ if walker < target:
250
+ best = copy.deepcopy(walker)
251
+ if stage < 2 and walker > target:
252
+ if stage == 0:
253
+ start = subspace_index - 1
254
+ end = subspace_index - 1
255
+ stage = 2
256
+
257
+ if self.isPhextBreak(next):
258
+ if compare == self.SCROLL_BREAK:
259
+ walker.scrollBreak()
260
+ if compare == self.SECTION_BREAK:
261
+ walker.sectionBreak()
262
+ if compare == self.CHAPTER_BREAK:
263
+ walker.chapterBreak()
264
+ if compare == self.BOOK_BREAK:
265
+ walker.bookBreak()
266
+ if compare == self.VOLUME_BREAK:
267
+ walker.volumeBreak()
268
+ if compare == self.COLLECTION_BREAK:
269
+ walker.collectionBreak()
270
+ if compare == self.SERIES_BREAK:
271
+ walker.seriesBreak()
272
+ if compare == self.SHELF_BREAK:
273
+ walker.shelfBreak()
274
+ if compare == self.LIBRARY_BREAK:
275
+ walker.libraryBreak()
276
+
277
+ if stage < 2 and walker > target:
278
+ end = subspace_index
279
+ stage = 2
280
+
281
+ subspace_index += 1
282
+
283
+ if stage == 1 and walker == target:
284
+ end = max
285
+ stage = 2
286
+
287
+ if stage == 0:
288
+ start = max
289
+ end = max
290
+
291
+ result = SubspaceBeacon(start, end, best)
292
+ return result
293
+
294
+ def merge(self, left: str, right:str) -> str:
295
+ tl = self.phokenize(left)
296
+ tr = self.phokenize(right)
297
+ tli = 0
298
+ tri = 0
299
+ maxtl = len(tl)
300
+ maxtr = len(tr)
301
+ result = ""
302
+ coord = self.defaultCoordinate()
303
+
304
+ while True:
305
+ have_left = tli < maxtl
306
+ have_right = tri < maxtr
307
+ pick_left = have_left and (have_right == False or tl[tli].coord <= tr[tri].coord)
308
+ pick_right = have_right and (have_left == False or tr[tri].coord <= tl[tli].coord)
309
+
310
+ if pick_left:
311
+ next = self.append_scroll(tl[tli], coord)
312
+ result += next.text
313
+ coord = next.coord
314
+ tli += 1
315
+ if pick_right:
316
+ next = self.append_scroll(tr[tri], coord)
317
+ result += next.text
318
+ coord = next.coord
319
+ tri += 1
320
+
321
+ if pick_left == False and pick_right == False:
322
+ break
323
+
324
+ return result
325
+
326
+ def subtract(self, left:str, right:str) -> str:
327
+ pl = self.phokenize(left)
328
+ pr = self.phokenize(right)
329
+ result = ""
330
+ pri = 0
331
+ max = len(pr)
332
+ coord = self.defaultCoordinate()
333
+ for item in pl:
334
+ do_append = pri == max
335
+ if pri < max:
336
+ compare = pr[pri]
337
+ if item.coord < compare.coord:
338
+ do_append = True
339
+ elif item.coord == compare.coord:
340
+ pri += 1
341
+ if do_append:
342
+ next = self.append_scroll(item, coord)
343
+ result += next.text
344
+ coord = next.coord
345
+ return result
346
+
347
+ def expand(self, buffer:str) -> str:
348
+ result = ""
349
+ for char in buffer:
350
+ if char == self.LINE_BREAK:
351
+ result += self.SCROLL_BREAK
352
+ elif char == self.SCROLL_BREAK:
353
+ result += self.SECTION_BREAK
354
+ elif char == self.SECTION_BREAK:
355
+ result += self.CHAPTER_BREAK
356
+ elif char == self.CHAPTER_BREAK:
357
+ result += self.BOOK_BREAK
358
+ elif char == self.BOOK_BREAK:
359
+ result += self.VOLUME_BREAK
360
+ elif char == self.VOLUME_BREAK:
361
+ result += self.COLLECTION_BREAK
362
+ elif char == self.COLLECTION_BREAK:
363
+ result += self.SERIES_BREAK
364
+ elif char == self.SERIES_BREAK:
365
+ result += self.SHELF_BREAK
366
+ elif char == self.SHELF_BREAK:
367
+ result += self.LIBRARY_BREAK
368
+ else:
369
+ result += char
370
+ return result
371
+
372
+ def contract(self, buffer:str) -> str:
373
+ result = ""
374
+ for char in buffer:
375
+ if char == self.LIBRARY_BREAK:
376
+ result += self.SHELF_BREAK
377
+ elif char == self.SHELF_BREAK:
378
+ result += self.SERIES_BREAK
379
+ elif char == self.SERIES_BREAK:
380
+ result += self.COLLECTION_BREAK
381
+ elif char == self.COLLECTION_BREAK:
382
+ result += self.VOLUME_BREAK
383
+ elif char == self.VOLUME_BREAK:
384
+ result += self.BOOK_BREAK
385
+ elif char == self.BOOK_BREAK:
386
+ result += self.CHAPTER_BREAK
387
+ elif char == self.CHAPTER_BREAK:
388
+ result += self.SECTION_BREAK
389
+ elif char == self.SECTION_BREAK:
390
+ result += self.SCROLL_BREAK
391
+ elif char == self.SCROLL_BREAK:
392
+ result += self.LINE_BREAK
393
+ else:
394
+ result += char
395
+ return result
396
+
397
+ def create_summary(self, buffer:str) -> str:
398
+ limit = 32
399
+ max = len(buffer)
400
+ if max < 32:
401
+ limit = max
402
+ summary = ""
403
+ for char in buffer:
404
+ if self.isPhextBreak(char):
405
+ break
406
+ summary += char
407
+ if len(summary) < len(buffer):
408
+ summary += "..."
409
+ return summary
410
+
411
+ def navmap(self, urlbase:str, buffer:str) -> str:
412
+ stack = self.phokenize(buffer)
413
+ result = ""
414
+ max = len(stack)
415
+ if max > 0:
416
+ result += "<ul>\n"
417
+ for ps in stack:
418
+ urlcoord = ps.coord.urlencoded()
419
+ summary = self.create_summary(ps.text)
420
+ result += f"<li><a href=\"{urlbase}{urlcoord}\">{ps.coord} {summary}</a></li>\n"
421
+ if max > 0:
422
+ result += "</ul>\n"
423
+ return result
424
+
425
+ def textmap(self, buffer:str) -> str:
426
+ stack = self.phokenize(buffer)
427
+ result = ""
428
+ for ps in stack:
429
+ summary = self.create_summary(ps.text)
430
+ result += f"* {ps.coord}: {summary}\n"
431
+ return result
432
+
433
+ def checksum(self, buffer:str) -> str:
434
+ hash = xxhash.xxh3_128(buffer).hexdigest()
435
+ return hash
436
+
437
+ def manifest(self, buffer:str) -> str:
438
+ stack = self.phokenize(buffer)
439
+ for ps in stack:
440
+ ps.text = self.checksum(ps.text)
441
+ result = self.dephokenize(stack)
442
+ return result
443
+
444
+ def soundex_v1(self, buffer:str) -> str:
445
+ stack = self.phokenize(buffer)
446
+
447
+ for ps in stack:
448
+ ps.text = self.soundex_internal(ps.text)
449
+
450
+ return self.dephokenize(stack)
451
+
452
+ def soundex_internal(self, buffer:str) -> str:
453
+ letter1 = "bpfv"
454
+ letter2 = "cskgjqxz"
455
+ letter3 = "dt"
456
+ letter4 = "l"
457
+ letter5 = "mn"
458
+ letter6 = "r"
459
+
460
+ value = 1 # 1-100
461
+ for byte in buffer:
462
+ if byte in letter1:
463
+ value += 1
464
+ if byte in letter2:
465
+ value += 2
466
+ if byte in letter3:
467
+ value += 3
468
+ if byte in letter4:
469
+ value += 4
470
+ if byte in letter5:
471
+ value += 5
472
+ if byte in letter6:
473
+ value += 6
474
+ return str(value % 99)
475
+
476
+ def explode(self, buffer:str) -> dict[Coordinate, str]:
477
+ parts = self.phokenize(buffer)
478
+ hash: dict[Coordinate, str] = {}
479
+ for ps in parts:
480
+ hash[ps.coord] = ps.text
481
+ return hash
482
+
483
+ def implode(self, hash: dict[Coordinate, str]) -> str:
484
+ parts = []
485
+ for coord in hash.keys():
486
+ parts.append(PositionedScroll(coord, hash[coord]))
487
+ return self.dephokenize(parts)
488
+
489
+ def index_phokens(self, buffer:str) -> list[PositionedScroll]:
490
+ stack = self.phokenize(buffer)
491
+ offset = 0
492
+ coord = self.defaultCoordinate()
493
+ output = []
494
+ for ps in stack:
495
+ reference = ps.coord
496
+ while coord.library < reference.library:
497
+ coord.libraryBreak()
498
+ offset += 1
499
+ while coord.shelf < reference.shelf:
500
+ coord.shelfBreak()
501
+ offset += 1
502
+ while coord.series < reference.series:
503
+ coord.seriesBreak()
504
+ offset += 1
505
+ while coord.collection < reference.collection:
506
+ coord.collectionBreak()
507
+ offset += 1
508
+ while coord.volume < reference.volume:
509
+ coord.volumeBreak()
510
+ offset += 1
511
+ while coord.book < reference.book:
512
+ coord.bookBreak()
513
+ offset += 1
514
+ while coord.chapter < reference.chapter:
515
+ coord.chapterBreak()
516
+ offset += 1
517
+ while coord.section < reference.section:
518
+ coord.sectionBreak()
519
+ offset += 1
520
+ while coord.scroll < reference.scroll:
521
+ coord.scrollBreak()
522
+ offset += 1
523
+ text = str(offset)
524
+ output.append(PositionedScroll(copy.deepcopy(coord), text))
525
+ offset += len(ps.text)
526
+
527
+ return output
528
+
529
+ def index(self, buffer:str) -> str:
530
+ output = self.index_phokens(buffer)
531
+ return self.dephokenize(output)
532
+
533
+ def offset(self, buffer:str, coord:Coordinate) -> int:
534
+ output = self.index_phokens(buffer)
535
+ best = self.defaultCoordinate()
536
+ fetch_coord = coord
537
+ matched = False
538
+ for ps in output:
539
+ if ps.coord <= coord:
540
+ best = copy.deepcopy(ps.coord)
541
+ if ps.coord == coord:
542
+ matched = True
543
+ if matched == False:
544
+ fetch_coord = best
545
+ index = self.dephokenize(output)
546
+ return int(self.fetch(index, fetch_coord))
@@ -0,0 +1,11 @@
1
+ from dataclasses import dataclass
2
+ from phext.coordinate import Coordinate
3
+
4
+ @dataclass
5
+ class PositionedScroll:
6
+ coord: Coordinate
7
+ text: str
8
+
9
+ def __init__(self, coord, text):
10
+ self.coord = coord
11
+ self.text = text
phext/range.py ADDED
@@ -0,0 +1,7 @@
1
+ from dataclasses import dataclass
2
+ from phext.coordinate import Coordinate
3
+
4
+ @dataclass
5
+ class Range:
6
+ start: Coordinate
7
+ end: Coordinate
@@ -0,0 +1,28 @@
1
+ Metadata-Version: 2.4
2
+ Name: phext
3
+ Version: 0.3.0
4
+ Summary: Pythonic implementation of phext
5
+ Project-URL: Homepage, https://phext.io
6
+ Project-URL: Repository, https://github.com/wbic16/phext-py
7
+ Project-URL: Issues, https://github.com/wbic16/phext-py/issues
8
+ Author-email: Will Bickford <wbic16@gmail.com>
9
+ License: MIT
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Topic :: Software Development :: Libraries
16
+ Requires-Python: >=3.10
17
+ Requires-Dist: xxhash
18
+ Provides-Extra: dev
19
+ Requires-Dist: mypy; extra == 'dev'
20
+ Requires-Dist: pytest; extra == 'dev'
21
+ Requires-Dist: ruff; extra == 'dev'
22
+ Description-Content-Type: text/markdown
23
+
24
+ # Phext
25
+
26
+ Welcome to the Pythonic port of phext.
27
+
28
+ This port is a downstream artifact that tracks the Rust-based implementation of Phext. See https://github.com/wbic16/phext-rs for more details.
@@ -0,0 +1,8 @@
1
+ phext/__init__.py,sha256=5mDtfU2t04ATmg92S5B-scUcdjCy1ZBq620y3GmpdtQ,21
2
+ phext/coordinate.py,sha256=dAaLTJUF9znpsm_nnVYiLV6dfQE769JhcKlxvQfazms,4821
3
+ phext/phext.py,sha256=P9er7vRegrTjhCMWcIitwqq704cIQQuUXtFPAognqEg,17171
4
+ phext/positionedScroll.py,sha256=b-p4wffIY_E_DDwRJxzOMIymlK1-2nTQVVVD7CADPGw,249
5
+ phext/range.py,sha256=tV3Zte0jxvA1FrTHFwRkjsRgEijyhm0QgUM1x3SvesA,142
6
+ phext-0.3.0.dist-info/METADATA,sha256=gvpUGRm0doT7VvCVUGDAfRo-JHetNXWl6frcHlLzzDM,1019
7
+ phext-0.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
8
+ phext-0.3.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any