lemmascript 0.2.0 → 0.3.1

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.
@@ -51,7 +51,7 @@ function escapeName(name) {
51
51
  const PREC = {
52
52
  "→": 1, "∨": 2, "∧": 3,
53
53
  "=": 4, "≠": 4, "≥": 4, "≤": 4, ">": 4, "<": 4,
54
- "+": 5, "-": 5, "*": 6, "/": 6, "%": 6,
54
+ "+": 5, "-": 5, "++": 5, "arrayConcat": 5, "*": 6, "/": 6, "%": 6,
55
55
  };
56
56
  function prec(op) { return PREC[op] ?? 10; }
57
57
  // ── Method call → Lean syntax ───────────────────────────────
@@ -143,7 +143,8 @@ function emitExpr(e, parentPrec) {
143
143
  return `-${e.expr.value}`;
144
144
  return `(-${emitExpr(e.expr)})`;
145
145
  case "binop": {
146
- const s = `${emitExpr(e.left, prec(e.op))} ${e.op} ${emitExpr(e.right, prec(e.op))}`;
146
+ const op = e.op === "arrayConcat" ? "++" : e.op;
147
+ const s = `${emitExpr(e.left, prec(e.op))} ${op} ${emitExpr(e.right, prec(e.op))}`;
147
148
  return (parentPrec !== undefined && prec(e.op) < parentPrec) ? `(${s})` : s;
148
149
  }
149
150
  case "implies": {
@@ -317,6 +318,9 @@ function emitDecl(d) {
317
318
  lines.push(`deriving ${d.deriving.join(", ")}`);
318
319
  return lines.join("\n");
319
320
  }
321
+ case "type-alias": {
322
+ return `abbrev ${d.name} := ${tyToLean(d.target)}`;
323
+ }
320
324
  case "def": {
321
325
  const params = d.params.map(p => `(${escapeName(p.name)} : ${tyToLean(p.type)})`).join(" ");
322
326
  return `def ${d.name} ${params} : ${tyToLean(d.returnType)} :=\n${emitPureExpr(d.body, 1)}`;
package/tools/dist/lsc.js CHANGED
@@ -9,7 +9,7 @@ import { existsSync } from "fs";
9
9
  import path from "path";
10
10
  import { extractModule } from "./extract.js";
11
11
  import { resolveModule } from "./resolve.js";
12
- import { transformModule, transformModuleDafny } from "./transform.js";
12
+ import { transformModuleLean, transformModuleDafny } from "./transform.js";
13
13
  import { emitLeanFile } from "./lean-emit.js";
14
14
  import { emitDafnyFile } from "./dafny-emit.js";
15
15
  import { dafnyGen, dafnyCheckDiff, dafnyVerify, dafnyRegen } from "./dafny-commands.js";
@@ -17,7 +17,7 @@ import { leanGen, leanCheck } from "./lean-commands.js";
17
17
  function main() {
18
18
  const args = process.argv.slice(2);
19
19
  const backendIdx = args.findIndex(a => a.startsWith("--backend="));
20
- let backend = "lean";
20
+ let backend = "dafny";
21
21
  if (backendIdx >= 0) {
22
22
  const val = args[backendIdx].split("=")[1];
23
23
  if (val !== "lean" && val !== "dafny") {
@@ -33,6 +33,12 @@ function main() {
33
33
  timeLimit = parseInt(args[timeLimitIdx].split("=")[1]);
34
34
  args.splice(timeLimitIdx, 1);
35
35
  }
36
+ const extraFlagsIdx = args.findIndex(a => a.startsWith("--extra-flags="));
37
+ let extraFlags;
38
+ if (extraFlagsIdx >= 0) {
39
+ extraFlags = args[extraFlagsIdx].split("=").slice(1).join("=");
40
+ args.splice(extraFlagsIdx, 1);
41
+ }
36
42
  const [cmd, filePath] = args;
37
43
  if (!cmd || !filePath) {
38
44
  console.error("Usage: lsc <gen|check|regen|extract> [--backend=lean|dafny] <file.ts>");
@@ -43,8 +49,25 @@ function main() {
43
49
  console.error(`File not found: ${absPath}`);
44
50
  process.exit(1);
45
51
  }
46
- const project = new Project({ compilerOptions: { strict: true, target: ScriptTarget.ESNext, lib: ["lib.esnext.d.ts"] } });
52
+ // Find nearest tsconfig.json for import resolution; fall back to bare options
53
+ function findTsConfig(from) {
54
+ let dir = path.dirname(from);
55
+ while (true) {
56
+ const candidate = path.join(dir, "tsconfig.json");
57
+ if (existsSync(candidate))
58
+ return candidate;
59
+ const parent = path.dirname(dir);
60
+ if (parent === dir)
61
+ return undefined;
62
+ dir = parent;
63
+ }
64
+ }
65
+ const tsConfigFilePath = findTsConfig(absPath);
66
+ const project = tsConfigFilePath
67
+ ? new Project({ tsConfigFilePath })
68
+ : new Project({ compilerOptions: { strict: true, target: ScriptTarget.ESNext, lib: ["lib.esnext.d.ts"] } });
47
69
  const sourceFile = project.addSourceFileAtPath(absPath);
70
+ project.resolveSourceFileDependencies();
48
71
  // Check //@ backend directive — skip if backend doesn't match
49
72
  const backendDirective = sourceFile.getFullText().match(/\/\/@ backend (\w+)/);
50
73
  if (backendDirective && backendDirective[1] !== backend) {
@@ -84,7 +107,7 @@ function main() {
84
107
  dafnyGen(genPath, dfyPath, text);
85
108
  if (!dafnyCheckDiff(genPath, dfyPath))
86
109
  process.exit(1);
87
- if (!dafnyVerify(dfyPath, dir, timeLimit))
110
+ if (!dafnyVerify(dfyPath, dir, timeLimit, extraFlags))
88
111
  process.exit(1);
89
112
  return;
90
113
  }
@@ -98,7 +121,7 @@ function main() {
98
121
  // ── Lean backend ──────────────────────────────────────────
99
122
  const specPath = path.join(dir, `${base}.spec.lean`);
100
123
  const specImport = existsSync(specPath) ? `«${base}.spec»` : undefined;
101
- const { typesFile, defFile } = transformModule(typed, specImport);
124
+ const { typesFile, defFile } = transformModuleLean(typed, specImport);
102
125
  const typesPath = typesFile ? path.join(dir, `${base}.types.lean`) : null;
103
126
  const typesText = typesFile ? emitLeanFile(typesFile) : null;
104
127
  const defPath = path.join(dir, `${base}.def.lean`);