gradient-script 0.1.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.
Files changed (52) hide show
  1. package/README.md +515 -0
  2. package/dist/cli.d.ts +2 -0
  3. package/dist/cli.js +136 -0
  4. package/dist/dsl/AST.d.ts +123 -0
  5. package/dist/dsl/AST.js +23 -0
  6. package/dist/dsl/BuiltIns.d.ts +58 -0
  7. package/dist/dsl/BuiltIns.js +181 -0
  8. package/dist/dsl/CSE.d.ts +21 -0
  9. package/dist/dsl/CSE.js +194 -0
  10. package/dist/dsl/CodeGen.d.ts +60 -0
  11. package/dist/dsl/CodeGen.js +474 -0
  12. package/dist/dsl/Differentiation.d.ts +45 -0
  13. package/dist/dsl/Differentiation.js +421 -0
  14. package/dist/dsl/DiscontinuityAnalyzer.d.ts +18 -0
  15. package/dist/dsl/DiscontinuityAnalyzer.js +75 -0
  16. package/dist/dsl/Errors.d.ts +22 -0
  17. package/dist/dsl/Errors.js +49 -0
  18. package/dist/dsl/Expander.d.ts +13 -0
  19. package/dist/dsl/Expander.js +220 -0
  20. package/dist/dsl/ExpressionTransformer.d.ts +54 -0
  21. package/dist/dsl/ExpressionTransformer.js +102 -0
  22. package/dist/dsl/ExpressionUtils.d.ts +55 -0
  23. package/dist/dsl/ExpressionUtils.js +175 -0
  24. package/dist/dsl/GradientChecker.d.ts +71 -0
  25. package/dist/dsl/GradientChecker.js +258 -0
  26. package/dist/dsl/Guards.d.ts +27 -0
  27. package/dist/dsl/Guards.js +206 -0
  28. package/dist/dsl/Inliner.d.ts +10 -0
  29. package/dist/dsl/Inliner.js +40 -0
  30. package/dist/dsl/Lexer.d.ts +63 -0
  31. package/dist/dsl/Lexer.js +243 -0
  32. package/dist/dsl/Parser.d.ts +92 -0
  33. package/dist/dsl/Parser.js +328 -0
  34. package/dist/dsl/Simplify.d.ts +17 -0
  35. package/dist/dsl/Simplify.js +276 -0
  36. package/dist/dsl/TypeInference.d.ts +39 -0
  37. package/dist/dsl/TypeInference.js +147 -0
  38. package/dist/dsl/Types.d.ts +58 -0
  39. package/dist/dsl/Types.js +114 -0
  40. package/dist/index.d.ts +13 -0
  41. package/dist/index.js +11 -0
  42. package/dist/symbolic/AST.d.ts +113 -0
  43. package/dist/symbolic/AST.js +128 -0
  44. package/dist/symbolic/CodeGen.d.ts +35 -0
  45. package/dist/symbolic/CodeGen.js +280 -0
  46. package/dist/symbolic/Parser.d.ts +64 -0
  47. package/dist/symbolic/Parser.js +329 -0
  48. package/dist/symbolic/Simplify.d.ts +10 -0
  49. package/dist/symbolic/Simplify.js +244 -0
  50. package/dist/symbolic/SymbolicDiff.d.ts +35 -0
  51. package/dist/symbolic/SymbolicDiff.js +339 -0
  52. package/package.json +56 -0
@@ -0,0 +1,243 @@
1
+ /**
2
+ * Lexer for GradientScript DSL
3
+ * Tokenizes input with support for ∇, ^, **, and structured types
4
+ */
5
+ export var TokenType;
6
+ (function (TokenType) {
7
+ // Literals
8
+ TokenType["NUMBER"] = "NUMBER";
9
+ TokenType["IDENTIFIER"] = "IDENTIFIER";
10
+ // Keywords
11
+ TokenType["FUNCTION"] = "FUNCTION";
12
+ TokenType["RETURN"] = "RETURN";
13
+ // Operators
14
+ TokenType["PLUS"] = "PLUS";
15
+ TokenType["MINUS"] = "MINUS";
16
+ TokenType["MULTIPLY"] = "MULTIPLY";
17
+ TokenType["DIVIDE"] = "DIVIDE";
18
+ TokenType["POWER"] = "POWER";
19
+ TokenType["POWER_ALT"] = "POWER_ALT";
20
+ TokenType["NABLA"] = "NABLA";
21
+ // Delimiters
22
+ TokenType["LPAREN"] = "LPAREN";
23
+ TokenType["RPAREN"] = "RPAREN";
24
+ TokenType["LBRACE"] = "LBRACE";
25
+ TokenType["RBRACE"] = "RBRACE";
26
+ TokenType["COMMA"] = "COMMA";
27
+ TokenType["COLON"] = "COLON";
28
+ TokenType["DOT"] = "DOT";
29
+ TokenType["EQUALS"] = "EQUALS";
30
+ // Special
31
+ TokenType["NEWLINE"] = "NEWLINE";
32
+ TokenType["EOF"] = "EOF";
33
+ })(TokenType || (TokenType = {}));
34
+ export class Lexer {
35
+ input;
36
+ position = 0;
37
+ line = 1;
38
+ column = 1;
39
+ constructor(input) {
40
+ this.input = input;
41
+ }
42
+ /**
43
+ * Get all tokens
44
+ */
45
+ tokenize() {
46
+ const tokens = [];
47
+ let token = this.nextToken();
48
+ while (token.type !== TokenType.EOF) {
49
+ if (token.type !== TokenType.NEWLINE) {
50
+ // Skip newlines for now (treat as whitespace)
51
+ tokens.push(token);
52
+ }
53
+ token = this.nextToken();
54
+ }
55
+ tokens.push(token); // EOF token
56
+ return tokens;
57
+ }
58
+ /**
59
+ * Get next token
60
+ */
61
+ nextToken() {
62
+ this.skipWhitespace();
63
+ if (this.isAtEnd()) {
64
+ return this.makeToken(TokenType.EOF, '');
65
+ }
66
+ const char = this.peek();
67
+ const line = this.line;
68
+ const column = this.column;
69
+ // Numbers
70
+ if (this.isDigit(char)) {
71
+ return this.number();
72
+ }
73
+ // Identifiers and keywords
74
+ if (this.isAlpha(char)) {
75
+ return this.identifier();
76
+ }
77
+ // Single character tokens
78
+ switch (char) {
79
+ case '+':
80
+ this.advance();
81
+ return { type: TokenType.PLUS, value: '+', line, column };
82
+ case '-':
83
+ this.advance();
84
+ return { type: TokenType.MINUS, value: '-', line, column };
85
+ case '/':
86
+ this.advance();
87
+ return { type: TokenType.DIVIDE, value: '/', line, column };
88
+ case '(':
89
+ this.advance();
90
+ return { type: TokenType.LPAREN, value: '(', line, column };
91
+ case ')':
92
+ this.advance();
93
+ return { type: TokenType.RPAREN, value: ')', line, column };
94
+ case '{':
95
+ this.advance();
96
+ return { type: TokenType.LBRACE, value: '{', line, column };
97
+ case '}':
98
+ this.advance();
99
+ return { type: TokenType.RBRACE, value: '}', line, column };
100
+ case ',':
101
+ this.advance();
102
+ return { type: TokenType.COMMA, value: ',', line, column };
103
+ case ':':
104
+ this.advance();
105
+ return { type: TokenType.COLON, value: ':', line, column };
106
+ case '.':
107
+ this.advance();
108
+ return { type: TokenType.DOT, value: '.', line, column };
109
+ case '=':
110
+ this.advance();
111
+ return { type: TokenType.EQUALS, value: '=', line, column };
112
+ case '^':
113
+ this.advance();
114
+ return { type: TokenType.POWER, value: '^', line, column };
115
+ case '∇':
116
+ this.advance();
117
+ return { type: TokenType.NABLA, value: '∇', line, column };
118
+ case '*':
119
+ this.advance();
120
+ if (this.peek() === '*') {
121
+ this.advance();
122
+ return { type: TokenType.POWER_ALT, value: '**', line, column };
123
+ }
124
+ return { type: TokenType.MULTIPLY, value: '*', line, column };
125
+ case '\n':
126
+ this.advance();
127
+ return { type: TokenType.NEWLINE, value: '\n', line, column };
128
+ }
129
+ throw new Error(`Unexpected character '${char}' at line ${line}, column ${column}`);
130
+ }
131
+ number() {
132
+ const line = this.line;
133
+ const column = this.column;
134
+ let value = '';
135
+ while (this.isDigit(this.peek())) {
136
+ value += this.advance();
137
+ }
138
+ // Handle decimal point
139
+ if (this.peek() === '.' && this.isDigit(this.peekNext())) {
140
+ value += this.advance(); // consume '.'
141
+ while (this.isDigit(this.peek())) {
142
+ value += this.advance();
143
+ }
144
+ }
145
+ // Handle scientific notation
146
+ if (this.peek() === 'e' || this.peek() === 'E') {
147
+ value += this.advance(); // consume 'e'
148
+ if (this.peek() === '+' || this.peek() === '-') {
149
+ value += this.advance();
150
+ }
151
+ while (this.isDigit(this.peek())) {
152
+ value += this.advance();
153
+ }
154
+ }
155
+ return { type: TokenType.NUMBER, value, line, column };
156
+ }
157
+ identifier() {
158
+ const line = this.line;
159
+ const column = this.column;
160
+ let value = '';
161
+ while (this.isAlphaNumeric(this.peek())) {
162
+ value += this.advance();
163
+ }
164
+ // Check for keywords
165
+ let type = TokenType.IDENTIFIER;
166
+ if (value === 'function') {
167
+ type = TokenType.FUNCTION;
168
+ }
169
+ else if (value === 'return') {
170
+ type = TokenType.RETURN;
171
+ }
172
+ return { type, value, line, column };
173
+ }
174
+ skipWhitespace() {
175
+ while (!this.isAtEnd()) {
176
+ const char = this.peek();
177
+ if (char === ' ' || char === '\r' || char === '\t') {
178
+ this.advance();
179
+ }
180
+ else if (char === '/' && this.peekNext() === '/') {
181
+ // Skip single-line comment
182
+ while (this.peek() !== '\n' && !this.isAtEnd()) {
183
+ this.advance();
184
+ }
185
+ }
186
+ else if (char === '\n') {
187
+ // Don't skip newlines here - they're tokens
188
+ break;
189
+ }
190
+ else {
191
+ break;
192
+ }
193
+ }
194
+ }
195
+ peek() {
196
+ if (this.isAtEnd())
197
+ return '\0';
198
+ return this.input[this.position];
199
+ }
200
+ peekNext() {
201
+ if (this.position + 1 >= this.input.length)
202
+ return '\0';
203
+ return this.input[this.position + 1];
204
+ }
205
+ advance() {
206
+ const char = this.input[this.position++];
207
+ if (char === '\n') {
208
+ this.line++;
209
+ this.column = 1;
210
+ }
211
+ else {
212
+ this.column++;
213
+ }
214
+ return char;
215
+ }
216
+ isAtEnd() {
217
+ return this.position >= this.input.length;
218
+ }
219
+ isDigit(char) {
220
+ return char >= '0' && char <= '9';
221
+ }
222
+ isAlpha(char) {
223
+ return (char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z') || char === '_';
224
+ }
225
+ isAlphaNumeric(char) {
226
+ return this.isAlpha(char) || this.isDigit(char);
227
+ }
228
+ makeToken(type, value) {
229
+ return {
230
+ type,
231
+ value,
232
+ line: this.line,
233
+ column: this.column
234
+ };
235
+ }
236
+ }
237
+ /**
238
+ * Convenience function to tokenize input
239
+ */
240
+ export function tokenize(input) {
241
+ const lexer = new Lexer(input);
242
+ return lexer.tokenize();
243
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Parser for GradientScript DSL
3
+ * Parses function definitions with structured types
4
+ */
5
+ import { Program } from './AST.js';
6
+ export declare class Parser {
7
+ private tokens;
8
+ private current;
9
+ constructor(input: string);
10
+ /**
11
+ * Parse the entire program
12
+ */
13
+ parse(): Program;
14
+ /**
15
+ * Parse function definition
16
+ * function name(param1∇: {x, y}, param2) { ... }
17
+ */
18
+ private functionDef;
19
+ /**
20
+ * Parse parameter list
21
+ * param1∇: {x, y}, param2, param3∇
22
+ */
23
+ private parameterList;
24
+ /**
25
+ * Parse single parameter
26
+ * name∇: {x, y} or name or name∇
27
+ */
28
+ private parameter;
29
+ /**
30
+ * Parse struct type annotation
31
+ * {x, y} or {x, y, z}
32
+ */
33
+ private structTypeAnnotation;
34
+ /**
35
+ * Parse function body
36
+ * sequence of assignments followed by return
37
+ */
38
+ private functionBody;
39
+ /**
40
+ * Parse statement (currently only assignment)
41
+ */
42
+ private statement;
43
+ /**
44
+ * Parse assignment
45
+ * variable = expression
46
+ */
47
+ private assignment;
48
+ /**
49
+ * Parse expression
50
+ */
51
+ private expression;
52
+ /**
53
+ * Parse additive expression (+ and -)
54
+ */
55
+ private additive;
56
+ /**
57
+ * Parse multiplicative expression (* and /)
58
+ */
59
+ private multiplicative;
60
+ /**
61
+ * Parse power expression (^ and **)
62
+ */
63
+ private power;
64
+ /**
65
+ * Parse unary expression (- and +)
66
+ */
67
+ private unary;
68
+ /**
69
+ * Parse postfix expression (function calls and component access)
70
+ */
71
+ private postfix;
72
+ /**
73
+ * Parse argument list for function call
74
+ */
75
+ private argumentList;
76
+ /**
77
+ * Parse primary expression
78
+ */
79
+ private primary;
80
+ private match;
81
+ private check;
82
+ private advance;
83
+ private isAtEnd;
84
+ private peek;
85
+ private previous;
86
+ private consume;
87
+ private error;
88
+ }
89
+ /**
90
+ * Convenience function to parse input
91
+ */
92
+ export declare function parse(input: string): Program;
@@ -0,0 +1,328 @@
1
+ /**
2
+ * Parser for GradientScript DSL
3
+ * Parses function definitions with structured types
4
+ */
5
+ import { TokenType, Lexer } from './Lexer.js';
6
+ import { ParseError } from './Errors.js';
7
+ export class Parser {
8
+ tokens;
9
+ current = 0;
10
+ constructor(input) {
11
+ const lexer = new Lexer(input);
12
+ this.tokens = lexer.tokenize();
13
+ }
14
+ /**
15
+ * Parse the entire program
16
+ */
17
+ parse() {
18
+ const functions = [];
19
+ while (!this.isAtEnd()) {
20
+ functions.push(this.functionDef());
21
+ }
22
+ if (functions.length === 0) {
23
+ const token = this.peek();
24
+ throw new ParseError('Expected at least one function definition', token.line, token.column);
25
+ }
26
+ return {
27
+ kind: 'program',
28
+ functions
29
+ };
30
+ }
31
+ /**
32
+ * Parse function definition
33
+ * function name(param1∇: {x, y}, param2) { ... }
34
+ */
35
+ functionDef() {
36
+ this.consume(TokenType.FUNCTION, "Expected 'function'");
37
+ const name = this.consume(TokenType.IDENTIFIER, 'Expected function name').value;
38
+ this.consume(TokenType.LPAREN, "Expected '(' after function name");
39
+ const parameters = this.parameterList();
40
+ this.consume(TokenType.RPAREN, "Expected ')' after parameters");
41
+ this.consume(TokenType.LBRACE, "Expected '{' before function body");
42
+ const { body, returnExpr } = this.functionBody();
43
+ this.consume(TokenType.RBRACE, "Expected '}' after function body");
44
+ return {
45
+ kind: 'function',
46
+ name,
47
+ parameters,
48
+ body,
49
+ returnExpr
50
+ };
51
+ }
52
+ /**
53
+ * Parse parameter list
54
+ * param1∇: {x, y}, param2, param3∇
55
+ */
56
+ parameterList() {
57
+ const params = [];
58
+ if (this.check(TokenType.RPAREN)) {
59
+ return params; // Empty parameter list
60
+ }
61
+ do {
62
+ params.push(this.parameter());
63
+ } while (this.match(TokenType.COMMA));
64
+ return params;
65
+ }
66
+ /**
67
+ * Parse single parameter
68
+ * name∇: {x, y} or name or name∇
69
+ */
70
+ parameter() {
71
+ const nameToken = this.consume(TokenType.IDENTIFIER, 'Expected parameter name');
72
+ const name = nameToken.value;
73
+ // Check for ∇ (gradient annotation)
74
+ const requiresGrad = this.match(TokenType.NABLA);
75
+ // Check for type annotation : {x, y}
76
+ let paramType;
77
+ if (this.match(TokenType.COLON)) {
78
+ paramType = this.structTypeAnnotation();
79
+ }
80
+ return {
81
+ name,
82
+ requiresGrad,
83
+ paramType
84
+ };
85
+ }
86
+ /**
87
+ * Parse struct type annotation
88
+ * {x, y} or {x, y, z}
89
+ */
90
+ structTypeAnnotation() {
91
+ this.consume(TokenType.LBRACE, "Expected '{'");
92
+ const components = [];
93
+ do {
94
+ const comp = this.consume(TokenType.IDENTIFIER, 'Expected component name');
95
+ components.push(comp.value);
96
+ } while (this.match(TokenType.COMMA));
97
+ this.consume(TokenType.RBRACE, "Expected '}'");
98
+ return { components };
99
+ }
100
+ /**
101
+ * Parse function body
102
+ * sequence of assignments followed by return
103
+ */
104
+ functionBody() {
105
+ const body = [];
106
+ // Parse statements until we hit return
107
+ while (!this.check(TokenType.RETURN) && !this.check(TokenType.RBRACE)) {
108
+ body.push(this.statement());
109
+ }
110
+ // Parse return statement
111
+ this.consume(TokenType.RETURN, "Expected 'return' statement");
112
+ const returnExpr = this.expression();
113
+ return { body, returnExpr };
114
+ }
115
+ /**
116
+ * Parse statement (currently only assignment)
117
+ */
118
+ statement() {
119
+ return this.assignment();
120
+ }
121
+ /**
122
+ * Parse assignment
123
+ * variable = expression
124
+ */
125
+ assignment() {
126
+ const variable = this.consume(TokenType.IDENTIFIER, 'Expected variable name').value;
127
+ this.consume(TokenType.EQUALS, "Expected '=' in assignment");
128
+ const expression = this.expression();
129
+ return {
130
+ kind: 'assignment',
131
+ variable,
132
+ expression
133
+ };
134
+ }
135
+ /**
136
+ * Parse expression
137
+ */
138
+ expression() {
139
+ return this.additive();
140
+ }
141
+ /**
142
+ * Parse additive expression (+ and -)
143
+ */
144
+ additive() {
145
+ let expr = this.multiplicative();
146
+ while (this.match(TokenType.PLUS, TokenType.MINUS)) {
147
+ const operator = this.previous().value;
148
+ const right = this.multiplicative();
149
+ expr = {
150
+ kind: 'binary',
151
+ operator,
152
+ left: expr,
153
+ right
154
+ };
155
+ }
156
+ return expr;
157
+ }
158
+ /**
159
+ * Parse multiplicative expression (* and /)
160
+ */
161
+ multiplicative() {
162
+ let expr = this.power();
163
+ while (this.match(TokenType.MULTIPLY, TokenType.DIVIDE)) {
164
+ const operator = this.previous().value;
165
+ const right = this.power();
166
+ expr = {
167
+ kind: 'binary',
168
+ operator,
169
+ left: expr,
170
+ right
171
+ };
172
+ }
173
+ return expr;
174
+ }
175
+ /**
176
+ * Parse power expression (^ and **)
177
+ */
178
+ power() {
179
+ let expr = this.unary();
180
+ // Right-associative
181
+ if (this.match(TokenType.POWER, TokenType.POWER_ALT)) {
182
+ const operator = this.previous().type === TokenType.POWER ? '^' : '**';
183
+ const right = this.power(); // Right-associative recursion
184
+ expr = {
185
+ kind: 'binary',
186
+ operator,
187
+ left: expr,
188
+ right
189
+ };
190
+ }
191
+ return expr;
192
+ }
193
+ /**
194
+ * Parse unary expression (- and +)
195
+ */
196
+ unary() {
197
+ if (this.match(TokenType.MINUS, TokenType.PLUS)) {
198
+ const operator = this.previous().value;
199
+ const operand = this.unary();
200
+ return {
201
+ kind: 'unary',
202
+ operator,
203
+ operand
204
+ };
205
+ }
206
+ return this.postfix();
207
+ }
208
+ /**
209
+ * Parse postfix expression (function calls and component access)
210
+ */
211
+ postfix() {
212
+ let expr = this.primary();
213
+ while (true) {
214
+ if (this.match(TokenType.LPAREN)) {
215
+ // Function call
216
+ const args = this.argumentList();
217
+ this.consume(TokenType.RPAREN, "Expected ')' after arguments");
218
+ if (expr.kind !== 'variable') {
219
+ const token = this.previous();
220
+ throw new ParseError('Only simple function names are supported', token.line, token.column);
221
+ }
222
+ expr = {
223
+ kind: 'call',
224
+ name: expr.name,
225
+ args
226
+ };
227
+ }
228
+ else if (this.match(TokenType.DOT)) {
229
+ // Component access
230
+ const component = this.consume(TokenType.IDENTIFIER, 'Expected component name after "."').value;
231
+ expr = {
232
+ kind: 'component',
233
+ object: expr,
234
+ component
235
+ };
236
+ }
237
+ else {
238
+ break;
239
+ }
240
+ }
241
+ return expr;
242
+ }
243
+ /**
244
+ * Parse argument list for function call
245
+ */
246
+ argumentList() {
247
+ const args = [];
248
+ if (this.check(TokenType.RPAREN)) {
249
+ return args; // Empty argument list
250
+ }
251
+ do {
252
+ args.push(this.expression());
253
+ } while (this.match(TokenType.COMMA));
254
+ return args;
255
+ }
256
+ /**
257
+ * Parse primary expression
258
+ */
259
+ primary() {
260
+ // Number
261
+ if (this.match(TokenType.NUMBER)) {
262
+ const value = parseFloat(this.previous().value);
263
+ return {
264
+ kind: 'number',
265
+ value
266
+ };
267
+ }
268
+ // Variable
269
+ if (this.match(TokenType.IDENTIFIER)) {
270
+ const name = this.previous().value;
271
+ return {
272
+ kind: 'variable',
273
+ name
274
+ };
275
+ }
276
+ // Parenthesized expression
277
+ if (this.match(TokenType.LPAREN)) {
278
+ const expr = this.expression();
279
+ this.consume(TokenType.RPAREN, "Expected ')' after expression");
280
+ return expr;
281
+ }
282
+ throw this.error(this.peek(), 'Expected expression');
283
+ }
284
+ // Helper methods
285
+ match(...types) {
286
+ for (const type of types) {
287
+ if (this.check(type)) {
288
+ this.advance();
289
+ return true;
290
+ }
291
+ }
292
+ return false;
293
+ }
294
+ check(type) {
295
+ if (this.isAtEnd())
296
+ return false;
297
+ return this.peek().type === type;
298
+ }
299
+ advance() {
300
+ if (!this.isAtEnd())
301
+ this.current++;
302
+ return this.previous();
303
+ }
304
+ isAtEnd() {
305
+ return this.peek().type === TokenType.EOF;
306
+ }
307
+ peek() {
308
+ return this.tokens[this.current];
309
+ }
310
+ previous() {
311
+ return this.tokens[this.current - 1];
312
+ }
313
+ consume(type, message) {
314
+ if (this.check(type))
315
+ return this.advance();
316
+ throw this.error(this.peek(), message);
317
+ }
318
+ error(token, message) {
319
+ return new ParseError(message, token.line, token.column, token.value);
320
+ }
321
+ }
322
+ /**
323
+ * Convenience function to parse input
324
+ */
325
+ export function parse(input) {
326
+ const parser = new Parser(input);
327
+ return parser.parse();
328
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Expression simplification for GradientScript DSL
3
+ * Applies algebraic simplification rules
4
+ */
5
+ import { Expression } from './AST.js';
6
+ /**
7
+ * Simplify an expression using algebraic rules
8
+ */
9
+ export declare function simplify(expr: Expression): Expression;
10
+ /**
11
+ * Simplify all gradients in a map
12
+ */
13
+ export declare function simplifyGradients(gradients: Map<string, Expression | {
14
+ components: Map<string, Expression>;
15
+ }>): Map<string, Expression | {
16
+ components: Map<string, Expression>;
17
+ }>;