Skip to content

Commit ea29a8f

Browse files
authored
Variant constructor experiment (#1413)
* standardising the instantiation of variants using an object as a dictionary * using Map instead * adding a factory for the same functionality * key, value pair in the same array * we don't need to store a string as a key anymore, we can directly use the constructor from the Slang object * cleaner types * reversing the order of the parameter types to match the key, value order in the Map * separating the complex factory into 2 specific ones * cleaner name * passing the types down to the second factory * shorter name so it fits better and it's easier to read * standardising the factory type * small typo * some cleanup * deleting intermediate function in `Statement` and `YulStatement` * using `instanceof` instead of Map * Clear names * adding coverage for all cases only on `test` environment * adding an _exhaustiveCheck to make sure we check for every possible `variant` * adding a c8 ignore comment to exhaustiveCkecks since we know they'll never be reached * Moving all the exhaustive coverage check to the tests * renaming `check-coverage` to `variant-coverage`
1 parent cb4f99c commit ea29a8f

52 files changed

Lines changed: 830 additions & 587 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.c8rc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"functions": 90,
66
"statements": 90,
77
"exclude": ["/node_modules/"],
8-
"include": ["src/**/*.ts"],
8+
"include": ["src/**/*.ts", "variant-coverage/**/*.ts"],
99
"reporter": ["lcov", "text"],
1010
"temp-dir": "./coverage/"
1111
}

eslint.config.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const compat = new FlatCompat({
1818
export default [
1919
{
2020
ignores: [
21+
'variant-coverage/**/*.ts',
2122
'coverage/**/*.js',
2223
'dist/**/*.cjs',
2324
'dist/**/*.js',

src/slang-nodes/ArgumentsDeclaration.ts

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as ast from '@nomicfoundation/slang/ast';
22
import { NonterminalKind } from '@nomicfoundation/slang/cst';
3+
import { createNonterminalVariantSimpleCreator } from '../slang-utils/create-nonterminal-variant-creator.js';
34
import { SlangNode } from './SlangNode.js';
45
import { PositionalArgumentsDeclaration } from './PositionalArgumentsDeclaration.js';
56
import { NamedArgumentsDeclaration } from './NamedArgumentsDeclaration.js';
@@ -8,20 +9,13 @@ import type { ParserOptions } from 'prettier';
89
import type { CollectedMetadata } from '../types.d.ts';
910
import type { AstNode } from './types.d.ts';
1011

11-
function createNonterminalVariant(
12-
variant: ast.ArgumentsDeclaration['variant'],
13-
collected: CollectedMetadata,
14-
options: ParserOptions<AstNode>
15-
): ArgumentsDeclaration['variant'] {
16-
if (variant instanceof ast.PositionalArgumentsDeclaration) {
17-
return new PositionalArgumentsDeclaration(variant, collected, options);
18-
}
19-
if (variant instanceof ast.NamedArgumentsDeclaration) {
20-
return new NamedArgumentsDeclaration(variant, collected, options);
21-
}
22-
const exhaustiveCheck: never = variant;
23-
throw new Error(`Unexpected variant: ${JSON.stringify(exhaustiveCheck)}`);
24-
}
12+
const createNonterminalVariant = createNonterminalVariantSimpleCreator<
13+
ast.ArgumentsDeclaration,
14+
ArgumentsDeclaration
15+
>([
16+
[ast.PositionalArgumentsDeclaration, PositionalArgumentsDeclaration],
17+
[ast.NamedArgumentsDeclaration, NamedArgumentsDeclaration]
18+
]);
2519

2620
export class ArgumentsDeclaration extends SlangNode {
2721
readonly kind = NonterminalKind.ArgumentsDeclaration;

src/slang-nodes/ContractMember.ts

Lines changed: 19 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as ast from '@nomicfoundation/slang/ast';
22
import { NonterminalKind } from '@nomicfoundation/slang/cst';
3+
import { createNonterminalVariantSimpleCreator } from '../slang-utils/create-nonterminal-variant-creator.js';
34
import { SlangNode } from './SlangNode.js';
45
import { UsingDirective } from './UsingDirective.js';
56
import { FunctionDefinition } from './FunctionDefinition.js';
@@ -19,53 +20,24 @@ import type { ParserOptions } from 'prettier';
1920
import type { CollectedMetadata } from '../types.d.ts';
2021
import type { AstNode } from './types.d.ts';
2122

22-
function createNonterminalVariant(
23-
variant: ast.ContractMember['variant'],
24-
collected: CollectedMetadata,
25-
options: ParserOptions<AstNode>
26-
): ContractMember['variant'] {
27-
if (variant instanceof ast.UsingDirective) {
28-
return new UsingDirective(variant, collected, options);
29-
}
30-
if (variant instanceof ast.FunctionDefinition) {
31-
return new FunctionDefinition(variant, collected, options);
32-
}
33-
if (variant instanceof ast.ConstructorDefinition) {
34-
return new ConstructorDefinition(variant, collected, options);
35-
}
36-
if (variant instanceof ast.ReceiveFunctionDefinition) {
37-
return new ReceiveFunctionDefinition(variant, collected, options);
38-
}
39-
if (variant instanceof ast.FallbackFunctionDefinition) {
40-
return new FallbackFunctionDefinition(variant, collected, options);
41-
}
42-
if (variant instanceof ast.UnnamedFunctionDefinition) {
43-
return new UnnamedFunctionDefinition(variant, collected, options);
44-
}
45-
if (variant instanceof ast.ModifierDefinition) {
46-
return new ModifierDefinition(variant, collected, options);
47-
}
48-
if (variant instanceof ast.StructDefinition) {
49-
return new StructDefinition(variant, collected, options);
50-
}
51-
if (variant instanceof ast.EnumDefinition) {
52-
return new EnumDefinition(variant, collected);
53-
}
54-
if (variant instanceof ast.EventDefinition) {
55-
return new EventDefinition(variant, collected, options);
56-
}
57-
if (variant instanceof ast.StateVariableDefinition) {
58-
return new StateVariableDefinition(variant, collected, options);
59-
}
60-
if (variant instanceof ast.ErrorDefinition) {
61-
return new ErrorDefinition(variant, collected, options);
62-
}
63-
if (variant instanceof ast.UserDefinedValueTypeDefinition) {
64-
return new UserDefinedValueTypeDefinition(variant, collected);
65-
}
66-
const exhaustiveCheck: never = variant;
67-
throw new Error(`Unexpected variant: ${JSON.stringify(exhaustiveCheck)}`);
68-
}
23+
const createNonterminalVariant = createNonterminalVariantSimpleCreator<
24+
ast.ContractMember,
25+
ContractMember
26+
>([
27+
[ast.UsingDirective, UsingDirective],
28+
[ast.FunctionDefinition, FunctionDefinition],
29+
[ast.ConstructorDefinition, ConstructorDefinition],
30+
[ast.ReceiveFunctionDefinition, ReceiveFunctionDefinition],
31+
[ast.FallbackFunctionDefinition, FallbackFunctionDefinition],
32+
[ast.UnnamedFunctionDefinition, UnnamedFunctionDefinition],
33+
[ast.ModifierDefinition, ModifierDefinition],
34+
[ast.StructDefinition, StructDefinition],
35+
[ast.EnumDefinition, EnumDefinition],
36+
[ast.EventDefinition, EventDefinition],
37+
[ast.StateVariableDefinition, StateVariableDefinition],
38+
[ast.ErrorDefinition, ErrorDefinition],
39+
[ast.UserDefinedValueTypeDefinition, UserDefinedValueTypeDefinition]
40+
]);
6941

7042
export class ContractMember extends SlangNode {
7143
readonly kind = NonterminalKind.ContractMember;

src/slang-nodes/ContractSpecifier.ts

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as ast from '@nomicfoundation/slang/ast';
22
import { NonterminalKind } from '@nomicfoundation/slang/cst';
3+
import { createNonterminalVariantSimpleCreator } from '../slang-utils/create-nonterminal-variant-creator.js';
34
import { SlangNode } from './SlangNode.js';
45
import { InheritanceSpecifier } from './InheritanceSpecifier.js';
56
import { StorageLayoutSpecifier } from './StorageLayoutSpecifier.js';
@@ -8,20 +9,13 @@ import type { ParserOptions } from 'prettier';
89
import type { CollectedMetadata } from '../types.d.ts';
910
import type { AstNode } from './types.d.ts';
1011

11-
function createNonterminalVariant(
12-
variant: ast.ContractSpecifier['variant'],
13-
collected: CollectedMetadata,
14-
options: ParserOptions<AstNode>
15-
): ContractSpecifier['variant'] {
16-
if (variant instanceof ast.InheritanceSpecifier) {
17-
return new InheritanceSpecifier(variant, collected, options);
18-
}
19-
if (variant instanceof ast.StorageLayoutSpecifier) {
20-
return new StorageLayoutSpecifier(variant, collected, options);
21-
}
22-
const exhaustiveCheck: never = variant;
23-
throw new Error(`Unexpected variant: ${JSON.stringify(exhaustiveCheck)}`);
24-
}
12+
const createNonterminalVariant = createNonterminalVariantSimpleCreator<
13+
ast.ContractSpecifier,
14+
ContractSpecifier
15+
>([
16+
[ast.InheritanceSpecifier, InheritanceSpecifier],
17+
[ast.StorageLayoutSpecifier, StorageLayoutSpecifier]
18+
]);
2519

2620
export class ContractSpecifier extends SlangNode {
2721
readonly kind = NonterminalKind.ContractSpecifier;

src/slang-nodes/Expression.ts

Lines changed: 37 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {
33
NonterminalKind,
44
TerminalNode as SlangTerminalNode
55
} from '@nomicfoundation/slang/cst';
6-
import { extractVariant } from '../slang-utils/extract-variant.js';
6+
import { createNonterminalVariantCreator } from '../slang-utils/create-nonterminal-variant-creator.js';
77
import { SlangNode } from './SlangNode.js';
88
import { AssignmentExpression } from './AssignmentExpression.js';
99
import { ConditionalExpression } from './ConditionalExpression.js';
@@ -38,95 +38,42 @@ import type { ParserOptions } from 'prettier';
3838
import type { CollectedMetadata } from '../types.d.ts';
3939
import type { AstNode } from './types.d.ts';
4040

41-
function createNonterminalVariant(
42-
variant: Exclude<ast.Expression['variant'], SlangTerminalNode>,
43-
collected: CollectedMetadata,
44-
options: ParserOptions<AstNode>
45-
): Expression['variant'] {
46-
if (variant instanceof ast.AssignmentExpression) {
47-
return new AssignmentExpression(variant, collected, options);
48-
}
49-
if (variant instanceof ast.ConditionalExpression) {
50-
return new ConditionalExpression(variant, collected, options);
51-
}
52-
if (variant instanceof ast.OrExpression) {
53-
return new OrExpression(variant, collected, options);
54-
}
55-
if (variant instanceof ast.AndExpression) {
56-
return new AndExpression(variant, collected, options);
57-
}
58-
if (variant instanceof ast.EqualityExpression) {
59-
return new EqualityExpression(variant, collected, options);
60-
}
61-
if (variant instanceof ast.InequalityExpression) {
62-
return new InequalityExpression(variant, collected, options);
63-
}
64-
if (variant instanceof ast.BitwiseOrExpression) {
65-
return new BitwiseOrExpression(variant, collected, options);
66-
}
67-
if (variant instanceof ast.BitwiseXorExpression) {
68-
return new BitwiseXorExpression(variant, collected, options);
69-
}
70-
if (variant instanceof ast.BitwiseAndExpression) {
71-
return new BitwiseAndExpression(variant, collected, options);
72-
}
73-
if (variant instanceof ast.ShiftExpression) {
74-
return new ShiftExpression(variant, collected, options);
75-
}
76-
if (variant instanceof ast.AdditiveExpression) {
77-
return new AdditiveExpression(variant, collected, options);
78-
}
79-
if (variant instanceof ast.MultiplicativeExpression) {
80-
return new MultiplicativeExpression(variant, collected, options);
81-
}
82-
if (variant instanceof ast.ExponentiationExpression) {
83-
return new ExponentiationExpression(variant, collected, options);
84-
}
85-
if (variant instanceof ast.PostfixExpression) {
86-
return new PostfixExpression(variant, collected, options);
87-
}
88-
if (variant instanceof ast.PrefixExpression) {
89-
return new PrefixExpression(variant, collected, options);
90-
}
91-
if (variant instanceof ast.FunctionCallExpression) {
92-
return new FunctionCallExpression(variant, collected, options);
93-
}
94-
if (variant instanceof ast.CallOptionsExpression) {
95-
return new CallOptionsExpression(variant, collected, options);
96-
}
97-
if (variant instanceof ast.MemberAccessExpression) {
98-
return new MemberAccessExpression(variant, collected, options);
99-
}
100-
if (variant instanceof ast.IndexAccessExpression) {
101-
return new IndexAccessExpression(variant, collected, options);
102-
}
103-
if (variant instanceof ast.NewExpression) {
104-
return new NewExpression(variant, collected, options);
105-
}
106-
if (variant instanceof ast.TupleExpression) {
107-
return new TupleExpression(variant, collected, options);
108-
}
109-
if (variant instanceof ast.TypeExpression) {
110-
return new TypeExpression(variant, collected, options);
111-
}
112-
if (variant instanceof ast.ArrayExpression) {
113-
return new ArrayExpression(variant, collected, options);
114-
}
115-
if (variant instanceof ast.HexNumberExpression) {
116-
return new HexNumberExpression(variant, collected);
117-
}
118-
if (variant instanceof ast.DecimalNumberExpression) {
119-
return new DecimalNumberExpression(variant, collected);
120-
}
121-
if (variant instanceof ast.StringExpression) {
122-
return extractVariant(new StringExpression(variant, collected, options));
123-
}
124-
if (variant instanceof ast.ElementaryType) {
125-
return extractVariant(new ElementaryType(variant, collected));
126-
}
127-
const exhaustiveCheck: never = variant;
128-
throw new Error(`Unexpected variant: ${JSON.stringify(exhaustiveCheck)}`);
129-
}
41+
const createNonterminalVariant = createNonterminalVariantCreator<
42+
ast.Expression,
43+
Expression
44+
>(
45+
[
46+
[ast.AssignmentExpression, AssignmentExpression],
47+
[ast.ConditionalExpression, ConditionalExpression],
48+
[ast.OrExpression, OrExpression],
49+
[ast.AndExpression, AndExpression],
50+
[ast.EqualityExpression, EqualityExpression],
51+
[ast.InequalityExpression, InequalityExpression],
52+
[ast.BitwiseOrExpression, BitwiseOrExpression],
53+
[ast.BitwiseXorExpression, BitwiseXorExpression],
54+
[ast.BitwiseAndExpression, BitwiseAndExpression],
55+
[ast.ShiftExpression, ShiftExpression],
56+
[ast.AdditiveExpression, AdditiveExpression],
57+
[ast.MultiplicativeExpression, MultiplicativeExpression],
58+
[ast.ExponentiationExpression, ExponentiationExpression],
59+
[ast.PostfixExpression, PostfixExpression],
60+
[ast.PrefixExpression, PrefixExpression],
61+
[ast.FunctionCallExpression, FunctionCallExpression],
62+
[ast.CallOptionsExpression, CallOptionsExpression],
63+
[ast.MemberAccessExpression, MemberAccessExpression],
64+
[ast.IndexAccessExpression, IndexAccessExpression],
65+
[ast.NewExpression, NewExpression],
66+
[ast.TupleExpression, TupleExpression],
67+
[ast.TypeExpression, TypeExpression],
68+
[ast.ArrayExpression, ArrayExpression],
69+
[ast.HexNumberExpression, HexNumberExpression],
70+
[ast.DecimalNumberExpression, DecimalNumberExpression]
71+
],
72+
[
73+
[ast.StringExpression, StringExpression],
74+
[ast.ElementaryType, ElementaryType]
75+
]
76+
);
13077

13178
export class Expression extends SlangNode {
13279
readonly kind = NonterminalKind.Expression;

src/slang-nodes/FallbackFunctionAttribute.ts

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
NonterminalKind,
44
TerminalNode as SlangTerminalNode
55
} from '@nomicfoundation/slang/cst';
6+
import { createNonterminalVariantSimpleCreator } from '../slang-utils/create-nonterminal-variant-creator.js';
67
import { SlangNode } from './SlangNode.js';
78
import { ModifierInvocation } from './ModifierInvocation.js';
89
import { OverrideSpecifier } from './OverrideSpecifier.js';
@@ -12,20 +13,13 @@ import type { ParserOptions } from 'prettier';
1213
import type { CollectedMetadata } from '../types.d.ts';
1314
import type { AstNode } from './types.d.ts';
1415

15-
function createNonterminalVariant(
16-
variant: Exclude<ast.FallbackFunctionAttribute['variant'], SlangTerminalNode>,
17-
collected: CollectedMetadata,
18-
options: ParserOptions<AstNode>
19-
): Exclude<FallbackFunctionAttribute['variant'], TerminalNode> {
20-
if (variant instanceof ast.ModifierInvocation) {
21-
return new ModifierInvocation(variant, collected, options);
22-
}
23-
if (variant instanceof ast.OverrideSpecifier) {
24-
return new OverrideSpecifier(variant, collected);
25-
}
26-
const exhaustiveCheck: never = variant;
27-
throw new Error(`Unexpected variant: ${JSON.stringify(exhaustiveCheck)}`);
28-
}
16+
const createNonterminalVariant = createNonterminalVariantSimpleCreator<
17+
ast.FallbackFunctionAttribute,
18+
FallbackFunctionAttribute
19+
>([
20+
[ast.ModifierInvocation, ModifierInvocation],
21+
[ast.OverrideSpecifier, OverrideSpecifier]
22+
]);
2923

3024
export class FallbackFunctionAttribute extends SlangNode {
3125
readonly kind = NonterminalKind.FallbackFunctionAttribute;

src/slang-nodes/ForStatementInitialization.ts

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
NonterminalKind,
44
TerminalNode as SlangTerminalNode
55
} from '@nomicfoundation/slang/cst';
6+
import { createNonterminalVariantSimpleCreator } from '../slang-utils/create-nonterminal-variant-creator.js';
67
import { SlangNode } from './SlangNode.js';
78
import { ExpressionStatement } from './ExpressionStatement.js';
89
import { VariableDeclarationStatement } from './VariableDeclarationStatement.js';
@@ -13,26 +14,14 @@ import type { ParserOptions } from 'prettier';
1314
import type { CollectedMetadata } from '../types.d.ts';
1415
import type { AstNode } from './types.d.ts';
1516

16-
function createNonterminalVariant(
17-
variant: Exclude<
18-
ast.ForStatementInitialization['variant'],
19-
SlangTerminalNode
20-
>,
21-
collected: CollectedMetadata,
22-
options: ParserOptions<AstNode>
23-
): Exclude<ForStatementInitialization['variant'], TerminalNode> {
24-
if (variant instanceof ast.ExpressionStatement) {
25-
return new ExpressionStatement(variant, collected, options);
26-
}
27-
if (variant instanceof ast.VariableDeclarationStatement) {
28-
return new VariableDeclarationStatement(variant, collected, options);
29-
}
30-
if (variant instanceof ast.TupleDeconstructionStatement) {
31-
return new TupleDeconstructionStatement(variant, collected, options);
32-
}
33-
const exhaustiveCheck: never = variant;
34-
throw new Error(`Unexpected variant: ${JSON.stringify(exhaustiveCheck)}`);
35-
}
17+
const createNonterminalVariant = createNonterminalVariantSimpleCreator<
18+
ast.ForStatementInitialization,
19+
ForStatementInitialization
20+
>([
21+
[ast.ExpressionStatement, ExpressionStatement],
22+
[ast.VariableDeclarationStatement, VariableDeclarationStatement],
23+
[ast.TupleDeconstructionStatement, TupleDeconstructionStatement]
24+
]);
3625

3726
export class ForStatementInitialization extends SlangNode {
3827
readonly kind = NonterminalKind.ForStatementInitialization;

0 commit comments

Comments
 (0)