comparison src/nabble/view/web/util/codemirror/js/stringstream.js @ 0:7ecd1a4ef557

add content
author Franklin Schmidt <fschmidt@gmail.com>
date Thu, 21 Mar 2019 19:15:52 -0600
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:7ecd1a4ef557
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.
6 *
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.
12 */
13
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.
19 var current = "";
20 // Position in that string.
21 var pos = 0;
22 // Accumulator for strings that have been iterated over but not
23 // get()-ed yet.
24 var accum = "";
25 // Make sure there are more characters ready, or throw
26 // StopIteration.
27 function ensureChars() {
28 while (pos == current.length) {
29 accum += current;
30 current = ""; // In case source.next() throws
31 pos = 0;
32 try {current = source.next();}
33 catch (e) {
34 if (e != StopIteration) throw e;
35 else return false;
36 }
37 }
38 return true;
39 }
40
41 return {
42 // peek: -> character
43 // Return the next character in the stream.
44 peek: function() {
45 if (!ensureChars()) return null;
46 return current.charAt(pos);
47 },
48 // next: -> character
49 // Get the next character, throw StopIteration if at end, check
50 // for unused content.
51 next: function() {
52 if (!ensureChars()) {
53 if (accum.length > 0)
54 throw "End of stringstream reached without emptying buffer ('" + accum + "').";
55 else
56 throw StopIteration;
57 }
58 return current.charAt(pos++);
59 },
60 // get(): -> string
61 // Return the characters iterated over since the last call to
62 // .get().
63 get: function() {
64 var temp = accum;
65 accum = "";
66 if (pos > 0){
67 temp += current.slice(0, pos);
68 current = current.slice(pos);
69 pos = 0;
70 }
71 return temp;
72 },
73 // Push a string back into the stream.
74 push: function(str) {
75 current = current.slice(0, pos) + str + current.slice(pos);
76 },
77 lookAhead: function(str, consume, skipSpaces, caseInsensitive) {
78 function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
79 str = cased(str);
80 var found = false;
81
82 var _accum = accum, _pos = pos;
83 if (skipSpaces) this.nextWhileMatches(/[\s\u00a0]/);
84
85 while (true) {
86 var end = pos + str.length, left = current.length - pos;
87 if (end <= current.length) {
88 found = str == cased(current.slice(pos, end));
89 pos = end;
90 break;
91 }
92 else if (str.slice(0, left) == cased(current.slice(pos))) {
93 accum += current; current = "";
94 try {current = source.next();}
95 catch (e) {if (e != StopIteration) throw e; break;}
96 pos = 0;
97 str = str.slice(left);
98 }
99 else {
100 break;
101 }
102 }
103
104 if (!(found && consume)) {
105 current = accum.slice(_accum.length) + current;
106 pos = _pos;
107 accum = _accum;
108 }
109
110 return found;
111 },
112 // Wont't match past end of line.
113 lookAheadRegex: function(regex, consume) {
114 if (regex.source.charAt(0) != "^")
115 throw new Error("Regexps passed to lookAheadRegex must start with ^");
116
117 // Fetch the rest of the line
118 while (current.indexOf("\n", pos) == -1) {
119 try {current += source.next();}
120 catch (e) {if (e != StopIteration) throw e; break;}
121 }
122 var matched = current.slice(pos).match(regex);
123 if (matched && consume) pos += matched[0].length;
124 return matched;
125 },
126
127 // Utils built on top of the above
128 // more: -> boolean
129 // Produce true if the stream isn't empty.
130 more: function() {
131 return this.peek() !== null;
132 },
133 applies: function(test) {
134 var next = this.peek();
135 return (next !== null && test(next));
136 },
137 nextWhile: function(test) {
138 var next;
139 while ((next = this.peek()) !== null && test(next))
140 this.next();
141 },
142 matches: function(re) {
143 var next = this.peek();
144 return (next !== null && re.test(next));
145 },
146 nextWhileMatches: function(re) {
147 var next;
148 while ((next = this.peek()) !== null && re.test(next))
149 this.next();
150 },
151 equals: function(ch) {
152 return ch === this.peek();
153 },
154 endOfLine: function() {
155 var next = this.peek();
156 return next == null || next == "\n";
157 }
158 };
159 };