UNPKG

84 kBJavaScriptView Raw
1/*global require, module */
2/*jslint evil:true*/
3
4var EvW = require('event-when');
5var commonmark = require('commonmark');
6require('string.fromcodepoint');
7
8
9var apply = function (instance, obj) {
10 var meth, i, n;
11
12 for (meth in obj) {
13 n = obj[meth].length;
14 for (i = 0; i < n; i += 1) {
15 instance[meth].apply(instance, obj[meth][i]);
16 }
17
18 }
19};
20
21
22var Folder = function (actions) {
23 actions = actions || Folder.actions;
24
25 var gcd = this.gcd = new EvW();
26 //.when will preserve initial, not emitted order
27 gcd.initialOrdering = true;
28
29 this.docs = {};
30 this.scopes = { g:{} };
31
32 this.commands = Object.create(Folder.commands);
33 this.directives = Object.create(Folder.directives);
34 this.subCommands = Object.create(Folder.subCommands);
35 this.reports = {};
36 this.recording = {};
37 this.stack = {};
38 this.reporters = Folder.reporters;
39 this.plugins = Object.create(Folder.plugins);
40 this.flags = {};
41 this.Folder = Folder;
42
43 this.done = {
44 gcd : new EvW(),
45 cache : {}
46 };
47 this.done.gcd.action("done", function (data, evObj) {
48 var folder = this;
49 folder.done.cache[evObj.ev] = true;
50 }, this);
51
52 gcd.parent = this;
53
54 gcd.on("block needs compiling", "compiling block");
55
56 gcd.action("compiling block", function (data, evObj) {
57 var gcd = evObj.emitter;
58 var file = evObj.pieces[1];
59 var blockname = evObj.pieces[0];
60 var doc = gcd.parent.docs[file];
61 var block = doc.blocks[blockname];
62 doc.blockCompiling(block, file, blockname);
63 }
64 );
65
66
67 gcd.on("heading found", "add block");
68
69 gcd.action("add block", function (data, evObj) {
70 var gcd = evObj.emitter;
71 var file = evObj.pieces[0];
72 var doc = gcd.parent.docs[file];
73 var text = data.trim().toLowerCase();
74 var curname = doc.heading = doc.curname = text;
75 doc.levels[0] = text;
76 doc.levels[1] = '';
77 doc.levels[2] = '';
78 if ( ! doc.blocks.hasOwnProperty(curname) ) {
79 doc.blocks[curname] = '';
80 }
81 }
82 );
83
84 gcd.on("heading found:5", "add slashed block");
85
86 gcd.action("add slashed block", function (data, evObj) {
87 var gcd = evObj.emitter;
88 var file = evObj.pieces[0];
89 var doc = gcd.parent.docs[file];
90 var text = data.trim().toLowerCase();
91 doc.levels[1] = text;
92 doc.levels[2] = '';
93 var curname = doc.heading = doc.curname = doc.levels[0]+'/'+text;
94 if ( ! doc.blocks.hasOwnProperty(curname) ) {
95 doc.blocks[curname] = '';
96 }
97 evObj.stop = true;
98 }
99 );
100
101 gcd.on("heading found:6", "add double slashed block");
102
103 gcd.action("add double slashed block", function (data, evObj) {
104 var gcd = evObj.emitter;
105 var file = evObj.pieces[0];
106 var doc = gcd.parent.docs[file];
107 var text = data.trim().toLowerCase();
108 doc.levels[2] = text;
109 var curname = doc.heading = doc.curname = doc.levels[0]+'/'+doc.levels[1]+'/'+text;
110 if ( ! doc.blocks.hasOwnProperty(curname) ) {
111 doc.blocks[curname] = '';
112 }
113 evObj.stop = true;
114 }
115 );
116
117 gcd.on("switch found", "create minor block");
118
119 gcd.action("create minor block", function (data, evObj) {
120 var gcd = evObj.emitter;
121 var file = evObj.pieces[0];
122 var doc = gcd.parent.docs[file];
123 var colon = doc.colon;
124 var text = data[0].trim().toLowerCase();
125
126 var subEmit, textEmit, doneEmit;
127
128 var curname = doc.curname = doc.heading+colon.v+text;
129 if ( ! doc.blocks.hasOwnProperty(curname) ) {
130 doc.blocks[curname] = '';
131 }
132
133
134 var title = data[1];
135 var fname = evObj.pieces[0] + ":" + curname;
136 doneEmit = "text ready:" + fname;
137 var pipename;
138 if (title) { // need piping
139 title = title.trim()+'"';
140 pipename = fname + colon.v + "sp";
141 textEmit = "text ready:" + pipename;
142 subEmit = "switch chain done:" + pipename;
143
144 gcd.when(textEmit, subEmit);
145
146 gcd.once(subEmit, function (data) {
147 var text = data[data.length-1][1] || '';
148 doc.store(curname, text);
149 gcd.emit(doneEmit, text);
150 });
151
152 gcd.flatWhen("minor ready:" + fname, textEmit);
153
154 doc.pipeParsing(title, 0, '"' , pipename, doc.heading,
155 subEmit, textEmit );
156 } else { //just go
157 gcd.once("minor ready:" + fname, function (text) {
158 doc.store(curname, text);
159 });
160 gcd.flatWhen("minor ready:" + fname, "text ready:" + fname);
161
162 }
163 }
164 );
165
166 gcd.on("code block found", "add code block");
167
168 gcd.action("add code block", function (data, evObj) {
169 var gcd = evObj.emitter;
170 var file = evObj.pieces[0];
171 var doc = gcd.parent.docs[file];
172 if (doc.blockOff > 0) { return;}
173 if (doc.blocks[doc.curname]) {
174 doc.blocks[doc.curname] += doc.join + data;
175 } else {
176 doc.blocks[doc.curname] = data;
177 }
178 }
179 );
180
181 gcd.on("code block found:ignore", "ignore code block");
182
183 gcd.action("ignore code block", function (data, evObj) {
184 var gcd = evObj.emitter;
185 evObj.stop = true;
186 }
187 );
188
189 gcd.on("directive found", "process directives");
190
191 gcd.action("process directives", function (data, evObj) {
192 var gcd = evObj.emitter;
193 var file = evObj.pieces[0];
194 var doc = gcd.parent.docs[file];
195 var fun;
196 var directive = evObj.pieces[1];
197 if (directive && (fun = doc.directives[directive] ) ) {
198 fun.call(doc, data);
199 }
200 }
201 );
202
203 gcd.on("parsing done", "list blocks to compile");
204
205 gcd.action("list blocks to compile", function (data, evObj) {
206 var gcd = evObj.emitter;
207 var file = evObj.pieces[0];
208 var doc = gcd.parent.docs[file];
209 var blocks = doc.blocks;
210 var name;
211 for (name in blocks) {
212 gcd.emit("block needs compiling:" + file + ":" + name);
213 }
214 }
215 );
216
217 gcd.on("waiting for", "wait reporting");
218
219 gcd.action("wait reporting", function (data, evObj) {
220 var gcd = evObj.emitter;
221
222 var reports = gcd.parent.reports;
223
224 var evt = data[0];
225 var msg = evObj.pieces.slice(0,-1).reverse().join(":");
226
227
228 reports[msg] = data.slice(1);
229 gcd.once(evt, function () {
230 delete reports[msg];
231 });
232 }
233 );
234
235 if (actions) {
236 apply(gcd, actions);
237 }
238
239 Folder.postInit(this);
240
241 return this;
242};
243
244Folder.prototype.parse = function (doc) {
245 var gcd = doc.gcd;
246 var file = doc.file;
247
248 gcd.when("marked done:"+file, "parsing done:"+file);
249
250 gcd.on("parsing done:"+file, function () {
251 doc.parsed = true;
252 });
253
254
255 var reader = new commonmark.Parser();
256 var parsed = reader.parse(doc.text);
257
258 var walker = parsed.walker();
259 var event, node, entering, htext = false, ltext = false, lang, code;
260 var ind, pipes, middle, title, href, directive; //for links
261
262 while ((event = walker.next())) {
263 node = event.node;
264 entering = event.entering;
265
266 switch (node.type) {
267 case "Text" :
268 if (htext) {
269 htext.push(node.literal);
270 }
271 if (ltext) {
272 ltext.push(node.literal);
273 }
274 break;
275 case "Link" :
276 if (entering) {
277 ltext = [];
278 } else {
279 href = node.destination;
280 title = node.title;
281 ltext = ltext.join('').trim();
282
283 if (title) {
284 title = title.replace(/&#39;/g, "'").replace(/&quot;/g, '"');
285 }
286 if ((!href) && (!title)) {
287 gcd.emit("switch found:"+file, [ltext, ""]);
288 } else if (title[0] === ":") {
289 if ((ltext.indexOf("|") !== -1) || ( ltext.length === 0) ) {
290 gcd.emit("directive found:transform:" + file,
291 { link : ltext,
292 input : title.slice(1),
293 href: href,
294 cur: doc.curname,
295 directive : "transform"
296 }
297 );
298 } else {
299 ind = 0;
300 pipes = title.indexOf("|");
301 if (pipes === -1) {
302 middle = title.slice(ind+1).trim();
303 pipes = '';
304 } else {
305 middle = title.slice(ind+1, pipes).trim();
306 pipes = title.slice(pipes+1).trim();
307 }
308 if (middle) {
309 ltext += "." + middle.toLowerCase();
310 }
311 gcd.emit("switch found:" + file, [ltext,pipes]);
312 }
313 } else if ( (ind = title.indexOf(":")) !== -1) {
314 directive = title.slice(0,ind).trim().toLowerCase();
315 gcd.emit("directive found:" +
316 directive + ":" + file,
317 { link : ltext,
318 input : title.slice(ind+1),
319 href: href,
320 cur: doc.curname,
321 directive : directive
322 }
323 );
324 }
325 ltext = false;
326 }
327 break;
328 case "CodeBlock" :
329 lang = node.info;
330 code = node.literal || '';
331 if (code[code.length -1] === "\n") {
332 code = code.slice(0,-1);
333 }
334 if (lang) {
335 gcd.emit("code block found:"+lang+":"+file, code);
336 } else {
337 gcd.emit("code block found:"+ file, code);
338 }
339 break;
340 case "Header" :
341 if (entering) {
342 htext = [];
343 } else {
344 gcd.emit("heading found:"+node.level+":"+file, htext.join(""));
345 htext = false;
346 }
347 break;
348 }
349
350 // console.log(node.type, node.literal || '', node.destination|| '', node.title|| '', node.info|| '', node.level|| '', node.sourcepos, event.entering);
351 }
352
353 gcd.emit("marked done:" + file);
354};
355
356Folder.prototype.newdoc = function (name, text, actions) {
357 var parent = this;
358
359 var doc = new parent.Doc(name, text, parent, actions);
360
361 try {
362 parent.parse(doc);
363 } catch (e) {
364 doc.log("Markdown parsing error. Last heading seen: " +
365 doc.curname);
366 }
367
368 return doc;
369
370};
371
372Folder.prototype.colon = { v : "\u2AF6",
373 escape : function (text) {
374 return (typeof text === "string") ?
375 text.replace(/:/g, "\u2AF6") : text;
376 },
377 restore : function (text) {
378 return (typeof text === "string") ?
379 text.replace( /[\u2AF6]/g, ":") : text;
380 }
381};
382
383Folder.prototype.createScope = function (name) {
384 var folder = this;
385 var gcd = folder.gcd;
386 var scopes = folder.scopes;
387 var colon = folder.colon;
388
389 name = colon.escape(name);
390
391 if (! scopes.hasOwnProperty(name) ) {
392 scopes[name] = {};
393 gcd.emit("scope created:" + name);
394 gcd.emit("scope exists:" + name);
395 } else if (typeof scopes[name] === "string") {
396 gcd.emit("error:conflict in scope naming:" + name);
397 scopes[name] = {};
398 }
399
400 return scopes[name];
401};
402
403Folder.prototype.join = "\n";
404
405Folder.prototype.log = function (text) { console.log(text); };
406
407Folder.prototype.indicator = "\u2AF6\u2AF6\u2AF6";
408
409var sync = Folder.prototype.wrapSync = function (fun, label) {
410 var temp;
411 if (typeof fun === "string") {
412 temp = fun;
413 fun = label;
414 label = fun;
415 }
416
417 var f = function (input, args, name, command) {
418 var doc = this;
419 var gcd = doc.gcd;
420
421 try {
422 args = doc.argsPrep(args, name, f.subCommands ||
423 doc.subCommands);
424 var out = fun.call(doc, input, args, name);
425 gcd.scope(name, null); // wipes out scope for args
426 gcd.emit("text ready:" + name, out);
427 } catch (e) {
428 doc.log(e);
429 gcd.emit("error:command execution:" + name,
430 [e, e.stack, input, args, command]);
431 }
432 };
433
434 if (label) {
435 f._label = label;
436 }
437
438 return f;
439};
440Folder.sync = function (name, fun) {
441 return (Folder.commands[name] = sync(name, fun));
442};
443
444var async = Folder.prototype.wrapAsync = function (fun, label) {
445 var temp;
446 if (typeof fun === "string") {
447 temp = fun;
448 fun = label;
449 label = fun;
450 }
451 var f = function (input, args, name, command) {
452
453 var doc = this;
454 var gcd = doc.gcd;
455
456 var callback = function (err, data) {
457 if (err) {
458 doc.log(err);
459 gcd.emit("error:command execution:" + name,
460 [err, input, args, command]);
461 } else {
462 gcd.scope(name, null); // wipes out scope for args
463 gcd.emit("text ready:" + name, data);
464 }
465 };
466 args = doc.argsPrep(args, name, f.subCommands || doc.subCommands);
467 fun.call(doc, input, args, callback, name);
468 };
469 if (label) {
470 f._label = label;
471 }
472
473 return f;
474};
475Folder.async = function (name, fun) {
476 return (Folder.commands[name] = async(name, fun));
477};
478
479var dirFactory = Folder.prototype.dirFactory = function (namefactory, handlerfactory, other) {
480
481 return function (state) {
482 var doc = this;
483 var gcd = doc.gcd;
484 var colon = doc.colon;
485
486 state.linkname = colon.escape(state.link);
487 var temp;
488
489 state.start = doc.getBlock(state.href, state.cur);
490
491 temp = doc.midPipes(state.input);
492 state.options = temp[0];
493 state.pipes = temp[1];
494
495 namefactory.call(doc, state);
496
497 handlerfactory.call(doc, state);
498
499 other.call(doc, state);
500
501 doc.pipeDirSetup(state.pipes, state.emitname, state.handler,
502 ( state.start || state.block) );
503
504 var pipeEmitStart = "text ready:" + state.emitname + colon.v + "sp";
505 if (! state.value) {
506 doc.retrieve(state.start, pipeEmitStart);
507 } else {
508 gcd.emit(pipeEmitStart, state.value || "" );
509 }
510 };
511
512
513};
514
515// communication between folders, say for caching read in files
516Folder.fcd = new EvW();
517
518
519Folder.prototype.subnameTransform = function (subname, lname, mainblock) {
520 var colind, first, second, main;
521 var doc = this;
522 var colon = doc.colon;
523
524 if (subname[0] === ":") {
525 if (mainblock) {
526 //console.log(mainblock)
527 } else {
528 colind = lname.indexOf(":");
529 mainblock = lname.slice(colind+1, lname.indexOf(colon.v, colind));
530 }
531 if (subname === ":") {
532 subname = mainblock;
533 } else {
534 subname = mainblock + subname;
535 }
536 return subname;
537 }
538
539 if (subname.slice(0, 6) === "../../" ) {
540 //in a/b/c asking for a
541 if (mainblock) {
542 //console.log(mainblock)
543 } else {
544 colind = lname.indexOf(":");
545 mainblock = lname.slice(colind+1, lname.indexOf(colon.v, colind));
546 }
547 main = mainblock.slice(0, mainblock.indexOf("/"));
548 if ((subname.length > 6) && (subname[6] !== ":") ) {
549 subname = main + "/" + subname.slice(6);
550 } else {
551 subname = main + subname.slice(6);
552 }
553 } else if (subname.slice(0,2) === "./" ) {
554 // in a/b asking for a/b/c using ./c
555 if (mainblock) {
556 //console.log(mainblock)
557 } else {
558 colind = lname.indexOf(":");
559 mainblock = lname.slice(colind+1, lname.indexOf(colon.v, colind));
560 }
561 if (subname[2] === ":" ) {
562 subname = mainblock + subname.slice(2);
563 } else {
564 subname = mainblock + "/" + subname.slice(2);
565 }
566 } else if (subname.slice(0,3) === "../") {
567 //either in a/b or in a/b/c and asking for a or a/b, respectively
568 if (mainblock) {
569 //console.log(mainblock)
570 } else {
571 colind = lname.indexOf(":");
572 mainblock = lname.slice(colind+1, lname.indexOf(colon.v, colind));
573 }
574 first = mainblock.indexOf("/");
575 second = mainblock.indexOf("/", first+1);
576
577 if (second !== -1) {
578 // a/b/c case
579 main = mainblock.slice(0, second);
580 } else {
581 main = mainblock.slice(0, first);
582 }
583
584 if ((subname.length > 3) && (subname[3] !== ":") ) {
585 subname = main + "/" + subname.slice(3);
586 } else {
587 subname = main + subname.slice(3);
588 }
589
590 }
591
592
593 return subname;
594
595};
596
597Folder.postInit = function () {}; //a hook for plugin this modification
598Folder.plugins = {};
599Folder.plugins.npminfo = {
600 deps : { val : function (arr) {return arr.join(",\n");},
601 element : function (str) {
602 var pieces;
603
604 if (str) {
605 pieces = str.trim().split(/\s+/);
606 if (pieces.length === 2) {
607 return '"' + pieces[0].trim() + '"' + " : " + '"^' +
608 pieces[1].trim() + '"';
609 }
610 }
611 },
612 save : "npm dependencies"
613 },
614 dev : { val : function (arr) {return arr.join(",\n");},
615 element : function (str) {
616 var pieces;
617
618 if (str) {
619 pieces = str.trim().split(/\s+/);
620 if (pieces.length === 2) {
621 return '"' + pieces[0].trim() + '"' + " : " + '"^' +
622 pieces[1].trim() + '"';
623 }
624 }
625 },
626 save : "npm dev dependencies"
627 }
628};
629
630Folder.reporters = {
631 save : function (args) {
632 var name = this.recording[args[2]] || args[2];
633 return "NOT SAVED: " + args[0] + " AS REQUESTED BY: " + args[1] +
634 " NEED: " + name;
635 },
636 out : function (args) {
637 var name = this.recording[args[2]] || args[2];
638 return "NOT REPORTED OUT: " + args[0] + " AS REQUESTED BY: " + args[1] +
639 "\nNEED: " + name;
640 },
641 "command defined" : function (args) {
642 var name = this.recording[args[2]] || args[2];
643 return "COMMAND NOT DEFINED: " + args[0] + " AS REQUESTED IN: " + args[1] +
644 "\nNEED: " + name;
645 },
646 "scope exists" : function (data) {
647 if (data[3]) {
648 return "NEED SCOPE: " + data[0] + " FOR RETRIEVING: " + data[2] +
649 " IN FILE: " + data[1];
650 } else {
651 return "NEED SCOPE: " + data[0] + " FOR SAVING: " + data[2] +
652 " IN FILE: " + data[1];
653 }
654 },
655 "text" : function (data) {
656 var hint = this.recording[data[0]];
657 var parts = data[0].split(":").reverse();
658 var block = parts[0].split(this.colon.v)[0];
659 if (hint) {
660 return "PROBLEM WITH: " + hint + " IN: " + block +
661 " FIlE: " + parts[1];
662 }
663
664 },
665 "minor" : function (data) {
666 var hint = this.recording[data[0]];
667 var parts = data[0].split(":").reverse();
668 var block = parts[0].split(this.colon.v)[0];
669 if (hint) {
670 return "PROBLEM WITH: " + hint + " IN: " + block +
671 " FIlE: " + parts[1];
672 }
673
674 },
675 "retrieval" : function (data) {
676 return "NEED VAR: " + data[1] + " FROM: " + data[0];
677 },
678 "cmd" : function (data) {
679 var ind = data[1].lastIndexOf(this.colon.v);
680 if (ind === -1) {
681 ind = data[1].length;
682 }
683 var name = data[1].slice(0, ind);
684 var hint = this.recording[name];
685 return "NEED COMMAND: " + data[0] + " FOR: " + hint;
686 }
687
688};
689
690
691Folder.prototype.reportwaits = function () {
692 var report = this.reports;
693 var reporters = this.reporters;
694 var arr, msg, data, temp;
695
696 arr = [];
697
698 for (msg in report) {
699 data = report[msg];
700 if (reporters.hasOwnProperty(data[0]) ) {
701 temp = reporters[data[0]].call(this, data.slice(1) );
702 if (temp) {
703 arr.push(temp);
704 } else {
705 // console.log(msg, data);
706 }
707 }
708 }
709
710 return arr;
711};
712
713Folder.prototype.simpleReport = function () {
714 var folder = this;
715 var recording = folder.recording;
716 var gcd = this.gcd;
717 var key, lname, ret = [], el, pieces;
718 var v = this.colon.v;
719 for (key in gcd.whens) {
720 if (key.slice(0,15) === "stitch fragment") {
721 lname = key.slice(16);
722 ret.push("PROBLEM WITH: " + recording[lname] +
723 " IN: " + lname.slice(lname.indexOf(":")+1,
724 lname.indexOf(v) ) +
725 " FILE: " + lname.slice(0, lname.indexOf(":")));
726 }
727 }
728 for (key in gcd._onces) {
729 el = gcd._onces[key];
730 if ( el[0].slice(0, 15) === "command defined") {
731 pieces = key.split(":");
732 if (pieces.length < 3) {
733 gcd.error("error:simple report:"+ el[1]);
734 return ret;
735 }
736 ret.push("COMMAND REQUESTED: " +
737 pieces[1] +
738 " BUT NOT DEFINED. REQUIRED IN: " +
739 pieces[3].slice(0, pieces[3].indexOf(v)) +
740 " FILE: " + pieces[2] );
741 }
742 }
743 return ret;
744};
745
746Folder.commands = { eval : sync(function ( text, args ) {
747 var doc = this;
748
749 var code = args.shift();
750
751 try {
752 eval(code);
753 return text.toString();
754 } catch (e) {
755 doc.gcd.emit("error:command:eval:", [e, e.stack, code, text]);
756 return e.name + ":" + e.message +"\n" + code + "\n\nACTING ON:\n" +
757 text;
758 }
759}, "eval"),
760 passthru : sync(function (text) {return text;}, "passthru"),
761 sub : function (str, args, name) {
762 var doc = this;
763 var gcd = this.gcd;
764
765 var index = 0, al = args.length, k, keys, obj = {},
766 i, j, old, newstr, indented;
767
768 for (i = 0; i < al; i +=2) {
769 obj[args[i]] = args[i+1];
770 }
771
772 keys = Object.keys(obj).sort(function (a, b) {
773 if ( a.length > b.length) {
774 return -1;
775 } else if (a.length < b.length) {
776 return 1;
777 } else {
778 return 0;
779 } });
780
781
782 k = keys.length;
783 for (j = 0; j < k; j += 1) {
784 index = 0;
785 old = keys[j];
786 newstr = obj.hasOwnProperty(keys[j]) ? obj[keys[j]] : '';
787 while (index < str.length ) {
788 i = str.indexOf(old, index);
789
790 if (i === -1) {
791 break;
792 } else {
793 indented = doc.indent(newstr, doc.getIndent(str, i));
794 str = str.slice(0,i) + indented + str.slice(i+old.length);
795 index = i + indented.length;
796 }
797 }
798 }
799
800 gcd.emit("text ready:" + name, str);
801 },
802 store: sync(function (input, args) {
803 var doc = this;
804
805 var vname = doc.colon.escape(args[0]);
806
807 if (vname) {
808 doc.store(vname, input);
809 }
810 return input;
811 }, "store"),
812 log : sync(function (input, args) {
813 var doc = this;
814 if (args && args.length) {
815 doc.log(input + "\n~~~\n" + args.join("\n~~~\n"));
816 } else {
817 doc.log(input);
818 }
819 return input;
820 }, "log"),
821 async : async(function (text, args, callback) {
822 var doc = this;
823
824 var code = args.shift();
825
826 try {
827 eval(code);
828 } catch (e) {
829 doc.gcd.emit("error:command:async:", [e, e.stack, code, text]);
830 callback( null, e.name + ":" + e.message +"\n" + code +
831 "\n\nACTING ON:\n" + text);
832 }
833 }, "async"),
834 compile : function (input, args, name) {
835 var doc = this;
836 var gcd = doc.gcd;
837 var file = doc.file;
838 var colon = doc.colon.v;
839 var escape = doc.colon.escape;
840 var i, n, start, nextname, oldname, firstname;
841
842 var stripped = name.slice(name.indexOf(":")+1) + colon + "c";
843
844
845 var hanMaker = function (file, nextname, start) {
846 return function (text) {
847 doc.blockCompiling(text, file, nextname, start);
848 };
849 };
850
851
852 if (args.length === 0) {
853 gcd.once("minor ready:" + name + colon + "c", function (text) {
854 gcd.emit("text ready:" + name, text);
855 });
856 doc.blockCompiling(input, file, stripped);
857 } else {
858 n = args.length;
859 firstname = oldname = escape(stripped + colon + ( args[0] || '') + colon + 0);
860 for (i = 1; i < n; i += 1) {
861 start = args[i] || '';
862 nextname = escape(stripped + colon + start + colon + i);
863 gcd.once("minor ready:" + file + ":" + oldname, hanMaker(file,
864 nextname, start) );
865 gcd.emit("waiting for:cmd compiling:" + nextname + ":from:" + doc.file,
866 ["minor ready:" + file + ":" + nextname, "compile", nextname, doc.file, start]);
867 oldname = nextname;
868 }
869 start = args[0] || '';
870 gcd.once("minor ready:" + file + ":" + oldname, function (text) {
871 gcd.emit("text ready:" + name, text);
872 });
873 doc.blockCompiling(input, file, firstname, start);
874 }
875 },
876 raw : sync(function (input, args) {
877 var doc = this;
878 var start, end, text;
879 var gcd = doc.gcd;
880
881 var file = doc.parent.docs[args[2]] || doc;
882
883 if (file) {
884 text = file.text;
885 start = args[0].trim() + "\n";
886 start = text.indexOf(start)+start.length;
887 end = "\n" + args[1].trim();
888 end = text.indexOf(args[1], start);
889 return text.slice(start, end);
890 } else {
891 gcd.emit("error:raw:" + doc.file, args);
892 return '';
893 }
894
895
896 }, "raw"),
897 trim : sync(function (input) {
898 return input.trim();
899 }, "trim"),
900 join : sync(function (input, args) {
901 var sep = args.shift() || '';
902 if (input) {
903 args.unshift(input);
904 }
905 return args.join(sep);
906 }, "join"),
907 cat : sync(function (input, args) {
908 var sep = '';
909 if (input) {
910 args.unshift(input);
911 }
912 return args.join(sep);
913 }, "cat"),
914 push : sync(function (input, args, name) {
915 var folder = this.parent;
916 var stack = folder.stack;
917 var cmdpipe = name.slice(0, name.lastIndexOf(folder.colon.v));
918 if (stack.hasOwnProperty(cmdpipe)) {
919 stack[cmdpipe].push(input);
920 } else {
921 stack[cmdpipe] = [input];
922 }
923 return input;
924 }, "push"),
925 pop : sync(function (input, args, name) {
926 var gcd = this.gcd;
927 var folder = this.parent;
928 var stack = folder.stack;
929 var cmdpipe = name.slice(0, name.lastIndexOf(folder.colon.v));
930 var ret;
931 if (stack.hasOwnProperty(cmdpipe)) {
932 ret = stack[cmdpipe].pop();
933 if (stack[cmdpipe].length === 0) {
934 delete stack[cmdpipe];
935 }
936 } else {
937 gcd.emit("error:pop found nothing to pop:"+name);
938 ret = input;
939 }
940 return ret;
941 }, "pop"),
942 "if" : function (input, args, name) {
943 var doc = this;
944 var gcd = doc.gcd;
945 var flag = args[0];
946
947 if (doc.parent.flags.hasOwnProperty(flag) ) {
948 doc.commands[args[1]].call(doc, input, args.slice(2), name);
949 } else {
950 gcd.emit("text ready:" + name, input);
951 }
952
953
954 },
955 "done" : function (input, args, name) {
956 var gcd = this.gcd;
957 this.parent.done.gcd.emit(args[0]);
958 gcd.emit("text ready:" + name, input);
959 },
960 "when" : function (input, args, name) {
961 var folder = this.parent;
962 var gcd = this.gcd;
963 var done = folder.done;
964 var cache = done.cache;
965 var when = [];
966
967 var i, n = args.length;
968 for (i = 0; i < n; i +=1) {
969 if (! cache[args[i]]) {
970 when.push(args[i]);
971 }
972 }
973 if (when.length > 0) {
974 done.gcd.once("ready to send:" + name, function () {
975 gcd.emit("text ready:" + name, input);
976 });
977 done.gcd.when(when, "ready to send:" + name);
978 } else {
979 gcd.emit("text ready:" + name, input);
980 }
981 }
982};
983
984Folder.directives = {
985 "save" : dirFactory(function (state) {
986 state.emitname = "for save:" + this.file + ":" + state.linkname;
987 }, function (state) {
988 var doc = this;
989 var gcd = this.gcd;
990 var linkname = state.linkname;
991
992 var f = function (data) {
993 if (data[data.length-1] !== "\n") {
994 data += "\n";
995 }
996 gcd.emit("file ready:" + linkname, data);
997 };
998 f._label = "save;;" + linkname;
999
1000 state.handler = f;
1001
1002 }, function (state) {
1003 var file = this.file;
1004 var gcd = this.gcd;
1005 var linkname = state.linkname;
1006 var options = state.options;
1007 var start = state.start;
1008 // es6 var {linkname, options, start} = state;
1009
1010 gcd.scope(linkname, options);
1011
1012 gcd.emit("waiting for:saving file:" + linkname + ":from:" + file,
1013 ["file ready:" + linkname, "save", linkname, file, start]);
1014
1015 }),
1016 "new scope" : function (args) {
1017 var doc = this;
1018 var scopename = args.link;
1019
1020 doc.parent.createScope(scopename);
1021
1022 },
1023 "store" : dirFactory(function (state) {
1024 var linkname = state.linkname;
1025
1026 state.emitname = "for store:" + this.file + ":" + linkname;
1027 }, function (state) {
1028 var doc = this;
1029 var gcd = this.gcd;
1030 var linkname = state.linkname;
1031
1032 var f = function (data) {
1033 doc.store(linkname, data);
1034 };
1035 f._label = "storeDir;;" + linkname;
1036
1037 state.handler = f;
1038
1039 }, function (state) {
1040
1041
1042 if (state.options) {
1043 state.block = state.start;
1044 state.start = '';
1045 state.value = state.options;
1046 }
1047 }),
1048 "log" : function (args) {
1049
1050 var doc = this;
1051 var gcd = doc.gcd;
1052
1053 var str = args.link;
1054 var i;
1055 while ( (i = str.indexOf("\\:") ) !== -1 ) {
1056 str = str.slice(0, i) + doc.colon.v + str.slice(i+2);
1057 }
1058
1059 str = str || doc.colon.escape(args.cur);
1060
1061 gcd.monitor(str, function (ev, data) {
1062 doc.log("EVENT: " + ev + " DATA: " + data);
1063 });
1064
1065 },
1066 "out" : dirFactory(function (state) {
1067 state.emitname = "for out:" + this.file + ":" + this.colon.escape(state.linkname);
1068 }, function (state) {
1069 var doc = this;
1070 var gcd = doc.gcd;
1071 var linkname = state.linkname;
1072 var emitname = state.emitname;
1073
1074
1075 var f = function (data) {
1076 gcd.emit(emitname, data);
1077 doc.log(linkname + ":\n" + data + "\n~~~\n");
1078 };
1079
1080 f._label = "out;;" + linkname;
1081
1082 state.handler = f;
1083
1084 }, function (state) {
1085 var gcd = this.gcd;
1086 var linkname = state.linkname;
1087 var emitname = state.emitname;
1088 var start = state.start;
1089 var options = state.options;
1090
1091 gcd.scope(linkname, options);
1092
1093 gcd.emit("waiting for:dumping out:" + linkname,
1094 [emitname, linkname, this.file, start] );
1095 }),
1096 "load" : function (args) {
1097 var doc = this;
1098 var gcd = doc.gcd;
1099 var folder = doc.parent;
1100 var url = args.href.trim();
1101 var options = args.input;
1102 var urlesc = folder.colon.escape(url);
1103 var nickname = doc.colon.escape(args.link.trim());
1104
1105 gcd.scope(urlesc, options);
1106
1107 if (nickname && (nickname !== urlesc) ) {
1108 doc.createLinkedScope(urlesc, nickname);
1109 if (!(folder.docs.hasOwnProperty(urlesc) ) ) {
1110 gcd.emit("waiting for:loading for:" + doc.file,
1111 "need document:" + urlesc);
1112 gcd.emit("need document:" + urlesc, url );
1113 }
1114 } else {
1115 if (!(folder.docs.hasOwnProperty(urlesc) ) ) {
1116 gcd.emit("waiting for:loading for:" + doc.file,
1117 "need document:" + urlesc);
1118 gcd.emit("need document:" + urlesc, url );
1119 }
1120 }
1121
1122 },
1123 "link scope" : function (args) {
1124 var doc = this;
1125 var alias = args.link;
1126 var scopename = args.input;
1127
1128 doc.createLinkedScope(scopename, alias);
1129
1130 },
1131 "transform" : dirFactory(function (state) {
1132 state.name = this.colon.escape(state.start + ":" + state.input);
1133 state.emitname = "for transform:" + this.file + ":" + state.name;
1134 }, function (state) {
1135 var doc = this;
1136 var gcd = doc.gcd;
1137
1138
1139 var f = function (data) {
1140 gcd.emit(state.emitname, data);
1141 var name = doc.getPostPipeName(state.linkname);
1142 if (name) {
1143 doc.store(name, data);
1144 }
1145 };
1146 f._label = "transform;;" + state.name;
1147
1148 state.handler = f;
1149 }, function (state) {
1150 var doc = this;
1151 var gcd = this.gcd;
1152 var name = state.name;
1153 var start = state.start;
1154 var emitname = state.emitname;
1155
1156 gcd.emit("waiting for:transforming:" + name,
1157 [emitname, name, doc.file, start] );
1158 }),
1159 define : dirFactory(function (state) {
1160 state.emitname = "cmddefine:" + state.linkname;
1161 }, function (state) {
1162 var cmdname = state.linkname;
1163 var doc = this;
1164 var gcd = this.gcd;
1165
1166 var han = function (block) {
1167 var f;
1168
1169 try {
1170 block = "f="+block;
1171 eval( block);
1172 } catch (e) {
1173 doc.gcd.emit("error:define:"+cmdname, [e, block]);
1174 doc.log(e.name + ":" + e.message +"\n" + block);
1175 return;
1176 }
1177
1178 switch (state.options) {
1179 case "raw" : f._label = cmdname;
1180 doc.commands[cmdname] = f;
1181 break;
1182 case "async" : doc.commands[cmdname] =
1183 doc.wrapAsync(f, cmdname);
1184 break;
1185 default : doc.commands[cmdname] =
1186 doc.wrapSync(f, cmdname);
1187 }
1188
1189 gcd.emit("command defined:" + cmdname);
1190 };
1191 han._label = "cmd define;;" + cmdname;
1192
1193 state.handler = han;
1194
1195 }, function (state) {
1196 var cmdname = state.linkname;
1197
1198 var file = this.file;
1199 var gcd = this.gcd;
1200
1201 gcd.emit("waiting for:command definition:" + cmdname,
1202 ["command defined:"+cmdname, cmdname, file, state.start] );
1203
1204 }),
1205 "block" : function (args) {
1206 var doc = this;
1207
1208 if (args.link === "off") {
1209 doc.blockOff += 1;
1210 } else if (args.link === "on") {
1211 if (doc.blockOff > 0 ) {
1212 doc.blockOff -= 1;
1213 }
1214 } else {
1215 doc.log("block directive found, but the toggle was not understood: " +
1216 args.link + " It should be either on or off");
1217 }
1218
1219 },
1220 "ignore" : function (args) {
1221 var lang = args.link;
1222
1223 var doc = this;
1224 var gcd = doc.gcd;
1225
1226 gcd.on("code block found:" + lang, "ignore code block");
1227
1228 },
1229 "eval" : function (args) {
1230 var doc = this;
1231
1232 var block = doc.blocks[args.cur];
1233
1234 var storageName = doc.getPostPipeName(args.link);
1235 var ret = '';
1236
1237 try {
1238 eval(block);
1239 if (storageName) {
1240 doc.store(storageName, ret);
1241 }
1242 } catch (e) {
1243 doc.gcd.emit("error:dir eval:", [e, block]);
1244 doc.log(e.name + ":" + e.message +"\n" + block);
1245 return;
1246 }
1247
1248 },
1249 "if" : function (args) {
1250
1251 var doc = this;
1252 var folder = doc.parent;
1253 var gcd = doc.gcd;
1254
1255 var title = args.input;
1256 var ind = title.indexOf(";");
1257 var flag = title.slice(0, ind).trim();
1258 var directive, semi, fun;
1259
1260 if (folder.flags.hasOwnProperty(flag) ) {
1261 semi = title.indexOf(":", ind);
1262 directive = title.slice(ind+1, semi).trim();
1263 args.directive = directive;
1264 args.input = title.slice(semi+1).trim();
1265
1266 if (directive && (fun = doc.directives[directive] ) ) {
1267 fun.call(doc, args);
1268 }
1269 }
1270 },
1271 "flag" : function (args) {
1272 this.parent.flags[args.link.trim()] = true;
1273
1274 },
1275 "version" : function (args) {
1276 var doc = this;
1277 var colon = doc.colon;
1278
1279 var ind = args.input.indexOf(";");
1280 if (ind === -1) { ind = args.input.length +1; }
1281
1282 doc.store(colon.escape("g::docname"),
1283 args.link.trim());
1284 doc.store(colon.escape("g::docversion"),
1285 args.input.slice(0, ind).trim());
1286 doc.store(colon.escape("g::tagline"),
1287 (args.input.slice(ind+1).trim() || "Tagline needed" ) );
1288
1289 },
1290 "npminfo" : function self (args) {
1291 var doc = this;
1292 var g = "g" + doc.colon.v + doc.colon.v;
1293
1294 var types = doc.plugins.npminfo;
1295
1296 doc.store(g+"authorname", args.link);
1297
1298 var gituser = args.href.slice(args.href.lastIndexOf("/")+1).trim();
1299 doc.store(g+"gituser", gituser);
1300
1301 var pieces = args.input.split(";");
1302
1303 doc.store(g + "authoremail", (pieces.shift() || '').trim());
1304
1305 pieces.forEach(function (el) {
1306 if (!el) {return;}
1307
1308 var ret = [];
1309
1310 var ind = el.indexOf(":");
1311 var prekind = el.slice(0, ind).trim();
1312 var kind = types[prekind];
1313 if (!kind) {
1314 doc.log("unrecognized type in npminfo:" + prekind );
1315 return;
1316 }
1317 var entries = el.slice(ind+1).split(",");
1318 entries.forEach(function(el) {
1319 if (!el) {return;}
1320 var bits = kind.element(el);
1321 if (bits) {
1322 ret.push(bits);
1323 }
1324 });
1325 doc.store(g + kind.save, kind.val(ret) );
1326 });
1327
1328 doc.store(g + "year", ( new Date() ).getFullYear().toString() );
1329 },
1330};
1331
1332Folder.subCommands = (function () {
1333 var ret = {};
1334
1335 ret.echo = ret.e = function () {
1336 var arr = Array.prototype.slice.call(arguments);
1337
1338 var ret = arr.map(function (str) {
1339 if (("\"'`".indexOf(str[0]) !== -1) &&
1340 (str[0] === str[str.length-1]) ) {
1341
1342 return str.slice(1, -1);
1343 } else {
1344 return str;
1345 }
1346 });
1347
1348 ret.args = true;
1349
1350 return ret;
1351 };
1352
1353 ret.join = ret.j = function (sep) {
1354 var args = Array.prototype.slice.call(arguments, 1);
1355 var ret = [];
1356
1357 args.forEach( function (el) {
1358 if ( Array.isArray(el)) {
1359 ret.push(el.join(sep));
1360 } else {
1361 ret.push(el);
1362 }
1363 });
1364
1365 return ret.join(sep);
1366
1367 };
1368
1369 ret.array = ret.arr = ret.a = function () {
1370 return Array.prototype.slice.call(arguments, 0);
1371 };
1372
1373 ret.object = ret.obj = ret.o = function (str) {
1374 var ret, doc = this;
1375 try {
1376 ret = JSON.parse(str);
1377 if (Array.isArray(ret) ) {
1378 return ["val", ret];
1379 } else {
1380 return ret;
1381 }
1382 } catch (e) {
1383 doc.gcd.emit("error:arg prepping:bad json parse:" + this.cmdname,
1384 [e, e.stack, str]);
1385 return ["error", e];
1386 }
1387 };
1388
1389 ret.merge = function (a) {
1390 var ret, args;
1391 if (Array.isArray(a) ) {
1392 args = Array.prototype.slice.call(arguments, 1);
1393 return Array.prototype.concat.apply(a, args);
1394 } else {
1395 args = Array.prototype.slice.call(arguments, 1);
1396 ret = a;
1397 args.forEach( function (el) {
1398 var key;
1399 for (key in el) {
1400 ret[key] = el[key];
1401 }
1402 });
1403 return ret;
1404 }
1405 };
1406
1407 ret["key-value"] = ret.kv = function () {
1408 var ret = {};
1409 var i, n = arguments.length;
1410 for (i = 0; i < n; i += 2) {
1411 ret[arguments[i]] = arguments[i+1];
1412 }
1413
1414 return ret;
1415 };
1416
1417 ret.act = function (obj, method) {
1418 try {
1419 return obj[method].apply(obj,
1420 Array.prototype.slice.call(arguments, 2)) ;
1421 } catch (e) {
1422 this.gcd.emit("error:arg prepping:bad method:" + this.cmdname,
1423 [e, e.stack, obj, method,
1424 Array.prototype.slice.call(arguments)]);
1425 return ;
1426 }
1427 };
1428
1429 ret.property = ret.prop = function () {
1430 var props = Array.prototype.slice.call(arguments, 0);
1431 var obj;
1432 try {
1433 obj = props.reduce(function (prev, cur) {
1434 return prev[cur];
1435 });
1436 return obj;
1437 } catch (e) {
1438 this.gcd.emit("error:bad property access:" +
1439 this.cmdname, [e, e.stack, props]);
1440 return;
1441 }
1442 };
1443
1444 ret.json = function (obj) {
1445 var doc = this;
1446 try {
1447 return JSON.stringify(obj);
1448 } catch (e) {
1449 this.gcd.emit("error:arg prepping:bad json:" + this.cmdname,
1450 [e, e.stack, obj]);
1451 return ;
1452 }
1453 };
1454
1455 ret.set = function (obj, retType) {
1456 var doc = this;
1457 var gcd = doc.gcd;
1458 var name = doc.cmdName;
1459 var scope, key;
1460
1461 scope = gcd.scope(name);
1462 if (!scope) {
1463 scope = {};
1464 gcd.scope(name, scope);
1465 }
1466 for (key in obj) {
1467 scope[key] = obj[key];
1468 }
1469 if (retType === "pass" ) {
1470 return obj;
1471 } else {
1472 return ;
1473 }
1474 };
1475
1476 ret.gset = function (obj, retType) {
1477 var doc = this;
1478 var gcd = doc.gcd;
1479 var name = doc.cmdName.slice(0, doc.cmdName.lastIndexOf(doc.colon.v)) ;
1480 var scope, key;
1481
1482 scope = gcd.scope(name);
1483 if (!scope) {
1484 scope = {};
1485 gcd.scope(name, scope);
1486 }
1487 for (key in obj) {
1488 scope[key] = obj[key];
1489 }
1490 if (retType === "pass" ) {
1491 return obj;
1492 } else {
1493 return ;
1494 }
1495 } ;
1496
1497 ret.get = function () {
1498 var doc = this;
1499 var gcd = doc.gcd;
1500 var name = doc.cmdName;
1501 var scope;
1502
1503 scope = gcd.scope(name);
1504 if (!scope) {
1505 gcd.emit("error:arg prepping:no scope:" + name);
1506 return ;
1507 }
1508
1509 var i, n = arguments.length;
1510 var ret = [];
1511 for (i = 0; i < n; i +=1 ) {
1512 ret.push(scope[arguments[i]]);
1513 }
1514 ret.args = true; // each is separate
1515 return ret;
1516 };
1517
1518 ret.gget = function () {
1519 var doc = this;
1520 var gcd = doc.gcd;
1521 var name = doc.cmdName.slice(0, doc.cmdName.lastIndexOf(doc.colon.v)) ;
1522 var scope;
1523
1524 scope = gcd.scope(name);
1525 if (!scope) {
1526 gcd.emit("error:arg prepping:no scope:" + name);
1527 return ;
1528 }
1529
1530 var i, n = arguments.length;
1531 var ret = [];
1532 for (i = 0; i < n; i +=1 ) {
1533 ret.push(scope[arguments[i]]);
1534 }
1535 ret.args = true; // each is separate
1536 return ret;
1537 } ;
1538
1539 ret.arguments = ret.args = function (arr) {
1540 var ret = arr.slice(0); //make a shallow copy
1541 ret.args = true;
1542 return ret;
1543 };
1544
1545 ret.number = ret.n = ret["#"] = function () {
1546 var ret = [], i, n = arguments.length;
1547 for (i = 0; i < n; i += 1) {
1548 ret.push(Number(arguments[i]));
1549 }
1550 ret.args = true;
1551 return ret;
1552 };
1553
1554 ret.eval = function (code) {
1555 var ret, doc = this;
1556 var args = Array.prototype.slice.call(arguments, 1);
1557
1558 if ( (code[0] === "`" ) && (code[code.length-1] === code[0]) ) {
1559 code = code.slice(1, code.length-1);
1560 }
1561
1562 try {
1563 eval(code);
1564 return ret;
1565 } catch (e) {
1566 this.gcd.emit("error:arg prepping:bad eval:" + this.cmdname,
1567 [e, e.stack, code]);
1568 return;
1569 }
1570
1571 };
1572
1573 ret.log = function () {
1574 var doc = this, name = doc.cmdName;
1575 var args = Array.prototype.slice.call(arguments);
1576 doc.log("arguments in " + name + ":\n---\n" +
1577 args.join("\n~~~\n") + "\n---\n");
1578 return args;
1579 };
1580
1581 ret.true = ret.t = function () {return true;};
1582 ret.false = ret.f = function () {return false;};
1583 ret.null = function () {return null;};
1584 ret.doc = function () {return this;};
1585 ret.skip = function () {return ;};
1586
1587 return ret;
1588
1589})();
1590
1591
1592var Doc = Folder.prototype.Doc = function (file, text, parent, actions) {
1593 this.parent = parent;
1594 var gcd = this.gcd = parent.gcd;
1595
1596 this.file = file; // globally unique name for this doc
1597
1598 parent.docs[file] = this;
1599
1600 this.text = text;
1601
1602 this.blockOff = 0;
1603
1604 this.levels = {};
1605 this.blocks = {'' : ''}; //an empty initial block in case of headless
1606 this.heading = this.curname = '';
1607 this.levels[0] = text;
1608 this.levels[1] = '';
1609 this.levels[2] = '';
1610
1611
1612 this.vars = parent.createScope(file);
1613
1614 this.commands = parent.commands;
1615 this.directives = parent.directives;
1616 this.subCommands = parent.subCommands;
1617 this.colon = Object.create(parent.colon);
1618 this.join = parent.join;
1619 this.log = this.parent.log;
1620 this.scopes = this.parent.scopes;
1621 this.subnameTransform = this.parent.subnameTransform;
1622 this.indicator = this.parent.indicator;
1623 this.wrapAsync = parent.wrapAsync;
1624 this.wrapSync = parent.wrapSync;
1625 this.sync = Folder.sync;
1626 this.async = Folder.async;
1627 this.dirFactory = parent.dirFactory;
1628 this.plugins = Object.create(parent.plugins);
1629
1630 if (actions) {
1631 apply(gcd, actions);
1632 }
1633
1634 return this;
1635
1636};
1637
1638var dp = Doc.prototype;
1639
1640dp.retrieve = function (name, cb) {
1641 var doc = this;
1642 var gcd = doc.gcd;
1643
1644 var scope = doc.getScope(name);
1645
1646
1647 var varname = scope[1];
1648 var file = scope[2];
1649 scope = scope[0];
1650 var f;
1651 if (scope) {
1652 if (scope.hasOwnProperty(varname) ) {
1653 if (typeof cb === "function") {
1654 cb(scope[varname]);
1655 } else if (typeof cb === "string") {
1656 gcd.emit(cb, scope[varname]);
1657 } else {
1658 gcd.emit("error:unrecognized callback type:" +
1659 doc.file + ":" + name, (typeof cb) );
1660 }
1661 return ;
1662 } else {
1663 gcd.emit("waiting for:retrieval:" + doc.file,
1664 ["text stored:" + file + ":" + varname, "retrieval", file, varname]);
1665 f = function () {
1666 doc.retrieve(name, cb);
1667 };
1668 f._label = "Retrieving:" + file + ":" + varname;
1669 gcd.once("text stored:" + file + ":" + varname, f);
1670 return ;
1671 }
1672 } else {
1673 gcd.emit("waiting for:retrieval:" + cb+ "need:" + name,
1674 ["scope exists:" + file, "scope exists", file, doc.file, varname,
1675 true]);
1676 f = function () {
1677 doc.retrieve(name, cb);
1678 };
1679 f._label = "Retrieving:" + doc.file + ":" + name;
1680 gcd.once("scope exists:" + file, f);
1681 return ;
1682 }
1683};
1684
1685dp.getScope = function (name) {
1686 var ind, scope, alias, scopename, varname;
1687 var doc = this;
1688 var colon = doc.colon;
1689 var folder = doc.parent;
1690
1691 if ( (ind = name.indexOf( colon.v + colon.v) ) !== -1 ) {
1692 alias = name.slice(0,ind);
1693 varname = name.slice(ind+2);
1694 scopename = doc.scopes[ alias ];
1695 if (typeof scopename === "string") {
1696 while ( typeof (scope = folder.scopes[scopename]) === "string") {
1697 scopename = scope;
1698 }
1699 if (scope) {
1700 return [scope, varname, scopename];
1701 } else { //this should never happen
1702 doc.gcd.emit("error:non-existent scope linked:" +
1703 alias, scopename);
1704 }
1705 } else if (scopename) { //object -- alias is scope's name
1706 return [scopename, varname, alias];
1707 } else { // not defined yet
1708 return [null, varname, alias];
1709 }
1710 } else { //doc's scope is being requested
1711 return [doc.vars, name, doc.file];
1712 }
1713};
1714
1715dp.createLinkedScope = function (name, alias) {
1716 var doc = this;
1717 var gcd = doc.gcd;
1718 var folder = doc.parent;
1719 var scopes = folder.scopes;
1720 var colon = doc.colon;
1721
1722 name = colon.escape(name);
1723 alias = colon.escape(alias);
1724
1725 if (scopes.hasOwnProperty(alias) ) {
1726 if (scopes[alias] !== name ) {
1727 gcd.emit("error:conflict in scope naming:" +
1728 doc.file, [alias, name] );
1729 }
1730 } else {
1731 if ( scopes.hasOwnProperty(name) ) {
1732 folder.scopes[alias] = name;
1733 gcd.emit("scope linked:" + doc.file + ":" + alias, name);
1734 gcd.emit("scope exists:" + alias);
1735 } else {
1736 gcd.once("scope exists:" + name, function () {
1737 folder.scopes[alias] = name;
1738 gcd.emit("scope linked:" + doc.file + ":" + alias, name);
1739 gcd.emit("scope exists:" + alias);
1740 });
1741 }
1742 }
1743
1744
1745};
1746
1747dp.indent = function (text, indent, gcd) {
1748 var line, ret;
1749 var i, n;
1750
1751 n = indent;
1752 line = '';
1753 for (i = 0; i <n; i += 1) {
1754 line += ' ';
1755 }
1756
1757 if (typeof text !== "string") {
1758 gcd.emit("error:indent does not see a text item", text);
1759 return ret;
1760 }
1761
1762 ret = text.replace(/\n/g, "\n"+line);
1763 return ret;
1764};
1765
1766dp.getIndent = function ( block, place ) {
1767 var first, backcount, indent, chr;
1768 first = place;
1769 backcount = place-1;
1770 indent = 0;
1771 while (true) {
1772 if ( (backcount < 0) || ( (chr = block[backcount]) === "\n" ) ) {
1773 indent = first - ( backcount + 1 );
1774 break;
1775 }
1776 if (chr.search(/\S/) === 0) {
1777 first = backcount;
1778 }
1779 backcount -= 1;
1780 }
1781 return indent;
1782};
1783
1784dp.blockCompiling = function (block, file, bname, mainblock) {
1785 var doc = this;
1786 var gcd = doc.gcd;
1787 var colon = doc.colon;
1788
1789 var quote, place, lname, slashcount, numstr, chr, indent;
1790 var name = file + ":" + bname;
1791 var ind = 0;
1792 var start = 0;
1793 var stitchfrag;
1794 var stitchend = "ready to stitch:" + name;
1795 gcd.when("block substitute parsing done:"+name, stitchend);
1796 var n = block.length;
1797
1798 gcd.once(stitchend, function (data) {
1799
1800 var text = '', insert, i, n = data.length;
1801 var indent = doc.indent;
1802
1803 for (i = 1; i < n; i += 1) {
1804 insert = data[i][1];
1805 if ( (i+1 < n) && ( data[i+1][0].slice(0,6) === "indent") ) {
1806 text += indent(insert, data[i+1][1], gcd);
1807 i += 1;
1808 } else {
1809 text += insert;
1810 }
1811
1812 }
1813
1814 if (bname.indexOf(colon.v) !== -1) {
1815 gcd.emit("minor ready:" + name, text);
1816 } else {
1817 doc.store(bname, text);
1818 gcd.emit("text ready:" + name, text);
1819 }
1820 });
1821
1822 var stitcher = function (start) {
1823 if (start < n) {
1824 stitchfrag = "stitch fragment:" + name + colon.v + start;
1825 gcd.when(stitchfrag, stitchend);
1826 }
1827 };
1828
1829 stitcher(0);
1830
1831 while (ind < n) {
1832 ind = block.indexOf("\u005F", ind);
1833 if (ind === -1) {
1834 gcd.emit(stitchfrag, block.slice(start) );
1835 break;
1836 } else {
1837 ind += 1;
1838
1839 if (block[ind].match(/['"`]/)) {
1840 quote = block[ind];
1841 } else {
1842 continue;
1843 }
1844
1845 place = ind-2;
1846 numstr = '0123456789';
1847 slashcount = '';
1848 chr = block[place];
1849 if (chr === '\\' ) { //escaped underscore; no escaping backslash!
1850 gcd.emit(stitchfrag, block.slice(start, place) + "\u005F" );
1851 start = ind;
1852 stitcher(start);
1853 continue;
1854 } else if ( numstr.indexOf(chr) !== -1 ) {
1855 slashcount += chr;
1856 place -= 1;
1857 chr = block[place];
1858 while ( numstr.indexOf(chr) !== -1 ) {
1859 slashcount += chr;
1860 place -= 1;
1861 chr = block[place];
1862 }
1863 if (chr === '\\') {
1864 slashcount = parseInt(slashcount, 10);
1865 if (slashcount > 0) {
1866 slashcount -= 1;
1867
1868 gcd.emit(stitchfrag, block.slice(start, place) + "\\" +
1869 slashcount + "\u005F");
1870
1871 stitcher(place);
1872 start = doc.findMatchQuote(block, quote, ind+1); //+1 to get past quote
1873 // yes this is supposed to be reversed (from quote to quote,
1874 // start is just beyond quote
1875 gcd.emit(stitchfrag, block.slice(ind, start));
1876
1877 stitcher(start);
1878 ind = start;
1879 continue;
1880 } else {
1881 gcd.emit(stitchfrag, block.slice(start, place));
1882 start = ind-1; // underscore
1883 stitcher(start-2); //to point to where the escape sequence
1884 }
1885 }
1886 }
1887
1888 place = ind-1;
1889 if (start !== place ) { // \0 could have happened
1890 gcd.emit(stitchfrag, block.slice(start, place));
1891 start = place; // underscore
1892 stitcher(start);
1893 }
1894 lname = name + colon.v + start;
1895 gcd.flatWhen("text ready:" + lname, stitchfrag);
1896
1897 if (place > 0) {
1898 indent = doc.getIndent(block, place);
1899 if ( indent > 0 ) {
1900 gcd.when("indent for prior:" + lname, stitchend);
1901 gcd.emit("indent for prior:" + lname, doc.getIndent(block, place));
1902 }
1903 }
1904
1905 start = doc.substituteParsing(block, ind+1, quote, lname,
1906 mainblock);
1907
1908 doc.parent.recording[lname] = block.slice(ind-1, start);
1909
1910 stitcher(start);
1911 ind = start ;
1912
1913 }
1914 }
1915
1916
1917 gcd.emit("block substitute parsing done:"+name);
1918};
1919
1920dp.substituteParsing = function (text, ind, quote, lname, mainblock ) {
1921
1922 var doc = this;
1923 var gcd = doc.gcd;
1924 var colon = doc.colon;
1925
1926 var match, subname, chr, subtext;
1927 var subreg = doc.regexs.subname[quote];
1928
1929 var doneEmit = "text ready:" + lname;
1930 var textEmit = doneEmit + colon.v + ind;
1931 var subEmit = "substitution chain done:" + lname;
1932
1933 gcd.when(textEmit, subEmit);
1934
1935 gcd.once(subEmit, function (data) {
1936 gcd.emit(doneEmit, data[data.length-1][1] || '');
1937 } );
1938
1939 subreg.lastIndex = ind;
1940
1941 match = subreg.exec(text);
1942 if (match) {
1943 ind = subreg.lastIndex;
1944 chr = match[2];
1945 subname = match[1].trim().toLowerCase();
1946 subname = doc.subnameTransform(subname, lname, mainblock);
1947 subname = colon.escape(subname);
1948 if (chr === "|") {
1949 ind = doc.pipeParsing(text, ind, quote, lname, mainblock, subEmit,
1950 textEmit );
1951 } else if (chr === quote) {
1952 // nothing to do; it should automatically work !!!
1953 } else {
1954 gcd.emit("failure in parsing:" + lname, ind);
1955 return ind;
1956 }
1957 } else {
1958 gcd.emit("failure in parsing:" + lname, ind);
1959 return ind;
1960 }
1961
1962
1963 if (subname === '') { // no incoming text, just commands acting
1964 gcd.emit(textEmit, '');
1965 } else {
1966 subtext = doc.retrieve(subname, textEmit);
1967 }
1968
1969 return ind;
1970
1971};
1972
1973dp.pipeParsing = function (text, ind, quote, name, mainblock, toEmit, textEmit) {
1974 var doc = this;
1975 var gcd = doc.gcd;
1976 var colon = doc.colon;
1977
1978 var incomingEmit = textEmit;
1979
1980 var chr, match, command,
1981 comname, start;
1982 var n = text.length;
1983 var comreg = doc.regexs.command[quote];
1984
1985
1986 while (ind < n) { // command processing loop
1987
1988 comreg.lastIndex = ind;
1989 start = ind;
1990
1991 match = comreg.exec(text);
1992 if (match) {
1993 command = match[1].trim().toLowerCase();
1994 chr = match[2];
1995 ind = comreg.lastIndex;
1996 if (command === '') {
1997 command = "passthru";
1998 }
1999 command = colon.escape(command);
2000 comname = name + colon.v + start;
2001
2002 gcd.once("arguments ready:" + comname,
2003 doc.argFinishingHandler(comname));
2004
2005 gcd.when([incomingEmit,
2006 "command parsed:" + comname ],
2007 "arguments ready:" + comname );
2008
2009 gcd.when("text ready:" + comname, toEmit);
2010
2011 incomingEmit = "text ready:" + comname;
2012
2013 if (chr === quote) {
2014 ind -= 1; // it is set to just after the last position
2015 gcd.emit("command parsed:" + comname,
2016 [doc.file, command, "text ready:" + comname ]);
2017 break;
2018
2019 } else if (chr === "|") {
2020 // nothing to do; just done.
2021 } else {
2022 ind = doc.argProcessing(text, ind, quote, comname, mainblock );
2023 }
2024
2025 } else {
2026 gcd.emit("error:command parsing:" + name + colon.v + ind);
2027 return ind+1;
2028 }
2029
2030 gcd.emit("command parsed:" + comname,
2031 [doc.file, command, "text ready:" + comname ]);
2032
2033 if (text[ind] === quote) {
2034 break;
2035 } else if (text[ind] === "|") {
2036 start = ind += 1;
2037 } else {
2038 gcd.emit("error:bad terminating character in command" +
2039 name, [start, ind, text[ind]]);
2040 }
2041
2042 }
2043
2044
2045 return ind+1;
2046
2047};
2048
2049dp.regexs = {
2050
2051 command : {
2052 "'" : /\s*([^|'\s]*)(.)/g,
2053 '"' : /\s*([^|"\s]*)(.)/g,
2054 "`" : /\s*([^|`\s]*)(.)/g
2055 },
2056 argument : {
2057 "'" : /\s*([^,|\\']*)(.)/g,
2058 '"' : /\s*([^,|\\"]*)(.)/g,
2059 "`" : /\s*([^,|\\`]*)(.)/g
2060 },
2061 endarg : {
2062 "'" : /\s*([,|\\'])/g,
2063 '"' : /\s*([,|\\"])/g,
2064 "`" : /\s*([,|\\`])/g
2065
2066 },
2067 subname : {
2068 "'" : /\s*([^|']*)(.)/g,
2069 '"' : /\s*([^|"]*)(.)/g,
2070 "`" : /\s*([^|`]*)(.)/g
2071 }
2072
2073
2074};
2075
2076dp.store = function (name, text) {
2077 var doc = this;
2078 var gcd = doc.gcd;
2079 var scope = doc.getScope(name);
2080
2081
2082 var f;
2083 if (! scope[0]) {
2084 gcd.emit("waiting for:storing:" + doc.file + ":" + name,
2085 ["scope exists:" + scope[2], "scope exists", scope[2],
2086 doc.file, scope[1] ]);
2087 f = function () {
2088 doc.store(name, text);
2089 };
2090 f._label = "Storing:" + doc.file + ":" + name;
2091 gcd.once("scope exists:" + scope[2], f);
2092 return;
2093 }
2094
2095 var varname = scope[1];
2096 var file = scope[2];
2097 scope = scope[0];
2098
2099 var old;
2100 if (scope.hasOwnProperty(varname) ) {
2101 old = scope[varname];
2102 scope[varname] = text;
2103 gcd.emit("overwriting existing var:" + file + ":" + varname,
2104 {oldtext:old, newtext: text} );
2105 } else {
2106 scope[varname] = text;
2107 }
2108 gcd.emit("text stored:" + file + ":" + varname, text);
2109};
2110
2111dp.getBlock = function (start, cur) {
2112 var doc = this;
2113 var colon = doc.colon;
2114
2115 if (typeof cur === "string") {
2116 cur = colon.restore(cur);
2117 } else {
2118 cur = '';
2119 }
2120
2121 if (typeof start === "string") {
2122 if ( start[0] === "#") {
2123 start = start.slice(1).replace(/-/g, " ");
2124 }
2125
2126 start = start.trim().toLowerCase();
2127
2128 if (start[0] === ":") {
2129 start = doc.stripSwitch(cur) + start;
2130 }
2131
2132 } else {
2133 doc.gcd.emit("error:start not a string in doc block", [start, cur]);
2134 start = '';
2135 }
2136 if (!start) {
2137 start = cur;
2138 }
2139
2140 return colon.escape(start);
2141};
2142
2143dp.stripSwitch = function (name) {
2144 var ind, blockhead;
2145
2146 blockhead = name;
2147
2148 if ( (ind = name.indexOf("::")) !== -1) {
2149 if ( (ind = name.indexOf(":", ind+2 )) !== -1 ) {
2150 blockhead = name.slice(0, ind);
2151 }
2152 } else if ( (ind = name.indexOf(":") ) !== -1) {
2153 blockhead = name.slice(0, ind);
2154 }
2155
2156 return blockhead;
2157
2158};
2159
2160dp.midPipes = function (str) {
2161 var ind = str.indexOf("|");
2162 var options, pipes;
2163
2164 ind = str.indexOf("|");
2165 if (ind === -1) {
2166 options = str.trim();
2167 pipes = "";
2168 } else {
2169 options = str.slice(0,ind).trim();
2170 pipes = str.slice(ind+1);
2171 }
2172
2173 return [options, pipes];
2174};
2175
2176dp.pipeDirSetup = function (str, emitname, handler, start) {
2177 var doc = this;
2178 var gcd = doc.gcd;
2179 var colon = doc.colon;
2180 var block;
2181
2182 var subEmit, textEmit, doneEmit;
2183
2184 if (str) {
2185 str = str + '"';
2186 doneEmit = "text ready:" + emitname;
2187 textEmit = "text ready:" + emitname + colon.v + "sp";
2188 subEmit = "pipe chain ready:" + emitname + colon.v + "sp";
2189 gcd.once(doneEmit, handler);
2190
2191 gcd.when(textEmit, subEmit);
2192
2193 gcd.once(subEmit, function (data) {
2194 var text = data[data.length-1][1] || '';
2195 gcd.emit(doneEmit, text);
2196 });
2197
2198
2199 block = doc.stripSwitch(colon.restore(start));
2200
2201 doc.pipeParsing(str, 0, '"', emitname + colon.v + "sp", block,
2202 subEmit, textEmit);
2203
2204 } else {
2205 gcd.once("text ready:" + emitname + colon.v + "sp", handler);
2206 }
2207
2208};
2209
2210dp.findMatchQuote = function (text, quote, ind) {
2211 var char;
2212 var n = text.length;
2213 var level = 0;
2214
2215 while ( ind < n) {
2216 char = text[ind];
2217 ind += 1;
2218 if (char === quote) {
2219 if ( level === 0) {
2220 break;
2221 } else {
2222 level -= 1;
2223 }
2224 } else if (char === '\u005F') {
2225 if (text[ind] === quote) {
2226 level += 1;
2227 ind += 1;
2228 }
2229
2230 } else if (char === "\\") {
2231 if ( text[ind] === quote) {
2232 ind += 1; // skip over the quote
2233 }
2234 }
2235 }
2236
2237 return ind;
2238};
2239
2240dp.argHandlerMaker = function (name, gcd) {
2241 var f = function (data) {
2242 var ret = [data[1][1]]; //cmd name
2243 var args = data.slice(2);
2244 args.forEach(function (el) {
2245 ret.push(el[1]);
2246 });
2247 ret.sub = true;
2248 gcd.emit("text ready:" + name, ret);
2249 };
2250 f._label = "arg command processing;;"+name;
2251 return f;
2252 };
2253
2254dp.argEscaping = function (text, ind ) {
2255 var chr, match, num;
2256 var uni = /[0-9A-F]+/g;
2257 var indicator = this.indicator;
2258
2259 chr = text[ind];
2260 switch (chr) {
2261 case "|" : return ["|", ind+1];
2262 case '\u005F' : return ['\u005F', ind+1];
2263 case "\\" : return ["\\", ind+1];
2264 case "'" : return ["'", ind+1];
2265 case "`" : return ["`", ind+1];
2266 case '"' : return ['"', ind+1];
2267 case "n" : return [indicator + "\n" + indicator, ind+1];
2268 case "t" : return [indicator + "\t" + indicator, ind+1];
2269 case " " : return [indicator + " " + indicator, ind+1];
2270 case "," : return [",", ind+1];
2271 case "u" : uni.lastIndex = ind;
2272 match = uni.exec(text);
2273 if (match) {
2274 num = parseInt(match[0], 16);
2275 try {
2276 chr = String.fromCodePoint(num);
2277 return [chr, uni.lastIndex];
2278 } catch (e) {
2279 return ["\\", ind];
2280 }
2281 } else {
2282 return ["\\", ind];
2283 }
2284 break;
2285 default : return ["\\", ind];
2286
2287 }
2288};
2289
2290dp.argProcessing = function (text, ind, quote, topname, mainblock) {
2291 var doc = this;
2292 var gcd = doc.gcd;
2293 var n = text.length;
2294 var stack = [];
2295 var name = [topname];
2296 var emitname = topname;
2297 var colon = doc.colon;
2298 var argstring = '';
2299 var curname;
2300 var err;
2301 var temp;
2302 var start = ind;
2303 var cp = "c\u0028"; // c parentheses
2304 var argdone = false;
2305
2306 var handlerMaker = doc.argHandlerMaker;
2307
2308 var whenIt = function () {
2309 name.push(start);
2310 curname = name.join(colon.v);
2311 gcd.when("text ready:" + curname, "arguments ready:" + emitname);
2312 };
2313
2314
2315 var wsreg = /\S/g;
2316
2317 wsreg.lastIndex = ind;
2318 if (wsreg.test(text) ) {
2319 start = ind = wsreg.lastIndex - 1;
2320 } else {
2321 ind = text.length;
2322 err = [start, ind];
2323 gcd.emit("error:" + topname, [err, "argument is just whitespace with no terminating"]);
2324 return;
2325 }
2326
2327
2328 while ( ind < n ) {
2329
2330 switch (text[ind]) {
2331
2332 case "\u005F" : // underscore
2333 if ( (start === ind) &&
2334 ( "\"'`".indexOf(text[ind+1]) !== -1 ) ) {
2335 whenIt();
2336 temp = doc.substituteParsing(text, ind+2, text[ind+1], curname, mainblock);
2337
2338 if ( temp === text.length) {
2339 //error
2340 err = [curname];
2341 gcd.emit("error:" + topname, [err, "substitution consumed rest of block"]);
2342 return temp;
2343 } else {
2344 ind = temp;
2345 }
2346
2347 argstring = '';
2348 argdone = true;
2349 wsreg.lastIndex = ind;
2350 if (wsreg.test(text) ) {
2351 start = ind = wsreg.lastIndex - 1;
2352 } else {
2353 ind = text.length;
2354 err = [start, ind];
2355 gcd.emit("error:" + topname, [err, "argument is just whitespace with no terminating"]);
2356 return;
2357 }
2358 continue;
2359 } else {
2360 argstring += "\u005F";
2361 }
2362 break;
2363
2364 case "," :
2365 if ( (stack.length === 0 ) || (stack[0] === cp) ) {
2366 if (argdone) {
2367 if (argstring !== "") {
2368 err = [argstring, text[start], text[ind], start, ind];
2369 gcd.emit("error:" + topname, [err, "stuff found after argument finished"]);
2370 argstring = "";
2371 }
2372 argdone = false;
2373 name.pop();
2374 start = ind+1;
2375
2376 } else { // simple string
2377 whenIt();
2378 gcd.emit("text ready:" + curname, doc.whitespaceEscape(argstring.trim()));
2379 name.pop();
2380 argstring = "";
2381 start = ind + 1;
2382 }
2383 ind += 1;
2384 wsreg.lastIndex = ind;
2385 if (wsreg.test(text) ) {
2386 start = ind = wsreg.lastIndex - 1;
2387 } else {
2388 ind = text.length;
2389 err = [start, ind];
2390 gcd.emit("error:" + topname, [err, "argument is just whitespace with no terminating"]);
2391 return;
2392 }
2393 continue;
2394 } else {
2395 argstring += ",";
2396 }
2397 break;
2398
2399 case "\\" :
2400 temp = doc.argEscaping(text, ind+1);
2401 argstring += temp[0];
2402 ind = temp[1];
2403 continue;
2404
2405 case "|" :
2406 if (stack.length === 0) {
2407 // make sure there is an argument
2408 if (argstring.trim()) {
2409 if (argdone) {
2410 if (argstring !== "") {
2411 err = [argstring, text[start], text[ind], start, ind];
2412 gcd.emit("error:" + topname, [err, "stuff found after argument finished"]);
2413 argstring = "";
2414 }
2415 argdone = false;
2416 name.pop();
2417 start = ind+1;
2418
2419 } else { // simple string
2420 whenIt();
2421 gcd.emit("text ready:" + curname, doc.whitespaceEscape(argstring.trim()));
2422 name.pop();
2423 argstring = "";
2424 start = ind + 1;
2425 }
2426 }
2427 return ind;
2428
2429 } else {
2430 argstring += "|";
2431 }
2432 break;
2433
2434
2435 case "[" :
2436 stack.unshift("[");
2437 argstring += "[";
2438 break;
2439
2440 case "]":
2441 if (stack[0] === "[") {
2442 stack.shift();
2443 }
2444 argstring += "]" ;
2445 break;
2446
2447 case "(" :
2448 //make sure no whitespace to be a command
2449 // also make sure either top level or in cp
2450 if ( ( (stack.length ===0) || (stack[0] === cp) ) &&
2451 (argstring.search(/^\S+\s*$/) !== -1) ) {
2452
2453 stack.unshift(cp);
2454 whenIt();
2455 emitname = curname;
2456 gcd.once("arguments ready:" + emitname, handlerMaker(emitname, gcd));
2457 gcd.when(["arg command parsed:" + emitname, "arg command is:" + emitname], "arguments ready:" + emitname);
2458 gcd.emit("arg command is:" + emitname, argstring.trim().toLowerCase());
2459 argstring = '';
2460 ind += 1;
2461 wsreg.lastIndex = ind;
2462 if (wsreg.test(text) ) {
2463 start = ind = wsreg.lastIndex - 1;
2464 } else {
2465 ind = text.length;
2466 err = [start, ind];
2467 gcd.emit("error:" + topname, [err, "argument is just whitespace with no terminating"]);
2468 return;
2469 }
2470 continue;
2471 } else {
2472 stack.unshift("(");
2473 argstring += "(";
2474 }
2475 break;
2476
2477 case ")" :
2478 if (stack[0] === cp) {
2479 stack.shift();
2480 if (argdone) {
2481 if (argstring !== "") {
2482 err = [argstring, text[start], text[ind], start, ind];
2483 gcd.emit("error:" + topname, [err, "stuff found after argument finished"]);
2484 argstring = "";
2485 }
2486 argdone = false;
2487 name.pop();
2488 start = ind+1;
2489
2490 } else { // simple string
2491 whenIt();
2492 gcd.emit("text ready:" + curname, doc.whitespaceEscape(argstring.trim()));
2493 name.pop();
2494 argstring = "";
2495 start = ind + 1;
2496 } // the last argument is popped
2497 gcd.emit("arg command parsed:" + emitname);
2498 emitname = name.slice(0, -1).join(colon.v);
2499 argdone = true;
2500 argstring = '';
2501 ind += 1;
2502 wsreg.lastIndex = ind;
2503 if (wsreg.test(text) ) {
2504 start = ind = wsreg.lastIndex - 1;
2505 } else {
2506 ind = text.length;
2507 err = [start, ind];
2508 gcd.emit("error:" + topname, [err, "argument is just whitespace with no terminating"]);
2509 return;
2510 }
2511 continue;
2512 } else {
2513 if (stack[0] === "(") {
2514 stack.shift();
2515 }
2516 argstring += ")" ;
2517 }
2518 break;
2519
2520 case "{" :
2521 stack.unshift("{");
2522 argstring += "{";
2523 break;
2524
2525 case "}" :
2526 if (stack[0] === "{") {
2527 stack.shift();
2528 }
2529 argstring += "}" ;
2530 break;
2531
2532 case "'" :
2533 if ( (stack.length === 0) && (quote === "'") ) {
2534 if (argstring.trim()) {
2535 if (argdone) {
2536 if (argstring !== "") {
2537 err = [argstring, text[start], text[ind], start, ind];
2538 gcd.emit("error:" + topname, [err, "stuff found after argument finished"]);
2539 argstring = "";
2540 }
2541 argdone = false;
2542 name.pop();
2543 start = ind+1;
2544
2545 } else { // simple string
2546 whenIt();
2547 gcd.emit("text ready:" + curname, doc.whitespaceEscape(argstring.trim()));
2548 name.pop();
2549 argstring = "";
2550 start = ind + 1;
2551 }
2552 }
2553 return ind;
2554 } else {
2555 // start after current place, get quote position
2556 temp = text.indexOf("'", ind+1)+1;
2557 if (temp === -1 ) {
2558 err = [start, ind, temp];
2559 gcd.emit("error:" + topname, [err, "non-terminating quote"]);
2560 argstring += "'";
2561 } else {
2562 argstring += text.slice(ind, temp);
2563 ind = temp;
2564
2565 continue;
2566 }
2567 }
2568 break;
2569 case "\u0022" :
2570 if ( (stack.length === 0) && (quote === "\u0022") ) {
2571 if (argstring.trim()) {
2572 if (argdone) {
2573 if (argstring !== "") {
2574 err = [argstring, text[start], text[ind], start, ind];
2575 gcd.emit("error:" + topname, [err, "stuff found after argument finished"]);
2576 argstring = "";
2577 }
2578 argdone = false;
2579 name.pop();
2580 start = ind+1;
2581
2582 } else { // simple string
2583 whenIt();
2584 gcd.emit("text ready:" + curname, doc.whitespaceEscape(argstring.trim()));
2585 name.pop();
2586 argstring = "";
2587 start = ind + 1;
2588 }
2589 }
2590 return ind;
2591 } else {
2592 // start after current place, get quote position
2593 temp = text.indexOf("\u0022", ind+1)+1;
2594 if (temp === -1 ) {
2595 err = [start, ind, temp];
2596 gcd.emit("error:" + topname, [err, "non-terminating quote"]);
2597 argstring += "\u0022";
2598 } else {
2599 argstring += text.slice(ind, temp);
2600 ind = temp;
2601
2602 continue;
2603 }
2604 }
2605 break;
2606
2607 case "`" :
2608 if ( (stack.length === 0) && (quote === "`") ) {
2609 if (argstring.trim()) {
2610 if (argdone) {
2611 if (argstring !== "") {
2612 err = [argstring, text[start], text[ind], start, ind];
2613 gcd.emit("error:" + topname, [err, "stuff found after argument finished"]);
2614 argstring = "";
2615 }
2616 argdone = false;
2617 name.pop();
2618 start = ind+1;
2619
2620 } else { // simple string
2621 whenIt();
2622 gcd.emit("text ready:" + curname, doc.whitespaceEscape(argstring.trim()));
2623 name.pop();
2624 argstring = "";
2625 start = ind + 1;
2626 }
2627 }
2628 return ind;
2629 } else {
2630 // start after current place, get quote position
2631 temp = text.indexOf("`", ind+1)+1;
2632 if (temp === -1 ) {
2633 err = [start, ind, temp];
2634 gcd.emit("error:" + topname, [err, "non-terminating quote"]);
2635 argstring += "`";
2636 } else {
2637 argstring += text.slice(ind, temp);
2638 ind = temp;
2639
2640 continue;
2641 }
2642 }
2643 break;
2644 default:
2645 argstring += text[ind];
2646
2647
2648
2649 }
2650
2651 ind +=1;
2652
2653 }
2654
2655 return ind;
2656
2657};
2658
2659dp.argFinishingHandler = function (comname) {
2660 var doc = this;
2661 var gcd = this.gcd;
2662
2663 var f = function (data) {
2664 var input, args, command, han;
2665
2666 input = data[0][1];
2667 command = data[1][1][1];
2668 args = data.slice(2).map(function (el) {
2669 return el[1];
2670 });
2671
2672 var fun = doc.commands[command];
2673
2674 if (fun) {
2675 fun.apply(doc, [input, args, comname, command]);
2676 } else {
2677 han = function () {
2678 fun = doc.commands[command];
2679 if (fun) {
2680 fun.apply(doc, [input, args, comname, command]);
2681 } else { // wait some more
2682 gcd.once("command defined:" + command, han);
2683 }
2684 };
2685 han._label = "delayed command:" + command + ":" + comname;
2686 gcd.once("command defined:" + command, han);
2687 }
2688
2689
2690 };
2691 f._label = "waiting for arguments:" + comname;
2692 return f;
2693};
2694
2695dp.whitespaceEscape = function (text) {
2696 var indicator = this.indicator;
2697 var n = indicator.length, start, end, rep;
2698 while ( (start = text.indexOf(indicator) ) !== -1 ) {
2699 end = text.indexOf(indicator, start + n);
2700 rep = text.slice(start+n, end);
2701 if (rep === "n") {
2702 rep = "\n";
2703 }
2704 text = text.slice(0, start) + rep + text.slice(end+n);
2705 }
2706 return text;
2707
2708};
2709
2710dp.argsPrep = function self (args, name, subs ) {
2711 var retArgs = [], i, n = args.length;
2712 var ret, subArgs;
2713 var cur, doc = this, gcd = this.gcd;
2714 doc.cmdName = name;
2715 for (i = 0; i < n; i += 1) {
2716 cur = args[i];
2717 if (Array.isArray(cur) && cur.sub ) {
2718 subArgs = cur.slice(1);
2719 if (subArgs.length) {
2720 subArgs = self.call(doc, subArgs, name, subs);
2721 }
2722 ret = subs[cur[0]].apply(doc, subArgs);
2723
2724 if (Array.isArray(ret) && (ret.args === true) ) {
2725 Array.prototype.push.apply(retArgs, ret);
2726 } else if (typeof ret === "undefined") {
2727 // no action, nothing added to retArgs
2728 } else {
2729 retArgs.push(ret);
2730 }
2731 } else { // should never happen
2732 retArgs.push(cur);
2733 }
2734
2735 }
2736
2737 return retArgs;
2738
2739};
2740
2741dp.getPostPipeName = function (name) {
2742 var ind = name.indexOf("|") + 1;
2743 if (ind) {
2744 return name.slice(ind);
2745 } else {
2746 return '';
2747 }
2748} ;
2749
2750module.exports = Folder;