import json
import re
import sys

# Define regular expressions for the tokens
token_patterns = [
    (r'\s+', None),  # whitespace (ignored)
    (r'#.', None),  # comments (ignored)
    (r'[', 'LBRACKET'),
    (r']', 'RBRACKET'),
    (r'{', 'LBRACE'),
    (r'}', 'RBRACE'),
    (r'%{', 'LMAPBRACE'),
    (r'=>', 'ARROW'),
    (r':', 'COLON'),  # The colon was missing a pattern name
    (r',', 'COMMA'),
    (r'(?:true|false)', 'BOOLEAN'),  # The boolean literal needed grouping
    (r'(?:0|[1-9]?(?:\d?)\d)', 'INTEGER'), # Refine the integer pattern to allow leading zeros only for the numeral 0
    (r':[A-Za-z_][\w]', 'ATOM'),
    (r'[A-Za-z_][\w]:', 'KEY'),
]

# Compile the regular expressions and make a lookup table
token_regex = '|'.join('(?:%s)' % pattern if name is None else '(?P<%s>%s)' % (name, pattern) for pattern, name in token_patterns)
token_re = re.compile(token_regex)
class TokenizerException(Exception): pass
class ParserException(Exception): pass

def tokenize(text):
    pos = 0
    while pos < len(text):
        match = token_re.match(text, pos)
        if not match:
            raise TokenizerException(f'Illegal character {text[pos]} at index {pos}')
        pos = match.end()
        tok_type = match.lastgroup
        tok_value = match.group(tok_type)
        if tok_type and tok_value:
            yield tok_type, tok_value

# Define a node structure to represent the parse tree
class Node:
    def __init__(self, kind, value):
        self.kind = kind
        self.value = value

    def to_json(self):
        return { "%k": self.kind, "%v": self.value }

class Parser:
    def __init__(self, tokens):
        self.tokens = iter(tokens)
        self.current_token = None
        self.next_token()

    def next_token(self):
        try:
            self.current_token = next(self.tokens)
        except StopIteration:
            self.current_token = None

    def parse(self):
        result = self.parse_sentence()
        if self.current_token is not None:
            raise ParserException('Unexpected token at the end')
        return result

    def parse_sentence(self):
        nodes = []
        while self.current_token is not None and self.current_token[0] not in {'RBRACE', 'RBRACKET'}:
            node = self.parse_data_literal()
            nodes.append(node)
            if self.current_token and self.current_token[0] == 'COMMA':
                self.next_token()  # Skip commas
        return nodes

    def parse_data_literal(self):
        if self.current_token[0] == 'LBRACKET':
            return self.parse_list()
        elif self.current_token[0] == 'LBRACE':
            return self.parse_tuple()
        elif self.current_token[0] == 'LMAPBRACE':
            return self.parse_map()
        elif self.current_token[0] == 'INTEGER':
            value = int(self.current_token[1].replace('_', ''))
            self.next_token()
            return Node('int', value)
        elif self.current_token[0] == 'ATOM':
            value = self.current_token[1]
            self.next_token()
            return Node('atom', value)
        elif self.current_token[0] == 'BOOLEAN':
            value = self.current_token[1] == 'true'
            self.next_token()
            return Node('bool', value)
        elif self.current_token[0] == 'KEY':
            value = ':' + self.current_token[1][:-1]
            self.next_token()
            return Node('atom', value)
        else:
            raise ParserException(f'Unexpected token {self.current_token[1]}')

    def parse_list(self):
        items = []
        self.next_token()  # Skip '['
        while self.current_token is not None and self.current_token[0] != 'RBRACKET':
            items.append(self.parse_data_literal())
            if self.current_token and self.current_token[0] == 'COMMA':
                self.next_token()  # Skip commas
        if self.current_token is None or self.current_token[0] != 'RBRACKET':
            raise ParserException('List not properly terminated with ]')
        self.next_token()  # Skip ']'
        return Node('list', [item.to_json() for item in items])

    def parse_tuple(self):
        items = []
        self.next_token()  # Skip '{'
        while self.current_token is not None and self.current_token[0] != 'RBRACE':
            items.append(self.parse_data_literal())
            if self.current_token and self.current_token[0] == 'COMMA':
                self.next_token()  # Skip commas
        if self.current_token is None or self.current_token[0] != 'RBRACE':
            raise ParserException('Tuple not properly terminated with }')
        self.next_token()  # Skip '}'
        return Node('tuple', [item.to_json() for item in items])

    def parse_map(self):
        items = []
        self.next_token()  # Skip '%{'
        while self.current_token is not None and self.current_token[0] != 'RBRACE':
            key = self.parse_data_literal()
            if self.current_token and self.current_token[0] == 'COLON':
                self.next_token()  # Skip ':'
                value = self.parse_data_literal()
                items.append([key.to_json(), value.to_json()])
            elif self.current_token and self.current_token[0] == 'ARROW':
                self.next_token()  # Skip '=>'
                value = self.parse_data_literal()
                items.append([key.to_json(), value.to_json()])
            else:
                raise ParserException('Invalid map entry format, expected ":" or "=>"')
        if self.current_token is None or self.current_token[0] != 'RBRACE':
            raise ParserException('Map not properly terminated with }')
        self.next_token()  # Skip '}'
        return Node('map', items)

def main():
    try:
        # Prompt for input
        text = input("Enter your input: ")
        tokens = tokenize(text)
        parser = Parser(tokens)
        result = parser.parse()
        json_output = json.dumps([node.to_json() for node in result], separators=(',', ':'))
        print(json_output)
    except TokenizerException as e:
        sys.stderr.write(str(e) + '\n')
        sys.exit(1)
    except ParserException as e:
        sys.stderr.write(str(e) + '\n')
        sys.exit(1)

if __name__ == '__main__':
    main()
Exception has occurred: SystemExit
1
  File "C:\Users\apoor\OneDrive\Documents\script.py", line 153, in main
    result = parser.parse()
             ^^^^^^^^^^^^^^
  File "C:\Users\apoor\OneDrive\Documents\script.py", line 63, in parse
    result = self.parse_sentence()
             ^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\apoor\OneDrive\Documents\script.py", line 71, in parse_sentence
    node = self.parse_data_literal()
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\apoor\OneDrive\Documents\script.py", line 101, in parse_data_literal
    raise ParserException(f'Unexpected token {self.current_token[1]}')
ParserException: Unexpected token :

During handling of the above exception, another exception occurred:

  File "C:\Users\apoor\OneDrive\Documents\script.py", line 161, in main
    sys.exit(1)
  File "C:\Users\apoor\OneDrive\Documents\script.py", line 164, in <module>
    main()
SystemExit: 1