Typescript all the parser things
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { traverse } from '/imports/parser/resolve';
|
||||
import traverse from '/imports/parser/traverse';
|
||||
|
||||
export default function linkCalculationDependencies(dependencyGraph, prop, { propsById }) {
|
||||
prop._computationDetails.calculations.forEach(calcObj => {
|
||||
|
||||
@@ -125,6 +125,6 @@ function parseCalculation(calcObj) {
|
||||
message: prettifyParseError(e),
|
||||
};
|
||||
calcObj.parseError = error;
|
||||
calcObj.parseNode = errorNode.create({ error });
|
||||
calcObj.parseNode = errorNode.create({ error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
export default function computeAction(computation, node) {
|
||||
import CreatureComputation from '/imports/api/engine/computation/CreatureComputation';
|
||||
import { Node } from 'ngraph.graph';
|
||||
|
||||
export default function computeAction(computation: CreatureComputation, node: Node) {
|
||||
const prop = node.data;
|
||||
if (Number.isFinite(prop.uses?.value)) {
|
||||
prop.usesLeft = prop.uses.value - (prop.usesUsed || 0);
|
||||
@@ -2,27 +2,30 @@ import call from '/imports/parser/parseTree/call';
|
||||
import constant from '/imports/parser/parseTree/constant';
|
||||
import operator from '/imports/parser/parseTree/operator';
|
||||
import parenthesis from '/imports/parser/parseTree/parenthesis';
|
||||
import resolve, { toPrimitiveOrString } from '/imports/parser/resolve';
|
||||
import resolve from '/imports/parser/resolve';
|
||||
import toPrimitiveOrString from '/imports/parser/toPrimitiveOrString';
|
||||
|
||||
export default function computeCalculation(computation, node) {
|
||||
export default async function computeCalculation(computation, node) {
|
||||
const calcObj = node.data;
|
||||
if (!calcObj) return;
|
||||
// resolve the parse node into the initial value
|
||||
resolveCalculationNode(calcObj, calcObj.parseNode, computation.scope);
|
||||
await resolveCalculationNode(calcObj, calcObj.parseNode, computation.scope);
|
||||
|
||||
// link and aggregate the effects and proficiencies
|
||||
// link the effects and proficiencies
|
||||
linkCalculationEffects(node, computation);
|
||||
aggregateCalculationEffects(calcObj, id => computation.propsById[id]);
|
||||
linkCalculationProficiencies(node, computation)
|
||||
aggregateCalculationProficiencies(calcObj, id => computation.propsById[id], computation.scope['proficiencyBonus']?.value || 0);
|
||||
|
||||
// Store the unaffected value
|
||||
if (calcObj.effectIds || calcObj.proficiencyIds) {
|
||||
calcObj.unaffected = toPrimitiveOrString(calcObj.valueNode);
|
||||
}
|
||||
|
||||
// Aggregate the effects and proficiencies
|
||||
aggregateCalculationEffects(calcObj, id => computation.propsById[id]);
|
||||
aggregateCalculationProficiencies(calcObj, id => computation.propsById[id], computation.scope['proficiencyBonus']?.value || 0);
|
||||
|
||||
// Resolve the valueNode after effects and proficiencies have been applied to it
|
||||
resolveCalculationNode(calcObj, calcObj.valueNode, computation.scope);
|
||||
await resolveCalculationNode(calcObj, calcObj.valueNode, computation.scope);
|
||||
|
||||
// Store the value as a primitive
|
||||
calcObj.value = toPrimitiveOrString(calcObj.valueNode);
|
||||
@@ -32,10 +35,13 @@ export default function computeCalculation(computation, node) {
|
||||
delete calcObj._localScope;
|
||||
}
|
||||
|
||||
export function resolveCalculationNode(calculation, parseNode, scope, givenContext) {
|
||||
export async function resolveCalculationNode(calculation, parseNode, scope, givenContext) {
|
||||
if (!parseNode) throw new Error('parseNode is required');
|
||||
const fn = calculation._parseLevel;
|
||||
const calculationScope = { ...calculation._localScope, ...scope };
|
||||
const { result: resultNode, context } = resolve(fn, parseNode, calculationScope, givenContext);
|
||||
const { result: resultNode, context } = await resolve(fn, parseNode, calculationScope, givenContext);
|
||||
if (calculation.hash === 1318417319946211 && calculation._key === 'attackRoll') console.log({ calculation, resultNode, parseNode, ers: context.errors })
|
||||
|
||||
calculation.errors = context.errors;
|
||||
calculation.valueNode = resultNode;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { has } from 'lodash';
|
||||
import { resolveCalculationNode } from '/imports/api/engine/computation/computeComputation/computeByType/computeCalculation';
|
||||
|
||||
export default function computePointBuy(computation, node) {
|
||||
export default async function computePointBuy(computation, node) {
|
||||
const prop = node.data;
|
||||
const min = has(prop, 'min.value') ? prop.min.value : null;
|
||||
const max = has(prop, 'max.value') ? prop.max.value : null;
|
||||
prop.spent = 0;
|
||||
prop.values?.forEach(row => {
|
||||
|
||||
for (const row of prop.values || []) {
|
||||
row.spent = 0;
|
||||
if (row.value === undefined) return;
|
||||
const costFunction = EJSON.clone(prop.cost);
|
||||
@@ -22,7 +21,7 @@ export default function computePointBuy(computation, node) {
|
||||
}
|
||||
// Evaluate the cost function
|
||||
if (!costFunction) return;
|
||||
resolveCalculationNode(costFunction, costFunction.parseNode, {
|
||||
await resolveCalculationNode(costFunction, costFunction.parseNode, {
|
||||
...computation.scope, value: row.value
|
||||
});
|
||||
// Write calculation errors
|
||||
@@ -37,7 +36,7 @@ export default function computePointBuy(computation, node) {
|
||||
row.spent = costFunction.value;
|
||||
prop.spent += costFunction.value;
|
||||
}
|
||||
});
|
||||
}
|
||||
prop.pointsLeft = (prop.total?.value || 0) - (prop.spent || 0);
|
||||
if (prop.spent > prop.total?.value) {
|
||||
prop.errors = prop.errors || [];
|
||||
|
||||
@@ -8,11 +8,9 @@ export default function computeVariableAsAttribute(computation, node, prop) {
|
||||
// Apply damage in a way that respects the damage rules, modifying damage if need be
|
||||
// Bound the damage
|
||||
if (!prop.ignoreLowerLimit && prop.damage > prop.total) {
|
||||
console.log(`reducing damage from ${prop.damage} to ${prop.total}`);
|
||||
prop.damage = prop.total;
|
||||
}
|
||||
if (!prop.ignoreUpperLimit && prop.damage < 0) {
|
||||
console.log(`increasing damage from ${prop.damage} to 0`);
|
||||
prop.damage = 0;
|
||||
}
|
||||
// Apply damage
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import getAggregatorResult from './getAggregatorResult';
|
||||
|
||||
export default function computeVariableAsToggle(computation, node, prop) {
|
||||
let result = getAggregatorResult(node, prop) || 0;
|
||||
let result = getAggregatorResult(node) || 0;
|
||||
|
||||
prop.value = !!result || !!prop.enabled || !!prop.condition?.value;
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ import { assert } from 'chai';
|
||||
import computeCreatureComputation from '../../computeCreatureComputation';
|
||||
import clean from '../../utility/cleanProp.testFn';
|
||||
|
||||
export default function () {
|
||||
export default async function () {
|
||||
const computation = buildComputationFromProps(testProperties);
|
||||
computeCreatureComputation(computation);
|
||||
await computeCreatureComputation(computation);
|
||||
|
||||
const prop = computation.propsById['actionId'];
|
||||
assert.equal(prop.summary.value, 'test summary 3 without referencing anything 7');
|
||||
|
||||
@@ -3,9 +3,9 @@ import { assert } from 'chai';
|
||||
import computeCreatureComputation from '../../computeCreatureComputation';
|
||||
import clean from '../../utility/cleanProp.testFn';
|
||||
|
||||
export default function () {
|
||||
export default async function () {
|
||||
const computation = buildComputationFromProps(testProperties);
|
||||
computeCreatureComputation(computation);
|
||||
await computeCreatureComputation(computation);
|
||||
const prop = id => computation.propsById[id];
|
||||
const scope = variableName => computation.scope[variableName];
|
||||
assert.equal(prop('emptyId').value, 0, 'calculates empty props to zero');
|
||||
|
||||
@@ -3,10 +3,13 @@ import { assert } from 'chai';
|
||||
import computeCreatureComputation from '../../computeCreatureComputation.js';
|
||||
import clean from '../../utility/cleanProp.testFn.js';
|
||||
|
||||
export default function () {
|
||||
export default async function () {
|
||||
const computation = buildComputationFromProps(testProperties);
|
||||
computeCreatureComputation(computation);
|
||||
await computeCreatureComputation(computation);
|
||||
const prop = id => computation.propsById[id];
|
||||
// Tag targeted effects make complicated parse trees
|
||||
console.log(prop('attackAction2'));
|
||||
assert.equal(prop('attackAction2').attackRoll.value, 'min(3 + d4, d100)', 'Tag targeted effects change the attack roll correctly');
|
||||
// Tags target effects on attributes
|
||||
assert.equal(prop('taggedCon').value, 26, 'Tagged targeted effects affect attribute values');
|
||||
assert.equal(prop('taggedCon').baseValue.value, 10, 'Tag targeted effects target the attribute itself, not the base value');
|
||||
@@ -14,7 +17,6 @@ export default function () {
|
||||
assert.equal(prop('attackAction').attackRoll.value, 20, 'Tag targeted effects change the attack roll correctly');
|
||||
// Tag target effects can deal with rolls
|
||||
assert.equal(prop('attackAction').attackRoll.value, 20, 'Tag targeted effects change the attack roll correctly');
|
||||
assert.equal(prop('attackAction2').attackRoll.value, 'min(3 + d4, d100)', 'Tag targeted effects change the attack roll correctly');
|
||||
}
|
||||
|
||||
var testProperties = [
|
||||
|
||||
@@ -4,9 +4,9 @@ import computeCreatureComputation from '../../computeCreatureComputation';
|
||||
import clean from '../../utility/cleanProp.testFn';
|
||||
import { applyNestedSetProperties } from '/imports/api/parenting/parentingFunctions';
|
||||
|
||||
export default function () {
|
||||
export default async function () {
|
||||
const computation = buildComputationFromProps(testProperties);
|
||||
computeCreatureComputation(computation);
|
||||
await computeCreatureComputation(computation);
|
||||
const scope = id => computation.scope[id];
|
||||
const prop = id => computation.propsById[id];
|
||||
assert.equal(scope('level').value, 5);
|
||||
|
||||
@@ -4,9 +4,9 @@ import computeCreatureComputation from '../../computeCreatureComputation';
|
||||
import clean from '../../utility/cleanProp.testFn';
|
||||
import { applyNestedSetProperties } from '/imports/api/parenting/parentingFunctions';
|
||||
|
||||
export default function () {
|
||||
export default async function () {
|
||||
const computation = buildComputationFromProps(testProperties);
|
||||
computeCreatureComputation(computation);
|
||||
await computeCreatureComputation(computation);
|
||||
const prop = id => computation.propsById[id];
|
||||
assert.equal(prop('attId').value, 6);
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ import { assert } from 'chai';
|
||||
import computeCreatureComputation from '../../computeCreatureComputation';
|
||||
import clean from '../../utility/cleanProp.testFn';
|
||||
|
||||
export default function () {
|
||||
export default async function () {
|
||||
const computation = buildComputationFromProps(testProperties);
|
||||
computeCreatureComputation(computation);
|
||||
await computeCreatureComputation(computation);
|
||||
const scope = id => computation.scope[id];
|
||||
assert.isTrue(scope('blugeoning').vulnerability);
|
||||
assert.isTrue(scope('customDamage').resistance);
|
||||
|
||||
@@ -4,9 +4,9 @@ import computeCreatureComputation from '../../computeCreatureComputation';
|
||||
import clean from '../../utility/cleanProp.testFn';
|
||||
import { propsFromForest } from '/imports/api/properties/tests/propTestBuilder.testFn';
|
||||
|
||||
export default function () {
|
||||
export default async function () {
|
||||
const computation = buildComputationFromProps(testProperties);
|
||||
computeCreatureComputation(computation);
|
||||
await computeCreatureComputation(computation);
|
||||
const prop = id => computation.propsById[id];
|
||||
assert.equal(prop('strengthId').value, 26);
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@ import computeCreatureComputation from '../../computeCreatureComputation';
|
||||
import clean from '../../utility/cleanProp.testFn';
|
||||
import { applyNestedSetProperties, compareOrder } from '/imports/api/parenting/parentingFunctions';
|
||||
|
||||
export default function () {
|
||||
export default async function () {
|
||||
const computation = buildComputationFromProps(testProperties);
|
||||
computeCreatureComputation(computation);
|
||||
await computeCreatureComputation(computation);
|
||||
const prop = id => computation.propsById[id];
|
||||
const scope = id => computation.scope[id].value;
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@ import { assert } from 'chai';
|
||||
import computeCreatureComputation from '../../computeCreatureComputation.js';
|
||||
import { propsFromForest } from '/imports/api/properties/tests/propTestBuilder.testFn.js';
|
||||
|
||||
export default function () {
|
||||
export default async function () {
|
||||
const computation = buildComputationFromProps(testProperties);
|
||||
computeCreatureComputation(computation);
|
||||
await computeCreatureComputation(computation);
|
||||
const prop = id => computation.propsById[id];
|
||||
assert.equal(prop('strengthId').value, 11, 'Point buys should apply a base value when active');
|
||||
}
|
||||
|
||||
@@ -4,10 +4,10 @@ import computeCreatureComputation from '../../computeCreatureComputation';
|
||||
import clean from '../../utility/cleanProp.testFn';
|
||||
import { applyNestedSetProperties, compareOrder } from '/imports/api/parenting/parentingFunctions';
|
||||
|
||||
export default function () {
|
||||
export default async function () {
|
||||
const computation = buildComputationFromProps(testProperties);
|
||||
const hasLink = computation.dependencyGraph.hasLink;
|
||||
computeCreatureComputation(computation);
|
||||
await computeCreatureComputation(computation);
|
||||
const prop = id => computation.propsById[id];
|
||||
assert.equal(
|
||||
prop('strengthId').value, 8,
|
||||
|
||||
@@ -3,9 +3,9 @@ import { assert } from 'chai';
|
||||
import computeCreatureComputation from '../../computeCreatureComputation';
|
||||
import clean from '../../utility/cleanProp.testFn';
|
||||
|
||||
export default function () {
|
||||
export default async function () {
|
||||
const computation = buildComputationFromProps(testProperties);
|
||||
computeCreatureComputation(computation);
|
||||
await computeCreatureComputation(computation);
|
||||
const prop = id => computation.propsById[id];
|
||||
|
||||
assert.equal(prop('atheleticsId').proficiency, 2, 'Inherits proficiency from ability');
|
||||
|
||||
@@ -4,7 +4,7 @@ import embedInlineCalculations from './utility/embedInlineCalculations';
|
||||
import { removeEmptyCalculations } from './buildComputation/parseCalculationFields';
|
||||
import path from 'ngraph.path';
|
||||
|
||||
export default function computeCreatureComputation(computation) {
|
||||
export default async function computeCreatureComputation(computation) {
|
||||
const stack = [];
|
||||
// Computation scope of {variableName: prop}
|
||||
const graph = computation.dependencyGraph;
|
||||
@@ -31,7 +31,7 @@ export default function computeCreatureComputation(computation) {
|
||||
top._visited = true;
|
||||
stack.pop();
|
||||
// Compute the top object of the stack
|
||||
compute(computation, top);
|
||||
await compute(computation, top);
|
||||
} else {
|
||||
top._visitedChildren = true;
|
||||
// Push dependencies to graph to be computed first
|
||||
@@ -40,14 +40,16 @@ export default function computeCreatureComputation(computation) {
|
||||
}
|
||||
|
||||
// Finish the props after the dependency graph has been traversed
|
||||
computation.props.forEach(finalizeProp);
|
||||
for (const prop of computation.props) {
|
||||
finalizeProp(prop);
|
||||
}
|
||||
}
|
||||
|
||||
function compute(computation, node) {
|
||||
async function compute(computation, node) {
|
||||
// Determine the prop's active status by its toggles
|
||||
computeToggles(computation, node);
|
||||
// Compute the property by type
|
||||
computeByType[node.data?.type || '_variable']?.(computation, node);
|
||||
await computeByType[node.data?.type || '_variable']?.(computation, node);
|
||||
}
|
||||
|
||||
function pushDependenciesToStack(nodeId, graph, stack, computation) {
|
||||
|
||||
@@ -1,16 +1,29 @@
|
||||
import { get } from 'lodash';
|
||||
|
||||
export default function applyFnToKey(doc, key, fn){
|
||||
if (key.includes('.$')){
|
||||
export default function applyFnToKey(doc, key, fn) {
|
||||
if (key.includes('.$')) {
|
||||
applyToArrayKey(doc, key, fn);
|
||||
} else {
|
||||
applyToSingleKey(doc, key, fn);
|
||||
}
|
||||
}
|
||||
|
||||
function applyToSingleKey(doc, key, fn){
|
||||
export async function applyFnToKeyAsync(doc, key, fn) {
|
||||
if (key.includes('.$')) {
|
||||
await applyToArrayKeyAsync(doc, key, fn);
|
||||
} else {
|
||||
await applyToSingleKeyAsync(doc, key, fn);
|
||||
}
|
||||
}
|
||||
|
||||
function applyToSingleKey(doc, key, fn) {
|
||||
// call the function with the current value and document for context
|
||||
fn(doc, key);
|
||||
return fn(doc, key);
|
||||
}
|
||||
|
||||
async function applyToSingleKeyAsync(doc, key, fn) {
|
||||
// call the function with the current value and document for context
|
||||
return await fn(doc, key);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -19,7 +32,7 @@ function applyToSingleKey(doc, key, fn){
|
||||
* Warning: Order might be confusing, it will traverse the deepest array in order
|
||||
* but the shallower arrays in reverse order
|
||||
*/
|
||||
function applyToArrayKey(doc, key, fn){
|
||||
function applyToArrayKey(doc, key, fn) {
|
||||
const keySplit = key.split('.$');
|
||||
// Stack based depth first traversal of arrays
|
||||
const array = get(doc, keySplit[0]);
|
||||
@@ -30,11 +43,12 @@ function applyToArrayKey(doc, key, fn){
|
||||
currentPath: keySplit[0],
|
||||
indices: [],
|
||||
}];
|
||||
while(stack.length){
|
||||
while (stack.length) {
|
||||
const state = stack.pop();
|
||||
for (let index in state.array){
|
||||
if (!state) break;
|
||||
for (let index in state.array) {
|
||||
const currentPath = `${state.currentPath}[${index}]${state.paths[0]}`
|
||||
if (state.paths.length == 1){
|
||||
if (state.paths.length == 1) {
|
||||
applyToSingleKey(doc, currentPath, fn);
|
||||
} else {
|
||||
const array = get(doc, currentPath);
|
||||
@@ -49,3 +63,35 @@ function applyToArrayKey(doc, key, fn){
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function applyToArrayKeyAsync(doc, key, fn) {
|
||||
const keySplit = key.split('.$');
|
||||
// Stack based depth first traversal of arrays
|
||||
const array = get(doc, keySplit[0]);
|
||||
if (!array) return;
|
||||
const stack = [{
|
||||
array,
|
||||
paths: keySplit.slice(1),
|
||||
currentPath: keySplit[0],
|
||||
indices: [],
|
||||
}];
|
||||
while (stack.length) {
|
||||
const state = stack.pop();
|
||||
if (!state) break;
|
||||
for (let index in state.array) {
|
||||
const currentPath = `${state.currentPath}[${index}]${state.paths[0]}`
|
||||
if (state.paths.length == 1) {
|
||||
await applyToSingleKey(doc, currentPath, fn);
|
||||
} else {
|
||||
const array = get(doc, currentPath);
|
||||
if (!array) return;
|
||||
stack.push({
|
||||
array,
|
||||
paths: state.paths.slice(1),
|
||||
currentPath,
|
||||
indices: [...state.indices, index],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user