Автор оригинала: 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'"]
Смотрите также
- Стандартная библиотечная документация для Chlex
- CMD – инструменты для создания интерактивных командных переводчиков.
- Argparse – параметр командной строки.
- Команды подпроцесс – запустите после анализа командной строки.