2016-09-22 2 views
2

Я пытаюсь использовать pyparsing для анализа ключей: пары значений из комментариев в документе. Клавиша начинается с начала строки, и после нее следует значение. Значения могут быть продолжены на нескольких строках, начинающихся с пробелов.Как использовать pyparsing LineStart?

import pyparsing as pp 

instring = """ 
-- This is (a) #%^& comment 

/* 
name1: val 
name2: val2 with $*&#@) junk 
name3: val3: with @)(*% multi- 
     line: content 
*/ 
""" 

comment1 = pp.Literal("--") + pp.originalTextFor(pp.SkipTo(pp.LineEnd())).setDebug() 
identifier = pp.Word(pp.alphanums + "_").setDebug() 
meta1 = pp.LineStart() + identifier + pp.Literal(":") + pp.SkipTo(pp.LineEnd()) 
meta2 = pp.LineStart() + pp.White() + pp.SkipTo(pp.LineEnd()) 
metaval = meta1 + pp.ZeroOrMore(meta2) 
metalist = pp.ZeroOrMore(comment1) + pp.Literal("/*") + pp.OneOrMore(metaval) + pp.Literal("*/") 

if __name__ == "__main__": 
    p = metalist.parseString(instring) 
    print(p) 

завершается с:

Matched {Empty SkipTo:(LineEnd) Empty} -> ['This is (a) #%^& comment'] 

File "C:\Users\user\py3\lib\site-packages\pyparsing.py", line 2305, in parseImpl 
raise ParseException(instring, loc, self.errmsg, self) 
pyparsing.ParseException: Expected start of line (at char 32), (line:4, col:1) 

Ответ на pyparsing whitespace match issues говорит

LineStart has always been difficult to work with, but ... 

Если анализатор находится в строке 4 столбца 1 (первый ключ: значение пары), то почему он не находит начало линии? Каков правильный синтаксис пипарации для распознавания строк, начинающихся без пробелов, и строк, начинающихся с пробелов?

ответ

2

Я думаю, что путаница у меня с LineStart является то, что для LineEnd, я могу искать '\n' характер, но нет никакого отдельного символа для LineStart. Итак, в LineStart Я смотрю, находится ли текущее местоположение синтаксического анализа сразу после '\n'; или в настоящее время на a '\n', пройдите мимо него и продолжайте движение. К сожалению, я реализовал это в месте, где запутывается местоположение отчетности, поэтому вы получаете те странные ошибки, которые читаются как «не удалось найти начало строки в строке X col 1», что действительно звучит так, будто должно быть успешно сопоставлено начало строки. Кроме того, я думаю, мне нужно пересмотреть этот неявный перевод строки, или, если на то пошло, все пропущенные пробелы вообще для LineStart.

В настоящем время, я получил свой код, чтобы работать, слегка расширяя свою линию, начиная с выражением, как:

LS = pp.Optional(pp.LineEnd()) + pp.LineStart() 

и заменить ссылки LineStart в meta1 и meta2 с LS:

comment1 = pp.Literal("--") + pp.originalTextFor(pp.SkipTo(pp.LineEnd())).setDebug() 
identifier = pp.Word(pp.alphanums + "_").setDebug() 
meta1 = LS + identifier + pp.Literal(":") + pp.SkipTo(pp.LineEnd()) 
meta2 = LS + pp.White() + pp.SkipTo(pp.LineEnd()) 
metaval = meta1 + pp.ZeroOrMore(meta2) 
metalist = pp.ZeroOrMore(comment1) + pp.Literal("/*") + pp.OneOrMore(metaval) + pp.Literal("*/") 

Если эта ситуация с LineStart оставляет вам неудобство, вот еще одна тактика, которую вы можете попробовать: используя условие синтаксического анализа, чтобы принимать только идентификаторы, начинающиеся в столбце 1:

comment1 = pp.Literal("--") + pp.originalTextFor(pp.SkipTo(pp.LineEnd())).setDebug() 

identifier = pp.Word(pp.alphanums + "_").setName("identifier") 
identifier.addCondition(lambda instring,loc,toks: pp.col(loc,instring) == 1) 

meta1 = identifier + pp.Literal(":") + pp.SkipTo(pp.LineEnd()).setDebug() 
meta2 = pp.White().setDebug() + pp.SkipTo(pp.LineEnd()).setDebug() 
metaval = meta1 + pp.ZeroOrMore(meta2, stopOn=pp.Literal('*/')) 
metalist = pp.ZeroOrMore(comment1) + pp.Literal("/*") + pp.LineEnd() + pp.OneOrMore(metaval) + pp.Literal("*/") 

Этот код полностью удаляет LineStart, в то время как я выясняю, что именно я хочу использовать этот конкретный токен. Мне также пришлось изменить повторение ZeroOrMore в metaval, так что */ не будет случайно обработан как продолжение содержимого комментария.

Благодарим за ваше терпение. Я не хочу быстро поменять исправленную замену LineStart, а затем обнаружил, что я упустил другую совместимость или другие кромки, которые просто вернули меня в текущее состояние, отличное от того, на этом классе. Но я приложу некоторые усилия для разъяснения этого поведения, прежде чем выпустить 2.1.10.

+0

Спасибо - состояние pp.col делает трюк. Но, как показано в ответе ниже, аргумент stopOn работает неправильно - он работает с pp.Literal, но не с pp.Word. Что-то, что нужно учитывать при работе над следующим выпуском. – Dave

+0

Улучшенная версия LineStart была выпущена только в 2.1.10. – PaulMcG

+0

Это работает для меня: seq = OneOrMore (Word (nums), stopOn = Word («0»)); print (seq.parseString ("349875 2330 204 123 000")) 'дает' ['349875', '2330', '204', '123'] '. – PaulMcG

Смежные вопросы