Skip to content

Commit

Permalink
feat: implement the rudimentary build system (#756)
Browse files Browse the repository at this point in the history
  • Loading branch information
Dotneteer committed Apr 20, 2024
1 parent 1f54b64 commit ef66dc8
Show file tree
Hide file tree
Showing 36 changed files with 863 additions and 405 deletions.
1 change: 1 addition & 0 deletions src/common/abstractions/IScriptManager.ts
Expand Up @@ -34,4 +34,5 @@ export type ScriptStartInfo = {
id?: number;
target?: string;
contents?: string;
hasParseError?: boolean;
};
2 changes: 2 additions & 0 deletions src/common/abstractions/ScriptRunInfo.ts
Expand Up @@ -19,6 +19,8 @@ export type ScriptRunInfo = {
startTime: Date;
endTime?: Date;
stopTime?: Date;
specialScript?: string;
scriptFunction?: string;
};

export type ScriptExecutionState = ScriptRunInfo & {
Expand Down
2 changes: 1 addition & 1 deletion src/common/ksx/eval-tree-common.ts
Expand Up @@ -436,7 +436,7 @@ export function evalArrow (
return lazyArrow;
}

function obtainClosures (thread: LogicalThread): BlockScope[] {
export function obtainClosures (thread: LogicalThread): BlockScope[] {
const closures = thread.blocks?.slice(0) ?? [];
return thread.parent
? [...obtainClosures(thread.parent), ...closures]
Expand Down
51 changes: 9 additions & 42 deletions src/common/ksx/ksx-module.ts
@@ -1,7 +1,9 @@
import { BlockScope } from "./BlockScope";
import { EvaluationContext, ModuleResolver, PackageResolver } from "./EvaluationContext";
import { Parser } from "./Parser";
import { ErrorCodes, ParserErrorMessage, errorMessages } from "./ParserError";
import { TokenType } from "./TokenType";
import { obtainClosures } from "./eval-tree-common";
import {
processStatementQueueAsync,
visitLetConstDeclarations
Expand Down Expand Up @@ -163,29 +165,6 @@ export async function parseKsxModule (
// --- Use these exports
let exportsToUse: Record<string, any> = {};

// // --- Find the imported package
// if (stmt.moduleFile.startsWith("@")) {
// const packageContent = await packageResolver(stmt.moduleFile.substring(1));
// if (packageContent === null) {
// errors.push(addErrorMessage("K027", stmt, stmt.moduleFile));
// continue;
// }

// // --- Successful import
// importedModules.push(packageModule);

// // --- Extract imported names
// for (const key in stmt.imports) {
// if (packageModule.exports.has(stmt.imports[key])) {
// imports[key] = packageModule.exports.get(stmt.imports[key]);
// } else {
// errors.push(addErrorMessage("K026", stmt, stmt.moduleFile, key));
// }
// }

// continue;
// }

// --- Find the imported module
const source = await moduleResolver(stmt.moduleFile);
if (source === null) {
Expand Down Expand Up @@ -277,23 +256,6 @@ export async function executeModule (
blockScope[0].constVars ??= new Set<string>();
const topConst = blockScope[0].constVars;

// --- Convert hoisted functions to ArrowExpressions
for (const functionDecl of Object.values(module.functions)) {
const arrowExpression = {
type: "ArrowExpression",
args: functionDecl.args,
statement: functionDecl.statement,
_ARROW_EXPR_: true
} as unknown as ArrowExpression;

// --- Store the compiled functions in the to-level BlockScope
if (topVars[functionDecl.name]) {
throw new Error(`Function ${functionDecl.name} already exists`);
}
topVars[functionDecl.name] = arrowExpression;
topConst.add(functionDecl.name);
}

// --- Load imported modules
module.statements
.filter(stmt => stmt.type === "ImportDeclaration")
Expand All @@ -308,13 +270,18 @@ export async function executeModule (

// --- Run the module
await processStatementQueueAsync(module.statements, evaluationContext);
module.executed = true;

// --- Get exported values
for (const key in module.exports) {
if (!(key in topVars)) {
throw new Error(`Export ${key} not found`);
}
module.exports[key] = topVars[key];
const exported = topVars[key];
module.exports[key] = exported;

// --- Set the closure context of exported arrow expressions
if (exported?.type === "ArrowExpression" && exported?._ARROW_EXPR_) {
exported.closureContext = obtainClosures(evaluationContext.mainThread!);
}
}
}
60 changes: 55 additions & 5 deletions src/common/ksx/process-statement-async.ts
Expand Up @@ -18,10 +18,12 @@ import {
} from "./process-statement-common";
import {
ArrayDestructure,
ArrowExpression,
AssignmentExpression,
ConstStatement,
EmptyStatement,
ExpressionStatement,
FunctionDeclaration,
Identifier,
LetStatement,
Literal,
Expand All @@ -45,6 +47,7 @@ import {
StatementExecutionError
} from "./engine-errors";
import { executeModule } from "./ksx-module";
import { obtainClosures } from "./eval-tree-common";

export type OnStatementCompletedCallback =
| ((
Expand All @@ -64,6 +67,10 @@ export async function processStatementQueueAsync (
// --- Create the main thread for the queue
thread = ensureMainThread(evalContext);
}

// --- Hoist function declarations to the top scope
hoistFunctionDeclarations(thread, statements);

// --- Fill the queue with items
const queue = new StatementQueue();
queue.push(mapStatementsToQueueItems(statements));
Expand Down Expand Up @@ -199,24 +206,24 @@ async function processStatementAsync (
const childEvalContext = createEvalContext({
cancellationToken: evalContext.cancellationToken
});
statement.module!.executed = true;
statement.module!.executed = true;
await executeModule(statement.module, childEvalContext);
}

// --- Import the module's exported variables into the parent module
const topVars = evalContext.mainThread.blocks[0].vars;
const topConst = evalContext.mainThread.blocks[0].constVars;
const topVars = evalContext.mainThread!.blocks![0].vars!;
const topConst = evalContext.mainThread!.blocks![0].constVars!;
for (const key of Object.keys(statement.imports)) {
if (key in topVars) {
throw new Error(`Import ${key} already exists`);
}
topVars[key] = statement.module.exports[statement.imports[key]];
topVars[key] = statement.module!.exports[statement.imports[key]];
topConst.add(key);
}
break;

case "FunctionDeclaration":
// --- Function declarations are already hoisted, nothing to do
// --- Function declarations are already hoisted, collect their closure context
break;

case "EmptyStatement":
Expand All @@ -233,6 +240,9 @@ async function processStatementAsync (
vars: {}
});

// --- Hoist function declarations to the innermost block scope
hoistFunctionDeclarations(thread, statement.statements);

// --- Prepare an empty statement that will only remove the block scope when the entire block is processed
const closing = {
type: "EmptyStatement",
Expand Down Expand Up @@ -1107,3 +1117,43 @@ export function visitLetConstDeclarations (
}
}
}

// --- Hoist function definitions to the innermost block scope
function hoistFunctionDeclarations (
thread: LogicalThread,
statements: Statement[]
): void {
const block = innermostBlockScope(thread);
if (!block) {
throw new Error("Missing block scope");
}
statements
.filter(stmt => stmt.type === "FunctionDeclaration")
.forEach(stmt => {
const funcDecl = stmt as FunctionDeclaration;

// --- Turn the function into an arrow expression
const arrowExpression = {
type: "ArrowExpression",
args: funcDecl.args,
statement: funcDecl.statement,
closureContext: obtainClosures(thread),
_ARROW_EXPR_: true
} as unknown as ArrowExpression;

// --- Remove the functions from the closure list

// --- Check name uniqueness
const id = funcDecl.name;
if (block.vars[id]) {
throw new Error(
`Variable ${id} is already declared in the current scope.`
);
}

// --- Function is a constant
block.vars[id] = arrowExpression;
block.constVars ??= new Set<string>();
block.constVars.add(id);
});
}
1 change: 1 addition & 0 deletions src/common/ksx/script-runner.ts
Expand Up @@ -46,6 +46,7 @@ export function concludeScript (
}
);
} catch (error) {
console.log("Script error", error);
script.status = "execError";
script.error = error.message;
script.endTime = new Date();
Expand Down
17 changes: 1 addition & 16 deletions src/common/messaging/EmuToMainMessenger.ts
Expand Up @@ -5,6 +5,7 @@ import {
} from "@messaging/messages-core";
import { MessengerBase } from "@messaging/MessengerBase";
import { ipcRenderer, IpcRendererEvent } from "electron";

/**
* Implements a messenger that send messages from the Emu to the Main process
*/
Expand Down Expand Up @@ -44,19 +45,3 @@ export class EmuToMainMessenger extends MessengerBase {
return "EmuToMainResponse";
}
}

/**
* The singleton messenger instance
*/
//const emuToMainMessenger = new EmuToMainMessenger();

// /**
// * Sends the specified message from the Emu process to Main
// * @param message Message to send
// * @returns Response
// */
// export async function sendFromEmuToMain<TResp extends ResponseMessage> (
// message: RequestMessage
// ): Promise<TResp> {
// return await emuToMainMessenger.sendMessage(message);
// }
36 changes: 25 additions & 11 deletions src/common/messaging/any-to-ide.ts
@@ -1,9 +1,8 @@
import { BufferOperation, OutputColor } from "@appIde/ToolArea/abstractions";
import { MessageBase } from "./messages-core";
import { ProjectStructure } from "@main/ksx-runner/ProjectStructure";

/**
* You can sens an output pane message with these request to the IDE
*/
// --- Ask the IDE to display an output in the specified pane
export interface IdeDisplayOutputRequest extends MessageBase {
type: "IdeDisplayOutput";
pane: string;
Expand All @@ -19,50 +18,65 @@ export interface IdeDisplayOutputRequest extends MessageBase {
writeLine?: boolean;
}

// --- Ask the IDE to display a message in the scripts output
export interface IdeScriptOutputRequest extends MessageBase {
type: "IdeScriptOutput";
id: number;
operation: BufferOperation;
args?: any[];
}


// --- Ask the IDE to open the Machine Memory display
export interface IdeShowMemoryRequest extends MessageBase {
type: "IdeShowMemory";
show: boolean;
}

// --- Ask the IDE to open the Disassembly display
export interface IdeShowDisassemblyRequest extends MessageBase {
type: "IdeShowDisassembly";
show: boolean;
}

export interface IdeShowBankedDisassemblyRequest extends MessageBase {
type: "IdeShowBankedDisassembly";
show: boolean;
}

// --- Ask the IDE to open the BASIC listing display
export interface IdeShowBasicRequest extends MessageBase {
type: "IdeShowBasic";
show: boolean;
}

// --- Ask the IDE to show a particular dialog
export interface IdeShowDialogRequest extends MessageBase {
type: "IdeShowDialog";
dialogId?: number;
}

// --- Ask the IDE to execute a command
export interface IdeExecuteCommandRequest extends MessageBase {
type: "IdeExecuteCommand";
commandText: string;
}

// --- Ask the IDE to save all files before quitting
export interface IdeSaveAllBeforeQuitRequest extends MessageBase {
type: "IdeSaveAllBeforeQuit";
}

// --- Ask the IDE to get the current project structure
export interface IdeGetProjectStructureRequest extends MessageBase {
type: "IdeGetProjectStructure";
}

// --- Reteurns the result of the command execution
export interface IdeExecuteCommandResponse extends MessageBase {
type: "IdeExecuteCommandResponse";
success: boolean;
finalMessage?: string;
value?: any;
}

export interface IdeSaveAllBeforeQuitRequest extends MessageBase {
type: "IdeSaveAllBeforeQuit";
// --- The response with the current project structure
export interface IdeGetProjectStructureResponse extends MessageBase {
type: "IdeGetProjectStructureResponse";
projectStructure: ProjectStructure;
}

0 comments on commit ef66dc8

Please sign in to comment.