首页 > Web开发 > 详细

javascript设计模式=解释其模式(interpreter)

时间:2014-01-25 17:55:27      阅读:501      评论:0      收藏:0      [点我收藏+]
bubuko.com,布布扣
   1 <!doctype html>
   2 <html lang="en">
   3 <head>
   4     <meta charset="UTF-8">
   5     <title>解释器模式</title>
   6 </head>
   7 <body>
   8 
   9 &lt;root id="rootId"><br>
  10     &lt;a> <br>
  11         &lt;b> <br>
  12             &lt;c name="testC">12345</c> <br>
  13             &lt;d id="1">d1</d> <br>
  14             &lt;d id="2">d2</d> <br>
  15             &lt;d id="3">d3</d> <br>
  16             &lt;d id="4">d4</d> <br>
  17         &lt;/b> <br>
  18     &lt;/a> <br>
  19 &lt;/root> <br>
  20 
  21 <script>
  22 /**
  23  * 解释器模式
  24  *
  25  * 定义:
  26  * 给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
  27  *
  28  * 本质: 分离实现,解释执行
  29  *
  30  * 动机
  31  * 如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。
  32  *
  33  * 抽象语法树
  34  * 解释器模式并未解释如何创建一个抽象语法树。它不涉及语法分析。抽象语法树可用一个表驱动的语法分析程序来完成,也可用手写的(通常为递归下降法)语法分析程序创建,或直接client提供。
  35  *
  36  * 解析器
  37  * 指的是把描述客户端调用要求的表达式,经过解析,形成一个抽象语法树的程序。
  38  *
  39  * 解释器
  40  * 指的是解释抽象语法树,并执行每个节点对应的功能的程序。
  41  *
  42  * 要使用解释器模式,一个重要的前提就是要定义一套语法规则,也成为文法。不管这套文法的规则是简单还是复杂,必须要有这些规则,因为解释器模式就是按照这些规则来进行解析并执行相应的功能的。
  43  *
  44  * 解释器模式用过一个解释器对象处理一个语法规则的方式,把复杂的功能分离开;然后选择需要被执行的功能,并把这些功能组合成为需要被解释执行的抽象语法树;再按照抽象语法树来解释执行,实现相应的功能。
  45  * 从表面上看,解释器模式关注的是我们平时不太用到的自定义语法的处理;但从实质上看,解释器模式的思想然后是分离,封装,简化,和很多模式是一样的。
  46  * 比如,可以使用解释器模式模拟状态模式的功能。如果把解释器模式要处理的语法简化到只有一个状态标记,把解释器看成是对状态的处理对象,对同一个表示状态的语法,可以有很多不用的解释器,也就是有很多不同的处理状态的对象,然后再创建抽象语法树的时候,简化成根据状态的标记来创建相应的解释器,不用再构建树了。
  47  * 同理,解释器模式可以模拟实现策略模式的功能,装饰器模式的功能等,尤其是模拟装饰器模式的功能,构建抽象语法树的过程,自然就对应成为组合装饰器的过程。
  48  *
  49  * 解释器模式执行速度通常不快(大多数时候非常慢),而且错误调试比较困难(附注:虽然调试比较困难,但事实上它降低了错误的发生可能性),但它的优势是显而易见的,它能有效控制模块之间接口的复杂性,对于那种执行频率不高但代码频率足够高,且多样性很强的功能,解释器是非常适合的模式。此外解释器还有一个不太为人所注意的优势,就是它可以方便地跨语言和跨平台。
  50  *
  51  * 优点:
  52  * 1.易于实现语法
  53  * 在解释器模式中,一条语法规则用一个解释器对象来解释执行。对于解释器的实现来讲,功能就变得比较简单,只需要考虑这一条语法规则的实现就可以了,其他的都不用管。
  54  * 2.易于扩展新的语法
  55  * 正是由于采用一个解释器对象负责一条语法规则的方式,使得扩展新的语法非常容易。扩展了新的语法,只需要创建相应的解释器对象,在创建抽象语法树的时候使用这个新的解释器对象就可以了。
  56  *
  57  * 缺点:
  58  * 不适合复杂的语法
  59  * 如果语法特别复杂,构建解释器模式需要的抽象语法树的工作是非常艰巨的,再加上有可能会需要构建多个抽象语法树。所以解释器模式不太适合复杂的语法。使用语法分析程序或编译器生成器可能会更好一些。
  60  *
  61  * 何时使用
  62  * 当有一个语言需要解释执行,并且可以将该语言中的句子表示为一个抽象语法树的时候,可以考虑使用解释器模式。
  63  * 在使用解释器模式的时候,还有两个特点需要考虑,一个是语法相对应该比较简单,太负责的语法不适合使用解释器模式玲玲一个是效率要求不是很高,对效率要求很高的,不适合使用。
  64  *
  65  *
  66  */
  67 
  68 (function(){
  69     // 示例代码
  70 
  71     // 终结符表达式
  72     var TerminalExpression = function(){};
  73     TerminalExpression.prototype = {
  74         /**
  75          * 解释的操作
  76          * @param  {[type]} context [上下文]
  77          */
  78         interpret: function(context){
  79             // 实现与语法规则中的终结符相关联的解释操作
  80         }
  81     };
  82 
  83     // 非终结符表达式
  84     var NonterminalExpression = function(){};
  85     NonterminalExpression.prototype = {
  86         interpret: function(context){
  87             // 实现与语法规则中的非终结符相关联的解释操作
  88         }
  89     };
  90 
  91     // 上下文,包含解释器之外的一些全局信息
  92     var Context = function(){};
  93 
  94     // 使用解释器的客户
  95     // 主要按照语法规则对特定句子构建抽象语法树
  96     // 然后调用解释操作
  97 }());
  98 
  99 (function(){
 100     /**
 101      * 1.为表达式设计简单的文法
 102      *
 103      * 为了通用,用root表示根元素,abc等来代表元素,一个简单的xml如下:
 104      * <?xml version="1.0" encoding="UTF-8">
 105      * <root id="rootId">
 106      *     <a>
 107      *         <b>
 108      *             <c name="testC">12345</c>
 109      *             <d id="1">d1</d>
 110      *             <d id="2">d2</d>
 111      *             <d id="3">d3</d>
 112      *             <d id="4">d4</d>
 113      *         </b>
 114      *     </a>
 115      * </root>
 116      *
 117      * 约定表达式的文法如下:
 118      * 1.获取单个元素的值:从根元素开始,一直到想要获取取值的元素,元素中间用“/”分隔,根元素前不加“/”。比如,表达式“root/a/b/c”就表示获取根元素下,a元素下,b元素下,c元素的值。
 119      * 2.获取单个元素的属性的值:当然是多个,要获取值的属性一定是表达式的最后一个元素的属性,在最后一个元素后面添加“.”然后再加上属性的名称。比如,表达式“root/a/b/c.name”就表示获取根元素下,a元素下,b元素下,c元素的name属性的值。
 120      * 3.获取相同元素名称的值,当然是多个,要获取值的元素一定是表达式的最后一个元素,在最后一个元素后面添加“$”。比如,表达式“root/a/b/d$”就表示获取根元素下,a元素下,b元素下的多个d元素的值的集合。
 121      * 4.获取相同元素名称的属性的值,当然也是多个:要获取属性值的元素一定是表达式的最后一个元素,在最后一个元素后面添加"$"。比如,表达式“root/a/b/d$.id$”就表示获取根元素下,a元素下,b元素下的多个d元素的id属性的值的集合。
 122      */
 123 
 124     /**
 125      * 上下文,用来包含解释器需要的一些全局信息
 126      * @param {String} filePathName [需要读取的xml的路径和名字]
 127      */
 128     function Context(filePathName){
 129         // 上一个被处理元素
 130         this.preEle = null;
 131         // xml的Document对象
 132         this.document = XmlUtil.getRoot(filePathName);
 133     }
 134     Context.prototype = {
 135         // 重新初始化上下文
 136         reInit: function(){
 137             this.preEle = null;
 138         },
 139         /**
 140          *  各个Expression公共使用的方法
 141          * 根据父元素和当前元素的名称来获取当前元素
 142          * @param  {Element} pEle    [父元素]
 143          * @param  {String} eleName [当前元素名称]
 144          * @return {Element|null}         [找到的当前元素]
 145          */
 146         getNowEle: function(pEle, eleName){
 147             var tempNodeList = pEle.childNodes;
 148             var nowEle;
 149 
 150             for(var i = 0, len = tempNodeList.length; i < len; i++) {
 151                 if((nowEle = tempNodeList[i]).nodeType === 1)
 152                     if(nowEle.nodeName === eleName)
 153                         return nowEle;
 154             }
 155 
 156             return null;
 157         },
 158         getPreEle: function(){
 159             return this.preEle;
 160         },
 161         setPreEle: function(preEle){
 162             this.preEle = preEle;
 163         },
 164         getDocument: function(){
 165             return this.document;
 166         }
 167     };
 168 
 169     // 工具对象
 170     // 解析xml,获取相应的Document对象
 171     var XmlUtil = {
 172         getRoot: function(filePathName){
 173             var parser = new DOMParser();
 174             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‘);
 175 
 176             return xmldom;
 177         }
 178     };
 179 
 180     /**
 181      * 元素作为非终结符对应的解释器,解释并执行中间元素
 182      * @param {String} eleName [元素的名称]
 183      */
 184     function ElementExpression(eleName){
 185         this.eles = [];
 186         this.eleName = eleName;
 187     }
 188     ElementExpression.prototype = {
 189         addEle: function(eleName){
 190             this.eles.push(eleName);
 191             return true;
 192         },
 193         removeEle: function(ele){
 194             for(var i = 0, len = this.eles.length; i < len; i++) {
 195                 if(ele === this.eles[i])
 196                     this.eles.splice(i--, 1);
 197             }
 198             return true;
 199         },
 200         interpret: function(context){
 201             // 先取出上下文中的当前元素作为父级元素
 202             // 查找到当前元素名称所对应的xml元素,并设置回到上下文中
 203             var pEle = context.getPreEle();
 204 
 205             if(!pEle) {
 206                 // 说明现在获取的是根元素
 207                 context.setPreEle(context.getDocument().documentElement);
 208             } else {
 209                 // 根据父级元素和要查找的元素的名称来获取当前的元素
 210                 var nowEle = context.getNowEle(pEle, this.eleName);
 211                 // 把当前获取的元素放到上下文中
 212                 context.setPreEle(nowEle);
 213             }
 214 
 215             var ss;
 216             // 循环调用子元素的interpret方法
 217             for(var i = 0, len = this.eles.length; i < len; i++) {
 218                 ss = this.eles[i].interpret(context);
 219             }
 220 
 221             // 返回最后一个解释器的解释结果,一般最后一个解释器就是终结符解释器了
 222             return ss;
 223         }
 224     };
 225 
 226     /**
 227      * 元素作为终结符对应的解释器
 228      * @param {String} name [元素的名称]
 229      */
 230     function ElementTerminalExpression(name){
 231         this.eleName = name;
 232     }
 233     ElementTerminalExpression.prototype = {
 234         interpret: function(context){
 235             var pEle = context.getPreEle();
 236             var ele = null;
 237             if(!pEle) {
 238                 ele = context.getDocument().documentElement;
 239             } else {
 240                 ele = context.getNowEle(pEle, this.eleName);
 241                 context.setPreEle(ele);
 242             }
 243 
 244             // 获取元素的值
 245             return ele.firstChild.nodeValue;
 246         }
 247     };
 248 
 249     /**
 250      * 属性作为终结符对应的解释器
 251      * @param {String} propName [属性的名称]
 252      */
 253     function PropertyTerminalExpression(propName){
 254         this.propName = propName;
 255     }
 256     PropertyTerminalExpression.prototype = {
 257         interpret: function(context){
 258             // 直接获取最后的元素属性的值
 259             return context.getPreEle().getAttribute(this.propName);
 260         }
 261     };
 262 
 263     new function(){
 264         var c = new Context(‘InterpreterTest.xml‘);
 265         // 想要获取多个d元素的值,也就是如下表达式的值:“root/a/b/c”
 266         // 首先要构建解释器的抽象语法树
 267         var root = new ElementExpression(‘root‘);
 268         var aEle = new ElementExpression(‘a‘);
 269         var bEle = new ElementExpression(‘b‘);
 270         var cEle = new ElementTerminalExpression(‘c‘);
 271 
 272         // 组合
 273         root.addEle(aEle);
 274         aEle.addEle(bEle);
 275         bEle.addEle(cEle);
 276 
 277         console.log(‘c的值是 = ‘ + root.interpret(c));
 278 
 279     }();
 280 
 281     new function(){
 282         var c = new Context(‘InterpreterTest.xml‘);
 283         // 想要获取d元素的id属性,也就是如下表达式的值:“a/b/c.name”
 284         // 这个时候c不是终结了,需要把c修改成ElementExpression
 285         var root = new ElementExpression(‘root‘);
 286         var aEle = new ElementExpression(‘a‘);
 287         var bEle = new ElementExpression(‘b‘);
 288         var cEle = new ElementExpression(‘c‘);
 289         var prop = new PropertyTerminalExpression(‘name‘);
 290 
 291         // 组合
 292         root.addEle(aEle);
 293         aEle.addEle(bEle);
 294         bEle.addEle(cEle);
 295         cEle.addEle(prop);
 296 
 297         console.log(‘c的属性name值是 = ‘ + root.interpret(c));
 298 
 299         // 如果要使用同一个上下文,连续进行解析,需要重新初始化上下文对象
 300         // 比如,要连续的重新再获取一次属性name的值,当然你可以重新组合元素
 301         // 重新解析,只要是在使用同一个上下文,就需要重新初始化上下文对象
 302         c.reInit();
 303         console.log(‘重新获取c的属性name值是 = ‘ + root.interpret(c));
 304     }();
 305 
 306 
 307     // 读取多个元素或属性的值
 308     (function(){
 309         /**
 310          * 上下文,用来包含解释器需要的一些全局信息
 311          * @param {String} filePathName [需要读取的xml的路径和名字]
 312          */
 313         function Context(filePathName){
 314             // 上一个被处理的多个元素
 315             this.preEles = [];
 316             // xml的Document对象
 317             this.document = XmlUtil.getRoot(filePathName);
 318         }
 319         Context.prototype = {
 320             // 重新初始化上下文
 321             reInit: function(){
 322                 this.preEles = [];
 323             },
 324             /**
 325              *  各个Expression公共使用的方法
 326              * 根据父元素和当前元素的名称来获取当前元素
 327              * @param  {Element} pEle    [父元素]
 328              * @param  {String} eleName [当前元素名称]
 329              * @return {Element|null}         [找到的当前元素]
 330              */
 331             getNowEles: function(pEle, eleName){
 332                 var elements = [];
 333                 var tempNodeList = pEle.childNodes;
 334                 var nowEle;
 335 
 336                 for(var i = 0, len = tempNodeList.length; i < len; i++) {
 337                     if((nowEle = tempNodeList[i]).nodeType === 1) {
 338                         if(nowEle.nodeName === eleName) {
 339                             elements.push(nowEle);
 340                         }
 341                     }
 342                 }
 343 
 344                 return elements;
 345             },
 346             getPreEles: function(){
 347                 return this.preEles;
 348             },
 349             setPreEles: function(nowEles){
 350                 this.preEles = nowEles;
 351             },
 352             getDocument: function(){
 353                 return this.document;
 354             }
 355         };
 356 
 357         // 工具对象
 358         // 解析xml,获取相应的Document对象
 359         var XmlUtil = {
 360             getRoot: function(filePathName){
 361                 var parser = new DOMParser();
 362                 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‘);
 363 
 364                 return xmldom;
 365             }
 366         };
 367 
 368         /**
 369          * 元素作为非终结符对应的解释器,解释并执行中间元素
 370          * @param {String} eleName [元素的名称]
 371          */
 372         function ElementExpression(eleName){
 373             this.eles = [];
 374             this.eleName = eleName;
 375         }
 376         ElementExpression.prototype = {
 377             addEle: function(eleName){
 378                 this.eles.push(eleName);
 379                 return true;
 380             },
 381             removeEle: function(ele){
 382                 for(var i = 0, len = this.eles.length; i < len; i++) {
 383                     if(ele === this.eles[i]) {
 384                         this.eles.splice(i--, 1);
 385                     }
 386                 }
 387                 return true;
 388             },
 389             interpret: function(context){
 390                 // 先取出上下文中的当前元素作为父级元素
 391                 // 查找到当前元素名称所对应的xml元素,并设置回到上下文中
 392                 var pEles = context.getPreEles();
 393                 var ele = null;
 394                 var nowEles = [];
 395 
 396                 if(!pEles.length) {
 397                     // 说明现在获取的是根元素
 398                     ele = context.getDocument().documentElement;
 399                     pEles.push(ele);
 400                     context.setPreEles(pEles);
 401                 } else {
 402                     var tempEle;
 403                     for(var i = 0, len = pEles.length; i < len; i++) {
 404                         tempEle = pEles[i];
 405                         nowEles = nowEles.concat(context.getNowEles(tempEle, this.eleName));
 406 
 407                         // 找到一个就停止
 408                         if(nowEles.length) break;
 409                     }
 410 
 411                     context.setPreEles([nowEles[0]]);
 412                 }
 413 
 414                 var ss;
 415                 // 循环调用子元素的interpret方法
 416                 for(var i = 0, len = this.eles.length; i < len; i++) {
 417                     ss = this.eles[i].interpret(context);
 418                 }
 419 
 420                 return ss;
 421             }
 422         };
 423 
 424         /**
 425          * 元素作为终结符对应的解释器
 426          * @param {String} name [元素的名称]
 427          */
 428         function ElementTerminalExpression(name){
 429             this.eleName = name;
 430         }
 431         ElementTerminalExpression.prototype = {
 432             interpret: function(context){
 433                 var pEles = context.getPreEles();
 434                 var ele = null;
 435                 if(!pEles.length) {
 436                     ele = context.getDocument().documentElement;
 437                 } else {
 438                     ele = context.getNowEles(pEles[0], this.eleName)[0];
 439                 }
 440 
 441                 // 获取元素的值
 442                 return ele.firstChild.nodeValue;
 443             }
 444         };
 445 
 446         /**
 447          * 属性作为终结符对应的解释器
 448          * @param {String} propName [属性的名称]
 449          */
 450         function PropertyTerminalExpression(propName){
 451             this.propName = propName;
 452         }
 453         PropertyTerminalExpression.prototype = {
 454             interpret: function(context){
 455                 // 直接获取最后的元素属性的值
 456                 return context.getPreEles()[0].getAttribute(this.propName);
 457             }
 458         };
 459 
 460         /**
 461          * 多个属性作为终结符对应的解释器
 462          * @param {String} propName [属性的名称]
 463          */
 464         function PropertysTerminalExpression(propName){
 465             this.propName = propName;
 466         }
 467         PropertysTerminalExpression.prototype = {
 468             interpret: function(context){
 469                 var eles = context.getPreEles();
 470                 var ss = [];
 471 
 472                 for(var i = 0, len = eles.length; i < len; i++) {
 473                     ss.push(eles[i].getAttribute(this.propName));
 474                 }
 475 
 476                 return ss;
 477             }
 478         };
 479 
 480         /**
 481          * 以多个元素作为终结符的解释处理对象
 482          * @param {[type]} name [description]
 483          */
 484         function ElementsTerminalExpression(name){
 485             this.eleName = name;
 486         }
 487         ElementsTerminalExpression.prototype = {
 488             interpret: function(context){
 489                 var pEles = context.getPreEles();
 490                 var nowEles = [];
 491 
 492                 for(var i = 0, len = pEles.length; i < len; i++) {
 493                     nowEles = nowEles.concat(context.getNowEles(pEles[i], this.eleName));
 494                 }
 495 
 496                 var ss = [];
 497 
 498                 for(i = 0, len = nowEles.length; i < len; i++) {
 499                     ss.push(nowEles[i].firstChild.nodeValue);
 500                 }
 501 
 502                 return ss;
 503             }
 504         };
 505 
 506         /**
 507          * 多个元素作为非终结符的解释处理对象
 508          */
 509         function ElementsExpression(name){
 510             this.eleName = name;
 511             this.eles = [];
 512         }
 513         ElementsExpression.prototype = {
 514             interpret: function(context){
 515                 var pEles = context.getPreEles();
 516                 var nowEles = [];
 517 
 518                 for(var i = 0, len = pEles.length; i < len; i++) {
 519                     nowEles = nowEles.concat(context.getNowEles(pEles[i], this.eleName));
 520                 }
 521                 context.setPreEles(nowEles);
 522 
 523                 var ss;
 524                 for(i = 0, len = this.eles.length; i < len; i++) {
 525                     ss = this.eles[i].interpret(context);
 526                 }
 527 
 528                 return ss;
 529             },
 530             addEle: function(ele){
 531                 this.eles.push(ele);
 532                 return true;
 533             },
 534             removeEle: function(ele){
 535                 for(var i = 0, len = this.eles.length; i < len; i++) {
 536                     if(ele === this.eles[i]) {
 537                         this.eles.splice(i--, 1);
 538                     }
 539                 }
 540                 return true;
 541             }
 542         };
 543 
 544         new function(){
 545             // "root/a/b/d$"
 546             var c = new Context(‘Interpreter.xml‘);
 547             var root = new ElementExpression(‘root‘);
 548             var aEle = new ElementExpression(‘a‘);
 549             var bEle = new ElementExpression(‘b‘);
 550             var dEle = new ElementsTerminalExpression(‘d‘);
 551 
 552             root.addEle(aEle);
 553             aEle.addEle(bEle);
 554             bEle.addEle(dEle);
 555 
 556             var ss = root.interpret(c);
 557 
 558             for(var i = 0, len = ss.length; i < len; i++) {
 559                 console.log(‘d的值是 = ‘ + ss[i]);
 560             }
 561         }();
 562 
 563         new function(){
 564             // a/b/d$.id$
 565             var c = new Context(‘Interpreter.xml‘);
 566             var root = new ElementExpression(‘root‘);
 567             var aEle = new ElementExpression(‘a‘);
 568             var bEle = new ElementExpression(‘b‘);
 569             var dEle = new ElementsExpression(‘d‘);
 570             var prop = new PropertysTerminalExpression(‘id‘);
 571 
 572             root.addEle(aEle);
 573             aEle.addEle(bEle);
 574             bEle.addEle(dEle);
 575             dEle.addEle(prop);
 576 
 577             var ss = root.interpret(c);
 578 
 579             for(var i = 0, len = ss.length; i < len; i++) {
 580                 console.log(‘d的属性id的值是 = ‘ + ss[i]);
 581             }
 582         }();
 583 
 584         // 解析器
 585         
 586         /**
 587          * 解析器的实现思路
 588          * 1.把客户端传递来的表达式进行分解,分解成为一个一个的元素,并用一个对应的解析模型来封装这个元素的一些信息。
 589          * 2.根据每个元素的信息,转化成相对应的解析器对象。
 590          * 3.按照先后顺序,把这些解析器对象组合起来,就得到抽象语法树了。
 591          *
 592          * 为什么不把1和2合并,直接分解出一个元素就转换成相应的解析器对象?
 593          * 1.功能分离,不要让一个方法的功能过于复杂。
 594          * 2.为了今后的修改和扩展,现在语法简单,所以转换成解析器对象需要考虑的东西少,直接转换也不难,但要是语法复杂了,直接转换就很杂乱了。
 595          */
 596 
 597         /**
 598          * 用来封装每一个解析出来的元素对应的属性
 599          */
 600         function ParserModel(){
 601             // 是否单个值
 602             this.singleValue;
 603             // 是否属性,不是属性就是元素
 604             this.propertyValue;
 605             // 是否终结符
 606             this.end;
 607         }
 608         ParserModel.prototype = {
 609             isEnd: function(){
 610                 return this.end;
 611             },
 612             setEnd: function(end){
 613                 this.end = end;
 614             },
 615             isSingleValue: function(){
 616                 return this.singleValue;
 617             },
 618             setSingleValue: function(oneValue){
 619                 this.singleValue = oneValue;
 620             },
 621             isPropertyValue: function(){
 622                 return this.propertyValue;
 623             },
 624             setPropertyValue: function(propertyValue){
 625                 this.propertyValue = propertyValue;
 626             }
 627         };
 628 
 629         var Parser = function (){
 630             var BACKLASH = ‘/‘;
 631             var DOT = ‘.‘;
 632             var DOLLAR = ‘$‘;
 633             // 按照分解的先后记录需要解析的元素的名称
 634             var listEle = null;
 635 
 636             // 开始实现第一步-------------------------------------
 637 
 638             /**
 639              * 传入一个字符串表达式,通过解析,组合成为一个抽象语法树
 640              * @param  {String} expr [描述要取值的字符串表达式]
 641              * @return {Object}      [对应的抽象语法树]
 642              */
 643             function parseMapPath(expr){
 644                 // 先按照“/”分割字符串
 645                 var tokenizer = expr.split(BACKLASH);
 646                 // 用来存放分解出来的值的表
 647                 var mapPath = {};
 648                 var onePath, eleName, propName;
 649                 var dotIndex = -1;
 650 
 651                 for(var i = 0, len = tokenizer.length; i < len; i++) {
 652                     onePath = tokenizer[i];
 653 
 654                     if(tokenizer[i + 1]) {
 655                         // 还有下一个值,说明这不是最后一个元素
 656                         // 按照现在的语法,属性必然在最后,因此也不是属性
 657                         setParsePath(false, onePath, false, mapPath);
 658                     } else {
 659                         // 说明到最后了
 660                         dotIndex = onePath.indexOf(DOT);
 661 
 662                         if(dotIndex >= 0) {
 663                             // 说明是要获取属性的值,那就按照“.”来分割
 664                             // 前面的就是元素名称,后面的是属性的名字
 665                             eleName = onePath.substring(0, dotIndex);
 666                             propName = onePath.substring(dotIndex + 1);
 667 
 668                             // 设置属性前面的那个元素,自然不是最后一个,也不是属性
 669                             setParsePath(false, eleName, false, mapPath);
 670                             // 设置属性,按照现在的语法定义,属性只能是最后一个
 671                             setParsePath(true, propName, true, mapPath);
 672                         } else {
 673                             // 说明是取元素的值,而且是最后一个元素的值
 674                             setParsePath(true, onePath, false, mapPath);
 675                         }
 676 
 677                         break;
 678                     }
 679                 }
 680 
 681                 return mapPath;
 682             }
 683 
 684             /**
 685              * 按照分解出来的位置和名称来设置需要解析的元素名称
 686              * @param {Boolean} end           [是否最后一个]
 687              * @param {String} ele           [元素名称]
 688              * @param {Boolean} propertyValue [是否取属性]
 689              * @param {Object} mapPath       [设置需要解析的元素名称,还有该元素对应的解析模型的表]
 690              */
 691             function setParsePath(end, ele, propertyValue, mapPath){
 692                 var pm = new ParserModel();
 693                 pm.setEnd(end);
 694                 // 如果带有“$”符号就说明不是一个值
 695                 pm.setSingleValue(!(ele.indexOf(DOLLAR) >= 0));
 696                 pm.setPropertyValue(propertyValue);
 697                 // 去掉"$"
 698                 ele = ele.replace(DOLLAR, ‘‘);
 699                 mapPath[ele] = pm;
 700                 listEle.push(ele);
 701             }
 702 
 703             // 开始实现第二步-------------------------------------
 704 
 705             /**
 706              * 把分解出来的元素名称根据对应的解析模型转换成为相应的解释器对象
 707              * @param  {Object} mapPath [分解出来的需解析的元素名称,还有该元素对应的解析模型]
 708              * @return {Array}         [把每个元素转换成为相应的解释器对象后的数组]
 709              */
 710             function mapPath2Interpreter(mapPath){
 711                 var list = [];
 712                 var pm, key;
 713                 var obj = null;
 714 
 715                 // 一定要按照分解的先后顺序来转换成解释器对象
 716                 for(var i = 0, len = listEle.length; i < len; i++) {
 717                     key = listEle[i];
 718                     pm = mapPath[key];
 719 
 720                     // 不是最后一个
 721                     if(!pm.isEnd()) {
 722 
 723                         if(pm.isSingleValue()) 
 724                             // 是一个值,转化
 725                             obj = new ElementExpression(key);
 726                         else 
 727                             // 是多个值,转化
 728                             obj = new ElementsExpression(key);
 729                         
 730                     } else {
 731                         // 是最后一个
 732                         
 733                         // 是属性值
 734                         if(pm.isPropertyValue()) {
 735                             if(pm.isSingleValue())
 736                                 obj = new PropertyTerminalExpression(key);
 737                              else
 738                                 obj = new PropertysTerminalExpression(key);
 739 
 740                         // 取元素的值
 741                         } else {
 742                             if(pm.isSingleValue())
 743                                 obj = new ElementTerminalExpression(key);
 744                             else
 745                                 obj = new ElementsTerminalExpression(key);
 746                         }
 747                     }
 748 
 749                     list.push(obj);
 750                 }
 751 
 752                 return list;
 753             }
 754 
 755             // 开始实现第三步-------------------------------------
 756             
 757             /**
 758              * 构建抽象语法树
 759              * @param  {[type]} list [把每个元素转换成为相应的解释器对象后的数组]
 760              * @return {[type]}      [description]
 761              */
 762             function buildTree(list){
 763                 // 第一个对象,也是返回去的对象,就是抽象语法树的根
 764                 var returnReadXMLExpr = null;
 765                 // 定义上一个对象
 766                 var preReadXmlExpr = null;
 767                 var readXml, ele, eles;
 768 
 769                 for(var i = 0, len = list.length; i < len; i++) {
 770                     readXml = list[i];
 771                     // 说明是第一个元素
 772                     if(preReadXmlExpr === null) {
 773                         preReadXmlExpr = readXml;
 774                         returnReadXMLExpr = readXml;
 775 
 776                     // 把元素添加到上一个对象下面,同时把本对象设置成为oldRe
 777                     // 作为下一个对象的父节点
 778                     } else {
 779                         if(preReadXmlExpr instanceof ElementExpression) {
 780                             ele = preReadXmlExpr;
 781                             ele.addEle(readXml);
 782                             preReadXmlExpr = readXml;
 783                         } else if(preReadXmlExpr instanceof ElementsExpression){
 784                             eles = preReadXmlExpr;
 785                             eles.addEle(readXml);
 786                             preReadXmlExpr = readXml;
 787                         }
 788                     }
 789                 }
 790 
 791                 return returnReadXMLExpr;
 792             }
 793 
 794             return {
 795                 // 公共方法
 796                 parse: function(expr){
 797                     listEle = [];
 798 
 799                     var mapPath = parseMapPath(expr);
 800                     var list = mapPath2Interpreter(mapPath);
 801 
 802                     return buildTree(list);
 803                 }
 804             };
 805         }();
 806 
 807         new function(){
 808             // 准备上下文
 809             var c = new Context(‘Interpreter.xml‘);
 810             // 通过解析其获取抽象语法树
 811             var readXmlExpr = Parser.parse(‘root/a/b/d$.id$‘);
 812             // 请求解析,获取返回值
 813             var ss = readXmlExpr.interpret(c);
 814 
 815             console.log(‘------------parseing--------------‘);
 816             for(var i = 0, len = ss.length; i < len; i++) {
 817                 console.log(‘d的属性id的值是 = ‘ + ss[i]);
 818             }
 819             console.log(‘---------------parsed--------------‘);
 820 
 821             // 如果要使用同一个上下文,连续进行解析,需要重新初始化上下文对象
 822             c.reInit();
 823             var readxmlExpr2 = Parser.parse(‘root/a/b/d$‘);
 824             var ss2 = readxmlExpr2.interpret(c);
 825             console.log(‘------------parseing--------------‘);
 826             for(i = 0, len = ss2.length; i < len; i++) {
 827                 console.log(‘d的值是 = ‘ + ss2[i]);
 828             }
 829             console.log(‘---------------parsed--------------‘);
 830 
 831             c.reInit();
 832             var readxmlExpr3 = Parser.parse(‘root/a/b/c‘);
 833             var ss3 = readxmlExpr3.interpret(c);
 834             console.log(‘------------parseing--------------‘);
 835             console.log(‘c的name属性值是 = ‘ + ss3);
 836             console.log(‘---------------parsed--------------‘);
 837 
 838             c.reInit();
 839             var readxmlExpr4 = Parser.parse(‘root/a/b/c.name‘);
 840             var ss4 = readxmlExpr4.interpret(c);
 841             console.log(‘------------parseing--------------‘);
 842             console.log(‘c的name属性值是 = ‘ + ss4);
 843             console.log(‘---------------parsed--------------‘);
 844         }();
 845 
 846         // 这样就实现了类似XPath的部分功能
 847         // 没错,就类似于jQuery选择器的部分功能
 848     }());
 849 
 850 
 851 
 852     /**
 853      * 1.功能
 854      * 解释器模式使用解释器对象来表示和处理相应的语法规则,一般一个解释器处理一条语法规则。理论上来说,只要能用解释器对象把符合语法的表达式表示出来,而且能够构成抽象的语法树,就可以使用解释器模式来处理。
 855      *
 856      * 2.语法规则和解释器
 857      * 语法规则和解释器之间是有对应关系的,一般一个解释器处理一条语法规则,但是反过来并不成立,一条语法规则是可以有多种解释和处理的,也就是一条语法规则可以对应多个解释器。
 858      *
 859      * 3.上下文的公用性
 860      * 上下文在解释器模式中起着非常重要的作用。由于上下文会被传递到所有的解释器中。因此可以在上下文中存储和访问解释器的状态,比如,前面的解释器可以存储一些数据在上下文中,后面的解释器就可以获取这些值。
 861      * 另外还可以通过上下文传递一些在解释器外部,但是解释器需要的数据,也可以是一些全局的,公共的数据。
 862      * 上下文还有一个功能,就是可以提供所有解释器对象的公共功能,类似于对象组合,而不是使用继承来获取公共功能,在每个解释器对象中都可以调用
 863      *
 864      * 4.谁来构建抽象语法树
 865      * 在前面的示例中,是自己在客户端手工构建抽象语法树,是很麻烦的,但是在解释器模式中,并没有涉及这部分功能,只是负责对构建好的抽象语法树进行解释处理。后面会介绍可以提供解析器来实现把表达式转换成为抽象语法树。
 866      * 还有一个问题,就是一条语法规则是可以对应多个解释器对象的,也就是说同一个元素,是可以转换成多个解释器对象的,这也就意味着同样一个表达式,是可以构成不用的抽象语法树的,这也造成构建抽象语法树变得很困难,而且工作量非常大。
 867      *
 868      * 5.谁负责解释操作
 869      * 只要定义好了抽象语法树,肯定是解释器来负责解释执行。虽然有不同的语法规则,但是解释器不负责选择究竟用哪个解释器对象来解释执行语法规则,选择解释器的功能在构建抽象语法树的时候就完成了。
 870      *
 871      * 6.解释器模式的调用顺序
 872      * 1)创建上下文对象
 873      * 2)创建多个解释器对象,组合抽象语法树
 874      * 3)调用解释器对象的解释操作
 875      * 3.1)通过上下文来存储和访问解释器的状态。
 876      * 对于非终结符解释器对象,递归调用它所包含的字解释器对象。
 877      *
 878      * 相关模式
 879      *
 880      * 解释器模式和组合模式
 881      * 这两个模式可以组合使用。
 882      * 通常解释器模式都会使用组合模式来实现,这样能够方便地构建抽象语法树,一般非终结符解释器就相当于组合模式中的组合对象;终结符解释器就相当于叶子对象。
 883      *
 884      * 解释器模式和迭代器模式
 885      * 这两个模式可以组合使用。
 886      * 由于解释器模式通常使用组合模式来实现,因此在遍历整个对象结构的时候,自然可以使用迭代器模式。
 887      *
 888      * 解释器和享元模式
 889      * 这两个模式可以组合使用。
 890      * 在使用解释器模式的时候,可能会造成多个细粒度对象,比如,会有各种各样的终结符解释器,而这些终结符解释器对不同的表达式来说是一样的,是可以共用的,因此可以引入享元模式来共享这些对象。
 891      *
 892      * 解释器模式和访问者模式
 893      * 这两个模式可以组合使用。
 894      * 在解释器模式中,语法规则和解释器对象是有对应关系的。语法规则的变动意味着功能的变化,自然会导致使用不用的解释器对象;而且一个语法规则可以被不同的解释器解释执行。
 895      * 因此在构建抽象语法树的时候,如果每个节点所对应的解释器对象是固定的,这就意味着该节点对应的功能是固定的,那么就不得不根据需要来构建不用抽象语法树,
 896      * 为了让构建的抽象语法树较为通用,那就要求解释器的功能不要那么固定,要能很方便的改变解释器的功能,这个时候问题就变成了如何能够很方便地更改树形结构中节点对象的功能了,访问者模式可以很好的实现这个功能。
 897      */
 898 }());
 899 
 900 (function(){
 901     // http://blog.csdn.net/dead_of_winter/article/details/2158492
 902 
 903     /**
 904      * 解释器模式在js中有两个最典型的应用json和正则表达式,对js程序员来说,这应该是很熟悉的两种东西。json用于序列化对象型数据,这个js的对象文字量形式在包括C++,Java在内的各种语言中都有实现的类库,在一些ajax应用中,java或者C#中的对象被序列化为json格式,通过相应客户端的http请求传递给客户端的js程序,js几乎不需要任何处理,仅仅使用eval就可以把json格式的数据还原成js对象(因为json恰巧是来自js),这在解释器模式的实现中是很少见的。现在,不仅仅使与js相关的应用,即使在其他语言的应用中,json也是一种很受欢迎的数据交换格式。正则表达式是js的内置对象,它可以说是最著名的解释器模式了,几乎所有语言中都有它的实现,现在它已经几乎是字符串匹配的事实标准。它能处理字符串的各种格式,有效地避免了过于复杂的string对象接口或者大段的字符串分析代码,这对开发效率至关重要。js的实现是一个比较强的版本,相比java和C#等语言,js允许函数参数为它提供了更大的灵活性。
 905      *
 906      *
 907      * 词法分析·状态机的实现
 908      *
 909      * 通常解释器模式需要将所定义的"语言"字符流转换成适合的程序数据结构,再对这个结构进行分析。对于比较简单的情况,转换和分析可以在一步完成。为了很好好的完成这项工作,我们需要实现一个状态机。
 910      * 状态机原本不是软件和程序中的术语,在数字逻辑中有限状态机是指输出取决于过去输入部分和当前输入部分的时序逻辑电路。这里甚至无需强调有限状态机,可以简单理解状态机为一个黑箱子,向其中投入指令后即可进行操作和装换状态,它有一个最终状态,当到达最终状态时,即可完成任务。
 911      * 词法分析有限状态机任务很简单,从输入字符流中读入一个一个的字符,当辨认出输入的字符能构成一个独立的语法单元(token)时,便将这个token放入待分析的词句流中。
 912      * 这里给出一个简单的例子:正斜杠转义的实现。通常字符串转义都是以反斜杠/实现的,假如有一个字符串,现在我们要把正斜杠用作转义符以做一些特殊用途,其他字符原样放置。那么正斜杠/和它后面的字符必须被看成一个整体,其它每个字符都是一个整体。
 913      * 这个状态机只有两个状态 第一个状态是读入普通字符状态 第二个状态是读入正斜杠以后的状态
 914      */
 915 
 916     // 在js中 充分利用语言特性 将每个状态实现为一个函数 它接受一个状态改变参数 然后返回下一个状态
 917     // 这是一个标准的状态机处理词法分析的例子,事实上,有些简单的解释器模式,仅仅通过词法分析即可实现,功能可以写在状态改变函数中,而无需对产生的token流进行处理。
 918 
 919     function state_machine()
 920     {
 921         this.state = _1;
 922         this.result = [];
 923         function _1(c){
 924             if(c != ‘/‘){
 925                 this.result.push(c);
 926                 return _1;
 927             } else {
 928                 return _2;
 929             }
 930         }
 931         function _2(c){
 932             this.result.push(‘/‘ + c);
 933             return _1;
 934         }
 935         this.change = function(c){
 936             this.state = this.state(c);
 937         };
 938     }
 939     var sm = new state_machine();
 940     var queue = ("a//sd/jh/ds").split(‘‘);
 941 
 942     for(var i = 0; i < queue.length; i++)
 943         sm.change(queue[i]);
 944 
 945     console.log(sm.result);
 946 
 947 
 948     /**
 949      * 函数式语言特性与状态机
 950      *
 951      * 作为函数式语言,js实现解释器模式有非常有趣的方式:以不定个数的参数形式传入函数进行处理,这样可以方便的扩展功能,同时可以使用户更自由的使用解释器提供的接口。
 952      * 下面一段代码是一个用于日期对象的格式化的类 它是状态机词法分析的一个稍微复杂的例子,同时它以函数参数的方式为用户提供了扩展功能。
 953      */
 954 
 955     /*
 956     DateEx类
 957     说明:以参数形式继承自Date对象 为Date对象扩展方法
 958     方法:
 959         format(formatString,[fun],......)
 960         参数:
 961             formatString:格式字符串 将日期转换成所规定的格式字符串
 962                 格式说明:
 963                     %[x]:
 964                         [x]代表日期的一个部分
 965                     %y:年
 966                     %m:月
 967                     %d:日
 968                     %w:星期
 969                     %h:小时
 970                     %i:分
 971                     %s:秒
 972                     
 973                     %[num][x]:
 974                         [num]代表长度 [x]意义同上 如果长度不足则用0补齐 如果长度超出[num]则将高位截断
 975                         
 976                     %f[x]:
 977                         以自定义函数处理%[x]得到的值,自定义函数在参数列表[fun]中给出,参数中[fun]的个数应与%f[x]的数目一致
 978                    
 979             
 980             fun:可选的,处理函数,当格式字符串中有格式符%f出现时,则在fun中取相应的函数处理
 981     */
 982     function DateEx(date){
 983         date = date || new Date();
 984         date.format = function(formatString)
 985         {
 986             var f;
 987             var j = 0;
 988             function fbuilder(n){
 989                 return function(v){
 990                     var s = v.toString();
 991                     if(s.length >= n) return s.slice(s.length - n, s.length);
 992                     if(s.length < n) return new Array(n - s.length + 1).join(0) + s;
 993                 };
 994             }
 995             var args = arguments;
 996             var resault = new String();
 997             var _1 = function(c)//状态1 是读入格式字符串的状态
 998             {
 999                 if(c != "%")//对于非%字符按原样输出
1000                 {
1001                     resault += c;
1002                     return _1;
1003                 }
1004                 else//读到%时进入状态2 否则延续状态1
1005                 {
1006                     return _2;
1007                 }
1008             };
1009             var _2 = function(c)//状态2 是读入特殊格式字符串的状态
1010             {
1011                 if(c.match(/d/) != null)//对于数字 构造相应处理函数 返回状态3
1012                 {
1013                     f = fbuilder(Number(c));
1014                     return _3;
1015                 }
1016                 else if(c == "f")//对于格式符f 从参数中获取相应处理函数 返回状态3
1017                 {
1018                     f = args[++j];
1019                     return _3;
1020                 }
1021                 else//没有特殊格式符 直接进入状态3
1022                 {
1023                     f = function(v){return v;}
1024                     return _3(c);
1025                 }
1026 
1027 
1028             };
1029             var _3 = function(c)
1030             {
1031                 if(c == "%")//格式符% 连续2个%将被转义为一个% 返回状态1
1032                 {
1033                     resault += c;
1034                     return _1;
1035                 }
1036                 else if(c == "y")//格式符y 取出年份 返回状态1
1037                 {
1038                     resault += f(date.getFullYear());
1039 
1040                     return _1;
1041                 }
1042                 else if(c == "m")//格式符m 取出月份 返回状态1
1043                 {
1044                     resault += f(date.getMonth()+1);
1045                     return _1;
1046                 }
1047                 else if(c == "d")//格式符d 取出日期 返回状态1
1048                 {
1049                     resault += f(date.getDate());
1050                     return _1;
1051                 }
1052                 else if(c == "w")//格式符w 取出星期 返回状态1
1053                 {
1054                     resault += f(date.getDay());
1055                     return _1;
1056                 }
1057                 else if(c == "h")//格式符h 取出小时 返回状态1
1058                 {
1059                     resault += f(date.getHours());
1060                     return _1;
1061                 }
1062                 else if(c == "i")//格式符i 取出分 返回状态1
1063                 {
1064                     resault += f(date.getMinutes());
1065                     return _1;
1066                 }
1067                 else if(c == "s")//格式符s 取出秒 返回状态1
1068                 {
1069                     resault += f(date.getSeconds());
1070                     return _1;
1071                 }
1072                 else return _1//没有合法格式符 忽略 返回状态1
1073             };
1074             var status = _1;
1075             for(var i = 0; i < formatString.length; i++)
1076             {
1077                 status = status(formatString.charAt(i));
1078             }
1079             return resault;
1080         }
1081         return date;
1082     }
1083     var weekdays = "日一二三四五六";
1084     console.log(new DateEx().format("%2y-%2m-%2d 星期%fw %2h:%2i:%2s %%", function(v){return weekdays.charAt(v);}))
1085 
1086 
1087     /**
1088      * 动态语言特性·eval与解释器模式
1089      *
1090      * js的另一个非常有趣特点是它本身是一门解释型语言,它允许用eval和Function等方式调用其本身的解释器引擎,这样给解释器的实现带来了很大的方便,可以将某段自定义语言(如代数运算或者布尔运算不分)作为一个独立的token用eval直接执行,这种形式的解释器是静态语言无法比拟的
1091      */
1092 }());
1093 
1094 (function(){
1095     // http://www.dofactory.com/javascript-interpreter-pattern.aspx
1096 
1097     // 将罗马数字表达式转换为阿拉伯数字
1098 
1099     var Context = function (input) {
1100         this.input = input;
1101         this.output = 0;
1102     };
1103 
1104     Context.prototype = {
1105         startsWith : function (str) {
1106             return this.input.substr(0, str.length) === str;
1107         }
1108     };
1109 
1110     var Expression = function (name, one, four, five, nine, multiplier) {
1111         this.name = name;
1112         this.one = one;
1113         this.four = four;
1114         this.five = five;
1115         this.nine = nine;
1116         this.multiplier = multiplier;
1117     };
1118 
1119     Expression.prototype = {
1120         interpret: function (context) {
1121             if (context.input.length == 0) {
1122                 return;
1123             }
1124             else if (context.startsWith(this.nine)) {
1125                 context.output += (9 * this.multiplier);
1126                 context.input = context.input.substr(2);
1127             }
1128             else if (context.startsWith(this.four)) {
1129                 context.output += (4 * this.multiplier);
1130                 context.input = context.input.substr(2);
1131             }
1132             else if (context.startsWith(this.five)) {
1133                 context.output += (5 * this.multiplier);
1134                 context.input = context.input.substr(1);
1135             }
1136 
1137             while (context.startsWith(this.one)) {
1138                 context.output += (1 * this.multiplier);
1139                 context.input = context.input.substr(1);
1140             }
1141         }
1142     };
1143 
1144 
1145     void function run() {
1146 
1147         var roman = "MCMXXVIII"
1148         var context = new Context(roman);
1149         var tree = [];
1150 
1151         tree.push(new Expression("thousand", "M", " " , " ", " " , 1000));
1152         tree.push(new Expression("hundred",  "C", "CD", "D", "CM", 100));
1153         tree.push(new Expression("ten",      "X", "XL", "L", "XC", 10));
1154         tree.push(new Expression("one",      "I", "IV", "V", "IX", 1));
1155 
1156         for (var i = 0, len = tree.length; i < len; i++) {
1157             tree[i].interpret(context);
1158         }
1159 
1160         console.log(roman + " = " + context.output);
1161     }();
1162 }());
1163 
1164 </script>
1165 </body>
1166 </html>
bubuko.com,布布扣

javascript设计模式=解释其模式(interpreter)

原文:http://www.cnblogs.com/webFrontDev/p/3533129.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!