самый эффективный способ парсить этот скриптовый язык

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

Вот пример проблемной части языка:

T
L /LOCATE ME/
C /LOCATE ME/CHANGED ME/ * *
C ;CHANGED ME;CHANGED ME AGAIN; 1 *

Символы /, по-видимому, заключают строки в кавычки, а также действуют как разделитель для команды C (CHANGE) в синтаксисе типа sed, хотя он допускает использование любого символа в качестве разделителя.

Я, вероятно, реализовал около половины наиболее распространенных команд, просто используя parse_tokens(line.split()) до сих пор. Это было быстро и грязно, но сработало на удивление хорошо.

Чтобы не писать собственный лексер, я попробовал shlex.

Это работает довольно хорошо, за исключением случаев CHANGE:

import shlex

def shlex_test(cmd_str):
    lex = shlex.shlex(cmd_str)
    lex.quotes = '/'
    return list(lex)

print(shlex_test('L /spaced string/'))
# OK! gives: ['L', '/spaced string/']

print(shlex_test('C /spaced string/another string/ * *'))
# gives   : ['C', '/spaced string/', 'another', 'string/', '*', '*']
# desired : any format that doesn't split on a space between /'s

print(shlex_test('C ;a b;b a;'))
# gives   : ['C', ';', 'b', 'a', ';', 'a', 'b', ';']
# desired : same format as CHANGE command above

Кто-нибудь знает простой способ сделать это (с shlex или иначе)?

РЕДАКТИРОВАТЬ:

Если это поможет, вот синтаксис команды CHANGE, приведенный в файле справки:

'''
C [/stg1/stg2/ [n|n m]]

    The CHANGE command replaces the m-th occurrence of "stg1" with "stg2"
for the next n lines.  The default value for m and n is 1.'''

Столь же сложные для токенизации команды X и Y:

'''
X [/command/[command/[...]]n]
Y [/command/[command/[...]]n]

    The X and Y commands allow the execution of several commands contained
in one command.  To define an X or Y "command string", enter X (or Y)
followed by a space, then individual commands, each separated by a
delimiter (e.g. a period ".").  An unlimited number of commands may be
placed in the X or Y command string.  Once the command string has been
defined, entering X (or Y) followed optionally by a count n will execute
the defined command string n times.  If n is not specified, it will
default to 1.'''

person Robbie Rosati    schedule 19.07.2012    source источник
comment
У вас есть доступ к определению языка? Если это так, цитата из соответствующей части может быть полезной для всех нас.   -  person Marcin    schedule 19.07.2012
comment
@Marcin Я добавил некоторую соответствующую информацию из файла справки, это вся документация, которая у меня есть.   -  person Robbie Rosati    schedule 19.07.2012
comment
Я не знаю shlex, но думаю, что regex (re) также может быть полезным.   -  person machaku    schedule 19.07.2012
comment
re может работать... но очень сложно реализовать полноценный лексер/парсер языка только с re   -  person Joran Beasley    schedule 19.07.2012
comment
Теперь у вас значительно больше, чем 2 проблемы.   -  person Wooble    schedule 19.07.2012
comment
Я имел в виду re для простого разбора или извлечения C /spaced string/another string/ * *, а затем используйте все, что хотите, для остальной части кода.   -  person machaku    schedule 19.07.2012
comment
Я думаю, что pyparsing может быть лучшим выбором.   -  person Davoud Taghawi-Nejad    schedule 21.07.2012


Ответы (1)


Проблема, возможно, в том, что / не стоит для кавычек, а только для разграничения. Я предполагаю, что третий символ всегда используется для определения разделителя. Кроме того, вам не нужны / или ; в выводе, не так ли?

Я только что сделал следующее только с разделением для случая команд L и C:

>>> def parse(cmd):
...     delim = cmd[2]
...     return cmd.split(delim)
...
>>> c_cmd = "C /LOCATE ME/CHANGED ME/ * *"
>>> parse(c_cmd)
['C ', 'LOCATE ME', 'CHANGED ME', ' * *']

>>> c_cmd2 = "C ;a b;b a;"
>>> parse(c_cmd2)
['C ', 'a b', 'b a', '']

>>> l_cmd = "L /spaced string/"
>>> parse(l_cmd)
['L ', 'spaced string', '']

Для необязательной части " * *" вы можете использовать split(" ") в последнем элементе списка.

>>> parse(c_cmd)[-1].split(" ")
['', '*', '*']
person sevenforce    schedule 19.07.2012
comment
к сожалению, это не всегда третий символ, но я попробую этот подход и отпишусь, спасибо. - person Robbie Rosati; 20.07.2012