264 lines
9.1 KiB
Python
264 lines
9.1 KiB
Python
from tests._helpers import (
|
|
unittest, parameterized, Fraction, time,
|
|
run_test, run_compiler_error_test,
|
|
ArrayIndex, ArraySlice, Bool, BinOp, BuiltIn, DataArray, DataDict, DataRangeArray,
|
|
Defini, Continva, Designa, DesignaDestructure, DesignaIndex, DumStatement,
|
|
Erumpe, ExpressionStatement, Fvnctio, ID, InterpolatedString, Invoca,
|
|
ModuleCall, Nullus, Numeral, PerStatement, Program, Redi, SiStatement,
|
|
String, TemptaStatement, UnaryMinus, UnaryNot, Fractio, frac_to_fraction,
|
|
fraction_to_frac, num_to_int, int_to_num, make_string,
|
|
ValInt, ValStr, ValBool, ValList, ValDict, ValNul, ValFunc, ValFrac,
|
|
CentvrionError, _RUNTIME_C, _cent_rng,
|
|
Lexer, Parser, compile_program,
|
|
os, subprocess, tempfile, StringIO, patch,
|
|
)
|
|
|
|
|
|
# --- FRACTIO module ---
|
|
|
|
fractio_tests = [
|
|
# Basic fraction literals
|
|
("CVM FRACTIO\nIIIS",
|
|
Program([ModuleCall("FRACTIO")], [ExpressionStatement(Fractio("IIIS"))]),
|
|
ValFrac(Fraction(7, 2))),
|
|
("CVM FRACTIO\nS",
|
|
Program([ModuleCall("FRACTIO")], [ExpressionStatement(Fractio("S"))]),
|
|
ValFrac(Fraction(1, 2))),
|
|
("CVM FRACTIO\nS:.",
|
|
Program([ModuleCall("FRACTIO")], [ExpressionStatement(Fractio("S:."))]),
|
|
ValFrac(Fraction(3, 4))),
|
|
("CVM FRACTIO\n.",
|
|
Program([ModuleCall("FRACTIO")], [ExpressionStatement(Fractio("."))]),
|
|
ValFrac(Fraction(1, 12))),
|
|
("CVM FRACTIO\n:.",
|
|
Program([ModuleCall("FRACTIO")], [ExpressionStatement(Fractio(":."))]),
|
|
ValFrac(Fraction(1, 4))),
|
|
# Integer part with fraction
|
|
("CVM FRACTIO\nVIIS:|::",
|
|
Program([ModuleCall("FRACTIO")], [ExpressionStatement(Fractio("VIIS:|::"))]),
|
|
ValFrac(Fraction(7) + Fraction(100, 144))),
|
|
# Arithmetic
|
|
("CVM FRACTIO\nIIIS + S",
|
|
Program([ModuleCall("FRACTIO")], [
|
|
ExpressionStatement(BinOp(Fractio("IIIS"), Fractio("S"), "SYMBOL_PLUS"))
|
|
]),
|
|
ValFrac(Fraction(4))
|
|
),
|
|
("CVM FRACTIO\nIIIS - S",
|
|
Program([ModuleCall("FRACTIO")], [
|
|
ExpressionStatement(BinOp(Fractio("IIIS"), Fractio("S"), "SYMBOL_MINUS"))
|
|
]),
|
|
ValFrac(Fraction(3))
|
|
),
|
|
("CVM FRACTIO\nS * IV",
|
|
Program([ModuleCall("FRACTIO")], [
|
|
ExpressionStatement(BinOp(Fractio("S"), Numeral("IV"), "SYMBOL_TIMES"))
|
|
]),
|
|
ValFrac(Fraction(2))
|
|
),
|
|
# Division returns fraction
|
|
("CVM FRACTIO\nI / IV",
|
|
Program([ModuleCall("FRACTIO")], [
|
|
ExpressionStatement(BinOp(Numeral("I"), Numeral("IV"), "SYMBOL_DIVIDE"))
|
|
]),
|
|
ValFrac(Fraction(1, 4))
|
|
),
|
|
("CVM FRACTIO\nI / III",
|
|
Program([ModuleCall("FRACTIO")], [
|
|
ExpressionStatement(BinOp(Numeral("I"), Numeral("III"), "SYMBOL_DIVIDE"))
|
|
]),
|
|
ValFrac(Fraction(1, 3))
|
|
),
|
|
# Integer division still works without fractions in operands... but with FRACTIO returns ValFrac
|
|
("CVM FRACTIO\nX / II",
|
|
Program([ModuleCall("FRACTIO")], [
|
|
ExpressionStatement(BinOp(Numeral("X"), Numeral("II"), "SYMBOL_DIVIDE"))
|
|
]),
|
|
ValFrac(Fraction(5))
|
|
),
|
|
# Modulo on fractions: 7/2 RELIQVVM 3/2 = 1/2 (7/2 / 3/2 = 7/3, floor=2, 7/2 - 3 = 1/2)
|
|
("CVM FRACTIO\nIIIS RELIQVVM IS",
|
|
Program([ModuleCall("FRACTIO")], [
|
|
ExpressionStatement(BinOp(Fractio("IIIS"), Fractio("IS"), "KEYWORD_RELIQVVM"))
|
|
]),
|
|
ValFrac(Fraction(1, 2))
|
|
),
|
|
# Modulo with mixed operand types: 5/2 RELIQVVM 1 = 1/2
|
|
("CVM FRACTIO\nIIS RELIQVVM I",
|
|
Program([ModuleCall("FRACTIO")], [
|
|
ExpressionStatement(BinOp(Fractio("IIS"), Numeral("I"), "KEYWORD_RELIQVVM"))
|
|
]),
|
|
ValFrac(Fraction(1, 2))
|
|
),
|
|
# Int operands under FRACTIO still return a fraction: 10 RELIQVVM 3 = 1 (as Fraction)
|
|
("CVM FRACTIO\nX RELIQVVM III",
|
|
Program([ModuleCall("FRACTIO")], [
|
|
ExpressionStatement(BinOp(Numeral("X"), Numeral("III"), "KEYWORD_RELIQVVM"))
|
|
]),
|
|
ValFrac(Fraction(1))
|
|
),
|
|
# Exact multiple under FRACTIO: 3 RELIQVVM 3/2 = 0
|
|
("CVM FRACTIO\nIII RELIQVVM IS",
|
|
Program([ModuleCall("FRACTIO")], [
|
|
ExpressionStatement(BinOp(Numeral("III"), Fractio("IS"), "KEYWORD_RELIQVVM"))
|
|
]),
|
|
ValFrac(Fraction(0))
|
|
),
|
|
# String concatenation with fraction
|
|
("CVM FRACTIO\nDIC(IIIS & \"!\")",
|
|
Program([ModuleCall("FRACTIO")], [
|
|
ExpressionStatement(BuiltIn("DIC", [BinOp(Fractio("IIIS"), String("!"), "SYMBOL_AMPERSAND")]))
|
|
]),
|
|
ValStr("IIIS!"), "IIIS!\n"
|
|
),
|
|
# Negative fractions
|
|
("CVM FRACTIO\nCVM SVBNVLLA\n-IIS",
|
|
Program([ModuleCall("FRACTIO"),ModuleCall("SVBNVLLA")],[
|
|
ExpressionStatement(UnaryMinus(Fractio("IIS")))
|
|
]),
|
|
ValFrac(Fraction(-5,2))
|
|
)
|
|
]
|
|
|
|
class TestFractio(unittest.TestCase):
|
|
@parameterized.expand(fractio_tests)
|
|
def test_fractio(self, source, nodes, value, output=""):
|
|
run_test(self, source, nodes, value, output)
|
|
|
|
|
|
fractio_comparison_tests = [
|
|
# fraction vs fraction
|
|
("CVM FRACTIO\nIIIS PLVS III",
|
|
Program([ModuleCall("FRACTIO")], [
|
|
ExpressionStatement(BinOp(Fractio("IIIS"), Numeral("III"), "KEYWORD_PLVS"))
|
|
]),
|
|
ValBool(True)
|
|
),
|
|
("CVM FRACTIO\nIII MINVS IIIS",
|
|
Program([ModuleCall("FRACTIO")], [
|
|
ExpressionStatement(BinOp(Numeral("III"), Fractio("IIIS"), "KEYWORD_MINVS"))
|
|
]),
|
|
ValBool(True)
|
|
),
|
|
("CVM FRACTIO\nIIIS MINVS IV",
|
|
Program([ModuleCall("FRACTIO")], [
|
|
ExpressionStatement(BinOp(Fractio("IIIS"), Numeral("IV"), "KEYWORD_MINVS"))
|
|
]),
|
|
ValBool(True)
|
|
),
|
|
("CVM FRACTIO\nIV PLVS IIIS",
|
|
Program([ModuleCall("FRACTIO")], [
|
|
ExpressionStatement(BinOp(Numeral("IV"), Fractio("IIIS"), "KEYWORD_PLVS"))
|
|
]),
|
|
ValBool(True)
|
|
),
|
|
("CVM FRACTIO\nIIIS PLVS IIIS",
|
|
Program([ModuleCall("FRACTIO")], [
|
|
ExpressionStatement(BinOp(Fractio("IIIS"), Fractio("IIIS"), "KEYWORD_PLVS"))
|
|
]),
|
|
ValBool(False)
|
|
),
|
|
("CVM FRACTIO\nIIIS MINVS IIIS",
|
|
Program([ModuleCall("FRACTIO")], [
|
|
ExpressionStatement(BinOp(Fractio("IIIS"), Fractio("IIIS"), "KEYWORD_MINVS"))
|
|
]),
|
|
ValBool(False)
|
|
),
|
|
# HAVD_PLVS / HAVD_MINVS on fractions — equality boundary distinguishes from MINVS / PLVS
|
|
("CVM FRACTIO\nIIIS HAVD_PLVS III",
|
|
Program([ModuleCall("FRACTIO")], [
|
|
ExpressionStatement(BinOp(Fractio("IIIS"), Numeral("III"), "KEYWORD_HAVD_PLVS"))
|
|
]),
|
|
ValBool(False) # 3.5 <= 3 is false
|
|
),
|
|
("CVM FRACTIO\nIIIS HAVD_MINVS IIIS",
|
|
Program([ModuleCall("FRACTIO")], [
|
|
ExpressionStatement(BinOp(Fractio("IIIS"), Fractio("IIIS"), "KEYWORD_HAVD_MINVS"))
|
|
]),
|
|
ValBool(True) # 3.5 >= 3.5 is true (equality boundary)
|
|
),
|
|
("CVM FRACTIO\nIIIS HAVD_PLVS IIIS",
|
|
Program([ModuleCall("FRACTIO")], [
|
|
ExpressionStatement(BinOp(Fractio("IIIS"), Fractio("IIIS"), "KEYWORD_HAVD_PLVS"))
|
|
]),
|
|
ValBool(True) # 3.5 <= 3.5 is true (equality boundary)
|
|
),
|
|
# equality: fraction == fraction
|
|
("CVM FRACTIO\nIIIS EST IIIS",
|
|
Program([ModuleCall("FRACTIO")], [
|
|
ExpressionStatement(BinOp(Fractio("IIIS"), Fractio("IIIS"), "KEYWORD_EST"))
|
|
]),
|
|
ValBool(True)
|
|
),
|
|
("CVM FRACTIO\nIIIS EST IV",
|
|
Program([ModuleCall("FRACTIO")], [
|
|
ExpressionStatement(BinOp(Fractio("IIIS"), Numeral("IV"), "KEYWORD_EST"))
|
|
]),
|
|
ValBool(False)
|
|
),
|
|
# equality: fraction == whole number (ValFrac(4) vs ValInt(4))
|
|
("CVM FRACTIO\nIIIS + S EST IV",
|
|
Program([ModuleCall("FRACTIO")], [
|
|
ExpressionStatement(BinOp(
|
|
BinOp(Fractio("IIIS"), Fractio("S"), "SYMBOL_PLUS"),
|
|
Numeral("IV"), "KEYWORD_EST"))
|
|
]),
|
|
ValBool(True)
|
|
),
|
|
("CVM FRACTIO\nS + S EST I",
|
|
Program([ModuleCall("FRACTIO")], [
|
|
ExpressionStatement(BinOp(
|
|
BinOp(Fractio("S"), Fractio("S"), "SYMBOL_PLUS"),
|
|
Numeral("I"), "KEYWORD_EST"))
|
|
]),
|
|
ValBool(True)
|
|
),
|
|
]
|
|
|
|
class TestFractioComparisons(unittest.TestCase):
|
|
@parameterized.expand(fractio_comparison_tests)
|
|
def test_fractio_comparison(self, source, nodes, value):
|
|
run_test(self, source, nodes, value)
|
|
|
|
|
|
class TestFractioHelpers(unittest.TestCase):
|
|
def test_frac_to_fraction_ordering(self):
|
|
with self.assertRaises(CentvrionError):
|
|
frac_to_fraction(".S") # . before S violates highest-to-lowest
|
|
|
|
def test_frac_to_fraction_level_overflow(self):
|
|
with self.assertRaises(CentvrionError):
|
|
frac_to_fraction("SSSSSS") # SS means S twice = 12/12 = 1, violating < 12/12 constraint
|
|
|
|
def test_frac_to_fraction_iiis(self):
|
|
self.assertEqual(frac_to_fraction("IIIS"), Fraction(7, 2))
|
|
|
|
def test_frac_to_fraction_s_colon_dot(self):
|
|
self.assertEqual(frac_to_fraction("S:."), Fraction(3, 4))
|
|
|
|
def test_frac_to_fraction_dot(self):
|
|
self.assertEqual(frac_to_fraction("."), Fraction(1, 12))
|
|
|
|
def test_frac_to_fraction_multilevel(self):
|
|
self.assertEqual(frac_to_fraction("VIIS:|::"), Fraction(7) + Fraction(100, 144))
|
|
|
|
def test_fraction_to_frac_iiis(self):
|
|
self.assertEqual(fraction_to_frac(Fraction(7, 2)), "IIIS")
|
|
|
|
def test_fraction_to_frac_s_colon_dot(self):
|
|
self.assertEqual(fraction_to_frac(Fraction(3, 4)), "S:.")
|
|
|
|
def test_fraction_to_frac_dot(self):
|
|
self.assertEqual(fraction_to_frac(Fraction(1, 12)), ".")
|
|
|
|
def test_fraction_to_frac_multilevel(self):
|
|
self.assertEqual(
|
|
fraction_to_frac(Fraction(7) + Fraction(100, 144)),
|
|
"VIIS:|::"
|
|
)
|
|
|
|
def test_roundtrip(self):
|
|
# Only canonical forms roundtrip — fraction_to_frac always uses max colons before dots
|
|
for s in ["IIIS", "S:.", ".", "::", "VIIS:|::", "S"]:
|
|
self.assertEqual(fraction_to_frac(frac_to_fraction(s)), s)
|