authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-04-06 05:34:04-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-04-06 05:34:04-04:00
log6fbe1632d0c958b9abbc9f38a7e497ef69543bf1
treec5ee08ab23927e79a2827beebeb5e879e6596f7c
parentd15bcdce691c1f42f70a5e9943817eb5ba974893

Update zig build system to support user defined options

* Fix assertion failure when switching on type. Closes #310 * Update zig build system to support user defined options. See #204 * fmt.format supports {sNNN} to set padding for a buffer arg. * add std.fmt.bufPrint and std.fmt.allocPrint * std.hash_map.HashMap.put returns the previous value * add std.mem.startsWith

10 files changed, 442 insertions(+), 69 deletions(-)

src/ir.cpp+13-1
......@@ -57,6 +57,7 @@ static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *ins
5757static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, TypeTableEntry *expected_type);
5858
5959ConstExprValue *const_ptr_pointee(ConstExprValue *const_val) {
60 assert(const_val->type->id == TypeTableEntryIdPointer);
6061 assert(const_val->special == ConstValSpecialStatic);
6162 switch (const_val->data.x_ptr.special) {
6263 case ConstPtrSpecialInvalid:
......@@ -10350,10 +10351,21 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,
1035010351 if (type_is_invalid(target_value_ptr->value.type))
1035110352 return ira->codegen->builtin_types.entry_invalid;
1035210353
10354 if (target_value_ptr->value.type->id == TypeTableEntryIdMetaType) {
10355 assert(instr_is_comptime(target_value_ptr));
10356 TypeTableEntry *ptr_type = target_value_ptr->value.data.x_type;
10357 assert(ptr_type->id == TypeTableEntryIdPointer);
10358 ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base);
10359 out_val->type = ira->codegen->builtin_types.entry_type;
10360 out_val->data.x_type = ptr_type->data.pointer.child_type;
10361 return out_val->type;
10362 }
10363
1035310364 assert(target_value_ptr->value.type->id == TypeTableEntryIdPointer);
10365
1035410366 TypeTableEntry *target_type = target_value_ptr->value.type->data.pointer.child_type;
1035510367 ConstExprValue *pointee_val = nullptr;
10356 if (target_value_ptr->value.special != ConstValSpecialRuntime) {
10368 if (instr_is_comptime(target_value_ptr)) {
1035710369 pointee_val = const_ptr_pointee(&target_value_ptr->value);
1035810370 if (pointee_val->special == ConstValSpecialRuntime)
1035910371 pointee_val = nullptr;
src/main.cpp+1-2
......@@ -167,9 +167,8 @@ int main(int argc, char **argv) {
167167 ZigList<const char *> args = {0};
168168 args.append(zig_exe_path);
169169 for (int i = 2; i < argc; i += 1) {
170 if (strcmp(argv[i], "--verbose") == 0) {
170 if (strcmp(argv[i], "--debug-build-verbose") == 0) {
171171 verbose = true;
172 args.append(argv[i]);
173172 } else {
174173 args.append(argv[i]);
175174 }
std/buf_map.zig+3-4
......@@ -11,14 +11,13 @@ pub const BufMap = struct {
1111
1212 pub fn init(allocator: &Allocator) -> BufMap {
1313 var self = BufMap {
14 .hash_map = undefined,
14 .hash_map = BufMapHashMap.init(allocator),
1515 };
16 self.hash_map.init(allocator);
1716 return self;
1817 }
1918
2019 pub fn deinit(self: &BufMap) {
21 var it = self.hash_map.entryIterator();
20 var it = self.hash_map.iterator();
2221 while (true) {
2322 const entry = it.next() ?? break;
2423 self.free(entry.key);
......@@ -54,7 +53,7 @@ pub const BufMap = struct {
5453 }
5554
5655 pub fn iterator(self: &const BufMap) -> BufMapHashMap.Iterator {
57 return self.hash_map.entryIterator();
56 return self.hash_map.iterator();
5857 }
5958
6059 fn free(self: &BufMap, value: []const u8) {
std/buf_set.zig+3-4
......@@ -9,14 +9,13 @@ pub const BufSet = struct {
99
1010 pub fn init(allocator: &Allocator) -> BufSet {
1111 var self = BufSet {
12 .hash_map = undefined,
12 .hash_map = BufSetHashMap.init(allocator),
1313 };
14 self.hash_map.init(allocator);
1514 return self;
1615 }
1716
1817 pub fn deinit(self: &BufSet) {
19 var it = self.hash_map.entryIterator();
18 var it = self.hash_map.iterator();
2019 while (true) {
2120 const entry = it.next() ?? break;
2221 self.free(entry.key);
......@@ -43,7 +42,7 @@ pub const BufSet = struct {
4342 }
4443
4544 pub fn iterator(self: &const BufSet) -> BufSetHashMap.Iterator {
46 return self.hash_map.entryIterator();
45 return self.hash_map.iterator();
4746 }
4847
4948 fn free(self: &BufSet, value: []const u8) {
std/build.zig+212-27
......@@ -2,6 +2,7 @@ const io = @import("io.zig");
22const mem = @import("mem.zig");
33const debug = @import("debug.zig");
44const List = @import("list.zig").List;
5const HashMap = @import("hash_map.zig").HashMap;
56const Allocator = @import("mem.zig").Allocator;
67const os = @import("os/index.zig");
78const StdIo = os.ChildProcess.StdIo;
......@@ -12,21 +13,58 @@ error ExtraArg;
1213error UncleanExit;
1314
1415pub const Builder = struct {
15 zig_exe: []const u8,
1616 allocator: &Allocator,
1717 exe_list: List(&Exe),
1818 lib_paths: List([]const u8),
1919 include_paths: List([]const u8),
2020 rpaths: List([]const u8),
21 user_input_options: UserInputOptionsMap,
22 available_options_map: AvailableOptionsMap,
23 available_options_list: List(AvailableOption),
24 verbose: bool,
25 invalid_user_input: bool,
26
27 const UserInputOptionsMap = HashMap([]const u8, UserInputOption, mem.hash_slice_u8, mem.eql_slice_u8);
28 const AvailableOptionsMap = HashMap([]const u8, AvailableOption, mem.hash_slice_u8, mem.eql_slice_u8);
29
30 const AvailableOption = struct {
31 name: []const u8,
32 type_id: TypeId,
33 description: []const u8,
34 };
35
36 const UserInputOption = struct {
37 name: []const u8,
38 value: UserValue,
39 used: bool,
40 };
41
42 const UserValue = enum {
43 Flag,
44 Scalar: []const u8,
45 List: List([]const u8),
46 };
47
48 const TypeId = enum {
49 Bool,
50 Int,
51 Float,
52 String,
53 List,
54 };
2155
22 pub fn init(zig_exe: []const u8, allocator: &Allocator) -> Builder {
56 pub fn init(allocator: &Allocator) -> Builder {
2357 var self = Builder {
24 .zig_exe = zig_exe,
58 .verbose = false,
59 .invalid_user_input = false,
2560 .allocator = allocator,
2661 .exe_list = List(&Exe).init(allocator),
2762 .lib_paths = List([]const u8).init(allocator),
2863 .include_paths = List([]const u8).init(allocator),
2964 .rpaths = List([]const u8).init(allocator),
65 .user_input_options = UserInputOptionsMap.init(allocator),
66 .available_options_map = AvailableOptionsMap.init(allocator),
67 .available_options_list = List(AvailableOption).init(allocator),
3068 };
3169 self.processNixOSEnvVars();
3270 return self;
......@@ -46,6 +84,8 @@ pub const Builder = struct {
4684 pub fn addExeErr(self: &Builder, root_src: []const u8, name: []const u8) -> %&Exe {
4785 const exe = %return self.allocator.create(Exe);
4886 *exe = Exe {
87 .verbose = false,
88 .release = false,
4989 .root_src = root_src,
5090 .name = name,
5191 .target = Target.Native,
......@@ -68,20 +108,13 @@ pub const Builder = struct {
68108 %%self.lib_paths.append(path);
69109 }
70110
71 pub fn make(self: &Builder, leftover_arg_index: usize) -> %void {
111 pub fn make(self: &Builder, zig_exe: []const u8, targets: []const []const u8) -> %void {
112 if (targets.len != 0) {
113 debug.panic("TODO non default targets");
114 }
115
72116 var env_map = %return os.getEnvMap(self.allocator);
73117
74 var verbose = false;
75 var arg_i: usize = leftover_arg_index;
76 while (arg_i < os.args.count(); arg_i += 1) {
77 const arg = os.args.at(arg_i);
78 if (mem.eql(u8, arg, "--verbose")) {
79 verbose = true;
80 } else {
81 %%io.stderr.printf("Unrecognized argument: '{}'\n", arg);
82 return error.ExtraArg;
83 }
84 }
85118 for (self.exe_list.toSlice()) |exe| {
86119 var zig_args = List([]const u8).init(self.allocator);
87120 defer zig_args.deinit();
......@@ -89,10 +122,14 @@ pub const Builder = struct {
89122 %return zig_args.append("build_exe"[0...]); // TODO issue #296
90123 %return zig_args.append(exe.root_src);
91124
92 if (verbose) {
125 if (exe.verbose) {
93126 %return zig_args.append("--verbose"[0...]); // TODO issue #296
94127 }
95128
129 if (exe.release) {
130 %return zig_args.append("--release"[0...]); // TODO issue #296
131 }
132
96133 %return zig_args.append("--name"[0...]); // TODO issue #296
97134 %return zig_args.append(exe.name);
98135
......@@ -149,22 +186,21 @@ pub const Builder = struct {
149186 %return zig_args.append(lib_path);
150187 }
151188
189 if (self.verbose) {
190 printInvocation(zig_exe, zig_args);
191 }
152192 // TODO issue #301
153 var child = os.ChildProcess.spawn(self.zig_exe, zig_args.toSliceConst(), &env_map,
193 var child = os.ChildProcess.spawn(zig_exe, zig_args.toSliceConst(), &env_map,
154194 StdIo.Ignore, StdIo.Inherit, StdIo.Inherit, self.allocator)
155195 %% |err| debug.panic("Unable to spawn zig compiler: {}\n", @errorName(err));
156196 const term = %%child.wait();
157 const exe_result = switch (term) {
197 switch (term) {
158198 Term.Clean => |code| {
159199 if (code != 0) {
160 %%io.stderr.printf("\nCompile failed with code {}. To reproduce:\n", code);
161 printInvocation(self.zig_exe, zig_args);
162200 return error.UncleanExit;
163201 }
164202 },
165203 else => {
166 %%io.stderr.printf("\nCompile crashed. To reproduce:\n");
167 printInvocation(self.zig_exe, zig_args);
168204 return error.UncleanExit;
169205 },
170206 };
......@@ -208,6 +244,144 @@ pub const Builder = struct {
208244 }
209245 }
210246 }
247
248 pub fn option(self: &Builder, comptime T: type, name: []const u8, description: []const u8) -> ?T {
249 const type_id = typeToEnum(T);
250 const available_option = AvailableOption {
251 .name = name,
252 .type_id = type_id,
253 .description = description,
254 };
255 if (const _ ?= %%self.available_options_map.put(name, available_option)) {
256 debug.panic("Option '{}' declared twice", name);
257 }
258 %%self.available_options_list.append(available_option);
259
260 const entry = self.user_input_options.get(name) ?? return null;
261 entry.value.used = true;
262 switch (type_id) {
263 TypeId.Bool => switch (entry.value.value) {
264 UserValue.Flag => return true,
265 UserValue.Scalar => |s| {
266 if (mem.eql(u8, s, "true")) {
267 return true;
268 } else if (mem.eql(u8, s, "false")) {
269 return false;
270 } else {
271 %%io.stderr.printf("Expected -O{} to be a boolean, but received '{}'\n", name, s);
272 self.markInvalidUserInput();
273 return null;
274 }
275 },
276 UserValue.List => {
277 %%io.stderr.printf("Expected -O{} to be a boolean, but received a list.\n", name);
278 self.markInvalidUserInput();
279 return null;
280 },
281 },
282 TypeId.Int => debug.panic("TODO integer options to build script"),
283 TypeId.Float => debug.panic("TODO float options to build script"),
284 TypeId.String => debug.panic("TODO string options to build script"),
285 TypeId.List => debug.panic("TODO list options to build script"),
286 }
287 }
288
289 pub fn addUserInputOption(self: &Builder, name: []const u8, value: []const u8) -> bool {
290 if (var prev_value ?= %%self.user_input_options.put(name, UserInputOption {
291 .name = name,
292 .value = UserValue.Scalar { value },
293 .used = false,
294 })) {
295 switch (prev_value.value) {
296 UserValue.Scalar => |s| {
297 var list = List([]const u8).init(self.allocator);
298 %%list.append(s);
299 %%list.append(value);
300 %%self.user_input_options.put(name, UserInputOption {
301 .name = name,
302 .value = UserValue.List { list },
303 .used = false,
304 });
305 },
306 UserValue.List => |*list| {
307 %%list.append(value);
308 %%self.user_input_options.put(name, UserInputOption {
309 .name = name,
310 .value = UserValue.List { *list },
311 .used = false,
312 });
313 },
314 UserValue.Flag => {
315 %%io.stderr.printf("Option '-O{}={}' conflicts with flag '-O{}'.\n", name, value, name);
316 return true;
317 },
318 }
319 }
320 return false;
321 }
322
323 pub fn addUserInputFlag(self: &Builder, name: []const u8) -> bool {
324 if (const prev_value ?= %%self.user_input_options.put(name, UserInputOption {
325 .name = name,
326 .value = UserValue.Flag,
327 .used = false,
328 })) {
329 switch (prev_value.value) {
330 UserValue.Scalar => |s| {
331 %%io.stderr.printf("Flag '-O{}' conflicts with option '-O{}={}'.\n", name, name, s);
332 return true;
333 },
334 UserValue.List => {
335 %%io.stderr.printf("Flag '-O{}' conflicts with multiple options of the same name.\n", name);
336 return true;
337 },
338 UserValue.Flag => {},
339 }
340 }
341 return false;
342 }
343
344 fn typeToEnum(comptime T: type) -> TypeId {
345 if (@isInteger(T)) {
346 TypeId.Int
347 } else if (@isFloat(T)) {
348 TypeId.Float
349 } else switch (T) {
350 bool => TypeId.Bool,
351 []const u8 => TypeId.String,
352 []const []const u8 => TypeId.List,
353 else => @compileError("Unsupported type: " ++ @typeName(T)),
354 }
355 }
356
357 fn markInvalidUserInput(self: &Builder) {
358 self.invalid_user_input = true;
359 }
360
361 pub fn typeIdName(id: TypeId) -> []const u8 {
362 return switch (id) {
363 TypeId.Bool => ([]const u8)("bool"), // TODO issue #125
364 TypeId.Int => ([]const u8)("int"), // TODO issue #125
365 TypeId.Float => ([]const u8)("float"), // TODO issue #125
366 TypeId.String => ([]const u8)("string"), // TODO issue #125
367 TypeId.List => ([]const u8)("list"), // TODO issue #125
368 };
369 }
370
371 pub fn validateUserInputDidItFail(self: &Builder) -> bool {
372 // make sure all args are used
373 var it = self.user_input_options.iterator();
374 while (true) {
375 const entry = it.next() ?? break;
376 if (!entry.value.used) {
377 %%io.stderr.printf("Invalid option: -O{}\n\n", entry.key);
378 self.markInvalidUserInput();
379 }
380 }
381
382 return self.invalid_user_input;
383 }
384
211385};
212386
213387const CrossTarget = struct {
......@@ -233,12 +407,14 @@ const Exe = struct {
233407 target: Target,
234408 linker_script: LinkerScript,
235409 link_libs: BufSet,
410 verbose: bool,
411 release: bool,
236412
237 fn deinit(self: &Exe) {
413 pub fn deinit(self: &Exe) {
238414 self.link_libs.deinit();
239415 }
240416
241 fn setTarget(self: &Exe, target_arch: Arch, target_os: Os, target_environ: Environ) {
417 pub fn setTarget(self: &Exe, target_arch: Arch, target_os: Os, target_environ: Environ) {
242418 self.target = Target.Cross {
243419 CrossTarget {
244420 .arch = target_arch,
......@@ -250,17 +426,25 @@ const Exe = struct {
250426
251427 /// Exe keeps a reference to script for its lifetime or until this function
252428 /// is called again.
253 fn setLinkerScriptContents(self: &Exe, script: []const u8) {
429 pub fn setLinkerScriptContents(self: &Exe, script: []const u8) {
254430 self.linker_script = LinkerScript.Embed { script };
255431 }
256432
257 fn setLinkerScriptPath(self: &Exe, path: []const u8) {
433 pub fn setLinkerScriptPath(self: &Exe, path: []const u8) {
258434 self.linker_script = LinkerScript.Path { path };
259435 }
260436
261 fn linkLibrary(self: &Exe, name: []const u8) {
437 pub fn linkLibrary(self: &Exe, name: []const u8) {
262438 %%self.link_libs.put(name);
263439 }
440
441 pub fn setVerbose(self: &Exe, value: bool) {
442 self.verbose = value;
443 }
444
445 pub fn setRelease(self: &Exe, value: bool) {
446 self.release = value;
447 }
264448};
265449
266450fn handleErr(err: error) -> noreturn {
......@@ -401,3 +585,4 @@ fn targetEnvironName(target_environ: Environ) -> []const u8 {
401585 Environ.coreclr => ([]const u8)("coreclr"),
402586 };
403587}
588
std/fmt.zig+72
......@@ -13,6 +13,8 @@ const State = enum { // TODO put inside format function and make sure the name a
1313 Integer,
1414 IntegerWidth,
1515 Character,
16 Buf,
17 BufWidth,
1618};
1719
1820/// Renders fmt string with args, calling output with slices of bytes.
......@@ -82,8 +84,21 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)->bool,
8284 'c' => {
8385 state = State.Character;
8486 },
87 's' => {
88 state = State.Buf;
89 },
8590 else => @compileError("Unknown format character: " ++ []u8{c}),
8691 },
92 State.Buf => switch (c) {
93 '}' => {
94 return output(context, args[next_arg]);
95 },
96 '0' ... '9' => {
97 width_start = i;
98 state = State.BufWidth;
99 },
100 else => @compileError("Unexpected character in format string: " ++ []u8{c}),
101 },
87102 State.CloseBrace => switch (c) {
88103 '}' => {
89104 state = State.Start;
......@@ -117,6 +132,18 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)->bool,
117132 '0' ... '9' => {},
118133 else => @compileError("Unexpected character in format string: " ++ []u8{c}),
119134 },
135 State.BufWidth => switch (c) {
136 '}' => {
137 width = comptime %%parseUnsigned(usize, fmt[width_start...i], 10);
138 if (!formatBuf(args[next_arg], width, context, output))
139 return false;
140 next_arg += 1;
141 state = State.Start;
142 start_index = i + 1;
143 },
144 '0' ... '9' => {},
145 else => @compileError("Unexpected character in format string: " ++ []u8{c}),
146 },
120147 State.Character => switch (c) {
121148 '}' => {
122149 if (!formatAsciiChar(args[next_arg], context, output))
......@@ -165,6 +192,23 @@ pub fn formatAsciiChar(c: u8, context: var, output: fn(@typeOf(context), []const
165192 return output(context, (&c)[0...1]);
166193}
167194
195pub fn formatBuf(buf: []const u8, width: usize,
196 context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool
197{
198 if (!output(context, buf))
199 return false;
200
201 var leftover_padding = if (width > buf.len) (width - buf.len) else return true;
202 const pad_byte: u8 = ' ';
203 while (leftover_padding > 0; leftover_padding -= 1) {
204 if (!output(context, (&pad_byte)[0...1]))
205 return false;
206 }
207
208 return true;
209}
210
211
168212pub fn formatInt(value: var, base: u8, uppercase: bool, width: usize,
169213 context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool
170214{
......@@ -291,6 +335,34 @@ fn digitToChar(digit: u8, uppercase: bool) -> u8 {
291335 };
292336}
293337
338const BufPrintContext = struct {
339 remaining: []u8,
340};
341
342fn bufPrintWrite(context: &BufPrintContext, bytes: []const u8) -> bool {
343 mem.copy(u8, context.remaining, bytes);
344 context.remaining = context.remaining[bytes.len...];
345 return true;
346}
347
348pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) {
349 var context = BufPrintContext { .remaining = buf, };
350 _ = format(&context, bufPrintWrite, fmt, args);
351}
352
353pub fn allocPrint(allocator: &mem.Allocator, comptime fmt: []const u8, args: ...) -> %[]u8 {
354 var size: usize = 0;
355 _ = format(&size, countSize, fmt, args);
356 const buf = %return allocator.alloc(u8, size);
357 bufPrint(buf, fmt, args);
358 return buf;
359}
360
361fn countSize(size: &usize, bytes: []const u8) -> bool {
362 *size += bytes.len;
363 return true;
364}
365
294366test "testBufPrintInt" {
295367 var buffer: [max_int_digits]u8 = undefined;
296368 const buf = buffer[0...];
std/hash_map.zig+33-22
......@@ -54,13 +54,15 @@ pub fn HashMap(comptime K: type, comptime V: type,
5454 }
5555 };
5656
57 pub fn init(hm: &Self, allocator: &Allocator) {
58 hm.entries = []Entry{};
59 hm.allocator = allocator;
60 hm.size = 0;
61 hm.max_distance_from_start_index = 0;
62 // it doesn't actually matter what we set this to since we use wrapping integer arithmetic
63 hm.modification_count = undefined;
57 pub fn init(allocator: &Allocator) -> Self {
58 Self {
59 .entries = []Entry{},
60 .allocator = allocator,
61 .size = 0,
62 .max_distance_from_start_index = 0,
63 // it doesn't actually matter what we set this to since we use wrapping integer arithmetic
64 .modification_count = undefined,
65 }
6466 }
6567
6668 pub fn deinit(hm: &Self) {
......@@ -76,7 +78,8 @@ pub fn HashMap(comptime K: type, comptime V: type,
7678 hm.incrementModificationCount();
7779 }
7880
79 pub fn put(hm: &Self, key: K, value: V) -> %void {
81 /// Returns the value that was already there.
82 pub fn put(hm: &Self, key: K, value: &const V) -> %?V {
8083 if (hm.entries.len == 0) {
8184 %return hm.initCapacity(16);
8285 }
......@@ -89,13 +92,13 @@ pub fn HashMap(comptime K: type, comptime V: type,
8992 // dump all of the old elements into the new table
9093 for (old_entries) |*old_entry| {
9194 if (old_entry.used) {
92 hm.internalPut(old_entry.key, old_entry.value);
95 _ = hm.internalPut(old_entry.key, old_entry.value);
9396 }
9497 }
9598 hm.allocator.free(old_entries);
9699 }
97100
98 hm.internalPut(key, value);
101 return hm.internalPut(key, value);
99102 }
100103
101104 pub fn get(hm: &Self, key: K) -> ?&Entry {
......@@ -134,7 +137,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
134137 return null;
135138 }
136139
137 pub fn entryIterator(hm: &const Self) -> Iterator {
140 pub fn iterator(hm: &const Self) -> Iterator {
138141 return Iterator {
139142 .hm = hm,
140143 .count = 0,
......@@ -158,9 +161,10 @@ pub fn HashMap(comptime K: type, comptime V: type,
158161 }
159162 }
160163
161 fn internalPut(hm: &Self, orig_key: K, orig_value: V) {
164 /// Returns the value that was already there.
165 fn internalPut(hm: &Self, orig_key: K, orig_value: &const V) -> ?V {
162166 var key = orig_key;
163 var value = orig_value;
167 var value = *orig_value;
164168 const start_index = hm.keyToIndex(key);
165169 var roll_over: usize = 0;
166170 var distance_from_start_index: usize = 0;
......@@ -187,7 +191,10 @@ pub fn HashMap(comptime K: type, comptime V: type,
187191 continue;
188192 }
189193
190 if (!entry.used) {
194 var result: ?V = null;
195 if (entry.used) {
196 result = entry.value;
197 } else {
191198 // adding an entry. otherwise overwriting old value with
192199 // same key
193200 hm.size += 1;
......@@ -200,7 +207,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
200207 .key = key,
201208 .value = value,
202209 };
203 return;
210 return result;
204211 }
205212 unreachable // put into a full map
206213 }
......@@ -224,15 +231,18 @@ pub fn HashMap(comptime K: type, comptime V: type,
224231}
225232
226233test "basicHashMapTest" {
227 var map: HashMap(i32, i32, hash_i32, eql_i32) = undefined;
228 map.init(&debug.global_allocator);
234 var map = HashMap(i32, i32, hash_i32, eql_i32).init(&debug.global_allocator);
229235 defer map.deinit();
230236
231 %%map.put(1, 11);
232 %%map.put(2, 22);
233 %%map.put(3, 33);
234 %%map.put(4, 44);
235 %%map.put(5, 55);
237 // TODO issue #311
238 assert(%%map.put(1, i32(11)) == null);
239 assert(%%map.put(2, i32(22)) == null);
240 assert(%%map.put(3, i32(33)) == null);
241 assert(%%map.put(4, i32(44)) == null);
242 assert(%%map.put(5, i32(55)) == null);
243
244 assert(??%%map.put(5, i32(66)) == 55);
245 assert(??%%map.put(5, i32(55)) == 66);
236246
237247 assert((??map.get(2)).value == 22);
238248 _ = map.remove(2);
......@@ -243,6 +253,7 @@ test "basicHashMapTest" {
243253fn hash_i32(x: i32) -> u32 {
244254 *@ptrcast(&u32, &x)
245255}
256
246257fn eql_i32(a: i32, b: i32) -> bool {
247258 a == b
248259}
std/mem.zig+4
......@@ -208,6 +208,10 @@ pub fn split(s: []const u8, c: u8) -> SplitIterator {
208208 }
209209}
210210
211pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) -> bool {
212 return if (needle.len > haystack.len) false else eql(T, haystack[0...needle.len], needle);
213}
214
211215const SplitIterator = struct {
212216 s: []const u8,
213217 c: u8,
std/special/build_runner.zig+89-9
......@@ -1,26 +1,106 @@
11const root = @import("@build");
22const std = @import("std");
33const io = std.io;
4const fmt = std.fmt;
45const os = std.os;
56const Builder = std.build.Builder;
67const mem = std.mem;
8const List = std.list.List;
79
810error InvalidArgs;
911
1012pub fn main() -> %void {
11 if (os.args.count() < 2) {
12 %%io.stderr.printf("Expected first argument to be path to zig compiler\n");
13 return error.InvalidArgs;
14 }
15 const zig_exe = os.args.at(1);
16 const leftover_arg_index = 2;
17
1813 // TODO use a more general purpose allocator here
1914 var inc_allocator = %%mem.IncrementingAllocator.init(10 * 1024 * 1024);
2015 defer inc_allocator.deinit();
2116
22 var builder = Builder.init(zig_exe, &inc_allocator.allocator);
17 const allocator = &inc_allocator.allocator;
18
19 var builder = Builder.init(allocator);
2320 defer builder.deinit();
21
22 var maybe_zig_exe: ?[]const u8 = null;
23 var targets = List([]const u8).init(allocator);
24
25 var arg_i: usize = 1;
26 while (arg_i < os.args.count(); arg_i += 1) {
27 const arg = os.args.at(arg_i);
28 if (mem.startsWith(u8, arg, "-O")) {
29 const option_contents = arg[2...];
30 if (option_contents.len == 0) {
31 %%io.stderr.printf("Expected option name after '-O'\n\n");
32 return usage(&builder, maybe_zig_exe, false, &io.stderr);
33 }
34 if (const name_end ?= mem.indexOfScalar(u8, option_contents, '=')) {
35 const option_name = option_contents[0...name_end];
36 const option_value = option_contents[name_end...];
37 if (builder.addUserInputOption(option_name, option_value))
38 return usage(&builder, maybe_zig_exe, false, &io.stderr);
39 } else {
40 if (builder.addUserInputFlag(option_contents))
41 return usage(&builder, maybe_zig_exe, false, &io.stderr);
42 }
43 } else if (mem.startsWith(u8, arg, "-")) {
44 if (mem.eql(u8, arg, "--verbose")) {
45 builder.verbose = true;
46 } else if (mem.eql(u8, arg, "--help")) {
47 return usage(&builder, maybe_zig_exe, false, &io.stdout);
48 } else {
49 %%io.stderr.printf("Unrecognized argument: {}\n\n", arg);
50 return usage(&builder, maybe_zig_exe, false, &io.stderr);
51 }
52 } else if (maybe_zig_exe == null) {
53 maybe_zig_exe = arg;
54 } else {
55 %%targets.append(arg);
56 }
57 }
58
59 const zig_exe = maybe_zig_exe ?? return usage(&builder, null, false, &io.stderr);
60
2461 root.build(&builder);
25 %return builder.make(leftover_arg_index);
62
63 if (builder.validateUserInputDidItFail())
64 return usage(&builder, maybe_zig_exe, true, &io.stderr);
65
66 %return builder.make(zig_exe, targets.toSliceConst());
67}
68
69fn usage(builder: &Builder, maybe_zig_exe: ?[]const u8, already_ran_build: bool, out_stream: &io.OutStream) -> %void {
70 const zig_exe = maybe_zig_exe ?? {
71 %%out_stream.printf("Expected first argument to be path to zig compiler\n");
72 return error.InvalidArgs;
73 };
74
75 // run the build script to collect the options
76 if (!already_ran_build) {
77 root.build(builder);
78 }
79
80 %%out_stream.printf(
81 \\Usage: {} build [options]
82 \\
83 \\General Options:
84 \\ --help Print this help and exit.
85 \\ --verbose Print commands before executing them.
86 \\ --debug-build-verbose Print verbose debugging information for the build system itself.
87 \\
88 \\Project-Specific Options:
89 \\
90 , zig_exe);
91
92 if (builder.available_options_list.len == 0) {
93 %%out_stream.printf(" (none)\n");
94 } else {
95 const allocator = builder.allocator;
96 for (builder.available_options_list.toSliceConst()) |option| {
97 const name = %%fmt.allocPrint(allocator,
98 " -O{}=({})", option.name, Builder.typeIdName(option.type_id));
99 defer allocator.free(name);
100 %%out_stream.printf("{s24} {}\n", name, option.description);
101 }
102 }
103
104 if (out_stream == &io.stderr)
105 return error.InvalidArgs;
26106}
test/cases/switch.zig+12
......@@ -138,3 +138,15 @@ fn returnsFalse() -> bool {
138138test "switchOnConstEnumWithVar" {
139139 assert(!returnsFalse());
140140}
141
142test "switch on type" {
143 assert(trueIfBoolFalseOtherwise(bool));
144 assert(!trueIfBoolFalseOtherwise(i32));
145}
146
147fn trueIfBoolFalseOtherwise(comptime T: type) -> bool {
148 switch (T) {
149 bool => true,
150 else => false,
151 }
152}