@mathigon/hilbert
Advanced tools
Comparing version 0.2.2 to 0.2.3
import resolve from 'rollup-plugin-node-resolve'; | ||
export default { | ||
entry: 'index.js', | ||
dest: 'build/hilbert.js', | ||
format: 'cjs', | ||
input: 'index.js', | ||
output: { | ||
file: 'build/hilbert.js', | ||
format: 'cjs' | ||
}, | ||
plugins: [ | ||
@@ -8,0 +10,0 @@ resolve({ |
@@ -140,4 +140,4 @@ 'use strict'; | ||
* Join multiple Arrays | ||
* @param {*[]...} arrays | ||
* @returns {*[]} | ||
* @param {...Array} arrays | ||
* @returns {Array} | ||
*/ | ||
@@ -187,2 +187,3 @@ function join(...arrays) { | ||
function nearlyEquals(x, y, t = tolerance) { | ||
if (isNaN(x) || isNaN(y)) return false; | ||
return Math.abs(x - y) < t; | ||
@@ -265,2 +266,3 @@ } | ||
EE: '∃', | ||
'\'': '’', | ||
@@ -278,2 +280,3 @@ '!=': '≠', | ||
prop: '∝', | ||
oo: '∞', | ||
@@ -338,6 +341,23 @@ '<-': '←', | ||
const SIMPLE_SYMBOLS = '|()[]{}÷,!<>=*/+-–−~^_…°'; | ||
const SIMPLE_SYMBOLS = '|()[]{}÷,!<>=*/+-–−~^_…°•∥⊥\''; | ||
const COMPLEX_SYMBOLS = Object.values(SPECIAL_OPERATORS); | ||
const OPERATOR_SYMBOLS = [...SIMPLE_SYMBOLS, ...COMPLEX_SYMBOLS]; | ||
const ESCAPES = { | ||
'<': '<', | ||
'>': '>' | ||
}; | ||
function escape(char) { | ||
return (char in ESCAPES) ? ESCAPES[char] : char; | ||
} | ||
const SPECIAL = new Set(['sin', 'cos', 'tan', 'sec', 'csc', 'cot', 'arcsin', | ||
'arccos', 'arctan', 'sinh', 'cosh', 'tanh', 'sech', 'csch', 'coth', 'exp', | ||
'log', 'ln', 'det', 'dim', 'mod', 'gcd', 'lcm', 'min', 'max']); | ||
function isSpecialFunction(fn) { | ||
return SPECIAL.has(fn); | ||
} | ||
// ============================================================================= | ||
@@ -422,6 +442,10 @@ | ||
toMathML() { | ||
const variant = isSpecialFunction(this.i) ? ' mathvariant="normal"' : ''; | ||
return `<mi${variant}>${this.i}</mi>`; | ||
} | ||
substitute(vars={}) { return vars[this.i] || this; } | ||
get variables() { return [this.i]; } | ||
toString() { return this.i; } | ||
toMathML() { return `<mi>${this.i}</mi>`; } | ||
} | ||
@@ -444,4 +468,8 @@ | ||
toString() { return this.o.replace('//', '/'); } | ||
toMathML() { return `<mo value="${this.toString()}">${this.toString()}</mo>`; } | ||
get functions() { return [this.o]; } | ||
toMathML() { | ||
const op = escape(this.toString()); | ||
return `<mo value="${op}">${op}</mo>`; | ||
} | ||
} | ||
@@ -550,3 +578,3 @@ | ||
if (words('+ * × · / = < > ≤ ≥').includes(this.fn)) | ||
if (words('+ * × · / = < > ≤ ≥ ≈').includes(this.fn)) | ||
return args.join(' ' + this.fn + ' '); | ||
@@ -575,4 +603,6 @@ | ||
if (isOneOf(this.fn, '+', '=', '<', '>', '≤', '≥')) | ||
return argsF.join(`<mo value="${this.fn}">${this.fn}</mo>`); | ||
if (isOneOf(this.fn, '+', '=', '<', '>', '≤', '≥', '≈')) { | ||
const fn = escape(this.fn); | ||
return argsF.join(`<mo value="${fn}">${fn}</mo>`); | ||
} | ||
@@ -611,4 +641,14 @@ if (isOneOf(this.fn, '*', '×', '·')) { | ||
if (this.fn === 'abs') | ||
return `<mfenced open="|" close="|">${argsF.join(COMMA)}</mfenced>`; | ||
if (this.fn === 'bar') | ||
return `<mover>${addMRow(this.args[0], argsF[0])}<mo value="‾">‾</mo></mover>`; | ||
if (this.fn === 'vec') | ||
return `<mover>${addMRow(this.args[0], argsF[0])}<mo value="→">→</mo></mover>`; | ||
// TODO Implement other functions | ||
return `<mi>${this.fn}</mi><mfenced>${argsF.join(COMMA)}</mfenced>`; | ||
const variant = isSpecialFunction(this.fn) ? ' mathvariant="normal"' : ''; | ||
return `<mi${variant}>${this.fn}</mi><mfenced>${argsF.join(COMMA)}</mfenced>`; | ||
} | ||
@@ -627,6 +667,11 @@ } | ||
if (type === 'NUM') return new ExprNumber(+buffer); | ||
if (type === 'SPACE' && buffer.length > 1) return new ExprSpace(); | ||
if (type === 'STR') return new ExprString(buffer); | ||
if (type === 'NUM') { | ||
// This can happen if users simply type ".", which get parsed as number. | ||
if (isNaN(+buffer)) throw ExprError.invalidExpression(); | ||
return new ExprNumber(+buffer); | ||
} | ||
if (type === 'VAR') { | ||
@@ -749,2 +794,3 @@ if (buffer in SPECIAL_IDENTIFIERS) { | ||
findBinaryFunction(tokens, '^', 'sup'); | ||
findBinaryFunction(tokens, '_', 'sub'); | ||
findBinaryFunction(tokens, '/'); | ||
@@ -755,3 +801,2 @@ return makeTerm(tokens); | ||
function matchBrackets(tokens) { | ||
findBinaryFunction(tokens, '_', 'sub'); | ||
const stack = [[]]; | ||
@@ -758,0 +803,0 @@ |
{ | ||
"name": "@mathigon/hilbert", | ||
"version": "0.2.2", | ||
"version": "0.2.3", | ||
"description": "JavaScript expression parsing, MathML rendering and CAS.", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -9,3 +9,3 @@ // ============================================================================= | ||
import { join, unique } from '@mathigon/core'; | ||
import { CONSTANTS } from './symbols' | ||
import { CONSTANTS, escape, isSpecialFunction } from './symbols' | ||
import { collapseTerm } from './parser' | ||
@@ -91,6 +91,10 @@ import { ExprError } from './errors' | ||
toMathML() { | ||
const variant = isSpecialFunction(this.i) ? ' mathvariant="normal"' : ''; | ||
return `<mi${variant}>${this.i}</mi>`; | ||
} | ||
substitute(vars={}) { return vars[this.i] || this; } | ||
get variables() { return [this.i]; } | ||
toString() { return this.i; } | ||
toMathML() { return `<mi>${this.i}</mi>`; } | ||
} | ||
@@ -113,4 +117,8 @@ | ||
toString() { return this.o.replace('//', '/'); } | ||
toMathML() { return `<mo value="${this.toString()}">${this.toString()}</mo>`; } | ||
get functions() { return [this.o]; } | ||
toMathML() { | ||
const op = escape(this.toString()); | ||
return `<mo value="${op}">${op}</mo>`; | ||
} | ||
} | ||
@@ -117,0 +125,0 @@ |
@@ -9,3 +9,3 @@ // ============================================================================= | ||
import { unique, flatten, words, isOneOf } from '@mathigon/core' | ||
import { BRACKETS } from './symbols' | ||
import { BRACKETS, escape, isSpecialFunction } from './symbols' | ||
import { ExprElement, ExprTerm, ExprNumber } from './elements' | ||
@@ -101,3 +101,3 @@ import { ExprError } from './errors' | ||
if (words('+ * × · / = < > ≤ ≥').includes(this.fn)) | ||
if (words('+ * × · / = < > ≤ ≥ ≈').includes(this.fn)) | ||
return args.join(' ' + this.fn + ' '); | ||
@@ -126,4 +126,6 @@ | ||
if (isOneOf(this.fn, '+', '=', '<', '>', '≤', '≥')) | ||
return argsF.join(`<mo value="${this.fn}">${this.fn}</mo>`); | ||
if (isOneOf(this.fn, '+', '=', '<', '>', '≤', '≥', '≈')) { | ||
const fn = escape(this.fn); | ||
return argsF.join(`<mo value="${fn}">${fn}</mo>`); | ||
} | ||
@@ -162,5 +164,15 @@ if (isOneOf(this.fn, '*', '×', '·')) { | ||
if (this.fn === 'abs') | ||
return `<mfenced open="|" close="|">${argsF.join(COMMA)}</mfenced>`; | ||
if (this.fn === 'bar') | ||
return `<mover>${addMRow(this.args[0], argsF[0])}<mo value="‾">‾</mo></mover>`; | ||
if (this.fn === 'vec') | ||
return `<mover>${addMRow(this.args[0], argsF[0])}<mo value="→">→</mo></mover>`; | ||
// TODO Implement other functions | ||
return `<mi>${this.fn}</mi><mfenced>${argsF.join(COMMA)}</mfenced>`; | ||
const variant = isSpecialFunction(this.fn) ? ' mathvariant="normal"' : ''; | ||
return `<mi${variant}>${this.fn}</mi><mfenced>${argsF.join(COMMA)}</mfenced>`; | ||
} | ||
} |
@@ -22,6 +22,11 @@ // ============================================================================= | ||
if (type === 'NUM') return new ExprNumber(+buffer); | ||
if (type === 'SPACE' && buffer.length > 1) return new ExprSpace(); | ||
if (type === 'STR') return new ExprString(buffer); | ||
if (type === 'NUM') { | ||
// This can happen if users simply type ".", which get parsed as number. | ||
if (isNaN(+buffer)) throw ExprError.invalidExpression(); | ||
return new ExprNumber(+buffer); | ||
} | ||
if (type === 'VAR') { | ||
@@ -144,2 +149,3 @@ if (buffer in SPECIAL_IDENTIFIERS) { | ||
findBinaryFunction(tokens, '^', 'sup'); | ||
findBinaryFunction(tokens, '_', 'sub'); | ||
findBinaryFunction(tokens, '/'); | ||
@@ -150,3 +156,2 @@ return makeTerm(tokens); | ||
export function matchBrackets(tokens) { | ||
findBinaryFunction(tokens, '_', 'sub'); | ||
const stack = [[]]; | ||
@@ -153,0 +158,0 @@ |
@@ -33,2 +33,3 @@ // ============================================================================= | ||
EE: '∃', | ||
'\'': '’', | ||
@@ -46,2 +47,3 @@ '!=': '≠', | ||
prop: '∝', | ||
oo: '∞', | ||
@@ -106,4 +108,21 @@ '<-': '←', | ||
const SIMPLE_SYMBOLS = '|()[]{}÷,!<>=*/+-–−~^_…°'; | ||
const SIMPLE_SYMBOLS = '|()[]{}÷,!<>=*/+-–−~^_…°•∥⊥\''; | ||
const COMPLEX_SYMBOLS = Object.values(SPECIAL_OPERATORS); | ||
export const OPERATOR_SYMBOLS = [...SIMPLE_SYMBOLS, ...COMPLEX_SYMBOLS]; | ||
const ESCAPES = { | ||
'<': '<', | ||
'>': '>' | ||
}; | ||
export function escape(char) { | ||
return (char in ESCAPES) ? ESCAPES[char] : char; | ||
} | ||
const SPECIAL = new Set(['sin', 'cos', 'tan', 'sec', 'csc', 'cot', 'arcsin', | ||
'arccos', 'arctan', 'sinh', 'cosh', 'tanh', 'sech', 'csch', 'coth', 'exp', | ||
'log', 'ln', 'det', 'dim', 'mod', 'gcd', 'lcm', 'min', 'max']); | ||
export function isSpecialFunction(fn) { | ||
return SPECIAL.has(fn); | ||
} |
@@ -20,2 +20,3 @@ // ============================================================================= | ||
test.equal(mathML('xy'), '<mi>xy</mi>'); | ||
test.equal(mathML('log'), '<mi mathvariant="normal">log</mi>'); | ||
test.equal(mathML('x y'), '<mi>x</mi><mi>y</mi>'); | ||
@@ -29,2 +30,8 @@ test.equal(mathML('+'), '<mo value="+">+</mo>'); | ||
tape('HTML Characters', function(test) { | ||
test.equal(mathML('a < b'), '<mi>a</mi><mo value="<"><</mo><mi>b</mi>'); | ||
test.equal(mathML('a > b'), '<mi>a</mi><mo value=">">></mo><mi>b</mi>'); | ||
test.equal(mathML('a ≥ b'), '<mi>a</mi><mo value="≥">≥</mo><mi>b</mi>'); | ||
test.end(); | ||
}); | ||
@@ -52,7 +59,7 @@ tape('Custom Functions', function(test) { | ||
tape('Functions', function(test) { | ||
test.equal(mathML('sin (a + b)'), '<mi>sin</mi><mfenced><mi>a</mi><mo value="+">+</mo><mi>b</mi></mfenced>'); | ||
test.equal(mathML('tan = sin/cos'), '<mi>tan</mi><mo value="=">=</mo><mfrac><mi>sin</mi><mi>cos</mi></mfrac>'); | ||
test.equal(mathML('sinh(x) = (e^x - e^(-x))/2'), '<mi>sinh</mi><mfenced><mi>x</mi></mfenced><mo value="=">=</mo><mfrac><mrow><msup><mi>e</mi><mi>x</mi></msup><mo value="−">−</mo><msup><mi>e</mi><mrow><mo value="−">−</mo><mi>x</mi></mrow></msup></mrow><mn>2</mn></mfrac>'); | ||
test.equal(mathML('ln(x^2) = 2 ln(x)'), '<mi>ln</mi><mfenced><msup><mi>x</mi><mn>2</mn></msup></mfenced><mo value="=">=</mo><mn>2</mn><mi>ln</mi><mfenced><mi>x</mi></mfenced>'); | ||
test.equal(mathML('ln(x/y) = ln(x) - ln(y)'), '<mi>ln</mi><mfenced><mfrac><mi>x</mi><mi>y</mi></mfrac></mfenced><mo value="=">=</mo><mi>ln</mi><mfenced><mi>x</mi></mfenced><mo value="−">−</mo><mi>ln</mi><mfenced><mi>y</mi></mfenced>'); | ||
test.equal(mathML('sin(a + b)'), '<mi mathvariant="normal">sin</mi><mfenced><mi>a</mi><mo value="+">+</mo><mi>b</mi></mfenced>'); | ||
test.equal(mathML('tan = sin/cos'), '<mi mathvariant="normal">tan</mi><mo value="=">=</mo><mfrac><mi mathvariant="normal">sin</mi><mi mathvariant="normal">cos</mi></mfrac>'); | ||
test.equal(mathML('sinh(x) = (e^x - e^(-x))/2'), '<mi mathvariant="normal">sinh</mi><mfenced><mi>x</mi></mfenced><mo value="=">=</mo><mfrac><mrow><msup><mi>e</mi><mi>x</mi></msup><mo value="−">−</mo><msup><mi>e</mi><mrow><mo value="−">−</mo><mi>x</mi></mrow></msup></mrow><mn>2</mn></mfrac>'); | ||
test.equal(mathML('ln(x^2) = 2 ln(x)'), '<mi mathvariant="normal">ln</mi><mfenced><msup><mi>x</mi><mn>2</mn></msup></mfenced><mo value="=">=</mo><mn>2</mn><mi mathvariant="normal">ln</mi><mfenced><mi>x</mi></mfenced>'); | ||
test.equal(mathML('ln(x/y) = ln(x) - ln(y)'), '<mi mathvariant="normal">ln</mi><mfenced><mfrac><mi>x</mi><mi>y</mi></mfrac></mfenced><mo value="=">=</mo><mi mathvariant="normal">ln</mi><mfenced><mi>x</mi></mfenced><mo value="−">−</mo><mi mathvariant="normal">ln</mi><mfenced><mi>y</mi></mfenced>'); | ||
test.equal(mathML('a^(p-1) == 1'), '<msup><mi>a</mi><mrow><mi>p</mi><mo value="−">−</mo><mn>1</mn></mrow></msup><mo value="≡">≡</mo><mn>1</mn>'); | ||
@@ -93,2 +100,3 @@ // test.equal(mathML('log_b(x) = log_k(x)/log_k(b)'), 'xxx'); // TODO Support functions with subscripts | ||
test.equal(mathML('(a+b)'), '<mfenced open="(" close=")"><mi>a</mi><mo value="+">+</mo><mi>b</mi></mfenced>'); | ||
test.equal(mathML('|a+b|'), '<mfenced open="|" close="|"><mi>a</mi><mo value="+">+</mo><mi>b</mi></mfenced>'); | ||
test.equal(mathML('a,b,c'), '<mi>a</mi><mo value=",">,</mo><mi>b</mi><mo value=",">,</mo><mi>c</mi>'); | ||
@@ -95,0 +103,0 @@ test.equal(mathML('(a,b,c)'), '<mfenced open="(" close=")"><mi>a</mi><mo value="," lspace="0">,</mo><mi>b</mi><mo value="," lspace="0">,</mo><mi>c</mi></mfenced>'); |
@@ -45,2 +45,20 @@ // ============================================================================= | ||
tape('super and subscripts', function(test) { | ||
test.equal(str('x^2_n'), '(x^2)_n'); | ||
test.equal(str('x^(2_n)'), 'x^2_n'); | ||
test.equal(str('x_n^2'), 'x_(n^2)'); | ||
test.equal(str('x_1^2 + 2^3_(n^2)'), 'x_(1^2) + (2^3)_(n^2)'); | ||
test.equal(str('x_(n+1) = x_n / 2'), 'x_(n + 1) = x_n / 2'); | ||
test.end(); | ||
}); | ||
tape('symbols', function(test) { | ||
test.equal(str('oo'), '∞'); | ||
test.equal(str('AA A'), '∀ A'); | ||
test.equal(str('EE E'), '∃ E'); | ||
test.equal(str('x in y'), 'x ∈ y'); | ||
test.equal(str('a != b'), 'a ≠ b'); | ||
test.end(); | ||
}); | ||
tape('errors', function(test) { | ||
@@ -47,0 +65,0 @@ test.throws(() => expr('a + + b').collapse()); |
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
66824
1637