diff --git a/exec/agent-lang.ts b/exec/agent-lang.ts index 049655b..c97f9dc 100644 --- a/exec/agent-lang.ts +++ b/exec/agent-lang.ts @@ -4,16 +4,14 @@ import { Logger } from "./logger.ts"; import { Parser } from "./parser.ts"; import { Process } from "./process.ts"; import { performance } from "node:perf_hooks"; +import { Performance } from "./performance.ts"; interface ProgramOutput { steps: Output[]; } -let startTime = 0; -let endTime = 0; - // @ts-ignore -const { sourceCode, outputFile, config, debug } = await Parser.initialize(); +const { sourceCode, outputFile, config, debug, compact } = await Parser.initialize(); const interpreter: Interpreter = new Interpreter(); const programOtput: ProgramOutput = { steps: [] }; @@ -25,19 +23,34 @@ interpreter.get(sourceCode, config).subscribe((output: InterpreterOutput) => { } if (debug) { - Logger.info(`Evaluating step ${output.output!.step}`); + Performance.now(); + const elapsed = Performance.lastElapsed(); + const delay = config.delay; + + const step = output.output!.step; + const steps = config.steps; + + Logger.info(`Evaluating step ${step}/${steps} (${elapsed}ms) (${Performance.slowdown(delay, elapsed)}x slowdown)`); } programOtput.steps.push(output.output!); - writeFileSync(outputFile, JSON.stringify(programOtput)); if (output.output!.step === config.steps) { - endTime = performance.now(); - const seconds = (endTime - startTime) / 1000; - const elapsedTime = Math.round(seconds * 100) / 100; + Performance.now(); + const elapsed = Performance.allElapsed(); + + const actualTime = Performance.allElapsed(Performance.milliseconds); + const expectedTime = config.steps * config.delay; - Logger.info(`Finished running (${elapsedTime}s)`); + Logger.info(`Finished running (${elapsed}s) (${Performance.slowdown(expectedTime, actualTime)}x slowdown)`); + + if (compact) { + writeFileSync(outputFile, JSON.stringify(programOtput)); + } else { + writeFileSync(outputFile, JSON.stringify(programOtput, null, 2)); + } Logger.done(`Output has been written to ${outputFile}`); + Process.exit(0); } }); @@ -50,5 +63,5 @@ Logger.info("-----------------"); Logger.info("Running AgentLang"); Logger.info("-----------------"); -startTime = performance.now(); +Performance.now(); interpreter.start(); \ No newline at end of file diff --git a/exec/parser.ts b/exec/parser.ts index 93160fb..ec81cb1 100644 --- a/exec/parser.ts +++ b/exec/parser.ts @@ -9,6 +9,7 @@ interface Data { outputFile: string; config: InterpreterConfiguration; debug: boolean; + compact: boolean; } export class Parser { @@ -17,22 +18,30 @@ export class Parser { public static DEFAULT_OUTPUT_FILE = "output.json"; private static OPTIONS = [ + { name: "in", alias: "i", type: String }, + { name: "out", alias: "o", type: String }, { name: "steps", alias: "s", type: Number }, { name: "delay", alias: "d", type: Number }, { name: "width", alias: "w", type: Number }, { name: "height", alias: "h", type: Number }, - { name: "in", alias: "i", type: String }, - { name: "out", alias: "o", type: String }, { name: "debug", type: Boolean }, + { name: "compact", type: Boolean }, + { name: "help", type: Boolean } ]; static async initialize(): Promise { const options = commandLineArgs(Parser.OPTIONS); + if (options.help !== undefined) { + Parser.help(); + Process.exit(0, false); + } + let inputFile: string; let outputFile = this.DEFAULT_OUTPUT_FILE; let config = Parser.DEFAULT_CONFIG; let debug = false; + let compact = false; if (options.in === undefined) { Logger.error("--input file was not provided"); @@ -75,10 +84,14 @@ export class Parser { debug = options.debug; } + if (options.compact !== undefined) { + compact = options.compact; + } + let sourceCode = Parser.getSourceCode(inputFile); await Parser.createOutputFile(outputFile); - return { sourceCode, outputFile, config, debug }; + return { sourceCode, outputFile, config, debug, compact }; } private static getSourceCode(filename: string): string { @@ -102,4 +115,60 @@ export class Parser { await Deno.create(filename); } } + + private static help(): void { + console.log("AgentLang"); + console.log("----------------------------"); + console.log("%cInterprets AgentLang source code and stores it into an output file as JSON.", "color: rgb(190, 190, 190)"); + + console.log(); + + console.log("Options"); + console.log("----------------------------"); + + console.log("--in, -i [file]"); + console.log(" %cspecifies the path to the source code input file", "color: rgb(190, 190, 190);"); + + console.log(); + + console.log("--out, -o [file]"); + console.log(" %cspecifies the path to the output file", "color: rgb(190, 190, 190);"); + console.log(" [default: output.json]"); + + console.log(); + + console.log("--steps, -s [number]"); + console.log(" %cspecifies the number of steps to evaluate", "color: rgb(190, 190, 190);"); + console.log(" [default: 10]"); + + console.log(); + + console.log("--delay, -d [number]"); + console.log(" %cspecifies the delay in milliseconds between each step", "color: rgb(190, 190, 190);"); + console.log(" [default: 200]"); + + console.log(); + + console.log("--width, -w [number]"); + console.log(" %cspecifies the width of the simulation bounds in pixels", "color: rgb(190, 190, 190);"); + console.log(" [default: 500]"); + + console.log(); + + console.log("--height, -h [number]"); + console.log(" %cspecifies the height of the simulation bounds in pixels", "color: rgb(190, 190, 190);"); + console.log(" [default: 500]"); + + console.log(); + + console.log("--debug [flag]"); + console.log(" whether to print the debug information to the user"); + console.log(" [default: false]"); + + console.log(); + + console.log("--compact [flag]"); + console.log(" whether to store the output JSON in compact mode"); + console.log(" [default: false]"); + } } diff --git a/exec/performance.ts b/exec/performance.ts new file mode 100644 index 0000000..09798b8 --- /dev/null +++ b/exec/performance.ts @@ -0,0 +1,58 @@ +import { performance } from "node:perf_hooks"; + +type PerformanceFunction = (start: number, end: number) => number; + +export class Performance { + + private static entries: number[] = []; + + public static now(): number { + const value = performance.now(); + Performance.entries.push(value); + return value; + } + + public static milliseconds(start: number, end: number): number { + const milliseconds = end - start; + return Performance.elapsed(milliseconds); + } + + public static seconds(start: number, end: number): number { + const seconds = (end - start) / 1000; + return Performance.elapsed(seconds); + } + + public static lastElapsed(func: PerformanceFunction = Performance.milliseconds): number { + if (Performance.entries.length < 2) { + return 0; + } + + const start = Performance.entries[Performance.entries.length - 2]; + const end = Performance.entries[Performance.entries.length - 1]; + + return func(start, end); + } + + public static allElapsed(func: PerformanceFunction = Performance.seconds): number { + if (Performance.entries.length < 2) { + return 0; + } + + const start = Performance.entries[0]; + const end = Performance.entries[Performance.entries.length - 1]; + + return func(start, end); + } + + public static slowdown(expected: number, actual: number): number { + return Performance.elapsed(actual / expected); + } + + private static elapsed(value: number): number { + return Math.round(value * 100) / 100; + } + + public static getEntries(): number[] { + return Performance.entries; + } +} \ No newline at end of file diff --git a/exec/process.ts b/exec/process.ts index 0538a23..e8b95ca 100644 --- a/exec/process.ts +++ b/exec/process.ts @@ -3,15 +3,17 @@ import { exit as processExit } from "node:process"; export class Process { - static exit(code: number): void { - switch (code) { - case 0: { - Logger.done(`Finished with exit code ${code}`); - break; - } - default: { - Logger.error(`Finished with exit code ${code}`); - break; + static exit(code: number, print: boolean = true): void { + if (print) { + switch (code) { + case 0: { + Logger.done(`Finished with exit code ${code}`); + break; + } + default: { + Logger.error(`Finished with exit code ${code}`); + break; + } } }