2010-03-01 2 views
10

어휘 분석의 세계로 들어가는 첫 번째 벤처이기 때문에 여기에서 발견 과정을 상당히 빠르게 진행할 수 있습니다. 어쩌면 이것은 잘못된 경로 일 수도 있습니다. 먼저, 내 문제를 설명해 드리겠습니다 :파이썬 - 어휘 분석 및 토큰 화

증류기가 약 15 개의 중요한 속성이고 나머지는 거의 생성 할 수없는 매우 큰 특성 파일 (1,000 개의 속성 순)이 있습니다. 변화.

따라서, 예를 들어 :

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 

로 어휘 분석과 토큰 화 :

general { 
    name = myname 
    ip = 127.0.0.1 
} 

component1 { 
    key = value 
    foo = bar 
} 

이 내가 뭔가를 토큰 화하는 만들려는 형식의 유형입니다 내 최고의 경로처럼 들리지만, 이것은 매우 간단한 형태입니다. 그것은 간단한 문법이고, 간단한 대치입니다. 저는 망치를 두드리는 데 쓰레기를 가져 가지 않을 것이라고 확신하고 싶습니다.

내 자신의 렉서와 토크 나이저를 만들 수 있습니다. 또는 ANTlr이 가능하지만 휠을 다시 발명하고 ANTlr이 잔인한 소리를내는 것을 좋아하지 않습니다.

컴파일러 기술에 익숙하지 않아 올바른 방향의 포인터 & 코드가 가장 만족 스러울 것입니다.

참고 : 입력 형식을 변경할 수 있습니다.

+2

왜 대신 자신의 파서를 생성하는 JSON을 사용하지 ?? – AndiDog

+0

예제 번역에는 약간의 오류가있는 것 같습니다. 그렇지 않다면 "$ {component1} .ip"이 예제 3 행의 "component1"으로 변환 된 이유를 알 수 없습니다. 구문이 규칙적인 경우 $ {identifiers}를 정규 표현식으로 추출한 다음 사전 검색이없는 사전 검색으로 바꿀 수 있습니다. – msw

+0

거기에 몇 가지 실수가 있었는데, 나는 그것들을 바로 잡았다 고 생각합니다. –

답변

10

Using Regular Expressions for Lexical Analysis의 우수 기사가 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 

을 위해 :

========================== 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') 
+0

Fyi, [그런 종류의 토크 나이저] (http://docs.python.org/3.2/library/re.html#writing-a-tokenizer)는're' 모듈의 stdlib 문서화에 포함 시켰습니다 – cfi

1

입력 파일의 형식을 변경할 수 있으면 JSON과 같은 기존 형식에 대해 파서를 사용할 수 있습니다.

그러나 문제의 진술에 따르면 그렇지 않은 것처럼 들립니다. 그러므로 사용자 정의 렉서와 파서를 만들고 싶다면 PLY (Python Lex/Yacc)을 사용하십시오. 사용하기 쉽고 lex/yacc와 동일하게 작동합니다.

다음은 PLY를 사용하여 작성한 계산기 example에 대한 링크입니다. t_으로 시작하는 모든 것은 유효한 토큰을 정의하는 렉서 규칙이며, p_으로 시작하는 모든 것은 문법의 생성을 정의하는 파서 규칙입니다.

2

형식이 단순한 것처럼 보입니다. 전체 분석기/렉서가 과장 될 것 같습니다. 정규 표현식과 문자열 조작이 조합 된 것처럼 보입니다.

또 다른 아이디어는 파일을 json 또는 xml과 같은 것으로 변경하고 기존 패키지를 사용하는 것입니다.

2

간단한 DFA가 이에 적합합니다.

  1. 적어도 하나 개의 유효한 문자가 더 이름 문자 나 }을 찾고, 이름을 신
  2. 적어도 하나 개의 유효한 이름 문자를 형성 찾고 ${
  3. ${ 찾고 : 당신은 몇 주이 필요합니다.

등록 정보 파일이 순서에 구속력이 없으면 두 개의 통과 프로세서에서 각 이름이 올바르게 해석되는지 확인할 수 있습니다.

물론 대체 코드를 작성해야하지만 사용 된 모든 이름의 목록이 있으면 가장 간단한 구현은 ${name}에 해당 값이있는 찾기/바꾸기입니다.

1

제공하는 구문은 Mako templates engine과 비슷합니다. 나는 당신이 그것을 시도해 볼 수 있다고 생각한다. 그것은 오히려 간단한 API이다.