Um parser em bash que identifica enums de um fonte Java

jeffquesadojeffque

but why

Roteiro

  • Por quê?
  • Estratégia de detecção de deriva
  • Gramática Java e autômato de pilha
  • Toque em gramática enum Dart
  • Conclusão

Era uma vez...

Uma enum Java

ela era inocente

e boa

funcionava para Java Web e GWT

Era uma vez...

Uma enum Java

e começou o processo de migrar o mobile...

vamos para Flutter...

e a mesma enum precisou aparecer em Flutter

... em código Dart...

Code drift

Duas pessoas em um caiaque a deriva

Code drift

  • Duas bases de código?
  • Um código vai prum lado, outro pro outro
  • Enums que deveriam ser acopladas?

    • Evolução independente 🤪

Evitar deriva

  • Single source of truth
  • QUEBRAR SE DERIVAR!!!!

Single source of truth

  • Geração de código
SEM TEMPO IRMÃO

Single source of truth

  • De java obter dart?
  • De dart obter java?
  • DSL neutra e gerar source java+dart?
  • E meus métodos Java?
  • Como configurar no lifecycle?

Quebrar se derivar

  • Meu gitlab-ci job sanity (apenas bash)
  • Ler as enums do Java
  • Ler as enums do Dart
  • Detectar mudanças
se tiver enumerados no lado dart não contemplados pelo lado java

Como detectar?

se tiver enumerados no lado dart não contemplados pelo lado java

...

enumerados dart subconjunto enumerados java

Como detectar?

  • todo elemento de enum_dart contido em enum_java
  • strings
  • se já tiver vetores de strings? loopar

Pertence, aleatório

#!/bin/bash
declare dart_enum_item="MONDAY" # tente SEXTOU, BOOM
declare -a java_enums=( MONDAY TUESDAY WEDNESDAY THIRSDAY FRIDAY SATURDAY SUNDAY )
declare found=false
for java_enum_item in "${java_enums[@]}"; do
if [ "$dart_enum_item" = "$java_enum_item" ]; then
found=true
break
fi
done
if ! $found; then
command_to_abort "Dart item '${dart_enum_item}' does not have java counterpart"
fi

Enumeração conhecida

Como detectar?

  • Subótimo
  • ~100 itens
  • Ordenar Java -> detecção precoce
  • Ordenar Dart -> detecção precoce

    • Precisa iterar de modo esperto
    • Tá bom só ordenar Java

Pertence, ordenado

#!/bin/bash
declare dart_enum_item="MONDAY" # tente SEXTOU, BOOM
declare -a java_enums=( FRIDAY MONDAY SATURDAY SUNDAY THIRSDAY TUESDAY WEDNESDAY )
declare found=false
for java_enum_item in "${java_enums[@]}"; do
if [ "$dart_enum_item" = "$java_enum_item" ]; then
found=true
break
elif [ "$dart_enum_item" '>' "$java_enum_item" ]; then
break
fi
done
if ! $found; then
command_to_abort "Dart item '${dart_enum_item}' does not have java counterpart"
fi

Enumeração já ordenada

Similar produção

local -a java_enums=( `extract-enum MyEnum.java | sort` )
local dart_enum_item
local found
for dart_enum_item in `extract-enum MyEnum.dart`; do
found=false
for java_enum_item in "${java_enums[@]}"; do
if [ "$dart_enum_item" = "$java_enum_item" ]; then
found=true
break
elif [ "$dart_enum_item" '>' "$java_enum_item" ]; then
break
fi
done
if ! $found; then
command_to_abort "Dart item '${dart_enum_item}' does not have java counterpart"
fi
done

Extração e ordenação enum java

Convenções úteis

  • apenas um elemento Java top level
  • compila (well formed)

    • java 8
  • apenas enum no arquivo Dart
  • arquivos acoplados anti-deriva?

    • em pares SEMPRE <java:dart>

Cadê Java?

  • Cenário real:

    • enum elemento top level
    • métodos sobrecarregados por enum
    • comentários
    • anotações em métodos
    • anotações em enums
    • métodos com strings
    • vários itens mesma linha
    • construtor

Enumeração simples

package com.github.jeffque;
public enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY
}

Os itens

Output?

  • SUNDAY
  • MONDAY
  • TUESDAY
  • WEDNESDAY
  • THURSDAY
  • FRIDAY
  • SATURDAY

Gramática Java?

  • Comentários de linha //
  • Comentários de bloco /* */
  • Comentários selvagens aparecem

Gramática Java (simplificada)

arq-java ==> preâmbulo elemento elementos
preâmbulo ==> pacote? imports
imports ==>
imports ==> import imports
import ==> <<import>> classe
import ==> <<import>> wildcard
import ==> <<import static>> classe
import ==> <<import static>> wildcard
elementos ==>
elementos ==> elemento elementos
elemento ==> modificador-acesso id-interface nome super-interface corpo-interface
elemento ==> modificador-acesso id-anotação nome super-anotação corpo-anotação
elemento ==> modificador-acesso id-classe nome super-classe corpo-classe
elemento ==> modificador-acesso id-enumeração nome super-enumeração corpo-enumeração
modificador-acesso ==> <<public>>
modificador-acesso ==> <<protected>>
modificador-acesso ==> <<private>>
modificador-acesso ==>
id-interface ==> <<interface>>
id-anotação ==> <<@interface>>
id-classe ==> <<class>>
id-enumeração ==> <<enum>>
corpo-interface ==> <<{>> vísceras-interface <<}>>
corpo-anotação ==> <<{>> vísceras-anotação <<}>>
corpo-classe ==> <<{>> vísceras-classe <<}>>
corpo-enumeração ==> <<{>> vísceras-enumeração <<}>>
vísceras-enumeração ==> lista-enumerada
vísceras-enumeração ==> lista-enumerada <<;>> vísceras-classe
lista-enumerada ==> elemento-enumeração <<,>> lista-enumerada
lista-enumerada ==> elemento-enumeração
lista-enumerada ==>
elemento-enumeração ==> nome args-construtor? subclasse?
elemento-interno ==> campo
elemento-interno ==> método
elemento-interno ==> construtor
elemento-interno ==> bloco-código
elemento-interno ==> bloco-código-estático
args-construtor ==> <<(>> args <<)>>
subclasse ==> <<{>> vísceras-classe <<}>>

Estrutura geral

Heurísticas meu caso

  • só vai passar enum

    • diferenciar classe/interface/etc?
  • "primeiro" {?

    • começar extrais enums
  • "primeiro" ;?

    • EOE
  • } fechando o "primeiro" {?

    • acabou mesmo aquivo java 🤷‍♂️

Heurística do { funciona?

arq-java ==> preâmbulo elemento elementos
preâmbulo ==> pacote? imports
imports ==>
imports ==> import imports
import ==> <<import>> classe
import ==> <<import>> wildcard
import ==> <<import static>> classe
import ==> <<import static>> wildcard
elementos ==>
elementos ==> elemento elementos
elemento ==> modificador-acesso id-interface nome super-interface corpo-interface
elemento ==> modificador-acesso id-anotação nome super-anotação corpo-anotação
elemento ==> modificador-acesso id-classe nome super-classe corpo-classe
elemento ==> modificador-acesso id-enumeração nome super-enumeração corpo-enumeração
modificador-acesso ==> <<public>>
modificador-acesso ==> <<protected>>
modificador-acesso ==> <<private>>
modificador-acesso ==>
id-interface ==> <<interface>>
id-anotação ==> <<@interface>>
id-classe ==> <<class>>
id-enumeração ==> <<enum>>
corpo-interface ==> <<{>> vísceras-interface <<}>>
corpo-anotação ==> <<{>> vísceras-anotação <<}>>
corpo-classe ==> <<{>> vísceras-classe <<}>>
corpo-enumeração ==> <<{>> vísceras-enumeração <<}>>
vísceras-enumeração ==> lista-enumerada
vísceras-enumeração ==> lista-enumerada <<;>> vísceras-classe
lista-enumerada ==> elemento-enumeração <<,>> lista-enumerada
lista-enumerada ==> elemento-enumeração
lista-enumerada ==>
elemento-enumeração ==> nome args-construtor? subclasse?
elemento-interno ==> campo
elemento-interno ==> método
elemento-interno ==> construtor
elemento-interno ==> bloco-código
elemento-interno ==> bloco-código-estático
args-construtor ==> <<(>> args <<)>>
subclasse ==> <<{>> vísceras-classe <<}>>

Preâmbulo + elemento

Heurística do ; funciona?

arq-java ==> preâmbulo elemento elementos
preâmbulo ==> pacote? imports
imports ==>
imports ==> import imports
import ==> <<import>> classe
import ==> <<import>> wildcard
import ==> <<import static>> classe
import ==> <<import static>> wildcard
elementos ==>
elementos ==> elemento elementos
elemento ==> modificador-acesso id-interface nome super-interface corpo-interface
elemento ==> modificador-acesso id-anotação nome super-anotação corpo-anotação
elemento ==> modificador-acesso id-classe nome super-classe corpo-classe
elemento ==> modificador-acesso id-enumeração nome super-enumeração corpo-enumeração
modificador-acesso ==> <<public>>
modificador-acesso ==> <<protected>>
modificador-acesso ==> <<private>>
modificador-acesso ==>
id-interface ==> <<interface>>
id-anotação ==> <<@interface>>
id-classe ==> <<class>>
id-enumeração ==> <<enum>>
corpo-interface ==> <<{>> vísceras-interface <<}>>
corpo-anotação ==> <<{>> vísceras-anotação <<}>>
corpo-classe ==> <<{>> vísceras-classe <<}>>
corpo-enumeração ==> <<{>> vísceras-enumeração <<}>>
vísceras-enumeração ==> lista-enumerada
vísceras-enumeração ==> lista-enumerada <<;>> vísceras-classe
lista-enumerada ==> elemento-enumeração <<,>> lista-enumerada
lista-enumerada ==> elemento-enumeração
lista-enumerada ==>
elemento-enumeração ==> nome args-construtor? subclasse?
elemento-interno ==> campo
elemento-interno ==> método
elemento-interno ==> construtor
elemento-interno ==> bloco-código
elemento-interno ==> bloco-código-estático
args-construtor ==> <<(>> args <<)>>
subclasse ==> <<{>> vísceras-classe <<}>>

Lista vs vísceras

Ao redor do elemento

  • subclasse

    • contido { entre chaves }
  • construtor

    • contido ( entre parênteses )

Construindo autômato

  • DFA por padrão
  • situações de pilha

    • +@ empilha símbolo @
    • /@ remove símbolo @
  • #eventos de escrita

    • #ID: char da enum
    • #FIM: quebra linha (se #ID antes)
  • EOE: end of enum

Autômato identificador de enums

inicial ==> `{` possíveis-enums
inicial ==> . inicial
possíveis-enums ==> [A-Za-z0-9_] possíveis-enums #ID
possíveis-enums ==> `;` EOE #FIM
possíveis-enums ==> `}` EOE #FIM
possíveis-enums ==> . possíveis-enums #FIM
inicial ==> `/` barra +"inicial"
possíveis-enums ==> `/` barra +"possíveis-enums" #FIM
barra ==> `/` comentário-linha
comentário-linha /"inicial" ==> `\r` inicial
comentário-linha /"inicial" ==> `\n` inicial
comentário-linha /"possíveis-enums" ==> `\r` possíveis-enums
comentário-linha /"possíveis-enums" ==> `\n` possíveis-enums
barra ==> `*` comentário-bloco
comentário-bloco ==> `*` comentário-bloco-star
comentário-bloco-star /"inicial" ==> `/` inicial
comentário-bloco-star /"possíveis-enums" ==> `/` possíveis-enums
comentário-bloco-star ==> `*` comentário-bloco-star
comentário-bloco-star ==> . comentário-bloco
barra /"inicial" ==> . inicial
barra /"possíveis-enums" ==> . possíveis-enums
possíveis-enums ==> `{` subclasse +"{E" #FIM
subclasse ==> `{` subclasse +"{"
subclasse /"{" ==> `}` subclasse
subclasse /"{E" ==> `}` possíveis-enums
possíveis-enums ==> `(` ctor +"(E" #FIM
ctor ==> `(` ctor +"("
ctor /"(" ==> `)` ctor
ctor /"(E" ==> `)` possíveis-enums
subclasse ==> `"` string-aspas +"subclasse"
subclasse ==> `'` string-apostrof +"subclasse"
string-aspas /"subclasse" ==> `"` subclasse
string-apostrof /"subclasse" ==> `'` subclasse
ctor ==> `"` string-aspas +"ctor"
ctor ==> `'` string-apostrof +"ctor"
string-aspas /"ctor" ==> `"` ctor
string-apostrof /"ctor" ==> `'` ctor
string-aspas ==> `\` string-aspas-escape
string-aspas-escape ==> . string-aspas
string-apostrof ==> `\` string-apostrof-escape
string-apostrof-escape ==> . string-apostrof
subclasse ==> . subclasse
ctor ==> . ctor
string-aspas ==> . string-aspas
string-apostrof ==> . string-apostrof
inicial ==> `(` ctor +"(I"
ctor /"(I" ==> `)` inicial
possíveis-enums ==> `@` annotation #FIM
annotation ==> [A-Za-z0-9_] annotation
annotation ==> `@` annotation
annotation ==> `(` ctor +"(E"

Qualquer coisa quebra ID

Enumeração simples

package com.github.jeffque;
public enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY
}

Os itens

Enumeração com construtor e campo

package com.github.jeffque;
public enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY;
private final int weekday;
Day() {
weekday = -1;
}
Day(int weekday) {
this.weekday = weekday;
}
}

Os itens

Complicando: comentários

Enumeração comentada

// comentário de linha
/* e o
de bloco */
package com.github.jeffque;
// comentário de linha {
/* e o
de bloco{ */
public enum Day {
SUNDAY /* um bloco *no meio */, MONDAY, TUESDAY, WEDNESDAY,
// quebrando a linha} */;
THURSDAY, FRIDAY, SATURDAY;
// oops quebrando a linha
private final int weekday;
Day() {
weekday = -1;
}
Day(int weekday) {
this.weekday = weekday;
}
/* passando aqui
com o meu bloco */
}

Os itens

Autômato identificador de enums

inicial ==> `{` possíveis-enums
inicial ==> . inicial
possíveis-enums ==> [A-Za-z0-9_] possíveis-enums #ID
possíveis-enums ==> `;` EOE #FIM
possíveis-enums ==> `}` EOE #FIM
possíveis-enums ==> . possíveis-enums #FIM
inicial ==> `/` barra +"inicial"
possíveis-enums ==> `/` barra +"possíveis-enums" #FIM
barra ==> `/` comentário-linha
comentário-linha /"inicial" ==> `\r` inicial
comentário-linha /"inicial" ==> `\n` inicial
comentário-linha /"possíveis-enums" ==> `\r` possíveis-enums
comentário-linha /"possíveis-enums" ==> `\n` possíveis-enums
barra ==> `*` comentário-bloco
comentário-bloco ==> `*` comentário-bloco-star
comentário-bloco-star /"inicial" ==> `/` inicial
comentário-bloco-star /"possíveis-enums" ==> `/` possíveis-enums
comentário-bloco-star ==> `*` comentário-bloco-star
comentário-bloco-star ==> . comentário-bloco
barra /"inicial" ==> . inicial
barra /"possíveis-enums" ==> . possíveis-enums
possíveis-enums ==> `{` subclasse +"{E" #FIM
subclasse ==> `{` subclasse +"{"
subclasse /"{" ==> `}` subclasse
subclasse /"{E" ==> `}` possíveis-enums
possíveis-enums ==> `(` ctor +"(E" #FIM
ctor ==> `(` ctor +"("
ctor /"(" ==> `)` ctor
ctor /"(E" ==> `)` possíveis-enums
subclasse ==> `"` string-aspas +"subclasse"
subclasse ==> `'` string-apostrof +"subclasse"
string-aspas /"subclasse" ==> `"` subclasse
string-apostrof /"subclasse" ==> `'` subclasse
ctor ==> `"` string-aspas +"ctor"
ctor ==> `'` string-apostrof +"ctor"
string-aspas /"ctor" ==> `"` ctor
string-apostrof /"ctor" ==> `'` ctor
string-aspas ==> `\` string-aspas-escape
string-aspas-escape ==> . string-aspas
string-apostrof ==> `\` string-apostrof-escape
string-apostrof-escape ==> . string-apostrof
subclasse ==> . subclasse
ctor ==> . ctor
string-aspas ==> . string-aspas
string-apostrof ==> . string-apostrof
inicial ==> `(` ctor +"(I"
ctor /"(I" ==> `)` inicial
possíveis-enums ==> `@` annotation #FIM
annotation ==> [A-Za-z0-9_] annotation
annotation ==> `@` annotation
annotation ==> `(` ctor +"(E"

Comentários de linha

E como tá em bash?

  • tratar tokens especiais

    • tokens de retorno estado anterior
  • não precisa estar dentro do DFA

Enumeração comentada

#!/bin/bash
leitura_comentario_linha() {
local CARACTER
while read -N1 CARACTER; do
if [ -z "$CARACTER" ] || [ "$CARACTER" = $'\r' ] ||
[ "$CARACTER" = $'\n' ]; then
return
fi
done
}
leitura_comentario_bloco() {
local CARACTER
local star=false
while read -N1 CARACTER; do
if [ "$CARACTER" = '*' ]; then
star=true
elif $star; then
if [ "$CARACTER" = '/' ]; then
return
fi
star=false
fi
done
}
estado=INICIAL
enum_lida=''
barra=false
while read -N1 CARACTER; do
case "$estado" in
INICIAL)
if [ "$CARACTER" = '{' ]; then
estado=POSSIVEIS_ENUM
fi
;;
POSSIVEIS_ENUM)
if [[ "$CARACTER" = [A-Za-z0-9_] ]]; then
enum_lida+="$CARACTER" #evento #ID
else
if [ -n "$enum_lida" ]; then
echo "$enum_lida" #evento #FIM
enum_lida=''
fi
if [ "$CARACTER" = '}' ] || [ "$CARACTER" = ';' ]; then
estado=EOE
fi
fi
;;
EOE)
break
;;
esac
if [ "$CARACTER" = / ]; then
if $barra; then
leitura_comentario_linha
barra=false
else
barra=true
fi
elif $barra; then
if [ "$CARACTER" = '*' ]; then
leitura_comentario_bloco
fi
barra=false
fi
done

Decisão char a char

Subclasse

  • @Override e outras brincadeiras
  • abre com {
  • fecha com } equivalente

    • pilha de {
    • desempilhado por }

Gramática Java (simplificada)

arq-java ==> preâmbulo elemento elementos
preâmbulo ==> pacote? imports
imports ==>
imports ==> import imports
import ==> <<import>> classe
import ==> <<import>> wildcard
import ==> <<import static>> classe
import ==> <<import static>> wildcard
elementos ==>
elementos ==> elemento elementos
elemento ==> modificador-acesso id-interface nome super-interface corpo-interface
elemento ==> modificador-acesso id-anotação nome super-anotação corpo-anotação
elemento ==> modificador-acesso id-classe nome super-classe corpo-classe
elemento ==> modificador-acesso id-enumeração nome super-enumeração corpo-enumeração
modificador-acesso ==> <<public>>
modificador-acesso ==> <<protected>>
modificador-acesso ==> <<private>>
modificador-acesso ==>
id-interface ==> <<interface>>
id-anotação ==> <<@interface>>
id-classe ==> <<class>>
id-enumeração ==> <<enum>>
corpo-interface ==> <<{>> vísceras-interface <<}>>
corpo-anotação ==> <<{>> vísceras-anotação <<}>>
corpo-classe ==> <<{>> vísceras-classe <<}>>
corpo-enumeração ==> <<{>> vísceras-enumeração <<}>>
vísceras-enumeração ==> lista-enumerada
vísceras-enumeração ==> lista-enumerada <<;>> vísceras-classe
lista-enumerada ==> elemento-enumeração <<,>> lista-enumerada
lista-enumerada ==> elemento-enumeração
lista-enumerada ==>
elemento-enumeração ==> nome args-construtor? subclasse?
elemento-interno ==> campo
elemento-interno ==> método
elemento-interno ==> construtor
elemento-interno ==> bloco-código
elemento-interno ==> bloco-código-estático
args-construtor ==> <<(>> args <<)>>
subclasse ==> <<{>> vísceras-classe <<}>>

Posição para "subclassear"

O que é um método?

  • acesso (private, public, etc)
  • static? strictfp? final?
  • generics
  • retorno
  • nome
  • args entre ( parênteses )
  • { CÓDIGO }

Código

  • auto-aninhado
  • estrutura de controle { CÓDIGO }

Heurísticas meu caso

compila, portanto...

  • pilha começa com {E
  • a cada { sintático, empilhar {
  • a cada } sintático, remover { ou {E da pilha
  • removeu {E => pilha vazia => fim do método

E chamada de construtor?

THURSDAY(5), FRIDAY(6) {

  @Override
  public String toString() {
    return "SEXTOOOOU!";
  }
}

chamada passando args?

  • elemento parametrizável

    • nome de função, ctor, anotação
  • ( argumentos separados por vírgulas )
  • arg: pode ser instância de classe anônima

Heurísticas meu caso

compila, portanto...

  • pilha começa com (E
  • a cada ( sintático, empilhar (
  • a cada ) sintático, remover ( ou (E da pilha
  • removeu (E => pilha vazia => fim dos argumentos

Abstraindo PDA

PDA: caracter PUSH vs caracter POP

  • pilha começa com PUSH-E
  • a cada PUSH sintático, empilhar PUSH
  • a cada POP sintático, remover PUSH ou PUSH-E da pilha
  • removeu PUSH-E => pilha vazia => fim da seção PDA

Argumentos? E strings?

  • Java 8
  • "([^\\"]|\\.)*"

    • começa com "
    • se não for " ou \, parte da string
    • se for \, \ e o próximo pertencem a string
    • termina com "

E chars?

  • Heurística: caso especial de string
  • Estou assumindo que compila...

Autômato identificador de enums

inicial ==> `{` possíveis-enums
inicial ==> . inicial
possíveis-enums ==> [A-Za-z0-9_] possíveis-enums #ID
possíveis-enums ==> `;` EOE #FIM
possíveis-enums ==> `}` EOE #FIM
possíveis-enums ==> . possíveis-enums #FIM
inicial ==> `/` barra +"inicial"
possíveis-enums ==> `/` barra +"possíveis-enums" #FIM
barra ==> `/` comentário-linha
comentário-linha /"inicial" ==> `\r` inicial
comentário-linha /"inicial" ==> `\n` inicial
comentário-linha /"possíveis-enums" ==> `\r` possíveis-enums
comentário-linha /"possíveis-enums" ==> `\n` possíveis-enums
barra ==> `*` comentário-bloco
comentário-bloco ==> `*` comentário-bloco-star
comentário-bloco-star /"inicial" ==> `/` inicial
comentário-bloco-star /"possíveis-enums" ==> `/` possíveis-enums
comentário-bloco-star ==> `*` comentário-bloco-star
comentário-bloco-star ==> . comentário-bloco
barra /"inicial" ==> . inicial
barra /"possíveis-enums" ==> . possíveis-enums
possíveis-enums ==> `{` subclasse +"{E" #FIM
subclasse ==> `{` subclasse +"{"
subclasse /"{" ==> `}` subclasse
subclasse /"{E" ==> `}` possíveis-enums
possíveis-enums ==> `(` ctor +"(E" #FIM
ctor ==> `(` ctor +"("
ctor /"(" ==> `)` ctor
ctor /"(E" ==> `)` possíveis-enums
subclasse ==> `"` string-aspas +"subclasse"
subclasse ==> `'` string-apostrof +"subclasse"
string-aspas /"subclasse" ==> `"` subclasse
string-apostrof /"subclasse" ==> `'` subclasse
ctor ==> `"` string-aspas +"ctor"
ctor ==> `'` string-apostrof +"ctor"
string-aspas /"ctor" ==> `"` ctor
string-apostrof /"ctor" ==> `'` ctor
string-aspas ==> `\` string-aspas-escape
string-aspas-escape ==> . string-aspas
string-apostrof ==> `\` string-apostrof-escape
string-apostrof-escape ==> . string-apostrof
subclasse ==> . subclasse
ctor ==> . ctor
string-aspas ==> . string-aspas
string-apostrof ==> . string-apostrof
inicial ==> `(` ctor +"(I"
ctor /"(I" ==> `)` inicial
possíveis-enums ==> `@` annotation #FIM
annotation ==> [A-Za-z0-9_] annotation
annotation ==> `@` annotation
annotation ==> `(` ctor +"(E"

"PDA" para bloco de código

E anotações?

  • dentro de subclasse/ctor

    • já tratada
  • anotando a enum como um todo

    • complexidade equiv de bloco de construtor
    • inicial => ( ctor +"inicial"
  • anotando item da enum

    • ignorar string depois do arroba

Autômato identificador de enums

inicial ==> `{` possíveis-enums
inicial ==> . inicial
possíveis-enums ==> [A-Za-z0-9_] possíveis-enums #ID
possíveis-enums ==> `;` EOE #FIM
possíveis-enums ==> `}` EOE #FIM
possíveis-enums ==> . possíveis-enums #FIM
inicial ==> `/` barra +"inicial"
possíveis-enums ==> `/` barra +"possíveis-enums" #FIM
barra ==> `/` comentário-linha
comentário-linha /"inicial" ==> `\r` inicial
comentário-linha /"inicial" ==> `\n` inicial
comentário-linha /"possíveis-enums" ==> `\r` possíveis-enums
comentário-linha /"possíveis-enums" ==> `\n` possíveis-enums
barra ==> `*` comentário-bloco
comentário-bloco ==> `*` comentário-bloco-star
comentário-bloco-star /"inicial" ==> `/` inicial
comentário-bloco-star /"possíveis-enums" ==> `/` possíveis-enums
comentário-bloco-star ==> `*` comentário-bloco-star
comentário-bloco-star ==> . comentário-bloco
barra /"inicial" ==> . inicial
barra /"possíveis-enums" ==> . possíveis-enums
possíveis-enums ==> `{` subclasse +"{E" #FIM
subclasse ==> `{` subclasse +"{"
subclasse /"{" ==> `}` subclasse
subclasse /"{E" ==> `}` possíveis-enums
possíveis-enums ==> `(` ctor +"(E" #FIM
ctor ==> `(` ctor +"("
ctor /"(" ==> `)` ctor
ctor /"(E" ==> `)` possíveis-enums
subclasse ==> `"` string-aspas +"subclasse"
subclasse ==> `'` string-apostrof +"subclasse"
string-aspas /"subclasse" ==> `"` subclasse
string-apostrof /"subclasse" ==> `'` subclasse
ctor ==> `"` string-aspas +"ctor"
ctor ==> `'` string-apostrof +"ctor"
string-aspas /"ctor" ==> `"` ctor
string-apostrof /"ctor" ==> `'` ctor
string-aspas ==> `\` string-aspas-escape
string-aspas-escape ==> . string-aspas
string-apostrof ==> `\` string-apostrof-escape
string-apostrof-escape ==> . string-apostrof
subclasse ==> . subclasse
ctor ==> . ctor
string-aspas ==> . string-aspas
string-apostrof ==> . string-apostrof
inicial ==> `(` ctor +"(I"
ctor /"(I" ==> `)` inicial
possíveis-enums ==> `@` annotation #FIM
annotation ==> [A-Za-z0-9_] annotation
annotation ==> `@` annotation
annotation ==> `(` ctor +"(E"

Complexidades adicionais annotation

Enumeração completa

#!/bin/bash
leitura_comentario_linha() {
local CARACTER
while read -N1 CARACTER; do
if [ -z "$CARACTER" ] || [ "$CARACTER" = $'\r' ] ||
[ "$CARACTER" = $'\n' ]; then
return
fi
done
}
leitura_comentario_bloco() {
local CARACTER
local star=false
while read -N1 CARACTER; do
if [ "$CARACTER" = '*' ]; then
star=true
elif $star; then
if [ "$CARACTER" = '/' ]; then
return
fi
star=false
fi
done
}
leitura_string() {
local CARACTER
local ASPAS="$1"
while read -rN1 CARACTER; do
if [ "$CARACTER" = '\' ]; then
read -N1 CARACTER
elif [ "$CARACTER" = "$ASPAS" ]; then
return
fi
done
}
simple_pushdown_automata() {
local -r OPEN="$1" CLOSE="$2"
local -i cnt=0
local CARACTER
local barra=false
while read -N1 CARACTER; do
case "$CARACTER" in
"$OPEN")
cnt+=1 #empilha PUSH
barra=false
;;
"$CLOSE")
if [ $cnt = 0 ]; then
return #removeu PUSH-E
fi
cnt+=-1 #desempilha PUSH
barra=false
;;
/)
if $barra; then
leitura_comentario_linha
barra=false
else
barra=true;
fi
;;
'"'|"'")
leitura_string "$CARACTER"
;;
'*')
if $barra; then
leitura_comentario_bloco
barra=false
fi
;;
*)
barra=false
;;
esac
done
}
estado=INICIAL
enum_lida=''
barra=false
while read -N1 CARACTER; do
case "$estado" in
INICIAL)
if [ "$CARACTER" = '{' ]; then
estado=POSSIVEIS_ENUM
elif [ "$CARACTER" = '(' ]; then # só acontece com anotação
simple_pushdown_automata '(' ')'
fi
;;
POSSIVEIS_ENUM)
if [[ "$CARACTER" = [A-Za-z0-9_] ]]; then
enum_lida+="$CARACTER" #evento #ID
else
if [ -n "$enum_lida" ]; then
echo "$enum_lida" #evento #FIM
enum_lida=''
fi
if [ "$CARACTER" = { ]; then
simple_pushdown_automata { }
elif [ "$CARACTER" = '(' ]; then
simple_pushdown_automata '(' ')'
elif [ "$CARACTER" = '}' ] || [ "$CARACTER" = ';' ]; then
estado=EOE
elif [ "$CARACTER" = '@' ]; then
estado=ANNOTATION
fi
fi
;;
ANNOTATION)
if [[ "$CARACTER" = [A-Za-z0-9_] ]]; then
# não faz nada, identificador da annotation
else
# saiu da annotation ou falta só parametrizar ela
estado=POSSIVEIS_ENUM
if [ "$CARACTER" = '(' ]; then
simple_pushdown_automata '(' ')'
elif [ "$CARACTER" = '@' ]; then
# caiu em nova annotation
estado=ANNOTATION
fi
fi
;;
EOE)
break
;;
esac
if [ "$CARACTER" = / ]; then
if $barra; then
leitura_comentario_linha
barra=false
else
barra=true
fi
elif $barra; then
if [ "$CARACTER" = '*' ]; then
leitura_comentario_bloco
fi
barra=false
fi
done

Decisão char a char

E enumeração dart?

  • "caso especial" da enum Java
  • considerando todas as Heurísticas

    • compila
    • elemento único no arquivo

Enumeração em dart

enum Day {
SUNDAY,
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY
}

Esquema aproximado da enum de dart

enum ==> id-enumeração nome corpo-enumeração
id-enumeração ==> <<enum>>
corpo-enumeração ==> `{` lista-enumerada `}`
lista-enumerada ==> elemento-enumeração <<,>> lista-enumerada
lista-enumerada ==> elemento-enumeração
elemento-enumeração ==> nome

Conclusão

  • Code drift de enums 😤

    • Source generation
    • Acoplamento
  • Acoplar, com runtime 😁
  • Acoplar, sem runtime...

    • Possível 🙄

Conclusão

  • Conheça a lang
  • Heurísticas facilitam

    • eg: compila/código válido
  • Prepare-se para overengineering
  • Diverta-se com gramática e autômato

obrigado

blog: https://computaria.gitlab.io/blog/

source: https://github.com/jeffque/bash-java-enums-parser-butwhyyy

talk: https://jeffque.github.io/bash-java-enums-parser-butwhyyy/