Implement output/clipboard interfaces, stub implementations, and implement execute()s for Copy and Paste
This commit is contained in:
parent
f36621c646
commit
06ff1b396f
10
src/index.ts
10
src/index.ts
@ -5,6 +5,7 @@ import {Lexer} from "./vm/lexer.js";
|
|||||||
import {Parser} from "./vm/parser.js";
|
import {Parser} from "./vm/parser.js";
|
||||||
import {commands} from "./vm/commands/index.js";
|
import {commands} from "./vm/commands/index.js";
|
||||||
import {Executor} from "./vm/vm.js";
|
import {Executor} from "./vm/vm.js";
|
||||||
|
import {ConsoleDisplay, FakeClipboard, OutputManager} from "./vm/output.js";
|
||||||
|
|
||||||
const lifecycle = new Lifecycle()
|
const lifecycle = new Lifecycle()
|
||||||
const input = new Input()
|
const input = new Input()
|
||||||
@ -17,8 +18,13 @@ lexer.subscribe(token => log.verbose('token', token))
|
|||||||
const parser = new Parser(commands, lexer)
|
const parser = new Parser(commands, lexer)
|
||||||
parser.subscribe(exec => log.verbose('exec', exec))
|
parser.subscribe(exec => log.verbose('exec', exec))
|
||||||
|
|
||||||
const exec = new Executor(parser)
|
const output: OutputManager = {
|
||||||
exec.subscribe(state => state.output())
|
display: new ConsoleDisplay,
|
||||||
|
clipboard: new FakeClipboard,
|
||||||
|
}
|
||||||
|
|
||||||
|
const exec = new Executor(output, parser)
|
||||||
|
exec.subscribe(state => state.outputSubject())
|
||||||
|
|
||||||
input.setupPrompt()
|
input.setupPrompt()
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import {Command, ParseContext} from "./command.js";
|
import {Command, ParseContext, unwrapString} from "./command.js";
|
||||||
import {LexInput} from "../lexer.js";
|
import {LexInput} from "../lexer.js";
|
||||||
|
import {StrVM} from "../vm.js";
|
||||||
|
import {Awaitable} from "../../util/types.js";
|
||||||
|
|
||||||
export class Copy extends Command<{}> {
|
export class Copy extends Command<{}> {
|
||||||
isParseCandidate(token: LexInput): boolean {
|
isParseCandidate(token: LexInput): boolean {
|
||||||
@ -13,4 +15,10 @@ export class Copy extends Command<{}> {
|
|||||||
getDisplayName(): string {
|
getDisplayName(): string {
|
||||||
return 'copy'
|
return 'copy'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
execute(vm: StrVM): Awaitable<StrVM> {
|
||||||
|
return vm.tapInPlace(ctx =>
|
||||||
|
vm.withOutput(output =>
|
||||||
|
output.clipboard.overwrite(unwrapString(ctx.getSubject()))))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import {Command, ParseContext} from "./command.js";
|
import {Command, ParseContext, wrapString} from "./command.js";
|
||||||
import {LexInput} from "../lexer.js";
|
import {LexInput} from "../lexer.js";
|
||||||
|
import {StrVM} from "../vm.js";
|
||||||
|
import {Awaitable} from "../../util/types.js";
|
||||||
|
|
||||||
export class Paste extends Command<{}> {
|
export class Paste extends Command<{}> {
|
||||||
isParseCandidate(token: LexInput): boolean {
|
isParseCandidate(token: LexInput): boolean {
|
||||||
@ -13,4 +15,12 @@ export class Paste extends Command<{}> {
|
|||||||
getDisplayName(): string {
|
getDisplayName(): string {
|
||||||
return 'paste'
|
return 'paste'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
execute(vm: StrVM): Awaitable<StrVM> {
|
||||||
|
return vm.replaceContextMatchingTerm({
|
||||||
|
override: () =>
|
||||||
|
vm.withOutput(async output =>
|
||||||
|
wrapString(await output.clipboard.read()))
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import {StrRVal} from "./commands/command.js";
|
import {StrRVal} from "./commands/command.js";
|
||||||
|
import {Awaitable} from "../util/types.js";
|
||||||
|
|
||||||
export const getSubjectDisplay = (sub: StrRVal): string => {
|
export const getSubjectDisplay = (sub: StrRVal): string => {
|
||||||
if ( sub.term === 'string' ) {
|
if ( sub.term === 'string' ) {
|
||||||
@ -11,3 +12,39 @@ export const getSubjectDisplay = (sub: StrRVal): string => {
|
|||||||
|
|
||||||
return JSON.stringify(sub.value, null, '\t') // fixme
|
return JSON.stringify(sub.value, null, '\t') // fixme
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Display = {
|
||||||
|
showSubject(sub: StrRVal): Awaitable<unknown>
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ConsoleDisplay implements Display {
|
||||||
|
showSubject(sub: StrRVal) {
|
||||||
|
console.log(`\n---------------\n${getSubjectDisplay(sub)}\n---------------\n`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class NullDisplay implements Display {
|
||||||
|
showSubject() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Clipboard = {
|
||||||
|
read(): Awaitable<string>
|
||||||
|
overwrite(sub: string): Awaitable<unknown>
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FakeClipboard {
|
||||||
|
private val = ''
|
||||||
|
|
||||||
|
read() {
|
||||||
|
return this.val
|
||||||
|
}
|
||||||
|
|
||||||
|
overwrite(sub: string) {
|
||||||
|
this.val = sub
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type OutputManager = {
|
||||||
|
display: Display,
|
||||||
|
clipboard: Clipboard,
|
||||||
|
}
|
||||||
|
|||||||
25
src/vm/vm.ts
25
src/vm/vm.ts
@ -14,7 +14,7 @@ import {StreamLogger} from "../util/log.js";
|
|||||||
import {Parser} from "./parser.js";
|
import {Parser} from "./parser.js";
|
||||||
import {log} from "../log.js";
|
import {log} from "../log.js";
|
||||||
import {Executable} from "./parse.js";
|
import {Executable} from "./parse.js";
|
||||||
import {getSubjectDisplay} from "./output.js";
|
import {getSubjectDisplay, OutputManager} from "./output.js";
|
||||||
|
|
||||||
export class Scope {
|
export class Scope {
|
||||||
private entries: Record<string, StrRVal> = {}
|
private entries: Record<string, StrRVal> = {}
|
||||||
@ -188,13 +188,16 @@ export class ExecutionContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class StrVM {
|
export class StrVM {
|
||||||
public static make(): StrVM {
|
public static make(output: OutputManager): StrVM {
|
||||||
return new StrVM(
|
return new StrVM(
|
||||||
new ExecutionContext(wrapString(''), new Scope()))
|
new ExecutionContext(wrapString(''), new Scope()),
|
||||||
|
output,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private context: ExecutionContext,
|
private context: ExecutionContext,
|
||||||
|
private output: OutputManager,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public async runInPlace<TReturn>(operator: (ctx: ExecutionContext) => Awaitable<TReturn>): Promise<TReturn> {
|
public async runInPlace<TReturn>(operator: (ctx: ExecutionContext) => Awaitable<TReturn>): Promise<TReturn> {
|
||||||
@ -230,28 +233,30 @@ export class StrVM {
|
|||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
output() {
|
public async withOutput<TReturn>(operator: (output: OutputManager) => Awaitable<TReturn>): Promise<TReturn> {
|
||||||
console.log('---------------')
|
return operator(this.output)
|
||||||
console.log(getSubjectDisplay(this.context.getSubject()))
|
|
||||||
console.log('---------------')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
makeChild(): StrVM {
|
makeChild(): StrVM {
|
||||||
return new StrVM(this.context.makeChild())
|
return new StrVM(this.context.makeChild(), this.output)
|
||||||
|
}
|
||||||
|
|
||||||
|
async outputSubject(): Promise<void> {
|
||||||
|
await this.output.display.showSubject(this.context.getSubject())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Executor extends BehaviorSubject<StrVM> {
|
export class Executor extends BehaviorSubject<StrVM> {
|
||||||
private logger: StreamLogger
|
private logger: StreamLogger
|
||||||
|
|
||||||
constructor(parser?: Parser) {
|
constructor(private output: OutputManager, parser?: Parser) {
|
||||||
super()
|
super()
|
||||||
this.logger = log.getStreamLogger('executor')
|
this.logger = log.getStreamLogger('executor')
|
||||||
parser?.subscribe(exec => this.handleExecutable(exec))
|
parser?.subscribe(exec => this.handleExecutable(exec))
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleExecutable(exec: Executable<CommandData>) {
|
async handleExecutable(exec: Executable<CommandData>) {
|
||||||
const vm = this.currentValue || StrVM.make()
|
const vm = this.currentValue || StrVM.make(this.output)
|
||||||
await this.next(await exec.command.execute(vm, exec.data))
|
await this.next(await exec.command.execute(vm, exec.data))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user