aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authoralyx <alyx@aleteoryx.me>2022-06-08 17:43:49 +0000
committerAleteoryx <alyx@aleteoryx.me>2022-06-08 17:43:49 +0000
commitd286258f7c05b1a98f9a9aea388690a85246fd87 (patch)
treeadf06bde381b8839cf1b0ae522f55d9553986a1c /lib
parent7f6f5549fe6d1bd03772c13e96f9d92a26660929 (diff)
downloadRRCUtils-d286258f7c05b1a98f9a9aea388690a85246fd87.tar.gz
RRCUtils-d286258f7c05b1a98f9a9aea388690a85246fd87.tar.bz2
RRCUtils-d286258f7c05b1a98f9a9aea388690a85246fd87.zip
Type intersecting is fully implemented.
Diffstat (limited to 'lib')
-rw-r--r--lib/types.js222
1 files changed, 192 insertions, 30 deletions
diff --git a/lib/types.js b/lib/types.js
index 0253780..3fbb1c9 100644
--- a/lib/types.js
+++ b/lib/types.js
@@ -2,6 +2,7 @@
//TypeName: string, Params: Array<string>
function Type(TypeName,Params=[]) {
+ if (!Type.all) Type.all = {};
this.typename = "";
this.mode = "";
@@ -49,7 +50,6 @@ function Type(TypeName,Params=[]) {
}
if (workspace) types.push(workspace);
this.template = types.map(t => t.trim()).map(t => Params.includes(t) ? Type.any : new Type(t, Params));
-
}
//Template
else if (/^[^<]+<.+>$/.test(TypeName)) {
@@ -79,12 +79,20 @@ function Type(TypeName,Params=[]) {
workspace += c;
}
if (workspace) types.push(workspace);
- this.template = types.map(t => t.trim()).map(t => Params.includes(t) ? t : new Type(t, Params));
+ this.template = types.map(t => t.trim()).map(t => Params.includes(t) ? Type.any : new Type(t, Params));
+ Type.all[this.typename] = () => new Type(TypeName, Array.from(Params));
}
- //Standard Type
+ //Standard Type, or Template Param
else if (/^[^()|<>,]+$/.test(TypeName)) {
- this.typename = TypeName;
- this.mode = (TypeName == 'any') ? "any" : "standard"
+ if (!Params.includes(TypeName)) {
+ this.typename = TypeName;
+ this.mode = (TypeName == 'any') ? "any" : "standard";
+ if (this.mode == "standard") Type.all[this.typename] = () => new Type(TypeName);
+ } else {
+ this.mode = "param";
+ this.typename = TypeName;
+ this.template = Type.any;
+ }
}
}
@@ -99,49 +107,78 @@ Type.prototype.toString = function() {
case "any":
case "standard":
return this.typename;
+ case "param":
+ return this.template.toString();
}
}
-Type.prototype._resolve = function(idx, val) {
- if (val === undefined && this.template.length === 1) {
- val = idx;
- idx = 0;
- }
- if (0 <= idx && this.template.length > idx) {
- this.template = this.template?.map(t => (t === name) ? val.copy() : ((typeof t == 'string') ? t : t.resolve(name, val)))
+Type.prototype._resolve = function(key, val) {
+ if (this.mode != "param") {
+ if (val === undefined && this.template?.length === 1) {
+ val = key;
+ key = 0;
+ }
+ if (this.template?.length) this.template = this.template.map(t => t.resolve(key, val));
+ } else {
+ if (this.typename == key) this.template = new Type(val)
}
}
-Type.prototype.resolve = function(idx, val) {
+Type.prototype.resolve = function(key, val) {
const t = this.copy();
- t._resolve(idx, val);
+ t._resolve(key, val);
return t;
}
Type.prototype.copy = function() {
- const t = new Type(this.toString(), this.template?.filter(t => typeof t == 'string'));
- return t;
+ if (this.mode != "param") {
+ const t = new Type(this.toString());
+ for (const k in this.template)
+ t.template[k] = this.template[k].copy();
+ return t;
+ } else {
+ const t = new Type(this.typename, [this.typename]);
+ t._resolve(this.typename, this.toString())
+ return t;
+ }
}
-Type.prototype.intersect = function(ot, params=[]) {
- if (!ot instanceof Type) ot = new Type(ot, params)
- if (this.mode == 'any') return ot.copy();
- if (ot.mode == 'any') return this.copy();
- if (ot.mode != this.mode) return undefined;
-
- switch (ot.mode) {
+Type.prototype.getClasses = function() {
+ if (this.mode == "param") return this.template.getClasses();
+ const ret = new Set();
+ switch (this.mode) {
+ case 'standard':
+ switch (this.typename) {
+ case 'bool':
+ case 'int':
+ case 'float':
+ case 'string':
+ ret.add(this.typename);
+ break;
+ default:
+ ret.add('special');
+ break;
+ }
+ break;
case 'templated':
+ if (this.template.length == 1) {
+ if (this.typename == "List") ret.add("list");
+ this.template[0].getClasses().forEach(ret.add.bind(ret));
+ } else {
+ ret.add('any');
+ }
+
+ break;
case 'union':
case 'tuple':
- if (this.template.length != ot.template.length) return undefined;
- case 'standard':
- if (ot.typename == this.typename)
- return this.copy();
- else return undefined;
-
+ case 'any':
+ ret.add('any');
+ break;
}
+ return Array.from(ret);
}
+//just makes my life that much easier when writing code. Type.any is nicer than new Type('any')
Type.typeDef = (function(typename, name, params=[]) {
Object.defineProperty(this, typename, {
get: () => new Type(name, params)
@@ -159,4 +196,129 @@ Type.QuickTD("float");
Type.QuickTD("string");
Type.QuickTD("bool");
Type.QuickTD("Vector3");
-Type.QuickTD("List<T>", ["T"]); \ No newline at end of file
+Type.typeDef("list", "List<T>", ["T"]);
+
+function intersect(type1, type2) {
+ const T1 = new Type(type1.toString());
+ const T2 = new Type(type2.toString());
+
+ if(T1 == T2) return T1;
+
+ if (T1.mode == "any") return T2;
+ if (T2.mode == "any") return T1;
+ if (T1.mode == T2.mode) {
+ switch (T1.mode) {
+ case "standard":
+ return (String(T1) == String(T2)) ? T1 : null;
+ case "templated":
+ case "tuple":
+ if (
+ T1.typename != T2.typename ||
+ T1.template.length != T2.template.length
+ ) return null;
+ var t = T1.copy();
+ for (const k in T1.template)
+ t.template[k] = intersect(T1.template[k], T2.template[k]);
+ if (t.template.includes(null))
+ return null;
+ else return t;
+ case "union":
+ const rettempl = Array.from(
+ new Set(
+ T1.template.map(
+ t => T2.template
+ .map(t2 => intersect(t,t2))
+ .filter(t => t ?? false))
+ .flat().map(String)
+ )).map(n => new Type(n));
+ if (rettempl.length > 1) {
+ const ret = T1.copy();
+ ret.template = rettempl;
+ return ret;
+ }
+ else if (rettempl.length) return rettempl[0];
+ return null
+ }
+ } else if ([T1.mode,T2.mode].includes("union")) {
+ const UT = (T1.mode == "union") ? T1 : T2;
+ const ST = (T1.mode == "union") ? T2 : T1;
+ const isect = Array.from(
+ new Set(
+ UT.template
+ .map(t => intersect(ST, t))
+ .filter(t => t ?? false)
+ .map(String)
+ )).map(n => new Type(n));
+ if (isect.length > 1) {
+ const ret = UT.copy();
+ ret.template = isect;
+ return ret;
+ }
+ else if (isect.length) return isect[0];
+ }
+ return null;
+}
+function intersectParams(type1, type2) {
+ const sect = intersect(type1, type2);
+ if (!(sect ?? false)) return null;
+
+ function walkDown(t, tres) {
+ const ret = {};
+ if (t.mode == "param") {
+ ret[t.typename] = tres;
+ }
+ else if (
+ t.mode == tres.mode &&
+ t.typename == tres.typename &&
+ t.template?.length == tres.template?.length
+ ) {
+ for (const p in t.template)
+ ret = {...ret, ...walkDown(t.template[p], tres.template[p])};
+ }
+ return ret;
+ }
+ const resolution1 = walkDown(type1, sect);
+ const resolution2 = walkDown(type2, sect);
+ return [resolution1, resolution2];
+}
+
+function Chip(Entry) {
+ this.types = [];
+ if (Entry.ChipNameSource != "FirstNodeDesc") throw new TypeError("Chip is not FirstNodeDesc mode!");
+
+ const cur = Entry.NodeDescs[0];
+ const params = cur.ReadonlyTypeParams;
+ const passed = Object.keys(params)
+
+ this.root = newEl('div', 'chip');
+ const header = newEl('div', 'chipheader');
+ header.innerText = cur.Name;
+ const input = newEl('div', 'input');
+ const output = newEl('div', 'output');
+ this.root.append(header, input, output);
+ const genPort = (port) => {
+ let classes,name;
+ if (port.ReadonlyType != "exec") {
+ const type = Object.entries(params).reduce((t,[k,v])=>t.resolve(k,v),new Type(port.ReadonlyType, passed));
+ Object.entries(params).forEach(t => type.resolve(t[0], t[1]));
+ classes = type.getClasses();
+ name = type.toString();
+ this.types.push(type);
+ } else {
+ classes = ["exec"];
+ name = "exec";
+ }
+
+ const ret = newEl('div', '');
+ ret.innerHTML = port.Name + "&nbsp;";
+ classes.forEach(p => ret.classList.add(p))
+
+ const tooltip = newEl('div', 'type');
+ tooltip.innerText = name;
+
+ return [ret, tooltip];
+ }
+
+ input.append(...cur.Inputs.map(genPort).flat());
+ output.append(...cur.Outputs.map(genPort).flat());
+} \ No newline at end of file