Skip to content

Commit

Permalink
feat: add block index calculation function
Browse files Browse the repository at this point in the history
- add `countAllBlocks` function to count all blocks in an block array, including nested ones
- use `countAllBlocks` to set the index when creating Blocks
- update tests
  • Loading branch information
ozhanefemeral committed Aug 3, 2024
1 parent 5bf2ad4 commit a821183
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 8 deletions.
2 changes: 1 addition & 1 deletion src/generator/__tests__/if.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ describe("If Block Generator", () => {
expect(block.thenBlocks[0]).toBe(innerIfBlock);
expect(block.elseIfBlocks).toBeUndefined();
expect(block.elseBlock).toBeUndefined();
expect(block.index).toBe(2);
expect(block.index).toBe(5);
expect(state.blocks).toHaveLength(3);
expect(state.blocks[2]).toBe(block);
});
Expand Down
170 changes: 170 additions & 0 deletions src/generator/__tests__/nested-blocks.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import { createFunctionCallBlock } from "generator/blocks/function-call";
import { createIfBlock } from "generator/blocks/if-block";
import { createWhileBlock } from "generator/blocks/while-block";
import { CodeGeneratorState } from "types/generator";

test("creates function call block inside if/else/else-if blocks", () => {
let state: CodeGeneratorState = {
blocks: [],
variables: [],
isAsync: false,
};

// Create an if block to nest our function calls in
const { block: ifBlock, state: state1 } = createIfBlock(
"x > 0",
[],
state,
[
{
condition: "x < 0",
blocks: [],
blockType: "else-if",
index: 1,
},
],
{
blocks: [],
blockType: "else",
index: 2,
}
);
state = state1;

// Create function calls
const { block: thenFuncBlock, state: state2 } = createFunctionCallBlock(
{ name: "thenFunc", returnType: "void" },
state,
ifBlock
);
state = state2;

const { state: state3 } = createFunctionCallBlock(
{ name: "elseIfFunc", returnType: "void" },
state,
ifBlock.elseIfBlocks![0]
);
state = state3;

const { state: state4 } = createFunctionCallBlock(
{ name: "elseFunc", returnType: "void" },
state,
ifBlock.elseBlock!
);
state = state4;

// Assertions
expect(state.blocks).toHaveLength(1);

const mainBlock = state.blocks[0];
expect(mainBlock?.blockType).toBe("if");

if (mainBlock?.blockType !== "if") {
throw new Error("Expected main block to be an if block");
}

expect(mainBlock).toEqual({
condition: "x > 0",
thenBlocks: [thenFuncBlock],
elseIfBlocks: [
{
condition: "x < 0",
blocks: [],
blockType: "else-if",
index: 1,
},
],
elseBlock: {
blocks: [],
blockType: "else",
index: 2,
},
index: 0,
blockType: "if",
});

// Check that the variables were added correctly
expect(state.variables).toHaveLength(3);
expect(state.variables.map((v) => v.name)).toEqual([
"thenfunc",
"elseiffunc",
"elsefunc",
]);
});

test("creates function call block inside while block", () => {
let state: CodeGeneratorState = {
blocks: [],
variables: [],
isAsync: false,
};

// Create a while block to nest our function calls in
const { block: whileBlock, state: state1 } = createWhileBlock(
"x < 10",
[],
state
);
state = state1;

// Create a function call inside the while block
const { block: incrementFunc, state: state2 } = createFunctionCallBlock(
{ name: "incrementX", returnType: "number" },
state,
whileBlock
);
state = state2;

// Create another function call inside the while block
const { block: printFunc, state: state3 } = createFunctionCallBlock(
{ name: "printX", returnType: "void" },
state,
whileBlock
);
state = state3;

// Assertions
expect(state.blocks).toHaveLength(1);

const mainBlock = state.blocks[0];
expect(mainBlock?.blockType).toBe("while");

if (mainBlock?.blockType !== "while") {
throw new Error("Expected main block to be a while block");
}

expect(mainBlock).toEqual({
condition: "x < 10",
loopBlocks: [incrementFunc, printFunc],
index: 0,
blockType: "while",
});

// Check that the nested blocks are correct
expect(mainBlock.loopBlocks).toHaveLength(2);
expect(mainBlock.loopBlocks[0]).toBe(incrementFunc);
expect(mainBlock.loopBlocks[1]).toBe(printFunc);

// Check that the variables were added correctly
expect(state.variables).toHaveLength(2);
expect(state.variables.map((v) => v.name)).toEqual(["incrementx", "printx"]);

// Check the structure of the nested function call blocks
expect(incrementFunc).toEqual({
functionInfo: { name: "incrementX", returnType: "number" },
returnVariable: { name: "incrementx", type: "number", index: 1 },
isAsync: false,
index: 1,
blockType: "functionCall",
parameters: undefined,
});

expect(printFunc).toEqual({
functionInfo: { name: "printX", returnType: "void" },
returnVariable: { name: "printx", type: "void", index: 2 },
isAsync: false,
index: 2,
blockType: "functionCall",
parameters: undefined,
});
});
4 changes: 2 additions & 2 deletions src/generator/__tests__/while.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ describe("While Block Generator", () => {
expect(block.condition).toBe(outerCondition);
expect(block.loopBlocks).toHaveLength(1);
expect(block.loopBlocks[0]).toBe(innerWhileBlock);
expect(block.index).toBe(2); // 0 is the dummy function block, 1 is the inner while block
expect(block.index).toBe(3);
expect(state.blocks).toHaveLength(3);
expect(state.blocks[2]).toBe(block);
});
Expand Down Expand Up @@ -109,7 +109,7 @@ describe("While Block Generator", () => {
);

expect(whileBlock1.index).toBe(1);
expect(whileBlock2.index).toBe(3);
expect(whileBlock2.index).toBe(4);
expect(finalState.blocks).toHaveLength(4);
expect(finalState.blocks[1]).toBe(whileBlock1);
expect(finalState.blocks[3]).toBe(whileBlock2);
Expand Down
5 changes: 4 additions & 1 deletion src/generator/blocks/function-call.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import {
countAllBlocks,
extractReturnType,
findVariableByType,
getUniqueVariableName,
PROMISE_PREFIX,
} from "generator/utils";
import {
BlockAndState,
CodeBlock,
ElseBlock,
ElseIfBlock,
FunctionCallBlock,
Expand Down Expand Up @@ -152,7 +154,7 @@ export function createFunctionCallBlock(
state: CodeGeneratorState,
createInside?: IfBlock | ElseIfBlock | ElseBlock | WhileLoopBlock
): BlockAndState<FunctionCallBlock> {
const index = state.blocks.length;
const index = countAllBlocks(state.blocks);

const variableName = functionInfo.name.toLowerCase();
const newVariableName = getUniqueVariableName(variableName, state.variables);
Expand Down Expand Up @@ -188,6 +190,7 @@ export function createFunctionCallBlock(
case "else-if":
case "else":
return { ...b, blocks: [...(b.blocks || []), block] };

default:
throw new Error(
`Unexpected block type creating inside: ${b.blockType}`
Expand Down
3 changes: 2 additions & 1 deletion src/generator/blocks/if-block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
import { CodeGeneratorState } from "types/generator";
import { factory, Statement } from "typescript";
import { blockToTypeScript } from "../block-generator";
import { countAllBlocks } from "generator/utils";

export function ifBlockToTypeScript(
block: IfBlock,
Expand Down Expand Up @@ -91,7 +92,7 @@ export function createIfBlock(
elseIfBlocks?: ElseIfBlock[],
elseBlock?: ElseBlock
): BlockAndState<IfBlock> {
const index = state.blocks.length;
const index = countAllBlocks(state.blocks);

const block: IfBlock = {
condition,
Expand Down
5 changes: 3 additions & 2 deletions src/generator/blocks/while-block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { BlockAndState, CodeBlock, WhileLoopBlock } from "../../types/blocks";
import { CodeGeneratorState } from "../../types/generator";
import { factory, Statement } from "typescript";
import { blockToTypeScript } from "../block-generator";
import { countAllBlocks } from "generator/utils";

export function whileBlockToTypeScript(
block: WhileLoopBlock,
Expand All @@ -22,8 +23,8 @@ export function createWhileBlock(
loopBlocks: CodeBlock[],
state: CodeGeneratorState
): BlockAndState<WhileLoopBlock> {
const index = state.blocks.length;

const totalBlockCount = countAllBlocks(state.blocks);
const index = totalBlockCount;
const block: WhileLoopBlock = {
condition,
loopBlocks,
Expand Down
20 changes: 19 additions & 1 deletion src/generator/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FunctionInfo, VariableInfoWithIndex } from "types";
import { CodeBlock, FunctionInfo, VariableInfoWithIndex } from "types";

export const PROMISE_PREFIX = "Promise<";

Expand Down Expand Up @@ -77,3 +77,21 @@ export function extractVariables(
index: index,
}));
}

export const countAllBlocks = (blocks: CodeBlock[]): number => {
return blocks.reduce((count, block) => {
if (block.blockType === "if") {
return (
count +
1 +
countAllBlocks(block.thenBlocks) +
countAllBlocks(block.elseIfBlocks?.flatMap((b) => b.blocks) || []) +
countAllBlocks(block.elseBlock?.blocks || [])
);
} else if (block.blockType === "while") {
return count + 1 + countAllBlocks(block.loopBlocks);
} else {
return count + 1;
}
}, 0);
};

0 comments on commit a821183

Please sign in to comment.