@hatchingpoint/point 0.0.6 → 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.
@@ -1,16 +1,16 @@
1
- import type {
2
- PointCoreDeclaration,
3
- PointCoreFunctionDeclaration,
4
- PointCoreProgram,
5
- PointCoreTypeDeclaration,
6
- PointCoreTypeExpression,
7
- PointCoreValueDeclaration,
8
- PointSourceSpan,
9
- } from "./ast.ts";
10
- import type {
11
- PointCoreDiagnostic,
12
- } from "./check.ts";
13
-
1
+ import type {
2
+ PointCoreDeclaration,
3
+ PointCoreFunctionDeclaration,
4
+ PointCoreProgram,
5
+ PointCoreTypeDeclaration,
6
+ PointCoreTypeExpression,
7
+ PointCoreValueDeclaration,
8
+ PointSourceSpan,
9
+ } from "./ast.ts";
10
+ import type {
11
+ PointCoreDiagnostic,
12
+ } from "./check.ts";
13
+
14
14
  export type PointCoreSymbolKind =
15
15
  | "module"
16
16
  | "import"
@@ -30,62 +30,62 @@ export type PointCoreSymbolKind =
30
30
  | "route"
31
31
  | "workflow"
32
32
  | "command";
33
-
34
- export interface PointCoreSymbol {
35
- ref: string;
36
- path: string;
37
- kind: PointCoreSymbolKind;
38
- name: string;
39
- module: string;
40
- type?: string;
41
- mutable?: boolean;
33
+
34
+ export interface PointCoreSymbol {
35
+ ref: string;
36
+ path: string;
37
+ kind: PointCoreSymbolKind;
38
+ name: string;
39
+ module: string;
40
+ type?: string;
41
+ mutable?: boolean;
42
42
  from?: string;
43
43
  effects?: string[];
44
44
  span: PointSourceSpan | null;
45
- }
46
-
47
- export interface PointCoreIndex {
48
- schemaVersion: "point.core.index.v1";
49
- module: string;
50
- refs: PointCoreSymbol[];
51
- }
52
-
53
- export interface PointCoreExplanation {
54
- schemaVersion: "point.core.explain.v1";
55
- ref: string;
56
- found: boolean;
57
- symbol?: PointCoreSymbol;
58
- relatedRefs: string[];
59
- summary: string;
60
- }
61
-
62
- export interface PointCoreRepairPlan {
63
- schemaVersion: "point.core.repair-plan.v1";
64
- ok: boolean;
65
- steps: PointCoreRepairStep[];
66
- }
67
-
68
- export interface PointCoreRepairStep {
69
- ref: string;
70
- code: string;
71
- message: string;
72
- repair: string;
73
- expected?: string | string[];
74
- actual?: string;
75
- relatedRefs: string[];
76
- }
77
-
45
+ }
46
+
47
+ export interface PointCoreIndex {
48
+ schemaVersion: "point.core.index.v1";
49
+ module: string;
50
+ refs: PointCoreSymbol[];
51
+ }
52
+
53
+ export interface PointCoreExplanation {
54
+ schemaVersion: "point.core.explain.v1";
55
+ ref: string;
56
+ found: boolean;
57
+ symbol?: PointCoreSymbol;
58
+ relatedRefs: string[];
59
+ summary: string;
60
+ }
61
+
62
+ export interface PointCoreRepairPlan {
63
+ schemaVersion: "point.core.repair-plan.v1";
64
+ ok: boolean;
65
+ steps: PointCoreRepairStep[];
66
+ }
67
+
68
+ export interface PointCoreRepairStep {
69
+ ref: string;
70
+ code: string;
71
+ message: string;
72
+ repair: string;
73
+ expected?: string | string[];
74
+ actual?: string;
75
+ relatedRefs: string[];
76
+ }
77
+
78
78
  export function createPointCoreIndex(program: PointCoreProgram): PointCoreIndex {
79
79
  const moduleName = program.module ?? "anonymous";
80
- const refs: PointCoreSymbol[] = [
81
- {
82
- ref: refFor(moduleName, "module"),
83
- path: "module",
84
- kind: "module",
85
- name: moduleName,
86
- module: moduleName,
87
- span: program.span ?? null,
88
- },
80
+ const refs: PointCoreSymbol[] = [
81
+ {
82
+ ref: refFor(moduleName, "module"),
83
+ path: "module",
84
+ kind: "module",
85
+ name: moduleName,
86
+ module: moduleName,
87
+ span: program.span ?? null,
88
+ },
89
89
  ];
90
90
  for (const declaration of program.declarations) {
91
91
  refs.push(...symbolsForDeclaration(moduleName, declaration));
@@ -93,44 +93,44 @@ export function createPointCoreIndex(program: PointCoreProgram): PointCoreIndex
93
93
  }
94
94
  return { schemaVersion: "point.core.index.v1", module: moduleName, refs };
95
95
  }
96
-
97
- export function explainPointCoreRef(program: PointCoreProgram, ref: string): PointCoreExplanation {
98
- const index = createPointCoreIndex(program);
99
- const symbol = index.refs.find((candidate) => candidate.ref === ref);
100
- if (!symbol) {
101
- return {
102
- schemaVersion: "point.core.explain.v1",
103
- ref,
104
- found: false,
105
- relatedRefs: [],
106
- summary: `No Point symbol found for ${ref}.`,
107
- };
108
- }
109
- const relatedRefs = relatedRefsFor(symbol, index);
110
- return {
111
- schemaVersion: "point.core.explain.v1",
112
- ref,
113
- found: true,
114
- symbol,
115
- relatedRefs,
116
- summary: summaryFor(symbol),
117
- };
118
- }
119
-
96
+
97
+ export function explainPointCoreRef(program: PointCoreProgram, ref: string): PointCoreExplanation {
98
+ const index = createPointCoreIndex(program);
99
+ const symbol = index.refs.find((candidate) => candidate.ref === ref);
100
+ if (!symbol) {
101
+ return {
102
+ schemaVersion: "point.core.explain.v1",
103
+ ref,
104
+ found: false,
105
+ relatedRefs: [],
106
+ summary: `No Point symbol found for ${ref}.`,
107
+ };
108
+ }
109
+ const relatedRefs = relatedRefsFor(symbol, index);
110
+ return {
111
+ schemaVersion: "point.core.explain.v1",
112
+ ref,
113
+ found: true,
114
+ symbol,
115
+ relatedRefs,
116
+ summary: summaryFor(symbol),
117
+ };
118
+ }
119
+
120
120
  export function createPointCoreRepairPlan(diagnostics: PointCoreDiagnostic[]): PointCoreRepairPlan {
121
- return {
122
- schemaVersion: "point.core.repair-plan.v1",
123
- ok: diagnostics.length === 0,
124
- steps: diagnostics.map((diagnostic) => ({
125
- ref: diagnostic.ref,
126
- code: diagnostic.code,
127
- message: diagnostic.message,
128
- repair: diagnostic.repair ?? "Inspect this ref and update the Point source.",
129
- expected: diagnostic.expected,
130
- actual: diagnostic.actual,
131
- relatedRefs: diagnostic.relatedRefs ?? [],
132
- })),
133
- };
121
+ return {
122
+ schemaVersion: "point.core.repair-plan.v1",
123
+ ok: diagnostics.length === 0,
124
+ steps: diagnostics.map((diagnostic) => ({
125
+ ref: diagnostic.ref,
126
+ code: diagnostic.code,
127
+ message: diagnostic.message,
128
+ repair: diagnostic.repair ?? "Inspect this ref and update the Point source.",
129
+ expected: diagnostic.expected,
130
+ actual: diagnostic.actual,
131
+ relatedRefs: diagnostic.relatedRefs ?? [],
132
+ })),
133
+ };
134
134
  }
135
135
 
136
136
  export function mapDiagnosticsToSemanticRefs(program: PointCoreProgram, diagnostics: PointCoreDiagnostic[]): PointCoreDiagnostic[] {
@@ -140,16 +140,16 @@ export function mapDiagnosticsToSemanticRefs(program: PointCoreProgram, diagnost
140
140
  relatedRefs: diagnostic.relatedRefs?.map((ref) => semanticRefForCoreRef(program, ref) ?? ref),
141
141
  }));
142
142
  }
143
-
144
- function symbolsForDeclaration(moduleName: string, declaration: PointCoreDeclaration): PointCoreSymbol[] {
143
+
144
+ function symbolsForDeclaration(moduleName: string, declaration: PointCoreDeclaration): PointCoreSymbol[] {
145
145
  if (declaration.kind === "import") {
146
146
  return declaration.names.map((name) => ({
147
- ref: refFor(moduleName, `import.${name}`),
148
- path: `import.${name}`,
149
- kind: "import",
150
- name,
151
- module: moduleName,
152
- from: declaration.from,
147
+ ref: refFor(moduleName, `import.${name}`),
148
+ path: `import.${name}`,
149
+ kind: "import",
150
+ name,
151
+ module: moduleName,
152
+ from: declaration.from,
153
153
  span: declaration.span ?? null,
154
154
  }));
155
155
  }
@@ -170,65 +170,65 @@ function symbolsForDeclaration(moduleName: string, declaration: PointCoreDeclara
170
170
  if (declaration.kind === "value") return [valueSymbol(moduleName, declaration, `value.${declaration.name}`)];
171
171
  if (declaration.kind === "type") return typeSymbols(moduleName, declaration);
172
172
  return functionSymbols(moduleName, declaration);
173
- }
174
-
175
- function valueSymbol(moduleName: string, declaration: PointCoreValueDeclaration, path: string): PointCoreSymbol {
176
- return {
177
- ref: refFor(moduleName, path),
178
- path,
179
- kind: "value",
180
- name: declaration.name,
181
- module: moduleName,
182
- type: formatType(declaration.type),
183
- mutable: declaration.mutable,
184
- span: declaration.span ?? null,
185
- };
186
- }
187
-
188
- function typeSymbols(moduleName: string, declaration: PointCoreTypeDeclaration): PointCoreSymbol[] {
189
- return [
190
- {
191
- ref: refFor(moduleName, `type.${declaration.name}`),
192
- path: `type.${declaration.name}`,
193
- kind: "type",
194
- name: declaration.name,
195
- module: moduleName,
196
- span: declaration.span ?? null,
197
- },
198
- ...declaration.fields.map((field) => ({
199
- ref: refFor(moduleName, `type.${declaration.name}.${field.name}`),
200
- path: `type.${declaration.name}.${field.name}`,
201
- kind: "field" as const,
202
- name: field.name,
203
- module: moduleName,
204
- type: formatType(field.type),
205
- span: field.span ?? null,
206
- })),
207
- ];
208
- }
209
-
173
+ }
174
+
175
+ function valueSymbol(moduleName: string, declaration: PointCoreValueDeclaration, path: string): PointCoreSymbol {
176
+ return {
177
+ ref: refFor(moduleName, path),
178
+ path,
179
+ kind: "value",
180
+ name: declaration.name,
181
+ module: moduleName,
182
+ type: formatType(declaration.type),
183
+ mutable: declaration.mutable,
184
+ span: declaration.span ?? null,
185
+ };
186
+ }
187
+
188
+ function typeSymbols(moduleName: string, declaration: PointCoreTypeDeclaration): PointCoreSymbol[] {
189
+ return [
190
+ {
191
+ ref: refFor(moduleName, `type.${declaration.name}`),
192
+ path: `type.${declaration.name}`,
193
+ kind: "type",
194
+ name: declaration.name,
195
+ module: moduleName,
196
+ span: declaration.span ?? null,
197
+ },
198
+ ...declaration.fields.map((field) => ({
199
+ ref: refFor(moduleName, `type.${declaration.name}.${field.name}`),
200
+ path: `type.${declaration.name}.${field.name}`,
201
+ kind: "field" as const,
202
+ name: field.name,
203
+ module: moduleName,
204
+ type: formatType(field.type),
205
+ span: field.span ?? null,
206
+ })),
207
+ ];
208
+ }
209
+
210
210
  function functionSymbols(moduleName: string, declaration: PointCoreFunctionDeclaration): PointCoreSymbol[] {
211
- return [
212
- {
213
- ref: refFor(moduleName, `fn.${declaration.name}`),
214
- path: `fn.${declaration.name}`,
215
- kind: "function",
216
- name: declaration.name,
217
- module: moduleName,
211
+ return [
212
+ {
213
+ ref: refFor(moduleName, `fn.${declaration.name}`),
214
+ path: `fn.${declaration.name}`,
215
+ kind: "function",
216
+ name: declaration.name,
217
+ module: moduleName,
218
218
  type: formatType(declaration.returnType),
219
219
  effects: declaration.semantic?.effects,
220
220
  span: declaration.span ?? null,
221
221
  },
222
- ...declaration.params.map((param) => ({
223
- ref: refFor(moduleName, `fn.${declaration.name}.param.${param.name}`),
224
- path: `fn.${declaration.name}.param.${param.name}`,
225
- kind: "param" as const,
226
- name: param.name,
227
- module: moduleName,
228
- type: formatType(param.type),
229
- span: param.span ?? null,
230
- })),
231
- ];
222
+ ...declaration.params.map((param) => ({
223
+ ref: refFor(moduleName, `fn.${declaration.name}.param.${param.name}`),
224
+ path: `fn.${declaration.name}.param.${param.name}`,
225
+ kind: "param" as const,
226
+ name: param.name,
227
+ module: moduleName,
228
+ type: formatType(param.type),
229
+ span: param.span ?? null,
230
+ })),
231
+ ];
232
232
  }
233
233
 
234
234
  function semanticSymbolsForDeclaration(moduleName: string, declaration: PointCoreDeclaration): PointCoreSymbol[] {
@@ -311,12 +311,12 @@ function refFor(moduleName: string, path: string): string {
311
311
  function semanticRefFor(moduleName: string, path: string): string {
312
312
  return `point://semantic/${moduleName}/${path}`;
313
313
  }
314
-
315
- function relatedRefsFor(symbol: PointCoreSymbol, index: PointCoreIndex): string[] {
316
- if (symbol.kind === "field") {
317
- const ownerPath = symbol.path.split(".").slice(0, 2).join(".");
318
- return index.refs.filter((candidate) => candidate.path.startsWith(`${ownerPath}.`) && candidate.ref !== symbol.ref).map((candidate) => candidate.ref);
319
- }
314
+
315
+ function relatedRefsFor(symbol: PointCoreSymbol, index: PointCoreIndex): string[] {
316
+ if (symbol.kind === "field") {
317
+ const ownerPath = symbol.path.split(".").slice(0, 2).join(".");
318
+ return index.refs.filter((candidate) => candidate.path.startsWith(`${ownerPath}.`) && candidate.ref !== symbol.ref).map((candidate) => candidate.ref);
319
+ }
320
320
  if (
321
321
  symbol.kind === "function" ||
322
322
  symbol.kind === "calculation" ||
@@ -333,16 +333,16 @@ function relatedRefsFor(symbol: PointCoreSymbol, index: PointCoreIndex): string[
333
333
  .filter((candidate) => candidate.path.startsWith(`${symbol.path}.param.`) || candidate.path.startsWith(`${symbol.path}.input.`))
334
334
  .map((candidate) => candidate.ref);
335
335
  }
336
- return [];
337
- }
338
-
336
+ return [];
337
+ }
338
+
339
339
  function summaryFor(symbol: PointCoreSymbol): string {
340
- if (symbol.kind === "module") return `Module ${symbol.name}.`;
341
- if (symbol.kind === "import") return `Import ${symbol.name} from ${symbol.from}.`;
342
- if (symbol.kind === "value") return `${symbol.mutable ? "Mutable" : "Immutable"} value ${symbol.name}: ${symbol.type}.`;
340
+ if (symbol.kind === "module") return `Module ${symbol.name}.`;
341
+ if (symbol.kind === "import") return `Import ${symbol.name} from ${symbol.from}.`;
342
+ if (symbol.kind === "value") return `${symbol.mutable ? "Mutable" : "Immutable"} value ${symbol.name}: ${symbol.type}.`;
343
343
  if (symbol.kind === "function") return `Function ${symbol.name} returns ${symbol.type}.`;
344
344
  if (symbol.kind === "external") return `External function ${symbol.name} from ${symbol.from} returns ${symbol.type}.`;
345
- if (symbol.kind === "param") return `Parameter ${symbol.name}: ${symbol.type}.`;
345
+ if (symbol.kind === "param") return `Parameter ${symbol.name}: ${symbol.type}.`;
346
346
  if (symbol.kind === "type") return `Named type ${symbol.name}.`;
347
347
  if (symbol.kind === "record") return `Semantic record ${symbol.name}.`;
348
348
  if (symbol.kind === "calculation") return `Semantic calculation ${symbol.name} returns ${symbol.type}.`;
@@ -387,8 +387,8 @@ function semanticRefForDiagnosticPath(program: PointCoreProgram, path: string):
387
387
  }
388
388
  return null;
389
389
  }
390
-
391
- function formatType(type: PointCoreTypeExpression): string {
392
- if (type.args.length === 0) return String(type.name);
393
- return `${type.name}<${type.args.map(formatType).join(", ")}>`;
394
- }
390
+
391
+ function formatType(type: PointCoreTypeExpression): string {
392
+ if (type.args.length === 0) return String(type.name);
393
+ return `${type.name}<${type.args.map(formatType).join(", ")}>`;
394
+ }
@@ -1,124 +1,124 @@
1
- import type {
2
- PointCoreDeclaration,
3
- PointCoreExpression,
4
- PointCoreFunctionDeclaration,
5
- PointCoreParameter,
6
- PointCoreProgram,
7
- PointCoreStatement,
8
- PointCoreTypeDeclaration,
9
- PointCoreValueDeclaration,
10
- } from "./ast.ts";
11
-
12
- const BINARY_OPERATORS: Record<string, string> = {
13
- and: "&&",
14
- or: "||",
15
- };
16
-
17
- /** Emit JavaScript from a core AST program (no type syntax). Production path: parsePointSource → check → emit. */
18
- export function emitPointCoreJavaScript(program: PointCoreProgram): string {
19
- const lines: string[] = [];
20
- lines.push("// Generated by Point. Do not edit directly.");
21
- if (program.module) lines.push(`// Point module: ${program.module}`);
22
- lines.push("");
23
- for (const declaration of program.declarations) {
24
- lines.push(...emitDeclaration(declaration), "");
25
- }
26
- return `${trimTrailingBlankLines(lines).join("\n")}\n`;
27
- }
28
-
29
- function emitDeclaration(declaration: PointCoreDeclaration): string[] {
30
- if (declaration.kind === "import") {
31
- return [`import { ${declaration.names.join(", ")} } from ${JSON.stringify(declaration.from)};`];
32
- }
33
- if (declaration.kind === "external") {
34
- const imported = declaration.importName ? `${declaration.importName} as ${declaration.name}` : declaration.name;
35
- return [`import { ${imported} } from ${JSON.stringify(declaration.from)};`];
36
- }
37
- if (declaration.kind === "type") return [];
38
- if (declaration.kind === "value") return [emitValue(declaration, true)];
39
- return emitFunction(declaration);
40
- }
41
-
42
- function emitFunction(declaration: PointCoreFunctionDeclaration): string[] {
43
- const asyncPrefix = declaration.semantic?.kind === "action" || declaration.semantic?.kind === "workflow" || declaration.semantic?.kind === "command" ? "async " : "";
44
- return [
45
- `export ${asyncPrefix}function ${declaration.name}(${declaration.params.map(emitParam).join(", ")}) {`,
46
- ...indentLines(declaration.body.flatMap((statement) => emitStatement(statement, declaration.semantic?.kind))),
47
- "}",
48
- ];
49
- }
50
-
51
- function emitStatement(statement: PointCoreStatement, semanticKind?: string): string[] {
52
- if (statement.kind === "return") {
53
- if (semanticKind === "view" && statement.value?.kind === "literal" && typeof statement.value.value === "string") {
54
- return [`return ${JSON.stringify(statement.value.value)};`];
55
- }
56
- return [statement.value ? `return ${emitExpression(statement.value)};` : "return;"];
57
- }
58
- if (statement.kind === "value") return [emitValue(statement, false)];
59
- if (statement.kind === "assignment") return [`${statement.name} ${statement.operator} ${emitExpression(statement.value)};`];
60
- if (statement.kind === "if") {
61
- const lines = [
62
- `if (${emitCondition(statement.condition)}) {`,
63
- ...indentLines(statement.thenBody.flatMap((child) => emitStatement(child, semanticKind))),
64
- "}",
65
- ];
66
- if (statement.elseBody.length > 0) {
67
- lines.push(
68
- "else {",
69
- ...indentLines(statement.elseBody.flatMap((child) => emitStatement(child, semanticKind))),
70
- "}",
71
- );
72
- }
73
- return lines;
74
- }
75
- if (statement.kind === "for") {
76
- return [
77
- `for (const ${statement.itemName} of ${emitExpression(statement.iterable)}) {`,
78
- ...indentLines(statement.body.flatMap((child) => emitStatement(child, semanticKind))),
79
- "}",
80
- ];
81
- }
82
- return [`${emitExpression(statement.value)};`];
83
- }
84
-
85
- function emitValue(declaration: PointCoreValueDeclaration, exported: boolean): string {
86
- const prefix = exported ? "export " : "";
87
- const keyword = declaration.mutable ? "let" : "const";
88
- return `${prefix}${keyword} ${declaration.name} = ${emitExpression(declaration.value)};`;
89
- }
90
-
91
- function emitParam(param: PointCoreParameter): string {
92
- return param.name;
93
- }
94
-
95
- function emitExpression(expression: PointCoreExpression): string {
96
- if (expression.kind === "literal") return JSON.stringify(expression.value);
97
- if (expression.kind === "identifier") return expression.name;
98
- if (expression.kind === "list") return `[${expression.items.map(emitExpression).join(", ")}]`;
99
- if (expression.kind === "record") {
100
- return `{ ${expression.fields.map((field) => `${field.name}: ${emitExpression(field.value)}`).join(", ")} }`;
101
- }
102
- if (expression.kind === "await") return `await ${emitExpression(expression.value)}`;
103
- if (expression.kind === "property") return `${emitExpression(expression.target)}.${expression.name}`;
104
- if (expression.kind === "call") {
105
- if (expression.callee === "Error") return `{ message: ${expression.args[0] ? emitExpression(expression.args[0]) : JSON.stringify("")} }`;
106
- return `${expression.callee}(${expression.args.map(emitExpression).join(", ")})`;
107
- }
108
- const operator = BINARY_OPERATORS[expression.operator] ?? expression.operator;
109
- return `(${emitExpression(expression.left)} ${operator} ${emitExpression(expression.right)})`;
110
- }
111
-
112
- function emitCondition(expression: PointCoreExpression): string {
113
- const emitted = emitExpression(expression);
114
- return emitted.startsWith("(") && emitted.endsWith(")") ? emitted.slice(1, -1) : emitted;
115
- }
116
-
117
- function indentLines(lines: string[]): string[] {
118
- return lines.map((line) => ` ${line}`);
119
- }
120
-
121
- function trimTrailingBlankLines(lines: string[]): string[] {
122
- while (lines.at(-1) === "") lines.pop();
123
- return lines;
124
- }
1
+ import type {
2
+ PointCoreDeclaration,
3
+ PointCoreExpression,
4
+ PointCoreFunctionDeclaration,
5
+ PointCoreParameter,
6
+ PointCoreProgram,
7
+ PointCoreStatement,
8
+ PointCoreTypeDeclaration,
9
+ PointCoreValueDeclaration,
10
+ } from "./ast.ts";
11
+
12
+ const BINARY_OPERATORS: Record<string, string> = {
13
+ and: "&&",
14
+ or: "||",
15
+ };
16
+
17
+ /** Emit JavaScript from a core AST program (no type syntax). Production path: parsePointSource → check → emit. */
18
+ export function emitPointCoreJavaScript(program: PointCoreProgram): string {
19
+ const lines: string[] = [];
20
+ lines.push("// Generated by Point. Do not edit directly.");
21
+ if (program.module) lines.push(`// Point module: ${program.module}`);
22
+ lines.push("");
23
+ for (const declaration of program.declarations) {
24
+ lines.push(...emitDeclaration(declaration), "");
25
+ }
26
+ return `${trimTrailingBlankLines(lines).join("\n")}\n`;
27
+ }
28
+
29
+ function emitDeclaration(declaration: PointCoreDeclaration): string[] {
30
+ if (declaration.kind === "import") {
31
+ return [`import { ${declaration.names.join(", ")} } from ${JSON.stringify(declaration.from)};`];
32
+ }
33
+ if (declaration.kind === "external") {
34
+ const imported = declaration.importName ? `${declaration.importName} as ${declaration.name}` : declaration.name;
35
+ return [`import { ${imported} } from ${JSON.stringify(declaration.from)};`];
36
+ }
37
+ if (declaration.kind === "type") return [];
38
+ if (declaration.kind === "value") return [emitValue(declaration, true)];
39
+ return emitFunction(declaration);
40
+ }
41
+
42
+ function emitFunction(declaration: PointCoreFunctionDeclaration): string[] {
43
+ const asyncPrefix = declaration.semantic?.kind === "action" || declaration.semantic?.kind === "workflow" || declaration.semantic?.kind === "command" ? "async " : "";
44
+ return [
45
+ `export ${asyncPrefix}function ${declaration.name}(${declaration.params.map(emitParam).join(", ")}) {`,
46
+ ...indentLines(declaration.body.flatMap((statement) => emitStatement(statement, declaration.semantic?.kind))),
47
+ "}",
48
+ ];
49
+ }
50
+
51
+ function emitStatement(statement: PointCoreStatement, semanticKind?: string): string[] {
52
+ if (statement.kind === "return") {
53
+ if (semanticKind === "view" && statement.value?.kind === "literal" && typeof statement.value.value === "string") {
54
+ return [`return ${JSON.stringify(statement.value.value)};`];
55
+ }
56
+ return [statement.value ? `return ${emitExpression(statement.value)};` : "return;"];
57
+ }
58
+ if (statement.kind === "value") return [emitValue(statement, false)];
59
+ if (statement.kind === "assignment") return [`${statement.name} ${statement.operator} ${emitExpression(statement.value)};`];
60
+ if (statement.kind === "if") {
61
+ const lines = [
62
+ `if (${emitCondition(statement.condition)}) {`,
63
+ ...indentLines(statement.thenBody.flatMap((child) => emitStatement(child, semanticKind))),
64
+ "}",
65
+ ];
66
+ if (statement.elseBody.length > 0) {
67
+ lines.push(
68
+ "else {",
69
+ ...indentLines(statement.elseBody.flatMap((child) => emitStatement(child, semanticKind))),
70
+ "}",
71
+ );
72
+ }
73
+ return lines;
74
+ }
75
+ if (statement.kind === "for") {
76
+ return [
77
+ `for (const ${statement.itemName} of ${emitExpression(statement.iterable)}) {`,
78
+ ...indentLines(statement.body.flatMap((child) => emitStatement(child, semanticKind))),
79
+ "}",
80
+ ];
81
+ }
82
+ return [`${emitExpression(statement.value)};`];
83
+ }
84
+
85
+ function emitValue(declaration: PointCoreValueDeclaration, exported: boolean): string {
86
+ const prefix = exported ? "export " : "";
87
+ const keyword = declaration.mutable ? "let" : "const";
88
+ return `${prefix}${keyword} ${declaration.name} = ${emitExpression(declaration.value)};`;
89
+ }
90
+
91
+ function emitParam(param: PointCoreParameter): string {
92
+ return param.name;
93
+ }
94
+
95
+ function emitExpression(expression: PointCoreExpression): string {
96
+ if (expression.kind === "literal") return JSON.stringify(expression.value);
97
+ if (expression.kind === "identifier") return expression.name;
98
+ if (expression.kind === "list") return `[${expression.items.map(emitExpression).join(", ")}]`;
99
+ if (expression.kind === "record") {
100
+ return `{ ${expression.fields.map((field) => `${field.name}: ${emitExpression(field.value)}`).join(", ")} }`;
101
+ }
102
+ if (expression.kind === "await") return `await ${emitExpression(expression.value)}`;
103
+ if (expression.kind === "property") return `${emitExpression(expression.target)}.${expression.name}`;
104
+ if (expression.kind === "call") {
105
+ if (expression.callee === "Error") return `{ message: ${expression.args[0] ? emitExpression(expression.args[0]) : JSON.stringify("")} }`;
106
+ return `${expression.callee}(${expression.args.map(emitExpression).join(", ")})`;
107
+ }
108
+ const operator = BINARY_OPERATORS[expression.operator] ?? expression.operator;
109
+ return `(${emitExpression(expression.left)} ${operator} ${emitExpression(expression.right)})`;
110
+ }
111
+
112
+ function emitCondition(expression: PointCoreExpression): string {
113
+ const emitted = emitExpression(expression);
114
+ return emitted.startsWith("(") && emitted.endsWith(")") ? emitted.slice(1, -1) : emitted;
115
+ }
116
+
117
+ function indentLines(lines: string[]): string[] {
118
+ return lines.map((line) => ` ${line}`);
119
+ }
120
+
121
+ function trimTrailingBlankLines(lines: string[]): string[] {
122
+ while (lines.at(-1) === "") lines.pop();
123
+ return lines;
124
+ }