Writing Parsers and Compilers with PLY
-
Upload
david-beazley-dabeaz-llc -
Category
Technology
-
view
9.057 -
download
9
description
Transcript of Writing Parsers and Compilers with PLY
![Page 1: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/1.jpg)
Writing Parsers and Compilers with PLYDavid Beazley
http://www.dabeaz.com
February 23, 2007
![Page 2: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/2.jpg)
Overview
• Crash course on compilers
• An introduction to PLY
• Notable PLY features (why use it?)
• Experience writing a compiler in Python
![Page 3: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/3.jpg)
Background
• Programs that process other programs
• Compilers
• Interpreters
• Wrapper generators
• Domain-specific languages
• Code-checkers
![Page 4: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/4.jpg)
Example
/* Compute GCD of two integers */fun gcd(x:int, y:int) g: int; begin g := y; while x > 0 do begin g := x; x := y - (y/x)*x; y := g end; return g end
• Parse and generate assembly code
![Page 5: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/5.jpg)
Compilers 101
parser
• Compilers have multiple phases
• First phase usually concerns "parsing"
• Read program and create abstract representation
/* Compute GCD of two integers */fun gcd(x:int, y:int) g: int; begin g := y; while x > 0 do begin g := x; x := y - (y/x)*x; y := g end; return g end
![Page 6: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/6.jpg)
Compilers 101
• Code generation phase
• Process the abstract representation
• Produce some kind of output
codegenLOAD R1, ALOAD R2, BADD R1,R2,R1STORE C, R1...
![Page 7: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/7.jpg)
Commentary
• There are many advanced details
• Most people care about code generation
• Yet, parsing is often the most annoying problem
• A major focus of tool building
![Page 8: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/8.jpg)
Parsing in a Nutshell• Lexing : Input is split into tokens
b = 40 + 20*(2+3)/37.5
NAME = NUM + NUM * ( NUM + NUM ) / FLOAT
• Parsing : Applying language grammar rules
=
NAME +
NUM
FLOAT
/
NUM
*
+
NUM NUM
![Page 9: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/9.jpg)
Lex & Yacc• Programming tools for writing parsers
• Lex - Lexical analysis (tokenizing)
• Yacc - Yet Another Compiler Compiler (parsing)
• History: - Yacc : ~1973. Stephen Johnson (AT&T)- Lex : ~1974. Eric Schmidt and Mike Lesk (AT&T)
• Variations of both tools are widely known
• Covered in compilers classes and textbooks
![Page 10: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/10.jpg)
Lex/Yacc Big Picture
tokenspecification
lexer.l
![Page 11: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/11.jpg)
Lex/Yacc Big Picture
tokenspecification
grammarspecification
lexer.l
/* lexer.l */%{#include “header.h”int lineno = 1;%}%%[ \t]* ; /* Ignore whitespace */\n { lineno++; }[0-9]+ { yylval.val = atoi(yytext); return NUMBER; }[a-zA-Z_][a-zA-Z0-9_]* { yylval.name = strdup(yytext); return ID; }\+ { return PLUS; }- { return MINUS; }\* { return TIMES; }\/ { return DIVIDE; } = { return EQUALS; }%%
![Page 12: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/12.jpg)
Lex/Yacc Big Picture
tokenspecification
lexer.l
lex
lexer.c
![Page 13: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/13.jpg)
Lex/Yacc Big Picture
tokenspecification
grammarspecification
lexer.l parser.y
lex
lexer.c
![Page 14: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/14.jpg)
Lex/Yacc Big Picture
tokenspecification
grammarspecification
lexer.l parser.y
lex
lexer.c.c
/* parser.y */%{#include “header.h”%}%union { char *name; int val;}%token PLUS MINUS TIMES DIVIDE EQUALS%token<name> ID;%token<val> NUMBER;%%start : ID EQUALS expr;expr : expr PLUS term | expr MINUS term | term ;...
![Page 15: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/15.jpg)
Lex/Yacc Big Picture
tokenspecification
grammarspecification
lexer.l parser.y
lex
lexer.c
yacc
parser.c
![Page 16: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/16.jpg)
Lex/Yacc Big Picture
tokenspecification
grammarspecification
lexer.l parser.y
lex
lexer.c
yacc
parser.c
typecheck.c codegen.c otherstuff.c
![Page 17: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/17.jpg)
Lex/Yacc Big Picture
tokenspecification
grammarspecification
lexer.l parser.y
lex
lexer.c
yacc
parser.c
typecheck.c codegen.c otherstuff.c
mycompiler
![Page 18: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/18.jpg)
What is PLY?
• PLY = Python Lex-Yacc
• A Python version of the lex/yacc toolset
• Same functionality as lex/yacc
• But a different interface
• Influences : Unix yacc, SPARK (John Aycock)
![Page 19: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/19.jpg)
Some History
• Late 90's : "Why isn't SWIG written in Python?"
• 2001 : Taught a compilers course. Students write a compiler in Python as an experiment.
• 2001 : PLY-1.0 developed and released
• 2001-2005: Occasional maintenance
• 2006 : Major update to PLY-2.x.
![Page 20: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/20.jpg)
PLY Package
• PLY consists of two Python modules
ply.lexply.yacc
• You simply import the modules to use them
• However, PLY is not a code generator
![Page 21: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/21.jpg)
ply.lex
• A module for writing lexers
• Tokens specified using regular expressions
• Provides functions for reading input text
• An annotated example follows...
![Page 22: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/22.jpg)
ply.lex exampleimport ply.lex as lextokens = [ ‘NAME’,’NUMBER’,’PLUS’,’MINUS’,’TIMES’, ’DIVIDE’, EQUALS’ ]t_ignore = ‘ \t’ t_PLUS = r’\+’t_MINUS = r’-’t_TIMES = r’\*’t_DIVIDE = r’/’t_EQUALS = r’=’t_NAME = r’[a-zA-Z_][a-zA-Z0-9_]*’
def t_NUMBER(t): r’\d+’ t.value = int(t.value) return t
lex.lex() # Build the lexer
![Page 23: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/23.jpg)
ply.lex exampleimport ply.lex as lextokens = [ ‘NAME’,’NUMBER’,’PLUS’,’MINUS’,’TIMES’, ’DIVIDE’, EQUALS’ ]t_ignore = ‘ \t’ t_PLUS = r’\+’t_MINUS = r’-’t_TIMES = r’\*’t_DIVIDE = r’/’t_EQUALS = r’=’t_NAME = r’[a-zA-Z_][a-zA-Z0-9_]*’
def t_NUMBER(t): r’\d+’ t.value = int(t.value) return t
lex.lex() # Build the lexer
tokens list specifiesall of the possible tokens
![Page 24: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/24.jpg)
ply.lex exampleimport ply.lex as lextokens = [ ‘NAME’,’NUMBER’,’PLUS’,’MINUS’,’TIMES’, ’DIVIDE’, EQUALS’ ]t_ignore = ‘ \t’ t_PLUS = r’\+’t_MINUS = r’-’t_TIMES = r’\*’t_DIVIDE = r’/’t_EQUALS = r’=’t_NAME = r’[a-zA-Z_][a-zA-Z0-9_]*’
def t_NUMBER(t): r’\d+’ t.value = int(t.value) return t
lex.lex() # Build the lexer
Each token has a matchingdeclaration of the form
t_TOKNAME
![Page 25: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/25.jpg)
ply.lex exampleimport ply.lex as lextokens = [ ‘NAME’,’NUMBER’,’PLUS’,’MINUS’,’TIMES’, ’DIVIDE’, EQUALS’ ]t_ignore = ‘ \t’ t_PLUS = r’\+’t_MINUS = r’-’t_TIMES = r’\*’t_DIVIDE = r’/’t_EQUALS = r’=’t_NAME = r’[a-zA-Z_][a-zA-Z0-9_]*’
def t_NUMBER(t): r’\d+’ t.value = int(t.value) return t
lex.lex() # Build the lexer
These names must match
![Page 26: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/26.jpg)
ply.lex exampleimport ply.lex as lextokens = [ ‘NAME’,’NUMBER’,’PLUS’,’MINUS’,’TIMES’, ’DIVIDE’, EQUALS’ ]t_ignore = ‘ \t’ t_PLUS = r’\+’t_MINUS = r’-’t_TIMES = r’\*’t_DIVIDE = r’/’t_EQUALS = r’=’t_NAME = r’[a-zA-Z_][a-zA-Z0-9_]*’
def t_NUMBER(t): r’\d+’ t.value = int(t.value) return t
lex.lex() # Build the lexer
Tokens are defined by regular expressions
![Page 27: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/27.jpg)
ply.lex exampleimport ply.lex as lextokens = [ ‘NAME’,’NUMBER’,’PLUS’,’MINUS’,’TIMES’, ’DIVIDE’, EQUALS’ ]t_ignore = ‘ \t’ t_PLUS = r’\+’t_MINUS = r’-’t_TIMES = r’\*’t_DIVIDE = r’/’t_EQUALS = r’=’t_NAME = r’[a-zA-Z_][a-zA-Z0-9_]*’
def t_NUMBER(t): r’\d+’ t.value = int(t.value) return t
lex.lex() # Build the lexer
For simple tokens,strings are used.
![Page 28: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/28.jpg)
ply.lex exampleimport ply.lex as lextokens = [ ‘NAME’,’NUMBER’,’PLUS’,’MINUS’,’TIMES’, ’DIVIDE’, EQUALS’ ]t_ignore = ‘ \t’ t_PLUS = r’\+’t_MINUS = r’-’t_TIMES = r’\*’t_DIVIDE = r’/’t_EQUALS = r’=’t_NAME = r’[a-zA-Z_][a-zA-Z0-9_]*’
def t_NUMBER(t): r’\d+’ t.value = int(t.value) return t
lex.lex() # Build the lexer
Functions are used whenspecial action code
must execute
![Page 29: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/29.jpg)
ply.lex exampleimport ply.lex as lextokens = [ ‘NAME’,’NUMBER’,’PLUS’,’MINUS’,’TIMES’, ’DIVIDE’, EQUALS’ ]t_ignore = ‘ \t’ t_PLUS = r’\+’t_MINUS = r’-’t_TIMES = r’\*’t_DIVIDE = r’/’t_EQUALS = r’=’t_NAME = r’[a-zA-Z_][a-zA-Z0-9_]*’
def t_NUMBER(t): r’\d+’ t.value = int(t.value) return t
lex.lex() # Build the lexer
docstring holdsregular expression
![Page 30: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/30.jpg)
ply.lex exampleimport ply.lex as lextokens = [ ‘NAME’,’NUMBER’,’PLUS’,’MINUS’,’TIMES’, ’DIVIDE’, EQUALS’ ]t_ignore = ‘ \t’ t_PLUS = r’\+’t_MINUS = r’-’t_TIMES = r’\*’t_DIVIDE = r’/’t_EQUALS = r’=’t_NAME = r’[a-zA-Z_][a-zA-Z0-9_]*’
def t_NUMBER(t): r’\d+’ t.value = int(t.value) return t
lex.lex() # Build the lexer
Specifies ignoredcharacters between
tokens (usually whitespace)
![Page 31: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/31.jpg)
ply.lex exampleimport ply.lex as lextokens = [ ‘NAME’,’NUMBER’,’PLUS’,’MINUS’,’TIMES’, ’DIVIDE’, EQUALS’ ]t_ignore = ‘ \t’ t_PLUS = r’\+’t_MINUS = r’-’t_TIMES = r’\*’t_DIVIDE = r’/’t_EQUALS = r’=’t_NAME = r’[a-zA-Z_][a-zA-Z0-9_]*’
def t_NUMBER(t): r’\d+’ t.value = int(t.value) return t
lex.lex() # Build the lexerBuilds the lexer
by creating a masterregular expression
![Page 32: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/32.jpg)
ply.lex exampleimport ply.lex as lextokens = [ ‘NAME’,’NUMBER’,’PLUS’,’MINUS’,’TIMES’, ’DIVIDE’, EQUALS’ ]t_ignore = ‘ \t’ t_PLUS = r’\+’t_MINUS = r’-’t_TIMES = r’\*’t_DIVIDE = r’/’t_EQUALS = r’=’t_NAME = r’[a-zA-Z_][a-zA-Z0-9_]*’
def t_NUMBER(t): r’\d+’ t.value = int(t.value) return t
lex.lex() # Build the lexer
Introspection usedto examine contents
of calling module.
![Page 33: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/33.jpg)
ply.lex exampleimport ply.lex as lextokens = [ ‘NAME’,’NUMBER’,’PLUS’,’MINUS’,’TIMES’, ’DIVIDE’, EQUALS’ ]t_ignore = ‘ \t’ t_PLUS = r’\+’t_MINUS = r’-’t_TIMES = r’\*’t_DIVIDE = r’/’t_EQUALS = r’=’t_NAME = r’[a-zA-Z_][a-zA-Z0-9_]*’
def t_NUMBER(t): r’\d+’ t.value = int(t.value) return t
lex.lex() # Build the lexer
Introspection usedto examine contents
of calling module.
__dict__ = { 'tokens' : [ 'NAME' ...], 't_ignore' : ' \t', 't_PLUS' : '\\+', ... 't_NUMBER' : <function ...}
![Page 34: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/34.jpg)
ply.lex use
...lex.lex() # Build the lexer...lex.input("x = 3 * 4 + 5 * 6")while True: tok = lex.token() if not tok: break
# Use token ...
• Two functions: input() and token()
![Page 35: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/35.jpg)
ply.lex use
...lex.lex() # Build the lexer...lex.input("x = 3 * 4 + 5 * 6")while True: tok = lex.token() if not tok: break
# Use token ...
• Two functions: input() and token()
input() feeds a stringinto the lexer
![Page 36: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/36.jpg)
ply.lex use
...lex.lex() # Build the lexer...lex.input("x = 3 * 4 + 5 * 6")while True: tok = lex.token() if not tok: break
# Use token ...
• Two functions: input() and token()
token() returns thenext token or None
![Page 37: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/37.jpg)
ply.lex use
...lex.lex() # Build the lexer...lex.input("x = 3 * 4 + 5 * 6")while True: tok = lex.token() if not tok: break
# Use token ...
• Two functions: input() and token()
tok.typetok.valuetok.linetok.lexpos
![Page 38: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/38.jpg)
ply.lex use
...lex.lex() # Build the lexer...lex.input("x = 3 * 4 + 5 * 6")while True: tok = lex.token() if not tok: break
# Use token ...
• Two functions: input() and token()
tok.typetok.valuetok.linetok.lexpos t_NAME = r’[a-zA-Z_][a-zA-Z0-9_]*’
![Page 39: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/39.jpg)
ply.lex use
...lex.lex() # Build the lexer...lex.input("x = 3 * 4 + 5 * 6")while True: tok = lex.token() if not tok: break
# Use token ...
• Two functions: input() and token()
tok.typetok.valuetok.linetok.lexpos t_NAME = r’[a-zA-Z_][a-zA-Z0-9_]*’
matching text
![Page 40: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/40.jpg)
ply.lex use
...lex.lex() # Build the lexer...lex.input("x = 3 * 4 + 5 * 6")while True: tok = lex.token() if not tok: break
# Use token ...
• Two functions: input() and token()
tok.typetok.valuetok.linetok.lexpos Position in input text
![Page 41: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/41.jpg)
ply.lex Commentary
• Normally you don't use the tokenizer directly
• Instead, it's used by the parser module
![Page 42: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/42.jpg)
ply.yacc preliminaries• ply.yacc is a module for creating a parser
• Assumes you have defined a BNF grammar
assign : NAME EQUALS exprexpr : expr PLUS term | expr MINUS term | termterm : term TIMES factor | term DIVIDE factor | factorfactor : NUMBER
![Page 43: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/43.jpg)
ply.yacc exampleimport ply.yacc as yaccimport mylexer # Import lexer informationtokens = mylexer.tokens # Need token list
def p_assign(p): '''assign : NAME EQUALS expr'''
def p_expr(p): '''expr : expr PLUS term | expr MINUS term | term'''def p_term(p): '''term : term TIMES factor | term DIVIDE factor | factor'''def p_factor(p): '''factor : NUMBER'''
yacc.yacc() # Build the parser
![Page 44: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/44.jpg)
ply.yacc exampleimport ply.yacc as yaccimport mylexer # Import lexer informationtokens = mylexer.tokens # Need token list
def p_assign(p): '''assign : NAME EQUALS expr'''
def p_expr(p): '''expr : expr PLUS term | expr MINUS term | term'''def p_term(p): '''term : term TIMES factor | term DIVIDE factor | factor'''def p_factor(p): '''factor : NUMBER'''
yacc.yacc() # Build the parser
token informationimported from lexer
![Page 45: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/45.jpg)
ply.yacc exampleimport ply.yacc as yaccimport mylexer # Import lexer informationtokens = mylexer.tokens # Need token list
def p_assign(p): '''assign : NAME EQUALS expr'''
def p_expr(p): '''expr : expr PLUS term | expr MINUS term | term'''def p_term(p): '''term : term TIMES factor | term DIVIDE factor | factor'''def p_factor(p): '''factor : NUMBER'''
yacc.yacc() # Build the parser
grammar rules encodedas functions with names
p_rulename
Note: Name doesn't matter as long as it
starts with p_
![Page 46: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/46.jpg)
ply.yacc exampleimport ply.yacc as yaccimport mylexer # Import lexer informationtokens = mylexer.tokens # Need token list
def p_assign(p): '''assign : NAME EQUALS expr'''
def p_expr(p): '''expr : expr PLUS term | expr MINUS term | term'''def p_term(p): '''term : term TIMES factor | term DIVIDE factor | factor'''def p_factor(p): '''factor : NUMBER'''
yacc.yacc() # Build the parser
docstrings containgrammar rules
from BNF
![Page 47: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/47.jpg)
ply.yacc exampleimport ply.yacc as yaccimport mylexer # Import lexer informationtokens = mylexer.tokens # Need token list
def p_assign(p): '''assign : NAME EQUALS expr'''
def p_expr(p): '''expr : expr PLUS term | expr MINUS term | term'''def p_term(p): '''term : term TIMES factor | term DIVIDE factor | factor'''def p_factor(p): '''factor : NUMBER'''
yacc.yacc() # Build the parserBuilds the parser
using introspection
![Page 48: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/48.jpg)
ply.yacc parsing
• yacc.parse() function
yacc.yacc() # Build the parser...data = "x = 3*4+5*6"yacc.parse(data) # Parse some text
• This feeds data into lexer
• Parses the text and invokes grammar rules
![Page 49: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/49.jpg)
A peek inside
• PLY uses LR-parsing. LALR(1)
• AKA: Shift-reduce parsing
• Widely used parsing technique
• Table driven
![Page 50: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/50.jpg)
General Idea
• Input tokens are shifted onto a parsing stack
X = 3 * 4 + 5= 3 * 4 + 5
3 * 4 + 5* 4 + 5
NAMENAME =NAME = NUM
Stack Input
• This continues until a complete grammar rule appears on the top of the stack
![Page 51: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/51.jpg)
General Idea
• If rules are found, a "reduction" occurs
X = 3 * 4 + 5= 3 * 4 + 5
3 * 4 + 5 * 4 + 5
NAME NAME =NAME = NUM
Stack Input
NAME = factor
reduce factor : NUM
• RHS of grammar rule replaced with LHS
![Page 52: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/52.jpg)
Rule Functions
• During reduction, rule functions are invokeddef p_factor(p): ‘factor : NUMBER’
• Parameter p contains grammar symbol valuesdef p_factor(p): ‘factor : NUMBER’
p[0] p[1]
![Page 53: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/53.jpg)
Using an LR Parser
• Rule functions generally process values on right hand side of grammar rule
• Result is then stored in left hand side
• Results propagate up through the grammar
• Bottom-up parsing
![Page 54: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/54.jpg)
def p_assign(p): ‘’’assign : NAME EQUALS expr’’’ vars[p[1]] = p[3]
def p_expr_plus(p): ‘’’expr : expr PLUS term’’’ p[0] = p[1] + p[3]
def p_term_mul(p): ‘’’term : term TIMES factor’’’ p[0] = p[1] * p[3]
def p_term_factor(p): '''term : factor''' p[0] = p[1]
def p_factor(p): ‘’’factor : NUMBER’’’ p[0] = p[1]
Example: Calculator
![Page 55: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/55.jpg)
def p_assign(p): ‘’’assign : NAME EQUALS expr’’’ p[0] = (‘ASSIGN’,p[1],p[3])
def p_expr_plus(p): ‘’’expr : expr PLUS term’’’ p[0] = (‘+’,p[1],p[3])
def p_term_mul(p): ‘’’term : term TIMES factor’’’ p[0] = (‘*’,p[1],p[3])
def p_term_factor(p): '''term : factor''' p[0] = p[1]
def p_factor(p): ‘’’factor : NUMBER’’’ p[0] = (‘NUM’,p[1])
Example: Parse Tree
![Page 56: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/56.jpg)
>>> t = yacc.parse("x = 3*4 + 5*6")>>> t('ASSIGN','x',('+', ('*',('NUM',3),('NUM',4)), ('*',('NUM',5),('NUM',6)) ))>>>
Example: Parse Tree
ASSIGN
'x' '+'
'*''*'
3 4 5 6
![Page 57: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/57.jpg)
Why use PLY?
• There are many Python parsing tools
• Some use more powerful parsing algorithms
• Isn't parsing a "solved" problem anyways?
![Page 58: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/58.jpg)
PLY is Informative
• Compiler writing is hard
• Tools should not make it even harder
• PLY provides extensive diagnostics
• Major emphasis on error reporting
• Provides the same information as yacc
![Page 59: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/59.jpg)
PLY Diagnostics• PLY produces the same diagnostics as yacc
• Yacc% yacc grammar.y4 shift/reduce conflicts2 reduce/reduce conflicts
• PLY% python mycompiler.pyyacc: Generating LALR parsing table...4 shift/reduce conflicts2 reduce/reduce conflicts
• PLY also produces the same debugging output
![Page 60: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/60.jpg)
Debugging OutputGrammar
Rule 1 statement -> NAME = expressionRule 2 statement -> expressionRule 3 expression -> expression + expressionRule 4 expression -> expression - expressionRule 5 expression -> expression * expressionRule 6 expression -> expression / expressionRule 7 expression -> NUMBER
Terminals, with rules where they appear
* : 5+ : 3- : 4/ : 6= : 1NAME : 1NUMBER : 7error :
Nonterminals, with rules where they appear
expression : 1 2 3 3 4 4 5 5 6 6statement : 0
Parsing method: LALR
state 0
(0) S' -> . statement (1) statement -> . NAME = expression (2) statement -> . expression (3) expression -> . expression + expression (4) expression -> . expression - expression (5) expression -> . expression * expression (6) expression -> . expression / expression (7) expression -> . NUMBER
NAME shift and go to state 1 NUMBER shift and go to state 2
expression shift and go to state 4 statement shift and go to state 3
state 1
(1) statement -> NAME . = expression
= shift and go to state 5
state 10
(1) statement -> NAME = expression . (3) expression -> expression . + expression (4) expression -> expression . - expression (5) expression -> expression . * expression (6) expression -> expression . / expression
$end reduce using rule 1 (statement -> NAME = expression .) + shift and go to state 7 - shift and go to state 6 * shift and go to state 8 / shift and go to state 9
state 11
(4) expression -> expression - expression . (3) expression -> expression . + expression (4) expression -> expression . - expression (5) expression -> expression . * expression (6) expression -> expression . / expression
! shift/reduce conflict for + resolved as shift. ! shift/reduce conflict for - resolved as shift. ! shift/reduce conflict for * resolved as shift. ! shift/reduce conflict for / resolved as shift. $end reduce using rule 4 (expression -> expression - expression .) + shift and go to state 7 - shift and go to state 6 * shift and go to state 8 / shift and go to state 9
! + [ reduce using rule 4 (expression -> expression - expression .) ] ! - [ reduce using rule 4 (expression -> expression - expression .) ] ! * [ reduce using rule 4 (expression -> expression - expression .) ] ! / [ reduce using rule 4 (expression -> expression - expression .) ]
![Page 61: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/61.jpg)
Debugging OutputGrammar
Rule 1 statement -> NAME = expressionRule 2 statement -> expressionRule 3 expression -> expression + expressionRule 4 expression -> expression - expressionRule 5 expression -> expression * expressionRule 6 expression -> expression / expressionRule 7 expression -> NUMBER
Terminals, with rules where they appear
* : 5+ : 3- : 4/ : 6= : 1NAME : 1NUMBER : 7error :
Nonterminals, with rules where they appear
expression : 1 2 3 3 4 4 5 5 6 6statement : 0
Parsing method: LALR
state 0
(0) S' -> . statement (1) statement -> . NAME = expression (2) statement -> . expression (3) expression -> . expression + expression (4) expression -> . expression - expression (5) expression -> . expression * expression (6) expression -> . expression / expression (7) expression -> . NUMBER
NAME shift and go to state 1 NUMBER shift and go to state 2
expression shift and go to state 4 statement shift and go to state 3
state 1
(1) statement -> NAME . = expression
= shift and go to state 5
state 10
(1) statement -> NAME = expression . (3) expression -> expression . + expression (4) expression -> expression . - expression (5) expression -> expression . * expression (6) expression -> expression . / expression
$end reduce using rule 1 (statement -> NAME = expression .) + shift and go to state 7 - shift and go to state 6 * shift and go to state 8 / shift and go to state 9
state 11
(4) expression -> expression - expression . (3) expression -> expression . + expression (4) expression -> expression . - expression (5) expression -> expression . * expression (6) expression -> expression . / expression
! shift/reduce conflict for + resolved as shift. ! shift/reduce conflict for - resolved as shift. ! shift/reduce conflict for * resolved as shift. ! shift/reduce conflict for / resolved as shift. $end reduce using rule 4 (expression -> expression - expression .) + shift and go to state 7 - shift and go to state 6 * shift and go to state 8 / shift and go to state 9
! + [ reduce using rule 4 (expression -> expression - expression .) ] ! - [ reduce using rule 4 (expression -> expression - expression .) ] ! * [ reduce using rule 4 (expression -> expression - expression .) ] ! / [ reduce using rule 4 (expression -> expression - expression .) ]
...state 11
(4) expression -> expression - expression . (3) expression -> expression . + expression (4) expression -> expression . - expression (5) expression -> expression . * expression (6) expression -> expression . / expression
! shift/reduce conflict for + resolved as shift. ! shift/reduce conflict for - resolved as shift. ! shift/reduce conflict for * resolved as shift. ! shift/reduce conflict for / resolved as shift. $end reduce using rule 4 (expression -> expression - expression .) + shift and go to state 7 - shift and go to state 6 * shift and go to state 8 / shift and go to state 9
! + [ reduce using rule 4 (expression -> expression - expression .) ] ! - [ reduce using rule 4 (expression -> expression - expression .) ] ! * [ reduce using rule 4 (expression -> expression - expression .) ] ! / [ reduce using rule 4 (expression -> expression - expression .) ]...
![Page 62: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/62.jpg)
PLY Validation• PLY validates all token/grammar specs
• Duplicate rules
• Malformed regexs and grammars
• Missing rules and tokens
• Unused tokens and rules
• Improper function declarations
• Infinite recursion
![Page 63: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/63.jpg)
Error Exampleimport ply.lex as lextokens = [ ‘NAME’,’NUMBER’,’PLUS’,’MINUS’,’TIMES’, ’DIVIDE’, EQUALS’ ]t_ignore = ‘ \t’ t_PLUS = r’\+’t_MINUS = r’-’t_TIMES = r’\*’t_DIVIDE = r’/’t_EQUALS = r’=’t_NAME = r’[a-zA-Z_][a-zA-Z0-9_]*’t_MINUS = r'-'t_POWER = r'\^'
def t_NUMBER(): r’\d+’ t.value = int(t.value) return t
lex.lex() # Build the lexer
example.py:12: Rule t_MINUS redefined. Previously defined on line 6
![Page 64: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/64.jpg)
Error Exampleimport ply.lex as lextokens = [ ‘NAME’,’NUMBER’,’PLUS’,’MINUS’,’TIMES’, ’DIVIDE’, EQUALS’ ]t_ignore = ‘ \t’ t_PLUS = r’\+’t_MINUS = r’-’t_TIMES = r’\*’t_DIVIDE = r’/’t_EQUALS = r’=’t_NAME = r’[a-zA-Z_][a-zA-Z0-9_]*’t_MINUS = r'-'t_POWER = r'\^'
def t_NUMBER(): r’\d+’ t.value = int(t.value) return t
lex.lex() # Build the lexer
lex: Rule 't_POWER' defined for an unspecified token POWER
![Page 65: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/65.jpg)
Error Exampleimport ply.lex as lextokens = [ ‘NAME’,’NUMBER’,’PLUS’,’MINUS’,’TIMES’, ’DIVIDE’, EQUALS’ ]t_ignore = ‘ \t’ t_PLUS = r’\+’t_MINUS = r’-’t_TIMES = r’\*’t_DIVIDE = r’/’t_EQUALS = r’=’t_NAME = r’[a-zA-Z_][a-zA-Z0-9_]*’t_MINUS = r'-'t_POWER = r'\^'
def t_NUMBER(): r’\d+’ t.value = int(t.value) return t
lex.lex() # Build the lexer
example.py:15: Rule 't_NUMBER' requires an argument.
![Page 66: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/66.jpg)
Commentary
• PLY was developed for classroom use
• Major emphasis on identifying and reporting potential problems
• Report errors rather that fail with exception
![Page 67: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/67.jpg)
PLY is Yacc• PLY supports all of the major features of
Unix lex/yacc
• Syntax error handling and synchronization
• Precedence specifiers
• Character literals
• Start conditions
• Inherited attributes
![Page 68: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/68.jpg)
Precedence Specifiers• Yacc
%left PLUS MINUS%left TIMES DIVIDE%nonassoc UMINUS...expr : MINUS expr %prec UMINUS { $$ = -$1;}
• PLYprecedence = ( ('left','PLUS','MINUS'), ('left','TIMES','DIVIDE'), ('nonassoc','UMINUS'),)def p_expr_uminus(p): 'expr : MINUS expr %prec UMINUS' p[0] = -p[1]
![Page 69: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/69.jpg)
Character Literals• Yacc
expr : expr '+' expr { $$ = $1 + $3; } | expr '-' expr { $$ = $1 - $3; } | expr '*' expr { $$ = $1 * $3; } | expr '/' expr { $$ = $1 / $3; } ;
• PLYdef p_expr(p): '''expr : expr '+' expr | expr '-' expr | expr '*' expr | expr '/' expr''' ...
![Page 70: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/70.jpg)
Error Productions
• Yaccfuncall_err : ID LPAREN error RPAREN { printf("Syntax error in arguments\n"); } ;
• PLYdef p_funcall_err(p): '''ID LPAREN error RPAREN''' print "Syntax error in arguments\n"
![Page 71: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/71.jpg)
Commentary
• Books and documentation on yacc/bison used to guide the development of PLY
• Tried to copy all of the major features
• Usage as similar to lex/yacc as reasonable
![Page 72: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/72.jpg)
PLY is Simple
• Two pure-Python modules. That's it.
• Not part of a "parser framework"
• Use doesn't involve exotic design patterns
• Doesn't rely upon C extension modules
• Doesn't rely on third party tools
![Page 73: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/73.jpg)
PLY is Fast
• For a parser written entirely in Python
• Underlying parser is table driven
• Parsing tables are saved and only regenerated if the grammar changes
• Considerable work went into optimization from the start (developed on 200Mhz PC)
![Page 74: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/74.jpg)
PLY Performance• Example: Generating the LALR tables
• Input: SWIG C++ grammar
• 459 grammar rules, 892 parser states
• 3.6 seconds (PLY-2.3, 2.66Ghz Intel Xeon)
• 0.026 seconds (bison/ANSI C)
• Fast enough not to be annoying
• Tables only generated once and reused
![Page 75: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/75.jpg)
PLY Performance• Parse file with 1000 random expressions
(805KB) and build an abstract syntax tree
• PLY-2.3 : 2.95 sec, 10.2 MB (Python)
• YAPPS2 : 6.57 sec, 32.5 MB (Python)
• PyParsing : 13.11 sec, 15.6 MB (Python)
• ANTLR : 53.16 sec, 94 MB (Python)
• SPARK : 235.88 sec, 347 MB (Python)
• System: MacPro 2.66Ghz Xeon, Python-2.5
![Page 76: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/76.jpg)
PLY Performance• Parse file with 1000 random expressions
(805KB) and build an abstract syntax tree
• PLY-2.3 : 2.95 sec, 10.2 MB (Python)
• DParser : 0.71 sec, 72 MB (Python/C)
• BisonGen : 0.25 sec, 13 MB (Python/C)
• Bison : 0.063 sec, 7.9 MB (C)
• System: MacPro 2.66Ghz Xeon, Python-2.5
• 12x slower than BisonGen (mostly C)
• 47x slower than pure C
![Page 77: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/77.jpg)
Perf. Breakdown• Parse file with 1000 random expressions
(805KB) and build an abstract syntax tree
• Total time : 2.95 sec
• Startup : 0.02 sec
• Lexing : 1.20 sec
• Parsing : 1.12 sec
• AST : 0.61 sec
• System: MacPro 2.66Ghz Xeon, Python-2.5
![Page 78: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/78.jpg)
Advanced PLY
• PLY has many advanced features
• Lexers/parsers can be defined as classes
• Support for multiple lexers and parsers
• Support for optimized mode (python -O)
![Page 79: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/79.jpg)
Class Exampleimport ply.yacc as yacc
class MyParser: def p_assign(self,p): ‘’’assign : NAME EQUALS expr’’’ def p_expr(self,p): ‘’’expr : expr PLUS term | expr MINUS term | term’’’ def p_term(self,p): ‘’’term : term TIMES factor | term DIVIDE factor | factor’’’ def p_factor(self,p): ‘’’factor : NUMBER’’’ def build(self): self.parser = yacc.yacc(object=self)
![Page 80: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/80.jpg)
Experience with PLY
• In 2001, I taught a compilers course
• Students wrote a full compiler
• Lexing, parsing, type checking, code generation
• Procedures, nested scopes, and type inference
• Produced working SPARC assembly code
![Page 81: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/81.jpg)
Classroom Results
• You can write a real compiler in Python
• Students were successful with projects
• However, many projects were quite "hacky"
• Still unsure about dynamic nature of Python
• May be too easy to create a "bad" compiler
![Page 82: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/82.jpg)
General PLY Experience
• May be very useful for prototyping
• PLY's strength is in its diagnostics
• Significantly faster than most Python parsers
• Not sure I'd rewrite gcc in Python just yet
• I'm still thinking about SWIG.
![Page 83: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/83.jpg)
Limitations
• LALR(1) parsing
• Not easy to work with very complex grammars (e.g., C++ parsing)
• Retains all of yacc's black magic
• Not as powerful as more general parsing algorithms (ANTLR, SPARK, etc.)
• Tradeoff : Speed vs. Generality
![Page 84: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/84.jpg)
PLY Usage
• Current version : Ply-2.3
• >100 downloads/week
• People are obviously using it
• Largest project I know of : Ada parser
• Many other small projects
![Page 85: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/85.jpg)
Future Directions
• PLY was written for Python-2.0
• Not yet updated to use modern Python features such as iterators and generators
• May update, but not at the expense of performance
• Working on some add-ons to ease transition between yacc <---> PLY.
![Page 86: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/86.jpg)
Acknowledgements• Many people have contributed to PLY
Thad Austin Shannon Behrens Michael Brown Russ Cox Johan Dahl Andrew Dalke Michael Dyck Joshua Gerth Elias Ioup Oldrich Jedlicka Sverre Jørgensen Lee June Andreas Jung Cem Karan
Adam Kerrison Daniel Larraz David McNab Patrick Mezard Pearu Peterson François Pinard Eric Raymond Adam Ring Rich Salz Markus Schoepflin Christoper Stawarz Miki Tebeka Andrew Waters
• Apologies to anyone I forgot
![Page 87: Writing Parsers and Compilers with PLY](https://reader035.fdocuments.net/reader035/viewer/2022062303/554f5b84b4c905524c8b5513/html5/thumbnails/87.jpg)
Resources
• PLY homepage
http://www.dabeaz.com/ply
• Mailing list/group
http://groups.google.com/group/ply-hack