python, lex, yacc: ply

Nada como escrever parsers usando o bom e velho modo: lex e yacc (ou flex e bison, ambos GNU).

Procurando um pouco sobre BNF para python, acabei encontrando primeiro o pyparsing, e depois o PLY – este último que me chamou mais a atenção, pois embora meio esquisitão nos primeiros 10 minutos, se parece mais com a maneira “padrão” de usar o lex e yacc.

A documentação é muito boa, e cheia de exemplos.

Pra que eu mesmo não esqueça, fiz um pequeno exemplo, que interpreta o seguinte texto:

group1 {
    "file1.txt";
    "file2.txt";
};

group2 {
    "file3.txt";
    "file4.txt";
};

Pra facilitar, coloquei uma cópia desse conteúdo dentro do próprio fonte, que está abaixo.


#!/usr/bin/env python
# coding: utf-8
# 20080922 AF

import sys
from ply import lex, yacc

tokens = ('KEYWORD','FILENAME','QUOTE','OBRACE','EBRACE','SEMICOLON')

# LEX stuff
t_KEYWORD = r'[a-zA-Z][a-zA-Z0-9]*\ '
t_FILENAME = r'[-.\/a-zA-Z0-9]+'
t_QUOTE = r'"'
t_OBRACE = r'{'
t_EBRACE = r'}'
t_SEMICOLON = ';'

def t_newline(t): r'\n'
def t_whitespace(t): r'[ \t]+'
def t_error(t): raise TypeError('unknown text: %s' % t.value)

# YACC stuff
current_items = []
def p_groups(p):
    """groups :
              | groups KEYWORD OBRACE files EBRACE SEMICOLON"""
    if len(p) > 2:
        grp = p[2].strip()
        if groups.has_key(grp):
            raise ValueError('group %s already exists!' % grp)
        global current_items
        groups[grp] = current_items
        current_items = []

def p_files(p):
    """files :
             | files QUOTE FILENAME QUOTE SEMICOLON"""
    if len(p) > 2:
        global current_items
        current_items.append(p[3])

def p_error(p): raise TypeError('parser error: %s' % p.value)

buff = """
group1 {
    "file1.txt";
    "file2.txt";
};

group2 {
    "file3.txt";
    "file4.txt";
};
"""

if __name__ == '__main__':
    groups = {}
    parser = yacc.yacc(write_tables=False, debug=False)

    try:
        parser.parse(buff, lexer=lex.lex())
    except Exception, e:
        print 'ERROR', e
    else:
        print groups

Anúncios


Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s