mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-05 15:18:49 +00:00
Merge remote-tracking branch 'upstream/master' into fix_odin_test
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
# These are supported funding model platforms
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: odin-lang
|
||||||
patreon: gingerbill
|
patreon: gingerbill
|
||||||
|
|||||||
@@ -38,6 +38,12 @@ jobs:
|
|||||||
cd tests/vendor
|
cd tests/vendor
|
||||||
make
|
make
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
|
- name: Odin check examples/all for Linux i386
|
||||||
|
run: ./odin check examples/all -vet -strict-style -target:linux_i386
|
||||||
|
timeout-minutes: 10
|
||||||
|
- name: Odin check examples/all for OpenBSD amd64
|
||||||
|
run: ./odin check examples/all -vet -strict-style -target:openbsd_amd64
|
||||||
|
timeout-minutes: 10
|
||||||
build_macOS:
|
build_macOS:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
steps:
|
steps:
|
||||||
@@ -78,6 +84,9 @@ jobs:
|
|||||||
cd tests/vendor
|
cd tests/vendor
|
||||||
make
|
make
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
|
- name: Odin check examples/all for Darwin arm64
|
||||||
|
run: ./odin check examples/all -vet -strict-style -target:darwin_arm64
|
||||||
|
timeout-minutes: 10
|
||||||
build_windows:
|
build_windows:
|
||||||
runs-on: windows-2019
|
runs-on: windows-2019
|
||||||
steps:
|
steps:
|
||||||
@@ -138,3 +147,9 @@ jobs:
|
|||||||
cd tests\core\math\big
|
cd tests\core\math\big
|
||||||
call build.bat
|
call build.bat
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
|
- name: Odin check examples/all for Windows 32bits
|
||||||
|
shell: cmd
|
||||||
|
run: |
|
||||||
|
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||||
|
odin check examples/all -strict-style -target:windows_i386
|
||||||
|
timeout-minutes: 10
|
||||||
|
|||||||
@@ -279,3 +279,5 @@ shared/
|
|||||||
*.ll
|
*.ll
|
||||||
|
|
||||||
*.sublime-workspace
|
*.sublime-workspace
|
||||||
|
examples/bug/
|
||||||
|
build.sh
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
GIT_SHA=$(shell git rev-parse --short HEAD)
|
GIT_SHA=$(shell git rev-parse --short HEAD)
|
||||||
DISABLED_WARNINGS=-Wno-switch -Wno-macro-redefined -Wno-unused-value
|
DISABLED_WARNINGS=-Wno-switch -Wno-macro-redefined -Wno-unused-value
|
||||||
LDFLAGS=-pthread -ldl -lm -lstdc++
|
LDFLAGS=-pthread -lm -lstdc++
|
||||||
CFLAGS=-std=c++14 -DGIT_SHA=\"$(GIT_SHA)\"
|
CFLAGS=-std=c++14 -DGIT_SHA=\"$(GIT_SHA)\"
|
||||||
CFLAGS:=$(CFLAGS) -DODIN_VERSION_RAW=\"dev-$(shell date +"%Y-%m")\"
|
CFLAGS:=$(CFLAGS) -DODIN_VERSION_RAW=\"dev-$(shell date +"%Y-%m")\"
|
||||||
CC=clang
|
CC=clang
|
||||||
@@ -10,7 +10,7 @@ OS=$(shell uname)
|
|||||||
ifeq ($(OS), Darwin)
|
ifeq ($(OS), Darwin)
|
||||||
|
|
||||||
ARCH=$(shell uname -m)
|
ARCH=$(shell uname -m)
|
||||||
LLVM_CONFIG=
|
LLVM_CONFIG=llvm-config
|
||||||
|
|
||||||
# allow for arm only llvm's with version 13
|
# allow for arm only llvm's with version 13
|
||||||
ifeq ($(ARCH), arm64)
|
ifeq ($(ARCH), arm64)
|
||||||
@@ -27,9 +27,7 @@ ifeq ($(OS), Darwin)
|
|||||||
LLMV_VERSION_PATTERN_REMOVE_SINGLE_STR = $(subst ",,$(LLVM_VERSION_PATTERN_REMOVE_ELEMENTS))
|
LLMV_VERSION_PATTERN_REMOVE_SINGLE_STR = $(subst ",,$(LLVM_VERSION_PATTERN_REMOVE_ELEMENTS))
|
||||||
LLVM_VERSION_PATTERN = "^(($(LLMV_VERSION_PATTERN_REMOVE_SINGLE_STR)))"
|
LLVM_VERSION_PATTERN = "^(($(LLMV_VERSION_PATTERN_REMOVE_SINGLE_STR)))"
|
||||||
|
|
||||||
ifneq ($(shell llvm-config --version | grep -E $(LLVM_VERSION_PATTERN)),)
|
ifeq ($(shell $(LLVM_CONFIG) --version | grep -E $(LLVM_VERSION_PATTERN)),)
|
||||||
LLVM_CONFIG=llvm-config
|
|
||||||
else
|
|
||||||
ifeq ($(ARCH), arm64)
|
ifeq ($(ARCH), arm64)
|
||||||
$(error "Requirement: llvm-config must be base version 13 for arm64")
|
$(error "Requirement: llvm-config must be base version 13 for arm64")
|
||||||
else
|
else
|
||||||
@@ -37,7 +35,7 @@ ifeq ($(OS), Darwin)
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
LDFLAGS:=$(LDFLAGS) -liconv
|
LDFLAGS:=$(LDFLAGS) -liconv -ldl
|
||||||
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
|
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
|
||||||
LDFLAGS:=$(LDFLAGS) -lLLVM-C
|
LDFLAGS:=$(LDFLAGS) -lLLVM-C
|
||||||
endif
|
endif
|
||||||
@@ -48,13 +46,19 @@ ifeq ($(OS), Linux)
|
|||||||
else ifneq ($(shell which llvm-config-11-64 2>/dev/null),)
|
else ifneq ($(shell which llvm-config-11-64 2>/dev/null),)
|
||||||
LLVM_CONFIG=llvm-config-11-64
|
LLVM_CONFIG=llvm-config-11-64
|
||||||
else
|
else
|
||||||
ifneq ($(shell llvm-config --version | grep '^11\.'),)
|
ifeq ($(shell $(LLVM_CONFIG) --version | grep '^11\.'),)
|
||||||
LLVM_CONFIG=llvm-config
|
|
||||||
else
|
|
||||||
$(error "Requirement: llvm-config must be version 11")
|
$(error "Requirement: llvm-config must be version 11")
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
LDFLAGS:=$(LDFLAGS) -ldl
|
||||||
|
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
|
||||||
|
LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
|
||||||
|
endif
|
||||||
|
ifeq ($(OS), OpenBSD)
|
||||||
|
LLVM_CONFIG=/usr/local/bin/llvm-config
|
||||||
|
|
||||||
|
LDFLAGS:=$(LDFLAGS) -liconv
|
||||||
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
|
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
|
||||||
LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
|
LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
|
||||||
endif
|
endif
|
||||||
|
|||||||
@@ -27,6 +27,19 @@ when ODIN_OS == .Linux || ODIN_OS == .FreeBSD {
|
|||||||
ERANGE :: 34
|
ERANGE :: 34
|
||||||
}
|
}
|
||||||
|
|
||||||
|
when ODIN_OS == .OpenBSD {
|
||||||
|
@(private="file")
|
||||||
|
@(default_calling_convention="c")
|
||||||
|
foreign libc {
|
||||||
|
@(link_name="__errno")
|
||||||
|
_get_errno :: proc() -> ^int ---
|
||||||
|
}
|
||||||
|
|
||||||
|
EDOM :: 33
|
||||||
|
EILSEQ :: 84
|
||||||
|
ERANGE :: 34
|
||||||
|
}
|
||||||
|
|
||||||
when ODIN_OS == .Windows {
|
when ODIN_OS == .Windows {
|
||||||
@(private="file")
|
@(private="file")
|
||||||
@(default_calling_convention="c")
|
@(default_calling_convention="c")
|
||||||
|
|||||||
@@ -78,6 +78,31 @@ when ODIN_OS == .Linux {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
when ODIN_OS == .OpenBSD {
|
||||||
|
fpos_t :: i64
|
||||||
|
|
||||||
|
_IOFBF :: 0
|
||||||
|
_IOLBF :: 1
|
||||||
|
_IONBF :: 1
|
||||||
|
|
||||||
|
BUFSIZ :: 1024
|
||||||
|
|
||||||
|
EOF :: int(-1)
|
||||||
|
|
||||||
|
FOPEN_MAX :: 20
|
||||||
|
FILENAME_MAX :: 1024
|
||||||
|
|
||||||
|
SEEK_SET :: 0
|
||||||
|
SEEK_CUR :: 1
|
||||||
|
SEEK_END :: 2
|
||||||
|
|
||||||
|
foreign libc {
|
||||||
|
stderr: ^FILE
|
||||||
|
stdin: ^FILE
|
||||||
|
stdout: ^FILE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
when ODIN_OS == .Darwin {
|
when ODIN_OS == .Darwin {
|
||||||
fpos_t :: distinct i64
|
fpos_t :: distinct i64
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ when ODIN_OS == .Windows {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin {
|
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD {
|
||||||
@(default_calling_convention="c")
|
@(default_calling_convention="c")
|
||||||
foreign libc {
|
foreign libc {
|
||||||
// 7.27.2 Time manipulation functions
|
// 7.27.2 Time manipulation functions
|
||||||
@@ -63,7 +63,12 @@ when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin {
|
|||||||
strftime :: proc(s: [^]char, maxsize: size_t, format: cstring, timeptr: ^tm) -> size_t ---
|
strftime :: proc(s: [^]char, maxsize: size_t, format: cstring, timeptr: ^tm) -> size_t ---
|
||||||
}
|
}
|
||||||
|
|
||||||
CLOCKS_PER_SEC :: 1000000
|
when ODIN_OS == .OpenBSD {
|
||||||
|
CLOCKS_PER_SEC :: 100
|
||||||
|
} else {
|
||||||
|
CLOCKS_PER_SEC :: 1000000
|
||||||
|
}
|
||||||
|
|
||||||
TIME_UTC :: 1
|
TIME_UTC :: 1
|
||||||
|
|
||||||
time_t :: distinct i64
|
time_t :: distinct i64
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ when ODIN_OS == .Darwin {
|
|||||||
wctype_t :: distinct u32
|
wctype_t :: distinct u32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
when ODIN_OS == .OpenBSD {
|
||||||
|
wctrans_t :: distinct rawptr
|
||||||
|
wctype_t :: distinct rawptr
|
||||||
|
}
|
||||||
|
|
||||||
@(default_calling_convention="c")
|
@(default_calling_convention="c")
|
||||||
foreign libc {
|
foreign libc {
|
||||||
// 7.30.2.1 Wide character classification functions
|
// 7.30.2.1 Wide character classification functions
|
||||||
|
|||||||
@@ -139,7 +139,13 @@ Context_Memory_Input :: struct #packed {
|
|||||||
size_packed: i64,
|
size_packed: i64,
|
||||||
size_unpacked: i64,
|
size_unpacked: i64,
|
||||||
}
|
}
|
||||||
#assert(size_of(Context_Memory_Input) == 64)
|
when size_of(rawptr) == 8 {
|
||||||
|
#assert(size_of(Context_Memory_Input) == 64)
|
||||||
|
} else {
|
||||||
|
// e.g. `-target:windows_i386`
|
||||||
|
#assert(size_of(Context_Memory_Input) == 52)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Context_Stream_Input :: struct #packed {
|
Context_Stream_Input :: struct #packed {
|
||||||
input_data: []u8,
|
input_data: []u8,
|
||||||
@@ -473,4 +479,4 @@ discard_to_next_byte_lsb_from_stream :: proc(z: ^Context_Stream_Input) {
|
|||||||
consume_bits_lsb(z, discard)
|
consume_bits_lsb(z, discard)
|
||||||
}
|
}
|
||||||
|
|
||||||
discard_to_next_byte_lsb :: proc{discard_to_next_byte_lsb_from_memory, discard_to_next_byte_lsb_from_stream};
|
discard_to_next_byte_lsb :: proc{discard_to_next_byte_lsb_from_memory, discard_to_next_byte_lsb_from_stream}
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ E_GZIP :: compress.GZIP_Error
|
|||||||
E_ZLIB :: compress.ZLIB_Error
|
E_ZLIB :: compress.ZLIB_Error
|
||||||
E_Deflate :: compress.Deflate_Error
|
E_Deflate :: compress.Deflate_Error
|
||||||
|
|
||||||
GZIP_MAX_PAYLOAD_SIZE :: int(max(u32le))
|
GZIP_MAX_PAYLOAD_SIZE :: i64(max(u32le))
|
||||||
|
|
||||||
load :: proc{load_from_slice, load_from_file, load_from_context}
|
load :: proc{load_from_slice, load_from_file, load_from_context}
|
||||||
|
|
||||||
@@ -136,7 +136,7 @@ load_from_context :: proc(z: ^$C, buf: ^bytes.Buffer, known_gzip_size := -1, exp
|
|||||||
|
|
||||||
z.output = buf
|
z.output = buf
|
||||||
|
|
||||||
if expected_output_size > GZIP_MAX_PAYLOAD_SIZE {
|
if i64(expected_output_size) > i64(GZIP_MAX_PAYLOAD_SIZE) {
|
||||||
return E_GZIP.Payload_Size_Exceeds_Max_Payload
|
return E_GZIP.Payload_Size_Exceeds_Max_Payload
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -677,4 +677,4 @@ inflate_from_byte_array_raw :: proc(input: []u8, buf: ^bytes.Buffer, raw := fals
|
|||||||
return inflate_raw(z=&ctx, expected_output_size=expected_output_size)
|
return inflate_raw(z=&ctx, expected_output_size=expected_output_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
inflate :: proc{inflate_from_context, inflate_from_byte_array};
|
inflate :: proc{inflate_from_context, inflate_from_byte_array}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package dynamic_bit_array
|
package dynamic_bit_array
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "core:intrinsics"
|
||||||
|
import "core:mem"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Note that these constants are dependent on the backing being a u64.
|
Note that these constants are dependent on the backing being a u64.
|
||||||
@@ -15,15 +16,16 @@ INDEX_MASK :: 63
|
|||||||
NUM_BITS :: 64
|
NUM_BITS :: 64
|
||||||
|
|
||||||
Bit_Array :: struct {
|
Bit_Array :: struct {
|
||||||
bits: [dynamic]u64,
|
bits: [dynamic]u64,
|
||||||
bias: int,
|
bias: int,
|
||||||
max_index: int,
|
max_index: int,
|
||||||
|
free_pointer: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
Bit_Array_Iterator :: struct {
|
Bit_Array_Iterator :: struct {
|
||||||
array: ^Bit_Array,
|
array: ^Bit_Array,
|
||||||
word_idx: int,
|
word_idx: int,
|
||||||
bit_idx: uint,
|
bit_idx: uint,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -186,7 +188,7 @@ set :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator
|
|||||||
/*
|
/*
|
||||||
A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
|
A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
|
||||||
*/
|
*/
|
||||||
create :: proc(max_index: int, min_index := 0, allocator := context.allocator) -> (res: Bit_Array, ok: bool) #optional_ok {
|
create :: proc(max_index: int, min_index := 0, allocator := context.allocator) -> (res: ^Bit_Array, ok: bool) #optional_ok {
|
||||||
context.allocator = allocator
|
context.allocator = allocator
|
||||||
size_in_bits := max_index - min_index
|
size_in_bits := max_index - min_index
|
||||||
|
|
||||||
@@ -194,11 +196,11 @@ create :: proc(max_index: int, min_index := 0, allocator := context.allocator) -
|
|||||||
|
|
||||||
legs := size_in_bits >> INDEX_SHIFT
|
legs := size_in_bits >> INDEX_SHIFT
|
||||||
|
|
||||||
res = Bit_Array{
|
res = new(Bit_Array)
|
||||||
bias = min_index,
|
res.bias = min_index
|
||||||
max_index = max_index,
|
res.max_index = max_index
|
||||||
}
|
res.free_pointer = true
|
||||||
return res, resize_if_needed(&res, legs)
|
return res, resize_if_needed(res, legs)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -206,7 +208,7 @@ create :: proc(max_index: int, min_index := 0, allocator := context.allocator) -
|
|||||||
*/
|
*/
|
||||||
clear :: proc(ba: ^Bit_Array) {
|
clear :: proc(ba: ^Bit_Array) {
|
||||||
if ba == nil { return }
|
if ba == nil { return }
|
||||||
ba.bits = {}
|
mem.zero_slice(ba.bits[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -215,6 +217,9 @@ clear :: proc(ba: ^Bit_Array) {
|
|||||||
destroy :: proc(ba: ^Bit_Array) {
|
destroy :: proc(ba: ^Bit_Array) {
|
||||||
if ba == nil { return }
|
if ba == nil { return }
|
||||||
delete(ba.bits)
|
delete(ba.bits)
|
||||||
|
if ba.free_pointer { // Only free if this Bit_Array was created using `create`, not when on the stack.
|
||||||
|
free(ba)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ package dynamic_bit_array
|
|||||||
// returns `false`, `false`, because this Bit Array wasn't created to allow negative indices.
|
// returns `false`, `false`, because this Bit Array wasn't created to allow negative indices.
|
||||||
was_set, was_retrieved := get(&bits, -1)
|
was_set, was_retrieved := get(&bits, -1)
|
||||||
fmt.println(was_set, was_retrieved)
|
fmt.println(was_set, was_retrieved)
|
||||||
|
destroy(&bits)
|
||||||
}
|
}
|
||||||
|
|
||||||
-- A Bit Array can optionally allow for negative indices, if the mininum value was given during creation:
|
-- A Bit Array can optionally allow for negative indices, if the mininum value was given during creation:
|
||||||
@@ -40,13 +41,13 @@ package dynamic_bit_array
|
|||||||
using bit_array
|
using bit_array
|
||||||
|
|
||||||
bits := create(int(max(Foo)), int(min(Foo)))
|
bits := create(int(max(Foo)), int(min(Foo)))
|
||||||
defer destroy(&bits)
|
defer destroy(bits)
|
||||||
|
|
||||||
fmt.printf("Set(Bar): %v\n", set(&bits, Foo.Bar))
|
fmt.printf("Set(Bar): %v\n", set(bits, Foo.Bar))
|
||||||
fmt.printf("Get(Bar): %v, %v\n", get(&bits, Foo.Bar))
|
fmt.printf("Get(Bar): %v, %v\n", get(bits, Foo.Bar))
|
||||||
fmt.printf("Set(Negative_Test): %v\n", set(&bits, Foo.Negative_Test))
|
fmt.printf("Set(Negative_Test): %v\n", set(bits, Foo.Negative_Test))
|
||||||
fmt.printf("Get(Leaves): %v, %v\n", get(&bits, Foo.Leaves))
|
fmt.printf("Get(Leaves): %v, %v\n", get(bits, Foo.Leaves))
|
||||||
fmt.printf("Get(Negative_Test): %v, %v\n", get(&bits, Foo.Negative_Test))
|
fmt.printf("Get(Negative_Test): %v, %v\n", get(bits, Foo.Negative_Test))
|
||||||
fmt.printf("Freed.\n")
|
fmt.printf("Freed.\n")
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
when ODIN_OS != .Linux {
|
when ODIN_OS != .Linux && ODIN_OS != .OpenBSD && ODIN_OS != .Windows {
|
||||||
_rand_bytes :: proc (dst: []byte) {
|
_rand_bytes :: proc (dst: []byte) {
|
||||||
unimplemented("crypto: rand_bytes not supported on this OS")
|
unimplemented("crypto: rand_bytes not supported on this OS")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
import "core:c"
|
||||||
|
|
||||||
|
foreign import libc "system:c"
|
||||||
|
foreign libc {
|
||||||
|
arc4random_buf :: proc "c" (buf: rawptr, nbytes: c.size_t) ---
|
||||||
|
}
|
||||||
|
|
||||||
|
_rand_bytes :: proc (dst: []byte) {
|
||||||
|
arc4random_buf(raw_data(dst), len(dst))
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
import win32 "core:sys/windows"
|
||||||
|
import "core:os"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
_rand_bytes :: proc(dst: []byte) {
|
||||||
|
ret := (os.Errno)(win32.BCryptGenRandom(nil, raw_data(dst), u32(len(dst)), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG))
|
||||||
|
if ret != os.ERROR_NONE {
|
||||||
|
switch ret {
|
||||||
|
case os.ERROR_INVALID_HANDLE:
|
||||||
|
// The handle to the first parameter is invalid.
|
||||||
|
// This should not happen here, since we explicitly pass nil to it
|
||||||
|
panic("crypto: BCryptGenRandom Invalid handle for hAlgorithm")
|
||||||
|
case os.ERROR_INVALID_PARAMETER:
|
||||||
|
// One of the parameters was invalid
|
||||||
|
panic("crypto: BCryptGenRandom Invalid parameter")
|
||||||
|
case:
|
||||||
|
// Unknown error
|
||||||
|
panic(fmt.tprintf("crypto: BCryptGenRandom failed: %d\n", ret))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,335 @@
|
|||||||
|
package siphash
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2022 zhibog
|
||||||
|
Made available under the BSD-3 license.
|
||||||
|
|
||||||
|
List of contributors:
|
||||||
|
zhibog: Initial implementation.
|
||||||
|
|
||||||
|
Implementation of the SipHash hashing algorithm, as defined at <https://github.com/veorq/SipHash> and <https://www.aumasson.jp/siphash/siphash.pdf>
|
||||||
|
|
||||||
|
Use the specific procedures for a certain setup. The generic procdedures will default to Siphash 2-4
|
||||||
|
*/
|
||||||
|
|
||||||
|
import "core:crypto"
|
||||||
|
import "core:crypto/util"
|
||||||
|
|
||||||
|
/*
|
||||||
|
High level API
|
||||||
|
*/
|
||||||
|
|
||||||
|
KEY_SIZE :: 16
|
||||||
|
DIGEST_SIZE :: 8
|
||||||
|
|
||||||
|
// sum_string_1_3 will hash the given message with the key and return
|
||||||
|
// the computed hash as a u64
|
||||||
|
sum_string_1_3 :: proc(msg, key: string) -> u64 {
|
||||||
|
return sum_bytes_1_3(transmute([]byte)(msg), transmute([]byte)(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
// sum_bytes_1_3 will hash the given message with the key and return
|
||||||
|
// the computed hash as a u64
|
||||||
|
sum_bytes_1_3 :: proc (msg, key: []byte) -> u64 {
|
||||||
|
ctx: Context
|
||||||
|
hash: u64
|
||||||
|
init(&ctx, key, 1, 3)
|
||||||
|
update(&ctx, msg)
|
||||||
|
final(&ctx, &hash)
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// sum_string_to_buffer_1_3 will hash the given message with the key and write
|
||||||
|
// the computed hash into the provided destination buffer
|
||||||
|
sum_string_to_buffer_1_3 :: proc(msg, key: string, dst: []byte) {
|
||||||
|
sum_bytes_to_buffer_1_3(transmute([]byte)(msg), transmute([]byte)(key), dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sum_bytes_to_buffer_1_3 will hash the given message with the key and write
|
||||||
|
// the computed hash into the provided destination buffer
|
||||||
|
sum_bytes_to_buffer_1_3 :: proc(msg, key, dst: []byte) {
|
||||||
|
assert(len(dst) >= DIGEST_SIZE, "crypto/siphash: Destination buffer needs to be at least of size 8")
|
||||||
|
hash := sum_bytes_1_3(msg, key)
|
||||||
|
_collect_output(dst[:], hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
sum_1_3 :: proc {
|
||||||
|
sum_string_1_3,
|
||||||
|
sum_bytes_1_3,
|
||||||
|
sum_string_to_buffer_1_3,
|
||||||
|
sum_bytes_to_buffer_1_3,
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify_u64_1_3 will check if the supplied tag matches with the output you
|
||||||
|
// will get from the provided message and key
|
||||||
|
verify_u64_1_3 :: proc (tag: u64 msg, key: []byte) -> bool {
|
||||||
|
return sum_bytes_1_3(msg, key) == tag
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify_bytes will check if the supplied tag matches with the output you
|
||||||
|
// will get from the provided message and key
|
||||||
|
verify_bytes_1_3 :: proc (tag, msg, key: []byte) -> bool {
|
||||||
|
derived_tag: [8]byte
|
||||||
|
sum_bytes_to_buffer_1_3(msg, key, derived_tag[:])
|
||||||
|
return crypto.compare_constant_time(derived_tag[:], tag) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
verify_1_3 :: proc {
|
||||||
|
verify_bytes_1_3,
|
||||||
|
verify_u64_1_3,
|
||||||
|
}
|
||||||
|
|
||||||
|
// sum_string_2_4 will hash the given message with the key and return
|
||||||
|
// the computed hash as a u64
|
||||||
|
sum_string_2_4 :: proc(msg, key: string) -> u64 {
|
||||||
|
return sum_bytes_2_4(transmute([]byte)(msg), transmute([]byte)(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
// sum_bytes_2_4 will hash the given message with the key and return
|
||||||
|
// the computed hash as a u64
|
||||||
|
sum_bytes_2_4 :: proc (msg, key: []byte) -> u64 {
|
||||||
|
ctx: Context
|
||||||
|
hash: u64
|
||||||
|
init(&ctx, key, 2, 4)
|
||||||
|
update(&ctx, msg)
|
||||||
|
final(&ctx, &hash)
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// sum_string_to_buffer_2_4 will hash the given message with the key and write
|
||||||
|
// the computed hash into the provided destination buffer
|
||||||
|
sum_string_to_buffer_2_4 :: proc(msg, key: string, dst: []byte) {
|
||||||
|
sum_bytes_to_buffer_2_4(transmute([]byte)(msg), transmute([]byte)(key), dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sum_bytes_to_buffer_2_4 will hash the given message with the key and write
|
||||||
|
// the computed hash into the provided destination buffer
|
||||||
|
sum_bytes_to_buffer_2_4 :: proc(msg, key, dst: []byte) {
|
||||||
|
assert(len(dst) >= DIGEST_SIZE, "crypto/siphash: Destination buffer needs to be at least of size 8")
|
||||||
|
hash := sum_bytes_2_4(msg, key)
|
||||||
|
_collect_output(dst[:], hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
sum_2_4 :: proc {
|
||||||
|
sum_string_2_4,
|
||||||
|
sum_bytes_2_4,
|
||||||
|
sum_string_to_buffer_2_4,
|
||||||
|
sum_bytes_to_buffer_2_4,
|
||||||
|
}
|
||||||
|
|
||||||
|
sum_string :: sum_string_2_4
|
||||||
|
sum_bytes :: sum_bytes_2_4
|
||||||
|
sum_string_to_buffer :: sum_string_to_buffer_2_4
|
||||||
|
sum_bytes_to_buffer :: sum_bytes_to_buffer_2_4
|
||||||
|
sum :: proc {
|
||||||
|
sum_string,
|
||||||
|
sum_bytes,
|
||||||
|
sum_string_to_buffer,
|
||||||
|
sum_bytes_to_buffer,
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify_u64_2_4 will check if the supplied tag matches with the output you
|
||||||
|
// will get from the provided message and key
|
||||||
|
verify_u64_2_4 :: proc (tag: u64 msg, key: []byte) -> bool {
|
||||||
|
return sum_bytes_2_4(msg, key) == tag
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify_bytes will check if the supplied tag matches with the output you
|
||||||
|
// will get from the provided message and key
|
||||||
|
verify_bytes_2_4 :: proc (tag, msg, key: []byte) -> bool {
|
||||||
|
derived_tag: [8]byte
|
||||||
|
sum_bytes_to_buffer_2_4(msg, key, derived_tag[:])
|
||||||
|
return crypto.compare_constant_time(derived_tag[:], tag) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
verify_2_4 :: proc {
|
||||||
|
verify_bytes_2_4,
|
||||||
|
verify_u64_2_4,
|
||||||
|
}
|
||||||
|
|
||||||
|
verify_bytes :: verify_bytes_2_4
|
||||||
|
verify_u64 :: verify_u64_2_4
|
||||||
|
verify :: proc {
|
||||||
|
verify_bytes,
|
||||||
|
verify_u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
// sum_string_4_8 will hash the given message with the key and return
|
||||||
|
// the computed hash as a u64
|
||||||
|
sum_string_4_8 :: proc(msg, key: string) -> u64 {
|
||||||
|
return sum_bytes_4_8(transmute([]byte)(msg), transmute([]byte)(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
// sum_bytes_4_8 will hash the given message with the key and return
|
||||||
|
// the computed hash as a u64
|
||||||
|
sum_bytes_4_8 :: proc (msg, key: []byte) -> u64 {
|
||||||
|
ctx: Context
|
||||||
|
hash: u64
|
||||||
|
init(&ctx, key, 4, 8)
|
||||||
|
update(&ctx, msg)
|
||||||
|
final(&ctx, &hash)
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// sum_string_to_buffer_4_8 will hash the given message with the key and write
|
||||||
|
// the computed hash into the provided destination buffer
|
||||||
|
sum_string_to_buffer_4_8 :: proc(msg, key: string, dst: []byte) {
|
||||||
|
sum_bytes_to_buffer_4_8(transmute([]byte)(msg), transmute([]byte)(key), dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sum_bytes_to_buffer_4_8 will hash the given message with the key and write
|
||||||
|
// the computed hash into the provided destination buffer
|
||||||
|
sum_bytes_to_buffer_4_8 :: proc(msg, key, dst: []byte) {
|
||||||
|
assert(len(dst) >= DIGEST_SIZE, "crypto/siphash: Destination buffer needs to be at least of size 8")
|
||||||
|
hash := sum_bytes_4_8(msg, key)
|
||||||
|
_collect_output(dst[:], hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
sum_4_8 :: proc {
|
||||||
|
sum_string_4_8,
|
||||||
|
sum_bytes_4_8,
|
||||||
|
sum_string_to_buffer_4_8,
|
||||||
|
sum_bytes_to_buffer_4_8,
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify_u64_4_8 will check if the supplied tag matches with the output you
|
||||||
|
// will get from the provided message and key
|
||||||
|
verify_u64_4_8 :: proc (tag: u64 msg, key: []byte) -> bool {
|
||||||
|
return sum_bytes_4_8(msg, key) == tag
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify_bytes will check if the supplied tag matches with the output you
|
||||||
|
// will get from the provided message and key
|
||||||
|
verify_bytes_4_8 :: proc (tag, msg, key: []byte) -> bool {
|
||||||
|
derived_tag: [8]byte
|
||||||
|
sum_bytes_to_buffer_4_8(msg, key, derived_tag[:])
|
||||||
|
return crypto.compare_constant_time(derived_tag[:], tag) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
verify_4_8 :: proc {
|
||||||
|
verify_bytes_4_8,
|
||||||
|
verify_u64_4_8,
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Low level API
|
||||||
|
*/
|
||||||
|
|
||||||
|
init :: proc(ctx: ^Context, key: []byte, c_rounds, d_rounds: int) {
|
||||||
|
assert(len(key) == KEY_SIZE, "crypto/siphash: Invalid key size, want 16")
|
||||||
|
ctx.c_rounds = c_rounds
|
||||||
|
ctx.d_rounds = d_rounds
|
||||||
|
is_valid_setting := (ctx.c_rounds == 1 && ctx.d_rounds == 3) ||
|
||||||
|
(ctx.c_rounds == 2 && ctx.d_rounds == 4) ||
|
||||||
|
(ctx.c_rounds == 4 && ctx.d_rounds == 8)
|
||||||
|
assert(is_valid_setting, "crypto/siphash: Incorrect rounds set up. Valid pairs are (1,3), (2,4) and (4,8)")
|
||||||
|
ctx.k0 = util.U64_LE(key[:8])
|
||||||
|
ctx.k1 = util.U64_LE(key[8:])
|
||||||
|
ctx.v0 = 0x736f6d6570736575 ~ ctx.k0
|
||||||
|
ctx.v1 = 0x646f72616e646f6d ~ ctx.k1
|
||||||
|
ctx.v2 = 0x6c7967656e657261 ~ ctx.k0
|
||||||
|
ctx.v3 = 0x7465646279746573 ~ ctx.k1
|
||||||
|
ctx.is_initialized = true
|
||||||
|
}
|
||||||
|
|
||||||
|
update :: proc(ctx: ^Context, data: []byte) {
|
||||||
|
assert(ctx.is_initialized, "crypto/siphash: Context is not initalized")
|
||||||
|
ctx.last_block = len(data) / 8 * 8
|
||||||
|
ctx.buf = data
|
||||||
|
i := 0
|
||||||
|
m: u64
|
||||||
|
for i < ctx.last_block {
|
||||||
|
m = u64(ctx.buf[i] & 0xff)
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
for r in u64(1)..<8 {
|
||||||
|
m |= u64(ctx.buf[i] & 0xff) << (r * 8)
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.v3 ~= m
|
||||||
|
for _ in 0..<ctx.c_rounds {
|
||||||
|
_compress(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.v0 ~= m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final :: proc(ctx: ^Context, dst: ^u64) {
|
||||||
|
m: u64
|
||||||
|
for i := len(ctx.buf) - 1; i >= ctx.last_block; i -= 1 {
|
||||||
|
m <<= 8
|
||||||
|
m |= u64(ctx.buf[i] & 0xff)
|
||||||
|
}
|
||||||
|
m |= u64(len(ctx.buf) << 56)
|
||||||
|
|
||||||
|
ctx.v3 ~= m
|
||||||
|
|
||||||
|
for _ in 0..<ctx.c_rounds {
|
||||||
|
_compress(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.v0 ~= m
|
||||||
|
ctx.v2 ~= 0xff
|
||||||
|
|
||||||
|
for _ in 0..<ctx.d_rounds {
|
||||||
|
_compress(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
dst^ = ctx.v0 ~ ctx.v1 ~ ctx.v2 ~ ctx.v3
|
||||||
|
|
||||||
|
reset(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
reset :: proc(ctx: ^Context) {
|
||||||
|
ctx.k0, ctx.k1 = 0, 0
|
||||||
|
ctx.v0, ctx.v1 = 0, 0
|
||||||
|
ctx.v2, ctx.v3 = 0, 0
|
||||||
|
ctx.last_block = 0
|
||||||
|
ctx.c_rounds = 0
|
||||||
|
ctx.d_rounds = 0
|
||||||
|
ctx.is_initialized = false
|
||||||
|
}
|
||||||
|
|
||||||
|
Context :: struct {
|
||||||
|
v0, v1, v2, v3: u64, // State values
|
||||||
|
k0, k1: u64, // Split key
|
||||||
|
c_rounds: int, // Number of message rounds
|
||||||
|
d_rounds: int, // Number of finalization rounds
|
||||||
|
buf: []byte, // Provided data
|
||||||
|
last_block: int, // Offset from the last block
|
||||||
|
is_initialized: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_byte :: #force_inline proc "contextless" (byte_num: byte, into: u64) -> byte {
|
||||||
|
return byte(into >> (((~byte_num) & (size_of(u64) - 1)) << 3))
|
||||||
|
}
|
||||||
|
|
||||||
|
_collect_output :: #force_inline proc "contextless" (dst: []byte, hash: u64) {
|
||||||
|
dst[0] = _get_byte(7, hash)
|
||||||
|
dst[1] = _get_byte(6, hash)
|
||||||
|
dst[2] = _get_byte(5, hash)
|
||||||
|
dst[3] = _get_byte(4, hash)
|
||||||
|
dst[4] = _get_byte(3, hash)
|
||||||
|
dst[5] = _get_byte(2, hash)
|
||||||
|
dst[6] = _get_byte(1, hash)
|
||||||
|
dst[7] = _get_byte(0, hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
_compress :: #force_inline proc "contextless" (ctx: ^Context) {
|
||||||
|
ctx.v0 += ctx.v1
|
||||||
|
ctx.v1 = util.ROTL64(ctx.v1, 13)
|
||||||
|
ctx.v1 ~= ctx.v0
|
||||||
|
ctx.v0 = util.ROTL64(ctx.v0, 32)
|
||||||
|
ctx.v2 += ctx.v3
|
||||||
|
ctx.v3 = util.ROTL64(ctx.v3, 16)
|
||||||
|
ctx.v3 ~= ctx.v2
|
||||||
|
ctx.v0 += ctx.v3
|
||||||
|
ctx.v3 = util.ROTL64(ctx.v3, 21)
|
||||||
|
ctx.v3 ~= ctx.v0
|
||||||
|
ctx.v2 += ctx.v1
|
||||||
|
ctx.v1 = util.ROTL64(ctx.v1, 17)
|
||||||
|
ctx.v1 ~= ctx.v2
|
||||||
|
ctx.v2 = util.ROTL64(ctx.v2, 32)
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build linux, darwin, freebsd
|
// +build linux, darwin, freebsd, openbsd
|
||||||
package dynlib
|
package dynlib
|
||||||
|
|
||||||
import "core:os"
|
import "core:os"
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
Implementation of the LEB128 variable integer encoding as used by DWARF encoding and DEX files, among others.
|
||||||
|
|
||||||
|
Author of this Odin package: Jeroen van Rijn
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```odin
|
||||||
|
import "core:encoding/varint"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
main :: proc() {
|
||||||
|
buf: [varint.LEB128_MAX_BYTES]u8
|
||||||
|
|
||||||
|
value := u128(42)
|
||||||
|
|
||||||
|
encode_size, encode_err := varint.encode_uleb128(buf[:], value)
|
||||||
|
assert(encode_size == 1 && encode_err == .None)
|
||||||
|
|
||||||
|
fmt.println(buf[:encode_size])
|
||||||
|
|
||||||
|
decoded_val, decode_size, decode_err := varint.decode_uleb128(buf[:encode_size])
|
||||||
|
assert(decoded_val == value && decode_size == encode_size && decode_err == .None)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
*/
|
||||||
|
package varint
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||||
|
Made available under Odin's BSD-3 license.
|
||||||
|
|
||||||
|
List of contributors:
|
||||||
|
Jeroen van Rijn: Initial implementation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// package varint implements variable length integer encoding and decoding using
|
||||||
|
// the LEB128 format as used by DWARF debug info, Android .dex and other file formats.
|
||||||
|
package varint
|
||||||
|
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
// In theory we should use the bigint package. In practice, varints bigger than this indicate a corrupted file.
|
||||||
|
// Instead we'll set limits on the values we'll encode/decode
|
||||||
|
// 18 * 7 bits = 126, which means that a possible 19th byte may at most be `0b0000_0011`.
|
||||||
|
LEB128_MAX_BYTES :: 19
|
||||||
|
|
||||||
|
Error :: enum {
|
||||||
|
None = 0,
|
||||||
|
Buffer_Too_Small = 1,
|
||||||
|
Value_Too_Large = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode a slice of bytes encoding an unsigned LEB128 integer into value and number of bytes used.
|
||||||
|
// Returns `size` == 0 for an invalid value, empty slice, or a varint > 18 bytes.
|
||||||
|
decode_uleb128 :: proc(buf: []u8) -> (val: u128, size: int, err: Error) {
|
||||||
|
more := true
|
||||||
|
|
||||||
|
for v, i in buf {
|
||||||
|
size = i + 1
|
||||||
|
|
||||||
|
// 18 * 7 bits = 126, which means that a possible 19th byte may at most be 0b0000_0011.
|
||||||
|
if size > LEB128_MAX_BYTES || size == LEB128_MAX_BYTES && v > 0b0000_0011 {
|
||||||
|
return 0, 0, .Value_Too_Large
|
||||||
|
}
|
||||||
|
|
||||||
|
val |= u128(v & 0x7f) << uint(i * 7)
|
||||||
|
|
||||||
|
if v < 128 {
|
||||||
|
more = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the buffer runs out before the number ends, return an error.
|
||||||
|
if more {
|
||||||
|
return 0, 0, .Buffer_Too_Small
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode a slice of bytes encoding a signed LEB128 integer into value and number of bytes used.
|
||||||
|
// Returns `size` == 0 for an invalid value, empty slice, or a varint > 18 bytes.
|
||||||
|
decode_ileb128 :: proc(buf: []u8) -> (val: i128, size: int, err: Error) {
|
||||||
|
shift: uint
|
||||||
|
|
||||||
|
if len(buf) == 0 {
|
||||||
|
return 0, 0, .Buffer_Too_Small
|
||||||
|
}
|
||||||
|
|
||||||
|
for v in buf {
|
||||||
|
size += 1
|
||||||
|
|
||||||
|
// 18 * 7 bits = 126, which including sign means we can have a 19th byte.
|
||||||
|
if size > LEB128_MAX_BYTES || size == LEB128_MAX_BYTES && v > 0x7f {
|
||||||
|
return 0, 0, .Value_Too_Large
|
||||||
|
}
|
||||||
|
|
||||||
|
val |= i128(v & 0x7f) << shift
|
||||||
|
shift += 7
|
||||||
|
|
||||||
|
if v < 128 { break }
|
||||||
|
}
|
||||||
|
|
||||||
|
if buf[size - 1] & 0x40 == 0x40 {
|
||||||
|
val |= max(i128) << shift
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode `val` into `buf` as an unsigned LEB128 encoded series of bytes.
|
||||||
|
// `buf` must be appropriately sized.
|
||||||
|
encode_uleb128 :: proc(buf: []u8, val: u128) -> (size: int, err: Error) {
|
||||||
|
val := val
|
||||||
|
|
||||||
|
for {
|
||||||
|
size += 1
|
||||||
|
|
||||||
|
if size > len(buf) {
|
||||||
|
fmt.println(val, buf[:size - 1])
|
||||||
|
return 0, .Buffer_Too_Small
|
||||||
|
}
|
||||||
|
|
||||||
|
low := val & 0x7f
|
||||||
|
val >>= 7
|
||||||
|
|
||||||
|
if val > 0 {
|
||||||
|
low |= 0x80 // more bytes to follow
|
||||||
|
}
|
||||||
|
buf[size - 1] = u8(low)
|
||||||
|
|
||||||
|
if val == 0 { break }
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
SIGN_MASK :: (i128(1) << 121) // sign extend mask
|
||||||
|
|
||||||
|
// Encode `val` into `buf` as a signed LEB128 encoded series of bytes.
|
||||||
|
// `buf` must be appropriately sized.
|
||||||
|
encode_ileb128 :: proc(buf: []u8, val: i128) -> (size: int, err: Error) {
|
||||||
|
val := val
|
||||||
|
more := true
|
||||||
|
|
||||||
|
for more {
|
||||||
|
size += 1
|
||||||
|
|
||||||
|
if size > len(buf) {
|
||||||
|
return 0, .Buffer_Too_Small
|
||||||
|
}
|
||||||
|
|
||||||
|
low := val & 0x7f
|
||||||
|
val >>= 7
|
||||||
|
|
||||||
|
low = (low ~ SIGN_MASK) - SIGN_MASK
|
||||||
|
|
||||||
|
if (val == 0 && low & 0x40 != 0x40) || (val == -1 && low & 0x40 == 0x40) {
|
||||||
|
more = false
|
||||||
|
} else {
|
||||||
|
low |= 0x80
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[size - 1] = u8(low)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
+221
-213
File diff suppressed because it is too large
Load Diff
@@ -41,6 +41,8 @@ mem_copy_non_overlapping :: proc(dst, src: rawptr, len: int) ---
|
|||||||
mem_zero :: proc(ptr: rawptr, len: int) ---
|
mem_zero :: proc(ptr: rawptr, len: int) ---
|
||||||
mem_zero_volatile :: proc(ptr: rawptr, len: int) ---
|
mem_zero_volatile :: proc(ptr: rawptr, len: int) ---
|
||||||
|
|
||||||
|
unaligned_load :: proc(src: ^$T) -> T ---
|
||||||
|
unaligned_store :: proc(dst: ^$T, val: T) -> T ---
|
||||||
|
|
||||||
fixed_point_mul :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) ---
|
fixed_point_mul :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) ---
|
||||||
fixed_point_div :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) ---
|
fixed_point_div :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) ---
|
||||||
|
|||||||
+11
-11
@@ -12,22 +12,22 @@ import "core:mem"
|
|||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
|
||||||
_main :: proc() {
|
_main :: proc() {
|
||||||
do stuff
|
do stuff
|
||||||
}
|
}
|
||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
track: mem.Tracking_Allocator
|
track: mem.Tracking_Allocator
|
||||||
mem.tracking_allocator_init(&track, context.allocator)
|
mem.tracking_allocator_init(&track, context.allocator)
|
||||||
context.allocator = mem.tracking_allocator(&track)
|
context.allocator = mem.tracking_allocator(&track)
|
||||||
|
|
||||||
_main()
|
_main()
|
||||||
|
|
||||||
for _, v in track.allocation_map {
|
for _, leak in track.allocation_map {
|
||||||
fmt.printf("%v leaked %v bytes", v.location, v.size)
|
fmt.printf("%v leaked %v bytes\n", leak.location, leak.size)
|
||||||
}
|
}
|
||||||
for bf in track.bad_free_array {
|
for bad_free in track.bad_free_array {
|
||||||
fmt.printf("%v allocation %p was freed badly", bf.location, bf.memory)
|
fmt.printf("%v allocation %p was freed badly\n", bad_free.location, bad_free.memory)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -6,25 +6,25 @@ DEFAULT_PAGE_SIZE := uint(4096)
|
|||||||
|
|
||||||
Allocator_Error :: mem.Allocator_Error
|
Allocator_Error :: mem.Allocator_Error
|
||||||
|
|
||||||
reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
|
reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
|
||||||
return _reserve(size)
|
return _reserve(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
commit :: proc(data: rawptr, size: uint) -> Allocator_Error {
|
commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
|
||||||
return _commit(data, size)
|
return _commit(data, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
reserve_and_commit :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
|
reserve_and_commit :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
|
||||||
data = reserve(size) or_return
|
data = reserve(size) or_return
|
||||||
commit(raw_data(data), size) or_return
|
commit(raw_data(data), size) or_return
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
decommit :: proc(data: rawptr, size: uint) {
|
decommit :: proc "contextless" (data: rawptr, size: uint) {
|
||||||
_decommit(data, size)
|
_decommit(data, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
release :: proc(data: rawptr, size: uint) {
|
release :: proc "contextless" (data: rawptr, size: uint) {
|
||||||
_release(data, size)
|
_release(data, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ Protect_Flag :: enum u32 {
|
|||||||
Protect_Flags :: distinct bit_set[Protect_Flag; u32]
|
Protect_Flags :: distinct bit_set[Protect_Flag; u32]
|
||||||
Protect_No_Access :: Protect_Flags{}
|
Protect_No_Access :: Protect_Flags{}
|
||||||
|
|
||||||
protect :: proc(data: rawptr, size: uint, flags: Protect_Flags) -> bool {
|
protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool {
|
||||||
return _protect(data, size, flags)
|
return _protect(data, size, flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,11 +82,13 @@ memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags)
|
|||||||
pmblock := platform_memory_alloc(0, total_size) or_return
|
pmblock := platform_memory_alloc(0, total_size) or_return
|
||||||
|
|
||||||
pmblock.block.base = ([^]byte)(uintptr(pmblock) + base_offset)
|
pmblock.block.base = ([^]byte)(uintptr(pmblock) + base_offset)
|
||||||
commit(pmblock.block.base, committed) or_return
|
commit_err := platform_memory_commit(pmblock, uint(base_offset) + committed)
|
||||||
|
assert(commit_err == nil)
|
||||||
|
|
||||||
// Should be zeroed
|
// Should be zeroed
|
||||||
assert(pmblock.block.used == 0)
|
assert(pmblock.block.used == 0)
|
||||||
assert(pmblock.block.prev == nil)
|
assert(pmblock.block.prev == nil)
|
||||||
if (do_protection) {
|
if do_protection {
|
||||||
protect(rawptr(uintptr(pmblock) + protect_offset), page_size, Protect_No_Access)
|
protect(rawptr(uintptr(pmblock) + protect_offset), page_size, Protect_No_Access)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +107,7 @@ memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags)
|
|||||||
}
|
}
|
||||||
|
|
||||||
alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int) -> (data: []byte, err: Allocator_Error) {
|
alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int) -> (data: []byte, err: Allocator_Error) {
|
||||||
calc_alignment_offset :: proc(block: ^Memory_Block, alignment: uintptr) -> uint {
|
calc_alignment_offset :: proc "contextless" (block: ^Memory_Block, alignment: uintptr) -> uint {
|
||||||
alignment_offset := uint(0)
|
alignment_offset := uint(0)
|
||||||
ptr := uintptr(block.base[block.used:])
|
ptr := uintptr(block.base[block.used:])
|
||||||
mask := alignment-1
|
mask := alignment-1
|
||||||
@@ -115,23 +117,37 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int)
|
|||||||
return alignment_offset
|
return alignment_offset
|
||||||
|
|
||||||
}
|
}
|
||||||
|
do_commit_if_necessary :: proc(block: ^Memory_Block, size: uint) -> (err: Allocator_Error) {
|
||||||
|
if block.committed - block.used < size {
|
||||||
|
pmblock := (^Platform_Memory_Block)(block)
|
||||||
|
base_offset := uint(uintptr(block) - uintptr(pmblock))
|
||||||
|
platform_total_commit := base_offset + block.used + size
|
||||||
|
|
||||||
|
assert(pmblock.committed <= pmblock.reserved)
|
||||||
|
assert(pmblock.committed < platform_total_commit)
|
||||||
|
|
||||||
|
platform_memory_commit(pmblock, platform_total_commit) or_return
|
||||||
|
|
||||||
|
pmblock.committed = platform_total_commit
|
||||||
|
block.committed = pmblock.committed - base_offset
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
alignment_offset := calc_alignment_offset(block, uintptr(alignment))
|
alignment_offset := calc_alignment_offset(block, uintptr(alignment))
|
||||||
|
|
||||||
size := uint(min_size) + alignment_offset
|
size := uint(min_size) + alignment_offset
|
||||||
|
|
||||||
if block.used + size > block.reserved {
|
if block.used + size > block.reserved {
|
||||||
err = .Out_Of_Memory
|
err = .Out_Of_Memory
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
assert(block.committed <= block.reserved)
|
||||||
|
do_commit_if_necessary(block, size) or_return
|
||||||
|
|
||||||
ptr := block.base[block.used:]
|
data = block.base[block.used+alignment_offset:][:min_size]
|
||||||
ptr = ptr[alignment_offset:]
|
|
||||||
|
|
||||||
block.used += size
|
block.used += size
|
||||||
assert(block.used <= block.reserved)
|
return
|
||||||
|
|
||||||
return ptr[:min_size], nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ madvise :: proc "contextless" (addr: rawptr, length: uint, advice: c.int) -> c.i
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
|
_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
|
||||||
MAP_FAILED := rawptr(~uintptr(0))
|
MAP_FAILED := rawptr(~uintptr(0))
|
||||||
result := mmap(nil, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
|
result := mmap(nil, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
|
||||||
if result == MAP_FAILED {
|
if result == MAP_FAILED {
|
||||||
@@ -67,7 +67,7 @@ _reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
|
|||||||
return ([^]byte)(result)[:size], nil
|
return ([^]byte)(result)[:size], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_commit :: proc(data: rawptr, size: uint) -> Allocator_Error {
|
_commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
|
||||||
result := mprotect(data, size, PROT_READ|PROT_WRITE)
|
result := mprotect(data, size, PROT_READ|PROT_WRITE)
|
||||||
if result != 0 {
|
if result != 0 {
|
||||||
// TODO(bill): Handle error value correctly
|
// TODO(bill): Handle error value correctly
|
||||||
@@ -75,14 +75,14 @@ _commit :: proc(data: rawptr, size: uint) -> Allocator_Error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
_decommit :: proc(data: rawptr, size: uint) {
|
_decommit :: proc "contextless" (data: rawptr, size: uint) {
|
||||||
mprotect(data, size, PROT_NONE)
|
mprotect(data, size, PROT_NONE)
|
||||||
madvise(data, size, MADV_FREE)
|
madvise(data, size, MADV_FREE)
|
||||||
}
|
}
|
||||||
_release :: proc(data: rawptr, size: uint) {
|
_release :: proc "contextless" (data: rawptr, size: uint) {
|
||||||
munmap(data, size)
|
munmap(data, size)
|
||||||
}
|
}
|
||||||
_protect :: proc(data: rawptr, size: uint, flags: Protect_Flags) -> bool {
|
_protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool {
|
||||||
pflags: c.int
|
pflags: c.int
|
||||||
pflags = PROT_NONE
|
pflags = PROT_NONE
|
||||||
if .Read in flags { pflags |= PROT_READ }
|
if .Read in flags { pflags |= PROT_READ }
|
||||||
|
|||||||
@@ -4,12 +4,13 @@ package mem_virtual
|
|||||||
import sync "core:sync/sync2"
|
import sync "core:sync/sync2"
|
||||||
|
|
||||||
Platform_Memory_Block :: struct {
|
Platform_Memory_Block :: struct {
|
||||||
block: Memory_Block,
|
block: Memory_Block,
|
||||||
reserved: uint,
|
committed: uint,
|
||||||
|
reserved: uint,
|
||||||
prev, next: ^Platform_Memory_Block,
|
prev, next: ^Platform_Memory_Block,
|
||||||
}
|
}
|
||||||
|
|
||||||
platform_memory_alloc :: proc(to_commit, to_reserve: uint) -> (block: ^Platform_Memory_Block, err: Allocator_Error) {
|
platform_memory_alloc :: proc "contextless" (to_commit, to_reserve: uint) -> (block: ^Platform_Memory_Block, err: Allocator_Error) {
|
||||||
to_commit, to_reserve := to_commit, to_reserve
|
to_commit, to_reserve := to_commit, to_reserve
|
||||||
to_reserve = max(to_commit, to_reserve)
|
to_reserve = max(to_commit, to_reserve)
|
||||||
|
|
||||||
@@ -20,12 +21,13 @@ platform_memory_alloc :: proc(to_commit, to_reserve: uint) -> (block: ^Platform_
|
|||||||
commit(raw_data(data), to_commit)
|
commit(raw_data(data), to_commit)
|
||||||
|
|
||||||
block = (^Platform_Memory_Block)(raw_data(data))
|
block = (^Platform_Memory_Block)(raw_data(data))
|
||||||
block.reserved = to_reserve
|
block.committed = to_commit
|
||||||
|
block.reserved = to_reserve
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
platform_memory_free :: proc(block: ^Platform_Memory_Block) {
|
platform_memory_free :: proc "contextless" (block: ^Platform_Memory_Block) {
|
||||||
if block != nil {
|
if block != nil {
|
||||||
release(block, block.reserved)
|
release(block, block.reserved)
|
||||||
}
|
}
|
||||||
@@ -52,3 +54,17 @@ platform_memory_init :: proc() {
|
|||||||
global_platform_memory_block_sentinel_set = true
|
global_platform_memory_block_sentinel_set = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
platform_memory_commit :: proc "contextless" (block: ^Platform_Memory_Block, to_commit: uint) -> (err: Allocator_Error) {
|
||||||
|
if to_commit < block.committed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if to_commit > block.reserved {
|
||||||
|
return .Out_Of_Memory
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
commit(block, to_commit) or_return
|
||||||
|
block.committed = to_commit
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ foreign Kernel32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
|
_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
|
||||||
result := VirtualAlloc(nil, size, MEM_RESERVE, PAGE_READWRITE)
|
result := VirtualAlloc(nil, size, MEM_RESERVE, PAGE_READWRITE)
|
||||||
if result == nil {
|
if result == nil {
|
||||||
err = .Out_Of_Memory
|
err = .Out_Of_Memory
|
||||||
@@ -72,7 +72,7 @@ _reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_commit :: proc(data: rawptr, size: uint) -> Allocator_Error {
|
_commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
|
||||||
result := VirtualAlloc(data, size, MEM_COMMIT, PAGE_READWRITE)
|
result := VirtualAlloc(data, size, MEM_COMMIT, PAGE_READWRITE)
|
||||||
if result == nil {
|
if result == nil {
|
||||||
switch err := GetLastError(); err {
|
switch err := GetLastError(); err {
|
||||||
@@ -85,13 +85,13 @@ _commit :: proc(data: rawptr, size: uint) -> Allocator_Error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
_decommit :: proc(data: rawptr, size: uint) {
|
_decommit :: proc "contextless" (data: rawptr, size: uint) {
|
||||||
VirtualFree(data, size, MEM_DECOMMIT)
|
VirtualFree(data, size, MEM_DECOMMIT)
|
||||||
}
|
}
|
||||||
_release :: proc(data: rawptr, size: uint) {
|
_release :: proc "contextless" (data: rawptr, size: uint) {
|
||||||
VirtualFree(data, 0, MEM_RELEASE)
|
VirtualFree(data, 0, MEM_RELEASE)
|
||||||
}
|
}
|
||||||
_protect :: proc(data: rawptr, size: uint, flags: Protect_Flags) -> bool {
|
_protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool {
|
||||||
pflags: u32
|
pflags: u32
|
||||||
pflags = PAGE_NOACCESS
|
pflags = PAGE_NOACCESS
|
||||||
switch flags {
|
switch flags {
|
||||||
|
|||||||
+173
-2
@@ -34,7 +34,7 @@ Node :: struct {
|
|||||||
pos: tokenizer.Pos,
|
pos: tokenizer.Pos,
|
||||||
end: tokenizer.Pos,
|
end: tokenizer.Pos,
|
||||||
state_flags: Node_State_Flags,
|
state_flags: Node_State_Flags,
|
||||||
derived: any,
|
derived: Any_Node,
|
||||||
}
|
}
|
||||||
|
|
||||||
Comment_Group :: struct {
|
Comment_Group :: struct {
|
||||||
@@ -88,9 +88,11 @@ File :: struct {
|
|||||||
|
|
||||||
Expr :: struct {
|
Expr :: struct {
|
||||||
using expr_base: Node,
|
using expr_base: Node,
|
||||||
|
derived_expr: Any_Expr,
|
||||||
}
|
}
|
||||||
Stmt :: struct {
|
Stmt :: struct {
|
||||||
using stmt_base: Node,
|
using stmt_base: Node,
|
||||||
|
derived_stmt: Any_Stmt,
|
||||||
}
|
}
|
||||||
Decl :: struct {
|
Decl :: struct {
|
||||||
using decl_base: Stmt,
|
using decl_base: Stmt,
|
||||||
@@ -541,7 +543,7 @@ unparen_expr :: proc(expr: ^Expr) -> (val: ^Expr) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
e, ok := val.derived.(Paren_Expr)
|
e, ok := val.derived.(^Paren_Expr)
|
||||||
if !ok || e.expr == nil {
|
if !ok || e.expr == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -759,3 +761,172 @@ Matrix_Type :: struct {
|
|||||||
column_count: ^Expr,
|
column_count: ^Expr,
|
||||||
elem: ^Expr,
|
elem: ^Expr,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Any_Node :: union {
|
||||||
|
^Package,
|
||||||
|
^File,
|
||||||
|
^Comment_Group,
|
||||||
|
|
||||||
|
^Bad_Expr,
|
||||||
|
^Ident,
|
||||||
|
^Implicit,
|
||||||
|
^Undef,
|
||||||
|
^Basic_Lit,
|
||||||
|
^Basic_Directive,
|
||||||
|
^Ellipsis,
|
||||||
|
^Proc_Lit,
|
||||||
|
^Comp_Lit,
|
||||||
|
^Tag_Expr,
|
||||||
|
^Unary_Expr,
|
||||||
|
^Binary_Expr,
|
||||||
|
^Paren_Expr,
|
||||||
|
^Selector_Expr,
|
||||||
|
^Implicit_Selector_Expr,
|
||||||
|
^Selector_Call_Expr,
|
||||||
|
^Index_Expr,
|
||||||
|
^Deref_Expr,
|
||||||
|
^Slice_Expr,
|
||||||
|
^Matrix_Index_Expr,
|
||||||
|
^Call_Expr,
|
||||||
|
^Field_Value,
|
||||||
|
^Ternary_If_Expr,
|
||||||
|
^Ternary_When_Expr,
|
||||||
|
^Or_Else_Expr,
|
||||||
|
^Or_Return_Expr,
|
||||||
|
^Type_Assertion,
|
||||||
|
^Type_Cast,
|
||||||
|
^Auto_Cast,
|
||||||
|
^Inline_Asm_Expr,
|
||||||
|
|
||||||
|
^Proc_Group,
|
||||||
|
|
||||||
|
^Typeid_Type,
|
||||||
|
^Helper_Type,
|
||||||
|
^Distinct_Type,
|
||||||
|
^Poly_Type,
|
||||||
|
^Proc_Type,
|
||||||
|
^Pointer_Type,
|
||||||
|
^Multi_Pointer_Type,
|
||||||
|
^Array_Type,
|
||||||
|
^Dynamic_Array_Type,
|
||||||
|
^Struct_Type,
|
||||||
|
^Union_Type,
|
||||||
|
^Enum_Type,
|
||||||
|
^Bit_Set_Type,
|
||||||
|
^Map_Type,
|
||||||
|
^Relative_Type,
|
||||||
|
^Matrix_Type,
|
||||||
|
|
||||||
|
^Bad_Stmt,
|
||||||
|
^Empty_Stmt,
|
||||||
|
^Expr_Stmt,
|
||||||
|
^Tag_Stmt,
|
||||||
|
^Assign_Stmt,
|
||||||
|
^Block_Stmt,
|
||||||
|
^If_Stmt,
|
||||||
|
^When_Stmt,
|
||||||
|
^Return_Stmt,
|
||||||
|
^Defer_Stmt,
|
||||||
|
^For_Stmt,
|
||||||
|
^Range_Stmt,
|
||||||
|
^Inline_Range_Stmt,
|
||||||
|
^Case_Clause,
|
||||||
|
^Switch_Stmt,
|
||||||
|
^Type_Switch_Stmt,
|
||||||
|
^Branch_Stmt,
|
||||||
|
^Using_Stmt,
|
||||||
|
|
||||||
|
^Bad_Decl,
|
||||||
|
^Value_Decl,
|
||||||
|
^Package_Decl,
|
||||||
|
^Import_Decl,
|
||||||
|
^Foreign_Block_Decl,
|
||||||
|
^Foreign_Import_Decl,
|
||||||
|
|
||||||
|
^Attribute,
|
||||||
|
^Field,
|
||||||
|
^Field_List,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Any_Expr :: union {
|
||||||
|
^Bad_Expr,
|
||||||
|
^Ident,
|
||||||
|
^Implicit,
|
||||||
|
^Undef,
|
||||||
|
^Basic_Lit,
|
||||||
|
^Basic_Directive,
|
||||||
|
^Ellipsis,
|
||||||
|
^Proc_Lit,
|
||||||
|
^Comp_Lit,
|
||||||
|
^Tag_Expr,
|
||||||
|
^Unary_Expr,
|
||||||
|
^Binary_Expr,
|
||||||
|
^Paren_Expr,
|
||||||
|
^Selector_Expr,
|
||||||
|
^Implicit_Selector_Expr,
|
||||||
|
^Selector_Call_Expr,
|
||||||
|
^Index_Expr,
|
||||||
|
^Deref_Expr,
|
||||||
|
^Slice_Expr,
|
||||||
|
^Matrix_Index_Expr,
|
||||||
|
^Call_Expr,
|
||||||
|
^Field_Value,
|
||||||
|
^Ternary_If_Expr,
|
||||||
|
^Ternary_When_Expr,
|
||||||
|
^Or_Else_Expr,
|
||||||
|
^Or_Return_Expr,
|
||||||
|
^Type_Assertion,
|
||||||
|
^Type_Cast,
|
||||||
|
^Auto_Cast,
|
||||||
|
^Inline_Asm_Expr,
|
||||||
|
|
||||||
|
^Proc_Group,
|
||||||
|
|
||||||
|
^Typeid_Type,
|
||||||
|
^Helper_Type,
|
||||||
|
^Distinct_Type,
|
||||||
|
^Poly_Type,
|
||||||
|
^Proc_Type,
|
||||||
|
^Pointer_Type,
|
||||||
|
^Multi_Pointer_Type,
|
||||||
|
^Array_Type,
|
||||||
|
^Dynamic_Array_Type,
|
||||||
|
^Struct_Type,
|
||||||
|
^Union_Type,
|
||||||
|
^Enum_Type,
|
||||||
|
^Bit_Set_Type,
|
||||||
|
^Map_Type,
|
||||||
|
^Relative_Type,
|
||||||
|
^Matrix_Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Any_Stmt :: union {
|
||||||
|
^Bad_Stmt,
|
||||||
|
^Empty_Stmt,
|
||||||
|
^Expr_Stmt,
|
||||||
|
^Tag_Stmt,
|
||||||
|
^Assign_Stmt,
|
||||||
|
^Block_Stmt,
|
||||||
|
^If_Stmt,
|
||||||
|
^When_Stmt,
|
||||||
|
^Return_Stmt,
|
||||||
|
^Defer_Stmt,
|
||||||
|
^For_Stmt,
|
||||||
|
^Range_Stmt,
|
||||||
|
^Inline_Range_Stmt,
|
||||||
|
^Case_Clause,
|
||||||
|
^Switch_Stmt,
|
||||||
|
^Type_Switch_Stmt,
|
||||||
|
^Branch_Stmt,
|
||||||
|
^Using_Stmt,
|
||||||
|
|
||||||
|
^Bad_Decl,
|
||||||
|
^Value_Decl,
|
||||||
|
^Package_Decl,
|
||||||
|
^Import_Decl,
|
||||||
|
^Foreign_Block_Decl,
|
||||||
|
^Foreign_Import_Decl,
|
||||||
|
}
|
||||||
|
|||||||
+117
-83
@@ -1,16 +1,25 @@
|
|||||||
package odin_ast
|
package odin_ast
|
||||||
|
|
||||||
|
import "core:intrinsics"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
import "core:reflect"
|
||||||
import "core:odin/tokenizer"
|
import "core:odin/tokenizer"
|
||||||
|
_ :: intrinsics
|
||||||
|
|
||||||
new :: proc($T: typeid, pos, end: tokenizer.Pos) -> ^T {
|
new :: proc($T: typeid, pos, end: tokenizer.Pos) -> ^T {
|
||||||
n, _ := mem.new(T)
|
n, _ := mem.new(T)
|
||||||
n.pos = pos
|
n.pos = pos
|
||||||
n.end = end
|
n.end = end
|
||||||
n.derived = n^
|
n.derived = n
|
||||||
base: ^Node = n // dummy check
|
base: ^Node = n // dummy check
|
||||||
_ = base // "Use" type to make -vet happy
|
_ = base // "Use" type to make -vet happy
|
||||||
|
when intrinsics.type_has_field(T, "derived_expr") {
|
||||||
|
n.derived_expr = n
|
||||||
|
}
|
||||||
|
when intrinsics.type_has_field(T, "derived_stmt") {
|
||||||
|
n.derived_stmt = n
|
||||||
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,232 +68,257 @@ clone_node :: proc(node: ^Node) -> ^Node {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
size := size_of(Node)
|
size := size_of(Node)
|
||||||
align := align_of(Node)
|
align := align_of(Node)
|
||||||
ti := type_info_of(node.derived.id)
|
ti := reflect.union_variant_type_info(node.derived)
|
||||||
if ti != nil {
|
if ti != nil {
|
||||||
size = ti.size
|
elem := ti.variant.(reflect.Type_Info_Pointer).elem
|
||||||
align = ti.align
|
size = elem.size
|
||||||
|
align = elem.align
|
||||||
}
|
}
|
||||||
|
|
||||||
switch in node.derived {
|
#partial switch in node.derived {
|
||||||
case Package, File:
|
case ^Package, ^File:
|
||||||
panic("Cannot clone this node type")
|
panic("Cannot clone this node type")
|
||||||
}
|
}
|
||||||
|
|
||||||
res := cast(^Node)mem.alloc(size, align)
|
res := cast(^Node)mem.alloc(size, align)
|
||||||
src: rawptr = node
|
src: rawptr = node
|
||||||
if node.derived != nil {
|
if node.derived != nil {
|
||||||
src = node.derived.data
|
src = (^rawptr)(&node.derived)^
|
||||||
}
|
}
|
||||||
mem.copy(res, src, size)
|
mem.copy(res, src, size)
|
||||||
res.derived.data = rawptr(res)
|
res_ptr_any: any
|
||||||
res.derived.id = node.derived.id
|
res_ptr_any.data = &res
|
||||||
|
res_ptr_any.id = ti.id
|
||||||
|
|
||||||
switch r in &res.derived {
|
reflect.set_union_value(res.derived, res_ptr_any)
|
||||||
case Bad_Expr:
|
|
||||||
case Ident:
|
|
||||||
case Implicit:
|
|
||||||
case Undef:
|
|
||||||
case Basic_Lit:
|
|
||||||
|
|
||||||
case Ellipsis:
|
res_ptr := reflect.deref(res_ptr_any)
|
||||||
|
|
||||||
|
if de := reflect.struct_field_value_by_name(res_ptr, "derived_expr", true); de != nil {
|
||||||
|
reflect.set_union_value(de, res_ptr_any)
|
||||||
|
}
|
||||||
|
if ds := reflect.struct_field_value_by_name(res_ptr, "derived_stmt", true); ds != nil {
|
||||||
|
reflect.set_union_value(ds, res_ptr_any)
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.derived != nil do switch r in res.derived {
|
||||||
|
case ^Package, ^File:
|
||||||
|
case ^Bad_Expr:
|
||||||
|
case ^Ident:
|
||||||
|
case ^Implicit:
|
||||||
|
case ^Undef:
|
||||||
|
case ^Basic_Lit:
|
||||||
|
case ^Basic_Directive:
|
||||||
|
case ^Comment_Group:
|
||||||
|
|
||||||
|
case ^Ellipsis:
|
||||||
r.expr = clone(r.expr)
|
r.expr = clone(r.expr)
|
||||||
case Proc_Lit:
|
case ^Proc_Lit:
|
||||||
r.type = auto_cast clone(r.type)
|
r.type = auto_cast clone(r.type)
|
||||||
r.body = clone(r.body)
|
r.body = clone(r.body)
|
||||||
case Comp_Lit:
|
case ^Comp_Lit:
|
||||||
r.type = clone(r.type)
|
r.type = clone(r.type)
|
||||||
r.elems = clone(r.elems)
|
r.elems = clone(r.elems)
|
||||||
|
|
||||||
case Tag_Expr:
|
case ^Tag_Expr:
|
||||||
r.expr = clone(r.expr)
|
r.expr = clone(r.expr)
|
||||||
case Unary_Expr:
|
case ^Unary_Expr:
|
||||||
r.expr = clone(r.expr)
|
r.expr = clone(r.expr)
|
||||||
case Binary_Expr:
|
case ^Binary_Expr:
|
||||||
r.left = clone(r.left)
|
r.left = clone(r.left)
|
||||||
r.right = clone(r.right)
|
r.right = clone(r.right)
|
||||||
case Paren_Expr:
|
case ^Paren_Expr:
|
||||||
r.expr = clone(r.expr)
|
r.expr = clone(r.expr)
|
||||||
case Selector_Expr:
|
case ^Selector_Expr:
|
||||||
r.expr = clone(r.expr)
|
r.expr = clone(r.expr)
|
||||||
r.field = auto_cast clone(r.field)
|
r.field = auto_cast clone(r.field)
|
||||||
case Implicit_Selector_Expr:
|
case ^Implicit_Selector_Expr:
|
||||||
r.field = auto_cast clone(r.field)
|
r.field = auto_cast clone(r.field)
|
||||||
case Selector_Call_Expr:
|
case ^Selector_Call_Expr:
|
||||||
r.expr = clone(r.expr)
|
r.expr = clone(r.expr)
|
||||||
r.call = auto_cast clone(r.call)
|
r.call = auto_cast clone(r.call)
|
||||||
case Index_Expr:
|
case ^Index_Expr:
|
||||||
r.expr = clone(r.expr)
|
r.expr = clone(r.expr)
|
||||||
r.index = clone(r.index)
|
r.index = clone(r.index)
|
||||||
case Matrix_Index_Expr:
|
case ^Matrix_Index_Expr:
|
||||||
r.expr = clone(r.expr)
|
r.expr = clone(r.expr)
|
||||||
r.row_index = clone(r.row_index)
|
r.row_index = clone(r.row_index)
|
||||||
r.column_index = clone(r.column_index)
|
r.column_index = clone(r.column_index)
|
||||||
case Deref_Expr:
|
case ^Deref_Expr:
|
||||||
r.expr = clone(r.expr)
|
r.expr = clone(r.expr)
|
||||||
case Slice_Expr:
|
case ^Slice_Expr:
|
||||||
r.expr = clone(r.expr)
|
r.expr = clone(r.expr)
|
||||||
r.low = clone(r.low)
|
r.low = clone(r.low)
|
||||||
r.high = clone(r.high)
|
r.high = clone(r.high)
|
||||||
case Call_Expr:
|
case ^Call_Expr:
|
||||||
r.expr = clone(r.expr)
|
r.expr = clone(r.expr)
|
||||||
r.args = clone(r.args)
|
r.args = clone(r.args)
|
||||||
case Field_Value:
|
case ^Field_Value:
|
||||||
r.field = clone(r.field)
|
r.field = clone(r.field)
|
||||||
r.value = clone(r.value)
|
r.value = clone(r.value)
|
||||||
case Ternary_If_Expr:
|
case ^Ternary_If_Expr:
|
||||||
r.x = clone(r.x)
|
r.x = clone(r.x)
|
||||||
r.cond = clone(r.cond)
|
r.cond = clone(r.cond)
|
||||||
r.y = clone(r.y)
|
r.y = clone(r.y)
|
||||||
case Ternary_When_Expr:
|
case ^Ternary_When_Expr:
|
||||||
r.x = clone(r.x)
|
r.x = clone(r.x)
|
||||||
r.cond = clone(r.cond)
|
r.cond = clone(r.cond)
|
||||||
r.y = clone(r.y)
|
r.y = clone(r.y)
|
||||||
case Or_Else_Expr:
|
case ^Or_Else_Expr:
|
||||||
r.x = clone(r.x)
|
r.x = clone(r.x)
|
||||||
r.y = clone(r.y)
|
r.y = clone(r.y)
|
||||||
case Or_Return_Expr:
|
case ^Or_Return_Expr:
|
||||||
r.expr = clone(r.expr)
|
r.expr = clone(r.expr)
|
||||||
case Type_Assertion:
|
case ^Type_Assertion:
|
||||||
r.expr = clone(r.expr)
|
r.expr = clone(r.expr)
|
||||||
r.type = clone(r.type)
|
r.type = clone(r.type)
|
||||||
case Type_Cast:
|
case ^Type_Cast:
|
||||||
r.type = clone(r.type)
|
r.type = clone(r.type)
|
||||||
r.expr = clone(r.expr)
|
r.expr = clone(r.expr)
|
||||||
case Auto_Cast:
|
case ^Auto_Cast:
|
||||||
r.expr = clone(r.expr)
|
r.expr = clone(r.expr)
|
||||||
case Inline_Asm_Expr:
|
case ^Inline_Asm_Expr:
|
||||||
r.param_types = clone(r.param_types)
|
r.param_types = clone(r.param_types)
|
||||||
r.return_type = clone(r.return_type)
|
r.return_type = clone(r.return_type)
|
||||||
r.constraints_string = clone(r.constraints_string)
|
r.constraints_string = clone(r.constraints_string)
|
||||||
r.asm_string = clone(r.asm_string)
|
r.asm_string = clone(r.asm_string)
|
||||||
|
|
||||||
case Bad_Stmt:
|
case ^Bad_Stmt:
|
||||||
// empty
|
// empty
|
||||||
case Empty_Stmt:
|
case ^Empty_Stmt:
|
||||||
// empty
|
// empty
|
||||||
case Expr_Stmt:
|
case ^Expr_Stmt:
|
||||||
r.expr = clone(r.expr)
|
r.expr = clone(r.expr)
|
||||||
case Tag_Stmt:
|
case ^Tag_Stmt:
|
||||||
r.stmt = clone(r.stmt)
|
r.stmt = clone(r.stmt)
|
||||||
|
|
||||||
case Assign_Stmt:
|
case ^Assign_Stmt:
|
||||||
r.lhs = clone(r.lhs)
|
r.lhs = clone(r.lhs)
|
||||||
r.rhs = clone(r.rhs)
|
r.rhs = clone(r.rhs)
|
||||||
case Block_Stmt:
|
case ^Block_Stmt:
|
||||||
r.label = clone(r.label)
|
r.label = clone(r.label)
|
||||||
r.stmts = clone(r.stmts)
|
r.stmts = clone(r.stmts)
|
||||||
case If_Stmt:
|
case ^If_Stmt:
|
||||||
r.label = clone(r.label)
|
r.label = clone(r.label)
|
||||||
r.init = clone(r.init)
|
r.init = clone(r.init)
|
||||||
r.cond = clone(r.cond)
|
r.cond = clone(r.cond)
|
||||||
r.body = clone(r.body)
|
r.body = clone(r.body)
|
||||||
r.else_stmt = clone(r.else_stmt)
|
r.else_stmt = clone(r.else_stmt)
|
||||||
case When_Stmt:
|
case ^When_Stmt:
|
||||||
r.cond = clone(r.cond)
|
r.cond = clone(r.cond)
|
||||||
r.body = clone(r.body)
|
r.body = clone(r.body)
|
||||||
r.else_stmt = clone(r.else_stmt)
|
r.else_stmt = clone(r.else_stmt)
|
||||||
case Return_Stmt:
|
case ^Return_Stmt:
|
||||||
r.results = clone(r.results)
|
r.results = clone(r.results)
|
||||||
case Defer_Stmt:
|
case ^Defer_Stmt:
|
||||||
r.stmt = clone(r.stmt)
|
r.stmt = clone(r.stmt)
|
||||||
case For_Stmt:
|
case ^For_Stmt:
|
||||||
r.label = clone(r.label)
|
r.label = clone(r.label)
|
||||||
r.init = clone(r.init)
|
r.init = clone(r.init)
|
||||||
r.cond = clone(r.cond)
|
r.cond = clone(r.cond)
|
||||||
r.post = clone(r.post)
|
r.post = clone(r.post)
|
||||||
r.body = clone(r.body)
|
r.body = clone(r.body)
|
||||||
case Range_Stmt:
|
case ^Range_Stmt:
|
||||||
r.label = clone(r.label)
|
r.label = clone(r.label)
|
||||||
r.vals = clone(r.vals)
|
r.vals = clone(r.vals)
|
||||||
r.expr = clone(r.expr)
|
r.expr = clone(r.expr)
|
||||||
r.body = clone(r.body)
|
r.body = clone(r.body)
|
||||||
case Case_Clause:
|
case ^Inline_Range_Stmt:
|
||||||
|
r.label = clone(r.label)
|
||||||
|
r.val0 = clone(r.val0)
|
||||||
|
r.val1 = clone(r.val1)
|
||||||
|
r.expr = clone(r.expr)
|
||||||
|
r.body = clone(r.body)
|
||||||
|
case ^Case_Clause:
|
||||||
r.list = clone(r.list)
|
r.list = clone(r.list)
|
||||||
r.body = clone(r.body)
|
r.body = clone(r.body)
|
||||||
case Switch_Stmt:
|
case ^Switch_Stmt:
|
||||||
r.label = clone(r.label)
|
r.label = clone(r.label)
|
||||||
r.init = clone(r.init)
|
r.init = clone(r.init)
|
||||||
r.cond = clone(r.cond)
|
r.cond = clone(r.cond)
|
||||||
r.body = clone(r.body)
|
r.body = clone(r.body)
|
||||||
case Type_Switch_Stmt:
|
case ^Type_Switch_Stmt:
|
||||||
r.label = clone(r.label)
|
r.label = clone(r.label)
|
||||||
r.tag = clone(r.tag)
|
r.tag = clone(r.tag)
|
||||||
r.expr = clone(r.expr)
|
r.expr = clone(r.expr)
|
||||||
r.body = clone(r.body)
|
r.body = clone(r.body)
|
||||||
case Branch_Stmt:
|
case ^Branch_Stmt:
|
||||||
r.label = auto_cast clone(r.label)
|
r.label = auto_cast clone(r.label)
|
||||||
case Using_Stmt:
|
case ^Using_Stmt:
|
||||||
r.list = clone(r.list)
|
r.list = clone(r.list)
|
||||||
case Bad_Decl:
|
case ^Bad_Decl:
|
||||||
case Value_Decl:
|
case ^Value_Decl:
|
||||||
r.attributes = clone(r.attributes)
|
r.attributes = clone(r.attributes)
|
||||||
r.names = clone(r.names)
|
r.names = clone(r.names)
|
||||||
r.type = clone(r.type)
|
r.type = clone(r.type)
|
||||||
r.values = clone(r.values)
|
r.values = clone(r.values)
|
||||||
case Package_Decl:
|
case ^Package_Decl:
|
||||||
case Import_Decl:
|
case ^Import_Decl:
|
||||||
case Foreign_Block_Decl:
|
case ^Foreign_Block_Decl:
|
||||||
r.attributes = clone(r.attributes)
|
r.attributes = clone(r.attributes)
|
||||||
r.foreign_library = clone(r.foreign_library)
|
r.foreign_library = clone(r.foreign_library)
|
||||||
r.body = clone(r.body)
|
r.body = clone(r.body)
|
||||||
case Foreign_Import_Decl:
|
case ^Foreign_Import_Decl:
|
||||||
r.name = auto_cast clone(r.name)
|
r.name = auto_cast clone(r.name)
|
||||||
case Proc_Group:
|
case ^Proc_Group:
|
||||||
r.args = clone(r.args)
|
r.args = clone(r.args)
|
||||||
case Attribute:
|
case ^Attribute:
|
||||||
r.elems = clone(r.elems)
|
r.elems = clone(r.elems)
|
||||||
case Field:
|
case ^Field:
|
||||||
r.names = clone(r.names)
|
r.names = clone(r.names)
|
||||||
r.type = clone(r.type)
|
r.type = clone(r.type)
|
||||||
r.default_value = clone(r.default_value)
|
r.default_value = clone(r.default_value)
|
||||||
case Field_List:
|
case ^Field_List:
|
||||||
r.list = clone(r.list)
|
r.list = clone(r.list)
|
||||||
case Typeid_Type:
|
case ^Typeid_Type:
|
||||||
r.specialization = clone(r.specialization)
|
r.specialization = clone(r.specialization)
|
||||||
case Helper_Type:
|
case ^Helper_Type:
|
||||||
r.type = clone(r.type)
|
r.type = clone(r.type)
|
||||||
case Distinct_Type:
|
case ^Distinct_Type:
|
||||||
r.type = clone(r.type)
|
r.type = clone(r.type)
|
||||||
case Poly_Type:
|
case ^Poly_Type:
|
||||||
r.type = auto_cast clone(r.type)
|
r.type = auto_cast clone(r.type)
|
||||||
r.specialization = clone(r.specialization)
|
r.specialization = clone(r.specialization)
|
||||||
case Proc_Type:
|
case ^Proc_Type:
|
||||||
r.params = auto_cast clone(r.params)
|
r.params = auto_cast clone(r.params)
|
||||||
r.results = auto_cast clone(r.results)
|
r.results = auto_cast clone(r.results)
|
||||||
case Pointer_Type:
|
case ^Pointer_Type:
|
||||||
r.elem = clone(r.elem)
|
r.elem = clone(r.elem)
|
||||||
case Multi_Pointer_Type:
|
case ^Multi_Pointer_Type:
|
||||||
r.elem = clone(r.elem)
|
r.elem = clone(r.elem)
|
||||||
case Array_Type:
|
case ^Array_Type:
|
||||||
r.len = clone(r.len)
|
r.len = clone(r.len)
|
||||||
r.elem = clone(r.elem)
|
r.elem = clone(r.elem)
|
||||||
case Dynamic_Array_Type:
|
case ^Dynamic_Array_Type:
|
||||||
r.elem = clone(r.elem)
|
r.elem = clone(r.elem)
|
||||||
case Struct_Type:
|
case ^Struct_Type:
|
||||||
r.poly_params = auto_cast clone(r.poly_params)
|
r.poly_params = auto_cast clone(r.poly_params)
|
||||||
r.align = clone(r.align)
|
r.align = clone(r.align)
|
||||||
r.fields = auto_cast clone(r.fields)
|
r.fields = auto_cast clone(r.fields)
|
||||||
case Union_Type:
|
case ^Union_Type:
|
||||||
r.poly_params = auto_cast clone(r.poly_params)
|
r.poly_params = auto_cast clone(r.poly_params)
|
||||||
r.align = clone(r.align)
|
r.align = clone(r.align)
|
||||||
r.variants = clone(r.variants)
|
r.variants = clone(r.variants)
|
||||||
case Enum_Type:
|
case ^Enum_Type:
|
||||||
r.base_type = clone(r.base_type)
|
r.base_type = clone(r.base_type)
|
||||||
r.fields = clone(r.fields)
|
r.fields = clone(r.fields)
|
||||||
case Bit_Set_Type:
|
case ^Bit_Set_Type:
|
||||||
r.elem = clone(r.elem)
|
r.elem = clone(r.elem)
|
||||||
r.underlying = clone(r.underlying)
|
r.underlying = clone(r.underlying)
|
||||||
case Map_Type:
|
case ^Map_Type:
|
||||||
r.key = clone(r.key)
|
r.key = clone(r.key)
|
||||||
r.value = clone(r.value)
|
r.value = clone(r.value)
|
||||||
case Matrix_Type:
|
case ^Matrix_Type:
|
||||||
r.row_count = clone(r.row_count)
|
r.row_count = clone(r.row_count)
|
||||||
r.column_count = clone(r.column_count)
|
r.column_count = clone(r.column_count)
|
||||||
r.elem = clone(r.elem)
|
r.elem = clone(r.elem)
|
||||||
|
case ^Relative_Type:
|
||||||
|
r.tag = clone(r.tag)
|
||||||
|
r.type = clone(r.type)
|
||||||
case:
|
case:
|
||||||
fmt.panicf("Unhandled node kind: %T", r)
|
fmt.panicf("Unhandled node kind: %v", r)
|
||||||
}
|
}
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|||||||
+77
-77
@@ -59,64 +59,64 @@ walk :: proc(v: ^Visitor, node: ^Node) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch n in &node.derived {
|
switch n in &node.derived {
|
||||||
case File:
|
case ^File:
|
||||||
if n.docs != nil {
|
if n.docs != nil {
|
||||||
walk(v, n.docs)
|
walk(v, n.docs)
|
||||||
}
|
}
|
||||||
walk_stmt_list(v, n.decls[:])
|
walk_stmt_list(v, n.decls[:])
|
||||||
case Package:
|
case ^Package:
|
||||||
for _, f in n.files {
|
for _, f in n.files {
|
||||||
walk(v, f)
|
walk(v, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
case Comment_Group:
|
case ^Comment_Group:
|
||||||
// empty
|
// empty
|
||||||
case Bad_Expr:
|
case ^Bad_Expr:
|
||||||
case Ident:
|
case ^Ident:
|
||||||
case Implicit:
|
case ^Implicit:
|
||||||
case Undef:
|
case ^Undef:
|
||||||
case Basic_Lit:
|
case ^Basic_Lit:
|
||||||
case Basic_Directive:
|
case ^Basic_Directive:
|
||||||
case Ellipsis:
|
case ^Ellipsis:
|
||||||
if n.expr != nil {
|
if n.expr != nil {
|
||||||
walk(v, n.expr)
|
walk(v, n.expr)
|
||||||
}
|
}
|
||||||
case Proc_Lit:
|
case ^Proc_Lit:
|
||||||
walk(v, n.type)
|
walk(v, n.type)
|
||||||
walk(v, n.body)
|
walk(v, n.body)
|
||||||
walk_expr_list(v, n.where_clauses)
|
walk_expr_list(v, n.where_clauses)
|
||||||
case Comp_Lit:
|
case ^Comp_Lit:
|
||||||
if n.type != nil {
|
if n.type != nil {
|
||||||
walk(v, n.type)
|
walk(v, n.type)
|
||||||
}
|
}
|
||||||
walk_expr_list(v, n.elems)
|
walk_expr_list(v, n.elems)
|
||||||
case Tag_Expr:
|
case ^Tag_Expr:
|
||||||
walk(v, n.expr)
|
walk(v, n.expr)
|
||||||
case Unary_Expr:
|
case ^Unary_Expr:
|
||||||
walk(v, n.expr)
|
walk(v, n.expr)
|
||||||
case Binary_Expr:
|
case ^Binary_Expr:
|
||||||
walk(v, n.left)
|
walk(v, n.left)
|
||||||
walk(v, n.right)
|
walk(v, n.right)
|
||||||
case Paren_Expr:
|
case ^Paren_Expr:
|
||||||
walk(v, n.expr)
|
walk(v, n.expr)
|
||||||
case Selector_Expr:
|
case ^Selector_Expr:
|
||||||
walk(v, n.expr)
|
walk(v, n.expr)
|
||||||
walk(v, n.field)
|
walk(v, n.field)
|
||||||
case Implicit_Selector_Expr:
|
case ^Implicit_Selector_Expr:
|
||||||
walk(v, n.field)
|
walk(v, n.field)
|
||||||
case Selector_Call_Expr:
|
case ^Selector_Call_Expr:
|
||||||
walk(v, n.expr)
|
walk(v, n.expr)
|
||||||
walk(v, n.call)
|
walk(v, n.call)
|
||||||
case Index_Expr:
|
case ^Index_Expr:
|
||||||
walk(v, n.expr)
|
walk(v, n.expr)
|
||||||
walk(v, n.index)
|
walk(v, n.index)
|
||||||
case Matrix_Index_Expr:
|
case ^Matrix_Index_Expr:
|
||||||
walk(v, n.expr)
|
walk(v, n.expr)
|
||||||
walk(v, n.row_index)
|
walk(v, n.row_index)
|
||||||
walk(v, n.column_index)
|
walk(v, n.column_index)
|
||||||
case Deref_Expr:
|
case ^Deref_Expr:
|
||||||
walk(v, n.expr)
|
walk(v, n.expr)
|
||||||
case Slice_Expr:
|
case ^Slice_Expr:
|
||||||
walk(v, n.expr)
|
walk(v, n.expr)
|
||||||
if n.low != nil {
|
if n.low != nil {
|
||||||
walk(v, n.low)
|
walk(v, n.low)
|
||||||
@@ -124,57 +124,57 @@ walk :: proc(v: ^Visitor, node: ^Node) {
|
|||||||
if n.high != nil {
|
if n.high != nil {
|
||||||
walk(v, n.high)
|
walk(v, n.high)
|
||||||
}
|
}
|
||||||
case Call_Expr:
|
case ^Call_Expr:
|
||||||
walk(v, n.expr)
|
walk(v, n.expr)
|
||||||
walk_expr_list(v, n.args)
|
walk_expr_list(v, n.args)
|
||||||
case Field_Value:
|
case ^Field_Value:
|
||||||
walk(v, n.field)
|
walk(v, n.field)
|
||||||
walk(v, n.value)
|
walk(v, n.value)
|
||||||
case Ternary_If_Expr:
|
case ^Ternary_If_Expr:
|
||||||
walk(v, n.x)
|
walk(v, n.x)
|
||||||
walk(v, n.cond)
|
walk(v, n.cond)
|
||||||
walk(v, n.y)
|
walk(v, n.y)
|
||||||
case Ternary_When_Expr:
|
case ^Ternary_When_Expr:
|
||||||
walk(v, n.x)
|
walk(v, n.x)
|
||||||
walk(v, n.cond)
|
walk(v, n.cond)
|
||||||
walk(v, n.y)
|
walk(v, n.y)
|
||||||
case Or_Else_Expr:
|
case ^Or_Else_Expr:
|
||||||
walk(v, n.x)
|
walk(v, n.x)
|
||||||
walk(v, n.y)
|
walk(v, n.y)
|
||||||
case Or_Return_Expr:
|
case ^Or_Return_Expr:
|
||||||
walk(v, n.expr)
|
walk(v, n.expr)
|
||||||
case Type_Assertion:
|
case ^Type_Assertion:
|
||||||
walk(v, n.expr)
|
walk(v, n.expr)
|
||||||
if n.type != nil {
|
if n.type != nil {
|
||||||
walk(v, n.type)
|
walk(v, n.type)
|
||||||
}
|
}
|
||||||
case Type_Cast:
|
case ^Type_Cast:
|
||||||
walk(v, n.type)
|
walk(v, n.type)
|
||||||
walk(v, n.expr)
|
walk(v, n.expr)
|
||||||
case Auto_Cast:
|
case ^Auto_Cast:
|
||||||
walk(v, n.expr)
|
walk(v, n.expr)
|
||||||
case Inline_Asm_Expr:
|
case ^Inline_Asm_Expr:
|
||||||
walk_expr_list(v, n.param_types)
|
walk_expr_list(v, n.param_types)
|
||||||
walk(v, n.return_type)
|
walk(v, n.return_type)
|
||||||
walk(v, n.constraints_string)
|
walk(v, n.constraints_string)
|
||||||
walk(v, n.asm_string)
|
walk(v, n.asm_string)
|
||||||
|
|
||||||
|
|
||||||
case Bad_Stmt:
|
case ^Bad_Stmt:
|
||||||
case Empty_Stmt:
|
case ^Empty_Stmt:
|
||||||
case Expr_Stmt:
|
case ^Expr_Stmt:
|
||||||
walk(v, n.expr)
|
walk(v, n.expr)
|
||||||
case Tag_Stmt:
|
case ^Tag_Stmt:
|
||||||
walk(v, n.stmt)
|
walk(v, n.stmt)
|
||||||
case Assign_Stmt:
|
case ^Assign_Stmt:
|
||||||
walk_expr_list(v, n.lhs)
|
walk_expr_list(v, n.lhs)
|
||||||
walk_expr_list(v, n.rhs)
|
walk_expr_list(v, n.rhs)
|
||||||
case Block_Stmt:
|
case ^Block_Stmt:
|
||||||
if n.label != nil {
|
if n.label != nil {
|
||||||
walk(v, n.label)
|
walk(v, n.label)
|
||||||
}
|
}
|
||||||
walk_stmt_list(v, n.stmts)
|
walk_stmt_list(v, n.stmts)
|
||||||
case If_Stmt:
|
case ^If_Stmt:
|
||||||
if n.label != nil {
|
if n.label != nil {
|
||||||
walk(v, n.label)
|
walk(v, n.label)
|
||||||
}
|
}
|
||||||
@@ -186,17 +186,17 @@ walk :: proc(v: ^Visitor, node: ^Node) {
|
|||||||
if n.else_stmt != nil {
|
if n.else_stmt != nil {
|
||||||
walk(v, n.else_stmt)
|
walk(v, n.else_stmt)
|
||||||
}
|
}
|
||||||
case When_Stmt:
|
case ^When_Stmt:
|
||||||
walk(v, n.cond)
|
walk(v, n.cond)
|
||||||
walk(v, n.body)
|
walk(v, n.body)
|
||||||
if n.else_stmt != nil {
|
if n.else_stmt != nil {
|
||||||
walk(v, n.else_stmt)
|
walk(v, n.else_stmt)
|
||||||
}
|
}
|
||||||
case Return_Stmt:
|
case ^Return_Stmt:
|
||||||
walk_expr_list(v, n.results)
|
walk_expr_list(v, n.results)
|
||||||
case Defer_Stmt:
|
case ^Defer_Stmt:
|
||||||
walk(v, n.stmt)
|
walk(v, n.stmt)
|
||||||
case For_Stmt:
|
case ^For_Stmt:
|
||||||
if n.label != nil {
|
if n.label != nil {
|
||||||
walk(v, n.label)
|
walk(v, n.label)
|
||||||
}
|
}
|
||||||
@@ -210,7 +210,7 @@ walk :: proc(v: ^Visitor, node: ^Node) {
|
|||||||
walk(v, n.post)
|
walk(v, n.post)
|
||||||
}
|
}
|
||||||
walk(v, n.body)
|
walk(v, n.body)
|
||||||
case Range_Stmt:
|
case ^Range_Stmt:
|
||||||
if n.label != nil {
|
if n.label != nil {
|
||||||
walk(v, n.label)
|
walk(v, n.label)
|
||||||
}
|
}
|
||||||
@@ -221,7 +221,7 @@ walk :: proc(v: ^Visitor, node: ^Node) {
|
|||||||
}
|
}
|
||||||
walk(v, n.expr)
|
walk(v, n.expr)
|
||||||
walk(v, n.body)
|
walk(v, n.body)
|
||||||
case Inline_Range_Stmt:
|
case ^Inline_Range_Stmt:
|
||||||
if n.label != nil {
|
if n.label != nil {
|
||||||
walk(v, n.label)
|
walk(v, n.label)
|
||||||
}
|
}
|
||||||
@@ -233,10 +233,10 @@ walk :: proc(v: ^Visitor, node: ^Node) {
|
|||||||
}
|
}
|
||||||
walk(v, n.expr)
|
walk(v, n.expr)
|
||||||
walk(v, n.body)
|
walk(v, n.body)
|
||||||
case Case_Clause:
|
case ^Case_Clause:
|
||||||
walk_expr_list(v, n.list)
|
walk_expr_list(v, n.list)
|
||||||
walk_stmt_list(v, n.body)
|
walk_stmt_list(v, n.body)
|
||||||
case Switch_Stmt:
|
case ^Switch_Stmt:
|
||||||
if n.label != nil {
|
if n.label != nil {
|
||||||
walk(v, n.label)
|
walk(v, n.label)
|
||||||
}
|
}
|
||||||
@@ -247,7 +247,7 @@ walk :: proc(v: ^Visitor, node: ^Node) {
|
|||||||
walk(v, n.cond)
|
walk(v, n.cond)
|
||||||
}
|
}
|
||||||
walk(v, n.body)
|
walk(v, n.body)
|
||||||
case Type_Switch_Stmt:
|
case ^Type_Switch_Stmt:
|
||||||
if n.label != nil {
|
if n.label != nil {
|
||||||
walk(v, n.label)
|
walk(v, n.label)
|
||||||
}
|
}
|
||||||
@@ -258,16 +258,16 @@ walk :: proc(v: ^Visitor, node: ^Node) {
|
|||||||
walk(v, n.expr)
|
walk(v, n.expr)
|
||||||
}
|
}
|
||||||
walk(v, n.body)
|
walk(v, n.body)
|
||||||
case Branch_Stmt:
|
case ^Branch_Stmt:
|
||||||
if n.label != nil {
|
if n.label != nil {
|
||||||
walk(v, n.label)
|
walk(v, n.label)
|
||||||
}
|
}
|
||||||
case Using_Stmt:
|
case ^Using_Stmt:
|
||||||
walk_expr_list(v, n.list)
|
walk_expr_list(v, n.list)
|
||||||
|
|
||||||
|
|
||||||
case Bad_Decl:
|
case ^Bad_Decl:
|
||||||
case Value_Decl:
|
case ^Value_Decl:
|
||||||
if n.docs != nil {
|
if n.docs != nil {
|
||||||
walk(v, n.docs)
|
walk(v, n.docs)
|
||||||
}
|
}
|
||||||
@@ -280,21 +280,21 @@ walk :: proc(v: ^Visitor, node: ^Node) {
|
|||||||
if n.comment != nil {
|
if n.comment != nil {
|
||||||
walk(v, n.comment)
|
walk(v, n.comment)
|
||||||
}
|
}
|
||||||
case Package_Decl:
|
case ^Package_Decl:
|
||||||
if n.docs != nil {
|
if n.docs != nil {
|
||||||
walk(v, n.docs)
|
walk(v, n.docs)
|
||||||
}
|
}
|
||||||
if n.comment != nil {
|
if n.comment != nil {
|
||||||
walk(v, n.comment)
|
walk(v, n.comment)
|
||||||
}
|
}
|
||||||
case Import_Decl:
|
case ^Import_Decl:
|
||||||
if n.docs != nil {
|
if n.docs != nil {
|
||||||
walk(v, n.docs)
|
walk(v, n.docs)
|
||||||
}
|
}
|
||||||
if n.comment != nil {
|
if n.comment != nil {
|
||||||
walk(v, n.comment)
|
walk(v, n.comment)
|
||||||
}
|
}
|
||||||
case Foreign_Block_Decl:
|
case ^Foreign_Block_Decl:
|
||||||
if n.docs != nil {
|
if n.docs != nil {
|
||||||
walk(v, n.docs)
|
walk(v, n.docs)
|
||||||
}
|
}
|
||||||
@@ -303,7 +303,7 @@ walk :: proc(v: ^Visitor, node: ^Node) {
|
|||||||
walk(v, n.foreign_library)
|
walk(v, n.foreign_library)
|
||||||
}
|
}
|
||||||
walk(v, n.body)
|
walk(v, n.body)
|
||||||
case Foreign_Import_Decl:
|
case ^Foreign_Import_Decl:
|
||||||
if n.docs != nil {
|
if n.docs != nil {
|
||||||
walk(v, n.docs)
|
walk(v, n.docs)
|
||||||
}
|
}
|
||||||
@@ -313,11 +313,11 @@ walk :: proc(v: ^Visitor, node: ^Node) {
|
|||||||
walk(v, n.comment)
|
walk(v, n.comment)
|
||||||
}
|
}
|
||||||
|
|
||||||
case Proc_Group:
|
case ^Proc_Group:
|
||||||
walk_expr_list(v, n.args)
|
walk_expr_list(v, n.args)
|
||||||
case Attribute:
|
case ^Attribute:
|
||||||
walk_expr_list(v, n.elems)
|
walk_expr_list(v, n.elems)
|
||||||
case Field:
|
case ^Field:
|
||||||
if n.docs != nil {
|
if n.docs != nil {
|
||||||
walk(v, n.docs)
|
walk(v, n.docs)
|
||||||
}
|
}
|
||||||
@@ -331,31 +331,31 @@ walk :: proc(v: ^Visitor, node: ^Node) {
|
|||||||
if n.comment != nil {
|
if n.comment != nil {
|
||||||
walk(v, n.comment)
|
walk(v, n.comment)
|
||||||
}
|
}
|
||||||
case Field_List:
|
case ^Field_List:
|
||||||
for x in n.list {
|
for x in n.list {
|
||||||
walk(v, x)
|
walk(v, x)
|
||||||
}
|
}
|
||||||
case Typeid_Type:
|
case ^Typeid_Type:
|
||||||
if n.specialization != nil {
|
if n.specialization != nil {
|
||||||
walk(v, n.specialization)
|
walk(v, n.specialization)
|
||||||
}
|
}
|
||||||
case Helper_Type:
|
case ^Helper_Type:
|
||||||
walk(v, n.type)
|
walk(v, n.type)
|
||||||
case Distinct_Type:
|
case ^Distinct_Type:
|
||||||
walk(v, n.type)
|
walk(v, n.type)
|
||||||
case Poly_Type:
|
case ^Poly_Type:
|
||||||
walk(v, n.type)
|
walk(v, n.type)
|
||||||
if n.specialization != nil {
|
if n.specialization != nil {
|
||||||
walk(v, n.specialization)
|
walk(v, n.specialization)
|
||||||
}
|
}
|
||||||
case Proc_Type:
|
case ^Proc_Type:
|
||||||
walk(v, n.params)
|
walk(v, n.params)
|
||||||
walk(v, n.results)
|
walk(v, n.results)
|
||||||
case Pointer_Type:
|
case ^Pointer_Type:
|
||||||
walk(v, n.elem)
|
walk(v, n.elem)
|
||||||
case Multi_Pointer_Type:
|
case ^Multi_Pointer_Type:
|
||||||
walk(v, n.elem)
|
walk(v, n.elem)
|
||||||
case Array_Type:
|
case ^Array_Type:
|
||||||
if n.tag != nil {
|
if n.tag != nil {
|
||||||
walk(v, n.tag)
|
walk(v, n.tag)
|
||||||
}
|
}
|
||||||
@@ -363,12 +363,12 @@ walk :: proc(v: ^Visitor, node: ^Node) {
|
|||||||
walk(v, n.len)
|
walk(v, n.len)
|
||||||
}
|
}
|
||||||
walk(v, n.elem)
|
walk(v, n.elem)
|
||||||
case Dynamic_Array_Type:
|
case ^Dynamic_Array_Type:
|
||||||
if n.tag != nil {
|
if n.tag != nil {
|
||||||
walk(v, n.tag)
|
walk(v, n.tag)
|
||||||
}
|
}
|
||||||
walk(v, n.elem)
|
walk(v, n.elem)
|
||||||
case Struct_Type:
|
case ^Struct_Type:
|
||||||
if n.poly_params != nil {
|
if n.poly_params != nil {
|
||||||
walk(v, n.poly_params)
|
walk(v, n.poly_params)
|
||||||
}
|
}
|
||||||
@@ -377,7 +377,7 @@ walk :: proc(v: ^Visitor, node: ^Node) {
|
|||||||
}
|
}
|
||||||
walk_expr_list(v, n.where_clauses)
|
walk_expr_list(v, n.where_clauses)
|
||||||
walk(v, n.fields)
|
walk(v, n.fields)
|
||||||
case Union_Type:
|
case ^Union_Type:
|
||||||
if n.poly_params != nil {
|
if n.poly_params != nil {
|
||||||
walk(v, n.poly_params)
|
walk(v, n.poly_params)
|
||||||
}
|
}
|
||||||
@@ -386,23 +386,23 @@ walk :: proc(v: ^Visitor, node: ^Node) {
|
|||||||
}
|
}
|
||||||
walk_expr_list(v, n.where_clauses)
|
walk_expr_list(v, n.where_clauses)
|
||||||
walk_expr_list(v, n.variants)
|
walk_expr_list(v, n.variants)
|
||||||
case Enum_Type:
|
case ^Enum_Type:
|
||||||
if n.base_type != nil {
|
if n.base_type != nil {
|
||||||
walk(v, n.base_type)
|
walk(v, n.base_type)
|
||||||
}
|
}
|
||||||
walk_expr_list(v, n.fields)
|
walk_expr_list(v, n.fields)
|
||||||
case Bit_Set_Type:
|
case ^Bit_Set_Type:
|
||||||
walk(v, n.elem)
|
walk(v, n.elem)
|
||||||
if n.underlying != nil {
|
if n.underlying != nil {
|
||||||
walk(v, n.underlying)
|
walk(v, n.underlying)
|
||||||
}
|
}
|
||||||
case Map_Type:
|
case ^Map_Type:
|
||||||
walk(v, n.key)
|
walk(v, n.key)
|
||||||
walk(v, n.value)
|
walk(v, n.value)
|
||||||
case Relative_Type:
|
case ^Relative_Type:
|
||||||
walk(v, n.tag)
|
walk(v, n.tag)
|
||||||
walk(v, n.type)
|
walk(v, n.type)
|
||||||
case Matrix_Type:
|
case ^Matrix_Type:
|
||||||
walk(v, n.row_count)
|
walk(v, n.row_count)
|
||||||
walk(v, n.column_count)
|
walk(v, n.column_count)
|
||||||
walk(v, n.elem)
|
walk(v, n.elem)
|
||||||
|
|||||||
@@ -195,10 +195,10 @@ parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool {
|
|||||||
for p.curr_tok.kind != .EOF {
|
for p.curr_tok.kind != .EOF {
|
||||||
stmt := parse_stmt(p)
|
stmt := parse_stmt(p)
|
||||||
if stmt != nil {
|
if stmt != nil {
|
||||||
if _, ok := stmt.derived.(ast.Empty_Stmt); !ok {
|
if _, ok := stmt.derived.(^ast.Empty_Stmt); !ok {
|
||||||
append(&p.file.decls, stmt)
|
append(&p.file.decls, stmt)
|
||||||
if es, es_ok := stmt.derived.(ast.Expr_Stmt); es_ok && es.expr != nil {
|
if es, es_ok := stmt.derived.(^ast.Expr_Stmt); es_ok && es.expr != nil {
|
||||||
if _, pl_ok := es.expr.derived.(ast.Proc_Lit); pl_ok {
|
if _, pl_ok := es.expr.derived.(^ast.Proc_Lit); pl_ok {
|
||||||
error(p, stmt.pos, "procedure literal evaluated but not used")
|
error(p, stmt.pos, "procedure literal evaluated but not used")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -459,7 +459,7 @@ is_blank_ident_token :: proc(tok: tokenizer.Token) -> bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
is_blank_ident_node :: proc(node: ^ast.Node) -> bool {
|
is_blank_ident_node :: proc(node: ^ast.Node) -> bool {
|
||||||
if ident, ok := node.derived.(ast.Ident); ok {
|
if ident, ok := node.derived.(^ast.Ident); ok {
|
||||||
return is_blank_ident(ident.name)
|
return is_blank_ident(ident.name)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@@ -502,34 +502,34 @@ is_semicolon_optional_for_node :: proc(p: ^Parser, node: ^ast.Node) -> bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
switch n in node.derived {
|
#partial switch n in node.derived {
|
||||||
case ast.Empty_Stmt, ast.Block_Stmt:
|
case ^ast.Empty_Stmt, ^ast.Block_Stmt:
|
||||||
return true
|
return true
|
||||||
|
|
||||||
case ast.If_Stmt, ast.When_Stmt,
|
case ^ast.If_Stmt, ^ast.When_Stmt,
|
||||||
ast.For_Stmt, ast.Range_Stmt, ast.Inline_Range_Stmt,
|
^ast.For_Stmt, ^ast.Range_Stmt, ^ast.Inline_Range_Stmt,
|
||||||
ast.Switch_Stmt, ast.Type_Switch_Stmt:
|
^ast.Switch_Stmt, ^ast.Type_Switch_Stmt:
|
||||||
return true
|
return true
|
||||||
|
|
||||||
case ast.Helper_Type:
|
case ^ast.Helper_Type:
|
||||||
return is_semicolon_optional_for_node(p, n.type)
|
return is_semicolon_optional_for_node(p, n.type)
|
||||||
case ast.Distinct_Type:
|
case ^ast.Distinct_Type:
|
||||||
return is_semicolon_optional_for_node(p, n.type)
|
return is_semicolon_optional_for_node(p, n.type)
|
||||||
case ast.Pointer_Type:
|
case ^ast.Pointer_Type:
|
||||||
return is_semicolon_optional_for_node(p, n.elem)
|
return is_semicolon_optional_for_node(p, n.elem)
|
||||||
case ast.Struct_Type, ast.Union_Type, ast.Enum_Type:
|
case ^ast.Struct_Type, ^ast.Union_Type, ^ast.Enum_Type:
|
||||||
// Require semicolon within a procedure body
|
// Require semicolon within a procedure body
|
||||||
return p.curr_proc == nil
|
return p.curr_proc == nil
|
||||||
case ast.Proc_Lit:
|
case ^ast.Proc_Lit:
|
||||||
return true
|
return true
|
||||||
|
|
||||||
case ast.Package_Decl, ast.Import_Decl, ast.Foreign_Import_Decl:
|
case ^ast.Package_Decl, ^ast.Import_Decl, ^ast.Foreign_Import_Decl:
|
||||||
return true
|
return true
|
||||||
|
|
||||||
case ast.Foreign_Block_Decl:
|
case ^ast.Foreign_Block_Decl:
|
||||||
return is_semicolon_optional_for_node(p, n.body)
|
return is_semicolon_optional_for_node(p, n.body)
|
||||||
|
|
||||||
case ast.Value_Decl:
|
case ^ast.Value_Decl:
|
||||||
if n.is_mutable {
|
if n.is_mutable {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -641,10 +641,10 @@ parse_stmt_list :: proc(p: ^Parser) -> []^ast.Stmt {
|
|||||||
p.curr_tok.kind != .EOF {
|
p.curr_tok.kind != .EOF {
|
||||||
stmt := parse_stmt(p)
|
stmt := parse_stmt(p)
|
||||||
if stmt != nil {
|
if stmt != nil {
|
||||||
if _, ok := stmt.derived.(ast.Empty_Stmt); !ok {
|
if _, ok := stmt.derived.(^ast.Empty_Stmt); !ok {
|
||||||
append(&list, stmt)
|
append(&list, stmt)
|
||||||
if es, es_ok := stmt.derived.(ast.Expr_Stmt); es_ok && es.expr != nil {
|
if es, es_ok := stmt.derived.(^ast.Expr_Stmt); es_ok && es.expr != nil {
|
||||||
if _, pl_ok := es.expr.derived.(ast.Proc_Lit); pl_ok {
|
if _, pl_ok := es.expr.derived.(^ast.Proc_Lit); pl_ok {
|
||||||
error(p, stmt.pos, "procedure literal evaluated but not used")
|
error(p, stmt.pos, "procedure literal evaluated but not used")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -722,7 +722,7 @@ convert_stmt_to_expr :: proc(p: ^Parser, stmt: ^ast.Stmt, kind: string) -> ^ast.
|
|||||||
if stmt == nil {
|
if stmt == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if es, ok := stmt.derived.(ast.Expr_Stmt); ok {
|
if es, ok := stmt.derived.(^ast.Expr_Stmt); ok {
|
||||||
return es.expr
|
return es.expr
|
||||||
}
|
}
|
||||||
error(p, stmt.pos, "expected %s, found a simple statement", kind)
|
error(p, stmt.pos, "expected %s, found a simple statement", kind)
|
||||||
@@ -864,7 +864,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
|
|||||||
|
|
||||||
if p.curr_tok.kind != .Semicolon {
|
if p.curr_tok.kind != .Semicolon {
|
||||||
cond = parse_simple_stmt(p, {Stmt_Allow_Flag.In})
|
cond = parse_simple_stmt(p, {Stmt_Allow_Flag.In})
|
||||||
if as, ok := cond.derived.(ast.Assign_Stmt); ok && as.op.kind == .In {
|
if as, ok := cond.derived.(^ast.Assign_Stmt); ok && as.op.kind == .In {
|
||||||
is_range = true
|
is_range = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -906,7 +906,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
|
|||||||
|
|
||||||
|
|
||||||
if is_range {
|
if is_range {
|
||||||
assign_stmt := cond.derived.(ast.Assign_Stmt)
|
assign_stmt := cond.derived.(^ast.Assign_Stmt)
|
||||||
vals := assign_stmt.lhs[:]
|
vals := assign_stmt.lhs[:]
|
||||||
|
|
||||||
rhs: ^ast.Expr
|
rhs: ^ast.Expr
|
||||||
@@ -987,7 +987,7 @@ parse_switch_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
|
|||||||
tag = as
|
tag = as
|
||||||
} else {
|
} else {
|
||||||
tag = parse_simple_stmt(p, {Stmt_Allow_Flag.In})
|
tag = parse_simple_stmt(p, {Stmt_Allow_Flag.In})
|
||||||
if as, ok := tag.derived.(ast.Assign_Stmt); ok && as.op.kind == .In {
|
if as, ok := tag.derived.(^ast.Assign_Stmt); ok && as.op.kind == .In {
|
||||||
is_type_switch = true
|
is_type_switch = true
|
||||||
} else if parse_control_statement_semicolon_separator(p) {
|
} else if parse_control_statement_semicolon_separator(p) {
|
||||||
init = tag
|
init = tag
|
||||||
@@ -1074,14 +1074,14 @@ parse_attribute :: proc(p: ^Parser, tok: tokenizer.Token, open_kind, close_kind:
|
|||||||
skip_possible_newline(p)
|
skip_possible_newline(p)
|
||||||
|
|
||||||
decl := parse_stmt(p)
|
decl := parse_stmt(p)
|
||||||
switch d in &decl.derived {
|
#partial switch d in decl.derived_stmt {
|
||||||
case ast.Value_Decl:
|
case ^ast.Value_Decl:
|
||||||
if d.docs == nil { d.docs = docs }
|
if d.docs == nil { d.docs = docs }
|
||||||
append(&d.attributes, attribute)
|
append(&d.attributes, attribute)
|
||||||
case ast.Foreign_Block_Decl:
|
case ^ast.Foreign_Block_Decl:
|
||||||
if d.docs == nil { d.docs = docs }
|
if d.docs == nil { d.docs = docs }
|
||||||
append(&d.attributes, attribute)
|
append(&d.attributes, attribute)
|
||||||
case ast.Foreign_Import_Decl:
|
case ^ast.Foreign_Import_Decl:
|
||||||
if d.docs == nil { d.docs = docs }
|
if d.docs == nil { d.docs = docs }
|
||||||
append(&d.attributes, attribute)
|
append(&d.attributes, attribute)
|
||||||
case:
|
case:
|
||||||
@@ -1095,11 +1095,11 @@ parse_attribute :: proc(p: ^Parser, tok: tokenizer.Token, open_kind, close_kind:
|
|||||||
|
|
||||||
parse_foreign_block_decl :: proc(p: ^Parser) -> ^ast.Stmt {
|
parse_foreign_block_decl :: proc(p: ^Parser) -> ^ast.Stmt {
|
||||||
decl := parse_stmt(p)
|
decl := parse_stmt(p)
|
||||||
switch in decl.derived {
|
#partial switch in decl.derived_stmt {
|
||||||
case ast.Empty_Stmt, ast.Bad_Stmt, ast.Bad_Decl:
|
case ^ast.Empty_Stmt, ^ast.Bad_Stmt, ^ast.Bad_Decl:
|
||||||
// Ignore
|
// Ignore
|
||||||
return nil
|
return nil
|
||||||
case ast.When_Stmt, ast.Value_Decl:
|
case ^ast.When_Stmt, ^ast.Value_Decl:
|
||||||
return decl
|
return decl
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1303,13 +1303,13 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
|
|||||||
case .Defer:
|
case .Defer:
|
||||||
tok := advance_token(p)
|
tok := advance_token(p)
|
||||||
stmt := parse_stmt(p)
|
stmt := parse_stmt(p)
|
||||||
switch s in stmt.derived {
|
#partial switch s in stmt.derived_stmt {
|
||||||
case ast.Empty_Stmt:
|
case ^ast.Empty_Stmt:
|
||||||
error(p, s.pos, "empty statement after defer (e.g. ';')")
|
error(p, s.pos, "empty statement after defer (e.g. ';')")
|
||||||
case ast.Defer_Stmt:
|
case ^ast.Defer_Stmt:
|
||||||
error(p, s.pos, "you cannot defer a defer statement")
|
error(p, s.pos, "you cannot defer a defer statement")
|
||||||
stmt = s.stmt
|
stmt = s.stmt
|
||||||
case ast.Return_Stmt:
|
case ^ast.Return_Stmt:
|
||||||
error(p, s.pos, "you cannot defer a return statement")
|
error(p, s.pos, "you cannot defer a return statement")
|
||||||
}
|
}
|
||||||
ds := ast.new(ast.Defer_Stmt, tok.pos, stmt.end)
|
ds := ast.new(ast.Defer_Stmt, tok.pos, stmt.end)
|
||||||
@@ -1381,8 +1381,8 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
|
|||||||
expect_token_after(p, .Colon, "identifier list")
|
expect_token_after(p, .Colon, "identifier list")
|
||||||
decl := parse_value_decl(p, list, docs)
|
decl := parse_value_decl(p, list, docs)
|
||||||
if decl != nil {
|
if decl != nil {
|
||||||
switch d in &decl.derived {
|
#partial switch d in decl.derived_stmt {
|
||||||
case ast.Value_Decl:
|
case ^ast.Value_Decl:
|
||||||
d.is_using = true
|
d.is_using = true
|
||||||
return decl
|
return decl
|
||||||
}
|
}
|
||||||
@@ -1413,9 +1413,9 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
|
|||||||
return stmt
|
return stmt
|
||||||
case "partial":
|
case "partial":
|
||||||
stmt := parse_stmt(p)
|
stmt := parse_stmt(p)
|
||||||
switch s in &stmt.derived {
|
#partial switch s in stmt.derived_stmt {
|
||||||
case ast.Switch_Stmt: s.partial = true
|
case ^ast.Switch_Stmt: s.partial = true
|
||||||
case ast.Type_Switch_Stmt: s.partial = true
|
case ^ast.Type_Switch_Stmt: s.partial = true
|
||||||
case: error(p, stmt.pos, "#partial can only be applied to a switch statement")
|
case: error(p, stmt.pos, "#partial can only be applied to a switch statement")
|
||||||
}
|
}
|
||||||
return stmt
|
return stmt
|
||||||
@@ -1560,11 +1560,11 @@ parse_body :: proc(p: ^Parser) -> ^ast.Block_Stmt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
convert_stmt_to_body :: proc(p: ^Parser, stmt: ^ast.Stmt) -> ^ast.Stmt {
|
convert_stmt_to_body :: proc(p: ^Parser, stmt: ^ast.Stmt) -> ^ast.Stmt {
|
||||||
switch s in stmt.derived {
|
#partial switch s in stmt.derived_stmt {
|
||||||
case ast.Block_Stmt:
|
case ^ast.Block_Stmt:
|
||||||
error(p, stmt.pos, "expected a normal statement rather than a block statement")
|
error(p, stmt.pos, "expected a normal statement rather than a block statement")
|
||||||
return stmt
|
return stmt
|
||||||
case ast.Empty_Stmt:
|
case ^ast.Empty_Stmt:
|
||||||
error(p, stmt.pos, "expected a non-empty statement")
|
error(p, stmt.pos, "expected a non-empty statement")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1641,10 +1641,10 @@ convert_to_ident_list :: proc(p: ^Parser, list: []Expr_And_Flags, ignore_flags,
|
|||||||
|
|
||||||
id: ^ast.Expr = ident.expr
|
id: ^ast.Expr = ident.expr
|
||||||
|
|
||||||
switch n in ident.expr.derived {
|
#partial switch n in ident.expr.derived_expr {
|
||||||
case ast.Ident:
|
case ^ast.Ident:
|
||||||
case ast.Bad_Expr:
|
case ^ast.Bad_Expr:
|
||||||
case ast.Poly_Type:
|
case ^ast.Poly_Type:
|
||||||
if allow_poly_names {
|
if allow_poly_names {
|
||||||
if n.specialization == nil {
|
if n.specialization == nil {
|
||||||
break
|
break
|
||||||
@@ -1806,21 +1806,21 @@ check_procedure_name_list :: proc(p: ^Parser, names: []^ast.Expr) -> bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
_, first_is_polymorphic := names[0].derived.(ast.Poly_Type)
|
_, first_is_polymorphic := names[0].derived.(^ast.Poly_Type)
|
||||||
any_polymorphic_names := first_is_polymorphic
|
any_polymorphic_names := first_is_polymorphic
|
||||||
|
|
||||||
for i := 1; i < len(names); i += 1 {
|
for i := 1; i < len(names); i += 1 {
|
||||||
name := names[i]
|
name := names[i]
|
||||||
|
|
||||||
if first_is_polymorphic {
|
if first_is_polymorphic {
|
||||||
if _, ok := name.derived.(ast.Poly_Type); ok {
|
if _, ok := name.derived.(^ast.Poly_Type); ok {
|
||||||
any_polymorphic_names = true
|
any_polymorphic_names = true
|
||||||
} else {
|
} else {
|
||||||
error(p, name.pos, "mixture of polymorphic and non-polymorphic identifiers")
|
error(p, name.pos, "mixture of polymorphic and non-polymorphic identifiers")
|
||||||
return any_polymorphic_names
|
return any_polymorphic_names
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if _, ok := name.derived.(ast.Poly_Type); ok {
|
if _, ok := name.derived.(^ast.Poly_Type); ok {
|
||||||
any_polymorphic_names = true
|
any_polymorphic_names = true
|
||||||
error(p, name.pos, "mixture of polymorphic and non-polymorphic identifiers")
|
error(p, name.pos, "mixture of polymorphic and non-polymorphic identifiers")
|
||||||
return any_polymorphic_names
|
return any_polymorphic_names
|
||||||
@@ -1885,7 +1885,7 @@ parse_field_list :: proc(p: ^Parser, follow: tokenizer.Token_Kind, allowed_flags
|
|||||||
if type == nil {
|
if type == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
_, ok := type.derived.(ast.Ellipsis)
|
_, ok := type.derived.(^ast.Ellipsis)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1903,7 +1903,7 @@ parse_field_list :: proc(p: ^Parser, follow: tokenizer.Token_Kind, allowed_flags
|
|||||||
type = parse_var_type(p, allowed_flags)
|
type = parse_var_type(p, allowed_flags)
|
||||||
tt := ast.unparen_expr(type)
|
tt := ast.unparen_expr(type)
|
||||||
if is_signature && !any_polymorphic_names {
|
if is_signature && !any_polymorphic_names {
|
||||||
if ti, ok := tt.derived.(ast.Typeid_Type); ok && ti.specialization != nil {
|
if ti, ok := tt.derived.(^ast.Typeid_Type); ok && ti.specialization != nil {
|
||||||
error(p, tt.pos, "specialization of typeid is not allowed without polymorphic names")
|
error(p, tt.pos, "specialization of typeid is not allowed without polymorphic names")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1979,7 +1979,7 @@ parse_field_list :: proc(p: ^Parser, follow: tokenizer.Token_Kind, allowed_flags
|
|||||||
p.curr_tok.kind != .EOF {
|
p.curr_tok.kind != .EOF {
|
||||||
prefix_flags := parse_field_prefixes(p)
|
prefix_flags := parse_field_prefixes(p)
|
||||||
param := parse_var_type(p, allowed_flags & {.Typeid_Token, .Ellipsis})
|
param := parse_var_type(p, allowed_flags & {.Typeid_Token, .Ellipsis})
|
||||||
if _, ok := param.derived.(ast.Ellipsis); ok {
|
if _, ok := param.derived.(^ast.Ellipsis); ok {
|
||||||
if seen_ellipsis {
|
if seen_ellipsis {
|
||||||
error(p, param.pos, "extra variadic parameter after ellipsis")
|
error(p, param.pos, "extra variadic parameter after ellipsis")
|
||||||
}
|
}
|
||||||
@@ -2006,8 +2006,8 @@ parse_field_list :: proc(p: ^Parser, follow: tokenizer.Token_Kind, allowed_flags
|
|||||||
|
|
||||||
names := make([]^ast.Expr, 1)
|
names := make([]^ast.Expr, 1)
|
||||||
names[0] = ast.new(ast.Ident, tok.pos, end_pos(tok))
|
names[0] = ast.new(ast.Ident, tok.pos, end_pos(tok))
|
||||||
switch ident in &names[0].derived {
|
#partial switch ident in names[0].derived_expr {
|
||||||
case ast.Ident:
|
case ^ast.Ident:
|
||||||
ident.name = tok.text
|
ident.name = tok.text
|
||||||
case:
|
case:
|
||||||
unreachable()
|
unreachable()
|
||||||
@@ -2137,12 +2137,12 @@ parse_proc_type :: proc(p: ^Parser, tok: tokenizer.Token) -> ^ast.Proc_Type {
|
|||||||
|
|
||||||
loop: for param in params.list {
|
loop: for param in params.list {
|
||||||
if param.type != nil {
|
if param.type != nil {
|
||||||
if _, ok := param.type.derived.(ast.Poly_Type); ok {
|
if _, ok := param.type.derived.(^ast.Poly_Type); ok {
|
||||||
is_generic = true
|
is_generic = true
|
||||||
break loop
|
break loop
|
||||||
}
|
}
|
||||||
for name in param.names {
|
for name in param.names {
|
||||||
if _, ok := name.derived.(ast.Poly_Type); ok {
|
if _, ok := name.derived.(^ast.Poly_Type); ok {
|
||||||
is_generic = true
|
is_generic = true
|
||||||
break loop
|
break loop
|
||||||
}
|
}
|
||||||
@@ -2179,13 +2179,13 @@ parse_inlining_operand :: proc(p: ^Parser, lhs: bool, tok: tokenizer.Token) -> ^
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch e in &ast.unparen_expr(expr).derived {
|
#partial switch e in ast.unparen_expr(expr).derived_expr {
|
||||||
case ast.Proc_Lit:
|
case ^ast.Proc_Lit:
|
||||||
if e.inlining != .None && e.inlining != pi {
|
if e.inlining != .None && e.inlining != pi {
|
||||||
error(p, expr.pos, "both 'inline' and 'no_inline' cannot be applied to a procedure literal")
|
error(p, expr.pos, "both 'inline' and 'no_inline' cannot be applied to a procedure literal")
|
||||||
}
|
}
|
||||||
e.inlining = pi
|
e.inlining = pi
|
||||||
case ast.Call_Expr:
|
case ^ast.Call_Expr:
|
||||||
if e.inlining != .None && e.inlining != pi {
|
if e.inlining != .None && e.inlining != pi {
|
||||||
error(p, expr.pos, "both 'inline' and 'no_inline' cannot be applied to a procedure call")
|
error(p, expr.pos, "both 'inline' and 'no_inline' cannot be applied to a procedure call")
|
||||||
}
|
}
|
||||||
@@ -2276,9 +2276,9 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
|||||||
bd.name = name.text
|
bd.name = name.text
|
||||||
original_type := parse_type(p)
|
original_type := parse_type(p)
|
||||||
type := ast.unparen_expr(original_type)
|
type := ast.unparen_expr(original_type)
|
||||||
switch t in &type.derived {
|
#partial switch t in type.derived_expr {
|
||||||
case ast.Array_Type: t.tag = bd
|
case ^ast.Array_Type: t.tag = bd
|
||||||
case ast.Dynamic_Array_Type: t.tag = bd
|
case ^ast.Dynamic_Array_Type: t.tag = bd
|
||||||
case:
|
case:
|
||||||
error(p, original_type.pos, "expected an array type after #%s", name.text)
|
error(p, original_type.pos, "expected an array type after #%s", name.text)
|
||||||
}
|
}
|
||||||
@@ -2290,10 +2290,10 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
|||||||
tag.name = name.text
|
tag.name = name.text
|
||||||
original_expr := parse_expr(p, lhs)
|
original_expr := parse_expr(p, lhs)
|
||||||
expr := ast.unparen_expr(original_expr)
|
expr := ast.unparen_expr(original_expr)
|
||||||
switch t in &expr.derived {
|
#partial switch t in expr.derived_expr {
|
||||||
case ast.Comp_Lit:
|
case ^ast.Comp_Lit:
|
||||||
t.tag = tag
|
t.tag = tag
|
||||||
case ast.Array_Type:
|
case ^ast.Array_Type:
|
||||||
t.tag = tag
|
t.tag = tag
|
||||||
error(p, tok.pos, "#%s has been replaced with #sparse for non-contiguous enumerated array types", name.text)
|
error(p, tok.pos, "#%s has been replaced with #sparse for non-contiguous enumerated array types", name.text)
|
||||||
case:
|
case:
|
||||||
@@ -2308,8 +2308,8 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
|||||||
tag.name = name.text
|
tag.name = name.text
|
||||||
original_type := parse_type(p)
|
original_type := parse_type(p)
|
||||||
type := ast.unparen_expr(original_type)
|
type := ast.unparen_expr(original_type)
|
||||||
switch t in &type.derived {
|
#partial switch t in type.derived_expr {
|
||||||
case ast.Array_Type:
|
case ^ast.Array_Type:
|
||||||
t.tag = tag
|
t.tag = tag
|
||||||
case:
|
case:
|
||||||
error(p, tok.pos, "expected an enumerated array type after #%s", name.text)
|
error(p, tok.pos, "expected an enumerated array type after #%s", name.text)
|
||||||
@@ -2689,7 +2689,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
|||||||
variants: [dynamic]^ast.Expr
|
variants: [dynamic]^ast.Expr
|
||||||
for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF {
|
for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF {
|
||||||
type := parse_type(p)
|
type := parse_type(p)
|
||||||
if _, ok := type.derived.(ast.Bad_Expr); !ok {
|
if _, ok := type.derived.(^ast.Bad_Expr); !ok {
|
||||||
append(&variants, type)
|
append(&variants, type)
|
||||||
}
|
}
|
||||||
if !allow_token(p, .Comma) {
|
if !allow_token(p, .Comma) {
|
||||||
@@ -2864,19 +2864,19 @@ is_literal_type :: proc(expr: ^ast.Expr) -> bool {
|
|||||||
if val == nil {
|
if val == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
switch _ in val.derived {
|
#partial switch _ in val.derived_expr {
|
||||||
case ast.Bad_Expr,
|
case ^ast.Bad_Expr,
|
||||||
ast.Ident,
|
^ast.Ident,
|
||||||
ast.Selector_Expr,
|
^ast.Selector_Expr,
|
||||||
ast.Array_Type,
|
^ast.Array_Type,
|
||||||
ast.Struct_Type,
|
^ast.Struct_Type,
|
||||||
ast.Union_Type,
|
^ast.Union_Type,
|
||||||
ast.Enum_Type,
|
^ast.Enum_Type,
|
||||||
ast.Dynamic_Array_Type,
|
^ast.Dynamic_Array_Type,
|
||||||
ast.Map_Type,
|
^ast.Map_Type,
|
||||||
ast.Bit_Set_Type,
|
^ast.Bit_Set_Type,
|
||||||
ast.Matrix_Type,
|
^ast.Matrix_Type,
|
||||||
ast.Call_Expr:
|
^ast.Call_Expr:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@@ -2998,7 +2998,7 @@ parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Expr {
|
|||||||
ce.close = close.pos
|
ce.close = close.pos
|
||||||
|
|
||||||
o := ast.unparen_expr(operand)
|
o := ast.unparen_expr(operand)
|
||||||
if se, ok := o.derived.(ast.Selector_Expr); ok && se.op.kind == .Arrow_Right {
|
if se, ok := o.derived.(^ast.Selector_Expr); ok && se.op.kind == .Arrow_Right {
|
||||||
sce := ast.new(ast.Selector_Call_Expr, ce.pos, ce.end)
|
sce := ast.new(ast.Selector_Call_Expr, ce.pos, ce.end)
|
||||||
sce.expr = o
|
sce.expr = o
|
||||||
sce.call = ce
|
sce.call = ce
|
||||||
@@ -3428,13 +3428,13 @@ parse_simple_stmt :: proc(p: ^Parser, flags: Stmt_Allow_Flags) -> ^ast.Stmt {
|
|||||||
stmt := parse_stmt(p)
|
stmt := parse_stmt(p)
|
||||||
|
|
||||||
if stmt != nil {
|
if stmt != nil {
|
||||||
switch n in &stmt.derived {
|
#partial switch n in stmt.derived_stmt {
|
||||||
case ast.Block_Stmt: n.label = label
|
case ^ast.Block_Stmt: n.label = label
|
||||||
case ast.If_Stmt: n.label = label
|
case ^ast.If_Stmt: n.label = label
|
||||||
case ast.For_Stmt: n.label = label
|
case ^ast.For_Stmt: n.label = label
|
||||||
case ast.Switch_Stmt: n.label = label
|
case ^ast.Switch_Stmt: n.label = label
|
||||||
case ast.Type_Switch_Stmt: n.label = label
|
case ^ast.Type_Switch_Stmt: n.label = label
|
||||||
case ast.Range_Stmt: n.label = label
|
case ^ast.Range_Stmt: n.label = label
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+132
-94
@@ -342,16 +342,16 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch v in &decl.derived {
|
#partial switch v in decl.derived_stmt {
|
||||||
case Expr_Stmt:
|
case ^Expr_Stmt:
|
||||||
move_line(p, decl.pos)
|
move_line(p, decl.pos)
|
||||||
visit_expr(p, v.expr)
|
visit_expr(p, v.expr)
|
||||||
if p.config.semicolons {
|
if p.config.semicolons {
|
||||||
push_generic_token(p, .Semicolon, 0)
|
push_generic_token(p, .Semicolon, 0)
|
||||||
}
|
}
|
||||||
case When_Stmt:
|
case ^When_Stmt:
|
||||||
visit_stmt(p, cast(^Stmt)decl)
|
visit_stmt(p, cast(^Stmt)decl)
|
||||||
case Foreign_Import_Decl:
|
case ^Foreign_Import_Decl:
|
||||||
if len(v.attributes) > 0 {
|
if len(v.attributes) > 0 {
|
||||||
sort.sort(sort_attribute(&v.attributes))
|
sort.sort(sort_attribute(&v.attributes))
|
||||||
move_line(p, v.attributes[0].pos)
|
move_line(p, v.attributes[0].pos)
|
||||||
@@ -370,7 +370,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
|
|||||||
for path in v.fullpaths {
|
for path in v.fullpaths {
|
||||||
push_ident_token(p, path, 0)
|
push_ident_token(p, path, 0)
|
||||||
}
|
}
|
||||||
case Foreign_Block_Decl:
|
case ^Foreign_Block_Decl:
|
||||||
if len(v.attributes) > 0 {
|
if len(v.attributes) > 0 {
|
||||||
sort.sort(sort_attribute(&v.attributes))
|
sort.sort(sort_attribute(&v.attributes))
|
||||||
move_line(p, v.attributes[0].pos)
|
move_line(p, v.attributes[0].pos)
|
||||||
@@ -383,7 +383,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
|
|||||||
|
|
||||||
visit_expr(p, v.foreign_library)
|
visit_expr(p, v.foreign_library)
|
||||||
visit_stmt(p, v.body)
|
visit_stmt(p, v.body)
|
||||||
case Import_Decl:
|
case ^Import_Decl:
|
||||||
move_line(p, decl.pos)
|
move_line(p, decl.pos)
|
||||||
|
|
||||||
if v.name.text != "" {
|
if v.name.text != "" {
|
||||||
@@ -395,7 +395,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
|
|||||||
push_ident_token(p, v.fullpath, 1)
|
push_ident_token(p, v.fullpath, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
case Value_Decl:
|
case ^Value_Decl:
|
||||||
if len(v.attributes) > 0 {
|
if len(v.attributes) > 0 {
|
||||||
sort.sort(sort_attribute(&v.attributes))
|
sort.sort(sort_attribute(&v.attributes))
|
||||||
move_line(p, v.attributes[0].pos)
|
move_line(p, v.attributes[0].pos)
|
||||||
@@ -446,10 +446,10 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
|
|||||||
add_semicolon := true
|
add_semicolon := true
|
||||||
|
|
||||||
for value in v.values {
|
for value in v.values {
|
||||||
switch a in value.derived {
|
#partial switch a in value.derived {
|
||||||
case Union_Type, Enum_Type, Struct_Type:
|
case ^Union_Type, ^Enum_Type, ^Struct_Type:
|
||||||
add_semicolon = false || called_in_stmt
|
add_semicolon = false || called_in_stmt
|
||||||
case Proc_Lit:
|
case ^Proc_Lit:
|
||||||
add_semicolon = false
|
add_semicolon = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -516,23 +516,34 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch v in stmt.derived {
|
|
||||||
case Import_Decl:
|
|
||||||
visit_decl(p, cast(^Decl)stmt, true)
|
|
||||||
return
|
|
||||||
case Value_Decl:
|
|
||||||
visit_decl(p, cast(^Decl)stmt, true)
|
|
||||||
return
|
|
||||||
case Foreign_Import_Decl:
|
|
||||||
visit_decl(p, cast(^Decl)stmt, true)
|
|
||||||
return
|
|
||||||
case Foreign_Block_Decl:
|
|
||||||
visit_decl(p, cast(^Decl)stmt, true)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch v in stmt.derived {
|
switch v in stmt.derived_stmt {
|
||||||
case Using_Stmt:
|
case ^Bad_Stmt:
|
||||||
|
case ^Bad_Decl:
|
||||||
|
case ^Package_Decl:
|
||||||
|
|
||||||
|
case ^Empty_Stmt:
|
||||||
|
push_generic_token(p, .Semicolon, 0)
|
||||||
|
case ^Tag_Stmt:
|
||||||
|
push_generic_token(p, .Hash, 1)
|
||||||
|
push_generic_token(p, v.op.kind, 1, v.op.text)
|
||||||
|
visit_stmt(p, v.stmt)
|
||||||
|
|
||||||
|
|
||||||
|
case ^Import_Decl:
|
||||||
|
visit_decl(p, cast(^Decl)stmt, true)
|
||||||
|
return
|
||||||
|
case ^Value_Decl:
|
||||||
|
visit_decl(p, cast(^Decl)stmt, true)
|
||||||
|
return
|
||||||
|
case ^Foreign_Import_Decl:
|
||||||
|
visit_decl(p, cast(^Decl)stmt, true)
|
||||||
|
return
|
||||||
|
case ^Foreign_Block_Decl:
|
||||||
|
visit_decl(p, cast(^Decl)stmt, true)
|
||||||
|
return
|
||||||
|
|
||||||
|
case ^Using_Stmt:
|
||||||
move_line(p, v.pos)
|
move_line(p, v.pos)
|
||||||
|
|
||||||
push_generic_token(p, .Using, 1)
|
push_generic_token(p, .Using, 1)
|
||||||
@@ -542,7 +553,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
|
|||||||
if p.config.semicolons {
|
if p.config.semicolons {
|
||||||
push_generic_token(p, .Semicolon, 0)
|
push_generic_token(p, .Semicolon, 0)
|
||||||
}
|
}
|
||||||
case Block_Stmt:
|
case ^Block_Stmt:
|
||||||
move_line(p, v.pos)
|
move_line(p, v.pos)
|
||||||
|
|
||||||
if v.pos.line == v.end.line {
|
if v.pos.line == v.end.line {
|
||||||
@@ -572,7 +583,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
|
|||||||
visit_end_brace(p, v.end)
|
visit_end_brace(p, v.end)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case If_Stmt:
|
case ^If_Stmt:
|
||||||
move_line(p, v.pos)
|
move_line(p, v.pos)
|
||||||
|
|
||||||
if v.label != nil {
|
if v.label != nil {
|
||||||
@@ -595,7 +606,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
|
|||||||
|
|
||||||
uses_do := false
|
uses_do := false
|
||||||
|
|
||||||
if check_stmt, ok := v.body.derived.(Block_Stmt); ok && check_stmt.uses_do {
|
if check_stmt, ok := v.body.derived.(^Block_Stmt); ok && check_stmt.uses_do {
|
||||||
uses_do = true
|
uses_do = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -626,7 +637,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
|
|||||||
|
|
||||||
visit_stmt(p, v.else_stmt)
|
visit_stmt(p, v.else_stmt)
|
||||||
}
|
}
|
||||||
case Switch_Stmt:
|
case ^Switch_Stmt:
|
||||||
move_line(p, v.pos)
|
move_line(p, v.pos)
|
||||||
|
|
||||||
if v.label != nil {
|
if v.label != nil {
|
||||||
@@ -654,7 +665,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
|
|||||||
|
|
||||||
visit_expr(p, v.cond)
|
visit_expr(p, v.cond)
|
||||||
visit_stmt(p, v.body)
|
visit_stmt(p, v.body)
|
||||||
case Case_Clause:
|
case ^Case_Clause:
|
||||||
move_line(p, v.pos)
|
move_line(p, v.pos)
|
||||||
|
|
||||||
if !p.config.indent_cases {
|
if !p.config.indent_cases {
|
||||||
@@ -678,7 +689,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
|
|||||||
if !p.config.indent_cases {
|
if !p.config.indent_cases {
|
||||||
indent(p)
|
indent(p)
|
||||||
}
|
}
|
||||||
case Type_Switch_Stmt:
|
case ^Type_Switch_Stmt:
|
||||||
move_line(p, v.pos)
|
move_line(p, v.pos)
|
||||||
|
|
||||||
hint_current_line(p, {.Switch_Stmt})
|
hint_current_line(p, {.Switch_Stmt})
|
||||||
@@ -696,7 +707,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
|
|||||||
|
|
||||||
visit_stmt(p, v.tag)
|
visit_stmt(p, v.tag)
|
||||||
visit_stmt(p, v.body)
|
visit_stmt(p, v.body)
|
||||||
case Assign_Stmt:
|
case ^Assign_Stmt:
|
||||||
move_line(p, v.pos)
|
move_line(p, v.pos)
|
||||||
|
|
||||||
hint_current_line(p, {.Assign})
|
hint_current_line(p, {.Assign})
|
||||||
@@ -710,13 +721,13 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
|
|||||||
if block_stmt && p.config.semicolons {
|
if block_stmt && p.config.semicolons {
|
||||||
push_generic_token(p, .Semicolon, 0)
|
push_generic_token(p, .Semicolon, 0)
|
||||||
}
|
}
|
||||||
case Expr_Stmt:
|
case ^Expr_Stmt:
|
||||||
move_line(p, v.pos)
|
move_line(p, v.pos)
|
||||||
visit_expr(p, v.expr)
|
visit_expr(p, v.expr)
|
||||||
if block_stmt && p.config.semicolons {
|
if block_stmt && p.config.semicolons {
|
||||||
push_generic_token(p, .Semicolon, 0)
|
push_generic_token(p, .Semicolon, 0)
|
||||||
}
|
}
|
||||||
case For_Stmt:
|
case ^For_Stmt:
|
||||||
// this should be simplified
|
// this should be simplified
|
||||||
move_line(p, v.pos)
|
move_line(p, v.pos)
|
||||||
|
|
||||||
@@ -753,7 +764,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
|
|||||||
|
|
||||||
visit_stmt(p, v.body)
|
visit_stmt(p, v.body)
|
||||||
|
|
||||||
case Inline_Range_Stmt:
|
case ^Inline_Range_Stmt:
|
||||||
move_line(p, v.pos)
|
move_line(p, v.pos)
|
||||||
|
|
||||||
if v.label != nil {
|
if v.label != nil {
|
||||||
@@ -779,7 +790,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
|
|||||||
visit_expr(p, v.expr)
|
visit_expr(p, v.expr)
|
||||||
visit_stmt(p, v.body)
|
visit_stmt(p, v.body)
|
||||||
|
|
||||||
case Range_Stmt:
|
case ^Range_Stmt:
|
||||||
move_line(p, v.pos)
|
move_line(p, v.pos)
|
||||||
|
|
||||||
if v.label != nil {
|
if v.label != nil {
|
||||||
@@ -805,7 +816,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
|
|||||||
visit_expr(p, v.expr)
|
visit_expr(p, v.expr)
|
||||||
|
|
||||||
visit_stmt(p, v.body)
|
visit_stmt(p, v.body)
|
||||||
case Return_Stmt:
|
case ^Return_Stmt:
|
||||||
move_line(p, v.pos)
|
move_line(p, v.pos)
|
||||||
|
|
||||||
push_generic_token(p, .Return, 1)
|
push_generic_token(p, .Return, 1)
|
||||||
@@ -817,7 +828,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
|
|||||||
if block_stmt && p.config.semicolons {
|
if block_stmt && p.config.semicolons {
|
||||||
push_generic_token(p, .Semicolon, 0)
|
push_generic_token(p, .Semicolon, 0)
|
||||||
}
|
}
|
||||||
case Defer_Stmt:
|
case ^Defer_Stmt:
|
||||||
move_line(p, v.pos)
|
move_line(p, v.pos)
|
||||||
push_generic_token(p, .Defer, 0)
|
push_generic_token(p, .Defer, 0)
|
||||||
|
|
||||||
@@ -826,7 +837,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
|
|||||||
if p.config.semicolons {
|
if p.config.semicolons {
|
||||||
push_generic_token(p, .Semicolon, 0)
|
push_generic_token(p, .Semicolon, 0)
|
||||||
}
|
}
|
||||||
case When_Stmt:
|
case ^When_Stmt:
|
||||||
move_line(p, v.pos)
|
move_line(p, v.pos)
|
||||||
push_generic_token(p, .When, 1)
|
push_generic_token(p, .When, 1)
|
||||||
visit_expr(p, v.cond)
|
visit_expr(p, v.cond)
|
||||||
@@ -846,7 +857,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
|
|||||||
visit_stmt(p, v.else_stmt)
|
visit_stmt(p, v.else_stmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
case Branch_Stmt:
|
case ^Branch_Stmt:
|
||||||
move_line(p, v.pos)
|
move_line(p, v.pos)
|
||||||
|
|
||||||
push_generic_token(p, v.tok.kind, 0)
|
push_generic_token(p, v.tok.kind, 0)
|
||||||
@@ -918,8 +929,15 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
|
|||||||
|
|
||||||
set_source_position(p, expr.pos)
|
set_source_position(p, expr.pos)
|
||||||
|
|
||||||
switch v in expr.derived {
|
switch v in expr.derived_expr {
|
||||||
case Inline_Asm_Expr:
|
case ^Bad_Expr:
|
||||||
|
|
||||||
|
case ^Tag_Expr:
|
||||||
|
push_generic_token(p, .Hash, 1)
|
||||||
|
push_generic_token(p, v.op.kind, 1, v.op.text)
|
||||||
|
visit_expr(p, v.expr)
|
||||||
|
|
||||||
|
case ^Inline_Asm_Expr:
|
||||||
push_generic_token(p, v.tok.kind, 1, v.tok.text)
|
push_generic_token(p, v.tok.kind, 1, v.tok.text)
|
||||||
|
|
||||||
push_generic_token(p, .Open_Paren, 1)
|
push_generic_token(p, .Open_Paren, 1)
|
||||||
@@ -936,42 +954,42 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
|
|||||||
push_generic_token(p, .Comma, 0)
|
push_generic_token(p, .Comma, 0)
|
||||||
visit_expr(p, v.constraints_string)
|
visit_expr(p, v.constraints_string)
|
||||||
push_generic_token(p, .Close_Brace, 0)
|
push_generic_token(p, .Close_Brace, 0)
|
||||||
case Undef:
|
case ^Undef:
|
||||||
push_generic_token(p, .Undef, 1)
|
push_generic_token(p, .Undef, 1)
|
||||||
case Auto_Cast:
|
case ^Auto_Cast:
|
||||||
push_generic_token(p, v.op.kind, 1)
|
push_generic_token(p, v.op.kind, 1)
|
||||||
visit_expr(p, v.expr)
|
visit_expr(p, v.expr)
|
||||||
case Ternary_If_Expr:
|
case ^Ternary_If_Expr:
|
||||||
visit_expr(p, v.x)
|
visit_expr(p, v.x)
|
||||||
push_generic_token(p, v.op1.kind, 1)
|
push_generic_token(p, v.op1.kind, 1)
|
||||||
visit_expr(p, v.cond)
|
visit_expr(p, v.cond)
|
||||||
push_generic_token(p, v.op2.kind, 1)
|
push_generic_token(p, v.op2.kind, 1)
|
||||||
visit_expr(p, v.y)
|
visit_expr(p, v.y)
|
||||||
case Ternary_When_Expr:
|
case ^Ternary_When_Expr:
|
||||||
visit_expr(p, v.x)
|
visit_expr(p, v.x)
|
||||||
push_generic_token(p, v.op1.kind, 1)
|
push_generic_token(p, v.op1.kind, 1)
|
||||||
visit_expr(p, v.cond)
|
visit_expr(p, v.cond)
|
||||||
push_generic_token(p, v.op2.kind, 1)
|
push_generic_token(p, v.op2.kind, 1)
|
||||||
visit_expr(p, v.y)
|
visit_expr(p, v.y)
|
||||||
case Or_Else_Expr:
|
case ^Or_Else_Expr:
|
||||||
visit_expr(p, v.x)
|
visit_expr(p, v.x)
|
||||||
push_generic_token(p, v.token.kind, 1)
|
push_generic_token(p, v.token.kind, 1)
|
||||||
visit_expr(p, v.y)
|
visit_expr(p, v.y)
|
||||||
case Or_Return_Expr:
|
case ^Or_Return_Expr:
|
||||||
visit_expr(p, v.expr)
|
visit_expr(p, v.expr)
|
||||||
push_generic_token(p, v.token.kind, 1)
|
push_generic_token(p, v.token.kind, 1)
|
||||||
case Selector_Call_Expr:
|
case ^Selector_Call_Expr:
|
||||||
visit_expr(p, v.call.expr)
|
visit_expr(p, v.call.expr)
|
||||||
push_generic_token(p, .Open_Paren, 1)
|
push_generic_token(p, .Open_Paren, 1)
|
||||||
visit_exprs(p, v.call.args, {.Add_Comma})
|
visit_exprs(p, v.call.args, {.Add_Comma})
|
||||||
push_generic_token(p, .Close_Paren, 0)
|
push_generic_token(p, .Close_Paren, 0)
|
||||||
case Ellipsis:
|
case ^Ellipsis:
|
||||||
push_generic_token(p, .Ellipsis, 1)
|
push_generic_token(p, .Ellipsis, 1)
|
||||||
visit_expr(p, v.expr)
|
visit_expr(p, v.expr)
|
||||||
case Relative_Type:
|
case ^Relative_Type:
|
||||||
visit_expr(p, v.tag)
|
visit_expr(p, v.tag)
|
||||||
visit_expr(p, v.type)
|
visit_expr(p, v.type)
|
||||||
case Slice_Expr:
|
case ^Slice_Expr:
|
||||||
visit_expr(p, v.expr)
|
visit_expr(p, v.expr)
|
||||||
push_generic_token(p, .Open_Bracket, 0)
|
push_generic_token(p, .Open_Bracket, 0)
|
||||||
visit_expr(p, v.low)
|
visit_expr(p, v.low)
|
||||||
@@ -981,37 +999,37 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
|
|||||||
visit_expr(p, v.high)
|
visit_expr(p, v.high)
|
||||||
}
|
}
|
||||||
push_generic_token(p, .Close_Bracket, 0)
|
push_generic_token(p, .Close_Bracket, 0)
|
||||||
case Ident:
|
case ^Ident:
|
||||||
if .Enforce_Poly_Names in options {
|
if .Enforce_Poly_Names in options {
|
||||||
push_generic_token(p, .Dollar, 1)
|
push_generic_token(p, .Dollar, 1)
|
||||||
push_ident_token(p, v.name, 0)
|
push_ident_token(p, v.name, 0)
|
||||||
} else {
|
} else {
|
||||||
push_ident_token(p, v.name, 1)
|
push_ident_token(p, v.name, 1)
|
||||||
}
|
}
|
||||||
case Deref_Expr:
|
case ^Deref_Expr:
|
||||||
visit_expr(p, v.expr)
|
visit_expr(p, v.expr)
|
||||||
push_generic_token(p, v.op.kind, 0)
|
push_generic_token(p, v.op.kind, 0)
|
||||||
case Type_Cast:
|
case ^Type_Cast:
|
||||||
push_generic_token(p, v.tok.kind, 1)
|
push_generic_token(p, v.tok.kind, 1)
|
||||||
push_generic_token(p, .Open_Paren, 0)
|
push_generic_token(p, .Open_Paren, 0)
|
||||||
visit_expr(p, v.type)
|
visit_expr(p, v.type)
|
||||||
push_generic_token(p, .Close_Paren, 0)
|
push_generic_token(p, .Close_Paren, 0)
|
||||||
merge_next_token(p)
|
merge_next_token(p)
|
||||||
visit_expr(p, v.expr)
|
visit_expr(p, v.expr)
|
||||||
case Basic_Directive:
|
case ^Basic_Directive:
|
||||||
push_generic_token(p, v.tok.kind, 1)
|
push_generic_token(p, v.tok.kind, 1)
|
||||||
push_ident_token(p, v.name, 0)
|
push_ident_token(p, v.name, 0)
|
||||||
case Distinct_Type:
|
case ^Distinct_Type:
|
||||||
push_generic_token(p, .Distinct, 1)
|
push_generic_token(p, .Distinct, 1)
|
||||||
visit_expr(p, v.type)
|
visit_expr(p, v.type)
|
||||||
case Dynamic_Array_Type:
|
case ^Dynamic_Array_Type:
|
||||||
visit_expr(p, v.tag)
|
visit_expr(p, v.tag)
|
||||||
push_generic_token(p, .Open_Bracket, 1)
|
push_generic_token(p, .Open_Bracket, 1)
|
||||||
push_generic_token(p, .Dynamic, 0)
|
push_generic_token(p, .Dynamic, 0)
|
||||||
push_generic_token(p, .Close_Bracket, 0)
|
push_generic_token(p, .Close_Bracket, 0)
|
||||||
merge_next_token(p)
|
merge_next_token(p)
|
||||||
visit_expr(p, v.elem)
|
visit_expr(p, v.elem)
|
||||||
case Bit_Set_Type:
|
case ^Bit_Set_Type:
|
||||||
push_generic_token(p, .Bit_Set, 1)
|
push_generic_token(p, .Bit_Set, 1)
|
||||||
push_generic_token(p, .Open_Bracket, 0)
|
push_generic_token(p, .Open_Bracket, 0)
|
||||||
|
|
||||||
@@ -1023,7 +1041,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
push_generic_token(p, .Close_Bracket, 0)
|
push_generic_token(p, .Close_Bracket, 0)
|
||||||
case Union_Type:
|
case ^Union_Type:
|
||||||
push_generic_token(p, .Union, 1)
|
push_generic_token(p, .Union, 1)
|
||||||
|
|
||||||
push_poly_params(p, v.poly_params)
|
push_poly_params(p, v.poly_params)
|
||||||
@@ -1045,7 +1063,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
|
|||||||
visit_exprs(p, v.variants, {.Add_Comma, .Trailing})
|
visit_exprs(p, v.variants, {.Add_Comma, .Trailing})
|
||||||
visit_end_brace(p, v.end)
|
visit_end_brace(p, v.end)
|
||||||
}
|
}
|
||||||
case Enum_Type:
|
case ^Enum_Type:
|
||||||
push_generic_token(p, .Enum, 1)
|
push_generic_token(p, .Enum, 1)
|
||||||
|
|
||||||
hint_current_line(p, {.Enum})
|
hint_current_line(p, {.Enum})
|
||||||
@@ -1068,7 +1086,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
set_source_position(p, v.end)
|
set_source_position(p, v.end)
|
||||||
case Struct_Type:
|
case ^Struct_Type:
|
||||||
push_generic_token(p, .Struct, 1)
|
push_generic_token(p, .Struct, 1)
|
||||||
|
|
||||||
hint_current_line(p, {.Struct})
|
hint_current_line(p, {.Struct})
|
||||||
@@ -1103,7 +1121,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
set_source_position(p, v.end)
|
set_source_position(p, v.end)
|
||||||
case Proc_Lit:
|
case ^Proc_Lit:
|
||||||
switch v.inlining {
|
switch v.inlining {
|
||||||
case .None:
|
case .None:
|
||||||
case .Inline:
|
case .Inline:
|
||||||
@@ -1112,7 +1130,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
|
|||||||
push_ident_token(p, "#force_no_inline", 0)
|
push_ident_token(p, "#force_no_inline", 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
visit_proc_type(p, v.type^, true)
|
visit_proc_type(p, v.type, true)
|
||||||
|
|
||||||
push_where_clauses(p, v.where_clauses)
|
push_where_clauses(p, v.where_clauses)
|
||||||
|
|
||||||
@@ -1122,16 +1140,16 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
|
|||||||
} else {
|
} else {
|
||||||
push_generic_token(p, .Undef, 1)
|
push_generic_token(p, .Undef, 1)
|
||||||
}
|
}
|
||||||
case Proc_Type:
|
case ^Proc_Type:
|
||||||
visit_proc_type(p, v)
|
visit_proc_type(p, v)
|
||||||
case Basic_Lit:
|
case ^Basic_Lit:
|
||||||
push_generic_token(p, v.tok.kind, 1, v.tok.text)
|
push_generic_token(p, v.tok.kind, 1, v.tok.text)
|
||||||
case Binary_Expr:
|
case ^Binary_Expr:
|
||||||
visit_binary_expr(p, v)
|
visit_binary_expr(p, v)
|
||||||
case Implicit_Selector_Expr:
|
case ^Implicit_Selector_Expr:
|
||||||
push_generic_token(p, .Period, 1)
|
push_generic_token(p, .Period, 1)
|
||||||
push_ident_token(p, v.field.name, 0)
|
push_ident_token(p, v.field.name, 0)
|
||||||
case Call_Expr:
|
case ^Call_Expr:
|
||||||
visit_expr(p, v.expr)
|
visit_expr(p, v.expr)
|
||||||
|
|
||||||
push_format_token(p,
|
push_format_token(p,
|
||||||
@@ -1146,27 +1164,34 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
|
|||||||
|
|
||||||
visit_call_exprs(p, v.args, v.ellipsis.kind == .Ellipsis)
|
visit_call_exprs(p, v.args, v.ellipsis.kind == .Ellipsis)
|
||||||
push_generic_token(p, .Close_Paren, 0)
|
push_generic_token(p, .Close_Paren, 0)
|
||||||
case Typeid_Type:
|
case ^Typeid_Type:
|
||||||
push_generic_token(p, .Typeid, 1)
|
push_generic_token(p, .Typeid, 1)
|
||||||
|
|
||||||
if v.specialization != nil {
|
if v.specialization != nil {
|
||||||
push_generic_token(p, .Quo, 0)
|
push_generic_token(p, .Quo, 0)
|
||||||
visit_expr(p, v.specialization)
|
visit_expr(p, v.specialization)
|
||||||
}
|
}
|
||||||
case Selector_Expr:
|
case ^Selector_Expr:
|
||||||
visit_expr(p, v.expr)
|
visit_expr(p, v.expr)
|
||||||
push_generic_token(p, v.op.kind, 0)
|
push_generic_token(p, v.op.kind, 0)
|
||||||
visit_expr(p, v.field)
|
visit_expr(p, v.field)
|
||||||
case Paren_Expr:
|
case ^Paren_Expr:
|
||||||
push_generic_token(p, .Open_Paren, 1)
|
push_generic_token(p, .Open_Paren, 1)
|
||||||
visit_expr(p, v.expr)
|
visit_expr(p, v.expr)
|
||||||
push_generic_token(p, .Close_Paren, 0)
|
push_generic_token(p, .Close_Paren, 0)
|
||||||
case Index_Expr:
|
case ^Index_Expr:
|
||||||
visit_expr(p, v.expr)
|
visit_expr(p, v.expr)
|
||||||
push_generic_token(p, .Open_Bracket, 0)
|
push_generic_token(p, .Open_Bracket, 0)
|
||||||
visit_expr(p, v.index)
|
visit_expr(p, v.index)
|
||||||
push_generic_token(p, .Close_Bracket, 0)
|
push_generic_token(p, .Close_Bracket, 0)
|
||||||
case Proc_Group:
|
case ^Matrix_Index_Expr:
|
||||||
|
visit_expr(p, v.expr)
|
||||||
|
push_generic_token(p, .Open_Bracket, 0)
|
||||||
|
visit_expr(p, v.row_index)
|
||||||
|
push_generic_token(p, .Comma, 0)
|
||||||
|
visit_expr(p, v.column_index)
|
||||||
|
push_generic_token(p, .Close_Bracket, 0)
|
||||||
|
case ^Proc_Group:
|
||||||
push_generic_token(p, v.tok.kind, 1)
|
push_generic_token(p, v.tok.kind, 1)
|
||||||
|
|
||||||
if len(v.args) != 0 && v.pos.line != v.args[len(v.args) - 1].pos.line {
|
if len(v.args) != 0 && v.pos.line != v.args[len(v.args) - 1].pos.line {
|
||||||
@@ -1181,7 +1206,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
|
|||||||
push_generic_token(p, .Close_Brace, 0)
|
push_generic_token(p, .Close_Brace, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
case Comp_Lit:
|
case ^Comp_Lit:
|
||||||
if v.type != nil {
|
if v.type != nil {
|
||||||
visit_expr(p, v.type)
|
visit_expr(p, v.type)
|
||||||
}
|
}
|
||||||
@@ -1198,18 +1223,18 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
|
|||||||
push_generic_token(p, .Close_Brace, 0)
|
push_generic_token(p, .Close_Brace, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
case Unary_Expr:
|
case ^Unary_Expr:
|
||||||
push_generic_token(p, v.op.kind, 1)
|
push_generic_token(p, v.op.kind, 1)
|
||||||
merge_next_token(p)
|
merge_next_token(p)
|
||||||
visit_expr(p, v.expr)
|
visit_expr(p, v.expr)
|
||||||
case Field_Value:
|
case ^Field_Value:
|
||||||
visit_expr(p, v.field)
|
visit_expr(p, v.field)
|
||||||
push_generic_token(p, .Eq, 1)
|
push_generic_token(p, .Eq, 1)
|
||||||
visit_expr(p, v.value)
|
visit_expr(p, v.value)
|
||||||
case Type_Assertion:
|
case ^Type_Assertion:
|
||||||
visit_expr(p, v.expr)
|
visit_expr(p, v.expr)
|
||||||
|
|
||||||
if unary, ok := v.type.derived.(Unary_Expr); ok && unary.op.text == "?" {
|
if unary, ok := v.type.derived.(^Unary_Expr); ok && unary.op.text == "?" {
|
||||||
push_generic_token(p, .Period, 0)
|
push_generic_token(p, .Period, 0)
|
||||||
visit_expr(p, v.type)
|
visit_expr(p, v.type)
|
||||||
} else {
|
} else {
|
||||||
@@ -1219,13 +1244,13 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
|
|||||||
push_generic_token(p, .Close_Paren, 0)
|
push_generic_token(p, .Close_Paren, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
case Pointer_Type:
|
case ^Pointer_Type:
|
||||||
push_generic_token(p, .Pointer, 1)
|
push_generic_token(p, .Pointer, 1)
|
||||||
merge_next_token(p)
|
merge_next_token(p)
|
||||||
visit_expr(p, v.elem)
|
visit_expr(p, v.elem)
|
||||||
case Implicit:
|
case ^Implicit:
|
||||||
push_generic_token(p, v.tok.kind, 1)
|
push_generic_token(p, v.tok.kind, 1)
|
||||||
case Poly_Type:
|
case ^Poly_Type:
|
||||||
push_generic_token(p, .Dollar, 1)
|
push_generic_token(p, .Dollar, 1)
|
||||||
merge_next_token(p)
|
merge_next_token(p)
|
||||||
visit_expr(p, v.type)
|
visit_expr(p, v.type)
|
||||||
@@ -1235,22 +1260,35 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
|
|||||||
merge_next_token(p)
|
merge_next_token(p)
|
||||||
visit_expr(p, v.specialization)
|
visit_expr(p, v.specialization)
|
||||||
}
|
}
|
||||||
case Array_Type:
|
case ^Array_Type:
|
||||||
visit_expr(p, v.tag)
|
visit_expr(p, v.tag)
|
||||||
push_generic_token(p, .Open_Bracket, 1)
|
push_generic_token(p, .Open_Bracket, 1)
|
||||||
visit_expr(p, v.len)
|
visit_expr(p, v.len)
|
||||||
push_generic_token(p, .Close_Bracket, 0)
|
push_generic_token(p, .Close_Bracket, 0)
|
||||||
merge_next_token(p)
|
merge_next_token(p)
|
||||||
visit_expr(p, v.elem)
|
visit_expr(p, v.elem)
|
||||||
case Map_Type:
|
case ^Map_Type:
|
||||||
push_generic_token(p, .Map, 1)
|
push_generic_token(p, .Map, 1)
|
||||||
push_generic_token(p, .Open_Bracket, 0)
|
push_generic_token(p, .Open_Bracket, 0)
|
||||||
visit_expr(p, v.key)
|
visit_expr(p, v.key)
|
||||||
push_generic_token(p, .Close_Bracket, 0)
|
push_generic_token(p, .Close_Bracket, 0)
|
||||||
merge_next_token(p)
|
merge_next_token(p)
|
||||||
visit_expr(p, v.value)
|
visit_expr(p, v.value)
|
||||||
case Helper_Type:
|
case ^Helper_Type:
|
||||||
visit_expr(p, v.type)
|
visit_expr(p, v.type)
|
||||||
|
case ^Multi_Pointer_Type:
|
||||||
|
push_generic_token(p, .Open_Bracket, 1)
|
||||||
|
push_generic_token(p, .Pointer, 0)
|
||||||
|
push_generic_token(p, .Close_Bracket, 0)
|
||||||
|
visit_expr(p, v.elem)
|
||||||
|
case ^Matrix_Type:
|
||||||
|
push_generic_token(p, .Matrix, 1)
|
||||||
|
push_generic_token(p, .Open_Bracket, 0)
|
||||||
|
visit_expr(p, v.row_count)
|
||||||
|
push_generic_token(p, .Comma, 0)
|
||||||
|
visit_expr(p, v.column_count)
|
||||||
|
push_generic_token(p, .Close_Bracket, 0)
|
||||||
|
visit_expr(p, v.elem)
|
||||||
case:
|
case:
|
||||||
panic(fmt.aprint(expr.derived))
|
panic(fmt.aprint(expr.derived))
|
||||||
}
|
}
|
||||||
@@ -1348,7 +1386,7 @@ visit_field_list :: proc(p: ^Printer, list: ^ast.Field_List, options := List_Opt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type, is_proc_lit := false) {
|
visit_proc_type :: proc(p: ^Printer, proc_type: ^ast.Proc_Type, is_proc_lit := false) {
|
||||||
if is_proc_lit {
|
if is_proc_lit {
|
||||||
push_format_token(p, Format_Token {
|
push_format_token(p, Format_Token {
|
||||||
kind = .Proc,
|
kind = .Proc,
|
||||||
@@ -1392,7 +1430,7 @@ visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type, is_proc_lit := fa
|
|||||||
} else if len(proc_type.results.list) == 1 {
|
} else if len(proc_type.results.list) == 1 {
|
||||||
|
|
||||||
for name in proc_type.results.list[0].names {
|
for name in proc_type.results.list[0].names {
|
||||||
if ident, ok := name.derived.(ast.Ident); ok {
|
if ident, ok := name.derived.(^ast.Ident); ok {
|
||||||
if ident.name != "_" {
|
if ident.name != "_" {
|
||||||
use_parens = true
|
use_parens = true
|
||||||
}
|
}
|
||||||
@@ -1410,19 +1448,19 @@ visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type, is_proc_lit := fa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
visit_binary_expr :: proc(p: ^Printer, binary: ast.Binary_Expr) {
|
visit_binary_expr :: proc(p: ^Printer, binary: ^ast.Binary_Expr) {
|
||||||
move_line(p, binary.left.pos)
|
move_line(p, binary.left.pos)
|
||||||
|
|
||||||
if v, ok := binary.left.derived.(ast.Binary_Expr); ok {
|
if v, ok := binary.left.derived.(^ast.Binary_Expr); ok {
|
||||||
visit_binary_expr(p, v)
|
visit_binary_expr(p, v)
|
||||||
} else {
|
} else {
|
||||||
visit_expr(p, binary.left)
|
visit_expr(p, binary.left)
|
||||||
}
|
}
|
||||||
|
|
||||||
either_implicit_selector := false
|
either_implicit_selector := false
|
||||||
if _, ok := binary.left.derived.(ast.Implicit_Selector_Expr); ok {
|
if _, ok := binary.left.derived.(^ast.Implicit_Selector_Expr); ok {
|
||||||
either_implicit_selector = true
|
either_implicit_selector = true
|
||||||
} else if _, ok := binary.right.derived.(ast.Implicit_Selector_Expr); ok {
|
} else if _, ok := binary.right.derived.(^ast.Implicit_Selector_Expr); ok {
|
||||||
either_implicit_selector = true
|
either_implicit_selector = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1439,7 +1477,7 @@ visit_binary_expr :: proc(p: ^Printer, binary: ast.Binary_Expr) {
|
|||||||
move_line(p, binary.right.pos)
|
move_line(p, binary.right.pos)
|
||||||
|
|
||||||
|
|
||||||
if v, ok := binary.right.derived.(ast.Binary_Expr); ok {
|
if v, ok := binary.right.derived.(^ast.Binary_Expr); ok {
|
||||||
visit_binary_expr(p, v)
|
visit_binary_expr(p, v)
|
||||||
} else {
|
} else {
|
||||||
visit_expr(p, binary.right)
|
visit_expr(p, binary.right)
|
||||||
@@ -1499,7 +1537,7 @@ visit_signature_list :: proc(p: ^Printer, list: ^ast.Field_List, remove_blank :=
|
|||||||
named := false
|
named := false
|
||||||
|
|
||||||
for name in field.names {
|
for name in field.names {
|
||||||
if ident, ok := name.derived.(ast.Ident); ok {
|
if ident, ok := name.derived.(^ast.Ident); ok {
|
||||||
//for some reason the parser uses _ to mean empty
|
//for some reason the parser uses _ to mean empty
|
||||||
if ident.name != "_" || !remove_blank {
|
if ident.name != "_" || !remove_blank {
|
||||||
named = true
|
named = true
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package os
|
||||||
|
|
||||||
|
import "core:strings"
|
||||||
|
import "core:mem"
|
||||||
|
|
||||||
|
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
|
||||||
|
dirp: Dir
|
||||||
|
dirp, err = _fdopendir(fd)
|
||||||
|
if err != ERROR_NONE {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer _closedir(dirp)
|
||||||
|
|
||||||
|
// XXX OpenBSD
|
||||||
|
dirpath: string
|
||||||
|
dirpath, err = absolute_path_from_handle(fd)
|
||||||
|
|
||||||
|
if err != ERROR_NONE {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer delete(dirpath)
|
||||||
|
|
||||||
|
n := n
|
||||||
|
size := n
|
||||||
|
if n <= 0 {
|
||||||
|
n = -1
|
||||||
|
size = 100
|
||||||
|
}
|
||||||
|
|
||||||
|
dfi := make([dynamic]File_Info, 0, size, allocator)
|
||||||
|
|
||||||
|
for {
|
||||||
|
entry: Dirent
|
||||||
|
end_of_stream: bool
|
||||||
|
entry, err, end_of_stream = _readdir(dirp)
|
||||||
|
if err != ERROR_NONE {
|
||||||
|
for fi_ in dfi {
|
||||||
|
file_info_delete(fi_, allocator)
|
||||||
|
}
|
||||||
|
delete(dfi)
|
||||||
|
return
|
||||||
|
} else if end_of_stream {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
fi_: File_Info
|
||||||
|
filename := cast(string)(transmute(cstring)mem.Raw_Cstring{ data = &entry.name[0] })
|
||||||
|
|
||||||
|
if filename == "." || filename == ".." {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fullpath := strings.join( []string{ dirpath, filename }, "/", context.temp_allocator)
|
||||||
|
defer delete(fullpath, context.temp_allocator)
|
||||||
|
|
||||||
|
fi_, err = stat(fullpath, allocator)
|
||||||
|
if err != ERROR_NONE {
|
||||||
|
for fi__ in dfi {
|
||||||
|
file_info_delete(fi__, allocator)
|
||||||
|
}
|
||||||
|
delete(dfi)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
append(&dfi, fi_)
|
||||||
|
}
|
||||||
|
|
||||||
|
return dfi[:], ERROR_NONE
|
||||||
|
}
|
||||||
@@ -108,7 +108,11 @@ read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) {
|
|||||||
buf8: [4*BUF_SIZE]u8
|
buf8: [4*BUF_SIZE]u8
|
||||||
|
|
||||||
for n < len(b) && err == 0 {
|
for n < len(b) && err == 0 {
|
||||||
max_read := u32(min(BUF_SIZE, len(b)/4))
|
min_read := max(len(b)/4, 1 if len(b) > 0 else 0)
|
||||||
|
max_read := u32(min(BUF_SIZE, min_read))
|
||||||
|
if max_read == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
single_read_length: u32
|
single_read_length: u32
|
||||||
ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil)
|
ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil)
|
||||||
@@ -129,9 +133,16 @@ read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) {
|
|||||||
b[n] = x
|
b[n] = x
|
||||||
n += 1
|
n += 1
|
||||||
}
|
}
|
||||||
if ctrl_z || single_read_length < len(buf16) {
|
if ctrl_z || single_read_length < max_read {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE(bill): if the last two values were a newline, then it is expected that
|
||||||
|
// this is the end of the input
|
||||||
|
if n >= 2 && single_read_length == max_read && string(b[n-2:n]) == "\r\n" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|||||||
+12
-2
@@ -313,6 +313,7 @@ foreign libc {
|
|||||||
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
|
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
|
||||||
@(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring ---
|
@(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring ---
|
||||||
@(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int ---
|
@(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int ---
|
||||||
|
@(link_name="mkdir") _unix_mkdir :: proc(buf: cstring, mode: u32) -> c.int ---
|
||||||
@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
|
@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
|
||||||
|
|
||||||
@(link_name="strerror") _darwin_string_error :: proc(num : c.int) -> cstring ---
|
@(link_name="strerror") _darwin_string_error :: proc(num : c.int) -> cstring ---
|
||||||
@@ -344,9 +345,8 @@ get_last_error_string :: proc() -> string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open :: proc(path: string, flags: int = O_RDWR, mode: int = 0) -> (Handle, Errno) {
|
open :: proc(path: string, flags: int = O_RDWR, mode: int = 0) -> (Handle, Errno) {
|
||||||
cstr := strings.clone_to_cstring(path)
|
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||||
handle := _unix_open(cstr, i32(flags), u16(mode))
|
handle := _unix_open(cstr, i32(flags), u16(mode))
|
||||||
delete(cstr)
|
|
||||||
if handle == -1 {
|
if handle == -1 {
|
||||||
return INVALID_HANDLE, 1
|
return INVALID_HANDLE, 1
|
||||||
}
|
}
|
||||||
@@ -670,7 +670,17 @@ set_current_directory :: proc(path: string) -> (err: Errno) {
|
|||||||
return ERROR_NONE
|
return ERROR_NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
make_directory :: proc(path: string, mode: u32 = 0o775) -> Errno {
|
||||||
|
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||||
|
res := _unix_mkdir(path_cstr, mode)
|
||||||
|
if res == -1 {
|
||||||
|
return Errno(get_last_error())
|
||||||
|
}
|
||||||
|
return ERROR_NONE
|
||||||
|
}
|
||||||
|
|
||||||
exit :: proc "contextless" (code: int) -> ! {
|
exit :: proc "contextless" (code: int) -> ! {
|
||||||
|
runtime._cleanup_runtime_contextless()
|
||||||
_unix_exit(i32(code))
|
_unix_exit(i32(code))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+273
-279
@@ -7,149 +7,149 @@ import "core:runtime"
|
|||||||
import "core:strings"
|
import "core:strings"
|
||||||
import "core:c"
|
import "core:c"
|
||||||
|
|
||||||
Handle :: distinct i32;
|
Handle :: distinct i32
|
||||||
File_Time :: distinct u64;
|
File_Time :: distinct u64
|
||||||
Errno :: distinct i32;
|
Errno :: distinct i32
|
||||||
Syscall :: distinct i32;
|
Syscall :: distinct i32
|
||||||
|
|
||||||
INVALID_HANDLE :: ~Handle(0);
|
INVALID_HANDLE :: ~Handle(0)
|
||||||
|
|
||||||
ERROR_NONE: Errno : 0;
|
ERROR_NONE: Errno : 0
|
||||||
EPERM: Errno : 1;
|
EPERM: Errno : 1
|
||||||
ENOENT: Errno : 2;
|
ENOENT: Errno : 2
|
||||||
ESRCH: Errno : 3;
|
ESRCH: Errno : 3
|
||||||
EINTR: Errno : 4;
|
EINTR: Errno : 4
|
||||||
EIO: Errno : 5;
|
EIO: Errno : 5
|
||||||
ENXIO: Errno : 6;
|
ENXIO: Errno : 6
|
||||||
E2BIG: Errno : 7;
|
E2BIG: Errno : 7
|
||||||
ENOEXEC: Errno : 8;
|
ENOEXEC: Errno : 8
|
||||||
EBADF: Errno : 9;
|
EBADF: Errno : 9
|
||||||
ECHILD: Errno : 10;
|
ECHILD: Errno : 10
|
||||||
EBEADLK: Errno : 11;
|
EBEADLK: Errno : 11
|
||||||
ENOMEM: Errno : 12;
|
ENOMEM: Errno : 12
|
||||||
EACCESS: Errno : 13;
|
EACCESS: Errno : 13
|
||||||
EFAULT: Errno : 14;
|
EFAULT: Errno : 14
|
||||||
ENOTBLK: Errno : 15;
|
ENOTBLK: Errno : 15
|
||||||
EBUSY: Errno : 16;
|
EBUSY: Errno : 16
|
||||||
EEXIST: Errno : 17;
|
EEXIST: Errno : 17
|
||||||
EXDEV: Errno : 18;
|
EXDEV: Errno : 18
|
||||||
ENODEV: Errno : 19;
|
ENODEV: Errno : 19
|
||||||
ENOTDIR: Errno : 20;
|
ENOTDIR: Errno : 20
|
||||||
EISDIR: Errno : 21;
|
EISDIR: Errno : 21
|
||||||
EINVAL: Errno : 22;
|
EINVAL: Errno : 22
|
||||||
ENFILE: Errno : 23;
|
ENFILE: Errno : 23
|
||||||
EMFILE: Errno : 24;
|
EMFILE: Errno : 24
|
||||||
ENOTTY: Errno : 25;
|
ENOTTY: Errno : 25
|
||||||
ETXTBSY: Errno : 26;
|
ETXTBSY: Errno : 26
|
||||||
EFBIG: Errno : 27;
|
EFBIG: Errno : 27
|
||||||
ENOSPC: Errno : 28;
|
ENOSPC: Errno : 28
|
||||||
ESPIPE: Errno : 29;
|
ESPIPE: Errno : 29
|
||||||
EROFS: Errno : 30;
|
EROFS: Errno : 30
|
||||||
EMLINK: Errno : 31;
|
EMLINK: Errno : 31
|
||||||
EPIPE: Errno : 32;
|
EPIPE: Errno : 32
|
||||||
EDOM: Errno : 33;
|
EDOM: Errno : 33
|
||||||
ERANGE: Errno : 34; /* Result too large */
|
ERANGE: Errno : 34 /* Result too large */
|
||||||
EAGAIN: Errno : 35;
|
EAGAIN: Errno : 35
|
||||||
EINPROGRESS: Errno : 36;
|
EINPROGRESS: Errno : 36
|
||||||
EALREADY: Errno : 37;
|
EALREADY: Errno : 37
|
||||||
ENOTSOCK: Errno : 38;
|
ENOTSOCK: Errno : 38
|
||||||
EDESTADDRREQ: Errno : 39;
|
EDESTADDRREQ: Errno : 39
|
||||||
EMSGSIZE: Errno : 40;
|
EMSGSIZE: Errno : 40
|
||||||
EPROTOTYPE: Errno : 41;
|
EPROTOTYPE: Errno : 41
|
||||||
ENOPROTOOPT: Errno : 42;
|
ENOPROTOOPT: Errno : 42
|
||||||
EPROTONOSUPPORT: Errno : 43;
|
EPROTONOSUPPORT: Errno : 43
|
||||||
ESOCKTNOSUPPORT: Errno : 44;
|
ESOCKTNOSUPPORT: Errno : 44
|
||||||
EOPNOTSUPP: Errno : 45;
|
EOPNOTSUPP: Errno : 45
|
||||||
EPFNOSUPPORT: Errno : 46;
|
EPFNOSUPPORT: Errno : 46
|
||||||
EAFNOSUPPORT: Errno : 47;
|
EAFNOSUPPORT: Errno : 47
|
||||||
EADDRINUSE: Errno : 48;
|
EADDRINUSE: Errno : 48
|
||||||
EADDRNOTAVAIL: Errno : 49;
|
EADDRNOTAVAIL: Errno : 49
|
||||||
ENETDOWN: Errno : 50;
|
ENETDOWN: Errno : 50
|
||||||
ENETUNREACH: Errno : 51;
|
ENETUNREACH: Errno : 51
|
||||||
ENETRESET: Errno : 52;
|
ENETRESET: Errno : 52
|
||||||
ECONNABORTED: Errno : 53;
|
ECONNABORTED: Errno : 53
|
||||||
ECONNRESET: Errno : 54;
|
ECONNRESET: Errno : 54
|
||||||
ENOBUFS: Errno : 55;
|
ENOBUFS: Errno : 55
|
||||||
EISCONN: Errno : 56;
|
EISCONN: Errno : 56
|
||||||
ENOTCONN: Errno : 57;
|
ENOTCONN: Errno : 57
|
||||||
ESHUTDOWN: Errno : 58;
|
ESHUTDOWN: Errno : 58
|
||||||
ETIMEDOUT: Errno : 60;
|
ETIMEDOUT: Errno : 60
|
||||||
ECONNREFUSED: Errno : 61;
|
ECONNREFUSED: Errno : 61
|
||||||
ELOOP: Errno : 62;
|
ELOOP: Errno : 62
|
||||||
ENAMETOOLING: Errno : 63;
|
ENAMETOOLING: Errno : 63
|
||||||
EHOSTDOWN: Errno : 64;
|
EHOSTDOWN: Errno : 64
|
||||||
EHOSTUNREACH: Errno : 65;
|
EHOSTUNREACH: Errno : 65
|
||||||
ENOTEMPTY: Errno : 66;
|
ENOTEMPTY: Errno : 66
|
||||||
EPROCLIM: Errno : 67;
|
EPROCLIM: Errno : 67
|
||||||
EUSERS: Errno : 68;
|
EUSERS: Errno : 68
|
||||||
EDQUOT: Errno : 69;
|
EDQUOT: Errno : 69
|
||||||
ESTALE: Errno : 70;
|
ESTALE: Errno : 70
|
||||||
EBADRPC: Errno : 72;
|
EBADRPC: Errno : 72
|
||||||
ERPCMISMATCH: Errno : 73;
|
ERPCMISMATCH: Errno : 73
|
||||||
EPROGUNAVAIL: Errno : 74;
|
EPROGUNAVAIL: Errno : 74
|
||||||
EPROGMISMATCH: Errno : 75;
|
EPROGMISMATCH: Errno : 75
|
||||||
EPROCUNAVAIL: Errno : 76;
|
EPROCUNAVAIL: Errno : 76
|
||||||
ENOLCK: Errno : 77;
|
ENOLCK: Errno : 77
|
||||||
ENOSYS: Errno : 78;
|
ENOSYS: Errno : 78
|
||||||
EFTYPE: Errno : 79;
|
EFTYPE: Errno : 79
|
||||||
EAUTH: Errno : 80;
|
EAUTH: Errno : 80
|
||||||
ENEEDAUTH: Errno : 81;
|
ENEEDAUTH: Errno : 81
|
||||||
EIDRM: Errno : 82;
|
EIDRM: Errno : 82
|
||||||
ENOMSG: Errno : 83;
|
ENOMSG: Errno : 83
|
||||||
EOVERFLOW: Errno : 84;
|
EOVERFLOW: Errno : 84
|
||||||
ECANCELED: Errno : 85;
|
ECANCELED: Errno : 85
|
||||||
EILSEQ: Errno : 86;
|
EILSEQ: Errno : 86
|
||||||
ENOATTR: Errno : 87;
|
ENOATTR: Errno : 87
|
||||||
EDOOFUS: Errno : 88;
|
EDOOFUS: Errno : 88
|
||||||
EBADMSG: Errno : 89;
|
EBADMSG: Errno : 89
|
||||||
EMULTIHOP: Errno : 90;
|
EMULTIHOP: Errno : 90
|
||||||
ENOLINK: Errno : 91;
|
ENOLINK: Errno : 91
|
||||||
EPROTO: Errno : 92;
|
EPROTO: Errno : 92
|
||||||
ENOTCAPABLE: Errno : 93;
|
ENOTCAPABLE: Errno : 93
|
||||||
ECAPMODE: Errno : 94;
|
ECAPMODE: Errno : 94
|
||||||
ENOTRECOVERABLE: Errno : 95;
|
ENOTRECOVERABLE: Errno : 95
|
||||||
EOWNERDEAD: Errno : 96;
|
EOWNERDEAD: Errno : 96
|
||||||
|
|
||||||
O_RDONLY :: 0x00000;
|
O_RDONLY :: 0x00000
|
||||||
O_WRONLY :: 0x00001;
|
O_WRONLY :: 0x00001
|
||||||
O_RDWR :: 0x00002;
|
O_RDWR :: 0x00002
|
||||||
O_CREATE :: 0x00040;
|
O_CREATE :: 0x00040
|
||||||
O_EXCL :: 0x00080;
|
O_EXCL :: 0x00080
|
||||||
O_NOCTTY :: 0x00100;
|
O_NOCTTY :: 0x00100
|
||||||
O_TRUNC :: 0x00200;
|
O_TRUNC :: 0x00200
|
||||||
O_NONBLOCK :: 0x00800;
|
O_NONBLOCK :: 0x00800
|
||||||
O_APPEND :: 0x00400;
|
O_APPEND :: 0x00400
|
||||||
O_SYNC :: 0x01000;
|
O_SYNC :: 0x01000
|
||||||
O_ASYNC :: 0x02000;
|
O_ASYNC :: 0x02000
|
||||||
O_CLOEXEC :: 0x80000;
|
O_CLOEXEC :: 0x80000
|
||||||
|
|
||||||
|
|
||||||
SEEK_SET :: 0;
|
SEEK_SET :: 0
|
||||||
SEEK_CUR :: 1;
|
SEEK_CUR :: 1
|
||||||
SEEK_END :: 2;
|
SEEK_END :: 2
|
||||||
SEEK_DATA :: 3;
|
SEEK_DATA :: 3
|
||||||
SEEK_HOLE :: 4;
|
SEEK_HOLE :: 4
|
||||||
SEEK_MAX :: SEEK_HOLE;
|
SEEK_MAX :: SEEK_HOLE
|
||||||
|
|
||||||
// NOTE: These are OS specific!
|
// NOTE: These are OS specific!
|
||||||
// Do not mix these up!
|
// Do not mix these up!
|
||||||
RTLD_LAZY :: 0x001;
|
RTLD_LAZY :: 0x001
|
||||||
RTLD_NOW :: 0x002;
|
RTLD_NOW :: 0x002
|
||||||
//RTLD_BINDING_MASK :: 0x3; // Called MODEMASK in dlfcn.h
|
//RTLD_BINDING_MASK :: 0x3 // Called MODEMASK in dlfcn.h
|
||||||
RTLD_GLOBAL :: 0x100;
|
RTLD_GLOBAL :: 0x100
|
||||||
RTLD_LOCAL :: 0x000;
|
RTLD_LOCAL :: 0x000
|
||||||
RTLD_TRACE :: 0x200;
|
RTLD_TRACE :: 0x200
|
||||||
RTLD_NODELETE :: 0x01000;
|
RTLD_NODELETE :: 0x01000
|
||||||
RTLD_NOLOAD :: 0x02000;
|
RTLD_NOLOAD :: 0x02000
|
||||||
|
|
||||||
args := _alloc_command_line_arguments();
|
args := _alloc_command_line_arguments()
|
||||||
|
|
||||||
Unix_File_Time :: struct {
|
Unix_File_Time :: struct {
|
||||||
seconds: i64,
|
seconds: i64,
|
||||||
nanoseconds: c.long,
|
nanoseconds: c.long,
|
||||||
}
|
}
|
||||||
|
|
||||||
pid_t :: u32;
|
pid_t :: u32
|
||||||
|
|
||||||
OS_Stat :: struct {
|
OS_Stat :: struct {
|
||||||
device_id: u64,
|
device_id: u64,
|
||||||
@@ -177,297 +177,291 @@ OS_Stat :: struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// File type
|
// File type
|
||||||
S_IFMT :: 0o170000; // Type of file mask
|
S_IFMT :: 0o170000 // Type of file mask
|
||||||
S_IFIFO :: 0o010000; // Named pipe (fifo)
|
S_IFIFO :: 0o010000 // Named pipe (fifo)
|
||||||
S_IFCHR :: 0o020000; // Character special
|
S_IFCHR :: 0o020000 // Character special
|
||||||
S_IFDIR :: 0o040000; // Directory
|
S_IFDIR :: 0o040000 // Directory
|
||||||
S_IFBLK :: 0o060000; // Block special
|
S_IFBLK :: 0o060000 // Block special
|
||||||
S_IFREG :: 0o100000; // Regular
|
S_IFREG :: 0o100000 // Regular
|
||||||
S_IFLNK :: 0o120000; // Symbolic link
|
S_IFLNK :: 0o120000 // Symbolic link
|
||||||
S_IFSOCK :: 0o140000; // Socket
|
S_IFSOCK :: 0o140000 // Socket
|
||||||
//S_ISVTX :: 0o001000; // Save swapped text even after use
|
//S_ISVTX :: 0o001000 // Save swapped text even after use
|
||||||
|
|
||||||
// File mode
|
// File mode
|
||||||
// Read, write, execute/search by owner
|
// Read, write, execute/search by owner
|
||||||
S_IRWXU :: 0o0700; // RWX mask for owner
|
S_IRWXU :: 0o0700 // RWX mask for owner
|
||||||
S_IRUSR :: 0o0400; // R for owner
|
S_IRUSR :: 0o0400 // R for owner
|
||||||
S_IWUSR :: 0o0200; // W for owner
|
S_IWUSR :: 0o0200 // W for owner
|
||||||
S_IXUSR :: 0o0100; // X for owner
|
S_IXUSR :: 0o0100 // X for owner
|
||||||
|
|
||||||
// Read, write, execute/search by group
|
// Read, write, execute/search by group
|
||||||
S_IRWXG :: 0o0070; // RWX mask for group
|
S_IRWXG :: 0o0070 // RWX mask for group
|
||||||
S_IRGRP :: 0o0040; // R for group
|
S_IRGRP :: 0o0040 // R for group
|
||||||
S_IWGRP :: 0o0020; // W for group
|
S_IWGRP :: 0o0020 // W for group
|
||||||
S_IXGRP :: 0o0010; // X for group
|
S_IXGRP :: 0o0010 // X for group
|
||||||
|
|
||||||
// Read, write, execute/search by others
|
// Read, write, execute/search by others
|
||||||
S_IRWXO :: 0o0007; // RWX mask for other
|
S_IRWXO :: 0o0007 // RWX mask for other
|
||||||
S_IROTH :: 0o0004; // R for other
|
S_IROTH :: 0o0004 // R for other
|
||||||
S_IWOTH :: 0o0002; // W for other
|
S_IWOTH :: 0o0002 // W for other
|
||||||
S_IXOTH :: 0o0001; // X for other
|
S_IXOTH :: 0o0001 // X for other
|
||||||
|
|
||||||
S_ISUID :: 0o4000; // Set user id on execution
|
S_ISUID :: 0o4000 // Set user id on execution
|
||||||
S_ISGID :: 0o2000; // Set group id on execution
|
S_ISGID :: 0o2000 // Set group id on execution
|
||||||
S_ISVTX :: 0o1000; // Directory restrcted delete
|
S_ISVTX :: 0o1000 // Directory restrcted delete
|
||||||
|
|
||||||
|
|
||||||
S_ISLNK :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFLNK;
|
S_ISLNK :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFLNK
|
||||||
S_ISREG :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFREG;
|
S_ISREG :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFREG
|
||||||
S_ISDIR :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFDIR;
|
S_ISDIR :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFDIR
|
||||||
S_ISCHR :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFCHR;
|
S_ISCHR :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFCHR
|
||||||
S_ISBLK :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFBLK;
|
S_ISBLK :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFBLK
|
||||||
S_ISFIFO :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFIFO;
|
S_ISFIFO :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFIFO
|
||||||
S_ISSOCK :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFSOCK;
|
S_ISSOCK :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFSOCK
|
||||||
|
|
||||||
F_OK :: 0; // Test for file existance
|
F_OK :: 0 // Test for file existance
|
||||||
X_OK :: 1; // Test for execute permission
|
X_OK :: 1 // Test for execute permission
|
||||||
W_OK :: 2; // Test for write permission
|
W_OK :: 2 // Test for write permission
|
||||||
R_OK :: 4; // Test for read permission
|
R_OK :: 4 // Test for read permission
|
||||||
|
|
||||||
foreign libc {
|
foreign libc {
|
||||||
@(link_name="__error") __errno_location :: proc() -> ^int ---;
|
@(link_name="__error") __errno_location :: proc() -> ^int ---
|
||||||
@(link_name="syscall") syscall :: proc(number: Syscall, #c_vararg args: ..any) -> int ---;
|
@(link_name="syscall") syscall :: proc(number: Syscall, #c_vararg args: ..any) -> int ---
|
||||||
|
|
||||||
@(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---;
|
@(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---
|
||||||
@(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---;
|
@(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---
|
||||||
@(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---;
|
@(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
|
||||||
@(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---;
|
@(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
|
||||||
@(link_name="lseek64") _unix_seek :: proc(fd: Handle, offset: i64, whence: c.int) -> i64 ---;
|
@(link_name="lseek64") _unix_seek :: proc(fd: Handle, offset: i64, whence: c.int) -> i64 ---
|
||||||
@(link_name="gettid") _unix_gettid :: proc() -> u64 ---;
|
@(link_name="gettid") _unix_gettid :: proc() -> u64 ---
|
||||||
@(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int ---;
|
@(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int ---
|
||||||
@(link_name="stat64") _unix_stat :: proc(path: cstring, stat: ^OS_Stat) -> c.int ---;
|
@(link_name="stat64") _unix_stat :: proc(path: cstring, stat: ^OS_Stat) -> c.int ---
|
||||||
@(link_name="fstat") _unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> c.int ---;
|
@(link_name="fstat") _unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> c.int ---
|
||||||
@(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int ---;
|
@(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int ---
|
||||||
|
|
||||||
@(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr ---;
|
@(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr ---
|
||||||
@(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr ---;
|
@(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr ---
|
||||||
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---;
|
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---
|
||||||
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---;
|
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
|
||||||
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---;
|
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
|
||||||
@(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring ---;
|
@(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring ---
|
||||||
@(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int ---;
|
@(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int ---
|
||||||
|
|
||||||
@(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---;
|
@(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---
|
||||||
}
|
}
|
||||||
foreign dl {
|
foreign dl {
|
||||||
@(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: c.int) -> rawptr ---;
|
@(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: c.int) -> rawptr ---
|
||||||
@(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr ---;
|
@(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr ---
|
||||||
@(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> c.int ---;
|
@(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> c.int ---
|
||||||
@(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---;
|
@(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---
|
||||||
|
|
||||||
@(link_name="pthread_getthreadid_np") pthread_getthreadid_np :: proc() -> c.int ---;
|
@(link_name="pthread_getthreadid_np") pthread_getthreadid_np :: proc() -> c.int ---
|
||||||
}
|
}
|
||||||
|
|
||||||
is_path_separator :: proc(r: rune) -> bool {
|
is_path_separator :: proc(r: rune) -> bool {
|
||||||
return r == '/';
|
return r == '/'
|
||||||
}
|
}
|
||||||
|
|
||||||
get_last_error :: proc() -> int {
|
get_last_error :: proc() -> int {
|
||||||
return __errno_location()^;
|
return __errno_location()^
|
||||||
}
|
}
|
||||||
|
|
||||||
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
|
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
|
||||||
cstr := strings.clone_to_cstring(path);
|
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||||
handle := _unix_open(cstr, c.int(flags), c.int(mode));
|
handle := _unix_open(cstr, c.int(flags), c.int(mode))
|
||||||
delete(cstr);
|
|
||||||
if handle == -1 {
|
if handle == -1 {
|
||||||
return INVALID_HANDLE, Errno(get_last_error());
|
return INVALID_HANDLE, Errno(get_last_error())
|
||||||
}
|
}
|
||||||
return handle, ERROR_NONE;
|
return handle, ERROR_NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
close :: proc(fd: Handle) -> Errno {
|
close :: proc(fd: Handle) -> Errno {
|
||||||
result := _unix_close(fd);
|
result := _unix_close(fd)
|
||||||
if result == -1 {
|
if result == -1 {
|
||||||
return Errno(get_last_error());
|
return Errno(get_last_error())
|
||||||
}
|
}
|
||||||
return ERROR_NONE;
|
return ERROR_NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||||
bytes_read := _unix_read(fd, &data[0], c.size_t(len(data)));
|
bytes_read := _unix_read(fd, &data[0], c.size_t(len(data)))
|
||||||
if bytes_read == -1 {
|
if bytes_read == -1 {
|
||||||
return -1, Errno(get_last_error());
|
return -1, Errno(get_last_error())
|
||||||
}
|
}
|
||||||
return int(bytes_read), ERROR_NONE;
|
return int(bytes_read), ERROR_NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
return 0, ERROR_NONE;
|
return 0, ERROR_NONE
|
||||||
}
|
}
|
||||||
bytes_written := _unix_write(fd, &data[0], c.size_t(len(data)));
|
bytes_written := _unix_write(fd, &data[0], c.size_t(len(data)))
|
||||||
if bytes_written == -1 {
|
if bytes_written == -1 {
|
||||||
return -1, Errno(get_last_error());
|
return -1, Errno(get_last_error())
|
||||||
}
|
}
|
||||||
return int(bytes_written), ERROR_NONE;
|
return int(bytes_written), ERROR_NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
|
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
|
||||||
res := _unix_seek(fd, offset, c.int(whence));
|
res := _unix_seek(fd, offset, c.int(whence))
|
||||||
if res == -1 {
|
if res == -1 {
|
||||||
return -1, Errno(get_last_error());
|
return -1, Errno(get_last_error())
|
||||||
}
|
}
|
||||||
return res, ERROR_NONE;
|
return res, ERROR_NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
file_size :: proc(fd: Handle) -> (i64, Errno) {
|
file_size :: proc(fd: Handle) -> (i64, Errno) {
|
||||||
s, err := fstat(fd);
|
s, err := fstat(fd)
|
||||||
if err != ERROR_NONE {
|
if err != ERROR_NONE {
|
||||||
return -1, err;
|
return -1, err
|
||||||
}
|
}
|
||||||
return s.size, ERROR_NONE;
|
return s.size, ERROR_NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
stdin: Handle = 0;
|
stdin: Handle = 0
|
||||||
stdout: Handle = 1;
|
stdout: Handle = 1
|
||||||
stderr: Handle = 2;
|
stderr: Handle = 2
|
||||||
|
|
||||||
last_write_time :: proc(fd: Handle) -> (File_Time, Errno) {
|
last_write_time :: proc(fd: Handle) -> (File_Time, Errno) {
|
||||||
s, err := fstat(fd);
|
s, err := fstat(fd)
|
||||||
if err != ERROR_NONE {
|
if err != ERROR_NONE {
|
||||||
return 0, err;
|
return 0, err
|
||||||
}
|
}
|
||||||
modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds;
|
modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
|
||||||
return File_Time(modified), ERROR_NONE;
|
return File_Time(modified), ERROR_NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
|
last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
|
||||||
s, err := stat(name);
|
s, err := stat(name)
|
||||||
if err != ERROR_NONE {
|
if err != ERROR_NONE {
|
||||||
return 0, err;
|
return 0, err
|
||||||
}
|
}
|
||||||
modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds;
|
modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
|
||||||
return File_Time(modified), ERROR_NONE;
|
return File_Time(modified), ERROR_NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
stat :: proc(path: string) -> (OS_Stat, Errno) {
|
stat :: proc(path: string) -> (OS_Stat, Errno) {
|
||||||
cstr := strings.clone_to_cstring(path);
|
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||||
defer delete(cstr);
|
s: OS_Stat
|
||||||
|
result := _unix_stat(cstr, &s)
|
||||||
s: OS_Stat;
|
|
||||||
result := _unix_stat(cstr, &s);
|
|
||||||
if result == -1 {
|
if result == -1 {
|
||||||
return s, Errno(get_last_error());
|
return s, Errno(get_last_error())
|
||||||
}
|
}
|
||||||
return s, ERROR_NONE;
|
return s, ERROR_NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
fstat :: proc(fd: Handle) -> (OS_Stat, Errno) {
|
fstat :: proc(fd: Handle) -> (OS_Stat, Errno) {
|
||||||
s: OS_Stat;
|
s: OS_Stat
|
||||||
result := _unix_fstat(fd, &s);
|
result := _unix_fstat(fd, &s)
|
||||||
if result == -1 {
|
if result == -1 {
|
||||||
return s, Errno(get_last_error());
|
return s, Errno(get_last_error())
|
||||||
}
|
}
|
||||||
return s, ERROR_NONE;
|
return s, ERROR_NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
access :: proc(path: string, mask: int) -> (bool, Errno) {
|
access :: proc(path: string, mask: int) -> (bool, Errno) {
|
||||||
cstr := strings.clone_to_cstring(path);
|
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||||
defer delete(cstr);
|
result := _unix_access(cstr, c.int(mask))
|
||||||
result := _unix_access(cstr, c.int(mask));
|
|
||||||
if result == -1 {
|
if result == -1 {
|
||||||
return false, Errno(get_last_error());
|
return false, Errno(get_last_error())
|
||||||
}
|
}
|
||||||
return true, ERROR_NONE;
|
return true, ERROR_NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
heap_alloc :: proc(size: int) -> rawptr {
|
heap_alloc :: proc(size: int) -> rawptr {
|
||||||
assert(size >= 0);
|
assert(size >= 0)
|
||||||
return _unix_calloc(1, c.size_t(size));
|
return _unix_calloc(1, c.size_t(size))
|
||||||
}
|
}
|
||||||
|
|
||||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||||
// NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on
|
// NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on
|
||||||
// POSIX platforms. Ensure your caller takes this into account.
|
// POSIX platforms. Ensure your caller takes this into account.
|
||||||
return _unix_realloc(ptr, c.size_t(new_size));
|
return _unix_realloc(ptr, c.size_t(new_size))
|
||||||
}
|
}
|
||||||
|
|
||||||
heap_free :: proc(ptr: rawptr) {
|
heap_free :: proc(ptr: rawptr) {
|
||||||
_unix_free(ptr);
|
_unix_free(ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
getenv :: proc(name: string) -> (string, bool) {
|
getenv :: proc(name: string) -> (string, bool) {
|
||||||
path_str := strings.clone_to_cstring(name);
|
path_str := strings.clone_to_cstring(name, context.temp_allocator)
|
||||||
defer delete(path_str);
|
cstr := _unix_getenv(path_str)
|
||||||
cstr := _unix_getenv(path_str);
|
|
||||||
if cstr == nil {
|
if cstr == nil {
|
||||||
return "", false;
|
return "", false
|
||||||
}
|
}
|
||||||
return string(cstr), true;
|
return string(cstr), true
|
||||||
}
|
}
|
||||||
|
|
||||||
get_current_directory :: proc() -> string {
|
get_current_directory :: proc() -> string {
|
||||||
// NOTE(tetra): I would use PATH_MAX here, but I was not able to find
|
// NOTE(tetra): I would use PATH_MAX here, but I was not able to find
|
||||||
// an authoritative value for it across all systems.
|
// an authoritative value for it across all systems.
|
||||||
// The largest value I could find was 4096, so might as well use the page size.
|
// The largest value I could find was 4096, so might as well use the page size.
|
||||||
page_size := get_page_size();
|
page_size := get_page_size()
|
||||||
buf := make([dynamic]u8, page_size);
|
buf := make([dynamic]u8, page_size)
|
||||||
#no_bounds_check for {
|
#no_bounds_check for {
|
||||||
cwd := _unix_getcwd(cstring(&buf[0]), c.size_t(len(buf)));
|
cwd := _unix_getcwd(cstring(&buf[0]), c.size_t(len(buf)))
|
||||||
if cwd != nil {
|
if cwd != nil {
|
||||||
return string(cwd);
|
return string(cwd)
|
||||||
}
|
}
|
||||||
if Errno(get_last_error()) != ERANGE {
|
if Errno(get_last_error()) != ERANGE {
|
||||||
return "";
|
return ""
|
||||||
}
|
}
|
||||||
resize(&buf, len(buf)+page_size);
|
resize(&buf, len(buf)+page_size)
|
||||||
}
|
}
|
||||||
unreachable();
|
unreachable()
|
||||||
}
|
}
|
||||||
|
|
||||||
set_current_directory :: proc(path: string) -> (err: Errno) {
|
set_current_directory :: proc(path: string) -> (err: Errno) {
|
||||||
cstr := strings.clone_to_cstring(path, context.temp_allocator);
|
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||||
res := _unix_chdir(cstr);
|
res := _unix_chdir(cstr)
|
||||||
if res == -1 do return Errno(get_last_error());
|
if res == -1 do return Errno(get_last_error())
|
||||||
return ERROR_NONE;
|
return ERROR_NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
exit :: proc "contextless" (code: int) -> ! {
|
exit :: proc "contextless" (code: int) -> ! {
|
||||||
_unix_exit(c.int(code));
|
runtime._cleanup_runtime_contextless()
|
||||||
|
_unix_exit(c.int(code))
|
||||||
}
|
}
|
||||||
|
|
||||||
current_thread_id :: proc "contextless" () -> int {
|
current_thread_id :: proc "contextless" () -> int {
|
||||||
return cast(int) pthread_getthreadid_np();
|
return cast(int) pthread_getthreadid_np()
|
||||||
}
|
}
|
||||||
|
|
||||||
dlopen :: proc(filename: string, flags: int) -> rawptr {
|
dlopen :: proc(filename: string, flags: int) -> rawptr {
|
||||||
cstr := strings.clone_to_cstring(filename);
|
cstr := strings.clone_to_cstring(filename, context.temp_allocator)
|
||||||
defer delete(cstr);
|
handle := _unix_dlopen(cstr, c.int(flags))
|
||||||
handle := _unix_dlopen(cstr, c.int(flags));
|
return handle
|
||||||
return handle;
|
|
||||||
}
|
}
|
||||||
dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
|
dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
|
||||||
assert(handle != nil);
|
assert(handle != nil)
|
||||||
cstr := strings.clone_to_cstring(symbol);
|
cstr := strings.clone_to_cstring(symbol, context.temp_allocator)
|
||||||
defer delete(cstr);
|
proc_handle := _unix_dlsym(handle, cstr)
|
||||||
proc_handle := _unix_dlsym(handle, cstr);
|
return proc_handle
|
||||||
return proc_handle;
|
|
||||||
}
|
}
|
||||||
dlclose :: proc(handle: rawptr) -> bool {
|
dlclose :: proc(handle: rawptr) -> bool {
|
||||||
assert(handle != nil);
|
assert(handle != nil)
|
||||||
return _unix_dlclose(handle) == 0;
|
return _unix_dlclose(handle) == 0
|
||||||
}
|
}
|
||||||
dlerror :: proc() -> string {
|
dlerror :: proc() -> string {
|
||||||
return string(_unix_dlerror());
|
return string(_unix_dlerror())
|
||||||
}
|
}
|
||||||
|
|
||||||
get_page_size :: proc() -> int {
|
get_page_size :: proc() -> int {
|
||||||
// NOTE(tetra): The page size never changes, so why do anything complicated
|
// NOTE(tetra): The page size never changes, so why do anything complicated
|
||||||
// if we don't have to.
|
// if we don't have to.
|
||||||
@static page_size := -1;
|
@static page_size := -1
|
||||||
if page_size != -1 do return page_size;
|
if page_size != -1 do return page_size
|
||||||
|
|
||||||
page_size = int(_unix_getpagesize());
|
page_size = int(_unix_getpagesize())
|
||||||
return page_size;
|
return page_size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_alloc_command_line_arguments :: proc() -> []string {
|
_alloc_command_line_arguments :: proc() -> []string {
|
||||||
res := make([]string, len(runtime.args__));
|
res := make([]string, len(runtime.args__))
|
||||||
for arg, i in runtime.args__ {
|
for arg, i in runtime.args__ {
|
||||||
res[i] = string(arg);
|
res[i] = string(arg)
|
||||||
}
|
}
|
||||||
return res;
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+17
-23
@@ -460,9 +460,8 @@ fork :: proc() -> (Pid, Errno) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
|
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
|
||||||
cstr := strings.clone_to_cstring(path)
|
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||||
handle := _unix_open(cstr, flags, mode)
|
handle := _unix_open(cstr, flags, mode)
|
||||||
defer delete(cstr)
|
|
||||||
if handle < 0 {
|
if handle < 0 {
|
||||||
return INVALID_HANDLE, _get_errno(int(handle))
|
return INVALID_HANDLE, _get_errno(int(handle))
|
||||||
}
|
}
|
||||||
@@ -501,11 +500,13 @@ seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
file_size :: proc(fd: Handle) -> (i64, Errno) {
|
file_size :: proc(fd: Handle) -> (i64, Errno) {
|
||||||
s, err := _fstat(fd)
|
// deliberately uninitialized; the syscall fills this buffer for us
|
||||||
if err != ERROR_NONE {
|
s: OS_Stat = ---
|
||||||
return 0, err
|
result := _unix_fstat(fd, &s)
|
||||||
}
|
if result < 0 {
|
||||||
return max(s.size, 0), ERROR_NONE
|
return 0, _get_errno(result)
|
||||||
|
}
|
||||||
|
return max(s.size, 0), ERROR_NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
rename :: proc(old_path, new_path: string) -> Errno {
|
rename :: proc(old_path, new_path: string) -> Errno {
|
||||||
@@ -608,8 +609,7 @@ last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
|
|||||||
|
|
||||||
@private
|
@private
|
||||||
_stat :: proc(path: string) -> (OS_Stat, Errno) {
|
_stat :: proc(path: string) -> (OS_Stat, Errno) {
|
||||||
cstr := strings.clone_to_cstring(path)
|
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||||
defer delete(cstr)
|
|
||||||
|
|
||||||
// deliberately uninitialized; the syscall fills this buffer for us
|
// deliberately uninitialized; the syscall fills this buffer for us
|
||||||
s: OS_Stat = ---
|
s: OS_Stat = ---
|
||||||
@@ -622,8 +622,7 @@ _stat :: proc(path: string) -> (OS_Stat, Errno) {
|
|||||||
|
|
||||||
@private
|
@private
|
||||||
_lstat :: proc(path: string) -> (OS_Stat, Errno) {
|
_lstat :: proc(path: string) -> (OS_Stat, Errno) {
|
||||||
cstr := strings.clone_to_cstring(path)
|
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||||
defer delete(cstr)
|
|
||||||
|
|
||||||
// deliberately uninitialized; the syscall fills this buffer for us
|
// deliberately uninitialized; the syscall fills this buffer for us
|
||||||
s: OS_Stat = ---
|
s: OS_Stat = ---
|
||||||
@@ -690,8 +689,7 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
|
|||||||
|
|
||||||
@private
|
@private
|
||||||
_readlink :: proc(path: string) -> (string, Errno) {
|
_readlink :: proc(path: string) -> (string, Errno) {
|
||||||
path_cstr := strings.clone_to_cstring(path)
|
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||||
defer delete(path_cstr)
|
|
||||||
|
|
||||||
bufsz : uint = 256
|
bufsz : uint = 256
|
||||||
buf := make([]byte, bufsz)
|
buf := make([]byte, bufsz)
|
||||||
@@ -727,8 +725,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
|
|||||||
rel = "."
|
rel = "."
|
||||||
}
|
}
|
||||||
|
|
||||||
rel_cstr := strings.clone_to_cstring(rel)
|
rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator)
|
||||||
defer delete(rel_cstr)
|
|
||||||
|
|
||||||
path_ptr := _unix_realpath(rel_cstr, nil)
|
path_ptr := _unix_realpath(rel_cstr, nil)
|
||||||
if path_ptr == nil {
|
if path_ptr == nil {
|
||||||
@@ -743,8 +740,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
access :: proc(path: string, mask: int) -> (bool, Errno) {
|
access :: proc(path: string, mask: int) -> (bool, Errno) {
|
||||||
cstr := strings.clone_to_cstring(path)
|
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||||
defer delete(cstr)
|
|
||||||
result := _unix_access(cstr, mask)
|
result := _unix_access(cstr, mask)
|
||||||
if result < 0 {
|
if result < 0 {
|
||||||
return false, _get_errno(result)
|
return false, _get_errno(result)
|
||||||
@@ -768,8 +764,7 @@ heap_free :: proc(ptr: rawptr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getenv :: proc(name: string) -> (string, bool) {
|
getenv :: proc(name: string) -> (string, bool) {
|
||||||
path_str := strings.clone_to_cstring(name)
|
path_str := strings.clone_to_cstring(name, context.temp_allocator)
|
||||||
defer delete(path_str)
|
|
||||||
cstr := _unix_getenv(path_str)
|
cstr := _unix_getenv(path_str)
|
||||||
if cstr == nil {
|
if cstr == nil {
|
||||||
return "", false
|
return "", false
|
||||||
@@ -807,6 +802,7 @@ set_current_directory :: proc(path: string) -> (err: Errno) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
exit :: proc "contextless" (code: int) -> ! {
|
exit :: proc "contextless" (code: int) -> ! {
|
||||||
|
runtime._cleanup_runtime_contextless()
|
||||||
_unix_exit(c.int(code))
|
_unix_exit(c.int(code))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -815,15 +811,13 @@ current_thread_id :: proc "contextless" () -> int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dlopen :: proc(filename: string, flags: int) -> rawptr {
|
dlopen :: proc(filename: string, flags: int) -> rawptr {
|
||||||
cstr := strings.clone_to_cstring(filename)
|
cstr := strings.clone_to_cstring(filename, context.temp_allocator)
|
||||||
defer delete(cstr)
|
|
||||||
handle := _unix_dlopen(cstr, c.int(flags))
|
handle := _unix_dlopen(cstr, c.int(flags))
|
||||||
return handle
|
return handle
|
||||||
}
|
}
|
||||||
dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
|
dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
|
||||||
assert(handle != nil)
|
assert(handle != nil)
|
||||||
cstr := strings.clone_to_cstring(symbol)
|
cstr := strings.clone_to_cstring(symbol, context.temp_allocator)
|
||||||
defer delete(cstr)
|
|
||||||
proc_handle := _unix_dlsym(handle, cstr)
|
proc_handle := _unix_dlsym(handle, cstr)
|
||||||
return proc_handle
|
return proc_handle
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,707 @@
|
|||||||
|
package os
|
||||||
|
|
||||||
|
foreign import libc "system:c"
|
||||||
|
|
||||||
|
import "core:strings"
|
||||||
|
import "core:c"
|
||||||
|
import "core:runtime"
|
||||||
|
|
||||||
|
Handle :: distinct i32
|
||||||
|
Pid :: distinct i32
|
||||||
|
File_Time :: distinct u64
|
||||||
|
Errno :: distinct i32
|
||||||
|
|
||||||
|
INVALID_HANDLE :: ~Handle(0)
|
||||||
|
|
||||||
|
ERROR_NONE: Errno: 0
|
||||||
|
|
||||||
|
EPERM: Errno: 1
|
||||||
|
ENOENT: Errno: 2
|
||||||
|
ESRCH: Errno: 3
|
||||||
|
EINTR: Errno: 4
|
||||||
|
EIO: Errno: 5
|
||||||
|
ENXIO: Errno: 6
|
||||||
|
E2BIG: Errno: 7
|
||||||
|
ENOEXEC: Errno: 8
|
||||||
|
EBADF: Errno: 9
|
||||||
|
ECHILD: Errno: 10
|
||||||
|
EDEADLK: Errno: 11
|
||||||
|
ENOMEM: Errno: 12
|
||||||
|
EACCES: Errno: 13
|
||||||
|
EFAULT: Errno: 14
|
||||||
|
ENOTBLK: Errno: 15
|
||||||
|
EBUSY: Errno: 16
|
||||||
|
EEXIST: Errno: 17
|
||||||
|
EXDEV: Errno: 18
|
||||||
|
ENODEV: Errno: 19
|
||||||
|
ENOTDIR: Errno: 20
|
||||||
|
EISDIR: Errno: 21
|
||||||
|
EINVAL: Errno: 22
|
||||||
|
ENFILE: Errno: 23
|
||||||
|
EMFILE: Errno: 24
|
||||||
|
ENOTTY: Errno: 25
|
||||||
|
ETXTBSY: Errno: 26
|
||||||
|
EFBIG: Errno: 27
|
||||||
|
ENOSPC: Errno: 28
|
||||||
|
ESPIPE: Errno: 29
|
||||||
|
EROFS: Errno: 30
|
||||||
|
EMLINK: Errno: 31
|
||||||
|
EPIPE: Errno: 32
|
||||||
|
EDOM: Errno: 33
|
||||||
|
ERANGE: Errno: 34
|
||||||
|
EAGAIN: Errno: 35
|
||||||
|
EWOULDBLOCK: Errno: EAGAIN
|
||||||
|
EINPROGRESS: Errno: 36
|
||||||
|
EALREADY: Errno: 37
|
||||||
|
ENOTSOCK: Errno: 38
|
||||||
|
EDESTADDRREQ: Errno: 39
|
||||||
|
EMSGSIZE: Errno: 40
|
||||||
|
EPROTOTYPE: Errno: 41
|
||||||
|
ENOPROTOOPT: Errno: 42
|
||||||
|
EPROTONOSUPPORT: Errno: 43
|
||||||
|
ESOCKTNOSUPPORT: Errno: 44
|
||||||
|
EOPNOTSUPP: Errno: 45
|
||||||
|
EPFNOSUPPORT: Errno: 46
|
||||||
|
EAFNOSUPPORT: Errno: 47
|
||||||
|
EADDRINUSE: Errno: 48
|
||||||
|
EADDRNOTAVAIL: Errno: 49
|
||||||
|
ENETDOWN: Errno: 50
|
||||||
|
ENETUNREACH: Errno: 51
|
||||||
|
ENETRESET: Errno: 52
|
||||||
|
ECONNABORTED: Errno: 53
|
||||||
|
ECONNRESET: Errno: 54
|
||||||
|
ENOBUFS: Errno: 55
|
||||||
|
EISCONN: Errno: 56
|
||||||
|
ENOTCONN: Errno: 57
|
||||||
|
ESHUTDOWN: Errno: 58
|
||||||
|
ETOOMANYREFS: Errno: 59
|
||||||
|
ETIMEDOUT: Errno: 60
|
||||||
|
ECONNREFUSED: Errno: 61
|
||||||
|
ELOOP: Errno: 62
|
||||||
|
ENAMETOOLONG: Errno: 63
|
||||||
|
EHOSTDOWN: Errno: 64
|
||||||
|
EHOSTUNREACH: Errno: 65
|
||||||
|
ENOTEMPTY: Errno: 66
|
||||||
|
EPROCLIM: Errno: 67
|
||||||
|
EUSERS: Errno: 68
|
||||||
|
EDQUOT: Errno: 69
|
||||||
|
ESTALE: Errno: 70
|
||||||
|
EREMOTE: Errno: 71
|
||||||
|
EBADRPC: Errno: 72
|
||||||
|
ERPCMISMATCH: Errno: 73
|
||||||
|
EPROGUNAVAIL: Errno: 74
|
||||||
|
EPROGMISMATCH: Errno: 75
|
||||||
|
EPROCUNAVAIL: Errno: 76
|
||||||
|
ENOLCK: Errno: 77
|
||||||
|
ENOSYS: Errno: 78
|
||||||
|
EFTYPE: Errno: 79
|
||||||
|
EAUTH: Errno: 80
|
||||||
|
ENEEDAUTH: Errno: 81
|
||||||
|
EIPSEC: Errno: 82
|
||||||
|
ENOATTR: Errno: 83
|
||||||
|
EILSEQ: Errno: 84
|
||||||
|
ENOMEDIUM: Errno: 85
|
||||||
|
EMEDIUMTYPE: Errno: 86
|
||||||
|
EOVERFLOW: Errno: 87
|
||||||
|
ECANCELED: Errno: 88
|
||||||
|
EIDRM: Errno: 89
|
||||||
|
ENOMSG: Errno: 90
|
||||||
|
ENOTSUP: Errno: 91
|
||||||
|
EBADMSG: Errno: 92
|
||||||
|
ENOTRECOVERABLE: Errno: 93
|
||||||
|
EOWNERDEAD: Errno: 94
|
||||||
|
EPROTO: Errno: 95
|
||||||
|
|
||||||
|
O_RDONLY :: 0x00000
|
||||||
|
O_WRONLY :: 0x00001
|
||||||
|
O_RDWR :: 0x00002
|
||||||
|
O_NONBLOCK :: 0x00004
|
||||||
|
O_APPEND :: 0x00008
|
||||||
|
O_ASYNC :: 0x00040
|
||||||
|
O_SYNC :: 0x00080
|
||||||
|
O_CREATE :: 0x00200
|
||||||
|
O_TRUNC :: 0x00400
|
||||||
|
O_EXCL :: 0x00800
|
||||||
|
O_NOCTTY :: 0x08000
|
||||||
|
O_CLOEXEC :: 0x10000
|
||||||
|
|
||||||
|
SEEK_SET :: 0
|
||||||
|
SEEK_CUR :: 1
|
||||||
|
SEEK_END :: 2
|
||||||
|
|
||||||
|
RTLD_LAZY :: 0x001
|
||||||
|
RTLD_NOW :: 0x002
|
||||||
|
RTLD_LOCAL :: 0x000
|
||||||
|
RTLD_GLOBAL :: 0x100
|
||||||
|
RTLD_TRACE :: 0x200
|
||||||
|
RTLD_NODELETE :: 0x400
|
||||||
|
|
||||||
|
MAX_PATH :: 1024
|
||||||
|
|
||||||
|
// "Argv" arguments converted to Odin strings
|
||||||
|
args := _alloc_command_line_arguments()
|
||||||
|
|
||||||
|
pid_t :: i32
|
||||||
|
time_t :: i64
|
||||||
|
mode_t :: u32
|
||||||
|
dev_t :: i32
|
||||||
|
ino_t :: u64
|
||||||
|
nlink_t :: u32
|
||||||
|
uid_t :: u32
|
||||||
|
gid_t :: u32
|
||||||
|
off_t :: i64
|
||||||
|
blkcnt_t :: u64
|
||||||
|
blksize_t :: i32
|
||||||
|
|
||||||
|
Unix_File_Time :: struct {
|
||||||
|
seconds: time_t,
|
||||||
|
nanoseconds: c.long,
|
||||||
|
}
|
||||||
|
|
||||||
|
OS_Stat :: struct {
|
||||||
|
mode: mode_t, // inode protection mode
|
||||||
|
device_id: dev_t, // inode's device
|
||||||
|
serial: ino_t, // inode's number
|
||||||
|
nlink: nlink_t, // number of hard links
|
||||||
|
uid: uid_t, // user ID of the file's owner
|
||||||
|
gid: gid_t, // group ID of the file's group
|
||||||
|
rdev: dev_t, // device type
|
||||||
|
|
||||||
|
last_access: Unix_File_Time, // time of last access
|
||||||
|
modified: Unix_File_Time, // time of last data modification
|
||||||
|
status_change: Unix_File_Time, // time of last file status change
|
||||||
|
|
||||||
|
size: off_t, // file size, in bytes
|
||||||
|
blocks: blkcnt_t, // blocks allocated for file
|
||||||
|
block_size: blksize_t, // optimal blocksize for I/O
|
||||||
|
|
||||||
|
flags: u32, // user defined flags for file
|
||||||
|
gen: u32, // file generation number
|
||||||
|
birthtime: Unix_File_Time, // time of file creation
|
||||||
|
}
|
||||||
|
|
||||||
|
MAXNAMLEN :: 255
|
||||||
|
|
||||||
|
// NOTE(laleksic, 2021-01-21): Comment and rename these to match OS_Stat above
|
||||||
|
Dirent :: struct {
|
||||||
|
ino: ino_t, // file number of entry
|
||||||
|
off: off_t, // offset after this entry
|
||||||
|
reclen: u16, // length of this record
|
||||||
|
type: u8, // file type
|
||||||
|
namlen: u8, // length of string in name
|
||||||
|
_padding: [4]u8,
|
||||||
|
name: [MAXNAMLEN + 1]byte, // name
|
||||||
|
}
|
||||||
|
|
||||||
|
Dir :: distinct rawptr // DIR*
|
||||||
|
|
||||||
|
// File type
|
||||||
|
S_IFMT :: 0o170000 // Type of file mask
|
||||||
|
S_IFIFO :: 0o010000 // Named pipe (fifo)
|
||||||
|
S_IFCHR :: 0o020000 // Character special
|
||||||
|
S_IFDIR :: 0o040000 // Directory
|
||||||
|
S_IFBLK :: 0o060000 // Block special
|
||||||
|
S_IFREG :: 0o100000 // Regular
|
||||||
|
S_IFLNK :: 0o120000 // Symbolic link
|
||||||
|
S_IFSOCK :: 0o140000 // Socket
|
||||||
|
S_ISVTX :: 0o001000 // Save swapped text even after use
|
||||||
|
|
||||||
|
// File mode
|
||||||
|
// Read, write, execute/search by owner
|
||||||
|
S_IRWXU :: 0o0700 // RWX mask for owner
|
||||||
|
S_IRUSR :: 0o0400 // R for owner
|
||||||
|
S_IWUSR :: 0o0200 // W for owner
|
||||||
|
S_IXUSR :: 0o0100 // X for owner
|
||||||
|
|
||||||
|
// Read, write, execute/search by group
|
||||||
|
S_IRWXG :: 0o0070 // RWX mask for group
|
||||||
|
S_IRGRP :: 0o0040 // R for group
|
||||||
|
S_IWGRP :: 0o0020 // W for group
|
||||||
|
S_IXGRP :: 0o0010 // X for group
|
||||||
|
|
||||||
|
// Read, write, execute/search by others
|
||||||
|
S_IRWXO :: 0o0007 // RWX mask for other
|
||||||
|
S_IROTH :: 0o0004 // R for other
|
||||||
|
S_IWOTH :: 0o0002 // W for other
|
||||||
|
S_IXOTH :: 0o0001 // X for other
|
||||||
|
|
||||||
|
S_ISUID :: 0o4000 // Set user id on execution
|
||||||
|
S_ISGID :: 0o2000 // Set group id on execution
|
||||||
|
S_ISTXT :: 0o1000 // Sticky bit
|
||||||
|
|
||||||
|
S_ISLNK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFLNK }
|
||||||
|
S_ISREG :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFREG }
|
||||||
|
S_ISDIR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFDIR }
|
||||||
|
S_ISCHR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFCHR }
|
||||||
|
S_ISBLK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK }
|
||||||
|
S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO }
|
||||||
|
S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK }
|
||||||
|
|
||||||
|
F_OK :: 0x00 // Test for file existance
|
||||||
|
X_OK :: 0x01 // Test for execute permission
|
||||||
|
W_OK :: 0x02 // Test for write permission
|
||||||
|
R_OK :: 0x04 // Test for read permission
|
||||||
|
|
||||||
|
AT_FDCWD :: -100
|
||||||
|
AT_EACCESS :: 0x01
|
||||||
|
AT_SYMLINK_NOFOLLOW :: 0x02
|
||||||
|
AT_SYMLINK_FOLLOW :: 0x04
|
||||||
|
AT_REMOVEDIR :: 0x08
|
||||||
|
|
||||||
|
@(default_calling_convention="c")
|
||||||
|
foreign libc {
|
||||||
|
@(link_name="__errno") __errno :: proc() -> ^int ---
|
||||||
|
|
||||||
|
@(link_name="fork") _unix_fork :: proc() -> pid_t ---
|
||||||
|
@(link_name="getthrid") _unix_getthrid :: proc() -> int ---
|
||||||
|
|
||||||
|
@(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---
|
||||||
|
@(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---
|
||||||
|
@(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
|
||||||
|
@(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
|
||||||
|
@(link_name="lseek") _unix_seek :: proc(fd: Handle, offset: off_t, whence: c.int) -> off_t ---
|
||||||
|
@(link_name="stat") _unix_stat :: proc(path: cstring, sb: ^OS_Stat) -> c.int ---
|
||||||
|
@(link_name="fstat") _unix_fstat :: proc(fd: Handle, sb: ^OS_Stat) -> c.int ---
|
||||||
|
@(link_name="lstat") _unix_lstat :: proc(path: cstring, sb: ^OS_Stat) -> c.int ---
|
||||||
|
@(link_name="readlink") _unix_readlink :: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t ---
|
||||||
|
@(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int ---
|
||||||
|
@(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring ---
|
||||||
|
@(link_name="chdir") _unix_chdir :: proc(path: cstring) -> c.int ---
|
||||||
|
@(link_name="rename") _unix_rename :: proc(old, new: cstring) -> c.int ---
|
||||||
|
@(link_name="unlink") _unix_unlink :: proc(path: cstring) -> c.int ---
|
||||||
|
@(link_name="rmdir") _unix_rmdir :: proc(path: cstring) -> c.int ---
|
||||||
|
@(link_name="mkdir") _unix_mkdir :: proc(path: cstring, mode: mode_t) -> c.int ---
|
||||||
|
|
||||||
|
@(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int ---
|
||||||
|
@(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir ---
|
||||||
|
@(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int ---
|
||||||
|
@(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) ---
|
||||||
|
@(link_name="readdir_r") _unix_readdir_r :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int ---
|
||||||
|
|
||||||
|
@(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr ---
|
||||||
|
@(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr ---
|
||||||
|
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---
|
||||||
|
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
|
||||||
|
|
||||||
|
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
|
||||||
|
@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
|
||||||
|
|
||||||
|
@(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---
|
||||||
|
|
||||||
|
@(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: c.int) -> rawptr ---
|
||||||
|
@(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr ---
|
||||||
|
@(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> c.int ---
|
||||||
|
@(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---
|
||||||
|
}
|
||||||
|
|
||||||
|
is_path_separator :: proc(r: rune) -> bool {
|
||||||
|
return r == '/'
|
||||||
|
}
|
||||||
|
|
||||||
|
get_last_error :: proc() -> int {
|
||||||
|
return __errno()^
|
||||||
|
}
|
||||||
|
|
||||||
|
fork :: proc() -> (Pid, Errno) {
|
||||||
|
pid := _unix_fork()
|
||||||
|
if pid == -1 {
|
||||||
|
return Pid(-1), Errno(get_last_error())
|
||||||
|
}
|
||||||
|
return Pid(pid), ERROR_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
|
||||||
|
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||||
|
handle := _unix_open(cstr, c.int(flags), c.int(mode))
|
||||||
|
if handle == -1 {
|
||||||
|
return INVALID_HANDLE, Errno(get_last_error())
|
||||||
|
}
|
||||||
|
return handle, ERROR_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
close :: proc(fd: Handle) -> Errno {
|
||||||
|
result := _unix_close(fd)
|
||||||
|
if result == -1 {
|
||||||
|
return Errno(get_last_error())
|
||||||
|
}
|
||||||
|
return ERROR_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||||
|
bytes_read := _unix_read(fd, &data[0], c.size_t(len(data)))
|
||||||
|
if bytes_read == -1 {
|
||||||
|
return -1, Errno(get_last_error())
|
||||||
|
}
|
||||||
|
return int(bytes_read), ERROR_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||||
|
if len(data) == 0 {
|
||||||
|
return 0, ERROR_NONE
|
||||||
|
}
|
||||||
|
bytes_written := _unix_write(fd, &data[0], c.size_t(len(data)))
|
||||||
|
if bytes_written == -1 {
|
||||||
|
return -1, Errno(get_last_error())
|
||||||
|
}
|
||||||
|
return int(bytes_written), ERROR_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
|
||||||
|
res := _unix_seek(fd, offset, c.int(whence))
|
||||||
|
if res == -1 {
|
||||||
|
return -1, Errno(get_last_error())
|
||||||
|
}
|
||||||
|
return res, ERROR_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
file_size :: proc(fd: Handle) -> (i64, Errno) {
|
||||||
|
s, err := _fstat(fd)
|
||||||
|
if err != ERROR_NONE {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
return s.size, ERROR_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
rename :: proc(old_path, new_path: string) -> Errno {
|
||||||
|
old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator)
|
||||||
|
new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator)
|
||||||
|
res := _unix_rename(old_path_cstr, new_path_cstr)
|
||||||
|
if res == -1 {
|
||||||
|
return Errno(get_last_error())
|
||||||
|
}
|
||||||
|
return ERROR_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
remove :: proc(path: string) -> Errno {
|
||||||
|
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||||
|
res := _unix_unlink(path_cstr)
|
||||||
|
if res == -1 {
|
||||||
|
return Errno(get_last_error())
|
||||||
|
}
|
||||||
|
return ERROR_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
make_directory :: proc(path: string, mode: mode_t = 0o775) -> Errno {
|
||||||
|
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||||
|
res := _unix_mkdir(path_cstr, mode)
|
||||||
|
if res == -1 {
|
||||||
|
return Errno(get_last_error())
|
||||||
|
}
|
||||||
|
return ERROR_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_directory :: proc(path: string) -> Errno {
|
||||||
|
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||||
|
res := _unix_rmdir(path_cstr)
|
||||||
|
if res == -1 {
|
||||||
|
return Errno(get_last_error())
|
||||||
|
}
|
||||||
|
return ERROR_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
is_file_handle :: proc(fd: Handle) -> bool {
|
||||||
|
s, err := _fstat(fd)
|
||||||
|
if err != ERROR_NONE {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return S_ISREG(s.mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
is_file_path :: proc(path: string, follow_links: bool = true) -> bool {
|
||||||
|
s: OS_Stat
|
||||||
|
err: Errno
|
||||||
|
if follow_links {
|
||||||
|
s, err = _stat(path)
|
||||||
|
} else {
|
||||||
|
s, err = _lstat(path)
|
||||||
|
}
|
||||||
|
if err != ERROR_NONE {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return S_ISREG(s.mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
is_dir_handle :: proc(fd: Handle) -> bool {
|
||||||
|
s, err := _fstat(fd)
|
||||||
|
if err != ERROR_NONE {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return S_ISDIR(s.mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
|
||||||
|
s: OS_Stat
|
||||||
|
err: Errno
|
||||||
|
if follow_links {
|
||||||
|
s, err = _stat(path)
|
||||||
|
} else {
|
||||||
|
s, err = _lstat(path)
|
||||||
|
}
|
||||||
|
if err != ERROR_NONE {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return S_ISDIR(s.mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
is_file :: proc {is_file_path, is_file_handle}
|
||||||
|
is_dir :: proc {is_dir_path, is_dir_handle}
|
||||||
|
|
||||||
|
// NOTE(bill): Uses startup to initialize it
|
||||||
|
|
||||||
|
stdin: Handle = 0
|
||||||
|
stdout: Handle = 1
|
||||||
|
stderr: Handle = 2
|
||||||
|
|
||||||
|
/* TODO(zangent): Implement these!
|
||||||
|
last_write_time :: proc(fd: Handle) -> File_Time {}
|
||||||
|
last_write_time_by_name :: proc(name: string) -> File_Time {}
|
||||||
|
*/
|
||||||
|
last_write_time :: proc(fd: Handle) -> (File_Time, Errno) {
|
||||||
|
s, err := _fstat(fd)
|
||||||
|
if err != ERROR_NONE {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
|
||||||
|
return File_Time(modified), ERROR_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
|
||||||
|
s, err := _stat(name)
|
||||||
|
if err != ERROR_NONE {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
|
||||||
|
return File_Time(modified), ERROR_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
_stat :: proc(path: string) -> (OS_Stat, Errno) {
|
||||||
|
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||||
|
|
||||||
|
// deliberately uninitialized
|
||||||
|
s: OS_Stat = ---
|
||||||
|
res := _unix_stat(cstr, &s)
|
||||||
|
if res == -1 {
|
||||||
|
return s, Errno(get_last_error())
|
||||||
|
}
|
||||||
|
return s, ERROR_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
_lstat :: proc(path: string) -> (OS_Stat, Errno) {
|
||||||
|
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||||
|
|
||||||
|
// deliberately uninitialized
|
||||||
|
s: OS_Stat = ---
|
||||||
|
res := _unix_lstat(cstr, &s)
|
||||||
|
if res == -1 {
|
||||||
|
return s, Errno(get_last_error())
|
||||||
|
}
|
||||||
|
return s, ERROR_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) {
|
||||||
|
// deliberately uninitialized
|
||||||
|
s: OS_Stat = ---
|
||||||
|
res := _unix_fstat(fd, &s)
|
||||||
|
if res == -1 {
|
||||||
|
return s, Errno(get_last_error())
|
||||||
|
}
|
||||||
|
return s, ERROR_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
_fdopendir :: proc(fd: Handle) -> (Dir, Errno) {
|
||||||
|
dirp := _unix_fdopendir(fd)
|
||||||
|
if dirp == cast(Dir)nil {
|
||||||
|
return nil, Errno(get_last_error())
|
||||||
|
}
|
||||||
|
return dirp, ERROR_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
_closedir :: proc(dirp: Dir) -> Errno {
|
||||||
|
rc := _unix_closedir(dirp)
|
||||||
|
if rc != 0 {
|
||||||
|
return Errno(get_last_error())
|
||||||
|
}
|
||||||
|
return ERROR_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
_rewinddir :: proc(dirp: Dir) {
|
||||||
|
_unix_rewinddir(dirp)
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) {
|
||||||
|
result: ^Dirent
|
||||||
|
rc := _unix_readdir_r(dirp, &entry, &result)
|
||||||
|
|
||||||
|
if rc != 0 {
|
||||||
|
err = Errno(get_last_error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = ERROR_NONE
|
||||||
|
|
||||||
|
if result == nil {
|
||||||
|
end_of_stream = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
@private
|
||||||
|
_readlink :: proc(path: string) -> (string, Errno) {
|
||||||
|
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||||
|
|
||||||
|
bufsz : uint = MAX_PATH
|
||||||
|
buf := make([]byte, MAX_PATH)
|
||||||
|
for {
|
||||||
|
rc := _unix_readlink(path_cstr, &(buf[0]), bufsz)
|
||||||
|
if rc == -1 {
|
||||||
|
delete(buf)
|
||||||
|
return "", Errno(get_last_error())
|
||||||
|
} else if rc == int(bufsz) {
|
||||||
|
bufsz += MAX_PATH
|
||||||
|
delete(buf)
|
||||||
|
buf = make([]byte, bufsz)
|
||||||
|
} else {
|
||||||
|
return strings.string_from_ptr(&buf[0], rc), ERROR_NONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX OpenBSD
|
||||||
|
absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) {
|
||||||
|
return "", Errno(ENOSYS)
|
||||||
|
}
|
||||||
|
|
||||||
|
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
|
||||||
|
rel := rel
|
||||||
|
if rel == "" {
|
||||||
|
rel = "."
|
||||||
|
}
|
||||||
|
|
||||||
|
rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator)
|
||||||
|
|
||||||
|
path_ptr := _unix_realpath(rel_cstr, nil)
|
||||||
|
if path_ptr == nil {
|
||||||
|
return "", Errno(get_last_error())
|
||||||
|
}
|
||||||
|
defer _unix_free(path_ptr)
|
||||||
|
|
||||||
|
path_cstr := transmute(cstring)path_ptr
|
||||||
|
path = strings.clone( string(path_cstr) )
|
||||||
|
|
||||||
|
return path, ERROR_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
access :: proc(path: string, mask: int) -> (bool, Errno) {
|
||||||
|
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||||
|
res := _unix_access(cstr, c.int(mask))
|
||||||
|
if res == -1 {
|
||||||
|
return false, Errno(get_last_error())
|
||||||
|
}
|
||||||
|
return true, ERROR_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
heap_alloc :: proc(size: int) -> rawptr {
|
||||||
|
assert(size >= 0)
|
||||||
|
return _unix_calloc(1, c.size_t(size))
|
||||||
|
}
|
||||||
|
|
||||||
|
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||||
|
// NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on
|
||||||
|
// POSIX platforms. Ensure your caller takes this into account.
|
||||||
|
return _unix_realloc(ptr, c.size_t(new_size))
|
||||||
|
}
|
||||||
|
|
||||||
|
heap_free :: proc(ptr: rawptr) {
|
||||||
|
_unix_free(ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
getenv :: proc(name: string) -> (string, bool) {
|
||||||
|
path_str := strings.clone_to_cstring(name, context.temp_allocator)
|
||||||
|
cstr := _unix_getenv(path_str)
|
||||||
|
if cstr == nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return string(cstr), true
|
||||||
|
}
|
||||||
|
|
||||||
|
get_current_directory :: proc() -> string {
|
||||||
|
buf := make([dynamic]u8, MAX_PATH)
|
||||||
|
for {
|
||||||
|
cwd := _unix_getcwd(cstring(raw_data(buf)), c.size_t(len(buf)))
|
||||||
|
if cwd != nil {
|
||||||
|
return string(cwd)
|
||||||
|
}
|
||||||
|
if Errno(get_last_error()) != ERANGE {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
resize(&buf, len(buf) + MAX_PATH)
|
||||||
|
}
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
|
||||||
|
set_current_directory :: proc(path: string) -> (err: Errno) {
|
||||||
|
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||||
|
res := _unix_chdir(cstr)
|
||||||
|
if res == -1 {
|
||||||
|
return Errno(get_last_error())
|
||||||
|
}
|
||||||
|
return ERROR_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
exit :: proc "contextless" (code: int) -> ! {
|
||||||
|
runtime._cleanup_runtime_contextless()
|
||||||
|
_unix_exit(c.int(code))
|
||||||
|
}
|
||||||
|
|
||||||
|
current_thread_id :: proc "contextless" () -> int {
|
||||||
|
return _unix_getthrid()
|
||||||
|
}
|
||||||
|
|
||||||
|
dlopen :: proc(filename: string, flags: int) -> rawptr {
|
||||||
|
cstr := strings.clone_to_cstring(filename, context.temp_allocator)
|
||||||
|
handle := _unix_dlopen(cstr, c.int(flags))
|
||||||
|
return handle
|
||||||
|
}
|
||||||
|
dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
|
||||||
|
assert(handle != nil)
|
||||||
|
cstr := strings.clone_to_cstring(symbol, context.temp_allocator)
|
||||||
|
proc_handle := _unix_dlsym(handle, cstr)
|
||||||
|
return proc_handle
|
||||||
|
}
|
||||||
|
dlclose :: proc(handle: rawptr) -> bool {
|
||||||
|
assert(handle != nil)
|
||||||
|
return _unix_dlclose(handle) == 0
|
||||||
|
}
|
||||||
|
dlerror :: proc() -> string {
|
||||||
|
return string(_unix_dlerror())
|
||||||
|
}
|
||||||
|
|
||||||
|
get_page_size :: proc() -> int {
|
||||||
|
// NOTE(tetra): The page size never changes, so why do anything complicated
|
||||||
|
// if we don't have to.
|
||||||
|
@static page_size := -1
|
||||||
|
if page_size != -1 {
|
||||||
|
return page_size
|
||||||
|
}
|
||||||
|
|
||||||
|
page_size = int(_unix_getpagesize())
|
||||||
|
return page_size
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_alloc_command_line_arguments :: proc() -> []string {
|
||||||
|
res := make([]string, len(runtime.args__))
|
||||||
|
for arg, i in runtime.args__ {
|
||||||
|
res[i] = string(arg)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package os
|
package os
|
||||||
|
|
||||||
import "core:sys/wasm/wasi"
|
import "core:sys/wasm/wasi"
|
||||||
|
import "core:runtime"
|
||||||
|
|
||||||
Handle :: distinct i32
|
Handle :: distinct i32
|
||||||
Errno :: distinct i32
|
Errno :: distinct i32
|
||||||
@@ -93,5 +94,6 @@ heap_free :: proc(ptr: rawptr) {
|
|||||||
|
|
||||||
|
|
||||||
exit :: proc "contextless" (code: int) -> ! {
|
exit :: proc "contextless" (code: int) -> ! {
|
||||||
|
runtime._cleanup_runtime_contextless()
|
||||||
wasi.proc_exit(wasi.exitcode_t(code))
|
wasi.proc_exit(wasi.exitcode_t(code))
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
package os
|
package os
|
||||||
|
|
||||||
import win32 "core:sys/windows"
|
import win32 "core:sys/windows"
|
||||||
|
import "core:runtime"
|
||||||
|
|
||||||
Handle :: distinct uintptr
|
Handle :: distinct uintptr
|
||||||
File_Time :: distinct u64
|
File_Time :: distinct u64
|
||||||
@@ -128,6 +129,7 @@ get_page_size :: proc() -> int {
|
|||||||
|
|
||||||
|
|
||||||
exit :: proc "contextless" (code: int) -> ! {
|
exit :: proc "contextless" (code: int) -> ! {
|
||||||
|
runtime._cleanup_runtime_contextless()
|
||||||
win32.ExitProcess(win32.DWORD(code))
|
win32.ExitProcess(win32.DWORD(code))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//+build linux, darwin, freebsd
|
//+build linux, darwin, freebsd, openbsd
|
||||||
package os
|
package os
|
||||||
|
|
||||||
import "core:time"
|
import "core:time"
|
||||||
|
|||||||
+10
-33
@@ -132,26 +132,11 @@ cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 {
|
|||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
cleanpath_from_handle :: proc(fd: Handle) -> (string, Errno) {
|
cleanpath_from_handle :: proc(fd: Handle) -> (string, Errno) {
|
||||||
if fd == 0 {
|
buf, err := cleanpath_from_handle_u16(fd)
|
||||||
return "", ERROR_INVALID_HANDLE
|
if err != 0 {
|
||||||
|
return "", err
|
||||||
}
|
}
|
||||||
h := win32.HANDLE(fd)
|
return win32.utf16_to_utf8(buf, context.allocator), err
|
||||||
|
|
||||||
MAX_PATH := win32.DWORD(260) + 1
|
|
||||||
buf: []u16
|
|
||||||
for {
|
|
||||||
buf = make([]u16, MAX_PATH, context.temp_allocator)
|
|
||||||
err := win32.GetFinalPathNameByHandleW(h, raw_data(buf), MAX_PATH, 0)
|
|
||||||
switch Errno(err) {
|
|
||||||
case ERROR_PATH_NOT_FOUND, ERROR_INVALID_PARAMETER:
|
|
||||||
return "", Errno(err)
|
|
||||||
case ERROR_NOT_ENOUGH_MEMORY:
|
|
||||||
MAX_PATH = MAX_PATH*2 + 1
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return cleanpath_from_buf(buf), ERROR_NONE
|
|
||||||
}
|
}
|
||||||
@(private)
|
@(private)
|
||||||
cleanpath_from_handle_u16 :: proc(fd: Handle) -> ([]u16, Errno) {
|
cleanpath_from_handle_u16 :: proc(fd: Handle) -> ([]u16, Errno) {
|
||||||
@@ -160,21 +145,13 @@ cleanpath_from_handle_u16 :: proc(fd: Handle) -> ([]u16, Errno) {
|
|||||||
}
|
}
|
||||||
h := win32.HANDLE(fd)
|
h := win32.HANDLE(fd)
|
||||||
|
|
||||||
MAX_PATH := win32.DWORD(260) + 1
|
n := win32.GetFinalPathNameByHandleW(h, nil, 0, 0)
|
||||||
buf: []u16
|
if n == 0 {
|
||||||
for {
|
return nil, Errno(win32.GetLastError())
|
||||||
buf = make([]u16, MAX_PATH, context.temp_allocator)
|
|
||||||
err := win32.GetFinalPathNameByHandleW(h, raw_data(buf), MAX_PATH, 0)
|
|
||||||
switch Errno(err) {
|
|
||||||
case ERROR_PATH_NOT_FOUND, ERROR_INVALID_PARAMETER:
|
|
||||||
return nil, Errno(err)
|
|
||||||
case ERROR_NOT_ENOUGH_MEMORY:
|
|
||||||
MAX_PATH = MAX_PATH*2 + 1
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
return cleanpath_strip_prefix(buf), ERROR_NONE
|
buf := make([]u16, max(n, win32.DWORD(260))+1, context.temp_allocator)
|
||||||
|
buf_len := win32.GetFinalPathNameByHandleW(h, raw_data(buf), n, 0)
|
||||||
|
return buf[:buf_len], ERROR_NONE
|
||||||
}
|
}
|
||||||
@(private)
|
@(private)
|
||||||
cleanpath_from_buf :: proc(buf: []u16) -> string {
|
cleanpath_from_buf :: proc(buf: []u16) -> string {
|
||||||
|
|||||||
@@ -284,13 +284,14 @@ rel :: proc(base_path, target_path: string, allocator := context.allocator) -> (
|
|||||||
}
|
}
|
||||||
|
|
||||||
dir :: proc(path: string, allocator := context.allocator) -> string {
|
dir :: proc(path: string, allocator := context.allocator) -> string {
|
||||||
|
context.allocator = allocator
|
||||||
vol := volume_name(path)
|
vol := volume_name(path)
|
||||||
i := len(path) - 1
|
i := len(path) - 1
|
||||||
for i >= len(vol) && !is_separator(path[i]) {
|
for i >= len(vol) && !is_separator(path[i]) {
|
||||||
i -= 1
|
i -= 1
|
||||||
}
|
}
|
||||||
dir := clean(path[len(vol) : i+1], allocator)
|
dir := clean(path[len(vol) : i+1])
|
||||||
defer delete(dir, allocator)
|
defer delete(dir)
|
||||||
if dir == "." && len(vol) > 2 {
|
if dir == "." && len(vol) > 2 {
|
||||||
return strings.clone(vol)
|
return strings.clone(vol)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//+build linux, darwin, freebsd
|
//+build linux, darwin, freebsd, openbsd
|
||||||
package filepath
|
package filepath
|
||||||
|
|
||||||
when ODIN_OS == .Darwin {
|
when ODIN_OS == .Darwin {
|
||||||
@@ -59,6 +59,11 @@ when ODIN_OS == .Darwin {
|
|||||||
foreign libc {
|
foreign libc {
|
||||||
@(link_name="__error") __error :: proc() -> ^i32 ---
|
@(link_name="__error") __error :: proc() -> ^i32 ---
|
||||||
}
|
}
|
||||||
|
} else when ODIN_OS == .OpenBSD {
|
||||||
|
@(private)
|
||||||
|
foreign libc {
|
||||||
|
@(link_name="__errno") __error :: proc() -> ^i32 ---
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
@(private)
|
@(private)
|
||||||
foreign libc {
|
foreign libc {
|
||||||
|
|||||||
@@ -365,6 +365,19 @@ index :: proc(val: any, i: int, loc := #caller_location) -> any {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deref :: proc(val: any) -> any {
|
||||||
|
if val != nil {
|
||||||
|
ti := type_info_base(type_info_of(val.id))
|
||||||
|
if info, ok := ti.variant.(Type_Info_Pointer); ok {
|
||||||
|
return any{
|
||||||
|
(^rawptr)(val.data)^,
|
||||||
|
info.elem.id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Struct_Tag represents the type of the string of a struct field
|
// Struct_Tag represents the type of the string of a struct field
|
||||||
@@ -680,7 +693,6 @@ union_variant_typeid :: proc(a: any) -> typeid {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
panic("expected a union to reflect.union_variant_typeid")
|
panic("expected a union to reflect.union_variant_typeid")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get_union_variant_raw_tag :: proc(a: any) -> i64 {
|
get_union_variant_raw_tag :: proc(a: any) -> i64 {
|
||||||
|
|||||||
@@ -334,11 +334,11 @@ is_relative_slice :: proc(info: ^Type_Info) -> bool {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
write_typeid_builder :: proc(buf: ^strings.Builder, id: typeid) {
|
write_typeid_builder :: proc(buf: ^strings.Builder, id: typeid, n_written: ^int = nil) -> (n: int, err: io.Error) {
|
||||||
write_type(buf, type_info_of(id))
|
return write_type_writer(strings.to_writer(buf), type_info_of(id))
|
||||||
}
|
}
|
||||||
write_typeid_writer :: proc(writer: io.Writer, id: typeid) {
|
write_typeid_writer :: proc(writer: io.Writer, id: typeid, n_written: ^int = nil) -> (n: int, err: io.Error) {
|
||||||
write_type(writer, type_info_of(id))
|
return write_type_writer(writer, type_info_of(id), n_written)
|
||||||
}
|
}
|
||||||
|
|
||||||
write_typeid :: proc{
|
write_typeid :: proc{
|
||||||
|
|||||||
@@ -351,7 +351,6 @@ Context :: struct {
|
|||||||
assertion_failure_proc: Assertion_Failure_Proc,
|
assertion_failure_proc: Assertion_Failure_Proc,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
|
|
||||||
user_data: any,
|
|
||||||
user_ptr: rawptr,
|
user_ptr: rawptr,
|
||||||
user_index: int,
|
user_index: int,
|
||||||
|
|
||||||
@@ -460,6 +459,11 @@ _cleanup_runtime :: proc() {
|
|||||||
default_temp_allocator_destroy(&global_default_temp_allocator_data)
|
default_temp_allocator_destroy(&global_default_temp_allocator_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_cleanup_runtime_contextless :: proc "contextless" () {
|
||||||
|
context = default_context()
|
||||||
|
_cleanup_runtime()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////
|
/////////////////////////////
|
||||||
/////////////////////////////
|
/////////////////////////////
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//+private
|
//+private
|
||||||
//+build linux, darwin, freebsd
|
//+build linux, darwin, freebsd, openbsd
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "core:intrinsics"
|
||||||
|
|||||||
+96
-5
@@ -304,7 +304,7 @@ filter :: proc(s: $S/[]$U, f: proc(U) -> bool, allocator := context.allocator) -
|
|||||||
return r[:]
|
return r[:]
|
||||||
}
|
}
|
||||||
|
|
||||||
scanner :: proc (s: $S/[]$U, initializer: $V, f: proc(V, U)->V, allocator := context.allocator) -> []V {
|
scanner :: proc (s: $S/[]$U, initializer: $V, f: proc(V, U) -> V, allocator := context.allocator) -> []V {
|
||||||
if len(s) == 0 { return {} }
|
if len(s) == 0 { return {} }
|
||||||
|
|
||||||
res := make([]V, len(s), allocator)
|
res := make([]V, len(s), allocator)
|
||||||
@@ -344,15 +344,106 @@ max :: proc(s: $S/[]$T) -> (res: T, ok: bool) where intrinsics.type_is_ordered(T
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
min_max :: proc(s: $S/[]$T) -> (min, max: T, ok: bool) where intrinsics.type_is_ordered(T) {
|
||||||
|
if len(s) != 0 {
|
||||||
|
min, max = s[0], s[0]
|
||||||
|
ok = true
|
||||||
|
for v in s[1:] {
|
||||||
|
min = builtin.min(min, v)
|
||||||
|
max = builtin.max(max, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
dot_product :: proc(a, b: $S/[]$T) -> T
|
any_of :: proc(s: $S/[]$T, value: T) -> bool where intrinsics.type_is_comparable(T) {
|
||||||
|
for v in s {
|
||||||
|
if v == value {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
none_of :: proc(s: $S/[]$T, value: T) -> bool where intrinsics.type_is_comparable(T) {
|
||||||
|
for v in s {
|
||||||
|
if v == value {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
all_of :: proc(s: $S/[]$T, value: T) -> bool where intrinsics.type_is_comparable(T) {
|
||||||
|
if len(s) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for v in s {
|
||||||
|
if v != value {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
any_of_proc :: proc(s: $S/[]$T, f: proc(T) -> bool) -> bool {
|
||||||
|
for v in s {
|
||||||
|
if f(v) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
none_of_proc :: proc(s: $S/[]$T, f: proc(T) -> bool) -> bool {
|
||||||
|
for v in s {
|
||||||
|
if f(v) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
all_of_proc :: proc(s: $S/[]$T, f: proc(T) -> bool) -> bool {
|
||||||
|
if len(s) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for v in s {
|
||||||
|
if !f(v) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
count :: proc(s: $S/[]$T, value: T) -> (n: int) where intrinsics.type_is_comparable(T) {
|
||||||
|
for v in s {
|
||||||
|
if v == value {
|
||||||
|
n += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
count_proc :: proc(s: $S/[]$T, f: proc(T) -> bool) -> (n: int) {
|
||||||
|
for v in s {
|
||||||
|
if f(v) {
|
||||||
|
n += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
dot_product :: proc(a, b: $S/[]$T) -> (r: T, ok: bool)
|
||||||
where intrinsics.type_is_numeric(T) {
|
where intrinsics.type_is_numeric(T) {
|
||||||
if len(a) != len(b) {
|
if len(a) != len(b) {
|
||||||
panic("slice.dot_product: slices of unequal length")
|
return
|
||||||
}
|
}
|
||||||
r: T
|
|
||||||
#no_bounds_check for _, i in a {
|
#no_bounds_check for _, i in a {
|
||||||
r += a[i] * b[i]
|
r += a[i] * b[i]
|
||||||
}
|
}
|
||||||
return r
|
return r, true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -895,6 +895,7 @@ unquote_string :: proc(lit: string, allocator := context.allocator) -> (res: str
|
|||||||
if s == `""` {
|
if s == `""` {
|
||||||
return "", false, true
|
return "", false, true
|
||||||
}
|
}
|
||||||
|
s = s[1:len(s)-1]
|
||||||
|
|
||||||
if contains_rune(s, '\n') >= 0 {
|
if contains_rune(s, '\n') >= 0 {
|
||||||
return s, false, false
|
return s, false, false
|
||||||
|
|||||||
@@ -280,10 +280,29 @@ _split :: proc(s_, sep: string, sep_save, n_: int, allocator := context.allocato
|
|||||||
return res[:i+1]
|
return res[:i+1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Splits a string into parts, based on a separator.
|
||||||
|
Returned strings are substrings of 's'.
|
||||||
|
```
|
||||||
|
s := "aaa.bbb.ccc.ddd.eee" // 5 parts
|
||||||
|
ss := split(s, ".")
|
||||||
|
fmt.println(ss) // [aaa, bbb, ccc, ddd, eee]
|
||||||
|
```
|
||||||
|
*/
|
||||||
split :: proc(s, sep: string, allocator := context.allocator) -> []string {
|
split :: proc(s, sep: string, allocator := context.allocator) -> []string {
|
||||||
return _split(s, sep, 0, -1, allocator)
|
return _split(s, sep, 0, -1, allocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Splits a string into a total of 'n' parts, based on a separator.
|
||||||
|
Returns fewer parts if there wasn't enough occurrences of the separator.
|
||||||
|
Returned strings are substrings of 's'.
|
||||||
|
```
|
||||||
|
s := "aaa.bbb.ccc.ddd.eee" // 5 parts present
|
||||||
|
ss := split_n(s, ".", 3) // total of 3 wanted
|
||||||
|
fmt.println(ss) // [aaa, bbb, ccc.ddd.eee]
|
||||||
|
```
|
||||||
|
*/
|
||||||
split_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> []string {
|
split_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> []string {
|
||||||
return _split(s, sep, 0, n, allocator)
|
return _split(s, sep, 0, n, allocator)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build linux, darwin, freebsd
|
// +build linux, darwin, freebsd, openbsd
|
||||||
package sync
|
package sync
|
||||||
|
|
||||||
import "core:time"
|
import "core:time"
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
//+private
|
||||||
|
//+build openbsd
|
||||||
|
package sync2
|
||||||
|
|
||||||
|
import "core:c"
|
||||||
|
import "core:os"
|
||||||
|
import "core:time"
|
||||||
|
|
||||||
|
FUTEX_WAIT :: 1
|
||||||
|
FUTEX_WAKE :: 2
|
||||||
|
|
||||||
|
FUTEX_PRIVATE_FLAG :: 128
|
||||||
|
|
||||||
|
FUTEX_WAIT_PRIVATE :: (FUTEX_WAIT | FUTEX_PRIVATE_FLAG)
|
||||||
|
FUTEX_WAKE_PRIVATE :: (FUTEX_WAKE | FUTEX_PRIVATE_FLAG)
|
||||||
|
|
||||||
|
foreign import libc "system:c"
|
||||||
|
|
||||||
|
foreign libc {
|
||||||
|
@(link_name="futex")
|
||||||
|
_unix_futex :: proc "c" (f: ^Futex, op: c.int, val: u32, timeout: rawptr) -> c.int ---
|
||||||
|
}
|
||||||
|
|
||||||
|
_futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
|
||||||
|
res := _unix_futex(f, FUTEX_WAIT_PRIVATE, expected, nil)
|
||||||
|
|
||||||
|
if res != -1 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if os.Errno(os.get_last_error()) == os.ETIMEDOUT {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("futex_wait failure")
|
||||||
|
}
|
||||||
|
|
||||||
|
_futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
|
||||||
|
if duration <= 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
timespec_t :: struct {
|
||||||
|
tv_sec: c.long,
|
||||||
|
tv_nsec: c.long,
|
||||||
|
}
|
||||||
|
|
||||||
|
res := _unix_futex(f, FUTEX_WAIT_PRIVATE, expected, ×pec_t{
|
||||||
|
tv_sec = (c.long)(duration/1e9),
|
||||||
|
tv_nsec = (c.long)(duration%1e9),
|
||||||
|
})
|
||||||
|
|
||||||
|
if res != -1 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if os.Errno(os.get_last_error()) == os.ETIMEDOUT {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("futex_wait_with_timeout failure")
|
||||||
|
}
|
||||||
|
|
||||||
|
_futex_signal :: proc(f: ^Futex) {
|
||||||
|
res := _unix_futex(f, FUTEX_WAKE_PRIVATE, 1, nil)
|
||||||
|
|
||||||
|
if res == -1 {
|
||||||
|
panic("futex_wake_single failure")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_futex_broadcast :: proc(f: ^Futex) {
|
||||||
|
res := _unix_futex(f, FUTEX_WAKE_PRIVATE, u32(max(i32)), nil)
|
||||||
|
|
||||||
|
if res == -1 {
|
||||||
|
panic("_futex_wake_all failure")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
//+build openbsd
|
||||||
|
//+private
|
||||||
|
package sync2
|
||||||
|
|
||||||
|
import "core:os"
|
||||||
|
|
||||||
|
_current_thread_id :: proc "contextless" () -> int {
|
||||||
|
return os.current_thread_id()
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
//+build linux, freebsd
|
//+build linux, freebsd, openbsd
|
||||||
//+private
|
//+private
|
||||||
package sync2
|
package sync2
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package sync
|
||||||
|
|
||||||
|
import "core:sys/unix"
|
||||||
|
import "core:os"
|
||||||
|
|
||||||
|
current_thread_id :: proc "contextless" () -> int {
|
||||||
|
return os.current_thread_id()
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Darwin docs say it best:
|
||||||
|
// A semaphore is much like a lock, except that a finite number of threads can hold it simultaneously.
|
||||||
|
// Semaphores can be thought of as being much like piles of tokens; multiple threads can take these tokens,
|
||||||
|
// but when there are none left, a thread must wait until another thread returns one.
|
||||||
|
Semaphore :: struct #align 16 {
|
||||||
|
handle: unix.sem_t,
|
||||||
|
}
|
||||||
|
|
||||||
|
semaphore_init :: proc(s: ^Semaphore, initial_count := 0) {
|
||||||
|
assert(unix.sem_init(&s.handle, 0, u32(initial_count)) == 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
semaphore_destroy :: proc(s: ^Semaphore) {
|
||||||
|
assert(unix.sem_destroy(&s.handle) == 0)
|
||||||
|
s.handle = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
semaphore_post :: proc(s: ^Semaphore, count := 1) {
|
||||||
|
// NOTE: SPEED: If there's one syscall to do this, we should use it instead of the loop.
|
||||||
|
for in 0..<count {
|
||||||
|
assert(unix.sem_post(&s.handle) == 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
semaphore_wait_for :: proc(s: ^Semaphore) {
|
||||||
|
assert(unix.sem_wait(&s.handle) == 0)
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build linux, darwin, freebsd
|
// +build linux, darwin, freebsd, openbsd
|
||||||
package sync
|
package sync
|
||||||
|
|
||||||
import "core:sys/unix"
|
import "core:sys/unix"
|
||||||
|
|||||||
@@ -91,8 +91,7 @@ sys_open :: proc(path: string, oflag: Open_Flags, mode: Permission) -> (c.int, b
|
|||||||
|
|
||||||
cmode: u32 = 0
|
cmode: u32 = 0
|
||||||
cflags: u32 = 0
|
cflags: u32 = 0
|
||||||
cpath: cstring = strings.clone_to_cstring(path)
|
cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator)
|
||||||
defer delete(cpath)
|
|
||||||
|
|
||||||
cflags = _sys_permission_mode(mode)
|
cflags = _sys_permission_mode(mode)
|
||||||
|
|
||||||
@@ -124,39 +123,32 @@ sys_open :: proc(path: string, oflag: Open_Flags, mode: Permission) -> (c.int, b
|
|||||||
}
|
}
|
||||||
|
|
||||||
sys_mkdir :: proc(path: string, mode: Permission) -> bool {
|
sys_mkdir :: proc(path: string, mode: Permission) -> bool {
|
||||||
cpath: cstring = strings.clone_to_cstring(path)
|
cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator)
|
||||||
defer delete(cpath)
|
|
||||||
cflags := _sys_permission_mode(mode)
|
cflags := _sys_permission_mode(mode)
|
||||||
return syscall_mkdir(cpath, cflags) != -1
|
return syscall_mkdir(cpath, cflags) != -1
|
||||||
}
|
}
|
||||||
|
|
||||||
sys_mkdir_at :: proc(fd: c.int, path: string, mode: Permission) -> bool {
|
sys_mkdir_at :: proc(fd: c.int, path: string, mode: Permission) -> bool {
|
||||||
cpath: cstring = strings.clone_to_cstring(path)
|
cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator)
|
||||||
defer delete(cpath)
|
|
||||||
cflags := _sys_permission_mode(mode)
|
cflags := _sys_permission_mode(mode)
|
||||||
return syscall_mkdir_at(fd, cpath, cflags) != -1
|
return syscall_mkdir_at(fd, cpath, cflags) != -1
|
||||||
}
|
}
|
||||||
|
|
||||||
sys_rmdir :: proc(path: string, mode: Permission) -> bool {
|
sys_rmdir :: proc(path: string, mode: Permission) -> bool {
|
||||||
cpath: cstring = strings.clone_to_cstring(path)
|
cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator)
|
||||||
defer delete(cpath)
|
|
||||||
cflags := _sys_permission_mode(mode)
|
cflags := _sys_permission_mode(mode)
|
||||||
return syscall_rmdir(cpath, cflags) != -1
|
return syscall_rmdir(cpath, cflags) != -1
|
||||||
}
|
}
|
||||||
|
|
||||||
sys_rename :: proc(path: string, new_path: string) -> bool {
|
sys_rename :: proc(path: string, new_path: string) -> bool {
|
||||||
cpath: cstring = strings.clone_to_cstring(path)
|
cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator)
|
||||||
defer delete(cpath)
|
cnpath: cstring = strings.clone_to_cstring(new_path, context.temp_allocator)
|
||||||
cnpath: cstring = strings.clone_to_cstring(new_path)
|
|
||||||
defer delete(cnpath)
|
|
||||||
return syscall_rename(cpath, cnpath) != -1
|
return syscall_rename(cpath, cnpath) != -1
|
||||||
}
|
}
|
||||||
|
|
||||||
sys_rename_at :: proc(fd: c.int, path: string, to_fd: c.int, new_path: string) -> bool {
|
sys_rename_at :: proc(fd: c.int, path: string, to_fd: c.int, new_path: string) -> bool {
|
||||||
cpath: cstring = strings.clone_to_cstring(path)
|
cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator)
|
||||||
defer delete(cpath)
|
cnpath: cstring = strings.clone_to_cstring(new_path, context.temp_allocator)
|
||||||
cnpath: cstring = strings.clone_to_cstring(new_path)
|
|
||||||
defer delete(cnpath)
|
|
||||||
return syscall_rename_at(fd, cpath, to_fd, cnpath) != -1
|
return syscall_rename_at(fd, cpath, to_fd, cnpath) != -1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,14 +157,12 @@ sys_lseek :: proc(fd: c.int, offset: i64, whence: Offset_From) -> i64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sys_chmod :: proc(path: string, mode: Permission) -> bool {
|
sys_chmod :: proc(path: string, mode: Permission) -> bool {
|
||||||
cpath: cstring = strings.clone_to_cstring(path)
|
cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator)
|
||||||
defer delete(cpath)
|
|
||||||
cmode := _sys_permission_mode(mode)
|
cmode := _sys_permission_mode(mode)
|
||||||
return syscall_chmod(cpath, cmode) != -1
|
return syscall_chmod(cpath, cmode) != -1
|
||||||
}
|
}
|
||||||
|
|
||||||
sys_lstat :: proc(path: string, status: ^stat) -> bool {
|
sys_lstat :: proc(path: string, status: ^stat) -> bool {
|
||||||
cpath: cstring = strings.clone_to_cstring(path)
|
cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator)
|
||||||
defer delete(cpath)
|
|
||||||
return syscall_lstat(cpath, status) != -1
|
return syscall_lstat(cpath, status) != -1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
//+build openbsd
|
||||||
|
package unix
|
||||||
|
|
||||||
|
import "core:c"
|
||||||
|
|
||||||
|
pthread_t :: distinct rawptr
|
||||||
|
pthread_attr_t :: distinct rawptr
|
||||||
|
pthread_mutex_t :: distinct rawptr
|
||||||
|
pthread_mutexattr_t :: distinct rawptr
|
||||||
|
pthread_cond_t :: distinct rawptr
|
||||||
|
pthread_condattr_t :: distinct rawptr
|
||||||
|
pthread_rwlock_t :: distinct rawptr
|
||||||
|
pthread_rwlockattr_t :: distinct rawptr
|
||||||
|
pthread_barrier_t :: distinct rawptr
|
||||||
|
pthread_barrierattr_t :: distinct rawptr
|
||||||
|
pthread_spinlock_t :: distinct rawptr
|
||||||
|
|
||||||
|
pthread_key_t :: distinct c.int
|
||||||
|
pthread_once_t :: struct {
|
||||||
|
state: c.int,
|
||||||
|
mutex: pthread_mutex_t,
|
||||||
|
}
|
||||||
|
|
||||||
|
PTHREAD_MUTEX_ERRORCHECK :: 1
|
||||||
|
PTHREAD_MUTEX_RECURSIVE :: 2
|
||||||
|
PTHREAD_MUTEX_NORMAL :: 3
|
||||||
|
PTHREAD_MUTEX_STRICT_NP :: 4
|
||||||
|
|
||||||
|
PTHREAD_DETACHED :: 0x1
|
||||||
|
PTHREAD_SCOPE_SYSTEM :: 0x2
|
||||||
|
PTHREAD_INHERIT_SCHED :: 0x4
|
||||||
|
PTHREAD_NOFLOAT :: 0x8
|
||||||
|
|
||||||
|
PTHREAD_CREATE_DETACHED :: PTHREAD_DETACHED
|
||||||
|
PTHREAD_CREATE_JOINABLE :: 0
|
||||||
|
PTHREAD_SCOPE_PROCESS :: 0
|
||||||
|
PTHREAD_EXPLICIT_SCHED :: 0
|
||||||
|
|
||||||
|
SCHED_FIFO :: 1
|
||||||
|
SCHED_OTHER :: 2
|
||||||
|
SCHED_RR :: 3
|
||||||
|
|
||||||
|
sched_param :: struct {
|
||||||
|
sched_priority: c.int,
|
||||||
|
}
|
||||||
|
|
||||||
|
sem_t :: distinct rawptr
|
||||||
|
|
||||||
|
foreign import libc "system:c"
|
||||||
|
|
||||||
|
@(default_calling_convention="c")
|
||||||
|
foreign libc {
|
||||||
|
sem_open :: proc(name: cstring, flags: c.int) -> ^sem_t ---
|
||||||
|
|
||||||
|
sem_init :: proc(sem: ^sem_t, pshared: c.int, initial_value: c.uint) -> c.int ---
|
||||||
|
sem_destroy :: proc(sem: ^sem_t) -> c.int ---
|
||||||
|
sem_post :: proc(sem: ^sem_t) -> c.int ---
|
||||||
|
sem_wait :: proc(sem: ^sem_t) -> c.int ---
|
||||||
|
sem_trywait :: proc(sem: ^sem_t) -> c.int ---
|
||||||
|
//sem_timedwait :: proc(sem: ^sem_t, timeout: time.TimeSpec) -> c.int ---
|
||||||
|
|
||||||
|
// NOTE: unclear whether pthread_yield is well-supported on Linux systems,
|
||||||
|
// see https://linux.die.net/man/3/pthread_yield
|
||||||
|
pthread_yield :: proc() ---
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
//+build linux, darwin, freebsd
|
//+build linux, darwin, freebsd, openbsd
|
||||||
package unix
|
package unix
|
||||||
|
|
||||||
foreign import "system:pthread"
|
foreign import "system:pthread"
|
||||||
|
|||||||
@@ -101,6 +101,9 @@ foreign user32 {
|
|||||||
}
|
}
|
||||||
@(link_name="GetCursorPos") get_cursor_pos :: proc(p: ^Point) -> Bool ---
|
@(link_name="GetCursorPos") get_cursor_pos :: proc(p: ^Point) -> Bool ---
|
||||||
@(link_name="SetCursorPos") set_cursor_pos :: proc(x, y: i32) -> Bool ---
|
@(link_name="SetCursorPos") set_cursor_pos :: proc(x, y: i32) -> Bool ---
|
||||||
|
@(link_name="GetCapure") get_capture :: proc(hwnd: Hwnd) -> Hwnd ---
|
||||||
|
@(link_name="SetCapture") set_capture :: proc(hwnd: Hwnd) -> Hwnd ---
|
||||||
|
@(link_name="ReleaseCapture") release_capture :: proc() -> Bool ---
|
||||||
@(link_name="ScreenToClient") screen_to_client :: proc(h: Hwnd, p: ^Point) -> Bool ---
|
@(link_name="ScreenToClient") screen_to_client :: proc(h: Hwnd, p: ^Point) -> Bool ---
|
||||||
@(link_name="ClientToScreen") client_to_screen :: proc(h: Hwnd, p: ^Point) -> Bool ---
|
@(link_name="ClientToScreen") client_to_screen :: proc(h: Hwnd, p: ^Point) -> Bool ---
|
||||||
@(link_name="PostQuitMessage") post_quit_message :: proc(exit_code: i32) ---
|
@(link_name="PostQuitMessage") post_quit_message :: proc(exit_code: i32) ---
|
||||||
|
|||||||
@@ -376,6 +376,9 @@ FILE_TYPE_DISK :: 0x0001
|
|||||||
FILE_TYPE_CHAR :: 0x0002
|
FILE_TYPE_CHAR :: 0x0002
|
||||||
FILE_TYPE_PIPE :: 0x0003
|
FILE_TYPE_PIPE :: 0x0003
|
||||||
|
|
||||||
|
RECT :: struct {left, top, right, bottom: LONG}
|
||||||
|
POINT :: struct {x, y: LONG}
|
||||||
|
|
||||||
|
|
||||||
when size_of(uintptr) == 4 {
|
when size_of(uintptr) == 4 {
|
||||||
WSADATA :: struct {
|
WSADATA :: struct {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build linux, darwin, freebsd
|
// +build linux, darwin, freebsd, openbsd
|
||||||
// +private
|
// +private
|
||||||
package thread
|
package thread
|
||||||
|
|
||||||
|
|||||||
+23
-11
@@ -1,4 +1,4 @@
|
|||||||
//+build linux, darwin, freebsd
|
//+build linux, darwin, freebsd, openbsd
|
||||||
package time
|
package time
|
||||||
|
|
||||||
IS_SUPPORTED :: true // NOTE: Times on Darwin are UTC.
|
IS_SUPPORTED :: true // NOTE: Times on Darwin are UTC.
|
||||||
@@ -22,16 +22,28 @@ TimeSpec :: struct {
|
|||||||
tv_nsec : i64, /* nanoseconds */
|
tv_nsec : i64, /* nanoseconds */
|
||||||
}
|
}
|
||||||
|
|
||||||
CLOCK_REALTIME :: 0 // NOTE(tetra): May jump in time, when user changes the system time.
|
when ODIN_OS == .OpenBSD {
|
||||||
CLOCK_MONOTONIC :: 1 // NOTE(tetra): May stand still while system is asleep.
|
CLOCK_REALTIME :: 0
|
||||||
CLOCK_PROCESS_CPUTIME_ID :: 2
|
CLOCK_PROCESS_CPUTIME_ID :: 2
|
||||||
CLOCK_THREAD_CPUTIME_ID :: 3
|
CLOCK_MONOTONIC :: 3
|
||||||
CLOCK_MONOTONIC_RAW :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP.
|
CLOCK_THREAD_CPUTIME_ID :: 4
|
||||||
CLOCK_REALTIME_COARSE :: 5 // NOTE(tetra): "COARSE" clocks are apparently much faster, but not "fine-grained."
|
CLOCK_UPTIME :: 5
|
||||||
CLOCK_MONOTONIC_COARSE :: 6
|
CLOCK_BOOTTIME :: 6
|
||||||
CLOCK_BOOTTIME :: 7 // NOTE(tetra): Same as MONOTONIC, except also including time system was asleep.
|
|
||||||
CLOCK_REALTIME_ALARM :: 8
|
// CLOCK_MONOTONIC_RAW doesn't exist, use CLOCK_MONOTONIC
|
||||||
CLOCK_BOOTTIME_ALARM :: 9
|
CLOCK_MONOTONIC_RAW :: CLOCK_MONOTONIC
|
||||||
|
} else {
|
||||||
|
CLOCK_REALTIME :: 0 // NOTE(tetra): May jump in time, when user changes the system time.
|
||||||
|
CLOCK_MONOTONIC :: 1 // NOTE(tetra): May stand still while system is asleep.
|
||||||
|
CLOCK_PROCESS_CPUTIME_ID :: 2
|
||||||
|
CLOCK_THREAD_CPUTIME_ID :: 3
|
||||||
|
CLOCK_MONOTONIC_RAW :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP.
|
||||||
|
CLOCK_REALTIME_COARSE :: 5 // NOTE(tetra): "COARSE" clocks are apparently much faster, but not "fine-grained."
|
||||||
|
CLOCK_MONOTONIC_COARSE :: 6
|
||||||
|
CLOCK_BOOTTIME :: 7 // NOTE(tetra): Same as MONOTONIC, except also including time system was asleep.
|
||||||
|
CLOCK_REALTIME_ALARM :: 8
|
||||||
|
CLOCK_BOOTTIME_ALARM :: 9
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(tetra, 2019-11-05): The original implementation of this package for Darwin used this constants.
|
// TODO(tetra, 2019-11-05): The original implementation of this package for Darwin used this constants.
|
||||||
// I do not know if Darwin programmers are used to the existance of these constants or not, so
|
// I do not know if Darwin programmers are used to the existance of these constants or not, so
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
//+build windows
|
|
||||||
package all
|
package all
|
||||||
|
|
||||||
|
|
||||||
import botan "vendor:botan"
|
import botan "vendor:botan"
|
||||||
import ENet "vendor:ENet"
|
import ENet "vendor:ENet"
|
||||||
import gl "vendor:OpenGL"
|
import gl "vendor:OpenGL"
|
||||||
@@ -17,14 +15,11 @@ import IMG "vendor:sdl2/image"
|
|||||||
import MIX "vendor:sdl2/mixer"
|
import MIX "vendor:sdl2/mixer"
|
||||||
import TTF "vendor:sdl2/ttf"
|
import TTF "vendor:sdl2/ttf"
|
||||||
|
|
||||||
import stb_easy_font "vendor:stb/easy_font"
|
|
||||||
import stbi "vendor:stb/image"
|
|
||||||
import stbrp "vendor:stb/rect_pack"
|
|
||||||
import stbtt "vendor:stb/truetype"
|
|
||||||
import stb_vorbis "vendor:stb/vorbis"
|
|
||||||
|
|
||||||
import vk "vendor:vulkan"
|
import vk "vendor:vulkan"
|
||||||
|
|
||||||
|
import NS "vendor:darwin/Foundation"
|
||||||
|
import MTL "vendor:darwin/Metal"
|
||||||
|
import CA "vendor:darwin/QuartzCore"
|
||||||
|
|
||||||
_ :: botan
|
_ :: botan
|
||||||
_ :: ENet
|
_ :: ENet
|
||||||
@@ -39,9 +34,7 @@ _ :: SDLNet
|
|||||||
_ :: IMG
|
_ :: IMG
|
||||||
_ :: MIX
|
_ :: MIX
|
||||||
_ :: TTF
|
_ :: TTF
|
||||||
_ :: stb_easy_font
|
|
||||||
_ :: stbi
|
|
||||||
_ :: stbrp
|
|
||||||
_ :: stbtt
|
|
||||||
_ :: stb_vorbis
|
|
||||||
_ :: vk
|
_ :: vk
|
||||||
|
_ :: NS
|
||||||
|
_ :: MTL
|
||||||
|
_ :: CA
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
//+build windows
|
||||||
|
package all
|
||||||
|
|
||||||
|
import D3D11 "vendor:directx/d3d11"
|
||||||
|
import D3D12 "vendor:directx/d3d12"
|
||||||
|
import DXGI "vendor:directx/dxgi"
|
||||||
|
|
||||||
|
_ :: D3D11
|
||||||
|
_ :: D3D12
|
||||||
|
_ :: DXGI
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
//+build windows, linux
|
||||||
|
package all
|
||||||
|
|
||||||
|
import stb_easy_font "vendor:stb/easy_font"
|
||||||
|
import stbi "vendor:stb/image"
|
||||||
|
import stbrp "vendor:stb/rect_pack"
|
||||||
|
import stbtt "vendor:stb/truetype"
|
||||||
|
import stb_vorbis "vendor:stb/vorbis"
|
||||||
|
|
||||||
|
_ :: stb_easy_font
|
||||||
|
_ :: stbi
|
||||||
|
_ :: stbrp
|
||||||
|
_ :: stbtt
|
||||||
|
_ :: stb_vorbis
|
||||||
|
|
||||||
+13
-7
@@ -11,22 +11,28 @@ import "core:intrinsics"
|
|||||||
import "core:math/big"
|
import "core:math/big"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The Odin programming language is fast, concise, readable, pragmatic and open sourced.
|
Odin is a general-purpose programming language with distinct typing built
|
||||||
It is designed with the intent of replacing C with the following goals:
|
for high performance, modern systems and data-oriented programming.
|
||||||
* simplicity
|
|
||||||
* high performance
|
Odin is the C alternative for the Joy of Programming.
|
||||||
* built for modern systems
|
|
||||||
* joy of programming
|
|
||||||
|
|
||||||
# Installing Odin
|
# Installing Odin
|
||||||
Getting Started - https://odin-lang.org/docs/install/
|
Getting Started - https://odin-lang.org/docs/install/
|
||||||
Instructions for downloading and install the Odin compiler and libraries.
|
Instructions for downloading and install the Odin compiler and libraries.
|
||||||
|
|
||||||
# Learning Odin
|
# Learning Odin
|
||||||
|
Getting Started - https://odin-lang.org/docs/install/
|
||||||
|
Getting Started with Odin. Downloading, installing, and getting your
|
||||||
|
first program to compile and run.
|
||||||
Overview of Odin - https://odin-lang.org/docs/overview/
|
Overview of Odin - https://odin-lang.org/docs/overview/
|
||||||
An overview of the Odin programming language.
|
An overview of the Odin programming language and its features.
|
||||||
Frequently Asked Questions (FAQ) - https://odin-lang.org/docs/faq/
|
Frequently Asked Questions (FAQ) - https://odin-lang.org/docs/faq/
|
||||||
Answers to common questions about Odin.
|
Answers to common questions about Odin.
|
||||||
|
Packages - https://pkg.odin-lang.org/
|
||||||
|
Documentation for all the official packages part of the
|
||||||
|
core and vendor library collections.
|
||||||
|
Nightly Builds - https://odin-lang.org/docs/nightly/
|
||||||
|
Get the latest nightly builds of Odin.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
the_basics :: proc() {
|
the_basics :: proc() {
|
||||||
|
|||||||
+3
-1
@@ -89,7 +89,9 @@ template <typename T>
|
|||||||
void slice_init(Slice<T> *s, gbAllocator const &allocator, isize count) {
|
void slice_init(Slice<T> *s, gbAllocator const &allocator, isize count) {
|
||||||
GB_ASSERT(count >= 0);
|
GB_ASSERT(count >= 0);
|
||||||
s->data = gb_alloc_array(allocator, T, count);
|
s->data = gb_alloc_array(allocator, T, count);
|
||||||
GB_ASSERT(s->data != nullptr);
|
if (count > 0) {
|
||||||
|
GB_ASSERT(s->data != nullptr);
|
||||||
|
}
|
||||||
s->count = count;
|
s->count = count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+23
-2
@@ -17,6 +17,11 @@
|
|||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(GB_SYSTEM_OPENBSD)
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
#include <sys/utsname.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
NOTE(Jeroen): This prints the Windows product edition only, to be called from `print_platform_details`.
|
NOTE(Jeroen): This prints the Windows product edition only, to be called from `print_platform_details`.
|
||||||
*/
|
*/
|
||||||
@@ -242,6 +247,14 @@ void report_ram_info() {
|
|||||||
if (sysctl(sysctls, 2, &ram_amount, &val_size, NULL, 0) != -1) {
|
if (sysctl(sysctls, 2, &ram_amount, &val_size, NULL, 0) != -1) {
|
||||||
gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1));
|
gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1));
|
||||||
}
|
}
|
||||||
|
#elif defined(GB_SYSTEM_OPENBSD)
|
||||||
|
uint64_t ram_amount;
|
||||||
|
size_t val_size = sizeof(ram_amount);
|
||||||
|
|
||||||
|
int sysctls[] = { CTL_HW, HW_PHYSMEM64 };
|
||||||
|
if (sysctl(sysctls, 2, &ram_amount, &val_size, NULL, 0) != -1) {
|
||||||
|
gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1));
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
gb_printf("Unknown.\n");
|
gb_printf("Unknown.\n");
|
||||||
#endif
|
#endif
|
||||||
@@ -473,11 +486,11 @@ void print_bug_report_help() {
|
|||||||
|
|
||||||
#elif defined(GB_SYSTEM_LINUX)
|
#elif defined(GB_SYSTEM_LINUX)
|
||||||
/*
|
/*
|
||||||
Try to parse `/usr/lib/os-release` for `PRETTY_NAME="Ubuntu 20.04.3 LTS`
|
Try to parse `/etc/os-release` for `PRETTY_NAME="Ubuntu 20.04.3 LTS`
|
||||||
*/
|
*/
|
||||||
gbAllocator a = heap_allocator();
|
gbAllocator a = heap_allocator();
|
||||||
|
|
||||||
gbFileContents release = gb_file_read_contents(a, 1, "/usr/lib/os-release");
|
gbFileContents release = gb_file_read_contents(a, 1, "/etc/os-release");
|
||||||
defer (gb_file_free_contents(&release));
|
defer (gb_file_free_contents(&release));
|
||||||
|
|
||||||
b32 found = 0;
|
b32 found = 0;
|
||||||
@@ -643,6 +656,14 @@ void print_bug_report_help() {
|
|||||||
} else {
|
} else {
|
||||||
gb_printf("macOS: Unknown\n");
|
gb_printf("macOS: Unknown\n");
|
||||||
}
|
}
|
||||||
|
#elif defined(GB_SYSTEM_OPENBSD)
|
||||||
|
struct utsname un;
|
||||||
|
|
||||||
|
if (uname(&un) != -1) {
|
||||||
|
gb_printf("%s %s %s %s\n", un.sysname, un.release, un.version, un.machine);
|
||||||
|
} else {
|
||||||
|
gb_printf("OpenBSD: Unknown\n");
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
gb_printf("Unknown\n");
|
gb_printf("Unknown\n");
|
||||||
|
|
||||||
|
|||||||
+57
-12
@@ -1,4 +1,4 @@
|
|||||||
#if defined(GB_SYSTEM_FREEBSD)
|
#if defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -16,6 +16,7 @@ enum TargetOsKind {
|
|||||||
TargetOs_linux,
|
TargetOs_linux,
|
||||||
TargetOs_essence,
|
TargetOs_essence,
|
||||||
TargetOs_freebsd,
|
TargetOs_freebsd,
|
||||||
|
TargetOs_openbsd,
|
||||||
|
|
||||||
TargetOs_wasi,
|
TargetOs_wasi,
|
||||||
TargetOs_js,
|
TargetOs_js,
|
||||||
@@ -53,6 +54,7 @@ String target_os_names[TargetOs_COUNT] = {
|
|||||||
str_lit("linux"),
|
str_lit("linux"),
|
||||||
str_lit("essence"),
|
str_lit("essence"),
|
||||||
str_lit("freebsd"),
|
str_lit("freebsd"),
|
||||||
|
str_lit("openbsd"),
|
||||||
|
|
||||||
str_lit("wasi"),
|
str_lit("wasi"),
|
||||||
str_lit("js"),
|
str_lit("js"),
|
||||||
@@ -278,7 +280,7 @@ bool global_ignore_warnings(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
gb_global TargetMetrics target_windows_386 = {
|
gb_global TargetMetrics target_windows_i386 = {
|
||||||
TargetOs_windows,
|
TargetOs_windows,
|
||||||
TargetArch_i386,
|
TargetArch_i386,
|
||||||
4,
|
4,
|
||||||
@@ -294,7 +296,7 @@ gb_global TargetMetrics target_windows_amd64 = {
|
|||||||
str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
|
str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
|
||||||
};
|
};
|
||||||
|
|
||||||
gb_global TargetMetrics target_linux_386 = {
|
gb_global TargetMetrics target_linux_i386 = {
|
||||||
TargetOs_linux,
|
TargetOs_linux,
|
||||||
TargetArch_i386,
|
TargetArch_i386,
|
||||||
4,
|
4,
|
||||||
@@ -337,7 +339,7 @@ gb_global TargetMetrics target_darwin_arm64 = {
|
|||||||
str_lit("e-m:o-i64:64-i128:128-n32:64-S128"), // TODO(bill): Is this correct?
|
str_lit("e-m:o-i64:64-i128:128-n32:64-S128"), // TODO(bill): Is this correct?
|
||||||
};
|
};
|
||||||
|
|
||||||
gb_global TargetMetrics target_freebsd_386 = {
|
gb_global TargetMetrics target_freebsd_i386 = {
|
||||||
TargetOs_freebsd,
|
TargetOs_freebsd,
|
||||||
TargetArch_i386,
|
TargetArch_i386,
|
||||||
4,
|
4,
|
||||||
@@ -354,6 +356,15 @@ gb_global TargetMetrics target_freebsd_amd64 = {
|
|||||||
str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
|
str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
gb_global TargetMetrics target_openbsd_amd64 = {
|
||||||
|
TargetOs_openbsd,
|
||||||
|
TargetArch_amd64,
|
||||||
|
8,
|
||||||
|
16,
|
||||||
|
str_lit("x86_64-unknown-openbsd-elf"),
|
||||||
|
str_lit("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"),
|
||||||
|
};
|
||||||
|
|
||||||
gb_global TargetMetrics target_essence_amd64 = {
|
gb_global TargetMetrics target_essence_amd64 = {
|
||||||
TargetOs_essence,
|
TargetOs_essence,
|
||||||
TargetArch_amd64,
|
TargetArch_amd64,
|
||||||
@@ -410,13 +421,14 @@ gb_global NamedTargetMetrics named_targets[] = {
|
|||||||
{ str_lit("darwin_amd64"), &target_darwin_amd64 },
|
{ str_lit("darwin_amd64"), &target_darwin_amd64 },
|
||||||
{ str_lit("darwin_arm64"), &target_darwin_arm64 },
|
{ str_lit("darwin_arm64"), &target_darwin_arm64 },
|
||||||
{ str_lit("essence_amd64"), &target_essence_amd64 },
|
{ str_lit("essence_amd64"), &target_essence_amd64 },
|
||||||
{ str_lit("linux_386"), &target_linux_386 },
|
{ str_lit("linux_i386"), &target_linux_i386 },
|
||||||
{ str_lit("linux_amd64"), &target_linux_amd64 },
|
{ str_lit("linux_amd64"), &target_linux_amd64 },
|
||||||
{ str_lit("linux_arm64"), &target_linux_arm64 },
|
{ str_lit("linux_arm64"), &target_linux_arm64 },
|
||||||
{ str_lit("windows_386"), &target_windows_386 },
|
{ str_lit("windows_i386"), &target_windows_i386 },
|
||||||
{ str_lit("windows_amd64"), &target_windows_amd64 },
|
{ str_lit("windows_amd64"), &target_windows_amd64 },
|
||||||
{ str_lit("freebsd_386"), &target_freebsd_386 },
|
{ str_lit("freebsd_i386"), &target_freebsd_i386 },
|
||||||
{ str_lit("freebsd_amd64"), &target_freebsd_amd64 },
|
{ str_lit("freebsd_amd64"), &target_freebsd_amd64 },
|
||||||
|
{ str_lit("openbsd_amd64"), &target_openbsd_amd64 },
|
||||||
{ str_lit("freestanding_wasm32"), &target_freestanding_wasm32 },
|
{ str_lit("freestanding_wasm32"), &target_freestanding_wasm32 },
|
||||||
{ str_lit("wasi_wasm32"), &target_wasi_wasm32 },
|
{ str_lit("wasi_wasm32"), &target_wasi_wasm32 },
|
||||||
{ str_lit("js_wasm32"), &target_js_wasm32 },
|
{ str_lit("js_wasm32"), &target_js_wasm32 },
|
||||||
@@ -722,10 +734,38 @@ String internal_odin_root_dir(void) {
|
|||||||
len = readlink("/proc/curproc/exe", &path_buf[0], path_buf.count);
|
len = readlink("/proc/curproc/exe", &path_buf[0], path_buf.count);
|
||||||
#elif defined(GB_SYSTEM_DRAGONFLYBSD)
|
#elif defined(GB_SYSTEM_DRAGONFLYBSD)
|
||||||
len = readlink("/proc/curproc/file", &path_buf[0], path_buf.count);
|
len = readlink("/proc/curproc/file", &path_buf[0], path_buf.count);
|
||||||
#else
|
#elif defined(GB_SYSTEM_LINUX)
|
||||||
len = readlink("/proc/self/exe", &path_buf[0], path_buf.count);
|
len = readlink("/proc/self/exe", &path_buf[0], path_buf.count);
|
||||||
|
#elif defined(GB_SYSTEM_OPENBSD)
|
||||||
|
int error;
|
||||||
|
int mib[] = {
|
||||||
|
CTL_KERN,
|
||||||
|
KERN_PROC_ARGS,
|
||||||
|
getpid(),
|
||||||
|
KERN_PROC_ARGV,
|
||||||
|
};
|
||||||
|
// get argv size
|
||||||
|
error = sysctl(mib, 4, NULL, (size_t *) &len, NULL, 0);
|
||||||
|
if (error == -1) {
|
||||||
|
// sysctl error
|
||||||
|
return make_string(nullptr, 0);
|
||||||
|
}
|
||||||
|
// get argv
|
||||||
|
char **argv = (char **)gb_malloc(len);
|
||||||
|
error = sysctl(mib, 4, argv, (size_t *) &len, NULL, 0);
|
||||||
|
if (error == -1) {
|
||||||
|
// sysctl error
|
||||||
|
gb_mfree(argv);
|
||||||
|
return make_string(nullptr, 0);
|
||||||
|
}
|
||||||
|
// copy argv[0] to path_buf
|
||||||
|
len = gb_strlen(argv[0]);
|
||||||
|
if(len < path_buf.count) {
|
||||||
|
gb_memmove(&path_buf[0], argv[0], len);
|
||||||
|
}
|
||||||
|
gb_mfree(argv);
|
||||||
#endif
|
#endif
|
||||||
if(len == 0) {
|
if(len == 0 || len == -1) {
|
||||||
return make_string(nullptr, 0);
|
return make_string(nullptr, 0);
|
||||||
}
|
}
|
||||||
if (len < path_buf.count) {
|
if (len < path_buf.count) {
|
||||||
@@ -922,6 +962,8 @@ void init_build_context(TargetMetrics *cross_target) {
|
|||||||
#endif
|
#endif
|
||||||
#elif defined(GB_SYSTEM_FREEBSD)
|
#elif defined(GB_SYSTEM_FREEBSD)
|
||||||
metrics = &target_freebsd_amd64;
|
metrics = &target_freebsd_amd64;
|
||||||
|
#elif defined(GB_SYSTEM_OPENBSD)
|
||||||
|
metrics = &target_openbsd_amd64;
|
||||||
#elif defined(GB_CPU_ARM)
|
#elif defined(GB_CPU_ARM)
|
||||||
metrics = &target_linux_arm64;
|
metrics = &target_linux_arm64;
|
||||||
#else
|
#else
|
||||||
@@ -929,13 +971,13 @@ void init_build_context(TargetMetrics *cross_target) {
|
|||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
#if defined(GB_SYSTEM_WINDOWS)
|
#if defined(GB_SYSTEM_WINDOWS)
|
||||||
metrics = &target_windows_386;
|
metrics = &target_windows_i386;
|
||||||
#elif defined(GB_SYSTEM_OSX)
|
#elif defined(GB_SYSTEM_OSX)
|
||||||
#error "Build Error: Unsupported architecture"
|
#error "Build Error: Unsupported architecture"
|
||||||
#elif defined(GB_SYSTEM_FREEBSD)
|
#elif defined(GB_SYSTEM_FREEBSD)
|
||||||
metrics = &target_freebsd_386;
|
metrics = &target_freebsd_i386;
|
||||||
#else
|
#else
|
||||||
metrics = &target_linux_386;
|
metrics = &target_linux_i386;
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -980,6 +1022,9 @@ void init_build_context(TargetMetrics *cross_target) {
|
|||||||
case TargetOs_freebsd:
|
case TargetOs_freebsd:
|
||||||
bc->link_flags = str_lit("-arch x86-64 ");
|
bc->link_flags = str_lit("-arch x86-64 ");
|
||||||
break;
|
break;
|
||||||
|
case TargetOs_openbsd:
|
||||||
|
bc->link_flags = str_lit("-arch x86-64 ");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else if (bc->metrics.arch == TargetArch_i386) {
|
} else if (bc->metrics.arch == TargetArch_i386) {
|
||||||
switch (bc->metrics.os) {
|
switch (bc->metrics.os) {
|
||||||
|
|||||||
+44
-28
@@ -223,36 +223,35 @@ void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_type, Slice<T
|
|||||||
map_set(&c->info->objc_msgSend_types, call, data);
|
map_set(&c->info->objc_msgSend_types, call, data);
|
||||||
mutex_unlock(&c->info->objc_types_mutex);
|
mutex_unlock(&c->info->objc_types_mutex);
|
||||||
|
|
||||||
add_package_dependency(c, "runtime", "objc_lookUpClass");
|
try_to_add_package_dependency(c, "runtime", "objc_msgSend");
|
||||||
add_package_dependency(c, "runtime", "sel_registerName");
|
try_to_add_package_dependency(c, "runtime", "objc_msgSend_fpret");
|
||||||
add_package_dependency(c, "runtime", "objc_allocateClassPair");
|
try_to_add_package_dependency(c, "runtime", "objc_msgSend_fp2ret");
|
||||||
|
try_to_add_package_dependency(c, "runtime", "objc_msgSend_stret");
|
||||||
|
}
|
||||||
|
|
||||||
add_package_dependency(c, "runtime", "objc_msgSend");
|
bool is_constant_string(CheckerContext *c, String const &builtin_name, Ast *expr, String *name_) {
|
||||||
add_package_dependency(c, "runtime", "objc_msgSend_fpret");
|
Operand op = {};
|
||||||
add_package_dependency(c, "runtime", "objc_msgSend_fp2ret");
|
check_expr(c, &op, expr);
|
||||||
add_package_dependency(c, "runtime", "objc_msgSend_stret");
|
if (op.mode == Addressing_Constant && op.value.kind == ExactValue_String) {
|
||||||
|
if (name_) *name_ = op.value.value_string;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
gbString e = expr_to_string(op.expr);
|
||||||
|
gbString t = type_to_string(op.type);
|
||||||
|
error(op.expr, "'%.*s' expected a constant string value, got %s of type %s", LIT(builtin_name), e, t);
|
||||||
|
gb_string_free(t);
|
||||||
|
gb_string_free(e);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) {
|
bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) {
|
||||||
auto const is_constant_string = [](CheckerContext *c, String const &builtin_name, Ast *expr, String *name_) -> bool {
|
|
||||||
Operand op = {};
|
|
||||||
check_expr(c, &op, expr);
|
|
||||||
if (op.mode == Addressing_Constant && op.value.kind == ExactValue_String) {
|
|
||||||
if (name_) *name_ = op.value.value_string;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
gbString e = expr_to_string(op.expr);
|
|
||||||
gbString t = type_to_string(op.type);
|
|
||||||
error(op.expr, "'%.*s' expected a constant string value, got %s of type %s", LIT(builtin_name), e, t);
|
|
||||||
gb_string_free(t);
|
|
||||||
gb_string_free(e);
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
String builtin_name = builtin_procs[id].name;
|
String builtin_name = builtin_procs[id].name;
|
||||||
|
|
||||||
if (build_context.metrics.os != TargetOs_darwin) {
|
if (build_context.metrics.os != TargetOs_darwin) {
|
||||||
error(call, "'%.*s' only works on darwin", LIT(builtin_name));
|
// allow on doc generation (e.g. Metal stuff)
|
||||||
return false;
|
if (build_context.command_kind != Command_doc && build_context.command_kind != Command_check) {
|
||||||
|
error(call, "'%.*s' only works on darwin", LIT(builtin_name));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -371,6 +370,10 @@ bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call
|
|||||||
|
|
||||||
}
|
}
|
||||||
operand->mode = Addressing_Value;
|
operand->mode = Addressing_Value;
|
||||||
|
|
||||||
|
try_to_add_package_dependency(c, "runtime", "objc_lookUpClass");
|
||||||
|
try_to_add_package_dependency(c, "runtime", "sel_registerName");
|
||||||
|
try_to_add_package_dependency(c, "runtime", "objc_allocateClassPair");
|
||||||
return true;
|
return true;
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
@@ -949,7 +952,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
|||||||
mode = Addressing_Constant;
|
mode = Addressing_Constant;
|
||||||
value = exact_value_i64(at->EnumeratedArray.count);
|
value = exact_value_i64(at->EnumeratedArray.count);
|
||||||
type = t_untyped_integer;
|
type = t_untyped_integer;
|
||||||
} else if (is_type_slice(op_type) && id == BuiltinProc_len) {
|
} else if ((is_type_slice(op_type) || is_type_relative_slice(op_type)) && id == BuiltinProc_len) {
|
||||||
mode = Addressing_Value;
|
mode = Addressing_Value;
|
||||||
} else if (is_type_dynamic_array(op_type)) {
|
} else if (is_type_dynamic_array(op_type)) {
|
||||||
mode = Addressing_Value;
|
mode = Addressing_Value;
|
||||||
@@ -1103,7 +1106,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
|||||||
|
|
||||||
Selection sel = lookup_field(type, field_name, false);
|
Selection sel = lookup_field(type, field_name, false);
|
||||||
if (sel.entity == nullptr) {
|
if (sel.entity == nullptr) {
|
||||||
gbString type_str = type_to_string(type);
|
gbString type_str = type_to_string_shorthand(type);
|
||||||
error(ce->args[0],
|
error(ce->args[0],
|
||||||
"'%s' has no field named '%.*s'", type_str, LIT(field_name));
|
"'%s' has no field named '%.*s'", type_str, LIT(field_name));
|
||||||
gb_string_free(type_str);
|
gb_string_free(type_str);
|
||||||
@@ -1115,7 +1118,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (sel.indirect) {
|
if (sel.indirect) {
|
||||||
gbString type_str = type_to_string(type);
|
gbString type_str = type_to_string_shorthand(type);
|
||||||
error(ce->args[0],
|
error(ce->args[0],
|
||||||
"Field '%.*s' is embedded via a pointer in '%s'", LIT(field_name), type_str);
|
"Field '%.*s' is embedded via a pointer in '%s'", LIT(field_name), type_str);
|
||||||
gb_string_free(type_str);
|
gb_string_free(type_str);
|
||||||
@@ -1176,7 +1179,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
|||||||
|
|
||||||
Selection sel = lookup_field(type, field_name, false);
|
Selection sel = lookup_field(type, field_name, false);
|
||||||
if (sel.entity == nullptr) {
|
if (sel.entity == nullptr) {
|
||||||
gbString type_str = type_to_string(type);
|
gbString type_str = type_to_string_shorthand(type);
|
||||||
error(ce->args[0],
|
error(ce->args[0],
|
||||||
"'%s' has no field named '%.*s'", type_str, LIT(field_name));
|
"'%s' has no field named '%.*s'", type_str, LIT(field_name));
|
||||||
gb_string_free(type_str);
|
gb_string_free(type_str);
|
||||||
@@ -1188,7 +1191,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (sel.indirect) {
|
if (sel.indirect) {
|
||||||
gbString type_str = type_to_string(type);
|
gbString type_str = type_to_string_shorthand(type);
|
||||||
error(ce->args[0],
|
error(ce->args[0],
|
||||||
"Field '%.*s' is embedded via a pointer in '%s'", LIT(field_name), type_str);
|
"Field '%.*s' is embedded via a pointer in '%s'", LIT(field_name), type_str);
|
||||||
gb_string_free(type_str);
|
gb_string_free(type_str);
|
||||||
@@ -3503,6 +3506,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
|||||||
case TargetOs_linux:
|
case TargetOs_linux:
|
||||||
case TargetOs_essence:
|
case TargetOs_essence:
|
||||||
case TargetOs_freebsd:
|
case TargetOs_freebsd:
|
||||||
|
case TargetOs_openbsd:
|
||||||
switch (build_context.metrics.arch) {
|
switch (build_context.metrics.arch) {
|
||||||
case TargetArch_i386:
|
case TargetArch_i386:
|
||||||
case TargetArch_amd64:
|
case TargetArch_amd64:
|
||||||
@@ -4086,6 +4090,18 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case BuiltinProc_constant_utf16_cstring:
|
||||||
|
{
|
||||||
|
String value = {};
|
||||||
|
if (!is_constant_string(c, builtin_name, ce->args[0], &value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
operand->mode = Addressing_Value;
|
||||||
|
operand->type = alloc_type_multi_pointer(t_u16);
|
||||||
|
operand->value = {};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -174,6 +174,10 @@ void check_init_constant(CheckerContext *ctx, Entity *e, Operand *operand) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_type_proc(e->type)) {
|
||||||
|
error(e->token, "Illegal declaration of a constant procedure value");
|
||||||
|
}
|
||||||
|
|
||||||
e->parent_proc_decl = ctx->curr_proc_decl;
|
e->parent_proc_decl = ctx->curr_proc_decl;
|
||||||
|
|
||||||
e->Constant.value = operand->value;
|
e->Constant.value = operand->value;
|
||||||
|
|||||||
+70
-5
@@ -132,6 +132,62 @@ void check_did_you_mean_print(DidYouMeanAnswers *d, char const *prefix = "") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void populate_check_did_you_mean_objc_entity(StringSet *set, Entity *e, bool is_type) {
|
||||||
|
if (e->kind != Entity_TypeName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (e->TypeName.objc_metadata == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TypeNameObjCMetadata *objc_metadata = e->TypeName.objc_metadata;
|
||||||
|
Type *t = base_type(e->type);
|
||||||
|
GB_ASSERT(t->kind == Type_Struct);
|
||||||
|
|
||||||
|
if (is_type) {
|
||||||
|
for_array(i, objc_metadata->type_entries) {
|
||||||
|
String name = objc_metadata->type_entries[i].name;
|
||||||
|
string_set_add(set, name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for_array(i, objc_metadata->value_entries) {
|
||||||
|
String name = objc_metadata->value_entries[i].name;
|
||||||
|
string_set_add(set, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for_array(i, t->Struct.fields) {
|
||||||
|
Entity *f = t->Struct.fields[i];
|
||||||
|
if (f->flags & EntityFlag_Using && f->type != nullptr) {
|
||||||
|
if (f->type->kind == Type_Named && f->type->Named.type_name) {
|
||||||
|
populate_check_did_you_mean_objc_entity(set, f->type->Named.type_name, is_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void check_did_you_mean_objc_entity(String const &name, Entity *e, bool is_type, char const *prefix = "") {
|
||||||
|
ERROR_BLOCK();
|
||||||
|
GB_ASSERT(e->kind == Entity_TypeName);
|
||||||
|
GB_ASSERT(e->TypeName.objc_metadata != nullptr);
|
||||||
|
auto *objc_metadata = e->TypeName.objc_metadata;
|
||||||
|
mutex_lock(objc_metadata->mutex);
|
||||||
|
defer (mutex_unlock(objc_metadata->mutex));
|
||||||
|
|
||||||
|
StringSet set = {};
|
||||||
|
string_set_init(&set, heap_allocator());
|
||||||
|
defer (string_set_destroy(&set));
|
||||||
|
populate_check_did_you_mean_objc_entity(&set, e, is_type);
|
||||||
|
|
||||||
|
|
||||||
|
DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), set.entries.count, name);
|
||||||
|
defer (did_you_mean_destroy(&d));
|
||||||
|
for_array(i, set.entries) {
|
||||||
|
did_you_mean_append(&d, set.entries[i].value);
|
||||||
|
}
|
||||||
|
check_did_you_mean_print(&d, prefix);
|
||||||
|
}
|
||||||
|
|
||||||
void check_did_you_mean_type(String const &name, Array<Entity *> const &fields, char const *prefix = "") {
|
void check_did_you_mean_type(String const &name, Array<Entity *> const &fields, char const *prefix = "") {
|
||||||
ERROR_BLOCK();
|
ERROR_BLOCK();
|
||||||
|
|
||||||
@@ -144,6 +200,7 @@ void check_did_you_mean_type(String const &name, Array<Entity *> const &fields,
|
|||||||
check_did_you_mean_print(&d, prefix);
|
check_did_you_mean_print(&d, prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void check_did_you_mean_type(String const &name, Slice<Entity *> const &fields, char const *prefix = "") {
|
void check_did_you_mean_type(String const &name, Slice<Entity *> const &fields, char const *prefix = "") {
|
||||||
ERROR_BLOCK();
|
ERROR_BLOCK();
|
||||||
|
|
||||||
@@ -4413,14 +4470,19 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
|
|||||||
|
|
||||||
if (entity == nullptr) {
|
if (entity == nullptr) {
|
||||||
gbString op_str = expr_to_string(op_expr);
|
gbString op_str = expr_to_string(op_expr);
|
||||||
gbString type_str = type_to_string(operand->type);
|
gbString type_str = type_to_string_shorthand(operand->type);
|
||||||
gbString sel_str = expr_to_string(selector);
|
gbString sel_str = expr_to_string(selector);
|
||||||
error(op_expr, "'%s' of type '%s' has no field '%s'", op_str, type_str, sel_str);
|
error(op_expr, "'%s' of type '%s' has no field '%s'", op_str, type_str, sel_str);
|
||||||
|
|
||||||
if (operand->type != nullptr && selector->kind == Ast_Ident) {
|
if (operand->type != nullptr && selector->kind == Ast_Ident) {
|
||||||
String const &name = selector->Ident.token.string;
|
String const &name = selector->Ident.token.string;
|
||||||
Type *bt = base_type(operand->type);
|
Type *bt = base_type(operand->type);
|
||||||
if (bt->kind == Type_Struct) {
|
if (operand->type->kind == Type_Named &&
|
||||||
|
operand->type->Named.type_name &&
|
||||||
|
operand->type->Named.type_name->kind == Entity_TypeName &&
|
||||||
|
operand->type->Named.type_name->TypeName.objc_metadata) {
|
||||||
|
check_did_you_mean_objc_entity(name, operand->type->Named.type_name, operand->mode == Addressing_Type);
|
||||||
|
} else if (bt->kind == Type_Struct) {
|
||||||
check_did_you_mean_type(name, bt->Struct.fields);
|
check_did_you_mean_type(name, bt->Struct.fields);
|
||||||
} else if (bt->kind == Type_Enum) {
|
} else if (bt->kind == Type_Enum) {
|
||||||
check_did_you_mean_type(name, bt->Enum.fields);
|
check_did_you_mean_type(name, bt->Enum.fields);
|
||||||
@@ -4449,7 +4511,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
|
|||||||
}
|
}
|
||||||
|
|
||||||
gbString op_str = expr_to_string(op_expr);
|
gbString op_str = expr_to_string(op_expr);
|
||||||
gbString type_str = type_to_string(operand->type);
|
gbString type_str = type_to_string_shorthand(operand->type);
|
||||||
gbString sel_str = expr_to_string(selector);
|
gbString sel_str = expr_to_string(selector);
|
||||||
error(op_expr, "Cannot access non-constant field '%s' from '%s'", sel_str, op_str);
|
error(op_expr, "Cannot access non-constant field '%s' from '%s'", sel_str, op_str);
|
||||||
gb_string_free(sel_str);
|
gb_string_free(sel_str);
|
||||||
@@ -4474,7 +4536,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
|
|||||||
}
|
}
|
||||||
|
|
||||||
gbString op_str = expr_to_string(op_expr);
|
gbString op_str = expr_to_string(op_expr);
|
||||||
gbString type_str = type_to_string(operand->type);
|
gbString type_str = type_to_string_shorthand(operand->type);
|
||||||
gbString sel_str = expr_to_string(selector);
|
gbString sel_str = expr_to_string(selector);
|
||||||
error(op_expr, "Cannot access non-constant field '%s' from '%s'", sel_str, op_str);
|
error(op_expr, "Cannot access non-constant field '%s' from '%s'", sel_str, op_str);
|
||||||
gb_string_free(sel_str);
|
gb_string_free(sel_str);
|
||||||
@@ -4487,7 +4549,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
|
|||||||
|
|
||||||
if (expr_entity != nullptr && is_type_polymorphic(expr_entity->type)) {
|
if (expr_entity != nullptr && is_type_polymorphic(expr_entity->type)) {
|
||||||
gbString op_str = expr_to_string(op_expr);
|
gbString op_str = expr_to_string(op_expr);
|
||||||
gbString type_str = type_to_string(operand->type);
|
gbString type_str = type_to_string_shorthand(operand->type);
|
||||||
gbString sel_str = expr_to_string(selector);
|
gbString sel_str = expr_to_string(selector);
|
||||||
error(op_expr, "Cannot access field '%s' from non-specialized polymorphic type '%s'", sel_str, op_str);
|
error(op_expr, "Cannot access field '%s' from non-specialized polymorphic type '%s'", sel_str, op_str);
|
||||||
gb_string_free(sel_str);
|
gb_string_free(sel_str);
|
||||||
@@ -9774,6 +9836,9 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) {
|
|||||||
if (f->flags&FieldFlag_const) {
|
if (f->flags&FieldFlag_const) {
|
||||||
str = gb_string_appendc(str, "#const ");
|
str = gb_string_appendc(str, "#const ");
|
||||||
}
|
}
|
||||||
|
if (f->flags&FieldFlag_subtype) {
|
||||||
|
str = gb_string_appendc(str, "#subtype ");
|
||||||
|
}
|
||||||
|
|
||||||
for_array(i, f->names) {
|
for_array(i, f->names) {
|
||||||
Ast *name = f->names[i];
|
Ast *name = f->names[i];
|
||||||
|
|||||||
@@ -144,6 +144,7 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entity *> *fields
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool is_using = (p->flags&FieldFlag_using) != 0;
|
bool is_using = (p->flags&FieldFlag_using) != 0;
|
||||||
|
bool is_subtype = (p->flags&FieldFlag_subtype) != 0;
|
||||||
|
|
||||||
for_array(j, p->names) {
|
for_array(j, p->names) {
|
||||||
Ast *name = p->names[j];
|
Ast *name = p->names[j];
|
||||||
@@ -158,6 +159,9 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entity *> *fields
|
|||||||
Entity *field = alloc_entity_field(ctx->scope, name_token, type, is_using, field_src_index);
|
Entity *field = alloc_entity_field(ctx->scope, name_token, type, is_using, field_src_index);
|
||||||
add_entity(ctx, ctx->scope, name, field);
|
add_entity(ctx, ctx->scope, name, field);
|
||||||
field->Variable.field_group_index = field_group_index;
|
field->Variable.field_group_index = field_group_index;
|
||||||
|
if (is_subtype) {
|
||||||
|
field->flags |= EntityFlag_Subtype;
|
||||||
|
}
|
||||||
|
|
||||||
if (j == 0) {
|
if (j == 0) {
|
||||||
field->Variable.docs = docs;
|
field->Variable.docs = docs;
|
||||||
@@ -194,6 +198,20 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entity *> *fields
|
|||||||
|
|
||||||
populate_using_entity_scope(ctx, node, p, type);
|
populate_using_entity_scope(ctx, node, p, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_subtype && p->names.count > 0) {
|
||||||
|
Type *first_type = fields_array[fields_array.count-1]->type;
|
||||||
|
Type *t = base_type(type_deref(first_type));
|
||||||
|
|
||||||
|
if (!does_field_type_allow_using(t) &&
|
||||||
|
p->names.count >= 1 &&
|
||||||
|
p->names[0]->kind == Ast_Ident) {
|
||||||
|
Token name_token = p->names[0]->Ident.token;
|
||||||
|
gbString type_str = type_to_string(first_type);
|
||||||
|
error(name_token, "'subtype' cannot be applied to the field '%.*s' of type '%s'", LIT(name_token.string), type_str);
|
||||||
|
gb_string_free(type_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*fields = slice_from_array(fields_array);
|
*fields = slice_from_array(fields_array);
|
||||||
|
|||||||
+17
-3
@@ -225,8 +225,8 @@ bool decl_info_has_init(DeclInfo *d) {
|
|||||||
Scope *create_scope(CheckerInfo *info, Scope *parent, isize init_elements_capacity=DEFAULT_SCOPE_CAPACITY) {
|
Scope *create_scope(CheckerInfo *info, Scope *parent, isize init_elements_capacity=DEFAULT_SCOPE_CAPACITY) {
|
||||||
Scope *s = gb_alloc_item(permanent_allocator(), Scope);
|
Scope *s = gb_alloc_item(permanent_allocator(), Scope);
|
||||||
s->parent = parent;
|
s->parent = parent;
|
||||||
string_map_init(&s->elements, permanent_allocator(), init_elements_capacity);
|
string_map_init(&s->elements, heap_allocator(), init_elements_capacity);
|
||||||
ptr_set_init(&s->imported, permanent_allocator(), 0);
|
ptr_set_init(&s->imported, heap_allocator(), 0);
|
||||||
mutex_init(&s->mutex);
|
mutex_init(&s->mutex);
|
||||||
|
|
||||||
if (parent != nullptr && parent != builtin_pkg->scope) {
|
if (parent != nullptr && parent != builtin_pkg->scope) {
|
||||||
@@ -733,12 +733,25 @@ void add_package_dependency(CheckerContext *c, char const *package_name, char co
|
|||||||
String n = make_string_c(name);
|
String n = make_string_c(name);
|
||||||
AstPackage *p = get_core_package(&c->checker->info, make_string_c(package_name));
|
AstPackage *p = get_core_package(&c->checker->info, make_string_c(package_name));
|
||||||
Entity *e = scope_lookup(p->scope, n);
|
Entity *e = scope_lookup(p->scope, n);
|
||||||
e->flags |= EntityFlag_Used;
|
|
||||||
GB_ASSERT_MSG(e != nullptr, "%s", name);
|
GB_ASSERT_MSG(e != nullptr, "%s", name);
|
||||||
GB_ASSERT(c->decl != nullptr);
|
GB_ASSERT(c->decl != nullptr);
|
||||||
|
e->flags |= EntityFlag_Used;
|
||||||
add_dependency(c->info, c->decl, e);
|
add_dependency(c->info, c->decl, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void try_to_add_package_dependency(CheckerContext *c, char const *package_name, char const *name) {
|
||||||
|
String n = make_string_c(name);
|
||||||
|
AstPackage *p = get_core_package(&c->checker->info, make_string_c(package_name));
|
||||||
|
Entity *e = scope_lookup(p->scope, n);
|
||||||
|
if (e == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GB_ASSERT(c->decl != nullptr);
|
||||||
|
e->flags |= EntityFlag_Used;
|
||||||
|
add_dependency(c->info, c->decl, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void add_declaration_dependency(CheckerContext *c, Entity *e) {
|
void add_declaration_dependency(CheckerContext *c, Entity *e) {
|
||||||
if (e == nullptr) {
|
if (e == nullptr) {
|
||||||
return;
|
return;
|
||||||
@@ -893,6 +906,7 @@ void init_universal(void) {
|
|||||||
{"Linux", TargetOs_linux},
|
{"Linux", TargetOs_linux},
|
||||||
{"Essence", TargetOs_essence},
|
{"Essence", TargetOs_essence},
|
||||||
{"FreeBSD", TargetOs_freebsd},
|
{"FreeBSD", TargetOs_freebsd},
|
||||||
|
{"OpenBSD", TargetOs_openbsd},
|
||||||
{"WASI", TargetOs_wasi},
|
{"WASI", TargetOs_wasi},
|
||||||
{"JS", TargetOs_js},
|
{"JS", TargetOs_js},
|
||||||
{"Freestanding", TargetOs_freestanding},
|
{"Freestanding", TargetOs_freestanding},
|
||||||
|
|||||||
@@ -258,6 +258,9 @@ BuiltinProc__type_end,
|
|||||||
BuiltinProc_objc_register_selector,
|
BuiltinProc_objc_register_selector,
|
||||||
BuiltinProc_objc_register_class,
|
BuiltinProc_objc_register_class,
|
||||||
|
|
||||||
|
BuiltinProc_constant_utf16_cstring,
|
||||||
|
|
||||||
|
|
||||||
BuiltinProc_COUNT,
|
BuiltinProc_COUNT,
|
||||||
};
|
};
|
||||||
gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
|
gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
|
||||||
@@ -517,4 +520,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
|
|||||||
{STR_LIT("objc_find_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
{STR_LIT("objc_find_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||||
{STR_LIT("objc_register_selector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
{STR_LIT("objc_register_selector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||||
{STR_LIT("objc_register_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
{STR_LIT("objc_register_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||||
|
|
||||||
|
{STR_LIT("constant_utf16_cstring"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
+2
-2
@@ -848,7 +848,7 @@ ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
|
|||||||
|
|
||||||
return ReadDirectory_None;
|
return ReadDirectory_None;
|
||||||
}
|
}
|
||||||
#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD)
|
#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
|
||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
|
||||||
@@ -1021,7 +1021,7 @@ LoadedFileError load_file_32(char const *fullpath, LoadedFile *memory_mapped_fil
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
gbFileContents fc = gb_file_read_contents(heap_allocator(), true, fullpath);
|
gbFileContents fc = gb_file_read_contents(permanent_allocator(), true, fullpath);
|
||||||
|
|
||||||
if (fc.size > I32_MAX) {
|
if (fc.size > I32_MAX) {
|
||||||
err = LoadedFile_FileTooLarge;
|
err = LoadedFile_FileTooLarge;
|
||||||
|
|||||||
+17
-2
@@ -139,6 +139,7 @@ struct PlatformMemoryBlock {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
gb_global std::atomic<isize> global_platform_memory_total_usage;
|
||||||
gb_global PlatformMemoryBlock global_platform_memory_block_sentinel;
|
gb_global PlatformMemoryBlock global_platform_memory_block_sentinel;
|
||||||
|
|
||||||
PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size);
|
PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size);
|
||||||
@@ -158,10 +159,17 @@ void platform_virtual_memory_protect(void *memory, isize size);
|
|||||||
|
|
||||||
PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size) {
|
PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size) {
|
||||||
PlatformMemoryBlock *pmblock = (PlatformMemoryBlock *)VirtualAlloc(0, total_size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
|
PlatformMemoryBlock *pmblock = (PlatformMemoryBlock *)VirtualAlloc(0, total_size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
|
||||||
GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no...");
|
if (pmblock == nullptr) {
|
||||||
|
gb_printf_err("Out of Virtual memory, oh no...\n");
|
||||||
|
gb_printf_err("Requested: %lld bytes\n", cast(long long)total_size);
|
||||||
|
gb_printf_err("Total Usage: %lld bytes\n", cast(long long)global_platform_memory_total_usage);
|
||||||
|
GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no...");
|
||||||
|
}
|
||||||
|
global_platform_memory_total_usage += total_size;
|
||||||
return pmblock;
|
return pmblock;
|
||||||
}
|
}
|
||||||
void platform_virtual_memory_free(PlatformMemoryBlock *block) {
|
void platform_virtual_memory_free(PlatformMemoryBlock *block) {
|
||||||
|
global_platform_memory_total_usage -= block->total_size;
|
||||||
GB_ASSERT(VirtualFree(block, 0, MEM_RELEASE));
|
GB_ASSERT(VirtualFree(block, 0, MEM_RELEASE));
|
||||||
}
|
}
|
||||||
void platform_virtual_memory_protect(void *memory, isize size) {
|
void platform_virtual_memory_protect(void *memory, isize size) {
|
||||||
@@ -180,11 +188,18 @@ void platform_virtual_memory_protect(void *memory, isize size);
|
|||||||
|
|
||||||
PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size) {
|
PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size) {
|
||||||
PlatformMemoryBlock *pmblock = (PlatformMemoryBlock *)mmap(nullptr, total_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
PlatformMemoryBlock *pmblock = (PlatformMemoryBlock *)mmap(nullptr, total_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||||
GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no...");
|
if (pmblock == nullptr) {
|
||||||
|
gb_printf_err("Out of Virtual memory, oh no...\n");
|
||||||
|
gb_printf_err("Requested: %lld bytes\n", cast(long long)total_size);
|
||||||
|
gb_printf_err("Total Usage: %lld bytes\n", cast(long long)global_platform_memory_total_usage);
|
||||||
|
GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no...");
|
||||||
|
}
|
||||||
|
global_platform_memory_total_usage += total_size;
|
||||||
return pmblock;
|
return pmblock;
|
||||||
}
|
}
|
||||||
void platform_virtual_memory_free(PlatformMemoryBlock *block) {
|
void platform_virtual_memory_free(PlatformMemoryBlock *block) {
|
||||||
isize size = block->total_size;
|
isize size = block->total_size;
|
||||||
|
global_platform_memory_total_usage -= size;
|
||||||
munmap(block, size);
|
munmap(block, size);
|
||||||
}
|
}
|
||||||
void platform_virtual_memory_protect(void *memory, isize size) {
|
void platform_virtual_memory_protect(void *memory, isize size) {
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ enum EntityFlag : u64 {
|
|||||||
|
|
||||||
EntityFlag_Test = 1ull<<30,
|
EntityFlag_Test = 1ull<<30,
|
||||||
EntityFlag_Init = 1ull<<31,
|
EntityFlag_Init = 1ull<<31,
|
||||||
|
EntityFlag_Subtype = 1ull<<32,
|
||||||
|
|
||||||
EntityFlag_CustomLinkName = 1ull<<40,
|
EntityFlag_CustomLinkName = 1ull<<40,
|
||||||
EntityFlag_CustomLinkage_Internal = 1ull<<41,
|
EntityFlag_CustomLinkage_Internal = 1ull<<41,
|
||||||
@@ -86,6 +87,10 @@ enum EntityFlag : u64 {
|
|||||||
EntityFlag_Overridden = 1ull<<63,
|
EntityFlag_Overridden = 1ull<<63,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum : u64 {
|
||||||
|
EntityFlags_IsSubtype = EntityFlag_Using|EntityFlag_Subtype,
|
||||||
|
};
|
||||||
|
|
||||||
enum EntityState : u32 {
|
enum EntityState : u32 {
|
||||||
EntityState_Unresolved = 0,
|
EntityState_Unresolved = 0,
|
||||||
EntityState_InProgress = 1,
|
EntityState_InProgress = 1,
|
||||||
|
|||||||
+16
-5
@@ -50,9 +50,9 @@ struct ExactValue {
|
|||||||
union {
|
union {
|
||||||
bool value_bool;
|
bool value_bool;
|
||||||
String value_string;
|
String value_string;
|
||||||
BigInt value_integer; // NOTE(bill): This must be an integer and not a pointer
|
BigInt value_integer;
|
||||||
f64 value_float;
|
f64 value_float;
|
||||||
i64 value_pointer;
|
i64 value_pointer; // NOTE(bill): This must be an integer and not a pointer
|
||||||
Complex128 *value_complex;
|
Complex128 *value_complex;
|
||||||
Quaternion256 *value_quaternion;
|
Quaternion256 *value_quaternion;
|
||||||
Ast * value_compound;
|
Ast * value_compound;
|
||||||
@@ -630,6 +630,9 @@ void match_exact_values(ExactValue *x, ExactValue *y) {
|
|||||||
case ExactValue_Bool:
|
case ExactValue_Bool:
|
||||||
case ExactValue_String:
|
case ExactValue_String:
|
||||||
case ExactValue_Quaternion:
|
case ExactValue_Quaternion:
|
||||||
|
case ExactValue_Pointer:
|
||||||
|
case ExactValue_Procedure:
|
||||||
|
case ExactValue_Typeid:
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case ExactValue_Integer:
|
case ExactValue_Integer:
|
||||||
@@ -671,9 +674,6 @@ void match_exact_values(ExactValue *x, ExactValue *y) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ExactValue_Procedure:
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
compiler_error("match_exact_values: How'd you get here? Invalid ExactValueKind %d", x->kind);
|
compiler_error("match_exact_values: How'd you get here? Invalid ExactValueKind %d", x->kind);
|
||||||
@@ -932,6 +932,17 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case ExactValue_Pointer: {
|
||||||
|
switch (op) {
|
||||||
|
case Token_CmpEq: return x.value_pointer == y.value_pointer;
|
||||||
|
case Token_NotEq: return x.value_pointer != y.value_pointer;
|
||||||
|
case Token_Lt: return x.value_pointer < y.value_pointer;
|
||||||
|
case Token_LtEq: return x.value_pointer <= y.value_pointer;
|
||||||
|
case Token_Gt: return x.value_pointer > y.value_pointer;
|
||||||
|
case Token_GtEq: return x.value_pointer >= y.value_pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case ExactValue_Typeid:
|
case ExactValue_Typeid:
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case Token_CmpEq: return are_types_identical(x.value_typeid, y.value_typeid);
|
case Token_CmpEq: return are_types_identical(x.value_typeid, y.value_typeid);
|
||||||
|
|||||||
+87
-2
@@ -79,6 +79,10 @@ extern "C" {
|
|||||||
#ifndef GB_SYSTEM_FREEBSD
|
#ifndef GB_SYSTEM_FREEBSD
|
||||||
#define GB_SYSTEM_FREEBSD 1
|
#define GB_SYSTEM_FREEBSD 1
|
||||||
#endif
|
#endif
|
||||||
|
#elif defined(__OpenBSD__)
|
||||||
|
#ifndef GB_SYSTEM_OPENBSD
|
||||||
|
#define GB_SYSTEM_OPENBSD 1
|
||||||
|
#endif
|
||||||
#else
|
#else
|
||||||
#error This UNIX operating system is not supported
|
#error This UNIX operating system is not supported
|
||||||
#endif
|
#endif
|
||||||
@@ -199,7 +203,7 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
#include <stdlib.h> // NOTE(bill): malloc on linux
|
#include <stdlib.h> // NOTE(bill): malloc on linux
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__)
|
#if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
|
||||||
#include <sys/sendfile.h>
|
#include <sys/sendfile.h>
|
||||||
#endif
|
#endif
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
@@ -235,6 +239,12 @@ extern "C" {
|
|||||||
#define sendfile(out, in, offset, count) sendfile(out, in, offset, count, NULL, NULL, 0)
|
#define sendfile(out, in, offset, count) sendfile(out, in, offset, count, NULL, NULL, 0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(GB_SYSTEM_OPENBSD)
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <pthread_np.h>
|
||||||
|
#define lseek64 lseek
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(GB_SYSTEM_UNIX)
|
#if defined(GB_SYSTEM_UNIX)
|
||||||
#include <semaphore.h>
|
#include <semaphore.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -783,6 +793,13 @@ typedef struct gbAffinity {
|
|||||||
isize thread_count;
|
isize thread_count;
|
||||||
isize threads_per_core;
|
isize threads_per_core;
|
||||||
} gbAffinity;
|
} gbAffinity;
|
||||||
|
#elif defined(GB_SYSTEM_OPENBSD)
|
||||||
|
typedef struct gbAffinity {
|
||||||
|
b32 is_accurate;
|
||||||
|
isize core_count;
|
||||||
|
isize thread_count;
|
||||||
|
isize threads_per_core;
|
||||||
|
} gbAffinity;
|
||||||
#else
|
#else
|
||||||
#error TODO(bill): Unknown system
|
#error TODO(bill): Unknown system
|
||||||
#endif
|
#endif
|
||||||
@@ -3678,6 +3695,30 @@ b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) {
|
||||||
|
GB_ASSERT(0 <= core && core < a->core_count);
|
||||||
|
return a->threads_per_core;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(GB_SYSTEM_OPENBSD)
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
void gb_affinity_init(gbAffinity *a) {
|
||||||
|
a->core_count = sysconf(_SC_NPROCESSORS_ONLN);
|
||||||
|
a->threads_per_core = 1;
|
||||||
|
a->is_accurate = a->core_count > 0;
|
||||||
|
a->core_count = a->is_accurate ? a->core_count : 1;
|
||||||
|
a->thread_count = a->core_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gb_affinity_destroy(gbAffinity *a) {
|
||||||
|
gb_unused(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) {
|
isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) {
|
||||||
GB_ASSERT(0 <= core && core < a->core_count);
|
GB_ASSERT(0 <= core && core < a->core_count);
|
||||||
return a->threads_per_core;
|
return a->threads_per_core;
|
||||||
@@ -6025,7 +6066,7 @@ gbFileTime gb_file_last_write_time(char const *filepath) {
|
|||||||
gb_inline b32 gb_file_copy(char const *existing_filename, char const *new_filename, b32 fail_if_exists) {
|
gb_inline b32 gb_file_copy(char const *existing_filename, char const *new_filename, b32 fail_if_exists) {
|
||||||
#if defined(GB_SYSTEM_OSX)
|
#if defined(GB_SYSTEM_OSX)
|
||||||
return copyfile(existing_filename, new_filename, NULL, COPYFILE_DATA) == 0;
|
return copyfile(existing_filename, new_filename, NULL, COPYFILE_DATA) == 0;
|
||||||
#else
|
#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_FREEBSD)
|
||||||
isize size;
|
isize size;
|
||||||
int existing_fd = open(existing_filename, O_RDONLY, 0);
|
int existing_fd = open(existing_filename, O_RDONLY, 0);
|
||||||
int new_fd = open(new_filename, O_WRONLY|O_CREAT, 0666);
|
int new_fd = open(new_filename, O_WRONLY|O_CREAT, 0666);
|
||||||
@@ -6041,6 +6082,49 @@ gb_inline b32 gb_file_copy(char const *existing_filename, char const *new_filena
|
|||||||
close(new_fd);
|
close(new_fd);
|
||||||
close(existing_fd);
|
close(existing_fd);
|
||||||
|
|
||||||
|
return size == stat_existing.st_size;
|
||||||
|
#else
|
||||||
|
int new_flags = O_WRONLY | O_CREAT;
|
||||||
|
if (fail_if_exists) {
|
||||||
|
new_flags |= O_EXCL;
|
||||||
|
}
|
||||||
|
int existing_fd = open(existing_filename, O_RDONLY, 0);
|
||||||
|
int new_fd = open(new_filename, new_flags, 0666);
|
||||||
|
|
||||||
|
struct stat stat_existing;
|
||||||
|
if (fstat(existing_fd, &stat_existing) == -1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t bsize = stat_existing.st_blksize > BUFSIZ ? stat_existing.st_blksize : BUFSIZ;
|
||||||
|
char *buf = (char *)malloc(bsize);
|
||||||
|
if (buf == NULL) {
|
||||||
|
close(new_fd);
|
||||||
|
close(existing_fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
isize size = 0;
|
||||||
|
ssize_t nread, nwrite, offset;
|
||||||
|
while ((nread = read(existing_fd, buf, bsize)) != -1 && nread != 0) {
|
||||||
|
for (offset = 0; nread; nread -= nwrite, offset += nwrite) {
|
||||||
|
if ((nwrite = write(new_fd, buf + offset, nread)) == -1 || nwrite == 0) {
|
||||||
|
free(buf);
|
||||||
|
close(new_fd);
|
||||||
|
close(existing_fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
size += nwrite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(buf);
|
||||||
|
close(new_fd);
|
||||||
|
close(existing_fd);
|
||||||
|
|
||||||
|
if (nread == -1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
return size == stat_existing.st_size;
|
return size == stat_existing.st_size;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -6093,6 +6177,7 @@ gbFileContents gb_file_read_contents(gbAllocator a, b32 zero_terminate, char con
|
|||||||
}
|
}
|
||||||
|
|
||||||
void gb_file_free_contents(gbFileContents *fc) {
|
void gb_file_free_contents(gbFileContents *fc) {
|
||||||
|
if (fc == NULL || fc->size == 0) return;
|
||||||
GB_ASSERT_NOT_NULL(fc->data);
|
GB_ASSERT_NOT_NULL(fc->data);
|
||||||
gb_free(fc->allocator, fc->data);
|
gb_free(fc->allocator, fc->data);
|
||||||
fc->data = NULL;
|
fc->data = NULL;
|
||||||
|
|||||||
@@ -1295,6 +1295,11 @@ void lb_generate_code(lbGenerator *gen) {
|
|||||||
reloc_mode = LLVMRelocPIC;
|
reloc_mode = LLVMRelocPIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (build_context.metrics.os == TargetOs_openbsd) {
|
||||||
|
// Always use PIC for OpenBSD: it defaults to PIE
|
||||||
|
reloc_mode = LLVMRelocPIC;
|
||||||
|
}
|
||||||
|
|
||||||
for_array(i, gen->modules.entries) {
|
for_array(i, gen->modules.entries) {
|
||||||
target_machines[i] = LLVMCreateTargetMachine(
|
target_machines[i] = LLVMCreateTargetMachine(
|
||||||
target, target_triple, llvm_cpu,
|
target, target_triple, llvm_cpu,
|
||||||
|
|||||||
@@ -962,7 +962,7 @@ void lb_add_debug_local_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, T
|
|||||||
LLVMMetadataRef llvm_debug_loc = lb_debug_location_from_token_pos(p, token.pos);
|
LLVMMetadataRef llvm_debug_loc = lb_debug_location_from_token_pos(p, token.pos);
|
||||||
LLVMMetadataRef llvm_expr = LLVMDIBuilderCreateExpression(m->debug_builder, nullptr, 0);
|
LLVMMetadataRef llvm_expr = LLVMDIBuilderCreateExpression(m->debug_builder, nullptr, 0);
|
||||||
lb_set_llvm_metadata(m, ptr, llvm_expr);
|
lb_set_llvm_metadata(m, ptr, llvm_expr);
|
||||||
LLVMDIBuilderInsertDbgValueAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block);
|
LLVMDIBuilderInsertDeclareAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2202,6 +2202,21 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_type_matrix(a) && (op_kind == Token_CmpEq || op_kind == Token_NotEq)) {
|
||||||
|
Type *tl = base_type(a);
|
||||||
|
lbValue lhs = lb_address_from_load_or_generate_local(p, left);
|
||||||
|
lbValue rhs = lb_address_from_load_or_generate_local(p, right);
|
||||||
|
|
||||||
|
|
||||||
|
// TODO(bill): Test to see if this is actually faster!!!!
|
||||||
|
auto args = array_make<lbValue>(permanent_allocator(), 3);
|
||||||
|
args[0] = lb_emit_conv(p, lhs, t_rawptr);
|
||||||
|
args[1] = lb_emit_conv(p, rhs, t_rawptr);
|
||||||
|
args[2] = lb_const_int(p->module, t_int, type_size_of(tl));
|
||||||
|
lbValue val = lb_emit_runtime_call(p, "memory_compare", args);
|
||||||
|
lbValue res = lb_emit_comp(p, op_kind, val, lb_const_nil(p->module, val.type));
|
||||||
|
return lb_emit_conv(p, res, t_bool);
|
||||||
|
}
|
||||||
if (is_type_array(a) || is_type_enumerated_array(a)) {
|
if (is_type_array(a) || is_type_enumerated_array(a)) {
|
||||||
Type *tl = base_type(a);
|
Type *tl = base_type(a);
|
||||||
lbValue lhs = lb_address_from_load_or_generate_local(p, left);
|
lbValue lhs = lb_address_from_load_or_generate_local(p, left);
|
||||||
|
|||||||
+74
-10
@@ -57,6 +57,7 @@ void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbVal
|
|||||||
LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
|
LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) {
|
lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) {
|
||||||
GB_ASSERT(entity != nullptr);
|
GB_ASSERT(entity != nullptr);
|
||||||
GB_ASSERT(entity->kind == Entity_Procedure);
|
GB_ASSERT(entity->kind == Entity_Procedure);
|
||||||
@@ -163,14 +164,6 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// lbCallingConventionKind cc_kind = lbCallingConvention_C;
|
|
||||||
// // TODO(bill): Clean up this logic
|
|
||||||
// if (build_context.metrics.os != TargetOs_js) {
|
|
||||||
// cc_kind = lb_calling_convention_map[pt->Proc.calling_convention];
|
|
||||||
// }
|
|
||||||
// LLVMSetFunctionCallConv(p->value, cc_kind);
|
|
||||||
lbValue proc_value = {p->value, p->type};
|
lbValue proc_value = {p->value, p->type};
|
||||||
lb_add_entity(m, entity, proc_value);
|
lb_add_entity(m, entity, proc_value);
|
||||||
lb_add_member(m, p->name, proc_value);
|
lb_add_member(m, p->name, proc_value);
|
||||||
@@ -1049,7 +1042,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
|
|||||||
return lb_string_len(p, v);
|
return lb_string_len(p, v);
|
||||||
} else if (is_type_array(t)) {
|
} else if (is_type_array(t)) {
|
||||||
GB_PANIC("Array lengths are constant");
|
GB_PANIC("Array lengths are constant");
|
||||||
} else if (is_type_slice(t)) {
|
} else if (is_type_slice(t) || is_type_relative_slice(t)) {
|
||||||
return lb_slice_len(p, v);
|
return lb_slice_len(p, v);
|
||||||
} else if (is_type_dynamic_array(t)) {
|
} else if (is_type_dynamic_array(t)) {
|
||||||
return lb_dynamic_array_len(p, v);
|
return lb_dynamic_array_len(p, v);
|
||||||
@@ -1075,7 +1068,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
|
|||||||
GB_PANIC("Unreachable");
|
GB_PANIC("Unreachable");
|
||||||
} else if (is_type_array(t)) {
|
} else if (is_type_array(t)) {
|
||||||
GB_PANIC("Array lengths are constant");
|
GB_PANIC("Array lengths are constant");
|
||||||
} else if (is_type_slice(t)) {
|
} else if (is_type_slice(t) || is_type_relative_slice(t)) {
|
||||||
return lb_slice_len(p, v);
|
return lb_slice_len(p, v);
|
||||||
} else if (is_type_dynamic_array(t)) {
|
} else if (is_type_dynamic_array(t)) {
|
||||||
return lb_dynamic_array_cap(p, v);
|
return lb_dynamic_array_cap(p, v);
|
||||||
@@ -2122,6 +2115,77 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
|
|||||||
case BuiltinProc_objc_find_class: return lb_handle_objc_find_class(p, expr);
|
case BuiltinProc_objc_find_class: return lb_handle_objc_find_class(p, expr);
|
||||||
case BuiltinProc_objc_register_selector: return lb_handle_objc_register_selector(p, expr);
|
case BuiltinProc_objc_register_selector: return lb_handle_objc_register_selector(p, expr);
|
||||||
case BuiltinProc_objc_register_class: return lb_handle_objc_register_class(p, expr);
|
case BuiltinProc_objc_register_class: return lb_handle_objc_register_class(p, expr);
|
||||||
|
|
||||||
|
|
||||||
|
case BuiltinProc_constant_utf16_cstring:
|
||||||
|
{
|
||||||
|
auto const encode_surrogate_pair = [](Rune r, u16 *r1, u16 *r2) {
|
||||||
|
if (r < 0x10000 || r > 0x10ffff) {
|
||||||
|
*r1 = 0xfffd;
|
||||||
|
*r2 = 0xfffd;
|
||||||
|
} else {
|
||||||
|
r -= 0x10000;
|
||||||
|
*r1 = 0xd800 + ((r>>10)&0x3ff);
|
||||||
|
*r2 = 0xdc00 + (r&0x3ff);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
lbModule *m = p->module;
|
||||||
|
|
||||||
|
auto tav = type_and_value_of_expr(ce->args[0]);
|
||||||
|
GB_ASSERT(tav.value.kind == ExactValue_String);
|
||||||
|
String value = tav.value.value_string;
|
||||||
|
|
||||||
|
LLVMTypeRef llvm_u16 = lb_type(m, t_u16);
|
||||||
|
|
||||||
|
isize max_len = value.len*2 + 1;
|
||||||
|
LLVMValueRef *buffer = gb_alloc_array(temporary_allocator(), LLVMValueRef, max_len);
|
||||||
|
isize n = 0;
|
||||||
|
while (value.len > 0) {
|
||||||
|
Rune r = 0;
|
||||||
|
isize w = gb_utf8_decode(value.text, value.len, &r);
|
||||||
|
value.text += w;
|
||||||
|
value.len -= w;
|
||||||
|
if ((0 <= r && r < 0xd800) || (0xe000 <= r && r < 0x10000)) {
|
||||||
|
buffer[n++] = LLVMConstInt(llvm_u16, cast(u16)r, false);
|
||||||
|
} else if (0x10000 <= r && r <= 0x10ffff) {
|
||||||
|
u16 r1, r2;
|
||||||
|
encode_surrogate_pair(r, &r1, &r2);
|
||||||
|
buffer[n++] = LLVMConstInt(llvm_u16, r1, false);
|
||||||
|
buffer[n++] = LLVMConstInt(llvm_u16, r2, false);
|
||||||
|
} else {
|
||||||
|
buffer[n++] = LLVMConstInt(llvm_u16, 0xfffd, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[n++] = LLVMConstInt(llvm_u16, 0, false);
|
||||||
|
|
||||||
|
LLVMValueRef array = LLVMConstArray(llvm_u16, buffer, cast(unsigned int)n);
|
||||||
|
|
||||||
|
char *name = nullptr;
|
||||||
|
{
|
||||||
|
isize max_len = 7+8+1;
|
||||||
|
name = gb_alloc_array(permanent_allocator(), char, max_len);
|
||||||
|
u32 id = m->gen->global_array_index.fetch_add(1);
|
||||||
|
isize len = gb_snprintf(name, max_len, "csbs$%x", id);
|
||||||
|
len -= 1;
|
||||||
|
}
|
||||||
|
LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(array), name);
|
||||||
|
LLVMSetInitializer(global_data, array);
|
||||||
|
LLVMSetLinkage(global_data, LLVMInternalLinkage);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
LLVMValueRef indices[] = {
|
||||||
|
LLVMConstInt(lb_type(m, t_u32), 0, false),
|
||||||
|
LLVMConstInt(lb_type(m, t_u32), 0, false),
|
||||||
|
};
|
||||||
|
lbValue res = {};
|
||||||
|
res.type = tv.type;
|
||||||
|
res.value = LLVMBuildInBoundsGEP(p->builder, global_data, indices, gb_count_of(indices), "");
|
||||||
|
return res;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name));
|
GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name));
|
||||||
|
|||||||
@@ -1373,7 +1373,7 @@ lbValue lb_slice_elem(lbProcedure *p, lbValue slice) {
|
|||||||
return lb_emit_struct_ev(p, slice, 0);
|
return lb_emit_struct_ev(p, slice, 0);
|
||||||
}
|
}
|
||||||
lbValue lb_slice_len(lbProcedure *p, lbValue slice) {
|
lbValue lb_slice_len(lbProcedure *p, lbValue slice) {
|
||||||
GB_ASSERT(is_type_slice(slice.type));
|
GB_ASSERT(is_type_slice(slice.type) || is_type_relative_slice(slice.type));
|
||||||
return lb_emit_struct_ev(p, slice, 1);
|
return lb_emit_struct_ev(p, slice, 1);
|
||||||
}
|
}
|
||||||
lbValue lb_dynamic_array_elem(lbProcedure *p, lbValue da) {
|
lbValue lb_dynamic_array_elem(lbProcedure *p, lbValue da) {
|
||||||
|
|||||||
+54
-46
@@ -384,7 +384,7 @@ i32 linker_stage(lbGenerator *gen) {
|
|||||||
// NOTE(zangent): Sometimes, you have to use -framework on MacOS.
|
// NOTE(zangent): Sometimes, you have to use -framework on MacOS.
|
||||||
// This allows you to specify '-f' in a #foreign_system_library,
|
// This allows you to specify '-f' in a #foreign_system_library,
|
||||||
// without having to implement any new syntax specifically for MacOS.
|
// without having to implement any new syntax specifically for MacOS.
|
||||||
#if defined(GB_SYSTEM_OSX)
|
if (build_context.metrics.os == TargetOs_darwin) {
|
||||||
if (string_ends_with(lib, str_lit(".framework"))) {
|
if (string_ends_with(lib, str_lit(".framework"))) {
|
||||||
// framework thingie
|
// framework thingie
|
||||||
String lib_name = lib;
|
String lib_name = lib;
|
||||||
@@ -400,14 +400,14 @@ i32 linker_stage(lbGenerator *gen) {
|
|||||||
// dynamic or static system lib, just link regularly searching system library paths
|
// dynamic or static system lib, just link regularly searching system library paths
|
||||||
lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
|
lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
|
||||||
}
|
}
|
||||||
#else
|
} else {
|
||||||
// NOTE(vassvik): static libraries (.a files) in linux can be linked to directly using the full path,
|
// NOTE(vassvik): static libraries (.a files) in linux can be linked to directly using the full path,
|
||||||
// since those are statically linked to at link time. shared libraries (.so) has to be
|
// since those are statically linked to at link time. shared libraries (.so) has to be
|
||||||
// available at runtime wherever the executable is run, so we make require those to be
|
// available at runtime wherever the executable is run, so we make require those to be
|
||||||
// local to the executable (unless the system collection is used, in which case we search
|
// local to the executable (unless the system collection is used, in which case we search
|
||||||
// the system library paths for the library file).
|
// the system library paths for the library file).
|
||||||
if (string_ends_with(lib, str_lit(".a"))) {
|
if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o"))) {
|
||||||
// static libs, absolute full path relative to the file in which the lib was imported from
|
// static libs and object files, absolute full path relative to the file in which the lib was imported from
|
||||||
lib_str = gb_string_append_fmt(lib_str, " -l:\"%.*s\" ", LIT(lib));
|
lib_str = gb_string_append_fmt(lib_str, " -l:\"%.*s\" ", LIT(lib));
|
||||||
} else if (string_ends_with(lib, str_lit(".so"))) {
|
} else if (string_ends_with(lib, str_lit(".so"))) {
|
||||||
// dynamic lib, relative path to executable
|
// dynamic lib, relative path to executable
|
||||||
@@ -418,7 +418,7 @@ i32 linker_stage(lbGenerator *gen) {
|
|||||||
// dynamic or static system lib, just link regularly searching system library paths
|
// dynamic or static system lib, just link regularly searching system library paths
|
||||||
lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
|
lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
|
||||||
}
|
}
|
||||||
#endif
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gbString object_files = gb_string_make(heap_allocator(), "");
|
gbString object_files = gb_string_make(heap_allocator(), "");
|
||||||
@@ -456,14 +456,15 @@ i32 linker_stage(lbGenerator *gen) {
|
|||||||
// line arguments prepared previously are incompatible with ld.
|
// line arguments prepared previously are incompatible with ld.
|
||||||
//
|
//
|
||||||
// Shared libraries are .dylib on MacOS and .so on Linux.
|
// Shared libraries are .dylib on MacOS and .so on Linux.
|
||||||
#if defined(GB_SYSTEM_OSX)
|
if (build_context.metrics.os == TargetOs_darwin) {
|
||||||
output_ext = STR_LIT(".dylib");
|
output_ext = STR_LIT(".dylib");
|
||||||
#else
|
} else {
|
||||||
output_ext = STR_LIT(".so");
|
output_ext = STR_LIT(".so");
|
||||||
#endif
|
}
|
||||||
link_settings = gb_string_appendc(link_settings, "-Wl,-init,'_odin_entry_point' ");
|
link_settings = gb_string_appendc(link_settings, "-Wl,-init,'_odin_entry_point' ");
|
||||||
link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' ");
|
link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' ");
|
||||||
} else {
|
} else if (build_context.metrics.os != TargetOs_openbsd) {
|
||||||
|
// OpenBSD defaults to PIE executable. do not pass -no-pie for it.
|
||||||
link_settings = gb_string_appendc(link_settings, "-no-pie ");
|
link_settings = gb_string_appendc(link_settings, "-no-pie ");
|
||||||
}
|
}
|
||||||
if (build_context.out_filepath.len > 0) {
|
if (build_context.out_filepath.len > 0) {
|
||||||
@@ -474,34 +475,39 @@ i32 linker_stage(lbGenerator *gen) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result = system_exec_command_line_app("ld-link",
|
gbString platform_lib_str = gb_string_make(heap_allocator(), "");
|
||||||
"clang -Wno-unused-command-line-argument %s -o \"%.*s%.*s\" %s "
|
defer (gb_string_free(platform_lib_str));
|
||||||
" %s "
|
if (build_context.metrics.os == TargetOs_darwin) {
|
||||||
" %.*s "
|
platform_lib_str = gb_string_appendc(platform_lib_str, "-lSystem -lm -Wl,-syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib");
|
||||||
" %.*s "
|
} else {
|
||||||
" %s "
|
platform_lib_str = gb_string_appendc(platform_lib_str, "-lc -lm");
|
||||||
#if defined(GB_SYSTEM_OSX)
|
}
|
||||||
// This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
|
|
||||||
// NOTE: If you change this (although this minimum is as low as you can go with Odin working)
|
if (build_context.metrics.os == TargetOs_darwin) {
|
||||||
// make sure to also change the 'mtriple' param passed to 'opt'
|
// This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
|
||||||
#if defined(GB_CPU_ARM)
|
// NOTE: If you change this (although this minimum is as low as you can go with Odin working)
|
||||||
" -mmacosx-version-min=12.0.0 "
|
// make sure to also change the 'mtriple' param passed to 'opt'
|
||||||
#else
|
if (build_context.metrics.arch == TargetArch_arm64) {
|
||||||
" -mmacosx-version-min=10.8.0 "
|
link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=12.0.0 ");
|
||||||
#endif
|
} else {
|
||||||
// This points the linker to where the entry point is
|
link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=10.8.0 ");
|
||||||
" -e _main "
|
}
|
||||||
#endif
|
// This points the linker to where the entry point is
|
||||||
, object_files, LIT(output_base), LIT(output_ext),
|
link_settings = gb_string_appendc(link_settings, " -e _main ");
|
||||||
#if defined(GB_SYSTEM_OSX)
|
}
|
||||||
"-lSystem -lm -Wl,-syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib",
|
|
||||||
#else
|
gbString link_command_line = gb_string_make(heap_allocator(), "clang -Wno-unused-command-line-argument ");
|
||||||
"-lc -lm",
|
defer (gb_string_free(link_command_line));
|
||||||
#endif
|
|
||||||
lib_str,
|
link_command_line = gb_string_appendc(link_command_line, object_files);
|
||||||
LIT(build_context.link_flags),
|
link_command_line = gb_string_append_fmt(link_command_line, " -o \"%.*s%.*s\" ", LIT(output_base), LIT(output_ext));
|
||||||
LIT(build_context.extra_linker_flags),
|
link_command_line = gb_string_append_fmt(link_command_line, " %s ", platform_lib_str);
|
||||||
link_settings);
|
link_command_line = gb_string_append_fmt(link_command_line, " %s ", lib_str);
|
||||||
|
link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.link_flags));
|
||||||
|
link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.extra_linker_flags));
|
||||||
|
link_command_line = gb_string_append_fmt(link_command_line, " %s ", link_settings);
|
||||||
|
|
||||||
|
result = system_exec_command_line_app("ld-link", link_command_line);
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
return result;
|
return result;
|
||||||
@@ -572,14 +578,16 @@ void usage(String argv0) {
|
|||||||
print_usage_line(0, "Usage:");
|
print_usage_line(0, "Usage:");
|
||||||
print_usage_line(1, "%.*s command [arguments]", LIT(argv0));
|
print_usage_line(1, "%.*s command [arguments]", LIT(argv0));
|
||||||
print_usage_line(0, "Commands:");
|
print_usage_line(0, "Commands:");
|
||||||
print_usage_line(1, "build compile .odin file, or directory of .odin files, as an executable.");
|
print_usage_line(1, "build compile .odin file, or directory of .odin files, as an executable.");
|
||||||
print_usage_line(1, " one must contain the program's entry point, all must be in the same package.");
|
print_usage_line(1, " one must contain the program's entry point, all must be in the same package.");
|
||||||
print_usage_line(1, "run same as 'build', but also then runs the newly compiled executable.");
|
print_usage_line(1, "run same as 'build', but also then runs the newly compiled executable.");
|
||||||
print_usage_line(1, "check parse and type check .odin file");
|
print_usage_line(1, "check parse, and type check an .odin file, or directory of .odin files");
|
||||||
print_usage_line(1, "query parse, type check, and output a .json file containing information about the program");
|
print_usage_line(1, "query parse, type check, and output a .json file containing information about the program");
|
||||||
print_usage_line(1, "doc generate documentation .odin file, or directory of .odin files");
|
print_usage_line(1, "strip-semicolon parse, type check, and remove unneeded semicolons from the entire program");
|
||||||
print_usage_line(1, "version print version");
|
print_usage_line(1, "test build ands runs procedures with the attribute @(test) in the initial package");
|
||||||
print_usage_line(1, "report print information useful to reporting a bug");
|
print_usage_line(1, "doc generate documentation .odin file, or directory of .odin files");
|
||||||
|
print_usage_line(1, "version print version");
|
||||||
|
print_usage_line(1, "report print information useful to reporting a bug");
|
||||||
print_usage_line(0, "");
|
print_usage_line(0, "");
|
||||||
print_usage_line(0, "For further details on a command, use -help after the command name");
|
print_usage_line(0, "For further details on a command, use -help after the command name");
|
||||||
print_usage_line(1, "e.g. odin build -help");
|
print_usage_line(1, "e.g. odin build -help");
|
||||||
@@ -2577,7 +2585,7 @@ int main(int arg_count, char const **arg_ptr) {
|
|||||||
// NOTE(bill): add 'shared' directory if it is not already set
|
// NOTE(bill): add 'shared' directory if it is not already set
|
||||||
if (!find_library_collection_path(str_lit("shared"), nullptr)) {
|
if (!find_library_collection_path(str_lit("shared"), nullptr)) {
|
||||||
add_library_collection(str_lit("shared"),
|
add_library_collection(str_lit("shared"),
|
||||||
get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("shared")));
|
get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("shared")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+73
-56
@@ -57,6 +57,9 @@ isize ast_node_size(AstKind kind) {
|
|||||||
return align_formula_isize(gb_size_of(AstCommonStuff) + ast_variant_sizes[kind], gb_align_of(void *));
|
return align_formula_isize(gb_size_of(AstCommonStuff) + ast_variant_sizes[kind], gb_align_of(void *));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gb_global std::atomic<isize> global_total_node_memory_allocated;
|
||||||
|
|
||||||
// NOTE(bill): And this below is why is I/we need a new language! Discriminated unions are a pain in C/C++
|
// NOTE(bill): And this below is why is I/we need a new language! Discriminated unions are a pain in C/C++
|
||||||
Ast *alloc_ast_node(AstFile *f, AstKind kind) {
|
Ast *alloc_ast_node(AstFile *f, AstKind kind) {
|
||||||
gbAllocator a = ast_allocator(f);
|
gbAllocator a = ast_allocator(f);
|
||||||
@@ -66,6 +69,9 @@ Ast *alloc_ast_node(AstFile *f, AstKind kind) {
|
|||||||
Ast *node = cast(Ast *)gb_alloc(a, size);
|
Ast *node = cast(Ast *)gb_alloc(a, size);
|
||||||
node->kind = kind;
|
node->kind = kind;
|
||||||
node->file_id = f ? f->id : 0;
|
node->file_id = f ? f->id : 0;
|
||||||
|
|
||||||
|
global_total_node_memory_allocated += size;
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1532,7 +1538,7 @@ void fix_advance_to_next_stmt(AstFile *f) {
|
|||||||
Token expect_closing(AstFile *f, TokenKind kind, String context) {
|
Token expect_closing(AstFile *f, TokenKind kind, String context) {
|
||||||
if (f->curr_token.kind != kind &&
|
if (f->curr_token.kind != kind &&
|
||||||
f->curr_token.kind == Token_Semicolon &&
|
f->curr_token.kind == Token_Semicolon &&
|
||||||
f->curr_token.string == "\n") {
|
(f->curr_token.string == "\n" || f->curr_token.kind == Token_EOF)) {
|
||||||
Token tok = f->prev_token;
|
Token tok = f->prev_token;
|
||||||
tok.pos.column += cast(i32)tok.string.len;
|
tok.pos.column += cast(i32)tok.string.len;
|
||||||
syntax_error(tok, "Missing ',' before newline in %.*s", LIT(context));
|
syntax_error(tok, "Missing ',' before newline in %.*s", LIT(context));
|
||||||
@@ -1554,6 +1560,7 @@ void assign_removal_flag_to_semicolon(AstFile *f) {
|
|||||||
switch (curr_token->kind) {
|
switch (curr_token->kind) {
|
||||||
case Token_CloseBrace:
|
case Token_CloseBrace:
|
||||||
case Token_CloseParen:
|
case Token_CloseParen:
|
||||||
|
case Token_EOF:
|
||||||
ok = true;
|
ok = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -3009,64 +3016,62 @@ i32 token_precedence(AstFile *f, TokenKind t) {
|
|||||||
|
|
||||||
Ast *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) {
|
Ast *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) {
|
||||||
Ast *expr = parse_unary_expr(f, lhs);
|
Ast *expr = parse_unary_expr(f, lhs);
|
||||||
for (i32 prec = token_precedence(f, f->curr_token.kind); prec >= prec_in; prec--) {
|
for (;;) {
|
||||||
for (;;) {
|
Token op = f->curr_token;
|
||||||
Token op = f->curr_token;
|
i32 op_prec = token_precedence(f, op.kind);
|
||||||
i32 op_prec = token_precedence(f, op.kind);
|
if (op_prec < prec_in) {
|
||||||
if (op_prec != prec) {
|
// NOTE(bill): This will also catch operators that are not valid "binary" operators
|
||||||
// NOTE(bill): This will also catch operators that are not valid "binary" operators
|
break;
|
||||||
break;
|
}
|
||||||
|
Token prev = f->prev_token;
|
||||||
|
switch (op.kind) {
|
||||||
|
case Token_if:
|
||||||
|
case Token_when:
|
||||||
|
if (prev.pos.line < op.pos.line) {
|
||||||
|
// NOTE(bill): Check to see if the `if` or `when` is on the same line of the `lhs` condition
|
||||||
|
goto loop_end;
|
||||||
}
|
}
|
||||||
Token prev = f->prev_token;
|
break;
|
||||||
|
}
|
||||||
|
expect_operator(f); // NOTE(bill): error checks too
|
||||||
|
|
||||||
|
if (op.kind == Token_Question) {
|
||||||
|
Ast *cond = expr;
|
||||||
|
// Token_Question
|
||||||
|
Ast *x = parse_expr(f, lhs);
|
||||||
|
Token token_c = expect_token(f, Token_Colon);
|
||||||
|
Ast *y = parse_expr(f, lhs);
|
||||||
|
expr = ast_ternary_if_expr(f, x, cond, y);
|
||||||
|
} else if (op.kind == Token_if || op.kind == Token_when) {
|
||||||
|
Ast *x = expr;
|
||||||
|
Ast *cond = parse_expr(f, lhs);
|
||||||
|
Token tok_else = expect_token(f, Token_else);
|
||||||
|
Ast *y = parse_expr(f, lhs);
|
||||||
|
|
||||||
switch (op.kind) {
|
switch (op.kind) {
|
||||||
case Token_if:
|
case Token_if:
|
||||||
|
expr = ast_ternary_if_expr(f, x, cond, y);
|
||||||
|
break;
|
||||||
case Token_when:
|
case Token_when:
|
||||||
if (prev.pos.line < op.pos.line) {
|
expr = ast_ternary_when_expr(f, x, cond, y);
|
||||||
// NOTE(bill): Check to see if the `if` or `when` is on the same line of the `lhs` condition
|
|
||||||
goto loop_end;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
expect_operator(f); // NOTE(bill): error checks too
|
} else {
|
||||||
|
Ast *right = parse_binary_expr(f, false, op_prec+1);
|
||||||
if (op.kind == Token_Question) {
|
if (right == nullptr) {
|
||||||
Ast *cond = expr;
|
syntax_error(op, "Expected expression on the right-hand side of the binary operator '%.*s'", LIT(op.string));
|
||||||
// Token_Question
|
}
|
||||||
Ast *x = parse_expr(f, lhs);
|
if (op.kind == Token_or_else) {
|
||||||
Token token_c = expect_token(f, Token_Colon);
|
// NOTE(bill): easier to handle its logic different with its own AST kind
|
||||||
Ast *y = parse_expr(f, lhs);
|
expr = ast_or_else_expr(f, expr, op, right);
|
||||||
expr = ast_ternary_if_expr(f, x, cond, y);
|
|
||||||
} else if (op.kind == Token_if || op.kind == Token_when) {
|
|
||||||
Ast *x = expr;
|
|
||||||
Ast *cond = parse_expr(f, lhs);
|
|
||||||
Token tok_else = expect_token(f, Token_else);
|
|
||||||
Ast *y = parse_expr(f, lhs);
|
|
||||||
|
|
||||||
switch (op.kind) {
|
|
||||||
case Token_if:
|
|
||||||
expr = ast_ternary_if_expr(f, x, cond, y);
|
|
||||||
break;
|
|
||||||
case Token_when:
|
|
||||||
expr = ast_ternary_when_expr(f, x, cond, y);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Ast *right = parse_binary_expr(f, false, prec+1);
|
expr = ast_binary_expr(f, op, expr, right);
|
||||||
if (right == nullptr) {
|
|
||||||
syntax_error(op, "Expected expression on the right-hand side of the binary operator '%.*s'", LIT(op.string));
|
|
||||||
}
|
|
||||||
if (op.kind == Token_or_else) {
|
|
||||||
// NOTE(bill): easier to handle its logic different with its own AST kind
|
|
||||||
expr = ast_or_else_expr(f, expr, op, right);
|
|
||||||
} else {
|
|
||||||
expr = ast_binary_expr(f, op, expr, right);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lhs = false;
|
|
||||||
}
|
}
|
||||||
loop_end:;
|
|
||||||
|
lhs = false;
|
||||||
}
|
}
|
||||||
|
loop_end:;
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3510,12 +3515,13 @@ enum FieldPrefixKind : i32 {
|
|||||||
FieldPrefix_Unknown = -1,
|
FieldPrefix_Unknown = -1,
|
||||||
FieldPrefix_Invalid = 0,
|
FieldPrefix_Invalid = 0,
|
||||||
|
|
||||||
FieldPrefix_using,
|
FieldPrefix_using, // implies #subtype
|
||||||
FieldPrefix_const,
|
FieldPrefix_const,
|
||||||
FieldPrefix_no_alias,
|
FieldPrefix_no_alias,
|
||||||
FieldPrefix_c_vararg,
|
FieldPrefix_c_vararg,
|
||||||
FieldPrefix_auto_cast,
|
FieldPrefix_auto_cast,
|
||||||
FieldPrefix_any_int,
|
FieldPrefix_any_int,
|
||||||
|
FieldPrefix_subtype, // does not imply `using` semantics
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ParseFieldPrefixMapping {
|
struct ParseFieldPrefixMapping {
|
||||||
@@ -3532,6 +3538,7 @@ gb_global ParseFieldPrefixMapping parse_field_prefix_mappings[] = {
|
|||||||
{str_lit("c_vararg"), Token_Hash, FieldPrefix_c_vararg, FieldFlag_c_vararg},
|
{str_lit("c_vararg"), Token_Hash, FieldPrefix_c_vararg, FieldFlag_c_vararg},
|
||||||
{str_lit("const"), Token_Hash, FieldPrefix_const, FieldFlag_const},
|
{str_lit("const"), Token_Hash, FieldPrefix_const, FieldFlag_const},
|
||||||
{str_lit("any_int"), Token_Hash, FieldPrefix_any_int, FieldFlag_any_int},
|
{str_lit("any_int"), Token_Hash, FieldPrefix_any_int, FieldFlag_any_int},
|
||||||
|
{str_lit("subtype"), Token_Hash, FieldPrefix_subtype, FieldFlag_subtype},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -4849,12 +4856,6 @@ ParseFileError init_ast_file(AstFile *f, String fullpath, TokenPos *err_pos) {
|
|||||||
f->prev_token = f->tokens[f->prev_token_index];
|
f->prev_token = f->tokens[f->prev_token_index];
|
||||||
f->curr_token = f->tokens[f->curr_token_index];
|
f->curr_token = f->tokens[f->curr_token_index];
|
||||||
|
|
||||||
isize const page_size = 4*1024;
|
|
||||||
isize block_size = 2*f->tokens.count*gb_size_of(Ast);
|
|
||||||
block_size = ((block_size + page_size-1)/page_size) * page_size;
|
|
||||||
block_size = gb_clamp(block_size, page_size, DEFAULT_MINIMUM_BLOCK_SIZE);
|
|
||||||
f->arena.minimum_block_size = block_size;
|
|
||||||
|
|
||||||
array_init(&f->comments, heap_allocator(), 0, 0);
|
array_init(&f->comments, heap_allocator(), 0, 0);
|
||||||
array_init(&f->imports, heap_allocator(), 0, 0);
|
array_init(&f->imports, heap_allocator(), 0, 0);
|
||||||
|
|
||||||
@@ -5719,6 +5720,22 @@ ParseFileError parse_packages(Parser *p, String init_filename) {
|
|||||||
error_line("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename));
|
error_line("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename));
|
||||||
return ParseFile_WrongExtension;
|
return ParseFile_WrongExtension;
|
||||||
}
|
}
|
||||||
|
} else if (init_fullpath.len != 0) {
|
||||||
|
String path = init_fullpath;
|
||||||
|
if (path[path.len-1] == '/') {
|
||||||
|
path.len -= 1;
|
||||||
|
}
|
||||||
|
if ((build_context.command_kind & Command__does_build) &&
|
||||||
|
build_context.build_mode == BuildMode_Executable) {
|
||||||
|
String short_path = filename_from_path(path);
|
||||||
|
char *cpath = alloc_cstring(heap_allocator(), short_path);
|
||||||
|
defer (gb_free(heap_allocator(), cpath));
|
||||||
|
|
||||||
|
if (gb_file_exists(cpath)) {
|
||||||
|
error_line("Please specify the executable name with -out:<string> as a directory exists with the same name in the current working directory");
|
||||||
|
return ParseFile_DirectoryAlreadyExists;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+5
-5
@@ -46,6 +46,7 @@ enum ParseFileError {
|
|||||||
ParseFile_InvalidToken,
|
ParseFile_InvalidToken,
|
||||||
ParseFile_GeneralError,
|
ParseFile_GeneralError,
|
||||||
ParseFile_FileTooLarge,
|
ParseFile_FileTooLarge,
|
||||||
|
ParseFile_DirectoryAlreadyExists,
|
||||||
|
|
||||||
ParseFile_Count,
|
ParseFile_Count,
|
||||||
};
|
};
|
||||||
@@ -97,8 +98,6 @@ struct AstFile {
|
|||||||
AstPackage * pkg;
|
AstPackage * pkg;
|
||||||
Scope * scope;
|
Scope * scope;
|
||||||
|
|
||||||
Arena arena;
|
|
||||||
|
|
||||||
Ast * pkg_decl;
|
Ast * pkg_decl;
|
||||||
String fullpath;
|
String fullpath;
|
||||||
Tokenizer tokenizer;
|
Tokenizer tokenizer;
|
||||||
@@ -300,6 +299,7 @@ enum FieldFlag : u32 {
|
|||||||
FieldFlag_auto_cast = 1<<4,
|
FieldFlag_auto_cast = 1<<4,
|
||||||
FieldFlag_const = 1<<5,
|
FieldFlag_const = 1<<5,
|
||||||
FieldFlag_any_int = 1<<6,
|
FieldFlag_any_int = 1<<6,
|
||||||
|
FieldFlag_subtype = 1<<7,
|
||||||
|
|
||||||
// Internal use by the parser only
|
// Internal use by the parser only
|
||||||
FieldFlag_Tags = 1<<10,
|
FieldFlag_Tags = 1<<10,
|
||||||
@@ -307,7 +307,7 @@ enum FieldFlag : u32 {
|
|||||||
|
|
||||||
// Parameter List Restrictions
|
// Parameter List Restrictions
|
||||||
FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_auto_cast|FieldFlag_const|FieldFlag_any_int,
|
FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_auto_cast|FieldFlag_const|FieldFlag_any_int,
|
||||||
FieldFlag_Struct = FieldFlag_using|FieldFlag_Tags,
|
FieldFlag_Struct = FieldFlag_using|FieldFlag_subtype|FieldFlag_Tags,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum StmtAllowFlag {
|
enum StmtAllowFlag {
|
||||||
@@ -800,10 +800,10 @@ gb_inline bool is_ast_when_stmt(Ast *node) {
|
|||||||
return node->kind == Ast_WhenStmt;
|
return node->kind == Ast_WhenStmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
gb_global gb_thread_local Arena global_ast_arena = {};
|
gb_global gb_thread_local Arena global_thread_local_ast_arena = {};
|
||||||
|
|
||||||
gbAllocator ast_allocator(AstFile *f) {
|
gbAllocator ast_allocator(AstFile *f) {
|
||||||
Arena *arena = f ? &f->arena : &global_ast_arena;
|
Arena *arena = &global_thread_local_ast_arena;
|
||||||
return arena_allocator(arena);
|
return arena_allocator(arena);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -486,7 +486,7 @@ void thread_set_name(Thread *t, char const *name) {
|
|||||||
#elif defined(GB_SYSTEM_OSX)
|
#elif defined(GB_SYSTEM_OSX)
|
||||||
// TODO(bill): Test if this works
|
// TODO(bill): Test if this works
|
||||||
pthread_setname_np(name);
|
pthread_setname_np(name);
|
||||||
#elif defined(GB_SYSTEM_FREEBSD)
|
#elif defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
|
||||||
pthread_set_name_np(t->posix_handle, name);
|
pthread_set_name_np(t->posix_handle, name);
|
||||||
#else
|
#else
|
||||||
// TODO(bill): Test if this works
|
// TODO(bill): Test if this works
|
||||||
|
|||||||
+41
-21
@@ -703,7 +703,7 @@ struct TypePath;
|
|||||||
i64 type_size_of (Type *t);
|
i64 type_size_of (Type *t);
|
||||||
i64 type_align_of (Type *t);
|
i64 type_align_of (Type *t);
|
||||||
i64 type_offset_of (Type *t, i32 index);
|
i64 type_offset_of (Type *t, i32 index);
|
||||||
gbString type_to_string (Type *type);
|
gbString type_to_string (Type *type, bool shorthand=false);
|
||||||
i64 type_size_of_internal(Type *t, TypePath *path);
|
i64 type_size_of_internal(Type *t, TypePath *path);
|
||||||
void init_map_internal_types(Type *type);
|
void init_map_internal_types(Type *type);
|
||||||
Type * bit_set_to_int(Type *t);
|
Type * bit_set_to_int(Type *t);
|
||||||
@@ -2334,7 +2334,7 @@ String lookup_subtype_polymorphic_field(Type *dst, Type *src) {
|
|||||||
GB_ASSERT(is_type_struct(src) || is_type_union(src));
|
GB_ASSERT(is_type_struct(src) || is_type_union(src));
|
||||||
for_array(i, src->Struct.fields) {
|
for_array(i, src->Struct.fields) {
|
||||||
Entity *f = src->Struct.fields[i];
|
Entity *f = src->Struct.fields[i];
|
||||||
if (f->kind == Entity_Variable && f->flags & EntityFlag_Using) {
|
if (f->kind == Entity_Variable && f->flags & EntityFlags_IsSubtype) {
|
||||||
if (are_types_identical(dst, f->type)) {
|
if (are_types_identical(dst, f->type)) {
|
||||||
return f->token.string;
|
return f->token.string;
|
||||||
}
|
}
|
||||||
@@ -2343,7 +2343,7 @@ String lookup_subtype_polymorphic_field(Type *dst, Type *src) {
|
|||||||
return f->token.string;
|
return f->token.string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (is_type_struct(f->type)) {
|
if ((f->flags & EntityFlag_Using) != 0 && is_type_struct(f->type)) {
|
||||||
String name = lookup_subtype_polymorphic_field(dst, f->type);
|
String name = lookup_subtype_polymorphic_field(dst, f->type);
|
||||||
if (name.len > 0) {
|
if (name.len > 0) {
|
||||||
return name;
|
return name;
|
||||||
@@ -2489,9 +2489,9 @@ bool are_types_identical_internal(Type *x, Type *y, bool check_tuple_names) {
|
|||||||
if (xf->token.string != yf->token.string) {
|
if (xf->token.string != yf->token.string) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool xf_is_using = (xf->flags&EntityFlag_Using) != 0;
|
u64 xf_flags = (xf->flags&EntityFlags_IsSubtype);
|
||||||
bool yf_is_using = (yf->flags&EntityFlag_Using) != 0;
|
u64 yf_flags = (yf->flags&EntityFlags_IsSubtype);
|
||||||
if (xf_is_using ^ yf_is_using) {
|
if (xf_flags != yf_flags) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2623,6 +2623,17 @@ i64 union_tag_size(Type *u) {
|
|||||||
|
|
||||||
// TODO(bill): Is this an okay approach?
|
// TODO(bill): Is this an okay approach?
|
||||||
i64 max_align = 1;
|
i64 max_align = 1;
|
||||||
|
|
||||||
|
if (u->Union.variants.count < 1ull<<8) {
|
||||||
|
max_align = 1;
|
||||||
|
} else if (u->Union.variants.count < 1ull<<16) {
|
||||||
|
max_align = 2;
|
||||||
|
} else if (u->Union.variants.count < 1ull<<32) {
|
||||||
|
max_align = 4;
|
||||||
|
} else {
|
||||||
|
GB_PANIC("how many variants do you have?!");
|
||||||
|
}
|
||||||
|
|
||||||
for_array(i, u->Union.variants) {
|
for_array(i, u->Union.variants) {
|
||||||
Type *variant_type = u->Union.variants[i];
|
Type *variant_type = u->Union.variants[i];
|
||||||
i64 align = type_align_of(variant_type);
|
i64 align = type_align_of(variant_type);
|
||||||
@@ -3813,7 +3824,7 @@ isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isize level = 0
|
|||||||
|
|
||||||
for_array(i, src->Struct.fields) {
|
for_array(i, src->Struct.fields) {
|
||||||
Entity *f = src->Struct.fields[i];
|
Entity *f = src->Struct.fields[i];
|
||||||
if (f->kind != Entity_Variable || (f->flags&EntityFlag_Using) == 0) {
|
if (f->kind != Entity_Variable || (f->flags&EntityFlags_IsSubtype) == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3925,7 +3936,7 @@ Type *alloc_type_proc_from_types(Type **param_types, unsigned param_count, Type
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
gbString write_type_to_string(gbString str, Type *type) {
|
gbString write_type_to_string(gbString str, Type *type, bool shorthand=false) {
|
||||||
if (type == nullptr) {
|
if (type == nullptr) {
|
||||||
return gb_string_appendc(str, "<no type>");
|
return gb_string_appendc(str, "<no type>");
|
||||||
}
|
}
|
||||||
@@ -4040,15 +4051,21 @@ gbString write_type_to_string(gbString str, Type *type) {
|
|||||||
if (type->Struct.is_raw_union) str = gb_string_appendc(str, " #raw_union");
|
if (type->Struct.is_raw_union) str = gb_string_appendc(str, " #raw_union");
|
||||||
if (type->Struct.custom_align != 0) str = gb_string_append_fmt(str, " #align %d", cast(int)type->Struct.custom_align);
|
if (type->Struct.custom_align != 0) str = gb_string_append_fmt(str, " #align %d", cast(int)type->Struct.custom_align);
|
||||||
str = gb_string_appendc(str, " {");
|
str = gb_string_appendc(str, " {");
|
||||||
for_array(i, type->Struct.fields) {
|
|
||||||
Entity *f = type->Struct.fields[i];
|
|
||||||
GB_ASSERT(f->kind == Entity_Variable);
|
if (shorthand && type->Struct.fields.count > 16) {
|
||||||
if (i > 0) {
|
str = gb_string_append_fmt(str, "%lld fields...", cast(long long)type->Struct.fields.count);
|
||||||
str = gb_string_appendc(str, ", ");
|
} else {
|
||||||
|
for_array(i, type->Struct.fields) {
|
||||||
|
Entity *f = type->Struct.fields[i];
|
||||||
|
GB_ASSERT(f->kind == Entity_Variable);
|
||||||
|
if (i > 0) {
|
||||||
|
str = gb_string_appendc(str, ", ");
|
||||||
|
}
|
||||||
|
str = gb_string_append_length(str, f->token.string.text, f->token.string.len);
|
||||||
|
str = gb_string_appendc(str, ": ");
|
||||||
|
str = write_type_to_string(str, f->type);
|
||||||
}
|
}
|
||||||
str = gb_string_append_length(str, f->token.string.text, f->token.string.len);
|
|
||||||
str = gb_string_appendc(str, ": ");
|
|
||||||
str = write_type_to_string(str, f->type);
|
|
||||||
}
|
}
|
||||||
str = gb_string_append_rune(str, '}');
|
str = gb_string_append_rune(str, '}');
|
||||||
} break;
|
} break;
|
||||||
@@ -4223,13 +4240,16 @@ gbString write_type_to_string(gbString str, Type *type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
gbString type_to_string(Type *type, gbAllocator allocator) {
|
gbString type_to_string(Type *type, gbAllocator allocator, bool shorthand=false) {
|
||||||
return write_type_to_string(gb_string_make(allocator, ""), type);
|
return write_type_to_string(gb_string_make(allocator, ""), type, shorthand);
|
||||||
}
|
}
|
||||||
gbString type_to_string(Type *type) {
|
gbString type_to_string(Type *type, bool shorthand) {
|
||||||
return write_type_to_string(gb_string_make(heap_allocator(), ""), type);
|
return write_type_to_string(gb_string_make(heap_allocator(), ""), type, shorthand);
|
||||||
|
}
|
||||||
|
|
||||||
|
gbString type_to_string_shorthand(Type *type) {
|
||||||
|
return type_to_string(type, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+5
-1
@@ -1,7 +1,7 @@
|
|||||||
ODIN=../../odin
|
ODIN=../../odin
|
||||||
PYTHON=$(shell which python3)
|
PYTHON=$(shell which python3)
|
||||||
|
|
||||||
all: download_test_assets image_test compress_test strings_test hash_test crypto_test noise_test
|
all: download_test_assets image_test compress_test strings_test hash_test crypto_test noise_test encoding_test
|
||||||
|
|
||||||
download_test_assets:
|
download_test_assets:
|
||||||
$(PYTHON) download_assets.py
|
$(PYTHON) download_assets.py
|
||||||
@@ -23,3 +23,7 @@ crypto_test:
|
|||||||
|
|
||||||
noise_test:
|
noise_test:
|
||||||
$(ODIN) run math/noise -out=test_noise
|
$(ODIN) run math/noise -out=test_noise
|
||||||
|
|
||||||
|
encoding_test:
|
||||||
|
$(ODIN) run encoding/json -out=test_json
|
||||||
|
$(ODIN) run encoding/varint -out=test_varint
|
||||||
@@ -35,7 +35,8 @@ echo ---
|
|||||||
echo ---
|
echo ---
|
||||||
echo Running core:encoding tests
|
echo Running core:encoding tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run encoding %COMMON%
|
%PATH_TO_ODIN% run encoding/json %COMMON%
|
||||||
|
%PATH_TO_ODIN% run encoding/varint %COMMON%
|
||||||
|
|
||||||
echo ---
|
echo ---
|
||||||
echo Running core:math/noise tests
|
echo Running core:math/noise tests
|
||||||
|
|||||||
@@ -30,14 +30,12 @@ when ODIN_TEST {
|
|||||||
log :: testing.log
|
log :: testing.log
|
||||||
} else {
|
} else {
|
||||||
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
|
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
|
||||||
fmt.printf("[%v] ", loc)
|
|
||||||
TEST_count += 1
|
TEST_count += 1
|
||||||
if !condition {
|
if !condition {
|
||||||
TEST_fail += 1
|
TEST_fail += 1
|
||||||
fmt.println(message)
|
fmt.printf("[%v] %v\n", loc, message)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.println(" PASS")
|
|
||||||
}
|
}
|
||||||
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
|
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
|
||||||
fmt.printf("[%v] ", loc)
|
fmt.printf("[%v] ", loc)
|
||||||
@@ -52,6 +50,9 @@ main :: proc() {
|
|||||||
gzip_test(&t)
|
gzip_test(&t)
|
||||||
|
|
||||||
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
|
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
|
||||||
|
if TEST_fail > 0 {
|
||||||
|
os.exit(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@test
|
@test
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+20
-18
@@ -3,37 +3,39 @@ package test_core_json
|
|||||||
import "core:encoding/json"
|
import "core:encoding/json"
|
||||||
import "core:testing"
|
import "core:testing"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
import "core:os"
|
||||||
|
|
||||||
TEST_count := 0
|
TEST_count := 0
|
||||||
TEST_fail := 0
|
TEST_fail := 0
|
||||||
|
|
||||||
when ODIN_TEST {
|
when ODIN_TEST {
|
||||||
expect :: testing.expect
|
expect :: testing.expect
|
||||||
log :: testing.log
|
log :: testing.log
|
||||||
} else {
|
} else {
|
||||||
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
|
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
|
||||||
fmt.printf("[%v] ", loc)
|
TEST_count += 1
|
||||||
TEST_count += 1
|
if !condition {
|
||||||
if !condition {
|
TEST_fail += 1
|
||||||
TEST_fail += 1
|
fmt.printf("[%v] %v\n", loc, message)
|
||||||
fmt.println(message)
|
return
|
||||||
return
|
}
|
||||||
}
|
}
|
||||||
fmt.println(" PASS")
|
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
|
||||||
}
|
fmt.printf("[%v] ", loc)
|
||||||
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
|
fmt.printf("log: %v\n", v)
|
||||||
fmt.printf("[%v] ", loc)
|
}
|
||||||
fmt.printf("log: %v\n", v)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
t := testing.T{}
|
t := testing.T{}
|
||||||
|
|
||||||
parse_json(&t)
|
parse_json(&t)
|
||||||
marshal_json(&t)
|
marshal_json(&t)
|
||||||
|
|
||||||
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
|
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
|
||||||
|
if TEST_fail > 0 {
|
||||||
|
os.exit(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@test
|
@test
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
package test_core_varint
|
||||||
|
|
||||||
|
import "core:encoding/varint"
|
||||||
|
import "core:testing"
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:os"
|
||||||
|
import "core:slice"
|
||||||
|
import "core:math/rand"
|
||||||
|
|
||||||
|
TEST_count := 0
|
||||||
|
TEST_fail := 0
|
||||||
|
|
||||||
|
RANDOM_TESTS :: 100
|
||||||
|
|
||||||
|
when ODIN_TEST {
|
||||||
|
expect :: testing.expect
|
||||||
|
log :: testing.log
|
||||||
|
} else {
|
||||||
|
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
|
||||||
|
TEST_count += 1
|
||||||
|
if !condition {
|
||||||
|
TEST_fail += 1
|
||||||
|
fmt.printf("[%v] %v\n", loc, message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
|
||||||
|
fmt.printf("[%v] ", loc)
|
||||||
|
fmt.printf("log: %v\n", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main :: proc() {
|
||||||
|
t := testing.T{}
|
||||||
|
|
||||||
|
test_leb128(&t)
|
||||||
|
|
||||||
|
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
|
||||||
|
if TEST_fail > 0 {
|
||||||
|
os.exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
test_leb128 :: proc(t: ^testing.T) {
|
||||||
|
buf: [varint.LEB128_MAX_BYTES]u8
|
||||||
|
|
||||||
|
for vector in ULEB_Vectors {
|
||||||
|
val, size, err := varint.decode_uleb128(vector.encoded)
|
||||||
|
|
||||||
|
msg := fmt.tprintf("Expected %02x to decode to %v consuming %v bytes, got %v and %v", vector.encoded, vector.value, vector.size, val, size)
|
||||||
|
expect(t, size == vector.size && val == vector.value, msg)
|
||||||
|
|
||||||
|
msg = fmt.tprintf("Expected decoder to return error %v, got %v", vector.error, err)
|
||||||
|
expect(t, err == vector.error, msg)
|
||||||
|
|
||||||
|
if err == .None { // Try to roundtrip
|
||||||
|
size, err = varint.encode_uleb128(buf[:], vector.value)
|
||||||
|
|
||||||
|
msg = fmt.tprintf("Expected %v to encode to %02x, got %02x", vector.value, vector.encoded, buf[:size])
|
||||||
|
expect(t, size == vector.size && slice.simple_equal(vector.encoded, buf[:size]), msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for vector in ILEB_Vectors {
|
||||||
|
val, size, err := varint.decode_ileb128(vector.encoded)
|
||||||
|
|
||||||
|
msg := fmt.tprintf("Expected %02x to decode to %v consuming %v bytes, got %v and %v", vector.encoded, vector.value, vector.size, val, size)
|
||||||
|
expect(t, size == vector.size && val == vector.value, msg)
|
||||||
|
|
||||||
|
msg = fmt.tprintf("Expected decoder to return error %v, got %v", vector.error, err)
|
||||||
|
expect(t, err == vector.error, msg)
|
||||||
|
|
||||||
|
if err == .None { // Try to roundtrip
|
||||||
|
size, err = varint.encode_ileb128(buf[:], vector.value)
|
||||||
|
|
||||||
|
msg = fmt.tprintf("Expected %v to encode to %02x, got %02x", vector.value, vector.encoded, buf[:size])
|
||||||
|
expect(t, size == vector.size && slice.simple_equal(vector.encoded, buf[:size]), msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for num_bytes in 1..uint(16) {
|
||||||
|
for _ in 0..RANDOM_TESTS {
|
||||||
|
unsigned, signed := get_random(num_bytes)
|
||||||
|
|
||||||
|
{
|
||||||
|
encode_size, encode_err := varint.encode_uleb128(buf[:], unsigned)
|
||||||
|
msg := fmt.tprintf("%v failed to encode as an unsigned LEB128 value, got %v", unsigned, encode_err)
|
||||||
|
expect(t, encode_err == .None, msg)
|
||||||
|
|
||||||
|
decoded, decode_size, decode_err := varint.decode_uleb128(buf[:])
|
||||||
|
msg = fmt.tprintf("Expected %02x to decode as %v, got %v", buf[:encode_size], unsigned, decoded)
|
||||||
|
expect(t, decode_err == .None && decode_size == encode_size && decoded == unsigned, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
encode_size, encode_err := varint.encode_ileb128(buf[:], signed)
|
||||||
|
msg := fmt.tprintf("%v failed to encode as a signed LEB128 value, got %v", signed, encode_err)
|
||||||
|
expect(t, encode_err == .None, msg)
|
||||||
|
|
||||||
|
decoded, decode_size, decode_err := varint.decode_ileb128(buf[:])
|
||||||
|
msg = fmt.tprintf("Expected %02x to decode as %v, got %v, err: %v", buf[:encode_size], signed, decoded, decode_err)
|
||||||
|
expect(t, decode_err == .None && decode_size == encode_size && decoded == signed, msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get_random :: proc(byte_count: uint) -> (u: u128, i: i128) {
|
||||||
|
assert(byte_count >= 0 && byte_count <= size_of(u128))
|
||||||
|
|
||||||
|
for _ in 1..byte_count {
|
||||||
|
u <<= 8
|
||||||
|
u |= u128(rand.uint32() & 0xff)
|
||||||
|
}
|
||||||
|
|
||||||
|
bias := i128(1 << (byte_count * 7)) - 1
|
||||||
|
i = i128(u) - bias
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ULEB_Test_Vector :: struct {
|
||||||
|
encoded: []u8,
|
||||||
|
value: u128,
|
||||||
|
size: int,
|
||||||
|
error: varint.Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
ULEB_Vectors :: []ULEB_Test_Vector{
|
||||||
|
{ []u8{0x00}, 0, 1, .None },
|
||||||
|
{ []u8{0x7f}, 127, 1, .None },
|
||||||
|
{ []u8{0xE5, 0x8E, 0x26}, 624485, 3, .None },
|
||||||
|
{ []u8{0x80}, 0, 0, .Buffer_Too_Small },
|
||||||
|
{ []u8{}, 0, 0, .Buffer_Too_Small },
|
||||||
|
|
||||||
|
{ []u8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03}, max(u128), 19, .None },
|
||||||
|
}
|
||||||
|
|
||||||
|
ILEB_Test_Vector :: struct {
|
||||||
|
encoded: []u8,
|
||||||
|
value: i128,
|
||||||
|
size: int,
|
||||||
|
error: varint.Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
ILEB_Vectors :: []ILEB_Test_Vector{
|
||||||
|
{ []u8{0x00}, 0, 1, .None },
|
||||||
|
{ []u8{0x3f}, 63, 1, .None },
|
||||||
|
{ []u8{0x40}, -64, 1, .None },
|
||||||
|
{ []u8{0xC0, 0xBB, 0x78}, -123456, 3, .None },
|
||||||
|
{ []u8{}, 0, 0, .Buffer_Too_Small },
|
||||||
|
|
||||||
|
{ []u8{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7e}, min(i128), 19, .None },
|
||||||
|
{ []u8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01}, max(i128), 19, .None },
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import "core:hash"
|
|||||||
import "core:time"
|
import "core:time"
|
||||||
import "core:testing"
|
import "core:testing"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
import "core:os"
|
||||||
|
|
||||||
TEST_count := 0
|
TEST_count := 0
|
||||||
TEST_fail := 0
|
TEST_fail := 0
|
||||||
@@ -14,14 +15,12 @@ when ODIN_TEST {
|
|||||||
log :: testing.log
|
log :: testing.log
|
||||||
} else {
|
} else {
|
||||||
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
|
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
|
||||||
fmt.printf("[%v] ", loc)
|
|
||||||
TEST_count += 1
|
TEST_count += 1
|
||||||
if !condition {
|
if !condition {
|
||||||
TEST_fail += 1
|
TEST_fail += 1
|
||||||
fmt.println(" FAIL:", message)
|
fmt.printf("[%v] %v\n", loc, message)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.println(" PASS")
|
|
||||||
}
|
}
|
||||||
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
|
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
|
||||||
fmt.printf("[%v] ", loc)
|
fmt.printf("[%v] ", loc)
|
||||||
@@ -35,6 +34,9 @@ main :: proc() {
|
|||||||
test_xxhash_vectors(&t)
|
test_xxhash_vectors(&t)
|
||||||
test_crc64_vectors(&t)
|
test_crc64_vectors(&t)
|
||||||
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
|
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
|
||||||
|
if TEST_fail > 0 {
|
||||||
|
os.exit(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user