Получить позицию токена в строке с помощью pyparsing, используя именованные значения - PullRequest
0 голосов
/ 07 ноября 2018

Я пытаюсь получить позицию токена в строке при использовании pyparsing. Я хочу сообщить о местонахождении проблемы включения защиты в файлах C:

import pyparsing as pp

m = None
n = None

#a sample C header file
lines = "\
#ifndef HEADER_FILE_H\n\
#define HEADER_FILE_H 1\n\
\n\
\n\
/* code is here */\n\
\n\
#endif /* HEADER_FILE_H */\
"

LBRACE,RBRACE,LBRACK,RBRACK,LT,GT,LPAREN,RPAREN,DQ,SEMI = map(pp.Suppress,'{}[]<>()";')
CIDENT = pp.Word(pp.alphanums + "_")  #any C identifier
LCOMMENT = pp.Suppress("/*")
RCOMMENT = pp.Suppress("*/")

last_line = lines.split("\n")[-1]  #get last line

pound = pp.Literal("#") + pp.Suppress(pp.Optional(pp.White(" \t")))
ifndef = pound + pp.Literal("ifndef")
ifnotdefined = pound + pp.Literal("if") + pp.Literal("!defined")
define = pound + pp.Literal("define")
endif = pound + pp.Literal("endif")
comment = pp.Optional(LCOMMENT + CIDENT("guardname_endif") + RCOMMENT)("guard_end_comment")

includeguardifndef = pp.Or([ifndef, ifnotdefined]) + pp.Optional(LPAREN) + CIDENT("guardname_ifndef_val") + pp.Optional(RPAREN)
includeguard = define + CIDENT("guardname_define_val") + pp.Optional(pp.Literal("1")("guard_is_one"))
includeguard_top = includeguardifndef + includeguard
includeguardendif = endif("includeguardendif") + comment

try:
   m = includeguard_top.parseString(lines)
except pp.ParseException:
   pass

try:
   n = includeguardendif.parseString(last_line)
except pp.ParseException:
   pass

print(m)
print(n)

Теперь, когда я получаю свое совпадение "m", я могу получить m.guardname_define_value, и в конечном итоге я хочу получить что-то вроде m.guardname_define_value.pos, которое является позицией совпадения в "lines".

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

Я не новичок в регулярных выражениях, но я плохо знаком с pyparsing и довольно удивлен тем, насколько он силен и понятен. Действительно наслаждаюсь этим. Если есть предложения по поводу того, что я сделал выше, я тоже это приму.

  1. Главный вопрос - получить местоположение токена с указанным результатом, не используя магические числа. например: я не хочу делать что-то загадочное, например m.guardname_define_value[0][0], чтобы получить позицию
  2. Нужно ли пытаться / ловить pp.ParseException, как я делаю выше? Если я этого не сделаю, я получу исключение. Мне все равно, если матч провалится, я все равно проверяю результат на None.
  3. Думая, что nestedExpr может делать то, что я хочу, но на один шаг лучше, где я мог бы автоматически найти #ifdef ... #endif, магически совпадающий? (может быть, если opener - «#ifdef», а ближе - «#endif»?)
  4. Как правильно говорить «искать что-нибудь», не будучи слишком жадным? то есть это регулярное выражение: ".*(FOO).*" будет потреблять и отбрасывать что-либо, пока не найдет и не захватит FOO, а затем поглотить и отбросить что-нибудь после этого, мне будет трудно воспроизвести это.

Спасибо.

1 Ответ

0 голосов
/ 05 февраля 2019

Вот ваш пример кода с некоторыми небольшими изменениями. (Мне действительно не нравится обратная косая черта, и вам может показаться, что при встраивании текстовых примеров в ваши сценарии работа с тройными кавычками проще для глаз.) В частности, я показываю использование locatedExpr. Это может быть то, что вы действительно хотели увидеть. Также см. Этот вопрос SO: Pyparsing: получить местоположение токена в имени результата

import pyparsing as pp

#a sample C header file
lines = """
#ifndef HEADER_FILE_H
#define HEADER_FILE_H 1


/* code is here */

#endif /* HEADER_FILE_H */
"""

LBRACE,RBRACE,LBRACK,RBRACK,LT,GT,LPAREN,RPAREN,DQ,SEMI = map(pp.Suppress,'{}[]<>()";')
CIDENT = pp.Word(pp.alphas + "_", pp.alphanums + "_")  #any C identifier
LCOMMENT = pp.Suppress("/*")
RCOMMENT = pp.Suppress("*/")


def make_directive(s, pound=pp.Literal("#")):
    return pp.Combine(pound + s, adjacent=False)

ifndef = make_directive("ifndef")
ifnotdefined = make_directive("if") + pp.Literal("!defined")
define = make_directive("define")
endif = make_directive("endif")

comment = pp.Optional(LCOMMENT
                      + CIDENT("guardname_endif") 
                      + RCOMMENT)("guard_end_comment")


includeguardifndef = ((ifndef | ifnotdefined) 
                      + pp.Optional(LPAREN) 
                      + CIDENT("guardname_ifndef_val") 
                      + pp.Optional(RPAREN))
includeguard = (define 
                + CIDENT("guardname_define_val") 
                + pp.Optional(pp.Literal("1")("guard_is_one")))
includeguard_top = includeguardifndef + includeguard
includeguardendif = endif("includeguardendif") + comment

# parse the header
parser = includeguard_top + pp.SkipTo(includeguardendif).suppress() + includeguardendif
print(parser.parseString(lines).dump())

# parse the header, with locns
loc = pp.locatedExpr
parser = loc(includeguard_top) + pp.SkipTo(includeguardendif).suppress() + loc(includeguardendif)
print(parser.parseString(lines).dump())

Печать:

['#ifndef', 'HEADER_FILE_H', '#define', 'HEADER_FILE_H', '1', '#endif', 'HEADER_FILE_H']
- guard_end_comment: ['HEADER_FILE_H']
- guard_is_one: '1'
- guardname_define_val: 'HEADER_FILE_H'
- guardname_endif: 'HEADER_FILE_H'
- guardname_ifndef_val: 'HEADER_FILE_H'
- includeguardendif: '#endif'

[[1, '#ifndef', 'HEADER_FILE_H', '#define', 'HEADER_FILE_H', '1', 46], [69, '#endif', 'HEADER_FILE_H', 95]]
[0]:
  [1, '#ifndef', 'HEADER_FILE_H', '#define', 'HEADER_FILE_H', '1', 46]
  - guard_is_one: '1'
  - guardname_define_val: 'HEADER_FILE_H'
  - guardname_ifndef_val: 'HEADER_FILE_H'
  - locn_end: 46
  - locn_start: 1
  - value: ['#ifndef', 'HEADER_FILE_H', '#define', 'HEADER_FILE_H', '1']
[1]:
  [69, '#endif', 'HEADER_FILE_H', 95]
  - guard_end_comment: ['HEADER_FILE_H']
  - guardname_endif: 'HEADER_FILE_H'
  - includeguardendif: '#endif'
  - locn_end: 95
  - locn_start: 69
  - value: ['#endif', 'HEADER_FILE_H']
...