-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(core): new framework for plugins
- Loading branch information
1 parent
96375b9
commit 5af445c
Showing
9 changed files
with
555 additions
and
1,131 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { allTokens } from '@slangroom/core/tokens'; | ||
import { Lexer as L } from '@slangroom/deps/chevrotain'; | ||
|
||
const Lexer = new L(allTokens); | ||
|
||
/** | ||
* Lexes the given line. | ||
*/ | ||
export const lex = (line: string) => Lexer.tokenize(line); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import { | ||
allTokens, | ||
Read, | ||
Connect, | ||
Save, | ||
Within, | ||
And, | ||
Into, | ||
Buzzword, | ||
Identifier, | ||
} from '@slangroom/core/tokens'; | ||
import { CstParser, type IToken } from '@slangroom/deps/chevrotain'; | ||
|
||
const Parser = new (class extends CstParser { | ||
constructor() { | ||
super(allTokens); | ||
this.performSelfAnalysis(); | ||
} | ||
|
||
statement = this.RULE('statement', () => { | ||
this.OR([ | ||
{ ALT: () => this.SUBRULE(this.#connect) }, | ||
{ ALT: () => this.SUBRULE(this.#read) }, | ||
]); | ||
}); | ||
|
||
#connect = this.RULE('connect', () => { | ||
this.CONSUME(Connect); | ||
this.SUBRULE(this.#action); | ||
}); | ||
|
||
#read = this.RULE('read', () => { | ||
this.CONSUME(Read); | ||
this.SUBRULE(this.#action, { LABEL: 'readAction' }); | ||
this.OPTION(() => | ||
this.OR([ | ||
{ ALT: () => this.SUBRULE(this.#andSave) }, | ||
{ ALT: () => this.SUBRULE(this.#into) }, | ||
]) | ||
); | ||
}); | ||
|
||
#andSave = this.RULE('andSave', () => { | ||
this.CONSUME(And); | ||
this.CONSUME(Save); | ||
this.SUBRULE(this.#action, { LABEL: 'saveAction' }); | ||
}); | ||
|
||
#into = this.RULE('into', () => { | ||
this.CONSUME(Into); | ||
this.CONSUME(Identifier, { LABEL: 'intoIdentifier' }); | ||
this.OPTION(() => this.SUBRULE(this.#within)); | ||
}); | ||
|
||
#within = this.RULE('within', () => { | ||
this.CONSUME(Within); | ||
this.CONSUME(Identifier, { LABEL: 'withinIdentifier' }); | ||
}); | ||
|
||
#action = this.RULE('action', () => { | ||
this.AT_LEAST_ONE(() => this.SUBRULE(this.#phrase)); | ||
}); | ||
|
||
#phrase = this.RULE('phrase', () => { | ||
this.CONSUME(Buzzword); | ||
this.OPTION(() => this.CONSUME(Identifier)); | ||
}); | ||
})(); | ||
|
||
export const CstVisitor = Parser.getBaseCstVisitorConstructor(); | ||
|
||
export const parse = (tokens: IToken[]) => { | ||
Parser.input = tokens; | ||
return Parser.statement(); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,52 +1,105 @@ | ||
import type { ZenroomParams, JsonableObject } from '@slangroom/shared'; | ||
|
||
/** | ||
* A plugin that must be executed **before** the actual Zenroom execution takes | ||
* place. | ||
* | ||
* The plugin is defined using a single parameter which is a callback, | ||
* named [execute], which takes in the necessary parameters from [BeforeParams] | ||
* and optionally returns a [ZenroomParams]. | ||
*/ | ||
export class BeforePlugin { | ||
constructor( | ||
readonly execute: (params: BeforeParams | ||
) => Promise<void> | void | Promise<JsonableObject> | JsonableObject | ||
) { } | ||
import type { Jsonable, JsonableObject, ZenroomParams } from '@slangroom/shared'; | ||
import { Action } from '@slangroom/core/visitor'; | ||
|
||
export class ExecContext { | ||
#store = new Map<any, any>(); | ||
|
||
get(key: any): any { | ||
return this.#store.get(key); | ||
} | ||
|
||
set(key: any, value: any) { | ||
this.#store.set(key, value); | ||
} | ||
} | ||
|
||
/** | ||
* A plugin that must be executed **after** the actual Zenroom execution takes | ||
* place. | ||
* | ||
* The plugin is defined using a single parameter which is a callback, | ||
* named [execute], which takes in the necessary parameters from [AfterParams]. | ||
*/ | ||
export class AfterPlugin { | ||
constructor(readonly execute: (params: AfterParams | ||
) => Promise<void> | void | Promise<JsonableObject> | JsonableObject | ||
) { } | ||
export class ExecParams { | ||
#data: JsonableObject; | ||
#keys: JsonableObject; | ||
|
||
contsructor(params: ZenroomParams) { | ||
this.#data = params.data || {}; | ||
this.#keys = params.keys || {}; | ||
} | ||
|
||
get(key: string): Jsonable | undefined { | ||
return this.#data[key] ? this.#data[key] : this.#keys[key]; | ||
} | ||
|
||
set(key: string, value: Jsonable) { | ||
this.#data[key] = value; | ||
} | ||
} | ||
|
||
/** | ||
* The parameters passed down to [BeforePlugin]'s callback. | ||
* | ||
* [statement] is the ignored statement for each iteration. | ||
* [params] is the original parameters passed to Zenroom, if any. | ||
*/ | ||
export type BeforeParams = { | ||
readonly statement: string; | ||
readonly params: ZenroomParams | undefined; | ||
}; | ||
|
||
/** | ||
* The parameters passed down to [BeforePlugin]'s callback. | ||
* [statement] is the ignored statement for each iteration. | ||
* [params] is the original parameters passed to Zenroom, if any. | ||
* [result] is the result of the actual Zenroom execution. | ||
*/ | ||
export type AfterParams = { | ||
readonly statement: string; | ||
readonly params: ZenroomParams | undefined; | ||
readonly result: JsonableObject; | ||
}; | ||
export abstract class Plugin { | ||
#phrase: string; | ||
|
||
constructor(phrase: string) { | ||
this.#phrase = phrase.toLowerCase(); | ||
} | ||
|
||
match(actn: Action) { | ||
return actn.phrase.toLowerCase() === this.#phrase; | ||
} | ||
|
||
abstract execute(params: ExecParams, ctx: ExecContext): void | Promise<void>; | ||
} | ||
|
||
export class ConnectPlugin extends Plugin { | ||
#execute: (params: ExecParams, ctx: ExecContext) => void; | ||
constructor(phrase: string, cb: (params: ExecParams, ctx: ExecContext) => void) { | ||
super(phrase); | ||
// TODO: using `phrase`, fetch all the variables and put them inside | ||
// `ExecParams`. | ||
this.#execute = cb; | ||
} | ||
|
||
execute(params: ExecParams, ctx: ExecContext) { | ||
this.#execute(params, ctx); | ||
} | ||
} | ||
|
||
export class ReadPlugin extends Plugin { | ||
#execute: (params: ExecParams, ctx: ExecContext) => void; | ||
|
||
constructor(phrase: string, cb: (params: ExecParams, ctx: ExecContext) => void) { | ||
super(phrase); | ||
// TODO: using `phrase`, fetch all the variables and put them inside | ||
// `ExecParams`. | ||
this.#execute = cb; | ||
} | ||
|
||
execute(params: ExecParams, ctx: ExecContext) { | ||
this.#execute(params, ctx); | ||
} | ||
} | ||
|
||
export class IntoPlugin extends Plugin { | ||
#execute: (params: ExecParams) => void; | ||
|
||
constructor(phrase: string, cb: (params: ExecParams) => void) { | ||
super(phrase); | ||
// TODO: using `phrase`, fetch all the variables and put them inside | ||
// `ExecParams`. | ||
this.#execute = cb; | ||
} | ||
|
||
execute(params: ExecParams) { | ||
this.#execute(params); | ||
} | ||
} | ||
|
||
export class SavePlugin extends Plugin { | ||
#execute: (params: ExecParams) => void; | ||
|
||
constructor(phrase: string, cb: (params: ExecParams) => void) { | ||
super(phrase); | ||
// TODO: using `phrase`, fetch all the variables and put them inside | ||
// `ExecParams`. | ||
this.#execute = cb; | ||
} | ||
|
||
execute(params: ExecParams) { | ||
this.#execute(params); | ||
} | ||
} |
Oops, something went wrong.