Я хочу использовать Python для разделения текста на токены. Маркеры разделяются точкой с запятой без кавычек и без комментариев, где мы предполагаем, что " определяет кавычки, а -- определяет комментарии. Код выполняет то, что я хочу (по крайней мере, в тестовых примерах, которые я рассмотрел). Однако этот код кажется довольно хакерским и хрупким из-за замен, которые я делаю, чтобы сохранить заключенные в кавычки -- и ;. Есть ли более чистый и надежный подход к этой проблеме?

import shlex
import string

testdata = '''
Line 1
Line 2
Line 3;

Line 4;

Line 5
Line 6 -- ;
Line 7;

Line 8 ";"
Line 9 "--"
Line 10 "--;"
Line 11 ";--"
Line 12;
'''

dash_suffix = '__you_should_know_better_1__'
quote_prefix = '__you_should_know_better_2__'
s = testdata.replace('--', f'#{dash_suffix}')
s = s.replace('"', f'{quote_prefix}"')

parser = shlex.shlex(instream=s, posix=True)
parser.whitespace = ';'
parser.whitespace_split = True

for token in parser:
    trimmed_token = token.strip()
    if trimmed_token:
        parsed_token = trimmed_token.replace(quote_prefix, '"')
        parsed_token = parsed_token.replace(f'#{dash_suffix}', '--')
        print(80 * '=')
        print(parsed_token)
0
Stirling 6 Мар 2021 в 07:57

2 ответа

Лучший ответ

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

testdata = '''
Line 1
Line 2
Line 3;

Line 4;

Line 5
Line 6 -- ;
Line 7;

Line 8 ";"
Line 9 "--"
Line 10 "--;"
Line 11 ";--"
Line 12;
'''


def advance_until(i: int, s: str, c: str, check_escape: bool = False) -> int:
    while i < len(s):
        if s[i] == c:
            if not check_escape:
                return i
            if i > 0 and s[i - 1] != '\\':
                return i
        i += 1
    return i


tokens = []
token_start = 0
while token_start < len(testdata):
    token_end = token_start
    while token_end < len(testdata):
        if testdata[token_end] == ';':
            # An unescaped, unquoted semicolon terminates a statement.
            tokens.append(testdata[token_start:(token_end + 1)].strip())
            break
        elif testdata[token_end] in '\'"':
            # Advance the cursor to the end of the string.
            token_end = advance_until(token_end + 1, testdata,
                                      testdata[token_end], True)
        elif testdata[token_end] == '#':
            # Advance the cursor to the end of the line.
            token_end = advance_until(token_end + 1, testdata, '\n')
        elif testdata[token_end] == '-':
            # Check if this is the beginning of a comment.
            if (token_end < len(testdata) - 1
                    and testdata[token_end + 1] == '-'):
                token_end = advance_until(token_end + 2, testdata, '\n')
        token_end += 1
    token_start = token_end + 1

for token in tokens:
    print(80 * '*')
    print(token)
0
Stirling 6 Мар 2021 в 22:40

Вы используете лексический анализатор, предназначенный для анализа сценариев оболочки. Он не был разработан для синтаксического анализа общего назначения, поэтому всегда будет взлом. С помощью модуля Python re вполне возможно построить мощный лексический анализатор. Проверьте пример токенизатора в документации:

https://docs.python.org/3/library/re.html#writing-a-tokenizer

0
Tim Roberts 6 Мар 2021 в 05:23