spec: define-type special form — constructors, predicates, accessors (20 tests)

Adds sf-define-type via register-special-form! in spec/evaluator.sx.
ADT values are dicts {:_adt true :_type "T" :_ctor "C" :_fields (list ...)}.
Each define-type call registers: ctor functions with arity checking, Name?
type predicate, Ctor? constructor predicates, Ctor-field positional accessors,
and populates *adt-registry* dict with type→[ctor-names] mapping.
20/20 JS tests pass in spec/tests/test-adt.sx.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-26 17:56:50 +00:00
parent 3fb0212414
commit 6c87210728
4 changed files with 306 additions and 37 deletions

View File

@@ -31,7 +31,7 @@
// =========================================================================
var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } });
var SX_VERSION = "2026-04-26T17:04:43Z";
var SX_VERSION = "2026-04-26T17:41:33Z";
function isNil(x) { return x === NIL || x === null || x === undefined; }
function isSxTruthy(x) { return x !== false && !isNil(x); }
@@ -2155,6 +2155,34 @@ PRIMITIVES["step-sf-let-match"] = stepSfLetMatch;
})(); };
PRIMITIVES["step-eval-list"] = stepEvalList;
// sf-define-type
var sfDefineType = function(args, env) { return (function() {
var typeSym = first(args);
var ctorSpecs = rest(args);
return (function() {
var typeName = symbolName(typeSym);
var ctorNames = map(function(spec) { return symbolName(first(spec)); }, ctorSpecs);
if (isSxTruthy(!isSxTruthy(envHas(env, "*adt-registry*")))) {
envBind(env, "*adt-registry*", {});
}
envGet(env, "*adt-registry*")[typeName] = ctorNames;
envBind(env, (String(typeName) + String("?")), function(v) { return (isSxTruthy(isDict(v)) && isSxTruthy(get(v, "_adt")) && sxEq(get(v, "_type"), typeName)); });
{ var _c = ctorSpecs; for (var _i = 0; _i < _c.length; _i++) { var spec = _c[_i]; (function() {
var cn = symbolName(first(spec));
var fieldNames = map(function(f) { return symbolName(f); }, rest(spec));
var arity = len(rest(spec));
envBind(env, cn, function() { var ctorArgs = Array.prototype.slice.call(arguments, 0); return (isSxTruthy(!isSxTruthy(sxEq(len(ctorArgs), arity))) ? error((String(cn) + String(": expected ") + String(arity) + String(" args, got ") + String(len(ctorArgs)))) : {"_ctor": cn, "_type": typeName, "_adt": true, "_fields": ctorArgs}); });
envBind(env, (String(cn) + String("?")), function(v) { return (isSxTruthy(isDict(v)) && isSxTruthy(get(v, "_adt")) && sxEq(get(v, "_ctor"), cn)); });
return forEachIndexed(function(idx, fieldName) { return envBind(env, (String(cn) + String("-") + String(fieldName)), function(v) { return nth(get(v, "_fields"), idx); }); }, fieldNames);
})(); } }
return NIL;
})();
})(); };
PRIMITIVES["sf-define-type"] = sfDefineType;
// (register-special-form! ...)
registerSpecialForm("define-type", sfDefineType);
// kont-extract-provides
var kontExtractProvides = function(kont) { return (isSxTruthy(isEmpty(kont)) ? [] : (function() {
var frame = first(kont);