OLD | NEW |
1 /* Parse function for JavaScript. Makes use of the tokenizer from | 1 /* Parse function for JavaScript. Makes use of the tokenizer from |
2 * tokenizejavascript.js. Note that your parsers do not have to be | 2 * tokenizejavascript.js. Note that your parsers do not have to be |
3 * this complicated -- if you don't want to recognize local variables, | 3 * this complicated -- if you don't want to recognize local variables, |
4 * in many languages it is enough to just look for braces, semicolons, | 4 * in many languages it is enough to just look for braces, semicolons, |
5 * parentheses, etc, and know when you are inside a string or comment. | 5 * parentheses, etc, and know when you are inside a string or comment. |
6 * | 6 * |
7 * See manual.html for more info about the parser interface. | 7 * See manual.html for more info about the parser interface. |
8 */ | 8 */ |
9 | 9 |
10 var JSParser = Editor.Parser = (function() { | 10 var JSParser = Editor.Parser = (function() { |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
61 // The lexical scope, used mostly for indentation. | 61 // The lexical scope, used mostly for indentation. |
62 var lexical = new JSLexical((basecolumn || 0) - 2, 0, "block", false); | 62 var lexical = new JSLexical((basecolumn || 0) - 2, 0, "block", false); |
63 // Current column, and the indentation at the start of the current | 63 // Current column, and the indentation at the start of the current |
64 // line. Used to create lexical scope objects. | 64 // line. Used to create lexical scope objects. |
65 var column = 0; | 65 var column = 0; |
66 var indented = 0; | 66 var indented = 0; |
67 // Variables which are used by the mark, cont, and pass functions | 67 // Variables which are used by the mark, cont, and pass functions |
68 // below to communicate with the driver loop in the 'next' | 68 // below to communicate with the driver loop in the 'next' |
69 // function. | 69 // function. |
70 var consume, marked; | 70 var consume, marked; |
71 | 71 |
72 // The iterator object. | 72 // The iterator object. |
73 var parser = {next: next, copy: copy}; | 73 var parser = {next: next, copy: copy}; |
74 | 74 |
75 function next(){ | 75 function next(){ |
76 // Start by performing any 'lexical' actions (adjusting the | 76 // Start by performing any 'lexical' actions (adjusting the |
77 // lexical variable), or the operations below will be working | 77 // lexical variable), or the operations below will be working |
78 // with the wrong lexical state. | 78 // with the wrong lexical state. |
79 while(cc[cc.length - 1].lex) | 79 while(cc[cc.length - 1].lex) |
80 cc.pop()(); | 80 cc.pop()(); |
81 | 81 |
(...skipping 16 matching lines...) Expand all Loading... |
98 } | 98 } |
99 // No more processing for meaningless tokens. | 99 // No more processing for meaningless tokens. |
100 if (token.type == "whitespace" || token.type == "comment") | 100 if (token.type == "whitespace" || token.type == "comment") |
101 return token; | 101 return token; |
102 // When a meaningful token is found and the lexical scope's | 102 // When a meaningful token is found and the lexical scope's |
103 // align is undefined, it is an aligned scope. | 103 // align is undefined, it is an aligned scope. |
104 if (!("align" in lexical)) | 104 if (!("align" in lexical)) |
105 lexical.align = true; | 105 lexical.align = true; |
106 | 106 |
107 // Execute actions until one 'consumes' the token and we can | 107 // Execute actions until one 'consumes' the token and we can |
108 // return it. Marked is used to | 108 // return it. Marked is used to |
109 while(true){ | 109 while(true){ |
110 consume = marked = false; | 110 consume = marked = false; |
111 // Take and execute the topmost action. | 111 // Take and execute the topmost action. |
112 cc.pop()(token.type, token.content); | 112 cc.pop()(token.type, token.content); |
113 if (consume){ | 113 if (consume){ |
114 // Marked is used to change the style of the current token. | 114 // Marked is used to change the style of the current token. |
115 if (marked) | 115 if (marked) |
116 token.style = marked; | 116 token.style = marked; |
117 // Here we differentiate between local and global variables. | 117 // Here we differentiate between local and global variables. |
118 else if (token.type == "variable" && inScope(token.content)) | 118 else if (token.type == "variable" && inScope(token.content)) |
119 token.style = "js-localvariable"; | 119 token.style = "js-localvariable"; |
120 return token; | 120 return token; |
121 } | 121 } |
122 } | 122 } |
123 } | 123 } |
124 | 124 |
125 // This makes a copy of the parser state. It stores all the | 125 // This makes a copy of the parser state. It stores all the |
126 // stateful variables in a closure, and returns a function that | 126 // stateful variables in a closure, and returns a function that |
127 // will restore them when called with a new input stream. Note | 127 // will restore them when called with a new input stream. Note |
128 // that the cc array has to be copied, because it is contantly | 128 // that the cc array has to be copied, because it is contantly |
129 // being modified. Lexical objects are not mutated, and context | 129 // being modified. Lexical objects are not mutated, and context |
130 // objects are not mutated in a harmful way, so they can be shared | 130 // objects are not mutated in a harmful way, so they can be shared |
131 // between runs of the parser. | 131 // between runs of the parser. |
132 function copy(){ | 132 function copy(){ |
133 var _context = context, _lexical = lexical, _cc = cc.concat([]), _tokenSta
te = tokens.state; | 133 var _context = context, _lexical = lexical, _cc = cc.concat([]), _tokenSta
te = tokens.state; |
134 | 134 |
135 return function(input){ | 135 return function(input){ |
136 context = _context; | 136 context = _context; |
137 lexical = _lexical; | 137 lexical = _lexical; |
138 cc = _cc.concat([]); // copies the array | 138 cc = _cc.concat([]); // copies the array |
139 column = indented = 0; | 139 column = indented = 0; |
140 tokens = tokenizeJavaScript(input, _tokenState); | 140 tokens = tokenizeJavaScript(input, _tokenState); |
141 return parser; | 141 return parser; |
142 }; | 142 }; |
143 } | 143 } |
144 | 144 |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
183 // Check whether a variable is defined in the current scope. | 183 // Check whether a variable is defined in the current scope. |
184 function inScope(varname){ | 184 function inScope(varname){ |
185 var cursor = context; | 185 var cursor = context; |
186 while (cursor) { | 186 while (cursor) { |
187 if (cursor.vars[varname]) | 187 if (cursor.vars[varname]) |
188 return true; | 188 return true; |
189 cursor = cursor.prev; | 189 cursor = cursor.prev; |
190 } | 190 } |
191 return false; | 191 return false; |
192 } | 192 } |
193 | 193 |
194 // Push a new lexical context of the given type. | 194 // Push a new lexical context of the given type. |
195 function pushlex(type){ | 195 function pushlex(type){ |
196 var result = function(){ | 196 var result = function(){ |
197 lexical = new JSLexical(indented, column, type, null, lexical) | 197 lexical = new JSLexical(indented, column, type, null, lexical) |
198 }; | 198 }; |
199 result.lex = true; | 199 result.lex = true; |
200 return result; | 200 return result; |
201 } | 201 } |
202 // Pop off the current lexical context. | 202 // Pop off the current lexical context. |
203 function poplex(){ | 203 function poplex(){ |
204 lexical = lexical.prev; | 204 lexical = lexical.prev; |
205 } | 205 } |
206 poplex.lex = true; | 206 poplex.lex = true; |
207 // The 'lex' flag on these actions is used by the 'next' function | 207 // The 'lex' flag on these actions is used by the 'next' function |
208 // to know they can (and have to) be ran before moving on to the | 208 // to know they can (and have to) be ran before moving on to the |
209 // next token. | 209 // next token. |
210 | 210 |
211 // Creates an action that discards tokens until it finds one of | 211 // Creates an action that discards tokens until it finds one of |
212 // the given type. | 212 // the given type. |
213 function expect(wanted){ | 213 function expect(wanted){ |
214 return function(type){ | 214 return function(type){ |
215 if (type == wanted) cont(); | 215 if (type == wanted) cont(); |
216 else cont(arguments.callee); | 216 else cont(arguments.callee); |
217 }; | 217 }; |
218 } | 218 } |
219 | 219 |
220 // Looks for a statement, and then calls itself. | 220 // Looks for a statement, and then calls itself. |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
307 } | 307 } |
308 // A function definition creates a new context, and the variables | 308 // A function definition creates a new context, and the variables |
309 // in its argument list have to be added to this context. | 309 // in its argument list have to be added to this context. |
310 function functiondef(type, value){ | 310 function functiondef(type, value){ |
311 if (type == "variable"){register(value); cont(functiondef);} | 311 if (type == "variable"){register(value); cont(functiondef);} |
312 else if (type == "(") cont(pushcontext, commasep(funarg), expect(")"), sta
tement, popcontext); | 312 else if (type == "(") cont(pushcontext, commasep(funarg), expect(")"), sta
tement, popcontext); |
313 } | 313 } |
314 function funarg(type, value){ | 314 function funarg(type, value){ |
315 if (type == "variable"){register(value); cont();} | 315 if (type == "variable"){register(value); cont();} |
316 } | 316 } |
317 | 317 |
318 return parser; | 318 return parser; |
319 } | 319 } |
320 | 320 |
321 return {make: parseJS, electricChars: "{}"}; | 321 return {make: parseJS, electricChars: "{}"}; |
322 })(); | 322 })(); |
OLD | NEW |