1 /* String streams are the things fed to parsers (which can feed them
2 * to a tokenizer if they want). They provide peek and next methods
3 * for looking at the current character (next 'consumes' this
4 * character, peek does not), and a get method for retrieving all the
5 * text that was consumed since the last time get was called.
7 * An easy mistake to make is to let a StopIteration exception finish
8 * the token stream while there are still characters pending in the
9 * string stream (hitting the end of the buffer while parsing a
10 * token). To make it easier to detect such errors, the stringstreams
11 * throw an exception when this happens.
14 // Make a stringstream stream out of an iterator that returns strings.
15 // This is applied to the result of traverseDOM (see codemirror.js),
16 // and the resulting stream is fed to the parser.
17 var stringStream = function(source){
18 // String that's currently being iterated over.
20 // Position in that string.
22 // Accumulator for strings that have been iterated over but not
25 // Make sure there are more characters ready, or throw
27 function ensureChars() {
28 while (pos == current.length) {
30 current = ""; // In case source.next() throws
32 try {current = source.next();}
34 if (e != StopIteration) throw e;
42 // Return the next character in the stream.
44 if (!ensureChars()) return null;
45 return current.charAt(pos);
47 // Get the next character, throw StopIteration if at end, check
48 // for unused content.
52 throw "End of stringstream reached without emptying buffer ('" + accum + "').";
56 return current.charAt(pos++);
58 // Return the characters iterated over since the last call to
64 temp += current.slice(0, pos);
65 current = current.slice(pos);
70 // Push a string back into the stream.
72 current = current.slice(0, pos) + str + current.slice(pos);
74 lookAhead: function(str, consume, skipSpaces, caseInsensitive) {
75 function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
79 var _accum = accum, _pos = pos;
80 if (skipSpaces) this.nextWhileMatches(/[\s\u00a0]/);
83 var end = pos + str.length, left = current.length - pos;
84 if (end <= current.length) {
85 found = str == cased(current.slice(pos, end));
89 else if (str.slice(0, left) == cased(current.slice(pos))) {
90 accum += current; current = "";
91 try {current = source.next();}
94 str = str.slice(left);
101 if (!(found && consume)) {
102 current = accum.slice(_accum.length) + current;
110 // Utils built on top of the above
112 return this.peek() !== null;
114 applies: function(test) {
115 var next = this.peek();
116 return (next !== null && test(next));
118 nextWhile: function(test) {
120 while ((next = this.peek()) !== null && test(next))
123 matches: function(re) {
124 var next = this.peek();
125 return (next !== null && re.test(next));
127 nextWhileMatches: function(re) {
129 while ((next = this.peek()) !== null && re.test(next))
132 equals: function(ch) {
133 return ch === this.peek();
135 endOfLine: function() {
136 var next = this.peek();
137 return next == null || next == "\n";