Как лучше всего проанализировать простую грамматику?

Хорошо, таким образом, я спросил набор меньших вопросов об этом проекте, но у меня все еще нет большой уверенности в проектах, я придумываю, таким образом, я собираюсь задать вопрос в более широком масштабе.

Я анализирую необходимые как условие описания для каталога курса. Описания почти всегда следуют за определенной формой, которая заставляет меня думать, что я могу проанализировать большинство из них.

Из текста я хотел бы генерировать график, конечно, необходимые как условие отношения. (Что часть будет легка, после того, как я проанализирую данные.)

Некоторые демонстрационные вводы и выводы:

"CS 2110" => ("CS", 2110) # 0

"CS 2110 and INFO 3300" => [("CS", 2110), ("INFO", 3300)] # 1
"CS 2110, INFO 3300" => [("CS", 2110), ("INFO", 3300)] # 1
"CS 2110, 3300, 3140" => [("CS", 2110), ("CS", 3300), ("CS", 3140)] # 1

"CS 2110 or INFO 3300" => [[("CS", 2110)], [("INFO", 3300)]] # 2

"MATH 2210, 2230, 2310, or 2940" => [[("MATH", 2210), ("MATH", 2230), ("MATH", 2310)], [("MATH", 2940)]] # 3  
  1. Если все описание является просто курсом, оно производится непосредственно.

  2. Если курсы соединены ("и"), они все производятся в том же списке

  3. Если курс разъединен ("или"), они находятся в отдельных списках

  4. Здесь, у нас есть оба "и" и "или".

Один протест, который помогает: кажется, что вложение "и" / "или" фразы никогда не больше, чем как показано в примере 3.

Что лучший способ состоит в том, чтобы сделать это? Я запустил со СГИБА, но я не мог выяснить, как разрешить уменьшать/уменьшать конфликты. Преимущество СГИБА состоит в том, что легко управлять тем, что генерирует каждое правило синтаксического анализа:

def p_course(p):
 'course : DEPT_CODE COURSE_NUMBER'
 p[0] = (p[1], int(p[2]))

С PyParse менее ясно, как изменить вывод parseString(). Я рассматривал здание @Alex идея Martelli сохранить состояние в объекте и создать вывод от этого, но я не уверен точно, как это лучше всего сделано.

 def addCourse(self, str, location, tokens):
  self.result.append((tokens[0][0], tokens[0][1]))

 def makeCourseList(self, str, location, tokens):

  dept = tokens[0][0]
  new_tokens = [(dept, tokens[0][1])]
  new_tokens.extend((dept, tok) for tok in tokens[1:])

  self.result.append(new_tokens)

Например, для обработки "или" случаи:

    def __init__(self):
            self.result = []
            # ...
  self.statement = (course_data + Optional(OR_CONJ + course_data)).setParseAction(self.disjunctionCourses)



 def disjunctionCourses(self, str, location, tokens):
  if len(tokens) == 1:
   return tokens

  print "disjunction tokens: %s" % tokens

Как делает disjunctionCourses() знать который меньшие фразы разъединить? Все, что это получает, является маркерами, но что было проанализировано, до сих пор хранится в result, таким образом, как функция может сказать который данные в result соответствует который элементы token? Я предполагаю, что мог перерыть маркеры, затем найти элемент result с теми же данными, но тем замысловатым чувством...

Кроме того, существует много описаний, которые включают misc текст, как:

"CS 2110 or permission of instructor"
"INFO 3140 or equivalent experience"
"PYSCH 2210 and sophomore standing"

Но не очень важно, что я анализирую тот текст.

Что лучший путь состоит в том, чтобы приблизиться к этой проблеме?

27
задан martineau 19 February 2018 в 22:12
поделиться

4 ответа

def parse(astr):
    astr=astr.replace(',','')
    astr=astr.replace('and','')    
    tokens=astr.split()
    dept=None
    number=None
    result=[]
    option=[]
    for tok in tokens:
        if tok=='or':
            result.append(option)
            option=[]
            continue
        if tok.isalpha():
            dept=tok
            number=None
        else:
            number=int(tok)
        if dept and number:
            option.append((dept,number))
    else:
        if option:
            result.append(option)
    return result

if __name__=='__main__':
    tests=[ ("CS 2110" , [[("CS", 2110)]]),
            ("CS 2110 and INFO 3300" , [[("CS", 2110), ("INFO", 3300)]]),
            ("CS 2110, INFO 3300" , [[("CS", 2110), ("INFO", 3300)]]),
            ("CS 2110, 3300, 3140", [[("CS", 2110), ("CS", 3300), ("CS", 3140)]]),
            ("CS 2110 or INFO 3300", [[("CS", 2110)], [("INFO", 3300)]]),
            ("MATH 2210, 2230, 2310, or 2940", [[("MATH", 2210), ("MATH", 2230), ("MATH", 2310)], [("MATH", 2940)]])]

    for test,answer in tests:
        result=parse(test)
        if result==answer:
            print('GOOD: {0} => {1}'.format(test,answer))
        else:
            print('ERROR: {0} => {1} != {2}'.format(test,result,answer))
            break

дает

GOOD: CS 2110 => [[('CS', 2110)]]
GOOD: CS 2110 and INFO 3300 => [[('CS', 2110), ('INFO', 3300)]]
GOOD: CS 2110, INFO 3300 => [[('CS', 2110), ('INFO', 3300)]]
GOOD: CS 2110, 3300, 3140 => [[('CS', 2110), ('CS', 3300), ('CS', 3140)]]
GOOD: CS 2110 or INFO 3300 => [[('CS', 2110)], [('INFO', 3300)]]
GOOD: MATH 2210, 2230, 2310, or 2940 => [[('MATH', 2210), ('MATH', 2230), ('MATH', 2310)], [('MATH', 2940)]]
24
ответ дан 28 November 2019 в 05:38
поделиться

Для простых грамматик мне очень нравятся грамматики выражений (Parsing Expression Grammar, PEG), которые представляют собой дисциплинированный, структурированный способ написания парсера с рекурсивным спуском. В динамически типизированном языке, таком как Python, можно делать полезные вещи, не имея отдельного "генератора парсера". Это означает отсутствие глупостей с конфликтами reduce-reduce или другими арканами парсинга LR.

Я немного поискал, и pyPEG оказался хорошей библиотекой для Python.

7
ответ дан 28 November 2019 в 05:38
поделиться

Если вы получаете конфликты уменьшения / уменьшения, вам необходимо указать приоритет «или» и «и». Я предполагаю, что «и» связывает крепче всего, что означает «CS 101 и CS 102 или CS 201» означает [[CS 101, CS 102] [CS 201]].

Если вы можете найти примеры того и другого, то грамматика неоднозначна и вам не повезло. Однако вы можете позволить этой двусмысленности не уточнять, все зависит от того, что вы собираетесь делать с результатами.

PS, Похоже, язык обычный, можно было бы рассмотреть DFA.

0
ответ дан 28 November 2019 в 05:38
поделиться

Я не претендую на то, что много знаю о разборе грамматики, и для вашего случая решение unutbu - это все, что вам нужно. Но я узнал достаточно много о синтаксическом разборе от Эрика Липперта в его недавней серии записей в блоге.

http://blogs.msdn.com/b/ericlippert/archive/2010/04/26/every-program-there-is-part-one.aspx

Это серия из 7 частей, в которой рассматривается создание и разбор грамматики, а затем оптимизация грамматики для облегчения и повышения производительности разбора. Он создает код на C# для генерации всех комбинаций определенных грамматик, но не должно быть слишком сложным преобразовать его в python для разбора достаточно простой грамматики.

1
ответ дан 28 November 2019 в 05:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: