Skip to content

Commit

Permalink
feat: add support for multi-config JSEP
Browse files Browse the repository at this point in the history
Before this change, JSEP `parse` was constructing a new instance of the JSEP class to encapsulate the expression and index as it was parsed, and the JSEP configuration was static on the class.

Since JSEP parsing is synchronous, separate calls to parse can safely reset the expression and index instead of creating a new instance. Doing so allows the configuration to be moved into the JSEP instance, so that separate configurations can be used for different jsep parsing usages. The `parse` method then resets the instance's expression and index rather than constructing a new instance. This allows JSEP to functionally maintain backward compatibility despite the structural change.

This commit essentially adds `instance()` (with default `clearConfig()` setup), and `defaultConfig()` methods. All static accesses were replaced with `this`.

Specific things to note:
- JSEP still exports the parse function with the instance's properties and methods for configuration
- all static methods and properties have been moved to the instance (even constants and methods that don't access instance properties, so they can still be used by plugins). To keep the static constants available on each instance without forcing users to now access the prototype, they get assigned in the constructor
- the export is still the instance's parse function, with all methods and properties accessed through a Proxy (rather than copying all properties and binding all methods)
- the Jsep class export is replaced with the jsep instance export so that `{ Jsep } = require('jsep')` still works as before (unless user was calling (new Jsep('expr')).parse())
- because the ternary plugin is included by default, it gets specially registered once, so that `defaultConfig` can include the default plugin as well
- fix `removeAllBinaryOps` not clearing `right_associative`

fixes #211

BREAKING: changes JSEP internals to be a class instance per config, rather than per parse. `Jsep` export is now the combined parse function & class instance instead of the Jsep class. so usages that were directly using the Jsep class (`new Jsep(expression)`) will have update to either `Jsep(expression)` or `Jsep.parse(expression)`. Most applications should be backward compatible without any changes to code
  • Loading branch information
6utt3rfly committed Nov 18, 2024
1 parent c68ca83 commit e18c887
Show file tree
Hide file tree
Showing 6 changed files with 418 additions and 282 deletions.
44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,47 @@ const { Jsep } = require('jsep');
const parse_tree = Jsep.parse('1 + 1');
```

### Configuration

By default, JSEP is configured for basic javascript expression parsing:
- binary operators (
`||`, `??`, `&&`, `|`, `^`, `&`,
`==`, `!=`, `===`, `!==`, `<`, `>`, `<=`, `>=`,
`<<`, `>>`, `>>>`, `+`, `-`, `*`, `/`, `%`, `**`)
- unary operators (`-`, `!`, `~`, `+`)
- identifier chars (`&`, `_`)
- literals (`true`, `false`, `null`)
- (NOT `undefined`, `Infinity` or `NaN`)
- `this`
- and the ternary plugin (`condition ? true : false`)

The configuration can be modified by calling any of the `addBinaryOp`, `addUnaryOp`, `addLiteral`, or `addIdentifierChar`
(or their related `remove` and `removeAll` methods).

In addition, the JSEP configuration can be completely cleared by calling `clearConfig`,
and reset back to defaults by calling `defaultConfig`.

By default, jsep exports an instance of the Jsep class with its default configuration.
In order to have a separate instance for another configuration, you can use the `instance` method
to obtain a new instance, then configure that instance as needed.

```javascript
import jsep from 'jsep'; // function & Jsep methods

const jsep2 = jsep.instance(); // empty config

jsep('1 + 1') // BinaryExpression

jsep2('1 + 1') // Error - unexpected + (no binary operators)
jsep2.defaultConfig();
jsep2.parse('1 + 1') // BinaryExpression

jsep2.addBinaryOp('@', 10);
jsep2.parse('1 @ 1'); // BinaryExpression
jsep.parse('1 @ 1'); // Error - unexpected @
```


#### Custom Operators

```javascript
Expand Down Expand Up @@ -182,6 +223,9 @@ export interface HookScope {
readonly expr: string;
readonly char: string; // current character of the expression
readonly code: number; // current character code of the expression
isDecimalDigit: (ch: number) => boolean;
isIdentifierStart: (ch: number) => boolean;
isIdentifierPart: (ch: number) => boolean;
gobbleSpaces: () => void;
gobbleExpressions: (untilICode?: number) => Expression[];
gobbleExpression: () => Expression;
Expand Down
1 change: 1 addition & 0 deletions rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export default [
versionPlugin,
replace({
'export class Jsep': 'class Jsep', // single default export
'export const Jsep = jsep': '// export const Jsep = jsep', // single default export
preventAssignment: false,
}),
],
Expand Down
6 changes: 4 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
import jsep from './jsep.js';
import ternary from '../packages/ternary/src/index.js';

jsep.plugins.register(ternary);
// NOTE: jsep has historically included ternary support, which was later moved into a plugin
// to include ternary in the "defaultConfig()" method, it needs to be specially added to Jsep
jsep.registerTernary(ternary);

export * from './jsep.js';
export const Jsep = jsep;
export default jsep;
Loading

0 comments on commit e18c887

Please sign in to comment.