Pyparding: не все токены, переданные на SetParseaction ()
Вопрос
Я разбираю предложения, такие как «CS 2110 или информация 3300». Я хотел бы вывести формат, как:
[[("CS" 2110)], [("INFO", 3300)]]
Сделать это, я думал, что могу использовать setParseAction()
. Отказ Однако print
заявления в statementParse()
предположить, что только последние токены фактически переданы:
>>> statement.parseString("CS 2110 or INFO 3300")
Match [{Suppress:("or") Re:('[A-Z]{2,}') Re:('[0-9]{4}')}] at loc 7(1,8)
string CS 2110 or INFO 3300
loc: 7
tokens: ['INFO', 3300]
Matched [{Suppress:("or") Re:('[A-Z]{2,}') Re:('[0-9]{4}')}] -> ['INFO', 3300]
(['CS', 2110, 'INFO', 3300], {'Course': [(2110, 1), (3300, 3)], 'DeptCode': [('CS', 0), ('INFO', 2)]})
Я ожидал, что все жетоны будут переданы, но это только ['INFO', 3300]
. Отказ Я делаю что-то неправильно? Или есть еще один способ, которым я могу произвести желаемый выход?
Вот код Pyparsing:
from pyparsing import *
def statementParse(str, location, tokens):
print "string %s" % str
print "loc: %s " % location
print "tokens: %s" % tokens
DEPT_CODE = Regex(r'[A-Z]{2,}').setResultsName("DeptCode")
COURSE_NUMBER = Regex(r'[0-9]{4}').setResultsName("CourseNumber")
OR_CONJ = Suppress("or")
COURSE_NUMBER.setParseAction(lambda s, l, toks : int(toks[0]))
course = DEPT_CODE + COURSE_NUMBER.setResultsName("Course")
statement = course + Optional(OR_CONJ + course).setParseAction(statementParse).setDebug()
Решение
Чтобы держать биты токена от «CS 2110» и «info 3300», я предлагаю вам обернуть определение конечно в группе:
course = Group(DEPT_CODE + COURSE_NUMBER).setResultsName("Course")
Он также выглядит так, будто вы заряжаете голову при распределении некоторого вида поискового выражения, например, «X и Y или Z». Существует некоторая тонкость к этой проблеме, и я предлагаю вам проверить некоторые из примеров на Pyparding Wiki о том, как создать эти виды выражений. В противном случае вы окажетесь с птичьим гнездом Optional("or" + this)
а также ZeroOrMore(
"and" + that)
шт. Как последнее канавы, вы даже можете просто использовать что-то с operatorPrecedence
, подобно:
DEPT_CODE = Regex(r'[A-Z]{2,}').setResultsName("DeptCode")
COURSE_NUMBER = Regex(r'[0-9]{4}').setResultsName("CourseNumber")
course = Group(DEPT_CODE + COURSE_NUMBER)
courseSearch = operatorPrecedence(course,
[
("not", 1, opAssoc.RIGHT),
("and", 2, opAssoc.LEFT),
("or", 2, opAssoc.LEFT),
])
(Возможно, вам придется загрузить последнюю версию 1.5.3 от SourceForge SVN для этого на работу.)
Другие советы
Работает лучше, если вы устанавливаете действие разбора на обе course
и то Optional
(Вы настроили только на Optional
!):
>>> statement = (course + Optional(OR_CONJ + course)).setParseAction(statementParse).setDebug()
>>> statement.parseString("CS 2110 or INFO 3300")
дает
Match {Re:('[A-Z]{2,}') Re:('[0-9]{4}') [{Suppress:("or") Re:('[A-Z]{2,}') Re:('[0-9]{4}')}]} at loc 0(1,1)
string CS 2110 or INFO 3300
loc: 0
tokens: ['CS', 2110, 'INFO', 3300]
Matched {Re:('[A-Z]{2,}') Re:('[0-9]{4}') [{Suppress:("or") Re:('[A-Z]{2,}') Re:('[0-9]{4}')}]} -> ['CS', 2110, 'INFO', 3300]
(['CS', 2110, 'INFO', 3300], {'Course': [(2110, 1), (3300, 3)], 'DeptCode': [('CS', 0), ('INFO', 2)]})
Хотя я подозреваю, что вы на самом деле хотите, это установить действие разбора на каждом курс, не на утверждение:
>>> statement = course + Optional(OR_CONJ + course)
>>> statement.parseString("CS 2110 or INFO 3300") Match {Re:('[A-Z]{2,}') Re:('[0-9]{4}')} at loc 0(1,1)
string CS 2110 or INFO 3300
loc: 0
tokens: ['CS', 2110]
Matched {Re:('[A-Z]{2,}') Re:('[0-9]{4}')} -> ['CS', 2110]
Match {Re:('[A-Z]{2,}') Re:('[0-9]{4}')} at loc 10(1,11)
string CS 2110 or INFO 3300
loc: 10
tokens: ['INFO', 3300]
Matched {Re:('[A-Z]{2,}') Re:('[0-9]{4}')} -> ['INFO', 3300]
(['CS', 2110, 'INFO', 3300], {'Course': [(2110, 1), (3300, 3)], 'DeptCode': [('CS', 0), ('INFO', 2)]})