or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

data-loading.mddataflow.mdevents.mdexpressions.mdindex.mdparsing.mdscales.mdscenegraph.mdstatistics.mdtime.mdutilities.mdview.md

expressions.mddocs/

0

# Expression System

1

2

Vega's expression system provides a custom expression language for dynamic specifications with function registration, parsing, code generation, and runtime evaluation capabilities.

3

4

## Capabilities

5

6

### Expression Functions

7

8

Custom function registration and management for the expression language.

9

10

```typescript { .api }

11

/**

12

* Register or access expression functions

13

* @param name - Function name to register or access

14

* @param fn - Function implementation (if registering)

15

* @param visitor - Optional AST visitor function for optimization

16

* @returns Function implementation or registration result

17

*/

18

function expressionFunction(name: string, fn?: Function, visitor?: Function): any;

19

20

/**

21

* Register multiple expression functions at once

22

* @param functions - Object mapping function names to implementations

23

*/

24

function registerExpressionFunctions(functions: { [name: string]: Function }): void;

25

26

/**

27

* Get all registered expression functions

28

* @returns Object containing all registered functions

29

*/

30

function getExpressionFunctions(): { [name: string]: Function };

31

32

/**

33

* Check if expression function is registered

34

* @param name - Function name to check

35

* @returns True if function is registered

36

*/

37

function hasExpressionFunction(name: string): boolean;

38

```

39

40

### Expression Parsing

41

42

Expression parsing and AST generation.

43

44

```typescript { .api }

45

/**

46

* Parse expression string into AST

47

* @param expression - Expression string to parse

48

* @param options - Optional parsing configuration

49

* @returns Expression AST node

50

*/

51

function parseExpression(expression: string, options?: ParseOptions): ExpressionNode;

52

53

interface ParseOptions {

54

/** Allow assignment expressions */

55

assignment?: boolean;

56

57

/** Function whitelist */

58

functions?: string[];

59

60

/** Constant values */

61

constants?: { [name: string]: any };

62

63

/** Global scope variables */

64

globals?: string[];

65

66

/** Field accessors */

67

fields?: string[];

68

}

69

70

interface ExpressionNode {

71

/** Node type */

72

type: string;

73

74

/** Node value (for literals) */

75

value?: any;

76

77

/** Node name (for identifiers) */

78

name?: string;

79

80

/** Child nodes */

81

arguments?: ExpressionNode[];

82

83

/** Left operand (for binary expressions) */

84

left?: ExpressionNode;

85

86

/** Right operand (for binary expressions) */

87

right?: ExpressionNode;

88

89

/** Operand (for unary expressions) */

90

argument?: ExpressionNode;

91

92

/** Operator symbol */

93

operator?: string;

94

95

/** Object for member expressions */

96

object?: ExpressionNode;

97

98

/** Property for member expressions */

99

property?: ExpressionNode;

100

101

/** Computed property flag */

102

computed?: boolean;

103

104

/** Test condition (for conditional expressions) */

105

test?: ExpressionNode;

106

107

/** Consequent (for conditional expressions) */

108

consequent?: ExpressionNode;

109

110

/** Alternate (for conditional expressions) */

111

alternate?: ExpressionNode;

112

}

113

```

114

115

### Code Generation

116

117

Expression compilation and code generation.

118

119

```typescript { .api }

120

/**

121

* Generate executable JavaScript function from expression AST

122

* @param ast - Expression AST node

123

* @param options - Optional code generation options

124

* @returns Compiled JavaScript function

125

*/

126

function codegenExpression(ast: ExpressionNode, options?: CodegenOptions): Function;

127

128

/**

129

* Generate JavaScript code string from expression AST

130

* @param ast - Expression AST node

131

* @param options - Optional code generation options

132

* @returns JavaScript code string

133

*/

134

function codegenExpressionString(ast: ExpressionNode, options?: CodegenOptions): string;

135

136

interface CodegenOptions {

137

/** Variable whitelist */

138

whitelist?: string[];

139

140

/** Global variables */

141

globals?: { [name: string]: any };

142

143

/** Function registry */

144

functions?: { [name: string]: Function };

145

146

/** Field accessor function */

147

fieldFunction?: string;

148

149

/** Generate debug information */

150

debug?: boolean;

151

}

152

```

153

154

### Built-in Expression Functions

155

156

Core functions available in the expression language.

157

158

```typescript { .api }

159

/** Mathematical functions */

160

interface MathFunctions {

161

/** Absolute value */

162

abs(x: number): number;

163

164

/** Arc cosine */

165

acos(x: number): number;

166

167

/** Arc sine */

168

asin(x: number): number;

169

170

/** Arc tangent */

171

atan(x: number): number;

172

173

/** Arc tangent of y/x */

174

atan2(y: number, x: number): number;

175

176

/** Ceiling function */

177

ceil(x: number): number;

178

179

/** Clamp value to range */

180

clamp(value: number, min: number, max: number): number;

181

182

/** Cosine */

183

cos(x: number): number;

184

185

/** Exponential function */

186

exp(x: number): number;

187

188

/** Floor function */

189

floor(x: number): number;

190

191

/** Natural logarithm */

192

log(x: number): number;

193

194

/** Maximum value */

195

max(...values: number[]): number;

196

197

/** Minimum value */

198

min(...values: number[]): number;

199

200

/** Power function */

201

pow(x: number, y: number): number;

202

203

/** Random number [0,1) */

204

random(): number;

205

206

/** Round to nearest integer */

207

round(x: number): number;

208

209

/** Sine function */

210

sin(x: number): number;

211

212

/** Square root */

213

sqrt(x: number): number;

214

215

/** Tangent */

216

tan(x: number): number;

217

}

218

219

/** String functions */

220

interface StringFunctions {

221

/** Get string length */

222

length(str: string): number;

223

224

/** Convert to lowercase */

225

lower(str: string): string;

226

227

/** Pad string to length */

228

pad(str: string, length: number, character?: string, align?: 'left' | 'right' | 'center'): string;

229

230

/** Test regular expression */

231

test(str: string, regexp: string | RegExp, flags?: string): boolean;

232

233

/** Extract substring */

234

substring(str: string, start: number, end?: number): string;

235

236

/** Trim whitespace */

237

trim(str: string): string;

238

239

/** Convert to uppercase */

240

upper(str: string): string;

241

242

/** Replace text with regexp */

243

replace(str: string, pattern: string | RegExp, replacement: string): string;

244

245

/** Split string */

246

split(str: string, separator: string | RegExp, limit?: number): string[];

247

248

/** Find substring index */

249

indexof(str: string, substring: string): number;

250

251

/** Get last index of substring */

252

lastindexof(str: string, substring: string): number;

253

254

/** Extract substring by length */

255

slice(str: string, start: number, length?: number): string;

256

}

257

258

/** Date/Time functions */

259

interface DateTimeFunctions {

260

/** Create new date */

261

datetime(year?: number, month?: number, day?: number, hour?: number, minute?: number, second?: number, millisecond?: number): Date;

262

263

/** Get current timestamp */

264

now(): number;

265

266

/** Parse date string */

267

date(dateString: string): Date;

268

269

/** Get day of month */

270

day(date: Date): number;

271

272

/** Get day of year */

273

dayofyear(date: Date): number;

274

275

/** Get hours */

276

hours(date: Date): number;

277

278

/** Get milliseconds */

279

milliseconds(date: Date): number;

280

281

/** Get minutes */

282

minutes(date: Date): number;

283

284

/** Get month */

285

month(date: Date): number;

286

287

/** Get quarter */

288

quarter(date: Date): number;

289

290

/** Get seconds */

291

seconds(date: Date): number;

292

293

/** Get timestamp */

294

time(date: Date): number;

295

296

/** Get timezone offset */

297

timezoneoffset(date: Date): number;

298

299

/** Get year */

300

year(date: Date): number;

301

302

/** UTC versions of date functions */

303

utcday(date: Date): number;

304

utchours(date: Date): number;

305

utcmilliseconds(date: Date): number;

306

utcminutes(date: Date): number;

307

utcmonth(date: Date): number;

308

utcseconds(date: Date): number;

309

utcyear(date: Date): number;

310

}

311

312

/** Array functions */

313

interface ArrayFunctions {

314

/** Get array length */

315

length(array: any[]): number;

316

317

/** Extract array slice */

318

slice(array: any[], start: number, end?: number): any[];

319

320

/** Reverse array */

321

reverse(array: any[]): any[];

322

323

/** Join array elements */

324

join(array: any[], separator?: string): string;

325

326

/** Find index of element */

327

indexof(array: any[], element: any): number;

328

329

/** Check if array includes element */

330

includes(array: any[], element: any): boolean;

331

332

/** Map array elements */

333

map(array: any[], mapper: string): any[];

334

335

/** Filter array elements */

336

filter(array: any[], predicate: string): any[];

337

338

/** Reduce array */

339

reduce(array: any[], reducer: string, initial?: any): any;

340

341

/** Sort array */

342

sort(array: any[], comparator?: string): any[];

343

}

344

345

/** Type checking functions */

346

interface TypeFunctions {

347

/** Check if value is array */

348

isArray(value: any): boolean;

349

350

/** Check if value is boolean */

351

isBoolean(value: any): boolean;

352

353

/** Check if value is date */

354

isDate(value: any): boolean;

355

356

/** Check if value is finite number */

357

isFinite(value: any): boolean;

358

359

/** Check if value is NaN */

360

isNaN(value: any): boolean;

361

362

/** Check if value is number */

363

isNumber(value: any): boolean;

364

365

/** Check if value is object */

366

isObject(value: any): boolean;

367

368

/** Check if value is string */

369

isString(value: any): boolean;

370

371

/** Check if value is valid (not null/undefined) */

372

isValid(value: any): boolean;

373

}

374

375

/** Conversion functions */

376

interface ConversionFunctions {

377

/** Convert to boolean */

378

toBoolean(value: any): boolean;

379

380

/** Convert to date */

381

toDate(value: any): Date;

382

383

/** Convert to number */

384

toNumber(value: any): number;

385

386

/** Convert to string */

387

toString(value: any): string;

388

}

389

390

/** Scale functions */

391

interface ScaleFunctions {

392

/** Apply scale function */

393

scale(scaleName: string, value: any): any;

394

395

/** Invert scale function */

396

invert(scaleName: string, value: any): any;

397

398

/** Get scale bandwidth */

399

bandwidth(scaleName: string): number;

400

401

/** Get scale copy */

402

copy(scaleName: string): any;

403

404

/** Get scale domain */

405

domain(scaleName: string): any[];

406

407

/** Get scale range */

408

range(scaleName: string): any[];

409

}

410

411

/** Data access functions */

412

interface DataFunctions {

413

/** Get data array */

414

data(datasetName: string): any[];

415

416

/** Get data length */

417

length(datasetName: string): number;

418

419

/** Check if data exists */

420

indata(datasetName: string, field: string, value: any): boolean;

421

}

422

423

/** Color functions */

424

interface ColorFunctions {

425

/** Create RGB color */

426

rgb(r: number, g: number, b: number): string;

427

428

/** Create HSL color */

429

hsl(h: number, s: number, l: number): string;

430

431

/** Create Lab color */

432

lab(l: number, a: number, b: number): string;

433

434

/** Create HCL color */

435

hcl(h: number, c: number, l: number): string;

436

}

437

```

438

439

### Expression Context

440

441

Runtime context for expression evaluation.

442

443

```typescript { .api }

444

/**

445

* Expression evaluation context

446

*/

447

interface ExpressionContext {

448

/** Current data tuple */

449

datum?: any;

450

451

/** Event object (for event expressions) */

452

event?: any;

453

454

/** Item object (for mark expressions) */

455

item?: any;

456

457

/** Group object (for group expressions) */

458

group?: any;

459

460

/** Signal values */

461

signals?: { [name: string]: any };

462

463

/** Data sources */

464

data?: { [name: string]: any[] };

465

466

/** Scale functions */

467

scales?: { [name: string]: Function };

468

469

/** Custom functions */

470

functions?: { [name: string]: Function };

471

472

/** Global constants */

473

constants?: { [name: string]: any };

474

}

475

476

/**

477

* Create expression evaluator function

478

* @param expression - Expression string or AST

479

* @param context - Evaluation context

480

* @returns Evaluator function

481

*/

482

function createExpressionEvaluator(

483

expression: string | ExpressionNode,

484

context: ExpressionContext

485

): (datum?: any, event?: any) => any;

486

```

487

488

## Usage Examples

489

490

### Basic Expression Evaluation

491

492

```typescript

493

import { parseExpression, codegenExpression } from "vega";

494

495

// Parse and compile expression

496

const ast = parseExpression('datum.value * 2 + 10');

497

const evaluator = codegenExpression(ast);

498

499

// Evaluate with data

500

const result = evaluator({ datum: { value: 5 } });

501

console.log(result); // 20

502

```

503

504

### Custom Function Registration

505

506

```typescript

507

import { expressionFunction } from "vega";

508

509

// Register custom functions

510

expressionFunction('formatCurrency', (value) => {

511

return new Intl.NumberFormat('en-US', {

512

style: 'currency',

513

currency: 'USD'

514

}).format(value);

515

});

516

517

expressionFunction('clampPercent', (value) => {

518

return Math.max(0, Math.min(100, value));

519

});

520

521

// Use in expressions

522

const ast = parseExpression('formatCurrency(datum.sales)');

523

const formatter = codegenExpression(ast);

524

525

const formatted = formatter({ datum: { sales: 1234.56 } });

526

console.log(formatted); // "$1,234.56"

527

```

528

529

### Complex Expression Parsing

530

531

```typescript

532

import { parseExpression, codegenExpression } from "vega";

533

534

// Complex conditional expression

535

const expression = `

536

datum.category === 'A' ? 'red' :

537

datum.category === 'B' ? 'blue' :

538

datum.value > 100 ? 'green' : 'gray'

539

`;

540

541

const ast = parseExpression(expression);

542

const evaluator = codegenExpression(ast);

543

544

// Test with different data

545

const testData = [

546

{ category: 'A', value: 50 },

547

{ category: 'B', value: 150 },

548

{ category: 'C', value: 200 },

549

{ category: 'C', value: 25 }

550

];

551

552

testData.forEach(datum => {

553

console.log(datum, '->', evaluator({ datum }));

554

});

555

// { category: 'A', value: 50 } -> 'red'

556

// { category: 'B', value: 150 } -> 'blue'

557

// { category: 'C', value: 200 } -> 'green'

558

// { category: 'C', value: 25 } -> 'gray'

559

```

560

561

### Expression with Context

562

563

```typescript

564

import { parseExpression, codegenExpression } from "vega";

565

566

// Expression using signals and scales

567

const expression = 'scale("xscale", datum.x) + signal("offset")';

568

569

const ast = parseExpression(expression, {

570

functions: ['scale', 'signal'],

571

globals: ['datum']

572

});

573

574

const evaluator = codegenExpression(ast, {

575

functions: {

576

scale: (name, value) => {

577

if (name === 'xscale') {

578

return value * 10; // Simple linear scale

579

}

580

return value;

581

},

582

signal: (name) => {

583

if (name === 'offset') {

584

return 50;

585

}

586

return 0;

587

}

588

}

589

});

590

591

const result = evaluator({ datum: { x: 5 } });

592

console.log(result); // 100 (5 * 10 + 50)

593

```

594

595

### Array and String Operations

596

597

```typescript

598

import { parseExpression, codegenExpression } from "vega";

599

600

// Array operations

601

const arrayExpr = parseExpression('slice(datum.values, 0, 3)');

602

const arrayEval = codegenExpression(arrayExpr);

603

604

const arrayResult = arrayEval({

605

datum: { values: [1, 2, 3, 4, 5] }

606

});

607

console.log(arrayResult); // [1, 2, 3]

608

609

// String operations

610

const stringExpr = parseExpression('upper(substring(datum.name, 0, 3))');

611

const stringEval = codegenExpression(stringExpr);

612

613

const stringResult = stringEval({

614

datum: { name: 'hello world' }

615

});

616

console.log(stringResult); // 'HEL'

617

```

618

619

### Date/Time Expressions

620

621

```typescript

622

import { parseExpression, codegenExpression } from "vega";

623

624

// Date extraction

625

const dateExpr = parseExpression('year(datum.timestamp)');

626

const dateEval = codegenExpression(dateExpr);

627

628

const dateResult = dateEval({

629

datum: { timestamp: new Date('2023-06-15') }

630

});

631

console.log(dateResult); // 2023

632

633

// Date formatting

634

const formatExpr = parseExpression(`

635

month(datum.date) + '/' + day(datum.date) + '/' + year(datum.date)

636

`);

637

const formatEval = codegenExpression(formatExpr);

638

639

const formatResult = formatEval({

640

datum: { date: new Date('2023-06-15') }

641

});

642

console.log(formatResult); // '6/15/2023'

643

```

644

645

### Mathematical Expressions

646

647

```typescript

648

import { parseExpression, codegenExpression } from "vega";

649

650

// Complex mathematical expression

651

const mathExpr = parseExpression(`

652

sqrt(pow(datum.x - datum.centerX, 2) + pow(datum.y - datum.centerY, 2))

653

`);

654

const mathEval = codegenExpression(mathExpr);

655

656

// Calculate distance from center

657

const distance = mathEval({

658

datum: {

659

x: 10,

660

y: 8,

661

centerX: 5,

662

centerY: 3

663

}

664

});

665

console.log(distance); // ~7.07

666

```

667

668

### Type Checking and Conversion

669

670

```typescript

671

import { parseExpression, codegenExpression } from "vega";

672

673

// Type checking and conversion

674

const typeExpr = parseExpression(`

675

isNumber(datum.value) ? toNumber(datum.value) : 0

676

`);

677

const typeEval = codegenExpression(typeExpr);

678

679

const testValues = [

680

{ value: "123" },

681

{ value: "abc" },

682

{ value: 456 },

683

{ value: null }

684

];

685

686

testValues.forEach(datum => {

687

console.log(datum.value, '->', typeEval({ datum }));

688

});

689

// "123" -> 123

690

// "abc" -> 0

691

// 456 -> 456

692

// null -> 0

693

```

694

695

### Data Access Expressions

696

697

```typescript

698

import { expressionFunction, parseExpression, codegenExpression } from "vega";

699

700

// Register data access function

701

expressionFunction('indata', (dataset, field, value) => {

702

// Mock data lookup

703

const mockData = {

704

'categories': [

705

{ name: 'A', active: true },

706

{ name: 'B', active: false },

707

{ name: 'C', active: true }

708

]

709

};

710

711

const data = mockData[dataset] || [];

712

return data.some(d => d[field] === value);

713

});

714

715

// Use data lookup in expression

716

const expr = parseExpression(`

717

indata('categories', 'name', datum.category) ? 'valid' : 'invalid'

718

`);

719

const eval = codegenExpression(expr);

720

721

const result = eval({ datum: { category: 'A' } });

722

console.log(result); // 'valid'

723

```

724

725

### Whitelist and Security

726

727

```typescript

728

import { parseExpression, codegenExpression } from "vega";

729

730

// Restricted parsing with whitelist

731

const restrictedAST = parseExpression('datum.value + offset', {

732

functions: ['datum'], // Only allow datum access

733

globals: ['offset'], // Allow offset global

734

fields: ['value'] // Only allow value field

735

});

736

737

const restrictedEval = codegenExpression(restrictedAST, {

738

whitelist: ['datum', 'offset'], // Restrict variable access

739

globals: { offset: 10 } // Provide global values

740

});

741

742

const safeResult = restrictedEval({ datum: { value: 5 } });

743

console.log(safeResult); // 15

744

```