Skip to content

Commit 5cf812b

Browse files
committed
feat: initial commit
0 parents  commit 5cf812b

11 files changed

+366
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

default-export-example.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
export const consequentAlternate = () => {
2+
const a = 41;
3+
if (a < 42) {
4+
return true;
5+
} else {
6+
return false;
7+
}
8+
};
9+
export const consequent = () => {
10+
const a = 41;
11+
if (a < 42) {
12+
return true;
13+
} else if (a < 21) {
14+
return true;
15+
}
16+
return false;
17+
};

example.cjs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const consequentAlternate = () => {
2+
const a = 41;
3+
if (a < 42) {
4+
return true;
5+
} else {
6+
return false;
7+
}
8+
};
9+
const consequent = () => {
10+
const a = 41;
11+
if (a < 42) {
12+
return true;
13+
} else if (a < 21) {
14+
return true;
15+
}
16+
return false;
17+
};
18+
module.exports = { consequent, consequentAlternate };

example_export_function.cjs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const consequentAlternate = () => {
2+
const a = 41;
3+
if (a < 42) {
4+
return true;
5+
} else {
6+
return false;
7+
}
8+
};
9+
const consequent = () => {
10+
const a = 41;
11+
if (a < 42) {
12+
return true;
13+
} else if (a < 21) {
14+
return true;
15+
}
16+
return false;
17+
};
18+
module.exports = { consequent, consequentAlternate };

index.js

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
import { readFile } from "fs/promises";
2+
import ast from "abstract-syntax-tree";
3+
4+
// minimal evaluation path of boolean values
5+
const potentialValues = [
6+
[true, true],
7+
[true, false],
8+
[false, true],
9+
[false, false],
10+
];
11+
12+
function buildTruthTable(symbol) {
13+
// symbol: ||, &&
14+
// take the number of boolean values
15+
const result = [];
16+
const possibleValues = variableNum;
17+
// cycle through possible values for p and q
18+
// log each set of values to avoid duplicates
19+
// add a record to result
20+
// determine possible combinations
21+
22+
/**
23+
* take this example:
24+
* a || b && c || b
25+
*
26+
* assumption on steps
27+
* 1. a || b = z
28+
* 2. c || b = y
29+
* 3. z && y
30+
*/
31+
32+
for (let i = 0; i < possibleValues; i++) {}
33+
result.push({
34+
p: true,
35+
o: "&&",
36+
q: false,
37+
r: false,
38+
});
39+
}
40+
41+
const calculateLogicalComplexityMultiplier = () => {
42+
// generate a truth table for the logical operation
43+
// assumption -> all nodes in a logical expression
44+
// will reduce to a true / false
45+
// the multiplier is the amount of rows in a logical expression's truth table
46+
// each row represents a different test case required to test
47+
return 1;
48+
};
49+
50+
const typesAddingComplexity = [
51+
"IfStatement",
52+
"TryStatement",
53+
"CatchClause",
54+
"DoWhileStatement",
55+
"ForInStatement",
56+
"ForOfStatement",
57+
"ForStatement",
58+
"SwitchStatement",
59+
"BinaryExpression",
60+
// "SwitchCase",
61+
"WhileStatement",
62+
"ConditionalExpression",
63+
];
64+
65+
function increasesComplexity(node) {
66+
if (typesAddingComplexity.includes(node.type)) {
67+
return [true, 1];
68+
}
69+
70+
if (
71+
node.type === "LogicalExpression" &&
72+
(node.operator === "||" || node.operator === "&&" || node.operator === "??")
73+
) {
74+
return [true, calculateLogicalComplexityMultiplier(node)];
75+
}
76+
77+
return [false, 0];
78+
}
79+
80+
const resolveBody = {
81+
CatchClause: (node) => [node.handler.body.body],
82+
TryStatement: (node) => [node?.block?.body, node.handler.body.body],
83+
TryStatementHandler: (node) => [],
84+
LogicalExpression: (node) => [[node.left], [node.right]],
85+
ForStatement: (node) => [],
86+
ForOfStatement: (node) => [],
87+
ForInStatement: (node) => [],
88+
SwitchStatement: (node) => [],
89+
WhileStatement: (node) => [],
90+
BinaryExpression: (node) => [],
91+
IfStatement: (node) => [
92+
[node.test],
93+
node?.consequent?.body,
94+
node?.alternate?.body,
95+
],
96+
FunctionDeclaration: (node) => [node.body],
97+
DoWhileStatement: (node) => [],
98+
BlockStatement: (node) => [node.body],
99+
VariableDeclaration: (node) => [node.declarations],
100+
VariableDeclarator: (node) => [[node.init]],
101+
ConditionalExpression: (node) => [
102+
[node.test],
103+
[node.consequent],
104+
[node.alternate],
105+
],
106+
};
107+
function determineLogicalComplexity(bodyInput) {
108+
let complexity = 1; // default to complexity of one because every function has at minimal one path through
109+
function processNodes(body) {
110+
for (const node of body) {
111+
if (node.type === "ExportNamedDeclaration") {
112+
complexity = 1; // reset clock on each function
113+
console.log(node);
114+
console.log(`export name: ${node.id.lk}`);
115+
if (node.declaration.type === "FunctionDeclaration") {
116+
processNodes([node.declaration.body]);
117+
} else {
118+
findDeclarations(node.declaration);
119+
}
120+
}
121+
const resolvedBody = resolveBody[node.type];
122+
if (!resolvedBody) continue;
123+
const [shouldIncrease] = increasesComplexity(node);
124+
if (shouldIncrease) {
125+
console.log("increasing", node);
126+
complexity++;
127+
}
128+
const nodeBody = resolvedBody(node);
129+
if (nodeBody) {
130+
for (const nb of nodeBody) {
131+
if (!nb) continue;
132+
processNodes(nb);
133+
}
134+
}
135+
}
136+
}
137+
function findDeclarations(node, complexity) {
138+
console.log(node);
139+
if (node.declaration) return processNodes([node.declaration]);
140+
if (!node.declarations) return;
141+
142+
for (const declaration of node.declarations) {
143+
console.log();
144+
const isFunction = !!declaration.init?.body?.body;
145+
console.log(`declaration name: ${declaration.id.name} - ${isFunction}`);
146+
if (declaration.init?.body?.body) {
147+
processNodes(declaration.init.body.body);
148+
}
149+
}
150+
}
151+
processNodes(bodyInput);
152+
// if (node.type === "SwitchCase" || node.type === "SwitchStatement") {
153+
// console.log(JSON.stringify(node, undefined, 2));
154+
// }
155+
156+
// if (node.handler?.type === "CatchClause") {
157+
// complexity++;
158+
// determineLogicalComplexity(node.handler.body.body, complexity);
159+
// }
160+
161+
// if (node.type === "TryStatement") {
162+
// determineLogicalComplexity(node.block.body, complexity);
163+
// determineLogicalComplexity([node.handler?.body?.body]);
164+
// }
165+
166+
// if (node.type === "LogicalExpression") {
167+
// determineLogicalComplexity([node.left], complexity);
168+
// determineLogicalComplexity([node.right], complexity);
169+
170+
// if (node.operator === "||" || node.operator === "&&") {
171+
// complexity++;
172+
// }
173+
// }
174+
175+
// if (node.type === "IfStatement") {
176+
// determineLogicalComplexity([node.test], complexity);
177+
// // consequent and alternate are BlockStatement nodes
178+
// determineLogicalComplexity(node.consequent.body, complexity);
179+
// if (node?.alternate?.body) {
180+
// complexity++;
181+
// determineLogicalComplexity(node.alternate.body, complexity);
182+
// }
183+
// }
184+
return complexity;
185+
}
186+
187+
function calculateComplexity(tree) {
188+
const complexity = determineLogicalComplexity(tree.body);
189+
console.log(complexity);
190+
}
191+
console.log(process.argv[2]);
192+
const file = await readFile(process.argv[2], "utf-8");
193+
const tree = ast.parse(file);
194+
calculateComplexity(tree);
195+
// const result = calculateComplexity(tree);
196+
// console.log(JSON.stringify(tree, undefined, 2));

logical-complexity.js

Whitespace-only changes.

named-export-example.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
export const consequent = () => {
2+
try {
3+
console.log("test");
4+
} catch (e) {
5+
return false;
6+
}
7+
8+
// a logical structure with two || has 8 possible paths
9+
// a || b || c
10+
// -----------
11+
// 0 || 0 || 0 F
12+
// 1 || 0 || 0 P
13+
// 0 || 1 || 0 P
14+
// 0 || 0 || 1 P
15+
// 1 || 1 || 0 P
16+
// 1 || 0 || 1 P
17+
// 0 || 1 || 1 P
18+
// 1 || 1 || 1 P
19+
const first = 2 + 2 == 4;
20+
const second = 1 + 1 == 2;
21+
const third = 1 + 2 == 3;
22+
if (first || second || third) {
23+
return true;
24+
}
25+
26+
// a && b
27+
// ------
28+
// 1 && 1 P
29+
// 0 && 1 F
30+
// 1 && 0 F
31+
// 0 && 0 F
32+
if (first && second) {
33+
}
34+
let i = 0;
35+
do {
36+
console.log("hi");
37+
i++;
38+
} while (i < 2);
39+
return false;
40+
};

package.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"name": "forecast-action-js",
3+
"version": "1.0.0",
4+
"main": "index.js",
5+
"type": "module",
6+
"license": "MIT",
7+
"dependencies": {
8+
"abstract-syntax-tree": "^2.20.6"
9+
}
10+
}

switch-case.js

Whitespace-only changes.

testing-logical-operations.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export function testingLogicalOperations() {
2+
const a = true;
3+
const b = false;
4+
const inline = a || b ? true : false;
5+
return inline;
6+
}
7+
8+
export function testingOperators() {
9+
const a = true || false;
10+
const b = false ?? true;
11+
const c = false && true;
12+
}

0 commit comments

Comments
 (0)