From b822da66ab934ad2978fc00b7835df53cdccbb78 Mon Sep 17 00:00:00 2001 From: srfsh Date: Fri, 13 Oct 2023 12:09:56 +0300 Subject: [PATCH] feat(core): synonymize "open" and "connect to" Now, "open" and "connect to" are synonyms. Added two new method signature to PluginContext as well, getOpen() and fetchOpen(), which are synonyms to getOpen() and fetchOpen() too. --- pkg/core/src/parser.ts | 20 +++++++---- pkg/core/src/plugin.ts | 72 +++++++++++++++++++++++++++++--------- pkg/core/src/tokens.ts | 6 ++++ pkg/core/src/visitor.ts | 10 +++--- pkg/core/test/slangroom.ts | 27 +++++++++++++- pkg/core/test/visitor.ts | 38 ++++++++++---------- pkg/http/test/plugins.ts | 2 +- 7 files changed, 127 insertions(+), 48 deletions(-) diff --git a/pkg/core/src/parser.ts b/pkg/core/src/parser.ts index 35d8516f..031e5eca 100644 --- a/pkg/core/src/parser.ts +++ b/pkg/core/src/parser.ts @@ -3,6 +3,7 @@ import { And, Buzzword, Connect, + Open, Identifier, Into, Output, @@ -14,14 +15,14 @@ import { CstParser, type IToken, type CstNode } from '@slangroom/deps/chevrotain export type StatementCst = CstNode & { children: { - connect?: ConnectCst; + openconnect?: OpenconnectCst; sendpass: SendpassCst[]; phrase: PhraseCst; into?: IntoCst; }; }; -export type ConnectCst = CstNode & { +export type OpenconnectCst = CstNode & { children: { Identifier: [IToken] }; }; @@ -49,15 +50,22 @@ const Parser = new (class extends CstParser { } statement = this.RULE('statement', () => { - this.OPTION1(() => this.SUBRULE(this.#connect)); + this.OPTION1(() => this.SUBRULE(this.#openconnect)); this.MANY(() => this.SUBRULE(this.#sendpass)); this.SUBRULE(this.#phrase); this.OPTION2(() => this.SUBRULE(this.#into)); }); - #connect = this.RULE('connect', () => { - this.CONSUME(Connect); - this.CONSUME(To); + #openconnect = this.RULE('openconnect', () => { + this.OR([ + { ALT: () => this.CONSUME(Open) }, + { + ALT: () => { + this.CONSUME(Connect); + this.CONSUME(To); + }, + }, + ]); this.CONSUME(Identifier); this.CONSUME(And); }); diff --git a/pkg/core/src/plugin.ts b/pkg/core/src/plugin.ts index ea0013d0..4e763241 100644 --- a/pkg/core/src/plugin.ts +++ b/pkg/core/src/plugin.ts @@ -43,21 +43,33 @@ export type PluginContext = { phrase: string; /** - * Gets the value of the Connect part of a Statement« if available. + * Gets the value of the Open or Connect part of a Statement, if available. * - * @returns The array of values of the Connect part (might be empty). + * @returns The array of values of the Open or Connect part (might be + * empty). */ getConnect(): string[]; /** * Basically the same as {@link getConnect}, but throws if unavailable or empty. * - * @returns The array of values of the Connect part (has at least one item); + * @returns The array of values of the Open or Connect part (has at least + * one item). * * @throws {@link Error} if the part is unavailable. */ fetchConnect(): [string, ...string[]]; + /** + * {@inheritDoc getConnect} + */ + getOpen(): string[]; + + /** + * {@inheritDoc fetchConnect} + */ + fetchOpen(): [string, ...string[]]; + /** * Gets the value of the parameter needed by the Plugin, if available. * @@ -106,10 +118,10 @@ export class PluginContextImpl implements PluginContext { readonly phrase: string; /** - * The name of the identifier used to reference the Connection parameters - * (via {@link #zparams}). It is an rhs value. + * The name of the identifier used to reference the Open or Connect + * parameters (via {@link #zparams}). It is an rhs value. */ - #connect: string | undefined = undefined; + #openconnect: string | undefined = undefined; /** * A map between parameters that should be provided to a statetment and @@ -129,7 +141,7 @@ export class PluginContextImpl implements PluginContext { constructor(stmt: Statement, zparams: ZenParams) { this.phrase = stmt.phrase.toLowerCase(); - this.#connect = stmt.connect; + this.#openconnect = stmt.openconnect; this.#bindings = stmt.bindings; this.#zparams = zparams; } @@ -159,14 +171,14 @@ export class PluginContextImpl implements PluginContext { * {@inheritDoc PluginContext.getConnect} */ getConnect(): string[] { - if (!this.#connect) return []; - const val = this.#getDataKeys(this.#connect); + if (!this.#openconnect) return []; + const val = this.#getDataKeys(this.#openconnect); if (typeof val === 'string') return [val]; if (Array.isArray(val)) { if (val.every((x) => typeof x === 'string')) return val as string[]; else throw new Error( - `the array referenced by ${this.#connect} must solely composed of strings` + `the array referenced by ${this.#openconnect} must solely composed of strings` ); } return []; @@ -181,6 +193,20 @@ export class PluginContextImpl implements PluginContext { return val as [string, ...string[]]; } + /** + * {@inheritDoc PluginContext.getOpen} + */ + getOpen(): string[] { + return this.getConnect(); + } + + /** + * {@inheritDoc PluginContext.fetchOpen} + */ + fetchOpen(): [string, ...string[]] { + return this.fetchConnect(); + } + /** * {@inheritDoc PluginContext.get} */ @@ -210,20 +236,20 @@ export class PluginContextImpl implements PluginContext { * @internal */ export class PluginContextTest implements PluginContext { - #connect: string[] = []; + #openconnect: string[] = []; #params = new Map(); readonly phrase: string = ''; // not used but required by the interface - constructor(connect: string | string[], params: Record) { - this.#connect = typeof connect === 'string' ? [connect] : connect; + constructor(openconnect: string | string[], params: Record) { + this.#openconnect = typeof openconnect === 'string' ? [openconnect] : openconnect; this.#params = new Map(Object.entries(params)); } /** * @constructor */ - static connect(connect: string | string[]) { - return new this(connect, {}); + static openconnect(openconnect: string | string[]) { + return new this(openconnect, {}); } /** @@ -251,7 +277,7 @@ export class PluginContextTest implements PluginContext { * {@inheritDoc PluginContext.getConnect} */ getConnect(): string[] { - return this.#connect; + return this.#openconnect; } /** @@ -263,6 +289,20 @@ export class PluginContextTest implements PluginContext { return val as [string, ...string[]]; } + /** + * {@inheritDoc PluginContext.getOpen} + */ + getOpen(): string[] { + return this.getConnect(); + } + + /** + * {@inheritDoc PluginContext.fetchOpen} + */ + fetchOpen(): [string, ...string[]] { + return this.fetchConnect(); + } + /** * {@inheritDoc PluginContext.get} */ diff --git a/pkg/core/src/tokens.ts b/pkg/core/src/tokens.ts index d4c4e534..4e780d99 100644 --- a/pkg/core/src/tokens.ts +++ b/pkg/core/src/tokens.ts @@ -18,6 +18,11 @@ export const Connect = createToken({ pattern: /connect/i, }); +export const Open = createToken({ + name: 'Open', + pattern: /open/i, +}); + export const To = createToken({ name: 'To', pattern: /to/i, @@ -51,6 +56,7 @@ export const And = createToken({ export const allTokens = [ Whitespace, Connect, + Open, Into, Output, And, diff --git a/pkg/core/src/visitor.ts b/pkg/core/src/visitor.ts index 41b5bb2f..31724ff7 100644 --- a/pkg/core/src/visitor.ts +++ b/pkg/core/src/visitor.ts @@ -4,12 +4,12 @@ import { type PhraseCst, type IntoCst, type SendpassCst, - type ConnectCst, + type OpenconnectCst, } from '@slangroom/core'; import type { IToken } from '@slangroom/deps/chevrotain'; export type Statement = { - connect?: string; + openconnect?: string; bindings: Map; phrase: string; into?: string; @@ -27,7 +27,7 @@ interface V { visit(cst: StatementCst): ReturnType; visit(cst: PhraseCst): ReturnType; visit(cst: SendpassCst): ReturnType; - visit(cst: ConnectCst): ReturnType; + visit(cst: OpenconnectCst): ReturnType; visit(cst: IntoCst): ReturnType; } @@ -47,7 +47,7 @@ class V extends CstVisitor { if (stmt.bindings.has(key)) throw new ErrorKeyExists(key); stmt.bindings.set(key, value); }); - if (ctx.connect) stmt.connect = this.visit(ctx.connect); + if (ctx.openconnect) stmt.openconnect = this.visit(ctx.openconnect); if (ctx.into) stmt.into = this.visit(ctx.into); return stmt; } @@ -60,7 +60,7 @@ class V extends CstVisitor { return [this.visit(ctx.phrase), ctx.Identifier[0].image.slice(1, -1)]; } - connect(ctx: ConnectCst['children']): string { + openconnect(ctx: OpenconnectCst['children']): string { return ctx.Identifier[0].image.slice(1, -1); } diff --git a/pkg/core/test/slangroom.ts b/pkg/core/test/slangroom.ts index b5fdbc9e..2607832c 100644 --- a/pkg/core/test/slangroom.ts +++ b/pkg/core/test/slangroom.ts @@ -5,6 +5,8 @@ test('runs all unknown statements', async (t) => { let usedP0 = false; let usedP1 = false; let usedP2 = false; + let usedP3 = false; + let usedP4 = false; const p0 = (ctx: PluginContext): PluginResult => { if (ctx.phrase === 'a') { @@ -32,6 +34,25 @@ test('runs all unknown statements', async (t) => { return ctx.fail('Unkown phrase'); }; + const p3 = (ctx: PluginContext): PluginResult => { + if (ctx.phrase === 'e f') { + usedP3 = true; + t.is(ctx.fetchOpen()[0], 'bar'); + return ctx.pass(null); + } + return ctx.fail('Unkown phrase'); + }; + + const p4 = (ctx: PluginContext): PluginResult => { + if (ctx.phrase === 'f g') { + usedP4 = true; + t.is(ctx.fetchConnect()[0], 'foo'); + return ctx.pass(null); + } + return ctx.fail('Unkown phrase'); + }; + + const script = ` Rule caller restroom-mw Given I A and output into 'a' @@ -42,11 +63,15 @@ Then print 'a' Then I pass a 'a' and B and output into 'b' Then I pass a 'b' and c D Then I pass a 'b' and C d and output into 'mimmo' +Then I open 'b' and e F +Then I connect to 'a' and F g `; - const slangroom = new Slangroom(p0, [p1, new Set([p2])]); + const slangroom = new Slangroom(p0, [p1, new Set([p2]), p3], p4); const res = await slangroom.execute(script); t.true(usedP0); t.true(usedP1); t.true(usedP2); + t.true(usedP3); + t.true(usedP4); t.deepEqual(res.result, { a: 'foo', b: 'bar', mimmo: 'foobar' }, res.logs); }); diff --git a/pkg/core/test/visitor.ts b/pkg/core/test/visitor.ts index e7e53f6a..173edc49 100644 --- a/pkg/core/test/visitor.ts +++ b/pkg/core/test/visitor.ts @@ -21,29 +21,29 @@ test('generated ast is correct', async (t) => { ]), }, "connect to 'foo' and read the ethereum balance": { - connect: 'foo', + openconnect: 'foo', phrase: 'read the ethereum balance', bindings: new Map(), }, "connect to 'foo' and pass address 'addr' and send contract 'contract' and read the ethereum balance": - { - connect: 'foo', - phrase: 'read the ethereum balance', - bindings: new Map([ - ['address', 'addr'], - ['contract', 'contract'], - ]), - }, - "connect to 'foo' and pass address 'addr' and send contract 'contract' and read the ethereum balance and output into 'var'": - { - connect: 'foo', - phrase: 'read the ethereum balance', - bindings: new Map([ - ['address', 'addr'], - ['contract', 'contract'], - ]), - into: 'var', - }, + { + openconnect: 'foo', + phrase: 'read the ethereum balance', + bindings: new Map([ + ['address', 'addr'], + ['contract', 'contract'], + ]), + }, + "open 'foo' and pass address 'addr' and send contract 'contract' and read the ethereum balance and output into 'var'": + { + openconnect: 'foo', + phrase: 'read the ethereum balance', + bindings: new Map([ + ['address', 'addr'], + ['contract', 'contract'], + ]), + into: 'var', + }, }; for (const [line, astWant] of Object.entries(cases)) { diff --git a/pkg/http/test/plugins.ts b/pkg/http/test/plugins.ts index a15003a7..54d60a18 100644 --- a/pkg/http/test/plugins.ts +++ b/pkg/http/test/plugins.ts @@ -40,7 +40,7 @@ test('Simple GET', async (t) => { return; } - const ctx = PluginContextTest.connect('http://localhost/normaljson'); + const ctx = PluginContextTest.openconnect('http://localhost/normaljson'); const res = await execute(ctx, ast.kind, ast.method); t.deepEqual(res, { ok: true,