This commit is contained in:
NikolajDanger
2022-06-14 08:59:38 +02:00
parent f2a15dd586
commit ba8451638d
5 changed files with 182 additions and 80 deletions

View File

@@ -1,17 +1,23 @@
import re
import random
import roman
from rply.token import BaseBox as BB
from rply.token import BaseBox
NUMERALS = [
("I", 1),
("V", 5),
("X", 10),
("L", 50),
("C", 100),
("D", 500),
("M", 1000)
]
NUMERALS = {
"I": 1,
"IV": 4,
"V": 5,
"IX": 9,
"X": 10,
"XL": 40,
"L": 50,
"XC": 90,
"C": 100,
"CD": 400,
"D": 500,
"CM": 900,
"M": 1000
}
def rep_join(l):
format_string = ',\n'.join(
@@ -23,24 +29,85 @@ def rep_join(l):
return format_string
# TODO: Magnum
def num_to_int(n):
return roman.fromRoman(n)
def single_num_to_int(i, m):
if i[-1] == "_":
if not m:
raise Exception(
"Cannot calculate numbers above 3999 without 'MAGNVM' module"
)
def make_string(n):
if i[0] != "I":
raise Exception(
"Cannot use 'I' with thousands operator, use 'M' instead"
)
return 1000 * single_num_to_int(i[:-1], m)
else:
return NUMERALS[i]
def num_to_int(n, m):
chars = re.findall(r"[IVXLCDM]_*", n)
nums = [single_num_to_int(i, m) for i in chars]
new_nums = nums.copy()
for x, num in enumerate(nums[:-3]):
if all(num == nums[x+i] for i in range(0,3)):
raise Exception(n, "is not a valid roman numeral")
while True:
for x, num in enumerate(nums[:-1]):
if num < nums[x+1]:
if (not nums[x+1] % 5) and (num in [nums[x+1]/5, nums[x+1]/10]):
new_nums[x] = nums[x+1] - num
new_nums[x+1] = 0
break
else:
raise Exception(n, "is not a valid roman numeral")
if new_nums != nums:
nums = [i for i in new_nums if i != 0]
new_nums = nums
else:
break
if nums != sorted(nums)[::-1]:
raise Exception(n, "is not a valid roman numeral")
return sum(nums)
def int_to_num(n, m):
if n > 3999:
if not m:
raise Exception(
"Cannot display numbers above 3999 without 'MAGNVM' module"
)
thousands_chars = re.findall(r"[IVXLCDM]_*", int_to_num(n//1000, m))
thousands = ''.join([
"M" if i == "I" else i+"_"
for i in thousands_chars
])
return thousands + int_to_num(n % 1000, m)
else:
nums = []
while n > 0:
for num, i in list(NUMERALS.items())[::-1]:
if n >= i:
nums.append(num)
n -= i
break
return ''.join(nums)
def make_string(n, m):
if isinstance(n, str):
return n
elif isinstance(n, int):
return roman.toRoman(n)
return int_to_num(n, m)
elif isinstance(n, list):
return f"[{' '.join([make_string(i) for i in n])}]"
return f"[{' '.join([make_string(i, m) for i in n])}]"
else:
raise Exception(n)
class BaseBox(BB):
def eval(self, vtable, ftable, modules):
return None
class ExpressionStatement(BaseBox):
def __init__(self, expression) -> None:
self.expression = expression
@@ -94,8 +161,8 @@ class Numeral(BaseBox):
def __repr__(self):
return f"Numeral({self.value})"
def eval(self, *_):
return num_to_int(self.value)
def eval(self, *args):
return num_to_int(self.value, "MAGNVM" in args[2])
class Bool(BaseBox):
def __init__(self, value) -> None:
@@ -354,15 +421,20 @@ class BuiltIn(BaseBox):
match self.builtin:
case "AVDI_NVMERVS":
return num_to_int(input())
return num_to_int(input(), "MAGNVM" in modules)
case "DICE":
print(' '.join(make_string(i) for i in parameters))
print(' '.join(
make_string(i, "MAGNVM" in modules) for i in parameters)
)
return None
case "ERVMPE":
vtable["ERVMPE"] = True
return None
case "FORTIS_NVMERVS":
# TODO: Fors
if "FORS" not in modules:
raise Exception(
"Cannot use 'FORTIS_NVMERVS' without module 'FORS'"
)
return random.randint(parameters[0], parameters[1])
case _:
raise Exception(self.builtin)