@venn-lang/core 0.1.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.
Files changed (168) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +303 -0
  3. package/dist/index.d.ts +3654 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +13137 -0
  6. package/dist/index.js.map +1 -0
  7. package/package.json +61 -0
  8. package/src/ast/call-args.ts +15 -0
  9. package/src/ast/dotted-path.ts +22 -0
  10. package/src/ast/index.ts +7 -0
  11. package/src/ast/is-statement.ts +40 -0
  12. package/src/ast/split-call.ts +28 -0
  13. package/src/ast/walk.ts +6 -0
  14. package/src/codes/build-problem.ts +24 -0
  15. package/src/codes/catalog.ts +35 -0
  16. package/src/codes/code.types.ts +7 -0
  17. package/src/codes/index.ts +3 -0
  18. package/src/compile/compile.ts +169 -0
  19. package/src/compile/compile.types.ts +40 -0
  20. package/src/compile/index.ts +3 -0
  21. package/src/compile/lex-scope.ts +78 -0
  22. package/src/compile/nodes/binary.ts +56 -0
  23. package/src/compile/nodes/call.ts +50 -0
  24. package/src/compile/nodes/collection.ts +83 -0
  25. package/src/compile/nodes/const-lit.ts +30 -0
  26. package/src/compile/nodes/fast-binary.ts +71 -0
  27. package/src/compile/nodes/fn.ts +87 -0
  28. package/src/compile/nodes/index.ts +8 -0
  29. package/src/compile/nodes/literal.ts +86 -0
  30. package/src/compile/nodes/map-fields.ts +79 -0
  31. package/src/compile/nodes/member.ts +32 -0
  32. package/src/events/envelope.types.ts +17 -0
  33. package/src/events/event-data.types.ts +25 -0
  34. package/src/events/ids.types.ts +10 -0
  35. package/src/events/index.ts +5 -0
  36. package/src/events/run-plan.types.ts +18 -0
  37. package/src/events/status.types.ts +2 -0
  38. package/src/expand/accepted-kinds.ts +23 -0
  39. package/src/expand/deco/deco-decorator.ts +77 -0
  40. package/src/expand/deco/deco-env.ts +24 -0
  41. package/src/expand/deco/deco.types.ts +43 -0
  42. package/src/expand/deco/document-decos.ts +64 -0
  43. package/src/expand/deco/index.ts +7 -0
  44. package/src/expand/deco/read-signature.ts +57 -0
  45. package/src/expand/deco/run-body.ts +119 -0
  46. package/src/expand/decorate-callable.ts +49 -0
  47. package/src/expand/decorations.ts +41 -0
  48. package/src/expand/expand.ts +108 -0
  49. package/src/expand/expand.types.ts +78 -0
  50. package/src/expand/handles/around-verbs.ts +35 -0
  51. package/src/expand/handles/binding-verbs.ts +27 -0
  52. package/src/expand/handles/common-verbs.ts +40 -0
  53. package/src/expand/handles/fn-verbs.ts +58 -0
  54. package/src/expand/handles/handle.types.ts +33 -0
  55. package/src/expand/handles/index.ts +4 -0
  56. package/src/expand/handles/kind-of.ts +36 -0
  57. package/src/expand/handles/make-handle.ts +100 -0
  58. package/src/expand/handles/missing-verb.ts +27 -0
  59. package/src/expand/handles/runnable-verbs.ts +13 -0
  60. package/src/expand/handles/type-verbs.ts +56 -0
  61. package/src/expand/index.ts +40 -0
  62. package/src/expand/make-context.ts +69 -0
  63. package/src/expand/node-meta.ts +30 -0
  64. package/src/expand/node-span.ts +17 -0
  65. package/src/expand/swap-node.ts +28 -0
  66. package/src/expand/wrong-kind.ts +102 -0
  67. package/src/expr/cell.types.ts +25 -0
  68. package/src/expr/closure.ts +53 -0
  69. package/src/expr/closure.types.ts +31 -0
  70. package/src/expr/eval-env.types.ts +9 -0
  71. package/src/expr/evaluate.ts +18 -0
  72. package/src/expr/frame.ts +60 -0
  73. package/src/expr/index.ts +14 -0
  74. package/src/expr/invoke.ts +140 -0
  75. package/src/expr/member-value.ts +91 -0
  76. package/src/expr/methods/index.ts +65 -0
  77. package/src/expr/methods/list-grouping.ts +107 -0
  78. package/src/expr/methods/list-methods.ts +57 -0
  79. package/src/expr/methods/list-selection.ts +142 -0
  80. package/src/expr/methods/map-extras.ts +87 -0
  81. package/src/expr/methods/map-methods.ts +17 -0
  82. package/src/expr/methods/number-methods.ts +29 -0
  83. package/src/expr/methods/string-extras.ts +66 -0
  84. package/src/expr/methods/string-methods.ts +32 -0
  85. package/src/expr/methods/unit-methods.ts +54 -0
  86. package/src/expr/namespace.ts +20 -0
  87. package/src/expr/native.types.ts +40 -0
  88. package/src/expr/operators.ts +91 -0
  89. package/src/expr/pending.ts +47 -0
  90. package/src/expr/prelude.ts +75 -0
  91. package/src/expr/task.ts +71 -0
  92. package/src/format/format-text.ts +22 -0
  93. package/src/format/format.types.ts +18 -0
  94. package/src/format/from-settings.ts +40 -0
  95. package/src/format/index.ts +5 -0
  96. package/src/format/organize-header.ts +66 -0
  97. package/src/format/reindent.ts +35 -0
  98. package/src/generated/ast.ts +2383 -0
  99. package/src/generated/grammar.ts +5236 -0
  100. package/src/generated/module.ts +25 -0
  101. package/src/grammar/venn.langium +291 -0
  102. package/src/graph/graph.types.ts +20 -0
  103. package/src/graph/index.ts +2 -0
  104. package/src/graph/to-graph.ts +87 -0
  105. package/src/index.ts +93 -0
  106. package/src/interpolation/compile-template.ts +40 -0
  107. package/src/interpolation/index.ts +4 -0
  108. package/src/interpolation/interpolation.types.ts +18 -0
  109. package/src/interpolation/scan-interpolations.ts +47 -0
  110. package/src/interpolation/template.types.ts +19 -0
  111. package/src/lang/default-services.ts +14 -0
  112. package/src/lang/index.ts +3 -0
  113. package/src/lang/venn-lexer.ts +34 -0
  114. package/src/lang/venn-module.ts +51 -0
  115. package/src/module/index.ts +5 -0
  116. package/src/module/specifier.ts +21 -0
  117. package/src/parse/error-to-problem.ts +43 -0
  118. package/src/parse/index.ts +3 -0
  119. package/src/parse/parse-expression.ts +27 -0
  120. package/src/parse/parse-output.types.ts +8 -0
  121. package/src/parse/parse.ts +26 -0
  122. package/src/problem/build-diff.ts +116 -0
  123. package/src/problem/diff.types.ts +20 -0
  124. package/src/problem/format-value.ts +40 -0
  125. package/src/problem/index.ts +8 -0
  126. package/src/problem/problem-error.ts +15 -0
  127. package/src/problem/problem.types.ts +21 -0
  128. package/src/problem/related.types.ts +7 -0
  129. package/src/problem/severity.types.ts +2 -0
  130. package/src/problem/span.types.ts +8 -0
  131. package/src/typecheck/action-signature.ts +53 -0
  132. package/src/typecheck/builtin-types.ts +63 -0
  133. package/src/typecheck/builtins.ts +267 -0
  134. package/src/typecheck/catalog.types.ts +16 -0
  135. package/src/typecheck/check-deco.ts +174 -0
  136. package/src/typecheck/check-stmts.ts +151 -0
  137. package/src/typecheck/check-types.ts +232 -0
  138. package/src/typecheck/context.ts +31 -0
  139. package/src/typecheck/imported-types.ts +127 -0
  140. package/src/typecheck/index.ts +26 -0
  141. package/src/typecheck/infer.ts +416 -0
  142. package/src/typecheck/kind-types.ts +79 -0
  143. package/src/typecheck/member-docs.ts +211 -0
  144. package/src/typecheck/named-types.ts +57 -0
  145. package/src/typecheck/prelude-types.ts +152 -0
  146. package/src/typecheck/reshaped-fns.ts +58 -0
  147. package/src/typecheck/scheme.ts +75 -0
  148. package/src/typecheck/seed-params.ts +72 -0
  149. package/src/typecheck/seed-values.ts +64 -0
  150. package/src/typecheck/show.ts +64 -0
  151. package/src/typecheck/solidify.ts +60 -0
  152. package/src/typecheck/spec-to-type.ts +88 -0
  153. package/src/typecheck/type-env.ts +46 -0
  154. package/src/typecheck/type-ref.ts +62 -0
  155. package/src/typecheck/type.types.ts +168 -0
  156. package/src/typecheck/unify.ts +192 -0
  157. package/src/typecheck/unit-types.ts +49 -0
  158. package/src/units/combine.ts +97 -0
  159. package/src/units/index.ts +6 -0
  160. package/src/units/parse-instant.ts +12 -0
  161. package/src/units/parse-number.ts +48 -0
  162. package/src/units/unit-guards.ts +16 -0
  163. package/src/units/unit.types.ts +38 -0
  164. package/src/value/equals.ts +4 -0
  165. package/src/value/index.ts +4 -0
  166. package/src/value/is-numeric.ts +6 -0
  167. package/src/value/truthiness.ts +9 -0
  168. package/src/value/value.types.ts +12 -0
@@ -0,0 +1,75 @@
1
+ import { isClosure } from "./closure.js";
2
+ import { invoke } from "./invoke.js";
3
+ import { isNativeFn, nativeFn } from "./native.types.js";
4
+ import { startTask } from "./task.js";
5
+
6
+ /**
7
+ * Render a value the way `print` and `str` do: a string as itself, anything
8
+ * else as JSON. A value JSON cannot hold falls back to its plain text rather
9
+ * than throwing, because printing must never be the thing that fails a flow.
10
+ */
11
+ export function display(value: unknown): string {
12
+ if (typeof value === "string") return value;
13
+ if (value === null || value === undefined) return String(value);
14
+ if (typeof value !== "object") return String(value);
15
+ try {
16
+ return JSON.stringify(value);
17
+ } catch {
18
+ return String(value);
19
+ }
20
+ }
21
+
22
+ /**
23
+ * The name of a value's type, as the language talks about it: "null", "list",
24
+ * "fn", "map", "bool", "string", "number", or a unit's own kind.
25
+ */
26
+ export function typeName(value: unknown): string {
27
+ if (value === null || value === undefined) return "null";
28
+ if (Array.isArray(value)) return "list";
29
+ if (isClosure(value) || isNativeFn(value)) return "fn";
30
+ const kind = (value as { kind?: unknown }).kind;
31
+ if (typeof value === "object" && typeof kind === "string") return kind;
32
+ if (typeof value === "object") return "map";
33
+ return typeof value === "boolean" ? "bool" : typeof value;
34
+ }
35
+
36
+ /** `range(3)` → [0,1,2]; `range(1, 4)` → [1,2,3]; `range(0, 10, 2)` → [0,2,4,6,8]. */
37
+ function range(args: readonly unknown[]): number[] {
38
+ const [a, b, c] = args.map(Number);
39
+ const from = args.length > 1 ? (a ?? 0) : 0;
40
+ const to = args.length > 1 ? (b ?? 0) : (a ?? 0);
41
+ const step = c && c !== 0 ? c : from <= to ? 1 : -1;
42
+ const out: number[] = [];
43
+ for (let n = from; step > 0 ? n < to : n > to; n += step) out.push(n);
44
+ return out;
45
+ }
46
+
47
+ /**
48
+ * The whole prelude of values: what has no receiver to hang off.
49
+ *
50
+ * `len(xs)`, `sum(xs)`, `keys(m)` and `round(x, 2)` are deliberately absent:
51
+ * they read better as `xs.len`, `xs.sum`, `m.keys` and `x.round(2)`, and a
52
+ * second spelling would only be a second way to say the same thing. Those names
53
+ * are also exactly the ones a script wants to bind (`const sum = …`), and a
54
+ * binding would silently shadow them.
55
+ */
56
+ export const PRELUDE_VALUES: Readonly<Record<string, unknown>> = {
57
+ // Builds a list out of nothing: there is no receiver to ask.
58
+ range: nativeFn(range),
59
+ // Works on `null`, which can carry no method of its own.
60
+ str: nativeFn((args) => args.map(display).join(" ")),
61
+ typeOf: nativeFn((args) => typeName(args[0])),
62
+ // `fmt.json(x, 2)` without the import: the everyday "show me this".
63
+ pretty: nativeFn((args) => prettyJson(args[0])),
64
+ // Start work without stopping for it. Everything else waits by itself, so
65
+ // this is the only way to say "carry on"; `.wait` asks for it back.
66
+ spawn: nativeFn((args) => startTask(() => invoke(args[0], []))),
67
+ };
68
+
69
+ function prettyJson(value: unknown): string {
70
+ try {
71
+ return JSON.stringify(value, null, 2) ?? String(value);
72
+ } catch {
73
+ return String(value);
74
+ }
75
+ }
@@ -0,0 +1,71 @@
1
+ import { type Method, nativeFn } from "./native.types.js";
2
+
3
+ const TASK = Symbol("venn.task");
4
+
5
+ /**
6
+ * Work already under way, held rather than waited for.
7
+ *
8
+ * Every call in Venn waits by itself, which is what makes `async` and `await`
9
+ * unnecessary. `spawn` is how to opt out and start something without stopping
10
+ * for it, the other half of the same feature.
11
+ *
12
+ * A task is deliberately not a promise. A promise handed to `let` would be
13
+ * waited for by the statement, which is exactly what `spawn` exists to avoid,
14
+ * so the promise is kept inside and `.wait` is how a reader asks for it back.
15
+ */
16
+ export interface Task {
17
+ readonly [TASK]: true;
18
+ readonly promise: Promise<unknown>;
19
+ settled: boolean;
20
+ failed: boolean;
21
+ }
22
+
23
+ /** Whether this value is a running task. */
24
+ export function isTask(value: unknown): value is Task {
25
+ return typeof value === "object" && value !== null && TASK in value;
26
+ }
27
+
28
+ /**
29
+ * Start work and hand back a handle to it.
30
+ *
31
+ * The work begins on the next microtask, and the handle observes the promise
32
+ * straight away, so a task nobody waits for cannot raise an unhandled
33
+ * rejection.
34
+ */
35
+ export function startTask(run: () => unknown): Task {
36
+ const task: Task = {
37
+ [TASK]: true,
38
+ promise: Promise.resolve().then(run),
39
+ settled: false,
40
+ failed: false,
41
+ };
42
+ void task.promise.then(
43
+ () => {
44
+ task.settled = true;
45
+ },
46
+ () => {
47
+ task.settled = true;
48
+ task.failed = true;
49
+ },
50
+ );
51
+ return task;
52
+ }
53
+
54
+ /**
55
+ * What a task answers to.
56
+ *
57
+ * `.wait` gives back the promise and the statement that binds it does the
58
+ * waiting, which is the same rule as everywhere else.
59
+ */
60
+ export const TASK_METHODS: Record<string, Method> = {
61
+ wait: (task: Task) => task.promise,
62
+ done: (task: Task) => task.settled,
63
+ failed: (task: Task) => task.failed,
64
+ // Failure is the caller's to handle: a task nobody waits for must not take
65
+ // the process down with an unhandled rejection.
66
+ settle: (task: Task) => nativeFn(() => task.promise.then(ok, () => undefined)),
67
+ };
68
+
69
+ function ok(value: unknown): unknown {
70
+ return value;
71
+ }
@@ -0,0 +1,22 @@
1
+ import { DEFAULT_FORMAT, type FormatOptions } from "./format.types.js";
2
+ import { organizeHeader } from "./organize-header.js";
3
+ import { reindent } from "./reindent.js";
4
+
5
+ /**
6
+ * Format a `.vn` source: group the header, then re-indent. Line-oriented by
7
+ * design, so it never joins or splits lines. That makes it idempotent, and a
8
+ * one-line block stays on one line.
9
+ */
10
+ export function formatText(source: string, options?: Partial<FormatOptions>): string {
11
+ const settings = { ...DEFAULT_FORMAT, ...options };
12
+ const eol = source.includes("\r\n") ? "\r\n" : "\n";
13
+ const lines = source.split(/\r?\n/);
14
+ const organized = settings.organizeHeader
15
+ ? organizeHeader(lines, settings.sortHeader)
16
+ : [...lines];
17
+ return reindent(organized, unitOf(settings)).join(eol);
18
+ }
19
+
20
+ function unitOf(options: FormatOptions): string {
21
+ return options.useTabs ? "\t" : " ".repeat(Math.max(1, options.indentWidth));
22
+ }
@@ -0,0 +1,18 @@
1
+ /** How to format a `.vn` file. Mirrors the `[format]` table of `venn.toml`. */
2
+ export interface FormatOptions {
3
+ /** Spaces per indent level. Ignored when {@link useTabs} is on. */
4
+ indentWidth: number;
5
+ useTabs: boolean;
6
+ /** Move every `use` above every `import`. */
7
+ organizeHeader: boolean;
8
+ /** Sort each header group alphabetically. */
9
+ sortHeader: boolean;
10
+ }
11
+
12
+ /** What the formatter does when `venn.toml` says nothing. */
13
+ export const DEFAULT_FORMAT: FormatOptions = {
14
+ indentWidth: 2,
15
+ useTabs: false,
16
+ organizeHeader: true,
17
+ sortHeader: false,
18
+ };
@@ -0,0 +1,40 @@
1
+ import type { FormatOptions } from "./format.types.js";
2
+
3
+ /** The shape of the `[format]` table, loose enough for a parsed TOML record. */
4
+ export interface FormatTable {
5
+ indent?: unknown;
6
+ tabs?: unknown;
7
+ organize?: unknown;
8
+ sort?: unknown;
9
+ }
10
+
11
+ /**
12
+ * Map the raw `[format]` table of `venn.toml` onto formatter options. Keys the
13
+ * project did not set are dropped, so the defaults survive the merge.
14
+ */
15
+ export function formatOptionsFrom(settings: FormatTable | undefined): Partial<FormatOptions> {
16
+ const table: FormatTable = settings ?? {};
17
+ return prune({
18
+ indentWidth: numberOf(table.indent),
19
+ useTabs: booleanOf(table.tabs),
20
+ organizeHeader: booleanOf(table.organize),
21
+ sortHeader: booleanOf(table.sort),
22
+ });
23
+ }
24
+
25
+ function prune(options: Partial<FormatOptions>): Partial<FormatOptions> {
26
+ const out: Record<string, unknown> = {};
27
+ for (const [key, value] of Object.entries(options)) {
28
+ if (value !== undefined) out[key] = value;
29
+ }
30
+ return out as Partial<FormatOptions>;
31
+ }
32
+
33
+ function numberOf(value: unknown): number | undefined {
34
+ const parsed = Number(value);
35
+ return value === undefined || Number.isNaN(parsed) ? undefined : parsed;
36
+ }
37
+
38
+ function booleanOf(value: unknown): boolean | undefined {
39
+ return value === undefined ? undefined : value === true || value === "true";
40
+ }
@@ -0,0 +1,5 @@
1
+ export { DEFAULT_FORMAT, type FormatOptions } from "./format.types.js";
2
+ export { formatText } from "./format-text.js";
3
+ export { type FormatTable, formatOptionsFrom } from "./from-settings.js";
4
+ export { organizeHeader } from "./organize-header.js";
5
+ export { reindent } from "./reindent.js";
@@ -0,0 +1,66 @@
1
+ interface Entry {
2
+ kind: "use" | "import";
3
+ comments: string[];
4
+ line: string;
5
+ }
6
+
7
+ interface Header {
8
+ head: string[];
9
+ entries: Entry[];
10
+ floating: string[];
11
+ rest: string[];
12
+ }
13
+
14
+ /**
15
+ * Put every `use` above every `import`. A comment sitting directly on top of a
16
+ * line travels with it; a comment separated by a blank line is left where the
17
+ * header begins, so nothing is silently re-attached to the wrong statement.
18
+ */
19
+ export function organizeHeader(lines: readonly string[], sort: boolean): string[] {
20
+ const header = split(lines);
21
+ if (header.entries.length === 0) return [...lines];
22
+ const head = header.head.length > 0 ? [...header.head, ""] : [];
23
+ const uses = group(header.entries, "use", sort);
24
+ const imports = group(header.entries, "import", sort);
25
+ return [...head, ...uses, ...imports, ...header.floating, ...header.rest];
26
+ }
27
+
28
+ function split(lines: readonly string[]): Header {
29
+ const header: Header = { head: [], entries: [], floating: [], rest: [] };
30
+ let pending: string[] = [];
31
+ let index = 0;
32
+ for (; index < lines.length; index++) {
33
+ const line = (lines[index] ?? "").trim();
34
+ const kind = kindOf(line);
35
+ if (kind) {
36
+ header.entries.push({ kind, comments: pending, line });
37
+ pending = [];
38
+ } else if (/^module\s/.test(line)) {
39
+ header.head.push(...pending, line);
40
+ pending = [];
41
+ } else if (line.startsWith("#")) pending.push(line);
42
+ else if (line === "") pending = park(header, pending);
43
+ else break;
44
+ }
45
+ header.rest = [...pending, ...lines.slice(index)];
46
+ return header;
47
+ }
48
+
49
+ // A comment block cut off by a blank line belongs to nobody: keep it in place.
50
+ function park(header: Header, pending: string[]): string[] {
51
+ if (pending.length > 0)
52
+ (header.entries.length === 0 ? header.head : header.floating).push(...pending);
53
+ return [];
54
+ }
55
+
56
+ function group(entries: readonly Entry[], kind: Entry["kind"], sort: boolean): string[] {
57
+ const picked = entries.filter((entry) => entry.kind === kind);
58
+ const ordered = sort ? [...picked].sort((a, b) => a.line.localeCompare(b.line)) : picked;
59
+ if (ordered.length === 0) return [];
60
+ return [...ordered.flatMap((entry) => [...entry.comments, entry.line]), ""];
61
+ }
62
+
63
+ function kindOf(line: string): Entry["kind"] | undefined {
64
+ if (/^use\s/.test(line)) return "use";
65
+ return /^(pub\s+)?import\s/.test(line) ? "import" : undefined;
66
+ }
@@ -0,0 +1,35 @@
1
+ const OPEN = /[{[(]/g;
2
+ const CLOSE = /[}\])]/g;
3
+
4
+ /**
5
+ * Re-indent by bracket depth. Venn statements are newline terminated, so this
6
+ * only ever rewrites the leading whitespace of a line. It never joins or splits
7
+ * lines, which makes it idempotent by construction.
8
+ *
9
+ * @param unit The string for one indent level, either spaces or a tab.
10
+ */
11
+ export function reindent(lines: readonly string[], unit: string): string[] {
12
+ const out: string[] = [];
13
+ let depth = 0;
14
+ for (const line of lines) {
15
+ const stripped = stripLiterals(line).trim();
16
+ const level = Math.max(0, depth - leadingClosers(stripped));
17
+ const trimmed = line.trim();
18
+ out.push(trimmed === "" ? "" : unit.repeat(level) + trimmed);
19
+ depth = Math.max(0, depth + count(stripped, OPEN) - count(stripped, CLOSE));
20
+ }
21
+ return out;
22
+ }
23
+
24
+ // Brackets inside strings or comments must not move the depth.
25
+ function stripLiterals(line: string): string {
26
+ return line.replace(/"(\\.|[^"\\])*"/g, '""').replace(/#.*$/, "");
27
+ }
28
+
29
+ function leadingClosers(stripped: string): number {
30
+ return /^[}\])]*/.exec(stripped)?.[0].length ?? 0;
31
+ }
32
+
33
+ function count(text: string, pattern: RegExp): number {
34
+ return text.match(pattern)?.length ?? 0;
35
+ }