Skip to content

Commit 4bdd969

Browse files
fix: add InvariantStatement (#28)
1 parent add7796 commit 4bdd969

File tree

9 files changed

+77
-3
lines changed

9 files changed

+77
-3
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Tools and IntelliSense for GLSL and WGSL.
2626
- [DiscardStatement](#discardstatement)
2727
- [PreprocessorStatement](#preprocessorstatement)
2828
- [PrecisionStatement](#precisionstatement)
29+
- [InvariantStatement](#invariantstatement)
2930
- Control Flow
3031
- [ReturnStatement](#returnstatement)
3132
- [BreakStatement](#breakstatement)
@@ -406,6 +407,17 @@ interface PrecisionStatement extends Node {
406407
}
407408
```
408409

410+
### InvariantStatement
411+
412+
A GLSL invariant statement.
413+
414+
```ts
415+
interface InvariantStatement extends Node {
416+
type: 'InvariantStatement'
417+
typeSpecifier: Identifier
418+
}
419+
```
420+
409421
### ReturnStatement
410422

411423
A return statement with an optional argument.

src/ast.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,14 @@ export interface PrecisionStatement extends Node {
361361
typeSpecifier: Identifier
362362
}
363363

364+
/**
365+
* A GLSL invariant statement.
366+
*/
367+
export interface InvariantStatement extends Node {
368+
type: 'InvariantStatement'
369+
typeSpecifier: Identifier
370+
}
371+
364372
export type Expression =
365373
| Literal
366374
| Identifier
@@ -392,6 +400,7 @@ export type Statement =
392400
| StructDeclaration
393401
| PreprocessorStatement
394402
| PrecisionStatement
403+
| InvariantStatement
395404

396405
export type AST =
397406
| Program

src/generator.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ function format(node: AST | null): string {
3737
}
3838
case 'PrecisionStatement':
3939
return `precision ${node.precision} ${node.typeSpecifier.name};`
40+
case 'InvariantStatement':
41+
return `invariant ${format(node.typeSpecifier)};`
4042
case 'ReturnStatement':
4143
return node.argument ? `return ${format(node.argument)};` : 'return;'
4244
case 'BreakStatement':

src/parser.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
Identifier,
1616
IfStatement,
1717
InterpolationQualifier,
18+
InvariantStatement,
1819
LayoutQualifier,
1920
Literal,
2021
LogicalOperator,
@@ -594,6 +595,13 @@ function parsePreprocessor(tokens: Token[]): PreprocessorStatement {
594595
return { type: 'PreprocessorStatement', name, value }
595596
}
596597

598+
function parseInvariant(tokens: Token[]): InvariantStatement {
599+
consume(tokens, 'invariant')
600+
const typeSpecifier = parseExpression(tokens) as Identifier
601+
consume(tokens, ';')
602+
return { type: 'InvariantStatement', typeSpecifier }
603+
}
604+
597605
function parseStatements(tokens: Token[]): Statement[] {
598606
const body: Statement[] = []
599607
let scopeIndex = 0
@@ -619,6 +627,7 @@ function parseStatements(tokens: Token[]): Statement[] {
619627
else if (token.value === 'do') statement = parseDoWhile(tokens)
620628
else if (token.value === 'switch') statement = parseSwitch(tokens)
621629
else if (token.value === 'precision') statement = parsePrecision(tokens)
630+
else if (token.value === 'invariant') statement = parseInvariant(tokens)
622631
else if (VARIABLE_REGEX.test(token.value) && tokens[1].value !== '[') statement = parseIndeterminate(tokens)
623632
else {
624633
const expression = parseExpression(tokens)

src/visitor.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ export function visit(node: AST, visitors: Visitors, ancestors: AST[] = []): voi
3838
case 'PrecisionStatement':
3939
visit(node.typeSpecifier, visitors, ancestors)
4040
break
41+
case 'InvariantStatement':
42+
visit(node.typeSpecifier, visitors, ancestors)
43+
break
4144
case 'ReturnStatement':
4245
if (node.argument) visit(node.argument, visitors, ancestors)
4346
break

tests/__snapshots__/index.test.ts.snap

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ int y;
1616
#else
1717
float z;
1818
#endif
19-
};struct f{float intensity;vec3 position;float one,two;};uniform f Light[4];void main(){vec4 g=vec4(Light[0].position.xyz*Light[0].intensity,0.0);vec4 h=projectionMatrix*modelViewMatrix*vec4(0,0,0,1);vec4 i=d.projectionMatrix*d.modelViewMatrix*vec4(0,0,0,1);if(false){}pc_FragColor=vec4(texture(map,vUv).rgb,0.0);float j=0.0;pc_FragColor.a+=1.0+j;}"
19+
};struct f{float intensity;vec3 position;float one,two;};uniform f Light[4];invariant g;void main(){vec4 h=vec4(Light[0].position.xyz*Light[0].intensity,0.0);vec4 i=projectionMatrix*modelViewMatrix*vec4(0,0,0,1);vec4 j=d.projectionMatrix*d.modelViewMatrix*vec4(0,0,0,1);if(false){}g=vec4(texture(map,vUv).rgb,0.0);float k=0.0;g.a+=1.0+k;}"
2020
`;
2121
2222
exports[`minify > can mangle GLSL 2`] = `
@@ -52,7 +52,7 @@ int y;
5252
#else
5353
float z;
5454
#endif
55-
};struct m{float intensity;vec3 position;float one,two;};uniform m n[4];void main(){vec4 o=vec4(n[0].position.xyz*n[0].intensity,0.0);vec4 p=projectionMatrix*modelViewMatrix*vec4(0,0,0,1);vec4 q=k.projectionMatrix*k.modelViewMatrix*vec4(0,0,0,1);if(false){}h=vec4(texture(f,g).rgb,0.0);float e=0.0;h.a+=1.0+e;}"
55+
};struct m{float intensity;vec3 position;float one,two;};uniform m n[4];invariant h;void main(){vec4 o=vec4(n[0].position.xyz*n[0].intensity,0.0);vec4 p=projectionMatrix*modelViewMatrix*vec4(0,0,0,1);vec4 q=k.projectionMatrix*k.modelViewMatrix*vec4(0,0,0,1);if(false){}h=vec4(texture(f,g).rgb,0.0);float e=0.0;h.a+=1.0+e;}"
5656
`;
5757
5858
exports[`minify > can mangle externals in GLSL 2`] = `
@@ -116,7 +116,7 @@ int y;
116116
#else
117117
float z;
118118
#endif
119-
};struct LightData{float intensity;vec3 position;float one,two;};uniform LightData Light[4];void main(){vec4 lightNormal=vec4(Light[0].position.xyz*Light[0].intensity,0.0);vec4 clipPosition=projectionMatrix*modelViewMatrix*vec4(0,0,0,1);vec4 clipPositionGlobals=globals.projectionMatrix*globals.modelViewMatrix*vec4(0,0,0,1);if(false){}pc_FragColor=vec4(texture(map,vUv).rgb,0.0);float bar=0.0;pc_FragColor.a+=1.0+bar;}"
119+
};struct LightData{float intensity;vec3 position;float one,two;};uniform LightData Light[4];invariant pc_FragColor;void main(){vec4 lightNormal=vec4(Light[0].position.xyz*Light[0].intensity,0.0);vec4 clipPosition=projectionMatrix*modelViewMatrix*vec4(0,0,0,1);vec4 clipPositionGlobals=globals.projectionMatrix*globals.modelViewMatrix*vec4(0,0,0,1);if(false){}pc_FragColor=vec4(texture(map,vUv).rgb,0.0);float bar=0.0;pc_FragColor.a+=1.0+bar;}"
120120
`;
121121
122122
exports[`minify > can minify WGSL 1`] = `"struct LightData{intensity:f32,position:vec3<f32>,one:f32,two:f32,};struct Uniforms{projectionMatrix:mat4x4<f32>,modelViewMatrix:mat4x4<f32>,normalMatrix:mat3x3<f32>,one:f32,two:f32,lights:array<LightData,4>,};@binding(0)@group(0)var<uniform>uniforms:Uniforms;@binding(1)@group(0)var sample:sampler;@binding(2)@group(0)var map:texture_2d<f32>;struct VertexIn{@location(0)position:vec4<f32>,@location(1)uv:vec2<f32>,};struct VertexOut{@builtin(position)position:vec4<f32>,@location(0)uv:vec2<f32>,};@vertex fn vert_main(input:VertexIn)->VertexOut{var output:VertexOut;output.position=input.position;output.uv=input.uv;return output;}@fragment fn frag_main(uv:vec2<f32>)->vec4<f32>{var lightNormal=vec4<f32>(uniforms.lights[0].position*uniforms.lights[0].intensity,0.0);var clipPosition=uniforms.projectionMatrix*uniforms.modelViewMatrix*vec4<f32>(0.0,0.0,0.0,1.0);var color=textureSample(map,sample,uv);color.a+=1.0;return color;}"`;
@@ -1228,6 +1228,28 @@ exports[`tokenize > can tokenize GLSL 1`] = `
12281228
"type": "whitespace",
12291229
"value": "
12301230
1231+
",
1232+
},
1233+
{
1234+
"type": "keyword",
1235+
"value": "invariant",
1236+
},
1237+
{
1238+
"type": "whitespace",
1239+
"value": " ",
1240+
},
1241+
{
1242+
"type": "identifier",
1243+
"value": "pc_FragColor",
1244+
},
1245+
{
1246+
"type": "symbol",
1247+
"value": ";",
1248+
},
1249+
{
1250+
"type": "whitespace",
1251+
"value": "
1252+
12311253
",
12321254
},
12331255
{

tests/generator.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ const glsl = /* glsl */ `#version 300 es
5555
};
5656
uniform LightData Light[4];
5757
58+
invariant pc_FragColor;
59+
5860
void main() {
5961
vec4 lightNormal = vec4(Light[0].position.xyz * Light[0].intensity, 0.0);
6062
vec4 clipPosition = projectionMatrix * modelViewMatrix * vec4(0, 0, 0, 1);

tests/index.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ const glsl = /* glsl */ `#version 300 es
5555
};
5656
uniform LightData Light[4];
5757
58+
invariant pc_FragColor;
59+
5860
void main() {
5961
vec4 lightNormal = vec4(Light[0].position.xyz * Light[0].intensity, 0.0);
6062
vec4 clipPosition = projectionMatrix * modelViewMatrix * vec4(0, 0, 0, 1);

tests/parser.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
WhileStatement,
2323
Identifier,
2424
ArraySpecifier,
25+
InvariantStatement,
2526
} from 'shaderkit'
2627

2728
describe('parser', () => {
@@ -773,6 +774,18 @@ describe('parser', () => {
773774
])
774775
})
775776

777+
it('parses invariant statements', () => {
778+
expect(parse('invariant position;').body).toStrictEqual<[InvariantStatement]>([
779+
{
780+
type: 'InvariantStatement',
781+
typeSpecifier: {
782+
name: 'position',
783+
type: 'Identifier',
784+
},
785+
},
786+
])
787+
})
788+
776789
it('parses preprocessor statements', () => {
777790
expect(parse('#\n').body).toStrictEqual<[PreprocessorStatement]>([
778791
{

0 commit comments

Comments
 (0)