Skin:
[NORMAL]
[BLUE] [DOS] [LIGHT]  / コピーするための表示 / 実行
比較対象からの変更点をマーキング
このファイル: batch - 20180805.js
比較対象: batch - 20180803.js

1 /*
2 スクリプトを分析し、オブジェクト化する。
3 1行ずつステップ実行でき、
4 //anmのある行まで一気に実行することもできる。
5
6 アニメーションにおいて、1つのアニメが終わった後、別のアニメを実行したい。
7 1つのアニメを関数に定義して、次のアニメも関数に定義する。
8 それらの関数を呼び出す関数を定義し、実行する。
9
10 そのためには、まず1行についてアニメ処理となるようにする。
11
12
13
14 サンプル:
15 onloadx() 関数からテキストを抽出し、instructionへ渡し、setInterval。
16 debugprint_recursive()
17
18 本体:
19 InstructionRE 実行環境
20 exec() instructionを実行
21 Instruction テキストを受け取り、プログラムとして解析。
22 analyze() テキストを解析し、階層構造を作成する。
23 escapeBrackets()
24 test()
25 test2();
26
27 */
28
29 function debugprint_recursive( object, indent ) {
30 //check.
31 if( indent == null ) indent = "";
32
33 console.log( "DEC[" + object.declaration + "]\tINST[" + object.instruction + "]\tARG[" + object.argtext + "]" );
34 for( var i = 0; i < object.children.length; i++ ) {
35 indent += "\t";
36 debugprint_recursive( object.children[ i ], indent );
37 indent = indent.substr( 0, indent.length - 1 );
38 }
39 }
40
41
42 //プログラムの命令クラス
43 function Instruction( declaration, instruction, argtext, blocktext, comment, tp ) {
44 /*
45 一般にプログラムの1命令は以下の4項目で成り立っているとここでは考える。
46
47 declaration instracion( argtext ) { blocktext }
48 宣言 命令 引数 ブロック
49
50 単語を以下のように略す。
51 decl inst( argt ) blkt
52
53 例:
54 a = b; //inst decl, argt, blktがない状態
55 if( a == b ) { .. } //inst( argt ) { blkt } declがない状態
56 else { .. } //inst { blkt } decl, argtがない状態
57 test(); //inst( argt ) decl, blktがない状態
58 function test() { .. } //decl inst( argt ) { blkt } すべてそろっている状態
59 var a; //decl inst argt, blktがない状態
60 var a = b; //decl inst argt, blktがない状態
61
62 引数の parts.blocktext は analyze() にとおし、階層構造にして children へ収める。
63 */
64
65
66 this.declaration = declaration;
67 this.instruction = instruction;
68 this.argtext = argtext;
69 this.blocktext = blocktext;
70 this.comment = comment;
71 this.tp = tp;
72
73 this.children = new Array();
74
75 //ブロック部分を解析
76 if( this.blocktext != null ) this.analyze( this.blocktext );
77
78 } [There are deleted line(s).]
79
80 Instruction.prototype.analyze = function( text ) {
81 //if(..) test(); を if(..) {test();} にする。
82 text = text.replace( /(if|while|for)(\(.*?\)\s*)([^\{\s][\s\S]+)/g, function( m, a, b, c ) { return a + b + "{" + c + "}" } );
83
84 //1. 不要なタブと改行を削除する。
85
86 text = text.replace( /^\t+/mg, " " ); //行頭のタブを半角スペース1個にする
87 text = text.replace( /\n/g, ";" ); //改行を半角スペース1個にする
88
89
90 //2. 括弧で囲まれた部分を退避する。
91 //(すると入れ子構造がなくなり1行1行の4項目の取得が行いやすくなる)
92
93 var res = escapeBrackets( text, "{", "}", "_block_#_;", true );
94 var blocks = res.contents;
95
96 var res = escapeBrackets( res.text, "(", ")", "_kakko_#_" );
97 var kakkos = res.contents;
98
99
100 var text2 = res.text;
101 text2 = text2.replace( /;\s+/mg, ";" ); //;に続くスペースを削除
102 text2 = text2.replace( /^\s+/gm, "" ); //行頭のスペースを削除
103
104 //3. そのテキストを解析。
105
106 var lines = text2.split( /;/ );
107 for( var i = 0; i < lines.length; i++ ) {
108 var line = lines[ i ];
109 //check. 空行はスキップ
110 if( line.match( /^\s*$/ ) ) continue;
111
112 //1命令を構成する4項目
113 var declaration = null;
114 var instruction = null;
115 var argtext = null;
116 var blocktext = null;
117 var comment = null;
118 var commentN = null; //NはNextのN
119 var tp = null;
120
121 //コメントを処理 ※注: test();//comment は;で改行されて単独行になっている
122 if( line.match( /\/\// ) ) {
123 line = RegExp.leftContext;
124 comment = RegExp.rightContext;
125 comment = comment.replace( /^\s+|\s+$/g, "" );
126 }
127 if( i + 1 < lines.length && lines[ i + 1 ].match( /\/\// ) ) {
128 commentN = RegExp.rightContext;
129 commentN = commentN.replace( /^\s+|\s+$/g, "" );
130 }
131
132 //各行の形状別に4項目を取得
133 /*
134 var a = b;
135
136 */
137 if( line.match( /(var\s+|)(.+=)(.+)/ ) ) { //var a=b または a=b
138
139 tp = 1;
140 declaration = RegExp.$1;
141 instruction = RegExp.$2;
142 argtext = RegExp.$3;
143
144 } else if( line.match( /(var\s+)(.+)/ ) ) { //var a
145
146 tp = 2;
147 declaration = RegExp.$1;
148 instruction = RegExp.$2;
149
150 } else if( line.match( /(function\s+|)(.+)\((.*)\)(.+)/ ) ) { //function f() .. または if(true) ..など
151
152 tp = 3;
153 declaration = RegExp.$1;
154 instruction = RegExp.$2;
155 argtext = RegExp.$3;
156 blocktext = RegExp.$4;
157
158 } else if( line.match( /(.+)\((.*)\)/ ) && commentN == "stepin" ) { //func(); //stepin
159
160 tp = 4.5;
161 instruction = "if";
162 argtext = "true";
163 blocktext = eval( RegExp.$1 ).toString().match( /\{([\s\S]*)\}/ )[ 1 ];
164
165 } else if( line.match( /(.+)\((.*)\)/ ) ) { //func();
166
167 tp = 4;
168 instruction = RegExp.$1;
169 argtext = RegExp.$2;
170
171 } else if( line.match( /(.*)\s(.*)/ ) ) { //else ..
172
173 tp = 5;
174 instruction = RegExp.$1;
175 blocktext = RegExp.$2;
176
177 } else if( comment != null ) { //コメント行
178
179 } else {
180
181 console.log( "syntax error: " + ": [" + line + "]" );
182 continue;
183
184 }
185
186 //4. 4項目のうち argtext と blocktext について、退避した括弧部分を展開
187 if( argtext != null )
188 while( argtext.match( /_kakko_(\d+)_/ ) ) {
189 argtext = RegExp.leftContext + kakkos[ RegExp.$1 ] + RegExp.rightContext;
190 }
191
192 if( blocktext != null ) {
193
194 //if() {..}ではなく、if() .. の形で .. の部分で退避された括弧を展開
195 while( blocktext.match( /_kakko_(\d+)_/ ) ) {
196 blocktext = RegExp.leftContext + kakkos[ RegExp.$1 ] + RegExp.rightContext;
197 }
198
199 if( blocktext.match( /_block_(\d+)_/ ) ) {
200 blocktext = blocks[ RegExp.$1 ];
201 }
202 }
203
204 //先頭または末尾のスペースを除去
205 if( declaration != null ) declaration = declaration.replace( /^\s+|\s+$/, "" );
206
207 //5. 4項目から新しい instruction を生成する(階層構造となる)
208 this.children.push( new Instruction( declaration, instruction, argtext, blocktext, comment, tp ) );
209 }//for i
210 }
211
212 Instruction.prototype.exec = function() {
213 var envs = new Array(); //スタックとして使用
214 var env = {
215 object : this,
216 counter : 0,
217 next : null,
218 parentEnv : null,
219 intoBlk : true,
220 };
221
222 owner = this;
223
224 var func = function() {
225 //envs はこの中でいつまでも有効(クロージャ)
226 //env はこの中でいつまでも有効(クロージャ)
227 loop1: while( 1 ) {
228 //check. ループを抜ける
229 if( ! env.object.commentProcessed && env.object.comment && env.object.comment.match( /anm(\d+)/ ) ) {
230 env.object.commentProcessed = true;
231 var ms = Number( RegExp.$1 );
232 setTimeout( func, ms ); //ループへ戻る
233 break loop1;
234 }
235 else if( ! env.object.commentProcessed && env.object.comment && env.object.comment.match( /suspend/ ) ) {
236 env.object.commentProcessed = true;
237 owner.resume = func;
238 break loop1;
239 }
240
241 env.object.commentProcessed = false;
242
243 //処理実行
244 switch( env.object.instruction ) {
245 //Y 制御 分岐
246 case "if":
247 env.intoBlk = eval( env.object.argtext );
248 //check. ifに対応するelseについて
249 if( env.next && env.next.instruction == "else" ) {
250 env.next.elseCancel = env.intoBlk;
251 }
252 break;
253 case "else":
254 break;
255 //O 制御 繰り返し
256 case "for":
257 if( typeof env.forInit === "undefined" ) {
258 if( env.object.argtext.match( /(.*);(.*);(.*)/ ) ) {
259 env.forInit = RegExp.$1;
260 env.forCond = RegExp.$2;
261 env.forAdd = RegExp.$3;
262 } else {
263 console.log( "syntax erro on for: " + env.object.argtext );
264 }
265 env.forInit = env.forInit.replace( /var\s+/, "env." );
266 eval( env.forInit );
267 } else {
268 with( env ) eval( env.forAdd );
269 }
270 with( env ) env.intoBlk = eval( env.forCond );
271 break;
272 //I 制御 順次
273 default:
274 var script = "";
275 if( env.object.declaration == "function" ) {
276 script += "env.parentEnv.";
277 script += env.object.instruction + " = function( ";
278 script += env.object.argtext + " ) { ";
279 script += env.object.blocktext + " }";
280 env.intoBlk = false;
281 } else if( env.object.declaration == "var" ) {
282 script += "env.parentEnv.";
283 script += env.object.instruction;
284 if( env.object.argtext == null ) {
285 script += " = null";
286 } else {
287 script += env.object.argtext;
288 }
289 env.intoBlk = false;
290 } else if( env.object.instruction != null ) {
291 script += env.object.instruction;
292 if( env.object.instruction.substr( -1 ) == "=" ) {
293 // script += "env.parentEnv.";
294 script += env.object.argtext;
295 } else if( env.object.argtext != null ) {
296 script += "(" + env.object.argtext + ")";
297 }
298 // if( env.object.blocktext != null ) script += "{" + env.object.blocktext + "}";
299 } else {
300 //コメント行などの場合はここに来る。
301 break;
302 }
303
304 //宣言や命令文の実行
305 var allWith = "";
306 for( var j = 1; j < envs.length - 1; j++ ) {
307 allWith += "with( envs[ " + j + " ] ) ";
308 }
309 if( env.parentEnv ) allWith += "with( env.parentEnv ) ";
310
311 eval( allWith + script );
312 }//switch
313
314 //check. 子の走査が終了した?
315 while( env.object.elseCancel || ! env.intoBlk || env.counter == env.object.children.length ) {
316 //check. 最上位である?
317 if( envs.length == 0 ) break loop1;
318 //check. forである
319 if( env.object.instruction == "for" && env.intoBlk ) {
320 env.counter = 0;
321 continue loop1;
322 }
323
324 //親へ戻る
325 env = envs.pop();
326 }
327
328 //次の子へ移る
329 envs.push( env );
330 env = {
331 object : env.object.children[ env.counter++ ],
332 next : env.object.children[ env.counter ],
333 counter : 0,
334 intoBlk : true,
335 parentEnv : env,
336 }
337
338 }//loop1:while
339
340 };//func()
341
342
343 func();
344
345 };
346 function escapeBrackets( text, startBracket, endBracket, identifier, swDelBracket ) {
347 /*
348 テキスト中の括弧を配列へ退避する。
349 処理結果は複数あるので、オブジェクトを返り値として返している。
350
351 usage: var res = escapeBrackets( text, "(", ")", "_kakko_#_" );
352 usage: var res = escapeBrackets( text, "{", "}", "_block_#_", true );
353
354 引数:
355 text 処理したいテキスト
356 startBracket 開始括弧文字指定
357 endBracket 終了括弧文字指定
358 identifier テキスト中の括弧でくくられた部分をこの識別子に置き換える。
359 識別子文字列内の#は通し番号に置き換わる。
360 swDelBracket 括弧でくくられた部分を識別子に置き換えた際、括弧を削除するか。
361 たとえば、
362 if( true ) { .. }
363 のとき、()と{}を処理するとして、
364 {}についてはswDelBracketをtrueにして処理すると、
365 if( _kakko_0 ) {_block_0_}
366 にはならず、
367 if( _kakko_0 ) _block_0_
368 のようになる。
369 つまり、ifの条件部分の括弧()は消したくないが、{}は消したいときに利用する。
370 返り値:
371 res.text
372 引数のtextを処理した結果。
373 text中の括弧でくくられた部分は identifier に置き換わっている。
374 res.contents
375 text中の括弧でくくられた部分を収めた配列。
376 結果のテキスト内の identifier 部分にあたるテキストを収めている。
377 たとえば、
378 res.contents[ 0 ] はテキスト中の1個目の括弧内、
379 res.contents[ 1 ] はテキスト中の2個目の括弧内となる。
380 */
381 var res = {
382 text : "",
383 contents : new Array()
384 };
385
386 var level = 0;
387 var buffer = "";
388 var tail = text;
389 var bracketSearchKey = "[" + startBracket + endBracket + "]";
390
391 while( tail.match( bracketSearchKey ) ) {
392 var bracket = RegExp.lastMatch;
393 var left = RegExp.leftContext;
394 tail = RegExp.rightContext;
395
396 //括弧内の場合は取得し、括弧外(level==0)のときは結果のテキストへ
397 if( level > 0 )
398 buffer += left;
399 else
400 res.text += left;
401
402 //括弧閉じである
403 if( bracket == endBracket ) {
404 level --;
405 //check. 対応する括弧であるとき
406 if( level == 0 ) {
407 var id = identifier.replace( "#", res.contents.length );
408 res.text += id;
409 res.contents.push( buffer );
410 buffer = "";
411 }
412 }
413
414 //括弧そのものは、外側の他の括弧内の場合は取得し、
415 //括弧外(level==0)のときは削除指定でなければ結果のテキストへ
416 if( level > 0 )
417 buffer += bracket;
418 else if( ! swDelBracket )
419 res.text += bracket;
420
421 //括弧開始である
422 if( bracket == startBracket ) {
423 level ++;
424 }
425 }
426 res.text += tail;
427
428 return res;
429 }
430 console.log( "batch.js is loaded." );