Я обращаюсь к скорости вдоль своего процесса открытия здесь вполне немного, поскольку это - мое первое предприятие в мир лексического анализа. Возможно, это - даже неправильный путь. Во-первых, я опишу свою проблему:
У меня есть очень большие файлы свойств (в порядке 1 000 свойств), которые при дистилляции являются действительно примерно 15 важными свойствами, и остальные могут быть сгенерированы или редко когда-либо изменяться.
Так, например:
general {
name = myname
ip = 127.0.0.1
}
component1 {
key = value
foo = bar
}
Это - тип формата, который я хочу создать для маркирования чего-то как:
property.${general.name}blah.home.directory = /blah
property.${general.name}.ip = ${general.ip}
property.${component1}.ip = ${general.ip}
property.${component1}.foo = ${component1.foo}
в
property.mynameblah.home.directory = /blah
property.myname.ip = 127.0.0.1
property.component1.ip = 127.0.0.1
property.component1.foo = bar
Лексический анализ и токенизация походят на мой оптимальный маршрут, но это - очень простая форма его. Это - простая грамматика, простая замена, и я хотел бы удостовериться, что я не приношу кувалду для вбивания гвоздя.
Я мог создать свой собственный лексический анализатор и токенизатор, или ANTlr является возможностью, но мне не нравится изобретать велосипед, и ANTlr походит на излишество.
Я не знаком с методами компилятора, таким образом, указатели в правильном направлении и коде больше всего ценились бы.
Примечание: Я могу изменить формат ввода.
На сайте effbot.org есть отличная статья по использованию регулярных выражений для лексического анализа.
Адаптация токенизатора к вашей проблеме:
import re
token_pattern = r"""
(?P<identifier>[a-zA-Z_][a-zA-Z0-9_]*)
|(?P<integer>[0-9]+)
|(?P<dot>\.)
|(?P<open_variable>[$][{])
|(?P<open_curly>[{])
|(?P<close_curly>[}])
|(?P<newline>\n)
|(?P<whitespace>\s+)
|(?P<equals>[=])
|(?P<slash>[/])
"""
token_re = re.compile(token_pattern, re.VERBOSE)
class TokenizerException(Exception): pass
def tokenize(text):
pos = 0
while True:
m = token_re.match(text, pos)
if not m: break
pos = m.end()
tokname = m.lastgroup
tokvalue = m.group(tokname)
yield tokname, tokvalue
if pos != len(text):
raise TokenizerException('tokenizer stopped at pos %r of %r' % (
pos, len(text)))
Для проверки делаем:
stuff = r'property.${general.name}.ip = ${general.ip}'
stuff2 = r'''
general {
name = myname
ip = 127.0.0.1
}
'''
print ' stuff '.center(60, '=')
for tok in tokenize(stuff):
print tok
print ' stuff2 '.center(60, '=')
for tok in tokenize(stuff2):
print tok
for:
========================== stuff ===========================
('identifier', 'property')
('dot', '.')
('open_variable', '${')
('identifier', 'general')
('dot', '.')
('identifier', 'name')
('close_curly', '}')
('dot', '.')
('identifier', 'ip')
('whitespace', ' ')
('equals', '=')
('whitespace', ' ')
('open_variable', '${')
('identifier', 'general')
('dot', '.')
('identifier', 'ip')
('close_curly', '}')
========================== stuff2 ==========================
('newline', '\n')
('identifier', 'general')
('whitespace', ' ')
('open_curly', '{')
('newline', '\n')
('whitespace', ' ')
('identifier', 'name')
('whitespace', ' ')
('equals', '=')
('whitespace', ' ')
('identifier', 'myname')
('newline', '\n')
('whitespace', ' ')
('identifier', 'ip')
('whitespace', ' ')
('equals', '=')
('whitespace', ' ')
('integer', '127')
('dot', '.')
('integer', '0')
('dot', '.')
('integer', '0')
('dot', '.')
('integer', '1')
('newline', '\n')
('close_curly', '}')
('newline', '\n')
Каким бы простым ни был ваш формат, я думаю, что полноценный синтаксический анализатор / лексер будет излишним. Похоже, что комбинация регулярных выражений и манипуляции со строками поможет.
Другая идея - изменить файл на что-то вроде json или xml и использовать существующий пакет.
Для этого хорошо работает простой DFA. Вам нужно всего несколько состояний:
$ {
$ {
ищу хотя бы один допустимый символ, образующий имя }
. Если файл свойств не зависит от порядка, вам может потребоваться двухпроходный процессор для проверки правильности разрешения каждого имени.
Конечно, затем вам нужно написать код замены, но как только у вас есть список всех используемых имен, простейшей возможной реализацией будет поиск / замена на $ {name}
с соответствующими значение.
Если вы можете изменить формат входных файлов, вы можете использовать синтаксический анализатор для существующего формата, такого как JSON.
Однако из вашей постановки проблемы похоже, что это не так. Поэтому, если вы хотите создать собственный лексический анализатор и анализатор, используйте PLY (Python Lex / Yacc). Он прост в использовании и работает так же, как lex / yacc.
Вот ссылка на пример калькулятора, построенного с использованием PLY. Обратите внимание, что все, что начинается с t_
, является правилом лексера, определяющим действительный токен, а все, что начинается с p_
, является правилом синтаксического анализатора, определяющим создание грамматики.
Предоставляемый вами синтаксис похож на механизм шаблонов Mako . Думаю, вы могли бы попробовать, это довольно простой API.