multilingualprogramming 0.2.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.
- multilingualprogramming/__init__.py +74 -0
- multilingualprogramming/__main__.py +194 -0
- multilingualprogramming/codegen/__init__.py +12 -0
- multilingualprogramming/codegen/executor.py +215 -0
- multilingualprogramming/codegen/python_generator.py +592 -0
- multilingualprogramming/codegen/repl.py +489 -0
- multilingualprogramming/codegen/runtime_builtins.py +308 -0
- multilingualprogramming/core/__init__.py +12 -0
- multilingualprogramming/core/ir.py +29 -0
- multilingualprogramming/core/lowering.py +24 -0
- multilingualprogramming/datetime/__init__.py +11 -0
- multilingualprogramming/datetime/date_parser.py +190 -0
- multilingualprogramming/datetime/mp_date.py +210 -0
- multilingualprogramming/datetime/mp_datetime.py +153 -0
- multilingualprogramming/datetime/mp_time.py +147 -0
- multilingualprogramming/datetime/resource_loader.py +18 -0
- multilingualprogramming/exceptions.py +158 -0
- multilingualprogramming/imports.py +150 -0
- multilingualprogramming/keyword/__init__.py +13 -0
- multilingualprogramming/keyword/keyword_registry.py +249 -0
- multilingualprogramming/keyword/keyword_validator.py +59 -0
- multilingualprogramming/keyword/language_pack_validator.py +110 -0
- multilingualprogramming/lexer/__init__.py +11 -0
- multilingualprogramming/lexer/lexer.py +570 -0
- multilingualprogramming/lexer/source_reader.py +91 -0
- multilingualprogramming/lexer/token.py +54 -0
- multilingualprogramming/lexer/token_types.py +38 -0
- multilingualprogramming/numeral/__init__.py +11 -0
- multilingualprogramming/numeral/abstract_numeral.py +232 -0
- multilingualprogramming/numeral/complex_numeral.py +190 -0
- multilingualprogramming/numeral/fraction_numeral.py +165 -0
- multilingualprogramming/numeral/mp_numeral.py +243 -0
- multilingualprogramming/numeral/numeral_converter.py +151 -0
- multilingualprogramming/numeral/roman_numeral.py +301 -0
- multilingualprogramming/numeral/unicode_numeral.py +292 -0
- multilingualprogramming/parser/__init__.py +28 -0
- multilingualprogramming/parser/ast_nodes.py +459 -0
- multilingualprogramming/parser/ast_printer.py +677 -0
- multilingualprogramming/parser/error_messages.py +75 -0
- multilingualprogramming/parser/parser.py +1796 -0
- multilingualprogramming/parser/semantic_analyzer.py +689 -0
- multilingualprogramming/parser/surface_normalizer.py +282 -0
- multilingualprogramming/resources/datetime/eras.json +23 -0
- multilingualprogramming/resources/datetime/formats.json +32 -0
- multilingualprogramming/resources/datetime/months.json +150 -0
- multilingualprogramming/resources/datetime/weekdays.json +90 -0
- multilingualprogramming/resources/parser/error_messages.json +310 -0
- multilingualprogramming/resources/repl/commands.json +636 -0
- multilingualprogramming/resources/usm/builtins_aliases.json +731 -0
- multilingualprogramming/resources/usm/keywords.json +1063 -0
- multilingualprogramming/resources/usm/operators.json +532 -0
- multilingualprogramming/resources/usm/schema.json +34 -0
- multilingualprogramming/resources/usm/surface_patterns.json +1523 -0
- multilingualprogramming/unicode_string.py +140 -0
- multilingualprogramming/version.py +9 -0
- multilingualprogramming-0.2.0.dist-info/METADATA +350 -0
- multilingualprogramming-0.2.0.dist-info/RECORD +61 -0
- multilingualprogramming-0.2.0.dist-info/WHEEL +5 -0
- multilingualprogramming-0.2.0.dist-info/entry_points.txt +3 -0
- multilingualprogramming-0.2.0.dist-info/licenses/LICENSE +674 -0
- multilingualprogramming-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
#
|
|
2
|
+
# SPDX-FileCopyrightText: 2022 John Samuel <johnsamuelwrites@gmail.com>
|
|
3
|
+
#
|
|
4
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
"""Functions to handle numbers of multiple languages
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from operator import invert, neg
|
|
11
|
+
from roman import toRoman
|
|
12
|
+
from multilingualprogramming.unicode_string import get_unicode_character_string
|
|
13
|
+
import multilingualprogramming.numeral.unicode_numeral as un
|
|
14
|
+
import multilingualprogramming.numeral.roman_numeral as rn
|
|
15
|
+
from multilingualprogramming.exceptions import (
|
|
16
|
+
MultipleLanguageCharacterMixError,
|
|
17
|
+
InvalidNumeralCharacterError,
|
|
18
|
+
DifferentNumeralTypeError
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class MPNumeral:
|
|
23
|
+
"""
|
|
24
|
+
Handling numerals in unicode-supported languages and
|
|
25
|
+
Roman numerals
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, numstr: str):
|
|
29
|
+
self.numstr = numstr
|
|
30
|
+
self.num = None
|
|
31
|
+
try:
|
|
32
|
+
if rn.RomanNumeral.is_roman_numeral(numstr):
|
|
33
|
+
self.num = rn.RomanNumeral(numstr) # create a Roman numeral
|
|
34
|
+
self.numeral_type = "Roman"
|
|
35
|
+
else:
|
|
36
|
+
self.num = un.UnicodeNumeral(numstr) # create a Unicode numeral
|
|
37
|
+
self.numeral_type = "Unicode"
|
|
38
|
+
except (
|
|
39
|
+
MultipleLanguageCharacterMixError,
|
|
40
|
+
InvalidNumeralCharacterError,
|
|
41
|
+
) as exception:
|
|
42
|
+
raise exception
|
|
43
|
+
|
|
44
|
+
def to_decimal(self):
|
|
45
|
+
"""
|
|
46
|
+
Returns the number (Multilingual Numeral) associated with the number string
|
|
47
|
+
given by the user
|
|
48
|
+
|
|
49
|
+
return:
|
|
50
|
+
number: number associated with the number string
|
|
51
|
+
"""
|
|
52
|
+
if self.num:
|
|
53
|
+
return self.num.to_decimal()
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
def __str__(self):
|
|
57
|
+
"""
|
|
58
|
+
Returns the original number string (Multilingual Numeral)
|
|
59
|
+
|
|
60
|
+
return:
|
|
61
|
+
numstr: original number string
|
|
62
|
+
"""
|
|
63
|
+
return self.numstr
|
|
64
|
+
|
|
65
|
+
def __repr__(self):
|
|
66
|
+
"""
|
|
67
|
+
Returns the representation (Multilingual numeral) of an instance
|
|
68
|
+
|
|
69
|
+
return:
|
|
70
|
+
reprstr: representation of an instance
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
def to_mp_numeral(self, number):
|
|
74
|
+
"""
|
|
75
|
+
Returns the number (Multilingual Numeral) associated with the number string
|
|
76
|
+
given by the user
|
|
77
|
+
|
|
78
|
+
return:
|
|
79
|
+
mpnumeral: MPNumber associated with the number string
|
|
80
|
+
"""
|
|
81
|
+
result = None
|
|
82
|
+
if self.numeral_type == "Roman":
|
|
83
|
+
result = toRoman(number)
|
|
84
|
+
else:
|
|
85
|
+
result = get_unicode_character_string(self.num.language_name, number)
|
|
86
|
+
return MPNumeral(result)
|
|
87
|
+
|
|
88
|
+
def _perform_operation(self, numeral, operation):
|
|
89
|
+
"""
|
|
90
|
+
Helper function to perform arithmetic operations on MPNumeral objects.
|
|
91
|
+
|
|
92
|
+
Parameters:
|
|
93
|
+
numeral (MPNumeral): The numeral to operate with.
|
|
94
|
+
operation (function): The arithmetic operation to apply.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
MPNumeral: The result of the arithmetic operation.
|
|
98
|
+
"""
|
|
99
|
+
if isinstance(numeral, MPNumeral) and self.numeral_type == numeral.numeral_type:
|
|
100
|
+
result = operation(self.to_decimal(), numeral.to_decimal())
|
|
101
|
+
return self.to_mp_numeral(result)
|
|
102
|
+
raise DifferentNumeralTypeError(operation.__name__)
|
|
103
|
+
|
|
104
|
+
def __add__(self, numeral):
|
|
105
|
+
"""
|
|
106
|
+
Add a MPNumeral with a numeral or another MPNumeral.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
MPNumeral: The sum of the MPNumeral values.
|
|
110
|
+
"""
|
|
111
|
+
return self._perform_operation(numeral, lambda x, y: x + y)
|
|
112
|
+
|
|
113
|
+
def __mul__(self, numeral):
|
|
114
|
+
"""
|
|
115
|
+
Multiply a MPNumeral with a numeral or another MPNumeral.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
MPNumeral: The product of the MPNumeral values.
|
|
119
|
+
"""
|
|
120
|
+
return self._perform_operation(numeral, lambda x, y: x * y)
|
|
121
|
+
|
|
122
|
+
def __lshift__(self, numeral):
|
|
123
|
+
"""
|
|
124
|
+
Left-shifting of Multilingual numerals
|
|
125
|
+
|
|
126
|
+
return:
|
|
127
|
+
MPNumeral: returns the left shifted value
|
|
128
|
+
"""
|
|
129
|
+
return self._perform_operation(numeral, lambda x, y: x << y)
|
|
130
|
+
|
|
131
|
+
def __rshift__(self, numeral):
|
|
132
|
+
"""
|
|
133
|
+
Right-shifting of Multilingual numerals
|
|
134
|
+
|
|
135
|
+
return:
|
|
136
|
+
MPNumeral: returns the right shifted value
|
|
137
|
+
"""
|
|
138
|
+
return self._perform_operation(numeral, lambda x, y: x >> y)
|
|
139
|
+
|
|
140
|
+
def __sub__(self, numeral):
|
|
141
|
+
"""
|
|
142
|
+
Substraction of Multilingual numerals
|
|
143
|
+
|
|
144
|
+
return:
|
|
145
|
+
MPNumeral: returns the difference
|
|
146
|
+
"""
|
|
147
|
+
return self._perform_operation(numeral, lambda x, y: x - y)
|
|
148
|
+
|
|
149
|
+
def __truediv__(self, numeral):
|
|
150
|
+
"""
|
|
151
|
+
True division of Multilingual numerals
|
|
152
|
+
|
|
153
|
+
return:
|
|
154
|
+
MPNumeral: returns the value after true division
|
|
155
|
+
"""
|
|
156
|
+
return self._perform_operation(numeral, lambda x, y: x / y)
|
|
157
|
+
|
|
158
|
+
def __floordiv__(self, numeral):
|
|
159
|
+
"""
|
|
160
|
+
Floor division of Multilingual numerals
|
|
161
|
+
|
|
162
|
+
return:
|
|
163
|
+
MPNumeral: returns the value after floor division
|
|
164
|
+
"""
|
|
165
|
+
return self._perform_operation(numeral, lambda x, y: x // y)
|
|
166
|
+
|
|
167
|
+
def __neg__(self):
|
|
168
|
+
"""
|
|
169
|
+
Negation of Multilingual numerals
|
|
170
|
+
|
|
171
|
+
return:
|
|
172
|
+
MPNumeral: returns the negation
|
|
173
|
+
"""
|
|
174
|
+
return MPNumeral(str(neg(self.to_decimal())))
|
|
175
|
+
|
|
176
|
+
def __pow__(self, numeral):
|
|
177
|
+
"""
|
|
178
|
+
Power of Multilingual numerals
|
|
179
|
+
|
|
180
|
+
return:
|
|
181
|
+
MPNumeral: returns the power
|
|
182
|
+
"""
|
|
183
|
+
return self._perform_operation(numeral, lambda x, y: x**y)
|
|
184
|
+
|
|
185
|
+
def __mod__(self, numeral):
|
|
186
|
+
"""
|
|
187
|
+
Modulus of Multilingual numerals
|
|
188
|
+
|
|
189
|
+
return:
|
|
190
|
+
MPNumeral: returns the modulus value
|
|
191
|
+
"""
|
|
192
|
+
return self._perform_operation(numeral, lambda x, y: x % y)
|
|
193
|
+
|
|
194
|
+
def __xor__(self, numeral):
|
|
195
|
+
"""
|
|
196
|
+
XOR value of Multilingual numerals
|
|
197
|
+
|
|
198
|
+
return:
|
|
199
|
+
MPNumeral: returns the XOR value
|
|
200
|
+
"""
|
|
201
|
+
return self._perform_operation(numeral, lambda x, y: x ^ y)
|
|
202
|
+
|
|
203
|
+
def __invert__(self):
|
|
204
|
+
"""
|
|
205
|
+
Bitwise inversion value of Multilingual numerals
|
|
206
|
+
|
|
207
|
+
return:
|
|
208
|
+
MPNumeral: returns the bitwise-inverted value
|
|
209
|
+
"""
|
|
210
|
+
result = invert(self.to_decimal())
|
|
211
|
+
if self.numeral_type == "Roman":
|
|
212
|
+
result = toRoman(result)
|
|
213
|
+
else:
|
|
214
|
+
result = get_unicode_character_string(self.num.language_name, result)
|
|
215
|
+
return MPNumeral(result)
|
|
216
|
+
|
|
217
|
+
def __or__(self, numeral):
|
|
218
|
+
"""
|
|
219
|
+
OR value of Multilingual numerals
|
|
220
|
+
|
|
221
|
+
return:
|
|
222
|
+
MPNumeral: returns the OR value
|
|
223
|
+
"""
|
|
224
|
+
return self._perform_operation(numeral, lambda x, y: x | y)
|
|
225
|
+
|
|
226
|
+
def convert_to(self, target_language):
|
|
227
|
+
"""
|
|
228
|
+
Convert this numeral to a different script.
|
|
229
|
+
|
|
230
|
+
Only works for Unicode numerals.
|
|
231
|
+
|
|
232
|
+
Parameters:
|
|
233
|
+
target_language (str): Target language name (e.g., "ARABIC-INDIC")
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
MPNumeral: A new MPNumeral in the target script
|
|
237
|
+
"""
|
|
238
|
+
if self.numeral_type == "Unicode":
|
|
239
|
+
converted = self.num.convert_to(target_language)
|
|
240
|
+
return MPNumeral(str(converted))
|
|
241
|
+
raise DifferentNumeralTypeError(
|
|
242
|
+
"Cannot convert Roman numerals to another script"
|
|
243
|
+
)
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#
|
|
2
|
+
# SPDX-FileCopyrightText: 2024 John Samuel <johnsamuelwrites@gmail.com>
|
|
3
|
+
#
|
|
4
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
"""Functions for converting numerals between scripts and notations"""
|
|
8
|
+
|
|
9
|
+
from multilingualprogramming.numeral.unicode_numeral import UnicodeNumeral
|
|
10
|
+
from multilingualprogramming.unicode_string import (
|
|
11
|
+
get_unicode_character_string,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class NumeralConverter:
|
|
16
|
+
"""
|
|
17
|
+
Utility class for converting numerals between scripts and formats.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
# Unicode superscript digits for scientific notation
|
|
21
|
+
SUPERSCRIPT_MAP = {
|
|
22
|
+
"0": "\u2070",
|
|
23
|
+
"1": "\u00b9",
|
|
24
|
+
"2": "\u00b2",
|
|
25
|
+
"3": "\u00b3",
|
|
26
|
+
"4": "\u2074",
|
|
27
|
+
"5": "\u2075",
|
|
28
|
+
"6": "\u2076",
|
|
29
|
+
"7": "\u2077",
|
|
30
|
+
"8": "\u2078",
|
|
31
|
+
"9": "\u2079",
|
|
32
|
+
"-": "\u207b",
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
SUPERSCRIPT_REVERSE = {v: k for k, v in SUPERSCRIPT_MAP.items()}
|
|
36
|
+
|
|
37
|
+
@staticmethod
|
|
38
|
+
def convert(numeral, target_language):
|
|
39
|
+
"""
|
|
40
|
+
Convert a UnicodeNumeral to a different script.
|
|
41
|
+
|
|
42
|
+
Parameters:
|
|
43
|
+
numeral (UnicodeNumeral): The numeral to convert
|
|
44
|
+
target_language (str): Target language name (e.g., "ARABIC-INDIC")
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
UnicodeNumeral: A new numeral in the target script
|
|
48
|
+
"""
|
|
49
|
+
return numeral.convert_to(target_language)
|
|
50
|
+
|
|
51
|
+
@staticmethod
|
|
52
|
+
def to_scientific(numeral, language=None):
|
|
53
|
+
"""
|
|
54
|
+
Convert a numeral to scientific notation string.
|
|
55
|
+
|
|
56
|
+
Parameters:
|
|
57
|
+
numeral (UnicodeNumeral): The numeral to convert
|
|
58
|
+
language (str): Target language for output (default: same as input)
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
str: Scientific notation string (e.g., "1.23×10³")
|
|
62
|
+
"""
|
|
63
|
+
value = numeral.to_decimal()
|
|
64
|
+
lang = language or numeral.language_name or "DIGIT"
|
|
65
|
+
|
|
66
|
+
if value == 0:
|
|
67
|
+
zero = get_unicode_character_string(lang, 0)
|
|
68
|
+
return zero
|
|
69
|
+
|
|
70
|
+
# Calculate mantissa and exponent
|
|
71
|
+
negative = value < 0
|
|
72
|
+
abs_value = abs(value)
|
|
73
|
+
|
|
74
|
+
exponent = 0
|
|
75
|
+
mantissa = abs_value
|
|
76
|
+
if mantissa >= 10:
|
|
77
|
+
while mantissa >= 10:
|
|
78
|
+
mantissa /= 10
|
|
79
|
+
exponent += 1
|
|
80
|
+
elif 0 < mantissa < 1:
|
|
81
|
+
while mantissa < 1:
|
|
82
|
+
mantissa *= 10
|
|
83
|
+
exponent -= 1
|
|
84
|
+
|
|
85
|
+
# Format mantissa
|
|
86
|
+
if mantissa == int(mantissa):
|
|
87
|
+
mantissa = int(mantissa)
|
|
88
|
+
|
|
89
|
+
# Build the string
|
|
90
|
+
mantissa_val = -mantissa if negative else mantissa
|
|
91
|
+
if isinstance(mantissa_val, float):
|
|
92
|
+
# Round to avoid floating point artifacts
|
|
93
|
+
mantissa_val = round(mantissa_val, 10)
|
|
94
|
+
|
|
95
|
+
if exponent == 0:
|
|
96
|
+
return get_unicode_character_string(lang, mantissa_val)
|
|
97
|
+
|
|
98
|
+
mantissa_str = get_unicode_character_string(lang, mantissa_val)
|
|
99
|
+
ten_str = get_unicode_character_string(lang, 10)
|
|
100
|
+
|
|
101
|
+
# Build superscript exponent
|
|
102
|
+
exp_str = ""
|
|
103
|
+
for char in str(exponent):
|
|
104
|
+
exp_str += NumeralConverter.SUPERSCRIPT_MAP.get(char, char)
|
|
105
|
+
|
|
106
|
+
return f"{mantissa_str}\u00d7{ten_str}{exp_str}"
|
|
107
|
+
|
|
108
|
+
@staticmethod
|
|
109
|
+
def from_scientific(sci_str):
|
|
110
|
+
"""
|
|
111
|
+
Parse a scientific notation string into a UnicodeNumeral.
|
|
112
|
+
|
|
113
|
+
Parameters:
|
|
114
|
+
sci_str (str): Scientific notation string (e.g., "1.23×10³")
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
UnicodeNumeral: The parsed numeral
|
|
118
|
+
"""
|
|
119
|
+
# Split on multiplication sign
|
|
120
|
+
if "\u00d7" in sci_str:
|
|
121
|
+
parts = sci_str.split("\u00d7", 1)
|
|
122
|
+
mantissa_str = parts[0]
|
|
123
|
+
base_exp_str = parts[1]
|
|
124
|
+
else:
|
|
125
|
+
# No scientific notation, just a plain number
|
|
126
|
+
return UnicodeNumeral(sci_str)
|
|
127
|
+
|
|
128
|
+
# Parse mantissa
|
|
129
|
+
mantissa_numeral = UnicodeNumeral(mantissa_str)
|
|
130
|
+
mantissa = mantissa_numeral.to_decimal()
|
|
131
|
+
lang = mantissa_numeral.language_name
|
|
132
|
+
|
|
133
|
+
# Parse exponent from superscript digits after the base
|
|
134
|
+
# Find where superscripts start
|
|
135
|
+
exp_chars = ""
|
|
136
|
+
base_chars = ""
|
|
137
|
+
for char in base_exp_str:
|
|
138
|
+
if char in NumeralConverter.SUPERSCRIPT_REVERSE:
|
|
139
|
+
exp_chars += NumeralConverter.SUPERSCRIPT_REVERSE[char]
|
|
140
|
+
else:
|
|
141
|
+
base_chars += char
|
|
142
|
+
|
|
143
|
+
exponent = int(exp_chars) if exp_chars else 0
|
|
144
|
+
|
|
145
|
+
# Calculate final value, rounding to avoid floating point artifacts
|
|
146
|
+
result = mantissa * (10 ** exponent)
|
|
147
|
+
result = round(result, 10)
|
|
148
|
+
if result == int(result):
|
|
149
|
+
result = int(result)
|
|
150
|
+
|
|
151
|
+
return UnicodeNumeral(get_unicode_character_string(lang, result))
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
#
|
|
2
|
+
# SPDX-FileCopyrightText: 2022 John Samuel <johnsamuelwrites@gmail.com>
|
|
3
|
+
#
|
|
4
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
"""Functions to handle Roman numerals
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from operator import invert, neg
|
|
11
|
+
from roman import fromRoman, toRoman
|
|
12
|
+
from multilingualprogramming.exceptions import (
|
|
13
|
+
InvalidNumeralCharacterError,
|
|
14
|
+
)
|
|
15
|
+
from multilingualprogramming.numeral.abstract_numeral import AbstractNumeral
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class RomanNumeral(AbstractNumeral):
|
|
19
|
+
"""
|
|
20
|
+
Handling Roman numerals
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
roman_numerals_list = [
|
|
24
|
+
"X",
|
|
25
|
+
"V",
|
|
26
|
+
"I",
|
|
27
|
+
"L",
|
|
28
|
+
"C",
|
|
29
|
+
"D",
|
|
30
|
+
"M",
|
|
31
|
+
"x",
|
|
32
|
+
"v",
|
|
33
|
+
"i",
|
|
34
|
+
"l",
|
|
35
|
+
"c",
|
|
36
|
+
"d",
|
|
37
|
+
"m",
|
|
38
|
+
"Ⅰ",
|
|
39
|
+
"Ⅱ",
|
|
40
|
+
"Ⅲ",
|
|
41
|
+
"Ⅳ",
|
|
42
|
+
"Ⅴ",
|
|
43
|
+
"Ⅵ",
|
|
44
|
+
"Ⅶ",
|
|
45
|
+
"Ⅷ",
|
|
46
|
+
"Ⅸ",
|
|
47
|
+
"Ⅹ",
|
|
48
|
+
"Ⅺ",
|
|
49
|
+
"Ⅻ,Ⅼ",
|
|
50
|
+
"Ⅽ",
|
|
51
|
+
"Ⅾ",
|
|
52
|
+
"Ⅿ",
|
|
53
|
+
"ↀ",
|
|
54
|
+
"ↁ",
|
|
55
|
+
"ↂ",
|
|
56
|
+
"ↇ",
|
|
57
|
+
"ↈ",
|
|
58
|
+
"ⅰ",
|
|
59
|
+
"ⅱ",
|
|
60
|
+
"ⅲ",
|
|
61
|
+
"ⅳ",
|
|
62
|
+
"ⅴ",
|
|
63
|
+
"ⅵ",
|
|
64
|
+
"ⅶ",
|
|
65
|
+
"ⅷ",
|
|
66
|
+
"ⅷ",
|
|
67
|
+
"ⅸ",
|
|
68
|
+
"ⅹ",
|
|
69
|
+
"ⅺ",
|
|
70
|
+
"ⅻ",
|
|
71
|
+
"ⅼ",
|
|
72
|
+
"ⅽ",
|
|
73
|
+
"ⅾ",
|
|
74
|
+
"ⅿ",
|
|
75
|
+
"ↅ",
|
|
76
|
+
"ↆ",
|
|
77
|
+
"Ↄ",
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
@classmethod
|
|
81
|
+
def __verify_roman_characters__(cls, self, numstr: str):
|
|
82
|
+
"""
|
|
83
|
+
Verify whether each character is a Roman character
|
|
84
|
+
"""
|
|
85
|
+
for character in numstr:
|
|
86
|
+
if character not in self.roman_numerals_list:
|
|
87
|
+
raise InvalidNumeralCharacterError(
|
|
88
|
+
"Not a valid number, contains the character: " + character
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
@staticmethod
|
|
92
|
+
def is_roman_numeral(numstr: str) -> bool:
|
|
93
|
+
"""
|
|
94
|
+
Verify whether each character is a Roman character
|
|
95
|
+
"""
|
|
96
|
+
for character in numstr:
|
|
97
|
+
if character not in RomanNumeral.roman_numerals_list:
|
|
98
|
+
return False
|
|
99
|
+
return True
|
|
100
|
+
|
|
101
|
+
def __init__(self, numstr: str):
|
|
102
|
+
super().__init__(numstr)
|
|
103
|
+
self.numstr = numstr
|
|
104
|
+
self.__verify_roman_characters__(self, numstr)
|
|
105
|
+
|
|
106
|
+
def to_decimal(self):
|
|
107
|
+
"""
|
|
108
|
+
Returns the number associated with the number string (Roman numeral)
|
|
109
|
+
given by the user
|
|
110
|
+
|
|
111
|
+
return:
|
|
112
|
+
number: number associated with the number string
|
|
113
|
+
"""
|
|
114
|
+
return fromRoman(self.numstr)
|
|
115
|
+
|
|
116
|
+
@staticmethod
|
|
117
|
+
def get_roman_numerals() -> list:
|
|
118
|
+
"""
|
|
119
|
+
Get list of Roman numerals
|
|
120
|
+
"""
|
|
121
|
+
return RomanNumeral.roman_numerals_list
|
|
122
|
+
|
|
123
|
+
def set_roman_numerals(self, numerals: list):
|
|
124
|
+
"""
|
|
125
|
+
Set list of Roman numerals
|
|
126
|
+
"""
|
|
127
|
+
self.roman_numerals_list = numerals
|
|
128
|
+
|
|
129
|
+
def __str__(self):
|
|
130
|
+
"""
|
|
131
|
+
Returns the original number string (Roman numeral)
|
|
132
|
+
|
|
133
|
+
return:
|
|
134
|
+
numstr: original number string
|
|
135
|
+
"""
|
|
136
|
+
return self.numstr
|
|
137
|
+
|
|
138
|
+
def __repr__(self):
|
|
139
|
+
"""
|
|
140
|
+
Returns the representation (Roman numeral) of an instance
|
|
141
|
+
|
|
142
|
+
return:
|
|
143
|
+
reprstr: representation of an instance
|
|
144
|
+
"""
|
|
145
|
+
return f'RomanNumeral("{self.numstr}")'
|
|
146
|
+
|
|
147
|
+
def __add__(self, second):
|
|
148
|
+
"""
|
|
149
|
+
Add a RomanNumeral with a numeral or another RomanNumeral
|
|
150
|
+
|
|
151
|
+
return:
|
|
152
|
+
RomanNumeral: returns the sum of a RomanNumeral
|
|
153
|
+
"""
|
|
154
|
+
if isinstance(second, RomanNumeral):
|
|
155
|
+
return RomanNumeral(toRoman(self.to_decimal() + second.to_decimal()))
|
|
156
|
+
raise TypeError("Cannot substract a Roman numeral with a non-Roman numeral")
|
|
157
|
+
|
|
158
|
+
def __mul__(self, second):
|
|
159
|
+
"""
|
|
160
|
+
Multiply a RomanNumeral with a numeral or another RomanNumeral
|
|
161
|
+
|
|
162
|
+
return:
|
|
163
|
+
RomanNumeral: multiplication of the two RomanNumeral values
|
|
164
|
+
"""
|
|
165
|
+
if isinstance(second, RomanNumeral):
|
|
166
|
+
return RomanNumeral(toRoman(self.to_decimal() * second.to_decimal()))
|
|
167
|
+
raise TypeError("Cannot multiply a Roman numeral with a non-Roman numeral")
|
|
168
|
+
|
|
169
|
+
def __sub__(self, second):
|
|
170
|
+
"""
|
|
171
|
+
Substraction of Roman Numerals
|
|
172
|
+
|
|
173
|
+
return:
|
|
174
|
+
AbstractNumeral: returns the difference
|
|
175
|
+
"""
|
|
176
|
+
if isinstance(second, RomanNumeral):
|
|
177
|
+
return RomanNumeral(toRoman(self.to_decimal() - second.to_decimal()))
|
|
178
|
+
raise TypeError("Cannot substract a Roman numeral with a non-Roman numeral")
|
|
179
|
+
|
|
180
|
+
def __lshift__(self, second):
|
|
181
|
+
"""
|
|
182
|
+
Left-shifting of Roman Numerals
|
|
183
|
+
|
|
184
|
+
return:
|
|
185
|
+
AbstractNumeral: returns the left shifted value
|
|
186
|
+
"""
|
|
187
|
+
if isinstance(second, RomanNumeral):
|
|
188
|
+
return RomanNumeral(toRoman(self.to_decimal() << second.to_decimal()))
|
|
189
|
+
raise TypeError("Cannot left-shift a Roman numeral with a non-Roman numeral")
|
|
190
|
+
|
|
191
|
+
def __rshift__(self, second):
|
|
192
|
+
"""
|
|
193
|
+
Right-shifting of Roman Numerals
|
|
194
|
+
|
|
195
|
+
return:
|
|
196
|
+
AbstractNumeral: returns the right shifted value
|
|
197
|
+
"""
|
|
198
|
+
if isinstance(second, RomanNumeral):
|
|
199
|
+
return RomanNumeral(toRoman(self.to_decimal() >> second.to_decimal()))
|
|
200
|
+
raise TypeError("Cannot right-shift a Roman numeral with a non-Roman numeral")
|
|
201
|
+
|
|
202
|
+
def __truediv__(self, second):
|
|
203
|
+
"""
|
|
204
|
+
True division of Roman Numerals
|
|
205
|
+
|
|
206
|
+
return:
|
|
207
|
+
AbstractNumeral: returns the value after true division
|
|
208
|
+
"""
|
|
209
|
+
if isinstance(second, RomanNumeral):
|
|
210
|
+
return RomanNumeral(toRoman(self.to_decimal() / second.to_decimal()))
|
|
211
|
+
raise TypeError("Cannot divide a Roman numeral with a non-Roman numeral")
|
|
212
|
+
|
|
213
|
+
def __floordiv__(self, second):
|
|
214
|
+
"""
|
|
215
|
+
Floor division of Roman Numerals
|
|
216
|
+
|
|
217
|
+
return:
|
|
218
|
+
AbstractNumeral: returns the value after floor division
|
|
219
|
+
"""
|
|
220
|
+
if isinstance(second, RomanNumeral):
|
|
221
|
+
return RomanNumeral(toRoman(self.to_decimal() // second.to_decimal()))
|
|
222
|
+
raise TypeError("Cannot floor divide a Roman numeral with a non-Roman numeral")
|
|
223
|
+
|
|
224
|
+
def __neg__(self):
|
|
225
|
+
"""
|
|
226
|
+
Negation of Roman Numerals
|
|
227
|
+
|
|
228
|
+
return:
|
|
229
|
+
AbstractNumeral: returns the negation
|
|
230
|
+
"""
|
|
231
|
+
return RomanNumeral(toRoman(neg(self.to_decimal())))
|
|
232
|
+
|
|
233
|
+
def __pow__(self, second):
|
|
234
|
+
"""
|
|
235
|
+
Power of Roman Numerals
|
|
236
|
+
|
|
237
|
+
return:
|
|
238
|
+
AbstractNumeral: returns the power
|
|
239
|
+
"""
|
|
240
|
+
if isinstance(second, RomanNumeral):
|
|
241
|
+
return RomanNumeral(toRoman(self.to_decimal() ** second.to_decimal()))
|
|
242
|
+
raise TypeError(
|
|
243
|
+
"Cannot compute power of a Roman numeral with a non-Roman numeral"
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
def __mod__(self, second):
|
|
247
|
+
"""
|
|
248
|
+
Modulus of Roman Numerals
|
|
249
|
+
|
|
250
|
+
return:
|
|
251
|
+
AbstractNumeral: returns the modulus value
|
|
252
|
+
"""
|
|
253
|
+
if isinstance(second, RomanNumeral):
|
|
254
|
+
return RomanNumeral(toRoman(self.to_decimal() % second.to_decimal()))
|
|
255
|
+
raise TypeError(
|
|
256
|
+
"Cannot compute modulus of a Roman numeral with a non-Roman numeral"
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
def __xor__(self, second):
|
|
260
|
+
"""
|
|
261
|
+
XOR value of Roman Numerals
|
|
262
|
+
|
|
263
|
+
return:
|
|
264
|
+
AbstractNumeral: returns the XOR value
|
|
265
|
+
"""
|
|
266
|
+
if isinstance(second, RomanNumeral):
|
|
267
|
+
return RomanNumeral(toRoman(self.to_decimal() ^ second.to_decimal()))
|
|
268
|
+
raise TypeError(
|
|
269
|
+
"Cannot compute XOR of a Roman numeral with a non-Roman numeral"
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
def __invert__(self):
|
|
273
|
+
"""
|
|
274
|
+
Bitwise inversion value of Roman Numerals
|
|
275
|
+
|
|
276
|
+
return:
|
|
277
|
+
AbstractNumeral: returns the bitwise-inverted value
|
|
278
|
+
"""
|
|
279
|
+
return RomanNumeral(invert(toRoman(self.to_decimal())))
|
|
280
|
+
|
|
281
|
+
def __or__(self, second):
|
|
282
|
+
"""
|
|
283
|
+
OR value of Roman Numerals
|
|
284
|
+
|
|
285
|
+
return:
|
|
286
|
+
AbstractNumeral: returns the OR value
|
|
287
|
+
"""
|
|
288
|
+
if isinstance(second, RomanNumeral):
|
|
289
|
+
return RomanNumeral(toRoman(self.to_decimal() | second.to_decimal()))
|
|
290
|
+
raise TypeError(
|
|
291
|
+
"Cannot computer OR of a Roman numeral with a non-Roman numeral"
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
def __abs__(self):
|
|
295
|
+
"""
|
|
296
|
+
Absolute value of Roman Numerals
|
|
297
|
+
|
|
298
|
+
return:
|
|
299
|
+
RomanNumeral: returns the absolute value
|
|
300
|
+
"""
|
|
301
|
+
return RomanNumeral(toRoman(abs(self.to_decimal())))
|