<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 An Interpreter for a Simple Programming Language
In this notebook we develop an interpreter for a small programming language.
The grammar for this language is stored in the file Pure.g4.
Step1:  The grammar shown above does only contain skip actions.  The corrsponding grammar that is enriched with actions is stored in the file Simple.g4.
An example program that conforms to this grammar is stored in the file sum.sl.
Step2:  The file Simple.g4 contains a parser for the language described by the grammar Pure.g4. This parser returns
an abstract syntax tree.  This tree is represented as a nested tuple.
Step3:  The parser shown above will transform the program sum.sl into the nested tuple stored in the file sum.ast.
Step4:  The function main takes one parameter file.  This parameter is a string specifying a program file.
The function reads the program contained in this file and executes it.
Step5:  The function execute_list takes two arguments
Step6:  The function execute takes two arguments
Step7:  The function evaluate takes two arguments
<ASSISTANT_TASK:>
Python Code:
!cat -n Pure.g4
Explanation: An Interpreter for a Simple Programming Language
In this notebook we develop an interpreter for a small programming language.
The grammar for this language is stored in the file Pure.g4.
End of explanation
!cat sum.sl
Explanation: The grammar shown above does only contain skip actions.  The corrsponding grammar that is enriched with actions is stored in the file Simple.g4.
An example program that conforms to this grammar is stored in the file sum.sl.
End of explanation
!cat -n Simple.g4
Explanation: The file Simple.g4 contains a parser for the language described by the grammar Pure.g4. This parser returns
an abstract syntax tree.  This tree is represented as a nested tuple.
End of explanation
!cat sum.ast
!antlr4 -Dlanguage=Python3 Simple.g4
from SimpleLexer  import SimpleLexer
from SimpleParser import SimpleParser
import antlr4
%run ../AST-2-Dot.ipynb
Explanation: The parser shown above will transform the program sum.sl into the nested tuple stored in the file sum.ast.
End of explanation
def main(file):
    with open(file, 'r') as handle:
        program_text = handle.read()
    input_stream  = antlr4.InputStream(program_text)
    lexer         = SimpleLexer(input_stream)
    token_stream  = antlr4.CommonTokenStream(lexer)
    parser        = SimpleParser(token_stream)
    result        = parser.program()
    Statements    = result.stmnt_list
    ast           = tuple2dot(Statements)
    print(Statements)
    display(ast)
    ast.render('ast', view=True)
    execute_tuple(Statements)
Explanation: The function main takes one parameter file.  This parameter is a string specifying a program file.
The function reads the program contained in this file and executes it.
End of explanation
def execute_tuple(Statement_List, Values={}):
    for stmnt in Statement_List:
        execute(stmnt, Values)
Explanation: The function execute_list takes two arguments:
- Statement_List is a list of statements,
- Values is a dictionary assigning integer values to variable names.
The function executes the statements in Statement_List.  If an assignment statement is executed,
the dictionary Values is updated.
End of explanation
L = [1,2,3,4,5]
a, b, *R = L
a, b, R
def execute(stmnt, Values):
    op = stmnt[0]
    if stmnt == 'program':
        pass
    elif op == ':=':
        _, var, value = stmnt
        Values[var] = evaluate(value, Values)
    elif op == 'read':
        _, var = stmnt
        Values[var] = int(input())
    elif op == 'print':
        _, expr = stmnt
        print(evaluate(expr, Values))
    elif op == 'if':
        _, test, *SL = stmnt
        if evaluate(test, Values):
            execute_tuple(SL, Values)
    elif op == 'while':
        _, test, *SL = stmnt
        while evaluate(test, Values):
            execute_tuple(SL, Values)
    else:
        assert False, f'{stmnt} unexpected'
Explanation: The function execute takes two arguments:
- stmnt is a statement,
- Values is a dictionary assigning integer values to variable names.
The function executes the statements in Statement_List.  If an assignment statement is executed,
the dictionary Values is updated.
End of explanation
def evaluate(expr, Values):
    if isinstance(expr, int):
        return expr
    if isinstance(expr, str):
        return Values[expr] 
    op = expr[0]
    if op == '==':
        _, lhs, rhs = expr
        return evaluate(lhs, Values) == evaluate(rhs, Values)
    if op == '<':
        _, lhs, rhs = expr
        return evaluate(lhs, Values) < evaluate(rhs, Values)
    if op == '+':
        _, lhs, rhs = expr
        return evaluate(lhs, Values) + evaluate(rhs, Values)
    if op == '-':
        _, lhs, rhs = expr
        return evaluate(lhs, Values) - evaluate(rhs, Values)
    if op == '*':
        _, lhs, rhs = expr
        return evaluate(lhs, Values) * evaluate(rhs, Values)
    if op == '/':
        _, lhs, rhs = expr
        return evaluate(lhs, Values) / evaluate(rhs, Values)
    assert False, f'{stmnt} unexpected'
!cat sum.sl
main('sum.sl')
!cat factorial.sl
main('factorial.sl')
!rm *.py *.tokens *.interp
!rm -r __pycache__/
!rm *.pdf
!ls
Explanation: The function evaluate takes two arguments:
- expr is a logical expression or an arithmetic expression,
- Values is a dictionary assigning integer values to variable names.
The function evaluates the given expression and returns this value.
End of explanation
<END_TASK>