Replaced old parser with new parser

This commit is contained in:
Stefan Zermatten
2020-11-10 14:07:22 +02:00
parent 1f0678b50b
commit 3024168e95
25 changed files with 117 additions and 426 deletions

View File

@@ -1,4 +1,4 @@
import math from '/imports/math.js';
import roll from '/imports/parser/roll.js';
import {insertCreatureLog} from '/imports/api/creature/log/CreatureLogs.js';
export default function applyAttack({
@@ -8,7 +8,7 @@ export default function applyAttack({
//targets,
//actionContext
}){
let result = math.roll(1, 20) + prop.rollBonusResult;
let result = roll(1, 20) + prop.rollBonusResult;
insertCreatureLog.call({
log: {
text: `${prop.name} attack. ${result} to hit`,

View File

@@ -1,4 +1,4 @@
import evaluateAndRollString from '/imports/api/creature/computation/afterComputation/evaluateAndRollString.js';
import evaluateString from '/imports/api/creature/computation/afterComputation/evaluateString.js';
//if (Meteor.isServer){
// var sendWebhook = require('/imports/server/discord/webhook.js').default;
@@ -15,7 +15,7 @@ export default function applyDamage({
...creature.variables,
...actionContext,
};
let {result, errors} = evaluateAndRollString(prop.amount, scope);
let {result, errors} = evaluateString(prop.amount, scope, 'reduce');
if (Meteor.isClient){
errors.forEach(e => console.error(e));
console.log(`${result} ${prop.damageType}${prop.damageType !== 'healing'? ' damage': ''}`);

View File

@@ -4,10 +4,10 @@ export default class EffectAggregator{
constructor(stat, memo){
delete this.baseValueErrors;
if (stat.baseValueCalculation){
let {value, errors} = evaluateCalculation(stat.baseValueCalculation, memo);
this.statBaseValue = value;
if (errors.length){
this.baseValueErrors = errors;
let {result, context} = evaluateCalculation(stat.baseValueCalculation, memo);
this.statBaseValue = result.value;
if (context.errors.length){
this.baseValueErrors = context.errors;
}
this.base = this.statBaseValue;
} else {

View File

@@ -1,77 +0,0 @@
import math from '/imports/math.js';
import bareSymbolSubtitutor from '/imports/api/creature/computation/utility/bareSymbolSubtitutor.js';
import substituteRollsWithFunctions from '/imports/api/creature/computation/afterComputation/substituteRollsWithFunctions.js'
export default function evaluateAndRollString(string, scope){
let errors = [];
if (!string){
errors.push('No string provided');
return {result: string, errors};
}
if (!scope) errors.push('No scope provided');
// Parse the string using mathjs
let calc;
try {
calc = math.parse(string);
} catch (e) {
errors.push(e);
return {result: string, errors};
}
// Replace all bare symbols with symbol.value
let transformedCalc = calc.transform(bareSymbolSubtitutor(scope));
// Replace all rolls with the function to call them
transformedCalc = calc.transform(substituteRollsWithFunctions);
// Evaluate the expression to a number or return with substitutions
try {
let result = transformedCalc.evaluate(scope);
return {result, errors};
} catch (e1){
errors.push(e1);
try {
let result = simplifyWithAccessors(transformedCalc, scope).toHTML();
return {result, errors};
} catch (e2){
errors.push(e2);
return {result: transformedCalc.toHTML(), errors};
}
}
}
function simplifyWithAccessors(calc, scope){
let noAccessorCalc = calc.transform(substituteAccessors(scope));
return math.simplify(noAccessorCalc);
}
// returns a function to replace all accessors with either their resolved value
// or a symbol to simplify with
function substituteAccessors(scope){
return function(node){
if (node.isAccessorNode){
try {
return evaluateAccessor(node, scope);
} catch (e) {
return replaceAccessorWithSymbol(node);
}
} else {
return node;
}
}
}
// Throws error if symbol is undefined in scope
function evaluateAccessor(node, scope){
let value = node.evaluate(scope);
if (value === undefined){
throw 'Undefined symbol'
}
return new math.ConstantNode(value);
}
function replaceAccessorWithSymbol(node){
let symbolNode = new math.SymbolNode(node.toString());
return symbolNode;
}

View File

@@ -1,7 +1,7 @@
import math from '/imports/math.js';
import bareSymbolSubtitutor from '/imports/api/creature/computation/utility/bareSymbolSubtitutor.js';
import { parse, CompilationContext } from '/imports/parser/parser.js';
import ConstantNode from '/imports/parser/parseTree/ConstantNode.js';
export default function evaluateString(string, scope){
export default function evaluateString(string, scope, fn = 'compile'){
let errors = [];
if (!string){
errors.push('No string provided');
@@ -11,88 +11,19 @@ export default function evaluateString(string, scope){
if (!scope) errors.push('No scope provided');
// Parse the string using mathjs
let calc;
let node;
try {
calc = math.parse(string);
node = parse(string);
} catch (e) {
errors.push(e);
return {result: string, errors};
}
// Replace all bare symbols with symbol.value
let transformedCalc = calc.transform(bareSymbolSubtitutor(scope));
// Evaluate the expression to a number or return with substitutions
try {
let result = transformedCalc.evaluate(scope);
return {result, errors};
} catch (e1){
errors.push(e1);
try {
let result = simplifyWithAccessors(transformedCalc, scope).toHTML();
return {result, errors};
} catch (e2){
errors.push(e2);
return {result: transformedCalc.toHTML(), errors};
}
let context = new CompilationContext();
let result = node[fn](scope, context);
if (result instanceof ConstantNode){
return {result: result.value, errors: context.errors}
} else {
return {result: result.toString(), errors: context.errors};
}
}
function simplifyWithAccessors(calc, scope){
let noAccessorCalc = calc.transform(substituteAccessors(scope));
return math.simplify(noAccessorCalc);
}
// returns a function to replace all accessors with either their resolved value
// or a symbol to simplify with
function substituteAccessors(scope){
return function(node){
if (node.isAccessorNode){
try {
return evaluateAccessor(node, scope);
} catch (e) {
return replaceAccessorWithSymbol(node);
}
} else {
return node;
}
}
}
// Throws error if symbol is undefined in scope
function evaluateAccessor(node, scope){
let value = node.evaluate(scope);
if (value === undefined){
throw 'Undefined symbol'
}
return new math.ConstantNode(value);
}
function replaceAccessorWithSymbol(node){
let symbolNode = new math.SymbolNode(node.toString());
return symbolNode;
}
/*
function overrideSymbolNodeHTML(symbolNode){
let safeName = escape(symbolNode.name);
symbolNode.toHTML = function(){
console.log('running custom tohtml function')
return `<span class="math-symbol math-substitution-failed">${safeName}</span>`
}
return symbolNode;
}
// Escape special HTML characters
// Copied directly from math.js source to help with overriding toHTML
function escape (value) {
let text = String(value)
text = text.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
return text
}
*/

View File

@@ -1,28 +0,0 @@
import math from '/imports/math.js';
const diceRegex = /d\d+/;
export default function substituteRollsWithFunctions(node){
// TODO also replace dx as 1dx
if (
node.isOperatorNode &&
node.fn === 'multiply' &&
node.implicit &&
node.args[1].isSymbolNode &&
diceRegex.test(node.args[1].name)
){
let diceSize = node.args[1].name.slice(1);
let diceSizeNode = new math.ConstantNode(diceSize);
return new math.FunctionNode('roll', [node.args[0], diceSizeNode]);
} else if (
node.isSymbolNode &&
diceRegex.test(node.name)
) {
let diceSize = node.name.slice(1);
let diceSizeNode = new math.ConstantNode(diceSize);
let diceNumberNode = new math.ConstantNode(1);
return new math.FunctionNode('roll', [diceNumberNode, diceSizeNode]);
} else {
return node;
}
}

View File

@@ -37,9 +37,9 @@ function combineAttribute(stat, aggregator, memo){
stat.modifier = Math.floor((stat.value - 10) / 2);
}
if (stat.attributeType === 'spellSlot'){
let {value, errors} = evaluateCalculation(stat.spellSlotLevelCalculation, memo);
stat.spellSlotLevelValue = value,
stat.spellSlotLevelErrors = errors;
let {result, context} = evaluateCalculation(stat.spellSlotLevelCalculation, memo);
stat.spellSlotLevelValue = result.value;
stat.spellSlotLevelErrors = context.errors;
}
stat.currentValue = stat.value - (stat.damage || 0);
stat.hide = aggregator.hasNoEffects &&

View File

@@ -34,10 +34,10 @@ export default function computeEffect(effect, memo){
} else if(_.contains(['advantage', 'disadvantage', 'fail'], effect.operation)){
effect.result = 1;
} else {
let {value, errors} = evaluateCalculation(effect.calculation, memo);
effect.result = value;
if (errors.length){
effect.errors = errors;
let {result, context} = evaluateCalculation(effect.calculation, memo);
effect.result = result.value;
if (context.errors.length){
effect.errors = context.errors;
}
}
effect.computationDetails.computed = true;

View File

@@ -23,10 +23,10 @@ export default function computeEndStepProperty(prop, memo){
function computeAction(prop, memo){
// Uses
let {value, errors} = evaluateCalculation(prop.uses, memo);
prop.usesResult = value;
if (errors.length){
prop.usesErrors = errors;
let {result, context} = evaluateCalculation(prop.uses, memo);
prop.usesResult = result.value;
if (context.errors.length){
prop.usesErrors = context.errors;
} else {
delete prop.usesErrors;
}
@@ -69,40 +69,40 @@ function computeAction(prop, memo){
function computeAttack(prop, memo){
// Roll bonus
let {value, errors} = evaluateCalculation(prop.rollBonus, memo);
prop.rollBonusResult = value;
if (errors.length){
prop.rollBonusErrors = errors;
let {result, context} = evaluateCalculation(prop.rollBonus, memo);
prop.rollBonusResult = result.value;
if (context.errors.length){
prop.rollBonusErrors = context.errors;
} else {
delete prop.rollBonusErrors;
}
}
function computeSavingThrow(prop, memo){
let {value, errors} = evaluateCalculation(prop.dc, memo);
prop.dcResult = value;
if (errors.length){
prop.dcErrors = errors;
let {result, context} = evaluateCalculation(prop.dc, memo);
prop.dcResult = result.value;
if (context.errors.length){
prop.dcErrors = context.errors;
} else {
delete prop.dcErrors;
}
}
function computeSpellList(prop, memo){
let {value, errors} = evaluateCalculation(prop.maxPrepared, memo);
prop.maxPreparedResult = value;
if (errors.length){
prop.maxPreparedErrors = errors;
let {result, context} = evaluateCalculation(prop.maxPrepared, memo);
prop.maxPreparedResult = result.value;
if (context.errors.length){
prop.maxPreparedErrors = context.errors;
} else {
delete prop.maxPreparedErrors;
}
}
function computeSlot(prop, memo){
let {value, errors} = evaluateCalculation(prop.slotCondition, memo);
prop.slotConditionResult = value;
if (errors.length){
prop.slotConditionErrors = errors;
let {result, context} = evaluateCalculation(prop.slotCondition, memo);
prop.slotConditionResult = result.value;
if (context.errors.length){
prop.slotConditionErrors = context.errors;
} else {
delete prop.slotConditionErrors;
}

View File

@@ -26,10 +26,10 @@ export default function computeToggle(toggle, memo){
} else if (Number.isFinite(+toggle.condition)){
toggle.toggleResult = !!+toggle.condition;
} else {
let {value, errors} = evaluateCalculation(toggle.condition, memo);
toggle.toggleResult = !!value;
if (errors.length){
toggle.errors = errors;
let {result, context} = evaluateCalculation(toggle.condition, memo);
toggle.toggleResult = !!result.value;
if (context.errors.length){
toggle.errors = context.errors;
}
}
toggle.computationDetails.computed = true;

View File

@@ -1,98 +1,41 @@
import computeStat from '/imports/api/creature/computation/computeStat.js';
import math from '/imports/math.js';
import { parse, CompilationContext } from '/imports/parser/parser.js';
import SymbolNode from '/imports/parser/parseTree/SymbolNode.js';
import AccessorNode from '/imports/parser/parseTree/AccessorNode.js';
import ConstantNode from '/imports/parser/parseTree/ConstantNode.js';
/* Convert a calculation into a constant output and errors*/
export default function evaluateCalculation(string, memo){
if (!string) return {errors: [], value: string};
export default function evaluateCalculation(string, memo, fn = 'reduce'){
if (!string) return {
context: {errors: []},
result: new ConstantNode({value: string, type: 'string'}),
};
let errors = [];
// Parse the string using mathjs
// Parse the string
let calc;
try {
calc = math.parse(string);
calc = parse(string);
} catch (e) {
errors.push({
type: 'parsing',
message: e.message || e
});
return {errors, value: string};
return {
context: {errors},
result: new ConstantNode({value: string, type: 'string'}),
};
}
// Ensure all symbol nodes are defined and coputed
calc.traverse(node => {
if (node.isSymbolNode){
if (node instanceof SymbolNode || node instanceof AccessorNode){
let stat = memo.statsByVariableName[node.name];
if (stat && !stat.computationDetails.computed){
computeStat(stat, memo);
}
}
});
// Replace all symbols with their subtitution
let substitutedCalc = calc.transform(
symbolSubtitutor(memo.statsByVariableName, errors)
);
// Evaluate the expression to a number or return with substitutions
try {
let value = substitutedCalc.evaluate(memo.statsByVariableName);
if (typeof value === 'object') value = value.toString();
return {errors, value};
} catch (e){
errors.push({
type: 'evaluation',
message: e.message || e
});
let value = substitutedCalc.toString();
return {errors, value};
}
}
// returns a function to replace all symbols with either their resolved value
// or zero, keeping the errors
function symbolSubtitutor(scope, errors){
return function(node){
// mark symbol nodes that are children of function nodes to be skipped
if (node.isFunctionNode){
let fn = node.fn;
if (fn && fn.isSymbolNode){
fn.skipReplacement = true;
}
return node;
} else if (node.isSymbolNode && node.skipReplacement !== true){
//bare symbols of name "stat", should search for stat.value
let stat = scope[node.name];
if (stat){
if (stat.value === undefined){
errors.push({
type: 'subsitution',
message: `${node.name} does not have a value, set to 0`
});
return new math.ConstantNode(0);
} else {
return new math.ConstantNode(stat.value);
}
} else {
try {
return new math.ConstantNode(node.evaluate(scope));
} catch (e) {
errors.push({
type: 'subsitution',
message: `${node.name} not found, set to 0`
});
return new math.ConstantNode(0);
}
}
} else if (node.isAccessorNode && node.object.isSymbolNode){
try {
let value = node.evaluate(scope);
if (value === undefined) throw 'Not found';
return new math.ConstantNode(value);
} catch (e) {
errors.push({
type: 'subsitution',
message: `${node.toString()} not found, set to 0`
});
return new math.ConstantNode(0);
}
} else {
return node;
}
}
// Evaluate
let context = new CompilationContext();
let result = calc[fn](memo.statsByVariableName, context);
return {result, context};
}

View File

@@ -1,24 +0,0 @@
import math from '/imports/math.js';
export default function bareSymbolSubtitutor(scope){
return function(node, path){
if (!scope) return node;
if (node.isFunctionNode){
let fn = node.fn;
if (fn && fn.isSymbolNode){
fn.skipReplacement = true;
}
return node;
} else if (
node.isSymbolNode &&
path !== 'object' &&
node.skipReplacement !== true
) {
let stat = scope[node.name];
if (!stat) return node;
return new math.ConstantNode(stat.value);
} else {
return node;
}
}
}

View File

@@ -12,4 +12,8 @@ export default class ArrayNode extends ParseNode {
toString(){
return `[${this.values.map(node => node.toString()).join(', ')}]`;
}
traverse(fn){
fn(this);
this.values.forEach(value => value.traverse(fn));
}
}

View File

@@ -50,6 +50,10 @@ export default class CallNode extends ParseNode {
toString(){
return `${this.functionName}(${this.args.map(node => node.toString()).join(', ')})`;
}
traverse(fn){
fn(this);
this.args.forEach(arg => arg.traverse(fn));
}
}
function castArgsToType({fn, scope, context, args, type}){
@@ -65,6 +69,5 @@ function castArgsToType({fn, scope, context, args, type}){
})
}
if (resolvedArgs.failed) return resolvedArgs;
console.log({result})
return result;
}

View File

@@ -28,4 +28,10 @@ export default class IfNode extends ParseNode {
});
}
}
traverse(fn){
fn(this);
this.condition.traverse(fn);
this.consequent.traverse(fn);
this.alternative.traverse(fn);
}
}

View File

@@ -24,4 +24,9 @@ export default class IndexNode extends ParseNode {
toString(){
return `${this.array.toString()}[${this.index.toString()}]`;
}
traverse(fn){
fn(this);
this.array.traverse(fn);
this.index.traverse(fn);
}
}

View File

@@ -24,4 +24,8 @@ export default class NotOperatorNode extends ParseNode {
let {right} = this;
return `!${right.toString()}`;
}
traverse(fn){
fn(this);
this.right.traverse(fn);
}
}

View File

@@ -53,4 +53,9 @@ export default class OperatorNode extends ParseNode {
let {left, right, operator} = this;
return `${left.toString()} ${operator} ${right.toString()}`;
}
traverse(fn){
fn(this);
this.left.traverse(fn);
this.right.traverse(fn);
}
}

View File

@@ -20,4 +20,8 @@ export default class ParenthesisNode extends ParseNode {
toString(){
return `(${this.content.toString()})`;
}
traverse(fn){
fn(this);
this.content.traverse(fn);
}
}

View File

@@ -26,4 +26,8 @@ export default class ParseNode {
return this.roll(scope, context);
}
}
// If traverse isn't implemented, just apply it to the current node
traverse(fn){
fn(this);
}
}

View File

@@ -59,4 +59,9 @@ export default class RollNode extends ParseNode {
reduce(scope, context){
return this.roll(scope, context).reduce(scope, context);
}
traverse(fn){
fn(this);
this.left.traverse(fn);
this.right.traverse(fn);
}
}

View File

@@ -30,4 +30,8 @@ export default class UnaryOperatorNode extends ParseNode {
let {right, operator} = this;
return `${operator}${right.toString()}`;
}
traverse(fn){
fn(this);
this.right.traverse(fn);
}
}

View File

@@ -1,43 +0,0 @@
/*
* Creates a function that takes an object which represents the updates to be
* made as key: value pairs. The function collects these updates, (overwriting
* duplicates), and runs the given callback with the collated updates after the
* `time` has passed without the function being called.
*/
export default function debounceUpdate(callback, time = 300){
let collatedUpdate = {};
let interval;
return (update, ...rest) => {
// Every time we're called, collect the update
for (key in update){
collatedUpdate[key] = update[key];
}
// Reset the clock and set it again to run the callback
clearTimeout(interval);
interval = setTimeout(() => {
// Clock timed out, apply all the collated updates
callback(collatedUpdate, ...rest);
// Reset the collation
collatedUpdate = {};
}, time);
};
};
// Example use
//
// let f = update => {
// console.log({wroteUpdate: update});
// };
// let df = debounceUpdate(f, 300);
// df({name: 'john'});
// df({name: 'jack'});
// df({age: 24, country: 'Ethiopia'});
// df({transport: 'bus'});
//
// --> { wroteUpdate: {
// name: 'jack',
// age: 24,
// country: 'Ethiopia',
// transport: 'bus'
// }}

58
app/package-lock.json generated
View File

@@ -414,11 +414,6 @@
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
},
"complex.js": {
"version": "2.0.11",
"resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.0.11.tgz",
"integrity": "sha512-6IArJLApNtdg1P1dFtn3dnyzoZBEF0MwMnrfF1exSBRpZYoy4yieMkpZhQDC0uwctw48vii0CFVyHfpgZ/DfGw=="
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -499,11 +494,6 @@
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
},
"decimal.js": {
"version": "10.2.0",
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.0.tgz",
"integrity": "sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw=="
},
"deep-extend": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
@@ -583,11 +573,6 @@
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA=="
},
"escape-latex": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz",
"integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw=="
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
@@ -897,11 +882,6 @@
"mime-types": "^2.1.12"
}
},
"fraction.js": {
"version": "4.0.12",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.0.12.tgz",
"integrity": "sha512-8Z1K0VTG4hzYY7kA/1sj4/r1/RWLBD3xwReT/RCrUCbzPszjNQCCsy3ktkU/eaEqX3MYa4pY37a52eiBlPMlhA=="
},
"fs-minipass": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
@@ -1251,11 +1231,6 @@
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
},
"javascript-natural-sort": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz",
"integrity": "sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k="
},
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -1525,21 +1500,6 @@
"resolved": "https://registry.npmjs.org/marked/-/marked-0.8.2.tgz",
"integrity": "sha512-EGwzEeCcLniFX51DhTpmTom+dSA/MG/OBUDjnWtHbEnjAH180VzUeAw+oE4+Zv+CoYBWyRlYOTR0N8SO9R1PVw=="
},
"mathjs": {
"version": "6.6.4",
"resolved": "https://registry.npmjs.org/mathjs/-/mathjs-6.6.4.tgz",
"integrity": "sha512-fvmP89ujJbDAC8ths7FZh7PWdA71dfA5WJVAzJbQhSDCHK1aBk8WRf1XcTw51ERs+sKx9nYBGsRshqmb/oe8Ag==",
"requires": {
"complex.js": "^2.0.11",
"decimal.js": "^10.2.0",
"escape-latex": "^1.2.0",
"fraction.js": "^4.0.12",
"javascript-natural-sort": "^0.7.1",
"seed-random": "^2.2.0",
"tiny-emitter": "^2.1.0",
"typed-function": "^1.1.1"
}
},
"mem": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/mem/-/mem-6.0.1.tgz",
@@ -1828,7 +1788,8 @@
},
"elliptic": {
"version": "6.4.0",
"resolved": "",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz",
"integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=",
"requires": {
"bn.js": "^4.4.0",
"brorand": "^1.0.1",
@@ -2768,11 +2729,6 @@
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
},
"seed-random": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/seed-random/-/seed-random-2.2.0.tgz",
"integrity": "sha1-KpsZ4lCoFwmSMaW5mk2vgLf77VQ="
},
"semver": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
@@ -3008,11 +2964,6 @@
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
"dev": true
},
"tiny-emitter": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
},
"tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
@@ -3065,11 +3016,6 @@
"integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
"dev": true
},
"typed-function": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/typed-function/-/typed-function-1.1.1.tgz",
"integrity": "sha512-RbN7MaTQBZLJYzDENHPA0nUmWT0Ex80KHItprrgbTPufYhIlTePvCXZxyQK7wgn19FW5bnuaBIKcBb5mRWjB1Q=="
},
"underscore": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz",

View File

@@ -29,7 +29,6 @@
"dompurify": "^2.0.10",
"lodash": "^4.17.20",
"marked": "^0.8.2",
"mathjs": "^6.6.4",
"meteor-node-stubs": "^0.3.3",
"moo": "^0.5.1",
"nearley": "^2.19.1",