Как разобрать код (в Python)? - PullRequest
6 голосов
/ 07 марта 2011

Мне нужно проанализировать некоторые специальные структуры данных. Они в каком-то формате, похожем на C, который выглядит примерно так:

Group("GroupName") {
    /* C-Style comment */
    Group("AnotherGroupName") {
        Entry("some","variables",0,3.141);
        Entry("other","variables",1,2.718);
    }
    Entry("linebreaks",
          "allowed",
          3,
          1.414
         );
}

Я могу придумать несколько способов сделать это. Я мог бы «токенизировать» код, используя регулярные выражения. Я мог читать код по одному символу за раз и использовать конечный автомат для построения моей структуры данных. Я мог бы избавиться от запятых и читать строку за строкой. Я мог бы написать сценарий преобразования, который преобразует этот код в исполняемый код Python.

Есть ли хороший питонный способ для разбора таких файлов?
Как бы вы пошли на его разбор?

Это более общий вопрос о том, как анализировать строки, а не об этом конкретном формате файла.

Ответы [ 3 ]

7 голосов
/ 07 марта 2011

Используя pyparsing (Марк Толонен, я только что собирался нажать «Отправить сообщение», когда ваше сообщение появилось), это довольно просто - см. Комментарии, встроенные в код ниже:

data = """Group("GroupName") { 
    /* C-Style comment */ 
    Group("AnotherGroupName") { 
        Entry("some","variables",0,3.141); 
        Entry("other","variables",1,2.718); 
    } 
    Entry("linebreaks", 
          "allowed", 
          3, 
          1.414 
         ); 
} """

from pyparsing import *

# define basic punctuation and data types
LBRACE,RBRACE,LPAREN,RPAREN,SEMI = map(Suppress,"{}();")
GROUP = Keyword("Group")
ENTRY = Keyword("Entry")

# use parse actions to do parse-time conversion of values
real = Regex(r"[+-]?\d+\.\d*").setParseAction(lambda t:float(t[0]))
integer = Regex(r"[+-]?\d+").setParseAction(lambda t:int(t[0]))

# parses a string enclosed in quotes, but strips off the quotes at parse time
string = QuotedString('"')

# define structure expressions
value = string | real | integer
entry = Group(ENTRY + LPAREN + Group(Optional(delimitedList(value)))) + RPAREN + SEMI

# since Groups can contain Groups, need to use a Forward to define recursive expression
group = Forward()
group << Group(GROUP + LPAREN + string("name") + RPAREN + 
            LBRACE + Group(ZeroOrMore(group | entry))("body") + RBRACE)

# ignore C style comments wherever they occur
group.ignore(cStyleComment)

# parse the sample text
result = group.parseString(data)

# print out the tokens as a nice indented list using pprint
from pprint import pprint
pprint(result.asList())

Печать

[['Group',
  'GroupName',
  [['Group',
    'AnotherGroupName',
    [['Entry', ['some', 'variables', 0, 3.141]],
     ['Entry', ['other', 'variables', 1, 2.718]]]],
   ['Entry', ['linebreaks', 'allowed', 3, 1.4139999999999999]]]]]

(К сожалению, может возникнуть некоторая путаница, поскольку pyparsing определяет класс «Group» для передачи структуры разбираемым токенам - обратите внимание, как списки значений в Entry группируются, поскольку выражение списка заключено вПипарсинг группа.)

2 голосов
/ 07 марта 2011

Проверить pyparsing . Он имеет множество примеров разбора .

1 голос
/ 07 марта 2011

Зависит от того, как часто вам это нужно и остается ли синтаксис неизменным. Если ответы «довольно часто» и «более или менее да», то я бы искал способ выразить синтаксис и написать специальный синтаксический анализатор для этого языка с помощью инструмента, подобного PyPEG или LEPL . Определение правил синтаксического анализа - большая работа, поэтому, если вам не нужно часто анализировать файлы одного и того же типа, это не всегда может быть эффективным.

Но если вы посмотрите на страницу PyPEG, она скажет вам, как вывести проанализированные данные в XML, поэтому, если этот инструмент не дает вам достаточной мощности, вы можете использовать его для генерации XML, а затем использовать, например, например. lxml для анализа xml.

...