diff --git a/.gitignore b/.gitignore
index af55aa1..a342025 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,4 +8,4 @@ saida
saidas_dos_testes
# arquivos de cache do python
-src/__pycache__
\ No newline at end of file
+*.pyc
diff --git a/README.md b/README.md
index ef1cdf2..c4969d8 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,136 @@
-# Compiladores
+
+
+
+## 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.
+
+
+
+
+
+
+## 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)
+
+
+
+
+
+## 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.
+
+
+
+
+
+## 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'
+
+
+Se você usa o Visual Studio Code como editor não precisa fazer nada.
+
+
+
+### 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'
+
+
+
+## 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
diff --git a/src/TokenUtils/Token.py b/src/TokenUtils/Token.py
new file mode 100644
index 0000000..d4b9a57
--- /dev/null
+++ b/src/TokenUtils/Token.py
@@ -0,0 +1,8 @@
+from typing import NamedTuple
+
+class Token(NamedTuple):
+ token: str;
+ line: int;
+ tokenStartIndex: int;
+ tokenEndIndex: int;
+ value: str;
diff --git a/src/TokenUtils/generateTokenFromState.py b/src/TokenUtils/generateTokenFromState.py
new file mode 100644
index 0000000..b74b2b3
--- /dev/null
+++ b/src/TokenUtils/generateTokenFromState.py
@@ -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);
diff --git a/src/TokenUtils/getTokenTypeFromState.py b/src/TokenUtils/getTokenTypeFromState.py
new file mode 100644
index 0000000..c958e2f
--- /dev/null
+++ b/src/TokenUtils/getTokenTypeFromState.py
@@ -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');
diff --git a/src/TokenUtils/orderTokens.py b/src/TokenUtils/orderTokens.py
new file mode 100644
index 0000000..c23bf18
--- /dev/null
+++ b/src/TokenUtils/orderTokens.py
@@ -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;
+
\ No newline at end of file
diff --git a/src/automata/ArithmeticOperatorAutomata.py b/src/automata/ArithmeticOperatorAutomata.py
new file mode 100644
index 0000000..a0f6807
--- /dev/null
+++ b/src/automata/ArithmeticOperatorAutomata.py
@@ -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';
diff --git a/src/automata/CommentAutomata.py b/src/automata/CommentAutomata.py
new file mode 100644
index 0000000..0b96834
--- /dev/null
+++ b/src/automata/CommentAutomata.py
@@ -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;
diff --git a/src/automata/DelimiterAutomata.py b/src/automata/DelimiterAutomata.py
new file mode 100644
index 0000000..86be0d5
--- /dev/null
+++ b/src/automata/DelimiterAutomata.py
@@ -0,0 +1,6 @@
+def DelimiterAutomata(state: str, input: str):
+ if (state == 'DelimiterFinal'):
+ return 'DelimiterFinal';
+ if (state == 'Delimiter'):
+ return 'DelimiterFinal';
+ return state + 'Error:_' + input;
diff --git a/src/automata/ErrorAutomata.py b/src/automata/ErrorAutomata.py
new file mode 100644
index 0000000..6be1f77
--- /dev/null
+++ b/src/automata/ErrorAutomata.py
@@ -0,0 +1,2 @@
+def ErrorAutomata(state: str, input: str):
+ return state + 'Error:_' + input;
diff --git a/src/automata/IdentifierAutomata.py b/src/automata/IdentifierAutomata.py
new file mode 100644
index 0000000..ecae90a
--- /dev/null
+++ b/src/automata/IdentifierAutomata.py
@@ -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;
diff --git a/src/automata/LogicalOperatorAutomata.py b/src/automata/LogicalOperatorAutomata.py
new file mode 100644
index 0000000..1f49fa6
--- /dev/null
+++ b/src/automata/LogicalOperatorAutomata.py
@@ -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';
diff --git a/src/automata/NumbertAutomata.py b/src/automata/NumbertAutomata.py
new file mode 100644
index 0000000..45d8afa
--- /dev/null
+++ b/src/automata/NumbertAutomata.py
@@ -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'
diff --git a/src/automata/RelationalOperatorAutomata.py b/src/automata/RelationalOperatorAutomata.py
new file mode 100644
index 0000000..c61f8d5
--- /dev/null
+++ b/src/automata/RelationalOperatorAutomata.py
@@ -0,0 +1,6 @@
+def RelationalOperatorAutomata(state: str, input: str):
+ if (state == 'Relational' and input == '='):
+ return 'DoubleRelationalOperator'
+ if (state == 'DoubleRelationalOperator'):
+ return 'RelationalOperatorFinal'
+ return 'RelationalOperatorFinal';
diff --git a/src/automata/StringAutomata.py b/src/automata/StringAutomata.py
new file mode 100644
index 0000000..5742d86
--- /dev/null
+++ b/src/automata/StringAutomata.py
@@ -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';
diff --git a/src/automata/findApropriateAutomata.py b/src/automata/findApropriateAutomata.py
new file mode 100644
index 0000000..e4d0f13
--- /dev/null
+++ b/src/automata/findApropriateAutomata.py
@@ -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;
diff --git a/src/automata/findTokensInStringAutomata.py b/src/automata/findTokensInStringAutomata.py
new file mode 100644
index 0000000..0203e13
--- /dev/null
+++ b/src/automata/findTokensInStringAutomata.py
@@ -0,0 +1,127 @@
+import re
+from TokenUtils.Token import Token
+from TokenUtils.generateTokenFromState import generateTokenFromState
+from automata.findApropriateAutomata import Automata, findApropriateAutomata
+from filesystem import ResTokenList
+
+def findTokensInStringAutomata(line: str, lineCount: int, initialState: str, overflow: str) -> ResTokenList:
+ lineLength: int = len(line);
+ tokenStartIndex: int = 0;
+ currentIndex: int = 0;
+ currentState: str = initialState;
+ tokensFoundInThisLine: list[Token] = [];
+ tokenOverflow: str = overflow if initialState == 'BlockComment' else '';
+
+ exitLoop = False;
+
+ while (not exitLoop and currentIndex < lineLength):
+ # Se ainda no estado inicial, considere o caractere atual como inicio do token
+ if (currentState == 'InitialState'):
+ tokenStartIndex = currentIndex;
+
+ # Caractere atual
+ character: str = line[currentIndex];
+
+ # Proximo estado, dado o caractere lido
+ nextState: str = getNextState(currentState, character);
+
+ if (nextState == 'NumberFinalInPossibleOperation'):
+ token = generateTokenFromState('NumberFinal', lineCount, line, tokenStartIndex, currentIndex);
+ tokensFoundInThisLine.append(token);
+ tokenStartIndex = currentIndex;
+ currentIndex = currentIndex - 1;
+
+ if (nextState == 'GoBack'):
+ nextState = 'InitialState';
+ currentIndex = tokenStartIndex - 1;
+
+ # Se for um estado final, gere um token
+ if (isFinalState(nextState)):
+ token = generateTokenFromState(nextState, lineCount, line, tokenStartIndex, currentIndex);
+ if (not (token.token == 'COM' or token.token == 'CMB')):
+ tokensFoundInThisLine.append(token); # Apos salvar o token
+ currentState = 'InitialState'; # Volte para o estado inicial
+
+ # Do contrario, leia o proximo caractere
+ else:
+ # Se a linha termina e o estado não é final, decrementa o index
+ # para chegar num estado final na proxima iteração
+ if (currentIndex + 1 >= lineLength and not (nextState == 'InitialState' or nextState == 'BlockCommentOverflow')):
+ nextState = getNextState(nextState, '\n');
+ currentIndex = currentIndex + 2;
+ token = generateTokenFromState(nextState, lineCount, line, tokenStartIndex, currentIndex);
+ if (not (token.token == 'COM' or token.token == 'CMB' or token.token == 'None')):
+ tokensFoundInThisLine.append(token); # Apos salvar o token
+ currentState = 'InitialState';
+
+ currentState = nextState # Define o priximo estado
+ currentIndex = currentIndex + 1;
+
+ if (currentState != 'BlockCommentOverflow'):
+ currentState = 'InitialState';
+ else:
+ tokenOverflow = tokenOverflow + line[tokenStartIndex:].replace('\n', '');
+ currentState = 'BlockComment';
+ return ResTokenList(currentState, tokenStartIndex, lineCount, tokenOverflow, tokensFoundInThisLine);
+
+def getNextState(state: str, input: str) -> str:
+ if (state == 'MalformedToken'):
+ return 'MalformedTokenFinal';
+ if (not state == 'InitialState'):
+ automata: Automata = findApropriateAutomata(state);
+ return automata(state, input);
+ if (input == '/'):
+ return 'PossibleComment';
+ elif (isDelimiter(input)):
+ return 'Delimiter'
+ elif (re.match( r'[a-zA-Z]+', input)): # Se for uma letra
+ return 'Identifier'
+ elif (input == '"'):
+ return 'String';
+ elif (input == '&'):
+ return 'PossibleLogical&&';
+ elif (input == '|'):
+ return 'PossibleLogical||';
+ elif (input == '!'):
+ return 'PossibleLogical!';
+ elif (input == '=' or input == '<' or input == '>'):
+ return 'Relational';
+ elif (input == '+'):
+ return 'Arithmetic';
+ elif (input == '*'):
+ return 'Arithmetic*';
+ elif (input == '-'):
+ return 'Arithmetic-';
+ elif (re.match(r'\d', input)):
+ return 'Number';
+ elif (input == ' ' or input == '\t' or input == '\n'):
+ return ('InitialState');
+ return 'MalformedToken';
+
+# verifica se um caractere é um delimitador
+def isDelimiter(charactere: str) -> bool:
+ delimiters = {'.', ';', ',', '(', ')', '[', ']', '{', '}'}
+ if charactere in delimiters:
+ return True
+ return False
+
+def isFinalState(state: str):
+ finalStates = {
+ 'DelimiterFinal',
+ 'MalformedStringFinal',
+ 'StringFinal',
+ 'LineCommentFinal',
+ 'BlockCommentFinal',
+ 'ArithmeticFinal',
+ 'LogicalOperatorFinal',
+ 'NumberFinal',
+ 'ArithmeticOperatorFinal',
+ 'RelationalOperatorFinal',
+ 'IdentifierFinal',
+ 'MalformedNumberFinal',
+ 'MalformedTokenFinal',
+ };
+ if state in finalStates:
+ return True;
+ return False;
+
diff --git a/src/filesystem.py b/src/filesystem.py
index 697f275..f0e4caa 100644
--- a/src/filesystem.py
+++ b/src/filesystem.py
@@ -2,20 +2,14 @@
from os import listdir
from os.path import isfile, join
from typing import Callable, NamedTuple, TypeVar, Dict
+from TokenUtils.Token import Token
T = TypeVar('T');
TokenListPerFile = Dict[str, 'list[str]']
-class Token(NamedTuple):
- token: str;
- line: int;
- tokenStartIndex: int;
- tokenEndIndex: int;
- value: str;
-
class ResTokenList(NamedTuple):
- lastState: int;
+ lastState: str;
lastStartTokenIndex: int;
tokenStartLine: int;
tokenOverflow: str;
@@ -31,11 +25,11 @@ def listDirFiles(dirPath: str) -> 'list[str]':
onlyfiles = [f for f in listdir(dirPath) if isfile(join(dirPath, f))]
return onlyfiles;
-def readFileLines(opened_file: TextIOWrapper, on_line: Callable[[str, int, int, str], ResTokenList]):
+def readFileLines(opened_file: TextIOWrapper, on_line: Callable[[str, int, str, str], ResTokenList]):
# Lê cada linha do arquivo e passa para a função de callback on_line
response: list[Token] = [];
- currentState: ResTokenList = ResTokenList(0, 0, 0, '', []);
+ currentState: ResTokenList = ResTokenList('InitialState', 0, 0, '', []);
# Loop through each line via file handler
for count, line in enumerate(opened_file):
@@ -45,7 +39,7 @@ def readFileLines(opened_file: TextIOWrapper, on_line: Callable[[str, int, int,
response = response + res.tokenList;
currentState = res;
- if (currentState.lastState == 8):
+ if (currentState.lastState == 'BlockComment'):
t = Token('CoMF', currentState.tokenStartLine, currentState.lastStartTokenIndex, 0, currentState.tokenOverflow);
response.append(t);
diff --git a/src/main.py b/src/main.py
index 8565c32..4044f0b 100644
--- a/src/main.py
+++ b/src/main.py
@@ -1,331 +1,14 @@
-import re
-from filesystem import ResTokenList, Token, TokenListPerFile, listDirFiles, readFileLines
+from TokenUtils.Token import Token
+from TokenUtils.orderTokens import isError, orderTokens
+from automata.findTokensInStringAutomata import findTokensInStringAutomata
+from filesystem import ResTokenList, TokenListPerFile, listDirFiles, readFileLines
-LETRA = re.compile(r'/[a-zA-Z]+/g');
-DIGITO = re.compile(r'/\d/g');
-
-
-def onReadLine(line: str, lineNumber: int, initialState: int, overflow: str) -> ResTokenList:
+def findTokensInString(line: str, lineNumber: int, initialState: str, overflow: str) -> ResTokenList:
# Se a linha estiver vaxia não fax nada
- tokens = findTokensInString(line, lineNumber, initialState, overflow);
+ tokens = findTokensInStringAutomata(line, lineNumber, initialState, overflow);
return tokens;
-# verifica se um caractere é um delimitador
-def isDelimiter(charactere: str) -> bool:
- delimiters = {'.', ';', ',', '(', ')', '[', ']', '{', '}'}
- if charactere in delimiters:
- return True
- return False
-
-# 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 findTokensInString(line: str, lineCount: int, initialState: int, overflow: str) -> ResTokenList:
- lineLength: int = len(line);
- tokenStartIndex: int = 0;
- currentIndex: int = 0;
- currentState: int = initialState;
- tokensFoundInThisLine: list[Token] = [];
- tokenOverflow: str = overflow if initialState == 8 else '';
-
- exitLoop = False;
-
- while (not exitLoop and currentIndex < lineLength):
- if (currentState == 0):
- if (line[currentIndex] == '/'):
- currentIndex = currentIndex + 1;
- currentState = 2;
- elif (isDelimiter(line[currentIndex])):
- t = Token('DEL', lineCount, currentIndex, currentIndex + 1, line[currentIndex:currentIndex + 1]);
- tokensFoundInThisLine.append(t);
- currentState = 0;
- currentIndex = currentIndex + 1;
- elif (re.match( r'[a-zA-Z]+', line[currentIndex])):
- currentState = 5;
- tokenStartIndex = currentIndex;
- if(currentIndex + 1 >= lineLength):
- atEndOfLine = line[tokenStartIndex:]
- if (isReserved(line[tokenStartIndex: currentIndex])):
- t = Token('PRE', lineCount, tokenStartIndex, currentIndex, atEndOfLine);
- else:
- t = Token('IDE', lineCount, tokenStartIndex, currentIndex, atEndOfLine);
- tokensFoundInThisLine.append(t);
- currentState = 0;
- currentIndex = currentIndex + 1;
- else:
- currentIndex = currentIndex + 1;
- elif (line[currentIndex] == '"'):
- tokenStartIndex = currentIndex;
- currentIndex = currentIndex + 1;
- currentState = 6;
- elif (line[currentIndex] == '&'):
- currentIndex = currentIndex + 1;
- currentState = 14;
- elif (line[currentIndex] == '|'):
- currentIndex = currentIndex + 1;
- currentState = 16;
- elif (line[currentIndex] == '!'):
- currentIndex = currentIndex + 1;
- currentState = 12;
- elif (line[currentIndex] == '=' or line[currentIndex] == '<' or line[currentIndex] == '>'):
- currentIndex = currentIndex + 1;
- currentState = 18;
- elif (line[currentIndex] == '+'):
- currentIndex = currentIndex + 1;
- currentState = 19;
- elif (line[currentIndex] == '*'):
- t = Token('ART', lineCount, currentIndex, currentIndex + 1, line[currentIndex:currentIndex + 1]);
- tokensFoundInThisLine.append(t);
- currentIndex = currentIndex + 1;
- currentState = 0;
- elif (line[currentIndex] == '-'):
- if (line[currentIndex + 1] == '-'):
- t = Token('ART', lineCount, currentIndex, currentIndex + 2, line[currentIndex:currentIndex + 2]);
- tokensFoundInThisLine.append(t);
- currentIndex = currentIndex + 2;
- currentState = 0;
- else:
- tokenStartIndex = currentIndex;
- currentIndex = currentIndex + 1;
- currentState = 20;
- elif (re.match(r'\d', line[currentIndex])):
- tokenStartIndex = currentIndex;
- currentIndex = currentIndex + 1;
- currentState = 21;
- elif(line[currentIndex] == ' ' or line[currentIndex] == '\t' or line[currentIndex] == '\n'):
- currentIndex = currentIndex + 1;
- currentState = 0;
- else:
- mlkmk = line[currentIndex];
- t = Token('TMF', lineCount, currentIndex, currentIndex, mlkmk);
- tokensFoundInThisLine.append(t);
- currentIndex = currentIndex + 1;
- currentState = 0;
- elif(currentState == 2):
- if (line[currentIndex] == '*'):
- currentState = 8;
- tokenStartIndex = currentIndex - 1; # considerando a barra anterior
- currentIndex = currentIndex + 1;
- elif (line[currentIndex] == '/'):
- t = Token('COM', lineCount, currentIndex, lineLength - 1, line[currentIndex - 1: -1]);
- #tokensFoundInThisLine.append(t);
- exitLoop = True;
- currentIndex = lineLength - 1;
- currentState = 0;
- else:
- t = Token('ART', lineCount, currentIndex -1, currentIndex, line[currentIndex - 1: currentIndex]);
- tokensFoundInThisLine.append(t);
- currentState = 0;
- currentIndex = currentIndex + 1;
- elif(currentState == 5):
- if(currentIndex + 1 >= lineLength):
- atEndOfLine = line[tokenStartIndex:]
- if (isReserved(line[tokenStartIndex: currentIndex])):
- t = Token('PRE', lineCount, tokenStartIndex, currentIndex, atEndOfLine);
- else:
- t = Token('IDE', lineCount, tokenStartIndex, currentIndex, atEndOfLine);
- tokensFoundInThisLine.append(t);
- currentState = 0;
- tokenStartIndex = 0;
- elif (line[currentIndex] == '_' or re.match(r'[a-zA-Z]+', line[currentIndex]) or re.match(r'\d', line[currentIndex])):
- currentIndex = currentIndex + 1;
- else:
- ideToken = line[tokenStartIndex: currentIndex]
- if (isReserved(line[tokenStartIndex: currentIndex])):
- t = Token('PRE', lineCount, tokenStartIndex, currentIndex, ideToken);
- else:
- t = Token('IDE', lineCount, tokenStartIndex, currentIndex, ideToken);
- tokensFoundInThisLine.append(t);
- currentState = 0;
- tokenStartIndex = 0;
- elif(currentState == 6):
- if (line[currentIndex] == '"'):
- stoken = line[tokenStartIndex: currentIndex + 1];
- if (hasNonASCII(stoken)):
- t = Token('CMF', lineCount, tokenStartIndex, currentIndex, stoken);
- else:
- t = Token('CAC', lineCount, tokenStartIndex, currentIndex, stoken);
- tokensFoundInThisLine.append(t);
- currentState = 0;
- tokenStartIndex = 0;
- currentIndex = currentIndex + 1;
- elif(line[currentIndex] == '\n'):
- t = Token('CMF', lineCount, tokenStartIndex, lineLength, line[tokenStartIndex: lineLength - 1]);
- tokensFoundInThisLine.append(t);
- currentState = 0;
- tokenStartIndex = 0;
- currentIndex = currentIndex + 1;
- else:
- currentIndex = currentIndex + 1;
- elif(currentState == 8):
- if (line[currentIndex] == '*'):
- currentState = 10;
- currentIndex = currentIndex + 1;
- elif (line[currentIndex] == '\n' or currentIndex == lineLength -1):
- l = line.replace('\n', '');
- if (line != '\n'):
- tokenOverflow = tokenOverflow + l[tokenStartIndex: len(l)];
- currentIndex = currentIndex + 1;
- else:
- currentIndex = currentIndex + 1;
- elif(currentState == 10):
- if (line[currentIndex] == '/'):
- t = Token('COM', lineCount, tokenStartIndex, currentIndex, line[tokenStartIndex: currentIndex]);
- #tokensFoundInThisLine.append(t);
- currentState = 0;
- currentIndex = currentIndex + 1;
- else:
- currentState = 8;
- currentIndex = currentIndex + 1;
- elif(currentState == 14):
- if (line[currentIndex] == '&'):
- t = Token('LOG', lineCount, currentIndex -1, currentIndex + 1, line[currentIndex -1: currentIndex + 1]);
- tokensFoundInThisLine.append(t);
- currentState = 0;
- currentIndex = currentIndex + 1;
- else:
- t = Token('TMF', lineCount, currentIndex -1, currentIndex + 1, line[currentIndex -1: currentIndex]);
- tokensFoundInThisLine.append(t);
- currentIndex = currentIndex + 1;
- currentState = 0;
- elif(currentState == 16):
- if (line[currentIndex] == '|'):
- t = Token('LOG', lineCount, currentIndex -1, currentIndex + 1, line[currentIndex -1: currentIndex + 1]);
- tokensFoundInThisLine.append(t);
- currentState = 0;
- currentIndex = currentIndex + 1;
- else:
- t = Token('TMF', lineCount, currentIndex -1, currentIndex + 1, line[currentIndex -1: currentIndex]);
- tokensFoundInThisLine.append(t);
- currentIndex = currentIndex + 1;
- currentState = 0;
- elif(currentState == 12):
- if (line[currentIndex] == '='):
- t = Token('REL', lineCount, currentIndex -1, currentIndex + 1, line[currentIndex -1: currentIndex + 1]);
- tokensFoundInThisLine.append(t);
- currentState = 0;
- currentIndex = currentIndex + 1;
- else:
- t = Token('LOG', lineCount, currentIndex -1, currentIndex, line[currentIndex -1: currentIndex]);
- tokensFoundInThisLine.append(t);
- currentState = 0;
- elif(currentState == 18):
- if (line[currentIndex] == '='):
- t = Token('REL', lineCount, currentIndex -1, currentIndex + 1, line[currentIndex -1: currentIndex + 1]);
- tokensFoundInThisLine.append(t);
- currentState = 0;
- currentIndex = currentIndex + 1;
- else:
- t = Token('REL', lineCount, currentIndex -1, currentIndex, line[currentIndex -1: currentIndex]);
- tokensFoundInThisLine.append(t);
- currentState = 0;
- elif(currentState == 19):
- if (line[currentIndex] == '+'):
- t = Token('ART', lineCount, currentIndex -1, currentIndex + 1, line[currentIndex -1: currentIndex + 1]);
- tokensFoundInThisLine.append(t);
- currentState = 0;
- currentIndex = currentIndex + 1;
- else:
- t = Token('ART', lineCount, currentIndex -1, currentIndex, line[currentIndex -1: currentIndex]);
- tokensFoundInThisLine.append(t);
- currentState = 0;
- elif(currentState == 20):
- if (re.match(r'\d', line[currentIndex])):
- currentState = 21;
- elif (line[currentIndex] == ' '):
- currentIndex = currentIndex + 1;
- else:
- t = Token('ART', lineCount, tokenStartIndex, currentIndex, line[tokenStartIndex: currentIndex]);
- tokensFoundInThisLine.append(t);
- currentState = 0;
- elif(currentState == 21):
- if(currentIndex + 1 >= lineLength):
- l = line[tokenStartIndex:]
- if (l[len(l) - 1] == '\n'):
- l = l
- #l = l[ :- 1]
- t = Token('NRO', lineCount, tokenStartIndex, currentIndex, l);
- tokensFoundInThisLine.append(t);
- currentIndex = currentIndex + 1;
- elif (re.match(r'\d', line[currentIndex])):
- currentIndex = currentIndex + 1;
- elif (line[currentIndex] == '.'):
- currentState = 22;
- currentIndex = currentIndex + 1;
- else:
- t = Token('NRO', lineCount, tokenStartIndex, currentIndex, line[tokenStartIndex: currentIndex]);
- tokensFoundInThisLine.append(t);
- if (line[currentIndex] == ' ' or line[currentIndex] == '-' or line[currentIndex] == '\t'):
- currentState = 24;
- else:
- currentState = 0;
- elif(currentState == 22):
- if (re.match(r'\d', line[currentIndex])):
- currentIndex = currentIndex + 1;
- currentState = 23;
- else:
- t = Token('NMF', lineCount, tokenStartIndex, currentIndex, line[tokenStartIndex: currentIndex]);
- tokensFoundInThisLine.append(t);
- currentState = 0;
- elif(currentState == 23):
- if (re.match(r'\d', line[currentIndex])):
- currentIndex = currentIndex + 1;
- else:
- t = Token('NRO', lineCount, tokenStartIndex, currentIndex, line[tokenStartIndex: currentIndex]);
- tokensFoundInThisLine.append(t);
- currentState = 0;
- elif(currentState == 24):
- if (line[currentIndex] == ' ' or line[currentIndex] == '\t'):
- currentIndex = currentIndex + 1;
- elif (line[currentIndex] == '-'):
- tokenStartIndex = currentIndex;
- currentIndex = currentIndex + 1;
- currentState = 25;
- else:
- currentState = 0;
- elif(currentState == 25):
- if (line[currentIndex] == ' ' or line[currentIndex] == '\t'):
- currentIndex = currentIndex + 1;
- elif (re.match(r'\d', line[currentIndex])):
- t = Token('ART', lineCount, tokenStartIndex, tokenStartIndex + 1, line[tokenStartIndex: tokenStartIndex + 1]);
- tokensFoundInThisLine.append(t);
- currentState = 0;
- else:
- currentIndex = tokenStartIndex;
- currentState = 0;
- else:
- exitLoop = True;
-
- if (currentState != 8):
- currentState = 0;
- return ResTokenList(currentState, tokenStartIndex, lineCount, tokenOverflow, tokensFoundInThisLine);
-
-
-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;
-
+# Conta a quantidade de error léxicos presentes em uma lista de tokens
def errorCount(tk: 'list[Token]'):
ec = 0
for t in tk:
@@ -336,8 +19,7 @@ def errorCount(tk: 'list[Token]'):
def lexico():
source_directory = 'entrada';
- # Get the file handler
- #fhand = open('src/entrada.txt', 'r');
+
entry_files: list[str] = listDirFiles(source_directory);
tokenListPerFile: TokenListPerFile = {}
@@ -346,13 +28,12 @@ def lexico():
for filename in entry_files:
filepath = source_directory + '/' + filename; # define o caminho para o arquivo
source_file = open(filepath, 'r'); # abre o arquivo
- tokensFound: list[Token] = readFileLines(source_file, onReadLine); # le os arquivos e recupera os token
+ tokensFound: list[Token] = readFileLines(source_file, findTokensInString); # le os arquivos e recupera os tokens
tokensFound.sort(key=orderTokens);
errorsNum = errorCount(tokensFound);
# salva as informaações em um arquivo
- #print(tabulate(tokensFound, headers=['token', 'line', 'tokeStartIndex', 'tokenEndIndex', 'value']));
tokenListPerFile[filename] = [];
for token in tokensFound:
formatedOutput = '{0:02d} {1:s} {2:s}\n'.format(token.line, token.token, token.value.replace('\n', ''));
@@ -373,4 +54,4 @@ def lexico():
outputFile.close();
return;
-lexico();
\ No newline at end of file
+lexico();