|
6 | 6 | // https://github.com/pegjs/pegjs/issues/187 |
7 | 7 | const OPEN_CURLY = String.fromCharCode(123); |
8 | 8 |
|
9 | | - const makeScope = (name, parent) => ({ |
10 | | - name, |
11 | | - parent, |
12 | | - bindings: {}, |
13 | | - types: {}, |
14 | | - functions: {}, |
15 | | - }); |
16 | | - |
17 | 9 | // Types (aka struct) scope |
18 | 10 | const addTypes = (scope, ...types) => { |
19 | 11 | types.forEach(([identifier, type]) => { |
|
293 | 285 |
|
294 | 286 | // Per-parse initializations |
295 | 287 | { |
296 | | - // location() (and etc. functions) are not available in global scope, |
| 288 | + const getLocation = (loc) => { |
| 289 | + // Try to avoid calling getLocation() more than neccessary |
| 290 | + if(!options.includeLocation) { |
| 291 | + return; |
| 292 | + } |
| 293 | + // Intentionally drop the "source" and "offset" keys from the location object |
| 294 | + const { start, end } = loc || location(); |
| 295 | + return { start, end }; |
| 296 | + } |
| 297 | + |
| 298 | + // getLocation() (and etc. functions) are not available in global scope, |
297 | 299 | // so node() is moved to per-parse scope |
298 | 300 | const node = (type, attrs) => { |
299 | 301 | const n = { |
300 | 302 | type, |
301 | 303 | ...attrs, |
302 | 304 | } |
303 | 305 | if(options.includeLocation) { |
304 | | - const { start, end } = location(); |
305 | | - n.location = { start, end } |
| 306 | + n.location = getLocation(); |
306 | 307 | } |
307 | 308 | return n; |
308 | 309 | }; |
309 | 310 |
|
| 311 | + const makeScope = (name, parent, startLocation) => { |
| 312 | + let newLocation = getLocation(startLocation); |
| 313 | + |
| 314 | + return { |
| 315 | + name, |
| 316 | + parent, |
| 317 | + ...(newLocation ? { location: newLocation } : false), |
| 318 | + bindings: {}, |
| 319 | + types: {}, |
| 320 | + functions: {}, |
| 321 | + }; |
| 322 | + }; |
| 323 | + |
| 324 | + const warn = (...args) => !options.quiet && console.warn(...args); |
| 325 | + |
| 326 | + let scope = makeScope('global'); |
| 327 | + let scopes = [scope]; |
| 328 | + |
| 329 | + const pushScope = scope => { |
| 330 | + // console.log('pushing scope at ',text()); |
| 331 | + scopes.push(scope); |
| 332 | + return scope; |
| 333 | + }; |
| 334 | + const popScope = scope => { |
| 335 | + // console.log('popping scope at ',text()); |
| 336 | + if(!scope.parent) { |
| 337 | + throw new Error('popped bad scope', scope, 'at', text()); |
| 338 | + } |
| 339 | + return scope.parent; |
| 340 | + }; |
| 341 | + const setScopeEnd = (scope, end) => { |
| 342 | + if(options.includeLocation) { |
| 343 | + if(!scope.location) { |
| 344 | + console.error('no end location at', text()); |
| 345 | + } |
| 346 | + scope.location.end = end; |
| 347 | + } |
| 348 | + }; |
| 349 | + |
310 | 350 | // Group the statements in a switch statement into cases / default arrays |
311 | 351 | const groupCases = (statements) => statements.reduce((cases, stmt) => { |
312 | 352 | const partial = stmt.partial || {}; |
|
349 | 389 | }]; |
350 | 390 | } |
351 | 391 | }, []); |
352 | | - |
353 | | - const warn = (...args) => !options.quiet && console.warn(...args); |
354 | | - |
355 | | - let scope = makeScope('global'); |
356 | | - let scopes = [scope]; |
357 | | - |
358 | | - const pushScope = scope => { |
359 | | - // console.log('pushing scope at ',text()); |
360 | | - |
361 | | - if(options.includeLocation){ |
362 | | - let { start } = location(); |
363 | | - scope.location = { start }; |
364 | | - } |
365 | | - |
366 | | - scopes.push(scope); |
367 | | - return scope; |
368 | | - }; |
369 | | - const popScope = scope => { |
370 | | - // console.log('popping scope at ',text()); |
371 | | - if(!scope.parent) { |
372 | | - throw new Error('popped bad scope', scope, 'at', text()); |
373 | | - } |
374 | | - return scope.parent; |
375 | | - }; |
376 | 392 | } |
377 | 393 |
|
378 | | -// Extra whitespace here at start is to help with screenshots by adding |
379 | | -// extra linebreaks |
| 394 | +// Entrypoint to parsing! |
380 | 395 | start = wsStart:_ program:translation_unit { |
381 | | - return { type: 'program', wsStart, program, scopes }; |
| 396 | + // Set the global scope end to the end of the program |
| 397 | + setScopeEnd(scope, getLocation()?.end); |
| 398 | + return node('program', { wsStart, program, scopes }); |
382 | 399 | } |
| 400 | + |
383 | 401 | // "compatibility profile only and vertex language only; same as in when in a |
384 | 402 | // vertex shader" |
385 | 403 | ATTRIBUTE = token:"attribute" t:terminal { return node('keyword', { token, whitespace: t }); } |
@@ -1080,7 +1098,7 @@ function_header_new_scope "function header" |
1080 | 1098 | 'function_header', |
1081 | 1099 | { returnType, name, lp } |
1082 | 1100 | ); |
1083 | | - scope = pushScope(makeScope(name.identifier, scope)); |
| 1101 | + scope = pushScope(makeScope(name.identifier, scope, lp.location)); |
1084 | 1102 | return n; |
1085 | 1103 | } |
1086 | 1104 |
|
@@ -1407,12 +1425,9 @@ compound_statement = |
1407 | 1425 | }) |
1408 | 1426 | statements:statement_list? |
1409 | 1427 | rb:RIGHT_BRACE { |
1410 | | - |
1411 | | - if(options.includeLocation){ |
1412 | | - // using start of right bracket, so trailing whitespace is not counted towards scope range |
1413 | | - let end = rb.location.start; |
1414 | | - scope.location.end = end; |
1415 | | - } |
| 1428 | + // Use start of right bracket, so trailing whitespace is not counted towards |
| 1429 | + // scope range |
| 1430 | + setScopeEnd(scope, rb.location?.start); |
1416 | 1431 |
|
1417 | 1432 | scope = popScope(scope); |
1418 | 1433 |
|
@@ -1505,12 +1520,9 @@ iteration_statement "iteration statement" |
1505 | 1520 | condition:condition |
1506 | 1521 | rp:RIGHT_PAREN |
1507 | 1522 | body:statement_no_new_scope { |
1508 | | - |
1509 | | - if(options.includeLocation){ |
1510 | | - // use right bracket or fallback to location.end |
1511 | | - let end = body.rb ? body.rb.location.start : body.location.end; |
1512 | | - scope.location.end = end; |
1513 | | - } |
| 1523 | + // use right bracket or fallback to location.end |
| 1524 | + const end = body.rb ? body.rb.location?.start : body.location?.end; |
| 1525 | + setScopeEnd(scope, end); |
1514 | 1526 |
|
1515 | 1527 | scope = popScope(scope); |
1516 | 1528 |
|
@@ -1561,11 +1573,8 @@ iteration_statement "iteration statement" |
1561 | 1573 | operation:expression? |
1562 | 1574 | rp:RIGHT_PAREN |
1563 | 1575 | body:statement_no_new_scope { |
1564 | | - |
1565 | | - if(options.includeLocation){ |
1566 | | - let end = body.rb ? body.rb.location.start : body.location.end; |
1567 | | - scope.location.end = end; |
1568 | | - } |
| 1576 | + const end = body.rb ? body.rb.location?.start : body.location?.end; |
| 1577 | + setScopeEnd(scope, end); |
1569 | 1578 |
|
1570 | 1579 | scope = popScope(scope); |
1571 | 1580 |
|
@@ -1669,10 +1678,7 @@ function_definition = |
1669 | 1678 |
|
1670 | 1679 | const n = node('function', { prototype, body }); |
1671 | 1680 |
|
1672 | | - if(options.includeLocation){ |
1673 | | - let end = body.rb.location.start; |
1674 | | - scope.location.end = end; |
1675 | | - } |
| 1681 | + setScopeEnd(scope, body.rb.location?.start); |
1676 | 1682 |
|
1677 | 1683 | scope = popScope(scope); |
1678 | 1684 |
|
|
0 commit comments