WIP: pre-existing changes from WASM browser work + test infrastructure
Accumulated changes from WASM browser development sessions: - sx_runtime.ml: signal subscription + notify, env unwrap tolerance - sx_browser.bc.js: rebuilt js_of_ocaml browser kernel - sx_browser.bc.wasm.js + assets: WASM browser kernel build - sx-platform.js browser tests (test_js, test_platform, test_wasm) - Playwright sx-inspect.js: interactive page inspector tool - harness-web.sx: DOM assertion updates - deploy.sh, Dockerfile, dune-project: build config updates - test-stepper.sx: stepper unit tests - reader-macro-demo plan update Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
// =========================================================================
|
||||
|
||||
var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } });
|
||||
var SX_VERSION = "2026-03-26T11:30:03Z";
|
||||
var SX_VERSION = "2026-03-26T16:13:53Z";
|
||||
|
||||
function isNil(x) { return x === NIL || x === null || x === undefined; }
|
||||
function isSxTruthy(x) { return x !== false && !isNil(x); }
|
||||
@@ -6583,6 +6583,400 @@ PRIMITIVES["bridge-event"] = bridgeEvent;
|
||||
PRIMITIVES["resource"] = resource;
|
||||
|
||||
|
||||
// === Transpiled from types (gradual type system) ===
|
||||
|
||||
// base-types
|
||||
var baseTypes = ["number", "string", "boolean", "nil", "symbol", "keyword", "element", "any", "never", "list", "dict", "lambda", "component", "island", "macro", "signal"];
|
||||
PRIMITIVES["base-types"] = baseTypes;
|
||||
|
||||
// type-any?
|
||||
var typeAny_p = function(t) { return (t == "any"); };
|
||||
PRIMITIVES["type-any?"] = typeAny_p;
|
||||
|
||||
// type-never?
|
||||
var typeNever_p = function(t) { return (t == "never"); };
|
||||
PRIMITIVES["type-never?"] = typeNever_p;
|
||||
|
||||
// type-nullable?
|
||||
var typeNullable_p = function(t) { return (isSxTruthy((t == "any")) ? true : (isSxTruthy((t == "nil")) ? true : (isSxTruthy((isSxTruthy((typeOf(t) == "string")) && endsWith(t, "?"))) ? true : (isSxTruthy((isSxTruthy((typeOf(t) == "list")) && isSxTruthy(!isSxTruthy(isEmpty(t))) && (first(t) == "or"))) ? contains(rest(t), "nil") : false)))); };
|
||||
PRIMITIVES["type-nullable?"] = typeNullable_p;
|
||||
|
||||
// nullable-base
|
||||
var nullableBase = function(t) { return (isSxTruthy((isSxTruthy((typeOf(t) == "string")) && isSxTruthy(endsWith(t, "?")) && !isSxTruthy((t == "?")))) ? slice(t, 0, (stringLength(t) - 1)) : t); };
|
||||
PRIMITIVES["nullable-base"] = nullableBase;
|
||||
|
||||
// subtype?
|
||||
var subtype_p = function(a, b) { return (isSxTruthy(typeAny_p(b)) ? true : (isSxTruthy(typeNever_p(a)) ? true : (isSxTruthy(typeAny_p(a)) ? false : (isSxTruthy((a == b)) ? true : (isSxTruthy((a == "nil")) ? typeNullable_p(b) : (isSxTruthy((isSxTruthy((typeOf(b) == "string")) && endsWith(b, "?"))) ? (function() {
|
||||
var base = nullableBase(b);
|
||||
return sxOr((a == base), (a == "nil"));
|
||||
})() : (isSxTruthy((isSxTruthy((typeOf(a) == "list")) && isSxTruthy(!isSxTruthy(isEmpty(a))) && (first(a) == "or"))) ? isEvery(function(member) { return subtype_p(member, b); }, rest(a)) : (isSxTruthy((isSxTruthy((typeOf(b) == "list")) && isSxTruthy(!isSxTruthy(isEmpty(b))) && (first(b) == "or"))) ? some(function(member) { return subtype_p(a, member); }, rest(b)) : (isSxTruthy((isSxTruthy((typeOf(a) == "list")) && isSxTruthy((typeOf(b) == "list")) && isSxTruthy((len(a) == 2)) && isSxTruthy((len(b) == 2)) && isSxTruthy((first(a) == "list-of")) && (first(b) == "list-of"))) ? subtype_p(nth(a, 1), nth(b, 1)) : (isSxTruthy((isSxTruthy((a == "list")) && isSxTruthy((typeOf(b) == "list")) && isSxTruthy((len(b) == 2)) && (first(b) == "list-of"))) ? typeAny_p(nth(b, 1)) : (isSxTruthy((isSxTruthy((typeOf(a) == "list")) && isSxTruthy((len(a) == 2)) && isSxTruthy((first(a) == "list-of")) && (b == "list"))) ? true : false))))))))))); };
|
||||
PRIMITIVES["subtype?"] = subtype_p;
|
||||
|
||||
// type-union
|
||||
var typeUnion = function(a, b) { return (isSxTruthy((a == b)) ? a : (isSxTruthy(typeAny_p(a)) ? "any" : (isSxTruthy(typeAny_p(b)) ? "any" : (isSxTruthy(typeNever_p(a)) ? b : (isSxTruthy(typeNever_p(b)) ? a : (isSxTruthy(subtype_p(a, b)) ? b : (isSxTruthy(subtype_p(b, a)) ? a : (isSxTruthy((a == "nil")) ? (isSxTruthy((isSxTruthy((typeOf(b) == "string")) && !isSxTruthy(endsWith(b, "?")))) ? (String(b) + String("?")) : ["or", a, b]) : (isSxTruthy((b == "nil")) ? (isSxTruthy((isSxTruthy((typeOf(a) == "string")) && !isSxTruthy(endsWith(a, "?")))) ? (String(a) + String("?")) : ["or", a, b]) : ["or", a, b]))))))))); };
|
||||
PRIMITIVES["type-union"] = typeUnion;
|
||||
|
||||
// narrow-type
|
||||
var narrowType = function(t, predicateName) { return (isSxTruthy((predicateName == "nil?")) ? ["nil", narrowExcludeNil(t)] : (isSxTruthy((predicateName == "string?")) ? ["string", narrowExclude(t, "string")] : (isSxTruthy((predicateName == "number?")) ? ["number", narrowExclude(t, "number")] : (isSxTruthy((predicateName == "list?")) ? ["list", narrowExclude(t, "list")] : (isSxTruthy((predicateName == "dict?")) ? ["dict", narrowExclude(t, "dict")] : (isSxTruthy((predicateName == "boolean?")) ? ["boolean", narrowExclude(t, "boolean")] : [t, t])))))); };
|
||||
PRIMITIVES["narrow-type"] = narrowType;
|
||||
|
||||
// narrow-exclude-nil
|
||||
var narrowExcludeNil = function(t) { return (isSxTruthy((t == "nil")) ? "never" : (isSxTruthy((t == "any")) ? "any" : (isSxTruthy((isSxTruthy((typeOf(t) == "string")) && endsWith(t, "?"))) ? nullableBase(t) : (isSxTruthy((isSxTruthy((typeOf(t) == "list")) && isSxTruthy(!isSxTruthy(isEmpty(t))) && (first(t) == "or"))) ? (function() {
|
||||
var members = filter(function(m) { return !isSxTruthy((m == "nil")); }, rest(t));
|
||||
return (isSxTruthy((len(members) == 1)) ? first(members) : (isSxTruthy(isEmpty(members)) ? "never" : cons("or", members)));
|
||||
})() : t)))); };
|
||||
PRIMITIVES["narrow-exclude-nil"] = narrowExcludeNil;
|
||||
|
||||
// narrow-exclude
|
||||
var narrowExclude = function(t, excluded) { return (isSxTruthy((t == excluded)) ? "never" : (isSxTruthy((t == "any")) ? "any" : (isSxTruthy((isSxTruthy((typeOf(t) == "list")) && isSxTruthy(!isSxTruthy(isEmpty(t))) && (first(t) == "or"))) ? (function() {
|
||||
var members = filter(function(m) { return !isSxTruthy((m == excluded)); }, rest(t));
|
||||
return (isSxTruthy((len(members) == 1)) ? first(members) : (isSxTruthy(isEmpty(members)) ? "never" : cons("or", members)));
|
||||
})() : t))); };
|
||||
PRIMITIVES["narrow-exclude"] = narrowExclude;
|
||||
|
||||
// infer-type
|
||||
var inferType = function(node, typeEnv, primTypes, typeRegistry) { return (function() {
|
||||
var kind = typeOf(node);
|
||||
return (isSxTruthy((kind == "number")) ? "number" : (isSxTruthy((kind == "string")) ? "string" : (isSxTruthy((kind == "boolean")) ? "boolean" : (isSxTruthy(isNil(node)) ? "nil" : (isSxTruthy((kind == "keyword")) ? "keyword" : (isSxTruthy((kind == "symbol")) ? (function() {
|
||||
var name = symbolName(node);
|
||||
return (isSxTruthy(dictHas(typeEnv, name)) ? get(typeEnv, name) : (isSxTruthy((name == "true")) ? "boolean" : (isSxTruthy((name == "false")) ? "boolean" : (isSxTruthy((name == "nil")) ? "nil" : (isSxTruthy(dictHas(primTypes, name)) ? get(primTypes, name) : "any")))));
|
||||
})() : (isSxTruthy((kind == "dict")) ? "dict" : (isSxTruthy((kind == "list")) ? inferListType(node, typeEnv, primTypes, typeRegistry) : "any"))))))));
|
||||
})(); };
|
||||
PRIMITIVES["infer-type"] = inferType;
|
||||
|
||||
// infer-list-type
|
||||
var inferListType = function(node, typeEnv, primTypes, typeRegistry) { return (isSxTruthy(isEmpty(node)) ? "list" : (function() {
|
||||
var head = first(node);
|
||||
var args = rest(node);
|
||||
return (isSxTruthy(!isSxTruthy((typeOf(head) == "symbol"))) ? "any" : (function() {
|
||||
var name = symbolName(head);
|
||||
return (isSxTruthy((name == "if")) ? inferIfType(args, typeEnv, primTypes, typeRegistry) : (isSxTruthy((name == "when")) ? (isSxTruthy((len(args) >= 2)) ? typeUnion(inferType(last(args), typeEnv, primTypes, typeRegistry), "nil") : "nil") : (isSxTruthy(sxOr((name == "cond"), (name == "case"))) ? "any" : (isSxTruthy((name == "let")) ? inferLetType(args, typeEnv, primTypes, typeRegistry) : (isSxTruthy(sxOr((name == "do"), (name == "begin"))) ? (isSxTruthy(isEmpty(args)) ? "nil" : inferType(last(args), typeEnv, primTypes, typeRegistry)) : (isSxTruthy(sxOr((name == "lambda"), (name == "fn"))) ? "lambda" : (isSxTruthy((name == "and")) ? (isSxTruthy(isEmpty(args)) ? "boolean" : inferType(last(args), typeEnv, primTypes, typeRegistry)) : (isSxTruthy((name == "or")) ? (isSxTruthy(isEmpty(args)) ? "boolean" : reduce(typeUnion, "never", map(function(a) { return inferType(a, typeEnv, primTypes, typeRegistry); }, args))) : (isSxTruthy((name == "map")) ? (isSxTruthy((len(args) >= 2)) ? (function() {
|
||||
var fnType = inferType(first(args), typeEnv, primTypes, typeRegistry);
|
||||
return (isSxTruthy((isSxTruthy((typeOf(fnType) == "list")) && (first(fnType) == "->"))) ? ["list-of", last(fnType)] : "list");
|
||||
})() : "list") : (isSxTruthy((name == "filter")) ? (isSxTruthy((len(args) >= 2)) ? inferType(nth(args, 1), typeEnv, primTypes, typeRegistry) : "list") : (isSxTruthy((name == "reduce")) ? "any" : (isSxTruthy((name == "list")) ? "list" : (isSxTruthy((name == "dict")) ? "dict" : (isSxTruthy((name == "quote")) ? "any" : (isSxTruthy((name == "str")) ? "string" : (isSxTruthy((name == "not")) ? "boolean" : (isSxTruthy((name == "get")) ? (isSxTruthy((isSxTruthy((len(args) >= 2)) && !isSxTruthy(isNil(typeRegistry)))) ? (function() {
|
||||
var dictType = inferType(first(args), typeEnv, primTypes, typeRegistry);
|
||||
var keyArg = nth(args, 1);
|
||||
var keyName = (isSxTruthy((typeOf(keyArg) == "keyword")) ? keywordName(keyArg) : (isSxTruthy((typeOf(keyArg) == "string")) ? keyArg : NIL));
|
||||
return (isSxTruthy((isSxTruthy(keyName) && isSxTruthy((typeOf(dictType) == "string")) && dictHas(typeRegistry, dictType))) ? (function() {
|
||||
var resolved = resolveType(dictType, typeRegistry);
|
||||
return (isSxTruthy((isSxTruthy((typeOf(resolved) == "dict")) && dictHas(resolved, keyName))) ? get(resolved, keyName) : "any");
|
||||
})() : "any");
|
||||
})() : "any") : (isSxTruthy(startsWith(name, "~")) ? "element" : (isSxTruthy(dictHas(primTypes, name)) ? get(primTypes, name) : "any")))))))))))))))))));
|
||||
})());
|
||||
})()); };
|
||||
PRIMITIVES["infer-list-type"] = inferListType;
|
||||
|
||||
// infer-if-type
|
||||
var inferIfType = function(args, typeEnv, primTypes, typeRegistry) { return (isSxTruthy((len(args) < 2)) ? "nil" : (function() {
|
||||
var thenType = inferType(nth(args, 1), typeEnv, primTypes, typeRegistry);
|
||||
return (isSxTruthy((len(args) >= 3)) ? typeUnion(thenType, inferType(nth(args, 2), typeEnv, primTypes, typeRegistry)) : typeUnion(thenType, "nil"));
|
||||
})()); };
|
||||
PRIMITIVES["infer-if-type"] = inferIfType;
|
||||
|
||||
// infer-let-type
|
||||
var inferLetType = function(args, typeEnv, primTypes, typeRegistry) { return (isSxTruthy((len(args) < 2)) ? "nil" : (function() {
|
||||
var bindings = first(args);
|
||||
var body = last(args);
|
||||
var extended = merge(typeEnv, {});
|
||||
{ var _c = bindings; for (var _i = 0; _i < _c.length; _i++) { var binding = _c[_i]; if (isSxTruthy((isSxTruthy((typeOf(binding) == "list")) && (len(binding) >= 2)))) {
|
||||
(function() {
|
||||
var name = (isSxTruthy((typeOf(first(binding)) == "symbol")) ? symbolName(first(binding)) : (String(first(binding))));
|
||||
var valType = inferType(nth(binding, 1), extended, primTypes, typeRegistry);
|
||||
return dictSet(extended, name, valType);
|
||||
})();
|
||||
} } }
|
||||
return inferType(body, extended, primTypes, typeRegistry);
|
||||
})()); };
|
||||
PRIMITIVES["infer-let-type"] = inferLetType;
|
||||
|
||||
// make-diagnostic
|
||||
var makeDiagnostic = function(level, message, component, expr) { return {"level": level, "component": component, "expr": expr, "message": message}; };
|
||||
PRIMITIVES["make-diagnostic"] = makeDiagnostic;
|
||||
|
||||
// check-primitive-call
|
||||
var checkPrimitiveCall = function(name, args, typeEnv, primTypes, primParamTypes, compName, typeRegistry) { return (function() {
|
||||
var diagnostics = [];
|
||||
if (isSxTruthy((isSxTruthy(!isSxTruthy(isNil(primParamTypes))) && dictHas(primParamTypes, name)))) {
|
||||
(function() {
|
||||
var sig = get(primParamTypes, name);
|
||||
var positional = get(sig, "positional");
|
||||
var restType = get(sig, "rest-type");
|
||||
return forEach(function(idx) { return (isSxTruthy((idx < len(args))) ? (isSxTruthy((idx < len(positional))) ? (function() {
|
||||
var paramInfo = nth(positional, idx);
|
||||
var argExpr = nth(args, idx);
|
||||
return (function() {
|
||||
var expectedType = nth(paramInfo, 1);
|
||||
return (isSxTruthy(!isSxTruthy(isNil(expectedType))) ? (function() {
|
||||
var actual = inferType(argExpr, typeEnv, primTypes, typeRegistry);
|
||||
return (isSxTruthy((isSxTruthy(!isSxTruthy(typeAny_p(expectedType))) && isSxTruthy(!isSxTruthy(typeAny_p(actual))) && !isSxTruthy(subtypeResolved_p(actual, expectedType, typeRegistry)))) ? append_b(diagnostics, makeDiagnostic("error", (String("Argument ") + String((idx + 1)) + String(" of `") + String(name) + String("` expects ") + String(expectedType) + String(", got ") + String(actual)), compName, argExpr)) : NIL);
|
||||
})() : NIL);
|
||||
})();
|
||||
})() : (isSxTruthy(!isSxTruthy(isNil(restType))) ? (function() {
|
||||
var argExpr = nth(args, idx);
|
||||
var actual = inferType(argExpr, typeEnv, primTypes, typeRegistry);
|
||||
return (isSxTruthy((isSxTruthy(!isSxTruthy(typeAny_p(restType))) && isSxTruthy(!isSxTruthy(typeAny_p(actual))) && !isSxTruthy(subtypeResolved_p(actual, restType, typeRegistry)))) ? append_b(diagnostics, makeDiagnostic("error", (String("Argument ") + String((idx + 1)) + String(" of `") + String(name) + String("` expects ") + String(restType) + String(", got ") + String(actual)), compName, argExpr)) : NIL);
|
||||
})() : NIL)) : NIL); }, range(0, len(args), 1));
|
||||
})();
|
||||
}
|
||||
return diagnostics;
|
||||
})(); };
|
||||
PRIMITIVES["check-primitive-call"] = checkPrimitiveCall;
|
||||
|
||||
// check-component-call
|
||||
var checkComponentCall = function(compName, comp, callArgs, typeEnv, primTypes, typeRegistry) { return (function() {
|
||||
var diagnostics = [];
|
||||
var paramTypes = componentParamTypes(comp);
|
||||
var params = componentParams(comp);
|
||||
if (isSxTruthy((isSxTruthy(!isSxTruthy(isNil(paramTypes))) && !isSxTruthy(isEmpty(keys(paramTypes)))))) {
|
||||
(function() {
|
||||
var i = 0;
|
||||
var providedKeys = [];
|
||||
{ var _c = range(0, len(callArgs), 1); for (var _i = 0; _i < _c.length; _i++) { var idx = _c[_i]; if (isSxTruthy((idx < len(callArgs)))) {
|
||||
(function() {
|
||||
var arg = nth(callArgs, idx);
|
||||
return (isSxTruthy((typeOf(arg) == "keyword")) ? (function() {
|
||||
var keyName = keywordName(arg);
|
||||
providedKeys.push(keyName);
|
||||
return (isSxTruthy(((idx + 1) < len(callArgs))) ? (function() {
|
||||
var valExpr = nth(callArgs, (idx + 1));
|
||||
return (isSxTruthy(dictHas(paramTypes, keyName)) ? (function() {
|
||||
var expected = get(paramTypes, keyName);
|
||||
var actual = inferType(valExpr, typeEnv, primTypes, typeRegistry);
|
||||
return (isSxTruthy((isSxTruthy(!isSxTruthy(typeAny_p(expected))) && isSxTruthy(!isSxTruthy(typeAny_p(actual))) && !isSxTruthy(subtypeResolved_p(actual, expected, typeRegistry)))) ? append_b(diagnostics, makeDiagnostic("error", (String("Keyword :") + String(keyName) + String(" of ") + String(compName) + String(" expects ") + String(expected) + String(", got ") + String(actual)), compName, valExpr)) : NIL);
|
||||
})() : NIL);
|
||||
})() : NIL);
|
||||
})() : NIL);
|
||||
})();
|
||||
} } }
|
||||
{ var _c = params; for (var _i = 0; _i < _c.length; _i++) { var paramName = _c[_i]; if (isSxTruthy((isSxTruthy(dictHas(paramTypes, paramName)) && isSxTruthy(!isSxTruthy(contains(providedKeys, paramName))) && !isSxTruthy(typeNullable_p(get(paramTypes, paramName)))))) {
|
||||
diagnostics.push(makeDiagnostic("warning", (String("Required param :") + String(paramName) + String(" of ") + String(compName) + String(" not provided")), compName, NIL));
|
||||
} } }
|
||||
return forEach(function(key) { return (isSxTruthy(!isSxTruthy(contains(params, key))) ? append_b(diagnostics, makeDiagnostic("warning", (String("Unknown keyword :") + String(key) + String(" passed to ") + String(compName)), compName, NIL)) : NIL); }, providedKeys);
|
||||
})();
|
||||
}
|
||||
return diagnostics;
|
||||
})(); };
|
||||
PRIMITIVES["check-component-call"] = checkComponentCall;
|
||||
|
||||
// check-body-walk
|
||||
var checkBodyWalk = function(node, compName, typeEnv, primTypes, primParamTypes, env, diagnostics, typeRegistry, effectAnnotations) { return (function() {
|
||||
var kind = typeOf(node);
|
||||
return (isSxTruthy((kind == "list")) ? (isSxTruthy(!isSxTruthy(isEmpty(node))) ? (function() {
|
||||
var head = first(node);
|
||||
var args = rest(node);
|
||||
if (isSxTruthy((typeOf(head) == "symbol"))) {
|
||||
(function() {
|
||||
var name = symbolName(head);
|
||||
if (isSxTruthy(startsWith(name, "~"))) {
|
||||
(function() {
|
||||
var compVal = envGet(env, name);
|
||||
return (isSxTruthy((typeOf(compVal) == "component")) ? forEach(function(d) { return append_b(diagnostics, d); }, checkComponentCall(name, compVal, args, typeEnv, primTypes, typeRegistry)) : NIL);
|
||||
})();
|
||||
if (isSxTruthy(!isSxTruthy(isNil(effectAnnotations)))) {
|
||||
(function() {
|
||||
var callerEffects = getEffects(compName, effectAnnotations);
|
||||
return forEach(function(d) { return append_b(diagnostics, d); }, checkEffectCall(name, callerEffects, effectAnnotations, compName));
|
||||
})();
|
||||
}
|
||||
}
|
||||
if (isSxTruthy((isSxTruthy(!isSxTruthy(startsWith(name, "~"))) && isSxTruthy(!isSxTruthy(isNil(primParamTypes))) && dictHas(primParamTypes, name)))) {
|
||||
{ var _c = checkPrimitiveCall(name, args, typeEnv, primTypes, primParamTypes, compName, typeRegistry); for (var _i = 0; _i < _c.length; _i++) { var d = _c[_i]; diagnostics.push(d); } }
|
||||
}
|
||||
if (isSxTruthy((isSxTruthy(!isSxTruthy(startsWith(name, "~"))) && !isSxTruthy(isNil(effectAnnotations))))) {
|
||||
(function() {
|
||||
var callerEffects = getEffects(compName, effectAnnotations);
|
||||
return forEach(function(d) { return append_b(diagnostics, d); }, checkEffectCall(name, callerEffects, effectAnnotations, compName));
|
||||
})();
|
||||
}
|
||||
if (isSxTruthy(sxOr((name == "let"), (name == "let*")))) {
|
||||
if (isSxTruthy((len(args) >= 2))) {
|
||||
(function() {
|
||||
var bindings = first(args);
|
||||
var bodyExprs = rest(args);
|
||||
var extended = merge(typeEnv, {});
|
||||
{ var _c = bindings; for (var _i = 0; _i < _c.length; _i++) { var binding = _c[_i]; if (isSxTruthy((isSxTruthy((typeOf(binding) == "list")) && (len(binding) >= 2)))) {
|
||||
(function() {
|
||||
var bname = (isSxTruthy((typeOf(first(binding)) == "symbol")) ? symbolName(first(binding)) : (String(first(binding))));
|
||||
var valType = inferType(nth(binding, 1), extended, primTypes, typeRegistry);
|
||||
return dictSet(extended, bname, valType);
|
||||
})();
|
||||
} } }
|
||||
return forEach(function(body) { return checkBodyWalk(body, compName, extended, primTypes, primParamTypes, env, diagnostics, typeRegistry, effectAnnotations); }, bodyExprs);
|
||||
})();
|
||||
}
|
||||
}
|
||||
return (isSxTruthy((name == "define")) ? (isSxTruthy((len(args) >= 2)) ? (function() {
|
||||
var defName = (isSxTruthy((typeOf(first(args)) == "symbol")) ? symbolName(first(args)) : NIL);
|
||||
var defVal = nth(args, 1);
|
||||
if (isSxTruthy(defName)) {
|
||||
typeEnv[defName] = inferType(defVal, typeEnv, primTypes, typeRegistry);
|
||||
}
|
||||
return checkBodyWalk(defVal, compName, typeEnv, primTypes, primParamTypes, env, diagnostics, typeRegistry, effectAnnotations);
|
||||
})() : NIL) : NIL);
|
||||
})();
|
||||
}
|
||||
return forEach(function(child) { return checkBodyWalk(child, compName, typeEnv, primTypes, primParamTypes, env, diagnostics, typeRegistry, effectAnnotations); }, args);
|
||||
})() : NIL) : NIL);
|
||||
})(); };
|
||||
PRIMITIVES["check-body-walk"] = checkBodyWalk;
|
||||
|
||||
// check-component
|
||||
var checkComponent = function(compName, env, primTypes, primParamTypes, typeRegistry, effectAnnotations) { return (function() {
|
||||
var comp = envGet(env, compName);
|
||||
var diagnostics = [];
|
||||
if (isSxTruthy((typeOf(comp) == "component"))) {
|
||||
(function() {
|
||||
var body = componentBody(comp);
|
||||
var params = componentParams(comp);
|
||||
var paramTypes = componentParamTypes(comp);
|
||||
var typeEnv = {};
|
||||
{ var _c = params; for (var _i = 0; _i < _c.length; _i++) { var p = _c[_i]; typeEnv[p] = (isSxTruthy((isSxTruthy(!isSxTruthy(isNil(paramTypes))) && dictHas(paramTypes, p))) ? get(paramTypes, p) : "any"); } }
|
||||
if (isSxTruthy(componentHasChildren(comp))) {
|
||||
typeEnv["children"] = ["list-of", "element"];
|
||||
}
|
||||
return checkBodyWalk(body, compName, typeEnv, primTypes, primParamTypes, env, diagnostics, typeRegistry, effectAnnotations);
|
||||
})();
|
||||
}
|
||||
return diagnostics;
|
||||
})(); };
|
||||
PRIMITIVES["check-component"] = checkComponent;
|
||||
|
||||
// check-all
|
||||
var checkAll = function(env, primTypes, primParamTypes, typeRegistry, effectAnnotations) { return (function() {
|
||||
var allDiagnostics = [];
|
||||
{ var _c = keys(env); for (var _i = 0; _i < _c.length; _i++) { var name = _c[_i]; (function() {
|
||||
var val = envGet(env, name);
|
||||
return (isSxTruthy((typeOf(val) == "component")) ? forEach(function(d) { return append_b(allDiagnostics, d); }, checkComponent(name, env, primTypes, primParamTypes, typeRegistry, effectAnnotations)) : NIL);
|
||||
})(); } }
|
||||
return allDiagnostics;
|
||||
})(); };
|
||||
PRIMITIVES["check-all"] = checkAll;
|
||||
|
||||
// build-type-registry
|
||||
var buildTypeRegistry = function(primDeclarations, ioDeclarations) { return (function() {
|
||||
var registry = {};
|
||||
{ var _c = primDeclarations; for (var _i = 0; _i < _c.length; _i++) { var decl = _c[_i]; (function() {
|
||||
var name = get(decl, "name");
|
||||
var returns = get(decl, "returns");
|
||||
return (isSxTruthy((isSxTruthy(!isSxTruthy(isNil(name))) && !isSxTruthy(isNil(returns)))) ? dictSet(registry, name, returns) : NIL);
|
||||
})(); } }
|
||||
{ var _c = ioDeclarations; for (var _i = 0; _i < _c.length; _i++) { var decl = _c[_i]; (function() {
|
||||
var name = get(decl, "name");
|
||||
var returns = get(decl, "returns");
|
||||
return (isSxTruthy((isSxTruthy(!isSxTruthy(isNil(name))) && !isSxTruthy(isNil(returns)))) ? dictSet(registry, name, returns) : NIL);
|
||||
})(); } }
|
||||
return registry;
|
||||
})(); };
|
||||
PRIMITIVES["build-type-registry"] = buildTypeRegistry;
|
||||
|
||||
// type-def-name
|
||||
var typeDefName = function(td) { return get(td, "name"); };
|
||||
PRIMITIVES["type-def-name"] = typeDefName;
|
||||
|
||||
// type-def-params
|
||||
var typeDefParams = function(td) { return get(td, "params"); };
|
||||
PRIMITIVES["type-def-params"] = typeDefParams;
|
||||
|
||||
// type-def-body
|
||||
var typeDefBody = function(td) { return get(td, "body"); };
|
||||
PRIMITIVES["type-def-body"] = typeDefBody;
|
||||
|
||||
// resolve-type
|
||||
var resolveType = function(t, registry) { return (isSxTruthy(isNil(registry)) ? t : (isSxTruthy((typeOf(t) == "string")) ? (isSxTruthy(dictHas(registry, t)) ? (function() {
|
||||
var td = get(registry, t);
|
||||
return (function() {
|
||||
var params = typeDefParams(td);
|
||||
var body = typeDefBody(td);
|
||||
return (isSxTruthy(isEmpty(params)) ? resolveType(body, registry) : t);
|
||||
})();
|
||||
})() : t) : (isSxTruthy((typeOf(t) == "list")) ? (isSxTruthy(isEmpty(t)) ? t : (function() {
|
||||
var head = first(t);
|
||||
return (isSxTruthy(sxOr((head == "or"), (head == "list-of"), (head == "->"), (head == "dict-of"))) ? cons(head, map(function(m) { return resolveType(m, registry); }, rest(t))) : (isSxTruthy((isSxTruthy((typeOf(head) == "string")) && dictHas(registry, head))) ? (function() {
|
||||
var td = get(registry, head);
|
||||
var params = typeDefParams(td);
|
||||
var body = typeDefBody(td);
|
||||
var args = rest(t);
|
||||
return (isSxTruthy((len(params) == len(args))) ? resolveType(substituteTypeVars(body, params, args), registry) : t);
|
||||
})() : t));
|
||||
})()) : (isSxTruthy((typeOf(t) == "dict")) ? mapDict(function(k, v) { return resolveType(v, registry); }, t) : t)))); };
|
||||
PRIMITIVES["resolve-type"] = resolveType;
|
||||
|
||||
// substitute-type-vars
|
||||
var substituteTypeVars = function(body, params, args) { return (function() {
|
||||
var subst = {};
|
||||
{ var _c = range(0, len(params), 1); for (var _i = 0; _i < _c.length; _i++) { var i = _c[_i]; subst[nth(params, i)] = nth(args, i); } }
|
||||
return substituteInType(body, subst);
|
||||
})(); };
|
||||
PRIMITIVES["substitute-type-vars"] = substituteTypeVars;
|
||||
|
||||
// substitute-in-type
|
||||
var substituteInType = function(t, subst) { return (isSxTruthy((typeOf(t) == "string")) ? (isSxTruthy(dictHas(subst, t)) ? get(subst, t) : t) : (isSxTruthy((typeOf(t) == "list")) ? map(function(m) { return substituteInType(m, subst); }, t) : (isSxTruthy((typeOf(t) == "dict")) ? mapDict(function(k, v) { return substituteInType(v, subst); }, t) : t))); };
|
||||
PRIMITIVES["substitute-in-type"] = substituteInType;
|
||||
|
||||
// subtype-resolved?
|
||||
var subtypeResolved_p = function(a, b, registry) { return (isSxTruthy(isNil(registry)) ? subtype_p(a, b) : (function() {
|
||||
var ra = resolveType(a, registry);
|
||||
var rb = resolveType(b, registry);
|
||||
return (isSxTruthy((isSxTruthy((typeOf(ra) == "dict")) && (typeOf(rb) == "dict"))) ? isEvery(function(key) { return (isSxTruthy(dictHas(ra, key)) && subtypeResolved_p(get(ra, key), get(rb, key), registry)); }, keys(rb)) : subtype_p(ra, rb));
|
||||
})()); };
|
||||
PRIMITIVES["subtype-resolved?"] = subtypeResolved_p;
|
||||
|
||||
// get-effects
|
||||
var getEffects = function(name, effectAnnotations) { return (isSxTruthy(isNil(effectAnnotations)) ? NIL : (isSxTruthy(dictHas(effectAnnotations, name)) ? get(effectAnnotations, name) : NIL)); };
|
||||
PRIMITIVES["get-effects"] = getEffects;
|
||||
|
||||
// effects-subset?
|
||||
var effectsSubset_p = function(calleeEffects, callerEffects) { return (isSxTruthy(isNil(callerEffects)) ? true : (isSxTruthy(isNil(calleeEffects)) ? true : isEvery(function(e) { return contains(callerEffects, e); }, calleeEffects))); };
|
||||
PRIMITIVES["effects-subset?"] = effectsSubset_p;
|
||||
|
||||
// check-effect-call
|
||||
var checkEffectCall = function(calleeName, callerEffects, effectAnnotations, compName) { return (function() {
|
||||
var diagnostics = [];
|
||||
var calleeEffects = getEffects(calleeName, effectAnnotations);
|
||||
if (isSxTruthy((isSxTruthy(!isSxTruthy(isNil(callerEffects))) && isSxTruthy(!isSxTruthy(isNil(calleeEffects))) && !isSxTruthy(effectsSubset_p(calleeEffects, callerEffects))))) {
|
||||
diagnostics.push(makeDiagnostic("error", (String("`") + String(calleeName) + String("` has effects ") + String(join(", ", calleeEffects)) + String(" but `") + String(compName) + String("` only allows ") + String((isSxTruthy(isEmpty(callerEffects)) ? "[pure]" : join(", ", callerEffects)))), compName, NIL));
|
||||
}
|
||||
return diagnostics;
|
||||
})(); };
|
||||
PRIMITIVES["check-effect-call"] = checkEffectCall;
|
||||
|
||||
// build-effect-annotations
|
||||
var buildEffectAnnotations = function(ioDeclarations) { return (function() {
|
||||
var annotations = {};
|
||||
{ var _c = ioDeclarations; for (var _i = 0; _i < _c.length; _i++) { var decl = _c[_i]; (function() {
|
||||
var name = get(decl, "name");
|
||||
return (isSxTruthy(!isSxTruthy(isNil(name))) ? dictSet(annotations, name, ["io"]) : NIL);
|
||||
})(); } }
|
||||
return annotations;
|
||||
})(); };
|
||||
PRIMITIVES["build-effect-annotations"] = buildEffectAnnotations;
|
||||
|
||||
// check-component-effects
|
||||
var checkComponentEffects = function(compName, env, effectAnnotations) { return (function() {
|
||||
var comp = envGet(env, compName);
|
||||
var diagnostics = [];
|
||||
if (isSxTruthy((typeOf(comp) == "component"))) {
|
||||
(function() {
|
||||
var body = componentBody(comp);
|
||||
return checkBodyWalk(body, compName, {}, {}, NIL, env, diagnostics, NIL, effectAnnotations);
|
||||
})();
|
||||
}
|
||||
return diagnostics;
|
||||
})(); };
|
||||
PRIMITIVES["check-component-effects"] = checkComponentEffects;
|
||||
|
||||
// check-all-effects
|
||||
var checkAllEffects = function(env, effectAnnotations) { return (function() {
|
||||
var allDiagnostics = [];
|
||||
{ var _c = keys(env); for (var _i = 0; _i < _c.length; _i++) { var name = _c[_i]; (function() {
|
||||
var val = envGet(env, name);
|
||||
return (isSxTruthy((typeOf(val) == "component")) ? forEach(function(d) { return append_b(allDiagnostics, d); }, checkComponentEffects(name, env, effectAnnotations)) : NIL);
|
||||
})(); } }
|
||||
return allDiagnostics;
|
||||
})(); };
|
||||
PRIMITIVES["check-all-effects"] = checkAllEffects;
|
||||
|
||||
|
||||
// =========================================================================
|
||||
// Post-transpilation fixups
|
||||
// =========================================================================
|
||||
|
||||
Reference in New Issue
Block a user