eval-hs helper + generator fixes — 304/941 (32%)

Added eval-hs: compile and evaluate HS expressions/commands, used by
conformance-dev tests. Smart wrapping: adds 'return' prefix for
expressions, leaves commands (set/put/get/then/return) as-is.

Fixed generator ref() to use context-aware variable mapping.

304/941 with the user's conformance-dev.sx tests included (110 new).
Failure breakdown: 111 stubs, 74 "bar" (eval errors), 51 assertion
failures, 30 eval-only stubs, 24 undefined "live", 18 parser errors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-10 16:09:03 +00:00
parent ce4579badb
commit 1461919857
3 changed files with 756 additions and 792 deletions

View File

@@ -38,7 +38,7 @@ function getModuleSrc(mod) {
}
// Cache test file sources
const TEST_FILES = ['spec/harness.sx', 'spec/tests/test-framework.sx', 'spec/tests/test-hyperscript-behavioral.sx'];
const TEST_FILES = ['spec/harness.sx', 'spec/tests/test-framework.sx', 'spec/tests/test-hyperscript-behavioral.sx', 'spec/tests/test-hyperscript-conformance-dev.sx'];
const TEST_FILE_CACHE = {};
for (const f of TEST_FILES) {
TEST_FILE_CACHE[f] = fs.readFileSync(path.join(PROJECT_ROOT, f), 'utf8');
@@ -111,7 +111,7 @@ async function bootSandbox(page) {
}
await page.evaluate(() => { if (window.SxKernel.endModuleLoad) window.SxKernel.endModuleLoad(); });
// Deferred test registration
// Deferred test registration + helpers
await page.evaluate(() => {
const K = window.SxKernel;
K.eval('(define _test-registry (list))');
@@ -127,6 +127,18 @@ async function bootSandbox(page) {
K.eval(`(define report-fail (fn (name error)
(let ((i (- (len _test-registry) 1)))
(when (>= i 0) (dict-set! (nth _test-registry i) "name" name)))))`);
// eval-hs: compile and evaluate a hyperscript expression/command, return its value.
// If src contains 'return', use as-is. If it starts with a command keyword (set/put/get),
// use as-is (the last expression is the result). Otherwise wrap in 'return'.
K.eval(`(define eval-hs (fn (src)
(let ((has-cmd (or (string-contains? src "return ")
(string-contains? src "then ")
(= "set " (slice src 0 4))
(= "put " (slice src 0 4))
(= "get " (slice src 0 4)))))
(let ((wrapped (if has-cmd src (str "return " src))))
(let ((sx (hs-to-sx-from-source wrapped)))
(eval-expr sx))))))`);
});
for (const f of TEST_FILES) {
@@ -256,19 +268,25 @@ test.describe('Hyperscript behavioral tests', () => {
for (const [t, n] of Object.entries(errTypes).sort((a,b) => b[1] - a[1])) {
console.log(` ${t}: ${n}`);
}
// Show sample failures per type
for (const [t, n] of Object.entries(errTypes).sort((a,b) => b[1] - a[1])) {
const samples = results.filter(r => !r.p).filter(r => {
const e = r.e || '';
if (t === 'crash') return e.includes('callFn');
if (t === 'stub') return e.includes('NOT IMPLEMENTED');
if (t === 'timeout') return e === 'TIMEOUT';
return false;
}).slice(0, 3);
for (const s of samples) console.log(` ${s.s}/${s.n}: ${(s.e||'').slice(0, 120)}`);
// Show ALL crash errors (deduplicated by error message)
const uniqueErrors = {};
for (const r of results.filter(r => !r.p)) {
const e = (r.e || '').slice(0, 100);
if (!uniqueErrors[e]) uniqueErrors[e] = { count: 0, example: r };
uniqueErrors[e].count++;
}
console.log(` Unique error messages (${Object.keys(uniqueErrors).length}):`);
for (const [e, info] of Object.entries(uniqueErrors).sort((a,b) => b[1].count - a[1].count).slice(0, 25)) {
console.log(` [${info.count}x] ${e}`);
}
// Show samples of "bar" error specifically
const barSamples = results.filter(r => !r.p && (r.e||'').endsWith('bar') || (r.e||'').endsWith('bar ')).slice(0, 8);
if (barSamples.length > 0) {
console.log(` "bar" error samples (${barSamples.length}):`);
for (const s of barSamples) console.log(` ${s.s}/${s.n}`);
}
expect(results.length).toBeGreaterThanOrEqual(830);
expect(results.length).toBeGreaterThanOrEqual(940);
expect(passed).toBeGreaterThanOrEqual(420);
});
});