#!/usr/bin/env python3

import re
import sys
from collections import namedtuple
import json

'''
program:
  : '\n'+ program
  | #empty
  | expr program
  ;
expr
  : term ( ( '+' | '-' )  term )*
  ;
term
  : factor ( ( '*' | '/' ) factor )*
  ;
factor
  : '-' factor
  |  INTEGER
  |  '(' expr ')'
  ;
'''


################################# Parser ################################


def parse(text):
    toks = tokenize(text)
    toksIndex = 0
    tok = toks[toksIndex]
    toksIndex += 1

    def program(asts):
        if peek('\n'):
            while peek('\n'):
                consume('\n')
            return program(asts)
        elif peek('EOF'):
            return asts;
        else:
            e = expr()
            asts.append(e)
            return program(asts)

    def expr():
        t = term()
        while peek('+') or peek('-'):
            op = tok.kind
            consume(op)
            t1 = term()
            t = { 'tag': op, 'kids': [ t, t1 ] }
        return t


    def term():
        f = factor()
        while peek('*') or peek('/'):
            op = tok.kind
            consume(op)
            f1 = factor()
            f = { 'tag': op, 'kids': [ f, f1 ] }
        return f

    def factor():
        if peek('-'):
            consume('-')
            f = factor()
            return { 'tag': '-', 'kids': [ f ] }
        elif peek('INT'):
            v = int(tok.lexeme);
            consume('INT');
            return v;
        else:
            consume('(')
            e = expr()
            consume(')')
            return e

    def peek(kind):
        nonlocal tok
        return tok.kind == kind

    def consume(kind):
        nonlocal tok, toks, toksIndex
        #print(f"consuming {kind} at {tok}")
        if (peek(kind)):
            tok = toks[toksIndex]
            toksIndex += 1
        else:
            error(kind, text)

    def error(kind, text):
        nonlocal tok
        pos = tok.pos
        if pos >= len(text) or text[pos] == '\n': pos -= 1
        lineBegin = text.rfind('\n', 0, pos)
        if lineBegin < 0: lineBegin = 0
        lineEnd = text.find('\n', pos)
        if lineEnd < 0: lineEnd = len(text)
        line = text[lineBegin:lineEnd]
        print(f"error: expecting '{kind}' but got '{tok.kind}'",
              file=sys.stderr)
        print(line, file=sys.stderr)
        nSpace = pos - lineBegin if pos >= lineBegin else 0
        print('^'.rjust(nSpace+1), file=sys.stderr)
        sys.exit(1)

    asts = [];
    program(asts)
    if tok.kind != 'EOF': error('EOF', text)
    return asts

################################# Lexer #################################

SKIP_RE = re.compile(r'(( |\t)|\#.*)+')
INT_RE = re.compile(r'\d+')

Token = namedtuple('Token', 'kind lexeme pos')

def tokenize(text, pos=0):
    toks = []
    while pos < len(text):
        m = SKIP_RE.match(text, pos)
        if m:
            pos += len(m.group())
        if pos >= len(text): break
        if m := INT_RE.match(text, pos):
            tok = Token('INT', m.group(), pos)
        else:
            tok = Token(text[pos], text[pos], pos)
        pos += len(tok.lexeme)
        toks.append(tok)
    toks.append(Token('EOF', '<EOF>', pos))
    return toks


################################## Main #################################

def main():
    text = sys.stdin.read()
    #print(tokenize(text))
    asts = parse(text)
    print(json.dumps(asts, separators=(',', ':'))) #no whitespace

if __name__ == "__main__":
    main() I want you to make changes to the code such that the python3 is used implement a parser for the following language of which is a subset of the syntax used for elixir data literals.

A sentence in the language consists of a sequence of zero-or-more data-literal's.

A data-literal is either a list literal, a tuple literal, a map literal, or a primitive literal.

A primitive literal is either an integer, atom or boolean.

A list literal is a sequence of 0-or-more comma-separated data-literal's within square brackets [ and ].

A tuple literal is a sequence of 0-or-more comma-separated data-literal's within braces { and }.

A map literal is a sequence of 0-or-more comma-separated key-pair's within a decorated left brace %{ and a regular right brace }.

A key-pair is either a sequence consisting of a data-literal, a right-arrow => followed by a data-literal, or a sequence consisting of a key followed by a data-literal.

An integer consists of a sequence of one-or-more digits, possibly containing internal underscores _.

An atom consists of a colon :, followed by an alphabetic character or underscore _ followed by a sequence of zero-or-more alphanumeric characters or underscores _.

A key is just like an atom but the : must be at the end instead of the start.

A boolean is one of the words true or false.

Whitespace and #-to-end-of-line comments should be ignored.

Note that a map literal of the form %{ key: 22 } is syntactic sugar for %{ :key => 22 }.

You will specifically need to submit a zip-archive which unpacks into a prj1-sol directory minimally containing a file elixir-data.ebnf plus two shell scripts make.sh and run.sh:

elixir-data.ebnf must contain a grammar for the above language using the EBNF notation described in class.

Running make.sh from any directory should build any artifacts needed to run your program within the prj1-sol directory.

Running run.sh from any directory should read and parse a sentence of the above language from standard input and output on standard output a single line containing the JSON representation of the parse.

Different kinds of data literals should be output as a JSON object having two properties:

%k
The kind of the literal, as defined below.

%v
The value of the literal, as defined below.

The top-level JSON should consist of a JSON list containing the JSON representations of the top-level data-literal's read from standard input.

The JSON representation of the different kind of literals should be as follows:

An integer has the JSON representation { "%k": "int", "%v": value } where value is a JSON integer respresenting the value of the integer. For example, the integer 123 should have the JSON representation { "%k": "int", "%v": 123 }.

An atom has the JSON representation { "%k": "atom", "%v": value } where value is a JSON string spelling out the atom. For example, the atom :_a32 should have the JSON representation { "%k": "atom", "%v": ":_a32" }.

A boolean has the JSON representation { "%k": "bool", "%v": value } where value is a JSON boolean representing the value of the boolean. For example, the boolean true should have the JSON representation { "%k": "bool", "%v": true }.

A key has the JSON representation { "%k": "atom", "%v": value } where value is a JSON string spelling out the key lexeme, but with the : moved to the front. For example, the key abc: should have the JSON representation { "%k": "atom", "%v": ":abc" }.

A list has the JSON representation { "%k": "list", "%v": value } where value is a JSON list containing the JSON representations of the individual items in the list. For example, the list [ 1, 2 ] should have the JSON representation:

	{ "%k": "list",
	  "%v": [
	     { "%k": "int", "%v": 1 },
	     { "%k": "int", "%v": 2 }
	  ]
	}
A tuple has the JSON representation { "%k": "tuple", "%v": value } where value is a JSON list containing the JSON representations of the individual items in the tuple. For example, the tuple { 1, :k } should have the JSON representation:

	{ "%k": "tuple",
	  "%v": [
	     { "%k": "int", "%v": 1 },
	     { "%k": "atom", "%v": ":k" }
	  ]
	}
A map has the JSON representation { "%k": "map", "%v": value } where value is a JSON list containing the 2-element JSON lists representing the individual elements in the map. For example, the map %{ :a => 22, b: 33 } should have the JSON representation:

	{ "%k": "map",
	  "%v": [
	     [ { "%k": "atom", "%v": ":a" },
	       { "%k": "int", "%v": 22 }
	     ],
	     [ { "%k": "atom", "%v": ":b" },
	       { "%k": "int", "%v": 33 }
	     ]
	  ]
	}
The JSON output should consist of a single line without any whitespace other than the newline terminator. The members of a JSON object may be output in any order.

If there are errors in the content, the program should exit with a non-zero status after detecting the first syntax error. It should output a suitable error message on standard error.

An annotated log of the running project and the provided tests should help clarify the above requirements. write the main python program that will be required.