add some stuffs

This commit is contained in:
Rowan 2025-04-16 22:21:46 -05:00
commit 35ba1a129f
12 changed files with 520 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
node_modules/

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "vendor/kuebiko"]
path = vendor/kuebiko
url = git@git.kitsu.cafe:rowan/kuebiko.git

22
package-lock.json generated Normal file
View file

@ -0,0 +1,22 @@
{
"name": "wgsl-reflect",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "wgsl-reflect",
"version": "1.0.0",
"license": "ISC",
"devDependencies": {
"folktest": "git+https://git.kitsu.cafe/rowan/folktest.git"
}
},
"node_modules/folktest": {
"version": "1.0.0",
"resolved": "git+https://git.kitsu.cafe/rowan/folktest.git#708d44f1215be33fcceba426029f44b4f963dbe5",
"dev": true,
"license": "GPL-3.0-or-later"
}
}
}

16
package.json Normal file
View file

@ -0,0 +1,16 @@
{
"name": "wgsl-reflect",
"version": "1.0.0",
"main": "index.js",
"type": "module",
"scripts": {
"test": "./tests/index.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"devDependencies": {
"folktest": "git+https://git.kitsu.cafe/rowan/folktest.git"
}
}

77
src/index.js Normal file
View file

@ -0,0 +1,77 @@
const shader = `// A fragment shader which lights textured geometry with point lights.
// Lights from a storage buffer binding.
struct PointLight {
position : vec3f,
color : vec3f,
}
struct LightStorage {
pointCount : u32,
point : array<PointLight>,
}
@group(0) @binding(0) var<storage> lights : LightStorage;
// Texture and sampler.
@group(1) @binding(0) var baseColorSampler : sampler;
@group(1) @binding(1) var baseColorTexture : texture_2d<f32>;
// Function arguments are values from the vertex shader.
@fragment
fn fragmentMain(@location(0) worldPos : vec3f,
@location(1) normal : vec3f,
@location(2) uv : vec2f) -> @location(0) vec4f {
// Sample the base color of the surface from a texture.
let baseColor = textureSample(baseColorTexture, baseColorSampler, uv);
let N = normalize(normal);
var surfaceColor = vec3f(0);
// Loop over the scene point lights.
for (var i = 0u; i < lights.pointCount; i++) {
let worldToLight = lights.point[i].position - worldPos;
let dist = length(worldToLight);
let dir = normalize(worldToLight);
// Determine the contribution of this light to the surface color.
let radiance = lights.point[i].color * (1 / pow(dist, 2));
let nDotL = max(dot(N, dir), 0);
// Accumulate light contribution to the surface color.
surfaceColor += baseColor.rgb * radiance * nDotL;
}
// Return the accumulated surface color.
return vec4(surfaceColor, baseColor.a);
}`
/**
*
3.1 Parsing
Remove comments:
Replace the first comment with a space code point (U+0020).
Repeat until no comments remain.
Find template lists, using the algorithm in §3.9 Template Lists.
Parse the whole text, attempting to match the translation_unit grammar rule. Parsing uses a LALR(1) parser (one token of lookahead) [DeRemer1969], with the following customization:
Tokenization is interleaved with parsing, and is context-aware. When the parser requests the next token:
Consume and ignore an initial sequence of blankspace code points.
If the next code point is the start of a template list, consume it and return _template_args_start.
If the next code point is the end of a template list, consume it and return _template_args_end.
Otherwise:
A token candidate is any WGSL token formed from the non-empty prefix of the remaining unconsumed code points.
The token returned is the longest token candidate that is also a valid lookahead token for the current parser state. [VanWyk2007]
*/

16
src/parser/comments.js Normal file
View file

@ -0,0 +1,16 @@
import { char, until } from '../../vendor/kuebiko/src/index.js'
// https://www.w3.org/TR/WGSL/#comments
const slash = char('\u002f')
const asterisk = char('\u002a')
const comment = seq(slash, slash)
const blockCommentStart = seq(slash, asterisk)
const blockCommentEnd = seq(asterisk, slash)
const blockComment = seq(
blockCommentStart,
until(blockCommentEnd)
)

188
src/parser/keyword.js Normal file
View file

@ -0,0 +1,188 @@
import { any, anyChar, char, many, not, seq, str, until } from '../../vendor/kuebiko/src/index.js'
export const ident = seq(
not(any(
char('_'),
seq(str('__'), many(anyChar)),
)),
until(' ')
)
const Keyword = [
'alias',
'break',
'case',
'const',
'const_assert',
'continue',
'continuing',
'default',
'diagnostic',
'discard',
'else',
'enable',
'false',
'fn',
'for',
'if',
'let',
'loop',
'override',
'requires',
'return',
'struct',
'switch',
'true',
'var',
'while'
]
const Reserved = [
'NULL',
'Self',
'abstract',
'active',
'alignas',
'alignof',
'as',
'asm',
'asm_fragment',
'async',
'attribute',
'auto',
'await',
'become',
'cast',
'catch',
'class',
'co_await',
'co_return',
'co_yield',
'coherent',
'column_major',
'common',
'compile',
'compile_fragment',
'concept',
'const_cast',
'consteval',
'constexpr',
'constinit',
'crate',
'debugger',
'decltype',
'delete',
'demote',
'demote_to_helper',
'do',
'dynamic_cast',
'enum',
'explicit',
'export',
'extends',
'extern',
'external',
'fallthrough',
'filter',
'final',
'finally',
'friend',
'from',
'fxgroup',
'get',
'goto',
'groupshared',
'highp',
'impl',
'implements',
'import',
'inline',
'instanceof',
'interface',
'layout',
'lowp',
'macro',
'macro_rules',
'match',
'mediump',
'meta',
'mod',
'module',
'move',
'mut',
'mutable',
'namespace',
'new',
'nil',
'noexcept',
'noinline',
'nointerpolation',
'non_coherent',
'noncoherent',
'noperspective',
'null',
'nullptr',
'of',
'operator',
'package',
'packoffset',
'partition',
'pass',
'patch',
'pixelfragment',
'precise',
'precision',
'premerge',
'priv',
'protected',
'pub',
'public',
'readonly',
'ref',
'regardless',
'register',
'reinterpret_cast',
'require',
'resource',
'restrict',
'self',
'set',
'shared',
'sizeof',
'smooth',
'snorm',
'static',
'static_assert',
'static_cast',
'std',
'subroutine',
'super',
'target',
'template',
'this',
'thread_local',
'throw',
'trait',
'try',
'type',
'typedef',
'typeid',
'typename',
'typeof',
'union',
'unless',
'unorm',
'unsafe',
'unsized',
'use',
'using',
'varying',
'virtual',
'volatile',
'wgsl',
'where',
'with',
'writeonly',
'yield'
]

145
src/parser/literal.js Normal file
View file

@ -0,0 +1,145 @@
import { any, anyOf, digit, maybe, seq, str } from '../../vendor/kuebiko/src/index.js'
import { many } from '../../vendor/kuebiko/src/index.js'
const iu = anyOf('iu')
const decimal = char('.')
const sign = anyOf('+-')
const e = anyOf('eE')
const p = anyOf('pP')
const fh = anyOf('fh')
export const zero = char('0')
export const digits = many(digit)
export const booleanLiteral = any(str('true'), str('false'))
export const decimalIntLiteral = any(
zero,
seq(
not(zero),
digits,
maybe(iu)
)
)
const hexDigit = any(digit, anyOf('abcdef'), anyOf('ABCDEF'))
const hexPrefix = seq(zero, anyOf('xX'))
const hexIntLiteral = seq(
hexPrefix,
hexDigit,
maybe(iu)
)
export const integerLiteral = any(
decimalIntLiteral,
hexIntLiteral
)
const nonzeroDigit = anyOf('123456789')
const many1 = parser => seq(parser, many(parser))
const exponent = seq(
e,
sign,
many1(digit)
)
const fhDecimalFloatLiteral = seq(
any(
zero,
seq(
nonzeroDigit,
digits
)
),
fh
)
const eDecimalFloatLiteral = seq(
digits,
exponent,
fh
)
const cDecimalFloatLiteral = seq(
any(
seq(
digits,
decimal,
many1(digit)
),
seq(
many1(digit),
decimal,
digits
)
),
maybe(seq(exponent, fh)),
)
const decimalFloatLiteral = any(
fhDecimalFloatLiteral,
eDecimalFloatLiteral,
cDecimalFloatLiteral
)
const sHexFloatLiteral = seq(
hexPrefix,
any(
seq(
many(hexDigit),
decimal,
many1(hexDigit)
),
seq(
many1(hexDigit),
decimal,
many(hexDigit)
)
),
maybe(
seq(
p,
maybe(sign),
many1(digit),
fh
)
)
)
const zHexFloatLiteral = seq(
hexPrefix,
many1(hexDigit),
p,
maybe(sign),
many1(digit),
maybe(fh)
)
const hexFloatLiteral = any(
sHexFloatLiteral,
zHexFloatLiteral
)
export const floatingPointLiteral = any(
decimalFloatLiteral,
hexFloatLiteral
)
export const numericLiteral = any(
integerLiteral,
floatingPointLiteral
)
export const literal = any(
numericLiteral,
booleanLiteral
)

25
src/parser/whitespace.js Normal file
View file

@ -0,0 +1,25 @@
import { any, char, seq } from '../../vendor/kuebiko/src/index.js'
// https://www.w3.org/TR/WGSL/#blankspace-and-line-breaks
export const space = char('\u0020')
export const ht = char('\u0009')
export const lf = char('\u000a')
export const vt = char('\u000b')
export const ff = char('\u000c')
export const cr = char('\u000d')
export const nel = char('\u000d')
export const ltr = char('\u200e')
export const rtl = char('\u200f')
export const ls = char('\u200f')
export const ps = char('\u2029')
export const blankspace = any(
space, ht, lf, vt, ff,
cr, nel, ltr, rtl, ls
)
export const linebreak = any(
lf, vt, ff, cr, nel, ls, ps
)

8
tests/index.js Executable file
View file

@ -0,0 +1,8 @@
#!/usr/bin/env node
import { TerminalRunner } from 'folktest'
import * as Tests from './units/index.js'
console.log(TerminalRunner(Tests).toString())

17
tests/units/index.js Normal file
View file

@ -0,0 +1,17 @@
import { it, assert } from 'folktest'
import { ident } from '../../src/parser/keyword.js'
export const Tests = [
it('whatever', () => {
const pass1 = 'test1'
const pass2 = '_Test2'
const fail1 = '_'
const fail2 = '__test'
const fail3 = 'default'
console.log(
ident
)
})
]

1
vendor/kuebiko vendored Submodule

@ -0,0 +1 @@
Subproject commit 74bac48730187475160fe6cf3cdb4bc89c8e6c0b