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.
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:
tokens = (
# data types
"NUM",
"FLOAT",
# arithmetic operators
"PLUS",
"MINUS",
"MUL",
"DIV",
# parentheses
"LPAREN",
"RPAREN",
)The lexer rules are expressed with regular expressions, for example:
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"Action functions convert matched text to Python values:
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 tAn 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:
precedence = (
("left", "PLUS", "MINUS"),
("left", "MUL", "DIV"),
("left", "POW"),
("right", "UPLUS", "UMINUS"),
)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:
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])Additional rules handle unary plus ( UPLUS), unary minus ( UMINUS), and numeric literals:
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]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:
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")The article concludes by acknowledging that the topic is extensive and promises further detailed posts.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
