Fundamentals 9 min read

Building a Simple Python Interpreter with PLY

This article guides readers through designing and implementing a basic Python interpreter that can evaluate arithmetic expressions by defining tokens, writing a lexer and parser with PLY, specifying BNF grammar, handling precedence, and assembling a runnable program that processes user input.

Python Programming Learning Circle
Python Programming Learning Circle
Python Programming Learning Circle
Building a Simple Python Interpreter with PLY

In this tutorial we design a simple Python interpreter capable of evaluating arithmetic expressions using the PLY (Python Lex‑Yacc) library.

Token definitions are listed as a tuple of token names such as NUM , FLOAT , PLUS , MINUS , MUL , DIV , LPAREN , and RPAREN :

<code>tokens = (
    # data types
    "NUM",
    "FLOAT",
    # arithmetic operators
    "PLUS",
    "MINUS",
    "MUL",
    "DIV",
    # parentheses
    "LPAREN",
    "RPAREN",
)</code>

The lexer rules are expressed with regular expressions, for example:

<code>t_PLUS   = r"\+"
t_MINUS  = r"\-"
t_MUL    = r"\*"
t_DIV    = r"/"
t_LPAREN = r"\("
t_RPAREN = r"\)"
t_POW    = r"\^"
# ignore spaces and tabs
t_ignore = " \t"
</code>

Action functions convert matched text to Python values:

<code>def t_FLOAT(t):
    r"""\d+\.\d+"""
    t.value = float(t.value)
    return t

def t_NUM(t):
    r"""\d+"""
    t.value = int(t.value)
    return t
</code>

An error handler reports unknown characters and skips them, while a newline rule updates the line number.

Operator precedence is declared as a tuple of associativity specifications:

<code>precedence = (
    ("left", "PLUS", "MINUS"),
    ("left", "MUL", "DIV"),
    ("left", "POW"),
    ("right", "UPLUS", "UMINUS"),
)
</code>

The grammar is written in Backus‑Naur Form (BNF) and implemented as parser functions. The main expression rule handles binary operations, unary plus/minus, parentheses, numbers, and floats:

<code>def p_expression(p):
    """expression : expression PLUS expression
                 | expression MINUS expression
                 | expression DIV expression
                 | expression MUL expression
                 | expression POW expression"""
    if (p[2], p[3]) == ("/", 0):
        p[0] = float("INF")
    else:
        p[0] = ops[p[2]](p[1], p[3])
</code>

Additional rules handle unary plus ( UPLUS ), unary minus ( UMINUS ), and numeric literals:

<code>def p_expression_uplus_or_expr(p):
    """expression : PLUS expression %prec UPLUS
                 | LPAREN expression RPAREN"""
    p[0] = p[2]

def p_expression_uminus(p):
    """expression : MINUS expression %prec UMINUS"""
    p[0] = -p[2]

def p_expression_num(p):
    """expression : NUM
                 | FLOAT"""
    p[0] = p[1]
</code>

A syntax‑error rule prints a helpful message when parsing fails.

The complete program imports the necessary modules, builds the lexer and parser, and enters an interactive loop that reads expressions, parses them, and prints the result:

<code>if __name__ == "__main__":
    basicConfig(level=INFO, filename="logs.txt")
    lexer = lex.lex()
    parser = yacc.yacc()
    while True:
        try:
            result = parser.parse(input(">>> "), debug=getLogger())
            print(result)
        except AttributeError:
            print("invalid syntax")
</code>

The article concludes by acknowledging that the topic is extensive and promises further detailed posts.

TutorialBNFInterpreterParserLexerPLY
Python Programming Learning Circle
Written by

Python Programming Learning Circle

A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.