diff --git a/src/slang-nodes/ArgumentsDeclaration.ts b/src/slang-nodes/ArgumentsDeclaration.ts index 5c2967544..ce6eb22ca 100644 --- a/src/slang-nodes/ArgumentsDeclaration.ts +++ b/src/slang-nodes/ArgumentsDeclaration.ts @@ -1,13 +1,27 @@ +import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; import { SlangNode } from './SlangNode.js'; import { PositionalArgumentsDeclaration } from './PositionalArgumentsDeclaration.js'; import { NamedArgumentsDeclaration } from './NamedArgumentsDeclaration.js'; -import type * as ast from '@nomicfoundation/slang/ast'; import type { AstPath, Doc, ParserOptions } from 'prettier'; import type { AstNode } from './types.d.ts'; import type { PrintFunction } from '../types.d.ts'; +function createNonterminalVariant( + variant: ast.ArgumentsDeclaration['variant'], + options: ParserOptions +): ArgumentsDeclaration['variant'] { + if (variant instanceof ast.PositionalArgumentsDeclaration) { + return new PositionalArgumentsDeclaration(variant, options); + } + if (variant instanceof ast.NamedArgumentsDeclaration) { + return new NamedArgumentsDeclaration(variant, options); + } + const exhaustiveCheck: never = variant; + return exhaustiveCheck; +} + export class ArgumentsDeclaration extends SlangNode { readonly kind = NonterminalKind.ArgumentsDeclaration; @@ -16,22 +30,7 @@ export class ArgumentsDeclaration extends SlangNode { constructor(ast: ast.ArgumentsDeclaration, options: ParserOptions) { super(ast); - switch (ast.variant.cst.kind) { - case NonterminalKind.PositionalArgumentsDeclaration: - this.variant = new PositionalArgumentsDeclaration( - ast.variant as ast.PositionalArgumentsDeclaration, - options - ); - break; - case NonterminalKind.NamedArgumentsDeclaration: - this.variant = new NamedArgumentsDeclaration( - ast.variant as ast.NamedArgumentsDeclaration, - options - ); - break; - default: - throw new Error(`Unexpected variant: ${ast.variant.cst.kind}`); - } + this.variant = createNonterminalVariant(ast.variant, options); this.updateMetadata(this.variant); } diff --git a/src/slang-nodes/ConstructorAttribute.ts b/src/slang-nodes/ConstructorAttribute.ts index b050d45cf..5de3cf4fc 100644 --- a/src/slang-nodes/ConstructorAttribute.ts +++ b/src/slang-nodes/ConstructorAttribute.ts @@ -1,4 +1,5 @@ import { NonterminalKind, TerminalNode } from '@nomicfoundation/slang/cst'; +import { printVariant } from '../slang-printers/print-variant.js'; import { SlangNode } from './SlangNode.js'; import { ModifierInvocation } from './ModifierInvocation.js'; @@ -15,17 +16,17 @@ export class ConstructorAttribute extends SlangNode { constructor(ast: ast.ConstructorAttribute, options: ParserOptions) { super(ast); - this.variant = - ast.variant instanceof TerminalNode - ? ast.variant.unparse() - : new ModifierInvocation(ast.variant, options); + const variant = ast.variant; + if (variant instanceof TerminalNode) { + this.variant = variant.unparse(); + return; + } + this.variant = new ModifierInvocation(variant, options); - if (typeof this.variant !== 'string') this.updateMetadata(this.variant); + this.updateMetadata(this.variant); } print(path: AstPath, print: PrintFunction): Doc { - return typeof this.variant === 'string' - ? this.variant - : path.call(print, 'variant'); + return printVariant(this, path, print); } } diff --git a/src/slang-nodes/ContractMember.ts b/src/slang-nodes/ContractMember.ts index b74ed3393..fa41a309c 100644 --- a/src/slang-nodes/ContractMember.ts +++ b/src/slang-nodes/ContractMember.ts @@ -1,3 +1,4 @@ +import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; import { SlangNode } from './SlangNode.js'; import { UsingDirective } from './UsingDirective.js'; @@ -14,11 +15,57 @@ import { StateVariableDefinition } from './StateVariableDefinition.js'; import { ErrorDefinition } from './ErrorDefinition.js'; import { UserDefinedValueTypeDefinition } from './UserDefinedValueTypeDefinition.js'; -import type * as ast from '@nomicfoundation/slang/ast'; import type { AstPath, Doc, ParserOptions } from 'prettier'; import type { AstNode } from './types.d.ts'; import type { PrintFunction } from '../types.d.ts'; +function createNonterminalVariant( + variant: ast.ContractMember['variant'], + options: ParserOptions +): ContractMember['variant'] { + if (variant instanceof ast.UsingDirective) { + return new UsingDirective(variant, options); + } + if (variant instanceof ast.FunctionDefinition) { + return new FunctionDefinition(variant, options); + } + if (variant instanceof ast.ConstructorDefinition) { + return new ConstructorDefinition(variant, options); + } + if (variant instanceof ast.ReceiveFunctionDefinition) { + return new ReceiveFunctionDefinition(variant, options); + } + if (variant instanceof ast.FallbackFunctionDefinition) { + return new FallbackFunctionDefinition(variant, options); + } + if (variant instanceof ast.UnnamedFunctionDefinition) { + return new UnnamedFunctionDefinition(variant, options); + } + if (variant instanceof ast.ModifierDefinition) { + return new ModifierDefinition(variant, options); + } + if (variant instanceof ast.StructDefinition) { + return new StructDefinition(variant, options); + } + if (variant instanceof ast.EnumDefinition) { + return new EnumDefinition(variant); + } + if (variant instanceof ast.EventDefinition) { + return new EventDefinition(variant, options); + } + if (variant instanceof ast.StateVariableDefinition) { + return new StateVariableDefinition(variant, options); + } + if (variant instanceof ast.ErrorDefinition) { + return new ErrorDefinition(variant, options); + } + if (variant instanceof ast.UserDefinedValueTypeDefinition) { + return new UserDefinedValueTypeDefinition(variant); + } + const exhaustiveCheck: never = variant; + return exhaustiveCheck; +} + export class ContractMember extends SlangNode { readonly kind = NonterminalKind.ContractMember; @@ -40,84 +87,7 @@ export class ContractMember extends SlangNode { constructor(ast: ast.ContractMember, options: ParserOptions) { super(ast); - switch (ast.variant.cst.kind) { - case NonterminalKind.UsingDirective: - this.variant = new UsingDirective( - ast.variant as ast.UsingDirective, - options - ); - break; - case NonterminalKind.FunctionDefinition: - this.variant = new FunctionDefinition( - ast.variant as ast.FunctionDefinition, - options - ); - break; - case NonterminalKind.ConstructorDefinition: - this.variant = new ConstructorDefinition( - ast.variant as ast.ConstructorDefinition, - options - ); - break; - case NonterminalKind.ReceiveFunctionDefinition: - this.variant = new ReceiveFunctionDefinition( - ast.variant as ast.ReceiveFunctionDefinition, - options - ); - break; - case NonterminalKind.FallbackFunctionDefinition: - this.variant = new FallbackFunctionDefinition( - ast.variant as ast.FallbackFunctionDefinition, - options - ); - break; - case NonterminalKind.UnnamedFunctionDefinition: - this.variant = new UnnamedFunctionDefinition( - ast.variant as ast.UnnamedFunctionDefinition, - options - ); - break; - case NonterminalKind.ModifierDefinition: - this.variant = new ModifierDefinition( - ast.variant as ast.ModifierDefinition, - options - ); - break; - case NonterminalKind.StructDefinition: - this.variant = new StructDefinition( - ast.variant as ast.StructDefinition, - options - ); - break; - case NonterminalKind.EnumDefinition: - this.variant = new EnumDefinition(ast.variant as ast.EnumDefinition); - break; - case NonterminalKind.EventDefinition: - this.variant = new EventDefinition( - ast.variant as ast.EventDefinition, - options - ); - break; - case NonterminalKind.StateVariableDefinition: - this.variant = new StateVariableDefinition( - ast.variant as ast.StateVariableDefinition, - options - ); - break; - case NonterminalKind.ErrorDefinition: - this.variant = new ErrorDefinition( - ast.variant as ast.ErrorDefinition, - options - ); - break; - case NonterminalKind.UserDefinedValueTypeDefinition: - this.variant = new UserDefinedValueTypeDefinition( - ast.variant as ast.UserDefinedValueTypeDefinition - ); - break; - default: - throw new Error(`Unexpected variant: ${ast.variant.cst.kind}`); - } + this.variant = createNonterminalVariant(ast.variant, options); this.updateMetadata(this.variant); } diff --git a/src/slang-nodes/ContractSpecifier.ts b/src/slang-nodes/ContractSpecifier.ts index 0b0030378..a46015215 100644 --- a/src/slang-nodes/ContractSpecifier.ts +++ b/src/slang-nodes/ContractSpecifier.ts @@ -1,13 +1,27 @@ +import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; import { SlangNode } from './SlangNode.js'; import { InheritanceSpecifier } from './InheritanceSpecifier.js'; import { StorageLayoutSpecifier } from './StorageLayoutSpecifier.js'; -import type * as ast from '@nomicfoundation/slang/ast'; import type { AstPath, Doc, ParserOptions } from 'prettier'; import type { AstNode } from './types.d.ts'; import type { PrintFunction } from '../types.d.ts'; +function createNonterminalVariant( + variant: ast.ContractSpecifier['variant'], + options: ParserOptions +): ContractSpecifier['variant'] { + if (variant instanceof ast.InheritanceSpecifier) { + return new InheritanceSpecifier(variant, options); + } + if (variant instanceof ast.StorageLayoutSpecifier) { + return new StorageLayoutSpecifier(variant, options); + } + const exhaustiveCheck: never = variant; + return exhaustiveCheck; +} + export class ContractSpecifier extends SlangNode { readonly kind = NonterminalKind.ContractSpecifier; @@ -16,22 +30,8 @@ export class ContractSpecifier extends SlangNode { constructor(ast: ast.ContractSpecifier, options: ParserOptions) { super(ast); - switch (ast.variant.cst.kind) { - case NonterminalKind.InheritanceSpecifier: - this.variant = new InheritanceSpecifier( - ast.variant as ast.InheritanceSpecifier, - options - ); - break; - case NonterminalKind.StorageLayoutSpecifier: - this.variant = new StorageLayoutSpecifier( - ast.variant as ast.StorageLayoutSpecifier, - options - ); - break; - default: - throw new Error(`Unexpected variant: ${ast.variant.cst.kind}`); - } + this.variant = createNonterminalVariant(ast.variant, options); + this.updateMetadata(this.variant); } diff --git a/src/slang-nodes/ElementaryType.ts b/src/slang-nodes/ElementaryType.ts index 1e28c45b1..db4c63f19 100644 --- a/src/slang-nodes/ElementaryType.ts +++ b/src/slang-nodes/ElementaryType.ts @@ -1,4 +1,5 @@ import { NonterminalKind, TerminalNode } from '@nomicfoundation/slang/cst'; +import { printVariant } from '../slang-printers/print-variant.js'; import { SlangNode } from './SlangNode.js'; import { AddressType } from './AddressType.js'; @@ -14,17 +15,17 @@ export class ElementaryType extends SlangNode { constructor(ast: ast.ElementaryType) { super(ast); - this.variant = - ast.variant instanceof TerminalNode - ? ast.variant.unparse() - : new AddressType(ast.variant); + const variant = ast.variant; + if (variant instanceof TerminalNode) { + this.variant = variant.unparse(); + return; + } + this.variant = new AddressType(variant); - if (typeof this.variant !== 'string') this.updateMetadata(this.variant); + this.updateMetadata(this.variant); } print(path: AstPath, print: PrintFunction): Doc { - return typeof this.variant === 'string' - ? this.variant - : path.call(print, 'variant'); + return printVariant(this, path, print); } } diff --git a/src/slang-nodes/ExperimentalFeature.ts b/src/slang-nodes/ExperimentalFeature.ts index fbf84674a..b6fa3fad8 100644 --- a/src/slang-nodes/ExperimentalFeature.ts +++ b/src/slang-nodes/ExperimentalFeature.ts @@ -1,8 +1,4 @@ -import { - NonterminalKind, - TerminalKind, - TerminalNode -} from '@nomicfoundation/slang/cst'; +import { NonterminalKind, TerminalNode } from '@nomicfoundation/slang/cst'; import { SlangNode } from './SlangNode.js'; import { StringLiteral } from './StringLiteral.js'; import { Identifier } from './Identifier.js'; @@ -20,13 +16,14 @@ export class ExperimentalFeature extends SlangNode { constructor(ast: ast.ExperimentalFeature, options: ParserOptions) { super(ast); - this.variant = - ast.variant instanceof TerminalNode - ? new Identifier(ast.variant) - : new StringLiteral(ast.variant, options); + const variant = ast.variant; + if (variant instanceof TerminalNode) { + this.variant = new Identifier(variant); + return; + } + this.variant = new StringLiteral(variant, options); - if (this.variant.kind !== TerminalKind.Identifier) - this.updateMetadata(this.variant); + this.updateMetadata(this.variant); } print(path: AstPath, print: PrintFunction): Doc { diff --git a/src/slang-nodes/Expression.ts b/src/slang-nodes/Expression.ts index 008c52e44..0d21154e0 100644 --- a/src/slang-nodes/Expression.ts +++ b/src/slang-nodes/Expression.ts @@ -1,8 +1,5 @@ -import { - NonterminalKind, - TerminalKind, - TerminalNode -} from '@nomicfoundation/slang/cst'; +import * as ast from '@nomicfoundation/slang/ast'; +import { NonterminalKind, TerminalNode } from '@nomicfoundation/slang/cst'; import { SlangNode } from './SlangNode.js'; import { AssignmentExpression } from './AssignmentExpression.js'; import { ConditionalExpression } from './ConditionalExpression.js'; @@ -33,11 +30,99 @@ import { StringExpression } from './StringExpression.js'; import { ElementaryType } from './ElementaryType.js'; import { Identifier } from './Identifier.js'; -import type * as ast from '@nomicfoundation/slang/ast'; import type { AstPath, Doc, ParserOptions } from 'prettier'; import type { AstNode } from './types.d.ts'; import type { PrintFunction } from '../types.d.ts'; +function createNonterminalVariant( + variant: Exclude, + options: ParserOptions +): Exclude { + if (variant instanceof ast.AssignmentExpression) { + return new AssignmentExpression(variant, options); + } + if (variant instanceof ast.ConditionalExpression) { + return new ConditionalExpression(variant, options); + } + if (variant instanceof ast.OrExpression) { + return new OrExpression(variant, options); + } + if (variant instanceof ast.AndExpression) { + return new AndExpression(variant, options); + } + if (variant instanceof ast.EqualityExpression) { + return new EqualityExpression(variant, options); + } + if (variant instanceof ast.InequalityExpression) { + return new InequalityExpression(variant, options); + } + if (variant instanceof ast.BitwiseOrExpression) { + return new BitwiseOrExpression(variant, options); + } + if (variant instanceof ast.BitwiseXorExpression) { + return new BitwiseXorExpression(variant, options); + } + if (variant instanceof ast.BitwiseAndExpression) { + return new BitwiseAndExpression(variant, options); + } + if (variant instanceof ast.ShiftExpression) { + return new ShiftExpression(variant, options); + } + if (variant instanceof ast.AdditiveExpression) { + return new AdditiveExpression(variant, options); + } + if (variant instanceof ast.MultiplicativeExpression) { + return new MultiplicativeExpression(variant, options); + } + if (variant instanceof ast.ExponentiationExpression) { + return new ExponentiationExpression(variant, options); + } + if (variant instanceof ast.PostfixExpression) { + return new PostfixExpression(variant, options); + } + if (variant instanceof ast.PrefixExpression) { + return new PrefixExpression(variant, options); + } + if (variant instanceof ast.FunctionCallExpression) { + return new FunctionCallExpression(variant, options); + } + if (variant instanceof ast.CallOptionsExpression) { + return new CallOptionsExpression(variant, options); + } + if (variant instanceof ast.MemberAccessExpression) { + return new MemberAccessExpression(variant, options); + } + if (variant instanceof ast.IndexAccessExpression) { + return new IndexAccessExpression(variant, options); + } + if (variant instanceof ast.NewExpression) { + return new NewExpression(variant, options); + } + if (variant instanceof ast.TupleExpression) { + return new TupleExpression(variant, options); + } + if (variant instanceof ast.TypeExpression) { + return new TypeExpression(variant, options); + } + if (variant instanceof ast.ArrayExpression) { + return new ArrayExpression(variant, options); + } + if (variant instanceof ast.HexNumberExpression) { + return new HexNumberExpression(variant); + } + if (variant instanceof ast.DecimalNumberExpression) { + return new DecimalNumberExpression(variant); + } + if (variant instanceof ast.StringExpression) { + return new StringExpression(variant, options); + } + if (variant instanceof ast.ElementaryType) { + return new ElementaryType(variant); + } + const exhaustiveCheck: never = variant; + return exhaustiveCheck; +} + export class Expression extends SlangNode { readonly kind = NonterminalKind.Expression; @@ -74,174 +159,14 @@ export class Expression extends SlangNode { constructor(ast: ast.Expression, options: ParserOptions) { super(ast); - if (ast.variant instanceof TerminalNode) { - this.variant = new Identifier(ast.variant); - } else { - switch (ast.variant.cst.kind) { - case NonterminalKind.AssignmentExpression: - this.variant = new AssignmentExpression( - ast.variant as ast.AssignmentExpression, - options - ); - break; - case NonterminalKind.ConditionalExpression: - this.variant = new ConditionalExpression( - ast.variant as ast.ConditionalExpression, - options - ); - break; - case NonterminalKind.OrExpression: - this.variant = new OrExpression( - ast.variant as ast.OrExpression, - options - ); - break; - case NonterminalKind.AndExpression: - this.variant = new AndExpression( - ast.variant as ast.AndExpression, - options - ); - break; - case NonterminalKind.EqualityExpression: - this.variant = new EqualityExpression( - ast.variant as ast.EqualityExpression, - options - ); - break; - case NonterminalKind.InequalityExpression: - this.variant = new InequalityExpression( - ast.variant as ast.InequalityExpression, - options - ); - break; - case NonterminalKind.BitwiseOrExpression: - this.variant = new BitwiseOrExpression( - ast.variant as ast.BitwiseOrExpression, - options - ); - break; - case NonterminalKind.BitwiseXorExpression: - this.variant = new BitwiseXorExpression( - ast.variant as ast.BitwiseXorExpression, - options - ); - break; - case NonterminalKind.BitwiseAndExpression: - this.variant = new BitwiseAndExpression( - ast.variant as ast.BitwiseAndExpression, - options - ); - break; - case NonterminalKind.ShiftExpression: - this.variant = new ShiftExpression( - ast.variant as ast.ShiftExpression, - options - ); - break; - case NonterminalKind.AdditiveExpression: - this.variant = new AdditiveExpression( - ast.variant as ast.AdditiveExpression, - options - ); - break; - case NonterminalKind.MultiplicativeExpression: - this.variant = new MultiplicativeExpression( - ast.variant as ast.MultiplicativeExpression, - options - ); - break; - case NonterminalKind.ExponentiationExpression: - this.variant = new ExponentiationExpression( - ast.variant as ast.ExponentiationExpression, - options - ); - break; - case NonterminalKind.PostfixExpression: - this.variant = new PostfixExpression( - ast.variant as ast.PostfixExpression, - options - ); - break; - case NonterminalKind.PrefixExpression: - this.variant = new PrefixExpression( - ast.variant as ast.PrefixExpression, - options - ); - break; - case NonterminalKind.FunctionCallExpression: - this.variant = new FunctionCallExpression( - ast.variant as ast.FunctionCallExpression, - options - ); - break; - case NonterminalKind.CallOptionsExpression: - this.variant = new CallOptionsExpression( - ast.variant as ast.CallOptionsExpression, - options - ); - break; - case NonterminalKind.MemberAccessExpression: - this.variant = new MemberAccessExpression( - ast.variant as ast.MemberAccessExpression, - options - ); - break; - case NonterminalKind.IndexAccessExpression: - this.variant = new IndexAccessExpression( - ast.variant as ast.IndexAccessExpression, - options - ); - break; - case NonterminalKind.NewExpression: - this.variant = new NewExpression( - ast.variant as ast.NewExpression, - options - ); - break; - case NonterminalKind.TupleExpression: - this.variant = new TupleExpression( - ast.variant as ast.TupleExpression, - options - ); - break; - case NonterminalKind.TypeExpression: - this.variant = new TypeExpression( - ast.variant as ast.TypeExpression, - options - ); - break; - case NonterminalKind.ArrayExpression: - this.variant = new ArrayExpression( - ast.variant as ast.ArrayExpression, - options - ); - break; - case NonterminalKind.HexNumberExpression: - this.variant = new HexNumberExpression( - ast.variant as ast.HexNumberExpression - ); - break; - case NonterminalKind.DecimalNumberExpression: - this.variant = new DecimalNumberExpression( - ast.variant as ast.DecimalNumberExpression - ); - break; - case NonterminalKind.StringExpression: - this.variant = new StringExpression( - ast.variant as ast.StringExpression, - options - ); - break; - case NonterminalKind.ElementaryType: - this.variant = new ElementaryType(ast.variant as ast.ElementaryType); - break; - default: - throw new Error(`Unexpected variant: ${ast.variant.cst.kind}`); - } + const variant = ast.variant; + if (variant instanceof TerminalNode) { + this.variant = new Identifier(variant); + return; } + this.variant = createNonterminalVariant(variant, options); - if (this.variant.kind !== TerminalKind.Identifier) - this.updateMetadata(this.variant); + this.updateMetadata(this.variant); } print(path: AstPath, print: PrintFunction): Doc { diff --git a/src/slang-nodes/FallbackFunctionAttribute.ts b/src/slang-nodes/FallbackFunctionAttribute.ts index e346aef99..0b65983a1 100644 --- a/src/slang-nodes/FallbackFunctionAttribute.ts +++ b/src/slang-nodes/FallbackFunctionAttribute.ts @@ -1,13 +1,28 @@ +import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind, TerminalNode } from '@nomicfoundation/slang/cst'; +import { printVariant } from '../slang-printers/print-variant.js'; import { SlangNode } from './SlangNode.js'; import { ModifierInvocation } from './ModifierInvocation.js'; import { OverrideSpecifier } from './OverrideSpecifier.js'; -import type * as ast from '@nomicfoundation/slang/ast'; import type { AstPath, Doc, ParserOptions } from 'prettier'; import type { AstNode } from './types.d.ts'; import type { PrintFunction } from '../types.d.ts'; +function createNonterminalVariant( + variant: Exclude, + options: ParserOptions +): Exclude { + if (variant instanceof ast.ModifierInvocation) { + return new ModifierInvocation(variant, options); + } + if (variant instanceof ast.OverrideSpecifier) { + return new OverrideSpecifier(variant); + } + const exhaustiveCheck: never = variant; + return exhaustiveCheck; +} + export class FallbackFunctionAttribute extends SlangNode { readonly kind = NonterminalKind.FallbackFunctionAttribute; @@ -19,32 +34,17 @@ export class FallbackFunctionAttribute extends SlangNode { ) { super(ast); - if (ast.variant instanceof TerminalNode) { - this.variant = ast.variant.unparse(); - } else { - switch (ast.variant.cst.kind) { - case NonterminalKind.ModifierInvocation: - this.variant = new ModifierInvocation( - ast.variant as ast.ModifierInvocation, - options - ); - break; - case NonterminalKind.OverrideSpecifier: - this.variant = new OverrideSpecifier( - ast.variant as ast.OverrideSpecifier - ); - break; - default: - throw new Error(`Unexpected variant: ${ast.variant.cst.kind}`); - } + const variant = ast.variant; + if (variant instanceof TerminalNode) { + this.variant = variant.unparse(); + return; } + this.variant = createNonterminalVariant(variant, options); - if (typeof this.variant !== 'string') this.updateMetadata(this.variant); + this.updateMetadata(this.variant); } print(path: AstPath, print: PrintFunction): Doc { - return typeof this.variant === 'string' - ? this.variant - : path.call(print, 'variant'); + return printVariant(this, path, print); } } diff --git a/src/slang-nodes/ForStatementCondition.ts b/src/slang-nodes/ForStatementCondition.ts index 4aae16e23..15c95a31b 100644 --- a/src/slang-nodes/ForStatementCondition.ts +++ b/src/slang-nodes/ForStatementCondition.ts @@ -1,4 +1,5 @@ import { NonterminalKind, TerminalNode } from '@nomicfoundation/slang/cst'; +import { printVariant } from '../slang-printers/print-variant.js'; import { SlangNode } from './SlangNode.js'; import { ExpressionStatement } from './ExpressionStatement.js'; @@ -15,17 +16,17 @@ export class ForStatementCondition extends SlangNode { constructor(ast: ast.ForStatementCondition, options: ParserOptions) { super(ast); - this.variant = - ast.variant instanceof TerminalNode - ? ast.variant.unparse() - : new ExpressionStatement(ast.variant, options); + const variant = ast.variant; + if (variant instanceof TerminalNode) { + this.variant = variant.unparse(); + return; + } + this.variant = new ExpressionStatement(variant, options); - if (typeof this.variant !== 'string') this.updateMetadata(this.variant); + this.updateMetadata(this.variant); } print(path: AstPath, print: PrintFunction): Doc { - return typeof this.variant === 'string' - ? this.variant - : path.call(print, 'variant'); + return printVariant(this, path, print); } } diff --git a/src/slang-nodes/ForStatementInitialization.ts b/src/slang-nodes/ForStatementInitialization.ts index 95a7a879d..48d3c2b40 100644 --- a/src/slang-nodes/ForStatementInitialization.ts +++ b/src/slang-nodes/ForStatementInitialization.ts @@ -1,14 +1,32 @@ +import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind, TerminalNode } from '@nomicfoundation/slang/cst'; +import { printVariant } from '../slang-printers/print-variant.js'; import { SlangNode } from './SlangNode.js'; import { ExpressionStatement } from './ExpressionStatement.js'; import { VariableDeclarationStatement } from './VariableDeclarationStatement.js'; import { TupleDeconstructionStatement } from './TupleDeconstructionStatement.js'; -import type * as ast from '@nomicfoundation/slang/ast'; import type { AstPath, Doc, ParserOptions } from 'prettier'; import type { AstNode } from './types.d.ts'; import type { PrintFunction } from '../types.d.ts'; +function createNonterminalVariant( + variant: Exclude, + options: ParserOptions +): Exclude { + if (variant instanceof ast.ExpressionStatement) { + return new ExpressionStatement(variant, options); + } + if (variant instanceof ast.VariableDeclarationStatement) { + return new VariableDeclarationStatement(variant, options); + } + if (variant instanceof ast.TupleDeconstructionStatement) { + return new TupleDeconstructionStatement(variant, options); + } + const exhaustiveCheck: never = variant; + return exhaustiveCheck; +} + export class ForStatementInitialization extends SlangNode { readonly kind = NonterminalKind.ForStatementInitialization; @@ -24,39 +42,17 @@ export class ForStatementInitialization extends SlangNode { ) { super(ast); - if (ast.variant instanceof TerminalNode) { - this.variant = ast.variant.unparse(); - } else { - switch (ast.variant.cst.kind) { - case NonterminalKind.ExpressionStatement: - this.variant = new ExpressionStatement( - ast.variant as ast.ExpressionStatement, - options - ); - break; - case NonterminalKind.VariableDeclarationStatement: - this.variant = new VariableDeclarationStatement( - ast.variant as ast.VariableDeclarationStatement, - options - ); - break; - case NonterminalKind.TupleDeconstructionStatement: - this.variant = new TupleDeconstructionStatement( - ast.variant as ast.TupleDeconstructionStatement, - options - ); - break; - default: - throw new Error(`Unexpected variant: ${ast.variant.cst.kind}`); - } + const variant = ast.variant; + if (variant instanceof TerminalNode) { + this.variant = variant.unparse(); + return; } + this.variant = createNonterminalVariant(variant, options); - if (typeof this.variant !== 'string') this.updateMetadata(this.variant); + this.updateMetadata(this.variant); } print(path: AstPath, print: PrintFunction): Doc { - return typeof this.variant === 'string' - ? this.variant - : path.call(print, 'variant'); + return printVariant(this, path, print); } } diff --git a/src/slang-nodes/FunctionAttribute.ts b/src/slang-nodes/FunctionAttribute.ts index 55769bb44..e1035622c 100644 --- a/src/slang-nodes/FunctionAttribute.ts +++ b/src/slang-nodes/FunctionAttribute.ts @@ -1,13 +1,28 @@ +import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind, TerminalNode } from '@nomicfoundation/slang/cst'; +import { printVariant } from '../slang-printers/print-variant.js'; import { SlangNode } from './SlangNode.js'; import { ModifierInvocation } from './ModifierInvocation.js'; import { OverrideSpecifier } from './OverrideSpecifier.js'; -import type * as ast from '@nomicfoundation/slang/ast'; import type { AstPath, Doc, ParserOptions } from 'prettier'; import type { AstNode } from './types.d.ts'; import type { PrintFunction } from '../types.d.ts'; +function createNonterminalVariant( + variant: Exclude, + options: ParserOptions +): Exclude { + if (variant instanceof ast.ModifierInvocation) { + return new ModifierInvocation(variant, options); + } + if (variant instanceof ast.OverrideSpecifier) { + return new OverrideSpecifier(variant); + } + const exhaustiveCheck: never = variant; + return exhaustiveCheck; +} + export class FunctionAttribute extends SlangNode { readonly kind = NonterminalKind.FunctionAttribute; @@ -16,32 +31,17 @@ export class FunctionAttribute extends SlangNode { constructor(ast: ast.FunctionAttribute, options: ParserOptions) { super(ast); - if (ast.variant instanceof TerminalNode) { - this.variant = ast.variant.unparse(); - } else { - switch (ast.variant.cst.kind) { - case NonterminalKind.ModifierInvocation: - this.variant = new ModifierInvocation( - ast.variant as ast.ModifierInvocation, - options - ); - break; - case NonterminalKind.OverrideSpecifier: - this.variant = new OverrideSpecifier( - ast.variant as ast.OverrideSpecifier - ); - break; - default: - throw new Error(`Unexpected variant: ${ast.variant.cst.kind}`); - } + const variant = ast.variant; + if (variant instanceof TerminalNode) { + this.variant = variant.unparse(); + return; } + this.variant = createNonterminalVariant(variant, options); - if (typeof this.variant !== 'string') this.updateMetadata(this.variant); + this.updateMetadata(this.variant); } print(path: AstPath, print: PrintFunction): Doc { - return typeof this.variant === 'string' - ? this.variant - : path.call(print, 'variant'); + return printVariant(this, path, print); } } diff --git a/src/slang-nodes/FunctionBody.ts b/src/slang-nodes/FunctionBody.ts index 9ab675215..94322bae0 100644 --- a/src/slang-nodes/FunctionBody.ts +++ b/src/slang-nodes/FunctionBody.ts @@ -1,4 +1,5 @@ import { NonterminalKind, TerminalNode } from '@nomicfoundation/slang/cst'; +import { printVariant } from '../slang-printers/print-variant.js'; import { SlangNode } from './SlangNode.js'; import { Block } from './Block.js'; @@ -15,17 +16,17 @@ export class FunctionBody extends SlangNode { constructor(ast: ast.FunctionBody, options: ParserOptions) { super(ast); - this.variant = - ast.variant instanceof TerminalNode - ? ast.variant.unparse() - : new Block(ast.variant, options); + const variant = ast.variant; + if (variant instanceof TerminalNode) { + this.variant = variant.unparse(); + return; + } + this.variant = new Block(variant, options); - if (typeof this.variant !== 'string') this.updateMetadata(this.variant); + this.updateMetadata(this.variant); } print(path: AstPath, print: PrintFunction): Doc { - return typeof this.variant === 'string' - ? this.variant - : path.call(print, 'variant'); + return printVariant(this, path, print); } } diff --git a/src/slang-nodes/ImportClause.ts b/src/slang-nodes/ImportClause.ts index 945229810..b0bfaa645 100644 --- a/src/slang-nodes/ImportClause.ts +++ b/src/slang-nodes/ImportClause.ts @@ -1,14 +1,31 @@ +import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; import { SlangNode } from './SlangNode.js'; import { PathImport } from './PathImport.js'; import { NamedImport } from './NamedImport.js'; import { ImportDeconstruction } from './ImportDeconstruction.js'; -import type * as ast from '@nomicfoundation/slang/ast'; import type { AstPath, Doc, ParserOptions } from 'prettier'; import type { AstNode } from './types.d.ts'; import type { PrintFunction } from '../types.d.ts'; +function createNonterminalVariant( + variant: ast.ImportClause['variant'], + options: ParserOptions +): ImportClause['variant'] { + if (variant instanceof ast.PathImport) { + return new PathImport(variant, options); + } + if (variant instanceof ast.NamedImport) { + return new NamedImport(variant, options); + } + if (variant instanceof ast.ImportDeconstruction) { + return new ImportDeconstruction(variant, options); + } + const exhaustiveCheck: never = variant; + return exhaustiveCheck; +} + export class ImportClause extends SlangNode { readonly kind = NonterminalKind.ImportClause; @@ -17,22 +34,7 @@ export class ImportClause extends SlangNode { constructor(ast: ast.ImportClause, options: ParserOptions) { super(ast); - switch (ast.variant.cst.kind) { - case NonterminalKind.PathImport: - this.variant = new PathImport(ast.variant as ast.PathImport, options); - break; - case NonterminalKind.NamedImport: - this.variant = new NamedImport(ast.variant as ast.NamedImport, options); - break; - case NonterminalKind.ImportDeconstruction: - this.variant = new ImportDeconstruction( - ast.variant as ast.ImportDeconstruction, - options - ); - break; - default: - throw new Error(`Unexpected variant: ${ast.variant.cst.kind}`); - } + this.variant = createNonterminalVariant(ast.variant, options); this.updateMetadata(this.variant); } diff --git a/src/slang-nodes/MappingKeyType.ts b/src/slang-nodes/MappingKeyType.ts index c2b25067e..7096eaf2f 100644 --- a/src/slang-nodes/MappingKeyType.ts +++ b/src/slang-nodes/MappingKeyType.ts @@ -1,12 +1,25 @@ +import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; import { SlangNode } from './SlangNode.js'; import { ElementaryType } from './ElementaryType.js'; import { IdentifierPath } from './IdentifierPath.js'; -import type * as ast from '@nomicfoundation/slang/ast'; import type { AstPath, Doc } from 'prettier'; import type { PrintFunction } from '../types.d.ts'; +function createNonterminalVariant( + variant: ast.MappingKeyType['variant'] +): MappingKeyType['variant'] { + if (variant instanceof ast.ElementaryType) { + return new ElementaryType(variant); + } + if (variant instanceof ast.IdentifierPath) { + return new IdentifierPath(variant); + } + const exhaustiveCheck: never = variant; + return exhaustiveCheck; +} + export class MappingKeyType extends SlangNode { readonly kind = NonterminalKind.MappingKeyType; @@ -15,16 +28,7 @@ export class MappingKeyType extends SlangNode { constructor(ast: ast.MappingKeyType) { super(ast); - switch (ast.variant.cst.kind) { - case NonterminalKind.ElementaryType: - this.variant = new ElementaryType(ast.variant as ast.ElementaryType); - break; - case NonterminalKind.IdentifierPath: - this.variant = new IdentifierPath(ast.variant as ast.IdentifierPath); - break; - default: - throw new Error(`Unexpected variant: ${ast.variant.cst.kind}`); - } + this.variant = createNonterminalVariant(ast.variant); this.updateMetadata(this.variant); } diff --git a/src/slang-nodes/ModifierAttribute.ts b/src/slang-nodes/ModifierAttribute.ts index e49c3e2e4..a2f647ee8 100644 --- a/src/slang-nodes/ModifierAttribute.ts +++ b/src/slang-nodes/ModifierAttribute.ts @@ -1,4 +1,5 @@ import { NonterminalKind, TerminalNode } from '@nomicfoundation/slang/cst'; +import { printVariant } from '../slang-printers/print-variant.js'; import { SlangNode } from './SlangNode.js'; import { OverrideSpecifier } from './OverrideSpecifier.js'; @@ -14,17 +15,17 @@ export class ModifierAttribute extends SlangNode { constructor(ast: ast.ModifierAttribute) { super(ast); - this.variant = - ast.variant instanceof TerminalNode - ? ast.variant.unparse() - : new OverrideSpecifier(ast.variant); + const variant = ast.variant; + if (variant instanceof TerminalNode) { + this.variant = variant.unparse(); + return; + } + this.variant = new OverrideSpecifier(variant); - if (typeof this.variant !== 'string') this.updateMetadata(this.variant); + this.updateMetadata(this.variant); } print(path: AstPath, print: PrintFunction): Doc { - return typeof this.variant === 'string' - ? this.variant - : path.call(print, 'variant'); + return printVariant(this, path, print); } } diff --git a/src/slang-nodes/Pragma.ts b/src/slang-nodes/Pragma.ts index 00f8ed5b4..efb6a940c 100644 --- a/src/slang-nodes/Pragma.ts +++ b/src/slang-nodes/Pragma.ts @@ -1,14 +1,31 @@ +import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; import { SlangNode } from './SlangNode.js'; import { AbicoderPragma } from './AbicoderPragma.js'; import { ExperimentalPragma } from './ExperimentalPragma.js'; import { VersionPragma } from './VersionPragma.js'; -import type * as ast from '@nomicfoundation/slang/ast'; import type { AstPath, Doc, ParserOptions } from 'prettier'; import type { AstNode } from './types.d.ts'; import type { PrintFunction } from '../types.d.ts'; +function createNonterminalVariant( + variant: ast.Pragma['variant'], + options: ParserOptions +): Pragma['variant'] { + if (variant instanceof ast.AbicoderPragma) { + return new AbicoderPragma(variant); + } + if (variant instanceof ast.ExperimentalPragma) { + return new ExperimentalPragma(variant, options); + } + if (variant instanceof ast.VersionPragma) { + return new VersionPragma(variant); + } + const exhaustiveCheck: never = variant; + return exhaustiveCheck; +} + export class Pragma extends SlangNode { readonly kind = NonterminalKind.Pragma; @@ -17,22 +34,7 @@ export class Pragma extends SlangNode { constructor(ast: ast.Pragma, options: ParserOptions) { super(ast); - switch (ast.variant.cst.kind) { - case NonterminalKind.AbicoderPragma: - this.variant = new AbicoderPragma(ast.variant as ast.AbicoderPragma); - break; - case NonterminalKind.ExperimentalPragma: - this.variant = new ExperimentalPragma( - ast.variant as ast.ExperimentalPragma, - options - ); - break; - case NonterminalKind.VersionPragma: - this.variant = new VersionPragma(ast.variant as ast.VersionPragma); - break; - default: - throw new Error(`Unexpected variant: ${ast.variant.cst.kind}`); - } + this.variant = createNonterminalVariant(ast.variant, options); this.updateMetadata(this.variant); } diff --git a/src/slang-nodes/ReceiveFunctionAttribute.ts b/src/slang-nodes/ReceiveFunctionAttribute.ts index 5c6138663..6f2f6cc0d 100644 --- a/src/slang-nodes/ReceiveFunctionAttribute.ts +++ b/src/slang-nodes/ReceiveFunctionAttribute.ts @@ -1,13 +1,28 @@ +import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind, TerminalNode } from '@nomicfoundation/slang/cst'; +import { printVariant } from '../slang-printers/print-variant.js'; import { SlangNode } from './SlangNode.js'; import { ModifierInvocation } from './ModifierInvocation.js'; import { OverrideSpecifier } from './OverrideSpecifier.js'; -import type * as ast from '@nomicfoundation/slang/ast'; import type { AstPath, Doc, ParserOptions } from 'prettier'; import type { AstNode } from './types.d.ts'; import type { PrintFunction } from '../types.d.ts'; +function createNonterminalVariant( + variant: Exclude, + options: ParserOptions +): Exclude { + if (variant instanceof ast.ModifierInvocation) { + return new ModifierInvocation(variant, options); + } + if (variant instanceof ast.OverrideSpecifier) { + return new OverrideSpecifier(variant); + } + const exhaustiveCheck: never = variant; + return exhaustiveCheck; +} + export class ReceiveFunctionAttribute extends SlangNode { readonly kind = NonterminalKind.ReceiveFunctionAttribute; @@ -19,32 +34,17 @@ export class ReceiveFunctionAttribute extends SlangNode { ) { super(ast); - if (ast.variant instanceof TerminalNode) { - this.variant = ast.variant.unparse(); - } else { - switch (ast.variant.cst.kind) { - case NonterminalKind.ModifierInvocation: - this.variant = new ModifierInvocation( - ast.variant as ast.ModifierInvocation, - options - ); - break; - case NonterminalKind.OverrideSpecifier: - this.variant = new OverrideSpecifier( - ast.variant as ast.OverrideSpecifier - ); - break; - default: - throw new Error(`Unexpected variant: ${ast.variant.cst.kind}`); - } + const variant = ast.variant; + if (variant instanceof TerminalNode) { + this.variant = variant.unparse(); + return; } + this.variant = createNonterminalVariant(variant, options); - if (typeof this.variant !== 'string') this.updateMetadata(this.variant); + this.updateMetadata(this.variant); } print(path: AstPath, print: PrintFunction): Doc { - return typeof this.variant === 'string' - ? this.variant - : path.call(print, 'variant'); + return printVariant(this, path, print); } } diff --git a/src/slang-nodes/SourceUnitMember.ts b/src/slang-nodes/SourceUnitMember.ts index 9a61b639e..cb9d349bf 100644 --- a/src/slang-nodes/SourceUnitMember.ts +++ b/src/slang-nodes/SourceUnitMember.ts @@ -1,3 +1,4 @@ +import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; import { SlangNode } from './SlangNode.js'; import { PragmaDirective } from './PragmaDirective.js'; @@ -14,11 +15,57 @@ import { UserDefinedValueTypeDefinition } from './UserDefinedValueTypeDefinition import { UsingDirective } from './UsingDirective.js'; import { EventDefinition } from './EventDefinition.js'; -import type * as ast from '@nomicfoundation/slang/ast'; import type { AstPath, Doc, ParserOptions } from 'prettier'; import type { AstNode } from './types.d.ts'; import type { PrintFunction } from '../types.d.ts'; +function createNonterminalVariant( + variant: ast.SourceUnitMember['variant'], + options: ParserOptions +): SourceUnitMember['variant'] { + if (variant instanceof ast.PragmaDirective) { + return new PragmaDirective(variant, options); + } + if (variant instanceof ast.ImportDirective) { + return new ImportDirective(variant, options); + } + if (variant instanceof ast.ContractDefinition) { + return new ContractDefinition(variant, options); + } + if (variant instanceof ast.InterfaceDefinition) { + return new InterfaceDefinition(variant, options); + } + if (variant instanceof ast.LibraryDefinition) { + return new LibraryDefinition(variant, options); + } + if (variant instanceof ast.StructDefinition) { + return new StructDefinition(variant, options); + } + if (variant instanceof ast.EnumDefinition) { + return new EnumDefinition(variant); + } + if (variant instanceof ast.FunctionDefinition) { + return new FunctionDefinition(variant, options); + } + if (variant instanceof ast.ConstantDefinition) { + return new ConstantDefinition(variant, options); + } + if (variant instanceof ast.ErrorDefinition) { + return new ErrorDefinition(variant, options); + } + if (variant instanceof ast.UserDefinedValueTypeDefinition) { + return new UserDefinedValueTypeDefinition(variant); + } + if (variant instanceof ast.UsingDirective) { + return new UsingDirective(variant, options); + } + if (variant instanceof ast.EventDefinition) { + return new EventDefinition(variant, options); + } + const exhaustiveCheck: never = variant; + return exhaustiveCheck; +} + export class SourceUnitMember extends SlangNode { readonly kind = NonterminalKind.SourceUnitMember; @@ -40,84 +87,7 @@ export class SourceUnitMember extends SlangNode { constructor(ast: ast.SourceUnitMember, options: ParserOptions) { super(ast); - switch (ast.variant.cst.kind) { - case NonterminalKind.PragmaDirective: - this.variant = new PragmaDirective( - ast.variant as ast.PragmaDirective, - options - ); - break; - case NonterminalKind.ImportDirective: - this.variant = new ImportDirective( - ast.variant as ast.ImportDirective, - options - ); - break; - case NonterminalKind.ContractDefinition: - this.variant = new ContractDefinition( - ast.variant as ast.ContractDefinition, - options - ); - break; - case NonterminalKind.InterfaceDefinition: - this.variant = new InterfaceDefinition( - ast.variant as ast.InterfaceDefinition, - options - ); - break; - case NonterminalKind.LibraryDefinition: - this.variant = new LibraryDefinition( - ast.variant as ast.LibraryDefinition, - options - ); - break; - case NonterminalKind.StructDefinition: - this.variant = new StructDefinition( - ast.variant as ast.StructDefinition, - options - ); - break; - case NonterminalKind.EnumDefinition: - this.variant = new EnumDefinition(ast.variant as ast.EnumDefinition); - break; - case NonterminalKind.FunctionDefinition: - this.variant = new FunctionDefinition( - ast.variant as ast.FunctionDefinition, - options - ); - break; - case NonterminalKind.ConstantDefinition: - this.variant = new ConstantDefinition( - ast.variant as ast.ConstantDefinition, - options - ); - break; - case NonterminalKind.ErrorDefinition: - this.variant = new ErrorDefinition( - ast.variant as ast.ErrorDefinition, - options - ); - break; - case NonterminalKind.UserDefinedValueTypeDefinition: - this.variant = new UserDefinedValueTypeDefinition( - ast.variant as ast.UserDefinedValueTypeDefinition - ); - break; - case NonterminalKind.UsingDirective: - this.variant = new UsingDirective( - ast.variant as ast.UsingDirective, - options - ); - break; - case NonterminalKind.EventDefinition: - this.variant = new EventDefinition( - ast.variant as ast.EventDefinition, - options - ); - break; - default: - throw new Error(`Unexpected variant: ${ast.variant.cst.kind}`); - } + this.variant = createNonterminalVariant(ast.variant, options); this.updateMetadata(this.variant); } diff --git a/src/slang-nodes/StateVariableAttribute.ts b/src/slang-nodes/StateVariableAttribute.ts index b25a90968..b64b33554 100644 --- a/src/slang-nodes/StateVariableAttribute.ts +++ b/src/slang-nodes/StateVariableAttribute.ts @@ -1,4 +1,5 @@ import { NonterminalKind, TerminalNode } from '@nomicfoundation/slang/cst'; +import { printVariant } from '../slang-printers/print-variant.js'; import { SlangNode } from './SlangNode.js'; import { OverrideSpecifier } from './OverrideSpecifier.js'; @@ -14,17 +15,17 @@ export class StateVariableAttribute extends SlangNode { constructor(ast: ast.StateVariableAttribute) { super(ast); - this.variant = - ast.variant instanceof TerminalNode - ? ast.variant.unparse() - : new OverrideSpecifier(ast.variant); + const variant = ast.variant; + if (variant instanceof TerminalNode) { + this.variant = variant.unparse(); + return; + } + this.variant = new OverrideSpecifier(variant); - if (typeof this.variant !== 'string') this.updateMetadata(this.variant); + this.updateMetadata(this.variant); } print(path: AstPath, print: PrintFunction): Doc { - return typeof this.variant === 'string' - ? this.variant - : path.call(print, 'variant'); + return printVariant(this, path, print); } } diff --git a/src/slang-nodes/Statement.ts b/src/slang-nodes/Statement.ts index 293dbdc00..3b5916222 100644 --- a/src/slang-nodes/Statement.ts +++ b/src/slang-nodes/Statement.ts @@ -1,3 +1,4 @@ +import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; import { SlangNode } from './SlangNode.js'; import { ExpressionStatement } from './ExpressionStatement.js'; @@ -18,11 +19,69 @@ import { AssemblyStatement } from './AssemblyStatement.js'; import { Block } from './Block.js'; import { UncheckedBlock } from './UncheckedBlock.js'; -import type * as ast from '@nomicfoundation/slang/ast'; import type { AstPath, Doc, ParserOptions } from 'prettier'; import type { AstNode } from './types.d.ts'; import type { PrintFunction } from '../types.d.ts'; +function createNonterminalVariant( + variant: ast.Statement['variant'], + options: ParserOptions +): Statement['variant'] { + if (variant instanceof ast.ExpressionStatement) { + return new ExpressionStatement(variant, options); + } + if (variant instanceof ast.VariableDeclarationStatement) { + return new VariableDeclarationStatement(variant, options); + } + if (variant instanceof ast.TupleDeconstructionStatement) { + return new TupleDeconstructionStatement(variant, options); + } + if (variant instanceof ast.IfStatement) { + return new IfStatement(variant, options); + } + if (variant instanceof ast.ForStatement) { + return new ForStatement(variant, options); + } + if (variant instanceof ast.WhileStatement) { + return new WhileStatement(variant, options); + } + if (variant instanceof ast.DoWhileStatement) { + return new DoWhileStatement(variant, options); + } + if (variant instanceof ast.ContinueStatement) { + return new ContinueStatement(variant); + } + if (variant instanceof ast.BreakStatement) { + return new BreakStatement(variant); + } + if (variant instanceof ast.ReturnStatement) { + return new ReturnStatement(variant, options); + } + if (variant instanceof ast.ThrowStatement) { + return new ThrowStatement(variant); + } + if (variant instanceof ast.EmitStatement) { + return new EmitStatement(variant, options); + } + if (variant instanceof ast.TryStatement) { + return new TryStatement(variant, options); + } + if (variant instanceof ast.RevertStatement) { + return new RevertStatement(variant, options); + } + if (variant instanceof ast.AssemblyStatement) { + return new AssemblyStatement(variant, options); + } + if (variant instanceof ast.Block) { + return new Block(variant, options); + } + if (variant instanceof ast.UncheckedBlock) { + return new UncheckedBlock(variant, options); + } + const exhaustiveCheck: never = variant; + return exhaustiveCheck; +} + export class Statement extends SlangNode { readonly kind = NonterminalKind.Statement; @@ -48,99 +107,7 @@ export class Statement extends SlangNode { constructor(ast: ast.Statement, options: ParserOptions) { super(ast); - switch (ast.variant.cst.kind) { - case NonterminalKind.ExpressionStatement: - this.variant = new ExpressionStatement( - ast.variant as ast.ExpressionStatement, - options - ); - break; - case NonterminalKind.VariableDeclarationStatement: - this.variant = new VariableDeclarationStatement( - ast.variant as ast.VariableDeclarationStatement, - options - ); - break; - case NonterminalKind.TupleDeconstructionStatement: - this.variant = new TupleDeconstructionStatement( - ast.variant as ast.TupleDeconstructionStatement, - options - ); - break; - case NonterminalKind.IfStatement: - this.variant = new IfStatement(ast.variant as ast.IfStatement, options); - break; - case NonterminalKind.ForStatement: - this.variant = new ForStatement( - ast.variant as ast.ForStatement, - options - ); - break; - case NonterminalKind.WhileStatement: - this.variant = new WhileStatement( - ast.variant as ast.WhileStatement, - options - ); - break; - case NonterminalKind.DoWhileStatement: - this.variant = new DoWhileStatement( - ast.variant as ast.DoWhileStatement, - options - ); - break; - case NonterminalKind.ContinueStatement: - this.variant = new ContinueStatement( - ast.variant as ast.ContinueStatement - ); - break; - case NonterminalKind.BreakStatement: - this.variant = new BreakStatement(ast.variant as ast.BreakStatement); - break; - case NonterminalKind.ReturnStatement: - this.variant = new ReturnStatement( - ast.variant as ast.ReturnStatement, - options - ); - break; - case NonterminalKind.ThrowStatement: - this.variant = new ThrowStatement(ast.variant as ast.ThrowStatement); - break; - case NonterminalKind.EmitStatement: - this.variant = new EmitStatement( - ast.variant as ast.EmitStatement, - options - ); - break; - case NonterminalKind.TryStatement: - this.variant = new TryStatement( - ast.variant as ast.TryStatement, - options - ); - break; - case NonterminalKind.RevertStatement: - this.variant = new RevertStatement( - ast.variant as ast.RevertStatement, - options - ); - break; - case NonterminalKind.AssemblyStatement: - this.variant = new AssemblyStatement( - ast.variant as ast.AssemblyStatement, - options - ); - break; - case NonterminalKind.Block: - this.variant = new Block(ast.variant as ast.Block, options); - break; - case NonterminalKind.UncheckedBlock: - this.variant = new UncheckedBlock( - ast.variant as ast.UncheckedBlock, - options - ); - break; - default: - throw new Error(`Unexpected variant: ${ast.variant.cst.kind}`); - } + this.variant = createNonterminalVariant(ast.variant, options); this.updateMetadata(this.variant); } diff --git a/src/slang-nodes/StringExpression.ts b/src/slang-nodes/StringExpression.ts index fb4563d0c..c7edb758d 100644 --- a/src/slang-nodes/StringExpression.ts +++ b/src/slang-nodes/StringExpression.ts @@ -1,3 +1,4 @@ +import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; import { SlangNode } from './SlangNode.js'; import { StringLiteral } from './StringLiteral.js'; @@ -6,11 +7,33 @@ import { HexStringLiteral } from './HexStringLiteral.js'; import { HexStringLiterals } from './HexStringLiterals.js'; import { UnicodeStringLiterals } from './UnicodeStringLiterals.js'; -import type * as ast from '@nomicfoundation/slang/ast'; import type { AstPath, Doc, ParserOptions } from 'prettier'; import type { AstNode } from './types.d.ts'; import type { PrintFunction } from '../types.d.ts'; +function createNonterminalVariant( + variant: ast.StringExpression['variant'], + options: ParserOptions +): StringExpression['variant'] { + if (variant instanceof ast.StringLiteral) { + return new StringLiteral(variant, options); + } + if (variant instanceof ast.StringLiterals) { + return new StringLiterals(variant, options); + } + if (variant instanceof ast.HexStringLiteral) { + return new HexStringLiteral(variant, options); + } + if (variant instanceof ast.HexStringLiterals) { + return new HexStringLiterals(variant, options); + } + if (variant instanceof ast.UnicodeStringLiterals) { + return new UnicodeStringLiterals(variant, options); + } + const exhaustiveCheck: never = variant; + return exhaustiveCheck; +} + export class StringExpression extends SlangNode { readonly kind = NonterminalKind.StringExpression; @@ -24,40 +47,7 @@ export class StringExpression extends SlangNode { constructor(ast: ast.StringExpression, options: ParserOptions) { super(ast); - switch (ast.variant.cst.kind) { - case NonterminalKind.StringLiteral: - this.variant = new StringLiteral( - ast.variant as ast.StringLiteral, - options - ); - break; - case NonterminalKind.StringLiterals: - this.variant = new StringLiterals( - ast.variant as ast.StringLiterals, - options - ); - break; - case NonterminalKind.HexStringLiteral: - this.variant = new HexStringLiteral( - ast.variant as ast.HexStringLiteral, - options - ); - break; - case NonterminalKind.HexStringLiterals: - this.variant = new HexStringLiterals( - ast.variant as ast.HexStringLiterals, - options - ); - break; - case NonterminalKind.UnicodeStringLiterals: - this.variant = new UnicodeStringLiterals( - ast.variant as ast.UnicodeStringLiterals, - options - ); - break; - default: - throw new Error(`Unexpected variant: ${ast.variant.cst.kind}`); - } + this.variant = createNonterminalVariant(ast.variant, options); this.updateMetadata(this.variant); } diff --git a/src/slang-nodes/TupleMember.ts b/src/slang-nodes/TupleMember.ts index 31f84a284..2da49eeb9 100644 --- a/src/slang-nodes/TupleMember.ts +++ b/src/slang-nodes/TupleMember.ts @@ -1,13 +1,27 @@ +import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; import { SlangNode } from './SlangNode.js'; import { TypedTupleMember } from './TypedTupleMember.js'; import { UntypedTupleMember } from './UntypedTupleMember.js'; -import type * as ast from '@nomicfoundation/slang/ast'; import type { AstPath, Doc, ParserOptions } from 'prettier'; import type { AstNode } from './types.d.ts'; import type { PrintFunction } from '../types.d.ts'; +function createNonterminalVariant( + variant: ast.TupleMember['variant'], + options: ParserOptions +): TupleMember['variant'] { + if (variant instanceof ast.TypedTupleMember) { + return new TypedTupleMember(variant, options); + } + if (variant instanceof ast.UntypedTupleMember) { + return new UntypedTupleMember(variant); + } + const exhaustiveCheck: never = variant; + return exhaustiveCheck; +} + export class TupleMember extends SlangNode { readonly kind = NonterminalKind.TupleMember; @@ -16,21 +30,7 @@ export class TupleMember extends SlangNode { constructor(ast: ast.TupleMember, options: ParserOptions) { super(ast); - switch (ast.variant.cst.kind) { - case NonterminalKind.TypedTupleMember: - this.variant = new TypedTupleMember( - ast.variant as ast.TypedTupleMember, - options - ); - break; - case NonterminalKind.UntypedTupleMember: - this.variant = new UntypedTupleMember( - ast.variant as ast.UntypedTupleMember - ); - break; - default: - throw new Error(`Unexpected variant: ${ast.variant.cst.kind}`); - } + this.variant = createNonterminalVariant(ast.variant, options); this.updateMetadata(this.variant); } diff --git a/src/slang-nodes/TypeName.ts b/src/slang-nodes/TypeName.ts index 75aa619f4..931d1ea25 100644 --- a/src/slang-nodes/TypeName.ts +++ b/src/slang-nodes/TypeName.ts @@ -1,3 +1,4 @@ +import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; import { SlangNode } from './SlangNode.js'; import { ArrayTypeName } from './ArrayTypeName.js'; @@ -6,11 +7,33 @@ import { MappingType } from './MappingType.js'; import { ElementaryType } from './ElementaryType.js'; import { IdentifierPath } from './IdentifierPath.js'; -import type * as ast from '@nomicfoundation/slang/ast'; import type { AstPath, Doc, ParserOptions } from 'prettier'; import type { AstNode } from './types.d.ts'; import type { PrintFunction } from '../types.d.ts'; +function createNonterminalVariant( + variant: ast.TypeName['variant'], + options: ParserOptions +): TypeName['variant'] { + if (variant instanceof ast.ArrayTypeName) { + return new ArrayTypeName(variant, options); + } + if (variant instanceof ast.FunctionType) { + return new FunctionType(variant, options); + } + if (variant instanceof ast.MappingType) { + return new MappingType(variant, options); + } + if (variant instanceof ast.ElementaryType) { + return new ElementaryType(variant); + } + if (variant instanceof ast.IdentifierPath) { + return new IdentifierPath(variant); + } + const exhaustiveCheck: never = variant; + return exhaustiveCheck; +} + export class TypeName extends SlangNode { readonly kind = NonterminalKind.TypeName; @@ -24,31 +47,7 @@ export class TypeName extends SlangNode { constructor(ast: ast.TypeName, options: ParserOptions) { super(ast); - switch (ast.variant.cst.kind) { - case NonterminalKind.ArrayTypeName: - this.variant = new ArrayTypeName( - ast.variant as ast.ArrayTypeName, - options - ); - break; - case NonterminalKind.FunctionType: - this.variant = new FunctionType( - ast.variant as ast.FunctionType, - options - ); - break; - case NonterminalKind.MappingType: - this.variant = new MappingType(ast.variant as ast.MappingType, options); - break; - case NonterminalKind.ElementaryType: - this.variant = new ElementaryType(ast.variant as ast.ElementaryType); - break; - case NonterminalKind.IdentifierPath: - this.variant = new IdentifierPath(ast.variant as ast.IdentifierPath); - break; - default: - throw new Error(`Unexpected variant: ${ast.variant.cst.kind}`); - } + this.variant = createNonterminalVariant(ast.variant, options); this.updateMetadata(this.variant); } diff --git a/src/slang-nodes/UnnamedFunctionAttribute.ts b/src/slang-nodes/UnnamedFunctionAttribute.ts index d25bbc7de..a829e18c7 100644 --- a/src/slang-nodes/UnnamedFunctionAttribute.ts +++ b/src/slang-nodes/UnnamedFunctionAttribute.ts @@ -1,4 +1,5 @@ import { NonterminalKind, TerminalNode } from '@nomicfoundation/slang/cst'; +import { printVariant } from '../slang-printers/print-variant.js'; import { SlangNode } from './SlangNode.js'; import { ModifierInvocation } from './ModifierInvocation.js'; @@ -18,17 +19,17 @@ export class UnnamedFunctionAttribute extends SlangNode { ) { super(ast); - this.variant = - ast.variant instanceof TerminalNode - ? ast.variant.unparse() - : new ModifierInvocation(ast.variant, options); + const variant = ast.variant; + if (variant instanceof TerminalNode) { + this.variant = variant.unparse(); + return; + } + this.variant = new ModifierInvocation(variant, options); - if (typeof this.variant !== 'string') this.updateMetadata(this.variant); + this.updateMetadata(this.variant); } print(path: AstPath, print: PrintFunction): Doc { - return typeof this.variant === 'string' - ? this.variant - : path.call(print, 'variant'); + return printVariant(this, path, print); } } diff --git a/src/slang-nodes/UsingClause.ts b/src/slang-nodes/UsingClause.ts index 17ffc5c75..03ae7a09c 100644 --- a/src/slang-nodes/UsingClause.ts +++ b/src/slang-nodes/UsingClause.ts @@ -1,12 +1,25 @@ +import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; import { SlangNode } from './SlangNode.js'; import { IdentifierPath } from './IdentifierPath.js'; import { UsingDeconstruction } from './UsingDeconstruction.js'; -import type * as ast from '@nomicfoundation/slang/ast'; import type { AstPath, Doc } from 'prettier'; import type { PrintFunction } from '../types.d.ts'; +function createNonterminalVariant( + variant: ast.UsingClause['variant'] +): UsingClause['variant'] { + if (variant instanceof ast.IdentifierPath) { + return new IdentifierPath(variant); + } + if (variant instanceof ast.UsingDeconstruction) { + return new UsingDeconstruction(variant); + } + const exhaustiveCheck: never = variant; + return exhaustiveCheck; +} + export class UsingClause extends SlangNode { readonly kind = NonterminalKind.UsingClause; @@ -15,18 +28,7 @@ export class UsingClause extends SlangNode { constructor(ast: ast.UsingClause) { super(ast); - switch (ast.variant.cst.kind) { - case NonterminalKind.IdentifierPath: - this.variant = new IdentifierPath(ast.variant as ast.IdentifierPath); - break; - case NonterminalKind.UsingDeconstruction: - this.variant = new UsingDeconstruction( - ast.variant as ast.UsingDeconstruction - ); - break; - default: - throw new Error(`Unexpected variant: ${ast.variant.cst.kind}`); - } + this.variant = createNonterminalVariant(ast.variant); this.updateMetadata(this.variant); } diff --git a/src/slang-nodes/UsingTarget.ts b/src/slang-nodes/UsingTarget.ts index 77acd9c0a..792645c63 100644 --- a/src/slang-nodes/UsingTarget.ts +++ b/src/slang-nodes/UsingTarget.ts @@ -1,4 +1,5 @@ import { NonterminalKind, TerminalNode } from '@nomicfoundation/slang/cst'; +import { printVariant } from '../slang-printers/print-variant.js'; import { SlangNode } from './SlangNode.js'; import { TypeName } from './TypeName.js'; @@ -15,17 +16,17 @@ export class UsingTarget extends SlangNode { constructor(ast: ast.UsingTarget, options: ParserOptions) { super(ast); - this.variant = - ast.variant instanceof TerminalNode - ? ast.variant.unparse() - : new TypeName(ast.variant, options); + const variant = ast.variant; + if (variant instanceof TerminalNode) { + this.variant = variant.unparse(); + return; + } + this.variant = new TypeName(variant, options); - if (typeof this.variant !== 'string') this.updateMetadata(this.variant); + this.updateMetadata(this.variant); } print(path: AstPath, print: PrintFunction): Doc { - return typeof this.variant === 'string' - ? this.variant - : path.call(print, 'variant'); + return printVariant(this, path, print); } } diff --git a/src/slang-nodes/VariableDeclarationType.ts b/src/slang-nodes/VariableDeclarationType.ts index 7e1f04bb0..759208dc6 100644 --- a/src/slang-nodes/VariableDeclarationType.ts +++ b/src/slang-nodes/VariableDeclarationType.ts @@ -1,4 +1,5 @@ import { NonterminalKind, TerminalNode } from '@nomicfoundation/slang/cst'; +import { printVariant } from '../slang-printers/print-variant.js'; import { SlangNode } from './SlangNode.js'; import { TypeName } from './TypeName.js'; @@ -18,17 +19,17 @@ export class VariableDeclarationType extends SlangNode { ) { super(ast); - this.variant = - ast.variant instanceof TerminalNode - ? ast.variant.unparse() - : new TypeName(ast.variant, options); + const variant = ast.variant; + if (variant instanceof TerminalNode) { + this.variant = variant.unparse(); + return; + } + this.variant = new TypeName(variant, options); - if (typeof this.variant !== 'string') this.updateMetadata(this.variant); + this.updateMetadata(this.variant); } print(path: AstPath, print: PrintFunction): Doc { - return typeof this.variant === 'string' - ? this.variant - : path.call(print, 'variant'); + return printVariant(this, path, print); } } diff --git a/src/slang-nodes/VersionExpression.ts b/src/slang-nodes/VersionExpression.ts index 3c87be7ae..76ae215a9 100644 --- a/src/slang-nodes/VersionExpression.ts +++ b/src/slang-nodes/VersionExpression.ts @@ -1,12 +1,25 @@ +import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; import { SlangNode } from './SlangNode.js'; import { VersionRange } from './VersionRange.js'; import { VersionTerm } from './VersionTerm.js'; -import type * as ast from '@nomicfoundation/slang/ast'; import type { AstPath, Doc } from 'prettier'; import type { PrintFunction } from '../types.d.ts'; +function createNonterminalVariant( + variant: ast.VersionExpression['variant'] +): VersionExpression['variant'] { + if (variant instanceof ast.VersionRange) { + return new VersionRange(variant); + } + if (variant instanceof ast.VersionTerm) { + return new VersionTerm(variant); + } + const exhaustiveCheck: never = variant; + return exhaustiveCheck; +} + export class VersionExpression extends SlangNode { readonly kind = NonterminalKind.VersionExpression; @@ -15,16 +28,7 @@ export class VersionExpression extends SlangNode { constructor(ast: ast.VersionExpression) { super(ast); - switch (ast.variant.cst.kind) { - case NonterminalKind.VersionRange: - this.variant = new VersionRange(ast.variant as ast.VersionRange); - break; - case NonterminalKind.VersionTerm: - this.variant = new VersionTerm(ast.variant as ast.VersionTerm); - break; - default: - throw new Error(`Unexpected variant: ${ast.variant.cst.kind}`); - } + this.variant = createNonterminalVariant(ast.variant); this.updateMetadata(this.variant); } diff --git a/src/slang-nodes/VersionLiteral.ts b/src/slang-nodes/VersionLiteral.ts index 2a47b818c..5ee3e9008 100644 --- a/src/slang-nodes/VersionLiteral.ts +++ b/src/slang-nodes/VersionLiteral.ts @@ -1,4 +1,5 @@ import { NonterminalKind, TerminalNode } from '@nomicfoundation/slang/cst'; +import { printVariant } from '../slang-printers/print-variant.js'; import { SlangNode } from './SlangNode.js'; import { SimpleVersionLiteral } from './SimpleVersionLiteral.js'; @@ -14,17 +15,17 @@ export class VersionLiteral extends SlangNode { constructor(ast: ast.VersionLiteral) { super(ast); - this.variant = - ast.variant instanceof TerminalNode - ? ast.variant.unparse() - : new SimpleVersionLiteral(ast.variant); + const variant = ast.variant; + if (variant instanceof TerminalNode) { + this.variant = variant.unparse(); + return; + } + this.variant = new SimpleVersionLiteral(variant); - if (typeof this.variant !== 'string') this.updateMetadata(this.variant); + this.updateMetadata(this.variant); } print(path: AstPath, print: PrintFunction): Doc { - return typeof this.variant === 'string' - ? this.variant - : path.call(print, 'variant'); + return printVariant(this, path, print); } } diff --git a/src/slang-nodes/YulAssignmentOperator.ts b/src/slang-nodes/YulAssignmentOperator.ts index ac6be165a..193ff3710 100644 --- a/src/slang-nodes/YulAssignmentOperator.ts +++ b/src/slang-nodes/YulAssignmentOperator.ts @@ -1,4 +1,5 @@ import { NonterminalKind, TerminalNode } from '@nomicfoundation/slang/cst'; +import { printVariant } from '../slang-printers/print-variant.js'; import { SlangNode } from './SlangNode.js'; import { YulColonAndEqual } from './YulColonAndEqual.js'; @@ -14,17 +15,17 @@ export class YulAssignmentOperator extends SlangNode { constructor(ast: ast.YulAssignmentOperator) { super(ast); - this.variant = - ast.variant instanceof TerminalNode - ? ast.variant.unparse() - : new YulColonAndEqual(ast.variant); + const variant = ast.variant; + if (variant instanceof TerminalNode) { + this.variant = variant.unparse(); + return; + } + this.variant = new YulColonAndEqual(variant); - if (typeof this.variant !== 'string') this.updateMetadata(this.variant); + this.updateMetadata(this.variant); } print(path: AstPath, print: PrintFunction): Doc { - return typeof this.variant === 'string' - ? this.variant - : path.call(print, 'variant'); + return printVariant(this, path, print); } } diff --git a/src/slang-nodes/YulExpression.ts b/src/slang-nodes/YulExpression.ts index 55dceb456..42acf09ce 100644 --- a/src/slang-nodes/YulExpression.ts +++ b/src/slang-nodes/YulExpression.ts @@ -1,14 +1,31 @@ +import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; import { SlangNode } from './SlangNode.js'; import { YulFunctionCallExpression } from './YulFunctionCallExpression.js'; import { YulLiteral } from './YulLiteral.js'; import { YulPath } from './YulPath.js'; -import type * as ast from '@nomicfoundation/slang/ast'; import type { AstPath, Doc, ParserOptions } from 'prettier'; import type { AstNode } from './types.d.ts'; import type { PrintFunction } from '../types.d.ts'; +function createNonterminalVariant( + variant: ast.YulExpression['variant'], + options: ParserOptions +): YulExpression['variant'] { + if (variant instanceof ast.YulFunctionCallExpression) { + return new YulFunctionCallExpression(variant, options); + } + if (variant instanceof ast.YulLiteral) { + return new YulLiteral(variant, options); + } + if (variant instanceof ast.YulPath) { + return new YulPath(variant); + } + const exhaustiveCheck: never = variant; + return exhaustiveCheck; +} + export class YulExpression extends SlangNode { readonly kind = NonterminalKind.YulExpression; @@ -17,22 +34,7 @@ export class YulExpression extends SlangNode { constructor(ast: ast.YulExpression, options: ParserOptions) { super(ast); - switch (ast.variant.cst.kind) { - case NonterminalKind.YulFunctionCallExpression: - this.variant = new YulFunctionCallExpression( - ast.variant as ast.YulFunctionCallExpression, - options - ); - break; - case NonterminalKind.YulLiteral: - this.variant = new YulLiteral(ast.variant as ast.YulLiteral, options); - break; - case NonterminalKind.YulPath: - this.variant = new YulPath(ast.variant as ast.YulPath); - break; - default: - throw new Error(`Unexpected variant: ${ast.variant.cst.kind}`); - } + this.variant = createNonterminalVariant(ast.variant, options); this.updateMetadata(this.variant); } diff --git a/src/slang-nodes/YulLiteral.ts b/src/slang-nodes/YulLiteral.ts index eeb03505d..8666c0dad 100644 --- a/src/slang-nodes/YulLiteral.ts +++ b/src/slang-nodes/YulLiteral.ts @@ -1,13 +1,28 @@ +import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind, TerminalNode } from '@nomicfoundation/slang/cst'; +import { printVariant } from '../slang-printers/print-variant.js'; import { SlangNode } from './SlangNode.js'; import { HexStringLiteral } from './HexStringLiteral.js'; import { StringLiteral } from './StringLiteral.js'; -import type * as ast from '@nomicfoundation/slang/ast'; import type { AstPath, Doc, ParserOptions } from 'prettier'; import type { AstNode } from './types.d.ts'; import type { PrintFunction } from '../types.d.ts'; +function createNonterminalVariant( + variant: Exclude, + options: ParserOptions +): Exclude { + if (variant instanceof ast.HexStringLiteral) { + return new HexStringLiteral(variant, options); + } + if (variant instanceof ast.StringLiteral) { + return new StringLiteral(variant, options); + } + const exhaustiveCheck: never = variant; + return exhaustiveCheck; +} + export class YulLiteral extends SlangNode { readonly kind = NonterminalKind.YulLiteral; @@ -16,33 +31,17 @@ export class YulLiteral extends SlangNode { constructor(ast: ast.YulLiteral, options: ParserOptions) { super(ast); - if (ast.variant instanceof TerminalNode) { - this.variant = ast.variant.unparse(); - } else { - switch (ast.variant.cst.kind) { - case NonterminalKind.HexStringLiteral: - this.variant = new HexStringLiteral( - ast.variant as ast.HexStringLiteral, - options - ); - break; - case NonterminalKind.StringLiteral: - this.variant = new StringLiteral( - ast.variant as ast.StringLiteral, - options - ); - break; - default: - throw new Error(`Unexpected variant: ${ast.variant.cst.kind}`); - } + const variant = ast.variant; + if (variant instanceof TerminalNode) { + this.variant = variant.unparse(); + return; } + this.variant = createNonterminalVariant(variant, options); - if (typeof this.variant !== 'string') this.updateMetadata(this.variant); + this.updateMetadata(this.variant); } print(path: AstPath, print: PrintFunction): Doc { - return typeof this.variant === 'string' - ? this.variant - : path.call(print, 'variant'); + return printVariant(this, path, print); } } diff --git a/src/slang-nodes/YulStackAssignmentOperator.ts b/src/slang-nodes/YulStackAssignmentOperator.ts index b14b28065..658e4bf02 100644 --- a/src/slang-nodes/YulStackAssignmentOperator.ts +++ b/src/slang-nodes/YulStackAssignmentOperator.ts @@ -1,4 +1,5 @@ import { NonterminalKind, TerminalNode } from '@nomicfoundation/slang/cst'; +import { printVariant } from '../slang-printers/print-variant.js'; import { SlangNode } from './SlangNode.js'; import { YulEqualAndColon } from './YulEqualAndColon.js'; @@ -14,17 +15,17 @@ export class YulStackAssignmentOperator extends SlangNode { constructor(ast: ast.YulStackAssignmentOperator) { super(ast); - this.variant = - ast.variant instanceof TerminalNode - ? ast.variant.unparse() - : new YulEqualAndColon(ast.variant); + const variant = ast.variant; + if (variant instanceof TerminalNode) { + this.variant = variant.unparse(); + return; + } + this.variant = new YulEqualAndColon(variant); - if (typeof this.variant !== 'string') this.updateMetadata(this.variant); + this.updateMetadata(this.variant); } print(path: AstPath, print: PrintFunction): Doc { - return typeof this.variant === 'string' - ? this.variant - : path.call(print, 'variant'); + return printVariant(this, path, print); } } diff --git a/src/slang-nodes/YulStatement.ts b/src/slang-nodes/YulStatement.ts index 100a406b1..fa8f4b414 100644 --- a/src/slang-nodes/YulStatement.ts +++ b/src/slang-nodes/YulStatement.ts @@ -1,3 +1,4 @@ +import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; import { SlangNode } from './SlangNode.js'; import { YulBlock } from './YulBlock.js'; @@ -14,11 +15,57 @@ import { YulContinueStatement } from './YulContinueStatement.js'; import { YulLabel } from './YulLabel.js'; import { YulExpression } from './YulExpression.js'; -import type * as ast from '@nomicfoundation/slang/ast'; import type { AstPath, Doc, ParserOptions } from 'prettier'; import type { AstNode } from './types.d.ts'; import type { PrintFunction } from '../types.d.ts'; +function createNonterminalVariant( + variant: ast.YulStatement['variant'], + options: ParserOptions +): YulStatement['variant'] { + if (variant instanceof ast.YulBlock) { + return new YulBlock(variant, options); + } + if (variant instanceof ast.YulFunctionDefinition) { + return new YulFunctionDefinition(variant, options); + } + if (variant instanceof ast.YulVariableDeclarationStatement) { + return new YulVariableDeclarationStatement(variant, options); + } + if (variant instanceof ast.YulVariableAssignmentStatement) { + return new YulVariableAssignmentStatement(variant, options); + } + if (variant instanceof ast.YulStackAssignmentStatement) { + return new YulStackAssignmentStatement(variant); + } + if (variant instanceof ast.YulIfStatement) { + return new YulIfStatement(variant, options); + } + if (variant instanceof ast.YulForStatement) { + return new YulForStatement(variant, options); + } + if (variant instanceof ast.YulSwitchStatement) { + return new YulSwitchStatement(variant, options); + } + if (variant instanceof ast.YulLeaveStatement) { + return new YulLeaveStatement(variant); + } + if (variant instanceof ast.YulBreakStatement) { + return new YulBreakStatement(variant); + } + if (variant instanceof ast.YulContinueStatement) { + return new YulContinueStatement(variant); + } + if (variant instanceof ast.YulLabel) { + return new YulLabel(variant); + } + if (variant instanceof ast.YulExpression) { + return new YulExpression(variant, options); + } + const exhaustiveCheck: never = variant; + return exhaustiveCheck; +} + export class YulStatement extends SlangNode { readonly kind = NonterminalKind.YulStatement; @@ -40,78 +87,7 @@ export class YulStatement extends SlangNode { constructor(ast: ast.YulStatement, options: ParserOptions) { super(ast); - switch (ast.variant.cst.kind) { - case NonterminalKind.YulBlock: - this.variant = new YulBlock(ast.variant as ast.YulBlock, options); - break; - case NonterminalKind.YulFunctionDefinition: - this.variant = new YulFunctionDefinition( - ast.variant as ast.YulFunctionDefinition, - options - ); - break; - case NonterminalKind.YulVariableDeclarationStatement: - this.variant = new YulVariableDeclarationStatement( - ast.variant as ast.YulVariableDeclarationStatement, - options - ); - break; - case NonterminalKind.YulVariableAssignmentStatement: - this.variant = new YulVariableAssignmentStatement( - ast.variant as ast.YulVariableAssignmentStatement, - options - ); - break; - case NonterminalKind.YulStackAssignmentStatement: - this.variant = new YulStackAssignmentStatement( - ast.variant as ast.YulStackAssignmentStatement - ); - break; - case NonterminalKind.YulIfStatement: - this.variant = new YulIfStatement( - ast.variant as ast.YulIfStatement, - options - ); - break; - case NonterminalKind.YulForStatement: - this.variant = new YulForStatement( - ast.variant as ast.YulForStatement, - options - ); - break; - case NonterminalKind.YulSwitchStatement: - this.variant = new YulSwitchStatement( - ast.variant as ast.YulSwitchStatement, - options - ); - break; - case NonterminalKind.YulLeaveStatement: - this.variant = new YulLeaveStatement( - ast.variant as ast.YulLeaveStatement - ); - break; - case NonterminalKind.YulBreakStatement: - this.variant = new YulBreakStatement( - ast.variant as ast.YulBreakStatement - ); - break; - case NonterminalKind.YulContinueStatement: - this.variant = new YulContinueStatement( - ast.variant as ast.YulContinueStatement - ); - break; - case NonterminalKind.YulLabel: - this.variant = new YulLabel(ast.variant as ast.YulLabel); - break; - case NonterminalKind.YulExpression: - this.variant = new YulExpression( - ast.variant as ast.YulExpression, - options - ); - break; - default: - throw new Error(`Unexpected variant: ${ast.variant.cst.kind}`); - } + this.variant = createNonterminalVariant(ast.variant, options); this.updateMetadata(this.variant); } diff --git a/src/slang-nodes/YulSwitchCase.ts b/src/slang-nodes/YulSwitchCase.ts index f881c7093..366ca6d44 100644 --- a/src/slang-nodes/YulSwitchCase.ts +++ b/src/slang-nodes/YulSwitchCase.ts @@ -1,13 +1,27 @@ +import * as ast from '@nomicfoundation/slang/ast'; import { NonterminalKind } from '@nomicfoundation/slang/cst'; import { SlangNode } from './SlangNode.js'; import { YulDefaultCase } from './YulDefaultCase.js'; import { YulValueCase } from './YulValueCase.js'; -import type * as ast from '@nomicfoundation/slang/ast'; import type { AstPath, Doc, ParserOptions } from 'prettier'; import type { AstNode } from './types.d.ts'; import type { PrintFunction } from '../types.d.ts'; +function createNonterminalVariant( + variant: ast.YulSwitchCase['variant'], + options: ParserOptions +): YulSwitchCase['variant'] { + if (variant instanceof ast.YulDefaultCase) { + return new YulDefaultCase(variant, options); + } + if (variant instanceof ast.YulValueCase) { + return new YulValueCase(variant, options); + } + const exhaustiveCheck: never = variant; + return exhaustiveCheck; +} + export class YulSwitchCase extends SlangNode { readonly kind = NonterminalKind.YulSwitchCase; @@ -16,22 +30,7 @@ export class YulSwitchCase extends SlangNode { constructor(ast: ast.YulSwitchCase, options: ParserOptions) { super(ast); - switch (ast.variant.cst.kind) { - case NonterminalKind.YulDefaultCase: - this.variant = new YulDefaultCase( - ast.variant as ast.YulDefaultCase, - options - ); - break; - case NonterminalKind.YulValueCase: - this.variant = new YulValueCase( - ast.variant as ast.YulValueCase, - options - ); - break; - default: - throw new Error(`Unexpected variant: ${ast.variant.cst.kind}`); - } + this.variant = createNonterminalVariant(ast.variant, options); this.updateMetadata(this.variant); } diff --git a/src/slang-nodes/types.d.ts b/src/slang-nodes/types.d.ts index c8756e9fb..3f2571d82 100644 --- a/src/slang-nodes/types.d.ts +++ b/src/slang-nodes/types.d.ts @@ -460,6 +460,13 @@ export type StrictAstNode = | YulPaths | YulPath; +export type PolymorphicNode = Extract; + +export type StrictPolymorphicNode = Extract< + StrictAstNode, + { variant: StrictAstNode | Identifier | YulIdentifier } +>; + export type Collection = Extract; export type NodeCollection = Extract< @@ -469,7 +476,7 @@ export type NodeCollection = Extract< export type LineCollection = Extract< NodeCollection, - { items: Extract[] } + { items: StrictPolymorphicNode[] } >; export type BinaryOperation = Extract< diff --git a/src/slang-printers/print-variant.ts b/src/slang-printers/print-variant.ts new file mode 100644 index 000000000..9dced13d2 --- /dev/null +++ b/src/slang-printers/print-variant.ts @@ -0,0 +1,11 @@ +import type { AstPath, Doc } from 'prettier'; +import type { PolymorphicNode } from '../slang-nodes/types.d.ts'; +import type { PrintFunction } from '../types.d.ts'; + +export function printVariant( + { variant }: PolymorphicNode, + path: AstPath, + print: PrintFunction +): Doc { + return typeof variant === 'string' ? variant : path.call(print, 'variant'); +}