Skip to content

Commit f895b27

Browse files
Merge pull request #10 from ShaderFrog/location-updates
Test updates and minor refactor of location information
2 parents 59dfda8 + bd5004c commit f895b27

File tree

6 files changed

+171
-118
lines changed

6 files changed

+171
-118
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
The [Shaderfrog](https://shaderfrog.com/app) GLSL compiler is an open source
44
GLSL 1.00 and 3.00 parser and preprocessor that compiles [back to
5-
GLSL](parser/generator.ts). Both the parser and preprocessor can preserve
5+
GLSL](src/parser/generator.ts). Both the parser and preprocessor can preserve
66
comments and whitespace.
77

88
The parser uses PEG grammar via the Peggy Javascript library. The PEG grammars
@@ -49,7 +49,7 @@ Where `options` is:
4949
// The origin of the GLSL, for debugging. For example, "main.js", If the
5050
// parser raises an error (specifically a GrammarError), and you call
5151
// error.format([]) on it, the error shows { source: 'main.js', ... }
52-
grammarSource: string | object,
52+
grammarSource: string,
5353
// If true, sets location information on each AST node, in the form of
5454
// { column: number, line: number, offset: number }
5555
includeLocation: boolean

package-lock.json

Lines changed: 23 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"engines": {
44
"node": ">=16"
55
},
6-
"version": "1.3.0",
6+
"version": "1.4.0",
77
"description": "A GLSL ES 1.0 and 3.0 parser and preprocessor that can preserve whitespace and comments",
88
"scripts": {
99
"prepare": "npm run build && ./prepublish.sh",

src/parser/glsl-grammar.pegjs

Lines changed: 66 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,6 @@
66
// https://github.com/pegjs/pegjs/issues/187
77
const OPEN_CURLY = String.fromCharCode(123);
88

9-
const makeScope = (name, parent) => ({
10-
name,
11-
parent,
12-
bindings: {},
13-
types: {},
14-
functions: {},
15-
});
16-
179
// Types (aka struct) scope
1810
const addTypes = (scope, ...types) => {
1911
types.forEach(([identifier, type]) => {
@@ -293,20 +285,68 @@
293285

294286
// Per-parse initializations
295287
{
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,
297299
// so node() is moved to per-parse scope
298300
const node = (type, attrs) => {
299301
const n = {
300302
type,
301303
...attrs,
302304
}
303305
if(options.includeLocation) {
304-
const { start, end } = location();
305-
n.location = { start, end }
306+
n.location = getLocation();
306307
}
307308
return n;
308309
};
309310

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+
310350
// Group the statements in a switch statement into cases / default arrays
311351
const groupCases = (statements) => statements.reduce((cases, stmt) => {
312352
const partial = stmt.partial || {};
@@ -349,37 +389,15 @@
349389
}];
350390
}
351391
}, []);
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-
};
376392
}
377393

378-
// Extra whitespace here at start is to help with screenshots by adding
379-
// extra linebreaks
394+
// Entrypoint to parsing!
380395
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 });
382399
}
400+
383401
// "compatibility profile only and vertex language only; same as in when in a
384402
// vertex shader"
385403
ATTRIBUTE = token:"attribute" t:terminal { return node('keyword', { token, whitespace: t }); }
@@ -1080,7 +1098,7 @@ function_header_new_scope "function header"
10801098
'function_header',
10811099
{ returnType, name, lp }
10821100
);
1083-
scope = pushScope(makeScope(name.identifier, scope));
1101+
scope = pushScope(makeScope(name.identifier, scope, lp.location));
10841102
return n;
10851103
}
10861104

@@ -1407,12 +1425,9 @@ compound_statement =
14071425
})
14081426
statements:statement_list?
14091427
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);
14161431

14171432
scope = popScope(scope);
14181433

@@ -1505,12 +1520,9 @@ iteration_statement "iteration statement"
15051520
condition:condition
15061521
rp:RIGHT_PAREN
15071522
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);
15141526

15151527
scope = popScope(scope);
15161528

@@ -1561,11 +1573,8 @@ iteration_statement "iteration statement"
15611573
operation:expression?
15621574
rp:RIGHT_PAREN
15631575
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);
15691578

15701579
scope = popScope(scope);
15711580

@@ -1669,10 +1678,7 @@ function_definition =
16691678

16701679
const n = node('function', { prototype, body });
16711680

1672-
if(options.includeLocation){
1673-
let end = body.rb.location.start;
1674-
scope.location.end = end;
1675-
}
1681+
setScopeEnd(scope, body.rb.location?.start);
16761682

16771683
scope = popScope(scope);
16781684

0 commit comments

Comments
 (0)