glre 0.33.0 → 0.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/node/infer.ts CHANGED
@@ -1,108 +1,91 @@
1
1
  import { is } from '../utils/helpers'
2
2
  import {
3
- BOOL_RETURN_FUNCTIONS,
4
3
  BUILTIN_TYPES,
5
4
  COMPARISON_OPERATORS,
6
5
  COMPONENT_COUNT_TO_TYPE,
7
- CONSTANTS,
8
- FIRST_ARG_TYPE_FUNCTIONS,
9
- HIGHEST_TYPE_FUNCTIONS,
6
+ FUNCTION_RETURN_TYPES,
10
7
  LOGICAL_OPERATORS,
11
- PRESERVE_TYPE_FUNCTIONS,
12
- SCALAR_RETURN_FUNCTIONS,
13
- VEC3_RETURN_FUNCTIONS,
14
- VEC4_RETURN_FUNCTIONS,
15
8
  } from './const'
16
9
  import { isConstants, isNodeProxy, isSwizzle } from './utils'
17
- import type { Constants, NodeContext, NodeProxy, X } from './types'
10
+ import type { Constants as C, NodeContext, NodeProxy, X } from './types'
18
11
 
19
- const getHighestPriorityType = (args: X[], c: NodeContext) => {
20
- return args.reduce((highest, current) => {
21
- const currentType = infer(current, c)
22
- const highestPriority = CONSTANTS.indexOf(highest as any)
23
- const currentPriority = CONSTANTS.indexOf(currentType as any)
24
- return currentPriority > highestPriority ? currentType : highest
25
- }, 'float') as Constants
12
+ const inferBuiltin = <T extends C>(id: string | undefined) => {
13
+ return BUILTIN_TYPES[id as keyof typeof BUILTIN_TYPES] as T
26
14
  }
27
15
 
28
- const inferBuiltin = (id: string | undefined): Constants => {
29
- return BUILTIN_TYPES[id as keyof typeof BUILTIN_TYPES]!
16
+ // Unified logic with types.ts InferOperator type
17
+ const inferOperator = <T extends C>(L: T, R: T, op: string): T => {
18
+ if (COMPARISON_OPERATORS.includes(op as any) || LOGICAL_OPERATORS.includes(op as any)) return 'bool' as T
19
+ if (L === R) return L
20
+ // broadcast
21
+ if (L === 'float' || L === 'int') return R
22
+ if (R === 'float' || R === 'int') return L
23
+ // mat * vec → vec
24
+ if (L === 'mat4' && R === 'vec4') return R
25
+ if (L === 'mat3' && R === 'vec3') return R
26
+ if (L === 'mat2' && R === 'vec2') return R
27
+ // vec * mat → vec
28
+ if (L === 'vec4' && R === 'mat4') return L
29
+ if (L === 'vec3' && R === 'mat3') return L
30
+ if (L === 'vec2' && R === 'mat2') return L
31
+ return L
30
32
  }
31
33
 
32
- const inferFunction = (funcName: string, args: X[], c: NodeContext): Constants => {
33
- const firstArgType = args.length > 0 ? infer(args[0], c) : 'float'
34
- if (FIRST_ARG_TYPE_FUNCTIONS.includes(funcName as any)) return firstArgType
35
- if (SCALAR_RETURN_FUNCTIONS.includes(funcName as any)) return 'float'
36
- if (BOOL_RETURN_FUNCTIONS.includes(funcName as any)) return 'bool'
37
- if (PRESERVE_TYPE_FUNCTIONS.includes(funcName as any)) return firstArgType
38
- if (VEC3_RETURN_FUNCTIONS.includes(funcName as any)) return 'vec3'
39
- if (VEC4_RETURN_FUNCTIONS.includes(funcName as any)) return 'vec4'
40
- if (HIGHEST_TYPE_FUNCTIONS.includes(funcName as any)) return getHighestPriorityType(args, c)
41
- return firstArgType
34
+ export const inferPrimitiveType = <T extends C>(x: X) => {
35
+ if (is.bol(x)) return 'bool' as T
36
+ if (is.str(x)) return 'texture' as T
37
+ if (is.num(x)) return 'float' as T // @TODO FIX: Number.isInteger(x) ? 'int' : 'float'
38
+ if (is.arr(x)) return COMPONENT_COUNT_TO_TYPE[x.length as keyof typeof COMPONENT_COUNT_TO_TYPE] as T
39
+ return 'float' as T
42
40
  }
43
41
 
44
- const inferOperator = (leftType: string, rightType: string, op: string): Constants => {
45
- if (COMPARISON_OPERATORS.includes(op as any)) return 'bool'
46
- if (LOGICAL_OPERATORS.includes(op as any)) return 'bool'
47
- if (leftType === rightType) return leftType as Constants
48
- if (leftType.includes('vec') && !rightType.includes('vec')) return leftType as Constants
49
- if (rightType.includes('vec') && !leftType.includes('vec')) return rightType as Constants
50
- const leftPriority = CONSTANTS.indexOf(leftType as any)
51
- const rightPriority = CONSTANTS.indexOf(rightType as any)
52
- return (leftPriority >= rightPriority ? leftType : rightType) as Constants
42
+ const inferFromCount = <T extends C>(count: number) => {
43
+ return COMPONENT_COUNT_TO_TYPE[count as keyof typeof COMPONENT_COUNT_TO_TYPE] as T
53
44
  }
54
45
 
55
- export const inferPrimitiveType = (x: any): Constants => {
56
- if (is.bol(x)) return 'bool'
57
- if (is.str(x)) return 'texture'
58
- if (is.num(x)) return Number.isInteger(x) ? 'int' : 'float'
59
- if (is.arr(x)) return COMPONENT_COUNT_TO_TYPE[x.length as keyof typeof COMPONENT_COUNT_TO_TYPE] || 'float'
60
- return 'float'
61
- }
62
-
63
- const inferFromCount = (count: number): Constants => {
64
- return COMPONENT_COUNT_TO_TYPE[count as keyof typeof COMPONENT_COUNT_TO_TYPE]!
65
- }
66
-
67
- const inferFromArray = (arr: X[], c: NodeContext): Constants => {
68
- if (arr.length === 0) return 'void'
46
+ const inferFromArray = <T extends C>(arr: X<T>[], c: NodeContext) => {
47
+ if (arr.length === 0) return 'void' as T
69
48
  const [x] = arr
70
- if (is.str(x)) return x as Constants // for struct
49
+ if (is.str(x)) return x as T // for struct
71
50
  const ret = infer(x, c)
72
51
  for (const x of arr.slice(1))
73
52
  if (ret !== infer(x, c)) throw new Error(`glre node system error: defined scope return mismatch`)
74
53
  return ret
75
54
  }
76
55
 
77
- export const inferImpl = (target: NodeProxy, c: NodeContext): Constants => {
56
+ export const inferFunction = <T extends C>(x: X) => {
57
+ return FUNCTION_RETURN_TYPES[x as keyof typeof FUNCTION_RETURN_TYPES] as T
58
+ }
59
+
60
+ export const inferImpl = <T extends C>(target: NodeProxy<T>, c: NodeContext): T => {
78
61
  const { type, props } = target
79
- const { id, children = [], layout, inferFrom } = props
62
+ const { id, children = [], inferFrom, layout } = props
80
63
  const [x, y, z] = children
81
- if (type === 'conversion') return x as Constants
82
- if (type === 'operator') return inferOperator(infer(y, c), infer(z, c), x as string)
83
- if (type === 'function') return inferFunction(x as string, children.slice(1), c)
64
+ if (type === 'conversion') return x
65
+ if (type === 'operator') return inferOperator(infer(y, c), infer(z, c), x)
84
66
  if (type === 'ternary') return inferOperator(infer(y, c), infer(z, c), 'add')
85
67
  if (type === 'builtin') return inferBuiltin(id)
86
- if (type === 'define' && isConstants(layout?.type)) return layout?.type
68
+ if (type === 'function') return inferFunction(x) || infer(y, c)
69
+ if (type === 'define' && isConstants(layout?.type)) return layout?.type as T
87
70
  if (type === 'attribute' && is.arr(x) && c.gl?.count) return inferFromCount(x.length / c.gl.count)
88
71
  if (type === 'member') {
89
72
  if (isSwizzle(x)) return inferFromCount(x.length)
90
73
  if (isNodeProxy(y) && is.str(x)) {
91
- const field = y.props.fields?.[x] // for variable node of struct member
74
+ const field = (y as any).props.fields?.[x] // for variable node of struct member
92
75
  if (field) return infer(field, c)
93
76
  }
94
- return 'float' // fallback @TODO FIX
77
+ return 'float' as T // fallback @TODO FIX
95
78
  }
96
79
  if (inferFrom) return inferFromArray(inferFrom, c)
97
- return infer(x, c)
80
+ return infer(x, c) // for uniform
98
81
  }
99
82
 
100
- export const infer = (target: X, c?: NodeContext | null): Constants => {
83
+ export const infer = <T extends C>(target: X<T>, c?: NodeContext | null): T => {
101
84
  if (!c) c = {}
102
85
  if (!isNodeProxy(target)) return inferPrimitiveType(target)
103
86
  if (is.arr(target)) return inferFromCount(target.length)
104
- if (!c.infers) c.infers = new WeakMap<NodeProxy, Constants>()
105
- if (c.infers.has(target)) return c.infers.get(target)!
87
+ if (!c.infers) c.infers = new WeakMap<NodeProxy<T>, C>()
88
+ if (c.infers.has(target)) return c.infers.get(target) as T
106
89
  const ret = inferImpl(target, c)
107
90
  c.infers.set(target, ret)
108
91
  return ret
package/src/node/node.ts CHANGED
@@ -2,13 +2,13 @@ import { is } from '../utils/helpers'
2
2
  import { code } from './code'
3
3
  import { assign, toVar } from './scope'
4
4
  import { conversionToConstant, isConversion, isFunction, isOperator, getId } from './utils'
5
- import type { Functions, NodeProps, NodeProxy, NodeTypes, Operators, X } from './types'
5
+ import type { Functions, NodeProps, NodeProxy, NodeTypes, Operators, X, Constants as C } from './types'
6
6
 
7
7
  const toPrimitive = (x: X, hint: string) => {
8
8
  if (hint === 'string') return code(x)
9
9
  }
10
10
 
11
- export const node = (type: NodeTypes, props?: NodeProps | null, ...args: X[]) => {
11
+ export const node = <T extends C>(type: NodeTypes, props?: NodeProps | null, ...args: X[]) => {
12
12
  if (!props) props = {}
13
13
  if (args.length) props.children = args
14
14
  const listeners = new Set<(value: any) => void>()
@@ -24,28 +24,30 @@ export const node = (type: NodeTypes, props?: NodeProps | null, ...args: X[]) =>
24
24
  if (isOperator(key)) return (...y: X[]) => operator(key, x, ...y)
25
25
  if (isFunction(key)) return (...y: X[]) => function_(key, x, ...y)
26
26
  if (isConversion(key)) return () => conversion(conversionToConstant(key), x)
27
- if (is.str(key)) return member(key, x) // for struct
27
+ if (is.str(key)) return member(key, x) // for struct and swizzling
28
28
  }
29
29
  const set = (_: unknown, key: string, y: X) => {
30
30
  if (key === 'value') listeners.forEach((fun) => fun(y))
31
31
  if (is.str(key)) member(key, x).assign(y)
32
32
  return true
33
33
  }
34
- const x = new Proxy({}, { get, set }) as unknown as NodeProxy
34
+ const x = new Proxy({}, { get, set }) as unknown as NodeProxy<T>
35
35
  return x
36
36
  }
37
37
 
38
- // headers
39
- export const attribute = (x: X, id = getId()) => node('attribute', { id }, x)
40
- export const constant = (x: X, id = getId()) => node('constant', { id }, x)
41
- export const uniform = (x: X, id = getId()) => node('uniform', { id }, x)
42
- export const variable = (id = getId()) => node('variable', { id })
43
- export const builtin = (id = getId()) => node('builtin', { id })
44
- export const vertexStage = (x: X, id = getId()) => node('varying', { id, inferFrom: [x] }, x)
38
+ // headers with proper type inference
39
+ export const attribute = <T extends C>(x: X, id = getId()) => node<T>('attribute', { id }, x)
40
+ export const constant = <T extends C>(x: X<T>, id = getId()) => node<T>('constant', { id }, x)
41
+ export const uniform = <T extends C>(x: X<T>, id = getId()) => node<T>('uniform', { id }, x)
42
+ export const variable = <T extends C>(id = getId()) => node<T>('variable', { id })
43
+ export const builtin = <T extends C>(id = getId()) => node<T>('builtin', { id })
44
+ export const vertexStage = <T extends C>(x: X<T>, id = getId()) => {
45
+ return node<T>('varying', { id, inferFrom: [x] }, x)
46
+ }
45
47
 
46
- // Node shorthands
47
- export const operator = (key: Operators, ...x: X[]) => node('operator', null, key, ...x)
48
- export const function_ = (key: Functions, ...x: X[]) => node('function', null, key, ...x)
49
- export const conversion = (key: string, ...x: X[]) => node('conversion', null, key, ...x)
50
- export const member = (key: string, x: X) => node('member', null, key, x)
51
- export const select = (x: X, y: X, z: X) => node('ternary', null, x, y, z) // x ? y : z @TODO REMOVE
48
+ // Node shorthands with proper typing
49
+ export const member = <T extends C>(key: string, x: X) => node<T>('member', null, key, x)
50
+ export const select = <T extends C>(x: X<T>, y: X<T>, z: X) => node<T>('ternary', null, x, y, z) // z ? x : y @TODO REMOVE
51
+ export const operator = <T extends C>(key: Operators, ...x: X[]) => node<T>('operator', null, key, ...x)
52
+ export const function_ = <T extends C>(key: Functions, ...x: X[]) => node<T>('function', null, key, ...x)
53
+ export const conversion = <T extends C>(key: T, ...x: X[]) => node<T>('conversion', null, key, ...x)
package/src/node/parse.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { is } from '../utils/helpers'
2
2
  import { code } from './code'
3
3
  import { infer } from './infer'
4
- import { formatConversions, isConstants, addDependency } from './utils'
4
+ import { formatConversions, addDependency } from './utils'
5
5
  import type { Constants, NodeContext, NodeProps, NodeProxy, X } from './types'
6
6
 
7
7
  export const parseArray = (children: X[], c: NodeContext) => {
@@ -58,37 +58,6 @@ export const parseDeclare = (c: NodeContext, x: X, y: X) => {
58
58
  return `var ${varName}: ${wgslType} = ${code(x, c)};`
59
59
  }
60
60
 
61
- export const parseDefine = (c: NodeContext, props: NodeProps, returnType: Constants | string) => {
62
- const { id, children = [], layout } = props
63
- const [x, ...args] = children
64
- const argParams: [name: string, type: string][] = []
65
- const params: string[] = []
66
- if (layout?.inputs)
67
- for (const input of layout.inputs) {
68
- argParams.push([input.name, input.type])
69
- }
70
- else
71
- for (let i = 0; i < args.length; i++) {
72
- argParams.push([`p${i}`, infer(args[i], c)])
73
- }
74
- const ret = []
75
- if (c?.isWebGL) {
76
- for (const [paramId, type] of argParams) {
77
- addDependency(c, id!, type)
78
- params.push(`${type} ${paramId}`)
79
- }
80
- addDependency(c, id!, returnType)
81
- ret.push(`${returnType} ${id}(${params}) {`)
82
- } else {
83
- for (const [paramId, type] of argParams) params.push(`${paramId}: ${formatConversions(type, c)}`)
84
- ret.push(`fn ${id}(${params}) -> ${formatConversions(returnType, c)} {`)
85
- }
86
- const scopeCode = code(x, c)
87
- if (scopeCode) ret.push(scopeCode)
88
- ret.push('}')
89
- return ret.join('\n')
90
- }
91
-
92
61
  export const parseStructHead = (c: NodeContext, id: string, fields: Record<string, NodeProxy> = {}) => {
93
62
  const lines: string[] = []
94
63
  for (const key in fields) {
@@ -123,11 +92,47 @@ export const parseStruct = (
123
92
  }
124
93
  }
125
94
 
95
+ /**
96
+ * define
97
+ */
98
+ export const parseDefine = (c: NodeContext, props: NodeProps, returnType: Constants) => {
99
+ const { id, children = [], layout } = props
100
+ const [x, ...args] = children
101
+ const argParams: [name: string, type: string][] = []
102
+ const params: string[] = []
103
+ if (layout?.inputs)
104
+ for (const input of layout.inputs) {
105
+ argParams.push([input.name, input.type])
106
+ }
107
+ else
108
+ for (let i = 0; i < args.length; i++) {
109
+ argParams.push([`p${i}`, infer(args[i], c)])
110
+ }
111
+ const ret = []
112
+ if (c?.isWebGL) {
113
+ for (const [paramId, type] of argParams) {
114
+ addDependency(c, id!, type)
115
+ params.push(`${type} ${paramId}`)
116
+ }
117
+ addDependency(c, id!, returnType)
118
+ ret.push(`${returnType} ${id}(${params}) {`)
119
+ } else {
120
+ for (const [paramId, type] of argParams) params.push(`${paramId}: ${formatConversions(type, c)}`)
121
+ ret.push(`fn ${id}(${params}) -> ${formatConversions(returnType, c)} {`)
122
+ }
123
+ const scopeCode = code(x, c)
124
+ if (scopeCode) ret.push(scopeCode)
125
+ ret.push('}')
126
+ return ret.join('\n')
127
+ }
128
+
126
129
  /**
127
130
  * headers
128
131
  */
129
132
  export const parseVaryingHead = (c: NodeContext, id: string, type: string) => {
130
- return c.isWebGL ? `${type} ${id};` : `@location(${c.code?.vertVaryings?.size || 0}) ${id}: ${formatConversions(type, c)}`
133
+ return c.isWebGL
134
+ ? `${type} ${id};`
135
+ : `@location(${c.code?.vertVaryings?.size || 0}) ${id}: ${formatConversions(type, c)}`
131
136
  }
132
137
 
133
138
  export const parseUniformHead = (c: NodeContext, id: string, type: Constants) => {
package/src/node/scope.ts CHANGED
@@ -1,7 +1,6 @@
1
- import { int } from '.'
2
1
  import { conversion, node } from './node'
3
2
  import { getId } from './utils'
4
- import type { FnLayout, NodeProps, NodeProxy, X } from './types'
3
+ import type { FnLayout, NodeProps, NodeProxy, X, Constants, Int } from './types'
5
4
 
6
5
  let scope: NodeProxy | null = null
7
6
  let define: NodeProxy | null = null
@@ -16,29 +15,28 @@ const addToScope = (x: NodeProxy) => {
16
15
  props.inferFrom.push(x)
17
16
  }
18
17
 
19
- export const toVar = (x: X, id?: string) => {
18
+ export const toVar = <T extends Constants>(x: X<T>, id?: string): NodeProxy<T> => {
20
19
  if (!id) id = getId()
21
- const y = node('variable', { id, inferFrom: [x] })
20
+ const y = node<T>('variable', { id, inferFrom: [x] })
22
21
  const z = node('declare', null, x, y)
23
22
  addToScope(z)
24
23
  return y
25
24
  }
26
25
 
27
- export const assign = (x: X, y: X) => {
26
+ export const assign = <T extends Constants>(x: X<T>, y: X<T>): X<T> => {
28
27
  const z = node('assign', null, x, y)
29
28
  addToScope(z)
30
29
  return x
31
30
  }
32
31
 
33
- export const Return = (x: X) => {
34
- const y = node('return', { inferFrom: [x] }, x)
32
+ export const Return = <T extends Constants>(x: X<T>): NodeProxy<T> => {
33
+ const y = node<T>('return', { inferFrom: [x] }, x)
35
34
  addToScope(y)
36
35
  return y
37
36
  }
38
37
 
39
- // Struct functions
40
- export const struct = (fields: Record<string, NodeProxy>, id = getId()) => {
41
- return (initialValues: Record<string, NodeProxy> = {}, instanceId = getId()) => {
38
+ export const struct = <T extends Record<string, NodeProxy>>(fields: T, id = getId()) => {
39
+ return (initialValues: T = {} as T, instanceId = getId()) => {
42
40
  const x = node('variable', { id: instanceId, inferFrom: [id] })
43
41
  const y = node('struct', { id, fields, initialValues }, x)
44
42
  addToScope(y)
@@ -61,7 +59,7 @@ const scoped = (x: NodeProxy, fun: () => NodeProxy | void, y = define) => {
61
59
  define = _define
62
60
  }
63
61
 
64
- export const If = (x: X, fun: () => void) => {
62
+ export const If = (x: NodeProxy, fun: () => void) => {
65
63
  const y = node('scope')
66
64
  scoped(y, fun)
67
65
  const ifNode = node('if', null, x, y)
@@ -82,15 +80,16 @@ export const If = (x: X, fun: () => void) => {
82
80
  return ret()
83
81
  }
84
82
 
85
- export const Loop = (x: X, fun: (params: { i: NodeProxy }) => void) => {
83
+ export const Loop = (x: NodeProxy, fun: (params: { i: Int }) => void) => {
86
84
  const y = node('scope')
87
- scoped(y, () => fun({ i: node('variable', { id: 'i', inferFrom: [int(0)] }) }))
88
- const ret = node('loop', null, x, y)
85
+ const id = getId()
86
+ scoped(y, () => fun({ i: node<'int'>('variable', { id, inferFrom: [conversion('int', 0)] }) }))
87
+ const ret = node('loop', { id }, x, y)
89
88
  addToScope(ret)
90
89
  return ret
91
90
  }
92
91
 
93
- export const Switch = (x: X) => {
92
+ export const Switch = (x: NodeProxy) => {
94
93
  const switchNode = node('switch', null, x)
95
94
  addToScope(switchNode)
96
95
  const ret = () => ({
@@ -111,10 +110,13 @@ export const Switch = (x: X) => {
111
110
  return ret()
112
111
  }
113
112
 
114
- export const Fn = (fun: (paramVars: NodeProxy[]) => NodeProxy) => {
113
+ export const Fn = <ReturnType extends Constants, Args extends Constants[]>(
114
+ fun: (paramVars: NodeProxy[]) => NodeProxy<ReturnType> | void,
115
+ defaultId = getId()
116
+ ) => {
115
117
  let layout: FnLayout
116
- const ret = (...args: X[]) => {
117
- const id = layout?.name || getId()
118
+ const ret = (...args: X<Args[number]>[]): NodeProxy<ReturnType> => {
119
+ const id = layout?.name || defaultId
118
120
  const x = node('scope')
119
121
  const paramVars: NodeProxy[] = []
120
122
  const paramDefs: NodeProps[] = []
@@ -127,10 +129,13 @@ export const Fn = (fun: (paramVars: NodeProxy[]) => NodeProxy) => {
127
129
  paramDefs.push({ id: `p${i}`, inferFrom: [args[i]] })
128
130
  }
129
131
  for (const props of paramDefs) paramVars.push(node('variable', props))
130
- const y = node('define', { id, layout }, x, ...args)
132
+ const y = node<ReturnType>('define', { id, layout }, x, ...args)
131
133
  scoped(x, () => fun(paramVars), y)
132
134
  return y
133
135
  }
134
- ret.setLayout = (_layout: FnLayout) => void (layout = _layout)
136
+ ret.setLayout = (_layout: FnLayout) => {
137
+ layout = _layout
138
+ return ret
139
+ }
135
140
  return ret
136
141
  }