@thi.ng/pointfree-lang
Version:
Forth style syntax layer/compiler & CLI for the @thi.ng/pointfree DSL
104 lines (101 loc) • 2.77 kB
JavaScript
import {
ARG_VERBOSE,
cliApp,
configureLogLevel,
string,
THING_HEADER
} from "@thi.ng/args";
import { timedResult } from "@thi.ng/bench";
import { readJSON, readText, writeFile } from "@thi.ng/file-io";
import { ensureStack } from "@thi.ng/pointfree";
import { readdirSync, readFileSync } from "node:fs";
import { join } from "node:path";
import { ffi, run, runU } from "./runtime.js";
const PKG = readJSON(join(process.argv[2], "package.json"));
const CMD = {
desc: "",
opts: {},
fn: async ({ logger, opts, inputs }) => {
let src = opts.exec;
if (!src) {
if (!inputs.length) {
process.stderr.write(`require --exec option or a source file`);
process.exit(1);
}
try {
src = readFileSync(inputs[0]).toString();
inputs.shift();
} catch (e) {
process.stderr.write(
`error reading source file ${e.message}`
);
process.exit(1);
}
}
const includeCache = /* @__PURE__ */ new Set();
const rootEnv = { args: inputs };
const builtins = {
include: (ctx) => {
const stack = ctx[0];
ensureStack(stack, 1);
const path = stack.pop();
if (!includeCache.has(path)) {
includeCache.add(path);
run(readText(path, logger), {
...ctx[2],
__vars: null
});
} else {
logger.debug(` ${path} already included, skipping...`);
}
return ctx;
},
"read-file": (ctx) => {
const stack = ctx[0];
ensureStack(stack, 1);
const path = stack.pop();
stack.push(readText(path, logger));
return ctx;
},
"write-file": (ctx) => {
const stack = ctx[0];
ensureStack(stack, 2);
const path = stack.pop();
writeFile(path, stack.pop(), void 0, logger);
return ctx;
},
"read-dir": (ctx) => {
const stack = ctx[0];
ensureStack(stack, 1);
const path = stack.pop();
logger.debug(`reading directory: ${path}`);
stack.push(readdirSync(path));
return ctx;
}
};
const env = ffi(rootEnv, builtins);
const [res, time] = timedResult(() => runU(src, env));
logger.debug(`executed in ${time}ms`);
process.exit(typeof res === "number" ? res : 0);
}
};
cliApp({
name: "pointfree",
start: 3,
opts: {
...ARG_VERBOSE,
exec: string({ alias: "e", desc: "Execute given string" })
},
commands: { CMD },
single: true,
usage: {
prefix: `${THING_HEADER(PKG.name, PKG.version, "Forth-style DSL & CLI")}
Usage: pointfree [opts] [file]
`,
paramWidth: 24
},
ctx: async (ctx) => {
configureLogLevel(ctx.logger, ctx.opts.verbose);
return ctx;
}
});