diff --git a/2023-11-28.md b/2023-11-28.md index 32893d7..f971c46 100644 --- a/2023-11-28.md +++ b/2023-11-28.md @@ -16,7 +16,7 @@ O objetivo desta aula é introduzir os conceitos fundamentais relacionados à ex - [Slides (pdf)](https://drive.google.com/file/d/1aod-7wnQcyC3SQal8vdmguik3tRij2rA/view?usp=drive_web&authuser=0) - 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/1CRbR5nIYMkKeTnAGezQiI6_MCAIoMse2/view) - - versão do código ao final da aula (.zip com código escrito durante a aula + - [versão do código ao final da aula (.zip com código escrito durante a aula](https://drive.google.com/file/d/1tbCqWs98A1tlS81sTKGllA29STt9NcsO/view) ### Vídeos diff --git a/2023-11-30.md b/2023-11-30.md new file mode 100644 index 0000000..5e76353 --- /dev/null +++ b/2023-11-30.md @@ -0,0 +1,16 @@ +# IF688 - Teoria e Implementação de Linguagens Computacionais + +## Análise Semântica - Escopo, Tipos, Tabelas de Símbolos + +### Objetivo + +O objetivo desta aula é introduzir alguns conceitos gerais relacionados à análise semântica, como questões associadas a escopo, tipos e tabelas de símbolos. + +### Vídeos + +- [Análise Semântica (Tipos, Escopo & Tabela de Símbolos)](https://www.youtube.com/playlist?list=PLHoVp5NAbKJb4L5y5qIZ8JKGujjYTVzIM) + +### Links Relacionados + +- [Symbol Table](https://en.wikipedia.org/wiki/Symbol_table) +- [Type System](https://en.wikipedia.org/wiki/Type_system) diff --git a/2023-12-12.md b/2023-12-12.md new file mode 100644 index 0000000..4176ede --- /dev/null +++ b/2023-12-12.md @@ -0,0 +1,25 @@ +# IF688 - Teoria e Implementação de Linguagens Computacionais + +## Análise Semântica - Tabelas de Símbolos e Type Checking + +### Objetivo + +O objetivo desta aula é consolidar os conceitos relacionados à análise semântica, em particular a noção de tabelas de símbolos e a verificação de tipos usando *visitors*. + +### Questões para Discussão + +- Como usar tabelas de símbolos como estruturas auxiliares para análise semântica? +- Como coletar informações e armazenar ao executar uma travessia da AST com *visitors*? + +### 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/1FEeA9LKLOs4gB9luTpHRHieyqBhpvVKN/view) + - [versão do código ao final da aula (.zip com código escrito durante a aula](https://drive.google.com/file/d/1HOl8xT7f8UkWsTKXFv9LWrzCRFebgD6Y/view) + +### Links Relacionados + +- [Symbol Table](https://en.wikipedia.org/wiki/Symbol_table) +- [Type System](https://en.wikipedia.org/wiki/Type_system) +- [Type Safety](https://en.wikipedia.org/wiki/Type_safety) +- [Strong and Weak Typing](https://en.wikipedia.org/wiki/Strong_and_weak_typing) \ No newline at end of file diff --git a/2023-12-12/astnodes.py b/2023-12-12/astnodes.py new file mode 100644 index 0000000..32d8010 --- /dev/null +++ b/2023-12-12/astnodes.py @@ -0,0 +1,119 @@ +class Program(object): + def __init__(self,name,ls): + self.name = name + self.stmts = ls + +class Stmt(object): + pass + +class PrintStmt(Stmt): + def __init__(self, exp): + self.exp = exp + +class InputStmt(Stmt): + def __init__(self, nome): + self.id = nome + +class VarDeclStmt(Stmt): + def __init__(self, nome, t): + self.id = nome + self.type = t + +class LetStmt(Stmt): + def __init__(self, nome, e): + self.id = nome + self.exp = e + +class BlockStmt(Stmt): + def __init__(self, nome, corpo): + self.name = nome + self.body = corpo + +class IfStmt(Stmt): + def __init__(self, c, corpo): + self.cond = c + self.body = corpo + +class WhileStmt(Stmt): + def __init__(self, c, corpo): + self.cond = c + self.body = corpo + +class Expr(object): + pass + +class IdExpr(Expr): + def __init__(self, nome): + self.id = nome + +class StringExpr(Expr): + def __init__(self, s): + self.str = s + +class ArithExpr(Expr): + pass + +class SumExpr(ArithExpr): + def __init__(self, ladoEsquerdo, ladoDireito): + self.left = ladoEsquerdo + self.right = ladoDireito + +class SubExpr(ArithExpr): + def __init__(self, ladoEsquerdo, ladoDireito): + self.left = ladoEsquerdo + self.right = ladoDireito + +class MulExpr(ArithExpr): + def __init__(self, ladoEsquerdo, ladoDireito): + self.left = ladoEsquerdo + self.right = ladoDireito + +class DivExpr(ArithExpr): + def __init__(self, ladoEsquerdo, ladoDireito): + self.left = ladoEsquerdo + self.right = ladoDireito + +class UnaryPlusExpr(ArithExpr): + def __init__(self, e): + self.exp = e + +class UnaryMinusExpr(ArithExpr): + def __init__(self, e): + self.exp = e + +class NumExpr(ArithExpr): + def __init__(self, valor): + self.v = valor + +class BooleanExpr(Expr): + pass + +class EqualsExpr(BooleanExpr):# left == right + def __init__(self, ladoEsquerdo, ladoDireito): + self.left = ladoEsquerdo + self.right = ladoDireito + +class NotEqualsExpr(BooleanExpr):# left != right + def __init__(self, ladoEsquerdo, ladoDireito): + self.left = ladoEsquerdo + self.right = ladoDireito + +class GreaterThanEqualsExpr(BooleanExpr):# left >= right + def __init__(self, ladoEsquerdo, ladoDireito): + self.left = ladoEsquerdo + self.right = ladoDireito + +class GreaterThanExpr(BooleanExpr):# left > right + def __init__(self, ladoEsquerdo, ladoDireito): + self.left = ladoEsquerdo + self.right = ladoDireito + +class LessThanEqualsExpr(BooleanExpr):# left <= right + def __init__(self, ladoEsquerdo, ladoDireito): + self.left = ladoEsquerdo + self.right = ladoDireito + +class LessThanExpr(BooleanExpr):# left < right + def __init__(self, ladoEsquerdo, ladoDireito): + self.left = ladoEsquerdo + self.right = ladoDireito \ No newline at end of file diff --git a/2023-12-12/basic.py b/2023-12-12/basic.py new file mode 100644 index 0000000..0542937 --- /dev/null +++ b/2023-12-12/basic.py @@ -0,0 +1,35 @@ +from lexer import * +from parse import * +from visitor import * +import sys + +def main(): + if len(sys.argv) != 2: + sys.exit("Erro: Precisamos de um arquivo como argumento.") + with open(sys.argv[1], 'r') as inputFile: + input = inputFile.read() + + lexer = Lexer(input) + parser = Parser(lexer) + program = parser.parse() + + print(program) + # visitor = CountPrint(program) + # print('Antes de contar: '+ str(visitor.numPrint)) + # visitor.contar() + # print('Depois de contar: '+ str(visitor.numPrint)) + + + # visitor = PrintAST(program) + # print(visitor.printAST()) + + # visitor = BuildSymbolTable(program) + # symbolTable = visitor.build() + # print(symbolTable) + + visitor = TypeCheckVisitor(program) + visitor.typecheck() + print(visitor.symbolTable) + + +main() \ No newline at end of file diff --git a/2023-12-12/compare.basic b/2023-12-12/compare.basic new file mode 100644 index 0000000..3cfde61 --- /dev/null +++ b/2023-12-12/compare.basic @@ -0,0 +1,16 @@ +PROGRAM outronome BEGIN + PRINT "Type a number" + DECL num : INT + INPUT num + IF num > 100 THEN + PRINT "Maior que 100" + ENDIF + DECL x : INT + IF num < 100 THEN + PRINT "Menor que 100" + ENDIF + DECL y : INT + IF num == 100 THEN + PRINT "Digitou 100" + ENDIF +ENDPROGRAM \ No newline at end of file diff --git a/2023-12-12/fib.basic b/2023-12-12/fib.basic new file mode 100644 index 0000000..0296572 --- /dev/null +++ b/2023-12-12/fib.basic @@ -0,0 +1,29 @@ +PROGRAM fibonacci BEGIN + PRINT "How many fibonacci numbers do you want?" + DECL nums : INT + INPUT nums + DECL a : INT + DECL b : INT + DECL c : INT + DECL d : INT + DECL e : STRING + DECL f : BOOLEAN + LET a = 0 + LET b = 1 + + WHILE nums > 0 REPEAT + PRINT a + LET c = a + b + LET a = b + LET b = c + LET nums = nums - 1 + ENDWHILE + + PRINT b + BLOCK teste BEGIN + DECL w : STRING + LET w = "Algum valor" + PRINT w + ENDBLOCK + PRINT b +ENDPROGRAM \ No newline at end of file diff --git a/2023-12-12/lexer.py b/2023-12-12/lexer.py new file mode 100644 index 0000000..3175fce --- /dev/null +++ b/2023-12-12/lexer.py @@ -0,0 +1,182 @@ +import enum +import sys + +class Lexer: + def __init__(self, input): + self.codigoFonte = input + '\n' + self.caractereAtual = '' + self.posicaoAtual = -1 + self.nextChar() + + # Processa o proximo caractere + def nextChar(self): + self.posicaoAtual += 1 + if self.posicaoAtual >= len(self.codigoFonte): + self.caractereAtual = '\0' #EOF + else: + self.caractereAtual = self.codigoFonte[self.posicaoAtual] + + # Retorna o caractere seguinte (ainda não lido). + def peek(self): + if self.posicaoAtual + 1 >= len(self.codigoFonte): + return '\0' + return self.codigoFonte[self.posicaoAtual+1] + + # Token inválido encontrado, método usado para imprimir mensagem de erro e encerrar. + def abort(self, message): + sys.exit("Erro léxico! "+message) + + # 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 == '\r': + self.nextChar() + + # Pular comentários. + def skipComment(self): + pass + + # Return o próximo token. + def getToken(self): + self.skipWhitespace() + token = None + + # Olha para o caractere atual e vê se dá pra decidir qual o token + if self.caractereAtual == '+': + token = Token(self.caractereAtual, TokenType.PLUS) #RETORNAR SIMBOLO DE ADICAO + elif self.caractereAtual == '-': + token = Token(self.caractereAtual, TokenType.MINUS) #RETORNAR SIMBOLO DE SUBTRACAO + elif self.caractereAtual == '*': + token = Token(self.caractereAtual, TokenType.ASTERISK) #RETORNAR SIMBOLO DE MULTIPLICACAO + elif self.caractereAtual == '/': + token = Token(self.caractereAtual, TokenType.SLASH) #RETORNAR SIMBOLO DE DIVISAO + elif self.caractereAtual == ':': + token = Token(self.caractereAtual, TokenType.COLON) #RETORNAR SIMBOLO DE DOIS PONTOS - : + elif self.caractereAtual == '\n': + token = Token(self.caractereAtual, TokenType.QUEBRA_LINHA) #RETORNAR SIMBOLO DE QUEBRA DE LINHA + elif self.caractereAtual == '\0': + token = Token(self.caractereAtual, TokenType.EOF) #RETORNAR EOF + elif self.caractereAtual == '=': + if self.peek() == '=': + c = self.caractereAtual + self.nextChar() + token = Token(c + self.caractereAtual, TokenType.EQEQ) # RETORNAR Token '==' + else: + token = Token(self.caractereAtual, TokenType.EQ) # RETORNAR Token '=' + elif self.caractereAtual == '>': + if self.peek() == '=': + c = self.caractereAtual + self.nextChar() + token = Token(c + self.caractereAtual, TokenType.GTEQ) # RETORNAR Token '>=' + else: + token = Token(self.caractereAtual, TokenType.GT) # RETORNAR Token '>' + elif self.caractereAtual == '<': + if self.peek() == '=': + c = self.caractereAtual + self.nextChar() + token = Token(c + self.caractereAtual, TokenType.LTEQ) # RETORNAR Token '<=' + else: + token = Token(self.caractereAtual, TokenType.LT) # RETORNAR Token '<' + elif self.caractereAtual == '!': + if self.peek() == '=': + c = self.caractereAtual + self.nextChar() + token = Token(c + self.caractereAtual, TokenType.NOTEQ) # RETORNAR Token '!=' + else: + token = Token(self.caractereAtual, TokenType.BANG) + elif self.caractereAtual == '\"': + self.nextChar() + posicaoInicial = self.posicaoAtual + while self.caractereAtual != '\"': + if self.caractereAtual == '\r' or self.caractereAtual == '\n' or self.caractereAtual == '\t' or self.caractereAtual == '\\' or self.caractereAtual == '%': + self.abort("Caractere ilegal dentro da string: " + self.caractereAtual) + self.nextChar() + textoString = self.codigoFonte[posicaoInicial : self.posicaoAtual] + token = Token(textoString, TokenType.STRING_LITERAL) + elif self.caractereAtual.isdigit(): + posicaoInicial = self.posicaoAtual + while self.peek().isdigit(): + self.nextChar() + if self.peek() == '.': + self.nextChar() + if not self.peek().isdigit(): + self.abort("Caractere ilegal dentro de um número: "+self.peek()) + while self.peek().isdigit(): + self.nextChar() + + textoNumero = self.codigoFonte[posicaoInicial : self.posicaoAtual + 1] + token = Token(textoNumero, TokenType.NUMERO) + elif self.caractereAtual.isalpha(): + posicaoInicial = self.posicaoAtual + while self.peek().isalnum(): + self.nextChar() + + textoToken = self.codigoFonte[posicaoInicial : self.posicaoAtual + 1] + tipoToken = Token.checkIfKeyword(textoToken) + if tipoToken == None: + token = Token(textoToken, TokenType.IDENTIFICADOR) + else: + token = Token(textoToken, tipoToken) + + else: + self.abort("Token desconhecido iniciado com o caractere: " + self.caractereAtual) + + self.nextChar() + return token + +class Token: + def __init__(self, lexema, tipo):#Token(23,NUMBER) + self.lexema = lexema + self.tipo = tipo + + @staticmethod + def checkIfKeyword(lexema): + for kind in TokenType: + if kind.name == lexema and kind.value >= 100 and kind.value <200: + return kind + return None + +class TokenType(enum.Enum): + EOF = -1 + QUEBRA_LINHA = 0 + NUMERO = 1 + IDENTIFICADOR = 2 + STRING_LITERAL = 3 + COLON = 4 # : + L_PAREN = 5 + R_PAREN = 6 + #PALAVRAS RESERVADAS + LABEL = 101 + GOTO = 102 + PRINT = 103 + INPUT = 104 + LET = 105 + IF = 106 + THEN = 107 + ENDIF = 108 + WHILE = 109 + REPEAT = 110 + ENDWHILE = 111 + DECL = 112 + INT = 113 + BOOLEAN = 114 + TRUE = 115 + FALSE = 116 + STRING = 117 + PROGRAM = 118 + ENDPROGRAM = 119 + BLOCK = 120 + BEGIN = 121 + ENDBLOCK = 122 + #OPERADORES + EQ = 201 + PLUS = 202 + MINUS = 203 + ASTERISK = 204 + SLASH = 205 + EQEQ = 206 + NOTEQ = 207 + LT = 208 + LTEQ = 209 + GT = 210 + GTEQ = 211 + BANG = 212 # ! \ No newline at end of file diff --git a/2023-12-12/manual_symboltable.py b/2023-12-12/manual_symboltable.py new file mode 100644 index 0000000..edfbf88 --- /dev/null +++ b/2023-12-12/manual_symboltable.py @@ -0,0 +1,18 @@ +from lexer import * +from parse import * +from visitor import * +from symboltable import * +import sys + +def main(): + symbolTable = SymbolTable() + print(symbolTable) + INT = symbolTable.lookup('INT') + + symbolTable.insert('y', VarSymbol('y', INT)) + symbolTable.insert('x', VarSymbol('x', INT)) + print(symbolTable) + + + +main() \ No newline at end of file diff --git a/2023-12-12/manualast.py b/2023-12-12/manualast.py new file mode 100644 index 0000000..643c053 --- /dev/null +++ b/2023-12-12/manualast.py @@ -0,0 +1,32 @@ +from lexer import * +from parse import * +from visitor import * +import sys + +def main(): + program = Program("simples", [PrintStmt(StringExpr("Hello World")),PrintStmt(StringExpr("Outro hello"))]) + + # program = Program( + # "simples", + # [ + # PrintStmt( + # StringExpr( + # "Hello World" + # ) + # ) + # , + # PrintStmt( + # StringExpr( + # "Outro hello" + # ) + # ) + # ] + # ) + print(program) + visitor = CountPrint(program) + print('Antes de contar: '+ str(visitor.numPrint)) + visitor.contar() + print('Depois de contar: '+ str(visitor.numPrint)) + + +main() \ No newline at end of file diff --git a/2023-12-12/numbersize.basic b/2023-12-12/numbersize.basic new file mode 100644 index 0000000..2b562b5 --- /dev/null +++ b/2023-12-12/numbersize.basic @@ -0,0 +1,21 @@ +PROGRAM NumberSize BEGIN +PRINT "Type a number" +DECL num : INT +INPUT num +DECL c1 : BOOLEAN +DECL c2 : BOOLEAN +DECL c3 : BOOLEAN +LET c1 = num>100 +LET c2 = num<1000 +LET c3 = num>500 + +IF c1 THEN + PRINT "Maior que 100" + IF c2 THEN + PRINT "Menor que 1000" + IF c3 THEN + PRINT "Maior que 500" + ENDIF + ENDIF +ENDIF +ENDPROGRAM \ No newline at end of file diff --git a/2023-12-12/parse.py b/2023-12-12/parse.py new file mode 100644 index 0000000..fa47fc2 --- /dev/null +++ b/2023-12-12/parse.py @@ -0,0 +1,233 @@ +import sys +from lexer import * +from astnodes import * + +class Parser: + def __init__(self, lexer): + self.lexer = lexer + self.tokenAtual = None + self.proximoToken = None + self.nextToken() + self.nextToken() + + #Retorna true se o Token **atual** casa com tipo de Token esperado + def checkToken(self, tipo): + return tipo == self.tokenAtual.tipo + + #Retorna true se o próximo Token **(peek)** casa com tipo de Token esperado + def checkPeek(self, tipo): + return tipo == self.proximoToken.tipo + + #Tenta fazer o casamento do Token atual. Se conseguir, avança para o próximo Token. Do contrário, gera mensagem de erro. + def match(self, tipo): + if not self.checkToken(tipo): + self.abort("Esperava por token do tipo " + tipo.name + ", mas apareceu " + self.tokenAtual.tipo.name) + else: + self.nextToken() + + # Avançando com os ponteiros dos tokens (atual e peek) + def nextToken(self): + self.tokenAtual = self.proximoToken + self.proximoToken = self.lexer.getToken() + + def abort(self, msg): + sys.exit("Erro sintático: "+msg) + + def parse(self): + return self.program() + + # program ::= statement + def program(self): + stmts = [] + self.match(TokenType.PROGRAM) + nomePrograma = self.tokenAtual.lexema + self.match(TokenType.IDENTIFICADOR) + self.match(TokenType.BEGIN) + self.nl() + while not self.checkToken(TokenType.ENDPROGRAM): + stmts.append(self.statement()) + self.match(TokenType.ENDPROGRAM) + self.nl() + self.match(TokenType.EOF) + return Program(nomePrograma, stmts) + + # statement ::= + # PRINT expression nl | + # INPUT IDENTIFICADOR nl | + # DECL IDENTIFICADOR ":" (INT | BOOLEAN | STRING) nl | + # LET IDENTIFICADOR "=" expression nl | + # BLOCK IDENT BEGIN nl {statement} ENDBLOCK nl | + # WHILE expression REPEAT nl {statement} ENDWHILE nl | + # IF expression "THEN" nl {statement} ENDIF nl + def statement(self): + stm = None + # PRINT expression nl | + if self.checkToken(TokenType.PRINT): + self.match(TokenType.PRINT) + e = self.expression() + stm = PrintStmt(e) + # INPUT IDENTIFICADOR nl | + elif self.checkToken(TokenType.INPUT): + self.match(TokenType.INPUT) + nome = self.tokenAtual.lexema + self.match(TokenType.IDENTIFICADOR) + stm = InputStmt(nome) + # DECL IDENTIFICADOR ":" TYPE nl | + elif self.checkToken(TokenType.DECL): + self.match(TokenType.DECL) + nome = self.tokenAtual.lexema + self.match(TokenType.IDENTIFICADOR) + self.match(TokenType.COLON) + if self.checkToken(TokenType.INT) or self.checkToken(TokenType.BOOLEAN) or self.checkToken(TokenType.STRING): + tipo = self.tokenAtual.lexema + self.nextToken() + else: + self.abort('Esperava por um tipo válido dentre INT, BOOLEAN, ou STRING, recebeu ' + self.tokenAtual.lexema) + stm = VarDeclStmt(nome,tipo) + # LET IDENTIFICADOR "=" expression nl | + elif self.checkToken(TokenType.LET): + self.match(TokenType.LET) + nome = self.tokenAtual.lexema + self.match(TokenType.IDENTIFICADOR) + self.match(TokenType.EQ) + e = self.expression() + stm = LetStmt(nome,e) + # BLOCK IDENT BEGIN nl {statement} ENDBLOCK nl | + elif self.checkToken(TokenType.BLOCK): + self.match(TokenType.BLOCK) + nome = self.tokenAtual.lexema + self.match(TokenType.IDENTIFICADOR) + self.match(TokenType.BEGIN) + self.nl() + body = [] + while not self.checkToken(TokenType.ENDBLOCK): + body.append(self.statement()) + self.match(TokenType.ENDBLOCK) + stm = BlockStmt(nome,body) + # WHILE expression REPEAT nl {statement} ENDWHILE nl | + elif self.checkToken(TokenType.WHILE): + self.match(TokenType.WHILE) + cond = self.expression() + self.match(TokenType.REPEAT) + self.nl() + body = [] + while not self.checkToken(TokenType.ENDWHILE): + body.append(self.statement()) + self.match(TokenType.ENDWHILE) + stm = WhileStmt(cond,body) + # IF expression "THEN" nl {statement} ENDIF nl + elif self.checkToken(TokenType.IF): + self.match(TokenType.IF) + cond = self.expression() + self.match(TokenType.THEN) + self.nl() + body = [] + while not self.checkToken(TokenType.ENDIF): + body.append(self.statement()) + self.match(TokenType.ENDIF) + stm = IfStmt(cond, body) + else: + self.abort("Problema com " + self.tokenAtual.lexema + " (" + self.tokenAtual.tipo.name + ")") + self.nl() + return stm + + def nl(self): + self.match(TokenType.QUEBRA_LINHA) + while self.checkToken(TokenType.QUEBRA_LINHA): + self.match(TokenType.QUEBRA_LINHA) + + # expression ::== equality + def expression(self): + return self.equality() + # equality ::== comparison ( ("==" | "!=" ) comparison)* + def equality(self): + e = self.comparison() + while self.checkToken(TokenType.EQEQ) or self.checkToken(TokenType.NOTEQ): + if self.checkToken(TokenType.EQEQ): + self.match(TokenType.EQEQ) + tmp = self.comparison() + e = EqualsExpr(e,tmp) + elif self.checkToken(TokenType.NOTEQ): + self.match(TokenType.NOTEQ) + tmp = self.comparison() + e = NotEqualsExpr(e,tmp) + return e + # comparison ::== term ( ("<" | "<=" | ">" | ">=" ) term)* + def comparison(self): + e = self.term() + while self.checkToken(TokenType.LT) or self.checkToken(TokenType.LTEQ) or self.checkToken(TokenType.GT) or self.checkToken(TokenType.GTEQ): + if self.checkToken(TokenType.LT): + self.match(TokenType.LT) + tmp = self.term() + e = LessThanExpr(e,tmp) + elif self.checkToken(TokenType.LTEQ): + self.match(TokenType.LTEQ) + tmp = self.term() + e = LessThanEqualsExpr(e,tmp) + elif self.checkToken(TokenType.GT): + self.match(TokenType.GT) + tmp = self.term() + e = GreaterThanExpr(e,tmp) + elif self.checkToken(TokenType.GTEQ): + self.match(TokenType.GTEQ) + tmp = self.term() + e = GreaterThanEqualsExpr(e,tmp) + return e + + # term ::== factor {("-" | "+") factor} + def term(self): + e = self.factor() + while self.checkToken(TokenType.MINUS) or self.checkToken(TokenType.PLUS): + if self.checkToken(TokenType.MINUS): + self.match(TokenType.MINUS) + tmp = self.factor() + e = SubExpr(e,tmp) + elif self.checkToken(TokenType.PLUS): + self.match(TokenType.PLUS) + tmp = self.factor() + e = SumExpr(e,tmp) + return e + # factor ::== unary {("*" | "/") unary} + def factor(self): + e = self.unary() + while self.checkToken(TokenType.ASTERISK) or self.checkToken(TokenType.SLASH): + if self.checkToken(TokenType.ASTERISK): + self.match(TokenType.ASTERISK) + tmp = self.unary() + e = MulExpr(e,tmp) + elif self.checkToken(TokenType.SLASH): + self.match(TokenType.SLASH) + tmp = self.unary() + e = DivExpr(e,tmp) + return e + # unary ::== ["-" | "+" ] unary | primary + def unary(self): + e = None + if self.checkToken(TokenType.MINUS): + self.match(TokenType.MINUS) + e = UnaryMinusExpr(self.unary()) + elif self.checkToken(TokenType.PLUS): + self.match(TokenType.PLUS) + e = UnaryPlusExpr(self.unary()) + else: + e = self.primary() + return e + # primary ::== NUM | ID | STRING + def primary(self): + e = None + if self.checkToken(TokenType.NUMERO): + str_numero = self.tokenAtual.lexema + val_numero = int(str_numero) + e = NumExpr(val_numero) + self.match(TokenType.NUMERO) + elif self.checkToken(TokenType.IDENTIFICADOR): + nome_var = self.tokenAtual.lexema + e = IdExpr(nome_var) + self.match(TokenType.IDENTIFICADOR) + elif self.checkToken(TokenType.STRING_LITERAL): + valor_string = self.tokenAtual.lexema + e = StringExpr(valor_string) + self.match(TokenType.STRING_LITERAL) + else: + self.abort("Token inesperado, esperava por NUMERO, IDENTIFICADOR, ou STRING_LITERAL, apareceu: " + self.tokenAtual.tipo.name + " (" + self.tokenAtual.lexema + ")") + return e \ No newline at end of file diff --git a/2023-12-12/simples.basic b/2023-12-12/simples.basic new file mode 100644 index 0000000..66ec4e9 --- /dev/null +++ b/2023-12-12/simples.basic @@ -0,0 +1,9 @@ +PROGRAM simples BEGIN + DECL x : STRING + INPUT x + DECL y : INT + LET y = 500+2 + IF y>100 THEN + LET y = 2 + ENDIF +ENDPROGRAM \ No newline at end of file diff --git a/2023-12-12/symboltable.py b/2023-12-12/symboltable.py new file mode 100644 index 0000000..bc6bfe4 --- /dev/null +++ b/2023-12-12/symboltable.py @@ -0,0 +1,55 @@ +class Symbol(object): + def __init__(self, n, t=None, v=None): + self.name = n + self.type = t + self.value = v + +class BuiltInTypeSymbol(Symbol): + def __init__(self, n): + super().__init__(n) + + def __str__(self): + return '<{n}>'.format(n=self.name) + + __repr__ = __str__ + +class VarSymbol(Symbol): + def __init__(self, n, t, v=None): + super().__init__(n, t, v=v) + + def __str__(self): + return '<{n} : {t} = {v}>'.format(n=self.name, t=self.type, v=self.value) + + __repr__ = __str__ + +class SymbolTable(object): + def __init__(self): + self.symbols = {} + for primitive in ['INT', 'BOOLEAN', 'STRING']: + self.insert(primitive, BuiltInTypeSymbol(primitive)) + + def insert(self, name, data): + self.symbols[name] = data + + #Retorna um objeto Symbol, se houver algo associado a esta chave (name) ou None + def lookup(self, name): + return self.symbols.get(name) + + def update(self, name, data): + if self.lookup(name) is None: + raise Exception('Símbolo inexistente: '+ name) + else: + self.insert(name,data) + + def __str__(self): + symtab_header = ' Symbol Table ' + lines = ['\n', symtab_header, '_' * len(symtab_header)] + lines.extend( + ('%2s: %r' % (key, value)) + for key, value in self.symbols.items() + ) + lines.append('\n') + s = '\n'.join(lines) + return s + + __repr__ = __str__ \ No newline at end of file diff --git a/2023-12-12/teste.basic b/2023-12-12/teste.basic new file mode 100644 index 0000000..76b423a --- /dev/null +++ b/2023-12-12/teste.basic @@ -0,0 +1,41 @@ +PROGRAM teste BEGIN +DECL y : INT +LET y = 500 +PRINT y>=0 +DECL x : INT + +INPUT x + +DECL m : INT +LET m = x+2*3 +DECL b : BOOLEAN +LET b = x > 12 + 12 +DECL s : STRING +LET s = "oi" + +PRINT 1*y + +PRINT "w" +DECL z : INT +DECL w : INT +DECL u : STRING + +IF x+2 > 10 THEN + + WHILE z == w REPEAT + PRINT z + IF w > 0 THEN + LET w = w-1 + ENDIF + LET z = z+1 + ENDWHILE + + PRINT x + + INPUT u + +ENDIF + +PRINT x+2 + +ENDPROGRAM \ No newline at end of file diff --git a/2023-12-12/visitor.py b/2023-12-12/visitor.py new file mode 100644 index 0000000..381e442 --- /dev/null +++ b/2023-12-12/visitor.py @@ -0,0 +1,212 @@ +from symboltable import * + +class NodeVisitor(object): + def visit(self, node): + method_name = 'visit_' + type(node).__name__ + visitor = getattr(self, method_name, self.generic_visit) + return visitor(node) + + def generic_visit(self, node): + raise Exception('No visit_{} method'.format(type(node).__name__)) + +class GenericVisitor(NodeVisitor): + def __init__(self,tree): + self.ast = tree + + def traverse(self): + self.visit(self.ast) + def erro(self,msg): + raise Exception(msg) + + def visit_Program(self,node): + for stmt in node.stmts: + self.visit(stmt) + def visit_LetStmt(self, node): + self.visit(node.exp) + def visit_VarDeclStmt(self,node): + pass + def visit_PrintStmt(self,node): + self.visit(node.exp) + def visit_InputStmt(self,node): + pass + def visit_BlockStmt(self,node): + for stm in node.body: + self.visit(stm) + def visit_WhileStmt(self,node): + self.visit(node.cond) + for stm in node.body: + self.visit(stm) + def visit_IfStmt(self,node): + self.visit(node.cond) + for stm in node.body: + self.visit(stm) + + def visit_NumExpr(self, node): + pass + def visit_IdExpr(self,node): + pass + def visit_StringExpr(self, node): + pass + def visit_GreaterThanExpr(self,node): + self.visit(node.left) + self.visit(node.right) + def visit_GreaterThanEqualsExpr(self,node): + self.visit(node.left) + self.visit(node.right) + def visit_LessThanExpr(self,node): + self.visit(node.left) + self.visit(node.right) + def visit_LessThanEqualsExpr(self,node): + self.visit(node.left) + self.visit(node.right) + def visit_EqualsExpr(self,node): + self.visit(node.left) + self.visit(node.right) + def visit_NotEqualsExpr(self,node): + self.visit(node.left) + self.visit(node.right) + def visit_UnaryPlusExpr(self,node): + self.visit(node.exp) + def visit_UnaryMinusExpr(self,node): + self.visit(node.exp) + def visit_SumExpr(self,node): + self.visit(node.left) + self.visit(node.right) + def visit_SubExpr(self,node): + self.visit(node.left) + self.visit(node.right) + def visit_MulExpr(self,node): + self.visit(node.left) + self.visit(node.right) + def visit_DivExpr(self,node): + self.visit(node.left) + self.visit(node.right) + +class CountVars(GenericVisitor): + def __init__(self, tree): + super().__init__(tree) + self.numVars = 0 + + def visit_VarDeclStmt(self, node): + self.numVars += 1 + +class CountPrint(GenericVisitor): + def __init__(self, tree): + super().__init__(tree) + self.numPrint = 0 + + def contar(self): + self.numPrint = 0 + super().traverse() + return self.numPrint + + def visit_PrintStmt(self, node): + self.numPrint += 1 + + +class PrintAST(GenericVisitor): + def __init__(self, tree): + super().__init__(tree) + self.result = '' + + def printAST(self): + super().traverse() + return self.result + +#Program(simples, [PrintStmt(StringExpr("Hello World"))PrintStmt(StringExpr("Outro hello"))]) + def visit_Program(self, node): + self.result = 'Program("'+node.name+'", [' + for stmt in node.stmts: + self.result += self.visit(stmt)+',' + self.result = self.result[:-1] + self.result += '])' + + def visit_PrintStmt(self, node): + return 'PrintStmt('+self.visit(node.exp)+')' + + def visit_StringExpr(self, node): + return 'StringExpr("'+node.str+'")' + + +class BuildSymbolTable(GenericVisitor): + def __init__(self, tree): + super().__init__(tree) + self.symbolTable = SymbolTable() + + def build(self): + self.traverse() + return self.symbolTable + + def visit_VarDeclStmt(self, node): + nomeVariavel = node.id + if self.symbolTable.lookup(nomeVariavel) is None: + tipoVariavel = self.symbolTable.lookup(node.type) + simbolo = VarSymbol(nomeVariavel, tipoVariavel) + self.symbolTable.insert(nomeVariavel, simbolo) + else: + self.erro('Variável já foi declarada') + +class TypeCheckVisitor(GenericVisitor): + def __init__(self, tree): + super().__init__(tree) + visitor = BuildSymbolTable(tree) + self.symbolTable = visitor.build() + + def typecheck(self): + self.traverse() + return True + + def BOOLEAN(self): + return self.symbolTable.lookup('BOOLEAN') + def INT(self): + return self.symbolTable.lookup('INT') + def STRING(self): + return self.symbolTable.lookup('STRING') + + def visit_LetStmt(self, node): + nomeVariavel = node.id + simbolo = self.symbolTable.lookup(nomeVariavel) + if simbolo is None: + self.erro("Variável não declarada: %s" % node.id) + else: + tipoVariavel = simbolo.type + tipoExpressao = self.visit(node.exp) + if tipoVariavel != tipoExpressao: + self.erro("Variável não compatível com expressão") + + def visit_StringExpr(self, node): + return self.STRING() + + def visit_NumExpr(self, node): + return self.INT() + + def visit_IdExpr(self, node): + simbolo = self.symbolTable.lookup(node.id) + if simbolo is None: + self.erro("Variável não declarada: %s" % node.id) + else: + return simbolo.type + + def visit_SumExpr(self, node): + tipoEsquerda = self.visit(node.left) + tipoDireita = self.visit(node.right) + if tipoEsquerda == self.INT() and tipoDireita == self.INT(): + return self.INT() + + def visit_IfStmt(self, node): + tipoCondicao = self.visit(node.cond) + if tipoCondicao == self.BOOLEAN(): + for stm in node.body: + self.visit(stm) + else: + self.erro("Condição do IF deve ser booleana") + + def visit_GreaterThanExpr(self, node): + tipoEsquerda = self.visit(node.left) + tipoDireita = self.visit(node.right) + if tipoEsquerda == tipoDireita: + return self.BOOLEAN() + + def visit_InputStmt(self, node): + if self.symbolTable.lookup(node.id) is None: + self.erro("Variável não declarada: %s" % node.id) diff --git a/2024-01-30.md b/2024-01-30.md new file mode 100644 index 0000000..eaf5823 --- /dev/null +++ b/2024-01-30.md @@ -0,0 +1,29 @@ +# IF688 - Teoria e Implementação de Linguagens Computacionais + +## Resolução da Prova e Introdução a Representações Intermediárias de Código + +### Objetivo + +O objetivo desta aula é resolver a prova e tirar dúvidas relacionadas à mesma e apresentar o conceito de representações intermediárias de código, discutindo as vantagens de utilizar este tipo de representação e quais as questões associadas ao seu uso. + +### Questões para Discussão + +- Qual a principal vantagem de utilizar uma representação intermediária de código no processo de compilação? +- Que informações devem ser guardadas e de que forma, durante o processo de síntese? +- Como podemos classificar os diferentes tipos de representação intermediária de código? + +### Material usado em sala de aula + +- [Slides](https://drive.google.com/file/d/18OPTOFClyiSvEy-NvAgh3OsT9PbGT9L5/view?usp=drive_open) + +### Vídeos + +- [Representações Intermediárias de Código - Introdução](https://www.youtube.com/watch?v=DE9dcfDLQ2U&list=PLHoVp5NAbKJb6W0Om65rzUgCbT2bvvhzf&index=1) +- [Representações Gráficas de Código](https://www.youtube.com/watch?v=D_kn_P0jxFA&list=PLHoVp5NAbKJb6W0Om65rzUgCbT2bvvhzf&index=2) +- [Representações Lineares de Código](https://www.youtube.com/watch?v=OlSgNM7k6Vc&list=PLHoVp5NAbKJb6W0Om65rzUgCbT2bvvhzf&index=3) + +### Links Relacionados + +- [Intermediate representation](https://en.wikipedia.org/wiki/Intermediate_representation) +- [Bytecode](https://en.wikipedia.org/wiki/Bytecode) +- [Three-address code](https://en.wikipedia.org/wiki/Three-address_code) \ No newline at end of file diff --git a/2024-02-01.md b/2024-02-01.md new file mode 100644 index 0000000..360eb23 --- /dev/null +++ b/2024-02-01.md @@ -0,0 +1,35 @@ +# IF688 - Teoria e Implementação de Linguagens Computacionais + +## Representações Intermediárias de Código - Código de 3 endereços e *Control-flow Graphs* + +### Objetivo + +O objetivo desta aula é apresentar o conceito de representações intermediárias de código por meio de exemplos, com foco particular em código de três endereços e _control-flow graphs_. + +### Questões para Discussão + +- Como representar programas simples em código de três endereços? +- Qual a principal vantagem de utilizar grafos ao invés de árvores como representação intermediária de código? +- O que são blocos básicos de código? +- Como dividir código em blocos básicos? +- Como construir um _control-flow graph_ recursivamente? +- Como construir um _control-flow graph_ eficientemente a partir da definição de blocos básicos? +- Quais outras representações visuais de código podem se beneficiar da estrutura de grafos? + +### Material usado em sala de aula + +- [Slides](https://drive.google.com/file/d/1D2GWEMNj3IhtueqvI8hwxb14a2klBoq8/view) + +### Vídeos + +- [Gerando Representações Lineares de Código](https://www.youtube.com/watch?v=T69WHa90QQs&list=PLHoVp5NAbKJb6W0Om65rzUgCbT2bvvhzf&index=4) +- [Representações Intermediárias de Código - Control-Flow Graphs](https://www.youtube.com/watch?v=2BlBPc11PLs&list=PLHoVp5NAbKJb6W0Om65rzUgCbT2bvvhzf&index=5) +- [Construindo Control-Flow Graphs a partir de Código de 3 Endereços](https://www.youtube.com/watch?v=lGx9drsIz-g&list=PLHoVp5NAbKJb6W0Om65rzUgCbT2bvvhzf&index=6) + +### Links Relacionados + +- [Three-address code](https://en.wikipedia.org/wiki/Three-address_code) +- [Control-flow graph](https://en.wikipedia.org/wiki/Control_flow_graph) +- [Dependency graph](https://en.wikipedia.org/wiki/Dependency_graph) +- [Program Dependence Graph](https://en.wikipedia.org/wiki/Program_Dependence_Graph) +- [Call graph](https://en.wikipedia.org/wiki/Call_graph) \ No newline at end of file diff --git a/README.md b/README.md index cc5c724..2e1ed65 100644 --- a/README.md +++ b/README.md @@ -87,13 +87,13 @@ Na disciplina, utilizaremos uma mistura de aulas tradicionais com exercícios e | 21.11.23 | Terça | [Análise Sintática Bottom-Up - LR(1) Parsing](2023-11-21.md) | | | 23.11.23 | Quinta | [Análise Semântica - Intro e ASTs](2023-11-23.md) | | | 28.11.23 | Terça | [Análise Semântica - ASTs e Visitors](2023-11-28.md) | | -| 30.11.23 | Quinta | [Análise Semântica - Tipos e Escopo](2023-11-30.md) | | +| 30.11.23 | Quinta | [Análise Semântica - Tipos, Escopo, Tabelas de Símbolos](2023-11-30.md) | | | 05.12.23 | Terça | APS - Exercícios | | | 07.12.23 | Quinta | APS - Exercícios | | -| 12.12.23 | Terça | [Análise Semântica - Tabelas de Símbolos](#) | | -| 14.12.23 | Quinta | Análise Semântica | | -| 19.12.23 | Terça | **1 Exercício Escolar** | | -| 21.12.23 | Quinta | APS - Exercícios | | +| 12.12.23 | Terça | [Análise Semântica - Tabelas de Símbolos e Type Checking](2023-12-12.md) | | +| 14.12.23 | Quinta | Revisão e tirar dúvidas | | +| 19.12.23 | Terça | Revisão e tirar dúvidas | *Prova remarcada por conta da chuva* | +| 21.12.23 | Quinta | **1 Exercício Escolar** | | | 26.12.23 | Terça | **Recesso Escolar** | --- | | 28.12.23 | Quinta | **Recesso Escolar** | --- | | 02.01.24 | Terça | **Recesso Escolar** | --- | @@ -104,8 +104,8 @@ Na disciplina, utilizaremos uma mistura de aulas tradicionais com exercícios e | 18.01.24 | Quinta | **Recesso Escolar** | --- | | 23.01.24 | Terça | **Recesso Escolar** | --- | | 25.01.24 | Quinta | **Recesso Escolar** | --- | -| 30.01.24 | Terça | Representação Intermediária de Código | | -| 01.02.24 | Quinta | Representação Intermediária de Código | | +| 30.01.24 | Terça | [Representação Intermediária de Código](2024-01-30.md) | | +| 01.02.24 | Quinta | [Representação Intermediária de Código](2024-01-30.md) | | | 06.02.24 | Terça | APS | | | 08.02.24 | Quinta | APS | | | 13.02.24 | Terça | **CARNAVAL** | | @@ -118,6 +118,6 @@ Na disciplina, utilizaremos uma mistura de aulas tradicionais com exercícios e | 07.03.24 | Quinta | Geração de Código | | | 12.03.24 | Terça | Revisão | | | 14.03.24 | Quinta | **2 Exercício Escolar** | | -| 19.03.24 | Terça | Tira-dúvidas | | -| 21.03.24 | Quinta | **Segunda Chamada** | | -| 28.03.24 | Quinta | **Prova Final** | | \ No newline at end of file +| 19.03.24 | Terça | **Segunda Chamada** | | +| 21.03.24 | Quinta | **Prova Final** | | +| 28.03.24 | Quinta | Divulgação notas | | \ No newline at end of file