diff --git a/src/ast/ast_node_factory.ts b/src/ast/ast_node_factory.ts index fa0cc75c..6c5ea9e3 100644 --- a/src/ast/ast_node_factory.ts +++ b/src/ast/ast_node_factory.ts @@ -1,64 +1,94 @@ import { ASTNode, ASTNodeConstructor } from "./ast_node"; import { ASTContext, ASTPostprocessor } from "./ast_reader"; import { FunctionStateMutability, FunctionVisibility } from "./constants"; -import { ContractDefinition } from "./implementation/declaration/contract_definition"; -import { EnumDefinition } from "./implementation/declaration/enum_definition"; -import { EnumValue } from "./implementation/declaration/enum_value"; -import { ErrorDefinition } from "./implementation/declaration/error_definition"; -import { EventDefinition } from "./implementation/declaration/event_definition"; -import { FunctionDefinition } from "./implementation/declaration/function_definition"; -import { ModifierDefinition } from "./implementation/declaration/modifier_definition"; -import { StructDefinition } from "./implementation/declaration/struct_definition"; -import { UserDefinedValueTypeDefinition } from "./implementation/declaration/user_defined_value_type_definition"; -import { VariableDeclaration } from "./implementation/declaration/variable_declaration"; -import { Assignment } from "./implementation/expression/assignment"; -import { BinaryOperation } from "./implementation/expression/binary_operation"; -import { Conditional } from "./implementation/expression/conditional"; -import { ElementaryTypeNameExpression } from "./implementation/expression/elementary_type_name_expression"; -import { FunctionCall } from "./implementation/expression/function_call"; -import { FunctionCallOptions } from "./implementation/expression/function_call_options"; -import { Identifier } from "./implementation/expression/identifier"; -import { IndexAccess } from "./implementation/expression/index_access"; -import { IndexRangeAccess } from "./implementation/expression/index_range_access"; -import { Literal } from "./implementation/expression/literal"; -import { MemberAccess } from "./implementation/expression/member_access"; -import { NewExpression } from "./implementation/expression/new_expression"; -import { PrimaryExpression } from "./implementation/expression/primary_expression"; -import { TupleExpression } from "./implementation/expression/tuple_expression"; -import { UnaryOperation } from "./implementation/expression/unary_operation"; -import { IdentifierPath } from "./implementation/meta/identifier_path"; -import { ImportDirective } from "./implementation/meta/import_directive"; -import { InheritanceSpecifier } from "./implementation/meta/inheritance_specifier"; -import { ModifierInvocation } from "./implementation/meta/modifier_invocation"; -import { OverrideSpecifier } from "./implementation/meta/override_specifier"; -import { ParameterList } from "./implementation/meta/parameter_list"; -import { PragmaDirective } from "./implementation/meta/pragma_directive"; -import { SourceUnit } from "./implementation/meta/source_unit"; -import { StructuredDocumentation } from "./implementation/meta/structured_documentation"; -import { UsingForDirective } from "./implementation/meta/using_for_directive"; -import { Block } from "./implementation/statement/block"; -import { Break } from "./implementation/statement/break"; -import { Continue } from "./implementation/statement/continue"; -import { DoWhileStatement } from "./implementation/statement/do_while_statement"; -import { EmitStatement } from "./implementation/statement/emit_statement"; -import { ExpressionStatement } from "./implementation/statement/expression_statement"; -import { ForStatement } from "./implementation/statement/for_statement"; -import { IfStatement } from "./implementation/statement/if_statement"; -import { InlineAssembly } from "./implementation/statement/inline_assembly"; -import { PlaceholderStatement } from "./implementation/statement/placeholder_statement"; -import { Return } from "./implementation/statement/return"; -import { RevertStatement } from "./implementation/statement/revert_statement"; -import { Throw } from "./implementation/statement/throw"; -import { TryCatchClause } from "./implementation/statement/try_catch_clause"; -import { TryStatement } from "./implementation/statement/try_statement"; -import { UncheckedBlock } from "./implementation/statement/unchecked_block"; -import { VariableDeclarationStatement } from "./implementation/statement/variable_declaration_statement"; -import { WhileStatement } from "./implementation/statement/while_statement"; -import { ArrayTypeName } from "./implementation/type/array_type_name"; -import { ElementaryTypeName } from "./implementation/type/elementary_type_name"; -import { FunctionTypeName } from "./implementation/type/function_type_name"; -import { Mapping } from "./implementation/type/mapping"; -import { UserDefinedTypeName } from "./implementation/type/user_defined_type_name"; +import { + ContractDefinition, + EnumDefinition, + EnumValue, + ErrorDefinition, + EventDefinition, + FunctionDefinition, + ModifierDefinition, + StructDefinition, + UserDefinedValueTypeDefinition, + VariableDeclaration +} from "./implementation/declaration"; +import { + Assignment, + BinaryOperation, + Conditional, + ElementaryTypeNameExpression, + FunctionCall, + FunctionCallOptions, + Identifier, + IndexAccess, + IndexRangeAccess, + Literal, + MemberAccess, + NewExpression, + PrimaryExpression, + TupleExpression, + UnaryOperation +} from "./implementation/expression"; +import { + IdentifierPath, + ImportDirective, + InheritanceSpecifier, + ModifierInvocation, + OverrideSpecifier, + ParameterList, + PragmaDirective, + SourceUnit, + StructuredDocumentation, + UsingForDirective +} from "./implementation/meta"; +import { + Block, + Break, + Continue, + DoWhileStatement, + EmitStatement, + ExpressionStatement, + ForStatement, + IfStatement, + InlineAssembly, + PlaceholderStatement, + Return, + RevertStatement, + Throw, + TryCatchClause, + TryStatement, + UncheckedBlock, + VariableDeclarationStatement, + WhileStatement +} from "./implementation/statement"; +import { + ArrayTypeName, + ElementaryTypeName, + FunctionTypeName, + Mapping, + UserDefinedTypeName +} from "./implementation/type"; +import { + YulAssignment, + YulBlock, + YulBreak, + YulContinue, + YulCase, + YulExpression, + YulExpressionStatement, + YulForLoop, + YulFunctionDefinition, + YulIf, + YulLeave, + YulSwitch, + YulVariableDeclaration, + YulLiteral, + YulIdentifier, + YulFunctionCall, + YulTypedName, + isYulASTNode +} from "./implementation/yul"; /** * When applied to following tuple type: @@ -651,6 +681,140 @@ const argExtractionMapping = new Map, (node: any) => node.path, node.raw ] + ], + [ + YulAssignment, + (node: YulAssignment): Specific> => [ + node.variableNames, + node.value, + node.documentation + ] + ], + [ + YulBlock, + (node: YulBlock): Specific> => [ + node.vStatements, + node.documentation, + node.raw + ] + ], + [ + YulBreak, + (node: YulBreak): Specific> => [ + node.documentation, + node.raw + ] + ], + [ + YulContinue, + (node: YulContinue): Specific> => [ + node.documentation, + node.raw + ] + ], + [ + YulCase, + (node: YulCase): Specific> => [ + node.value, + node.vBody, + node.documentation, + node.raw + ] + ], + [ + YulExpressionStatement, + ( + node: YulExpressionStatement + ): Specific> => [ + node.vExpression, + node.documentation, + node.raw + ] + ], + [ + YulForLoop, + (node: YulForLoop): Specific> => [ + node.vPre, + node.vCondition, + node.vPost, + node.vBody, + node.documentation, + node.raw + ] + ], + [ + YulFunctionDefinition, + ( + node: YulFunctionDefinition + ): Specific> => [ + node.scope, + node.name, + node.vParameters, + node.vReturnParameters, + node.vBody, + node.documentation, + node.raw + ] + ], + [ + YulIf, + (node: YulIf): Specific> => [ + node.vCondition, + node.vBody, + node.documentation, + node.raw + ] + ], + [ + YulLeave, + (node: YulLeave): Specific> => [ + node.documentation, + node.raw + ] + ], + [ + YulSwitch, + (node: YulSwitch): Specific> => [ + node.vExpression, + node.vCases, + node.documentation, + node.raw + ] + ], + [ + YulVariableDeclaration, + ( + node: YulVariableDeclaration + ): Specific> => [ + node.variables, + node.value, + node.documentation, + node.raw + ] + ], + [ + YulLiteral, + (node: YulLiteral): Specific> => { + return [node.kind, node.value, node.hexValue, node.typeString, node.raw]; + } + ], + [ + YulIdentifier, + (node: YulIdentifier): Specific> => { + return [node.name, node.referencedDeclaration, node.raw]; + } + ], + [ + YulFunctionCall, + (node: YulFunctionCall): Specific> => { + return [node.vFunctionName, node.vArguments, node.raw]; + } + ], + [ + YulTypedName, + (node: YulTypedName): Specific> => { + return [node.name, node.typeString, node.raw]; + } ] ]); @@ -659,12 +823,90 @@ export class ASTNodeFactory { postprocessor: ASTPostprocessor; private lastId: number; + private lastYulId: number; constructor(context = new ASTContext(), postprocessor = new ASTPostprocessor()) { this.context = context; this.postprocessor = postprocessor; this.lastId = context.lastId; + this.lastYulId = context.lastYulId; + } + + makeYulAssignment( + ...args: Specific> + ): YulAssignment { + return this.make(YulAssignment, ...args); + } + + makeYulBlock(...args: Specific>): YulBlock { + return this.make(YulBlock, ...args); + } + + makeYulBreak(...args: Specific>): YulBreak { + return this.make(YulBreak, ...args); + } + + makeYulContinue(...args: Specific>): YulContinue { + return this.make(YulContinue, ...args); + } + + makeYulCase(...args: Specific>): YulCase { + return this.make(YulCase, ...args); + } + + makeYulExpressionStatement( + ...args: Specific> + ): YulExpressionStatement { + return this.make(YulExpressionStatement, ...args); + } + + makeYulForLoop(...args: Specific>): YulForLoop { + return this.make(YulForLoop, ...args); + } + + makeYulFunctionDefinition( + ...args: Specific> + ): YulFunctionDefinition { + return this.make(YulFunctionDefinition, ...args); + } + + makeYulIf(...args: Specific>): YulIf { + return this.make(YulIf, ...args); + } + + makeYulLeave(...args: Specific>): YulLeave { + return this.make(YulLeave, ...args); + } + + makeYulSwitch(...args: Specific>): YulSwitch { + return this.make(YulSwitch, ...args); + } + + makeYulVariableDeclaration( + ...args: Specific> + ): YulVariableDeclaration { + return this.make(YulVariableDeclaration, ...args); + } + + makeYulLiteral(...args: Specific>): YulLiteral { + return this.make(YulLiteral, ...args); + } + + makeYulIdentifier( + ...args: Specific> + ): YulIdentifier { + return this.make(YulIdentifier, ...args); + } + + makeYulFunctionCall( + ...args: Specific> + ): YulFunctionCall { + return this.make(YulFunctionCall, ...args); + } + + makeYulTypedName(...args: Specific>): YulTypedName { + return this.make(YulTypedName, ...args); } makeContractDefinition( @@ -1046,11 +1288,44 @@ export class ASTNodeFactory { ); } + makeYulIdentifierFor( + target: + | FunctionDefinition + | VariableDeclaration + | Identifier + | YulFunctionDefinition + | YulVariableDeclaration, + name?: string + ): YulIdentifier { + if (target instanceof YulVariableDeclaration) { + if (target.variables.length === 1) { + name = target.variables[0].name; + } else if ( + name === undefined || + target.variables.find((v) => v.name === name) === undefined + ) { + const declarationString = `${target.type} (${target.variables.map((v) => v.name)})`; + throw Error(`variable ${name} not found in ${declarationString}`); + } + } else { + name = target.name; + } + return this.makeYulIdentifier(name, target.id); + } + + makeYulFunctionCallFor( + fn: YulFunctionDefinition, + parameters: YulExpression[] + ): YulFunctionCall { + return this.makeYulFunctionCall(this.makeYulIdentifierFor(fn), parameters); + } + makeUnfinalized( type: ASTNodeConstructor, ...args: Specific> ): T { - const node = new type(++this.lastId, "0:0:0", ...args); + const id = isYulASTNode(type.prototype) ? ++this.lastYulId : ++this.lastId; + const node = new type(id, "0:0:0", ...args); this.context.register(node); diff --git a/src/ast/ast_node_formatter.ts b/src/ast/ast_node_formatter.ts index f477b084..cb62462f 100644 --- a/src/ast/ast_node_formatter.ts +++ b/src/ast/ast_node_formatter.ts @@ -1,4 +1,4 @@ -import { ASTContext, ASTNode } from "./"; +import { ASTContext, ASTNode, YulASTNode, YulASTNodeWithChildren } from "./"; const INDENT = " ".repeat(4); const SKIP = new Set(["requiredContext", "raw", "ownChildren"]); @@ -82,6 +82,9 @@ export class ASTNodeFormatter { } private formatNode(node: ASTNode, level: number, depth: number): string { + if (node instanceof YulASTNode || node instanceof YulASTNodeWithChildren) { + return this.formatValue(node.raw); + } const output = []; const value = this.formatNodeValue(node); diff --git a/src/ast/ast_reader.ts b/src/ast/ast_reader.ts index 3a51d424..fec4fd92 100644 --- a/src/ast/ast_reader.ts +++ b/src/ast/ast_reader.ts @@ -1,5 +1,7 @@ +// import { deepFindIn } from "../misc"; import { ASTNode, ASTNodeConstructor } from "./ast_node"; import { SourceUnit } from "./implementation/meta/source_unit"; +import { isYulASTNode } from "./implementation/yul"; import { LegacyConfiguration } from "./legacy"; import { ModernConfiguration } from "./modern"; import { DefaultNodePostprocessorList } from "./postprocessing"; @@ -45,6 +47,12 @@ export class ASTContext { */ id = contextIdSequence.next().value; + /** + * Temporary workaround + */ + readonly yulIdStart = 1e5; + lastYulId = this.yulIdStart; + /** * Map from ID number to the `AST` node with same ID in tree */ @@ -61,6 +69,9 @@ export class ASTContext { let last = 0; for (const id of this.map.keys()) { + if (id >= this.yulIdStart) { + continue; + } if (id > last) { last = id; } @@ -75,6 +86,9 @@ export class ASTContext { register(...nodes: ASTNode[]): void { for (const node of nodes) { + if (node.id === undefined && isYulASTNode(node)) { + node.id = ++this.lastYulId; + } if (this.map.has(node.id)) { throw new Error(`The id ${node.id} is already taken for the context`); } @@ -190,6 +204,19 @@ export class ASTReader { const rootNodeTypeName = "SourceUnit"; const result: SourceUnit[] = []; + // const nodes = entries.map((e) => e[1]); + // const hasAssemblyAST = deepFindIn( + // nodes, + // "nodeType", + // (node: any) => node.nodeType.startsWith("Yul"), + // true + // ); + // if (hasAssemblyAST) { + // const ids = deepFindIn(nodes, "id", (id) => typeof id === "number"); + // const lastId = Math.max(...ids); + // this.context.lastOriginalId = lastId; + // } + for (const [key, content] of entries) { let ast; diff --git a/src/ast/constants.ts b/src/ast/constants.ts index 5fc2f489..d46010f8 100644 --- a/src/ast/constants.ts +++ b/src/ast/constants.ts @@ -72,6 +72,12 @@ export enum LiteralKind { UnicodeString = "unicodeString" } +export enum YulLiteralKind { + Number = "number", + Bool = "bool", + String = "string" +} + export enum EtherUnit { Wei = "wei", GWei = "gwei", diff --git a/src/ast/documentation.ts b/src/ast/documentation.ts index 564a594a..72384404 100644 --- a/src/ast/documentation.ts +++ b/src/ast/documentation.ts @@ -21,8 +21,8 @@ export interface WithDanglingDocs { danglingDocString?: string; } -export function getDocumentation( - node: WithPrecedingDocs & ASTNodeWithChildren +export function getDocumentation( + node: WithPrecedingDocs & ASTNodeWithChildren ): string | StructuredDocumentation | undefined { if (node.docString !== undefined) { return node.docString; @@ -50,8 +50,8 @@ export function getDocumentation( return undefined; } -export function setDocumentation( - node: WithPrecedingDocs & ASTNodeWithChildren, +export function setDocumentation( + node: WithPrecedingDocs & ASTNodeWithChildren, value: string | StructuredDocumentation | undefined ): void { const old = node.documentation; @@ -61,22 +61,22 @@ export function setDocumentation( if (old instanceof StructuredDocumentation) { if (value !== old) { - node.replaceChild(value, old); + node.replaceChild(value as any, old as any); } } else { - node.insertAtBeginning(value); + node.insertAtBeginning(value as any); } } else { if (old instanceof StructuredDocumentation) { - node.removeChild(old); + node.removeChild(old as any); } node.docString = value; } } -export function getDanglingDocumentation( - node: WithDanglingDocs & ASTNodeWithChildren +export function getDanglingDocumentation( + node: WithDanglingDocs & ASTNodeWithChildren ): string | StructuredDocumentation | undefined { if (node.danglingDocString !== undefined) { return node.danglingDocString; @@ -104,8 +104,8 @@ export function getDanglingDocumentation( return undefined; } -export function setDanglingDocumentation( - node: WithDanglingDocs & ASTNodeWithChildren, +export function setDanglingDocumentation( + node: WithDanglingDocs & ASTNodeWithChildren, value: string | StructuredDocumentation | undefined ): void { const old = node.danglingDocumentation; diff --git a/src/ast/implementation/index.ts b/src/ast/implementation/index.ts new file mode 100644 index 00000000..7061a63e --- /dev/null +++ b/src/ast/implementation/index.ts @@ -0,0 +1,6 @@ +export * from "./declaration"; +export * from "./expression"; +export * from "./meta"; +export * from "./statement"; +export * from "./type"; +export * from "./yul"; diff --git a/src/ast/implementation/statement/inline_assembly.ts b/src/ast/implementation/statement/inline_assembly.ts index 3f84b5e5..b3767b41 100644 --- a/src/ast/implementation/statement/inline_assembly.ts +++ b/src/ast/implementation/statement/inline_assembly.ts @@ -1,4 +1,6 @@ +import { ASTNode } from "../../ast_node"; import { StructuredDocumentation } from "../meta"; +import { YulBlock } from "../yul"; import { Statement } from "./statement"; export interface YulNode { @@ -12,7 +14,7 @@ export class InlineAssembly extends Statement { externalReferences: any[]; operations?: string; - yul?: YulNode; + yul?: YulBlock; flags?: string[]; evmVersion?: string; @@ -22,7 +24,7 @@ export class InlineAssembly extends Statement { src: string, externalReferences: any[], operations?: string, - yul?: YulNode, + yul?: YulBlock, flags?: string[], evmVersion?: string, documentation?: string | StructuredDocumentation, @@ -35,5 +37,10 @@ export class InlineAssembly extends Statement { this.yul = yul; this.flags = flags; this.evmVersion = evmVersion; + this.acceptChildren(); + } + + get children(): ASTNode[] { + return this.pickNodes(this.documentation, this.yul); } } diff --git a/src/ast/implementation/yul/expression/index.ts b/src/ast/implementation/yul/expression/index.ts new file mode 100644 index 00000000..c828ac07 --- /dev/null +++ b/src/ast/implementation/yul/expression/index.ts @@ -0,0 +1,5 @@ +export * from "./yul_expression"; +export * from "./yul_function_call"; +export * from "./yul_literal"; +export * from "./yul_identifier"; +export * from "./yul_typed_name"; diff --git a/src/ast/implementation/yul/expression/yul_expression.ts b/src/ast/implementation/yul/expression/yul_expression.ts new file mode 100644 index 00000000..7ebbc53d --- /dev/null +++ b/src/ast/implementation/yul/expression/yul_expression.ts @@ -0,0 +1,3 @@ +import { YulASTNode } from "../yul_ast_node"; + +export class YulExpression extends YulASTNode {} diff --git a/src/ast/implementation/yul/expression/yul_function_call.ts b/src/ast/implementation/yul/expression/yul_function_call.ts new file mode 100644 index 00000000..f9f352de --- /dev/null +++ b/src/ast/implementation/yul/expression/yul_function_call.ts @@ -0,0 +1,56 @@ +import { ExternalReferenceType } from "../../../constants"; +import { YulIdentifier } from "./yul_identifier"; +import { YulExpression } from "./yul_expression"; +import { YulFunctionDefinition } from "../statement/yul_function_definition"; +import { YulASTNode } from "../yul_ast_node"; + +export class YulFunctionCall extends YulExpression { + /** + * YulIdentifier that defines the callee + */ + vFunctionName: YulIdentifier; + + /** + * Call arguments, e.g array with `1` and `2` expressions in `foo(1, 2)` + */ + vArguments: YulExpression[]; + + constructor( + id: number, + src: string, + functionName: YulIdentifier, + args: YulExpression[], + raw?: any + ) { + super(id, src, raw); + this.vFunctionName = functionName; + this.vArguments = args; + + this.acceptChildren(); + } + + get children(): readonly YulASTNode[] { + return this.pickNodes(this.vFunctionName, this.vArguments); + } + + /** + * Identifier of the function name, e.g. `sha3(...)` + */ + get vIdentifier(): string { + return this.vFunctionName.name; + } + + /** + * Solidity builtin or user-defined function + */ + get vFunctionCallType(): ExternalReferenceType { + return this.vFunctionName.vIdentifierType; + } + + /** + * Called function definition reference + */ + get vReferencedDeclaration(): YulFunctionDefinition | undefined { + return this.vFunctionName.vReferencedDeclaration as YulFunctionDefinition; + } +} diff --git a/src/ast/implementation/yul/expression/yul_identifier.ts b/src/ast/implementation/yul/expression/yul_identifier.ts new file mode 100644 index 00000000..396164fa --- /dev/null +++ b/src/ast/implementation/yul/expression/yul_identifier.ts @@ -0,0 +1,52 @@ +import { ASTNode } from "../../../ast_node"; +import { ExternalReferenceType } from "../../../constants"; +import { YulExpression } from "./yul_expression"; + +export class YulIdentifier extends YulExpression { + /** + * Name of the identifier + */ + name: string; + + /** + * Id of the referenced declaration + */ + referencedDeclaration: number; + + constructor(id: number, src: string, name: string, referencedDeclaration?: number, raw?: any) { + super(id, src, raw); + + this.name = name; + this.referencedDeclaration = referencedDeclaration ?? -1; + } + + /** + * Attribute to access the converted referenced declaration. + * + * Is `undefined` when this is a Solidity internal identifier. + */ + get vReferencedDeclaration(): ASTNode | undefined { + return this.requiredContext.locate(this.referencedDeclaration); + } + + set vReferencedDeclaration(value: ASTNode | undefined) { + if (value === undefined) { + this.referencedDeclaration = -1; + } else { + if (!this.requiredContext.contains(value)) { + throw new Error(`Node ${value.type}#${value.id} not belongs to a current context`); + } + + this.referencedDeclaration = value.id; + } + } + + /** + * Solidity builtin or user-defined reference + */ + get vIdentifierType(): ExternalReferenceType { + return this.vReferencedDeclaration + ? ExternalReferenceType.UserDefined + : ExternalReferenceType.Builtin; + } +} diff --git a/src/ast/implementation/yul/expression/yul_literal.ts b/src/ast/implementation/yul/expression/yul_literal.ts new file mode 100644 index 00000000..be721734 --- /dev/null +++ b/src/ast/implementation/yul/expression/yul_literal.ts @@ -0,0 +1,41 @@ +import { YulLiteralKind } from "../../../constants"; +import { YulExpression } from "./yul_expression"; + +export class YulLiteral extends YulExpression { + /** + * The type of literal: `number`, `string` or `bool` + */ + kind: YulLiteralKind; + + /** + * Hexadecimal representation of value of the literal symbol + */ + hexValue: string; + + /** + * Value of the literal symbol + */ + value: string; + + /** + * Yul type string, e.g.u256 + */ + typeString: string; + + constructor( + id: number, + src: string, + kind: YulLiteralKind, + value: string, + hexValue: string, + typeString = "", + raw?: any + ) { + super(id, src, raw); + + this.kind = kind; + this.value = value; + this.hexValue = hexValue; + this.typeString = typeString; + } +} diff --git a/src/ast/implementation/yul/expression/yul_typed_name.ts b/src/ast/implementation/yul/expression/yul_typed_name.ts new file mode 100644 index 00000000..59031b7b --- /dev/null +++ b/src/ast/implementation/yul/expression/yul_typed_name.ts @@ -0,0 +1,20 @@ +import { YulExpression } from "./yul_expression"; + +export class YulTypedName extends YulExpression { + /** + * Name of the identifier + */ + name: string; + + /** + * Yul type string, e.g.u256 + */ + typeString: string; + + constructor(id: number, src: string, name: string, typeString = "", raw?: any) { + super(id, src, raw); + + this.name = name; + this.typeString = typeString; + } +} diff --git a/src/ast/implementation/yul/index.ts b/src/ast/implementation/yul/index.ts new file mode 100644 index 00000000..0fd1ec2a --- /dev/null +++ b/src/ast/implementation/yul/index.ts @@ -0,0 +1,3 @@ +export * from "./expression"; +export * from "./statement"; +export * from "./yul_ast_node"; diff --git a/src/ast/implementation/yul/statement/index.ts b/src/ast/implementation/yul/statement/index.ts new file mode 100644 index 00000000..7fd11a5f --- /dev/null +++ b/src/ast/implementation/yul/statement/index.ts @@ -0,0 +1,13 @@ +export * from "./yul_assignment"; +export * from "./yul_block"; +export * from "./yul_break"; +export * from "./yul_case"; +export * from "./yul_continue"; +export * from "./yul_expression_statement"; +export * from "./yul_for_loop"; +export * from "./yul_function_definition"; +export * from "./yul_if"; +export * from "./yul_leave"; +export * from "./yul_statement"; +export * from "./yul_switch"; +export * from "./yul_variable_declaration"; diff --git a/src/ast/implementation/yul/statement/yul_assignment.ts b/src/ast/implementation/yul/statement/yul_assignment.ts new file mode 100644 index 00000000..c8c250e0 --- /dev/null +++ b/src/ast/implementation/yul/statement/yul_assignment.ts @@ -0,0 +1,29 @@ +import { StructuredDocumentation } from "../../meta"; +import { YulExpression, YulIdentifier } from "../expression"; +import { YulASTNode } from "../yul_ast_node"; + +import { YulStatement } from "./yul_statement"; + +export class YulAssignment extends YulStatement { + variableNames: YulIdentifier[]; + + value: YulExpression; + + constructor( + id: number, + src: string, + variableNames: YulIdentifier[], + value: YulExpression, + documentation?: string | StructuredDocumentation, + raw?: any + ) { + super(id, src, documentation, raw); + this.variableNames = variableNames; + this.value = value; + this.acceptChildren(); + } + + get children(): readonly YulASTNode[] { + return this.pickNodes(this.documentation, this.variableNames, this.value); + } +} diff --git a/src/ast/implementation/yul/statement/yul_block.ts b/src/ast/implementation/yul/statement/yul_block.ts new file mode 100644 index 00000000..acb010cf --- /dev/null +++ b/src/ast/implementation/yul/statement/yul_block.ts @@ -0,0 +1,30 @@ +import { StructuredDocumentation } from "../../meta"; +import { YulASTNode } from "../yul_ast_node"; +import { YulStatement, YulStatementWithChildren } from "./yul_statement"; + +export class YulBlock extends YulStatementWithChildren< + YulStatement | YulStatementWithChildren | StructuredDocumentation +> { + constructor( + id: number, + src: string, + statements: Iterable, + documentation?: string | StructuredDocumentation, + raw?: any + ) { + super(id, src, documentation, raw); + + for (const statement of statements) { + this.appendChild(statement); + } + } + + /** + * An array of the member statements + */ + get vStatements(): Array> { + return this.ownChildren.filter( + (node) => node instanceof YulStatement || node instanceof YulStatementWithChildren + ); + } +} diff --git a/src/ast/implementation/yul/statement/yul_break.ts b/src/ast/implementation/yul/statement/yul_break.ts new file mode 100644 index 00000000..b7adfaf9 --- /dev/null +++ b/src/ast/implementation/yul/statement/yul_break.ts @@ -0,0 +1,3 @@ +import { YulStatement } from "./yul_statement"; + +export class YulBreak extends YulStatement {} diff --git a/src/ast/implementation/yul/statement/yul_case.ts b/src/ast/implementation/yul/statement/yul_case.ts new file mode 100644 index 00000000..1d9a4382 --- /dev/null +++ b/src/ast/implementation/yul/statement/yul_case.ts @@ -0,0 +1,30 @@ +import { StructuredDocumentation } from "../../meta"; +import { YulLiteral } from "../expression"; +import { YulASTNode } from "../yul_ast_node"; +import { YulBlock } from "./yul_block"; +import { YulStatement } from "./yul_statement"; + +export class YulCase extends YulStatement { + value: YulLiteral | "default"; + + vBody: YulBlock; + + constructor( + id: number, + src: string, + value: YulLiteral | "default", + body: YulBlock, + documentation?: string | StructuredDocumentation, + raw?: any + ) { + super(id, src, documentation, raw); + this.value = value; + this.vBody = body; + + this.acceptChildren(); + } + + get children(): readonly YulASTNode[] { + return this.pickNodes(this.documentation, this.value, this.vBody); + } +} diff --git a/src/ast/implementation/yul/statement/yul_continue.ts b/src/ast/implementation/yul/statement/yul_continue.ts new file mode 100644 index 00000000..68c0411b --- /dev/null +++ b/src/ast/implementation/yul/statement/yul_continue.ts @@ -0,0 +1,3 @@ +import { YulStatement } from "./yul_statement"; + +export class YulContinue extends YulStatement {} diff --git a/src/ast/implementation/yul/statement/yul_expression_statement.ts b/src/ast/implementation/yul/statement/yul_expression_statement.ts new file mode 100644 index 00000000..1e5ae345 --- /dev/null +++ b/src/ast/implementation/yul/statement/yul_expression_statement.ts @@ -0,0 +1,31 @@ +import { StructuredDocumentation } from "../../meta"; +import { YulExpression } from "../expression"; +import { YulASTNode } from "../yul_ast_node"; +import { YulStatement } from "./yul_statement"; +/** + * An expression, that is specified on a statement level. + */ +export class YulExpressionStatement extends YulStatement { + /** + * A contained expression, e.g. `foo(1);` or `x = 1 + 1;` + */ + vExpression: YulExpression; + + constructor( + id: number, + src: string, + expression: YulExpression, + documentation?: string | StructuredDocumentation, + raw?: any + ) { + super(id, src, documentation, raw); + + this.vExpression = expression; + + this.acceptChildren(); + } + + get children(): readonly YulASTNode[] { + return this.pickNodes(this.documentation, this.vExpression); + } +} diff --git a/src/ast/implementation/yul/statement/yul_for_loop.ts b/src/ast/implementation/yul/statement/yul_for_loop.ts new file mode 100644 index 00000000..b1fb6c44 --- /dev/null +++ b/src/ast/implementation/yul/statement/yul_for_loop.ts @@ -0,0 +1,58 @@ +import { YulASTNode } from "../yul_ast_node"; +import { YulExpression } from "../expression/yul_expression"; +import { StructuredDocumentation } from "../../meta"; +import { YulBlock } from "./yul_block"; +import { YulStatement } from "./yul_statement"; + +export class YulForLoop extends YulStatement { + /** + * Variable declaration and initialization `uint x = 1`. + * Also accepts other expression statements. + */ + vPre: YulBlock; + + /** + * Continuation condition, e.g. `x < 10` + */ + vCondition: YulExpression; + + /** + * Loop expression, e.g. `x++` + */ + vPost: YulBlock; + + /** + * YulBlock that gets executed if the condition is evaluated to `true` + */ + vBody: YulBlock; + + constructor( + id: number, + src: string, + pre: YulBlock, + condition: YulExpression, + post: YulBlock, + body: YulBlock, + documentation?: string | StructuredDocumentation, + raw?: any + ) { + super(id, src, documentation, raw); + + this.vPre = pre; + this.vCondition = condition; + this.vPost = post; + this.vBody = body; + + this.acceptChildren(); + } + + get children(): readonly YulASTNode[] { + return this.pickNodes( + this.documentation, + this.vPre, + this.vCondition, + this.vPost, + this.vBody + ); + } +} diff --git a/src/ast/implementation/yul/statement/yul_function_definition.ts b/src/ast/implementation/yul/statement/yul_function_definition.ts new file mode 100644 index 00000000..24ed8b88 --- /dev/null +++ b/src/ast/implementation/yul/statement/yul_function_definition.ts @@ -0,0 +1,92 @@ +import { StructuredDocumentation } from "../../meta/structured_documentation"; +import { YulBlock } from "../statement/yul_block"; +import { YulASTNode } from "../yul_ast_node"; +import { YulTypedName } from "../expression"; +import { YulStatement } from "./yul_statement"; + +export class YulFunctionDefinition extends YulStatement { + /** + * Node id of scoped block + */ + scope: number; + + /** + * Identifier of the function + */ + name: string; + + /** + * Optional documentation appearing above the function definition: + * - Is `undefined` when not specified. + * - Is type of `string` when specified and compiler version is older than `0.6.3`. + * - Is instance of `StructuredDocumentation` when specified and compiler version is `0.6.3` or newer. + */ + documentation?: string | StructuredDocumentation; + + /** + * Function body block: can be empty if function is declared, but not implemented. + * Always filled otherwise. + */ + vBody: YulBlock; + + /** + * A list of local variables that are declared and initialized with the input values + */ + vParameters: YulTypedName[]; + + /** + * A list of local variables that are declared and returned to the caller + */ + vReturnParameters: YulTypedName[]; + + constructor( + id: number, + src: string, + scope: number, + name: string, + // solc does not generate empty arrays if parameters are not populated + parameters: YulTypedName[] = [], + returnParameters: YulTypedName[] = [], + body: YulBlock, + documentation?: string | StructuredDocumentation, + raw?: any + ) { + super(id, src, documentation, raw); + + this.scope = scope; + this.name = name; + this.documentation = documentation; + + this.vParameters = parameters; + this.vReturnParameters = returnParameters; + this.vBody = body; + + this.acceptChildren(); + } + + get children(): readonly YulASTNode[] { + return this.pickNodes( + this.documentation, + this.vParameters, + this.vReturnParameters, + this.vBody + ); + } + + /** + * Reference to a scoped `ContractDefinition` if function is declared in contract. + * Reference to a scoped `SourceUnit` if function is declared on file level + * (since Solidity 0.7.1). + */ + get vScope(): YulBlock { + return this.requiredContext.locate(this.scope) as YulBlock; + } + + set vScope(value: YulBlock) { + if (!this.requiredContext.contains(value)) { + throw new Error(`Node ${value.type}#${value.id} not belongs to a current context`); + } + + this.scope = value.id; + } +} diff --git a/src/ast/implementation/yul/statement/yul_if.ts b/src/ast/implementation/yul/statement/yul_if.ts new file mode 100644 index 00000000..bfa28537 --- /dev/null +++ b/src/ast/implementation/yul/statement/yul_if.ts @@ -0,0 +1,37 @@ +import { YulASTNode } from "../yul_ast_node"; +import { YulExpression } from "../expression"; +import { StructuredDocumentation } from "../../meta"; +import { YulBlock } from "./yul_block"; +import { YulStatement } from "./yul_statement"; + +export class YulIf extends YulStatement { + /** + * Condition expression of the statement + */ + vCondition: YulExpression; + + /** + * YulBlock that gets executed if condition is truthy + */ + vBody: YulBlock; + + constructor( + id: number, + src: string, + condition: YulExpression, + body: YulBlock, + documentation?: string | StructuredDocumentation, + raw?: any + ) { + super(id, src, documentation, raw); + + this.vCondition = condition; + this.vBody = body; + + this.acceptChildren(); + } + + get children(): readonly YulASTNode[] { + return this.pickNodes(this.documentation, this.vCondition, this.vBody); + } +} diff --git a/src/ast/implementation/yul/statement/yul_leave.ts b/src/ast/implementation/yul/statement/yul_leave.ts new file mode 100644 index 00000000..4b77a379 --- /dev/null +++ b/src/ast/implementation/yul/statement/yul_leave.ts @@ -0,0 +1,3 @@ +import { YulStatement } from "./yul_statement"; + +export class YulLeave extends YulStatement {} diff --git a/src/ast/implementation/yul/statement/yul_statement.ts b/src/ast/implementation/yul/statement/yul_statement.ts new file mode 100644 index 00000000..49712eca --- /dev/null +++ b/src/ast/implementation/yul/statement/yul_statement.ts @@ -0,0 +1,83 @@ +import { YulASTNode, YulASTNodeWithChildren } from "../yul_ast_node"; +import { + getDanglingDocumentation, + getDocumentation, + setDanglingDocumentation, + setDocumentation, + WithDanglingDocs, + WithPrecedingDocs +} from "../../../documentation"; +import { StructuredDocumentation } from "../../meta"; + +export class YulStatement extends YulASTNode { + /** + * Optional documentation appearing above the statement: + * - Is `undefined` when not specified. + * - Is type of `string` for compatibility reasons. + */ + documentation?: string | StructuredDocumentation; + + constructor( + id: number, + src: string, + documentation?: string | StructuredDocumentation, + raw?: any + ) { + super(id, src, raw); + + this.documentation = documentation; + } + + get children(): readonly YulASTNode[] { + return this.pickNodes(this.documentation); + } +} + +export class YulStatementWithChildren + extends YulASTNodeWithChildren + implements WithPrecedingDocs, WithDanglingDocs +{ + docString?: string; + danglingDocString?: string; + + constructor( + id: number, + src: string, + documentation?: string | StructuredDocumentation, + raw?: any + ) { + super(id, src, raw); + + this.documentation = documentation; + } + + /** + * Optional documentation appearing above the contract definition: + * - Is `undefined` when not specified. + * - Is type of `string` when specified and compiler version is older than `0.6.3`. + * - Is instance of `StructuredDocumentation` when specified and compiler version is `0.6.3` or newer. + */ + get documentation(): string | StructuredDocumentation | undefined { + return getDocumentation(this); + } + + set documentation(value: string | StructuredDocumentation | undefined) { + setDocumentation(this, value); + } + + /** + * Optional documentation that is dangling in the source fragment, + * that is after end of last child and before the end of the current node. + * + * It is: + * - Is `undefined` when not detected. + * - Is type of `string` for compatibility reasons. + */ + get danglingDocumentation(): string | StructuredDocumentation | undefined { + return getDanglingDocumentation(this); + } + + set danglingDocumentation(value: string | StructuredDocumentation | undefined) { + setDanglingDocumentation(this, value); + } +} diff --git a/src/ast/implementation/yul/statement/yul_switch.ts b/src/ast/implementation/yul/statement/yul_switch.ts new file mode 100644 index 00000000..942ca1d1 --- /dev/null +++ b/src/ast/implementation/yul/statement/yul_switch.ts @@ -0,0 +1,29 @@ +import { StructuredDocumentation } from "../../meta"; +import { YulExpression } from "../expression"; +import { YulASTNode } from "../yul_ast_node"; +import { YulCase } from "./yul_case"; +import { YulStatement } from "./yul_statement"; + +export class YulSwitch extends YulStatement { + vExpression: YulExpression; + + vCases: YulCase[]; + + constructor( + id: number, + src: string, + expression: YulExpression, + cases: YulCase[], + documentation?: string | StructuredDocumentation, + raw?: any + ) { + super(id, src, documentation, raw); + this.vExpression = expression; + this.vCases = cases; + this.acceptChildren(); + } + + get children(): readonly YulASTNode[] { + return this.pickNodes(this.documentation, this.vExpression, this.vCases); + } +} diff --git a/src/ast/implementation/yul/statement/yul_variable_declaration.ts b/src/ast/implementation/yul/statement/yul_variable_declaration.ts new file mode 100644 index 00000000..d25fdda1 --- /dev/null +++ b/src/ast/implementation/yul/statement/yul_variable_declaration.ts @@ -0,0 +1,30 @@ +// import { YulStatement } from "./yul_statement"; + +import { StructuredDocumentation } from "../../meta"; +import { YulExpression, YulTypedName } from "../expression"; +import { YulASTNode } from "../yul_ast_node"; +import { YulStatement } from "./yul_statement"; + +export class YulVariableDeclaration extends YulStatement { + variables: YulTypedName[]; + + value?: YulExpression; + + constructor( + id: number, + src: string, + variables: YulTypedName[], + value?: YulExpression, + documentation?: string | StructuredDocumentation, + raw?: any + ) { + super(id, src, documentation, raw); + this.variables = variables; + this.value = value; + this.acceptChildren(); + } + + get children(): readonly YulASTNode[] { + return this.pickNodes(this.documentation, this.variables, this.value); + } +} diff --git a/src/ast/implementation/yul/yul_ast_node.ts b/src/ast/implementation/yul/yul_ast_node.ts new file mode 100644 index 00000000..ad91661a --- /dev/null +++ b/src/ast/implementation/yul/yul_ast_node.ts @@ -0,0 +1,121 @@ +import { ASTNode, ASTNodeWithChildren } from "../../ast_node"; +import { InlineAssembly } from "../statement"; + +export function isYulASTNode( + node: ASTNode +): node is YulASTNode | YulASTNodeWithChildren { + return node instanceof YulASTNode || node instanceof YulASTNodeWithChildren; +} + +export class YulASTNode extends ASTNode {} + +export interface YulASTNode { + get children(): readonly YulASTNode[]; + get firstChild(): ASTNode | undefined; + get lastChild(): YulASTNode | undefined; + get previousSibling(): YulASTNode | undefined; + get nextSibling(): YulASTNode | undefined; + + parent?: YulASTNode | InlineAssembly; + + walk(callback: YulASTNodeCallback): void; + walkChildren(callback: YulASTNodeCallback): void; + + getChildren(inclusive?: boolean): YulASTNode[]; + getChildrenBySelector( + selector: YulASTNodeSelector, + inclusive?: boolean + ): T[]; + getChildrenByType( + type: YulASTNodeConstructor, + inclusive?: boolean + ): T[]; + getChildrenByTypeString(typeString: string, inclusive?: boolean): T[]; +} + +export class YulASTNodeWithChildren + extends ASTNodeWithChildren + implements YulASTNode +{ + protected ownChildren: YulASTNode[] = []; + + get children(): readonly YulASTNode[] { + return this.ownChildren; + } + + removeChild(node: T): T { + const index = this.ownChildren.indexOf(node); + + if (index === -1) { + throw new Error("Reference node is not a child of current node"); + } + + this.ownChildren.splice(index, 1); + + node.parent = undefined; + + return node; + } + + appendChild(node: T): T { + this.ownChildren.push(node); + + node.parent = this; + + return node; + } + + insertBefore(node: T, referenceNode: YulASTNode): T { + const index = this.ownChildren.indexOf(referenceNode); + + if (index === -1) { + throw new Error("Reference node is not a child of current node"); + } + + this.ownChildren.splice(index, 0, node); + + node.parent = this; + + return node; + } + + insertAfter(node: T, referenceNode: YulASTNode): T { + if (this.ownChildren.indexOf(referenceNode) === -1) { + throw new Error("Reference node is not a child of current node"); + } + + const sibling = referenceNode.nextSibling; + + return sibling ? this.insertBefore(node, sibling) : this.appendChild(node); + } + + insertAtBeginning(node: T): T { + const firstChild = this.firstChild; + + return firstChild ? this.insertBefore(node, firstChild) : this.appendChild(node); + } + + replaceChild(newNode: N, oldNode: O): O { + const index = this.ownChildren.indexOf(oldNode); + + if (index === -1) { + throw new Error("Old node is not a child of current node"); + } + + this.ownChildren.splice(index, 1, newNode); + + newNode.parent = this; + oldNode.parent = undefined; + + return oldNode; + } +} + +export type YulASTNodeConstructor = new ( + id: number, + src: string, + ...args: any[] +) => T; + +export type YulASTNodeCallback = (node: YulASTNode) => void; +export type YulASTNodeSelector = (node: YulASTNode) => boolean; diff --git a/src/ast/index.ts b/src/ast/index.ts index cfd9140e..f17da6a1 100644 --- a/src/ast/index.ts +++ b/src/ast/index.ts @@ -3,6 +3,7 @@ export * from "./implementation/expression"; export * from "./implementation/meta"; export * from "./implementation/statement"; export * from "./implementation/type"; +export * from "./implementation/yul"; export * from "./ast_node_factory"; export * from "./ast_node_formatter"; export * from "./ast_node"; diff --git a/src/ast/modern/configuration.ts b/src/ast/modern/configuration.ts index 04bb4253..d4b89e24 100644 --- a/src/ast/modern/configuration.ts +++ b/src/ast/modern/configuration.ts @@ -57,6 +57,24 @@ import { ElementaryTypeName } from "../implementation/type/elementary_type_name" import { FunctionTypeName } from "../implementation/type/function_type_name"; import { Mapping } from "../implementation/type/mapping"; import { UserDefinedTypeName } from "../implementation/type/user_defined_type_name"; +import { + YulAssignment, + YulBlock, + YulBreak, + YulCase, + YulContinue, + YulExpressionStatement, + YulFunctionCall, + YulFunctionDefinition, + YulForLoop, + YulIdentifier, + YulIf, + YulLeave, + YulLiteral, + YulSwitch, + YulTypedName, + YulVariableDeclaration +} from "../implementation/yul"; import { ModernArrayTypeNameProcessor } from "./array_type_name_processor"; import { ModernAssignmentProcessor } from "./assignment_processor"; import { ModernBinaryOperationProcessor } from "./binary_operation_processor"; @@ -116,6 +134,23 @@ import { ModernVariableDeclarationProcessor } from "./variable_declaration_proce import { ModernVariableDeclarationStatementProcessor } from "./variable_declaration_statement_processor"; import { ModernWhileStatementProcessor } from "./while_statement_processor"; +import { ModernYulAssignmentProcessor } from "./yul_assignment_processor"; +import { ModernYulBlockProcessor } from "./yul_block_processor"; +import { ModernYulBreakProcessor } from "./yul_break_processor"; +import { ModernYulCaseProcessor } from "./yul_case_processor"; +import { ModernYulContinueProcessor } from "./yul_continue_processor"; +import { ModernYulExpressionStatementProcessor } from "./yul_expression_statement_processor"; +import { ModernYulForLoopProcessor } from "./yul_for_loop_processor"; +import { ModernYulFunctionCallProcessor } from "./yul_function_call_processor"; +import { ModernYulFunctionDefinitionProcessor } from "./yul_function_definition_processor"; +import { ModernYulIdentifierProcessor } from "./yul_identifier_processor"; +import { ModernYulIfProcessor } from "./yul_if_processor"; +import { ModernYulLeaveProcessor } from "./yul_leave_processor"; +import { ModernYulLiteralProcessor } from "./yul_literal_processor"; +import { ModernYulSwitchProcessor } from "./yul_switch_processor"; +import { ModernYulTypedNameProcessor } from "./yul_typed_name_processor"; +import { ModernYulVariableDeclarationProcessor } from "./yul_variable_declaration_processor"; + const processors = { /** * For any non-existent nodes: @@ -181,7 +216,23 @@ const processors = { Continue: new ModernContinueProcessor(), PlaceholderStatement: new ModernPlaceholderStatementProcessor(), Throw: new ModernThrowProcessor(), - UserDefinedValueTypeDefinition: new ModernUserDefinedValueTypeDefinitionProcessor() + UserDefinedValueTypeDefinition: new ModernUserDefinedValueTypeDefinitionProcessor(), + YulAssignment: new ModernYulAssignmentProcessor(), + YulBlock: new ModernYulBlockProcessor(), + YulBreak: new ModernYulBreakProcessor(), + YulCase: new ModernYulCaseProcessor(), + YulContinue: new ModernYulContinueProcessor(), + YulExpressionStatement: new ModernYulExpressionStatementProcessor(), + YulFunctionCall: new ModernYulFunctionCallProcessor(), + YulFunctionDefinition: new ModernYulFunctionDefinitionProcessor(), + YulForLoop: new ModernYulForLoopProcessor(), + YulIdentifier: new ModernYulIdentifierProcessor(), + YulIf: new ModernYulIfProcessor(), + YulLeave: new ModernYulLeaveProcessor(), + YulLiteral: new ModernYulLiteralProcessor(), + YulSwitch: new ModernYulSwitchProcessor(), + YulTypedName: new ModernYulTypedNameProcessor(), + YulVariableDeclaration: new ModernYulVariableDeclarationProcessor() }; export const ModernConfiguration: ASTReaderConfiguration = { @@ -476,6 +527,86 @@ export const ModernConfiguration: ASTReaderConfiguration = { UserDefinedValueTypeDefinition: { constructor: UserDefinedValueTypeDefinition, processor: processors.UserDefinedValueTypeDefinition + }, + + YulAssignment: { + constructor: YulAssignment, + processor: processors.YulAssignment + }, + + YulBlock: { + constructor: YulBlock, + processor: processors.YulBlock + }, + + YulBreak: { + constructor: YulBreak, + processor: processors.YulBreak + }, + + YulCase: { + constructor: YulCase, + processor: processors.YulCase + }, + + YulContinue: { + constructor: YulContinue, + processor: processors.YulContinue + }, + + YulExpressionStatement: { + constructor: YulExpressionStatement, + processor: processors.YulExpressionStatement + }, + + YulFunctionCall: { + constructor: YulFunctionCall, + processor: processors.YulFunctionCall + }, + + YulFunctionDefinition: { + constructor: YulFunctionDefinition, + processor: processors.YulFunctionDefinition + }, + + YulForLoop: { + constructor: YulForLoop, + processor: processors.YulForLoop + }, + + YulIdentifier: { + constructor: YulIdentifier, + processor: processors.YulIdentifier + }, + + YulIf: { + constructor: YulIf, + processor: processors.YulIf + }, + + YulLeave: { + constructor: YulLeave, + processor: processors.YulLeave + }, + + YulLiteral: { + constructor: YulLiteral, + processor: processors.YulLiteral + }, + + YulSwitch: { + constructor: YulSwitch, + processor: processors.YulSwitch + }, + + YulTypedName: { + constructor: YulTypedName, + processor: processors.YulTypedName + }, + + YulVariableDeclaration: { + constructor: YulVariableDeclaration, + processor: processors.YulVariableDeclaration } } }; diff --git a/src/ast/modern/inline_assembly_processor.ts b/src/ast/modern/inline_assembly_processor.ts index 33b9b588..ecabd6a2 100644 --- a/src/ast/modern/inline_assembly_processor.ts +++ b/src/ast/modern/inline_assembly_processor.ts @@ -1,5 +1,6 @@ import { ASTReader, ASTReaderConfiguration } from "../ast_reader"; -import { InlineAssembly, YulNode } from "../implementation/statement/inline_assembly"; +import { InlineAssembly } from "../implementation/statement/inline_assembly"; +import { YulBlock } from "../implementation/yul"; import { ModernNodeProcessor } from "./node_processor"; export class ModernInlineAssemblyProcessor extends ModernNodeProcessor { @@ -13,7 +14,7 @@ export class ModernInlineAssemblyProcessor extends ModernNodeProcessor { const kind: LiteralKind = raw.kind; const hexValue: string = raw.hexValue; - const value: string = raw.value === undefined ? null : raw.value; + const value: string = raw.value ?? null; const subdenomination: TimeUnit | EtherUnit | undefined = raw.subdenomination ? raw.subdenomination : undefined; diff --git a/src/ast/modern/yul_assignment_processor.ts b/src/ast/modern/yul_assignment_processor.ts new file mode 100644 index 00000000..cd37d180 --- /dev/null +++ b/src/ast/modern/yul_assignment_processor.ts @@ -0,0 +1,18 @@ +import { ASTReader, ASTReaderConfiguration } from "../ast_reader"; +import { YulAssignment, YulExpression, YulIdentifier } from "../implementation/yul"; +import { ModernNodeProcessor } from "./node_processor"; + +export class ModernYulAssignmentProcessor extends ModernNodeProcessor { + process( + reader: ASTReader, + config: ASTReaderConfiguration, + raw: any + ): ConstructorParameters { + const [id, src] = super.process(reader, config, raw); + + const variableNames = reader.convertArray(raw.variableNames, config) as YulIdentifier[]; + const value = reader.convert(raw.value, config) as YulExpression; + + return [id, src, variableNames, value, undefined]; + } +} diff --git a/src/ast/modern/yul_block_processor.ts b/src/ast/modern/yul_block_processor.ts new file mode 100644 index 00000000..f74c63c2 --- /dev/null +++ b/src/ast/modern/yul_block_processor.ts @@ -0,0 +1,17 @@ +import { ASTReader, ASTReaderConfiguration } from "../ast_reader"; +import { YulBlock, YulStatement } from "../implementation/yul"; +import { ModernNodeProcessor } from "./node_processor"; + +export class ModernYulBlockProcessor extends ModernNodeProcessor { + process( + reader: ASTReader, + config: ASTReaderConfiguration, + raw: any + ): ConstructorParameters { + const [id, src] = super.process(reader, config, raw); + + const statements = reader.convertArray(raw.statements, config) as YulStatement[]; + + return [id, src, statements, undefined, raw]; + } +} diff --git a/src/ast/modern/yul_break_processor.ts b/src/ast/modern/yul_break_processor.ts new file mode 100644 index 00000000..129bbe4b --- /dev/null +++ b/src/ast/modern/yul_break_processor.ts @@ -0,0 +1,15 @@ +import { ASTReader, ASTReaderConfiguration } from "../ast_reader"; +import { YulBreak } from "../implementation/yul"; +import { ModernNodeProcessor } from "./node_processor"; + +export class ModernYulBreakProcessor extends ModernNodeProcessor { + process( + reader: ASTReader, + config: ASTReaderConfiguration, + raw: any + ): ConstructorParameters { + const [id, src] = super.process(reader, config, raw); + + return [id, src, undefined, raw]; + } +} diff --git a/src/ast/modern/yul_case_processor.ts b/src/ast/modern/yul_case_processor.ts new file mode 100644 index 00000000..ad7677bf --- /dev/null +++ b/src/ast/modern/yul_case_processor.ts @@ -0,0 +1,20 @@ +import { ASTReader, ASTReaderConfiguration } from "../ast_reader"; +import { YulBlock, YulCase, YulLiteral } from "../implementation/yul"; +import { ModernNodeProcessor } from "./node_processor"; + +export class ModernYulCaseProcessor extends ModernNodeProcessor { + process( + reader: ASTReader, + config: ASTReaderConfiguration, + raw: any + ): ConstructorParameters { + const [id, src] = super.process(reader, config, raw); + + const value = + raw.value === "default" ? raw.value : (reader.convert(raw.value, config) as YulLiteral); + + const body = reader.convert(raw.body, config) as YulBlock; + + return [id, src, value, body, undefined, raw]; + } +} diff --git a/src/ast/modern/yul_continue_processor.ts b/src/ast/modern/yul_continue_processor.ts new file mode 100644 index 00000000..53c69e95 --- /dev/null +++ b/src/ast/modern/yul_continue_processor.ts @@ -0,0 +1,15 @@ +import { ASTReader, ASTReaderConfiguration } from "../ast_reader"; +import { YulContinue } from "../implementation/yul"; +import { ModernNodeProcessor } from "./node_processor"; + +export class ModernYulContinueProcessor extends ModernNodeProcessor { + process( + reader: ASTReader, + config: ASTReaderConfiguration, + raw: any + ): ConstructorParameters { + const [id, src] = super.process(reader, config, raw); + + return [id, src, undefined, raw]; + } +} diff --git a/src/ast/modern/yul_expression_statement_processor.ts b/src/ast/modern/yul_expression_statement_processor.ts new file mode 100644 index 00000000..ed271649 --- /dev/null +++ b/src/ast/modern/yul_expression_statement_processor.ts @@ -0,0 +1,17 @@ +import { ASTReader, ASTReaderConfiguration } from "../ast_reader"; +import { YulExpression, YulExpressionStatement } from "../implementation/yul"; +import { ModernNodeProcessor } from "./node_processor"; + +export class ModernYulExpressionStatementProcessor extends ModernNodeProcessor { + process( + reader: ASTReader, + config: ASTReaderConfiguration, + raw: any + ): ConstructorParameters { + const [id, src] = super.process(reader, config, raw); + + const expression = reader.convert(raw.expression, config) as YulExpression; + + return [id, src, expression, undefined, raw]; + } +} diff --git a/src/ast/modern/yul_for_loop_processor.ts b/src/ast/modern/yul_for_loop_processor.ts new file mode 100644 index 00000000..201323df --- /dev/null +++ b/src/ast/modern/yul_for_loop_processor.ts @@ -0,0 +1,20 @@ +import { ASTReader, ASTReaderConfiguration } from "../ast_reader"; +import { YulBlock, YulExpression, YulForLoop } from "../implementation/yul"; +import { ModernNodeProcessor } from "./node_processor"; + +export class ModernYulForLoopProcessor extends ModernNodeProcessor { + process( + reader: ASTReader, + config: ASTReaderConfiguration, + raw: any + ): ConstructorParameters { + const [id, src] = super.process(reader, config, raw); + + const pre = reader.convert(raw.pre, config) as YulBlock; + const condition = reader.convert(raw.condition, config) as YulExpression; + const post = reader.convert(raw.post, config) as YulBlock; + const body = reader.convert(raw.body, config) as YulBlock; + + return [id, src, pre, condition, post, body, undefined, raw]; + } +} diff --git a/src/ast/modern/yul_function_call_processor.ts b/src/ast/modern/yul_function_call_processor.ts new file mode 100644 index 00000000..e992b8a5 --- /dev/null +++ b/src/ast/modern/yul_function_call_processor.ts @@ -0,0 +1,18 @@ +import { ASTReader, ASTReaderConfiguration } from "../ast_reader"; +import { YulExpression, YulFunctionCall, YulIdentifier } from "../implementation/yul"; +import { ModernNodeProcessor } from "./node_processor"; + +export class ModernYulFunctionCallProcessor extends ModernNodeProcessor { + process( + reader: ASTReader, + config: ASTReaderConfiguration, + raw: any + ): ConstructorParameters { + const [id, src] = super.process(reader, config, raw); + + const functionName = reader.convert(raw.functionName, config) as YulIdentifier; + const args = reader.convertArray(raw.arguments, config) as YulExpression[]; + + return [id, src, functionName, args, raw]; + } +} diff --git a/src/ast/modern/yul_function_definition_processor.ts b/src/ast/modern/yul_function_definition_processor.ts new file mode 100644 index 00000000..08ab9f96 --- /dev/null +++ b/src/ast/modern/yul_function_definition_processor.ts @@ -0,0 +1,22 @@ +import { ASTReader, ASTReaderConfiguration } from "../ast_reader"; +import { YulBlock, YulFunctionDefinition, YulTypedName } from "../implementation/yul"; +import { ModernNodeProcessor } from "./node_processor"; + +export class ModernYulFunctionDefinitionProcessor extends ModernNodeProcessor { + process( + reader: ASTReader, + config: ASTReaderConfiguration, + raw: any + ): ConstructorParameters { + const [id, src] = super.process(reader, config, raw); + + const parameters = reader.convertArray(raw.parameters, config) as YulTypedName[]; + const returnParameters = reader.convertArray( + raw.returnParameters, + config + ) as YulTypedName[]; + const body = reader.convert(raw.body, config) as YulBlock; + + return [id, src, -1, raw.name, parameters, returnParameters, body, undefined, raw]; + } +} diff --git a/src/ast/modern/yul_identifier_processor.ts b/src/ast/modern/yul_identifier_processor.ts new file mode 100644 index 00000000..9cf30a07 --- /dev/null +++ b/src/ast/modern/yul_identifier_processor.ts @@ -0,0 +1,16 @@ +import { ASTReader, ASTReaderConfiguration } from "../ast_reader"; +import { YulIdentifier } from "../implementation/yul"; +import { ModernNodeProcessor } from "./node_processor"; + +export class ModernYulIdentifierProcessor extends ModernNodeProcessor { + process( + reader: ASTReader, + config: ASTReaderConfiguration, + raw: any + ): ConstructorParameters { + const [id, src] = super.process(reader, config, raw); + const name: string = raw.name; + + return [id, src, name, -1, raw]; + } +} diff --git a/src/ast/modern/yul_if_processor.ts b/src/ast/modern/yul_if_processor.ts new file mode 100644 index 00000000..7ee931b0 --- /dev/null +++ b/src/ast/modern/yul_if_processor.ts @@ -0,0 +1,18 @@ +import { ASTReader, ASTReaderConfiguration } from "../ast_reader"; +import { YulBlock, YulExpression, YulIf } from "../implementation/yul"; +import { ModernNodeProcessor } from "./node_processor"; + +export class ModernYulIfProcessor extends ModernNodeProcessor { + process( + reader: ASTReader, + config: ASTReaderConfiguration, + raw: any + ): ConstructorParameters { + const [id, src] = super.process(reader, config, raw); + + const condition = reader.convert(raw.condition, config) as YulExpression; + const body = reader.convert(raw.body, config) as YulBlock; + + return [id, src, condition, body, undefined, raw]; + } +} diff --git a/src/ast/modern/yul_leave_processor.ts b/src/ast/modern/yul_leave_processor.ts new file mode 100644 index 00000000..3a096494 --- /dev/null +++ b/src/ast/modern/yul_leave_processor.ts @@ -0,0 +1,15 @@ +import { ASTReader, ASTReaderConfiguration } from "../ast_reader"; +import { YulLeave } from "../implementation/yul"; +import { ModernNodeProcessor } from "./node_processor"; + +export class ModernYulLeaveProcessor extends ModernNodeProcessor { + process( + reader: ASTReader, + config: ASTReaderConfiguration, + raw: any + ): ConstructorParameters { + const [id, src] = super.process(reader, config, raw); + + return [id, src, undefined, raw]; + } +} diff --git a/src/ast/modern/yul_literal_processor.ts b/src/ast/modern/yul_literal_processor.ts new file mode 100644 index 00000000..00ca8fc8 --- /dev/null +++ b/src/ast/modern/yul_literal_processor.ts @@ -0,0 +1,20 @@ +import { ASTReader, ASTReaderConfiguration } from "../ast_reader"; +import { YulLiteralKind } from "../constants"; +import { YulLiteral } from "../implementation/yul"; +import { ModernNodeProcessor } from "./node_processor"; + +export class ModernYulLiteralProcessor extends ModernNodeProcessor { + process( + reader: ASTReader, + config: ASTReaderConfiguration, + raw: any + ): ConstructorParameters { + const [id, src] = super.process(reader, config, raw); + const kind: YulLiteralKind = raw.kind; + const value: string = raw.value === undefined ? null : raw.value; + const hexValue: string = raw.hexValue; + const typeString: string = raw.type; + + return [id, src, kind, value, hexValue, typeString, raw]; + } +} diff --git a/src/ast/modern/yul_switch_processor.ts b/src/ast/modern/yul_switch_processor.ts new file mode 100644 index 00000000..9b3aa2a2 --- /dev/null +++ b/src/ast/modern/yul_switch_processor.ts @@ -0,0 +1,18 @@ +import { ASTReader, ASTReaderConfiguration } from "../ast_reader"; +import { YulCase, YulExpression, YulSwitch } from "../implementation/yul"; +import { ModernNodeProcessor } from "./node_processor"; + +export class ModernYulSwitchProcessor extends ModernNodeProcessor { + process( + reader: ASTReader, + config: ASTReaderConfiguration, + raw: any + ): ConstructorParameters { + const [id, src] = super.process(reader, config, raw); + + const expression = reader.convert(raw.expression, config) as YulExpression; + const cases = reader.convertArray(raw.cases, config) as YulCase[]; + + return [id, src, expression, cases, undefined, raw]; + } +} diff --git a/src/ast/modern/yul_typed_name_processor.ts b/src/ast/modern/yul_typed_name_processor.ts new file mode 100644 index 00000000..63403b4c --- /dev/null +++ b/src/ast/modern/yul_typed_name_processor.ts @@ -0,0 +1,17 @@ +import { ASTReader, ASTReaderConfiguration } from "../ast_reader"; +import { YulTypedName } from "../implementation/yul"; +import { ModernNodeProcessor } from "./node_processor"; + +export class ModernYulTypedNameProcessor extends ModernNodeProcessor { + process( + reader: ASTReader, + config: ASTReaderConfiguration, + raw: any + ): ConstructorParameters { + const [id, src] = super.process(reader, config, raw); + const name: string = raw.name; + const typeString: string = raw.type; + + return [id, src, name, typeString, raw]; + } +} diff --git a/src/ast/modern/yul_variable_declaration_processor.ts b/src/ast/modern/yul_variable_declaration_processor.ts new file mode 100644 index 00000000..9d1d920d --- /dev/null +++ b/src/ast/modern/yul_variable_declaration_processor.ts @@ -0,0 +1,18 @@ +import { ASTReader, ASTReaderConfiguration } from "../ast_reader"; +import { YulExpression, YulTypedName, YulVariableDeclaration } from "../implementation/yul"; +import { ModernNodeProcessor } from "./node_processor"; + +export class ModernYulVariableDeclarationProcessor extends ModernNodeProcessor { + process( + reader: ASTReader, + config: ASTReaderConfiguration, + raw: any + ): ConstructorParameters { + const [id, src] = super.process(reader, config, raw); + + const variables = reader.convertArray(raw.variables, config) as YulTypedName[]; + const value = raw.value && (reader.convert(raw.value, config) as YulExpression); + + return [id, src, variables, value, undefined, raw]; + } +} diff --git a/src/ast/writing/ast_mapping.ts b/src/ast/writing/ast_mapping.ts index 3ac0f11b..05c33d32 100644 --- a/src/ast/writing/ast_mapping.ts +++ b/src/ast/writing/ast_mapping.ts @@ -6,8 +6,27 @@ import { FunctionStateMutability, LiteralKind, Mutability, - StateVariableVisibility + StateVariableVisibility, + YulLiteralKind } from "../constants"; +import { + YulAssignment, + YulBlock, + YulBreak, + YulCase, + YulContinue, + YulExpressionStatement, + YulForLoop, + YulFunctionCall, + YulFunctionDefinition, + YulIdentifier, + YulIf, + YulLeave, + YulLiteral, + YulSwitch, + YulTypedName, + YulVariableDeclaration +} from "../implementation/yul"; import { ContractDefinition, EnumDefinition, @@ -77,8 +96,7 @@ import { UserDefinedTypeName } from "../implementation/type"; import { SourceFormatter } from "./formatter"; -import { ASTNodeWriter, ASTWriter, DescArgs, SrcDesc, YulWriter } from "./writer"; -import { DefaultYulWriterMapping } from "./yul_mapping"; +import { ASTNodeWriter, ASTWriter, DescArgs, SrcDesc } from "./writer"; type CompoundStatement = IfStatement | ForStatement | WhileStatement; @@ -724,7 +742,7 @@ class PlaceholderStatementWriter extends SimpleStatementWriter `"${flag}"`); @@ -735,9 +753,7 @@ class InlineAssemblyWriter extends ASTNodeWriter { if (node.operations !== undefined) { result.push(node.operations); } else if (node.yul !== undefined) { - const yulWriter = new YulWriter(DefaultYulWriterMapping, writer.formatter); - - result.push(yulWriter.write(node.yul)); + return writer.desc(...result, node.yul); } else { throw new Error("Unable to detect Yul data in inline assembly node: " + node.print()); } @@ -1410,6 +1426,264 @@ class SourceUnitWriter extends ASTNodeWriter { } } +class YulBlockWriter extends ASTNodeWriter { + writeInner(node: YulBlock, writer: ASTWriter): SrcDesc { + if ( + node.children.length === 0 || + (node.children.length === 1 && node.documentation === node.firstChild) + ) { + return ["{}"]; + } + + const formatter = writer.formatter; + const wrap = formatter.renderWrap(); + const oldIndent = formatter.renderIndent(); + + formatter.increaseNesting(); + + const doc = node.documentation; + const nested = node.children.filter((node) => node !== doc); + + const res: SrcDesc = [ + "{", + wrap, + ...flatJoin( + nested.map((stmt) => [formatter.renderIndent(), ...writer.desc(stmt)]), + wrap + ), + wrap, + oldIndent, + "}" + ]; + + formatter.decreaseNesting(); + + return res; + } + + writeWhole(node: YulBlock, writer: ASTWriter): SrcDesc { + return [ + ...writePrecedingDocs(node.documentation, writer), + [node, this.writeInner(node, writer)] + ]; + } +} + +class YulLiteralWriter extends ASTNodeWriter { + writeInner(node: YulLiteral): SrcDesc { + let result: string; + if (node.kind === YulLiteralKind.String) { + result = + node.value === undefined ? `hex"${node.hexValue}"` : JSON.stringify(node.value); + } else { + result = node.value; + } + + if (node.typeString) result = `${result}:${node.typeString}`; + + return [result]; + } +} + +class YulIdentifierWriter extends ASTNodeWriter { + writeInner(node: YulIdentifier): SrcDesc { + return [node.name]; + } +} +class YulTypedNameWriter extends ASTNodeWriter { + writeInner(node: YulTypedName): SrcDesc { + return [node.typeString ? node.name + ":" + node.typeString : node.name]; + } +} +class YulFunctionCallWriter extends ASTNodeWriter { + writeInner(node: YulFunctionCall, writer: ASTWriter): SrcDesc { + const elements: DescArgs = [node.vFunctionName, "("]; + + const args = node.vArguments; + + elements.push(...join(args, ", ")); + + elements.push(")"); + + return writer.desc(...elements); + } +} + +class YulVariableDeclarationWriter extends ASTNodeWriter { + writeInner(node: YulVariableDeclaration, writer: ASTWriter): SrcDesc { + const elements: DescArgs = ["let ", ...join(node.variables, ",")]; + if (node.value) { + elements.push(` := `, node.value); + } + + return writer.desc(...elements); + } +} + +class YulExpressionStatementWriter extends ASTNodeWriter { + writeInner(node: YulExpressionStatement, writer: ASTWriter): SrcDesc { + return writer.desc(node.vExpression); + } + + writeWhole(node: YulExpressionStatement, writer: ASTWriter): SrcDesc { + return [ + ...writePrecedingDocs(node.documentation, writer), + [node, this.writeInner(node, writer)] + ]; + } +} + +class YulAssignmentWriter implements ASTNodeWriter { + writeInner(node: YulAssignment, writer: ASTWriter): SrcDesc { + const elements: DescArgs = ["let ", ...join(node.variableNames, ","), ` := `, node.value]; + + return writer.desc(...elements); + } + + writeWhole(node: YulAssignment, writer: ASTWriter): SrcDesc { + return [ + ...writePrecedingDocs(node.documentation, writer), + [node, this.writeInner(node, writer)] + ]; + } +} + +class YulIfWriter implements ASTNodeWriter { + writeInner(node: YulIf, writer: ASTWriter): SrcDesc { + return writer.desc("if ", node.vCondition, " ", node.vBody); + } + + writeWhole(node: YulIf, writer: ASTWriter): SrcDesc { + return [ + ...writePrecedingDocs(node.documentation, writer), + [node, this.writeInner(node, writer)] + ]; + } +} + +class YulCaseWriter implements ASTNodeWriter { + writeInner(node: YulCase, writer: ASTWriter): SrcDesc { + return writer.desc( + ...(node.value === "default" ? ["default"] : ["case ", node.value]), + " ", + node.vBody + ); + } + + writeWhole(node: YulCase, writer: ASTWriter): SrcDesc { + return [ + ...writePrecedingDocs(node.documentation, writer), + [node, this.writeInner(node, writer)] + ]; + } +} + +class YulSwitchWriter implements ASTNodeWriter { + writeInner(node: YulSwitch, writer: ASTWriter): SrcDesc { + const formatter = writer.formatter; + const wrap = formatter.renderWrap(); + + return [ + "switch ", + ...writer.desc(node.vExpression), + wrap, + ...flatJoin( + node.vCases.map((vCase) => [ + formatter.renderIndent(), + ...writer.desc(vCase) + ]), + wrap + ) + ]; + } + + writeWhole(node: YulSwitch, writer: ASTWriter): SrcDesc { + return [ + ...writePrecedingDocs(node.documentation, writer), + [node, this.writeInner(node, writer)] + ]; + } +} + +class YulContinueWriter implements ASTNodeWriter { + writeInner(): SrcDesc { + return ["continue"]; + } + + writeWhole(node: YulContinue, writer: ASTWriter): SrcDesc { + return [...writePrecedingDocs(node.documentation, writer), [node, this.writeInner()]]; + } +} + +class YulBreakWriter implements ASTNodeWriter { + writeInner(): SrcDesc { + return ["break"]; + } + + writeWhole(node: YulBreak, writer: ASTWriter): SrcDesc { + return [...writePrecedingDocs(node.documentation, writer), [node, this.writeInner()]]; + } +} + +class YulLeaveWriter implements ASTNodeWriter { + writeInner(): SrcDesc { + return ["leave"]; + } + + writeWhole(node: YulLeave, writer: ASTWriter): SrcDesc { + return [...writePrecedingDocs(node.documentation, writer), [node, this.writeInner()]]; + } +} + +class YulForLoopWriter implements ASTNodeWriter { + writeInner(node: YulForLoop, writer: ASTWriter): SrcDesc { + // return `for ${pre} ${condition} ${post} ${body}`; + return writer.desc( + "for ", + ...join([node.vPre, node.vCondition, node.vPost, node.vBody], " ") + ); + } + + writeWhole(node: YulForLoop, writer: ASTWriter): SrcDesc { + return [ + ...writePrecedingDocs(node.documentation, writer), + [node, this.writeInner(node, writer)] + ]; + } +} +class YulFunctionDefinitionWriter implements ASTNodeWriter { + getHeader(node: YulFunctionDefinition): DescArgs { + const result: DescArgs = [ + "function ", + node.name, + " (", + ...join(node.vParameters, ", "), + ")" + ]; + if (node.vReturnParameters.length) { + result.push(" -> ", ...join(node.vParameters, ", ")); + } + return result; + } + + writeInner(node: YulFunctionDefinition, writer: ASTWriter): SrcDesc { + const args = this.getHeader(node); + + const result = writer.desc(...args); + + result.push(" ", ...writer.desc(node.vBody)); + + return result; + } + + writeWhole(node: YulFunctionDefinition, writer: ASTWriter): SrcDesc { + return [ + ...writePrecedingDocs(node.documentation, writer), + [node, this.writeInner(node, writer)] + ]; + } +} + export const DefaultASTWriterMapping = new Map, ASTNodeWriter>([ [ElementaryTypeName, new ElementaryTypeNameWriter()], [ArrayTypeName, new ArrayTypeNameWriter()], @@ -1467,5 +1741,21 @@ export const DefaultASTWriterMapping = new Map, ASTN [StructuredDocumentation, new StructuredDocumentationWriter()], [ImportDirective, new ImportDirectiveWriter()], [PragmaDirective, new PragmaDirectiveWriter()], - [SourceUnit, new SourceUnitWriter()] + [SourceUnit, new SourceUnitWriter()], + [YulBlock, new YulBlockWriter()], + [YulLiteral, new YulLiteralWriter()], + [YulIdentifier, new YulIdentifierWriter()], + [YulTypedName, new YulTypedNameWriter()], + [YulFunctionCall, new YulFunctionCallWriter()], + [YulVariableDeclaration, new YulVariableDeclarationWriter()], + [YulExpressionStatement, new YulExpressionStatementWriter()], + [YulAssignment, new YulAssignmentWriter()], + [YulIf, new YulIfWriter()], + [YulCase, new YulCaseWriter()], + [YulSwitch, new YulSwitchWriter()], + [YulContinue, new YulContinueWriter()], + [YulBreak, new YulBreakWriter()], + [YulLeave, new YulLeaveWriter()], + [YulForLoop, new YulForLoopWriter()], + [YulFunctionDefinition, new YulFunctionDefinitionWriter()] ]); diff --git a/src/ast/writing/writer.ts b/src/ast/writing/writer.ts index d6017ec6..25153c63 100644 --- a/src/ast/writing/writer.ts +++ b/src/ast/writing/writer.ts @@ -2,6 +2,7 @@ import { ASTNode, ASTNodeConstructor } from "../ast_node"; import { YulNode } from "../implementation/statement/inline_assembly"; import { SourceFormatter } from "./formatter"; +/** @deprecated Use `ASTWriter` */ export interface YulNodeWriter { write(node: YulNode, writer: YulWriter): string; } @@ -66,6 +67,7 @@ export abstract class ASTNodeWriter { } } +/** @deprecated Use `ASTWriter` */ export class YulWriter { mapping: Map; formatter: SourceFormatter; diff --git a/src/ast/writing/yul_mapping.ts b/src/ast/writing/yul_mapping.ts index 5ca6622a..d6883af5 100644 --- a/src/ast/writing/yul_mapping.ts +++ b/src/ast/writing/yul_mapping.ts @@ -1,6 +1,7 @@ import { YulNode } from "../implementation/statement/inline_assembly"; import { YulNodeWriter, YulWriter } from "./writer"; +/** @deprecated Use `ASTWriter` */ class YulBlockWriter implements YulNodeWriter { write(node: YulNode, writer: YulWriter): string { if (node.statements.length === 0) { @@ -24,6 +25,7 @@ class YulBlockWriter implements YulNodeWriter { } } +/** @deprecated Use `ASTWriter` */ class YulLiteralWriter implements YulNodeWriter { write(node: YulNode): string { let result; @@ -44,18 +46,21 @@ class YulLiteralWriter implements YulNodeWriter { } } +/** @deprecated Use `ASTWriter` */ class YulIdentifierWriter implements YulNodeWriter { write(node: YulNode): string { return node.name; } } +/** @deprecated Use `ASTWriter` */ class YulTypedNameWriter implements YulNodeWriter { write(node: YulNode): string { return node.type !== "" ? node.name + ":" + node.type : node.name; } } +/** @deprecated Use `ASTWriter` */ class YulFunctionCallWriter implements YulNodeWriter { write(node: YulNode, writer: YulWriter): string { const id = writer.write(node.functionName); @@ -65,6 +70,7 @@ class YulFunctionCallWriter implements YulNodeWriter { } } +/** @deprecated Use `ASTWriter` */ class YulVariableDeclarationWriter implements YulNodeWriter { write(node: YulNode, writer: YulWriter): string { const vars = node.variables.map((v: YulNode) => writer.write(v)); @@ -75,12 +81,14 @@ class YulVariableDeclarationWriter implements YulNodeWriter { } } +/** @deprecated Use `ASTWriter` */ class YulExpressionStatementWriter implements YulNodeWriter { write(node: YulNode, writer: YulWriter): string { return writer.write(node.expression); } } +/** @deprecated Use `ASTWriter` */ class YulAssignmentWriter implements YulNodeWriter { write(node: YulNode, writer: YulWriter): string { const lhs = node.variableNames.map((v: YulNode) => writer.write(v)); @@ -90,6 +98,7 @@ class YulAssignmentWriter implements YulNodeWriter { } } +/** @deprecated Use `ASTWriter` */ class YulIfWriter implements YulNodeWriter { write(node: YulNode, writer: YulWriter): string { const condition = writer.write(node.condition); @@ -99,6 +108,7 @@ class YulIfWriter implements YulNodeWriter { } } +/** @deprecated Use `ASTWriter` */ class YulCaseWriter implements YulNodeWriter { write(node: YulNode, writer: YulWriter): string { const body = writer.write(node.body); @@ -113,6 +123,7 @@ class YulCaseWriter implements YulNodeWriter { } } +/** @deprecated Use `ASTWriter` */ class YulSwitchWriter implements YulNodeWriter { write(node: YulNode, writer: YulWriter): string { const expression = writer.write(node.expression); @@ -129,24 +140,28 @@ class YulSwitchWriter implements YulNodeWriter { } } +/** @deprecated Use `ASTWriter` */ class YulContinueWriter implements YulNodeWriter { write(): string { return "continue"; } } +/** @deprecated Use `ASTWriter` */ class YulBreakWriter implements YulNodeWriter { write(): string { return "break"; } } +/** @deprecated Use `ASTWriter` */ class YulLeaveWriter implements YulNodeWriter { write(): string { return "leave"; } } +/** @deprecated Use `ASTWriter` */ class YulForLoopWriter implements YulNodeWriter { write(node: YulNode, writer: YulWriter): string { const pre = writer.write(node.pre); @@ -158,6 +173,7 @@ class YulForLoopWriter implements YulNodeWriter { } } +/** @deprecated Use `ASTWriter` */ class YulFunctionDefinitionWriter implements YulNodeWriter { write(node: YulNode, writer: YulWriter): string { const args = node.parameters diff --git a/src/misc/utils.ts b/src/misc/utils.ts index f04dfb8c..08def604 100644 --- a/src/misc/utils.ts +++ b/src/misc/utils.ts @@ -55,3 +55,45 @@ export function assert( throw new Error(message); } + +/** + * Recursively search all values in an object or array. + * @param obj Object to search recursively + * @param matchKey Key an object must have to be matched + * @param cb Callback function to check objects with a matching key. + * If no callback provided, collects all properties at `matchKey` anywhere in the tree. + * If callback returns `true` for `node`, `node[matchKey]` is collected. + * If callback returns anything else, returned value is collected. + * @param onlyFirst Whether to stop after first located element + */ +export function deepFindIn( + obj: any, + matchKey: string | number, + cb?: (o: any) => any, + onlyFirst?: boolean +): any[] { + const result: any[] = []; + for (const key of Object.getOwnPropertyNames(obj)) { + const value = obj[key]; + if (key === matchKey) { + if (!cb) result.push(value); + else { + const ret = cb(obj); + if (ret || typeof ret === "number") { + result.push(typeof ret === "boolean" ? value : ret); + } + } + } else if (value && typeof value === "object") { + result.push(...deepFindIn(value, matchKey, cb)); + } + if (onlyFirst && result.length) break; + } + return result; +} + +type Constructor = new (...args: any[]) => C; + +export const isInstanceOf = >>( + node: any, + ...nodeTypes: NodeTypes +): node is InstanceType => nodeTypes.some((type) => node instanceof type); diff --git a/src/types/ast/index.ts b/src/types/ast/index.ts index b6bab197..8f2fbee5 100644 --- a/src/types/ast/index.ts +++ b/src/types/ast/index.ts @@ -28,3 +28,6 @@ export * from "./tuple_type"; export * from "./type"; export * from "./typename_type"; export * from "./user_defined_type"; +export * from "./u256_type"; +export * from "./yul_builtin_function"; +export * from "./yul_function_type"; diff --git a/src/types/ast/u256_type.ts b/src/types/ast/u256_type.ts new file mode 100644 index 00000000..8e475593 --- /dev/null +++ b/src/types/ast/u256_type.ts @@ -0,0 +1,16 @@ +import { Range } from "../../misc"; +import { TypeNode } from "./type"; + +/** + * 256 bit native type of the EVM. It is the default (only, as of writing) type in yul, + * representing a value on the stack. Identical to uint256 in solidity. + */ +export class U256Type extends TypeNode { + constructor(src?: Range) { + super(src); + } + + pp(): string { + return `u256`; + } +} diff --git a/src/types/ast/yul_builtin_function.ts b/src/types/ast/yul_builtin_function.ts new file mode 100644 index 00000000..d123cc22 --- /dev/null +++ b/src/types/ast/yul_builtin_function.ts @@ -0,0 +1,42 @@ +import { FunctionLikeType } from "./function_like_type"; +import { TypeNode } from "./type"; +import { Range } from "../../misc"; + +/** + * The type of a yul builtin function + */ +export class YulBuiltinFunctionType extends FunctionLikeType { + returns: TypeNode[]; + + isPure: boolean; + + constructor( + name: string | undefined, + parameters: TypeNode[], + returns: TypeNode[], + isPure = true, + src?: Range + ) { + super(name, parameters, src); + this.returns = returns; + this.isPure = Boolean(isPure); + } + + pp(): string { + const mapper = (node: TypeNode) => node.pp(); + + const argStr = this.parameters.map(mapper).join(","); + + let retStr = this.returns.map(mapper).join(","); + + retStr = retStr !== "" ? ` -> ${retStr}` : retStr; + + return `yul_builtin_function ${ + this.name !== undefined ? this.name : "" + }(${argStr})${retStr}`; + } + + getChildren(): TypeNode[] { + return [...this.parameters, ...this.returns]; + } +} diff --git a/src/types/ast/yul_function_type.ts b/src/types/ast/yul_function_type.ts new file mode 100644 index 00000000..caa1d283 --- /dev/null +++ b/src/types/ast/yul_function_type.ts @@ -0,0 +1,38 @@ +import { FunctionLikeType } from "./function_like_type"; +import { TypeNode } from "./type"; +import { Range } from "../../misc"; + +/** + * The type of a yul function. + */ +export class YulFunctionType extends FunctionLikeType { + returns: TypeNode[]; + + constructor( + name: string | undefined, + parameters: TypeNode[], + returns: TypeNode[], + src?: Range + ) { + super(name, parameters, src); + this.returns = returns; + } + + pp(): string { + const mapper = (node: TypeNode) => node.pp(); + + const argStr = this.parameters.map(mapper).join(","); + + let retStr = this.returns.map(mapper).join(","); + + retStr = retStr !== "" ? ` -> ${retStr}` : retStr; + + return `yul_builtin_function ${ + this.name !== undefined ? this.name : "" + }(${argStr})${retStr}`; + } + + getChildren(): TypeNode[] { + return [...this.parameters, ...this.returns]; + } +} diff --git a/src/types/builtins.ts b/src/types/builtins.ts index 3d491911..02388d50 100644 --- a/src/types/builtins.ts +++ b/src/types/builtins.ts @@ -6,9 +6,10 @@ import { TRest, TVar, TypeNameType, - TypeNode + TypeNode, + YulBuiltinFunctionType } from "./ast"; -import { types } from "./reserved"; +import { types, yulTypes } from "./reserved"; export type VersionDependentType = [TypeNode, string]; @@ -385,3 +386,767 @@ export const globalBuiltins = new BuiltinStructType( ] ]) ); + +export const yulBuiltins = new BuiltinStructType( + "", + new Map([ + ["stop", [[new YulBuiltinFunctionType("stop", [], []), ">=0.3.1"]]], + [ + "add", + [ + [ + new YulBuiltinFunctionType( + "add", + [yulTypes.u256, yulTypes.u256], + [yulTypes.u256] + ), + ">=0.3.1" + ] + ] + ], + [ + "sub", + [ + [ + new YulBuiltinFunctionType( + "sub", + [yulTypes.u256, yulTypes.u256], + [yulTypes.u256] + ), + ">=0.3.1" + ] + ] + ], + [ + "mul", + [ + [ + new YulBuiltinFunctionType( + "mul", + [yulTypes.u256, yulTypes.u256], + [yulTypes.u256] + ), + ">=0.3.1" + ] + ] + ], + [ + "div", + [ + [ + new YulBuiltinFunctionType( + "div", + [yulTypes.u256, yulTypes.u256], + [yulTypes.u256] + ), + ">=0.3.1" + ] + ] + ], + [ + "sdiv", + [ + [ + new YulBuiltinFunctionType( + "sdiv", + [yulTypes.u256, yulTypes.u256], + [yulTypes.u256] + ), + ">=0.3.1" + ] + ] + ], + [ + "mod", + [ + [ + new YulBuiltinFunctionType( + "mod", + [yulTypes.u256, yulTypes.u256], + [yulTypes.u256] + ), + ">=0.3.1" + ] + ] + ], + [ + "smod", + [ + [ + new YulBuiltinFunctionType( + "smod", + [yulTypes.u256, yulTypes.u256], + [yulTypes.u256] + ), + ">=0.3.1" + ] + ] + ], + [ + "exp", + [ + [ + new YulBuiltinFunctionType( + "exp", + [yulTypes.u256, yulTypes.u256], + [yulTypes.u256] + ), + ">=0.3.1" + ] + ] + ], + ["not", [[new YulBuiltinFunctionType("not", [yulTypes.u256], [yulTypes.u256]), ">=0.3.1"]]], + [ + "lt", + [ + [ + new YulBuiltinFunctionType( + "lt", + [yulTypes.u256, yulTypes.u256], + [yulTypes.u256] + ), + ">=0.3.1" + ] + ] + ], + [ + "gt", + [ + [ + new YulBuiltinFunctionType( + "gt", + [yulTypes.u256, yulTypes.u256], + [yulTypes.u256] + ), + ">=0.3.1" + ] + ] + ], + [ + "slt", + [ + [ + new YulBuiltinFunctionType( + "slt", + [yulTypes.u256, yulTypes.u256], + [yulTypes.u256] + ), + ">=0.3.1" + ] + ] + ], + [ + "sgt", + [ + [ + new YulBuiltinFunctionType( + "sgt", + [yulTypes.u256, yulTypes.u256], + [yulTypes.u256] + ), + ">=0.3.1" + ] + ] + ], + [ + "eq", + [ + [ + new YulBuiltinFunctionType( + "eq", + [yulTypes.u256, yulTypes.u256], + [yulTypes.u256] + ), + ">=0.3.1" + ] + ] + ], + [ + "iszero", + [[new YulBuiltinFunctionType("iszero", [yulTypes.u256], [yulTypes.u256]), ">=0.3.1"]] + ], + [ + "and", + [ + [ + new YulBuiltinFunctionType( + "and", + [yulTypes.u256, yulTypes.u256], + [yulTypes.u256] + ), + ">=0.3.1" + ] + ] + ], + [ + "or", + [ + [ + new YulBuiltinFunctionType( + "or", + [yulTypes.u256, yulTypes.u256], + [yulTypes.u256] + ), + ">=0.3.1" + ] + ] + ], + [ + "xor", + [ + [ + new YulBuiltinFunctionType( + "xor", + [yulTypes.u256, yulTypes.u256], + [yulTypes.u256] + ), + ">=0.3.1" + ] + ] + ], + [ + "byte", + [ + [ + new YulBuiltinFunctionType( + "byte", + [yulTypes.u256, yulTypes.u256], + [yulTypes.u256] + ), + ">=0.3.1" + ] + ] + ], + [ + "shl", + [ + [ + new YulBuiltinFunctionType( + "shl", + [yulTypes.u256, yulTypes.u256], + [yulTypes.u256] + ), + ">=0.4.22" + ] + ] + ], + [ + "shr", + [ + [ + new YulBuiltinFunctionType( + "shr", + [yulTypes.u256, yulTypes.u256], + [yulTypes.u256] + ), + ">=0.4.22" + ] + ] + ], + [ + "sar", + [ + [ + new YulBuiltinFunctionType( + "sar", + [yulTypes.u256, yulTypes.u256], + [yulTypes.u256] + ), + ">=0.4.22" + ] + ] + ], + [ + "addmod", + [ + [ + new YulBuiltinFunctionType( + "addmod", + [yulTypes.u256, yulTypes.u256, yulTypes.u256], + [yulTypes.u256] + ), + ">=0.3.1" + ] + ] + ], + [ + "mulmod", + [ + [ + new YulBuiltinFunctionType( + "mulmod", + [yulTypes.u256, yulTypes.u256, yulTypes.u256], + [yulTypes.u256] + ), + ">=0.3.1" + ] + ] + ], + [ + "signextend", + [ + [ + new YulBuiltinFunctionType( + "signextend", + [yulTypes.u256, yulTypes.u256], + [yulTypes.u256] + ), + ">=0.3.1" + ] + ] + ], + [ + "keccak256", + [ + [ + new YulBuiltinFunctionType( + "keccak256", + [yulTypes.u256, yulTypes.u256], + [yulTypes.u256], + false + ), + ">=0.3.1" + ] + ] + ], + ["pc", [[new YulBuiltinFunctionType("pc", [], [yulTypes.u256], false), ">=0.3.1"]]], + ["pop", [[new YulBuiltinFunctionType("pop", [yulTypes.u256], [], false), ">=0.3.1"]]], + [ + "mload", + [ + [ + new YulBuiltinFunctionType("mload", [yulTypes.u256], [yulTypes.u256], false), + ">=0.3.1" + ] + ] + ], + [ + "mstore", + [ + [ + new YulBuiltinFunctionType("mstore", [yulTypes.u256, yulTypes.u256], [], false), + ">=0.3.1" + ] + ] + ], + [ + "mstore8", + [ + [ + new YulBuiltinFunctionType( + "mstore8", + [yulTypes.u256, yulTypes.u256], + [], + false + ), + ">=0.3.1" + ] + ] + ], + [ + "sload", + [ + [ + new YulBuiltinFunctionType("sload", [yulTypes.u256], [yulTypes.u256], false), + ">=0.3.1" + ] + ] + ], + [ + "sstore", + [ + [ + new YulBuiltinFunctionType("sstore", [yulTypes.u256, yulTypes.u256], [], false), + ">=0.3.1" + ] + ] + ], + ["msize", [[new YulBuiltinFunctionType("msize", [], [yulTypes.u256], false), ">=0.3.1"]]], + ["gas", [[new YulBuiltinFunctionType("gas", [], [yulTypes.u256], false), ">=0.3.1"]]], + [ + "address", + [[new YulBuiltinFunctionType("address", [], [yulTypes.u256], false), ">=0.3.1"]] + ], + [ + "balance", + [ + [ + new YulBuiltinFunctionType("balance", [yulTypes.u256], [yulTypes.u256], false), + ">=0.3.1" + ] + ] + ], + [ + "selfbalance", + [[new YulBuiltinFunctionType("selfbalance", [], [yulTypes.u256], false), ">=0.5.12"]] + ], + ["caller", [[new YulBuiltinFunctionType("caller", [], [yulTypes.u256], false), ">=0.3.1"]]], + [ + "callvalue", + [[new YulBuiltinFunctionType("callvalue", [], [yulTypes.u256], false), ">=0.3.1"]] + ], + [ + "calldataload", + [ + [ + new YulBuiltinFunctionType( + "calldataload", + [yulTypes.u256], + [yulTypes.u256], + false + ), + ">=0.3.1" + ] + ] + ], + [ + "calldatasize", + [[new YulBuiltinFunctionType("calldatasize", [], [yulTypes.u256], false), ">=0.3.1"]] + ], + [ + "calldatacopy", + [ + [ + new YulBuiltinFunctionType( + "calldatacopy", + [yulTypes.u256, yulTypes.u256, yulTypes.u256], + [], + false + ), + ">=0.3.1" + ] + ] + ], + [ + "codesize", + [[new YulBuiltinFunctionType("codesize", [], [yulTypes.u256], false), ">=0.3.1"]] + ], + [ + "codecopy", + [ + [ + new YulBuiltinFunctionType( + "codecopy", + [yulTypes.u256, yulTypes.u256, yulTypes.u256], + [], + false + ), + ">=0.3.1" + ] + ] + ], + [ + "extcodesize", + [ + [ + new YulBuiltinFunctionType( + "extcodesize", + [yulTypes.u256], + [yulTypes.u256], + false + ), + ">=0.3.1" + ] + ] + ], + [ + "extcodecopy", + [ + [ + new YulBuiltinFunctionType( + "extcodecopy", + [yulTypes.u256, yulTypes.u256, yulTypes.u256, yulTypes.u256], + [], + false + ), + ">=0.3.1" + ] + ] + ], + [ + "returndatasize", + [[new YulBuiltinFunctionType("returndatasize", [], [yulTypes.u256], false), ">=0.4.12"]] + ], + [ + "returndatacopy", + [ + [ + new YulBuiltinFunctionType( + "returndatacopy", + [yulTypes.u256, yulTypes.u256, yulTypes.u256], + [], + false + ), + ">=0.4.12" + ] + ] + ], + [ + "extcodehash", + [ + [ + new YulBuiltinFunctionType( + "extcodehash", + [yulTypes.u256], + [yulTypes.u256], + false + ), + ">=0.4.22" + ] + ] + ], + [ + "create", + [ + [ + new YulBuiltinFunctionType( + "create", + [yulTypes.u256, yulTypes.u256, yulTypes.u256], + [yulTypes.u256], + false + ), + ">=0.3.1" + ] + ] + ], + [ + "create2", + [ + [ + new YulBuiltinFunctionType( + "create2", + [yulTypes.u256, yulTypes.u256, yulTypes.u256, yulTypes.u256], + [yulTypes.u256], + false + ), + ">=0.4.22" + ] + ] + ], + [ + "call", + [ + [ + new YulBuiltinFunctionType( + "call", + [ + yulTypes.u256, + yulTypes.u256, + yulTypes.u256, + yulTypes.u256, + yulTypes.u256, + yulTypes.u256, + yulTypes.u256 + ], + [yulTypes.u256], + false + ), + ">=0.3.1" + ] + ] + ], + [ + "callcode", + [ + [ + new YulBuiltinFunctionType( + "callcode", + [ + yulTypes.u256, + yulTypes.u256, + yulTypes.u256, + yulTypes.u256, + yulTypes.u256, + yulTypes.u256, + yulTypes.u256 + ], + [yulTypes.u256], + false + ), + ">=0.3.1" + ] + ] + ], + [ + "delegatecall", + [ + [ + new YulBuiltinFunctionType( + "delegatecall", + [ + yulTypes.u256, + yulTypes.u256, + yulTypes.u256, + yulTypes.u256, + yulTypes.u256, + yulTypes.u256 + ], + [yulTypes.u256], + false + ), + ">=0.3.1" + ] + ] + ], + [ + "staticcall", + [ + [ + new YulBuiltinFunctionType( + "staticcall", + [ + yulTypes.u256, + yulTypes.u256, + yulTypes.u256, + yulTypes.u256, + yulTypes.u256, + yulTypes.u256 + ], + [yulTypes.u256], + false + ), + ">=0.4.12" + ] + ] + ], + [ + "return", + [ + [ + new YulBuiltinFunctionType("return", [yulTypes.u256, yulTypes.u256], [], false), + ">=0.3.1" + ] + ] + ], + [ + "revert", + [ + [ + new YulBuiltinFunctionType("revert", [yulTypes.u256, yulTypes.u256], [], false), + ">=0.4.12" + ] + ] + ], + [ + "selfdestruct", + [[new YulBuiltinFunctionType("selfdestruct", [yulTypes.u256], [], false), ">=0.3.1"]] + ], + ["invalid", [[new YulBuiltinFunctionType("invalid", [], [], false), ">=0.3.1"]]], + [ + "log0", + [ + [ + new YulBuiltinFunctionType("log0", [yulTypes.u256, yulTypes.u256], [], false), + ">=0.3.1" + ] + ] + ], + [ + "log1", + [ + [ + new YulBuiltinFunctionType( + "log1", + [yulTypes.u256, yulTypes.u256, yulTypes.u256], + [], + false + ), + ">=0.3.1" + ] + ] + ], + [ + "log2", + [ + [ + new YulBuiltinFunctionType( + "log2", + [yulTypes.u256, yulTypes.u256, yulTypes.u256, yulTypes.u256], + [], + false + ), + ">=0.3.1" + ] + ] + ], + [ + "log3", + [ + [ + new YulBuiltinFunctionType( + "log3", + [yulTypes.u256, yulTypes.u256, yulTypes.u256, yulTypes.u256, yulTypes.u256], + [], + false + ), + ">=0.3.1" + ] + ] + ], + [ + "log4", + [ + [ + new YulBuiltinFunctionType( + "log4", + [ + yulTypes.u256, + yulTypes.u256, + yulTypes.u256, + yulTypes.u256, + yulTypes.u256, + yulTypes.u256 + ], + [], + false + ), + ">=0.3.1" + ] + ] + ], + [ + "chainid", + [[new YulBuiltinFunctionType("chainid", [], [yulTypes.u256], false), ">=0.5.12"]] + ], + [ + "basefee", + [[new YulBuiltinFunctionType("basefee", [], [yulTypes.u256], false), ">=0.8.7"]] + ], + ["origin", [[new YulBuiltinFunctionType("origin", [], [yulTypes.u256], false), ">=0.3.1"]]], + [ + "gasprice", + [[new YulBuiltinFunctionType("gasprice", [], [yulTypes.u256], false), ">=0.3.1"]] + ], + [ + "blockhash", + [ + [ + new YulBuiltinFunctionType( + "blockhash", + [yulTypes.u256], + [yulTypes.u256], + false + ), + ">=0.3.1" + ] + ] + ], + [ + "coinbase", + [[new YulBuiltinFunctionType("coinbase", [], [yulTypes.u256], false), ">=0.3.1"]] + ], + [ + "timestamp", + [[new YulBuiltinFunctionType("timestamp", [], [yulTypes.u256], false), ">=0.3.1"]] + ], + ["number", [[new YulBuiltinFunctionType("number", [], [yulTypes.u256], false), ">=0.3.1"]]], + [ + "difficulty", + [[new YulBuiltinFunctionType("difficulty", [], [yulTypes.u256], false), ">=0.3.1"]] + ], + [ + "gaslimit", + [[new YulBuiltinFunctionType("gaslimit", [], [yulTypes.u256], false), ">=0.3.1"]] + ] + ]) +); diff --git a/src/types/eval_const.ts b/src/types/eval_const.ts index 7044ba04..8a8deb2e 100644 --- a/src/types/eval_const.ts +++ b/src/types/eval_const.ts @@ -3,6 +3,7 @@ import { BinaryOperation, Conditional, Expression, + ExternalReferenceType, FunctionCall, FunctionCallKind, Identifier, @@ -10,10 +11,19 @@ import { LiteralKind, TupleExpression, UnaryOperation, - VariableDeclaration + VariableDeclaration, + YulExpression, + YulFunctionCall, + YulIdentifier, + YulLiteral, + YulLiteralKind, + YulVariableDeclaration } from "../ast"; +import { LatestCompilerVersion } from "../compile"; import { pp } from "../misc"; -import { binaryOperatorGroups, subdenominationMultipliers } from "./infer"; +import { YulBuiltinFunctionType } from "./ast"; +import { yulBuiltins } from "./builtins"; +import { binaryOperatorGroups, subdenominationMultipliers, yulBinaryBuiltinGroups } from "./infer"; /** * Tune up precision of decimal values to follow Solidity behavior. * Be careful with precision - setting it to large values causes NodeJS to crash. @@ -25,9 +35,9 @@ Decimal.set({ precision: 100 }); export type Value = Decimal | boolean | string | bigint; export class EvalError extends Error { - expr: Expression; + expr: Expression | YulExpression; - constructor(e: Expression, msg: string) { + constructor(e: Expression | YulExpression, msg: string) { super(msg); this.expr = e; @@ -35,7 +45,7 @@ export class EvalError extends Error { } export class NonConstantExpressionError extends EvalError { - constructor(e: Expression) { + constructor(e: Expression | YulExpression) { super(e, `Found non-constant expression ${pp(e)} during constant evaluation`); } } @@ -52,11 +62,39 @@ function demoteFromDec(d: Decimal): Decimal | bigint { return d.isInt() ? BigInt(d.toFixed()) : d; } -export function isConstant(expr: Expression): boolean { +function utf8ToHex(s: string): string { + return Array.from(new TextEncoder().encode(s), (byte) => + byte.toString(16).padStart(2, "0") + ).join(""); +} + +/** + * Used for evaluating yul constants, where all values are treated as uint256 + */ +function coerceInteger(v: Value): bigint { + if (typeof v === "bigint") return v; + // If value is a yul literal string, it's already been converted to a BigInt. + // If it is any other string, it's an illegal reference to a string constant + if (typeof v === "string") + throw Error( + `coerceInteger called with a string - inline assembly can not reference external string literals` + ); + if (v instanceof Decimal) return BigInt(v.toFixed()); + return v ? BigInt(1) : BigInt(0); +} + +export function isConstant( + expr: Expression | YulExpression, + version: string = LatestCompilerVersion +): boolean { if (expr instanceof Literal) { return true; } + if (expr instanceof YulLiteral) { + return true; + } + if (expr instanceof UnaryOperation && isConstant(expr.vSubExpression)) { return true; } @@ -91,7 +129,7 @@ export function isConstant(expr: Expression): boolean { return true; } - if (expr instanceof Identifier) { + if (expr instanceof Identifier || expr instanceof YulIdentifier) { const decl = expr.vReferencedDeclaration; if ( @@ -102,6 +140,10 @@ export function isConstant(expr: Expression): boolean { ) { return true; } + + if (decl instanceof YulVariableDeclaration && decl.value && isConstant(decl.value)) { + return true; + } } if ( @@ -112,9 +154,35 @@ export function isConstant(expr: Expression): boolean { return true; } + if ( + expr instanceof YulFunctionCall && + expr.vFunctionCallType === ExternalReferenceType.Builtin + ) { + const builtinFunction = yulBuiltins.getFieldForVersion(expr.vFunctionName.name, version) as + | YulBuiltinFunctionType + | undefined; + if (builtinFunction?.isPure && expr.vArguments.every((arg) => isConstant(arg))) { + return true; + } + } + return false; } +function evalYulLiteral(expr: YulLiteral): bigint { + if (expr.kind === YulLiteralKind.String) { + return BigInt(`0x${utf8ToHex(expr.value as string).padEnd(64, "0")}`); + } + if (expr.kind === YulLiteralKind.Bool) { + return expr.value === "true" ? BigInt(1) : BigInt(0); + } + if (expr.kind === YulLiteralKind.Number) { + return BigInt(expr.value); + } + + throw new EvalError(expr, `Unrecognized yul literal kind "${expr.kind}"`); +} + function evalLiteral(expr: Literal): Value { if (expr.kind === LiteralKind.Bool) { return expr.value === "true"; @@ -355,6 +423,158 @@ function evalBinary(expr: BinaryOperation): Value { throw new EvalError(expr, `Unknown binary op ${expr.operator}`); } +function evalYulBinaryComparison(expr: YulFunctionCall, lVal: bigint, rVal: bigint): Value { + const op = expr.vFunctionName.name; + + const lDec = promoteToDec(lVal); + const rDec = promoteToDec(rVal); + + if (op === "lt" || op === "slt") { + return coerceInteger(lDec.lessThan(rDec)); + } + + if (op === "gt" || op === "sgt") { + return coerceInteger(lDec.greaterThan(rDec)); + } + + if (op === "eq") { + return coerceInteger(lDec.eq(rDec)); + } + + throw new EvalError(expr, `Unknown comparison op ${expr.vFunctionName}`); +} + +function evalYulBinaryArithmetic(expr: YulFunctionCall, lVal: bigint, rVal: bigint): Value { + const op = expr.vFunctionName.name; + + const lDec = promoteToDec(lVal); + const rDec = promoteToDec(rVal); + + let res: Decimal; + + if (op === "add") { + res = lDec.plus(rDec); + } else if (op === "sub") { + res = lDec.minus(rDec); + } else if (op === "mul") { + res = lDec.times(rDec); + } else if (op === "div" || op === "sdiv") { + res = lDec.div(rDec); + } else if (op === "mod" || op === "smod") { + res = lDec.modulo(rDec); + } else if (op === "exp") { + res = lDec.pow(rDec); + } else if (op === "signextend") { + // @todo Implement `signextend` + throw new EvalError(expr, `Unimplemented binary op ${expr.vFunctionName}`); + } else { + throw new EvalError(expr, `Unknown arithmetic op ${expr.vFunctionName}`); + } + + return coerceInteger(res); +} + +function evalYulBinaryBitwise(expr: YulFunctionCall, lVal: bigint, rVal: bigint): Value { + const op = expr.vFunctionName.name; + + if (op === "shl") { + return rVal << lVal; + } + + // @todo How should sar be handled differently from shr? + if (op === "shr" || op === "sar") { + return rVal >> lVal; + } + + if (op === "or") { + return lVal | rVal; + } + + if (op === "and") { + return lVal & rVal; + } + + if (op === "xor") { + return lVal ^ rVal; + } + + if (op === "byte") { + const shiftSize = BigInt(248 - Number(lVal) * 8); + return (rVal >> shiftSize) & BigInt(0xff); + } + + throw new EvalError(expr, `Unknown bitwise op ${expr.vFunctionName}`); +} + +function evalYulUnary(expr: YulFunctionCall) { + if (expr.vArguments.length !== 1) { + throw new EvalError( + expr, + `Expected a single argument in unary builtin function ${pp(expr)}` + ); + } + + const subVal = coerceInteger(evalConstantExpr(expr.vArguments[0])); + + if (expr.vFunctionName.name === "iszero") { + return coerceInteger(subVal === BigInt(0)); + } + + if (expr.vFunctionName.name === "not") { + return ~subVal; + } + + throw new EvalError(expr, `NYI unary operator ${expr.vFunctionName}`); +} + +function evalYulBinary(expr: YulFunctionCall) { + if (expr.vArguments.length !== 2) { + throw new EvalError(expr, `Expected two arguments in binary builtin function ${pp(expr)}`); + } + + const [lVal, rVal] = expr.vArguments.map((arg) => coerceInteger(evalConstantExpr(arg))); + + const op = expr.vFunctionName.name; + + if (yulBinaryBuiltinGroups.Comparison.includes(op)) { + return evalYulBinaryComparison(expr, lVal, rVal); + } + + if (yulBinaryBuiltinGroups.Arithmetic.includes(op)) { + return evalYulBinaryArithmetic(expr, lVal, rVal); + } + + if (yulBinaryBuiltinGroups.Bitwise.includes(op)) { + return evalYulBinaryBitwise(expr, lVal, rVal); + } + + throw new EvalError(expr, `Unknown binary op ${expr.vFunctionName}`); +} + +function evalYulTernary(expr: YulFunctionCall): Value { + if (expr.vArguments.length !== 3) { + throw new EvalError( + expr, + `Expected three arguments in ternary builtin function ${pp(expr)}` + ); + } + const op = expr.vFunctionName.name; + + const [dec1, dec2, dec3] = expr.vArguments.map((arg) => + promoteToDec(coerceInteger(evalConstantExpr(arg))) + ); + + if (op === "mulmod") { + return coerceInteger(dec1.mul(dec2).mod(dec3)); + } + + if (op === "addmod") { + return coerceInteger(dec1.add(dec2).mod(dec3)); + } + + throw new EvalError(expr, `Unknown ternary op ${expr.vFunctionName}`); +} + /** * Given a constant expression `expr` evaluate it to a concrete `Value`. * If `expr` is not constant throw `NonConstantExpressionError`. @@ -362,7 +582,10 @@ function evalBinary(expr: BinaryOperation): Value { * TODO: The order of some operations changed in some version. * So perhaps to be fully precise here we will need a compiler version too? */ -export function evalConstantExpr(expr: Expression): Value { +export function evalConstantExpr( + expr: Expression | YulExpression, + version = LatestCompilerVersion +): Value { if (!isConstant(expr)) { throw new NonConstantExpressionError(expr); } @@ -371,6 +594,10 @@ export function evalConstantExpr(expr: Expression): Value { return evalLiteral(expr); } + if (expr instanceof YulLiteral) { + return evalYulLiteral(expr); + } + if (expr instanceof UnaryOperation) { return evalUnary(expr); } @@ -389,6 +616,16 @@ export function evalConstantExpr(expr: Expression): Value { : evalConstantExpr(expr.vFalseExpression); } + if (expr instanceof YulIdentifier) { + const decl = expr.vReferencedDeclaration; + if (decl instanceof VariableDeclaration) { + return coerceInteger(evalConstantExpr(decl.vValue as Expression)); + } + if (decl instanceof YulVariableDeclaration) { + return coerceInteger(evalConstantExpr(decl.value as YulExpression)); + } + } + if (expr instanceof Identifier) { const decl = expr.vReferencedDeclaration; @@ -405,6 +642,25 @@ export function evalConstantExpr(expr: Expression): Value { return evalConstantExpr(expr.vArguments[0]); } + if ( + expr instanceof YulFunctionCall && + expr.vFunctionCallType === ExternalReferenceType.Builtin + ) { + const builtinFunction = yulBuiltins.getFieldForVersion(expr.vFunctionName.name, version) as + | YulBuiltinFunctionType + | undefined; + if (builtinFunction?.isPure) { + if (builtinFunction.parameters.length === 1) { + return evalYulUnary(expr); + } + if (builtinFunction.parameters.length === 2) { + return evalYulBinary(expr); + } + if (builtinFunction.parameters.length === 3) { + return evalYulTernary(expr); + } + } + } /// Note that from the point of view of the type system constant conditionals and /// indexing in constant array literals are not considered constant expressions. /// So for now we don't support them, but we may change that in the future. diff --git a/src/types/infer.ts b/src/types/infer.ts index bdfb275b..f3809ce8 100644 --- a/src/types/infer.ts +++ b/src/types/infer.ts @@ -117,6 +117,12 @@ export const binaryOperatorGroups = { Logical: ["&&", "||"] }; +export const yulBinaryBuiltinGroups = { + Arithmetic: ["add", "sub", "div", "sdiv", "exp", "mul", "mod", "smod", "signextend"], + Bitwise: ["and", "or", "xor", "sar", "shl", "shr", "byte"], + Comparison: ["gt", "lt", "sgt", "slt", "eq"] +}; + export const subdenominationMultipliers: { [key: string]: Decimal } = { seconds: new Decimal(1), minutes: new Decimal(60), diff --git a/src/types/reserved.ts b/src/types/reserved.ts index 5868e6a8..b5e4b8b3 100644 --- a/src/types/reserved.ts +++ b/src/types/reserved.ts @@ -8,7 +8,8 @@ import { AddressType, IntType, TupleType, - BuiltinFunctionType + BuiltinFunctionType, + U256Type } from "./ast"; // Helper with some singleton types to avoid unnecessary allocations @@ -29,3 +30,7 @@ export const types = { noType: new TupleType([]), typeOfType: new BuiltinFunctionType(undefined, [], []) }; + +export const yulTypes = { + u256: new U256Type(undefined) +}; diff --git a/test/integration/compile/latest_08.spec.ts b/test/integration/compile/latest_08.spec.ts index e58a498e..74ed6f73 100644 --- a/test/integration/compile/latest_08.spec.ts +++ b/test/integration/compile/latest_08.spec.ts @@ -10,6 +10,7 @@ import { PossibleCompilerKinds, SourceUnit } from "../../../src"; +import { isYulASTNode } from "../../../src/ast/implementation/yul"; import { createImprint } from "./common"; const mainSample = "./test/samples/solidity/latest_08.sol"; @@ -120,7 +121,9 @@ for (const compilerKind of PossibleCompilerKinds) { expect(sourceUnit.src).toEqual("0:8713:0"); expect(sourceUnit.absolutePath).toEqual(mainSample); expect(sourceUnit.children.length).toEqual(30); - expect(sourceUnit.getChildren().length).toEqual(743); + + const children = sourceUnit.getChildrenBySelector((node) => !isYulASTNode(node), false); + expect(children.length).toEqual(743); }); it(`Validate parsed output (${astKind})`, () => { diff --git a/test/unit/misc/utils/deepFindIn.spec.ts b/test/unit/misc/utils/deepFindIn.spec.ts new file mode 100644 index 00000000..1194791e --- /dev/null +++ b/test/unit/misc/utils/deepFindIn.spec.ts @@ -0,0 +1,51 @@ +import expect from "expect"; +import { deepFindIn } from "../../../../src"; + +const testObject = { + type: "SourceUnit", + id: 0, + children: [ + { + id: 2, + type: "FunctionDefinition", + vParameters: { + a: 1, + b: 3, + c: { + id: 1 + } + } + }, + [[{ id: 5 }], { id: "6" }, { x: 2 }] + ] +}; + +const cases: Array<[string, any, ((obj: any) => any) | undefined, any[], ...([boolean] | [])]> = [ + [ + "Returns deeply nested obj[matchKey] when no callback is provided", + "id", + undefined, + [0, 2, 1, 5, "6"] + ], + [ + "Returns deeply nested obj[matchKey] when cb returns true", + "id", + (obj) => typeof obj.id === "number" && obj.id > 1, + [2, 5] + ], + [ + "Returns cb response when it is not true or falsey", + "0", + (arr) => arr.length === 2 && arr[0]?.type, + ["FunctionDefinition"] + ], + ["Stops at first result if match found", "id", undefined, [0], true] +]; + +describe("deepFindIn()", () => { + for (const [title, key, cb, expectation, onlyFirst] of cases) { + it(`${title}`, () => { + expect(deepFindIn(testObject, key, cb, onlyFirst)).toMatchObject(expectation); + }); + } +}); diff --git a/test/unit/types/eval_const.spec.ts b/test/unit/types/eval_const.spec.ts index a98f88ba..26c38b9b 100644 --- a/test/unit/types/eval_const.spec.ts +++ b/test/unit/types/eval_const.spec.ts @@ -11,910 +11,1001 @@ import { LiteralKind, Mutability, StateVariableVisibility, - Value + Value, + YulLiteralKind, + YulExpression } from "../../../src"; -const cases: Array<[string, (factory: ASTNodeFactory) => Expression, boolean, Value | undefined]> = - [ - [ - "PrimaryExpression (invalid expression)", - (factory: ASTNodeFactory) => factory.makePrimaryExpression("???"), - false, - undefined - ], - [ - "Literal (unknown)", - (factory: ASTNodeFactory) => - factory.makeLiteral("", "unknown" as LiteralKind, "", "???"), - true, - undefined - ], - [ - "Literal (true)", - (factory: ASTNodeFactory) => +const cases: Array< + [string, (factory: ASTNodeFactory) => Expression | YulExpression, boolean, Value | undefined] +> = [ + [ + "PrimaryExpression (invalid expression)", + (factory: ASTNodeFactory) => factory.makePrimaryExpression("???"), + false, + undefined + ], + [ + "Literal (unknown)", + (factory: ASTNodeFactory) => + factory.makeLiteral("", "unknown" as LiteralKind, "", "???"), + true, + undefined + ], + [ + "Literal (true)", + (factory: ASTNodeFactory) => factory.makeLiteral("", LiteralKind.Bool, "", "true"), + true, + true + ], + [ + "Literal (false)", + (factory: ASTNodeFactory) => + factory.makeLiteral("", LiteralKind.Bool, "", "false"), + true, + false + ], + [ + "Literal (string)", + (factory: ASTNodeFactory) => + factory.makeLiteral("", LiteralKind.String, "", "abc"), + true, + "abc" + ], + [ + "Literal (unicode string)", + (factory: ASTNodeFactory) => + factory.makeLiteral("", LiteralKind.UnicodeString, "", "Some 😎 string"), + true, + "Some 😎 string" + ], + [ + "Literal (hex string)", + (factory: ASTNodeFactory) => + factory.makeLiteral("", LiteralKind.HexString, "ffcc33", "abcdef"), + true, + "ffcc33" + ], + [ + "Literal (uint8)", + (factory: ASTNodeFactory) => + factory.makeLiteral("", LiteralKind.Number, "", "256"), + true, + 256n + ], + [ + "Literal (uint16, underscore separator)", + (factory: ASTNodeFactory) => + factory.makeLiteral("", LiteralKind.Number, "", "0xff_ff"), + true, + 65535n + ], + [ + "Literal (uint with subdenomintation)", + (factory: ASTNodeFactory) => + factory.makeLiteral("", LiteralKind.Number, "", "2", EtherUnit.Ether), + true, + 2_000_000_000_000_000_000n + ], + [ + "Literal (decimal)", + (factory: ASTNodeFactory) => + factory.makeLiteral("", LiteralKind.Number, "", "2.5"), + true, + new Decimal(2.5) + ], + [ + "Literal (decimal with subdenomintation)", + (factory: ASTNodeFactory) => + factory.makeLiteral("", LiteralKind.Number, "", "2.5", EtherUnit.Ether), + true, + 2_500_000_000_000_000_000n + ], + [ + "Literal (uint with invalid subdenomintation)", + (factory: ASTNodeFactory) => + factory.makeLiteral("", LiteralKind.Number, "", "1", "unknown" as EtherUnit), + true, + undefined + ], + [ + "UnaryOperation (!true)", + (factory: ASTNodeFactory) => + factory.makeUnaryOperation( + "", + true, + "!", + factory.makeLiteral("", LiteralKind.Bool, "", "true") + ), + true, + false + ], + [ + "UnaryOperation (!false)", + (factory: ASTNodeFactory) => + factory.makeUnaryOperation( + "", + true, + "!", + factory.makeLiteral("", LiteralKind.Bool, "", "false") + ), + true, + true + ], + [ + "UnaryOperation (!0)", + (factory: ASTNodeFactory) => + factory.makeUnaryOperation( + "", + true, + "!", + factory.makeLiteral("", LiteralKind.Number, "", "0") + ), + true, + undefined + ], + [ + "UnaryOperation (~0)", + (factory: ASTNodeFactory) => + factory.makeUnaryOperation( + "", + true, + "~", + factory.makeLiteral("", LiteralKind.Number, "", "0") + ), + true, + -1n + ], + [ + "UnaryOperation (~false)", + (factory: ASTNodeFactory) => + factory.makeUnaryOperation( + "", + true, + "~", + factory.makeLiteral("", LiteralKind.Bool, "", "false") + ), + true, + undefined + ], + [ + "UnaryOperation (-1)", + (factory: ASTNodeFactory) => + factory.makeUnaryOperation( + "", + true, + "-", + factory.makeLiteral("", LiteralKind.Number, "", "1") + ), + true, + -1n + ], + [ + "UnaryOperation (-0.5)", + (factory: ASTNodeFactory) => + factory.makeUnaryOperation( + "", + true, + "-", + factory.makeLiteral("", LiteralKind.Number, "", "0.5") + ), + true, + new Decimal(-0.5) + ], + [ + "UnaryOperation (-true)", + (factory: ASTNodeFactory) => + factory.makeUnaryOperation( + "", + true, + "-", + factory.makeLiteral("", LiteralKind.Bool, "", "true") + ), + true, + undefined + ], + [ + "UnaryOperation (+1)", + (factory: ASTNodeFactory) => + factory.makeUnaryOperation( + "", + true, + "+", + factory.makeLiteral("", LiteralKind.Number, "", "1") + ), + true, + 1n + ], + [ + "UnaryOperation (+0.5)", + (factory: ASTNodeFactory) => + factory.makeUnaryOperation( + "", + true, + "+", + factory.makeLiteral("", LiteralKind.Number, "", "0.5") + ), + true, + new Decimal(0.5) + ], + [ + "UnaryOperation (+true)", + (factory: ASTNodeFactory) => + factory.makeUnaryOperation( + "", + true, + "+", + factory.makeLiteral("", LiteralKind.Bool, "", "true") + ), + true, + undefined + ], + [ + "UnaryOperation (???true)", + (factory: ASTNodeFactory) => + factory.makeUnaryOperation( + "", + true, + "???", + factory.makeLiteral("", LiteralKind.Bool, "", "true") + ), + true, + undefined + ], + [ + "BinaryOperation (true && false)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "&&", factory.makeLiteral("", LiteralKind.Bool, "", "true"), - true, - true - ], - [ - "Literal (false)", - (factory: ASTNodeFactory) => - factory.makeLiteral("", LiteralKind.Bool, "", "false"), - true, - false - ], - [ - "Literal (string)", - (factory: ASTNodeFactory) => + factory.makeLiteral("", LiteralKind.Bool, "", "false") + ), + true, + false + ], + [ + "BinaryOperation (true || false)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "||", + factory.makeLiteral("", LiteralKind.Bool, "", "true"), + factory.makeLiteral("", LiteralKind.Bool, "", "false") + ), + true, + true + ], + [ + "BinaryOperation (1 || 2)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "||", + factory.makeLiteral("", LiteralKind.Number, "", "1"), + factory.makeLiteral("", LiteralKind.Number, "", "2") + ), + true, + undefined + ], + [ + "BinaryOperation (string == string)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "==", factory.makeLiteral("", LiteralKind.String, "", "abc"), - true, - "abc" - ], - [ - "Literal (unicode string)", - (factory: ASTNodeFactory) => - factory.makeLiteral("", LiteralKind.UnicodeString, "", "Some 😎 string"), - true, - "Some 😎 string" - ], - [ - "Literal (hex string)", - (factory: ASTNodeFactory) => - factory.makeLiteral("", LiteralKind.HexString, "ffcc33", "abcdef"), - true, - "ffcc33" - ], - [ - "Literal (uint8)", - (factory: ASTNodeFactory) => - factory.makeLiteral("", LiteralKind.Number, "", "256"), - true, - 256n - ], - [ - "Literal (uint16, underscore separator)", - (factory: ASTNodeFactory) => - factory.makeLiteral("", LiteralKind.Number, "", "0xff_ff"), - true, - 65535n - ], - [ - "Literal (uint with subdenomintation)", - (factory: ASTNodeFactory) => - factory.makeLiteral("", LiteralKind.Number, "", "2", EtherUnit.Ether), - true, - 2_000_000_000_000_000_000n - ], - [ - "Literal (decimal)", - (factory: ASTNodeFactory) => - factory.makeLiteral("", LiteralKind.Number, "", "2.5"), - true, - new Decimal(2.5) - ], - [ - "Literal (decimal with subdenomintation)", - (factory: ASTNodeFactory) => - factory.makeLiteral("", LiteralKind.Number, "", "2.5", EtherUnit.Ether), - true, - 2_500_000_000_000_000_000n - ], - [ - "Literal (uint with invalid subdenomintation)", - (factory: ASTNodeFactory) => + factory.makeLiteral("", LiteralKind.String, "", "def") + ), + true, + undefined + ], + [ + "BinaryOperation (1 == 1)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "==", + factory.makeLiteral("", LiteralKind.Number, "", "1"), + factory.makeLiteral("", LiteralKind.Number, "", "1") + ), + true, + true + ], + [ + "BinaryOperation (1 == 2)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "==", + factory.makeLiteral("", LiteralKind.Number, "", "1"), + factory.makeLiteral("", LiteralKind.Number, "", "2") + ), + true, + false + ], + [ + "BinaryOperation (1 != 2)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "!=", + factory.makeLiteral("", LiteralKind.Number, "", "1"), + factory.makeLiteral("", LiteralKind.Number, "", "2") + ), + true, + true + ], + [ + "BinaryOperation (2 != 2)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "!=", + factory.makeLiteral("", LiteralKind.Number, "", "2"), + factory.makeLiteral("", LiteralKind.Number, "", "2") + ), + true, + false + ], + [ + "BinaryOperation (0.5 != 0.5)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "!=", + factory.makeLiteral("", LiteralKind.Number, "", "0.5"), + factory.makeLiteral("", LiteralKind.Number, "", "0.5") + ), + true, + false + ], + [ + "BinaryOperation (1 < 2)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "<", + factory.makeLiteral("", LiteralKind.Number, "", "1"), + factory.makeLiteral("", LiteralKind.Number, "", "2") + ), + true, + true + ], + [ + "BinaryOperation (1 < 1)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "<", + factory.makeLiteral("", LiteralKind.Number, "", "1"), + factory.makeLiteral("", LiteralKind.Number, "", "1") + ), + true, + false + ], + [ + "BinaryOperation (1 <= 2)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "<=", + factory.makeLiteral("", LiteralKind.Number, "", "1"), + factory.makeLiteral("", LiteralKind.Number, "", "2") + ), + true, + true + ], + [ + "BinaryOperation (1 <= 1)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "<=", + factory.makeLiteral("", LiteralKind.Number, "", "1"), + factory.makeLiteral("", LiteralKind.Number, "", "1") + ), + true, + true + ], + [ + "BinaryOperation (2 <= 1)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "<=", + factory.makeLiteral("", LiteralKind.Number, "", "2"), + factory.makeLiteral("", LiteralKind.Number, "", "1") + ), + true, + false + ], + [ + "BinaryOperation (2 > 1)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + ">", + factory.makeLiteral("", LiteralKind.Number, "", "2"), + factory.makeLiteral("", LiteralKind.Number, "", "1") + ), + true, + true + ], + [ + "BinaryOperation (1 > 1)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + ">", + factory.makeLiteral("", LiteralKind.Number, "", "1"), + factory.makeLiteral("", LiteralKind.Number, "", "1") + ), + true, + false + ], + [ + "BinaryOperation (2 >= 1)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + ">=", + factory.makeLiteral("", LiteralKind.Number, "", "2"), + factory.makeLiteral("", LiteralKind.Number, "", "1") + ), + true, + true + ], + [ + "BinaryOperation (1 >= 1)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + ">=", + factory.makeLiteral("", LiteralKind.Number, "", "1"), + factory.makeLiteral("", LiteralKind.Number, "", "1") + ), + true, + true + ], + [ + "BinaryOperation (1 >= 2)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + ">=", + factory.makeLiteral("", LiteralKind.Number, "", "1"), + factory.makeLiteral("", LiteralKind.Number, "", "2") + ), + true, + false + ], + [ + "BinaryOperation (1 + 2)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "+", + factory.makeLiteral("", LiteralKind.Number, "", "1"), + factory.makeLiteral("", LiteralKind.Number, "", "2") + ), + true, + 3n + ], + [ + "BinaryOperation (1 - 2)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "-", + factory.makeLiteral("", LiteralKind.Number, "", "1"), + factory.makeLiteral("", LiteralKind.Number, "", "2") + ), + true, + -1n + ], + [ + "BinaryOperation (2 * 2)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "*", + factory.makeLiteral("", LiteralKind.Number, "", "2"), + factory.makeLiteral("", LiteralKind.Number, "", "2") + ), + true, + 4n + ], + [ + "BinaryOperation (4 / 2)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "/", + factory.makeLiteral("", LiteralKind.Number, "", "4"), + factory.makeLiteral("", LiteralKind.Number, "", "2") + ), + true, + 2n + ], + /** + * @todo Need to double-check and fix + */ + // [ + // "BinaryOperation (1 / 2)", + // (factory: ASTNodeFactory) => + // factory.makeBinaryOperation( + // "", + // "/", + // factory.makeLiteral("", LiteralKind.Number, "", "1"), + // factory.makeLiteral("", LiteralKind.Number, "", "2") + // ), + // true, + // "0n" + // ], + [ + "BinaryOperation (3 % 2)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "%", + factory.makeLiteral("", LiteralKind.Number, "", "3"), + factory.makeLiteral("", LiteralKind.Number, "", "2") + ), + true, + 1n + ], + [ + "BinaryOperation (4 % 2)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "%", + factory.makeLiteral("", LiteralKind.Number, "", "4"), + factory.makeLiteral("", LiteralKind.Number, "", "2") + ), + true, + 0n + ], + [ + "BinaryOperation (2 ** 8)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "**", + factory.makeLiteral("", LiteralKind.Number, "", "2"), + factory.makeLiteral("", LiteralKind.Number, "", "8") + ), + true, + 256n + ], + [ + "BinaryOperation (2 ** 256)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "**", + factory.makeLiteral("", LiteralKind.Number, "", "2"), + factory.makeLiteral("", LiteralKind.Number, "", "256") + ), + true, + 115792089237316195423570985008687907853269984665640564039457584007913129639936n + ], + [ + "BinaryOperation (2 << 5)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "<<", + factory.makeLiteral("", LiteralKind.Number, "", "2"), + factory.makeLiteral("", LiteralKind.Number, "", "5") + ), + true, + 64n + ], + [ + "BinaryOperation (2 << 100)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "<<", + factory.makeLiteral("", LiteralKind.Number, "", "2"), + factory.makeLiteral("", LiteralKind.Number, "", "100") + ), + true, + 2535301200456458802993406410752n + ], + [ + "BinaryOperation (126 >> 3)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + ">>", + factory.makeLiteral("", LiteralKind.Number, "", "126"), + factory.makeLiteral("", LiteralKind.Number, "", "3") + ), + true, + 15n + ], + [ + "BinaryOperation (2535301200456458802993406410752 >> 100)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + ">>", factory.makeLiteral( "", LiteralKind.Number, "", - "1", - "unknown" as EtherUnit - ), - true, - undefined - ], - [ - "UnaryOperation (!true)", - (factory: ASTNodeFactory) => - factory.makeUnaryOperation( - "", - true, - "!", - factory.makeLiteral("", LiteralKind.Bool, "", "true") - ), - true, - false - ], - [ - "UnaryOperation (!false)", - (factory: ASTNodeFactory) => - factory.makeUnaryOperation( - "", - true, - "!", - factory.makeLiteral("", LiteralKind.Bool, "", "false") - ), - true, - true - ], - [ - "UnaryOperation (!0)", - (factory: ASTNodeFactory) => - factory.makeUnaryOperation( - "", - true, - "!", - factory.makeLiteral("", LiteralKind.Number, "", "0") - ), - true, - undefined - ], - [ - "UnaryOperation (~0)", - (factory: ASTNodeFactory) => - factory.makeUnaryOperation( - "", - true, - "~", - factory.makeLiteral("", LiteralKind.Number, "", "0") - ), - true, - -1n - ], - [ - "UnaryOperation (~false)", - (factory: ASTNodeFactory) => - factory.makeUnaryOperation( - "", - true, - "~", - factory.makeLiteral("", LiteralKind.Bool, "", "false") - ), - true, - undefined - ], - [ - "UnaryOperation (-1)", - (factory: ASTNodeFactory) => - factory.makeUnaryOperation( - "", - true, - "-", - factory.makeLiteral("", LiteralKind.Number, "", "1") - ), - true, - -1n - ], - [ - "UnaryOperation (-0.5)", - (factory: ASTNodeFactory) => - factory.makeUnaryOperation( - "", - true, - "-", - factory.makeLiteral("", LiteralKind.Number, "", "0.5") - ), - true, - new Decimal(-0.5) - ], - [ - "UnaryOperation (-true)", - (factory: ASTNodeFactory) => - factory.makeUnaryOperation( - "", - true, - "-", - factory.makeLiteral("", LiteralKind.Bool, "", "true") - ), - true, - undefined - ], - [ - "UnaryOperation (+1)", - (factory: ASTNodeFactory) => - factory.makeUnaryOperation( - "", - true, - "+", - factory.makeLiteral("", LiteralKind.Number, "", "1") - ), - true, - 1n - ], - [ - "UnaryOperation (+0.5)", - (factory: ASTNodeFactory) => - factory.makeUnaryOperation( - "", - true, - "+", - factory.makeLiteral("", LiteralKind.Number, "", "0.5") - ), - true, - new Decimal(0.5) - ], - [ - "UnaryOperation (+true)", - (factory: ASTNodeFactory) => - factory.makeUnaryOperation( - "", - true, - "+", - factory.makeLiteral("", LiteralKind.Bool, "", "true") - ), - true, - undefined - ], - [ - "UnaryOperation (???true)", - (factory: ASTNodeFactory) => - factory.makeUnaryOperation( - "", - true, - "???", - factory.makeLiteral("", LiteralKind.Bool, "", "true") - ), - true, - undefined - ], - [ - "BinaryOperation (true && false)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "&&", - factory.makeLiteral("", LiteralKind.Bool, "", "true"), - factory.makeLiteral("", LiteralKind.Bool, "", "false") - ), - true, - false - ], - [ - "BinaryOperation (true || false)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "||", - factory.makeLiteral("", LiteralKind.Bool, "", "true"), - factory.makeLiteral("", LiteralKind.Bool, "", "false") - ), - true, - true - ], - [ - "BinaryOperation (1 || 2)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "||", - factory.makeLiteral("", LiteralKind.Number, "", "1"), - factory.makeLiteral("", LiteralKind.Number, "", "2") - ), - true, - undefined - ], - [ - "BinaryOperation (string == string)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "==", - factory.makeLiteral("", LiteralKind.String, "", "abc"), - factory.makeLiteral("", LiteralKind.String, "", "def") - ), - true, - undefined - ], - [ - "BinaryOperation (1 == 1)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "==", - factory.makeLiteral("", LiteralKind.Number, "", "1"), - factory.makeLiteral("", LiteralKind.Number, "", "1") - ), - true, - true - ], - [ - "BinaryOperation (1 == 2)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "==", - factory.makeLiteral("", LiteralKind.Number, "", "1"), - factory.makeLiteral("", LiteralKind.Number, "", "2") - ), - true, - false - ], - [ - "BinaryOperation (1 != 2)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "!=", - factory.makeLiteral("", LiteralKind.Number, "", "1"), - factory.makeLiteral("", LiteralKind.Number, "", "2") - ), - true, - true - ], - [ - "BinaryOperation (2 != 2)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "!=", - factory.makeLiteral("", LiteralKind.Number, "", "2"), - factory.makeLiteral("", LiteralKind.Number, "", "2") - ), - true, - false - ], - [ - "BinaryOperation (0.5 != 0.5)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "!=", - factory.makeLiteral("", LiteralKind.Number, "", "0.5"), - factory.makeLiteral("", LiteralKind.Number, "", "0.5") - ), - true, - false - ], - [ - "BinaryOperation (1 < 2)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "<", - factory.makeLiteral("", LiteralKind.Number, "", "1"), - factory.makeLiteral("", LiteralKind.Number, "", "2") - ), - true, - true - ], - [ - "BinaryOperation (1 < 1)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "<", - factory.makeLiteral("", LiteralKind.Number, "", "1"), - factory.makeLiteral("", LiteralKind.Number, "", "1") - ), - true, - false - ], - [ - "BinaryOperation (1 <= 2)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "<=", - factory.makeLiteral("", LiteralKind.Number, "", "1"), - factory.makeLiteral("", LiteralKind.Number, "", "2") - ), - true, - true - ], - [ - "BinaryOperation (1 <= 1)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "<=", - factory.makeLiteral("", LiteralKind.Number, "", "1"), - factory.makeLiteral("", LiteralKind.Number, "", "1") - ), - true, - true - ], - [ - "BinaryOperation (2 <= 1)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "<=", - factory.makeLiteral("", LiteralKind.Number, "", "2"), - factory.makeLiteral("", LiteralKind.Number, "", "1") - ), - true, - false - ], - [ - "BinaryOperation (2 > 1)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - ">", - factory.makeLiteral("", LiteralKind.Number, "", "2"), - factory.makeLiteral("", LiteralKind.Number, "", "1") - ), - true, - true - ], - [ - "BinaryOperation (1 > 1)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - ">", - factory.makeLiteral("", LiteralKind.Number, "", "1"), - factory.makeLiteral("", LiteralKind.Number, "", "1") - ), - true, - false - ], - [ - "BinaryOperation (2 >= 1)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - ">=", - factory.makeLiteral("", LiteralKind.Number, "", "2"), - factory.makeLiteral("", LiteralKind.Number, "", "1") - ), - true, - true - ], - [ - "BinaryOperation (1 >= 1)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - ">=", - factory.makeLiteral("", LiteralKind.Number, "", "1"), - factory.makeLiteral("", LiteralKind.Number, "", "1") - ), - true, - true - ], - [ - "BinaryOperation (1 >= 2)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - ">=", - factory.makeLiteral("", LiteralKind.Number, "", "1"), - factory.makeLiteral("", LiteralKind.Number, "", "2") - ), - true, - false - ], - [ - "BinaryOperation (1 + 2)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "+", - factory.makeLiteral("", LiteralKind.Number, "", "1"), - factory.makeLiteral("", LiteralKind.Number, "", "2") - ), - true, - 3n - ], - [ - "BinaryOperation (1 - 2)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "-", - factory.makeLiteral("", LiteralKind.Number, "", "1"), - factory.makeLiteral("", LiteralKind.Number, "", "2") + "2535301200456458802993406410752" ), - true, - -1n - ], - [ - "BinaryOperation (2 * 2)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "*", - factory.makeLiteral("", LiteralKind.Number, "", "2"), - factory.makeLiteral("", LiteralKind.Number, "", "2") - ), - true, - 4n - ], - [ - "BinaryOperation (4 / 2)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "/", - factory.makeLiteral("", LiteralKind.Number, "", "4"), - factory.makeLiteral("", LiteralKind.Number, "", "2") - ), - true, - 2n - ], - /** - * @todo Need to double-check and fix - */ - // [ - // "BinaryOperation (1 / 2)", - // (factory: ASTNodeFactory) => - // factory.makeBinaryOperation( - // "", - // "/", - // factory.makeLiteral("", LiteralKind.Number, "", "1"), - // factory.makeLiteral("", LiteralKind.Number, "", "2") - // ), - // true, - // "0n" - // ], - [ - "BinaryOperation (3 % 2)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "%", - factory.makeLiteral("", LiteralKind.Number, "", "3"), - factory.makeLiteral("", LiteralKind.Number, "", "2") - ), - true, - 1n - ], - [ - "BinaryOperation (4 % 2)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "%", - factory.makeLiteral("", LiteralKind.Number, "", "4"), - factory.makeLiteral("", LiteralKind.Number, "", "2") - ), - true, - 0n - ], - [ - "BinaryOperation (2 ** 8)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "**", - factory.makeLiteral("", LiteralKind.Number, "", "2"), - factory.makeLiteral("", LiteralKind.Number, "", "8") - ), - true, - 256n - ], - [ - "BinaryOperation (2 ** 256)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "**", - factory.makeLiteral("", LiteralKind.Number, "", "2"), - factory.makeLiteral("", LiteralKind.Number, "", "256") - ), - true, - 115792089237316195423570985008687907853269984665640564039457584007913129639936n - ], - [ - "BinaryOperation (2 << 5)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "<<", - factory.makeLiteral("", LiteralKind.Number, "", "2"), - factory.makeLiteral("", LiteralKind.Number, "", "5") - ), - true, - 64n - ], - [ - "BinaryOperation (2 << 100)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "<<", - factory.makeLiteral("", LiteralKind.Number, "", "2"), - factory.makeLiteral("", LiteralKind.Number, "", "100") - ), - true, - 2535301200456458802993406410752n - ], - [ - "BinaryOperation (126 >> 3)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - ">>", - factory.makeLiteral("", LiteralKind.Number, "", "126"), - factory.makeLiteral("", LiteralKind.Number, "", "3") - ), - true, - 15n - ], - [ - "BinaryOperation (2535301200456458802993406410752 >> 100)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - ">>", - factory.makeLiteral( - "", - LiteralKind.Number, - "", - "2535301200456458802993406410752" - ), - factory.makeLiteral("", LiteralKind.Number, "", "100") - ), - true, - 2n - ], - [ - "BinaryOperation (11 | 116)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "|", - factory.makeLiteral("", LiteralKind.Number, "", "11"), - factory.makeLiteral("", LiteralKind.Number, "", "116") - ), - true, - 127n - ], - [ - "BinaryOperation (-11 | -116)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "|", - factory.makeLiteral("", LiteralKind.Number, "", "-11"), - factory.makeLiteral("", LiteralKind.Number, "", "-116") - ), - true, - -3n - ], - [ - "BinaryOperation (10 & 3)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "&", - factory.makeLiteral("", LiteralKind.Number, "", "10"), - factory.makeLiteral("", LiteralKind.Number, "", "3") - ), - true, - 2n - ], - [ - "BinaryOperation (-10 & -3)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "&", - factory.makeLiteral("", LiteralKind.Number, "", "-10"), - factory.makeLiteral("", LiteralKind.Number, "", "-3") - ), - true, - -12n - ], - [ - "BinaryOperation (8 ^ 10)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "^", - factory.makeLiteral("", LiteralKind.Number, "", "8"), - factory.makeLiteral("", LiteralKind.Number, "", "10") - ), - true, - 2n - ], - [ - "BinaryOperation (-8 ^ -10)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "^", - factory.makeLiteral("", LiteralKind.Number, "", "-8"), - factory.makeLiteral("", LiteralKind.Number, "", "-10") - ), - true, - 14n - ], - [ - "BinaryOperation (0.5 ^ 0.5)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "^", - factory.makeLiteral("", LiteralKind.Number, "", "0.5"), - factory.makeLiteral("", LiteralKind.Number, "", "0.5") - ), - true, - undefined - ], - [ - "BinaryOperation (1 ??? 2)", - (factory: ASTNodeFactory) => - factory.makeBinaryOperation( - "", - "???", - factory.makeLiteral("", LiteralKind.Number, "", "1"), - factory.makeLiteral("", LiteralKind.Number, "", "2") - ), - true, - undefined - ], - [ - "TupleExpression (empty)", - (factory: ASTNodeFactory) => factory.makeTupleExpression("", false, []), - false, - undefined - ], - [ - "TupleExpression ([10])", - (factory: ASTNodeFactory) => - factory.makeTupleExpression("", true, [ - factory.makeLiteral("", LiteralKind.Number, "", "10") - ]), - false, - undefined - ], - [ - "TupleExpression ((10, 20))", - (factory: ASTNodeFactory) => - factory.makeTupleExpression("", false, [ - factory.makeLiteral("", LiteralKind.Number, "", "10"), - factory.makeLiteral("", LiteralKind.Number, "", "20") - ]), - false, - undefined - ], - [ - "TupleExpression ((null))", - (factory: ASTNodeFactory) => factory.makeTupleExpression("", false, [null]), - false, - undefined - ], - [ - "TupleExpression ((10))", - (factory: ASTNodeFactory) => - factory.makeTupleExpression("", false, [ - factory.makeLiteral("", LiteralKind.Number, "", "10") - ]), - true, - 10n - ], - [ - "Conditional (true ? 10 : 1000)", - (factory: ASTNodeFactory) => - factory.makeConditional( - "", - factory.makeLiteral("", LiteralKind.Bool, "", "true"), - factory.makeLiteral("", LiteralKind.Number, "", "10"), - factory.makeLiteral("", LiteralKind.Number, "", "1000") - ), - true, - 10n - ], - [ - "Conditional (false ? 10 : 1000)", - (factory: ASTNodeFactory) => - factory.makeConditional( - "", - factory.makeLiteral("", LiteralKind.Bool, "", "false"), - factory.makeLiteral("", LiteralKind.Number, "", "10"), - factory.makeLiteral("", LiteralKind.Number, "", "1000") - ), - true, - 1000n - ], - [ - "Identifier & VariableDeclaration (A + 1, const A = 2)", - (factory: ASTNodeFactory) => { - const v = factory.makeVariableDeclaration( - true, - false, - "A", - 0, - true, - DataLocation.Default, - StateVariableVisibility.Public, - Mutability.Constant, - "uint8", - undefined, - factory.makeElementaryTypeName("uint8", "uint8"), - undefined, - factory.makeLiteral("", LiteralKind.Number, "", "2") - ); + factory.makeLiteral("", LiteralKind.Number, "", "100") + ), + true, + 2n + ], + [ + "BinaryOperation (11 | 116)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "|", + factory.makeLiteral("", LiteralKind.Number, "", "11"), + factory.makeLiteral("", LiteralKind.Number, "", "116") + ), + true, + 127n + ], + [ + "BinaryOperation (-11 | -116)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "|", + factory.makeLiteral("", LiteralKind.Number, "", "-11"), + factory.makeLiteral("", LiteralKind.Number, "", "-116") + ), + true, + -3n + ], + [ + "BinaryOperation (10 & 3)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "&", + factory.makeLiteral("", LiteralKind.Number, "", "10"), + factory.makeLiteral("", LiteralKind.Number, "", "3") + ), + true, + 2n + ], + [ + "BinaryOperation (-10 & -3)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "&", + factory.makeLiteral("", LiteralKind.Number, "", "-10"), + factory.makeLiteral("", LiteralKind.Number, "", "-3") + ), + true, + -12n + ], + [ + "BinaryOperation (8 ^ 10)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "^", + factory.makeLiteral("", LiteralKind.Number, "", "8"), + factory.makeLiteral("", LiteralKind.Number, "", "10") + ), + true, + 2n + ], + [ + "BinaryOperation (-8 ^ -10)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "^", + factory.makeLiteral("", LiteralKind.Number, "", "-8"), + factory.makeLiteral("", LiteralKind.Number, "", "-10") + ), + true, + 14n + ], + [ + "BinaryOperation (0.5 ^ 0.5)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "^", + factory.makeLiteral("", LiteralKind.Number, "", "0.5"), + factory.makeLiteral("", LiteralKind.Number, "", "0.5") + ), + true, + undefined + ], + [ + "BinaryOperation (1 ??? 2)", + (factory: ASTNodeFactory) => + factory.makeBinaryOperation( + "", + "???", + factory.makeLiteral("", LiteralKind.Number, "", "1"), + factory.makeLiteral("", LiteralKind.Number, "", "2") + ), + true, + undefined + ], + [ + "TupleExpression (empty)", + (factory: ASTNodeFactory) => factory.makeTupleExpression("", false, []), + false, + undefined + ], + [ + "TupleExpression ([10])", + (factory: ASTNodeFactory) => + factory.makeTupleExpression("", true, [ + factory.makeLiteral("", LiteralKind.Number, "", "10") + ]), + false, + undefined + ], + [ + "TupleExpression ((10, 20))", + (factory: ASTNodeFactory) => + factory.makeTupleExpression("", false, [ + factory.makeLiteral("", LiteralKind.Number, "", "10"), + factory.makeLiteral("", LiteralKind.Number, "", "20") + ]), + false, + undefined + ], + [ + "TupleExpression ((null))", + (factory: ASTNodeFactory) => factory.makeTupleExpression("", false, [null]), + false, + undefined + ], + [ + "TupleExpression ((10))", + (factory: ASTNodeFactory) => + factory.makeTupleExpression("", false, [ + factory.makeLiteral("", LiteralKind.Number, "", "10") + ]), + true, + 10n + ], + [ + "Conditional (true ? 10 : 1000)", + (factory: ASTNodeFactory) => + factory.makeConditional( + "", + factory.makeLiteral("", LiteralKind.Bool, "", "true"), + factory.makeLiteral("", LiteralKind.Number, "", "10"), + factory.makeLiteral("", LiteralKind.Number, "", "1000") + ), + true, + 10n + ], + [ + "Conditional (false ? 10 : 1000)", + (factory: ASTNodeFactory) => + factory.makeConditional( + "", + factory.makeLiteral("", LiteralKind.Bool, "", "false"), + factory.makeLiteral("", LiteralKind.Number, "", "10"), + factory.makeLiteral("", LiteralKind.Number, "", "1000") + ), + true, + 1000n + ], + [ + "Identifier & VariableDeclaration (A + 1, const A = 2)", + (factory: ASTNodeFactory) => { + const v = factory.makeVariableDeclaration( + true, + false, + "A", + 0, + true, + DataLocation.Default, + StateVariableVisibility.Public, + Mutability.Constant, + "uint8", + undefined, + factory.makeElementaryTypeName("uint8", "uint8"), + undefined, + factory.makeLiteral("", LiteralKind.Number, "", "2") + ); - return factory.makeBinaryOperation( - "uint8", - "+", - factory.makeIdentifierFor(v), - factory.makeLiteral("", LiteralKind.Number, "", "1") - ); - }, - true, - 3n - ], - [ - "Identifier & VariableDeclaration (A + 1, mutable A)", - (factory: ASTNodeFactory) => { - const v = factory.makeVariableDeclaration( - false, - false, - "A", - 0, - true, - DataLocation.Default, - StateVariableVisibility.Public, - Mutability.Mutable, - "uint8", - undefined, - factory.makeElementaryTypeName("uint8", "uint8"), - undefined - ); + return factory.makeBinaryOperation( + "uint8", + "+", + factory.makeIdentifierFor(v), + factory.makeLiteral("", LiteralKind.Number, "", "1") + ); + }, + true, + 3n + ], + [ + "Identifier & VariableDeclaration (A + 1, mutable A)", + (factory: ASTNodeFactory) => { + const v = factory.makeVariableDeclaration( + false, + false, + "A", + 0, + true, + DataLocation.Default, + StateVariableVisibility.Public, + Mutability.Mutable, + "uint8", + undefined, + factory.makeElementaryTypeName("uint8", "uint8"), + undefined + ); - return factory.makeBinaryOperation( - "uint8", - "+", - factory.makeIdentifierFor(v), - factory.makeLiteral("", LiteralKind.Number, "", "1") - ); - }, - false, - undefined - ], - [ - "Identifier & StructDefinition (invalid)", - (factory: ASTNodeFactory) => - factory.makeIdentifierFor( - factory.makeStructDefinition("SomeStruct", 0, "internal", []) - ), - false, - undefined - ], - [ - "FunctionCall (typeConversion)", - (factory: ASTNodeFactory) => - factory.makeFunctionCall( - "uint256", - FunctionCallKind.TypeConversion, - factory.makeElementaryTypeNameExpression("uint256", "uint256"), - [factory.makeLiteral("uint8", LiteralKind.Number, "", "1")] - ), - true, - 1n - ], - [ - "FunctionCall (typeConversion, mutable variable)", - (factory: ASTNodeFactory) => { - return factory.makeFunctionCall( - "uint256", - FunctionCallKind.TypeConversion, - factory.makeElementaryTypeNameExpression("uint256", "uint256"), - [ - factory.makeVariableDeclaration( - false, - false, - "A", - 0, - true, - DataLocation.Default, - StateVariableVisibility.Public, - Mutability.Mutable, - "uint8", - undefined, - factory.makeElementaryTypeName("uint8", "uint8"), - undefined - ) - ] - ); - }, - false, - undefined - ] - ]; + return factory.makeBinaryOperation( + "uint8", + "+", + factory.makeIdentifierFor(v), + factory.makeLiteral("", LiteralKind.Number, "", "1") + ); + }, + false, + undefined + ], + [ + "Identifier & StructDefinition (invalid)", + (factory: ASTNodeFactory) => + factory.makeIdentifierFor( + factory.makeStructDefinition("SomeStruct", 0, "internal", []) + ), + false, + undefined + ], + [ + "FunctionCall (typeConversion)", + (factory: ASTNodeFactory) => + factory.makeFunctionCall( + "uint256", + FunctionCallKind.TypeConversion, + factory.makeElementaryTypeNameExpression("uint256", "uint256"), + [factory.makeLiteral("uint8", LiteralKind.Number, "", "1")] + ), + true, + 1n + ], + [ + "FunctionCall (typeConversion, mutable variable)", + (factory: ASTNodeFactory) => { + return factory.makeFunctionCall( + "uint256", + FunctionCallKind.TypeConversion, + factory.makeElementaryTypeNameExpression("uint256", "uint256"), + [ + factory.makeVariableDeclaration( + false, + false, + "A", + 0, + true, + DataLocation.Default, + StateVariableVisibility.Public, + Mutability.Mutable, + "uint8", + undefined, + factory.makeElementaryTypeName("uint8", "uint8"), + undefined + ) + ] + ); + }, + false, + undefined + ], + [ + "YulLiteral (bool false)", + (factory: ASTNodeFactory) => { + return factory.makeYulLiteral(YulLiteralKind.Bool, "false", ""); + }, + true, + 0n + ], + [ + "YulLiteral (bool true)", + (factory: ASTNodeFactory) => { + return factory.makeYulLiteral(YulLiteralKind.Bool, "true", ""); + }, + true, + 1n + ], + [ + "YulLiteral (string)", + (factory: ASTNodeFactory) => { + return factory.makeYulLiteral(YulLiteralKind.String, "abcd", ""); + }, + true, + BigInt("0x61626364") << BigInt(224) + ], + [ + "YulLiteral (number)", + (factory: ASTNodeFactory) => { + return factory.makeYulLiteral(YulLiteralKind.Number, "0xff", ""); + }, + true, + 255n + ], + [ + "YulLiteral (number)", + (factory: ASTNodeFactory) => { + return factory.makeYulLiteral(YulLiteralKind.Number, "255", ""); + }, + true, + 255n + ], + [ + "YulFunctionCall (unary builtin iszero)", + (factory: ASTNodeFactory) => { + return factory.makeYulFunctionCall(factory.makeYulIdentifier("iszero"), [ + factory.makeYulLiteral(YulLiteralKind.Number, "0", "") + ]); + }, + true, + 1n + ], + [ + "YulFunctionCall (binary builtin add)", + (factory: ASTNodeFactory) => { + return factory.makeYulFunctionCall(factory.makeYulIdentifier("add"), [ + factory.makeYulLiteral(YulLiteralKind.Number, "1", ""), + factory.makeYulLiteral(YulLiteralKind.Number, "2", "") + ]); + }, + true, + 3n + ], + [ + "YulFunctionCall (binary builtin shl)", + (factory: ASTNodeFactory) => { + return factory.makeYulFunctionCall(factory.makeYulIdentifier("shl"), [ + factory.makeYulLiteral(YulLiteralKind.Number, "8", ""), + factory.makeYulLiteral(YulLiteralKind.Number, "0xff", "") + ]); + }, + true, + 65280n + ], + [ + "YulFunctionCall (binary builtin lt)", + (factory: ASTNodeFactory) => { + return factory.makeYulFunctionCall(factory.makeYulIdentifier("lt"), [ + factory.makeYulLiteral(YulLiteralKind.Number, "1", ""), + factory.makeYulLiteral(YulLiteralKind.Number, "2", "") + ]); + }, + true, + 1n + ], + [ + "YulFunctionCall (ternary builtin mulmod)", + (factory: ASTNodeFactory) => { + return factory.makeYulFunctionCall(factory.makeYulIdentifier("mulmod"), [ + factory.makeYulLiteral(YulLiteralKind.Number, "20", ""), + factory.makeYulLiteral(YulLiteralKind.Number, "10", ""), + factory.makeYulLiteral(YulLiteralKind.Number, "30", "") + ]); + }, + true, + 20n + ] +]; describe("Constant expression evaluator unit test (isConstant() + evalConstantExpr())", () => { let factory: ASTNodeFactory;