• Home
  • About
    • pedro@matias:~$ photo

      pedro@matias:~$

      Eu estou perdido a muito tempo, talvez possa te ensinar alguma coisa!

    • Learn More
    • Email
    • Medium
    • Twitter
    • LinkedIn
    • Github
    • StackOverflow
    • DEV
  • Posts
    • All Posts
    • All Tags

Exemplo PLY (Python Lex-Yacc)

27 Jul 2018

Reading time ~3 minutes

Quando estava no último ano da faculdade. Tive que fazer um compilador para uma linguagem fictícia. O escolhido foi o python para me ajudar na tarefa, pois era mais prático utilizá-lo pois muitas estruturas de dados eram naturais na linguagem não sendo necessárias cria-las na mão como em C ou C++ por exemplo.

Não queria criar algo do 0, queria fazer algo prático, foi quando vi o PLY(Python Lex-Yacc), que é uma implementação de lex e yacc parser para Python, decidi usá-lo. O material da linguagem está aqui(em inglês), mas infelizmente como é um material completo, não é um guia e tem que ficar lendo vários parágrafos e garimpando informação para achar algo.

Usar o PLY foi uma boa escolha, pois ele é muito mais fácil do que imaginei e consegui desenvolver o analisador léxico e sintático, mas tive dificuldades de encontrar um material em português, dessa forma esse post é para ajudar quem precisa de ajuda na implementação de algo usando PLY, espero que seja útil para alguém.

O projeto está no meu github, esse post será baseado nele e por que desenvolvi ele dessa forma, escrevendo algumas dúvidas que tive enquanto o desenvolvi.

O arquivo principal é o cmm.py, é a partir dele que será chamada os parsers da minha linguagem.

Criei um arquivo chamado de mylexer.py que é o analisador léxico, onde coloquei todas as definições de token, ou seja, o que é numero, string, comentários, palavras reservadas e coisas específicas da análise léxica.

reserved = {
	'false'	:	'FALSE',
	'if'	:	'IF',
	'else'	:	'ELSE',
	'int'	:	'INT',
	'string':	'STRING',
	'true'	:	'TRUE',
	'write' :	'WRITE',
	'read' :	'READ'
}

tokens = [ 'NUMBER', 'RPAREN', 'LPAREN' ]+ list(reserved.values())

t_ignore 		= ' \t'

t_RPAREN		= r'\)'
t_LPAREN		= r'\('


def t_NUMBER(t):
	r'\d+'
	t.value = int(t.value)
	return t

Como nesse exemplo simplificado , optei por criar um array com as palavras reservadas, dessa forma ficam separadas dos *tokens *e sendo mais difícil gerar confusão.

As funções tem que ser criadas com o mesmo nome do token, mas com um “t_” na frente. É dessa forma que o PLY faz a relação.

Após isso criei o arquivo grammar.py, onde está as regras da minha gramática, onde defino as regras de precedência, o que é um literal, um programa , variável, e todas as regras da análise sintática necessária.

# Parsing rules
precedence = (
    ('left','LPAREN','RPAREN'),
    ('left','AND','OR'),
    ('left','MAIOR','MENOR', 'MAIOREQUALS', 'MENOREQUALS', 'EQUALS', 'DIFF'),
    ('left','PLUS','MINUS'),
    ('left','TIMES','DIVIDE'),
    ('right','UMINUS', 'NOT', 'TERNARY'),
    )

#define
def p_empty(p):
    'empty :'
    pass

def p_define_end_of_instruction(p):
    'end : SEMICOLON'


def p_literal(t):
    '''literal : NUMBER
                | TRUE
                | FALSE
                | NORMALSTRING
                '''
    t[0]=t[1]

def p_sequence_literal(t):
    '''sequence_literal : literal COMMA sequence_literal
                        | literal'''


def p_define_type(t):
    '''type : INT
            | STRING
            | BOOL'''
    t[0]=t[1]

def p_variavel(t):
    '''variavel : NAME
                | NAME LCOLC expression RCOLC'''
    t[0] = t[1]

O arquivo grammar usa os tokens definidos na análise léxica.

As funções devem que ser criadas com o mesmo nome do regra semântica, mas com um “p_” na frente.

Eu usei bastante prints do python para imprimir cada token e cada operação durante a execução para entender cada etapa me ajudou bastante o fluxo.

Nesse projeto eu comecei a análise semântica, mas está incompleto. Também tentei fazer um arquivo separado para erros, mas como era o último período e eu tinha outros projetos não finalizei, podem dar fork e terminarem sem preocupação.

Eu escrevi esse texto 1 ano depois de ter entregado o projeto, por isso muita coisa pode ter se perdido e provavelmente muitas informações ficaram rasas. Por isso se chegou até aqui, pode entrar em contato comigo se tiver alguma dúvida que terei prazer de ajudar o máximo possível.



python Share Tweet +1