| ... | @@ -36,20 +36,52 @@ pub fn create(builder: *Builder, source: build.FileSource, obj_format: std.Targe | ... | @@ -36,20 +36,52 @@ pub fn create(builder: *Builder, source: build.FileSource, obj_format: std.Targe |
| 36 | } | 36 | } |
| 37 | | 37 | |
| 38 | const Action = union(enum) { | 38 | const Action = union(enum) { |
| 39 | exact_match: []const u8, | 39 | match: MatchAction, |
| 40 | extract_var: struct { | 40 | compute_eq: ComputeEqAction, |
| 41 | fuzzy_match: []const u8, | | |
| 42 | var_name: []const u8, | | |
| 43 | var_value: u64, | | |
| 44 | }, | | |
| 45 | compare: CompareAction, | | |
| 46 | }; | 41 | }; |
| 47 | | 42 | |
| 48 | const CompareAction = struct { | 43 | const MatchAction = struct { |
| 49 | expected: union(enum) { | 44 | needle: []const u8, |
| 50 | literal: u64, | 45 | |
| 51 | varr: []const u8, | 46 | fn match(act: MatchAction, haystack: []const u8, global_vars: anytype) !bool { |
| 52 | }, | 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 | var_stack: std.ArrayList([]const u8), | 85 | var_stack: std.ArrayList([]const u8), |
| 54 | op_stack: std.ArrayList(Op), | 86 | op_stack: std.ArrayList(Op), |
| 55 | | 87 | |
| ... | @@ -69,43 +101,29 @@ const Check = struct { | ... | @@ -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 | self.actions.append(.{ | 105 | self.actions.append(.{ |
| 74 | .exact_match = self.builder.dupe(phrase), | 106 | .match = .{ .needle = self.builder.dupe(needle) }, |
| 75 | }) catch unreachable; | 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 | self.actions.append(.{ | 111 | self.actions.append(.{ |
| 80 | .extract_var = .{ | 112 | .compute_eq = act, |
| 81 | .fuzzy_match = self.builder.dupe(phrase), | | |
| 82 | .var_name = self.builder.dupe(var_name), | | |
| 83 | .var_value = undefined, | | |
| 84 | }, | | |
| 85 | }) catch unreachable; | 113 | }) catch unreachable; |
| 86 | } | 114 | } |
| 87 | }; | 115 | }; |
| 88 | | 116 | |
| 89 | pub fn check(self: *CheckObjectStep, phrase: []const u8) void { | 117 | pub fn check(self: *CheckObjectStep, phrase: []const u8) void { |
| 90 | var new_check = Check.create(self.builder); | 118 | var new_check = Check.create(self.builder); |
| 91 | new_check.exactMatch(phrase); | 119 | new_check.match(phrase); |
| 92 | self.checks.append(new_check) catch unreachable; | 120 | self.checks.append(new_check) catch unreachable; |
| 93 | } | 121 | } |
| 94 | | 122 | |
| 95 | pub fn checkNext(self: *CheckObjectStep, phrase: []const u8) void { | 123 | pub fn checkNext(self: *CheckObjectStep, phrase: []const u8) void { |
| 96 | assert(self.checks.items.len > 0); | 124 | assert(self.checks.items.len > 0); |
| 97 | const last = &self.checks.items[self.checks.items.len - 1]; | 125 | const last = &self.checks.items[self.checks.items.len - 1]; |
| 98 | last.exactMatch(phrase); | 126 | last.match(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]); | | |
| 109 | } | 127 | } |
| 110 | | 128 | |
| 111 | pub fn checkInSymtab(self: *CheckObjectStep) void { | 129 | pub fn checkInSymtab(self: *CheckObjectStep) void { |
| ... | @@ -113,18 +131,15 @@ pub fn checkInSymtab(self: *CheckObjectStep) void { | ... | @@ -113,18 +131,15 @@ pub fn checkInSymtab(self: *CheckObjectStep) void { |
| 113 | self.check("symtab"); | 131 | self.check("symtab"); |
| 114 | } | 132 | } |
| 115 | | 133 | |
| 116 | pub fn checkCompare(self: *CheckObjectStep, comptime phrase: []const u8, expected: anytype) void { | 134 | pub fn checkComputeEq(self: *CheckObjectStep, program: []const u8, expected: []const u8) void { |
| 117 | comptime assert(phrase[0] == '{'); | | |
| 118 | comptime assert(phrase[phrase.len - 1] == '}'); | | |
| 119 | | | |
| 120 | const gpa = self.builder.allocator; | 135 | const gpa = self.builder.allocator; |
| 121 | var ca = CompareAction{ | 136 | var ca = ComputeEqAction{ |
| 122 | .expected = expected, | 137 | .expected = expected, |
| 123 | .var_stack = std.ArrayList([]const u8).init(gpa), | 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 | while (it.next()) |next| { | 143 | while (it.next()) |next| { |
| 129 | if (mem.eql(u8, next, "+")) { | 144 | if (mem.eql(u8, next, "+")) { |
| 130 | ca.op_stack.append(.add) catch unreachable; | 145 | ca.op_stack.append(.add) catch unreachable; |
| ... | @@ -134,7 +149,7 @@ pub fn checkCompare(self: *CheckObjectStep, comptime phrase: []const u8, expecte | ... | @@ -134,7 +149,7 @@ pub fn checkCompare(self: *CheckObjectStep, comptime phrase: []const u8, expecte |
| 134 | } | 149 | } |
| 135 | | 150 | |
| 136 | var new_check = Check.create(self.builder); | 151 | var new_check = Check.create(self.builder); |
| 137 | new_check.actions.append(.{ .compare = ca }) catch unreachable; | 152 | new_check.computeEq(ca); |
| 138 | self.checks.append(new_check) catch unreachable; | 153 | self.checks.append(new_check) catch unreachable; |
| 139 | } | 154 | } |
| 140 | | 155 | |
| ... | @@ -159,80 +174,50 @@ fn make(step: *Step) !void { | ... | @@ -159,80 +174,50 @@ fn make(step: *Step) !void { |
| 159 | var vars = std.StringHashMap(u64).init(gpa); | 174 | var vars = std.StringHashMap(u64).init(gpa); |
| 160 | | 175 | |
| 161 | for (self.checks.items) |chk| { | 176 | for (self.checks.items) |chk| { |
| 162 | const first_action = chk.actions.items[0]; | 177 | var it = mem.tokenize(u8, output, "\r\n"); |
| 163 | | 178 | for (chk.actions.items) |act| { |
| 164 | switch (first_action) { | 179 | switch (act) { |
| 165 | .exact_match => |first| { | 180 | .match => |match_act| { |
| 166 | if (mem.indexOf(u8, output, first)) |index| { | 181 | while (it.next()) |line| { |
| 167 | // TODO backtrack to track current scope | 182 | if (try match_act.match(line, &vars)) { |
| 168 | var it = std.mem.tokenize(u8, output[index..], "\r\n"); | 183 | std.debug.print("{s} == {s}\n", .{ line, match_act.needle }); |
| 169 | | 184 | break; |
| 170 | outer: for (chk.actions.items[1..]) |next_action| { | 185 | } else { |
| 171 | switch (next_action) { | 186 | std.debug.print("{s} != {s}\n", .{ line, match_act.needle }); |
| 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, | | |
| 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; | 200 | var op_i: usize = 1; |
| 213 | var reduced: u64 = values.items[0]; | 201 | var reduced: u64 = values.items[0]; |
| 214 | for (act.op_stack.items) |op| { | 202 | for (c_eq.op_stack.items) |op| { |
| 215 | const other = values.items[op_i]; | 203 | const other = values.items[op_i]; |
| 216 | switch (op) { | 204 | switch (op) { |
| 217 | .add => { | 205 | .add => { |
| 218 | reduced += other; | 206 | reduced += other; |
| 219 | }, | 207 | }, |
| | 208 | } |
| 220 | } | 209 | } |
| 221 | } | | |
| 222 | | 210 | |
| 223 | const expected = switch (act.expected) { | 211 | const expected = vars.get(c_eq.expected) orelse return error.TestFailed; |
| 224 | .literal => |exp| exp, | 212 | if (reduced != expected) return error.TestFailed; |
| 225 | .varr => |vv| vars.get(vv) orelse return error.TestFailed, | 213 | }, |
| 226 | }; | 214 | } |
| 227 | if (reduced != expected) return error.TestFailed; | | |
| 228 | }, | | |
| 229 | .extract_var => unreachable, | | |
| 230 | } | 215 | } |
| 231 | } | 216 | } |
| 232 | | 217 | |
| 233 | var it = vars.iterator(); | 218 | var it = vars.iterator(); |
| 234 | while (it.next()) |entry| { | 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 | |