libmorpheus 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.
libmorpheus/_types.py ADDED
@@ -0,0 +1,502 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-or-later
2
+ """Public Python values and normalized analysis and generation records."""
3
+
4
+ from __future__ import annotations
5
+
6
+ from dataclasses import dataclass
7
+ from enum import IntEnum, IntFlag
8
+ from typing import Sequence, TypeAlias
9
+
10
+
11
+ class Language(IntEnum):
12
+ GREEK = 0
13
+ LATIN = 1
14
+ ITALIAN = 2
15
+
16
+
17
+ class Status(IntEnum):
18
+ OK = 0
19
+ INVALID_ARGUMENT = 1
20
+ ABI_MISMATCH = 2
21
+ NO_MEMORY = 3
22
+ INPUT_TOO_LONG = 4
23
+ OUT_OF_RANGE = 5
24
+ INTERNAL_ERROR = 6
25
+ BUFFER_TOO_SMALL = 7
26
+ STEMLIB_ERROR = 8
27
+ RESULT_LIMIT_EXCEEDED = 9
28
+
29
+
30
+ class Option(IntFlag):
31
+ NONE = 0
32
+ STRICT_CASE = 1 << 0
33
+ IGNORE_ACCENTS = 1 << 1
34
+ VERBS_ONLY = 1 << 2
35
+ NO_CRASIS = 1 << 3
36
+ QUICK = 1 << 4
37
+ HQ_DICTIONARY = 1 << 5
38
+ DIALECT_AEOLIC = 1 << 16
39
+ DIALECT_ATTIC = 2 << 16
40
+ DIALECT_DORIC = 4 << 16
41
+ DIALECT_HOMERIC = 8 << 16
42
+ DIALECT_IONIC = 16 << 16
43
+ DIALECT_LESBIAN = 32 << 16
44
+ DIALECT_NON_HOMERIC_EPIC = 64 << 16
45
+ DIALECT_PARADIGM = 128 << 16
46
+ DIALECT_EPIC = 72 << 16
47
+ DIALECT_PROSE = 256 << 16
48
+
49
+
50
+ class PartOfSpeech(IntEnum):
51
+ UNKNOWN = 0
52
+ NOUN = 1
53
+ VERB = 2
54
+ ADJECTIVE = 3
55
+ ADVERB = 4
56
+ ARTICLE = 5
57
+ PRONOUN = 6
58
+ NUMERAL = 7
59
+ PREPOSITION = 8
60
+ CONJUNCTION = 9
61
+ PARTICLE = 10
62
+ INTERJECTION = 11
63
+
64
+
65
+ class Person(IntEnum):
66
+ NONE = 0
67
+ FIRST = 1
68
+ SECOND = 2
69
+ THIRD = 3
70
+
71
+
72
+ class Number(IntEnum):
73
+ NONE = 0
74
+ SINGULAR = 1
75
+ DUAL = 2
76
+ PLURAL = 3
77
+
78
+
79
+ class Gender(IntFlag):
80
+ NONE = 0
81
+ ADVERBIAL = 1
82
+ FEMININE = 2
83
+ MASCULINE = 4
84
+ NEUTER = 8
85
+
86
+
87
+ class Case(IntFlag):
88
+ NONE = 0
89
+ ABLATIVE = 1
90
+ ACCUSATIVE = 2
91
+ DATIVE = 4
92
+ GENITIVE = 8
93
+ NOMINATIVE = 16
94
+ VOCATIVE = 32
95
+
96
+
97
+ class Tense(IntEnum):
98
+ NONE = 0
99
+ PRESENT = 1
100
+ IMPERFECT = 2
101
+ FUTURE = 3
102
+ AORIST = 4
103
+ PERFECT = 5
104
+ PLUPERFECT = 6
105
+ FUTURE_PERFECT = 7
106
+ PAST_ABSOLUTE = 8
107
+
108
+
109
+ class Mood(IntEnum):
110
+ NONE = 0
111
+ CONDITIONAL = 1
112
+ GERUNDIVE = 2
113
+ IMPERATIVE = 3
114
+ INDICATIVE = 4
115
+ INFINITIVE = 5
116
+ OPTATIVE = 6
117
+ PARTICIPLE = 7
118
+ SUBJUNCTIVE = 8
119
+ SUPINE = 9
120
+
121
+
122
+ class Voice(IntFlag):
123
+ NONE = 0
124
+ ACTIVE = 1
125
+ PASSIVE = 2
126
+ MIDDLE = 4
127
+ DEPONENT = ACTIVE | MIDDLE
128
+ MEDIO_PASSIVE = PASSIVE | MIDDLE
129
+
130
+
131
+ class Degree(IntEnum):
132
+ NONE = 0
133
+ POSITIVE = 1
134
+ COMPARATIVE = 2
135
+ SUPERLATIVE = 3
136
+
137
+
138
+ class Dialect(IntFlag):
139
+ ALL = 0
140
+ AEOLIC = 1
141
+ ATTIC = 2
142
+ DORIC = 4
143
+ HOMERIC = 8
144
+ IONIC = 16
145
+ LESBIAN = 32
146
+ NON_HOMERIC_EPIC = 64
147
+ PARADIGM = 128
148
+ EPIC = HOMERIC | NON_HOMERIC_EPIC
149
+ PROSE = 256
150
+
151
+
152
+ class GeographicRegion(IntFlag):
153
+ NONE = 0
154
+ ARCADIA = 1
155
+ ARGOLID = 2
156
+ BOEOTIA = 4
157
+ COS = 8
158
+ CRETE = 16
159
+ CYPRUS = 32
160
+ CYRENE = 64
161
+ ELIS = 128
162
+ HERACLEA = 256
163
+ LACONIA = 512
164
+ LOCRIS = 1024
165
+ MEGARID = 2048
166
+ PHOCIS = 4096
167
+ RHODES = 8192
168
+ THERA = 16384
169
+
170
+
171
+ class TruncatedField(IntFlag):
172
+ NONE = 0
173
+ RAW = 1 << 0
174
+ WORKWORD = 1 << 1
175
+ LEMMA = 1 << 2
176
+ PREVERB = 1 << 3
177
+ AUGMENT = 1 << 4
178
+ STEM = 1 << 5
179
+ SUFFIX = 1 << 6
180
+ ENDING = 1 << 7
181
+ CRASIS = 1 << 8
182
+ DICTIONARY_FORM = 1 << 9
183
+ ENGLISH_FORM = 1 << 10
184
+ RAW_PREVERB = 1 << 11
185
+ DOMAINS = 1 << 12
186
+
187
+
188
+ MorphFlagName: TypeAlias = str
189
+
190
+ MORPH_FLAG_NAMES = (
191
+ "accent-optional", "alpha-copulative", "alpha-privative",
192
+ "antepenult-accent", "apocope", "attic-reduplication", "causal",
193
+ "compound-only", "contracted", "delta-preverb", "derivative",
194
+ "desiderative", "diminutive", "dissimilation", "doubled-consonant",
195
+ "double-augment", "double-reduplication", "early", "elided-preverb",
196
+ "enclitic", "ends-in-digamma", "en-to-eni", "frequentative",
197
+ "geographic-name", "group-name", "has-augment", "has-preverb",
198
+ "impersonal", "indeclinable-form", "intervocalic-s-to-h",
199
+ "intransitive", "iota-intensive", "irregular-comparative",
200
+ "irregular-form", "irregular-superlative", "iterative", "late", "later",
201
+ "long-penult", "lost-accent", "metathesis", "meta-to-peda",
202
+ "metrically-long", "needs-accent", "needs-rough-breathing",
203
+ "not-in-composition", "no-circumflex", "no-comparison",
204
+ "no-reduplication", "nu-movable", "n-infix", "para-to-parai",
205
+ "person-name", "poetic", "present-reduplication", "preverb-augment",
206
+ "proclitic", "prodelision", "pros-to-poti", "pros-to-proti",
207
+ "quantity-metathesis", "rare", "raw-preverb", "raw-sonant",
208
+ "recessive-accent", "reduplication", "rho-eta-iota-alpha",
209
+ "root-preverb", "short-eis", "short-penult", "short-subjunctive",
210
+ "sigma-to-ci", "stem-accent", "suffix-accent", "syllabic-augment",
211
+ "syncope", "tau-preverb", "tmesis", "unaspirated-preverb", "unaugmented",
212
+ "uncontracted-ending", "uncontracted-stem", "uper-to-upeir", "upo-to-upai",
213
+ )
214
+
215
+
216
+ @dataclass(frozen=True, slots=True)
217
+ class RawAnalysis:
218
+ struct_size: int
219
+ part_of_speech: int
220
+ dialect: int
221
+ geographic_region: int
222
+ person: int
223
+ number: int
224
+ gender: int
225
+ grammatical_case: int
226
+ tense: int
227
+ mood: int
228
+ voice: int
229
+ degree: int
230
+ raw: str
231
+ workword: str
232
+ lemma: str
233
+ preverb: str
234
+ augment: str
235
+ stem: str
236
+ suffix: str
237
+ ending: str
238
+ crasis: str
239
+ dictionary_form: str
240
+ english_form: str
241
+ raw_preverb: str
242
+ domains: str
243
+ morph_flags: bytes
244
+ truncated_fields: int
245
+
246
+
247
+ @dataclass(frozen=True, slots=True)
248
+ class Analysis:
249
+ part_of_speech: str
250
+ dialects: tuple[str, ...]
251
+ geographic_regions: tuple[str, ...]
252
+ person: str | None
253
+ grammatical_number: str | None
254
+ genders: tuple[str, ...]
255
+ grammatical_cases: tuple[str, ...]
256
+ tense: str | None
257
+ mood: str | None
258
+ voices: tuple[str, ...]
259
+ degree: str | None
260
+ raw: str
261
+ workword: str
262
+ lemma: str
263
+ preverb: str
264
+ augment: str
265
+ stem: str
266
+ suffix: str
267
+ ending: str
268
+ crasis: str
269
+ dictionary_form: str
270
+ english_form: str
271
+ raw_preverb: str
272
+ domains: str
273
+ morph_flags: tuple[MorphFlagName, ...]
274
+ truncated_fields: tuple[str, ...]
275
+
276
+
277
+ @dataclass(frozen=True, slots=True)
278
+ class GenerationOptions:
279
+ result_limit: int = 0
280
+ exclude_duals: bool = False
281
+ part_of_speech: PartOfSpeech = PartOfSpeech.UNKNOWN
282
+ dialect: Dialect = Dialect.ALL
283
+ geographic_region: GeographicRegion = GeographicRegion.NONE
284
+ person: Person = Person.NONE
285
+ number: Number = Number.NONE
286
+ gender: Gender = Gender.NONE
287
+ grammatical_case: Case = Case.NONE
288
+ tense: Tense = Tense.NONE
289
+ mood: Mood = Mood.NONE
290
+ voice: Voice = Voice.NONE
291
+ degree: Degree = Degree.NONE
292
+
293
+
294
+ @dataclass(frozen=True, slots=True)
295
+ class RawGeneration:
296
+ struct_size: int
297
+ part_of_speech: int
298
+ dialect: int
299
+ geographic_region: int
300
+ person: int
301
+ number: int
302
+ gender: int
303
+ grammatical_case: int
304
+ tense: int
305
+ mood: int
306
+ voice: int
307
+ degree: int
308
+ surface: str
309
+ lemma: str
310
+ morph_flags: bytes
311
+ truncated_fields: int
312
+
313
+
314
+ @dataclass(frozen=True, slots=True)
315
+ class Generation:
316
+ part_of_speech: str
317
+ dialects: tuple[str, ...]
318
+ geographic_regions: tuple[str, ...]
319
+ person: str | None
320
+ grammatical_number: str | None
321
+ genders: tuple[str, ...]
322
+ grammatical_cases: tuple[str, ...]
323
+ tense: str | None
324
+ mood: str | None
325
+ voices: tuple[str, ...]
326
+ degree: str | None
327
+ surface: str
328
+ lemma: str
329
+ morph_flags: tuple[MorphFlagName, ...]
330
+ truncated_fields: tuple[str, ...]
331
+
332
+
333
+ PART_OF_SPEECH_NAMES = tuple(
334
+ name.lower().replace("_", "-") for name in PartOfSpeech.__members__
335
+ )
336
+ PERSON_NAMES = (None, "first", "second", "third")
337
+ NUMBER_NAMES = (None, "singular", "dual", "plural")
338
+ TENSE_NAMES = (
339
+ None, "present", "imperfect", "future", "aorist", "perfect",
340
+ "pluperfect", "future-perfect", "past-absolute",
341
+ )
342
+ MOOD_NAMES = (
343
+ None, "conditional", "gerundive", "imperative", "indicative",
344
+ "infinitive", "optative", "participle", "subjunctive", "supine",
345
+ )
346
+ DEGREE_NAMES = (None, "positive", "comparative", "superlative")
347
+ GENDER_NAMES = (
348
+ (Gender.ADVERBIAL, "adverbial"), (Gender.FEMININE, "feminine"),
349
+ (Gender.MASCULINE, "masculine"), (Gender.NEUTER, "neuter"),
350
+ )
351
+ CASE_NAMES = (
352
+ (Case.ABLATIVE, "ablative"), (Case.ACCUSATIVE, "accusative"),
353
+ (Case.DATIVE, "dative"), (Case.GENITIVE, "genitive"),
354
+ (Case.NOMINATIVE, "nominative"), (Case.VOCATIVE, "vocative"),
355
+ )
356
+ VOICE_NAMES = (
357
+ (Voice.ACTIVE, "active"), (Voice.PASSIVE, "passive"),
358
+ (Voice.MIDDLE, "middle"),
359
+ )
360
+ DIALECT_NAMES = (
361
+ (Dialect.ATTIC, "attic"), (Dialect.IONIC, "ionic"),
362
+ (Dialect.AEOLIC, "aeolic"), (Dialect.LESBIAN, "lesbian"),
363
+ (Dialect.HOMERIC, "homeric"), (Dialect.DORIC, "doric"),
364
+ (Dialect.PARADIGM, "paradigm"),
365
+ (Dialect.NON_HOMERIC_EPIC, "non-homeric-epic"),
366
+ (Dialect.PROSE, "prose"),
367
+ )
368
+ REGION_NAMES = tuple(
369
+ (region, region.name.lower().replace("_", "-"))
370
+ for region in GeographicRegion
371
+ if region is not GeographicRegion.NONE
372
+ )
373
+ TRUNCATED_NAMES = tuple(
374
+ (field, field.name.lower())
375
+ for field in TruncatedField
376
+ if field is not TruncatedField.NONE
377
+ )
378
+
379
+
380
+ def _exact(value: int, names: tuple[str | None, ...]) -> str | None:
381
+ return names[value] if 0 <= value < len(names) else None
382
+
383
+
384
+ def _mask(value: int, entries: tuple[tuple[IntFlag, str], ...]) -> tuple[str, ...]:
385
+ return tuple(name for flag, name in entries if value & int(flag) == int(flag))
386
+
387
+
388
+ def _dialects(value: int) -> tuple[str, ...]:
389
+ if value & int(Dialect.EPIC) == int(Dialect.EPIC):
390
+ remaining = value & ~int(Dialect.EPIC)
391
+ selected = {"epic"}
392
+ else:
393
+ remaining = value
394
+ selected = set()
395
+ selected.update(
396
+ name for flag, name in DIALECT_NAMES
397
+ if remaining & int(flag) == int(flag)
398
+ )
399
+ order = tuple(name for _, name in DIALECT_NAMES[:-1]) + ("epic", "prose")
400
+ return tuple(name for name in order if name in selected)
401
+
402
+
403
+ def has_morph_flag(
404
+ analysis: (
405
+ RawAnalysis
406
+ | Analysis
407
+ | RawGeneration
408
+ | Generation
409
+ | Sequence[RawAnalysis | Analysis | RawGeneration | Generation]
410
+ ),
411
+ flag: int | str,
412
+ ) -> bool:
413
+ if isinstance(analysis, (list, tuple)):
414
+ return any(has_morph_flag(item, flag) for item in analysis)
415
+ if isinstance(analysis, (Analysis, Generation)):
416
+ name = (
417
+ MORPH_FLAG_NAMES[flag]
418
+ if isinstance(flag, int) and 0 <= flag < len(MORPH_FLAG_NAMES)
419
+ else flag
420
+ )
421
+ return isinstance(name, str) and name in analysis.morph_flags
422
+ if isinstance(flag, str):
423
+ try:
424
+ flag = MORPH_FLAG_NAMES.index(flag)
425
+ except ValueError:
426
+ return False
427
+ if flag < 0:
428
+ return False
429
+ index, bit = divmod(flag, 8)
430
+ return index < len(analysis.morph_flags) and bool(analysis.morph_flags[index] & 1 << bit)
431
+
432
+
433
+ def normalize_analysis(raw: RawAnalysis) -> Analysis:
434
+ voices = (
435
+ ("medio-passive",) if raw.voice == int(Voice.MEDIO_PASSIVE)
436
+ else ("deponent",) if raw.voice == int(Voice.DEPONENT)
437
+ else _mask(raw.voice, VOICE_NAMES)
438
+ )
439
+ return Analysis(
440
+ part_of_speech=_exact(raw.part_of_speech, PART_OF_SPEECH_NAMES) or "unknown",
441
+ dialects=_dialects(raw.dialect),
442
+ geographic_regions=_mask(raw.geographic_region, REGION_NAMES),
443
+ person=_exact(raw.person, PERSON_NAMES),
444
+ grammatical_number=_exact(raw.number, NUMBER_NAMES),
445
+ genders=_mask(raw.gender, GENDER_NAMES),
446
+ grammatical_cases=_mask(raw.grammatical_case, CASE_NAMES),
447
+ tense=_exact(raw.tense, TENSE_NAMES),
448
+ mood=_exact(raw.mood, MOOD_NAMES),
449
+ voices=voices,
450
+ degree=_exact(raw.degree, DEGREE_NAMES),
451
+ raw=raw.raw,
452
+ workword=raw.workword,
453
+ lemma=raw.lemma,
454
+ preverb=raw.preverb,
455
+ augment=raw.augment,
456
+ stem=raw.stem,
457
+ suffix=raw.suffix,
458
+ ending=raw.ending,
459
+ crasis=raw.crasis,
460
+ dictionary_form=raw.dictionary_form,
461
+ english_form=raw.english_form,
462
+ raw_preverb=raw.raw_preverb,
463
+ domains=raw.domains,
464
+ morph_flags=tuple(
465
+ name for index, name in enumerate(MORPH_FLAG_NAMES)
466
+ if has_morph_flag(raw, index)
467
+ ),
468
+ truncated_fields=_mask(raw.truncated_fields, TRUNCATED_NAMES),
469
+ )
470
+
471
+
472
+ def normalize_generation(raw: RawGeneration) -> Generation:
473
+ voices = (
474
+ ("medio-passive",) if raw.voice == int(Voice.MEDIO_PASSIVE)
475
+ else ("deponent",) if raw.voice == int(Voice.DEPONENT)
476
+ else _mask(raw.voice, VOICE_NAMES)
477
+ )
478
+ truncated = []
479
+ if raw.truncated_fields & int(TruncatedField.WORKWORD):
480
+ truncated.append("surface")
481
+ if raw.truncated_fields & int(TruncatedField.LEMMA):
482
+ truncated.append("lemma")
483
+ return Generation(
484
+ part_of_speech=_exact(raw.part_of_speech, PART_OF_SPEECH_NAMES) or "unknown",
485
+ dialects=_dialects(raw.dialect),
486
+ geographic_regions=_mask(raw.geographic_region, REGION_NAMES),
487
+ person=_exact(raw.person, PERSON_NAMES),
488
+ grammatical_number=_exact(raw.number, NUMBER_NAMES),
489
+ genders=_mask(raw.gender, GENDER_NAMES),
490
+ grammatical_cases=_mask(raw.grammatical_case, CASE_NAMES),
491
+ tense=_exact(raw.tense, TENSE_NAMES),
492
+ mood=_exact(raw.mood, MOOD_NAMES),
493
+ voices=voices,
494
+ degree=_exact(raw.degree, DEGREE_NAMES),
495
+ surface=raw.surface,
496
+ lemma=raw.lemma,
497
+ morph_flags=tuple(
498
+ name for index, name in enumerate(MORPH_FLAG_NAMES)
499
+ if has_morph_flag(raw, index)
500
+ ),
501
+ truncated_fields=tuple(truncated),
502
+ )
@@ -0,0 +1,6 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-or-later
2
+ """Independent Python-binding and compatible native-runtime versions."""
3
+
4
+ MORPHEUS_PYTHON_VERSION = "0.1.0"
5
+ MORPHEUS_NATIVE_VERSION = "0.3.2"
6
+ MORPHEUS_NATIVE_ABI_VERSION = 2
libmorpheus/py.typed ADDED
File without changes