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...@@ -57,6 +57,7 @@ static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *ins
57static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, TypeTableEntry *expected_type);57static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, TypeTableEntry *expected_type);
5858
59ConstExprValue *const_ptr_pointee(ConstExprValue *const_val) {59ConstExprValue *const_ptr_pointee(ConstExprValue *const_val) {
60 assert(const_val->type->id == TypeTableEntryIdPointer);
60 assert(const_val->special == ConstValSpecialStatic);61 assert(const_val->special == ConstValSpecialStatic);
61 switch (const_val->data.x_ptr.special) {62 switch (const_val->data.x_ptr.special) {
62 case ConstPtrSpecialInvalid:63 case ConstPtrSpecialInvalid:
...@@ -10350,10 +10351,21 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,...@@ -10350,10 +10351,21 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,
10350 if (type_is_invalid(target_value_ptr->value.type))10351 if (type_is_invalid(target_value_ptr->value.type))
10351 return ira->codegen->builtin_types.entry_invalid;10352 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
10353 assert(target_value_ptr->value.type->id == TypeTableEntryIdPointer);10364 assert(target_value_ptr->value.type->id == TypeTableEntryIdPointer);
10365
10354 TypeTableEntry *target_type = target_value_ptr->value.type->data.pointer.child_type;10366 TypeTableEntry *target_type = target_value_ptr->value.type->data.pointer.child_type;
10355 ConstExprValue *pointee_val = nullptr;10367 ConstExprValue *pointee_val = nullptr;
10356 if (target_value_ptr->value.special != ConstValSpecialRuntime) {10368 if (instr_is_comptime(target_value_ptr)) {
10357 pointee_val = const_ptr_pointee(&target_value_ptr->value);10369 pointee_val = const_ptr_pointee(&target_value_ptr->value);
10358 if (pointee_val->special == ConstValSpecialRuntime)10370 if (pointee_val->special == ConstValSpecialRuntime)
10359 pointee_val = nullptr;10371 pointee_val = nullptr;
src/main.cpp+1-2
...@@ -167,9 +167,8 @@ int main(int argc, char **argv) {...@@ -167,9 +167,8 @@ int main(int argc, char **argv) {
167 ZigList<const char *> args = {0};167 ZigList<const char *> args = {0};
168 args.append(zig_exe_path);168 args.append(zig_exe_path);
169 for (int i = 2; i < argc; i += 1) {169 for (int i = 2; i < argc; i += 1) {
170 if (strcmp(argv[i], "--verbose") == 0) {170 if (strcmp(argv[i], "--debug-build-verbose") == 0) {
171 verbose = true;171 verbose = true;
172 args.append(argv[i]);
173 } else {172 } else {
174 args.append(argv[i]);173 args.append(argv[i]);
175 }174 }
std/buf_map.zig+3-4
...@@ -11,14 +11,13 @@ pub const BufMap = struct {...@@ -11,14 +11,13 @@ pub const BufMap = struct {
1111
12 pub fn init(allocator: &Allocator) -> BufMap {12 pub fn init(allocator: &Allocator) -> BufMap {
13 var self = BufMap {13 var self = BufMap {
14 .hash_map = undefined,14 .hash_map = BufMapHashMap.init(allocator),
15 };15 };
16 self.hash_map.init(allocator);
17 return self;16 return self;
18 }17 }
1918
20 pub fn deinit(self: &BufMap) {19 pub fn deinit(self: &BufMap) {
21 var it = self.hash_map.entryIterator();20 var it = self.hash_map.iterator();
22 while (true) {21 while (true) {
23 const entry = it.next() ?? break; 22 const entry = it.next() ?? break;
24 self.free(entry.key);23 self.free(entry.key);
...@@ -54,7 +53,7 @@ pub const BufMap = struct {...@@ -54,7 +53,7 @@ pub const BufMap = struct {
54 }53 }
5554
56 pub fn iterator(self: &const BufMap) -> BufMapHashMap.Iterator {55 pub fn iterator(self: &const BufMap) -> BufMapHashMap.Iterator {
57 return self.hash_map.entryIterator();56 return self.hash_map.iterator();
58 }57 }
5958
60 fn free(self: &BufMap, value: []const u8) {59 fn free(self: &BufMap, value: []const u8) {
std/buf_set.zig+3-4
...@@ -9,14 +9,13 @@ pub const BufSet = struct {...@@ -9,14 +9,13 @@ pub const BufSet = struct {
99
10 pub fn init(allocator: &Allocator) -> BufSet {10 pub fn init(allocator: &Allocator) -> BufSet {
11 var self = BufSet {11 var self = BufSet {
12 .hash_map = undefined,12 .hash_map = BufSetHashMap.init(allocator),
13 };13 };
14 self.hash_map.init(allocator);
15 return self;14 return self;
16 }15 }
1716
18 pub fn deinit(self: &BufSet) {17 pub fn deinit(self: &BufSet) {
19 var it = self.hash_map.entryIterator();18 var it = self.hash_map.iterator();
20 while (true) {19 while (true) {
21 const entry = it.next() ?? break; 20 const entry = it.next() ?? break;
22 self.free(entry.key);21 self.free(entry.key);
...@@ -43,7 +42,7 @@ pub const BufSet = struct {...@@ -43,7 +42,7 @@ pub const BufSet = struct {
43 }42 }
4443
45 pub fn iterator(self: &const BufSet) -> BufSetHashMap.Iterator {44 pub fn iterator(self: &const BufSet) -> BufSetHashMap.Iterator {
46 return self.hash_map.entryIterator();45 return self.hash_map.iterator();
47 }46 }
4847
49 fn free(self: &BufSet, value: []const u8) {48 fn free(self: &BufSet, value: []const u8) {
std/build.zig+212-27
...@@ -2,6 +2,7 @@ const io = @import("io.zig");...@@ -2,6 +2,7 @@ const io = @import("io.zig");
2const mem = @import("mem.zig");2const mem = @import("mem.zig");
3const debug = @import("debug.zig");3const debug = @import("debug.zig");
4const List = @import("list.zig").List;4const List = @import("list.zig").List;
5const HashMap = @import("hash_map.zig").HashMap;
5const Allocator = @import("mem.zig").Allocator;6const Allocator = @import("mem.zig").Allocator;
6const os = @import("os/index.zig");7const os = @import("os/index.zig");
7const StdIo = os.ChildProcess.StdIo;8const StdIo = os.ChildProcess.StdIo;
...@@ -12,21 +13,58 @@ error ExtraArg;...@@ -12,21 +13,58 @@ error ExtraArg;
12error UncleanExit;13error UncleanExit;
1314
14pub const Builder = struct {15pub const Builder = struct {
15 zig_exe: []const u8,
16 allocator: &Allocator,16 allocator: &Allocator,
17 exe_list: List(&Exe),17 exe_list: List(&Exe),
18 lib_paths: List([]const u8),18 lib_paths: List([]const u8),
19 include_paths: List([]const u8),19 include_paths: List([]const u8),
20 rpaths: List([]const u8),20 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 {
23 var self = Builder {57 var self = Builder {
24 .zig_exe = zig_exe,58 .verbose = false,
59 .invalid_user_input = false,
25 .allocator = allocator,60 .allocator = allocator,
26 .exe_list = List(&Exe).init(allocator),61 .exe_list = List(&Exe).init(allocator),
27 .lib_paths = List([]const u8).init(allocator),62 .lib_paths = List([]const u8).init(allocator),
28 .include_paths = List([]const u8).init(allocator),63 .include_paths = List([]const u8).init(allocator),
29 .rpaths = List([]const u8).init(allocator),64 .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),
30 };68 };
31 self.processNixOSEnvVars();69 self.processNixOSEnvVars();
32 return self;70 return self;
...@@ -46,6 +84,8 @@ pub const Builder = struct {...@@ -46,6 +84,8 @@ pub const Builder = struct {
46 pub fn addExeErr(self: &Builder, root_src: []const u8, name: []const u8) -> %&Exe {84 pub fn addExeErr(self: &Builder, root_src: []const u8, name: []const u8) -> %&Exe {
47 const exe = %return self.allocator.create(Exe);85 const exe = %return self.allocator.create(Exe);
48 *exe = Exe {86 *exe = Exe {
87 .verbose = false,
88 .release = false,
49 .root_src = root_src,89 .root_src = root_src,
50 .name = name,90 .name = name,
51 .target = Target.Native,91 .target = Target.Native,
...@@ -68,20 +108,13 @@ pub const Builder = struct {...@@ -68,20 +108,13 @@ pub const Builder = struct {
68 %%self.lib_paths.append(path);108 %%self.lib_paths.append(path);
69 }109 }
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
72 var env_map = %return os.getEnvMap(self.allocator);116 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 }
85 for (self.exe_list.toSlice()) |exe| {118 for (self.exe_list.toSlice()) |exe| {
86 var zig_args = List([]const u8).init(self.allocator);119 var zig_args = List([]const u8).init(self.allocator);
87 defer zig_args.deinit();120 defer zig_args.deinit();
...@@ -89,10 +122,14 @@ pub const Builder = struct {...@@ -89,10 +122,14 @@ pub const Builder = struct {
89 %return zig_args.append("build_exe"[0...]); // TODO issue #296122 %return zig_args.append("build_exe"[0...]); // TODO issue #296
90 %return zig_args.append(exe.root_src);123 %return zig_args.append(exe.root_src);
91124
92 if (verbose) {125 if (exe.verbose) {
93 %return zig_args.append("--verbose"[0...]); // TODO issue #296126 %return zig_args.append("--verbose"[0...]); // TODO issue #296
94 }127 }
95128
129 if (exe.release) {
130 %return zig_args.append("--release"[0...]); // TODO issue #296
131 }
132
96 %return zig_args.append("--name"[0...]); // TODO issue #296133 %return zig_args.append("--name"[0...]); // TODO issue #296
97 %return zig_args.append(exe.name);134 %return zig_args.append(exe.name);
98135
...@@ -149,22 +186,21 @@ pub const Builder = struct {...@@ -149,22 +186,21 @@ pub const Builder = struct {
149 %return zig_args.append(lib_path);186 %return zig_args.append(lib_path);
150 }187 }
151188
189 if (self.verbose) {
190 printInvocation(zig_exe, zig_args);
191 }
152 // TODO issue #301192 // 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,
154 StdIo.Ignore, StdIo.Inherit, StdIo.Inherit, self.allocator)194 StdIo.Ignore, StdIo.Inherit, StdIo.Inherit, self.allocator)
155 %% |err| debug.panic("Unable to spawn zig compiler: {}\n", @errorName(err));195 %% |err| debug.panic("Unable to spawn zig compiler: {}\n", @errorName(err));
156 const term = %%child.wait();196 const term = %%child.wait();
157 const exe_result = switch (term) {197 switch (term) {
158 Term.Clean => |code| {198 Term.Clean => |code| {
159 if (code != 0) {199 if (code != 0) {
160 %%io.stderr.printf("\nCompile failed with code {}. To reproduce:\n", code);
161 printInvocation(self.zig_exe, zig_args);
162 return error.UncleanExit;200 return error.UncleanExit;
163 }201 }
164 },202 },
165 else => {203 else => {
166 %%io.stderr.printf("\nCompile crashed. To reproduce:\n");
167 printInvocation(self.zig_exe, zig_args);
168 return error.UncleanExit;204 return error.UncleanExit;
169 },205 },
170 };206 };
...@@ -208,6 +244,144 @@ pub const Builder = struct {...@@ -208,6 +244,144 @@ pub const Builder = struct {
208 }244 }
209 }245 }
210 }246 }
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
211};385};
212386
213const CrossTarget = struct {387const CrossTarget = struct {
...@@ -233,12 +407,14 @@ const Exe = struct {...@@ -233,12 +407,14 @@ const Exe = struct {
233 target: Target,407 target: Target,
234 linker_script: LinkerScript,408 linker_script: LinkerScript,
235 link_libs: BufSet,409 link_libs: BufSet,
410 verbose: bool,
411 release: bool,
236412
237 fn deinit(self: &Exe) {413 pub fn deinit(self: &Exe) {
238 self.link_libs.deinit();414 self.link_libs.deinit();
239 }415 }
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) {
242 self.target = Target.Cross {418 self.target = Target.Cross {
243 CrossTarget {419 CrossTarget {
244 .arch = target_arch,420 .arch = target_arch,
...@@ -250,17 +426,25 @@ const Exe = struct {...@@ -250,17 +426,25 @@ const Exe = struct {
250426
251 /// Exe keeps a reference to script for its lifetime or until this function427 /// Exe keeps a reference to script for its lifetime or until this function
252 /// is called again.428 /// is called again.
253 fn setLinkerScriptContents(self: &Exe, script: []const u8) {429 pub fn setLinkerScriptContents(self: &Exe, script: []const u8) {
254 self.linker_script = LinkerScript.Embed { script };430 self.linker_script = LinkerScript.Embed { script };
255 }431 }
256432
257 fn setLinkerScriptPath(self: &Exe, path: []const u8) {433 pub fn setLinkerScriptPath(self: &Exe, path: []const u8) {
258 self.linker_script = LinkerScript.Path { path };434 self.linker_script = LinkerScript.Path { path };
259 }435 }
260436
261 fn linkLibrary(self: &Exe, name: []const u8) {437 pub fn linkLibrary(self: &Exe, name: []const u8) {
262 %%self.link_libs.put(name);438 %%self.link_libs.put(name);
263 }439 }
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 }
264};448};
265449
266fn handleErr(err: error) -> noreturn {450fn handleErr(err: error) -> noreturn {
...@@ -401,3 +585,4 @@ fn targetEnvironName(target_environ: Environ) -> []const u8 {...@@ -401,3 +585,4 @@ fn targetEnvironName(target_environ: Environ) -> []const u8 {
401 Environ.coreclr => ([]const u8)("coreclr"),585 Environ.coreclr => ([]const u8)("coreclr"),
402 };586 };
403}587}
588
std/fmt.zig+72
...@@ -13,6 +13,8 @@ const State = enum { // TODO put inside format function and make sure the name a...@@ -13,6 +13,8 @@ const State = enum { // TODO put inside format function and make sure the name a
13 Integer,13 Integer,
14 IntegerWidth,14 IntegerWidth,
15 Character,15 Character,
16 Buf,
17 BufWidth,
16};18};
1719
18/// Renders fmt string with args, calling output with slices of bytes.20/// 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,...@@ -82,8 +84,21 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)->bool,
82 'c' => {84 'c' => {
83 state = State.Character;85 state = State.Character;
84 },86 },
87 's' => {
88 state = State.Buf;
89 },
85 else => @compileError("Unknown format character: " ++ []u8{c}),90 else => @compileError("Unknown format character: " ++ []u8{c}),
86 },91 },
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 },
87 State.CloseBrace => switch (c) {102 State.CloseBrace => switch (c) {
88 '}' => {103 '}' => {
89 state = State.Start;104 state = State.Start;
...@@ -117,6 +132,18 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)->bool,...@@ -117,6 +132,18 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)->bool,
117 '0' ... '9' => {},132 '0' ... '9' => {},
118 else => @compileError("Unexpected character in format string: " ++ []u8{c}),133 else => @compileError("Unexpected character in format string: " ++ []u8{c}),
119 },134 },
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 },
120 State.Character => switch (c) {147 State.Character => switch (c) {
121 '}' => {148 '}' => {
122 if (!formatAsciiChar(args[next_arg], context, output))149 if (!formatAsciiChar(args[next_arg], context, output))
...@@ -165,6 +192,23 @@ pub fn formatAsciiChar(c: u8, context: var, output: fn(@typeOf(context), []const...@@ -165,6 +192,23 @@ pub fn formatAsciiChar(c: u8, context: var, output: fn(@typeOf(context), []const
165 return output(context, (&c)[0...1]);192 return output(context, (&c)[0...1]);
166}193}
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
168pub fn formatInt(value: var, base: u8, uppercase: bool, width: usize,212pub fn formatInt(value: var, base: u8, uppercase: bool, width: usize,
169 context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool213 context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool
170{214{
...@@ -291,6 +335,34 @@ fn digitToChar(digit: u8, uppercase: bool) -> u8 {...@@ -291,6 +335,34 @@ fn digitToChar(digit: u8, uppercase: bool) -> u8 {
291 };335 };
292}336}
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
294test "testBufPrintInt" {366test "testBufPrintInt" {
295 var buffer: [max_int_digits]u8 = undefined;367 var buffer: [max_int_digits]u8 = undefined;
296 const buf = buffer[0...];368 const buf = buffer[0...];
std/hash_map.zig+33-22
...@@ -54,13 +54,15 @@ pub fn HashMap(comptime K: type, comptime V: type,...@@ -54,13 +54,15 @@ pub fn HashMap(comptime K: type, comptime V: type,
54 }54 }
55 };55 };
5656
57 pub fn init(hm: &Self, allocator: &Allocator) {57 pub fn init(allocator: &Allocator) -> Self {
58 hm.entries = []Entry{};58 Self {
59 hm.allocator = allocator;59 .entries = []Entry{},
60 hm.size = 0;60 .allocator = allocator,
61 hm.max_distance_from_start_index = 0;61 .size = 0,
62 // it doesn't actually matter what we set this to since we use wrapping integer arithmetic62 .max_distance_from_start_index = 0,
63 hm.modification_count = undefined;63 // it doesn't actually matter what we set this to since we use wrapping integer arithmetic
64 .modification_count = undefined,
65 }
64 }66 }
6567
66 pub fn deinit(hm: &Self) {68 pub fn deinit(hm: &Self) {
...@@ -76,7 +78,8 @@ pub fn HashMap(comptime K: type, comptime V: type,...@@ -76,7 +78,8 @@ pub fn HashMap(comptime K: type, comptime V: type,
76 hm.incrementModificationCount();78 hm.incrementModificationCount();
77 }79 }
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 {
80 if (hm.entries.len == 0) {83 if (hm.entries.len == 0) {
81 %return hm.initCapacity(16);84 %return hm.initCapacity(16);
82 }85 }
...@@ -89,13 +92,13 @@ pub fn HashMap(comptime K: type, comptime V: type,...@@ -89,13 +92,13 @@ pub fn HashMap(comptime K: type, comptime V: type,
89 // dump all of the old elements into the new table92 // dump all of the old elements into the new table
90 for (old_entries) |*old_entry| {93 for (old_entries) |*old_entry| {
91 if (old_entry.used) {94 if (old_entry.used) {
92 hm.internalPut(old_entry.key, old_entry.value);95 _ = hm.internalPut(old_entry.key, old_entry.value);
93 }96 }
94 }97 }
95 hm.allocator.free(old_entries);98 hm.allocator.free(old_entries);
96 }99 }
97100
98 hm.internalPut(key, value);101 return hm.internalPut(key, value);
99 }102 }
100103
101 pub fn get(hm: &Self, key: K) -> ?&Entry {104 pub fn get(hm: &Self, key: K) -> ?&Entry {
...@@ -134,7 +137,7 @@ pub fn HashMap(comptime K: type, comptime V: type,...@@ -134,7 +137,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
134 return null;137 return null;
135 }138 }
136139
137 pub fn entryIterator(hm: &const Self) -> Iterator {140 pub fn iterator(hm: &const Self) -> Iterator {
138 return Iterator {141 return Iterator {
139 .hm = hm,142 .hm = hm,
140 .count = 0,143 .count = 0,
...@@ -158,9 +161,10 @@ pub fn HashMap(comptime K: type, comptime V: type,...@@ -158,9 +161,10 @@ pub fn HashMap(comptime K: type, comptime V: type,
158 }161 }
159 }162 }
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 {
162 var key = orig_key;166 var key = orig_key;
163 var value = orig_value;167 var value = *orig_value;
164 const start_index = hm.keyToIndex(key);168 const start_index = hm.keyToIndex(key);
165 var roll_over: usize = 0;169 var roll_over: usize = 0;
166 var distance_from_start_index: usize = 0;170 var distance_from_start_index: usize = 0;
...@@ -187,7 +191,10 @@ pub fn HashMap(comptime K: type, comptime V: type,...@@ -187,7 +191,10 @@ pub fn HashMap(comptime K: type, comptime V: type,
187 continue;191 continue;
188 }192 }
189193
190 if (!entry.used) {194 var result: ?V = null;
195 if (entry.used) {
196 result = entry.value;
197 } else {
191 // adding an entry. otherwise overwriting old value with198 // adding an entry. otherwise overwriting old value with
192 // same key199 // same key
193 hm.size += 1;200 hm.size += 1;
...@@ -200,7 +207,7 @@ pub fn HashMap(comptime K: type, comptime V: type,...@@ -200,7 +207,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
200 .key = key,207 .key = key,
201 .value = value,208 .value = value,
202 };209 };
203 return;210 return result;
204 }211 }
205 unreachable // put into a full map212 unreachable // put into a full map
206 }213 }
...@@ -224,15 +231,18 @@ pub fn HashMap(comptime K: type, comptime V: type,...@@ -224,15 +231,18 @@ pub fn HashMap(comptime K: type, comptime V: type,
224}231}
225232
226test "basicHashMapTest" {233test "basicHashMapTest" {
227 var map: HashMap(i32, i32, hash_i32, eql_i32) = undefined;234 var map = HashMap(i32, i32, hash_i32, eql_i32).init(&debug.global_allocator);
228 map.init(&debug.global_allocator);
229 defer map.deinit();235 defer map.deinit();
230236
231 %%map.put(1, 11);237 // TODO issue #311
232 %%map.put(2, 22);238 assert(%%map.put(1, i32(11)) == null);
233 %%map.put(3, 33);239 assert(%%map.put(2, i32(22)) == null);
234 %%map.put(4, 44);240 assert(%%map.put(3, i32(33)) == null);
235 %%map.put(5, 55);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
237 assert((??map.get(2)).value == 22);247 assert((??map.get(2)).value == 22);
238 _ = map.remove(2);248 _ = map.remove(2);
...@@ -243,6 +253,7 @@ test "basicHashMapTest" {...@@ -243,6 +253,7 @@ test "basicHashMapTest" {
243fn hash_i32(x: i32) -> u32 {253fn hash_i32(x: i32) -> u32 {
244 *@ptrcast(&u32, &x)254 *@ptrcast(&u32, &x)
245}255}
256
246fn eql_i32(a: i32, b: i32) -> bool {257fn eql_i32(a: i32, b: i32) -> bool {
247 a == b258 a == b
248}259}
std/mem.zig+4
...@@ -208,6 +208,10 @@ pub fn split(s: []const u8, c: u8) -> SplitIterator {...@@ -208,6 +208,10 @@ pub fn split(s: []const u8, c: u8) -> SplitIterator {
208 }208 }
209}209}
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
211const SplitIterator = struct {215const SplitIterator = struct {
212 s: []const u8,216 s: []const u8,
213 c: u8,217 c: u8,
std/special/build_runner.zig+89-9
...@@ -1,26 +1,106 @@...@@ -1,26 +1,106 @@
1const root = @import("@build");1const root = @import("@build");
2const std = @import("std");2const std = @import("std");
3const io = std.io;3const io = std.io;
4const fmt = std.fmt;
4const os = std.os;5const os = std.os;
5const Builder = std.build.Builder;6const Builder = std.build.Builder;
6const mem = std.mem;7const mem = std.mem;
8const List = std.list.List;
79
8error InvalidArgs;10error InvalidArgs;
911
10pub fn main() -> %void {12pub 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
18 // TODO use a more general purpose allocator here13 // TODO use a more general purpose allocator here
19 var inc_allocator = %%mem.IncrementingAllocator.init(10 * 1024 * 1024);14 var inc_allocator = %%mem.IncrementingAllocator.init(10 * 1024 * 1024);
20 defer inc_allocator.deinit();15 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);
23 defer builder.deinit();20 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
24 root.build(&builder);61 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;
26}106}
test/cases/switch.zig+12
...@@ -138,3 +138,15 @@ fn returnsFalse() -> bool {...@@ -138,3 +138,15 @@ fn returnsFalse() -> bool {
138test "switchOnConstEnumWithVar" {138test "switchOnConstEnumWithVar" {
139 assert(!returnsFalse());139 assert(!returnsFalse());
140}140}
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}