| ... | ... | @@ -36,20 +36,52 @@ pub fn create(builder: *Builder, source: build.FileSource, obj_format: std.Targe |
| 36 | 36 | } |
| 37 | 37 | |
| 38 | 38 | const Action = union(enum) { |
| 39 | | exact_match: []const u8, |
| 40 | | extract_var: struct { |
| 41 | | fuzzy_match: []const u8, |
| 42 | | var_name: []const u8, |
| 43 | | var_value: u64, |
| 44 | | }, |
| 45 | | compare: CompareAction, |
| 39 | match: MatchAction, |
| 40 | compute_eq: ComputeEqAction, |
| 46 | 41 | }; |
| 47 | 42 | |
| 48 | | const CompareAction = struct { |
| 49 | | expected: union(enum) { |
| 50 | | literal: u64, |
| 51 | | varr: []const u8, |
| 52 | | }, |
| 43 | const MatchAction = struct { |
| 44 | needle: []const u8, |
| 45 | |
| 46 | fn match(act: MatchAction, haystack: []const u8, global_vars: anytype) !bool { |
| 47 | var hay_it = mem.tokenize(u8, mem.trim(u8, haystack, " "), " "); |
| 48 | var needle_it = mem.tokenize(u8, mem.trim(u8, act.needle, " "), " "); |
| 49 | |
| 50 | while (needle_it.next()) |needle_tok| { |
| 51 | const hay_tok = hay_it.next() orelse return false; |
| 52 | |
| 53 | if (mem.indexOf(u8, needle_tok, "{*}")) |index| { |
| 54 | // We have fuzzy matchers within the search pattern, so we match substrings. |
| 55 | var start = index; |
| 56 | var n_tok = needle_tok; |
| 57 | var h_tok = hay_tok; |
| 58 | while (true) { |
| 59 | n_tok = n_tok[start + 3 ..]; |
| 60 | const inner = if (mem.indexOf(u8, n_tok, "{*}")) |sub_end| |
| 61 | n_tok[0..sub_end] |
| 62 | else |
| 63 | n_tok; |
| 64 | if (mem.indexOf(u8, h_tok, inner) == null) return false; |
| 65 | start = mem.indexOf(u8, n_tok, "{*}") orelse break; |
| 66 | } |
| 67 | } else if (mem.startsWith(u8, needle_tok, "{")) { |
| 68 | const closing_brace = mem.indexOf(u8, needle_tok, "}") orelse return error.MissingClosingBrace; |
| 69 | if (closing_brace != needle_tok.len - 1) return error.ClosingBraceNotLast; |
| 70 | |
| 71 | const name = needle_tok[1..closing_brace]; |
| 72 | const value = try std.fmt.parseInt(u64, hay_tok, 16); |
| 73 | try global_vars.putNoClobber(name, value); |
| 74 | } else { |
| 75 | if (!mem.eql(u8, hay_tok, needle_tok)) return false; |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | return true; |
| 80 | } |
| 81 | }; |
| 82 | |
| 83 | const ComputeEqAction = struct { |
| 84 | expected: []const u8, |
| 53 | 85 | var_stack: std.ArrayList([]const u8), |
| 54 | 86 | op_stack: std.ArrayList(Op), |
| 55 | 87 | |
| ... | ... | @@ -69,43 +101,29 @@ const Check = struct { |
| 69 | 101 | }; |
| 70 | 102 | } |
| 71 | 103 | |
| 72 | | fn exactMatch(self: *Check, phrase: []const u8) void { |
| 104 | fn match(self: *Check, needle: []const u8) void { |
| 73 | 105 | self.actions.append(.{ |
| 74 | | .exact_match = self.builder.dupe(phrase), |
| 106 | .match = .{ .needle = self.builder.dupe(needle) }, |
| 75 | 107 | }) catch unreachable; |
| 76 | 108 | } |
| 77 | 109 | |
| 78 | | fn extractVar(self: *Check, phrase: []const u8, var_name: []const u8) void { |
| 110 | fn computeEq(self: *Check, act: ComputeEqAction) void { |
| 79 | 111 | self.actions.append(.{ |
| 80 | | .extract_var = .{ |
| 81 | | .fuzzy_match = self.builder.dupe(phrase), |
| 82 | | .var_name = self.builder.dupe(var_name), |
| 83 | | .var_value = undefined, |
| 84 | | }, |
| 112 | .compute_eq = act, |
| 85 | 113 | }) catch unreachable; |
| 86 | 114 | } |
| 87 | 115 | }; |
| 88 | 116 | |
| 89 | 117 | pub fn check(self: *CheckObjectStep, phrase: []const u8) void { |
| 90 | 118 | var new_check = Check.create(self.builder); |
| 91 | | new_check.exactMatch(phrase); |
| 119 | new_check.match(phrase); |
| 92 | 120 | self.checks.append(new_check) catch unreachable; |
| 93 | 121 | } |
| 94 | 122 | |
| 95 | 123 | pub fn checkNext(self: *CheckObjectStep, phrase: []const u8) void { |
| 96 | 124 | assert(self.checks.items.len > 0); |
| 97 | 125 | const last = &self.checks.items[self.checks.items.len - 1]; |
| 98 | | last.exactMatch(phrase); |
| 99 | | } |
| 100 | | |
| 101 | | pub fn checkNextExtract(self: *CheckObjectStep, comptime phrase: []const u8) void { |
| 102 | | assert(self.checks.items.len > 0); |
| 103 | | const matcher_start = comptime mem.indexOf(u8, phrase, "{") orelse |
| 104 | | @compileError("missing { } matcher"); |
| 105 | | const matcher_end = comptime mem.indexOf(u8, phrase, "}") orelse |
| 106 | | @compileError("missing { } matcher"); |
| 107 | | const last = &self.checks.items[self.checks.items.len - 1]; |
| 108 | | last.extractVar(phrase[0..matcher_start], phrase[matcher_start + 1 .. matcher_end]); |
| 126 | last.match(phrase); |
| 109 | 127 | } |
| 110 | 128 | |
| 111 | 129 | pub fn checkInSymtab(self: *CheckObjectStep) void { |
| ... | ... | @@ -113,18 +131,15 @@ pub fn checkInSymtab(self: *CheckObjectStep) void { |
| 113 | 131 | self.check("symtab"); |
| 114 | 132 | } |
| 115 | 133 | |
| 116 | | pub fn checkCompare(self: *CheckObjectStep, comptime phrase: []const u8, expected: anytype) void { |
| 117 | | comptime assert(phrase[0] == '{'); |
| 118 | | comptime assert(phrase[phrase.len - 1] == '}'); |
| 119 | | |
| 134 | pub fn checkComputeEq(self: *CheckObjectStep, program: []const u8, expected: []const u8) void { |
| 120 | 135 | const gpa = self.builder.allocator; |
| 121 | | var ca = CompareAction{ |
| 136 | var ca = ComputeEqAction{ |
| 122 | 137 | .expected = expected, |
| 123 | 138 | .var_stack = std.ArrayList([]const u8).init(gpa), |
| 124 | | .op_stack = std.ArrayList(CompareAction.Op).init(gpa), |
| 139 | .op_stack = std.ArrayList(ComputeEqAction.Op).init(gpa), |
| 125 | 140 | }; |
| 126 | 141 | |
| 127 | | var it = mem.tokenize(u8, phrase[1 .. phrase.len - 1], " "); |
| 142 | var it = mem.tokenize(u8, program, " "); |
| 128 | 143 | while (it.next()) |next| { |
| 129 | 144 | if (mem.eql(u8, next, "+")) { |
| 130 | 145 | ca.op_stack.append(.add) catch unreachable; |
| ... | ... | @@ -134,7 +149,7 @@ pub fn checkCompare(self: *CheckObjectStep, comptime phrase: []const u8, expecte |
| 134 | 149 | } |
| 135 | 150 | |
| 136 | 151 | var new_check = Check.create(self.builder); |
| 137 | | new_check.actions.append(.{ .compare = ca }) catch unreachable; |
| 152 | new_check.computeEq(ca); |
| 138 | 153 | self.checks.append(new_check) catch unreachable; |
| 139 | 154 | } |
| 140 | 155 | |
| ... | ... | @@ -159,80 +174,50 @@ fn make(step: *Step) !void { |
| 159 | 174 | var vars = std.StringHashMap(u64).init(gpa); |
| 160 | 175 | |
| 161 | 176 | for (self.checks.items) |chk| { |
| 162 | | const first_action = chk.actions.items[0]; |
| 163 | | |
| 164 | | switch (first_action) { |
| 165 | | .exact_match => |first| { |
| 166 | | if (mem.indexOf(u8, output, first)) |index| { |
| 167 | | // TODO backtrack to track current scope |
| 168 | | var it = std.mem.tokenize(u8, output[index..], "\r\n"); |
| 169 | | |
| 170 | | outer: for (chk.actions.items[1..]) |next_action| { |
| 171 | | switch (next_action) { |
| 172 | | .exact_match => |exact| { |
| 173 | | while (it.next()) |line| { |
| 174 | | if (mem.eql(u8, line, exact)) { |
| 175 | | std.debug.print("{s} == {s}\n", .{ line, exact }); |
| 176 | | continue :outer; |
| 177 | | } |
| 178 | | std.debug.print("{s} != {s}\n", .{ line, exact }); |
| 179 | | } else { |
| 180 | | return error.TestFailed; |
| 181 | | } |
| 182 | | }, |
| 183 | | .extract_var => |extract| { |
| 184 | | const phrase = extract.fuzzy_match; |
| 185 | | while (it.next()) |line| { |
| 186 | | if (mem.indexOf(u8, line, phrase)) |found| { |
| 187 | | std.debug.print("{s} in {s}\n", .{ phrase, line }); |
| 188 | | // Extract variable and save back in the action. |
| 189 | | const trimmed = mem.trim(u8, line[found + phrase.len ..], " "); |
| 190 | | const parsed = try std.fmt.parseInt(u64, trimmed, 16); |
| 191 | | try vars.putNoClobber(extract.var_name, parsed); |
| 192 | | continue :outer; |
| 193 | | } |
| 194 | | std.debug.print("{s} not in {s}\n", .{ extract.fuzzy_match, line }); |
| 195 | | } |
| 196 | | }, |
| 197 | | .compare => unreachable, |
| 177 | var it = mem.tokenize(u8, output, "\r\n"); |
| 178 | for (chk.actions.items) |act| { |
| 179 | switch (act) { |
| 180 | .match => |match_act| { |
| 181 | while (it.next()) |line| { |
| 182 | if (try match_act.match(line, &vars)) { |
| 183 | std.debug.print("{s} == {s}\n", .{ line, match_act.needle }); |
| 184 | break; |
| 185 | } else { |
| 186 | std.debug.print("{s} != {s}\n", .{ line, match_act.needle }); |
| 198 | 187 | } |
| 188 | } else { |
| 189 | return error.TestFailed; |
| 190 | } |
| 191 | }, |
| 192 | .compute_eq => |c_eq| { |
| 193 | var values = std.ArrayList(u64).init(gpa); |
| 194 | try values.ensureTotalCapacity(c_eq.var_stack.items.len); |
| 195 | for (c_eq.var_stack.items) |vv| { |
| 196 | const val = vars.get(vv) orelse return error.TestFailed; |
| 197 | values.appendAssumeCapacity(val); |
| 199 | 198 | } |
| 200 | | } else { |
| 201 | | return error.TestFailed; |
| 202 | | } |
| 203 | | }, |
| 204 | | .compare => |act| { |
| 205 | | var values = std.ArrayList(u64).init(gpa); |
| 206 | | try values.ensureTotalCapacity(act.var_stack.items.len); |
| 207 | | for (act.var_stack.items) |vv| { |
| 208 | | const val = vars.get(vv) orelse return error.TestFailed; |
| 209 | | values.appendAssumeCapacity(val); |
| 210 | | } |
| 211 | 199 | |
| 212 | | var op_i: usize = 1; |
| 213 | | var reduced: u64 = values.items[0]; |
| 214 | | for (act.op_stack.items) |op| { |
| 215 | | const other = values.items[op_i]; |
| 216 | | switch (op) { |
| 217 | | .add => { |
| 218 | | reduced += other; |
| 219 | | }, |
| 200 | var op_i: usize = 1; |
| 201 | var reduced: u64 = values.items[0]; |
| 202 | for (c_eq.op_stack.items) |op| { |
| 203 | const other = values.items[op_i]; |
| 204 | switch (op) { |
| 205 | .add => { |
| 206 | reduced += other; |
| 207 | }, |
| 208 | } |
| 220 | 209 | } |
| 221 | | } |
| 222 | 210 | |
| 223 | | const expected = switch (act.expected) { |
| 224 | | .literal => |exp| exp, |
| 225 | | .varr => |vv| vars.get(vv) orelse return error.TestFailed, |
| 226 | | }; |
| 227 | | if (reduced != expected) return error.TestFailed; |
| 228 | | }, |
| 229 | | .extract_var => unreachable, |
| 211 | const expected = vars.get(c_eq.expected) orelse return error.TestFailed; |
| 212 | if (reduced != expected) return error.TestFailed; |
| 213 | }, |
| 214 | } |
| 230 | 215 | } |
| 231 | 216 | } |
| 232 | 217 | |
| 233 | 218 | var it = vars.iterator(); |
| 234 | 219 | while (it.next()) |entry| { |
| 235 | | std.debug.print(" {s} => {x}", .{ entry.key_ptr.*, entry.value_ptr.* }); |
| 220 | std.debug.print(" {s} => {x}\n", .{ entry.key_ptr.*, entry.value_ptr.* }); |
| 236 | 221 | } |
| 237 | 222 | } |
| 238 | 223 | |