2010-09-29 7 views
6

Sto cercando di capire come analizzare una stringa in questo formato in una struttura ad albero come dati di profondità arbitraria.Scorretta stringa in una struttura ad albero?

"{{Hello big|Hi|Hey} {world|earth}|{Goodbye|farewell} {planet|rock|globe{.|!}}}" 

[[["Hello big" "Hi" "Hey"] 
    ["world" "earth"]] 
[["Goodbye" "farewell"] 
    ["planet" "rock" "globe" ["." 
          "!"]]]] 

Ho provato a giocare con alcune espressioni regolari per questo (come # "{([^ {}] *)}"), ma tutto quello che ho provato sembra "appiattire" l'albero in una grande lista di liste. Potrei avvicinarmi a questo da un angolo sbagliato, o forse una regex non è lo strumento giusto per il lavoro.

Grazie per il vostro aiuto!

risposta

9

Non utilizzare espressioni regolari per questa attività. Un metodo più semplice sarebbe quello di descrivere la stringa con una grammatica (BNF o EBNF) e quindi scrivere un parser per analizzare la stringa in base alla grammatica. È possibile generare un albero di analisi da EBNF e BNF e quindi si finisce con una struttura ad albero.

Si può iniziare con qualcosa di simile:

element  ::= element-type, { ["|"], element-type } 
element-type ::= primitive | "{", element, "}" 
primitive ::= symbol | word 
symbol  ::= "." | "!" 
word   ::= character { character } 
character ::= "a" | "b" | ... | "z" 

Nota: ho scritto questo in fretta, e quindi potrebbe non essere del tutto corretto. Ma dovrebbe darti un'idea.

+1

Quindi dopo aver avuto quella grammatica, è necessario utilizzare un generatore di parser per generare un parser basato su questa grammatica, non è vero? Inoltre, il parser dovrebbe essere alimentato con una frase e quindi l'albero potrebbe essere ceduto, no? – bikashg

+1

@Bikash - Sì e No. È possibile * utilizzare un generatore di parser (come yacc o bisonte) se lo si desidera, oppure è possibile scrivere il proprio parser ricorsivo-discendente (è straordinariamente semplice). Se usi yacc o bison, devi scrivere azioni che costruiranno effettivamente l'albero. Non penso che yacc/bison ti dia l'albero da solo. Semplicemente riconoscono la grammatica. –

3

se volete un trucco veloce:

  • sostituire i caratteri {con [
  • sostituire i caratteri} con]
  • sostituire l'| caratteri con spazi
  • spero che tu non entri con gli spazi.

read in modo che si presenti come matrici annidate.

ps: Sono d'accordo che un reg-ex non può farlo.

pss: set * read-eval * su false (non si vuole l'ingresso di esecuzione è di per sé)

+0

La stringa di esempio in realtà include uno spazio in uno dei segmenti. – Rayne

+0

@Rayne: è stato modificato in. L'OP non ha incluso lo spazio in nessuna delle stringhe foglia risultanti. – aschepler

+0

Oh. Stavo anche considerando questa soluzione, fino a quando non ho visto lo spazio. Poi ho pianto per dormire. – Rayne

4

cercando di abbinare il tutto con una sola espressione regolare non sta per arrivare troppo lontano , poiché le espressioni regolari generano al massimo un elenco di posizioni di sottostringa corrispondenti, nessuna struttura ad albero. Vuoi un lexer o una grammatica che faccia qualcosa del genere:

Dividi l'input in token - pezzi atomici come '{', '|', e 'world', quindi elabora quei token in ordine. Inizia con un albero vuoto con un singolo nodo radice.

Ogni volta che si trova {, creare e passare a un nodo figlio.

Ogni volta che trovi |, crea e vai a un nodo fratello.

Ogni volta che trovi }, vai al nodo genitore.

Ogni volta che trovi una parola, metti quella parola nel nodo foglia corrente.

+2

Come si risolve il caso '{{text} {text}}'? Penso che la sua stringa sia un po 'ambigua ... tutti i nodi fratelli dovrebbero essere delimitati con "|" –

+0

Sì, ci sono alcuni punti di confusione nell'esempio. Assomiglia a '} {' tra Hey e world e il '} | {' tra earth e Goodbye causano relazioni simili a fratelli a diverse profondità nell'albero. Potrei solo intuire il motivo per cui è così. (Un altro problema ho notato con il mio algoritmo: cosa succede se {è giusto dopo una parola, come per 'globe'?) Quindi questa non è una soluzione completa, ma "qualcosa come" dovrebbe essere adattabile per risolvere questo tipo di problema. – aschepler

+0

Yup ha un senso :) –

1

È possibile utilizzare amotoen per costruire la grammatica e analizzare questo:

(ns pegg.core 
    (:gen-class) 
    (:use 
    (com.lithinos.amotoen 
    core string-wrapper)) 
    (:use clojure.contrib.pprint)) 

(def input "{{Hello big|Hi|Hey} {world|earth}|{Goodbye|farewell} {planet|rock|globe{.|!}}}") 

(def grammar 
    { 
     :Start :List 
     :ws #"^[ \n\r\t]*" 
     :Sep "|" 
     :String #"^[A-Za-z !.]+" 
     :Item '(| :String :List) 
     :Items [:Item '(+ [:Sep :Item])] 
     :List [:ws "{" '(* (| :Items :Item)) "}" :ws] 
     }) 

(def parser (create-parser grammar)) 

(defn parse 
    [^String input] 
    (validate grammar) 
    (pprint (parser (wrap-string input)))) 

Risultato:

pegg.core> (parse input) 
{:List [{:ws ""} "{" ({:Item {:List [{:ws ""} "{" ({:Items [{:Item {:String "Hello big"}} ([{:Sep "|"} {:Item {:String "Hi"}}] [{:Sep "|"} {:Item {:String "Hey"}}])]}) "}" {:ws " "}]}} {:Items [{:Item {:List [{:ws ""} "{" ({:Items [{:Item {:String "world"}} ([{:Sep "|"} {:Item {:String "earth"}}])]}) "}" {:ws ""}]}} ([{:Sep "|"} {:Item {:List [{:ws ""} "{" ({:Items [{:Item {:String "Goodbye"}} ([{:Sep "|"} {:Item {:String "farewell"}}])]}) "}" {:ws " "}]}}])]} {:Item {:List [{:ws ""} "{" ({:Items [{:Item {:String "planet"}} ([{:Sep "|"} {:Item {:String "rock"}}] [{:Sep "|"} {:Item {:String "globe"}}])]} {:Item {:List [{:ws ""} "{" ({:Items [{:Item {:String "."}} ([{:Sep "|"} {:Item {:String "!"}}])]}) "}" {:ws ""}]}}) "}" {:ws ""}]}}) "}" {:ws ""}]} 

P.S. Questa è una delle mie prime grammatiche di peg e può essere migliore. Vedi anche http://en.wikipedia.org/wiki/Parsing_expression_grammar