Source code for diplomacy.daide.requests

# ==============================================================================
# Copyright (C) 2019 - Philip Paquette
#
#  This program is free software: you can redistribute it and/or modify it under
#  the terms of the GNU Affero General Public License as published by the Free
#  Software Foundation, either version 3 of the License, or (at your option) any
#  later version.
#
#  This program is distributed in the hope that it will be useful, but WITHOUT
#  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
#  FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
#  details.
#
#  You should have received a copy of the GNU Affero General Public License along
#  with this program.  If not, see <https://www.gnu.org/licenses/>.
# ==============================================================================
""" Daide Requests - Contains a list of requests sent by client to server """
from diplomacy.communication.requests import _AbstractGameRequest
from diplomacy.daide.clauses import String, Number, Power, Order, Turn, SingleToken, strip_parentheses, \
    break_next_group, parse_bytes
from diplomacy.daide import tokens
from diplomacy.daide.tokens import Token, is_ascii_token
from diplomacy.utils import parsing, strings

[docs]class RequestBuilder: """ Builds DaideRequest from bytes or tokens """
[docs] @staticmethod def from_bytes(daide_bytes, **kwargs): """ Builds a request from DAIDE bytes :param daide_bytes: The bytes representation of a request :return: The DaideRequest built from the bytes """ if len(daide_bytes) < 2: return None initial_bytes = daide_bytes[:2] if initial_bytes not in __REQUEST_CONSTRUCTORS__: raise ValueError('Unable to find a constructor for %s' % str(Token(from_bytes=initial_bytes))) request = __REQUEST_CONSTRUCTORS__[initial_bytes](**kwargs) # type: DaideRequest request.parse_bytes(daide_bytes) return request
[docs]class DaideRequest(_AbstractGameRequest): """ Represents a DAIDE request. """
[docs] def __init__(self, **kwargs): """ Constructor """ self._bytes = b'' self._str = '' super(DaideRequest, self).__init__(token='', game_id='', game_role='', phase='', **kwargs)
def __bytes__(self): """ Returning the bytes representation of the request """ return self._bytes def __str__(self): """ Returning the string representation of the request """ return self._str
[docs] def parse_bytes(self, daide_bytes): """ Builds the request from DAIDE bytes """ assert len(daide_bytes) % 2 == 0, 'Expected request to have an even number of bytes. Got %d' % len(daide_bytes) self._bytes = daide_bytes # Building str representation while daide_bytes: token = Token(from_bytes=(daide_bytes[0], daide_bytes[1])) new_str = str(token) daide_bytes = daide_bytes[2:] pad = '' if (not self._str or self._str[-1] == '(' or new_str == ')' or (is_ascii_token(token) and new_str != '(')) else ' ' self._str = self._str + pad + new_str
# ==================== # Connection requests # ====================
[docs]class NameRequest(DaideRequest): """ Represents a NME DAIDE request. Can be sent by the client as soon as it connects to the server. Syntax: :: NME ('name') ('version') """ __slots__ = ['client_name', 'client_version'] params = { strings.CLIENT_NAME: str, strings.CLIENT_VERSION: str }
[docs] def __init__(self, **kwargs): """ Constructor """ self.client_name = '' self.client_version = '' super(NameRequest, self).__init__(client_name='', client_version='', **kwargs)
[docs] def parse_bytes(self, daide_bytes): """ Builds the request from DAIDE bytes """ super(NameRequest, self).parse_bytes(daide_bytes) # Parsing lead_token, daide_bytes = parse_bytes(SingleToken, daide_bytes) client_name, daide_bytes = parse_bytes(String, daide_bytes) client_version, daide_bytes = parse_bytes(String, daide_bytes) assert str(lead_token) == 'NME', 'Expected NME request' assert not daide_bytes, '%s bytes remaining. Request is malformed' % len(daide_bytes) # Setting properties self.client_name = str(client_name) self.client_version = str(client_version)
[docs]class ObserverRequest(DaideRequest): """ Represents a NME DAIDE request. Can be sent by the client as soon as it connects to the server. Syntax: :: OBS """ __slots__ = [] params = {}
[docs] def parse_bytes(self, daide_bytes): """ Builds the request from DAIDE bytes """ super(ObserverRequest, self).parse_bytes(daide_bytes) # Parsing lead_token, daide_bytes = parse_bytes(SingleToken, daide_bytes) assert str(lead_token) == 'OBS', 'Expected OBS request' assert not daide_bytes, '%s bytes remaining. Request is malformed' % len(daide_bytes)
[docs]class IAmRequest(DaideRequest): """ Represents a IAM DAIDE request. Can be sent by the client at anytime to rejoin the game. Syntax: :: IAM (power) (passcode) """ __slots__ = ['power_name', 'passcode'] params = { strings.POWER_NAME: str, strings.PASSCODE: parsing.OptionalValueType(int) }
[docs] def __init__(self, **kwargs): """ Constructor """ self.power_name = '' self.passcode = 0 super(IAmRequest, self).__init__(power_name='', passcode=0, **kwargs)
[docs] def parse_bytes(self, daide_bytes): """ Builds the request from DAIDE bytes """ super(IAmRequest, self).parse_bytes(daide_bytes) # Parsing lead_token, daide_bytes = parse_bytes(SingleToken, daide_bytes) assert str(lead_token) == 'IAM', 'Expected IAM request' # Power power_group_bytes, daide_bytes = break_next_group(daide_bytes) power_group_bytes = strip_parentheses(power_group_bytes) power, power_group_bytes = parse_bytes(Power, power_group_bytes) assert not power_group_bytes, '%s bytes remaining in power group. Request is malformed' % len(power_group_bytes) # Passcode passcode_group_bytes, daide_bytes = break_next_group(daide_bytes) passcode_group_bytes = strip_parentheses(passcode_group_bytes) passcode, passcode_group_bytes = parse_bytes(SingleToken, passcode_group_bytes) assert not passcode_group_bytes, '%s bytes remaining in passcode group. Req. error' % len(passcode_group_bytes) assert not daide_bytes, '%s bytes remaining. Request is malformed' % len(daide_bytes) # Setting properties self.power_name = str(power) self.passcode = str(passcode)
[docs]class HelloRequest(DaideRequest): """ Represents a HLO DAIDE request. Sent by the client to request a copy of the HLO message. Syntax: :: HLO """ __slots__ = [] params = {}
[docs] def parse_bytes(self, daide_bytes): """ Builds the request from DAIDE bytes """ super(HelloRequest, self).parse_bytes(daide_bytes) # Parsing lead_token, daide_bytes = parse_bytes(SingleToken, daide_bytes) assert str(lead_token) == 'HLO', 'Expected HLO request' assert not daide_bytes, '%s bytes remaining. Request is malformed' % len(daide_bytes)
[docs]class MapRequest(DaideRequest): """ Represents a MAP DAIDE request. Sent by the client to request a copy of the MAP message. Syntax: :: MAP """ __slots__ = [] params = {}
[docs] def parse_bytes(self, daide_bytes): """ Builds the request from DAIDE bytes """ super(MapRequest, self).parse_bytes(daide_bytes) # Parsing lead_token, daide_bytes = parse_bytes(SingleToken, daide_bytes) assert str(lead_token) == 'MAP', 'Expected MAP request' assert not daide_bytes, '%s bytes remaining. Request is malformed' % len(daide_bytes)
[docs]class MapDefinitionRequest(DaideRequest): """ Represents a MDF DAIDE request. Sent by the client to request the map definition of the game. Syntax: :: MDF """ __slots__ = [] params = {}
[docs] def parse_bytes(self, daide_bytes): """ Builds the request from DAIDE bytes """ super(MapDefinitionRequest, self).parse_bytes(daide_bytes) # Parsing lead_token, daide_bytes = parse_bytes(SingleToken, daide_bytes) assert str(lead_token) == 'MDF', 'Expected MDF request' assert not daide_bytes, '%s bytes remaining. Request is malformed' % len(daide_bytes)
# ==================== # Game updates # ====================
[docs]class SupplyCentreOwnershipRequest(DaideRequest): """ Represents a SCO DAIDE request. Sent by the client to request a copy of the last SCO message. Syntax: :: SCO """ __slots__ = [] params = {}
[docs] def parse_bytes(self, daide_bytes): """ Builds the request from DAIDE bytes """ super(SupplyCentreOwnershipRequest, self).parse_bytes(daide_bytes) # Parsing lead_token, daide_bytes = parse_bytes(SingleToken, daide_bytes) assert str(lead_token) == 'SCO', 'Expected SCO request' assert not daide_bytes, '%s bytes remaining. Request is malformed' % len(daide_bytes)
[docs]class CurrentPositionRequest(DaideRequest): """ Represents a NOW DAIDE request. Sent by the client to request a copy of the last NOW message. Syntax: :: NOW """ __slots__ = [] params = {}
[docs] def parse_bytes(self, daide_bytes): """ Builds the request from DAIDE bytes """ super(CurrentPositionRequest, self).parse_bytes(daide_bytes) # Parsing lead_token, daide_bytes = parse_bytes(SingleToken, daide_bytes) assert str(lead_token) == 'NOW', 'Expected NOW request' assert not daide_bytes, '%s bytes remaining. Request is malformed' % len(daide_bytes)
[docs]class HistoryRequest(DaideRequest): """ Represents a HST DAIDE request. Sent by the client to request a copy of a previous ORD, SCO and NOW messages. Syntax: :: HST (turn) """
[docs] def __init__(self, **kwargs): """ Constructor """ self.phase = '' super(HistoryRequest, self).__init__(**kwargs)
[docs] def parse_bytes(self, daide_bytes): """ Builds the request from DAIDE bytes """ super(HistoryRequest, self).parse_bytes(daide_bytes) # Parsing lead_token, daide_bytes = parse_bytes(SingleToken, daide_bytes) assert str(lead_token) == 'HST', 'Expected HST request' # Turn turn, daide_bytes = parse_bytes(Turn, daide_bytes) assert not daide_bytes, '%s bytes remaining. Request is malformed' % len(daide_bytes) # Setting properties self.phase = str(turn)
# ==================== # Orders # ====================
[docs]class SubmitOrdersRequest(DaideRequest): """ Represents a SUB DAIDE request. Sent by the client to submit orders. Syntax: :: SUB (order) (order) ... SUB (turn) (order) (order) ... order syntax: :: (unit) HLD # Hold (unit) MTO province # Move to (unit) SUP (unit) # Support (unit) SUP (unit) MTO (prov_no_coast) # Support to move (unit) CVY (unit) CTO province # Convoy (unit) CTO province VIA (sea_prov sea_prov ...) # Convoy to via provinces (unit) RTO province # Retreat to (unit) DSB # Disband (R phase) (unit) BLD # Build (unit) REM # Remove (A phase) (unit) WVE # Waive """ __slots__ = ['power_name', 'orders'] params = { strings.POWER_NAME: parsing.OptionalValueType(str), strings.ORDERS: parsing.SequenceType(str) }
[docs] def __init__(self, **kwargs): """ Constructor """ self.power_name = None self.phase = '' self.orders = [] super(SubmitOrdersRequest, self).__init__(power_name='', orders=[], **kwargs)
[docs] def parse_bytes(self, daide_bytes): """ Builds the request from DAIDE bytes """ super(SubmitOrdersRequest, self).parse_bytes(daide_bytes) orders = [] # Parsing lead_token, daide_bytes = parse_bytes(SingleToken, daide_bytes) turn, daide_bytes = parse_bytes(Turn, daide_bytes, on_error='ignore') while daide_bytes: order, daide_bytes = parse_bytes(Order, daide_bytes) orders += [order] assert str(lead_token) == 'SUB', 'Expected SUB request' assert not daide_bytes, '%s bytes remaining. Request is malformed' % len(daide_bytes) # Setting properties self.phase = '' if not turn else str(turn) self.power_name = None if not orders or not orders[0].power_name else str(orders[0].power_name) self.orders = [str(order) for order in orders]
[docs]class MissingOrdersRequest(DaideRequest): """ Represents a MIS DAIDE request. Sent by the client to request a copy of the current MIS message. Syntax: :: MIS """ __slots__ = [] params = {}
[docs] def parse_bytes(self, daide_bytes): """ Builds the request from DAIDE bytes """ super(MissingOrdersRequest, self).parse_bytes(daide_bytes) # Parsing lead_token, daide_bytes = parse_bytes(SingleToken, daide_bytes) assert str(lead_token) == 'MIS', 'Expected MIS request' assert not daide_bytes, '%s bytes remaining. Request is malformed' % len(daide_bytes)
[docs]class GoFlagRequest(DaideRequest): """ Represents a GOF DAIDE request. Sent by the client to notify that the client is ready to process the turn. Syntax: :: GOF """ __slots__ = [] params = {}
[docs] def parse_bytes(self, daide_bytes): """ Builds the request from DAIDE bytes """ super(GoFlagRequest, self).parse_bytes(daide_bytes) # Parsing lead_token, daide_bytes = parse_bytes(SingleToken, daide_bytes) assert str(lead_token) == 'GOF', 'Expected GOF request' assert not daide_bytes, '%s bytes remaining. Request is malformed' % len(daide_bytes)
# ==================== # Deadline # ====================
[docs]class TimeToDeadlineRequest(DaideRequest): """ Represents a TME DAIDE request. Sent by the client to request a TME message or to request it at a later time. Syntax: :: TME TME (seconds) """ __slots__ = ['seconds'] params = { strings.SECONDS: parsing.OptionalValueType(int) }
[docs] def __init__(self, **kwargs): """ Constructor """ self.seconds = None super(TimeToDeadlineRequest, self).__init__(seconds=0, **kwargs)
[docs] def parse_bytes(self, daide_bytes): """ Builds the request from DAIDE bytes """ super(TimeToDeadlineRequest, self).parse_bytes(daide_bytes) # Parsing lead_token, daide_bytes = parse_bytes(SingleToken, daide_bytes) seconds_group_bytes, daide_bytes = break_next_group(daide_bytes) assert str(lead_token) == 'TME', 'Expected TME request' # Seconds if seconds_group_bytes: seconds_group_bytes = strip_parentheses(seconds_group_bytes) seconds, daide_bytes = parse_bytes(Number, seconds_group_bytes) assert not daide_bytes, '%s bytes remaining. Request is malformed' % len(daide_bytes) # Setting properties self.seconds = None if not seconds_group_bytes else int(seconds)
# ==================== # End of the game # ====================
[docs]class DrawRequest(DaideRequest): """ Represents a DRW DAIDE request. Sent by the client to notify that the client would accept a draw. Syntax: :: DRW LVL 10: :: DRW (power power ...) """ __slots__ = ['powers'] params = { strings.POWERS: parsing.SequenceType(str) }
[docs] def __init__(self, **kwargs): """ Constructor """ self.powers = [] super(DrawRequest, self).__init__(powers=[], **kwargs)
[docs] def parse_bytes(self, daide_bytes): """ Builds the request from DAIDE bytes """ super(DrawRequest, self).parse_bytes(daide_bytes) powers = [] # Parsing lead_token, daide_bytes = parse_bytes(SingleToken, daide_bytes) assert str(lead_token) == 'DRW', 'Expected DRW request' # Powers powers_group_bytes, daide_bytes = break_next_group(daide_bytes) if powers_group_bytes: powers_group_bytes = strip_parentheses(powers_group_bytes) while powers_group_bytes: power, powers_group_bytes = parse_bytes(Power, powers_group_bytes) powers += [power] assert not daide_bytes, '%s bytes remaining. Request is malformed' % len(daide_bytes) # Setting properties self.powers = [str(power) for power in powers]
# ==================== # Messaging # ====================
[docs]class SendMessageRequest(DaideRequest): """ Represents a SND DAIDE request Syntax: :: SND (power ...) (press_message) SND (power ...) (reply) SND (turn) (power ...) (press_message) SND (turn) (power ...) (reply) Press message syntax: :: PRP (arrangement) CCL (press_message) FCT (arrangement) TRY (tokens) Reply syntax: :: YES (press_message) REJ (press_message) BWX (press_message) HUH (press_message) """ __slots__ = ['powers', 'message_bytes'] params = { strings.POWERS: parsing.SequenceType(str), strings.MESSAGE_BYTES: parsing.OptionalValueType(str), }
[docs] def __init__(self, **kwargs): """ Constructor """ self.phase = '' self.powers = [] self.message_bytes = None super(SendMessageRequest, self).__init__(powers=[], press_message='', reply='', **kwargs)
[docs] def parse_bytes(self, daide_bytes): """ Builds the request from DAIDE bytes """ super(SendMessageRequest, self).parse_bytes(daide_bytes) # Parsing lead_token, daide_bytes = parse_bytes(SingleToken, daide_bytes) assert str(lead_token) == 'SND', 'Expected SND request' # Turn turn, daide_bytes = parse_bytes(Turn, daide_bytes, on_error='ignore') # Powers powers = [] powers_group_bytes, daide_bytes = break_next_group(daide_bytes) powers_group_bytes = strip_parentheses(powers_group_bytes) while powers_group_bytes: power, powers_group_bytes = parse_bytes(Power, powers_group_bytes) powers += [power] assert powers, 'Expected a group of `power`. Request is malformed' # Press message or reply message_group_bytes, daide_bytes = break_next_group(daide_bytes) message_group_bytes = strip_parentheses(message_group_bytes) assert message_group_bytes, 'Expected a `press_message` or a `reply`. Request is malformed' assert not daide_bytes, '%s bytes remaining. Request is malformed' % len(daide_bytes) # Setting properties self.phase = '' if not turn else str(turn) self.powers = [str(power) for power in powers] self.message_bytes = message_group_bytes
# ==================== # Cancel Request # ====================
[docs]class NotRequest(DaideRequest): """ Represents a NOT DAIDE request. Sent by the client to cancel a previous request. Syntax: :: NOT (SUB) # Cancel all submitted orders NOT (SUB (order)) # Cancel specific submitted order NOT (GOF) # Do not process orders until the deadline NOT (TME) # Cancel all requested time messages NOT (TME (seconds)) # Cancel specific requested time message NOT (DRW) # Cancel all draw requests """ __slots__ = ['request'] params = { strings.REQUEST: parsing.JsonableClassType(DaideRequest) }
[docs] def __init__(self, **kwargs): """ Constructor """ self.request = None # type: DaideRequest super(NotRequest, self).__init__(request=DaideRequest(), **kwargs)
[docs] def parse_bytes(self, daide_bytes): """ Builds the request from DAIDE bytes """ super(NotRequest, self).parse_bytes(daide_bytes) # Parsing lead_token, daide_bytes = parse_bytes(SingleToken, daide_bytes) request_group_bytes, daide_bytes = break_next_group(daide_bytes) assert str(lead_token) == 'NOT', 'Expected NOT request' # Request request_group_bytes = strip_parentheses(request_group_bytes) request = RequestBuilder.from_bytes(request_group_bytes) assert not daide_bytes, '%s bytes remaining. Request is malformed' % len(daide_bytes) # Setting properties self.request = request
# ======================== # Accept / Reject Response # ========================
[docs]class AcceptRequest(DaideRequest): """ Represents a YES DAIDE request. Syntax: :: YES (MAP ('name')) YES (SVE ('gamename')) """ __slots__ = ['response_bytes'] params = { strings.RESPONSE_BYTES: bytes }
[docs] def __init__(self, **kwargs): """ Constructor """ self.response_bytes = b'' super(AcceptRequest, self).__init__(response_bytes=b'', **kwargs)
[docs] def parse_bytes(self, daide_bytes): """ Builds the request from DAIDE bytes """ super(AcceptRequest, self).parse_bytes(daide_bytes) # Parsing lead_token, daide_bytes = parse_bytes(SingleToken, daide_bytes) response_bytes, daide_bytes = break_next_group(daide_bytes) response_bytes = strip_parentheses(response_bytes) assert str(lead_token) == 'YES', 'Expected YES request' assert not daide_bytes, '%s bytes remaining. Request is malformed' % len(daide_bytes) # Setting properties self.response_bytes = response_bytes
[docs]class RejectRequest(DaideRequest): """ Represents a REJ DAIDE request. Syntax: :: REJ (SVE ('gamename')) """ __slots__ = ['response_bytes'] params = { strings.RESPONSE_BYTES: bytes }
[docs] def __init__(self, **kwargs): """ Constructor """ self.response_bytes = b'' super(RejectRequest, self).__init__(response_bytes=b'', **kwargs)
[docs] def parse_bytes(self, daide_bytes): """ Builds the request from DAIDE bytes """ super(RejectRequest, self).parse_bytes(daide_bytes) # Parsing lead_token, daide_bytes = parse_bytes(SingleToken, daide_bytes) response_bytes, daide_bytes = break_next_group(daide_bytes) response_bytes = strip_parentheses(response_bytes) assert str(lead_token) == 'REJ', 'Expected REJ request' assert not daide_bytes, '%s bytes remaining. Request is malformed' % len(daide_bytes) # Setting properties self.response_bytes = response_bytes
# ==================== # Errors # ====================
[docs]class ParenthesisErrorRequest(DaideRequest): """ Represents a PRN DAIDE request. Sent by the client to specify an error in the set of parenthesis. Syntax: :: PRN (message) """ __slots__ = ['message_bytes'] params = { strings.MESSAGE_BYTES: bytes }
[docs] def __init__(self, **kwargs): """ Constructor """ self.message_bytes = b'' super(ParenthesisErrorRequest, self).__init__(message_bytes=b'', **kwargs)
[docs] def parse_bytes(self, daide_bytes): """ Builds the request from DAIDE bytes """ super(ParenthesisErrorRequest, self).parse_bytes(daide_bytes) # Parsing lead_token, daide_bytes = parse_bytes(SingleToken, daide_bytes) message_bytes, daide_bytes = break_next_group(daide_bytes) if message_bytes: message_bytes = strip_parentheses(message_bytes) else: message_bytes = strip_parentheses(daide_bytes) daide_bytes = b'' assert str(lead_token) == 'PRN', 'Expected PRN request' assert not daide_bytes, '%s bytes remaining. Request is malformed' % len(daide_bytes) # Setting properties self.message_bytes = message_bytes
[docs]class SyntaxErrorRequest(DaideRequest): """ Represents a HUH DAIDE request. Sent by the client to specify an error in a message. Syntax: :: HUH (message) """ __slots__ = ['message_bytes'] params = { strings.MESSAGE_BYTES: bytes }
[docs] def __init__(self, **kwargs): """ Constructor """ self.message_bytes = b'' super(SyntaxErrorRequest, self).__init__(message_bytes=b'', **kwargs)
[docs] def parse_bytes(self, daide_bytes): """ Builds the request from DAIDE bytes """ super(SyntaxErrorRequest, self).parse_bytes(daide_bytes) # Parsing lead_token, daide_bytes = parse_bytes(SingleToken, daide_bytes) message_bytes, daide_bytes = break_next_group(daide_bytes) message_bytes = strip_parentheses(message_bytes) assert str(lead_token) == 'HUH', 'Expected HUH request' assert not daide_bytes, '%s bytes remaining. Request is malformed' % len(daide_bytes) # Setting properties self.message_bytes = message_bytes
# ==================== # Admin Messages # ====================
[docs]class AdminMessageRequest(DaideRequest): """ Represents a ADM DAIDE request. Can be sent by the client to send a message to all clients. Should not be used for negotiation. Syntax: :: ADM ('message') """ __slots__ = ['adm_message'] params = { strings.ADM_MESSAGE: str }
[docs] def __init__(self, **kwargs): """ Constructor """ self.adm_message = '' super(AdminMessageRequest, self).__init__(adm_message='', **kwargs)
[docs] def parse_bytes(self, daide_bytes): """ Builds the request from DAIDE bytes """ super(AdminMessageRequest, self).parse_bytes(daide_bytes) # Parsing lead_token, daide_bytes = parse_bytes(SingleToken, daide_bytes) adm_message, daide_bytes = parse_bytes(String, daide_bytes) assert str(lead_token) == 'ADM', 'Expected ADM request' assert not daide_bytes, '%s bytes remaining. Request is malformed' % len(daide_bytes) # Setting properties self.adm_message = str(adm_message)
# ===================== # Constants and aliases # ===================== NME = NameRequest OBS = ObserverRequest IAM = IAmRequest HLO = HelloRequest MAP = MapRequest MDF = MapDefinitionRequest SCO = SupplyCentreOwnershipRequest NOW = CurrentPositionRequest HST = HistoryRequest SUB = SubmitOrdersRequest MIS = MissingOrdersRequest GOF = GoFlagRequest TME = TimeToDeadlineRequest DRW = DrawRequest SND = SendMessageRequest NOT = NotRequest YES = AcceptRequest REJ = RejectRequest PRN = ParenthesisErrorRequest HUH = SyntaxErrorRequest ADM = AdminMessageRequest # Constants __REQUEST_CONSTRUCTORS__ = {bytes(tokens.NME): NME, bytes(tokens.OBS): OBS, bytes(tokens.IAM): IAM, bytes(tokens.HLO): HLO, bytes(tokens.MAP): MAP, bytes(tokens.MDF): MDF, bytes(tokens.SCO): SCO, bytes(tokens.NOW): NOW, bytes(tokens.HST): HST, bytes(tokens.SUB): SUB, bytes(tokens.MIS): MIS, bytes(tokens.GOF): GOF, bytes(tokens.TME): TME, bytes(tokens.DRW): DRW, bytes(tokens.SND): SND, bytes(tokens.NOT): NOT, bytes(tokens.YES): YES, bytes(tokens.REJ): REJ, bytes(tokens.PRN): PRN, bytes(tokens.HUH): HUH, bytes(tokens.ADM): ADM}