Разбор dhcpd.conf с textX - PullRequest
       58

Разбор dhcpd.conf с textX

0 голосов
/ 19 ноября 2018

Я использую https://github.com/igordejanovic/textX для разбора файла dhcpd.conf (нет, https://pypi.org/project/iscconf/ у меня не работает, он падает на моем файле dhcpd.conf), специально для извлечения хостов с фиксированными адресами .

Записи похожи на:

    host example1 {
    option host-name "example1";
    ddns-hostname "example1";
    fixed-address 192.168.1.181;
    }

    host example2 {
    hardware ethernet aa:bb:ff:20:fa:13;
    fixed-address 192.168.1.191;
    option host-name "example2";
    ddns-hostname "example2";
    }

Код:

def get_hosts(s):
    grammar = """
    config: hosts*=host ;

    host: 'host' hostname=ID '{'
        (
            ('hardware ethernet' hardware_ethernet=/[0-9a-fA-F:]+/';')?

            'fixed-address' fixed_address=/([0-9]{1,3}\.){3}[0-9]{1,3}/';'

            ('option host-name' option_host_name=STRING';')?

            ('ddns-hostname' ddns_hostname=STRING';')?
        )#
    '}'
    ;
    """
    mm = metamodel_from_str(grammar)
    model = mm.model_from_str(s)
    for host in model.hosts:
        print host.hostname, host.fixed_address

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

Конечно, я могу извлекать только записи хоста с помощью регулярных выражений и анализировать их отдельно, но мне интересно, есть ли какой-нибудь способ заставить textX извлекать только host записей из файла и игнорировать остальную часть содержимого?

1 Ответ

0 голосов
/ 19 ноября 2018

Автор textX здесь. Я не частый посетитель ТАК :). Вы можете поиграть с регулярными выражениями и заглядывать в регулярные выражения, чтобы использовать нежелательный контент. Вот полный пример, который правильно обрабатывает промежуточный текст, даже если есть ключевое слово host. Правило config сначала использует символ, если впереди нет слова host, и это повторяется из-за нуля или более оператора. Когда мы получаем слово host, мы пытаемся выполнить правило host правило один или несколько раз и собрать все объекты хоста, если правило не выполнено хотя бы один (обратите внимание на использование +=), мы используем слово host и повторите процесс. Это, вероятно, может быть сделано лучше (более производительным), но вы поняли идею. При выполнении такого рода вещей полезно знать, что textX по умолчанию использует пробелы, но вы можете отключить это либо глобально, либо по правилу, используя noskipws (см. документы ).

from textx import metamodel_from_str


def test_get_hosts():
    grammar = r"""
    config: ( /(?!host)./ | hosts+=host | 'host' )* ;

    host: 'host' hostname=ID '{'
        (
            ('hardware ethernet' hardware_ethernet=/[0-9a-fA-F:]+/';')?
            'fixed-address' fixed_address=/([0-9]{1,3}\.){3}[0-9]{1,3}/';'
            ('option host-name' option_host_name=STRING';')?
            ('ddns-hostname' ddns_hostname=STRING';')?
        )#
    '}'
    ;
    """
    conf_file = r"""
    host example1 {
    option host-name "example1";
    ddns-hostname "example1";
    fixed-address 192.168.1.181;
    }

    some arbitrary content in between
    with word host but that fails to match host config.

    host example2 {
    hardware ethernet aa:bb:ff:20:fa:13;
    fixed-address 192.168.1.191;
    option host-name "example2";
    ddns-hostname "example2";
    }
    """
    mm = metamodel_from_str(grammar)
    model = mm.model_from_str(conf_file)
    assert len(model.hosts) == 2
    for host in model.hosts:
        print(host.hostname, host.fixed_address)


if __name__ == "__main__":
    test_get_hosts()

Редактировать : Вот еще две идеи для правила config: Простой:

config: ( hosts+=host | /./ )* ;

И (возможно) более производительный, который потребляет столько, сколько может, используя движок регулярных выражений, прежде чем пытаться host:

config: ( /(?s:.*?(?=host))/ hosts*=host | 'host' )*
        /(?s).*/;
...