aboutsummaryrefslogtreecommitdiffstats
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
parent7f6f5549fe6d1bd03772c13e96f9d92a26660929 (diff)
downloadRRCUtils-d286258f7c05b1a98f9a9aea388690a85246fd87.tar.gz
RRCUtils-d286258f7c05b1a98f9a9aea388690a85246fd87.tar.bz2
RRCUtils-d286258f7c05b1a98f9a9aea388690a85246fd87.zip
Type intersecting is fully implemented.
-rw-r--r--grapher/index.html9
-rw-r--r--grapher/style.css6
-rw-r--r--index.html21
-rw-r--r--lib/types.js222
-rw-r--r--pwathings/appver.json2
-rw-r--r--style.css9
-rw-r--r--types.html79
7 files changed, 303 insertions, 45 deletions
diff --git a/grapher/index.html b/grapher/index.html
index 4a70153..b81b31b 100644
--- a/grapher/index.html
+++ b/grapher/index.html
@@ -10,17 +10,12 @@
<link crossorigin href="/oi.css" rel="stylesheet" type="text/css">
<link crossorigin href="style.css" rel="stylesheet" type="text/css" />
<link crossorigin href="/lib/chips.css" rel="stylesheet" type="text/css" />
-
- <link crossorigin href="/lib/util.js" rel="preload" as="script"/>
- <link crossorigin href="/lib/chips.js" rel="preload" as="script"/>
- <link crossorigin href="script.js" rel="preload" as="script"/>
- <link crossorigin href="/oi.css" rel="preload" as="style"/>
- <link crossorigin href="/lib/chips.css" rel="preload" as="style"/>
- <link crossorigin href="style.css" rel="preload" as="style"/>
+
<link crossorigin href="/circuits.json" rel="preload" as="fetch"/>
<link crossorigin href="https://raw.githubusercontent.com/tyleo-rec/CircuitsV2Resources/master/misc/circuitsv2.json" rel="preload" as="fetch"/>
<script crossorigin src="/lib/util.js"></script>
+ <script crossorigin src="/lib/types.js"></script>
<script crossorigin src="/lib/chips.js"></script>
<script crossorigin src="script.js"></script>
<noscript><style> #searcher, #graph, #canvas { display: none } </style></noscript>
diff --git a/grapher/style.css b/grapher/style.css
index 7b9f51e..119452c 100644
--- a/grapher/style.css
+++ b/grapher/style.css
@@ -245,4 +245,8 @@ button:hover {
background: var(--foreforeground);
}
-a {color: white;} \ No newline at end of file
+a {color: white;}
+
+#searchbox:not([open]) + #graph :is(.exec, .float, .int, .bool, .string, .special, .any):hover + .type {
+ left: calc(var(--mouse-x) + 10px - (var(--chipOffsetX) * 1px) + 13cm);
+} \ No newline at end of file
diff --git a/index.html b/index.html
index 5b0d7a3..9cac405 100644
--- a/index.html
+++ b/index.html
@@ -43,6 +43,27 @@
<button id="enabler" onclick="reloader();">Click to enable offline use!</button>
<section id="changelog">
<article>
+ <h1>June 8, 2022 - <code>
+ <span style="color:lightblue;">typedef</span>
+ (<span style="color:crimson;">you</span>|<span style="color:crimson;">me</span>)
+ <span style="color:khaki;">Frosty</span>;</code></h1>
+ <hr>
+ <ul>
+ <li>Continued work on type inference logic.</li>
+ <ul>
+ <li><q>as well as plenty of methods for computing intersections between them.</q></li>
+ <li>But actually now! The <code><span style="color:khaki;">intersect</span>(<span style="color:crimson;">T1</span>,<span style="color:crimson;">T2</span>)</code> function generates the intersection between 2 types.</li>
+ <li>Support exists for things as dense as nested unions, even.</li>
+ <li><b>As Always:</b></li>
+ <ul>
+ <li>You can view it at <a href="/types.html">types.html</a>, by opening the DevTools console or reading the table.</li>
+ <li>The actual logic is contained in <code>/lib/types.js</code>.</li>
+ </ul>
+ </ul>
+ <li>Adjusted the frosted glass a bit.</li>
+ </ul>
+ </article>
+ <article>
<h1>June 6, 2022 - Just the same as you and me!</h1>
<hr>
<ul>
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
diff --git a/pwathings/appver.json b/pwathings/appver.json
index da656d1..6923e35 100644
--- a/pwathings/appver.json
+++ b/pwathings/appver.json
@@ -1,3 +1,3 @@
{
- "ver": 8
+ "ver": 9
} \ No newline at end of file
diff --git a/style.css b/style.css
index cf84ede..4bf9929 100644
--- a/style.css
+++ b/style.css
@@ -179,7 +179,8 @@ ul.links > li::marker {
margin: 0;
padding: 0.5cm;
position: absolute;
- bottom: 0;
+ bottom: -1px;
+ z-index: 100;
}
@media (orientation:portrait) {
@@ -202,4 +203,10 @@ ul.links > li::marker {
display: block;
height: calc(var(--widhei) - 2cm);
}
+}
+
+code {
+ background: #041720;
+ padding: 0.5mm;
+ border-radius: 0.5mm;
} \ No newline at end of file
diff --git a/types.html b/types.html
index 6794e75..3f9d74a 100644
--- a/types.html
+++ b/types.html
@@ -5,9 +5,32 @@
<meta name="viewport" content="width=device-width">
<title>Document</title>
- <script src="/lib/types.js" ></script>
+ <script src="/lib/util.js"></script>
+ <script src="/lib/types.js"></script>
+ <script src="/lib/chips.js"></script>
+ <link href="/lib/chips.css" rel="stylesheet"></link>
+ <style>
+ table {
+ background: #333;
+ color: white;
+ }
+ table * {
+ border: solid #888 1px;
+ }
+ table th {
+ background: #222;
+ }
+ table td {
+ font-family: sans-serif;
+ }
+ </style>
+
+</head>
+<body>
+ <table><thead><tr id="head"><th>Type</th></tr></thead><tbody id="body"></tbody></table>
<script>
+ console.log("Testing type parsing...")
console.log(new Type("int"))
console.log(new Type("List<int>"))
console.log(new Type("(int, T1)", ["T1"]))
@@ -15,11 +38,57 @@
console.log(new Type("(int|float|Vector3)"))
console.log(new Type("List<(int|float|Vector3)>"))
console.log(new Type("(List<List<(int|float|Vector3)>>|List<any>)"))
+ console.log("Done!")
- </script>
+ console.log("Testing type intersections");
+ const tistart = performance.now();
+ const types = [
+ //standard
+ new Type("int"),
+ new Type("bool"),
+ //template
+ new Type("List<int>"),
+ new Type("List<float>"),
+ new Type("List<List<int>>"),
+ new Type("List<List<any>>"),
+ new Type("List<(int|float)>"),
+ new Type("List<(float|Vector3)>"),
+ new Type("List<any>"),
+ //union
+ new Type("(int|float|bool)"),
+ new Type("(List<any>|bool)"),
+ //any
+ new Type("any"),
+ //dumb shit
+ new Type("List<(List<(int|float)>|Vector3)>"),
+ ];
+ const head = document.getElementById("head");
+ const body = document.getElementById("body");
-</head>
-<body>
-
+ for (const t of types) {
+ const headel = document.createElement("th");
+ headel.innerText = t;
+ head.appendChild(headel);
+
+ const row = document.createElement("tr");
+ body.appendChild(row);
+ const title = document.createElement("th");
+ title.innerText = t;
+ row.appendChild(title);
+ for (const t2 of types) {
+ const sect = document.createElement("td");
+ sect.innerText = intersect(t,t2);
+ row.appendChild(sect);
+ }
+ }
+
+ console.log(`Done in ${performance.now() - tistart}ms!`)
+
+ console.log("Testing chip class...")
+ const node = {"ReadonlyPaletteName": "Set Rotation","ReadonlyChipName": "Set Rotation","Description": "Sets the rotation of the target player or object. Players will rotate about the vertical axis only. Will fail in the following cases: If the target object is currently held, select/frozen by the maker pen, or is the child of a gizmo. Will also fail on players that are seated.","IsBetaChip": true,"DeprecationStage": "Active","PaletteNameSource": "FirstNodeDesc","ChipNameSource": "FirstNodeDesc","NodeDescs": [{"Name": "Set Rotation","ReadonlyTypeParams": {"T": "(Player | Rec Room Object)","U": "(Vector3 | Quaternion)"},"Inputs": [{"Name": "","ReadonlyType": "exec","Description": ""},{"Name": "Target","ReadonlyType": "T","Description": ""},{"Name": "Rotation","ReadonlyType": "U","Description": ""}],"Outputs": [{"Name": "","ReadonlyType": "exec","Description": ""},{"Name": "Success","ReadonlyType": "bool","Description": ""}]}],"NodeFilters": [{"FilterPath": ["Player","Physics"]},{"FilterPath": ["Object","Physics"]}]};
+ document.body.append((x=new Chip(node).root));
+ document.body.appendChild(x);
+
+ </script>
</body>
</html> \ No newline at end of file