mirror of
https://github.com/ziglang/zig.git
synced 2026-02-21 16:54:52 +00:00
Merge remote-tracking branch 'origin/master' into llvm6
This commit is contained in:
commit
fe66046283
53
README.md
53
README.md
@ -119,31 +119,22 @@ libc. Create demo games using Zig.
|
||||
[](https://travis-ci.org/zig-lang/zig)
|
||||
[](https://ci.appveyor.com/project/andrewrk/zig-d3l86/branch/master)
|
||||
|
||||
### Dependencies
|
||||
### Stage 1: Build Zig from C++ Source Code
|
||||
|
||||
#### Build Dependencies
|
||||
|
||||
These compile tools must be available on your system and are used to build
|
||||
the Zig compiler itself:
|
||||
#### Dependencies
|
||||
|
||||
##### POSIX
|
||||
|
||||
* gcc >= 5.0.0 or clang >= 3.6.0
|
||||
* cmake >= 2.8.5
|
||||
* LLVM, Clang, LLD libraries == 6.x, compiled with the same gcc or clang version above
|
||||
|
||||
##### Windows
|
||||
|
||||
* Microsoft Visual Studio 2015
|
||||
* LLVM, Clang, LLD libraries == 6.x, compiled with the same MSVC version above
|
||||
|
||||
#### Library Dependencies
|
||||
|
||||
These libraries must be installed on your system, with the development files
|
||||
available. The Zig compiler links against them. You have to use the same
|
||||
compiler for these libraries as you do to compile Zig.
|
||||
|
||||
* LLVM, Clang, and LLD libraries == 6.x
|
||||
|
||||
### Debug / Development Build
|
||||
#### Instructions
|
||||
|
||||
If you have gcc or clang installed, you can find out what `ZIG_LIBC_LIB_DIR`,
|
||||
`ZIG_LIBC_STATIC_LIB_DIR`, and `ZIG_LIBC_INCLUDE_DIR` should be set to
|
||||
@ -158,7 +149,7 @@ make install
|
||||
./zig build --build-file ../build.zig test
|
||||
```
|
||||
|
||||
#### MacOS
|
||||
##### MacOS
|
||||
|
||||
`ZIG_LIBC_LIB_DIR` and `ZIG_LIBC_STATIC_LIB_DIR` are unused.
|
||||
|
||||
@ -172,21 +163,35 @@ make install
|
||||
./zig build --build-file ../build.zig test
|
||||
```
|
||||
|
||||
#### Windows
|
||||
##### Windows
|
||||
|
||||
See https://github.com/zig-lang/zig/wiki/Building-Zig-on-Windows
|
||||
|
||||
### Release / Install Build
|
||||
### Stage 2: Build Self-Hosted Zig from Zig Source Code
|
||||
|
||||
Once installed, `ZIG_LIBC_LIB_DIR` and `ZIG_LIBC_INCLUDE_DIR` can be overridden
|
||||
by the `--libc-lib-dir` and `--libc-include-dir` parameters to the zig binary.
|
||||
*Note: Stage 2 compiler is not complete. Beta users of Zig should use the
|
||||
Stage 1 compiler for now.*
|
||||
|
||||
Dependencies are the same as Stage 1, except now you have a working zig compiler.
|
||||
|
||||
```
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DZIG_LIBC_LIB_DIR=/some/path -DZIG_LIBC_INCLUDE_DIR=/some/path -DZIG_LIBC_STATIC_INCLUDE_DIR=/some/path
|
||||
make
|
||||
sudo make install
|
||||
bin/zig build --build-file ../build.zig --prefix $(pwd)/stage2 install
|
||||
```
|
||||
|
||||
### Stage 3: Rebuild Self-Hosted Zig Using the Self-Hosted Compiler
|
||||
|
||||
This is the actual compiler binary that we will install to the system.
|
||||
|
||||
#### Debug / Development Build
|
||||
|
||||
```
|
||||
./stage2/bin/zig build --build-file ../build.zig --prefix $(pwd)/stage3 install
|
||||
```
|
||||
|
||||
#### Release / Install Build
|
||||
|
||||
```
|
||||
./stage2/bin/zig build --build-file ../build.zig install -Drelease-fast
|
||||
```
|
||||
|
||||
### Test Coverage
|
||||
|
||||
226
build.zig
226
build.zig
@ -1,6 +1,13 @@
|
||||
const Builder = @import("std").build.Builder;
|
||||
const std = @import("std");
|
||||
const Builder = std.build.Builder;
|
||||
const tests = @import("test/tests.zig");
|
||||
const os = @import("std").os;
|
||||
const os = std.os;
|
||||
const BufMap = std.BufMap;
|
||||
const warn = std.debug.warn;
|
||||
const mem = std.mem;
|
||||
const ArrayList = std.ArrayList;
|
||||
const Buffer = std.Buffer;
|
||||
const io = std.io;
|
||||
|
||||
pub fn build(b: &Builder) {
|
||||
const mode = b.standardReleaseOptions();
|
||||
@ -25,14 +32,18 @@ pub fn build(b: &Builder) {
|
||||
docs_step.dependOn(&docgen_cmd.step);
|
||||
docs_step.dependOn(&docgen_home_cmd.step);
|
||||
|
||||
var exe = b.addExecutable("zig", "src-self-hosted/main.zig");
|
||||
exe.setBuildMode(mode);
|
||||
exe.linkSystemLibrary("c");
|
||||
if (findLLVM(b)) |llvm| {
|
||||
var exe = b.addExecutable("zig", "src-self-hosted/main.zig");
|
||||
exe.setBuildMode(mode);
|
||||
exe.linkSystemLibrary("c");
|
||||
dependOnLib(exe, llvm);
|
||||
|
||||
b.default_step.dependOn(&exe.step);
|
||||
b.default_step.dependOn(docs_step);
|
||||
b.default_step.dependOn(&exe.step);
|
||||
b.default_step.dependOn(docs_step);
|
||||
|
||||
b.installArtifact(exe);
|
||||
b.installArtifact(exe);
|
||||
installStdLib(b);
|
||||
}
|
||||
|
||||
|
||||
const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter");
|
||||
@ -53,6 +64,10 @@ pub fn build(b: &Builder) {
|
||||
"std/special/compiler_rt/index.zig", "compiler-rt", "Run the compiler_rt tests",
|
||||
with_lldb));
|
||||
|
||||
test_step.dependOn(tests.addPkgTests(b, test_filter,
|
||||
"src-self-hosted/main.zig", "fmt", "Run the fmt tests",
|
||||
with_lldb));
|
||||
|
||||
test_step.dependOn(tests.addCompareOutputTests(b, test_filter));
|
||||
test_step.dependOn(tests.addBuildExampleTests(b, test_filter));
|
||||
test_step.dependOn(tests.addCompileErrorTests(b, test_filter));
|
||||
@ -60,3 +75,198 @@ pub fn build(b: &Builder) {
|
||||
test_step.dependOn(tests.addDebugSafetyTests(b, test_filter));
|
||||
test_step.dependOn(tests.addTranslateCTests(b, test_filter));
|
||||
}
|
||||
|
||||
fn dependOnLib(lib_exe_obj: &std.build.LibExeObjStep, dep: &const LibraryDep) {
|
||||
for (dep.libdirs.toSliceConst()) |lib_dir| {
|
||||
lib_exe_obj.addLibPath(lib_dir);
|
||||
}
|
||||
for (dep.libs.toSliceConst()) |lib| {
|
||||
lib_exe_obj.linkSystemLibrary(lib);
|
||||
}
|
||||
for (dep.includes.toSliceConst()) |include_path| {
|
||||
lib_exe_obj.addIncludeDir(include_path);
|
||||
}
|
||||
}
|
||||
|
||||
const LibraryDep = struct {
|
||||
libdirs: ArrayList([]const u8),
|
||||
libs: ArrayList([]const u8),
|
||||
includes: ArrayList([]const u8),
|
||||
};
|
||||
|
||||
fn findLLVM(b: &Builder) -> ?LibraryDep {
|
||||
const llvm_config_exe = b.findProgram(
|
||||
[][]const u8{"llvm-config-5.0", "llvm-config"},
|
||||
[][]const u8{
|
||||
"/usr/local/opt/llvm@5/bin",
|
||||
"/mingw64/bin",
|
||||
"/c/msys64/mingw64/bin",
|
||||
"c:/msys64/mingw64/bin",
|
||||
"C:/Libraries/llvm-5.0.0/bin",
|
||||
}) %% |err|
|
||||
{
|
||||
warn("unable to find llvm-config: {}\n", err);
|
||||
return null;
|
||||
};
|
||||
const libs_output = b.exec([][]const u8{llvm_config_exe, "--libs", "--system-libs"});
|
||||
const includes_output = b.exec([][]const u8{llvm_config_exe, "--includedir"});
|
||||
const libdir_output = b.exec([][]const u8{llvm_config_exe, "--libdir"});
|
||||
|
||||
var result = LibraryDep {
|
||||
.libs = ArrayList([]const u8).init(b.allocator),
|
||||
.includes = ArrayList([]const u8).init(b.allocator),
|
||||
.libdirs = ArrayList([]const u8).init(b.allocator),
|
||||
};
|
||||
{
|
||||
var it = mem.split(libs_output, " \n");
|
||||
while (it.next()) |lib_arg| {
|
||||
if (mem.startsWith(u8, lib_arg, "-l")) {
|
||||
%%result.libs.append(lib_arg[2..]);
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
var it = mem.split(includes_output, " \n");
|
||||
while (it.next()) |include_arg| {
|
||||
if (mem.startsWith(u8, include_arg, "-I")) {
|
||||
%%result.includes.append(include_arg[2..]);
|
||||
} else {
|
||||
%%result.includes.append(include_arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
var it = mem.split(libdir_output, " \n");
|
||||
while (it.next()) |libdir| {
|
||||
if (mem.startsWith(u8, libdir, "-L")) {
|
||||
%%result.libdirs.append(libdir[2..]);
|
||||
} else {
|
||||
%%result.libdirs.append(libdir);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn installStdLib(b: &Builder) {
|
||||
const stdlib_files = []const []const u8 {
|
||||
"array_list.zig",
|
||||
"base64.zig",
|
||||
"buf_map.zig",
|
||||
"buf_set.zig",
|
||||
"buffer.zig",
|
||||
"build.zig",
|
||||
"c/darwin.zig",
|
||||
"c/index.zig",
|
||||
"c/linux.zig",
|
||||
"c/windows.zig",
|
||||
"cstr.zig",
|
||||
"debug.zig",
|
||||
"dwarf.zig",
|
||||
"elf.zig",
|
||||
"empty.zig",
|
||||
"endian.zig",
|
||||
"fmt/errol/enum3.zig",
|
||||
"fmt/errol/index.zig",
|
||||
"fmt/errol/lookup.zig",
|
||||
"fmt/index.zig",
|
||||
"hash_map.zig",
|
||||
"heap.zig",
|
||||
"index.zig",
|
||||
"io.zig",
|
||||
"linked_list.zig",
|
||||
"math/acos.zig",
|
||||
"math/acosh.zig",
|
||||
"math/asin.zig",
|
||||
"math/asinh.zig",
|
||||
"math/atan.zig",
|
||||
"math/atan2.zig",
|
||||
"math/atanh.zig",
|
||||
"math/cbrt.zig",
|
||||
"math/ceil.zig",
|
||||
"math/copysign.zig",
|
||||
"math/cos.zig",
|
||||
"math/cosh.zig",
|
||||
"math/exp.zig",
|
||||
"math/exp2.zig",
|
||||
"math/expm1.zig",
|
||||
"math/expo2.zig",
|
||||
"math/fabs.zig",
|
||||
"math/floor.zig",
|
||||
"math/fma.zig",
|
||||
"math/frexp.zig",
|
||||
"math/hypot.zig",
|
||||
"math/ilogb.zig",
|
||||
"math/index.zig",
|
||||
"math/inf.zig",
|
||||
"math/isfinite.zig",
|
||||
"math/isinf.zig",
|
||||
"math/isnan.zig",
|
||||
"math/isnormal.zig",
|
||||
"math/ln.zig",
|
||||
"math/log.zig",
|
||||
"math/log10.zig",
|
||||
"math/log1p.zig",
|
||||
"math/log2.zig",
|
||||
"math/modf.zig",
|
||||
"math/nan.zig",
|
||||
"math/pow.zig",
|
||||
"math/round.zig",
|
||||
"math/scalbn.zig",
|
||||
"math/signbit.zig",
|
||||
"math/sin.zig",
|
||||
"math/sinh.zig",
|
||||
"math/sqrt.zig",
|
||||
"math/tan.zig",
|
||||
"math/tanh.zig",
|
||||
"math/trunc.zig",
|
||||
"mem.zig",
|
||||
"net.zig",
|
||||
"os/child_process.zig",
|
||||
"os/darwin.zig",
|
||||
"os/darwin_errno.zig",
|
||||
"os/get_user_id.zig",
|
||||
"os/index.zig",
|
||||
"os/linux.zig",
|
||||
"os/linux_errno.zig",
|
||||
"os/linux_i386.zig",
|
||||
"os/linux_x86_64.zig",
|
||||
"os/path.zig",
|
||||
"os/windows/error.zig",
|
||||
"os/windows/index.zig",
|
||||
"os/windows/util.zig",
|
||||
"rand.zig",
|
||||
"sort.zig",
|
||||
"special/bootstrap.zig",
|
||||
"special/bootstrap_lib.zig",
|
||||
"special/build_file_template.zig",
|
||||
"special/build_runner.zig",
|
||||
"special/builtin.zig",
|
||||
"special/compiler_rt/aulldiv.zig",
|
||||
"special/compiler_rt/aullrem.zig",
|
||||
"special/compiler_rt/comparetf2.zig",
|
||||
"special/compiler_rt/fixuint.zig",
|
||||
"special/compiler_rt/fixunsdfdi.zig",
|
||||
"special/compiler_rt/fixunsdfsi.zig",
|
||||
"special/compiler_rt/fixunsdfti.zig",
|
||||
"special/compiler_rt/fixunssfdi.zig",
|
||||
"special/compiler_rt/fixunssfsi.zig",
|
||||
"special/compiler_rt/fixunssfti.zig",
|
||||
"special/compiler_rt/fixunstfdi.zig",
|
||||
"special/compiler_rt/fixunstfsi.zig",
|
||||
"special/compiler_rt/fixunstfti.zig",
|
||||
"special/compiler_rt/index.zig",
|
||||
"special/compiler_rt/udivmod.zig",
|
||||
"special/compiler_rt/udivmoddi4.zig",
|
||||
"special/compiler_rt/udivmodti4.zig",
|
||||
"special/compiler_rt/udivti3.zig",
|
||||
"special/compiler_rt/umodti3.zig",
|
||||
"special/panic.zig",
|
||||
"special/test_runner.zig",
|
||||
};
|
||||
for (stdlib_files) |stdlib_file| {
|
||||
const src_path = %%os.path.join(b.allocator, "std", stdlib_file);
|
||||
const dest_path = %%os.path.join(b.allocator, "lib", "zig", "std", stdlib_file);
|
||||
b.installFile(src_path, dest_path);
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,14 +42,14 @@ const State = enum {
|
||||
|
||||
// TODO look for code segments
|
||||
|
||||
fn gen(in: &io.InStream, out: &const io.OutStream) {
|
||||
fn gen(in: &io.InStream, out: &io.OutStream) {
|
||||
var state = State.Start;
|
||||
while (true) {
|
||||
const byte = in.readByte() %% |err| {
|
||||
if (err == error.EndOfStream) {
|
||||
return;
|
||||
}
|
||||
std.debug.panic("{}", err)
|
||||
std.debug.panic("{}", err);
|
||||
};
|
||||
switch (state) {
|
||||
State.Start => switch (byte) {
|
||||
|
||||
@ -136,6 +136,7 @@
|
||||
<li><a href="#builtin-divFloor">@divFloor</a></li>
|
||||
<li><a href="#builtin-divTrunc">@divTrunc</a></li>
|
||||
<li><a href="#builtin-embedFile">@embedFile</a></li>
|
||||
<li><a href="#builtin-export">@export</a></li>
|
||||
<li><a href="#builtin-tagName">@tagName</a></li>
|
||||
<li><a href="#builtin-EnumTagType">@EnumTagType</a></li>
|
||||
<li><a href="#builtin-errorName">@errorName</a></li>
|
||||
@ -3020,14 +3021,13 @@ const assert = @import("std").debug.assert;</code></pre>
|
||||
<pre><code class="zig">const assert = @import("std").debug.assert;
|
||||
|
||||
// Functions are declared like this
|
||||
// The last expression in the function can be used as the return value.
|
||||
fn add(a: i8, b: i8) -> i8 {
|
||||
if (a == 0) {
|
||||
// You can still return manually if needed.
|
||||
return b;
|
||||
}
|
||||
|
||||
a + b
|
||||
return a + b;
|
||||
}
|
||||
|
||||
// The export specifier makes a function externally visible in the generated
|
||||
@ -4368,6 +4368,11 @@ test.zig:6:2: error: found compile log statement
|
||||
<ul>
|
||||
<li><a href="#builtin-import">@import</a></li>
|
||||
</ul>
|
||||
<h3 id="builtin-export">@export</h3>
|
||||
<pre><code class="zig">@export(comptime name: []const u8, target: var, linkage: builtin.GlobalLinkage) -> []const u8</code></pre>
|
||||
<p>
|
||||
Creates a symbol in the output object file.
|
||||
</p>
|
||||
<h3 id="builtin-tagName">@tagName</h3>
|
||||
<pre><code class="zig">@tagName(value: var) -> []const u8</code></pre>
|
||||
<p>
|
||||
@ -5815,13 +5820,15 @@ TopLevelItem = ErrorValueDecl | CompTimeExpression(Block) | TopLevelDecl | TestD
|
||||
|
||||
TestDecl = "test" String Block
|
||||
|
||||
TopLevelDecl = option(VisibleMod) (FnDef | ExternDecl | GlobalVarDecl | UseDecl)
|
||||
TopLevelDecl = option("pub") (FnDef | ExternDecl | GlobalVarDecl | UseDecl)
|
||||
|
||||
ErrorValueDecl = "error" Symbol ";"
|
||||
|
||||
GlobalVarDecl = VariableDeclaration ";"
|
||||
GlobalVarDecl = option("export") VariableDeclaration ";"
|
||||
|
||||
VariableDeclaration = option("comptime") ("var" | "const") Symbol option(":" TypeExpr) option("align" "(" Expression ")") "=" Expression
|
||||
LocalVarDecl = option("comptime") VariableDeclaration
|
||||
|
||||
VariableDeclaration = ("var" | "const") Symbol option(":" TypeExpr) option("align" "(" Expression ")") option("section" "(" Expression ")") "=" Expression
|
||||
|
||||
ContainerMember = (ContainerField | FnDef | GlobalVarDecl)
|
||||
|
||||
@ -5831,21 +5838,17 @@ UseDecl = "use" Expression ";"
|
||||
|
||||
ExternDecl = "extern" option(String) (FnProto | VariableDeclaration) ";"
|
||||
|
||||
FnProto = option("coldcc" | "nakedcc" | "stdcallcc") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("->" TypeExpr)
|
||||
FnProto = option("coldcc" | "nakedcc" | "stdcallcc" | "extern") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("->" TypeExpr)
|
||||
|
||||
VisibleMod = "pub" | "export"
|
||||
|
||||
FnDef = option("inline" | "extern") FnProto Block
|
||||
FnDef = option("inline" | "export") FnProto Block
|
||||
|
||||
ParamDeclList = "(" list(ParamDecl, ",") ")"
|
||||
|
||||
ParamDecl = option("noalias" | "comptime") option(Symbol ":") (TypeExpr | "...")
|
||||
|
||||
Block = "{" many(Statement) option(Expression) "}"
|
||||
Block = option(Symbol ":") "{" many(Statement) "}"
|
||||
|
||||
Statement = Label | VariableDeclaration ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";"
|
||||
|
||||
Label = Symbol ":"
|
||||
Statement = LocalVarDecl ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";"
|
||||
|
||||
TypeExpr = PrefixOpExpression | "var"
|
||||
|
||||
@ -5885,13 +5888,13 @@ SwitchProng = (list(SwitchItem, ",") | "else") "=>" option("|" option("*") Sy
|
||||
|
||||
SwitchItem = Expression | (Expression "..." Expression)
|
||||
|
||||
ForExpression(body) = option("inline") "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body option("else" BlockExpression(body))
|
||||
ForExpression(body) = option(Symbol ":") option("inline") "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body option("else" BlockExpression(body))
|
||||
|
||||
BoolOrExpression = BoolAndExpression "or" BoolOrExpression | BoolAndExpression
|
||||
|
||||
ReturnExpression = option("%") "return" option(Expression)
|
||||
|
||||
BreakExpression = "break" option(Expression)
|
||||
BreakExpression = "break" option(":" Symbol) option(Expression)
|
||||
|
||||
Defer(body) = option("%") "defer" body
|
||||
|
||||
@ -5901,7 +5904,7 @@ TryExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|")
|
||||
|
||||
TestExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body option("else" BlockExpression(body))
|
||||
|
||||
WhileExpression(body) = option("inline") "while" "(" Expression ")" option("|" option("*") Symbol "|") option(":" "(" Expression ")") body option("else" option("|" Symbol "|") BlockExpression(body))
|
||||
WhileExpression(body) = option(Symbol ":") option("inline") "while" "(" Expression ")" option("|" option("*") Symbol "|") option(":" "(" Expression ")") body option("else" option("|" Symbol "|") BlockExpression(body))
|
||||
|
||||
BoolAndExpression = ComparisonExpression "and" BoolAndExpression | ComparisonExpression
|
||||
|
||||
@ -5949,15 +5952,13 @@ StructLiteralField = "." Symbol "=" Expression
|
||||
|
||||
PrefixOp = "!" | "-" | "~" | "*" | ("&" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "%" | "%%" | "??" | "-%"
|
||||
|
||||
PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | (option("extern") FnProto) | AsmExpression | ("error" "." Symbol) | ContainerDecl
|
||||
PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." Symbol) | ContainerDecl | ("continue" option(":" Symbol))
|
||||
|
||||
ArrayType : "[" option(Expression) "]" option("align" "(" Expression option(":" Integer ":" Integer) ")")) option("const") option("volatile") TypeExpr
|
||||
|
||||
GotoExpression = "goto" Symbol
|
||||
|
||||
GroupedExpression = "(" Expression ")"
|
||||
|
||||
KeywordLiteral = "true" | "false" | "null" | "continue" | "undefined" | "error" | "this" | "unreachable"
|
||||
KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "unreachable"
|
||||
|
||||
ContainerDecl = option("extern" | "packed")
|
||||
("struct" option(GroupedExpression) | "union" option("enum" option(GroupedExpression) | GroupedExpression) | ("enum" option(GroupedExpression)))
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
export fn add(a: i32, b: i32) -> i32 {
|
||||
a + b
|
||||
return a + b;
|
||||
}
|
||||
|
||||
273
src-self-hosted/ast.zig
Normal file
273
src-self-hosted/ast.zig
Normal file
@ -0,0 +1,273 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const ArrayList = std.ArrayList;
|
||||
const Token = @import("tokenizer.zig").Token;
|
||||
const mem = std.mem;
|
||||
|
||||
pub const Node = struct {
|
||||
id: Id,
|
||||
|
||||
pub const Id = enum {
|
||||
Root,
|
||||
VarDecl,
|
||||
Identifier,
|
||||
FnProto,
|
||||
ParamDecl,
|
||||
Block,
|
||||
InfixOp,
|
||||
PrefixOp,
|
||||
IntegerLiteral,
|
||||
FloatLiteral,
|
||||
};
|
||||
|
||||
pub fn iterate(base: &Node, index: usize) -> ?&Node {
|
||||
return switch (base.id) {
|
||||
Id.Root => @fieldParentPtr(NodeRoot, "base", base).iterate(index),
|
||||
Id.VarDecl => @fieldParentPtr(NodeVarDecl, "base", base).iterate(index),
|
||||
Id.Identifier => @fieldParentPtr(NodeIdentifier, "base", base).iterate(index),
|
||||
Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).iterate(index),
|
||||
Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).iterate(index),
|
||||
Id.Block => @fieldParentPtr(NodeBlock, "base", base).iterate(index),
|
||||
Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).iterate(index),
|
||||
Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).iterate(index),
|
||||
Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).iterate(index),
|
||||
Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).iterate(index),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn destroy(base: &Node, allocator: &mem.Allocator) {
|
||||
return switch (base.id) {
|
||||
Id.Root => allocator.destroy(@fieldParentPtr(NodeRoot, "base", base)),
|
||||
Id.VarDecl => allocator.destroy(@fieldParentPtr(NodeVarDecl, "base", base)),
|
||||
Id.Identifier => allocator.destroy(@fieldParentPtr(NodeIdentifier, "base", base)),
|
||||
Id.FnProto => allocator.destroy(@fieldParentPtr(NodeFnProto, "base", base)),
|
||||
Id.ParamDecl => allocator.destroy(@fieldParentPtr(NodeParamDecl, "base", base)),
|
||||
Id.Block => allocator.destroy(@fieldParentPtr(NodeBlock, "base", base)),
|
||||
Id.InfixOp => allocator.destroy(@fieldParentPtr(NodeInfixOp, "base", base)),
|
||||
Id.PrefixOp => allocator.destroy(@fieldParentPtr(NodePrefixOp, "base", base)),
|
||||
Id.IntegerLiteral => allocator.destroy(@fieldParentPtr(NodeIntegerLiteral, "base", base)),
|
||||
Id.FloatLiteral => allocator.destroy(@fieldParentPtr(NodeFloatLiteral, "base", base)),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const NodeRoot = struct {
|
||||
base: Node,
|
||||
decls: ArrayList(&Node),
|
||||
|
||||
pub fn iterate(self: &NodeRoot, index: usize) -> ?&Node {
|
||||
if (index < self.decls.len) {
|
||||
return self.decls.items[self.decls.len - index - 1];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
pub const NodeVarDecl = struct {
|
||||
base: Node,
|
||||
visib_token: ?Token,
|
||||
name_token: Token,
|
||||
eq_token: Token,
|
||||
mut_token: Token,
|
||||
comptime_token: ?Token,
|
||||
extern_token: ?Token,
|
||||
lib_name: ?&Node,
|
||||
type_node: ?&Node,
|
||||
align_node: ?&Node,
|
||||
init_node: ?&Node,
|
||||
|
||||
pub fn iterate(self: &NodeVarDecl, index: usize) -> ?&Node {
|
||||
var i = index;
|
||||
|
||||
if (self.type_node) |type_node| {
|
||||
if (i < 1) return type_node;
|
||||
i -= 1;
|
||||
}
|
||||
|
||||
if (self.align_node) |align_node| {
|
||||
if (i < 1) return align_node;
|
||||
i -= 1;
|
||||
}
|
||||
|
||||
if (self.init_node) |init_node| {
|
||||
if (i < 1) return init_node;
|
||||
i -= 1;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
pub const NodeIdentifier = struct {
|
||||
base: Node,
|
||||
name_token: Token,
|
||||
|
||||
pub fn iterate(self: &NodeIdentifier, index: usize) -> ?&Node {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
pub const NodeFnProto = struct {
|
||||
base: Node,
|
||||
visib_token: ?Token,
|
||||
fn_token: Token,
|
||||
name_token: ?Token,
|
||||
params: ArrayList(&Node),
|
||||
return_type: ?&Node,
|
||||
var_args_token: ?Token,
|
||||
extern_token: ?Token,
|
||||
inline_token: ?Token,
|
||||
cc_token: ?Token,
|
||||
body_node: ?&Node,
|
||||
lib_name: ?&Node, // populated if this is an extern declaration
|
||||
align_expr: ?&Node, // populated if align(A) is present
|
||||
|
||||
pub fn iterate(self: &NodeFnProto, index: usize) -> ?&Node {
|
||||
var i = index;
|
||||
|
||||
if (self.body_node) |body_node| {
|
||||
if (i < 1) return body_node;
|
||||
i -= 1;
|
||||
}
|
||||
|
||||
if (self.return_type) |return_type| {
|
||||
if (i < 1) return return_type;
|
||||
i -= 1;
|
||||
}
|
||||
|
||||
if (self.align_expr) |align_expr| {
|
||||
if (i < 1) return align_expr;
|
||||
i -= 1;
|
||||
}
|
||||
|
||||
if (i < self.params.len) return self.params.items[self.params.len - i - 1];
|
||||
i -= self.params.len;
|
||||
|
||||
if (self.lib_name) |lib_name| {
|
||||
if (i < 1) return lib_name;
|
||||
i -= 1;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
pub const NodeParamDecl = struct {
|
||||
base: Node,
|
||||
comptime_token: ?Token,
|
||||
noalias_token: ?Token,
|
||||
name_token: ?Token,
|
||||
type_node: &Node,
|
||||
var_args_token: ?Token,
|
||||
|
||||
pub fn iterate(self: &NodeParamDecl, index: usize) -> ?&Node {
|
||||
var i = index;
|
||||
|
||||
if (i < 1) return self.type_node;
|
||||
i -= 1;
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
pub const NodeBlock = struct {
|
||||
base: Node,
|
||||
begin_token: Token,
|
||||
end_token: Token,
|
||||
statements: ArrayList(&Node),
|
||||
|
||||
pub fn iterate(self: &NodeBlock, index: usize) -> ?&Node {
|
||||
var i = index;
|
||||
|
||||
if (i < self.statements.len) return self.statements.items[i];
|
||||
i -= self.statements.len;
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
pub const NodeInfixOp = struct {
|
||||
base: Node,
|
||||
op_token: Token,
|
||||
lhs: &Node,
|
||||
op: InfixOp,
|
||||
rhs: &Node,
|
||||
|
||||
const InfixOp = enum {
|
||||
EqualEqual,
|
||||
BangEqual,
|
||||
};
|
||||
|
||||
pub fn iterate(self: &NodeInfixOp, index: usize) -> ?&Node {
|
||||
var i = index;
|
||||
|
||||
if (i < 1) return self.lhs;
|
||||
i -= 1;
|
||||
|
||||
switch (self.op) {
|
||||
InfixOp.EqualEqual => {},
|
||||
InfixOp.BangEqual => {},
|
||||
}
|
||||
|
||||
if (i < 1) return self.rhs;
|
||||
i -= 1;
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
pub const NodePrefixOp = struct {
|
||||
base: Node,
|
||||
op_token: Token,
|
||||
op: PrefixOp,
|
||||
rhs: &Node,
|
||||
|
||||
const PrefixOp = union(enum) {
|
||||
Return,
|
||||
AddrOf: AddrOfInfo,
|
||||
};
|
||||
const AddrOfInfo = struct {
|
||||
align_expr: ?&Node,
|
||||
bit_offset_start_token: ?Token,
|
||||
bit_offset_end_token: ?Token,
|
||||
const_token: ?Token,
|
||||
volatile_token: ?Token,
|
||||
};
|
||||
|
||||
pub fn iterate(self: &NodePrefixOp, index: usize) -> ?&Node {
|
||||
var i = index;
|
||||
|
||||
switch (self.op) {
|
||||
PrefixOp.Return => {},
|
||||
PrefixOp.AddrOf => |addr_of_info| {
|
||||
if (addr_of_info.align_expr) |align_expr| {
|
||||
if (i < 1) return align_expr;
|
||||
i -= 1;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
if (i < 1) return self.rhs;
|
||||
i -= 1;
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
pub const NodeIntegerLiteral = struct {
|
||||
base: Node,
|
||||
token: Token,
|
||||
|
||||
pub fn iterate(self: &NodeIntegerLiteral, index: usize) -> ?&Node {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
pub const NodeFloatLiteral = struct {
|
||||
base: Node,
|
||||
token: Token,
|
||||
|
||||
pub fn iterate(self: &NodeFloatLiteral, index: usize) -> ?&Node {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
7
src-self-hosted/c.zig
Normal file
7
src-self-hosted/c.zig
Normal file
@ -0,0 +1,7 @@
|
||||
pub use @cImport({
|
||||
@cInclude("llvm-c/Core.h");
|
||||
@cInclude("llvm-c/Analysis.h");
|
||||
@cInclude("llvm-c/Target.h");
|
||||
@cInclude("llvm-c/Initialization.h");
|
||||
@cInclude("llvm-c/TargetMachine.h");
|
||||
});
|
||||
13
src-self-hosted/llvm.zig
Normal file
13
src-self-hosted/llvm.zig
Normal file
@ -0,0 +1,13 @@
|
||||
const builtin = @import("builtin");
|
||||
const c = @import("c.zig");
|
||||
const assert = @import("std").debug.assert;
|
||||
|
||||
pub const ValueRef = removeNullability(c.LLVMValueRef);
|
||||
pub const ModuleRef = removeNullability(c.LLVMModuleRef);
|
||||
pub const ContextRef = removeNullability(c.LLVMContextRef);
|
||||
pub const BuilderRef = removeNullability(c.LLVMBuilderRef);
|
||||
|
||||
fn removeNullability(comptime T: type) -> type {
|
||||
comptime assert(@typeId(T) == builtin.TypeId.Nullable);
|
||||
return T.Child;
|
||||
}
|
||||
@ -1,208 +1,476 @@
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const io = std.io;
|
||||
const os = std.os;
|
||||
const heap = std.heap;
|
||||
const warn = std.debug.warn;
|
||||
const assert = std.debug.assert;
|
||||
const target = @import("target.zig");
|
||||
const Target = target.Target;
|
||||
const Module = @import("module.zig").Module;
|
||||
const ErrColor = Module.ErrColor;
|
||||
const Emit = Module.Emit;
|
||||
const builtin = @import("builtin");
|
||||
const io = @import("std").io;
|
||||
const os = @import("std").os;
|
||||
const heap = @import("std").heap;
|
||||
const ArrayList = std.ArrayList;
|
||||
|
||||
// TODO: sync up CLI with c++ code
|
||||
// TODO: concurrency
|
||||
error InvalidCommandLineArguments;
|
||||
error ZigLibDirNotFound;
|
||||
error ZigInstallationNotFound;
|
||||
|
||||
error InvalidArgument;
|
||||
error MissingArg0;
|
||||
|
||||
var arg0: []u8 = undefined;
|
||||
|
||||
var stderr_file: io.File = undefined;
|
||||
const stderr = &stderr_file.out_stream;
|
||||
const default_zig_cache_name = "zig-cache";
|
||||
|
||||
pub fn main() -> %void {
|
||||
stderr_file = %return io.getStdErr();
|
||||
if (internal_main()) |_| {
|
||||
return;
|
||||
} else |err| {
|
||||
if (err == error.InvalidArgument) {
|
||||
stderr.print("\n") %% return err;
|
||||
printUsage(stderr) %% return err;
|
||||
} else {
|
||||
stderr.print("{}\n", err) %% return err;
|
||||
main2() %% |err| {
|
||||
if (err != error.InvalidCommandLineArguments) {
|
||||
warn("{}\n", @errorName(err));
|
||||
}
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn internal_main() -> %void {
|
||||
var args_it = os.args();
|
||||
|
||||
var incrementing_allocator = heap.IncrementingAllocator.init(10 * 1024 * 1024) %% |err| {
|
||||
io.stderr.printf("Unable to allocate memory") %% {};
|
||||
return err;
|
||||
};
|
||||
defer incrementing_allocator.deinit();
|
||||
}
|
||||
|
||||
const allocator = &incrementing_allocator.allocator;
|
||||
|
||||
arg0 = %return (args_it.next(allocator) ?? error.MissingArg0);
|
||||
defer allocator.free(arg0);
|
||||
const Cmd = enum {
|
||||
None,
|
||||
Build,
|
||||
Test,
|
||||
Version,
|
||||
Zen,
|
||||
TranslateC,
|
||||
Targets,
|
||||
};
|
||||
|
||||
fn badArgs(comptime format: []const u8, args: ...) -> error {
|
||||
var stderr = %return io.getStdErr();
|
||||
var stderr_stream_adapter = io.FileOutStream.init(&stderr);
|
||||
const stderr_stream = &stderr_stream_adapter.stream;
|
||||
%return stderr_stream.print(format ++ "\n\n", args);
|
||||
%return printUsage(&stderr_stream_adapter.stream);
|
||||
return error.InvalidCommandLineArguments;
|
||||
}
|
||||
|
||||
pub fn main2() -> %void {
|
||||
const allocator = std.heap.c_allocator;
|
||||
|
||||
const args = %return os.argsAlloc(allocator);
|
||||
defer os.argsFree(allocator, args);
|
||||
|
||||
var cmd = Cmd.None;
|
||||
var build_kind: Module.Kind = undefined;
|
||||
var build_mode: builtin.Mode = builtin.Mode.Debug;
|
||||
var color = ErrColor.Auto;
|
||||
var emit_file_type = Emit.Binary;
|
||||
|
||||
var build_mode = builtin.Mode.Debug;
|
||||
var strip = false;
|
||||
var is_static = false;
|
||||
var verbose = false;
|
||||
var verbose_tokenize = false;
|
||||
var verbose_ast_tree = false;
|
||||
var verbose_ast_fmt = false;
|
||||
var verbose_link = false;
|
||||
var verbose_ir = false;
|
||||
var verbose_llvm_ir = false;
|
||||
var verbose_cimport = false;
|
||||
var mwindows = false;
|
||||
var mconsole = false;
|
||||
var rdynamic = false;
|
||||
var each_lib_rpath = false;
|
||||
var timing_info = false;
|
||||
|
||||
while (args_it.next()) |arg_or_err| {
|
||||
const arg = %return arg_or_err;
|
||||
var in_file_arg: ?[]u8 = null;
|
||||
var out_file: ?[]u8 = null;
|
||||
var out_file_h: ?[]u8 = null;
|
||||
var out_name_arg: ?[]u8 = null;
|
||||
var libc_lib_dir_arg: ?[]u8 = null;
|
||||
var libc_static_lib_dir_arg: ?[]u8 = null;
|
||||
var libc_include_dir_arg: ?[]u8 = null;
|
||||
var msvc_lib_dir_arg: ?[]u8 = null;
|
||||
var kernel32_lib_dir_arg: ?[]u8 = null;
|
||||
var zig_install_prefix: ?[]u8 = null;
|
||||
var dynamic_linker_arg: ?[]u8 = null;
|
||||
var cache_dir_arg: ?[]const u8 = null;
|
||||
var target_arch: ?[]u8 = null;
|
||||
var target_os: ?[]u8 = null;
|
||||
var target_environ: ?[]u8 = null;
|
||||
var mmacosx_version_min: ?[]u8 = null;
|
||||
var mios_version_min: ?[]u8 = null;
|
||||
var linker_script_arg: ?[]u8 = null;
|
||||
var test_name_prefix_arg: ?[]u8 = null;
|
||||
|
||||
if (arg[0] == '-') {
|
||||
if (strcmp(arg, "--release-fast") == 0) {
|
||||
var test_filters = ArrayList([]const u8).init(allocator);
|
||||
defer test_filters.deinit();
|
||||
|
||||
var lib_dirs = ArrayList([]const u8).init(allocator);
|
||||
defer lib_dirs.deinit();
|
||||
|
||||
var clang_argv = ArrayList([]const u8).init(allocator);
|
||||
defer clang_argv.deinit();
|
||||
|
||||
var llvm_argv = ArrayList([]const u8).init(allocator);
|
||||
defer llvm_argv.deinit();
|
||||
|
||||
var link_libs = ArrayList([]const u8).init(allocator);
|
||||
defer link_libs.deinit();
|
||||
|
||||
var frameworks = ArrayList([]const u8).init(allocator);
|
||||
defer frameworks.deinit();
|
||||
|
||||
var objects = ArrayList([]const u8).init(allocator);
|
||||
defer objects.deinit();
|
||||
|
||||
var asm_files = ArrayList([]const u8).init(allocator);
|
||||
defer asm_files.deinit();
|
||||
|
||||
var rpath_list = ArrayList([]const u8).init(allocator);
|
||||
defer rpath_list.deinit();
|
||||
|
||||
var ver_major: u32 = 0;
|
||||
var ver_minor: u32 = 0;
|
||||
var ver_patch: u32 = 0;
|
||||
|
||||
var arg_i: usize = 1;
|
||||
while (arg_i < args.len) : (arg_i += 1) {
|
||||
const arg = args[arg_i];
|
||||
|
||||
if (arg.len != 0 and arg[0] == '-') {
|
||||
if (mem.eql(u8, arg, "--release-fast")) {
|
||||
build_mode = builtin.Mode.ReleaseFast;
|
||||
} else if (strcmp(arg, "--release-safe") == 0) {
|
||||
} else if (mem.eql(u8, arg, "--release-safe")) {
|
||||
build_mode = builtin.Mode.ReleaseSafe;
|
||||
} else if (strcmp(arg, "--strip") == 0) {
|
||||
} else if (mem.eql(u8, arg, "--strip")) {
|
||||
strip = true;
|
||||
} else if (strcmp(arg, "--static") == 0) {
|
||||
} else if (mem.eql(u8, arg, "--static")) {
|
||||
is_static = true;
|
||||
} else if (strcmp(arg, "--verbose") == 0) {
|
||||
verbose = true;
|
||||
} else if (strcmp(arg, "--verbose-link") == 0) {
|
||||
} else if (mem.eql(u8, arg, "--verbose-tokenize")) {
|
||||
verbose_tokenize = true;
|
||||
} else if (mem.eql(u8, arg, "--verbose-ast-tree")) {
|
||||
verbose_ast_tree = true;
|
||||
} else if (mem.eql(u8, arg, "--verbose-ast-fmt")) {
|
||||
verbose_ast_fmt = true;
|
||||
} else if (mem.eql(u8, arg, "--verbose-link")) {
|
||||
verbose_link = true;
|
||||
} else if (strcmp(arg, "--verbose-ir") == 0) {
|
||||
} else if (mem.eql(u8, arg, "--verbose-ir")) {
|
||||
verbose_ir = true;
|
||||
} else if (strcmp(arg, "-mwindows") == 0) {
|
||||
} else if (mem.eql(u8, arg, "--verbose-llvm-ir")) {
|
||||
verbose_llvm_ir = true;
|
||||
} else if (mem.eql(u8, arg, "--verbose-cimport")) {
|
||||
verbose_cimport = true;
|
||||
} else if (mem.eql(u8, arg, "-mwindows")) {
|
||||
mwindows = true;
|
||||
} else if (strcmp(arg, "-mconsole") == 0) {
|
||||
} else if (mem.eql(u8, arg, "-mconsole")) {
|
||||
mconsole = true;
|
||||
} else if (strcmp(arg, "-municode") == 0) {
|
||||
municode = true;
|
||||
} else if (strcmp(arg, "-rdynamic") == 0) {
|
||||
} else if (mem.eql(u8, arg, "-rdynamic")) {
|
||||
rdynamic = true;
|
||||
} else if (strcmp(arg, "--each-lib-rpath") == 0) {
|
||||
} else if (mem.eql(u8, arg, "--each-lib-rpath")) {
|
||||
each_lib_rpath = true;
|
||||
} else if (strcmp(arg, "--enable-timing-info") == 0) {
|
||||
} else if (mem.eql(u8, arg, "--enable-timing-info")) {
|
||||
timing_info = true;
|
||||
} else if (strcmp(arg, "--test-cmd-bin") == 0) {
|
||||
test_exec_args.append(nullptr);
|
||||
} else if (arg[1] == 'L' && arg[2] != 0) {
|
||||
} else if (mem.eql(u8, arg, "--test-cmd-bin")) {
|
||||
@panic("TODO --test-cmd-bin");
|
||||
} else if (arg[1] == 'L' and arg.len > 2) {
|
||||
// alias for --library-path
|
||||
lib_dirs.append(&arg[2]);
|
||||
} else if (strcmp(arg, "--pkg-begin") == 0) {
|
||||
if (i + 2 >= argc) {
|
||||
fprintf(stderr, "Expected 2 arguments after --pkg-begin\n");
|
||||
return usage(arg0);
|
||||
}
|
||||
CliPkg *new_cur_pkg = allocate<CliPkg>(1);
|
||||
i += 1;
|
||||
new_cur_pkg->name = argv[i];
|
||||
i += 1;
|
||||
new_cur_pkg->path = argv[i];
|
||||
new_cur_pkg->parent = cur_pkg;
|
||||
cur_pkg->children.append(new_cur_pkg);
|
||||
cur_pkg = new_cur_pkg;
|
||||
} else if (strcmp(arg, "--pkg-end") == 0) {
|
||||
if (cur_pkg->parent == nullptr) {
|
||||
fprintf(stderr, "Encountered --pkg-end with no matching --pkg-begin\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
cur_pkg = cur_pkg->parent;
|
||||
} else if (i + 1 >= argc) {
|
||||
fprintf(stderr, "Expected another argument after %s\n", arg);
|
||||
return usage(arg0);
|
||||
%return lib_dirs.append(arg[1..]);
|
||||
} else if (mem.eql(u8, arg, "--pkg-begin")) {
|
||||
@panic("TODO --pkg-begin");
|
||||
} else if (mem.eql(u8, arg, "--pkg-end")) {
|
||||
@panic("TODO --pkg-end");
|
||||
} else if (arg_i + 1 >= args.len) {
|
||||
return badArgs("expected another argument after {}", arg);
|
||||
} else {
|
||||
i += 1;
|
||||
if (strcmp(arg, "--output") == 0) {
|
||||
out_file = argv[i];
|
||||
} else if (strcmp(arg, "--output-h") == 0) {
|
||||
out_file_h = argv[i];
|
||||
} else if (strcmp(arg, "--color") == 0) {
|
||||
if (strcmp(argv[i], "auto") == 0) {
|
||||
color = ErrColorAuto;
|
||||
} else if (strcmp(argv[i], "on") == 0) {
|
||||
color = ErrColorOn;
|
||||
} else if (strcmp(argv[i], "off") == 0) {
|
||||
color = ErrColorOff;
|
||||
arg_i += 1;
|
||||
if (mem.eql(u8, arg, "--output")) {
|
||||
out_file = args[arg_i];
|
||||
} else if (mem.eql(u8, arg, "--output-h")) {
|
||||
out_file_h = args[arg_i];
|
||||
} else if (mem.eql(u8, arg, "--color")) {
|
||||
if (mem.eql(u8, args[arg_i], "auto")) {
|
||||
color = ErrColor.Auto;
|
||||
} else if (mem.eql(u8, args[arg_i], "on")) {
|
||||
color = ErrColor.On;
|
||||
} else if (mem.eql(u8, args[arg_i], "off")) {
|
||||
color = ErrColor.Off;
|
||||
} else {
|
||||
fprintf(stderr, "--color options are 'auto', 'on', or 'off'\n");
|
||||
return usage(arg0);
|
||||
return badArgs("--color options are 'auto', 'on', or 'off'");
|
||||
}
|
||||
} else if (strcmp(arg, "--name") == 0) {
|
||||
out_name = argv[i];
|
||||
} else if (strcmp(arg, "--libc-lib-dir") == 0) {
|
||||
libc_lib_dir = argv[i];
|
||||
} else if (strcmp(arg, "--libc-static-lib-dir") == 0) {
|
||||
libc_static_lib_dir = argv[i];
|
||||
} else if (strcmp(arg, "--libc-include-dir") == 0) {
|
||||
libc_include_dir = argv[i];
|
||||
} else if (strcmp(arg, "--msvc-lib-dir") == 0) {
|
||||
msvc_lib_dir = argv[i];
|
||||
} else if (strcmp(arg, "--kernel32-lib-dir") == 0) {
|
||||
kernel32_lib_dir = argv[i];
|
||||
} else if (strcmp(arg, "--zig-install-prefix") == 0) {
|
||||
zig_install_prefix = argv[i];
|
||||
} else if (strcmp(arg, "--dynamic-linker") == 0) {
|
||||
dynamic_linker = argv[i];
|
||||
} else if (strcmp(arg, "-isystem") == 0) {
|
||||
clang_argv.append("-isystem");
|
||||
clang_argv.append(argv[i]);
|
||||
} else if (strcmp(arg, "-dirafter") == 0) {
|
||||
clang_argv.append("-dirafter");
|
||||
clang_argv.append(argv[i]);
|
||||
} else if (strcmp(arg, "-mllvm") == 0) {
|
||||
clang_argv.append("-mllvm");
|
||||
clang_argv.append(argv[i]);
|
||||
} else if (mem.eql(u8, arg, "--emit")) {
|
||||
if (mem.eql(u8, args[arg_i], "asm")) {
|
||||
emit_file_type = Emit.Assembly;
|
||||
} else if (mem.eql(u8, args[arg_i], "bin")) {
|
||||
emit_file_type = Emit.Binary;
|
||||
} else if (mem.eql(u8, args[arg_i], "llvm-ir")) {
|
||||
emit_file_type = Emit.LlvmIr;
|
||||
} else {
|
||||
return badArgs("--emit options are 'asm', 'bin', or 'llvm-ir'");
|
||||
}
|
||||
} else if (mem.eql(u8, arg, "--name")) {
|
||||
out_name_arg = args[arg_i];
|
||||
} else if (mem.eql(u8, arg, "--libc-lib-dir")) {
|
||||
libc_lib_dir_arg = args[arg_i];
|
||||
} else if (mem.eql(u8, arg, "--libc-static-lib-dir")) {
|
||||
libc_static_lib_dir_arg = args[arg_i];
|
||||
} else if (mem.eql(u8, arg, "--libc-include-dir")) {
|
||||
libc_include_dir_arg = args[arg_i];
|
||||
} else if (mem.eql(u8, arg, "--msvc-lib-dir")) {
|
||||
msvc_lib_dir_arg = args[arg_i];
|
||||
} else if (mem.eql(u8, arg, "--kernel32-lib-dir")) {
|
||||
kernel32_lib_dir_arg = args[arg_i];
|
||||
} else if (mem.eql(u8, arg, "--zig-install-prefix")) {
|
||||
zig_install_prefix = args[arg_i];
|
||||
} else if (mem.eql(u8, arg, "--dynamic-linker")) {
|
||||
dynamic_linker_arg = args[arg_i];
|
||||
} else if (mem.eql(u8, arg, "-isystem")) {
|
||||
%return clang_argv.append("-isystem");
|
||||
%return clang_argv.append(args[arg_i]);
|
||||
} else if (mem.eql(u8, arg, "-dirafter")) {
|
||||
%return clang_argv.append("-dirafter");
|
||||
%return clang_argv.append(args[arg_i]);
|
||||
} else if (mem.eql(u8, arg, "-mllvm")) {
|
||||
%return clang_argv.append("-mllvm");
|
||||
%return clang_argv.append(args[arg_i]);
|
||||
|
||||
llvm_argv.append(argv[i]);
|
||||
} else if (strcmp(arg, "--library-path") == 0 || strcmp(arg, "-L") == 0) {
|
||||
lib_dirs.append(argv[i]);
|
||||
} else if (strcmp(arg, "--library") == 0) {
|
||||
link_libs.append(argv[i]);
|
||||
} else if (strcmp(arg, "--object") == 0) {
|
||||
objects.append(argv[i]);
|
||||
} else if (strcmp(arg, "--assembly") == 0) {
|
||||
asm_files.append(argv[i]);
|
||||
} else if (strcmp(arg, "--cache-dir") == 0) {
|
||||
cache_dir = argv[i];
|
||||
} else if (strcmp(arg, "--target-arch") == 0) {
|
||||
target_arch = argv[i];
|
||||
} else if (strcmp(arg, "--target-os") == 0) {
|
||||
target_os = argv[i];
|
||||
} else if (strcmp(arg, "--target-environ") == 0) {
|
||||
target_environ = argv[i];
|
||||
} else if (strcmp(arg, "-mmacosx-version-min") == 0) {
|
||||
mmacosx_version_min = argv[i];
|
||||
} else if (strcmp(arg, "-mios-version-min") == 0) {
|
||||
mios_version_min = argv[i];
|
||||
} else if (strcmp(arg, "-framework") == 0) {
|
||||
frameworks.append(argv[i]);
|
||||
} else if (strcmp(arg, "--linker-script") == 0) {
|
||||
linker_script = argv[i];
|
||||
} else if (strcmp(arg, "-rpath") == 0) {
|
||||
rpath_list.append(argv[i]);
|
||||
} else if (strcmp(arg, "--test-filter") == 0) {
|
||||
test_filter = argv[i];
|
||||
} else if (strcmp(arg, "--test-name-prefix") == 0) {
|
||||
test_name_prefix = argv[i];
|
||||
} else if (strcmp(arg, "--ver-major") == 0) {
|
||||
ver_major = atoi(argv[i]);
|
||||
} else if (strcmp(arg, "--ver-minor") == 0) {
|
||||
ver_minor = atoi(argv[i]);
|
||||
} else if (strcmp(arg, "--ver-patch") == 0) {
|
||||
ver_patch = atoi(argv[i]);
|
||||
} else if (strcmp(arg, "--test-cmd") == 0) {
|
||||
test_exec_args.append(argv[i]);
|
||||
%return llvm_argv.append(args[arg_i]);
|
||||
} else if (mem.eql(u8, arg, "--library-path") or mem.eql(u8, arg, "-L")) {
|
||||
%return lib_dirs.append(args[arg_i]);
|
||||
} else if (mem.eql(u8, arg, "--library")) {
|
||||
%return link_libs.append(args[arg_i]);
|
||||
} else if (mem.eql(u8, arg, "--object")) {
|
||||
%return objects.append(args[arg_i]);
|
||||
} else if (mem.eql(u8, arg, "--assembly")) {
|
||||
%return asm_files.append(args[arg_i]);
|
||||
} else if (mem.eql(u8, arg, "--cache-dir")) {
|
||||
cache_dir_arg = args[arg_i];
|
||||
} else if (mem.eql(u8, arg, "--target-arch")) {
|
||||
target_arch = args[arg_i];
|
||||
} else if (mem.eql(u8, arg, "--target-os")) {
|
||||
target_os = args[arg_i];
|
||||
} else if (mem.eql(u8, arg, "--target-environ")) {
|
||||
target_environ = args[arg_i];
|
||||
} else if (mem.eql(u8, arg, "-mmacosx-version-min")) {
|
||||
mmacosx_version_min = args[arg_i];
|
||||
} else if (mem.eql(u8, arg, "-mios-version-min")) {
|
||||
mios_version_min = args[arg_i];
|
||||
} else if (mem.eql(u8, arg, "-framework")) {
|
||||
%return frameworks.append(args[arg_i]);
|
||||
} else if (mem.eql(u8, arg, "--linker-script")) {
|
||||
linker_script_arg = args[arg_i];
|
||||
} else if (mem.eql(u8, arg, "-rpath")) {
|
||||
%return rpath_list.append(args[arg_i]);
|
||||
} else if (mem.eql(u8, arg, "--test-filter")) {
|
||||
%return test_filters.append(args[arg_i]);
|
||||
} else if (mem.eql(u8, arg, "--test-name-prefix")) {
|
||||
test_name_prefix_arg = args[arg_i];
|
||||
} else if (mem.eql(u8, arg, "--ver-major")) {
|
||||
ver_major = %return std.fmt.parseUnsigned(u32, args[arg_i], 10);
|
||||
} else if (mem.eql(u8, arg, "--ver-minor")) {
|
||||
ver_minor = %return std.fmt.parseUnsigned(u32, args[arg_i], 10);
|
||||
} else if (mem.eql(u8, arg, "--ver-patch")) {
|
||||
ver_patch = %return std.fmt.parseUnsigned(u32, args[arg_i], 10);
|
||||
} else if (mem.eql(u8, arg, "--test-cmd")) {
|
||||
@panic("TODO --test-cmd");
|
||||
} else {
|
||||
fprintf(stderr, "Invalid argument: %s\n", arg);
|
||||
return usage(arg0);
|
||||
return badArgs("invalid argument: {}", arg);
|
||||
}
|
||||
}
|
||||
} else if (cmd == Cmd.None) {
|
||||
if (mem.eql(u8, arg, "build-obj")) {
|
||||
cmd = Cmd.Build;
|
||||
build_kind = Module.Kind.Obj;
|
||||
} else if (mem.eql(u8, arg, "build-exe")) {
|
||||
cmd = Cmd.Build;
|
||||
build_kind = Module.Kind.Exe;
|
||||
} else if (mem.eql(u8, arg, "build-lib")) {
|
||||
cmd = Cmd.Build;
|
||||
build_kind = Module.Kind.Lib;
|
||||
} else if (mem.eql(u8, arg, "version")) {
|
||||
cmd = Cmd.Version;
|
||||
} else if (mem.eql(u8, arg, "zen")) {
|
||||
cmd = Cmd.Zen;
|
||||
} else if (mem.eql(u8, arg, "translate-c")) {
|
||||
cmd = Cmd.TranslateC;
|
||||
} else if (mem.eql(u8, arg, "test")) {
|
||||
cmd = Cmd.Test;
|
||||
build_kind = Module.Kind.Exe;
|
||||
} else {
|
||||
return badArgs("unrecognized command: {}", arg);
|
||||
}
|
||||
} else switch (cmd) {
|
||||
Cmd.Build, Cmd.TranslateC, Cmd.Test => {
|
||||
if (in_file_arg == null) {
|
||||
in_file_arg = arg;
|
||||
} else {
|
||||
return badArgs("unexpected extra parameter: {}", arg);
|
||||
}
|
||||
},
|
||||
Cmd.Version, Cmd.Zen, Cmd.Targets => {
|
||||
return badArgs("unexpected extra parameter: {}", arg);
|
||||
},
|
||||
Cmd.None => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
target.initializeAll();
|
||||
|
||||
// TODO
|
||||
// ZigTarget alloc_target;
|
||||
// ZigTarget *target;
|
||||
// if (!target_arch && !target_os && !target_environ) {
|
||||
// target = nullptr;
|
||||
// } else {
|
||||
// target = &alloc_target;
|
||||
// get_unknown_target(target);
|
||||
// if (target_arch) {
|
||||
// if (parse_target_arch(target_arch, &target->arch)) {
|
||||
// fprintf(stderr, "invalid --target-arch argument\n");
|
||||
// return usage(arg0);
|
||||
// }
|
||||
// }
|
||||
// if (target_os) {
|
||||
// if (parse_target_os(target_os, &target->os)) {
|
||||
// fprintf(stderr, "invalid --target-os argument\n");
|
||||
// return usage(arg0);
|
||||
// }
|
||||
// }
|
||||
// if (target_environ) {
|
||||
// if (parse_target_environ(target_environ, &target->env_type)) {
|
||||
// fprintf(stderr, "invalid --target-environ argument\n");
|
||||
// return usage(arg0);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
switch (cmd) {
|
||||
Cmd.None => return badArgs("expected command"),
|
||||
Cmd.Zen => return printZen(),
|
||||
Cmd.Build, Cmd.Test, Cmd.TranslateC => {
|
||||
if (cmd == Cmd.Build and in_file_arg == null and objects.len == 0 and asm_files.len == 0) {
|
||||
return badArgs("expected source file argument or at least one --object or --assembly argument");
|
||||
} else if ((cmd == Cmd.TranslateC or cmd == Cmd.Test) and in_file_arg == null) {
|
||||
return badArgs("expected source file argument");
|
||||
} else if (cmd == Cmd.Build and build_kind == Module.Kind.Obj and objects.len != 0) {
|
||||
return badArgs("When building an object file, --object arguments are invalid");
|
||||
}
|
||||
|
||||
const root_name = switch (cmd) {
|
||||
Cmd.Build, Cmd.TranslateC => x: {
|
||||
if (out_name_arg) |out_name| {
|
||||
break :x out_name;
|
||||
} else if (in_file_arg) |in_file_path| {
|
||||
const basename = os.path.basename(in_file_path);
|
||||
var it = mem.split(basename, ".");
|
||||
break :x it.next() ?? return badArgs("file name cannot be empty");
|
||||
} else {
|
||||
return badArgs("--name [name] not provided and unable to infer");
|
||||
}
|
||||
},
|
||||
Cmd.Test => "test",
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
const zig_root_source_file = if (cmd == Cmd.TranslateC) null else in_file_arg;
|
||||
|
||||
const chosen_cache_dir = cache_dir_arg ?? default_zig_cache_name;
|
||||
const full_cache_dir = %return os.path.resolve(allocator, ".", chosen_cache_dir);
|
||||
defer allocator.free(full_cache_dir);
|
||||
|
||||
const zig_lib_dir = %return resolveZigLibDir(allocator, zig_install_prefix);
|
||||
%defer allocator.free(zig_lib_dir);
|
||||
|
||||
const module = %return Module.create(allocator, root_name, zig_root_source_file,
|
||||
Target.Native, build_kind, build_mode, zig_lib_dir, full_cache_dir);
|
||||
defer module.destroy();
|
||||
|
||||
module.version_major = ver_major;
|
||||
module.version_minor = ver_minor;
|
||||
module.version_patch = ver_patch;
|
||||
|
||||
module.is_test = cmd == Cmd.Test;
|
||||
if (linker_script_arg) |linker_script| {
|
||||
module.linker_script = linker_script;
|
||||
}
|
||||
module.each_lib_rpath = each_lib_rpath;
|
||||
module.clang_argv = clang_argv.toSliceConst();
|
||||
module.llvm_argv = llvm_argv.toSliceConst();
|
||||
module.strip = strip;
|
||||
module.is_static = is_static;
|
||||
|
||||
if (libc_lib_dir_arg) |libc_lib_dir| {
|
||||
module.libc_lib_dir = libc_lib_dir;
|
||||
}
|
||||
if (libc_static_lib_dir_arg) |libc_static_lib_dir| {
|
||||
module.libc_static_lib_dir = libc_static_lib_dir;
|
||||
}
|
||||
if (libc_include_dir_arg) |libc_include_dir| {
|
||||
module.libc_include_dir = libc_include_dir;
|
||||
}
|
||||
if (msvc_lib_dir_arg) |msvc_lib_dir| {
|
||||
module.msvc_lib_dir = msvc_lib_dir;
|
||||
}
|
||||
if (kernel32_lib_dir_arg) |kernel32_lib_dir| {
|
||||
module.kernel32_lib_dir = kernel32_lib_dir;
|
||||
}
|
||||
if (dynamic_linker_arg) |dynamic_linker| {
|
||||
module.dynamic_linker = dynamic_linker;
|
||||
}
|
||||
module.verbose_tokenize = verbose_tokenize;
|
||||
module.verbose_ast_tree = verbose_ast_tree;
|
||||
module.verbose_ast_fmt = verbose_ast_fmt;
|
||||
module.verbose_link = verbose_link;
|
||||
module.verbose_ir = verbose_ir;
|
||||
module.verbose_llvm_ir = verbose_llvm_ir;
|
||||
module.verbose_cimport = verbose_cimport;
|
||||
|
||||
module.err_color = color;
|
||||
|
||||
module.lib_dirs = lib_dirs.toSliceConst();
|
||||
module.darwin_frameworks = frameworks.toSliceConst();
|
||||
module.rpath_list = rpath_list.toSliceConst();
|
||||
|
||||
for (link_libs.toSliceConst()) |name| {
|
||||
_ = %return module.addLinkLib(name, true);
|
||||
}
|
||||
|
||||
module.windows_subsystem_windows = mwindows;
|
||||
module.windows_subsystem_console = mconsole;
|
||||
module.linker_rdynamic = rdynamic;
|
||||
|
||||
if (mmacosx_version_min != null and mios_version_min != null) {
|
||||
return badArgs("-mmacosx-version-min and -mios-version-min options not allowed together");
|
||||
}
|
||||
|
||||
if (mmacosx_version_min) |ver| {
|
||||
module.darwin_version_min = Module.DarwinVersionMin { .MacOS = ver };
|
||||
} else if (mios_version_min) |ver| {
|
||||
module.darwin_version_min = Module.DarwinVersionMin { .Ios = ver };
|
||||
}
|
||||
|
||||
module.test_filters = test_filters.toSliceConst();
|
||||
module.test_name_prefix = test_name_prefix_arg;
|
||||
module.out_h_path = out_file_h;
|
||||
|
||||
// TODO
|
||||
//add_package(g, cur_pkg, g->root_package);
|
||||
|
||||
switch (cmd) {
|
||||
Cmd.Build => {
|
||||
module.emit_file_type = emit_file_type;
|
||||
|
||||
module.link_objects = objects.toSliceConst();
|
||||
module.assembly_files = asm_files.toSliceConst();
|
||||
|
||||
%return module.build();
|
||||
%return module.link(out_file);
|
||||
},
|
||||
Cmd.TranslateC => @panic("TODO translate-c"),
|
||||
Cmd.Test => @panic("TODO test cmd"),
|
||||
else => unreachable,
|
||||
}
|
||||
},
|
||||
Cmd.Version => @panic("TODO zig version"),
|
||||
Cmd.Targets => @panic("TODO zig targets"),
|
||||
}
|
||||
}
|
||||
|
||||
fn printUsage(outstream: &io.OutStream) -> %void {
|
||||
%return outstream.print("Usage: {} [command] [options]\n", arg0);
|
||||
%return outstream.write(
|
||||
fn printUsage(stream: &io.OutStream) -> %void {
|
||||
%return stream.write(
|
||||
\\Usage: zig [command] [options]
|
||||
\\
|
||||
\\Commands:
|
||||
\\ build build project from build.zig
|
||||
\\ build-exe [source] create executable from source or object files
|
||||
@ -217,6 +485,7 @@ fn printUsage(outstream: &io.OutStream) -> %void {
|
||||
\\ --assembly [source] add assembly file to build
|
||||
\\ --cache-dir [path] override the cache directory
|
||||
\\ --color [auto|off|on] enable or disable colored error messages
|
||||
\\ --emit [filetype] emit a specific file format as compilation output
|
||||
\\ --enable-timing-info print timing diagnostics
|
||||
\\ --libc-include-dir [path] directory where libc stdlib.h resides
|
||||
\\ --name [name] override output name
|
||||
@ -231,9 +500,13 @@ fn printUsage(outstream: &io.OutStream) -> %void {
|
||||
\\ --target-arch [name] specify target architecture
|
||||
\\ --target-environ [name] specify target environment
|
||||
\\ --target-os [name] specify target operating system
|
||||
\\ --verbose turn on compiler debug output
|
||||
\\ --verbose-link turn on compiler debug output for linking only
|
||||
\\ --verbose-ir turn on compiler debug output for IR only
|
||||
\\ --verbose-tokenize enable compiler debug info: tokenization
|
||||
\\ --verbose-ast-tree enable compiler debug info: parsing into an AST (treeview)
|
||||
\\ --verbose-ast-fmt enable compiler debug info: parsing into an AST (render source)
|
||||
\\ --verbose-cimport enable compiler debug info: C imports
|
||||
\\ --verbose-ir enable compiler debug info: Zig IR
|
||||
\\ --verbose-llvm-ir enable compiler debug info: LLVM IR
|
||||
\\ --verbose-link enable compiler debug info: linking
|
||||
\\ --zig-install-prefix [path] override directory where zig thinks it is installed
|
||||
\\ -dirafter [dir] same as -isystem but do it last
|
||||
\\ -isystem [dir] add additional search path for other .h files
|
||||
@ -255,7 +528,6 @@ fn printUsage(outstream: &io.OutStream) -> %void {
|
||||
\\ -rpath [path] add directory to the runtime library search path
|
||||
\\ -mconsole (windows) --subsystem console to the linker
|
||||
\\ -mwindows (windows) --subsystem windows to the linker
|
||||
\\ -municode (windows) link with unicode
|
||||
\\ -framework [name] (darwin) link against framework
|
||||
\\ -mios-version-min [ver] (darwin) set iOS deployment target
|
||||
\\ -mmacosx-version-min [ver] (darwin) set Mac OS X deployment target
|
||||
@ -271,17 +543,81 @@ fn printUsage(outstream: &io.OutStream) -> %void {
|
||||
);
|
||||
}
|
||||
|
||||
const ZIG_ZEN =
|
||||
\\ * Communicate intent precisely.
|
||||
\\ * Edge cases matter.
|
||||
\\ * Favor reading code over writing code.
|
||||
\\ * Only one obvious way to do things.
|
||||
\\ * Runtime crashes are better than bugs.
|
||||
\\ * Compile errors are better than runtime crashes.
|
||||
\\ * Incremental improvements.
|
||||
\\ * Avoid local maximums.
|
||||
\\ * Reduce the amount one must remember.
|
||||
\\ * Minimize energy spent on coding style.
|
||||
\\ * Together we serve end users.
|
||||
\\
|
||||
;
|
||||
fn printZen() -> %void {
|
||||
var stdout_file = %return io.getStdErr();
|
||||
%return stdout_file.write(
|
||||
\\
|
||||
\\ * Communicate intent precisely.
|
||||
\\ * Edge cases matter.
|
||||
\\ * Favor reading code over writing code.
|
||||
\\ * Only one obvious way to do things.
|
||||
\\ * Runtime crashes are better than bugs.
|
||||
\\ * Compile errors are better than runtime crashes.
|
||||
\\ * Incremental improvements.
|
||||
\\ * Avoid local maximums.
|
||||
\\ * Reduce the amount one must remember.
|
||||
\\ * Minimize energy spent on coding style.
|
||||
\\ * Together we serve end users.
|
||||
\\
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
||||
/// Caller must free result
|
||||
fn resolveZigLibDir(allocator: &mem.Allocator, zig_install_prefix_arg: ?[]const u8) -> %[]u8 {
|
||||
if (zig_install_prefix_arg) |zig_install_prefix| {
|
||||
return testZigInstallPrefix(allocator, zig_install_prefix) %% |err| {
|
||||
warn("No Zig installation found at prefix {}: {}\n", zig_install_prefix_arg, @errorName(err));
|
||||
return error.ZigInstallationNotFound;
|
||||
};
|
||||
} else {
|
||||
return findZigLibDir(allocator) %% |err| {
|
||||
warn("Unable to find zig lib directory: {}.\nReinstall Zig or use --zig-install-prefix.\n",
|
||||
@errorName(err));
|
||||
return error.ZigLibDirNotFound;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Caller must free result
|
||||
fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) -> %[]u8 {
|
||||
const test_zig_dir = %return os.path.join(allocator, test_path, "lib", "zig");
|
||||
%defer allocator.free(test_zig_dir);
|
||||
|
||||
const test_index_file = %return os.path.join(allocator, test_zig_dir, "std", "index.zig");
|
||||
defer allocator.free(test_index_file);
|
||||
|
||||
var file = %return io.File.openRead(test_index_file, allocator);
|
||||
file.close();
|
||||
|
||||
return test_zig_dir;
|
||||
}
|
||||
|
||||
/// Caller must free result
|
||||
fn findZigLibDir(allocator: &mem.Allocator) -> %[]u8 {
|
||||
const self_exe_path = %return os.selfExeDirPath(allocator);
|
||||
defer allocator.free(self_exe_path);
|
||||
|
||||
var cur_path: []const u8 = self_exe_path;
|
||||
while (true) {
|
||||
const test_dir = os.path.dirname(cur_path);
|
||||
|
||||
if (mem.eql(u8, test_dir, cur_path)) {
|
||||
break;
|
||||
}
|
||||
|
||||
return testZigInstallPrefix(allocator, test_dir) %% |err| {
|
||||
cur_path = test_dir;
|
||||
continue;
|
||||
};
|
||||
}
|
||||
|
||||
// TODO look in hard coded installation path from configuration
|
||||
//if (ZIG_INSTALL_PREFIX != nullptr) {
|
||||
// if (test_zig_install_prefix(buf_create_from_str(ZIG_INSTALL_PREFIX), out_path)) {
|
||||
// return 0;
|
||||
// }
|
||||
//}
|
||||
|
||||
return error.FileNotFound;
|
||||
}
|
||||
|
||||
295
src-self-hosted/module.zig
Normal file
295
src-self-hosted/module.zig
Normal file
@ -0,0 +1,295 @@
|
||||
const std = @import("std");
|
||||
const os = std.os;
|
||||
const io = std.io;
|
||||
const mem = std.mem;
|
||||
const Buffer = std.Buffer;
|
||||
const llvm = @import("llvm.zig");
|
||||
const c = @import("c.zig");
|
||||
const builtin = @import("builtin");
|
||||
const Target = @import("target.zig").Target;
|
||||
const warn = std.debug.warn;
|
||||
const Tokenizer = @import("tokenizer.zig").Tokenizer;
|
||||
const Token = @import("tokenizer.zig").Token;
|
||||
const Parser = @import("parser.zig").Parser;
|
||||
const ArrayList = std.ArrayList;
|
||||
|
||||
pub const Module = struct {
|
||||
allocator: &mem.Allocator,
|
||||
name: Buffer,
|
||||
root_src_path: ?[]const u8,
|
||||
module: llvm.ModuleRef,
|
||||
context: llvm.ContextRef,
|
||||
builder: llvm.BuilderRef,
|
||||
target: Target,
|
||||
build_mode: builtin.Mode,
|
||||
zig_lib_dir: []const u8,
|
||||
|
||||
version_major: u32,
|
||||
version_minor: u32,
|
||||
version_patch: u32,
|
||||
|
||||
linker_script: ?[]const u8,
|
||||
cache_dir: []const u8,
|
||||
libc_lib_dir: ?[]const u8,
|
||||
libc_static_lib_dir: ?[]const u8,
|
||||
libc_include_dir: ?[]const u8,
|
||||
msvc_lib_dir: ?[]const u8,
|
||||
kernel32_lib_dir: ?[]const u8,
|
||||
dynamic_linker: ?[]const u8,
|
||||
out_h_path: ?[]const u8,
|
||||
|
||||
is_test: bool,
|
||||
each_lib_rpath: bool,
|
||||
strip: bool,
|
||||
is_static: bool,
|
||||
linker_rdynamic: bool,
|
||||
|
||||
clang_argv: []const []const u8,
|
||||
llvm_argv: []const []const u8,
|
||||
lib_dirs: []const []const u8,
|
||||
rpath_list: []const []const u8,
|
||||
assembly_files: []const []const u8,
|
||||
link_objects: []const []const u8,
|
||||
|
||||
windows_subsystem_windows: bool,
|
||||
windows_subsystem_console: bool,
|
||||
|
||||
link_libs_list: ArrayList(&LinkLib),
|
||||
libc_link_lib: ?&LinkLib,
|
||||
|
||||
err_color: ErrColor,
|
||||
|
||||
verbose_tokenize: bool,
|
||||
verbose_ast_tree: bool,
|
||||
verbose_ast_fmt: bool,
|
||||
verbose_cimport: bool,
|
||||
verbose_ir: bool,
|
||||
verbose_llvm_ir: bool,
|
||||
verbose_link: bool,
|
||||
|
||||
darwin_frameworks: []const []const u8,
|
||||
darwin_version_min: DarwinVersionMin,
|
||||
|
||||
test_filters: []const []const u8,
|
||||
test_name_prefix: ?[]const u8,
|
||||
|
||||
emit_file_type: Emit,
|
||||
|
||||
kind: Kind,
|
||||
|
||||
pub const DarwinVersionMin = union(enum) {
|
||||
None,
|
||||
MacOS: []const u8,
|
||||
Ios: []const u8,
|
||||
};
|
||||
|
||||
pub const Kind = enum {
|
||||
Exe,
|
||||
Lib,
|
||||
Obj,
|
||||
};
|
||||
|
||||
pub const ErrColor = enum {
|
||||
Auto,
|
||||
Off,
|
||||
On,
|
||||
};
|
||||
|
||||
pub const LinkLib = struct {
|
||||
name: []const u8,
|
||||
path: ?[]const u8,
|
||||
/// the list of symbols we depend on from this lib
|
||||
symbols: ArrayList([]u8),
|
||||
provided_explicitly: bool,
|
||||
};
|
||||
|
||||
pub const Emit = enum {
|
||||
Binary,
|
||||
Assembly,
|
||||
LlvmIr,
|
||||
};
|
||||
|
||||
pub fn create(allocator: &mem.Allocator, name: []const u8, root_src_path: ?[]const u8, target: &const Target,
|
||||
kind: Kind, build_mode: builtin.Mode, zig_lib_dir: []const u8, cache_dir: []const u8) -> %&Module
|
||||
{
|
||||
var name_buffer = %return Buffer.init(allocator, name);
|
||||
%defer name_buffer.deinit();
|
||||
|
||||
const context = c.LLVMContextCreate() ?? return error.OutOfMemory;
|
||||
%defer c.LLVMContextDispose(context);
|
||||
|
||||
const module = c.LLVMModuleCreateWithNameInContext(name_buffer.ptr(), context) ?? return error.OutOfMemory;
|
||||
%defer c.LLVMDisposeModule(module);
|
||||
|
||||
const builder = c.LLVMCreateBuilderInContext(context) ?? return error.OutOfMemory;
|
||||
%defer c.LLVMDisposeBuilder(builder);
|
||||
|
||||
const module_ptr = %return allocator.create(Module);
|
||||
%defer allocator.destroy(module_ptr);
|
||||
|
||||
*module_ptr = Module {
|
||||
.allocator = allocator,
|
||||
.name = name_buffer,
|
||||
.root_src_path = root_src_path,
|
||||
.module = module,
|
||||
.context = context,
|
||||
.builder = builder,
|
||||
.target = *target,
|
||||
.kind = kind,
|
||||
.build_mode = build_mode,
|
||||
.zig_lib_dir = zig_lib_dir,
|
||||
.cache_dir = cache_dir,
|
||||
|
||||
.version_major = 0,
|
||||
.version_minor = 0,
|
||||
.version_patch = 0,
|
||||
|
||||
.verbose_tokenize = false,
|
||||
.verbose_ast_tree = false,
|
||||
.verbose_ast_fmt = false,
|
||||
.verbose_cimport = false,
|
||||
.verbose_ir = false,
|
||||
.verbose_llvm_ir = false,
|
||||
.verbose_link = false,
|
||||
|
||||
.linker_script = null,
|
||||
.libc_lib_dir = null,
|
||||
.libc_static_lib_dir = null,
|
||||
.libc_include_dir = null,
|
||||
.msvc_lib_dir = null,
|
||||
.kernel32_lib_dir = null,
|
||||
.dynamic_linker = null,
|
||||
.out_h_path = null,
|
||||
.is_test = false,
|
||||
.each_lib_rpath = false,
|
||||
.strip = false,
|
||||
.is_static = false,
|
||||
.linker_rdynamic = false,
|
||||
.clang_argv = [][]const u8{},
|
||||
.llvm_argv = [][]const u8{},
|
||||
.lib_dirs = [][]const u8{},
|
||||
.rpath_list = [][]const u8{},
|
||||
.assembly_files = [][]const u8{},
|
||||
.link_objects = [][]const u8{},
|
||||
.windows_subsystem_windows = false,
|
||||
.windows_subsystem_console = false,
|
||||
.link_libs_list = ArrayList(&LinkLib).init(allocator),
|
||||
.libc_link_lib = null,
|
||||
.err_color = ErrColor.Auto,
|
||||
.darwin_frameworks = [][]const u8{},
|
||||
.darwin_version_min = DarwinVersionMin.None,
|
||||
.test_filters = [][]const u8{},
|
||||
.test_name_prefix = null,
|
||||
.emit_file_type = Emit.Binary,
|
||||
};
|
||||
return module_ptr;
|
||||
}
|
||||
|
||||
fn dump(self: &Module) {
|
||||
c.LLVMDumpModule(self.module);
|
||||
}
|
||||
|
||||
pub fn destroy(self: &Module) {
|
||||
c.LLVMDisposeBuilder(self.builder);
|
||||
c.LLVMDisposeModule(self.module);
|
||||
c.LLVMContextDispose(self.context);
|
||||
self.name.deinit();
|
||||
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
|
||||
pub fn build(self: &Module) -> %void {
|
||||
const root_src_path = self.root_src_path ?? @panic("TODO handle null root src path");
|
||||
const root_src_real_path = os.path.real(self.allocator, root_src_path) %% |err| {
|
||||
%return printError("unable to open '{}': {}", root_src_path, err);
|
||||
return err;
|
||||
};
|
||||
%defer self.allocator.free(root_src_real_path);
|
||||
|
||||
const source_code = io.readFileAlloc(root_src_real_path, self.allocator) %% |err| {
|
||||
%return printError("unable to open '{}': {}", root_src_real_path, err);
|
||||
return err;
|
||||
};
|
||||
%defer self.allocator.free(source_code);
|
||||
|
||||
warn("====input:====\n");
|
||||
|
||||
warn("{}", source_code);
|
||||
|
||||
warn("====tokenization:====\n");
|
||||
{
|
||||
var tokenizer = Tokenizer.init(source_code);
|
||||
while (true) {
|
||||
const token = tokenizer.next();
|
||||
tokenizer.dump(token);
|
||||
if (token.id == Token.Id.Eof) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
warn("====parse:====\n");
|
||||
|
||||
var tokenizer = Tokenizer.init(source_code);
|
||||
var parser = Parser.init(&tokenizer, self.allocator, root_src_real_path);
|
||||
defer parser.deinit();
|
||||
|
||||
const root_node = %return parser.parse();
|
||||
defer parser.freeAst(root_node);
|
||||
|
||||
var stderr_file = %return std.io.getStdErr();
|
||||
var stderr_file_out_stream = std.io.FileOutStream.init(&stderr_file);
|
||||
const out_stream = &stderr_file_out_stream.stream;
|
||||
%return parser.renderAst(out_stream, root_node);
|
||||
|
||||
warn("====fmt:====\n");
|
||||
%return parser.renderSource(out_stream, root_node);
|
||||
|
||||
warn("====ir:====\n");
|
||||
warn("TODO\n\n");
|
||||
|
||||
warn("====llvm ir:====\n");
|
||||
self.dump();
|
||||
|
||||
}
|
||||
|
||||
pub fn link(self: &Module, out_file: ?[]const u8) -> %void {
|
||||
warn("TODO link");
|
||||
}
|
||||
|
||||
pub fn addLinkLib(self: &Module, name: []const u8, provided_explicitly: bool) -> %&LinkLib {
|
||||
const is_libc = mem.eql(u8, name, "c");
|
||||
|
||||
if (is_libc) {
|
||||
if (self.libc_link_lib) |libc_link_lib| {
|
||||
return libc_link_lib;
|
||||
}
|
||||
}
|
||||
|
||||
for (self.link_libs_list.toSliceConst()) |existing_lib| {
|
||||
if (mem.eql(u8, name, existing_lib.name)) {
|
||||
return existing_lib;
|
||||
}
|
||||
}
|
||||
|
||||
const link_lib = %return self.allocator.create(LinkLib);
|
||||
*link_lib = LinkLib {
|
||||
.name = name,
|
||||
.path = null,
|
||||
.provided_explicitly = provided_explicitly,
|
||||
.symbols = ArrayList([]u8).init(self.allocator),
|
||||
};
|
||||
%return self.link_libs_list.append(link_lib);
|
||||
if (is_libc) {
|
||||
self.libc_link_lib = link_lib;
|
||||
}
|
||||
return link_lib;
|
||||
}
|
||||
};
|
||||
|
||||
fn printError(comptime format: []const u8, args: ...) -> %void {
|
||||
var stderr_file = %return std.io.getStdErr();
|
||||
var stderr_file_out_stream = std.io.FileOutStream.init(&stderr_file);
|
||||
const out_stream = &stderr_file_out_stream.stream;
|
||||
%return out_stream.print(format, args);
|
||||
}
|
||||
1203
src-self-hosted/parser.zig
Normal file
1203
src-self-hosted/parser.zig
Normal file
File diff suppressed because it is too large
Load Diff
60
src-self-hosted/target.zig
Normal file
60
src-self-hosted/target.zig
Normal file
@ -0,0 +1,60 @@
|
||||
const builtin = @import("builtin");
|
||||
const c = @import("c.zig");
|
||||
|
||||
pub const CrossTarget = struct {
|
||||
arch: builtin.Arch,
|
||||
os: builtin.Os,
|
||||
environ: builtin.Environ,
|
||||
};
|
||||
|
||||
pub const Target = union(enum) {
|
||||
Native,
|
||||
Cross: CrossTarget,
|
||||
|
||||
pub fn oFileExt(self: &const Target) -> []const u8 {
|
||||
const environ = switch (*self) {
|
||||
Target.Native => builtin.environ,
|
||||
Target.Cross => |t| t.environ,
|
||||
};
|
||||
return switch (environ) {
|
||||
builtin.Environ.msvc => ".obj",
|
||||
else => ".o",
|
||||
};
|
||||
}
|
||||
|
||||
pub fn exeFileExt(self: &const Target) -> []const u8 {
|
||||
return switch (self.getOs()) {
|
||||
builtin.Os.windows => ".exe",
|
||||
else => "",
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getOs(self: &const Target) -> builtin.Os {
|
||||
return switch (*self) {
|
||||
Target.Native => builtin.os,
|
||||
Target.Cross => |t| t.os,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isDarwin(self: &const Target) -> bool {
|
||||
return switch (self.getOs()) {
|
||||
builtin.Os.darwin, builtin.Os.ios, builtin.Os.macosx => true,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isWindows(self: &const Target) -> bool {
|
||||
return switch (self.getOs()) {
|
||||
builtin.Os.windows => true,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub fn initializeAll() {
|
||||
c.LLVMInitializeAllTargets();
|
||||
c.LLVMInitializeAllTargetInfos();
|
||||
c.LLVMInitializeAllTargetMCs();
|
||||
c.LLVMInitializeAllAsmPrinters();
|
||||
c.LLVMInitializeAllAsmParsers();
|
||||
}
|
||||
509
src-self-hosted/tokenizer.zig
Normal file
509
src-self-hosted/tokenizer.zig
Normal file
@ -0,0 +1,509 @@
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
|
||||
pub const Token = struct {
|
||||
id: Id,
|
||||
start: usize,
|
||||
end: usize,
|
||||
|
||||
const KeywordId = struct {
|
||||
bytes: []const u8,
|
||||
id: Id,
|
||||
};
|
||||
|
||||
const keywords = []KeywordId {
|
||||
KeywordId{.bytes="align", .id = Id.Keyword_align},
|
||||
KeywordId{.bytes="and", .id = Id.Keyword_and},
|
||||
KeywordId{.bytes="asm", .id = Id.Keyword_asm},
|
||||
KeywordId{.bytes="break", .id = Id.Keyword_break},
|
||||
KeywordId{.bytes="coldcc", .id = Id.Keyword_coldcc},
|
||||
KeywordId{.bytes="comptime", .id = Id.Keyword_comptime},
|
||||
KeywordId{.bytes="const", .id = Id.Keyword_const},
|
||||
KeywordId{.bytes="continue", .id = Id.Keyword_continue},
|
||||
KeywordId{.bytes="defer", .id = Id.Keyword_defer},
|
||||
KeywordId{.bytes="else", .id = Id.Keyword_else},
|
||||
KeywordId{.bytes="enum", .id = Id.Keyword_enum},
|
||||
KeywordId{.bytes="error", .id = Id.Keyword_error},
|
||||
KeywordId{.bytes="export", .id = Id.Keyword_export},
|
||||
KeywordId{.bytes="extern", .id = Id.Keyword_extern},
|
||||
KeywordId{.bytes="false", .id = Id.Keyword_false},
|
||||
KeywordId{.bytes="fn", .id = Id.Keyword_fn},
|
||||
KeywordId{.bytes="for", .id = Id.Keyword_for},
|
||||
KeywordId{.bytes="goto", .id = Id.Keyword_goto},
|
||||
KeywordId{.bytes="if", .id = Id.Keyword_if},
|
||||
KeywordId{.bytes="inline", .id = Id.Keyword_inline},
|
||||
KeywordId{.bytes="nakedcc", .id = Id.Keyword_nakedcc},
|
||||
KeywordId{.bytes="noalias", .id = Id.Keyword_noalias},
|
||||
KeywordId{.bytes="null", .id = Id.Keyword_null},
|
||||
KeywordId{.bytes="or", .id = Id.Keyword_or},
|
||||
KeywordId{.bytes="packed", .id = Id.Keyword_packed},
|
||||
KeywordId{.bytes="pub", .id = Id.Keyword_pub},
|
||||
KeywordId{.bytes="return", .id = Id.Keyword_return},
|
||||
KeywordId{.bytes="stdcallcc", .id = Id.Keyword_stdcallcc},
|
||||
KeywordId{.bytes="struct", .id = Id.Keyword_struct},
|
||||
KeywordId{.bytes="switch", .id = Id.Keyword_switch},
|
||||
KeywordId{.bytes="test", .id = Id.Keyword_test},
|
||||
KeywordId{.bytes="this", .id = Id.Keyword_this},
|
||||
KeywordId{.bytes="true", .id = Id.Keyword_true},
|
||||
KeywordId{.bytes="undefined", .id = Id.Keyword_undefined},
|
||||
KeywordId{.bytes="union", .id = Id.Keyword_union},
|
||||
KeywordId{.bytes="unreachable", .id = Id.Keyword_unreachable},
|
||||
KeywordId{.bytes="use", .id = Id.Keyword_use},
|
||||
KeywordId{.bytes="var", .id = Id.Keyword_var},
|
||||
KeywordId{.bytes="volatile", .id = Id.Keyword_volatile},
|
||||
KeywordId{.bytes="while", .id = Id.Keyword_while},
|
||||
};
|
||||
|
||||
fn getKeyword(bytes: []const u8) -> ?Id {
|
||||
for (keywords) |kw| {
|
||||
if (mem.eql(u8, kw.bytes, bytes)) {
|
||||
return kw.id;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const StrLitKind = enum {Normal, C};
|
||||
|
||||
pub const Id = union(enum) {
|
||||
Invalid,
|
||||
Identifier,
|
||||
StringLiteral: StrLitKind,
|
||||
Eof,
|
||||
Builtin,
|
||||
Bang,
|
||||
Equal,
|
||||
EqualEqual,
|
||||
BangEqual,
|
||||
LParen,
|
||||
RParen,
|
||||
Semicolon,
|
||||
Percent,
|
||||
LBrace,
|
||||
RBrace,
|
||||
Period,
|
||||
Ellipsis2,
|
||||
Ellipsis3,
|
||||
Minus,
|
||||
Arrow,
|
||||
Colon,
|
||||
Slash,
|
||||
Comma,
|
||||
Ampersand,
|
||||
AmpersandEqual,
|
||||
IntegerLiteral,
|
||||
FloatLiteral,
|
||||
Keyword_align,
|
||||
Keyword_and,
|
||||
Keyword_asm,
|
||||
Keyword_break,
|
||||
Keyword_coldcc,
|
||||
Keyword_comptime,
|
||||
Keyword_const,
|
||||
Keyword_continue,
|
||||
Keyword_defer,
|
||||
Keyword_else,
|
||||
Keyword_enum,
|
||||
Keyword_error,
|
||||
Keyword_export,
|
||||
Keyword_extern,
|
||||
Keyword_false,
|
||||
Keyword_fn,
|
||||
Keyword_for,
|
||||
Keyword_goto,
|
||||
Keyword_if,
|
||||
Keyword_inline,
|
||||
Keyword_nakedcc,
|
||||
Keyword_noalias,
|
||||
Keyword_null,
|
||||
Keyword_or,
|
||||
Keyword_packed,
|
||||
Keyword_pub,
|
||||
Keyword_return,
|
||||
Keyword_stdcallcc,
|
||||
Keyword_struct,
|
||||
Keyword_switch,
|
||||
Keyword_test,
|
||||
Keyword_this,
|
||||
Keyword_true,
|
||||
Keyword_undefined,
|
||||
Keyword_union,
|
||||
Keyword_unreachable,
|
||||
Keyword_use,
|
||||
Keyword_var,
|
||||
Keyword_volatile,
|
||||
Keyword_while,
|
||||
};
|
||||
};
|
||||
|
||||
pub const Tokenizer = struct {
|
||||
buffer: []const u8,
|
||||
index: usize,
|
||||
|
||||
pub const Location = struct {
|
||||
line: usize,
|
||||
column: usize,
|
||||
line_start: usize,
|
||||
line_end: usize,
|
||||
};
|
||||
|
||||
pub fn getTokenLocation(self: &Tokenizer, token: &const Token) -> Location {
|
||||
var loc = Location {
|
||||
.line = 0,
|
||||
.column = 0,
|
||||
.line_start = 0,
|
||||
.line_end = 0,
|
||||
};
|
||||
for (self.buffer) |c, i| {
|
||||
if (i == token.start) {
|
||||
loc.line_end = i;
|
||||
while (loc.line_end < self.buffer.len and self.buffer[loc.line_end] != '\n') : (loc.line_end += 1) {}
|
||||
return loc;
|
||||
}
|
||||
if (c == '\n') {
|
||||
loc.line += 1;
|
||||
loc.column = 0;
|
||||
loc.line_start = i + 1;
|
||||
} else {
|
||||
loc.column += 1;
|
||||
}
|
||||
}
|
||||
return loc;
|
||||
}
|
||||
|
||||
/// For debugging purposes
|
||||
pub fn dump(self: &Tokenizer, token: &const Token) {
|
||||
std.debug.warn("{} \"{}\"\n", @tagName(token.id), self.buffer[token.start..token.end]);
|
||||
}
|
||||
|
||||
pub fn init(buffer: []const u8) -> Tokenizer {
|
||||
return Tokenizer {
|
||||
.buffer = buffer,
|
||||
.index = 0,
|
||||
};
|
||||
}
|
||||
|
||||
const State = enum {
|
||||
Start,
|
||||
Identifier,
|
||||
Builtin,
|
||||
C,
|
||||
StringLiteral,
|
||||
StringLiteralBackslash,
|
||||
Equal,
|
||||
Bang,
|
||||
Minus,
|
||||
Slash,
|
||||
LineComment,
|
||||
Zero,
|
||||
IntegerLiteral,
|
||||
NumberDot,
|
||||
FloatFraction,
|
||||
FloatExponentUnsigned,
|
||||
FloatExponentNumber,
|
||||
Ampersand,
|
||||
Period,
|
||||
Period2,
|
||||
};
|
||||
|
||||
pub fn next(self: &Tokenizer) -> Token {
|
||||
var state = State.Start;
|
||||
var result = Token {
|
||||
.id = Token.Id.Eof,
|
||||
.start = self.index,
|
||||
.end = undefined,
|
||||
};
|
||||
while (self.index < self.buffer.len) : (self.index += 1) {
|
||||
const c = self.buffer[self.index];
|
||||
switch (state) {
|
||||
State.Start => switch (c) {
|
||||
' ', '\n' => {
|
||||
result.start = self.index + 1;
|
||||
},
|
||||
'c' => {
|
||||
state = State.C;
|
||||
result.id = Token.Id.Identifier;
|
||||
},
|
||||
'"' => {
|
||||
state = State.StringLiteral;
|
||||
result.id = Token.Id { .StringLiteral = Token.StrLitKind.Normal };
|
||||
},
|
||||
'a'...'b', 'd'...'z', 'A'...'Z', '_' => {
|
||||
state = State.Identifier;
|
||||
result.id = Token.Id.Identifier;
|
||||
},
|
||||
'@' => {
|
||||
state = State.Builtin;
|
||||
result.id = Token.Id.Builtin;
|
||||
},
|
||||
'=' => {
|
||||
state = State.Equal;
|
||||
},
|
||||
'!' => {
|
||||
state = State.Bang;
|
||||
},
|
||||
'(' => {
|
||||
result.id = Token.Id.LParen;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
')' => {
|
||||
result.id = Token.Id.RParen;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
';' => {
|
||||
result.id = Token.Id.Semicolon;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
',' => {
|
||||
result.id = Token.Id.Comma;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
':' => {
|
||||
result.id = Token.Id.Colon;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
'%' => {
|
||||
result.id = Token.Id.Percent;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
'{' => {
|
||||
result.id = Token.Id.LBrace;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
'}' => {
|
||||
result.id = Token.Id.RBrace;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
'.' => {
|
||||
state = State.Period;
|
||||
},
|
||||
'-' => {
|
||||
state = State.Minus;
|
||||
},
|
||||
'/' => {
|
||||
state = State.Slash;
|
||||
},
|
||||
'&' => {
|
||||
state = State.Ampersand;
|
||||
},
|
||||
'0' => {
|
||||
state = State.Zero;
|
||||
result.id = Token.Id.IntegerLiteral;
|
||||
},
|
||||
'1'...'9' => {
|
||||
state = State.IntegerLiteral;
|
||||
result.id = Token.Id.IntegerLiteral;
|
||||
},
|
||||
else => {
|
||||
result.id = Token.Id.Invalid;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
},
|
||||
State.Ampersand => switch (c) {
|
||||
'=' => {
|
||||
result.id = Token.Id.AmpersandEqual;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.id = Token.Id.Ampersand;
|
||||
break;
|
||||
},
|
||||
},
|
||||
State.Identifier => switch (c) {
|
||||
'a'...'z', 'A'...'Z', '_', '0'...'9' => {},
|
||||
else => {
|
||||
if (Token.getKeyword(self.buffer[result.start..self.index])) |id| {
|
||||
result.id = id;
|
||||
}
|
||||
break;
|
||||
},
|
||||
},
|
||||
State.Builtin => switch (c) {
|
||||
'a'...'z', 'A'...'Z', '_', '0'...'9' => {},
|
||||
else => break,
|
||||
},
|
||||
State.C => switch (c) {
|
||||
'\\' => @panic("TODO"),
|
||||
'"' => {
|
||||
state = State.StringLiteral;
|
||||
result.id = Token.Id { .StringLiteral = Token.StrLitKind.C };
|
||||
},
|
||||
'a'...'z', 'A'...'Z', '_', '0'...'9' => {
|
||||
state = State.Identifier;
|
||||
},
|
||||
else => break,
|
||||
},
|
||||
State.StringLiteral => switch (c) {
|
||||
'\\' => {
|
||||
state = State.StringLiteralBackslash;
|
||||
},
|
||||
'"' => {
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
'\n' => break, // Look for this error later.
|
||||
else => {},
|
||||
},
|
||||
|
||||
State.StringLiteralBackslash => switch (c) {
|
||||
'\n' => break, // Look for this error later.
|
||||
else => {
|
||||
state = State.StringLiteral;
|
||||
},
|
||||
},
|
||||
|
||||
State.Bang => switch (c) {
|
||||
'=' => {
|
||||
result.id = Token.Id.BangEqual;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.id = Token.Id.Bang;
|
||||
break;
|
||||
},
|
||||
},
|
||||
|
||||
State.Equal => switch (c) {
|
||||
'=' => {
|
||||
result.id = Token.Id.EqualEqual;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.id = Token.Id.Equal;
|
||||
break;
|
||||
},
|
||||
},
|
||||
|
||||
State.Minus => switch (c) {
|
||||
'>' => {
|
||||
result.id = Token.Id.Arrow;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.id = Token.Id.Minus;
|
||||
break;
|
||||
},
|
||||
},
|
||||
|
||||
State.Period => switch (c) {
|
||||
'.' => {
|
||||
state = State.Period2;
|
||||
},
|
||||
else => {
|
||||
result.id = Token.Id.Period;
|
||||
break;
|
||||
},
|
||||
},
|
||||
|
||||
State.Period2 => switch (c) {
|
||||
'.' => {
|
||||
result.id = Token.Id.Ellipsis3;
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.id = Token.Id.Ellipsis2;
|
||||
break;
|
||||
},
|
||||
},
|
||||
|
||||
State.Slash => switch (c) {
|
||||
'/' => {
|
||||
result.id = undefined;
|
||||
state = State.LineComment;
|
||||
},
|
||||
else => {
|
||||
result.id = Token.Id.Slash;
|
||||
break;
|
||||
},
|
||||
},
|
||||
State.LineComment => switch (c) {
|
||||
'\n' => {
|
||||
state = State.Start;
|
||||
result = Token {
|
||||
.id = Token.Id.Eof,
|
||||
.start = self.index + 1,
|
||||
.end = undefined,
|
||||
};
|
||||
},
|
||||
else => {},
|
||||
},
|
||||
State.Zero => switch (c) {
|
||||
'b', 'o', 'x' => {
|
||||
state = State.IntegerLiteral;
|
||||
},
|
||||
else => {
|
||||
// reinterpret as a normal number
|
||||
self.index -= 1;
|
||||
state = State.IntegerLiteral;
|
||||
},
|
||||
},
|
||||
State.IntegerLiteral => switch (c) {
|
||||
'.' => {
|
||||
state = State.NumberDot;
|
||||
},
|
||||
'p', 'P', 'e', 'E' => {
|
||||
state = State.FloatExponentUnsigned;
|
||||
},
|
||||
'0'...'9', 'a'...'f', 'A'...'F' => {},
|
||||
else => break,
|
||||
},
|
||||
State.NumberDot => switch (c) {
|
||||
'.' => {
|
||||
self.index -= 1;
|
||||
state = State.Start;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
self.index -= 1;
|
||||
result.id = Token.Id.FloatLiteral;
|
||||
state = State.FloatFraction;
|
||||
},
|
||||
},
|
||||
State.FloatFraction => switch (c) {
|
||||
'p', 'P', 'e', 'E' => {
|
||||
state = State.FloatExponentUnsigned;
|
||||
},
|
||||
'0'...'9', 'a'...'f', 'A'...'F' => {},
|
||||
else => break,
|
||||
},
|
||||
State.FloatExponentUnsigned => switch (c) {
|
||||
'+', '-' => {
|
||||
state = State.FloatExponentNumber;
|
||||
},
|
||||
else => {
|
||||
// reinterpret as a normal exponent number
|
||||
self.index -= 1;
|
||||
state = State.FloatExponentNumber;
|
||||
}
|
||||
},
|
||||
State.FloatExponentNumber => switch (c) {
|
||||
'0'...'9', 'a'...'f', 'A'...'F' => {},
|
||||
else => break,
|
||||
},
|
||||
}
|
||||
}
|
||||
result.end = self.index;
|
||||
// TODO check state when returning EOF
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn getTokenSlice(self: &const Tokenizer, token: &const Token) -> []const u8 {
|
||||
return self.buffer[token.start..token.end];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -26,7 +26,6 @@ struct ScopeFnDef;
|
||||
struct TypeTableEntry;
|
||||
struct VariableTableEntry;
|
||||
struct ErrorTableEntry;
|
||||
struct LabelTableEntry;
|
||||
struct BuiltinFnEntry;
|
||||
struct TypeStructField;
|
||||
struct CodeGen;
|
||||
@ -37,6 +36,7 @@ struct IrBasicBlock;
|
||||
struct ScopeDecls;
|
||||
struct ZigWindowsSDK;
|
||||
struct Tld;
|
||||
struct TldExport;
|
||||
|
||||
struct IrGotoItem {
|
||||
AstNode *source_node;
|
||||
@ -53,7 +53,6 @@ struct IrExecutable {
|
||||
size_t *backward_branch_count;
|
||||
size_t backward_branch_quota;
|
||||
bool invalid;
|
||||
ZigList<LabelTableEntry *> all_labels;
|
||||
ZigList<IrGotoItem> goto_list;
|
||||
bool is_inline;
|
||||
FnTableEntry *fn_entry;
|
||||
@ -272,7 +271,6 @@ enum ReturnKnowledge {
|
||||
enum VisibMod {
|
||||
VisibModPrivate,
|
||||
VisibModPub,
|
||||
VisibModExport,
|
||||
};
|
||||
|
||||
enum GlobalLinkageId {
|
||||
@ -313,11 +311,8 @@ struct TldVar {
|
||||
Tld base;
|
||||
|
||||
VariableTableEntry *var;
|
||||
AstNode *set_global_section_node;
|
||||
Buf *section_name;
|
||||
AstNode *set_global_linkage_node;
|
||||
GlobalLinkageId linkage;
|
||||
Buf *extern_lib_name;
|
||||
Buf *section_name;
|
||||
};
|
||||
|
||||
struct TldFn {
|
||||
@ -389,8 +384,6 @@ enum NodeType {
|
||||
NodeTypeSwitchExpr,
|
||||
NodeTypeSwitchProng,
|
||||
NodeTypeSwitchRange,
|
||||
NodeTypeLabel,
|
||||
NodeTypeGoto,
|
||||
NodeTypeCompTime,
|
||||
NodeTypeBreak,
|
||||
NodeTypeContinue,
|
||||
@ -425,6 +418,7 @@ struct AstNodeFnProto {
|
||||
AstNode *return_type;
|
||||
bool is_var_args;
|
||||
bool is_extern;
|
||||
bool is_export;
|
||||
bool is_inline;
|
||||
CallingConvention cc;
|
||||
AstNode *fn_def_node;
|
||||
@ -432,6 +426,8 @@ struct AstNodeFnProto {
|
||||
Buf *lib_name;
|
||||
// populated if the "align A" is present
|
||||
AstNode *align_expr;
|
||||
// populated if the "section(S)" is present
|
||||
AstNode *section_expr;
|
||||
};
|
||||
|
||||
struct AstNodeFnDef {
|
||||
@ -452,8 +448,8 @@ struct AstNodeParamDecl {
|
||||
};
|
||||
|
||||
struct AstNodeBlock {
|
||||
Buf *name;
|
||||
ZigList<AstNode *> statements;
|
||||
bool last_statement_is_result_expression;
|
||||
};
|
||||
|
||||
enum ReturnKind {
|
||||
@ -480,15 +476,18 @@ struct AstNodeVariableDeclaration {
|
||||
VisibMod visib_mod;
|
||||
Buf *symbol;
|
||||
bool is_const;
|
||||
bool is_inline;
|
||||
bool is_comptime;
|
||||
bool is_export;
|
||||
bool is_extern;
|
||||
// one or both of type and expr will be non null
|
||||
AstNode *type;
|
||||
AstNode *expr;
|
||||
// populated if this is an extern declaration
|
||||
Buf *lib_name;
|
||||
// populated if the "align A" is present
|
||||
// populated if the "align(A)" is present
|
||||
AstNode *align_expr;
|
||||
// populated if the "section(S)" is present
|
||||
AstNode *section_expr;
|
||||
};
|
||||
|
||||
struct AstNodeErrorValueDecl {
|
||||
@ -659,6 +658,7 @@ struct AstNodeTestExpr {
|
||||
};
|
||||
|
||||
struct AstNodeWhileExpr {
|
||||
Buf *name;
|
||||
AstNode *condition;
|
||||
Buf *var_symbol;
|
||||
bool var_is_ptr;
|
||||
@ -670,6 +670,7 @@ struct AstNodeWhileExpr {
|
||||
};
|
||||
|
||||
struct AstNodeForExpr {
|
||||
Buf *name;
|
||||
AstNode *array_expr;
|
||||
AstNode *elem_node; // always a symbol
|
||||
AstNode *index_node; // always a symbol, might be null
|
||||
@ -701,11 +702,6 @@ struct AstNodeLabel {
|
||||
Buf *name;
|
||||
};
|
||||
|
||||
struct AstNodeGoto {
|
||||
Buf *name;
|
||||
bool is_inline;
|
||||
};
|
||||
|
||||
struct AstNodeCompTime {
|
||||
AstNode *expr;
|
||||
};
|
||||
@ -833,11 +829,14 @@ struct AstNodeBoolLiteral {
|
||||
};
|
||||
|
||||
struct AstNodeBreakExpr {
|
||||
Buf *name;
|
||||
AstNode *expr; // may be null
|
||||
};
|
||||
|
||||
struct AstNodeContinueExpr {
|
||||
Buf *name;
|
||||
};
|
||||
|
||||
struct AstNodeUnreachableExpr {
|
||||
};
|
||||
|
||||
@ -883,7 +882,6 @@ struct AstNode {
|
||||
AstNodeSwitchProng switch_prong;
|
||||
AstNodeSwitchRange switch_range;
|
||||
AstNodeLabel label;
|
||||
AstNodeGoto goto_expr;
|
||||
AstNodeCompTime comptime_expr;
|
||||
AstNodeAsmExpr asm_expr;
|
||||
AstNodeFieldAccessExpr field_access_expr;
|
||||
@ -1177,6 +1175,11 @@ enum FnInline {
|
||||
FnInlineNever,
|
||||
};
|
||||
|
||||
struct FnExport {
|
||||
Buf name;
|
||||
GlobalLinkageId linkage;
|
||||
};
|
||||
|
||||
struct FnTableEntry {
|
||||
LLVMValueRef llvm_value;
|
||||
const char *llvm_name;
|
||||
@ -1204,12 +1207,11 @@ struct FnTableEntry {
|
||||
ZigList<IrInstruction *> alloca_list;
|
||||
ZigList<VariableTableEntry *> variable_list;
|
||||
|
||||
AstNode *set_global_section_node;
|
||||
Buf *section_name;
|
||||
AstNode *set_global_linkage_node;
|
||||
GlobalLinkageId linkage;
|
||||
AstNode *set_alignstack_node;
|
||||
uint32_t alignstack_value;
|
||||
|
||||
ZigList<FnExport> export_list;
|
||||
};
|
||||
|
||||
uint32_t fn_table_entry_hash(FnTableEntry*);
|
||||
@ -1258,8 +1260,6 @@ enum BuiltinFnId {
|
||||
BuiltinFnIdSetFloatMode,
|
||||
BuiltinFnIdTypeName,
|
||||
BuiltinFnIdCanImplicitCast,
|
||||
BuiltinFnIdSetGlobalSection,
|
||||
BuiltinFnIdSetGlobalLinkage,
|
||||
BuiltinFnIdPanic,
|
||||
BuiltinFnIdPtrCast,
|
||||
BuiltinFnIdBitCast,
|
||||
@ -1270,6 +1270,7 @@ enum BuiltinFnId {
|
||||
BuiltinFnIdFieldParentPtr,
|
||||
BuiltinFnIdOffsetOf,
|
||||
BuiltinFnIdInlineCall,
|
||||
BuiltinFnIdNoInlineCall,
|
||||
BuiltinFnIdTypeId,
|
||||
BuiltinFnIdShlExact,
|
||||
BuiltinFnIdShrExact,
|
||||
@ -1278,6 +1279,7 @@ enum BuiltinFnId {
|
||||
BuiltinFnIdOpaqueType,
|
||||
BuiltinFnIdSetAlignStack,
|
||||
BuiltinFnIdArgType,
|
||||
BuiltinFnIdExport,
|
||||
};
|
||||
|
||||
struct BuiltinFnEntry {
|
||||
@ -1424,7 +1426,7 @@ struct CodeGen {
|
||||
HashMap<GenericFnTypeId *, FnTableEntry *, generic_fn_type_id_hash, generic_fn_type_id_eql> generic_table;
|
||||
HashMap<Scope *, IrInstruction *, fn_eval_hash, fn_eval_eql> memoized_fn_eval_table;
|
||||
HashMap<ZigLLVMFnKey, LLVMValueRef, zig_llvm_fn_key_hash, zig_llvm_fn_key_eql> llvm_fn_table;
|
||||
HashMap<Buf *, Tld *, buf_hash, buf_eql_buf> exported_symbol_names;
|
||||
HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> exported_symbol_names;
|
||||
HashMap<Buf *, Tld *, buf_hash, buf_eql_buf> external_prototypes;
|
||||
|
||||
|
||||
@ -1439,7 +1441,7 @@ struct CodeGen {
|
||||
|
||||
struct {
|
||||
TypeTableEntry *entry_bool;
|
||||
TypeTableEntry *entry_int[2][11]; // [signed,unsigned][2,3,4,5,6,7,8,16,32,64,128]
|
||||
TypeTableEntry *entry_int[2][12]; // [signed,unsigned][2,3,4,5,6,7,8,16,29,32,64,128]
|
||||
TypeTableEntry *entry_c_int[CIntTypeCount];
|
||||
TypeTableEntry *entry_c_longdouble;
|
||||
TypeTableEntry *entry_c_void;
|
||||
@ -1639,12 +1641,6 @@ struct ErrorTableEntry {
|
||||
ConstExprValue *cached_error_name_val;
|
||||
};
|
||||
|
||||
struct LabelTableEntry {
|
||||
AstNode *decl_node;
|
||||
IrBasicBlock *bb;
|
||||
bool used;
|
||||
};
|
||||
|
||||
enum ScopeId {
|
||||
ScopeIdDecls,
|
||||
ScopeIdBlock,
|
||||
@ -1688,7 +1684,12 @@ struct ScopeDecls {
|
||||
struct ScopeBlock {
|
||||
Scope base;
|
||||
|
||||
HashMap<Buf *, LabelTableEntry *, buf_hash, buf_eql_buf> label_table;
|
||||
Buf *name;
|
||||
IrBasicBlock *end_block;
|
||||
IrInstruction *is_comptime;
|
||||
ZigList<IrInstruction *> *incoming_values;
|
||||
ZigList<IrBasicBlock *> *incoming_blocks;
|
||||
|
||||
bool safety_off;
|
||||
AstNode *safety_set_node;
|
||||
bool fast_math_off;
|
||||
@ -1734,6 +1735,7 @@ struct ScopeCImport {
|
||||
struct ScopeLoop {
|
||||
Scope base;
|
||||
|
||||
Buf *name;
|
||||
IrBasicBlock *break_block;
|
||||
IrBasicBlock *continue_block;
|
||||
IrInstruction *is_comptime;
|
||||
@ -1885,8 +1887,6 @@ enum IrInstructionId {
|
||||
IrInstructionIdCheckStatementIsVoid,
|
||||
IrInstructionIdTypeName,
|
||||
IrInstructionIdCanImplicitCast,
|
||||
IrInstructionIdSetGlobalSection,
|
||||
IrInstructionIdSetGlobalLinkage,
|
||||
IrInstructionIdDeclRef,
|
||||
IrInstructionIdPanic,
|
||||
IrInstructionIdTagName,
|
||||
@ -1900,6 +1900,7 @@ enum IrInstructionId {
|
||||
IrInstructionIdOpaqueType,
|
||||
IrInstructionIdSetAlignStack,
|
||||
IrInstructionIdArgType,
|
||||
IrInstructionIdExport,
|
||||
};
|
||||
|
||||
struct IrInstruction {
|
||||
@ -2102,7 +2103,7 @@ struct IrInstructionCall {
|
||||
IrInstruction **args;
|
||||
bool is_comptime;
|
||||
LLVMValueRef tmp_ptr;
|
||||
bool is_inline;
|
||||
FnInline fn_inline;
|
||||
};
|
||||
|
||||
struct IrInstructionConst {
|
||||
@ -2625,20 +2626,6 @@ struct IrInstructionCanImplicitCast {
|
||||
IrInstruction *target_value;
|
||||
};
|
||||
|
||||
struct IrInstructionSetGlobalSection {
|
||||
IrInstruction base;
|
||||
|
||||
Tld *tld;
|
||||
IrInstruction *value;
|
||||
};
|
||||
|
||||
struct IrInstructionSetGlobalLinkage {
|
||||
IrInstruction base;
|
||||
|
||||
Tld *tld;
|
||||
IrInstruction *value;
|
||||
};
|
||||
|
||||
struct IrInstructionDeclRef {
|
||||
IrInstruction base;
|
||||
|
||||
@ -2727,6 +2714,14 @@ struct IrInstructionArgType {
|
||||
IrInstruction *arg_index;
|
||||
};
|
||||
|
||||
struct IrInstructionExport {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *name;
|
||||
IrInstruction *linkage;
|
||||
IrInstruction *target;
|
||||
};
|
||||
|
||||
static const size_t slice_ptr_index = 0;
|
||||
static const size_t slice_len_index = 1;
|
||||
|
||||
|
||||
211
src/analyze.cpp
211
src/analyze.cpp
@ -110,7 +110,7 @@ ScopeBlock *create_block_scope(AstNode *node, Scope *parent) {
|
||||
assert(node->type == NodeTypeBlock);
|
||||
ScopeBlock *scope = allocate<ScopeBlock>(1);
|
||||
init_scope(&scope->base, ScopeIdBlock, node, parent);
|
||||
scope->label_table.init(1);
|
||||
scope->name = node->data.block.name;
|
||||
return scope;
|
||||
}
|
||||
|
||||
@ -144,9 +144,15 @@ ScopeCImport *create_cimport_scope(AstNode *node, Scope *parent) {
|
||||
}
|
||||
|
||||
ScopeLoop *create_loop_scope(AstNode *node, Scope *parent) {
|
||||
assert(node->type == NodeTypeWhileExpr || node->type == NodeTypeForExpr);
|
||||
ScopeLoop *scope = allocate<ScopeLoop>(1);
|
||||
init_scope(&scope->base, ScopeIdLoop, node, parent);
|
||||
if (node->type == NodeTypeWhileExpr) {
|
||||
scope->name = node->data.while_expr.name;
|
||||
} else if (node->type == NodeTypeForExpr) {
|
||||
scope->name = node->data.for_expr.name;
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
return scope;
|
||||
}
|
||||
|
||||
@ -429,7 +435,7 @@ TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) {
|
||||
ensure_complete_type(g, child_type);
|
||||
|
||||
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdMaybe);
|
||||
assert(child_type->type_ref);
|
||||
assert(child_type->type_ref || child_type->zero_bits);
|
||||
assert(child_type->di_type);
|
||||
entry->is_copyable = type_is_copyable(g, child_type);
|
||||
|
||||
@ -1062,7 +1068,7 @@ void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_cou
|
||||
AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
|
||||
|
||||
if (fn_proto->cc == CallingConventionUnspecified) {
|
||||
bool extern_abi = fn_proto->is_extern || (fn_proto->visib_mod == VisibModExport);
|
||||
bool extern_abi = fn_proto->is_extern || fn_proto->is_export;
|
||||
fn_type_id->cc = extern_abi ? CallingConventionC : CallingConventionUnspecified;
|
||||
} else {
|
||||
fn_type_id->cc = fn_proto->cc;
|
||||
@ -1093,6 +1099,38 @@ static bool analyze_const_align(CodeGen *g, Scope *scope, AstNode *node, uint32_
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool analyze_const_string(CodeGen *g, Scope *scope, AstNode *node, Buf **out_buffer) {
|
||||
TypeTableEntry *ptr_type = get_pointer_to_type(g, g->builtin_types.entry_u8, true);
|
||||
TypeTableEntry *str_type = get_slice_type(g, ptr_type);
|
||||
IrInstruction *instr = analyze_const_value(g, scope, node, str_type, nullptr);
|
||||
if (type_is_invalid(instr->value.type))
|
||||
return false;
|
||||
|
||||
ConstExprValue *ptr_field = &instr->value.data.x_struct.fields[slice_ptr_index];
|
||||
ConstExprValue *len_field = &instr->value.data.x_struct.fields[slice_len_index];
|
||||
|
||||
assert(ptr_field->data.x_ptr.special == ConstPtrSpecialBaseArray);
|
||||
ConstExprValue *array_val = ptr_field->data.x_ptr.data.base_array.array_val;
|
||||
expand_undef_array(g, array_val);
|
||||
size_t len = bigint_as_unsigned(&len_field->data.x_bigint);
|
||||
Buf *result = buf_alloc();
|
||||
buf_resize(result, len);
|
||||
for (size_t i = 0; i < len; i += 1) {
|
||||
size_t new_index = ptr_field->data.x_ptr.data.base_array.elem_index + i;
|
||||
ConstExprValue *char_val = &array_val->data.x_array.s_none.elements[new_index];
|
||||
if (char_val->special == ConstValSpecialUndef) {
|
||||
add_node_error(g, node, buf_sprintf("use of undefined value"));
|
||||
return false;
|
||||
}
|
||||
uint64_t big_c = bigint_as_unsigned(&char_val->data.x_bigint);
|
||||
assert(big_c <= UINT8_MAX);
|
||||
uint8_t c = (uint8_t)big_c;
|
||||
buf_ptr(result)[i] = c;
|
||||
}
|
||||
*out_buffer = result;
|
||||
return true;
|
||||
}
|
||||
|
||||
static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_scope) {
|
||||
assert(proto_node->type == NodeTypeFnProto);
|
||||
AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
|
||||
@ -1130,6 +1168,15 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
|
||||
}
|
||||
|
||||
TypeTableEntry *type_entry = analyze_type_expr(g, child_scope, param_node->data.param_decl.type);
|
||||
if (fn_type_id.cc != CallingConventionUnspecified) {
|
||||
type_ensure_zero_bits_known(g, type_entry);
|
||||
if (!type_has_bits(type_entry)) {
|
||||
add_node_error(g, param_node->data.param_decl.type,
|
||||
buf_sprintf("parameter of type '%s' has 0 bits; not allowed in function with calling convention '%s'",
|
||||
buf_ptr(&type_entry->name), calling_convention_name(fn_type_id.cc)));
|
||||
return g->builtin_types.entry_invalid;
|
||||
}
|
||||
}
|
||||
|
||||
switch (type_entry->id) {
|
||||
case TypeTableEntryIdInvalid:
|
||||
@ -2227,7 +2274,7 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
|
||||
|
||||
tag_type = new_type_table_entry(TypeTableEntryIdEnum);
|
||||
buf_resize(&tag_type->name, 0);
|
||||
buf_appendf(&tag_type->name, "@EnumTagType(%s)", buf_ptr(&union_type->name));
|
||||
buf_appendf(&tag_type->name, "@TagType(%s)", buf_ptr(&union_type->name));
|
||||
tag_type->is_copyable = true;
|
||||
tag_type->type_ref = tag_int_type->type_ref;
|
||||
tag_type->zero_bits = tag_int_type->zero_bits;
|
||||
@ -2244,12 +2291,10 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
|
||||
TypeTableEntry *enum_type = analyze_type_expr(g, scope, enum_type_node);
|
||||
if (type_is_invalid(enum_type)) {
|
||||
union_type->data.unionation.is_invalid = true;
|
||||
union_type->data.unionation.embedded_in_current = false;
|
||||
return;
|
||||
}
|
||||
if (enum_type->id != TypeTableEntryIdEnum) {
|
||||
union_type->data.unionation.is_invalid = true;
|
||||
union_type->data.unionation.embedded_in_current = false;
|
||||
add_node_error(g, enum_type_node,
|
||||
buf_sprintf("expected enum tag type, found '%s'", buf_ptr(&enum_type->name)));
|
||||
return;
|
||||
@ -2474,7 +2519,7 @@ static void get_fully_qualified_decl_name(Buf *buf, Tld *tld, uint8_t sep) {
|
||||
buf_append_buf(buf, tld->name);
|
||||
}
|
||||
|
||||
FnTableEntry *create_fn_raw(FnInline inline_value, GlobalLinkageId linkage) {
|
||||
FnTableEntry *create_fn_raw(FnInline inline_value) {
|
||||
FnTableEntry *fn_entry = allocate<FnTableEntry>(1);
|
||||
|
||||
fn_entry->analyzed_executable.backward_branch_count = &fn_entry->prealloc_bbc;
|
||||
@ -2482,7 +2527,6 @@ FnTableEntry *create_fn_raw(FnInline inline_value, GlobalLinkageId linkage) {
|
||||
fn_entry->analyzed_executable.fn_entry = fn_entry;
|
||||
fn_entry->ir_executable.fn_entry = fn_entry;
|
||||
fn_entry->fn_inline = inline_value;
|
||||
fn_entry->linkage = linkage;
|
||||
|
||||
return fn_entry;
|
||||
}
|
||||
@ -2492,9 +2536,7 @@ FnTableEntry *create_fn(AstNode *proto_node) {
|
||||
AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
|
||||
|
||||
FnInline inline_value = fn_proto->is_inline ? FnInlineAlways : FnInlineAuto;
|
||||
GlobalLinkageId linkage = (fn_proto->visib_mod == VisibModExport || proto_node->data.fn_proto.is_extern) ?
|
||||
GlobalLinkageIdStrong : GlobalLinkageIdInternal;
|
||||
FnTableEntry *fn_entry = create_fn_raw(inline_value, linkage);
|
||||
FnTableEntry *fn_entry = create_fn_raw(inline_value);
|
||||
|
||||
fn_entry->proto_node = proto_node;
|
||||
fn_entry->body_node = (proto_node->data.fn_proto.fn_def_node == nullptr) ? nullptr :
|
||||
@ -2550,6 +2592,34 @@ TypeTableEntry *get_test_fn_type(CodeGen *g) {
|
||||
return g->test_fn_type;
|
||||
}
|
||||
|
||||
void add_fn_export(CodeGen *g, FnTableEntry *fn_table_entry, Buf *symbol_name, GlobalLinkageId linkage, bool ccc) {
|
||||
if (ccc) {
|
||||
if (buf_eql_str(symbol_name, "main") && g->libc_link_lib != nullptr) {
|
||||
g->have_c_main = true;
|
||||
g->windows_subsystem_windows = false;
|
||||
g->windows_subsystem_console = true;
|
||||
} else if (buf_eql_str(symbol_name, "WinMain") &&
|
||||
g->zig_target.os == ZigLLVM_Win32)
|
||||
{
|
||||
g->have_winmain = true;
|
||||
g->windows_subsystem_windows = true;
|
||||
g->windows_subsystem_console = false;
|
||||
} else if (buf_eql_str(symbol_name, "WinMainCRTStartup") &&
|
||||
g->zig_target.os == ZigLLVM_Win32)
|
||||
{
|
||||
g->have_winmain_crt_startup = true;
|
||||
} else if (buf_eql_str(symbol_name, "DllMainCRTStartup") &&
|
||||
g->zig_target.os == ZigLLVM_Win32)
|
||||
{
|
||||
g->have_dllmain_crt_startup = true;
|
||||
}
|
||||
}
|
||||
FnExport *fn_export = fn_table_entry->export_list.add_one();
|
||||
memset(fn_export, 0, sizeof(FnExport));
|
||||
buf_init_from_buf(&fn_export->name, symbol_name);
|
||||
fn_export->linkage = linkage;
|
||||
}
|
||||
|
||||
static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
|
||||
ImportTableEntry *import = tld_fn->base.import;
|
||||
AstNode *source_node = tld_fn->base.source_node;
|
||||
@ -2561,6 +2631,11 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
|
||||
FnTableEntry *fn_table_entry = create_fn(source_node);
|
||||
get_fully_qualified_decl_name(&fn_table_entry->symbol_name, &tld_fn->base, '_');
|
||||
|
||||
if (fn_proto->is_export) {
|
||||
bool ccc = (fn_proto->cc == CallingConventionUnspecified || fn_proto->cc == CallingConventionC);
|
||||
add_fn_export(g, fn_table_entry, &fn_table_entry->symbol_name, GlobalLinkageIdStrong, ccc);
|
||||
}
|
||||
|
||||
tld_fn->fn_entry = fn_table_entry;
|
||||
|
||||
if (fn_table_entry->body_node) {
|
||||
@ -2574,7 +2649,7 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
|
||||
add_node_error(g, param_node, buf_sprintf("missing parameter name"));
|
||||
}
|
||||
}
|
||||
} else if (fn_table_entry->linkage != GlobalLinkageIdInternal) {
|
||||
} else {
|
||||
g->external_prototypes.put_unique(tld_fn->base.name, &tld_fn->base);
|
||||
}
|
||||
|
||||
@ -2582,6 +2657,15 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
|
||||
|
||||
fn_table_entry->type_entry = analyze_fn_type(g, source_node, child_scope);
|
||||
|
||||
if (fn_proto->section_expr != nullptr) {
|
||||
if (fn_table_entry->body_node == nullptr) {
|
||||
add_node_error(g, fn_proto->section_expr,
|
||||
buf_sprintf("cannot set section of external function '%s'", buf_ptr(&fn_table_entry->symbol_name)));
|
||||
} else {
|
||||
analyze_const_string(g, child_scope, fn_proto->section_expr, &fn_table_entry->section_name);
|
||||
}
|
||||
}
|
||||
|
||||
if (fn_table_entry->type_entry->id == TypeTableEntryIdInvalid) {
|
||||
tld_fn->base.resolution = TldResolutionInvalid;
|
||||
return;
|
||||
@ -2596,15 +2680,12 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
|
||||
{
|
||||
if (g->have_pub_main && buf_eql_str(&fn_table_entry->symbol_name, "main")) {
|
||||
g->main_fn = fn_table_entry;
|
||||
|
||||
if (tld_fn->base.visib_mod != VisibModExport) {
|
||||
TypeTableEntry *err_void = get_error_type(g, g->builtin_types.entry_void);
|
||||
TypeTableEntry *actual_return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type;
|
||||
if (actual_return_type != err_void) {
|
||||
add_node_error(g, fn_proto->return_type,
|
||||
buf_sprintf("expected return type of main to be '%%void', instead is '%s'",
|
||||
buf_ptr(&actual_return_type->name)));
|
||||
}
|
||||
TypeTableEntry *err_void = get_error_type(g, g->builtin_types.entry_void);
|
||||
TypeTableEntry *actual_return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type;
|
||||
if (actual_return_type != err_void) {
|
||||
add_node_error(g, fn_proto->return_type,
|
||||
buf_sprintf("expected return type of main to be '%%void', instead is '%s'",
|
||||
buf_ptr(&actual_return_type->name)));
|
||||
}
|
||||
} else if ((import->package == g->panic_package || g->have_pub_panic) &&
|
||||
buf_eql_str(&fn_table_entry->symbol_name, "panic"))
|
||||
@ -2615,7 +2696,7 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
|
||||
}
|
||||
}
|
||||
} else if (source_node->type == NodeTypeTestDecl) {
|
||||
FnTableEntry *fn_table_entry = create_fn_raw(FnInlineAuto, GlobalLinkageIdStrong);
|
||||
FnTableEntry *fn_table_entry = create_fn_raw(FnInlineAuto);
|
||||
|
||||
get_fully_qualified_decl_name(&fn_table_entry->symbol_name, &tld_fn->base, '_');
|
||||
|
||||
@ -2642,17 +2723,23 @@ static void resolve_decl_comptime(CodeGen *g, TldCompTime *tld_comptime) {
|
||||
}
|
||||
|
||||
static void add_top_level_decl(CodeGen *g, ScopeDecls *decls_scope, Tld *tld) {
|
||||
if (tld->visib_mod == VisibModExport) {
|
||||
g->resolve_queue.append(tld);
|
||||
bool is_export = false;
|
||||
if (tld->id == TldIdVar) {
|
||||
assert(tld->source_node->type == NodeTypeVariableDeclaration);
|
||||
is_export = tld->source_node->data.variable_declaration.is_export;
|
||||
} else if (tld->id == TldIdFn) {
|
||||
assert(tld->source_node->type == NodeTypeFnProto);
|
||||
is_export = tld->source_node->data.fn_proto.is_export;
|
||||
}
|
||||
if (is_export) {
|
||||
g->resolve_queue.append(tld);
|
||||
|
||||
if (tld->visib_mod == VisibModExport) {
|
||||
auto entry = g->exported_symbol_names.put_unique(tld->name, tld);
|
||||
auto entry = g->exported_symbol_names.put_unique(tld->name, tld->source_node);
|
||||
if (entry) {
|
||||
Tld *other_tld = entry->value;
|
||||
AstNode *other_source_node = entry->value;
|
||||
ErrorMsg *msg = add_node_error(g, tld->source_node,
|
||||
buf_sprintf("exported symbol collision: '%s'", buf_ptr(tld->name)));
|
||||
add_error_note(g, msg, other_tld->source_node, buf_sprintf("other symbol is here"));
|
||||
add_error_note(g, msg, other_source_node, buf_sprintf("other symbol here"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -2731,7 +2818,6 @@ static void preview_comptime_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_s
|
||||
g->resolve_queue.append(&tld_comptime->base);
|
||||
}
|
||||
|
||||
|
||||
void init_tld(Tld *tld, TldId id, Buf *name, VisibMod visib_mod, AstNode *source_node,
|
||||
Scope *parent_scope)
|
||||
{
|
||||
@ -2836,8 +2922,6 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
|
||||
case NodeTypeSwitchExpr:
|
||||
case NodeTypeSwitchProng:
|
||||
case NodeTypeSwitchRange:
|
||||
case NodeTypeLabel:
|
||||
case NodeTypeGoto:
|
||||
case NodeTypeBreak:
|
||||
case NodeTypeContinue:
|
||||
case NodeTypeUnreachable:
|
||||
@ -2987,8 +3071,8 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
|
||||
AstNodeVariableDeclaration *var_decl = &source_node->data.variable_declaration;
|
||||
|
||||
bool is_const = var_decl->is_const;
|
||||
bool is_export = (tld_var->base.visib_mod == VisibModExport);
|
||||
bool is_extern = var_decl->is_extern;
|
||||
bool is_export = var_decl->is_export;
|
||||
|
||||
TypeTableEntry *explicit_type = nullptr;
|
||||
if (var_decl->type) {
|
||||
@ -2996,9 +3080,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
|
||||
explicit_type = validate_var_type(g, var_decl->type, proposed_type);
|
||||
}
|
||||
|
||||
if (is_export && is_extern) {
|
||||
add_node_error(g, source_node, buf_sprintf("variable is both export and extern"));
|
||||
}
|
||||
assert(!is_export || !is_extern);
|
||||
|
||||
VarLinkage linkage;
|
||||
if (is_export) {
|
||||
@ -3009,7 +3091,6 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
|
||||
linkage = VarLinkageInternal;
|
||||
}
|
||||
|
||||
|
||||
IrInstruction *init_value = nullptr;
|
||||
|
||||
// TODO more validation for types that can't be used for export/extern variables
|
||||
@ -3058,6 +3139,15 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
|
||||
}
|
||||
}
|
||||
|
||||
if (var_decl->section_expr != nullptr) {
|
||||
if (var_decl->is_extern) {
|
||||
add_node_error(g, var_decl->section_expr,
|
||||
buf_sprintf("cannot set section of external variable '%s'", buf_ptr(var_decl->symbol)));
|
||||
} else if (!analyze_const_string(g, tld_var->base.parent_scope, var_decl->section_expr, &tld_var->section_name)) {
|
||||
tld_var->section_name = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
g->global_vars.append(tld_var);
|
||||
}
|
||||
|
||||
@ -3319,7 +3409,7 @@ TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name) {
|
||||
|
||||
TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name) {
|
||||
assert(type_entry->id == TypeTableEntryIdUnion);
|
||||
assert(type_entry->data.unionation.complete);
|
||||
assert(type_entry->data.unionation.zero_bits_known);
|
||||
for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) {
|
||||
TypeUnionField *field = &type_entry->data.unionation.fields[i];
|
||||
if (buf_eql_buf(field->enum_field->name, name)) {
|
||||
@ -3331,7 +3421,7 @@ TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name) {
|
||||
|
||||
TypeUnionField *find_union_field_by_tag(TypeTableEntry *type_entry, const BigInt *tag) {
|
||||
assert(type_entry->id == TypeTableEntryIdUnion);
|
||||
assert(type_entry->data.unionation.complete);
|
||||
assert(type_entry->data.unionation.zero_bits_known);
|
||||
assert(type_entry->data.unionation.gen_tag_index != SIZE_MAX);
|
||||
for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) {
|
||||
TypeUnionField *field = &type_entry->data.unionation.fields[i];
|
||||
@ -3726,8 +3816,10 @@ ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, Buf *a
|
||||
Buf *proto_name = proto_node->data.fn_proto.name;
|
||||
|
||||
bool is_pub = (proto_node->data.fn_proto.visib_mod == VisibModPub);
|
||||
bool ok_cc = (proto_node->data.fn_proto.cc == CallingConventionUnspecified ||
|
||||
proto_node->data.fn_proto.cc == CallingConventionCold);
|
||||
|
||||
if (is_pub) {
|
||||
if (is_pub && ok_cc) {
|
||||
if (buf_eql_str(proto_name, "main")) {
|
||||
g->have_pub_main = true;
|
||||
g->windows_subsystem_windows = false;
|
||||
@ -3735,28 +3827,7 @@ ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, Buf *a
|
||||
} else if (buf_eql_str(proto_name, "panic")) {
|
||||
g->have_pub_panic = true;
|
||||
}
|
||||
} else if (proto_node->data.fn_proto.visib_mod == VisibModExport && buf_eql_str(proto_name, "main") &&
|
||||
g->libc_link_lib != nullptr)
|
||||
{
|
||||
g->have_c_main = true;
|
||||
g->windows_subsystem_windows = false;
|
||||
g->windows_subsystem_console = true;
|
||||
} else if (proto_node->data.fn_proto.visib_mod == VisibModExport && buf_eql_str(proto_name, "WinMain") &&
|
||||
g->zig_target.os == ZigLLVM_Win32)
|
||||
{
|
||||
g->have_winmain = true;
|
||||
g->windows_subsystem_windows = true;
|
||||
g->windows_subsystem_console = false;
|
||||
} else if (proto_node->data.fn_proto.visib_mod == VisibModExport &&
|
||||
buf_eql_str(proto_name, "WinMainCRTStartup") && g->zig_target.os == ZigLLVM_Win32)
|
||||
{
|
||||
g->have_winmain_crt_startup = true;
|
||||
} else if (proto_node->data.fn_proto.visib_mod == VisibModExport &&
|
||||
buf_eql_str(proto_name, "DllMainCRTStartup") && g->zig_target.os == ZigLLVM_Win32)
|
||||
{
|
||||
g->have_dllmain_crt_startup = true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -3820,12 +3891,14 @@ TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, uint32_t size_in_b
|
||||
index = 6;
|
||||
} else if (size_in_bits == 16) {
|
||||
index = 7;
|
||||
} else if (size_in_bits == 32) {
|
||||
} else if (size_in_bits == 29) {
|
||||
index = 8;
|
||||
} else if (size_in_bits == 64) {
|
||||
} else if (size_in_bits == 32) {
|
||||
index = 9;
|
||||
} else if (size_in_bits == 128) {
|
||||
} else if (size_in_bits == 64) {
|
||||
index = 10;
|
||||
} else if (size_in_bits == 128) {
|
||||
index = 11;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
@ -3888,7 +3961,6 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
|
||||
return false;
|
||||
case TypeTableEntryIdArray:
|
||||
case TypeTableEntryIdStruct:
|
||||
case TypeTableEntryIdUnion:
|
||||
return type_has_bits(type_entry);
|
||||
case TypeTableEntryIdErrorUnion:
|
||||
return type_has_bits(type_entry->data.error.child_type);
|
||||
@ -3896,6 +3968,14 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
|
||||
return type_has_bits(type_entry->data.maybe.child_type) &&
|
||||
type_entry->data.maybe.child_type->id != TypeTableEntryIdPointer &&
|
||||
type_entry->data.maybe.child_type->id != TypeTableEntryIdFn;
|
||||
case TypeTableEntryIdUnion:
|
||||
assert(type_entry->data.unionation.complete);
|
||||
if (type_entry->data.unionation.gen_field_count == 0)
|
||||
return false;
|
||||
if (!type_has_bits(type_entry))
|
||||
return false;
|
||||
return true;
|
||||
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
@ -5438,3 +5518,4 @@ uint32_t type_ptr_hash(const TypeTableEntry *ptr) {
|
||||
bool type_ptr_eql(const TypeTableEntry *a, const TypeTableEntry *b) {
|
||||
return a == b;
|
||||
}
|
||||
|
||||
|
||||
@ -180,5 +180,9 @@ void add_link_lib_symbol(CodeGen *g, Buf *lib_name, Buf *symbol_name);
|
||||
|
||||
uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry);
|
||||
TypeTableEntry *get_align_amt_type(CodeGen *g);
|
||||
PackageTableEntry *new_anonymous_package(void);
|
||||
|
||||
Buf *const_value_to_buffer(ConstExprValue *const_val);
|
||||
void add_fn_export(CodeGen *g, FnTableEntry *fn_table_entry, Buf *symbol_name, GlobalLinkageId linkage, bool ccc);
|
||||
|
||||
#endif
|
||||
|
||||
@ -78,7 +78,6 @@ static const char *visib_mod_string(VisibMod mod) {
|
||||
switch (mod) {
|
||||
case VisibModPub: return "pub ";
|
||||
case VisibModPrivate: return "";
|
||||
case VisibModExport: return "export ";
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
@ -112,6 +111,10 @@ static const char *extern_string(bool is_extern) {
|
||||
return is_extern ? "extern " : "";
|
||||
}
|
||||
|
||||
static const char *export_string(bool is_export) {
|
||||
return is_export ? "export " : "";
|
||||
}
|
||||
|
||||
//static const char *calling_convention_string(CallingConvention cc) {
|
||||
// switch (cc) {
|
||||
// case CallingConventionUnspecified: return "";
|
||||
@ -212,10 +215,6 @@ static const char *node_type_str(NodeType node_type) {
|
||||
return "SwitchProng";
|
||||
case NodeTypeSwitchRange:
|
||||
return "SwitchRange";
|
||||
case NodeTypeLabel:
|
||||
return "Label";
|
||||
case NodeTypeGoto:
|
||||
return "Goto";
|
||||
case NodeTypeCompTime:
|
||||
return "CompTime";
|
||||
case NodeTypeBreak:
|
||||
@ -388,7 +387,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
|
||||
switch (node->type) {
|
||||
case NodeTypeSwitchProng:
|
||||
case NodeTypeSwitchRange:
|
||||
case NodeTypeLabel:
|
||||
case NodeTypeStructValueField:
|
||||
zig_unreachable();
|
||||
case NodeTypeRoot:
|
||||
@ -411,8 +409,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
|
||||
{
|
||||
const char *pub_str = visib_mod_string(node->data.fn_proto.visib_mod);
|
||||
const char *extern_str = extern_string(node->data.fn_proto.is_extern);
|
||||
const char *export_str = export_string(node->data.fn_proto.is_export);
|
||||
const char *inline_str = inline_string(node->data.fn_proto.is_inline);
|
||||
fprintf(ar->f, "%s%s%sfn", pub_str, inline_str, extern_str);
|
||||
fprintf(ar->f, "%s%s%s%sfn", pub_str, inline_str, export_str, extern_str);
|
||||
if (node->data.fn_proto.name != nullptr) {
|
||||
fprintf(ar->f, " ");
|
||||
print_symbol(ar, node->data.fn_proto.name);
|
||||
@ -440,6 +439,16 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
|
||||
}
|
||||
}
|
||||
fprintf(ar->f, ")");
|
||||
if (node->data.fn_proto.align_expr) {
|
||||
fprintf(ar->f, " align(");
|
||||
render_node_grouped(ar, node->data.fn_proto.align_expr);
|
||||
fprintf(ar->f, ")");
|
||||
}
|
||||
if (node->data.fn_proto.section_expr) {
|
||||
fprintf(ar->f, " section(");
|
||||
render_node_grouped(ar, node->data.fn_proto.section_expr);
|
||||
fprintf(ar->f, ")");
|
||||
}
|
||||
|
||||
AstNode *return_type_node = node->data.fn_proto.return_type;
|
||||
if (return_type_node != nullptr) {
|
||||
@ -456,6 +465,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
|
||||
break;
|
||||
}
|
||||
case NodeTypeBlock:
|
||||
if (node->data.block.name != nullptr) {
|
||||
fprintf(ar->f, "%s: ", buf_ptr(node->data.block.name));
|
||||
}
|
||||
if (node->data.block.statements.length == 0) {
|
||||
fprintf(ar->f, "{}");
|
||||
break;
|
||||
@ -464,19 +476,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
|
||||
ar->indent += ar->indent_size;
|
||||
for (size_t i = 0; i < node->data.block.statements.length; i += 1) {
|
||||
AstNode *statement = node->data.block.statements.at(i);
|
||||
if (statement->type == NodeTypeLabel) {
|
||||
ar->indent -= ar->indent_size;
|
||||
print_indent(ar);
|
||||
fprintf(ar->f, "%s:\n", buf_ptr(statement->data.label.name));
|
||||
ar->indent += ar->indent_size;
|
||||
continue;
|
||||
}
|
||||
print_indent(ar);
|
||||
render_node_grouped(ar, statement);
|
||||
if (!(i == node->data.block.statements.length - 1 &&
|
||||
node->data.block.last_statement_is_result_expression)) {
|
||||
fprintf(ar->f, ";");
|
||||
}
|
||||
fprintf(ar->f, ";");
|
||||
fprintf(ar->f, "\n");
|
||||
}
|
||||
ar->indent -= ar->indent_size;
|
||||
@ -501,6 +503,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
|
||||
case NodeTypeBreak:
|
||||
{
|
||||
fprintf(ar->f, "break");
|
||||
if (node->data.break_expr.name != nullptr) {
|
||||
fprintf(ar->f, " :%s", buf_ptr(node->data.break_expr.name));
|
||||
}
|
||||
if (node->data.break_expr.expr) {
|
||||
fprintf(ar->f, " ");
|
||||
render_node_grouped(ar, node->data.break_expr.expr);
|
||||
@ -526,6 +531,16 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
|
||||
fprintf(ar->f, ": ");
|
||||
render_node_grouped(ar, node->data.variable_declaration.type);
|
||||
}
|
||||
if (node->data.variable_declaration.align_expr) {
|
||||
fprintf(ar->f, "align(");
|
||||
render_node_grouped(ar, node->data.variable_declaration.align_expr);
|
||||
fprintf(ar->f, ") ");
|
||||
}
|
||||
if (node->data.variable_declaration.section_expr) {
|
||||
fprintf(ar->f, "section(");
|
||||
render_node_grouped(ar, node->data.variable_declaration.section_expr);
|
||||
fprintf(ar->f, ") ");
|
||||
}
|
||||
if (node->data.variable_declaration.expr) {
|
||||
fprintf(ar->f, " = ");
|
||||
render_node_grouped(ar, node->data.variable_declaration.expr);
|
||||
@ -584,12 +599,15 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
|
||||
PrefixOp op = node->data.prefix_op_expr.prefix_op;
|
||||
fprintf(ar->f, "%s", prefix_op_str(op));
|
||||
|
||||
render_node_ungrouped(ar, node->data.prefix_op_expr.primary_expr);
|
||||
AstNode *child_node = node->data.prefix_op_expr.primary_expr;
|
||||
bool new_grouped = child_node->type == NodeTypePrefixOpExpr || child_node->type == NodeTypeAddrOfExpr;
|
||||
render_node_extra(ar, child_node, new_grouped);
|
||||
if (!grouped) fprintf(ar->f, ")");
|
||||
break;
|
||||
}
|
||||
case NodeTypeAddrOfExpr:
|
||||
{
|
||||
if (!grouped) fprintf(ar->f, "(");
|
||||
fprintf(ar->f, "&");
|
||||
if (node->data.addr_of_expr.align_expr != nullptr) {
|
||||
fprintf(ar->f, "align(");
|
||||
@ -617,6 +635,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
|
||||
}
|
||||
|
||||
render_node_ungrouped(ar, node->data.addr_of_expr.op_expr);
|
||||
if (!grouped) fprintf(ar->f, ")");
|
||||
break;
|
||||
}
|
||||
case NodeTypeFnCallExpr:
|
||||
@ -625,7 +644,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
|
||||
fprintf(ar->f, "@");
|
||||
}
|
||||
AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr;
|
||||
bool grouped = (fn_ref_node->type != NodeTypePrefixOpExpr);
|
||||
bool grouped = (fn_ref_node->type != NodeTypePrefixOpExpr && fn_ref_node->type != NodeTypeAddrOfExpr);
|
||||
render_node_extra(ar, fn_ref_node, grouped);
|
||||
fprintf(ar->f, "(");
|
||||
for (size_t i = 0; i < node->data.fn_call_expr.params.length; i += 1) {
|
||||
@ -800,6 +819,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
|
||||
}
|
||||
case NodeTypeWhileExpr:
|
||||
{
|
||||
if (node->data.while_expr.name != nullptr) {
|
||||
fprintf(ar->f, "%s: ", buf_ptr(node->data.while_expr.name));
|
||||
}
|
||||
const char *inline_str = node->data.while_expr.is_inline ? "inline " : "";
|
||||
fprintf(ar->f, "%swhile (", inline_str);
|
||||
render_node_grouped(ar, node->data.while_expr.condition);
|
||||
@ -929,11 +951,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
|
||||
fprintf(ar->f, "}");
|
||||
break;
|
||||
}
|
||||
case NodeTypeGoto:
|
||||
{
|
||||
fprintf(ar->f, "goto %s", buf_ptr(node->data.goto_expr.name));
|
||||
break;
|
||||
}
|
||||
case NodeTypeCompTime:
|
||||
{
|
||||
fprintf(ar->f, "comptime ");
|
||||
@ -942,6 +959,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
|
||||
}
|
||||
case NodeTypeForExpr:
|
||||
{
|
||||
if (node->data.for_expr.name != nullptr) {
|
||||
fprintf(ar->f, "%s: ", buf_ptr(node->data.for_expr.name));
|
||||
}
|
||||
const char *inline_str = node->data.for_expr.is_inline ? "inline " : "";
|
||||
fprintf(ar->f, "%sfor (", inline_str);
|
||||
render_node_grouped(ar, node->data.for_expr.array_expr);
|
||||
@ -967,6 +987,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
|
||||
case NodeTypeContinue:
|
||||
{
|
||||
fprintf(ar->f, "continue");
|
||||
if (node->data.continue_expr.name != nullptr) {
|
||||
fprintf(ar->f, " :%s", buf_ptr(node->data.continue_expr.name));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NodeTypeUnreachable:
|
||||
|
||||
@ -121,6 +121,9 @@ static void begin_token(CTokenize *ctok, CTokId id) {
|
||||
case CTokIdRParen:
|
||||
case CTokIdEOF:
|
||||
case CTokIdDot:
|
||||
case CTokIdAsterisk:
|
||||
case CTokIdBang:
|
||||
case CTokIdTilde:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -228,10 +231,22 @@ void tokenize_c_macro(CTokenize *ctok, const uint8_t *c) {
|
||||
begin_token(ctok, CTokIdRParen);
|
||||
end_token(ctok);
|
||||
break;
|
||||
case '*':
|
||||
begin_token(ctok, CTokIdAsterisk);
|
||||
end_token(ctok);
|
||||
break;
|
||||
case '-':
|
||||
begin_token(ctok, CTokIdMinus);
|
||||
end_token(ctok);
|
||||
break;
|
||||
case '!':
|
||||
begin_token(ctok, CTokIdBang);
|
||||
end_token(ctok);
|
||||
break;
|
||||
case '~':
|
||||
begin_token(ctok, CTokIdTilde);
|
||||
end_token(ctok);
|
||||
break;
|
||||
default:
|
||||
return mark_error(ctok);
|
||||
}
|
||||
|
||||
@ -22,6 +22,9 @@ enum CTokId {
|
||||
CTokIdRParen,
|
||||
CTokIdEOF,
|
||||
CTokIdDot,
|
||||
CTokIdAsterisk,
|
||||
CTokIdBang,
|
||||
CTokIdTilde,
|
||||
};
|
||||
|
||||
enum CNumLitSuffix {
|
||||
|
||||
199
src/codegen.cpp
199
src/codegen.cpp
@ -55,6 +55,10 @@ static PackageTableEntry *new_package(const char *root_src_dir, const char *root
|
||||
return entry;
|
||||
}
|
||||
|
||||
PackageTableEntry *new_anonymous_package(void) {
|
||||
return new_package("", "");
|
||||
}
|
||||
|
||||
CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode,
|
||||
Buf *zig_lib_dir)
|
||||
{
|
||||
@ -387,24 +391,51 @@ static void add_uwtable_attr(CodeGen *g, LLVMValueRef fn_val) {
|
||||
}
|
||||
}
|
||||
|
||||
static LLVMLinkage to_llvm_linkage(GlobalLinkageId id) {
|
||||
switch (id) {
|
||||
case GlobalLinkageIdInternal:
|
||||
return LLVMInternalLinkage;
|
||||
case GlobalLinkageIdStrong:
|
||||
return LLVMExternalLinkage;
|
||||
case GlobalLinkageIdWeak:
|
||||
return LLVMWeakODRLinkage;
|
||||
case GlobalLinkageIdLinkOnce:
|
||||
return LLVMLinkOnceODRLinkage;
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
|
||||
if (fn_table_entry->llvm_value)
|
||||
return fn_table_entry->llvm_value;
|
||||
|
||||
bool external_linkage = (fn_table_entry->linkage != GlobalLinkageIdInternal);
|
||||
Buf *symbol_name = get_mangled_name(g, &fn_table_entry->symbol_name, external_linkage);
|
||||
Buf *unmangled_name = &fn_table_entry->symbol_name;
|
||||
Buf *symbol_name;
|
||||
GlobalLinkageId linkage;
|
||||
if (fn_table_entry->body_node == nullptr) {
|
||||
symbol_name = unmangled_name;
|
||||
linkage = GlobalLinkageIdStrong;
|
||||
} else if (fn_table_entry->export_list.length == 0) {
|
||||
symbol_name = get_mangled_name(g, unmangled_name, false);
|
||||
linkage = GlobalLinkageIdInternal;
|
||||
} else {
|
||||
FnExport *fn_export = &fn_table_entry->export_list.items[0];
|
||||
symbol_name = &fn_export->name;
|
||||
linkage = fn_export->linkage;
|
||||
}
|
||||
|
||||
bool external_linkage = linkage != GlobalLinkageIdInternal;
|
||||
if (fn_table_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionStdcall && external_linkage &&
|
||||
g->zig_target.arch.arch == ZigLLVM_x86)
|
||||
{
|
||||
// prevent name mangling
|
||||
// prevent llvm name mangling
|
||||
symbol_name = buf_sprintf("\x01_%s", buf_ptr(symbol_name));
|
||||
}
|
||||
|
||||
|
||||
TypeTableEntry *fn_type = fn_table_entry->type_entry;
|
||||
LLVMTypeRef fn_llvm_type = fn_type->data.fn.raw_type_ref;
|
||||
if (external_linkage && fn_table_entry->body_node == nullptr) {
|
||||
if (fn_table_entry->body_node == nullptr) {
|
||||
LLVMValueRef existing_llvm_fn = LLVMGetNamedFunction(g->module, buf_ptr(symbol_name));
|
||||
if (existing_llvm_fn) {
|
||||
fn_table_entry->llvm_value = LLVMConstBitCast(existing_llvm_fn, LLVMPointerType(fn_llvm_type, 0));
|
||||
@ -414,6 +445,12 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
|
||||
}
|
||||
} else {
|
||||
fn_table_entry->llvm_value = LLVMAddFunction(g->module, buf_ptr(symbol_name), fn_llvm_type);
|
||||
|
||||
for (size_t i = 1; i < fn_table_entry->export_list.length; i += 1) {
|
||||
FnExport *fn_export = &fn_table_entry->export_list.items[i];
|
||||
LLVMAddAlias(g->module, LLVMTypeOf(fn_table_entry->llvm_value),
|
||||
fn_table_entry->llvm_value, buf_ptr(&fn_export->name));
|
||||
}
|
||||
}
|
||||
fn_table_entry->llvm_name = LLVMGetValueName(fn_table_entry->llvm_value);
|
||||
|
||||
@ -441,20 +478,10 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
|
||||
}
|
||||
}
|
||||
|
||||
switch (fn_table_entry->linkage) {
|
||||
case GlobalLinkageIdInternal:
|
||||
LLVMSetLinkage(fn_table_entry->llvm_value, LLVMInternalLinkage);
|
||||
LLVMSetUnnamedAddr(fn_table_entry->llvm_value, true);
|
||||
break;
|
||||
case GlobalLinkageIdStrong:
|
||||
LLVMSetLinkage(fn_table_entry->llvm_value, LLVMExternalLinkage);
|
||||
break;
|
||||
case GlobalLinkageIdWeak:
|
||||
LLVMSetLinkage(fn_table_entry->llvm_value, LLVMWeakODRLinkage);
|
||||
break;
|
||||
case GlobalLinkageIdLinkOnce:
|
||||
LLVMSetLinkage(fn_table_entry->llvm_value, LLVMLinkOnceODRLinkage);
|
||||
break;
|
||||
LLVMSetLinkage(fn_table_entry->llvm_value, to_llvm_linkage(linkage));
|
||||
|
||||
if (linkage == GlobalLinkageIdInternal) {
|
||||
LLVMSetUnnamedAddr(fn_table_entry->llvm_value, true);
|
||||
}
|
||||
|
||||
if (fn_type->data.fn.fn_type_id.return_type->id == TypeTableEntryIdUnreachable) {
|
||||
@ -561,7 +588,8 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) {
|
||||
bool is_definition = fn_table_entry->body_node != nullptr;
|
||||
unsigned flags = 0;
|
||||
bool is_optimized = g->build_mode != BuildModeDebug;
|
||||
bool is_internal_linkage = (fn_table_entry->linkage == GlobalLinkageIdInternal);
|
||||
bool is_internal_linkage = (fn_table_entry->body_node != nullptr &&
|
||||
fn_table_entry->export_list.length == 0);
|
||||
ZigLLVMDISubprogram *subprogram = ZigLLVMCreateFunction(g->dbuilder,
|
||||
get_di_scope(g, scope->parent), buf_ptr(&fn_table_entry->symbol_name), "",
|
||||
import->di_file, line_number,
|
||||
@ -839,7 +867,7 @@ static void gen_panic(CodeGen *g, LLVMValueRef msg_arg) {
|
||||
assert(g->panic_fn != nullptr);
|
||||
LLVMValueRef fn_val = fn_llvm_value(g, g->panic_fn);
|
||||
LLVMCallConv llvm_cc = get_llvm_cc(g, g->panic_fn->type_entry->data.fn.fn_type_id.cc);
|
||||
ZigLLVMBuildCall(g->builder, fn_val, &msg_arg, 1, llvm_cc, false, "");
|
||||
ZigLLVMBuildCall(g->builder, fn_val, &msg_arg, 1, llvm_cc, ZigLLVM_FnInlineAuto, "");
|
||||
LLVMBuildUnreachable(g->builder);
|
||||
}
|
||||
|
||||
@ -988,7 +1016,7 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
|
||||
static void gen_debug_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val) {
|
||||
LLVMValueRef safety_crash_err_fn = get_safety_crash_err_fn(g);
|
||||
ZigLLVMBuildCall(g->builder, safety_crash_err_fn, &err_val, 1, get_llvm_cc(g, CallingConventionUnspecified),
|
||||
false, "");
|
||||
ZigLLVM_FnInlineAuto, "");
|
||||
LLVMBuildUnreachable(g->builder);
|
||||
}
|
||||
|
||||
@ -1210,11 +1238,13 @@ static LLVMValueRef gen_assign_raw(CodeGen *g, LLVMValueRef ptr, TypeTableEntry
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool big_endian = g->is_big_endian;
|
||||
|
||||
LLVMValueRef containing_int = gen_load(g, ptr, ptr_type, "");
|
||||
|
||||
uint32_t bit_offset = ptr_type->data.pointer.bit_offset;
|
||||
uint32_t host_bit_count = LLVMGetIntTypeWidth(LLVMTypeOf(containing_int));
|
||||
uint32_t shift_amt = host_bit_count - bit_offset - unaligned_bit_count;
|
||||
uint32_t shift_amt = big_endian ? host_bit_count - bit_offset - unaligned_bit_count : bit_offset;
|
||||
LLVMValueRef shift_amt_val = LLVMConstInt(LLVMTypeOf(containing_int), shift_amt, false);
|
||||
|
||||
LLVMValueRef mask_val = LLVMConstAllOnes(child_type->type_ref);
|
||||
@ -2170,12 +2200,14 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI
|
||||
if (unaligned_bit_count == 0)
|
||||
return get_handle_value(g, ptr, child_type, ptr_type);
|
||||
|
||||
bool big_endian = g->is_big_endian;
|
||||
|
||||
assert(!handle_is_ptr(child_type));
|
||||
LLVMValueRef containing_int = gen_load(g, ptr, ptr_type, "");
|
||||
|
||||
uint32_t bit_offset = ptr_type->data.pointer.bit_offset;
|
||||
uint32_t host_bit_count = LLVMGetIntTypeWidth(LLVMTypeOf(containing_int));
|
||||
uint32_t shift_amt = host_bit_count - bit_offset - unaligned_bit_count;
|
||||
uint32_t shift_amt = big_endian ? host_bit_count - bit_offset - unaligned_bit_count : bit_offset;
|
||||
|
||||
LLVMValueRef shift_amt_val = LLVMConstInt(LLVMTypeOf(containing_int), shift_amt, false);
|
||||
LLVMValueRef shifted_value = LLVMBuildLShr(g->builder, containing_int, shift_amt_val, "");
|
||||
@ -2316,12 +2348,22 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
|
||||
}
|
||||
}
|
||||
|
||||
bool want_always_inline = (instruction->fn_entry != nullptr &&
|
||||
instruction->fn_entry->fn_inline == FnInlineAlways) || instruction->is_inline;
|
||||
ZigLLVM_FnInline fn_inline;
|
||||
switch (instruction->fn_inline) {
|
||||
case FnInlineAuto:
|
||||
fn_inline = ZigLLVM_FnInlineAuto;
|
||||
break;
|
||||
case FnInlineAlways:
|
||||
fn_inline = (instruction->fn_entry == nullptr) ? ZigLLVM_FnInlineAuto : ZigLLVM_FnInlineAlways;
|
||||
break;
|
||||
case FnInlineNever:
|
||||
fn_inline = ZigLLVM_FnInlineNever;
|
||||
break;
|
||||
}
|
||||
|
||||
LLVMCallConv llvm_cc = get_llvm_cc(g, fn_type->data.fn.fn_type_id.cc);
|
||||
LLVMValueRef result = ZigLLVMBuildCall(g->builder, fn_val,
|
||||
gen_param_values, (unsigned)gen_param_index, llvm_cc, want_always_inline, "");
|
||||
gen_param_values, (unsigned)gen_param_index, llvm_cc, fn_inline, "");
|
||||
|
||||
for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) {
|
||||
FnGenParamInfo *gen_info = &fn_type->data.fn.gen_param_info[param_i];
|
||||
@ -2684,6 +2726,9 @@ static LLVMValueRef ir_render_phi(CodeGen *g, IrExecutable *executable, IrInstru
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_ref(CodeGen *g, IrExecutable *executable, IrInstructionRef *instruction) {
|
||||
if (!type_has_bits(instruction->base.value.type)) {
|
||||
return nullptr;
|
||||
}
|
||||
LLVMValueRef value = ir_llvm_value(g, instruction->value);
|
||||
if (handle_is_ptr(instruction->value->value.type)) {
|
||||
return value;
|
||||
@ -2975,6 +3020,15 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst
|
||||
add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, array_end);
|
||||
}
|
||||
}
|
||||
if (!type_has_bits(array_type)) {
|
||||
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_len_index, "");
|
||||
|
||||
// TODO if debug safety is on, store 0xaaaaaaa in ptr field
|
||||
LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
|
||||
gen_store_untyped(g, len_value, len_field_ptr, 0, false);
|
||||
return tmp_struct_ptr;
|
||||
}
|
||||
|
||||
|
||||
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_ptr_index, "");
|
||||
LLVMValueRef indices[] = {
|
||||
@ -3473,8 +3527,6 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
||||
case IrInstructionIdCheckStatementIsVoid:
|
||||
case IrInstructionIdTypeName:
|
||||
case IrInstructionIdCanImplicitCast:
|
||||
case IrInstructionIdSetGlobalSection:
|
||||
case IrInstructionIdSetGlobalLinkage:
|
||||
case IrInstructionIdDeclRef:
|
||||
case IrInstructionIdSwitchVar:
|
||||
case IrInstructionIdOffsetOf:
|
||||
@ -3485,6 +3537,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
||||
case IrInstructionIdSetAlignStack:
|
||||
case IrInstructionIdArgType:
|
||||
case IrInstructionIdTagType:
|
||||
case IrInstructionIdExport:
|
||||
zig_unreachable();
|
||||
case IrInstructionIdReturn:
|
||||
return ir_render_return(g, executable, (IrInstructionReturn *)instruction);
|
||||
@ -3747,17 +3800,26 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con
|
||||
case TypeTableEntryIdStruct:
|
||||
{
|
||||
assert(type_entry->data.structure.layout == ContainerLayoutPacked);
|
||||
bool is_big_endian = g->is_big_endian; // TODO get endianness from struct type
|
||||
|
||||
LLVMValueRef val = LLVMConstInt(big_int_type_ref, 0, false);
|
||||
size_t used_bits = 0;
|
||||
for (size_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) {
|
||||
TypeStructField *field = &type_entry->data.structure.fields[i];
|
||||
if (field->gen_index == SIZE_MAX) {
|
||||
continue;
|
||||
}
|
||||
LLVMValueRef child_val = pack_const_int(g, big_int_type_ref, &const_val->data.x_struct.fields[i]);
|
||||
LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, field->packed_bits_size, false);
|
||||
val = LLVMConstShl(val, shift_amt);
|
||||
val = LLVMConstOr(val, child_val);
|
||||
if (is_big_endian) {
|
||||
LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, field->packed_bits_size, false);
|
||||
val = LLVMConstShl(val, shift_amt);
|
||||
val = LLVMConstOr(val, child_val);
|
||||
} else {
|
||||
LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, used_bits, false);
|
||||
LLVMValueRef child_val_shifted = LLVMConstShl(child_val, shift_amt);
|
||||
val = LLVMConstOr(val, child_val_shifted);
|
||||
used_bits += field->packed_bits_size;
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
@ -3882,9 +3944,11 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
|
||||
fields[type_struct_field->gen_index] = val;
|
||||
make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(field_val->type, val);
|
||||
} else {
|
||||
bool is_big_endian = g->is_big_endian; // TODO get endianness from struct type
|
||||
LLVMTypeRef big_int_type_ref = LLVMStructGetTypeAtIndex(type_entry->type_ref,
|
||||
(unsigned)type_struct_field->gen_index);
|
||||
LLVMValueRef val = LLVMConstInt(big_int_type_ref, 0, false);
|
||||
size_t used_bits = 0;
|
||||
for (size_t i = src_field_index; i < src_field_index_end; i += 1) {
|
||||
TypeStructField *it_field = &type_entry->data.structure.fields[i];
|
||||
if (it_field->gen_index == SIZE_MAX) {
|
||||
@ -3892,10 +3956,17 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
|
||||
}
|
||||
LLVMValueRef child_val = pack_const_int(g, big_int_type_ref,
|
||||
&const_val->data.x_struct.fields[i]);
|
||||
LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref,
|
||||
it_field->packed_bits_size, false);
|
||||
val = LLVMConstShl(val, shift_amt);
|
||||
val = LLVMConstOr(val, child_val);
|
||||
if (is_big_endian) {
|
||||
LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref,
|
||||
it_field->packed_bits_size, false);
|
||||
val = LLVMConstShl(val, shift_amt);
|
||||
val = LLVMConstOr(val, child_val);
|
||||
} else {
|
||||
LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, used_bits, false);
|
||||
LLVMValueRef child_val_shifted = LLVMConstShl(child_val, shift_amt);
|
||||
val = LLVMConstOr(val, child_val_shifted);
|
||||
used_bits += it_field->packed_bits_size;
|
||||
}
|
||||
}
|
||||
fields[type_struct_field->gen_index] = val;
|
||||
}
|
||||
@ -3946,8 +4017,6 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
|
||||
case TypeTableEntryIdUnion:
|
||||
{
|
||||
LLVMTypeRef union_type_ref = type_entry->data.unionation.union_type_ref;
|
||||
ConstExprValue *payload_value = const_val->data.x_union.payload;
|
||||
assert(payload_value != nullptr);
|
||||
|
||||
if (type_entry->data.unionation.gen_field_count == 0) {
|
||||
if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) {
|
||||
@ -3960,7 +4029,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
|
||||
|
||||
LLVMValueRef union_value_ref;
|
||||
bool make_unnamed_struct;
|
||||
if (!type_has_bits(payload_value->type)) {
|
||||
ConstExprValue *payload_value = const_val->data.x_union.payload;
|
||||
if (payload_value == nullptr || !type_has_bits(payload_value->type)) {
|
||||
if (type_entry->data.unionation.gen_tag_index == SIZE_MAX)
|
||||
return LLVMGetUndef(type_entry->type_ref);
|
||||
|
||||
@ -4635,6 +4705,7 @@ static const uint8_t int_sizes_in_bits[] = {
|
||||
7,
|
||||
8,
|
||||
16,
|
||||
29,
|
||||
32,
|
||||
64,
|
||||
128,
|
||||
@ -4955,8 +5026,6 @@ static void define_builtin_fns(CodeGen *g) {
|
||||
create_builtin_fn(g, BuiltinFnIdIntType, "IntType", 2); // TODO rename to Int
|
||||
create_builtin_fn(g, BuiltinFnIdSetDebugSafety, "setDebugSafety", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdSetFloatMode, "setFloatMode", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdSetGlobalSection, "setGlobalSection", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdSetGlobalLinkage, "setGlobalLinkage", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdPanic, "panic", 1);
|
||||
create_builtin_fn(g, BuiltinFnIdPtrCast, "ptrCast", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdBitCast, "bitCast", 2);
|
||||
@ -4972,6 +5041,7 @@ static void define_builtin_fns(CodeGen *g) {
|
||||
create_builtin_fn(g, BuiltinFnIdRem, "rem", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdMod, "mod", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX);
|
||||
create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX);
|
||||
create_builtin_fn(g, BuiltinFnIdTypeId, "typeId", 1);
|
||||
create_builtin_fn(g, BuiltinFnIdShlExact, "shlExact", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdShrExact, "shrExact", 2);
|
||||
@ -4980,6 +5050,7 @@ static void define_builtin_fns(CodeGen *g) {
|
||||
create_builtin_fn(g, BuiltinFnIdOpaqueType, "OpaqueType", 0);
|
||||
create_builtin_fn(g, BuiltinFnIdSetAlignStack, "setAlignStack", 1);
|
||||
create_builtin_fn(g, BuiltinFnIdArgType, "ArgType", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdExport, "export", 3);
|
||||
}
|
||||
|
||||
static const char *bool_to_str(bool b) {
|
||||
@ -5298,18 +5369,19 @@ void codegen_translate_c(CodeGen *g, Buf *full_path) {
|
||||
|
||||
ZigList<ErrorMsg *> errors = {0};
|
||||
int err = parse_h_file(import, &errors, buf_ptr(full_path), g, nullptr);
|
||||
if (err) {
|
||||
fprintf(stderr, "unable to parse C file: %s\n", err_str(err));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
if (err == ErrorCCompileErrors && errors.length > 0) {
|
||||
for (size_t i = 0; i < errors.length; i += 1) {
|
||||
ErrorMsg *err_msg = errors.at(i);
|
||||
print_err_msg(err_msg, g->err_color);
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (err) {
|
||||
fprintf(stderr, "unable to parse C file: %s\n", err_str(err));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static ImportTableEntry *add_special_code(CodeGen *g, PackageTableEntry *package, const char *basename) {
|
||||
@ -5417,6 +5489,27 @@ static void gen_root_source(CodeGen *g) {
|
||||
assert(g->root_out_name);
|
||||
assert(g->out_type != OutTypeUnknown);
|
||||
|
||||
{
|
||||
// Zig has lazy top level definitions. Here we semantically analyze the panic function.
|
||||
ImportTableEntry *import_with_panic;
|
||||
if (g->have_pub_panic) {
|
||||
import_with_panic = g->root_import;
|
||||
} else {
|
||||
g->panic_package = create_panic_pkg(g);
|
||||
import_with_panic = add_special_code(g, g->panic_package, "panic.zig");
|
||||
}
|
||||
scan_import(g, import_with_panic);
|
||||
Tld *panic_tld = find_decl(g, &import_with_panic->decls_scope->base, buf_create_from_str("panic"));
|
||||
assert(panic_tld != nullptr);
|
||||
resolve_top_level_decl(g, panic_tld, false, nullptr);
|
||||
}
|
||||
|
||||
|
||||
if (!g->error_during_imports) {
|
||||
semantic_analyze(g);
|
||||
}
|
||||
report_errors_and_maybe_exit(g);
|
||||
|
||||
if (!g->is_test_build && g->zig_target.os != ZigLLVM_UnknownOS &&
|
||||
!g->have_c_main && !g->have_winmain && !g->have_winmain_crt_startup &&
|
||||
((g->have_pub_main && g->out_type == OutTypeObj) || g->out_type == OutTypeExe))
|
||||
@ -5426,20 +5519,6 @@ static void gen_root_source(CodeGen *g) {
|
||||
if (g->zig_target.os == ZigLLVM_Win32 && !g->have_dllmain_crt_startup && g->out_type == OutTypeLib) {
|
||||
g->bootstrap_import = add_special_code(g, create_bootstrap_pkg(g, g->root_package), "bootstrap_lib.zig");
|
||||
}
|
||||
ImportTableEntry *import_with_panic;
|
||||
if (g->have_pub_panic) {
|
||||
import_with_panic = g->root_import;
|
||||
} else {
|
||||
g->panic_package = create_panic_pkg(g);
|
||||
import_with_panic = add_special_code(g, g->panic_package, "panic.zig");
|
||||
}
|
||||
// Zig has lazy top level definitions. Here we semantically analyze the panic function.
|
||||
{
|
||||
scan_import(g, import_with_panic);
|
||||
Tld *panic_tld = find_decl(g, &import_with_panic->decls_scope->base, buf_create_from_str("panic"));
|
||||
assert(panic_tld != nullptr);
|
||||
resolve_top_level_decl(g, panic_tld, false, nullptr);
|
||||
}
|
||||
|
||||
if (!g->error_during_imports) {
|
||||
semantic_analyze(g);
|
||||
@ -5666,7 +5745,7 @@ static void gen_h_file(CodeGen *g) {
|
||||
for (size_t fn_def_i = 0; fn_def_i < g->fn_defs.length; fn_def_i += 1) {
|
||||
FnTableEntry *fn_table_entry = g->fn_defs.at(fn_def_i);
|
||||
|
||||
if (fn_table_entry->linkage == GlobalLinkageIdInternal)
|
||||
if (fn_table_entry->export_list.length == 0)
|
||||
continue;
|
||||
|
||||
FnTypeId *fn_type_id = &fn_table_entry->type_entry->data.fn.fn_type_id;
|
||||
|
||||
1085
src/ir.cpp
1085
src/ir.cpp
File diff suppressed because it is too large
Load Diff
@ -886,8 +886,12 @@ static void ir_print_can_implicit_cast(IrPrint *irp, IrInstructionCanImplicitCas
|
||||
}
|
||||
|
||||
static void ir_print_ptr_type_of(IrPrint *irp, IrInstructionPtrTypeOf *instruction) {
|
||||
fprintf(irp->f, "&align ");
|
||||
ir_print_other_instruction(irp, instruction->align_value);
|
||||
fprintf(irp->f, "&");
|
||||
if (instruction->align_value != nullptr) {
|
||||
fprintf(irp->f, "align(");
|
||||
ir_print_other_instruction(irp, instruction->align_value);
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
const char *const_str = instruction->is_const ? "const " : "";
|
||||
const char *volatile_str = instruction->is_volatile ? "volatile " : "";
|
||||
fprintf(irp->f, ":%" PRIu32 ":%" PRIu32 " %s%s", instruction->bit_offset_start, instruction->bit_offset_end,
|
||||
@ -895,19 +899,6 @@ static void ir_print_ptr_type_of(IrPrint *irp, IrInstructionPtrTypeOf *instructi
|
||||
ir_print_other_instruction(irp, instruction->child_type);
|
||||
}
|
||||
|
||||
static void ir_print_set_global_section(IrPrint *irp, IrInstructionSetGlobalSection *instruction) {
|
||||
fprintf(irp->f, "@setGlobalSection(%s,", buf_ptr(instruction->tld->name));
|
||||
ir_print_other_instruction(irp, instruction->value);
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_set_global_linkage(IrPrint *irp, IrInstructionSetGlobalLinkage *instruction) {
|
||||
fprintf(irp->f, "@setGlobalLinkage(%s,", buf_ptr(instruction->tld->name));
|
||||
ir_print_other_instruction(irp, instruction->value);
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
|
||||
static void ir_print_decl_ref(IrPrint *irp, IrInstructionDeclRef *instruction) {
|
||||
const char *ptr_str = instruction->lval.is_ptr ? "ptr " : "";
|
||||
const char *const_str = instruction->lval.is_const ? "const " : "";
|
||||
@ -987,6 +978,24 @@ static void ir_print_enum_tag_type(IrPrint *irp, IrInstructionTagType *instructi
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_export(IrPrint *irp, IrInstructionExport *instruction) {
|
||||
if (instruction->linkage == nullptr) {
|
||||
fprintf(irp->f, "@export(");
|
||||
ir_print_other_instruction(irp, instruction->name);
|
||||
fprintf(irp->f, ",");
|
||||
ir_print_other_instruction(irp, instruction->target);
|
||||
fprintf(irp->f, ")");
|
||||
} else {
|
||||
fprintf(irp->f, "@exportWithLinkage(");
|
||||
ir_print_other_instruction(irp, instruction->name);
|
||||
fprintf(irp->f, ",");
|
||||
ir_print_other_instruction(irp, instruction->target);
|
||||
fprintf(irp->f, ",");
|
||||
ir_print_other_instruction(irp, instruction->linkage);
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||
ir_print_prefix(irp, instruction);
|
||||
@ -1263,12 +1272,6 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||
case IrInstructionIdPtrTypeOf:
|
||||
ir_print_ptr_type_of(irp, (IrInstructionPtrTypeOf *)instruction);
|
||||
break;
|
||||
case IrInstructionIdSetGlobalSection:
|
||||
ir_print_set_global_section(irp, (IrInstructionSetGlobalSection *)instruction);
|
||||
break;
|
||||
case IrInstructionIdSetGlobalLinkage:
|
||||
ir_print_set_global_linkage(irp, (IrInstructionSetGlobalLinkage *)instruction);
|
||||
break;
|
||||
case IrInstructionIdDeclRef:
|
||||
ir_print_decl_ref(irp, (IrInstructionDeclRef *)instruction);
|
||||
break;
|
||||
@ -1302,6 +1305,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||
case IrInstructionIdTagType:
|
||||
ir_print_enum_tag_type(irp, (IrInstructionTagType *)instruction);
|
||||
break;
|
||||
case IrInstructionIdExport:
|
||||
ir_print_export(irp, (IrInstructionExport *)instruction);
|
||||
break;
|
||||
}
|
||||
fprintf(irp->f, "\n");
|
||||
}
|
||||
|
||||
415
src/parser.cpp
415
src/parser.cpp
@ -632,27 +632,6 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m
|
||||
return node;
|
||||
}
|
||||
|
||||
/*
|
||||
GotoExpression = "goto" Symbol
|
||||
*/
|
||||
static AstNode *ast_parse_goto_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
||||
Token *goto_token = &pc->tokens->at(*token_index);
|
||||
if (goto_token->id == TokenIdKeywordGoto) {
|
||||
*token_index += 1;
|
||||
} else if (mandatory) {
|
||||
ast_expect_token(pc, goto_token, TokenIdKeywordGoto);
|
||||
zig_unreachable();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AstNode *node = ast_create_node(pc, NodeTypeGoto, goto_token);
|
||||
|
||||
Token *dest_symbol = ast_eat_token(pc, token_index, TokenIdSymbol);
|
||||
node->data.goto_expr.name = token_buf(dest_symbol);
|
||||
return node;
|
||||
}
|
||||
|
||||
/*
|
||||
CompTimeExpression(body) = "comptime" body
|
||||
*/
|
||||
@ -676,8 +655,8 @@ static AstNode *ast_parse_comptime_expr(ParseContext *pc, size_t *token_index, b
|
||||
}
|
||||
|
||||
/*
|
||||
PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | (option("extern") FnProto) | AsmExpression | ("error" "." Symbol) | ContainerDecl
|
||||
KeywordLiteral = "true" | "false" | "null" | "continue" | "undefined" | "error" | "this" | "unreachable"
|
||||
PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." Symbol) | ContainerDecl | ("continue" option(":" Symbol))
|
||||
KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "unreachable"
|
||||
*/
|
||||
static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
@ -721,6 +700,12 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bo
|
||||
} else if (token->id == TokenIdKeywordContinue) {
|
||||
AstNode *node = ast_create_node(pc, NodeTypeContinue, token);
|
||||
*token_index += 1;
|
||||
Token *maybe_colon_token = &pc->tokens->at(*token_index);
|
||||
if (maybe_colon_token->id == TokenIdColon) {
|
||||
*token_index += 1;
|
||||
Token *name = ast_eat_token(pc, token_index, TokenIdSymbol);
|
||||
node->data.continue_expr.name = token_buf(name);
|
||||
}
|
||||
return node;
|
||||
} else if (token->id == TokenIdKeywordUndefined) {
|
||||
AstNode *node = ast_create_node(pc, NodeTypeUndefinedLiteral, token);
|
||||
@ -740,9 +725,21 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bo
|
||||
return node;
|
||||
} else if (token->id == TokenIdAtSign) {
|
||||
*token_index += 1;
|
||||
Token *name_tok = ast_eat_token(pc, token_index, TokenIdSymbol);
|
||||
Token *name_tok = &pc->tokens->at(*token_index);
|
||||
Buf *name_buf;
|
||||
if (name_tok->id == TokenIdKeywordExport) {
|
||||
name_buf = buf_create_from_str("export");
|
||||
*token_index += 1;
|
||||
} else if (name_tok->id == TokenIdSymbol) {
|
||||
name_buf = token_buf(name_tok);
|
||||
*token_index += 1;
|
||||
} else {
|
||||
ast_expect_token(pc, name_tok, TokenIdSymbol);
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
AstNode *name_node = ast_create_node(pc, NodeTypeSymbol, name_tok);
|
||||
name_node->data.symbol_expr.symbol = token_buf(name_tok);
|
||||
name_node->data.symbol_expr.symbol = name_buf;
|
||||
|
||||
AstNode *node = ast_create_node(pc, NodeTypeFnCallExpr, token);
|
||||
node->data.fn_call_expr.fn_ref_expr = name_node;
|
||||
@ -751,27 +748,25 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bo
|
||||
node->data.fn_call_expr.is_builtin = true;
|
||||
|
||||
return node;
|
||||
} else if (token->id == TokenIdSymbol) {
|
||||
}
|
||||
|
||||
AstNode *block_expr_node = ast_parse_block_expr(pc, token_index, false);
|
||||
if (block_expr_node) {
|
||||
return block_expr_node;
|
||||
}
|
||||
|
||||
if (token->id == TokenIdSymbol) {
|
||||
*token_index += 1;
|
||||
AstNode *node = ast_create_node(pc, NodeTypeSymbol, token);
|
||||
node->data.symbol_expr.symbol = token_buf(token);
|
||||
return node;
|
||||
}
|
||||
|
||||
AstNode *goto_node = ast_parse_goto_expr(pc, token_index, false);
|
||||
if (goto_node)
|
||||
return goto_node;
|
||||
|
||||
AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false);
|
||||
if (grouped_expr_node) {
|
||||
return grouped_expr_node;
|
||||
}
|
||||
|
||||
AstNode *block_expr_node = ast_parse_block_expr(pc, token_index, false);
|
||||
if (block_expr_node) {
|
||||
return block_expr_node;
|
||||
}
|
||||
|
||||
AstNode *array_type_node = ast_parse_array_type_expr(pc, token_index, false);
|
||||
if (array_type_node) {
|
||||
return array_type_node;
|
||||
@ -791,13 +786,6 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bo
|
||||
if (container_decl)
|
||||
return container_decl;
|
||||
|
||||
if (token->id == TokenIdKeywordExtern) {
|
||||
*token_index += 1;
|
||||
AstNode *node = ast_parse_fn_proto(pc, token_index, true, VisibModPrivate);
|
||||
node->data.fn_proto.is_extern = true;
|
||||
return node;
|
||||
}
|
||||
|
||||
if (!mandatory)
|
||||
return nullptr;
|
||||
|
||||
@ -1483,7 +1471,7 @@ static AstNode *ast_parse_return_expr(ParseContext *pc, size_t *token_index) {
|
||||
}
|
||||
|
||||
/*
|
||||
BreakExpression : "break" option(Expression)
|
||||
BreakExpression = "break" option(":" Symbol) option(Expression)
|
||||
*/
|
||||
static AstNode *ast_parse_break_expr(ParseContext *pc, size_t *token_index) {
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
@ -1493,8 +1481,15 @@ static AstNode *ast_parse_break_expr(ParseContext *pc, size_t *token_index) {
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AstNode *node = ast_create_node(pc, NodeTypeBreak, token);
|
||||
|
||||
Token *maybe_colon_token = &pc->tokens->at(*token_index);
|
||||
if (maybe_colon_token->id == TokenIdColon) {
|
||||
*token_index += 1;
|
||||
Token *name = ast_eat_token(pc, token_index, TokenIdSymbol);
|
||||
node->data.break_expr.name = token_buf(name);
|
||||
}
|
||||
|
||||
node->data.break_expr.expr = ast_parse_expression(pc, token_index, false);
|
||||
|
||||
return node;
|
||||
@ -1534,38 +1529,20 @@ static AstNode *ast_parse_defer_expr(ParseContext *pc, size_t *token_index) {
|
||||
}
|
||||
|
||||
/*
|
||||
VariableDeclaration = option("comptime") ("var" | "const") Symbol option(":" TypeExpr) option("align" "(" Expression ")") "=" Expression
|
||||
VariableDeclaration = ("var" | "const") Symbol option(":" TypeExpr) option("align" "(" Expression ")") "=" Expression
|
||||
*/
|
||||
static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, size_t *token_index, bool mandatory,
|
||||
VisibMod visib_mod)
|
||||
VisibMod visib_mod, bool is_comptime, bool is_export)
|
||||
{
|
||||
Token *first_token = &pc->tokens->at(*token_index);
|
||||
Token *var_token;
|
||||
|
||||
bool is_const;
|
||||
bool is_comptime;
|
||||
if (first_token->id == TokenIdKeywordCompTime) {
|
||||
is_comptime = true;
|
||||
var_token = &pc->tokens->at(*token_index + 1);
|
||||
|
||||
if (var_token->id == TokenIdKeywordVar) {
|
||||
is_const = false;
|
||||
} else if (var_token->id == TokenIdKeywordConst) {
|
||||
is_const = true;
|
||||
} else if (mandatory) {
|
||||
ast_invalid_token_error(pc, var_token);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
*token_index += 2;
|
||||
} else if (first_token->id == TokenIdKeywordVar) {
|
||||
is_comptime = false;
|
||||
if (first_token->id == TokenIdKeywordVar) {
|
||||
is_const = false;
|
||||
var_token = first_token;
|
||||
*token_index += 1;
|
||||
} else if (first_token->id == TokenIdKeywordConst) {
|
||||
is_comptime = false;
|
||||
is_const = true;
|
||||
var_token = first_token;
|
||||
*token_index += 1;
|
||||
@ -1577,7 +1554,8 @@ static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, size_t *to
|
||||
|
||||
AstNode *node = ast_create_node(pc, NodeTypeVariableDeclaration, var_token);
|
||||
|
||||
node->data.variable_declaration.is_inline = is_comptime;
|
||||
node->data.variable_declaration.is_comptime = is_comptime;
|
||||
node->data.variable_declaration.is_export = is_export;
|
||||
node->data.variable_declaration.is_const = is_const;
|
||||
node->data.variable_declaration.visib_mod = visib_mod;
|
||||
|
||||
@ -1600,6 +1578,14 @@ static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, size_t *to
|
||||
next_token = &pc->tokens->at(*token_index);
|
||||
}
|
||||
|
||||
if (next_token->id == TokenIdKeywordSection) {
|
||||
*token_index += 1;
|
||||
ast_eat_token(pc, token_index, TokenIdLParen);
|
||||
node->data.variable_declaration.section_expr = ast_parse_expression(pc, token_index, true);
|
||||
ast_eat_token(pc, token_index, TokenIdRParen);
|
||||
next_token = &pc->tokens->at(*token_index);
|
||||
}
|
||||
|
||||
if (next_token->id == TokenIdEq) {
|
||||
*token_index += 1;
|
||||
node->data.variable_declaration.expr = ast_parse_expression(pc, token_index, true);
|
||||
@ -1612,6 +1598,50 @@ static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, size_t *to
|
||||
return node;
|
||||
}
|
||||
|
||||
/*
|
||||
GlobalVarDecl = option("export") VariableDeclaration ";"
|
||||
*/
|
||||
static AstNode *ast_parse_global_var_decl(ParseContext *pc, size_t *token_index, VisibMod visib_mod) {
|
||||
Token *first_token = &pc->tokens->at(*token_index);
|
||||
|
||||
bool is_export = false;;
|
||||
if (first_token->id == TokenIdKeywordExport) {
|
||||
*token_index += 1;
|
||||
is_export = true;
|
||||
}
|
||||
|
||||
AstNode *node = ast_parse_variable_declaration_expr(pc, token_index, false, visib_mod, false, is_export);
|
||||
if (node == nullptr) {
|
||||
if (is_export) {
|
||||
*token_index -= 1;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/*
|
||||
LocalVarDecl = option("comptime") VariableDeclaration
|
||||
*/
|
||||
static AstNode *ast_parse_local_var_decl(ParseContext *pc, size_t *token_index) {
|
||||
Token *first_token = &pc->tokens->at(*token_index);
|
||||
|
||||
bool is_comptime = false;;
|
||||
if (first_token->id == TokenIdKeywordCompTime) {
|
||||
*token_index += 1;
|
||||
is_comptime = true;
|
||||
}
|
||||
|
||||
AstNode *node = ast_parse_variable_declaration_expr(pc, token_index, false, VisibModPrivate, is_comptime, false);
|
||||
if (node == nullptr) {
|
||||
if (is_comptime) {
|
||||
*token_index -= 1;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/*
|
||||
BoolOrExpression = BoolAndExpression "or" BoolOrExpression | BoolAndExpression
|
||||
*/
|
||||
@ -1638,35 +1668,53 @@ static AstNode *ast_parse_bool_or_expr(ParseContext *pc, size_t *token_index, bo
|
||||
}
|
||||
|
||||
/*
|
||||
WhileExpression(body) = option("inline") "while" "(" Expression ")" option("|" option("*") Symbol "|") option(":" "(" Expression ")") body option("else" option("|" Symbol "|") BlockExpression(body))
|
||||
WhileExpression(body) = option(Symbol ":") option("inline") "while" "(" Expression ")" option("|" option("*") Symbol "|") option(":" "(" Expression ")") body option("else" option("|" Symbol "|") BlockExpression(body))
|
||||
*/
|
||||
static AstNode *ast_parse_while_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
||||
Token *first_token = &pc->tokens->at(*token_index);
|
||||
Token *while_token;
|
||||
size_t orig_token_index = *token_index;
|
||||
|
||||
bool is_inline;
|
||||
if (first_token->id == TokenIdKeywordInline) {
|
||||
while_token = &pc->tokens->at(*token_index + 1);
|
||||
if (while_token->id == TokenIdKeywordWhile) {
|
||||
is_inline = true;
|
||||
*token_index += 2;
|
||||
Token *name_token = nullptr;
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
|
||||
if (token->id == TokenIdSymbol) {
|
||||
*token_index += 1;
|
||||
Token *colon_token = &pc->tokens->at(*token_index);
|
||||
if (colon_token->id == TokenIdColon) {
|
||||
*token_index += 1;
|
||||
name_token = token;
|
||||
token = &pc->tokens->at(*token_index);
|
||||
} else if (mandatory) {
|
||||
ast_expect_token(pc, while_token, TokenIdKeywordWhile);
|
||||
ast_expect_token(pc, colon_token, TokenIdColon);
|
||||
zig_unreachable();
|
||||
} else {
|
||||
*token_index = orig_token_index;
|
||||
return nullptr;
|
||||
}
|
||||
} else if (first_token->id == TokenIdKeywordWhile) {
|
||||
while_token = first_token;
|
||||
is_inline = false;
|
||||
}
|
||||
|
||||
bool is_inline = false;
|
||||
if (token->id == TokenIdKeywordInline) {
|
||||
is_inline = true;
|
||||
*token_index += 1;
|
||||
token = &pc->tokens->at(*token_index);
|
||||
}
|
||||
|
||||
Token *while_token;
|
||||
if (token->id == TokenIdKeywordWhile) {
|
||||
while_token = token;
|
||||
*token_index += 1;
|
||||
} else if (mandatory) {
|
||||
ast_expect_token(pc, first_token, TokenIdKeywordWhile);
|
||||
ast_expect_token(pc, token, TokenIdKeywordWhile);
|
||||
zig_unreachable();
|
||||
} else {
|
||||
*token_index = orig_token_index;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AstNode *node = ast_create_node(pc, NodeTypeWhileExpr, while_token);
|
||||
if (name_token != nullptr) {
|
||||
node->data.while_expr.name = token_buf(name_token);
|
||||
}
|
||||
node->data.while_expr.is_inline = is_inline;
|
||||
|
||||
ast_eat_token(pc, token_index, TokenIdLParen);
|
||||
@ -1726,36 +1774,53 @@ static AstNode *ast_parse_symbol(ParseContext *pc, size_t *token_index) {
|
||||
}
|
||||
|
||||
/*
|
||||
ForExpression(body) = option("inline") "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body option("else" BlockExpression(body))
|
||||
ForExpression(body) = option(Symbol ":") option("inline") "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body option("else" BlockExpression(body))
|
||||
*/
|
||||
static AstNode *ast_parse_for_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
||||
Token *first_token = &pc->tokens->at(*token_index);
|
||||
Token *for_token;
|
||||
size_t orig_token_index = *token_index;
|
||||
|
||||
bool is_inline;
|
||||
if (first_token->id == TokenIdKeywordInline) {
|
||||
is_inline = true;
|
||||
for_token = &pc->tokens->at(*token_index + 1);
|
||||
if (for_token->id == TokenIdKeywordFor) {
|
||||
*token_index += 2;
|
||||
Token *name_token = nullptr;
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
|
||||
if (token->id == TokenIdSymbol) {
|
||||
*token_index += 1;
|
||||
Token *colon_token = &pc->tokens->at(*token_index);
|
||||
if (colon_token->id == TokenIdColon) {
|
||||
*token_index += 1;
|
||||
name_token = token;
|
||||
token = &pc->tokens->at(*token_index);
|
||||
} else if (mandatory) {
|
||||
ast_expect_token(pc, first_token, TokenIdKeywordFor);
|
||||
ast_expect_token(pc, colon_token, TokenIdColon);
|
||||
zig_unreachable();
|
||||
} else {
|
||||
*token_index = orig_token_index;
|
||||
return nullptr;
|
||||
}
|
||||
} else if (first_token->id == TokenIdKeywordFor) {
|
||||
for_token = first_token;
|
||||
is_inline = false;
|
||||
}
|
||||
|
||||
bool is_inline = false;
|
||||
if (token->id == TokenIdKeywordInline) {
|
||||
is_inline = true;
|
||||
*token_index += 1;
|
||||
token = &pc->tokens->at(*token_index);
|
||||
}
|
||||
|
||||
Token *for_token;
|
||||
if (token->id == TokenIdKeywordFor) {
|
||||
for_token = token;
|
||||
*token_index += 1;
|
||||
} else if (mandatory) {
|
||||
ast_expect_token(pc, first_token, TokenIdKeywordFor);
|
||||
ast_expect_token(pc, token, TokenIdKeywordFor);
|
||||
zig_unreachable();
|
||||
} else {
|
||||
*token_index = orig_token_index;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AstNode *node = ast_create_node(pc, NodeTypeForExpr, for_token);
|
||||
if (name_token != nullptr) {
|
||||
node->data.for_expr.name = token_buf(name_token);
|
||||
}
|
||||
node->data.for_expr.is_inline = is_inline;
|
||||
|
||||
ast_eat_token(pc, token_index, TokenIdLParen);
|
||||
@ -2082,35 +2147,6 @@ static AstNode *ast_parse_expression(ParseContext *pc, size_t *token_index, bool
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
Label: token(Symbol) token(Colon)
|
||||
*/
|
||||
static AstNode *ast_parse_label(ParseContext *pc, size_t *token_index, bool mandatory) {
|
||||
Token *symbol_token = &pc->tokens->at(*token_index);
|
||||
if (symbol_token->id != TokenIdSymbol) {
|
||||
if (mandatory) {
|
||||
ast_expect_token(pc, symbol_token, TokenIdSymbol);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Token *colon_token = &pc->tokens->at(*token_index + 1);
|
||||
if (colon_token->id != TokenIdColon) {
|
||||
if (mandatory) {
|
||||
ast_expect_token(pc, colon_token, TokenIdColon);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
*token_index += 2;
|
||||
|
||||
AstNode *node = ast_create_node(pc, NodeTypeLabel, symbol_token);
|
||||
node->data.label.name = token_buf(symbol_token);
|
||||
return node;
|
||||
}
|
||||
|
||||
static bool statement_terminates_without_semicolon(AstNode *node) {
|
||||
switch (node->type) {
|
||||
case NodeTypeIfBoolExpr:
|
||||
@ -2135,7 +2171,6 @@ static bool statement_terminates_without_semicolon(AstNode *node) {
|
||||
return node->data.defer.expr->type == NodeTypeBlock;
|
||||
case NodeTypeSwitchExpr:
|
||||
case NodeTypeBlock:
|
||||
case NodeTypeLabel:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@ -2143,27 +2178,54 @@ static bool statement_terminates_without_semicolon(AstNode *node) {
|
||||
}
|
||||
|
||||
/*
|
||||
Block = "{" many(Statement) option(Expression) "}"
|
||||
Statement = Label | VariableDeclaration ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";"
|
||||
Block = option(Symbol ":") "{" many(Statement) "}"
|
||||
Statement = Label | VariableDeclaration ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";" | ExportDecl
|
||||
*/
|
||||
static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mandatory) {
|
||||
size_t orig_token_index = *token_index;
|
||||
|
||||
Token *name_token = nullptr;
|
||||
Token *last_token = &pc->tokens->at(*token_index);
|
||||
|
||||
if (last_token->id == TokenIdSymbol) {
|
||||
*token_index += 1;
|
||||
Token *colon_token = &pc->tokens->at(*token_index);
|
||||
if (colon_token->id == TokenIdColon) {
|
||||
*token_index += 1;
|
||||
name_token = last_token;
|
||||
last_token = &pc->tokens->at(*token_index);
|
||||
} else if (mandatory) {
|
||||
ast_expect_token(pc, colon_token, TokenIdColon);
|
||||
zig_unreachable();
|
||||
} else {
|
||||
*token_index = orig_token_index;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (last_token->id != TokenIdLBrace) {
|
||||
if (mandatory) {
|
||||
ast_expect_token(pc, last_token, TokenIdLBrace);
|
||||
} else {
|
||||
*token_index = orig_token_index;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
*token_index += 1;
|
||||
|
||||
AstNode *node = ast_create_node(pc, NodeTypeBlock, last_token);
|
||||
if (name_token != nullptr) {
|
||||
node->data.block.name = token_buf(name_token);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
AstNode *statement_node = ast_parse_label(pc, token_index, false);
|
||||
if (!statement_node)
|
||||
statement_node = ast_parse_variable_declaration_expr(pc, token_index, false, VisibModPrivate);
|
||||
last_token = &pc->tokens->at(*token_index);
|
||||
if (last_token->id == TokenIdRBrace) {
|
||||
*token_index += 1;
|
||||
return node;
|
||||
}
|
||||
|
||||
AstNode *statement_node = ast_parse_local_var_decl(pc, token_index);
|
||||
if (!statement_node)
|
||||
statement_node = ast_parse_defer_expr(pc, token_index);
|
||||
if (!statement_node)
|
||||
@ -2171,47 +2233,28 @@ static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mand
|
||||
if (!statement_node)
|
||||
statement_node = ast_parse_expression(pc, token_index, false);
|
||||
|
||||
bool semicolon_expected = true;
|
||||
if (statement_node) {
|
||||
node->data.block.statements.append(statement_node);
|
||||
if (statement_terminates_without_semicolon(statement_node)) {
|
||||
semicolon_expected = false;
|
||||
} else {
|
||||
if (statement_node->type == NodeTypeDefer) {
|
||||
// defer without a block body requires a semicolon
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
ast_expect_token(pc, token, TokenIdSemicolon);
|
||||
}
|
||||
}
|
||||
if (!statement_node) {
|
||||
ast_invalid_token_error(pc, last_token);
|
||||
}
|
||||
|
||||
node->data.block.last_statement_is_result_expression = statement_node && !(
|
||||
statement_node->type == NodeTypeLabel ||
|
||||
statement_node->type == NodeTypeDefer);
|
||||
node->data.block.statements.append(statement_node);
|
||||
|
||||
last_token = &pc->tokens->at(*token_index);
|
||||
if (last_token->id == TokenIdRBrace) {
|
||||
*token_index += 1;
|
||||
return node;
|
||||
} else if (!semicolon_expected) {
|
||||
continue;
|
||||
} else if (last_token->id == TokenIdSemicolon) {
|
||||
*token_index += 1;
|
||||
} else {
|
||||
ast_invalid_token_error(pc, last_token);
|
||||
if (!statement_terminates_without_semicolon(statement_node)) {
|
||||
ast_eat_token(pc, token_index, TokenIdSemicolon);
|
||||
}
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
/*
|
||||
FnProto = option("coldcc" | "nakedcc" | "stdcallcc") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("->" TypeExpr)
|
||||
FnProto = option("coldcc" | "nakedcc" | "stdcallcc" | "extern") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("->" TypeExpr)
|
||||
*/
|
||||
static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod) {
|
||||
Token *first_token = &pc->tokens->at(*token_index);
|
||||
Token *fn_token;
|
||||
|
||||
CallingConvention cc;
|
||||
bool is_extern = false;
|
||||
if (first_token->id == TokenIdKeywordColdCC) {
|
||||
*token_index += 1;
|
||||
fn_token = ast_eat_token(pc, token_index, TokenIdKeywordFn);
|
||||
@ -2224,6 +2267,21 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool m
|
||||
*token_index += 1;
|
||||
fn_token = ast_eat_token(pc, token_index, TokenIdKeywordFn);
|
||||
cc = CallingConventionStdcall;
|
||||
} else if (first_token->id == TokenIdKeywordExtern) {
|
||||
is_extern = true;
|
||||
*token_index += 1;
|
||||
Token *next_token = &pc->tokens->at(*token_index);
|
||||
if (next_token->id == TokenIdKeywordFn) {
|
||||
fn_token = next_token;
|
||||
*token_index += 1;
|
||||
} else if (mandatory) {
|
||||
ast_expect_token(pc, next_token, TokenIdKeywordFn);
|
||||
zig_unreachable();
|
||||
} else {
|
||||
*token_index -= 1;
|
||||
return nullptr;
|
||||
}
|
||||
cc = CallingConventionC;
|
||||
} else if (first_token->id == TokenIdKeywordFn) {
|
||||
fn_token = first_token;
|
||||
*token_index += 1;
|
||||
@ -2238,6 +2296,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool m
|
||||
AstNode *node = ast_create_node(pc, NodeTypeFnProto, fn_token);
|
||||
node->data.fn_proto.visib_mod = visib_mod;
|
||||
node->data.fn_proto.cc = cc;
|
||||
node->data.fn_proto.is_extern = is_extern;
|
||||
|
||||
Token *fn_name = &pc->tokens->at(*token_index);
|
||||
|
||||
@ -2259,6 +2318,14 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool m
|
||||
ast_eat_token(pc, token_index, TokenIdRParen);
|
||||
next_token = &pc->tokens->at(*token_index);
|
||||
}
|
||||
if (next_token->id == TokenIdKeywordSection) {
|
||||
*token_index += 1;
|
||||
ast_eat_token(pc, token_index, TokenIdLParen);
|
||||
|
||||
node->data.fn_proto.section_expr = ast_parse_expression(pc, token_index, true);
|
||||
ast_eat_token(pc, token_index, TokenIdRParen);
|
||||
next_token = &pc->tokens->at(*token_index);
|
||||
}
|
||||
if (next_token->id == TokenIdArrow) {
|
||||
*token_index += 1;
|
||||
node->data.fn_proto.return_type = ast_parse_type_expr(pc, token_index, false);
|
||||
@ -2270,35 +2337,35 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool m
|
||||
}
|
||||
|
||||
/*
|
||||
FnDef = option("inline" | "extern") FnProto Block
|
||||
FnDef = option("inline" | "export") FnProto Block
|
||||
*/
|
||||
static AstNode *ast_parse_fn_def(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod) {
|
||||
Token *first_token = &pc->tokens->at(*token_index);
|
||||
bool is_inline;
|
||||
bool is_extern;
|
||||
bool is_export;
|
||||
if (first_token->id == TokenIdKeywordInline) {
|
||||
*token_index += 1;
|
||||
is_inline = true;
|
||||
is_extern = false;
|
||||
} else if (first_token->id == TokenIdKeywordExtern) {
|
||||
is_export = false;
|
||||
} else if (first_token->id == TokenIdKeywordExport) {
|
||||
*token_index += 1;
|
||||
is_extern = true;
|
||||
is_export = true;
|
||||
is_inline = false;
|
||||
} else {
|
||||
is_inline = false;
|
||||
is_extern = false;
|
||||
is_export = false;
|
||||
}
|
||||
|
||||
AstNode *fn_proto = ast_parse_fn_proto(pc, token_index, mandatory, visib_mod);
|
||||
if (!fn_proto) {
|
||||
if (is_inline || is_extern) {
|
||||
if (is_inline || is_export) {
|
||||
*token_index -= 1;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
fn_proto->data.fn_proto.is_inline = is_inline;
|
||||
fn_proto->data.fn_proto.is_extern = is_extern;
|
||||
fn_proto->data.fn_proto.is_export = is_export;
|
||||
|
||||
Token *semi_token = &pc->tokens->at(*token_index);
|
||||
if (semi_token->id == TokenIdSemicolon) {
|
||||
@ -2344,7 +2411,7 @@ static AstNode *ast_parse_extern_decl(ParseContext *pc, size_t *token_index, boo
|
||||
return fn_proto_node;
|
||||
}
|
||||
|
||||
AstNode *var_decl_node = ast_parse_variable_declaration_expr(pc, token_index, false, visib_mod);
|
||||
AstNode *var_decl_node = ast_parse_variable_declaration_expr(pc, token_index, false, visib_mod, false, false);
|
||||
if (var_decl_node) {
|
||||
ast_eat_token(pc, token_index, TokenIdSemicolon);
|
||||
|
||||
@ -2447,9 +2514,6 @@ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index,
|
||||
if (visib_tok->id == TokenIdKeywordPub) {
|
||||
*token_index += 1;
|
||||
visib_mod = VisibModPub;
|
||||
} else if (visib_tok->id == TokenIdKeywordExport) {
|
||||
*token_index += 1;
|
||||
visib_mod = VisibModExport;
|
||||
} else {
|
||||
visib_mod = VisibModPrivate;
|
||||
}
|
||||
@ -2460,7 +2524,7 @@ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index,
|
||||
continue;
|
||||
}
|
||||
|
||||
AstNode *var_decl_node = ast_parse_variable_declaration_expr(pc, token_index, false, visib_mod);
|
||||
AstNode *var_decl_node = ast_parse_global_var_decl(pc, token_index, visib_mod);
|
||||
if (var_decl_node) {
|
||||
ast_eat_token(pc, token_index, TokenIdSemicolon);
|
||||
node->data.container_decl.decls.append(var_decl_node);
|
||||
@ -2553,7 +2617,7 @@ static AstNode *ast_parse_test_decl_node(ParseContext *pc, size_t *token_index)
|
||||
|
||||
/*
|
||||
TopLevelItem = ErrorValueDecl | CompTimeExpression(Block) | TopLevelDecl | TestDecl
|
||||
TopLevelDecl = option(VisibleMod) (FnDef | ExternDecl | GlobalVarDecl | UseDecl)
|
||||
TopLevelDecl = option("pub") (FnDef | ExternDecl | GlobalVarDecl | UseDecl)
|
||||
*/
|
||||
static void ast_parse_top_level_decls(ParseContext *pc, size_t *token_index, ZigList<AstNode *> *top_level_decls) {
|
||||
for (;;) {
|
||||
@ -2580,9 +2644,6 @@ static void ast_parse_top_level_decls(ParseContext *pc, size_t *token_index, Zig
|
||||
if (visib_tok->id == TokenIdKeywordPub) {
|
||||
*token_index += 1;
|
||||
visib_mod = VisibModPub;
|
||||
} else if (visib_tok->id == TokenIdKeywordExport) {
|
||||
*token_index += 1;
|
||||
visib_mod = VisibModExport;
|
||||
} else {
|
||||
visib_mod = VisibModPrivate;
|
||||
}
|
||||
@ -2605,7 +2666,7 @@ static void ast_parse_top_level_decls(ParseContext *pc, size_t *token_index, Zig
|
||||
continue;
|
||||
}
|
||||
|
||||
AstNode *var_decl_node = ast_parse_variable_declaration_expr(pc, token_index, false, visib_mod);
|
||||
AstNode *var_decl_node = ast_parse_global_var_decl(pc, token_index, visib_mod);
|
||||
if (var_decl_node) {
|
||||
ast_eat_token(pc, token_index, TokenIdSemicolon);
|
||||
top_level_decls->append(var_decl_node);
|
||||
@ -2669,6 +2730,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
|
||||
visit_field(&node->data.fn_proto.return_type, visit, context);
|
||||
visit_node_list(&node->data.fn_proto.params, visit, context);
|
||||
visit_field(&node->data.fn_proto.align_expr, visit, context);
|
||||
visit_field(&node->data.fn_proto.section_expr, visit, context);
|
||||
break;
|
||||
case NodeTypeFnDef:
|
||||
visit_field(&node->data.fn_def.fn_proto, visit, context);
|
||||
@ -2696,6 +2758,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
|
||||
visit_field(&node->data.variable_declaration.type, visit, context);
|
||||
visit_field(&node->data.variable_declaration.expr, visit, context);
|
||||
visit_field(&node->data.variable_declaration.align_expr, visit, context);
|
||||
visit_field(&node->data.variable_declaration.section_expr, visit, context);
|
||||
break;
|
||||
case NodeTypeErrorValueDecl:
|
||||
// none
|
||||
@ -2799,12 +2862,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
|
||||
visit_field(&node->data.switch_range.start, visit, context);
|
||||
visit_field(&node->data.switch_range.end, visit, context);
|
||||
break;
|
||||
case NodeTypeLabel:
|
||||
// none
|
||||
break;
|
||||
case NodeTypeGoto:
|
||||
// none
|
||||
break;
|
||||
case NodeTypeCompTime:
|
||||
visit_field(&node->data.comptime_expr.expr, visit, context);
|
||||
break;
|
||||
|
||||
@ -134,6 +134,7 @@ static const struct ZigKeyword zig_keywords[] = {
|
||||
{"packed", TokenIdKeywordPacked},
|
||||
{"pub", TokenIdKeywordPub},
|
||||
{"return", TokenIdKeywordReturn},
|
||||
{"section", TokenIdKeywordSection},
|
||||
{"stdcallcc", TokenIdKeywordStdcallCC},
|
||||
{"struct", TokenIdKeywordStruct},
|
||||
{"switch", TokenIdKeywordSwitch},
|
||||
@ -1533,6 +1534,7 @@ const char * token_name(TokenId id) {
|
||||
case TokenIdKeywordPacked: return "packed";
|
||||
case TokenIdKeywordPub: return "pub";
|
||||
case TokenIdKeywordReturn: return "return";
|
||||
case TokenIdKeywordSection: return "section";
|
||||
case TokenIdKeywordStdcallCC: return "stdcallcc";
|
||||
case TokenIdKeywordStruct: return "struct";
|
||||
case TokenIdKeywordSwitch: return "switch";
|
||||
|
||||
@ -47,6 +47,7 @@ enum TokenId {
|
||||
TokenIdFloatLiteral,
|
||||
TokenIdIntLiteral,
|
||||
TokenIdKeywordAlign,
|
||||
TokenIdKeywordSection,
|
||||
TokenIdKeywordAnd,
|
||||
TokenIdKeywordAsm,
|
||||
TokenIdKeywordBreak,
|
||||
|
||||
@ -73,7 +73,7 @@ struct Context {
|
||||
ImportTableEntry *import;
|
||||
ZigList<ErrorMsg *> *errors;
|
||||
VisibMod visib_mod;
|
||||
VisibMod export_visib_mod;
|
||||
bool want_export;
|
||||
AstNode *root;
|
||||
HashMap<const void *, AstNode *, ptr_hash, ptr_eq> decl_table;
|
||||
HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> macro_table;
|
||||
@ -104,10 +104,8 @@ static TransScopeRoot *trans_scope_root_create(Context *c);
|
||||
static TransScopeWhile *trans_scope_while_create(Context *c, TransScope *parent_scope);
|
||||
static TransScopeBlock *trans_scope_block_create(Context *c, TransScope *parent_scope);
|
||||
static TransScopeVar *trans_scope_var_create(Context *c, TransScope *parent_scope, Buf *wanted_name);
|
||||
static TransScopeSwitch *trans_scope_switch_create(Context *c, TransScope *parent_scope);
|
||||
|
||||
static TransScopeBlock *trans_scope_block_find(TransScope *scope);
|
||||
static TransScopeSwitch *trans_scope_switch_find(TransScope *scope);
|
||||
|
||||
static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl);
|
||||
static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl);
|
||||
@ -173,6 +171,28 @@ static AstNode * trans_create_node(Context *c, NodeType id) {
|
||||
return node;
|
||||
}
|
||||
|
||||
static AstNode *trans_create_node_break(Context *c, Buf *label_name, AstNode *value_node) {
|
||||
AstNode *node = trans_create_node(c, NodeTypeBreak);
|
||||
node->data.break_expr.name = label_name;
|
||||
node->data.break_expr.expr = value_node;
|
||||
return node;
|
||||
}
|
||||
|
||||
static AstNode *trans_create_node_return(Context *c, AstNode *value_node) {
|
||||
AstNode *node = trans_create_node(c, NodeTypeReturnExpr);
|
||||
node->data.return_expr.kind = ReturnKindUnconditional;
|
||||
node->data.return_expr.expr = value_node;
|
||||
return node;
|
||||
}
|
||||
|
||||
static AstNode *trans_create_node_if(Context *c, AstNode *cond_node, AstNode *then_node, AstNode *else_node) {
|
||||
AstNode *node = trans_create_node(c, NodeTypeIfBoolExpr);
|
||||
node->data.if_bool_expr.condition = cond_node;
|
||||
node->data.if_bool_expr.then_block = then_node;
|
||||
node->data.if_bool_expr.else_node = else_node;
|
||||
return node;
|
||||
}
|
||||
|
||||
static AstNode *trans_create_node_float_lit(Context *c, double value) {
|
||||
AstNode *node = trans_create_node(c, NodeTypeFloatLiteral);
|
||||
node->data.float_literal.bigfloat = allocate<BigFloat>(1);
|
||||
@ -257,18 +277,6 @@ static AstNode *trans_create_node_addr_of(Context *c, bool is_const, bool is_vol
|
||||
return node;
|
||||
}
|
||||
|
||||
static AstNode *trans_create_node_goto(Context *c, Buf *label_name) {
|
||||
AstNode *goto_node = trans_create_node(c, NodeTypeGoto);
|
||||
goto_node->data.goto_expr.name = label_name;
|
||||
return goto_node;
|
||||
}
|
||||
|
||||
static AstNode *trans_create_node_label(Context *c, Buf *label_name) {
|
||||
AstNode *label_node = trans_create_node(c, NodeTypeLabel);
|
||||
label_node->data.label.name = label_name;
|
||||
return label_node;
|
||||
}
|
||||
|
||||
static AstNode *trans_create_node_bool(Context *c, bool value) {
|
||||
AstNode *bool_node = trans_create_node(c, NodeTypeBoolLiteral);
|
||||
bool_node->data.bool_literal.value = value;
|
||||
@ -378,8 +386,7 @@ static AstNode *trans_create_node_inline_fn(Context *c, Buf *fn_name, AstNode *r
|
||||
|
||||
AstNode *block = trans_create_node(c, NodeTypeBlock);
|
||||
block->data.block.statements.resize(1);
|
||||
block->data.block.statements.items[0] = fn_call_node;
|
||||
block->data.block.last_statement_is_result_expression = true;
|
||||
block->data.block.statements.items[0] = trans_create_node_return(c, fn_call_node);
|
||||
|
||||
fn_def->data.fn_def.body = block;
|
||||
return fn_def;
|
||||
@ -1149,13 +1156,15 @@ static AstNode *trans_create_assign(Context *c, ResultUsed result_used, TransSco
|
||||
} else {
|
||||
// worst case
|
||||
// c: lhs = rhs
|
||||
// zig: {
|
||||
// zig: x: {
|
||||
// zig: const _tmp = rhs;
|
||||
// zig: lhs = _tmp;
|
||||
// zig: _tmp
|
||||
// zig: break :x _tmp
|
||||
// zig: }
|
||||
|
||||
TransScopeBlock *child_scope = trans_scope_block_create(c, scope);
|
||||
Buf *label_name = buf_create_from_str("x");
|
||||
child_scope->node->data.block.name = label_name;
|
||||
|
||||
// const _tmp = rhs;
|
||||
AstNode *rhs_node = trans_expr(c, ResultUsedYes, &child_scope->base, rhs, TransRValue);
|
||||
@ -1172,9 +1181,9 @@ static AstNode *trans_create_assign(Context *c, ResultUsed result_used, TransSco
|
||||
trans_create_node_bin_op(c, lhs_node, BinOpTypeAssign,
|
||||
trans_create_node_symbol(c, tmp_var_name)));
|
||||
|
||||
// _tmp
|
||||
child_scope->node->data.block.statements.append(trans_create_node_symbol(c, tmp_var_name));
|
||||
child_scope->node->data.block.last_statement_is_result_expression = true;
|
||||
// break :x _tmp
|
||||
AstNode *tmp_symbol_node = trans_create_node_symbol(c, tmp_var_name);
|
||||
child_scope->node->data.block.statements.append(trans_create_node_break(c, label_name, tmp_symbol_node));
|
||||
|
||||
return child_scope->node;
|
||||
}
|
||||
@ -1279,6 +1288,9 @@ static AstNode *trans_binary_operator(Context *c, ResultUsed result_used, TransS
|
||||
case BO_Comma:
|
||||
{
|
||||
TransScopeBlock *scope_block = trans_scope_block_create(c, scope);
|
||||
Buf *label_name = buf_create_from_str("x");
|
||||
scope_block->node->data.block.name = label_name;
|
||||
|
||||
AstNode *lhs = trans_expr(c, ResultUsedNo, &scope_block->base, stmt->getLHS(), TransRValue);
|
||||
if (lhs == nullptr)
|
||||
return nullptr;
|
||||
@ -1287,9 +1299,7 @@ static AstNode *trans_binary_operator(Context *c, ResultUsed result_used, TransS
|
||||
AstNode *rhs = trans_expr(c, result_used, &scope_block->base, stmt->getRHS(), TransRValue);
|
||||
if (rhs == nullptr)
|
||||
return nullptr;
|
||||
scope_block->node->data.block.statements.append(maybe_suppress_result(c, result_used, rhs));
|
||||
|
||||
scope_block->node->data.block.last_statement_is_result_expression = true;
|
||||
scope_block->node->data.block.statements.append(trans_create_node_break(c, label_name, maybe_suppress_result(c, result_used, rhs)));
|
||||
return scope_block->node;
|
||||
}
|
||||
case BO_MulAssign:
|
||||
@ -1329,14 +1339,16 @@ static AstNode *trans_create_compound_assign_shift(Context *c, ResultUsed result
|
||||
} else {
|
||||
// need more complexity. worst case, this looks like this:
|
||||
// c: lhs >>= rhs
|
||||
// zig: {
|
||||
// zig: x: {
|
||||
// zig: const _ref = &lhs;
|
||||
// zig: *_ref = result_type(operation_type(*_ref) >> u5(rhs));
|
||||
// zig: *_ref
|
||||
// zig: break :x *_ref
|
||||
// zig: }
|
||||
// where u5 is the appropriate type
|
||||
|
||||
TransScopeBlock *child_scope = trans_scope_block_create(c, scope);
|
||||
Buf *label_name = buf_create_from_str("x");
|
||||
child_scope->node->data.block.name = label_name;
|
||||
|
||||
// const _ref = &lhs;
|
||||
AstNode *lhs = trans_expr(c, ResultUsedYes, &child_scope->base, stmt->getLHS(), TransLValue);
|
||||
@ -1378,11 +1390,11 @@ static AstNode *trans_create_compound_assign_shift(Context *c, ResultUsed result
|
||||
child_scope->node->data.block.statements.append(assign_statement);
|
||||
|
||||
if (result_used == ResultUsedYes) {
|
||||
// *_ref
|
||||
// break :x *_ref
|
||||
child_scope->node->data.block.statements.append(
|
||||
trans_create_node_prefix_op(c, PrefixOpDereference,
|
||||
trans_create_node_symbol(c, tmp_var_name)));
|
||||
child_scope->node->data.block.last_statement_is_result_expression = true;
|
||||
trans_create_node_break(c, label_name,
|
||||
trans_create_node_prefix_op(c, PrefixOpDereference,
|
||||
trans_create_node_symbol(c, tmp_var_name))));
|
||||
}
|
||||
|
||||
return child_scope->node;
|
||||
@ -1403,13 +1415,15 @@ static AstNode *trans_create_compound_assign(Context *c, ResultUsed result_used,
|
||||
} else {
|
||||
// need more complexity. worst case, this looks like this:
|
||||
// c: lhs += rhs
|
||||
// zig: {
|
||||
// zig: x: {
|
||||
// zig: const _ref = &lhs;
|
||||
// zig: *_ref = *_ref + rhs;
|
||||
// zig: *_ref
|
||||
// zig: break :x *_ref
|
||||
// zig: }
|
||||
|
||||
TransScopeBlock *child_scope = trans_scope_block_create(c, scope);
|
||||
Buf *label_name = buf_create_from_str("x");
|
||||
child_scope->node->data.block.name = label_name;
|
||||
|
||||
// const _ref = &lhs;
|
||||
AstNode *lhs = trans_expr(c, ResultUsedYes, &child_scope->base, stmt->getLHS(), TransLValue);
|
||||
@ -1436,11 +1450,11 @@ static AstNode *trans_create_compound_assign(Context *c, ResultUsed result_used,
|
||||
rhs));
|
||||
child_scope->node->data.block.statements.append(assign_statement);
|
||||
|
||||
// *_ref
|
||||
// break :x *_ref
|
||||
child_scope->node->data.block.statements.append(
|
||||
trans_create_node_prefix_op(c, PrefixOpDereference,
|
||||
trans_create_node_symbol(c, tmp_var_name)));
|
||||
child_scope->node->data.block.last_statement_is_result_expression = true;
|
||||
trans_create_node_break(c, label_name,
|
||||
trans_create_node_prefix_op(c, PrefixOpDereference,
|
||||
trans_create_node_symbol(c, tmp_var_name))));
|
||||
|
||||
return child_scope->node;
|
||||
}
|
||||
@ -1735,13 +1749,15 @@ static AstNode *trans_create_post_crement(Context *c, ResultUsed result_used, Tr
|
||||
}
|
||||
// worst case
|
||||
// c: expr++
|
||||
// zig: {
|
||||
// zig: x: {
|
||||
// zig: const _ref = &expr;
|
||||
// zig: const _tmp = *_ref;
|
||||
// zig: *_ref += 1;
|
||||
// zig: _tmp
|
||||
// zig: break :x _tmp
|
||||
// zig: }
|
||||
TransScopeBlock *child_scope = trans_scope_block_create(c, scope);
|
||||
Buf *label_name = buf_create_from_str("x");
|
||||
child_scope->node->data.block.name = label_name;
|
||||
|
||||
// const _ref = &expr;
|
||||
AstNode *expr = trans_expr(c, ResultUsedYes, &child_scope->base, op_expr, TransLValue);
|
||||
@ -1767,9 +1783,8 @@ static AstNode *trans_create_post_crement(Context *c, ResultUsed result_used, Tr
|
||||
trans_create_node_unsigned(c, 1));
|
||||
child_scope->node->data.block.statements.append(assign_statement);
|
||||
|
||||
// _tmp
|
||||
child_scope->node->data.block.statements.append(trans_create_node_symbol(c, tmp_var_name));
|
||||
child_scope->node->data.block.last_statement_is_result_expression = true;
|
||||
// break :x _tmp
|
||||
child_scope->node->data.block.statements.append(trans_create_node_break(c, label_name, trans_create_node_symbol(c, tmp_var_name)));
|
||||
|
||||
return child_scope->node;
|
||||
}
|
||||
@ -1790,12 +1805,14 @@ static AstNode *trans_create_pre_crement(Context *c, ResultUsed result_used, Tra
|
||||
}
|
||||
// worst case
|
||||
// c: ++expr
|
||||
// zig: {
|
||||
// zig: x: {
|
||||
// zig: const _ref = &expr;
|
||||
// zig: *_ref += 1;
|
||||
// zig: *_ref
|
||||
// zig: break :x *_ref
|
||||
// zig: }
|
||||
TransScopeBlock *child_scope = trans_scope_block_create(c, scope);
|
||||
Buf *label_name = buf_create_from_str("x");
|
||||
child_scope->node->data.block.name = label_name;
|
||||
|
||||
// const _ref = &expr;
|
||||
AstNode *expr = trans_expr(c, ResultUsedYes, &child_scope->base, op_expr, TransLValue);
|
||||
@ -1814,11 +1831,10 @@ static AstNode *trans_create_pre_crement(Context *c, ResultUsed result_used, Tra
|
||||
trans_create_node_unsigned(c, 1));
|
||||
child_scope->node->data.block.statements.append(assign_statement);
|
||||
|
||||
// *_ref
|
||||
// break :x *_ref
|
||||
AstNode *deref_expr = trans_create_node_prefix_op(c, PrefixOpDereference,
|
||||
trans_create_node_symbol(c, ref_var_name));
|
||||
child_scope->node->data.block.statements.append(deref_expr);
|
||||
child_scope->node->data.block.last_statement_is_result_expression = true;
|
||||
child_scope->node->data.block.statements.append(trans_create_node_break(c, label_name, deref_expr));
|
||||
|
||||
return child_scope->node;
|
||||
}
|
||||
@ -2374,145 +2390,6 @@ static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const DoStmt
|
||||
return while_scope->node;
|
||||
}
|
||||
|
||||
static AstNode *trans_switch_stmt(Context *c, TransScope *parent_scope, const SwitchStmt *stmt) {
|
||||
TransScopeBlock *block_scope = trans_scope_block_create(c, parent_scope);
|
||||
|
||||
TransScopeSwitch *switch_scope;
|
||||
|
||||
const DeclStmt *var_decl_stmt = stmt->getConditionVariableDeclStmt();
|
||||
if (var_decl_stmt == nullptr) {
|
||||
switch_scope = trans_scope_switch_create(c, &block_scope->base);
|
||||
} else {
|
||||
AstNode *vars_node;
|
||||
TransScope *var_scope = trans_stmt(c, &block_scope->base, var_decl_stmt, &vars_node);
|
||||
if (var_scope == nullptr)
|
||||
return nullptr;
|
||||
if (vars_node != nullptr)
|
||||
block_scope->node->data.block.statements.append(vars_node);
|
||||
switch_scope = trans_scope_switch_create(c, var_scope);
|
||||
}
|
||||
block_scope->node->data.block.statements.append(switch_scope->switch_node);
|
||||
|
||||
// TODO avoid name collisions
|
||||
Buf *end_label_name = buf_create_from_str("end");
|
||||
switch_scope->end_label_name = end_label_name;
|
||||
|
||||
const Expr *cond_expr = stmt->getCond();
|
||||
assert(cond_expr != nullptr);
|
||||
|
||||
AstNode *expr_node = trans_expr(c, ResultUsedYes, &block_scope->base, cond_expr, TransRValue);
|
||||
if (expr_node == nullptr)
|
||||
return nullptr;
|
||||
switch_scope->switch_node->data.switch_expr.expr = expr_node;
|
||||
|
||||
AstNode *body_node;
|
||||
const Stmt *body_stmt = stmt->getBody();
|
||||
if (body_stmt->getStmtClass() == Stmt::CompoundStmtClass) {
|
||||
if (trans_compound_stmt_inline(c, &switch_scope->base, (const CompoundStmt *)body_stmt,
|
||||
block_scope->node, nullptr))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
TransScope *body_scope = trans_stmt(c, &switch_scope->base, body_stmt, &body_node);
|
||||
if (body_scope == nullptr)
|
||||
return nullptr;
|
||||
if (body_node != nullptr)
|
||||
block_scope->node->data.block.statements.append(body_node);
|
||||
}
|
||||
|
||||
if (!switch_scope->found_default && !stmt->isAllEnumCasesCovered()) {
|
||||
AstNode *prong_node = trans_create_node(c, NodeTypeSwitchProng);
|
||||
prong_node->data.switch_prong.expr = trans_create_node_goto(c, end_label_name);
|
||||
switch_scope->switch_node->data.switch_expr.prongs.append(prong_node);
|
||||
}
|
||||
|
||||
// This is necessary if the last switch case "falls through" the end of the switch block
|
||||
block_scope->node->data.block.statements.append(trans_create_node_goto(c, end_label_name));
|
||||
|
||||
block_scope->node->data.block.statements.append(trans_create_node_label(c, end_label_name));
|
||||
|
||||
return block_scope->node;
|
||||
}
|
||||
|
||||
static int trans_switch_case(Context *c, TransScope *parent_scope, const CaseStmt *stmt, AstNode **out_node,
|
||||
TransScope **out_scope)
|
||||
{
|
||||
*out_node = nullptr;
|
||||
|
||||
if (stmt->getRHS() != nullptr) {
|
||||
emit_warning(c, stmt->getLocStart(), "TODO support GNU switch case a ... b extension");
|
||||
return ErrorUnexpected;
|
||||
}
|
||||
|
||||
TransScopeSwitch *switch_scope = trans_scope_switch_find(parent_scope);
|
||||
assert(switch_scope != nullptr);
|
||||
|
||||
Buf *label_name = buf_sprintf("case_%" PRIu32, switch_scope->case_index);
|
||||
switch_scope->case_index += 1;
|
||||
|
||||
{
|
||||
// Add the prong
|
||||
AstNode *prong_node = trans_create_node(c, NodeTypeSwitchProng);
|
||||
AstNode *item_node = trans_expr(c, ResultUsedYes, &switch_scope->base, stmt->getLHS(), TransRValue);
|
||||
if (item_node == nullptr)
|
||||
return ErrorUnexpected;
|
||||
prong_node->data.switch_prong.items.append(item_node);
|
||||
|
||||
prong_node->data.switch_prong.expr = trans_create_node_goto(c, label_name);
|
||||
|
||||
switch_scope->switch_node->data.switch_expr.prongs.append(prong_node);
|
||||
}
|
||||
|
||||
TransScopeBlock *scope_block = trans_scope_block_find(parent_scope);
|
||||
scope_block->node->data.block.statements.append(trans_create_node_label(c, label_name));
|
||||
|
||||
AstNode *sub_stmt_node;
|
||||
TransScope *new_scope = trans_stmt(c, parent_scope, stmt->getSubStmt(), &sub_stmt_node);
|
||||
if (new_scope == nullptr)
|
||||
return ErrorUnexpected;
|
||||
if (sub_stmt_node != nullptr)
|
||||
scope_block->node->data.block.statements.append(sub_stmt_node);
|
||||
|
||||
*out_scope = new_scope;
|
||||
return ErrorNone;
|
||||
}
|
||||
|
||||
static int trans_switch_default(Context *c, TransScope *parent_scope, const DefaultStmt *stmt, AstNode **out_node,
|
||||
TransScope **out_scope)
|
||||
{
|
||||
*out_node = nullptr;
|
||||
|
||||
TransScopeSwitch *switch_scope = trans_scope_switch_find(parent_scope);
|
||||
assert(switch_scope != nullptr);
|
||||
|
||||
Buf *label_name = buf_sprintf("default");
|
||||
|
||||
{
|
||||
// Add the prong
|
||||
AstNode *prong_node = trans_create_node(c, NodeTypeSwitchProng);
|
||||
|
||||
prong_node->data.switch_prong.expr = trans_create_node_goto(c, label_name);
|
||||
|
||||
switch_scope->switch_node->data.switch_expr.prongs.append(prong_node);
|
||||
switch_scope->found_default = true;
|
||||
}
|
||||
|
||||
TransScopeBlock *scope_block = trans_scope_block_find(parent_scope);
|
||||
scope_block->node->data.block.statements.append(trans_create_node_label(c, label_name));
|
||||
|
||||
|
||||
AstNode *sub_stmt_node;
|
||||
TransScope *new_scope = trans_stmt(c, parent_scope, stmt->getSubStmt(), &sub_stmt_node);
|
||||
if (new_scope == nullptr)
|
||||
return ErrorUnexpected;
|
||||
if (sub_stmt_node != nullptr)
|
||||
scope_block->node->data.block.statements.append(sub_stmt_node);
|
||||
|
||||
*out_scope = new_scope;
|
||||
return ErrorNone;
|
||||
}
|
||||
|
||||
static AstNode *trans_for_loop(Context *c, TransScope *parent_scope, const ForStmt *stmt) {
|
||||
AstNode *loop_block_node;
|
||||
TransScopeWhile *while_scope;
|
||||
@ -2590,8 +2467,7 @@ static AstNode *trans_break_stmt(Context *c, TransScope *scope, const BreakStmt
|
||||
if (cur_scope->id == TransScopeIdWhile) {
|
||||
return trans_create_node(c, NodeTypeBreak);
|
||||
} else if (cur_scope->id == TransScopeIdSwitch) {
|
||||
TransScopeSwitch *switch_scope = (TransScopeSwitch *)cur_scope;
|
||||
return trans_create_node_goto(c, switch_scope->end_label_name);
|
||||
zig_panic("TODO");
|
||||
}
|
||||
cur_scope = cur_scope->parent;
|
||||
}
|
||||
@ -2691,12 +2567,14 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt,
|
||||
return wrap_stmt(out_node, out_child_scope, scope,
|
||||
trans_expr(c, result_used, scope, ((const ParenExpr*)stmt)->getSubExpr(), lrvalue));
|
||||
case Stmt::SwitchStmtClass:
|
||||
return wrap_stmt(out_node, out_child_scope, scope,
|
||||
trans_switch_stmt(c, scope, (const SwitchStmt *)stmt));
|
||||
emit_warning(c, stmt->getLocStart(), "TODO handle C SwitchStmtClass");
|
||||
return ErrorUnexpected;
|
||||
case Stmt::CaseStmtClass:
|
||||
return trans_switch_case(c, scope, (const CaseStmt *)stmt, out_node, out_child_scope);
|
||||
emit_warning(c, stmt->getLocStart(), "TODO handle C CaseStmtClass");
|
||||
return ErrorUnexpected;
|
||||
case Stmt::DefaultStmtClass:
|
||||
return trans_switch_default(c, scope, (const DefaultStmt *)stmt, out_node, out_child_scope);
|
||||
emit_warning(c, stmt->getLocStart(), "TODO handle C DefaultStmtClass");
|
||||
return ErrorUnexpected;
|
||||
case Stmt::NoStmtClass:
|
||||
emit_warning(c, stmt->getLocStart(), "TODO handle C NoStmtClass");
|
||||
return ErrorUnexpected;
|
||||
@ -3246,7 +3124,8 @@ static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) {
|
||||
|
||||
StorageClass sc = fn_decl->getStorageClass();
|
||||
if (sc == SC_None) {
|
||||
proto_node->data.fn_proto.visib_mod = fn_decl->hasBody() ? c->export_visib_mod : c->visib_mod;
|
||||
proto_node->data.fn_proto.visib_mod = c->visib_mod;
|
||||
proto_node->data.fn_proto.is_export = fn_decl->hasBody() ? c->want_export : false;
|
||||
} else if (sc == SC_Extern || sc == SC_Static) {
|
||||
proto_node->data.fn_proto.visib_mod = c->visib_mod;
|
||||
} else if (sc == SC_PrivateExtern) {
|
||||
@ -3865,14 +3744,6 @@ static TransScopeVar *trans_scope_var_create(Context *c, TransScope *parent_scop
|
||||
return result;
|
||||
}
|
||||
|
||||
static TransScopeSwitch *trans_scope_switch_create(Context *c, TransScope *parent_scope) {
|
||||
TransScopeSwitch *result = allocate<TransScopeSwitch>(1);
|
||||
result->base.id = TransScopeIdSwitch;
|
||||
result->base.parent = parent_scope;
|
||||
result->switch_node = trans_create_node(c, NodeTypeSwitchExpr);
|
||||
return result;
|
||||
}
|
||||
|
||||
static TransScopeBlock *trans_scope_block_find(TransScope *scope) {
|
||||
while (scope != nullptr) {
|
||||
if (scope->id == TransScopeIdBlock) {
|
||||
@ -3883,16 +3754,6 @@ static TransScopeBlock *trans_scope_block_find(TransScope *scope) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static TransScopeSwitch *trans_scope_switch_find(TransScope *scope) {
|
||||
while (scope != nullptr) {
|
||||
if (scope->id == TransScopeIdSwitch) {
|
||||
return (TransScopeSwitch *)scope;
|
||||
}
|
||||
scope = scope->parent;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void render_aliases(Context *c) {
|
||||
for (size_t i = 0; i < c->aliases.length; i += 1) {
|
||||
Alias *alias = &c->aliases.at(i);
|
||||
@ -4003,6 +3864,10 @@ static void render_macros(Context *c) {
|
||||
}
|
||||
}
|
||||
|
||||
static AstNode *parse_ctok_primary_expr(Context *c, CTokenize *ctok, size_t *tok_i);
|
||||
static AstNode *parse_ctok_expr(Context *c, CTokenize *ctok, size_t *tok_i);
|
||||
static AstNode *parse_ctok_prefix_op_expr(Context *c, CTokenize *ctok, size_t *tok_i);
|
||||
|
||||
static AstNode *parse_ctok_num_lit(Context *c, CTokenize *ctok, size_t *tok_i, bool negate) {
|
||||
CTok *tok = &ctok->tokens.at(*tok_i);
|
||||
if (tok->id == CTokIdNumLitInt) {
|
||||
@ -4030,7 +3895,7 @@ static AstNode *parse_ctok_num_lit(Context *c, CTokenize *ctok, size_t *tok_i, b
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static AstNode *parse_ctok(Context *c, CTokenize *ctok, size_t *tok_i) {
|
||||
static AstNode *parse_ctok_primary_expr(Context *c, CTokenize *ctok, size_t *tok_i) {
|
||||
CTok *tok = &ctok->tokens.at(*tok_i);
|
||||
switch (tok->id) {
|
||||
case CTokIdCharLit:
|
||||
@ -4047,55 +3912,131 @@ static AstNode *parse_ctok(Context *c, CTokenize *ctok, size_t *tok_i) {
|
||||
return parse_ctok_num_lit(c, ctok, tok_i, false);
|
||||
case CTokIdSymbol:
|
||||
{
|
||||
bool need_symbol = false;
|
||||
CTokId curr_id = CTokIdSymbol;
|
||||
*tok_i += 1;
|
||||
Buf *symbol_name = buf_create_from_buf(&tok->data.symbol);
|
||||
AstNode *curr_node = trans_create_node_symbol(c, symbol_name);
|
||||
AstNode *parent_node = curr_node;
|
||||
do {
|
||||
*tok_i += 1;
|
||||
CTok* curr_tok = &ctok->tokens.at(*tok_i);
|
||||
if (need_symbol) {
|
||||
if (curr_tok->id == CTokIdSymbol) {
|
||||
symbol_name = buf_create_from_buf(&curr_tok->data.symbol);
|
||||
curr_node = trans_create_node_field_access(c, parent_node, buf_create_from_buf(symbol_name));
|
||||
parent_node = curr_node;
|
||||
need_symbol = false;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
if (curr_tok->id == CTokIdDot) {
|
||||
need_symbol = true;
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (curr_id != CTokIdEOF);
|
||||
return curr_node;
|
||||
return trans_create_node_symbol(c, symbol_name);
|
||||
}
|
||||
case CTokIdLParen:
|
||||
{
|
||||
*tok_i += 1;
|
||||
AstNode *inner_node = parse_ctok(c, ctok, tok_i);
|
||||
AstNode *inner_node = parse_ctok_expr(c, ctok, tok_i);
|
||||
if (inner_node == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CTok *next_tok = &ctok->tokens.at(*tok_i);
|
||||
if (next_tok->id != CTokIdRParen) {
|
||||
if (next_tok->id == CTokIdRParen) {
|
||||
*tok_i += 1;
|
||||
return inner_node;
|
||||
}
|
||||
|
||||
AstNode *node_to_cast = parse_ctok_expr(c, ctok, tok_i);
|
||||
if (node_to_cast == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CTok *next_tok2 = &ctok->tokens.at(*tok_i);
|
||||
if (next_tok2->id != CTokIdRParen) {
|
||||
return nullptr;
|
||||
}
|
||||
*tok_i += 1;
|
||||
return inner_node;
|
||||
|
||||
|
||||
//if (@typeId(@typeOf(x)) == @import("builtin").TypeId.Pointer)
|
||||
// @ptrCast(dest, x)
|
||||
//else if (@typeId(@typeOf(x)) == @import("builtin").TypeId.Integer)
|
||||
// @intToPtr(dest, x)
|
||||
//else
|
||||
// (dest)(x)
|
||||
|
||||
AstNode *import_builtin = trans_create_node_builtin_fn_call_str(c, "import");
|
||||
import_builtin->data.fn_call_expr.params.append(trans_create_node_str_lit_non_c(c, buf_create_from_str("builtin")));
|
||||
AstNode *typeid_type = trans_create_node_field_access_str(c, import_builtin, "TypeId");
|
||||
AstNode *typeid_pointer = trans_create_node_field_access_str(c, typeid_type, "Pointer");
|
||||
AstNode *typeid_integer = trans_create_node_field_access_str(c, typeid_type, "Int");
|
||||
AstNode *typeof_x = trans_create_node_builtin_fn_call_str(c, "typeOf");
|
||||
typeof_x->data.fn_call_expr.params.append(node_to_cast);
|
||||
AstNode *typeid_value = trans_create_node_builtin_fn_call_str(c, "typeId");
|
||||
typeid_value->data.fn_call_expr.params.append(typeof_x);
|
||||
|
||||
AstNode *outer_if_cond = trans_create_node_bin_op(c, typeid_value, BinOpTypeCmpEq, typeid_pointer);
|
||||
AstNode *inner_if_cond = trans_create_node_bin_op(c, typeid_value, BinOpTypeCmpEq, typeid_integer);
|
||||
AstNode *inner_if_then = trans_create_node_builtin_fn_call_str(c, "intToPtr");
|
||||
inner_if_then->data.fn_call_expr.params.append(inner_node);
|
||||
inner_if_then->data.fn_call_expr.params.append(node_to_cast);
|
||||
AstNode *inner_if_else = trans_create_node_cast(c, inner_node, node_to_cast);
|
||||
AstNode *inner_if = trans_create_node_if(c, inner_if_cond, inner_if_then, inner_if_else);
|
||||
AstNode *outer_if_then = trans_create_node_builtin_fn_call_str(c, "ptrCast");
|
||||
outer_if_then->data.fn_call_expr.params.append(inner_node);
|
||||
outer_if_then->data.fn_call_expr.params.append(node_to_cast);
|
||||
return trans_create_node_if(c, outer_if_cond, outer_if_then, inner_if);
|
||||
}
|
||||
case CTokIdDot:
|
||||
case CTokIdEOF:
|
||||
case CTokIdRParen:
|
||||
case CTokIdAsterisk:
|
||||
case CTokIdBang:
|
||||
case CTokIdTilde:
|
||||
// not able to make sense of this
|
||||
return nullptr;
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static AstNode *parse_ctok_expr(Context *c, CTokenize *ctok, size_t *tok_i) {
|
||||
return parse_ctok_prefix_op_expr(c, ctok, tok_i);
|
||||
}
|
||||
|
||||
static AstNode *parse_ctok_suffix_op_expr(Context *c, CTokenize *ctok, size_t *tok_i) {
|
||||
AstNode *node = parse_ctok_primary_expr(c, ctok, tok_i);
|
||||
if (node == nullptr)
|
||||
return nullptr;
|
||||
|
||||
while (true) {
|
||||
CTok *first_tok = &ctok->tokens.at(*tok_i);
|
||||
if (first_tok->id == CTokIdDot) {
|
||||
*tok_i += 1;
|
||||
|
||||
CTok *name_tok = &ctok->tokens.at(*tok_i);
|
||||
if (name_tok->id != CTokIdSymbol) {
|
||||
return nullptr;
|
||||
}
|
||||
*tok_i += 1;
|
||||
|
||||
node = trans_create_node_field_access(c, node, buf_create_from_buf(&name_tok->data.symbol));
|
||||
} else if (first_tok->id == CTokIdAsterisk) {
|
||||
*tok_i += 1;
|
||||
|
||||
node = trans_create_node_addr_of(c, false, false, node);
|
||||
} else {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static PrefixOp ctok_to_prefix_op(CTok *token) {
|
||||
switch (token->id) {
|
||||
case CTokIdBang: return PrefixOpBoolNot;
|
||||
case CTokIdMinus: return PrefixOpNegation;
|
||||
case CTokIdTilde: return PrefixOpBinNot;
|
||||
case CTokIdAsterisk: return PrefixOpDereference;
|
||||
default: return PrefixOpInvalid;
|
||||
}
|
||||
}
|
||||
static AstNode *parse_ctok_prefix_op_expr(Context *c, CTokenize *ctok, size_t *tok_i) {
|
||||
CTok *op_tok = &ctok->tokens.at(*tok_i);
|
||||
PrefixOp prefix_op = ctok_to_prefix_op(op_tok);
|
||||
if (prefix_op == PrefixOpInvalid) {
|
||||
return parse_ctok_suffix_op_expr(c, ctok, tok_i);
|
||||
}
|
||||
*tok_i += 1;
|
||||
|
||||
AstNode *prefix_op_expr = parse_ctok_prefix_op_expr(c, ctok, tok_i);
|
||||
if (prefix_op_expr == nullptr)
|
||||
return nullptr;
|
||||
return trans_create_node_prefix_op(c, prefix_op, prefix_op_expr);
|
||||
}
|
||||
|
||||
static void process_macro(Context *c, CTokenize *ctok, Buf *name, const char *char_ptr) {
|
||||
tokenize_c_macro(ctok, (const uint8_t *)char_ptr);
|
||||
|
||||
@ -4108,7 +4049,7 @@ static void process_macro(Context *c, CTokenize *ctok, Buf *name, const char *ch
|
||||
assert(name_tok->id == CTokIdSymbol && buf_eql_buf(&name_tok->data.symbol, name));
|
||||
tok_i += 1;
|
||||
|
||||
AstNode *result_node = parse_ctok(c, ctok, &tok_i);
|
||||
AstNode *result_node = parse_ctok_suffix_op_expr(c, ctok, &tok_i);
|
||||
if (result_node == nullptr) {
|
||||
return;
|
||||
}
|
||||
@ -4189,10 +4130,10 @@ int parse_h_file(ImportTableEntry *import, ZigList<ErrorMsg *> *errors, const ch
|
||||
c->errors = errors;
|
||||
if (buf_ends_with_str(buf_create_from_str(target_file), ".h")) {
|
||||
c->visib_mod = VisibModPub;
|
||||
c->export_visib_mod = VisibModPub;
|
||||
c->want_export = false;
|
||||
} else {
|
||||
c->visib_mod = VisibModPub;
|
||||
c->export_visib_mod = VisibModExport;
|
||||
c->want_export = true;
|
||||
}
|
||||
c->decl_table.init(8);
|
||||
c->macro_table.init(8);
|
||||
|
||||
@ -175,12 +175,19 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
|
||||
|
||||
|
||||
LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args,
|
||||
unsigned NumArgs, unsigned CC, bool always_inline, const char *Name)
|
||||
unsigned NumArgs, unsigned CC, ZigLLVM_FnInline fn_inline, const char *Name)
|
||||
{
|
||||
CallInst *call_inst = CallInst::Create(unwrap(Fn), makeArrayRef(unwrap(Args), NumArgs), Name);
|
||||
call_inst->setCallingConv(CC);
|
||||
if (always_inline) {
|
||||
call_inst->addAttribute(AttributeList::FunctionIndex, Attribute::AlwaysInline);
|
||||
switch (fn_inline) {
|
||||
case ZigLLVM_FnInlineAuto:
|
||||
break;
|
||||
case ZigLLVM_FnInlineAlways:
|
||||
call_inst->addAttribute(AttributeList::FunctionIndex, Attribute::AlwaysInline);
|
||||
break;
|
||||
case ZigLLVM_FnInlineNever:
|
||||
call_inst->addAttribute(AttributeList::FunctionIndex, Attribute::NoInline);
|
||||
break;
|
||||
}
|
||||
return wrap(unwrap(B)->Insert(call_inst));
|
||||
}
|
||||
|
||||
@ -45,8 +45,13 @@ enum ZigLLVM_EmitOutputType {
|
||||
bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
|
||||
const char *filename, ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug);
|
||||
|
||||
enum ZigLLVM_FnInline {
|
||||
ZigLLVM_FnInlineAuto,
|
||||
ZigLLVM_FnInlineAlways,
|
||||
ZigLLVM_FnInlineNever,
|
||||
};
|
||||
LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args,
|
||||
unsigned NumArgs, unsigned CC, bool always_inline, const char *Name);
|
||||
unsigned NumArgs, unsigned CC, ZigLLVM_FnInline fn_inline, const char *Name);
|
||||
|
||||
LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp,
|
||||
LLVMValueRef new_val, LLVMAtomicOrdering success_ordering,
|
||||
|
||||
@ -3,42 +3,46 @@ const assert = debug.assert;
|
||||
const mem = @import("mem.zig");
|
||||
const Allocator = mem.Allocator;
|
||||
|
||||
pub fn ArrayList(comptime T: type) -> type{
|
||||
struct {
|
||||
pub fn ArrayList(comptime T: type) -> type {
|
||||
return AlignedArrayList(T, @alignOf(T));
|
||||
}
|
||||
|
||||
pub fn AlignedArrayList(comptime T: type, comptime A: u29) -> type{
|
||||
return struct {
|
||||
const Self = this;
|
||||
|
||||
/// Use toSlice instead of slicing this directly, because if you don't
|
||||
/// specify the end position of the slice, this will potentially give
|
||||
/// you uninitialized memory.
|
||||
items: []T,
|
||||
items: []align(A) T,
|
||||
len: usize,
|
||||
allocator: &Allocator,
|
||||
|
||||
/// Deinitialize with `deinit` or use `toOwnedSlice`.
|
||||
pub fn init(allocator: &Allocator) -> Self {
|
||||
Self {
|
||||
.items = []T{},
|
||||
return Self {
|
||||
.items = []align(A) T{},
|
||||
.len = 0,
|
||||
.allocator = allocator,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(l: &Self) {
|
||||
l.allocator.free(l.items);
|
||||
}
|
||||
|
||||
pub fn toSlice(l: &Self) -> []T {
|
||||
pub fn toSlice(l: &Self) -> []align(A) T {
|
||||
return l.items[0..l.len];
|
||||
}
|
||||
|
||||
pub fn toSliceConst(l: &const Self) -> []const T {
|
||||
pub fn toSliceConst(l: &const Self) -> []align(A) const T {
|
||||
return l.items[0..l.len];
|
||||
}
|
||||
|
||||
/// ArrayList takes ownership of the passed in slice. The slice must have been
|
||||
/// allocated with `allocator`.
|
||||
/// Deinitialize with `deinit` or use `toOwnedSlice`.
|
||||
pub fn fromOwnedSlice(allocator: &Allocator, slice: []T) -> Self {
|
||||
pub fn fromOwnedSlice(allocator: &Allocator, slice: []align(A) T) -> Self {
|
||||
return Self {
|
||||
.items = slice,
|
||||
.len = slice.len,
|
||||
@ -47,9 +51,9 @@ pub fn ArrayList(comptime T: type) -> type{
|
||||
}
|
||||
|
||||
/// The caller owns the returned memory. ArrayList becomes empty.
|
||||
pub fn toOwnedSlice(self: &Self) -> []T {
|
||||
pub fn toOwnedSlice(self: &Self) -> []align(A) T {
|
||||
const allocator = self.allocator;
|
||||
const result = allocator.shrink(T, self.items, self.len);
|
||||
const result = allocator.alignedShrink(T, A, self.items, self.len);
|
||||
*self = init(allocator);
|
||||
return result;
|
||||
}
|
||||
@ -59,7 +63,7 @@ pub fn ArrayList(comptime T: type) -> type{
|
||||
*new_item_ptr = *item;
|
||||
}
|
||||
|
||||
pub fn appendSlice(l: &Self, items: []const T) -> %void {
|
||||
pub fn appendSlice(l: &Self, items: []align(A) const T) -> %void {
|
||||
%return l.ensureCapacity(l.len + items.len);
|
||||
mem.copy(T, l.items[l.len..], items);
|
||||
l.len += items.len;
|
||||
@ -82,7 +86,7 @@ pub fn ArrayList(comptime T: type) -> type{
|
||||
better_capacity += better_capacity / 2 + 8;
|
||||
if (better_capacity >= new_capacity) break;
|
||||
}
|
||||
l.items = %return l.allocator.realloc(T, l.items, better_capacity);
|
||||
l.items = %return l.allocator.alignedRealloc(T, A, l.items, better_capacity);
|
||||
}
|
||||
|
||||
pub fn addOne(l: &Self) -> %&T {
|
||||
@ -97,7 +101,13 @@ pub fn ArrayList(comptime T: type) -> type{
|
||||
self.len -= 1;
|
||||
return self.items[self.len];
|
||||
}
|
||||
}
|
||||
|
||||
pub fn popOrNull(self: &Self) -> ?T {
|
||||
if (self.len == 0)
|
||||
return null;
|
||||
return self.pop();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test "basic ArrayList test" {
|
||||
|
||||
@ -193,7 +193,7 @@ pub const Base64DecoderWithIgnore = struct {
|
||||
/// Decoding more data than can fit in dest results in error.OutputTooSmall. See also ::calcSizeUpperBound.
|
||||
/// Returns the number of bytes writen to dest.
|
||||
pub fn decode(decoder_with_ignore: &const Base64DecoderWithIgnore, dest: []u8, source: []const u8) -> %usize {
|
||||
const decoder = &const decoder_with_ignore.decoder;
|
||||
const decoder = &decoder_with_ignore.decoder;
|
||||
|
||||
var src_cursor: usize = 0;
|
||||
var dest_cursor: usize = 0;
|
||||
|
||||
@ -42,6 +42,11 @@ pub const BufMap = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(self: &BufMap, key: []const u8) -> ?[]const u8 {
|
||||
const entry = self.hash_map.get(key) ?? return null;
|
||||
return entry.value;
|
||||
}
|
||||
|
||||
pub fn delete(self: &BufMap, key: []const u8) {
|
||||
const entry = self.hash_map.remove(key) ?? return;
|
||||
self.free(entry.key);
|
||||
|
||||
@ -30,9 +30,9 @@ pub const Buffer = struct {
|
||||
/// * ::replaceContentsBuffer
|
||||
/// * ::resize
|
||||
pub fn initNull(allocator: &Allocator) -> Buffer {
|
||||
Buffer {
|
||||
return Buffer {
|
||||
.list = ArrayList(u8).init(allocator),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Must deinitialize with deinit.
|
||||
@ -98,14 +98,17 @@ pub const Buffer = struct {
|
||||
mem.copy(u8, self.list.toSlice()[old_len..], m);
|
||||
}
|
||||
|
||||
// TODO: remove, use OutStream for this
|
||||
pub fn appendFormat(self: &Buffer, comptime format: []const u8, args: ...) -> %void {
|
||||
return fmt.format(self, append, format, args);
|
||||
}
|
||||
|
||||
// TODO: remove, use OutStream for this
|
||||
pub fn appendByte(self: &Buffer, byte: u8) -> %void {
|
||||
return self.appendByteNTimes(byte, 1);
|
||||
}
|
||||
|
||||
// TODO: remove, use OutStream for this
|
||||
pub fn appendByteNTimes(self: &Buffer, byte: u8, count: usize) -> %void {
|
||||
var prev_size: usize = self.len();
|
||||
%return self.resize(prev_size + count);
|
||||
@ -117,7 +120,7 @@ pub const Buffer = struct {
|
||||
}
|
||||
|
||||
pub fn eql(self: &const Buffer, m: []const u8) -> bool {
|
||||
mem.eql(u8, self.toSliceConst(), m)
|
||||
return mem.eql(u8, self.toSliceConst(), m);
|
||||
}
|
||||
|
||||
pub fn startsWith(self: &const Buffer, m: []const u8) -> bool {
|
||||
@ -136,6 +139,11 @@ pub const Buffer = struct {
|
||||
%return self.resize(m.len);
|
||||
mem.copy(u8, self.list.toSlice(), m);
|
||||
}
|
||||
|
||||
/// For passing to C functions.
|
||||
pub fn ptr(self: &const Buffer) -> &u8 {
|
||||
return self.list.items.ptr;
|
||||
}
|
||||
};
|
||||
|
||||
test "simple Buffer" {
|
||||
|
||||
134
std/build.zig
134
std/build.zig
@ -221,11 +221,11 @@ pub const Builder = struct {
|
||||
}
|
||||
|
||||
pub fn version(self: &const Builder, major: u32, minor: u32, patch: u32) -> Version {
|
||||
Version {
|
||||
return Version {
|
||||
.major = major,
|
||||
.minor = minor,
|
||||
.patch = patch,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn addCIncludePath(self: &Builder, path: []const u8) {
|
||||
@ -432,16 +432,16 @@ pub const Builder = struct {
|
||||
const release_safe = self.option(bool, "release-safe", "optimizations on and safety on") ?? false;
|
||||
const release_fast = self.option(bool, "release-fast", "optimizations on and safety off") ?? false;
|
||||
|
||||
const mode = if (release_safe and !release_fast) {
|
||||
const mode = if (release_safe and !release_fast)
|
||||
builtin.Mode.ReleaseSafe
|
||||
} else if (release_fast and !release_safe) {
|
||||
else if (release_fast and !release_safe)
|
||||
builtin.Mode.ReleaseFast
|
||||
} else if (!release_fast and !release_safe) {
|
||||
else if (!release_fast and !release_safe)
|
||||
builtin.Mode.Debug
|
||||
} else {
|
||||
else x: {
|
||||
warn("Both -Drelease-safe and -Drelease-fast specified");
|
||||
self.markInvalidUserInput();
|
||||
builtin.Mode.Debug
|
||||
break :x builtin.Mode.Debug;
|
||||
};
|
||||
self.release_mode = mode;
|
||||
return mode;
|
||||
@ -506,7 +506,7 @@ pub const Builder = struct {
|
||||
}
|
||||
|
||||
fn typeToEnum(comptime T: type) -> TypeId {
|
||||
switch (@typeId(T)) {
|
||||
return switch (@typeId(T)) {
|
||||
builtin.TypeId.Int => TypeId.Int,
|
||||
builtin.TypeId.Float => TypeId.Float,
|
||||
builtin.TypeId.Bool => TypeId.Bool,
|
||||
@ -515,7 +515,7 @@ pub const Builder = struct {
|
||||
[]const []const u8 => TypeId.List,
|
||||
else => @compileError("Unsupported type: " ++ @typeName(T)),
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn markInvalidUserInput(self: &Builder) {
|
||||
@ -590,8 +590,7 @@ pub const Builder = struct {
|
||||
|
||||
return error.UncleanExit;
|
||||
},
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
pub fn makePath(self: &Builder, path: []const u8) -> %void {
|
||||
@ -662,13 +661,70 @@ pub const Builder = struct {
|
||||
if (builtin.environ == builtin.Environ.msvc) {
|
||||
return "cl.exe";
|
||||
} else {
|
||||
return os.getEnvVarOwned(self.allocator, "CC") %% |err| {
|
||||
if (err == error.EnvironmentVariableNotFound) {
|
||||
return os.getEnvVarOwned(self.allocator, "CC") %% |err|
|
||||
if (err == error.EnvironmentVariableNotFound)
|
||||
([]const u8)("cc")
|
||||
} else {
|
||||
debug.panic("Unable to get environment variable: {}", err);
|
||||
else
|
||||
debug.panic("Unable to get environment variable: {}", err)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn findProgram(self: &Builder, names: []const []const u8, paths: []const []const u8) -> %[]const u8 {
|
||||
const exe_extension = (Target { .Native = {}}).exeFileExt();
|
||||
if (self.env_map.get("PATH")) |PATH| {
|
||||
for (names) |name| {
|
||||
if (os.path.isAbsolute(name)) {
|
||||
return name;
|
||||
}
|
||||
};
|
||||
var it = mem.split(PATH, []u8{os.path.delimiter});
|
||||
while (it.next()) |path| {
|
||||
const full_path = %return os.path.join(self.allocator, path, self.fmt("{}{}", name, exe_extension));
|
||||
if (os.path.real(self.allocator, full_path)) |real_path| {
|
||||
return real_path;
|
||||
} else |_| {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (names) |name| {
|
||||
if (os.path.isAbsolute(name)) {
|
||||
return name;
|
||||
}
|
||||
for (paths) |path| {
|
||||
const full_path = %return os.path.join(self.allocator, path, self.fmt("{}{}", name, exe_extension));
|
||||
if (os.path.real(self.allocator, full_path)) |real_path| {
|
||||
return real_path;
|
||||
} else |_| {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return error.FileNotFound;
|
||||
}
|
||||
|
||||
pub fn exec(self: &Builder, argv: []const []const u8) -> []u8 {
|
||||
const max_output_size = 100 * 1024;
|
||||
const result = os.ChildProcess.exec(self.allocator, argv, null, null, max_output_size) %% |err| {
|
||||
std.debug.panic("Unable to spawn {}: {}", argv[0], @errorName(err));
|
||||
};
|
||||
switch (result.term) {
|
||||
os.ChildProcess.Term.Exited => |code| {
|
||||
if (code != 0) {
|
||||
warn("The following command exited with error code {}:\n", code);
|
||||
printCmd(null, argv);
|
||||
warn("stderr:{}\n", result.stderr);
|
||||
std.debug.panic("command failed");
|
||||
}
|
||||
return result.stdout;
|
||||
},
|
||||
else => {
|
||||
warn("The following command terminated unexpectedly:\n");
|
||||
printCmd(null, argv);
|
||||
warn("stderr:{}\n", result.stderr);
|
||||
std.debug.panic("command failed");
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -755,6 +811,7 @@ pub const LibExeObjStep = struct {
|
||||
is_zig: bool,
|
||||
cflags: ArrayList([]const u8),
|
||||
include_dirs: ArrayList([]const u8),
|
||||
lib_paths: ArrayList([]const u8),
|
||||
disable_libc: bool,
|
||||
frameworks: BufSet,
|
||||
|
||||
@ -844,7 +901,7 @@ pub const LibExeObjStep = struct {
|
||||
.kind = kind,
|
||||
.root_src = root_src,
|
||||
.name = name,
|
||||
.target = Target { .Native = {} },
|
||||
.target = Target.Native,
|
||||
.linker_script = null,
|
||||
.link_libs = BufSet.init(builder.allocator),
|
||||
.frameworks = BufSet.init(builder.allocator),
|
||||
@ -865,6 +922,7 @@ pub const LibExeObjStep = struct {
|
||||
.cflags = ArrayList([]const u8).init(builder.allocator),
|
||||
.source_files = undefined,
|
||||
.include_dirs = ArrayList([]const u8).init(builder.allocator),
|
||||
.lib_paths = ArrayList([]const u8).init(builder.allocator),
|
||||
.object_src = undefined,
|
||||
.disable_libc = true,
|
||||
};
|
||||
@ -879,7 +937,7 @@ pub const LibExeObjStep = struct {
|
||||
.kind = kind,
|
||||
.version = *version,
|
||||
.static = static,
|
||||
.target = Target { .Native = {} },
|
||||
.target = Target.Native,
|
||||
.cflags = ArrayList([]const u8).init(builder.allocator),
|
||||
.source_files = ArrayList([]const u8).init(builder.allocator),
|
||||
.object_files = ArrayList([]const u8).init(builder.allocator),
|
||||
@ -888,6 +946,7 @@ pub const LibExeObjStep = struct {
|
||||
.frameworks = BufSet.init(builder.allocator),
|
||||
.full_path_libs = ArrayList([]const u8).init(builder.allocator),
|
||||
.include_dirs = ArrayList([]const u8).init(builder.allocator),
|
||||
.lib_paths = ArrayList([]const u8).init(builder.allocator),
|
||||
.output_path = null,
|
||||
.out_filename = undefined,
|
||||
.major_only_filename = undefined,
|
||||
@ -1018,11 +1077,10 @@ pub const LibExeObjStep = struct {
|
||||
}
|
||||
|
||||
pub fn getOutputPath(self: &LibExeObjStep) -> []const u8 {
|
||||
if (self.output_path) |output_path| {
|
||||
return if (self.output_path) |output_path|
|
||||
output_path
|
||||
} else {
|
||||
%%os.path.join(self.builder.allocator, self.builder.cache_root, self.out_filename)
|
||||
}
|
||||
else
|
||||
%%os.path.join(self.builder.allocator, self.builder.cache_root, self.out_filename);
|
||||
}
|
||||
|
||||
pub fn setOutputHPath(self: &LibExeObjStep, file_path: []const u8) {
|
||||
@ -1035,11 +1093,10 @@ pub const LibExeObjStep = struct {
|
||||
}
|
||||
|
||||
pub fn getOutputHPath(self: &LibExeObjStep) -> []const u8 {
|
||||
if (self.output_h_path) |output_h_path| {
|
||||
return if (self.output_h_path) |output_h_path|
|
||||
output_h_path
|
||||
} else {
|
||||
%%os.path.join(self.builder.allocator, self.builder.cache_root, self.out_h_filename)
|
||||
}
|
||||
else
|
||||
%%os.path.join(self.builder.allocator, self.builder.cache_root, self.out_h_filename);
|
||||
}
|
||||
|
||||
pub fn addAssemblyFile(self: &LibExeObjStep, path: []const u8) {
|
||||
@ -1069,11 +1126,14 @@ pub const LibExeObjStep = struct {
|
||||
%%self.include_dirs.append(self.builder.cache_root);
|
||||
}
|
||||
|
||||
// TODO put include_dirs in zig command line
|
||||
pub fn addIncludeDir(self: &LibExeObjStep, path: []const u8) {
|
||||
%%self.include_dirs.append(path);
|
||||
}
|
||||
|
||||
pub fn addLibPath(self: &LibExeObjStep, path: []const u8) {
|
||||
%%self.lib_paths.append(path);
|
||||
}
|
||||
|
||||
pub fn addPackagePath(self: &LibExeObjStep, name: []const u8, pkg_index_path: []const u8) {
|
||||
assert(self.is_zig);
|
||||
|
||||
@ -1222,6 +1282,11 @@ pub const LibExeObjStep = struct {
|
||||
%%zig_args.append("--pkg-end");
|
||||
}
|
||||
|
||||
for (self.include_dirs.toSliceConst()) |include_path| {
|
||||
%%zig_args.append("-isystem");
|
||||
%%zig_args.append(self.builder.pathFromRoot(include_path));
|
||||
}
|
||||
|
||||
for (builder.include_paths.toSliceConst()) |include_path| {
|
||||
%%zig_args.append("-isystem");
|
||||
%%zig_args.append(builder.pathFromRoot(include_path));
|
||||
@ -1232,6 +1297,11 @@ pub const LibExeObjStep = struct {
|
||||
%%zig_args.append(rpath);
|
||||
}
|
||||
|
||||
for (self.lib_paths.toSliceConst()) |lib_path| {
|
||||
%%zig_args.append("--library-path");
|
||||
%%zig_args.append(lib_path);
|
||||
}
|
||||
|
||||
for (builder.lib_paths.toSliceConst()) |lib_path| {
|
||||
%%zig_args.append("--library-path");
|
||||
%%zig_args.append(lib_path);
|
||||
@ -1544,7 +1614,7 @@ pub const TestStep = struct {
|
||||
|
||||
pub fn init(builder: &Builder, root_src: []const u8) -> TestStep {
|
||||
const step_name = builder.fmt("test {}", root_src);
|
||||
TestStep {
|
||||
return TestStep {
|
||||
.step = Step.init(step_name, builder.allocator, make),
|
||||
.builder = builder,
|
||||
.root_src = root_src,
|
||||
@ -1555,7 +1625,7 @@ pub const TestStep = struct {
|
||||
.link_libs = BufSet.init(builder.allocator),
|
||||
.target = Target { .Native = {} },
|
||||
.exec_cmd_args = null,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn setVerbose(self: &TestStep, value: bool) {
|
||||
@ -1862,16 +1932,16 @@ pub const Step = struct {
|
||||
done_flag: bool,
|
||||
|
||||
pub fn init(name: []const u8, allocator: &Allocator, makeFn: fn (&Step)->%void) -> Step {
|
||||
Step {
|
||||
return Step {
|
||||
.name = name,
|
||||
.makeFn = makeFn,
|
||||
.dependencies = ArrayList(&Step).init(allocator),
|
||||
.loop_flag = false,
|
||||
.done_flag = false,
|
||||
}
|
||||
};
|
||||
}
|
||||
pub fn initNoOp(name: []const u8, allocator: &Allocator) -> Step {
|
||||
init(name, allocator, makeNoOp)
|
||||
return init(name, allocator, makeNoOp);
|
||||
}
|
||||
|
||||
pub fn make(self: &Step) -> %void {
|
||||
|
||||
@ -48,3 +48,4 @@ pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) -> c_int;
|
||||
pub extern "c" fn malloc(usize) -> ?&c_void;
|
||||
pub extern "c" fn realloc(&c_void, usize) -> ?&c_void;
|
||||
pub extern "c" fn free(&c_void);
|
||||
pub extern "c" fn posix_memalign(memptr: &&c_void, alignment: usize, size: usize) -> c_int;
|
||||
|
||||
@ -17,7 +17,7 @@ pub fn cmp(a: &const u8, b: &const u8) -> i8 {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toSliceConst(str: &const u8) -> []const u8 {
|
||||
|
||||
214
std/debug.zig
214
std/debug.zig
@ -32,7 +32,7 @@ fn getStderrStream() -> %&io.OutStream {
|
||||
const st = &stderr_file_out_stream.stream;
|
||||
stderr_stream = st;
|
||||
return st;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned.
|
||||
@ -52,9 +52,9 @@ pub fn assert(ok: bool) {
|
||||
// we insert an explicit call to @panic instead of unreachable.
|
||||
// TODO we should use `assertOrPanic` in tests and remove this logic.
|
||||
if (builtin.is_test) {
|
||||
@panic("assertion failure")
|
||||
@panic("assertion failure");
|
||||
} else {
|
||||
unreachable // assertion failure
|
||||
unreachable; // assertion failure
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -96,8 +96,6 @@ const WHITE = "\x1b[37;1m";
|
||||
const DIM = "\x1b[2m";
|
||||
const RESET = "\x1b[0m";
|
||||
|
||||
pub var user_main_fn: ?fn() -> %void = null;
|
||||
|
||||
error PathNotFound;
|
||||
error InvalidDebugInfo;
|
||||
|
||||
@ -113,6 +111,7 @@ pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty
|
||||
.debug_abbrev = undefined,
|
||||
.debug_str = undefined,
|
||||
.debug_line = undefined,
|
||||
.debug_ranges = null,
|
||||
.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator),
|
||||
.compile_unit_list = ArrayList(CompileUnit).init(allocator),
|
||||
};
|
||||
@ -127,6 +126,7 @@ pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty
|
||||
st.debug_abbrev = (%return st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo;
|
||||
st.debug_str = (%return st.elf.findSection(".debug_str")) ?? return error.MissingDebugInfo;
|
||||
st.debug_line = (%return st.elf.findSection(".debug_line")) ?? return error.MissingDebugInfo;
|
||||
st.debug_ranges = (%return st.elf.findSection(".debug_ranges"));
|
||||
%return scanAllCompileUnits(st);
|
||||
|
||||
var ignored_count: usize = 0;
|
||||
@ -144,7 +144,7 @@ pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty
|
||||
// at compile time. I'll call it issue #313
|
||||
const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}";
|
||||
|
||||
const compile_unit = findCompileUnit(st, return_address) ?? {
|
||||
const compile_unit = findCompileUnit(st, return_address) %% {
|
||||
%return out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",
|
||||
return_address);
|
||||
continue;
|
||||
@ -175,7 +175,7 @@ pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty
|
||||
return_address, compile_unit_name);
|
||||
},
|
||||
else => return err,
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
builtin.ObjectFormat.coff => {
|
||||
@ -233,6 +233,7 @@ const ElfStackTrace = struct {
|
||||
debug_abbrev: &elf.SectionHeader,
|
||||
debug_str: &elf.SectionHeader,
|
||||
debug_line: &elf.SectionHeader,
|
||||
debug_ranges: ?&elf.SectionHeader,
|
||||
abbrev_table_list: ArrayList(AbbrevTableHeader),
|
||||
compile_unit_list: ArrayList(CompileUnit),
|
||||
|
||||
@ -333,6 +334,15 @@ const Die = struct {
|
||||
};
|
||||
}
|
||||
|
||||
fn getAttrSecOffset(self: &const Die, id: u64) -> %u64 {
|
||||
const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;
|
||||
return switch (*form_value) {
|
||||
FormValue.Const => |value| value.asUnsignedLe(),
|
||||
FormValue.SecOffset => |value| value,
|
||||
else => error.InvalidDebugInfo,
|
||||
};
|
||||
}
|
||||
|
||||
fn getAttrUnsignedLe(self: &const Die, id: u64) -> %u64 {
|
||||
const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;
|
||||
return switch (*form_value) {
|
||||
@ -347,7 +357,7 @@ const Die = struct {
|
||||
FormValue.String => |value| value,
|
||||
FormValue.StrPtr => |offset| getString(st, offset),
|
||||
else => error.InvalidDebugInfo,
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@ -393,7 +403,7 @@ const LineNumberProgram = struct {
|
||||
pub fn init(is_stmt: bool, include_dirs: []const []const u8,
|
||||
file_entries: &ArrayList(FileEntry), target_address: usize) -> LineNumberProgram
|
||||
{
|
||||
LineNumberProgram {
|
||||
return LineNumberProgram {
|
||||
.address = 0,
|
||||
.file = 1,
|
||||
.line = 1,
|
||||
@ -411,7 +421,7 @@ const LineNumberProgram = struct {
|
||||
.prev_is_stmt = undefined,
|
||||
.prev_basic_block = undefined,
|
||||
.prev_end_sequence = undefined,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn checkLineMatch(self: &LineNumberProgram) -> %?LineInfo {
|
||||
@ -420,14 +430,11 @@ const LineNumberProgram = struct {
|
||||
return error.MissingDebugInfo;
|
||||
} else if (self.prev_file - 1 >= self.file_entries.len) {
|
||||
return error.InvalidDebugInfo;
|
||||
} else {
|
||||
&self.file_entries.items[self.prev_file - 1]
|
||||
};
|
||||
} else &self.file_entries.items[self.prev_file - 1];
|
||||
|
||||
const dir_name = if (file_entry.dir_index >= self.include_dirs.len) {
|
||||
return error.InvalidDebugInfo;
|
||||
} else {
|
||||
self.include_dirs[file_entry.dir_index]
|
||||
};
|
||||
} else self.include_dirs[file_entry.dir_index];
|
||||
const file_name = %return os.path.join(self.file_entries.allocator, dir_name, file_entry.file_name);
|
||||
%defer self.file_entries.allocator.free(file_name);
|
||||
return LineInfo {
|
||||
@ -484,28 +491,21 @@ fn parseFormValueBlock(allocator: &mem.Allocator, in_stream: &io.InStream, size:
|
||||
}
|
||||
|
||||
fn parseFormValueConstant(allocator: &mem.Allocator, in_stream: &io.InStream, signed: bool, size: usize) -> %FormValue {
|
||||
FormValue { .Const = Constant {
|
||||
return FormValue { .Const = Constant {
|
||||
.signed = signed,
|
||||
.payload = %return readAllocBytes(allocator, in_stream, size),
|
||||
}}
|
||||
}};
|
||||
}
|
||||
|
||||
fn parseFormValueDwarfOffsetSize(in_stream: &io.InStream, is_64: bool) -> %u64 {
|
||||
return if (is_64) {
|
||||
%return in_stream.readIntLe(u64)
|
||||
} else {
|
||||
u64(%return in_stream.readIntLe(u32))
|
||||
};
|
||||
return if (is_64) %return in_stream.readIntLe(u64)
|
||||
else u64(%return in_stream.readIntLe(u32)) ;
|
||||
}
|
||||
|
||||
fn parseFormValueTargetAddrSize(in_stream: &io.InStream) -> %u64 {
|
||||
return if (@sizeOf(usize) == 4) {
|
||||
u64(%return in_stream.readIntLe(u32))
|
||||
} else if (@sizeOf(usize) == 8) {
|
||||
%return in_stream.readIntLe(u64)
|
||||
} else {
|
||||
unreachable;
|
||||
};
|
||||
return if (@sizeOf(usize) == 4) u64(%return in_stream.readIntLe(u32))
|
||||
else if (@sizeOf(usize) == 8) %return in_stream.readIntLe(u64)
|
||||
else unreachable;
|
||||
}
|
||||
|
||||
fn parseFormValueRefLen(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) -> %FormValue {
|
||||
@ -524,9 +524,9 @@ fn parseFormValue(allocator: &mem.Allocator, in_stream: &io.InStream, form_id: u
|
||||
DW.FORM_block1 => parseFormValueBlock(allocator, in_stream, 1),
|
||||
DW.FORM_block2 => parseFormValueBlock(allocator, in_stream, 2),
|
||||
DW.FORM_block4 => parseFormValueBlock(allocator, in_stream, 4),
|
||||
DW.FORM_block => {
|
||||
DW.FORM_block => x: {
|
||||
const block_len = %return readULeb128(in_stream);
|
||||
parseFormValueBlockLen(allocator, in_stream, block_len)
|
||||
return parseFormValueBlockLen(allocator, in_stream, block_len);
|
||||
},
|
||||
DW.FORM_data1 => parseFormValueConstant(allocator, in_stream, false, 1),
|
||||
DW.FORM_data2 => parseFormValueConstant(allocator, in_stream, false, 2),
|
||||
@ -535,7 +535,7 @@ fn parseFormValue(allocator: &mem.Allocator, in_stream: &io.InStream, form_id: u
|
||||
DW.FORM_udata, DW.FORM_sdata => {
|
||||
const block_len = %return readULeb128(in_stream);
|
||||
const signed = form_id == DW.FORM_sdata;
|
||||
parseFormValueConstant(allocator, in_stream, signed, block_len)
|
||||
return parseFormValueConstant(allocator, in_stream, signed, block_len);
|
||||
},
|
||||
DW.FORM_exprloc => {
|
||||
const size = %return readULeb128(in_stream);
|
||||
@ -552,7 +552,7 @@ fn parseFormValue(allocator: &mem.Allocator, in_stream: &io.InStream, form_id: u
|
||||
DW.FORM_ref8 => parseFormValueRef(allocator, in_stream, u64),
|
||||
DW.FORM_ref_udata => {
|
||||
const ref_len = %return readULeb128(in_stream);
|
||||
parseFormValueRefLen(allocator, in_stream, ref_len)
|
||||
return parseFormValueRefLen(allocator, in_stream, ref_len);
|
||||
},
|
||||
|
||||
DW.FORM_ref_addr => FormValue { .RefAddr = %return parseFormValueDwarfOffsetSize(in_stream, is_64) },
|
||||
@ -562,10 +562,10 @@ fn parseFormValue(allocator: &mem.Allocator, in_stream: &io.InStream, form_id: u
|
||||
DW.FORM_strp => FormValue { .StrPtr = %return parseFormValueDwarfOffsetSize(in_stream, is_64) },
|
||||
DW.FORM_indirect => {
|
||||
const child_form_id = %return readULeb128(in_stream);
|
||||
parseFormValue(allocator, in_stream, child_form_id, is_64)
|
||||
return parseFormValue(allocator, in_stream, child_form_id, is_64);
|
||||
},
|
||||
else => error.InvalidDebugInfo,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn parseAbbrevTable(st: &ElfStackTrace) -> %AbbrevTable {
|
||||
@ -842,11 +842,9 @@ fn scanAllCompileUnits(st: &ElfStackTrace) -> %void {
|
||||
const version = %return in_stream.readInt(st.elf.endian, u16);
|
||||
if (version < 2 or version > 5) return error.InvalidDebugInfo;
|
||||
|
||||
const debug_abbrev_offset = if (is_64) {
|
||||
%return in_stream.readInt(st.elf.endian, u64)
|
||||
} else {
|
||||
%return in_stream.readInt(st.elf.endian, u32)
|
||||
};
|
||||
const debug_abbrev_offset =
|
||||
if (is_64) %return in_stream.readInt(st.elf.endian, u64)
|
||||
else %return in_stream.readInt(st.elf.endian, u32);
|
||||
|
||||
const address_size = %return in_stream.readByte();
|
||||
if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
|
||||
@ -862,28 +860,28 @@ fn scanAllCompileUnits(st: &ElfStackTrace) -> %void {
|
||||
if (compile_unit_die.tag_id != DW.TAG_compile_unit)
|
||||
return error.InvalidDebugInfo;
|
||||
|
||||
const pc_range = {
|
||||
const pc_range = x: {
|
||||
if (compile_unit_die.getAttrAddr(DW.AT_low_pc)) |low_pc| {
|
||||
if (compile_unit_die.getAttr(DW.AT_high_pc)) |high_pc_value| {
|
||||
const pc_end = switch (*high_pc_value) {
|
||||
FormValue.Address => |value| value,
|
||||
FormValue.Const => |value| {
|
||||
FormValue.Const => |value| b: {
|
||||
const offset = %return value.asUnsignedLe();
|
||||
low_pc + offset
|
||||
break :b (low_pc + offset);
|
||||
},
|
||||
else => return error.InvalidDebugInfo,
|
||||
};
|
||||
PcRange {
|
||||
break :x PcRange {
|
||||
.start = low_pc,
|
||||
.end = pc_end,
|
||||
}
|
||||
};
|
||||
} else {
|
||||
null
|
||||
break :x null;
|
||||
}
|
||||
} else |err| {
|
||||
if (err != error.MissingDebugInfo)
|
||||
return err;
|
||||
null
|
||||
break :x null;
|
||||
}
|
||||
};
|
||||
|
||||
@ -900,25 +898,51 @@ fn scanAllCompileUnits(st: &ElfStackTrace) -> %void {
|
||||
}
|
||||
}
|
||||
|
||||
fn findCompileUnit(st: &ElfStackTrace, target_address: u64) -> ?&const CompileUnit {
|
||||
fn findCompileUnit(st: &ElfStackTrace, target_address: u64) -> %&const CompileUnit {
|
||||
var in_file_stream = io.FileInStream.init(&st.self_exe_file);
|
||||
const in_stream = &in_file_stream.stream;
|
||||
for (st.compile_unit_list.toSlice()) |*compile_unit| {
|
||||
if (compile_unit.pc_range) |range| {
|
||||
if (target_address >= range.start and target_address < range.end)
|
||||
return compile_unit;
|
||||
}
|
||||
if (compile_unit.die.getAttrSecOffset(DW.AT_ranges)) |ranges_offset| {
|
||||
var base_address: usize = 0;
|
||||
if (st.debug_ranges) |debug_ranges| {
|
||||
%return st.self_exe_file.seekTo(debug_ranges.offset + ranges_offset);
|
||||
while (true) {
|
||||
const begin_addr = %return in_stream.readIntLe(usize);
|
||||
const end_addr = %return in_stream.readIntLe(usize);
|
||||
if (begin_addr == 0 and end_addr == 0) {
|
||||
break;
|
||||
}
|
||||
if (begin_addr == @maxValue(usize)) {
|
||||
base_address = begin_addr;
|
||||
continue;
|
||||
}
|
||||
if (target_address >= begin_addr and target_address < end_addr) {
|
||||
return compile_unit;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else |err| {
|
||||
if (err != error.MissingDebugInfo)
|
||||
return err;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return error.MissingDebugInfo;
|
||||
}
|
||||
|
||||
fn readInitialLength(in_stream: &io.InStream, is_64: &bool) -> %u64 {
|
||||
const first_32_bits = %return in_stream.readIntLe(u32);
|
||||
*is_64 = (first_32_bits == 0xffffffff);
|
||||
return if (*is_64) {
|
||||
%return in_stream.readIntLe(u64)
|
||||
if (*is_64) {
|
||||
return in_stream.readIntLe(u64);
|
||||
} else {
|
||||
if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo;
|
||||
u64(first_32_bits)
|
||||
};
|
||||
return u64(first_32_bits);
|
||||
}
|
||||
}
|
||||
|
||||
fn readULeb128(in_stream: &io.InStream) -> %u64 {
|
||||
@ -965,40 +989,62 @@ fn readILeb128(in_stream: &io.InStream) -> %i64 {
|
||||
}
|
||||
}
|
||||
|
||||
pub const global_allocator = &global_allocator_state;
|
||||
var global_allocator_state = mem.Allocator {
|
||||
.allocFn = globalAlloc,
|
||||
.reallocFn = globalRealloc,
|
||||
.freeFn = globalFree,
|
||||
};
|
||||
pub const global_allocator = &global_fixed_allocator.allocator;
|
||||
var global_fixed_allocator = mem.FixedBufferAllocator.init(global_allocator_mem[0..]);
|
||||
var global_allocator_mem: [100 * 1024]u8 = undefined;
|
||||
|
||||
var some_mem: [100 * 1024]u8 = undefined;
|
||||
var some_mem_index: usize = 0;
|
||||
/// Allocator that fails after N allocations, useful for making sure out of
|
||||
/// memory conditions are handled correctly.
|
||||
pub const FailingAllocator = struct {
|
||||
allocator: mem.Allocator,
|
||||
index: usize,
|
||||
fail_index: usize,
|
||||
internal_allocator: &mem.Allocator,
|
||||
allocated_bytes: usize,
|
||||
|
||||
error OutOfMemory;
|
||||
|
||||
fn globalAlloc(self: &mem.Allocator, n: usize, alignment: usize) -> %[]u8 {
|
||||
const addr = @ptrToInt(&some_mem[some_mem_index]);
|
||||
const rem = @rem(addr, alignment);
|
||||
const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
|
||||
const adjusted_index = some_mem_index + march_forward_bytes;
|
||||
const end_index = adjusted_index + n;
|
||||
if (end_index > some_mem.len) {
|
||||
return error.OutOfMemory;
|
||||
pub fn init(allocator: &mem.Allocator, fail_index: usize) -> FailingAllocator {
|
||||
return FailingAllocator {
|
||||
.internal_allocator = allocator,
|
||||
.fail_index = fail_index,
|
||||
.index = 0,
|
||||
.allocated_bytes = 0,
|
||||
.allocator = mem.Allocator {
|
||||
.allocFn = alloc,
|
||||
.reallocFn = realloc,
|
||||
.freeFn = free,
|
||||
},
|
||||
};
|
||||
}
|
||||
const result = some_mem[adjusted_index .. end_index];
|
||||
some_mem_index = end_index;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn globalRealloc(self: &mem.Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 {
|
||||
if (new_size <= old_mem.len) {
|
||||
return old_mem[0..new_size];
|
||||
} else {
|
||||
const result = %return globalAlloc(self, new_size, alignment);
|
||||
@memcpy(result.ptr, old_mem.ptr, old_mem.len);
|
||||
fn alloc(allocator: &mem.Allocator, n: usize, alignment: u29) -> %[]u8 {
|
||||
const self = @fieldParentPtr(FailingAllocator, "allocator", allocator);
|
||||
if (self.index == self.fail_index) {
|
||||
return error.OutOfMemory;
|
||||
}
|
||||
self.index += 1;
|
||||
const result = %return self.internal_allocator.allocFn(self.internal_allocator, n, alignment);
|
||||
self.allocated_bytes += result.len;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
fn globalFree(self: &mem.Allocator, memory: []u8) { }
|
||||
fn realloc(allocator: &mem.Allocator, old_mem: []u8, new_size: usize, alignment: u29) -> %[]u8 {
|
||||
const self = @fieldParentPtr(FailingAllocator, "allocator", allocator);
|
||||
if (new_size <= old_mem.len) {
|
||||
self.allocated_bytes -= old_mem.len - new_size;
|
||||
return self.internal_allocator.reallocFn(self.internal_allocator, old_mem, new_size, alignment);
|
||||
}
|
||||
if (self.index == self.fail_index) {
|
||||
return error.OutOfMemory;
|
||||
}
|
||||
self.index += 1;
|
||||
const result = %return self.internal_allocator.reallocFn(self.internal_allocator, old_mem, new_size, alignment);
|
||||
self.allocated_bytes += new_size - old_mem.len;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn free(allocator: &mem.Allocator, bytes: []u8) {
|
||||
const self = @fieldParentPtr(FailingAllocator, "allocator", allocator);
|
||||
self.allocated_bytes -= bytes.len;
|
||||
return self.internal_allocator.freeFn(self.internal_allocator, bytes);
|
||||
}
|
||||
};
|
||||
|
||||
66
std/elf.zig
66
std/elf.zig
@ -188,39 +188,39 @@ pub const Elf = struct {
|
||||
if (elf.is_64) {
|
||||
if (sh_entry_size != 64) return error.InvalidFormat;
|
||||
|
||||
for (elf.section_headers) |*section| {
|
||||
section.name = %return in.readInt(elf.endian, u32);
|
||||
section.sh_type = %return in.readInt(elf.endian, u32);
|
||||
section.flags = %return in.readInt(elf.endian, u64);
|
||||
section.addr = %return in.readInt(elf.endian, u64);
|
||||
section.offset = %return in.readInt(elf.endian, u64);
|
||||
section.size = %return in.readInt(elf.endian, u64);
|
||||
section.link = %return in.readInt(elf.endian, u32);
|
||||
section.info = %return in.readInt(elf.endian, u32);
|
||||
section.addr_align = %return in.readInt(elf.endian, u64);
|
||||
section.ent_size = %return in.readInt(elf.endian, u64);
|
||||
for (elf.section_headers) |*elf_section| {
|
||||
elf_section.name = %return in.readInt(elf.endian, u32);
|
||||
elf_section.sh_type = %return in.readInt(elf.endian, u32);
|
||||
elf_section.flags = %return in.readInt(elf.endian, u64);
|
||||
elf_section.addr = %return in.readInt(elf.endian, u64);
|
||||
elf_section.offset = %return in.readInt(elf.endian, u64);
|
||||
elf_section.size = %return in.readInt(elf.endian, u64);
|
||||
elf_section.link = %return in.readInt(elf.endian, u32);
|
||||
elf_section.info = %return in.readInt(elf.endian, u32);
|
||||
elf_section.addr_align = %return in.readInt(elf.endian, u64);
|
||||
elf_section.ent_size = %return in.readInt(elf.endian, u64);
|
||||
}
|
||||
} else {
|
||||
if (sh_entry_size != 40) return error.InvalidFormat;
|
||||
|
||||
for (elf.section_headers) |*section| {
|
||||
for (elf.section_headers) |*elf_section| {
|
||||
// TODO (multiple occurences) allow implicit cast from %u32 -> %u64 ?
|
||||
section.name = %return in.readInt(elf.endian, u32);
|
||||
section.sh_type = %return in.readInt(elf.endian, u32);
|
||||
section.flags = u64(%return in.readInt(elf.endian, u32));
|
||||
section.addr = u64(%return in.readInt(elf.endian, u32));
|
||||
section.offset = u64(%return in.readInt(elf.endian, u32));
|
||||
section.size = u64(%return in.readInt(elf.endian, u32));
|
||||
section.link = %return in.readInt(elf.endian, u32);
|
||||
section.info = %return in.readInt(elf.endian, u32);
|
||||
section.addr_align = u64(%return in.readInt(elf.endian, u32));
|
||||
section.ent_size = u64(%return in.readInt(elf.endian, u32));
|
||||
elf_section.name = %return in.readInt(elf.endian, u32);
|
||||
elf_section.sh_type = %return in.readInt(elf.endian, u32);
|
||||
elf_section.flags = u64(%return in.readInt(elf.endian, u32));
|
||||
elf_section.addr = u64(%return in.readInt(elf.endian, u32));
|
||||
elf_section.offset = u64(%return in.readInt(elf.endian, u32));
|
||||
elf_section.size = u64(%return in.readInt(elf.endian, u32));
|
||||
elf_section.link = %return in.readInt(elf.endian, u32);
|
||||
elf_section.info = %return in.readInt(elf.endian, u32);
|
||||
elf_section.addr_align = u64(%return in.readInt(elf.endian, u32));
|
||||
elf_section.ent_size = u64(%return in.readInt(elf.endian, u32));
|
||||
}
|
||||
}
|
||||
|
||||
for (elf.section_headers) |*section| {
|
||||
if (section.sh_type != SHT_NOBITS) {
|
||||
const file_end_offset = %return math.add(u64, section.offset, section.size);
|
||||
for (elf.section_headers) |*elf_section| {
|
||||
if (elf_section.sh_type != SHT_NOBITS) {
|
||||
const file_end_offset = %return math.add(u64, elf_section.offset, elf_section.size);
|
||||
if (stream_end < file_end_offset) return error.InvalidFormat;
|
||||
}
|
||||
}
|
||||
@ -243,29 +243,27 @@ pub const Elf = struct {
|
||||
var file_stream = io.FileInStream.init(elf.in_file);
|
||||
const in = &file_stream.stream;
|
||||
|
||||
for (elf.section_headers) |*section| {
|
||||
if (section.sh_type == SHT_NULL) continue;
|
||||
section_loop: for (elf.section_headers) |*elf_section| {
|
||||
if (elf_section.sh_type == SHT_NULL) continue;
|
||||
|
||||
const name_offset = elf.string_section.offset + section.name;
|
||||
const name_offset = elf.string_section.offset + elf_section.name;
|
||||
%return elf.in_file.seekTo(name_offset);
|
||||
|
||||
for (name) |expected_c| {
|
||||
const target_c = %return in.readByte();
|
||||
if (target_c == 0 or expected_c != target_c) goto next_section;
|
||||
if (target_c == 0 or expected_c != target_c) continue :section_loop;
|
||||
}
|
||||
|
||||
{
|
||||
const null_byte = %return in.readByte();
|
||||
if (null_byte == 0) return section;
|
||||
if (null_byte == 0) return elf_section;
|
||||
}
|
||||
|
||||
next_section:
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn seekToSection(elf: &Elf, section: &SectionHeader) -> %void {
|
||||
%return elf.in_file.seekTo(section.offset);
|
||||
pub fn seekToSection(elf: &Elf, elf_section: &SectionHeader) -> %void {
|
||||
%return elf.in_file.seekTo(elf_section.offset);
|
||||
}
|
||||
};
|
||||
|
||||
@ -2,15 +2,15 @@ const mem = @import("mem.zig");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
pub fn swapIfLe(comptime T: type, x: T) -> T {
|
||||
swapIf(false, T, x)
|
||||
return swapIf(false, T, x);
|
||||
}
|
||||
|
||||
pub fn swapIfBe(comptime T: type, x: T) -> T {
|
||||
swapIf(true, T, x)
|
||||
return swapIf(true, T, x);
|
||||
}
|
||||
|
||||
pub fn swapIf(endian: builtin.Endian, comptime T: type, x: T) -> T {
|
||||
if (builtin.endian == endian) swap(T, x) else x
|
||||
return if (builtin.endian == endian) swap(T, x) else x;
|
||||
}
|
||||
|
||||
pub fn swap(comptime T: type, x: T) -> T {
|
||||
|
||||
@ -439,10 +439,10 @@ const Slab = struct {
|
||||
};
|
||||
|
||||
fn slab(str: []const u8, exp: i32) -> Slab {
|
||||
Slab {
|
||||
return Slab {
|
||||
.str = str,
|
||||
.exp = exp,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub const enum3_data = []Slab {
|
||||
|
||||
@ -251,11 +251,10 @@ pub fn formatFloat(value: var, context: var, output: fn(@typeOf(context), []cons
|
||||
%return output(context, float_decimal.digits[0..1]);
|
||||
%return output(context, ".");
|
||||
if (float_decimal.digits.len > 1) {
|
||||
const num_digits = if (@typeOf(value) == f32) {
|
||||
const num_digits = if (@typeOf(value) == f32)
|
||||
math.min(usize(9), float_decimal.digits.len)
|
||||
} else {
|
||||
float_decimal.digits.len
|
||||
};
|
||||
else
|
||||
float_decimal.digits.len;
|
||||
%return output(context, float_decimal.digits[1 .. num_digits]);
|
||||
} else {
|
||||
%return output(context, "0");
|
||||
@ -372,6 +371,10 @@ test "fmt.parseInt" {
|
||||
assert(%%parseInt(i32, "-10", 10) == -10);
|
||||
assert(%%parseInt(i32, "+10", 10) == 10);
|
||||
assert(if (parseInt(i32, " 10", 10)) |_| false else |err| err == error.InvalidChar);
|
||||
assert(if (parseInt(i32, "10 ", 10)) |_| false else |err| err == error.InvalidChar);
|
||||
assert(if (parseInt(u32, "-10", 10)) |_| false else |err| err == error.InvalidChar);
|
||||
assert(%%parseInt(u8, "255", 10) == 255);
|
||||
assert(if (parseInt(u8, "256", 10)) |_| false else |err| err == error.Overflow);
|
||||
}
|
||||
|
||||
pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) -> %T {
|
||||
@ -413,14 +416,16 @@ const BufPrintContext = struct {
|
||||
remaining: []u8,
|
||||
};
|
||||
|
||||
error BufferTooSmall;
|
||||
fn bufPrintWrite(context: &BufPrintContext, bytes: []const u8) -> %void {
|
||||
if (context.remaining.len < bytes.len) return error.BufferTooSmall;
|
||||
mem.copy(u8, context.remaining, bytes);
|
||||
context.remaining = context.remaining[bytes.len..];
|
||||
}
|
||||
|
||||
pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) -> []u8 {
|
||||
pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) -> %[]u8 {
|
||||
var context = BufPrintContext { .remaining = buf, };
|
||||
%%format(&context, bufPrintWrite, fmt, args);
|
||||
%return format(&context, bufPrintWrite, fmt, args);
|
||||
return buf[0..buf.len - context.remaining.len];
|
||||
}
|
||||
|
||||
@ -476,31 +481,31 @@ test "fmt.format" {
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: ?i32 = 1234;
|
||||
const result = bufPrint(buf1[0..], "nullable: {}\n", value);
|
||||
const result = %%bufPrint(buf1[0..], "nullable: {}\n", value);
|
||||
assert(mem.eql(u8, result, "nullable: 1234\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: ?i32 = null;
|
||||
const result = bufPrint(buf1[0..], "nullable: {}\n", value);
|
||||
const result = %%bufPrint(buf1[0..], "nullable: {}\n", value);
|
||||
assert(mem.eql(u8, result, "nullable: null\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: %i32 = 1234;
|
||||
const result = bufPrint(buf1[0..], "error union: {}\n", value);
|
||||
const result = %%bufPrint(buf1[0..], "error union: {}\n", value);
|
||||
assert(mem.eql(u8, result, "error union: 1234\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: %i32 = error.InvalidChar;
|
||||
const result = bufPrint(buf1[0..], "error union: {}\n", value);
|
||||
const result = %%bufPrint(buf1[0..], "error union: {}\n", value);
|
||||
assert(mem.eql(u8, result, "error union: error.InvalidChar\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: u3 = 0b101;
|
||||
const result = bufPrint(buf1[0..], "u3: {}\n", value);
|
||||
const result = %%bufPrint(buf1[0..], "u3: {}\n", value);
|
||||
assert(mem.eql(u8, result, "u3: 5\n"));
|
||||
}
|
||||
|
||||
@ -510,28 +515,28 @@ test "fmt.format" {
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f32 = 12.34;
|
||||
const result = bufPrint(buf1[0..], "f32: {}\n", value);
|
||||
const result = %%bufPrint(buf1[0..], "f32: {}\n", value);
|
||||
assert(mem.eql(u8, result, "f32: 1.23400001e1\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value: f64 = -12.34e10;
|
||||
const result = bufPrint(buf1[0..], "f64: {}\n", value);
|
||||
const result = %%bufPrint(buf1[0..], "f64: {}\n", value);
|
||||
assert(mem.eql(u8, result, "f64: -1.234e11\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const result = bufPrint(buf1[0..], "f64: {}\n", math.nan_f64);
|
||||
const result = %%bufPrint(buf1[0..], "f64: {}\n", math.nan_f64);
|
||||
assert(mem.eql(u8, result, "f64: NaN\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const result = bufPrint(buf1[0..], "f64: {}\n", math.inf_f64);
|
||||
const result = %%bufPrint(buf1[0..], "f64: {}\n", math.inf_f64);
|
||||
assert(mem.eql(u8, result, "f64: Infinity\n"));
|
||||
}
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
const result = bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64);
|
||||
const result = %%bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64);
|
||||
assert(mem.eql(u8, result, "f64: -Infinity\n"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
|
||||
comptime hash: fn(key: K)->u32,
|
||||
comptime eql: fn(a: K, b: K)->bool) -> type
|
||||
{
|
||||
struct {
|
||||
return struct {
|
||||
entries: []Entry,
|
||||
size: usize,
|
||||
max_distance_from_start_index: usize,
|
||||
@ -51,19 +51,19 @@ pub fn HashMap(comptime K: type, comptime V: type,
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
unreachable // no next item
|
||||
unreachable; // no next item
|
||||
}
|
||||
};
|
||||
|
||||
pub fn init(allocator: &Allocator) -> Self {
|
||||
Self {
|
||||
return Self {
|
||||
.entries = []Entry{},
|
||||
.allocator = allocator,
|
||||
.size = 0,
|
||||
.max_distance_from_start_index = 0,
|
||||
// it doesn't actually matter what we set this to since we use wrapping integer arithmetic
|
||||
.modification_count = undefined,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(hm: &Self) {
|
||||
@ -133,7 +133,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
|
||||
entry.distance_from_start_index -= 1;
|
||||
entry = next_entry;
|
||||
}
|
||||
unreachable // shifting everything in the table
|
||||
unreachable; // shifting everything in the table
|
||||
}}
|
||||
return null;
|
||||
}
|
||||
@ -169,7 +169,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
|
||||
const start_index = hm.keyToIndex(key);
|
||||
var roll_over: usize = 0;
|
||||
var distance_from_start_index: usize = 0;
|
||||
while (roll_over < hm.entries.len) : ({roll_over += 1; distance_from_start_index += 1}) {
|
||||
while (roll_over < hm.entries.len) : ({roll_over += 1; distance_from_start_index += 1;}) {
|
||||
const index = (start_index + roll_over) % hm.entries.len;
|
||||
const entry = &hm.entries[index];
|
||||
|
||||
@ -210,7 +210,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
|
||||
};
|
||||
return result;
|
||||
}
|
||||
unreachable // put into a full map
|
||||
unreachable; // put into a full map
|
||||
}
|
||||
|
||||
fn internalGet(hm: &Self, key: K) -> ?&Entry {
|
||||
@ -228,7 +228,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
|
||||
fn keyToIndex(hm: &Self, key: K) -> usize {
|
||||
return usize(hash(key)) % hm.entries.len;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test "basicHashMapTest" {
|
||||
@ -251,9 +251,9 @@ test "basicHashMapTest" {
|
||||
}
|
||||
|
||||
fn hash_i32(x: i32) -> u32 {
|
||||
@bitCast(u32, x)
|
||||
return @bitCast(u32, x);
|
||||
}
|
||||
|
||||
fn eql_i32(a: i32, b: i32) -> bool {
|
||||
a == b
|
||||
return a == b;
|
||||
}
|
||||
|
||||
32
std/heap.zig
32
std/heap.zig
@ -10,30 +10,28 @@ const Allocator = mem.Allocator;
|
||||
|
||||
error OutOfMemory;
|
||||
|
||||
pub var c_allocator = Allocator {
|
||||
pub const c_allocator = &c_allocator_state;
|
||||
var c_allocator_state = Allocator {
|
||||
.allocFn = cAlloc,
|
||||
.reallocFn = cRealloc,
|
||||
.freeFn = cFree,
|
||||
};
|
||||
|
||||
fn cAlloc(self: &Allocator, n: usize, alignment: usize) -> %[]u8 {
|
||||
if (c.malloc(usize(n))) |buf| {
|
||||
fn cAlloc(self: &Allocator, n: usize, alignment: u29) -> %[]u8 {
|
||||
return if (c.malloc(usize(n))) |buf|
|
||||
@ptrCast(&u8, buf)[0..n]
|
||||
} else {
|
||||
error.OutOfMemory
|
||||
}
|
||||
else
|
||||
error.OutOfMemory;
|
||||
}
|
||||
|
||||
fn cRealloc(self: &Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 {
|
||||
if (new_size <= old_mem.len) {
|
||||
old_mem[0..new_size]
|
||||
fn cRealloc(self: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) -> %[]u8 {
|
||||
const old_ptr = @ptrCast(&c_void, old_mem.ptr);
|
||||
if (c.realloc(old_ptr, new_size)) |buf| {
|
||||
return @ptrCast(&u8, buf)[0..new_size];
|
||||
} else if (new_size <= old_mem.len) {
|
||||
return old_mem[0..new_size];
|
||||
} else {
|
||||
const old_ptr = @ptrCast(&c_void, old_mem.ptr);
|
||||
if (c.realloc(old_ptr, usize(new_size))) |buf| {
|
||||
@ptrCast(&u8, buf)[0..new_size]
|
||||
} else {
|
||||
error.OutOfMemory
|
||||
}
|
||||
return error.OutOfMemory;
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,7 +104,7 @@ pub const IncrementingAllocator = struct {
|
||||
return self.bytes.len - self.end_index;
|
||||
}
|
||||
|
||||
fn alloc(allocator: &Allocator, n: usize, alignment: usize) -> %[]u8 {
|
||||
fn alloc(allocator: &Allocator, n: usize, alignment: u29) -> %[]u8 {
|
||||
const self = @fieldParentPtr(IncrementingAllocator, "allocator", allocator);
|
||||
const addr = @ptrToInt(&self.bytes[self.end_index]);
|
||||
const rem = @rem(addr, alignment);
|
||||
@ -121,7 +119,7 @@ pub const IncrementingAllocator = struct {
|
||||
return result;
|
||||
}
|
||||
|
||||
fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 {
|
||||
fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) -> %[]u8 {
|
||||
if (new_size <= old_mem.len) {
|
||||
return old_mem[0..new_size];
|
||||
} else {
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
pub const ArrayList = @import("array_list.zig").ArrayList;
|
||||
pub const AlignedArrayList = @import("array_list.zig").AlignedArrayList;
|
||||
pub const BufMap = @import("buf_map.zig").BufMap;
|
||||
pub const BufSet = @import("buf_set.zig").BufSet;
|
||||
pub const Buffer = @import("buffer.zig").Buffer;
|
||||
pub const BufferOutStream = @import("buffer.zig").BufferOutStream;
|
||||
pub const HashMap = @import("hash_map.zig").HashMap;
|
||||
pub const LinkedList = @import("linked_list.zig").LinkedList;
|
||||
|
||||
@ -26,12 +28,12 @@ pub const sort = @import("sort.zig");
|
||||
|
||||
test "std" {
|
||||
// run tests from these
|
||||
_ = @import("array_list.zig").ArrayList;
|
||||
_ = @import("buf_map.zig").BufMap;
|
||||
_ = @import("buf_set.zig").BufSet;
|
||||
_ = @import("buffer.zig").Buffer;
|
||||
_ = @import("hash_map.zig").HashMap;
|
||||
_ = @import("linked_list.zig").LinkedList;
|
||||
_ = @import("array_list.zig");
|
||||
_ = @import("buf_map.zig");
|
||||
_ = @import("buf_set.zig");
|
||||
_ = @import("buffer.zig");
|
||||
_ = @import("hash_map.zig");
|
||||
_ = @import("linked_list.zig");
|
||||
|
||||
_ = @import("base64.zig");
|
||||
_ = @import("build.zig");
|
||||
|
||||
72
std/io.zig
72
std/io.zig
@ -50,35 +50,32 @@ error Unseekable;
|
||||
error EndOfFile;
|
||||
|
||||
pub fn getStdErr() -> %File {
|
||||
const handle = if (is_windows) {
|
||||
const handle = if (is_windows)
|
||||
%return os.windowsGetStdHandle(system.STD_ERROR_HANDLE)
|
||||
} else if (is_posix) {
|
||||
else if (is_posix)
|
||||
system.STDERR_FILENO
|
||||
} else {
|
||||
unreachable
|
||||
};
|
||||
else
|
||||
unreachable;
|
||||
return File.openHandle(handle);
|
||||
}
|
||||
|
||||
pub fn getStdOut() -> %File {
|
||||
const handle = if (is_windows) {
|
||||
const handle = if (is_windows)
|
||||
%return os.windowsGetStdHandle(system.STD_OUTPUT_HANDLE)
|
||||
} else if (is_posix) {
|
||||
else if (is_posix)
|
||||
system.STDOUT_FILENO
|
||||
} else {
|
||||
unreachable
|
||||
};
|
||||
else
|
||||
unreachable;
|
||||
return File.openHandle(handle);
|
||||
}
|
||||
|
||||
pub fn getStdIn() -> %File {
|
||||
const handle = if (is_windows) {
|
||||
const handle = if (is_windows)
|
||||
%return os.windowsGetStdHandle(system.STD_INPUT_HANDLE)
|
||||
} else if (is_posix) {
|
||||
else if (is_posix)
|
||||
system.STDIN_FILENO
|
||||
} else {
|
||||
unreachable
|
||||
};
|
||||
else
|
||||
unreachable;
|
||||
return File.openHandle(handle);
|
||||
}
|
||||
|
||||
@ -261,7 +258,7 @@ pub const File = struct {
|
||||
system.EBADF => error.BadFd,
|
||||
system.ENOMEM => error.SystemResources,
|
||||
else => os.unexpectedErrorPosix(err),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return usize(stat.size);
|
||||
@ -481,6 +478,14 @@ pub const OutStream = struct {
|
||||
const slice = (&byte)[0..1];
|
||||
return self.writeFn(self, slice);
|
||||
}
|
||||
|
||||
pub fn writeByteNTimes(self: &OutStream, byte: u8, n: usize) -> %void {
|
||||
const slice = (&byte)[0..1];
|
||||
var i: usize = 0;
|
||||
while (i < n) : (i += 1) {
|
||||
%return self.writeFn(self, slice);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// `path` may need to be copied in memory to add a null terminating byte. In this case
|
||||
@ -493,6 +498,20 @@ pub fn writeFile(path: []const u8, data: []const u8, allocator: ?&mem.Allocator)
|
||||
%return file.write(data);
|
||||
}
|
||||
|
||||
/// On success, caller owns returned buffer.
|
||||
pub fn readFileAlloc(path: []const u8, allocator: &mem.Allocator) -> %[]u8 {
|
||||
var file = %return File.openRead(path, allocator);
|
||||
defer file.close();
|
||||
|
||||
const size = %return file.getEndPos();
|
||||
const buf = %return allocator.alloc(u8, size);
|
||||
%defer allocator.free(buf);
|
||||
|
||||
var adapter = FileInStream.init(&file);
|
||||
%return adapter.stream.readNoEof(buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
pub const BufferedInStream = BufferedInStreamCustom(os.page_size);
|
||||
|
||||
pub fn BufferedInStreamCustom(comptime buffer_size: usize) -> type {
|
||||
@ -619,3 +638,24 @@ pub fn BufferedOutStreamCustom(comptime buffer_size: usize) -> type {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Implementation of OutStream trait for Buffer
|
||||
pub const BufferOutStream = struct {
|
||||
buffer: &Buffer,
|
||||
stream: OutStream,
|
||||
|
||||
pub fn init(buffer: &Buffer) -> BufferOutStream {
|
||||
return BufferOutStream {
|
||||
.buffer = buffer,
|
||||
.stream = OutStream {
|
||||
.writeFn = writeFn,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fn writeFn(out_stream: &OutStream, bytes: []const u8) -> %void {
|
||||
const self = @fieldParentPtr(BufferOutStream, "stream", out_stream);
|
||||
return self.buffer.append(bytes);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ const Allocator = mem.Allocator;
|
||||
|
||||
/// Generic doubly linked list.
|
||||
pub fn LinkedList(comptime T: type) -> type {
|
||||
struct {
|
||||
return struct {
|
||||
const Self = this;
|
||||
|
||||
/// Node inside the linked list wrapping the actual data.
|
||||
@ -15,11 +15,11 @@ pub fn LinkedList(comptime T: type) -> type {
|
||||
data: T,
|
||||
|
||||
pub fn init(data: &const T) -> Node {
|
||||
Node {
|
||||
return Node {
|
||||
.prev = null,
|
||||
.next = null,
|
||||
.data = *data,
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@ -32,11 +32,11 @@ pub fn LinkedList(comptime T: type) -> type {
|
||||
/// Returns:
|
||||
/// An empty linked list.
|
||||
pub fn init() -> Self {
|
||||
Self {
|
||||
return Self {
|
||||
.first = null,
|
||||
.last = null,
|
||||
.len = 0,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Insert a new node after an existing one.
|
||||
@ -166,7 +166,7 @@ pub fn LinkedList(comptime T: type) -> type {
|
||||
/// Returns:
|
||||
/// A pointer to the new node.
|
||||
pub fn allocateNode(list: &Self, allocator: &Allocator) -> %&Node {
|
||||
allocator.create(Node)
|
||||
return allocator.create(Node);
|
||||
}
|
||||
|
||||
/// Deallocate a node.
|
||||
@ -191,7 +191,7 @@ pub fn LinkedList(comptime T: type) -> type {
|
||||
*node = Node.init(data);
|
||||
return node;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test "basic linked list test" {
|
||||
|
||||
@ -7,11 +7,11 @@ const assert = @import("../debug.zig").assert;
|
||||
|
||||
pub fn acos(x: var) -> @typeOf(x) {
|
||||
const T = @typeOf(x);
|
||||
switch (T) {
|
||||
f32 => @inlineCall(acos32, x),
|
||||
f64 => @inlineCall(acos64, x),
|
||||
return switch (T) {
|
||||
f32 => acos32(x),
|
||||
f64 => acos64(x),
|
||||
else => @compileError("acos not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn r32(z: f32) -> f32 {
|
||||
@ -22,7 +22,7 @@ fn r32(z: f32) -> f32 {
|
||||
|
||||
const p = z * (pS0 + z * (pS1 + z * pS2));
|
||||
const q = 1.0 + z * qS1;
|
||||
p / q
|
||||
return p / q;
|
||||
}
|
||||
|
||||
fn acos32(x: f32) -> f32 {
|
||||
@ -69,7 +69,7 @@ fn acos32(x: f32) -> f32 {
|
||||
const df = @bitCast(f32, jx & 0xFFFFF000);
|
||||
const c = (z - df * df) / (s + df);
|
||||
const w = r32(z) * s + c;
|
||||
2 * (df + w)
|
||||
return 2 * (df + w);
|
||||
}
|
||||
|
||||
fn r64(z: f64) -> f64 {
|
||||
@ -86,7 +86,7 @@ fn r64(z: f64) -> f64 {
|
||||
|
||||
const p = z * (pS0 + z * (pS1 + z * (pS2 + z * (pS3 + z * (pS4 + z * pS5)))));
|
||||
const q = 1.0 + z * (qS1 + z * (qS2 + z * (qS3 + z * qS4)));
|
||||
p / q
|
||||
return p / q;
|
||||
}
|
||||
|
||||
fn acos64(x: f64) -> f64 {
|
||||
@ -138,7 +138,7 @@ fn acos64(x: f64) -> f64 {
|
||||
const df = @bitCast(f64, jx & 0xFFFFFFFF00000000);
|
||||
const c = (z - df * df) / (s + df);
|
||||
const w = r64(z) * s + c;
|
||||
2 * (df + w)
|
||||
return 2 * (df + w);
|
||||
}
|
||||
|
||||
test "math.acos" {
|
||||
|
||||
@ -9,11 +9,11 @@ const assert = @import("../debug.zig").assert;
|
||||
|
||||
pub fn acosh(x: var) -> @typeOf(x) {
|
||||
const T = @typeOf(x);
|
||||
switch (T) {
|
||||
f32 => @inlineCall(acosh32, x),
|
||||
f64 => @inlineCall(acosh64, x),
|
||||
return switch (T) {
|
||||
f32 => acosh32(x),
|
||||
f64 => acosh64(x),
|
||||
else => @compileError("acosh not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// acosh(x) = log(x + sqrt(x * x - 1))
|
||||
@ -23,15 +23,15 @@ fn acosh32(x: f32) -> f32 {
|
||||
|
||||
// |x| < 2, invalid if x < 1 or nan
|
||||
if (i < 0x3F800000 + (1 << 23)) {
|
||||
math.log1p(x - 1 + math.sqrt((x - 1) * (x - 1) + 2 * (x - 1)))
|
||||
return math.log1p(x - 1 + math.sqrt((x - 1) * (x - 1) + 2 * (x - 1)));
|
||||
}
|
||||
// |x| < 0x1p12
|
||||
else if (i < 0x3F800000 + (12 << 23)) {
|
||||
math.ln(2 * x - 1 / (x + math.sqrt(x * x - 1)))
|
||||
return math.ln(2 * x - 1 / (x + math.sqrt(x * x - 1)));
|
||||
}
|
||||
// |x| >= 0x1p12
|
||||
else {
|
||||
math.ln(x) + 0.693147180559945309417232121458176568
|
||||
return math.ln(x) + 0.693147180559945309417232121458176568;
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,15 +41,15 @@ fn acosh64(x: f64) -> f64 {
|
||||
|
||||
// |x| < 2, invalid if x < 1 or nan
|
||||
if (e < 0x3FF + 1) {
|
||||
math.log1p(x - 1 + math.sqrt((x - 1) * (x - 1) + 2 * (x - 1)))
|
||||
return math.log1p(x - 1 + math.sqrt((x - 1) * (x - 1) + 2 * (x - 1)));
|
||||
}
|
||||
// |x| < 0x1p26
|
||||
else if (e < 0x3FF + 26) {
|
||||
math.ln(2 * x - 1 / (x + math.sqrt(x * x - 1)))
|
||||
return math.ln(2 * x - 1 / (x + math.sqrt(x * x - 1)));
|
||||
}
|
||||
// |x| >= 0x1p26 or nan
|
||||
else {
|
||||
math.ln(x) + 0.693147180559945309417232121458176568
|
||||
return math.ln(x) + 0.693147180559945309417232121458176568;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -8,11 +8,11 @@ const assert = @import("../debug.zig").assert;
|
||||
|
||||
pub fn asin(x: var) -> @typeOf(x) {
|
||||
const T = @typeOf(x);
|
||||
switch (T) {
|
||||
f32 => @inlineCall(asin32, x),
|
||||
f64 => @inlineCall(asin64, x),
|
||||
return switch (T) {
|
||||
f32 => asin32(x),
|
||||
f64 => asin64(x),
|
||||
else => @compileError("asin not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn r32(z: f32) -> f32 {
|
||||
@ -23,7 +23,7 @@ fn r32(z: f32) -> f32 {
|
||||
|
||||
const p = z * (pS0 + z * (pS1 + z * pS2));
|
||||
const q = 1.0 + z * qS1;
|
||||
p / q
|
||||
return p / q;
|
||||
}
|
||||
|
||||
fn asin32(x: f32) -> f32 {
|
||||
@ -58,9 +58,9 @@ fn asin32(x: f32) -> f32 {
|
||||
const fx = pio2 - 2 * (s + s * r32(z));
|
||||
|
||||
if (hx >> 31 != 0) {
|
||||
-fx
|
||||
return -fx;
|
||||
} else {
|
||||
fx
|
||||
return fx;
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,7 +78,7 @@ fn r64(z: f64) -> f64 {
|
||||
|
||||
const p = z * (pS0 + z * (pS1 + z * (pS2 + z * (pS3 + z * (pS4 + z * pS5)))));
|
||||
const q = 1.0 + z * (qS1 + z * (qS2 + z * (qS3 + z * qS4)));
|
||||
p / q
|
||||
return p / q;
|
||||
}
|
||||
|
||||
fn asin64(x: f64) -> f64 {
|
||||
@ -119,7 +119,7 @@ fn asin64(x: f64) -> f64 {
|
||||
|
||||
// |x| > 0.975
|
||||
if (ix >= 0x3FEF3333) {
|
||||
fx = pio2_hi - 2 * (s + s * r)
|
||||
fx = pio2_hi - 2 * (s + s * r);
|
||||
} else {
|
||||
const jx = @bitCast(u64, s);
|
||||
const df = @bitCast(f64, jx & 0xFFFFFFFF00000000);
|
||||
@ -128,9 +128,9 @@ fn asin64(x: f64) -> f64 {
|
||||
}
|
||||
|
||||
if (hx >> 31 != 0) {
|
||||
-fx
|
||||
return -fx;
|
||||
} else {
|
||||
fx
|
||||
return fx;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -9,11 +9,11 @@ const assert = @import("../debug.zig").assert;
|
||||
|
||||
pub fn asinh(x: var) -> @typeOf(x) {
|
||||
const T = @typeOf(x);
|
||||
switch (T) {
|
||||
f32 => @inlineCall(asinh32, x),
|
||||
f64 => @inlineCall(asinh64, x),
|
||||
return switch (T) {
|
||||
f32 => asinh32(x),
|
||||
f64 => asinh64(x),
|
||||
else => @compileError("asinh not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// asinh(x) = sign(x) * log(|x| + sqrt(x * x + 1)) ~= x - x^3/6 + o(x^5)
|
||||
@ -46,7 +46,7 @@ fn asinh32(x: f32) -> f32 {
|
||||
math.forceEval(x + 0x1.0p120);
|
||||
}
|
||||
|
||||
if (s != 0) -rx else rx
|
||||
return if (s != 0) -rx else rx;
|
||||
}
|
||||
|
||||
fn asinh64(x: f64) -> f64 {
|
||||
@ -77,7 +77,7 @@ fn asinh64(x: f64) -> f64 {
|
||||
math.forceEval(x + 0x1.0p120);
|
||||
}
|
||||
|
||||
if (s != 0) -rx else rx
|
||||
return if (s != 0) -rx else rx;
|
||||
}
|
||||
|
||||
test "math.asinh" {
|
||||
|
||||
@ -8,11 +8,11 @@ const assert = @import("../debug.zig").assert;
|
||||
|
||||
pub fn atan(x: var) -> @typeOf(x) {
|
||||
const T = @typeOf(x);
|
||||
switch (T) {
|
||||
f32 => @inlineCall(atan32, x),
|
||||
f64 => @inlineCall(atan64, x),
|
||||
return switch (T) {
|
||||
f32 => atan32(x),
|
||||
f64 => atan64(x),
|
||||
else => @compileError("atan not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn atan32(x_: f32) -> f32 {
|
||||
@ -99,11 +99,11 @@ fn atan32(x_: f32) -> f32 {
|
||||
const s1 = z * (aT[0] + w * (aT[2] + w * aT[4]));
|
||||
const s2 = w * (aT[1] + w * aT[3]);
|
||||
|
||||
if (id == null) {
|
||||
x - x * (s1 + s2)
|
||||
if (id) |id_value| {
|
||||
const zz = atanhi[id_value] - ((x * (s1 + s2) - atanlo[id_value]) - x);
|
||||
return if (sign != 0) -zz else zz;
|
||||
} else {
|
||||
const zz = atanhi[??id] - ((x * (s1 + s2) - atanlo[??id]) - x);
|
||||
if (sign != 0) -zz else zz
|
||||
return x - x * (s1 + s2);
|
||||
}
|
||||
}
|
||||
|
||||
@ -198,16 +198,16 @@ fn atan64(x_: f64) -> f64 {
|
||||
const s1 = z * (aT[0] + w * (aT[2] + w * (aT[4] + w * (aT[6] + w * (aT[8] + w * aT[10])))));
|
||||
const s2 = w * (aT[1] + w * (aT[3] + w * (aT[5] + w * (aT[7] + w * aT[9]))));
|
||||
|
||||
if (id == null) {
|
||||
x - x * (s1 + s2)
|
||||
if (id) |id_value| {
|
||||
const zz = atanhi[id_value] - ((x * (s1 + s2) - atanlo[id_value]) - x);
|
||||
return if (sign != 0) -zz else zz;
|
||||
} else {
|
||||
const zz = atanhi[??id] - ((x * (s1 + s2) - atanlo[??id]) - x);
|
||||
if (sign != 0) -zz else zz
|
||||
return x - x * (s1 + s2);
|
||||
}
|
||||
}
|
||||
|
||||
test "math.atan" {
|
||||
assert(atan(f32(0.2)) == atan32(0.2));
|
||||
assert(@bitCast(u32, atan(f32(0.2))) == @bitCast(u32, atan32(0.2)));
|
||||
assert(atan(f64(0.2)) == atan64(0.2));
|
||||
}
|
||||
|
||||
|
||||
@ -22,11 +22,11 @@ const math = @import("index.zig");
|
||||
const assert = @import("../debug.zig").assert;
|
||||
|
||||
fn atan2(comptime T: type, x: T, y: T) -> T {
|
||||
switch (T) {
|
||||
f32 => @inlineCall(atan2_32, x, y),
|
||||
f64 => @inlineCall(atan2_64, x, y),
|
||||
return switch (T) {
|
||||
f32 => atan2_32(x, y),
|
||||
f64 => atan2_64(x, y),
|
||||
else => @compileError("atan2 not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn atan2_32(y: f32, x: f32) -> f32 {
|
||||
@ -97,11 +97,11 @@ fn atan2_32(y: f32, x: f32) -> f32 {
|
||||
}
|
||||
|
||||
// z = atan(|y / x|) with correct underflow
|
||||
var z = {
|
||||
var z = z: {
|
||||
if ((m & 2) != 0 and iy + (26 << 23) < ix) {
|
||||
0.0
|
||||
break :z 0.0;
|
||||
} else {
|
||||
math.atan(math.fabs(y / x))
|
||||
break :z math.atan(math.fabs(y / x));
|
||||
}
|
||||
};
|
||||
|
||||
@ -187,11 +187,11 @@ fn atan2_64(y: f64, x: f64) -> f64 {
|
||||
}
|
||||
|
||||
// z = atan(|y / x|) with correct underflow
|
||||
var z = {
|
||||
var z = z: {
|
||||
if ((m & 2) != 0 and iy +% (64 << 20) < ix) {
|
||||
0.0
|
||||
break :z 0.0;
|
||||
} else {
|
||||
math.atan(math.fabs(y / x))
|
||||
break :z math.atan(math.fabs(y / x));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -9,11 +9,11 @@ const assert = @import("../debug.zig").assert;
|
||||
|
||||
pub fn atanh(x: var) -> @typeOf(x) {
|
||||
const T = @typeOf(x);
|
||||
switch (T) {
|
||||
f32 => @inlineCall(atanh_32, x),
|
||||
f64 => @inlineCall(atanh_64, x),
|
||||
return switch (T) {
|
||||
f32 => atanh_32(x),
|
||||
f64 => atanh_64(x),
|
||||
else => @compileError("atanh not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// atanh(x) = log((1 + x) / (1 - x)) / 2 = log1p(2x / (1 - x)) / 2 ~= x + x^3 / 3 + o(x^5)
|
||||
@ -32,7 +32,7 @@ fn atanh_32(x: f32) -> f32 {
|
||||
if (u < 0x3F800000 - (32 << 23)) {
|
||||
// underflow
|
||||
if (u < (1 << 23)) {
|
||||
math.forceEval(y * y)
|
||||
math.forceEval(y * y);
|
||||
}
|
||||
}
|
||||
// |x| < 0.5
|
||||
@ -43,7 +43,7 @@ fn atanh_32(x: f32) -> f32 {
|
||||
y = 0.5 * math.log1p(2 * (y / (1 - y)));
|
||||
}
|
||||
|
||||
if (s != 0) -y else y
|
||||
return if (s != 0) -y else y;
|
||||
}
|
||||
|
||||
fn atanh_64(x: f64) -> f64 {
|
||||
@ -72,7 +72,7 @@ fn atanh_64(x: f64) -> f64 {
|
||||
y = 0.5 * math.log1p(2 * (y / (1 - y)));
|
||||
}
|
||||
|
||||
if (s != 0) -y else y
|
||||
return if (s != 0) -y else y;
|
||||
}
|
||||
|
||||
test "math.atanh" {
|
||||
|
||||
@ -9,11 +9,11 @@ const assert = @import("../debug.zig").assert;
|
||||
|
||||
pub fn cbrt(x: var) -> @typeOf(x) {
|
||||
const T = @typeOf(x);
|
||||
switch (T) {
|
||||
f32 => @inlineCall(cbrt32, x),
|
||||
f64 => @inlineCall(cbrt64, x),
|
||||
return switch (T) {
|
||||
f32 => cbrt32(x),
|
||||
f64 => cbrt64(x),
|
||||
else => @compileError("cbrt not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn cbrt32(x: f32) -> f32 {
|
||||
@ -53,7 +53,7 @@ fn cbrt32(x: f32) -> f32 {
|
||||
r = t * t * t;
|
||||
t = t * (f64(x) + x + r) / (x + r + r);
|
||||
|
||||
f32(t)
|
||||
return f32(t);
|
||||
}
|
||||
|
||||
fn cbrt64(x: f64) -> f64 {
|
||||
@ -109,7 +109,7 @@ fn cbrt64(x: f64) -> f64 {
|
||||
var w = t + t;
|
||||
q = (q - t) / (w + q);
|
||||
|
||||
t + t * q
|
||||
return t + t * q;
|
||||
}
|
||||
|
||||
test "math.cbrt" {
|
||||
|
||||
@ -10,11 +10,11 @@ const assert = @import("../debug.zig").assert;
|
||||
|
||||
pub fn ceil(x: var) -> @typeOf(x) {
|
||||
const T = @typeOf(x);
|
||||
switch (T) {
|
||||
f32 => @inlineCall(ceil32, x),
|
||||
f64 => @inlineCall(ceil64, x),
|
||||
return switch (T) {
|
||||
f32 => ceil32(x),
|
||||
f64 => ceil64(x),
|
||||
else => @compileError("ceil not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn ceil32(x: f32) -> f32 {
|
||||
@ -39,13 +39,13 @@ fn ceil32(x: f32) -> f32 {
|
||||
u += m;
|
||||
}
|
||||
u &= ~m;
|
||||
@bitCast(f32, u)
|
||||
return @bitCast(f32, u);
|
||||
} else {
|
||||
math.forceEval(x + 0x1.0p120);
|
||||
if (u >> 31 != 0) {
|
||||
return -0.0;
|
||||
} else {
|
||||
1.0
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -70,14 +70,14 @@ fn ceil64(x: f64) -> f64 {
|
||||
if (e <= 0x3FF-1) {
|
||||
math.forceEval(y);
|
||||
if (u >> 63 != 0) {
|
||||
return -0.0; // Compiler requires return.
|
||||
return -0.0;
|
||||
} else {
|
||||
1.0
|
||||
return 1.0;
|
||||
}
|
||||
} else if (y < 0) {
|
||||
x + y + 1
|
||||
return x + y + 1;
|
||||
} else {
|
||||
x + y
|
||||
return x + y;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2,11 +2,11 @@ const math = @import("index.zig");
|
||||
const assert = @import("../debug.zig").assert;
|
||||
|
||||
pub fn copysign(comptime T: type, x: T, y: T) -> T {
|
||||
switch (T) {
|
||||
f32 => @inlineCall(copysign32, x, y),
|
||||
f64 => @inlineCall(copysign64, x, y),
|
||||
return switch (T) {
|
||||
f32 => copysign32(x, y),
|
||||
f64 => copysign64(x, y),
|
||||
else => @compileError("copysign not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn copysign32(x: f32, y: f32) -> f32 {
|
||||
@ -15,7 +15,7 @@ fn copysign32(x: f32, y: f32) -> f32 {
|
||||
|
||||
const h1 = ux & (@maxValue(u32) / 2);
|
||||
const h2 = uy & (u32(1) << 31);
|
||||
@bitCast(f32, h1 | h2)
|
||||
return @bitCast(f32, h1 | h2);
|
||||
}
|
||||
|
||||
fn copysign64(x: f64, y: f64) -> f64 {
|
||||
@ -24,7 +24,7 @@ fn copysign64(x: f64, y: f64) -> f64 {
|
||||
|
||||
const h1 = ux & (@maxValue(u64) / 2);
|
||||
const h2 = uy & (u64(1) << 63);
|
||||
@bitCast(f64, h1 | h2)
|
||||
return @bitCast(f64, h1 | h2);
|
||||
}
|
||||
|
||||
test "math.copysign" {
|
||||
|
||||
@ -9,11 +9,11 @@ const assert = @import("../debug.zig").assert;
|
||||
|
||||
pub fn cos(x: var) -> @typeOf(x) {
|
||||
const T = @typeOf(x);
|
||||
switch (T) {
|
||||
f32 => @inlineCall(cos32, x),
|
||||
f64 => @inlineCall(cos64, x),
|
||||
return switch (T) {
|
||||
f32 => cos32(x),
|
||||
f64 => cos64(x),
|
||||
else => @compileError("cos not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// sin polynomial coefficients
|
||||
@ -73,18 +73,18 @@ fn cos32(x_: f32) -> f32 {
|
||||
const z = ((x - y * pi4a) - y * pi4b) - y * pi4c;
|
||||
const w = z * z;
|
||||
|
||||
const r = {
|
||||
const r = r: {
|
||||
if (j == 1 or j == 2) {
|
||||
z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0)))))
|
||||
break :r z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0)))));
|
||||
} else {
|
||||
1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0)))))
|
||||
break :r 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0)))));
|
||||
}
|
||||
};
|
||||
|
||||
if (sign) {
|
||||
-r
|
||||
return -r;
|
||||
} else {
|
||||
r
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,18 +124,18 @@ fn cos64(x_: f64) -> f64 {
|
||||
const z = ((x - y * pi4a) - y * pi4b) - y * pi4c;
|
||||
const w = z * z;
|
||||
|
||||
const r = {
|
||||
const r = r: {
|
||||
if (j == 1 or j == 2) {
|
||||
z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0)))))
|
||||
break :r z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0)))));
|
||||
} else {
|
||||
1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0)))))
|
||||
break :r 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0)))));
|
||||
}
|
||||
};
|
||||
|
||||
if (sign) {
|
||||
-r
|
||||
return -r;
|
||||
} else {
|
||||
r
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -11,11 +11,11 @@ const assert = @import("../debug.zig").assert;
|
||||
|
||||
pub fn cosh(x: var) -> @typeOf(x) {
|
||||
const T = @typeOf(x);
|
||||
switch (T) {
|
||||
f32 => @inlineCall(cosh32, x),
|
||||
f64 => @inlineCall(cosh64, x),
|
||||
return switch (T) {
|
||||
f32 => cosh32(x),
|
||||
f64 => cosh64(x),
|
||||
else => @compileError("cosh not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// cosh(x) = (exp(x) + 1 / exp(x)) / 2
|
||||
@ -43,7 +43,7 @@ fn cosh32(x: f32) -> f32 {
|
||||
}
|
||||
|
||||
// |x| > log(FLT_MAX) or nan
|
||||
expo2(ax)
|
||||
return expo2(ax);
|
||||
}
|
||||
|
||||
fn cosh64(x: f64) -> f64 {
|
||||
@ -76,7 +76,7 @@ fn cosh64(x: f64) -> f64 {
|
||||
}
|
||||
|
||||
// |x| > log(CBL_MAX) or nan
|
||||
expo2(ax)
|
||||
return expo2(ax);
|
||||
}
|
||||
|
||||
test "math.cosh" {
|
||||
|
||||
@ -8,11 +8,11 @@ const assert = @import("../debug.zig").assert;
|
||||
|
||||
pub fn exp(x: var) -> @typeOf(x) {
|
||||
const T = @typeOf(x);
|
||||
switch (T) {
|
||||
f32 => @inlineCall(exp32, x),
|
||||
f64 => @inlineCall(exp64, x),
|
||||
return switch (T) {
|
||||
f32 => exp32(x),
|
||||
f64 => exp64(x),
|
||||
else => @compileError("exp not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn exp32(x_: f32) -> f32 {
|
||||
@ -86,9 +86,9 @@ fn exp32(x_: f32) -> f32 {
|
||||
const y = 1 + (x * c / (2 - c) - lo + hi);
|
||||
|
||||
if (k == 0) {
|
||||
y
|
||||
return y;
|
||||
} else {
|
||||
math.scalbn(y, k)
|
||||
return math.scalbn(y, k);
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,9 +172,9 @@ fn exp64(x_: f64) -> f64 {
|
||||
const y = 1 + (x * c / (2 - c) - lo + hi);
|
||||
|
||||
if (k == 0) {
|
||||
y
|
||||
return y;
|
||||
} else {
|
||||
math.scalbn(y, k)
|
||||
return math.scalbn(y, k);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -8,11 +8,11 @@ const assert = @import("../debug.zig").assert;
|
||||
|
||||
pub fn exp2(x: var) -> @typeOf(x) {
|
||||
const T = @typeOf(x);
|
||||
switch (T) {
|
||||
f32 => @inlineCall(exp2_32, x),
|
||||
f64 => @inlineCall(exp2_64, x),
|
||||
return switch (T) {
|
||||
f32 => exp2_32(x),
|
||||
f64 => exp2_64(x),
|
||||
else => @compileError("exp2 not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const exp2ft = []const f64 {
|
||||
@ -88,7 +88,7 @@ fn exp2_32(x: f32) -> f32 {
|
||||
var r: f64 = exp2ft[i0];
|
||||
const t: f64 = r * z;
|
||||
r = r + t * (P1 + z * P2) + t * (z * z) * (P3 + z * P4);
|
||||
f32(r * uk)
|
||||
return f32(r * uk);
|
||||
}
|
||||
|
||||
const exp2dt = []f64 {
|
||||
@ -414,7 +414,7 @@ fn exp2_64(x: f64) -> f64 {
|
||||
z -= exp2dt[2 * i0 + 1];
|
||||
const r = t + t * z * (P1 + z * (P2 + z * (P3 + z * (P4 + z * P5))));
|
||||
|
||||
math.scalbn(r, ik)
|
||||
return math.scalbn(r, ik);
|
||||
}
|
||||
|
||||
test "math.exp2" {
|
||||
|
||||
@ -9,11 +9,11 @@ const assert = @import("../debug.zig").assert;
|
||||
|
||||
pub fn expm1(x: var) -> @typeOf(x) {
|
||||
const T = @typeOf(x);
|
||||
switch (T) {
|
||||
f32 => @inlineCall(expm1_32, x),
|
||||
f64 => @inlineCall(expm1_64, x),
|
||||
return switch (T) {
|
||||
f32 => expm1_32(x),
|
||||
f64 => expm1_64(x),
|
||||
else => @compileError("exp1m not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn expm1_32(x_: f32) -> f32 {
|
||||
|
||||
@ -2,11 +2,11 @@ const math = @import("index.zig");
|
||||
|
||||
pub fn expo2(x: var) -> @typeOf(x) {
|
||||
const T = @typeOf(x);
|
||||
switch (T) {
|
||||
return switch (T) {
|
||||
f32 => expo2f(x),
|
||||
f64 => expo2d(x),
|
||||
else => @compileError("expo2 not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn expo2f(x: f32) -> f32 {
|
||||
@ -15,7 +15,7 @@ fn expo2f(x: f32) -> f32 {
|
||||
|
||||
const u = (0x7F + k / 2) << 23;
|
||||
const scale = @bitCast(f32, u);
|
||||
math.exp(x - kln2) * scale * scale
|
||||
return math.exp(x - kln2) * scale * scale;
|
||||
}
|
||||
|
||||
fn expo2d(x: f64) -> f64 {
|
||||
@ -24,5 +24,5 @@ fn expo2d(x: f64) -> f64 {
|
||||
|
||||
const u = (0x3FF + k / 2) << 20;
|
||||
const scale = @bitCast(f64, u64(u) << 32);
|
||||
math.exp(x - kln2) * scale * scale
|
||||
return math.exp(x - kln2) * scale * scale;
|
||||
}
|
||||
|
||||
@ -8,23 +8,23 @@ const assert = @import("../debug.zig").assert;
|
||||
|
||||
pub fn fabs(x: var) -> @typeOf(x) {
|
||||
const T = @typeOf(x);
|
||||
switch (T) {
|
||||
f32 => @inlineCall(fabs32, x),
|
||||
f64 => @inlineCall(fabs64, x),
|
||||
return switch (T) {
|
||||
f32 => fabs32(x),
|
||||
f64 => fabs64(x),
|
||||
else => @compileError("fabs not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn fabs32(x: f32) -> f32 {
|
||||
var u = @bitCast(u32, x);
|
||||
u &= 0x7FFFFFFF;
|
||||
@bitCast(f32, u)
|
||||
return @bitCast(f32, u);
|
||||
}
|
||||
|
||||
fn fabs64(x: f64) -> f64 {
|
||||
var u = @bitCast(u64, x);
|
||||
u &= @maxValue(u64) >> 1;
|
||||
@bitCast(f64, u)
|
||||
return @bitCast(f64, u);
|
||||
}
|
||||
|
||||
test "math.fabs" {
|
||||
|
||||
@ -10,11 +10,11 @@ const math = @import("index.zig");
|
||||
|
||||
pub fn floor(x: var) -> @typeOf(x) {
|
||||
const T = @typeOf(x);
|
||||
switch (T) {
|
||||
f32 => @inlineCall(floor32, x),
|
||||
f64 => @inlineCall(floor64, x),
|
||||
return switch (T) {
|
||||
f32 => floor32(x),
|
||||
f64 => floor64(x),
|
||||
else => @compileError("floor not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn floor32(x: f32) -> f32 {
|
||||
@ -40,13 +40,13 @@ fn floor32(x: f32) -> f32 {
|
||||
if (u >> 31 != 0) {
|
||||
u += m;
|
||||
}
|
||||
@bitCast(f32, u & ~m)
|
||||
return @bitCast(f32, u & ~m);
|
||||
} else {
|
||||
math.forceEval(x + 0x1.0p120);
|
||||
if (u >> 31 == 0) {
|
||||
return 0.0; // Compiler requires return
|
||||
return 0.0;
|
||||
} else {
|
||||
-1.0
|
||||
return -1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -71,14 +71,14 @@ fn floor64(x: f64) -> f64 {
|
||||
if (e <= 0x3FF-1) {
|
||||
math.forceEval(y);
|
||||
if (u >> 63 != 0) {
|
||||
return -1.0; // Compiler requires return.
|
||||
return -1.0;
|
||||
} else {
|
||||
0.0
|
||||
return 0.0;
|
||||
}
|
||||
} else if (y > 0) {
|
||||
x + y - 1
|
||||
return x + y - 1;
|
||||
} else {
|
||||
x + y
|
||||
return x + y;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2,11 +2,11 @@ const math = @import("index.zig");
|
||||
const assert = @import("../debug.zig").assert;
|
||||
|
||||
pub fn fma(comptime T: type, x: T, y: T, z: T) -> T {
|
||||
switch (T) {
|
||||
f32 => @inlineCall(fma32, x, y, z),
|
||||
f64 => @inlineCall(fma64, x, y ,z),
|
||||
return switch (T) {
|
||||
f32 => fma32(x, y, z),
|
||||
f64 => fma64(x, y ,z),
|
||||
else => @compileError("fma not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn fma32(x: f32, y: f32, z: f32) -> f32 {
|
||||
@ -16,10 +16,10 @@ fn fma32(x: f32, y: f32, z: f32) -> f32 {
|
||||
const e = (u >> 52) & 0x7FF;
|
||||
|
||||
if ((u & 0x1FFFFFFF) != 0x10000000 or e == 0x7FF or xy_z - xy == z) {
|
||||
f32(xy_z)
|
||||
return f32(xy_z);
|
||||
} else {
|
||||
// TODO: Handle inexact case with double-rounding
|
||||
f32(xy_z)
|
||||
return f32(xy_z);
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,9 +64,9 @@ fn fma64(x: f64, y: f64, z: f64) -> f64 {
|
||||
|
||||
const adj = add_adjusted(r.lo, xy.lo);
|
||||
if (spread + math.ilogb(r.hi) > -1023) {
|
||||
math.scalbn(r.hi + adj, spread)
|
||||
return math.scalbn(r.hi + adj, spread);
|
||||
} else {
|
||||
add_and_denorm(r.hi, adj, spread)
|
||||
return add_and_denorm(r.hi, adj, spread);
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,7 +77,7 @@ fn dd_add(a: f64, b: f64) -> dd {
|
||||
ret.hi = a + b;
|
||||
const s = ret.hi - a;
|
||||
ret.lo = (a - (ret.hi - s)) + (b - s);
|
||||
ret
|
||||
return ret;
|
||||
}
|
||||
|
||||
fn dd_mul(a: f64, b: f64) -> dd {
|
||||
@ -99,7 +99,7 @@ fn dd_mul(a: f64, b: f64) -> dd {
|
||||
|
||||
ret.hi = p + q;
|
||||
ret.lo = p - ret.hi + q + la * lb;
|
||||
ret
|
||||
return ret;
|
||||
}
|
||||
|
||||
fn add_adjusted(a: f64, b: f64) -> f64 {
|
||||
@ -113,7 +113,7 @@ fn add_adjusted(a: f64, b: f64) -> f64 {
|
||||
sum.hi = @bitCast(f64, uhii);
|
||||
}
|
||||
}
|
||||
sum.hi
|
||||
return sum.hi;
|
||||
}
|
||||
|
||||
fn add_and_denorm(a: f64, b: f64, scale: i32) -> f64 {
|
||||
@ -127,7 +127,7 @@ fn add_and_denorm(a: f64, b: f64, scale: i32) -> f64 {
|
||||
sum.hi = @bitCast(f64, uhii);
|
||||
}
|
||||
}
|
||||
math.scalbn(sum.hi, scale)
|
||||
return math.scalbn(sum.hi, scale);
|
||||
}
|
||||
|
||||
test "math.fma" {
|
||||
|
||||
@ -8,21 +8,21 @@ const math = @import("index.zig");
|
||||
const assert = @import("../debug.zig").assert;
|
||||
|
||||
fn frexp_result(comptime T: type) -> type {
|
||||
struct {
|
||||
return struct {
|
||||
significand: T,
|
||||
exponent: i32,
|
||||
}
|
||||
};
|
||||
}
|
||||
pub const frexp32_result = frexp_result(f32);
|
||||
pub const frexp64_result = frexp_result(f64);
|
||||
|
||||
pub fn frexp(x: var) -> frexp_result(@typeOf(x)) {
|
||||
const T = @typeOf(x);
|
||||
switch (T) {
|
||||
f32 => @inlineCall(frexp32, x),
|
||||
f64 => @inlineCall(frexp64, x),
|
||||
return switch (T) {
|
||||
f32 => frexp32(x),
|
||||
f64 => frexp64(x),
|
||||
else => @compileError("frexp not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn frexp32(x: f32) -> frexp32_result {
|
||||
@ -59,7 +59,7 @@ fn frexp32(x: f32) -> frexp32_result {
|
||||
y &= 0x807FFFFF;
|
||||
y |= 0x3F000000;
|
||||
result.significand = @bitCast(f32, y);
|
||||
result
|
||||
return result;
|
||||
}
|
||||
|
||||
fn frexp64(x: f64) -> frexp64_result {
|
||||
@ -96,7 +96,7 @@ fn frexp64(x: f64) -> frexp64_result {
|
||||
y &= 0x800FFFFFFFFFFFFF;
|
||||
y |= 0x3FE0000000000000;
|
||||
result.significand = @bitCast(f64, y);
|
||||
result
|
||||
return result;
|
||||
}
|
||||
|
||||
test "math.frexp" {
|
||||
|
||||
@ -9,11 +9,11 @@ const math = @import("index.zig");
|
||||
const assert = @import("../debug.zig").assert;
|
||||
|
||||
pub fn hypot(comptime T: type, x: T, y: T) -> T {
|
||||
switch (T) {
|
||||
f32 => @inlineCall(hypot32, x, y),
|
||||
f64 => @inlineCall(hypot64, x, y),
|
||||
return switch (T) {
|
||||
f32 => hypot32(x, y),
|
||||
f64 => hypot64(x, y),
|
||||
else => @compileError("hypot not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn hypot32(x: f32, y: f32) -> f32 {
|
||||
@ -48,7 +48,7 @@ fn hypot32(x: f32, y: f32) -> f32 {
|
||||
yy *= 0x1.0p-90;
|
||||
}
|
||||
|
||||
z * math.sqrt(f32(f64(x) * x + f64(y) * y))
|
||||
return z * math.sqrt(f32(f64(x) * x + f64(y) * y));
|
||||
}
|
||||
|
||||
fn sq(hi: &f64, lo: &f64, x: f64) {
|
||||
@ -109,7 +109,7 @@ fn hypot64(x: f64, y: f64) -> f64 {
|
||||
sq(&hx, &lx, x);
|
||||
sq(&hy, &ly, y);
|
||||
|
||||
z * math.sqrt(ly + lx + hy + hx)
|
||||
return z * math.sqrt(ly + lx + hy + hx);
|
||||
}
|
||||
|
||||
test "math.hypot" {
|
||||
|
||||
@ -9,11 +9,11 @@ const assert = @import("../debug.zig").assert;
|
||||
|
||||
pub fn ilogb(x: var) -> i32 {
|
||||
const T = @typeOf(x);
|
||||
switch (T) {
|
||||
f32 => @inlineCall(ilogb32, x),
|
||||
f64 => @inlineCall(ilogb64, x),
|
||||
return switch (T) {
|
||||
f32 => ilogb32(x),
|
||||
f64 => ilogb64(x),
|
||||
else => @compileError("ilogb not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// NOTE: Should these be exposed publically?
|
||||
@ -53,7 +53,7 @@ fn ilogb32(x: f32) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
e - 0x7F
|
||||
return e - 0x7F;
|
||||
}
|
||||
|
||||
fn ilogb64(x: f64) -> i32 {
|
||||
@ -88,7 +88,7 @@ fn ilogb64(x: f64) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
e - 0x3FF
|
||||
return e - 0x3FF;
|
||||
}
|
||||
|
||||
test "math.ilogb" {
|
||||
|
||||
@ -36,7 +36,7 @@ pub const inf = @import("inf.zig").inf;
|
||||
|
||||
pub fn approxEq(comptime T: type, x: T, y: T, epsilon: T) -> bool {
|
||||
assert(@typeId(T) == TypeId.Float);
|
||||
fabs(x - y) < epsilon
|
||||
return fabs(x - y) < epsilon;
|
||||
}
|
||||
|
||||
// TODO: Hide the following in an internal module.
|
||||
@ -174,14 +174,8 @@ test "math" {
|
||||
}
|
||||
|
||||
|
||||
pub const Cmp = enum {
|
||||
Less,
|
||||
Equal,
|
||||
Greater,
|
||||
};
|
||||
|
||||
pub fn min(x: var, y: var) -> @typeOf(x + y) {
|
||||
if (x < y) x else y
|
||||
return if (x < y) x else y;
|
||||
}
|
||||
|
||||
test "math.min" {
|
||||
@ -189,7 +183,7 @@ test "math.min" {
|
||||
}
|
||||
|
||||
pub fn max(x: var, y: var) -> @typeOf(x + y) {
|
||||
if (x > y) x else y
|
||||
return if (x > y) x else y;
|
||||
}
|
||||
|
||||
test "math.max" {
|
||||
@ -199,19 +193,19 @@ test "math.max" {
|
||||
error Overflow;
|
||||
pub fn mul(comptime T: type, a: T, b: T) -> %T {
|
||||
var answer: T = undefined;
|
||||
if (@mulWithOverflow(T, a, b, &answer)) error.Overflow else answer
|
||||
return if (@mulWithOverflow(T, a, b, &answer)) error.Overflow else answer;
|
||||
}
|
||||
|
||||
error Overflow;
|
||||
pub fn add(comptime T: type, a: T, b: T) -> %T {
|
||||
var answer: T = undefined;
|
||||
if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer
|
||||
return if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer;
|
||||
}
|
||||
|
||||
error Overflow;
|
||||
pub fn sub(comptime T: type, a: T, b: T) -> %T {
|
||||
var answer: T = undefined;
|
||||
if (@subWithOverflow(T, a, b, &answer)) error.Overflow else answer
|
||||
return if (@subWithOverflow(T, a, b, &answer)) error.Overflow else answer;
|
||||
}
|
||||
|
||||
pub fn negate(x: var) -> %@typeOf(x) {
|
||||
@ -221,7 +215,7 @@ pub fn negate(x: var) -> %@typeOf(x) {
|
||||
error Overflow;
|
||||
pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) -> %T {
|
||||
var answer: T = undefined;
|
||||
if (@shlWithOverflow(T, a, shift_amt, &answer)) error.Overflow else answer
|
||||
return if (@shlWithOverflow(T, a, shift_amt, &answer)) error.Overflow else answer;
|
||||
}
|
||||
|
||||
/// Shifts left. Overflowed bits are truncated.
|
||||
@ -273,7 +267,7 @@ test "math.shr" {
|
||||
}
|
||||
|
||||
pub fn Log2Int(comptime T: type) -> type {
|
||||
@IntType(false, log2(T.bit_count))
|
||||
return @IntType(false, log2(T.bit_count));
|
||||
}
|
||||
|
||||
test "math overflow functions" {
|
||||
@ -522,3 +516,28 @@ pub fn cast(comptime T: type, x: var) -> %T {
|
||||
return T(x);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn floorPowerOfTwo(comptime T: type, value: T) -> T {
|
||||
var x = value;
|
||||
|
||||
comptime var i = 1;
|
||||
inline while(T.bit_count > i) : (i *= 2) {
|
||||
x |= (x >> i);
|
||||
}
|
||||
|
||||
return x - (x >> 1);
|
||||
}
|
||||
|
||||
test "math.floorPowerOfTwo" {
|
||||
testFloorPowerOfTwo();
|
||||
comptime testFloorPowerOfTwo();
|
||||
}
|
||||
|
||||
fn testFloorPowerOfTwo() {
|
||||
assert(floorPowerOfTwo(u32, 63) == 32);
|
||||
assert(floorPowerOfTwo(u32, 64) == 64);
|
||||
assert(floorPowerOfTwo(u32, 65) == 64);
|
||||
assert(floorPowerOfTwo(u4, 7) == 4);
|
||||
assert(floorPowerOfTwo(u4, 8) == 8);
|
||||
assert(floorPowerOfTwo(u4, 9) == 8);
|
||||
}
|
||||
|
||||
@ -2,9 +2,9 @@ const math = @import("index.zig");
|
||||
const assert = @import("../debug.zig").assert;
|
||||
|
||||
pub fn inf(comptime T: type) -> T {
|
||||
switch (T) {
|
||||
return switch (T) {
|
||||
f32 => @bitCast(f32, math.inf_u32),
|
||||
f64 => @bitCast(f64, math.inf_u64),
|
||||
else => @compileError("inf not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -6,11 +6,11 @@ pub fn isFinite(x: var) -> bool {
|
||||
switch (T) {
|
||||
f32 => {
|
||||
const bits = @bitCast(u32, x);
|
||||
bits & 0x7FFFFFFF < 0x7F800000
|
||||
return bits & 0x7FFFFFFF < 0x7F800000;
|
||||
},
|
||||
f64 => {
|
||||
const bits = @bitCast(u64, x);
|
||||
bits & (@maxValue(u64) >> 1) < (0x7FF << 52)
|
||||
return bits & (@maxValue(u64) >> 1) < (0x7FF << 52);
|
||||
},
|
||||
else => {
|
||||
@compileError("isFinite not implemented for " ++ @typeName(T));
|
||||
|
||||
@ -6,11 +6,11 @@ pub fn isInf(x: var) -> bool {
|
||||
switch (T) {
|
||||
f32 => {
|
||||
const bits = @bitCast(u32, x);
|
||||
bits & 0x7FFFFFFF == 0x7F800000
|
||||
return bits & 0x7FFFFFFF == 0x7F800000;
|
||||
},
|
||||
f64 => {
|
||||
const bits = @bitCast(u64, x);
|
||||
bits & (@maxValue(u64) >> 1) == (0x7FF << 52)
|
||||
return bits & (@maxValue(u64) >> 1) == (0x7FF << 52);
|
||||
},
|
||||
else => {
|
||||
@compileError("isInf not implemented for " ++ @typeName(T));
|
||||
@ -22,10 +22,10 @@ pub fn isPositiveInf(x: var) -> bool {
|
||||
const T = @typeOf(x);
|
||||
switch (T) {
|
||||
f32 => {
|
||||
@bitCast(u32, x) == 0x7F800000
|
||||
return @bitCast(u32, x) == 0x7F800000;
|
||||
},
|
||||
f64 => {
|
||||
@bitCast(u64, x) == 0x7FF << 52
|
||||
return @bitCast(u64, x) == 0x7FF << 52;
|
||||
},
|
||||
else => {
|
||||
@compileError("isPositiveInf not implemented for " ++ @typeName(T));
|
||||
@ -37,10 +37,10 @@ pub fn isNegativeInf(x: var) -> bool {
|
||||
const T = @typeOf(x);
|
||||
switch (T) {
|
||||
f32 => {
|
||||
@bitCast(u32, x) == 0xFF800000
|
||||
return @bitCast(u32, x) == 0xFF800000;
|
||||
},
|
||||
f64 => {
|
||||
@bitCast(u64, x) == 0xFFF << 52
|
||||
return @bitCast(u64, x) == 0xFFF << 52;
|
||||
},
|
||||
else => {
|
||||
@compileError("isNegativeInf not implemented for " ++ @typeName(T));
|
||||
|
||||
@ -6,11 +6,11 @@ pub fn isNan(x: var) -> bool {
|
||||
switch (T) {
|
||||
f32 => {
|
||||
const bits = @bitCast(u32, x);
|
||||
bits & 0x7FFFFFFF > 0x7F800000
|
||||
return bits & 0x7FFFFFFF > 0x7F800000;
|
||||
},
|
||||
f64 => {
|
||||
const bits = @bitCast(u64, x);
|
||||
(bits & (@maxValue(u64) >> 1)) > (u64(0x7FF) << 52)
|
||||
return (bits & (@maxValue(u64) >> 1)) > (u64(0x7FF) << 52);
|
||||
},
|
||||
else => {
|
||||
@compileError("isNan not implemented for " ++ @typeName(T));
|
||||
@ -21,7 +21,7 @@ pub fn isNan(x: var) -> bool {
|
||||
// Note: A signalling nan is identical to a standard right now by may have a different bit
|
||||
// representation in the future when required.
|
||||
pub fn isSignalNan(x: var) -> bool {
|
||||
isNan(x)
|
||||
return isNan(x);
|
||||
}
|
||||
|
||||
test "math.isNan" {
|
||||
|
||||
@ -6,11 +6,11 @@ pub fn isNormal(x: var) -> bool {
|
||||
switch (T) {
|
||||
f32 => {
|
||||
const bits = @bitCast(u32, x);
|
||||
(bits + 0x00800000) & 0x7FFFFFFF >= 0x01000000
|
||||
return (bits + 0x00800000) & 0x7FFFFFFF >= 0x01000000;
|
||||
},
|
||||
f64 => {
|
||||
const bits = @bitCast(u64, x);
|
||||
(bits + (1 << 52)) & (@maxValue(u64) >> 1) >= (1 << 53)
|
||||
return (bits + (1 << 52)) & (@maxValue(u64) >> 1) >= (1 << 53);
|
||||
},
|
||||
else => {
|
||||
@compileError("isNormal not implemented for " ++ @typeName(T));
|
||||
|
||||
@ -14,7 +14,7 @@ pub fn ln(x: var) -> @typeOf(x) {
|
||||
const T = @typeOf(x);
|
||||
switch (@typeId(T)) {
|
||||
TypeId.FloatLiteral => {
|
||||
return @typeOf(1.0)(ln_64(x))
|
||||
return @typeOf(1.0)(ln_64(x));
|
||||
},
|
||||
TypeId.Float => {
|
||||
return switch (T) {
|
||||
@ -84,7 +84,7 @@ pub fn ln_32(x_: f32) -> f32 {
|
||||
const hfsq = 0.5 * f * f;
|
||||
const dk = f32(k);
|
||||
|
||||
s * (hfsq + R) + dk * ln2_lo - hfsq + f + dk * ln2_hi
|
||||
return s * (hfsq + R) + dk * ln2_lo - hfsq + f + dk * ln2_hi;
|
||||
}
|
||||
|
||||
pub fn ln_64(x_: f64) -> f64 {
|
||||
@ -116,7 +116,7 @@ pub fn ln_64(x_: f64) -> f64 {
|
||||
// subnormal, scale x
|
||||
k -= 54;
|
||||
x *= 0x1.0p54;
|
||||
hx = u32(@bitCast(u64, ix) >> 32)
|
||||
hx = u32(@bitCast(u64, ix) >> 32);
|
||||
}
|
||||
else if (hx >= 0x7FF00000) {
|
||||
return x;
|
||||
@ -142,7 +142,7 @@ pub fn ln_64(x_: f64) -> f64 {
|
||||
const R = t2 + t1;
|
||||
const dk = f64(k);
|
||||
|
||||
s * (hfsq + R) + dk * ln2_lo - hfsq + f + dk * ln2_hi
|
||||
return s * (hfsq + R) + dk * ln2_lo - hfsq + f + dk * ln2_hi;
|
||||
}
|
||||
|
||||
test "math.ln" {
|
||||
|
||||
@ -29,7 +29,7 @@ pub fn log(comptime T: type, base: T, x: T) -> T {
|
||||
f32 => return f32(math.ln(f64(x)) / math.ln(f64(base))),
|
||||
f64 => return math.ln(x) / math.ln(f64(base)),
|
||||
else => @compileError("log not implemented for " ++ @typeName(T)),
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
else => {
|
||||
|
||||
@ -14,7 +14,7 @@ pub fn log10(x: var) -> @typeOf(x) {
|
||||
const T = @typeOf(x);
|
||||
switch (@typeId(T)) {
|
||||
TypeId.FloatLiteral => {
|
||||
return @typeOf(1.0)(log10_64(x))
|
||||
return @typeOf(1.0)(log10_64(x));
|
||||
},
|
||||
TypeId.Float => {
|
||||
return switch (T) {
|
||||
@ -90,7 +90,7 @@ pub fn log10_32(x_: f32) -> f32 {
|
||||
const lo = f - hi - hfsq + s * (hfsq + R);
|
||||
const dk = f32(k);
|
||||
|
||||
dk * log10_2lo + (lo + hi) * ivln10lo + lo * ivln10hi + hi * ivln10hi + dk * log10_2hi
|
||||
return dk * log10_2lo + (lo + hi) * ivln10lo + lo * ivln10hi + hi * ivln10hi + dk * log10_2hi;
|
||||
}
|
||||
|
||||
pub fn log10_64(x_: f64) -> f64 {
|
||||
@ -124,7 +124,7 @@ pub fn log10_64(x_: f64) -> f64 {
|
||||
// subnormal, scale x
|
||||
k -= 54;
|
||||
x *= 0x1.0p54;
|
||||
hx = u32(@bitCast(u64, x) >> 32)
|
||||
hx = u32(@bitCast(u64, x) >> 32);
|
||||
}
|
||||
else if (hx >= 0x7FF00000) {
|
||||
return x;
|
||||
@ -167,7 +167,7 @@ pub fn log10_64(x_: f64) -> f64 {
|
||||
val_lo += (y - ww) + val_hi;
|
||||
val_hi = ww;
|
||||
|
||||
val_lo + val_hi
|
||||
return val_lo + val_hi;
|
||||
}
|
||||
|
||||
test "math.log10" {
|
||||
|
||||
@ -11,11 +11,11 @@ const assert = @import("../debug.zig").assert;
|
||||
|
||||
pub fn log1p(x: var) -> @typeOf(x) {
|
||||
const T = @typeOf(x);
|
||||
switch (T) {
|
||||
f32 => @inlineCall(log1p_32, x),
|
||||
f64 => @inlineCall(log1p_64, x),
|
||||
return switch (T) {
|
||||
f32 => log1p_32(x),
|
||||
f64 => log1p_64(x),
|
||||
else => @compileError("log1p not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn log1p_32(x: f32) -> f32 {
|
||||
@ -91,7 +91,7 @@ fn log1p_32(x: f32) -> f32 {
|
||||
const hfsq = 0.5 * f * f;
|
||||
const dk = f32(k);
|
||||
|
||||
s * (hfsq + R) + (dk * ln2_lo + c) - hfsq + f + dk * ln2_hi
|
||||
return s * (hfsq + R) + (dk * ln2_lo + c) - hfsq + f + dk * ln2_hi;
|
||||
}
|
||||
|
||||
fn log1p_64(x: f64) -> f64 {
|
||||
@ -172,7 +172,7 @@ fn log1p_64(x: f64) -> f64 {
|
||||
const R = t2 + t1;
|
||||
const dk = f64(k);
|
||||
|
||||
s * (hfsq + R) + (dk * ln2_lo + c) - hfsq + f + dk * ln2_hi
|
||||
return s * (hfsq + R) + (dk * ln2_lo + c) - hfsq + f + dk * ln2_hi;
|
||||
}
|
||||
|
||||
test "math.log1p" {
|
||||
|
||||
@ -14,7 +14,7 @@ pub fn log2(x: var) -> @typeOf(x) {
|
||||
const T = @typeOf(x);
|
||||
switch (@typeId(T)) {
|
||||
TypeId.FloatLiteral => {
|
||||
return @typeOf(1.0)(log2_64(x))
|
||||
return @typeOf(1.0)(log2_64(x));
|
||||
},
|
||||
TypeId.Float => {
|
||||
return switch (T) {
|
||||
@ -26,7 +26,7 @@ pub fn log2(x: var) -> @typeOf(x) {
|
||||
TypeId.IntLiteral => comptime {
|
||||
var result = 0;
|
||||
var x_shifted = x;
|
||||
while ({x_shifted >>= 1; x_shifted != 0}) : (result += 1) {}
|
||||
while (b: {x_shifted >>= 1; break :b x_shifted != 0;}) : (result += 1) {}
|
||||
return result;
|
||||
},
|
||||
TypeId.Int => {
|
||||
@ -94,7 +94,7 @@ pub fn log2_32(x_: f32) -> f32 {
|
||||
u &= 0xFFFFF000;
|
||||
hi = @bitCast(f32, u);
|
||||
const lo = f - hi - hfsq + s * (hfsq + R);
|
||||
(lo + hi) * ivln2lo + lo * ivln2hi + hi * ivln2hi + f32(k)
|
||||
return (lo + hi) * ivln2lo + lo * ivln2hi + hi * ivln2hi + f32(k);
|
||||
}
|
||||
|
||||
pub fn log2_64(x_: f64) -> f64 {
|
||||
@ -165,7 +165,7 @@ pub fn log2_64(x_: f64) -> f64 {
|
||||
val_lo += (y - ww) + val_hi;
|
||||
val_hi = ww;
|
||||
|
||||
val_lo + val_hi
|
||||
return val_lo + val_hi;
|
||||
}
|
||||
|
||||
test "math.log2" {
|
||||
|
||||
@ -7,21 +7,21 @@ const math = @import("index.zig");
|
||||
const assert = @import("../debug.zig").assert;
|
||||
|
||||
fn modf_result(comptime T: type) -> type {
|
||||
struct {
|
||||
return struct {
|
||||
fpart: T,
|
||||
ipart: T,
|
||||
}
|
||||
};
|
||||
}
|
||||
pub const modf32_result = modf_result(f32);
|
||||
pub const modf64_result = modf_result(f64);
|
||||
|
||||
pub fn modf(x: var) -> modf_result(@typeOf(x)) {
|
||||
const T = @typeOf(x);
|
||||
switch (T) {
|
||||
f32 => @inlineCall(modf32, x),
|
||||
f64 => @inlineCall(modf64, x),
|
||||
return switch (T) {
|
||||
f32 => modf32(x),
|
||||
f64 => modf64(x),
|
||||
else => @compileError("modf not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn modf32(x: f32) -> modf32_result {
|
||||
@ -66,7 +66,7 @@ fn modf32(x: f32) -> modf32_result {
|
||||
const uf = @bitCast(f32, u & ~mask);
|
||||
result.ipart = uf;
|
||||
result.fpart = x - uf;
|
||||
result
|
||||
return result;
|
||||
}
|
||||
|
||||
fn modf64(x: f64) -> modf64_result {
|
||||
@ -110,7 +110,7 @@ fn modf64(x: f64) -> modf64_result {
|
||||
const uf = @bitCast(f64, u & ~mask);
|
||||
result.ipart = uf;
|
||||
result.fpart = x - uf;
|
||||
result
|
||||
return result;
|
||||
}
|
||||
|
||||
test "math.modf" {
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
const math = @import("index.zig");
|
||||
|
||||
pub fn nan(comptime T: type) -> T {
|
||||
switch (T) {
|
||||
return switch (T) {
|
||||
f32 => @bitCast(f32, math.nan_u32),
|
||||
f64 => @bitCast(f64, math.nan_u64),
|
||||
else => @compileError("nan not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Note: A signalling nan is identical to a standard right now by may have a different bit
|
||||
// representation in the future when required.
|
||||
pub fn snan(comptime T: type) -> T {
|
||||
switch (T) {
|
||||
return switch (T) {
|
||||
f32 => @bitCast(f32, math.nan_u32),
|
||||
f64 => @bitCast(f64, math.nan_u64),
|
||||
else => @compileError("snan not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -166,12 +166,12 @@ pub fn pow(comptime T: type, x: T, y: T) -> T {
|
||||
ae = -ae;
|
||||
}
|
||||
|
||||
math.scalbn(a1, ae)
|
||||
return math.scalbn(a1, ae);
|
||||
}
|
||||
|
||||
fn isOddInteger(x: f64) -> bool {
|
||||
const r = math.modf(x);
|
||||
r.fpart == 0.0 and i64(r.ipart) & 1 == 1
|
||||
return r.fpart == 0.0 and i64(r.ipart) & 1 == 1;
|
||||
}
|
||||
|
||||
test "math.pow" {
|
||||
|
||||
@ -10,11 +10,11 @@ const math = @import("index.zig");
|
||||
|
||||
pub fn round(x: var) -> @typeOf(x) {
|
||||
const T = @typeOf(x);
|
||||
switch (T) {
|
||||
f32 => @inlineCall(round32, x),
|
||||
f64 => @inlineCall(round64, x),
|
||||
return switch (T) {
|
||||
f32 => round32(x),
|
||||
f64 => round64(x),
|
||||
else => @compileError("round not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn round32(x_: f32) -> f32 {
|
||||
@ -48,9 +48,9 @@ fn round32(x_: f32) -> f32 {
|
||||
}
|
||||
|
||||
if (u >> 31 != 0) {
|
||||
-y
|
||||
return -y;
|
||||
} else {
|
||||
y
|
||||
return y;
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,9 +85,9 @@ fn round64(x_: f64) -> f64 {
|
||||
}
|
||||
|
||||
if (u >> 63 != 0) {
|
||||
-y
|
||||
return -y;
|
||||
} else {
|
||||
y
|
||||
return y;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3,11 +3,11 @@ const assert = @import("../debug.zig").assert;
|
||||
|
||||
pub fn scalbn(x: var, n: i32) -> @typeOf(x) {
|
||||
const T = @typeOf(x);
|
||||
switch (T) {
|
||||
f32 => @inlineCall(scalbn32, x, n),
|
||||
f64 => @inlineCall(scalbn64, x, n),
|
||||
return switch (T) {
|
||||
f32 => scalbn32(x, n),
|
||||
f64 => scalbn64(x, n),
|
||||
else => @compileError("scalbn not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn scalbn32(x: f32, n_: i32) -> f32 {
|
||||
@ -37,7 +37,7 @@ fn scalbn32(x: f32, n_: i32) -> f32 {
|
||||
}
|
||||
|
||||
const u = u32(n +% 0x7F) << 23;
|
||||
y * @bitCast(f32, u)
|
||||
return y * @bitCast(f32, u);
|
||||
}
|
||||
|
||||
fn scalbn64(x: f64, n_: i32) -> f64 {
|
||||
@ -67,7 +67,7 @@ fn scalbn64(x: f64, n_: i32) -> f64 {
|
||||
}
|
||||
|
||||
const u = u64(n +% 0x3FF) << 52;
|
||||
y * @bitCast(f64, u)
|
||||
return y * @bitCast(f64, u);
|
||||
}
|
||||
|
||||
test "math.scalbn" {
|
||||
|
||||
@ -3,21 +3,21 @@ const assert = @import("../debug.zig").assert;
|
||||
|
||||
pub fn signbit(x: var) -> bool {
|
||||
const T = @typeOf(x);
|
||||
switch (T) {
|
||||
f32 => @inlineCall(signbit32, x),
|
||||
f64 => @inlineCall(signbit64, x),
|
||||
return switch (T) {
|
||||
f32 => signbit32(x),
|
||||
f64 => signbit64(x),
|
||||
else => @compileError("signbit not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn signbit32(x: f32) -> bool {
|
||||
const bits = @bitCast(u32, x);
|
||||
bits >> 31 != 0
|
||||
return bits >> 31 != 0;
|
||||
}
|
||||
|
||||
fn signbit64(x: f64) -> bool {
|
||||
const bits = @bitCast(u64, x);
|
||||
bits >> 63 != 0
|
||||
return bits >> 63 != 0;
|
||||
}
|
||||
|
||||
test "math.signbit" {
|
||||
|
||||
@ -10,11 +10,11 @@ const assert = @import("../debug.zig").assert;
|
||||
|
||||
pub fn sin(x: var) -> @typeOf(x) {
|
||||
const T = @typeOf(x);
|
||||
switch (T) {
|
||||
f32 => @inlineCall(sin32, x),
|
||||
f64 => @inlineCall(sin64, x),
|
||||
return switch (T) {
|
||||
f32 => sin32(x),
|
||||
f64 => sin64(x),
|
||||
else => @compileError("sin not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// sin polynomial coefficients
|
||||
@ -75,18 +75,18 @@ fn sin32(x_: f32) -> f32 {
|
||||
const z = ((x - y * pi4a) - y * pi4b) - y * pi4c;
|
||||
const w = z * z;
|
||||
|
||||
const r = {
|
||||
const r = r: {
|
||||
if (j == 1 or j == 2) {
|
||||
1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0)))))
|
||||
break :r 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0)))));
|
||||
} else {
|
||||
z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0)))))
|
||||
break :r z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0)))));
|
||||
}
|
||||
};
|
||||
|
||||
if (sign) {
|
||||
-r
|
||||
return -r;
|
||||
} else {
|
||||
r
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,25 +127,25 @@ fn sin64(x_: f64) -> f64 {
|
||||
const z = ((x - y * pi4a) - y * pi4b) - y * pi4c;
|
||||
const w = z * z;
|
||||
|
||||
const r = {
|
||||
const r = r: {
|
||||
if (j == 1 or j == 2) {
|
||||
1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0)))))
|
||||
break :r 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0)))));
|
||||
} else {
|
||||
z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0)))))
|
||||
break :r z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0)))));
|
||||
}
|
||||
};
|
||||
|
||||
if (sign) {
|
||||
-r
|
||||
return -r;
|
||||
} else {
|
||||
r
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
test "math.sin" {
|
||||
assert(sin(f32(0.0)) == sin32(0.0));
|
||||
assert(sin(f64(0.0)) == sin64(0.0));
|
||||
assert(comptime {math.sin(f64(2))} == math.sin(f64(2)));
|
||||
assert(comptime (math.sin(f64(2))) == math.sin(f64(2)));
|
||||
}
|
||||
|
||||
test "math.sin32" {
|
||||
|
||||
@ -11,11 +11,11 @@ const expo2 = @import("expo2.zig").expo2;
|
||||
|
||||
pub fn sinh(x: var) -> @typeOf(x) {
|
||||
const T = @typeOf(x);
|
||||
switch (T) {
|
||||
f32 => @inlineCall(sinh32, x),
|
||||
f64 => @inlineCall(sinh64, x),
|
||||
return switch (T) {
|
||||
f32 => sinh32(x),
|
||||
f64 => sinh64(x),
|
||||
else => @compileError("sinh not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// sinh(x) = (exp(x) - 1 / exp(x)) / 2
|
||||
@ -49,7 +49,7 @@ fn sinh32(x: f32) -> f32 {
|
||||
}
|
||||
|
||||
// |x| > log(FLT_MAX) or nan
|
||||
2 * h * expo2(ax)
|
||||
return 2 * h * expo2(ax);
|
||||
}
|
||||
|
||||
fn sinh64(x: f64) -> f64 {
|
||||
@ -83,7 +83,7 @@ fn sinh64(x: f64) -> f64 {
|
||||
}
|
||||
|
||||
// |x| > log(DBL_MAX) or nan
|
||||
2 * h * expo2(ax)
|
||||
return 2 * h * expo2(ax);
|
||||
}
|
||||
|
||||
test "math.sinh" {
|
||||
|
||||
@ -7,12 +7,34 @@
|
||||
|
||||
const math = @import("index.zig");
|
||||
const assert = @import("../debug.zig").assert;
|
||||
const builtin = @import("builtin");
|
||||
const TypeId = builtin.TypeId;
|
||||
|
||||
pub fn sqrt(x: var) -> @typeOf(x) {
|
||||
pub fn sqrt(x: var) -> (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typeOf(x).bit_count / 2) else @typeOf(x)) {
|
||||
const T = @typeOf(x);
|
||||
switch (T) {
|
||||
f32 => @inlineCall(sqrt32, x),
|
||||
f64 => @inlineCall(sqrt64, x),
|
||||
switch (@typeId(T)) {
|
||||
TypeId.FloatLiteral => {
|
||||
return T(sqrt64(x));
|
||||
},
|
||||
TypeId.Float => {
|
||||
return switch (T) {
|
||||
f32 => sqrt32(x),
|
||||
f64 => sqrt64(x),
|
||||
else => @compileError("sqrt not implemented for " ++ @typeName(T)),
|
||||
};
|
||||
},
|
||||
TypeId.IntLiteral => comptime {
|
||||
if (x > @maxValue(u128)) {
|
||||
@compileError("sqrt not implemented for comptime_int greater than 128 bits");
|
||||
}
|
||||
if (x < 0) {
|
||||
@compileError("sqrt on negative number");
|
||||
}
|
||||
return T(sqrt_int(u128, x));
|
||||
},
|
||||
TypeId.Int => {
|
||||
return sqrt_int(T, x);
|
||||
},
|
||||
else => @compileError("sqrt not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
}
|
||||
@ -42,7 +64,7 @@ fn sqrt32(x: f32) -> f32 {
|
||||
// subnormal
|
||||
var i: i32 = 0;
|
||||
while (ix & 0x00800000 == 0) : (i += 1) {
|
||||
ix <<= 1
|
||||
ix <<= 1;
|
||||
}
|
||||
m -= i - 1;
|
||||
}
|
||||
@ -90,7 +112,7 @@ fn sqrt32(x: f32) -> f32 {
|
||||
|
||||
ix = (q >> 1) + 0x3f000000;
|
||||
ix += m << 23;
|
||||
@bitCast(f32, ix)
|
||||
return @bitCast(f32, ix);
|
||||
}
|
||||
|
||||
// NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound
|
||||
@ -131,7 +153,7 @@ fn sqrt64(x: f64) -> f64 {
|
||||
// subnormal
|
||||
var i: u32 = 0;
|
||||
while (ix0 & 0x00100000 == 0) : (i += 1) {
|
||||
ix0 <<= 1
|
||||
ix0 <<= 1;
|
||||
}
|
||||
m -= i32(i) - 1;
|
||||
ix0 |= ix1 >> u5(32 - i);
|
||||
@ -223,7 +245,7 @@ fn sqrt64(x: f64) -> f64 {
|
||||
iix0 = iix0 +% (m << 20);
|
||||
|
||||
const uz = (u64(iix0) << 32) | ix1;
|
||||
@bitCast(f64, uz)
|
||||
return @bitCast(f64, uz);
|
||||
}
|
||||
|
||||
test "math.sqrt" {
|
||||
@ -274,3 +296,35 @@ test "math.sqrt64.special" {
|
||||
assert(math.isNan(sqrt64(-1.0)));
|
||||
assert(math.isNan(sqrt64(math.nan(f64))));
|
||||
}
|
||||
|
||||
fn sqrt_int(comptime T: type, value: T) -> @IntType(false, T.bit_count / 2) {
|
||||
var op = value;
|
||||
var res: T = 0;
|
||||
var one: T = 1 << (T.bit_count - 2);
|
||||
|
||||
// "one" starts at the highest power of four <= than the argument.
|
||||
while (one > op) {
|
||||
one >>= 2;
|
||||
}
|
||||
|
||||
while (one != 0) {
|
||||
if (op >= res + one) {
|
||||
op -= res + one;
|
||||
res += 2 * one;
|
||||
}
|
||||
res >>= 1;
|
||||
one >>= 2;
|
||||
}
|
||||
|
||||
const ResultType = @IntType(false, T.bit_count / 2);
|
||||
return ResultType(res);
|
||||
}
|
||||
|
||||
test "math.sqrt_int" {
|
||||
assert(sqrt_int(u32, 3) == 1);
|
||||
assert(sqrt_int(u32, 4) == 2);
|
||||
assert(sqrt_int(u32, 5) == 2);
|
||||
assert(sqrt_int(u32, 8) == 2);
|
||||
assert(sqrt_int(u32, 9) == 3);
|
||||
assert(sqrt_int(u32, 10) == 3);
|
||||
}
|
||||
|
||||
@ -10,11 +10,11 @@ const assert = @import("../debug.zig").assert;
|
||||
|
||||
pub fn tan(x: var) -> @typeOf(x) {
|
||||
const T = @typeOf(x);
|
||||
switch (T) {
|
||||
f32 => @inlineCall(tan32, x),
|
||||
f64 => @inlineCall(tan64, x),
|
||||
return switch (T) {
|
||||
f32 => tan32(x),
|
||||
f64 => tan64(x),
|
||||
else => @compileError("tan not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const Tp0 = -1.30936939181383777646E4;
|
||||
@ -62,11 +62,11 @@ fn tan32(x_: f32) -> f32 {
|
||||
const z = ((x - y * pi4a) - y * pi4b) - y * pi4c;
|
||||
const w = z * z;
|
||||
|
||||
var r = {
|
||||
var r = r: {
|
||||
if (w > 1e-14) {
|
||||
z + z * (w * ((Tp0 * w + Tp1) * w + Tp2) / ((((w + Tq1) * w + Tq2) * w + Tq3) * w + Tq4))
|
||||
break :r z + z * (w * ((Tp0 * w + Tp1) * w + Tp2) / ((((w + Tq1) * w + Tq2) * w + Tq3) * w + Tq4));
|
||||
} else {
|
||||
z
|
||||
break :r z;
|
||||
}
|
||||
};
|
||||
|
||||
@ -77,7 +77,7 @@ fn tan32(x_: f32) -> f32 {
|
||||
r = -r;
|
||||
}
|
||||
|
||||
r
|
||||
return r;
|
||||
}
|
||||
|
||||
fn tan64(x_: f64) -> f64 {
|
||||
@ -111,11 +111,11 @@ fn tan64(x_: f64) -> f64 {
|
||||
const z = ((x - y * pi4a) - y * pi4b) - y * pi4c;
|
||||
const w = z * z;
|
||||
|
||||
var r = {
|
||||
var r = r: {
|
||||
if (w > 1e-14) {
|
||||
z + z * (w * ((Tp0 * w + Tp1) * w + Tp2) / ((((w + Tq1) * w + Tq2) * w + Tq3) * w + Tq4))
|
||||
break :r z + z * (w * ((Tp0 * w + Tp1) * w + Tp2) / ((((w + Tq1) * w + Tq2) * w + Tq3) * w + Tq4));
|
||||
} else {
|
||||
z
|
||||
break :r z;
|
||||
}
|
||||
};
|
||||
|
||||
@ -126,7 +126,7 @@ fn tan64(x_: f64) -> f64 {
|
||||
r = -r;
|
||||
}
|
||||
|
||||
r
|
||||
return r;
|
||||
}
|
||||
|
||||
test "math.tan" {
|
||||
|
||||
@ -11,11 +11,11 @@ const expo2 = @import("expo2.zig").expo2;
|
||||
|
||||
pub fn tanh(x: var) -> @typeOf(x) {
|
||||
const T = @typeOf(x);
|
||||
switch (T) {
|
||||
f32 => @inlineCall(tanh32, x),
|
||||
f64 => @inlineCall(tanh64, x),
|
||||
return switch (T) {
|
||||
f32 => tanh32(x),
|
||||
f64 => tanh64(x),
|
||||
else => @compileError("tanh not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// tanh(x) = (exp(x) - exp(-x)) / (exp(x) + exp(-x))
|
||||
@ -59,9 +59,9 @@ fn tanh32(x: f32) -> f32 {
|
||||
}
|
||||
|
||||
if (u >> 31 != 0) {
|
||||
-t
|
||||
return -t;
|
||||
} else {
|
||||
t
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,9 +104,9 @@ fn tanh64(x: f64) -> f64 {
|
||||
}
|
||||
|
||||
if (u >> 63 != 0) {
|
||||
-t
|
||||
return -t;
|
||||
} else {
|
||||
t
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -9,11 +9,11 @@ const assert = @import("../debug.zig").assert;
|
||||
|
||||
pub fn trunc(x: var) -> @typeOf(x) {
|
||||
const T = @typeOf(x);
|
||||
switch (T) {
|
||||
f32 => @inlineCall(trunc32, x),
|
||||
f64 => @inlineCall(trunc64, x),
|
||||
return switch (T) {
|
||||
f32 => trunc32(x),
|
||||
f64 => trunc64(x),
|
||||
else => @compileError("trunc not implemented for " ++ @typeName(T)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn trunc32(x: f32) -> f32 {
|
||||
@ -30,10 +30,10 @@ fn trunc32(x: f32) -> f32 {
|
||||
|
||||
m = u32(@maxValue(u32)) >> u5(e);
|
||||
if (u & m == 0) {
|
||||
x
|
||||
return x;
|
||||
} else {
|
||||
math.forceEval(x + 0x1p120);
|
||||
@bitCast(f32, u & ~m)
|
||||
return @bitCast(f32, u & ~m);
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,10 +51,10 @@ fn trunc64(x: f64) -> f64 {
|
||||
|
||||
m = u64(@maxValue(u64)) >> u6(e);
|
||||
if (u & m == 0) {
|
||||
x
|
||||
return x;
|
||||
} else {
|
||||
math.forceEval(x + 0x1p120);
|
||||
@bitCast(f64, u & ~m)
|
||||
return @bitCast(f64, u & ~m);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
185
std/mem.zig
185
std/mem.zig
@ -3,26 +3,31 @@ const assert = debug.assert;
|
||||
const math = @import("math/index.zig");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
pub const Cmp = math.Cmp;
|
||||
error OutOfMemory;
|
||||
|
||||
pub const Allocator = struct {
|
||||
/// Allocate byte_count bytes and return them in a slice, with the
|
||||
/// slicer's pointer aligned at least to alignment bytes.
|
||||
allocFn: fn (self: &Allocator, byte_count: usize, alignment: usize) -> %[]u8,
|
||||
/// slice's pointer aligned at least to alignment bytes.
|
||||
/// The returned newly allocated memory is undefined.
|
||||
allocFn: fn (self: &Allocator, byte_count: usize, alignment: u29) -> %[]u8,
|
||||
|
||||
/// Guaranteed: `old_mem.len` is the same as what was returned from allocFn or reallocFn.
|
||||
/// Guaranteed: alignment >= alignment of old_mem.ptr
|
||||
/// If `new_byte_count > old_mem.len`:
|
||||
/// * `old_mem.len` is the same as what was returned from allocFn or reallocFn.
|
||||
/// * alignment >= alignment of old_mem.ptr
|
||||
///
|
||||
/// If `new_byte_count` is less than or equal to `old_mem.len` this function must
|
||||
/// return successfully.
|
||||
reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: usize) -> %[]u8,
|
||||
/// If `new_byte_count <= old_mem.len`:
|
||||
/// * this function must return successfully.
|
||||
/// * alignment <= alignment of old_mem.ptr
|
||||
///
|
||||
/// The returned newly allocated memory is undefined.
|
||||
reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) -> %[]u8,
|
||||
|
||||
/// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn`
|
||||
freeFn: fn (self: &Allocator, old_mem: []u8),
|
||||
|
||||
fn create(self: &Allocator, comptime T: type) -> %&T {
|
||||
const slice = %return self.alloc(T, 1);
|
||||
&slice[0]
|
||||
return &slice[0];
|
||||
}
|
||||
|
||||
fn destroy(self: &Allocator, ptr: var) {
|
||||
@ -30,28 +35,52 @@ pub const Allocator = struct {
|
||||
}
|
||||
|
||||
fn alloc(self: &Allocator, comptime T: type, n: usize) -> %[]T {
|
||||
return self.alignedAlloc(T, @alignOf(T), n);
|
||||
}
|
||||
|
||||
fn alignedAlloc(self: &Allocator, comptime T: type, comptime alignment: u29,
|
||||
n: usize) -> %[]align(alignment) T
|
||||
{
|
||||
const byte_count = %return math.mul(usize, @sizeOf(T), n);
|
||||
const byte_slice = %return self.allocFn(self, byte_count, @alignOf(T));
|
||||
([]T)(@alignCast(@alignOf(T), byte_slice))
|
||||
const byte_slice = %return self.allocFn(self, byte_count, alignment);
|
||||
// This loop should get optimized out in ReleaseFast mode
|
||||
for (byte_slice) |*byte| {
|
||||
*byte = undefined;
|
||||
}
|
||||
return ([]align(alignment) T)(@alignCast(alignment, byte_slice));
|
||||
}
|
||||
|
||||
fn realloc(self: &Allocator, comptime T: type, old_mem: []T, n: usize) -> %[]T {
|
||||
return self.alignedRealloc(T, @alignOf(T), @alignCast(@alignOf(T), old_mem), n);
|
||||
}
|
||||
|
||||
fn alignedRealloc(self: &Allocator, comptime T: type, comptime alignment: u29,
|
||||
old_mem: []align(alignment) T, n: usize) -> %[]align(alignment) T
|
||||
{
|
||||
if (old_mem.len == 0) {
|
||||
return self.alloc(T, n);
|
||||
}
|
||||
|
||||
// Assert that old_mem.ptr is properly aligned.
|
||||
const aligned_old_mem = @alignCast(@alignOf(T), old_mem);
|
||||
|
||||
const old_byte_slice = ([]u8)(old_mem);
|
||||
const byte_count = %return math.mul(usize, @sizeOf(T), n);
|
||||
const byte_slice = %return self.reallocFn(self, ([]u8)(aligned_old_mem), byte_count, @alignOf(T));
|
||||
return ([]T)(@alignCast(@alignOf(T), byte_slice));
|
||||
const byte_slice = %return self.reallocFn(self, old_byte_slice, byte_count, alignment);
|
||||
// This loop should get optimized out in ReleaseFast mode
|
||||
for (byte_slice[old_byte_slice.len..]) |*byte| {
|
||||
*byte = undefined;
|
||||
}
|
||||
return ([]T)(@alignCast(alignment, byte_slice));
|
||||
}
|
||||
|
||||
/// Reallocate, but `n` must be less than or equal to `old_mem.len`.
|
||||
/// Unlike `realloc`, this function cannot fail.
|
||||
/// Shrinking to 0 is the same as calling `free`.
|
||||
fn shrink(self: &Allocator, comptime T: type, old_mem: []T, n: usize) -> []T {
|
||||
return self.alignedShrink(T, @alignOf(T), @alignCast(@alignOf(T), old_mem), n);
|
||||
}
|
||||
|
||||
fn alignedShrink(self: &Allocator, comptime T: type, comptime alignment: u29,
|
||||
old_mem: []align(alignment) T, n: usize) -> []align(alignment) T
|
||||
{
|
||||
if (n == 0) {
|
||||
self.free(old_mem);
|
||||
return old_mem[0..0];
|
||||
@ -59,15 +88,12 @@ pub const Allocator = struct {
|
||||
|
||||
assert(n <= old_mem.len);
|
||||
|
||||
// Assert that old_mem.ptr is properly aligned.
|
||||
const aligned_old_mem = @alignCast(@alignOf(T), old_mem);
|
||||
|
||||
// Here we skip the overflow checking on the multiplication because
|
||||
// n <= old_mem.len and the multiplication didn't overflow for that operation.
|
||||
const byte_count = @sizeOf(T) * n;
|
||||
|
||||
const byte_slice = %%self.reallocFn(self, ([]u8)(aligned_old_mem), byte_count, @alignOf(T));
|
||||
return ([]T)(@alignCast(@alignOf(T), byte_slice));
|
||||
const byte_slice = %%self.reallocFn(self, ([]u8)(old_mem), byte_count, alignment);
|
||||
return ([]align(alignment) T)(@alignCast(alignment, byte_slice));
|
||||
}
|
||||
|
||||
fn free(self: &Allocator, memory: var) {
|
||||
@ -79,6 +105,51 @@ pub const Allocator = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub const FixedBufferAllocator = struct {
|
||||
allocator: Allocator,
|
||||
end_index: usize,
|
||||
buffer: []u8,
|
||||
|
||||
pub fn init(buffer: []u8) -> FixedBufferAllocator {
|
||||
return FixedBufferAllocator {
|
||||
.allocator = Allocator {
|
||||
.allocFn = alloc,
|
||||
.reallocFn = realloc,
|
||||
.freeFn = free,
|
||||
},
|
||||
.buffer = buffer,
|
||||
.end_index = 0,
|
||||
};
|
||||
}
|
||||
|
||||
fn alloc(allocator: &Allocator, n: usize, alignment: u29) -> %[]u8 {
|
||||
const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator);
|
||||
const addr = @ptrToInt(&self.buffer[self.end_index]);
|
||||
const rem = @rem(addr, alignment);
|
||||
const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
|
||||
const adjusted_index = self.end_index + march_forward_bytes;
|
||||
const new_end_index = adjusted_index + n;
|
||||
if (new_end_index > self.buffer.len) {
|
||||
return error.OutOfMemory;
|
||||
}
|
||||
const result = self.buffer[adjusted_index .. new_end_index];
|
||||
self.end_index = new_end_index;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) -> %[]u8 {
|
||||
if (new_size <= old_mem.len) {
|
||||
return old_mem[0..new_size];
|
||||
} else {
|
||||
const result = %return alloc(allocator, new_size, alignment);
|
||||
copy(u8, result, old_mem);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
fn free(allocator: &Allocator, bytes: []u8) { }
|
||||
};
|
||||
|
||||
|
||||
/// Copy all of source into dest at position 0.
|
||||
/// dest.len must be >= source.len.
|
||||
@ -95,17 +166,24 @@ pub fn set(comptime T: type, dest: []T, value: T) {
|
||||
for (dest) |*d| *d = value;
|
||||
}
|
||||
|
||||
/// Return < 0, == 0, or > 0 if memory a is less than, equal to, or greater than,
|
||||
/// memory b, respectively.
|
||||
pub fn cmp(comptime T: type, a: []const T, b: []const T) -> Cmp {
|
||||
const n = math.min(a.len, b.len);
|
||||
/// Returns true if lhs < rhs, false otherwise
|
||||
pub fn lessThan(comptime T: type, lhs: []const T, rhs: []const T) -> bool {
|
||||
const n = math.min(lhs.len, rhs.len);
|
||||
var i: usize = 0;
|
||||
while (i < n) : (i += 1) {
|
||||
if (a[i] == b[i]) continue;
|
||||
return if (a[i] > b[i]) Cmp.Greater else if (a[i] < b[i]) Cmp.Less else Cmp.Equal;
|
||||
if (lhs[i] == rhs[i]) continue;
|
||||
return lhs[i] < rhs[i];
|
||||
}
|
||||
|
||||
return if (a.len > b.len) Cmp.Greater else if (a.len < b.len) Cmp.Less else Cmp.Equal;
|
||||
return lhs.len < rhs.len;
|
||||
}
|
||||
|
||||
test "mem.lessThan" {
|
||||
assert(lessThan(u8, "abcd", "bee"));
|
||||
assert(!lessThan(u8, "abc", "abc"));
|
||||
assert(lessThan(u8, "abc", "abc0"));
|
||||
assert(!lessThan(u8, "", ""));
|
||||
assert(lessThan(u8, "", "a"));
|
||||
}
|
||||
|
||||
/// Compares two slices and returns whether they are equal.
|
||||
@ -276,11 +354,11 @@ pub fn eql_slice_u8(a: []const u8, b: []const u8) -> bool {
|
||||
/// split(" abc def ghi ", " ")
|
||||
/// Will return slices for "abc", "def", "ghi", null, in that order.
|
||||
pub fn split(buffer: []const u8, split_bytes: []const u8) -> SplitIterator {
|
||||
SplitIterator {
|
||||
return SplitIterator {
|
||||
.index = 0,
|
||||
.buffer = buffer,
|
||||
.split_bytes = split_bytes,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test "mem.split" {
|
||||
@ -433,9 +511,8 @@ fn testWriteIntImpl() {
|
||||
|
||||
pub fn min(comptime T: type, slice: []const T) -> T {
|
||||
var best = slice[0];
|
||||
var i: usize = 1;
|
||||
while (i < slice.len) : (i += 1) {
|
||||
best = math.min(best, slice[i]);
|
||||
for (slice[1..]) |item| {
|
||||
best = math.min(best, item);
|
||||
}
|
||||
return best;
|
||||
}
|
||||
@ -446,9 +523,8 @@ test "mem.min" {
|
||||
|
||||
pub fn max(comptime T: type, slice: []const T) -> T {
|
||||
var best = slice[0];
|
||||
var i: usize = 1;
|
||||
while (i < slice.len) : (i += 1) {
|
||||
best = math.max(best, slice[i]);
|
||||
for (slice[1..]) |item| {
|
||||
best = math.max(best, item);
|
||||
}
|
||||
return best;
|
||||
}
|
||||
@ -456,3 +532,40 @@ pub fn max(comptime T: type, slice: []const T) -> T {
|
||||
test "mem.max" {
|
||||
assert(max(u8, "abcdefg") == 'g');
|
||||
}
|
||||
|
||||
pub fn swap(comptime T: type, a: &T, b: &T) {
|
||||
const tmp = *a;
|
||||
*a = *b;
|
||||
*b = tmp;
|
||||
}
|
||||
|
||||
/// In-place order reversal of a slice
|
||||
pub fn reverse(comptime T: type, items: []T) {
|
||||
var i: usize = 0;
|
||||
const end = items.len / 2;
|
||||
while (i < end) : (i += 1) {
|
||||
swap(T, &items[i], &items[items.len - i - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
test "std.mem.reverse" {
|
||||
var arr = []i32{ 5, 3, 1, 2, 4 };
|
||||
reverse(i32, arr[0..]);
|
||||
|
||||
assert(eql(i32, arr, []i32{ 4, 2, 1, 3, 5 }));
|
||||
}
|
||||
|
||||
/// In-place rotation of the values in an array ([0 1 2 3] becomes [1 2 3 0] if we rotate by 1)
|
||||
/// Assumes 0 <= amount <= items.len
|
||||
pub fn rotate(comptime T: type, items: []T, amount: usize) {
|
||||
reverse(T, items[0..amount]);
|
||||
reverse(T, items[amount..]);
|
||||
reverse(T, items);
|
||||
}
|
||||
|
||||
test "std.mem.rotate" {
|
||||
var arr = []i32{ 5, 3, 1, 2, 4 };
|
||||
rotate(i32, arr[0..], 2);
|
||||
|
||||
assert(eql(i32, arr, []i32{ 1, 2, 4, 5, 3 }));
|
||||
}
|
||||
|
||||
22
std/net.zig
22
std/net.zig
@ -72,7 +72,7 @@ pub fn lookup(hostname: []const u8, out_addrs: []Address) -> %[]Address {
|
||||
// if (family != AF_INET)
|
||||
// buf[cnt++] = (struct address){ .family = AF_INET6, .addr = { [15] = 1 } };
|
||||
//
|
||||
unreachable // TODO
|
||||
unreachable; // TODO
|
||||
}
|
||||
|
||||
// TODO
|
||||
@ -84,7 +84,7 @@ pub fn lookup(hostname: []const u8, out_addrs: []Address) -> %[]Address {
|
||||
// else => {},
|
||||
//};
|
||||
|
||||
unreachable // TODO
|
||||
unreachable; // TODO
|
||||
}
|
||||
|
||||
pub fn connectAddr(addr: &Address, port: u16) -> %Connection {
|
||||
@ -96,23 +96,23 @@ pub fn connectAddr(addr: &Address, port: u16) -> %Connection {
|
||||
}
|
||||
const socket_fd = i32(socket_ret);
|
||||
|
||||
const connect_ret = if (addr.family == linux.AF_INET) {
|
||||
const connect_ret = if (addr.family == linux.AF_INET) x: {
|
||||
var os_addr: linux.sockaddr_in = undefined;
|
||||
os_addr.family = addr.family;
|
||||
os_addr.port = endian.swapIfLe(u16, port);
|
||||
@memcpy((&u8)(&os_addr.addr), &addr.addr[0], 4);
|
||||
@memset(&os_addr.zero[0], 0, @sizeOf(@typeOf(os_addr.zero)));
|
||||
linux.connect(socket_fd, (&linux.sockaddr)(&os_addr), @sizeOf(linux.sockaddr_in))
|
||||
} else if (addr.family == linux.AF_INET6) {
|
||||
break :x linux.connect(socket_fd, (&linux.sockaddr)(&os_addr), @sizeOf(linux.sockaddr_in));
|
||||
} else if (addr.family == linux.AF_INET6) x: {
|
||||
var os_addr: linux.sockaddr_in6 = undefined;
|
||||
os_addr.family = addr.family;
|
||||
os_addr.port = endian.swapIfLe(u16, port);
|
||||
os_addr.flowinfo = 0;
|
||||
os_addr.scope_id = addr.scope_id;
|
||||
@memcpy(&os_addr.addr[0], &addr.addr[0], 16);
|
||||
linux.connect(socket_fd, (&linux.sockaddr)(&os_addr), @sizeOf(linux.sockaddr_in6))
|
||||
break :x linux.connect(socket_fd, (&linux.sockaddr)(&os_addr), @sizeOf(linux.sockaddr_in6));
|
||||
} else {
|
||||
unreachable
|
||||
unreachable;
|
||||
};
|
||||
const connect_err = linux.getErrno(connect_ret);
|
||||
if (connect_err > 0) {
|
||||
@ -165,13 +165,13 @@ pub fn parseIpLiteral(buf: []const u8) -> %Address {
|
||||
fn hexDigit(c: u8) -> u8 {
|
||||
// TODO use switch with range
|
||||
if ('0' <= c and c <= '9') {
|
||||
c - '0'
|
||||
return c - '0';
|
||||
} else if ('A' <= c and c <= 'Z') {
|
||||
c - 'A' + 10
|
||||
return c - 'A' + 10;
|
||||
} else if ('a' <= c and c <= 'z') {
|
||||
c - 'a' + 10
|
||||
return c - 'a' + 10;
|
||||
} else {
|
||||
@maxValue(u8)
|
||||
return @maxValue(u8);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -5,7 +5,6 @@ const os = std.os;
|
||||
const posix = os.posix;
|
||||
const windows = os.windows;
|
||||
const mem = std.mem;
|
||||
const Allocator = mem.Allocator;
|
||||
const debug = std.debug;
|
||||
const assert = debug.assert;
|
||||
const BufMap = std.BufMap;
|
||||
@ -74,7 +73,7 @@ pub const ChildProcess = struct {
|
||||
|
||||
/// First argument in argv is the executable.
|
||||
/// On success must call deinit.
|
||||
pub fn init(argv: []const []const u8, allocator: &Allocator) -> %&ChildProcess {
|
||||
pub fn init(argv: []const []const u8, allocator: &mem.Allocator) -> %&ChildProcess {
|
||||
const child = %return allocator.create(ChildProcess);
|
||||
%defer allocator.destroy(child);
|
||||
|
||||
@ -116,7 +115,7 @@ pub const ChildProcess = struct {
|
||||
return self.spawnWindows();
|
||||
} else {
|
||||
return self.spawnPosix();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spawnAndWait(self: &ChildProcess) -> %Term {
|
||||
@ -180,6 +179,46 @@ pub const ChildProcess = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub const ExecResult = struct {
|
||||
term: os.ChildProcess.Term,
|
||||
stdout: []u8,
|
||||
stderr: []u8,
|
||||
};
|
||||
|
||||
/// Spawns a child process, waits for it, collecting stdout and stderr, and then returns.
|
||||
/// If it succeeds, the caller owns result.stdout and result.stderr memory.
|
||||
pub fn exec(allocator: &mem.Allocator, argv: []const []const u8, cwd: ?[]const u8,
|
||||
env_map: ?&const BufMap, max_output_size: usize) -> %ExecResult
|
||||
{
|
||||
const child = %%ChildProcess.init(argv, allocator);
|
||||
defer child.deinit();
|
||||
|
||||
child.stdin_behavior = ChildProcess.StdIo.Ignore;
|
||||
child.stdout_behavior = ChildProcess.StdIo.Pipe;
|
||||
child.stderr_behavior = ChildProcess.StdIo.Pipe;
|
||||
child.cwd = cwd;
|
||||
child.env_map = env_map;
|
||||
|
||||
%return child.spawn();
|
||||
|
||||
var stdout = Buffer.initNull(allocator);
|
||||
var stderr = Buffer.initNull(allocator);
|
||||
defer Buffer.deinit(&stdout);
|
||||
defer Buffer.deinit(&stderr);
|
||||
|
||||
var stdout_file_in_stream = io.FileInStream.init(&??child.stdout);
|
||||
var stderr_file_in_stream = io.FileInStream.init(&??child.stderr);
|
||||
|
||||
%return stdout_file_in_stream.stream.readAllBuffer(&stdout, max_output_size);
|
||||
%return stderr_file_in_stream.stream.readAllBuffer(&stderr, max_output_size);
|
||||
|
||||
return ExecResult {
|
||||
.term = %return child.wait(),
|
||||
.stdout = stdout.toOwnedSlice(),
|
||||
.stderr = stderr.toOwnedSlice(),
|
||||
};
|
||||
}
|
||||
|
||||
fn waitWindows(self: &ChildProcess) -> %Term {
|
||||
if (self.term) |term| {
|
||||
self.cleanupStreams();
|
||||
@ -210,12 +249,12 @@ pub const ChildProcess = struct {
|
||||
fn waitUnwrappedWindows(self: &ChildProcess) -> %void {
|
||||
const result = os.windowsWaitSingle(self.handle, windows.INFINITE);
|
||||
|
||||
self.term = (%Term)({
|
||||
self.term = (%Term)(x: {
|
||||
var exit_code: windows.DWORD = undefined;
|
||||
if (windows.GetExitCodeProcess(self.handle, &exit_code) == 0) {
|
||||
Term { .Unknown = 0 }
|
||||
break :x Term { .Unknown = 0 };
|
||||
} else {
|
||||
Term { .Exited = @bitCast(i32, exit_code)}
|
||||
break :x Term { .Exited = @bitCast(i32, exit_code)};
|
||||
}
|
||||
});
|
||||
|
||||
@ -261,7 +300,7 @@ pub const ChildProcess = struct {
|
||||
defer {
|
||||
os.close(self.err_pipe[0]);
|
||||
os.close(self.err_pipe[1]);
|
||||
};
|
||||
}
|
||||
|
||||
// Write @maxValue(ErrInt) to the write end of the err_pipe. This is after
|
||||
// waitpid, so this write is guaranteed to be after the child
|
||||
@ -280,15 +319,15 @@ pub const ChildProcess = struct {
|
||||
}
|
||||
|
||||
fn statusToTerm(status: i32) -> Term {
|
||||
return if (posix.WIFEXITED(status)) {
|
||||
return if (posix.WIFEXITED(status))
|
||||
Term { .Exited = posix.WEXITSTATUS(status) }
|
||||
} else if (posix.WIFSIGNALED(status)) {
|
||||
else if (posix.WIFSIGNALED(status))
|
||||
Term { .Signal = posix.WTERMSIG(status) }
|
||||
} else if (posix.WIFSTOPPED(status)) {
|
||||
else if (posix.WIFSTOPPED(status))
|
||||
Term { .Stopped = posix.WSTOPSIG(status) }
|
||||
} else {
|
||||
else
|
||||
Term { .Unknown = status }
|
||||
};
|
||||
;
|
||||
}
|
||||
|
||||
fn spawnPosix(self: &ChildProcess) -> %void {
|
||||
@ -305,22 +344,22 @@ pub const ChildProcess = struct {
|
||||
%defer if (self.stderr_behavior == StdIo.Pipe) { destroyPipe(stderr_pipe); };
|
||||
|
||||
const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);
|
||||
const dev_null_fd = if (any_ignore) {
|
||||
const dev_null_fd = if (any_ignore)
|
||||
%return os.posixOpen("/dev/null", posix.O_RDWR, 0, null)
|
||||
} else {
|
||||
else
|
||||
undefined
|
||||
};
|
||||
defer { if (any_ignore) os.close(dev_null_fd); };
|
||||
;
|
||||
defer { if (any_ignore) os.close(dev_null_fd); }
|
||||
|
||||
var env_map_owned: BufMap = undefined;
|
||||
var we_own_env_map: bool = undefined;
|
||||
const env_map = if (self.env_map) |env_map| {
|
||||
const env_map = if (self.env_map) |env_map| x: {
|
||||
we_own_env_map = false;
|
||||
env_map
|
||||
} else {
|
||||
break :x env_map;
|
||||
} else x: {
|
||||
we_own_env_map = true;
|
||||
env_map_owned = %return os.getEnvMap(self.allocator);
|
||||
&env_map_owned
|
||||
break :x &env_map_owned;
|
||||
};
|
||||
defer { if (we_own_env_map) env_map_owned.deinit(); }
|
||||
|
||||
@ -411,13 +450,13 @@ pub const ChildProcess = struct {
|
||||
self.stdout_behavior == StdIo.Ignore or
|
||||
self.stderr_behavior == StdIo.Ignore);
|
||||
|
||||
const nul_handle = if (any_ignore) {
|
||||
const nul_handle = if (any_ignore)
|
||||
%return os.windowsOpen("NUL", windows.GENERIC_READ, windows.FILE_SHARE_READ,
|
||||
windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL, null)
|
||||
} else {
|
||||
else
|
||||
undefined
|
||||
};
|
||||
defer { if (any_ignore) os.close(nul_handle); };
|
||||
;
|
||||
defer { if (any_ignore) os.close(nul_handle); }
|
||||
if (any_ignore) {
|
||||
%return windowsSetHandleInfo(nul_handle, windows.HANDLE_FLAG_INHERIT, 0);
|
||||
}
|
||||
@ -503,30 +542,32 @@ pub const ChildProcess = struct {
|
||||
};
|
||||
var piProcInfo: windows.PROCESS_INFORMATION = undefined;
|
||||
|
||||
const cwd_slice = if (self.cwd) |cwd| {
|
||||
const cwd_slice = if (self.cwd) |cwd|
|
||||
%return cstr.addNullByte(self.allocator, cwd)
|
||||
} else {
|
||||
else
|
||||
null
|
||||
};
|
||||
;
|
||||
defer if (cwd_slice) |cwd| self.allocator.free(cwd);
|
||||
const cwd_ptr = if (cwd_slice) |cwd| cwd.ptr else null;
|
||||
|
||||
const maybe_envp_buf = if (self.env_map) |env_map| {
|
||||
const maybe_envp_buf = if (self.env_map) |env_map|
|
||||
%return os.createWindowsEnvBlock(self.allocator, env_map)
|
||||
} else {
|
||||
else
|
||||
null
|
||||
};
|
||||
;
|
||||
defer if (maybe_envp_buf) |envp_buf| self.allocator.free(envp_buf);
|
||||
const envp_ptr = if (maybe_envp_buf) |envp_buf| envp_buf.ptr else null;
|
||||
|
||||
// the cwd set in ChildProcess is in effect when choosing the executable path
|
||||
// to match posix semantics
|
||||
const app_name = if (self.cwd) |cwd| {
|
||||
const resolved = %return os.path.resolve(self.allocator, cwd, self.argv[0]);
|
||||
defer self.allocator.free(resolved);
|
||||
%return cstr.addNullByte(self.allocator, resolved)
|
||||
} else {
|
||||
%return cstr.addNullByte(self.allocator, self.argv[0])
|
||||
const app_name = x: {
|
||||
if (self.cwd) |cwd| {
|
||||
const resolved = %return os.path.resolve(self.allocator, cwd, self.argv[0]);
|
||||
defer self.allocator.free(resolved);
|
||||
break :x %return cstr.addNullByte(self.allocator, resolved);
|
||||
} else {
|
||||
break :x %return cstr.addNullByte(self.allocator, self.argv[0]);
|
||||
}
|
||||
};
|
||||
defer self.allocator.free(app_name);
|
||||
|
||||
@ -589,6 +630,7 @@ pub const ChildProcess = struct {
|
||||
StdIo.Ignore => %return os.posixDup2(dev_null_fd, std_fileno),
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
fn windowsCreateProcess(app_name: &u8, cmd_line: &u8, envp_ptr: ?&u8, cwd_ptr: ?&u8,
|
||||
@ -611,7 +653,7 @@ fn windowsCreateProcess(app_name: &u8, cmd_line: &u8, envp_ptr: ?&u8, cwd_ptr: ?
|
||||
|
||||
/// Caller must dealloc.
|
||||
/// Guarantees a null byte at result[result.len].
|
||||
fn windowsCreateCommandLine(allocator: &Allocator, argv: []const []const u8) -> %[]u8 {
|
||||
fn windowsCreateCommandLine(allocator: &mem.Allocator, argv: []const []const u8) -> %[]u8 {
|
||||
var buf = %return Buffer.initSize(allocator, 0);
|
||||
defer buf.deinit();
|
||||
|
||||
@ -701,7 +743,7 @@ fn makePipe() -> %[2]i32 {
|
||||
return switch (err) {
|
||||
posix.EMFILE, posix.ENFILE => error.SystemResources,
|
||||
else => os.unexpectedErrorPosix(err),
|
||||
}
|
||||
};
|
||||
}
|
||||
return fds;
|
||||
}
|
||||
@ -760,10 +802,10 @@ fn handleTerm(pid: i32, status: i32) {
|
||||
}
|
||||
}
|
||||
|
||||
const sigchld_set = {
|
||||
const sigchld_set = x: {
|
||||
var signal_set = posix.empty_sigset;
|
||||
posix.sigaddset(&signal_set, posix.SIGCHLD);
|
||||
signal_set
|
||||
break :x signal_set;
|
||||
};
|
||||
|
||||
fn block_SIGCHLD() {
|
||||
|
||||
@ -97,63 +97,63 @@ pub const SIGINFO = 29; /// information request
|
||||
pub const SIGUSR1 = 30; /// user defined signal 1
|
||||
pub const SIGUSR2 = 31; /// user defined signal 2
|
||||
|
||||
fn wstatus(x: i32) -> i32 { x & 0o177 }
|
||||
fn wstatus(x: i32) -> i32 { return x & 0o177; }
|
||||
const wstopped = 0o177;
|
||||
pub fn WEXITSTATUS(x: i32) -> i32 { x >> 8 }
|
||||
pub fn WTERMSIG(x: i32) -> i32 { wstatus(x) }
|
||||
pub fn WSTOPSIG(x: i32) -> i32 { x >> 8 }
|
||||
pub fn WIFEXITED(x: i32) -> bool { wstatus(x) == 0 }
|
||||
pub fn WIFSTOPPED(x: i32) -> bool { wstatus(x) == wstopped and WSTOPSIG(x) != 0x13 }
|
||||
pub fn WIFSIGNALED(x: i32) -> bool { wstatus(x) != wstopped and wstatus(x) != 0 }
|
||||
pub fn WEXITSTATUS(x: i32) -> i32 { return x >> 8; }
|
||||
pub fn WTERMSIG(x: i32) -> i32 { return wstatus(x); }
|
||||
pub fn WSTOPSIG(x: i32) -> i32 { return x >> 8; }
|
||||
pub fn WIFEXITED(x: i32) -> bool { return wstatus(x) == 0; }
|
||||
pub fn WIFSTOPPED(x: i32) -> bool { return wstatus(x) == wstopped and WSTOPSIG(x) != 0x13; }
|
||||
pub fn WIFSIGNALED(x: i32) -> bool { return wstatus(x) != wstopped and wstatus(x) != 0; }
|
||||
|
||||
/// Get the errno from a syscall return value, or 0 for no error.
|
||||
pub fn getErrno(r: usize) -> usize {
|
||||
const signed_r = @bitCast(isize, r);
|
||||
if (signed_r > -4096 and signed_r < 0) usize(-signed_r) else 0
|
||||
return if (signed_r > -4096 and signed_r < 0) usize(-signed_r) else 0;
|
||||
}
|
||||
|
||||
pub fn close(fd: i32) -> usize {
|
||||
errnoWrap(c.close(fd))
|
||||
return errnoWrap(c.close(fd));
|
||||
}
|
||||
|
||||
pub fn abort() -> noreturn {
|
||||
c.abort()
|
||||
c.abort();
|
||||
}
|
||||
|
||||
pub fn exit(code: i32) -> noreturn {
|
||||
c.exit(code)
|
||||
c.exit(code);
|
||||
}
|
||||
|
||||
pub fn isatty(fd: i32) -> bool {
|
||||
c.isatty(fd) != 0
|
||||
return c.isatty(fd) != 0;
|
||||
}
|
||||
|
||||
pub fn fstat(fd: i32, buf: &c.Stat) -> usize {
|
||||
errnoWrap(c.@"fstat$INODE64"(fd, buf))
|
||||
return errnoWrap(c.@"fstat$INODE64"(fd, buf));
|
||||
}
|
||||
|
||||
pub fn lseek(fd: i32, offset: isize, whence: c_int) -> usize {
|
||||
errnoWrap(c.lseek(fd, offset, whence))
|
||||
return errnoWrap(c.lseek(fd, offset, whence));
|
||||
}
|
||||
|
||||
pub fn open(path: &const u8, flags: u32, mode: usize) -> usize {
|
||||
errnoWrap(c.open(path, @bitCast(c_int, flags), mode))
|
||||
return errnoWrap(c.open(path, @bitCast(c_int, flags), mode));
|
||||
}
|
||||
|
||||
pub fn raise(sig: i32) -> usize {
|
||||
errnoWrap(c.raise(sig))
|
||||
return errnoWrap(c.raise(sig));
|
||||
}
|
||||
|
||||
pub fn read(fd: i32, buf: &u8, nbyte: usize) -> usize {
|
||||
errnoWrap(c.read(fd, @ptrCast(&c_void, buf), nbyte))
|
||||
return errnoWrap(c.read(fd, @ptrCast(&c_void, buf), nbyte));
|
||||
}
|
||||
|
||||
pub fn stat(noalias path: &const u8, noalias buf: &stat) -> usize {
|
||||
errnoWrap(c.stat(path, buf))
|
||||
return errnoWrap(c.stat(path, buf));
|
||||
}
|
||||
|
||||
pub fn write(fd: i32, buf: &const u8, nbyte: usize) -> usize {
|
||||
errnoWrap(c.write(fd, @ptrCast(&const c_void, buf), nbyte))
|
||||
return errnoWrap(c.write(fd, @ptrCast(&const c_void, buf), nbyte));
|
||||
}
|
||||
|
||||
pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32,
|
||||
@ -166,79 +166,79 @@ pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32,
|
||||
}
|
||||
|
||||
pub fn munmap(address: &u8, length: usize) -> usize {
|
||||
errnoWrap(c.munmap(@ptrCast(&c_void, address), length))
|
||||
return errnoWrap(c.munmap(@ptrCast(&c_void, address), length));
|
||||
}
|
||||
|
||||
pub fn unlink(path: &const u8) -> usize {
|
||||
errnoWrap(c.unlink(path))
|
||||
return errnoWrap(c.unlink(path));
|
||||
}
|
||||
|
||||
pub fn getcwd(buf: &u8, size: usize) -> usize {
|
||||
if (c.getcwd(buf, size) == null) @bitCast(usize, -isize(*c._errno())) else 0
|
||||
return if (c.getcwd(buf, size) == null) @bitCast(usize, -isize(*c._errno())) else 0;
|
||||
}
|
||||
|
||||
pub fn waitpid(pid: i32, status: &i32, options: u32) -> usize {
|
||||
comptime assert(i32.bit_count == c_int.bit_count);
|
||||
errnoWrap(c.waitpid(pid, @ptrCast(&c_int, status), @bitCast(c_int, options)))
|
||||
return errnoWrap(c.waitpid(pid, @ptrCast(&c_int, status), @bitCast(c_int, options)));
|
||||
}
|
||||
|
||||
pub fn fork() -> usize {
|
||||
errnoWrap(c.fork())
|
||||
return errnoWrap(c.fork());
|
||||
}
|
||||
|
||||
pub fn pipe(fds: &[2]i32) -> usize {
|
||||
comptime assert(i32.bit_count == c_int.bit_count);
|
||||
errnoWrap(c.pipe(@ptrCast(&c_int, fds)))
|
||||
return errnoWrap(c.pipe(@ptrCast(&c_int, fds)));
|
||||
}
|
||||
|
||||
pub fn mkdir(path: &const u8, mode: u32) -> usize {
|
||||
errnoWrap(c.mkdir(path, mode))
|
||||
return errnoWrap(c.mkdir(path, mode));
|
||||
}
|
||||
|
||||
pub fn symlink(existing: &const u8, new: &const u8) -> usize {
|
||||
errnoWrap(c.symlink(existing, new))
|
||||
return errnoWrap(c.symlink(existing, new));
|
||||
}
|
||||
|
||||
pub fn rename(old: &const u8, new: &const u8) -> usize {
|
||||
errnoWrap(c.rename(old, new))
|
||||
return errnoWrap(c.rename(old, new));
|
||||
}
|
||||
|
||||
pub fn chdir(path: &const u8) -> usize {
|
||||
errnoWrap(c.chdir(path))
|
||||
return errnoWrap(c.chdir(path));
|
||||
}
|
||||
|
||||
pub fn execve(path: &const u8, argv: &const ?&const u8, envp: &const ?&const u8)
|
||||
-> usize
|
||||
{
|
||||
errnoWrap(c.execve(path, argv, envp))
|
||||
return errnoWrap(c.execve(path, argv, envp));
|
||||
}
|
||||
|
||||
pub fn dup2(old: i32, new: i32) -> usize {
|
||||
errnoWrap(c.dup2(old, new))
|
||||
return errnoWrap(c.dup2(old, new));
|
||||
}
|
||||
|
||||
pub fn readlink(noalias path: &const u8, noalias buf_ptr: &u8, buf_len: usize) -> usize {
|
||||
errnoWrap(c.readlink(path, buf_ptr, buf_len))
|
||||
return errnoWrap(c.readlink(path, buf_ptr, buf_len));
|
||||
}
|
||||
|
||||
pub fn nanosleep(req: &const timespec, rem: ?×pec) -> usize {
|
||||
errnoWrap(c.nanosleep(req, rem))
|
||||
return errnoWrap(c.nanosleep(req, rem));
|
||||
}
|
||||
|
||||
pub fn realpath(noalias filename: &const u8, noalias resolved_name: &u8) -> usize {
|
||||
if (c.realpath(filename, resolved_name) == null) @bitCast(usize, -isize(*c._errno())) else 0
|
||||
return if (c.realpath(filename, resolved_name) == null) @bitCast(usize, -isize(*c._errno())) else 0;
|
||||
}
|
||||
|
||||
pub fn setreuid(ruid: u32, euid: u32) -> usize {
|
||||
errnoWrap(c.setreuid(ruid, euid))
|
||||
return errnoWrap(c.setreuid(ruid, euid));
|
||||
}
|
||||
|
||||
pub fn setregid(rgid: u32, egid: u32) -> usize {
|
||||
errnoWrap(c.setregid(rgid, egid))
|
||||
return errnoWrap(c.setregid(rgid, egid));
|
||||
}
|
||||
|
||||
pub fn sigprocmask(flags: u32, noalias set: &const sigset_t, noalias oldset: ?&sigset_t) -> usize {
|
||||
errnoWrap(c.sigprocmask(@bitCast(c_int, flags), set, oldset))
|
||||
return errnoWrap(c.sigprocmask(@bitCast(c_int, flags), set, oldset));
|
||||
}
|
||||
|
||||
pub fn sigaction(sig: u5, noalias act: &const Sigaction, noalias oact: ?&Sigaction) -> usize {
|
||||
@ -285,9 +285,5 @@ pub fn sigaddset(set: &sigset_t, signo: u5) {
|
||||
/// that the kernel represents it to libc. Errno was a mistake, let's make
|
||||
/// it go away forever.
|
||||
fn errnoWrap(value: isize) -> usize {
|
||||
@bitCast(usize, if (value == -1) {
|
||||
-isize(*c._errno())
|
||||
} else {
|
||||
value
|
||||
})
|
||||
return @bitCast(usize, if (value == -1) -isize(*c._errno()) else value);
|
||||
}
|
||||
|
||||
256
std/os/index.zig
256
std/os/index.zig
@ -84,7 +84,7 @@ pub fn getRandomBytes(buf: []u8) -> %void {
|
||||
posix.EFAULT => unreachable,
|
||||
posix.EINTR => continue,
|
||||
else => unexpectedErrorPosix(err),
|
||||
}
|
||||
};
|
||||
}
|
||||
return;
|
||||
},
|
||||
@ -151,18 +151,17 @@ pub coldcc fn exit(status: i32) -> noreturn {
|
||||
}
|
||||
switch (builtin.os) {
|
||||
Os.linux, Os.darwin, Os.macosx, Os.ios => {
|
||||
posix.exit(status)
|
||||
posix.exit(status);
|
||||
},
|
||||
Os.windows => {
|
||||
// Map a possibly negative status code to a non-negative status for the systems default
|
||||
// integer width.
|
||||
const p_status = if (@sizeOf(c_uint) < @sizeOf(u32)) {
|
||||
const p_status = if (@sizeOf(c_uint) < @sizeOf(u32))
|
||||
@truncate(c_uint, @bitCast(u32, status))
|
||||
} else {
|
||||
c_uint(@bitCast(u32, status))
|
||||
};
|
||||
else
|
||||
c_uint(@bitCast(u32, status));
|
||||
|
||||
windows.ExitProcess(p_status)
|
||||
windows.ExitProcess(p_status);
|
||||
},
|
||||
else => @compileError("Unsupported OS"),
|
||||
}
|
||||
@ -289,7 +288,7 @@ pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize, allocator: ?&Al
|
||||
posix.EPERM => error.AccessDenied,
|
||||
posix.EEXIST => error.PathAlreadyExists,
|
||||
else => unexpectedErrorPosix(err),
|
||||
}
|
||||
};
|
||||
}
|
||||
return i32(result);
|
||||
}
|
||||
@ -680,7 +679,7 @@ pub fn deleteFileWindows(allocator: &Allocator, file_path: []const u8) -> %void
|
||||
windows.ERROR.ACCESS_DENIED => error.AccessDenied,
|
||||
windows.ERROR.FILENAME_EXCED_RANGE, windows.ERROR.INVALID_PARAMETER => error.NameTooLong,
|
||||
else => unexpectedErrorWindows(err),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -902,40 +901,41 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) -> %void {
|
||||
/// this function recursively removes its entries and then tries again.
|
||||
// TODO non-recursive implementation
|
||||
pub fn deleteTree(allocator: &Allocator, full_path: []const u8) -> %void {
|
||||
start_over:
|
||||
// First, try deleting the item as a file. This way we don't follow sym links.
|
||||
if (deleteFile(allocator, full_path)) {
|
||||
return;
|
||||
} else |err| {
|
||||
if (err == error.FileNotFound)
|
||||
start_over: while (true) {
|
||||
// First, try deleting the item as a file. This way we don't follow sym links.
|
||||
if (deleteFile(allocator, full_path)) {
|
||||
return;
|
||||
if (err != error.IsDir)
|
||||
return err;
|
||||
}
|
||||
{
|
||||
var dir = Dir.open(allocator, full_path) %% |err| {
|
||||
} else |err| {
|
||||
if (err == error.FileNotFound)
|
||||
return;
|
||||
if (err == error.NotDir)
|
||||
goto start_over;
|
||||
return err;
|
||||
};
|
||||
defer dir.close();
|
||||
|
||||
var full_entry_buf = ArrayList(u8).init(allocator);
|
||||
defer full_entry_buf.deinit();
|
||||
|
||||
while (%return dir.next()) |entry| {
|
||||
%return full_entry_buf.resize(full_path.len + entry.name.len + 1);
|
||||
const full_entry_path = full_entry_buf.toSlice();
|
||||
mem.copy(u8, full_entry_path, full_path);
|
||||
full_entry_path[full_path.len] = '/';
|
||||
mem.copy(u8, full_entry_path[full_path.len + 1..], entry.name);
|
||||
|
||||
%return deleteTree(allocator, full_entry_path);
|
||||
if (err != error.IsDir)
|
||||
return err;
|
||||
}
|
||||
{
|
||||
var dir = Dir.open(allocator, full_path) %% |err| {
|
||||
if (err == error.FileNotFound)
|
||||
return;
|
||||
if (err == error.NotDir)
|
||||
continue :start_over;
|
||||
return err;
|
||||
};
|
||||
defer dir.close();
|
||||
|
||||
var full_entry_buf = ArrayList(u8).init(allocator);
|
||||
defer full_entry_buf.deinit();
|
||||
|
||||
while (%return dir.next()) |entry| {
|
||||
%return full_entry_buf.resize(full_path.len + entry.name.len + 1);
|
||||
const full_entry_path = full_entry_buf.toSlice();
|
||||
mem.copy(u8, full_entry_path, full_path);
|
||||
full_entry_path[full_path.len] = '/';
|
||||
mem.copy(u8, full_entry_path[full_path.len + 1..], entry.name);
|
||||
|
||||
%return deleteTree(allocator, full_entry_path);
|
||||
}
|
||||
}
|
||||
return deleteDir(allocator, full_path);
|
||||
}
|
||||
return deleteDir(allocator, full_path);
|
||||
}
|
||||
|
||||
pub const Dir = struct {
|
||||
@ -988,58 +988,59 @@ pub const Dir = struct {
|
||||
/// Memory such as file names referenced in this returned entry becomes invalid
|
||||
/// with subsequent calls to next, as well as when this ::Dir is deinitialized.
|
||||
pub fn next(self: &Dir) -> %?Entry {
|
||||
start_over:
|
||||
if (self.index >= self.end_index) {
|
||||
if (self.buf.len == 0) {
|
||||
self.buf = %return self.allocator.alloc(u8, page_size);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const result = posix.getdents(self.fd, self.buf.ptr, self.buf.len);
|
||||
const err = linux.getErrno(result);
|
||||
if (err > 0) {
|
||||
switch (err) {
|
||||
posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
|
||||
posix.EINVAL => {
|
||||
self.buf = %return self.allocator.realloc(u8, self.buf, self.buf.len * 2);
|
||||
continue;
|
||||
},
|
||||
else => return unexpectedErrorPosix(err),
|
||||
};
|
||||
start_over: while (true) {
|
||||
if (self.index >= self.end_index) {
|
||||
if (self.buf.len == 0) {
|
||||
self.buf = %return self.allocator.alloc(u8, page_size);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const result = posix.getdents(self.fd, self.buf.ptr, self.buf.len);
|
||||
const err = linux.getErrno(result);
|
||||
if (err > 0) {
|
||||
switch (err) {
|
||||
posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
|
||||
posix.EINVAL => {
|
||||
self.buf = %return self.allocator.realloc(u8, self.buf, self.buf.len * 2);
|
||||
continue;
|
||||
},
|
||||
else => return unexpectedErrorPosix(err),
|
||||
}
|
||||
}
|
||||
if (result == 0)
|
||||
return null;
|
||||
self.index = 0;
|
||||
self.end_index = result;
|
||||
break;
|
||||
}
|
||||
if (result == 0)
|
||||
return null;
|
||||
self.index = 0;
|
||||
self.end_index = result;
|
||||
break;
|
||||
}
|
||||
const linux_entry = @ptrCast(& align(1) LinuxEntry, &self.buf[self.index]);
|
||||
const next_index = self.index + linux_entry.d_reclen;
|
||||
self.index = next_index;
|
||||
|
||||
const name = cstr.toSlice(&linux_entry.d_name);
|
||||
|
||||
// skip . and .. entries
|
||||
if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
|
||||
continue :start_over;
|
||||
}
|
||||
|
||||
const type_char = self.buf[next_index - 1];
|
||||
const entry_kind = switch (type_char) {
|
||||
posix.DT_BLK => Entry.Kind.BlockDevice,
|
||||
posix.DT_CHR => Entry.Kind.CharacterDevice,
|
||||
posix.DT_DIR => Entry.Kind.Directory,
|
||||
posix.DT_FIFO => Entry.Kind.NamedPipe,
|
||||
posix.DT_LNK => Entry.Kind.SymLink,
|
||||
posix.DT_REG => Entry.Kind.File,
|
||||
posix.DT_SOCK => Entry.Kind.UnixDomainSocket,
|
||||
else => Entry.Kind.Unknown,
|
||||
};
|
||||
return Entry {
|
||||
.name = name,
|
||||
.kind = entry_kind,
|
||||
};
|
||||
}
|
||||
const linux_entry = @ptrCast(& align(1) LinuxEntry, &self.buf[self.index]);
|
||||
const next_index = self.index + linux_entry.d_reclen;
|
||||
self.index = next_index;
|
||||
|
||||
const name = cstr.toSlice(&linux_entry.d_name);
|
||||
|
||||
// skip . and .. entries
|
||||
if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
|
||||
goto start_over;
|
||||
}
|
||||
|
||||
const type_char = self.buf[next_index - 1];
|
||||
const entry_kind = switch (type_char) {
|
||||
posix.DT_BLK => Entry.Kind.BlockDevice,
|
||||
posix.DT_CHR => Entry.Kind.CharacterDevice,
|
||||
posix.DT_DIR => Entry.Kind.Directory,
|
||||
posix.DT_FIFO => Entry.Kind.NamedPipe,
|
||||
posix.DT_LNK => Entry.Kind.SymLink,
|
||||
posix.DT_REG => Entry.Kind.File,
|
||||
posix.DT_SOCK => Entry.Kind.UnixDomainSocket,
|
||||
else => Entry.Kind.Unknown,
|
||||
};
|
||||
return Entry {
|
||||
.name = name,
|
||||
.kind = entry_kind,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@ -1422,6 +1423,54 @@ pub fn args() -> ArgIterator {
|
||||
return ArgIterator.init();
|
||||
}
|
||||
|
||||
/// Caller must call freeArgs on result.
|
||||
pub fn argsAlloc(allocator: &mem.Allocator) -> %[]const []u8 {
|
||||
// TODO refactor to only make 1 allocation.
|
||||
var it = args();
|
||||
var contents = %return Buffer.initSize(allocator, 0);
|
||||
defer contents.deinit();
|
||||
|
||||
var slice_list = ArrayList(usize).init(allocator);
|
||||
defer slice_list.deinit();
|
||||
|
||||
while (it.next(allocator)) |arg_or_err| {
|
||||
const arg = %return arg_or_err;
|
||||
defer allocator.free(arg);
|
||||
%return contents.append(arg);
|
||||
%return slice_list.append(arg.len);
|
||||
}
|
||||
|
||||
const contents_slice = contents.toSliceConst();
|
||||
const slice_sizes = slice_list.toSliceConst();
|
||||
const slice_list_bytes = %return math.mul(usize, @sizeOf([]u8), slice_sizes.len);
|
||||
const total_bytes = %return math.add(usize, slice_list_bytes, contents_slice.len);
|
||||
const buf = %return allocator.alignedAlloc(u8, @alignOf([]u8), total_bytes);
|
||||
%defer allocator.free(buf);
|
||||
|
||||
const result_slice_list = ([][]u8)(buf[0..slice_list_bytes]);
|
||||
const result_contents = buf[slice_list_bytes..];
|
||||
mem.copy(u8, result_contents, contents_slice);
|
||||
|
||||
var contents_index: usize = 0;
|
||||
for (slice_sizes) |len, i| {
|
||||
const new_index = contents_index + len;
|
||||
result_slice_list[i] = result_contents[contents_index..new_index];
|
||||
contents_index = new_index;
|
||||
}
|
||||
|
||||
return result_slice_list;
|
||||
}
|
||||
|
||||
pub fn argsFree(allocator: &mem.Allocator, args_alloc: []const []u8) {
|
||||
var total_bytes: usize = 0;
|
||||
for (args_alloc) |arg| {
|
||||
total_bytes += @sizeOf([]u8) + arg.len;
|
||||
}
|
||||
const unaligned_allocated_buf = @ptrCast(&u8, args_alloc.ptr)[0..total_bytes];
|
||||
const aligned_allocated_buf = @alignCast(@alignOf([]u8), unaligned_allocated_buf);
|
||||
return allocator.free(aligned_allocated_buf);
|
||||
}
|
||||
|
||||
test "windows arg parsing" {
|
||||
testWindowsCmdLine(c"a b\tc d", [][]const u8{"a", "b", "c", "d"});
|
||||
testWindowsCmdLine(c"\"abc\" d e", [][]const u8{"abc", "d", "e"});
|
||||
@ -1494,6 +1543,39 @@ pub fn openSelfExe() -> %io.File {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the directory path that contains the current executable.
|
||||
/// Caller owns returned memory.
|
||||
pub fn selfExeDirPath(allocator: &mem.Allocator) -> %[]u8 {
|
||||
switch (builtin.os) {
|
||||
Os.linux => {
|
||||
// If the currently executing binary has been deleted,
|
||||
// the file path looks something like `/a/b/c/exe (deleted)`
|
||||
// This path cannot be opened, but it's valid for determining the directory
|
||||
// the executable was in when it was run.
|
||||
const full_exe_path = %return readLink(allocator, "/proc/self/exe");
|
||||
%defer allocator.free(full_exe_path);
|
||||
const dir = path.dirname(full_exe_path);
|
||||
return allocator.shrink(u8, full_exe_path, dir.len);
|
||||
},
|
||||
Os.windows => {
|
||||
@panic("TODO windows std.os.selfExeDirPath");
|
||||
//buf_resize(out_path, 256);
|
||||
//for (;;) {
|
||||
// DWORD copied_amt = GetModuleFileName(nullptr, buf_ptr(out_path), buf_len(out_path));
|
||||
// if (copied_amt <= 0) {
|
||||
// return ErrorFileNotFound;
|
||||
// }
|
||||
// if (copied_amt < buf_len(out_path)) {
|
||||
// buf_resize(out_path, copied_amt);
|
||||
// return 0;
|
||||
// }
|
||||
// buf_resize(out_path, buf_len(out_path) * 2);
|
||||
//}
|
||||
},
|
||||
else => @compileError("unimplemented: std.os.selfExeDirPath for " ++ @tagName(builtin.os)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn isTty(handle: FileHandle) -> bool {
|
||||
if (is_windows) {
|
||||
return windows_util.windowsIsTty(handle);
|
||||
|
||||
158
std/os/linux.zig
158
std/os/linux.zig
@ -367,14 +367,14 @@ pub const TFD_CLOEXEC = O_CLOEXEC;
|
||||
pub const TFD_TIMER_ABSTIME = 1;
|
||||
pub const TFD_TIMER_CANCEL_ON_SET = (1 << 1);
|
||||
|
||||
fn unsigned(s: i32) -> u32 { @bitCast(u32, s) }
|
||||
fn signed(s: u32) -> i32 { @bitCast(i32, s) }
|
||||
pub fn WEXITSTATUS(s: i32) -> i32 { signed((unsigned(s) & 0xff00) >> 8) }
|
||||
pub fn WTERMSIG(s: i32) -> i32 { signed(unsigned(s) & 0x7f) }
|
||||
pub fn WSTOPSIG(s: i32) -> i32 { WEXITSTATUS(s) }
|
||||
pub fn WIFEXITED(s: i32) -> bool { WTERMSIG(s) == 0 }
|
||||
pub fn WIFSTOPPED(s: i32) -> bool { (u16)(((unsigned(s)&0xffff)*%0x10001)>>8) > 0x7f00 }
|
||||
pub fn WIFSIGNALED(s: i32) -> bool { (unsigned(s)&0xffff)-%1 < 0xff }
|
||||
fn unsigned(s: i32) -> u32 { return @bitCast(u32, s); }
|
||||
fn signed(s: u32) -> i32 { return @bitCast(i32, s); }
|
||||
pub fn WEXITSTATUS(s: i32) -> i32 { return signed((unsigned(s) & 0xff00) >> 8); }
|
||||
pub fn WTERMSIG(s: i32) -> i32 { return signed(unsigned(s) & 0x7f); }
|
||||
pub fn WSTOPSIG(s: i32) -> i32 { return WEXITSTATUS(s); }
|
||||
pub fn WIFEXITED(s: i32) -> bool { return WTERMSIG(s) == 0; }
|
||||
pub fn WIFSTOPPED(s: i32) -> bool { return (u16)(((unsigned(s)&0xffff)*%0x10001)>>8) > 0x7f00; }
|
||||
pub fn WIFSIGNALED(s: i32) -> bool { return (unsigned(s)&0xffff)-%1 < 0xff; }
|
||||
|
||||
|
||||
pub const winsize = extern struct {
|
||||
@ -387,31 +387,31 @@ pub const winsize = extern struct {
|
||||
/// Get the errno from a syscall return value, or 0 for no error.
|
||||
pub fn getErrno(r: usize) -> usize {
|
||||
const signed_r = @bitCast(isize, r);
|
||||
if (signed_r > -4096 and signed_r < 0) usize(-signed_r) else 0
|
||||
return if (signed_r > -4096 and signed_r < 0) usize(-signed_r) else 0;
|
||||
}
|
||||
|
||||
pub fn dup2(old: i32, new: i32) -> usize {
|
||||
arch.syscall2(arch.SYS_dup2, usize(old), usize(new))
|
||||
return arch.syscall2(arch.SYS_dup2, usize(old), usize(new));
|
||||
}
|
||||
|
||||
pub fn chdir(path: &const u8) -> usize {
|
||||
arch.syscall1(arch.SYS_chdir, @ptrToInt(path))
|
||||
return arch.syscall1(arch.SYS_chdir, @ptrToInt(path));
|
||||
}
|
||||
|
||||
pub fn execve(path: &const u8, argv: &const ?&const u8, envp: &const ?&const u8) -> usize {
|
||||
arch.syscall3(arch.SYS_execve, @ptrToInt(path), @ptrToInt(argv), @ptrToInt(envp))
|
||||
return arch.syscall3(arch.SYS_execve, @ptrToInt(path), @ptrToInt(argv), @ptrToInt(envp));
|
||||
}
|
||||
|
||||
pub fn fork() -> usize {
|
||||
arch.syscall0(arch.SYS_fork)
|
||||
return arch.syscall0(arch.SYS_fork);
|
||||
}
|
||||
|
||||
pub fn getcwd(buf: &u8, size: usize) -> usize {
|
||||
arch.syscall2(arch.SYS_getcwd, @ptrToInt(buf), size)
|
||||
return arch.syscall2(arch.SYS_getcwd, @ptrToInt(buf), size);
|
||||
}
|
||||
|
||||
pub fn getdents(fd: i32, dirp: &u8, count: usize) -> usize {
|
||||
arch.syscall3(arch.SYS_getdents, usize(fd), @ptrToInt(dirp), count)
|
||||
return arch.syscall3(arch.SYS_getdents, usize(fd), @ptrToInt(dirp), count);
|
||||
}
|
||||
|
||||
pub fn isatty(fd: i32) -> bool {
|
||||
@ -420,123 +420,123 @@ pub fn isatty(fd: i32) -> bool {
|
||||
}
|
||||
|
||||
pub fn readlink(noalias path: &const u8, noalias buf_ptr: &u8, buf_len: usize) -> usize {
|
||||
arch.syscall3(arch.SYS_readlink, @ptrToInt(path), @ptrToInt(buf_ptr), buf_len)
|
||||
return arch.syscall3(arch.SYS_readlink, @ptrToInt(path), @ptrToInt(buf_ptr), buf_len);
|
||||
}
|
||||
|
||||
pub fn mkdir(path: &const u8, mode: u32) -> usize {
|
||||
arch.syscall2(arch.SYS_mkdir, @ptrToInt(path), mode)
|
||||
return arch.syscall2(arch.SYS_mkdir, @ptrToInt(path), mode);
|
||||
}
|
||||
|
||||
pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32, offset: isize)
|
||||
-> usize
|
||||
{
|
||||
arch.syscall6(arch.SYS_mmap, @ptrToInt(address), length, prot, flags, usize(fd),
|
||||
@bitCast(usize, offset))
|
||||
return arch.syscall6(arch.SYS_mmap, @ptrToInt(address), length, prot, flags, usize(fd),
|
||||
@bitCast(usize, offset));
|
||||
}
|
||||
|
||||
pub fn munmap(address: &u8, length: usize) -> usize {
|
||||
arch.syscall2(arch.SYS_munmap, @ptrToInt(address), length)
|
||||
return arch.syscall2(arch.SYS_munmap, @ptrToInt(address), length);
|
||||
}
|
||||
|
||||
pub fn read(fd: i32, buf: &u8, count: usize) -> usize {
|
||||
arch.syscall3(arch.SYS_read, usize(fd), @ptrToInt(buf), count)
|
||||
return arch.syscall3(arch.SYS_read, usize(fd), @ptrToInt(buf), count);
|
||||
}
|
||||
|
||||
pub fn rmdir(path: &const u8) -> usize {
|
||||
arch.syscall1(arch.SYS_rmdir, @ptrToInt(path))
|
||||
return arch.syscall1(arch.SYS_rmdir, @ptrToInt(path));
|
||||
}
|
||||
|
||||
pub fn symlink(existing: &const u8, new: &const u8) -> usize {
|
||||
arch.syscall2(arch.SYS_symlink, @ptrToInt(existing), @ptrToInt(new))
|
||||
return arch.syscall2(arch.SYS_symlink, @ptrToInt(existing), @ptrToInt(new));
|
||||
}
|
||||
|
||||
pub fn pread(fd: i32, buf: &u8, count: usize, offset: usize) -> usize {
|
||||
arch.syscall4(arch.SYS_pread, usize(fd), @ptrToInt(buf), count, offset)
|
||||
return arch.syscall4(arch.SYS_pread, usize(fd), @ptrToInt(buf), count, offset);
|
||||
}
|
||||
|
||||
pub fn pipe(fd: &[2]i32) -> usize {
|
||||
pipe2(fd, 0)
|
||||
return pipe2(fd, 0);
|
||||
}
|
||||
|
||||
pub fn pipe2(fd: &[2]i32, flags: usize) -> usize {
|
||||
arch.syscall2(arch.SYS_pipe2, @ptrToInt(fd), flags)
|
||||
return arch.syscall2(arch.SYS_pipe2, @ptrToInt(fd), flags);
|
||||
}
|
||||
|
||||
pub fn write(fd: i32, buf: &const u8, count: usize) -> usize {
|
||||
arch.syscall3(arch.SYS_write, usize(fd), @ptrToInt(buf), count)
|
||||
return arch.syscall3(arch.SYS_write, usize(fd), @ptrToInt(buf), count);
|
||||
}
|
||||
|
||||
pub fn pwrite(fd: i32, buf: &const u8, count: usize, offset: usize) -> usize {
|
||||
arch.syscall4(arch.SYS_pwrite, usize(fd), @ptrToInt(buf), count, offset)
|
||||
return arch.syscall4(arch.SYS_pwrite, usize(fd), @ptrToInt(buf), count, offset);
|
||||
}
|
||||
|
||||
pub fn rename(old: &const u8, new: &const u8) -> usize {
|
||||
arch.syscall2(arch.SYS_rename, @ptrToInt(old), @ptrToInt(new))
|
||||
return arch.syscall2(arch.SYS_rename, @ptrToInt(old), @ptrToInt(new));
|
||||
}
|
||||
|
||||
pub fn open(path: &const u8, flags: u32, perm: usize) -> usize {
|
||||
arch.syscall3(arch.SYS_open, @ptrToInt(path), flags, perm)
|
||||
return arch.syscall3(arch.SYS_open, @ptrToInt(path), flags, perm);
|
||||
}
|
||||
|
||||
pub fn create(path: &const u8, perm: usize) -> usize {
|
||||
arch.syscall2(arch.SYS_creat, @ptrToInt(path), perm)
|
||||
return arch.syscall2(arch.SYS_creat, @ptrToInt(path), perm);
|
||||
}
|
||||
|
||||
pub fn openat(dirfd: i32, path: &const u8, flags: usize, mode: usize) -> usize {
|
||||
arch.syscall4(arch.SYS_openat, usize(dirfd), @ptrToInt(path), flags, mode)
|
||||
return arch.syscall4(arch.SYS_openat, usize(dirfd), @ptrToInt(path), flags, mode);
|
||||
}
|
||||
|
||||
pub fn close(fd: i32) -> usize {
|
||||
arch.syscall1(arch.SYS_close, usize(fd))
|
||||
return arch.syscall1(arch.SYS_close, usize(fd));
|
||||
}
|
||||
|
||||
pub fn lseek(fd: i32, offset: isize, ref_pos: usize) -> usize {
|
||||
arch.syscall3(arch.SYS_lseek, usize(fd), @bitCast(usize, offset), ref_pos)
|
||||
return arch.syscall3(arch.SYS_lseek, usize(fd), @bitCast(usize, offset), ref_pos);
|
||||
}
|
||||
|
||||
pub fn exit(status: i32) -> noreturn {
|
||||
_ = arch.syscall1(arch.SYS_exit, @bitCast(usize, isize(status)));
|
||||
unreachable
|
||||
unreachable;
|
||||
}
|
||||
|
||||
pub fn getrandom(buf: &u8, count: usize, flags: u32) -> usize {
|
||||
arch.syscall3(arch.SYS_getrandom, @ptrToInt(buf), count, usize(flags))
|
||||
return arch.syscall3(arch.SYS_getrandom, @ptrToInt(buf), count, usize(flags));
|
||||
}
|
||||
|
||||
pub fn kill(pid: i32, sig: i32) -> usize {
|
||||
arch.syscall2(arch.SYS_kill, @bitCast(usize, isize(pid)), usize(sig))
|
||||
return arch.syscall2(arch.SYS_kill, @bitCast(usize, isize(pid)), usize(sig));
|
||||
}
|
||||
|
||||
pub fn unlink(path: &const u8) -> usize {
|
||||
arch.syscall1(arch.SYS_unlink, @ptrToInt(path))
|
||||
return arch.syscall1(arch.SYS_unlink, @ptrToInt(path));
|
||||
}
|
||||
|
||||
pub fn waitpid(pid: i32, status: &i32, options: i32) -> usize {
|
||||
arch.syscall4(arch.SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0)
|
||||
return arch.syscall4(arch.SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0);
|
||||
}
|
||||
|
||||
pub fn nanosleep(req: &const timespec, rem: ?×pec) -> usize {
|
||||
arch.syscall2(arch.SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem))
|
||||
return arch.syscall2(arch.SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem));
|
||||
}
|
||||
|
||||
pub fn setuid(uid: u32) -> usize {
|
||||
arch.syscall1(arch.SYS_setuid, uid)
|
||||
return arch.syscall1(arch.SYS_setuid, uid);
|
||||
}
|
||||
|
||||
pub fn setgid(gid: u32) -> usize {
|
||||
arch.syscall1(arch.SYS_setgid, gid)
|
||||
return arch.syscall1(arch.SYS_setgid, gid);
|
||||
}
|
||||
|
||||
pub fn setreuid(ruid: u32, euid: u32) -> usize {
|
||||
arch.syscall2(arch.SYS_setreuid, ruid, euid)
|
||||
return arch.syscall2(arch.SYS_setreuid, ruid, euid);
|
||||
}
|
||||
|
||||
pub fn setregid(rgid: u32, egid: u32) -> usize {
|
||||
arch.syscall2(arch.SYS_setregid, rgid, egid)
|
||||
return arch.syscall2(arch.SYS_setregid, rgid, egid);
|
||||
}
|
||||
|
||||
pub fn sigprocmask(flags: u32, noalias set: &const sigset_t, noalias oldset: ?&sigset_t) -> usize {
|
||||
arch.syscall4(arch.SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG/8)
|
||||
return arch.syscall4(arch.SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG/8);
|
||||
}
|
||||
|
||||
pub fn sigaction(sig: u6, noalias act: &const Sigaction, noalias oact: ?&Sigaction) -> usize {
|
||||
@ -651,92 +651,70 @@ pub const iovec = extern struct {
|
||||
iov_len: usize,
|
||||
};
|
||||
|
||||
//
|
||||
//const IF_NAMESIZE = 16;
|
||||
//
|
||||
//export struct ifreq {
|
||||
// ifrn_name: [IF_NAMESIZE]u8,
|
||||
// union {
|
||||
// ifru_addr: sockaddr,
|
||||
// ifru_dstaddr: sockaddr,
|
||||
// ifru_broadaddr: sockaddr,
|
||||
// ifru_netmask: sockaddr,
|
||||
// ifru_hwaddr: sockaddr,
|
||||
// ifru_flags: i16,
|
||||
// ifru_ivalue: i32,
|
||||
// ifru_mtu: i32,
|
||||
// ifru_map: ifmap,
|
||||
// ifru_slave: [IF_NAMESIZE]u8,
|
||||
// ifru_newname: [IF_NAMESIZE]u8,
|
||||
// ifru_data: &u8,
|
||||
// } ifr_ifru;
|
||||
//}
|
||||
//
|
||||
|
||||
pub fn getsockname(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t) -> usize {
|
||||
arch.syscall3(arch.SYS_getsockname, usize(fd), @ptrToInt(addr), @ptrToInt(len))
|
||||
return arch.syscall3(arch.SYS_getsockname, usize(fd), @ptrToInt(addr), @ptrToInt(len));
|
||||
}
|
||||
|
||||
pub fn getpeername(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t) -> usize {
|
||||
arch.syscall3(arch.SYS_getpeername, usize(fd), @ptrToInt(addr), @ptrToInt(len))
|
||||
return arch.syscall3(arch.SYS_getpeername, usize(fd), @ptrToInt(addr), @ptrToInt(len));
|
||||
}
|
||||
|
||||
pub fn socket(domain: i32, socket_type: i32, protocol: i32) -> usize {
|
||||
arch.syscall3(arch.SYS_socket, usize(domain), usize(socket_type), usize(protocol))
|
||||
return arch.syscall3(arch.SYS_socket, usize(domain), usize(socket_type), usize(protocol));
|
||||
}
|
||||
|
||||
pub fn setsockopt(fd: i32, level: i32, optname: i32, optval: &const u8, optlen: socklen_t) -> usize {
|
||||
arch.syscall5(arch.SYS_setsockopt, usize(fd), usize(level), usize(optname), usize(optval), @ptrToInt(optlen))
|
||||
return arch.syscall5(arch.SYS_setsockopt, usize(fd), usize(level), usize(optname), usize(optval), @ptrToInt(optlen));
|
||||
}
|
||||
|
||||
pub fn getsockopt(fd: i32, level: i32, optname: i32, noalias optval: &u8, noalias optlen: &socklen_t) -> usize {
|
||||
arch.syscall5(arch.SYS_getsockopt, usize(fd), usize(level), usize(optname), @ptrToInt(optval), @ptrToInt(optlen))
|
||||
return arch.syscall5(arch.SYS_getsockopt, usize(fd), usize(level), usize(optname), @ptrToInt(optval), @ptrToInt(optlen));
|
||||
}
|
||||
|
||||
pub fn sendmsg(fd: i32, msg: &const arch.msghdr, flags: u32) -> usize {
|
||||
arch.syscall3(arch.SYS_sendmsg, usize(fd), @ptrToInt(msg), flags)
|
||||
return arch.syscall3(arch.SYS_sendmsg, usize(fd), @ptrToInt(msg), flags);
|
||||
}
|
||||
|
||||
pub fn connect(fd: i32, addr: &const sockaddr, len: socklen_t) -> usize {
|
||||
arch.syscall3(arch.SYS_connect, usize(fd), @ptrToInt(addr), usize(len))
|
||||
return arch.syscall3(arch.SYS_connect, usize(fd), @ptrToInt(addr), usize(len));
|
||||
}
|
||||
|
||||
pub fn recvmsg(fd: i32, msg: &arch.msghdr, flags: u32) -> usize {
|
||||
arch.syscall3(arch.SYS_recvmsg, usize(fd), @ptrToInt(msg), flags)
|
||||
return arch.syscall3(arch.SYS_recvmsg, usize(fd), @ptrToInt(msg), flags);
|
||||
}
|
||||
|
||||
pub fn recvfrom(fd: i32, noalias buf: &u8, len: usize, flags: u32,
|
||||
noalias addr: ?&sockaddr, noalias alen: ?&socklen_t) -> usize
|
||||
{
|
||||
arch.syscall6(arch.SYS_recvfrom, usize(fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen))
|
||||
return arch.syscall6(arch.SYS_recvfrom, usize(fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen));
|
||||
}
|
||||
|
||||
pub fn shutdown(fd: i32, how: i32) -> usize {
|
||||
arch.syscall2(arch.SYS_shutdown, usize(fd), usize(how))
|
||||
return arch.syscall2(arch.SYS_shutdown, usize(fd), usize(how));
|
||||
}
|
||||
|
||||
pub fn bind(fd: i32, addr: &const sockaddr, len: socklen_t) -> usize {
|
||||
arch.syscall3(arch.SYS_bind, usize(fd), @ptrToInt(addr), usize(len))
|
||||
return arch.syscall3(arch.SYS_bind, usize(fd), @ptrToInt(addr), usize(len));
|
||||
}
|
||||
|
||||
pub fn listen(fd: i32, backlog: i32) -> usize {
|
||||
arch.syscall2(arch.SYS_listen, usize(fd), usize(backlog))
|
||||
return arch.syscall2(arch.SYS_listen, usize(fd), usize(backlog));
|
||||
}
|
||||
|
||||
pub fn sendto(fd: i32, buf: &const u8, len: usize, flags: u32, addr: ?&const sockaddr, alen: socklen_t) -> usize {
|
||||
arch.syscall6(arch.SYS_sendto, usize(fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), usize(alen))
|
||||
return arch.syscall6(arch.SYS_sendto, usize(fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), usize(alen));
|
||||
}
|
||||
|
||||
pub fn socketpair(domain: i32, socket_type: i32, protocol: i32, fd: [2]i32) -> usize {
|
||||
arch.syscall4(arch.SYS_socketpair, usize(domain), usize(socket_type), usize(protocol), @ptrToInt(&fd[0]))
|
||||
return arch.syscall4(arch.SYS_socketpair, usize(domain), usize(socket_type), usize(protocol), @ptrToInt(&fd[0]));
|
||||
}
|
||||
|
||||
pub fn accept(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t) -> usize {
|
||||
accept4(fd, addr, len, 0)
|
||||
return accept4(fd, addr, len, 0);
|
||||
}
|
||||
|
||||
pub fn accept4(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t, flags: u32) -> usize {
|
||||
arch.syscall4(arch.SYS_accept4, usize(fd), @ptrToInt(addr), @ptrToInt(len), flags)
|
||||
return arch.syscall4(arch.SYS_accept4, usize(fd), @ptrToInt(addr), @ptrToInt(len), flags);
|
||||
}
|
||||
|
||||
// error NameTooLong;
|
||||
@ -771,7 +749,7 @@ pub const Stat = arch.Stat;
|
||||
pub const timespec = arch.timespec;
|
||||
|
||||
pub fn fstat(fd: i32, stat_buf: &Stat) -> usize {
|
||||
arch.syscall2(arch.SYS_fstat, usize(fd), @ptrToInt(stat_buf))
|
||||
return arch.syscall2(arch.SYS_fstat, usize(fd), @ptrToInt(stat_buf));
|
||||
}
|
||||
|
||||
pub const epoll_data = u64;
|
||||
@ -782,19 +760,19 @@ pub const epoll_event = extern struct {
|
||||
};
|
||||
|
||||
pub fn epoll_create() -> usize {
|
||||
arch.syscall1(arch.SYS_epoll_create, usize(1))
|
||||
return arch.syscall1(arch.SYS_epoll_create, usize(1));
|
||||
}
|
||||
|
||||
pub fn epoll_ctl(epoll_fd: i32, op: i32, fd: i32, ev: &epoll_event) -> usize {
|
||||
arch.syscall4(arch.SYS_epoll_ctl, usize(epoll_fd), usize(op), usize(fd), @ptrToInt(ev))
|
||||
return arch.syscall4(arch.SYS_epoll_ctl, usize(epoll_fd), usize(op), usize(fd), @ptrToInt(ev));
|
||||
}
|
||||
|
||||
pub fn epoll_wait(epoll_fd: i32, events: &epoll_event, maxevents: i32, timeout: i32) -> usize {
|
||||
arch.syscall4(arch.SYS_epoll_wait, usize(epoll_fd), @ptrToInt(events), usize(maxevents), usize(timeout))
|
||||
return arch.syscall4(arch.SYS_epoll_wait, usize(epoll_fd), @ptrToInt(events), usize(maxevents), usize(timeout));
|
||||
}
|
||||
|
||||
pub fn timerfd_create(clockid: i32, flags: u32) -> usize {
|
||||
arch.syscall2(arch.SYS_timerfd_create, usize(clockid), usize(flags))
|
||||
return arch.syscall2(arch.SYS_timerfd_create, usize(clockid), usize(flags));
|
||||
}
|
||||
|
||||
pub const itimerspec = extern struct {
|
||||
@ -803,11 +781,11 @@ pub const itimerspec = extern struct {
|
||||
};
|
||||
|
||||
pub fn timerfd_gettime(fd: i32, curr_value: &itimerspec) -> usize {
|
||||
arch.syscall2(arch.SYS_timerfd_gettime, usize(fd), @ptrToInt(curr_value))
|
||||
return arch.syscall2(arch.SYS_timerfd_gettime, usize(fd), @ptrToInt(curr_value));
|
||||
}
|
||||
|
||||
pub fn timerfd_settime(fd: i32, flags: u32, new_value: &const itimerspec, old_value: ?&itimerspec) -> usize {
|
||||
arch.syscall4(arch.SYS_timerfd_settime, usize(fd), usize(flags), @ptrToInt(new_value), @ptrToInt(old_value))
|
||||
return arch.syscall4(arch.SYS_timerfd_settime, usize(fd), usize(flags), @ptrToInt(new_value), @ptrToInt(old_value));
|
||||
}
|
||||
|
||||
test "import linux_test" {
|
||||
|
||||
@ -502,13 +502,3 @@ pub nakedcc fn restore_rt() {
|
||||
: [number] "{eax}" (usize(SYS_rt_sigreturn))
|
||||
: "rcx", "r11")
|
||||
}
|
||||
|
||||
export struct msghdr {
|
||||
msg_name: &u8,
|
||||
msg_namelen: socklen_t,
|
||||
msg_iov: &iovec,
|
||||
msg_iovlen: i32,
|
||||
msg_control: &u8,
|
||||
msg_controllen: socklen_t,
|
||||
msg_flags: i32,
|
||||
}
|
||||
|
||||
@ -371,52 +371,52 @@ pub const F_GETOWN_EX = 16;
|
||||
pub const F_GETOWNER_UIDS = 17;
|
||||
|
||||
pub fn syscall0(number: usize) -> usize {
|
||||
asm volatile ("syscall"
|
||||
return asm volatile ("syscall"
|
||||
: [ret] "={rax}" (-> usize)
|
||||
: [number] "{rax}" (number)
|
||||
: "rcx", "r11")
|
||||
: "rcx", "r11");
|
||||
}
|
||||
|
||||
pub fn syscall1(number: usize, arg1: usize) -> usize {
|
||||
asm volatile ("syscall"
|
||||
return asm volatile ("syscall"
|
||||
: [ret] "={rax}" (-> usize)
|
||||
: [number] "{rax}" (number),
|
||||
[arg1] "{rdi}" (arg1)
|
||||
: "rcx", "r11")
|
||||
: "rcx", "r11");
|
||||
}
|
||||
|
||||
pub fn syscall2(number: usize, arg1: usize, arg2: usize) -> usize {
|
||||
asm volatile ("syscall"
|
||||
return asm volatile ("syscall"
|
||||
: [ret] "={rax}" (-> usize)
|
||||
: [number] "{rax}" (number),
|
||||
[arg1] "{rdi}" (arg1),
|
||||
[arg2] "{rsi}" (arg2)
|
||||
: "rcx", "r11")
|
||||
: "rcx", "r11");
|
||||
}
|
||||
|
||||
pub fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) -> usize {
|
||||
asm volatile ("syscall"
|
||||
return asm volatile ("syscall"
|
||||
: [ret] "={rax}" (-> usize)
|
||||
: [number] "{rax}" (number),
|
||||
[arg1] "{rdi}" (arg1),
|
||||
[arg2] "{rsi}" (arg2),
|
||||
[arg3] "{rdx}" (arg3)
|
||||
: "rcx", "r11")
|
||||
: "rcx", "r11");
|
||||
}
|
||||
|
||||
pub fn syscall4(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize) -> usize {
|
||||
asm volatile ("syscall"
|
||||
return asm volatile ("syscall"
|
||||
: [ret] "={rax}" (-> usize)
|
||||
: [number] "{rax}" (number),
|
||||
[arg1] "{rdi}" (arg1),
|
||||
[arg2] "{rsi}" (arg2),
|
||||
[arg3] "{rdx}" (arg3),
|
||||
[arg4] "{r10}" (arg4)
|
||||
: "rcx", "r11")
|
||||
: "rcx", "r11");
|
||||
}
|
||||
|
||||
pub fn syscall5(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) -> usize {
|
||||
asm volatile ("syscall"
|
||||
return asm volatile ("syscall"
|
||||
: [ret] "={rax}" (-> usize)
|
||||
: [number] "{rax}" (number),
|
||||
[arg1] "{rdi}" (arg1),
|
||||
@ -424,13 +424,13 @@ pub fn syscall5(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usiz
|
||||
[arg3] "{rdx}" (arg3),
|
||||
[arg4] "{r10}" (arg4),
|
||||
[arg5] "{r8}" (arg5)
|
||||
: "rcx", "r11")
|
||||
: "rcx", "r11");
|
||||
}
|
||||
|
||||
pub fn syscall6(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize,
|
||||
arg5: usize, arg6: usize) -> usize
|
||||
{
|
||||
asm volatile ("syscall"
|
||||
return asm volatile ("syscall"
|
||||
: [ret] "={rax}" (-> usize)
|
||||
: [number] "{rax}" (number),
|
||||
[arg1] "{rdi}" (arg1),
|
||||
@ -439,14 +439,14 @@ pub fn syscall6(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usiz
|
||||
[arg4] "{r10}" (arg4),
|
||||
[arg5] "{r8}" (arg5),
|
||||
[arg6] "{r9}" (arg6)
|
||||
: "rcx", "r11")
|
||||
: "rcx", "r11");
|
||||
}
|
||||
|
||||
pub nakedcc fn restore_rt() {
|
||||
asm volatile ("syscall"
|
||||
return asm volatile ("syscall"
|
||||
:
|
||||
: [number] "{rax}" (usize(SYS_rt_sigreturn))
|
||||
: "rcx", "r11")
|
||||
: "rcx", "r11");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -749,21 +749,19 @@ pub fn relativeWindows(allocator: &Allocator, from: []const u8, to: []const u8)
|
||||
const resolved_to = %return resolveWindows(allocator, [][]const u8{to});
|
||||
defer if (clean_up_resolved_to) allocator.free(resolved_to);
|
||||
|
||||
const result_is_to = if (drive(resolved_to)) |to_drive| {
|
||||
if (drive(resolved_from)) |from_drive| {
|
||||
const result_is_to = if (drive(resolved_to)) |to_drive|
|
||||
if (drive(resolved_from)) |from_drive|
|
||||
asciiUpper(from_drive[0]) != asciiUpper(to_drive[0])
|
||||
} else {
|
||||
else
|
||||
true
|
||||
}
|
||||
} else if (networkShare(resolved_to)) |to_ns| {
|
||||
if (networkShare(resolved_from)) |from_ns| {
|
||||
else if (networkShare(resolved_to)) |to_ns|
|
||||
if (networkShare(resolved_from)) |from_ns|
|
||||
!networkShareServersEql(to_ns, from_ns)
|
||||
} else {
|
||||
else
|
||||
true
|
||||
}
|
||||
} else {
|
||||
unreachable
|
||||
};
|
||||
else
|
||||
unreachable;
|
||||
|
||||
if (result_is_to) {
|
||||
clean_up_resolved_to = false;
|
||||
return resolved_to;
|
||||
@ -964,14 +962,16 @@ pub fn real(allocator: &Allocator, pathname: []const u8) -> %[]u8 {
|
||||
|
||||
// windows returns \\?\ prepended to the path
|
||||
// we strip it because nobody wants \\?\ prepended to their path
|
||||
const final_len = if (result > 4 and mem.startsWith(u8, buf, "\\\\?\\")) {
|
||||
var i: usize = 4;
|
||||
while (i < result) : (i += 1) {
|
||||
buf[i - 4] = buf[i];
|
||||
const final_len = x: {
|
||||
if (result > 4 and mem.startsWith(u8, buf, "\\\\?\\")) {
|
||||
var i: usize = 4;
|
||||
while (i < result) : (i += 1) {
|
||||
buf[i - 4] = buf[i];
|
||||
}
|
||||
break :x result - 4;
|
||||
} else {
|
||||
break :x result;
|
||||
}
|
||||
result - 4
|
||||
} else {
|
||||
result
|
||||
};
|
||||
|
||||
return allocator.shrink(u8, buf, final_len);
|
||||
@ -1012,7 +1012,7 @@ pub fn real(allocator: &Allocator, pathname: []const u8) -> %[]u8 {
|
||||
defer os.close(fd);
|
||||
|
||||
var buf: ["/proc/self/fd/-2147483648".len]u8 = undefined;
|
||||
const proc_path = fmt.bufPrint(buf[0..], "/proc/self/fd/{}", fd);
|
||||
const proc_path = %%fmt.bufPrint(buf[0..], "/proc/self/fd/{}", fd);
|
||||
|
||||
return os.readLink(allocator, proc_path);
|
||||
},
|
||||
|
||||
@ -16,11 +16,11 @@ pub fn windowsWaitSingle(handle: windows.HANDLE, milliseconds: windows.DWORD) ->
|
||||
windows.WAIT_ABANDONED => error.WaitAbandoned,
|
||||
windows.WAIT_OBJECT_0 => {},
|
||||
windows.WAIT_TIMEOUT => error.WaitTimeOut,
|
||||
windows.WAIT_FAILED => {
|
||||
windows.WAIT_FAILED => x: {
|
||||
const err = windows.GetLastError();
|
||||
switch (err) {
|
||||
break :x switch (err) {
|
||||
else => os.unexpectedErrorWindows(err),
|
||||
}
|
||||
};
|
||||
},
|
||||
else => error.Unexpected,
|
||||
};
|
||||
@ -122,7 +122,7 @@ pub fn windowsOpen(file_path: []const u8, desired_access: windows.DWORD, share_m
|
||||
/// Caller must free result.
|
||||
pub fn createWindowsEnvBlock(allocator: &mem.Allocator, env_map: &const BufMap) -> %[]u8 {
|
||||
// count bytes needed
|
||||
const bytes_needed = {
|
||||
const bytes_needed = x: {
|
||||
var bytes_needed: usize = 1; // 1 for the final null byte
|
||||
var it = env_map.iterator();
|
||||
while (it.next()) |pair| {
|
||||
@ -130,7 +130,7 @@ pub fn createWindowsEnvBlock(allocator: &mem.Allocator, env_map: &const BufMap)
|
||||
// +1 for null byte
|
||||
bytes_needed += pair.key.len + pair.value.len + 2;
|
||||
}
|
||||
bytes_needed
|
||||
break :x bytes_needed;
|
||||
};
|
||||
const result = %return allocator.alloc(u8, bytes_needed);
|
||||
%defer allocator.free(result);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user