import json
import re import sys

token_patterns = [
(r’#[^\n]‘, None),
(r’\s+‘, None),
(r’[‘, ‘LBRACKET’),
(r’]‘, ‘RBRACKET’),
(r’{‘, ‘LBRACE’),
(r’}‘, ‘RBRACE’),
(r’%{‘, ‘LMAPBRACE’),
(r’=>‘, ‘ARROW’),
(r’,‘, ‘COMMA’),
(r’(true|false)‘, ‘BOOLEAN’),
(r’(0|[1-9][0-9_])‘, ‘INTEGER’),
(r’:[A-Za-z_]\w*‘, ‘ATOM’),
(r’[A-Za-z_]\w*‘, ‘KEY’),
(r’:‘, ‘COLON’),
]

regex_parts = [f’(?P<{name}>{pattern})’ if name else f’(?:{pattern})’ for pattern, name in token_patterns]
token_regex = ‘|’.join(regex_parts)
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]!r} at index {pos}‘)
pos = match.end()
if match.lastgroup:
yield match.lastgroup, match.group(match.lastgroup)

def make_hashable(value):
“”“Recursively converts JSON-like objects to an immutable and hashable type.”“”
if isinstance(value, list):
return tuple(make_hashable(x) for x in value)
elif isinstance(value, dict):
return tuple((make_hashable(k), make_hashable(v)) for k, v in value.items())
else:
return value # Integers, strings, booleans, and None are already hashable


class Node:
def init(self, kind, value):
self.kind = kind
self.value = value

def to_json(self):
if self.kind == “map”:
# Corrected conditional to account for empty maps
if not self.value: # self.value is empty
return {“%k”: self.kind, “%v”: {}} # Return an empty dictionary instead of an empty list
return {“%k”: self.kind, “%v”: {k.to_json()[“%v”]: v.to_json() for k, v in self.value}}
elif self.kind == “list”:
return {“%k”: self.kind, “%v”: [item.to_json() for item in self.value]}
else:
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:
node = self.parse_data_literal()
nodes.append(node)
if self.current_token and self.current_token[0] in {‘COMMA’, ‘COLON’, ‘ARROW’}:
self.next_token()
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])
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]
self.next_token()
if self.current_token and self.current_token[0] == ‘COLON’:
self.next_token()
return Node(‘key’, value)
return Node(‘string’, value)
else:
raise ParserException(f’Unexpected token {self.current_token[1]}’)

def parse_list(self):
self.next_token()
items = []
while self.current_token and self.current_token[0] != ‘RBRACKET’:
item = self.parse_data_literal()
items.append(item)
if self.current_token and self.current_token[0] == ‘COMMA’:
self.next_token()
if not self.current_token or self.current_token[0] != ‘RBRACKET’:
raise ParserException(‘List not properly terminated with ]’)
self.next_token()
return Node(‘list’, items)

def parse_tuple(self):
items = []
self.next_token()
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()
if self.current_token is None or self.current_token[0] != ‘RBRACE’:
raise ParserException(‘Tuple not properly terminated with }’)
self.next_token()
return Node(‘tuple’, [item.to_json() for item in items])

def parse_map(self):
self.next_token() # Skip the LMAPBRACE
map_items = {}
while self.current_token and self.current_token[0] != ‘RBRACE’:
key = self.parse_data_literal() # Parses the key
if self.current_token and self.current_token[0] not in {‘ARROW’, ‘COLON’}:
raise ParserException(f’Expected “=>” or “:”, found {self.current_token}‘)
self.next_token() # Skip the ARROW or COLON

value = self.parse_data_literal() # Parses the value associated with the key

# Serialize and then parse the key Node to get a hashable dict key
json_key = json.dumps(key.to_json(), separators=(’,‘, ‘:’))
json_key = json.loads(json_key)

# Convert JSON objects to immutable, hashable tuples
hashable_key = make_hashable(json_key)

map_items[hashable_key] = value.to_json()

if self.current_token and self.current_token[0] == ‘COMMA’:
self.next_token() # Skip the COMMA

if not self.current_token or self.current_token[0] != ‘RBRACE’:
raise ParserException(‘Map not properly terminated with “}”’)
self.next_token() # Skip the RBRACE

return Node(‘map’, map_items)

def main():
try:
text = sys.stdin.read()

tokens = list(tokenize(text))

if not tokens:
print(json.dumps([]))
else:
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’)
except ParserException as e:
sys.stderr.write(str(e) + ‘\n’)


if name == ‘main’:
main()
Standard Error
Expected “=>” or “:”, found (‘LBRACKET’, ‘[’)

diff compound-data
— /autograder/source/test-files/50-compound-data.out 2024-01-31 14:31:45.000000000 +0000
+++ tmp/50-compound-data.test 2024-02-19 17:16:11.429490265 +0000
@@ -1,203 +0,0 @@
-[
input is %{ [:a, 22] => { [1, 2, 3], :x },
   x: [99, %{ a: 33 }]
}

{ [1, 2], {:a, 22}, %{ a: 99, :b => 11} }

[ {1, 2}, %{[:x] => 33, b: 44}, :c, [], [:d, 55] ] and the output should be [
  {
    "%k": "map",
    "%v": [
      [
        {
          "%k": "list",
          "%v": [
            {
              "%k": "atom",
              "%v": ":a"
            },
            {
              "%k": "int",
              "%v": 22
            }
          ]
        },
        {
          "%k": "tuple",
          "%v": [
            {
              "%k": "list",
              "%v": [
                {
                  "%k": "int",
                  "%v": 1
                },
                {
                  "%k": "int",
                  "%v": 2
                },
                {
                  "%k": "int",
                  "%v": 3
                }
              ]
            },
            {
              "%k": "atom",
              "%v": ":x"
            }
          ]
        }
      ],
      [
        {
          "%k": "atom",
          "%v": ":x"
        },
        {
          "%k": "list",
          "%v": [
            {
              "%k": "int",
              "%v": 99
            },
            {
              "%k": "map",
              "%v": [
                [
                  {
                    "%k": "atom",
                    "%v": ":a"
                  },
                  {
                    "%k": "int",
                    "%v": 33
                  }
                ]
              ]
            }
          ]
        }
      ]
    ]
  },
  {
    "%k": "tuple",
    "%v": [
      {
        "%k": "list",
        "%v": [
          {
            "%k": "int",
            "%v": 1
          },
          {
            "%k": "int",
            "%v": 2
          }
        ]
      },
      {
        "%k": "tuple",
        "%v": [
          {
            "%k": "atom",
            "%v": ":a"
          },
          {
            "%k": "int",
            "%v": 22
          }
        ]
      },
      {
        "%k": "map",
        "%v": [
          [
            {
              "%k": "atom",
              "%v": ":a"
            },
            {
              "%k": "int",
              "%v": 99
            }
          ],
          [
            {
              "%k": "atom",
              "%v": ":b"
            },
            {
              "%k": "int",
              "%v": 11
            }
          ]
        ]
      }
    ]
  },
  {
    "%k": "list",
    "%v": [
      {
        "%k": "tuple",
        "%v": [
          {
            "%k": "int",
            "%v": 1
          },
          {
            "%k": "int",
            "%v": 2
          }
        ]
      },
      {
        "%k": "map",
        "%v": [
          [
            {
              "%k": "list",
              "%v": [
                {
                  "%k": "atom",
                  "%v": ":x"
                }
              ]
            },
            {
              "%k": "int",
              "%v": 33
            }
          ],
          [
            {
              "%k": "atom",
              "%v": ":b"
            },
            {
              "%k": "int",
              "%v": 44
            }
          ]
        ]
      },
      {
        "%k": "atom",
        "%v": ":c"
      },
      {
        "%k": "list",
        "%v": []
      },
      {
        "%k": "list",
        "%v": [
          {
            "%k": "atom",
            "%v": ":d"
          },
          {
            "%k": "int",
            "%v": 55
          }
        ]
      }
    ]
  }
] fix the error