Skip to content

Commit

Permalink
parser as discussed
Browse files Browse the repository at this point in the history
  • Loading branch information
albertolerda committed Oct 4, 2023
1 parent 5b8d08c commit e893ff6
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 167 deletions.
55 changes: 15 additions & 40 deletions pkg/core/src/parser.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import {
allTokens,
Read,
Connect,
Save,
Send,
Pass,
Within,
And,
To,
Into,
Buzzword,
Connect,
Identifier,
Into,
Output,
Pass,
Send,
To,
} from '@slangroom/core/tokens';
import { CstParser, type IToken } from '@slangroom/deps/chevrotain';

Expand All @@ -20,19 +18,15 @@ const Parser = new (class extends CstParser {
this.performSelfAnalysis();
}

statements = this.RULE('statements', () => {
this.AT_LEAST_ONE_SEP({
SEP: And,
DEF: () => this.SUBRULE(this.#statement),
});
});

#statement = this.RULE('statement', () => {
statement = this.RULE('statement', () => {
this.OPTION1(() => this.SUBRULE(this.#connect));
this.MANY(() => {
this.SUBRULE(this.#sendpass);
});
this.SUBRULE(this.#readsave);
this.SUBRULE(this.#buzzwords);
this.OPTION(() => {
this.SUBRULE(this.#into);
});
});

#connect = this.RULE('connect', () => {
Expand All @@ -51,35 +45,16 @@ const Parser = new (class extends CstParser {
ALT: () => this.CONSUME(Pass),
},
]);
this.OPTION(() => this.SUBRULE(this.#buzzwords));
this.SUBRULE(this.#buzzwords);
this.CONSUME(Identifier);
this.CONSUME(And);
});

#readsave = this.RULE('readsave', () => {
this.OR([{ ALT: () => this.SUBRULE(this.#read) }, { ALT: () => this.SUBRULE(this.#save) }]);
});

#save = this.RULE('save', () => {
this.CONSUME(Save);
this.SUBRULE(this.#buzzwords);
});

#read = this.RULE('read', () => {
this.CONSUME(Read);
this.SUBRULE(this.#buzzwords);
this.OPTION(() => this.SUBRULE(this.#into));
});

#into = this.RULE('into', () => {
this.CONSUME(And);
this.CONSUME(Output);
this.CONSUME(Into);
this.CONSUME(Identifier);
this.OPTION(() => this.SUBRULE(this.#within));
});

#within = this.RULE('within', () => {
this.CONSUME(Within);
this.CONSUME(Identifier);
});

#buzzwords = this.RULE('buzzwords', () => {
Expand All @@ -92,7 +67,7 @@ export const CstVisitor = Parser.getBaseCstVisitorConstructor();
export const parse = (tokens: IToken[]) => {
Parser.input = tokens;

const res = Parser.statements();
const res = Parser.statement();
console.log(Parser.errors)
return res
};
13 changes: 3 additions & 10 deletions pkg/core/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,10 @@ export const buildNormalizedPharse = (phrase: string) =>
phrase.toLowerCase().replace(' ', '_');

export class Plugin {
#func: (...args: Jsonable[]) => Jsonable;
#params: string[];
#phrase: string;

constructor(phrase: string, params: string[]) {
this.#params = params
this.#phrase = buildNormalizedPharse(phrase)
}

protected buildParams(bindings: Map<string, string>, execParams: ExecParams): Jsonable[] {
const args = this.#params.map((v: any) => {
Expand All @@ -70,13 +67,9 @@ export class Plugin {
getPhrase() {
return this.#phrase
}
}

export class ReadPlugin extends Plugin {
#func: (...args: Jsonable[]) => Jsonable;

constructor(phrase: string, params: string[], func: (...args: Jsonable[]) => Jsonable) {
super(phrase, params);
this.#params = params
this.#phrase = buildNormalizedPharse(phrase)
this.#func = func;
}

Expand Down
35 changes: 11 additions & 24 deletions pkg/core/src/slangroom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,51 +3,37 @@ import { ZenroomParams, zencodeExec } from '@slangroom/shared';
import {
Plugin,
ExecParams,
ReadPlugin,
buildNormalizedPharse,
} from '@slangroom/core/plugin';
import { lex } from '@slangroom/core/lexer';
import { parse } from '@slangroom/core/parser';
import { visit, type Statement } from '@slangroom/core/visitor';
import { ActionType } from './visitor.js';
import { Jsonable } from '@slangroom/shared';

type Plugins = Plugin | Set<Plugin> | Array<Plugin | Set<Plugin>>;

export class Slangroom {
#readPlugins = new Map<string, ReadPlugin>();
#plugins = new Map<string, Plugin>();

constructor(first: Plugins, ...rest: Plugins[]) {
const recurse = (x: Plugins) => {
if (Array.isArray(x) || x instanceof Set) x.forEach(recurse);
else {
if (x instanceof ReadPlugin) this.#readPlugins.set(x.getPhrase(), x);
else throw new Error("Unknown object")
this.#plugins.set(x.getPhrase(), x);
}
};
[first, ...rest].forEach(recurse);
}

async executePlugin(p: Statement, params: ExecParams) {
if(p.action.kind == ActionType.Read) {
const normalizedBuzzwords = buildNormalizedPharse(p.action.buzzwords)
const plugin = this.#readPlugins.get(normalizedBuzzwords)
if(plugin) {
const result = plugin.execute(p.bindings, params)
if(p.action.into.length > 0) {
let val: Jsonable = {}
if(p.action.into.length > 1) {
val[p.action.into[1] || ""] = result
} else {
val = result
}
params.set(p.action.into[0] || "", val)
} else {
params.set(normalizedBuzzwords, result)
}
} else {
throw new Error("Unknown phrase")
const normalizedBuzzwords = buildNormalizedPharse(p.buzzwords)
const plugin = this.#plugins.get(normalizedBuzzwords)
if(plugin) {
const result = plugin.execute(p.bindings, params)
if(p.into) {
params.set(p.into, result)
}
} else {
throw new Error("Unknown phrase")
}
}

Expand Down Expand Up @@ -91,3 +77,4 @@ const astify = (line: string) => {
const cst = parse(tokens);
return visit(cst);
};

10 changes: 4 additions & 6 deletions pkg/core/src/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ export const Into = createToken({
pattern: /into/i,
});

export const Within = createToken({
name: 'Within',
pattern: /within/i,
export const Output = createToken({
name: 'Output',
pattern: /output/i,
});

export const Buzzword = createToken({
Expand All @@ -63,11 +63,9 @@ export const Whitespace = createToken({

export const allTokens = [
Whitespace,
Read,
Save,
Connect,
Into,
Within,
Output,
And,
To,
Identifier,
Expand Down
54 changes: 8 additions & 46 deletions pkg/core/src/visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,11 @@ export enum ActionType {
Save,
}

export type Read = {
kind: ActionType.Read,
buzzwords: string,
into: string[],
}

export type Save = {
kind: ActionType.Save,
buzzwords: string,
}

export type Action = Read | Save

export type Statement = {
connect?: string,
bindings: Map<string, string>,
action: Action
buzzwords: string,
into?: string,
}

export class StatementBindings {
Expand All @@ -48,55 +36,29 @@ export const Visitor = new (class extends CstVisitor {
super();
this.validateVisitor();
}
statements(ctx: any) {
return ctx.statement.map((v: any) => this.visit(v))
}
statement(ctx: any) {
const node: Statement = {
action: this.visit(ctx.readsave),
buzzwords: this.visit(ctx.buzzwords),
bindings: new Map<string, string>(
ctx.sendpass?.map((v: any) => this.visit(v))),
}
if(ctx.connect) {
node.connect = this.visit(ctx.connect)
}
if(ctx.into) {
node.into = this.visit(ctx.into)
}
return node
}
connect(ctx: any) {
return ctx.Identifier[0].image.slice(1,-1)
}
sendpass(ctx: any) {
const identifier = ctx.Identifier[0].image.slice(1,-1)
if(ctx.buzzwords) {
return [this.visit(ctx.buzzwords), identifier]
}
return [identifier, identifier]
}
readsave(ctx: any): Action {
return this.visit(ctx.read || ctx.save)
}
save(ctx: any): Save {
return {
kind: ActionType.Save,
buzzwords: this.visit(ctx.buzzwords)
}
}
read(ctx: any): Read {
return {
kind: ActionType.Read,
buzzwords: this.visit(ctx.buzzwords),
into: ctx.into ? this.visit(ctx.into) : []
}
return [this.visit(ctx.buzzwords), identifier]
}
into(ctx: any) {
let path = [ctx.Identifier[0].image.slice(1,-1)]
if(ctx.within) {
path.concat(this.visit(ctx.within))
}
return path
}
within(ctx: any) {
return [ctx.Identifier[0].image.slice(1,-1)]
return ctx.Identifier[0].image.slice(1,-1)
}
buzzwords(ctx: any) {
return ctx.Buzzword.map((v: any) => v.image).join(' ');
Expand Down
24 changes: 12 additions & 12 deletions pkg/core/test/slangroom.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,40 @@
import test from 'ava';
import { ReadPlugin } from '../src/plugin.js';
import { Plugin } from '../src/plugin.js';
import { Slangroom } from '../src/slangroom.js';

test("Runs all unknown statements", async (t) => {
let useR1 = false;
let useR2 = false;
let useR3 = false;
const r1 = new ReadPlugin("a", [], (...[]) => {
const r1 = new Plugin("a", [], (...[]) => {
useR1 = true;
return "foo"
})
const r2 = new ReadPlugin("b", ["a"], (...[output]) => {
const r2 = new Plugin("b", ["a"], (...[a]) => {
useR2 = true
t.is(output, "foo")
t.is(a, "foo")
return "bar"
})
const r3 = new ReadPlugin("c d", ["a"], (...[output]) => {
const r3 = new Plugin("c d", ["a"], (...[a]) => {
useR3 = true
t.is(output, "bar")
t.is(a, "bar")
return "foobar"
})
const script = `
Rule caller restroom-mw
Given I read A
Given I A and output into 'a'
Given I have a 'string' named 'a'
Then print 'a'
Then I pass 'a' and read B
Then I pass a 'b' and read c D
Then I pass a 'b' and read C d into 'mimmo'
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'
`
const slangroom = new Slangroom(r1, r2, r3);
const slangroom = new Slangroom(r1, [r2, r3]);
const res = await slangroom.execute(script, {})
t.truthy(useR1, "r1 is not used")
t.truthy(useR2, "r2 is not used")
t.truthy(useR3, "r3 is not used")
t.deepEqual(res.result, { a: 'foo', b: 'bar', c_d: 'foobar', mimmo: 'foobar' }, res.logs)
t.deepEqual(res.result, { a: 'foo', b: 'bar', mimmo: 'foobar' }, res.logs)
})
Loading

0 comments on commit e893ff6

Please sign in to comment.