Skip to content

CodyJasonBennett/shaderkit

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Size Version Downloads

shaderkit

Tools and IntelliSense for GLSL and WGSL.

Table of Contents

Installation

To install, use your preferred package manager:

npm install shaderkit
yarn add shaderkit
pnpm add shaderkit

Or, use a CDN:

<script type="module">
  import * as shaderkit from 'https://unpkg.com/shaderkit'
</script>

Tokenize

Tokenizes a string of GLSL or WGSL code, returning an array of Token objects, where each Token object represents a single syntax feature in the input code.

interface Token {
  type: 'whitespace' | 'comment' | 'symbol' | 'bool' | 'float' | 'int' | 'identifier' | 'keyword'
  value: string
}
GLSL Example
import { tokenize } from 'shaderkit'

const code = 'void main() { gl_Position = vec4(0, 0, 0, 1); }'
const tokens = tokenize(code)

console.log(tokens)

The output of the above code will be:

[
  { "type": "keyword", "value": "void" },
  { "type": "whitespace", "value": " " },
  { "type": "identifier", "value": "main" },
  { "type": "symbol", "value": "(" },
  { "type": "symbol", "value": ")" },
  { "type": "whitespace", "value": " " },
  { "type": "symbol", "value": "{" },
  { "type": "whitespace", "value": " " },
  { "type": "keyword", "value": "gl_Position" },
  { "type": "whitespace", "value": " " },
  { "type": "symbol", "value": "=" },
  { "type": "whitespace", "value": " " },
  { "type": "keyword", "value": "vec4" },
  { "type": "symbol", "value": "(" },
  { "type": "int", "value": "0" },
  { "type": "symbol", "value": "," },
  { "type": "whitespace", "value": " " },
  { "type": "int", "value": "0" },
  { "type": "symbol", "value": "," },
  { "type": "whitespace", "value": " " },
  { "type": "int", "value": "0" },
  { "type": "symbol", "value": "," },
  { "type": "whitespace", "value": " " },
  { "type": "int", "value": "1" },
  { "type": "symbol", "value": ")" },
  { "type": "symbol", "value": ";" },
  { "type": "whitespace", "value": " " },
  { "type": "symbol", "value": "}" }
]
WGSL Example
import { tokenize } from 'shaderkit'

const code = '@vertex fn main() -> @builtin(position) vec4<f32> { return vec4(0, 0, 0, 1); }'
const tokens = tokenize(code)

console.log(tokens)

The output of the above code will be:

[
  { "type": "symbol", "value": "@" },
  { "type": "keyword", "value": "vertex" },
  { "type": "whitespace", "value": " " },
  { "type": "keyword", "value": "fn" },
  { "type": "whitespace", "value": " " },
  { "type": "identifier", "value": "main" },
  { "type": "symbol", "value": "(" },
  { "type": "symbol", "value": ")" },
  { "type": "whitespace", "value": " " },
  { "type": "symbol", "value": "->" },
  { "type": "whitespace", "value": " " },
  { "type": "symbol", "value": "@" },
  { "type": "keyword", "value": "builtin" },
  { "type": "symbol", "value": "(" },
  { "type": "keyword", "value": "position" },
  { "type": "symbol", "value": ")" },
  { "type": "whitespace", "value": " " },
  { "type": "keyword", "value": "vec4" },
  { "type": "symbol", "value": "<" },
  { "type": "keyword", "value": "f32" },
  { "type": "symbol", "value": ">" },
  { "type": "whitespace", "value": " " },
  { "type": "symbol", "value": "{" },
  { "type": "whitespace", "value": " " },
  { "type": "keyword", "value": "return" },
  { "type": "whitespace", "value": " " },
  { "type": "keyword", "value": "vec4" },
  { "type": "symbol", "value": "(" },
  { "type": "int", "value": "0" },
  { "type": "symbol", "value": "," },
  { "type": "whitespace", "value": " " },
  { "type": "int", "value": "0" },
  { "type": "symbol", "value": "," },
  { "type": "whitespace", "value": " " },
  { "type": "int", "value": "0" },
  { "type": "symbol", "value": "," },
  { "type": "whitespace", "value": " " },
  { "type": "int", "value": "1" },
  { "type": "symbol", "value": ")" },
  { "type": "symbol", "value": ";" },
  { "type": "whitespace", "value": " " },
  { "type": "symbol", "value": "}" }
]

The following are the supported token types and their descriptions:

Type Description
whitespace A sequence of one or more whitespace characters.
comment A single-line or multi-line comment.
symbol A symbol, such as an operator or punctuation mark.
bool A boolean value, either true or false.
float A floating-point number, represented by a sequence of digits and symbols.
int An integer number, represented by a sequence of digits.
identifier A user-defined identifier, such as a variable name or function name.
keyword A keyword reserved by the language, such as if, else, for, etc.

Minify

Minifies a string of GLSL or WGSL code, returning a minified version of the input code.

const minified: string = minify(code: string, {
  /** Whether to rename variables. Will call a MangleMatcher if specified. Default is `false`. */
  mangle: boolean | ((token: Token, index: number, tokens: Token[]) => boolean)
  /** A map to read and write renamed variables to when mangling. */
  mangleMap: Map<string, string>
  /** Whether to rename external variables such as uniforms or varyings. Default is `false`. */
  mangleExternals: boolean
})

To shared mangled interfaces when using mangleExternal, declare and re-use a mangleMap between shaders:

const options = { mangle: true, mangleExternals: true, mangleMap: new Map() }

// #version 300 es\nin vec2 a;out vec2 b;void main(){b=a;}
minify(`#version 300 es\nin vec2 sstt;out vec2 c;void main(){c=sstt;}`, options)

// #version 300 es\nin vec2 b;out vec4 a[gl_MaxDrawBuffers];void main(){a[0]=b.sstt;}
minify(`#version 300 es\nin vec2 c;out vec4 data[gl_MaxDrawBuffers];void main(){data[0]=c.sstt;}`, options)

Parse

Parses a string of GLSL (WGSL is WIP) code into an AST.

const ast: Program = parse(code: string)

Generate

Generates a string of GLSL (WGSL is WIP) code from an AST.

const code: string = generate(program: Program, {
  target: 'GLSL' // | 'WGSL'
})

Visit

Recurses through an AST, calling a visitor object on matching nodes.

visit(
  program: Program,
  visitors: {
    Program: {
      enter(node, ancestors) {
        // Called before any descendant nodes are processed
      },
      exit(node, ancestors) {
        // Called after all nodes are processed
      }
    },
    Identifier(node, ancestors) {
      // Called before any descendant nodes are processed (alias to enter)
    }
  } satisfies Visitors
)

AST

An Abstract Syntax Tree loosely based on ESTree for GLSL and WGSL grammars.

Node Objects

AST nodes extend Node objects which implement the following abstract interface:

interface Node {
  type: string
}

The type field is a string representing the AST variant type which can determine the interface a node implements.

Identifier

A variable identifier.

interface Identifier extends Node {
  type: 'Identifier'
  name: string
}

Literal

A shader literal representing a bool, float, int, or uint type.

interface Literal extends Node {
  type: 'Literal'
  value: string
}

ArraySpecifier

An array and its dimensions.

interface ArraySpecifier extends Node {
  type: 'ArraySpecifier'
  typeSpecifier: Identifier
  dimensions: (Literal | Identifier | null)[]
}

Program

Represents the root of an AST.

interface Program extends Node {
  type: 'Program'
  body: Statement[]
}

ExpressionStatement

An expression as a standalone statement.

interface ExpressionStatement extends Node {
  type: 'ExpressionStatement'
  expression: Expression
}

BlockStatement

A block statement.

interface BlockStatement extends Node {
  type: 'BlockStatement'
  body: Statement[]
}

DiscardStatement

A discard statement in fragment shaders.

interface DiscardStatement extends Node {
  type: 'DiscardStatement'
}

PreprocessorStatement

A GLSL preprocessor statement with an optional value.

interface PreprocessorStatement extends Node {
  type: 'PreprocessorStatement'
  name: string
  value: Expression[] | null
}

PrecisionQualifierStatement

A GLSL precision qualifier statement.

interface PrecisionQualifierStatement extends Node {
  type: 'PrecisionQualifierStatement'
  precision: PrecisionQualifier
  typeSpecifier: Identifier
}

InvariantQualifierStatement

A GLSL invariant qualifier statement.

interface InvariantQualifierStatement extends Node {
  type: 'InvariantQualifierStatement'
  typeSpecifier: Identifier
}

LayoutQualifierStatement

A layout qualifier statement.

interface LayoutQualifierStatement extends Node {
  type: 'LayoutQualifierStatement'
  layout: Record<string, string | boolean>
  qualifier: StorageQualifier
}

ReturnStatement

A return statement with an optional argument.

interface ReturnStatement extends Node {
  type: 'ReturnStatement'
  argument: Expression | null
}

BreakStatement

A break statement.

interface BreakStatement extends Node {
  type: 'BreakStatement'
}

ContinueStatement

A continue statement.

interface ContinueStatement extends Node {
  type: 'ContinueStatement'
}

IfStatement

An if-else statement.

interface IfStatement extends Node {
  type: 'IfStatement'
  test: Expression
  consequent: Statement
  alternate: Statement | null
}

SwitchStatement

A switch statement.

interface SwitchStatement extends Node {
  type: 'SwitchStatement'
  discriminant: Expression
  cases: SwitchCase[]
}

SwitchCase

A switch-case statement. test is null for a default case.

interface SwitchCase extends Node {
  type: 'SwitchCase'
  test: Expression | null
  consequent: Statement[]
}

WhileStatement

A while statement.

interface WhileStatement extends Node {
  type: 'WhileStatement'
  test: Expression
  body: Statement
}

DoWhileStatement

A do-while statement.

interface DoWhileStatement extends Node {
  type: 'DoWhileStatement'
  body: Statement
  test: Expression
}

ForStatement

A for statement.

interface ForStatement extends Node {
  type: 'ForStatement'
  init: VariableDeclaration | Expression | null
  test: Expression | null
  update: Expression | null
  body: Statement
}

FunctionDeclaration

A function declaration. body is null for overloads.

interface FunctionDeclaration extends Node {
  type: 'FunctionDeclaration'
  id: Identifier
  qualifiers: PrecisionQualifier[]
  typeSpecifier: Identifier | ArraySpecifier
  params: FunctionParameter[]
  body: BlockStatement | null
}

FunctionParameter

A function parameter within a function declaration.

interface FunctionParameter extends Node {
  type: 'FunctionParameter'
  id: Identifier | null
  qualifiers: (ConstantQualifier | ParameterQualifier | PrecisionQualifier)[]
  typeSpecifier: Identifier | ArraySpecifier
}

VariableDeclaration

A variable declaration.

interface VariableDeclaration extends Node {
  type: 'VariableDeclaration'
  declarations: VariableDeclarator[]
}

VariableDeclarator

A variable declarator within a variable declaration.

interface VariableDeclarator extends Node {
  type: 'VariableDeclarator'
  id: Identifier
  qualifiers: (ConstantQualifier | InterpolationQualifier | StorageQualifier | PrecisionQualifier)[]
  typeSpecifier: Identifier | ArraySpecifier
  layout: Record<string, string | boolean> | null
  init: Expression | null
}

StructuredBufferDeclaration

A buffer interface declaration with optional layout and qualifiers.

interface StructuredBufferDeclaration extends Node {
  type: 'StructuredBufferDeclaration'
  id: Identifier | null
  qualifiers: (InterfaceStorageQualifier | MemoryQualifier | LayoutQualifier)[]
  typeSpecifier: Identifier | ArraySpecifier
  layout: Record<string, string | boolean> | null
  members: VariableDeclaration[]
}

StructDeclaration

A struct declaration. Can be used as a type or constructor.

interface StructDeclaration extends Node {
  type: 'StructDeclaration'
  id: Identifier
  members: VariableDeclaration[]
}

ArrayExpression

An array initialization expression.

interface ArrayExpression extends Node {
  type: 'ArrayExpression'
  typeSpecifier: ArraySpecifier
  elements: Expression[]
}

UnaryExpression

A unary expression with a left or right handed operator.

interface UnaryExpression extends Node {
  type: 'UnaryExpression'
  operator: UnaryOperator
  prefix: boolean
  argument: Expression
}

UnaryOperator

type UnaryOperator = '-' | '+' | '!' | '~'

UpdateExpression

An update expression with an optionally prefixed operator.

interface UpdateExpression extends Node {
  type: 'UpdateExpression'
  operator: UpdateOperator
  argument: Expression
  prefix: boolean
}

UpdateOperator

type UpdateOperator = '++' | '--'

BinaryExpression

A binary expression with a left and right operand.

interface BinaryExpression extends Node {
  type: 'BinaryExpression'
  operator: BinaryOperator
  left: Expression
  right: Expression
}

BinaryOperator

type BinaryOperator =
  | '=='
  | '!='
  | '<'
  | '<='
  | '>'
  | '>='
  | '<<'
  | '>>'
  | '+'
  | '-'
  | '*'
  | '/'
  | '%'
  | '|'
  | '^'
  | '&'

AssignmentExpression

An assignment expression.

interface AssignmentExpression extends Node {
  type: 'AssignmentExpression'
  operator: AssignmentOperator
  left: Expression
  right: Expression
}

AssignmentOperator

type AssignmentOperator = '=' | '+=' | '-=' | '*=' | '/=' | '%=' | '<<=' | '>>=' | '>>>=' | '|=' | '^=' | '&='

LogicalExpression

A logical operation between two expressions.

interface LogicalExpression extends Node {
  type: 'LogicalExpression'
  operator: LogicalOperator
  left: Expression
  right: Expression
}

LogicalOperator

type LogicalOperator = '||' | '&&' | '^^'

MemberExpression

A member expression.

interface MemberExpression extends Node {
  type: 'MemberExpression'
  object: Expression
  property: Expression
  computed: boolean
}

ConditionalExpression

A conditional expression or ternary.

interface ConditionalExpression extends Node {
  type: 'ConditionalExpression'
  test: Expression
  alternate: Expression
  consequent: Expression
}

CallExpression

A function call expression or struct initialization.

interface CallExpression extends Node {
  type: 'CallExpression'
  callee: Expression
  arguments: Expression[]
}