2012年2月25日

JavaScriptコードをトークンに分解する正規表現

  1. /(\/\*[\s\S]*?\*\/|\/{2,}[^\r\n]*(?:\r\n|\r|\n|)|"(?:\\[\s\S]|[^"\r\n\\])*"|'(?:\\[\s\S]|[^'\r\n\\])*'|(?:(?:\/(?!\*)(?:\\.|[^\/\r\n\\])+\/)(?:[gimy]{0,4}|\b)(?=\s*(?:(?!\s*[\/\\<>*+%`^"'\w$-])[^\/\\<>*+%`^'"@({[\w$-]|===?|!==?|\|\||[&][&]|\/[*\/]|[,.;:!?)}\]\r\n]|$)))|<([^\s>]*)[^>]*>[\s\S]*?<\/\2>|>>>=?|<<=|===|!==|>>=|\+\+(?=\+)|\-\-(?=\-)|[=!<>*+\/&|^-]=|[&][&]|\|\||\+\+|\-\-|<<|>>|0(?:[xX][0-9a-fA-F]+|[0-7]+)|\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|[1-9]\d*|[-+\/%*=&|^~<>!?:,;@()\\[\].{}]|(?![\r\n])\s+|(?:\r\n|\r|\n)|[^\s+\/%*=&|^~<>!?:,;@()\\[\].{}'"-]+)/g  
これがパターンです。

例えば、
  1. /* comment */  
  2. var regex = /[\/\\"']*/g;  
  3. var str = "hoge\"fuga'\  
  4. piyo";  
  5. // 'comment'  
  6. var arr = [  
  7.     0x0FFF, 1e8, 0, 12345,  
  8.     3.524603890386048e+24,  
  9.     0.0006215, 0666 // (classic mode)  
  10. ];  
  11. var i = 0;  
  12. var e4x = <>{{///*E4X*///}}</>;  
  13. var xml = <root><hoge fuga="piyo" /></root>;  
  14. var ほげ = '/*"ほ//げ"*/';  
  15. var $var = re.test(str) && arr[2] || (i+++i) === 1;  
このコードを文字列として code.match(pattern) すると、以下の要素を持った配列が得られます。
  1. 0: /* comment */  
  2. 1:   
  3.   
  4. 2: var  
  5. 3:    
  6. 4: regex  
  7. 5:    
  8. 6: =  
  9. 7:    
  10. 8: /[\/\\"']*/g  
  11. 9: ;  
  12. 10:   
  13.   
  14. 11: var  
  15. 12:    
  16. 13: str  
  17. 14:    
  18. 15: =  
  19. 16:    
  20. 17: "hoge\"fuga'\  
  21. piyo"  
  22. 18: ;  
  23. 19:   
  24.   
  25. 20: // 'comment'  
  26.   
  27. 21: var  
  28. 22:    
  29. 23: arr  
  30. 24:    
  31. 25: =  
  32. 26:    
  33. 27: [  
  34. 28:   
  35.   
  36. 29:     
  37. 30: 0x0FFF  
  38. 31: ,  
  39. 32:    
  40. 33: 1e8  
  41. 34: ,  
  42. 35:    
  43. 36: 0  
  44. 37: ,  
  45. 38:    
  46. 39: 12345  
  47. 40: ,  
  48. 41:   
  49.   
  50. 42:     
  51. 43: 3.524603890386048e+24  
  52. 44: ,  
  53. 45:   
  54.   
  55. 46:     
  56. 47: 0.0006215  
  57. 48: ,  
  58. 49:    
  59. 50: 0666  
  60. 51:   
  61.   
  62. 52: ]  
  63. 53: ;  
  64. 54:   
  65.   
  66. 55: var  
  67. 56:    
  68. 57: i  
  69. 58:    
  70. 59: =  
  71. 60:    
  72. 61: 0  
  73. 62: ;  
  74. 63:   
  75.   
  76. 64: var  
  77. 65:    
  78. 66: e4x  
  79. 67:    
  80. 68: =  
  81. 69:    
  82. 70: <>{{///*E4X*///}}</>  
  83. 71: ;  
  84. 72:   
  85.   
  86. 73: var  
  87. 74:    
  88. 75: xml  
  89. 76:    
  90. 77: =  
  91. 78:    
  92. 79: <root><hoge fuga="piyo" /></root>  
  93. 80: ;  
  94. 81:   
  95.   
  96. 82: var  
  97. 83:    
  98. 84: ほげ  
  99. 85:    
  100. 86: =  
  101. 87:    
  102. 88: '/*"ほ//げ"*/'  
  103. 89: ;  
  104. 90:   
  105.   
  106. 91: var  
  107. 92:    
  108. 93: $var  
  109. 94:    
  110. 95: =  
  111. 96:    
  112. 97: re  
  113. 98: .  
  114. 99: test  
  115. 100: (  
  116. 101: str  
  117. 102: )  
  118. 103:    
  119. 104: &&  
  120. 105:    
  121. 106: arr  
  122. 107: [  
  123. 108: 2  
  124. 109: ]  
  125. 110:    
  126. 111: ||  
  127. 112:    
  128. 113: (  
  129. 114: i  
  130. 115: ++  
  131. 116: +  
  132. 117: i  
  133. 118: )  
  134. 119:    
  135. 120: ===  
  136. 121:    
  137. 122: 1  
  138. 123: ;  
そもそもシンタックスハイライトのほうが崩れてる…。

jsFiddle でテストできます。

文字列は、改行の前にバックスラッシュ (\) がある場合も対応してみました。
セミコロン挿入もあって、スペースと改行は区別されます。

E4X はシンプルなのだけ対応してます。
  1. var x = <root<div id={(function() {  
  2. return '</root>' ? 'hoge' : 'fuga';  
  3. }())}></root>  
こういうのは無理っぽい。あとネストとか。


match() に g つけて match(/(...)/g) がこんな便利だなんて…。


0 件のコメント:

コメントを投稿