Shenma é um "modo de intérprete"?
Vamos abrir o "GoF" primeiro e dar uma olhada na definição:
Dado um idioma, defina uma representação de sua gramática e defina um intérprete que usa essa representação para interpretar frases no idioma.
Antes do início, ainda preciso popularizar vários conceitos:
Árvore de sintaxe abstrata:
O padrão de intérprete não explica como criar uma árvore de sintaxe abstrata. Não envolve análises gramaticais. Uma árvore de sintaxe abstrata pode ser concluída por um programa de análise de sintaxe acionada por mesa, ou criado pelo programa de análise de sintaxe manuscrito (geralmente descida), ou fornecido diretamente pelo cliente.
Analisador:
Refere -se a um programa que descreve as expressões exigidas pela chamada do cliente e forma uma árvore de sintaxe abstrata após a análise.
Intérprete:
Refere -se a um programa que explica a árvore de sintaxe abstrata e executa as funções correspondentes de cada nó.
Um pré -requisito importante para usar o padrão de intérprete é definir um conjunto de regras gramaticais, também conhecidas como gramática. Independentemente de as regras dessa gramática serem simples ou complexas, essas regras devem ser incluídas, porque o modo de intérprete é analisar e executar funções correspondentes de acordo.
Vamos dar uma olhada no diagrama da estrutura e na descrição do modo de intérprete:
Resumo Expressão: define a interface do intérprete e concorda com a operação de interpretação do intérprete.
TerminalExpression: TerminalExpression, usado para implementar operações relacionadas ao Terminator em regras de sintaxe, não contém mais outros intérpretes. Se o padrão de combinação for usado para construir uma árvore de sintaxe abstrata, é equivalente a um objeto foliar no padrão de combinação e pode haver vários interpretadores do Terminator.
Expressão não terminal: um intérprete não terminal, usado para implementar operações não relacionadas ao terminal nas regras de sintaxe. Geralmente, um intérprete corresponde a uma regra de sintaxe e pode conter outros intérpretes. Se o padrão de composição for usado para construir uma árvore de sintaxe abstrata, é equivalente a um objeto combinado no padrão de composição. Pode haver vários intérpretes não terminais.
Contexto: o contexto, geralmente contém dados exigidos por cada intérprete ou funções públicas.
Cliente: um cliente refere -se a um cliente que usa um intérprete. Geralmente, as expressões feitas de acordo com a sintaxe do idioma são convertidas em uma árvore de sintaxe abstrata descrita pelo objeto de intérprete e, em seguida, é chamada uma operação de explicação.
Aqui usamos um exemplo XML para entender o padrão de intérprete:
Primeiro, precisamos projetar uma gramática simples para a expressão. Para fins gerais, use root para representar o elemento raiz, ABC etc. para representar o elemento. Um XML simples é o seguinte:
A cópia do código é a seguinte:
<? xml versão = "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>
A gramática da expressão da convenção é a seguinte:
1. Obtenha o valor de um único elemento: Inicie do elemento raiz e até o elemento que você deseja obter o valor. O meio do elemento é separado por "/" e não "/" é adicionado antes do elemento raiz. Por exemplo, a expressão "raiz/a/b/c" significa obter os valores do elemento c sob o elemento raiz, elemento A, elemento b e elemento c.
2. Obtenha o valor do atributo de um único elemento: é claro que existem vários atributos. O atributo para obter o valor deve ser o atributo do último elemento da expressão. Adicionar "." Após o último elemento e adicione o nome do atributo. Por exemplo, a expressão "raiz/a/b/c.name" significa obter o valor do atributo de nome do elemento raiz, elemento A, elemento B, elemento c.
3. Obtenha o valor do mesmo nome do elemento, é claro, existem vários elementos. O elemento para obter o valor deve ser o último elemento da expressão e adicionar "$" após o último elemento. Por exemplo, a expressão "raiz/a/b/d $" representa a coleta de valores de vários elementos d sob o elemento raiz, sob o elemento A e sob o elemento B.
4. Obtenha o valor do atributo com o mesmo nome do elemento, é claro, existem múltiplos: o elemento para obter o valor do atributo deve ser o último elemento da expressão e adicione "$" após o último elemento. Por exemplo, a expressão "raiz/a/b/d $ .id $" representa a coleta de valores de vários elementos d atributos de ID do elemento raiz, sob o elemento A e sob o elemento B.
O XML acima, correspondente à árvore de sintaxe abstrata, e a possível estrutura é mostrada na figura:
Vamos dar uma olhada no código específico abaixo:
1. Defina o contexto:
A cópia do código é a seguinte:
/**
* Contexto, usado para conter algumas informações globais exigidas pelo intérprete
* @param {string} filepathname [caminho e nome do xml que precisa ser lido]
*/
contexto da função (filepathname) {
// O elemento processado anterior
this.preele = null;
// Objeto de documento XML
this.document = xmlutil.getroot (filepathname);
}
Context.prototype = {
// reinicializar o contexto
Reinit: function () {
this.preele = null;
},
/**
* Métodos para uso público de cada expressão
* Obtenha o elemento atual de acordo com o nome do elemento pai e o elemento atual
* @param {element} pele [elemento pai]
* @param {string} elename [nome do elemento atual]
* @return {element | null} [elemento atual encontrado]
*/
getNowele: function (pele, elename) {
var tempnodelist = PELE.CHILDNODES;
var nowele;
for (var i = 0, len = tempnodelist.length; i <len; i ++) {
if ((agoraele = tempnodelist [i]). nodeType === 1)
if (agoraele.nodename === Elename)
retornar agora;
}
retornar nulo;
},
getpreele: function () {
devolver este.preele;
},
setpreele: function (preele) {
this.preele = preele;
},
getDocument: function () {
retornar this.Document;
}
};
No contexto, usei um objeto de ferramenta XMLUTIL para obter xmldom. Abaixo, estou usando o DOMPaser de DOM3. Alguns navegadores podem não apoiá -lo. Por favor, use o navegador base:
A cópia do código é a seguinte:
// objeto de ferramenta
// Parse XML para obter o objeto de documento correspondente
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> < id = "4"> d4 </d> </b> </a> </sic> ',' text/xml ');
retornar xmldom;
}
};
Aqui está o código para o intérprete:
A cópia do código é a seguinte:
/**
* Os elementos são usados como intérpretes correspondentes a não terminais para interpretar e executar elementos intermediários
* @param {string} elename [nome do elemento]
*/
Função ElementExpression (elename) {
this.eles = [];
this.Elename = elename;
}
ElementExpression.prototype = {
addEle: function (elename) {
this.eles.push (elename);
retornar true;
},
removele: function (ele) {
for (var i = 0, len = this.eles.length; i <len; i ++) {
if (ele === this.eles [i])
this.eles.splice (i--, 1);
}
retornar true;
},
Interprete: função (contexto) {
// Primeiro, retire o elemento atual no contexto como o elemento pai
// Encontre o elemento XML correspondente ao nome atual do elemento e defina -o de volta ao contexto
var pele = context.getpreele ();
if (! pele) {
// indica que o elemento raiz agora é obtido
context.setpreele (context.getDocument (). DocumentElement);
} outro {
// Obtenha o elemento atual com base no nome do elemento pai e do elemento a ser pesquisado
var nowele = context.getNowele (pele, this.elename);
// Coloque o elemento atualmente recuperado no contexto
context.setpreele (Nowele);
}
var ss;
// loop para chamar o método de interpretação do elemento filho
for (var i = 0, len = this.eles.length; i <len; i ++) {
ss = this.eles [i] .interpret (contexto);
}
// retorna o resultado da explicação do último intérprete. Geralmente, o último intérprete é o intérprete do Terminator.
retornar SS;
}
};
/**
* Os elementos são usados como o intérprete correspondente ao terminador
* @param {string} nome [nome do elemento]
*/
Função elementTerMinalExpression (nome) {
this.Elename = nome;
}
ElementMerMinalExpression.prototype = {
Interprete: função (contexto) {
var pele = context.getpreele ();
var ele = nulo;
if (! pele) {
ele = context.getDocument (). DocumentElement;
} outro {
ele = context.getNowele (pele, this.elename);
context.setpreele (ELE);
}
// obtenha o valor do elemento
retornar ele.firstchild.nodEvalue;
}
};
/**
* O atributo é usado como o intérprete correspondente ao terminador
* @param {string} propName [nome do atributo]
*/
Função PropertyMerMinalExpression (PropName) {
this.propName = propName;
}
Propertiminalexpression.prototype = {
Interprete: função (contexto) {
// obtenha o valor do último elemento atributo diretamente
retorno context.getpreele (). getAttribute (this.propName);
}
};
Vamos primeiro olhar como usar o intérprete para obter o valor de um único elemento:
A cópia do código é a seguinte:
function void () {
var c = novo contexto ();
// deseja obter o valor de vários elementos d, ou seja, o valor da seguinte expressão: "raiz/a/b/c"
// Primeiro, você precisa construir uma árvore de sintaxe abstrata para o intérprete
var root = new ElementExpression ('raiz');
var aele = new ElementExpression ('a');
var bele = new ElementExpression ('b');
var celebr = new ElementMerMinalExpression ('C');
// combinação
root.addele (aele);
aele.addele (Bele);
Bele.Addele (celebridade);
console.log ('C de C é =' + root.interpret (c));
} ();
Saída: o valor de C é = 12345
Em seguida, usamos o código acima para obter o valor do atributo de um único elemento:
A cópia do código é a seguinte:
function void () {
var c = novo contexto ();
// quer obter o atributo de identificação do elemento D, ou seja, o valor da seguinte expressão: "A/b/c.name"
// Desta vez C não acabou e você precisa modificar C para elemento elemento
var root = new ElementExpression ('raiz');
var aele = new ElementExpression ('a');
var bele = new ElementExpression ('b');
var celeb = new ElementExpression ('c');
var prop = new PropertyMerMinalExpression ('nome');
// combinação
root.addele (aele);
aele.addele (Bele);
Bele.Addele (celebridade);
celeb.addele (prop);
console.log ('C' C de c 'Cune de nome do valor é =' + root.interpret (c));
// Se você quiser usar o mesmo contexto e analisar continuamente, precisa reinicializar o objeto de contexto
// Por exemplo, você precisa re-obter o valor do nome do atributo novamente em sucessão, é claro que você pode recombinar os elementos
// re-separação, desde que o mesmo contexto seja usado, o objeto de contexto precisa ser reinicializado
C.Reinit ();
console.log ('o valor do nome da propriedade do Reget C is =' + root.interpret (c));
} ();
Saída: o valor do nome do atributo de c is = testc recuperar o valor do nome do atributo de c is = testc
explicar:
1. Função do modo de intérprete:
O padrão de intérprete usa objetos de intérprete para representar e processar regras de sintaxe correspondentes. Geralmente, um intérprete lida com uma regra de sintaxe. Teoricamente, desde que as expressões compatíveis com sintaxe possam ser representadas pelo objeto intérprete e formar uma árvore de sintaxe abstrata, o padrão de intérprete pode ser usado para lidar com isso.
2. Regras e intérpretes de sintaxe
Existe uma correspondência entre regras de sintaxe e intérpretes. Geralmente, um intérprete lida com uma regra de sintaxe, mas o oposto não é verdadeiro. Uma regra de sintaxe pode ter múltiplas interpretações e processamento, ou seja, uma regra de sintaxe pode corresponder a vários intérpretes.
3. Commonalidade de contexto
O contexto desempenha um papel muito importante no modo de intérprete. Porque o contexto é passado para todos os intérpretes. Portanto, o estado do intérprete pode ser armazenado e acessado no contexto. Por exemplo, o intérprete anterior pode armazenar alguns dados no contexto, e o último intérprete pode obter esses valores.
Além disso, alguns dados fora do intérprete podem ser transmitidos pelo contexto, mas o intérprete precisa dele e alguns dados públicos globais.
Há também uma função no contexto, que é que ele pode fornecer as funções comuns de todos os objetos de intérprete, semelhantes às combinações de objetos, em vez de usar a herança para obter funções comuns, que podem ser chamadas em cada objeto de intérprete.
4. Quem construirá uma árvore de sintaxe abstrata
No exemplo anterior, é muito problemático construir manualmente a árvore de sintaxe abstrata no lado do cliente, mas no modo intérprete, essa parte da função não está envolvida e é responsável apenas por interpretar e processar a árvore de sintaxe abstrata construída. Introduziremos que podemos fornecer um analisador para converter expressões em árvores abstratas de sintaxe.
Há outro problema, ou seja, uma regra de sintaxe pode corresponder a múltiplos objetos de intérprete, ou seja, o mesmo elemento pode ser convertido em múltiplos objetos de intérprete, o que significa que a mesma expressão pode formar uma árvore de sintaxe abstrata desnecessária, o que também dificulta a construção de uma árvore abstrata de sintaxe e a carga de trabalho é muito grande.
5. Quem é responsável por explicar a operação
Enquanto a árvore de sintaxe abstrata for definida, o intérprete deverá ser responsável pela interpretação e execução. Embora existam regras de sintaxe diferentes, o intérprete não se responsabiliza por escolher qual objeto de intérprete usar para interpretar as regras de sintaxe da execução. A função de selecionar o intérprete é concluída ao criar uma árvore de sintaxe abstrata.
6. A ordem de chamada do modo de intérprete
1) Crie um objeto de contexto
2) Crie múltiplos objetos de intérprete e combine árvores abstratas de sintaxe
3) Invoque a operação de interpretação do objeto de intérprete
3.1) Armazene e acesse o estado do intérprete através do contexto.
Para objetos de intérprete não do terminador, chama recursivamente o objeto Subinterpreter que ele contém.
A essência do padrão do intérprete: *Separe a implementação, interprete a execução *
O módulo intérprete usa um objeto de intérprete para processar uma regra de sintaxe para separar funções complexas; Em seguida, seleciona as funções que precisam ser executadas e combina essas funções em uma árvore de sintaxe abstrata que precisa ser interpretada e executada; Em seguida, interpreta a execução de acordo com a árvore de sintaxe abstrata para implementar funções correspondentes.
Na superfície, o modo de intérprete se concentra no processamento da sintaxe personalizada que geralmente não usamos; Mas, em essência, a idéia do modo de intérprete é então separação, encapsulamento, simplificação e é a mesma que muitos modos.
Por exemplo, o modo de intérprete pode ser usado para simular a função do modo de estado. Se a sintaxe a ser processada pelo modo de intérprete for simplificada para apenas uma marca de estado, o intérprete será considerado um objeto de processamento para o estado. Para a mesma sintaxe que representa o estado, pode haver muitos intérpretes não utilizados, ou seja, existem muitos objetos com diferentes estados de processamento. Ao criar uma árvore de sintaxe abstrata, é simplificado para criar o intérprete correspondente com base na marca de estado e não há necessidade de construir uma árvore.
Da mesma forma, o modo de intérprete pode simular a função de implementar o modo de política, a função do modo decorador etc., especialmente o processo de simulação da função do modo decorador e o processo de construção de uma árvore de sintaxe abstrata corresponderá naturalmente ao processo de combinação do decorador.
O modo de intérprete geralmente não é rápido (principalmente muito lento), e a depuração de erros é difícil (parte 1: embora a depuração seja difícil, na verdade reduz a possibilidade de erros), mas suas vantagens são óbvias. Ele pode efetivamente controlar a complexidade das interfaces entre os módulos. Para funções com baixa frequência de execução, mas alta frequência de código suficiente e são muito diversas, os intérpretes são muito adequados para os modos. Além disso, o intérprete tem outra vantagem menos perceptível, que é que pode ser convenientemente entre linguagem cruzada e plataforma cruzada.
Vantagens e desvantagens do modo de intérprete:
vantagem:
1. Fácil de implementar sintaxe
No modo de intérprete, uma regra de sintaxe é interpretada com um objeto de intérprete para interpretar a execução. Para a implementação do intérprete, a função se torna relativamente simples. Você só precisa considerar a implementação desta regra de sintaxe e não precisa se preocupar com mais nada. 2. Fácil de expandir a nova sintaxe
É precisamente por causa da maneira como um objeto de intérprete é responsável por uma regra de sintaxe que estender a nova sintaxe é muito fácil. A nova sintaxe foi expandida e você só precisa criar o objeto de intérprete correspondente e usar esse novo objeto de intérprete ao criar uma árvore de sintaxe abstrata.
deficiência:
Não é adequado para sintaxe complexa
Se a sintaxe for particularmente complexa, o trabalho de construção da árvore de sintaxe abstrata exigida pelo padrão do intérprete é muito difícil e é possível construir várias árvores abstratas de sintaxe. Portanto, o padrão de intérprete não é adequado para sintaxe complexa. Pode ser melhor usar um analisador de sintaxe ou gerador de compiladores.
Quando usá -lo?
Quando há um idioma que precisa ser interpretado e executado, e as frases nesse idioma podem ser representadas como uma árvore de sintaxe abstrata, você pode considerar o uso do padrão de intérprete.
Ao usar o modo de intérprete, há dois outros recursos que precisam ser considerados. Uma é que a sintaxe deve ser relativamente simples. A sintaxe que é muito responsável não é adequada para usar o modo de intérprete. O outro é que os requisitos de eficiência não são muito altos e os requisitos de eficiência são muito altos e não são adequados para uso.
A introdução anterior foi como obter o valor de um único elemento e o valor de um único atributo elemento. Vamos dar uma olhada em como obter os valores de vários elementos, bem como os valores dos nomes um do outro em vários elementos, e os testes anteriores são todas as árvores de sintaxe abstrata combinadas artificialmente. Também implementamos o seguinte analisador simples para converter expressões que estão em conformidade com a sintaxe definida acima em árvores de sintaxe abstrata do intérprete implementadas acima: Publiquei diretamente o código:
A cópia do código é a seguinte:
// Leia os valores de vários elementos ou atributos
(function () {
/**
* Contexto, usado para conter algumas informações globais exigidas pelo intérprete
* @param {string} filepathname [caminho e nome do xml que precisa ser lido]
*/
contexto da função (filepathname) {
// vários elementos que foram processados no anterior
this.preeles = [];
// Objeto de documento XML
this.document = xmlutil.getroot (filepathname);
}
Context.prototype = {
// reinicializar o contexto
Reinit: function () {
this.preeles = [];
},
/**
* Métodos para uso público de cada expressão
* Obtenha o elemento atual de acordo com o nome do elemento pai e o elemento atual
* @param {element} pele [elemento pai]
* @param {string} elename [nome do elemento atual]
* @return {element | null} [elemento atual encontrado]
*/
getNoweles: function (pele, elename) {
var elements = [];
var tempnodelist = PELE.CHILDNODES;
var nowele;
for (var i = 0, len = tempnodelist.length; i <len; i ++) {
if ((agoraele = tempnodelist [i]). nodeType === 1) {
if (agoraele.nodename === elename) {
Elements.push (Nowele);
}
}
}
elementos de retorno;
},
getpreeles: function () {
devolver this.preeles;
},
setpreeles: function (noweles) {
this.preeles = agora;
},
getDocument: function () {
retornar this.Document;
}
};
// objeto de ferramenta
// Parse XML para obter o objeto de documento correspondente
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> < id = "4"> d4 </d> </b> </a> </sic> ',' text/xml ');
retornar xmldom;
}
};
/**
* Os elementos são usados como intérpretes correspondentes a não terminais para interpretar e executar elementos intermediários
* @param {string} elename [nome do elemento]
*/
Função ElementExpression (elename) {
this.eles = [];
this.Elename = elename;
}
ElementExpression.prototype = {
addEle: function (elename) {
this.eles.push (elename);
retornar true;
},
removele: function (ele) {
for (var i = 0, len = this.eles.length; i <len; i ++) {
if (ele === this.eles [i]) {
this.eles.splice (i--, 1);
}
}
retornar true;
},
Interprete: função (contexto) {
// Primeiro, retire o elemento atual no contexto como o elemento pai
// Encontre o elemento XML correspondente ao nome atual do elemento e defina -o de volta ao contexto
var peles = context.getpreeles ();
var ele = nulo;
var noweles = [];
if (! peles.length) {
// indica que o elemento raiz agora é obtido
ele = context.getDocument (). DocumentElement;
peles.push (ele);
context.setpreeles (peles);
} outro {
var tempele;
for (var i = 0, len = peles.length; i <len; i ++) {
tempele = peles [i];
Noweles = noweles.concat (context.getNoweles (tempele, this.elename));
// pare se você encontrar um
if (noweleles.length) quebra;
}
context.setpreeles ([Noweles [0]]);
}
var ss;
// loop para chamar o método de interpretação do elemento filho
for (var i = 0, len = this.eles.length; i <len; i ++) {
ss = this.eles [i] .interpret (contexto);
}
retornar SS;
}
};
/**
* Os elementos são usados como o intérprete correspondente ao terminador
* @param {string} nome [nome do elemento]
*/
Função elementTerMinalExpression (nome) {
this.Elename = nome;
}
ElementMerMinalExpression.prototype = {
Interprete: função (contexto) {
var peles = context.getpreeles ();
var ele = nulo;
if (! peles.length) {
ele = context.getDocument (). DocumentElement;
} outro {
ele = context.getNoweles (peles [0], this.elename) [0];
}
// obtenha o valor do elemento
retornar ele.firstchild.nodEvalue;
}
};
/**
* O atributo é usado como o intérprete correspondente ao terminador
* @param {string} propName [nome do atributo]
*/
Função PropertyMerMinalExpression (PropName) {
this.propName = propName;
}
Propertiminalexpression.prototype = {
Interprete: função (contexto) {
// obtenha o valor do último elemento atributo diretamente
retorno context.getpreeles () [0] .getAttribute (this.propName);
}
};
/**
* Atributos múltiplos são usados como intérprete correspondente ao terminador
* @param {string} propName [nome do atributo]
*/
Função PropertysterMinalexpression (PropName) {
this.propName = propName;
}
PropertysterMinalExpression.prototype = {
Interprete: função (contexto) {
var elees = context.getpreeles ();
var ss = [];
for (var i = 0, len = ele.length; i <len; i ++) {
ss.push (ele [i] .getAttribute (this.propName));
}
retornar SS;
}
};
/**
* Objeto de processamento de interpretação com vários elementos como terminadores
* @param {[type]} nome [descrição]
*/
Função ElementSterMinalExpression (Nome) {
this.Elename = nome;
}
ElementSTerMinalExpression.prototype = {
Interprete: função (contexto) {
var peles = context.getpreeles ();
var noweles = [];
for (var i = 0, len = peles.length; i <len; i ++) {
Noweles = noweles.concat (context.getNoweles (peles [i], this.elename));
}
var ss = [];
for (i = 0, len = noweles.length; i <len; i ++) {
ss.push (agora [i] .firstchild.nodevalue);
}
retornar SS;
}
};
/**
* Vários elementos são interpretados como não-terminais
*/
função elementSexpression (nome) {
this.Elename = nome;
this.eles = [];
}
ElementSexpression.prototype = {
Interprete: função (contexto) {
var peles = context.getpreeles ();
var noweles = [];
for (var i = 0, len = peles.length; i <len; i ++) {
Noweles = noweles.concat (context.getNoweles (peles [i], this.elename));
}
context.setpreeles (Noweles);
var ss;
for (i = 0, len = this.eles.length; i <len; i ++) {
ss = this.eles [i] .interpret (contexto);
}
retornar SS;
},
addEle: function (ele) {
this.eles.push (ele);
retornar true;
},
removele: function (ele) {
for (var i = 0, len = this.eles.length; i <len; i ++) {
if (ele === this.eles [i]) {
this.eles.splice (i--, 1);
}
}
retornar true;
}
};
function void () {
// "root/a/b/d $"
var c = novo contexto ('Interpreter.xml');
var root = new ElementExpression ('raiz');
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);
for (var i = 0, len = ss.length; i <len; i ++) {
console.log ('D valor é =' + ss [i]);
}
} ();
function void () {
// a/b/d $ .id $
var c = novo contexto ('Interpreter.xml');
var root = new ElementExpression ('raiz');
var aele = new ElementExpression ('a');
var bele = new ElementExpression ('b');
var dele = new ElementSexpression ('d');
var prop = new PropertysterMinalexpression ('id');
root.addele (aele);
aele.addele (Bele);
Bele.addele (Dele);
dele.addele (prop);
var ss = root.interpret (c);
for (var i = 0, len = ss.length; i <len; i ++) {
console.log ('D Valor da identificação da propriedade é =' + ss [i]);
}
} ();
// Parser
/**
* Idéias de implementação do analisador
* 1. Decomponha as expressões passadas pelo cliente, as decompõe em elementos um por um e use um modelo analítico correspondente para encapsular algumas informações sobre esse elemento.
* 2. Converta -o no objeto Parser correspondente com base nas informações de cada elemento.
* 3. Combine esses objetos de analisador para obter uma árvore de sintaxe abstrata.
*
* Por que você não mescla 1 e 2, e decompõe diretamente um elemento e converte -o no objeto de analisador correspondente?
* 1. Separação funcional, não torne as funções de um método muito complicado.
* 2. Para modificação e extensão futuras, a sintaxe agora é simples, então há pouco a considerar ao converter em um objeto de analisador, e não é difícil converter diretamente, mas se a sintaxe for complicada, a conversão direta será muito confusa.
*/
/**
* Usado para encapsular os atributos correspondentes de cada elemento analisado
*/
função parsermodel () {
// seja um único valor
this.singleValue;
// seja um atributo, um atributo ou um elemento
this.PropertyValue;
// se deve encerrar
this.end;
}
Parsermodel.prototype = {
isend: function () {
devolver este.END;
},
setEnd: function (end) {
this.end = end;
},
IssingleValue: function () {
devolver este.singleValue;
},
SetsingleValue: function (OneValue) {
this.singleValue = OneValue;
},
isPropertyValue: function () {
retornar this.PropertyValue;
},
setPropertyValue: function (PropertyValue) {
this.PropertyValue = PropertyValue;
}
};
var parser = function () {
var a reação = '/';
var dot = '.';
var dólar = '$';
// registra os nomes dos elementos que precisam ser analisados de acordo com a ordem de decomposição
var listEle = nulo;
// Comece o primeiro passo ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
/**
* Passe em uma expressão de string e depois analise -a e combine -a em uma árvore de sintaxe abstrata
* @param {string} expr [descreva a expressão da string para pegar o valor]
* @return {objeto} [árvore de sintaxe abstrata correspondente]
*/
função parsemappath (expr) {
// primeiro divida a string de acordo com "/"
var tokenizer = expr.split (backlash);
// Tabela de valores decompostos
var mappath = {};
var onepath, elename, propName;
var dotindex = -1;
for (var i = 0, len = tokenizer.length; i <len; i ++) {
OnePath = tokenizer [i];
if (tokenizer [i + 1]) {
// Há outro valor, o que significa que este não é o último elemento
// De acordo com a sintaxe atual, o atributo deve estar no final, para que não seja o atributo.
setParsepath (falso, um caminho, falso, mappath);
} outro {
// é o fim
dotindex = OnePath.indexOf (DOT);
if (dotindex> = 0) {
// Isso significa que você deseja obter o valor do atributo, então divida -o de acordo com "".
// O primeiro é o nome do elemento, e o segundo é o nome do atributo
Elename = OnePath.substring (0, DotIndex);
propName = OnePath.substring (DotIndex + 1);
// O elemento em frente à propriedade naturalmente não é o último, nem é a propriedade
setParsepath (falso, elename, falso, mappath);
// Defina atributos. De acordo com a definição de sintaxe atual, o atributo pode ser apenas o último.
setParsePath (true, propname, true, mappath);
} outro {
// As instruções são tomadas como o valor do elemento e o valor do último elemento
setParsePath (true, um caminho, falso, mappath);
}
quebrar;
}
}
retornar mappath;
}
/**
* Defina o nome do elemento a ser analisado de acordo com o local e o nome em decomposição
* @param {boolean} end [é o último]
* @param {string} ele [nome do elemento]
* @param {boolean} PropertyValue [se deve pegar a propriedade]
* @param {objeto} mappath [Defina o nome do elemento que precisa ser analisado, e a tabela do modelo de análise correspondente ao elemento]
*/
função setParsePath (final, ele, PropertyValue, Mappath) {
var pm = novo parsermodel ();
pm.setEnd (end);
// se o símbolo "$" não for um valor
pm.SetsingleValue (! (Ele.indexOF (dólar)> = 0));
pm.setPropertyValue (PropertyValue);
// Remova "$"
ele = ele.replace (dólar, '');
mappath [ele] = pm;
listEle.push (ele);
}
// Comece o segundo passo ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
/**
* Converta o nome do elemento decomposto no objeto de intérprete correspondente de acordo com o modelo analítico correspondente.
* @param {objeto} mappath [o nome do elemento decomposto a ser analisado, e o modelo de análise correspondente ao elemento]
* @return {Array} [converta cada elemento em uma matriz de objetos de intérprete correspondentes]
*/
função mappath2interpreter (mappath) {
var lista = [];
var pm, chave;
var obj = null;
// é necessário convertê -lo em objetos de intérprete na ordem de decomposição
for (var i = 0, len = listEle.length; i <len; i ++) {
key = listEle [i];
pm = mappath [chave];
// não o último
if (! pm.isend ()) {
if (pm.issingleValue ())
// é um valor, conversão
obj = new ElementExpression (chave);
outro
// são vários valores, conversão
obj = new ElementSexpression (chave);
} outro {
// é o último
// é o valor do atributo
if (pm.isPropertyValue ()) {
if (pm.issingleValue ())
obj = novo imobiliário PROPROPEDERMINALEXPRESS (chave);
outro
obj = new PropertysterMinalExpression (chave);
// Tome o valor do elemento
} outro {
if (pm.issingleValue ())
obj = new ElementMerMinalExpression (chave);
outro
obj = new ElementSterMinalExpression (chave);
}
}
list.push (obj);
}
lista de retorno;
}
// Comece o terceiro passo --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
/**
* Construa uma árvore de sintaxe abstrata
* @param {[type]} list [converta cada elemento em uma matriz de objetos de intérprete correspondentes]
* @return {[type]} [Descrição]
*/
Função BuildTree (List) {
// O primeiro objeto, também o objeto devolvido, é a raiz da árvore de sintaxe abstrata
var retornreadxmlexpr = null;
// Defina o objeto anterior
var prereadxmlexpr = null;
var readxml, ele, ele;
for (var i = 0, len = list.length; i <len; i ++) {
readxml = list [i];
// Descrição é o primeiro elemento
if (prereadxmlexpr === null) {
prereadxmlexpr = readxml;
returnReadxmlexpr = readxml;
// Adicione o elemento ao objeto anterior e defina o objeto como Oldre
// como o nó pai do próximo objeto
} outro {
if (prereadxmlexpr instanceof elementExpression) {
ele = prereadxmlexpr;
ele.addele (readxml);
prereadxmlexpr = readxml;
} else if (prereadxmlexpr instanceof elementSexpression) {
ELES = prereadxmlexpr;
eles.addele (readxml);
prereadxmlexpr = readxml;
}
}
}
return returilreadxmlexpr;
}
retornar {
// Método público
Parse: function (expr) {
listEle = [];
var mappath = parsemappath (expr);
var lista = mappath2InterPreter (mappath);
return BuildTree (List);
}
};
} ();
function void () {
// Prepare o contexto
var c = novo contexto ('Interpreter.xml');
// Obtenha a árvore de sintaxe abstrata analisando -a
var readxmlexpr = parser.parse ('root/a/b/d $ .id $');
// Solicite a análise e obtenha o valor de retorno
var ss = readxmlexpr.interpret (c);
console.log ('------------ Parsing --------------');
for (var i = 0, len = ss.length; i <len; i ++) {
console.log ('D Valor da identificação da propriedade é =' + ss [i]);
}
console.log ('--------------- Parsed --------------');
// Se você quiser usar o mesmo contexto e analisar continuamente, precisa reinicializar o 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
------------análise--------------
d的属性id的值是= 1
d的属性id的值是= 2
d的属性id的值是= 3
d的属性id的值是= 4
---------------parsed--------------
------------análise--------------
d的值是= d1
d的值是= d2
d的值是= d3
d的值是= d4
---------------parsed--------------
------------análise--------------
c的name属性值是= 12345
---------------parsed--------------
------------parseing--------------
c的name属性值是= testC
---------------parsed--------------