Analizza in modo ricorsivo senza ricorrere a brutti schemi di progettazione

3

Attualmente sto costruendo un parser di pattern all'uncinetto in Java, e ho riscontrato qualche problema. Chiamerò la lingua utilizzata per l'input Crochet Pattern Code ( CPC ).

Ho una riscrittura piuttosto ampia sul contesto, alla fine viene aggiunta. Probabilmente non ti servirà tutto, quindi inizierò con:

Il problema

Il problema che sto affrontando è che voglio aggiungere funzioni, come expandRepetitions , per modificare quanto segue:

1 x repeat(3) - RepeatSymbol(3)
1 x chain - StitchSymbol(Stitch.Type.ch)

Into:

3 x chain - StitchSymbol(Stitch.Type.ch)

Questa è una rappresentazione di ArrayList che prima contiene RepeatSymbol(3) e Chain , e successivamente contiene tre volte StitchSymbol(Stitch.Type.ch) .

Voglio anche essere in grado di trasformare 2 {2 {c}} in:

1 x repeat(2) - RepeatSymbol(2)
1 x SymbolGroup {2{ch}}

E in:

4 x chain - StitchSymbol(Stitch.Type.ch)

Tuttavia, l'unico modo per applicare questa azione non solo al ArrayList<Symbol> corrente, ma anche al ArrayList<Symbol> in qualsiasi GroupSymbol . Finora, l'unica cosa che ho trovato è questa:

public class Parser {

    ...

    public static expandRepetitions(ArrayList<Symbol> input) {
        int pos = 0; 

        while (pos < input.size()) {
             /* on encounter with RepeatSimbol, simplify */
             ...
             /* on encounter with GroupSymbol, do this: */
             input.get(pos).expandRepetitions();
        }
    }

    ...
}

public class GroupSymbol {

    ...

    public void expandRepetitions() {
        Parser.expandRepetitions(this.symbolList);
    }

    ...

}

Tuttavia, questa sembra una soluzione terribile. Penso che sia molto brutto dover integrare il GroupSymbol così strettamente con Parser , e pensare che ci dovrebbe essere un modo migliore possibile, ma non possiamo pensarci.

La domanda

Come posso migliorare la struttura di GroupSymbol (un contenitore ArrayList<Symbol> ) e Parser ? Parser deve applicare azioni su ArrayList<Symbol> , che può contenere GroupSymbol s, che può contenere GroupSymbol s, e così via, e queste azioni devono essere applicate all'intero contenuto di questi GroupSymbol s .

Ad esempio, queste azioni includeranno l'espansione delle ripetizioni e la semplificazione di un elenco di simboli per non includere gruppi e ripetizioni.

Sfondo e amp; dettagli del parser

Sto costruendo un linguaggio formale per l'uncinetto, un parser per questa lingua e un visualizzatore. Al momento sono bloccato al parser, in particolare la parte che semplifica le costruzioni con raggruppamenti e ripetizioni in un elenco semplice.

Dichiarazione di non responsabilità: non ho ancora creato un parser, un compilatore, un interprete o una lingua e ho fatto un passo avanti fino a questo punto. Se vedi (altri) difetti fatali nel mio parser e / o nella lingua, ti preghiamo di indicarli e / o indicarmi le risorse per conoscere questo tipo di cose.

Il parser è strutturato come segue:

  • Un oggetto Parser con funzioni statiche.

  • Un'interfaccia Symbol e una serie di tipi diversi di implementazioni Symbol , ad esempio:

    • StitchSymbol , che ha la proprietà che rappresenta un certo Stitch.Type

    • GroupSymbol , che memorizza un ArrayList<Symbol> e può contenere altre istanze di GroupSymbol .

    • RepeatSymbol , che memorizza un valore int che rappresenta un numero di ripetizioni dell'elemento successivo (un punto, un'operazione come un salto o un gruppo).

  • Una funzione preprocess per trasformare una String in una ArrayList di (base) Symbols . Questo viene fatto usando un semplice parser di macchine a stati finiti.

  • Alcune funzioni per rendere questi elenchi di simboli più utilizzabili: uno per modificare un BeginGroupSymbol, un gruppo di simboli e un EndGroupSymbol in un singolo GroupSymbol che contiene il gruppo, uno per espandere le ripetizioni, uno per semplificare l'elenco a semplici istruzioni.

Ad esempio, in preprocess :

  • ch è trasformato in un StitchSymbol con Stitch.Type impostato su 'ch' (una catena, per chi ha familiarità con l'uncinetto).

  • { diventa un BeginGroupSymbol.

Un esempio di input e output:

10ch<{3sc2{chskdcchsk}}fo

si ottiene:

1 x repeat(10) - RepeatSymbol(10)
1 x chain - StitchSymbol(Stitch.Type.ch)
1 x reverse - ReverseSymbol()
1 x begin group - BeginGroupSymbol()
1 x repeat(3) - RepeatSymbol(3)
1 x single crochet - StitchSymbol(Stitch.Type.sc)
1 x repeat(2) - RepeatSymbol(3)
1 x begin group - BeginGroupSymbol()
1 x chain - StitchSymbol(Stitch.Type.ch)
1 x skip - SkipSymbol()
1 x double crochet - StitchSymbol(Stitch.Type.dc)
1 x chain - StitchSymbol(Stitch.Type.ch)
1 x skip - SkipSymbol()
2 x end group - EndGroupSymbol()
1 x fasten off - StitchSymbol(Stitch.Type.fo)

Successivamente, collapseGroups lo trasforma in:

1 x repeat(10) - RepeatSymbol(10)
1 x chain - StitchSymbol(Stitch.Type.ch)
1 x reverse - ReverseSymbol()
1 x GroupSymbol {3sc2{chskdcchsk}}
1 x fasten off - StitchSymbol(Stitch.Type.fo)

La stringa di CPC dopo che GroupSymbol è stata costruita dagli elementi in ArrayList all'interno di GroupSymbol . Potrebbe essere esteso a:

1 x repeat(3) - RepeatSymbol(3)
1 x single crochet - StitchSymbol(Stitch.Type.sc)
1 x repeat(2) - RepeatSymbol(2)
1 x GroupSymbol {chskdcchsk}

Questo GroupSymbol interno potrebbe essere ulteriormente ampliato, ma penso che l'immagine sia chiara.

Per coloro che lavorano all'uncinetto, ecco una spiegazione visiva:

Ecomeapparenellalana:

    
posta Willem 09.04.2015 - 00:07
fonte

1 risposta

4

Quello che hai scritto è davvero un lexer . I parser ti forniscono la struttura nidificata dal tuo input, mentre i lexer danno semplicemente una sequenza di simboli. Probabilmente hai passato molto tempo a reinventare la ruota, quando potevi usare un parser generator come antlr per fare gran parte del lavoro. Ad esempio, mi ci sono voluti circa 5 minuti per trovare la seguente grammatica di base per il tuo esempio. Funziona con il generatore di parser javascript pegjs :

start
  = pattern

pattern
  = symbol*

symbol
  = integer symbol / stitch / "{" pattern "}"

stitch
  = "ch" / "<" / "sc" / "sk" / "dc" / "fo"

integer
  = digits:[0-9]+ { return parseInt(digits.join(""), 10); }

Genera il seguente output strutturato:

[
   [
      10,
      "ch"
   ],
   "<",
   [
      "{",
      [
         [
            3,
            "sc"
         ],
         [
            2,
            [
               "{",
               [
                  "ch",
                  "sk",
                  "dc",
                  "ch",
                  "sk"
               ],
               "}"
            ]
         ]
      ],
      "}"
   ],
   "fo"
]

Questo tipo di modulo è molto più semplice per eseguire qualsiasi tipo di trasformazione tu abbia bisogno. I gruppi sono già nidificati correttamente. Il conteggio delle ripetizioni è già associato correttamente a ciò che dovrebbe ripetersi raggruppandosi all'interno della stessa matrice. Di nuovo, questo è 5 minuti di lavoro, e non ho nemmeno provato ad aggiungere semantica. Puoi renderlo molto più pulito e più utile con poco sforzo.

    
risposta data 09.04.2015 - 03:05
fonte

Leggi altre domande sui tag