Fundamentals 9 min read

Building an Arithmetic Interpreter in Python Using PLY

This article explains how to create a simple arithmetic expression interpreter in Python by leveraging the PLY (Python Lex‑Yacc) library, covering token definition, lexer implementation, BNF grammar, parser rules, operator precedence, error handling, and a complete runnable example.

Python Programming Learning Circle
Python Programming Learning Circle
Python Programming Learning Circle
Building an Arithmetic Interpreter in Python Using PLY

Computers only understand machine code, and programming languages exist to let humans write instructions more easily; the real magic is performed by compilers and interpreters that translate source code into machine code. This article demonstrates how to design an interpreter that evaluates arithmetic expressions.

We use the PLY library, a Python implementation of Lex/Yacc, which can be installed via <code>$ pip install ply</code> . PLY provides tools for lexical analysis (lexer) and parsing (parser).

Tokens are the smallest meaningful units for the interpreter. The token list includes data types (NUM, FLOAT) and arithmetic operators (PLUS, MINUS, MUL, DIV, POW) as well as parentheses (LPAREN, RPAREN). Example token definition:

<code>tokens = (
    "NUM",
    "FLOAT",
    "PLUS",
    "MINUS",
    "MUL",
    "DIV",
    "POW",
    "LPAREN",
    "RPAREN",
)</code>

The lexer converts input strings into tokens using regular expressions. Sample lexer rules:

<code># Token regexes
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"

# Actions for each token
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

def t_error(t):
    print(f"keyword not found: {t.value[0]}\nline {t.lineno}")
    t.lexer.skip(1)

def t_newline(t):
    r"""\n+"""
    t.lexer.lineno += t.value.count("\n")
</code>

We import the lexer with import ply.lex as lex and build it via lex.lex() . The lexer processes the input string and yields LexToken objects.

The parser is built using YACC. We define operator precedence:

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

Grammar rules are written in Backus‑Naur Form (BNF). Example BNF for expressions:

<code>expression : expression '+' expression
           | expression '-' expression
           | expression '/' expression
           | expression '*' expression
           | expression '^' expression
           | '+' expression
           | '-' expression
           | '(' expression ')'
           | NUM
           | FLOAT
</code>

Parser functions map these rules to Python actions, handling division by zero, unary operators, and literals. Error handling is provided via a p_error function.

Finally, the complete program ties everything together: it configures logging, creates the lexer and parser, and enters a REPL loop that reads expressions, parses them, and prints the result. The full source code is included in the article.

In conclusion, while the topic is extensive and the article cannot cover every detail, it gives readers a solid foundation for building a simple arithmetic interpreter in Python.

PythonBNFInterpreterParserLexerPLY
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.