Skip to content

Commit

Permalink
🔀 Merge branch refactor-automata
Browse files Browse the repository at this point in the history
Refactor automata
  • Loading branch information
DanielSRS authored Oct 9, 2022
2 parents 7aace23 + b9f43aa commit 7df8d46
Show file tree
Hide file tree
Showing 19 changed files with 514 additions and 342 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ saida
saidas_dos_testes

# arquivos de cache do python
src/__pycache__
*.pyc
137 changes: 136 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,136 @@
# Compiladores


<!-- PROJECT LOGO -->
## Sobre o projeto
Um analizador léxico para uma pseudo linguagem de programação escrito em python.

A entrada para este analisador é um conjunto de arquivos texto que será processado de acordo com a estrutur léxica da linguagem e dará como saída um conjunto de arquivos de saída apresentando a lista de tokens, proveniente da análise léxica, além dos erros léxicos, caso existam.
<br />

<br>

<!-- TABLE OF CONTENTS -->

## Tabela de Conteúdo

- [Sobre o Projeto](#sobre-o-projeto)
- [Tabela de Conteúdo](#tabela-de-conte%C3%BAdo)
- [Feito Com](#feito-com)
- [Começando](#come%C3%A7ando)
- [Pré-requisitos](#pr%C3%A9-requisitos)
- [Estrutura de Arquivos](#estrutura-de-arquivos)
- [Instalação](#instala%C3%A7%C3%A3o)
- [Linting](#edi%C3%A7%C3%A3o)
- [Edição](#edi%C3%A7%C3%A3o)
- [Executar projeto](#executar-projeto)
- [Contribuição](#contribui%C3%A7%C3%A3o)

<!-- ABOUT THE PROJECT -->

<br>

## Feito Com

Abaixo segue o que foi utilizado na criação deste projeto:

- [Python](https://www.python.org/) - Python é uma linguagem de programação que permite trabalhar rapidamente
e integrar os sistemas de forma mais eficaz.

<br>

<!-- GETTING STARTED -->

## Começando

Para conseguir rodar o projeto, siga os passos abaixo.

### Pré-requisitos

Antes de seguirmos, é preciso que você tenha o ambiente configurado para criar e testar aplicações em Python. Caso não tenha o python3 instalado na sua maquina, verifique como pode instalar na sua plataforma seguindo as instruções disponíveis na pagina do projeto: [Python.org](https://www.python.org/)

### Estrutura de Arquivos

A estrutura de arquivos está da seguinte maneira:

```bash
Compiladores
├── .vscode/
├── entrada/
├── saida/
├── src/
│ ├── automata/
│ │ └── automataName.py
│ ├── TokenUtils/
│ │ └── utilityName.py
│ ├── filesystem.py
│ └── main.py
├── .gitignore
└── README.md
```

Serão explicados os arquivos e diretórios na seção de [Edição](#edição).

### Instalação

1. Clone o projeto utilizando o comando:

```sh
$ git clone https://github.com/DanielSRS/Compiladores
```

2. Navegue para o diretorio raiz do projeto e crie as pastas nescessárias para execução com o camando:

```sh
$ cd Compiladores
$ mkdir entrada
$ mkdir saida
```

### Linting
O codigo do projeto é tipado. Esta etapa não é nescessária, mas para ter uma melhor experiencia habilite linting no seu editor de preferencia, e defina a verificação de tipos como 'strict'
<br>
<br>
Se você usa o Visual Studio Code como editor não precisa fazer nada.

<br>

### Edição

Nesta seção haverão instruções caso você queira editar o projeto, explicando para que os diretórios são utilizados e também os arquivos de configuração.

- **.vscode** - Arquivos de configuração do Visual Studio Code. Esses arquivos não são nescessarios caso você não use o VS Code como editor. São apenas as configurações descritas nas seção de [Linting](#linting).

- **entrada** - Diretório contendo todos os arquivos fonte que irão ser processdos pelo analizador léxido. Se não houver nenhum arquivo, não será produzido nenhum arquivo de saíde após execução. Se diretório estiver ausente, um erro acontecerá ao executar o projeto.

- **saida** - Após execução do projeto, o analizador léxico irá gerar arquivos de saída neste diretório contendo as informações processadas em cada arquivo de entrada.

- **src** - Diretório contendo todos os arquivos da aplicação, é criado um diretório `src` para que o código da aplicação possa ser isolado em um diretório e facilmente portado para outros projetos, se necessário.

- **automata** - A python package onde estão agrupados todos os automatos para processamento de lexemas e funções relacionadas

- **tokenUtils** - A python package onde estão agrupados todos modulos para geração, processamento e manipulação de tokens além de funções relacionadas;

- **main.py** - Arquivo responsável por centralizar o código do diretório `src`, aqui são realizadas as operções principais de abertura leitura dos arquivos de codigo fonte (presentes no arquivo de entrda) e gravação da lista de token nos arquivos de saída (no diretório 'saida').

- **filesystem.py** - Operações relacionadas ao sistema de arquivos, como a abertura e leitura de arquivos;


- **.gitignore** - Arquivo de configurção do git contendo informções de arquivos que não devem ser versionados junto com o codigo fonte;

- **README.md** - Este arquivo. Aqui é feito a documentação basica do projeto com instruções de instalação, configuração e execução.

## Executar projeto

- Ainda no diretório raiz:

```sh
$ python3 src/main.py
```
- Varifique a saida no diretório 'saida'

<br>

## Contribuição

- Quaisquer dúvidas, sugestões ou problemas que encontrar, fique livre para abrir uma issue.
- Se quiser contribuir ajustando o codigo, implementando novas funcionalidas ou corrigindo bugs, faça um fork do projeto, faça as alterações nescessárias como descrito na seção de [Edição](#edição) e abra um pull request
8 changes: 8 additions & 0 deletions src/TokenUtils/Token.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from typing import NamedTuple

class Token(NamedTuple):
token: str;
line: int;
tokenStartIndex: int;
tokenEndIndex: int;
value: str;
28 changes: 28 additions & 0 deletions src/TokenUtils/generateTokenFromState.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from TokenUtils.Token import Token
from TokenUtils.getTokenTypeFromState import getTokenTypeFromState

# verifica se um caractere é uma palavra reservada
def isReserved(identifier: str) -> bool:
reserved = {'var', 'const', 'struct', 'extends', 'procedure', 'function', 'start', 'return', 'if',
'else', 'then', 'while', 'read', 'print', 'int', 'real', 'boolean', 'string', 'true', 'false'}
if identifier in reserved:
return True
return False

def hasNonASCII(s: str):
count = 0;
for char in s:
if (ord(char) < 32 or ord(char) > 126):
count = count + 1;
if (count > 0):
return True;
return False

def generateTokenFromState(state: str, lineNumber: int, lineText: str, tokenStartIndex: int, tokenEndIndex: int) -> Token:
tokenType = getTokenTypeFromState(state);
tokenText = lineText[tokenStartIndex:tokenEndIndex];
if (tokenType == 'IDE'):
tokenType = 'PRE' if isReserved(tokenText) else 'IDE';
if (tokenType == 'CAC'):
tokenType = 'CMF' if hasNonASCII(tokenText) else 'CAC';
return Token(tokenType, lineNumber, tokenStartIndex, tokenEndIndex, tokenText);
18 changes: 18 additions & 0 deletions src/TokenUtils/getTokenTypeFromState.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
def getTokenTypeFromState(state: str):
stateToTokenType = {
'DelimiterFinal': 'DEL',
'MalformedStringFinal': 'CMF',
'StringFinal': 'CAC',
'LineCommentFinal': 'COM',
'BlockCommentFinal': 'CMB',
'ArithmeticFinal': 'ART',
'LogicalOperatorFinal': 'LOG',
'MalformedTokenFinal': 'TMF',
'RelationalOperatorFinal': 'REL',
'ArithmeticOperatorFinal': 'ART',
'NumberFinal': 'NRO',
'MalformedNumber': 'NMF',
'MalformedNumberFinal': 'NMF',
'IdentifierFinal': 'IDE',
}
return stateToTokenType.get(state, 'None');
14 changes: 14 additions & 0 deletions src/TokenUtils/orderTokens.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from TokenUtils.Token import Token


def isError(err: str):
errors = {'CMF', 'CoMF', 'NMF', 'IMF', 'TMF'}
if err in errors:
return True;
return False

def orderTokens(tk: Token):
if (isError(tk.token)):
return 1;
return 0;

30 changes: 30 additions & 0 deletions src/automata/ArithmeticOperatorAutomata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import re

def ArithmeticOperatorAutomata(state: str, input: str):
if (state == 'Arithmetic' and input == '+'):
return 'DoubleArithmeticOperator'
if (state == 'DoubleArithmeticOperator'):
return 'ArithmeticOperatorFinal'
if (state == 'Arithmetic*'):
return 'ArithmeticOperatorFinal'
if (state == 'Arithmetic-' and input == '-'):
return 'DoubleArithmeticOperator'
if (state == 'DoubleArithmeticOperator'):
return 'ArithmeticOperatorFinal'
if (state == 'Arithmetic-' and input == ' '):
return 'ArithmeticPossibleNROorART'
if (state == 'Arithmetic-' and re.match(r'\d', input)):
return 'Number'
if (state == 'ArithmeticPossibleNROorART' and re.match(r'\d', input)):
return 'Number'
if (state == 'ArithmeticPossibleNROorART' and input == ' '):
return 'ArithmeticPossibleNROorART'
if (state == 'ArithmeticPossibleNROorART'):
return 'ArithmeticOperatorFinal'
if (state == 'PossibleArithmeticMinus' and input == ' ' or input == '\t'):
return 'PossibleArithmeticMinus'
if (state == 'PossibleArithmeticMinus' and re.match(r'\d', input)):
return 'ArithmeticOperatorFinal'
if (state == 'PossibleArithmeticMinus'):
return 'GoBack'
return 'ArithmeticOperatorFinal';
24 changes: 24 additions & 0 deletions src/automata/CommentAutomata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
def CommentAutomata(state: str, input: str):
if (state == 'PossibleComment' and not (input == '*' or input == '/')):
return 'ArithmeticFinal'
if (state == 'PossibleComment' and input == '*'):
return 'BlockComment'
if (state == 'PossibleComment' and input == '/'):
return 'LineComment'
if (state == 'LineComment' and input == '\n'):
return 'LineCommentFinal'
if (state == 'BlockComment' and input == '\n'):
return 'BlockCommentOverflow'
if (state == 'LineComment'):
return 'LineComment'
if (state == 'BlockComment' and input == '*'):
return 'ClosingBlockComment'
if (state == 'ClosingBlockComment' and input == '/'):
return 'BlockCommentComplete'
if (state == 'BlockCommentComplete'):
return 'BlockCommentFinal'
if (state == 'ClosingBlockComment' and not input == '/'):
return 'BlockComment'
if (state == 'BlockComment'):
return 'BlockComment'
return state + 'Error:_' + input;
6 changes: 6 additions & 0 deletions src/automata/DelimiterAutomata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
def DelimiterAutomata(state: str, input: str):
if (state == 'DelimiterFinal'):
return 'DelimiterFinal';
if (state == 'Delimiter'):
return 'DelimiterFinal';
return state + 'Error:_' + input;
2 changes: 2 additions & 0 deletions src/automata/ErrorAutomata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def ErrorAutomata(state: str, input: str):
return state + 'Error:_' + input;
12 changes: 12 additions & 0 deletions src/automata/IdentifierAutomata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import re


def IdendifierAutomata(state: str, input: str):
isValid = re.match(r'[a-zA-Z]+', input) or re.match(r'\d', input) or input == '_';
if (state == 'IdentifierFinal'):
return 'IdentifierFinal';
if (state == 'Identifier' and isValid):
return 'Identifier'
elif (state == 'Identifier' and not isValid):
return 'IdentifierFinal'
return state + 'Error:_' + input;
12 changes: 12 additions & 0 deletions src/automata/LogicalOperatorAutomata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
def LogicalOperatorAutomata(state: str, input: str):
if (state == 'PossibleLogical&&' and input == '&'):
return 'DoubleLogicalOperator'
if (state == 'DoubleLogicalOperator'):
return 'LogicalOperatorFinal'
if (state == 'PossibleLogical||' and input == '|'):
return 'DoubleLogicalOperator'
if (state == 'PossibleLogical!' and not input == '='):
return 'LogicalOperatorFinal'
if (state == 'PossibleLogical!' and input == '='):
return 'DoubleRelationalOperator'
return 'MalformedToken';
33 changes: 33 additions & 0 deletions src/automata/NumbertAutomata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import re

def NumbertAutomata(state: str, input: str):
if (state == 'Number' and re.match(r'\d', input)):
return 'Number'
if (state == 'Number' and input == '.'):
return 'FPNumber'
if (state == 'FPNumber' and re.match(r'\d', input)):
return 'FPNumberComplete'
if (state == 'FPNumberComplete' and re.match(r'\d', input)):
return 'FPNumberComplete'
if (state == 'FPNumberComplete' and not re.match(r'\d', input)):
return 'NumberFinal'
if (state == 'FPNumber' and not re.match(r'\d', input)):
return 'MalformedNumberFinal'
if (state == 'Number' and not (input == ' ' or input == '-' or input == '\t')):
return 'NumberFinal'
if (state == 'Number' and (input == ' ' or input == '-' or input == '\t')):
return 'NumberFinalInPossibleOperation'
if (state == 'NumberFinalInPossibleOperation' and (input == ' ' or input == '\t')):
return 'NumberFinalInPossibleOperation_'
if (state == 'NumberFinalInPossibleOperation_' and (input == ' ' or input == '\t')):
return 'NumberFinalInPossibleOperation_'
if (state == 'NumberFinalInPossibleOperation' and input == '-'):
return 'PossibleArithmeticMinus'
if (state == 'NumberFinalInPossibleOperation_' and input == '-'):
return 'PossibleArithmeticMinus'
if (state == 'NumberFinalInPossibleOperation'):
return 'InitialState'
if (state == 'NumberFinalInPossibleOperation_'):
return 'InitialState'
else:
return 'NumberFinal'
6 changes: 6 additions & 0 deletions src/automata/RelationalOperatorAutomata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
def RelationalOperatorAutomata(state: str, input: str):
if (state == 'Relational' and input == '='):
return 'DoubleRelationalOperator'
if (state == 'DoubleRelationalOperator'):
return 'RelationalOperatorFinal'
return 'RelationalOperatorFinal';
10 changes: 10 additions & 0 deletions src/automata/StringAutomata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
def StringAutomata(state: str, input: str):
if (state == 'String' and input == '"'):
return 'StringComplete';
if (state == 'StringComplete'):
return 'StringFinal';
if (state == 'String' and input == '\n'):
return 'MalformedString';
if (state == 'MalformedString' and input == '\n'):
return 'MalformedStringFinal';
return 'String';
32 changes: 32 additions & 0 deletions src/automata/findApropriateAutomata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from typing import Callable
from automata.ArithmeticOperatorAutomata import ArithmeticOperatorAutomata
from automata.CommentAutomata import CommentAutomata
from automata.DelimiterAutomata import DelimiterAutomata
from automata.ErrorAutomata import ErrorAutomata
from automata.IdentifierAutomata import IdendifierAutomata
from automata.LogicalOperatorAutomata import LogicalOperatorAutomata
from automata.NumbertAutomata import NumbertAutomata
from automata.RelationalOperatorAutomata import RelationalOperatorAutomata
from automata.StringAutomata import StringAutomata


Automata = Callable[[str, str], str];

def findApropriateAutomata(state: str) -> Automata:
if ('Identifier' in state):
return IdendifierAutomata;
elif ('Delimiter' in state):
return DelimiterAutomata;
elif ('String' in state):
return StringAutomata;
elif ('Comment' in state):
return CommentAutomata;
elif ('Logical' in state):
return LogicalOperatorAutomata;
elif ('Relational' in state):
return RelationalOperatorAutomata;
elif ('Arithmetic' in state):
return ArithmeticOperatorAutomata;
elif ('Number' in state):
return NumbertAutomata;
return ErrorAutomata;
Loading

0 comments on commit 7df8d46

Please sign in to comment.