Merge remote-tracking branch 'origin/master' into llvm6

This commit is contained in:
Andrew Kelley 2017-12-23 12:00:25 -05:00
commit fe66046283
163 changed files with 8300 additions and 3438 deletions

View File

@ -119,31 +119,22 @@ libc. Create demo games using Zig.
[![Build Status](https://travis-ci.org/zig-lang/zig.svg?branch=master)](https://travis-ci.org/zig-lang/zig)
[![Build status](https://ci.appveyor.com/api/projects/status/4t80mk2dmucrc38i/branch/master?svg=true)](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
View File

@ -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);
}
}

View File

@ -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) {

View File

@ -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) -&gt; 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) -&gt; []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) -&gt; []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("-&gt;" TypeExpr)
FnProto = option("coldcc" | "nakedcc" | "stdcallcc" | "extern") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("-&gt;" 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") "=&gt;" 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 = "!" | "-" | "~" | "*" | ("&amp;" 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)))

View File

@ -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
View 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
View 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
View 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;
}

View File

@ -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
View 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

File diff suppressed because it is too large Load Diff

View 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();
}

View 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];
}
};

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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:

View File

@ -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);
}

View File

@ -22,6 +22,9 @@ enum CTokId {
CTokIdRParen,
CTokIdEOF,
CTokIdDot,
CTokIdAsterisk,
CTokIdBang,
CTokIdTilde,
};
enum CNumLitSuffix {

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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");
}

View File

@ -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("-&gt;" 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;

View File

@ -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";

View File

@ -47,6 +47,7 @@ enum TokenId {
TokenIdFloatLiteral,
TokenIdIntLiteral,
TokenIdKeywordAlign,
TokenIdKeywordSection,
TokenIdKeywordAnd,
TokenIdKeywordAsm,
TokenIdKeywordBreak,

View File

@ -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);

View File

@ -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));
}

View File

@ -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,

View File

@ -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" {

View File

@ -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;

View File

@ -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);

View File

@ -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" {

View File

@ -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 {

View File

@ -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;

View File

@ -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 {

View File

@ -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);
}
};

View File

@ -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);
}
};

View File

@ -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 {

View File

@ -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 {

View File

@ -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"));
}
}

View File

@ -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;
}

View File

@ -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 {

View File

@ -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");

View File

@ -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);
}
};

View File

@ -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" {

View File

@ -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" {

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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" {

View File

@ -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));
}

View File

@ -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));
}
};

View File

@ -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" {

View File

@ -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" {

View File

@ -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;
}
}

View File

@ -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" {

View File

@ -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;
}
}

View File

@ -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" {

View File

@ -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);
}
}

View File

@ -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" {

View File

@ -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 {

View File

@ -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;
}

View File

@ -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" {

View File

@ -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;
}
}

View File

@ -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" {

View File

@ -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" {

View File

@ -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" {

View File

@ -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" {

View File

@ -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);
}

View File

@ -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)),
}
};
}

View File

@ -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));

View File

@ -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));

View File

@ -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" {

View File

@ -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));

View File

@ -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" {

View File

@ -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 => {

View File

@ -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" {

View File

@ -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" {

View File

@ -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" {

View File

@ -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" {

View File

@ -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)),
}
};
}

View File

@ -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" {

View File

@ -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;
}
}

View File

@ -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" {

View File

@ -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" {

View File

@ -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" {

View File

@ -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" {

View File

@ -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);
}

View File

@ -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" {

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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 }));
}

View File

@ -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);
}
}

View File

@ -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() {

View File

@ -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: ?&timespec) -> 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);
}

View File

@ -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);

View File

@ -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: ?&timespec) -> 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" {

View File

@ -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,
}

View File

@ -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");
}

View File

@ -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);
},

View File

@ -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