diff --git a/README.md b/README.md index 97744ed..5b3a431 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ DAIDE parser using [parsimonious](https://github.com/erikrose/parsimonious). Par ## Use -Using the grammar in grammar.py, you can create a parse tree from a DAIDE press message or reply. The nodes of the parse tree can be visited to return something more useful. The visiting rules for each of the nodes of the parse tree are defined in node_visitor.py. +Using the grammar in `grammar.py`, you can create a parse tree from a DAIDE press message or reply. The nodes of the parse tree can be visited to return something more useful. The visiting rules for each of the nodes of the parse tree are defined in `node_visitor.py`. Example: @@ -31,6 +31,18 @@ Example: If the DAIDE token is not in the grammar or if the message is malformed, the parser will just thrown an exception. We're currently working on returning a list of unrecognized tokens instead of just erroring out. + +In addition, DAIDE strings can be constructed using the classes in `keywords.py`. Each class has type hints that indicate the parameters that should be used. + +Example: + +```python3 +>>> from daidepp import AND, PRP, PCE +>>> str(AND(PRP(PCE("AUS")), PRP(PCE("AUS", "ENG")))) +`AND ( ( PRP ( PCE ( AUS ) ) ) ( PRP ( PCE ( AUS ENG ) ) ) ( PRP ( PCE ( AUS ENG FRA ) ) ) )` + ``` +Each class in `keywords.py` uses different parameters for instantiation, so it is recommended to carefully follow the type hints or checkout `tests/test_keywords.py`, which provices examples for each class. + ## Pull Requests Three files should be updated whenever making a PR: diff --git a/setup.cfg b/setup.cfg index 13f006f..d740a58 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = daidepp -version = 0.1.0 +version = 0.2.0 author = Byung Oh author_email = byung.oh@cynnovative.com description = "DAIDE Parser" diff --git a/src/daidepp/__init__.py b/src/daidepp/__init__.py index d17191d..0d1293c 100644 --- a/src/daidepp/__init__.py +++ b/src/daidepp/__init__.py @@ -1,2 +1,3 @@ from daidepp.daide_visitor import daide_visitor from daidepp.grammar import create_daide_grammar +from daidepp.keywords import * diff --git a/src/daidepp/constants.py b/src/daidepp/constants.py new file mode 100644 index 0000000..9161c33 --- /dev/null +++ b/src/daidepp/constants.py @@ -0,0 +1,170 @@ +from typing_extensions import Literal + +POWER = Literal["AUS", "ENG", "FRA", "GER", "ITA", "RUS", "TUR"] +UNIT_TYPE = Literal["AMY", "FLT"] + +PROVINCE_LAND_SEA = Literal[ + "ALB", + "ANK", + "APU", + "ARM", + "BEL", + "BER", + "BRE", + "BUL", + "CLY", + "CON", + "DEN", + "EDI", + "FIN", + "GAS", + "GRE", + "HOL", + "KIE", + "LON", + "LVN", + "LVP", + "MAR", + "NAF", + "NAP", + "NWY", + "PIC", + "PIE", + "POR", + "PRU", + "ROM", + "RUM", + "SEV", + "SMY", + "SPA", + "STP", + "SWE", + "SYR", + "TRI", + "TUN", + "TUS", + "VEN", + "YOR", + "WAL", +] +PROVINCE_LANDLOCK = Literal[ + "BOH", + "BUD", + "BUR", + "MOS", + "MUN", + "GAL", + "PAR", + "RUH", + "SER", + "SIL", + "TYR", + "UKR", + "VIE", + "WAR", +] +PROVINCE_SEA = Literal[ + "ADR", + "AEG", + "BAL", + "BAR", + "BLA", + "BOT", + "EAS", + "ENG", + "HEL", + "ION", + "IRI", + "LYO", + "MAO", + "NAO", + "NTH", + "NWG", + "SKA", + "TYS", + "WES", +] +PROVINCE_COAST = Literal[ + "STP NCS", "STP SCS", "SPA NCS", "SPA SCS", "BUL ECS", "BUL SCS" +] +PROVINCE = Literal[PROVINCE_LAND_SEA, PROVINCE_LANDLOCK, PROVINCE_SEA, PROVINCE_COAST] +PROVINCE_NO_COAST = Literal[PROVINCE_LAND_SEA, PROVINCE_LANDLOCK, PROVINCE_SEA] + +SEASON = Literal["SPR", "SUM", "FAL", "AUT", "WIN"] + +TRY_TOKENS = Literal[ + "PRP", + "PCE", + "ALY", + "VSS", + "DRW", + "SLO", + "NOT", + "NAR", + "YES", + "REJ", + "BWX", + "FCT", + "XDO", + "DMZ", + "AND", + "ORR", + "SCD", + "OCC", + "CHO", + "INS", + "QRY", + "THK", + "IDK", + "SUG", + "HOW", + "WHT", + "EXP", + "SRY", + "FOR", + "IFF", + "XOY", + "YDO", + "SND", + "FWD", + "BCC", + "WHY", + "POB", +] + +SUPPLY_CENTER = Literal[ + "ANK", + "BEL", + "BER", + "BRE", + "BUD", + "BUL", + "CON", + "DEN", + "EDI", + "GRE", + "HOL", + "KIE", + "LON", + "LVP", + "MAR", + "MOS", + "MUN", + "NAP", + "NWY", + "PAR", + "POR", + "ROM", + "RUM", + "SER", + "SEV", + "SMY", + "SPA", + "STP", + "SWE", + "TRI", + "TUN", + "VEN", + "VIE", + "WAR", +] diff --git a/src/daidepp/daide_visitor.py b/src/daidepp/daide_visitor.py index fc96e80..94cea12 100644 --- a/src/daidepp/daide_visitor.py +++ b/src/daidepp/daide_visitor.py @@ -1,5 +1,7 @@ from parsimonious.nodes import Node, NodeVisitor +from daidepp.keywords import * + class DAIDEVisitor(NodeVisitor): def visit_message(self, node, visited_children): diff --git a/src/daidepp/grammar.py b/src/daidepp/grammar.py index f856ef3..2e10445 100644 --- a/src/daidepp/grammar.py +++ b/src/daidepp/grammar.py @@ -65,7 +65,6 @@ def from_level(level: DAIDE_LEVEL, allow_just_arrangment: bool = False): LEVEL_10: GRAMMAR_DICT = { "pce": '"PCE" lpar power (ws power)* rpar', "ccl": '"CCL" lpar press_message rpar', - "fct": '"FCT" lpar arrangement rpar', "try": '"TRY" lpar try_tokens (ws try_tokens)* rpar', "huh": '"HUH" lpar press_message rpar', "prp": '"PRP" lpar arrangement rpar', @@ -187,10 +186,10 @@ def from_level(level: DAIDE_LEVEL, allow_just_arrangment: bool = False): # Explanations LEVEL_130: GRAMMAR_DICT = { "fct_thk_prp_ins": "fct / thk / prp / ins", - "qry_wht_prp_ins_sug": "qry / wht/ prp / ins / sug", # added sug because it looks like it's also supported at level 130 + "qry_exp_wht_prp_ins_sug": "qry / exp / wht / prp / ins / sug", # added sug because it looks like it's also supported at level 130 "why": '"WHY" lpar fct_thk_prp_ins rpar', "pob": '"POB" lpar why rpar', - "idk": '"IDK" lpar qry_wht_prp_ins_sug rpar', + "idk": '"IDK" lpar qry_exp_wht_prp_ins_sug rpar', "reply": f"{TRAIL_TOKEN}why / pob / idk", "try_tokens": f'{TRAIL_TOKEN}"WHY" / "POB"', } diff --git a/src/daidepp/keywords.py b/src/daidepp/keywords.py new file mode 100644 index 0000000..9e1e7ed --- /dev/null +++ b/src/daidepp/keywords.py @@ -0,0 +1,554 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import List, Optional, Union + +from daidepp.constants import * + + +@dataclass +class Unit: + power: POWER + unit_type: UNIT_TYPE + province: PROVINCE + + def __str__(self): + return f"{self.power} {self.unit_type} {self.province}" + + +@dataclass +class HLD: + unit: Unit + + def __str__(self): + return f"( {self.unit} ) HLD" + + +@dataclass +class MTO: + unit: Unit + province: PROVINCE + + def __str__(self): + return f"( {self.unit} ) MTO {self.province}" + + +@dataclass +class SUP: + unit_1: Unit + unit_2: Unit + province_no_coast: Optional[PROVINCE_NO_COAST] = None + + def __str__(self): + if not self.province_no_coast: + return f"( {self.unit_1} ) SUP ( {self.unit_2} )" + else: + return ( + f"( {self.unit_1} ) SUP ( {self.unit_2} ) MTO {self.province_no_coast}" + ) + + +@dataclass +class CVY: + unit_1: Unit + unit_2: Unit + province: PROVINCE + + def __str__(self): + return f"( {self.unit_1} ) CVY ( {self.unit_2} ) CTO {self.province}" + + +@dataclass +class MoveByCVY: + unit: Unit + province: PROVINCE + province_seas: List[PROVINCE_SEA] + + def __init__(self, unit, province, *province_seas): + self.unit = unit + self.province = province + self.province_seas = province_seas + + def __str__(self): + return ( + f"( {self.unit} ) CTO {self.province} VIA ( " + + " ".join(self.province_seas) + + " )" + ) + + +@dataclass +class RTO: + unit: Unit + province: PROVINCE + + def __str__(self): + return f"( {self.unit} ) RTO {self.province}" + + +@dataclass +class DSB: + unit: Unit + + def __str__(self): + return f"( {self.unit} ) DSB" + + +@dataclass +class BLD: + unit: Unit + + def __str__(self): + return f"( {self.unit} ) BLD" + + +@dataclass +class REM: + unit: Unit + + def __str__(self): + return f"( {self.unit} ) REM" + + +@dataclass +class WVE: + power: POWER + + def __str__(self): + return f"{self.power} WVE" + + +@dataclass +class Turn: + season: SEASON + year: int + + def __str__(self): + return f"{self.season} {self.year}" + + +@dataclass +class PCE: + powers: List[POWER] + + def __init__(self, *powers): + self.powers = powers + + def __str__(self): + return "PCE ( " + " ".join(self.powers) + " )" + + +@dataclass +class CCL: + press_message: PRESS_MESSAGE + + def __str__(self): + return f"CCL ( {self.press_message} )" + + +@dataclass +class TRY: + try_tokens: List[TRY_TOKENS] + + def __init__(self, *try_tokens): + self.try_tokens = try_tokens + + def __str__(self): + return "TRY ( " + " ".join(self.try_tokens) + " )" + + +@dataclass +class HUH: + press_message: PRESS_MESSAGE + + def __str__(self): + return f"HUH ( {self.press_message} )" + + +@dataclass +class PRP: + arrangement: ARRANGEMENT + + def __str__(self): + return f"PRP ( {self.arrangement} )" + + +@dataclass +class ALYVSS: + aly_powers: List[POWER] + vss_powers: List[POWER] + + def __str__(self): + return ( + "ALY ( " + + " ".join(self.aly_powers) + + " ) VSS ( " + + " ".join(self.vss_powers) + + " )" + ) + + +@dataclass +class SLO: + power: POWER + + def __str__(self): + return f"SLO ( {self.power} )" + + +@dataclass +class NOT: + arrangement_qry: Union[ARRANGEMENT, QRY] + + def __str__(self): + return f"NOT ( {self.arrangement_qry} )" + + +@dataclass +class NAR: + arrangement: ARRANGEMENT + + def __str__(self): + return f"NAR ( {self.arrangement} )" + + +@dataclass +class DRW: + powers: Optional[List[POWER]] = None + + def __init__(self, *powers): + self.powers = powers + + def __str__(self): + if self.powers: + return f"DRW ( " + " ".join(self.powers) + " )" + else: + return f"DRW" + + +@dataclass +class YES: + press_message: PRESS_MESSAGE + + def __str__(self): + return f"YES ( {self.press_message} )" + + +@dataclass +class REJ: + press_message: PRESS_MESSAGE + + def __str__(self): + return f"REJ ( {self.press_message} )" + + +@dataclass +class BWX: + press_message: PRESS_MESSAGE + + def __str__(self): + return f"BWX ( {self.press_message} )" + + +@dataclass +class FCT: + arrangement_qry_not: Union[ARRANGEMENT, QRY, NOT] + + def __str__(self): + return f"FCT ( {self.arrangement_qry_not} )" + + +@dataclass +class FRM: + frm_power: POWER + to_powers: List[POWER] + message: MESSAGE + + def __str__(self): + return ( + f"FRM ( {self.frm_power} ) ( " + + " ".join(self.to_powers) + + f" ) ( {self.message} )" + ) + + +@dataclass +class XDO: + order: ORDER + + def __str__(self): + return f"XDO ( {self.order} )" + + +@dataclass +class DMZ: + powers: List[POWER] + provinces: List[PROVINCE] + + def __str__(self): + return ( + "DMZ ( " + " ".join(self.powers) + " ) ( " + " ".join(self.provinces) + " )" + ) + + +@dataclass +class AND: + arrangments: List[ARRANGEMENT] + + def __init__(self, *arrangements): + self.arrangments = arrangements + + def __str__(self): + arr_str = ["( " + str(arr) + " )" for arr in self.arrangments] + return f"AND ( " + " ".join(arr_str) + " )" + + +@dataclass +class ORR: + arrangments: List[ARRANGEMENT] + + def __init__(self, *arrangements): + self.arrangments = arrangements + + def __str__(self): + arr_str = ["( " + str(arr) + " )" for arr in self.arrangments] + return f"ORR ( " + " ".join(arr_str) + " )" + + +@dataclass +class SCD: + power: POWER + supply_centers: List[SUPPLY_CENTER] + + def __init__(self, power, *supply_centers): + self.power = power + self.supply_centers = supply_centers + + def __str__(self): + return f"SCD ( {self.power} " + " ".join(self.supply_centers) + " )" + + +@dataclass +class OCC: + units: List[Unit] + + def __init__(self, *units): + self.units = units + + def __str__(self): + unit_str = ["( " + str(unit) + " )" for unit in self.units] + return f"OCC ( " + " ".join(unit_str) + " )" + + +@dataclass +class CHO: + start_year: int + end_year: int + arrangments: List[ARRANGEMENT] + + def __init__(self, start_year, end_year, *arrangements): + self.start_year = start_year + self.end_year = end_year + self.arrangments = arrangements + + def __str__(self): + arr_str = ["( " + str(arr) + " )" for arr in self.arrangments] + + return f"CHO ( {self.start_year} {self.end_year} ) " + " ".join(arr_str) + + +@dataclass +class INS: + arrangment: ARRANGEMENT + + def __str__(self): + return f"INS ( {self.arrangment} )" + + +@dataclass +class QRY: + arrangment: ARRANGEMENT + + def __str__(self): + return f"QRY ( {self.arrangment} )" + + +@dataclass +class THK: + arrangement_qry_not: Union[ARRANGEMENT, QRY, NOT, None] + + def __str__(self): + return f"THK ( {self.arrangement_qry_not} )" + + +@dataclass +class IDK: + qry_exp_wht_prp_ins_sug: Union[QRY, EXP, WHT, PRP, INS, SUG] + + def __str__(self): + return f"IDK ( {self.qry_exp_wht_prp_ins_sug} )" + + +@dataclass +class SUG: + arrangement: ARRANGEMENT + + def __str__(self): + return f"SUG ( {self.arrangement} )" + + +@dataclass +class WHT: + unit: Unit + + def __str__(self): + return f"WHT ( {self.unit} )" + + +@dataclass +class HOW: + province_power: Union[PROVINCE, POWER] + + def __str__(self): + return f"HOW ( {self.province_power} )" + + +@dataclass +class EXP: + turn: Turn + message: MESSAGE + + def __str__(self): + return f"EXP ( {self.turn} ) ( {self.message} )" + + +@dataclass +class SRY: + exp: EXP + + def __str__(self): + return f"SRY ( {self.exp} )" + + +@dataclass +class FOR: + start_turn: Turn + end_turn: Optional[Turn] + arrangement: ARRANGEMENT + + def __str__(self): + if not self.end_turn: + return f"FOR ( {self.start_turn} ) ( {self.arrangement} )" + else: + return f"FOR ( ( {self.start_turn} ) ( {self.end_turn} ) ) ( {self.arrangement} )" + + +@dataclass +class IFF: + arrangement: ARRANGEMENT + press_message: PRESS_MESSAGE + els_press_message: Optional[PRESS_MESSAGE] = None + + def __str__(self): + if not self.els_press_message: + return f"IFF ( {self.arrangement} ) THN ( {self.press_message} )" + else: + return f"IFF ( {self.arrangement} ) THN ( {self.press_message} ) ELS ( {self.els_press_message} )" + + +@dataclass +class XOY: + power_1: POWER + power_2: POWER + + def __str__(self): + return f"XOY ( {self.power_1} ) ( {self.power_2} )" + + +@dataclass +class YDO: + power: POWER + units: List[Unit] + + def __init__(self, power, *units): + self.power = power + self.units = units + + def __str__(self): + unit_str = ["( " + str(unit) + " )" for unit in self.units] + return f"YDO ( {self.power} ) " + " ".join(unit_str) + + +@dataclass +class SND: + power: POWER + powers: List[POWER] + message: MESSAGE + + def __str__(self): + return ( + f"SND ( {self.power} ) ( " + + " ".join(self.powers) + + f" ) ( {self.message} )" + ) + + +@dataclass +class FWD: + powers: List[POWER] + power_1: POWER + power_2: POWER + + def __str__(self): + return ( + f"FWD ( " + + " ".join(self.powers) + + f" ) ( {self.power_1} ) ( {self.power_2} )" + ) + + +@dataclass +class BCC: + power_1: POWER + powers: List[POWER] + power_2: POWER + + def __str__(self): + return ( + f"BCC ( {self.power_1} ) ( " + + " ".join(self.powers) + + f" ) ( {self.power_2} )" + ) + + +@dataclass +class WHY: + fct_thk_prp_ins: Union[FCT, THK, PRP, INS] + + def __str__(self): + return f"WHY ( {self.fct_thk_prp_ins} )" + + +@dataclass +class POB: + why: WHY + + def __str__(self): + return f"POB ( {self.why} )" + + +RETREAT = Union[RTO, DSB] +BUILD = Union[BLD, REM, WVE] +ORDER = Union[ + HLD, + MTO, + SUP, + CVY, + MoveByCVY, +] +REPLY = Union[YES, REJ, BWX, HUH, FCT, THK, IDK, WHY, POB, IDK] +PRESS_MESSAGE = Union[PRP, CCL, FCT, TRY, FRM, THK, INS, QRY, SUG, HOW, WHT, EXP, IFF] +MESSAGE = Union[PRESS_MESSAGE, REPLY] +ARRANGEMENT = Union[ + PCE, ALYVSS, DRW, XDO, DMZ, AND, ORR, SCD, CHO, FOR, XOY, YDO, SND, FWD, BCC +] diff --git a/tests/test_keywords.py b/tests/test_keywords.py new file mode 100644 index 0000000..079f054 --- /dev/null +++ b/tests/test_keywords.py @@ -0,0 +1,401 @@ +from typing import List + +from daidepp.keywords import * + + +def test_Unit(): + unit_1 = Unit("AUS", "FLT", "ALB") + assert str(unit_1) == "AUS FLT ALB" + + unit_2 = Unit("ENG", "AMY", "ANK") + assert str(unit_2) == "ENG AMY ANK" + + unit_3 = Unit("FRA", "FLT", "APU") + assert str(unit_3) == "FRA FLT APU" + + unit_4 = Unit("GER", "AMY", "ARM") + assert str(unit_4) == "GER AMY ARM" + + unit_5 = Unit("ITA", "FLT", "BEL") + assert str(unit_5) == "ITA FLT BEL" + + unit_6 = Unit("RUS", "AMY", "BER") + assert str(unit_6) == "RUS AMY BER" + + unit_7 = Unit("TUR", "FLT", "BRE") + assert str(unit_7) == "TUR FLT BRE" + + +def test_HLD(): + hld_1 = HLD(Unit("AUS", "FLT", "ALB")) + assert str(hld_1) == "( AUS FLT ALB ) HLD" + + hld_2 = HLD(Unit("ENG", "AMY", "ANK")) + assert str(hld_2) == "( ENG AMY ANK ) HLD" + + +def test_MTO(): + mto_1 = MTO(Unit("AUS", "FLT", "ALB"), "BUL") + assert str(mto_1) == "( AUS FLT ALB ) MTO BUL" + + mto_2 = MTO(Unit("ENG", "AMY", "ANK"), "CLY") + assert str(mto_2) == "( ENG AMY ANK ) MTO CLY" + + +def test_SUP(): + sup_1 = SUP(Unit("AUS", "FLT", "ALB"), Unit("ENG", "AMY", "ANK"), "BUL") + assert str(sup_1) == "( AUS FLT ALB ) SUP ( ENG AMY ANK ) MTO BUL" + + sup_2 = SUP(Unit("FRA", "FLT", "APU"), Unit("GER", "AMY", "ARM"), "CLY") + assert str(sup_2) == "( FRA FLT APU ) SUP ( GER AMY ARM ) MTO CLY" + + +def test_CVY(): + cvy_1 = CVY(Unit("AUS", "FLT", "ALB"), Unit("ENG", "AMY", "ANK"), "BUL") + assert str(cvy_1) == "( AUS FLT ALB ) CVY ( ENG AMY ANK ) CTO BUL" + + cvy_2 = CVY(Unit("FRA", "FLT", "APU"), Unit("GER", "AMY", "ARM"), "CLY") + assert str(cvy_2) == "( FRA FLT APU ) CVY ( GER AMY ARM ) CTO CLY" + + +def test_MoveByCVY(): + mvc_1 = MoveByCVY(Unit("AUS", "FLT", "ALB"), "BUL", "ADR") + assert str(mvc_1) == "( AUS FLT ALB ) CTO BUL VIA ( ADR )" + + mvc_2 = MoveByCVY(Unit("ENG", "AMY", "ANK"), "CLY", "ADR", "AEG") + assert str(mvc_2) == "( ENG AMY ANK ) CTO CLY VIA ( ADR AEG )" + + mvc_3 = MoveByCVY(Unit("FRA", "FLT", "APU"), "CON", "ADR", "AEG", "BAL") + assert str(mvc_3) == "( FRA FLT APU ) CTO CON VIA ( ADR AEG BAL )" + + +def test_RTO(): + rto_1 = RTO(Unit("AUS", "FLT", "ALB"), "BUL") + assert str(rto_1) == "( AUS FLT ALB ) RTO BUL" + + rto_2 = RTO(Unit("ENG", "AMY", "ANK"), "CLY") + assert str(rto_2) == "( ENG AMY ANK ) RTO CLY" + + +def test_DSB(): + dsb_1 = DSB(Unit("AUS", "FLT", "ALB")) + assert str(dsb_1) == "( AUS FLT ALB ) DSB" + + dsb_2 = DSB(Unit("ENG", "AMY", "ANK")) + assert str(dsb_2) == "( ENG AMY ANK ) DSB" + + +def test_BLD(): + bld_1 = BLD(Unit("AUS", "FLT", "ALB")) + assert str(bld_1) == "( AUS FLT ALB ) BLD" + + bld_2 = BLD(Unit("ENG", "AMY", "ANK")) + assert str(bld_2) == "( ENG AMY ANK ) BLD" + + +def test_REM(): + rem_1 = REM(Unit("AUS", "FLT", "ALB")) + assert str(rem_1) == "( AUS FLT ALB ) REM" + + rem_2 = REM(Unit("ENG", "AMY", "ANK")) + assert str(rem_2) == "( ENG AMY ANK ) REM" + + +def test_WVE(): + wve_1 = WVE("AUS") + assert str(wve_1) == "AUS WVE" + + wve_2 = WVE("ENG") + assert str(wve_2) == "ENG WVE" + + +def test_turn(): + turn_1 = Turn("SPR", 1901) + assert str(turn_1) == "SPR 1901" + + +def test_PCE(): + pce_1 = PCE("AUS") + assert str(pce_1) == "PCE ( AUS )" + + pce_2 = PCE("AUS", "ENG") + assert str(pce_2) == "PCE ( AUS ENG )" + + pce_3 = PCE("AUS", "ENG", "FRA") + assert str(pce_3) == "PCE ( AUS ENG FRA )" + + +def test_PRP(): + arr_1 = PRP(PCE("AUS")) + assert str(arr_1) == "PRP ( PCE ( AUS ) )" + + arr_2 = PRP(PCE("AUS", "ENG")) + assert str(arr_2) == "PRP ( PCE ( AUS ENG ) )" + + arr_2 = PRP(PCE("AUS", "ENG", "FRA")) + assert str(arr_2) == "PRP ( PCE ( AUS ENG FRA ) )" + + +def test_CCL(): + ccl_1 = CCL(PRP(PCE("AUS"))) + assert str(ccl_1) == "CCL ( PRP ( PCE ( AUS ) ) )" + + ccl_2 = CCL(PRP(PCE("AUS", "ENG"))) + assert str(ccl_2) == "CCL ( PRP ( PCE ( AUS ENG ) ) )" + + ccl_3 = CCL(PRP(PCE("AUS", "ENG", "FRA"))) + assert str(ccl_3) == "CCL ( PRP ( PCE ( AUS ENG FRA ) ) )" + + +def test_TRY(): + try_1 = TRY("PRP") + assert str(try_1) == "TRY ( PRP )" + + try_2 = TRY("PCE", "ALY") + assert str(try_2) == "TRY ( PCE ALY )" + + try_3 = TRY("VSS", "DRW", "SLO") + assert str(try_3) == "TRY ( VSS DRW SLO )" + + +def test_HUH(): + huh_1 = HUH(PRP(PCE("AUS"))) + assert str(huh_1) == "HUH ( PRP ( PCE ( AUS ) ) )" + + huh_2 = HUH(PRP(PCE("AUS", "ENG"))) + assert str(huh_2) == "HUH ( PRP ( PCE ( AUS ENG ) ) )" + + huh_3 = HUH(PRP(PCE("AUS", "ENG", "FRA"))) + assert str(huh_3) == "HUH ( PRP ( PCE ( AUS ENG FRA ) ) )" + + +def test_ALYVSS(): + alyvss_1 = ALYVSS(["AUS"], ["ENG"]) + assert str(alyvss_1) == "ALY ( AUS ) VSS ( ENG )" + + alyvss_2 = ALYVSS(["FRA"], ["GER", "TUR"]) + assert str(alyvss_2) == "ALY ( FRA ) VSS ( GER TUR )" + + alyvss_3 = ALYVSS(["AUS", "FRA"], ["GER"]) + assert str(alyvss_3) == "ALY ( AUS FRA ) VSS ( GER )" + + alyvss_4 = ALYVSS(["AUS", "GER"], ["TUR", "ITA"]) + assert str(alyvss_4) == "ALY ( AUS GER ) VSS ( TUR ITA )" + + +def test_SLO(): + slo_1 = SLO("AUS") + assert str(slo_1) == "SLO ( AUS )" + + slo_2 = SLO("GER") + assert str(slo_2) == "SLO ( GER )" + + +def test_NOT(): + not_1 = NOT(PCE("AUS", "ENG")) + assert str(not_1) == "NOT ( PCE ( AUS ENG ) )" + + +def test_NAR(): + nar_1 = NAR(PCE("AUS", "ENG")) + assert str(nar_1) == "NAR ( PCE ( AUS ENG ) )" + + +def test_DRW(): + drw_1 = DRW() + assert str(drw_1) == "DRW" + + drw_2 = DRW("AUS", "ENG") + assert str(drw_2) == "DRW ( AUS ENG )" + + +def test_YES(): + yes_1 = YES(PRP(PCE("AUS", "ENG"))) + assert str(yes_1) == "YES ( PRP ( PCE ( AUS ENG ) ) )" + + +def test_REJ(): + rej_1 = REJ(PRP(PCE("AUS", "ENG"))) + assert str(rej_1) == "REJ ( PRP ( PCE ( AUS ENG ) ) )" + + +def test_BWX(): + bwx_1 = BWX(PRP(PCE("AUS", "ENG"))) + assert str(bwx_1) == "BWX ( PRP ( PCE ( AUS ENG ) ) )" + + +def test_FCT(): + fct_1 = FCT(PCE("AUS", "ENG")) + assert str(fct_1) == "FCT ( PCE ( AUS ENG ) )" + + fct_2 = FCT(NOT(PCE("AUS", "ENG"))) + assert str(fct_2) == "FCT ( NOT ( PCE ( AUS ENG ) ) )" + + +def test_FRM(): + frm_1 = FRM("AUS", ["GER", "FRA"], PRP(PCE("AUS", "ENG"))) + + assert str(frm_1) == "FRM ( AUS ) ( GER FRA ) ( PRP ( PCE ( AUS ENG ) ) )" + + +def test_XDO(): + xdo_1 = XDO(HLD(Unit("AUS", "FLT", "ALB"))) + assert str(xdo_1) == "XDO ( ( AUS FLT ALB ) HLD )" + + xdo_2 = XDO(MTO(Unit("AUS", "FLT", "ALB"), "BUL")) + assert str(xdo_2) == "XDO ( ( AUS FLT ALB ) MTO BUL )" + + +def test_DMZ(): + dmz_1 = DMZ(["AUS"], ["EDI"]) + assert str(dmz_1) == "DMZ ( AUS ) ( EDI )" + + dmz_2 = DMZ(["ITA", "TUR"], ["CLY", "ALB"]) + assert str(dmz_2) == "DMZ ( ITA TUR ) ( CLY ALB )" + + +def test_AND(): + and_1 = AND(PRP(PCE("AUS")), PRP(PCE("AUS", "ENG"))) + assert str(and_1) == "AND ( ( PRP ( PCE ( AUS ) ) ) ( PRP ( PCE ( AUS ENG ) ) ) )" + + and_2 = AND(PRP(PCE("AUS")), PRP(PCE("AUS", "ENG")), PRP(PCE("AUS", "ENG", "FRA"))) + assert ( + str(and_2) + == "AND ( ( PRP ( PCE ( AUS ) ) ) ( PRP ( PCE ( AUS ENG ) ) ) ( PRP ( PCE ( AUS ENG FRA ) ) ) )" + ) + + +def test_ORR(): + orr_1 = ORR(PRP(PCE("AUS")), PRP(PCE("AUS", "ENG"))) + assert str(orr_1) == "ORR ( ( PRP ( PCE ( AUS ) ) ) ( PRP ( PCE ( AUS ENG ) ) ) )" + + orr_2 = ORR(PRP(PCE("AUS")), PRP(PCE("AUS", "ENG")), PRP(PCE("AUS", "ENG", "FRA"))) + assert ( + str(orr_2) + == "ORR ( ( PRP ( PCE ( AUS ) ) ) ( PRP ( PCE ( AUS ENG ) ) ) ( PRP ( PCE ( AUS ENG FRA ) ) ) )" + ) + + +def test_SCD(): + scd_1 = SCD("AUS", "ANK", "BEL", "BER") + assert str(scd_1) == "SCD ( AUS ANK BEL BER )" + + scd_2 = SCD("GER", "BRE", "BUD") + assert str(scd_2) == "SCD ( GER BRE BUD )" + + +def test_OCC(): + unit_1 = Unit("AUS", "FLT", "ALB") + unit_2 = Unit("ENG", "AMY", "ANK") + unit_3 = Unit("FRA", "FLT", "APU") + + occ_1 = OCC(unit_1, unit_2, unit_3) + assert str(occ_1) == "OCC ( ( AUS FLT ALB ) ( ENG AMY ANK ) ( FRA FLT APU ) )" + + +def test_CHO(): + cho_1 = CHO(1901, 1903, PCE("AUS"), PCE("AUS", "ENG")) + assert str(cho_1) == "CHO ( 1901 1903 ) ( PCE ( AUS ) ) ( PCE ( AUS ENG ) )" + + +def test_INS(): + ins_1 = INS(PCE("AUS")) + assert str(ins_1) == "INS ( PCE ( AUS ) )" + + +def test_QRY(): + qry_1 = QRY(PCE("AUS")) + assert str(qry_1) == "QRY ( PCE ( AUS ) )" + + +def test_THK(): + thk_1 = THK(PCE("AUS")) + assert str(thk_1) == "THK ( PCE ( AUS ) )" + + +def test_SUG(): + sug_1 = SUG(PCE("AUS")) + assert str(sug_1) == "SUG ( PCE ( AUS ) )" + + +def test_WHT(): + wht_1 = WHT(Unit("AUS", "FLT", "ALB")) + + assert str(wht_1) == "WHT ( AUS FLT ALB )" + + +def test_HOW(): + how_1 = HOW("AUS") + assert str(how_1) == "HOW ( AUS )" + + how_2 = HOW("APU") + assert str(how_2) == "HOW ( APU )" + + +def test_EXP(): + exp_1 = EXP(Turn("SPR", 1901), PRP(PCE("AUS", "ENG"))) + + assert str(exp_1) == "EXP ( SPR 1901 ) ( PRP ( PCE ( AUS ENG ) ) )" + + +def test_SRY(): + sry_1 = SRY(EXP(Turn("SPR", 1901), PRP(PCE("AUS", "ENG")))) + + assert str(sry_1) == "SRY ( EXP ( SPR 1901 ) ( PRP ( PCE ( AUS ENG ) ) ) )" + + +def test_FOR(): + for_1 = FOR(Turn("SPR", 1901), None, PCE("AUS", "ENG")) + assert str(for_1) == "FOR ( SPR 1901 ) ( PCE ( AUS ENG ) )" + + for_2 = FOR(Turn("SPR", 1901), Turn("FAL", 1903), PCE("AUS", "ENG")) + assert str(for_2) == "FOR ( ( SPR 1901 ) ( FAL 1903 ) ) ( PCE ( AUS ENG ) )" + + +def test_IFF(): + iff_1 = IFF(PCE("AUS", "ENG"), PRP(PCE("AUS"))) + assert str(iff_1) == "IFF ( PCE ( AUS ENG ) ) THN ( PRP ( PCE ( AUS ) ) )" + + iff_2 = IFF(PCE("AUS", "ENG"), PRP(PCE("AUS")), PRP(PCE("GER", "FRA"))) + assert ( + str(iff_2) + == "IFF ( PCE ( AUS ENG ) ) THN ( PRP ( PCE ( AUS ) ) ) ELS ( PRP ( PCE ( GER FRA ) ) )" + ) + + +def test_XOY(): + xoy_1 = XOY("AUS", "ENG") + assert str(xoy_1) == "XOY ( AUS ) ( ENG )" + + +def test_YDO(): + ydo_1 = YDO("AUS", Unit("AUS", "FLT", "ALB")) + assert str(ydo_1) == "YDO ( AUS ) ( AUS FLT ALB )" + + ydo_2 = YDO("AUS", Unit("AUS", "FLT", "ALB"), Unit("ENG", "AMY", "ANK")) + assert str(ydo_2) == "YDO ( AUS ) ( AUS FLT ALB ) ( ENG AMY ANK )" + + +def test_SND(): + snd_1 = SND("AUS", ["GER", "FRA"], PRP(PCE("TUR", "RUS"))) + assert str(snd_1) == "SND ( AUS ) ( GER FRA ) ( PRP ( PCE ( TUR RUS ) ) )" + + +def test_FWD(): + fwd_1 = FWD(["GER", "ITA"], "FRA", "AUS") + assert str(fwd_1) == "FWD ( GER ITA ) ( FRA ) ( AUS )" + + +def test_BCC(): + bcc_1 = BCC("AUS", ["GER", "FRA"], "ITA") + assert str(bcc_1) == "BCC ( AUS ) ( GER FRA ) ( ITA )" + + +def test_WHY(): + why_1 = WHY(PRP(PCE("AUS", "GER"))) + assert str(why_1) == "WHY ( PRP ( PCE ( AUS GER ) ) )" + + +def test_POB(): + pob_1 = POB(WHY(PRP(PCE("AUS", "GER")))) + assert str(pob_1) == "POB ( WHY ( PRP ( PCE ( AUS GER ) ) ) )"