funcity-cli 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Kouji Matsui (@kekyo@mi.kekyo.net)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,113 @@
1
+ # funcity
2
+
3
+ A functional language interpreter with text processing.
4
+
5
+ ![funcity](./images/funcity.120.png)
6
+
7
+ [![Project Status: WIP – Initial development is in progress, but there has not yet been a stable, usable release suitable for the public.](https://www.repostatus.org/badges/latest/wip.svg)](https://www.repostatus.org/#wip)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
+
10
+ ---
11
+
12
+ ## What is this?
13
+
14
+ This is a lightweight functional language processor implemented in TypeScript, featuring syntax extensions for text processing.
15
+ It includes a CLI application and a library package containing only the core engine.
16
+
17
+ funcity can be considered a type of [text template processor](https://en.wikipedia.org/wiki/Template_processor).
18
+ For example, entering code like this:
19
+
20
+ ```funcity
21
+ Today is {{if weather.sunny}}nice{{else}}bad{{end}}weather.
22
+ ```
23
+
24
+ Evaluates the value of the `weather` variable manually bound to the core engine beforehand and generates different text outputs:
25
+
26
+ ```
27
+ Today is bad weather.
28
+ ```
29
+
30
+ The `if ... else ... end` statements in the text indicate that the script is being executed.
31
+ So, you might ask, what makes this a "Functional language"?
32
+ Or how is it different from existing text processors?
33
+
34
+ Let me show you another equivalent example:
35
+
36
+ ```funcity
37
+ Today is {{cond weather.sunny 'nice' 'bad'}} weather.
38
+ ```
39
+
40
+ This is an example of function application,
41
+ inserting the result of applying three arguments to the `cond` function.
42
+ The first argument is a conditional expression.
43
+
44
+ The following code may further interest you:
45
+
46
+ ```funcity
47
+ {{
48
+ set printWeather (fun w (cond w.sunny 'nice' 'bad'))
49
+ }}
50
+ Today is {{printWeather weather}} weather.
51
+ ```
52
+
53
+ - `fun` defines an anonymous lambda function.
54
+ - `set` performs a mutable binding in the current scope.
55
+
56
+ In other words, funcity is an interpreter that brings the power of functional programming to text template processors, making them easier to handle!
57
+
58
+ ### Features
59
+
60
+ - A lightweight functional language processor for handling untyped lambda calculus.
61
+ Adopted the simplest possible syntax.
62
+ Additionally, selected the syntax extensions that should be prioritized for text processing.
63
+ - All function objects are treated as asynchronous functions.
64
+ You do not need to be aware that they are asynchronous functions when applying them.
65
+ - There is also a CLI using the core engine.
66
+ The CLI has both REPL mode and text processing mode.
67
+ - The core engine includes a tokenizer, parser, and reducer (interpreter).
68
+ - The core engine library is highly independent,
69
+ requiring no dependencies on other libraries or packages.
70
+ It can be easily integrated into your application.
71
+ - Parsers and interpreters support both interpreting pure expressions and interpreting full text-processing syntax.
72
+ This means that even when an interpreter for a purely functional language is required,
73
+ it is possible to completely ignore the (somewhat incongruous) syntax of text processing.
74
+ - Allows pre-binding of useful standard function implementations.
75
+
76
+ ---
77
+
78
+ ## Installation (CLI)
79
+
80
+ TODO:
81
+
82
+ ```bash
83
+ npm install -D funcity-cli
84
+ ```
85
+
86
+ Or, global installation:
87
+
88
+ ```bash
89
+ npm install -g funcity-cli
90
+ ```
91
+
92
+ ## Installation (Library)
93
+
94
+ ```bash
95
+ npm install funcity
96
+ ```
97
+
98
+ ---
99
+
100
+ ## Documentation
101
+
102
+ For detailed documentation and advanced features, please visit our [GitHub repository](https://github.com/kekyo/funcity).
103
+
104
+ ## Note
105
+
106
+ funcity was separated from the document site generator [mark-the-ripper](https://github.com/kekyo/mark-the-ripper) during its design phase,
107
+ as it seemed better suited to function as an independent scripting engine.
108
+
109
+ Therefore, mark-the-ripper can leverage the power of funcity's functional language.
110
+
111
+ ## License
112
+
113
+ Under MIT.
package/dist/cli.d.ts ADDED
@@ -0,0 +1,26 @@
1
+ /*!
2
+ * name: funcity-cli
3
+ * version: 0.4.0
4
+ * description: A functional language interpreter with text processing
5
+ * author: Kouji Matsui (@kekyo@mi.kekyo.net)
6
+ * license: MIT
7
+ * repository.url: https://github.com/kekyo/funcity.git
8
+ * git.commit.hash: 7064fa018cf2e5818672d285f03a1985020da0a8
9
+ */
10
+
11
+ import { FunCityErrorInfo } from 'funcity';
12
+ export interface ReplEvaluationResult {
13
+ readonly output: string;
14
+ readonly errors: FunCityErrorInfo[];
15
+ }
16
+ export interface ReplSession {
17
+ evaluateLine: (line: string) => Promise<ReplEvaluationResult>;
18
+ }
19
+ export declare const createReplSession: () => ReplSession;
20
+ export interface ScriptRunResult {
21
+ readonly output: string;
22
+ readonly errors: FunCityErrorInfo[];
23
+ }
24
+ export declare const runScriptText: (script: string) => Promise<ScriptRunResult>;
25
+ export declare const runMain: (argv?: string[]) => Promise<void>;
26
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;;;;;;;;AAqBA,OAAO,KAAK,EAEV,gBAAgB,EAEjB,MAAM,SAAS,CAAC;AAuCjB,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC;CACrC;AAED,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,oBAAoB,CAAC,CAAC;CAC/D;AAED,eAAO,MAAM,iBAAiB,QAAO,WAqBpC,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC;CACrC;AAED,eAAO,MAAM,aAAa,GACxB,QAAQ,MAAM,KACb,OAAO,CAAC,eAAe,CAQzB,CAAC;AA6IF,eAAO,MAAM,OAAO,GAAU,OAAM,MAAM,EAAiB,KAAG,OAAO,CAAC,IAAI,CAmCzE,CAAC"}
@@ -0,0 +1,18 @@
1
+ /*!
2
+ * name: funcity-cli
3
+ * version: 0.4.0
4
+ * description: A functional language interpreter with text processing
5
+ * author: Kouji Matsui (@kekyo@mi.kekyo.net)
6
+ * license: MIT
7
+ * repository.url: https://github.com/kekyo/funcity.git
8
+ * git.commit.hash: 7064fa018cf2e5818672d285f03a1985020da0a8
9
+ */
10
+
11
+ export declare const name = "funcity-cli";
12
+ export declare const version = "0.4.0";
13
+ export declare const description = "A functional language interpreter with text processing";
14
+ export declare const author = "Kouji Matsui (@kekyo@mi.kekyo.net)";
15
+ export declare const license = "MIT";
16
+ export declare const repository_url = "https://github.com/kekyo/funcity.git";
17
+ export declare const git_commit_hash = "7064fa018cf2e5818672d285f03a1985020da0a8";
18
+ //# sourceMappingURL=packageMetadata.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"packageMetadata.d.ts","sourceRoot":"","sources":["../../src/generated/packageMetadata.ts"],"names":[],"mappings":";;;;;;;;;AAIA,eAAO,MAAM,IAAI,gBAAgB,CAAC;AAClC,eAAO,MAAM,OAAO,UAAU,CAAC;AAC/B,eAAO,MAAM,WAAW,2DAA2D,CAAC;AACpF,eAAO,MAAM,MAAM,uCAAuC,CAAC;AAC3D,eAAO,MAAM,OAAO,QAAQ,CAAC;AAC7B,eAAO,MAAM,cAAc,yCAAyC,CAAC;AACrE,eAAO,MAAM,eAAe,6CAA6C,CAAC"}
package/dist/index.cjs ADDED
@@ -0,0 +1,215 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ const commander = require("commander");
4
+ const readline = require("node:readline");
5
+ const promises = require("node:fs/promises");
6
+ const funcity = require("funcity");
7
+ const name = "funcity-cli";
8
+ const version = "0.4.0";
9
+ const description = "A functional language interpreter with text processing";
10
+ const git_commit_hash = "7064fa018cf2e5818672d285f03a1985020da0a8";
11
+ const promptText = "funcity> ";
12
+ const continuationPromptText = "? ";
13
+ const readStream = async (stream) => {
14
+ return await new Promise((resolve, reject) => {
15
+ let data = "";
16
+ if (typeof stream.setEncoding === "function") {
17
+ stream.setEncoding("utf8");
18
+ }
19
+ stream.on("data", (chunk) => {
20
+ data += String(chunk);
21
+ });
22
+ stream.on("end", () => {
23
+ resolve(data);
24
+ });
25
+ stream.on("error", (error) => {
26
+ reject(error);
27
+ });
28
+ });
29
+ };
30
+ const collectResults = async (context, nodes) => {
31
+ const resultList = [];
32
+ for (const node of nodes) {
33
+ const results = await funcity.reduceNode(context, node);
34
+ for (const result of results) {
35
+ if (result !== void 0) {
36
+ resultList.push(result);
37
+ }
38
+ }
39
+ }
40
+ return resultList;
41
+ };
42
+ const createReplSession = () => {
43
+ const variables = funcity.buildCandidateVariables();
44
+ const errors = [];
45
+ const context = funcity.createReducerContext(variables, errors);
46
+ const evaluateLine = async (line) => {
47
+ errors.length = 0;
48
+ const tokens = funcity.runCodeTokenizer(line, errors);
49
+ const nodes = funcity.parseExpressions(tokens, errors);
50
+ const results = await collectResults(context, nodes);
51
+ const output = results.length > 0 ? results.map((result) => funcity.convertToString(result)).join("\n") : "";
52
+ return {
53
+ output,
54
+ errors: [...errors]
55
+ };
56
+ };
57
+ return { evaluateLine };
58
+ };
59
+ const runScriptText = async (script) => {
60
+ const errors = [];
61
+ const variables = funcity.buildCandidateVariables();
62
+ const tokens = funcity.runTokenizer(script, errors);
63
+ const nodes = funcity.runParser(tokens, errors);
64
+ const results = await funcity.runReducer(nodes, variables, errors);
65
+ const output = results.map((result) => funcity.convertToString(result)).join("");
66
+ return { output, errors };
67
+ };
68
+ const runRepl = async () => {
69
+ const session = createReplSession();
70
+ const rl = readline.createInterface({
71
+ input: process.stdin,
72
+ output: process.stdout,
73
+ prompt: promptText
74
+ });
75
+ let bufferedLine = "";
76
+ const setPrompt = (isContinuation) => {
77
+ rl.setPrompt(isContinuation ? continuationPromptText : promptText);
78
+ };
79
+ const hasLineContinuation = (line) => line.endsWith("\\");
80
+ const evaluateAndPrint = async (line) => {
81
+ try {
82
+ const { output, errors } = await session.evaluateLine(line);
83
+ if (errors.length > 0) {
84
+ funcity.outputErrors("<repl>", errors);
85
+ }
86
+ if (output) {
87
+ console.log(output);
88
+ }
89
+ } catch (error) {
90
+ const message = error instanceof Error ? error.message : String(error);
91
+ console.error(message);
92
+ }
93
+ };
94
+ setPrompt(false);
95
+ rl.prompt();
96
+ for await (const line of rl) {
97
+ if (bufferedLine) {
98
+ bufferedLine += `
99
+ ${line}`;
100
+ } else {
101
+ bufferedLine = line;
102
+ }
103
+ if (hasLineContinuation(line)) {
104
+ setPrompt(true);
105
+ rl.prompt();
106
+ continue;
107
+ }
108
+ const logicalLine = bufferedLine;
109
+ bufferedLine = "";
110
+ await evaluateAndPrint(logicalLine);
111
+ setPrompt(false);
112
+ rl.prompt();
113
+ }
114
+ if (bufferedLine) {
115
+ await evaluateAndPrint(bufferedLine);
116
+ }
117
+ rl.close();
118
+ };
119
+ const runScript = async (input) => {
120
+ const isStdin = input === "-";
121
+ const source = isStdin ? "<stdin>" : input;
122
+ const script = isStdin ? await readStream(process.stdin) : await promises.readFile(input, "utf8");
123
+ const { output, errors } = await runScriptText(script);
124
+ const hasError = funcity.outputErrors(source, errors);
125
+ if (output) {
126
+ process.stdout.write(output);
127
+ }
128
+ if (hasError) {
129
+ process.exitCode = 1;
130
+ }
131
+ };
132
+ const findExplicitCommand = (args) => {
133
+ for (let index = 0; index < args.length; index++) {
134
+ const arg = args[index];
135
+ if (arg === "--") {
136
+ const next = args[index + 1];
137
+ if (next === "repl" || next === "run") {
138
+ return next;
139
+ }
140
+ return void 0;
141
+ }
142
+ if (arg === "-i" || arg === "--input") {
143
+ index += 1;
144
+ continue;
145
+ }
146
+ if (arg.startsWith("--input=")) {
147
+ continue;
148
+ }
149
+ if (arg.startsWith("-")) {
150
+ continue;
151
+ }
152
+ return arg === "repl" || arg === "run" ? arg : void 0;
153
+ }
154
+ return void 0;
155
+ };
156
+ const hasInputOption = (args) => {
157
+ for (const arg of args) {
158
+ if (arg === "--") {
159
+ break;
160
+ }
161
+ if (arg === "-i" || arg === "--input" || arg.startsWith("--input=")) {
162
+ return true;
163
+ }
164
+ }
165
+ return false;
166
+ };
167
+ const injectDefaultCommand = (argv) => {
168
+ const args = argv.slice(2);
169
+ const explicitCommand = findExplicitCommand(args);
170
+ if (explicitCommand) {
171
+ return argv;
172
+ }
173
+ const hasHelp = args.some(
174
+ (arg) => arg === "-h" || arg === "--help" || arg === "-V" || arg === "--version"
175
+ );
176
+ if (hasHelp) {
177
+ return argv;
178
+ }
179
+ const command = hasInputOption(args) ? "run" : "repl";
180
+ const [execPath = "node", scriptPath = "funcity"] = argv;
181
+ return [execPath, scriptPath, command, ...args];
182
+ };
183
+ const runMain = async (argv = process.argv) => {
184
+ const program = new commander.Command();
185
+ program.name(name);
186
+ program.summary(description);
187
+ program.addHelpText(
188
+ "beforeAll",
189
+ `${name}
190
+ ${description}
191
+ `
192
+ );
193
+ program.version(
194
+ `${version}-${git_commit_hash}`
195
+ );
196
+ program.showHelpAfterError(true);
197
+ program.command("repl").summary("Start an interactive REPL session").action(async () => {
198
+ await runRepl();
199
+ });
200
+ program.command("run").summary("Run a script from a file or stdin").addOption(
201
+ new commander.Option(
202
+ "-i, --input <path>",
203
+ 'Input file path or "-" for stdin'
204
+ ).default("-")
205
+ ).action(async (options) => {
206
+ await runScript(options.input);
207
+ });
208
+ await program.parseAsync(injectDefaultCommand(argv));
209
+ };
210
+ runMain().catch((error) => {
211
+ const message = error instanceof Error ? error.message : String(error);
212
+ console.error(message);
213
+ process.exitCode = 1;
214
+ });
215
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../src/generated/packageMetadata.ts","../src/cli.ts","../src/index.ts"],"sourcesContent":["// @ts-nocheck\n// This file is auto-generated by screw-up plugin\n// Do not edit manually\n\nexport const name = \"funcity-cli\";\nexport const version = \"0.4.0\";\nexport const description = \"A functional language interpreter with text processing\";\nexport const author = \"Kouji Matsui (@kekyo@mi.kekyo.net)\";\nexport const license = \"MIT\";\nexport const repository_url = \"https://github.com/kekyo/funcity.git\";\nexport const git_commit_hash = \"7064fa018cf2e5818672d285f03a1985020da0a8\";\n","// funcity - A functional language interpreter with text processing\n// Copyright (c) Kouji Matsui (@kekyo@mi.kekyo.net)\n// Under MIT.\n// https://github.com/kekyo/funcity/\n\nimport { Command, Option } from 'commander';\nimport readline from 'node:readline';\nimport { readFile } from 'node:fs/promises';\nimport * as packageMetadata from './generated/packageMetadata';\nimport {\n buildCandidateVariables,\n convertToString,\n createReducerContext,\n outputErrors,\n parseExpressions,\n reduceNode,\n runParser,\n runReducer,\n runTokenizer,\n runCodeTokenizer,\n} from 'funcity';\nimport type {\n FunCityBlockNode,\n FunCityErrorInfo,\n FunCityReducerContext,\n} from 'funcity';\n\nconst promptText = 'funcity> ';\nconst continuationPromptText = '? ';\n\nconst readStream = async (stream: NodeJS.ReadableStream): Promise<string> => {\n return await new Promise((resolve, reject) => {\n let data = '';\n if (typeof stream.setEncoding === 'function') {\n stream.setEncoding('utf8');\n }\n stream.on('data', (chunk) => {\n data += String(chunk);\n });\n stream.on('end', () => {\n resolve(data);\n });\n stream.on('error', (error) => {\n reject(error);\n });\n });\n};\n\nconst collectResults = async (\n context: FunCityReducerContext,\n nodes: readonly FunCityBlockNode[]\n): Promise<unknown[]> => {\n const resultList: unknown[] = [];\n for (const node of nodes) {\n const results = await reduceNode(context, node);\n for (const result of results) {\n if (result !== undefined) {\n resultList.push(result);\n }\n }\n }\n return resultList;\n};\n\nexport interface ReplEvaluationResult {\n readonly output: string;\n readonly errors: FunCityErrorInfo[];\n}\n\nexport interface ReplSession {\n evaluateLine: (line: string) => Promise<ReplEvaluationResult>;\n}\n\nexport const createReplSession = (): ReplSession => {\n const variables = buildCandidateVariables();\n const errors: FunCityErrorInfo[] = [];\n const context = createReducerContext(variables, errors);\n\n const evaluateLine = async (line: string): Promise<ReplEvaluationResult> => {\n errors.length = 0;\n const tokens = runCodeTokenizer(line, errors);\n const nodes = parseExpressions(tokens, errors);\n const results = await collectResults(context, nodes);\n const output =\n results.length > 0\n ? results.map((result) => convertToString(result)).join('\\n')\n : '';\n return {\n output,\n errors: [...errors],\n };\n };\n\n return { evaluateLine };\n};\n\nexport interface ScriptRunResult {\n readonly output: string;\n readonly errors: FunCityErrorInfo[];\n}\n\nexport const runScriptText = async (\n script: string\n): Promise<ScriptRunResult> => {\n const errors: FunCityErrorInfo[] = [];\n const variables = buildCandidateVariables();\n const tokens = runTokenizer(script, errors);\n const nodes = runParser(tokens, errors);\n const results = await runReducer(nodes, variables, errors);\n const output = results.map((result) => convertToString(result)).join('');\n return { output, errors };\n};\n\nconst runRepl = async (): Promise<void> => {\n const session = createReplSession();\n\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n prompt: promptText,\n });\n\n let bufferedLine = '';\n\n const setPrompt = (isContinuation: boolean) => {\n rl.setPrompt(isContinuation ? continuationPromptText : promptText);\n };\n\n const hasLineContinuation = (line: string): boolean => line.endsWith('\\\\');\n\n const evaluateAndPrint = async (line: string) => {\n try {\n const { output, errors } = await session.evaluateLine(line);\n if (errors.length > 0) {\n outputErrors('<repl>', errors);\n }\n if (output) {\n console.log(output);\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.error(message);\n }\n };\n\n setPrompt(false);\n rl.prompt();\n\n for await (const line of rl) {\n if (bufferedLine) {\n bufferedLine += `\\n${line}`;\n } else {\n bufferedLine = line;\n }\n\n if (hasLineContinuation(line)) {\n setPrompt(true);\n rl.prompt();\n continue;\n }\n\n const logicalLine = bufferedLine;\n bufferedLine = '';\n await evaluateAndPrint(logicalLine);\n setPrompt(false);\n rl.prompt();\n }\n\n if (bufferedLine) {\n await evaluateAndPrint(bufferedLine);\n }\n\n rl.close();\n};\n\nconst runScript = async (input: string): Promise<void> => {\n const isStdin = input === '-';\n const source = isStdin ? '<stdin>' : input;\n const script = isStdin\n ? await readStream(process.stdin)\n : await readFile(input, 'utf8');\n\n const { output, errors } = await runScriptText(script);\n const hasError = outputErrors(source, errors);\n\n if (output) {\n process.stdout.write(output);\n }\n if (hasError) {\n process.exitCode = 1;\n }\n};\n\nconst findExplicitCommand = (\n args: readonly string[]\n): 'repl' | 'run' | undefined => {\n for (let index = 0; index < args.length; index++) {\n const arg = args[index]!;\n if (arg === '--') {\n const next = args[index + 1];\n if (next === 'repl' || next === 'run') {\n return next;\n }\n return undefined;\n }\n if (arg === '-i' || arg === '--input') {\n index += 1;\n continue;\n }\n if (arg.startsWith('--input=')) {\n continue;\n }\n if (arg.startsWith('-')) {\n continue;\n }\n return arg === 'repl' || arg === 'run' ? arg : undefined;\n }\n return undefined;\n};\n\nconst hasInputOption = (args: readonly string[]): boolean => {\n for (const arg of args) {\n if (arg === '--') {\n break;\n }\n if (arg === '-i' || arg === '--input' || arg.startsWith('--input=')) {\n return true;\n }\n }\n return false;\n};\n\nconst injectDefaultCommand = (argv: string[]): string[] => {\n const args = argv.slice(2);\n const explicitCommand = findExplicitCommand(args);\n if (explicitCommand) {\n return argv;\n }\n\n const hasHelp = args.some(\n (arg) =>\n arg === '-h' || arg === '--help' || arg === '-V' || arg === '--version'\n );\n if (hasHelp) {\n return argv;\n }\n\n const command = hasInputOption(args) ? 'run' : 'repl';\n const [execPath = 'node', scriptPath = 'funcity'] = argv;\n return [execPath, scriptPath, command, ...args];\n};\n\nexport const runMain = async (argv: string[] = process.argv): Promise<void> => {\n const program = new Command();\n\n program.name(packageMetadata.name);\n program.summary(packageMetadata.description);\n program.addHelpText(\n 'beforeAll',\n `${packageMetadata.name}\\n${packageMetadata.description}\\n`\n );\n program.version(\n `${packageMetadata.version}-${packageMetadata.git_commit_hash}`\n );\n program.showHelpAfterError(true);\n\n program\n .command('repl')\n .summary('Start an interactive REPL session')\n .action(async () => {\n await runRepl();\n });\n\n program\n .command('run')\n .summary('Run a script from a file or stdin')\n .addOption(\n new Option(\n '-i, --input <path>',\n 'Input file path or \"-\" for stdin'\n ).default('-')\n )\n .action(async (options: { input: string }) => {\n await runScript(options.input);\n });\n\n await program.parseAsync(injectDefaultCommand(argv));\n};\n","// funcity - A functional language interpreter with text processing\n// Copyright (c) Kouji Matsui (@kekyo@mi.kekyo.net)\n// Under MIT.\n// https://github.com/kekyo/funcity/\n\nimport { runMain } from './cli';\n\nrunMain().catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n console.error(message);\n process.exitCode = 1;\n});\n"],"names":["reduceNode","buildCandidateVariables","createReducerContext","runCodeTokenizer","parseExpressions","convertToString","runTokenizer","runParser","runReducer","outputErrors","readFile","Command","packageMetadata.name","packageMetadata.description","packageMetadata.version","packageMetadata.git_commit_hash","Option"],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /*!
2
+ * name: funcity-cli
3
+ * version: 0.4.0
4
+ * description: A functional language interpreter with text processing
5
+ * author: Kouji Matsui (@kekyo@mi.kekyo.net)
6
+ * license: MIT
7
+ * repository.url: https://github.com/kekyo/funcity.git
8
+ * git.commit.hash: 7064fa018cf2e5818672d285f03a1985020da0a8
9
+ */
10
+
11
+ export {};
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;"}
package/dist/index.js ADDED
@@ -0,0 +1,214 @@
1
+ #!/usr/bin/env node
2
+ import { Command, Option } from "commander";
3
+ import readline from "node:readline";
4
+ import { readFile } from "node:fs/promises";
5
+ import { outputErrors, buildCandidateVariables, createReducerContext, runTokenizer, runParser, runReducer, convertToString, runCodeTokenizer, parseExpressions, reduceNode } from "funcity";
6
+ const name = "funcity-cli";
7
+ const version = "0.4.0";
8
+ const description = "A functional language interpreter with text processing";
9
+ const git_commit_hash = "7064fa018cf2e5818672d285f03a1985020da0a8";
10
+ const promptText = "funcity> ";
11
+ const continuationPromptText = "? ";
12
+ const readStream = async (stream) => {
13
+ return await new Promise((resolve, reject) => {
14
+ let data = "";
15
+ if (typeof stream.setEncoding === "function") {
16
+ stream.setEncoding("utf8");
17
+ }
18
+ stream.on("data", (chunk) => {
19
+ data += String(chunk);
20
+ });
21
+ stream.on("end", () => {
22
+ resolve(data);
23
+ });
24
+ stream.on("error", (error) => {
25
+ reject(error);
26
+ });
27
+ });
28
+ };
29
+ const collectResults = async (context, nodes) => {
30
+ const resultList = [];
31
+ for (const node of nodes) {
32
+ const results = await reduceNode(context, node);
33
+ for (const result of results) {
34
+ if (result !== void 0) {
35
+ resultList.push(result);
36
+ }
37
+ }
38
+ }
39
+ return resultList;
40
+ };
41
+ const createReplSession = () => {
42
+ const variables = buildCandidateVariables();
43
+ const errors = [];
44
+ const context = createReducerContext(variables, errors);
45
+ const evaluateLine = async (line) => {
46
+ errors.length = 0;
47
+ const tokens = runCodeTokenizer(line, errors);
48
+ const nodes = parseExpressions(tokens, errors);
49
+ const results = await collectResults(context, nodes);
50
+ const output = results.length > 0 ? results.map((result) => convertToString(result)).join("\n") : "";
51
+ return {
52
+ output,
53
+ errors: [...errors]
54
+ };
55
+ };
56
+ return { evaluateLine };
57
+ };
58
+ const runScriptText = async (script) => {
59
+ const errors = [];
60
+ const variables = buildCandidateVariables();
61
+ const tokens = runTokenizer(script, errors);
62
+ const nodes = runParser(tokens, errors);
63
+ const results = await runReducer(nodes, variables, errors);
64
+ const output = results.map((result) => convertToString(result)).join("");
65
+ return { output, errors };
66
+ };
67
+ const runRepl = async () => {
68
+ const session = createReplSession();
69
+ const rl = readline.createInterface({
70
+ input: process.stdin,
71
+ output: process.stdout,
72
+ prompt: promptText
73
+ });
74
+ let bufferedLine = "";
75
+ const setPrompt = (isContinuation) => {
76
+ rl.setPrompt(isContinuation ? continuationPromptText : promptText);
77
+ };
78
+ const hasLineContinuation = (line) => line.endsWith("\\");
79
+ const evaluateAndPrint = async (line) => {
80
+ try {
81
+ const { output, errors } = await session.evaluateLine(line);
82
+ if (errors.length > 0) {
83
+ outputErrors("<repl>", errors);
84
+ }
85
+ if (output) {
86
+ console.log(output);
87
+ }
88
+ } catch (error) {
89
+ const message = error instanceof Error ? error.message : String(error);
90
+ console.error(message);
91
+ }
92
+ };
93
+ setPrompt(false);
94
+ rl.prompt();
95
+ for await (const line of rl) {
96
+ if (bufferedLine) {
97
+ bufferedLine += `
98
+ ${line}`;
99
+ } else {
100
+ bufferedLine = line;
101
+ }
102
+ if (hasLineContinuation(line)) {
103
+ setPrompt(true);
104
+ rl.prompt();
105
+ continue;
106
+ }
107
+ const logicalLine = bufferedLine;
108
+ bufferedLine = "";
109
+ await evaluateAndPrint(logicalLine);
110
+ setPrompt(false);
111
+ rl.prompt();
112
+ }
113
+ if (bufferedLine) {
114
+ await evaluateAndPrint(bufferedLine);
115
+ }
116
+ rl.close();
117
+ };
118
+ const runScript = async (input) => {
119
+ const isStdin = input === "-";
120
+ const source = isStdin ? "<stdin>" : input;
121
+ const script = isStdin ? await readStream(process.stdin) : await readFile(input, "utf8");
122
+ const { output, errors } = await runScriptText(script);
123
+ const hasError = outputErrors(source, errors);
124
+ if (output) {
125
+ process.stdout.write(output);
126
+ }
127
+ if (hasError) {
128
+ process.exitCode = 1;
129
+ }
130
+ };
131
+ const findExplicitCommand = (args) => {
132
+ for (let index = 0; index < args.length; index++) {
133
+ const arg = args[index];
134
+ if (arg === "--") {
135
+ const next = args[index + 1];
136
+ if (next === "repl" || next === "run") {
137
+ return next;
138
+ }
139
+ return void 0;
140
+ }
141
+ if (arg === "-i" || arg === "--input") {
142
+ index += 1;
143
+ continue;
144
+ }
145
+ if (arg.startsWith("--input=")) {
146
+ continue;
147
+ }
148
+ if (arg.startsWith("-")) {
149
+ continue;
150
+ }
151
+ return arg === "repl" || arg === "run" ? arg : void 0;
152
+ }
153
+ return void 0;
154
+ };
155
+ const hasInputOption = (args) => {
156
+ for (const arg of args) {
157
+ if (arg === "--") {
158
+ break;
159
+ }
160
+ if (arg === "-i" || arg === "--input" || arg.startsWith("--input=")) {
161
+ return true;
162
+ }
163
+ }
164
+ return false;
165
+ };
166
+ const injectDefaultCommand = (argv) => {
167
+ const args = argv.slice(2);
168
+ const explicitCommand = findExplicitCommand(args);
169
+ if (explicitCommand) {
170
+ return argv;
171
+ }
172
+ const hasHelp = args.some(
173
+ (arg) => arg === "-h" || arg === "--help" || arg === "-V" || arg === "--version"
174
+ );
175
+ if (hasHelp) {
176
+ return argv;
177
+ }
178
+ const command = hasInputOption(args) ? "run" : "repl";
179
+ const [execPath = "node", scriptPath = "funcity"] = argv;
180
+ return [execPath, scriptPath, command, ...args];
181
+ };
182
+ const runMain = async (argv = process.argv) => {
183
+ const program = new Command();
184
+ program.name(name);
185
+ program.summary(description);
186
+ program.addHelpText(
187
+ "beforeAll",
188
+ `${name}
189
+ ${description}
190
+ `
191
+ );
192
+ program.version(
193
+ `${version}-${git_commit_hash}`
194
+ );
195
+ program.showHelpAfterError(true);
196
+ program.command("repl").summary("Start an interactive REPL session").action(async () => {
197
+ await runRepl();
198
+ });
199
+ program.command("run").summary("Run a script from a file or stdin").addOption(
200
+ new Option(
201
+ "-i, --input <path>",
202
+ 'Input file path or "-" for stdin'
203
+ ).default("-")
204
+ ).action(async (options) => {
205
+ await runScript(options.input);
206
+ });
207
+ await program.parseAsync(injectDefaultCommand(argv));
208
+ };
209
+ runMain().catch((error) => {
210
+ const message = error instanceof Error ? error.message : String(error);
211
+ console.error(message);
212
+ process.exitCode = 1;
213
+ });
214
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/generated/packageMetadata.ts","../src/cli.ts","../src/index.ts"],"sourcesContent":["// @ts-nocheck\n// This file is auto-generated by screw-up plugin\n// Do not edit manually\n\nexport const name = \"funcity-cli\";\nexport const version = \"0.4.0\";\nexport const description = \"A functional language interpreter with text processing\";\nexport const author = \"Kouji Matsui (@kekyo@mi.kekyo.net)\";\nexport const license = \"MIT\";\nexport const repository_url = \"https://github.com/kekyo/funcity.git\";\nexport const git_commit_hash = \"7064fa018cf2e5818672d285f03a1985020da0a8\";\n","// funcity - A functional language interpreter with text processing\n// Copyright (c) Kouji Matsui (@kekyo@mi.kekyo.net)\n// Under MIT.\n// https://github.com/kekyo/funcity/\n\nimport { Command, Option } from 'commander';\nimport readline from 'node:readline';\nimport { readFile } from 'node:fs/promises';\nimport * as packageMetadata from './generated/packageMetadata';\nimport {\n buildCandidateVariables,\n convertToString,\n createReducerContext,\n outputErrors,\n parseExpressions,\n reduceNode,\n runParser,\n runReducer,\n runTokenizer,\n runCodeTokenizer,\n} from 'funcity';\nimport type {\n FunCityBlockNode,\n FunCityErrorInfo,\n FunCityReducerContext,\n} from 'funcity';\n\nconst promptText = 'funcity> ';\nconst continuationPromptText = '? ';\n\nconst readStream = async (stream: NodeJS.ReadableStream): Promise<string> => {\n return await new Promise((resolve, reject) => {\n let data = '';\n if (typeof stream.setEncoding === 'function') {\n stream.setEncoding('utf8');\n }\n stream.on('data', (chunk) => {\n data += String(chunk);\n });\n stream.on('end', () => {\n resolve(data);\n });\n stream.on('error', (error) => {\n reject(error);\n });\n });\n};\n\nconst collectResults = async (\n context: FunCityReducerContext,\n nodes: readonly FunCityBlockNode[]\n): Promise<unknown[]> => {\n const resultList: unknown[] = [];\n for (const node of nodes) {\n const results = await reduceNode(context, node);\n for (const result of results) {\n if (result !== undefined) {\n resultList.push(result);\n }\n }\n }\n return resultList;\n};\n\nexport interface ReplEvaluationResult {\n readonly output: string;\n readonly errors: FunCityErrorInfo[];\n}\n\nexport interface ReplSession {\n evaluateLine: (line: string) => Promise<ReplEvaluationResult>;\n}\n\nexport const createReplSession = (): ReplSession => {\n const variables = buildCandidateVariables();\n const errors: FunCityErrorInfo[] = [];\n const context = createReducerContext(variables, errors);\n\n const evaluateLine = async (line: string): Promise<ReplEvaluationResult> => {\n errors.length = 0;\n const tokens = runCodeTokenizer(line, errors);\n const nodes = parseExpressions(tokens, errors);\n const results = await collectResults(context, nodes);\n const output =\n results.length > 0\n ? results.map((result) => convertToString(result)).join('\\n')\n : '';\n return {\n output,\n errors: [...errors],\n };\n };\n\n return { evaluateLine };\n};\n\nexport interface ScriptRunResult {\n readonly output: string;\n readonly errors: FunCityErrorInfo[];\n}\n\nexport const runScriptText = async (\n script: string\n): Promise<ScriptRunResult> => {\n const errors: FunCityErrorInfo[] = [];\n const variables = buildCandidateVariables();\n const tokens = runTokenizer(script, errors);\n const nodes = runParser(tokens, errors);\n const results = await runReducer(nodes, variables, errors);\n const output = results.map((result) => convertToString(result)).join('');\n return { output, errors };\n};\n\nconst runRepl = async (): Promise<void> => {\n const session = createReplSession();\n\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n prompt: promptText,\n });\n\n let bufferedLine = '';\n\n const setPrompt = (isContinuation: boolean) => {\n rl.setPrompt(isContinuation ? continuationPromptText : promptText);\n };\n\n const hasLineContinuation = (line: string): boolean => line.endsWith('\\\\');\n\n const evaluateAndPrint = async (line: string) => {\n try {\n const { output, errors } = await session.evaluateLine(line);\n if (errors.length > 0) {\n outputErrors('<repl>', errors);\n }\n if (output) {\n console.log(output);\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.error(message);\n }\n };\n\n setPrompt(false);\n rl.prompt();\n\n for await (const line of rl) {\n if (bufferedLine) {\n bufferedLine += `\\n${line}`;\n } else {\n bufferedLine = line;\n }\n\n if (hasLineContinuation(line)) {\n setPrompt(true);\n rl.prompt();\n continue;\n }\n\n const logicalLine = bufferedLine;\n bufferedLine = '';\n await evaluateAndPrint(logicalLine);\n setPrompt(false);\n rl.prompt();\n }\n\n if (bufferedLine) {\n await evaluateAndPrint(bufferedLine);\n }\n\n rl.close();\n};\n\nconst runScript = async (input: string): Promise<void> => {\n const isStdin = input === '-';\n const source = isStdin ? '<stdin>' : input;\n const script = isStdin\n ? await readStream(process.stdin)\n : await readFile(input, 'utf8');\n\n const { output, errors } = await runScriptText(script);\n const hasError = outputErrors(source, errors);\n\n if (output) {\n process.stdout.write(output);\n }\n if (hasError) {\n process.exitCode = 1;\n }\n};\n\nconst findExplicitCommand = (\n args: readonly string[]\n): 'repl' | 'run' | undefined => {\n for (let index = 0; index < args.length; index++) {\n const arg = args[index]!;\n if (arg === '--') {\n const next = args[index + 1];\n if (next === 'repl' || next === 'run') {\n return next;\n }\n return undefined;\n }\n if (arg === '-i' || arg === '--input') {\n index += 1;\n continue;\n }\n if (arg.startsWith('--input=')) {\n continue;\n }\n if (arg.startsWith('-')) {\n continue;\n }\n return arg === 'repl' || arg === 'run' ? arg : undefined;\n }\n return undefined;\n};\n\nconst hasInputOption = (args: readonly string[]): boolean => {\n for (const arg of args) {\n if (arg === '--') {\n break;\n }\n if (arg === '-i' || arg === '--input' || arg.startsWith('--input=')) {\n return true;\n }\n }\n return false;\n};\n\nconst injectDefaultCommand = (argv: string[]): string[] => {\n const args = argv.slice(2);\n const explicitCommand = findExplicitCommand(args);\n if (explicitCommand) {\n return argv;\n }\n\n const hasHelp = args.some(\n (arg) =>\n arg === '-h' || arg === '--help' || arg === '-V' || arg === '--version'\n );\n if (hasHelp) {\n return argv;\n }\n\n const command = hasInputOption(args) ? 'run' : 'repl';\n const [execPath = 'node', scriptPath = 'funcity'] = argv;\n return [execPath, scriptPath, command, ...args];\n};\n\nexport const runMain = async (argv: string[] = process.argv): Promise<void> => {\n const program = new Command();\n\n program.name(packageMetadata.name);\n program.summary(packageMetadata.description);\n program.addHelpText(\n 'beforeAll',\n `${packageMetadata.name}\\n${packageMetadata.description}\\n`\n );\n program.version(\n `${packageMetadata.version}-${packageMetadata.git_commit_hash}`\n );\n program.showHelpAfterError(true);\n\n program\n .command('repl')\n .summary('Start an interactive REPL session')\n .action(async () => {\n await runRepl();\n });\n\n program\n .command('run')\n .summary('Run a script from a file or stdin')\n .addOption(\n new Option(\n '-i, --input <path>',\n 'Input file path or \"-\" for stdin'\n ).default('-')\n )\n .action(async (options: { input: string }) => {\n await runScript(options.input);\n });\n\n await program.parseAsync(injectDefaultCommand(argv));\n};\n","// funcity - A functional language interpreter with text processing\n// Copyright (c) Kouji Matsui (@kekyo@mi.kekyo.net)\n// Under MIT.\n// https://github.com/kekyo/funcity/\n\nimport { runMain } from './cli';\n\nrunMain().catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n console.error(message);\n process.exitCode = 1;\n});\n"],"names":["packageMetadata.name","packageMetadata.description","packageMetadata.version","packageMetadata.git_commit_hash"],"mappings":"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"}
Binary file
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "git": {
3
+ "tags": [
4
+ "0.4.0"
5
+ ],
6
+ "branches": [
7
+ "main"
8
+ ],
9
+ "version": "0.4.0",
10
+ "commit": {
11
+ "hash": "7064fa018cf2e5818672d285f03a1985020da0a8",
12
+ "shortHash": "7064fa0",
13
+ "date": "2026-01-12T00:05:26+09:00",
14
+ "message": "Merge branch 'develop'"
15
+ }
16
+ },
17
+ "version": "0.4.0",
18
+ "author": "Kouji Matsui (@kekyo@mi.kekyo.net)",
19
+ "license": "MIT",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://github.com/kekyo/funcity.git"
23
+ },
24
+ "homepage": "https://github.com/kekyo/funcity#readme",
25
+ "files": [
26
+ "LICENSE",
27
+ "images/funcity.120.png",
28
+ "dist"
29
+ ],
30
+ "name": "funcity-cli",
31
+ "description": "A functional language interpreter with text processing",
32
+ "keywords": [
33
+ "functional",
34
+ "language",
35
+ "interpreter",
36
+ "text-processing",
37
+ "cli",
38
+ "repl"
39
+ ],
40
+ "type": "module",
41
+ "bin": {
42
+ "funcity": "./dist/index.cjs"
43
+ },
44
+ "main": "./dist/index.cjs",
45
+ "module": "./dist/index.js",
46
+ "types": "./dist/index.d.ts",
47
+ "exports": {
48
+ ".": {
49
+ "types": "./dist/index.d.ts",
50
+ "import": "./dist/index.js",
51
+ "require": "./dist/index.cjs"
52
+ }
53
+ },
54
+ "scripts": {
55
+ "build": "vite build",
56
+ "test": "npm run build && vitest run",
57
+ "pack": "npm run build && screw-up pack --pack-destination ../artifacts/"
58
+ },
59
+ "dependencies": {
60
+ "commander": ">=11.0.0",
61
+ "funcity": "*"
62
+ },
63
+ "devDependencies": {
64
+ "@types/node": ">=20.0.0",
65
+ "prettier-max": ">=1.13.0",
66
+ "screw-up": ">=1.19.0",
67
+ "typescript": ">=5.0.0",
68
+ "vite": ">=5.0.0",
69
+ "vite-plugin-dts": ">=3.0.0",
70
+ "vitest": ">=1.0.0"
71
+ },
72
+ "buildDate": "2026-01-12T00:06:23+09:00"
73
+ }