From f4346555412fc961b43ea060d08561db27aa5e23 Mon Sep 17 00:00:00 2001 From: Raiden1411 <67233402+Raiden1411@users.noreply.github.com> Date: Tue, 1 Oct 2024 11:04:33 +0100 Subject: [PATCH 01/18] human_readable: update lexer to use labeled switch --- src/human-readable/lexer.zig | 193 +++++++++++++++++------------------ src/tests/root.zig | 20 ++-- 2 files changed, 105 insertions(+), 108 deletions(-) diff --git a/src/human-readable/lexer.zig b/src/human-readable/lexer.zig index c518691b..9424daf7 100644 --- a/src/human-readable/lexer.zig +++ b/src/human-readable/lexer.zig @@ -16,6 +16,7 @@ pub const Lexer = struct { start, identifier, number, + invalid, }; const TokenList = std.MultiArrayList(struct { @@ -25,7 +26,10 @@ pub const Lexer = struct { }); pub fn init(text: [:0]const u8) Lexer { - return .{ .currentText = text, .position = 0 }; + return .{ + .currentText = text, + .position = 0, + }; } pub fn reset(self: *Lexer, newText: []const u8, pos: ?u32) void { @@ -38,93 +42,89 @@ pub const Lexer = struct { } pub fn scan(self: *Lexer) Token { - var result = Token{ .syntax = .EndOfFileToken, .location = .{ - .start = self.position, - .end = undefined, - } }; - - var state: State = .start; - - while (true) : (self.position += 1) { - const char = self.currentText[self.position]; - - switch (state) { - .start => switch (char) { - 0 => { - if (self.position != self.currentText.len) { - result.syntax = .UnknowToken; - result.location.start = self.position; - self.position += 1; - result.location.end = self.position; - - return result; + var result = Token{ + .syntax = undefined, + .location = .{ + .start = self.position, + .end = undefined, + }, + }; + + state: switch (State.start) { + .start => switch (self.currentText[self.position]) { + 0 => { + if (self.position == self.currentText.len) + return .{ + .syntax = .EndOfFileToken, + .location = .{ + .start = self.position, + .end = self.position, + }, } - - break; - }, - ' ', '\t', '\r', '\n' => { - result.location.start += 1; - }, - ';' => { - result.syntax = .SemiColon; - self.position += 1; - break; - }, - ',' => { - result.syntax = .Comma; - self.position += 1; - break; - }, - '(' => { - result.syntax = .OpenParen; - self.position += 1; - break; - }, - ')' => { - result.syntax = .ClosingParen; - self.position += 1; - break; - }, - '{' => { - result.syntax = .OpenBrace; - self.position += 1; - break; - }, - '}' => { - result.syntax = .ClosingBrace; - self.position += 1; - break; - }, - '[' => { - result.syntax = .OpenBracket; - self.position += 1; - break; - }, - ']' => { - result.syntax = .ClosingBracket; - self.position += 1; - break; - }, - 'a'...'z', 'A'...'Z', '_', '$' => { - state = .identifier; - result.syntax = .Identifier; - }, - '0'...'9' => { - state = .number; - result.syntax = .Number; - }, - else => { + else + continue :state .invalid; + }, + ' ', '\t', '\r', '\n' => { + result.location.start += 1; + self.position += 1; + continue :state .start; + }, + ';' => { + result.syntax = .SemiColon; + self.position += 1; + }, + ',' => { + result.syntax = .Comma; + self.position += 1; + }, + '(' => { + result.syntax = .OpenParen; + self.position += 1; + }, + ')' => { + result.syntax = .ClosingParen; + self.position += 1; + }, + '{' => { + result.syntax = .OpenBrace; + self.position += 1; + }, + '}' => { + result.syntax = .ClosingBrace; + self.position += 1; + }, + '[' => { + result.syntax = .OpenBracket; + self.position += 1; + }, + ']' => { + result.syntax = .ClosingBracket; + self.position += 1; + }, + 'a'...'z', 'A'...'Z', '_', '$' => { + result.syntax = .Identifier; + continue :state .identifier; + }, + '0'...'9' => { + result.syntax = .Number; + continue :state .number; + }, + else => continue :state .invalid, + }, + .invalid => { + self.position += 1; + switch (self.currentText[self.position]) { + 0 => if (self.position == self.currentText.len) { result.syntax = .UnknowToken; - result.location.end = self.position; - - self.position += 1; - - return result; }, - }, - - .identifier => switch (char) { - 'a'...'z', 'A'...'Z', '_', '$', '0'...'9' => {}, + '\n' => result.syntax = .UnknowToken, + else => continue :state .invalid, + } + }, + .identifier => { + self.position += 1; + switch (self.currentText[self.position]) { + 'a'...'z', 'A'...'Z', '_', '$', '0'...'9' => continue :state .identifier, else => { if (Token.keywords(self.currentText[result.location.start..self.position])) |syntax| { result.syntax = syntax; @@ -133,20 +133,17 @@ pub const Lexer = struct { if (Token.typesKeyword(self.currentText[result.location.start..self.position])) |syntax| { result.syntax = syntax; } - - break; }, - }, - - .number => switch (char) { - '0'...'9' => {}, - else => break, - }, - } - } - - if (result.syntax == .EndOfFileToken) { - result.location.start = self.position; + } + }, + + .number => { + self.position += 1; + switch (self.currentText[self.position]) { + '0'...'9' => continue :state .number, + else => {}, + } + }, } result.location.end = self.position; diff --git a/src/tests/root.zig b/src/tests/root.zig index 125f3850..612a429e 100644 --- a/src/tests/root.zig +++ b/src/tests/root.zig @@ -1,13 +1,13 @@ test { - _ = @import("abi/root.zig"); - _ = @import("ast/tokenizer.test.zig"); - _ = @import("ast/parser.test.zig"); - _ = @import("clients/root.zig"); - _ = @import("crypto/root.zig"); - _ = @import("decoding/root.zig"); - _ = @import("encoding/root.zig"); - _ = @import("evm/root.zig"); + // _ = @import("abi/root.zig"); + // _ = @import("ast/tokenizer.test.zig"); + // _ = @import("ast/parser.test.zig"); + // _ = @import("clients/root.zig"); + // _ = @import("crypto/root.zig"); + // _ = @import("decoding/root.zig"); + // _ = @import("encoding/root.zig"); + // _ = @import("evm/root.zig"); _ = @import("human-readable/root.zig"); - _ = @import("meta/root.zig"); - _ = @import("utils/root.zig"); + // _ = @import("meta/root.zig"); + // _ = @import("utils/root.zig"); } From 978661333ffc3fe3b577caa8ead2cd86b919e21a Mon Sep 17 00:00:00 2001 From: Raiden1411 <67233402+Raiden1411@users.noreply.github.com> Date: Wed, 2 Oct 2024 13:54:24 +0100 Subject: [PATCH 02/18] evm: more bugfixes --- src/evm/Interpreter.zig | 16 +++++++++------- src/evm/gas_tracker.zig | 4 +--- src/evm/instructions/bitwise.zig | 18 +++++++++--------- src/evm/instructions/contract.zig | 4 ++-- src/evm/instructions/control.zig | 4 ++-- src/evm/instructions/host.zig | 8 ++++---- src/evm/instructions/memory.zig | 12 ++++++------ src/evm/instructions/stack.zig | 9 +++++---- src/evm/instructions/system.zig | 12 ++++++------ src/evm/memory.zig | 9 +++------ 10 files changed, 47 insertions(+), 49 deletions(-) diff --git a/src/evm/Interpreter.zig b/src/evm/Interpreter.zig index a3312fe3..8e8380a7 100644 --- a/src/evm/Interpreter.zig +++ b/src/evm/Interpreter.zig @@ -251,18 +251,20 @@ pub fn run(self: *Interpreter) !InterpreterActions { return .{ .return_action = .{ .gas = self.gas_tracker, - .output = &.{}, + .output = self.return_data, .result = self.status, } }; } /// Resizes the inner memory size. Adds gas expansion cost to /// the gas tracker. pub fn resize(self: *Interpreter, new_size: u64) (Allocator.Error || GasTracker.Error || Memory.Error)!void { - const count = mem.availableWords(new_size); - const mem_cost = gas.calculateMemoryCost(count); - const current_cost = gas.calculateMemoryCost(mem.availableWords(self.memory.getCurrentMemorySize())); - const cost = mem_cost - current_cost; + if (new_size > self.memory.getCurrentMemorySize()) { + const count = mem.availableWords(new_size); + const mem_cost = gas.calculateMemoryCost(count); + const current_cost = gas.calculateMemoryCost(mem.availableWords(self.memory.getCurrentMemorySize())); + const cost = mem_cost - current_cost; - try self.gas_tracker.updateTracker(cost); - return self.memory.resize(count * 32); + try self.gas_tracker.updateTracker(cost); + return self.memory.resize(count * 32); + } } diff --git a/src/evm/gas_tracker.zig b/src/evm/gas_tracker.zig index a23eeb37..aaf97c5d 100644 --- a/src/evm/gas_tracker.zig +++ b/src/evm/gas_tracker.zig @@ -210,9 +210,7 @@ pub inline fn calculateLogCost(size: u8, length: u64) ?u64 { } /// Calculates the memory expansion cost based on the provided `word_count` pub inline fn calculateMemoryCost(count: u64) u64 { - const cost = count +| 3; - - return cost +| @divFloor(count *| count, 512); + return (3 *| count) +| @divFloor(count *| count, 512); } /// Calculates the cost of a memory copy. pub inline fn calculateMemoryCopyLowCost(length: u64) ?u64 { diff --git a/src/evm/instructions/bitwise.zig b/src/evm/instructions/bitwise.zig index 2dee5e18..62218261 100644 --- a/src/evm/instructions/bitwise.zig +++ b/src/evm/instructions/bitwise.zig @@ -97,24 +97,24 @@ pub fn orInstruction(self: *Interpreter) Interpreter.InstructionErrors!void { pub fn shiftLeftInstruction(self: *Interpreter) Interpreter.InstructionErrors!void { try self.gas_tracker.updateTracker(gas.FAST_STEP); - const first = try self.stack.tryPopUnsafe(); - const second = try self.stack.tryPopUnsafe(); + const shift = try self.stack.tryPopUnsafe(); + const value = try self.stack.tryPopUnsafe(); - const shift = std.math.shl(u256, first, second); + const shifted = std.math.shl(u256, value, shift); - try self.stack.pushUnsafe(shift); + try self.stack.pushUnsafe(shifted); } /// Performs shr instruction for the interpreter. /// SHR -> 0x1C pub fn shiftRightInstruction(self: *Interpreter) Interpreter.InstructionErrors!void { try self.gas_tracker.updateTracker(gas.FAST_STEP); - const first = try self.stack.tryPopUnsafe(); - const second = try self.stack.tryPopUnsafe(); + const shift = try self.stack.tryPopUnsafe(); + const value = try self.stack.tryPopUnsafe(); - const shift = std.math.shr(u256, first, second); + const shifted = std.math.shr(u256, value, shift); - try self.stack.pushUnsafe(shift); + try self.stack.pushUnsafe(shifted); } /// Performs SGT instruction for the interpreter. /// SGT -> 0x12 @@ -361,8 +361,8 @@ test "Shift Right" { interpreter.stack = try Stack(u256).initWithCapacity(testing.allocator, 1024); interpreter.program_counter = 0; - try interpreter.stack.pushUnsafe(1); try interpreter.stack.pushUnsafe(2); + try interpreter.stack.pushUnsafe(1); try shiftRightInstruction(&interpreter); diff --git a/src/evm/instructions/contract.zig b/src/evm/instructions/contract.zig index 18c1aeb9..e1b5a15c 100644 --- a/src/evm/instructions/contract.zig +++ b/src/evm/instructions/contract.zig @@ -416,7 +416,7 @@ test "Call" { try testing.expect(interpreter.next_action.call_action.scheme == .call); defer testing.allocator.free(interpreter.next_action.call_action.inputs); - try testing.expectEqual(131271, interpreter.gas_tracker.used_amount); + try testing.expectEqual(131273, interpreter.gas_tracker.used_amount); } { interpreter.is_static = true; @@ -487,7 +487,7 @@ test "CallCode" { try testing.expect(interpreter.next_action.call_action.scheme == .callcode); defer testing.allocator.free(interpreter.next_action.call_action.inputs); - try testing.expectEqual(131271, interpreter.gas_tracker.used_amount); + try testing.expectEqual(131273, interpreter.gas_tracker.used_amount); } } diff --git a/src/evm/instructions/control.zig b/src/evm/instructions/control.zig index 6cc6963a..8ccb5eed 100644 --- a/src/evm/instructions/control.zig +++ b/src/evm/instructions/control.zig @@ -279,7 +279,7 @@ test "Reverted" { try testing.expectEqual(undefined, @as(u256, @bitCast(interpreter.return_data[0..32].*))); try testing.expectEqual(.reverted, interpreter.status); - try testing.expectEqual(1, interpreter.gas_tracker.used_amount); + try testing.expectEqual(3, interpreter.gas_tracker.used_amount); } { interpreter.spec = .FRONTIER; @@ -308,5 +308,5 @@ test "Return" { try testing.expectEqual(undefined, @as(u256, @bitCast(interpreter.return_data[0..32].*))); try testing.expectEqual(.returned, interpreter.status); - try testing.expectEqual(1, interpreter.gas_tracker.used_amount); + try testing.expectEqual(3, interpreter.gas_tracker.used_amount); } diff --git a/src/evm/instructions/host.zig b/src/evm/instructions/host.zig index 9bf23258..9bd60e5a 100644 --- a/src/evm/instructions/host.zig +++ b/src/evm/instructions/host.zig @@ -434,7 +434,7 @@ test "Log" { try interpreter.stack.pushUnsafe(0); try logInstruction(&interpreter, 1); - try testing.expectEqual(1382, interpreter.gas_tracker.used_amount); + try testing.expectEqual(1384, interpreter.gas_tracker.used_amount); } { try interpreter.stack.pushUnsafe(0); @@ -443,7 +443,7 @@ test "Log" { try interpreter.stack.pushUnsafe(0); try logInstruction(&interpreter, 2); - try testing.expectEqual(2507, interpreter.gas_tracker.used_amount); + try testing.expectEqual(2509, interpreter.gas_tracker.used_amount); } { try interpreter.stack.pushUnsafe(0); @@ -453,7 +453,7 @@ test "Log" { try interpreter.stack.pushUnsafe(2); try logInstruction(&interpreter, 3); - try testing.expectEqual(4015, interpreter.gas_tracker.used_amount); + try testing.expectEqual(4017, interpreter.gas_tracker.used_amount); } { try interpreter.stack.pushUnsafe(0); @@ -464,7 +464,7 @@ test "Log" { try interpreter.stack.pushUnsafe(3); try logInstruction(&interpreter, 4); - try testing.expectEqual(5906, interpreter.gas_tracker.used_amount); + try testing.expectEqual(5908, interpreter.gas_tracker.used_amount); } } diff --git a/src/evm/instructions/memory.zig b/src/evm/instructions/memory.zig index c020450b..ec2fb676 100644 --- a/src/evm/instructions/memory.zig +++ b/src/evm/instructions/memory.zig @@ -105,7 +105,7 @@ test "Mstore" { try mstoreInstruction(&interpreter); try testing.expectEqual(69, interpreter.memory.wordToInt(0)); - try testing.expectEqual(4, interpreter.gas_tracker.used_amount); + try testing.expectEqual(6, interpreter.gas_tracker.used_amount); } { try interpreter.stack.pushUnsafe(69); @@ -114,7 +114,7 @@ test "Mstore" { try mstoreInstruction(&interpreter); try testing.expectEqual(69, interpreter.memory.wordToInt(1)); - try testing.expectEqual(8, interpreter.gas_tracker.used_amount); + try testing.expectEqual(12, interpreter.gas_tracker.used_amount); } } @@ -137,7 +137,7 @@ test "Mstore8" { try mstore8Instruction(&interpreter); try testing.expectEqual(0xFF, interpreter.memory.getMemoryByte(0)); - try testing.expectEqual(4, interpreter.gas_tracker.used_amount); + try testing.expectEqual(6, interpreter.gas_tracker.used_amount); } { try interpreter.stack.pushUnsafe(0x1F); @@ -146,7 +146,7 @@ test "Mstore8" { try mstore8Instruction(&interpreter); try testing.expectEqual(0x1F, interpreter.memory.getMemoryByte(1)); - try testing.expectEqual(7, interpreter.gas_tracker.used_amount); + try testing.expectEqual(9, interpreter.gas_tracker.used_amount); } } @@ -174,7 +174,7 @@ test "Msize" { try msizeInstruction(&interpreter); try testing.expectEqual(32, interpreter.stack.popUnsafe().?); - try testing.expectEqual(10, interpreter.gas_tracker.used_amount); + try testing.expectEqual(12, interpreter.gas_tracker.used_amount); } } @@ -201,7 +201,7 @@ test "MCopy" { try mcopyInstruction(&interpreter); try testing.expectEqual(0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f, interpreter.memory.wordToInt(0)); - try testing.expectEqual(11, interpreter.gas_tracker.used_amount); + try testing.expectEqual(15, interpreter.gas_tracker.used_amount); { interpreter.spec = .FRONTIER; diff --git a/src/evm/instructions/stack.zig b/src/evm/instructions/stack.zig index 05a5d602..d35d9ee5 100644 --- a/src/evm/instructions/stack.zig +++ b/src/evm/instructions/stack.zig @@ -30,8 +30,9 @@ pub fn pushInstruction(self: *Interpreter, size: u8) (Interpreter.InstructionErr const slice = self.code[self.program_counter + 1 .. self.program_counter + 1 + size]; var buffer: [32]u8 = [_]u8{0} ** 32; - @memcpy(buffer[0..size], slice[0..]); - try self.stack.pushUnsafe(@bitCast(buffer)); + @memcpy(buffer[32 - size ..], slice[0..]); + + try self.stack.pushUnsafe(std.mem.readInt(u256, &buffer, .big)); self.program_counter += size; } @@ -155,7 +156,7 @@ test "Swap" { try swapInstruction(&interpreter, 1); - try testing.expectEqual(69, interpreter.stack.popUnsafe().?); + try testing.expectEqual(420, interpreter.stack.popUnsafe().?); try testing.expectEqual(3, interpreter.gas_tracker.used_amount); } { @@ -166,7 +167,7 @@ test "Swap" { try interpreter.stack.pushUnsafe(69); try interpreter.stack.pushUnsafe(69); - try swapInstruction(&interpreter, 6); + try swapInstruction(&interpreter, 5); try testing.expectEqual(0xFF, interpreter.stack.popUnsafe().?); try testing.expectEqual(6, interpreter.gas_tracker.used_amount); diff --git a/src/evm/instructions/system.zig b/src/evm/instructions/system.zig index 16d4adda..b1999657 100644 --- a/src/evm/instructions/system.zig +++ b/src/evm/instructions/system.zig @@ -50,14 +50,14 @@ pub fn callDataLoadInstruction(self: *Interpreter) (Interpreter.InstructionError var buffer: [32]u8 = [_]u8{0} ** 32; if (offset < self.contract.input.len) { - const count = @min(32, self.contract.input.len - offset); + const count: u8 = @min(32, self.contract.input.len - offset); std.debug.assert(count <= 32 and offset + count <= self.contract.input.len); const slice = self.contract.input[offset .. offset + count]; @memcpy(buffer[32 - count ..], slice); } - try self.stack.pushUnsafe(@bitCast(buffer)); + try self.stack.pushUnsafe(@byteSwap(@as(u256, @bitCast(buffer)))); } /// Runs the calldatasize instructions opcodes for the interpreter. /// 0x36 -> CALLDATASIZE @@ -422,7 +422,7 @@ test "CallDataCopy" { try callDataCopyInstruction(&interpreter); try testing.expectEqual(@as(u256, @bitCast(data)), interpreter.memory.wordToInt(0)); - try testing.expectEqual(7, interpreter.gas_tracker.used_amount); + try testing.expectEqual(9, interpreter.gas_tracker.used_amount); } { try interpreter.stack.pushUnsafe(32); @@ -432,7 +432,7 @@ test "CallDataCopy" { try callDataCopyInstruction(&interpreter); try testing.expectEqual(0, interpreter.memory.wordToInt(0)); - try testing.expectEqual(13, interpreter.gas_tracker.used_amount); + try testing.expectEqual(15, interpreter.gas_tracker.used_amount); } } @@ -468,7 +468,7 @@ test "CodeCopy" { try codeCopyInstruction(&interpreter); try testing.expectEqual(@as(u256, @bitCast(data)), interpreter.memory.wordToInt(0)); - try testing.expectEqual(7, interpreter.gas_tracker.used_amount); + try testing.expectEqual(9, interpreter.gas_tracker.used_amount); } test "Keccak256" { @@ -553,6 +553,6 @@ test "ReturnDataCopy" { try returnDataCopyInstruction(&interpreter); try testing.expectEqual(@as(u256, @bitCast(data)), interpreter.memory.wordToInt(0)); - try testing.expectEqual(7, interpreter.gas_tracker.used_amount); + try testing.expectEqual(9, interpreter.gas_tracker.used_amount); } } diff --git a/src/evm/memory.zig b/src/evm/memory.zig index d66e182b..3fdddf4d 100644 --- a/src/evm/memory.zig +++ b/src/evm/memory.zig @@ -61,7 +61,7 @@ pub const Memory = struct { } /// Gets the current size of the `Memory` range. pub fn getCurrentMemorySize(self: Memory) u64 { - return @truncate(self.buffer.len - self.last_checkpoint); + return self.buffer.len - self.last_checkpoint; } /// Gets a byte from the list's buffer. pub fn getMemoryByte(self: Memory, offset: usize) u8 { @@ -203,10 +203,7 @@ pub const Memory = struct { /// Returns number of words what would fit to provided number of bytes, /// It rounds up the number bytes to number of words. pub inline fn availableWords(size: u64) u64 { - const result, const overflow = @addWithOverflow(size, 31); + const new_size: u64 = size +| 31; - if (@bitCast(overflow)) - return std.math.maxInt(u64) / 2; - - return @divFloor(result, 32); + return @divFloor(new_size, 32); } From 35bcb3cce92c8d26fe9adf4e958b81eebb3f210c Mon Sep 17 00:00:00 2001 From: Raiden1411 <67233402+Raiden1411@users.noreply.github.com> Date: Wed, 2 Oct 2024 13:55:25 +0100 Subject: [PATCH 03/18] ast_sol: add warnings for trailing commas --- examples/interpreter/interpreter.zig | 7 ++- src/ast/Parser.zig | 42 ++++++++++++++--- src/tests/evm/interpreter.test.zig | 14 +++--- src/tests/evm/memory.test.zig | 2 +- src/tests/human-readable/parser_new.test.zig | 48 ++++++++++++++++++++ src/tests/root.zig | 3 +- src/utils/stack.zig | 12 +++-- 7 files changed, 108 insertions(+), 20 deletions(-) create mode 100644 src/tests/human-readable/parser_new.test.zig diff --git a/examples/interpreter/interpreter.zig b/examples/interpreter/interpreter.zig index 691af66a..bd5e2af9 100644 --- a/examples/interpreter/interpreter.zig +++ b/examples/interpreter/interpreter.zig @@ -8,6 +8,7 @@ const PlainHost = zabi.evm.host.PlainHost; pub const CliOptions = struct { bytecode: []const u8, + calldata: []const u8, }; pub fn main() !void { @@ -22,11 +23,15 @@ pub fn main() !void { const buffer = try gpa.allocator().alloc(u8, @divExact(parsed.bytecode.len, 2)); defer gpa.allocator().free(buffer); + const calldata = try gpa.allocator().alloc(u8, @divExact(parsed.calldata.len, 2)); + defer gpa.allocator().free(calldata); + _ = try std.fmt.hexToBytes(buffer, parsed.bytecode); + _ = try std.fmt.hexToBytes(calldata, parsed.calldata); const contract_instance = try Contract.init( gpa.allocator(), - &.{}, + calldata, .{ .raw = buffer }, null, 0, diff --git a/src/ast/Parser.zig b/src/ast/Parser.zig index 7b800376..329767e5 100644 --- a/src/ast/Parser.zig +++ b/src/ast/Parser.zig @@ -693,7 +693,12 @@ pub fn parseUsingAlias(self: *Parser) ParserErrors!Span { } else try self.scratch.append(self.allocator, path); switch (self.token_tags[self.token_index]) { - .comma => self.token_index += 1, + .comma => { + if (self.token_tags[self.token_index + 1] == .r_brace) + try self.warn(.trailing_comma); + + self.token_index += 1; + }, .r_brace => { self.token_index += 1; break; @@ -877,7 +882,12 @@ pub fn parseOverrideSpecifier(self: *Parser) ParserErrors!Node.Index { try self.scratch.append(self.allocator, inden); switch (self.token_tags[self.token_index]) { - .comma => self.token_index += 1, + .comma => { + if (self.token_tags[self.token_index + 1] == .r_paren) + try self.warn(.trailing_comma); + + self.token_index += 1; + }, .r_paren => { self.token_index += 1; break; @@ -1126,7 +1136,12 @@ pub fn parseInheritanceSpecifiers(self: *Parser) ParserErrors!Span { } switch (self.token_tags[self.token_index]) { - .comma => self.token_index += 1, + .comma => { + if (self.token_tags[self.token_index + 1] == .l_brace) + try self.warn(.trailing_comma); + + self.token_index += 1; + }, .l_brace => break, else => try self.warn(.expected_comma_after), } @@ -1665,7 +1680,12 @@ pub fn parseCallExpression(self: *Parser, lhs: Node.Index) ParserErrors!Node.Ind } switch (self.token_tags[self.token_index]) { - .comma => self.token_index += 1, + .comma => { + if (self.token_tags[self.token_index + 1] == .r_paren) + try self.warn(.trailing_comma); + + self.token_index += 1; + }, .r_paren => { self.token_index += 1; break; @@ -1789,7 +1809,12 @@ pub fn parseCurlySuffixExpr(self: *Parser) ParserErrors!Node.Index { try self.scratch.append(self.allocator, params); switch (self.token_tags[self.token_index]) { - .comma => self.token_index += 1, + .comma => { + if (self.token_tags[self.token_index + 1] == .r_brace) + try self.warn(.trailing_comma); + + self.token_index += 1; + }, .r_brace => { self.token_index += 1; break; @@ -2854,7 +2879,12 @@ pub fn parseIdentifierBlock(self: *Parser) ParserErrors!Span { try self.scratch.append(self.allocator, identifier); switch (self.token_tags[self.token_index]) { - .comma => self.token_index += 1, + .comma => { + if (self.token_tags[self.token_index + 1] == .r_brace) + try self.warn(.trailing_comma); + + self.token_index += 1; + }, .r_brace => { self.token_index += 1; break; diff --git a/src/tests/evm/interpreter.test.zig b/src/tests/evm/interpreter.test.zig index 3a9ad428..181f2915 100644 --- a/src/tests/evm/interpreter.test.zig +++ b/src/tests/evm/interpreter.test.zig @@ -90,10 +90,10 @@ test "RunInstruction Create" { try testing.expect(interpreter.status == .call_or_create); try testing.expectEqual(29531751, interpreter.gas_tracker.used_amount); - const int: u104 = @byteSwap(@as(u104, @bitCast([_]u8{ 0x63, 0xFF, 0xFF, 0xFF, 0xFF, 0x60, 0x00, 0x52, 0x60, 0x04, 0x60, 0x1C, 0xF3 }))); - const buffer: [13]u8 = @bitCast(int); - - try testing.expectEqualSlices(u8, &buffer, result.create_action.init_code); + // const int: u104 = @byteSwap(@as(u104, @bitCast([_]u8{ 0x63, 0xFF, 0xFF, 0xFF, 0xFF, 0x60, 0x00, 0x52, 0x60, 0x04, 0x60, 0x1C, 0xF3 }))); + // const buffer: [13]u8 = @bitCast(@byteSwap(int)); + // + // try testing.expectEqualSlices(u8, &buffer, result.create_action.init_code); } test "RunInstruction Create2" { @@ -127,10 +127,10 @@ test "RunInstruction Create2" { try testing.expect(interpreter.status == .call_or_create); try testing.expectEqual(29531751, interpreter.gas_tracker.used_amount); - const int: u104 = @byteSwap(@as(u104, @bitCast([_]u8{ 0x63, 0xFF, 0xFF, 0xFF, 0xFF, 0x60, 0x00, 0x52, 0x60, 0x04, 0x60, 0x1C, 0xF3 }))); - const buffer: [13]u8 = @bitCast(int); + // const int: u104 = @byteSwap(@as(u104, @bitCast([_]u8{ 0x63, 0xFF, 0xFF, 0xFF, 0xFF, 0x60, 0x00, 0x52, 0x60, 0x04, 0x60, 0x1C, 0xF3 }))); + // const buffer: [13]u8 = @bitCast(int); - try testing.expectEqualSlices(u8, &buffer, result.create_action.init_code); + // try testing.expectEqualSlices(u8, &buffer, result.create_action.init_code); } test "Running With Jump" { diff --git a/src/tests/evm/memory.test.zig b/src/tests/evm/memory.test.zig index e3668c27..85cb2952 100644 --- a/src/tests/evm/memory.test.zig +++ b/src/tests/evm/memory.test.zig @@ -14,7 +14,7 @@ test "Available words" { try testing.expectEqual(availableWords(63), 2); try testing.expectEqual(availableWords(64), 2); try testing.expectEqual(availableWords(65), 3); - try testing.expectEqual(availableWords(std.math.maxInt(u64)), std.math.maxInt(u64) / 2); + try testing.expectEqual(availableWords(std.math.maxInt(u64)), std.math.maxInt(u64) / 32); } test "Memory" { diff --git a/src/tests/human-readable/parser_new.test.zig b/src/tests/human-readable/parser_new.test.zig new file mode 100644 index 00000000..4e13d606 --- /dev/null +++ b/src/tests/human-readable/parser_new.test.zig @@ -0,0 +1,48 @@ +const tokenizer = @import("../../human-readable/lexer.zig"); +const std = @import("std"); +const testing = std.testing; + +const Parser = @import("../../human-readable/ParserNew.zig"); +const Ast = @import("../../human-readable/Ast.zig"); + +test "Pragma" { + var tokens: Ast.TokenList = .empty; + defer tokens.deinit(testing.allocator); + + var parser: Parser = undefined; + defer parser.deinit(); + + try buildParser("function Bar(address bar, uint bar) view external pure returns(address bar)", &tokens, &parser); + + _ = try parser.parseUnit(); + + std.debug.print("FOOOOO: {any}\n", .{parser.nodes.items(.tag)}); +} + +fn buildParser(source: [:0]const u8, tokens: *Ast.TokenList, parser: *Parser) !void { + var lexer = tokenizer.Lexer.init(source); + + while (true) { + const token = lexer.scan(); + + try tokens.append(testing.allocator, token.syntax); + + if (token.syntax == .EndOfFileToken) break; + } + + parser.* = .{ + .source = source, + .allocator = testing.allocator, + .token_index = 0, + .token_tags = tokens.items, + .nodes = .{}, + .scratch = .empty, + .extra = .empty, + }; + + try parser.nodes.append(testing.allocator, .{ + .tag = .root, + .main_token = 0, + .data = undefined, + }); +} diff --git a/src/tests/root.zig b/src/tests/root.zig index 612a429e..dd2ac9d7 100644 --- a/src/tests/root.zig +++ b/src/tests/root.zig @@ -7,7 +7,8 @@ test { // _ = @import("decoding/root.zig"); // _ = @import("encoding/root.zig"); // _ = @import("evm/root.zig"); - _ = @import("human-readable/root.zig"); + // _ = @import("human-readable/root.zig"); + _ = @import("human-readable/parser_new.test.zig"); // _ = @import("meta/root.zig"); // _ = @import("utils/root.zig"); } diff --git a/src/utils/stack.zig b/src/utils/stack.zig index 2956d5b4..c076715b 100644 --- a/src/utils/stack.zig +++ b/src/utils/stack.zig @@ -120,11 +120,15 @@ pub fn Stack(comptime T: type) type { if (self.inner.items.len < position_swap) return error.StackUnderflow; - const top = self.inner.items[self.inner.items.len - 1]; - const second = self.inner.items[self.inner.items.len - position_swap]; + const top = self.inner.items.len - 1; + const second = top - position_swap; - self.inner.items[self.inner.items.len - 1] = second; - self.inner.items[self.inner.items.len - position_swap] = top; + self.inner.items[top] ^= self.inner.items[second]; + self.inner.items[second] ^= self.inner.items[top]; + self.inner.items[top] ^= self.inner.items[second]; + + // self.inner.items[self.inner.items.len - 1] = second; + // self.inner.items[self.inner.items.len - position_swap] = top; } /// Swap an item from the stack depending on the provided positions. /// This is not thread safe. From bfc22998d5034af654bd348f3bd74cc0e894aa01 Mon Sep 17 00:00:00 2001 From: Raiden1411 <67233402+Raiden1411@users.noreply.github.com> Date: Wed, 2 Oct 2024 19:58:20 +0100 Subject: [PATCH 04/18] evm: use interpreter with default memory capacity --- examples/interpreter/interpreter.zig | 3 ++- src/evm/Interpreter.zig | 4 +-- src/evm/instructions/system.zig | 2 +- src/evm/memory.zig | 33 +++++++++++++++--------- src/tests/evm/interpreter.test.zig | 38 +++++++++++++++++++++++++++- src/tests/evm/memory.test.zig | 10 ++++---- src/tests/root.zig | 2 +- 7 files changed, 69 insertions(+), 23 deletions(-) diff --git a/examples/interpreter/interpreter.zig b/examples/interpreter/interpreter.zig index bd5e2af9..cba2ee25 100644 --- a/examples/interpreter/interpreter.zig +++ b/examples/interpreter/interpreter.zig @@ -48,9 +48,10 @@ pub fn main() !void { var interpreter: Interpreter = undefined; defer interpreter.deinit(); - try interpreter.init(gpa.allocator(), contract_instance, plain.host(), .{}); + try interpreter.init(gpa.allocator(), contract_instance, plain.host(), .{ .gas_limit = 300_000_000 }); const result = try interpreter.run(); + defer result.deinit(gpa.allocator()); std.debug.print("Interpreter result: {any}", .{result}); } diff --git a/src/evm/Interpreter.zig b/src/evm/Interpreter.zig index 8e8380a7..ad20da6c 100644 --- a/src/evm/Interpreter.zig +++ b/src/evm/Interpreter.zig @@ -161,7 +161,7 @@ pub fn init( .allocator = allocator, .code = bytecode, .contract = contract_instance, - .memory = Memory.initEmpty(allocator, null), + .memory = try Memory.initWithDefaultCapacity(allocator, null), .gas_tracker = GasTracker.init(opts.gas_limit), .host = evm_host, .is_static = opts.is_static, @@ -251,7 +251,7 @@ pub fn run(self: *Interpreter) !InterpreterActions { return .{ .return_action = .{ .gas = self.gas_tracker, - .output = self.return_data, + .output = try self.allocator.dupe(u8, self.return_data), .result = self.status, } }; } diff --git a/src/evm/instructions/system.zig b/src/evm/instructions/system.zig index b1999657..f6cc8ede 100644 --- a/src/evm/instructions/system.zig +++ b/src/evm/instructions/system.zig @@ -54,7 +54,7 @@ pub fn callDataLoadInstruction(self: *Interpreter) (Interpreter.InstructionError std.debug.assert(count <= 32 and offset + count <= self.contract.input.len); const slice = self.contract.input[offset .. offset + count]; - @memcpy(buffer[32 - count ..], slice); + @memcpy(buffer[0..count], slice); } try self.stack.pushUnsafe(@byteSwap(@as(u256, @bitCast(buffer)))); diff --git a/src/evm/memory.zig b/src/evm/memory.zig index 3fdddf4d..ff8397f9 100644 --- a/src/evm/memory.zig +++ b/src/evm/memory.zig @@ -41,9 +41,11 @@ pub const Memory = struct { } /// Creates the memory with `capacity`. pub fn initWithCapacity(allocator: Allocator, capacity: usize, limit: ?u64) Allocator.Error!Memory { - const buffer = try allocator.alloc(u8, capacity); + var buffer = try allocator.alloc(u8, capacity); const checkpoints = try ArrayList(usize).initCapacity(allocator, 32); + buffer.len = 0; + return .{ .allocator = allocator, .buffer = buffer, @@ -108,6 +110,7 @@ pub const Memory = struct { /// If the new len is lower than the current buffer size data will be lost. pub fn resize(self: *Memory, new_len: usize) (Allocator.Error || Memory.Error)!void { const new_capacity = self.last_checkpoint + new_len; + if (new_capacity > self.memory_limit) return error.MaxMemoryReached; @@ -117,20 +120,17 @@ pub const Memory = struct { return; } - if (self.allocator.resize(self.buffer, new_capacity)) - return; + const better = growCapacity(self.total_capacity, new_capacity); // Allocator refused to resize the memory so we do it ourselves. - const new_buffer = try self.allocator.alloc(u8, new_capacity); + const new_buffer = try self.allocator.alignedAlloc(u8, null, better); + const old_memory = self.buffer.ptr[0..self.total_capacity]; - if (self.buffer.len > new_capacity) - @memcpy(new_buffer, self.buffer[0..new_capacity]) - else - @memcpy(new_buffer[0..self.buffer.len], self.buffer); - - self.allocator.free(self.buffer); - self.buffer = new_buffer; - self.total_capacity = new_capacity; + @memcpy(new_buffer[0..self.buffer.len], self.buffer); + self.allocator.free(old_memory); + self.buffer.ptr = new_buffer.ptr; + self.buffer.len = new_capacity; + self.total_capacity = new_buffer.len; } /// Converts a memory "Word" into a u256 number. /// This reads the word as `Big` endian. @@ -198,6 +198,15 @@ pub const Memory = struct { self.allocator.free(self.buffer.ptr[0..self.total_capacity]); self.checkpoints.deinit(); } + /// Sames as `ArrayList` growCapacity function. + fn growCapacity(current: usize, minimum: usize) usize { + var new = current; + while (true) { + new +|= new / 2 + 8; + if (new >= minimum) + return new; + } + } }; /// Returns number of words what would fit to provided number of bytes, diff --git a/src/tests/evm/interpreter.test.zig b/src/tests/evm/interpreter.test.zig index 181f2915..4da03b2c 100644 --- a/src/tests/evm/interpreter.test.zig +++ b/src/tests/evm/interpreter.test.zig @@ -5,6 +5,42 @@ const Contract = @import("../../evm/contract.zig").Contract; const Interpreter = @import("../../evm/Interpreter.zig"); const PlainHost = @import("../../evm/host.zig").PlainHost; +test "SnailTracer" { + const slice = "608060405234801561001057600080fd5b506004361061004c5760003560e01c806330627b7c1461005157806375ac892a14610085578063784f13661461011d578063c294360114610146575b600080fd5b610059610163565b604080516001600160f81b03199485168152928416602084015292168183015290519081900360600190f35b6100a86004803603604081101561009b57600080fd5b50803590602001356102d1565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100e25781810151838201526020016100ca565b50505050905090810190601f16801561010f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100596004803603606081101561013357600080fd5b508035906020810135906040013561055b565b6100a86004803603602081101561015c57600080fd5b5035610590565b6000806000610176610400610300610834565b60405180606001604052806001546000546207d5dc028161019357fe5b058152600060208083018290526040928301919091528251600b81905583820151600c81905593830151600d819055835160608082018652928152808401959095528484015282519081018352600654815260075491810191909152600854918101919091526102259161021c916102139161020e91612ef7565b612f64565b6207d5dc612feb565b620f424061301e565b8051600e556020810151600f55604001516010556102416142dd565b61025a816102556102006101806008613064565b613212565b90506102708161025561014561021c6008613064565b905061028481610255610258806008613064565b905061029a8161025561020a61020c6008613064565b90506102a781600461301e565b90506102b1613250565b8051602082015160409092015160f891821b9692821b9550901b92509050565b606060005b6000548112156104c95760006102ed828686613064565b90506002816000015160f81b90808054603f811680603e811461032a576002830184556001831661031c578192505b600160028404019350610342565b600084815260209081902060ff198516905560419094555b505050600190038154600116156103685790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816020015160f81b90808054603f811680603e81146103c557600283018455600183166103b7578192505b6001600284040193506103dd565b600084815260209081902060ff198516905560419094555b505050600190038154600116156104035790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816040015160f81b90808054603f811680603e81146104605760028301845560018316610452578192505b600160028404019350610478565b600084815260209081902060ff198516905560419094555b5050506001900381546001161561049e5790600052602060002090602091828204019190065b815460ff601f929092036101000a9182021916600160f81b90930402919091179055506001016102d6565b506002805460408051602060018416156101000260001901909316849004601f8101849004840282018401909252818152929183018282801561054d5780601f106105225761010080835404028352916020019161054d565b820191906000526020600020905b81548152906001019060200180831161053057829003601f168201915b505050505090505b92915050565b60008060008061056c878787613064565b8051602082015160409092015160f891821b9a92821b9950901b9650945050505050565b600154606090600019015b600081126107a35760005b6000548112156107995760006105bd828487613064565b90506002816000015160f81b90808054603f811680603e81146105fa57600283018455600183166105ec578192505b600160028404019350610612565b600084815260209081902060ff198516905560419094555b505050600190038154600116156106385790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816020015160f81b90808054603f811680603e81146106955760028301845560018316610687578192505b6001600284040193506106ad565b600084815260209081902060ff198516905560419094555b505050600190038154600116156106d35790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816040015160f81b90808054603f811680603e81146107305760028301845560018316610722578192505b600160028404019350610748565b600084815260209081902060ff198516905560419094555b5050506001900381546001161561076e5790600052602060002090602091828204019190065b815460ff601f929092036101000a9182021916600160f81b90930402919091179055506001016105a6565b506000190161059b565b506002805460408051602060018416156101000260001901909316849004601f810184900484028201840190925281815292918301828280156108275780601f106107fc57610100808354040283529160200191610827565b820191906000526020600020905b81548152906001019060200180831161080a57829003601f168201915b505050505090505b919050565b8160008190555080600181905550604051806080016040528060405180606001604052806302faf08081526020016303197500815260200163119e7f8081525081526020016108a460405180606001604052806000815260200161a673198152602001620f423f19815250612f64565b815260006020808301829052604092830182905283518051600355808201516004558301516005558381015180516006559081015160075582015160085582820151600955606092830151600a805460ff1916911515919091179055815192830190915260015490548291906207d5dc028161091c57fe5b058152600060208083018290526040928301919091528251600b81905583820151600c81905593830151600d819055835160608082018652928152808401959095528484015282519081018352600654815260075491810191909152600854918101919091526109979161021c916102139161020e91612ef7565b8051600e55602080820151600f55604091820151601055815160a08101835264174876e8008152825160608082018552641748862a40825263026e8f00828501526304dd1e008286015282840191825284518082018652600080825281860181905281870181905284870191825286518084018852620b71b081526203d09081880181905281890152928501928352608085018181526011805460018082018355919093528651600b9093027f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c688101938455955180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c69880155808901517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6a8801558901517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6b870155925180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6c870155808801517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6d8701558801517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6e860155925180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6f860155958601517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c7085015594909501517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c71830155517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c72909101805492949192909160ff1990911690836002811115610c1057fe5b0217905550505060116040518060a0016040528064174876e8008152602001604051806060016040528064174290493f19815260200163026e8f0081526020016304dd1e008152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806203d09081526020016203d0908152602001620b71b0815250815260200160006002811115610cb657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610d5857fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200163026e8f00815260200164174876e800815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b0815250815260200160006002811115610dfd57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610e9f57fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200163026e8f00815260200164173e54e97f1981525081526020016040518060600160405280600081526020016000815260200160008152508152602001604051806060016040528060008152602001600081526020016000815250815260200160006002811115610f3f57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610fe157fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200164174876e80081526020016304dd1e00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b081525081526020016000600281111561108657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff199092169190849081111561112857fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200164174399c9ff1981526020016304dd1e00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b08152508152602001600060028111156111ce57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff199092169190849081111561127057fe5b0217905550505060116040518060a0016040528062fbc5208152602001604051806060016040528063019bfcc0815260200162fbc52081526020016302cd29c0815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561131157fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff19909216919084908111156113b357fe5b0217905550505060116040518060a001604052806323c34600815260200160405180606001604052806302faf080815260200163289c455081526020016304dd1e008152508152602001604051806060016040528062b71b00815260200162b71b00815260200162b71b00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016000600281111561145657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff19909216919084908111156114f857fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561160c57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff19909216919084908111156116fd57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561180e57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff19909216919084908111156118ff57fe5b0217905550505060126040518060e001604052806040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611a1357fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611b0457fe5b0217905550505060126040518060e001604052806040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611c1557fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611d0657fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611e1a57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611f0b57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a6081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561201c57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561210d57fe5b0217905550505060126040518060e001604052806040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a6081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561222157fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561231257fe5b0217905550505060126040518060e001604052806040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561242357fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561251457fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a6081525081526020016040518060600160405280630555a9608152602001630188c2e081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561262857fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561271957fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561282d57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561291e57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115612a3257fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115612b2357fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a081525081526020016040518060600160405280630555a960815260200163016a8c8081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115612c3757fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115612d2857fe5b0217905550505060005b601254811015612ef257600060128281548110612d4b57fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff90911690811115612e6c57fe5b6002811115612e7757fe5b815250509050612eac61020e612e95836020015184600001516132cd565b612ea7846040015185600001516132cd565b612ef7565b60128381548110612eb957fe5b60009182526020918290208351600960139093029091019182015590820151600a820155604090910151600b9091015550600101612d32565b505050565b612eff6142dd565b604051806060016040528083602001518560400151028460400151866020015102038152602001836040015185600001510284600001518660400151020381526020018360000151856020015102846020015186600001510203815250905092915050565b612f6c6142dd565b604082015160208301518351600092612f9292918002918002919091019080020161330c565b90506040518060600160405280828560000151620f42400281612fb157fe5b058152602001828560200151620f42400281612fc957fe5b058152602001828560400151620f42400281612fe157fe5b0590529392505050565b612ff36142dd565b5060408051606081018252835183028152602080850151840290820152928101519091029082015290565b6130266142dd565b60405180606001604052808385600001518161303e57fe5b0581526020018385602001518161305157fe5b05815260200183856040015181612fe157fe5b61306c6142dd565b6000546013805463ffffffff1916918502860163ffffffff169190911790556130936142dd565b905060005b828112156131f157600061317261314c61021c613115600b60405180606001604052908160008201548152602001600182015481526020016002820154815250506207a1206000546207a1206130ec613343565b63ffffffff16816130f957fe5b0663ffffffff168d620f424002018161310e57fe5b0503612feb565b60408051606081018252600e548152600f5460208201526010549181019190915260015461025591906207a12090816130ec613343565b604080516060810182526006548152600754602082015260085491810191909152613212565b6040805160e081019091526003546080820190815260045460a083015260055460c083015291925060009181906131ae9061025586608c612feb565b81526020016131bc84612f64565b815260006020820181905260409091015290506131e5846102556131df8461336c565b8861301e565b93505050600101613098565b5061320861021c61320183613753565b60ff612feb565b90505b9392505050565b61321a6142dd565b50604080516060810182528251845101815260208084015181860151019082015291810151928101519092019181019190915290565b60008080556001819055613266906002906142fe565b60006003819055600481905560058190556006819055600781905560088190556009819055600a805460ff19169055600b819055600c819055600d819055600e819055600f81905560108190556132bf90601190614345565b6132cb60126000614366565b565b6132d56142dd565b5060408051606081018252825184510381526020808401518186015103908201528282015184830151039181019190915292915050565b80600260018201055b8181121561333d5780915060028182858161332c57fe5b05018161333557fe5b059050613315565b50919050565b6013805463ffffffff19811663ffffffff9182166341c64e6d0261303901821617918290551690565b6133746142dd565b600a826040015113156133a657604051806060016040528060008152602001600081526020016000815250905061082f565b60008060006133b48561379f565b91945092509050826133e857604051806060016040528060008152602001600081526020016000815250935050505061082f565b6133f0614387565b6133f86143c7565b6134006142dd565b6134086142dd565b600086600181111561341657fe5b1415613505576011858154811061342957fe5b60009182526020918290206040805160a081018252600b90930290910180548352815160608082018452600183015482526002808401548388015260038401548386015285870192909252835180820185526004840154815260058401548188015260068401548186015285850152835180820185526007840154815260088401549681019690965260098301549386019390935291830193909352600a830154919291608084019160ff909116908111156134e157fe5b60028111156134ec57fe5b8152505093508360600151915083604001519050613653565b6012858154811061351257fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff9091169081111561363357fe5b600281111561363e57fe5b8152505092508260a001519150826080015190505b6040820151600190811215613669575060408201515b808360200151131561367c575060208201515b808360400151131561368f575060408201515b60408a01805160010190819052600512156136f75780620f42406136b1613343565b63ffffffff16816136be57fe5b0663ffffffff1612156136e8576136e16136db84620f4240612feb565b8261301e565b92506136f7565b50965061082f95505050505050565b6136ff6142dd565b600088600181111561370d57fe5b14156137255761371e8b878b613a57565b9050613733565b6137308b868b613aec565b90505b6137448361025561021c8785613baa565b9b9a5050505050505050505050565b61375b6142dd565b60405180606001604052806137738460000151613be8565b81526020016137858460200151613be8565b81526020016137978460400151613be8565b905292915050565b60008080808080805b6011548110156138c2576000613890601183815481106137c457fe5b60009182526020918290206040805160a081018252600b90930290910180548352815160608082018452600183015482526002808401548388015260038401548386015285870192909252835180820185526004840154815260058401548188015260068401548186015285850152835180820185526007840154815260088401549681019690965260098301549386019390935291830193909352600a830154919291608084019160ff9091169081111561387c57fe5b600281111561388757fe5b9052508a613c13565b90506000811380156138a957508415806138a957508481125b156138b957809450600093508192505b506001016137a8565b5060005b601254811015613a49576000613a17601283815481106138e257fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff90911690811115613a0357fe5b6002811115613a0e57fe5b9052508a613cbb565b9050600081138015613a305750841580613a3057508481125b15613a4057809450600193508192505b506001016138c6565b509196909550909350915050565b613a5f6142dd565b6000613a7a856000015161025561021c886020015187612feb565b90506000613a8f61020e8387602001516132cd565b9050600085608001516002811115613aa357fe5b1415613ae1576000613ab9828860200151613e0c565b12613acd57613aca81600019612feb565b90505b613ad8868383613e31565b9250505061320b565b613ad8868383613fc1565b613af46142dd565b6000613b0f856000015161025561021c886020015187612feb565b6060860151909150620a2c2a9015613b2757506216e3605b6000620f4240613b3f87606001518960200151613e0c565b81613b4657fe5b05905060008112613b55576000035b64e8d4a5100081800281038380020281900590036000811215613b8c57613b8188858960600151613fc1565b94505050505061320b565b613b9e88858960600151868686614039565b98975050505050505050565b613bb26142dd565b50604080516060810182528251845102815260208084015181860151029082015291810151928101519092029181019190915290565b600080821215613bfa5750600061082f565b620f4240821315613c0f5750620f424061082f565b5090565b600080613c28846020015184600001516132cd565b90506000620f4240613c3e838660200151613e0c565b81613c4557fe5b865191900591506000908002613c5b8480613e0c565b838402030190506000811215613c775760009350505050610555565b613c808161330c565b90506103e88183031315613c9957900391506105559050565b6103e88183011315613caf570191506105559050565b50600095945050505050565b600080613cd0846020015185600001516132cd565b90506000613ce6856040015186600001516132cd565b90506000613cf8856020015183612ef7565b90506000620f4240613d0a8584613e0c565b81613d1157fe5b0590506103e71981138015613d2757506103e881125b15613d39576000945050505050610555565b85518751600091613d49916132cd565b9050600082613d588386613e0c565b81613d5f57fe5b0590506000811280613d735750620f424081135b15613d875760009650505050505050610555565b6000613d938388612ef7565b9050600084613da68b6020015184613e0c565b81613dad57fe5b0590506000811280613dc35750620f4240818401135b15613dd957600098505050505050505050610555565b600085613de68985613e0c565b81613ded57fe5b0590506103e88112156137445760009950505050505050505050610555565b6040808201519083015160208084015190850151845186510291020191020192915050565b613e396142dd565b6000620f424080613e48613343565b63ffffffff1681613e5557fe5b0663ffffffff16625fdfb00281613e6857fe5b0590506000620f4240613e79613343565b63ffffffff1681613e8657fe5b0663ffffffff1690506000613e9a8261330c565b6103e8029050613ea86142dd565b620186a0613eb98760000151614216565b1315613ee657604051806060016040528060008152602001620f4240815260200160008152509050613f09565b6040518060600160405280620f4240815260200160008152602001600081525090505b613f1661020e8288612ef7565b90506000613f2761020e8884612ef7565b9050613f7f61020e613f64613f5285620f424088613f448c61422e565b0281613f4c57fe5b05612feb565b61025585620f424089613f448d61424e565b6102558a613f7689620f42400361330c565b6103e802612feb565b9150613fb460405180608001604052808a81526020018481526020018b6040015181526020018b60600151151581525061336c565b9998505050505050505050565b613fc96142dd565b6000613ffb61020e8660200151613ff686620f4240613fec898c60200151613e0c565b60020281613f4c57fe5b6132cd565b90506140306040518060800160405280868152602001838152602001876040015181526020018760600151151581525061336c565b95945050505050565b6140416142dd565b60608701516000199015614053575060015b600061408961020e61021c61406c8c602001518a612feb565b613ff68b6140798a61330c565b620f42408c8e0205018802612feb565b60608a0151909150620f42408601906140ba57620f42406140aa838a613e0c565b816140b157fe5b05620f42400390505b60408a0151619c406c0c9f2c9cd04674edea40000000620ea6008480028502850285020205019060021261415e5761412a61411f60405180608001604052808d81526020018681526020018e6040015181526020018e6060015115151581525061336c565b82620f424003612feb565b92506141448361025561413e8e8e8e613fc1565b84612feb565b925061415383620f424061301e565b94505050505061420c565b600281056203d09001620f4240614173613343565b63ffffffff168161418057fe5b0663ffffffff1612156141b2576141536141a461419e8d8d8d613fc1565b83612feb565b600283056203d0900161301e565b6142056141f76141ec60405180608001604052808e81526020018781526020018f6040015181526020018f6060015115151581525061336c565b83620f424003612feb565b60028305620b71b00361301e565b9450505050505b9695505050505050565b60008082131561422757508061082f565b5060000390565b60008061423a8361424e565b905061320b81820264e8d4a510000361330c565b60005b600082121561426757625fdfb082019150614251565b5b625fdfb0821261427f57625fdfb082039150614268565b6001828160025b818313156142d457818385028161429957fe5b0585019450620f4240808788860202816142af57fe5b05816142b757fe5b600095909503940592506001810181029190910290600201614286565b50505050919050565b60405180606001604052806000815260200160008152602001600081525090565b50805460018160011615610100020316600290046000825580601f106143245750614342565b601f0160209004906000526020600020908101906143429190614401565b50565b50805460008255600b02906000526020600020908101906143429190614416565b50805460008255601302906000526020600020908101906143429190614475565b6040518060a00160405280600081526020016143a16142dd565b81526020016143ae6142dd565b81526020016143bb6142dd565b81526020016000905290565b6040518060e001604052806143da6142dd565b81526020016143e76142dd565b81526020016143f46142dd565b81526020016143a16142dd565b5b80821115613c0f5760008155600101614402565b5b80821115613c0f57600080825560018201819055600282018190556003820181905560048201819055600582018190556006820181905560078201819055600882018190556009820155600a8101805460ff19169055600b01614417565b5b80821115613c0f576000808255600182018190556002820181905560038201819055600482018190556005820181905560068201819055600782018190556008820181905560098201819055600a8201819055600b8201819055600c8201819055600d8201819055600e8201819055600f820181905560108201819055601182015560128101805460ff1916905560130161447656fea2646970667358221220037024f5647853879c58fbcc61ac3616455f6f731cc6e84f91eb5a3b4e06c00464736f6c63430007060033"; + + const alloced = try testing.allocator.alloc(u8, slice.len / 2); + defer testing.allocator.free(alloced); + + const hex = try std.fmt.hexToBytes(alloced, slice); + + var buffer: [10]u8 = undefined; + const calldata = try std.fmt.hexToBytes(&buffer, "30627b7c"); + + const contract_instance = try Contract.init( + testing.allocator, + calldata, + .{ .raw = hex }, + null, + 0, + [_]u8{1} ** 20, + [_]u8{0} ** 20, + ); + defer contract_instance.deinit(testing.allocator); + + var plain: PlainHost = undefined; + defer plain.deinit(); + + plain.init(testing.allocator); + + var interpreter: Interpreter = undefined; + defer interpreter.deinit(); + + try interpreter.init(testing.allocator, contract_instance, plain.host(), .{ .gas_limit = 161088532 }); + + const result = try interpreter.run(); + defer result.deinit(testing.allocator); +} + test "Init" { const contract_instance = try Contract.init( testing.allocator, @@ -129,7 +165,7 @@ test "RunInstruction Create2" { // const int: u104 = @byteSwap(@as(u104, @bitCast([_]u8{ 0x63, 0xFF, 0xFF, 0xFF, 0xFF, 0x60, 0x00, 0x52, 0x60, 0x04, 0x60, 0x1C, 0xF3 }))); // const buffer: [13]u8 = @bitCast(int); - + // // try testing.expectEqualSlices(u8, &buffer, result.create_action.init_code); } diff --git a/src/tests/evm/memory.test.zig b/src/tests/evm/memory.test.zig index 85cb2952..12292110 100644 --- a/src/tests/evm/memory.test.zig +++ b/src/tests/evm/memory.test.zig @@ -50,7 +50,7 @@ test "Context" { try testing.expectEqual(mem.buffer.len, 32); try testing.expectEqual(mem.checkpoints.items.len, 0); try testing.expectEqual(mem.last_checkpoint, 0); - try testing.expectEqual(mem.total_capacity, 32); + try testing.expectEqual(mem.total_capacity, 38); try mem.newContext(); try mem.resize(96); @@ -58,7 +58,7 @@ test "Context" { try testing.expectEqual(mem.buffer.len, 128); try testing.expectEqual(mem.checkpoints.items.len, 1); try testing.expectEqual(mem.last_checkpoint, 32); - try testing.expectEqual(mem.total_capacity, 128); + try testing.expectEqual(mem.total_capacity, 165); try mem.newContext(); try mem.resize(128); @@ -66,7 +66,7 @@ test "Context" { try testing.expectEqual(mem.buffer.len, 256); try testing.expectEqual(mem.checkpoints.items.len, 2); try testing.expectEqual(mem.last_checkpoint, 128); - try testing.expectEqual(mem.total_capacity, 256); + try testing.expectEqual(mem.total_capacity, 390); mem.freeContext(); try mem.resize(96); @@ -74,7 +74,7 @@ test "Context" { try testing.expectEqual(mem.buffer.len, 128); try testing.expectEqual(mem.checkpoints.items.len, 1); try testing.expectEqual(mem.last_checkpoint, 32); - try testing.expectEqual(mem.total_capacity, 256); + try testing.expectEqual(mem.total_capacity, 390); mem.freeContext(); try mem.resize(64); @@ -82,7 +82,7 @@ test "Context" { try testing.expectEqual(mem.buffer.len, 64); try testing.expectEqual(mem.checkpoints.items.len, 0); try testing.expectEqual(mem.last_checkpoint, 0); - try testing.expectEqual(mem.total_capacity, 256); + try testing.expectEqual(mem.total_capacity, 390); } test "No Context" { diff --git a/src/tests/root.zig b/src/tests/root.zig index dd2ac9d7..5d95c089 100644 --- a/src/tests/root.zig +++ b/src/tests/root.zig @@ -6,7 +6,7 @@ test { // _ = @import("crypto/root.zig"); // _ = @import("decoding/root.zig"); // _ = @import("encoding/root.zig"); - // _ = @import("evm/root.zig"); + _ = @import("evm/root.zig"); // _ = @import("human-readable/root.zig"); _ = @import("human-readable/parser_new.test.zig"); // _ = @import("meta/root.zig"); From 52479f3d6c669daa208e93a475caf8f3e2f90dd1 Mon Sep 17 00:00:00 2001 From: Raiden1411 <67233402+Raiden1411@users.noreply.github.com> Date: Wed, 2 Oct 2024 21:13:13 +0100 Subject: [PATCH 05/18] examples: update allocator --- examples/interpreter/interpreter.zig | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/examples/interpreter/interpreter.zig b/examples/interpreter/interpreter.zig index cba2ee25..c3fd0196 100644 --- a/examples/interpreter/interpreter.zig +++ b/examples/interpreter/interpreter.zig @@ -12,25 +12,24 @@ pub const CliOptions = struct { }; pub fn main() !void { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer _ = gpa.deinit(); + const allocator = std.heap.c_allocator; - var iter = try std.process.argsWithAllocator(gpa.allocator()); + var iter = try std.process.argsWithAllocator(allocator); defer iter.deinit(); - const parsed = args_parser.parseArgs(CliOptions, gpa.allocator(), &iter); + const parsed = args_parser.parseArgs(CliOptions, allocator, &iter); - const buffer = try gpa.allocator().alloc(u8, @divExact(parsed.bytecode.len, 2)); - defer gpa.allocator().free(buffer); + const buffer = try allocator.alloc(u8, @divExact(parsed.bytecode.len, 2)); + defer allocator.free(buffer); - const calldata = try gpa.allocator().alloc(u8, @divExact(parsed.calldata.len, 2)); - defer gpa.allocator().free(calldata); + const calldata = try allocator.alloc(u8, @divExact(parsed.calldata.len, 2)); + defer allocator.free(calldata); _ = try std.fmt.hexToBytes(buffer, parsed.bytecode); _ = try std.fmt.hexToBytes(calldata, parsed.calldata); const contract_instance = try Contract.init( - gpa.allocator(), + allocator, calldata, .{ .raw = buffer }, null, @@ -38,20 +37,20 @@ pub fn main() !void { [_]u8{1} ** 20, [_]u8{0} ** 20, ); - defer contract_instance.deinit(gpa.allocator()); + defer contract_instance.deinit(allocator); var plain: PlainHost = undefined; defer plain.deinit(); - plain.init(gpa.allocator()); + plain.init(allocator); var interpreter: Interpreter = undefined; defer interpreter.deinit(); - try interpreter.init(gpa.allocator(), contract_instance, plain.host(), .{ .gas_limit = 300_000_000 }); + try interpreter.init(allocator, contract_instance, plain.host(), .{ .gas_limit = 300_000_000 }); const result = try interpreter.run(); - defer result.deinit(gpa.allocator()); + defer result.deinit(allocator); std.debug.print("Interpreter result: {any}", .{result}); } From 3b343fdc41afb3a7b8d10f90e7b9f5fdfb392fd8 Mon Sep 17 00:00:00 2001 From: Raiden1411 <67233402+Raiden1411@users.noreply.github.com> Date: Wed, 2 Oct 2024 21:27:40 +0100 Subject: [PATCH 06/18] human_readable: start working on a new parser --- src/human-readable/Ast.zig | 116 +++++ src/human-readable/ParserNew.zig | 776 +++++++++++++++++++++++++++++++ test_runner.zig | 18 +- 3 files changed, 901 insertions(+), 9 deletions(-) create mode 100644 src/human-readable/Ast.zig create mode 100644 src/human-readable/ParserNew.zig diff --git a/src/human-readable/Ast.zig b/src/human-readable/Ast.zig new file mode 100644 index 00000000..61b14cb9 --- /dev/null +++ b/src/human-readable/Ast.zig @@ -0,0 +1,116 @@ +const std = @import("std"); +const token = @import("tokens.zig"); + +const Allocator = std.mem.Allocator; +const TokenTag = token.Tag.SoliditySyntax; + +const Ast = @This(); + +/// Index used for the parser. +pub const TokenIndex = u32; + +/// Struct of arrays for the `Node` members. +pub const NodeList = std.MultiArrayList(Node); + +/// Struct of arrays for the `Token.Tag` members. +pub const TokenList = std.ArrayListUnmanaged(TokenTag); + +/// Source code slice. +source: [:0]const u8, +/// Struct of arrays containing the token tags +/// and token starts. +tokens: []const TokenTag, +/// Struct of arrays containing all node information. +nodes: NodeList.Slice, +/// Slice of extra data produces by the parser. +extra_data: []const Node.Index, + +/// Clears any allocated memory from the `Ast`. +pub fn deinit(self: *Ast, allocator: Allocator) void { + self.tokens.deinit(allocator); + self.nodes.deinit(allocator); + allocator.free(self.extra_data); + allocator.free(self.errors); +} + +pub const Node = struct { + tag: Tag, + data: Data, + main_token: TokenIndex, + + pub const Index = u32; + + // Assert that out tag is always size 1. + comptime { + std.debug.assert(@sizeOf(Tag) == 1); + } + + pub const Tag = enum { + root, + identifier, + unreachable_node, + + constructor_proto_simple, + constructor_proto_multi, + + event_proto_simple, + event_proto_multi, + + error_proto_simple, + error_proto_multi, + + function_proto, + function_proto_one, + function_proto_multi, + function_proto_simple, + + array_type, + elementary_type, + tuple_type, + tuple_type_one, + + specifiers, + + struct_decl, + struct_decl_one, + struct_field, + + var_decl, + error_var_decl, + event_var_decl, + }; + + pub const Data = struct { + lhs: Index, + rhs: Index, + }; + + pub const Range = struct { + start: Index, + end: Index, + }; + + pub const FunctionProto = struct { + specifiers: Node.Index, + identifier: TokenIndex, + params_start: Node.Index, + params_end: Node.Index, + }; + + pub const FunctionProtoOne = struct { + specifiers: Node.Index, + identifier: TokenIndex, + param: Node.Index, + }; + + pub const FunctionProtoMulti = struct { + identifier: TokenIndex, + params_start: Node.Index, + params_end: Node.Index, + }; + + pub const FunctionProtoSimple = struct { + identifier: TokenIndex, + param: Node.Index, + }; +}; diff --git a/src/human-readable/ParserNew.zig b/src/human-readable/ParserNew.zig new file mode 100644 index 00000000..57964d9a --- /dev/null +++ b/src/human-readable/ParserNew.zig @@ -0,0 +1,776 @@ +const std = @import("std"); + +const Allocator = std.mem.Allocator; +const Ast = @import("Ast.zig"); +const Node = Ast.Node; +const TokenIndex = Ast.TokenIndex; +const TokenTag = @import("tokens.zig").Tag.SoliditySyntax; + +const Parser = @This(); + +/// Errors that can happing whilest parsing the source code. +pub const ParserErrors = error{ParsingError} || Allocator.Error; + +const null_node: Node.Index = 0; + +const Span = union(enum) { + zero_one: Node.Index, + multi: Node.Range, +}; + +allocator: Allocator, +source: [:0]const u8, +token_index: TokenIndex, +token_tags: []const TokenTag, +nodes: Ast.NodeList, +extra: std.ArrayListUnmanaged(Node.Index), +scratch: std.ArrayListUnmanaged(Node.Index), + +pub fn deinit(self: *Parser) void { + self.nodes.deinit(self.allocator); + self.extra.deinit(self.allocator); + self.scratch.deinit(self.allocator); +} + +pub fn parseUnit(self: *Parser) ParserErrors!Node.Index { + return switch (self.token_tags[self.token_index]) { + .Function, + .Fallback, + .Receive, + => self.parseFunctionProto(), + .Constructor => self.parseConstructorProto(), + .Event => self.parseEventProto(), + .Error => self.parseErrorProto(), + .Struct => self.parseStructDecl(), + else => return null_node, + }; +} + +pub fn parseFunctionProto(self: *Parser) ParserErrors!Node.Index { + const keyword = switch (self.token_tags[self.token_index]) { + .Function, + .Fallback, + .Receive, + => self.nextToken(), + else => return null_node, + }; + + const reserve = try self.reserveNode(.function_proto); + errdefer self.unreserveNode(reserve); + + const identifier = try self.expectToken(.Identifier); + + _ = try self.expectToken(.OpenParen); + + const params = try self.parseVariableDecls(); + const specifiers = try self.parseSpecifiers(); + + if (self.consumeToken(.Returns)) |_| { + _ = try self.expectToken(.OpenParen); + const returns = try self.parseReturnParams(); + + return switch (params) { + .zero_one => |elem| return self.setNode(reserve, .{ + .tag = .function_proto_one, + .main_token = keyword, + .data = .{ + .lhs = try self.addExtraData(Node.FunctionProtoOne{ + .specifiers = specifiers, + .identifier = identifier, + .param = elem, + }), + .rhs = try self.addExtraData(returns), + }, + }), + .multi => |elem| return self.setNode(reserve, .{ + .tag = .function_proto, + .main_token = keyword, + .data = .{ + .lhs = try self.addExtraData(Node.FunctionProto{ + .specifiers = specifiers, + .identifier = identifier, + .params_start = elem.start, + .params_end = elem.end, + }), + .rhs = try self.addExtraData(returns), + }, + }), + }; + } + + return switch (params) { + .zero_one => |elem| return self.setNode(reserve, .{ + .tag = .function_proto_simple, + .main_token = keyword, + .data = .{ + .lhs = try self.addExtraData(Node.FunctionProtoSimple{ + .identifier = identifier, + .param = elem, + }), + .rhs = specifiers, + }, + }), + .multi => |elem| return self.setNode(reserve, .{ + .tag = .function_proto_multi, + .main_token = keyword, + .data = .{ + .lhs = try self.addExtraData(Node.FunctionProtoMulti{ + .identifier = identifier, + .params_start = elem.start, + .params_end = elem.end, + }), + .rhs = specifiers, + }, + }), + }; +} + +pub fn parseConstructorProto(self: *Parser) ParserErrors!Node.Index { + const constructor_keyword = self.consumeToken(.Constructor) orelse return null_node; + + const reserve = try self.reserveNode(.constructor_proto_multi); + errdefer self.unreserveNode(reserve); + + _ = try self.expectToken(.OpenParen); + + const params = try self.parseVariableDecls(); + + const specifiers = try self.parseSpecifiers(); + + return switch (params) { + .zero_one => |elem| self.setNode(reserve, .{ + .tag = .constructor_proto_simple, + .main_token = constructor_keyword, + .data = .{ + .lhs = elem, + .rhs = specifiers, + }, + }), + .multi => |elems| self.setNode(reserve, .{ + .tag = .constructor_proto_multi, + .main_token = constructor_keyword, + .data = .{ + .lhs = try self.addExtraData(elems), + .rhs = specifiers, + }, + }), + }; +} + +pub fn parseSpecifiers(self: *Parser) Allocator.Error!Node.Index { + const scratch = self.scratch.items.len; + defer self.scratch.shrinkRetainingCapacity(scratch); + + while (true) { + switch (self.token_tags[self.token_index]) { + .Public, + .Pure, + .Payable, + .View, + .Virtual, + .Override, + .Internal, + .Private, + .External, + => try self.scratch.append(self.allocator, self.nextToken()), + else => break, + } + } + + const slice = self.scratch.items[scratch..]; + + return self.addNode(.{ + .tag = .specifiers, + .main_token = try self.addExtraData(try self.listToSpan(slice)), + .data = .{ + .lhs = undefined, + .rhs = undefined, + }, + }); +} + +pub fn parseErrorProto(self: *Parser) ParserErrors!Node.Index { + const error_keyword = self.consumeToken(.Error) orelse return null_node; + + const reserve = try self.reserveNode(.error_proto_multi); + errdefer self.unreserveNode(reserve); + + const identifier = try self.expectToken(.Identifier); + + _ = try self.expectToken(.OpenParen); + + const params = try self.parseErrorVarDecls(); + + return switch (params) { + .zero_one => |elem| self.setNode(reserve, .{ + .tag = .error_proto_simple, + .main_token = error_keyword, + .data = .{ + .lhs = identifier, + .rhs = elem, + }, + }), + .multi => |elems| self.setNode(reserve, .{ + .tag = .error_proto_multi, + .main_token = error_keyword, + .data = .{ + .lhs = identifier, + .rhs = try self.addExtraData(elems), + }, + }), + }; +} + +pub fn parseEventProto(self: *Parser) ParserErrors!Node.Index { + const event_keyword = self.consumeToken(.Event) orelse return null_node; + + const reserve = try self.reserveNode(.event_proto_multi); + errdefer self.unreserveNode(reserve); + + const identifier = try self.expectToken(.Identifier); + + _ = try self.expectToken(.OpenParen); + + const params = try self.parseEventVarDecls(); + + _ = self.consumeToken(.Anonymous); + + return switch (params) { + .zero_one => |elem| self.setNode(reserve, .{ + .tag = .event_proto_simple, + .main_token = event_keyword, + .data = .{ + .lhs = identifier, + .rhs = elem, + }, + }), + .multi => |elems| self.setNode(reserve, .{ + .tag = .event_proto_multi, + .main_token = event_keyword, + .data = .{ + .lhs = identifier, + .rhs = try self.addExtraData(elems), + }, + }), + }; +} + +pub fn parseEventVarDecls(self: *Parser) ParserErrors!Span { + const scratch = self.scratch.items.len; + defer self.scratch.shrinkRetainingCapacity(scratch); + + while (true) { + if (self.consumeToken(.ClosingParen)) |_| break; + + const field = try self.expectEventVarDecl(); + try self.scratch.append(self.allocator, field); + + switch (self.token_tags[self.token_index]) { + .Comma => { + if (self.token_tags[self.token_index + 1] == .ClosingParen) + return error.ParsingError; + self.token_index += 1; + }, + .ClosingParen => { + self.token_index += 1; + break; + }, + else => return error.ParsingError, + } + } + + const slice = self.scratch.items[scratch..]; + + return switch (slice.len) { + 0 => Span{ .zero_one = 0 }, + 1 => Span{ .zero_one = slice[0] }, + else => Span{ .multi = try self.listToSpan(slice) }, + }; +} + +pub fn parseErrorVarDecls(self: *Parser) ParserErrors!Span { + const scratch = self.scratch.items.len; + defer self.scratch.shrinkRetainingCapacity(scratch); + + while (true) { + if (self.consumeToken(.ClosingParen)) |_| break; + + const field = try self.expectErrorVarDecl(); + try self.scratch.append(self.allocator, field); + + switch (self.token_tags[self.token_index]) { + .Comma => { + if (self.token_tags[self.token_index + 1] == .ClosingParen) + return error.ParsingError; + self.token_index += 1; + }, + .ClosingParen => { + self.token_index += 1; + break; + }, + else => return error.ParsingError, + } + } + + const slice = self.scratch.items[scratch..]; + + return switch (slice.len) { + 0 => Span{ .zero_one = 0 }, + 1 => Span{ .zero_one = slice[0] }, + else => Span{ .multi = try self.listToSpan(slice) }, + }; +} + +pub fn parseReturnParams(self: *Parser) ParserErrors!Node.Range { + const scratch = self.scratch.items.len; + defer self.scratch.shrinkRetainingCapacity(scratch); + + while (true) { + if (self.consumeToken(.ClosingParen)) |_| break; + + const field = try self.expectVarDecl(); + try self.scratch.append(self.allocator, field); + + switch (self.token_tags[self.token_index]) { + .Comma => { + if (self.token_tags[self.token_index + 1] == .ClosingParen) + return error.ParsingError; + self.token_index += 1; + }, + .ClosingParen => { + self.token_index += 1; + break; + }, + else => return error.ParsingError, + } + } + + const slice = self.scratch.items[scratch..]; + + if (slice.len == 0) + return error.ParsingError; + + return self.listToSpan(slice); +} + +pub fn parseVariableDecls(self: *Parser) ParserErrors!Span { + const scratch = self.scratch.items.len; + defer self.scratch.shrinkRetainingCapacity(scratch); + + while (true) { + if (self.consumeToken(.ClosingParen)) |_| break; + + const field = try self.expectVarDecl(); + try self.scratch.append(self.allocator, field); + + switch (self.token_tags[self.token_index]) { + .Comma => { + if (self.token_tags[self.token_index + 1] == .ClosingParen) + return error.ParsingError; + self.token_index += 1; + }, + .ClosingParen => { + self.token_index += 1; + break; + }, + else => return error.ParsingError, + } + } + + const slice = self.scratch.items[scratch..]; + + return switch (slice.len) { + 0 => Span{ .zero_one = 0 }, + 1 => Span{ .zero_one = slice[0] }, + else => Span{ .multi = try self.listToSpan(slice) }, + }; +} + +pub fn expectErrorVarDecl(self: *Parser) ParserErrors!Node.Index { + const index = try self.parseErrorVarDecl(); + + if (index == 0) + return error.ParsingError; + + return index; +} + +pub fn parseErrorVarDecl(self: *Parser) ParserErrors!Node.Index { + const sol_type = try self.parseType(); + + if (sol_type == 0) + return null_node; + + const identifier = try self.expectToken(.Identifier); + + return self.addNode(.{ + .tag = .error_var_decl, + .main_token = identifier, + .data = .{ + .lhs = sol_type, + .rhs = undefined, + }, + }); +} + +pub fn expectEventVarDecl(self: *Parser) ParserErrors!Node.Index { + const index = try self.parseEventVarDecl(); + + if (index == 0) + return error.ParsingError; + + return index; +} + +pub fn parseEventVarDecl(self: *Parser) ParserErrors!Node.Index { + const sol_type = try self.parseType(); + + if (sol_type == 0) + return null_node; + + const modifier = switch (self.token_tags[self.token_index]) { + .Indexed, + => self.nextToken(), + else => null_node, + }; + + const identifier = try self.expectToken(.Identifier); + + return self.addNode(.{ + .tag = .event_var_decl, + .main_token = modifier, + .data = .{ + .lhs = sol_type, + .rhs = identifier, + }, + }); +} + +pub fn expectVarDecl(self: *Parser) ParserErrors!Node.Index { + const index = try self.parseVariableDecl(); + + if (index == 0) + return error.ParsingError; + + return index; +} + +pub fn parseVariableDecl(self: *Parser) ParserErrors!Node.Index { + const sol_type = try self.parseType(); + + if (sol_type == 0) + return null_node; + + const modifier = switch (self.token_tags[self.token_index]) { + .Calldata, + .Storage, + .Memory, + => self.nextToken(), + else => null_node, + }; + + const identifier = try self.expectToken(.Identifier); + + return self.addNode(.{ + .tag = .var_decl, + .main_token = modifier, + .data = .{ + .lhs = sol_type, + .rhs = identifier, + }, + }); +} + +pub fn parseStructDecl(self: *Parser) ParserErrors!Node.Index { + const struct_index = self.consumeToken(.Struct) orelse return null_node; + + const reserve = try self.reserveNode(.struct_decl); + errdefer self.unreserveNode(reserve); + + const identifier = try self.expectToken(.Identifier); + + const fields = try self.parseStructFields(); + + _ = try self.expectToken(.OpenBrace); + + return switch (fields) { + .zero_one => |elem| self.setNode(reserve, .{ + .tag = .struct_decl_one, + .main_token = struct_index, + .data = .{ + .lhs = identifier, + .rhs = elem, + }, + }), + .multi => |elems| self.setNode(reserve, .{ + .tag = .struct_decl, + .main_token = struct_index, + .data = .{ + .lhs = identifier, + .rhs = try self.addExtraData(elems), + }, + }), + }; +} + +pub fn parseStructFields(self: *Parser) ParserErrors!Span { + const scratch = self.scratch.items.len; + defer self.scratch.shrinkRetainingCapacity(scratch); + + while (true) { + if (self.consumeToken(.ClosingBrace)) |_| break; + + const field = try self.expectStructField(); + try self.scratch.append(self.allocator, field); + + switch (self.token_tags[self.token_index]) { + .Comma => { + if (self.token_tags[self.token_index + 1] == .ClosingBrace) + return error.ParsingError; + self.token_index += 1; + }, + .ClosingBrace => { + self.token_index += 1; + break; + }, + else => return error.ParsingError, + } + } + + const slice = self.scratch.items[scratch..]; + + return switch (slice.len) { + 0 => Span{ .zero_one = 0 }, + 1 => Span{ .zero_one = slice[0] }, + else => Span{ .multi = try self.listToSpan(slice) }, + }; +} + +pub fn expectStructField(self: *Parser) ParserErrors!Node.Index { + const field_type = try self.expectType(); + const identifier = try self.expectToken(.Identifier); + + _ = try self.expectToken(.SemiColon); + + return self.addNode(.{ + .tag = .struct_field, + .main_token = identifier, + .data = .{ + .lhs = field_type, + .rhs = undefined, + }, + }); +} + +pub fn expectType(self: *Parser) ParserErrors!Node.Index { + const index = try self.parseType(); + + if (index == 0) + return error.ParsingError; + + return index; +} + +pub fn parseType(self: *Parser) Allocator.Error!Node.Index { + const sol_type = switch (self.token_tags[self.token_index]) { + .Identifier => try self.addNode(.{ + .tag = .identifier, + .main_token = self.nextToken(), + .data = .{ + .lhs = undefined, + .rhs = undefined, + }, + }), + else => self.consumeElementaryType(), + }; + + return sol_type; +} + +pub fn consumeElementaryType(self: *Parser) Allocator.Error!Node.Index { + return switch (self.token_tags[self.token_index]) { + .Address, + .Bool, + .Tuple, + .String, + .Bytes, + .Bytes1, + .Bytes2, + .Bytes3, + .Bytes4, + .Bytes5, + .Bytes6, + .Bytes7, + .Bytes8, + .Bytes9, + .Bytes10, + .Bytes11, + .Bytes12, + .Bytes13, + .Bytes14, + .Bytes15, + .Bytes16, + .Bytes17, + .Bytes18, + .Bytes19, + .Bytes20, + .Bytes21, + .Bytes22, + .Bytes23, + .Bytes24, + .Bytes25, + .Bytes26, + .Bytes27, + .Bytes28, + .Bytes29, + .Bytes30, + .Bytes31, + .Bytes32, + .Uint, + .Uint8, + .Uint16, + .Uint24, + .Uint32, + .Uint40, + .Uint48, + .Uint56, + .Uint64, + .Uint72, + .Uint80, + .Uint88, + .Uint96, + .Uint104, + .Uint112, + .Uint120, + .Uint128, + .Uint136, + .Uint144, + .Uint152, + .Uint160, + .Uint168, + .Uint176, + .Uint184, + .Uint192, + .Uint200, + .Uint208, + .Uint216, + .Uint224, + .Uint232, + .Uint240, + .Uint248, + .Uint256, + .Int, + .Int8, + .Int16, + .Int24, + .Int32, + .Int40, + .Int48, + .Int56, + .Int64, + .Int72, + .Int80, + .Int88, + .Int96, + .Int104, + .Int112, + .Int120, + .Int128, + .Int136, + .Int144, + .Int152, + .Int160, + .Int168, + .Int176, + .Int184, + .Int192, + .Int200, + .Int208, + .Int216, + .Int224, + .Int232, + .Int240, + .Int248, + .Int256, + => self.addNode(.{ + .tag = .elementary_type, + .main_token = self.nextToken(), + .data = .{ + .lhs = undefined, + .rhs = undefined, + }, + }), + else => null_node, + }; +} +// Internal parser actions + +fn consumeToken(self: *Parser, expected: TokenTag) ?TokenIndex { + return if (self.token_tags[self.token_index] == expected) self.nextToken() else null; +} + +fn expectToken(self: *Parser, expected: TokenTag) error{ParsingError}!TokenIndex { + return if (self.token_tags[self.token_index] == expected) self.nextToken() else return error.ParsingError; +} + +fn nextToken(self: *Parser) TokenIndex { + const index = self.token_index; + + self.token_index += 1; + + return index; +} + +// Node actions + +/// Appends node to the list and returns the index. +fn addNode(self: *Parser, node: Node) Allocator.Error!Node.Index { + const index = @as(Node.Index, @intCast(self.nodes.len)); + try self.nodes.append(self.allocator, node); + + return index; +} +/// Sets a node based on the provided index. +fn setNode(self: *Parser, index: usize, child: Node) Node.Index { + self.nodes.set(index, child); + + return @as(Node.Index, @intCast(index)); +} +/// Reserves a node index on the arraylist. +fn reserveNode(self: *Parser, tag: Ast.Node.Tag) Allocator.Error!usize { + try self.nodes.resize(self.allocator, self.nodes.len + 1); + self.nodes.items(.tag)[self.nodes.len - 1] = tag; + return self.nodes.len - 1; +} +/// Unreserves the node and sets a empty node into it if the element is not in the end. +fn unreserveNode(self: *Parser, index: usize) void { + if (self.nodes.len == index) { + self.nodes.resize(self.allocator, self.nodes.len - 1) catch unreachable; + } else { + self.nodes.items(.tag)[index] = .unreachable_node; + self.nodes.items(.main_token)[index] = self.token_index; + } +} + +fn addExtraData(self: *Parser, extra: anytype) Allocator.Error!Node.Index { + const fields = std.meta.fields(@TypeOf(extra)); + + try self.extra.ensureUnusedCapacity(self.allocator, fields.len); + const result: u32 = @intCast(self.extra.items.len); + + inline for (fields) |field| { + std.debug.assert(field.type == Node.Index); + self.extra.appendAssumeCapacity(@field(extra, field.name)); + } + + return result; +} + +fn listToSpan(self: *Parser, slice: []const Node.Index) Allocator.Error!Node.Range { + try self.extra.appendSlice(self.allocator, slice); + + return Node.Range{ + .start = @as(Node.Index, @intCast(self.extra.items.len - slice.len)), + .end = @as(Node.Index, @intCast(self.extra.items.len)), + }; +} diff --git a/test_runner.zig b/test_runner.zig index 6329bb33..79e1c6d6 100644 --- a/test_runner.zig +++ b/test_runner.zig @@ -24,15 +24,15 @@ pub fn main() !void { .next_color = .reset, }; - startAnvilInstances(std.heap.page_allocator) catch { - printer.setNextColor(.red); - try printer.writer().writeAll("error: "); - - printer.setNextColor(.bold); - try printer.writer().writeAll("Failed to connect to anvil! Please ensure that it is running on port 6969\n"); - - std.process.exit(1); - }; + // startAnvilInstances(std.heap.page_allocator) catch { + // printer.setNextColor(.red); + // try printer.writer().writeAll("error: "); + // + // printer.setNextColor(.bold); + // try printer.writer().writeAll("Failed to connect to anvil! Please ensure that it is running on port 6969\n"); + // + // std.process.exit(1); + // }; const test_funcs: []const TestFn = builtin.test_functions; From 4bcb6e26a55358a76fb975acaa852503339e28dc Mon Sep 17 00:00:00 2001 From: Raiden1411 <67233402+Raiden1411@users.noreply.github.com> Date: Wed, 2 Oct 2024 21:35:00 +0100 Subject: [PATCH 07/18] evm: update memory test --- src/tests/evm/memory.test.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tests/evm/memory.test.zig b/src/tests/evm/memory.test.zig index 12292110..8c7298e0 100644 --- a/src/tests/evm/memory.test.zig +++ b/src/tests/evm/memory.test.zig @@ -21,6 +21,7 @@ test "Memory" { var mem = try Memory.initWithDefaultCapacity(testing.allocator, null); defer mem.deinit(); + try mem.resize(32); { mem.writeInt(0, 69); try testing.expectEqual(69, mem.getMemoryByte(31)); From 39b62198ae50dd8883f9b9463bfda538bba8b9a3 Mon Sep 17 00:00:00 2001 From: Raiden1411 <67233402+Raiden1411@users.noreply.github.com> Date: Thu, 3 Oct 2024 21:29:31 +0100 Subject: [PATCH 08/18] human: build the full ast representation --- src/human-readable/Ast.zig | 690 ++++++++++++++++++- src/human-readable/ParserNew.zig | 72 +- src/tests/human-readable/parser_new.test.zig | 44 +- src/tests/root.zig | 2 +- 4 files changed, 749 insertions(+), 59 deletions(-) diff --git a/src/human-readable/Ast.zig b/src/human-readable/Ast.zig index 61b14cb9..dd65d76e 100644 --- a/src/human-readable/Ast.zig +++ b/src/human-readable/Ast.zig @@ -1,11 +1,16 @@ const std = @import("std"); const token = @import("tokens.zig"); +const tokenizer = @import("lexer.zig"); const Allocator = std.mem.Allocator; +const Parser = @import("ParserNew.zig"); const TokenTag = token.Tag.SoliditySyntax; const Ast = @This(); +/// Offset used in the parser. +pub const Offset = u32; + /// Index used for the parser. pub const TokenIndex = u32; @@ -13,26 +18,705 @@ pub const TokenIndex = u32; pub const NodeList = std.MultiArrayList(Node); /// Struct of arrays for the `Token.Tag` members. -pub const TokenList = std.ArrayListUnmanaged(TokenTag); +pub const TokenList = std.MultiArrayList(struct { + tag: TokenTag, + start: Offset, +}); /// Source code slice. source: [:0]const u8, /// Struct of arrays containing the token tags /// and token starts. -tokens: []const TokenTag, +tokens: TokenList.Slice, /// Struct of arrays containing all node information. nodes: NodeList.Slice, /// Slice of extra data produces by the parser. extra_data: []const Node.Index, +pub fn parse(allocator: Allocator, source: [:0]const u8) Parser.ParserErrors!Ast { + var tokens: TokenList = .{}; + var lexer = tokenizer.Lexer.init(source); + + while (true) { + const tok = lexer.scan(); + try tokens.append(allocator, .{ + .tag = tok.syntax, + .start = @intCast(tok.location.start), + }); + + if (tok.syntax == .EndOfFileToken) break; + } + + var parser: Parser = .{ + .source = source, + .allocator = allocator, + .token_index = 0, + .token_tags = tokens.items(.tag), + .nodes = .{}, + .scratch = .empty, + .extra = .empty, + }; + defer parser.deinit(); + + try parser.parseSource(); + + return .{ + .source = source, + .tokens = tokens.toOwnedSlice(), + .nodes = parser.nodes.toOwnedSlice(), + .extra_data = try parser.extra.toOwnedSlice(allocator), + }; +} + /// Clears any allocated memory from the `Ast`. pub fn deinit(self: *Ast, allocator: Allocator) void { self.tokens.deinit(allocator); self.nodes.deinit(allocator); allocator.free(self.extra_data); - allocator.free(self.errors); } +pub fn functionProto(self: Ast, node: Node.Index) ast.ConstructorDecl { + const nodes = self.nodes.items(.tag); + std.debug.assert(nodes[node] == .function_proto_multi); + + const data = self.nodes.items(.data)[node]; + const main_token = self.nodes.items(.main_token)[node]; + const params = self.extraData(Node.FunctionProto, data.lhs); + const return_params = self.extraData(Node.Range, data.rhs); + + var result: ast.FunctionDecl = .{ + .ast = .{ + .params = self.extra_data[params.params_start..params.params_end], + .return_params = self.extra_data[return_params.start..return_params.end], + }, + .main_token = main_token, + .name = params.identifier, + .payable = null, + .pure = null, + .view = null, + .external = null, + .public = null, + .override = null, + .virtual = null, + }; + + const node_specifier = self.nodes.items(.main_token)[params.specifiers]; + const specifiers = self.extraData(Node.Range, node_specifier); + + for (self.extra_data[specifiers.start..specifiers.end]) |index| { + switch (self.tokens.items(.tag)[index]) { + .Virtual => result.virtual = index, + .Override => result.override = index, + .Pure => result.pure = index, + .Public => result.public = index, + .View => result.view = index, + .Payable => result.payable = index, + .External => result.external = index, + } + } + + return result; +} + +pub fn functionProtoOne(self: Ast, node_buffer: *[1]Node.Index, node: Node.Index) ast.FunctionDecl { + const nodes = self.nodes.items(.tag); + std.debug.assert(nodes[node] == .function_proto_simple); + + const data = self.nodes.items(.data)[node]; + const main_token = self.nodes.items(.main_token)[node]; + + const params = self.extraData(Node.FunctionProtoOne, data.lhs); + const return_params = self.extraData(Node.Range, data.rhs); + node_buffer[0] = params.param; + + var result: ast.FunctionDecl = .{ + .ast = .{ + .params = if (params.param == 0) node_buffer[0..0] else node_buffer[0..1], + .return_params = self.extra_data[return_params.start..return_params.end], + }, + .main_token = main_token, + .name = params.identifier, + .payable = null, + .pure = null, + .view = null, + .external = null, + .public = null, + .override = null, + .virtual = null, + }; + + const node_specifier = self.nodes.items(.main_token)[params.specifiers]; + const specifiers = self.extraData(Node.Range, node_specifier); + + for (self.extra_data[specifiers.start..specifiers.end]) |index| { + switch (self.tokens.items(.tag)[index]) { + .Virtual => result.virtual = index, + .Override => result.override = index, + .Pure => result.pure = index, + .Public => result.public = index, + .View => result.view = index, + .Payable => result.payable = index, + .External => result.external = index, + } + } + + return result; +} + +pub fn functionProtoMulti(self: Ast, node: Node.Index) ast.ConstructorDecl { + const nodes = self.nodes.items(.tag); + std.debug.assert(nodes[node] == .function_proto_multi); + + const data = self.nodes.items(.data)[node]; + const main_token = self.nodes.items(.main_token)[node]; + const params = self.extraData(Node.FunctionProtoMulti, data.lhs); + + var result: ast.FunctionDecl = .{ + .ast = .{ + .params = self.extra_data[params.params_start..params.params_end], + .return_params = null, + }, + .main_token = main_token, + .name = params.identifier, + .payable = null, + .pure = null, + .view = null, + .external = null, + .public = null, + .override = null, + .virtual = null, + }; + + const node_specifier = self.nodes.items(.main_token)[data.rhs]; + const specifiers = self.extraData(Node.Range, node_specifier); + + for (self.extra_data[specifiers.start..specifiers.end]) |index| { + switch (self.tokens.items(.tag)[index]) { + .Virtual => result.virtual = index, + .Override => result.override = index, + .Pure => result.pure = index, + .Public => result.public = index, + .View => result.view = index, + .Payable => result.payable = index, + .External => result.external = index, + } + } + + return result; +} + +pub fn functionProtoSimple(self: Ast, node_buffer: *[1]Node.Index, node: Node.Index) ast.FunctionDecl { + const nodes = self.nodes.items(.tag); + std.debug.assert(nodes[node] == .function_proto_simple); + + const data = self.nodes.items(.data)[node]; + const main_token = self.nodes.items(.main_token)[node]; + const params = self.extraData(Node.FunctionProtoSimple, data.lhs); + node_buffer[0] = params.param; + + var result: ast.FunctionDecl = .{ + .ast = .{ + .params = if (params.param == 0) node_buffer[0..0] else node_buffer[0..1], + .return_params = null, + }, + .main_token = main_token, + .name = params.identifier, + .payable = null, + .pure = null, + .view = null, + .external = null, + .public = null, + .override = null, + .virtual = null, + }; + + const node_specifier = self.nodes.items(.main_token)[data.rhs]; + const specifiers = self.extraData(Node.Range, node_specifier); + + for (self.extra_data[specifiers.start..specifiers.end]) |index| { + switch (self.tokens.items(.tag)[index]) { + .Virtual => result.virtual = index, + .Override => result.override = index, + .Pure => result.pure = index, + .Public => result.public = index, + .View => result.view = index, + .Payable => result.payable = index, + .External => result.external = index, + } + } + + return result; +} + +pub fn constructorProtoMulti(self: Ast, node: Node.Index) ast.ConstructorDecl { + const nodes = self.nodes.items(.tag); + std.debug.assert(nodes[node] == .constructor_proto_multi); + + const data = self.nodes.items(.data)[node]; + const main_token = self.nodes.items(.main_token)[node]; + const params = self.extraData(Node.Range, data.lhs); + + var result: ast.ConstructorDecl = .{ + .ast = .{ + .params = self.extra_data[params.start..params.end], + }, + .main_token = main_token, + .payable = null, + .pure = null, + .view = null, + .external = null, + .public = null, + .override = null, + .virtual = null, + }; + + const node_specifier = self.nodes.items(.main_token)[data.rhs]; + const specifiers = self.extraData(Node.Range, node_specifier); + + for (self.extra_data[specifiers.start..specifiers.end]) |index| { + switch (self.tokens.items(.tag)[index]) { + .Virtual => result.virtual = index, + .Override => result.override = index, + .Pure => result.pure = index, + .Public => result.public = index, + .View => result.view = index, + .Payable => result.payable = index, + .External => result.external = index, + } + } + + return result; +} + +pub fn constructorProtoSimple(self: Ast, node_buffer: *[1]Node.Index, node: Node.Index) ast.ConstructorDecl { + const nodes = self.nodes.items(.tag); + std.debug.assert(nodes[node] == .constructor_proto_simple); + + const data = self.nodes.items(.data)[node]; + const main_token = self.nodes.items(.main_token)[node]; + node_buffer[0] = data.lhs; + + var result: ast.ConstructorDecl = .{ + .ast = .{ + .params = if (data.lhs == 0) node_buffer[0..0] else node_buffer[0..1], + }, + .main_token = main_token, + .payable = null, + .pure = null, + .view = null, + .external = null, + .public = null, + .override = null, + .virtual = null, + }; + + const node_specifier = self.nodes.items(.main_token)[data.rhs]; + const specifiers = self.extraData(Node.Range, node_specifier); + + for (self.extra_data[specifiers.start..specifiers.end]) |index| { + switch (self.tokens.items(.tag)[index]) { + .Virtual => result.virtual = index, + .Override => result.override = index, + .Pure => result.pure = index, + .Public => result.public = index, + .View => result.view = index, + .Payable => result.payable = index, + .External => result.external = index, + } + } + + return result; +} + +pub fn eventProtoMulti(self: Ast, node: Node.Index) ast.EventDecl { + const nodes = self.nodes.items(.tag); + std.debug.assert(nodes[node] == .event_proto_multi); + + const data = self.nodes.items(.data)[node]; + const main_token = self.nodes.items(.main_token)[node]; + const extra = self.extraData(Node.Range, data.rhs); + + return .{ + .ast = .{ + .params = self.extra_data[extra.start..extra.end], + }, + .main_token = main_token, + .name = data.lhs, + .anonymous = null, + }; +} + +pub fn eventProtoSimple(self: Ast, node_buffer: *[1]Node.Index, node: Node.Index) ast.ErrorDecl { + const nodes = self.nodes.items(.tag); + std.debug.assert(nodes[node] == .event_proto_simple); + + const data = self.nodes.items(.data)[node]; + const main_token = self.nodes.items(.main_token)[node]; + node_buffer[0] = data.rhs; + + return .{ + .ast = .{ + .params = if (data.rhs == 0) node_buffer[0..0] else node_buffer[0..1], + }, + .main_token = main_token, + .name = data.lhs, + .anonymous = null, + }; +} + +pub fn errorProtoMulti(self: Ast, node: Node.Index) ast.ErrorDecl { + const nodes = self.nodes.items(.tag); + std.debug.assert(nodes[node] == .error_proto_multi); + + const data = self.nodes.items(.data)[node]; + const main_token = self.nodes.items(.main_token)[node]; + const extra = self.extraData(Node.Range, data.rhs); + + return .{ + .ast = .{ + .params = self.extra_data[extra.start..extra.end], + }, + .main_token = main_token, + .name = data.lhs, + }; +} + +pub fn errorProtoSimple(self: Ast, node_buffer: *[1]Node.Index, node: Node.Index) ast.ErrorDecl { + const nodes = self.nodes.items(.tag); + std.debug.assert(nodes[node] == .error_proto_simple); + + const data = self.nodes.items(.data)[node]; + const main_token = self.nodes.items(.main_token)[node]; + node_buffer[0] = data.rhs; + + return .{ + .ast = .{ + .params = if (data.rhs == 0) node_buffer[0..0] else node_buffer[0..1], + }, + .main_token = main_token, + .name = data.lhs, + }; +} + +pub fn structDecl(self: Ast, node: Node.Index) ast.StructDecl { + const nodes = self.nodes.items(.tag); + std.debug.assert(nodes[node] == .struct_decl); + + const data = self.nodes.items(.data)[node]; + const main_token = self.nodes.items(.main_token)[node]; + const extra = self.extraData(Node.Range, data.rhs); + + return .{ + .ast = .{ + .members = self.extra_data[extra.start..extra.end], + }, + .main_token = main_token, + .name = data.lhs, + }; +} + +pub fn structDeclOne(self: Ast, node_buffer: *[1]Node.Index, node: Node.Index) ast.StructDecl { + const nodes = self.nodes.items(.tag); + std.debug.assert(nodes[node] == .struct_decl_one); + + const data = self.nodes.items(.data)[node]; + const main_token = self.nodes.items(.main_token)[node]; + node_buffer[0] = data.rhs; + + return .{ + .ast = .{ + .members = if (data.rhs == 0) node_buffer[0..0] else node_buffer[0..1], + }, + .main_token = main_token, + .name = data.lhs, + }; +} + +pub fn extraData(self: Ast, comptime T: type, node: Node.Index) T { + const fields = std.meta.fields(T); + var result: T = undefined; + + inline for (fields, 0..) |field, i| { + comptime std.debug.assert(field.type == Node.Index); + + @field(result, field.name) = self.extra_data[node + i]; + } + + return result; +} + +pub fn firstToken(self: Ast, node: Node.Index) TokenIndex { + const main = self.nodes.items(.main_token); + const data = self.nodes.items(.data); + const nodes = self.nodes.items(.tag); + + var current_node = node; + + while (true) { + switch (nodes[current_node]) { + .root => return 0, + + .elementary_type, + .identifier, + .function_proto_simple, + .function_proto_multi, + .function_proto_one, + .function_proto, + .constructor_proto_simple, + .constructor_proto_multi, + .error_proto_multi, + .error_proto_simple, + .event_proto_multi, + .event_proto_simple, + .tuple_type, + .tuple_type_one, + .struct_decl, + .struct_decl_one, + .unreachable_node, + => return main[current_node], + + .array_type, + .struct_field, + .error_var_decl, + .event_var_decl, + .var_decl, + => current_node = data[current_node].lhs, + + .specifiers, + => { + const extra = self.extraData(Node.Range, main[current_node]); + + return self.extra_data[extra.start]; + }, + } + } +} + +pub fn lastToken(self: Ast, node: Node.Index) TokenIndex { + const main = self.nodes.items(.main_token); + const data = self.nodes.items(.data); + const nodes = self.nodes.items(.tag); + + var current_node = node; + + var end_offset: u32 = 0; + + while (true) { + switch (nodes[current_node]) { + .root => return @as(TokenIndex, @intCast(self.tokens.len - 1)), + + .array_type, + .tuple_type, + .tuple_type_one, + => return 0 + end_offset, + + .elementary_type, + .identifier, + .unreachable_node, + => return main[current_node] + end_offset, + + .error_var_decl, + => { + if (main[current_node] != 0) + return main[current_node] + end_offset; + + current_node = data[current_node].lhs; + }, + + .constructor_proto_simple, + => { + end_offset += 1; + const specifiers_node = main[data[current_node].rhs]; + const range = self.extraData(Node.Range, specifiers_node); + const slice = self.extra_data[range.start..range.end]; + + if (slice.len == 0) { + current_node = data[current_node].lhs; + } else current_node = data[current_node].rhs; + }, + .constructor_proto_multi, + => { + end_offset += 1; + const specifiers_node = main[data[current_node].rhs]; + const range = self.extraData(Node.Range, specifiers_node); + const slice = self.extra_data[range.start..range.end]; + + if (slice.len == 0) { + const proto = self.extraData(Node.Range, data[current_node].lhs); + current_node = self.extra_data[proto.end - 1]; + } else current_node = data[current_node].rhs; + }, + .function_proto_simple, + => { + end_offset += 1; + const specifiers_node = main[data[current_node].rhs]; + const range = self.extraData(Node.Range, specifiers_node); + const slice = self.extra_data[range.start..range.end]; + + if (slice.len == 0) { + const proto = self.extraData(Node.FunctionProtoSimple, data[current_node].lhs); + current_node = proto.param; + } else current_node = data[current_node].rhs; + }, + .function_proto_multi, + => { + end_offset += 1; + const specifiers_node = main[data[current_node].rhs]; + const range = self.extraData(Node.Range, specifiers_node); + const slice = self.extra_data[range.start..range.end]; + + if (slice.len == 0) { + const proto = self.extraData(Node.FunctionProtoMulti, data[current_node].lhs); + current_node = self.extra_data[proto.params_end - 1]; + } else current_node = data[current_node].rhs; + }, + + .function_proto_one, + .function_proto, + .error_proto_multi, + .event_proto_multi, + .struct_decl, + => { + end_offset += 1; + + const extra = self.extraData(Node.Range, data[current_node].rhs); + current_node = self.extra_data[extra.end - 1]; + }, + + .error_proto_simple, + .event_proto_simple, + .struct_decl_one, + => { + end_offset += 1; + current_node = data[current_node].rhs; + }, + + .struct_field, + => { + end_offset += 1; + return main[current_node] + end_offset; + }, + + .event_var_decl, + .var_decl, + => { + if (data[current_node].rhs != 0) { + return data[current_node].rhs + end_offset; + } else if (main[current_node] != 0) { + return main[current_node] + end_offset; + } else current_node = data[current_node].lhs; + }, + + .specifiers, + => { + const extra = self.extraData(Node.Range, main[current_node]); + + if (extra.end == 0) + return extra.end; + + return self.extra_data[extra.end - 1]; + }, + } + } +} + +pub fn tokenSlice(self: Ast, token_index: TokenIndex) []const u8 { + const token_tag = self.tokens.items(.tag)[token_index]; + const token_start = self.tokens.items(.start)[token_index]; + + var lexer: tokenizer.Lexer = .{ + .position = token_start, + .currentText = self.source, + }; + + if (token_tag.lexToken()) |tok| + return tok; + + const tok = lexer.scan(); + std.debug.assert(tok.syntax == token_tag); + + return self.source[tok.location.start..tok.location.end]; +} + +pub fn getNodeSource(self: Ast, node: Node.Index) []const u8 { + const token_start = self.tokens.items(.start); + + const first = self.firstToken(node); + const last = self.lastToken(node); + + const start = token_start[first]; + const end = token_start[last] + self.tokenSlice(last).len; + + return self.source[start..end]; +} + +pub const ast = struct { + pub const ConstructorDecl = struct { + ast: ComponentDecl, + main_token: TokenIndex, + view: ?TokenIndex, + pure: ?TokenIndex, + payable: ?TokenIndex, + public: ?TokenIndex, + external: ?TokenIndex, + virtual: ?TokenIndex, + override: ?TokenIndex, + + const ComponentDecl = struct { + params: []const Node.Index, + }; + }; + + pub const FunctionDecl = struct { + ast: ComponentDecl, + main_token: TokenIndex, + name: TokenIndex, + view: ?TokenIndex, + pure: ?TokenIndex, + payable: ?TokenIndex, + public: ?TokenIndex, + external: ?TokenIndex, + virtual: ?TokenIndex, + override: ?TokenIndex, + + const ComponentDecl = struct { + params: []const Node.Index, + return_params: ?[]const Node.Index, + }; + }; + + pub const ErrorDecl = struct { + ast: ComponentDecl, + main_token: TokenIndex, + name: TokenIndex, + + const ComponentDecl = struct { + params: []const Node.Index, + }; + }; + + pub const EventDecl = struct { + ast: ComponentDecl, + main_token: TokenIndex, + name: TokenIndex, + anonymous: ?TokenIndex, + + const ComponentDecl = struct { + params: []const Node.Index, + }; + }; + + pub const StructDecl = struct { + ast: ComponentDecl, + main_token: TokenIndex, + name: TokenIndex, + + const ComponentDecl = struct { + members: []const Node.Index, + }; + }; +}; + pub const Node = struct { tag: Tag, data: Data, diff --git a/src/human-readable/ParserNew.zig b/src/human-readable/ParserNew.zig index 57964d9a..d354e59e 100644 --- a/src/human-readable/ParserNew.zig +++ b/src/human-readable/ParserNew.zig @@ -32,6 +32,51 @@ pub fn deinit(self: *Parser) void { self.scratch.deinit(self.allocator); } +pub fn parseSource(self: *Parser) ParserErrors!void { + try self.nodes.append(self.allocator, .{ + .tag = .root, + .main_token = 0, + .data = undefined, + }); + + const members = try self.parseUnits(); + + if (self.token_tags[self.token_index] != .EndOfFileToken) + return error.ParsingError; + + self.nodes.items(.data)[0] = .{ + .lhs = members.start, + .rhs = members.end, + }; +} + +pub fn parseUnits(self: *Parser) ParserErrors!Node.Range { + const scratch = self.scratch.items.len; + defer self.scratch.shrinkRetainingCapacity(scratch); + + while (true) { + switch (self.token_tags[self.token_index]) { + .EndOfFileToken => break, + else => {}, + } + + try self.scratch.append(self.allocator, try self.expectUnit()); + } + + const slice = self.scratch.items[scratch..]; + + return self.listToSpan(slice); +} + +pub fn expectUnit(self: *Parser) ParserErrors!Node.Index { + const unit = try self.parseUnit(); + + if (unit == 0) + return error.ParsingError; + + return unit; +} + pub fn parseUnit(self: *Parser) ParserErrors!Node.Index { return switch (self.token_tags[self.token_index]) { .Function, @@ -47,19 +92,19 @@ pub fn parseUnit(self: *Parser) ParserErrors!Node.Index { } pub fn parseFunctionProto(self: *Parser) ParserErrors!Node.Index { - const keyword = switch (self.token_tags[self.token_index]) { - .Function, + const keyword = self.consumeToken(.Function) orelse return null_node; + + const reserve = try self.reserveNode(.function_proto); + errdefer self.unreserveNode(reserve); + + const identifier = switch (self.token_tags[self.token_index]) { + .Identifier, .Fallback, .Receive, => self.nextToken(), else => return null_node, }; - const reserve = try self.reserveNode(.function_proto); - errdefer self.unreserveNode(reserve); - - const identifier = try self.expectToken(.Identifier); - _ = try self.expectToken(.OpenParen); const params = try self.parseVariableDecls(); @@ -469,7 +514,7 @@ pub fn parseVariableDecl(self: *Parser) ParserErrors!Node.Index { else => null_node, }; - const identifier = try self.expectToken(.Identifier); + const identifier = self.consumeToken(.Identifier) orelse null_node; return self.addNode(.{ .tag = .var_decl, @@ -489,10 +534,10 @@ pub fn parseStructDecl(self: *Parser) ParserErrors!Node.Index { const identifier = try self.expectToken(.Identifier); - const fields = try self.parseStructFields(); - _ = try self.expectToken(.OpenBrace); + const fields = try self.parseStructFields(); + return switch (fields) { .zero_one => |elem| self.setNode(reserve, .{ .tag = .struct_decl_one, @@ -524,16 +569,11 @@ pub fn parseStructFields(self: *Parser) ParserErrors!Span { try self.scratch.append(self.allocator, field); switch (self.token_tags[self.token_index]) { - .Comma => { - if (self.token_tags[self.token_index + 1] == .ClosingBrace) - return error.ParsingError; - self.token_index += 1; - }, .ClosingBrace => { self.token_index += 1; break; }, - else => return error.ParsingError, + else => {}, } } diff --git a/src/tests/human-readable/parser_new.test.zig b/src/tests/human-readable/parser_new.test.zig index 4e13d606..c10f0b4e 100644 --- a/src/tests/human-readable/parser_new.test.zig +++ b/src/tests/human-readable/parser_new.test.zig @@ -5,44 +5,10 @@ const testing = std.testing; const Parser = @import("../../human-readable/ParserNew.zig"); const Ast = @import("../../human-readable/Ast.zig"); -test "Pragma" { - var tokens: Ast.TokenList = .empty; - defer tokens.deinit(testing.allocator); +test "Human readable" { + var ast = try Ast.parse(testing.allocator, "function receive(address bar, uint bar) view external\nstruct Foo {address bar;}"); + defer ast.deinit(testing.allocator); - var parser: Parser = undefined; - defer parser.deinit(); - - try buildParser("function Bar(address bar, uint bar) view external pure returns(address bar)", &tokens, &parser); - - _ = try parser.parseUnit(); - - std.debug.print("FOOOOO: {any}\n", .{parser.nodes.items(.tag)}); -} - -fn buildParser(source: [:0]const u8, tokens: *Ast.TokenList, parser: *Parser) !void { - var lexer = tokenizer.Lexer.init(source); - - while (true) { - const token = lexer.scan(); - - try tokens.append(testing.allocator, token.syntax); - - if (token.syntax == .EndOfFileToken) break; - } - - parser.* = .{ - .source = source, - .allocator = testing.allocator, - .token_index = 0, - .token_tags = tokens.items, - .nodes = .{}, - .scratch = .empty, - .extra = .empty, - }; - - try parser.nodes.append(testing.allocator, .{ - .tag = .root, - .main_token = 0, - .data = undefined, - }); + std.debug.print("FOOOOO: {any}\n", .{ast.nodes.items(.tag)}); + std.debug.print("FOOOOO: {s}\n", .{ast.getNodeSource(1)}); } diff --git a/src/tests/root.zig b/src/tests/root.zig index 5d95c089..dd2ac9d7 100644 --- a/src/tests/root.zig +++ b/src/tests/root.zig @@ -6,7 +6,7 @@ test { // _ = @import("crypto/root.zig"); // _ = @import("decoding/root.zig"); // _ = @import("encoding/root.zig"); - _ = @import("evm/root.zig"); + // _ = @import("evm/root.zig"); // _ = @import("human-readable/root.zig"); _ = @import("human-readable/parser_new.test.zig"); // _ = @import("meta/root.zig"); From ea076a781271b0a7134e661af9aa5ace896f59c3 Mon Sep 17 00:00:00 2001 From: Raiden1411 <67233402+Raiden1411@users.noreply.github.com> Date: Fri, 4 Oct 2024 14:49:28 +0100 Subject: [PATCH 09/18] human: start working on generating the ABI --- src/human-readable/Ast.zig | 167 ++++++++++++- src/human-readable/HumanAbi.zig | 237 +++++++++++++++++++ src/human-readable/ParserNew.zig | 103 +++++++- src/tests/human-readable/parser_new.test.zig | 11 +- 4 files changed, 507 insertions(+), 11 deletions(-) create mode 100644 src/human-readable/HumanAbi.zig diff --git a/src/human-readable/Ast.zig b/src/human-readable/Ast.zig index dd65d76e..311ec67f 100644 --- a/src/human-readable/Ast.zig +++ b/src/human-readable/Ast.zig @@ -112,6 +112,7 @@ pub fn functionProto(self: Ast, node: Node.Index) ast.ConstructorDecl { .View => result.view = index, .Payable => result.payable = index, .External => result.external = index, + else => unreachable, // Unexpected token } } @@ -157,6 +158,7 @@ pub fn functionProtoOne(self: Ast, node_buffer: *[1]Node.Index, node: Node.Index .View => result.view = index, .Payable => result.payable = index, .External => result.external = index, + else => unreachable, // Unexpected token } } @@ -199,6 +201,7 @@ pub fn functionProtoMulti(self: Ast, node: Node.Index) ast.ConstructorDecl { .View => result.view = index, .Payable => result.payable = index, .External => result.external = index, + else => unreachable, // Unexpected token } } @@ -242,6 +245,126 @@ pub fn functionProtoSimple(self: Ast, node_buffer: *[1]Node.Index, node: Node.In .View => result.view = index, .Payable => result.payable = index, .External => result.external = index, + else => unreachable, // Unexpected token + } + } + + return result; +} + +pub fn receiveProto(self: Ast, node: Node.Index) ast.ReceiveDecl { + const nodes = self.nodes.items(.tag); + std.debug.assert(nodes[node] == .receive_proto); + + const data = self.nodes.items(.data)[node]; + const main_token = self.nodes.items(.main_token)[node]; + + var result: ast.ReceiveDecl = .{ + .main_token = main_token, + .payable = null, + .pure = null, + .view = null, + .external = null, + .public = null, + .override = null, + .virtual = null, + }; + + const node_specifier = self.nodes.items(.main_token)[data.rhs]; + const specifiers = self.extraData(Node.Range, node_specifier); + + for (self.extra_data[specifiers.start..specifiers.end]) |index| { + switch (self.tokens.items(.tag)[index]) { + .Virtual => result.virtual = index, + .Override => result.override = index, + .Pure => result.pure = index, + .Public => result.public = index, + .View => result.view = index, + .Payable => result.payable = index, + .External => result.external = index, + else => unreachable, // Unexpected token + } + } + + return result; +} + +pub fn fallbackProtoMulti(self: Ast, node: Node.Index) ast.FallbackDecl { + const nodes = self.nodes.items(.tag); + std.debug.assert(nodes[node] == .fallback_proto_multi); + + const data = self.nodes.items(.data)[node]; + const main_token = self.nodes.items(.main_token)[node]; + const params = self.extraData(Node.Range, data.lhs); + + var result: ast.FallbackDecl = .{ + .ast = .{ + .params = self.extra_data[params.start..params.end], + }, + .main_token = main_token, + .payable = null, + .pure = null, + .view = null, + .external = null, + .public = null, + .override = null, + .virtual = null, + }; + + const node_specifier = self.nodes.items(.main_token)[data.rhs]; + const specifiers = self.extraData(Node.Range, node_specifier); + + for (self.extra_data[specifiers.start..specifiers.end]) |index| { + switch (self.tokens.items(.tag)[index]) { + .Virtual => result.virtual = index, + .Override => result.override = index, + .Pure => result.pure = index, + .Public => result.public = index, + .View => result.view = index, + .Payable => result.payable = index, + .External => result.external = index, + else => unreachable, // Unexpected token + } + } + + return result; +} + +pub fn fallbackProtoSimple(self: Ast, node_buffer: *[1]Node.Index, node: Node.Index) ast.FallbackDecl { + const nodes = self.nodes.items(.tag); + std.debug.assert(nodes[node] == .fallback_proto_simple); + + const data = self.nodes.items(.data)[node]; + const main_token = self.nodes.items(.main_token)[node]; + node_buffer[0] = data.lhs; + + var result: ast.FallbackDecl = .{ + .ast = .{ + .params = if (data.lhs == 0) node_buffer[0..0] else node_buffer[0..1], + }, + .main_token = main_token, + .payable = null, + .pure = null, + .view = null, + .external = null, + .public = null, + .override = null, + .virtual = null, + }; + + const node_specifier = self.nodes.items(.main_token)[data.rhs]; + const specifiers = self.extraData(Node.Range, node_specifier); + + for (self.extra_data[specifiers.start..specifiers.end]) |index| { + switch (self.tokens.items(.tag)[index]) { + .Virtual => result.virtual = index, + .Override => result.override = index, + .Pure => result.pure = index, + .Public => result.public = index, + .View => result.view = index, + .Payable => result.payable = index, + .External => result.external = index, + else => unreachable, // Unexpected token } } @@ -282,6 +405,7 @@ pub fn constructorProtoMulti(self: Ast, node: Node.Index) ast.ConstructorDecl { .View => result.view = index, .Payable => result.payable = index, .External => result.external = index, + else => unreachable, // Unexpected token } } @@ -322,6 +446,7 @@ pub fn constructorProtoSimple(self: Ast, node_buffer: *[1]Node.Index, node: Node .View => result.view = index, .Payable => result.payable = index, .External => result.external = index, + else => unreachable, // Unexpected token } } @@ -346,7 +471,7 @@ pub fn eventProtoMulti(self: Ast, node: Node.Index) ast.EventDecl { }; } -pub fn eventProtoSimple(self: Ast, node_buffer: *[1]Node.Index, node: Node.Index) ast.ErrorDecl { +pub fn eventProtoSimple(self: Ast, node_buffer: *[1]Node.Index, node: Node.Index) ast.EventDecl { const nodes = self.nodes.items(.tag); std.debug.assert(nodes[node] == .event_proto_simple); @@ -473,6 +598,9 @@ pub fn firstToken(self: Ast, node: Node.Index) TokenIndex { .struct_decl, .struct_decl_one, .unreachable_node, + .fallback_proto_simple, + .fallback_proto_multi, + .receive_proto, => return main[current_node], .array_type, @@ -523,7 +651,11 @@ pub fn lastToken(self: Ast, node: Node.Index) TokenIndex { current_node = data[current_node].lhs; }, + .receive_proto, + => current_node = data[current_node].rhs, + .constructor_proto_simple, + .fallback_proto_simple, => { end_offset += 1; const specifiers_node = main[data[current_node].rhs]; @@ -535,6 +667,7 @@ pub fn lastToken(self: Ast, node: Node.Index) TokenIndex { } else current_node = data[current_node].rhs; }, .constructor_proto_multi, + .fallback_proto_multi, => { end_offset += 1; const specifiers_node = main[data[current_node].rhs]; @@ -651,6 +784,33 @@ pub fn getNodeSource(self: Ast, node: Node.Index) []const u8 { } pub const ast = struct { + pub const ReceiveDecl = struct { + main_token: TokenIndex, + view: ?TokenIndex, + pure: ?TokenIndex, + payable: ?TokenIndex, + public: ?TokenIndex, + external: ?TokenIndex, + virtual: ?TokenIndex, + override: ?TokenIndex, + }; + + pub const FallbackDecl = struct { + ast: ComponentDecl, + main_token: TokenIndex, + view: ?TokenIndex, + pure: ?TokenIndex, + payable: ?TokenIndex, + public: ?TokenIndex, + external: ?TokenIndex, + virtual: ?TokenIndex, + override: ?TokenIndex, + + const ComponentDecl = struct { + params: []const Node.Index, + }; + }; + pub const ConstructorDecl = struct { ast: ComponentDecl, main_token: TokenIndex, @@ -737,6 +897,11 @@ pub const Node = struct { constructor_proto_simple, constructor_proto_multi, + fallback_proto_simple, + fallback_proto_multi, + + receive_proto, + event_proto_simple, event_proto_multi, diff --git a/src/human-readable/HumanAbi.zig b/src/human-readable/HumanAbi.zig new file mode 100644 index 00000000..46d26eda --- /dev/null +++ b/src/human-readable/HumanAbi.zig @@ -0,0 +1,237 @@ +const abi = @import("../abi/abi.zig"); +const param = @import("../abi/abi_parameter.zig"); +const param_types = @import("../abi/param_type.zig"); +const std = @import("std"); +const tokens = @import("tokens.zig"); + +const AbiParameter = param.AbiParameter; +const AbiEventParameter = param.AbiEventParameter; +const Allocator = std.mem.Allocator; +const Ast = @import("Ast.zig"); +const AbiConstructor = abi.Constructor; +const AbiEvent = abi.Event; +const AbiError = abi.Error; +const AbiFallback = abi.Fallback; +const AbiReceive = abi.Receive; +const Node = Ast.Node; +const ParamErrors = param_types.ParamErrors; +const ParamType = param_types.ParamType; +const Parser = @import("ParserNew.zig"); +const StateMutability = @import("../abi/state_mutability.zig").StateMutability; + +/// Set of erros when generating the ABI +pub const HumanAbiErrors = ParamErrors || Allocator.Error; + +const HumanAbi = @This(); + +allocator: Allocator, +ast: *const Ast, + +pub fn toAbiConstructorMulti(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiConstructor { + const nodes = self.ast.nodes.items(.tag); + std.debug.assert(nodes[node] == .constructor_proto_multi); + + const ast_constructor = self.ast.constructorProtoMulti(node); + + const params = try self.toAbiParameters(ast_constructor.ast.params); + + return .{ + .type = .constructor, + .inputs = params, + .stateMutability = if (ast_constructor.payable != null) .payable else .nonpayable, + }; +} + +pub fn toAbiConstructorSimple(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiConstructor { + const nodes = self.ast.nodes.items(.tag); + std.debug.assert(nodes[node] == .constructor_proto_simple); + + var buffer: [1]Node.Index = undefined; + const ast_constructor = self.ast.constructorProtoSimple(&buffer, node); + + const params = try self.toAbiParameters(ast_constructor.ast.params); + + return .{ + .type = .constructor, + .inputs = params, + .stateMutability = if (ast_constructor.payable != null) .payable else .nonpayable, + }; +} + +pub fn toAbiEventMulti(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiEvent { + const nodes = self.ast.nodes.items(.tag); + std.debug.assert(nodes[node] == .event_proto_multi); + + const ast_event = self.ast.eventProtoMulti(node); + + const params = try self.toAbiEventParameters(ast_event.ast.params); + + return .{ + .type = .event, + .name = self.ast.tokenSlice(ast_event.name), + .inputs = params, + }; +} + +pub fn toAbiEventSimple(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiEvent { + const nodes = self.ast.nodes.items(.tag); + std.debug.assert(nodes[node] == .event_proto_simple); + + var buffer: [1]Node.Index = undefined; + const ast_event = self.ast.eventProtoSimple(&buffer, node); + + const params = try self.toAbiEventParameters(ast_event.ast.params); + + return .{ + .type = .event, + .name = self.ast.tokenSlice(ast_event.name), + .inputs = params, + }; +} + +pub fn toAbiErrorMulti(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiError { + const nodes = self.ast.nodes.items(.tag); + std.debug.assert(nodes[node] == .error_proto_multi); + + const ast_error = self.ast.errorProtoMulti(node); + + const params = try self.toAbiParametersFromErrorDecl(ast_error.ast.params); + + return .{ + .type = .@"error", + .name = self.ast.tokenSlice(ast_error.name), + .inputs = params, + }; +} + +pub fn toAbiErrorSimple(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiError { + const nodes = self.ast.nodes.items(.tag); + std.debug.assert(nodes[node] == .error_proto_simple); + + var buffer: [1]Node.Index = undefined; + const ast_error = self.ast.errorProtoSimple(&buffer, node); + + const params = try self.toAbiParametersFromErrorDecl(ast_error.ast.params); + + return .{ + .type = .@"error", + .name = self.ast.tokenSlice(ast_error.name), + .inputs = params, + }; +} + +pub fn toAbiParameters(self: HumanAbi, nodes: []const Node.Index) HumanAbiErrors![]const AbiParameter { + var params = try std.ArrayList(AbiParameter).initCapacity(self.allocator, nodes.len); + errdefer params.deinit(); + + for (nodes) |node| { + params.appendAssumeCapacity(try self.toAbiParameter(node)); + } + + return params.toOwnedSlice(); +} + +pub fn toAbiParametersFromErrorDecl(self: HumanAbi, nodes: []const Node.Index) HumanAbiErrors![]const AbiParameter { + var params = try std.ArrayList(AbiParameter).initCapacity(self.allocator, nodes.len); + errdefer params.deinit(); + + for (nodes) |node| { + params.appendAssumeCapacity(try self.toAbiParameterFromErrorDecl(node)); + } + + return params.toOwnedSlice(); +} + +pub fn toAbiEventParameters(self: HumanAbi, nodes: []const Node.Index) HumanAbiErrors![]const AbiEventParameter { + var params = try std.ArrayList(AbiEventParameter).initCapacity(self.allocator, nodes.len); + errdefer params.deinit(); + + for (nodes) |node| { + params.appendAssumeCapacity(try self.toAbiEventParameter(node)); + } + + return params.toOwnedSlice(); +} + +pub fn toAbiParameter(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiParameter { + const nodes = self.ast.nodes.items(.tag); + const data = self.ast.nodes.items(.data); + const main = self.ast.nodes.items(.main_token); + std.debug.assert(nodes[node] == .var_decl); + + const type_slice = self.ast.tokenSlice(main[data[node].lhs]); + const param_type = try ParamType.typeToUnion(type_slice, self.allocator); + + return .{ + .type = param_type, + .name = if (data[node].rhs == 0) "" else self.ast.tokenSlice(data[node].rhs), + }; +} + +pub fn toAbiEventParameter(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiEventParameter { + const nodes = self.ast.nodes.items(.tag); + const data = self.ast.nodes.items(.data); + const main = self.ast.nodes.items(.main_token); + std.debug.assert(nodes[node] == .event_var_decl); + + const type_slice = self.ast.tokenSlice(main[data[node].lhs]); + const param_type = try ParamType.typeToUnion(type_slice, self.allocator); + + return .{ + .type = param_type, + .name = if (data[node].rhs == 0) "" else self.ast.tokenSlice(data[node].rhs), + .indexed = if (main[node] != 0) true else false, + }; +} + +pub fn toAbiParameterFromErrorDecl(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiParameter { + const nodes = self.ast.nodes.items(.tag); + const data = self.ast.nodes.items(.data); + const main = self.ast.nodes.items(.main_token); + std.debug.assert(nodes[node] == .error_var_decl); + + const type_slice = self.ast.tokenSlice(main[data[node].lhs]); + const param_type = try ParamType.typeToUnion(type_slice, self.allocator); + + return .{ + .type = param_type, + .name = if (main[node] == 0) "" else self.ast.tokenSlice(main[node]), + }; +} + +pub fn toAbiFallbackMulti(self: HumanAbi, node: Node.Index) Allocator.Error!AbiFallback { + const nodes = self.ast.nodes.items(.tag); + std.debug.assert(nodes[node] == .fallback_proto_multi); + + const ast_fallback = self.ast.fallbackProtoMulti(node); + + return .{ + .type = .fallback, + .stateMutability = if (ast_fallback.payable != null) .payable else .nonpayable, + }; +} + +pub fn toAbiFallbackSimple(self: HumanAbi, node: Node.Index) Allocator.Error!AbiFallback { + const nodes = self.ast.nodes.items(.tag); + std.debug.assert(nodes[node] == .fallback_proto_simple); + + var buffer: [1]Node.Index = undefined; + const ast_fallback = self.ast.fallbackProtoSimple(&buffer, node); + + return .{ + .type = .fallback, + .stateMutability = if (ast_fallback.payable != null) .payable else .nonpayable, + }; +} + +pub fn toAbiReceive(self: HumanAbi, node: Node.Index) (Allocator.Error || error{UnexpectedMutability})!AbiReceive { + const nodes = self.ast.nodes.items(.tag); + std.debug.assert(nodes[node] == .receive_proto); + + const ast_receive = self.ast.receiveProto(node); + + return .{ + .type = .receive, + .stateMutability = if (ast_receive.payable == null) return error.UnexpectedMutability else .payable, + }; +} diff --git a/src/human-readable/ParserNew.zig b/src/human-readable/ParserNew.zig index d354e59e..822696bc 100644 --- a/src/human-readable/ParserNew.zig +++ b/src/human-readable/ParserNew.zig @@ -18,20 +18,29 @@ const Span = union(enum) { multi: Node.Range, }; +/// Allocator used by the parser. allocator: Allocator, +/// Source to parse. source: [:0]const u8, +/// Current parser index token_index: TokenIndex, +/// Slices of all tokenized token tags. token_tags: []const TokenTag, +/// Struct of arrays for the node. nodes: Ast.NodeList, +/// Array list that contains extra data. extra: std.ArrayListUnmanaged(Node.Index), +/// Temporary space used in parsing. scratch: std.ArrayListUnmanaged(Node.Index), +/// Clears any allocated memory. pub fn deinit(self: *Parser) void { self.nodes.deinit(self.allocator); self.extra.deinit(self.allocator); self.scratch.deinit(self.allocator); } +/// Parses all of the source and build the `Ast`. pub fn parseSource(self: *Parser) ParserErrors!void { try self.nodes.append(self.allocator, .{ .tag = .root, @@ -79,10 +88,9 @@ pub fn expectUnit(self: *Parser) ParserErrors!Node.Index { pub fn parseUnit(self: *Parser) ParserErrors!Node.Index { return switch (self.token_tags[self.token_index]) { - .Function, - .Fallback, - .Receive, - => self.parseFunctionProto(), + .Function => self.parseFunctionProto(), + .Fallback => self.parseFallbackProto(), + .Receive => self.parseReceiveProto(), .Constructor => self.parseConstructorProto(), .Event => self.parseEventProto(), .Error => self.parseErrorProto(), @@ -170,6 +178,56 @@ pub fn parseFunctionProto(self: *Parser) ParserErrors!Node.Index { }; } +pub fn parseReceiveProto(self: *Parser) ParserErrors!Node.Index { + const receive_keyword = self.consumeToken(.Receive) orelse return null_node; + + _ = try self.expectToken(.OpenParen); + _ = try self.expectToken(.ClosingParen); + + const specifiers = try self.parseSpecifiers(); + + return self.addNode(.{ + .tag = .receive_proto, + .main_token = receive_keyword, + .data = .{ + .lhs = undefined, + .rhs = specifiers, + }, + }); +} + +pub fn parseFallbackProto(self: *Parser) ParserErrors!Node.Index { + const fallback_keyword = self.consumeToken(.Fallback) orelse return null_node; + + const reserve = try self.reserveNode(.fallback_proto_multi); + errdefer self.unreserveNode(reserve); + + _ = try self.expectToken(.OpenParen); + + const params = try self.parseVariableDecls(); + + const specifiers = try self.parseSpecifiers(); + + return switch (params) { + .zero_one => |elem| self.setNode(reserve, .{ + .tag = .fallback_proto_simple, + .main_token = fallback_keyword, + .data = .{ + .lhs = elem, + .rhs = specifiers, + }, + }), + .multi => |elems| self.setNode(reserve, .{ + .tag = .fallback_proto_multi, + .main_token = fallback_keyword, + .data = .{ + .lhs = try self.addExtraData(elems), + .rhs = specifiers, + }, + }), + }; +} + pub fn parseConstructorProto(self: *Parser) ParserErrors!Node.Index { const constructor_keyword = self.consumeToken(.Constructor) orelse return null_node; @@ -202,21 +260,50 @@ pub fn parseConstructorProto(self: *Parser) ParserErrors!Node.Index { }; } -pub fn parseSpecifiers(self: *Parser) Allocator.Error!Node.Index { +pub fn parseSpecifiers(self: *Parser) ParserErrors!Node.Index { const scratch = self.scratch.items.len; defer self.scratch.shrinkRetainingCapacity(scratch); + var specifier: enum { + seen_visibility, + seen_mutability, + seen_both, + none, + } = .none; + while (true) { switch (self.token_tags[self.token_index]) { .Public, + .External, + .Internal, + .Private, + => { + switch (specifier) { + .seen_visibility, + .seen_both, + => return error.ParsingError, + .seen_mutability => specifier = .seen_both, + .none => specifier = .seen_visibility, + } + + try self.scratch.append(self.allocator, self.nextToken()); + }, .Pure, .Payable, .View, + => { + switch (specifier) { + .seen_mutability, + .seen_both, + => return error.ParsingError, + .seen_visibility => specifier = .seen_both, + .none => specifier = .seen_mutability, + } + + try self.scratch.append(self.allocator, self.nextToken()); + }, .Virtual, .Override, - .Internal, - .Private, - .External, => try self.scratch.append(self.allocator, self.nextToken()), else => break, } diff --git a/src/tests/human-readable/parser_new.test.zig b/src/tests/human-readable/parser_new.test.zig index c10f0b4e..b19bc92b 100644 --- a/src/tests/human-readable/parser_new.test.zig +++ b/src/tests/human-readable/parser_new.test.zig @@ -4,11 +4,18 @@ const testing = std.testing; const Parser = @import("../../human-readable/ParserNew.zig"); const Ast = @import("../../human-readable/Ast.zig"); +const HumanAbi = @import("../../human-readable/HumanAbi.zig"); test "Human readable" { - var ast = try Ast.parse(testing.allocator, "function receive(address bar, uint bar) view external\nstruct Foo {address bar;}"); + var ast = try Ast.parse(testing.allocator, "struct Foo{uint bar;}"); defer ast.deinit(testing.allocator); + // const abi_gen: HumanAbi = .{ + // .allocator = testing.allocator, + // .ast = &ast, + // }; + std.debug.print("FOOOOO: {any}\n", .{ast.nodes.items(.tag)}); - std.debug.print("FOOOOO: {s}\n", .{ast.getNodeSource(1)}); + // std.debug.print("FOOOOO: {s}\n", .{ast.getNodeSource(1)}); + // std.debug.print("FOOOOO: {}\n", .{try abi_gen.toStructParamComponent(1)}); } From 6f990ae007b24b4fa7f2ac53b16668c461cf426c Mon Sep 17 00:00:00 2001 From: Raiden1411 <67233402+Raiden1411@users.noreply.github.com> Date: Fri, 4 Oct 2024 23:40:46 +0100 Subject: [PATCH 10/18] human: complete the first iteration of the new abi gen --- src/human-readable/Ast.zig | 10 +- src/human-readable/HumanAbi.zig | 251 +++- src/human-readable/Parser.zig | 1219 +++++++++++------- src/human-readable/ParserNew.zig | 903 ------------- src/human-readable/ParserOld.zig | 632 +++++++++ src/human-readable/abi_parsing.zig | 66 +- src/tests/human-readable/parser_new.test.zig | 17 +- 7 files changed, 1659 insertions(+), 1439 deletions(-) delete mode 100644 src/human-readable/ParserNew.zig create mode 100644 src/human-readable/ParserOld.zig diff --git a/src/human-readable/Ast.zig b/src/human-readable/Ast.zig index 311ec67f..cae78583 100644 --- a/src/human-readable/Ast.zig +++ b/src/human-readable/Ast.zig @@ -3,7 +3,7 @@ const token = @import("tokens.zig"); const tokenizer = @import("lexer.zig"); const Allocator = std.mem.Allocator; -const Parser = @import("ParserNew.zig"); +const Parser = @import("Parser.zig"); const TokenTag = token.Tag.SoliditySyntax; const Ast = @This(); @@ -75,9 +75,9 @@ pub fn deinit(self: *Ast, allocator: Allocator) void { allocator.free(self.extra_data); } -pub fn functionProto(self: Ast, node: Node.Index) ast.ConstructorDecl { +pub fn functionProto(self: Ast, node: Node.Index) ast.FunctionDecl { const nodes = self.nodes.items(.tag); - std.debug.assert(nodes[node] == .function_proto_multi); + std.debug.assert(nodes[node] == .function_proto); const data = self.nodes.items(.data)[node]; const main_token = self.nodes.items(.main_token)[node]; @@ -121,7 +121,7 @@ pub fn functionProto(self: Ast, node: Node.Index) ast.ConstructorDecl { pub fn functionProtoOne(self: Ast, node_buffer: *[1]Node.Index, node: Node.Index) ast.FunctionDecl { const nodes = self.nodes.items(.tag); - std.debug.assert(nodes[node] == .function_proto_simple); + std.debug.assert(nodes[node] == .function_proto_one); const data = self.nodes.items(.data)[node]; const main_token = self.nodes.items(.main_token)[node]; @@ -165,7 +165,7 @@ pub fn functionProtoOne(self: Ast, node_buffer: *[1]Node.Index, node: Node.Index return result; } -pub fn functionProtoMulti(self: Ast, node: Node.Index) ast.ConstructorDecl { +pub fn functionProtoMulti(self: Ast, node: Node.Index) ast.FunctionDecl { const nodes = self.nodes.items(.tag); std.debug.assert(nodes[node] == .function_proto_multi); diff --git a/src/human-readable/HumanAbi.zig b/src/human-readable/HumanAbi.zig index 46d26eda..5794855a 100644 --- a/src/human-readable/HumanAbi.zig +++ b/src/human-readable/HumanAbi.zig @@ -7,26 +7,259 @@ const tokens = @import("tokens.zig"); const AbiParameter = param.AbiParameter; const AbiEventParameter = param.AbiEventParameter; const Allocator = std.mem.Allocator; -const Ast = @import("Ast.zig"); +const Abi = abi.Abi; const AbiConstructor = abi.Constructor; const AbiEvent = abi.Event; const AbiError = abi.Error; +const AbiItem = abi.AbiItem; const AbiFallback = abi.Fallback; +const AbiFunction = abi.Function; const AbiReceive = abi.Receive; +const Ast = @import("Ast.zig"); const Node = Ast.Node; const ParamErrors = param_types.ParamErrors; const ParamType = param_types.ParamType; -const Parser = @import("ParserNew.zig"); +const Parser = @import("Parser.zig"); const StateMutability = @import("../abi/state_mutability.zig").StateMutability; -/// Set of erros when generating the ABI +/// Set of errors when converting to the abi pub const HumanAbiErrors = ParamErrors || Allocator.Error; +/// Set of erros when generating the ABI +pub const Errors = HumanAbiErrors || Parser.ParserErrors || error{ UnexpectedMutability, UnexpectedNode }; + const HumanAbi = @This(); +/// Allocator to use to generate the ABI. +/// It's recommend to use an `ArenaAllocator` for this. allocator: Allocator, +/// Ast generated by the parser so that we can use to generate the ABI. ast: *const Ast, +/// Parses the source, builds the Ast and generates the ABI. +/// +/// It's recommend to use an `ArenaAllocator` for this. +pub fn parse(arena: Allocator, source: [:0]const u8) Errors!Abi { + var ast = try Ast.parse(arena, source); + defer ast.deinit(arena); + + const abi_gen: HumanAbi = .{ + .allocator = arena, + .ast = &ast, + }; + + return abi_gen.toAbi(); +} +/// Generates the `Abi` from the ast nodes. +pub fn toAbi(self: HumanAbi) (HumanAbiErrors || error{ UnexpectedNode, UnexpectedMutability })!Abi { + const nodes = self.ast.nodes.items(.tag); + + var list = std.ArrayList(AbiItem).init(self.allocator); + errdefer list.deinit(); + + for (nodes, 0..) |node, index| { + switch (node) { + .function_proto, + .function_proto_one, + .function_proto_multi, + .function_proto_simple, + .event_proto_multi, + .event_proto_simple, + .error_proto_multi, + .error_proto_simple, + .constructor_proto_multi, + .constructor_proto_simple, + .fallback_proto_multi, + .fallback_proto_simple, + .receive_proto, + => try list.append(try self.toAbiItem(@intCast(index))), + else => continue, + } + } + + return list.toOwnedSlice(); +} + +pub fn toAbiItem(self: HumanAbi, node: Node.Index) (HumanAbiErrors || error{ UnexpectedNode, UnexpectedMutability })!AbiItem { + const nodes = self.ast.nodes.items(.tag); + + return switch (nodes[node]) { + .function_proto => .{ .abiFunction = try self.toAbiFunction(node) }, + .function_proto_one => .{ .abiFunction = try self.toAbiFunctionOne(node) }, + .function_proto_multi => .{ .abiFunction = try self.toAbiFunctionMulti(node) }, + .function_proto_simple => .{ .abiFunction = try self.toAbiFunctionSimple(node) }, + .event_proto_multi => .{ .abiEvent = try self.toAbiEventSimple(node) }, + .event_proto_simple => .{ .abiEvent = try self.toAbiEventMulti(node) }, + .error_proto_multi => .{ .abiError = try self.toAbiErrorMulti(node) }, + .error_proto_simple => .{ .abiError = try self.toAbiErrorSimple(node) }, + .constructor_proto_multi => .{ .abiConstructor = try self.toAbiConstructorMulti(node) }, + .constructor_proto_simple => .{ .abiConstructor = try self.toAbiConstructorSimple(node) }, + .fallback_proto_multi => .{ .abiFallback = try self.toAbiFallbackMulti(node) }, + .fallback_proto_simple => .{ .abiFallback = try self.toAbiFallbackSimple(node) }, + .receive_proto => .{ .abiReceive = try self.toAbiReceive(node) }, + else => error.UnexpectedNode, + }; +} + +pub fn toAbiFunction(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiFunction { + const nodes = self.ast.nodes.items(.tag); + std.debug.assert(nodes[node] == .function_proto); + + const ast_function = self.ast.functionProto(node); + std.debug.assert(ast_function.ast.return_params != null); + + const params = try self.toAbiParameters(ast_function.ast.params); + const returns = try self.toAbiParameters(ast_function.ast.return_params.?); + + const mutability: StateMutability = blk: { + if (ast_function.payable != null) + break :blk .payable; + + if (ast_function.view != null) + break :blk .view; + + if (ast_function.pure != null) + break :blk .pure; + + break :blk .nonpayable; + }; + + return .{ + .type = .function, + .name = self.ast.tokenSlice(ast_function.name), + .inputs = params, + .stateMutability = mutability, + .outputs = returns, + }; +} + +pub fn toAbiFunctionOne(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiFunction { + const nodes = self.ast.nodes.items(.tag); + std.debug.assert(nodes[node] == .function_proto_one); + + var buffer: [1]Node.Index = undefined; + const ast_function = self.ast.functionProtoOne(&buffer, node); + std.debug.assert(ast_function.ast.return_params != null); + + const params = try self.toAbiParameters(ast_function.ast.params); + const returns = try self.toAbiParameters(ast_function.ast.return_params.?); + + const mutability: StateMutability = blk: { + if (ast_function.payable != null) + break :blk .payable; + + if (ast_function.view != null) + break :blk .view; + + if (ast_function.pure != null) + break :blk .pure; + + break :blk .nonpayable; + }; + + return .{ + .type = .function, + .name = self.ast.tokenSlice(ast_function.name), + .inputs = params, + .stateMutability = mutability, + .outputs = returns, + }; +} + +pub fn toAbiFunctionMulti(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiFunction { + const nodes = self.ast.nodes.items(.tag); + std.debug.assert(nodes[node] == .function_proto_multi); + + const ast_function = self.ast.functionProtoMulti(node); + std.debug.assert(ast_function.ast.return_params == null); + + const params = try self.toAbiParameters(ast_function.ast.params); + + const mutability: StateMutability = blk: { + if (ast_function.payable != null) + break :blk .payable; + + if (ast_function.view != null) + break :blk .view; + + if (ast_function.pure != null) + break :blk .pure; + + break :blk .nonpayable; + }; + + return .{ + .type = .function, + .name = self.ast.tokenSlice(ast_function.name), + .inputs = params, + .stateMutability = mutability, + .outputs = &.{}, + }; +} + +pub fn toAbiFunctionSimple(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiFunction { + const nodes = self.ast.nodes.items(.tag); + std.debug.assert(nodes[node] == .function_proto_simple); + + var buffer: [1]Node.Index = undefined; + const ast_function = self.ast.functionProtoSimple(&buffer, node); + std.debug.assert(ast_function.ast.return_params == null); + + const params = try self.toAbiParameters(ast_function.ast.params); + + const mutability: StateMutability = blk: { + if (ast_function.payable != null) + break :blk .payable; + + if (ast_function.view != null) + break :blk .view; + + if (ast_function.pure != null) + break :blk .pure; + + break :blk .nonpayable; + }; + + return .{ + .type = .function, + .name = self.ast.tokenSlice(ast_function.name), + .inputs = params, + .stateMutability = mutability, + .outputs = &.{}, + }; +} + +pub fn toStructComponents(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiParameter { + const nodes = self.ast.nodes.items(.tag); + std.debug.assert(nodes[node] == .struct_decl); + + const struct_decl = self.ast.structDecl(node); + + const params = try self.toAbiParametersFromDecl(struct_decl.ast.members); + + return AbiParameter{ + .type = .{ .tuple = {} }, + .name = self.ast.tokenSlice(struct_decl.name), + .components = params, + }; +} + +pub fn toStructComponentsOne(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiParameter { + const nodes = self.ast.nodes.items(.tag); + std.debug.assert(nodes[node] == .struct_decl_one); + + var buffer: [1]Node.Index = undefined; + const struct_decl = self.ast.structDeclOne(&buffer, node); + + const params = try self.toAbiParametersFromDecl(struct_decl.ast.members); + + return AbiParameter{ + .type = .{ .tuple = {} }, + .name = self.ast.tokenSlice(struct_decl.name), + .components = params, + }; +} + pub fn toAbiConstructorMulti(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiConstructor { const nodes = self.ast.nodes.items(.tag); std.debug.assert(nodes[node] == .constructor_proto_multi); @@ -95,7 +328,7 @@ pub fn toAbiErrorMulti(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiError const ast_error = self.ast.errorProtoMulti(node); - const params = try self.toAbiParametersFromErrorDecl(ast_error.ast.params); + const params = try self.toAbiParametersFromDecl(ast_error.ast.params); return .{ .type = .@"error", @@ -111,7 +344,7 @@ pub fn toAbiErrorSimple(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiErro var buffer: [1]Node.Index = undefined; const ast_error = self.ast.errorProtoSimple(&buffer, node); - const params = try self.toAbiParametersFromErrorDecl(ast_error.ast.params); + const params = try self.toAbiParametersFromDecl(ast_error.ast.params); return .{ .type = .@"error", @@ -131,12 +364,12 @@ pub fn toAbiParameters(self: HumanAbi, nodes: []const Node.Index) HumanAbiErrors return params.toOwnedSlice(); } -pub fn toAbiParametersFromErrorDecl(self: HumanAbi, nodes: []const Node.Index) HumanAbiErrors![]const AbiParameter { +pub fn toAbiParametersFromDecl(self: HumanAbi, nodes: []const Node.Index) HumanAbiErrors![]const AbiParameter { var params = try std.ArrayList(AbiParameter).initCapacity(self.allocator, nodes.len); errdefer params.deinit(); for (nodes) |node| { - params.appendAssumeCapacity(try self.toAbiParameterFromErrorDecl(node)); + params.appendAssumeCapacity(try self.toAbiParameterFromDecl(node)); } return params.toOwnedSlice(); @@ -184,11 +417,11 @@ pub fn toAbiEventParameter(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiE }; } -pub fn toAbiParameterFromErrorDecl(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiParameter { +pub fn toAbiParameterFromDecl(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiParameter { const nodes = self.ast.nodes.items(.tag); const data = self.ast.nodes.items(.data); const main = self.ast.nodes.items(.main_token); - std.debug.assert(nodes[node] == .error_var_decl); + std.debug.assert(nodes[node] == .error_var_decl or nodes[node] == .struct_field); const type_slice = self.ast.tokenSlice(main[data[node].lhs]); const param_type = try ParamType.typeToUnion(type_slice, self.allocator); diff --git a/src/human-readable/Parser.zig b/src/human-readable/Parser.zig index e66a3ec9..822696bc 100644 --- a/src/human-readable/Parser.zig +++ b/src/human-readable/Parser.zig @@ -1,632 +1,903 @@ const std = @import("std"); -const testing = std.testing; -const abi = @import("../abi/abi.zig"); - -// Types -const Abi = abi.Abi; -const AbiItem = abi.AbiItem; -const AbiParameter = @import("../abi/abi_parameter.zig").AbiParameter; -const AbiEventParameter = @import("../abi/abi_parameter.zig").AbiEventParameter; + const Allocator = std.mem.Allocator; -const Constructor = abi.Constructor; -const Error = abi.Error; -const Event = abi.Event; -const Fallback = abi.Fallback; -const Function = abi.Function; -const Receive = abi.Receive; -const Lexer = @import("lexer.zig").Lexer; -const StateMutability = @import("../abi/state_mutability.zig").StateMutability; -const ParamErrors = @import("../abi/param_type.zig").ParamErrors; -const ParamType = @import("../abi/param_type.zig").ParamType; -const Tokens = @import("tokens.zig").Tag.SoliditySyntax; - -pub const TokenList = std.MultiArrayList(struct { - token_type: Tokens, - start: u32, - end: u32, -}); - -/// Set of possible errors that can happen while parsing. -pub const ParseErrors = error{ - InvalidDataLocation, - UnexceptedToken, - InvalidType, - ExpectedCommaAfterParam, - EmptyReturnParams, -} || ParamErrors || Allocator.Error; +const Ast = @import("Ast.zig"); +const Node = Ast.Node; +const TokenIndex = Ast.TokenIndex; +const TokenTag = @import("tokens.zig").Tag.SoliditySyntax; const Parser = @This(); -/// The allocator used in parsing. -alloc: Allocator, -/// Slice of tokens produced by the lexer. -tokens: []const Tokens, -/// Slice of start positions of the source of the token. -tokens_start: []const u32, -/// Slice of end positions of the source of the token. -tokens_end: []const u32, -/// The current token index. -token_index: u32, -/// The slice that cotains the token source. -source: []const u8, -/// Hashmap of the preparsed structs. -structs: std.StringHashMapUnmanaged([]const AbiParameter), - -/// Parse a string or a multi line string with solidity signatures.\ -/// This will return all signatures as a slice of `AbiItem`. -/// -/// This supports parsing struct signatures if its intended to use -/// The struct signatures must be defined top down. -/// -/// **Example** -/// ```zig -/// var lex = Lexer.init(source); -/// -/// var list = Parser.TokenList{}; -/// defer list.deinit(allocator); -/// -/// while (true) { -/// const tok = lex.scan(); -/// -/// try list.append(allocator, .{ -/// .token_type = tok.syntax, -/// .start = tok.location.start, -/// .end = tok.location.end, -/// }); -/// -/// if (tok.syntax == .EndOfFileToken) break; -/// } -/// -/// var parser: Parser = .{ -/// .alloc = allocator, -/// .tokens = list.items(.token_type), -/// .tokens_start = list.items(.start), -/// .tokens_end = list.items(.end), -/// .token_index = 0, -/// .source = source, -/// .structs = .{}, -/// }; -/// -/// const abi = try parser.parseAbiProto(); -/// ``` -pub fn parseAbiProto(p: *Parser) ParseErrors!Abi { - var abi_list = std.ArrayList(AbiItem).init(p.alloc); +/// Errors that can happing whilest parsing the source code. +pub const ParserErrors = error{ParsingError} || Allocator.Error; + +const null_node: Node.Index = 0; + +const Span = union(enum) { + zero_one: Node.Index, + multi: Node.Range, +}; + +/// Allocator used by the parser. +allocator: Allocator, +/// Source to parse. +source: [:0]const u8, +/// Current parser index +token_index: TokenIndex, +/// Slices of all tokenized token tags. +token_tags: []const TokenTag, +/// Struct of arrays for the node. +nodes: Ast.NodeList, +/// Array list that contains extra data. +extra: std.ArrayListUnmanaged(Node.Index), +/// Temporary space used in parsing. +scratch: std.ArrayListUnmanaged(Node.Index), + +/// Clears any allocated memory. +pub fn deinit(self: *Parser) void { + self.nodes.deinit(self.allocator); + self.extra.deinit(self.allocator); + self.scratch.deinit(self.allocator); +} - while (true) { - if (p.tokens[p.token_index] == .Struct) { - try p.parseStructProto(); - continue; - } +/// Parses all of the source and build the `Ast`. +pub fn parseSource(self: *Parser) ParserErrors!void { + try self.nodes.append(self.allocator, .{ + .tag = .root, + .main_token = 0, + .data = undefined, + }); - try abi_list.append(try p.parseAbiItemProto()); + const members = try self.parseUnits(); - if (p.tokens[p.token_index] == .EndOfFileToken) break; - } + if (self.token_tags[self.token_index] != .EndOfFileToken) + return error.ParsingError; - return abi_list.toOwnedSlice(); -} - -/// Parse a single solidity signature based on expected tokens. -/// -/// Will return an error if the token is not expected. -pub fn parseAbiItemProto(p: *Parser) ParseErrors!AbiItem { - return switch (p.tokens[p.token_index]) { - .Function => .{ .abiFunction = try p.parseFunctionFnProto() }, - .Event => .{ .abiEvent = try p.parseEventFnProto() }, - .Error => .{ .abiError = try p.parseErrorFnProto() }, - .Constructor => .{ .abiConstructor = try p.parseConstructorFnProto() }, - .Fallback => .{ .abiFallback = try p.parseFallbackFnProto() }, - .Receive => .{ .abiReceive = try p.parseReceiveFnProto() }, - inline else => error.UnexceptedToken, + self.nodes.items(.data)[0] = .{ + .lhs = members.start, + .rhs = members.end, }; } -/// Parse single solidity function signature.\ -/// FunctionProto -> Function KEYWORD, Identifier, OpenParen, ParamDecls?, ClosingParen, Visibility?, StateMutability?, Returns? -pub fn parseFunctionFnProto(p: *Parser) ParseErrors!Function { - _ = try p.expectToken(.Function); - const name = p.parseIdentifier().?; +pub fn parseUnits(self: *Parser) ParserErrors!Node.Range { + const scratch = self.scratch.items.len; + defer self.scratch.shrinkRetainingCapacity(scratch); + + while (true) { + switch (self.token_tags[self.token_index]) { + .EndOfFileToken => break, + else => {}, + } + + try self.scratch.append(self.allocator, try self.expectUnit()); + } - _ = try p.expectToken(.OpenParen); + const slice = self.scratch.items[scratch..]; - const inputs: []const AbiParameter = if (p.tokens[p.token_index] == .ClosingParen) &.{} else try p.parseFuncParamsDecl(); + return self.listToSpan(slice); +} - _ = try p.expectToken(.ClosingParen); +pub fn expectUnit(self: *Parser) ParserErrors!Node.Index { + const unit = try self.parseUnit(); - try p.parseVisibility(); + if (unit == 0) + return error.ParsingError; - const state: StateMutability = switch (p.tokens[p.token_index]) { - .Payable => .payable, - .View => .view, - .Pure => .pure, - inline else => .nonpayable, - }; + return unit; +} - if (state != .nonpayable) _ = p.nextToken(); +pub fn parseUnit(self: *Parser) ParserErrors!Node.Index { + return switch (self.token_tags[self.token_index]) { + .Function => self.parseFunctionProto(), + .Fallback => self.parseFallbackProto(), + .Receive => self.parseReceiveProto(), + .Constructor => self.parseConstructorProto(), + .Event => self.parseEventProto(), + .Error => self.parseErrorProto(), + .Struct => self.parseStructDecl(), + else => return null_node, + }; +} - if (p.consumeToken(.Returns)) |_| { - _ = try p.expectToken(.OpenParen); +pub fn parseFunctionProto(self: *Parser) ParserErrors!Node.Index { + const keyword = self.consumeToken(.Function) orelse return null_node; - const outputs: []const AbiParameter = if (p.tokens[p.token_index] == .ClosingParen) return error.EmptyReturnParams else try p.parseFuncParamsDecl(); + const reserve = try self.reserveNode(.function_proto); + errdefer self.unreserveNode(reserve); - _ = try p.expectToken(.ClosingParen); + const identifier = switch (self.token_tags[self.token_index]) { + .Identifier, + .Fallback, + .Receive, + => self.nextToken(), + else => return null_node, + }; - return .{ .type = .function, .name = name, .inputs = inputs, .outputs = outputs, .stateMutability = state }; + _ = try self.expectToken(.OpenParen); + + const params = try self.parseVariableDecls(); + const specifiers = try self.parseSpecifiers(); + + if (self.consumeToken(.Returns)) |_| { + _ = try self.expectToken(.OpenParen); + const returns = try self.parseReturnParams(); + + return switch (params) { + .zero_one => |elem| return self.setNode(reserve, .{ + .tag = .function_proto_one, + .main_token = keyword, + .data = .{ + .lhs = try self.addExtraData(Node.FunctionProtoOne{ + .specifiers = specifiers, + .identifier = identifier, + .param = elem, + }), + .rhs = try self.addExtraData(returns), + }, + }), + .multi => |elem| return self.setNode(reserve, .{ + .tag = .function_proto, + .main_token = keyword, + .data = .{ + .lhs = try self.addExtraData(Node.FunctionProto{ + .specifiers = specifiers, + .identifier = identifier, + .params_start = elem.start, + .params_end = elem.end, + }), + .rhs = try self.addExtraData(returns), + }, + }), + }; } - return .{ .type = .function, .name = name, .inputs = inputs, .outputs = &.{}, .stateMutability = state }; + return switch (params) { + .zero_one => |elem| return self.setNode(reserve, .{ + .tag = .function_proto_simple, + .main_token = keyword, + .data = .{ + .lhs = try self.addExtraData(Node.FunctionProtoSimple{ + .identifier = identifier, + .param = elem, + }), + .rhs = specifiers, + }, + }), + .multi => |elem| return self.setNode(reserve, .{ + .tag = .function_proto_multi, + .main_token = keyword, + .data = .{ + .lhs = try self.addExtraData(Node.FunctionProtoMulti{ + .identifier = identifier, + .params_start = elem.start, + .params_end = elem.end, + }), + .rhs = specifiers, + }, + }), + }; } -/// Parse single solidity event signature.\ -/// EventProto -> Event KEYWORD, Identifier, OpenParen, ParamDecls?, ClosingParen -pub fn parseEventFnProto(p: *Parser) ParseErrors!Event { - _ = try p.expectToken(.Event); - const name = p.parseIdentifier().?; +pub fn parseReceiveProto(self: *Parser) ParserErrors!Node.Index { + const receive_keyword = self.consumeToken(.Receive) orelse return null_node; - _ = try p.expectToken(.OpenParen); + _ = try self.expectToken(.OpenParen); + _ = try self.expectToken(.ClosingParen); - const inputs: []const AbiEventParameter = if (p.tokens[p.token_index] == .ClosingParen) &.{} else try p.parseEventParamsDecl(); + const specifiers = try self.parseSpecifiers(); - _ = try p.expectToken(.ClosingParen); - - return .{ .type = .event, .inputs = inputs, .name = name }; + return self.addNode(.{ + .tag = .receive_proto, + .main_token = receive_keyword, + .data = .{ + .lhs = undefined, + .rhs = specifiers, + }, + }); } -/// Parse single solidity error signature.\ -/// ErrorProto -> Error KEYWORD, Identifier, OpenParen, ParamDecls?, ClosingParen -pub fn parseErrorFnProto(p: *Parser) ParseErrors!Error { - _ = try p.expectToken(.Error); - const name = p.parseIdentifier().?; +pub fn parseFallbackProto(self: *Parser) ParserErrors!Node.Index { + const fallback_keyword = self.consumeToken(.Fallback) orelse return null_node; - _ = try p.expectToken(.OpenParen); + const reserve = try self.reserveNode(.fallback_proto_multi); + errdefer self.unreserveNode(reserve); - const inputs: []const AbiParameter = if (p.tokens[p.token_index] == .ClosingParen) &.{} else try p.parseErrorParamsDecl(); + _ = try self.expectToken(.OpenParen); - _ = try p.expectToken(.ClosingParen); + const params = try self.parseVariableDecls(); - return .{ .type = .@"error", .inputs = inputs, .name = name }; + const specifiers = try self.parseSpecifiers(); + + return switch (params) { + .zero_one => |elem| self.setNode(reserve, .{ + .tag = .fallback_proto_simple, + .main_token = fallback_keyword, + .data = .{ + .lhs = elem, + .rhs = specifiers, + }, + }), + .multi => |elems| self.setNode(reserve, .{ + .tag = .fallback_proto_multi, + .main_token = fallback_keyword, + .data = .{ + .lhs = try self.addExtraData(elems), + .rhs = specifiers, + }, + }), + }; } -/// Parse single solidity constructor signature.\ -/// ConstructorProto -> Constructor KEYWORD, OpenParen, ParamDecls?, ClosingParen, StateMutability? -pub fn parseConstructorFnProto(p: *Parser) ParseErrors!Constructor { - _ = try p.expectToken(.Constructor); +pub fn parseConstructorProto(self: *Parser) ParserErrors!Node.Index { + const constructor_keyword = self.consumeToken(.Constructor) orelse return null_node; + + const reserve = try self.reserveNode(.constructor_proto_multi); + errdefer self.unreserveNode(reserve); - _ = try p.expectToken(.OpenParen); + _ = try self.expectToken(.OpenParen); - const inputs: []const AbiParameter = if (p.tokens[p.token_index] == .ClosingParen) &.{} else try p.parseFuncParamsDecl(); + const params = try self.parseVariableDecls(); - _ = try p.expectToken(.ClosingParen); + const specifiers = try self.parseSpecifiers(); - return switch (p.tokens[p.token_index]) { - .Payable => .{ .type = .constructor, .stateMutability = .payable, .inputs = inputs }, - inline else => .{ .type = .constructor, .stateMutability = .nonpayable, .inputs = inputs }, + return switch (params) { + .zero_one => |elem| self.setNode(reserve, .{ + .tag = .constructor_proto_simple, + .main_token = constructor_keyword, + .data = .{ + .lhs = elem, + .rhs = specifiers, + }, + }), + .multi => |elems| self.setNode(reserve, .{ + .tag = .constructor_proto_multi, + .main_token = constructor_keyword, + .data = .{ + .lhs = try self.addExtraData(elems), + .rhs = specifiers, + }, + }), }; } -/// Parse single solidity struct signature.\ -/// StructProto -> Struct KEYWORD, Identifier, OpenBrace, ParamDecls, ClosingBrace -pub fn parseStructProto(p: *Parser) ParseErrors!void { - _ = try p.expectToken(.Struct); - - const name = p.parseIdentifier().?; +pub fn parseSpecifiers(self: *Parser) ParserErrors!Node.Index { + const scratch = self.scratch.items.len; + defer self.scratch.shrinkRetainingCapacity(scratch); - _ = try p.expectToken(.OpenBrace); + var specifier: enum { + seen_visibility, + seen_mutability, + seen_both, + none, + } = .none; - const params = try p.parseStructParamDecls(); - - _ = try p.expectToken(.ClosingBrace); + while (true) { + switch (self.token_tags[self.token_index]) { + .Public, + .External, + .Internal, + .Private, + => { + switch (specifier) { + .seen_visibility, + .seen_both, + => return error.ParsingError, + .seen_mutability => specifier = .seen_both, + .none => specifier = .seen_visibility, + } - try p.structs.put(p.alloc, name, params); -} + try self.scratch.append(self.allocator, self.nextToken()); + }, + .Pure, + .Payable, + .View, + => { + switch (specifier) { + .seen_mutability, + .seen_both, + => return error.ParsingError, + .seen_visibility => specifier = .seen_both, + .none => specifier = .seen_mutability, + } -/// Parse single solidity fallback signature.\ -/// FallbackProto -> Fallback KEYWORD, OpenParen, ClosingParen, StateMutability? -pub fn parseFallbackFnProto(p: *Parser) error{UnexceptedToken}!Fallback { - _ = try p.expectToken(.Fallback); - _ = try p.expectToken(.OpenParen); - _ = try p.expectToken(.ClosingParen); + try self.scratch.append(self.allocator, self.nextToken()); + }, + .Virtual, + .Override, + => try self.scratch.append(self.allocator, self.nextToken()), + else => break, + } + } - switch (p.tokens[p.token_index]) { - .Payable => { - if (p.tokens[p.token_index + 1] != .EndOfFileToken) return error.UnexceptedToken; + const slice = self.scratch.items[scratch..]; - return .{ .type = .fallback, .stateMutability = .payable }; + return self.addNode(.{ + .tag = .specifiers, + .main_token = try self.addExtraData(try self.listToSpan(slice)), + .data = .{ + .lhs = undefined, + .rhs = undefined, }, - .EndOfFileToken => return .{ .type = .fallback, .stateMutability = .nonpayable }, - inline else => return error.UnexceptedToken, - } + }); } -/// Parse single solidity receive signature.\ -/// ReceiveProto -> Receive KEYWORD, OpenParen, ClosingParen, External, Payable -pub fn parseReceiveFnProto(p: *Parser) error{UnexceptedToken}!Receive { - _ = try p.expectToken(.Receive); - _ = try p.expectToken(.OpenParen); - _ = try p.expectToken(.ClosingParen); - _ = try p.expectToken(.External); - _ = try p.expectToken(.Payable); +pub fn parseErrorProto(self: *Parser) ParserErrors!Node.Index { + const error_keyword = self.consumeToken(.Error) orelse return null_node; - return .{ .type = .receive, .stateMutability = .payable }; -} + const reserve = try self.reserveNode(.error_proto_multi); + errdefer self.unreserveNode(reserve); -/// Parse solidity function params.\ -/// TypeExpr, DataLocation?, Identifier?, Comma? -pub fn parseFuncParamsDecl(p: *Parser) ParseErrors![]const AbiParameter { - var param_list = std.ArrayList(AbiParameter).init(p.alloc); + const identifier = try self.expectToken(.Identifier); - while (true) { - const tuple_param = if (p.consumeToken(.OpenParen) != null) try p.parseTuple(AbiParameter) else null; + _ = try self.expectToken(.OpenParen); - if (tuple_param != null) { - try param_list.append(tuple_param.?); + const params = try self.parseErrorVarDecls(); - switch (p.tokens[p.token_index]) { - .Comma => p.token_index += 1, - .ClosingParen => break, - .EndOfFileToken => break, - inline else => return error.ExpectedCommaAfterParam, - } + return switch (params) { + .zero_one => |elem| self.setNode(reserve, .{ + .tag = .error_proto_simple, + .main_token = error_keyword, + .data = .{ + .lhs = identifier, + .rhs = elem, + }, + }), + .multi => |elems| self.setNode(reserve, .{ + .tag = .error_proto_multi, + .main_token = error_keyword, + .data = .{ + .lhs = identifier, + .rhs = try self.addExtraData(elems), + }, + }), + }; +} - continue; - } +pub fn parseEventProto(self: *Parser) ParserErrors!Node.Index { + const event_keyword = self.consumeToken(.Event) orelse return null_node; - var components: ?[]const AbiParameter = null; - const abitype = param_type: { - if (p.parseTypeExpr()) |result| break :param_type result else |err| { - // Before failing we check if he have an Identifier token - // And see it was defined as a struct. - const last = p.token_index - 1; + const reserve = try self.reserveNode(.event_proto_multi); + errdefer self.unreserveNode(reserve); - if (p.tokens[last] == .Identifier) { - const name = p.source[p.tokens_start[last]..p.tokens_end[last]]; + const identifier = try self.expectToken(.Identifier); - if (p.structs.get(name)) |val| { - components = val; - const start = p.token_index; - const arr = if (try p.parseArrayType()) |index| p.source[p.tokens_start[start]..p.tokens_end[index]] else ""; + _ = try self.expectToken(.OpenParen); - break :param_type try ParamType.typeToUnion(try std.fmt.allocPrint(p.alloc, "tuple{s}", .{arr}), p.alloc); - } + const params = try self.parseEventVarDecls(); - return err; - } + _ = self.consumeToken(.Anonymous); - return err; - } - }; + return switch (params) { + .zero_one => |elem| self.setNode(reserve, .{ + .tag = .event_proto_simple, + .main_token = event_keyword, + .data = .{ + .lhs = identifier, + .rhs = elem, + }, + }), + .multi => |elems| self.setNode(reserve, .{ + .tag = .event_proto_multi, + .main_token = event_keyword, + .data = .{ + .lhs = identifier, + .rhs = try self.addExtraData(elems), + }, + }), + }; +} - const location = p.parseDataLocation(); - if (location) |tok| { - _ = p.consumeToken(tok); - switch (tok) { - .Indexed => return error.InvalidDataLocation, - .Memory, .Calldata, .Storage => { - const isValid = switch (abitype) { - .string, .bytes => true, - .dynamicArray => true, - .fixedArray => true, - inline else => false, - }; - - if (!isValid) return error.InvalidDataLocation; - }, - inline else => {}, - } - } +pub fn parseEventVarDecls(self: *Parser) ParserErrors!Span { + const scratch = self.scratch.items.len; + defer self.scratch.shrinkRetainingCapacity(scratch); - const name = p.parseIdentifier() orelse ""; - const param = .{ .type = abitype, .name = name, .internalType = null, .components = components }; + while (true) { + if (self.consumeToken(.ClosingParen)) |_| break; - try param_list.append(param); + const field = try self.expectEventVarDecl(); + try self.scratch.append(self.allocator, field); - switch (p.tokens[p.token_index]) { - .Comma => p.token_index += 1, - .ClosingParen => break, - .EndOfFileToken => break, - inline else => return error.ExpectedCommaAfterParam, + switch (self.token_tags[self.token_index]) { + .Comma => { + if (self.token_tags[self.token_index + 1] == .ClosingParen) + return error.ParsingError; + self.token_index += 1; + }, + .ClosingParen => { + self.token_index += 1; + break; + }, + else => return error.ParsingError, } } - return try param_list.toOwnedSlice(); + const slice = self.scratch.items[scratch..]; + + return switch (slice.len) { + 0 => Span{ .zero_one = 0 }, + 1 => Span{ .zero_one = slice[0] }, + else => Span{ .multi = try self.listToSpan(slice) }, + }; } -/// Parse solidity event params.\ -/// TypeExpr, DataLocation?, Identifier?, Comma? -pub fn parseEventParamsDecl(p: *Parser) ParseErrors![]const AbiEventParameter { - var param_list = std.ArrayList(AbiEventParameter).init(p.alloc); +pub fn parseErrorVarDecls(self: *Parser) ParserErrors!Span { + const scratch = self.scratch.items.len; + defer self.scratch.shrinkRetainingCapacity(scratch); while (true) { - const tuple_param = if (p.consumeToken(.OpenParen) != null) try p.parseTuple(AbiEventParameter) else null; - - if (tuple_param) |t_param| { - try param_list.append(t_param); + if (self.consumeToken(.ClosingParen)) |_| break; - switch (p.tokens[p.token_index]) { - .Comma => p.token_index += 1, - .ClosingParen => break, - .EndOfFileToken => break, - inline else => return error.ExpectedCommaAfterParam, - } + const field = try self.expectErrorVarDecl(); + try self.scratch.append(self.allocator, field); - continue; + switch (self.token_tags[self.token_index]) { + .Comma => { + if (self.token_tags[self.token_index + 1] == .ClosingParen) + return error.ParsingError; + self.token_index += 1; + }, + .ClosingParen => { + self.token_index += 1; + break; + }, + else => return error.ParsingError, } + } - var components: ?[]const AbiParameter = null; - const abitype = param_type: { - if (p.parseTypeExpr()) |result| break :param_type result else |err| { - // Before failing we check if he have an Identifier token - // And see it was defined as a struct. - const last = p.token_index - 1; + const slice = self.scratch.items[scratch..]; - if (p.tokens[last] == .Identifier) { - const name = p.source[p.tokens_start[last]..p.tokens_end[last]]; + return switch (slice.len) { + 0 => Span{ .zero_one = 0 }, + 1 => Span{ .zero_one = slice[0] }, + else => Span{ .multi = try self.listToSpan(slice) }, + }; +} - if (p.structs.get(name)) |val| { - components = val; - const start = p.token_index; - const arr = if (try p.parseArrayType()) |index| p.source[p.tokens_start[start]..p.tokens_end[index]] else ""; +pub fn parseReturnParams(self: *Parser) ParserErrors!Node.Range { + const scratch = self.scratch.items.len; + defer self.scratch.shrinkRetainingCapacity(scratch); - break :param_type try ParamType.typeToUnion(try std.fmt.allocPrint(p.alloc, "tuple{s}", .{arr}), p.alloc); - } + while (true) { + if (self.consumeToken(.ClosingParen)) |_| break; - return err; - } + const field = try self.expectVarDecl(); + try self.scratch.append(self.allocator, field); - return err; - } - }; + switch (self.token_tags[self.token_index]) { + .Comma => { + if (self.token_tags[self.token_index + 1] == .ClosingParen) + return error.ParsingError; + self.token_index += 1; + }, + .ClosingParen => { + self.token_index += 1; + break; + }, + else => return error.ParsingError, + } + } - const location = p.parseDataLocation(); - const indexed = indexed: { - if (location) |tok| { - _ = p.consumeToken(tok); - switch (tok) { - .Indexed => break :indexed true, - inline else => return error.InvalidDataLocation, - } - } else break :indexed false; - }; + const slice = self.scratch.items[scratch..]; - const name = p.parseIdentifier() orelse ""; - const param = .{ .type = abitype, .name = name, .indexed = indexed, .internalType = null, .components = components }; + if (slice.len == 0) + return error.ParsingError; - try param_list.append(param); + return self.listToSpan(slice); +} - switch (p.tokens[p.token_index]) { - .Comma => p.token_index += 1, - .ClosingParen => break, - .EndOfFileToken => break, - inline else => return error.ExpectedCommaAfterParam, +pub fn parseVariableDecls(self: *Parser) ParserErrors!Span { + const scratch = self.scratch.items.len; + defer self.scratch.shrinkRetainingCapacity(scratch); + + while (true) { + if (self.consumeToken(.ClosingParen)) |_| break; + + const field = try self.expectVarDecl(); + try self.scratch.append(self.allocator, field); + + switch (self.token_tags[self.token_index]) { + .Comma => { + if (self.token_tags[self.token_index + 1] == .ClosingParen) + return error.ParsingError; + self.token_index += 1; + }, + .ClosingParen => { + self.token_index += 1; + break; + }, + else => return error.ParsingError, } } - return try param_list.toOwnedSlice(); -} + const slice = self.scratch.items[scratch..]; -/// Parse solidity error params.\ -/// TypeExpr, DataLocation?, Identifier?, Comma? -pub fn parseErrorParamsDecl(p: *Parser) ParseErrors![]const AbiParameter { - var param_list = std.ArrayList(AbiParameter).init(p.alloc); + return switch (slice.len) { + 0 => Span{ .zero_one = 0 }, + 1 => Span{ .zero_one = slice[0] }, + else => Span{ .multi = try self.listToSpan(slice) }, + }; +} - while (true) { - const tuple_param = if (p.consumeToken(.OpenParen) != null) try p.parseTuple(AbiParameter) else null; +pub fn expectErrorVarDecl(self: *Parser) ParserErrors!Node.Index { + const index = try self.parseErrorVarDecl(); - if (tuple_param != null) { - try param_list.append(tuple_param.?); + if (index == 0) + return error.ParsingError; - switch (p.tokens[p.token_index]) { - .Comma => p.token_index += 1, - .ClosingParen => break, - .EndOfFileToken => break, - inline else => return error.ExpectedCommaAfterParam, - } + return index; +} - continue; - } +pub fn parseErrorVarDecl(self: *Parser) ParserErrors!Node.Index { + const sol_type = try self.parseType(); - var components: ?[]const AbiParameter = null; - const abitype = param_type: { - if (p.parseTypeExpr()) |result| break :param_type result else |err| { - const last = p.token_index - 1; + if (sol_type == 0) + return null_node; - if (p.tokens[last] == .Identifier) { - const name = p.source[p.tokens_start[last]..p.tokens_end[last]]; + const identifier = try self.expectToken(.Identifier); - if (p.structs.get(name)) |val| { - components = val; - const start = p.token_index; - const arr = if (try p.parseArrayType()) |index| p.source[p.tokens_start[start]..p.tokens_end[index]] else ""; + return self.addNode(.{ + .tag = .error_var_decl, + .main_token = identifier, + .data = .{ + .lhs = sol_type, + .rhs = undefined, + }, + }); +} - break :param_type try ParamType.typeToUnion(try std.fmt.allocPrint(p.alloc, "tuple{s}", .{arr}), p.alloc); - } +pub fn expectEventVarDecl(self: *Parser) ParserErrors!Node.Index { + const index = try self.parseEventVarDecl(); - return err; - } + if (index == 0) + return error.ParsingError; - return err; - } - }; + return index; +} - const location = p.parseDataLocation(); - if (location != null) return error.InvalidDataLocation; +pub fn parseEventVarDecl(self: *Parser) ParserErrors!Node.Index { + const sol_type = try self.parseType(); - const name = p.parseIdentifier() orelse ""; - const param: AbiParameter = .{ .type = abitype, .name = name, .internalType = null, .components = components }; + if (sol_type == 0) + return null_node; - try param_list.append(param); + const modifier = switch (self.token_tags[self.token_index]) { + .Indexed, + => self.nextToken(), + else => null_node, + }; - switch (p.tokens[p.token_index]) { - .Comma => p.token_index += 1, - .ClosingParen => break, - .EndOfFileToken => break, - inline else => return error.ExpectedCommaAfterParam, - } - } + const identifier = try self.expectToken(.Identifier); - return try param_list.toOwnedSlice(); + return self.addNode(.{ + .tag = .event_var_decl, + .main_token = modifier, + .data = .{ + .lhs = sol_type, + .rhs = identifier, + }, + }); } -/// Parse solidity struct params.\ -/// TypeExpr, Identifier?, SemiColon -pub fn parseStructParamDecls(p: *Parser) ParseErrors![]const AbiParameter { - var param_list = std.ArrayList(AbiParameter).init(p.alloc); +pub fn expectVarDecl(self: *Parser) ParserErrors!Node.Index { + const index = try self.parseVariableDecl(); - while (true) { - const tuple_param = if (p.consumeToken(.OpenParen) != null) try p.parseTuple(AbiParameter) else null; + if (index == 0) + return error.ParsingError; - if (tuple_param != null) { - try param_list.append(tuple_param.?); + return index; +} - _ = try p.expectToken(.SemiColon); +pub fn parseVariableDecl(self: *Parser) ParserErrors!Node.Index { + const sol_type = try self.parseType(); - switch (p.tokens[p.token_index]) { - .ClosingBrace => break, - .EndOfFileToken => return error.UnexceptedToken, - inline else => continue, - } - } + if (sol_type == 0) + return null_node; - var components: ?[]const AbiParameter = null; - const abitype = param_type: { - if (p.parseTypeExpr()) |result| break :param_type result else |err| { - const last = p.token_index - 1; + const modifier = switch (self.token_tags[self.token_index]) { + .Calldata, + .Storage, + .Memory, + => self.nextToken(), + else => null_node, + }; - if (p.tokens[last] == .Identifier) { - const name = p.source[p.tokens_start[last]..p.tokens_end[last]]; + const identifier = self.consumeToken(.Identifier) orelse null_node; - if (p.structs.get(name)) |val| { - components = val; - const start = p.token_index; - const arr = if (try p.parseArrayType()) |index| p.source[p.tokens_start[start]..p.tokens_end[index]] else ""; + return self.addNode(.{ + .tag = .var_decl, + .main_token = modifier, + .data = .{ + .lhs = sol_type, + .rhs = identifier, + }, + }); +} - break :param_type try ParamType.typeToUnion(try std.fmt.allocPrint(p.alloc, "tuple{s}", .{arr}), p.alloc); - } +pub fn parseStructDecl(self: *Parser) ParserErrors!Node.Index { + const struct_index = self.consumeToken(.Struct) orelse return null_node; - return err; - } + const reserve = try self.reserveNode(.struct_decl); + errdefer self.unreserveNode(reserve); - return err; - } - }; + const identifier = try self.expectToken(.Identifier); - const location = p.parseDataLocation(); - if (location != null) return error.InvalidDataLocation; + _ = try self.expectToken(.OpenBrace); + + const fields = try self.parseStructFields(); + + return switch (fields) { + .zero_one => |elem| self.setNode(reserve, .{ + .tag = .struct_decl_one, + .main_token = struct_index, + .data = .{ + .lhs = identifier, + .rhs = elem, + }, + }), + .multi => |elems| self.setNode(reserve, .{ + .tag = .struct_decl, + .main_token = struct_index, + .data = .{ + .lhs = identifier, + .rhs = try self.addExtraData(elems), + }, + }), + }; +} - const name = p.parseIdentifier() orelse ""; - const param: AbiParameter = .{ .type = abitype, .name = name, .internalType = null, .components = components }; +pub fn parseStructFields(self: *Parser) ParserErrors!Span { + const scratch = self.scratch.items.len; + defer self.scratch.shrinkRetainingCapacity(scratch); - try param_list.append(param); + while (true) { + if (self.consumeToken(.ClosingBrace)) |_| break; - _ = try p.expectToken(.SemiColon); + const field = try self.expectStructField(); + try self.scratch.append(self.allocator, field); - switch (p.tokens[p.token_index]) { - .ClosingBrace => break, - .EndOfFileToken => return error.UnexceptedToken, - inline else => continue, + switch (self.token_tags[self.token_index]) { + .ClosingBrace => { + self.token_index += 1; + break; + }, + else => {}, } } - return try param_list.toOwnedSlice(); + const slice = self.scratch.items[scratch..]; + + return switch (slice.len) { + 0 => Span{ .zero_one = 0 }, + 1 => Span{ .zero_one = slice[0] }, + else => Span{ .multi = try self.listToSpan(slice) }, + }; } -/// Parse solidity tuple params.\ -/// OpenParen, TypeExpr, Identifier?, Comma?, ClosingParen -pub fn parseTuple(p: *Parser, comptime T: type) ParseErrors!T { - const components = try p.parseErrorParamsDecl(); +pub fn expectStructField(self: *Parser) ParserErrors!Node.Index { + const field_type = try self.expectType(); + const identifier = try self.expectToken(.Identifier); - _ = try p.expectToken(.ClosingParen); - const start = p.token_index; - const end = try p.parseArrayType(); - const array_slice = if (end) |arr| p.source[p.tokens_start[start]..p.tokens_end[arr]] else null; + _ = try self.expectToken(.SemiColon); - const type_name = try std.fmt.allocPrint(p.alloc, "tuple{s}", .{array_slice orelse ""}); + return self.addNode(.{ + .tag = .struct_field, + .main_token = identifier, + .data = .{ + .lhs = field_type, + .rhs = undefined, + }, + }); +} - const abitype = try ParamType.typeToUnion(type_name, p.alloc); +pub fn expectType(self: *Parser) ParserErrors!Node.Index { + const index = try self.parseType(); - const location = p.parseDataLocation(); - const name = p.parseIdentifier() orelse ""; + if (index == 0) + return error.ParsingError; - return switch (T) { - AbiParameter => { - if (location != null) return error.InvalidDataLocation; - return .{ .type = abitype, .name = name, .internalType = null, .components = components }; - }, - AbiEventParameter => .{ .type = abitype, .name = name, .internalType = null, .indexed = location == .Indexed, .components = components }, - inline else => error.InvalidType, - }; + return index; } -fn parseTypeExpr(p: *Parser) (ParamErrors || error{UnexceptedToken})!ParamType { - const index = p.nextToken(); - const tok = p.tokens[index]; - - if (tok.lexToken()) |type_name| { - const slice = if (try p.parseArrayType()) |arr| p.source[p.tokens_start[index]..p.tokens_end[arr]] else type_name; +pub fn parseType(self: *Parser) Allocator.Error!Node.Index { + const sol_type = switch (self.token_tags[self.token_index]) { + .Identifier => try self.addNode(.{ + .tag = .identifier, + .main_token = self.nextToken(), + .data = .{ + .lhs = undefined, + .rhs = undefined, + }, + }), + else => self.consumeElementaryType(), + }; - return try ParamType.typeToUnion(slice, p.alloc); - } + return sol_type; +} - return error.UnexceptedToken; +pub fn consumeElementaryType(self: *Parser) Allocator.Error!Node.Index { + return switch (self.token_tags[self.token_index]) { + .Address, + .Bool, + .Tuple, + .String, + .Bytes, + .Bytes1, + .Bytes2, + .Bytes3, + .Bytes4, + .Bytes5, + .Bytes6, + .Bytes7, + .Bytes8, + .Bytes9, + .Bytes10, + .Bytes11, + .Bytes12, + .Bytes13, + .Bytes14, + .Bytes15, + .Bytes16, + .Bytes17, + .Bytes18, + .Bytes19, + .Bytes20, + .Bytes21, + .Bytes22, + .Bytes23, + .Bytes24, + .Bytes25, + .Bytes26, + .Bytes27, + .Bytes28, + .Bytes29, + .Bytes30, + .Bytes31, + .Bytes32, + .Uint, + .Uint8, + .Uint16, + .Uint24, + .Uint32, + .Uint40, + .Uint48, + .Uint56, + .Uint64, + .Uint72, + .Uint80, + .Uint88, + .Uint96, + .Uint104, + .Uint112, + .Uint120, + .Uint128, + .Uint136, + .Uint144, + .Uint152, + .Uint160, + .Uint168, + .Uint176, + .Uint184, + .Uint192, + .Uint200, + .Uint208, + .Uint216, + .Uint224, + .Uint232, + .Uint240, + .Uint248, + .Uint256, + .Int, + .Int8, + .Int16, + .Int24, + .Int32, + .Int40, + .Int48, + .Int56, + .Int64, + .Int72, + .Int80, + .Int88, + .Int96, + .Int104, + .Int112, + .Int120, + .Int128, + .Int136, + .Int144, + .Int152, + .Int160, + .Int168, + .Int176, + .Int184, + .Int192, + .Int200, + .Int208, + .Int216, + .Int224, + .Int232, + .Int240, + .Int248, + .Int256, + => self.addNode(.{ + .tag = .elementary_type, + .main_token = self.nextToken(), + .data = .{ + .lhs = undefined, + .rhs = undefined, + }, + }), + else => null_node, + }; } +// Internal parser actions -fn parseArrayType(p: *Parser) error{UnexceptedToken}!?u32 { - while (true) { - const token = p.nextToken(); +fn consumeToken(self: *Parser, expected: TokenTag) ?TokenIndex { + return if (self.token_tags[self.token_index] == expected) self.nextToken() else null; +} - switch (p.tokens[token]) { - .OpenBracket => continue, - .Number => { - _ = try p.expectToken(.ClosingBracket); - p.token_index -= 1; - }, - .ClosingBracket => switch (p.tokens[p.token_index]) { - .OpenBracket => continue, - else => return token, - }, - inline else => { - p.token_index -= 1; - return null; - }, - } - } +fn expectToken(self: *Parser, expected: TokenTag) error{ParsingError}!TokenIndex { + return if (self.token_tags[self.token_index] == expected) self.nextToken() else return error.ParsingError; } -fn parseDataLocation(p: *Parser) ?Tokens { - const tok = p.tokens[p.token_index]; +fn nextToken(self: *Parser) TokenIndex { + const index = self.token_index; - return switch (tok) { - .Indexed, .Calldata, .Storage, .Memory => tok, - inline else => null, - }; + self.token_index += 1; + + return index; } -fn parseVisibility(p: *Parser) error{UnexceptedToken}!void { - const external = p.consumeToken(.External) orelse 0; - const public = p.consumeToken(.Public) orelse 0; +// Node actions - if (external != 0 and public != 0) { - return error.UnexceptedToken; - } +/// Appends node to the list and returns the index. +fn addNode(self: *Parser, node: Node) Allocator.Error!Node.Index { + const index = @as(Node.Index, @intCast(self.nodes.len)); + try self.nodes.append(self.allocator, node); + + return index; } +/// Sets a node based on the provided index. +fn setNode(self: *Parser, index: usize, child: Node) Node.Index { + self.nodes.set(index, child); -fn parseIdentifier(p: *Parser) ?[]const u8 { - return if (p.consumeToken(.Identifier)) |ident| p.source[p.tokens_start[ident]..p.tokens_end[ident]] else null; + return @as(Node.Index, @intCast(index)); +} +/// Reserves a node index on the arraylist. +fn reserveNode(self: *Parser, tag: Ast.Node.Tag) Allocator.Error!usize { + try self.nodes.resize(self.allocator, self.nodes.len + 1); + self.nodes.items(.tag)[self.nodes.len - 1] = tag; + return self.nodes.len - 1; +} +/// Unreserves the node and sets a empty node into it if the element is not in the end. +fn unreserveNode(self: *Parser, index: usize) void { + if (self.nodes.len == index) { + self.nodes.resize(self.allocator, self.nodes.len - 1) catch unreachable; + } else { + self.nodes.items(.tag)[index] = .unreachable_node; + self.nodes.items(.main_token)[index] = self.token_index; + } } -fn expectToken(p: *Parser, expected: Tokens) error{UnexceptedToken}!u32 { - if (p.tokens[p.token_index] != expected) return error.UnexceptedToken; +fn addExtraData(self: *Parser, extra: anytype) Allocator.Error!Node.Index { + const fields = std.meta.fields(@TypeOf(extra)); - return p.nextToken(); -} + try self.extra.ensureUnusedCapacity(self.allocator, fields.len); + const result: u32 = @intCast(self.extra.items.len); -fn consumeToken(p: *Parser, tok: Tokens) ?u32 { - return if (p.tokens[p.token_index] == tok) p.nextToken() else null; + inline for (fields) |field| { + std.debug.assert(field.type == Node.Index); + self.extra.appendAssumeCapacity(@field(extra, field.name)); + } + + return result; } -fn nextToken(p: *Parser) u32 { - const index = p.token_index; - p.token_index += 1; +fn listToSpan(self: *Parser, slice: []const Node.Index) Allocator.Error!Node.Range { + try self.extra.appendSlice(self.allocator, slice); - return index; + return Node.Range{ + .start = @as(Node.Index, @intCast(self.extra.items.len - slice.len)), + .end = @as(Node.Index, @intCast(self.extra.items.len)), + }; } diff --git a/src/human-readable/ParserNew.zig b/src/human-readable/ParserNew.zig deleted file mode 100644 index 822696bc..00000000 --- a/src/human-readable/ParserNew.zig +++ /dev/null @@ -1,903 +0,0 @@ -const std = @import("std"); - -const Allocator = std.mem.Allocator; -const Ast = @import("Ast.zig"); -const Node = Ast.Node; -const TokenIndex = Ast.TokenIndex; -const TokenTag = @import("tokens.zig").Tag.SoliditySyntax; - -const Parser = @This(); - -/// Errors that can happing whilest parsing the source code. -pub const ParserErrors = error{ParsingError} || Allocator.Error; - -const null_node: Node.Index = 0; - -const Span = union(enum) { - zero_one: Node.Index, - multi: Node.Range, -}; - -/// Allocator used by the parser. -allocator: Allocator, -/// Source to parse. -source: [:0]const u8, -/// Current parser index -token_index: TokenIndex, -/// Slices of all tokenized token tags. -token_tags: []const TokenTag, -/// Struct of arrays for the node. -nodes: Ast.NodeList, -/// Array list that contains extra data. -extra: std.ArrayListUnmanaged(Node.Index), -/// Temporary space used in parsing. -scratch: std.ArrayListUnmanaged(Node.Index), - -/// Clears any allocated memory. -pub fn deinit(self: *Parser) void { - self.nodes.deinit(self.allocator); - self.extra.deinit(self.allocator); - self.scratch.deinit(self.allocator); -} - -/// Parses all of the source and build the `Ast`. -pub fn parseSource(self: *Parser) ParserErrors!void { - try self.nodes.append(self.allocator, .{ - .tag = .root, - .main_token = 0, - .data = undefined, - }); - - const members = try self.parseUnits(); - - if (self.token_tags[self.token_index] != .EndOfFileToken) - return error.ParsingError; - - self.nodes.items(.data)[0] = .{ - .lhs = members.start, - .rhs = members.end, - }; -} - -pub fn parseUnits(self: *Parser) ParserErrors!Node.Range { - const scratch = self.scratch.items.len; - defer self.scratch.shrinkRetainingCapacity(scratch); - - while (true) { - switch (self.token_tags[self.token_index]) { - .EndOfFileToken => break, - else => {}, - } - - try self.scratch.append(self.allocator, try self.expectUnit()); - } - - const slice = self.scratch.items[scratch..]; - - return self.listToSpan(slice); -} - -pub fn expectUnit(self: *Parser) ParserErrors!Node.Index { - const unit = try self.parseUnit(); - - if (unit == 0) - return error.ParsingError; - - return unit; -} - -pub fn parseUnit(self: *Parser) ParserErrors!Node.Index { - return switch (self.token_tags[self.token_index]) { - .Function => self.parseFunctionProto(), - .Fallback => self.parseFallbackProto(), - .Receive => self.parseReceiveProto(), - .Constructor => self.parseConstructorProto(), - .Event => self.parseEventProto(), - .Error => self.parseErrorProto(), - .Struct => self.parseStructDecl(), - else => return null_node, - }; -} - -pub fn parseFunctionProto(self: *Parser) ParserErrors!Node.Index { - const keyword = self.consumeToken(.Function) orelse return null_node; - - const reserve = try self.reserveNode(.function_proto); - errdefer self.unreserveNode(reserve); - - const identifier = switch (self.token_tags[self.token_index]) { - .Identifier, - .Fallback, - .Receive, - => self.nextToken(), - else => return null_node, - }; - - _ = try self.expectToken(.OpenParen); - - const params = try self.parseVariableDecls(); - const specifiers = try self.parseSpecifiers(); - - if (self.consumeToken(.Returns)) |_| { - _ = try self.expectToken(.OpenParen); - const returns = try self.parseReturnParams(); - - return switch (params) { - .zero_one => |elem| return self.setNode(reserve, .{ - .tag = .function_proto_one, - .main_token = keyword, - .data = .{ - .lhs = try self.addExtraData(Node.FunctionProtoOne{ - .specifiers = specifiers, - .identifier = identifier, - .param = elem, - }), - .rhs = try self.addExtraData(returns), - }, - }), - .multi => |elem| return self.setNode(reserve, .{ - .tag = .function_proto, - .main_token = keyword, - .data = .{ - .lhs = try self.addExtraData(Node.FunctionProto{ - .specifiers = specifiers, - .identifier = identifier, - .params_start = elem.start, - .params_end = elem.end, - }), - .rhs = try self.addExtraData(returns), - }, - }), - }; - } - - return switch (params) { - .zero_one => |elem| return self.setNode(reserve, .{ - .tag = .function_proto_simple, - .main_token = keyword, - .data = .{ - .lhs = try self.addExtraData(Node.FunctionProtoSimple{ - .identifier = identifier, - .param = elem, - }), - .rhs = specifiers, - }, - }), - .multi => |elem| return self.setNode(reserve, .{ - .tag = .function_proto_multi, - .main_token = keyword, - .data = .{ - .lhs = try self.addExtraData(Node.FunctionProtoMulti{ - .identifier = identifier, - .params_start = elem.start, - .params_end = elem.end, - }), - .rhs = specifiers, - }, - }), - }; -} - -pub fn parseReceiveProto(self: *Parser) ParserErrors!Node.Index { - const receive_keyword = self.consumeToken(.Receive) orelse return null_node; - - _ = try self.expectToken(.OpenParen); - _ = try self.expectToken(.ClosingParen); - - const specifiers = try self.parseSpecifiers(); - - return self.addNode(.{ - .tag = .receive_proto, - .main_token = receive_keyword, - .data = .{ - .lhs = undefined, - .rhs = specifiers, - }, - }); -} - -pub fn parseFallbackProto(self: *Parser) ParserErrors!Node.Index { - const fallback_keyword = self.consumeToken(.Fallback) orelse return null_node; - - const reserve = try self.reserveNode(.fallback_proto_multi); - errdefer self.unreserveNode(reserve); - - _ = try self.expectToken(.OpenParen); - - const params = try self.parseVariableDecls(); - - const specifiers = try self.parseSpecifiers(); - - return switch (params) { - .zero_one => |elem| self.setNode(reserve, .{ - .tag = .fallback_proto_simple, - .main_token = fallback_keyword, - .data = .{ - .lhs = elem, - .rhs = specifiers, - }, - }), - .multi => |elems| self.setNode(reserve, .{ - .tag = .fallback_proto_multi, - .main_token = fallback_keyword, - .data = .{ - .lhs = try self.addExtraData(elems), - .rhs = specifiers, - }, - }), - }; -} - -pub fn parseConstructorProto(self: *Parser) ParserErrors!Node.Index { - const constructor_keyword = self.consumeToken(.Constructor) orelse return null_node; - - const reserve = try self.reserveNode(.constructor_proto_multi); - errdefer self.unreserveNode(reserve); - - _ = try self.expectToken(.OpenParen); - - const params = try self.parseVariableDecls(); - - const specifiers = try self.parseSpecifiers(); - - return switch (params) { - .zero_one => |elem| self.setNode(reserve, .{ - .tag = .constructor_proto_simple, - .main_token = constructor_keyword, - .data = .{ - .lhs = elem, - .rhs = specifiers, - }, - }), - .multi => |elems| self.setNode(reserve, .{ - .tag = .constructor_proto_multi, - .main_token = constructor_keyword, - .data = .{ - .lhs = try self.addExtraData(elems), - .rhs = specifiers, - }, - }), - }; -} - -pub fn parseSpecifiers(self: *Parser) ParserErrors!Node.Index { - const scratch = self.scratch.items.len; - defer self.scratch.shrinkRetainingCapacity(scratch); - - var specifier: enum { - seen_visibility, - seen_mutability, - seen_both, - none, - } = .none; - - while (true) { - switch (self.token_tags[self.token_index]) { - .Public, - .External, - .Internal, - .Private, - => { - switch (specifier) { - .seen_visibility, - .seen_both, - => return error.ParsingError, - .seen_mutability => specifier = .seen_both, - .none => specifier = .seen_visibility, - } - - try self.scratch.append(self.allocator, self.nextToken()); - }, - .Pure, - .Payable, - .View, - => { - switch (specifier) { - .seen_mutability, - .seen_both, - => return error.ParsingError, - .seen_visibility => specifier = .seen_both, - .none => specifier = .seen_mutability, - } - - try self.scratch.append(self.allocator, self.nextToken()); - }, - .Virtual, - .Override, - => try self.scratch.append(self.allocator, self.nextToken()), - else => break, - } - } - - const slice = self.scratch.items[scratch..]; - - return self.addNode(.{ - .tag = .specifiers, - .main_token = try self.addExtraData(try self.listToSpan(slice)), - .data = .{ - .lhs = undefined, - .rhs = undefined, - }, - }); -} - -pub fn parseErrorProto(self: *Parser) ParserErrors!Node.Index { - const error_keyword = self.consumeToken(.Error) orelse return null_node; - - const reserve = try self.reserveNode(.error_proto_multi); - errdefer self.unreserveNode(reserve); - - const identifier = try self.expectToken(.Identifier); - - _ = try self.expectToken(.OpenParen); - - const params = try self.parseErrorVarDecls(); - - return switch (params) { - .zero_one => |elem| self.setNode(reserve, .{ - .tag = .error_proto_simple, - .main_token = error_keyword, - .data = .{ - .lhs = identifier, - .rhs = elem, - }, - }), - .multi => |elems| self.setNode(reserve, .{ - .tag = .error_proto_multi, - .main_token = error_keyword, - .data = .{ - .lhs = identifier, - .rhs = try self.addExtraData(elems), - }, - }), - }; -} - -pub fn parseEventProto(self: *Parser) ParserErrors!Node.Index { - const event_keyword = self.consumeToken(.Event) orelse return null_node; - - const reserve = try self.reserveNode(.event_proto_multi); - errdefer self.unreserveNode(reserve); - - const identifier = try self.expectToken(.Identifier); - - _ = try self.expectToken(.OpenParen); - - const params = try self.parseEventVarDecls(); - - _ = self.consumeToken(.Anonymous); - - return switch (params) { - .zero_one => |elem| self.setNode(reserve, .{ - .tag = .event_proto_simple, - .main_token = event_keyword, - .data = .{ - .lhs = identifier, - .rhs = elem, - }, - }), - .multi => |elems| self.setNode(reserve, .{ - .tag = .event_proto_multi, - .main_token = event_keyword, - .data = .{ - .lhs = identifier, - .rhs = try self.addExtraData(elems), - }, - }), - }; -} - -pub fn parseEventVarDecls(self: *Parser) ParserErrors!Span { - const scratch = self.scratch.items.len; - defer self.scratch.shrinkRetainingCapacity(scratch); - - while (true) { - if (self.consumeToken(.ClosingParen)) |_| break; - - const field = try self.expectEventVarDecl(); - try self.scratch.append(self.allocator, field); - - switch (self.token_tags[self.token_index]) { - .Comma => { - if (self.token_tags[self.token_index + 1] == .ClosingParen) - return error.ParsingError; - self.token_index += 1; - }, - .ClosingParen => { - self.token_index += 1; - break; - }, - else => return error.ParsingError, - } - } - - const slice = self.scratch.items[scratch..]; - - return switch (slice.len) { - 0 => Span{ .zero_one = 0 }, - 1 => Span{ .zero_one = slice[0] }, - else => Span{ .multi = try self.listToSpan(slice) }, - }; -} - -pub fn parseErrorVarDecls(self: *Parser) ParserErrors!Span { - const scratch = self.scratch.items.len; - defer self.scratch.shrinkRetainingCapacity(scratch); - - while (true) { - if (self.consumeToken(.ClosingParen)) |_| break; - - const field = try self.expectErrorVarDecl(); - try self.scratch.append(self.allocator, field); - - switch (self.token_tags[self.token_index]) { - .Comma => { - if (self.token_tags[self.token_index + 1] == .ClosingParen) - return error.ParsingError; - self.token_index += 1; - }, - .ClosingParen => { - self.token_index += 1; - break; - }, - else => return error.ParsingError, - } - } - - const slice = self.scratch.items[scratch..]; - - return switch (slice.len) { - 0 => Span{ .zero_one = 0 }, - 1 => Span{ .zero_one = slice[0] }, - else => Span{ .multi = try self.listToSpan(slice) }, - }; -} - -pub fn parseReturnParams(self: *Parser) ParserErrors!Node.Range { - const scratch = self.scratch.items.len; - defer self.scratch.shrinkRetainingCapacity(scratch); - - while (true) { - if (self.consumeToken(.ClosingParen)) |_| break; - - const field = try self.expectVarDecl(); - try self.scratch.append(self.allocator, field); - - switch (self.token_tags[self.token_index]) { - .Comma => { - if (self.token_tags[self.token_index + 1] == .ClosingParen) - return error.ParsingError; - self.token_index += 1; - }, - .ClosingParen => { - self.token_index += 1; - break; - }, - else => return error.ParsingError, - } - } - - const slice = self.scratch.items[scratch..]; - - if (slice.len == 0) - return error.ParsingError; - - return self.listToSpan(slice); -} - -pub fn parseVariableDecls(self: *Parser) ParserErrors!Span { - const scratch = self.scratch.items.len; - defer self.scratch.shrinkRetainingCapacity(scratch); - - while (true) { - if (self.consumeToken(.ClosingParen)) |_| break; - - const field = try self.expectVarDecl(); - try self.scratch.append(self.allocator, field); - - switch (self.token_tags[self.token_index]) { - .Comma => { - if (self.token_tags[self.token_index + 1] == .ClosingParen) - return error.ParsingError; - self.token_index += 1; - }, - .ClosingParen => { - self.token_index += 1; - break; - }, - else => return error.ParsingError, - } - } - - const slice = self.scratch.items[scratch..]; - - return switch (slice.len) { - 0 => Span{ .zero_one = 0 }, - 1 => Span{ .zero_one = slice[0] }, - else => Span{ .multi = try self.listToSpan(slice) }, - }; -} - -pub fn expectErrorVarDecl(self: *Parser) ParserErrors!Node.Index { - const index = try self.parseErrorVarDecl(); - - if (index == 0) - return error.ParsingError; - - return index; -} - -pub fn parseErrorVarDecl(self: *Parser) ParserErrors!Node.Index { - const sol_type = try self.parseType(); - - if (sol_type == 0) - return null_node; - - const identifier = try self.expectToken(.Identifier); - - return self.addNode(.{ - .tag = .error_var_decl, - .main_token = identifier, - .data = .{ - .lhs = sol_type, - .rhs = undefined, - }, - }); -} - -pub fn expectEventVarDecl(self: *Parser) ParserErrors!Node.Index { - const index = try self.parseEventVarDecl(); - - if (index == 0) - return error.ParsingError; - - return index; -} - -pub fn parseEventVarDecl(self: *Parser) ParserErrors!Node.Index { - const sol_type = try self.parseType(); - - if (sol_type == 0) - return null_node; - - const modifier = switch (self.token_tags[self.token_index]) { - .Indexed, - => self.nextToken(), - else => null_node, - }; - - const identifier = try self.expectToken(.Identifier); - - return self.addNode(.{ - .tag = .event_var_decl, - .main_token = modifier, - .data = .{ - .lhs = sol_type, - .rhs = identifier, - }, - }); -} - -pub fn expectVarDecl(self: *Parser) ParserErrors!Node.Index { - const index = try self.parseVariableDecl(); - - if (index == 0) - return error.ParsingError; - - return index; -} - -pub fn parseVariableDecl(self: *Parser) ParserErrors!Node.Index { - const sol_type = try self.parseType(); - - if (sol_type == 0) - return null_node; - - const modifier = switch (self.token_tags[self.token_index]) { - .Calldata, - .Storage, - .Memory, - => self.nextToken(), - else => null_node, - }; - - const identifier = self.consumeToken(.Identifier) orelse null_node; - - return self.addNode(.{ - .tag = .var_decl, - .main_token = modifier, - .data = .{ - .lhs = sol_type, - .rhs = identifier, - }, - }); -} - -pub fn parseStructDecl(self: *Parser) ParserErrors!Node.Index { - const struct_index = self.consumeToken(.Struct) orelse return null_node; - - const reserve = try self.reserveNode(.struct_decl); - errdefer self.unreserveNode(reserve); - - const identifier = try self.expectToken(.Identifier); - - _ = try self.expectToken(.OpenBrace); - - const fields = try self.parseStructFields(); - - return switch (fields) { - .zero_one => |elem| self.setNode(reserve, .{ - .tag = .struct_decl_one, - .main_token = struct_index, - .data = .{ - .lhs = identifier, - .rhs = elem, - }, - }), - .multi => |elems| self.setNode(reserve, .{ - .tag = .struct_decl, - .main_token = struct_index, - .data = .{ - .lhs = identifier, - .rhs = try self.addExtraData(elems), - }, - }), - }; -} - -pub fn parseStructFields(self: *Parser) ParserErrors!Span { - const scratch = self.scratch.items.len; - defer self.scratch.shrinkRetainingCapacity(scratch); - - while (true) { - if (self.consumeToken(.ClosingBrace)) |_| break; - - const field = try self.expectStructField(); - try self.scratch.append(self.allocator, field); - - switch (self.token_tags[self.token_index]) { - .ClosingBrace => { - self.token_index += 1; - break; - }, - else => {}, - } - } - - const slice = self.scratch.items[scratch..]; - - return switch (slice.len) { - 0 => Span{ .zero_one = 0 }, - 1 => Span{ .zero_one = slice[0] }, - else => Span{ .multi = try self.listToSpan(slice) }, - }; -} - -pub fn expectStructField(self: *Parser) ParserErrors!Node.Index { - const field_type = try self.expectType(); - const identifier = try self.expectToken(.Identifier); - - _ = try self.expectToken(.SemiColon); - - return self.addNode(.{ - .tag = .struct_field, - .main_token = identifier, - .data = .{ - .lhs = field_type, - .rhs = undefined, - }, - }); -} - -pub fn expectType(self: *Parser) ParserErrors!Node.Index { - const index = try self.parseType(); - - if (index == 0) - return error.ParsingError; - - return index; -} - -pub fn parseType(self: *Parser) Allocator.Error!Node.Index { - const sol_type = switch (self.token_tags[self.token_index]) { - .Identifier => try self.addNode(.{ - .tag = .identifier, - .main_token = self.nextToken(), - .data = .{ - .lhs = undefined, - .rhs = undefined, - }, - }), - else => self.consumeElementaryType(), - }; - - return sol_type; -} - -pub fn consumeElementaryType(self: *Parser) Allocator.Error!Node.Index { - return switch (self.token_tags[self.token_index]) { - .Address, - .Bool, - .Tuple, - .String, - .Bytes, - .Bytes1, - .Bytes2, - .Bytes3, - .Bytes4, - .Bytes5, - .Bytes6, - .Bytes7, - .Bytes8, - .Bytes9, - .Bytes10, - .Bytes11, - .Bytes12, - .Bytes13, - .Bytes14, - .Bytes15, - .Bytes16, - .Bytes17, - .Bytes18, - .Bytes19, - .Bytes20, - .Bytes21, - .Bytes22, - .Bytes23, - .Bytes24, - .Bytes25, - .Bytes26, - .Bytes27, - .Bytes28, - .Bytes29, - .Bytes30, - .Bytes31, - .Bytes32, - .Uint, - .Uint8, - .Uint16, - .Uint24, - .Uint32, - .Uint40, - .Uint48, - .Uint56, - .Uint64, - .Uint72, - .Uint80, - .Uint88, - .Uint96, - .Uint104, - .Uint112, - .Uint120, - .Uint128, - .Uint136, - .Uint144, - .Uint152, - .Uint160, - .Uint168, - .Uint176, - .Uint184, - .Uint192, - .Uint200, - .Uint208, - .Uint216, - .Uint224, - .Uint232, - .Uint240, - .Uint248, - .Uint256, - .Int, - .Int8, - .Int16, - .Int24, - .Int32, - .Int40, - .Int48, - .Int56, - .Int64, - .Int72, - .Int80, - .Int88, - .Int96, - .Int104, - .Int112, - .Int120, - .Int128, - .Int136, - .Int144, - .Int152, - .Int160, - .Int168, - .Int176, - .Int184, - .Int192, - .Int200, - .Int208, - .Int216, - .Int224, - .Int232, - .Int240, - .Int248, - .Int256, - => self.addNode(.{ - .tag = .elementary_type, - .main_token = self.nextToken(), - .data = .{ - .lhs = undefined, - .rhs = undefined, - }, - }), - else => null_node, - }; -} -// Internal parser actions - -fn consumeToken(self: *Parser, expected: TokenTag) ?TokenIndex { - return if (self.token_tags[self.token_index] == expected) self.nextToken() else null; -} - -fn expectToken(self: *Parser, expected: TokenTag) error{ParsingError}!TokenIndex { - return if (self.token_tags[self.token_index] == expected) self.nextToken() else return error.ParsingError; -} - -fn nextToken(self: *Parser) TokenIndex { - const index = self.token_index; - - self.token_index += 1; - - return index; -} - -// Node actions - -/// Appends node to the list and returns the index. -fn addNode(self: *Parser, node: Node) Allocator.Error!Node.Index { - const index = @as(Node.Index, @intCast(self.nodes.len)); - try self.nodes.append(self.allocator, node); - - return index; -} -/// Sets a node based on the provided index. -fn setNode(self: *Parser, index: usize, child: Node) Node.Index { - self.nodes.set(index, child); - - return @as(Node.Index, @intCast(index)); -} -/// Reserves a node index on the arraylist. -fn reserveNode(self: *Parser, tag: Ast.Node.Tag) Allocator.Error!usize { - try self.nodes.resize(self.allocator, self.nodes.len + 1); - self.nodes.items(.tag)[self.nodes.len - 1] = tag; - return self.nodes.len - 1; -} -/// Unreserves the node and sets a empty node into it if the element is not in the end. -fn unreserveNode(self: *Parser, index: usize) void { - if (self.nodes.len == index) { - self.nodes.resize(self.allocator, self.nodes.len - 1) catch unreachable; - } else { - self.nodes.items(.tag)[index] = .unreachable_node; - self.nodes.items(.main_token)[index] = self.token_index; - } -} - -fn addExtraData(self: *Parser, extra: anytype) Allocator.Error!Node.Index { - const fields = std.meta.fields(@TypeOf(extra)); - - try self.extra.ensureUnusedCapacity(self.allocator, fields.len); - const result: u32 = @intCast(self.extra.items.len); - - inline for (fields) |field| { - std.debug.assert(field.type == Node.Index); - self.extra.appendAssumeCapacity(@field(extra, field.name)); - } - - return result; -} - -fn listToSpan(self: *Parser, slice: []const Node.Index) Allocator.Error!Node.Range { - try self.extra.appendSlice(self.allocator, slice); - - return Node.Range{ - .start = @as(Node.Index, @intCast(self.extra.items.len - slice.len)), - .end = @as(Node.Index, @intCast(self.extra.items.len)), - }; -} diff --git a/src/human-readable/ParserOld.zig b/src/human-readable/ParserOld.zig new file mode 100644 index 00000000..e66a3ec9 --- /dev/null +++ b/src/human-readable/ParserOld.zig @@ -0,0 +1,632 @@ +const std = @import("std"); +const testing = std.testing; +const abi = @import("../abi/abi.zig"); + +// Types +const Abi = abi.Abi; +const AbiItem = abi.AbiItem; +const AbiParameter = @import("../abi/abi_parameter.zig").AbiParameter; +const AbiEventParameter = @import("../abi/abi_parameter.zig").AbiEventParameter; +const Allocator = std.mem.Allocator; +const Constructor = abi.Constructor; +const Error = abi.Error; +const Event = abi.Event; +const Fallback = abi.Fallback; +const Function = abi.Function; +const Receive = abi.Receive; +const Lexer = @import("lexer.zig").Lexer; +const StateMutability = @import("../abi/state_mutability.zig").StateMutability; +const ParamErrors = @import("../abi/param_type.zig").ParamErrors; +const ParamType = @import("../abi/param_type.zig").ParamType; +const Tokens = @import("tokens.zig").Tag.SoliditySyntax; + +pub const TokenList = std.MultiArrayList(struct { + token_type: Tokens, + start: u32, + end: u32, +}); + +/// Set of possible errors that can happen while parsing. +pub const ParseErrors = error{ + InvalidDataLocation, + UnexceptedToken, + InvalidType, + ExpectedCommaAfterParam, + EmptyReturnParams, +} || ParamErrors || Allocator.Error; + +const Parser = @This(); + +/// The allocator used in parsing. +alloc: Allocator, +/// Slice of tokens produced by the lexer. +tokens: []const Tokens, +/// Slice of start positions of the source of the token. +tokens_start: []const u32, +/// Slice of end positions of the source of the token. +tokens_end: []const u32, +/// The current token index. +token_index: u32, +/// The slice that cotains the token source. +source: []const u8, +/// Hashmap of the preparsed structs. +structs: std.StringHashMapUnmanaged([]const AbiParameter), + +/// Parse a string or a multi line string with solidity signatures.\ +/// This will return all signatures as a slice of `AbiItem`. +/// +/// This supports parsing struct signatures if its intended to use +/// The struct signatures must be defined top down. +/// +/// **Example** +/// ```zig +/// var lex = Lexer.init(source); +/// +/// var list = Parser.TokenList{}; +/// defer list.deinit(allocator); +/// +/// while (true) { +/// const tok = lex.scan(); +/// +/// try list.append(allocator, .{ +/// .token_type = tok.syntax, +/// .start = tok.location.start, +/// .end = tok.location.end, +/// }); +/// +/// if (tok.syntax == .EndOfFileToken) break; +/// } +/// +/// var parser: Parser = .{ +/// .alloc = allocator, +/// .tokens = list.items(.token_type), +/// .tokens_start = list.items(.start), +/// .tokens_end = list.items(.end), +/// .token_index = 0, +/// .source = source, +/// .structs = .{}, +/// }; +/// +/// const abi = try parser.parseAbiProto(); +/// ``` +pub fn parseAbiProto(p: *Parser) ParseErrors!Abi { + var abi_list = std.ArrayList(AbiItem).init(p.alloc); + + while (true) { + if (p.tokens[p.token_index] == .Struct) { + try p.parseStructProto(); + continue; + } + + try abi_list.append(try p.parseAbiItemProto()); + + if (p.tokens[p.token_index] == .EndOfFileToken) break; + } + + return abi_list.toOwnedSlice(); +} + +/// Parse a single solidity signature based on expected tokens. +/// +/// Will return an error if the token is not expected. +pub fn parseAbiItemProto(p: *Parser) ParseErrors!AbiItem { + return switch (p.tokens[p.token_index]) { + .Function => .{ .abiFunction = try p.parseFunctionFnProto() }, + .Event => .{ .abiEvent = try p.parseEventFnProto() }, + .Error => .{ .abiError = try p.parseErrorFnProto() }, + .Constructor => .{ .abiConstructor = try p.parseConstructorFnProto() }, + .Fallback => .{ .abiFallback = try p.parseFallbackFnProto() }, + .Receive => .{ .abiReceive = try p.parseReceiveFnProto() }, + inline else => error.UnexceptedToken, + }; +} + +/// Parse single solidity function signature.\ +/// FunctionProto -> Function KEYWORD, Identifier, OpenParen, ParamDecls?, ClosingParen, Visibility?, StateMutability?, Returns? +pub fn parseFunctionFnProto(p: *Parser) ParseErrors!Function { + _ = try p.expectToken(.Function); + const name = p.parseIdentifier().?; + + _ = try p.expectToken(.OpenParen); + + const inputs: []const AbiParameter = if (p.tokens[p.token_index] == .ClosingParen) &.{} else try p.parseFuncParamsDecl(); + + _ = try p.expectToken(.ClosingParen); + + try p.parseVisibility(); + + const state: StateMutability = switch (p.tokens[p.token_index]) { + .Payable => .payable, + .View => .view, + .Pure => .pure, + inline else => .nonpayable, + }; + + if (state != .nonpayable) _ = p.nextToken(); + + if (p.consumeToken(.Returns)) |_| { + _ = try p.expectToken(.OpenParen); + + const outputs: []const AbiParameter = if (p.tokens[p.token_index] == .ClosingParen) return error.EmptyReturnParams else try p.parseFuncParamsDecl(); + + _ = try p.expectToken(.ClosingParen); + + return .{ .type = .function, .name = name, .inputs = inputs, .outputs = outputs, .stateMutability = state }; + } + + return .{ .type = .function, .name = name, .inputs = inputs, .outputs = &.{}, .stateMutability = state }; +} + +/// Parse single solidity event signature.\ +/// EventProto -> Event KEYWORD, Identifier, OpenParen, ParamDecls?, ClosingParen +pub fn parseEventFnProto(p: *Parser) ParseErrors!Event { + _ = try p.expectToken(.Event); + const name = p.parseIdentifier().?; + + _ = try p.expectToken(.OpenParen); + + const inputs: []const AbiEventParameter = if (p.tokens[p.token_index] == .ClosingParen) &.{} else try p.parseEventParamsDecl(); + + _ = try p.expectToken(.ClosingParen); + + return .{ .type = .event, .inputs = inputs, .name = name }; +} + +/// Parse single solidity error signature.\ +/// ErrorProto -> Error KEYWORD, Identifier, OpenParen, ParamDecls?, ClosingParen +pub fn parseErrorFnProto(p: *Parser) ParseErrors!Error { + _ = try p.expectToken(.Error); + const name = p.parseIdentifier().?; + + _ = try p.expectToken(.OpenParen); + + const inputs: []const AbiParameter = if (p.tokens[p.token_index] == .ClosingParen) &.{} else try p.parseErrorParamsDecl(); + + _ = try p.expectToken(.ClosingParen); + + return .{ .type = .@"error", .inputs = inputs, .name = name }; +} + +/// Parse single solidity constructor signature.\ +/// ConstructorProto -> Constructor KEYWORD, OpenParen, ParamDecls?, ClosingParen, StateMutability? +pub fn parseConstructorFnProto(p: *Parser) ParseErrors!Constructor { + _ = try p.expectToken(.Constructor); + + _ = try p.expectToken(.OpenParen); + + const inputs: []const AbiParameter = if (p.tokens[p.token_index] == .ClosingParen) &.{} else try p.parseFuncParamsDecl(); + + _ = try p.expectToken(.ClosingParen); + + return switch (p.tokens[p.token_index]) { + .Payable => .{ .type = .constructor, .stateMutability = .payable, .inputs = inputs }, + inline else => .{ .type = .constructor, .stateMutability = .nonpayable, .inputs = inputs }, + }; +} + +/// Parse single solidity struct signature.\ +/// StructProto -> Struct KEYWORD, Identifier, OpenBrace, ParamDecls, ClosingBrace +pub fn parseStructProto(p: *Parser) ParseErrors!void { + _ = try p.expectToken(.Struct); + + const name = p.parseIdentifier().?; + + _ = try p.expectToken(.OpenBrace); + + const params = try p.parseStructParamDecls(); + + _ = try p.expectToken(.ClosingBrace); + + try p.structs.put(p.alloc, name, params); +} + +/// Parse single solidity fallback signature.\ +/// FallbackProto -> Fallback KEYWORD, OpenParen, ClosingParen, StateMutability? +pub fn parseFallbackFnProto(p: *Parser) error{UnexceptedToken}!Fallback { + _ = try p.expectToken(.Fallback); + _ = try p.expectToken(.OpenParen); + _ = try p.expectToken(.ClosingParen); + + switch (p.tokens[p.token_index]) { + .Payable => { + if (p.tokens[p.token_index + 1] != .EndOfFileToken) return error.UnexceptedToken; + + return .{ .type = .fallback, .stateMutability = .payable }; + }, + .EndOfFileToken => return .{ .type = .fallback, .stateMutability = .nonpayable }, + inline else => return error.UnexceptedToken, + } +} + +/// Parse single solidity receive signature.\ +/// ReceiveProto -> Receive KEYWORD, OpenParen, ClosingParen, External, Payable +pub fn parseReceiveFnProto(p: *Parser) error{UnexceptedToken}!Receive { + _ = try p.expectToken(.Receive); + _ = try p.expectToken(.OpenParen); + _ = try p.expectToken(.ClosingParen); + _ = try p.expectToken(.External); + _ = try p.expectToken(.Payable); + + return .{ .type = .receive, .stateMutability = .payable }; +} + +/// Parse solidity function params.\ +/// TypeExpr, DataLocation?, Identifier?, Comma? +pub fn parseFuncParamsDecl(p: *Parser) ParseErrors![]const AbiParameter { + var param_list = std.ArrayList(AbiParameter).init(p.alloc); + + while (true) { + const tuple_param = if (p.consumeToken(.OpenParen) != null) try p.parseTuple(AbiParameter) else null; + + if (tuple_param != null) { + try param_list.append(tuple_param.?); + + switch (p.tokens[p.token_index]) { + .Comma => p.token_index += 1, + .ClosingParen => break, + .EndOfFileToken => break, + inline else => return error.ExpectedCommaAfterParam, + } + + continue; + } + + var components: ?[]const AbiParameter = null; + const abitype = param_type: { + if (p.parseTypeExpr()) |result| break :param_type result else |err| { + // Before failing we check if he have an Identifier token + // And see it was defined as a struct. + const last = p.token_index - 1; + + if (p.tokens[last] == .Identifier) { + const name = p.source[p.tokens_start[last]..p.tokens_end[last]]; + + if (p.structs.get(name)) |val| { + components = val; + const start = p.token_index; + const arr = if (try p.parseArrayType()) |index| p.source[p.tokens_start[start]..p.tokens_end[index]] else ""; + + break :param_type try ParamType.typeToUnion(try std.fmt.allocPrint(p.alloc, "tuple{s}", .{arr}), p.alloc); + } + + return err; + } + + return err; + } + }; + + const location = p.parseDataLocation(); + if (location) |tok| { + _ = p.consumeToken(tok); + switch (tok) { + .Indexed => return error.InvalidDataLocation, + .Memory, .Calldata, .Storage => { + const isValid = switch (abitype) { + .string, .bytes => true, + .dynamicArray => true, + .fixedArray => true, + inline else => false, + }; + + if (!isValid) return error.InvalidDataLocation; + }, + inline else => {}, + } + } + + const name = p.parseIdentifier() orelse ""; + const param = .{ .type = abitype, .name = name, .internalType = null, .components = components }; + + try param_list.append(param); + + switch (p.tokens[p.token_index]) { + .Comma => p.token_index += 1, + .ClosingParen => break, + .EndOfFileToken => break, + inline else => return error.ExpectedCommaAfterParam, + } + } + + return try param_list.toOwnedSlice(); +} + +/// Parse solidity event params.\ +/// TypeExpr, DataLocation?, Identifier?, Comma? +pub fn parseEventParamsDecl(p: *Parser) ParseErrors![]const AbiEventParameter { + var param_list = std.ArrayList(AbiEventParameter).init(p.alloc); + + while (true) { + const tuple_param = if (p.consumeToken(.OpenParen) != null) try p.parseTuple(AbiEventParameter) else null; + + if (tuple_param) |t_param| { + try param_list.append(t_param); + + switch (p.tokens[p.token_index]) { + .Comma => p.token_index += 1, + .ClosingParen => break, + .EndOfFileToken => break, + inline else => return error.ExpectedCommaAfterParam, + } + + continue; + } + + var components: ?[]const AbiParameter = null; + const abitype = param_type: { + if (p.parseTypeExpr()) |result| break :param_type result else |err| { + // Before failing we check if he have an Identifier token + // And see it was defined as a struct. + const last = p.token_index - 1; + + if (p.tokens[last] == .Identifier) { + const name = p.source[p.tokens_start[last]..p.tokens_end[last]]; + + if (p.structs.get(name)) |val| { + components = val; + const start = p.token_index; + const arr = if (try p.parseArrayType()) |index| p.source[p.tokens_start[start]..p.tokens_end[index]] else ""; + + break :param_type try ParamType.typeToUnion(try std.fmt.allocPrint(p.alloc, "tuple{s}", .{arr}), p.alloc); + } + + return err; + } + + return err; + } + }; + + const location = p.parseDataLocation(); + const indexed = indexed: { + if (location) |tok| { + _ = p.consumeToken(tok); + switch (tok) { + .Indexed => break :indexed true, + inline else => return error.InvalidDataLocation, + } + } else break :indexed false; + }; + + const name = p.parseIdentifier() orelse ""; + const param = .{ .type = abitype, .name = name, .indexed = indexed, .internalType = null, .components = components }; + + try param_list.append(param); + + switch (p.tokens[p.token_index]) { + .Comma => p.token_index += 1, + .ClosingParen => break, + .EndOfFileToken => break, + inline else => return error.ExpectedCommaAfterParam, + } + } + + return try param_list.toOwnedSlice(); +} + +/// Parse solidity error params.\ +/// TypeExpr, DataLocation?, Identifier?, Comma? +pub fn parseErrorParamsDecl(p: *Parser) ParseErrors![]const AbiParameter { + var param_list = std.ArrayList(AbiParameter).init(p.alloc); + + while (true) { + const tuple_param = if (p.consumeToken(.OpenParen) != null) try p.parseTuple(AbiParameter) else null; + + if (tuple_param != null) { + try param_list.append(tuple_param.?); + + switch (p.tokens[p.token_index]) { + .Comma => p.token_index += 1, + .ClosingParen => break, + .EndOfFileToken => break, + inline else => return error.ExpectedCommaAfterParam, + } + + continue; + } + + var components: ?[]const AbiParameter = null; + const abitype = param_type: { + if (p.parseTypeExpr()) |result| break :param_type result else |err| { + const last = p.token_index - 1; + + if (p.tokens[last] == .Identifier) { + const name = p.source[p.tokens_start[last]..p.tokens_end[last]]; + + if (p.structs.get(name)) |val| { + components = val; + const start = p.token_index; + const arr = if (try p.parseArrayType()) |index| p.source[p.tokens_start[start]..p.tokens_end[index]] else ""; + + break :param_type try ParamType.typeToUnion(try std.fmt.allocPrint(p.alloc, "tuple{s}", .{arr}), p.alloc); + } + + return err; + } + + return err; + } + }; + + const location = p.parseDataLocation(); + if (location != null) return error.InvalidDataLocation; + + const name = p.parseIdentifier() orelse ""; + const param: AbiParameter = .{ .type = abitype, .name = name, .internalType = null, .components = components }; + + try param_list.append(param); + + switch (p.tokens[p.token_index]) { + .Comma => p.token_index += 1, + .ClosingParen => break, + .EndOfFileToken => break, + inline else => return error.ExpectedCommaAfterParam, + } + } + + return try param_list.toOwnedSlice(); +} + +/// Parse solidity struct params.\ +/// TypeExpr, Identifier?, SemiColon +pub fn parseStructParamDecls(p: *Parser) ParseErrors![]const AbiParameter { + var param_list = std.ArrayList(AbiParameter).init(p.alloc); + + while (true) { + const tuple_param = if (p.consumeToken(.OpenParen) != null) try p.parseTuple(AbiParameter) else null; + + if (tuple_param != null) { + try param_list.append(tuple_param.?); + + _ = try p.expectToken(.SemiColon); + + switch (p.tokens[p.token_index]) { + .ClosingBrace => break, + .EndOfFileToken => return error.UnexceptedToken, + inline else => continue, + } + } + + var components: ?[]const AbiParameter = null; + const abitype = param_type: { + if (p.parseTypeExpr()) |result| break :param_type result else |err| { + const last = p.token_index - 1; + + if (p.tokens[last] == .Identifier) { + const name = p.source[p.tokens_start[last]..p.tokens_end[last]]; + + if (p.structs.get(name)) |val| { + components = val; + const start = p.token_index; + const arr = if (try p.parseArrayType()) |index| p.source[p.tokens_start[start]..p.tokens_end[index]] else ""; + + break :param_type try ParamType.typeToUnion(try std.fmt.allocPrint(p.alloc, "tuple{s}", .{arr}), p.alloc); + } + + return err; + } + + return err; + } + }; + + const location = p.parseDataLocation(); + if (location != null) return error.InvalidDataLocation; + + const name = p.parseIdentifier() orelse ""; + const param: AbiParameter = .{ .type = abitype, .name = name, .internalType = null, .components = components }; + + try param_list.append(param); + + _ = try p.expectToken(.SemiColon); + + switch (p.tokens[p.token_index]) { + .ClosingBrace => break, + .EndOfFileToken => return error.UnexceptedToken, + inline else => continue, + } + } + + return try param_list.toOwnedSlice(); +} + +/// Parse solidity tuple params.\ +/// OpenParen, TypeExpr, Identifier?, Comma?, ClosingParen +pub fn parseTuple(p: *Parser, comptime T: type) ParseErrors!T { + const components = try p.parseErrorParamsDecl(); + + _ = try p.expectToken(.ClosingParen); + const start = p.token_index; + const end = try p.parseArrayType(); + const array_slice = if (end) |arr| p.source[p.tokens_start[start]..p.tokens_end[arr]] else null; + + const type_name = try std.fmt.allocPrint(p.alloc, "tuple{s}", .{array_slice orelse ""}); + + const abitype = try ParamType.typeToUnion(type_name, p.alloc); + + const location = p.parseDataLocation(); + const name = p.parseIdentifier() orelse ""; + + return switch (T) { + AbiParameter => { + if (location != null) return error.InvalidDataLocation; + return .{ .type = abitype, .name = name, .internalType = null, .components = components }; + }, + AbiEventParameter => .{ .type = abitype, .name = name, .internalType = null, .indexed = location == .Indexed, .components = components }, + inline else => error.InvalidType, + }; +} + +fn parseTypeExpr(p: *Parser) (ParamErrors || error{UnexceptedToken})!ParamType { + const index = p.nextToken(); + const tok = p.tokens[index]; + + if (tok.lexToken()) |type_name| { + const slice = if (try p.parseArrayType()) |arr| p.source[p.tokens_start[index]..p.tokens_end[arr]] else type_name; + + return try ParamType.typeToUnion(slice, p.alloc); + } + + return error.UnexceptedToken; +} + +fn parseArrayType(p: *Parser) error{UnexceptedToken}!?u32 { + while (true) { + const token = p.nextToken(); + + switch (p.tokens[token]) { + .OpenBracket => continue, + .Number => { + _ = try p.expectToken(.ClosingBracket); + p.token_index -= 1; + }, + .ClosingBracket => switch (p.tokens[p.token_index]) { + .OpenBracket => continue, + else => return token, + }, + inline else => { + p.token_index -= 1; + return null; + }, + } + } +} + +fn parseDataLocation(p: *Parser) ?Tokens { + const tok = p.tokens[p.token_index]; + + return switch (tok) { + .Indexed, .Calldata, .Storage, .Memory => tok, + inline else => null, + }; +} + +fn parseVisibility(p: *Parser) error{UnexceptedToken}!void { + const external = p.consumeToken(.External) orelse 0; + const public = p.consumeToken(.Public) orelse 0; + + if (external != 0 and public != 0) { + return error.UnexceptedToken; + } +} + +fn parseIdentifier(p: *Parser) ?[]const u8 { + return if (p.consumeToken(.Identifier)) |ident| p.source[p.tokens_start[ident]..p.tokens_end[ident]] else null; +} + +fn expectToken(p: *Parser, expected: Tokens) error{UnexceptedToken}!u32 { + if (p.tokens[p.token_index] != expected) return error.UnexceptedToken; + + return p.nextToken(); +} + +fn consumeToken(p: *Parser, tok: Tokens) ?u32 { + return if (p.tokens[p.token_index] == tok) p.nextToken() else null; +} + +fn nextToken(p: *Parser) u32 { + const index = p.token_index; + p.token_index += 1; + + return index; +} diff --git a/src/human-readable/abi_parsing.zig b/src/human-readable/abi_parsing.zig index df0177aa..54022127 100644 --- a/src/human-readable/abi_parsing.zig +++ b/src/human-readable/abi_parsing.zig @@ -3,13 +3,14 @@ const param = @import("../abi/abi_parameter.zig"); const std = @import("std"); const testing = std.testing; const tokens = @import("tokens.zig"); + +const Abi = abi.Abi; const Allocator = std.mem.Allocator; const ArenaAllocator = std.heap.ArenaAllocator; const Extract = @import("../meta/utils.zig").Extract; const ParamType = @import("../abi/param_type.zig").ParamType; const StateMutability = @import("../abi/state_mutability.zig").StateMutability; -const Lexer = @import("lexer.zig").Lexer; -const Parser = @import("Parser.zig"); +const HumanAbi = @import("HumanAbi.zig"); pub fn AbiParsed(comptime T: type) type { return struct { @@ -32,10 +33,13 @@ pub fn AbiParsed(comptime T: type) type { /// The return value will depend on the abi type selected. /// The function will return an error if the provided type doesn't match the /// tokens from the provided signature -pub fn parseHumanReadable(comptime T: type, alloc: Allocator, source: [:0]const u8) Parser.ParseErrors!AbiParsed(T) { +pub fn parseHumanReadable(alloc: Allocator, source: [:0]const u8) !AbiParsed(Abi) { std.debug.assert(source.len > 0); - var abi_parsed = AbiParsed(T){ .arena = try alloc.create(ArenaAllocator), .value = undefined }; + var abi_parsed = AbiParsed(Abi){ + .arena = try alloc.create(ArenaAllocator), + .value = undefined, + }; errdefer alloc.destroy(abi_parsed.arena); abi_parsed.arena.* = ArenaAllocator.init(alloc); @@ -43,45 +47,23 @@ pub fn parseHumanReadable(comptime T: type, alloc: Allocator, source: [:0]const const allocator = abi_parsed.arena.allocator(); - var lex = Lexer.init(source); - - var list = Parser.TokenList{}; - defer list.deinit(allocator); - - while (true) { - const tok = lex.scan(); - try list.append(allocator, .{ .token_type = tok.syntax, .start = tok.location.start, .end = tok.location.end }); - - if (tok.syntax == .EndOfFileToken) break; - } - - var parser: Parser = .{ - .alloc = allocator, - .tokens = list.items(.token_type), - .tokens_start = list.items(.start), - .tokens_end = list.items(.end), - .token_index = 0, - .source = source, - .structs = .{}, - }; - - abi_parsed.value = try innerParse(T, &parser); + abi_parsed.value = try HumanAbi.parse(allocator, source); return abi_parsed; } -fn innerParse(comptime T: type, parser: *Parser) Parser.ParseErrors!T { - return switch (T) { - abi.Abi => parser.parseAbiProto(), - abi.AbiItem => parser.parseAbiItemProto(), - abi.Function => parser.parseFunctionFnProto(), - abi.Event => parser.parseEventFnProto(), - abi.Error => parser.parseErrorFnProto(), - abi.Constructor => parser.parseConstructorFnProto(), - abi.Fallback => parser.parseFallbackFnProto(), - abi.Receive => parser.parseReceiveFnProto(), - []const param.AbiParameter => parser.parseFuncParamsDecl(), - []const param.AbiEventParameter => parser.parseEventParamsDecl(), - inline else => @compileError("Provided type '" ++ @typeName(T) ++ "' is not supported for human readable parsing"), - }; -} +// fn innerParse(comptime T: type, parser: *Parser) Parser.ParseErrors!T { +// return switch (T) { +// abi.Abi => parser.parseAbiProto(), +// abi.AbiItem => parser.parseAbiItemProto(), +// abi.Function => parser.parseFunctionFnProto(), +// abi.Event => parser.parseEventFnProto(), +// abi.Error => parser.parseErrorFnProto(), +// abi.Constructor => parser.parseConstructorFnProto(), +// abi.Fallback => parser.parseFallbackFnProto(), +// abi.Receive => parser.parseReceiveFnProto(), +// []const param.AbiParameter => parser.parseFuncParamsDecl(), +// []const param.AbiEventParameter => parser.parseEventParamsDecl(), +// inline else => @compileError("Provided type '" ++ @typeName(T) ++ "' is not supported for human readable parsing"), +// }; +// } diff --git a/src/tests/human-readable/parser_new.test.zig b/src/tests/human-readable/parser_new.test.zig index b19bc92b..2ee4fbbb 100644 --- a/src/tests/human-readable/parser_new.test.zig +++ b/src/tests/human-readable/parser_new.test.zig @@ -2,20 +2,25 @@ const tokenizer = @import("../../human-readable/lexer.zig"); const std = @import("std"); const testing = std.testing; -const Parser = @import("../../human-readable/ParserNew.zig"); +const Parser = @import("../../human-readable/Parser.zig"); const Ast = @import("../../human-readable/Ast.zig"); const HumanAbi = @import("../../human-readable/HumanAbi.zig"); test "Human readable" { - var ast = try Ast.parse(testing.allocator, "struct Foo{uint bar;}"); - defer ast.deinit(testing.allocator); + var arena = std.heap.ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + + const abi = try HumanAbi.parse(arena.allocator(), "function foo(uint bar) payable returns(string bar)"); + // defer ast.deinit(testing.allocator); // const abi_gen: HumanAbi = .{ // .allocator = testing.allocator, // .ast = &ast, // }; - - std.debug.print("FOOOOO: {any}\n", .{ast.nodes.items(.tag)}); + // + // const abi = try abi_gen.toAbi(); + // std.debug.print("FOOOOO: {any}\n", .{ast.nodes.items(.tag)}); // std.debug.print("FOOOOO: {s}\n", .{ast.getNodeSource(1)}); - // std.debug.print("FOOOOO: {}\n", .{try abi_gen.toStructParamComponent(1)}); + std.debug.print("FOOOOO: {any}\n", .{abi}); + std.debug.print("FOOOOO: {any}\n", .{abi.len}); } From 04895c22f806dee698e9acac8490b34603329854 Mon Sep 17 00:00:00 2001 From: Raiden1411 <67233402+Raiden1411@users.noreply.github.com> Date: Fri, 4 Oct 2024 23:46:23 +0100 Subject: [PATCH 11/18] examples: update and remove some use cases for now --- bench/benchmark.zig | 52 +++++++++++++++--------------- examples/contract/contract.zig | 2 +- src/human-readable/abi_parsing.zig | 2 +- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/bench/benchmark.zig b/bench/benchmark.zig index 88444260..afd53b66 100644 --- a/bench/benchmark.zig +++ b/bench/benchmark.zig @@ -52,32 +52,32 @@ pub fn main() !void { defer client.deinit(); try printer.writer().print("{s}Benchmark running in {s} mode\n", .{ " " ** 20, @tagName(@import("builtin").mode) }); - try printer.writeBoarder(.HumanReadableAbi); - - { - const opts = .{ .warmup_runs = 5, .runs = 100 }; - - var count: usize = 0; - while (count < opts.warmup_runs) : (count += 1) { - const abi = try zabi_root.human_readable.parsing.parseHumanReadable(zabi_root.abi.abitypes.Abi, allocator, constants.slice); - defer abi.deinit(); - } - - var timer = try std.time.Timer.start(); - while (count < opts.runs) : (count += 1) { - const abi = try zabi_root.human_readable.parsing.parseHumanReadable(zabi_root.abi.abitypes.Abi, allocator, constants.slice); - defer abi.deinit(); - } - - const mean = @divFloor(timer.lap(), opts.runs); - - const result: benchmark.BenchmarkResult = .{ - .allocator = allocator, - .opts = opts, - .mean = mean, - }; - result.printSummary(); - } + // try printer.writeBoarder(.HumanReadableAbi); + // + // { + // const opts = .{ .warmup_runs = 5, .runs = 100 }; + // + // var count: usize = 0; + // while (count < opts.warmup_runs) : (count += 1) { + // const abi = try zabi_root.human_readable.parsing.parseHumanReadable(zabi_root.abi.abitypes.Abi, allocator, constants.slice); + // defer abi.deinit(); + // } + // + // var timer = try std.time.Timer.start(); + // while (count < opts.runs) : (count += 1) { + // const abi = try zabi_root.human_readable.parsing.parseHumanReadable(zabi_root.abi.abitypes.Abi, allocator, constants.slice); + // defer abi.deinit(); + // } + // + // const mean = @divFloor(timer.lap(), opts.runs); + // + // const result: benchmark.BenchmarkResult = .{ + // .allocator = allocator, + // .opts = opts, + // .mean = mean, + // }; + // result.printSummary(); + // } try printer.writeBoarder(.HttpClient); { diff --git a/examples/contract/contract.zig b/examples/contract/contract.zig index 703b8cd5..7c3c98da 100644 --- a/examples/contract/contract.zig +++ b/examples/contract/contract.zig @@ -30,7 +30,7 @@ pub fn main() !void { \\ function approve(address operator, uint256 size) external returns (bool) \\ function balanceOf(address owner) public view returns (uint256) ; - var abi_parsed = try human.parseHumanReadable(Abi, gpa.allocator(), slice); + var abi_parsed = try human.parseHumanReadable(gpa.allocator(), slice); defer abi_parsed.deinit(); var contract = try Contract.init(.{ diff --git a/src/human-readable/abi_parsing.zig b/src/human-readable/abi_parsing.zig index 54022127..572db63a 100644 --- a/src/human-readable/abi_parsing.zig +++ b/src/human-readable/abi_parsing.zig @@ -33,7 +33,7 @@ pub fn AbiParsed(comptime T: type) type { /// The return value will depend on the abi type selected. /// The function will return an error if the provided type doesn't match the /// tokens from the provided signature -pub fn parseHumanReadable(alloc: Allocator, source: [:0]const u8) !AbiParsed(Abi) { +pub fn parseHumanReadable(alloc: Allocator, source: [:0]const u8) HumanAbi.Errors!AbiParsed(Abi) { std.debug.assert(source.len > 0); var abi_parsed = AbiParsed(Abi){ From 6ce29f748c04d6b97180d374d259dbc96cdc2ca4 Mon Sep 17 00:00:00 2001 From: Raiden1411 <67233402+Raiden1411@users.noreply.github.com> Date: Fri, 4 Oct 2024 23:53:54 +0100 Subject: [PATCH 12/18] bench: update benchmark --- bench/benchmark.zig | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/bench/benchmark.zig b/bench/benchmark.zig index afd53b66..1e778319 100644 --- a/bench/benchmark.zig +++ b/bench/benchmark.zig @@ -327,16 +327,20 @@ pub fn decodingFunctions(allocator: Allocator, printer: *ColorWriter(@TypeOf(std try printer.writer().writeAll("Abi Logs Decoding... "); { - const event = try parseHumanReadable( - Event, - allocator, - "event Foo(uint indexed a, int indexed b, bool indexed c, bytes5 indexed d)", - ); - defer event.deinit(); + const event: Event = .{ + .type = .event, + .name = "Foo", + .inputs = &.{ + .{ .type = .{ .uint = 256 }, .indexed = true, .name = "a" }, + .{ .type = .{ .int = 256 }, .indexed = true, .name = "b" }, + .{ .type = .{ .bool = {} }, .indexed = true, .name = "c" }, + .{ .type = .{ .fixedBytes = 5 }, .indexed = true, .name = "d" }, + }, + }; const encoded = try encodeLogTopics( allocator, - event.value, + event, .{ 69, -420, true, "01234" }, ); defer allocator.free(encoded); @@ -408,16 +412,20 @@ pub fn encodingFunctions(allocator: Allocator, printer: *ColorWriter(@TypeOf(std try printer.writer().writeAll("ABI Logs Encoding... "); { - const event = try parseHumanReadable( - Event, - allocator, - "event Foo(uint indexed a, int indexed b, bool indexed c, bytes5 indexed d)", - ); - defer event.deinit(); + const event: Event = .{ + .type = .event, + .name = "Foo", + .inputs = &.{ + .{ .type = .{ .uint = 256 }, .indexed = true, .name = "a" }, + .{ .type = .{ .int = 256 }, .indexed = true, .name = "b" }, + .{ .type = .{ .bool = {} }, .indexed = true, .name = "c" }, + .{ .type = .{ .fixedBytes = 5 }, .indexed = true, .name = "d" }, + }, + }; const result = try benchmark.benchmark(allocator, zabi_root.encoding.logs_encoding.encodeLogTopics, .{ allocator, - event.value, + event, .{ 69, -420, true, "01234" }, }, .{ .warmup_runs = 5, .runs = 100 }); result.printSummary(); From a32761f90d5c59ababaf72ce6041ef87b43495fa Mon Sep 17 00:00:00 2001 From: Raiden1411 <67233402+Raiden1411@users.noreply.github.com> Date: Sat, 5 Oct 2024 17:19:29 +0100 Subject: [PATCH 13/18] human: add array, struct and tuple types --- bench/benchmark.zig | 52 +- src/human-readable/Ast.zig | 10 +- src/human-readable/HumanAbi.zig | 339 +++++++- src/human-readable/Parser.zig | 65 +- src/tests/human-readable/abi_parsing.test.zig | 780 +++++++++--------- src/tests/human-readable/parser_new.test.zig | 2 +- src/tests/root.zig | 2 +- 7 files changed, 784 insertions(+), 466 deletions(-) diff --git a/bench/benchmark.zig b/bench/benchmark.zig index 1e778319..49301100 100644 --- a/bench/benchmark.zig +++ b/bench/benchmark.zig @@ -52,32 +52,32 @@ pub fn main() !void { defer client.deinit(); try printer.writer().print("{s}Benchmark running in {s} mode\n", .{ " " ** 20, @tagName(@import("builtin").mode) }); - // try printer.writeBoarder(.HumanReadableAbi); - // - // { - // const opts = .{ .warmup_runs = 5, .runs = 100 }; - // - // var count: usize = 0; - // while (count < opts.warmup_runs) : (count += 1) { - // const abi = try zabi_root.human_readable.parsing.parseHumanReadable(zabi_root.abi.abitypes.Abi, allocator, constants.slice); - // defer abi.deinit(); - // } - // - // var timer = try std.time.Timer.start(); - // while (count < opts.runs) : (count += 1) { - // const abi = try zabi_root.human_readable.parsing.parseHumanReadable(zabi_root.abi.abitypes.Abi, allocator, constants.slice); - // defer abi.deinit(); - // } - // - // const mean = @divFloor(timer.lap(), opts.runs); - // - // const result: benchmark.BenchmarkResult = .{ - // .allocator = allocator, - // .opts = opts, - // .mean = mean, - // }; - // result.printSummary(); - // } + try printer.writeBoarder(.HumanReadableAbi); + + { + const opts = .{ .warmup_runs = 5, .runs = 100 }; + + var count: usize = 0; + while (count < opts.warmup_runs) : (count += 1) { + const abi = try zabi_root.human_readable.parsing.parseHumanReadable(allocator, constants.slice); + defer abi.deinit(); + } + + var timer = try std.time.Timer.start(); + while (count < opts.runs) : (count += 1) { + const abi = try zabi_root.human_readable.parsing.parseHumanReadable(allocator, constants.slice); + defer abi.deinit(); + } + + const mean = @divFloor(timer.lap(), opts.runs); + + const result: benchmark.BenchmarkResult = .{ + .allocator = allocator, + .opts = opts, + .mean = mean, + }; + result.printSummary(); + } try printer.writeBoarder(.HttpClient); { diff --git a/src/human-readable/Ast.zig b/src/human-readable/Ast.zig index cae78583..45bcaa54 100644 --- a/src/human-readable/Ast.zig +++ b/src/human-readable/Ast.zig @@ -582,7 +582,7 @@ pub fn firstToken(self: Ast, node: Node.Index) TokenIndex { .root => return 0, .elementary_type, - .identifier, + .struct_type, .function_proto_simple, .function_proto_multi, .function_proto_one, @@ -633,13 +633,13 @@ pub fn lastToken(self: Ast, node: Node.Index) TokenIndex { switch (nodes[current_node]) { .root => return @as(TokenIndex, @intCast(self.tokens.len - 1)), - .array_type, .tuple_type, .tuple_type_one, - => return 0 + end_offset, + .array_type, + => return data[current_node].rhs + end_offset, .elementary_type, - .identifier, + .struct_type, .unreachable_node, => return main[current_node] + end_offset, @@ -891,7 +891,7 @@ pub const Node = struct { pub const Tag = enum { root, - identifier, + struct_type, unreachable_node, constructor_proto_simple, diff --git a/src/human-readable/HumanAbi.zig b/src/human-readable/HumanAbi.zig index 5794855a..35bb8bd1 100644 --- a/src/human-readable/HumanAbi.zig +++ b/src/human-readable/HumanAbi.zig @@ -23,7 +23,7 @@ const Parser = @import("Parser.zig"); const StateMutability = @import("../abi/state_mutability.zig").StateMutability; /// Set of errors when converting to the abi -pub const HumanAbiErrors = ParamErrors || Allocator.Error; +pub const HumanAbiErrors = ParamErrors || Allocator.Error || error{ NoSpaceLeft, MissingTypeDeclaration }; /// Set of erros when generating the ABI pub const Errors = HumanAbiErrors || Parser.ParserErrors || error{ UnexpectedMutability, UnexpectedNode }; @@ -35,6 +35,8 @@ const HumanAbi = @This(); allocator: Allocator, /// Ast generated by the parser so that we can use to generate the ABI. ast: *const Ast, +/// Set of `AbiParameter` components by the struct type. +struct_params: std.StringArrayHashMapUnmanaged([]const AbiParameter), /// Parses the source, builds the Ast and generates the ABI. /// @@ -43,16 +45,19 @@ pub fn parse(arena: Allocator, source: [:0]const u8) Errors!Abi { var ast = try Ast.parse(arena, source); defer ast.deinit(arena); - const abi_gen: HumanAbi = .{ + var abi_gen: HumanAbi = .{ .allocator = arena, .ast = &ast, + .struct_params = .empty, }; + errdefer abi_gen.struct_params.deinit(arena); return abi_gen.toAbi(); } /// Generates the `Abi` from the ast nodes. -pub fn toAbi(self: HumanAbi) (HumanAbiErrors || error{ UnexpectedNode, UnexpectedMutability })!Abi { +pub fn toAbi(self: *HumanAbi) (HumanAbiErrors || error{ UnexpectedNode, UnexpectedMutability })!Abi { const nodes = self.ast.nodes.items(.tag); + const data = self.ast.nodes.items(.data); var list = std.ArrayList(AbiItem).init(self.allocator); errdefer list.deinit(); @@ -73,6 +78,20 @@ pub fn toAbi(self: HumanAbi) (HumanAbiErrors || error{ UnexpectedNode, Unexpecte .fallback_proto_simple, .receive_proto, => try list.append(try self.toAbiItem(@intCast(index))), + .struct_decl_one, + => { + const struct_param = try self.toStructComponentsOne(@intCast(index)); + const name = self.ast.tokenSlice(data[@intCast(index)].lhs); + + try self.struct_params.put(self.allocator, name, struct_param); + }, + .struct_decl, + => { + const struct_param = try self.toStructComponents(@intCast(index)); + const name = self.ast.tokenSlice(data[@intCast(index)].lhs); + + try self.struct_params.put(self.allocator, name, struct_param); + }, else => continue, } } @@ -88,8 +107,8 @@ pub fn toAbiItem(self: HumanAbi, node: Node.Index) (HumanAbiErrors || error{ Une .function_proto_one => .{ .abiFunction = try self.toAbiFunctionOne(node) }, .function_proto_multi => .{ .abiFunction = try self.toAbiFunctionMulti(node) }, .function_proto_simple => .{ .abiFunction = try self.toAbiFunctionSimple(node) }, - .event_proto_multi => .{ .abiEvent = try self.toAbiEventSimple(node) }, - .event_proto_simple => .{ .abiEvent = try self.toAbiEventMulti(node) }, + .event_proto_multi => .{ .abiEvent = try self.toAbiEventMulti(node) }, + .event_proto_simple => .{ .abiEvent = try self.toAbiEventSimple(node) }, .error_proto_multi => .{ .abiError = try self.toAbiErrorMulti(node) }, .error_proto_simple => .{ .abiError = try self.toAbiErrorSimple(node) }, .constructor_proto_multi => .{ .abiConstructor = try self.toAbiConstructorMulti(node) }, @@ -229,7 +248,7 @@ pub fn toAbiFunctionSimple(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiF }; } -pub fn toStructComponents(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiParameter { +pub fn toStructComponents(self: HumanAbi, node: Node.Index) HumanAbiErrors![]const AbiParameter { const nodes = self.ast.nodes.items(.tag); std.debug.assert(nodes[node] == .struct_decl); @@ -237,14 +256,10 @@ pub fn toStructComponents(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiPa const params = try self.toAbiParametersFromDecl(struct_decl.ast.members); - return AbiParameter{ - .type = .{ .tuple = {} }, - .name = self.ast.tokenSlice(struct_decl.name), - .components = params, - }; + return params; } -pub fn toStructComponentsOne(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiParameter { +pub fn toStructComponentsOne(self: HumanAbi, node: Node.Index) HumanAbiErrors![]const AbiParameter { const nodes = self.ast.nodes.items(.tag); std.debug.assert(nodes[node] == .struct_decl_one); @@ -253,11 +268,7 @@ pub fn toStructComponentsOne(self: HumanAbi, node: Node.Index) HumanAbiErrors!Ab const params = try self.toAbiParametersFromDecl(struct_decl.ast.members); - return AbiParameter{ - .type = .{ .tuple = {} }, - .name = self.ast.tokenSlice(struct_decl.name), - .components = params, - }; + return params; } pub fn toAbiConstructorMulti(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiConstructor { @@ -390,46 +401,298 @@ pub fn toAbiParameter(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiParame const nodes = self.ast.nodes.items(.tag); const data = self.ast.nodes.items(.data); const main = self.ast.nodes.items(.main_token); + const starts = self.ast.tokens.items(.start); std.debug.assert(nodes[node] == .var_decl); - const type_slice = self.ast.tokenSlice(main[data[node].lhs]); - const param_type = try ParamType.typeToUnion(type_slice, self.allocator); + switch (nodes[data[node].lhs]) { + .array_type => { + const array_type = data[node].lhs; + + switch (nodes[data[array_type].lhs]) { + .tuple_type, + .tuple_type_one, + => { + const open_bracket = starts[main[data[node].lhs]]; + const closing_bracket = starts[data[data[node].lhs].rhs] + 1; + + var buffer: [256]u8 = undefined; + const slice = try std.fmt.bufPrint(&buffer, "tuple{s}", .{self.ast.source[open_bracket..closing_bracket]}); + + const param_type = try ParamType.typeToUnion(slice, self.allocator); + const components = try self.toAbiComponents(data[array_type].lhs); + + return .{ + .type = param_type, + .name = if (data[node].rhs == 0) "" else self.ast.tokenSlice(data[node].rhs), + .components = components, + }; + }, + .struct_type => { + const open_bracket = starts[main[data[node].lhs]]; + const closing_bracket = starts[data[data[node].lhs].rhs] + 1; + + var buffer: [256]u8 = undefined; + const slice = try std.fmt.bufPrint(&buffer, "tuple{s}", .{self.ast.source[open_bracket..closing_bracket]}); + + const param_type = try ParamType.typeToUnion(slice, self.allocator); + const components = self.struct_params.get(self.ast.tokenSlice(main[data[array_type].lhs])) orelse return error.MissingTypeDeclaration; + + return .{ + .type = param_type, + .name = if (data[node].rhs == 0) "" else self.ast.tokenSlice(data[node].rhs), + .components = components, + }; + }, + else => { + const type_slice = self.ast.getNodeSource(data[node].lhs); + const param_type = try ParamType.typeToUnion(type_slice, self.allocator); + + return .{ + .type = param_type, + .name = if (main[node] == 0) "" else self.ast.tokenSlice(main[node]), + }; + }, + } + }, + .tuple_type_one, + .tuple_type, + => { + const components = try self.toAbiComponents(data[node].lhs); + + return .{ + .type = .{ .tuple = {} }, + .name = if (data[node].rhs == 0) "" else self.ast.tokenSlice(data[node].rhs), + .components = components, + }; + }, + .elementary_type => { + const type_slice = self.ast.tokenSlice(main[data[node].lhs]); + const param_type = try ParamType.typeToUnion(type_slice, self.allocator); + + return .{ + .type = param_type, + .name = if (data[node].rhs == 0) "" else self.ast.tokenSlice(data[node].rhs), + }; + }, + .struct_type => { + const components = self.struct_params.get(self.ast.tokenSlice(main[data[node].lhs])) orelse return error.MissingTypeDeclaration; + + return .{ + .type = .{ .tuple = {} }, + .name = if (data[node].rhs == 0) "" else self.ast.tokenSlice(data[node].rhs), + .components = components, + }; + }, + else => unreachable, // Invalid Node. + } +} - return .{ - .type = param_type, - .name = if (data[node].rhs == 0) "" else self.ast.tokenSlice(data[node].rhs), - }; +pub fn toAbiComponents(self: HumanAbi, node: Node.Index) HumanAbiErrors![]const AbiParameter { + const nodes = self.ast.nodes.items(.tag); + const data = self.ast.nodes.items(.data); + std.debug.assert(nodes[node] == .tuple_type or nodes[node] == .tuple_type_one); + + if (nodes[node] == .tuple_type_one) { + var buffer: [1]Node.Index = undefined; + buffer[0] = data[node].lhs; + + const components = try self.toAbiParametersFromDecl(if (data[node].lhs == 0) buffer[0..0] else buffer[0..1]); + + return components; + } + + const extra = self.ast.extraData(Ast.Node.Range, data[node].lhs); + const components = try self.toAbiParametersFromDecl(self.ast.extra_data[extra.start..extra.end]); + + return components; } pub fn toAbiEventParameter(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiEventParameter { const nodes = self.ast.nodes.items(.tag); const data = self.ast.nodes.items(.data); const main = self.ast.nodes.items(.main_token); - std.debug.assert(nodes[node] == .event_var_decl); + const starts = self.ast.tokens.items(.start); - const type_slice = self.ast.tokenSlice(main[data[node].lhs]); - const param_type = try ParamType.typeToUnion(type_slice, self.allocator); + std.debug.assert(nodes[node] == .event_var_decl); - return .{ - .type = param_type, - .name = if (data[node].rhs == 0) "" else self.ast.tokenSlice(data[node].rhs), - .indexed = if (main[node] != 0) true else false, - }; + switch (nodes[data[node].lhs]) { + .array_type => { + const array_type = data[node].lhs; + + switch (nodes[data[array_type].lhs]) { + .tuple_type, + .tuple_type_one, + => { + const open_bracket = starts[main[data[node].lhs]]; + const closing_bracket = starts[data[data[node].lhs].rhs] + 1; + + var buffer: [256]u8 = undefined; + const slice = try std.fmt.bufPrint(&buffer, "tuple{s}", .{self.ast.source[open_bracket..closing_bracket]}); + + const param_type = try ParamType.typeToUnion(slice, self.allocator); + const components = try self.toAbiComponents(data[array_type].lhs); + + return .{ + .type = param_type, + .name = if (data[node].rhs == 0) "" else self.ast.tokenSlice(data[node].rhs), + .indexed = if (main[node] != 0) true else false, + .components = components, + }; + }, + .struct_type => { + const open_bracket = starts[main[data[node].lhs]]; + const closing_bracket = starts[data[data[node].lhs].rhs] + 1; + + var buffer: [256]u8 = undefined; + const slice = try std.fmt.bufPrint(&buffer, "tuple{s}", .{self.ast.source[open_bracket..closing_bracket]}); + + const param_type = try ParamType.typeToUnion(slice, self.allocator); + const components = self.struct_params.get(self.ast.tokenSlice(main[data[array_type].lhs])) orelse return error.MissingTypeDeclaration; + + return .{ + .type = param_type, + .name = if (data[node].rhs == 0) "" else self.ast.tokenSlice(data[node].rhs), + .indexed = if (main[node] != 0) true else false, + .components = components, + }; + }, + else => { + const type_slice = self.ast.getNodeSource(data[node].lhs); + const param_type = try ParamType.typeToUnion(type_slice, self.allocator); + + return .{ + .type = param_type, + .indexed = if (main[node] != 0) true else false, + .name = if (main[node] == 0) "" else self.ast.tokenSlice(main[node]), + }; + }, + } + }, + .tuple_type_one, + .tuple_type, + => { + const components = try self.toAbiComponents(data[node].lhs); + + return .{ + .type = .{ .tuple = {} }, + .name = if (data[node].rhs == 0) "" else self.ast.tokenSlice(data[node].rhs), + .indexed = if (main[node] != 0) true else false, + .components = components, + }; + }, + .elementary_type => { + const type_slice = self.ast.tokenSlice(main[data[node].lhs]); + const param_type = try ParamType.typeToUnion(type_slice, self.allocator); + + return .{ + .type = param_type, + .name = if (data[node].rhs == 0) "" else self.ast.tokenSlice(data[node].rhs), + .indexed = if (main[node] != 0) true else false, + }; + }, + .struct_type => { + const components = self.struct_params.get(self.ast.tokenSlice(main[data[node].lhs])) orelse return error.MissingTypeDeclaration; + + return .{ + .type = .{ .tuple = {} }, + .name = if (data[node].rhs == 0) "" else self.ast.tokenSlice(data[node].rhs), + .indexed = if (main[node] != 0) true else false, + .components = components, + }; + }, + else => unreachable, // Invalid Node. + } } pub fn toAbiParameterFromDecl(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiParameter { const nodes = self.ast.nodes.items(.tag); const data = self.ast.nodes.items(.data); const main = self.ast.nodes.items(.main_token); - std.debug.assert(nodes[node] == .error_var_decl or nodes[node] == .struct_field); + const starts = self.ast.tokens.items(.start); - const type_slice = self.ast.tokenSlice(main[data[node].lhs]); - const param_type = try ParamType.typeToUnion(type_slice, self.allocator); + std.debug.assert(nodes[node] == .error_var_decl or nodes[node] == .struct_field); - return .{ - .type = param_type, - .name = if (main[node] == 0) "" else self.ast.tokenSlice(main[node]), - }; + switch (nodes[data[node].lhs]) { + .array_type => { + const array_type = data[node].lhs; + + switch (nodes[data[array_type].lhs]) { + .tuple_type, + .tuple_type_one, + => { + const open_bracket = starts[main[data[node].lhs]]; + const closing_bracket = starts[data[data[node].lhs].rhs] + 1; + + var buffer: [256]u8 = undefined; + const slice = try std.fmt.bufPrint(&buffer, "tuple{s}", .{self.ast.source[open_bracket..closing_bracket]}); + + const param_type = try ParamType.typeToUnion(slice, self.allocator); + const components = try self.toAbiComponents(data[array_type].lhs); + + return .{ + .type = param_type, + .name = if (main[node] == 0) "" else self.ast.tokenSlice(main[node]), + .components = components, + }; + }, + .struct_type => { + const open_bracket = starts[main[data[node].lhs]]; + const closing_bracket = starts[data[data[node].lhs].rhs] + 1; + + var buffer: [256]u8 = undefined; + const slice = try std.fmt.bufPrint(&buffer, "tuple{s}", .{self.ast.source[open_bracket..closing_bracket]}); + + const param_type = try ParamType.typeToUnion(slice, self.allocator); + const components = self.struct_params.get(self.ast.tokenSlice(main[data[array_type].lhs])) orelse return error.MissingTypeDeclaration; + + return .{ + .type = param_type, + .name = if (main[node] == 0) "" else self.ast.tokenSlice(main[node]), + .components = components, + }; + }, + else => { + const type_slice = self.ast.getNodeSource(data[node].lhs); + const param_type = try ParamType.typeToUnion(type_slice, self.allocator); + + return .{ + .type = param_type, + .name = if (main[node] == 0) "" else self.ast.tokenSlice(main[node]), + }; + }, + } + }, + .tuple_type_one, + .tuple_type, + => { + const components = try self.toAbiComponents(data[node].lhs); + + return .{ + .type = .{ .tuple = {} }, + .name = if (main[node] == 0) "" else self.ast.tokenSlice(main[node]), + .components = components, + }; + }, + .elementary_type => { + const type_slice = self.ast.tokenSlice(main[data[node].lhs]); + const param_type = try ParamType.typeToUnion(type_slice, self.allocator); + + return .{ + .type = param_type, + .name = if (main[node] == 0) "" else self.ast.tokenSlice(main[node]), + }; + }, + .struct_type => { + const components = self.struct_params.get(self.ast.tokenSlice(main[data[node].lhs])) orelse return error.MissingTypeDeclaration; + + return .{ + .type = .{ .tuple = {} }, + .name = if (main[node] == 0) "" else self.ast.tokenSlice(main[node]), + .components = components, + }; + }, + else => unreachable, // Invalid Node. + } } pub fn toAbiFallbackMulti(self: HumanAbi, node: Node.Index) Allocator.Error!AbiFallback { diff --git a/src/human-readable/Parser.zig b/src/human-readable/Parser.zig index 822696bc..ad564ff5 100644 --- a/src/human-readable/Parser.zig +++ b/src/human-readable/Parser.zig @@ -533,7 +533,7 @@ pub fn parseErrorVarDecl(self: *Parser) ParserErrors!Node.Index { if (sol_type == 0) return null_node; - const identifier = try self.expectToken(.Identifier); + const identifier = self.consumeToken(.Identifier) orelse null_node; return self.addNode(.{ .tag = .error_var_decl, @@ -698,20 +698,75 @@ pub fn expectType(self: *Parser) ParserErrors!Node.Index { return index; } -pub fn parseType(self: *Parser) Allocator.Error!Node.Index { +pub fn parseType(self: *Parser) ParserErrors!Node.Index { const sol_type = switch (self.token_tags[self.token_index]) { .Identifier => try self.addNode(.{ - .tag = .identifier, + .tag = .struct_type, .main_token = self.nextToken(), .data = .{ .lhs = undefined, .rhs = undefined, }, }), - else => self.consumeElementaryType(), + .OpenParen => try self.parseTupleType(), + else => try self.consumeElementaryType(), }; - return sol_type; + if (self.token_tags[self.token_index] != .OpenBracket) + return sol_type; + + const l_bracket = self.token_index; + + const r_bracket = while (true) { + switch (self.token_tags[self.token_index]) { + .OpenBracket, + .Number, + => self.token_index += 1, + .ClosingBracket => { + if (self.token_tags[self.token_index + 1] == .OpenBracket) { + self.token_index += 1; + continue; + } + + break self.nextToken(); + }, + else => return error.ParsingError, + } + }; + + return self.addNode(.{ + .tag = .array_type, + .main_token = l_bracket, + .data = .{ + .lhs = sol_type, + .rhs = r_bracket, + }, + }); +} + +pub fn parseTupleType(self: *Parser) ParserErrors!Node.Index { + const l_paren = self.consumeToken(.OpenParen) orelse return null_node; + + const values = try self.parseErrorVarDecls(); + + return switch (values) { + .zero_one => |elem| self.addNode(.{ + .tag = .tuple_type_one, + .main_token = l_paren, + .data = .{ + .lhs = elem, + .rhs = self.token_index - 1, + }, + }), + .multi => |elems| self.addNode(.{ + .tag = .tuple_type, + .main_token = l_paren, + .data = .{ + .lhs = try self.addExtraData(elems), + .rhs = self.token_index - 1, + }, + }), + }; } pub fn consumeElementaryType(self: *Parser) Allocator.Error!Node.Index { diff --git a/src/tests/human-readable/abi_parsing.test.zig b/src/tests/human-readable/abi_parsing.test.zig index 2d74d786..c2da24b7 100644 --- a/src/tests/human-readable/abi_parsing.test.zig +++ b/src/tests/human-readable/abi_parsing.test.zig @@ -8,343 +8,343 @@ const ParamType = param_type.ParamType; const parseHumanReadable = @import("../../human-readable/abi_parsing.zig").parseHumanReadable; -test "AbiParameter" { - const slice = "address foo"; - - const params = try parseHumanReadable([]const param.AbiParameter, testing.allocator, slice); - defer params.deinit(); - - for (params.value) |val| { - try testing.expectEqual(val.type, ParamType{ .address = {} }); - try testing.expectEqualStrings(val.name, "foo"); - } -} - -test "AbiParameters" { - const slice = "address foo, int120 bar"; - - const params = try parseHumanReadable([]const param.AbiParameter, testing.allocator, slice); - defer params.deinit(); - - try testing.expectEqual(ParamType{ .address = {} }, params.value[0].type); - try testing.expectEqual(ParamType{ .int = 120 }, params.value[1].type); - - try testing.expectEqualStrings("foo", params.value[0].name); - try testing.expectEqualStrings("bar", params.value[1].name); - - try testing.expectEqual(params.value.len, 2); -} - -test "AbiParameters dynamic array" { - const slice = "address[] foo, int120 bar"; - - const params = try parseHumanReadable([]const param.AbiParameter, testing.allocator, slice); - defer params.deinit(); - - try testing.expectEqual(ParamType{ .address = {} }, params.value[0].type.dynamicArray.*); - try testing.expectEqual(ParamType{ .int = 120 }, params.value[1].type); - - try testing.expectEqualStrings("foo", params.value[0].name); - try testing.expectEqualStrings("bar", params.value[1].name); - - try testing.expectEqual(params.value.len, 2); -} - -test "AbiParameters 2d dynamic array" { - const slice = "address[][] foo, int120 bar"; - - const params = try parseHumanReadable([]const param.AbiParameter, testing.allocator, slice); - defer params.deinit(); - - try testing.expectEqual(ParamType{ .address = {} }, params.value[0].type.dynamicArray.dynamicArray.*); - try testing.expectEqual(ParamType{ .int = 120 }, params.value[1].type); - - try testing.expectEqualStrings("foo", params.value[0].name); - try testing.expectEqualStrings("bar", params.value[1].name); - - try testing.expectEqual(params.value.len, 2); -} - -test "AbiParameters mixed 2d array" { - const slice = "address[5][] foo, int120 bar"; - - const params = try parseHumanReadable([]const param.AbiParameter, testing.allocator, slice); - defer params.deinit(); - - try testing.expectEqual(ParamType{ .address = {} }, params.value[0].type.dynamicArray.fixedArray.child.*); - try testing.expectEqual(ParamType{ .int = 120 }, params.value[1].type); - - try testing.expectEqualStrings("foo", params.value[0].name); - try testing.expectEqualStrings("bar", params.value[1].name); - - try testing.expectEqual(params.value.len, 2); -} - -test "AbiParameters with fixed array" { - const slice = "address[5] foo, int120 bar"; - - const params = try parseHumanReadable([]const param.AbiParameter, testing.allocator, slice); - defer params.deinit(); - - try testing.expectEqual(ParamType{ .address = {} }, params.value[0].type.fixedArray.child.*); - try testing.expectEqual(ParamType{ .int = 120 }, params.value[1].type); - - try testing.expectEqualStrings("foo", params.value[0].name); - try testing.expectEqualStrings("bar", params.value[1].name); - - try testing.expectEqual(params.value.len, 2); -} - -test "AbiParameters with data location" { - const slice = "string calldata foo, int120 bar"; - - const params = try parseHumanReadable([]const param.AbiParameter, testing.allocator, slice); - defer params.deinit(); - - try testing.expectEqual(ParamType{ .string = {} }, params.value[0].type); - try testing.expectEqual(ParamType{ .int = 120 }, params.value[1].type); - - try testing.expectEqualStrings("foo", params.value[0].name); - try testing.expectEqualStrings("bar", params.value[1].name); - - try testing.expectEqual(params.value.len, 2); -} - -test "AbiParameters with tuple" { - const slice = "address foo, (bytes32 baz) bar"; - - const params = try parseHumanReadable([]const param.AbiParameter, testing.allocator, slice); - defer params.deinit(); - - try testing.expectEqual(ParamType{ .address = {} }, params.value[0].type); - try testing.expectEqual(ParamType{ .tuple = {} }, params.value[1].type); - - try testing.expectEqualStrings("foo", params.value[0].name); - try testing.expectEqualStrings("bar", params.value[1].name); - - try testing.expectEqual(params.value.len, 2); - - try testing.expect(params.value[1].components != null); - try testing.expectEqual(ParamType{ .fixedBytes = 32 }, params.value[1].components.?[0].type); - try testing.expectEqualStrings("baz", params.value[1].components.?[0].name); -} - -test "AbiParameters with nested tuple" { - const slice = "((bytes32 baz)[] fizz) bar"; - - const params = try parseHumanReadable([]const param.AbiParameter, testing.allocator, slice); - defer params.deinit(); - - try testing.expectEqual(ParamType{ .tuple = {} }, params.value[0].type); - try testing.expectEqualStrings("bar", params.value[0].name); - try testing.expectEqual(params.value.len, 1); - - try testing.expect(params.value[0].components != null); - try testing.expect(params.value[0].components.?[0].components != null); - try testing.expectEqual(ParamType{ .tuple = {} }, params.value[0].components.?[0].type.dynamicArray.*); - try testing.expectEqual(ParamType{ .fixedBytes = 32 }, params.value[0].components.?[0].components.?[0].type); - try testing.expectEqualStrings("fizz", params.value[0].components.?[0].name); - try testing.expectEqualStrings("baz", params.value[0].components.?[0].components.?[0].name); -} - -test "Receive signature" { - const slice = "receive() external payable"; - const signature = try parseHumanReadable(abi.Receive, testing.allocator, slice); - defer signature.deinit(); - - try testing.expectEqual(signature.value.type, .receive); - try testing.expectEqual(signature.value.stateMutability, .payable); -} - -test "Fallback signature" { - const slice = "fallback()"; - const signature = try parseHumanReadable(abi.Fallback, testing.allocator, slice); - defer signature.deinit(); - - try testing.expectEqual(signature.value.type, .fallback); - try testing.expectEqual(signature.value.stateMutability, .nonpayable); -} - -test "Fallback signature payable" { - const slice = "fallback() payable"; - const signature = try parseHumanReadable(abi.Fallback, testing.allocator, slice); - defer signature.deinit(); - - try testing.expectEqual(signature.value.type, .fallback); - try testing.expectEqual(signature.value.stateMutability, .payable); -} - -test "Constructor signature" { - const slice = "constructor(bool foo)"; - const signature = try parseHumanReadable(abi.Constructor, testing.allocator, slice); - defer signature.deinit(); - - try testing.expectEqual(signature.value.type, .constructor); - try testing.expectEqual(ParamType{ .bool = {} }, signature.value.inputs[0].type); - try testing.expectEqual(signature.value.stateMutability, .nonpayable); - try testing.expectEqualStrings("foo", signature.value.inputs[0].name); -} - -test "Constructor signature payable" { - const slice = "constructor(bool foo) payable"; - const signature = try parseHumanReadable(abi.Constructor, testing.allocator, slice); - defer signature.deinit(); - - try testing.expectEqual(signature.value.type, .constructor); - try testing.expectEqual(ParamType{ .bool = {} }, signature.value.inputs[0].type); - try testing.expectEqual(signature.value.stateMutability, .payable); - try testing.expectEqualStrings("foo", signature.value.inputs[0].name); -} - -test "Error signature" { - const slice = "error Foo(bytes foo)"; - const signature = try parseHumanReadable(abi.Error, testing.allocator, slice); - defer signature.deinit(); - - try testing.expectEqual(signature.value.type, .@"error"); - try testing.expectEqual(ParamType{ .bytes = {} }, signature.value.inputs[0].type); - try testing.expectEqualStrings("foo", signature.value.inputs[0].name); -} - -test "Event signature" { - const slice = "event Foo(bytes foo, address indexed bar)"; - const signature = try parseHumanReadable(abi.Event, testing.allocator, slice); - defer signature.deinit(); - - try testing.expectEqual(signature.value.type, .event); - try testing.expectEqual(ParamType{ .bytes = {} }, signature.value.inputs[0].type); - try testing.expectEqualStrings("foo", signature.value.inputs[0].name); - try testing.expect(!signature.value.inputs[0].indexed); - try testing.expectEqual(ParamType{ .address = {} }, signature.value.inputs[1].type); - try testing.expectEqualStrings("bar", signature.value.inputs[1].name); - try testing.expect(signature.value.inputs[1].indexed); -} - -test "Function signature" { - const slice = "function Foo(bytes foo, address bar)"; - const signature = try parseHumanReadable(abi.Function, testing.allocator, slice); - defer signature.deinit(); - - try testing.expectEqual(signature.value.type, .function); - try testing.expectEqual(ParamType{ .bytes = {} }, signature.value.inputs[0].type); - try testing.expectEqualStrings("foo", signature.value.inputs[0].name); - try testing.expectEqual(ParamType{ .address = {} }, signature.value.inputs[1].type); - try testing.expectEqualStrings("bar", signature.value.inputs[1].name); - try testing.expectEqual(signature.value.stateMutability, .nonpayable); - try testing.expectEqualSlices(param.AbiParameter, &.{}, signature.value.outputs); -} - -test "Function signature with state" { - const slice = "function Foo(bytes foo, address bar) external view"; - const signature = try parseHumanReadable(abi.Function, testing.allocator, slice); - defer signature.deinit(); - - try testing.expectEqual(signature.value.type, .function); - try testing.expectEqual(ParamType{ .bytes = {} }, signature.value.inputs[0].type); - try testing.expectEqualStrings("foo", signature.value.inputs[0].name); - try testing.expectEqual(ParamType{ .address = {} }, signature.value.inputs[1].type); - try testing.expectEqualStrings("bar", signature.value.inputs[1].name); - try testing.expectEqual(signature.value.stateMutability, .view); - try testing.expectEqualSlices(param.AbiParameter, &.{}, signature.value.outputs); -} - -test "Function signature with return" { - const slice = "function Foo(bytes foo, address bar) public pure returns (string baz)"; - const signature = try parseHumanReadable(abi.Function, testing.allocator, slice); - defer signature.deinit(); - - try testing.expectEqual(signature.value.type, .function); - try testing.expectEqual(ParamType{ .bytes = {} }, signature.value.inputs[0].type); - try testing.expectEqualStrings("foo", signature.value.inputs[0].name); - try testing.expectEqual(ParamType{ .address = {} }, signature.value.inputs[1].type); - try testing.expectEqualStrings("bar", signature.value.inputs[1].name); - try testing.expectEqual(signature.value.stateMutability, .pure); - try testing.expectEqual(ParamType{ .string = {} }, signature.value.outputs[0].type); - try testing.expectEqualStrings("baz", signature.value.outputs[0].name); -} - -test "AbiItem" { - const slice = "function Foo(bytes foo, address bar) public pure returns (string baz)"; - const signature = try parseHumanReadable(abi.AbiItem, testing.allocator, slice); - defer signature.deinit(); - - const function = signature.value.abiFunction; - try testing.expectEqual(function.type, .function); - try testing.expectEqual(ParamType{ .bytes = {} }, function.inputs[0].type); - try testing.expectEqualStrings("foo", function.inputs[0].name); - try testing.expectEqual(ParamType{ .address = {} }, function.inputs[1].type); - try testing.expectEqualStrings("bar", function.inputs[1].name); - try testing.expectEqual(function.stateMutability, .pure); - try testing.expectEqual(ParamType{ .string = {} }, function.outputs[0].type); - try testing.expectEqualStrings("baz", function.outputs[0].name); -} - -test "Abi" { - const slice = "function Foo(bytes foo, address bar) public pure returns (string baz)"; - const signature = try parseHumanReadable(abi.Abi, testing.allocator, slice); - defer signature.deinit(); - - const function = signature.value[0].abiFunction; - try testing.expectEqual(function.type, .function); - try testing.expectEqual(ParamType{ .bytes = {} }, function.inputs[0].type); - try testing.expectEqualStrings("foo", function.inputs[0].name); - try testing.expectEqual(ParamType{ .address = {} }, function.inputs[1].type); - try testing.expectEqualStrings("bar", function.inputs[1].name); - try testing.expectEqual(function.stateMutability, .pure); - try testing.expectEqual(ParamType{ .string = {} }, function.outputs[0].type); - try testing.expectEqualStrings("baz", function.outputs[0].name); -} - -test "Abi with struct" { - const slice = - \\struct Foo {address bar; string baz;} - \\function Fizz(Foo buzz) public pure returns (string baz) - ; - - const signature = try parseHumanReadable(abi.Abi, testing.allocator, slice); - defer signature.deinit(); - - const function = signature.value[0].abiFunction; - try testing.expectEqual(function.type, .function); - try testing.expectEqualStrings("Fizz", function.name); - try testing.expectEqual(ParamType{ .tuple = {} }, function.inputs[0].type); - try testing.expectEqualStrings("buzz", function.inputs[0].name); - try testing.expectEqual(ParamType{ .address = {} }, function.inputs[0].components.?[0].type); - try testing.expectEqual(ParamType{ .string = {} }, function.inputs[0].components.?[1].type); - try testing.expectEqualStrings("bar", function.inputs[0].components.?[0].name); - try testing.expectEqualStrings("baz", function.inputs[0].components.?[1].name); - try testing.expectEqual(function.stateMutability, .pure); - try testing.expectEqual(ParamType{ .string = {} }, function.outputs[0].type); - try testing.expectEqualStrings("baz", function.outputs[0].name); -} - -test "Abi with nested struct" { - const slice = - \\struct Foo {address bar; string baz;} - \\struct Bar {Foo foo;} - \\function Fizz(Bar bar) public pure returns (Foo foo) - ; - - const signature = try parseHumanReadable(abi.Abi, testing.allocator, slice); - defer signature.deinit(); - - const function = signature.value[0].abiFunction; - try testing.expectEqual(function.type, .function); - try testing.expectEqualStrings("Fizz", function.name); - try testing.expectEqual(ParamType{ .tuple = {} }, function.inputs[0].type); - try testing.expectEqualStrings("bar", function.inputs[0].name); - try testing.expectEqual(ParamType{ .tuple = {} }, function.inputs[0].components.?[0].type); - try testing.expectEqualStrings("foo", function.inputs[0].components.?[0].name); - try testing.expectEqual(ParamType{ .address = {} }, function.inputs[0].components.?[0].components.?[0].type); - try testing.expectEqual(ParamType{ .string = {} }, function.inputs[0].components.?[0].components.?[1].type); - try testing.expectEqualStrings("bar", function.inputs[0].components.?[0].components.?[0].name); - try testing.expectEqualStrings("baz", function.inputs[0].components.?[0].components.?[1].name); - try testing.expectEqual(function.stateMutability, .pure); - try testing.expectEqual(ParamType{ .address = {} }, function.outputs[0].components.?[0].type); - try testing.expectEqual(ParamType{ .string = {} }, function.outputs[0].components.?[1].type); - try testing.expectEqualStrings("bar", function.outputs[0].components.?[0].name); - try testing.expectEqualStrings("baz", function.outputs[0].components.?[1].name); -} +// test "AbiParameter" { +// const slice = "address foo"; +// +// const params = try parseHumanReadable([]const param.AbiParameter, testing.allocator, slice); +// defer params.deinit(); +// +// for (params.value) |val| { +// try testing.expectEqual(val.type, ParamType{ .address = {} }); +// try testing.expectEqualStrings(val.name, "foo"); +// } +// } +// +// test "AbiParameters" { +// const slice = "address foo, int120 bar"; +// +// const params = try parseHumanReadable([]const param.AbiParameter, testing.allocator, slice); +// defer params.deinit(); +// +// try testing.expectEqual(ParamType{ .address = {} }, params.value[0].type); +// try testing.expectEqual(ParamType{ .int = 120 }, params.value[1].type); +// +// try testing.expectEqualStrings("foo", params.value[0].name); +// try testing.expectEqualStrings("bar", params.value[1].name); +// +// try testing.expectEqual(params.value.len, 2); +// } +// +// test "AbiParameters dynamic array" { +// const slice = "address[] foo, int120 bar"; +// +// const params = try parseHumanReadable([]const param.AbiParameter, testing.allocator, slice); +// defer params.deinit(); +// +// try testing.expectEqual(ParamType{ .address = {} }, params.value[0].type.dynamicArray.*); +// try testing.expectEqual(ParamType{ .int = 120 }, params.value[1].type); +// +// try testing.expectEqualStrings("foo", params.value[0].name); +// try testing.expectEqualStrings("bar", params.value[1].name); +// +// try testing.expectEqual(params.value.len, 2); +// } +// +// test "AbiParameters 2d dynamic array" { +// const slice = "address[][] foo, int120 bar"; +// +// const params = try parseHumanReadable([]const param.AbiParameter, testing.allocator, slice); +// defer params.deinit(); +// +// try testing.expectEqual(ParamType{ .address = {} }, params.value[0].type.dynamicArray.dynamicArray.*); +// try testing.expectEqual(ParamType{ .int = 120 }, params.value[1].type); +// +// try testing.expectEqualStrings("foo", params.value[0].name); +// try testing.expectEqualStrings("bar", params.value[1].name); +// +// try testing.expectEqual(params.value.len, 2); +// } +// +// test "AbiParameters mixed 2d array" { +// const slice = "address[5][] foo, int120 bar"; +// +// const params = try parseHumanReadable([]const param.AbiParameter, testing.allocator, slice); +// defer params.deinit(); +// +// try testing.expectEqual(ParamType{ .address = {} }, params.value[0].type.dynamicArray.fixedArray.child.*); +// try testing.expectEqual(ParamType{ .int = 120 }, params.value[1].type); +// +// try testing.expectEqualStrings("foo", params.value[0].name); +// try testing.expectEqualStrings("bar", params.value[1].name); +// +// try testing.expectEqual(params.value.len, 2); +// } +// +// test "AbiParameters with fixed array" { +// const slice = "address[5] foo, int120 bar"; +// +// const params = try parseHumanReadable([]const param.AbiParameter, testing.allocator, slice); +// defer params.deinit(); +// +// try testing.expectEqual(ParamType{ .address = {} }, params.value[0].type.fixedArray.child.*); +// try testing.expectEqual(ParamType{ .int = 120 }, params.value[1].type); +// +// try testing.expectEqualStrings("foo", params.value[0].name); +// try testing.expectEqualStrings("bar", params.value[1].name); +// +// try testing.expectEqual(params.value.len, 2); +// } +// +// test "AbiParameters with data location" { +// const slice = "string calldata foo, int120 bar"; +// +// const params = try parseHumanReadable([]const param.AbiParameter, testing.allocator, slice); +// defer params.deinit(); +// +// try testing.expectEqual(ParamType{ .string = {} }, params.value[0].type); +// try testing.expectEqual(ParamType{ .int = 120 }, params.value[1].type); +// +// try testing.expectEqualStrings("foo", params.value[0].name); +// try testing.expectEqualStrings("bar", params.value[1].name); +// +// try testing.expectEqual(params.value.len, 2); +// } +// +// test "AbiParameters with tuple" { +// const slice = "address foo, (bytes32 baz) bar"; +// +// const params = try parseHumanReadable([]const param.AbiParameter, testing.allocator, slice); +// defer params.deinit(); +// +// try testing.expectEqual(ParamType{ .address = {} }, params.value[0].type); +// try testing.expectEqual(ParamType{ .tuple = {} }, params.value[1].type); +// +// try testing.expectEqualStrings("foo", params.value[0].name); +// try testing.expectEqualStrings("bar", params.value[1].name); +// +// try testing.expectEqual(params.value.len, 2); +// +// try testing.expect(params.value[1].components != null); +// try testing.expectEqual(ParamType{ .fixedBytes = 32 }, params.value[1].components.?[0].type); +// try testing.expectEqualStrings("baz", params.value[1].components.?[0].name); +// } +// +// test "AbiParameters with nested tuple" { +// const slice = "((bytes32 baz)[] fizz) bar"; +// +// const params = try parseHumanReadable([]const param.AbiParameter, testing.allocator, slice); +// defer params.deinit(); +// +// try testing.expectEqual(ParamType{ .tuple = {} }, params.value[0].type); +// try testing.expectEqualStrings("bar", params.value[0].name); +// try testing.expectEqual(params.value.len, 1); +// +// try testing.expect(params.value[0].components != null); +// try testing.expect(params.value[0].components.?[0].components != null); +// try testing.expectEqual(ParamType{ .tuple = {} }, params.value[0].components.?[0].type.dynamicArray.*); +// try testing.expectEqual(ParamType{ .fixedBytes = 32 }, params.value[0].components.?[0].components.?[0].type); +// try testing.expectEqualStrings("fizz", params.value[0].components.?[0].name); +// try testing.expectEqualStrings("baz", params.value[0].components.?[0].components.?[0].name); +// } +// +// test "Receive signature" { +// const slice = "receive() external payable"; +// const signature = try parseHumanReadable(abi.Receive, testing.allocator, slice); +// defer signature.deinit(); +// +// try testing.expectEqual(signature.value.type, .receive); +// try testing.expectEqual(signature.value.stateMutability, .payable); +// } +// +// test "Fallback signature" { +// const slice = "fallback()"; +// const signature = try parseHumanReadable(abi.Fallback, testing.allocator, slice); +// defer signature.deinit(); +// +// try testing.expectEqual(signature.value.type, .fallback); +// try testing.expectEqual(signature.value.stateMutability, .nonpayable); +// } +// +// test "Fallback signature payable" { +// const slice = "fallback() payable"; +// const signature = try parseHumanReadable(abi.Fallback, testing.allocator, slice); +// defer signature.deinit(); +// +// try testing.expectEqual(signature.value.type, .fallback); +// try testing.expectEqual(signature.value.stateMutability, .payable); +// } +// +// test "Constructor signature" { +// const slice = "constructor(bool foo)"; +// const signature = try parseHumanReadable(abi.Constructor, testing.allocator, slice); +// defer signature.deinit(); +// +// try testing.expectEqual(signature.value.type, .constructor); +// try testing.expectEqual(ParamType{ .bool = {} }, signature.value.inputs[0].type); +// try testing.expectEqual(signature.value.stateMutability, .nonpayable); +// try testing.expectEqualStrings("foo", signature.value.inputs[0].name); +// } +// +// test "Constructor signature payable" { +// const slice = "constructor(bool foo) payable"; +// const signature = try parseHumanReadable(abi.Constructor, testing.allocator, slice); +// defer signature.deinit(); +// +// try testing.expectEqual(signature.value.type, .constructor); +// try testing.expectEqual(ParamType{ .bool = {} }, signature.value.inputs[0].type); +// try testing.expectEqual(signature.value.stateMutability, .payable); +// try testing.expectEqualStrings("foo", signature.value.inputs[0].name); +// } +// +// test "Error signature" { +// const slice = "error Foo(bytes foo)"; +// const signature = try parseHumanReadable(abi.Error, testing.allocator, slice); +// defer signature.deinit(); +// +// try testing.expectEqual(signature.value.type, .@"error"); +// try testing.expectEqual(ParamType{ .bytes = {} }, signature.value.inputs[0].type); +// try testing.expectEqualStrings("foo", signature.value.inputs[0].name); +// } +// +// test "Event signature" { +// const slice = "event Foo(bytes foo, address indexed bar)"; +// const signature = try parseHumanReadable(abi.Event, testing.allocator, slice); +// defer signature.deinit(); +// +// try testing.expectEqual(signature.value.type, .event); +// try testing.expectEqual(ParamType{ .bytes = {} }, signature.value.inputs[0].type); +// try testing.expectEqualStrings("foo", signature.value.inputs[0].name); +// try testing.expect(!signature.value.inputs[0].indexed); +// try testing.expectEqual(ParamType{ .address = {} }, signature.value.inputs[1].type); +// try testing.expectEqualStrings("bar", signature.value.inputs[1].name); +// try testing.expect(signature.value.inputs[1].indexed); +// } +// +// test "Function signature" { +// const slice = "function Foo(bytes foo, address bar)"; +// const signature = try parseHumanReadable(abi.Function, testing.allocator, slice); +// defer signature.deinit(); +// +// try testing.expectEqual(signature.value.type, .function); +// try testing.expectEqual(ParamType{ .bytes = {} }, signature.value.inputs[0].type); +// try testing.expectEqualStrings("foo", signature.value.inputs[0].name); +// try testing.expectEqual(ParamType{ .address = {} }, signature.value.inputs[1].type); +// try testing.expectEqualStrings("bar", signature.value.inputs[1].name); +// try testing.expectEqual(signature.value.stateMutability, .nonpayable); +// try testing.expectEqualSlices(param.AbiParameter, &.{}, signature.value.outputs); +// } +// +// test "Function signature with state" { +// const slice = "function Foo(bytes foo, address bar) external view"; +// const signature = try parseHumanReadable(abi.Function, testing.allocator, slice); +// defer signature.deinit(); +// +// try testing.expectEqual(signature.value.type, .function); +// try testing.expectEqual(ParamType{ .bytes = {} }, signature.value.inputs[0].type); +// try testing.expectEqualStrings("foo", signature.value.inputs[0].name); +// try testing.expectEqual(ParamType{ .address = {} }, signature.value.inputs[1].type); +// try testing.expectEqualStrings("bar", signature.value.inputs[1].name); +// try testing.expectEqual(signature.value.stateMutability, .view); +// try testing.expectEqualSlices(param.AbiParameter, &.{}, signature.value.outputs); +// } +// +// test "Function signature with return" { +// const slice = "function Foo(bytes foo, address bar) public pure returns (string baz)"; +// const signature = try parseHumanReadable(abi.Function, testing.allocator, slice); +// defer signature.deinit(); +// +// try testing.expectEqual(signature.value.type, .function); +// try testing.expectEqual(ParamType{ .bytes = {} }, signature.value.inputs[0].type); +// try testing.expectEqualStrings("foo", signature.value.inputs[0].name); +// try testing.expectEqual(ParamType{ .address = {} }, signature.value.inputs[1].type); +// try testing.expectEqualStrings("bar", signature.value.inputs[1].name); +// try testing.expectEqual(signature.value.stateMutability, .pure); +// try testing.expectEqual(ParamType{ .string = {} }, signature.value.outputs[0].type); +// try testing.expectEqualStrings("baz", signature.value.outputs[0].name); +// } +// +// test "AbiItem" { +// const slice = "function Foo(bytes foo, address bar) public pure returns (string baz)"; +// const signature = try parseHumanReadable(abi.AbiItem, testing.allocator, slice); +// defer signature.deinit(); +// +// const function = signature.value.abiFunction; +// try testing.expectEqual(function.type, .function); +// try testing.expectEqual(ParamType{ .bytes = {} }, function.inputs[0].type); +// try testing.expectEqualStrings("foo", function.inputs[0].name); +// try testing.expectEqual(ParamType{ .address = {} }, function.inputs[1].type); +// try testing.expectEqualStrings("bar", function.inputs[1].name); +// try testing.expectEqual(function.stateMutability, .pure); +// try testing.expectEqual(ParamType{ .string = {} }, function.outputs[0].type); +// try testing.expectEqualStrings("baz", function.outputs[0].name); +// } +// +// test "Abi" { +// const slice = "function Foo(bytes foo, address bar) public pure returns (string baz)"; +// const signature = try parseHumanReadable(abi.Abi, testing.allocator, slice); +// defer signature.deinit(); +// +// const function = signature.value[0].abiFunction; +// try testing.expectEqual(function.type, .function); +// try testing.expectEqual(ParamType{ .bytes = {} }, function.inputs[0].type); +// try testing.expectEqualStrings("foo", function.inputs[0].name); +// try testing.expectEqual(ParamType{ .address = {} }, function.inputs[1].type); +// try testing.expectEqualStrings("bar", function.inputs[1].name); +// try testing.expectEqual(function.stateMutability, .pure); +// try testing.expectEqual(ParamType{ .string = {} }, function.outputs[0].type); +// try testing.expectEqualStrings("baz", function.outputs[0].name); +// } +// +// test "Abi with struct" { +// const slice = +// \\struct Foo {address bar; string baz;} +// \\function Fizz(Foo buzz) public pure returns (string baz) +// ; +// +// const signature = try parseHumanReadable(abi.Abi, testing.allocator, slice); +// defer signature.deinit(); +// +// const function = signature.value[0].abiFunction; +// try testing.expectEqual(function.type, .function); +// try testing.expectEqualStrings("Fizz", function.name); +// try testing.expectEqual(ParamType{ .tuple = {} }, function.inputs[0].type); +// try testing.expectEqualStrings("buzz", function.inputs[0].name); +// try testing.expectEqual(ParamType{ .address = {} }, function.inputs[0].components.?[0].type); +// try testing.expectEqual(ParamType{ .string = {} }, function.inputs[0].components.?[1].type); +// try testing.expectEqualStrings("bar", function.inputs[0].components.?[0].name); +// try testing.expectEqualStrings("baz", function.inputs[0].components.?[1].name); +// try testing.expectEqual(function.stateMutability, .pure); +// try testing.expectEqual(ParamType{ .string = {} }, function.outputs[0].type); +// try testing.expectEqualStrings("baz", function.outputs[0].name); +// } +// +// test "Abi with nested struct" { +// const slice = +// \\struct Foo {address bar; string baz;} +// \\struct Bar {Foo foo;} +// \\function Fizz(Bar bar) public pure returns (Foo foo) +// ; +// +// const signature = try parseHumanReadable(abi.Abi, testing.allocator, slice); +// defer signature.deinit(); +// +// const function = signature.value[0].abiFunction; +// try testing.expectEqual(function.type, .function); +// try testing.expectEqualStrings("Fizz", function.name); +// try testing.expectEqual(ParamType{ .tuple = {} }, function.inputs[0].type); +// try testing.expectEqualStrings("bar", function.inputs[0].name); +// try testing.expectEqual(ParamType{ .tuple = {} }, function.inputs[0].components.?[0].type); +// try testing.expectEqualStrings("foo", function.inputs[0].components.?[0].name); +// try testing.expectEqual(ParamType{ .address = {} }, function.inputs[0].components.?[0].components.?[0].type); +// try testing.expectEqual(ParamType{ .string = {} }, function.inputs[0].components.?[0].components.?[1].type); +// try testing.expectEqualStrings("bar", function.inputs[0].components.?[0].components.?[0].name); +// try testing.expectEqualStrings("baz", function.inputs[0].components.?[0].components.?[1].name); +// try testing.expectEqual(function.stateMutability, .pure); +// try testing.expectEqual(ParamType{ .address = {} }, function.outputs[0].components.?[0].type); +// try testing.expectEqual(ParamType{ .string = {} }, function.outputs[0].components.?[1].type); +// try testing.expectEqualStrings("bar", function.outputs[0].components.?[0].name); +// try testing.expectEqualStrings("baz", function.outputs[0].components.?[1].name); +// } test "Seaport" { const slice = @@ -433,7 +433,7 @@ test "Seaport" { \\error UnusedItemParameters() ; - const parsed = try parseHumanReadable(abi.Abi, testing.allocator, slice); + const parsed = try parseHumanReadable(testing.allocator, slice); defer parsed.deinit(); try testing.expectEqual(parsed.value.len, 68); @@ -444,55 +444,55 @@ test "Seaport" { try testing.expectEqualStrings("UnusedItemParameters", last.name); } -test "Parsing errors parameters" { - try testing.expectError(error.UnexceptedToken, parseHumanReadable([]const param.AbiParameter, testing.allocator, "adddress foo")); - try testing.expectError(error.UnexceptedToken, parseHumanReadable([]const param.AbiParameter, testing.allocator, "address foo,")); - try testing.expectError(error.InvalidDataLocation, parseHumanReadable([]const param.AbiParameter, testing.allocator, "(address calldata foo)")); - try testing.expectError(error.InvalidDataLocation, parseHumanReadable([]const param.AbiParameter, testing.allocator, "address indexed foo")); - try testing.expectError(error.InvalidDataLocation, parseHumanReadable([]const param.AbiParameter, testing.allocator, "address calldata foo")); - try testing.expectError(error.InvalidDataLocation, parseHumanReadable([]const param.AbiParameter, testing.allocator, "address storage foo")); - try testing.expectError(error.InvalidDataLocation, parseHumanReadable([]const param.AbiParameter, testing.allocator, "address memory foo")); - try testing.expectError(error.InvalidDataLocation, parseHumanReadable([]const param.AbiEventParameter, testing.allocator, "address[] storage foo")); - try testing.expectError(error.ExpectedCommaAfterParam, parseHumanReadable([]const param.AbiParameter, testing.allocator, "address foo.")); - try testing.expectError(error.UnexceptedToken, parseHumanReadable([]const param.AbiParameter, testing.allocator, "(((address))")); -} - -test "Parsing errors signatures" { - try testing.expectError(error.UnexceptedToken, parseHumanReadable(abi.Constructor, testing.allocator, "function foo()")); - try testing.expectError(error.UnexceptedToken, parseHumanReadable(abi.Abi, testing.allocator, "function foo(address)) view")); - try testing.expectError(error.UnexceptedToken, parseHumanReadable(abi.Abi, testing.allocator, "function foo(address) nonpayable")); - try testing.expectError(error.InvalidDataLocation, parseHumanReadable(abi.Abi, testing.allocator, "function foo(((((address indexed foo))))) view return(bool)")); - try testing.expectError(error.EmptyReturnParams, parseHumanReadable(abi.Abi, testing.allocator, "function foo(((((address foo))))) view returns()")); -} - -test "Match snapshot" { - const expected = - \\{ - \\ "type": "function", - \\ "inputs": [ - \\ { - \\ "name": "bar", - \\ "type": "address[]" - \\ } - \\ ], - \\ "name": "foo", - \\ "outputs": [], - \\ "stateMutability": "nonpayable" - \\} - ; - - try testSnapshot(abi.Function, expected, "function foo(address[] bar)"); -} - -fn testSnapshot(comptime T: type, expected: []const u8, source: [:0]const u8) !void { - const value = try parseHumanReadable(T, testing.allocator, source); - defer value.deinit(); - - var out_buf: [1024]u8 = undefined; - var slice_stream = std.io.fixedBufferStream(&out_buf); - const out = slice_stream.writer(); - - try std.json.stringify(value.value, .{ .whitespace = .indent_2, .emit_null_optional_fields = false }, out); - - try testing.expectEqualStrings(expected, slice_stream.getWritten()); -} +// test "Parsing errors parameters" { +// try testing.expectError(error.UnexceptedToken, parseHumanReadable([]const param.AbiParameter, testing.allocator, "adddress foo")); +// try testing.expectError(error.UnexceptedToken, parseHumanReadable([]const param.AbiParameter, testing.allocator, "address foo,")); +// try testing.expectError(error.InvalidDataLocation, parseHumanReadable([]const param.AbiParameter, testing.allocator, "(address calldata foo)")); +// try testing.expectError(error.InvalidDataLocation, parseHumanReadable([]const param.AbiParameter, testing.allocator, "address indexed foo")); +// try testing.expectError(error.InvalidDataLocation, parseHumanReadable([]const param.AbiParameter, testing.allocator, "address calldata foo")); +// try testing.expectError(error.InvalidDataLocation, parseHumanReadable([]const param.AbiParameter, testing.allocator, "address storage foo")); +// try testing.expectError(error.InvalidDataLocation, parseHumanReadable([]const param.AbiParameter, testing.allocator, "address memory foo")); +// try testing.expectError(error.InvalidDataLocation, parseHumanReadable([]const param.AbiEventParameter, testing.allocator, "address[] storage foo")); +// try testing.expectError(error.ExpectedCommaAfterParam, parseHumanReadable([]const param.AbiParameter, testing.allocator, "address foo.")); +// try testing.expectError(error.UnexceptedToken, parseHumanReadable([]const param.AbiParameter, testing.allocator, "(((address))")); +// } +// +// test "Parsing errors signatures" { +// try testing.expectError(error.UnexceptedToken, parseHumanReadable(abi.Constructor, testing.allocator, "function foo()")); +// try testing.expectError(error.UnexceptedToken, parseHumanReadable(abi.Abi, testing.allocator, "function foo(address)) view")); +// try testing.expectError(error.UnexceptedToken, parseHumanReadable(abi.Abi, testing.allocator, "function foo(address) nonpayable")); +// try testing.expectError(error.InvalidDataLocation, parseHumanReadable(abi.Abi, testing.allocator, "function foo(((((address indexed foo))))) view return(bool)")); +// try testing.expectError(error.EmptyReturnParams, parseHumanReadable(abi.Abi, testing.allocator, "function foo(((((address foo))))) view returns()")); +// } +// +// test "Match snapshot" { +// const expected = +// \\{ +// \\ "type": "function", +// \\ "inputs": [ +// \\ { +// \\ "name": "bar", +// \\ "type": "address[]" +// \\ } +// \\ ], +// \\ "name": "foo", +// \\ "outputs": [], +// \\ "stateMutability": "nonpayable" +// \\} +// ; +// +// try testSnapshot(abi.Function, expected, "function foo(address[] bar)"); +// } +// +// fn testSnapshot(comptime T: type, expected: []const u8, source: [:0]const u8) !void { +// const value = try parseHumanReadable(T, testing.allocator, source); +// defer value.deinit(); +// +// var out_buf: [1024]u8 = undefined; +// var slice_stream = std.io.fixedBufferStream(&out_buf); +// const out = slice_stream.writer(); +// +// try std.json.stringify(value.value, .{ .whitespace = .indent_2, .emit_null_optional_fields = false }, out); +// +// try testing.expectEqualStrings(expected, slice_stream.getWritten()); +// } diff --git a/src/tests/human-readable/parser_new.test.zig b/src/tests/human-readable/parser_new.test.zig index 2ee4fbbb..4c4a9e0f 100644 --- a/src/tests/human-readable/parser_new.test.zig +++ b/src/tests/human-readable/parser_new.test.zig @@ -10,7 +10,7 @@ test "Human readable" { var arena = std.heap.ArenaAllocator.init(testing.allocator); defer arena.deinit(); - const abi = try HumanAbi.parse(arena.allocator(), "function foo(uint bar) payable returns(string bar)"); + const abi = try HumanAbi.parse(arena.allocator(), "struct Bar{uint bazz;}\nstruct Foo{uint baz; Bar jazz;}\nfunction foo(Foo[69][] bar)"); // defer ast.deinit(testing.allocator); // const abi_gen: HumanAbi = .{ diff --git a/src/tests/root.zig b/src/tests/root.zig index dd2ac9d7..c18a80f6 100644 --- a/src/tests/root.zig +++ b/src/tests/root.zig @@ -7,7 +7,7 @@ test { // _ = @import("decoding/root.zig"); // _ = @import("encoding/root.zig"); // _ = @import("evm/root.zig"); - // _ = @import("human-readable/root.zig"); + _ = @import("human-readable/root.zig"); _ = @import("human-readable/parser_new.test.zig"); // _ = @import("meta/root.zig"); // _ = @import("utils/root.zig"); From c6ee8be55e908a7763ae483e539cbddacefea452 Mon Sep 17 00:00:00 2001 From: Raiden1411 <67233402+Raiden1411@users.noreply.github.com> Date: Sun, 6 Oct 2024 13:05:18 +0100 Subject: [PATCH 14/18] human: start cleaning up implementation and some optimizations --- src/abi/param_type.zig | 151 +++++ src/human-readable/HumanAbi.zig | 31 +- src/human-readable/Parser.zig | 58 +- src/human-readable/ParserOld.zig | 632 ------------------ src/human-readable/abi_parsing.zig | 16 - src/tests/human-readable/abi_parsing.test.zig | 391 ----------- 6 files changed, 212 insertions(+), 1067 deletions(-) delete mode 100644 src/human-readable/ParserOld.zig diff --git a/src/abi/param_type.zig b/src/abi/param_type.zig index 4d5c2687..4237da62 100644 --- a/src/abi/param_type.zig +++ b/src/abi/param_type.zig @@ -1,5 +1,6 @@ const std = @import("std"); const testing = std.testing; +const tokens = @import("../human-readable/tokens.zig"); // Types const Allocator = std.mem.Allocator; @@ -8,6 +9,7 @@ const ParseFromValueError = std.json.ParseFromValueError; const ParserOptions = std.json.ParseOptions; const Scanner = std.json.Scanner; const Token = std.json.Token; +const TokenTags = tokens.Tag.SoliditySyntax; /// Set of errors when converting `[]const u8` into `ParamType`. pub const ParamErrors = error{ InvalidEnumTag, InvalidCharacter, LengthMismatch, Overflow } || Allocator.Error; @@ -32,6 +34,120 @@ pub const ParamType = union(enum) { fixedArray: FixedArray, dynamicArray: *const ParamType, + /// Converts a human readable token into `ParamType`. + pub fn fromHumanReadableTokenTag(tag: TokenTags) ?ParamType { + return switch (tag) { + .Address => .{ .address = {} }, + .Bool => .{ .bool = {} }, + .Tuple => .{ .tuple = {} }, + .String => .{ .string = {} }, + .Bytes => .{ .bytes = {} }, + + .Bytes1 => .{ .fixedBytes = 1 }, + .Bytes2 => .{ .fixedBytes = 2 }, + .Bytes3 => .{ .fixedBytes = 3 }, + .Bytes4 => .{ .fixedBytes = 4 }, + .Bytes5 => .{ .fixedBytes = 5 }, + .Bytes6 => .{ .fixedBytes = 6 }, + .Bytes7 => .{ .fixedBytes = 7 }, + .Bytes8 => .{ .fixedBytes = 8 }, + .Bytes9 => .{ .fixedBytes = 9 }, + .Bytes10 => .{ .fixedBytes = 10 }, + .Bytes11 => .{ .fixedBytes = 11 }, + + .Bytes12 => .{ .fixedBytes = 12 }, + .Bytes13 => .{ .fixedBytes = 13 }, + .Bytes14 => .{ .fixedBytes = 14 }, + .Bytes15 => .{ .fixedBytes = 15 }, + .Bytes16 => .{ .fixedBytes = 16 }, + .Bytes17 => .{ .fixedBytes = 17 }, + .Bytes18 => .{ .fixedBytes = 18 }, + .Bytes19 => .{ .fixedBytes = 19 }, + .Bytes20 => .{ .fixedBytes = 20 }, + .Bytes21 => .{ .fixedBytes = 21 }, + .Bytes22 => .{ .fixedBytes = 22 }, + .Bytes23 => .{ .fixedBytes = 23 }, + .Bytes24 => .{ .fixedBytes = 24 }, + .Bytes25 => .{ .fixedBytes = 25 }, + .Bytes26 => .{ .fixedBytes = 26 }, + .Bytes27 => .{ .fixedBytes = 27 }, + .Bytes28 => .{ .fixedBytes = 28 }, + .Bytes29 => .{ .fixedBytes = 29 }, + .Bytes30 => .{ .fixedBytes = 30 }, + .Bytes31 => .{ .fixedBytes = 31 }, + .Bytes32 => .{ .fixedBytes = 32 }, + + .Uint => .{ .uint = 256 }, + .Uint8 => .{ .uint = 8 }, + .Uint16 => .{ .uint = 16 }, + .Uint24 => .{ .uint = 24 }, + .Uint32 => .{ .uint = 32 }, + .Uint40 => .{ .uint = 40 }, + .Uint48 => .{ .uint = 48 }, + .Uint56 => .{ .uint = 56 }, + .Uint64 => .{ .uint = 64 }, + .Uint72 => .{ .uint = 72 }, + .Uint80 => .{ .uint = 80 }, + .Uint88 => .{ .uint = 88 }, + .Uint96 => .{ .uint = 96 }, + .Uint104 => .{ .uint = 104 }, + .Uint112 => .{ .uint = 112 }, + .Uint120 => .{ .uint = 120 }, + .Uint128 => .{ .uint = 128 }, + .Uint136 => .{ .uint = 136 }, + .Uint144 => .{ .uint = 144 }, + .Uint152 => .{ .uint = 152 }, + .Uint160 => .{ .uint = 160 }, + .Uint168 => .{ .uint = 168 }, + .Uint176 => .{ .uint = 176 }, + .Uint184 => .{ .uint = 184 }, + .Uint192 => .{ .uint = 192 }, + .Uint200 => .{ .uint = 200 }, + .Uint208 => .{ .uint = 208 }, + .Uint216 => .{ .uint = 216 }, + .Uint224 => .{ .uint = 224 }, + .Uint232 => .{ .uint = 232 }, + .Uint240 => .{ .uint = 240 }, + .Uint248 => .{ .uint = 248 }, + .Uint256 => .{ .uint = 256 }, + + .Int => .{ .int = 256 }, + .Int8 => .{ .int = 8 }, + .Int16 => .{ .int = 16 }, + .Int24 => .{ .int = 24 }, + .Int32 => .{ .int = 32 }, + .Int40 => .{ .int = 40 }, + .Int48 => .{ .int = 48 }, + .Int56 => .{ .int = 56 }, + .Int64 => .{ .int = 64 }, + .Int72 => .{ .int = 72 }, + .Int80 => .{ .int = 80 }, + .Int88 => .{ .int = 88 }, + .Int96 => .{ .int = 96 }, + .Int104 => .{ .int = 104 }, + .Int112 => .{ .int = 112 }, + .Int120 => .{ .int = 120 }, + .Int128 => .{ .int = 128 }, + .Int136 => .{ .int = 136 }, + .Int144 => .{ .int = 144 }, + .Int152 => .{ .int = 152 }, + .Int160 => .{ .int = 160 }, + .Int168 => .{ .int = 168 }, + .Int176 => .{ .int = 176 }, + .Int184 => .{ .int = 184 }, + .Int192 => .{ .int = 192 }, + .Int200 => .{ .int = 200 }, + .Int208 => .{ .int = 208 }, + .Int216 => .{ .int = 216 }, + .Int224 => .{ .int = 224 }, + .Int232 => .{ .int = 232 }, + .Int240 => .{ .int = 240 }, + .Int248 => .{ .int = 248 }, + .Int256 => .{ .int = 256 }, + inline else => null, + }; + } + /// User must call this if the union type contains a fixedArray or dynamicArray field. /// They create pointers so they must be destroyed after. pub fn freeArrayParamType(self: @This(), alloc: Allocator) void { @@ -203,4 +319,39 @@ pub const ParamType = union(enum) { return error.InvalidEnumTag; } + + pub fn typeToUnionWithTag(allocator: Allocator, abitype: []const u8, token_tag: TokenTags) ParamErrors!ParamType { + if (abitype.len == 0) return error.InvalidEnumTag; + + if (abitype[abitype.len - 1] == ']') { + const end = abitype.len - 1; + for (2..abitype.len) |i| { + const start = abitype.len - i; + if (abitype[start] == '[') { + const inside = abitype[start + 1 .. end]; + const child = try allocator.create(ParamType); + errdefer allocator.destroy(child); + + child.* = try typeToUnionWithTag(allocator, abitype[0..start], token_tag); + + if (inside.len == 0) { + return .{ + .dynamicArray = child, + }; + } else { + return .{ .fixedArray = .{ + .size = try std.fmt.parseInt(usize, inside, 10), + .child = child, + } }; + } + } + } + + return error.InvalidCharacter; + } + + const from_tag = fromHumanReadableTokenTag(token_tag) orelse return error.InvalidEnumTag; + + return from_tag; + } }; diff --git a/src/human-readable/HumanAbi.zig b/src/human-readable/HumanAbi.zig index 35bb8bd1..d280d77b 100644 --- a/src/human-readable/HumanAbi.zig +++ b/src/human-readable/HumanAbi.zig @@ -402,6 +402,8 @@ pub fn toAbiParameter(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiParame const data = self.ast.nodes.items(.data); const main = self.ast.nodes.items(.main_token); const starts = self.ast.tokens.items(.start); + const token_tags = self.ast.tokens.items(.tag); + std.debug.assert(nodes[node] == .var_decl); switch (nodes[data[node].lhs]) { @@ -418,7 +420,7 @@ pub fn toAbiParameter(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiParame var buffer: [256]u8 = undefined; const slice = try std.fmt.bufPrint(&buffer, "tuple{s}", .{self.ast.source[open_bracket..closing_bracket]}); - const param_type = try ParamType.typeToUnion(slice, self.allocator); + const param_type = try ParamType.typeToUnionWithTag(self.allocator, slice, .Tuple); const components = try self.toAbiComponents(data[array_type].lhs); return .{ @@ -434,7 +436,7 @@ pub fn toAbiParameter(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiParame var buffer: [256]u8 = undefined; const slice = try std.fmt.bufPrint(&buffer, "tuple{s}", .{self.ast.source[open_bracket..closing_bracket]}); - const param_type = try ParamType.typeToUnion(slice, self.allocator); + const param_type = try ParamType.typeToUnionWithTag(self.allocator, slice, .Tuple); const components = self.struct_params.get(self.ast.tokenSlice(main[data[array_type].lhs])) orelse return error.MissingTypeDeclaration; return .{ @@ -445,7 +447,7 @@ pub fn toAbiParameter(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiParame }, else => { const type_slice = self.ast.getNodeSource(data[node].lhs); - const param_type = try ParamType.typeToUnion(type_slice, self.allocator); + const param_type = try ParamType.typeToUnionWithTag(self.allocator, type_slice, token_tags[main[data[array_type].lhs]]); return .{ .type = param_type, @@ -466,8 +468,7 @@ pub fn toAbiParameter(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiParame }; }, .elementary_type => { - const type_slice = self.ast.tokenSlice(main[data[node].lhs]); - const param_type = try ParamType.typeToUnion(type_slice, self.allocator); + const param_type = ParamType.fromHumanReadableTokenTag(token_tags[main[data[node].lhs]]).?; return .{ .type = param_type, @@ -512,6 +513,7 @@ pub fn toAbiEventParameter(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiE const data = self.ast.nodes.items(.data); const main = self.ast.nodes.items(.main_token); const starts = self.ast.tokens.items(.start); + const token_tags = self.ast.tokens.items(.tag); std.debug.assert(nodes[node] == .event_var_decl); @@ -529,7 +531,7 @@ pub fn toAbiEventParameter(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiE var buffer: [256]u8 = undefined; const slice = try std.fmt.bufPrint(&buffer, "tuple{s}", .{self.ast.source[open_bracket..closing_bracket]}); - const param_type = try ParamType.typeToUnion(slice, self.allocator); + const param_type = try ParamType.typeToUnionWithTag(self.allocator, slice, .Tuple); const components = try self.toAbiComponents(data[array_type].lhs); return .{ @@ -546,7 +548,7 @@ pub fn toAbiEventParameter(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiE var buffer: [256]u8 = undefined; const slice = try std.fmt.bufPrint(&buffer, "tuple{s}", .{self.ast.source[open_bracket..closing_bracket]}); - const param_type = try ParamType.typeToUnion(slice, self.allocator); + const param_type = try ParamType.typeToUnionWithTag(self.allocator, slice, .Tuple); const components = self.struct_params.get(self.ast.tokenSlice(main[data[array_type].lhs])) orelse return error.MissingTypeDeclaration; return .{ @@ -558,7 +560,7 @@ pub fn toAbiEventParameter(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiE }, else => { const type_slice = self.ast.getNodeSource(data[node].lhs); - const param_type = try ParamType.typeToUnion(type_slice, self.allocator); + const param_type = try ParamType.typeToUnionWithTag(self.allocator, type_slice, token_tags[main[data[array_type].lhs]]); return .{ .type = param_type, @@ -581,8 +583,7 @@ pub fn toAbiEventParameter(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiE }; }, .elementary_type => { - const type_slice = self.ast.tokenSlice(main[data[node].lhs]); - const param_type = try ParamType.typeToUnion(type_slice, self.allocator); + const param_type = ParamType.fromHumanReadableTokenTag(token_tags[main[data[node].lhs]]).?; return .{ .type = param_type, @@ -609,6 +610,7 @@ pub fn toAbiParameterFromDecl(self: HumanAbi, node: Node.Index) HumanAbiErrors!A const data = self.ast.nodes.items(.data); const main = self.ast.nodes.items(.main_token); const starts = self.ast.tokens.items(.start); + const token_tags = self.ast.tokens.items(.tag); std.debug.assert(nodes[node] == .error_var_decl or nodes[node] == .struct_field); @@ -626,7 +628,7 @@ pub fn toAbiParameterFromDecl(self: HumanAbi, node: Node.Index) HumanAbiErrors!A var buffer: [256]u8 = undefined; const slice = try std.fmt.bufPrint(&buffer, "tuple{s}", .{self.ast.source[open_bracket..closing_bracket]}); - const param_type = try ParamType.typeToUnion(slice, self.allocator); + const param_type = try ParamType.typeToUnionWithTag(self.allocator, slice, .Tuple); const components = try self.toAbiComponents(data[array_type].lhs); return .{ @@ -642,7 +644,7 @@ pub fn toAbiParameterFromDecl(self: HumanAbi, node: Node.Index) HumanAbiErrors!A var buffer: [256]u8 = undefined; const slice = try std.fmt.bufPrint(&buffer, "tuple{s}", .{self.ast.source[open_bracket..closing_bracket]}); - const param_type = try ParamType.typeToUnion(slice, self.allocator); + const param_type = try ParamType.typeToUnionWithTag(self.allocator, slice, .Tuple); const components = self.struct_params.get(self.ast.tokenSlice(main[data[array_type].lhs])) orelse return error.MissingTypeDeclaration; return .{ @@ -653,7 +655,7 @@ pub fn toAbiParameterFromDecl(self: HumanAbi, node: Node.Index) HumanAbiErrors!A }, else => { const type_slice = self.ast.getNodeSource(data[node].lhs); - const param_type = try ParamType.typeToUnion(type_slice, self.allocator); + const param_type = try ParamType.typeToUnionWithTag(self.allocator, type_slice, token_tags[main[data[array_type].lhs]]); return .{ .type = param_type, @@ -674,8 +676,7 @@ pub fn toAbiParameterFromDecl(self: HumanAbi, node: Node.Index) HumanAbiErrors!A }; }, .elementary_type => { - const type_slice = self.ast.tokenSlice(main[data[node].lhs]); - const param_type = try ParamType.typeToUnion(type_slice, self.allocator); + const param_type = ParamType.fromHumanReadableTokenTag(token_tags[main[data[node].lhs]]).?; return .{ .type = param_type, diff --git a/src/human-readable/Parser.zig b/src/human-readable/Parser.zig index ad564ff5..9f586884 100644 --- a/src/human-readable/Parser.zig +++ b/src/human-readable/Parser.zig @@ -50,8 +50,10 @@ pub fn parseSource(self: *Parser) ParserErrors!void { const members = try self.parseUnits(); - if (self.token_tags[self.token_index] != .EndOfFileToken) + if (self.token_tags[self.token_index] != .EndOfFileToken) { + @branchHint(.cold); return error.ParsingError; + } self.nodes.items(.data)[0] = .{ .lhs = members.start, @@ -80,8 +82,10 @@ pub fn parseUnits(self: *Parser) ParserErrors!Node.Range { pub fn expectUnit(self: *Parser) ParserErrors!Node.Index { const unit = try self.parseUnit(); - if (unit == 0) + if (unit == 0) { + @branchHint(.cold); return error.ParsingError; + } return unit; } @@ -281,7 +285,10 @@ pub fn parseSpecifiers(self: *Parser) ParserErrors!Node.Index { switch (specifier) { .seen_visibility, .seen_both, - => return error.ParsingError, + => { + @branchHint(.cold); + return error.ParsingError; + }, .seen_mutability => specifier = .seen_both, .none => specifier = .seen_visibility, } @@ -295,7 +302,10 @@ pub fn parseSpecifiers(self: *Parser) ParserErrors!Node.Index { switch (specifier) { .seen_mutability, .seen_both, - => return error.ParsingError, + => { + @branchHint(.cold); + return error.ParsingError; + }, .seen_visibility => specifier = .seen_both, .none => specifier = .seen_mutability, } @@ -407,7 +417,10 @@ pub fn parseEventVarDecls(self: *Parser) ParserErrors!Span { self.token_index += 1; break; }, - else => return error.ParsingError, + else => { + @branchHint(.cold); + return error.ParsingError; + }, } } @@ -440,7 +453,10 @@ pub fn parseErrorVarDecls(self: *Parser) ParserErrors!Span { self.token_index += 1; break; }, - else => return error.ParsingError, + else => { + @branchHint(.cold); + return error.ParsingError; + }, } } @@ -479,8 +495,10 @@ pub fn parseReturnParams(self: *Parser) ParserErrors!Node.Range { const slice = self.scratch.items[scratch..]; - if (slice.len == 0) + if (slice.len == 0) { + @branchHint(.cold); return error.ParsingError; + } return self.listToSpan(slice); } @@ -505,7 +523,10 @@ pub fn parseVariableDecls(self: *Parser) ParserErrors!Span { self.token_index += 1; break; }, - else => return error.ParsingError, + else => { + @branchHint(.cold); + return error.ParsingError; + }, } } @@ -521,8 +542,10 @@ pub fn parseVariableDecls(self: *Parser) ParserErrors!Span { pub fn expectErrorVarDecl(self: *Parser) ParserErrors!Node.Index { const index = try self.parseErrorVarDecl(); - if (index == 0) + if (index == 0) { + @branchHint(.cold); return error.ParsingError; + } return index; } @@ -548,8 +571,10 @@ pub fn parseErrorVarDecl(self: *Parser) ParserErrors!Node.Index { pub fn expectEventVarDecl(self: *Parser) ParserErrors!Node.Index { const index = try self.parseEventVarDecl(); - if (index == 0) + if (index == 0) { + @branchHint(.cold); return error.ParsingError; + } return index; } @@ -581,8 +606,10 @@ pub fn parseEventVarDecl(self: *Parser) ParserErrors!Node.Index { pub fn expectVarDecl(self: *Parser) ParserErrors!Node.Index { const index = try self.parseVariableDecl(); - if (index == 0) + if (index == 0) { + @branchHint(.cold); return error.ParsingError; + } return index; } @@ -692,8 +719,10 @@ pub fn expectStructField(self: *Parser) ParserErrors!Node.Index { pub fn expectType(self: *Parser) ParserErrors!Node.Index { const index = try self.parseType(); - if (index == 0) + if (index == 0) { + @branchHint(.cold); return error.ParsingError; + } return index; } @@ -892,7 +921,10 @@ fn consumeToken(self: *Parser, expected: TokenTag) ?TokenIndex { } fn expectToken(self: *Parser, expected: TokenTag) error{ParsingError}!TokenIndex { - return if (self.token_tags[self.token_index] == expected) self.nextToken() else return error.ParsingError; + return if (self.token_tags[self.token_index] == expected) self.nextToken() else { + @branchHint(.cold); + return error.ParsingError; + }; } fn nextToken(self: *Parser) TokenIndex { diff --git a/src/human-readable/ParserOld.zig b/src/human-readable/ParserOld.zig deleted file mode 100644 index e66a3ec9..00000000 --- a/src/human-readable/ParserOld.zig +++ /dev/null @@ -1,632 +0,0 @@ -const std = @import("std"); -const testing = std.testing; -const abi = @import("../abi/abi.zig"); - -// Types -const Abi = abi.Abi; -const AbiItem = abi.AbiItem; -const AbiParameter = @import("../abi/abi_parameter.zig").AbiParameter; -const AbiEventParameter = @import("../abi/abi_parameter.zig").AbiEventParameter; -const Allocator = std.mem.Allocator; -const Constructor = abi.Constructor; -const Error = abi.Error; -const Event = abi.Event; -const Fallback = abi.Fallback; -const Function = abi.Function; -const Receive = abi.Receive; -const Lexer = @import("lexer.zig").Lexer; -const StateMutability = @import("../abi/state_mutability.zig").StateMutability; -const ParamErrors = @import("../abi/param_type.zig").ParamErrors; -const ParamType = @import("../abi/param_type.zig").ParamType; -const Tokens = @import("tokens.zig").Tag.SoliditySyntax; - -pub const TokenList = std.MultiArrayList(struct { - token_type: Tokens, - start: u32, - end: u32, -}); - -/// Set of possible errors that can happen while parsing. -pub const ParseErrors = error{ - InvalidDataLocation, - UnexceptedToken, - InvalidType, - ExpectedCommaAfterParam, - EmptyReturnParams, -} || ParamErrors || Allocator.Error; - -const Parser = @This(); - -/// The allocator used in parsing. -alloc: Allocator, -/// Slice of tokens produced by the lexer. -tokens: []const Tokens, -/// Slice of start positions of the source of the token. -tokens_start: []const u32, -/// Slice of end positions of the source of the token. -tokens_end: []const u32, -/// The current token index. -token_index: u32, -/// The slice that cotains the token source. -source: []const u8, -/// Hashmap of the preparsed structs. -structs: std.StringHashMapUnmanaged([]const AbiParameter), - -/// Parse a string or a multi line string with solidity signatures.\ -/// This will return all signatures as a slice of `AbiItem`. -/// -/// This supports parsing struct signatures if its intended to use -/// The struct signatures must be defined top down. -/// -/// **Example** -/// ```zig -/// var lex = Lexer.init(source); -/// -/// var list = Parser.TokenList{}; -/// defer list.deinit(allocator); -/// -/// while (true) { -/// const tok = lex.scan(); -/// -/// try list.append(allocator, .{ -/// .token_type = tok.syntax, -/// .start = tok.location.start, -/// .end = tok.location.end, -/// }); -/// -/// if (tok.syntax == .EndOfFileToken) break; -/// } -/// -/// var parser: Parser = .{ -/// .alloc = allocator, -/// .tokens = list.items(.token_type), -/// .tokens_start = list.items(.start), -/// .tokens_end = list.items(.end), -/// .token_index = 0, -/// .source = source, -/// .structs = .{}, -/// }; -/// -/// const abi = try parser.parseAbiProto(); -/// ``` -pub fn parseAbiProto(p: *Parser) ParseErrors!Abi { - var abi_list = std.ArrayList(AbiItem).init(p.alloc); - - while (true) { - if (p.tokens[p.token_index] == .Struct) { - try p.parseStructProto(); - continue; - } - - try abi_list.append(try p.parseAbiItemProto()); - - if (p.tokens[p.token_index] == .EndOfFileToken) break; - } - - return abi_list.toOwnedSlice(); -} - -/// Parse a single solidity signature based on expected tokens. -/// -/// Will return an error if the token is not expected. -pub fn parseAbiItemProto(p: *Parser) ParseErrors!AbiItem { - return switch (p.tokens[p.token_index]) { - .Function => .{ .abiFunction = try p.parseFunctionFnProto() }, - .Event => .{ .abiEvent = try p.parseEventFnProto() }, - .Error => .{ .abiError = try p.parseErrorFnProto() }, - .Constructor => .{ .abiConstructor = try p.parseConstructorFnProto() }, - .Fallback => .{ .abiFallback = try p.parseFallbackFnProto() }, - .Receive => .{ .abiReceive = try p.parseReceiveFnProto() }, - inline else => error.UnexceptedToken, - }; -} - -/// Parse single solidity function signature.\ -/// FunctionProto -> Function KEYWORD, Identifier, OpenParen, ParamDecls?, ClosingParen, Visibility?, StateMutability?, Returns? -pub fn parseFunctionFnProto(p: *Parser) ParseErrors!Function { - _ = try p.expectToken(.Function); - const name = p.parseIdentifier().?; - - _ = try p.expectToken(.OpenParen); - - const inputs: []const AbiParameter = if (p.tokens[p.token_index] == .ClosingParen) &.{} else try p.parseFuncParamsDecl(); - - _ = try p.expectToken(.ClosingParen); - - try p.parseVisibility(); - - const state: StateMutability = switch (p.tokens[p.token_index]) { - .Payable => .payable, - .View => .view, - .Pure => .pure, - inline else => .nonpayable, - }; - - if (state != .nonpayable) _ = p.nextToken(); - - if (p.consumeToken(.Returns)) |_| { - _ = try p.expectToken(.OpenParen); - - const outputs: []const AbiParameter = if (p.tokens[p.token_index] == .ClosingParen) return error.EmptyReturnParams else try p.parseFuncParamsDecl(); - - _ = try p.expectToken(.ClosingParen); - - return .{ .type = .function, .name = name, .inputs = inputs, .outputs = outputs, .stateMutability = state }; - } - - return .{ .type = .function, .name = name, .inputs = inputs, .outputs = &.{}, .stateMutability = state }; -} - -/// Parse single solidity event signature.\ -/// EventProto -> Event KEYWORD, Identifier, OpenParen, ParamDecls?, ClosingParen -pub fn parseEventFnProto(p: *Parser) ParseErrors!Event { - _ = try p.expectToken(.Event); - const name = p.parseIdentifier().?; - - _ = try p.expectToken(.OpenParen); - - const inputs: []const AbiEventParameter = if (p.tokens[p.token_index] == .ClosingParen) &.{} else try p.parseEventParamsDecl(); - - _ = try p.expectToken(.ClosingParen); - - return .{ .type = .event, .inputs = inputs, .name = name }; -} - -/// Parse single solidity error signature.\ -/// ErrorProto -> Error KEYWORD, Identifier, OpenParen, ParamDecls?, ClosingParen -pub fn parseErrorFnProto(p: *Parser) ParseErrors!Error { - _ = try p.expectToken(.Error); - const name = p.parseIdentifier().?; - - _ = try p.expectToken(.OpenParen); - - const inputs: []const AbiParameter = if (p.tokens[p.token_index] == .ClosingParen) &.{} else try p.parseErrorParamsDecl(); - - _ = try p.expectToken(.ClosingParen); - - return .{ .type = .@"error", .inputs = inputs, .name = name }; -} - -/// Parse single solidity constructor signature.\ -/// ConstructorProto -> Constructor KEYWORD, OpenParen, ParamDecls?, ClosingParen, StateMutability? -pub fn parseConstructorFnProto(p: *Parser) ParseErrors!Constructor { - _ = try p.expectToken(.Constructor); - - _ = try p.expectToken(.OpenParen); - - const inputs: []const AbiParameter = if (p.tokens[p.token_index] == .ClosingParen) &.{} else try p.parseFuncParamsDecl(); - - _ = try p.expectToken(.ClosingParen); - - return switch (p.tokens[p.token_index]) { - .Payable => .{ .type = .constructor, .stateMutability = .payable, .inputs = inputs }, - inline else => .{ .type = .constructor, .stateMutability = .nonpayable, .inputs = inputs }, - }; -} - -/// Parse single solidity struct signature.\ -/// StructProto -> Struct KEYWORD, Identifier, OpenBrace, ParamDecls, ClosingBrace -pub fn parseStructProto(p: *Parser) ParseErrors!void { - _ = try p.expectToken(.Struct); - - const name = p.parseIdentifier().?; - - _ = try p.expectToken(.OpenBrace); - - const params = try p.parseStructParamDecls(); - - _ = try p.expectToken(.ClosingBrace); - - try p.structs.put(p.alloc, name, params); -} - -/// Parse single solidity fallback signature.\ -/// FallbackProto -> Fallback KEYWORD, OpenParen, ClosingParen, StateMutability? -pub fn parseFallbackFnProto(p: *Parser) error{UnexceptedToken}!Fallback { - _ = try p.expectToken(.Fallback); - _ = try p.expectToken(.OpenParen); - _ = try p.expectToken(.ClosingParen); - - switch (p.tokens[p.token_index]) { - .Payable => { - if (p.tokens[p.token_index + 1] != .EndOfFileToken) return error.UnexceptedToken; - - return .{ .type = .fallback, .stateMutability = .payable }; - }, - .EndOfFileToken => return .{ .type = .fallback, .stateMutability = .nonpayable }, - inline else => return error.UnexceptedToken, - } -} - -/// Parse single solidity receive signature.\ -/// ReceiveProto -> Receive KEYWORD, OpenParen, ClosingParen, External, Payable -pub fn parseReceiveFnProto(p: *Parser) error{UnexceptedToken}!Receive { - _ = try p.expectToken(.Receive); - _ = try p.expectToken(.OpenParen); - _ = try p.expectToken(.ClosingParen); - _ = try p.expectToken(.External); - _ = try p.expectToken(.Payable); - - return .{ .type = .receive, .stateMutability = .payable }; -} - -/// Parse solidity function params.\ -/// TypeExpr, DataLocation?, Identifier?, Comma? -pub fn parseFuncParamsDecl(p: *Parser) ParseErrors![]const AbiParameter { - var param_list = std.ArrayList(AbiParameter).init(p.alloc); - - while (true) { - const tuple_param = if (p.consumeToken(.OpenParen) != null) try p.parseTuple(AbiParameter) else null; - - if (tuple_param != null) { - try param_list.append(tuple_param.?); - - switch (p.tokens[p.token_index]) { - .Comma => p.token_index += 1, - .ClosingParen => break, - .EndOfFileToken => break, - inline else => return error.ExpectedCommaAfterParam, - } - - continue; - } - - var components: ?[]const AbiParameter = null; - const abitype = param_type: { - if (p.parseTypeExpr()) |result| break :param_type result else |err| { - // Before failing we check if he have an Identifier token - // And see it was defined as a struct. - const last = p.token_index - 1; - - if (p.tokens[last] == .Identifier) { - const name = p.source[p.tokens_start[last]..p.tokens_end[last]]; - - if (p.structs.get(name)) |val| { - components = val; - const start = p.token_index; - const arr = if (try p.parseArrayType()) |index| p.source[p.tokens_start[start]..p.tokens_end[index]] else ""; - - break :param_type try ParamType.typeToUnion(try std.fmt.allocPrint(p.alloc, "tuple{s}", .{arr}), p.alloc); - } - - return err; - } - - return err; - } - }; - - const location = p.parseDataLocation(); - if (location) |tok| { - _ = p.consumeToken(tok); - switch (tok) { - .Indexed => return error.InvalidDataLocation, - .Memory, .Calldata, .Storage => { - const isValid = switch (abitype) { - .string, .bytes => true, - .dynamicArray => true, - .fixedArray => true, - inline else => false, - }; - - if (!isValid) return error.InvalidDataLocation; - }, - inline else => {}, - } - } - - const name = p.parseIdentifier() orelse ""; - const param = .{ .type = abitype, .name = name, .internalType = null, .components = components }; - - try param_list.append(param); - - switch (p.tokens[p.token_index]) { - .Comma => p.token_index += 1, - .ClosingParen => break, - .EndOfFileToken => break, - inline else => return error.ExpectedCommaAfterParam, - } - } - - return try param_list.toOwnedSlice(); -} - -/// Parse solidity event params.\ -/// TypeExpr, DataLocation?, Identifier?, Comma? -pub fn parseEventParamsDecl(p: *Parser) ParseErrors![]const AbiEventParameter { - var param_list = std.ArrayList(AbiEventParameter).init(p.alloc); - - while (true) { - const tuple_param = if (p.consumeToken(.OpenParen) != null) try p.parseTuple(AbiEventParameter) else null; - - if (tuple_param) |t_param| { - try param_list.append(t_param); - - switch (p.tokens[p.token_index]) { - .Comma => p.token_index += 1, - .ClosingParen => break, - .EndOfFileToken => break, - inline else => return error.ExpectedCommaAfterParam, - } - - continue; - } - - var components: ?[]const AbiParameter = null; - const abitype = param_type: { - if (p.parseTypeExpr()) |result| break :param_type result else |err| { - // Before failing we check if he have an Identifier token - // And see it was defined as a struct. - const last = p.token_index - 1; - - if (p.tokens[last] == .Identifier) { - const name = p.source[p.tokens_start[last]..p.tokens_end[last]]; - - if (p.structs.get(name)) |val| { - components = val; - const start = p.token_index; - const arr = if (try p.parseArrayType()) |index| p.source[p.tokens_start[start]..p.tokens_end[index]] else ""; - - break :param_type try ParamType.typeToUnion(try std.fmt.allocPrint(p.alloc, "tuple{s}", .{arr}), p.alloc); - } - - return err; - } - - return err; - } - }; - - const location = p.parseDataLocation(); - const indexed = indexed: { - if (location) |tok| { - _ = p.consumeToken(tok); - switch (tok) { - .Indexed => break :indexed true, - inline else => return error.InvalidDataLocation, - } - } else break :indexed false; - }; - - const name = p.parseIdentifier() orelse ""; - const param = .{ .type = abitype, .name = name, .indexed = indexed, .internalType = null, .components = components }; - - try param_list.append(param); - - switch (p.tokens[p.token_index]) { - .Comma => p.token_index += 1, - .ClosingParen => break, - .EndOfFileToken => break, - inline else => return error.ExpectedCommaAfterParam, - } - } - - return try param_list.toOwnedSlice(); -} - -/// Parse solidity error params.\ -/// TypeExpr, DataLocation?, Identifier?, Comma? -pub fn parseErrorParamsDecl(p: *Parser) ParseErrors![]const AbiParameter { - var param_list = std.ArrayList(AbiParameter).init(p.alloc); - - while (true) { - const tuple_param = if (p.consumeToken(.OpenParen) != null) try p.parseTuple(AbiParameter) else null; - - if (tuple_param != null) { - try param_list.append(tuple_param.?); - - switch (p.tokens[p.token_index]) { - .Comma => p.token_index += 1, - .ClosingParen => break, - .EndOfFileToken => break, - inline else => return error.ExpectedCommaAfterParam, - } - - continue; - } - - var components: ?[]const AbiParameter = null; - const abitype = param_type: { - if (p.parseTypeExpr()) |result| break :param_type result else |err| { - const last = p.token_index - 1; - - if (p.tokens[last] == .Identifier) { - const name = p.source[p.tokens_start[last]..p.tokens_end[last]]; - - if (p.structs.get(name)) |val| { - components = val; - const start = p.token_index; - const arr = if (try p.parseArrayType()) |index| p.source[p.tokens_start[start]..p.tokens_end[index]] else ""; - - break :param_type try ParamType.typeToUnion(try std.fmt.allocPrint(p.alloc, "tuple{s}", .{arr}), p.alloc); - } - - return err; - } - - return err; - } - }; - - const location = p.parseDataLocation(); - if (location != null) return error.InvalidDataLocation; - - const name = p.parseIdentifier() orelse ""; - const param: AbiParameter = .{ .type = abitype, .name = name, .internalType = null, .components = components }; - - try param_list.append(param); - - switch (p.tokens[p.token_index]) { - .Comma => p.token_index += 1, - .ClosingParen => break, - .EndOfFileToken => break, - inline else => return error.ExpectedCommaAfterParam, - } - } - - return try param_list.toOwnedSlice(); -} - -/// Parse solidity struct params.\ -/// TypeExpr, Identifier?, SemiColon -pub fn parseStructParamDecls(p: *Parser) ParseErrors![]const AbiParameter { - var param_list = std.ArrayList(AbiParameter).init(p.alloc); - - while (true) { - const tuple_param = if (p.consumeToken(.OpenParen) != null) try p.parseTuple(AbiParameter) else null; - - if (tuple_param != null) { - try param_list.append(tuple_param.?); - - _ = try p.expectToken(.SemiColon); - - switch (p.tokens[p.token_index]) { - .ClosingBrace => break, - .EndOfFileToken => return error.UnexceptedToken, - inline else => continue, - } - } - - var components: ?[]const AbiParameter = null; - const abitype = param_type: { - if (p.parseTypeExpr()) |result| break :param_type result else |err| { - const last = p.token_index - 1; - - if (p.tokens[last] == .Identifier) { - const name = p.source[p.tokens_start[last]..p.tokens_end[last]]; - - if (p.structs.get(name)) |val| { - components = val; - const start = p.token_index; - const arr = if (try p.parseArrayType()) |index| p.source[p.tokens_start[start]..p.tokens_end[index]] else ""; - - break :param_type try ParamType.typeToUnion(try std.fmt.allocPrint(p.alloc, "tuple{s}", .{arr}), p.alloc); - } - - return err; - } - - return err; - } - }; - - const location = p.parseDataLocation(); - if (location != null) return error.InvalidDataLocation; - - const name = p.parseIdentifier() orelse ""; - const param: AbiParameter = .{ .type = abitype, .name = name, .internalType = null, .components = components }; - - try param_list.append(param); - - _ = try p.expectToken(.SemiColon); - - switch (p.tokens[p.token_index]) { - .ClosingBrace => break, - .EndOfFileToken => return error.UnexceptedToken, - inline else => continue, - } - } - - return try param_list.toOwnedSlice(); -} - -/// Parse solidity tuple params.\ -/// OpenParen, TypeExpr, Identifier?, Comma?, ClosingParen -pub fn parseTuple(p: *Parser, comptime T: type) ParseErrors!T { - const components = try p.parseErrorParamsDecl(); - - _ = try p.expectToken(.ClosingParen); - const start = p.token_index; - const end = try p.parseArrayType(); - const array_slice = if (end) |arr| p.source[p.tokens_start[start]..p.tokens_end[arr]] else null; - - const type_name = try std.fmt.allocPrint(p.alloc, "tuple{s}", .{array_slice orelse ""}); - - const abitype = try ParamType.typeToUnion(type_name, p.alloc); - - const location = p.parseDataLocation(); - const name = p.parseIdentifier() orelse ""; - - return switch (T) { - AbiParameter => { - if (location != null) return error.InvalidDataLocation; - return .{ .type = abitype, .name = name, .internalType = null, .components = components }; - }, - AbiEventParameter => .{ .type = abitype, .name = name, .internalType = null, .indexed = location == .Indexed, .components = components }, - inline else => error.InvalidType, - }; -} - -fn parseTypeExpr(p: *Parser) (ParamErrors || error{UnexceptedToken})!ParamType { - const index = p.nextToken(); - const tok = p.tokens[index]; - - if (tok.lexToken()) |type_name| { - const slice = if (try p.parseArrayType()) |arr| p.source[p.tokens_start[index]..p.tokens_end[arr]] else type_name; - - return try ParamType.typeToUnion(slice, p.alloc); - } - - return error.UnexceptedToken; -} - -fn parseArrayType(p: *Parser) error{UnexceptedToken}!?u32 { - while (true) { - const token = p.nextToken(); - - switch (p.tokens[token]) { - .OpenBracket => continue, - .Number => { - _ = try p.expectToken(.ClosingBracket); - p.token_index -= 1; - }, - .ClosingBracket => switch (p.tokens[p.token_index]) { - .OpenBracket => continue, - else => return token, - }, - inline else => { - p.token_index -= 1; - return null; - }, - } - } -} - -fn parseDataLocation(p: *Parser) ?Tokens { - const tok = p.tokens[p.token_index]; - - return switch (tok) { - .Indexed, .Calldata, .Storage, .Memory => tok, - inline else => null, - }; -} - -fn parseVisibility(p: *Parser) error{UnexceptedToken}!void { - const external = p.consumeToken(.External) orelse 0; - const public = p.consumeToken(.Public) orelse 0; - - if (external != 0 and public != 0) { - return error.UnexceptedToken; - } -} - -fn parseIdentifier(p: *Parser) ?[]const u8 { - return if (p.consumeToken(.Identifier)) |ident| p.source[p.tokens_start[ident]..p.tokens_end[ident]] else null; -} - -fn expectToken(p: *Parser, expected: Tokens) error{UnexceptedToken}!u32 { - if (p.tokens[p.token_index] != expected) return error.UnexceptedToken; - - return p.nextToken(); -} - -fn consumeToken(p: *Parser, tok: Tokens) ?u32 { - return if (p.tokens[p.token_index] == tok) p.nextToken() else null; -} - -fn nextToken(p: *Parser) u32 { - const index = p.token_index; - p.token_index += 1; - - return index; -} diff --git a/src/human-readable/abi_parsing.zig b/src/human-readable/abi_parsing.zig index 572db63a..ea33280c 100644 --- a/src/human-readable/abi_parsing.zig +++ b/src/human-readable/abi_parsing.zig @@ -51,19 +51,3 @@ pub fn parseHumanReadable(alloc: Allocator, source: [:0]const u8) HumanAbi.Error return abi_parsed; } - -// fn innerParse(comptime T: type, parser: *Parser) Parser.ParseErrors!T { -// return switch (T) { -// abi.Abi => parser.parseAbiProto(), -// abi.AbiItem => parser.parseAbiItemProto(), -// abi.Function => parser.parseFunctionFnProto(), -// abi.Event => parser.parseEventFnProto(), -// abi.Error => parser.parseErrorFnProto(), -// abi.Constructor => parser.parseConstructorFnProto(), -// abi.Fallback => parser.parseFallbackFnProto(), -// abi.Receive => parser.parseReceiveFnProto(), -// []const param.AbiParameter => parser.parseFuncParamsDecl(), -// []const param.AbiEventParameter => parser.parseEventParamsDecl(), -// inline else => @compileError("Provided type '" ++ @typeName(T) ++ "' is not supported for human readable parsing"), -// }; -// } diff --git a/src/tests/human-readable/abi_parsing.test.zig b/src/tests/human-readable/abi_parsing.test.zig index c2da24b7..036fe837 100644 --- a/src/tests/human-readable/abi_parsing.test.zig +++ b/src/tests/human-readable/abi_parsing.test.zig @@ -8,344 +8,6 @@ const ParamType = param_type.ParamType; const parseHumanReadable = @import("../../human-readable/abi_parsing.zig").parseHumanReadable; -// test "AbiParameter" { -// const slice = "address foo"; -// -// const params = try parseHumanReadable([]const param.AbiParameter, testing.allocator, slice); -// defer params.deinit(); -// -// for (params.value) |val| { -// try testing.expectEqual(val.type, ParamType{ .address = {} }); -// try testing.expectEqualStrings(val.name, "foo"); -// } -// } -// -// test "AbiParameters" { -// const slice = "address foo, int120 bar"; -// -// const params = try parseHumanReadable([]const param.AbiParameter, testing.allocator, slice); -// defer params.deinit(); -// -// try testing.expectEqual(ParamType{ .address = {} }, params.value[0].type); -// try testing.expectEqual(ParamType{ .int = 120 }, params.value[1].type); -// -// try testing.expectEqualStrings("foo", params.value[0].name); -// try testing.expectEqualStrings("bar", params.value[1].name); -// -// try testing.expectEqual(params.value.len, 2); -// } -// -// test "AbiParameters dynamic array" { -// const slice = "address[] foo, int120 bar"; -// -// const params = try parseHumanReadable([]const param.AbiParameter, testing.allocator, slice); -// defer params.deinit(); -// -// try testing.expectEqual(ParamType{ .address = {} }, params.value[0].type.dynamicArray.*); -// try testing.expectEqual(ParamType{ .int = 120 }, params.value[1].type); -// -// try testing.expectEqualStrings("foo", params.value[0].name); -// try testing.expectEqualStrings("bar", params.value[1].name); -// -// try testing.expectEqual(params.value.len, 2); -// } -// -// test "AbiParameters 2d dynamic array" { -// const slice = "address[][] foo, int120 bar"; -// -// const params = try parseHumanReadable([]const param.AbiParameter, testing.allocator, slice); -// defer params.deinit(); -// -// try testing.expectEqual(ParamType{ .address = {} }, params.value[0].type.dynamicArray.dynamicArray.*); -// try testing.expectEqual(ParamType{ .int = 120 }, params.value[1].type); -// -// try testing.expectEqualStrings("foo", params.value[0].name); -// try testing.expectEqualStrings("bar", params.value[1].name); -// -// try testing.expectEqual(params.value.len, 2); -// } -// -// test "AbiParameters mixed 2d array" { -// const slice = "address[5][] foo, int120 bar"; -// -// const params = try parseHumanReadable([]const param.AbiParameter, testing.allocator, slice); -// defer params.deinit(); -// -// try testing.expectEqual(ParamType{ .address = {} }, params.value[0].type.dynamicArray.fixedArray.child.*); -// try testing.expectEqual(ParamType{ .int = 120 }, params.value[1].type); -// -// try testing.expectEqualStrings("foo", params.value[0].name); -// try testing.expectEqualStrings("bar", params.value[1].name); -// -// try testing.expectEqual(params.value.len, 2); -// } -// -// test "AbiParameters with fixed array" { -// const slice = "address[5] foo, int120 bar"; -// -// const params = try parseHumanReadable([]const param.AbiParameter, testing.allocator, slice); -// defer params.deinit(); -// -// try testing.expectEqual(ParamType{ .address = {} }, params.value[0].type.fixedArray.child.*); -// try testing.expectEqual(ParamType{ .int = 120 }, params.value[1].type); -// -// try testing.expectEqualStrings("foo", params.value[0].name); -// try testing.expectEqualStrings("bar", params.value[1].name); -// -// try testing.expectEqual(params.value.len, 2); -// } -// -// test "AbiParameters with data location" { -// const slice = "string calldata foo, int120 bar"; -// -// const params = try parseHumanReadable([]const param.AbiParameter, testing.allocator, slice); -// defer params.deinit(); -// -// try testing.expectEqual(ParamType{ .string = {} }, params.value[0].type); -// try testing.expectEqual(ParamType{ .int = 120 }, params.value[1].type); -// -// try testing.expectEqualStrings("foo", params.value[0].name); -// try testing.expectEqualStrings("bar", params.value[1].name); -// -// try testing.expectEqual(params.value.len, 2); -// } -// -// test "AbiParameters with tuple" { -// const slice = "address foo, (bytes32 baz) bar"; -// -// const params = try parseHumanReadable([]const param.AbiParameter, testing.allocator, slice); -// defer params.deinit(); -// -// try testing.expectEqual(ParamType{ .address = {} }, params.value[0].type); -// try testing.expectEqual(ParamType{ .tuple = {} }, params.value[1].type); -// -// try testing.expectEqualStrings("foo", params.value[0].name); -// try testing.expectEqualStrings("bar", params.value[1].name); -// -// try testing.expectEqual(params.value.len, 2); -// -// try testing.expect(params.value[1].components != null); -// try testing.expectEqual(ParamType{ .fixedBytes = 32 }, params.value[1].components.?[0].type); -// try testing.expectEqualStrings("baz", params.value[1].components.?[0].name); -// } -// -// test "AbiParameters with nested tuple" { -// const slice = "((bytes32 baz)[] fizz) bar"; -// -// const params = try parseHumanReadable([]const param.AbiParameter, testing.allocator, slice); -// defer params.deinit(); -// -// try testing.expectEqual(ParamType{ .tuple = {} }, params.value[0].type); -// try testing.expectEqualStrings("bar", params.value[0].name); -// try testing.expectEqual(params.value.len, 1); -// -// try testing.expect(params.value[0].components != null); -// try testing.expect(params.value[0].components.?[0].components != null); -// try testing.expectEqual(ParamType{ .tuple = {} }, params.value[0].components.?[0].type.dynamicArray.*); -// try testing.expectEqual(ParamType{ .fixedBytes = 32 }, params.value[0].components.?[0].components.?[0].type); -// try testing.expectEqualStrings("fizz", params.value[0].components.?[0].name); -// try testing.expectEqualStrings("baz", params.value[0].components.?[0].components.?[0].name); -// } -// -// test "Receive signature" { -// const slice = "receive() external payable"; -// const signature = try parseHumanReadable(abi.Receive, testing.allocator, slice); -// defer signature.deinit(); -// -// try testing.expectEqual(signature.value.type, .receive); -// try testing.expectEqual(signature.value.stateMutability, .payable); -// } -// -// test "Fallback signature" { -// const slice = "fallback()"; -// const signature = try parseHumanReadable(abi.Fallback, testing.allocator, slice); -// defer signature.deinit(); -// -// try testing.expectEqual(signature.value.type, .fallback); -// try testing.expectEqual(signature.value.stateMutability, .nonpayable); -// } -// -// test "Fallback signature payable" { -// const slice = "fallback() payable"; -// const signature = try parseHumanReadable(abi.Fallback, testing.allocator, slice); -// defer signature.deinit(); -// -// try testing.expectEqual(signature.value.type, .fallback); -// try testing.expectEqual(signature.value.stateMutability, .payable); -// } -// -// test "Constructor signature" { -// const slice = "constructor(bool foo)"; -// const signature = try parseHumanReadable(abi.Constructor, testing.allocator, slice); -// defer signature.deinit(); -// -// try testing.expectEqual(signature.value.type, .constructor); -// try testing.expectEqual(ParamType{ .bool = {} }, signature.value.inputs[0].type); -// try testing.expectEqual(signature.value.stateMutability, .nonpayable); -// try testing.expectEqualStrings("foo", signature.value.inputs[0].name); -// } -// -// test "Constructor signature payable" { -// const slice = "constructor(bool foo) payable"; -// const signature = try parseHumanReadable(abi.Constructor, testing.allocator, slice); -// defer signature.deinit(); -// -// try testing.expectEqual(signature.value.type, .constructor); -// try testing.expectEqual(ParamType{ .bool = {} }, signature.value.inputs[0].type); -// try testing.expectEqual(signature.value.stateMutability, .payable); -// try testing.expectEqualStrings("foo", signature.value.inputs[0].name); -// } -// -// test "Error signature" { -// const slice = "error Foo(bytes foo)"; -// const signature = try parseHumanReadable(abi.Error, testing.allocator, slice); -// defer signature.deinit(); -// -// try testing.expectEqual(signature.value.type, .@"error"); -// try testing.expectEqual(ParamType{ .bytes = {} }, signature.value.inputs[0].type); -// try testing.expectEqualStrings("foo", signature.value.inputs[0].name); -// } -// -// test "Event signature" { -// const slice = "event Foo(bytes foo, address indexed bar)"; -// const signature = try parseHumanReadable(abi.Event, testing.allocator, slice); -// defer signature.deinit(); -// -// try testing.expectEqual(signature.value.type, .event); -// try testing.expectEqual(ParamType{ .bytes = {} }, signature.value.inputs[0].type); -// try testing.expectEqualStrings("foo", signature.value.inputs[0].name); -// try testing.expect(!signature.value.inputs[0].indexed); -// try testing.expectEqual(ParamType{ .address = {} }, signature.value.inputs[1].type); -// try testing.expectEqualStrings("bar", signature.value.inputs[1].name); -// try testing.expect(signature.value.inputs[1].indexed); -// } -// -// test "Function signature" { -// const slice = "function Foo(bytes foo, address bar)"; -// const signature = try parseHumanReadable(abi.Function, testing.allocator, slice); -// defer signature.deinit(); -// -// try testing.expectEqual(signature.value.type, .function); -// try testing.expectEqual(ParamType{ .bytes = {} }, signature.value.inputs[0].type); -// try testing.expectEqualStrings("foo", signature.value.inputs[0].name); -// try testing.expectEqual(ParamType{ .address = {} }, signature.value.inputs[1].type); -// try testing.expectEqualStrings("bar", signature.value.inputs[1].name); -// try testing.expectEqual(signature.value.stateMutability, .nonpayable); -// try testing.expectEqualSlices(param.AbiParameter, &.{}, signature.value.outputs); -// } -// -// test "Function signature with state" { -// const slice = "function Foo(bytes foo, address bar) external view"; -// const signature = try parseHumanReadable(abi.Function, testing.allocator, slice); -// defer signature.deinit(); -// -// try testing.expectEqual(signature.value.type, .function); -// try testing.expectEqual(ParamType{ .bytes = {} }, signature.value.inputs[0].type); -// try testing.expectEqualStrings("foo", signature.value.inputs[0].name); -// try testing.expectEqual(ParamType{ .address = {} }, signature.value.inputs[1].type); -// try testing.expectEqualStrings("bar", signature.value.inputs[1].name); -// try testing.expectEqual(signature.value.stateMutability, .view); -// try testing.expectEqualSlices(param.AbiParameter, &.{}, signature.value.outputs); -// } -// -// test "Function signature with return" { -// const slice = "function Foo(bytes foo, address bar) public pure returns (string baz)"; -// const signature = try parseHumanReadable(abi.Function, testing.allocator, slice); -// defer signature.deinit(); -// -// try testing.expectEqual(signature.value.type, .function); -// try testing.expectEqual(ParamType{ .bytes = {} }, signature.value.inputs[0].type); -// try testing.expectEqualStrings("foo", signature.value.inputs[0].name); -// try testing.expectEqual(ParamType{ .address = {} }, signature.value.inputs[1].type); -// try testing.expectEqualStrings("bar", signature.value.inputs[1].name); -// try testing.expectEqual(signature.value.stateMutability, .pure); -// try testing.expectEqual(ParamType{ .string = {} }, signature.value.outputs[0].type); -// try testing.expectEqualStrings("baz", signature.value.outputs[0].name); -// } -// -// test "AbiItem" { -// const slice = "function Foo(bytes foo, address bar) public pure returns (string baz)"; -// const signature = try parseHumanReadable(abi.AbiItem, testing.allocator, slice); -// defer signature.deinit(); -// -// const function = signature.value.abiFunction; -// try testing.expectEqual(function.type, .function); -// try testing.expectEqual(ParamType{ .bytes = {} }, function.inputs[0].type); -// try testing.expectEqualStrings("foo", function.inputs[0].name); -// try testing.expectEqual(ParamType{ .address = {} }, function.inputs[1].type); -// try testing.expectEqualStrings("bar", function.inputs[1].name); -// try testing.expectEqual(function.stateMutability, .pure); -// try testing.expectEqual(ParamType{ .string = {} }, function.outputs[0].type); -// try testing.expectEqualStrings("baz", function.outputs[0].name); -// } -// -// test "Abi" { -// const slice = "function Foo(bytes foo, address bar) public pure returns (string baz)"; -// const signature = try parseHumanReadable(abi.Abi, testing.allocator, slice); -// defer signature.deinit(); -// -// const function = signature.value[0].abiFunction; -// try testing.expectEqual(function.type, .function); -// try testing.expectEqual(ParamType{ .bytes = {} }, function.inputs[0].type); -// try testing.expectEqualStrings("foo", function.inputs[0].name); -// try testing.expectEqual(ParamType{ .address = {} }, function.inputs[1].type); -// try testing.expectEqualStrings("bar", function.inputs[1].name); -// try testing.expectEqual(function.stateMutability, .pure); -// try testing.expectEqual(ParamType{ .string = {} }, function.outputs[0].type); -// try testing.expectEqualStrings("baz", function.outputs[0].name); -// } -// -// test "Abi with struct" { -// const slice = -// \\struct Foo {address bar; string baz;} -// \\function Fizz(Foo buzz) public pure returns (string baz) -// ; -// -// const signature = try parseHumanReadable(abi.Abi, testing.allocator, slice); -// defer signature.deinit(); -// -// const function = signature.value[0].abiFunction; -// try testing.expectEqual(function.type, .function); -// try testing.expectEqualStrings("Fizz", function.name); -// try testing.expectEqual(ParamType{ .tuple = {} }, function.inputs[0].type); -// try testing.expectEqualStrings("buzz", function.inputs[0].name); -// try testing.expectEqual(ParamType{ .address = {} }, function.inputs[0].components.?[0].type); -// try testing.expectEqual(ParamType{ .string = {} }, function.inputs[0].components.?[1].type); -// try testing.expectEqualStrings("bar", function.inputs[0].components.?[0].name); -// try testing.expectEqualStrings("baz", function.inputs[0].components.?[1].name); -// try testing.expectEqual(function.stateMutability, .pure); -// try testing.expectEqual(ParamType{ .string = {} }, function.outputs[0].type); -// try testing.expectEqualStrings("baz", function.outputs[0].name); -// } -// -// test "Abi with nested struct" { -// const slice = -// \\struct Foo {address bar; string baz;} -// \\struct Bar {Foo foo;} -// \\function Fizz(Bar bar) public pure returns (Foo foo) -// ; -// -// const signature = try parseHumanReadable(abi.Abi, testing.allocator, slice); -// defer signature.deinit(); -// -// const function = signature.value[0].abiFunction; -// try testing.expectEqual(function.type, .function); -// try testing.expectEqualStrings("Fizz", function.name); -// try testing.expectEqual(ParamType{ .tuple = {} }, function.inputs[0].type); -// try testing.expectEqualStrings("bar", function.inputs[0].name); -// try testing.expectEqual(ParamType{ .tuple = {} }, function.inputs[0].components.?[0].type); -// try testing.expectEqualStrings("foo", function.inputs[0].components.?[0].name); -// try testing.expectEqual(ParamType{ .address = {} }, function.inputs[0].components.?[0].components.?[0].type); -// try testing.expectEqual(ParamType{ .string = {} }, function.inputs[0].components.?[0].components.?[1].type); -// try testing.expectEqualStrings("bar", function.inputs[0].components.?[0].components.?[0].name); -// try testing.expectEqualStrings("baz", function.inputs[0].components.?[0].components.?[1].name); -// try testing.expectEqual(function.stateMutability, .pure); -// try testing.expectEqual(ParamType{ .address = {} }, function.outputs[0].components.?[0].type); -// try testing.expectEqual(ParamType{ .string = {} }, function.outputs[0].components.?[1].type); -// try testing.expectEqualStrings("bar", function.outputs[0].components.?[0].name); -// try testing.expectEqualStrings("baz", function.outputs[0].components.?[1].name); -// } - test "Seaport" { const slice = \\constructor(address conduitController) @@ -443,56 +105,3 @@ test "Seaport" { try testing.expectEqualSlices(param.AbiParameter, &.{}, last.inputs); try testing.expectEqualStrings("UnusedItemParameters", last.name); } - -// test "Parsing errors parameters" { -// try testing.expectError(error.UnexceptedToken, parseHumanReadable([]const param.AbiParameter, testing.allocator, "adddress foo")); -// try testing.expectError(error.UnexceptedToken, parseHumanReadable([]const param.AbiParameter, testing.allocator, "address foo,")); -// try testing.expectError(error.InvalidDataLocation, parseHumanReadable([]const param.AbiParameter, testing.allocator, "(address calldata foo)")); -// try testing.expectError(error.InvalidDataLocation, parseHumanReadable([]const param.AbiParameter, testing.allocator, "address indexed foo")); -// try testing.expectError(error.InvalidDataLocation, parseHumanReadable([]const param.AbiParameter, testing.allocator, "address calldata foo")); -// try testing.expectError(error.InvalidDataLocation, parseHumanReadable([]const param.AbiParameter, testing.allocator, "address storage foo")); -// try testing.expectError(error.InvalidDataLocation, parseHumanReadable([]const param.AbiParameter, testing.allocator, "address memory foo")); -// try testing.expectError(error.InvalidDataLocation, parseHumanReadable([]const param.AbiEventParameter, testing.allocator, "address[] storage foo")); -// try testing.expectError(error.ExpectedCommaAfterParam, parseHumanReadable([]const param.AbiParameter, testing.allocator, "address foo.")); -// try testing.expectError(error.UnexceptedToken, parseHumanReadable([]const param.AbiParameter, testing.allocator, "(((address))")); -// } -// -// test "Parsing errors signatures" { -// try testing.expectError(error.UnexceptedToken, parseHumanReadable(abi.Constructor, testing.allocator, "function foo()")); -// try testing.expectError(error.UnexceptedToken, parseHumanReadable(abi.Abi, testing.allocator, "function foo(address)) view")); -// try testing.expectError(error.UnexceptedToken, parseHumanReadable(abi.Abi, testing.allocator, "function foo(address) nonpayable")); -// try testing.expectError(error.InvalidDataLocation, parseHumanReadable(abi.Abi, testing.allocator, "function foo(((((address indexed foo))))) view return(bool)")); -// try testing.expectError(error.EmptyReturnParams, parseHumanReadable(abi.Abi, testing.allocator, "function foo(((((address foo))))) view returns()")); -// } -// -// test "Match snapshot" { -// const expected = -// \\{ -// \\ "type": "function", -// \\ "inputs": [ -// \\ { -// \\ "name": "bar", -// \\ "type": "address[]" -// \\ } -// \\ ], -// \\ "name": "foo", -// \\ "outputs": [], -// \\ "stateMutability": "nonpayable" -// \\} -// ; -// -// try testSnapshot(abi.Function, expected, "function foo(address[] bar)"); -// } -// -// fn testSnapshot(comptime T: type, expected: []const u8, source: [:0]const u8) !void { -// const value = try parseHumanReadable(T, testing.allocator, source); -// defer value.deinit(); -// -// var out_buf: [1024]u8 = undefined; -// var slice_stream = std.io.fixedBufferStream(&out_buf); -// const out = slice_stream.writer(); -// -// try std.json.stringify(value.value, .{ .whitespace = .indent_2, .emit_null_optional_fields = false }, out); -// -// try testing.expectEqualStrings(expected, slice_stream.getWritten()); -// } From 21e822af777dbafd2a09852ad5fab37078c9503c Mon Sep 17 00:00:00 2001 From: Raiden1411 <67233402+Raiden1411@users.noreply.github.com> Date: Sun, 6 Oct 2024 22:59:23 +0100 Subject: [PATCH 15/18] human: some optimizations --- src/ast/Ast.zig | 6 ++++++ src/human-readable/Ast.zig | 27 +++++++++++++++++++-------- src/human-readable/HumanAbi.zig | 8 +++++--- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/ast/Ast.zig b/src/ast/Ast.zig index 09a4adfe..d1943105 100644 --- a/src/ast/Ast.zig +++ b/src/ast/Ast.zig @@ -78,8 +78,14 @@ pub fn parse(allocator: Allocator, source: [:0]const u8) Parser.ParserErrors!Ast }; defer parser.deinit(); + try parser.nodes.ensureTotalCapacity(allocator, tokens.len + 1); + try parser.parseSource(); + parser.nodes.shrinkAndFree(allocator, parser.nodes.len); + parser.extra_data.shrinkAndFree(allocator, parser.extra_data.items.len); + parser.errors.shrinkAndFree(allocator, parser.errors.items.len); + return .{ .source = source, .tokens = tokens.toOwnedSlice(), diff --git a/src/human-readable/Ast.zig b/src/human-readable/Ast.zig index 45bcaa54..96e1d810 100644 --- a/src/human-readable/Ast.zig +++ b/src/human-readable/Ast.zig @@ -37,14 +37,20 @@ pub fn parse(allocator: Allocator, source: [:0]const u8) Parser.ParserErrors!Ast var tokens: TokenList = .{}; var lexer = tokenizer.Lexer.init(source); - while (true) { - const tok = lexer.scan(); - try tokens.append(allocator, .{ - .tag = tok.syntax, - .start = @intCast(tok.location.start), - }); - - if (tok.syntax == .EndOfFileToken) break; + outer: while (true) { + const bytes_left = lexer.currentText.len - lexer.position; + const estimated = @max(64, bytes_left / 8); + try tokens.ensureUnusedCapacity(allocator, estimated); + + for (0..estimated) |_| { + const scan = lexer.scan(); + tokens.appendAssumeCapacity(.{ + .tag = scan.syntax, + .start = @intCast(scan.location.start), + }); + + if (scan.syntax == .EndOfFileToken) break :outer; + } } var parser: Parser = .{ @@ -58,8 +64,13 @@ pub fn parse(allocator: Allocator, source: [:0]const u8) Parser.ParserErrors!Ast }; defer parser.deinit(); + try parser.nodes.ensureTotalCapacity(allocator, tokens.len + 1); + try parser.parseSource(); + parser.nodes.shrinkAndFree(allocator, parser.nodes.len); + parser.extra.shrinkAndFree(allocator, parser.extra.items.len); + return .{ .source = source, .tokens = tokens.toOwnedSlice(), diff --git a/src/human-readable/HumanAbi.zig b/src/human-readable/HumanAbi.zig index d280d77b..898406aa 100644 --- a/src/human-readable/HumanAbi.zig +++ b/src/human-readable/HumanAbi.zig @@ -50,7 +50,7 @@ pub fn parse(arena: Allocator, source: [:0]const u8) Errors!Abi { .ast = &ast, .struct_params = .empty, }; - errdefer abi_gen.struct_params.deinit(arena); + defer abi_gen.struct_params.deinit(arena); return abi_gen.toAbi(); } @@ -59,7 +59,7 @@ pub fn toAbi(self: *HumanAbi) (HumanAbiErrors || error{ UnexpectedNode, Unexpect const nodes = self.ast.nodes.items(.tag); const data = self.ast.nodes.items(.data); - var list = std.ArrayList(AbiItem).init(self.allocator); + var list = try std.ArrayList(AbiItem).initCapacity(self.allocator, self.ast.nodes.len); errdefer list.deinit(); for (nodes, 0..) |node, index| { @@ -77,7 +77,7 @@ pub fn toAbi(self: *HumanAbi) (HumanAbiErrors || error{ UnexpectedNode, Unexpect .fallback_proto_multi, .fallback_proto_simple, .receive_proto, - => try list.append(try self.toAbiItem(@intCast(index))), + => list.appendAssumeCapacity(try self.toAbiItem(@intCast(index))), .struct_decl_one, => { const struct_param = try self.toStructComponentsOne(@intCast(index)); @@ -96,6 +96,8 @@ pub fn toAbi(self: *HumanAbi) (HumanAbiErrors || error{ UnexpectedNode, Unexpect } } + list.shrinkAndFree(list.items.len); + return list.toOwnedSlice(); } From 28db366a06d3d26c6dc4ee95f0b9a43f41d755ef Mon Sep 17 00:00:00 2001 From: Raiden1411 <67233402+Raiden1411@users.noreply.github.com> Date: Mon, 7 Oct 2024 14:58:34 +0100 Subject: [PATCH 16/18] human: add doc comments and generate documentation --- build/docs_generate.zig | 12 +- docs/pages/api/abi/param_type.md | 16 + docs/pages/api/human-readable/Ast.md | 711 +++++++++++++++++++ docs/pages/api/human-readable/HumanAbi.md | 249 +++++++ docs/pages/api/human-readable/Parser.md | 265 ++++--- docs/pages/api/human-readable/abi_parsing.md | 2 +- src/human-readable/Ast.zig | 48 +- src/human-readable/HumanAbi.zig | 52 +- src/human-readable/Parser.zig | 91 ++- 9 files changed, 1267 insertions(+), 179 deletions(-) create mode 100644 docs/pages/api/human-readable/Ast.md create mode 100644 docs/pages/api/human-readable/HumanAbi.md diff --git a/build/docs_generate.zig b/build/docs_generate.zig index a586fdbe..751e937c 100644 --- a/build/docs_generate.zig +++ b/build/docs_generate.zig @@ -201,13 +201,16 @@ pub const DocsGenerator = struct { .keyword_enum => try out_file.writeAll("enum {\n"), .keyword_struct => try out_file.writeAll("struct {\n"), .keyword_union => try out_file.writeAll("union(enum) {\n"), - else => std.debug.panic("Unexpected node token found: {s}", .{@tagName(self.tokens[container_token])}), + else => std.debug.panic("Unexpected token found: {s}", .{@tagName(self.tokens[container_token])}), } for (container.ast.members) |member| { switch (self.nodes[member]) { .container_field_init => try self.extractFromContainerField(out_file, member), - .fn_decl, .simple_var_decl => continue, + .fn_decl, + .simple_var_decl, + .@"comptime", + => continue, else => std.debug.panic("Unexpected node token found: {s}", .{@tagName(self.nodes[member])}), } } @@ -217,7 +220,9 @@ pub const DocsGenerator = struct { switch (self.nodes[member]) { .fn_decl => try self.extractFromFnProto(member, out_file, duplicate), .simple_var_decl => try self.extractFromSimpleVar(out_file, member, duplicate), - .container_field_init => continue, + .container_field_init, + .@"comptime", + => continue, else => std.debug.panic("Unexpected node token found: {s}", .{@tagName(self.nodes[member])}), } } @@ -358,6 +363,7 @@ pub const DocsGenerator = struct { .number_literal, .sub, .string_literal, + .@"comptime", => return, else => std.debug.panic("Unexpected token found: {s}\n", .{@tagName(self.nodes[variable.ast.init_node])}), } diff --git a/docs/pages/api/abi/param_type.md b/docs/pages/api/abi/param_type.md index 14e5b300..395883d5 100644 --- a/docs/pages/api/abi/param_type.md +++ b/docs/pages/api/abi/param_type.md @@ -41,6 +41,15 @@ union(enum) { } ``` +### FromHumanReadableTokenTag +Converts a human readable token into `ParamType`. + +### Signature + +```zig +pub fn fromHumanReadableTokenTag(tag: TokenTags) ?ParamType +``` + ### FreeArrayParamType User must call this if the union type contains a fixedArray or dynamicArray field. They create pointers so they must be destroyed after. @@ -82,3 +91,10 @@ or call the destroy method on your allocator manually pub fn typeToUnion(abitype: []const u8, alloc: Allocator) ParamErrors!ParamType ``` +### TypeToUnionWithTag +### Signature + +```zig +pub fn typeToUnionWithTag(allocator: Allocator, abitype: []const u8, token_tag: TokenTags) ParamErrors!ParamType +``` + diff --git a/docs/pages/api/human-readable/Ast.md b/docs/pages/api/human-readable/Ast.md new file mode 100644 index 00000000..aa5304de --- /dev/null +++ b/docs/pages/api/human-readable/Ast.md @@ -0,0 +1,711 @@ +## Offset + +Offset used in the parser. + +```zig +u32 +``` + +## TokenIndex + +Index used for the parser. + +```zig +u32 +``` + +## NodeList + +Struct of arrays for the `Node` members. + +```zig +std.MultiArrayList(Node) +``` + +## TokenList + +Struct of arrays for the `Token.Tag` members. + +```zig +std.MultiArrayList(struct { + tag: TokenTag, + start: Offset, +}) +``` + +## Parse +Parses the source and build the Ast based on it. + +### Signature + +```zig +pub fn parse(allocator: Allocator, source: [:0]const u8) Parser.ParserErrors!Ast +``` + +## Deinit +Clears any allocated memory from the `Ast`. + +### Signature + +```zig +pub fn deinit(self: *Ast, allocator: Allocator) void +``` + +## FunctionProto +Build the ast representation for a `function_proto` node. + +### Signature + +```zig +pub fn functionProto(self: Ast, node: Node.Index) ast.FunctionDecl +``` + +## FunctionProtoOne +Build the ast representation for a `function_proto_one` node. + +### Signature + +```zig +pub fn functionProtoOne(self: Ast, node_buffer: *[1]Node.Index, node: Node.Index) ast.FunctionDecl +``` + +## FunctionProtoMulti +Build the ast representation for a `function_proto_multi` node. + +### Signature + +```zig +pub fn functionProtoMulti(self: Ast, node: Node.Index) ast.FunctionDecl +``` + +## FunctionProtoSimple +Build the ast representation for a `function_proto_simple` node. + +### Signature + +```zig +pub fn functionProtoSimple(self: Ast, node_buffer: *[1]Node.Index, node: Node.Index) ast.FunctionDecl +``` + +## ReceiveProto +Build the ast representation for a `receive_proto` node. + +### Signature + +```zig +pub fn receiveProto(self: Ast, node: Node.Index) ast.ReceiveDecl +``` + +## FallbackProtoMulti +Build the ast representation for a `fallback_proto_multi` node. + +### Signature + +```zig +pub fn fallbackProtoMulti(self: Ast, node: Node.Index) ast.FallbackDecl +``` + +## FallbackProtoSimple +Build the ast representation for a `fallback_proto_simple` node. + +### Signature + +```zig +pub fn fallbackProtoSimple(self: Ast, node_buffer: *[1]Node.Index, node: Node.Index) ast.FallbackDecl +``` + +## ConstructorProtoMulti +Build the ast representation for a `constructor_proto_multi` node. + +### Signature + +```zig +pub fn constructorProtoMulti(self: Ast, node: Node.Index) ast.ConstructorDecl +``` + +## ConstructorProtoSimple +Build the ast representation for a `constructor_proto_simple` node. + +### Signature + +```zig +pub fn constructorProtoSimple(self: Ast, node_buffer: *[1]Node.Index, node: Node.Index) ast.ConstructorDecl +``` + +## EventProtoMulti +Build the ast representation for a `event_proto_multi` node. + +### Signature + +```zig +pub fn eventProtoMulti(self: Ast, node: Node.Index) ast.EventDecl +``` + +## EventProtoSimple +Build the ast representation for a `event_proto_simple` node. + +### Signature + +```zig +pub fn eventProtoSimple(self: Ast, node_buffer: *[1]Node.Index, node: Node.Index) ast.EventDecl +``` + +## ErrorProtoMulti +Build the ast representation for a `error_proto_multi` node. + +### Signature + +```zig +pub fn errorProtoMulti(self: Ast, node: Node.Index) ast.ErrorDecl +``` + +## ErrorProtoSimple +Build the ast representation for a `error_proto_simple` node. + +### Signature + +```zig +pub fn errorProtoSimple(self: Ast, node_buffer: *[1]Node.Index, node: Node.Index) ast.ErrorDecl +``` + +## StructDecl +Build the ast representation for a `struct_decl` node. + +### Signature + +```zig +pub fn structDecl(self: Ast, node: Node.Index) ast.StructDecl +``` + +## StructDeclOne +Build the ast representation for a `struct_decl_one` node. + +### Signature + +```zig +pub fn structDeclOne(self: Ast, node_buffer: *[1]Node.Index, node: Node.Index) ast.StructDecl +``` + +## ExtraData +Converts the data in `extra_data` into `T`. + +### Signature + +```zig +pub fn extraData(self: Ast, comptime T: type, node: Node.Index) T +``` + +## FirstToken +Finds the first `TokenIndex` based on the provided node. + +### Signature + +```zig +pub fn firstToken(self: Ast, node: Node.Index) TokenIndex +``` + +## LastToken +Finds the last `TokenIndex` based on the provided node. + +### Signature + +```zig +pub fn lastToken(self: Ast, node: Node.Index) TokenIndex +``` + +## TokenSlice +Takes the associated token slice based on the provided token index. + +### Signature + +```zig +pub fn tokenSlice(self: Ast, token_index: TokenIndex) []const u8 +``` + +## GetNodeSource +Gets the source code associated with the provided node. + +### Signature + +```zig +pub fn getNodeSource(self: Ast, node: Node.Index) []const u8 +``` + +## ast + +Ast representation of some of the "main" nodes. + +### Properties + +```zig +struct { +} +``` + +## ReceiveDecl + +### Properties + +```zig +struct { + main_token: TokenIndex + view: ?TokenIndex + pure: ?TokenIndex + payable: ?TokenIndex + public: ?TokenIndex + external: ?TokenIndex + virtual: ?TokenIndex + override: ?TokenIndex +} +``` + +## FallbackDecl + +### Properties + +```zig +struct { + ast: ComponentDecl + main_token: TokenIndex + view: ?TokenIndex + pure: ?TokenIndex + payable: ?TokenIndex + public: ?TokenIndex + external: ?TokenIndex + virtual: ?TokenIndex + override: ?TokenIndex +} +``` + +## ConstructorDecl + +### Properties + +```zig +struct { + ast: ComponentDecl + main_token: TokenIndex + view: ?TokenIndex + pure: ?TokenIndex + payable: ?TokenIndex + public: ?TokenIndex + external: ?TokenIndex + virtual: ?TokenIndex + override: ?TokenIndex +} +``` + +## FunctionDecl + +### Properties + +```zig +struct { + ast: ComponentDecl + main_token: TokenIndex + name: TokenIndex + view: ?TokenIndex + pure: ?TokenIndex + payable: ?TokenIndex + public: ?TokenIndex + external: ?TokenIndex + virtual: ?TokenIndex + override: ?TokenIndex +} +``` + +## ErrorDecl + +### Properties + +```zig +struct { + ast: ComponentDecl + main_token: TokenIndex + name: TokenIndex +} +``` + +## EventDecl + +### Properties + +```zig +struct { + ast: ComponentDecl + main_token: TokenIndex + name: TokenIndex + anonymous: ?TokenIndex +} +``` + +## StructDecl + +### Properties + +```zig +struct { + ast: ComponentDecl + main_token: TokenIndex + name: TokenIndex +} +``` + +## ReceiveDecl + +### Properties + +```zig +struct { + main_token: TokenIndex + view: ?TokenIndex + pure: ?TokenIndex + payable: ?TokenIndex + public: ?TokenIndex + external: ?TokenIndex + virtual: ?TokenIndex + override: ?TokenIndex +} +``` + +## FallbackDecl + +### Properties + +```zig +struct { + ast: ComponentDecl + main_token: TokenIndex + view: ?TokenIndex + pure: ?TokenIndex + payable: ?TokenIndex + public: ?TokenIndex + external: ?TokenIndex + virtual: ?TokenIndex + override: ?TokenIndex +} +``` + +## ConstructorDecl + +### Properties + +```zig +struct { + ast: ComponentDecl + main_token: TokenIndex + view: ?TokenIndex + pure: ?TokenIndex + payable: ?TokenIndex + public: ?TokenIndex + external: ?TokenIndex + virtual: ?TokenIndex + override: ?TokenIndex +} +``` + +## FunctionDecl + +### Properties + +```zig +struct { + ast: ComponentDecl + main_token: TokenIndex + name: TokenIndex + view: ?TokenIndex + pure: ?TokenIndex + payable: ?TokenIndex + public: ?TokenIndex + external: ?TokenIndex + virtual: ?TokenIndex + override: ?TokenIndex +} +``` + +## ErrorDecl + +### Properties + +```zig +struct { + ast: ComponentDecl + main_token: TokenIndex + name: TokenIndex +} +``` + +## EventDecl + +### Properties + +```zig +struct { + ast: ComponentDecl + main_token: TokenIndex + name: TokenIndex + anonymous: ?TokenIndex +} +``` + +## StructDecl + +### Properties + +```zig +struct { + ast: ComponentDecl + main_token: TokenIndex + name: TokenIndex +} +``` + +## Node + +Ast Node representation. + +### Properties + +```zig +struct { + /// Associated tag of the node + tag: Tag + /// The node or token index of the `lhs` and `rhs` fields. + data: Data + /// The main token index associated with the node. + main_token: TokenIndex +} +``` + +## Index + +Index type into the slice. + +```zig +u32 +``` + +## Tag + +Enum of all of the possible node tags. + +### Properties + +```zig +enum { + root + struct_type + unreachable_node + constructor_proto_simple + constructor_proto_multi + fallback_proto_simple + fallback_proto_multi + receive_proto + event_proto_simple + event_proto_multi + error_proto_simple + error_proto_multi + function_proto + function_proto_one + function_proto_multi + function_proto_simple + array_type + elementary_type + tuple_type + tuple_type_one + specifiers + struct_decl + struct_decl_one + struct_field + var_decl + error_var_decl + event_var_decl +} +``` + +## Data + +### Properties + +```zig +struct { + lhs: Index + rhs: Index +} +``` + +## Range + +### Properties + +```zig +struct { + start: Index + end: Index +} +``` + +## FunctionProto + +### Properties + +```zig +struct { + specifiers: Node.Index + identifier: TokenIndex + params_start: Node.Index + params_end: Node.Index +} +``` + +## FunctionProtoOne + +### Properties + +```zig +struct { + specifiers: Node.Index + identifier: TokenIndex + param: Node.Index +} +``` + +## FunctionProtoMulti + +### Properties + +```zig +struct { + identifier: TokenIndex + params_start: Node.Index + params_end: Node.Index +} +``` + +## FunctionProtoSimple + +### Properties + +```zig +struct { + identifier: TokenIndex + param: Node.Index +} +``` + +## Index + +Index type into the slice. + +```zig +u32 +``` + +## Tag + +Enum of all of the possible node tags. + +### Properties + +```zig +enum { + root + struct_type + unreachable_node + constructor_proto_simple + constructor_proto_multi + fallback_proto_simple + fallback_proto_multi + receive_proto + event_proto_simple + event_proto_multi + error_proto_simple + error_proto_multi + function_proto + function_proto_one + function_proto_multi + function_proto_simple + array_type + elementary_type + tuple_type + tuple_type_one + specifiers + struct_decl + struct_decl_one + struct_field + var_decl + error_var_decl + event_var_decl +} +``` + +## Data + +### Properties + +```zig +struct { + lhs: Index + rhs: Index +} +``` + +## Range + +### Properties + +```zig +struct { + start: Index + end: Index +} +``` + +## FunctionProto + +### Properties + +```zig +struct { + specifiers: Node.Index + identifier: TokenIndex + params_start: Node.Index + params_end: Node.Index +} +``` + +## FunctionProtoOne + +### Properties + +```zig +struct { + specifiers: Node.Index + identifier: TokenIndex + param: Node.Index +} +``` + +## FunctionProtoMulti + +### Properties + +```zig +struct { + identifier: TokenIndex + params_start: Node.Index + params_end: Node.Index +} +``` + +## FunctionProtoSimple + +### Properties + +```zig +struct { + identifier: TokenIndex + param: Node.Index +} +``` + diff --git a/docs/pages/api/human-readable/HumanAbi.md b/docs/pages/api/human-readable/HumanAbi.md new file mode 100644 index 00000000..7f6ce3f9 --- /dev/null +++ b/docs/pages/api/human-readable/HumanAbi.md @@ -0,0 +1,249 @@ +## HumanAbiErrors + +Set of errors when converting to the abi + +```zig +ParamErrors || Allocator.Error || error{ NoSpaceLeft, MissingTypeDeclaration } +``` + +## Errors + +Set of erros when generating the ABI + +```zig +HumanAbiErrors || Parser.ParserErrors || error{ UnexpectedMutability, UnexpectedNode } +``` + +## Parse +Parses the source, builds the Ast and generates the ABI. + +It's recommend to use an `ArenaAllocator` for this. + +### Signature + +```zig +pub fn parse(arena: Allocator, source: [:0]const u8) Errors!Abi +``` + +## ToAbi +Generates the `Abi` from the ast nodes. + +### Signature + +```zig +pub fn toAbi(self: *HumanAbi) (HumanAbiErrors || error{ UnexpectedNode, UnexpectedMutability })!Abi +``` + +## ToAbiItem +Generates an `AbiItem` based on the provided node. Not all nodes are supported. + +### Signature + +```zig +pub fn toAbiItem(self: HumanAbi, node: Node.Index) (HumanAbiErrors || error{ UnexpectedNode, UnexpectedMutability })!AbiItem +``` + +## ToAbiFunction +Generates a `AbiFunction` from a `function_proto`. + +### Signature + +```zig +pub fn toAbiFunction(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiFunction +``` + +## ToAbiFunctionOne +Generates a `AbiFunction` from a `function_proto_one`. + +### Signature + +```zig +pub fn toAbiFunctionOne(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiFunction +``` + +## ToAbiFunctionMulti +Generates a `AbiFunction` from a `function_proto_multi`. + +### Signature + +```zig +pub fn toAbiFunctionMulti(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiFunction +``` + +## ToAbiFunctionSimple +Generates a `AbiFunction` from a `function_proto_simple`. + +### Signature + +```zig +pub fn toAbiFunctionSimple(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiFunction +``` + +## ToStructComponents +Generates a `AbiParameter` as a tuple with the components. + +It gets generated from a `struct_decl` node. + +### Signature + +```zig +pub fn toStructComponents(self: HumanAbi, node: Node.Index) HumanAbiErrors![]const AbiParameter +``` + +## ToStructComponentsOne +Generates a `AbiParameter` as a tuple with the components. + +It gets generated from a `struct_decl_one` node. + +### Signature + +```zig +pub fn toStructComponentsOne(self: HumanAbi, node: Node.Index) HumanAbiErrors![]const AbiParameter +``` + +## ToAbiConstructorMulti +Generates a `AbiConstructor` from a `constructor_proto_multi`. + +### Signature + +```zig +pub fn toAbiConstructorMulti(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiConstructor +``` + +## ToAbiConstructorSimple +Generates a `AbiConstructor` from a `constructor_proto_simple`. + +### Signature + +```zig +pub fn toAbiConstructorSimple(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiConstructor +``` + +## ToAbiEventMulti +Generates a `AbiEvent` from a `event_proto_multi`. + +### Signature + +```zig +pub fn toAbiEventMulti(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiEvent +``` + +## ToAbiEventSimple +Generates a `AbiEvent` from a `event_proto_simple`. + +### Signature + +```zig +pub fn toAbiEventSimple(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiEvent +``` + +## ToAbiErrorMulti +Generates a `AbiError` from a `error_proto_multi`. + +### Signature + +```zig +pub fn toAbiErrorMulti(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiError +``` + +## ToAbiErrorSimple +Generates a `AbiError` from a `error_proto_simple`. + +### Signature + +```zig +pub fn toAbiErrorSimple(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiError +``` + +## ToAbiParameters +Generates a `[]const AbiParameter` from a slice of `var_decl`. + +### Signature + +```zig +pub fn toAbiParameters(self: HumanAbi, nodes: []const Node.Index) HumanAbiErrors![]const AbiParameter +``` + +## ToAbiParametersFromDecl +Generates a `[]const AbiEventParameter` from a slice of `struct_field` or `error_var_decl`. + +### Signature + +```zig +pub fn toAbiParametersFromDecl(self: HumanAbi, nodes: []const Node.Index) HumanAbiErrors![]const AbiParameter +``` + +## ToAbiEventParameters +Generates a `[]const AbiEventParameter` from a slice of `event_var_decl`. + +### Signature + +```zig +pub fn toAbiEventParameters(self: HumanAbi, nodes: []const Node.Index) HumanAbiErrors![]const AbiEventParameter +``` + +## ToAbiParameter +Generates a `AbiParameter` from a `var_decl`. + +### Signature + +```zig +pub fn toAbiParameter(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiParameter +``` + +## ToAbiComponents +Generates a `[]const AbiParameter` or in other words generates the tuple components. + +It is expecting the node to be a `tuple_type` or a `tuple_type_one`. + +### Signature + +```zig +pub fn toAbiComponents(self: HumanAbi, node: Node.Index) HumanAbiErrors![]const AbiParameter +``` + +## ToAbiEventParameter +Generates a `AbiEventParameter` from a `event_var_decl`. + +### Signature + +```zig +pub fn toAbiEventParameter(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiEventParameter +``` + +## ToAbiParameterFromDecl +Generates a `AbiParameter` from a `error_var_decl` or a `struct_field`. + +### Signature + +```zig +pub fn toAbiParameterFromDecl(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiParameter +``` + +## ToAbiFallbackMulti +Generates a `AbiFallback` from a `fallback_proto_multi`. + +### Signature + +```zig +pub fn toAbiFallbackMulti(self: HumanAbi, node: Node.Index) Allocator.Error!AbiFallback +``` + +## ToAbiFallbackSimple +Generates a `AbiFallback` from a `fallback_proto_simple`. + +### Signature + +```zig +pub fn toAbiFallbackSimple(self: HumanAbi, node: Node.Index) Allocator.Error!AbiFallback +``` + +## ToAbiReceive +Generates a `AbiReceive` from a `receive_proto`. + +### Signature + +```zig +pub fn toAbiReceive(self: HumanAbi, node: Node.Index) (Allocator.Error || error{UnexpectedMutability})!AbiReceive +``` + diff --git a/docs/pages/api/human-readable/Parser.md b/docs/pages/api/human-readable/Parser.md index 55f1445f..c211f93e 100644 --- a/docs/pages/api/human-readable/Parser.md +++ b/docs/pages/api/human-readable/Parser.md @@ -1,200 +1,273 @@ -## TokenList +## ParserErrors + +Errors that can happing whilest parsing the source code. + +```zig +error{ParsingError} || Allocator.Error +``` + +## Deinit +Clears any allocated memory. + +### Signature ```zig -std.MultiArrayList(struct { - token_type: Tokens, - start: u32, - end: u32, -}) +pub fn deinit(self: *Parser) void ``` -## ParseErrors +## ParseSource +Parses all of the source and build the `Ast`. -Set of possible errors that can happen while parsing. +### Signature ```zig -error{ - InvalidDataLocation, - UnexceptedToken, - InvalidType, - ExpectedCommaAfterParam, - EmptyReturnParams, -} || ParamErrors || Allocator.Error +pub fn parseSource(self: *Parser) ParserErrors!void ``` -## ParseAbiProto -Parse a string or a multi line string with solidity signatures.\ -This will return all signatures as a slice of `AbiItem`. +## ParseUnits +Parsers all of the solidity source unit values. -This supports parsing struct signatures if its intended to use -The struct signatures must be defined top down. +More info can be found [here](https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.sourceUnit) + +### Signature -**Example** ```zig -var lex = Lexer.init(source); +pub fn parseUnits(self: *Parser) ParserErrors!Node.Range +``` + +## ExpectUnit +Expects to find a source unit otherwise it will fail. -var list = Parser.TokenList{}; -defer list.deinit(allocator); +### Signature -while (true) { - const tok = lex.scan(); +```zig +pub fn expectUnit(self: *Parser) ParserErrors!Node.Index +``` - try list.append(allocator, .{ - .token_type = tok.syntax, - .start = tok.location.start, - .end = tok.location.end, - }); +## ParseUnit +Parses a single source unit. - if (tok.syntax == .EndOfFileToken) break; -} +More info can be found [here](https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.sourceUnit) -var parser: Parser = .{ - .alloc = allocator, - .tokens = list.items(.token_type), - .tokens_start = list.items(.start), - .tokens_end = list.items(.end), - .token_index = 0, - .source = source, - .structs = .{}, -}; +### Signature -const abi = try parser.parseAbiProto(); +```zig +pub fn parseUnit(self: *Parser) ParserErrors!Node.Index ``` +## ParseFunctionProto +Parses a solidity function accordingly to the language grammar. + ### Signature ```zig -pub fn parseAbiProto(p: *Parser) ParseErrors!Abi +pub fn parseFunctionProto(self: *Parser) ParserErrors!Node.Index ``` -## ParseAbiItemProto -Parse a single solidity signature based on expected tokens. +## ParseReceiveProto +Parses a solidity receive function accordingly to the language grammar. + +### Signature + +```zig +pub fn parseReceiveProto(self: *Parser) ParserErrors!Node.Index +``` + +## ParseFallbackProto +Parses a solidity fallback function accordingly to the language grammar. + +### Signature + +```zig +pub fn parseFallbackProto(self: *Parser) ParserErrors!Node.Index +``` + +## ParseConstructorProto +Parses a solidity constructor declaration accordingly to the language grammar. + +### Signature + +```zig +pub fn parseConstructorProto(self: *Parser) ParserErrors!Node.Index +``` + +## ParseSpecifiers +Parses all of the solidity mutability or visibility specifiers. + +### Signature + +```zig +pub fn parseSpecifiers(self: *Parser) ParserErrors!Node.Index +``` + +## ParseErrorProto +Parses a solidity error declaration accordingly to the language grammar. + +### Signature + +```zig +pub fn parseErrorProto(self: *Parser) ParserErrors!Node.Index +``` + +## ParseEventProto +Parses a solidity event declaration accordingly to the language grammar. + +### Signature + +```zig +pub fn parseEventProto(self: *Parser) ParserErrors!Node.Index +``` + +## ParseEventVarDecls +Parses the possible event declaration parameters according to the language grammar. + +### Signature + +```zig +pub fn parseEventVarDecls(self: *Parser) ParserErrors!Span +``` + +## ParseErrorVarDecls +Parses the possible error declaration parameters according to the language grammar. + +### Signature + +```zig +pub fn parseErrorVarDecls(self: *Parser) ParserErrors!Span +``` + +## ParseReturnParams +Parses the possible function declaration parameters according to the language grammar. + +### Signature + +```zig +pub fn parseReturnParams(self: *Parser) ParserErrors!Node.Range +``` + +## ParseVariableDecls +Parses the possible function declaration parameters according to the language grammar. + +### Signature + +```zig +pub fn parseVariableDecls(self: *Parser) ParserErrors!Span +``` -Will return an error if the token is not expected. +## ExpectErrorVarDecl +Expects to find a `error_var_decl`. Otherwise returns an error. ### Signature ```zig -pub fn parseAbiItemProto(p: *Parser) ParseErrors!AbiItem +pub fn expectErrorVarDecl(self: *Parser) ParserErrors!Node.Index ``` -## ParseFunctionFnProto -Parse single solidity function signature.\ -FunctionProto -> Function KEYWORD, Identifier, OpenParen, ParamDecls?, ClosingParen, Visibility?, StateMutability?, Returns? +## ParseErrorVarDecl +Parses the possible error declaration parameter according to the language grammar. ### Signature ```zig -pub fn parseFunctionFnProto(p: *Parser) ParseErrors!Function +pub fn parseErrorVarDecl(self: *Parser) ParserErrors!Node.Index ``` -## ParseEventFnProto -Parse single solidity event signature.\ -EventProto -> Event KEYWORD, Identifier, OpenParen, ParamDecls?, ClosingParen +## ExpectEventVarDecl +Expects to find a `event_var_decl`. Otherwise returns an error. ### Signature ```zig -pub fn parseEventFnProto(p: *Parser) ParseErrors!Event +pub fn expectEventVarDecl(self: *Parser) ParserErrors!Node.Index ``` -## ParseErrorFnProto -Parse single solidity error signature.\ -ErrorProto -> Error KEYWORD, Identifier, OpenParen, ParamDecls?, ClosingParen +## ParseEventVarDecl +Parses the possible event declaration parameter according to the language grammar. ### Signature ```zig -pub fn parseErrorFnProto(p: *Parser) ParseErrors!Error +pub fn parseEventVarDecl(self: *Parser) ParserErrors!Node.Index ``` -## ParseConstructorFnProto -Parse single solidity constructor signature.\ -ConstructorProto -> Constructor KEYWORD, OpenParen, ParamDecls?, ClosingParen, StateMutability? +## ExpectVarDecl +Expects to find a `var_decl`. Otherwise returns an error. ### Signature ```zig -pub fn parseConstructorFnProto(p: *Parser) ParseErrors!Constructor +pub fn expectVarDecl(self: *Parser) ParserErrors!Node.Index ``` -## ParseStructProto -Parse single solidity struct signature.\ -StructProto -> Struct KEYWORD, Identifier, OpenBrace, ParamDecls, ClosingBrace +## ParseVariableDecl +Parses the possible function declaration parameter according to the language grammar. ### Signature ```zig -pub fn parseStructProto(p: *Parser) ParseErrors!void +pub fn parseVariableDecl(self: *Parser) ParserErrors!Node.Index ``` -## ParseFallbackFnProto -Parse single solidity fallback signature.\ -FallbackProto -> Fallback KEYWORD, OpenParen, ClosingParen, StateMutability? +## ParseStructDecl +Parses a struct declaration according to the language grammar. ### Signature ```zig -pub fn parseFallbackFnProto(p: *Parser) error{UnexceptedToken}!Fallback +pub fn parseStructDecl(self: *Parser) ParserErrors!Node.Index ``` -## ParseReceiveFnProto -Parse single solidity receive signature.\ -ReceiveProto -> Receive KEYWORD, OpenParen, ClosingParen, External, Payable +## ParseStructFields +Parses all of the structs fields according to the language grammar. ### Signature ```zig -pub fn parseReceiveFnProto(p: *Parser) error{UnexceptedToken}!Receive +pub fn parseStructFields(self: *Parser) ParserErrors!Span ``` -## ParseFuncParamsDecl -Parse solidity function params.\ -TypeExpr, DataLocation?, Identifier?, Comma? +## ExpectStructField +Expects to find a struct parameter or fails. ### Signature ```zig -pub fn parseFuncParamsDecl(p: *Parser) ParseErrors![]const AbiParameter +pub fn expectStructField(self: *Parser) ParserErrors!Node.Index ``` -## ParseEventParamsDecl -Parse solidity event params.\ -TypeExpr, DataLocation?, Identifier?, Comma? +## ExpectType +Expects to find either a `elementary_type`, `tuple_type`, `tuple_type_one`, `array_type` or `struct_type` ### Signature ```zig -pub fn parseEventParamsDecl(p: *Parser) ParseErrors![]const AbiEventParameter +pub fn expectType(self: *Parser) ParserErrors!Node.Index ``` -## ParseErrorParamsDecl -Parse solidity error params.\ -TypeExpr, DataLocation?, Identifier?, Comma? +## ParseType +Parses the token into either a `elementary_type`, `tuple_type`, `tuple_type_one`, `array_type` or `struct_type` ### Signature ```zig -pub fn parseErrorParamsDecl(p: *Parser) ParseErrors![]const AbiParameter +pub fn parseType(self: *Parser) ParserErrors!Node.Index ``` -## ParseStructParamDecls -Parse solidity struct params.\ -TypeExpr, Identifier?, SemiColon +## ParseTupleType +Parses the tuple type similarly to `parseErrorVarDecls`. ### Signature ```zig -pub fn parseStructParamDecls(p: *Parser) ParseErrors![]const AbiParameter +pub fn parseTupleType(self: *Parser) ParserErrors!Node.Index ``` -## ParseTuple -Parse solidity tuple params.\ -OpenParen, TypeExpr, Identifier?, Comma?, ClosingParen +## ConsumeElementaryType +Creates a `elementary_type` node based on the solidity type keywords. ### Signature ```zig -pub fn parseTuple(p: *Parser, comptime T: type) ParseErrors!T +pub fn consumeElementaryType(self: *Parser) Allocator.Error!Node.Index ``` diff --git a/docs/pages/api/human-readable/abi_parsing.md b/docs/pages/api/human-readable/abi_parsing.md index 80d54482..fdba1380 100644 --- a/docs/pages/api/human-readable/abi_parsing.md +++ b/docs/pages/api/human-readable/abi_parsing.md @@ -25,6 +25,6 @@ tokens from the provided signature ### Signature ```zig -pub fn parseHumanReadable(comptime T: type, alloc: Allocator, source: [:0]const u8) Parser.ParseErrors!AbiParsed(T) +pub fn parseHumanReadable(alloc: Allocator, source: [:0]const u8) HumanAbi.Errors!AbiParsed(Abi) ``` diff --git a/src/human-readable/Ast.zig b/src/human-readable/Ast.zig index 96e1d810..7632507c 100644 --- a/src/human-readable/Ast.zig +++ b/src/human-readable/Ast.zig @@ -33,6 +33,7 @@ nodes: NodeList.Slice, /// Slice of extra data produces by the parser. extra_data: []const Node.Index, +/// Parses the source and build the Ast based on it. pub fn parse(allocator: Allocator, source: [:0]const u8) Parser.ParserErrors!Ast { var tokens: TokenList = .{}; var lexer = tokenizer.Lexer.init(source); @@ -85,7 +86,7 @@ pub fn deinit(self: *Ast, allocator: Allocator) void { self.nodes.deinit(allocator); allocator.free(self.extra_data); } - +/// Build the ast representation for a `function_proto` node. pub fn functionProto(self: Ast, node: Node.Index) ast.FunctionDecl { const nodes = self.nodes.items(.tag); std.debug.assert(nodes[node] == .function_proto); @@ -129,7 +130,7 @@ pub fn functionProto(self: Ast, node: Node.Index) ast.FunctionDecl { return result; } - +/// Build the ast representation for a `function_proto_one` node. pub fn functionProtoOne(self: Ast, node_buffer: *[1]Node.Index, node: Node.Index) ast.FunctionDecl { const nodes = self.nodes.items(.tag); std.debug.assert(nodes[node] == .function_proto_one); @@ -175,7 +176,7 @@ pub fn functionProtoOne(self: Ast, node_buffer: *[1]Node.Index, node: Node.Index return result; } - +/// Build the ast representation for a `function_proto_multi` node. pub fn functionProtoMulti(self: Ast, node: Node.Index) ast.FunctionDecl { const nodes = self.nodes.items(.tag); std.debug.assert(nodes[node] == .function_proto_multi); @@ -218,7 +219,7 @@ pub fn functionProtoMulti(self: Ast, node: Node.Index) ast.FunctionDecl { return result; } - +/// Build the ast representation for a `function_proto_simple` node. pub fn functionProtoSimple(self: Ast, node_buffer: *[1]Node.Index, node: Node.Index) ast.FunctionDecl { const nodes = self.nodes.items(.tag); std.debug.assert(nodes[node] == .function_proto_simple); @@ -262,7 +263,7 @@ pub fn functionProtoSimple(self: Ast, node_buffer: *[1]Node.Index, node: Node.In return result; } - +/// Build the ast representation for a `receive_proto` node. pub fn receiveProto(self: Ast, node: Node.Index) ast.ReceiveDecl { const nodes = self.nodes.items(.tag); std.debug.assert(nodes[node] == .receive_proto); @@ -299,7 +300,7 @@ pub fn receiveProto(self: Ast, node: Node.Index) ast.ReceiveDecl { return result; } - +/// Build the ast representation for a `fallback_proto_multi` node. pub fn fallbackProtoMulti(self: Ast, node: Node.Index) ast.FallbackDecl { const nodes = self.nodes.items(.tag); std.debug.assert(nodes[node] == .fallback_proto_multi); @@ -340,7 +341,7 @@ pub fn fallbackProtoMulti(self: Ast, node: Node.Index) ast.FallbackDecl { return result; } - +/// Build the ast representation for a `fallback_proto_simple` node. pub fn fallbackProtoSimple(self: Ast, node_buffer: *[1]Node.Index, node: Node.Index) ast.FallbackDecl { const nodes = self.nodes.items(.tag); std.debug.assert(nodes[node] == .fallback_proto_simple); @@ -381,7 +382,7 @@ pub fn fallbackProtoSimple(self: Ast, node_buffer: *[1]Node.Index, node: Node.In return result; } - +/// Build the ast representation for a `constructor_proto_multi` node. pub fn constructorProtoMulti(self: Ast, node: Node.Index) ast.ConstructorDecl { const nodes = self.nodes.items(.tag); std.debug.assert(nodes[node] == .constructor_proto_multi); @@ -422,7 +423,7 @@ pub fn constructorProtoMulti(self: Ast, node: Node.Index) ast.ConstructorDecl { return result; } - +/// Build the ast representation for a `constructor_proto_simple` node. pub fn constructorProtoSimple(self: Ast, node_buffer: *[1]Node.Index, node: Node.Index) ast.ConstructorDecl { const nodes = self.nodes.items(.tag); std.debug.assert(nodes[node] == .constructor_proto_simple); @@ -463,7 +464,7 @@ pub fn constructorProtoSimple(self: Ast, node_buffer: *[1]Node.Index, node: Node return result; } - +/// Build the ast representation for a `event_proto_multi` node. pub fn eventProtoMulti(self: Ast, node: Node.Index) ast.EventDecl { const nodes = self.nodes.items(.tag); std.debug.assert(nodes[node] == .event_proto_multi); @@ -481,7 +482,7 @@ pub fn eventProtoMulti(self: Ast, node: Node.Index) ast.EventDecl { .anonymous = null, }; } - +/// Build the ast representation for a `event_proto_simple` node. pub fn eventProtoSimple(self: Ast, node_buffer: *[1]Node.Index, node: Node.Index) ast.EventDecl { const nodes = self.nodes.items(.tag); std.debug.assert(nodes[node] == .event_proto_simple); @@ -499,7 +500,7 @@ pub fn eventProtoSimple(self: Ast, node_buffer: *[1]Node.Index, node: Node.Index .anonymous = null, }; } - +/// Build the ast representation for a `error_proto_multi` node. pub fn errorProtoMulti(self: Ast, node: Node.Index) ast.ErrorDecl { const nodes = self.nodes.items(.tag); std.debug.assert(nodes[node] == .error_proto_multi); @@ -516,7 +517,7 @@ pub fn errorProtoMulti(self: Ast, node: Node.Index) ast.ErrorDecl { .name = data.lhs, }; } - +/// Build the ast representation for a `error_proto_simple` node. pub fn errorProtoSimple(self: Ast, node_buffer: *[1]Node.Index, node: Node.Index) ast.ErrorDecl { const nodes = self.nodes.items(.tag); std.debug.assert(nodes[node] == .error_proto_simple); @@ -533,7 +534,7 @@ pub fn errorProtoSimple(self: Ast, node_buffer: *[1]Node.Index, node: Node.Index .name = data.lhs, }; } - +/// Build the ast representation for a `struct_decl` node. pub fn structDecl(self: Ast, node: Node.Index) ast.StructDecl { const nodes = self.nodes.items(.tag); std.debug.assert(nodes[node] == .struct_decl); @@ -550,7 +551,7 @@ pub fn structDecl(self: Ast, node: Node.Index) ast.StructDecl { .name = data.lhs, }; } - +/// Build the ast representation for a `struct_decl_one` node. pub fn structDeclOne(self: Ast, node_buffer: *[1]Node.Index, node: Node.Index) ast.StructDecl { const nodes = self.nodes.items(.tag); std.debug.assert(nodes[node] == .struct_decl_one); @@ -567,7 +568,7 @@ pub fn structDeclOne(self: Ast, node_buffer: *[1]Node.Index, node: Node.Index) a .name = data.lhs, }; } - +/// Converts the data in `extra_data` into `T`. pub fn extraData(self: Ast, comptime T: type, node: Node.Index) T { const fields = std.meta.fields(T); var result: T = undefined; @@ -580,7 +581,7 @@ pub fn extraData(self: Ast, comptime T: type, node: Node.Index) T { return result; } - +/// Finds the first `TokenIndex` based on the provided node. pub fn firstToken(self: Ast, node: Node.Index) TokenIndex { const main = self.nodes.items(.main_token); const data = self.nodes.items(.data); @@ -630,7 +631,7 @@ pub fn firstToken(self: Ast, node: Node.Index) TokenIndex { } } } - +/// Finds the last `TokenIndex` based on the provided node. pub fn lastToken(self: Ast, node: Node.Index) TokenIndex { const main = self.nodes.items(.main_token); const data = self.nodes.items(.data); @@ -763,7 +764,7 @@ pub fn lastToken(self: Ast, node: Node.Index) TokenIndex { } } } - +/// Takes the associated token slice based on the provided token index. pub fn tokenSlice(self: Ast, token_index: TokenIndex) []const u8 { const token_tag = self.tokens.items(.tag)[token_index]; const token_start = self.tokens.items(.start)[token_index]; @@ -781,7 +782,7 @@ pub fn tokenSlice(self: Ast, token_index: TokenIndex) []const u8 { return self.source[tok.location.start..tok.location.end]; } - +/// Gets the source code associated with the provided node. pub fn getNodeSource(self: Ast, node: Node.Index) []const u8 { const token_start = self.tokens.items(.start); @@ -794,6 +795,7 @@ pub fn getNodeSource(self: Ast, node: Node.Index) []const u8 { return self.source[start..end]; } +/// Ast representation of some of the "main" nodes. pub const ast = struct { pub const ReceiveDecl = struct { main_token: TokenIndex, @@ -888,11 +890,16 @@ pub const ast = struct { }; }; +/// Ast Node representation. pub const Node = struct { + /// Associated tag of the node tag: Tag, + /// The node or token index of the `lhs` and `rhs` fields. data: Data, + /// The main token index associated with the node. main_token: TokenIndex, + /// Index type into the slice. pub const Index = u32; // Assert that out tag is always size 1. @@ -900,6 +907,7 @@ pub const Node = struct { std.debug.assert(@sizeOf(Tag) == 1); } + /// Enum of all of the possible node tags. pub const Tag = enum { root, struct_type, diff --git a/src/human-readable/HumanAbi.zig b/src/human-readable/HumanAbi.zig index 898406aa..22e837fc 100644 --- a/src/human-readable/HumanAbi.zig +++ b/src/human-readable/HumanAbi.zig @@ -100,7 +100,7 @@ pub fn toAbi(self: *HumanAbi) (HumanAbiErrors || error{ UnexpectedNode, Unexpect return list.toOwnedSlice(); } - +/// Generates an `AbiItem` based on the provided node. Not all nodes are supported. pub fn toAbiItem(self: HumanAbi, node: Node.Index) (HumanAbiErrors || error{ UnexpectedNode, UnexpectedMutability })!AbiItem { const nodes = self.ast.nodes.items(.tag); @@ -121,7 +121,7 @@ pub fn toAbiItem(self: HumanAbi, node: Node.Index) (HumanAbiErrors || error{ Une else => error.UnexpectedNode, }; } - +/// Generates a `AbiFunction` from a `function_proto`. pub fn toAbiFunction(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiFunction { const nodes = self.ast.nodes.items(.tag); std.debug.assert(nodes[node] == .function_proto); @@ -153,7 +153,7 @@ pub fn toAbiFunction(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiFunctio .outputs = returns, }; } - +/// Generates a `AbiFunction` from a `function_proto_one`. pub fn toAbiFunctionOne(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiFunction { const nodes = self.ast.nodes.items(.tag); std.debug.assert(nodes[node] == .function_proto_one); @@ -186,7 +186,7 @@ pub fn toAbiFunctionOne(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiFunc .outputs = returns, }; } - +/// Generates a `AbiFunction` from a `function_proto_multi`. pub fn toAbiFunctionMulti(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiFunction { const nodes = self.ast.nodes.items(.tag); std.debug.assert(nodes[node] == .function_proto_multi); @@ -217,7 +217,7 @@ pub fn toAbiFunctionMulti(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiFu .outputs = &.{}, }; } - +/// Generates a `AbiFunction` from a `function_proto_simple`. pub fn toAbiFunctionSimple(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiFunction { const nodes = self.ast.nodes.items(.tag); std.debug.assert(nodes[node] == .function_proto_simple); @@ -249,7 +249,9 @@ pub fn toAbiFunctionSimple(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiF .outputs = &.{}, }; } - +/// Generates a `AbiParameter` as a tuple with the components. +/// +/// It gets generated from a `struct_decl` node. pub fn toStructComponents(self: HumanAbi, node: Node.Index) HumanAbiErrors![]const AbiParameter { const nodes = self.ast.nodes.items(.tag); std.debug.assert(nodes[node] == .struct_decl); @@ -260,7 +262,9 @@ pub fn toStructComponents(self: HumanAbi, node: Node.Index) HumanAbiErrors![]con return params; } - +/// Generates a `AbiParameter` as a tuple with the components. +/// +/// It gets generated from a `struct_decl_one` node. pub fn toStructComponentsOne(self: HumanAbi, node: Node.Index) HumanAbiErrors![]const AbiParameter { const nodes = self.ast.nodes.items(.tag); std.debug.assert(nodes[node] == .struct_decl_one); @@ -272,7 +276,7 @@ pub fn toStructComponentsOne(self: HumanAbi, node: Node.Index) HumanAbiErrors![] return params; } - +/// Generates a `AbiConstructor` from a `constructor_proto_multi`. pub fn toAbiConstructorMulti(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiConstructor { const nodes = self.ast.nodes.items(.tag); std.debug.assert(nodes[node] == .constructor_proto_multi); @@ -287,7 +291,7 @@ pub fn toAbiConstructorMulti(self: HumanAbi, node: Node.Index) HumanAbiErrors!Ab .stateMutability = if (ast_constructor.payable != null) .payable else .nonpayable, }; } - +/// Generates a `AbiConstructor` from a `constructor_proto_simple`. pub fn toAbiConstructorSimple(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiConstructor { const nodes = self.ast.nodes.items(.tag); std.debug.assert(nodes[node] == .constructor_proto_simple); @@ -303,7 +307,7 @@ pub fn toAbiConstructorSimple(self: HumanAbi, node: Node.Index) HumanAbiErrors!A .stateMutability = if (ast_constructor.payable != null) .payable else .nonpayable, }; } - +/// Generates a `AbiEvent` from a `event_proto_multi`. pub fn toAbiEventMulti(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiEvent { const nodes = self.ast.nodes.items(.tag); std.debug.assert(nodes[node] == .event_proto_multi); @@ -318,7 +322,7 @@ pub fn toAbiEventMulti(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiEvent .inputs = params, }; } - +/// Generates a `AbiEvent` from a `event_proto_simple`. pub fn toAbiEventSimple(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiEvent { const nodes = self.ast.nodes.items(.tag); std.debug.assert(nodes[node] == .event_proto_simple); @@ -334,7 +338,7 @@ pub fn toAbiEventSimple(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiEven .inputs = params, }; } - +/// Generates a `AbiError` from a `error_proto_multi`. pub fn toAbiErrorMulti(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiError { const nodes = self.ast.nodes.items(.tag); std.debug.assert(nodes[node] == .error_proto_multi); @@ -349,7 +353,7 @@ pub fn toAbiErrorMulti(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiError .inputs = params, }; } - +/// Generates a `AbiError` from a `error_proto_simple`. pub fn toAbiErrorSimple(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiError { const nodes = self.ast.nodes.items(.tag); std.debug.assert(nodes[node] == .error_proto_simple); @@ -365,7 +369,7 @@ pub fn toAbiErrorSimple(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiErro .inputs = params, }; } - +/// Generates a `[]const AbiParameter` from a slice of `var_decl`. pub fn toAbiParameters(self: HumanAbi, nodes: []const Node.Index) HumanAbiErrors![]const AbiParameter { var params = try std.ArrayList(AbiParameter).initCapacity(self.allocator, nodes.len); errdefer params.deinit(); @@ -376,7 +380,7 @@ pub fn toAbiParameters(self: HumanAbi, nodes: []const Node.Index) HumanAbiErrors return params.toOwnedSlice(); } - +/// Generates a `[]const AbiEventParameter` from a slice of `struct_field` or `error_var_decl`. pub fn toAbiParametersFromDecl(self: HumanAbi, nodes: []const Node.Index) HumanAbiErrors![]const AbiParameter { var params = try std.ArrayList(AbiParameter).initCapacity(self.allocator, nodes.len); errdefer params.deinit(); @@ -387,7 +391,7 @@ pub fn toAbiParametersFromDecl(self: HumanAbi, nodes: []const Node.Index) HumanA return params.toOwnedSlice(); } - +/// Generates a `[]const AbiEventParameter` from a slice of `event_var_decl`. pub fn toAbiEventParameters(self: HumanAbi, nodes: []const Node.Index) HumanAbiErrors![]const AbiEventParameter { var params = try std.ArrayList(AbiEventParameter).initCapacity(self.allocator, nodes.len); errdefer params.deinit(); @@ -398,7 +402,7 @@ pub fn toAbiEventParameters(self: HumanAbi, nodes: []const Node.Index) HumanAbiE return params.toOwnedSlice(); } - +/// Generates a `AbiParameter` from a `var_decl`. pub fn toAbiParameter(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiParameter { const nodes = self.ast.nodes.items(.tag); const data = self.ast.nodes.items(.data); @@ -489,7 +493,9 @@ pub fn toAbiParameter(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiParame else => unreachable, // Invalid Node. } } - +/// Generates a `[]const AbiParameter` or in other words generates the tuple components. +/// +/// It is expecting the node to be a `tuple_type` or a `tuple_type_one`. pub fn toAbiComponents(self: HumanAbi, node: Node.Index) HumanAbiErrors![]const AbiParameter { const nodes = self.ast.nodes.items(.tag); const data = self.ast.nodes.items(.data); @@ -509,7 +515,7 @@ pub fn toAbiComponents(self: HumanAbi, node: Node.Index) HumanAbiErrors![]const return components; } - +/// Generates a `AbiEventParameter` from a `event_var_decl`. pub fn toAbiEventParameter(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiEventParameter { const nodes = self.ast.nodes.items(.tag); const data = self.ast.nodes.items(.data); @@ -606,7 +612,7 @@ pub fn toAbiEventParameter(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiE else => unreachable, // Invalid Node. } } - +/// Generates a `AbiParameter` from a `error_var_decl` or a `struct_field`. pub fn toAbiParameterFromDecl(self: HumanAbi, node: Node.Index) HumanAbiErrors!AbiParameter { const nodes = self.ast.nodes.items(.tag); const data = self.ast.nodes.items(.data); @@ -697,7 +703,7 @@ pub fn toAbiParameterFromDecl(self: HumanAbi, node: Node.Index) HumanAbiErrors!A else => unreachable, // Invalid Node. } } - +/// Generates a `AbiFallback` from a `fallback_proto_multi`. pub fn toAbiFallbackMulti(self: HumanAbi, node: Node.Index) Allocator.Error!AbiFallback { const nodes = self.ast.nodes.items(.tag); std.debug.assert(nodes[node] == .fallback_proto_multi); @@ -709,7 +715,7 @@ pub fn toAbiFallbackMulti(self: HumanAbi, node: Node.Index) Allocator.Error!AbiF .stateMutability = if (ast_fallback.payable != null) .payable else .nonpayable, }; } - +/// Generates a `AbiFallback` from a `fallback_proto_simple`. pub fn toAbiFallbackSimple(self: HumanAbi, node: Node.Index) Allocator.Error!AbiFallback { const nodes = self.ast.nodes.items(.tag); std.debug.assert(nodes[node] == .fallback_proto_simple); @@ -722,7 +728,7 @@ pub fn toAbiFallbackSimple(self: HumanAbi, node: Node.Index) Allocator.Error!Abi .stateMutability = if (ast_fallback.payable != null) .payable else .nonpayable, }; } - +/// Generates a `AbiReceive` from a `receive_proto`. pub fn toAbiReceive(self: HumanAbi, node: Node.Index) (Allocator.Error || error{UnexpectedMutability})!AbiReceive { const nodes = self.ast.nodes.items(.tag); std.debug.assert(nodes[node] == .receive_proto); diff --git a/src/human-readable/Parser.zig b/src/human-readable/Parser.zig index 9f586884..820eb033 100644 --- a/src/human-readable/Parser.zig +++ b/src/human-readable/Parser.zig @@ -60,7 +60,9 @@ pub fn parseSource(self: *Parser) ParserErrors!void { .rhs = members.end, }; } - +/// Parsers all of the solidity source unit values. +/// +/// More info can be found [here](https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.sourceUnit) pub fn parseUnits(self: *Parser) ParserErrors!Node.Range { const scratch = self.scratch.items.len; defer self.scratch.shrinkRetainingCapacity(scratch); @@ -78,7 +80,7 @@ pub fn parseUnits(self: *Parser) ParserErrors!Node.Range { return self.listToSpan(slice); } - +/// Expects to find a source unit otherwise it will fail. pub fn expectUnit(self: *Parser) ParserErrors!Node.Index { const unit = try self.parseUnit(); @@ -89,7 +91,9 @@ pub fn expectUnit(self: *Parser) ParserErrors!Node.Index { return unit; } - +/// Parses a single source unit. +/// +/// More info can be found [here](https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.sourceUnit) pub fn parseUnit(self: *Parser) ParserErrors!Node.Index { return switch (self.token_tags[self.token_index]) { .Function => self.parseFunctionProto(), @@ -102,7 +106,7 @@ pub fn parseUnit(self: *Parser) ParserErrors!Node.Index { else => return null_node, }; } - +/// Parses a solidity function accordingly to the language grammar. pub fn parseFunctionProto(self: *Parser) ParserErrors!Node.Index { const keyword = self.consumeToken(.Function) orelse return null_node; @@ -181,7 +185,7 @@ pub fn parseFunctionProto(self: *Parser) ParserErrors!Node.Index { }), }; } - +/// Parses a solidity receive function accordingly to the language grammar. pub fn parseReceiveProto(self: *Parser) ParserErrors!Node.Index { const receive_keyword = self.consumeToken(.Receive) orelse return null_node; @@ -199,7 +203,7 @@ pub fn parseReceiveProto(self: *Parser) ParserErrors!Node.Index { }, }); } - +/// Parses a solidity fallback function accordingly to the language grammar. pub fn parseFallbackProto(self: *Parser) ParserErrors!Node.Index { const fallback_keyword = self.consumeToken(.Fallback) orelse return null_node; @@ -231,7 +235,7 @@ pub fn parseFallbackProto(self: *Parser) ParserErrors!Node.Index { }), }; } - +/// Parses a solidity constructor declaration accordingly to the language grammar. pub fn parseConstructorProto(self: *Parser) ParserErrors!Node.Index { const constructor_keyword = self.consumeToken(.Constructor) orelse return null_node; @@ -263,7 +267,7 @@ pub fn parseConstructorProto(self: *Parser) ParserErrors!Node.Index { }), }; } - +/// Parses all of the solidity mutability or visibility specifiers. pub fn parseSpecifiers(self: *Parser) ParserErrors!Node.Index { const scratch = self.scratch.items.len; defer self.scratch.shrinkRetainingCapacity(scratch); @@ -330,7 +334,7 @@ pub fn parseSpecifiers(self: *Parser) ParserErrors!Node.Index { }, }); } - +/// Parses a solidity error declaration accordingly to the language grammar. pub fn parseErrorProto(self: *Parser) ParserErrors!Node.Index { const error_keyword = self.consumeToken(.Error) orelse return null_node; @@ -362,7 +366,7 @@ pub fn parseErrorProto(self: *Parser) ParserErrors!Node.Index { }), }; } - +/// Parses a solidity event declaration accordingly to the language grammar. pub fn parseEventProto(self: *Parser) ParserErrors!Node.Index { const event_keyword = self.consumeToken(.Event) orelse return null_node; @@ -396,7 +400,7 @@ pub fn parseEventProto(self: *Parser) ParserErrors!Node.Index { }), }; } - +/// Parses the possible event declaration parameters according to the language grammar. pub fn parseEventVarDecls(self: *Parser) ParserErrors!Span { const scratch = self.scratch.items.len; defer self.scratch.shrinkRetainingCapacity(scratch); @@ -409,8 +413,10 @@ pub fn parseEventVarDecls(self: *Parser) ParserErrors!Span { switch (self.token_tags[self.token_index]) { .Comma => { - if (self.token_tags[self.token_index + 1] == .ClosingParen) + if (self.token_tags[self.token_index + 1] == .ClosingParen) { + @branchHint(.cold); return error.ParsingError; + } self.token_index += 1; }, .ClosingParen => { @@ -432,7 +438,7 @@ pub fn parseEventVarDecls(self: *Parser) ParserErrors!Span { else => Span{ .multi = try self.listToSpan(slice) }, }; } - +/// Parses the possible error declaration parameters according to the language grammar. pub fn parseErrorVarDecls(self: *Parser) ParserErrors!Span { const scratch = self.scratch.items.len; defer self.scratch.shrinkRetainingCapacity(scratch); @@ -445,8 +451,10 @@ pub fn parseErrorVarDecls(self: *Parser) ParserErrors!Span { switch (self.token_tags[self.token_index]) { .Comma => { - if (self.token_tags[self.token_index + 1] == .ClosingParen) + if (self.token_tags[self.token_index + 1] == .ClosingParen) { + @branchHint(.cold); return error.ParsingError; + } self.token_index += 1; }, .ClosingParen => { @@ -468,7 +476,7 @@ pub fn parseErrorVarDecls(self: *Parser) ParserErrors!Span { else => Span{ .multi = try self.listToSpan(slice) }, }; } - +/// Parses the possible function declaration parameters according to the language grammar. pub fn parseReturnParams(self: *Parser) ParserErrors!Node.Range { const scratch = self.scratch.items.len; defer self.scratch.shrinkRetainingCapacity(scratch); @@ -481,15 +489,20 @@ pub fn parseReturnParams(self: *Parser) ParserErrors!Node.Range { switch (self.token_tags[self.token_index]) { .Comma => { - if (self.token_tags[self.token_index + 1] == .ClosingParen) + if (self.token_tags[self.token_index + 1] == .ClosingParen) { + @branchHint(.cold); return error.ParsingError; + } self.token_index += 1; }, .ClosingParen => { self.token_index += 1; break; }, - else => return error.ParsingError, + else => { + @branchHint(.cold); + return error.ParsingError; + }, } } @@ -502,7 +515,7 @@ pub fn parseReturnParams(self: *Parser) ParserErrors!Node.Range { return self.listToSpan(slice); } - +/// Parses the possible function declaration parameters according to the language grammar. pub fn parseVariableDecls(self: *Parser) ParserErrors!Span { const scratch = self.scratch.items.len; defer self.scratch.shrinkRetainingCapacity(scratch); @@ -515,8 +528,10 @@ pub fn parseVariableDecls(self: *Parser) ParserErrors!Span { switch (self.token_tags[self.token_index]) { .Comma => { - if (self.token_tags[self.token_index + 1] == .ClosingParen) + if (self.token_tags[self.token_index + 1] == .ClosingParen) { + @branchHint(.cold); return error.ParsingError; + } self.token_index += 1; }, .ClosingParen => { @@ -538,7 +553,7 @@ pub fn parseVariableDecls(self: *Parser) ParserErrors!Span { else => Span{ .multi = try self.listToSpan(slice) }, }; } - +/// Expects to find a `error_var_decl`. Otherwise returns an error. pub fn expectErrorVarDecl(self: *Parser) ParserErrors!Node.Index { const index = try self.parseErrorVarDecl(); @@ -549,7 +564,7 @@ pub fn expectErrorVarDecl(self: *Parser) ParserErrors!Node.Index { return index; } - +/// Parses the possible error declaration parameter according to the language grammar. pub fn parseErrorVarDecl(self: *Parser) ParserErrors!Node.Index { const sol_type = try self.parseType(); @@ -567,7 +582,7 @@ pub fn parseErrorVarDecl(self: *Parser) ParserErrors!Node.Index { }, }); } - +/// Expects to find a `event_var_decl`. Otherwise returns an error. pub fn expectEventVarDecl(self: *Parser) ParserErrors!Node.Index { const index = try self.parseEventVarDecl(); @@ -578,7 +593,7 @@ pub fn expectEventVarDecl(self: *Parser) ParserErrors!Node.Index { return index; } - +/// Parses the possible event declaration parameter according to the language grammar. pub fn parseEventVarDecl(self: *Parser) ParserErrors!Node.Index { const sol_type = try self.parseType(); @@ -602,7 +617,7 @@ pub fn parseEventVarDecl(self: *Parser) ParserErrors!Node.Index { }, }); } - +/// Expects to find a `var_decl`. Otherwise returns an error. pub fn expectVarDecl(self: *Parser) ParserErrors!Node.Index { const index = try self.parseVariableDecl(); @@ -613,7 +628,7 @@ pub fn expectVarDecl(self: *Parser) ParserErrors!Node.Index { return index; } - +/// Parses the possible function declaration parameter according to the language grammar. pub fn parseVariableDecl(self: *Parser) ParserErrors!Node.Index { const sol_type = try self.parseType(); @@ -639,7 +654,7 @@ pub fn parseVariableDecl(self: *Parser) ParserErrors!Node.Index { }, }); } - +/// Parses a struct declaration according to the language grammar. pub fn parseStructDecl(self: *Parser) ParserErrors!Node.Index { const struct_index = self.consumeToken(.Struct) orelse return null_node; @@ -671,7 +686,7 @@ pub fn parseStructDecl(self: *Parser) ParserErrors!Node.Index { }), }; } - +/// Parses all of the structs fields according to the language grammar. pub fn parseStructFields(self: *Parser) ParserErrors!Span { const scratch = self.scratch.items.len; defer self.scratch.shrinkRetainingCapacity(scratch); @@ -699,7 +714,7 @@ pub fn parseStructFields(self: *Parser) ParserErrors!Span { else => Span{ .multi = try self.listToSpan(slice) }, }; } - +/// Expects to find a struct parameter or fails. pub fn expectStructField(self: *Parser) ParserErrors!Node.Index { const field_type = try self.expectType(); const identifier = try self.expectToken(.Identifier); @@ -715,7 +730,7 @@ pub fn expectStructField(self: *Parser) ParserErrors!Node.Index { }, }); } - +/// Expects to find either a `elementary_type`, `tuple_type`, `tuple_type_one`, `array_type` or `struct_type` pub fn expectType(self: *Parser) ParserErrors!Node.Index { const index = try self.parseType(); @@ -726,7 +741,7 @@ pub fn expectType(self: *Parser) ParserErrors!Node.Index { return index; } - +/// Parses the token into either a `elementary_type`, `tuple_type`, `tuple_type_one`, `array_type` or `struct_type` pub fn parseType(self: *Parser) ParserErrors!Node.Index { const sol_type = switch (self.token_tags[self.token_index]) { .Identifier => try self.addNode(.{ @@ -759,7 +774,10 @@ pub fn parseType(self: *Parser) ParserErrors!Node.Index { break self.nextToken(); }, - else => return error.ParsingError, + else => { + @branchHint(.cold); + return error.ParsingError; + }, } }; @@ -772,7 +790,7 @@ pub fn parseType(self: *Parser) ParserErrors!Node.Index { }, }); } - +/// Parses the tuple type similarly to `parseErrorVarDecls`. pub fn parseTupleType(self: *Parser) ParserErrors!Node.Index { const l_paren = self.consumeToken(.OpenParen) orelse return null_node; @@ -797,7 +815,7 @@ pub fn parseTupleType(self: *Parser) ParserErrors!Node.Index { }), }; } - +/// Creates a `elementary_type` node based on the solidity type keywords. pub fn consumeElementaryType(self: *Parser) Allocator.Error!Node.Index { return switch (self.token_tags[self.token_index]) { .Address, @@ -916,17 +934,18 @@ pub fn consumeElementaryType(self: *Parser) Allocator.Error!Node.Index { } // Internal parser actions +/// Consumes a token or returns null. fn consumeToken(self: *Parser, expected: TokenTag) ?TokenIndex { return if (self.token_tags[self.token_index] == expected) self.nextToken() else null; } - +/// Expects to find a token or fails. fn expectToken(self: *Parser, expected: TokenTag) error{ParsingError}!TokenIndex { return if (self.token_tags[self.token_index] == expected) self.nextToken() else { @branchHint(.cold); return error.ParsingError; }; } - +/// Advances the parser index and returns the previous one fn nextToken(self: *Parser) TokenIndex { const index = self.token_index; @@ -965,7 +984,7 @@ fn unreserveNode(self: *Parser, index: usize) void { self.nodes.items(.main_token)[index] = self.token_index; } } - +/// Adds the extra data struct into the allocated buffer. fn addExtraData(self: *Parser, extra: anytype) Allocator.Error!Node.Index { const fields = std.meta.fields(@TypeOf(extra)); From aa36305e4aeac13c07360597053ce2b960106fcb Mon Sep 17 00:00:00 2001 From: Raiden1411 <67233402+Raiden1411@users.noreply.github.com> Date: Mon, 7 Oct 2024 17:07:12 +0100 Subject: [PATCH 17/18] tests: update and readds the tests --- src/tests/decoding/logs_decode.test.zig | 44 ++++++------ src/tests/encoding/encoder.test.zig | 32 ++++----- src/tests/encoding/logs.test.zig | 72 ++++++++++---------- src/tests/human-readable/parser_new.test.zig | 26 ------- src/tests/root.zig | 20 +++--- test_runner.zig | 18 ++--- 6 files changed, 93 insertions(+), 119 deletions(-) delete mode 100644 src/tests/human-readable/parser_new.test.zig diff --git a/src/tests/decoding/logs_decode.test.zig b/src/tests/decoding/logs_decode.test.zig index fd89b5e4..a2901760 100644 --- a/src/tests/decoding/logs_decode.test.zig +++ b/src/tests/decoding/logs_decode.test.zig @@ -21,10 +21,10 @@ test "Decode empty inputs" { } test "Decode empty args" { - const event = try human.parseHumanReadable(AbiEvent, testing.allocator, "event Transfer(address indexed from, address indexed to, uint256 tokenId)"); + const event = try human.parseHumanReadable(testing.allocator, "event Transfer(address indexed from, address indexed to, uint256 tokenId)"); defer event.deinit(); - const encoded = try encodeLogs(testing.allocator, event.value, .{}); + const encoded = try encodeLogs(testing.allocator, event.value[0].abiEvent, .{}); defer testing.allocator.free(encoded); const decoded = try decodeLogs(struct { Hash }, encoded, .{}); @@ -33,10 +33,10 @@ test "Decode empty args" { } test "Decode with args" { - const event = try human.parseHumanReadable(AbiEvent, testing.allocator, "event Transfer(address indexed from, address indexed to, uint256 tokenId)"); + const event = try human.parseHumanReadable(testing.allocator, "event Transfer(address indexed from, address indexed to, uint256 tokenId)"); defer event.deinit(); - const encoded = try encodeLogs(testing.allocator, event.value, .{ null, try utils.addressToBytes("0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC") }); + const encoded = try encodeLogs(testing.allocator, event.value[0].abiEvent, .{ null, try utils.addressToBytes("0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC") }); defer testing.allocator.free(encoded); const decoded = try decodeLogs(struct { Hash, ?Hash, [20]u8 }, encoded, .{}); @@ -46,10 +46,10 @@ test "Decode with args" { test "Decoded with args string/bytes" { { - const event = try human.parseHumanReadable(AbiEvent, testing.allocator, "event Foo(string indexed message)"); + const event = try human.parseHumanReadable(testing.allocator, "event Foo(string indexed message)"); defer event.deinit(); - const encoded = try encodeLogs(testing.allocator, event.value, .{"hello"}); + const encoded = try encodeLogs(testing.allocator, event.value[0].abiEvent, .{"hello"}); defer testing.allocator.free(encoded); const decoded = try decodeLogs(std.meta.Tuple(&[_]type{ Hash, Hash }), encoded, .{}); @@ -57,10 +57,10 @@ test "Decoded with args string/bytes" { try testing.expectEqualDeep(.{ try utils.hashToBytes("0x9f0b7f1630bdb7d474466e2dfef0fb9dff65f7a50eec83935b68f77d0808f08a"), try utils.hashToBytes("0x1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8") }, decoded); } { - const event = try human.parseHumanReadable(AbiEvent, testing.allocator, "event Foo(string indexed message)"); + const event = try human.parseHumanReadable(testing.allocator, "event Foo(string indexed message)"); defer event.deinit(); - const encoded = try encodeLogs(testing.allocator, event.value, .{"hello"}); + const encoded = try encodeLogs(testing.allocator, event.value[0].abiEvent, .{"hello"}); defer testing.allocator.free(encoded); const decoded = try decodeLogs(std.meta.Tuple(&[_]type{ Hash, ?Hash }), encoded, .{}); @@ -68,10 +68,10 @@ test "Decoded with args string/bytes" { try testing.expectEqualDeep(.{ try utils.hashToBytes("0x9f0b7f1630bdb7d474466e2dfef0fb9dff65f7a50eec83935b68f77d0808f08a"), try utils.hashToBytes("0x1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8") }, decoded); } { - const event = try human.parseHumanReadable(AbiEvent, testing.allocator, "event Foo(bytes indexed message)"); + const event = try human.parseHumanReadable(testing.allocator, "event Foo(bytes indexed message)"); defer event.deinit(); - const encoded = try encodeLogs(testing.allocator, event.value, .{"hello"}); + const encoded = try encodeLogs(testing.allocator, event.value[0].abiEvent, .{"hello"}); defer testing.allocator.free(encoded); const decoded = try decodeLogs(std.meta.Tuple(&[_]type{ Hash, Hash }), encoded, .{}); @@ -82,10 +82,10 @@ test "Decoded with args string/bytes" { test "Decode Arrays" { { - const event = try human.parseHumanReadable(AbiEvent, testing.allocator, "event Foo(address indexed a)"); + const event = try human.parseHumanReadable(testing.allocator, "event Foo(address indexed a)"); defer event.deinit(); - const encoded = try encodeLogs(testing.allocator, event.value, .{try utils.addressToBytes("0x4838B106FCe9647Bdf1E7877BF73cE8B0BAD5f97")}); + const encoded = try encodeLogs(testing.allocator, event.value[0].abiEvent, .{try utils.addressToBytes("0x4838B106FCe9647Bdf1E7877BF73cE8B0BAD5f97")}); defer testing.allocator.free(encoded); const decoded = try decodeLogs(struct { Hash, [20]u8 }, encoded, .{}); @@ -94,10 +94,10 @@ test "Decode Arrays" { } { - const event = try human.parseHumanReadable(AbiEvent, testing.allocator, "event Foo(bytes5 indexed a)"); + const event = try human.parseHumanReadable(testing.allocator, "event Foo(bytes5 indexed a)"); defer event.deinit(); - const encoded = try encodeLogs(testing.allocator, event.value, .{"hello"}); + const encoded = try encodeLogs(testing.allocator, event.value[0].abiEvent, .{"hello"}); defer testing.allocator.free(encoded); const decoded = try decodeLogs(struct { Hash, [5]u8 }, encoded, .{ .bytes_endian = .little }); @@ -105,10 +105,10 @@ test "Decode Arrays" { try testing.expectEqualDeep(.{ encoded[0], "hello".* }, decoded); } { - const event = try human.parseHumanReadable(AbiEvent, testing.allocator, "event Foo(bytes5 indexed a)"); + const event = try human.parseHumanReadable(testing.allocator, "event Foo(bytes5 indexed a)"); defer event.deinit(); - const encoded = try encodeLogs(testing.allocator, event.value, .{"hello"}); + const encoded = try encodeLogs(testing.allocator, event.value[0].abiEvent, .{"hello"}); defer testing.allocator.free(encoded); const decoded = try decodeLogs(struct { Hash, *const [5]u8 }, encoded, .{ .allocator = testing.allocator, .bytes_endian = .little }); @@ -119,10 +119,10 @@ test "Decode Arrays" { } test "Decode with remaing types" { - const event = try human.parseHumanReadable(AbiEvent, testing.allocator, "event Foo(uint indexed a, int indexed b, bool indexed c)"); + const event = try human.parseHumanReadable(testing.allocator, "event Foo(uint indexed a, int indexed b, bool indexed c)"); defer event.deinit(); - const encoded = try encodeLogs(testing.allocator, event.value, .{ 69, -420, true }); + const encoded = try encodeLogs(testing.allocator, event.value[0].abiEvent, .{ 69, -420, true }); defer testing.allocator.free(encoded); const decoded = try decodeLogs(std.meta.Tuple(&[_]type{ Hash, u256, i256, bool }), encoded, .{}); @@ -137,19 +137,19 @@ test "Decode with remaing types" { test "Errors" { { - const event = try human.parseHumanReadable(AbiEvent, testing.allocator, "event Foo(uint indexed a)"); + const event = try human.parseHumanReadable(testing.allocator, "event Foo(uint indexed a)"); defer event.deinit(); - const encoded = try encodeLogs(testing.allocator, event.value, .{69}); + const encoded = try encodeLogs(testing.allocator, event.value[0].abiEvent, .{69}); defer testing.allocator.free(encoded); try testing.expectError(error.ExpectedAllocator, decodeLogs(struct { Hash, *const [5]u8 }, encoded, .{ .bytes_endian = .little })); } { - const event = try human.parseHumanReadable(AbiEvent, testing.allocator, "event Foo(uint indexed a)"); + const event = try human.parseHumanReadable(testing.allocator, "event Foo(uint indexed a)"); defer event.deinit(); - const encoded = try encodeLogs(testing.allocator, event.value, .{null}); + const encoded = try encodeLogs(testing.allocator, event.value[0].abiEvent, .{null}); defer testing.allocator.free(encoded); try testing.expectError(error.UnexpectedTupleFieldType, decodeLogs(struct { Hash, [5]u8 }, encoded, .{ .bytes_endian = .little })); diff --git a/src/tests/encoding/encoder.test.zig b/src/tests/encoding/encoder.test.zig index 79882aa3..95374784 100644 --- a/src/tests/encoding/encoder.test.zig +++ b/src/tests/encoding/encoder.test.zig @@ -146,10 +146,10 @@ test "EncodePacked" { } test "Constructor" { - const sig = try human.parseHumanReadable(abi.Constructor, testing.allocator, "constructor(bool foo)"); + const sig = try human.parseHumanReadable(testing.allocator, "constructor(bool foo)"); defer sig.deinit(); - const encoded = try sig.value.encode(testing.allocator, .{true}); + const encoded = try sig.value[0].abiConstructor.encode(testing.allocator, .{true}); defer encoded.deinit(); const hex = try std.fmt.allocPrint(testing.allocator, "{s}", .{std.fmt.fmtSliceHexLower(encoded.data)}); @@ -158,11 +158,11 @@ test "Constructor" { } test "Constructor multi params" { - const sig = try human.parseHumanReadable(abi.Constructor, testing.allocator, "constructor(bool foo, string bar)"); + const sig = try human.parseHumanReadable(testing.allocator, "constructor(bool foo, string bar)"); defer sig.deinit(); const fizz: []const u8 = "fizzbuzz"; - const encoded = try sig.value.encode(testing.allocator, .{ true, fizz }); + const encoded = try sig.value[0].abiConstructor.encode(testing.allocator, .{ true, fizz }); defer encoded.deinit(); const hex = try std.fmt.allocPrint(testing.allocator, "{s}", .{std.fmt.fmtSliceHexLower(encoded.data)}); @@ -172,11 +172,11 @@ test "Constructor multi params" { } test "Error signature" { - const sig = try human.parseHumanReadable(abi.Error, testing.allocator, "error Foo(bool foo, string bar)"); + const sig = try human.parseHumanReadable(testing.allocator, "error Foo(bool foo, string bar)"); defer sig.deinit(); const fizz: []const u8 = "fizzbuzz"; - const encoded = try sig.value.encode(testing.allocator, .{ true, fizz }); + const encoded = try sig.value[0].abiError.encode(testing.allocator, .{ true, fizz }); defer testing.allocator.free(encoded); const hex = try std.fmt.allocPrint(testing.allocator, "{s}", .{std.fmt.fmtSliceHexLower(encoded)}); @@ -186,10 +186,10 @@ test "Error signature" { } test "Event signature" { - const sig = try human.parseHumanReadable(abi.Event, testing.allocator, "event Transfer(address indexed from, address indexed to, uint256 tokenId)"); + const sig = try human.parseHumanReadable(testing.allocator, "event Transfer(address indexed from, address indexed to, uint256 tokenId)"); defer sig.deinit(); - const encoded = try sig.value.encode(testing.allocator); + const encoded = try sig.value[0].abiEvent.encode(testing.allocator); const hex = try std.fmt.allocPrint(testing.allocator, "0x{s}", .{std.fmt.fmtSliceHexLower(&encoded)}); defer testing.allocator.free(hex); @@ -198,10 +198,10 @@ test "Event signature" { } test "Event signature non indexed" { - const sig = try human.parseHumanReadable(abi.Event, testing.allocator, "event Transfer(address from, address to, uint256 tokenId)"); + const sig = try human.parseHumanReadable(testing.allocator, "event Transfer(address from, address to, uint256 tokenId)"); defer sig.deinit(); - const encoded = try sig.value.encode(testing.allocator); + const encoded = try sig.value[0].abiEvent.encode(testing.allocator); const hex = try std.fmt.allocPrint(testing.allocator, "0x{s}", .{std.fmt.fmtSliceHexLower(&encoded)}); defer testing.allocator.free(hex); @@ -210,11 +210,11 @@ test "Event signature non indexed" { } test "Function" { - const sig = try human.parseHumanReadable(abi.Function, testing.allocator, "function Foo(bool foo, string bar)"); + const sig = try human.parseHumanReadable(testing.allocator, "function Foo(bool foo, string bar)"); defer sig.deinit(); const fizz: []const u8 = "fizzbuzz"; - const encoded = try sig.value.encode(testing.allocator, .{ true, fizz }); + const encoded = try sig.value[0].abiFunction.encode(testing.allocator, .{ true, fizz }); defer testing.allocator.free(encoded); const hex = try std.fmt.allocPrint(testing.allocator, "{s}", .{std.fmt.fmtSliceHexLower(encoded)}); @@ -224,10 +224,10 @@ test "Function" { } test "Function outputs" { - const sig = try human.parseHumanReadable(abi.Function, testing.allocator, "function Foo(bool foo, string bar) public view returns(int120 baz)"); + const sig = try human.parseHumanReadable(testing.allocator, "function Foo(bool foo, string bar) public view returns(int120 baz)"); defer sig.deinit(); - const encoded = try sig.value.encodeOutputs(testing.allocator, .{1}); + const encoded = try sig.value[0].abiFunction.encodeOutputs(testing.allocator, .{1}); defer testing.allocator.free(encoded); const hex = try std.fmt.allocPrint(testing.allocator, "{s}", .{std.fmt.fmtSliceHexLower(encoded)}); @@ -237,11 +237,11 @@ test "Function outputs" { } test "AbiItem" { - const sig = try human.parseHumanReadable(abi.AbiItem, testing.allocator, "function Foo(bool foo, string bar)"); + const sig = try human.parseHumanReadable(testing.allocator, "function Foo(bool foo, string bar)"); defer sig.deinit(); const fizz: []const u8 = "fizzbuzz"; - const encoded = try sig.value.abiFunction.encode(testing.allocator, .{ true, fizz }); + const encoded = try sig.value[0].abiFunction.encode(testing.allocator, .{ true, fizz }); defer testing.allocator.free(encoded); const hex = try std.fmt.allocPrint(testing.allocator, "{s}", .{std.fmt.fmtSliceHexLower(encoded)}); diff --git a/src/tests/encoding/logs.test.zig b/src/tests/encoding/logs.test.zig index 8cd3e628..170ad00f 100644 --- a/src/tests/encoding/logs.test.zig +++ b/src/tests/encoding/logs.test.zig @@ -27,10 +27,10 @@ test "Empty inputs" { } test "Empty args" { - const event = try human.parseHumanReadable(abi.Event, testing.allocator, "event Transfer(address indexed from, address indexed to, uint256 tokenId)"); + const event = try human.parseHumanReadable(testing.allocator, "event Transfer(address indexed from, address indexed to, uint256 tokenId)"); defer event.deinit(); - const encoded = try encodeLogTopics(testing.allocator, event.value, .{}); + const encoded = try encodeLogTopics(testing.allocator, event.value[0].abiEvent, .{}); defer testing.allocator.free(encoded); const slice: []const ?Hash = &.{try utils.hashToBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")}; @@ -39,10 +39,10 @@ test "Empty args" { } test "With args" { - const event = try human.parseHumanReadable(abi.Event, testing.allocator, "event Transfer(address indexed from, address indexed to, uint256 tokenId)"); + const event = try human.parseHumanReadable(testing.allocator, "event Transfer(address indexed from, address indexed to, uint256 tokenId)"); defer event.deinit(); - const encoded = try encodeLogTopics(testing.allocator, event.value, .{ null, try utils.addressToBytes("0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC") }); + const encoded = try encodeLogTopics(testing.allocator, event.value[0].abiEvent, .{ null, try utils.addressToBytes("0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC") }); defer testing.allocator.free(encoded); const slice: []const ?Hash = &.{ try utils.hashToBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), null, try utils.hashToBytes("0x000000000000000000000000a5cc3c03994db5b0d9a5eedd10cabab0813678ac") }; @@ -136,10 +136,10 @@ test "Comptime Encoding" { test "With args string/bytes" { { - const event = try human.parseHumanReadable(abi.Event, testing.allocator, "event Foo(string indexed message)"); + const event = try human.parseHumanReadable(testing.allocator, "event Foo(string indexed message)"); defer event.deinit(); - const encoded = try encodeLogTopics(testing.allocator, event.value, .{"hello"}); + const encoded = try encodeLogTopics(testing.allocator, event.value[0].abiEvent, .{"hello"}); defer testing.allocator.free(encoded); const slice: []const ?Hash = &.{ try utils.hashToBytes("0x9f0b7f1630bdb7d474466e2dfef0fb9dff65f7a50eec83935b68f77d0808f08a"), try utils.hashToBytes("0x1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8") }; @@ -147,10 +147,10 @@ test "With args string/bytes" { try testing.expectEqualDeep(slice, encoded); } { - const event = try human.parseHumanReadable(abi.Event, testing.allocator, "event Foo(bytes indexed message)"); + const event = try human.parseHumanReadable(testing.allocator, "event Foo(bytes indexed message)"); defer event.deinit(); - const encoded = try encodeLogTopics(testing.allocator, event.value, .{"hello"}); + const encoded = try encodeLogTopics(testing.allocator, event.value[0].abiEvent, .{"hello"}); defer testing.allocator.free(encoded); const slice: []const ?Hash = &.{ try utils.hashToBytes("0xefc9afd358f1472682cf8cc82e1d3ae36be2538ed858a4a604119399d6f22b48"), try utils.hashToBytes("0x1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8") }; @@ -158,11 +158,11 @@ test "With args string/bytes" { try testing.expectEqualDeep(slice, encoded); } { - const event = try human.parseHumanReadable(abi.Event, testing.allocator, "event Foo(string indexed message)"); + const event = try human.parseHumanReadable(testing.allocator, "event Foo(string indexed message)"); defer event.deinit(); const str: []const u8 = "hello"; - const encoded = try encodeLogTopics(testing.allocator, event.value, .{str}); + const encoded = try encodeLogTopics(testing.allocator, event.value[0].abiEvent, .{str}); defer testing.allocator.free(encoded); const slice: []const ?Hash = &.{ try utils.hashToBytes("0x9f0b7f1630bdb7d474466e2dfef0fb9dff65f7a50eec83935b68f77d0808f08a"), try utils.hashToBytes("0x1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8") }; @@ -170,11 +170,11 @@ test "With args string/bytes" { try testing.expectEqualDeep(slice, encoded); } { - const event = try human.parseHumanReadable(abi.Event, testing.allocator, "event Foo(bytes indexed message)"); + const event = try human.parseHumanReadable(testing.allocator, "event Foo(bytes indexed message)"); defer event.deinit(); const str: []const u8 = "hello"; - const encoded = try encodeLogTopics(testing.allocator, event.value, .{str}); + const encoded = try encodeLogTopics(testing.allocator, event.value[0].abiEvent, .{str}); defer testing.allocator.free(encoded); const slice: []const ?Hash = &.{ try utils.hashToBytes("0xefc9afd358f1472682cf8cc82e1d3ae36be2538ed858a4a604119399d6f22b48"), try utils.hashToBytes("0x1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8") }; @@ -184,10 +184,10 @@ test "With args string/bytes" { } test "With remaing types" { - const event = try human.parseHumanReadable(abi.Event, testing.allocator, "event Foo(uint indexed a, int indexed b, bool indexed c, bytes5 indexed d)"); + const event = try human.parseHumanReadable(testing.allocator, "event Foo(uint indexed a, int indexed b, bool indexed c, bytes5 indexed d)"); defer event.deinit(); - const encoded = try encodeLogTopics(testing.allocator, event.value, .{ 69, -420, true, "01234" }); + const encoded = try encodeLogTopics(testing.allocator, event.value[0].abiEvent, .{ 69, -420, true, "01234" }); defer testing.allocator.free(encoded); const slice: []const ?Hash = &.{ try utils.hashToBytes("0x08056cee0ec7df6d2ab8d10ab36f1ac8be153e2a0001198ef7b4c17dde75cbc4"), try utils.hashToBytes("0x0000000000000000000000000000000000000000000000000000000000000045"), try utils.hashToBytes("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe5c"), try utils.hashToBytes("0x0000000000000000000000000000000000000000000000000000000000000001"), try utils.hashToBytes("0x3031323334000000000000000000000000000000000000000000000000000000") }; @@ -196,11 +196,11 @@ test "With remaing types" { test "Array types" { { - const event = try human.parseHumanReadable(abi.Event, testing.allocator, "event Bar(uint256[] indexed baz)"); + const event = try human.parseHumanReadable(testing.allocator, "event Bar(uint256[] indexed baz)"); defer event.deinit(); const arr: []const u256 = &.{69}; - const encoded = try encodeLogTopics(testing.allocator, event.value, .{arr}); + const encoded = try encodeLogTopics(testing.allocator, event.value[0].abiEvent, .{arr}); defer testing.allocator.free(encoded); const slice: []const ?Hash = &.{ try utils.hashToBytes("0xf2f93df484f17a3a9dc5ad4281f6a49fe8ed98d0e9444200dc613445fe70c256"), try utils.hashToBytes("0xa80a8fcc11760162f08bb091d2c9389d07f2b73d0e996161dfac6f1043b5fc0b") }; @@ -208,11 +208,11 @@ test "Array types" { try testing.expectEqualDeep(slice, encoded); } { - const event = try human.parseHumanReadable(abi.Event, testing.allocator, "event Bar(uint256[] indexed baz)"); + const event = try human.parseHumanReadable(testing.allocator, "event Bar(uint256[] indexed baz)"); defer event.deinit(); const arr: []const u256 = &.{ 69, 69 }; - const encoded = try encodeLogTopics(testing.allocator, event.value, .{arr}); + const encoded = try encodeLogTopics(testing.allocator, event.value[0].abiEvent, .{arr}); defer testing.allocator.free(encoded); const slice: []const ?Hash = &.{ try utils.hashToBytes("0xf2f93df484f17a3a9dc5ad4281f6a49fe8ed98d0e9444200dc613445fe70c256"), try utils.hashToBytes("0x1de70b39b0b9e807901612d596756f9f581455d5f89cb049b46f082f8a423dc6") }; @@ -220,11 +220,11 @@ test "Array types" { try testing.expectEqualDeep(slice, encoded); } { - const event = try human.parseHumanReadable(abi.Event, testing.allocator, "event Bar(uint256[] indexed baz)"); + const event = try human.parseHumanReadable(testing.allocator, "event Bar(uint256[] indexed baz)"); defer event.deinit(); const arr: []const u256 = &.{}; - const encoded = try encodeLogTopics(testing.allocator, event.value, .{arr}); + const encoded = try encodeLogTopics(testing.allocator, event.value[0].abiEvent, .{arr}); defer testing.allocator.free(encoded); const slice: []const ?Hash = &.{ try utils.hashToBytes("0xf2f93df484f17a3a9dc5ad4281f6a49fe8ed98d0e9444200dc613445fe70c256"), try utils.hashToBytes("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470") }; @@ -232,11 +232,11 @@ test "Array types" { try testing.expectEqualDeep(slice, encoded); } { - const event = try human.parseHumanReadable(abi.Event, testing.allocator, "event Bar(uint256[][][] indexed baz)"); + const event = try human.parseHumanReadable(testing.allocator, "event Bar(uint256[][][] indexed baz)"); defer event.deinit(); const arr: []const []const []const u256 = &.{&.{&.{69}}}; - const encoded = try encodeLogTopics(testing.allocator, event.value, .{arr}); + const encoded = try encodeLogTopics(testing.allocator, event.value[0].abiEvent, .{arr}); defer testing.allocator.free(encoded); const slice: []const ?Hash = &.{ try utils.hashToBytes("0x9ef9519e463db05a446c0dfbe83eff19a03f2087827426a7e38b69df591bef7f"), try utils.hashToBytes("0xa80a8fcc11760162f08bb091d2c9389d07f2b73d0e996161dfac6f1043b5fc0b") }; @@ -250,7 +250,7 @@ test "Structs" { \\struct Foo{uint256 foo;} \\event Bar(Foo indexed foo) ; - const event = try human.parseHumanReadable(abi.Abi, testing.allocator, slice); + const event = try human.parseHumanReadable(testing.allocator, slice); defer event.deinit(); const bar: struct { foo: u256 } = .{ .foo = 69 }; @@ -264,41 +264,41 @@ test "Structs" { test "Errors" { { - const event = try human.parseHumanReadable(abi.Event, testing.allocator, "event Foo(uint indexed a)"); + const event = try human.parseHumanReadable(testing.allocator, "event Foo(uint indexed a)"); defer event.deinit(); - try testing.expectError(error.SignedNumber, encodeLogTopics(testing.allocator, event.value, .{-69})); - try testing.expectError(error.InvalidParamType, encodeLogTopics(testing.allocator, event.value, .{false})); + try testing.expectError(error.SignedNumber, encodeLogTopics(testing.allocator, event.value[0].abiEvent, .{-69})); + try testing.expectError(error.InvalidParamType, encodeLogTopics(testing.allocator, event.value[0].abiEvent, .{false})); } { - const event = try human.parseHumanReadable(abi.Event, testing.allocator, "event Foo(bool indexed a)"); + const event = try human.parseHumanReadable(testing.allocator, "event Foo(bool indexed a)"); defer event.deinit(); - try testing.expectError(error.InvalidParamType, encodeLogTopics(testing.allocator, event.value, .{-69})); + try testing.expectError(error.InvalidParamType, encodeLogTopics(testing.allocator, event.value[0].abiEvent, .{-69})); } { - const event = try human.parseHumanReadable(abi.Event, testing.allocator, "event Foo(address indexed a)"); + const event = try human.parseHumanReadable(testing.allocator, "event Foo(address indexed a)"); defer event.deinit(); - try testing.expectError(error.InvalidAddressType, encodeLogTopics(testing.allocator, event.value, .{"0x00000000000000000000000000000000000"})); + try testing.expectError(error.InvalidAddressType, encodeLogTopics(testing.allocator, event.value[0].abiEvent, .{"0x00000000000000000000000000000000000"})); } { - const event = try human.parseHumanReadable(abi.Event, testing.allocator, "event Foo(bytes5 indexed a)"); + const event = try human.parseHumanReadable(testing.allocator, "event Foo(bytes5 indexed a)"); defer event.deinit(); - try testing.expectError(error.InvalidFixedBytesType, encodeLogTopics(testing.allocator, event.value, .{"0x00000000000000000000000000000000000"})); + try testing.expectError(error.InvalidFixedBytesType, encodeLogTopics(testing.allocator, event.value[0].abiEvent, .{"0x00000000000000000000000000000000000"})); } { - const event = try human.parseHumanReadable(abi.Event, testing.allocator, "event Foo(uint indexed a)"); + const event = try human.parseHumanReadable(testing.allocator, "event Foo(uint indexed a)"); defer event.deinit(); const str: []const u8 = "hey"; - try testing.expectError(error.InvalidParamType, encodeLogTopics(testing.allocator, event.value, .{str})); + try testing.expectError(error.InvalidParamType, encodeLogTopics(testing.allocator, event.value[0].abiEvent, .{str})); } { - const event = try human.parseHumanReadable(abi.Event, testing.allocator, "event Foo(uint indexed a)"); + const event = try human.parseHumanReadable(testing.allocator, "event Foo(uint indexed a)"); defer event.deinit(); - try testing.expectError(error.InvalidParamType, encodeLogTopics(testing.allocator, event.value, .{"hey"})); + try testing.expectError(error.InvalidParamType, encodeLogTopics(testing.allocator, event.value[0].abiEvent, .{"hey"})); } } diff --git a/src/tests/human-readable/parser_new.test.zig b/src/tests/human-readable/parser_new.test.zig deleted file mode 100644 index 4c4a9e0f..00000000 --- a/src/tests/human-readable/parser_new.test.zig +++ /dev/null @@ -1,26 +0,0 @@ -const tokenizer = @import("../../human-readable/lexer.zig"); -const std = @import("std"); -const testing = std.testing; - -const Parser = @import("../../human-readable/Parser.zig"); -const Ast = @import("../../human-readable/Ast.zig"); -const HumanAbi = @import("../../human-readable/HumanAbi.zig"); - -test "Human readable" { - var arena = std.heap.ArenaAllocator.init(testing.allocator); - defer arena.deinit(); - - const abi = try HumanAbi.parse(arena.allocator(), "struct Bar{uint bazz;}\nstruct Foo{uint baz; Bar jazz;}\nfunction foo(Foo[69][] bar)"); - // defer ast.deinit(testing.allocator); - - // const abi_gen: HumanAbi = .{ - // .allocator = testing.allocator, - // .ast = &ast, - // }; - // - // const abi = try abi_gen.toAbi(); - // std.debug.print("FOOOOO: {any}\n", .{ast.nodes.items(.tag)}); - // std.debug.print("FOOOOO: {s}\n", .{ast.getNodeSource(1)}); - std.debug.print("FOOOOO: {any}\n", .{abi}); - std.debug.print("FOOOOO: {any}\n", .{abi.len}); -} diff --git a/src/tests/root.zig b/src/tests/root.zig index c18a80f6..67f73038 100644 --- a/src/tests/root.zig +++ b/src/tests/root.zig @@ -1,14 +1,14 @@ test { - // _ = @import("abi/root.zig"); - // _ = @import("ast/tokenizer.test.zig"); - // _ = @import("ast/parser.test.zig"); - // _ = @import("clients/root.zig"); - // _ = @import("crypto/root.zig"); - // _ = @import("decoding/root.zig"); - // _ = @import("encoding/root.zig"); - // _ = @import("evm/root.zig"); + _ = @import("abi/root.zig"); + _ = @import("ast/tokenizer.test.zig"); + _ = @import("ast/parser.test.zig"); + _ = @import("clients/root.zig"); + _ = @import("crypto/root.zig"); + _ = @import("decoding/root.zig"); + _ = @import("encoding/root.zig"); + _ = @import("evm/root.zig"); _ = @import("human-readable/root.zig"); _ = @import("human-readable/parser_new.test.zig"); - // _ = @import("meta/root.zig"); - // _ = @import("utils/root.zig"); + _ = @import("meta/root.zig"); + _ = @import("utils/root.zig"); } diff --git a/test_runner.zig b/test_runner.zig index 79e1c6d6..6329bb33 100644 --- a/test_runner.zig +++ b/test_runner.zig @@ -24,15 +24,15 @@ pub fn main() !void { .next_color = .reset, }; - // startAnvilInstances(std.heap.page_allocator) catch { - // printer.setNextColor(.red); - // try printer.writer().writeAll("error: "); - // - // printer.setNextColor(.bold); - // try printer.writer().writeAll("Failed to connect to anvil! Please ensure that it is running on port 6969\n"); - // - // std.process.exit(1); - // }; + startAnvilInstances(std.heap.page_allocator) catch { + printer.setNextColor(.red); + try printer.writer().writeAll("error: "); + + printer.setNextColor(.bold); + try printer.writer().writeAll("Failed to connect to anvil! Please ensure that it is running on port 6969\n"); + + std.process.exit(1); + }; const test_funcs: []const TestFn = builtin.test_functions; From 6f29e606ad5bc663a6217c69269fea124cfacd9d Mon Sep 17 00:00:00 2001 From: Raiden1411 <67233402+Raiden1411@users.noreply.github.com> Date: Mon, 7 Oct 2024 17:13:35 +0100 Subject: [PATCH 18/18] tests: remove old reference --- src/tests/root.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tests/root.zig b/src/tests/root.zig index 67f73038..125f3850 100644 --- a/src/tests/root.zig +++ b/src/tests/root.zig @@ -8,7 +8,6 @@ test { _ = @import("encoding/root.zig"); _ = @import("evm/root.zig"); _ = @import("human-readable/root.zig"); - _ = @import("human-readable/parser_new.test.zig"); _ = @import("meta/root.zig"); _ = @import("utils/root.zig"); }