lemmascript 0.5.22 → 0.6.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lemmascript",
3
- "version": "0.5.22",
3
+ "version": "0.6.0",
4
4
  "description": "A verification toolchain for TypeScript — generates Lean 4 or Dafny from annotated TS",
5
5
  "type": "module",
6
6
  "engines": {
@@ -1583,10 +1583,8 @@ export function emitDafnyFile(file, tsFileName, opts) {
1583
1583
  // skipped when the corresponding pure def was actually emitted.
1584
1584
  const emittedPureDefs = new Set();
1585
1585
  // Emit a decl, rolling back any preamble requirements it registered if it
1586
- // throws. A skipped decl must contribute neither text nor preambles — else a
1587
- // side-effecting `needPreamble` from a half-emitted decl leaves an unused
1588
- // preamble (e.g. `type Unknown` from a skipped const whose head is
1589
- // `unknown`-typed but whose value expr is unsupported).
1586
+ // throws, so callers that catch an emission error never observe partial
1587
+ // emitter state.
1590
1588
  const emitDeclTx = (d) => {
1591
1589
  const saved = new Set(_neededPreambles);
1592
1590
  try {
@@ -1601,13 +1599,12 @@ export function emitDafnyFile(file, tsFileName, opts) {
1601
1599
  };
1602
1600
  // Emit declarations
1603
1601
  const declLines = [];
1604
- const skipped = [];
1605
1602
  for (const decl of file.decls) {
1606
1603
  if (decl.kind === "method" && emittedPureDefs.has(decl.name))
1607
1604
  continue;
1608
1605
  if (decl.kind === "namespace") {
1609
- // Emit each inner decl individually if one fails, the rest survive
1610
- // and failed defs fall back to their method wrappers
1606
+ // Emit each inner declaration separately so failures name the exact
1607
+ // pure declaration that could not be translated.
1611
1608
  for (const inner of decl.decls) {
1612
1609
  try {
1613
1610
  declLines.push("");
@@ -1617,10 +1614,9 @@ export function emitDafnyFile(file, tsFileName, opts) {
1617
1614
  }
1618
1615
  catch (e) {
1619
1616
  const name = "name" in inner ? inner.name : "unknown";
1620
- const msg = e.message;
1621
- console.error(`WARNING: skipping pure '${name}': ${msg}`);
1622
- declLines.push(`\n// LemmaScript: skipped pure ${name}`);
1623
- skipped.push(name);
1617
+ const reason = e instanceof Error ? e.message : String(e);
1618
+ const source = tsFileName ? ` in ${tsFileName}` : "";
1619
+ throw new Error(`Dafny emission failed for '${name}'${source}: ${reason}`);
1624
1620
  }
1625
1621
  }
1626
1622
  continue;
@@ -1633,15 +1629,11 @@ export function emitDafnyFile(file, tsFileName, opts) {
1633
1629
  }
1634
1630
  catch (e) {
1635
1631
  const name = "name" in decl ? decl.name : "unknown";
1636
- const msg = e.message;
1637
- console.error(`WARNING: skipping '${name}': ${msg}`);
1638
- declLines.push(`// LemmaScript: skipped ${name}`);
1639
- skipped.push(name);
1632
+ const reason = e instanceof Error ? e.message : String(e);
1633
+ const source = tsFileName ? ` in ${tsFileName}` : "";
1634
+ throw new Error(`Dafny emission failed for '${name}'${source}: ${reason}`);
1640
1635
  }
1641
1636
  }
1642
- if (skipped.length > 0) {
1643
- console.error(`WARNING: ${skipped.length} declaration(s) skipped: ${skipped.join(", ")}`);
1644
- }
1645
1637
  // Build output with needed preambles
1646
1638
  const lines = [];
1647
1639
  if (tsFileName)
@@ -2158,7 +2158,8 @@ export function extractModule(sourceFile) {
2158
2158
  });
2159
2159
  }
2160
2160
  catch (e) {
2161
- console.error(`WARNING: skipping const '${decl.getName()}': ${e.message}`);
2161
+ const reason = e instanceof Error ? e.message : String(e);
2162
+ throw new Error(`Failed to extract const '${decl.getName()}' at ${sourceFile.getBaseName()}:${decl.getStartLineNumber()}: ${reason}`);
2162
2163
  }
2163
2164
  }
2164
2165
  }
package/tools/dist/lsc.js CHANGED
@@ -337,4 +337,10 @@ function runFile(cmd, filePath, backend, timeLimit, extraFlags, noVerify = false
337
337
  console.error(`Unknown command: ${cmd}`);
338
338
  process.exit(1);
339
339
  }
340
- main();
340
+ try {
341
+ main();
342
+ }
343
+ catch (e) {
344
+ console.error(`ERROR: ${e instanceof Error ? e.message : String(e)}`);
345
+ process.exitCode = 1;
346
+ }
@@ -4,11 +4,11 @@
4
4
  */
5
5
  import { normalizeBigIntLiteral } from "./rawir.js";
6
6
  const MULTI_OPS = ["<==>", "==>", "===", "!==", "==", "!=", ">=", "<=", "&&", "||"];
7
- /** Hex / binary / octal / decimal integer, with optional numeric separators and
8
- * an optional `n` (BigInt) suffix in capture group 1. Deliberately integer-only:
9
- * a trailing `.` is left for the tokenizer to emit as punctuation, exactly as
10
- * the previous digit-scanning loop did. */
11
- const INTEGER_LITERAL = /^(?:(?:0[xX][0-9a-fA-F](?:_?[0-9a-fA-F])*)|(?:0[bB][01](?:_?[01])*)|(?:0[oO][0-7](?:_?[0-7])*)|(?:[0-9](?:_?[0-9])*))(n)?/;
7
+ /** Numeric literals accepted in specs. BigInts are recognized first so their
8
+ * exact value never passes through Number; ordinary numbers additionally
9
+ * allow TypeScript's fractional and exponent forms. */
10
+ const BIGINT_LITERAL = /^(?:(?:0[xX][0-9a-fA-F](?:_?[0-9a-fA-F])*)|(?:0[bB][01](?:_?[01])*)|(?:0[oO][0-7](?:_?[0-7])*)|(?:[0-9](?:_?[0-9])*))n/;
11
+ const NUMBER_LITERAL = /^(?:(?:0[xX][0-9a-fA-F](?:_?[0-9a-fA-F])*)|(?:0[bB][01](?:_?[01])*)|(?:0[oO][0-7](?:_?[0-7])*)|(?:(?:[0-9](?:_?[0-9])*)(?:\.(?:[0-9](?:_?[0-9])*)?)?|\.(?:[0-9](?:_?[0-9])*))(?:[eE][+-]?(?:[0-9](?:_?[0-9])*))?)/;
12
12
  function tokenize(input) {
13
13
  const tokens = [];
14
14
  let i = 0;
@@ -47,15 +47,17 @@ function tokenize(input) {
47
47
  tokens.push({ type: "str", value: s });
48
48
  continue;
49
49
  }
50
- if (/[0-9]/.test(input[i])) {
51
- const match = input.slice(i).match(INTEGER_LITERAL);
50
+ if (/[0-9]/.test(input[i]) || (input[i] === "." && /[0-9]/.test(input[i + 1]))) {
51
+ const rest = input.slice(i);
52
+ const bigintMatch = rest.match(BIGINT_LITERAL);
53
+ const match = bigintMatch ?? rest.match(NUMBER_LITERAL);
52
54
  if (!match)
53
55
  throw new Error(`Invalid numeric literal at ${i} in: ${input}`);
54
56
  const text = match[0];
55
57
  i += text.length;
56
58
  // The `n` suffix is meaningful, not noise: a BigInt keeps its exact value
57
59
  // as a decimal string instead of being rounded into a double.
58
- if (match[1] === "n")
60
+ if (bigintMatch)
59
61
  tokens.push({ type: "bigint", value: normalizeBigIntLiteral(text) });
60
62
  else
61
63
  tokens.push({ type: "num", value: Number(text.replace(/_/g, "")) });