Mercurial Hosting > nabble
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 }; |