Skip to content

Instantly share code, notes, and snippets.

@tayllan
Created March 10, 2018 15:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tayllan/db4c2f85ca14f1e3c15c6b21bd18dfda to your computer and use it in GitHub Desktop.
Save tayllan/db4c2f85ca14f1e3c15c6b21bd18dfda to your computer and use it in GitHub Desktop.
%lex
%options flex case-insensitive
%% /* lexical grammar */
\s+ /* espaços em branco não importam */
[/][*][^*]*[*]+([^/*][^*]*[*]+)*[/] /* comentários multi-linha */
[@][^@]*[@] /* comentários em linha */
";" return ';'
"," return ','
":" return ':'
"." return '.'
"=" return '='
"<>" return '<>'
"<" return '<'
"<=" return '<='
">" return '>'
">=" return '>='
"{" return '{'
"}" return '}'
"(" return '('
")" return ')'
"*" return '*'
"/" return '/'
"-" return '-'
"+" return '+'
"++" return '++'
"--" return '--'
"Alfa" return 'ALFA'
"Cursor" return 'CURSOR'
"Data" return 'DATA'
"Definir" return 'DEFINIR'
"Funcao" return 'FUNCAO'
"Grid" return 'GRID'
"Numero" return 'NUMERO'
"Tabela" return 'TABELA'
"Lista" return 'LISTA'
"Inicio" return 'INICIO'
"Fim" return 'FIM'
"Enquanto" return 'ENQUANTO'
"Para" return 'PARA'
"Se" return 'SE'
"Senao" return 'SENAO'
"End" return 'END'
"OU" return 'OU'
"E" return 'E'
"SQL" return 'SQL'
"Pare" return 'PARE'
"Continue" return 'CONTINUE'
"ExecSql" return 'EXECSQL'
"VaPara" return 'VAPARA'
\'[^\']\' return 'CHAR'
\"([^\"\\]|\\.?|\\\n)*\" return 'STRING'
[0-9]+("."[0-9]+)?\b return 'NUM'
[a-z]\w{0,99}\b return 'VARIAVEL'
<<EOF>> return 'EOF'
. return 'INVALID'
/lex
/* operadores a precedências */
%left '+' '-'
%left '*' '/'
%left '^'
%left UMINUS
%start regra
%ebnf
%{
var variaveisDeclaradas = {web_html: true, valstr: true, valevt: true};
var cursoresAbertos = {};
var cursoresAvancadosAbertos = {};
var cursoresAvancadosCriados = {};
var cursoresAvancadosOrdenados = {};
var ordemFuncoesCursoresAvancados = [
{funcao: 'sql_criar', prioridade: 1},
{funcao: 'sql_usarsqlsenior2', prioridade: 2},
{funcao: 'sql_usarabrangencia', prioridade: 2},
{funcao: 'sql_definircomando', prioridade: 3},
{funcao: 'sql_definir', prioridade: 4},
{funcao: 'sql_abrircursor', prioridade: 5},
{funcao: 'sql_retornar', prioridade: 7},
{funcao: 'sql_proximo', prioridade: 8},
{funcao: 'sql_fecharcursor', prioridade: 9},
{funcao: 'sql_destruir', prioridade: 10},
];
var pilhaCursores = {};
var pilhaCursoresAvancados = {};
var exceptions = {
arr: [],
varNaoDeclarada: function(variavel, linha) {
if (!variaveisDeclaradas[variavel.toLowerCase()]) {
exceptions.arr.push({
linha: linha,
mensagem: 'Variável [' + variavel + '] não foi declarada.',
variavel: variavel,
tipo: 'Warning'
});
}
},
varNaoDeclaradaParaSQLString: function(string, linha) {
var variaveis = string.match(/:[a-z]\w{0,99}/g);
if (variaveis) {
variaveis.forEach(function(variavel) {
exceptions.varNaoDeclarada(variavel.substring(1), linha);
});
}
},
varJaDeclarada: function(variavel, linha) {
if (variaveisDeclaradas[variavel.toLowerCase()] === true) {
exceptions.arr.push({
linha: linha,
mensagem: 'Variável [' + variavel + '] já foi declarada.'
});
}
variaveisDeclaradas[variavel.toLowerCase()] = true;
},
cursorJaAberto: function(cursor, linha) {
if (cursoresAbertos[cursor.toLowerCase()] !== undefined) {
exceptions.arr.push({
linha: linha,
mensagem: 'Cursor [' + cursor + '] já foi aberto.'
});
}
cursoresAbertos[cursor.toLowerCase()] = cursor;
},
cursorNaoAberto: function(cursor, linha, shouldDelete) {
if (!cursoresAbertos[cursor.toLowerCase()]) {
exceptions.arr.push({
linha: linha,
mensagem: 'Cursor [' + cursor + '] não foi aberto.'
});
}
else if (shouldDelete) {
delete cursoresAbertos[cursor.toLowerCase()];
}
},
cursorNaoFechado: function(linha) {
for (var key in cursoresAbertos) {
exceptions.arr.push({
linha: linha,
mensagem: 'Cursor [' + cursoresAbertos[key] + '] não foi fechado.'
});
}
},
cursorDentroEnquanto: function(cursor, linha, empilhar) {
var aux = cursor.toLowerCase();
if (!pilhaCursores[aux]) {
pilhaCursores[aux] = 0;
}
if (empilhar) {
pilhaCursores[aux]++;
if (pilhaCursores[aux] > 0) {
exceptions.arr.push({
linha: linha,
mensagem: 'Cursor [' + cursor + '] sendo iterado sem chamar [.Proximo()].'
});
}
else if (pilhaCursores[aux] < 0) {
exceptions.arr.push({
linha: linha,
mensagem: 'Cursor [' + cursor + '] chamando [.Proximo()] mais de uma vez na mesma iteração.',
tipo: 'Warning'
});
}
}
else {
pilhaCursores[aux]--;
}
},
cursorAvancadoJaCriado: function(cursor, linha) {
if (cursoresAvancadosCriados[cursor.toLowerCase()] !== undefined) {
exceptions.arr.push({
linha: linha,
mensagem: 'Cursor Avançado [' + cursor + '] já foi criado.'
});
}
cursoresAvancadosCriados[cursor.toLowerCase()] = cursor;
},
cursorAvancadoJaAberto: function(cursor, linha) {
if (cursoresAvancadosAbertos[cursor.toLowerCase()] !== undefined) {
exceptions.arr.push({
linha: linha,
mensagem: 'Cursor Avançado [' + cursor + '] já foi aberto.'
});
}
cursoresAvancadosAbertos[cursor.toLowerCase()] = cursor;
},
cursorAvancadoNaoCriado: function(cursor, linha, shouldDelete) {
if (!cursoresAvancadosCriados[cursor.toLowerCase()]) {
exceptions.arr.push({
linha: linha,
mensagem: 'Cursor Avançado [' + cursor + '] não foi criado [SQL_Criar();].'
});
}
else if (shouldDelete) {
delete cursoresAvancadosCriados[cursor.toLowerCase()];
}
},
cursorAvancadoNaoAberto: function(cursor, linha, shouldDelete) {
if (!cursoresAvancadosAbertos[cursor.toLowerCase()]) {
exceptions.arr.push({
linha: linha,
mensagem: 'Cursor Avançado [' + cursor + '] não foi aberto [SQL_AbrirCursor();].'
});
}
else if (shouldDelete) {
delete cursoresAvancadosAbertos[cursor.toLowerCase()];
}
},
cursorAvancadoNaoFechado: function(linha) {
for (var key in cursoresAvancadosAbertos) {
exceptions.arr.push({
linha: linha,
mensagem: 'Cursor Avançado [' + cursoresAvancadosAbertos[key] + '] não foi fechado [SQL_FecharCursor();].'
});
}
},
cursorAvancadoNaoDestruido: function(linha) {
for (var key in cursoresAvancadosCriados) {
exceptions.arr.push({
linha: linha,
mensagem: 'Cursor Avançado [' + cursoresAvancadosCriados[key] + '] não foi destruído [SQL_Destruir();].'
});
}
},
cursorAvancadoDentroEnquanto: function(cursor, linha, empilhar) {
var aux = cursor.toLowerCase();
if (!pilhaCursoresAvancados[aux]) {
pilhaCursoresAvancados[aux] = 0;
}
if (empilhar) {
pilhaCursoresAvancados[aux]++;
if (pilhaCursoresAvancados[aux] > 0) {
exceptions.arr.push({
linha: linha,
mensagem: 'Cursor Avançado [' + cursor + '] sendo iterado sem chamar [SQL_Proximo();].'
});
}
else if (pilhaCursoresAvancados[aux] < 0) {
exceptions.arr.push({
linha: linha,
mensagem: 'Cursor Avançado [' + cursor + '] chamando [SQL_Proximo();] mais de uma vez na mesma iteração.',
tipo: 'Warning'
});
}
}
else {
pilhaCursoresAvancados[aux]--;
}
},
cursoresAvancadosEmOrdem: function(cursor, funcao, linha) {
var prioridade = ordemFuncoesCursoresAvancados.filter(function(e) {
return (funcao.indexOf(e.funcao) !== -1);
})[0].prioridade;
if (!cursoresAvancadosOrdenados[cursor.toLowerCase()] || cursoresAvancadosOrdenados[cursor.toLowerCase()] <= prioridade) {
cursoresAvancadosOrdenados[cursor.toLowerCase()] = prioridade;
if (funcao === 'sql_fecharcursor' || funcao === 'sql_destruir') {
delete cursoresAvancadosOrdenados[cursor.toLowerCase()];
}
}
else {
exceptions.arr.push({
linha: linha,
mensagem: 'Função do cursor Avançado [' + cursor + '] na ordem incorreta.'
});
}
},
flattenArray: function(arr) {
if (arr.constructor === Array) {
return [].concat.apply([], arr);
}
return arr;
},
};
%}
%% /* language grammar */
regra
: bloco?
{
exceptions.cursorNaoFechado(@1.last_line);
exceptions.cursorAvancadoNaoFechado(@1.last_line);
exceptions.cursorAvancadoNaoDestruido(@1.last_line);
var clonedArr = JSON.parse(JSON.stringify(exceptions.arr));
exceptions.arr = [];
/* Lançando as exceptions acumuladas em exceptions.arr apenas no final da regra */
if (clonedArr.length > 0) {
variaveisDeclaradas = {web_html: true, valstr: true, valevt: true};
cursoresAbertos = {};
cursoresAvancadosAbertos = {};
cursoresAvancadosCriados = {};
cursoresAvancadosOrdenados = {};
pilhaCursores = {};
pilhaCursoresAvancados = {};
throw clonedArr;
} else {
variaveisDeclaradas = {web_html: true, valstr: true, valevt: true};
cursoresAbertos = {};
cursoresAvancadosAbertos = {};
cursoresAvancadosCriados = {};
cursoresAvancadosOrdenados = {};
pilhaCursores = {};
pilhaCursoresAvancados = {};
}
}
;
bloco
: bloco-aux bloco?
| '{' bloco? '}' ';'? bloco?
| INICIO bloco? FIM ';'? bloco?
;
bloco-aux
: definicao
| atribuicao
| enquanto
| para
| se
| funcao
| chamada-funcao
| goto
;
definicao
: definicao-variavel
| definicao-funcao
| definicao-sql
;
definicao-variavel
: DEFINIR tipo VARIAVEL ';'
{ exceptions.varJaDeclarada($3, @3.first_line); }
;
definicao-funcao
: DEFINIR FUNCAO VARIAVEL '(' (tipo END? VARIAVEL ','?)* ')' ';'
;
definicao-sql
: VARIAVEL '.' SQL STRING ';'
{
exceptions.varNaoDeclarada($1, @1.first_line);
exceptions.varNaoDeclaradaParaSQLString($4, @4.first_line);
}
;
/* Declarações */
atribuicao
: atribuicao-aux ';'
;
atribuicao-aux
: variavel '=' expressao
{ exceptions.varNaoDeclarada($1, @1.first_line);}
| variavel '=' chamada-funcao-aux
{ exceptions.varNaoDeclarada($1, @1.first_line); }
| variavel operadores-posfixos
{ exceptions.varNaoDeclarada($1, @1.first_line); }
;
enquanto
: ENQUANTO '(' comparacao ')' declaracao-bloco
{
var temp = exceptions.flattenArray($3);
temp.forEach(function(exp, i) {
var match = exp.match(/^([^\.]+)\.Achou$/);
if (match && match.length == 2) {
exceptions.cursorDentroEnquanto(match[1], @3.first_line, true);
}
if (exp.toLowerCase() === 'sql_eof') {
exceptions.cursorAvancadoDentroEnquanto(temp[i + 1], @3.first_line, true);
}
});
}
;
para
: PARA '(' atribuicao-aux ';' comparacao ';' atribuicao-aux ')' declaracao-bloco
;
se
: SE '(' comparacao ')' declaracao-bloco senao-se* senao?
;
senao-se
: SENAO SE '(' comparacao ')' declaracao-bloco
;
senao
: SENAO declaracao-bloco
;
declaracao-bloco
: '{' bloco? pare-continue? '}' ';' ?
| INICIO bloco? pare-continue? FIM ';' ?
;
pare-continue
: ((PARE|CONTINUE) ';')
;
goto
: VARIAVEL ':'
;
/* Funções */
funcao
: FUNCAO VARIAVEL '(' (tipo END? variavel ','?)* ')' ';' funcao-bloco
;
funcao-bloco
: '{' bloco? '}' ';'?
| INICIO bloco? FIM ';'?
;
chamada-funcao
: chamada-funcao-aux ';'
{
var funcao = $1[0].toLowerCase();
var cursor = $1[1];
if (funcao === 'sql_criar') {
exceptions.cursorAvancadoJaCriado(cursor, @1.first_line);
}
else if (funcao === 'sql_abrircursor') {
exceptions.cursorAvancadoJaAberto(cursor, @1.first_line);
}
else if (funcao.indexOf('sql_') !== -1) {
exceptions.cursorAvancadoNaoCriado(cursor, @1.first_line, (funcao === 'sql_destruir'));
if (funcao === 'sql_proximo' || funcao === 'sql_fecharcursor' || funcao.indexOf('sql_retornar') !== -1) {
exceptions.cursorAvancadoNaoAberto(cursor, @1.first_line, (funcao === 'sql_fecharcursor'));
}
if (funcao === 'sql_proximo') {
exceptions.cursorAvancadoDentroEnquanto(cursor, @1.first_line, false);
}
}
// Tratando ordem correta das funções de SQL Avançado
if (funcao.indexOf('sql_') !== -1) {
exceptions.cursoresAvancadosEmOrdem(cursor, funcao, @1.first_line);
}
}
| EXECSQL STRING ';'
| VAPARA VARIAVEL ';'
;
chamada-funcao-aux
: variavel '(' (expressao ','?)* ')'
{ $$ = [$1, $3[0]]; }
;
/* Expressões */
comparacao
: expressao-aux-comparacao
{ $$ = [exceptions.flattenArray($1)]; }
| comparacao operadores-relacionais expressao-aux-comparacao-aux
{
$1.push(exceptions.flattenArray($3));
$$ = $1;
}
| '(' comparacao ')' (comparacao-aux)*
{
$2.push(exceptions.flattenArray($4));
$$ = $2;
}
;
comparacao-aux
: (OU|E) '(' comparacao ')'
{ $$ = exceptions.flattenArray($3); }
;
expressao-aux-comparacao-aux
: expressao-aux-comparacao
| '(' expressao-aux-comparacao ')'
;
/* Cópia quase completa de 'expressao' apenas para facilitar a construção de 'comparacao' */
expressao-aux-comparacao
: expressao-aux-comparacao '+' expressao-aux-comparacao
| expressao-aux-comparacao '-' expressao-aux-comparacao
| expressao-aux-comparacao '*' expressao-aux-comparacao
| expressao-aux-comparacao '/' expressao-aux-comparacao
| chamada-funcao-aux
| valor
;
expressao
: expressao '+' expressao
| expressao '-' expressao
| expressao '*' expressao
| expressao '/' expressao
| '(' expressao ')'
| valor
;
/* Misc. */
tipo
: tipo-aux
| VARIAVEL ('.' VARIAVEL)+
;
tipo-aux
: ALFA
| CURSOR
| DATA
| GRID
| NUMERO
| TABELA
| LISTA
;
valor
: NUM
| STRING
| CHAR
| variavel
| tipo-aux
;
variavel
: VARIAVEL
| VARIAVEL '.' VARIAVEL variavel-aux
{
var aux = $3.toLowerCase();
if (aux == 'abrircursor') {
exceptions.cursorJaAberto($1, @1.first_line);
}
else if (aux == 'proximo') {
exceptions.cursorDentroEnquanto($1, @1.first_line, false);
}
else if (aux == 'achou' || aux == 'fecharcursor' || aux == 'naoachou') {
exceptions.cursorNaoAberto($1, @1.first_line, aux == 'fecharcursor');
}
/* Se a variável for um cursor eu preciso concatenar tudo para pegar o nome do método SQL no futuro */
if (['abrircursor', 'proximo', 'achou', 'fecharcursor', 'naoachou', 'sql'].indexOf(aux) > 0) {
$$ = $1 + $2 + $3 + $4;
}
/* Do contrário eu só preciso do nome da variável comum */
else {
$$ = $1;
}
}
;
/* Não estou usando EBNF aqui porque não achei como capturar o valor de ('.' VARIAVEL)+ em $n */
variavel-aux
: '.' VARIAVEL variavel-aux
{ $$ = $1 + $2 + $3; }
| /* nada */
{ $$ = ''; }
;
operadores-relacionais
: '='
| '<>'
| '<'
| '<='
| '>'
| '>='
;
operadores-posfixos
: '++'
| '--'
;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment