PyParsing: не все токены переданы в setParseAction () - PullRequest
1 голос
/ 31 мая 2010

Я разбираю предложения типа "CS 2110 или INFO 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()

Ответы [ 2 ]

2 голосов
/ 31 мая 2010

Чтобы сохранить биты токенов от «CS 2110» и «INFO 3300», я предлагаю вам обернуть ваше определение курса в группе:

course = Group(DEPT_CODE + COURSE_NUMBER).setResultsName("Course")

Выглядит так, как будто вы заряжаете голову на разбор некоего поискового выражения, такого как "x и y или z". В этой проблеме есть некоторая тонкость, и я предлагаю вам ознакомиться с некоторыми примерами в викинге pyparsing о том, как создавать выражения такого типа. В противном случае вы получите птичье гнездо 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, чтобы это работало.)

2 голосов
/ 31 мая 2010

Работает лучше, если вы установили действие разбора на и 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)]})
...