Skip to content

Instantly share code, notes, and snippets.

@mystor
Created July 26, 2013 19:26
Show Gist options
  • Select an option

  • Save mystor/6091570 to your computer and use it in GitHub Desktop.

Select an option

Save mystor/6091570 to your computer and use it in GitHub Desktop.
Quick port of [this gist](https://gist.github.com/Mystor/6086481) to Python using PLY
################
# ENTERING LEX #
################
###
# Define Tokens
###
tokens = (
'COURSE',
'RECOMMEND',
'PREREQUISITE',
'COREQUISITE',
'EQUIVALENCY',
'EXCLUSION',
'OR',
'LPAREN',
'RPAREN',
)
t_RECOMMEND = r'(?i)recommend'
t_PREREQUISITE = r'(?i)prerequisite'
t_COREQUISITE = r'(?i)corequisite'
t_EQUIVALENCY = r'(?i)equivalen'
t_EXCLUSION = r'(?i)exclu'
t_OR = r'(?i)or'
t_LPAREN = r'\('
t_RPAREN = r'\)'
# t_COURSE = r'[a-zA-Z][a-zA-Z][a-zA-Z][a-zA-Z] [pP]?[0-9][0-9][0-9]'
t_COURSE = r'[a-zA-Z]{4}[ ]?[pP]?[0-9]{1,3}'
def t_error(t):
# Skip any invalid characters
t.lexer.skip(1)
# Build the lexer
import ply.lex as lex
lt = lex.lex()
#################
# ENTERING YACC #
#################
###
# Parsing Rules
###
precedence = (
('right', 'OR'),
)
###
# Full document
###
def p_full(t):
'full : reqsetlist'
t[0] = t[1]
# Print it out
print(t[0])
###
# REQSETLIST DEFINITIONS
###
def p_reqsetlist_empty(t):
'reqsetlist : '
t[0] = []
def p_reqsetlist_add(t):
'reqsetlist : reqsetlist reqset'
if len(t[2]['courses']['items']) is 0:
t[0] = t[1]
else:
t[0] = t[1] + [ t[2] ]
###
# REQSET DEFINITIONS
###
def p_reqset_recommend(t):
'reqset : RECOMMEND courselist'
t[0] = {
'type': 'recommend',
'courses': t[2]
}
def p_reqset_corequisite(t):
'reqset : COREQUISITE courselist'
t[0] = {
'type': 'corequisite',
'courses': t[2]
}
def p_reqset_prerequisite(t):
'reqset : PREREQUISITE courselist'
t[0] = {
'type': 'prerequisite',
'courses': t[2]
}
def p_reqset_equivalency(t):
'reqset : EQUIVALENCY courselist'
t[0] = {
'type': 'equivalency',
'courses': t[2]
}
def p_reqset_exclusion(t):
'reqset : EXCLUSION courselist'
t[0] = {
'type': 'exclusion',
'courses': t[2]
}
def p_reqset_bare(t):
'reqset : courselist'
# Default to prerequisites
t[0] = {
'type': 'prerequisite',
'courses': t[1]
}
###
# COURSELIST DEFINITIONS
###
def p_courselist_single(t):
'courselist : COURSE'
t[0] = {
'type': 'and',
'items': [t[1]]
}
def p_courselist_noop(t):
'courselist : courselist courselist'
t[0] = courselist_and(t[1], t[2])
def p_courselist_or(t):
'courselist : leftor courselist'
t[0] = courselist_or(t[1], t[2])
def p_courselist_floatingor(t):
'courselist : leftor'
t[0] = t[1]
def p_courselist_parens(t):
'courselist : LPAREN courselist RPAREN'
# This simply ensures that the entire courselist inside
# of the parens has already been evaluated
t[0] = t[2]
###
# OR
###
def p_leftor(t):
'leftor : courselist OR'
t[0] = t[1]
def p_error(p):
# Skipping bad tokens
yacc.errok()
# Build the Parser
import ply.yacc as yacc
yacc.yacc()
####################
# ENTERING HELPERS #
####################
def courselist_and(a, b):
if a['type'] is 'and' and b['type'] is 'and':
return {
'type': 'and',
'items': a['items'] + b['items']
}
elif a['type'] is 'and' and b['type'] is 'or':
return {
'type': 'and',
'items': a['items'] + [ b ]
}
elif a['type'] is 'or' and b['type'] is 'and':
return {
'type': 'and',
'items': [ a ] + b['items']
}
else:
return {
'type': 'and',
'items': [a, b]
}
def courselist_or(a, b):
if a['type'] is 'and' and b['type'] is 'and':
# Collapse single element ands
if len(a['items']) is 1:
a = a['items'][0]
if len(b['items']) is 1:
b = b['items'][0]
return {
'type': 'or',
'items': [a, b]
}
elif a['type'] is 'and' and b['type'] is 'or':
# Collapse single element ands
if len(a['items']) is 1:
a = a['items'][0]
return {
'type': 'or',
'items': [ a ] + b['items']
}
elif a['type'] is 'or' and b['type'] is 'and':
# Collapse single element ands
if len(b['items']) is 1:
b = b['items'][0]
return {
'type': 'or',
'items': a['items'] + [ b ]
}
else:
return {
'type': 'or',
'items': a['items'] + b['items']
}
##################
# ENTERING TESTS #
##################
if __name__ == '__main__':
strings = [
"Must be registered in a BSCE or UENG program. COURSE",
"APSC 131 OR CHEM 120",
"APSC 293 Cor req",
"Recommendation BIOL 201 and BIOL 202. Prerequisite A minimum grade of C- in BIOL 205.",
"Prerequisite BIOL 308. Prior to registering in the course, students must complete the application process, be placed in a module and complete the field work.",
"Prerequisites BIOL 201 and BIOL 202 and (a minimum grade of C- in BIOL 206) and (BIOL 243 or PSYC 202 or STAT 269)."
]
for string in strings:
print(string)
# lt.input(string)
# while True:
# tok = lt.token()
# if not tok: break
# print tok
yacc.parse(string)
# Horizontal Rule
print('*' * 80)
print('')
print('done')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment