@mathigon/hilbert
Advanced tools
Comparing version 0.6.5 to 0.6.6
@@ -26,2 +26,5 @@ 'use strict'; | ||
} | ||
static evalLoop(x) { | ||
return new ExprError('EvalError', `Loop in nested evaluation “${x}”.`); | ||
} | ||
// --------------------------------------------------------------------------- | ||
@@ -201,2 +204,4 @@ // Syntax Errors | ||
// ============================================================================= | ||
const toNumber = (x) => ((typeof x === 'number') ? x : x[0]); | ||
const toInterval = (x) => ((typeof x === 'number') ? [x, x] : x); | ||
/** | ||
@@ -207,7 +212,7 @@ * Maths Expression | ||
/** Evaluates an expression using a given map of variables and functions. */ | ||
evaluate(_vars = {}) { | ||
evaluate(_vars = {}, _privateNested) { | ||
return NaN; | ||
} | ||
interval(vars = {}) { | ||
return [this.evaluate(vars), this.evaluate(vars)]; | ||
interval(vars = {}, _privateNested) { | ||
return toInterval(this.evaluate(vars)); | ||
} | ||
@@ -248,2 +253,28 @@ /** Substitutes a new expression for a variable. */ | ||
// ----------------------------------------------------------------------------- | ||
// Evaluation tools | ||
const LOOP_DETECTION = new Set(); | ||
// TODO Cache results for performance! | ||
function evaluateHelper(name, vars, nested = false) { | ||
var _a; | ||
let value = (_a = vars[name]) !== null && _a !== void 0 ? _a : CONSTANTS[name]; | ||
if (!value) | ||
throw ExprError.undefinedVariable(name); | ||
if (typeof value === 'string' || value instanceof ExprElement) { | ||
if (!nested) | ||
LOOP_DETECTION.clear(); | ||
if (LOOP_DETECTION.has(name)) | ||
throw ExprError.evalLoop(name); | ||
LOOP_DETECTION.add(name); | ||
if (typeof value === 'string') | ||
value = Expression.parse(value); | ||
return value.evaluate(vars, true); | ||
} | ||
else if (typeof value === 'function') { | ||
return value(); | ||
} | ||
else { | ||
return value; | ||
} | ||
} | ||
// ----------------------------------------------------------------------------- | ||
class ExprNumber extends ExprElement { | ||
@@ -272,17 +303,7 @@ constructor(n) { | ||
} | ||
evaluate(vars = {}) { | ||
if (this.i in vars) | ||
return vars[this.i]; | ||
if (this.i in CONSTANTS) | ||
return CONSTANTS[this.i]; | ||
throw ExprError.undefinedVariable(this.i); | ||
evaluate(vars = {}, privateNested) { | ||
return toNumber(evaluateHelper(this.i, vars, privateNested)); | ||
} | ||
interval(vars = {}) { | ||
var _a; | ||
const x = (_a = vars[this.i]) !== null && _a !== void 0 ? _a : CONSTANTS[this.i]; | ||
if (Array.isArray(x)) | ||
return x; | ||
if (typeof x === 'number') | ||
return [x, x]; | ||
throw ExprError.undefinedVariable(this.i); | ||
interval(vars = {}, privateNested) { | ||
return toInterval(evaluateHelper(this.i, vars, privateNested)); | ||
} | ||
@@ -316,6 +337,4 @@ toMathML() { | ||
} | ||
evaluate(vars = {}) { | ||
if (this.s in vars) | ||
return vars[this.s]; | ||
throw ExprError.undefinedVariable(this.s); | ||
evaluate(vars = {}, privateNested) { | ||
return toNumber(evaluateHelper(this.s, vars, privateNested)); | ||
} | ||
@@ -322,0 +341,0 @@ toString() { |
@@ -22,2 +22,5 @@ import { total, words, repeat, unique, flatten, isOneOf, join, last, cache } from '@mathigon/core'; | ||
} | ||
static evalLoop(x) { | ||
return new ExprError('EvalError', `Loop in nested evaluation “${x}”.`); | ||
} | ||
// --------------------------------------------------------------------------- | ||
@@ -197,2 +200,4 @@ // Syntax Errors | ||
// ============================================================================= | ||
const toNumber = (x) => ((typeof x === 'number') ? x : x[0]); | ||
const toInterval = (x) => ((typeof x === 'number') ? [x, x] : x); | ||
/** | ||
@@ -203,7 +208,7 @@ * Maths Expression | ||
/** Evaluates an expression using a given map of variables and functions. */ | ||
evaluate(_vars = {}) { | ||
evaluate(_vars = {}, _privateNested) { | ||
return NaN; | ||
} | ||
interval(vars = {}) { | ||
return [this.evaluate(vars), this.evaluate(vars)]; | ||
interval(vars = {}, _privateNested) { | ||
return toInterval(this.evaluate(vars)); | ||
} | ||
@@ -244,2 +249,28 @@ /** Substitutes a new expression for a variable. */ | ||
// ----------------------------------------------------------------------------- | ||
// Evaluation tools | ||
const LOOP_DETECTION = new Set(); | ||
// TODO Cache results for performance! | ||
function evaluateHelper(name, vars, nested = false) { | ||
var _a; | ||
let value = (_a = vars[name]) !== null && _a !== void 0 ? _a : CONSTANTS[name]; | ||
if (!value) | ||
throw ExprError.undefinedVariable(name); | ||
if (typeof value === 'string' || value instanceof ExprElement) { | ||
if (!nested) | ||
LOOP_DETECTION.clear(); | ||
if (LOOP_DETECTION.has(name)) | ||
throw ExprError.evalLoop(name); | ||
LOOP_DETECTION.add(name); | ||
if (typeof value === 'string') | ||
value = Expression.parse(value); | ||
return value.evaluate(vars, true); | ||
} | ||
else if (typeof value === 'function') { | ||
return value(); | ||
} | ||
else { | ||
return value; | ||
} | ||
} | ||
// ----------------------------------------------------------------------------- | ||
class ExprNumber extends ExprElement { | ||
@@ -268,17 +299,7 @@ constructor(n) { | ||
} | ||
evaluate(vars = {}) { | ||
if (this.i in vars) | ||
return vars[this.i]; | ||
if (this.i in CONSTANTS) | ||
return CONSTANTS[this.i]; | ||
throw ExprError.undefinedVariable(this.i); | ||
evaluate(vars = {}, privateNested) { | ||
return toNumber(evaluateHelper(this.i, vars, privateNested)); | ||
} | ||
interval(vars = {}) { | ||
var _a; | ||
const x = (_a = vars[this.i]) !== null && _a !== void 0 ? _a : CONSTANTS[this.i]; | ||
if (Array.isArray(x)) | ||
return x; | ||
if (typeof x === 'number') | ||
return [x, x]; | ||
throw ExprError.undefinedVariable(this.i); | ||
interval(vars = {}, privateNested) { | ||
return toInterval(evaluateHelper(this.i, vars, privateNested)); | ||
} | ||
@@ -312,6 +333,4 @@ toMathML() { | ||
} | ||
evaluate(vars = {}) { | ||
if (this.s in vars) | ||
return vars[this.s]; | ||
throw ExprError.undefinedVariable(this.s); | ||
evaluate(vars = {}, privateNested) { | ||
return toNumber(evaluateHelper(this.s, vars, privateNested)); | ||
} | ||
@@ -318,0 +337,0 @@ toString() { |
{ | ||
"name": "@mathigon/hilbert", | ||
"version": "0.6.5", | ||
"version": "0.6.6", | ||
"description": "JavaScript expression parsing, MathML rendering and CAS.", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -7,4 +7,4 @@ // ============================================================================= | ||
import {Obj} from '@mathigon/core'; | ||
import {Interval} from './eval'; | ||
import {Expression} from './expression'; | ||
import {CONSTANTS, escape, isSpecialFunction, VOICE_STRINGS} from './symbols'; | ||
@@ -20,7 +20,10 @@ import {ExprError} from './errors'; | ||
export type CustomFunction = ((...args: number[]) => number); | ||
export type VarMap = Obj<number|Interval|CustomFunction>; | ||
export type ExprMap = Obj<ExprElement>; | ||
export type MathMLMap = Obj<(...args: MathMLArgument[]) => string>; | ||
export type VarMap = Record<string, number|string|Interval|ExprElement|CustomFunction>; | ||
export type ExprMap = Record<string, ExprElement>; | ||
export type MathMLMap = Record<string, (...args: MathMLArgument[]) => string>; | ||
const toNumber = (x: number|Interval) => ((typeof x === 'number') ? x : x[0]); | ||
const toInterval = (x: number|Interval): Interval => ((typeof x === 'number') ? [x, x] : x); | ||
/** | ||
@@ -32,8 +35,8 @@ * Maths Expression | ||
/** Evaluates an expression using a given map of variables and functions. */ | ||
evaluate(_vars: VarMap = {}): number { | ||
evaluate(_vars: VarMap = {}, _privateNested?: boolean): number { | ||
return NaN; | ||
} | ||
interval(vars: VarMap = {}): Interval { | ||
return [this.evaluate(vars), this.evaluate(vars)]; | ||
interval(vars: VarMap = {}, _privateNested?: boolean): Interval { | ||
return toInterval(this.evaluate(vars)); | ||
} | ||
@@ -82,4 +85,29 @@ | ||
// ----------------------------------------------------------------------------- | ||
// Evaluation tools | ||
const LOOP_DETECTION = new Set<string>(); | ||
// TODO Cache results for performance! | ||
function evaluateHelper(name: string, vars: VarMap, nested = false): number|Interval { | ||
let value = vars[name] ?? CONSTANTS[name]; | ||
if (!value) throw ExprError.undefinedVariable(name); | ||
if (typeof value === 'string' || value instanceof ExprElement) { | ||
if (!nested) LOOP_DETECTION.clear(); | ||
if (LOOP_DETECTION.has(name)) throw ExprError.evalLoop(name); | ||
LOOP_DETECTION.add(name); | ||
if (typeof value === 'string') value = Expression.parse(value); | ||
return value.evaluate(vars, true); | ||
} else if (typeof value === 'function') { | ||
return value(); | ||
} else { | ||
return value; | ||
} | ||
} | ||
// ----------------------------------------------------------------------------- | ||
export class ExprNumber extends ExprElement { | ||
@@ -114,13 +142,8 @@ | ||
evaluate(vars: VarMap = {}) { | ||
if (this.i in vars) return vars[this.i] as number; | ||
if (this.i in CONSTANTS) return CONSTANTS[this.i]; | ||
throw ExprError.undefinedVariable(this.i); | ||
evaluate(vars: VarMap = {}, privateNested?: boolean) { | ||
return toNumber(evaluateHelper(this.i, vars, privateNested)); | ||
} | ||
interval(vars: VarMap = {}): Interval { | ||
const x = vars[this.i] ?? CONSTANTS[this.i]; | ||
if (Array.isArray(x)) return x; | ||
if (typeof x === 'number') return [x, x]; | ||
throw ExprError.undefinedVariable(this.i); | ||
interval(vars: VarMap = {}, privateNested?: boolean): Interval { | ||
return toInterval(evaluateHelper(this.i, vars, privateNested)); | ||
} | ||
@@ -159,5 +182,4 @@ | ||
evaluate(vars: VarMap = {}) { | ||
if (this.s in vars) return vars[this.s] as number; | ||
throw ExprError.undefinedVariable(this.s); | ||
evaluate(vars: VarMap = {}, privateNested?: boolean) { | ||
return toNumber(evaluateHelper(this.s, vars, privateNested)); | ||
} | ||
@@ -164,0 +186,0 @@ |
@@ -29,3 +29,7 @@ // ============================================================================= | ||
static evalLoop(x: string) { | ||
return new ExprError('EvalError', `Loop in nested evaluation “${x}”.`); | ||
} | ||
// --------------------------------------------------------------------------- | ||
@@ -32,0 +36,0 @@ // Syntax Errors |
@@ -7,9 +7,9 @@ // ============================================================================= | ||
import {Obj} from '@mathigon/core'; | ||
import * as tape from 'tape'; | ||
import {Expression} from '../index'; | ||
import {VarMap} from '../src/elements'; | ||
const expr = (src: string) => Expression.parse(src); | ||
const value = (src: string, vars?: Obj<number>) => expr(src) | ||
const value = (src: string, vars?: VarMap) => expr(src) | ||
.evaluate(vars || {}); | ||
@@ -57,1 +57,9 @@ | ||
}); | ||
tape('Nested Expressions', (test) => { | ||
test.equal(value('a', {a: expr('1+2')}), 3); | ||
test.equal(value('a', {a: expr('b'), b: expr('3+4')}), 7); | ||
test.throws(() => value('a', {a: expr('b+1'), b: expr('2a')})); | ||
test.equal(value('2a', {a: 'sin(pi/2)'}), 2); | ||
test.end(); | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
140635
3727
0