2012年2月25日

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

/(\/\*[\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
これがパターンです。

例えば、
/* comment */
var regex = /[\/\\"']*/g;
var str = "hoge\"fuga'\
piyo";
// 'comment'
var arr = [
    0x0FFF, 1e8, 0, 12345,
    3.524603890386048e+24,
    0.0006215, 0666 // (classic mode)
];
var i = 0;
var e4x = <>{{///*E4X*///}}</>;
var xml = <root><hoge fuga="piyo" /></root>;
var ほげ = '/*"ほ//げ"*/';
var $var = re.test(str) && arr[2] || (i+++i) === 1;
このコードを文字列として code.match(pattern) すると、以下の要素を持った配列が得られます。
0: /* comment */
1: 

2: var
3:  
4: regex
5:  
6: =
7:  
8: /[\/\\"']*/g
9: ;
10: 

11: var
12:  
13: str
14:  
15: =
16:  
17: "hoge\"fuga'\
piyo"
18: ;
19: 

20: // 'comment'

21: var
22:  
23: arr
24:  
25: =
26:  
27: [
28: 

29:   
30: 0x0FFF
31: ,
32:  
33: 1e8
34: ,
35:  
36: 0
37: ,
38:  
39: 12345
40: ,
41: 

42:   
43: 3.524603890386048e+24
44: ,
45: 

46:   
47: 0.0006215
48: ,
49:  
50: 0666
51: 

52: ]
53: ;
54: 

55: var
56:  
57: i
58:  
59: =
60:  
61: 0
62: ;
63: 

64: var
65:  
66: e4x
67:  
68: =
69:  
70: <>{{///*E4X*///}}</>
71: ;
72: 

73: var
74:  
75: xml
76:  
77: =
78:  
79: <root><hoge fuga="piyo" /></root>
80: ;
81: 

82: var
83:  
84: ほげ
85:  
86: =
87:  
88: '/*"ほ//げ"*/'
89: ;
90: 

91: var
92:  
93: $var
94:  
95: =
96:  
97: re
98: .
99: test
100: (
101: str
102: )
103:  
104: &&
105:  
106: arr
107: [
108: 2
109: ]
110:  
111: ||
112:  
113: (
114: i
115: ++
116: +
117: i
118: )
119:  
120: ===
121:  
122: 1
123: ;
そもそもシンタックスハイライトのほうが崩れてる…。

jsFiddle でテストできます。

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

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


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


0 件のコメント:

コメントを投稿