Разбор dhcpd.conf с textX

Я использую 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 ответ

Решение

Автор 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).*/;
Другие вопросы по тегам