Skip to content

Commit bc978ed

Browse files
committed
Day 21.
1 parent ee5dbf2 commit bc978ed

File tree

1 file changed

+140
-0
lines changed

1 file changed

+140
-0
lines changed

2024/day-21.ts

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import { Expect, Equal } from "../test";
2+
3+
/**
4+
* **🎩Bernard's New Demands 2: Electric Boogaloo**
5+
*
6+
* After discovering the unused variables in the elves' code, 🎩Bernard has one more request...
7+
*
8+
* [🎩Bernard] These scripts are getting better, but we need to track ALL the variables - both the ones being used AND the ones collecting dust. We had a close call where a toy almost got left out of its gift box because the variable was declared but never used - if we have to check all this by hand it's going to take forever and kids might not get their presents!
9+
*
10+
* The elves need to improve their code analyzer one final time to create a complete linting tool that will:
11+
* 1. Track all variable declarations and usage
12+
* 2. Identify unused variables
13+
* 3. Return everything in a single, organized report
14+
*
15+
* For example, when analyzing this script:
16+
* ```ts
17+
* let robotDog = "standard_model";
18+
* const giftBox = "premium_wrap";
19+
* var ribbon123 = "silk";
20+
*
21+
* wrapGift(giftBox);
22+
* addRibbon(ribbon123);
23+
* ```
24+
*
25+
* The linter should produce:
26+
* ```ts
27+
* {
28+
* scope: {
29+
* declared: ["robotDog", "giftBox", "ribbon123"],
30+
* used: ["giftBox", "ribbon123"]
31+
* },
32+
* unused: ["robotDog"] // robotDog was never put in the box!
33+
* }
34+
* ```
35+
*
36+
* Implement a type `Lint` that performs this analysis, handling:
37+
* - All previous functionality (variable declarations and usage tracking)
38+
* - Identification of unused variables in a separate `unused` array
39+
* - Various amounts of whitespace, tabs, and empty lines
40+
* - Empty scripts (which should return empty arrays for all fields)
41+
*
42+
* Hint
43+
*
44+
* Build upon your previous analyzers! The unused variables are those that appear in `declared` but not in `used`. Think about how you can compare these two arrays at the type level.
45+
*/
46+
type Lint<T extends string> = AnalyzeUsed<AnalyzeScope<T>>;
47+
48+
type AnalyzeUsed<T extends AnalyzeResult> = {
49+
scope: T;
50+
unused: ExcludeArrays<T["declared"], T["used"]>;
51+
};
52+
53+
type ExcludeArrays<T extends string[], U extends string[]> = T extends [
54+
infer First,
55+
...infer Rest extends string[]
56+
]
57+
? First extends U[number]
58+
? ExcludeArrays<Rest, U>
59+
: [First, ...ExcludeArrays<Rest, U>]
60+
: [];
61+
62+
type AnalyzeScope<
63+
T extends string,
64+
R extends AnalyzeResult = { declared: []; used: [] }
65+
> = T extends `\n${infer Rest}`
66+
? AnalyzeScope<Rest, R>
67+
: T extends `${infer Start}\n${infer Rest}`
68+
? AnalyzeScope<
69+
Rest,
70+
{
71+
declared: [...R["declared"], ...ParseLine<Start>["declared"]];
72+
used: [...R["used"], ...ParseLine<Start>["used"]];
73+
}
74+
>
75+
: R;
76+
77+
type ParseLine<T extends string> =
78+
T extends `${infer _}${VariableDeclarations} ${infer Id} = ${infer _}`
79+
? { declared: [Id]; used: [] }
80+
: T extends `${infer _}(${infer Argument});`
81+
? { declared: []; used: [Argument] }
82+
: { declared: []; used: [] };
83+
84+
type AnalyzeResult = { declared: string[]; used: string[] };
85+
type VariableDeclarations = "let" | "const" | "var";
86+
87+
// *************************************************************************************
88+
// *** Tests ***
89+
// *************************************************************************************
90+
91+
type t0_actual = Lint<`
92+
let teddyBear = "standard_model";
93+
`>;
94+
type t0_expected = {
95+
scope: { declared: ["teddyBear"]; used: [] };
96+
unused: ["teddyBear"];
97+
};
98+
type t0 = Expect<Equal<t0_actual, t0_expected>>;
99+
100+
type t1_actual = Lint<`
101+
buildToy(teddyBear);
102+
`>;
103+
type t1_expected = {
104+
scope: { declared: []; used: ["teddyBear"] };
105+
unused: [];
106+
};
107+
type t1 = Expect<Equal<t1_actual, t1_expected>>;
108+
109+
type t2_actual = Lint<`
110+
let robotDog = "deluxe_model";
111+
assembleToy(robotDog);
112+
`>;
113+
type t2_expected = {
114+
scope: { declared: ["robotDog"]; used: ["robotDog"] };
115+
unused: [];
116+
};
117+
type t2 = Expect<Equal<t2_actual, t2_expected>>;
118+
119+
type t3_actual = Lint<`
120+
let robotDog = "standard_model";
121+
const giftBox = "premium_wrap";
122+
var ribbon123 = "silk";
123+
124+
\t
125+
wrapGift(giftBox);
126+
\r\n
127+
addRibbon(ribbon123);
128+
`>;
129+
type t3_expected = {
130+
scope: { declared: ["robotDog", "giftBox", "ribbon123"]; used: ["giftBox", "ribbon123"] };
131+
unused: ["robotDog"];
132+
};
133+
type t3 = Expect<Equal<t3_actual, t3_expected>>;
134+
135+
type t4_actual = Lint<"\n\t\r \t\r ">;
136+
type t4_expected = {
137+
scope: { declared: []; used: [] };
138+
unused: [];
139+
};
140+
type t4 = Expect<Equal<t4_actual, t4_expected>>;

0 commit comments

Comments
 (0)