Рубрики
Без рубрики

Chlex – анализ синтаксисов в стиле оболочки

Автор оригинала: Doug Hellmann.

Цель:

Лексический анализ синтаксисов в стиле оболочки.

Модуль CHLEX реализует класс для анализа простых синтаксисов, подобных оболочкам. Его можно использовать для написания удельного домена языка или для анализа цитируемых строк (задача, которая является более сложной, чем кажется на поверхности).

Разборные цитируемые строки

Общая проблема при работе с входным текстом – идентифицировать последовательность котируемых слов как единого объекта. Разделение текста на цитатах не всегда работает должным образом, особенно если есть вложенные уровни цитат. Возьмите следующий текст в качестве примера.

This string has embedded "double quotes" and
'single quotes' in it, and even "a 'nested example'".

Наивный подход будет построить регулярное выражение, чтобы найти части текста за пределами цитат, чтобы отделить их от текста внутри кавычек или наоборот. Это было бы излишне сложно и склонны к ошибкам, возникающим из краевых случаев, таких как апострофы или даже опечатки. Лучшее решение – использовать настоящий анализатор, такой как тот, который обеспечивается модулем Chust . Вот простой пример, который печатает токены, идентифицированные в входном файле с помощью Chust Class.

shlex_example.py

import shlex
import sys

if len(sys.argv)  2:
    print('Please specify one filename on the command line.')
    sys.exit(1)

filename  sys.argv[1]
with open(filename, 'r') as f:
    body  f.read()
print('ORIGINAL: {!r}'.format(body))
print()

print('TOKENS:')
lexer  shlex.shlex(body)
for token in lexer:
    print('{!r}'.format(token))

При запуске данных со встроенными кавычками анализатор производит список ожидаемых токенов.

$ python3 shlex_example.py quotes.txt

ORIGINAL: 'This string has embedded "double quotes" and\n\'singl
e quotes\' in it, and even "a \'nested example\'".\n'

TOKENS:
'This'
'string'
'has'
'embedded'
'"double quotes"'
'and'
"'single quotes'"
'in'
'it'
','
'and'
'even'
'"a \'nested example\'"'
'.'

Изолированные цитаты, такие как апострофы, также обрабатываются. Рассмотрим этот входной файл.

This string has an embedded apostrophe, doesn't it?

Токен с встроенной апострофом не проблема.

$ python3 shlex_example.py apostrophe.txt

ORIGINAL: "This string has an embedded apostrophe, doesn't it?"

TOKENS:
'This'
'string'
'has'
'an'
'embedded'
'apostrophe'
','
"doesn't"
'it'
'?'

Сделать безопасные строки для снарядов

Функция QUOTE () выполняет обратную операцию, убегая существующих цитат и добавляя недостающие цитаты для строк, чтобы их безопасно использовать в командах оболочки.

shlex_quote.py

import shlex

examples  [
    "Embedded'SingleQuote",
    'Embedded"DoubleQuote',
    'Embedded Space',
    '~SpecialCharacter',
    r'Back\slash',
]

for s in examples:
    print('ORIGINAL : {}'.format(s))
    print('QUOTED   : {}'.format(shlex.quote(s)))
    print()

Обычно все еще безопаснее использовать список аргументов при использовании SUPPOCESS.POPEN , но в ситуациях, где это невозможно QUOTE () предоставляет некоторую защиту, обеспечивая защиту, убедившись, что специальные символы И белое пространство котируются правильно.

$ python3 shlex_quote.py

ORIGINAL : Embedded'SingleQuote
QUOTED   : 'Embedded'"'"'SingleQuote'

ORIGINAL : Embedded"DoubleQuote
QUOTED   : 'Embedded"DoubleQuote'

ORIGINAL : Embedded Space
QUOTED   : 'Embedded Space'

ORIGINAL : ~SpecialCharacter
QUOTED   : '~SpecialCharacter'

ORIGINAL : Back\slash
QUOTED   : 'Back\slash'

Встроенные комментарии

Поскольку парсер предназначен для использования с командными языками, ему необходимо обрабатывать комментарии. По умолчанию любой текст после # считается частью комментариев и игнорируется. Благодаря характеру анализатора поддерживаются только одноразовые префиксы комментариев. Набор используемых символов комментариев может быть сконфигурирован через комментариев .

$ python3 shlex_example.py comments.txt

ORIGINAL: 'This line is recognized.\n# But this line is ignored.
\nAnd this line is processed.'

TOKENS:
'This'
'line'
'is'
'recognized'
'.'
'And'
'this'
'line'
'is'
'processed'
'.'

Расщепление строк в токены

Чтобы разделить существующую строку в токены компонентов, функция удобства Split () представляет собой простую обертку вокруг парсера.

shlex_split.py

import shlex

text  """This text has "quoted parts" inside it."""
print('ORIGINAL: {!r}'.format(text))
print()

print('TOKENS:')
print(shlex.split(text))

Результатом является список.

$ python3 shlex_split.py

ORIGINAL: 'This text has "quoted parts" inside it.'

TOKENS:
['This', 'text', 'has', 'quoted parts', 'inside', 'it.']

В том числе другие источники токенов

Класс CHLEX включает в себя несколько свойств конфигурации, которые контролируют его поведение. Свойство SOURCE включает в себя функцию для повторного использования функции кода (или конфигурации), позволяя одному потоку токена включить другой. Это похоже на Bourne Shell Source оператора, отсюда и имя.

shlex_source.py

import shlex

text  "This text says to source quotes.txt before continuing."
print('ORIGINAL: {!r}'.format(text))
print()

lexer  shlex.shlex(text)
lexer.wordchars  '.'
lexer.source  'source'

print('TOKENS:')
for token in lexer:
    print('{!r}'.format(token))

Строка « Источник Quotes.txt » в исходном тексте получает специальную обработку. Поскольку «код»> «Код»> «Источник» Свойство LEXER установлен на <код> «Источник» , когда встречается ключевое слово, произошедшее имя файла, отображаемое на следующей строке, автоматически включается. Чтобы привести к появлению имени файла в виде единого токена, . символ должен быть добавлен в список символов, которые включены в слова (в противном случае quotes.txt «становится тремя токенами», «Code> цитаты », «<код>. », «<код> TXT »). Это то, что вывод выглядит.

$ python3 shlex_source.py

ORIGINAL: 'This text says to source quotes.txt before
continuing.'

TOKENS:
'This'
'text'
'says'
'to'
'This'
'string'
'has'
'embedded'
'"double quotes"'
'and'
"'single quotes'"
'in'
'it'
','
'and'
'even'
'"a \'nested example\'"'
'.'
'before'
'continuing.'

Функция источника использует метод, называемый «Code> SourceHook () для загрузки дополнительного источника входного сигнала, поэтому подкласс CHLEX может предоставить альтернативную реализацию, которая загружает данные из локаций, отличных от файлов.

Контроль парсера

Более ранний пример продемонстрировал изменение значения WordChars для управления, какие символы включены в слова. Также возможно установить котировки <код> для использования дополнительных или альтернативных кавычек. Каждая цитата должна быть одним символом, поэтому невозможно иметь разные открытые и закрывать цитаты (например, разбираться в скобках).

shlex_table.py

import shlex

text  """|Col 1||Col 2||Col 3|"""
print('ORIGINAL: {!r}'.format(text))
print()

lexer  shlex.shlex(text)
lexer.quotes  '|'

print('TOKENS:')
for token in lexer:
    print('{!r}'.format(token))

В этом примере каждая ячейка таблицы завернута в вертикальные полосы.

$ python3 shlex_table.py

ORIGINAL: '|Col 1||Col 2||Col 3|'

TOKENS:
'|Col 1|'
'|Col 2|'
'|Col 3|'

Также возможно управлять персонажами пробелов, используемыми для разделения слов.

shlex_whitespace.py

import shlex
import sys

if len(sys.argv)  2:
    print('Please specify one filename on the command line.')
    sys.exit(1)

filename  sys.argv[1]
with open(filename, 'r') as f:
    body  f.read()
print('ORIGINAL: {!r}'.format(body))
print()

print('TOKENS:')
lexer  shlex.shlex(body)
lexer.whitespace  '.,'
for token in lexer:
    print('{!r}'.format(token))

Если пример в Shlex_example.py модифицируется, чтобы включить период и запятую, изменение результатов.

$ python3 shlex_whitespace.py quotes.txt

ORIGINAL: 'This string has embedded "double quotes" and\n\'singl
e quotes\' in it, and even "a \'nested example\'".\n'

TOKENS:
'This'
'string'
'has'
'embedded'
'"double quotes"'
'and'
"'single quotes'"
'in'
'it'
'and'
'even'
'"a \'nested example\'"'

Обработка ошибок

Когда парсер столкнулся с конца его ввода, прежде чем все цитируемые строки будут закрыты, он повышает <код> ValueError . Когда это произойдет, полезно изучить некоторые свойства, поддерживаемые парсером, поскольку он обрабатывает вход. Например, <Код> Inflile относится к названию обработанного файла (который может отличаться от исходного файла, если один файл источников другого). <Код> Lineno сообщает линию, когда ошибка обнаружена. <Код> Lineno обычно является концом файла, который может быть далеко от первой цитаты. Атрибут TOKEN содержит буфер текста, который еще не включен в действительный токен. Метод ERROR_LEADER () дает префикс сообщения в стиле, аналогично компиляторам Unix, что позволяет редакторы, такие как EMACS , чтобы проанализировать ошибку и ответить пользователю непосредственно на неверную линию Отказ

shlex_errors.py

import shlex

text  """This line is ok.
This line has an "unfinished quote.
This line is ok, too.
"""

print('ORIGINAL: {!r}'.format(text))
print()

lexer  shlex.shlex(text)

print('TOKENS:')
try:
    for token in lexer:
        print('{!r}'.format(token))
except ValueError as err:
    first_line_of_error  lexer.token.splitlines()[0]
    print('ERROR: {} {}'.format(lexer.error_leader(), err))
    print('following {!r}'.format(first_line_of_error))

Пример создает этот выход.

$ python3 shlex_errors.py

ORIGINAL: 'This line is ok.\nThis line has an "unfinished quote.
\nThis line is ok, too.\n'

TOKENS:
'This'
'line'
'is'
'ok'
'.'
'This'
'line'
'has'
'an'
ERROR: "None", line 4:  No closing quotation
following '"unfinished quote.'

POSIX против Non-Posix Parsing

Поведение по умолчанию для анализатора является использование обратно-совместимого в обратном направлении, который не соответствует POSIX. Для поведения POSIX установите аргумент POSIX при построении парсера.

shlex_posix.py

import shlex

examples  [
    'Do"Not"Separate',
    '"Do"Separate',
    'Escaped \e Character not in quotes',
    'Escaped "\e" Character in double quotes',
    "Escaped '\e' Character in single quotes",
    r"Escaped '\'' \"\'\" single quote",
    r'Escaped "\"" \'\"\' double quote',
    "\"'Strip extra layer of quotes'\"",
]

for s in examples:
    print('ORIGINAL : {!r}'.format(s))
    print('non-POSIX: ', end'')

    non_posix_lexer  shlex.shlex(s, posixFalse)
    try:
        print('{!r}'.format(list(non_posix_lexer)))
    except ValueError as err:
        print('error({})'.format(err))

    print('POSIX    : ', end'')
    posix_lexer  shlex.shlex(s, posixTrue)
    try:
        print('{!r}'.format(list(posix_lexer)))
    except ValueError as err:
        print('error({})'.format(err))

    print()

Вот несколько примеров различий в поведении анализа.

$ python3 shlex_posix.py

ORIGINAL : 'Do"Not"Separate'
non-POSIX: ['Do"Not"Separate']
POSIX    : ['DoNotSeparate']

ORIGINAL : '"Do"Separate'
non-POSIX: ['"Do"', 'Separate']
POSIX    : ['DoSeparate']

ORIGINAL : 'Escaped \\e Character not in quotes'
non-POSIX: ['Escaped', '\\', 'e', 'Character', 'not', 'in',
'quotes']
POSIX    : ['Escaped', 'e', 'Character', 'not', 'in', 'quotes']

ORIGINAL : 'Escaped "\\e" Character in double quotes'
non-POSIX: ['Escaped', '"\\e"', 'Character', 'in', 'double',
'quotes']
POSIX    : ['Escaped', '\\e', 'Character', 'in', 'double',
'quotes']

ORIGINAL : "Escaped '\\e' Character in single quotes"
non-POSIX: ['Escaped', "'\\e'", 'Character', 'in', 'single',
'quotes']
POSIX    : ['Escaped', '\\e', 'Character', 'in', 'single',
'quotes']

ORIGINAL : 'Escaped \'\\\'\' \\"\\\'\\" single quote'
non-POSIX: error(No closing quotation)
POSIX    : ['Escaped', '\\ \\"\\"', 'single', 'quote']

ORIGINAL : 'Escaped "\\"" \\\'\\"\\\' double quote'
non-POSIX: error(No closing quotation)
POSIX    : ['Escaped', '"', '\'"\'', 'double', 'quote']

ORIGINAL : '"\'Strip extra layer of quotes\'"'
non-POSIX: ['"\'Strip extra layer of quotes\'"']
POSIX    : ["'Strip extra layer of quotes'"]

Смотрите также