agency-lang 0.0.5 → 0.0.7

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.
@@ -43,5 +43,6 @@ export declare class AgencyGenerator extends BaseGenerator {
43
43
  protected processGraphNode(node: GraphNodeDefinition): string;
44
44
  protected processTool(node: FunctionDefinition): string;
45
45
  protected processUsesTool(node: UsesTool): string;
46
+ private indentStr;
46
47
  }
47
48
  export declare function generateAgency(program: AgencyProgram): string;
@@ -32,17 +32,17 @@ export class AgencyGenerator extends BaseGenerator {
32
32
  processTypeAlias(node) {
33
33
  this.typeAliases[node.aliasName] = node.aliasedType;
34
34
  const aliasedTypeStr = variableTypeToString(node.aliasedType, this.typeAliases);
35
- return `${this.indent()}type ${node.aliasName} = ${aliasedTypeStr}\n`;
35
+ return this.indentStr(`type ${node.aliasName} = ${aliasedTypeStr}\n`);
36
36
  }
37
37
  processTypeHint(node) {
38
38
  this.typeHints[node.variableName] = node.variableType;
39
39
  const typeStr = variableTypeToString(node.variableType, this.typeAliases);
40
- return `${this.indent()}${node.variableName} :: ${typeStr}\n`;
40
+ return this.indentStr(`${node.variableName} :: ${typeStr}\n`);
41
41
  }
42
42
  // Assignment and literals
43
43
  processAssignment(node) {
44
44
  const valueCode = this.processNode(node.value).trim();
45
- return `${this.indent()}${node.variableName} = ${valueCode}\n`;
45
+ return this.indentStr(`${node.variableName} = ${valueCode}\n\n`);
46
46
  }
47
47
  generateLiteral(literal) {
48
48
  switch (literal.type) {
@@ -86,7 +86,7 @@ export class AgencyGenerator extends BaseGenerator {
86
86
  // Build parameter list
87
87
  const params = parameters.join(", ");
88
88
  // Start function definition
89
- let result = `${this.indent()}def ${functionName}(${params}) {\n`;
89
+ let result = this.indentStr(`def ${functionName}(${params}) {\n`);
90
90
  // Process body with increased indentation
91
91
  this.increaseIndent();
92
92
  this.functionScopedVariables = [...parameters];
@@ -96,12 +96,12 @@ export class AgencyGenerator extends BaseGenerator {
96
96
  this.functionScopedVariables = [];
97
97
  this.decreaseIndent();
98
98
  // Close function
99
- result += `${this.indent()}}\n`;
99
+ result += this.indentStr(`}\n\n`);
100
100
  return result;
101
101
  }
102
102
  processFunctionCall(node) {
103
103
  const expr = this.generateFunctionCallExpression(node);
104
- return `${this.indent()}${expr}\n`;
104
+ return this.indentStr(`${expr}\n`);
105
105
  }
106
106
  generateFunctionCallExpression(node) {
107
107
  const args = node.arguments.map((arg) => {
@@ -151,7 +151,7 @@ export class AgencyGenerator extends BaseGenerator {
151
151
  // Control flow
152
152
  processMatchBlock(node) {
153
153
  const exprCode = this.processNode(node.expression).trim();
154
- let result = `${this.indent()}match(${exprCode}) {\n`;
154
+ let result = this.indentStr(`match(${exprCode}) {\n`);
155
155
  this.increaseIndent();
156
156
  for (const caseNode of node.cases) {
157
157
  // Handle comments within cases
@@ -165,39 +165,39 @@ export class AgencyGenerator extends BaseGenerator {
165
165
  : this.processNode(caseNode.caseValue).trim();
166
166
  // Format body (action)
167
167
  const bodyCode = this.processNode(caseNode.body).trim();
168
- result += `${this.indent()}${pattern} => ${bodyCode}\n`;
168
+ result += this.indentStr(`${pattern} => ${bodyCode}\n`);
169
169
  }
170
170
  this.decreaseIndent();
171
- result += `${this.indent()}}\n`;
171
+ result += this.indentStr(`}\n`);
172
172
  return result;
173
173
  }
174
174
  processWhileLoop(node) {
175
175
  const conditionCode = this.processNode(node.condition).trim();
176
- let result = `${this.indent()}while (${conditionCode}) {\n`;
176
+ let result = this.indentStr(`while (${conditionCode}) {\n`);
177
177
  this.increaseIndent();
178
178
  for (const stmt of node.body) {
179
179
  result += this.processNode(stmt);
180
180
  }
181
181
  this.decreaseIndent();
182
- result += `${this.indent()}}\n`;
182
+ result += this.indentStr(`}\n`);
183
183
  return result;
184
184
  }
185
185
  processReturnStatement(node) {
186
186
  const valueCode = this.processNode(node.value).trim();
187
- return `${this.indent()}return ${valueCode}\n`;
187
+ return this.indentStr(`return ${valueCode}\n`);
188
188
  }
189
189
  // Utility methods
190
190
  processComment(node) {
191
- return `${this.indent()}// ${node.content}\n`;
191
+ return this.indentStr(`// ${node.content}\n`);
192
192
  }
193
193
  processImportStatement(node) {
194
- return `${this.indent()}import {${node.importedNames}} from "${node.modulePath}"\n`;
194
+ return this.indentStr(`import {${node.importedNames}} from "${node.modulePath}"\n`);
195
195
  }
196
196
  processGraphNode(node) {
197
197
  // Graph nodes use similar syntax to functions
198
198
  const { nodeName, body, parameters } = node;
199
199
  const params = parameters.join(", ");
200
- let result = `${this.indent()}node ${nodeName}(${params}) {\n`;
200
+ let result = this.indentStr(`node ${nodeName}(${params}) {\n`);
201
201
  this.increaseIndent();
202
202
  this.functionScopedVariables = [...parameters];
203
203
  for (const stmt of body) {
@@ -205,7 +205,7 @@ export class AgencyGenerator extends BaseGenerator {
205
205
  }
206
206
  this.functionScopedVariables = [];
207
207
  this.decreaseIndent();
208
- result += `${this.indent()}}\n`;
208
+ result += this.indentStr(`}\n`);
209
209
  return result;
210
210
  }
211
211
  processTool(node) {
@@ -216,7 +216,10 @@ export class AgencyGenerator extends BaseGenerator {
216
216
  processUsesTool(node) {
217
217
  // Track tool usage but don't generate code
218
218
  this.toolsUsed.push(node.toolName);
219
- return "";
219
+ return this.indentStr(`+${node.toolName}\n`);
220
+ }
221
+ indentStr(str) {
222
+ return `${this.indent()}${str}`;
220
223
  }
221
224
  }
222
225
  export function generateAgency(program) {
@@ -4,4 +4,4 @@ export declare const agencyNode: Parser<AgencyNode[]>;
4
4
  export declare const agencyParser: Parser<AgencyProgram>;
5
5
  export declare const _multilineCommentParser: Parser<string[]>;
6
6
  export declare const multilineCommentParser: Parser<string[][]>;
7
- export declare function parseAgency(input: string): ParserResult<AgencyProgram>;
7
+ export declare function parseAgency(input: string, verbose?: boolean): ParserResult<AgencyProgram>;
@@ -9,6 +9,7 @@ import { importStatmentParser } from "./parsers/importStatement.js";
9
9
  import { matchBlockParser } from "./parsers/matchBlock.js";
10
10
  import { returnStatementParser } from "./parsers/returnStatement.js";
11
11
  import { usesToolParser } from "./parsers/tools.js";
12
+ import { EgonLog } from "egonlog";
12
13
  export const agencyNode = (input) => {
13
14
  const parser = sepBy(spaces, trace("agencyParser", or(usesToolParser, importStatmentParser, graphNodeParser, typeAliasParser, whileLoopParser, typeHintParser, matchBlockParser, functionParser, returnStatementParser, accessExpressionParser, assignmentParser, functionCallParser, commentParser)));
14
15
  return parser(input);
@@ -16,9 +17,15 @@ export const agencyNode = (input) => {
16
17
  export const agencyParser = seqC(set("type", "agencyProgram"), capture(agencyNode, "nodes"), eof);
17
18
  export const _multilineCommentParser = between(str("/*"), str("*/"), anyChar);
18
19
  export const multilineCommentParser = search(_multilineCommentParser);
19
- export function parseAgency(input) {
20
+ export function parseAgency(input, verbose = false) {
21
+ const logger = new EgonLog({ level: verbose ? "debug" : "warn" });
20
22
  let normalized = input;
23
+ logger.debug("Starting to parse agency program");
24
+ logger.debug(`Input: ${input}`);
25
+ logger.debug("================================");
21
26
  const comments = multilineCommentParser(normalized);
27
+ logger.debug(`Multiline comments: ${JSON.stringify(comments)}`);
28
+ logger.debug("================================");
22
29
  // get rid of all multiline comments
23
30
  normalized = comments.rest
24
31
  .split("\n")
@@ -33,6 +40,8 @@ export function parseAgency(input) {
33
40
  nodes: [],
34
41
  }, "");
35
42
  }
43
+ logger.debug(`Normalized input: ${normalized}`);
44
+ logger.debug("================================");
36
45
  const result = agencyParser(normalized);
37
46
  return result;
38
47
  }
@@ -44,7 +44,7 @@ describe("importStatmentParser", () => {
44
44
  result: {
45
45
  type: "importStatement",
46
46
  importedNames: "foo ",
47
- modulePath: '"./local/path"',
47
+ modulePath: '"./local/path.js"',
48
48
  },
49
49
  },
50
50
  },
@@ -55,7 +55,7 @@ describe("importStatmentParser", () => {
55
55
  result: {
56
56
  type: "importStatement",
57
57
  importedNames: "bar ",
58
- modulePath: '"@/utils/helper"',
58
+ modulePath: '"../utils/helper.js"',
59
59
  },
60
60
  },
61
61
  },
@@ -89,7 +89,7 @@ describe("importStatmentParser", () => {
89
89
  result: {
90
90
  type: "importStatement",
91
91
  importedNames: "test ",
92
- modulePath: "'./module'",
92
+ modulePath: "'./module.js'",
93
93
  },
94
94
  },
95
95
  },
@@ -240,7 +240,7 @@ describe("importStatmentParser", () => {
240
240
  result: {
241
241
  type: "importStatement",
242
242
  importedNames: "foo ",
243
- modulePath: '"../../../utils/helpers"',
243
+ modulePath: '"../../../utils/helpers.js"',
244
244
  },
245
245
  },
246
246
  },
@@ -7,7 +7,7 @@ export declare const angleBracketsArrayTypeParser: Parser<ArrayType>;
7
7
  export declare const stringLiteralTypeParser: Parser<StringLiteralType>;
8
8
  export declare const numberLiteralTypeParser: Parser<NumberLiteralType>;
9
9
  export declare const booleanLiteralTypeParser: Parser<BooleanLiteralType>;
10
- export declare const objectPropertyDelimiter: Parser<(string[] | ";")[]>;
10
+ export declare const objectPropertyDelimiter: Parser<"\n" | (string | string[])[]>;
11
11
  export declare const objectPropertyParser: Parser<ObjectProperty>;
12
12
  export declare const objectPropertyDescriptionParser: Parser<{
13
13
  description: string;
@@ -1,5 +1,5 @@
1
1
  import { optionalSpaces, varNameChar } from "./utils.js";
2
- import { capture, captureCaptures, char, count, digit, many1Till, many1WithJoin, or, sepBy, seqC, seqR, set, space, spaces, str, trace, } from "tarsec";
2
+ import { capture, captureCaptures, char, count, digit, many1Till, many1WithJoin, oneOf, or, sepBy, seqC, seqR, set, space, spaces, str, trace, } from "tarsec";
3
3
  import { optionalSemicolon } from "./parserUtils.js";
4
4
  export const primitiveTypeParser = trace("primitiveTypeParser", seqC(set("type", "primitiveType"), capture(or(str("number"), str("string"), str("boolean")), "value")));
5
5
  export const typeAliasVariableParser = trace("typeAliasVariableParser", seqC(set("type", "typeAliasVariable"), capture(many1WithJoin(varNameChar), "aliasName")));
@@ -27,12 +27,12 @@ export const angleBracketsArrayTypeParser = trace("angleBracketsArrayTypeParser"
27
27
  export const stringLiteralTypeParser = trace("stringLiteralTypeParser", seqC(set("type", "stringLiteralType"), char('"'), capture(many1Till(char('"')), "value"), char('"')));
28
28
  export const numberLiteralTypeParser = trace("numberLiteralTypeParser", seqC(set("type", "numberLiteralType"), capture(many1WithJoin(or(char("-"), char("."), digit)), "value")));
29
29
  export const booleanLiteralTypeParser = trace("booleanLiteralTypeParser", seqC(set("type", "booleanLiteralType"), capture(or(str("true"), str("false")), "value")));
30
- export const objectPropertyDelimiter = seqR(optionalSpaces, char(";"), optionalSpaces);
30
+ export const objectPropertyDelimiter = or(char("\n"), seqR(optionalSpaces, oneOf(",;"), optionalSpaces));
31
31
  export const objectPropertyParser = trace("objectPropertyParser", (input) => {
32
32
  const parser = seqC(capture(many1WithJoin(varNameChar), "key"), optionalSpaces, char(":"), optionalSpaces, capture(variableTypeParser, "value"));
33
33
  return parser(input);
34
34
  });
35
- export const objectPropertyDescriptionParser = seqC(char("#"), optionalSpaces, capture(many1Till(char(";")), "description"));
35
+ export const objectPropertyDescriptionParser = seqC(char("#"), optionalSpaces, capture(many1Till(oneOf(",;\n")), "description"));
36
36
  export const objectPropertyWithDescriptionParser = trace("objectPropertyWithDescriptionParser", seqC(captureCaptures(objectPropertyParser), spaces, captureCaptures(objectPropertyDescriptionParser)));
37
37
  export const objectTypeParser = trace("objectTypeParser", (input) => {
38
38
  const parser = seqC(set("type", "objectType"), char("{"), optionalSpaces, capture(sepBy(objectPropertyDelimiter, or(objectPropertyWithDescriptionParser, objectPropertyParser)), "properties"), optionalSpaces, char("}"));
@@ -1012,6 +1012,24 @@ describe("objectPropertyDescriptionParser", () => {
1012
1012
  },
1013
1013
  },
1014
1014
  },
1015
+ {
1016
+ input: "# comma,",
1017
+ expected: {
1018
+ success: true,
1019
+ result: {
1020
+ description: "comma",
1021
+ },
1022
+ },
1023
+ },
1024
+ {
1025
+ input: "# newline",
1026
+ expected: {
1027
+ success: true,
1028
+ result: {
1029
+ description: "newline",
1030
+ },
1031
+ },
1032
+ },
1015
1033
  {
1016
1034
  input: "# with extra spaces ;",
1017
1035
  expected: {
@@ -1022,11 +1040,11 @@ describe("objectPropertyDescriptionParser", () => {
1022
1040
  },
1023
1041
  },
1024
1042
  {
1025
- input: "# description with, punctuation!;",
1043
+ input: "# description with punctuation!;",
1026
1044
  expected: {
1027
1045
  success: true,
1028
1046
  result: {
1029
- description: "description with, punctuation!",
1047
+ description: "description with punctuation!",
1030
1048
  },
1031
1049
  },
1032
1050
  },
@@ -12,40 +12,69 @@ Usage:
12
12
  agency help Show this help message
13
13
  agency compile <input> [output] Compile .agency file to TypeScript
14
14
  agency run <input> [output] Compile and run .agency file
15
- agency format <input> Format .agency file in place
15
+ agency format [input] Format .agency file (reads from stdin if no input)
16
+ agency parse [input] Parse .agency file and show AST (reads from stdin if no input)
16
17
  agency <input> Compile and run .agency file (shorthand)
17
18
 
18
19
  Arguments:
19
- input Path to .agency input file
20
+ input Path to .agency input file (or omit to read from stdin for format/parse)
20
21
  output Path to output .ts file (optional)
21
22
  Default: <input-name>.ts
22
23
 
24
+ Flags:
25
+ -v, --verbose Enable verbose logging during parsing
26
+
23
27
  Examples:
24
28
  agency help Show help
25
29
  agency compile script.agency Compile to script.ts
26
30
  agency compile script.agency out.ts Compile to out.ts
27
31
  agency run script.agency Compile and run script.agency
32
+ agency -v parse script.agency Parse with verbose logging
33
+ cat script.agency | agency format Format from stdin
34
+ echo "x = 5" | agency parse Parse from stdin
28
35
  agency script.agency Compile and run (shorthand)
29
36
  `);
30
37
  }
31
- function compile(inputFile, outputFile) {
38
+ function readStdin() {
39
+ return new Promise((resolve, reject) => {
40
+ let data = "";
41
+ process.stdin.setEncoding("utf-8");
42
+ process.stdin.on("data", (chunk) => {
43
+ data += chunk;
44
+ });
45
+ process.stdin.on("end", () => {
46
+ resolve(data);
47
+ });
48
+ process.stdin.on("error", (err) => {
49
+ reject(err);
50
+ });
51
+ });
52
+ }
53
+ function parse(contents, verbose = false) {
54
+ const parseResult = parseAgency(contents, verbose);
55
+ // Check if parsing was successful
56
+ if (!parseResult.success) {
57
+ console.error("Parse error:");
58
+ console.error(parseResult);
59
+ process.exit(1);
60
+ }
61
+ return parseResult.result;
62
+ }
63
+ function readFile(inputFile) {
32
64
  // Validate input file
33
65
  if (!fs.existsSync(inputFile)) {
34
66
  console.error(`Error: Input file '${inputFile}' not found`);
35
67
  process.exit(1);
36
68
  }
37
- // Determine output file name
38
- const output = outputFile || inputFile.replace(".agency", ".ts");
39
69
  // Read and parse the Agency file
40
70
  const contents = fs.readFileSync(inputFile, "utf-8");
41
- const parseResult = parseAgency(contents);
42
- // Check if parsing was successful
43
- if (!parseResult.success) {
44
- console.error("Parse error:");
45
- console.error(parseResult);
46
- process.exit(1);
47
- }
48
- const parsedProgram = parseResult.result;
71
+ return contents;
72
+ }
73
+ function compile(inputFile, outputFile, verbose = false) {
74
+ // Determine output file name
75
+ const output = outputFile || inputFile.replace(".agency", ".ts");
76
+ const contents = readFile(inputFile);
77
+ const parsedProgram = parse(contents, verbose);
49
78
  // Generate TypeScript code
50
79
  const generatedCode = generateGraph(parsedProgram);
51
80
  // Write to output file
@@ -53,9 +82,9 @@ function compile(inputFile, outputFile) {
53
82
  console.log(`Generated ${output} from ${inputFile}`);
54
83
  return output;
55
84
  }
56
- function run(inputFile, outputFile) {
85
+ function run(inputFile, outputFile, verbose = false) {
57
86
  // Compile the file
58
- const output = compile(inputFile, outputFile);
87
+ const output = compile(inputFile, outputFile, verbose);
59
88
  // Run the generated TypeScript file with Node.js
60
89
  console.log(`Running ${output}...`);
61
90
  console.log("---");
@@ -73,39 +102,26 @@ function run(inputFile, outputFile) {
73
102
  }
74
103
  });
75
104
  }
76
- function format(inputFile) {
77
- // Validate input file
78
- if (!fs.existsSync(inputFile)) {
79
- console.error(`Error: Input file '${inputFile}' not found`);
80
- process.exit(1);
81
- }
82
- // Read and parse the Agency file
83
- const contents = fs.readFileSync(inputFile, "utf-8");
84
- const parseResult = parseAgency(contents);
85
- // Check if parsing was successful
86
- if (!parseResult.success) {
87
- console.error("Parse error:");
88
- console.error(parseResult);
89
- process.exit(1);
90
- }
91
- const parsedProgram = parseResult.result;
92
- // Generate TypeScript code
105
+ async function format(contents, verbose = false) {
106
+ const parsedProgram = parse(contents, verbose);
93
107
  const generatedCode = generateAgency(parsedProgram);
94
- // Write to output file
95
- //fs.writeFileSync(inputFile, generatedCode, "utf-8");
96
- // console.log(`Generated ${output} from ${inputFile}`);
97
108
  console.log(generatedCode);
98
109
  return generatedCode;
99
110
  }
100
111
  // Main CLI logic
101
- function main() {
112
+ async function main() {
102
113
  const args = process.argv.slice(2);
103
114
  // No arguments - show help
104
115
  if (args.length === 0) {
105
116
  help();
106
117
  return;
107
118
  }
108
- const command = args[0];
119
+ // Extract verbose flag
120
+ const verboseIndex = args.findIndex((arg) => arg === "-v" || arg === "--verbose");
121
+ const verbose = verboseIndex !== -1;
122
+ // Remove verbose flag from args
123
+ const filteredArgs = args.filter((arg) => arg !== "-v" && arg !== "--verbose");
124
+ const command = filteredArgs[0];
109
125
  switch (command) {
110
126
  case "help":
111
127
  case "--help":
@@ -113,34 +129,47 @@ function main() {
113
129
  help();
114
130
  break;
115
131
  case "compile":
116
- if (args.length < 2) {
132
+ if (filteredArgs.length < 2) {
117
133
  console.error("Error: 'compile' command requires an input file");
118
134
  console.error("Usage: agency compile <input> [output]");
119
135
  process.exit(1);
120
136
  }
121
- compile(args[1], args[2]);
137
+ compile(filteredArgs[1], filteredArgs[2], verbose);
122
138
  break;
123
139
  case "run":
124
- if (args.length < 2) {
140
+ if (filteredArgs.length < 2) {
125
141
  console.error("Error: 'run' command requires an input file");
126
142
  console.error("Usage: agency run <input> [output]");
127
143
  process.exit(1);
128
144
  }
129
- run(args[1], args[2]);
145
+ run(filteredArgs[1], filteredArgs[2], verbose);
130
146
  break;
131
147
  case "fmt":
132
148
  case "format":
133
- if (args.length < 1) {
134
- console.error("Error: 'format' command requires an input file");
135
- console.error("Usage: agency format <input>");
136
- process.exit(1);
149
+ let fmtContents;
150
+ if (filteredArgs.length < 2) {
151
+ fmtContents = await readStdin();
152
+ }
153
+ else {
154
+ fmtContents = readFile(filteredArgs[1]);
155
+ }
156
+ format(fmtContents, verbose);
157
+ break;
158
+ case "parse":
159
+ let contents;
160
+ if (filteredArgs.length < 2) {
161
+ contents = await readStdin();
162
+ }
163
+ else {
164
+ contents = readFile(filteredArgs[1]);
137
165
  }
138
- format(args[1]);
166
+ const result = parse(contents, verbose);
167
+ console.log(JSON.stringify(result, null, 2));
139
168
  break;
140
169
  default:
141
170
  // If first arg is not a recognized command, treat it as a file to run
142
171
  if (command.endsWith(".agency") || fs.existsSync(command)) {
143
- run(command, args[1]);
172
+ run(command, filteredArgs[1], verbose);
144
173
  }
145
174
  else {
146
175
  console.error(`Error: Unknown command '${command}'`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agency-lang",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "description": "The Agency language",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -39,6 +39,7 @@
39
39
  },
40
40
  "homepage": "https://github.com/egonSchiele/agency-lang#readme",
41
41
  "dependencies": {
42
+ "egonlog": "^0.0.2",
42
43
  "nanoid": "^5.1.6",
43
44
  "openai": "^6.15.0",
44
45
  "simplemachine": "github:egonSchiele/simplemachine",