zig/lib/std/zig.zig
Andrew Kelley bfded492f0 stage2: rewire the frontend driver to whole-file-zir
* Remove some unused imports in AstGen.zig. I think it would make sense
   to start decoupling AstGen from the rest of the compiler code,
   similar to how the tokenizer and parser are decoupled.
 * AstGen: For decls, move the block_inline instructions to the top of
   the function so that they get lower ZIR instruction indexes. With
   this, the block_inline instruction index combined with its corresponding
   break_inline instruction index can be used to form a ZIR instruction
   range. This is useful for allocating an array to map ZIR instructions
   to semantically analyzed instructions.

 * Module: extract emit-h functionality into a struct, and only allocate
   it when emit-h is activated.
 * Module: remove the `decl_table` field. This previously was a table of
   all Decls in the entire Module. A "name hash" strategy was used to
   find decls within a given namespace, using this global table. Now,
   each Namespace has its own map of name to children Decls.
   - Additionally, there were 3 places that relied on iterating over
     decl_table in order to function:
     - C backend and SPIR-V backend. These now have their own decl_table
       that they keep populated when `updateDecl` and `removeDecl` are
       called.
     - emit-h. A `decl_table` field has been added to the new GlobalEmitH
       struct which is only allocated when emit-h is activated.
 * Module: fix ZIR serialization/deserialization bug in debug mode having
   to do with the secret safety tag for untagged unions. There is still an
   open TODO to investigate a friendlier solution to this problem with
   the language.
 * Module: improve deserialization of ZIR to allocate only exactly as
   much capacity as length in the instructions array so as to not waste
   space.
 * Module: move `srcHashEql` to `std.zig` to live next to the definition
   of `SrcHash` itself.
 * Module: re-introduce the logic for scanning top level declarations
   within a namespace.

 * Compilation: add an `analyze_pkg` Job which is used to kick off the
   start of semantic analysis by doing the equivalent of
   `_ = @import("std");`. The `analyze_pkg` job is unconditionally added
   to the work queue on every update(), with pkg set to the std lib pkg.

 * Rename TZIR to AIR in a few places. A more comprehensive rename will
   come later.
2021-04-26 20:41:07 -07:00

285 lines
11 KiB
Zig

// SPDX-License-Identifier: MIT
// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
const std = @import("std.zig");
const tokenizer = @import("zig/tokenizer.zig");
pub const Token = tokenizer.Token;
pub const Tokenizer = tokenizer.Tokenizer;
pub const fmtId = @import("zig/fmt.zig").fmtId;
pub const fmtEscapes = @import("zig/fmt.zig").fmtEscapes;
pub const parse = @import("zig/parse.zig").parse;
pub const string_literal = @import("zig/string_literal.zig");
pub const ast = @import("zig/ast.zig");
pub const system = @import("zig/system.zig");
pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget;
pub const SrcHash = [16]u8;
pub fn hashSrc(src: []const u8) SrcHash {
var out: SrcHash = undefined;
std.crypto.hash.Blake3.hash(src, &out, .{});
return out;
}
pub fn srcHashEql(a: SrcHash, b: SrcHash) bool {
return @bitCast(u128, a) == @bitCast(u128, b);
}
pub fn hashName(parent_hash: SrcHash, sep: []const u8, name: []const u8) SrcHash {
var out: SrcHash = undefined;
var hasher = std.crypto.hash.Blake3.init(.{});
hasher.update(&parent_hash);
hasher.update(sep);
hasher.update(name);
hasher.final(&out);
return out;
}
pub const Loc = struct {
line: usize,
column: usize,
/// Does not include the trailing newline.
source_line: []const u8,
};
pub fn findLineColumn(source: []const u8, byte_offset: usize) Loc {
var line: usize = 0;
var column: usize = 0;
var line_start: usize = 0;
var i: usize = 0;
while (i < byte_offset) : (i += 1) {
switch (source[i]) {
'\n' => {
line += 1;
column = 0;
line_start = i + 1;
},
else => {
column += 1;
},
}
}
while (i < source.len and source[i] != '\n') {
i += 1;
}
return .{
.line = line,
.column = column,
.source_line = source[line_start..i],
};
}
pub fn lineDelta(source: []const u8, start: usize, end: usize) isize {
var line: isize = 0;
if (end >= start) {
for (source[start..end]) |byte| switch (byte) {
'\n' => line += 1,
else => continue,
};
} else {
for (source[end..start]) |byte| switch (byte) {
'\n' => line -= 1,
else => continue,
};
}
return line;
}
pub const BinNameOptions = struct {
root_name: []const u8,
target: std.Target,
output_mode: std.builtin.OutputMode,
link_mode: ?std.builtin.LinkMode = null,
object_format: ?std.Target.ObjectFormat = null,
version: ?std.builtin.Version = null,
};
/// Returns the standard file system basename of a binary generated by the Zig compiler.
pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) error{OutOfMemory}![]u8 {
const root_name = options.root_name;
const target = options.target;
switch (options.object_format orelse target.getObjectFormat()) {
.coff, .pe => switch (options.output_mode) {
.Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.exeFileExt() }),
.Lib => {
const suffix = switch (options.link_mode orelse .Static) {
.Static => ".lib",
.Dynamic => ".dll",
};
return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, suffix });
},
.Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }),
},
.elf => switch (options.output_mode) {
.Exe => return allocator.dupe(u8, root_name),
.Lib => {
switch (options.link_mode orelse .Static) {
.Static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{
target.libPrefix(), root_name,
}),
.Dynamic => {
if (options.version) |ver| {
return std.fmt.allocPrint(allocator, "{s}{s}.so.{d}.{d}.{d}", .{
target.libPrefix(), root_name, ver.major, ver.minor, ver.patch,
});
} else {
return std.fmt.allocPrint(allocator, "{s}{s}.so", .{
target.libPrefix(), root_name,
});
}
},
}
},
.Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }),
},
.macho => switch (options.output_mode) {
.Exe => return allocator.dupe(u8, root_name),
.Lib => {
switch (options.link_mode orelse .Static) {
.Static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{
target.libPrefix(), root_name,
}),
.Dynamic => {
if (options.version) |ver| {
return std.fmt.allocPrint(allocator, "{s}{s}.{d}.{d}.{d}.dylib", .{
target.libPrefix(), root_name, ver.major, ver.minor, ver.patch,
});
} else {
return std.fmt.allocPrint(allocator, "{s}{s}.dylib", .{
target.libPrefix(), root_name,
});
}
},
}
return std.fmt.allocPrint(allocator, "{s}{s}{s}", .{ target.libPrefix(), root_name, suffix });
},
.Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }),
},
.wasm => switch (options.output_mode) {
.Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.exeFileExt() }),
.Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }),
.Lib => return std.fmt.allocPrint(allocator, "{s}.wasm", .{root_name}),
},
.c => return std.fmt.allocPrint(allocator, "{s}.c", .{root_name}),
.spirv => return std.fmt.allocPrint(allocator, "{s}.spv", .{root_name}),
.hex => return std.fmt.allocPrint(allocator, "{s}.ihex", .{root_name}),
.raw => return std.fmt.allocPrint(allocator, "{s}.bin", .{root_name}),
}
}
/// Only validates escape sequence characters.
/// Slice must be valid utf8 starting and ending with "'" and exactly one codepoint in between.
pub fn parseCharLiteral(
slice: []const u8,
bad_index: *usize, // populated if error.InvalidCharacter is returned
) error{InvalidCharacter}!u32 {
std.debug.assert(slice.len >= 3 and slice[0] == '\'' and slice[slice.len - 1] == '\'');
if (slice[1] == '\\') {
switch (slice[2]) {
'n' => return '\n',
'r' => return '\r',
'\\' => return '\\',
't' => return '\t',
'\'' => return '\'',
'"' => return '"',
'x' => {
if (slice.len != 6) {
bad_index.* = slice.len - 2;
return error.InvalidCharacter;
}
var value: u32 = 0;
for (slice[3..5]) |c, i| {
switch (c) {
'0'...'9' => {
value *= 16;
value += c - '0';
},
'a'...'f' => {
value *= 16;
value += c - 'a' + 10;
},
'A'...'F' => {
value *= 16;
value += c - 'A' + 10;
},
else => {
bad_index.* = 3 + i;
return error.InvalidCharacter;
},
}
}
return value;
},
'u' => {
if (slice.len < "'\\u{0}'".len or slice[3] != '{' or slice[slice.len - 2] != '}') {
bad_index.* = 2;
return error.InvalidCharacter;
}
var value: u32 = 0;
for (slice[4 .. slice.len - 2]) |c, i| {
switch (c) {
'0'...'9' => {
value *= 16;
value += c - '0';
},
'a'...'f' => {
value *= 16;
value += c - 'a' + 10;
},
'A'...'F' => {
value *= 16;
value += c - 'A' + 10;
},
else => {
bad_index.* = 4 + i;
return error.InvalidCharacter;
},
}
if (value > 0x10ffff) {
bad_index.* = 4 + i;
return error.InvalidCharacter;
}
}
return value;
},
else => {
bad_index.* = 2;
return error.InvalidCharacter;
},
}
}
return std.unicode.utf8Decode(slice[1 .. slice.len - 1]) catch unreachable;
}
test "parseCharLiteral" {
var bad_index: usize = undefined;
std.testing.expectEqual(try parseCharLiteral("'a'", &bad_index), 'a');
std.testing.expectEqual(try parseCharLiteral("'ä'", &bad_index), 'ä');
std.testing.expectEqual(try parseCharLiteral("'\\x00'", &bad_index), 0);
std.testing.expectEqual(try parseCharLiteral("'\\x4f'", &bad_index), 0x4f);
std.testing.expectEqual(try parseCharLiteral("'\\x4F'", &bad_index), 0x4f);
std.testing.expectEqual(try parseCharLiteral("'ぁ'", &bad_index), 0x3041);
std.testing.expectEqual(try parseCharLiteral("'\\u{0}'", &bad_index), 0);
std.testing.expectEqual(try parseCharLiteral("'\\u{3041}'", &bad_index), 0x3041);
std.testing.expectEqual(try parseCharLiteral("'\\u{7f}'", &bad_index), 0x7f);
std.testing.expectEqual(try parseCharLiteral("'\\u{7FFF}'", &bad_index), 0x7FFF);
std.testing.expectError(error.InvalidCharacter, parseCharLiteral("'\\x0'", &bad_index));
std.testing.expectError(error.InvalidCharacter, parseCharLiteral("'\\x000'", &bad_index));
std.testing.expectError(error.InvalidCharacter, parseCharLiteral("'\\y'", &bad_index));
std.testing.expectError(error.InvalidCharacter, parseCharLiteral("'\\u'", &bad_index));
std.testing.expectError(error.InvalidCharacter, parseCharLiteral("'\\uFFFF'", &bad_index));
std.testing.expectError(error.InvalidCharacter, parseCharLiteral("'\\u{}'", &bad_index));
std.testing.expectError(error.InvalidCharacter, parseCharLiteral("'\\u{FFFFFF}'", &bad_index));
std.testing.expectError(error.InvalidCharacter, parseCharLiteral("'\\u{FFFF'", &bad_index));
std.testing.expectError(error.InvalidCharacter, parseCharLiteral("'\\u{FFFF}x'", &bad_index));
}
test {
@import("std").testing.refAllDecls(@This());
}