Typescript all the parser things
This commit is contained in:
@@ -1,28 +0,0 @@
|
||||
import { ParseNode } from '/imports/parser/parser';
|
||||
import { ResolvedResult, Context } from '/imports/parser/resolve';
|
||||
|
||||
export type ResolveLevel = 'compile' | 'roll' | 'reduce';
|
||||
|
||||
export default interface NodeFactory {
|
||||
create(node: Partial<ParseNode>): ParseNode;
|
||||
|
||||
compile?(
|
||||
node: ParseNode, scope: Record<string, any>, context: Context
|
||||
): ResolvedResult;
|
||||
|
||||
roll?(
|
||||
node: ParseNode, scope: Record<string, any>, context: Context
|
||||
): ResolvedResult;
|
||||
|
||||
reduce?(
|
||||
node: ParseNode, scope: Record<string, any>, context: Context
|
||||
): ResolvedResult;
|
||||
|
||||
resolve?(
|
||||
fn: ResolveLevel, node: ParseNode, scope: Record<string, any>, context: Context
|
||||
): ResolvedResult;
|
||||
|
||||
toString(node: ParseNode): string;
|
||||
traverse?(node: ParseNode, fn: (node: ParseNode) => any): ReturnType<typeof fn>;
|
||||
map?(node: ParseNode, fn: (node: ParseNode) => any): ReturnType<typeof fn>;
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
import accessor from './accessor';
|
||||
import array from './array';
|
||||
import call from './call';
|
||||
import constant from './constant';
|
||||
import error from './error';
|
||||
import ifNode from './if';
|
||||
import index from './indexNode';
|
||||
import not from './not';
|
||||
import operator from './operator';
|
||||
import parenthesis from './parenthesis';
|
||||
import roll from './roll';
|
||||
import rollArray from './rollArray';
|
||||
import unaryOperator from './unaryOperator';
|
||||
import NodeFactory from '/imports/parser/parseTree/NodeFactory';
|
||||
|
||||
const factories: Record<string, NodeFactory> = {
|
||||
accessor,
|
||||
array,
|
||||
call,
|
||||
constant,
|
||||
error,
|
||||
if: ifNode,
|
||||
index,
|
||||
not,
|
||||
operator,
|
||||
parenthesis,
|
||||
roll,
|
||||
rollArray,
|
||||
// What used to be symbols are now just treated as accessors without a path
|
||||
symbol: accessor,
|
||||
unaryOperator,
|
||||
};
|
||||
|
||||
export default factories;
|
||||
@@ -1,28 +1,20 @@
|
||||
import constant from './constant';
|
||||
import array from './array';
|
||||
import resolve, { Context, ResolvedResult } from '/imports/parser/resolve';
|
||||
import constant from '/imports/parser/parseTree/constant';
|
||||
import array from '/imports/parser/parseTree/array';
|
||||
import ResolvedResult from '/imports/parser/types/ResolvedResult';
|
||||
import { getFromScope } from '/imports/api/creature/creatures/CreatureVariables';
|
||||
import NodeFactory from '/imports/parser/parseTree/NodeFactory';
|
||||
import ResolveLevelFunction from '/imports/parser/types/ResolveLevelFunction';
|
||||
|
||||
export type AccessorNode = {
|
||||
parseType: 'accessor';
|
||||
parseType: 'accessor' | 'symbol';
|
||||
path?: string[];
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface AccessorFactory extends NodeFactory {
|
||||
type AccessorFactory = {
|
||||
create(node: Partial<AccessorNode>): AccessorNode;
|
||||
compile(
|
||||
node: AccessorNode, scope: Record<string, any>, context: Context
|
||||
): ResolvedResult;
|
||||
roll?: undefined;
|
||||
resolve?: undefined;
|
||||
reduce(
|
||||
node: AccessorNode, scope: Record<string, any>, context: Context
|
||||
): ResolvedResult;
|
||||
compile: ResolveLevelFunction<AccessorNode>;
|
||||
reduce: ResolveLevelFunction<AccessorNode>;
|
||||
toString(node: AccessorNode): string;
|
||||
traverse?: undefined;
|
||||
map?: undefined;
|
||||
}
|
||||
|
||||
const accessor: AccessorFactory = {
|
||||
@@ -33,9 +25,7 @@ const accessor: AccessorFactory = {
|
||||
name,
|
||||
};
|
||||
},
|
||||
compile(
|
||||
node: AccessorNode, scope: Record<string, any>, context: Context
|
||||
): ResolvedResult {
|
||||
async compile(node, scope, context) {
|
||||
let value = getFromScope(node.name, scope);
|
||||
// Get the value from the given path
|
||||
node.path?.forEach(name => {
|
||||
@@ -102,9 +92,9 @@ const accessor: AccessorFactory = {
|
||||
context,
|
||||
};
|
||||
},
|
||||
reduce(node, scope, context): ResolvedResult {
|
||||
let { result } = accessor.compile(node, scope, context);
|
||||
({ result } = resolve('reduce', result, scope, context));
|
||||
async reduce(node, scope, context, inputProvider, resolveOthers): Promise<ResolvedResult> {
|
||||
let { result } = await accessor.compile(node, scope, context, inputProvider, resolveOthers);
|
||||
({ result } = await resolveOthers('reduce', result, scope, context, inputProvider));
|
||||
if (result.parseType === 'accessor') {
|
||||
return {
|
||||
result: constant.create({
|
||||
|
||||
@@ -1,28 +1,26 @@
|
||||
import resolve, { toString, traverse, map, Context, ResolvedResult } from '/imports/parser/resolve';
|
||||
import constant from './constant';
|
||||
import NodeFactory, { ResolveLevel } from '/imports/parser/parseTree/NodeFactory';
|
||||
import { serialMap } from '/imports/api/utility/asyncMap';
|
||||
import constant from '/imports/parser/parseTree/constant';
|
||||
import ParseNode from '/imports/parser/parseTree/ParseNode';
|
||||
import ResolveFunction from '/imports/parser/types/ResolveFunction';
|
||||
import MapFunction from '/imports/parser/types/MapFunction';
|
||||
import TraverseFunction from '/imports/parser/types/TraverseFunction';
|
||||
import ToStringFunction from '/imports/parser/types/ToStringFunction';
|
||||
|
||||
export type ArrayNode = {
|
||||
parseType: 'array';
|
||||
values: ParseNode[];
|
||||
}
|
||||
|
||||
interface ArrayFactory extends NodeFactory {
|
||||
type ArrayFactory = {
|
||||
create(node: Partial<ArrayNode>): ArrayNode;
|
||||
fromConstantArray(array: (string | number | boolean | undefined)[]): ArrayNode;
|
||||
compile?: undefined;
|
||||
roll?: undefined;
|
||||
reduce?: undefined;
|
||||
resolve(
|
||||
fn: ResolveLevel, node: ArrayNode, scope: Record<string, any>, context: Context
|
||||
): ResolvedResult;
|
||||
toString(node: ArrayNode): string;
|
||||
traverse(node: ArrayNode, fn: (node: ParseNode) => any): ReturnType<typeof fn>;
|
||||
map(node: ArrayNode, fn: (node: ParseNode) => any): ReturnType<typeof fn>;
|
||||
resolve: ResolveFunction<ArrayNode>;
|
||||
toString: ToStringFunction<ArrayNode>;
|
||||
traverse: TraverseFunction<ArrayNode>;
|
||||
map: MapFunction<ArrayNode>;
|
||||
}
|
||||
|
||||
const array: ArrayFactory = {
|
||||
const arrayFactory: ArrayFactory = {
|
||||
create({ values }: { values: ParseNode[] }) {
|
||||
return {
|
||||
parseType: 'array',
|
||||
@@ -44,32 +42,33 @@ const array: ArrayFactory = {
|
||||
return constant.create({ value: undefined });
|
||||
}
|
||||
});
|
||||
return array.create({ values });
|
||||
return arrayFactory.create({ values });
|
||||
},
|
||||
resolve(fn, node, scope, context): ResolvedResult {
|
||||
const values = node.values.map(node => {
|
||||
const { result } = resolve(fn, node, scope, context);
|
||||
return result;
|
||||
});
|
||||
async resolve(fn, node, scope, context, inputProvider, resolveOthers) {
|
||||
const values: ParseNode[] = [];
|
||||
for (const val of node.values) {
|
||||
const { result } = await resolveOthers(fn, val, scope, context, inputProvider);
|
||||
values.push(result);
|
||||
}
|
||||
return {
|
||||
result: array.create({ values }),
|
||||
result: arrayFactory.create({ values }),
|
||||
context,
|
||||
};
|
||||
},
|
||||
toString(node) {
|
||||
return `[${node.values.map(value => toString(value)).join(', ')}]`;
|
||||
toString(node, toStringOthers) {
|
||||
return `[${node.values.map(value => toStringOthers(value)).join(', ')}]`;
|
||||
},
|
||||
traverse(node, fn) {
|
||||
traverse(node, fn, traverseOthers) {
|
||||
fn(node);
|
||||
node.values.forEach(value => traverse(value, fn));
|
||||
node.values.forEach(value => traverseOthers(value, fn));
|
||||
},
|
||||
map(node, fn) {
|
||||
const resultingNode = fn(node);
|
||||
async map(node, fn, mapOthers) {
|
||||
const resultingNode = await fn(node);
|
||||
if (resultingNode === node) {
|
||||
node.values = node.values.map(value => map(value, fn));
|
||||
node.values = await serialMap(node.values, async value => await mapOthers(value, fn));
|
||||
}
|
||||
return resultingNode;
|
||||
},
|
||||
}
|
||||
|
||||
export default array;
|
||||
export default arrayFactory;
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import error from './error';
|
||||
import constant from './constant';
|
||||
console.log('call.ts imports')
|
||||
import error from '/imports/parser/parseTree/error';
|
||||
import constant from '/imports/parser/parseTree/constant';
|
||||
import functions, { ParserFunction } from '/imports/parser/functions';
|
||||
import resolve, { toString, traverse, map, Context, ResolvedResult } from '/imports/parser/resolve';
|
||||
import Context from '../types/Context';
|
||||
import ResolvedResult from '../types/ResolvedResult';
|
||||
import ParseNode from '/imports/parser/parseTree/ParseNode';
|
||||
import NodeFactory, { ResolveLevel } from '/imports/parser/parseTree/NodeFactory';
|
||||
import { serialMap } from '/imports/api/utility/asyncMap';
|
||||
import ResolveFunction from '/imports/parser/types/ResolveFunction';
|
||||
import ResolveLevel from '/imports/parser/types/ResolveLevel';
|
||||
import TraverseFunction from '/imports/parser/types/TraverseFunction';
|
||||
import MapFunction from '/imports/parser/types/MapFunction';
|
||||
import ToStringFunction from '/imports/parser/types/ToStringFunction';
|
||||
|
||||
export type CallNode = {
|
||||
parseType: 'call';
|
||||
@@ -11,14 +18,12 @@ export type CallNode = {
|
||||
args: ParseNode[];
|
||||
}
|
||||
|
||||
interface CallFactory extends NodeFactory {
|
||||
type CallFactory = {
|
||||
create(node: Partial<CallNode>): CallNode;
|
||||
resolve(
|
||||
fn: ResolveLevel, node: CallNode, scope: Record<string, any>, context: Context
|
||||
): ResolvedResult;
|
||||
toString(node: CallNode): string;
|
||||
traverse(node: CallNode, fn: (node: ParseNode) => any): ReturnType<typeof fn>;
|
||||
map(node: CallNode, fn: (node: ParseNode) => any): ReturnType<typeof fn>;
|
||||
resolve: ResolveFunction<CallNode>;
|
||||
toString: ToStringFunction<CallNode>;
|
||||
traverse: TraverseFunction<CallNode>;
|
||||
map: MapFunction<CallNode>;
|
||||
checkArguments(node: CallNode, fn: ResolveLevel, func: ParserFunction,
|
||||
resolvedArgs: ParseNode[], context: Context): boolean;
|
||||
}
|
||||
@@ -31,7 +36,7 @@ const call: CallFactory = {
|
||||
args,
|
||||
}
|
||||
},
|
||||
resolve(fn, node, scope, context): ResolvedResult {
|
||||
async resolve(fn, node, scope, context, inputProvider, resolveOthers): Promise<ResolvedResult> {
|
||||
const func = functions[node.functionName];
|
||||
// Check that the function exists
|
||||
if (!func) {
|
||||
@@ -46,7 +51,7 @@ const call: CallFactory = {
|
||||
}
|
||||
|
||||
// Resolve a given node to a maximum depth of resolution
|
||||
const resolveToLevel = (node, maxResolveFn = 'reduce'): ResolvedResult => {
|
||||
const resolveToLevel = (node, maxResolveFn = 'reduce'): Promise<ResolvedResult> => {
|
||||
// Determine the actual depth to resolve to
|
||||
let resolveFn: ResolveLevel = 'reduce';
|
||||
if (fn === 'compile' || maxResolveFn === 'compile') {
|
||||
@@ -55,14 +60,15 @@ const call: CallFactory = {
|
||||
resolveFn = 'roll';
|
||||
}
|
||||
// Resolve
|
||||
return resolve(resolveFn, node, scope, context);
|
||||
return resolveOthers(resolveFn, node, scope, context, inputProvider);
|
||||
}
|
||||
|
||||
// Resolve the arguments
|
||||
const resolvedArgs = node.args.map((arg, i) => {
|
||||
const { result } = resolveToLevel(arg, func.maxResolveLevels?.[i]);
|
||||
return result;
|
||||
});
|
||||
const resolvedArgs: ParseNode[] = [];
|
||||
for (const [i, arg] of node.args.entries()) {
|
||||
const { result } = await resolveToLevel(arg, func.maxResolveLevels?.[i]);
|
||||
resolvedArgs.push(result);
|
||||
}
|
||||
|
||||
// Check that the arguments match what is expected
|
||||
const checkFailed = call.checkArguments(node, fn, func, resolvedArgs, context);
|
||||
@@ -116,7 +122,7 @@ const call: CallFactory = {
|
||||
};
|
||||
} else {
|
||||
// Resolve the return value
|
||||
return resolve(fn, value, scope, context);
|
||||
return resolveOthers(fn, value, scope, context, inputProvider);
|
||||
}
|
||||
} catch (err) {
|
||||
context.error(`Internal error: ${err.message || err}`);
|
||||
@@ -129,22 +135,22 @@ const call: CallFactory = {
|
||||
}
|
||||
}
|
||||
},
|
||||
toString(node) {
|
||||
return `${node.functionName}(${node.args.map(arg => toString(arg)).join(', ')})`;
|
||||
toString(node, toStringOthers) {
|
||||
return `${node.functionName}(${node.args.map(arg => toStringOthers(arg)).join(', ')})`;
|
||||
},
|
||||
traverse(node, fn) {
|
||||
traverse(node, fn, traverseOthers) {
|
||||
fn(node);
|
||||
node.args.forEach(arg => traverse(arg, fn));
|
||||
node.args.forEach(arg => traverseOthers(arg, fn));
|
||||
},
|
||||
map(node, fn) {
|
||||
const resultingNode = fn(node);
|
||||
async map(node, fn, mapOthers) {
|
||||
const resultingNode = await fn(node);
|
||||
if (resultingNode === node) {
|
||||
node.args = node.args.map(arg => map(arg, fn));
|
||||
node.args = await serialMap(node.args, async arg => mapOthers(arg, fn));
|
||||
}
|
||||
return resultingNode;
|
||||
},
|
||||
checkArguments(node, fn, func, resolvedArgs, context) {
|
||||
const argumentsExpected = func.arguments;
|
||||
checkArguments(callNode, fn, func, resolvedArgs, context) {
|
||||
const argumentsExpected = func.arguments as any;
|
||||
// Check that the number of arguments matches the number expected
|
||||
if (
|
||||
!argumentsExpected.anyLength &&
|
||||
@@ -152,7 +158,7 @@ const call: CallFactory = {
|
||||
resolvedArgs.length < (func.minArguments ?? argumentsExpected.length)
|
||||
) {
|
||||
context.error('Incorrect number of arguments ' +
|
||||
`to ${node.functionName} function, ` +
|
||||
`to ${callNode.functionName} function, ` +
|
||||
`expected ${argumentsExpected.length} got ${resolvedArgs.length}`);
|
||||
return true;
|
||||
}
|
||||
@@ -160,18 +166,22 @@ const call: CallFactory = {
|
||||
let failed = false;
|
||||
// Check that each argument is of the correct type
|
||||
resolvedArgs.forEach((node, index) => {
|
||||
let type: string;
|
||||
let type;
|
||||
if (argumentsExpected.anyLength) {
|
||||
type = argumentsExpected[0];
|
||||
} else {
|
||||
type = argumentsExpected[index];
|
||||
}
|
||||
if (type === 'parseNode') return;
|
||||
if (node.parseType !== type && node.valueType !== type) failed = true;
|
||||
if (
|
||||
node.parseType !== type
|
||||
&& node.parseType === 'constant'
|
||||
&& node.valueType !== type
|
||||
) failed = true;
|
||||
if (failed && fn === 'reduce') {
|
||||
const typeName = typeof type === 'string' ? type : type.constructor.name;
|
||||
const nodeName = node.parseType;
|
||||
context.error(`Incorrect arguments to ${node.functionName} function` +
|
||||
context.error(`Incorrect arguments to ${callNode.functionName} function` +
|
||||
`expected ${typeName} got ${nodeName}`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import NodeFactory from '/imports/parser/parseTree/NodeFactory';
|
||||
import { Context, ResolvedResult } from '/imports/parser/resolve';
|
||||
import ParseNode from '/imports/parser/parseTree/ParseNode';
|
||||
import ResolveLevelFunction from '/imports/parser/types/ResolveLevelFunction';
|
||||
|
||||
export type ConstantValueType = number | string | boolean | undefined
|
||||
|
||||
@@ -10,17 +10,17 @@ export type ConstantNode = {
|
||||
valueType: 'number' | 'string' | 'boolean' | 'undefined';
|
||||
}
|
||||
|
||||
interface ConstantFactory extends NodeFactory {
|
||||
export type FiniteNumberConstantNode = {
|
||||
parseType: 'constant';
|
||||
value: number;
|
||||
// TODO replace all `constantNode.valueType` with `typeof constantNode.value`
|
||||
valueType: 'number';
|
||||
}
|
||||
|
||||
type ConstantFactory = {
|
||||
create({ value }: { value: ConstantValueType }): ConstantNode;
|
||||
compile(
|
||||
node: ConstantNode, scope: Record<string, any>, context: Context
|
||||
): ResolvedResult;
|
||||
roll?: undefined;
|
||||
reduce?: undefined;
|
||||
resolve?: undefined;
|
||||
compile: ResolveLevelFunction<ConstantNode>;
|
||||
toString(node: ConstantNode): string;
|
||||
traverse?: undefined;
|
||||
map?: undefined;
|
||||
}
|
||||
|
||||
const constant: ConstantFactory = {
|
||||
@@ -31,7 +31,7 @@ const constant: ConstantFactory = {
|
||||
value,
|
||||
}
|
||||
},
|
||||
compile(node, scope, context) {
|
||||
async compile(node, scope, context) {
|
||||
return { result: node, context };
|
||||
},
|
||||
toString(node) {
|
||||
@@ -39,4 +39,11 @@ const constant: ConstantFactory = {
|
||||
},
|
||||
}
|
||||
|
||||
export function isFiniteNode(node: ParseNode): node is FiniteNumberConstantNode {
|
||||
return node.parseType === 'constant'
|
||||
&& node.valueType === 'number'
|
||||
&& typeof node.value === 'number'
|
||||
&& isFinite(node.value);
|
||||
}
|
||||
|
||||
export default constant;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import NodeFactory from '/imports/parser/parseTree/NodeFactory';
|
||||
import ParseNode from '/imports/parser/parseTree/ParseNode';
|
||||
import { Context, ResolvedResult } from '/imports/parser/resolve';
|
||||
import ResolveLevelFunction from '/imports/parser/types/ResolveLevelFunction';
|
||||
import ToStringFunction from '/imports/parser/types/ToStringFunction';
|
||||
|
||||
export type ErrorNode = {
|
||||
parseType: 'error';
|
||||
@@ -8,17 +8,10 @@ export type ErrorNode = {
|
||||
error: string;
|
||||
}
|
||||
|
||||
interface ErrorFactory extends NodeFactory {
|
||||
interface ErrorFactory {
|
||||
create(node: Partial<ErrorNode>): ErrorNode;
|
||||
compile(
|
||||
node: ErrorNode, scope: Record<string, any>, context: Context
|
||||
): ResolvedResult;
|
||||
roll?: undefined;
|
||||
reduce?: undefined;
|
||||
resolve?: undefined;
|
||||
toString(node: ErrorNode): string;
|
||||
traverse?: undefined;
|
||||
map?: undefined;
|
||||
compile: ResolveLevelFunction<ErrorNode>;
|
||||
toString: ToStringFunction<ErrorNode>;
|
||||
}
|
||||
|
||||
const error: ErrorFactory = {
|
||||
@@ -29,7 +22,7 @@ const error: ErrorFactory = {
|
||||
error,
|
||||
}
|
||||
},
|
||||
compile(node, scope, context) {
|
||||
async compile(node, scope, context) {
|
||||
return { result: node, context };
|
||||
},
|
||||
toString(node) {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import resolve, { traverse, toString, map } from '../resolve';
|
||||
import NodeFactory, { ResolveLevel } from '/imports/parser/parseTree/NodeFactory';
|
||||
import ParseNode from '/imports/parser/parseTree/ParseNode';
|
||||
import { Context, ResolvedResult } from '/imports/parser/resolve';
|
||||
import ResolvedResult from '../types/ResolvedResult';
|
||||
import TraverseFunction from '/imports/parser/types/TraverseFunction';
|
||||
import MapFunction from '/imports/parser/types/MapFunction';
|
||||
import ResolveFunction from '/imports/parser/types/ResolveFunction';
|
||||
import ToStringFunction from '/imports/parser/types/ToStringFunction';
|
||||
|
||||
export type IfNode = {
|
||||
parseType: 'if';
|
||||
@@ -10,17 +12,12 @@ export type IfNode = {
|
||||
alternative: ParseNode;
|
||||
}
|
||||
|
||||
interface IfFactory extends NodeFactory {
|
||||
type IfFactory = {
|
||||
create(node: Partial<IfNode>): IfNode;
|
||||
compile?: undefined;
|
||||
roll?: undefined;
|
||||
reduce?: undefined;
|
||||
resolve(
|
||||
fn: ResolveLevel, node: IfNode, scope: Record<string, any>, context: Context
|
||||
): ResolvedResult;
|
||||
toString(node: IfNode): string;
|
||||
traverse(node: IfNode, fn: (node: ParseNode) => any): ReturnType<typeof fn>;
|
||||
map(node: IfNode, fn: (node: ParseNode) => any): ReturnType<typeof fn>;
|
||||
resolve: ResolveFunction<IfNode>;
|
||||
toString: ToStringFunction<IfNode>;
|
||||
traverse: TraverseFunction<IfNode>;
|
||||
map: MapFunction<IfNode>;
|
||||
}
|
||||
|
||||
const ifNode: IfFactory = {
|
||||
@@ -35,18 +32,18 @@ const ifNode: IfFactory = {
|
||||
alternative,
|
||||
};
|
||||
},
|
||||
toString(node) {
|
||||
toString(node, stringOthers) {
|
||||
const { condition, consequent, alternative } = node;
|
||||
condition.parseType
|
||||
return `${toString(condition)} ? ${toString(consequent)} : ${toString(alternative)}`
|
||||
return `${stringOthers(condition)} ? ${stringOthers(consequent)} : ${stringOthers(alternative)}`
|
||||
},
|
||||
resolve(fn, node, scope, context): ResolvedResult {
|
||||
const { result: condition } = resolve(fn, node.condition, scope, context);
|
||||
async resolve(fn, node, scope, context, inputProvider, resolveOthers): Promise<ResolvedResult> {
|
||||
const { result: condition } = await resolveOthers(fn, node.condition, scope, context, inputProvider);
|
||||
if (condition.parseType === 'constant') {
|
||||
if (condition.value) {
|
||||
return resolve(fn, node.consequent, scope, context);
|
||||
return resolveOthers(fn, node.consequent, scope, context, inputProvider);
|
||||
} else {
|
||||
return resolve(fn, node.alternative, scope, context);
|
||||
return resolveOthers(fn, node.alternative, scope, context, inputProvider);
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
@@ -59,18 +56,18 @@ const ifNode: IfFactory = {
|
||||
};
|
||||
}
|
||||
},
|
||||
traverse(node, fn) {
|
||||
traverse(node, fn, traverseOthers) {
|
||||
fn(node);
|
||||
traverse(node.condition, fn);
|
||||
traverse(node.consequent, fn);
|
||||
traverse(node.alternative, fn);
|
||||
traverseOthers(node.condition, fn);
|
||||
traverseOthers(node.consequent, fn);
|
||||
traverseOthers(node.alternative, fn);
|
||||
},
|
||||
map(node, fn) {
|
||||
const resultingNode = fn(node);
|
||||
async map(node, fn, mapOthers) {
|
||||
const resultingNode = await fn(node);
|
||||
if (resultingNode === node) {
|
||||
node.condition = map(node.condition, fn);
|
||||
node.consequent = map(node.consequent, fn);
|
||||
node.alternative = map(node.alternative, fn);
|
||||
node.condition = await mapOthers(node.condition, fn);
|
||||
node.consequent = await mapOthers(node.consequent, fn);
|
||||
node.alternative = await mapOthers(node.alternative, fn);
|
||||
}
|
||||
return resultingNode;
|
||||
},
|
||||
|
||||
45
app/imports/parser/parseTree/index.ts
Normal file
45
app/imports/parser/parseTree/index.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
console.log('index.ts imports')
|
||||
|
||||
import accessor from '/imports/parser/parseTree/accessor';
|
||||
import array from '/imports/parser/parseTree/array';
|
||||
import call from '/imports/parser/parseTree/call';
|
||||
import constant from '/imports/parser/parseTree/constant';
|
||||
import error from '/imports/parser/parseTree/error';
|
||||
import ifNode from '/imports/parser/parseTree/if';
|
||||
import index from '/imports/parser/parseTree/indexNode';
|
||||
import not from '/imports/parser/parseTree/not';
|
||||
import operator from '/imports/parser/parseTree/operator';
|
||||
import parenthesis from '/imports/parser/parseTree/parenthesis';
|
||||
import roll from '/imports/parser/parseTree/roll';
|
||||
import rollArray from '/imports/parser/parseTree/rollArray';
|
||||
import unaryOperator from '/imports/parser/parseTree/unaryOperator';
|
||||
|
||||
console.log('index.ts')
|
||||
|
||||
const factories = {
|
||||
accessor,
|
||||
array,
|
||||
call,
|
||||
constant,
|
||||
error,
|
||||
if: ifNode,
|
||||
index,
|
||||
not,
|
||||
operator,
|
||||
parenthesis,
|
||||
roll,
|
||||
rollArray,
|
||||
// What used to be symbols are now just treated as accessors without a path
|
||||
symbol: accessor,
|
||||
unaryOperator,
|
||||
};
|
||||
|
||||
console.log('---------------------');
|
||||
console.log('---------------------');
|
||||
console.log('---------------------');
|
||||
console.log(factories.array);
|
||||
console.log('---------------------');
|
||||
console.log('---------------------');
|
||||
console.log('---------------------');
|
||||
|
||||
export default factories;
|
||||
@@ -1,7 +1,11 @@
|
||||
import resolve, { traverse, toString, map, ResolvedResult, Context } from '/imports/parser/resolve';
|
||||
import error from './error';
|
||||
import NodeFactory, { ResolveLevel } from '/imports/parser/parseTree/NodeFactory';
|
||||
import error from '/imports/parser/parseTree/error';
|
||||
import ParseNode from '/imports/parser/parseTree/ParseNode';
|
||||
import toString from '/imports/parser/toString';
|
||||
import { isFiniteNode } from '/imports/parser/parseTree/constant';
|
||||
import ResolveFunction from '/imports/parser/types/ResolveFunction';
|
||||
import TraverseFunction from '/imports/parser/types/TraverseFunction';
|
||||
import MapFunction from '/imports/parser/types/MapFunction';
|
||||
import ToStringFunction from '/imports/parser/types/ToStringFunction';
|
||||
|
||||
export type IndexNode = {
|
||||
parseType: 'index';
|
||||
@@ -9,17 +13,12 @@ export type IndexNode = {
|
||||
index: ParseNode;
|
||||
}
|
||||
|
||||
interface IndexFactory extends NodeFactory {
|
||||
type IndexFactory = {
|
||||
create(node: Partial<IndexNode>): IndexNode;
|
||||
compile?: undefined;
|
||||
roll?: undefined;
|
||||
reduce?: undefined;
|
||||
resolve(
|
||||
fn: ResolveLevel, node: IndexNode, scope: Record<string, any>, context: Context
|
||||
): ResolvedResult;
|
||||
toString(node: IndexNode): string;
|
||||
traverse(node: IndexNode, fn: (node: ParseNode) => any): ReturnType<typeof fn>;
|
||||
map(node: IndexNode, fn: (node: ParseNode) => any): ReturnType<typeof fn>;
|
||||
resolve: ResolveFunction<IndexNode>;
|
||||
toString: ToStringFunction<IndexNode>;
|
||||
traverse: TraverseFunction<IndexNode>;
|
||||
map: MapFunction<IndexNode>;
|
||||
}
|
||||
|
||||
const indexNode: IndexFactory = {
|
||||
@@ -30,25 +29,22 @@ const indexNode: IndexFactory = {
|
||||
index,
|
||||
}
|
||||
},
|
||||
resolve(fn, node, scope, context) {
|
||||
const { result: index } = resolve(fn, node.index, scope, context);
|
||||
const { result: array } = resolve(fn, node.array, scope, context);
|
||||
async resolve(fn, node, scope, context, inputProvider, resolveOthers) {
|
||||
const { result: index } = await resolveOthers(fn, node.index, scope, context, inputProvider);
|
||||
const { result: array } = await resolveOthers(fn, node.array, scope, context, inputProvider);
|
||||
|
||||
if (
|
||||
index.valueType === 'number' &&
|
||||
isFiniteNode(index) &&
|
||||
Number.isInteger(index.value) &&
|
||||
array.parseType === 'array'
|
||||
) {
|
||||
if (index.value < 1 || index.value > array.values.length) {
|
||||
context.error({
|
||||
type: 'warning',
|
||||
message: `Index of ${index.value} is out of range for an array` +
|
||||
` of length ${array.values.length}`,
|
||||
});
|
||||
context.error(`Index of ${index.value} is out of range for an array` +
|
||||
` of length ${array.values.length}`);
|
||||
}
|
||||
const selection = array.values[index.value - 1];
|
||||
if (selection) {
|
||||
return resolve(fn, selection, scope, context);
|
||||
return resolveOthers(fn, selection, scope, context, inputProvider);
|
||||
}
|
||||
} else if (fn === 'reduce') {
|
||||
if (array.parseType !== 'array') {
|
||||
@@ -61,7 +57,7 @@ const indexNode: IndexFactory = {
|
||||
}),
|
||||
context,
|
||||
};
|
||||
} else if (!index.isInteger) {
|
||||
} else if (!isFiniteNode(index) || !Number.isInteger(index.value)) {
|
||||
const message = `${toString(array)} is not an integer index of the array`
|
||||
context.error(message);
|
||||
return {
|
||||
@@ -81,19 +77,19 @@ const indexNode: IndexFactory = {
|
||||
context,
|
||||
};
|
||||
},
|
||||
toString(node) {
|
||||
return `${toString(node.array)}[${toString(node.index)}]`;
|
||||
toString(node, stringOthers) {
|
||||
return `${stringOthers(node.array)}[${stringOthers(node.index)}]`;
|
||||
},
|
||||
traverse(node, fn: (node: ParseNode) => any) {
|
||||
traverse(node, fn, traverseOthers) {
|
||||
fn(node);
|
||||
traverse(node.array, fn);
|
||||
traverse(node.index, fn);
|
||||
traverseOthers(node.array, fn);
|
||||
traverseOthers(node.index, fn);
|
||||
},
|
||||
map(node, fn: (node: ParseNode) => any) {
|
||||
const resultingNode = fn(node);
|
||||
async map(node, fn, mapOthers) {
|
||||
const resultingNode = await fn(node);
|
||||
if (resultingNode === node) {
|
||||
node.array = map(node.array, fn);
|
||||
node.index = map(node.index, fn);
|
||||
node.array = await mapOthers(node.array, fn);
|
||||
node.index = await mapOthers(node.index, fn);
|
||||
}
|
||||
return resultingNode;
|
||||
},
|
||||
|
||||
@@ -1,24 +1,21 @@
|
||||
import resolve, { toString, traverse, map, Context, ResolvedResult } from '/imports/parser/resolve';
|
||||
import constant from './constant';
|
||||
import NodeFactory, { ResolveLevel } from '/imports/parser/parseTree/NodeFactory';
|
||||
import constant from '/imports/parser/parseTree/constant';
|
||||
import ParseNode from '/imports/parser/parseTree/ParseNode';
|
||||
import ResolveFunction from '/imports/parser/types/ResolveFunction';
|
||||
import TraverseFunction from '/imports/parser/types/TraverseFunction';
|
||||
import MapFunction from '/imports/parser/types/MapFunction';
|
||||
import ToStringFunction from '/imports/parser/types/ToStringFunction';
|
||||
|
||||
export type NotNode = {
|
||||
parseType: 'not';
|
||||
right: ParseNode;
|
||||
}
|
||||
|
||||
interface NotFactory extends NodeFactory {
|
||||
type NotFactory = {
|
||||
create(node: Partial<NotNode>): NotNode;
|
||||
compile?: undefined;
|
||||
roll?: undefined;
|
||||
reduce?: undefined;
|
||||
resolve(
|
||||
fn: ResolveLevel, node: NotNode, scope: Record<string, any>, context: Context
|
||||
): ResolvedResult;
|
||||
toString(node: NotNode): string;
|
||||
traverse(node: NotNode, fn: (node: ParseNode) => any): ReturnType<typeof fn>;
|
||||
map(node: NotNode, fn: (node: ParseNode) => any): ReturnType<typeof fn>;
|
||||
resolve: ResolveFunction<NotNode>;
|
||||
toString: ToStringFunction<NotNode>;
|
||||
traverse: TraverseFunction<NotNode>;
|
||||
map: MapFunction<NotNode>;
|
||||
}
|
||||
|
||||
const not: NotFactory = {
|
||||
@@ -28,8 +25,8 @@ const not: NotFactory = {
|
||||
right,
|
||||
}
|
||||
},
|
||||
resolve(fn, node, scope, context) {
|
||||
const { result: right } = resolve(fn, node.right, scope, context);
|
||||
async resolve(fn, node, scope, context, inputProvider, resolveOthers) {
|
||||
const { result: right } = await resolveOthers(fn, node.right, scope, context, inputProvider);
|
||||
if (right.parseType !== 'constant') {
|
||||
return {
|
||||
result: not.create({
|
||||
@@ -45,17 +42,17 @@ const not: NotFactory = {
|
||||
context,
|
||||
};
|
||||
},
|
||||
toString(node) {
|
||||
return `!${toString(node.right)}`;
|
||||
toString(node, stringOthers) {
|
||||
return `!${stringOthers(node.right)}`;
|
||||
},
|
||||
traverse(node, fn) {
|
||||
traverse(node, fn, traverseOthers) {
|
||||
fn(node);
|
||||
traverse(node.right, fn);
|
||||
traverseOthers(node.right, fn);
|
||||
},
|
||||
map(node, fn) {
|
||||
const resultingNode = fn(node);
|
||||
async map(node, fn, mapOthers) {
|
||||
const resultingNode = await fn(node);
|
||||
if (resultingNode === node) {
|
||||
node.right = map(node.right, fn);
|
||||
node.right = await mapOthers(node.right, fn);
|
||||
}
|
||||
return resultingNode;
|
||||
},
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import resolve, { toString, traverse, map, ResolvedResult, Context } from '/imports/parser/resolve';
|
||||
import constant from './constant';
|
||||
import constant, { ConstantValueType, isFiniteNode } from '/imports/parser/parseTree/constant';
|
||||
import ParseNode from '/imports/parser/parseTree/ParseNode';
|
||||
import NodeFactory, { ResolveLevel } from '/imports/parser/parseTree/NodeFactory';
|
||||
import ResolveFunction from '/imports/parser/types/ResolveFunction';
|
||||
import TraverseFunction from '/imports/parser/types/TraverseFunction';
|
||||
import MapFunction from '/imports/parser/types/MapFunction';
|
||||
import ToStringFunction from '/imports/parser/types/ToStringFunction';
|
||||
|
||||
type OperatorSymbol = '*' | '/' | '^' | '+' | '-' | '%' | '&' | '&&' | '|' | '||' | '=' |
|
||||
'==' | '===' | '!=' | '!==' | '>' | '<' | '>=' | '<=';
|
||||
@@ -13,17 +15,12 @@ export type OperatorNode = {
|
||||
operator: OperatorSymbol;
|
||||
}
|
||||
|
||||
interface OperatorFactory extends NodeFactory {
|
||||
type OperatorFactory = {
|
||||
create(node: Partial<OperatorNode>): OperatorNode;
|
||||
compile?: undefined;
|
||||
roll?: undefined;
|
||||
reduce?: undefined;
|
||||
resolve(
|
||||
fn: ResolveLevel, node: OperatorNode, scope: Record<string, any>, context: Context
|
||||
): ResolvedResult;
|
||||
toString(node: OperatorNode): string;
|
||||
traverse(node: OperatorNode, fn: (node: ParseNode) => any): ReturnType<typeof fn>;
|
||||
map(node: OperatorNode, fn: (node: ParseNode) => any): ReturnType<typeof fn>;
|
||||
resolve: ResolveFunction<OperatorNode>;
|
||||
toString: ToStringFunction<OperatorNode>;
|
||||
traverse: TraverseFunction<OperatorNode>;
|
||||
map: MapFunction<OperatorNode>;
|
||||
}
|
||||
|
||||
// Which operators can be considered commutative by the parser
|
||||
@@ -43,10 +40,10 @@ const operator: OperatorFactory = {
|
||||
operator,
|
||||
};
|
||||
},
|
||||
resolve(fn, node, scope, context) {
|
||||
const { result: leftNode } = resolve(fn, node.left, scope, context);
|
||||
const { result: rightNode } = resolve(fn, node.right, scope, context);
|
||||
let left, right;
|
||||
async resolve(fn, node, scope, context, inputProvider, resolveOthers) {
|
||||
const { result: leftNode } = await resolveOthers(fn, node.left, scope, context, inputProvider);
|
||||
const { result: rightNode } = await resolveOthers(fn, node.right, scope, context, inputProvider);
|
||||
let left: ConstantValueType, right: ConstantValueType;
|
||||
|
||||
// If commutation is possible, do it and return that result
|
||||
const commutatedResult = reorderCommutativeOperations(node, leftNode, rightNode);
|
||||
@@ -73,32 +70,39 @@ const operator: OperatorFactory = {
|
||||
context,
|
||||
};
|
||||
},
|
||||
toString(node) {
|
||||
toString(node, stringOthers) {
|
||||
const { left, right, operator } = node;
|
||||
// special case of adding a negative number
|
||||
if (operator === '+' && right.valueType === 'number' && right.value < 0) {
|
||||
return `${toString(left)} - ${-right.value}`
|
||||
if (operator === '+' && isFiniteNode(right) && right.value < 0) {
|
||||
return `${stringOthers(left)} - ${-right.value}`
|
||||
}
|
||||
return `${toString(left)} ${operator} ${toString(right)}`;
|
||||
return `${stringOthers(left)} ${operator} ${stringOthers(right)}`;
|
||||
},
|
||||
traverse(node, fn) {
|
||||
traverse(node, fn, traverseOthers) {
|
||||
fn(node);
|
||||
traverse(node.left, fn);
|
||||
traverse(node.right, fn);
|
||||
traverseOthers(node.left, fn);
|
||||
traverseOthers(node.right, fn);
|
||||
},
|
||||
map(node, fn) {
|
||||
const resultingNode = fn(node);
|
||||
async map(node, fn, mapOthers) {
|
||||
const resultingNode = await fn(node);
|
||||
if (resultingNode === node) {
|
||||
node.left = map(node.left, fn);
|
||||
node.right = map(node.right, fn);
|
||||
node.left = await mapOthers(node.left, fn);
|
||||
node.right = await mapOthers(node.right, fn);
|
||||
}
|
||||
return resultingNode;
|
||||
},
|
||||
}
|
||||
|
||||
function applyOperator(operator: OperatorSymbol, left: ParseNode, right: ParseNode) {
|
||||
function applyOperator(operator: OperatorSymbol, left: ConstantValueType, right: ConstantValueType) {
|
||||
let result;
|
||||
if (left === undefined) {
|
||||
left = 0;
|
||||
}
|
||||
if (right === undefined) {
|
||||
right = 0;
|
||||
}
|
||||
switch (operator) {
|
||||
// Typescript might complain about these, but they return NaN as expected
|
||||
case '+': result = left + right; break;
|
||||
case '-': result = left - right; break;
|
||||
case '*': result = left * right; break;
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
import NodeFactory, { ResolveLevel } from '/imports/parser/parseTree/NodeFactory';
|
||||
import ParseNode from '/imports/parser/parseTree/ParseNode';
|
||||
import resolve, { toString, traverse, map, Context, ResolvedResult } from '/imports/parser/resolve';
|
||||
import ResolveFunction from '/imports/parser/types/ResolveFunction';
|
||||
import TraverseFunction from '/imports/parser/types/TraverseFunction';
|
||||
import MapFunction from '/imports/parser/types/MapFunction';
|
||||
import ToStringFunction from '/imports/parser/types/ToStringFunction';
|
||||
|
||||
export type ParenthesisNode = {
|
||||
parseType: 'parenthesis';
|
||||
content: ParseNode;
|
||||
}
|
||||
|
||||
interface ParenthesisFactory extends NodeFactory {
|
||||
type ParenthesisFactory = {
|
||||
create(node: Partial<ParenthesisNode>): ParenthesisNode;
|
||||
compile?: undefined;
|
||||
roll?: undefined;
|
||||
reduce?: undefined;
|
||||
resolve(
|
||||
fn: ResolveLevel, node: ParenthesisNode, scope: Record<string, any>, context: Context
|
||||
): ResolvedResult;
|
||||
toString(node: ParenthesisNode): string;
|
||||
traverse(node: ParenthesisNode, fn: (node: ParseNode) => any): ReturnType<typeof fn>;
|
||||
map(node: ParenthesisNode, fn: (node: ParseNode) => any): ReturnType<typeof fn>;
|
||||
resolve: ResolveFunction<ParenthesisNode>;
|
||||
toString: ToStringFunction<ParenthesisNode>;
|
||||
traverse: TraverseFunction<ParenthesisNode>;
|
||||
map: MapFunction<ParenthesisNode>;
|
||||
}
|
||||
|
||||
const parenthesis: ParenthesisFactory = {
|
||||
@@ -27,8 +24,8 @@ const parenthesis: ParenthesisFactory = {
|
||||
content,
|
||||
};
|
||||
},
|
||||
resolve(fn, node, scope, context) {
|
||||
const { result: content } = resolve(fn, node.content, scope, context);
|
||||
async resolve(fn, node, scope, context, inputProvider, resolveOthers) {
|
||||
const { result: content } = await resolveOthers(fn, node.content, scope, context, inputProvider);
|
||||
if (
|
||||
fn === 'reduce' ||
|
||||
content.parseType === 'constant' ||
|
||||
@@ -42,17 +39,17 @@ const parenthesis: ParenthesisFactory = {
|
||||
};
|
||||
}
|
||||
},
|
||||
toString(node) {
|
||||
return `(${toString(node.content)})`;
|
||||
toString(node, stringOthers) {
|
||||
return `(${stringOthers(node.content)})`;
|
||||
},
|
||||
traverse(node, fn: (node: ParseNode) => any) {
|
||||
traverse(node, fn, traverseOthers) {
|
||||
fn(node);
|
||||
traverse(node.content, fn);
|
||||
traverseOthers(node.content, fn);
|
||||
},
|
||||
map(node, fn: (node: ParseNode) => any) {
|
||||
const resultingNode = fn(node);
|
||||
async map(node, fn, mapOthers) {
|
||||
const resultingNode = await fn(node);
|
||||
if (resultingNode === node) {
|
||||
node.content = map(node.content, fn);
|
||||
node.content = await mapOthers(node.content, fn);
|
||||
}
|
||||
return resultingNode;
|
||||
},
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import resolve, { toString, traverse, map, ResolvedResult, Context } from '../resolve';
|
||||
import error from './error';
|
||||
import rollArray from './rollArray';
|
||||
import rollDice from '/imports/parser/rollDice';
|
||||
import error from '/imports/parser/parseTree/error';
|
||||
import rollArray from '/imports/parser/parseTree/rollArray';
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS';
|
||||
import ParseNode from '/imports/parser/parseTree/ParseNode';
|
||||
import NodeFactory from '/imports/parser/parseTree/NodeFactory';
|
||||
import ResolveLevelFunction from '/imports/parser/types/ResolveLevelFunction';
|
||||
import TraverseFunction from '/imports/parser/types/TraverseFunction';
|
||||
import MapFunction from '/imports/parser/types/MapFunction';
|
||||
import ToStringFunction from '/imports/parser/types/ToStringFunction';
|
||||
import Context from '/imports/parser/types/Context';
|
||||
|
||||
export type RollNode = {
|
||||
parseType: 'roll';
|
||||
@@ -12,21 +14,14 @@ export type RollNode = {
|
||||
right: ParseNode;
|
||||
}
|
||||
|
||||
interface RollNodeFactory extends NodeFactory {
|
||||
type RollNodeFactory = {
|
||||
create(node: Partial<RollNode>): RollNode;
|
||||
compile(
|
||||
node: RollNode, scope: Record<string, any>, context: Context
|
||||
): ResolvedResult;
|
||||
roll(
|
||||
node: RollNode, scope: Record<string, any>, context: Context
|
||||
): ResolvedResult;
|
||||
reduce(
|
||||
node: RollNode, scope: Record<string, any>, context: Context
|
||||
): ResolvedResult;
|
||||
resolve?: undefined;
|
||||
toString(node: RollNode): string;
|
||||
traverse(node: RollNode, fn: (node: ParseNode) => any): ReturnType<typeof fn>;
|
||||
map(node: RollNode, fn: (node: ParseNode) => any): ReturnType<typeof fn>;
|
||||
compile: ResolveLevelFunction<RollNode>;
|
||||
roll: ResolveLevelFunction<RollNode>;
|
||||
reduce: ResolveLevelFunction<RollNode>;
|
||||
toString: ToStringFunction<RollNode>;
|
||||
traverse: TraverseFunction<RollNode>;
|
||||
map: MapFunction<RollNode>;
|
||||
}
|
||||
|
||||
const rollNode: RollNodeFactory = {
|
||||
@@ -37,28 +32,28 @@ const rollNode: RollNodeFactory = {
|
||||
right,
|
||||
};
|
||||
},
|
||||
compile(node, scope, context) {
|
||||
const { result: left } = resolve('compile', node.left, scope, context);
|
||||
const { result: right } = resolve('compile', node.right, scope, context);
|
||||
async compile(node, scope, context, inputProvider, resolveOthers) {
|
||||
const { result: left } = await resolveOthers('compile', node.left, scope, context, inputProvider);
|
||||
const { result: right } = await resolveOthers('compile', node.right, scope, context, inputProvider);
|
||||
return {
|
||||
result: rollNode.create({ left, right }),
|
||||
context,
|
||||
};
|
||||
},
|
||||
toString(node) {
|
||||
toString(node, stringOthers) {
|
||||
if (
|
||||
node.left.parseType === 'constant'
|
||||
&& typeof node.left.value === 'number'
|
||||
&& node.left.value === 1
|
||||
) {
|
||||
return `d${toString(node.right)}`;
|
||||
return `d${stringOthers(node.right)}`;
|
||||
} else {
|
||||
return `${toString(node.left)}d${toString(node.right)}`;
|
||||
return `${stringOthers(node.left)}d${stringOthers(node.right)}`;
|
||||
}
|
||||
},
|
||||
roll(node, scope, context) {
|
||||
const { result: left } = resolve('reduce', node.left, scope, context);
|
||||
const { result: right } = resolve('reduce', node.right, scope, context);
|
||||
async roll(node, scope, context, inputProvider, resolveOthers) {
|
||||
const { result: left } = await resolveOthers('reduce', node.left, scope, context, inputProvider);
|
||||
const { result: right } = await resolveOthers('reduce', node.right, scope, context, inputProvider);
|
||||
if (
|
||||
left.parseType !== 'constant'
|
||||
|| typeof left.value !== 'number'
|
||||
@@ -82,7 +77,7 @@ const rollNode: RollNodeFactory = {
|
||||
return errorResult(message, node, context);
|
||||
}
|
||||
const diceSize = right.value;
|
||||
const values = rollDice(number, diceSize);
|
||||
const [values] = await inputProvider.rollDice([{ number, diceSize }]);
|
||||
if (context) {
|
||||
context.rolls.push({ number, diceSize, values });
|
||||
}
|
||||
@@ -95,20 +90,20 @@ const rollNode: RollNodeFactory = {
|
||||
context
|
||||
};
|
||||
},
|
||||
reduce(node, scope, context) {
|
||||
const { result } = rollNode.roll(node, scope, context);
|
||||
return resolve('reduce', result, scope, context);
|
||||
async reduce(node, scope, context, inputProvider, resolveOthers) {
|
||||
const { result } = await rollNode.roll(node, scope, context, inputProvider, resolveOthers);
|
||||
return resolveOthers('reduce', result, scope, context, inputProvider);
|
||||
},
|
||||
traverse(node, fn) {
|
||||
traverse(node, fn, traverseOthers) {
|
||||
fn(node);
|
||||
traverse(node.left, fn);
|
||||
traverse(node.right, fn);
|
||||
traverseOthers(node.left, fn);
|
||||
traverseOthers(node.right, fn);
|
||||
},
|
||||
map(node, fn) {
|
||||
const resultingNode = fn(node);
|
||||
async map(node, fn, mapOthers) {
|
||||
const resultingNode = await fn(node);
|
||||
if (resultingNode === node) {
|
||||
node.left = map(node.left, fn);
|
||||
node.right = map(node.right, fn);
|
||||
node.left = await mapOthers(node.left, fn);
|
||||
node.right = await mapOthers(node.right, fn);
|
||||
}
|
||||
return resultingNode;
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import constant from './constant';
|
||||
import NodeFactory from '/imports/parser/parseTree/NodeFactory';
|
||||
import { Context, ResolvedResult } from '/imports/parser/resolve';
|
||||
import constant from '/imports/parser/parseTree/constant';
|
||||
import ResolveLevelFunction from '/imports/parser/types/ResolveLevelFunction';
|
||||
import ToStringFunction from '/imports/parser/types/ToStringFunction';
|
||||
|
||||
type RollValue = {
|
||||
value: number,
|
||||
@@ -17,19 +17,11 @@ export type RollArrayNode = {
|
||||
diceNum: number,
|
||||
}
|
||||
|
||||
interface RollArrayFactory extends NodeFactory {
|
||||
type RollArrayFactory = {
|
||||
create(input: { values: number[], diceSize: number, diceNum: number }): RollArrayNode;
|
||||
compile(
|
||||
node: RollArrayNode, scope: Record<string, any>, context: Context
|
||||
): ResolvedResult;
|
||||
roll?: undefined;
|
||||
reduce(
|
||||
node: RollArrayNode, scope: Record<string, any>, context: Context
|
||||
): ResolvedResult;
|
||||
resolve?: undefined;
|
||||
toString(node: RollArrayNode): string;
|
||||
traverse?: undefined;
|
||||
map?: undefined;
|
||||
compile: ResolveLevelFunction<RollArrayNode>;
|
||||
reduce: ResolveLevelFunction<RollArrayNode>;
|
||||
toString: ToStringFunction<RollArrayNode>;
|
||||
}
|
||||
|
||||
const rollArray: RollArrayFactory = {
|
||||
@@ -43,7 +35,7 @@ const rollArray: RollArrayFactory = {
|
||||
diceNum,
|
||||
};
|
||||
},
|
||||
compile(node, scope, context) {
|
||||
async compile(node, scope, context) {
|
||||
return {
|
||||
result: node,
|
||||
context
|
||||
@@ -52,7 +44,7 @@ const rollArray: RollArrayFactory = {
|
||||
toString(node) {
|
||||
return `${node.diceNum || ''}d${node.diceSize} [${valuesToString(node.values)}]`;
|
||||
},
|
||||
reduce(node, scope, context) {
|
||||
async reduce(node, scope, context) {
|
||||
const total = node.values.reduce((a, b) => {
|
||||
if (b.disabled) return a;
|
||||
return a + b.value;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import resolve, { toString, traverse, map, Context, ResolvedResult } from '/imports/parser/resolve';
|
||||
import constant from './constant';
|
||||
import NodeFactory, { ResolveLevel } from '/imports/parser/parseTree/NodeFactory';
|
||||
import constant from '/imports/parser/parseTree/constant';
|
||||
import ParseNode from '/imports/parser/parseTree/ParseNode';
|
||||
import ResolveFunction from '/imports/parser/types/ResolveFunction';
|
||||
import TraverseFunction from '/imports/parser/types/TraverseFunction';
|
||||
import MapFunction from '/imports/parser/types/MapFunction';
|
||||
import ToStringFunction from '/imports/parser/types/ToStringFunction';
|
||||
|
||||
type UnaryOperatorSymbol = '+' | '-';
|
||||
|
||||
@@ -11,17 +13,12 @@ export type UnaryOperatorNode = {
|
||||
right: ParseNode;
|
||||
}
|
||||
|
||||
interface UnaryOperatorFactory extends NodeFactory {
|
||||
type UnaryOperatorFactory = {
|
||||
create(node: Partial<UnaryOperatorNode>): UnaryOperatorNode;
|
||||
compile?: undefined;
|
||||
roll?: undefined;
|
||||
reduce?: undefined;
|
||||
resolve(
|
||||
fn: ResolveLevel, node: UnaryOperatorNode, scope: Record<string, any>, context: Context
|
||||
): ResolvedResult;
|
||||
toString(node: UnaryOperatorNode): string;
|
||||
traverse(node: UnaryOperatorNode, fn: (node: ParseNode) => any): ReturnType<typeof fn>;
|
||||
map(node: UnaryOperatorNode, fn: (node: ParseNode) => any): ReturnType<typeof fn>;
|
||||
resolve: ResolveFunction<UnaryOperatorNode>;
|
||||
toString: ToStringFunction<UnaryOperatorNode>;
|
||||
traverse: TraverseFunction<UnaryOperatorNode>;
|
||||
map: MapFunction<UnaryOperatorNode>;
|
||||
}
|
||||
|
||||
const unaryOperator: UnaryOperatorFactory = {
|
||||
@@ -32,8 +29,8 @@ const unaryOperator: UnaryOperatorFactory = {
|
||||
right,
|
||||
};
|
||||
},
|
||||
resolve(fn, node, scope, context) {
|
||||
const { result: rightNode } = resolve(fn, node.right, scope, context);
|
||||
async resolve(fn, node, scope, context, inputProvider, resolveOthers) {
|
||||
const { result: rightNode } = await resolveOthers(fn, node.right, scope, context, inputProvider);
|
||||
if (
|
||||
rightNode.parseType !== 'constant'
|
||||
|| typeof rightNode.value !== 'number'
|
||||
@@ -59,17 +56,17 @@ const unaryOperator: UnaryOperatorFactory = {
|
||||
context,
|
||||
};
|
||||
},
|
||||
toString(node) {
|
||||
return `${node.operator}${toString(node.right)}`;
|
||||
toString(node, stringOthers) {
|
||||
return `${node.operator}${stringOthers(node.right)}`;
|
||||
},
|
||||
traverse(node, fn) {
|
||||
traverse(node, fn, traverseOthers) {
|
||||
fn(node);
|
||||
traverse(node.right, fn);
|
||||
traverseOthers(node.right, fn);
|
||||
},
|
||||
map(node, fn) {
|
||||
const resultingNode = fn(node);
|
||||
async map(node, fn, mapOthers) {
|
||||
const resultingNode = await fn(node);
|
||||
if (resultingNode === node) {
|
||||
node.right = map(node.right, fn);
|
||||
node.right = await mapOthers(node.right, fn);
|
||||
}
|
||||
return resultingNode;
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user