| author | |
| committer | |
| log | 6fbe1632d0c958b9abbc9f38a7e497ef69543bf1 |
| tree | c5ee08ab23927e79a2827beebeb5e879e6596f7c |
| parent | d15bcdce691c1f42f70a5e9943817eb5ba974893 |
* 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.startsWith10 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 | 57 | static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, TypeTableEntry *expected_type); |
| 58 | 58 | |
| 59 | 59 | ConstExprValue *const_ptr_pointee(ConstExprValue *const_val) { |
| 60 | assert(const_val->type->id == TypeTableEntryIdPointer); | |
| 60 | 61 | assert(const_val->special == ConstValSpecialStatic); |
| 61 | 62 | switch (const_val->data.x_ptr.special) { |
| 62 | 63 | case ConstPtrSpecialInvalid: |
| ... | ... | @@ -10350,10 +10351,21 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira, |
| 10350 | 10351 | if (type_is_invalid(target_value_ptr->value.type)) |
| 10351 | 10352 | return ira->codegen->builtin_types.entry_invalid; |
| 10352 | 10353 | |
| 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 | 10364 | assert(target_value_ptr->value.type->id == TypeTableEntryIdPointer); |
| 10365 | ||
| 10354 | 10366 | TypeTableEntry *target_type = target_value_ptr->value.type->data.pointer.child_type; |
| 10355 | 10367 | ConstExprValue *pointee_val = nullptr; |
| 10356 | if (target_value_ptr->value.special != ConstValSpecialRuntime) { | |
| 10368 | if (instr_is_comptime(target_value_ptr)) { | |
| 10357 | 10369 | pointee_val = const_ptr_pointee(&target_value_ptr->value); |
| 10358 | 10370 | if (pointee_val->special == ConstValSpecialRuntime) |
| 10359 | 10371 | pointee_val = nullptr; |
src/main.cpp+1-2| ... | ... | @@ -167,9 +167,8 @@ int main(int argc, char **argv) { |
| 167 | 167 | ZigList<const char *> args = {0}; |
| 168 | 168 | args.append(zig_exe_path); |
| 169 | 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 | 171 | verbose = true; |
| 172 | args.append(argv[i]); | |
| 173 | 172 | } else { |
| 174 | 173 | args.append(argv[i]); |
| 175 | 174 | } |
std/buf_map.zig+3-4| ... | ... | @@ -11,14 +11,13 @@ pub const BufMap = struct { |
| 11 | 11 | |
| 12 | 12 | pub fn init(allocator: &Allocator) -> BufMap { |
| 13 | 13 | var self = BufMap { |
| 14 | .hash_map = undefined, | |
| 14 | .hash_map = BufMapHashMap.init(allocator), | |
| 15 | 15 | }; |
| 16 | self.hash_map.init(allocator); | |
| 17 | 16 | return self; |
| 18 | 17 | } |
| 19 | 18 | |
| 20 | 19 | pub fn deinit(self: &BufMap) { |
| 21 | var it = self.hash_map.entryIterator(); | |
| 20 | var it = self.hash_map.iterator(); | |
| 22 | 21 | while (true) { |
| 23 | 22 | const entry = it.next() ?? break; |
| 24 | 23 | self.free(entry.key); |
| ... | ... | @@ -54,7 +53,7 @@ pub const BufMap = struct { |
| 54 | 53 | } |
| 55 | 54 | |
| 56 | 55 | pub fn iterator(self: &const BufMap) -> BufMapHashMap.Iterator { |
| 57 | return self.hash_map.entryIterator(); | |
| 56 | return self.hash_map.iterator(); | |
| 58 | 57 | } |
| 59 | 58 | |
| 60 | 59 | fn free(self: &BufMap, value: []const u8) { |
std/buf_set.zig+3-4| ... | ... | @@ -9,14 +9,13 @@ pub const BufSet = struct { |
| 9 | 9 | |
| 10 | 10 | pub fn init(allocator: &Allocator) -> BufSet { |
| 11 | 11 | var self = BufSet { |
| 12 | .hash_map = undefined, | |
| 12 | .hash_map = BufSetHashMap.init(allocator), | |
| 13 | 13 | }; |
| 14 | self.hash_map.init(allocator); | |
| 15 | 14 | return self; |
| 16 | 15 | } |
| 17 | 16 | |
| 18 | 17 | pub fn deinit(self: &BufSet) { |
| 19 | var it = self.hash_map.entryIterator(); | |
| 18 | var it = self.hash_map.iterator(); | |
| 20 | 19 | while (true) { |
| 21 | 20 | const entry = it.next() ?? break; |
| 22 | 21 | self.free(entry.key); |
| ... | ... | @@ -43,7 +42,7 @@ pub const BufSet = struct { |
| 43 | 42 | } |
| 44 | 43 | |
| 45 | 44 | pub fn iterator(self: &const BufSet) -> BufSetHashMap.Iterator { |
| 46 | return self.hash_map.entryIterator(); | |
| 45 | return self.hash_map.iterator(); | |
| 47 | 46 | } |
| 48 | 47 | |
| 49 | 48 | fn free(self: &BufSet, value: []const u8) { |
std/build.zig+212-27| ... | ... | @@ -2,6 +2,7 @@ const io = @import("io.zig"); |
| 2 | 2 | const mem = @import("mem.zig"); |
| 3 | 3 | const debug = @import("debug.zig"); |
| 4 | 4 | const List = @import("list.zig").List; |
| 5 | const HashMap = @import("hash_map.zig").HashMap; | |
| 5 | 6 | const Allocator = @import("mem.zig").Allocator; |
| 6 | 7 | const os = @import("os/index.zig"); |
| 7 | 8 | const StdIo = os.ChildProcess.StdIo; |
| ... | ... | @@ -12,21 +13,58 @@ error ExtraArg; |
| 12 | 13 | error UncleanExit; |
| 13 | 14 | |
| 14 | 15 | pub const Builder = struct { |
| 15 | zig_exe: []const u8, | |
| 16 | 16 | allocator: &Allocator, |
| 17 | 17 | exe_list: List(&Exe), |
| 18 | 18 | lib_paths: List([]const u8), |
| 19 | 19 | include_paths: List([]const u8), |
| 20 | 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 | }; | |
| 21 | 55 | |
| 22 | pub fn init(zig_exe: []const u8, allocator: &Allocator) -> Builder { | |
| 56 | pub fn init(allocator: &Allocator) -> Builder { | |
| 23 | 57 | var self = Builder { |
| 24 | .zig_exe = zig_exe, | |
| 58 | .verbose = false, | |
| 59 | .invalid_user_input = false, | |
| 25 | 60 | .allocator = allocator, |
| 26 | 61 | .exe_list = List(&Exe).init(allocator), |
| 27 | 62 | .lib_paths = List([]const u8).init(allocator), |
| 28 | 63 | .include_paths = List([]const u8).init(allocator), |
| 29 | 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 | 69 | self.processNixOSEnvVars(); |
| 32 | 70 | return self; |
| ... | ... | @@ -46,6 +84,8 @@ pub const Builder = struct { |
| 46 | 84 | pub fn addExeErr(self: &Builder, root_src: []const u8, name: []const u8) -> %&Exe { |
| 47 | 85 | const exe = %return self.allocator.create(Exe); |
| 48 | 86 | *exe = Exe { |
| 87 | .verbose = false, | |
| 88 | .release = false, | |
| 49 | 89 | .root_src = root_src, |
| 50 | 90 | .name = name, |
| 51 | 91 | .target = Target.Native, |
| ... | ... | @@ -68,20 +108,13 @@ pub const Builder = struct { |
| 68 | 108 | %%self.lib_paths.append(path); |
| 69 | 109 | } |
| 70 | 110 | |
| 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 | 116 | var env_map = %return os.getEnvMap(self.allocator); |
| 73 | 117 | |
| 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 | 118 | for (self.exe_list.toSlice()) |exe| { |
| 86 | 119 | var zig_args = List([]const u8).init(self.allocator); |
| 87 | 120 | defer zig_args.deinit(); |
| ... | ... | @@ -89,10 +122,14 @@ pub const Builder = struct { |
| 89 | 122 | %return zig_args.append("build_exe"[0...]); // TODO issue #296 |
| 90 | 123 | %return zig_args.append(exe.root_src); |
| 91 | 124 | |
| 92 | if (verbose) { | |
| 125 | if (exe.verbose) { | |
| 93 | 126 | %return zig_args.append("--verbose"[0...]); // TODO issue #296 |
| 94 | 127 | } |
| 95 | 128 | |
| 129 | if (exe.release) { | |
| 130 | %return zig_args.append("--release"[0...]); // TODO issue #296 | |
| 131 | } | |
| 132 | ||
| 96 | 133 | %return zig_args.append("--name"[0...]); // TODO issue #296 |
| 97 | 134 | %return zig_args.append(exe.name); |
| 98 | 135 | |
| ... | ... | @@ -149,22 +186,21 @@ pub const Builder = struct { |
| 149 | 186 | %return zig_args.append(lib_path); |
| 150 | 187 | } |
| 151 | 188 | |
| 189 | if (self.verbose) { | |
| 190 | printInvocation(zig_exe, zig_args); | |
| 191 | } | |
| 152 | 192 | // 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 | 194 | StdIo.Ignore, StdIo.Inherit, StdIo.Inherit, self.allocator) |
| 155 | 195 | %% |err| debug.panic("Unable to spawn zig compiler: {}\n", @errorName(err)); |
| 156 | 196 | const term = %%child.wait(); |
| 157 | const exe_result = switch (term) { | |
| 197 | switch (term) { | |
| 158 | 198 | Term.Clean => |code| { |
| 159 | 199 | if (code != 0) { |
| 160 | %%io.stderr.printf("\nCompile failed with code {}. To reproduce:\n", code); | |
| 161 | printInvocation(self.zig_exe, zig_args); | |
| 162 | 200 | return error.UncleanExit; |
| 163 | 201 | } |
| 164 | 202 | }, |
| 165 | 203 | else => { |
| 166 | %%io.stderr.printf("\nCompile crashed. To reproduce:\n"); | |
| 167 | printInvocation(self.zig_exe, zig_args); | |
| 168 | 204 | return error.UncleanExit; |
| 169 | 205 | }, |
| 170 | 206 | }; |
| ... | ... | @@ -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 | }; |
| 212 | 386 | |
| 213 | 387 | const CrossTarget = struct { |
| ... | ... | @@ -233,12 +407,14 @@ const Exe = struct { |
| 233 | 407 | target: Target, |
| 234 | 408 | linker_script: LinkerScript, |
| 235 | 409 | link_libs: BufSet, |
| 410 | verbose: bool, | |
| 411 | release: bool, | |
| 236 | 412 | |
| 237 | fn deinit(self: &Exe) { | |
| 413 | pub fn deinit(self: &Exe) { | |
| 238 | 414 | self.link_libs.deinit(); |
| 239 | 415 | } |
| 240 | 416 | |
| 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 | 418 | self.target = Target.Cross { |
| 243 | 419 | CrossTarget { |
| 244 | 420 | .arch = target_arch, |
| ... | ... | @@ -250,17 +426,25 @@ const Exe = struct { |
| 250 | 426 | |
| 251 | 427 | /// Exe keeps a reference to script for its lifetime or until this function |
| 252 | 428 | /// is called again. |
| 253 | fn setLinkerScriptContents(self: &Exe, script: []const u8) { | |
| 429 | pub fn setLinkerScriptContents(self: &Exe, script: []const u8) { | |
| 254 | 430 | self.linker_script = LinkerScript.Embed { script }; |
| 255 | 431 | } |
| 256 | 432 | |
| 257 | fn setLinkerScriptPath(self: &Exe, path: []const u8) { | |
| 433 | pub fn setLinkerScriptPath(self: &Exe, path: []const u8) { | |
| 258 | 434 | self.linker_script = LinkerScript.Path { path }; |
| 259 | 435 | } |
| 260 | 436 | |
| 261 | fn linkLibrary(self: &Exe, name: []const u8) { | |
| 437 | pub fn linkLibrary(self: &Exe, name: []const u8) { | |
| 262 | 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 | }; |
| 265 | 449 | |
| 266 | 450 | fn handleErr(err: error) -> noreturn { |
| ... | ... | @@ -401,3 +585,4 @@ fn targetEnvironName(target_environ: Environ) -> []const u8 { |
| 401 | 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 | 13 | Integer, |
| 14 | 14 | IntegerWidth, |
| 15 | 15 | Character, |
| 16 | Buf, | |
| 17 | BufWidth, | |
| 16 | 18 | }; |
| 17 | 19 | |
| 18 | 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 | 84 | 'c' => { |
| 83 | 85 | state = State.Character; |
| 84 | 86 | }, |
| 87 | 's' => { | |
| 88 | state = State.Buf; | |
| 89 | }, | |
| 85 | 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 | 102 | State.CloseBrace => switch (c) { |
| 88 | 103 | '}' => { |
| 89 | 104 | state = State.Start; |
| ... | ... | @@ -117,6 +132,18 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)->bool, |
| 117 | 132 | '0' ... '9' => {}, |
| 118 | 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 | 147 | State.Character => switch (c) { |
| 121 | 148 | '}' => { |
| 122 | 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 | 192 | return output(context, (&c)[0...1]); |
| 166 | 193 | } |
| 167 | 194 | |
| 195 | pub 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 | ||
| 168 | 212 | pub fn formatInt(value: var, base: u8, uppercase: bool, width: usize, |
| 169 | 213 | context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool |
| 170 | 214 | { |
| ... | ... | @@ -291,6 +335,34 @@ fn digitToChar(digit: u8, uppercase: bool) -> u8 { |
| 291 | 335 | }; |
| 292 | 336 | } |
| 293 | 337 | |
| 338 | const BufPrintContext = struct { | |
| 339 | remaining: []u8, | |
| 340 | }; | |
| 341 | ||
| 342 | fn 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 | ||
| 348 | pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) { | |
| 349 | var context = BufPrintContext { .remaining = buf, }; | |
| 350 | _ = format(&context, bufPrintWrite, fmt, args); | |
| 351 | } | |
| 352 | ||
| 353 | pub 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 | ||
| 361 | fn countSize(size: &usize, bytes: []const u8) -> bool { | |
| 362 | *size += bytes.len; | |
| 363 | return true; | |
| 364 | } | |
| 365 | ||
| 294 | 366 | test "testBufPrintInt" { |
| 295 | 367 | var buffer: [max_int_digits]u8 = undefined; |
| 296 | 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 | 54 | } |
| 55 | 55 | }; |
| 56 | 56 | |
| 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 | } | |
| 64 | 66 | } |
| 65 | 67 | |
| 66 | 68 | pub fn deinit(hm: &Self) { |
| ... | ... | @@ -76,7 +78,8 @@ pub fn HashMap(comptime K: type, comptime V: type, |
| 76 | 78 | hm.incrementModificationCount(); |
| 77 | 79 | } |
| 78 | 80 | |
| 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 | 83 | if (hm.entries.len == 0) { |
| 81 | 84 | %return hm.initCapacity(16); |
| 82 | 85 | } |
| ... | ... | @@ -89,13 +92,13 @@ pub fn HashMap(comptime K: type, comptime V: type, |
| 89 | 92 | // dump all of the old elements into the new table |
| 90 | 93 | for (old_entries) |*old_entry| { |
| 91 | 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 | 98 | hm.allocator.free(old_entries); |
| 96 | 99 | } |
| 97 | 100 | |
| 98 | hm.internalPut(key, value); | |
| 101 | return hm.internalPut(key, value); | |
| 99 | 102 | } |
| 100 | 103 | |
| 101 | 104 | pub fn get(hm: &Self, key: K) -> ?&Entry { |
| ... | ... | @@ -134,7 +137,7 @@ pub fn HashMap(comptime K: type, comptime V: type, |
| 134 | 137 | return null; |
| 135 | 138 | } |
| 136 | 139 | |
| 137 | pub fn entryIterator(hm: &const Self) -> Iterator { | |
| 140 | pub fn iterator(hm: &const Self) -> Iterator { | |
| 138 | 141 | return Iterator { |
| 139 | 142 | .hm = hm, |
| 140 | 143 | .count = 0, |
| ... | ... | @@ -158,9 +161,10 @@ pub fn HashMap(comptime K: type, comptime V: type, |
| 158 | 161 | } |
| 159 | 162 | } |
| 160 | 163 | |
| 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 | 166 | var key = orig_key; |
| 163 | var value = orig_value; | |
| 167 | var value = *orig_value; | |
| 164 | 168 | const start_index = hm.keyToIndex(key); |
| 165 | 169 | var roll_over: usize = 0; |
| 166 | 170 | var distance_from_start_index: usize = 0; |
| ... | ... | @@ -187,7 +191,10 @@ pub fn HashMap(comptime K: type, comptime V: type, |
| 187 | 191 | continue; |
| 188 | 192 | } |
| 189 | 193 | |
| 190 | if (!entry.used) { | |
| 194 | var result: ?V = null; | |
| 195 | if (entry.used) { | |
| 196 | result = entry.value; | |
| 197 | } else { | |
| 191 | 198 | // adding an entry. otherwise overwriting old value with |
| 192 | 199 | // same key |
| 193 | 200 | hm.size += 1; |
| ... | ... | @@ -200,7 +207,7 @@ pub fn HashMap(comptime K: type, comptime V: type, |
| 200 | 207 | .key = key, |
| 201 | 208 | .value = value, |
| 202 | 209 | }; |
| 203 | return; | |
| 210 | return result; | |
| 204 | 211 | } |
| 205 | 212 | unreachable // put into a full map |
| 206 | 213 | } |
| ... | ... | @@ -224,15 +231,18 @@ pub fn HashMap(comptime K: type, comptime V: type, |
| 224 | 231 | } |
| 225 | 232 | |
| 226 | 233 | test "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); | |
| 229 | 235 | defer map.deinit(); |
| 230 | 236 | |
| 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); | |
| 236 | 246 | |
| 237 | 247 | assert((??map.get(2)).value == 22); |
| 238 | 248 | _ = map.remove(2); |
| ... | ... | @@ -243,6 +253,7 @@ test "basicHashMapTest" { |
| 243 | 253 | fn hash_i32(x: i32) -> u32 { |
| 244 | 254 | *@ptrcast(&u32, &x) |
| 245 | 255 | } |
| 256 | ||
| 246 | 257 | fn eql_i32(a: i32, b: i32) -> bool { |
| 247 | 258 | a == b |
| 248 | 259 | } |
std/mem.zig+4| ... | ... | @@ -208,6 +208,10 @@ pub fn split(s: []const u8, c: u8) -> SplitIterator { |
| 208 | 208 | } |
| 209 | 209 | } |
| 210 | 210 | |
| 211 | pub 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 | ||
| 211 | 215 | const SplitIterator = struct { |
| 212 | 216 | s: []const u8, |
| 213 | 217 | c: u8, |
std/special/build_runner.zig+89-9| ... | ... | @@ -1,26 +1,106 @@ |
| 1 | 1 | const root = @import("@build"); |
| 2 | 2 | const std = @import("std"); |
| 3 | 3 | const io = std.io; |
| 4 | const fmt = std.fmt; | |
| 4 | 5 | const os = std.os; |
| 5 | 6 | const Builder = std.build.Builder; |
| 6 | 7 | const mem = std.mem; |
| 8 | const List = std.list.List; | |
| 7 | 9 | |
| 8 | 10 | error InvalidArgs; |
| 9 | 11 | |
| 10 | 12 | pub 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 | 13 | // TODO use a more general purpose allocator here |
| 19 | 14 | var inc_allocator = %%mem.IncrementingAllocator.init(10 * 1024 * 1024); |
| 20 | 15 | defer inc_allocator.deinit(); |
| 21 | 16 | |
| 22 | var builder = Builder.init(zig_exe, &inc_allocator.allocator); | |
| 17 | const allocator = &inc_allocator.allocator; | |
| 18 | ||
| 19 | var builder = Builder.init(allocator); | |
| 23 | 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 | 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 | ||
| 69 | fn 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 | 138 | test "switchOnConstEnumWithVar" { |
| 139 | 139 | assert(!returnsFalse()); |
| 140 | 140 | } |
| 141 | ||
| 142 | test "switch on type" { | |
| 143 | assert(trueIfBoolFalseOtherwise(bool)); | |
| 144 | assert(!trueIfBoolFalseOtherwise(i32)); | |
| 145 | } | |
| 146 | ||
| 147 | fn trueIfBoolFalseOtherwise(comptime T: type) -> bool { | |
| 148 | switch (T) { | |
| 149 | bool => true, | |
| 150 | else => false, | |
| 151 | } | |
| 152 | } |