From 0490ebfefc2fb9383f8ff3a3302af2842e928377 Mon Sep 17 00:00:00 2001
From: alyx <alyx@aleteoryx.me>
Date: Wed, 8 Jun 2022 22:02:16 +0000
Subject: moved types.js to chips2.js, added a basic copy of the old logic,
 written with the new type parser

---
 lib/chips2.js | 406 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/types.js  | 324 ----------------------------------------------
 2 files changed, 406 insertions(+), 324 deletions(-)
 create mode 100644 lib/chips2.js
 delete mode 100644 lib/types.js

(limited to 'lib')

diff --git a/lib/chips2.js b/lib/chips2.js
new file mode 100644
index 0000000..2c4c786
--- /dev/null
+++ b/lib/chips2.js
@@ -0,0 +1,406 @@
+//Gods-awful type-checking logic.
+
+//TypeName: string, Params: Array<string>
+function Type(TypeName,Params=[]) {
+	if (!Type.all) Type.all = {};
+	this.typename = "";
+	this.mode = "";
+	
+	if (TypeName instanceof Type) {
+		const t = TypeName.copy();
+		for (const k in t) {
+			this[k] = t[k];
+		}
+		return;
+	}
+	
+	//Union/Tuple
+	if (/^\(.+\)$/.test(TypeName)) {
+		const types = [];
+		var depth = 0;
+		var workspace = "";
+		for (const c of /^\((.+)\)$/.exec(TypeName)[1]) {
+			switch (c) {
+				case '(':
+				case '<':
+					depth++;
+					break;
+				case ')':
+				case '>':
+					depth--;
+					break;
+				case ',':
+					if (depth == 0) {
+						this.mode = "tuple";
+						types.push(workspace);
+						workspace = "";
+						continue;
+					}
+					break;
+				case '|':
+					if (depth == 0) {
+						this.mode = "union";
+						types.push(workspace);
+						workspace = "";
+						continue;
+					}
+					break;
+			}
+			workspace += c;
+		}
+		if (workspace) types.push(workspace);
+		this.template = types.map(t => t.trim()).map(t => new Type(t, Params));
+	}
+	//Template
+	else if (/^[^<]+<.+>$/.test(TypeName)) {
+		this.typename = /^([^<]+)</.exec(TypeName)[1];
+		this.mode = "templated";
+		const types = [];
+		var depth = 0;
+		var workspace = "";
+		for (const c of /^[^<]+<(.+)>$/.exec(TypeName)[1]) {
+			switch (c) {
+				case '(':
+				case '<':
+					depth++;
+					break;
+				case ')':
+				case '>':
+					depth--;
+					break;
+				case ',':
+					if (depth == 0) {
+						types.push(workspace);
+						workspace = "";
+						continue;
+					}
+					break;
+			}
+			workspace += c;
+		}
+		if (workspace) types.push(workspace);
+		this.template = types.map(t => t.trim()).map(t => new Type(t, Params));
+		Type.all[this.typename] = () => new Type(TypeName, Array.from(Params));
+	}
+	//Standard Type, or Template Param
+	else if (/^[^()|<>,]+$/.test(TypeName)) {
+		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;
+		}
+	}
+}
+
+Type.prototype.toString = function(info=false) {
+	switch (this.mode) {
+		case "tuple":
+			return `(${this.template.join(',')})`;
+		case "union":
+			return `(${this.template.join('|')})`;
+		case "templated":
+			return `${this.typename}<${this.template.join(',')}>`;
+		case "any":
+		case "standard":
+			return this.typename;
+		case "param":
+			return info ? `${this.typename}: ${this.template}` : this.template.toString();
+	}
+}
+
+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(key, val) {
+	const t = this.copy();
+	t._resolve(key, val);
+	return t;
+}
+
+Type.prototype.copy = function() {
+	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.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':
+		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)
+	})
+}).bind(Type)
+Type.QuickTD = (function(name, params=[]) {
+	Object.defineProperty(this, (new Type(name, params)).typename, {
+		get: () => new Type(name, params)
+	})
+}).bind(Type)
+
+Type.QuickTD("any");
+Type.QuickTD("int");
+Type.QuickTD("float");
+Type.QuickTD("string");
+Type.QuickTD("bool");
+Type.QuickTD("Vector3");
+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());
+}
+
+
+//chips.js reimplemented with the above
+
+let root = document.documentElement;
+if (window.location.pathname != '/grapher/') {
+	root.addEventListener("mousemove", e => {
+  	root.style.setProperty('--mouse-x', e.clientX + "px");
+  	root.style.setProperty('--mouse-y', e.clientY + "px");
+	});
+}
+
+const portColors = {
+	float:   '#186adc',
+	int:     '#0f6522',
+	exec:    '#f55b18',
+	string:  '#794284',
+	bool:    '#ea2e50',
+	any:     '#f6eee8',
+	special: '#f4c61e'
+}
+const	typeRegex = /(?:^|(?<=<))(?:int|float|bool|string|exec)(?:$|(?=>))/;
+const unionRegex = /^T\d*$|(?<=List<)T\d*(?=>)/;
+
+function computeType(tn, TypeParams, to) {
+	if (tn != "exec") {
+		const t = Object.entries({...TypeParams, ...to}).reduce((t,i) => t.resolve(...i), new Type(tn, Object.keys(TypeParams)));
+		return {typeclass: t.getClasses(), type: t.toString()};
+	} else {
+		return {typeclass: ['exec'], type: 'exec'};
+	}
+}
+
+//keeping this the same for now
+function generateChipHTML(NodeDescs, typeoverride = undefined) {
+	for (let cur of NodeDescs) {
+		let ins = cur.Inputs;
+		let outs = cur.Outputs;
+
+		const root = newEl('div', 'chip');
+		const header = newEl('div', 'chipheader');
+		header.innerText = cur.Name;
+		const input = newEl('div', 'input');
+		const output = newEl('div', 'output');
+		root.append(header, input, output);
+
+		for (const inp of ins) {
+			//work out the type
+			let {typeclass, type} = computeType(inp.ReadonlyType, cur.ReadonlyTypeParams, typeoverride);
+
+			const port = newEl('div', '');
+			port.innerHTML = inp.Name + "&nbsp;";
+			typeclass.map(port.classList.add.bind(port.classList));
+
+			const tooltip = newEl('div', 'type');
+			tooltip.innerText = type;
+
+			input.append(port, tooltip);
+		}
+
+		for (const out of outs) {
+			//work out the type
+			let {typeclass, type} = computeType(out.ReadonlyType, cur.ReadonlyTypeParams, typeoverride);
+
+			const port = newEl('div', '');
+			port.innerHTML = out.Name + "&nbsp;";
+			typeclass.map(port.classList.add.bind(port.classList));
+
+			const tooltip = newEl('div', 'type');
+			tooltip.innerText = type;
+
+			output.append(port, tooltip);
+		}
+
+		return root;
+	}
+
+}
+
+function ListAllTypes(Nodes) {
+	return Object.keys(Type.all);
+}
\ No newline at end of file
diff --git a/lib/types.js b/lib/types.js
deleted file mode 100644
index 3fbb1c9..0000000
--- a/lib/types.js
+++ /dev/null
@@ -1,324 +0,0 @@
-//Gods-awful type-checking logic.
-
-//TypeName: string, Params: Array<string>
-function Type(TypeName,Params=[]) {
-	if (!Type.all) Type.all = {};
-	this.typename = "";
-	this.mode = "";
-	
-	if (TypeName instanceof Type) {
-		const t = TypeName.copy();
-		for (const k in t) {
-			this[k] = t[k];
-		}
-		return;
-	}
-	
-	//Union/Tuple
-	if (/^\(.+\)$/.test(TypeName)) {
-		const types = [];
-		var depth = 0;
-		var workspace = "";
-		for (const c of /^\((.+)\)$/.exec(TypeName)[1]) {
-			switch (c) {
-				case '(':
-				case '<':
-					depth++;
-					break;
-				case ')':
-				case '>':
-					depth--;
-					break;
-				case ',':
-					if (depth == 0) {
-						this.mode = "tuple";
-						types.push(workspace);
-						workspace = "";
-						continue;
-					}
-					break;
-				case '|':
-					if (depth == 0) {
-						this.mode = "union";
-						types.push(workspace);
-						workspace = "";
-						continue;
-					}
-					break;
-			}
-			workspace += c;
-		}
-		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)) {
-		this.typename = /^([^<]+)</.exec(TypeName)[1];
-		this.mode = "templated";
-		const types = [];
-		var depth = 0;
-		var workspace = "";
-		for (const c of /^[^<]+<(.+)>$/.exec(TypeName)[1]) {
-			switch (c) {
-				case '(':
-				case '<':
-					depth++;
-					break;
-				case ')':
-				case '>':
-					depth--;
-					break;
-				case ',':
-					if (depth == 0) {
-						types.push(workspace);
-						workspace = "";
-						continue;
-					}
-					break;
-			}
-			workspace += c;
-		}
-		if (workspace) types.push(workspace);
-		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, or Template Param
-	else if (/^[^()|<>,]+$/.test(TypeName)) {
-		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;
-		}
-	}
-}
-
-Type.prototype.toString = function() {
-	switch (this.mode) {
-		case "tuple":
-			return `(${this.template.join(',')})`;
-		case "union":
-			return `(${this.template.join('|')})`;
-		case "templated":
-			return `${this.typename}<${this.template.join(',')}>`;
-		case "any":
-		case "standard":
-			return this.typename;
-		case "param":
-			return this.template.toString();
-	}
-}
-
-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(key, val) {
-	const t = this.copy();
-	t._resolve(key, val);
-	return t;
-}
-
-Type.prototype.copy = function() {
-	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.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':
-		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)
-	})
-}).bind(Type)
-Type.QuickTD = (function(name, params=[]) {
-	Object.defineProperty(this, (new Type(name, params)).typename, {
-		get: () => new Type(name, params)
-	})
-}).bind(Type)
-
-Type.QuickTD("any");
-Type.QuickTD("int");
-Type.QuickTD("float");
-Type.QuickTD("string");
-Type.QuickTD("bool");
-Type.QuickTD("Vector3");
-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
-- 
cgit v1.2.3-70-g09d2