Ist Shenma ein "Interpreter -Modus"?
Öffnen wir zuerst den "GoF" und schauen uns die Definition an:
Definieren Sie bei einer Sprache eine Darstellung seiner Grammatik und definieren Sie einen Interpreter, der diese Darstellung verwendet, um Sätze in der Sprache zu interpretieren.
Vor Beginn muss ich noch mehrere Konzepte populär machen:
Abstrakter Syntaxbaum:
Das Interpreter -Muster erklärt nicht, wie ein abstrakter Syntaxbaum erstellt wird. Es beinhaltet keine grammatikalische Analyse. Ein abstrakter Syntaxbaum kann durch ein Tabellen-gesteuerter Syntaxanalyseprogramm oder durch handgeschriebenes (normalerweise rekursives absteigendes) Syntaxanalyseprogramm oder direkt vom Client erstellt werden.
Parser:
Es bezieht sich auf ein Programm, das die Ausdrücke beschreibt, die vom Client -Anruf erforderlich sind, und bildet nach der Parsen einen abstrakten Syntaxbaum.
Interpreter:
Bezieht sich auf ein Programm, das den abstrakten Syntaxbaum erklärt und die entsprechenden Funktionen jedes Knotens ausführt.
Eine wichtige Voraussetzung für die Verwendung des Interpreter -Musters besteht darin, eine Reihe von grammatikalischen Regeln zu definieren, die auch als Grammatik bezeichnet werden. Unabhängig davon, ob die Regeln dieser Grammatik einfach oder komplex sind, müssen diese Regeln einbezogen werden, da der Interpreter -Modus entsprechende Funktionen analysieren und entsprechend ausführen soll.
Schauen wir uns das Strukturdiagramm und die Beschreibung des Interpreter -Modus an:
AbstractExpression: Definiert die Schnittstelle des Dolmetschers und stimmt der Interpretationsoperation des Interpreters zu.
TerminalExpression: Terminalpression, verwendet, um Operationen im Zusammenhang mit dem Terminator in Syntaxregeln zu implementieren, enthält keine anderen Interpreten mehr. Wenn das Kombinationsmuster verwendet wird, um einen abstrakten Syntaxbaum zu erstellen, entspricht es einem Blattobjekt im Kombinationsmuster, und es kann mehrere Terminator -Dolmetscher geben.
Nicht terminalexpression: ein nicht terminaler Dolmetscher, der zur Implementierung nicht terminalbezogener Operationen in Syntaxregeln verwendet wird. Normalerweise entspricht ein Dolmetscher einer Syntaxregel und kann andere Dolmetscher enthalten. Wenn das Zusammensetzungsmuster verwendet wird, um einen abstrakten Syntaxbaum zu erstellen, entspricht es einem Kombinationsobjekt im Zusammensetzungsmuster. Es kann mehrere nicht terminale Dolmetscher geben.
Kontext: Kontext enthält normalerweise Daten, die von jedem Interpreter oder jeder öffentlichen Funktionen erforderlich sind.
Kunde: Ein Kunde bezieht sich auf einen Kunden, der einen Dolmetscher verwendet. Normalerweise werden Ausdrücke, die gemäß der Syntax der Sprache hergestellt werden, in einen abstrakten Syntaxbaum umgewandelt, der vom Interpreter -Objekt beschrieben wird, und dann wird eine Erklärungsoperation aufgerufen.
Hier verwenden wir ein XML -Beispiel, um das Interpreter -Muster zu verstehen:
Zunächst müssen wir eine einfache Grammatik für den Ausdruck entwerfen. Verwenden Sie zur allgemeinen Zwecke das Root, um das Stammelement, ABC usw. darzustellen, um das Element darzustellen. Ein einfaches XML ist wie folgt:
Die Codekopie lautet wie folgt:
<? xml Version = "1.0" coding = "utf-8">
<root id = "rootId">
<a>
<b>
<c name = "testc"> 12345 </c>
<d id = "1"> d1 </d>
<d id = "2"> d2 </d>
<d id = "3"> d3 </d>
<d id = "4"> d4 </d>
</b>
</a>
</root>
Die Grammatik des Konventionsausdrucks lautet wie folgt:
1. Erhalten Sie den Wert eines einzelnen Elements: Starten Sie vom Stammelement und bis zu dem Element, das Sie den Wert erhalten möchten. Die Mitte des Elements wird durch "/" und nein "/" vor dem Stammelement hinzugefügt. Beispielsweise bedeutet der Ausdruck "root/a/b/c", die Werte von Element C unter dem Stammelement, Element A, Element B und Element c zu erhalten.
2. Erhalten Sie den Wert des Attributs eines einzelnen Elements: Natürlich gibt es mehrere Attribute. Das Attribut, um den Wert zu erhalten, muss das Attribut des letzten Elements des Ausdrucks sein. Hinzufügen "." Nach dem letzten Element und dann den Namen des Attributs hinzufügen. Beispielsweise bedeutet der Ausdruck "root/a/b/c.name", den Wert des Namensattributs des Stammelements, Element A, Element B, Element c zu erhalten.
3. Nehmen Sie den Wert desselben Elementnamens, natürlich gibt es mehrere Elemente. Das Element, um den Wert zu erhalten, muss das letzte Element des Ausdrucks sein und nach dem letzten Element "$" hinzufügen. Beispielsweise repräsentiert der Ausdruck "root/a/b/d $" die Sammlung von Werten mehrerer Delemente unter dem Stammelement, unter dem A -Element und unter dem B -Element.
4. Erhalten Sie den Wert des Attributs mit demselben Elementnamen. Natürlich gibt es mehrere: Das Element, um den Attributwert zu erhalten, muss das letzte Element des Ausdrucks sein, und fügen Sie nach dem letzten Element "$" hinzu. Beispielsweise repräsentiert der Ausdruck "root/a/b/d $ .id $" die Sammlung von Werten mehrerer D -Elemente -ID -Attribute unter dem Stammelement, unter dem A -Element und unter dem B -Element.
Das obige XML, entsprechend dem abstrakten Syntaxbaum, und die mögliche Struktur ist in der Abbildung dargestellt:
Schauen wir uns den folgenden Code an:
1. Definieren Sie den Kontext:
Die Codekopie lautet wie folgt:
/**
* Kontext, verwendet, um einige globale Informationen zu enthalten, die vom Dolmetscher erforderlich sind
* @param {string} filepathname [Pfad und Name des XML, der gelesen werden muss]
*/
Funktionskontext (filepathname) {
// das vorherige verarbeitete Element
this.preele = null;
// XML -Dokumentobjekt
this.document = xmlutil.getroot (filepathname);
}
Context.prototype = {
// den Kontext neu initialisieren
Reinit: function () {
this.preele = null;
},
/**
* Methoden für die öffentliche Verwendung jedes Ausdrucks
* Holen Sie sich das aktuelle Element gemäß dem Namen des übergeordneten Elements und des aktuellen Elements
* @param {element} pele [übergeordnetes Element]
* @param {String} Elename [Aktueller Elementname]
* @return {Element | null} [aktuelles Element gefunden]
*/
GetNowele: Funktion (Pele, Elename) {
var tempnodelist = pele.childnodes;
var nowele;
für (var i = 0, len = tempnodelist.length; i <len; i ++) {
if ((nowee = tempnodelist [i]). nodetype === 1)
if (nowee.nodename === Elename)
kehre jetzt zurück;
}
null zurückkehren;
},
getPreele: function () {
kehre diese.preele zurück;
},
setPreele: function (preele) {
this.preele = preele;
},
getDocument: function () {
Rückgabe dieses Dokuments;
}
};
Im Zusammenhang habe ich ein Tool -Objekt XMLutil verwendet, um XMLDOM zu erhalten. Unten verwende ich Dompaser von DOM3. Einige Browser unterstützen es möglicherweise nicht. Bitte verwenden Sie den Basisbrowser:
Die Codekopie lautet wie folgt:
// Werkzeugobjekt
// XML analysieren, um das entsprechende Dokumentobjekt zu erhalten
var xmlutil = {
getroot: function (filepathname) {
var parser = new domparser ();
var xmldom = parser.parsefromString ('<root id = "rootId"> <a> <b> <c name = "testc"> 12345 </c> <d id = "1"> d1 </d> <d id = "2"> d2 </d> <d id = "3"> d3 </d> </d> </d> </d> </d> </d> < id = "4"> d4 </d> </b> </a> </root> ',' text/xml ');
Return Xmldom;
}
};
Hier ist der Code für den Interpreter:
Die Codekopie lautet wie folgt:
/**
* Elemente werden als Dolmetscher verwendet, die Nicht-Terminals entsprechen, um Zwischenelemente zu interpretieren und auszuführen
* @param {String} Elename [Name des Elements]
*/
FunktionselementExpression (Elename) {
this.eles = [];
this.elename = Elename;
}
ElementExpression.prototype = {
Addele: Funktion (Elename) {
this.eles.push (Elename);
zurückkehren;
},
REMELELE: Funktion (ele) {
für (var i = 0, len = this.eles.length; i <len; i ++) {
if (ele === this.eles [i])
this.eles.splice (i--, 1);
}
zurückkehren;
},
interpretieren: Funktion (Kontext) {
// Nehmen Sie zuerst das aktuelle Element im Kontext als übergeordnetes Element heraus
// Ermitteln Sie das XML -Element, das dem aktuellen Elementnamen entspricht, und setzen Sie es auf den Kontext zurück
var pele = context.getPreele ();
if (! pele) {
// zeigt an, dass das Stammelement jetzt erhalten wird
context.setPreele (context.getDocument (). documentElement);
} anders {
// Erhalten Sie das aktuelle Element basierend auf dem Namen des übergeordneten Elements und dem zu durchsuchenden Element
var nowele = context.getNowele (pele, this.elename);
// Steck das aktuell abgerufene Element in den Kontext
context.setpreele (nowee);
}
var ss;
// Schleife, um die Interpretation der Methode des untergeordneten Elements aufzurufen
für (var i = 0, len = this.eles.length; i <len; i ++) {
ss = this.eles [i] .interpret (Kontext);
}
// Gibt das Erklärungsergebnis des letzten Dolmetschers zurück. Im Allgemeinen ist der letzte Dolmetscher der Terminator -Dolmetscher.
Rückkehr SS;
}
};
/**
* Elemente werden als Interpreter verwendet, der dem Terminator entspricht
* @param {String} Name [Name des Elements]
*/
Funktion elementterminalexpression (Name) {
this.elename = name;
}
ElementDerminalexpression.prototype = {
interpretieren: Funktion (Kontext) {
var pele = context.getPreele ();
var ele = null;
if (! pele) {
ele = context.getDocument (). documentElement;
} anders {
ele = context.getNowele (pele, this.elename);
context.setpreele (ele);
}
// Erhalten Sie den Wert des Elements
return ele.firstchild.nodeValue;
}
};
/**
* Das Attribut wird als Interpreter verwendet, der dem Terminator entspricht
* @param {string} propname [Name des Attributs]
*/
Funktion PropertyterminalExpression (PropName) {
this.PropName = propname;
}
PropertyterminalExpression.Prototype = {
interpretieren: Funktion (Kontext) {
// Erhalten Sie den Wert des letzten Elementattributs direkt
return context.getPreele (). getAtTribute (this.PropName);
}
};
Schauen wir uns zunächst an, wie Sie den Dolmetscher verwenden, um den Wert eines einzelnen Elements zu erhalten:
Die Codekopie lautet wie folgt:
void function () {
var c = neuer Kontext ();
// möchte den Wert mehrerer Delemente, dh den Wert des folgenden Ausdrucks: "Root/A/B/C" erhalten, dh den Wert des folgenden Ausdrucks erhalten.
// Zunächst müssen Sie einen abstrakten Syntaxbaum für den Interpreter erstellen
var root = new ElementExpression ('root');
var aele = new ElementExpression ('a');
var Bele = new ElementExpression ('B');
var Cely = new ElementterminalExpression ('C');
// Kombination
root.addele (aele);
aele.addele (Bele);
Bele.addele (Cely);
console.log ('cs Wert ist =' + root.interpret (c));
} ();
Ausgabe: Der Wert von C ist = 12345
Dann verwenden wir den obigen Code, um den Wert des Attributs eines einzelnen Elements zu erhalten:
Die Codekopie lautet wie folgt:
void function () {
var c = neuer Kontext ();
// möchte das ID -Attribut des D -Elements, dh den Wert des folgenden Ausdrucks, abrufen: "a/b/c.name"
// Diesmal ist C noch nicht vorbei und Sie müssen C an ElementExpression ändern
var root = new ElementExpression ('root');
var aele = new ElementExpression ('a');
var Bele = new ElementExpression ('B');
var Cely = new ElementExpression ('C');
var prop = new PropertyterminalExpression ('Name');
// Kombination
root.addele (aele);
aele.addele (Bele);
Bele.addele (Cely);
CONSADDELE (PROP);
console.log ('cs Eigenschaftsname Wert ist =' + root.interpret (c));
// Wenn Sie denselben Kontext verwenden und kontinuierlich analysieren möchten, müssen Sie das Kontextobjekt neu initialisieren
// Zum Beispiel müssen Sie den Wert des Attributnamens nacheinander erneut erneut nutzen. Natürlich können Sie die Elemente rekombinieren
// Neuparse, solange der gleiche Kontext verwendet wird, muss das Kontextobjekt erneut initialisiert werden
C.Reinit ();
console.log ('Reget Cs Eigenschaftsname Wert ist =' + root.interpret (c));
} ();
Ausgabe: Der Attributname -Wert von C ist = testc den Attributnamenwert von C is = testc abrufen
erklären:
1. Funktion der Interpreter -Modus:
Das Interpreter -Muster verwendet Interpreter -Objekte, um die entsprechenden Syntaxregeln darzustellen und zu verarbeiten. Im Allgemeinen behandelt ein Dolmetscher eine Syntaxregel. Theoretisch kann das Interpret-Muster theoretisch durch das Interpreter-Objekt dargestellt werden, solange die syntaxkonformen Ausdrücke durch das Interpreter-Objekt dargestellt werden können, um sie zu verarbeiten.
2. Syntaxregeln und Dolmetscher
Es gibt eine Korrespondenz zwischen Syntaxregeln und Dolmetschern. Im Allgemeinen behandelt ein Dolmetscher eine Syntaxregel, das Gegenteil ist jedoch nicht wahr. Eine Syntaxregel kann mehrere Interpretationen und Verarbeitung haben, dh eine Syntaxregel kann mehreren Dolmetschern entsprechen.
3. Kontext Gemeinsamkeit
Der Kontext spielt im Interpreter -Modus eine sehr wichtige Rolle. Weil der Kontext an alle Dolmetscher übergeben wird. Daher kann der Zustand des Dolmetschers im Kontext gespeichert und zugegriffen werden. Zum Beispiel kann der vorherige Interpreter einige Daten im Kontext speichern und dieser Interpreter kann diese Werte erhalten.
Darüber hinaus können einige Daten außerhalb des Interpreters durch den Kontext weitergegeben werden, der Dolmetscher benötigt diese und einige globale öffentliche Daten.
Es gibt auch eine Funktion im Kontext, nämlich die gemeinsamen Funktionen aller Interpreter -Objekte, ähnlich wie Objektkombinationen, anstatt die Vererbung zu verwenden, um gemeinsame Funktionen zu erhalten, die in jedem Interpreter -Objekt aufgerufen werden können.
4. Wer einen abstrakten Syntaxbaum bauen wird
Im vorherigen Beispiel ist es sehr problematisch, den abstrakten Syntaxbaum auf der Client -Seite manuell zu erstellen, aber im Interpreter -Modus ist dieser Teil der Funktion nicht beteiligt und ist nur für die Interpretation und Verarbeitung des konstruierten abstrakten Syntaxbaums verantwortlich. Wir werden vorstellen, dass wir einen Parser zur Umwandlung von Ausdrücken in abstrakte Syntaxbäume zur Verfügung stellen können.
Es gibt ein anderes Problem, dh eine Syntaxregel kann mehreren Interpreter -Objekten entsprechen, dh dasselbe Element kann in mehrere Interpreter -Objekte umgewandelt werden, was bedeutet, dass der gleiche Ausdruck einen unnötigen abstrakten Syntaxbaum bilden kann, was es auch schwierig macht, einen abstrakten Syntaxbaum zu erstellen, und die Arbeitsbelastung ist sehr groß.
5. Wer ist verantwortlich für die Erklärung der Operation
Solange der abstrakte Syntaxbaum definiert ist, muss der Dolmetscher für die Interpretation und Ausführung verantwortlich sein. Obwohl es unterschiedliche Syntaxregeln gibt, ist der Interpreter nicht dafür verantwortlich, welches Interpreter -Objekt die Ausführungssyntaxregeln interpretieren soll. Die Funktion der Auswahl des Interpreters wird beim Erstellen eines abstrakten Syntaxbaums abgeschlossen.
6. Die Reihenfolge des Call of Interpreter -Modus
1) Erstellen Sie ein Kontextobjekt
2) Erstellen Sie mehrere Interpreter -Objekte und kombinieren Sie abstrakte Syntaxbäume
3) Rufen Sie die Interpretationsoperation des Interpreter -Objekts auf
3.1) Speichern und greifen Sie auf den Zustand des Dolmetschers im Kontext.
Rufen Sie für Nicht-Terminator-Interpreter-Objekte das von ihm enthaltende Subinterpreter-Objekt rekursiv auf.
Die Essenz des Interpreter -Musters: *Separate Implementierung, Ausführung interpretieren *
Das Interpreter -Modul verwendet ein Interpreter -Objekt, um eine Syntaxregel zu verarbeiten, um komplexe Funktionen zu trennen. Wählt dann die Funktionen aus, die ausgeführt werden müssen, und kombiniert diese Funktionen zu einem abstrakten Syntaxbaum, der interpretiert und ausgeführt werden muss. dann interpretiert die Ausführung gemäß dem abstrakten Syntaxbaum, um entsprechende Funktionen zu implementieren.
Auf der Oberfläche konzentriert sich der Interpreter -Modus auf die Verarbeitung von benutzerdefinierten Syntax, die wir normalerweise nicht verwenden. Aber im Wesentlichen ist die Idee des Interpreter -Modus dann Trennung, Einkapselung, Vereinfachung und dieselben wie viele Modi.
Beispielsweise kann der Interpreter -Modus verwendet werden, um die Funktion des Statusmodus zu simulieren. Wenn die Syntax, die vom Interpreter -Modus verarbeitet wird, nur auf eine Zustandsmarke vereinfacht wird, wird der Interpreter als Verarbeitungsobjekt für den Zustand angesehen. Für die gleiche Syntax, die den Staat repräsentiert, kann es viele nicht verwendete Dolmetscher geben, dh viele Objekte mit unterschiedlichen Verarbeitungszuständen. Beim Erstellen eines abstrakten Syntaxbaums wird es vereinfacht, den entsprechenden Interpreter basierend auf der Zustandsmarke zu erstellen, und es besteht kein Grund zur Erstellung eines Baumes.
In ähnlicher Weise kann der Interpreter -Modus die Funktion der Implementierung des Richtlinienmodus, der Funktion des Dekoratormodus usw. simulieren, insbesondere die Simulation der Funktion des Dekorationsmodus, und der Prozess des Aufbaus eines abstrakten Syntaxbaums entspricht natürlich dem Prozess der Kombination des Dekorateurs.
Der Interpreter -Modus ist normalerweise nicht schnell (meistens sehr langsam), und das Debuggen des Fehlers ist schwierig (Teil 1: Obwohl das Debuggen schwierig ist, verringert es tatsächlich die Möglichkeit von Fehlern), aber seine Vorteile sind offensichtlich. Es kann die Komplexität von Schnittstellen zwischen Modulen effektiv steuern. Bei Funktionen mit geringer Ausführungsfrequenz, aber hoher Codefrequenz und sehr vielfältig sind Dolmetscher sehr für Modi geeignet. Darüber hinaus hat der Dolmetscher einen weiteren weniger spürbaren Vorteil, was bequem mit Verbreitung und plattformübergreifender Verbreitung und plattarischem Plattform versorgt werden kann.
Vor- und Nachteile des Interpreter -Modus:
Vorteil:
1. Einfach zu implementierende Syntax
Im Interpreter -Modus wird eine Syntaxregel mit einem Interpreter -Objekt zur Interpretation der Ausführung interpretiert. Für die Implementierung des Dolmetschers wird die Funktion relativ einfach. Sie müssen nur die Implementierung dieser Syntax -Regel berücksichtigen, und Sie müssen sich nichts anderes Sorgen machen. 2. Einfach zu erweitern, neue Syntax zu erweitern
Genau an die Art und Weise, wie ein Interpreter -Objekt für eine Syntaxregel verantwortlich ist, ist die Erweiterung neuer Syntax sehr einfach. Die neue Syntax wurde erweitert, und Sie müssen nur das entsprechende Interpreter -Objekt erstellen und dieses neue Interpreter -Objekt verwenden, wenn Sie einen abstrakten Syntaxbaum erstellen.
Mangel:
Nicht für komplexe Syntax geeignet
Wenn die Syntax besonders komplex ist, ist die Arbeit zum Aufbau des abstrakten Syntaxbaums sehr schwierig, und es ist möglich, mehrere abstrakte Syntaxbäume zu erstellen. Daher ist das Interpreter -Muster für komplexe Syntax nicht geeignet. Es ist möglicherweise besser, einen Syntaxanalysator oder einen Compiler -Generator zu verwenden.
Wann soll es verwendet werden?
Wenn es eine Sprache gibt, die interpretiert und ausgeführt werden muss, und Sätze in dieser Sprache als abstrakte Syntaxbaum dargestellt werden können, können Sie das Interpreter -Muster verwenden.
Bei Verwendung des Interpreter -Modus müssen zwei weitere Funktionen berücksichtigt werden. Eine davon ist, dass die Syntax relativ einfach sein sollte. Die zu verantwortungsbewusste Syntax ist nicht für die Verwendung des Interpreter -Modus geeignet. Das andere ist, dass die Effizienzanforderungen nicht sehr hoch sind und die Effizienzanforderungen sehr hoch sind und nicht für die Verwendung geeignet sind.
Die vorherige Einführung war, wie der Wert eines einzelnen Elements und der Wert eines einzelnen Elementattributs erhalten werden. Schauen wir uns an, wie Sie die Werte mehrerer Elemente sowie die Werte der Namen der anderen in mehreren Elementen erhalten können, und die vorherigen Tests sind alle künstlich kombinierte abstrakte Syntaxbäume. Wir implementieren auch den folgenden einfachen Parser, um Ausdrücke zu konvertieren, die der oben definierten Syntax in abstrakte Syntaxbäume des oben implementierten Interpreters entsprechen: Ich habe den Code direkt veröffentlicht:
Die Codekopie lautet wie folgt:
// Lesen Sie die Werte mehrerer Elemente oder Attribute
(function () {
/**
* Kontext, verwendet, um einige globale Informationen zu enthalten, die vom Dolmetscher erforderlich sind
* @param {string} filepathname [Pfad und Name des XML, der gelesen werden muss]
*/
Funktionskontext (filepathname) {
// mehrere Elemente, die im vorherigen verarbeitet wurden
this.preeles = [];
// XML -Dokumentobjekt
this.document = xmlutil.getroot (filepathname);
}
Context.prototype = {
// den Kontext neu initialisieren
Reinit: function () {
this.preeles = [];
},
/**
* Methoden für die öffentliche Verwendung jedes Ausdrucks
* Holen Sie sich das aktuelle Element gemäß dem Namen des übergeordneten Elements und des aktuellen Elements
* @param {element} pele [übergeordnetes Element]
* @param {String} Elename [Aktueller Elementname]
* @return {Element | null} [aktuelles Element gefunden]
*/
GetNoweles: Funktion (Pele, Elename) {
var elements = [];
var tempnodelist = pele.childnodes;
var nowele;
für (var i = 0, len = tempnodelist.length; i <len; i ++) {
if ((NOWELE = TEMPNODELIST [I]). NODETYP === 1) {
if (nowele.nodename === Elename) {
Elements.push (nowee);
}
}
}
Returnelemente;
},
getPreeles: function () {
kehre dies zurück.preeles;
},
setPreeles: function (noweles) {
this.preeles = noweles;
},
getDocument: function () {
Rückgabe dieses Dokuments;
}
};
// Werkzeugobjekt
// XML analysieren, um das entsprechende Dokumentobjekt zu erhalten
var xmlutil = {
getroot: function (filepathname) {
var parser = new domparser ();
var xmldom = parser.parsefromString ('<root id = "rootId"> <a> <b> <c name = "testc"> 12345 </c> <d id = "1"> d1 </d> <d id = "2"> d2 </d> <d id = "3"> d3 </d> </d> </d> </d> </d> </d> < id = "4"> d4 </d> </b> </a> </root> ',' text/xml ');
Return Xmldom;
}
};
/**
* Elemente werden als Dolmetscher verwendet, die Nicht-Terminals entsprechen, um Zwischenelemente zu interpretieren und auszuführen
* @param {String} Elename [Name des Elements]
*/
FunktionselementExpression (Elename) {
this.eles = [];
this.elename = Elename;
}
ElementExpression.prototype = {
Addele: Funktion (Elename) {
this.eles.push (Elename);
zurückkehren;
},
REMELELE: Funktion (ele) {
für (var i = 0, len = this.eles.length; i <len; i ++) {
if (ele === this.eles [i]) {
this.eles.splice (i--, 1);
}
}
zurückkehren;
},
interpretieren: Funktion (Kontext) {
// Nehmen Sie zuerst das aktuelle Element im Kontext als übergeordnetes Element heraus
// Ermitteln Sie das XML -Element, das dem aktuellen Elementnamen entspricht, und setzen Sie es auf den Kontext zurück
var peles = context.getPreeles ();
var ele = null;
var noweles = [];
if (! peles.length) {
// zeigt an, dass das Stammelement jetzt erhalten wird
ele = context.getDocument (). documentElement;
peles.push (ele);
context.setPreeles (peles);
} anders {
var tempele;
für (var i = 0, len = peles.length; i <len; i ++) {
tempele = peles [i];
noweles = noweles.concat (context.getNoweles (tempele, this.elename));
// Stoppen Sie, wenn Sie einen finden
if (noweles.length) brechen;
}
context.setPreeles ([noweles [0]]);
}
var ss;
// Schleife, um die Interpretation der Methode des untergeordneten Elements aufzurufen
für (var i = 0, len = this.eles.length; i <len; i ++) {
ss = this.eles [i] .interpret (Kontext);
}
Rückkehr SS;
}
};
/**
* Elemente werden als Interpreter verwendet, der dem Terminator entspricht
* @param {String} Name [Name des Elements]
*/
Funktion elementterminalexpression (Name) {
this.elename = name;
}
ElementDerminalexpression.prototype = {
interpretieren: Funktion (Kontext) {
var peles = context.getPreeles ();
var ele = null;
if (! peles.length) {
ele = context.getDocument (). documentElement;
} anders {
ele = context.getNoweles (peles [0], this.elename) [0];
}
// Erhalten Sie den Wert des Elements
return ele.firstchild.nodeValue;
}
};
/**
* Das Attribut wird als Interpreter verwendet, der dem Terminator entspricht
* @param {string} propname [Name des Attributs]
*/
Funktion PropertyterminalExpression (PropName) {
this.PropName = propname;
}
PropertyterminalExpression.Prototype = {
interpretieren: Funktion (Kontext) {
// Erhalten Sie den Wert des letzten Elementattributs direkt
return context.getPreeles () [0] .GetAttribute (this.PropName);
}
};
/**
* Mehrere Attribute werden als Interpreter verwendet, der dem Terminator entspricht
* @param {string} propname [Name des Attributs]
*/
Funktion PropertysterMinalExpression (PropName) {
this.PropName = propname;
}
PropertysterMinalExpression.Prototype = {
interpretieren: Funktion (Kontext) {
var eles = context.getPreeles ();
var ss = [];
für (var i = 0, len = eles.Length; i <len; i ++) {
Ss.push (Eles [i] .GetAttribute (this.PropName));
}
Rückkehr SS;
}
};
/**
* Interpretationsverarbeitungsobjekt mit mehreren Elementen als Terminatoren
* @param {[Typ]} Name [Beschreibung]
*/
Funktion ElementsterminalExpression (Name) {
this.elename = name;
}
ElementsterminalExpression.Prototype = {
interpretieren: Funktion (Kontext) {
var peles = context.getPreeles ();
var noweles = [];
für (var i = 0, len = peles.length; i <len; i ++) {
noweles = noweles.concat (context.getNoweles (peles [i], this.elename));
}
var ss = [];
für (i = 0, len = noweles.length; i <len; i ++) {
ss.push (noweles [i] .Firstchild.nodeValue);
}
Rückkehr SS;
}
};
/**
* Mehrere Elemente werden als Nicht-Terminale interpretiert
*/
Funktion elementSexpression (Name) {
this.elename = name;
this.eles = [];
}
ElementSexpression.prototype = {
interpretieren: Funktion (Kontext) {
var peles = context.getPreeles ();
var noweles = [];
für (var i = 0, len = peles.length; i <len; i ++) {
noweles = noweles.concat (context.getNoweles (peles [i], this.elename));
}
context.setPreeles (noweles);
var ss;
für (i = 0, len = this.eles.length; i <len; i ++) {
ss = this.eles [i] .interpret (Kontext);
}
Rückkehr SS;
},
Addele: Funktion (ele) {
this.eles.push (ele);
zurückkehren;
},
REMELELE: Funktion (ele) {
für (var i = 0, len = this.eles.length; i <len; i ++) {
if (ele === this.eles [i]) {
this.eles.splice (i--, 1);
}
}
zurückkehren;
}
};
void function () {
// "root/a/b/d $"
var c = neuer Kontext ('interpreter.xml');
var root = new ElementExpression ('root');
var aele = new ElementExpression ('a');
var Bele = new ElementExpression ('B');
var dele = new ElementsterminalExpression ('D');
root.addele (aele);
aele.addele (Bele);
Bele.addele (Dele);
var ss = root.interpret (c);
für (var i = 0, len = ss.Length; i <len; i ++) {
console.log ('d value is =' + ss [i]);
}
} ();
void function () {
// a/b/d $ .id $
var c = neuer Kontext ('interpreter.xml');
var root = new ElementExpression ('root');
var aele = new ElementExpression ('a');
var Bele = new ElementExpression ('B');
var dele = new elementSexpression ('d');
var prop = new PropertysterminInexpression ('id');
root.addele (aele);
aele.addele (Bele);
Bele.addele (Dele);
Dele.addele (Prop);
var ss = root.interpret (c);
für (var i = 0, len = ss.Length; i <len; i ++) {
console.log ('d Eigenschafts -ID -Wert ist =' + ss [i]);
}
} ();
// Parser
/**
* Implementierungsideen von Parser
* 1. Zersetzen Sie die vom Client übergebenen Ausdrücke, zersetzen Sie sie nacheinander in Elemente und verwenden Sie ein entsprechendes analytisches Modell, um einige Informationen zu diesem Element zu verkörpern.
* 2. Konvertieren Sie es in das entsprechende Parser -Objekt basierend auf den Informationen jedes Elements.
* 3. Kombinieren Sie diese Parser -Objekte, um einen abstrakten Syntaxbaum zu erhalten.
*
* Warum verschmelzen Sie nicht 1 und 2 und zersetzen ein Element direkt und konvertieren es in das entsprechende Parser -Objekt?
* 1. Funktionelle Trennung, machen Sie die Funktionen einer Methode nicht zu kompliziert.
* 2. Für die zukünftige Modifikation und Erweiterung ist die Syntax jetzt einfach, daher gibt es bei der Konvertierung in ein Parser -Objekt wenig zu beachten, und es ist nicht schwierig, direkt umzuwandeln. Wenn die Syntax jedoch kompliziert ist, ist die direkte Konvertierung sehr unordentlich.
*/
/**
* Wird verwendet, um die entsprechenden Attribute jedes Parsen -Elements zu verkapulieren
*/
Funktion parsermodel () {
// ob ein einzelner Wert
Dies.SingleValue;
// ob es sich um ein Attribut handelt, entweder ein Attribut oder ein Element
this.PropertyValue;
// ob zu beenden
this.end;
}
Parsermodel.Prototype = {
isend: function () {
kehre diesen.end zurück;
},
setend: function (end) {
this.end = Ende;
},
issingleValue: function () {
kehre dies zurück.
},
setsingleValue: function (OneValue) {
this.SingleValue = OneValue;
},
isPropertyValue: function () {
kehre diesen.PropertyValue zurück;
},
setPropertyValue: function (PropertyValue) {
this.PropertyValue = PropertyValue;
}
};
var parser = function () {
var backlash = '/';
var dot = '.';
var Dollar = '$';
// Die Namen von Elementen aufzeichnen, die gemäß der Reihenfolge der Zersetzung analysiert werden müssen
var listele = null;
// Start the first step --------------------------------------------------------------------------------------------------------------------------
/**
* Geben Sie einen Saitenausdruck ein und analysieren Sie ihn und kombinieren Sie ihn zu einem abstrakten Syntaxbaum
* @param {String} expr. [Beschreiben Sie den String -Ausdruck, um den Wert zu nehmen]
* @Return {Objekt} [entsprechendes abstraktes Syntaxbaum]
*/
Funktion parsemAppath (expr) {
// Teilen Sie zuerst die Zeichenfolge nach "/" auf
var tokenizer = expr.Split (Backlash);
// Tabelle zerlegter Werte
var mappath = {};
Var Onepath, Elename, Propname;
var dotindex = -1;
für (var i = 0, len = tokenizer.length; i <len; i ++) {
Onepath = Tokenizer [i];
if (Tokenizer [i + 1]) {
// Es gibt einen anderen Wert, was bedeutet, dass dies nicht das letzte Element ist
// Gemäß der aktuellen Syntax muss das Attribut am Ende sein, so dass es nicht das Attribut ist.
setParsepath (false, onepath, false, mappath);
} anders {
// Es ist das Ende
dotindex = Onepath.Indexof (dot);
if (dotindex> = 0) {
// Es bedeutet, dass Sie den Wert des Attributs erhalten möchten, also teilen Sie es nach "."
// Der erste ist der Elementname, und der zweite ist der Attributname
Elename = Onepath.substring (0, dotindex);
propname = onepath.substring (dotindex + 1);
// Das Element vor der Eigenschaft ist von Natur aus nicht das letzte, und es ist auch nicht die Eigenschaft
setParsepath (falsch, Elename, Falsch, Mappath);
// Attribute festlegen. Gemäß der aktuellen Syntax -Definition kann das Attribut nur das letzte sein.
setParsepath (true, propname, true, mappath);
} anders {
// Anweisungen werden als Wert des Elements und als Wert des letzten Elements angenommen
setParsepath (wahr, Onepath, Falsch, Mappath);
}
brechen;
}
}
Mappath zurückkehren;
}
/**
* Legen Sie den Elementnamen fest, der gemäß dem zerlegten Ort und dem Namen analysiert werden soll
* @param {boolean} Ende [ist es zuletzt]
* @param {string} ele [Elementname]
* @param {boolean} PropertyValue [Ob Sie die Eigenschaft nehmen]
* @param {Object} mappath [Setzen Sie den Namen des Elements, das analysiert werden muss, und die Tabelle des Parsing -Modells, das dem Element entspricht.
*/
Funktion SetParsePath (Ende, Ele, PropertyValue, Mappath) {
var PM = neuer Parsermodel ();
PM.Setend (Ende);
// Wenn das Symbol "$" kein Wert ist
PM.SetsingleValue (! (Ele.Indexof (Dollar)> = 0));
PM.SetPropertyValue (PropertyValue);
// entfernen "$"
ele = ele.replace (Dollar, '');
Mappath [ele] = PM;
listele.push (ele);
}
// Start the second step --------------------------------------------------------------------------------------------------------------------------
/**
* Konvertieren Sie den zerlegten Elementnamen in das entsprechende Interpreter -Objekt gemäß dem entsprechenden Analysemodell.
* @param {Object} Mappath [Der zerlegte Elementname, das analysiert wird, und das Parsing -Modell, das dem Element entspricht]
* @return {array} [Konvertieren Sie jedes Element in ein Array entsprechender Interpreter -Objekte]
*/
Funktion mappath2interpreter (mappath) {
var list = [];
var PM, Schlüssel;
var obj = null;
// Es ist notwendig, es in die Reihenfolge der Zersetzung in Interpreterobjekte umzuwandeln
für (var i = 0, len = listele.length; i <len; i ++) {
Key = Listele [i];
PM = Mappath [Schlüssel];
// nicht der letzte
if (! pm.isend ()) {
if (pm.issingleValue ())
// ist ein Wert, Konvertierung
obj = new ElementExpression (Schlüssel);
anders
// sind mehrere Werte, Konvertierung
obj = new ElementSexpression (Schlüssel);
} anders {
// Es ist der letzte
// ist der Attributwert
if (pm.ispropertyValue ()) {
if (pm.issingleValue ())
OBJ = New PropertyterminalExpression (Schlüssel);
anders
OBJ = New PropertysterminInexpression (Schlüssel);
// Nehmen Sie den Wert des Elements
} anders {
if (pm.issingleValue ())
OBJ = New ElementsterminalExpression (Schlüssel);
anders
OBJ = New ElementsterminalExpression (Schlüssel);
}
}
list.push (obj);
}
Rückgabeliste;
}
// Start the third step --------------------------------------------------------------------------------------------------------------------------
/**
* Erstellen Sie einen abstrakten Syntaxbaum
* @param {[type]} list [Konvertieren Sie jedes Element in ein Array entsprechender Interpreter -Objekte]
* @return {[type]} [Beschreibung]
*/
Funktion Buildtree (Liste) {
// Das erste Objekt, auch das zurückgegebene Objekt, ist die Wurzel des abstrakten Syntaxbaums
var returnReadxMlexPr = null;
// das vorherige Objekt definieren
var PrereadxMlexPR = NULL;
var readxml, lee, erne;
für (var i = 0, len = list.length; i <len; i ++) {
readxml = list [i];
// Beschreibung ist das erste Element
if (prereadxMlexPr === null) {
prereadxMlexPr = readxml;
returnReadxMlexPr = readxml;
// Fügen Sie das Element dem vorherigen Objekt hinzu und setzen Sie das Objekt in Oldre
// als Elternknoten des nächsten Objekts
} anders {
if (prereadxMlexPRinstanceof elementExpression) {{
ele = PrereadxMlexPR;
ele.addele (readxml);
prereadxMlexPr = readxml;
} else if (prereadxMlexPRinstance von elementSexpression) {
Eles = PrereadxMlexPR;
Eles.Addele (readxml);
prereadxMlexPr = readxml;
}
}
}
return returnReadxMlexPR;
}
zurückkehren {
// öffentliche Methode
analysieren: function (expr) {
Listele = [];
var mappath = parsemAppath (expr);
var list = mappath2interpreter (mappath);
return Buildtree (Liste);
}
};
} ();
void function () {
// den Kontext vorbereiten
var c = neuer Kontext ('interpreter.xml');
// Erhalten Sie den abstrakten Syntaxbaum, indem Sie ihn analysieren
var readxmlexpr = parser.parse ('root/a/b/d $ .id $');
// Anfragen von Parsen anfordern und den Rückgabewert erhalten
var ss = readxmlexpr.interpret (c);
console.log ('------------ analysieren --------------');
für (var i = 0, len = ss.Length; i <len; i ++) {
console.log ('d Eigenschafts -ID -Wert ist =' + ss [i]);
}
console.log ('--------------- an Parsen --------------');
// 如果要使用同一个上下文,连续进行解析,需要重新初始化上下文对象
c.reInit();
var readxmlExpr2 = Parser.parse('root/a/b/d$');
var ss2 = readxmlExpr2.interpret(c);
console.log('------------parsing--------------');
for (i = 0, len = ss2.length; i < len; i++) {
console.log('d的值是= ' + ss2[i]);
}
console.log('---------------parsed--------------');
c.reInit();
var readxmlExpr3 = Parser.parse('root/a/b/c');
var ss3 = readxmlExpr3.interpret(c);
console.log('------------parsing--------------');
console.log('c的name属性值是= ' + ss3);
console.log('---------------parsed--------------');
c.reInit();
var readxmlExpr4 = Parser.parse('root/a/b/c.name');
var ss4 = readxmlExpr4.interpret(c);
console.log('------------parseing--------------');
console.log('c的name属性值是= ' + ss4);
console.log('---------------parsed--------------');
}();
// 这样就实现了类似XPath的部分功能
// 没错,就类似于jQuery选择器的部分功能
}());
输出: d的值是= d1
d的值是= d2
d的值是= d3
d的值是= d4
d的属性id的值是= 1
d的属性id的值是= 2
d的属性id的值是= 3
d的属性id的值是= 4
------------Parsing--------------
d的属性id的值是= 1
d的属性id的值是= 2
d的属性id的值是= 3
d的属性id的值是= 4
---------------parsed--------------
------------Parsing--------------
d的值是= d1
d的值是= d2
d的值是= d3
d的值是= d4
---------------parsed--------------
------------Parsing--------------
c的name属性值是= 12345
---------------parsed--------------
------------parseing--------------
c的name属性值是= testC
---------------parsed--------------