Skip to content

Commit

Permalink
código ao fim da aula de 21/05/2024
Browse files Browse the repository at this point in the history
  • Loading branch information
leopoldomt committed May 21, 2024
1 parent ffdd4cf commit c62ef06
Show file tree
Hide file tree
Showing 9 changed files with 321 additions and 83 deletions.
29 changes: 29 additions & 0 deletions 2024-05-21.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# IF688 - Teoria e Implementação de Linguagens Computacionais

## _Recursive-Descent Parsing_ (Análise Sintática) e _Abstract Syntax Trees_ (Análise Semântica)

### Objetivo

O objetivo desta aula é apresentar os conceitos fundamentais relacionados à técnica _recursive-descent parsing_, que permite implementar análise sintática manualmente. Adicionalmente, também implementamos árvores sintáticas abstratas em código, que serão utilizadas para a análise semântica.

### Questões para Discussão

- Quais as vantagens e limitações de _recursive descent parsing_ como técnica de construção de _parsers_?
- Qual a diferença entre árvores sintáticas concretas e abstratas?

### Material usado em aula

- Código desenvolvido em sala de aula
- [versão para o início da aula (.zip com código incompleto)](https://drive.google.com/file/d/1x8KCmJhsKPX6aF5L5RwEcx_XpFL8LMvR/view?usp=sharing)
- [versão do código ao final da aula (.zip com código escrito durante a aula)](https://drive.google.com/file/d/1O0T0RHE_cARV9xzzdSGTOJj7jK-GCcfN/view?usp=sharing)

### Vídeos

- [Análise Sintática - Recursive-descent parsing](https://www.youtube.com/watch?v=-7B39_U6ZL4)
- [Análise Semântica - Introdução a ASTs](https://www.youtube.com/watch?v=Wz4TSKOrBrM)

### Links Relacionados

- [Recursive descent parser](https://en.wikipedia.org/wiki/Recursive_descent_parser)
- [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree)
- [Abstract Syntax Tree Implementation Idioms, by Joel Jones](http://www.hillside.net/plop/plop2003/Papers/Jones-ImplementingASTs.pdf)
2 changes: 2 additions & 0 deletions 2024-05-21/basic/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ def abort(self, msg):
def parse(self):
pass

# nl é quebra de linha

# program ::= statement

# statement ::=
Expand Down
40 changes: 40 additions & 0 deletions 2024-05-21/expressions/astnodes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
class Expr(object):
pass

class NumExpr(Expr):
def __init__(self, valor):
self.valor = valor
def __str__(self) -> str:
return "NumExpr("+str(self.valor)+")"

class IdExpr(Expr):
def __init__(self, nome):
self.nome = nome
def __str__(self) -> str:
return "IdExpr("+self.nome+")"

class SumExpr(Expr):
def __init__(self, l_esq, l_dir):
self.esq = l_esq
self.dir = l_dir
def __str__(self) -> str:
return "SumExpr("+str(self.esq)+", "+str(self.dir)+")"

class MulExpr(Expr):
def __init__(self, l_esq, l_dir):
self.esq = l_esq
self.dir = l_dir
def __str__(self) -> str:
return "MulExpr("+str(self.esq)+", "+str(self.dir)+")"
class DivExpr(Expr):
def __init__(self, l_esq, l_dir):
self.esq = l_esq
self.dir = l_dir
def __str__(self) -> str:
return "DivExpr("+str(self.esq)+", "+str(self.dir)+")"
class SubExpr(Expr):
def __init__(self, l_esq, l_dir):
self.esq = l_esq
self.dir = l_dir
def __str__(self) -> str:
return "SubExpr("+str(self.esq)+", "+str(self.dir)+")"
6 changes: 5 additions & 1 deletion 2024-05-21/expressions/exp.txt
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
1 - 2 + 3232
2+2;
1/2/3;
2+3+(4/0);
((((2 * 3232))));
1+2*(3+y);
207 changes: 140 additions & 67 deletions 2024-05-21/expressions/lexer.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,91 +2,164 @@
import sys

class Lexer:
def __init__(self, source):
self.source = source + '\n'
self.caractereAtual = ''
self.posicaoAtual = -1
self.proximoCaractere()

# Processa o próximo caractere
def proximoCaractere(self):
self.posicaoAtual+=1
if self.posicaoAtual >= len(self.source):
self.caractereAtual = None #EOF
def __init__(self, input):
self.source = input + '\n' #código-fonte (entrada)
self.curChar = '' #caractere atual dentro do código-fonte
self.curPos = -1
self.nextChar()
pass

# Processa o proximo caractere
def nextChar(self):
self.curPos = self.curPos + 1
if self.curPos >= len(self.source):
self.curChar = '\0' #EOF
else:
self.caractereAtual = self.source[self.posicaoAtual]
self.curChar = self.source[self.curPos]

# Retorna o caractere de lookahead (qual o que vem aí)
# Retorna o caractere seguinte (ainda não lido).
def peek(self):
if self.posicaoAtual+1 >= len(self.source):
return None #EOF
else:
return self.source[self.posicaoAtual+1]
if self.curPos+1 >= len(self.source):
return '\0'
else:
return self.source[self.curPos+1]

# Token inválido, imprime mensagem de erro
# Token inválido encontrado, método usado para imprimir mensagem de erro e encerrar.
def abort(self, message):
sys.exit('Erro Léxico: '+message)
sys.exit("Erro léxico! " + message)

# Pula espaços em branco
# Pular espaço em branco, exceto novas linhas, que são usadas como separadores.
def skipWhitespace(self):
while self.caractereAtual == ' ' or self.caractereAtual == '\t' or self.caractereAtual == '\n':
self.proximoCaractere()
while self.curChar == ' ' or self.curChar == '\n' or self.curChar == '\t' or self.curChar == '\r':
self.nextChar()

# Pula comentários
# Pular comentários.
def skipComment(self):
pass
if self.curChar=='#':
while self.curChar != '\n':
self.nextChar()

# Retorna o próximo token
# Return o próximo token.
def getToken(self):
self.skipWhitespace()
self.skipComment()
token = None
if self.caractereAtual is None:
token = Token(self.caractereAtual, TipoToken.EOF)
elif self.caractereAtual == '+':
token = Token(self.caractereAtual, TipoToken.SOMA)
elif self.caractereAtual == '-':
token = Token(self.caractereAtual, TipoToken.SUBTRACAO)
elif self.caractereAtual == '*':
token = Token(self.caractereAtual, TipoToken.MULTIPLICACAO)
elif self.caractereAtual == '/':
token = Token(self.caractereAtual, TipoToken.DIVISAO)
elif self.caractereAtual.isdigit():
posicaoInicial = self.posicaoAtual
while self.peek()!=None and self.peek().isdigit():
self.proximoCaractere()
lexema = self.source[ posicaoInicial : self.posicaoAtual+1 ]
token = Token(lexema, TipoToken.NUMERO)
elif self.caractereAtual.isalpha():
posicaoInicial = self.posicaoAtual
while self.peek()!=None and self.peek().isalnum():
self.proximoCaractere()
lexema = self.source[ posicaoInicial : self.posicaoAtual+1 ]
tipo = Token.checaPalavraReservada(lexema)
if tipo == None:
token = Token(lexema, TipoToken.IDENTIFICADOR)
if self.curChar == '+':
token = Token(self.curChar, TokenType.PLUS)
elif self.curChar == '-':
token = Token(self.curChar, TokenType.MINUS)
elif self.curChar == '*':
token = Token(self.curChar, TokenType.ASTERISK)
elif self.curChar == '/':
token = Token(self.curChar, TokenType.SLASH)
elif self.curChar == '(':
token = Token(self.curChar, TokenType.L_PAREN)
elif self.curChar == ')':
token = Token(self.curChar, TokenType.R_PAREN)
elif self.curChar == ';':
token = Token(self.curChar, TokenType.SEMICOLON)
elif self.curChar == '\0':
token = Token(self.curChar, TokenType.EOF)
#se for = EQ, se for == EQEQ
elif self.curChar == '=':
if self.peek() == '=':
c = self.curChar
self.nextChar()
token = Token(c + self.curChar, TokenType.EQEQ)
else:
token = Token(lexema, tipo)
else:
self.abort("Caractere não reconhecido: "+self.caractereAtual)
self.proximoCaractere()
token = Token(self.curChar, TokenType.EQ)
elif self.curChar == '!':
if self.peek() == '=':
c = self.curChar
self.nextChar()
token = Token(c + self.curChar, TokenType.NOTEQ)
else:
self.abort("Esperava o símbolo de = e recebeu "+self.peek())
elif self.curChar == '>':
if self.peek() == '=':
c = self.curChar
self.nextChar()
token = Token(c + self.curChar, TokenType.GTEQ)
else:
token = Token(self.curChar, TokenType.GT)
elif self.curChar == '<':
if self.peek() == '=':
c = self.curChar
self.nextChar()
token = Token(c + self.curChar, TokenType.LTEQ)
else:
token = Token(self.curChar, TokenType.LT)
elif self.curChar == '\"':
self.nextChar()
startPos = self.curPos
while self.curChar != '\"':
if self.curChar == '\\' or self.curChar == '\t' or self.curChar == '\r' or self.curChar == '%':
self.abort("Caractere ilegal dentro de uma string")
self.nextChar()
stringText = self.source[startPos : self.curPos]
token = Token(stringText, TokenType.STRING)
elif self.curChar.isdigit():
startPos = self.curPos
while self.peek().isdigit():
self.nextChar()
if self.peek() == '.': #decimais
self.nextChar()
if not self.peek().isdigit():
self.abort("Caractere ilegal dentro de um número: "+ self.peek())
while self.peek().isdigit():
self.nextChar()
number = self.source[startPos : self.curPos + 1]
token = Token(number, TokenType.NUMBER)
elif self.curChar.isalpha():
startPos = self.curPos
while self.peek().isalnum():
self.nextChar()
word = self.source[startPos : self.curPos + 1]
keyword = Token.checkIfKeyword(word)
if keyword == None:
token = Token(word, TokenType.IDENT)
else:
token = Token(word, keyword)
else:
#Token desconhecido
self.abort("Token desconhecido: "+self.curChar)

self.nextChar()
return token

class Token:
def __init__(self, lexema, tipo) -> None:
self.lexema = lexema
self.tipo = tipo
def __init__(self, tokenText, tokenKind):
self.text = tokenText #lexema, a instância específica encontrada
self.kind = tokenKind # o tipo de token (TokenType) classificado

@staticmethod
def checaPalavraReservada(lexema):
for tipo in TipoToken:
if tipo.name == lexema and tipo.value > 100 and tipo.value < 200:
return tipo
def checkIfKeyword(word):
for kind in TokenType:
if kind.name == word.upper() and kind.value > 100 and kind.value < 200:
return kind
return None

class TipoToken(enum.Enum):
class TokenType(enum.Enum):
EOF = -1
NUMERO = 1
IDENTIFICADOR = 2
SOMA = 202
SUBTRACAO = 203
MULTIPLICACAO = 204
DIVISAO = 205
NUMBER = 1
IDENT = 2
STRING = 3
SEMICOLON = 4
L_PAREN = 5
R_PAREN = 6
#PALAVRAS RESERVADAS
PRINT = 101
TRUE = 102
FALSE = 103
#OPERADORES
EQ = 201
PLUS = 202
MINUS = 203
ASTERISK = 204
SLASH = 205
EQEQ = 206
NOTEQ = 207
LT = 208
LTEQ = 209
GT = 210
GTEQ = 211
5 changes: 4 additions & 1 deletion 2024-05-21/expressions/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ def main():

lexer = Lexer(input)
parser = Parser(lexer)
parser.parse()
expressions = parser.parse()
print("Terminamos.")

for exp in expressions:
print(exp)

main()
Loading

0 comments on commit c62ef06

Please sign in to comment.