¿Shenma es un "modo intérprete"?
Abra primero el "GoF" y echemos un vistazo a la definición:
Dado un idioma, defina una representación de su gramática y defina un intérprete que use esta representación para interpretar las oraciones en el idioma.
Antes del comienzo, todavía necesito popularizar varios conceptos:
Árbol de sintaxis abstracta:
El patrón de intérprete no explica cómo crear un árbol de sintaxis abstracto. No implica análisis gramatical. Se puede completar un árbol de sintaxis abstracto mediante un programa de análisis de sintaxis basado en la tabla, o creado por el programa de análisis de sintaxis escrito a mano (generalmente recursivo descendente), o proporcionado directamente por el cliente.
Analizador:
Se refiere a un programa que describe las expresiones requeridas por la llamada del cliente y forma un árbol de sintaxis abstracto después del análisis.
Intérprete:
Se refiere a un programa que explica el árbol de sintaxis abstracto y ejecuta las funciones correspondientes de cada nodo.
Un requisito previo importante para usar el patrón de intérprete es definir un conjunto de reglas gramaticales, también conocidas como gramática. Independientemente de si las reglas de esta gramática son simples o complejas, estas reglas deben incluirse, porque el modo de intérprete es analizar y realizar las funciones correspondientes en consecuencia.
Echemos un vistazo al diagrama de estructura y la descripción del modo intérprete:
AbstractExpression: Define la interfaz del intérprete y acepta la operación de interpretación del intérprete.
TerminalExpression: TerminalExpression, utilizada para implementar operaciones relacionadas con Terminator en las reglas de sintaxis, ya no contiene otros intérpretes. Si el patrón de combinación se usa para construir un árbol de sintaxis abstracto, es equivalente a un objeto de hoja en el patrón de combinación, y puede haber múltiples intérpretes de terminadores.
No terminalAxpression: un intérprete no terminal, utilizado para implementar operaciones no relacionadas con el terminal en las reglas de sintaxis. Por lo general, un intérprete corresponde a una regla de sintaxis y puede contener otros intérpretes. Si el patrón de composición se usa para construir un árbol de sintaxis abstracto, es equivalente a un objeto combinado en el patrón de composición. Puede haber múltiples intérpretes no terminales.
Contexto: el contexto, generalmente contiene datos requeridos por cada intérprete o funciones públicas.
Cliente: un cliente se refiere a un cliente que usa un intérprete. Por lo general, las expresiones realizadas de acuerdo con la sintaxis del lenguaje se convierten en un árbol de sintaxis abstracto descrito por el objeto del intérprete, y luego se llama a una operación de explicación.
Aquí usamos un ejemplo XML para comprender el patrón de intérprete:
Primero, necesitamos diseñar una gramática simple para la expresión. Para fines generales, use la raíz para representar el elemento raíz, ABC, etc. para representar el elemento. Un XML simple es el siguiente:
La copia del código es la siguiente:
<? xml versión = "1.0" encoding = "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>
La gramática de la expresión de la convención es la siguiente:
1. Obtenga el valor de un solo elemento: comience desde el elemento raíz y hasta el elemento que desea obtener el valor. El medio del elemento está separado por "/" y no "/" se agrega antes del elemento raíz. Por ejemplo, la expresión "raíz/a/b/c" significa obtener los valores del elemento c debajo del elemento raíz, elemento a, elemento b y elemento c.
2. Obtenga el valor del atributo de un solo elemento: por supuesto, hay múltiples atributos. El atributo para obtener el valor debe ser el atributo del último elemento de la expresión. Agregar "." Después del último elemento y luego agregue el nombre del atributo. Por ejemplo, la expresión "raíz/a/b/c.name" significa obtener el valor del atributo de nombre del elemento raíz, elemento a, elemento b, elemento c.
3. Obtenga el valor del mismo nombre del elemento, por supuesto, hay múltiples elementos. El elemento para obtener el valor debe ser el último elemento de la expresión y agregar "$" después del último elemento. Por ejemplo, la expresión "raíz/a/b/d $" representa la colección de valores de múltiples elementos d debajo del elemento raíz, debajo del elemento A y debajo del elemento B.
4. Obtenga el valor del atributo con el mismo nombre del elemento, por supuesto, hay múltiples: el elemento para obtener el valor del atributo debe ser el último elemento de la expresión y agregar "$" después del último elemento. Por ejemplo, la expresión "raíz/a/b/d $ .id $" representa la recopilación de valores de múltiples atributos de ID de elementos d debajo del elemento raíz, debajo del elemento A y debajo del elemento B.
El XML anterior, correspondiente al árbol de sintaxis abstracto, y la posible estructura se muestra en la figura:
Echemos un vistazo al código específico a continuación:
1. Defina el contexto:
La copia del código es la siguiente:
/**
* Contexto, utilizado para contener alguna información global requerida por el intérprete
* @param {String} FilePathName [ruta y nombre del XML que debe leerse]
*/
contexto de funciones (filePathName) {
// El elemento procesado anterior
this.preele = null;
// Objeto de documento XML
this.document = xmlutil.getroot (filePathName);
}
Context.prototype = {
// reinicializar el contexto
reinit: funct () {
this.preele = null;
},
/**
* Métodos para el uso público de cada expresión
* Obtenga el elemento actual de acuerdo con el nombre del elemento principal y el elemento actual
* @param {elemento} Pele [Elemento principal]
* @param {string} elename [nombre del elemento actual]
* @return {elemento | nulo} [Elemento actual encontrado]
*/
getNowele: function (Pele, elename) {
var tempnodeList = Pele.childNodes;
var nowele;
para (var i = 0, len = tempnodeList.length; i <len; i ++) {
if ((nowele = tempnodeList [i]). nodetype === 1)
if (nowele.nodename === Elename)
regresar nowele;
}
regresar nulo;
},
getPreele: function () {
devolver esto.preele;
},
setPreele: function (preele) {
this.preele = preele;
},
getDocument: function () {
devolver esto.document;
}
};
En el contexto, utilicé un objeto de herramienta xmlutil para obtener xmldom. A continuación estoy usando Dompaser de DOM3. Algunos navegadores pueden no apoyarlo. Utilice el navegador base:
La copia del código es la siguiente:
// Objeto de herramienta
// analizar xml para obtener el objeto de documento correspondiente
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 id = "4"> d4 </d> </b> </a> </root> ',' text/xml ');
return xmldom;
}
};
Aquí está el código para el intérprete:
La copia del código es la siguiente:
/**
* Los elementos se utilizan como intérpretes correspondientes a los no terminales para interpretar y ejecutar elementos intermedios
* @param {string} elename [nombre del elemento]
*/
function ElementExpression (elename) {
this.eles = [];
this.elename = elename;
}
ElementExpression.prototype = {
addele: function (elename) {
this.eles.push (elename);
devolver verdadero;
},
removeele: function (ele) {
para (var i = 0, len = this.eles.length; i <len; i ++) {
if (ele === this.eles [i])
this.eles.splice (i--, 1);
}
devolver verdadero;
},
interpretar: function (context) {
// Primero elimine el elemento actual en el contexto como el elemento principal
// Encuentra el elemento XML correspondiente al nombre del elemento actual y configúrelo al contexto
var pele = context.getPreele ();
if (! Pele) {
// indica que el elemento raíz ahora se obtiene
context.setPreele (context.getDocument (). DocumentElement);
} demás {
// Obtener el elemento actual basado en el nombre del elemento principal y el elemento a buscar
var nowele = context.getNowele (Pele, this.elename);
// Poner el elemento actualmente recuperado en el contexto
context.setPreele (Nowele);
}
var ss;
// bucle para llamar al método de interpretación del elemento infantil
para (var i = 0, len = this.eles.length; i <len; i ++) {
ss = this.eles [i] .interpret (contexto);
}
// devuelve el resultado de explicación del último intérprete. En general, el último intérprete es el intérprete Terminator.
devolver ss;
}
};
/**
* Los elementos se utilizan como intérprete correspondiente al terminador
* @param {string} nombre [nombre del elemento]
*/
función elementterMinalAxpression (nombre) {
this.elename = nombre;
}
ElementterMinalAxpression.prototype = {
interpretar: function (context) {
var pele = context.getPreele ();
var ele = nulo;
if (! Pele) {
ele = context.getDocument (). DocumentElement;
} demás {
ele = context.getNowele (Pele, this.elename);
context.setPreele (ELE);
}
// Obtener el valor del elemento
return ele.firstchild.nodeValue;
}
};
/**
* El atributo se usa como intérprete correspondiente al terminador
* @param {string} propName [nombre del atributo]
*/
function PropertyTerminalAxpression (propName) {
this.propname = propName;
}
PropertyTerminalAxpression.prototype = {
interpretar: function (context) {
// Obtener el valor del atributo del último elemento directamente
return context.getPreele (). getAttribute (this.propName);
}
};
Primero veamos cómo usar el intérprete para obtener el valor de un solo elemento:
La copia del código es la siguiente:
función vacía () {
var c = nuevo contexto ();
// quiere obtener el valor de múltiples elementos d, es decir, el valor de la siguiente expresión: "root/a/b/c"
// Primero, debe construir un árbol de sintaxis abstracto para el intérprete
var root = new ElementExpression ('root');
var aELE = new ElementExpression ('A');
var bele = new ElementExpression ('B');
var cEle = new ElementTerminalAxpression ('C');
// Combinación
root.addele (AELE);
Aele.addele (Bele);
Bele.addele (Cele);
console.log (el valor de 'c es =' + root.interpret (c));
} ();
Salida: el valor de C es = 12345
Luego usamos el código anterior para obtener el valor del atributo de un solo elemento:
La copia del código es la siguiente:
función vacía () {
var c = nuevo contexto ();
// quiere obtener el atributo de identificación del elemento D, es decir, el valor de la siguiente expresión: "A/B/C.Name"
// Esta vez C no ha terminado, y debe modificar C a elementExpression
var root = new ElementExpression ('root');
var aELE = new ElementExpression ('A');
var bele = new ElementExpression ('B');
var cEle = new ElEmemExpression ('C');
var prop = new PropertyTermineAxpression ('nombre');
// Combinación
root.addele (AELE);
Aele.addele (Bele);
Bele.addele (Cele);
Cele.Addele (Prop);
console.log (el valor del nombre de la propiedad de 'C es =' + root.interpret (c));
// Si desea usar el mismo contexto y analizar continuamente, debe reinicializar el objeto de contexto
// Por ejemplo, debe ajustar el valor del nombre del atributo nuevamente en sucesión, por supuesto que puede recombinar los elementos
// Re-PARSE, siempre que se use el mismo contexto, el objeto de contexto debe reinicializar
c.reinit ();
console.log ('El valor del nombre de la propiedad de Reget C es =' + root.interpret (c));
} ();
Salida: el valor del nombre del atributo de c es = testc Recuperar el valor del nombre del atributo de c es = testc
explicar:
1. Función de modo de intérprete:
El patrón de intérprete utiliza objetos intérpretes para representar y procesar las reglas de sintaxis correspondientes. En general, un intérprete maneja una regla de sintaxis. Teóricamente, siempre que las expresiones que cumplen con la sintaxis pueden representar el objeto del intérprete y puedan formar un árbol de sintaxis abstracto, el patrón de intérprete se puede usar para manejarlo.
2. Reglas e intérpretes de sintaxis
Existe una correspondencia entre las reglas de sintaxis e intérpretes. En general, un intérprete maneja una regla de sintaxis, pero lo contrario no es cierto. Una regla de sintaxis puede tener múltiples interpretaciones y procesamiento, es decir, una regla de sintaxis puede corresponder a múltiples intérpretes.
3. Contexto en común
El contexto juega un papel muy importante en el modo intérprete. Porque el contexto se pasa a todos los intérpretes. Por lo tanto, el estado del intérprete se puede almacenar y acceder en el contexto. Por ejemplo, el intérprete anterior puede almacenar algunos datos en el contexto, y el último intérprete puede obtener estos valores.
Además, algunos datos fuera del intérprete pueden pasar por el contexto, pero el intérprete lo necesita, y algunos datos públicos globales.
También hay una función en el contexto, que es que puede proporcionar las funciones comunes de todos los objetos de intérprete, similar a las combinaciones de objetos, en lugar de usar la herencia para obtener funciones comunes, que pueden llamarse en cada objeto de intérprete.
4. ¿Quién construirá un árbol de sintaxis abstracto?
En el ejemplo anterior, es muy problemático construir manualmente el árbol de sintaxis abstracto en el lado del cliente, pero en el modo de intérprete, esta parte de la función no es involucrada, y solo es responsable de interpretar y procesar el árbol de sintaxis abstracto construido. Presentaremos que podemos proporcionar un analizador para convertir expresiones en árboles de sintaxis abstractos.
Hay otro problema, es decir, una regla de sintaxis puede corresponder a múltiples objetos de intérprete, es decir, el mismo elemento puede convertirse en múltiples objetos de intérprete, lo que significa que la misma expresión puede formar un árbol de sintaxis abstracto innecesario, lo que también hace que sea difícil construir un árbol de sintaxis abstracto y la carga de trabajo es muy grande.
5. Quién es responsable de explicar la operación
Mientras se define el árbol de sintaxis abstracto, el intérprete debe ser responsable de interpretar y ejecutar. Aunque existen diferentes reglas de sintaxis, el intérprete no es responsable de elegir qué objeto de intérprete usar para interpretar las reglas de sintaxis de ejecución. La función de seleccionar el intérprete se completa al construir un árbol de sintaxis abstracto.
6. El orden de llamada del modo intérprete
1) Crear un objeto de contexto
2) Crear múltiples objetos de intérpretes y combinar árboles de sintaxis abstracta
3) Invocar la operación de interpretación del objeto del intérprete
3.1) Almacene y acceda al estado del intérprete a través del contexto.
Para los objetos intérpretes no terminadores, llame recursivamente al objeto del subinterpreter que contiene.
La esencia del patrón del intérprete: *Implementación separada, interpretar la ejecución *
El módulo de intérprete utiliza un objeto de intérprete para procesar una regla de sintaxis para separar funciones complejas; Luego selecciona las funciones que deben ejecutarse, y combina estas funciones en un árbol de sintaxis abstracto que debe interpretarse y ejecutarse; Luego interpreta la ejecución de acuerdo con el árbol de sintaxis abstracto para implementar las funciones correspondientes.
En la superficie, el modo de intérprete se centra en el procesamiento de la sintaxis personalizada que no solemos usar; Pero, en esencia, la idea del modo intérprete es la separación, la encapsulación, la simplificación y es la misma que muchos modos.
Por ejemplo, el modo de intérprete se puede usar para simular la función del modo de estado. Si la sintaxis a procesar por el modo intérprete se simplifica a una sola marca de estado, el intérprete se considera un objeto de procesamiento para el estado. Para la misma sintaxis que representa el estado, puede haber muchos intérpretes no utilizados, es decir, hay muchos objetos con diferentes estados de procesamiento. Al crear un árbol de sintaxis abstracto, se simplifica crear el intérprete correspondiente basado en la marca de estado, y no hay necesidad de construir un árbol.
Del mismo modo, el modo intérprete puede simular la función de implementar el modo de política, la función del modo decorador, etc., especialmente el proceso de simular la función del modo decorador y el proceso de construir un árbol de sintaxis abstracto corresponderá naturalmente al proceso de combinación del decorador.
El modo intérprete generalmente no es rápido (en su mayoría muy lento), y la depuración de errores es difícil (la Parte 1: aunque la depuración es difícil, en realidad reduce la posibilidad de errores), pero sus ventajas son obvias. Puede controlar efectivamente la complejidad de las interfaces entre módulos. Para las funciones que tienen baja frecuencia de ejecución pero suficiente frecuencia de código, y son muy diversos, los intérpretes son muy adecuados para los modos. Además, el intérprete tiene otra ventaja menos notable, que es que puede ser convenientemente cruzado y multiplataforma.
Ventajas y desventajas del modo intérprete:
ventaja:
1. Sintaxis fácil de implementar
En modo intérprete, una regla de sintaxis se interpreta con un objeto de intérprete para interpretar la ejecución. Para la implementación del intérprete, la función se vuelve relativamente simple. Solo necesita considerar la implementación de esta regla de sintaxis, y no tiene que preocuparse por nada más. 2. Fácil de expandir una nueva sintaxis
Se debe precisamente a la forma en que un objeto intérprete es responsable de una regla de sintaxis de que extender una nueva sintaxis es muy fácil. La nueva sintaxis se ha ampliado, y solo necesita crear el objeto de intérprete correspondiente y usar este nuevo objeto de intérprete al crear un árbol de sintaxis abstracto.
defecto:
No es adecuado para sintaxis compleja
Si la sintaxis es particularmente compleja, el trabajo de construir el árbol de sintaxis abstracto requerido por el patrón de intérprete es muy difícil, y es posible construir múltiples árboles de sintaxis abstractos. Por lo tanto, el patrón de intérprete no es adecuado para la sintaxis compleja. Puede ser mejor usar un analizador de sintaxis o un generador de compiladores.
¿Cuándo usarlo?
Cuando hay un lenguaje que necesita ser interpretado y ejecutado, y las oraciones en ese lenguaje pueden representarse como un árbol de sintaxis abstracto, puede considerar usar el patrón de intérprete.
Al usar el modo intérprete, hay otras dos características que deben considerarse. Una es que la sintaxis debe ser relativamente simple. La sintaxis que es demasiado responsable no es adecuada para usar el modo intérprete. La otra es que los requisitos de eficiencia no son muy altos y los requisitos de eficiencia son muy altos, y no es adecuado para su uso.
La introducción anterior fue cómo obtener el valor de un solo elemento y el valor de un atributo de un solo elemento. Echemos un vistazo a cómo obtener los valores de múltiples elementos, así como los valores de los nombres entre sí en múltiples elementos, y las pruebas anteriores son árboles de sintaxis abstractos combinados artificialmente. También implementamos el siguiente analizador simple para convertir expresiones que se ajustan a la sintaxis definida anteriormente en árboles de sintaxis abstractos del intérprete implementado anteriormente: publiqué directamente el código:
La copia del código es la siguiente:
// Lea los valores de múltiples elementos o atributos
(función () {
/**
* Contexto, utilizado para contener alguna información global requerida por el intérprete
* @param {String} FilePathName [ruta y nombre del XML que debe leerse]
*/
contexto de funciones (filePathName) {
// múltiples elementos que se procesaron en el anterior
this.preeles = [];
// Objeto de documento XML
this.document = xmlutil.getroot (filePathName);
}
Context.prototype = {
// reinicializar el contexto
reinit: funct () {
this.preeles = [];
},
/**
* Métodos para el uso público de cada expresión
* Obtenga el elemento actual de acuerdo con el nombre del elemento principal y el elemento actual
* @param {elemento} Pele [Elemento principal]
* @param {string} elename [nombre del elemento actual]
* @return {elemento | nulo} [Elemento actual encontrado]
*/
getNoweles: function (Pele, elename) {
VAR Elements = [];
var tempnodeList = Pele.childNodes;
var nowele;
para (var i = 0, len = tempnodeList.length; i <len; i ++) {
if ((nowele = tempNodeList [i]). Nodetype === 1) {
if (nowele.nodename === elename) {
Elements.push (Nowele);
}
}
}
Elementos de retorno;
},
getPreeles: function () {
devolver esto.preeles;
},
setPreeles: function (noweles) {
this.preeles = noweles;
},
getDocument: function () {
devolver esto.document;
}
};
// Objeto de herramienta
// analizar xml para obtener el objeto de documento correspondiente
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 id = "4"> d4 </d> </b> </a> </root> ',' text/xml ');
return xmldom;
}
};
/**
* Los elementos se utilizan como intérpretes correspondientes a los no terminales para interpretar y ejecutar elementos intermedios
* @param {string} elename [nombre del elemento]
*/
function ElementExpression (elename) {
this.eles = [];
this.elename = elename;
}
ElementExpression.prototype = {
addele: function (elename) {
this.eles.push (elename);
devolver verdadero;
},
removeele: function (ele) {
para (var i = 0, len = this.eles.length; i <len; i ++) {
if (ele === this.eles [i]) {
this.eles.splice (i--, 1);
}
}
devolver verdadero;
},
interpretar: function (context) {
// Primero elimine el elemento actual en el contexto como el elemento principal
// Encuentra el elemento XML correspondiente al nombre del elemento actual y configúrelo al contexto
var peles = context.getPreeles ();
var ele = nulo;
var noweles = [];
if (! Peles.length) {
// indica que el elemento raíz ahora se obtiene
ele = context.getDocument (). DocumentElement;
Peles.push (ele);
context.setPreeles (Peles);
} demás {
Var Tempele;
para (var i = 0, len = peles.length; i <len; i ++) {
tempele = peles [i];
noweles = noweles.concat (context.getNoweles (tempele, this.elename));
// Detente si encuentra uno
if (noweles.length) rompa;
}
context.setPreeles ([Noweles [0]]);
}
var ss;
// bucle para llamar al método de interpretación del elemento infantil
para (var i = 0, len = this.eles.length; i <len; i ++) {
ss = this.eles [i] .interpret (contexto);
}
devolver ss;
}
};
/**
* Los elementos se utilizan como intérprete correspondiente al terminador
* @param {string} nombre [nombre del elemento]
*/
función elementterMinalAxpression (nombre) {
this.elename = nombre;
}
ElementterMinalAxpression.prototype = {
interpretar: function (context) {
var peles = context.getPreeles ();
var ele = nulo;
if (! Peles.length) {
ele = context.getDocument (). DocumentElement;
} demás {
ele = context.getNoweles (Peles [0], this.elename) [0];
}
// Obtener el valor del elemento
return ele.firstchild.nodeValue;
}
};
/**
* El atributo se usa como intérprete correspondiente al terminador
* @param {string} propName [nombre del atributo]
*/
function PropertyTerminalAxpression (propName) {
this.propname = propName;
}
PropertyTerminalAxpression.prototype = {
interpretar: function (context) {
// Obtener el valor del atributo del último elemento directamente
return context.getPreeles () [0] .getAttribute (this.propName);
}
};
/**
* Se utilizan múltiples atributos como intérprete correspondiente al terminador
* @param {string} propName [nombre del atributo]
*/
Función PropertySterminalExpression (propname) {
this.propname = propName;
}
PropertySterminalExpression.prototype = {
interpretar: function (context) {
var eles = context.getPreeles ();
var ss = [];
para (var i = 0, len = eles.length; i <len; i ++) {
ss.push (eles [i] .getAttribute (this.propname));
}
devolver ss;
}
};
/**
* Interpretación de objeto de procesamiento con múltiples elementos como terminadores
* @param {[type]} nombre [Descripción]
*/
Function ElementsTerminalAxpression (nombre) {
this.elename = nombre;
}
Elements terminalAxpression.prototype = {
interpretar: function (context) {
var peles = context.getPreeles ();
var noweles = [];
para (var i = 0, len = peles.length; i <len; i ++) {
noweles = noweles.concat (context.getNoweles (Peles [i], this.elename));
}
var ss = [];
para (i = 0, len = noweles.length; i <len; i ++) {
ss.push (noweles [i] .firstchild.nodeValue);
}
devolver ss;
}
};
/**
* Múltiples elementos se interpretan como no terminales
*/
función elementSexpression (nombre) {
this.elename = nombre;
this.eles = [];
}
Elementsexpression.prototype = {
interpretar: function (context) {
var peles = context.getPreeles ();
var noweles = [];
para (var i = 0, len = peles.length; i <len; i ++) {
noweles = noweles.concat (context.getNoweles (Peles [i], this.elename));
}
context.setPreeles (Noweles);
var ss;
para (i = 0, len = this.eles.length; i <len; i ++) {
ss = this.eles [i] .interpret (contexto);
}
devolver ss;
},
addele: function (ele) {
this.eles.push (ele);
devolver verdadero;
},
removeele: function (ele) {
para (var i = 0, len = this.eles.length; i <len; i ++) {
if (ele === this.eles [i]) {
this.eles.splice (i--, 1);
}
}
devolver verdadero;
}
};
función vacía () {
// "root/a/b/d $"
var c = nuevo contexto ('intreeter.xml');
var root = new ElementExpression ('root');
var aELE = new ElementExpression ('A');
var bele = new ElementExpression ('B');
var dele = new ElementsTerminalAxpression ('D');
root.addele (AELE);
Aele.addele (Bele);
Bele.addele (Dele);
var ss = root.interpret (c);
para (var i = 0, len = ss.length; i <len; i ++) {
console.log ('d valor es =' + ss [i]);
}
} ();
función vacía () {
// a/b/d $ .id $
var c = nuevo contexto ('intreeter.xml');
var root = new ElementExpression ('root');
var aELE = new ElementExpression ('A');
var bele = new ElementExpression ('B');
var dele = new ElementSexpression ('D');
var prop = new PropertySterminalAxpression ('id');
root.addele (AELE);
Aele.addele (Bele);
Bele.addele (Dele);
Dele.addele (Prop);
var ss = root.interpret (c);
para (var i = 0, len = ss.length; i <len; i ++) {
console.log ('D La propiedad de la propiedad El valor es =' + ss [i]);
}
} ();
// analizador
/**
* Ideas de implementación del analizador
* 1. Descompuesto las expresiones aprobadas por el cliente, descompondéralas en elementos uno por uno y use un modelo analítico correspondiente para encapsular cierta información sobre este elemento.
* 2. Conviértalo en el objeto analizador correspondiente basado en la información de cada elemento.
* 3. Combine estos objetos analizadores para obtener un árbol de sintaxis abstracto.
*
* ¿Por qué no se fusiona 1 y 2, y descompones directamente un elemento y lo convierte en el objeto analizador correspondiente?
* 1. Separación funcional, no haga que las funciones de un método sean demasiado complicadas.
* 2. Para la modificación y extensión futura, la sintaxis ahora es simple, por lo que hay poco que considerar al convertir en un objeto analizador, y no es difícil convertirlo directamente, pero si la sintaxis es complicada, la conversión directa será muy desordenada.
*/
/**
* Se utiliza para encapsular los atributos correspondientes de cada elemento analizado
*/
función parsermodel () {
// si un solo valor
esto.singlevalue;
// si se trata de un atributo, un atributo o un elemento
this.propertyvalue;
// si terminar
this.end;
}
Parsermodel.prototype = {
isend: function () {
devolver esto.end;
},
setend: function (end) {
this.end = end;
},
issingleValue: function () {
devolver esto.singlevalue;
},
setSingleValue: function (OneValue) {
this.singleValue = OneValue;
},
isPropertyValue: functer () {
devolver esto.propertyvalue;
},
setPropertyValue: function (PropertyValue) {
this.PropertyValue = PropertyValue;
}
};
var parser = function () {
var Backlash = '/';
var dot = '.';
var dólar = '$';
// Registre los nombres de elementos que deben analizarse de acuerdo con el orden de descomposición
var listele = null;
// Comience el primer paso ----------------------------------------------------------------------------------------------------------------------
/**
* Pase en una expresión de cadena, y luego analízalo y combínelo en un árbol de sintaxis abstracto
* @param {string} expr [Describa la expresión de cadena para tomar el valor]
* @return {object} [Árbol de sintaxis abstracto correspondiente]
*/
función parsEmappath (expr) {
// Primero divida la cadena de acuerdo con "/"
var tokenizer = expr.split (reacción);
// Tabla de valores descompuestos
var mappath = {};
var OnePath, Elename, Propname;
var dotIndex = -1;
para (var i = 0, len = tokenizer.length; i <len; i ++) {
OnePath = Tokenizer [i];
if (tokenizer [i + 1]) {
// Hay otro valor, lo que significa que este no es el último elemento
// Según la sintaxis actual, el atributo debe estar al final, por lo que no es el atributo.
setParsePath (falso, unpath, falso, mappath);
} demás {
// es el final
dotIndex = OnePath.IndexOf (DOT);
if (dotIndex> = 0) {
// Significa que desea obtener el valor del atributo, así que divídalo de acuerdo con ".
// El primero es el nombre del elemento, y el segundo es el nombre del atributo
elename = OnePath.Substring (0, dotIndex);
propname = OnePath.Substring (dotIndex + 1);
// El elemento frente a la propiedad, naturalmente, no es el último, ni es la propiedad
setParsePath (falso, elename, falso, mappath);
// Establecer atributos. Según la definición de sintaxis actual, el atributo solo puede ser el último.
setParsePath (verdadero, propname, verdadero, mappath);
} demás {
// Las instrucciones se toman como el valor del elemento y el valor del último elemento
setParsePath (verdadero, unpath, falso, mappath);
}
romper;
}
}
Mappato de regreso;
}
/**
* Establezca el nombre del elemento a analizar de acuerdo con la ubicación y el nombre descompuestos
* @param {boolean} end [es el último]
* @param {string} ele [nombre del elemento]
* @param {boolean} PropertyValue [si tomar la propiedad]
* @param {object} mappath [Establezca el nombre del elemento que debe analizarse y la tabla del modelo de análisis correspondiente al elemento]
*/
función setParsePath (end, ele, PropertyValue, mappath) {
var pm = nuevo parsermodel ();
PM.Setend (final);
// Si el símbolo "$" no es un valor
pm.setsingleValue (! (ele.indexof (dólar)> = 0));
PM.SetPropertyValue (PropertyValue);
// eliminar "$"
ele = ele.replace (dólar, '');
mappath [ele] = pm;
listele.push (ele);
}
// Inicie el segundo paso ----------------------------------------------------------------------------------------------------------------------
/**
* Convierta el nombre del elemento descompuesto en el objeto de intérprete correspondiente de acuerdo con el modelo analítico correspondiente.
* @param {object} mappath [el nombre del elemento descompuesto a analizar y el modelo de análisis correspondiente al elemento]
* @return {array} [Convierta cada elemento en una matriz de objetos de intérprete correspondientes]
*/
función mappath2interpreter (mappath) {
var list = [];
var PM, clave;
var obj = null;
// es necesario convertirlo en objetos intérpretes en el orden de descomposición
para (var i = 0, len = listele.length; i <len; i ++) {
clave = listele [i];
PM = mappath [clave];
// no el último
if (! pm.isend ()) {
if (pm.issingleValue ())
// es un valor, conversión
obj = new ElementExpression (clave);
demás
// son valores múltiples, conversión
obj = new ElementSexpression (clave);
} demás {
// es el último
// es el valor del atributo
if (pm.isPropertyValue ()) {
if (pm.issingleValue ())
obj = New PropertyTerminalAxpression (clave);
demás
obj = nuevo PropertySterminalExpression (clave);
// tomar el valor del elemento
} demás {
if (pm.issingleValue ())
obj = new ElementTerminalAxpression (clave);
demás
obj = nuevos ElementsTerminalAxpression (clave);
}
}
list.push (obj);
}
lista de devolución;
}
// Inicie el tercer paso ----------------------------------------------------------------------------------------------------------------------
/**
* Construir un árbol de sintaxis abstracto
* @param {[type]} List [Convierta cada elemento en una matriz de objetos de intérprete correspondientes]
* @return {[type]} [Descripción]
*/
función buildtree (list) {
// El primer objeto, también el objeto devuelto, es la raíz del árbol de sintaxis abstracta
var returnReadxmlexpr = null;
// Defina el objeto anterior
var prereadxmlexpr = null;
var readxml, ele, eles;
para (var i = 0, len = list.length; i <len; i ++) {
readxml = list [i];
// Descripción es el primer elemento
if (prereadxmlexpr === null) {
prereadxmlexpr = readXml;
returnReadxmlexpr = readXml;
// Agregue el elemento al objeto anterior y configure el objeto en Oldre
// como nodo principal del siguiente objeto
} demás {
if (prereadxmlexpr instanceOf elementExpression) {
ELE = PREREADXMLEXPR;
ELE.Addele (readXml);
prereadxmlexpr = readXml;
} else if (prereadxmlexpr instanceOf elementsexpression) {
eles = prereadxmlexpr;
eles.addele (readxml);
prereadxmlexpr = readXml;
}
}
}
return returnReadxMlexPR;
}
devolver {
// Método público
parse: function (exp) {
listEle = [];
var mappath = parsEmappath (expr);
var list = mappath2Interpreter (mappath);
return buildtree (lista);
}
};
} ();
función vacía () {
// Prepara el contexto
var c = nuevo contexto ('intreeter.xml');
// Obtener el árbol de sintaxis abstracto al analizarlo
var readxmlexpr = parser.parse ('root/a/b/d $ .id $');
// solicitar el análisis y obtener el valor de retorno
var ss = readxmlexpr.interpret (c);
console.log ('------------ Parsing --------------');
para (var i = 0, len = ss.length; i <len; i ++) {
console.log ('D La propiedad de la propiedad El valor es =' + ss [i]);
}
console.log ('--------------- Parsed --------------');
// Si desea usar el mismo contexto y analizar continuamente, debe reinicializar el objeto de contexto
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--------------