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

Bugs fixing #1

Merged
merged 4 commits into from
Mar 10, 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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@
/include
/build
*.tsbuildinfo

# misc
.DS_Store
*.pem
275 changes: 146 additions & 129 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"test:watch": "rbxtsc -w --verbose --type game --rojo test.project.json",
"test:serve": "rojo serve ./test.project.json",
"test:rojo": "mkdir build & npm run test:build && rojo build -o ./build/test.rbxlx test.project.json",
"test:run": "npm run prepublish && npm run test:serve",
"test:run": "npm run test:build && npm run test:serve",
"prepublish": "npm run build && mkdir build && npm pack --pack-destination=build",
"prepare": "husky install",
"test": "npm run test:run"
Expand Down Expand Up @@ -44,7 +44,7 @@
"typescript": "^4.9.5"
},
"dependencies": {
"@aethergames/mkscribe": "^0.1.0",
"@aethergames/mkscribe": "^0.3.0",
"@rbxts/services": "^1.5.1",
"@rbxts/testez": "^0.4.2-ts.0"
}
Expand Down
78 changes: 0 additions & 78 deletions src/ast-interpreter/index.ts

This file was deleted.

40 changes: 39 additions & 1 deletion src/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1 +1,39 @@
export = (): void => {}
/// <reference types="@rbxts/testez/globals" />

import { Scribe } from "."

export = (): void => {
describe("creating a new ScribeRuntime", () => {
const ScribeRuntime = Scribe.load(`
actor BEATRIZ "beatriz_id"

default objective hello "My objective!"

echo $test

interact BEATRIZ {
echo "Interacted with Beatriz!"
}

`, {
test: "Hello, world!"
})

it("should not throw when instantiating a new ScribeRuntime.", () => {
expect(() => Scribe.load("", {})).never.to.throw()
})

it("should not throw when retrieving an objective.", () => {
expect(() => {
ScribeRuntime.getCurrentObjective()
ScribeRuntime.getObjective("hello")
}).never.to.throw()
})

it("should interact correctly with the actor", () => {
expect(() => {
ScribeRuntime.interact("beatriz_id")
}).never.to.throw()
})
})
}
13 changes: 4 additions & 9 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
import { Runtime } from "./ast-interpreter";
import { ExecutionType, ModuleSource, ScribeEnviroment, StringSource, StringValueSource } from "./types";
import { ScribeVM } from "./vm";
import { Runtime } from "./runtime";
import { ModuleSource, ScribeEnviroment, StringSource, StringValueSource } from "./types";

export namespace Scribe {
export function load<T extends ExecutionType>(
file: string | StringValue | ModuleScript,
env: ScribeEnviroment,
executionType: T,
): T extends "ast-interpreter" ? Runtime : ScribeVM {
export function load(file: string | StringValue | ModuleScript, env: ScribeEnviroment): Runtime {
const toRetrieveFrom = typeOf(file);
let source!: string;

Expand All @@ -29,6 +24,6 @@ export namespace Scribe {
}
}

return (executionType === "ast-interpreter" ? new Runtime(source, env) : new ScribeVM(source, env)) as never;
return new Runtime(source, env);
}
}
158 changes: 158 additions & 0 deletions src/runtime/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import { MkScribe } from "@aethergames/mkscribe";
import { ScribeEnviroment } from "../types";
import {
DialogCallbackInput,
InteractionJob,
Objective,
ObjectiveChangeCallbackInput,
PipeToCallbackInput,
ScribeProgramProperties,
ScribeRuntimeImplementation,
} from "./types";
import { TokenLiteral } from "@aethergames/mkscribe/out/mkscribe/scanner/types";
import { ScribeVisitor, StatusInterpretationCode } from "./visitor";
import { RunService } from "@rbxts/services";

export class Runtime implements ScribeRuntimeImplementation {
public dialogCallback!: (input: DialogCallbackInput) => void;
public objectiveChangeCallback!: (input: ObjectiveChangeCallbackInput) => void;
public pipeTo!: (config: PipeToCallbackInput) => void;

private interpreter!: ScribeVisitor;

private interactions: Map<string, InteractionJob> = new Map();
private interactionsCooldown = 1 / 60;

constructor(private readonly source: string, private readonly env: ScribeEnviroment) {}

public start(): StatusInterpretationCode {
this.interpreter = new ScribeVisitor(
MkScribe.build(this.source),
{
onDialog: this.dialogCallback,
onStoreChange: this.pipeTo,
onObjectiveChange: this.objectiveChangeCallback,
},
this.env,
);

return this.interpreter.interpret();
}

public getObjective(objective: string): Objective | undefined {
return this.interpreter.records.objectives[objective];
}

public getCurrentObjective(): Objective | undefined {
const current = this.interpreter.records.objectives.current;

if (current !== undefined) {
return this.interpreter.records.objectives[current];
}
}

public getProperty(property: string): TokenLiteral {
return this.interpreter.programProperties[property];
}

public getProperties(): ScribeProgramProperties {
return this.interpreter.programProperties;
}

public incrementStore(valueTo: string, increment: number): void {
let head = this.interpreter.refs[valueTo];

while (head !== undefined) {
const { ref } = head;

if (typeOf(this.interpreter.records.stores[ref][0]) === "number") {
(this.interpreter.records.stores[ref][0] as number) += increment;
} else {
warn(`[Interpreter:incrementValue]: ${ref} isn't of type number, it can't be incremented.`);
}

head = head._next;
}
}

public setStore(valueTo: string, value: unknown): void {
let head = this.interpreter.refs[valueTo];

while (head !== undefined) {
this.interpreter.records.stores[head.ref][0] = value as TokenLiteral;

head = head._next;
}
}

public setCurrentObjective(objective: string): void {
this.interpreter.records.objectives.current = objective;
}

public play(id: string): void {
const scene = this.interpreter.records.scenes[id];

if (scene !== undefined) {
this.interpreter.resolve(this.interpreter.records.scenes[id]);
} else {
throw `Scene specified [${id}] isn't defined on the Scribe's program.`;
}
}

public async interact(id: string): Promise<void> {
if (this.interactions.has(id) === false) {
this.interactions.set(id, {
cleanup: undefined,
lastInteraction: 0,
queue: new Array(),
});
}

// eslint-disable-next-line prefer-const
let { cleanup, lastInteraction, queue } = this.interactions.get(id)!;

if (cleanup !== undefined) {
cleanup.Disconnect();
}

const interaction = this.interpreter.records.interactions[id];
if (os.clock() - lastInteraction > this.interactionsCooldown && queue.size() === 0) {
lastInteraction = os.clock();

return this.interpreter.resolve(interaction);
} else {
queue.push(interaction);

// eslint-disable-next-line no-constant-condition
while (true) {
if (os.clock() - lastInteraction > this.interactionsCooldown && queue[1] === interaction) {
this.clean(id);

return this.interpreter.resolve(interaction);
} else {
task.wait();
}
}
}
}

private clean(id: string): void {
const interactionJob = this.interactions.get(id);

if (interactionJob !== undefined) {
const { cleanup, lastInteraction, queue } = interactionJob;

if (cleanup !== undefined) {
interactionJob.cleanup = RunService.PostSimulation.Connect(() => {
if (os.clock() - lastInteraction > this.interactionsCooldown && queue.size() === 0) {
if (this.interactions.has(id)) {
this.interactions.delete(id);
}

cleanup.Disconnect();
}
});
}
}
}
}
27 changes: 26 additions & 1 deletion src/ast-interpreter/types.d.ts → src/runtime/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,20 @@ import { Statement } from "@aethergames/mkscribe";
import { TokenLiteral } from "@aethergames/mkscribe/out/mkscribe/scanner/types";
import { StatusInterpretationCode } from "./visitor";

declare type ScribeProperties = "title" | "description";
export declare type InteractionJob = {
cleanup: RBXScriptConnection | undefined;
lastInteraction: number;
queue: Array<Statement>;
};

export declare type ScribeProperties = "title" | "description";

export declare type Objective = {
id: number;
name: string;
desc: string;
active: boolean;
};

export declare type ScribeProgramProperties = Record<string | ScribeProperties, TokenLiteral>;

Expand All @@ -23,6 +36,11 @@ export declare type DialogCallbackInput = {
step: (id?: number) => void;
};

export declare type ObjectiveChangeCallbackInput = {
id: string;
description: string;
};

export declare type PipeToCallbackInput = {
identifier: string;
data: unknown;
Expand Down Expand Up @@ -52,6 +70,13 @@ export interface ScribeRuntimeImplementation {
*/
start(): StatusInterpretationCode;

getObjective(objective: string): Objective | undefined;

/**
*
*/
getCurrentObjective(): Objective | undefined;

/**
* Retrieve's a property's value.
*
Expand Down
File renamed without changes.
File renamed without changes.
Loading