Функции замены регулярных выражений на помощь
Я не переписываю аст-подобный синтаксический анализатор для вас, но одна хитрость, которая работает довольно хорошо - это использоватьрегулярные выражения для замены строк в кавычках и их замены на «переменные» (я выбрал __token(number)__
), немного похоже на то, что вы не используете какой-то код.
Запишите заменяемые строки(это должно заботиться о пробелах), замените пробел запятой (защита от символов до того, как :
позволяет пройти последний тест) и замените на строки снова.
import re,itertools
def to_dict(s):
rep_dict = {}
cnt = itertools.count()
def rep_func(m):
rval = "__token{}__".format(next(cnt))
rep_dict[rval] = m.group(0)
return rval
# replaces single/double quoted strings by token variable-like idents
# going on a limb to support escaped quotes in the string and double escapes at the end of the string
s = re.sub(r"(['\"]).*?([^\\]|\\\\)\1",rep_func,s)
# replaces spaces that follow a letter/digit/underscore by comma
s = re.sub("(\w)\s+",r"\1,",s)
#print("debug",s) # uncomment to see temp string
# put back the original strings
s = re.sub("__token\d+__",lambda m : rep_dict[m.group(0)],s)
return eval("dict({s})".format(s=s))
print(to_dict("k1='v1' k2='v2'"))
print(to_dict("s='1234' n=1234"))
print(to_dict(r"key='hello world'"))
print(to_dict('key="hello world"'))
print(to_dict("""k4='k5="hello"' k5={'k6': ['potato']}"""))
# extreme string test
print(to_dict(r"key='hello \'world\\'"))
печатает:
{'k2': 'v2', 'k1': 'v1'}
{'n': 1234, 's': '1234'}
{'key': 'hello world'}
{'key': 'hello world'}
{'k5': {'k6': ['potato']}, 'k4': 'k5="hello"'}
{'key': "hello 'world\\"}
Ключ заключается в том, чтобы извлечь строки (заключенные в кавычки / двойные кавычки), используя не жадные регулярные выражения, и заменить их не строковыми (например, если это были строковые переменные не литералы) в выражении.Регулярное выражение настроено таким образом, что оно может принимать экранированные кавычки и двойной экранирование в конце строки (пользовательское решение)
Функция замены является внутренней функцией, поэтому она может использовать нелокальный словарь и счетчик и отслеживатьтекст заменен, поэтому его можно восстановить после того, как пробелы были учтены.
При замене пробелов запятыми следует соблюдать осторожность, чтобы не делать это после двоеточия (последний тест) или всех рассмотренных вопросовпосле алфавитного / нижнего подчеркивания (следовательно, защита \w
в регулярном выражении замены для запятой)
Если мы раскомментируем код отладочной печати непосредственно перед тем, как будут возвращены исходные строки, на которых будет напечатано:
debug k1=__token0__,k2=__token1__
debug s=__token0__,n=1234
debug key=__token0__
debug k4=__token0__,k5={__token1__: [__token2__]}
debug key=__token0__
Строки были пробиты, и замена пробелов сработала правильно.Если приложить больше усилий, вероятно, можно будет заключить в кавычки ключи и заменить k1=
на "k1":
, чтобы вместо eval
можно было использовать ast.literal_eval
(более рискованно, и здесь не требуется)
Я уверен, что некоторые сверхсложные выражения могут нарушить мой код (я даже слышал, что существует очень мало парсеров json, способных проанализировать 100% допустимых файлов json), но для отправленных вами тестов это сработает (конечно, если какой-нибудь забавный парень попытается вставить __tokenxx__
идентификаторов в исходные строки, это потерпит неудачу, возможно, его можно будет заменить на некоторые другие недопустимые переменные-заполнители).Некоторое время назад я создал лексер Ada, используя эту технику, чтобы избежать пробелов в строках, и это сработало довольно хорошо.