Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: lib/encoding.js

Issue 10233013: Crawler, second version (Closed)
Patch Set: Created April 12, 2013, 1:38 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « lib/crawler.js ('k') | lib/instruction.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 let {Logger} = require( "logger" );
2
3 function tab( indent )
4 {
5 var s = "";
6 for ( let i = indent ; i > 0 ; --i )
7 s += " ";
8 return s;
9 }
10
11 //-------------------------------------------------------
12 // Multiple Format
13 //-------------------------------------------------------
14 /**
15 * Combine a number of formats as a single output.
16 * <p>
17 * Written to support an ordinary use case: YAML to the log window, JSON to file .
18 * @constructor
19 */
20 var Multiple_Format = function()
21 {
22 this.formatters = [];
23 };
24
25 Multiple_Format.prototype.add = function( formatter )
26 {
27 this.formatters.push( formatter );
28 };
29
30 Multiple_Format.prototype.primitive = function( value )
31 {
32 for ( let f of this.formatters )
33 {
34 f.primitive( value );
35 }
36 };
37
38 Multiple_Format.prototype.special_null = function()
39 {
40 for ( let f of this.formatters )
41 {
42 f.special_null();
43 }
44 };
45
46 Multiple_Format.prototype.object_begin = function()
47 {
48 for ( let f of this.formatters )
49 {
50 f.object_begin();
51 }
52 };
53
54 Multiple_Format.prototype.object_before_element = function( key )
55 {
56 for ( let f of this.formatters )
57 {
58 f.object_before_element( key );
59 }
60 };
61
62 Multiple_Format.prototype.object_end = function()
63 {
64 for ( let f of this.formatters )
65 {
66 f.object_end();
67 }
68 };
69
70 Multiple_Format.prototype.array_begin = function()
71 {
72 for ( let f of this.formatters )
73 {
74 f.array_begin();
75 }
76 };
77
78 Multiple_Format.prototype.array_before_element = function()
79 {
80 for ( let f of this.formatters )
81 {
82 f.array_before_element();
83 }
84 };
85
86 Multiple_Format.prototype.array_end = function()
87 {
88 for ( let f of this.formatters )
89 {
90 f.array_end();
91 }
92 };
93
94 //-------------------------------------------------------
95 // Stack-based Formatter
96 //-------------------------------------------------------
97 /**
98 * Base class for formatters that provides an internal stack.
99 * <p>
100 * Stack provides a default 'counter' member for detection first/subsequent sequ ence members to support delimiters.
101 * @constructor
102 */
103 var Stack_Format_class = function()
104 {
105 };
106
107 Stack_Format_class.init = function()
108 {
109 this._format_stack = [];
110 };
111
112 /**
113 * @param {Object} x
114 * Stack object. A 'counter' member is added whose initial value is zero.
115 */
116 Stack_Format_class.prototype.push_format = function( x )
117 {
118 this._format_stack.push( x );
119 this._format_stack[ this._format_stack.length - 1 ].counter = 0;
120 };
121
122 /**
123 * Pop an item off the top of the format stack.
124 */
125 Stack_Format_class.prototype.pop_format = function()
126 {
127 this._format_stack.pop();
128 };
129
130 /**
131 * Retrieve the format on the top of the format stack.
132 * @param {number} [n]
133 * @return {*}
134 */
135 Stack_Format_class.prototype.top_format = function( n )
136 {
137 if ( arguments.length == 0 )
138 {
139 n = 0;
140 }
141 return this._format_stack[ this._format_stack.length - n - 1];
142 };
143
144 Stack_Format_class.prototype.counter = function()
145 {
146 return this.top_format( 0 ).counter;
147 };
148
149 Stack_Format_class.prototype.advance = function()
150 {
151 ++this.top_format( 0 ).counter;
152 };
153
154 //-------------------------------------------------------
155 // JSON Formatter
156 //-------------------------------------------------------
157 var JSON_Format = function( sink )
158 {
159 Stack_Format_class.init.call( this );
160
161 this.sink = sink;
162 };
163 JSON_Format.prototype = new Stack_Format_class();
164
165 JSON_Format.prototype.primitive = function( value )
166 {
167 this.sink( JSON.stringify( value ) );
168 };
169
170 JSON_Format.prototype.special_null = function()
171 {
172 this.sink( "null" );
173 };
174
175 JSON_Format.prototype.object_begin = function()
176 {
177 this.sink( "{" );
178 this.push_format( {} );
179 };
180
181 JSON_Format.prototype.object_before_element = function( key )
182 {
183 if ( this.counter() != 0 )
184 this.sink( "," );
185 this.advance();
186 this.sink( JSON.stringify( key ) + ":" );
187 };
188
189 JSON_Format.prototype.object_end = function()
190 {
191 this.sink( "}" );
192 this.pop_format();
193 };
194
195 JSON_Format.prototype.array_begin = function()
196 {
197 this.sink( "[" );
198 this.push_format( {} );
199 };
200
201 JSON_Format.prototype.array_before_element = function()
202 {
203 if ( this.counter() != 0 )
204 this.sink( "," );
205 this.advance();
206 };
207
208 JSON_Format.prototype.array_end = function()
209 {
210 this.sink( "]" );
211 this.pop_format();
212 };
213
214 //-------------------------------------------------------
215 // YAML Formatter
216 //-------------------------------------------------------
217
218 var YAML_Format = function( sink )
219 {
220 Stack_Format_class.init.call( this );
221
222 this.sink = sink;
223
224 this.push_format( {
225 indent: 0,
226 primitive_extra: "",
227 aggregate_first: ""
228 } );
229
230 this.logger = new Logger( "YAML_format" );
231 //this.logger.suppress( true );
232
233 this.trace_token = false;
234 };
235 YAML_Format.prototype = new Stack_Format_class();
236
237 YAML_Format.array_mark = "- ";
238 YAML_Format.array_extra = " ";
239
240 YAML_Format.prototype.primitive = function( value )
241 {
242 this._atom( value.toString() );
243 };
244
245 YAML_Format.prototype.special_null = function()
246 {
247 this._atom( "null" );
248 };
249
250 YAML_Format.prototype._atom = function( s )
251 {
252 this.sink( this.top_format().primitive_extra + s + "\n" );
253 };
254
255 //----------------------------------
256 // YAML_format, object
257 //----------------------------------
258
259 YAML_Format.prototype.object_begin = function()
260 {
261 var format = this.top_format();
262 this.push_format( {
263 indent: format.indent + 1,
264 primitive_extra: " ",
265 aggregate_first: "\n" + tab( format.indent + 1 )
266 } );
267 };
268
269 YAML_Format.prototype.object_before_element = function( key )
270 {
271 let parent = this.top_format( 1 );
272
273 if ( this.counter() == 0 )
274 {
275 this.sink( parent.aggregate_first );
276 }
277 else
278 {
279 this.sink( tab( parent.indent ) );
280 }
281 this.sink( key + ":" );
282 this.advance();
283 };
284
285 YAML_Format.prototype.object_end = function()
286 {
287 this.pop_format();
288 };
289
290 //----------------------------------
291 // YAML_format, array
292 //----------------------------------
293
294 YAML_Format.prototype.array_begin = function()
295 {
296 let parent = this.top_format();
297 let prefix = parent.aggregate_first + YAML_Format.array_mark;
298
299 this.push_format( {
300 indent: parent.indent + 1,
301 primitive_extra: prefix,
302 aggregate_first: prefix + YAML_Format.array_extra
303 } );
304 this.logger.make_log( "array_begin" )(
305 "\n\tprefix = " + JSON.stringify( prefix )
306 + "\n\tparent = " + JSON.stringify( parent )
307 + "\n\tformat = " + JSON.stringify( this.top_format() ),
308 false
309 )
310 };
311
312 YAML_Format.prototype.array_before_element = function()
313 {
314 let format = this.top_format( 0 );
315 let parent = this.top_format( 1 );
316
317 this.logger.make_log( "array_before_element" )(
318 "\n\tparent = " + JSON.stringify( parent )
319 + "\n\tformat = " + JSON.stringify( format )
320 + "\n\tcounter = " + this.counter(),
321 false
322 );
323
324 if ( this.counter() > 0 )
325 {
326 let prefix = tab( parent.indent ) + YAML_Format.array_mark;
327 this.swap_format( format.indent, prefix, prefix + YAML_Format.array_extra );
328 }
329 this.advance();
330 };
331
332 YAML_Format.prototype.array_end = function()
333 {
334 let format = this.top_format( 0 );
335 let parent = this.top_format( 1 );
336
337 this.logger.make_log( "array_end" )(
338 "\n\tparent = " + JSON.stringify( parent )
339 + "\n\tformat = " + JSON.stringify( format )
340 + "\n\tcounter = " + this.counter(),
341 false
342 );
343
344 if ( this.counter() == 0 )
345 this.sink( parent.primitive_extra + "\n" );
346
347 this.pop_format();
348 };
349
350 /**
351 * Replace the top element of the format stack for the arguments. The top elemen t is discarded.
352 *
353 * @param {number} indent
354 * @param {string} primitive_extra
355 * @param {string} aggregate_first
356 */
357 YAML_Format.prototype.swap_format = function( indent, primitive_extra, aggregate _first )
358 {
359 var format = this.top_format( 0 );
360 format.indent = indent;
361 format.primitive_extra = primitive_extra;
362 format.aggregate_first = aggregate_first;
363 };
364
365 //-------------------------------------------------------
366 // Format_stream
367 //-------------------------------------------------------
368 /**
369 * Stream output in YAML format.
370 * @param formatter
371 * An instance of one of the *_Format classes.
372 * @constructor
373 */
374 var Format_stream = function( formatter )
375 {
376 this.logger = new Logger( "Format_stream" );
377
378 /**
379 * Traversal stack. Tracks the depth-first traversal tokens.
380 * @type {Array}
381 */
382 this.stack = [];
383
384 this._writing = false;
385
386 /**
387 * Input queue. We only need one token of lookahead, but that means that we ne ed to manage an input token that
388 * may be present or not.
389 * @type {Array}
390 */
391 this._input_queue = [];
392
393 this.formatter = formatter;
394
395 this.logger.suppress( true );
396 this.trace_input = false;
397 this.trace_token = false;
398 };
399
400 Format_stream.prototype.sequence_start = function()
401 {
402 this.logger.make_log( "sequence_start" )( "" );
403 this._send( { view: { type: Encoding.type.start }, content: { mark: Encoding.t ype.start } } );
404 };
405
406 Format_stream.prototype.sequence_stop = function()
407 {
408 this.logger.make_log( "sequence_stop" )( "" );
409 this._send( { view: { type: Encoding.type.stop }, content: { mark: Encoding.ty pe.stop } } );
410 };
411
412 Format_stream.prototype.sequence_send = function( value )
413 {
414 this._send( { content: { value: value } } );
415 };
416
417 function is_mark( token )
418 {
419 return ("content" in token) && ("mark" in token.content);
420 }
421
422 function is_value( token )
423 {
424 return ("content" in token) && ("value" in token.content);
425 }
426
427 function get_value( token )
428 {
429 return token.content.value;
430 }
431
432 /**
433 * Send a token to the state machine.
434 * @param token
435 * @private
436 */
437 Format_stream.prototype._send = function( token )
438 {
439 if ( !this._writing )
440 {
441 throw new Error( "May not send tokens when we aren't writing." )
442 }
443 this.logger.make_log( "_send" )( "SEND: " + "\n\ttoken = " + JSON.stringify( t oken ), false );
444 try
445 {
446 this._insert_input( token );
447 this.generator.next();
448 }
449 catch ( e )
450 {
451 if ( e === StopIteration )
452 // This is expected if it stops the last deferred sequence in the object.
453 return;
454 throw e;
455 }
456 };
457
458 Format_stream.prototype.write = function( value, view )
459 {
460 /*
461 * Consistency check to ensure that the caller has been feeding us correctly. If this throws, it means the
462 * caller has made a mistake.
463 */
464 if ( this._writing )
465 throw new Error( "Already writing" );
466 this._writing = true;
467
468 /*
469 * Initialize the input queue with a single token, the one that will be expand ed.
470 *
471 * Start the generator and bring it
472 * to its first yield statement. It will stop if there are no stream inputs wi thin the token, so we check for that.
473 */
474 this._insert_input( { view: view, content: { value: value } } );
475 var g = this._machine();
476 this.generator = g;
477 try
478 {
479 g.next();
480 }
481 catch ( e )
482 {
483 if ( e !== StopIteration ) throw e;
484 }
485 };
486
487 /**
488 * Predicate: "Is write() available to be called?"
489 * @return {boolean}
490 */
491 Format_stream.prototype.may_write = function()
492 {
493 return !this._writing;
494 };
495
496 /**
497 * Stack processing machine for a depth-first traversal of an object tree struct ure where some of the nodes may
498 * be deferred streams.
499 * @private
500 */
501 Format_stream.prototype._machine = function()
502 {
503 /*
504 * The head token of our input stream, if initialized; otherwise undefined
505 */
506 var token = null;
507 /*
508 * Predicate: "the variable 'token' and related variables represent the head t oken of our input stream"
509 */
510 var have_token = false;
511 /*
512 * Predicate: "we need to have a current token"
513 */
514 var need_token = false;
515 /*
516 * Predicate: "we need to have a lookahead token in the input queue that hasn' t been consumed yet"
517 */
518 var need_lookahead = false;
519
520 while ( this._has_token() || this._has_input() )
521 {
522 // Log function indicates stack depth.
523 var log = this.logger.make_log( "machine(" + this.stack.length + ")" );
524
525 /*---------------------
526 * STAGE ONE: Manage input queue.
527 *
528 * We need a token if one of the handlers below has asked for it. If we need a token and we don't already have
529 * one, we ensure we have one with a yield statement. Since _send() is the o nly thing that calls us as a
530 * generator, we will have an input token after a yield.
531 *
532 * This section is here as a result of unrolling a recursive-descent algorit hm. In that pattern, there would be
533 * calls such as queue.get() and queue.lookahead(1). Since we have unrolled everything, such calls occur here at
534 * the top of the loop. Code below makes the analogue of queue calls by sett ing the variables 'need_token' and
535 * 'need_lookahead'.
536 */
537 // Defense
538 if ( need_lookahead && need_token )
539 {
540 /*
541 * The head token of the stream cannot exist in two places at once. Either it's on the input queue
542 * or it has been consumed and is in the local 'token' variable.
543 */
544 throw new Error( "Requesting both lookahead and a current token is inconsi stent." );
545 }
546
547 if ( need_lookahead )
548 {
549 if ( !have_token )
550 {
551 if ( !this._has_input() )
552 {
553 yield null;
554 // Defense
555 if ( !this._has_input() )
556 throw new Error( "Yielded for lookahead token but did not receive it ." );
557 }
558
559 log( "Input LOOKAHEAD" +
560 "\n\ttoken = " + JSON.stringify( token ), this.trace_input );
561 }
562 else
563 {
564 // Untested
565 this._unconsume_input( token );
566 have_token = false;
567 }
568 need_lookahead = false;
569 }
570 else if ( need_token )
571 {
572 /*
573 * If we already have a token, we don't need to do anything more but clear the need_token flag.
574 */
575 if ( !have_token )
576 {
577 if ( !this._has_input() )
578 {
579 yield null;
580 // Defense
581 if ( !this._has_input() )
582 throw new Error( "Yielded for current token but did not receive it." );
583 }
584 // Assert this._has_input()
585 token = this._consume_input();
586 have_token = true;
587 }
588 need_token = false;
589
590 log( "Input CURRENT" +
591 "\n\ttoken = " + JSON.stringify( token ), this.trace_input );
592 }
593 else
594 {
595 // Defense
596 if ( !have_token && !this._has_token() && !this._has_input() )
597 throw new Error( "No current token, no token on traversal stack, and no token in queue" );
598 // Defense
599 token = undefined;
600 }
601
602 /*---------------------
603 * STAGE TWO: Automatic actions, those invoked without input.
604 *
605 * If we don't have a token, there's one either on the input queue or on the top of the traversal stack. If the
606 * traversal stack is empty, we use the input queue. If not, we act accordin g to the state of the token at the
607 * top of the traversal stack.
608 */
609 if ( !have_token )
610 {
611 if ( !this._has_token() )
612 {
613 /*
614 * The token stack is empty. There must be something on the input queue for use to still be here.
615 */
616 need_token = true;
617 continue;
618 }
619
620 // Current Token
621 let c_token = this._top_token();
622 // Look-Ahead Token
623 var la_token = this._look_input();
624
625 log( "TRAVERSAL STACK TOKEN"
626 + "\n\ttop of stack = " + JSON.stringify( c_token )
627 + "\n\t" + ((this._has_input()) ? "lookahead = " + JSON.stringify( la_to ken ) : "no lookahead"),
628 this.trace_token
629 );
630
631 switch ( c_token.view.type.id )
632 {
633 case Encoding.type.object.id:
634 if ( c_token.state.i == 0 )
635 {
636 this.formatter.object_begin();
637 }
638 if ( c_token.state.i < c_token.view.seq.length )
639 {
640 let field = c_token.view.seq[ c_token.state.i ];
641 this.formatter.object_before_element( field.key );
642 let new_token = {
643 content: { value: c_token.content.value[ field.key ] }
644 };
645 if ( 'element_view' in field )
646 {
647 new_token.view = field.element_view;
648 }
649 this._insert_input( new_token );
650 need_token = true;
651 ++c_token.state.i;
652 }
653 else
654 {
655 this.formatter.object_end();
656 this._pop_token();
657 }
658 break;
659
660 case Encoding.type.array.id:
661 if ( c_token.state.i == 0 )
662 {
663 this.formatter.array_begin();
664 }
665 if ( c_token.state.i < c_token.content.value.length )
666 {
667 this.formatter.array_before_element();
668 this._insert_input( {
669 view: c_token.view.element_view,
670 content: { value: c_token.content.value[ c_token.state.i ] }
671 } );
672 need_token = true;
673 ++c_token.state.i;
674 }
675 else
676 {
677 this.formatter.array_end();
678 this._pop_token();
679 }
680 break;
681
682 case Encoding.type.array_stream.id:
683 if ( !this._has_input() )
684 {
685 // If we can't see the next token, we can't proceed.
686 need_lookahead = true;
687 break;
688 }
689 if ( is_mark( la_token ) )
690 {
691 switch ( la_token.view.type.id )
692 {
693 case Encoding.type.start.id:
694 let first_pass = ( c_token.state.i == 0 );
695 if ( first_pass )
696 {
697 /*
698 * Always consume a start token the first time we see one. So this one's ours.
699 */
700 this._consume_input();
701 have_token = false;
702 this.formatter.array_begin();
703 }
704
705 /*
706 * Here we have the analogue of a shift-reduce decision. If the 'element_view' field
707 * in the array specification is an ordinary type, we reduce to the rule that the next
708 * elements are part of our list. If that field is a stream type , we need to shift to a
709 * new list. In the second case, it means we need a new node on the top of the stack.
710 */
711 let x = c_token.view.element_view;
712 if ( x && x.type.stream )
713 {
714 /*
715 * We need to associate the present start mark with a stream t oken we create. In
716 * order to do that, we need the stream token to appear before the start mark. Thus
717 * we stuff a token onto the _back_ end of the queue so that i t's the next token
718 * consumed. The start mark will then be consumed at the itera tion following.
719 */
720 this._unconsume_input( { view: x, content: { value: null } } ) ;
721 }
722 else
723 {
724 /*
725 * We have a start mark at some point after the first pass. Th at's only valid
726 * when our element type is a stream type, which it's not at t his point.
727 */
728 if ( !first_pass )
729 {
730 /*
731 * This is not an internal defense, but an external one. If the user
732 * makes a call to sequence_start() at the wrong time, we'll end up here.
733 */
734 throw new Error( "unexpected call to sequence_start()" );
735 }
736 /*
737 * Note that if 'element_view' is defined but not a stream, th at we have a type
738 * specified for unseen elements. There might be a type confli ct here. We are
739 * resolving that conflict by ignoring 'element_view' unless i t's a stream type.
740 */
741 }
742 /*
743 * Because the next token could be a stop mark, we don't load it as a current token.
744 * Marks are always consumed as lookahead.
745 */
746 need_lookahead = true;
747
748 log( "processed start mark"
749 + "\n\ttop of stack = " + JSON.stringify( this._top_token() ),
750 false );
751 break;
752 case Encoding.type.stop.id:
753 this._consume_input();
754 have_token = false;
755 this._pop_token();
756 this.formatter.array_end();
757 break;
758 default:
759 throw new Error( "Unexpected mark seen for array_stream" );
760 }
761 }
762 else
763 {
764 // Assert the lookahead token is a regular token, since it's not a m ark
765 this.formatter.array_before_element();
766 ++c_token.state.i;
767 // Don't use more than 10 million elements in an array.
768 if ( c_token.state.i > 10000000 )
769 throw new Error( "Runaway loop" );
770 need_token = true;
771 }
772 break;
773 default:
774 throw new Error( "Found token type that should not appear as a non-inp ut token" );
775 }
776 /*
777 * We're skipping all the code below and going back to the top of the loop , where any 'need_token' or
778 * 'need_lookahead' requests can be fulfilled.
779 */
780 continue;
781 }
782
783 /*---------------------
784 * Initialize the local variable 'value'. We may have received a mark, which requires different initialization.
785 */
786 // Defense
787 if ( is_mark( token ) )
788 throw new Error( "Value token expected; mark token found." );
789 // Defense
790 if ( !is_value( token ) )
791 throw new Error( "Value token expected; neither value nor mark token found ." );
792 var value = get_value( token );
793
794 /*---------------------
795 * Obtain a view object.
796 *
797 * A view can arise in three different ways:
798 * 1) From a view specification. This is the only way that deferred ite ms can arise.
799 * 2) By inference from a value. This is the ordinary way for most valu es.
800 * 3) By explicit specification on the object. [Not yet supported.] Mor al equivalent of toJSON().
801 */
802 if ( !( "view" in token ) || !token.view )
803 {
804 try
805 {
806 if ( "__encoding__" in value )
807 {
808 token.view = value["__encoding__"];
809 }
810 else
811 {
812 token.view = {};
813 }
814 }
815 catch ( e )
816 {
817 token.view = {};
818 }
819 }
820 var view = token.view;
821
822 /*---------------------
823 * Honor a toJSON replacement object, if any. Note that we do this _after_ g etting an encoding from the value,
824 * should we need to.
825 */
826 try
827 {
828 if ( "toJSON" in value )
829 {
830 value = value.toJSON();
831 }
832 }
833 catch ( e )
834 {
835 // Disregard errors. The most likely one happens when 'value' is not an ob ject at all.
836 }
837
838 /*---------------------
839 * If we still need a type within our view, we need to infer one from the va lue we have.
840 */
841 if ( !( "type" in view ) )
842 {
843 // Determine the encoding.
844 if ( value == null )
845 {
846 view.type = Encoding.type.null;
847 }
848 else if ( ( typeof value ) == "object" )
849 {
850 let t = Object.prototype.toString.call( value );
851 switch ( t )
852 {
853 case "[object Array]":
854 view.type = Encoding.type.array;
855 break;
856 case "[object Object]":
857 view.type = Encoding.type.object;
858 try
859 {
860 var keys = Object.keys( value );
861 }
862 catch ( e )
863 {
864 // Sometimes an object is not an object. Really.
865 view.type = Encoding.type.primitive;
866 if ( value == null )
867 {
868 value = "null";
869 }
870 break;
871 }
872 view.seq = Encoding.immediate_fields( keys );
873 break;
874 default:
875 /*
876 * For example, [object Date]. They all have meaningful toString() i mplementations.
877 */
878 view.type = Encoding.type.primitive;
879 break;
880 }
881 }
882 else
883 {
884 view.type = Encoding.type.primitive;
885 }
886 }
887
888 log( "INPUT TOKEN.\n\tvalue = " + JSON.stringify( value )
889 + "\n\tview = " + JSON.stringify( view )
890 + "\n\t" + ( ('state' in token) ? "state = " + JSON.stringify( token.state ) : "no state" ),
891 this.trace_token
892 );
893
894 /*---------------------
895 * Process a single iteration step of the state machine.
896 *
897 * - If there's a deferred value, push the next state on to the stack.
898 * - If there's an immediate value, write it to the sink.
899 */
900 /*
901 * If we get this far, we'll consume the token in each case.
902 */
903 have_token = false;
904 if ( view.type.aggregate )
905 {
906 this._push_token( token );
907 // We'll loop back to the top and process the top of the stack next.
908 // Assert need_lookahead == false && need_token == false && have_token == false
909 }
910 else
911 {
912 switch ( view.type.id )
913 {
914 case Encoding.type.null.id:
915 this.formatter.special_null();
916 break;
917 case Encoding.type.primitive.id:
918 this.formatter.primitive( value );
919 break;
920 default:
921 throw new Error( "unexpected encoding type" );
922 break;
923 /*
924 * If we need to output "undefined", we'd do it here.
925 */
926 }
927 }
928 }
929 log = this.logger.make_log( "machine(" + this.stack.length + ")" );
930 log( "machine end" );
931 this._writing = false;
932 };
933
934 //----------------------------------
935 // YAML_stream, traversal stack
936 //----------------------------------
937 /**
938 * Push a token onto the top of the traversal stack. Only aggregates go onto thi s stack. From the point of view of
939 * a graph traversal, atomic elements are leaf nodes and do not appear on the st ack, and aggregates are non-leaf nodes
940 * and do appear. Since an aggregate may only be pushed once onto the traversal stack, we do initialization and a
941 * validation check here.
942 * @param token
943 * @private
944 */
945 Format_stream.prototype._push_token = function( token )
946 {
947 if ( "state" in token )
948 {
949 throw new Error( "An aggregate type may not be pushed more than once onto th e traversal stack" );
950 }
951 else
952 {
953 token.state = { i: 0 };
954 }
955
956 if ( this.stack.length > 100 )
957 throw new Error( "stack overflow" );
958
959 this.stack.push( token );
960 };
961
962 /**
963 * The token on top of the traversal stack.
964 * @return {*}
965 * @private
966 */
967 Format_stream.prototype._top_token = function()
968 {
969 return this.stack[ this.stack.length - 1 ];
970 };
971
972 /**
973 * Pop a token off the top of the traversal stack.
974 * @return {*}
975 * @private
976 */
977 Format_stream.prototype._pop_token = function()
978 {
979 if ( this.stack.length == 0 )
980 throw new Error( "Stack empty" );
981 return this.stack.pop();
982 };
983
984 /**
985 * Predicate "Is the traversal stack non-empty?"
986 * @return {boolean}
987 * @private
988 */
989 Format_stream.prototype._has_token = function()
990 {
991 return this.stack.length != 0;
992 };
993
994 //----------------------------------
995 // YAML_stream, input queue
996 //----------------------------------
997 /**
998 * Predicate "does the input queue have a token ready for us?"
999 * @private
1000 */
1001 Format_stream.prototype._has_input = function()
1002 {
1003 return this._input_queue.length > 0;
1004 };
1005
1006 /**
1007 * Look-ahead function. We shouldn't need more than to call this with n=0.
1008 * @param {number} [n=0]
1009 * @private
1010 */
1011 Format_stream.prototype._look_input = function( n )
1012 {
1013 if ( arguments.length == 0 )
1014 n = 0;
1015 return this._input_queue[ n ];
1016 };
1017
1018 /**
1019 * Put a token into the input queue.
1020 * @param input
1021 * @private
1022 */
1023 Format_stream.prototype._insert_input = function( input )
1024 {
1025 return this._input_queue.push( input );
1026 };
1027
1028 /**
1029 * Retrieve a token from the input queue and remove it so that it won't be exami ned again.
1030 * @return {*}
1031 * @private
1032 */
1033 Format_stream.prototype._consume_input = function()
1034 {
1035 return this._input_queue.shift();
1036 };
1037
1038 /**
1039 * Stuff a token back into the back end of the queue so that it will be seen aga in.
1040 * @param token
1041 * @return {*}
1042 * @private
1043 */
1044 Format_stream.prototype._unconsume_input = function( token )
1045 {
1046 this._input_queue.unshift( token );
1047 };
1048
1049 //-------------------------------------------------------
1050 // Encoding
1051 //-------------------------------------------------------
1052
1053 /**
1054 * The export object for this module.
1055 */
1056 var Encoding = {
1057 Format_stream: Format_stream,
1058 YAML: YAML_Format,
1059 JSON: JSON_Format,
1060 Multiple_Format: Multiple_Format
1061 };
1062 Encoding.type = {
1063 /**
1064 * A primitive value or opaque object whose members are not separately output. Uses toString() to provide a value.
1065 */
1066 null: { id: 0, name: "null" },
1067 /**
1068 * A primitive value or opaque object whose members are not separately output. Uses toString() to provide a value.
1069 */
1070 primitive: { id: 1, name: "primitive" },
1071 /**
1072 * A transparent object whose members are each listed.
1073 */
1074 object: { id: 2, name: "object", aggregate: true },
1075 /**
1076 * An array object
1077 */
1078 array: { id: 3, name: "array", aggregate: true },
1079 /**
1080 * An array object
1081 */
1082 array_stream: { id: 4, name: "array_stream", aggregate: true, stream: true },
1083 /**
1084 * A start marker for the beginning of a deferred sequence
1085 */
1086 start: { id: 5, name: "start" },
1087 /**
1088 * A stop marker for the end of a deferred sequence
1089 */
1090 stop: { id: 6, name: "stop" }
1091 };
1092
1093 Encoding.immediate_fields = function( keys )
1094 {
1095 return keys.reduce( function( result, key )
1096 {
1097 result.push( { key: key } );
1098 return result;
1099 }, [] );
1100 };
1101
1102
1103 Encoding.as_object = function( encodings )
1104 {
1105 return {
1106 type: Encoding.type.object, // Since we're listing fields explicit ly, it's a transparent object
1107 seq: encodings.reduce(
1108 function( result, item )
1109 {
1110 return result.concat( item );
1111 }
1112 )
1113 };
1114 };
1115
1116 Encoding.field = function( key, view )
1117 {
1118 return [
1119 { key: key, element_view: view }
1120 ];
1121 };
1122
1123 /**
1124 * @param {*} [element_view=null]
1125 */
1126 Encoding.array = function( element_view )
1127 {
1128 return {
1129 type: Encoding.type.array,
1130 element_view: ( arguments.length >= 1 ) ? element_view : null
1131 };
1132 };
1133
1134 /**
1135 * @param {*} element_view=null
1136 */
1137 Encoding.array_stream = function( element_view )
1138 {
1139 return {
1140 type: Encoding.type.array_stream,
1141 element_view: element_view
1142 };
1143 };
1144
1145 exports.Encoding = Encoding;
OLDNEW
« no previous file with comments | « lib/crawler.js ('k') | lib/instruction.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld