Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 62 additions & 21 deletions docs/expressions/expression_trees.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,11 @@ tree generated by `math.parse('sqrt(2 + x)')`.

All nodes have the following methods:

- `clone() : Node`
- `clone(options: MetaOptions) : Node`
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The options argument is optional, and it is named meta in the source code (consistent with the constructor arguments of the Node classes). Can you document this like clone(meta?: MetaOptions) : Node?

And similarly, the argument meta is optional in the constructors of each of the Node classes, can you udpate that too in this documentation page?


Create a shallow clone of the node.
The node itself is cloned, its childs are not cloned.
The node itself is cloned, its childs are not cloned.
Information on available options can be found at [Type Definitions](expression_trees.md#type-definitions)

- `cloneDeep() : Node`

Expand Down Expand Up @@ -263,7 +264,10 @@ Each `Node` has the following properties:
- `type: string`

The type of the node, for example `'SymbolNode'` in case of a `SymbolNode`.


- `sources: SourceMapping[]`
An array of sources mapping this node back to its tokens in the parsed string.
The exact mapping will depend on the type of node and is listed in more detail for each node below.

## Nodes

Expand All @@ -276,14 +280,15 @@ namespace `math`.
Construction:

```
new AccessorNode(object: Node, index: IndexNode)
new AccessorNode(object: Node, index: IndexNode, meta: MetaOptions)
```

Properties:

- `object: Node`
- `index: IndexNode`
- `name: string` (read-only) The function or method name. Returns an empty string when undefined.
- `sources: SourceMapping[]` mappings to tokens defining this accessor. This will be `[` and `]` for array accessors, and `.` for dot notation accessors.

Examples:

Expand All @@ -302,12 +307,13 @@ const node2 = new math.AccessorNode(object, index)
Construction:

```
new ArrayNode(items: Node[])
new ArrayNode(items: Node[], meta: MetaOptions)
```

Properties:

- `items: Node[]`
- `sources: SourceMapping[]` mappings to the `[`, `]`, `,`, and `;` used to define this array in the parsed string

Examples:

Expand All @@ -326,8 +332,8 @@ const node2 = new math.ArrayNode([one, two, three])
Construction:

```
new AssignmentNode(object: SymbolNode, value: Node)
new AssignmentNode(object: SymbolNode | AccessorNode, index: IndexNode, value: Node)
new AssignmentNode(object: SymbolNode, value: Node, meta: MetaOptions)
new AssignmentNode(object: SymbolNode | AccessorNode, index: IndexNode, value: Node, meta: MetaOptions)
```

Properties:
Expand All @@ -336,6 +342,7 @@ Properties:
- `index: IndexNode | null`
- `value: Node`
- `name: string` (read-only) The function or method name. Returns an empty string when undefined.
- `sources: SourceMapping[]` mapping to the `=` defining this assignment node in the parsed string

Examples:

Expand All @@ -359,12 +366,13 @@ a semicolon).
Construction:

```
block = new BlockNode(Array.<{node: Node} | {node: Node, visible: boolean}>)
block = new BlockNode(Array.<{node: Node} | {node: Node, visible: boolean}>, meta: MetaOptions)
```

Properties:

- `blocks: Array.<{node: Node, visible: boolean}>`
- `sources: SourceMapping[]` mappings to each `;` token delimiting blocks in the parsed string

Examples:

Expand Down Expand Up @@ -396,14 +404,15 @@ const block2 = new BlockNode([
Construction:

```
new ConditionalNode(condition: Node, trueExpr: Node, falseExpr: Node)
new ConditionalNode(condition: Node, trueExpr: Node, falseExpr: Node, meta: MetaOptions)
```

Properties:

- `condition: Node`
- `trueExpr: Node`
- `falseExpr: Node`
- `sources: SourceMapping[]` mappings to the `?`, and `:` tokens defining this conditional in the parsed string

Examples:

Expand All @@ -423,12 +432,13 @@ const node2 = new math.ConditionalNode(condition, trueExpr, falseExpr)
Construction:

```
new ConstantNode(value: *)
new ConstantNode(value: *, meta: MetaOptions)
```

Properties:

- `value: *`
- `sources: SourceMapping[]` mapping to the token representing the constant in the parsed string.

Examples:

Expand All @@ -445,14 +455,15 @@ const node3 = new math.ConstantNode('foo')
Construction:

```
new FunctionAssignmentNode(name: string, params: string[], expr: Node)
new FunctionAssignmentNode(name: string, params: string[], expr: Node, meta: MetaOptions)
```

Properties:

- `name: string`
- `params: string[]`
- `expr: Node`
- `sources: SourceMapping[]` mapping to the `=` for this assignment in the parsed string

Examples:

Expand All @@ -471,13 +482,14 @@ const node2 = new math.FunctionAssignmentNode('f', ['x'], expr)
Construction:

```
new FunctionNode(fn: Node | string, args: Node[])
new FunctionNode(fn: Node | string, args: Node[], meta: MetaOptions)
```

Properties:

- `fn: Node | string` (read-only) The object or function name which to invoke.
- `args: Node[]`
- `sources: SourceMapping[]` mappings to the `(` and `)` defining this function, as well as any `,` delimiting its parameters.

Static functions:

Expand All @@ -499,8 +511,8 @@ const node3 = new math.FunctionNode(new SymbolNode('sqrt'), [four])
Construction:

```
new IndexNode(dimensions: Node[])
new IndexNode(dimensions: Node[], dotNotation: boolean)
new IndexNode(dimensions: Node[], meta: MetaOptions)
new IndexNode(dimensions: Node[], dotNotation: boolean, meta: MetaOptions)
```

Each dimension can be a single value, a range, or a property. The values of
Expand All @@ -515,6 +527,7 @@ Properties:

- `dimensions: Node[]`
- `dotNotation: boolean`
- `sources: SourceMapping[]` mappings to `,` delimiting items in an array index. If `dotNotation = true`, this will map to the constant following the `.` instead.

Examples:

Expand All @@ -536,12 +549,13 @@ const node2 = new math.AccessorNode(A, index)
Construction:

```
new ObjectNode(properties: Object.<string, Node>)
new ObjectNode(properties: Object.<string, Node>, meta: MetaOptions)
```

Properties:

- `properties: Object.<string, Node>`
- `sources: SourceMapping[]` mappings to the `{`, `}`, `:`, and `,` tokens defining this object in the parsed string

Examples:

Expand All @@ -560,7 +574,7 @@ const node2 = new math.ObjectNode({a: a, b: b, c: c})
Construction:

```
new OperatorNode(op: string, fn: string, args: Node[], implicit: boolean = false)
new OperatorNode(op: string, fn: string, args: Node[], implicit: boolean = false, meta: MetaOptions)
```

Additional methods:
Expand Down Expand Up @@ -594,6 +608,7 @@ Properties:
- `fn: string`
- `args: Node[]`
- `implicit: boolean` True in case of an implicit multiplication, false otherwise
- `sources: SourceMapping[]` mapping to the `+` or `-` defining this unary operator in the parsed string

Examples:

Expand All @@ -610,12 +625,13 @@ const node2 = new math.OperatorNode('+', 'add', [a, b])
Construction:

```
new ParenthesisNode(content: Node)
new ParenthesisNode(content: Node, meta: MetaOptions)
```

Properties:

- `content: Node`
- `sources: SourceMapping[]` mappings to the `(` and `)` for this node in the parsed string

Examples:

Expand All @@ -631,15 +647,16 @@ const node2 = new math.ParenthesisNode(a)
Construction:

```
new RangeNode(start: Node, end: Node [, step: Node])
new RangeNode(start: Node, end: Node [, step: Node], meta: MetaOptions)
```

Properties:

- `start: Node`
- `end: Node`
- `step: Node | null`

- `sources: SourceMapping[]` mappings to the `:` defining this range node in the parsed string. There will be 1 or 2 mappings, depending on whether step size was defined for the range

Examples:

```js
Expand All @@ -660,7 +677,7 @@ const node4 = new math.RangeNode(zero, ten, two)
Construction:

```
new RelationalNode(conditionals: string[], params: Node[])
new RelationalNode(conditionals: string[], params: Node[], meta: MetaOptions)
```

`conditionals` is an array of strings, each of which may be 'smaller', 'larger', 'smallerEq', 'largerEq', 'equal', or 'unequal'. The `conditionals` array must contain exactly one fewer item than `params`.
Expand All @@ -669,6 +686,7 @@ Properties:

- `conditionals: string[]`
- `params: Node[]`
- `sources: SourceMapping[]` mappings to the relational symbol `<`, `>`, `==`, `>=`, or `<=` defining this node in the parsed string. This may include multiple mappings if multiple relationals are chained: `10 < x < 20`

A `RelationalNode` efficiently represents a chained conditional expression with two or more comparison operators, such as `10 < x <= 50`. The expression is equivalent to `10 < x and x <= 50`, except that `x` is evaluated only once, and evaluation stops (is "short-circuited") once any condition tests false. Operators that are subject to chaining are `<`, `>`, `<=`, `>=`, `==`, and `!=`. For backward compatibility, `math.parse` will return an `OperatorNode` if only a single conditional is present (such as `x > 2`).

Expand All @@ -690,12 +708,13 @@ const node2 = math.parse('10 < x <= 50')
Construction:

```
new SymbolNode(name: string)
new SymbolNode(name: string, meta: MetaOptions)
```

Properties:

- `name: string`
- `sources: SourceMapping[]` a single mapping to the symbol defining this node in the parsed string. The text will match whatever symbol is defined.

Static functions:

Expand All @@ -709,3 +728,25 @@ const node = math.parse('x')

const x = new math.SymbolNode('x')
```

## Type Definitions

A few node methods and properties have complex object structures as their parameters or return types. They are:

### MetaOptions

This object is passed as a final parameter in the constructor of any node, or as the parameter when calling `clone()`.

Properties:

- `sources: SourceMapping` sets the sources for the newly created or cloned node

### SourceMapping

Each node has an array of `SourceMapping` objects which map back to the node's corresponding tokens in the original source string

Properties:

- `text: string` the token representing this node in the parsed string
- `index; number` the index of the token in the parsed string
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small typo, I think the semicolon ; sould be a colon :.

All is all well documented BTW 👍


9 changes: 8 additions & 1 deletion src/core/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,12 @@ export const DEFAULT_CONFIG = {
// legacy behavior for matrix subset. When true, the subset function
// returns a matrix or array with the same size as the index (except for scalars).
// When false, it returns a matrix or array with a size depending on the type of index.
legacySubset: false
legacySubset: false,

// If set to `true` (the default value), `parse` records information about
// the original source location of each `Node` in the parsed string. See `SourceMapping` and `Node#sources`.
// If set to `false`, `Node#sources` is always be empty.
// The only time you want to set this to `false` is when you want to speed up parsing of
// a large amount of text.
traceSources: true
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think traceSources: false may be a better default. Only in specific cases you need the sources, but peopler are mostly parsing an expression to just evaluate it straightaway. Since it slows down parsing and it is not needed in most use cases, it may be best to keep it turned off by default. What do you think?

}
2 changes: 2 additions & 0 deletions src/core/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ import { importFactory } from './function/import.js'
* {string} randomSeed
* Random seed for seeded pseudo random number generator.
* Set to null to randomly seed.
* {boolean} traceSources
* Enables node's source tracing in the parsed string. Slows down parsing a bit.
* @returns {Object} Returns a bare-bone math.js instance containing
* functions:
* - `import` to add new functions
Expand Down
7 changes: 7 additions & 0 deletions src/core/function/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ export function configFactory (config, emit) {
* {string} randomSeed
* Random seed for seeded pseudo random number generator.
* Set to null to randomly seed.
* {boolean} traceSources
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you document this new configuration option traceSources on the page docs/core/configuration.md?

* Enables node's source tracing in the parsed string. Slows down parsing a bit.
* @return {Object} Returns the current configuration
*/
function _config (options) {
Expand All @@ -71,6 +73,11 @@ export function configFactory (config, emit) {
validateOption(options, 'matrix', MATRIX_OPTIONS)
validateOption(options, 'number', NUMBER_OPTIONS)

if (options.traceSources !== undefined) {
if (typeof options.traceSources !== 'boolean') {
console.warn('Warning: The configuration option "traceSources" must be a boolean.')
}
}
// merge options
deepExtend(config, options)

Expand Down
11 changes: 7 additions & 4 deletions src/expression/node/AccessorNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import { getSafeProperty } from '../../utils/customs.js'
import { factory } from '../../utils/factory.js'
import { accessFactory } from './utils/access.js'
import { defaultMetaOptions } from './Node.js'

const name = 'AccessorNode'
const dependencies = [
Expand Down Expand Up @@ -47,9 +48,10 @@ export const createAccessorNode = /* #__PURE__ */ factory(name, dependencies, ({
* @param {Node} object The object from which to retrieve
* a property or subset.
* @param {IndexNode} index IndexNode containing ranges
* @param {MetaOptions} [meta] The object with additional options for building this node.
*/
constructor (object, index) {
super()
constructor (object, index, meta = defaultMetaOptions) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking aloud here: technically it is not needed to define a default value for meta in each of the Node classes since it will be set in the root Node.js class. What do you think? Would it be OK to leave meta as it is in these constructors and only set it in the constructor of Node.js? It would save a bit of overhead.

super(meta)
if (!isNode(object)) {
throw new TypeError('Node expected for parameter "object"')
}
Expand Down Expand Up @@ -133,10 +135,11 @@ export const createAccessorNode = /* #__PURE__ */ factory(name, dependencies, ({

/**
* Create a clone of this node, a shallow copy
* @param {MetaOptions} [meta] An object with additional options for cloning this node
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a more clear explanation would be:

An object replacing the meta data of the cloned node

What do you think?

* @return {AccessorNode}
*/
clone () {
return new AccessorNode(this.object, this.index)
clone (meta) {
return new AccessorNode(this.object, this.index, meta ?? { sources: this.sources })
}

/**
Expand Down
Loading