pyparsing: при игнорировании комментариев parseAll=True не вызывает ParseException

Я заметил странный побочный эффект в pyparsing:

При использовании .ignore() в расширенном наборе синтаксического анализатора parseString(..., parseAll = True) прекращает проверку всей строки на символе комментария. Лучше объяснить код ниже.

Как это исправить без использования stringEnd?

пример:

def test():        
    import pyparsing as p
    unquoted_exclude = "\\\"" + "':/|<>,;#"   
    unquoted_chars = ''.join(set(p.printables) - set(unquoted_exclude))
    unquotedkey = p.Word(unquoted_chars)

    more = p.OneOrMore(unquotedkey)
    more.ignore("#" + p.restOfLine) 
    # ^^ "more" should ignore comments, but not "unquotedkey" !!

    def parse(parser, input_, parseAll=True):
        try: 
            print input_
            print parser.parseString(input_, parseAll).asList()
        except Exception as err:
            print err


    parse(unquotedkey, "abc#d")
    parse(unquotedkey, "abc|d")

    withstringend = unquotedkey + p.stringEnd 
    parse(withstringend, "abc#d", False)
    parse(withstringend, "abc|d", False)

Выход:

abc#d     
['abc'] <--- should throw an exception but does not  
abc|d
Expected end of text (at char 3), (line:1, col:4)
abc#d
Expected stringEnd (at char 3), (line:1, col:4)
abc|d
Expected stringEnd (at char 3), (line:1, col:4)

person john-cd    schedule 24.07.2014    source источник


Ответы (1)


Чтобы сравнить яблоки с яблоками, вы также должны добавить эту строку после определения withstringend:

withstringend.ignore('#' + p.restOfLine)

Я думаю, вы увидите, что он ведет себя так же, как и ваш тест парсинга с помощью unquotedKey.

Целью ignore является игнорирование конструкции в любом месте в проанализированном входном тексте, а не только на самом верхнем уровне. Например, в программе на C нельзя просто игнорировать комментарии между операторами:

/* add one to x */
x ++;

Вы также должны игнорировать комментарии, которые могут появиться где угодно:

x /* this is a post-increment 
so it really won't add 1 to x until after the
statement executes */ ++
/* and this is the trailing semicolon 
for the previous statement -> */;

Или, может быть, чуть менее надуманно:

for (x = ptr; /* start at ptr */
     *x; /* keep going as long as we point to non-zero */
     x++ /* add one to x */ )

Таким образом, чтобы поддержать это, ignore() реализован для рекурсии по всему определенному синтаксическому анализатору и обновлению списка игнорируемых выражений на каждом вспомогательном синтаксическом анализаторе в общем синтаксическом анализаторе, так что игнорируемые выражения пропускаются на каждом уровне общего синтаксического анализатора. В качестве альтернативы можно было бы разбросать вызовы ignore по всему вашему определению анализатора и постоянно пытаться отследить те, которые были случайно пропущены.

Итак, в вашем первом случае, когда вы сделали:

more = p.OneOrMore(unquotedKey)
more.ignore('#' + p.restOfline)

вы также обновили игнорируемые объекты для unquotedKey. Если вы хотите изолировать unquotedKey, чтобы он не получил этого побочного эффекта, определите more, используя:

more = p.OneOrMore(unquotedKey.copy())

Еще один момент - ваше определение ключа без кавычек, определяя ключ как «все в печатных формах, кроме этих специальных символов». Используемый вами метод был хорош до версии 1.5.6, когда в класс Word был добавлен аргумент excludeChars. Теперь вам не нужно возиться с созданием списка только разрешенных символов, вы можете заставить Word сделать всю работу. Пытаться:

unquotedKey = p.Word(p.printables,
                     excludeChars = r'\"' + "':/|<>,;#")
person PaulMcG    schedule 25.07.2014