Moved parser to typescript
This commit is contained in:
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@@ -1,9 +1,11 @@
|
||||
{
|
||||
"cSpell.words": [
|
||||
"autorun",
|
||||
"blackbox",
|
||||
"Crits",
|
||||
"cyrb",
|
||||
"EJSON",
|
||||
"nearley",
|
||||
"uncomputed"
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,19 @@
|
||||
import { ResolveLevel } from '/imports/parser/parseTree/NodeFactory';
|
||||
import resolve from '/imports/parser/resolve'
|
||||
import rollDice from '/imports/parser/rollDice';
|
||||
|
||||
export default {
|
||||
export type ParserFunction = {
|
||||
comment: string;
|
||||
examples: { input: string, result: string }[];
|
||||
arguments: string[];
|
||||
maxResolveLevels?: ResolveLevel[];
|
||||
minArguments?: number,
|
||||
maxArguments?: number,
|
||||
resultType: string;
|
||||
fn: (...args: any[]) => any;
|
||||
}
|
||||
|
||||
const parserFunctions: { [name: string]: ParserFunction } = {
|
||||
'abs': {
|
||||
comment: 'Returns the absolute value of a number',
|
||||
examples: [
|
||||
@@ -105,22 +117,23 @@ export default {
|
||||
arguments: ['array', 'number'],
|
||||
resultType: 'number',
|
||||
fn: function tableLookup(arrayNode, number) {
|
||||
for (let i in arrayNode.values) {
|
||||
let node = arrayNode.values[i];
|
||||
for (const i in arrayNode.values) {
|
||||
const node = arrayNode.values[i];
|
||||
if (node.value > number) return +i;
|
||||
}
|
||||
return arrayNode.values.length;
|
||||
}
|
||||
},
|
||||
'resolve': {
|
||||
comment: 'Forces the given calcultion to resolve into a number, even in calculations where it would usually keep the unknown values as is',
|
||||
comment: 'Forces the given calculation to resolve into a number, even in calculations where it would usually keep the unknown values as is',
|
||||
examples: [
|
||||
{ input: 'resolve(someUndefinedVariable + 3 + 4)', result: '7' },
|
||||
{ input: 'resolve(1d6)', result: '4' },
|
||||
],
|
||||
arguments: ['parseNode'],
|
||||
resultType: 'parseNode',
|
||||
fn: function resolveFn(node) {
|
||||
let { result } = resolve('reduce', node, this.scope, this.context);
|
||||
const { result } = resolve('reduce', node, this.scope, this.context);
|
||||
return result;
|
||||
}
|
||||
},
|
||||
@@ -181,7 +194,7 @@ export default {
|
||||
maxArguments: 3,
|
||||
resultType: 'rollArray',
|
||||
fn: function rerollFn(rollArray, numberToReroll = 1, keepNewRoll = false) {
|
||||
let rollValues = rollArray.values
|
||||
const rollValues = rollArray.values
|
||||
// Iterate through the roll values
|
||||
for (let i = 0; i < rollValues.length; i += 1) {
|
||||
// If the number is less than the reroll limit
|
||||
@@ -218,7 +231,7 @@ export default {
|
||||
fn: function explodeFn(rollArray, depth = 1, numberToReroll = rollArray.diceSize) {
|
||||
let overflowErrored = false;
|
||||
if (depth > 99) depth = 99;
|
||||
let rollValues = rollArray.values
|
||||
const rollValues = rollArray.values
|
||||
// Iterate through the roll values
|
||||
for (let i = 0; i < rollValues.length; i += 1) {
|
||||
// If the number is greater than or equal to the reroll limit
|
||||
@@ -255,7 +268,9 @@ export default {
|
||||
}
|
||||
|
||||
function anyNumberOf(type) {
|
||||
let argumentArray = [type];
|
||||
const argumentArray = [type];
|
||||
argumentArray.anyLength = true;
|
||||
return argumentArray;
|
||||
}
|
||||
|
||||
export default parserFunctions;
|
||||
28
app/imports/parser/parseTree/NodeFactory.ts
Normal file
28
app/imports/parser/parseTree/NodeFactory.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
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>;
|
||||
}
|
||||
29
app/imports/parser/parseTree/ParseNode.ts
Normal file
29
app/imports/parser/parseTree/ParseNode.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { AccessorNode } from './accessor';
|
||||
import { ArrayNode } from './array';
|
||||
import { CallNode } from './call';
|
||||
import { ConstantNode } from './constant';
|
||||
import { ErrorNode } from './error';
|
||||
import { IfNode } from './if';
|
||||
import { IndexNode } from './indexNode';
|
||||
import { NotNode } from './not';
|
||||
import { OperatorNode } from './operator';
|
||||
import { ParenthesisNode } from './parenthesis';
|
||||
import { RollNode } from './roll';
|
||||
import { RollArrayNode } from './rollArray';
|
||||
import { UnaryOperatorNode } from './unaryOperator';
|
||||
|
||||
type ParseNode = AccessorNode
|
||||
| ArrayNode
|
||||
| CallNode
|
||||
| ConstantNode
|
||||
| ErrorNode
|
||||
| IfNode
|
||||
| IndexNode
|
||||
| NotNode
|
||||
| OperatorNode
|
||||
| ParenthesisNode
|
||||
| RollNode
|
||||
| RollArrayNode
|
||||
| UnaryOperatorNode
|
||||
|
||||
export default ParseNode;
|
||||
@@ -1,18 +1,19 @@
|
||||
import accessor from './accessor';
|
||||
import array from './array.js';
|
||||
import array from './array';
|
||||
import call from './call';
|
||||
import constant from './constant';
|
||||
import error from './error';
|
||||
import ifNode from './if';
|
||||
import index from './index';
|
||||
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';
|
||||
|
||||
export default {
|
||||
const factories: Record<string, NodeFactory> = {
|
||||
accessor,
|
||||
array,
|
||||
call,
|
||||
@@ -29,3 +30,5 @@ export default {
|
||||
symbol: accessor,
|
||||
unaryOperator,
|
||||
};
|
||||
|
||||
export default factories;
|
||||
@@ -1,17 +1,41 @@
|
||||
import constant from './constant';
|
||||
import array from './array';
|
||||
import resolve from '../resolve';
|
||||
import resolve, { Context, ResolvedResult } from '/imports/parser/resolve';
|
||||
import { getFromScope } from '/imports/api/creature/creatures/CreatureVariables';
|
||||
import NodeFactory from '/imports/parser/parseTree/NodeFactory';
|
||||
|
||||
const accessor = {
|
||||
create({ name, path }) {
|
||||
export type AccessorNode = {
|
||||
parseType: 'accessor';
|
||||
path?: string[];
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface AccessorFactory extends NodeFactory {
|
||||
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;
|
||||
toString(node: AccessorNode): string;
|
||||
traverse?: undefined;
|
||||
map?: undefined;
|
||||
}
|
||||
|
||||
const accessor: AccessorFactory = {
|
||||
create({ name, path }: { name: string, path?: string[] }): AccessorNode {
|
||||
return {
|
||||
parseType: 'accessor',
|
||||
path,
|
||||
name,
|
||||
};
|
||||
},
|
||||
compile(node, scope, context) {
|
||||
compile(
|
||||
node: AccessorNode, scope: Record<string, any>, context: Context
|
||||
): ResolvedResult {
|
||||
let value = getFromScope(node.name, scope);
|
||||
// Get the value from the given path
|
||||
node.path?.forEach(name => {
|
||||
@@ -78,7 +102,7 @@ const accessor = {
|
||||
context,
|
||||
};
|
||||
},
|
||||
reduce(node, scope, context) {
|
||||
reduce(node, scope, context): ResolvedResult {
|
||||
let { result } = accessor.compile(node, scope, context);
|
||||
({ result } = resolve('reduce', result, scope, context));
|
||||
if (result.parseType === 'accessor') {
|
||||
@@ -1,55 +0,0 @@
|
||||
import resolve, { toString, traverse, map } from '../resolve';
|
||||
import constant from './constant';
|
||||
|
||||
const array = {
|
||||
create({ values }) {
|
||||
return {
|
||||
parseType: 'array',
|
||||
values,
|
||||
};
|
||||
},
|
||||
fromConstantArray(array) {
|
||||
let values = array.map(value => {
|
||||
let valueType = typeof value;
|
||||
if (
|
||||
valueType === 'string' ||
|
||||
valueType === 'number' ||
|
||||
valueType === 'boolean' ||
|
||||
valueType === 'undefined'
|
||||
) {
|
||||
return constant.create({ value, valueType });
|
||||
} else {
|
||||
// Gracefully create an empty spot in the array for unsupported types
|
||||
return undefined;
|
||||
// throw `Unexpected type in constant array: ${valueType}`
|
||||
}
|
||||
});
|
||||
return array.create({ values });
|
||||
},
|
||||
resolve(fn, node, scope, context) {
|
||||
let values = node.values.map(node => {
|
||||
let { result } = resolve(fn, node, scope, context);
|
||||
return result;
|
||||
});
|
||||
return {
|
||||
result: array.create({ values }),
|
||||
context,
|
||||
};
|
||||
},
|
||||
toString(node) {
|
||||
return `[${node.values.map(value => toString(value)).join(', ')}]`;
|
||||
},
|
||||
traverse(node, fn) {
|
||||
fn(node);
|
||||
node.values.forEach(value => traverse(value, fn));
|
||||
},
|
||||
map(node, fn) {
|
||||
const resultingNode = fn(node);
|
||||
if (resultingNode === node) {
|
||||
node.values = node.values.map(value => map(value, fn));
|
||||
}
|
||||
return resultingNode;
|
||||
},
|
||||
}
|
||||
|
||||
export default array;
|
||||
75
app/imports/parser/parseTree/array.ts
Normal file
75
app/imports/parser/parseTree/array.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import resolve, { toString, traverse, map, Context, ResolvedResult } from '/imports/parser/resolve';
|
||||
import constant from './constant';
|
||||
import NodeFactory, { ResolveLevel } from '/imports/parser/parseTree/NodeFactory';
|
||||
import ParseNode from '/imports/parser/parseTree/ParseNode';
|
||||
|
||||
export type ArrayNode = {
|
||||
parseType: 'array';
|
||||
values: ParseNode[];
|
||||
}
|
||||
|
||||
interface ArrayFactory extends NodeFactory {
|
||||
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>;
|
||||
}
|
||||
|
||||
const array: ArrayFactory = {
|
||||
create({ values }: { values: ParseNode[] }) {
|
||||
return {
|
||||
parseType: 'array',
|
||||
values,
|
||||
};
|
||||
},
|
||||
fromConstantArray(constantArray) {
|
||||
const values = constantArray.map(value => {
|
||||
const valueType = typeof value;
|
||||
if (
|
||||
valueType === 'string' ||
|
||||
valueType === 'number' ||
|
||||
valueType === 'boolean' ||
|
||||
valueType === 'undefined'
|
||||
) {
|
||||
return constant.create({ value });
|
||||
} else {
|
||||
// Gracefully create an empty constant in the array for unsupported types
|
||||
return constant.create({ value: undefined });
|
||||
}
|
||||
});
|
||||
return array.create({ values });
|
||||
},
|
||||
resolve(fn, node, scope, context): ResolvedResult {
|
||||
const values = node.values.map(node => {
|
||||
const { result } = resolve(fn, node, scope, context);
|
||||
return result;
|
||||
});
|
||||
return {
|
||||
result: array.create({ values }),
|
||||
context,
|
||||
};
|
||||
},
|
||||
toString(node) {
|
||||
return `[${node.values.map(value => toString(value)).join(', ')}]`;
|
||||
},
|
||||
traverse(node, fn) {
|
||||
fn(node);
|
||||
node.values.forEach(value => traverse(value, fn));
|
||||
},
|
||||
map(node, fn) {
|
||||
const resultingNode = fn(node);
|
||||
if (resultingNode === node) {
|
||||
node.values = node.values.map(value => map(value, fn));
|
||||
}
|
||||
return resultingNode;
|
||||
},
|
||||
}
|
||||
|
||||
export default array;
|
||||
@@ -1,18 +1,38 @@
|
||||
import error from './error';
|
||||
import constant from './constant';
|
||||
import functions from '/imports/parser/functions';
|
||||
import resolve, { toString, traverse, map } from '../resolve';
|
||||
import functions, { ParserFunction } from '/imports/parser/functions';
|
||||
import resolve, { toString, traverse, map, Context, ResolvedResult } from '/imports/parser/resolve';
|
||||
import ParseNode from '/imports/parser/parseTree/ParseNode';
|
||||
import NodeFactory, { ResolveLevel } from '/imports/parser/parseTree/NodeFactory';
|
||||
|
||||
const call = {
|
||||
create({ functionName, args }) {
|
||||
export type CallNode = {
|
||||
parseType: 'call';
|
||||
functionName: string;
|
||||
args: ParseNode[];
|
||||
}
|
||||
|
||||
interface CallFactory extends NodeFactory {
|
||||
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>;
|
||||
checkArguments(node: CallNode, fn: ResolveLevel, func: ParserFunction,
|
||||
resolvedArgs: ParseNode[], context: Context): boolean;
|
||||
}
|
||||
|
||||
const call: CallFactory = {
|
||||
create({ functionName, args = [] }: { functionName: string, args: ParseNode[] }): CallNode {
|
||||
return {
|
||||
parseType: 'call',
|
||||
functionName,
|
||||
args,
|
||||
}
|
||||
},
|
||||
resolve(fn, node, scope, context) {
|
||||
let func = functions[node.functionName];
|
||||
resolve(fn, node, scope, context): ResolvedResult {
|
||||
const func = functions[node.functionName];
|
||||
// Check that the function exists
|
||||
if (!func) {
|
||||
context.error(`${node.functionName} is not a supported function`);
|
||||
@@ -26,9 +46,9 @@ const call = {
|
||||
}
|
||||
|
||||
// Resolve a given node to a maximum depth of resolution
|
||||
const resolveToLevel = (node, maxResolveFn = 'reduce') => {
|
||||
const resolveToLevel = (node, maxResolveFn = 'reduce'): ResolvedResult => {
|
||||
// Determine the actual depth to resolve to
|
||||
let resolveFn = 'reduce';
|
||||
let resolveFn: ResolveLevel = 'reduce';
|
||||
if (fn === 'compile' || maxResolveFn === 'compile') {
|
||||
resolveFn = 'compile';
|
||||
} else if (fn === 'roll' || maxResolveFn === 'roll') {
|
||||
@@ -39,19 +59,13 @@ const call = {
|
||||
}
|
||||
|
||||
// Resolve the arguments
|
||||
let resolvedArgs = node.args.map((arg, i) => {
|
||||
let { result } = resolveToLevel(arg, func.maxResolveLevels?.[i]);
|
||||
const resolvedArgs = node.args.map((arg, i) => {
|
||||
const { result } = resolveToLevel(arg, func.maxResolveLevels?.[i]);
|
||||
return result;
|
||||
});
|
||||
|
||||
// Check that the arguments match what is expected
|
||||
let checkFailed = call.checkArugments({
|
||||
node,
|
||||
fn,
|
||||
resolvedArgs,
|
||||
func,
|
||||
context,
|
||||
});
|
||||
const checkFailed = call.checkArguments(node, fn, func, resolvedArgs, context);
|
||||
|
||||
if (checkFailed) {
|
||||
if (fn === 'reduce') {
|
||||
@@ -74,8 +88,8 @@ const call = {
|
||||
}
|
||||
}
|
||||
|
||||
// Map contant nodes to constants before attempting to run the function
|
||||
let mappedArgs = resolvedArgs.map((arg, index) => {
|
||||
// Map constant nodes to constants before attempting to run the function
|
||||
const mappedArgs = resolvedArgs.map((arg, index) => {
|
||||
if (
|
||||
arg.parseType === 'constant' &&
|
||||
func.arguments[index] !== 'parseNode'
|
||||
@@ -88,12 +102,12 @@ const call = {
|
||||
|
||||
try {
|
||||
// Run the function
|
||||
let value = func.fn.apply({
|
||||
const value = func.fn.apply({
|
||||
scope,
|
||||
context,
|
||||
}, mappedArgs);
|
||||
|
||||
let valueType = typeof value;
|
||||
const valueType = typeof value;
|
||||
if (valueType === 'number' || valueType === 'string' || valueType === 'boolean') {
|
||||
// Convert constant results into constant nodes
|
||||
return {
|
||||
@@ -129,7 +143,7 @@ const call = {
|
||||
}
|
||||
return resultingNode;
|
||||
},
|
||||
checkArugments({ node, fn, func, resolvedArgs, context }) {
|
||||
checkArguments(node, fn, func, resolvedArgs, context) {
|
||||
const argumentsExpected = func.arguments;
|
||||
// Check that the number of arguments matches the number expected
|
||||
if (
|
||||
@@ -146,7 +160,7 @@ const call = {
|
||||
let failed = false;
|
||||
// Check that each argument is of the correct type
|
||||
resolvedArgs.forEach((node, index) => {
|
||||
let type;
|
||||
let type: string;
|
||||
if (argumentsExpected.anyLength) {
|
||||
type = argumentsExpected[0];
|
||||
} else {
|
||||
@@ -155,8 +169,8 @@ const call = {
|
||||
if (type === 'parseNode') return;
|
||||
if (node.parseType !== type && node.valueType !== type) failed = true;
|
||||
if (failed && fn === 'reduce') {
|
||||
let typeName = typeof type === 'string' ? type : type.constructor.name;
|
||||
let nodeName = node.parseType;
|
||||
const typeName = typeof type === 'string' ? type : type.constructor.name;
|
||||
const nodeName = node.parseType;
|
||||
context.error(`Incorrect arguments to ${node.functionName} function` +
|
||||
`expected ${typeName} got ${nodeName}`);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
const constant = {
|
||||
create({value}){
|
||||
return {
|
||||
parseType: 'constant',
|
||||
valueType: typeof value,
|
||||
value,
|
||||
}
|
||||
},
|
||||
compile(node, scope, context){
|
||||
return {result: node, context};
|
||||
},
|
||||
toString(node){
|
||||
return `${node.value}`;
|
||||
},
|
||||
}
|
||||
|
||||
export default constant;
|
||||
42
app/imports/parser/parseTree/constant.ts
Normal file
42
app/imports/parser/parseTree/constant.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import NodeFactory from '/imports/parser/parseTree/NodeFactory';
|
||||
import { Context, ResolvedResult } from '/imports/parser/resolve';
|
||||
|
||||
type ConstantValueType = number | string | boolean | undefined
|
||||
|
||||
export type ConstantNode = {
|
||||
parseType: 'constant';
|
||||
value: ConstantValueType;
|
||||
// TODO replace all `constantNode.valueType` with `typeof constantNode.value`
|
||||
valueType: 'number' | 'string' | 'boolean' | 'undefined';
|
||||
}
|
||||
|
||||
interface ConstantFactory extends NodeFactory {
|
||||
create({ value }: { value: ConstantValueType }): ConstantNode;
|
||||
compile(
|
||||
node: ConstantNode, scope: Record<string, any>, context: Context
|
||||
): ResolvedResult;
|
||||
roll?: undefined;
|
||||
reduce?: undefined;
|
||||
resolve?: undefined;
|
||||
toString(node: ConstantNode): string;
|
||||
traverse?: undefined;
|
||||
map?: undefined;
|
||||
}
|
||||
|
||||
const constant: ConstantFactory = {
|
||||
create({ value }): ConstantNode {
|
||||
return {
|
||||
parseType: 'constant',
|
||||
valueType: typeof value as 'number' | 'string' | 'boolean' | 'undefined',
|
||||
value,
|
||||
}
|
||||
},
|
||||
compile(node, scope, context) {
|
||||
return { result: node, context };
|
||||
},
|
||||
toString(node) {
|
||||
return `${node.value}`;
|
||||
},
|
||||
}
|
||||
|
||||
export default constant;
|
||||
@@ -1,17 +0,0 @@
|
||||
const error = {
|
||||
create({ node, error }) {
|
||||
return {
|
||||
parseType: 'error',
|
||||
node,
|
||||
error,
|
||||
}
|
||||
},
|
||||
compile(node, scope, context) {
|
||||
return { result: node, context };
|
||||
},
|
||||
toString(node) {
|
||||
return `${node.error.type} error: ${node.error.message}`;
|
||||
},
|
||||
}
|
||||
|
||||
export default error;
|
||||
40
app/imports/parser/parseTree/error.ts
Normal file
40
app/imports/parser/parseTree/error.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import NodeFactory from '/imports/parser/parseTree/NodeFactory';
|
||||
import ParseNode from '/imports/parser/parseTree/ParseNode';
|
||||
import { Context, ResolvedResult } from '/imports/parser/resolve';
|
||||
|
||||
export type ErrorNode = {
|
||||
parseType: 'error';
|
||||
node: ParseNode;
|
||||
error: string;
|
||||
}
|
||||
|
||||
interface ErrorFactory extends NodeFactory {
|
||||
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;
|
||||
}
|
||||
|
||||
const error: ErrorFactory = {
|
||||
create({ node, error }: { node: ParseNode, error: string }) {
|
||||
return {
|
||||
parseType: 'error',
|
||||
node,
|
||||
error,
|
||||
}
|
||||
},
|
||||
compile(node, scope, context) {
|
||||
return { result: node, context };
|
||||
},
|
||||
toString(node) {
|
||||
return node.error;
|
||||
},
|
||||
}
|
||||
|
||||
export default error;
|
||||
@@ -1,53 +0,0 @@
|
||||
import resolve, { traverse, toString, map } from '../resolve';
|
||||
|
||||
const ifNode = {
|
||||
create({condition, consequent, alternative}){
|
||||
return {
|
||||
parseType: 'if',
|
||||
condition,
|
||||
consequent,
|
||||
alternative,
|
||||
};
|
||||
},
|
||||
toString(node){
|
||||
let {condition, consequent, alternative} = node;
|
||||
return `${toString(condition)} ? ${toString(consequent)} : ${toString(alternative)}`
|
||||
},
|
||||
resolve(fn, node, scope, context){
|
||||
let {result: condition} = resolve(fn, node.condition, scope, context);
|
||||
|
||||
if (condition.parseType === 'constant'){
|
||||
if (condition.value){
|
||||
return resolve(fn, node.consequent, scope, context);
|
||||
} else {
|
||||
return resolve(fn, node.alternative, scope, context);
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
result: ifNode.create({
|
||||
condition: condition,
|
||||
consequent: node.consequent,
|
||||
alternative: node.alternative,
|
||||
}),
|
||||
context,
|
||||
};
|
||||
}
|
||||
},
|
||||
traverse(node, fn){
|
||||
fn(node);
|
||||
traverse(node.condition, fn);
|
||||
traverse(node.consequent, fn);
|
||||
traverse(node.alternative, fn);
|
||||
},
|
||||
map(node, fn){
|
||||
const resultingNode = fn(node);
|
||||
if (resultingNode === node){
|
||||
node.condition = map(node.condition, fn);
|
||||
node.consequent = map(node.consequent, fn);
|
||||
node.alternative = map(node.alternative, fn);
|
||||
}
|
||||
return resultingNode;
|
||||
},
|
||||
}
|
||||
|
||||
export default ifNode;
|
||||
79
app/imports/parser/parseTree/if.ts
Normal file
79
app/imports/parser/parseTree/if.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
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';
|
||||
|
||||
export type IfNode = {
|
||||
parseType: 'if';
|
||||
condition: ParseNode;
|
||||
consequent: ParseNode;
|
||||
alternative: ParseNode;
|
||||
}
|
||||
|
||||
interface IfFactory extends NodeFactory {
|
||||
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>;
|
||||
}
|
||||
|
||||
const ifNode: IfFactory = {
|
||||
create(
|
||||
{ condition, consequent, alternative }:
|
||||
{ condition: ParseNode, consequent: ParseNode, alternative: ParseNode }
|
||||
) {
|
||||
return {
|
||||
parseType: 'if',
|
||||
condition,
|
||||
consequent,
|
||||
alternative,
|
||||
};
|
||||
},
|
||||
toString(node) {
|
||||
const { condition, consequent, alternative } = node;
|
||||
condition.parseType
|
||||
return `${toString(condition)} ? ${toString(consequent)} : ${toString(alternative)}`
|
||||
},
|
||||
resolve(fn, node, scope, context): ResolvedResult {
|
||||
const { result: condition } = resolve(fn, node.condition, scope, context);
|
||||
if (condition.parseType === 'constant') {
|
||||
if (condition.value) {
|
||||
return resolve(fn, node.consequent, scope, context);
|
||||
} else {
|
||||
return resolve(fn, node.alternative, scope, context);
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
result: ifNode.create({
|
||||
condition: condition,
|
||||
consequent: node.consequent,
|
||||
alternative: node.alternative,
|
||||
}),
|
||||
context,
|
||||
};
|
||||
}
|
||||
},
|
||||
traverse(node, fn) {
|
||||
fn(node);
|
||||
traverse(node.condition, fn);
|
||||
traverse(node.consequent, fn);
|
||||
traverse(node.alternative, fn);
|
||||
},
|
||||
map(node, fn) {
|
||||
const resultingNode = fn(node);
|
||||
if (resultingNode === node) {
|
||||
node.condition = map(node.condition, fn);
|
||||
node.consequent = map(node.consequent, fn);
|
||||
node.alternative = map(node.alternative, fn);
|
||||
}
|
||||
return resultingNode;
|
||||
},
|
||||
}
|
||||
|
||||
export default ifNode;
|
||||
@@ -1,8 +1,29 @@
|
||||
import resolve, { traverse, toString, map } from '../resolve';
|
||||
import resolve, { traverse, toString, map, ResolvedResult, Context } from '/imports/parser/resolve';
|
||||
import error from './error';
|
||||
import NodeFactory, { ResolveLevel } from '/imports/parser/parseTree/NodeFactory';
|
||||
import ParseNode from '/imports/parser/parseTree/ParseNode';
|
||||
|
||||
const indexNode = {
|
||||
create({ array, index }) {
|
||||
export type IndexNode = {
|
||||
parseType: 'index';
|
||||
array: ParseNode;
|
||||
index: ParseNode;
|
||||
}
|
||||
|
||||
interface IndexFactory extends NodeFactory {
|
||||
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>;
|
||||
}
|
||||
|
||||
const indexNode: IndexFactory = {
|
||||
create({ array, index }: { array: ParseNode, index: ParseNode }) {
|
||||
return {
|
||||
parseType: 'index',
|
||||
array,
|
||||
@@ -10,8 +31,8 @@ const indexNode = {
|
||||
}
|
||||
},
|
||||
resolve(fn, node, scope, context) {
|
||||
let { result: index } = resolve(fn, node.index, scope, context);
|
||||
let { result: array } = resolve(fn, node.array, scope, context);
|
||||
const { result: index } = resolve(fn, node.index, scope, context);
|
||||
const { result: array } = resolve(fn, node.array, scope, context);
|
||||
|
||||
if (
|
||||
index.valueType === 'number' &&
|
||||
@@ -25,7 +46,7 @@ const indexNode = {
|
||||
` of length ${array.values.length}`,
|
||||
});
|
||||
}
|
||||
let selection = array.values[index.value - 1];
|
||||
const selection = array.values[index.value - 1];
|
||||
if (selection) {
|
||||
return resolve(fn, selection, scope, context);
|
||||
}
|
||||
@@ -63,12 +84,12 @@ const indexNode = {
|
||||
toString(node) {
|
||||
return `${toString(node.array)}[${toString(node.index)}]`;
|
||||
},
|
||||
traverse(node, fn) {
|
||||
traverse(node, fn: (node: ParseNode) => any) {
|
||||
fn(node);
|
||||
traverse(node.array, fn);
|
||||
traverse(node.index, fn);
|
||||
},
|
||||
map(node, fn) {
|
||||
map(node, fn: (node: ParseNode) => any) {
|
||||
const resultingNode = fn(node);
|
||||
if (resultingNode === node) {
|
||||
node.array = map(node.array, fn);
|
||||
@@ -1,44 +0,0 @@
|
||||
import resolve, { toString, traverse, map } from '../resolve';
|
||||
import constant from './constant';
|
||||
|
||||
const not = {
|
||||
create({ right }) {
|
||||
return {
|
||||
parseType: 'not',
|
||||
right,
|
||||
}
|
||||
},
|
||||
resolve(fn, node, scope, context) {
|
||||
const { result: right } = resolve(fn, node.right, scope, context);
|
||||
if (right.parseType !== 'constant') {
|
||||
return {
|
||||
result: not.create({
|
||||
right: right,
|
||||
}),
|
||||
context,
|
||||
};
|
||||
}
|
||||
return {
|
||||
result: constant.create({
|
||||
value: !right.value,
|
||||
}),
|
||||
context,
|
||||
};
|
||||
},
|
||||
toString(node) {
|
||||
return `!${toString(node.right)}`;
|
||||
},
|
||||
traverse(node, fn) {
|
||||
fn(node);
|
||||
traverse(node.right, fn);
|
||||
},
|
||||
map(node, fn) {
|
||||
const resultingNode = fn(node);
|
||||
if (resultingNode === node) {
|
||||
node.right = map(node.right, fn);
|
||||
}
|
||||
return resultingNode;
|
||||
},
|
||||
}
|
||||
|
||||
export default not;
|
||||
64
app/imports/parser/parseTree/not.ts
Normal file
64
app/imports/parser/parseTree/not.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import resolve, { toString, traverse, map, Context, ResolvedResult } from '/imports/parser/resolve';
|
||||
import constant from './constant';
|
||||
import NodeFactory, { ResolveLevel } from '/imports/parser/parseTree/NodeFactory';
|
||||
import ParseNode from '/imports/parser/parseTree/ParseNode';
|
||||
|
||||
export type NotNode = {
|
||||
parseType: 'not';
|
||||
right: ParseNode;
|
||||
}
|
||||
|
||||
interface NotFactory extends NodeFactory {
|
||||
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>;
|
||||
}
|
||||
|
||||
const not: NotFactory = {
|
||||
create({ right }: { right: ParseNode }) {
|
||||
return {
|
||||
parseType: 'not',
|
||||
right,
|
||||
}
|
||||
},
|
||||
resolve(fn, node, scope, context) {
|
||||
const { result: right } = resolve(fn, node.right, scope, context);
|
||||
if (right.parseType !== 'constant') {
|
||||
return {
|
||||
result: not.create({
|
||||
right: right,
|
||||
}),
|
||||
context,
|
||||
};
|
||||
}
|
||||
return {
|
||||
result: constant.create({
|
||||
value: !right.value,
|
||||
}),
|
||||
context,
|
||||
};
|
||||
},
|
||||
toString(node) {
|
||||
return `!${toString(node.right)}`;
|
||||
},
|
||||
traverse(node, fn) {
|
||||
fn(node);
|
||||
traverse(node.right, fn);
|
||||
},
|
||||
map(node, fn) {
|
||||
const resultingNode = fn(node);
|
||||
if (resultingNode === node) {
|
||||
node.right = map(node.right, fn);
|
||||
}
|
||||
return resultingNode;
|
||||
},
|
||||
}
|
||||
|
||||
export default not;
|
||||
@@ -1,12 +1,41 @@
|
||||
import resolve, { toString, traverse, map } from '../resolve';
|
||||
import resolve, { toString, traverse, map, ResolvedResult, Context } from '/imports/parser/resolve';
|
||||
import constant from './constant';
|
||||
import ParseNode from '/imports/parser/parseTree/ParseNode';
|
||||
import NodeFactory, { ResolveLevel } from '/imports/parser/parseTree/NodeFactory';
|
||||
|
||||
type OperatorSymbol = '*' | '/' | '^' | '+' | '-' | '%' | '&' | '&&' | '|' | '||' | '=' |
|
||||
'==' | '===' | '!=' | '!==' | '>' | '<' | '>=' | '<=';
|
||||
|
||||
export type OperatorNode = {
|
||||
parseType: 'operator';
|
||||
left: ParseNode;
|
||||
right: ParseNode;
|
||||
operator: OperatorSymbol;
|
||||
}
|
||||
|
||||
interface OperatorFactory extends NodeFactory {
|
||||
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>;
|
||||
}
|
||||
|
||||
// Which operators can be considered commutative by the parser
|
||||
// i.e. 1 + 2 + 3 === 2 + 3 + 1
|
||||
const commutativeOperators = ['+', '*']
|
||||
|
||||
const operator = {
|
||||
create({ left, right, operator }) {
|
||||
const operator: OperatorFactory = {
|
||||
create({
|
||||
left, right, operator
|
||||
}: {
|
||||
left: ParseNode, right: ParseNode, operator: OperatorSymbol
|
||||
}) {
|
||||
return {
|
||||
parseType: 'operator',
|
||||
left,
|
||||
@@ -45,7 +74,7 @@ const operator = {
|
||||
};
|
||||
},
|
||||
toString(node) {
|
||||
let { left, right, operator } = node;
|
||||
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}`
|
||||
@@ -67,7 +96,7 @@ const operator = {
|
||||
},
|
||||
}
|
||||
|
||||
function applyOperator(operator, left, right) {
|
||||
function applyOperator(operator: OperatorSymbol, left: ParseNode, right: ParseNode) {
|
||||
let result;
|
||||
switch (operator) {
|
||||
case '+': result = left + right; break;
|
||||
@@ -93,7 +122,9 @@ function applyOperator(operator, left, right) {
|
||||
return result;
|
||||
}
|
||||
|
||||
function reorderCommutativeOperations(node, leftNode, rightNode) {
|
||||
function reorderCommutativeOperations(
|
||||
node: OperatorNode, leftNode: ParseNode, rightNode: ParseNode
|
||||
) {
|
||||
// Make sure the operator is commutative
|
||||
if (!commutativeOperators.includes(node.operator)) return;
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
import resolve, { toString, traverse, map } from '../resolve';
|
||||
|
||||
const parenthesis = {
|
||||
create({ content }) {
|
||||
return {
|
||||
parseType: 'parenthesis',
|
||||
content,
|
||||
};
|
||||
},
|
||||
resolve(fn, node, scope, context) {
|
||||
const { result: content } = resolve(fn, node.content, scope, context);
|
||||
if (
|
||||
fn === 'reduce' ||
|
||||
content.parseType === 'constant' ||
|
||||
content.parseType === 'error'
|
||||
) {
|
||||
return { result: content, context };
|
||||
} else {
|
||||
return {
|
||||
result: parenthesis.create({ content }),
|
||||
context
|
||||
};
|
||||
}
|
||||
},
|
||||
toString(node) {
|
||||
return `(${toString(node.content)})`;
|
||||
},
|
||||
traverse(node, fn) {
|
||||
fn(node);
|
||||
traverse(node.content, fn);
|
||||
},
|
||||
map(node, fn) {
|
||||
const resultingNode = fn(node);
|
||||
if (resultingNode === node) {
|
||||
node.content = map(node.content, fn);
|
||||
}
|
||||
return resultingNode;
|
||||
},
|
||||
}
|
||||
|
||||
export default parenthesis;
|
||||
61
app/imports/parser/parseTree/parenthesis.ts
Normal file
61
app/imports/parser/parseTree/parenthesis.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
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';
|
||||
|
||||
export type ParenthesisNode = {
|
||||
parseType: 'parenthesis';
|
||||
content: ParseNode;
|
||||
}
|
||||
|
||||
interface ParenthesisFactory extends NodeFactory {
|
||||
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>;
|
||||
}
|
||||
|
||||
const parenthesis: ParenthesisFactory = {
|
||||
create({ content }: { content: ParseNode }) {
|
||||
return {
|
||||
parseType: 'parenthesis',
|
||||
content,
|
||||
};
|
||||
},
|
||||
resolve(fn, node, scope, context) {
|
||||
const { result: content } = resolve(fn, node.content, scope, context);
|
||||
if (
|
||||
fn === 'reduce' ||
|
||||
content.parseType === 'constant' ||
|
||||
content.parseType === 'error'
|
||||
) {
|
||||
return { result: content, context };
|
||||
} else {
|
||||
return {
|
||||
result: parenthesis.create({ content }),
|
||||
context
|
||||
};
|
||||
}
|
||||
},
|
||||
toString(node) {
|
||||
return `(${toString(node.content)})`;
|
||||
},
|
||||
traverse(node, fn: (node: ParseNode) => any) {
|
||||
fn(node);
|
||||
traverse(node.content, fn);
|
||||
},
|
||||
map(node, fn: (node: ParseNode) => any) {
|
||||
const resultingNode = fn(node);
|
||||
if (resultingNode === node) {
|
||||
node.content = map(node.content, fn);
|
||||
}
|
||||
return resultingNode;
|
||||
},
|
||||
}
|
||||
|
||||
export default parenthesis;
|
||||
@@ -1,11 +1,36 @@
|
||||
import resolve, { toString, traverse, map } from '../resolve';
|
||||
import resolve, { toString, traverse, map, ResolvedResult, Context } from '../resolve';
|
||||
import error from './error';
|
||||
import rollArray from './rollArray';
|
||||
import rollDice from '/imports/parser/rollDice';
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS';
|
||||
import ParseNode from '/imports/parser/parseTree/ParseNode';
|
||||
import NodeFactory from '/imports/parser/parseTree/NodeFactory';
|
||||
|
||||
const rollNode = {
|
||||
create({ left, right }) {
|
||||
export type RollNode = {
|
||||
parseType: 'roll';
|
||||
left: ParseNode;
|
||||
right: ParseNode;
|
||||
}
|
||||
|
||||
interface RollNodeFactory extends NodeFactory {
|
||||
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>;
|
||||
}
|
||||
|
||||
const rollNode: RollNodeFactory = {
|
||||
create({ left, right }: { left: ParseNode, right: ParseNode }) {
|
||||
return {
|
||||
parseType: 'roll',
|
||||
left,
|
||||
@@ -22,7 +47,9 @@ const rollNode = {
|
||||
},
|
||||
toString(node) {
|
||||
if (
|
||||
node.left.valueType === 'number' && node.left.value === 1
|
||||
node.left.parseType === 'constant'
|
||||
&& typeof node.left.value === 'number'
|
||||
&& node.left.value === 1
|
||||
) {
|
||||
return `d${toString(node.right)}`;
|
||||
} else {
|
||||
@@ -32,10 +59,18 @@ const rollNode = {
|
||||
roll(node, scope, context) {
|
||||
const { result: left } = resolve('reduce', node.left, scope, context);
|
||||
const { result: right } = resolve('reduce', node.right, scope, context);
|
||||
if (left.valueType !== 'number' && !Number.isInteger(left.value)) {
|
||||
if (
|
||||
left.parseType !== 'constant'
|
||||
|| typeof left.value !== 'number'
|
||||
|| !Number.isInteger(left.value)
|
||||
) {
|
||||
return errorResult('Number of dice is not an integer', node, context);
|
||||
}
|
||||
if (right.valueType !== 'number' && !Number.isInteger(right.value)) {
|
||||
if (
|
||||
right.parseType !== 'constant'
|
||||
|| typeof right.value !== 'number'
|
||||
|| !Number.isInteger(right.value)
|
||||
) {
|
||||
return errorResult('Dice size is not an integer', node, context);
|
||||
}
|
||||
let number = left.value;
|
||||
@@ -46,8 +81,8 @@ const rollNode = {
|
||||
const message = `Can't roll more than ${STORAGE_LIMITS.diceRollValuesCount} dice at once`;
|
||||
return errorResult(message, node, context);
|
||||
}
|
||||
let diceSize = right.value;
|
||||
let values = rollDice(number, diceSize);
|
||||
const diceSize = right.value;
|
||||
const values = rollDice(number, diceSize);
|
||||
if (context) {
|
||||
context.rolls.push({ number, diceSize, values });
|
||||
}
|
||||
@@ -79,7 +114,7 @@ const rollNode = {
|
||||
},
|
||||
}
|
||||
|
||||
function errorResult(message, node, context) {
|
||||
function errorResult(message: string, node: RollNode, context: Context) {
|
||||
context.error(message);
|
||||
return {
|
||||
result: error.create({ node, error: message }),
|
||||
@@ -1,46 +0,0 @@
|
||||
import constant from './constant';
|
||||
|
||||
const rollArray = {
|
||||
create({ values, diceSize, diceNum }) {
|
||||
return {
|
||||
parseType: 'rollArray',
|
||||
values: values.map(v => ({ value: v })),
|
||||
diceSize,
|
||||
diceNum,
|
||||
};
|
||||
},
|
||||
compile(node, scope, context) {
|
||||
return {
|
||||
result: node,
|
||||
context
|
||||
};
|
||||
},
|
||||
toString(node) {
|
||||
return `${node.diceNum || ''}d${node.diceSize} [${valuesToString(node.values)}]`;
|
||||
},
|
||||
reduce(node, scope, context) {
|
||||
const total = node.values.reduce((a, b) => {
|
||||
if (b.disabled) return a;
|
||||
return a + b.value;
|
||||
}, 0);
|
||||
return {
|
||||
result: constant.create({
|
||||
value: total,
|
||||
}),
|
||||
context,
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
function valuesToString(values) {
|
||||
return values.map(v => {
|
||||
let text = `${v.value}`;
|
||||
if (v.disabled) text = `~~${text}~~`;
|
||||
if (v.italics) text = `*${text}*`;
|
||||
if (v.bold) text = `**${text}**`;
|
||||
if (v.underline) text = `__${text}__`;
|
||||
return text;
|
||||
}).join(', ');
|
||||
}
|
||||
|
||||
export default rollArray;
|
||||
80
app/imports/parser/parseTree/rollArray.ts
Normal file
80
app/imports/parser/parseTree/rollArray.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import constant from './constant';
|
||||
import NodeFactory from '/imports/parser/parseTree/NodeFactory';
|
||||
import { Context, ResolvedResult } from '/imports/parser/resolve';
|
||||
|
||||
type RollValue = {
|
||||
value: number,
|
||||
disabled?: true,
|
||||
italics?: true,
|
||||
bold?: true,
|
||||
underline?: true,
|
||||
}
|
||||
|
||||
export type RollArrayNode = {
|
||||
parseType: 'rollArray';
|
||||
values: RollValue[];
|
||||
diceSize: number,
|
||||
diceNum: number,
|
||||
}
|
||||
|
||||
interface RollArrayFactory extends NodeFactory {
|
||||
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;
|
||||
}
|
||||
|
||||
const rollArray: RollArrayFactory = {
|
||||
create({
|
||||
values, diceSize, diceNum
|
||||
}) {
|
||||
return {
|
||||
parseType: 'rollArray',
|
||||
values: values.map(v => ({ value: v })),
|
||||
diceSize,
|
||||
diceNum,
|
||||
};
|
||||
},
|
||||
compile(node, scope, context) {
|
||||
return {
|
||||
result: node,
|
||||
context
|
||||
};
|
||||
},
|
||||
toString(node) {
|
||||
return `${node.diceNum || ''}d${node.diceSize} [${valuesToString(node.values)}]`;
|
||||
},
|
||||
reduce(node, scope, context) {
|
||||
const total = node.values.reduce((a, b) => {
|
||||
if (b.disabled) return a;
|
||||
return a + b.value;
|
||||
}, 0);
|
||||
return {
|
||||
result: constant.create({
|
||||
value: total,
|
||||
}),
|
||||
context,
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
function valuesToString(values: RollValue[]) {
|
||||
return values.map(v => {
|
||||
let text = `${v.value}`;
|
||||
if (v.disabled) text = `~~${text}~~`;
|
||||
if (v.italics) text = `*${text}*`;
|
||||
if (v.bold) text = `**${text}**`;
|
||||
if (v.underline) text = `__${text}__`;
|
||||
return text;
|
||||
}).join(', ');
|
||||
}
|
||||
|
||||
export default rollArray;
|
||||
@@ -1,53 +0,0 @@
|
||||
import resolve, { toString, traverse, map } from '../resolve';
|
||||
import constant from './constant';
|
||||
|
||||
const unaryOperator = {
|
||||
create({ operator, right }) {
|
||||
return {
|
||||
parseType: 'unaryOperator',
|
||||
operator,
|
||||
right,
|
||||
};
|
||||
},
|
||||
resolve(fn, node, scope, context) {
|
||||
const { result: rightNode } = resolve(fn, node.right, scope, context);
|
||||
if (rightNode.valueType !== 'number') {
|
||||
return {
|
||||
result: unaryOperator.create({
|
||||
operator: node.operator,
|
||||
right: rightNode,
|
||||
}),
|
||||
context,
|
||||
};
|
||||
}
|
||||
let right = rightNode.value;
|
||||
let result;
|
||||
switch (node.operator) {
|
||||
case '-': result = -right; break;
|
||||
case '+': result = +right; break;
|
||||
}
|
||||
return {
|
||||
result: constant.create({
|
||||
value: result,
|
||||
parseType: typeof result,
|
||||
}),
|
||||
context,
|
||||
};
|
||||
},
|
||||
toString(node) {
|
||||
return `${node.operator}${toString(node.right)}`;
|
||||
},
|
||||
traverse(node, fn) {
|
||||
fn(node);
|
||||
traverse(node.right, fn);
|
||||
},
|
||||
map(node, fn) {
|
||||
const resultingNode = fn(node);
|
||||
if (resultingNode === node) {
|
||||
node.right = map(node.right, fn);
|
||||
}
|
||||
return resultingNode;
|
||||
},
|
||||
};
|
||||
|
||||
export default unaryOperator;
|
||||
78
app/imports/parser/parseTree/unaryOperator.ts
Normal file
78
app/imports/parser/parseTree/unaryOperator.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import resolve, { toString, traverse, map, Context, ResolvedResult } from '/imports/parser/resolve';
|
||||
import constant from './constant';
|
||||
import NodeFactory, { ResolveLevel } from '/imports/parser/parseTree/NodeFactory';
|
||||
import ParseNode from '/imports/parser/parseTree/ParseNode';
|
||||
|
||||
type UnaryOperatorSymbol = '+' | '-';
|
||||
|
||||
export type UnaryOperatorNode = {
|
||||
parseType: 'unaryOperator';
|
||||
operator: UnaryOperatorSymbol;
|
||||
right: ParseNode;
|
||||
}
|
||||
|
||||
interface UnaryOperatorFactory extends NodeFactory {
|
||||
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>;
|
||||
}
|
||||
|
||||
const unaryOperator: UnaryOperatorFactory = {
|
||||
create({ operator, right }: { operator: UnaryOperatorSymbol, right: ParseNode }) {
|
||||
return {
|
||||
parseType: 'unaryOperator',
|
||||
operator,
|
||||
right,
|
||||
};
|
||||
},
|
||||
resolve(fn, node, scope, context) {
|
||||
const { result: rightNode } = resolve(fn, node.right, scope, context);
|
||||
if (
|
||||
rightNode.parseType !== 'constant'
|
||||
|| typeof rightNode.value !== 'number'
|
||||
) {
|
||||
return {
|
||||
result: unaryOperator.create({
|
||||
operator: node.operator,
|
||||
right: rightNode,
|
||||
}),
|
||||
context,
|
||||
};
|
||||
}
|
||||
const right = rightNode.value;
|
||||
let result: number;
|
||||
switch (node.operator) {
|
||||
case '-': result = -right; break;
|
||||
case '+': result = +right; break;
|
||||
}
|
||||
return {
|
||||
result: constant.create({
|
||||
value: result,
|
||||
}),
|
||||
context,
|
||||
};
|
||||
},
|
||||
toString(node) {
|
||||
return `${node.operator}${toString(node.right)}`;
|
||||
},
|
||||
traverse(node, fn) {
|
||||
fn(node);
|
||||
traverse(node.right, fn);
|
||||
},
|
||||
map(node, fn) {
|
||||
const resultingNode = fn(node);
|
||||
if (resultingNode === node) {
|
||||
node.right = map(node.right, fn);
|
||||
}
|
||||
return resultingNode;
|
||||
},
|
||||
};
|
||||
|
||||
export default unaryOperator;
|
||||
@@ -1,34 +0,0 @@
|
||||
import grammar from '/imports/parser/grammar';
|
||||
import nearley from 'nearley';
|
||||
|
||||
const nearleyGrammar = nearley.Grammar.fromCompiled(grammar);
|
||||
|
||||
export default function parser() {
|
||||
return new nearley.Parser(nearleyGrammar);
|
||||
}
|
||||
|
||||
export function parse(string) {
|
||||
let parser = new nearley.Parser(nearleyGrammar);
|
||||
parser.feed(string);
|
||||
let results = parser.results;
|
||||
if (results.length === 1) {
|
||||
return results[0];
|
||||
} else if (results.length === 0) {
|
||||
// Valid parsing up until now, but need more
|
||||
throw new EndOfInputError('Unexpected end of input');
|
||||
} else {
|
||||
console.warn('Grammar is ambiguous!', { string, results });
|
||||
return results[0];
|
||||
}
|
||||
}
|
||||
|
||||
export function prettifyParseError(e) {
|
||||
if (e.message) e = e.message
|
||||
return e.toString().split('.')[0];
|
||||
}
|
||||
|
||||
class EndOfInputError extends Error {
|
||||
constructor(message = '', ...args) {
|
||||
super(message, ...args);
|
||||
}
|
||||
}
|
||||
34
app/imports/parser/parser.ts
Normal file
34
app/imports/parser/parser.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import grammar from '/imports/parser/grammar';
|
||||
import { Parser, Grammar } from 'nearley';
|
||||
import ParseNode from '/imports/parser/parseTree/ParseNode';
|
||||
|
||||
const nearleyGrammar = Grammar.fromCompiled(grammar);
|
||||
|
||||
export default function parser() {
|
||||
return new Parser(nearleyGrammar);
|
||||
}
|
||||
|
||||
export function parse(string: string): ParseNode {
|
||||
const parser = new Parser(nearleyGrammar);
|
||||
parser.feed(string);
|
||||
const results = parser.results;
|
||||
if (results.length === 1) {
|
||||
return results[0];
|
||||
} else if (results.length === 0) {
|
||||
// Valid parsing up until now, but need more
|
||||
throw new EndOfInputError('Unexpected end of input');
|
||||
} else {
|
||||
console.warn('Grammar is ambiguous!', { string, results });
|
||||
return results[0];
|
||||
}
|
||||
}
|
||||
|
||||
export function prettifyParseError(e: Meteor.Error | Error): string {
|
||||
return e.message.split('.')[0];
|
||||
}
|
||||
|
||||
class EndOfInputError extends Error {
|
||||
constructor(message = '') {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,36 @@
|
||||
import nodeTypeIndex from './parseTree/_index';
|
||||
import ParseNode from '/imports/parser/parseTree/ParseNode';
|
||||
|
||||
// Takes a parse ndoe and computes it to a set detail level
|
||||
// Takes a parse node and computes it to a set detail level
|
||||
// returns {result, context}
|
||||
export default function resolve(fn, node, scope, context = new Context()) {
|
||||
if (!node) return { result: undefined, context };
|
||||
let type = nodeTypeIndex[node.parseType];
|
||||
if (!type) {
|
||||
export default function resolve(
|
||||
fn: 'roll' | 'reduce' | 'compile',
|
||||
node: ParseNode,
|
||||
scope: Record<string, any>,
|
||||
context = new Context()
|
||||
): ResolvedResult {
|
||||
if (!node) throw 'Node must be supplied';
|
||||
const factory = nodeTypeIndex[node.parseType];
|
||||
const handlerFunction = factory[fn];
|
||||
if (!factory) {
|
||||
throw new Meteor.Error(`Parse node type: ${node.parseType} not implemented`);
|
||||
}
|
||||
if (type.resolve) {
|
||||
return type.resolve(fn, node, scope, context);
|
||||
} else if (type[fn]) {
|
||||
return type[fn](node, scope, context);
|
||||
} else if (fn === 'reduce' && type.roll) {
|
||||
return type.roll(node, scope, context)
|
||||
} else if (type.compile) {
|
||||
return type.compile(node, scope, context)
|
||||
if (factory.resolve) {
|
||||
return factory.resolve(fn, node, scope, context);
|
||||
} else if (handlerFunction) {
|
||||
return handlerFunction(node, scope, context);
|
||||
} else if (fn === 'reduce' && factory.roll) {
|
||||
return factory.roll(node, scope, context)
|
||||
} else if (factory.compile) {
|
||||
return factory.compile(node, scope, context)
|
||||
} else {
|
||||
throw new Meteor.Error('Compile not implemented on ' + node.parseType);
|
||||
}
|
||||
}
|
||||
|
||||
export function toString(node) {
|
||||
export function toString(node: ParseNode) {
|
||||
if (!node) return '';
|
||||
if (!node.parseType) {
|
||||
throw new Meteor.Error(`Node does not have a parseType defined, node is type ${typeof node} with parseType ${node.parseType}`)
|
||||
}
|
||||
let type = nodeTypeIndex[node.parseType];
|
||||
const type = nodeTypeIndex[node.parseType];
|
||||
if (!type?.toString) {
|
||||
throw new Meteor.Error('toString not implemented on ' + node.parseType);
|
||||
}
|
||||
@@ -40,9 +44,9 @@ export function toPrimitiveOrString(node) {
|
||||
return toString(node);
|
||||
}
|
||||
|
||||
export function traverse(node, fn) {
|
||||
export function traverse(node: ParseNode, fn: (ParseNode) => any): ReturnType<typeof fn> {
|
||||
if (!node) return;
|
||||
let type = nodeTypeIndex[node.parseType];
|
||||
const type = nodeTypeIndex[node.parseType];
|
||||
if (!type) {
|
||||
console.error(node);
|
||||
throw new Meteor.Error('Not valid parse node');
|
||||
@@ -53,9 +57,9 @@ export function traverse(node, fn) {
|
||||
return fn(node);
|
||||
}
|
||||
|
||||
export function map(node, fn) {
|
||||
export function map(node: ParseNode, fn: (ParseNode) => any): ReturnType<typeof fn> {
|
||||
if (!node) return;
|
||||
let type = nodeTypeIndex[node.parseType];
|
||||
const type = nodeTypeIndex[node.parseType];
|
||||
if (!type) {
|
||||
console.error(node);
|
||||
throw new Meteor.Error('Not valid parse node');
|
||||
@@ -66,13 +70,23 @@ export function map(node, fn) {
|
||||
return fn(node);
|
||||
}
|
||||
|
||||
export type ResolvedResult = {
|
||||
result: ParseNode,
|
||||
context: Context
|
||||
}
|
||||
|
||||
export class Context {
|
||||
errors: (Error | { type: string, message: string })[];
|
||||
rolls: { number: number, diceSize: number, values: number[] }[];
|
||||
options: { [key: string]: any };
|
||||
|
||||
constructor({ errors = [], rolls = [], options = {} } = {}) {
|
||||
this.errors = errors;
|
||||
this.rolls = rolls;
|
||||
this.options = options;
|
||||
}
|
||||
error(e) {
|
||||
|
||||
error(e: Error | string) {
|
||||
if (!e) return;
|
||||
if (typeof e === 'string') {
|
||||
this.errors.push({
|
||||
@@ -83,6 +97,7 @@ export class Context {
|
||||
this.errors.push(e);
|
||||
}
|
||||
}
|
||||
|
||||
roll(r) {
|
||||
this.rolls.push(r);
|
||||
}
|
||||
@@ -125,7 +125,8 @@
|
||||
"error",
|
||||
"single"
|
||||
],
|
||||
"@typescript-eslint/no-this-alias": "off"
|
||||
"@typescript-eslint/no-this-alias": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
21
node_modules/@types/nearley/LICENSE
generated
vendored
Normal file
21
node_modules/@types/nearley/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE
|
||||
15
node_modules/@types/nearley/README.md
generated
vendored
Normal file
15
node_modules/@types/nearley/README.md
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# Installation
|
||||
> `npm install --save @types/nearley`
|
||||
|
||||
# Summary
|
||||
This package contains type definitions for nearley (https://github.com/Hardmath123/nearley#readme).
|
||||
|
||||
# Details
|
||||
Files were exported from https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/nearley.
|
||||
|
||||
### Additional Details
|
||||
* Last updated: Tue, 07 Nov 2023 09:09:39 GMT
|
||||
* Dependencies: none
|
||||
|
||||
# Credits
|
||||
These definitions were written by [Nikita Litvin](https://github.com/deltaidea), and [BendingBender](https://github.com/BendingBender).
|
||||
108
node_modules/@types/nearley/index.d.ts
generated
vendored
Normal file
108
node_modules/@types/nearley/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
export as namespace nearley;
|
||||
|
||||
export class Parser {
|
||||
/**
|
||||
* Reserved token for indicating a parse fail.
|
||||
*/
|
||||
static fail: {};
|
||||
|
||||
grammar: Grammar;
|
||||
options: ParserOptions;
|
||||
lexer: Lexer;
|
||||
lexerState?: LexerState | undefined;
|
||||
current: number;
|
||||
/**
|
||||
* An array of possible parsings. Each element is the thing returned by your grammar.
|
||||
*
|
||||
* Note that this is undefined before the first feed() call.
|
||||
* It isn't typed as `any[] | undefined` to spare you the null checks when it's definitely an array.
|
||||
*/
|
||||
results: any[];
|
||||
|
||||
constructor(grammar: Grammar, options?: ParserOptions);
|
||||
|
||||
/**
|
||||
* The Parser object can be fed data in parts with .feed(data).
|
||||
* You can then find an array of parsings with the .results property.
|
||||
* If results is empty, then there are no parsings.
|
||||
* If results contains multiple values, then that combination is ambiguous.
|
||||
*
|
||||
* @throws If there are no possible parsings, nearley will throw an error
|
||||
* whose offset property is the index of the offending token.
|
||||
*/
|
||||
feed(chunk: string): this;
|
||||
finish(): any[];
|
||||
restore(column: { [key: string]: any; lexerState: LexerState }): void;
|
||||
save(): { [key: string]: any; lexerState: LexerState };
|
||||
}
|
||||
|
||||
export interface ParserOptions {
|
||||
keepHistory?: boolean | undefined;
|
||||
lexer?: Lexer | undefined;
|
||||
}
|
||||
|
||||
export class Rule {
|
||||
static highestId: number;
|
||||
|
||||
id: number;
|
||||
name: string;
|
||||
symbols: any[];
|
||||
postprocess?: Postprocessor | undefined;
|
||||
|
||||
constructor(name: string, symbols: any[], postprocess?: Postprocessor);
|
||||
|
||||
toString(withCursorAt?: number): string;
|
||||
}
|
||||
|
||||
export class Grammar {
|
||||
static fromCompiled(rules: CompiledRules): Grammar;
|
||||
|
||||
rules: Rule[];
|
||||
start: string;
|
||||
byName: { [ruleName: string]: Rule[] };
|
||||
lexer?: Lexer | undefined;
|
||||
|
||||
constructor(rules: Rule[]);
|
||||
}
|
||||
|
||||
export interface CompiledRules {
|
||||
Lexer?: Lexer | undefined;
|
||||
ParserStart: string;
|
||||
ParserRules: ParserRule[];
|
||||
}
|
||||
|
||||
export interface ParserRule {
|
||||
name: string;
|
||||
symbols: any[];
|
||||
postprocess?: Postprocessor | undefined;
|
||||
}
|
||||
|
||||
export type Postprocessor = (data: any[], reference?: number, wantedBy?: {}) => void;
|
||||
|
||||
export interface Lexer {
|
||||
/**
|
||||
* Sets the internal buffer to data, and restores line/col/state info taken from save().
|
||||
*/
|
||||
reset(data: string, state?: LexerState): void;
|
||||
/**
|
||||
* Returns e.g. {type, value, line, col, …}. Only the value attribute is required.
|
||||
*/
|
||||
next(): Token | undefined;
|
||||
/**
|
||||
* Returns an object describing the current line/col etc. This allows us
|
||||
* to preserve this information between feed() calls, and also to support Parser#rewind().
|
||||
* The exact structure is lexer-specific; nearley doesn't care what's in it.
|
||||
*/
|
||||
save(): LexerState;
|
||||
/**
|
||||
* Returns a string with an error message describing the line/col of the offending token.
|
||||
* You might like to include a preview of the line in question.
|
||||
*/
|
||||
formatError(token: Token, message: string): string;
|
||||
}
|
||||
|
||||
export type Token = string | { value: string };
|
||||
|
||||
export interface LexerState {
|
||||
[x: string]: any;
|
||||
}
|
||||
58
node_modules/@types/nearley/package.json
generated
vendored
Normal file
58
node_modules/@types/nearley/package.json
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"_from": "@types/nearley",
|
||||
"_id": "@types/nearley@2.11.5",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-dM7TrN0bVxGGXTYGx4YhGear8ysLO5SOuouAWM9oltjQ3m9oYa13qi8Z1DJp5zxVMPukvQdsrnZmgzpeuTSEQA==",
|
||||
"_location": "/@types/nearley",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "tag",
|
||||
"registry": true,
|
||||
"raw": "@types/nearley",
|
||||
"name": "@types/nearley",
|
||||
"escapedName": "@types%2fnearley",
|
||||
"scope": "@types",
|
||||
"rawSpec": "",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "latest"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"#USER",
|
||||
"/"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/@types/nearley/-/nearley-2.11.5.tgz",
|
||||
"_shasum": "9087e1634e1c90efb25d661390702381789685cb",
|
||||
"_spec": "@types/nearley",
|
||||
"_where": "/Users/stef/github/DiceCloud",
|
||||
"bugs": {
|
||||
"url": "https://github.com/DefinitelyTyped/DefinitelyTyped/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Nikita Litvin",
|
||||
"url": "https://github.com/deltaidea"
|
||||
},
|
||||
{
|
||||
"name": "BendingBender",
|
||||
"url": "https://github.com/BendingBender"
|
||||
}
|
||||
],
|
||||
"dependencies": {},
|
||||
"deprecated": false,
|
||||
"description": "TypeScript definitions for nearley",
|
||||
"homepage": "https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/nearley",
|
||||
"license": "MIT",
|
||||
"main": "",
|
||||
"name": "@types/nearley",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/DefinitelyTyped/DefinitelyTyped.git",
|
||||
"directory": "types/nearley"
|
||||
},
|
||||
"scripts": {},
|
||||
"typeScriptVersion": "4.5",
|
||||
"types": "index.d.ts",
|
||||
"typesPublisherContentHash": "2b82830a1a87ef19e588c4f6dcd1c00fde50afd7c9dc5bd8233b054f436578d4",
|
||||
"version": "2.11.5"
|
||||
}
|
||||
11
package-lock.json
generated
Normal file
11
package-lock.json
generated
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"requires": true,
|
||||
"lockfileVersion": 1,
|
||||
"dependencies": {
|
||||
"@types/nearley": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/nearley/-/nearley-2.11.5.tgz",
|
||||
"integrity": "sha512-dM7TrN0bVxGGXTYGx4YhGear8ysLO5SOuouAWM9oltjQ3m9oYa13qi8Z1DJp5zxVMPukvQdsrnZmgzpeuTSEQA=="
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user