神馬是“解釋器模式”?
先翻開《GOF》看看Definition:
給定一個語言,定義它的文法的一種表示,并定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子。
在開篇之前還是要科普幾個概念:
抽象語法樹:
解釋器模式并未解釋如何創建一個抽象語法樹。它不涉及語法分析。抽象語法樹可用一個表驅動的語法分析程序來完成,也可用手寫的(通常為遞歸下降法)語法分析程序創建,或直接client提供。
解析器:
指的是把描述客戶端調用要求的表達式,經過解析,形成一個抽象語法樹的程序。
解釋器:
指的是解釋抽象語法樹,并執行每個節點對應的功能的程序。
要使用解釋器模式,一個重要的前提就是要定義一套語法規則,也稱為文法。不管這套文法的規則是簡單還是復雜,必須要有這些規則,因為解釋器模式就是按照這些規則來進行解析并執行相應的功能的。
先來看看解釋器模式的結構圖和說明:
AbstractExpression:定義解釋器的接口,約定解釋器的解釋操作。
TerminalExpression:終結符解釋器,用來實現語法規則中和終結符相關的操作,不再包含其他的解釋器,如果用組合模式來構建抽象語法樹的話,就相當于組合模式中的葉子對象,可以有多種終結符解釋器。
NonterminalExpression:非終結符解釋器,用來實現語法規則中非終結符相關的操作,通常一個解釋器對應一個語法規則,可以包含其他的解釋器,如果用組合模式來構建抽象語法樹的話,就相當于組合模式中的組合對象??梢杂卸喾N非終結符解釋器。
Context:上下文,通常包含各個解釋器需要的數據或是公共的功能。
Client:客戶端,指的是使用解釋器的客戶端,通常在這里將按照語言的語法做的表達式轉換成為使用解釋器對象描述的抽象語法樹,然后調用解釋操作。
下面我們通過一個xml示例來理解解釋器模式:
首先要為表達式設計簡單的文法,為了通用,用root表示根元素,abc等來代表元素,一個簡單的xml如下:
約定表達式的文法如下:
1.獲取單個元素的值:從根元素開始,一直到想要獲取取值的元素,元素中間用“/”分隔,根元素前不加“/”。比如,表達式“root/a/b/c”就表示獲取根元素下,a元素下,b元素下,c元素的值。
2.獲取單個元素的屬性的值:當然是多個,要獲取值的屬性一定是表達式的最后一個元素的屬性,在最后一個元素后面添加“.”然后再加上屬性的名稱。比如,表達式“root/a/b/c.name”就表示獲取根元素下,a元素下,b元素下,c元素的name屬性的值。
3.獲取相同元素名稱的值,當然是多個,要獲取值的元素一定是表達式的最后一個元素,在最后一個元素后面添加“$”。比如,表達式“root/a/b/d$”就表示獲取根元素下,a元素下,b元素下的多個d元素的值的集合。
4.獲取相同元素名稱的屬性的值,當然也是多個:要獲取屬性值的元素一定是表達式的最后一個元素,在最后一個元素后面添加"$"。比如,表達式“root/a/b/d$.id$”就表示獲取根元素下,a元素下,b元素下的多個d元素的id屬性的值的集合。
上面的xml,對應的抽象語法樹,可能的結構如圖:
下面我們來看看具體的代碼:
1.定義上下文:
Context.prototype = {
// 重新初始化上下文
reInit: function () {
this.preEle = null;
},
/**
* 各個Expression公共使用的方法
* 根據父元素和當前元素的名稱來獲取當前元素
* @param {Element} pEle [父元素]
* @param {String} eleName [當前元素名稱]
* @return {Element|null} [找到的當前元素]
*/
getNowEle: function (pEle, eleName) {
var tempNodeList = pEle.childNodes;
var nowEle;
for (var i = 0, len = tempNodeList.length; i < len; i++) {
if ((nowEle = tempNodeList[i]).nodeType === 1)
if (nowEle.nodeName === eleName)
return nowEle;
}
return null;
},
getPreEle: function () {
return this.preEle;
},
setPreEle: function (preEle) {
this.preEle = preEle;
},
getDocument: function () {
return this.document;
}
};
在上下文中使用了一個工具對象XmlUtil來獲取xmlDom,下面我使用的是DOM3的DOMPaser,某些瀏覽器可能不支持,請使用搞基瀏覽器:
return xmldom;
}
};
下面就是解釋器的代碼:
ElementExpression.prototype = {
addEle: function (eleName) {
this.eles.push(eleName);
return true;
},
removeEle: function (ele) {
for (var i = 0, len = this.eles.length; i < len; i++) {
if (ele === this.eles[i])
this.eles.splice(i--, 1);
}
return true;
},
interpret: function (context) {
// 先取出上下文中的當前元素作為父級元素
// 查找到當前元素名稱所對應的xml元素,并設置回到上下文中
var pEle = context.getPreEle();
if (!pEle) {
// 說明現在獲取的是根元素
context.setPreEle(context.getDocument().documentElement);
} else {
// 根據父級元素和要查找的元素的名稱來獲取當前的元素
var nowEle = context.getNowEle(pEle, this.eleName);
// 把當前獲取的元素放到上下文中
context.setPreEle(nowEle);
}
var ss;
// 循環調用子元素的interpret方法
for (var i = 0, len = this.eles.length; i < len; i++) {
ss = this.eles[i].interpret(context);
}
// 返回最后一個解釋器的解釋結果,一般最后一個解釋器就是終結符解釋器了
return ss;
}
};
/**
* 元素作為終結符對應的解釋器
* @param {String} name [元素的名稱]
*/
function ElementTerminalExpression(name) {
this.eleName = name;
}
ElementTerminalExpression.prototype = {
interpret: function (context) {
var pEle = context.getPreEle();
var ele = null;
if (!pEle) {
ele = context.getDocument().documentElement;
} else {
ele = context.getNowEle(pEle, this.eleName);
context.setPreEle(ele);
}
// 獲取元素的值
return ele.firstChild.nodeValue;
}
};
/**
* 屬性作為終結符對應的解釋器
* @param {String} propName [屬性的名稱]
*/
function PropertyTerminalExpression(propName) {
this.propName = propName;
}
PropertyTerminalExpression.prototype = {
interpret: function (context) {
// 直接獲取最后的元素屬性的值
return context.getPreEle().getAttribute(this.propName);
}
};
先來看看如何使用解釋器獲取單個元素的值:
// 組合
root.addEle(aEle);
aEle.addEle(bEle);
bEle.addEle(cEle);
console.log('c的值是 = ' + root.interpret(c));
}();
輸出: c的值是 = 12345
然后我們再用上面代碼獲取單個元素的屬性的值:
// 組合
root.addEle(aEle);
aEle.addEle(bEle);
bEle.addEle(cEle);
cEle.addEle(prop);
console.log('c的屬性name值是 = ' + root.interpret(c));
// 如果要使用同一個上下文,連續進行解析,需要重新初始化上下文對象
// 比如,要連續的重新再獲取一次屬性name的值,當然你可以重新組合元素
// 重新解析,只要是在使用同一個上下文,就需要重新初始化上下文對象
c.reInit();
console.log('重新獲取c的屬性name值是 = ' + root.interpret(c));
}();
輸出: c的屬性name值是 = testC 重新獲取c的屬性name值是 = testC
講解:
1.解釋器模式功能:
解釋器模式使用解釋器對象來表示和處理相應的語法規則,一般一個解釋器處理一條語法規則。理論上來說,只要能用解釋器對象把符合語法的表達式表示出來,而且能夠構成抽象的語法樹,就可以使用解釋器模式來處理。
2.語法規則和解釋器
語法規則和解釋器之間是有對應關系的,一般一個解釋器處理一條語法規則,但是反過來并不成立,一條語法規則是可以有多種解釋和處理的,也就是一條語法規則可以對應多個解釋器。
3.上下文的公用性
上下文在解釋器模式中起著非常重要的作用。由于上下文會被傳遞到所有的解釋器中。因此可以在上下文中存儲和訪問解釋器的狀態,比如,前面的解釋器可以存儲一些數據在上下文中,后面的解釋器就可以獲取這些值。
另外還可以通過上下文傳遞一些在解釋器外部,但是解釋器需要的數據,也可以是一些全局的,公共的數據。
上下文還有一個功能,就是可以提供所有解釋器對象的公共功能,類似于對象組合,而不是使用繼承來獲取公共功能,在每個解釋器對象中都可以調用
4.誰來構建抽象語法樹
在前面的示例中,是自己在客戶端手工構建抽象語法樹,是很麻煩的,但是在解釋器模式中,并沒有涉及這部分功能,只是負責對構建好的抽象語法樹進行解釋處理。后面會介紹可以提供解析器來實現把表達式轉換成為抽象語法樹。
還有一個問題,就是一條語法規則是可以對應多個解釋器對象的,也就是說同一個元素,是可以轉換成多個解釋器對象的,這也就意味著同樣一個表達式,是可以構成不用的抽象語法樹的,這也造成構建抽象語法樹變得很困難,而且工作量非常大。
5.誰負責解釋操作
只要定義好了抽象語法樹,肯定是解釋器來負責解釋執行。雖然有不同的語法規則,但是解釋器不負責選擇究竟用哪個解釋器對象來解釋執行語法規則,選擇解釋器的功能在構建抽象語法樹的時候就完成了。
6.解釋器模式的調用順序
1)創建上下文對象
2)創建多個解釋器對象,組合抽象語法樹
3)調用解釋器對象的解釋操作
3.1)通過上下文來存儲和訪問解釋器的狀態。
對于非終結符解釋器對象,遞歸調用它所包含的子解釋器對象。
解釋器模式的本質:*分離實現,解釋執行*
解釋器模使用一個解釋器對象處理一個語法規則的方式,把復雜的功能分離開;然后選擇需要被執行的功能,并把這些功能組合成為需要被解釋執行的抽象語法樹;再按照抽象語法樹來解釋執行,實現相應的功能。
從表面上看,解釋器模式關注的是我們平時不太用到的自定義語法的處理;但從實質上看,解釋器模式的思想然后是分離,封裝,簡化,和很多模式是一樣的。
比如,可以使用解釋器模式模擬狀態模式的功能。如果把解釋器模式要處理的語法簡化到只有一個狀態標記,把解釋器看成是對狀態的處理對象,對同一個表示狀態的語法,可以有很多不用的解釋器,也就是有很多不同的處理狀態的對象,然后再創建抽象語法樹的時候,簡化成根據狀態的標記來創建相應的解釋器,不用再構建樹了。
同理,解釋器模式可以模擬實現策略模式的功能,裝飾器模式的功能等,尤其是模擬裝飾器模式的功能,構建抽象語法樹的過程,自然就對應成為組合裝飾器的過程。
解釋器模式執行速度通常不快(大多數時候非常慢),而且錯誤調試比較困難(附注:雖然調試比較困難,但事實上它降低了錯誤的發生可能性),但它的優勢是顯而易見的,它能有效控制模塊之間接口的復雜性,對于那種執行頻率不高但代碼頻率足夠高,且多樣性很強的功能,解釋器是非常適合的模式。此外解釋器還有一個不太為人所注意的優勢,就是它可以方便地跨語言和跨平臺。
解釋器模式的優缺點:
優點:
1.易于實現語法
在解釋器模式中,一條語法規則用一個解釋器對象來解釋執行。對于解釋器的實現來講,功能就變得比較簡單,只需要考慮這一條語法規則的實現就可以了,其他的都不用管。 2.易于擴展新的語法
正是由于采用一個解釋器對象負責一條語法規則的方式,使得擴展新的語法非常容易。擴展了新的語法,只需要創建相應的解釋器對象,在創建抽象語法樹的時候使用這個新的解釋器對象就可以了。
缺點:
不適合復雜的語法
如果語法特別復雜,構建解釋器模式需要的抽象語法樹的工作是非常艱巨的,再加上有可能會需要構建多個抽象語法樹。所以解釋器模式不太適合復雜的語法。使用語法分析程序或編譯器生成器可能會更好一些。
何時使用?
當有一個語言需要解釋執行,并且可以將該語言中的句子表示為一個抽象語法樹的時候,可以考慮使用解釋器模式。
在使用解釋器模式的時候,還有兩個特點需要考慮,一個是語法相對應該比較簡單,太負責的語法不適合使用解釋器模式玲玲一個是效率要求不是很高,對效率要求很高的,不適合使用。
前面介紹了如何獲取單個元素的值和單個元素屬性的值,下面來看看如何獲取多個元素的值,還有多個元素中相擁名稱的值,還有前面的測試都是人工組合好的抽象語法樹,我們也順便實現以下簡單的解析器,把符合前面定義的語法的表達式,轉換成為前面實現的解釋器的抽象語法樹: 我就直接把代碼貼出來了:
Context.prototype = {
// 重新初始化上下文
reInit: function () {
this.preEles = [];
},
/**
* 各個Expression公共使用的方法
* 根據父元素和當前元素的名稱來獲取當前元素
* @param {Element} pEle [父元素]
* @param {String} eleName [當前元素名稱]
* @return {Element|null} [找到的當前元素]
*/
getNowEles: function (pEle, eleName) {
var elements = [];
var tempNodeList = pEle.childNodes;
var nowEle;
for (var i = 0, len = tempNodeList.length; i < len; i++) {
if ((nowEle = tempNodeList[i]).nodeType === 1) {
if (nowEle.nodeName === eleName) {
elements.push(nowEle);
}
}
}
return elements;
},
getPreEles: function () {
return this.preEles;
},
setPreEles: function (nowEles) {
this.preEles = nowEles;
},
getDocument: function () {
return this.document;
}
};
// 工具對象
// 解析xml,獲取相應的Document對象
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;
}
};
/**
* 元素作為非終結符對應的解釋器,解釋并執行中間元素
* @param {String} eleName [元素的名稱]
*/
function ElementExpression(eleName) {
this.eles = [];
this.eleName = eleName;
}
ElementExpression.prototype = {
addEle: function (eleName) {
this.eles.push(eleName);
return true;
},
removeEle: function (ele) {
for (var i = 0, len = this.eles.length; i < len; i++) {
if (ele === this.eles[i]) {
this.eles.splice(i--, 1);
}
}
return true;
},
interpret: function (context) {
// 先取出上下文中的當前元素作為父級元素
// 查找到當前元素名稱所對應的xml元素,并設置回到上下文中
var pEles = context.getPreEles();
var ele = null;
var nowEles = [];
if (!pEles.length) {
// 說明現在獲取的是根元素
ele = context.getDocument().documentElement;
pEles.push(ele);
context.setPreEles(pEles);
} else {
var tempEle;
for (var i = 0, len = pEles.length; i < len; i++) {
tempEle = pEles[i];
nowEles = nowEles.concat(context.getNowEles(tempEle, this.eleName));
// 找到一個就停止
if (nowEles.length) break;
}
context.setPreEles([nowEles[0]]);
}
var ss;
// 循環調用子元素的interpret方法
for (var i = 0, len = this.eles.length; i < len; i++) {
ss = this.eles[i].interpret(context);
}
return ss;
}
};
/**
* 元素作為終結符對應的解釋器
* @param {String} name [元素的名稱]
*/
function ElementTerminalExpression(name) {
this.eleName = name;
}
ElementTerminalExpression.prototype = {
interpret: function (context) {
var pEles = context.getPreEles();
var ele = null;
if (!pEles.length) {
ele = context.getDocument().documentElement;
} else {
ele = context.getNowEles(pEles[0], this.eleName)[0];
}
// 獲取元素的值
return ele.firstChild.nodeValue;
}
};
/**
* 屬性作為終結符對應的解釋器
* @param {String} propName [屬性的名稱]
*/
function PropertyTerminalExpression(propName) {
this.propName = propName;
}
PropertyTerminalExpression.prototype = {
interpret: function (context) {
// 直接獲取最后的元素屬性的值
return context.getPreEles()[0].getAttribute(this.propName);
}
};
/**
* 多個屬性作為終結符對應的解釋器
* @param {String} propName [屬性的名稱]
*/
function PropertysTerminalExpression(propName) {
this.propName = propName;
}
PropertysTerminalExpression.prototype = {
interpret: function (context) {
var eles = context.getPreEles();
var ss = [];
for (var i = 0, len = eles.length; i < len; i++) {
ss.push(eles[i].getAttribute(this.propName));
}
return ss;
}
};
/**
* 以多個元素作為終結符的解釋處理對象
* @param {[type]} name [description]
*/
function ElementsTerminalExpression(name) {
this.eleName = name;
}
ElementsTerminalExpression.prototype = {
interpret: function (context) {
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(nowEles[i].firstChild.nodeValue);
}
return ss;
}
};
/**
* 多個元素作為非終結符的解釋處理對象
*/
function ElementsExpression(name) {
this.eleName = name;
this.eles = [];
}
ElementsExpression.prototype = {
interpret: function (context) {
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(context);
}
return ss;
},
addEle: function (ele) {
this.eles.push(ele);
return true;
},
removeEle: function (ele) {
for (var i = 0, len = this.eles.length; i < len; i++) {
if (ele === this.eles[i]) {
this.eles.splice(i--, 1);
}
}
return true;
}
};
void function () {
// "root/a/b/d$"
var c = new Context('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);
for (var i = 0, len = ss.length; i < len; i++) {
console.log('d的值是 = ' + ss[i]);
}
}();
void function () {
// a/b/d$.id$
var c = new Context('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 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的屬性id的值是 = ' + ss[i]);
}
}();
// 解析器
/**
* 解析器的實現思路
* 1.把客戶端傳遞來的表達式進行分解,分解成為一個一個的元素,并用一個對應的解析模型來封裝這個元素的一些信息。
* 2.根據每個元素的信息,轉化成相對應的解析器對象。
* 3.按照先后順序,把這些解析器對象組合起來,就得到抽象語法樹了。
*
* 為什么不把1和2合并,直接分解出一個元素就轉換成相應的解析器對象?
* 1.功能分離,不要讓一個方法的功能過于復雜。
* 2.為了今后的修改和擴展,現在語法簡單,所以轉換成解析器對象需要考慮的東西少,直接轉換也不難,但要是語法復雜了,直接轉換就很雜亂了。
*/
/**
* 用來封裝每一個解析出來的元素對應的屬性
*/
function ParserModel() {
// 是否單個值
this.singleValue;
// 是否屬性,不是屬性就是元素
this.propertyValue;
// 是否終結符
this.end;
}
ParserModel.prototype = {
isEnd: function () {
return this.end;
},
setEnd: function (end) {
this.end = end;
},
isSingleValue: function () {
return this.singleValue;
},
setSingleValue: function (oneValue) {
this.singleValue = oneValue;
},
isPropertyValue: function () {
return this.propertyValue;
},
setPropertyValue: function (propertyValue) {
this.propertyValue = propertyValue;
}
};
var Parser = function () {
var BACKLASH = '/';
var DOT = '.';
var DOLLAR = '$';
// 按照分解的先后記錄需要解析的元素的名稱
var listEle = null;
// 開始實現第一步-------------------------------------
/**
* 傳入一個字符串表達式,通過解析,組合成為一個抽象語法樹
* @param {String} expr [描述要取值的字符串表達式]
* @return {Object} [對應的抽象語法樹]
*/
function parseMapPath(expr) {
// 先按照“/”分割字符串
var tokenizer = expr.split(BACKLASH);
// 用來存放分解出來的值的表
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]) {
// 還有下一個值,說明這不是最后一個元素
// 按照現在的語法,屬性必然在最后,因此也不是屬性
setParsePath(false, onePath, false, mapPath);
} else {
// 說明到最后了
dotIndex = onePath.indexOf(DOT);
if (dotIndex >= 0) {
// 說明是要獲取屬性的值,那就按照“.”來分割
// 前面的就是元素名稱,后面的是屬性的名字
eleName = onePath.substring(0, dotIndex);
propName = onePath.substring(dotIndex + 1);
// 設置屬性前面的那個元素,自然不是最后一個,也不是屬性
setParsePath(false, eleName, false, mapPath);
// 設置屬性,按照現在的語法定義,屬性只能是最后一個
setParsePath(true, propName, true, mapPath);
} else {
// 說明是取元素的值,而且是最后一個元素的值
setParsePath(true, onePath, false, mapPath);
}
break;
}
}
return mapPath;
}
/**
* 按照分解出來的位置和名稱來設置需要解析的元素名稱
* @param {Boolean} end [是否最后一個]
* @param {String} ele [元素名稱]
* @param {Boolean} propertyValue [是否取屬性]
* @param {Object} mapPath [設置需要解析的元素名稱,還有該元素對應的解析模型的表]
*/
function setParsePath(end, ele, propertyValue, mapPath) {
var pm = new ParserModel();
pm.setEnd(end);
// 如果帶有“$”符號就說明不是一個值
pm.setSingleValue(!(ele.indexOf(DOLLAR) >= 0));
pm.setPropertyValue(propertyValue);
// 去掉"$"
ele = ele.replace(DOLLAR, '');
mapPath[ele] = pm;
listEle.push(ele);
}
// 開始實現第二步-------------------------------------
/**
* 把分解出來的元素名稱根據對應的解析模型轉換成為相應的解釋器對象
* @param {Object} mapPath [分解出來的需解析的元素名稱,還有該元素對應的解析模型]
* @return {Array} [把每個元素轉換成為相應的解釋器對象后的數組]
*/
function mapPath2Interpreter(mapPath) {
var list = [];
var pm, key;
var obj = null;
// 一定要按照分解的先后順序來轉換成解釋器對象
for (var i = 0, len = listEle.length; i < len; i++) {
key = listEle[i];
pm = mapPath[key];
// 不是最后一個
if (!pm.isEnd()) {
if (pm.isSingleValue())
// 是一個值,轉化
obj = new ElementExpression(key);
else
// 是多個值,轉化
obj = new ElementsExpression(key);
} else {
// 是最后一個
// 是屬性值
if (pm.isPropertyValue()) {
if (pm.isSingleValue())
obj = new PropertyTerminalExpression(key);
else
obj = new PropertysTerminalExpression(key);
// 取元素的值
} else {
if (pm.isSingleValue())
obj = new ElementTerminalExpression(key);
else
obj = new ElementsTerminalExpression(key);
}
}
list.push(obj);
}
return list;
}
// 開始實現第三步-------------------------------------
/**
* 構建抽象語法樹
* @param {[type]} list [把每個元素轉換成為相應的解釋器對象后的數組]
* @return {[type]} [description]
*/
function buildTree(list) {
// 第一個對象,也是返回去的對象,就是抽象語法樹的根
var returnReadXMLExpr = null;
// 定義上一個對象
var preReadXmlExpr = null;
var readXml, ele, eles;
for (var i = 0, len = list.length; i < len; i++) {
readXml = list[i];
// 說明是第一個元素
if (preReadXmlExpr === null) {
preReadXmlExpr = readXml;
returnReadXMLExpr = readXml;
// 把元素添加到上一個對象下面,同時把本對象設置成為oldRe
// 作為下一個對象的父節點
} else {
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;
}
return {
// 公共方法
parse: function (expr) {
listEle = [];
var mapPath = parseMapPath(expr);
var list = mapPath2Interpreter(mapPath);
return buildTree(list);
}
};
}();
void function () {
// 準備上下文
var c = new Context('Interpreter.xml');
// 通過解析其獲取抽象語法樹
var readXmlExpr = Parser.parse('root/a/b/d$.id$');
// 請求解析,獲取返回值
var ss = readXmlExpr.interpret(c);
console.log('------------parsing--------------');
for (var i = 0, len = ss.length; i < len; i++) {
console.log('d的屬性id的值是 = ' + ss[i]);
}
console.log('---------------parsed--------------');
// 如果要使用同一個上下文,連續進行解析,需要重新初始化上下文對象
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--------------
新聞熱點
疑難解答