Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Model refactor api expr to circ #1238

Merged
merged 30 commits into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
a4fb430
add main files back in
TGCrystal Feb 9, 2023
2cc1949
Merge branch 'model_refactor_api' into model_refactor_api_expr_to_circ
TGCrystal Mar 16, 2023
daf7d00
expr to circuit refactor progress
TGCrystal Mar 16, 2023
e698b47
vscode issues
TGCrystal Mar 27, 2023
4085471
Merge branch 'model_refactor_api_views1' into model_refactor_api_expr…
TGCrystal Mar 27, 2023
b290cf7
Reimplement basic circuit tree to component algo
TGCrystal Mar 30, 2023
f237b81
Merge branch 'model_refactor_api' into model_refactor_api_expr_to_circ
TGCrystal Apr 16, 2023
a319216
Progress on Result Type
TGCrystal Apr 16, 2023
2e74a00
treeToCircuitCore working better
TGCrystal Apr 17, 2023
19f1f60
Incorporate Result into the rest of Expr to Circ algo
TGCrystal Apr 21, 2023
89267df
Attempt at fixing expression parser imports
TGCrystal Apr 21, 2023
320bbf8
Add toBeOk matcher
TGCrystal Apr 22, 2023
818cfb6
Get basic test working
TGCrystal Apr 23, 2023
2c8b5c4
Fix error case tests
TGCrystal Apr 24, 2023
82d736c
Merge branch 'model_refactor_api_headless_view' into model_refactor_a…
TGCrystal Apr 24, 2023
c38943c
Uncomment last error test
TGCrystal Apr 25, 2023
7168f31
Merge branch 'model_refactor_api' into model_refactor_api_expr_to_circ
TGCrystal Apr 25, 2023
1227b7a
Uncommented some more
TGCrystal Apr 25, 2023
4d12152
Merge branch 'model_refactor_api' into model_refactor_api_expr_to_circ
TGCrystal Apr 25, 2023
c49e0bd
Comment popup test
TGCrystal Apr 25, 2023
1fdb90d
Finsh cleaning up tests
TGCrystal Apr 26, 2023
258df28
Final changes before review
TGCrystal Apr 26, 2023
0f0859b
Apply Leon's suggestions
TGCrystal May 6, 2023
ba6df30
Add `.toIncludeError` matcher
TGCrystal May 6, 2023
274499c
Merge branch 'model_refactor_api' into model_refactor_api_expr_to_circ
TGCrystal May 7, 2023
095f0da
Fix lingering merge issue
TGCrystal May 25, 2023
bb321ff
Remove overzealous result usage
TGCrystal May 31, 2023
3ef208b
Final cleaning
TGCrystal May 31, 2023
0078640
Merge branch 'model_refactor_api' into model_refactor_api_expr_to_circ
TGCrystal Feb 6, 2024
32ffb96
Fix suggestions, update branch
TGCrystal Feb 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 8 additions & 5 deletions linting/.imports.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -114,17 +114,17 @@ module.exports = {
"from": [
"./src/site/pages/!(analog)/**",
"./src/app/!(analog|core)/**",
"./src/app/analog/!(public)/**",
"./src/app/core/!(public)/**",
"./src/app/analog/!(public|utils)/**",
"./src/app/core/!(public|utils)/**",
],
},
{
"target": "./src/site/pages/digital/**",
"from": [
"./src/site/pages/!(digital)/**",
"./src/app/!(digital|core)/**",
"./src/app/digital/!(public)/**",
"./src/app/core/!(public)/**",
"./src/app/digital/!(public|utils)/**",
"./src/app/core/!(public|utils)/**",
],
},
{
Expand Down Expand Up @@ -163,7 +163,10 @@ module.exports = {
},
{
"target": "./src/app/core/public/**",
"from": ["./src/app/!(core)/**", "./src/app/core/!(internal|public)/**"],
"from": [
"./src/app/!(core)/**",
"./src/app/core/!(internal|public|utils)/**",
],
},
{
"target": "./src/app/digital/public/**",
Expand Down
26 changes: 13 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,17 @@
"@babel/preset-env": "^7.18.6",
"@babel/preset-react": "^7.18.6",
"@pmmmwh/react-refresh-webpack-plugin": "0.5.7",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.3.0",
"@testing-library/user-event": "^14.2.1",
"@types/jest": "^29.0.0",
"@types/jest": "^29.5.1",
"@types/node": "^18.7.16",
"@types/prompts": "^2.0.14",
"@types/react": "^18.0.18",
"@types/react-dom": "^18.0.6",
"@types/react-redux": "^7.1.24",
"@typescript-eslint/eslint-plugin": "^5.36.2",
"@typescript-eslint/parser": "^5.36.2",
"@typescript-eslint/eslint-plugin": "^5.59.0",
"@typescript-eslint/parser": "^5.59.0",
"babel-loader": "^8.2.5",
"chalk": "^5.0.1",
"copyfiles": "^2.4.1",
Expand All @@ -44,13 +44,13 @@
"dotenv": "^16.0.1",
"dotenv-expand": "^8.0.3",
"emscript-loader": "^1.0.6",
"eslint": "^8.19.0",
"eslint": "^8.39.0",
"eslint-config-react-app": "^7.0.1",
"eslint-import-resolver-typescript": "^3.5.1",
"eslint-plugin-align-import": "^1.0.0",
"eslint-plugin-deprecation": "^1.3.2",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jest": "^26.5.3",
"eslint-plugin-jest": "^27.2.1",
"eslint-plugin-jest-formatting": "^3.1.0",
"eslint-plugin-jsdoc": "^39.3.13",
"eslint-plugin-jsx-a11y": "^6.6.0",
Expand All @@ -64,9 +64,9 @@
"friendly-errors-webpack-plugin": "^1.7.0",
"html-webpack-plugin": "^5.5.0",
"interpolate-html-plugin": "^4.0.0",
"jest": "^28.1.2",
"jest-environment-jsdom": "^28.1.2",
"jest-watch-typeahead": "^1.1.0",
"jest": "^29.5.0",
"jest-environment-jsdom": "^29.5.0",
"jest-watch-typeahead": "^2.2.2",
"mini-css-extract-plugin": "^2.6.1",
"open": "^8.4.0",
"ora": "^6.1.2",
Expand All @@ -78,10 +78,10 @@
"react-refresh-typescript": "^2.0.7",
"sass": "^1.53.0",
"sass-loader": "^13.0.2",
"ts-jest": "^28.0.5",
"ts-loader": "^9.3.1",
"ts-morph": "^15.1.0",
"typescript": "^4.8.2",
"ts-jest": "^29.1.0",
"ts-loader": "^9.4.2",
"ts-morph": "^18.0.0",
"typescript": "^5.0.4",
"url-loader": "^4.1.1",
"webpack": "^5.73.0",
"webpack-cli": "^4.10.0",
Expand Down
28 changes: 27 additions & 1 deletion src/app/core/public/api/Utilities.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,35 @@
import {Graph} from "math/Graph";
import {Component} from "./Component";
import {Obj} from "./Obj";
import {Port} from "./Port";
import {Wire} from "./Wire";
import {Circuit} from "./Circuit";


export const isObjComponent = (obj: Obj): obj is Component => (obj.baseKind === "Component");
export const isObjWire = (obj: Obj): obj is Wire => (obj.baseKind === "Wire");
export const isObjPort = (obj: Obj): obj is Port => (obj.baseKind === "Port");
export const isObjPort = (obj: Obj): obj is Port => (obj.baseKind === "Port");

// TODO[model_refactor_api_expr_to_circ](trevor) Is this the right place? Check for circular dependencies.
/**
* This function converts the provided circuit to a graph where the nodes are components and edges are wires.
* Both are represented by guids rather than object references.
*
* @param circuit The circuit to convert to a graph.
* @returns The provided circuit as a graph.
*/
export function CreateGraph(circuit: Circuit): Graph<string, string> {
const graph = new Graph<string, string>();

// TODO[model_refactor_api_expr_to_circ](trevor): Replace with getComponents and getWires functions on Circuit
const objs = circuit.getObjs().filter((obj) => (isObjComponent(obj)));
for (const obj of objs) {
graph.createNode(obj.id);
}
const wires = circuit.getObjs().filter((obj) => (isObjWire(obj))) as readonly Wire[];
for (const wire of wires) {
graph.createEdge(wire.p1.parent.id, wire.p2.parent.id, wire.id);
}

return graph;
}
11 changes: 11 additions & 0 deletions src/app/core/utils/StringUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* Checks if the substring of a given input starting at a given index is equal to a given sequence.
*
* @param input The input string that a substring of will be examined.
* @param index The starting index of input to compare at.
* @param sequence The sequence to check equality with.
* @returns True if input has a substring starting at index that matches sequence, false otherwise.
*/
export function SubStrEquals(input: string, index: number, sequence: string): boolean {
return input.slice(index, index + sequence.length) === sequence;
}
11 changes: 6 additions & 5 deletions src/app/core/utils/math/Graph.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

/**
* @param V The type for the vertices of the edge.
* @param E The type for weight of the edge.
Expand Down Expand Up @@ -53,13 +52,15 @@ export class Graph<V, E> {
}

public createEdge(source: V, target: V, weight: E): void {
if (!this.list.has(source))
const sourceNode = this.list.get(source);
const targetNode = this.reverseList.get(target);
if (!sourceNode)
throw new Error("Graph doesn't have node of value: " + source);
if (!this.list.has(target))
if (!targetNode)
throw new Error("Graph doesn't have node of value: " + target);

this.list.get(source)!.push(new Edge<V, E>(target, weight));
this.reverseList.get(target)!.push(new Edge<V, E>(source, weight));
sourceNode.push(new Edge<V, E>(target, weight));
targetNode.push(new Edge<V, E>(source, weight));
}

public isConnected(): boolean {
Expand Down
55 changes: 54 additions & 1 deletion src/app/tests/Extensions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import crypto from "node:crypto";
import {Result} from "core/utils/Result";
import crypto from "node:crypto";


// Define crypto for Jest for uuid generation
Expand All @@ -11,6 +12,8 @@ declare global {
interface Matchers<R> {
toApproximatelyEqual(expected: unknown, epsilon?: number): CustomMatcherResult;
toBeCloseToAngle(otherAngle: number, epsilon?: number): CustomMatcherResult;
toBeOk(): CustomMatcherResult;
toIncludeError(message: string): CustomMatcherResult;
// toBeConnectedTo(a: DigitalComponent, options?: {depth?: number}): CustomMatcherResult;
}
}
Expand Down Expand Up @@ -81,6 +84,56 @@ expect.extend({
};
},

toBeOk(received: unknown) {
if (!received || typeof received !== "object" || !("ok" in received)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can just say received: Result and then you don't have to type-check it, I should probably do that for the others but I was probably referencing some js

return {
message: () => "supplied value is not a Result",
pass: false,
}
}
const result = received as Result;
if (result.ok) {
return {
message: () => "expected Result to be Ok",
pass: true,
}
}
return {
message: () => `expected Result to not have errors:\n - ${
result.error.errors
.map((err) => err.message)
.join("\n - ")
}`,
pass: false,
}
},

toIncludeError(received: unknown, message: string) {
if (!received || typeof received !== "object" || !("ok" in received)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

return {
message: () => "supplied value is not a Result",
pass: false,
}
}
const result = received as Result;
if (result.ok) {
return {
message: () => "expected Result to be not be Ok",
pass: false,
}
}
return {
message: () => `expected Result to contain an error including the text "${message}", ` +
`instead includes:\n - ${
result.error.errors
.map((err) => err.message)
.join("\n - ")
}`,
pass: result.error.errors.some((error) => error.message.includes(message)),
}
},

// TODO[model_refactor_api_expr_to_circ](trevor): Check if this is/was needed for expression to circuit
// toBeConnectedTo(source: unknown, target: DigitalComponent, options = { depth: Infinity }) {
// if (!(source instanceof DigitalComponent))
// throw new Error("toBeConnectedTo can only be used with DigitalComponents!");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from "react";

import {OperatorFormat, TokenType} from "digital/utils/ExpressionParser/Constants/DataStructures";
import {OperatorFormat, TokenType} from "site/digital/utils/ExpressionParser/Constants/DataStructures";

import {InputField} from "shared/components/InputField";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,3 @@ export function Generate(info: DigitalCircuitInfo, expression: string,
// info.history.add(action);
// info.renderer.render();
}

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {useState} from "react";

import {OperatorFormat, OperatorFormatLabel} from "digital/utils/ExpressionParser/Constants/DataStructures";
import {FORMATS} from "digital/utils/ExpressionParser/Constants/Formats";
import {OperatorFormat, OperatorFormatLabel} from "site/digital/utils/ExpressionParser/Constants/DataStructures";
import {FORMATS} from "site/digital/utils/ExpressionParser/Constants/Formats";

import {DigitalCircuitInfo} from "digital/utils/DigitalCircuitInfo";

Expand Down
43 changes: 43 additions & 0 deletions src/site/pages/digital/src/utils/ComponentOrganizers.ts
TGCrystal marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// TODO[model_refactor_api](trevor): Get this working for expression to circuit frontend
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not get this working now ?

// import {Vector} from "Vector";

// import {CreateGraph, IOObjectSet} from "./ComponentUtils";


// const ORGANIZE_SEP_X = 4;
// const ORGANIZE_SEP_Y = 3;

// function OrganizeCore(groups: IOObjectSet, start: Vector, depths: number[][]): void {
// // Depths is a 2d array where the index of the inner array indicates the depth of all of the nodes inside that array
// const components = groups.getComponents();

// depths.forEach((nodes, depth) =>
// nodes.forEach((node, index) =>
// // extra space for labels
// components[node].setPos(start.add(ORGANIZE_SEP_X*(depth - (depths.length - 1)/2) + ORGANIZE_SEP_X/2,
// -ORGANIZE_SEP_Y*(index - (nodes.length - 1)/2)))
// )
// );
// }

// /**
// * Organizes the components so that components at greater depth are further to the right,
// * using the getMaxNodeDepths function of Graph to accomplish this.
// *
// * @param groups The components to organize.
// * @param start The top left coordinate where the organization should start.
// */
// export function OrganizeMaxDepth(groups: IOObjectSet, start: Vector): void {
// OrganizeCore(groups, start, CreateGraph(groups).getMaxNodeDepths());
// }

// /**
// * Organizes the components so that components at greater depth are further to the right,
// * using the getMinNodeDepths function of Graph to accomplish this.
// *
// * @param groups The components to organize.
// * @param start The top left coordinate where the organization should start.
// */
// export function OrganizeMinDepth(groups: IOObjectSet, start: Vector): void {
// OrganizeCore(groups, start, CreateGraph(groups).getMinNodeDepths());
// }
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/** Represents the operand of a binary operation. */
export type InputTreeBinOpType = "|" | "^" | "&";
/** Represents the operand of a unary operation. */
export type InputTreeUnOpType = "!";
/** Represents operands of both unary and binary operations. */
export type InputTreeOpType = InputTreeBinOpType | InputTreeUnOpType;
export type ParenType = "(" | ")";
export type TokenType =
| InputTreeOpType
| ParenType;

/** Represents that a "binary" operation can have 2-8 children. */
export type BinOpChildren = [InputTree, InputTree, InputTree?, InputTree?,
InputTree?, InputTree?, InputTree?, InputTree?];

/** Represents a node on the parsed tree that itself represents an input component. */
export interface InputTreeIdent {
kind: "leaf";
ident: string;
}
/** Represents a node on the parsed tree that itself represents a unary operation. */
export interface InputTreeUnOpNode {
kind: "unop";
type: InputTreeUnOpType;
child: InputTree;
}
/** Represents a node on the parsed tree that itself represents a binary operation. */
export interface InputTreeBinOpNode {
kind: "binop";
type: InputTreeBinOpType;
isNot: boolean; // true for NAND, false for AND
// Can have 2-8 children
children: BinOpChildren;
}
/** Represents all possible types of nodes for the input tree. */
export type InputTree =
| InputTreeIdent
| InputTreeUnOpNode
| InputTreeBinOpNode

/**
* Used to represent a unary operation, binary operation, or parenthesis when parsing the intial expression
* to a token list.
*/
export interface OpToken {
type: TokenType;
}
/** Used to represent the token of an input component when parsing the initial expression to a token list. */
export interface InputToken {
type: "input";
name: string;
}
export type Token =
| OpToken
| InputToken;

/** When adding a new format, this type must also be expanded to include its new unique icon. */
export type OperatorFormatLabel = "|" | "||" | "+" | "+_" | "OR" | "or" | "custom";

/** Represents a format to represent the operators in the expression. */
export type OperatorFormat = {
label: string; // Text displayed to explain the OperatorFormat
separator: string;
icon: OperatorFormatLabel; // All icons should be unique (and "custom" should be reserved for frontend)
ops: Record<TokenType, string>;
}