Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): synonymize "open" and "connect to" #29

Merged
merged 2 commits into from
Oct 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions pkg/core/src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
And,
Buzzword,
Connect,
Open,
Identifier,
Into,
Output,
Expand All @@ -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] };
};

Expand Down Expand Up @@ -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);
});
Expand Down
76 changes: 62 additions & 14 deletions pkg/core/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,33 @@
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.
*
Expand Down Expand Up @@ -106,10 +118,10 @@
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
Expand All @@ -129,7 +141,7 @@

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;
}
Expand Down Expand Up @@ -159,14 +171,14 @@
* {@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`

Check warning on line 181 in pkg/core/src/plugin.ts

View check run for this annotation

Codecov / codecov/patch

pkg/core/src/plugin.ts#L181

Added line #L181 was not covered by tests
);
}
return [];
Expand All @@ -181,6 +193,20 @@
return val as [string, ...string[]];
}

/**
* {@inheritDoc PluginContext.getOpen}
*/
getOpen(): string[] {
return this.getConnect();
}

Check warning on line 201 in pkg/core/src/plugin.ts

View check run for this annotation

Codecov / codecov/patch

pkg/core/src/plugin.ts#L200-L201

Added lines #L200 - L201 were not covered by tests

/**
* {@inheritDoc PluginContext.fetchOpen}
*/
fetchOpen(): [string, ...string[]] {
return this.fetchConnect();
}

/**
* {@inheritDoc PluginContext.get}
*/
Expand Down Expand Up @@ -210,22 +236,30 @@
* @internal
*/
export class PluginContextTest implements PluginContext {
#connect: string[] = [];
#openconnect: string[] = [];
#params = new Map<string, Jsonable>();
readonly phrase: string = ''; // not used but required by the interface

constructor(connect: string | string[], params: Record<string, Jsonable>) {
this.#connect = typeof connect === 'string' ? [connect] : connect;
constructor(openconnect: string | string[], params: Record<string, Jsonable>) {
this.#openconnect = typeof openconnect === 'string' ? [openconnect] : openconnect;
this.#params = new Map(Object.entries(params));
}

/**
* @constructor
*/
static open(open: string | string[]) {
return new this(open, {});
}

Check warning on line 253 in pkg/core/src/plugin.ts

View check run for this annotation

Codecov / codecov/patch

pkg/core/src/plugin.ts#L252-L253

Added lines #L252 - L253 were not covered by tests

/**
* @constructor
*/
static connect(connect: string | string[]) {
return new this(connect, {});
}


/**
* @constructor
*/
Expand All @@ -251,7 +285,7 @@
* {@inheritDoc PluginContext.getConnect}
*/
getConnect(): string[] {
return this.#connect;
return this.#openconnect;
}

/**
Expand All @@ -263,6 +297,20 @@
return val as [string, ...string[]];
}

/**
* {@inheritDoc PluginContext.getOpen}
*/
getOpen(): string[] {
return this.getConnect();
}

Check warning on line 305 in pkg/core/src/plugin.ts

View check run for this annotation

Codecov / codecov/patch

pkg/core/src/plugin.ts#L304-L305

Added lines #L304 - L305 were not covered by tests

/**
* {@inheritDoc PluginContext.fetchOpen}
*/
fetchOpen(): [string, ...string[]] {
return this.fetchConnect();
}

Check warning on line 312 in pkg/core/src/plugin.ts

View check run for this annotation

Codecov / codecov/patch

pkg/core/src/plugin.ts#L311-L312

Added lines #L311 - L312 were not covered by tests

/**
* {@inheritDoc PluginContext.get}
*/
Expand Down
6 changes: 6 additions & 0 deletions pkg/core/src/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -51,6 +56,7 @@ export const And = createToken({
export const allTokens = [
Whitespace,
Connect,
Open,
Into,
Output,
And,
Expand Down
10 changes: 5 additions & 5 deletions pkg/core/src/visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, string>;
phrase: string;
into?: string;
Expand All @@ -27,7 +27,7 @@ interface V {
visit(cst: StatementCst): ReturnType<this['statement']>;
visit(cst: PhraseCst): ReturnType<this['phrase']>;
visit(cst: SendpassCst): ReturnType<this['sendpass']>;
visit(cst: ConnectCst): ReturnType<this['connect']>;
visit(cst: OpenconnectCst): ReturnType<this['openconnect']>;
visit(cst: IntoCst): ReturnType<this['into']>;
}

Expand All @@ -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;
}
Expand All @@ -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);
}

Expand Down
27 changes: 26 additions & 1 deletion pkg/core/test/slangroom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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') {
Expand Down Expand Up @@ -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'
Expand All @@ -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);
});
38 changes: 19 additions & 19 deletions pkg/core/test/visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, string>(),
},
"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<string, string>([
['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<string, string>([
['address', 'addr'],
['contract', 'contract'],
]),
into: 'var',
},
{
openconnect: 'foo',
phrase: 'read the ethereum balance',
bindings: new Map<string, string>([
['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<string, string>([
['address', 'addr'],
['contract', 'contract'],
]),
into: 'var',
},
};

for (const [line, astWant] of Object.entries(cases)) {
Expand Down