mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-04 14:48:47 +00:00
Merge branch 'master' into parser-experiments
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
name: Nightly
|
name: Nightly
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: 0 20 * * *
|
- cron: 0 20 * * *
|
||||||
|
|
||||||
@@ -50,7 +51,7 @@ jobs:
|
|||||||
- name: (Linux) Download LLVM
|
- name: (Linux) Download LLVM
|
||||||
run: sudo apt-get install llvm
|
run: sudo apt-get install llvm
|
||||||
- name: build odin
|
- name: build odin
|
||||||
run: make release
|
run: make nightly
|
||||||
- name: Odin run
|
- name: Odin run
|
||||||
run: ./odin run examples/demo/demo.odin
|
run: ./odin run examples/demo/demo.odin
|
||||||
- name: Copy artifacts
|
- name: Copy artifacts
|
||||||
@@ -76,7 +77,7 @@ jobs:
|
|||||||
TMP_PATH=$(xcrun --show-sdk-path)/user/include
|
TMP_PATH=$(xcrun --show-sdk-path)/user/include
|
||||||
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
|
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
|
||||||
- name: build odin
|
- name: build odin
|
||||||
run: make release
|
run: make nightly
|
||||||
- name: Odin run
|
- name: Odin run
|
||||||
run: ./odin run examples/demo/demo.odin
|
run: ./odin run examples/demo/demo.odin
|
||||||
- name: Copy artifacts
|
- name: Copy artifacts
|
||||||
@@ -96,10 +97,18 @@ jobs:
|
|||||||
needs: [build_windows, build_macos, build_ubuntu]
|
needs: [build_windows, build_macos, build_ubuntu]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
|
- uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: '3.x'
|
||||||
|
|
||||||
- name: Install B2 CLI
|
- name: Install B2 CLI
|
||||||
shell: bash
|
shell: bash
|
||||||
run: sudo pip install --upgrade b2
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install --upgrade b2
|
||||||
|
|
||||||
|
- name: Display Python version
|
||||||
|
run: python -c "import sys; print(sys.version)"
|
||||||
|
|
||||||
- name: Download Windows artifacts
|
- name: Download Windows artifacts
|
||||||
uses: actions/download-artifact@v1
|
uses: actions/download-artifact@v1
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ release:
|
|||||||
$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 -march=native $(LDFLAGS) -o odin
|
$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 -march=native $(LDFLAGS) -o odin
|
||||||
|
|
||||||
nightly:
|
nightly:
|
||||||
$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -DNIGHTLY -O3 -march=native $(LDFLAGS) -o odin
|
$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -DNIGHTLY -O3 $(LDFLAGS) -o odin
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -30,7 +30,7 @@ The proposal process is the process for reviewing a proposal and reaching a deci
|
|||||||
* Accept proposal
|
* Accept proposal
|
||||||
* Decline proposal
|
* Decline proposal
|
||||||
|
|
||||||
After the proposal is accepted or declined, implementation of the proprosal proceeds in the same way as any other contribution to the project.
|
After the proposal is accepted or declined, implementation of the proposal proceeds in the same way as any other contribution to the project.
|
||||||
|
|
||||||
## Design Documents
|
## Design Documents
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ main :: proc() {
|
|||||||
|
|
||||||
#### [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 installing the Odin compiler and libraries.
|
||||||
|
|
||||||
### Learning Odin
|
### Learning Odin
|
||||||
|
|
||||||
|
|||||||
Executable
+31
@@ -0,0 +1,31 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
release_mode=$1
|
||||||
|
|
||||||
|
warnings_to_disable="-std=c++11 -Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined"
|
||||||
|
libraries="-pthread -ldl -lm -lstdc++"
|
||||||
|
other_args="-DLLVM_BACKEND_SUPPORT -DUSE_NEW_LLVM_ABI_SYSTEM"
|
||||||
|
compiler="clang"
|
||||||
|
|
||||||
|
if [ -z "$release_mode" ]; then release_mode="0"; fi
|
||||||
|
|
||||||
|
if [ "$release_mode" -eq "0" ]; then
|
||||||
|
other_args="${other_args} -g"
|
||||||
|
fi
|
||||||
|
if [ "$release_mode" -eq "1" ]; then
|
||||||
|
other_args="${other_args} -O3 -march=native"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$(uname)" == "Darwin" ]]; then
|
||||||
|
|
||||||
|
# Set compiler to clang on MacOS
|
||||||
|
# MacOS provides a symlink to clang called gcc, but it's nice to be explicit here.
|
||||||
|
compiler="clang"
|
||||||
|
|
||||||
|
other_args="${other_args} -liconv"
|
||||||
|
elif [[ "$(uname)" == "FreeBSD" ]]; then
|
||||||
|
compiler="clang"
|
||||||
|
fi
|
||||||
|
|
||||||
|
${compiler} src/main.cpp ${warnings_to_disable} ${libraries} ${other_args} -o odin \
|
||||||
|
&& ./odin run examples/demo/demo.odin -llvm-api
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package bufio
|
||||||
|
|
||||||
|
import "core:io"
|
||||||
|
|
||||||
|
// Read_Writer stores pointers to a Reader and a Writer
|
||||||
|
Read_Writer :: struct {
|
||||||
|
r: ^Reader,
|
||||||
|
w: ^Writer,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
read_writer_init :: proc(rw: ^Read_Writer, r: ^Reader, w: ^Writer) {
|
||||||
|
rw.r, rw.w = r, w;
|
||||||
|
}
|
||||||
|
|
||||||
|
read_writer_to_stream :: proc(rw: ^Read_Writer) -> (s: io.Stream) {
|
||||||
|
s.stream_data = rw;
|
||||||
|
s.stream_vtable = _read_writer_vtable;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_read_writer_vtable := &io.Stream_VTable{
|
||||||
|
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||||
|
b := (^Read_Writer)(s.stream_data).r;
|
||||||
|
return reader_read(b, p);
|
||||||
|
},
|
||||||
|
impl_read_byte = proc(s: io.Stream) -> (c: byte, err: io.Error) {
|
||||||
|
b := (^Read_Writer)(s.stream_data).r;
|
||||||
|
return reader_read_byte(b);
|
||||||
|
},
|
||||||
|
impl_unread_byte = proc(s: io.Stream) -> io.Error {
|
||||||
|
b := (^Read_Writer)(s.stream_data).r;
|
||||||
|
return reader_unread_byte(b);
|
||||||
|
},
|
||||||
|
impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) {
|
||||||
|
b := (^Read_Writer)(s.stream_data).r;
|
||||||
|
return reader_read_rune(b);
|
||||||
|
},
|
||||||
|
impl_unread_rune = proc(s: io.Stream) -> io.Error {
|
||||||
|
b := (^Read_Writer)(s.stream_data).r;
|
||||||
|
return reader_unread_rune(b);
|
||||||
|
},
|
||||||
|
impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||||
|
b := (^Read_Writer)(s.stream_data).r;
|
||||||
|
return reader_write_to(b, w);
|
||||||
|
},
|
||||||
|
impl_flush = proc(s: io.Stream) -> io.Error {
|
||||||
|
b := (^Read_Writer)(s.stream_data).w;
|
||||||
|
return writer_flush(b);
|
||||||
|
},
|
||||||
|
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||||
|
b := (^Read_Writer)(s.stream_data).w;
|
||||||
|
return writer_write(b, p);
|
||||||
|
},
|
||||||
|
impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
|
||||||
|
b := (^Read_Writer)(s.stream_data).w;
|
||||||
|
return writer_write_byte(b, c);
|
||||||
|
},
|
||||||
|
impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) {
|
||||||
|
b := (^Read_Writer)(s.stream_data).w;
|
||||||
|
return writer_write_rune(b, r);
|
||||||
|
},
|
||||||
|
impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) {
|
||||||
|
b := (^Read_Writer)(s.stream_data).w;
|
||||||
|
return writer_read_from(b, r);
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,474 @@
|
|||||||
|
package bufio
|
||||||
|
|
||||||
|
import "core:io"
|
||||||
|
import "core:mem"
|
||||||
|
import "core:unicode/utf8"
|
||||||
|
import "core:bytes"
|
||||||
|
|
||||||
|
// Reader is a buffered wrapper for an io.Reader
|
||||||
|
Reader :: struct {
|
||||||
|
buf: []byte,
|
||||||
|
buf_allocator: mem.Allocator,
|
||||||
|
|
||||||
|
rd: io.Reader, // reader
|
||||||
|
r, w: int, // read and write positions for buf
|
||||||
|
|
||||||
|
err: io.Error,
|
||||||
|
|
||||||
|
last_byte: int, // last byte read, invalid is -1
|
||||||
|
last_rune_size: int, // size of last rune read, invalid is -1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_BUF_SIZE :: 4096;
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
MIN_READ_BUFFER_SIZE :: 16;
|
||||||
|
@(private)
|
||||||
|
MAX_CONSECUTIVE_EMPTY_READS :: 128;
|
||||||
|
|
||||||
|
reader_init :: proc(b: ^Reader, rd: io.Reader, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator) {
|
||||||
|
size := size;
|
||||||
|
size = max(size, MIN_READ_BUFFER_SIZE);
|
||||||
|
reader_reset(b, rd);
|
||||||
|
b.buf_allocator = allocator;
|
||||||
|
b.buf = make([]byte, size, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
reader_init_with_buf :: proc(b: ^Reader, rd: io.Reader, buf: []byte) {
|
||||||
|
reader_reset(b, rd);
|
||||||
|
b.buf_allocator = {};
|
||||||
|
b.buf = buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reader_destroy destroys the underlying buffer with its associated allocator IFF that allocator has been set
|
||||||
|
reader_destroy :: proc(b: ^Reader) {
|
||||||
|
delete(b.buf, b.buf_allocator);
|
||||||
|
b^ = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
reader_size :: proc(b: ^Reader) -> int {
|
||||||
|
return len(b.buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
reader_reset :: proc(b: ^Reader, r: io.Reader) {
|
||||||
|
b.rd = r;
|
||||||
|
b.r, b.w = 0, 0;
|
||||||
|
b.err = nil;
|
||||||
|
b.last_byte = -1;
|
||||||
|
b.last_rune_size = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_reader_read_new_chunk :: proc(b: ^Reader) -> io.Error {
|
||||||
|
if b.r > 0 {
|
||||||
|
copy(b.buf, b.buf[b.r:b.w]);
|
||||||
|
b.w -= b.r;
|
||||||
|
b.r = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.w >= len(b.buf) {
|
||||||
|
return .Buffer_Full;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read new data, and try a limited number of times
|
||||||
|
for i := MAX_CONSECUTIVE_EMPTY_READS; i > 0; i -= 1 {
|
||||||
|
n, err := io.read(b.rd, b.buf[b.w:]);
|
||||||
|
if n < 0 {
|
||||||
|
return .Negative_Read;
|
||||||
|
}
|
||||||
|
b.w += n;
|
||||||
|
if err != nil {
|
||||||
|
b.err = err;
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
if n > 0 {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.err = .No_Progress;
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_reader_consume_err :: proc(b: ^Reader) -> io.Error {
|
||||||
|
err := b.err;
|
||||||
|
b.err = nil;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reader_peek returns the next n bytes without advancing the reader
|
||||||
|
// The bytes stop being valid on the next read call
|
||||||
|
// If reader_peek returns fewer than n bytes, it also return an error
|
||||||
|
// explaining why the read is short
|
||||||
|
// The error will be .Buffer_Full if n is larger than the internal buffer size
|
||||||
|
reader_peek :: proc(b: ^Reader, n: int) -> (data: []byte, err: io.Error) {
|
||||||
|
n := n;
|
||||||
|
|
||||||
|
if n < 0 {
|
||||||
|
return nil, .Negative_Count;
|
||||||
|
}
|
||||||
|
b.last_byte = -1;
|
||||||
|
b.last_rune_size = -1;
|
||||||
|
|
||||||
|
for b.w-b.r < n && b.w-b.r < len(b.buf) && b.err == nil {
|
||||||
|
if fill_err := _reader_read_new_chunk(b); fill_err != nil {
|
||||||
|
return nil, fill_err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if n > len(b.buf) {
|
||||||
|
return b.buf[b.r : b.w], .Buffer_Full;
|
||||||
|
}
|
||||||
|
|
||||||
|
if available := b.w - b.r; available < n {
|
||||||
|
n = available;
|
||||||
|
err = _reader_consume_err(b);
|
||||||
|
if err == nil {
|
||||||
|
err = .Buffer_Full;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.buf[b.r : b.r+n], err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reader_buffered returns the number of bytes that can be read from the current buffer
|
||||||
|
reader_buffered :: proc(b: ^Reader) -> int {
|
||||||
|
return b.w - b.r;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reader_discard skips the next n bytes, and returns the number of bytes that were discarded
|
||||||
|
reader_discard :: proc(b: ^Reader, n: int) -> (discarded: int, err: io.Error) {
|
||||||
|
if n < 0 {
|
||||||
|
return 0, .Negative_Count;
|
||||||
|
}
|
||||||
|
if n == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
remaining := n;
|
||||||
|
for {
|
||||||
|
skip := reader_buffered(b);
|
||||||
|
if skip == 0 {
|
||||||
|
if fill_err := _reader_read_new_chunk(b); fill_err != nil {
|
||||||
|
return 0, fill_err;
|
||||||
|
}
|
||||||
|
skip = reader_buffered(b);
|
||||||
|
}
|
||||||
|
skip = min(skip, remaining);
|
||||||
|
b.r += skip;
|
||||||
|
remaining -= skip;
|
||||||
|
if remaining == 0 {
|
||||||
|
return n, nil;
|
||||||
|
}
|
||||||
|
if b.err != nil {
|
||||||
|
return n - remaining, _reader_consume_err(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reader_read reads data into p
|
||||||
|
// The bytes are taken from at most one read on the underlying Reader, which means n may be less than len(p)
|
||||||
|
reader_read :: proc(b: ^Reader, p: []byte) -> (n: int, err: io.Error) {
|
||||||
|
n = len(p);
|
||||||
|
if n == 0 {
|
||||||
|
if reader_buffered(b) > 0 {
|
||||||
|
return 0, nil;
|
||||||
|
}
|
||||||
|
return 0, _reader_consume_err(b);
|
||||||
|
}
|
||||||
|
if b.r == b.w {
|
||||||
|
if b.err != nil {
|
||||||
|
return 0, _reader_consume_err(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(p) >= len(b.buf) {
|
||||||
|
n, b.err = io.read(b.rd, p);
|
||||||
|
if n < 0 {
|
||||||
|
return 0, .Negative_Read;
|
||||||
|
}
|
||||||
|
|
||||||
|
if n > 0 {
|
||||||
|
b.last_byte = int(p[n-1]);
|
||||||
|
b.last_rune_size = -1;
|
||||||
|
}
|
||||||
|
return n, _reader_consume_err(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
b.r, b.w = 0, 0;
|
||||||
|
n, b.err = io.read(b.rd, b.buf);
|
||||||
|
if n < 0 {
|
||||||
|
return 0, .Negative_Read;
|
||||||
|
}
|
||||||
|
if n == 0 {
|
||||||
|
return 0, _reader_consume_err(b);
|
||||||
|
}
|
||||||
|
b.w += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
n = copy(p, b.buf[b.r:b.w]);
|
||||||
|
b.r += n;
|
||||||
|
b.last_byte = int(b.buf[b.r-1]);
|
||||||
|
b.last_rune_size = -1;
|
||||||
|
return n, nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reader_read_byte reads and returns a single byte
|
||||||
|
// If no byte is available, it return an error
|
||||||
|
reader_read_byte :: proc(b: ^Reader) -> (byte, io.Error) {
|
||||||
|
b.last_rune_size = -1;
|
||||||
|
for b.r == b.w {
|
||||||
|
if b.err != nil {
|
||||||
|
return 0, _reader_consume_err(b);
|
||||||
|
}
|
||||||
|
if err := _reader_read_new_chunk(b); err != nil {
|
||||||
|
return 0, err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c := b.buf[b.r];
|
||||||
|
b.r += 1;
|
||||||
|
b.last_byte = int(c);
|
||||||
|
return c, nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reader_unread_byte unreads the last byte. Only the most recently read byte can be unread
|
||||||
|
reader_unread_byte :: proc(b: ^Reader) -> io.Error {
|
||||||
|
if b.last_byte < 0 || b.r == 0 && b.w > 0 {
|
||||||
|
return .Invalid_Unread;
|
||||||
|
}
|
||||||
|
if b.r > 0 {
|
||||||
|
b.r -= 1;
|
||||||
|
} else {
|
||||||
|
// b.r == 0 && b.w == 0
|
||||||
|
b.w = 1;
|
||||||
|
}
|
||||||
|
b.buf[b.r] = byte(b.last_byte);
|
||||||
|
b.last_byte = -1;
|
||||||
|
b.last_rune_size = -1;
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reader_read_rune reads a single UTF-8 encoded unicode character
|
||||||
|
// and returns the rune and its size in bytes
|
||||||
|
// If the encoded rune is invalid, it consumes one byte and returns utf8.RUNE_ERROR (U+FFFD) with a size of 1
|
||||||
|
reader_read_rune :: proc(b: ^Reader) -> (r: rune, size: int, err: io.Error) {
|
||||||
|
for b.r+utf8.UTF_MAX > b.w &&
|
||||||
|
!utf8.full_rune(b.buf[b.r:b.w]) &&
|
||||||
|
b.err == nil &&
|
||||||
|
b.w-b.w < len(b.buf) {
|
||||||
|
if err = _reader_read_new_chunk(b); err != nil {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.last_rune_size = -1;
|
||||||
|
if b.r == b.w {
|
||||||
|
err = _reader_consume_err(b);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
r, size = rune(b.buf[b.r]), 1;
|
||||||
|
if r >= utf8.RUNE_SELF {
|
||||||
|
r, size = utf8.decode_rune(b.buf[b.r : b.w]);
|
||||||
|
}
|
||||||
|
b.r += size;
|
||||||
|
b.last_byte = int(b.buf[b.r-1]);
|
||||||
|
b.last_rune_size = size;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reader_unread_rune unreads the last rune. Only the most recently read rune can be unread
|
||||||
|
reader_unread_rune :: proc(b: ^Reader) -> io.Error {
|
||||||
|
if b.last_rune_size < 0 || b.r < b.last_rune_size {
|
||||||
|
return .Invalid_Unread;
|
||||||
|
}
|
||||||
|
b.r -= b.last_rune_size;
|
||||||
|
b.last_byte = -1;
|
||||||
|
b.last_rune_size = -1;
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
reader_write_to :: proc(b: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||||
|
write_buf :: proc(b: ^Reader, w: io.Writer) -> (i64, io.Error) {
|
||||||
|
n, err := io.write(w, b.buf[b.r:b.w]);
|
||||||
|
if n < 0 {
|
||||||
|
return 0, .Negative_Write;
|
||||||
|
}
|
||||||
|
b.r += n;
|
||||||
|
return i64(n), err;
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err = write_buf(b, w);
|
||||||
|
if err != nil {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m: i64;
|
||||||
|
if nr, cerr := io.to_writer_to(b.rd); cerr == nil {
|
||||||
|
m, err = io.write_to(nr, w);
|
||||||
|
n += m;
|
||||||
|
return n, err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if nw, cerr := io.to_reader_from(w); cerr == nil {
|
||||||
|
m, err = io.read_from(nw, b.rd);
|
||||||
|
n += m;
|
||||||
|
return n, err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.w-b.r < len(b.buf) {
|
||||||
|
if err = _reader_read_new_chunk(b); err != nil {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for b.r < b.w {
|
||||||
|
m, err = write_buf(b, w);
|
||||||
|
n += m;
|
||||||
|
if err != nil {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if err = _reader_read_new_chunk(b); err != nil {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.err == .EOF {
|
||||||
|
b.err = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = _reader_consume_err(b);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// reader_to_stream converts a Reader into an io.Stream
|
||||||
|
reader_to_stream :: proc(b: ^Reader) -> (s: io.Stream) {
|
||||||
|
s.stream_data = b;
|
||||||
|
s.stream_vtable = _reader_vtable;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_reader_vtable := &io.Stream_VTable{
|
||||||
|
impl_destroy = proc(s: io.Stream) -> io.Error {
|
||||||
|
b := (^Reader)(s.stream_data);
|
||||||
|
reader_destroy(b);
|
||||||
|
return nil;
|
||||||
|
},
|
||||||
|
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||||
|
b := (^Reader)(s.stream_data);
|
||||||
|
return reader_read(b, p);
|
||||||
|
},
|
||||||
|
impl_read_byte = proc(s: io.Stream) -> (c: byte, err: io.Error) {
|
||||||
|
b := (^Reader)(s.stream_data);
|
||||||
|
return reader_read_byte(b);
|
||||||
|
},
|
||||||
|
impl_unread_byte = proc(s: io.Stream) -> io.Error {
|
||||||
|
b := (^Reader)(s.stream_data);
|
||||||
|
return reader_unread_byte(b);
|
||||||
|
},
|
||||||
|
impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) {
|
||||||
|
b := (^Reader)(s.stream_data);
|
||||||
|
return reader_read_rune(b);
|
||||||
|
},
|
||||||
|
impl_unread_rune = proc(s: io.Stream) -> io.Error {
|
||||||
|
b := (^Reader)(s.stream_data);
|
||||||
|
return reader_unread_rune(b);
|
||||||
|
},
|
||||||
|
impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||||
|
b := (^Reader)(s.stream_data);
|
||||||
|
return reader_write_to(b, w);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Utility procedures
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
// reader_read_slice reads until the first occurrence of delim from the reader
|
||||||
|
// It returns a slice pointing at the bytes in the buffer
|
||||||
|
// The bytes stop being valid at the next read
|
||||||
|
// If reader_read_slice encounters an error before finding a delimiter
|
||||||
|
// reader_read_slice fails with error .Buffer_Full if the buffer fills without a delim
|
||||||
|
// Because the data returned from reader_read_slice will be overwritten on the
|
||||||
|
// next IO operation, reader_read_bytes or reader_read_string is usually preferred
|
||||||
|
//
|
||||||
|
// reader_read_slice returns err != nil if and only if line does not end in delim
|
||||||
|
//
|
||||||
|
reader_read_slice :: proc(b: ^Reader, delim: byte) -> (line: []byte, err: io.Error) {
|
||||||
|
s := 0;
|
||||||
|
for {
|
||||||
|
if i := bytes.index_byte(b.buf[b.r+s : b.w], delim); i >= 0 {
|
||||||
|
i += s;
|
||||||
|
line = b.buf[b.r:][:i+1];
|
||||||
|
b.r += i + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.err != nil {
|
||||||
|
line = b.buf[b.r : b.w];
|
||||||
|
b.r = b.w;
|
||||||
|
err = _reader_consume_err(b);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if reader_buffered(b) >= len(b.buf) {
|
||||||
|
b.r = b.w;
|
||||||
|
line = b.buf;
|
||||||
|
err = .Buffer_Full;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
s = b.w - b.r;
|
||||||
|
|
||||||
|
if err = _reader_read_new_chunk(b); err != nil {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if i := len(line)-1; i >= 0 {
|
||||||
|
b.last_byte = int(line[i]);
|
||||||
|
b.last_rune_size = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reader_read_bytes reads until the first occurrence of delim from the Reader
|
||||||
|
// It returns an allocated slice containing the data up to and including the delimiter
|
||||||
|
reader_read_bytes :: proc(b: ^Reader, delim: byte, allocator := context.allocator) -> (buf: []byte, err: io.Error) {
|
||||||
|
full: [dynamic]byte;
|
||||||
|
full.allocator = allocator;
|
||||||
|
|
||||||
|
frag: []byte;
|
||||||
|
for {
|
||||||
|
e: io.Error;
|
||||||
|
frag, e = reader_read_slice(b, delim);
|
||||||
|
if e == nil {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if e != .Buffer_Full {
|
||||||
|
err = e;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
append(&full, ..frag);
|
||||||
|
}
|
||||||
|
append(&full, ..frag);
|
||||||
|
return full[:], err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reader_read_string reads until the first occurrence of delim from the Reader
|
||||||
|
// It returns an allocated string containing the data up to and including the delimiter
|
||||||
|
reader_read_string :: proc(b: ^Reader, delim: byte, allocator := context.allocator) -> (string, io.Error) {
|
||||||
|
buf, err := reader_read_bytes(b, delim, allocator);
|
||||||
|
return string(buf), err;
|
||||||
|
}
|
||||||
@@ -0,0 +1,255 @@
|
|||||||
|
package bufio
|
||||||
|
|
||||||
|
import "core:io"
|
||||||
|
import "core:mem"
|
||||||
|
import "core:unicode/utf8"
|
||||||
|
// import "core:bytes"
|
||||||
|
|
||||||
|
// Writer is a buffered wrapper for an io.Writer
|
||||||
|
Writer :: struct {
|
||||||
|
buf: []byte,
|
||||||
|
buf_allocator: mem.Allocator,
|
||||||
|
|
||||||
|
wr: io.Writer,
|
||||||
|
n: int,
|
||||||
|
|
||||||
|
err: io.Error,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
writer_init :: proc(b: ^Writer, wr: io.Writer, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator) {
|
||||||
|
size := size;
|
||||||
|
size = max(size, MIN_READ_BUFFER_SIZE);
|
||||||
|
writer_reset(b, wr);
|
||||||
|
b.buf_allocator = allocator;
|
||||||
|
b.buf = make([]byte, size, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
writer_init_with_buf :: proc(b: ^Writer, wr: io.Writer, buf: []byte) {
|
||||||
|
writer_reset(b, wr);
|
||||||
|
b.buf_allocator = {};
|
||||||
|
b.buf = buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
// writer_destroy destroys the underlying buffer with its associated allocator IFF that allocator has been set
|
||||||
|
writer_destroy :: proc(b: ^Writer) {
|
||||||
|
delete(b.buf, b.buf_allocator);
|
||||||
|
b^ = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// writer_size returns the size of underlying buffer in bytes
|
||||||
|
writer_size :: proc(b: ^Writer) -> int {
|
||||||
|
return len(b.buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
writer_reset :: proc(b: ^Writer, w: io.Writer) {
|
||||||
|
b.wr = w;
|
||||||
|
b.n = 0;
|
||||||
|
b.err = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// writer_flush writes any buffered data into the underlying io.Writer
|
||||||
|
writer_flush :: proc(b: ^Writer) -> io.Error {
|
||||||
|
if b.err != nil {
|
||||||
|
return b.err;
|
||||||
|
}
|
||||||
|
if b.n == 0 {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := io.write(b.wr, b.buf[0:b.n]);
|
||||||
|
if n < b.n && err == nil {
|
||||||
|
err = .Short_Write;
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
if n > 0 && n < b.n {
|
||||||
|
copy(b.buf[:b.n-n], b.buf[n : b.n]);
|
||||||
|
}
|
||||||
|
b.n -= n;
|
||||||
|
b.err = err;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
b.n = 0;
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
// writer_available returns how many bytes are unused in the buffer
|
||||||
|
writer_available :: proc(b: ^Writer) -> int {
|
||||||
|
return len(b.buf) - b.n;
|
||||||
|
}
|
||||||
|
|
||||||
|
// writer_buffered returns the number of bytes that have been writted into the current buffer
|
||||||
|
writer_buffered :: proc(b: ^Writer) -> int {
|
||||||
|
return b.n;
|
||||||
|
}
|
||||||
|
|
||||||
|
// writer_write writes the contents of p into the buffer
|
||||||
|
// It returns the number of bytes written
|
||||||
|
// If n < len(p), it will return an error explaining why the write is short
|
||||||
|
writer_write :: proc(b: ^Writer, p: []byte) -> (n: int, err: io.Error) {
|
||||||
|
p := p;
|
||||||
|
for len(p) > writer_available(b) && b.err == nil {
|
||||||
|
m: int;
|
||||||
|
if writer_buffered(b) == 0 {
|
||||||
|
m, b.err = io.write(b.wr, p);
|
||||||
|
} else {
|
||||||
|
m = copy(b.buf[b.n:], p);
|
||||||
|
b.n += m;
|
||||||
|
writer_flush(b);
|
||||||
|
}
|
||||||
|
n += m;
|
||||||
|
p = p[m:];
|
||||||
|
}
|
||||||
|
if b.err != nil {
|
||||||
|
return n, b.err;
|
||||||
|
}
|
||||||
|
m := copy(b.buf[b.n:], p);
|
||||||
|
b.n += m;
|
||||||
|
m += n;
|
||||||
|
return m, nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
// writer_write_byte writes a single byte
|
||||||
|
writer_write_byte :: proc(b: ^Writer, c: byte) -> io.Error {
|
||||||
|
if b.err != nil {
|
||||||
|
return b.err;
|
||||||
|
}
|
||||||
|
if writer_available(b) <= 0 && writer_flush(b) != nil {
|
||||||
|
return b.err;
|
||||||
|
}
|
||||||
|
b.buf[b.n] = c;
|
||||||
|
b.n += 1;
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
// writer_write_rune writes a single unicode code point, and returns the number of bytes written with any error
|
||||||
|
writer_write_rune :: proc(b: ^Writer, r: rune) -> (size: int, err: io.Error) {
|
||||||
|
if r < utf8.RUNE_SELF {
|
||||||
|
err = writer_write_byte(b, byte(r));
|
||||||
|
size = 0 if err != nil else 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if b.err != nil {
|
||||||
|
return 0, b.err;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf: [4]u8;
|
||||||
|
|
||||||
|
n := writer_available(b);
|
||||||
|
if n < utf8.UTF_MAX {
|
||||||
|
writer_flush(b);
|
||||||
|
if b.err != nil {
|
||||||
|
return 0, b.err;
|
||||||
|
}
|
||||||
|
n = writer_available(b);
|
||||||
|
if n < utf8.UTF_MAX {
|
||||||
|
// this only happens if the buffer is very small
|
||||||
|
w: int;
|
||||||
|
buf, w = utf8.encode_rune(r);
|
||||||
|
return writer_write(b, buf[:w]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, size = utf8.encode_rune(r);
|
||||||
|
copy(b.buf[b.n:], buf[:size]);
|
||||||
|
b.n += size;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// writer_write writes a string into the buffer
|
||||||
|
// It returns the number of bytes written
|
||||||
|
// If n < len(p), it will return an error explaining why the write is short
|
||||||
|
writer_write_string :: proc(b: ^Writer, s: string) -> (int, io.Error) {
|
||||||
|
return writer_write(b, transmute([]byte)s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// writer_read_from is to support io.Reader_From types
|
||||||
|
// If the underlying writer supports the io,read_from, and b has no buffered data yet,
|
||||||
|
// this procedure calls the underlying read_from implementation without buffering
|
||||||
|
writer_read_from :: proc(b: ^Writer, r: io.Reader) -> (n: i64, err: io.Error) {
|
||||||
|
if b.err != nil {
|
||||||
|
return 0, b.err;
|
||||||
|
}
|
||||||
|
if writer_buffered(b) == 0 {
|
||||||
|
if w, cerr := io.to_reader_from(b.wr); cerr != nil {
|
||||||
|
n, err = io.read_from(w, r);
|
||||||
|
b.err = err;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
if writer_available(b) == 0 {
|
||||||
|
if ferr := writer_flush(b); ferr != nil {
|
||||||
|
return n, ferr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m: int;
|
||||||
|
nr := 0;
|
||||||
|
for nr < MAX_CONSECUTIVE_EMPTY_READS {
|
||||||
|
m, err = io.read(r, b.buf[b.n:]);
|
||||||
|
if m != 0 || err != nil {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nr += 1;
|
||||||
|
}
|
||||||
|
if nr == MAX_CONSECUTIVE_EMPTY_READS {
|
||||||
|
return n, .No_Progress;
|
||||||
|
}
|
||||||
|
b.n += m;
|
||||||
|
n += i64(m);
|
||||||
|
if err != nil {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == .EOF {
|
||||||
|
if writer_available(b) == 0 {
|
||||||
|
err = writer_flush(b);
|
||||||
|
} else {
|
||||||
|
err = nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// writer_to_stream converts a Writer into an io.Stream
|
||||||
|
writer_to_stream :: proc(b: ^Writer) -> (s: io.Stream) {
|
||||||
|
s.stream_data = b;
|
||||||
|
s.stream_vtable = _writer_vtable;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_writer_vtable := &io.Stream_VTable{
|
||||||
|
impl_destroy = proc(s: io.Stream) -> io.Error {
|
||||||
|
b := (^Writer)(s.stream_data);
|
||||||
|
writer_destroy(b);
|
||||||
|
return nil;
|
||||||
|
},
|
||||||
|
impl_flush = proc(s: io.Stream) -> io.Error {
|
||||||
|
b := (^Writer)(s.stream_data);
|
||||||
|
return writer_flush(b);
|
||||||
|
},
|
||||||
|
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||||
|
b := (^Writer)(s.stream_data);
|
||||||
|
return writer_write(b, p);
|
||||||
|
},
|
||||||
|
impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
|
||||||
|
b := (^Writer)(s.stream_data);
|
||||||
|
return writer_write_byte(b, c);
|
||||||
|
},
|
||||||
|
impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) {
|
||||||
|
b := (^Writer)(s.stream_data);
|
||||||
|
return writer_write_rune(b, r);
|
||||||
|
},
|
||||||
|
impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) {
|
||||||
|
b := (^Writer)(s.stream_data);
|
||||||
|
return writer_read_from(b, r);
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,335 @@
|
|||||||
|
package bytes
|
||||||
|
|
||||||
|
import "core:io"
|
||||||
|
import "core:unicode/utf8"
|
||||||
|
|
||||||
|
MIN_READ :: 512;
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
SMALL_BUFFER_SIZE :: 64;
|
||||||
|
|
||||||
|
Buffer :: struct {
|
||||||
|
buf: [dynamic]byte,
|
||||||
|
off: int,
|
||||||
|
last_read: Read_Op,
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
Read_Op :: enum i8 {
|
||||||
|
Read = -1,
|
||||||
|
Invalid = 0,
|
||||||
|
Read_Rune1 = 1,
|
||||||
|
Read_Rune2 = 2,
|
||||||
|
Read_Rune3 = 3,
|
||||||
|
Read_Rune4 = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
buffer_init :: proc(b: ^Buffer, buf: []byte) {
|
||||||
|
resize(&b.buf, len(buf));
|
||||||
|
copy(b.buf[:], buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_init_string :: proc(b: ^Buffer, s: string) {
|
||||||
|
resize(&b.buf, len(s));
|
||||||
|
copy(b.buf[:], s);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
buffer_destroy :: proc(b: ^Buffer) {
|
||||||
|
delete(b.buf);
|
||||||
|
buffer_reset(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_to_bytes :: proc(b: ^Buffer) -> []byte {
|
||||||
|
return b.buf[b.off:];
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_to_string :: proc(b: ^Buffer) -> string {
|
||||||
|
if b == nil {
|
||||||
|
return "<nil>";
|
||||||
|
}
|
||||||
|
return string(b.buf[b.off:]);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_is_empty :: proc(b: ^Buffer) -> bool {
|
||||||
|
return len(b.buf) <= b.off;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_length :: proc(b: ^Buffer) -> int {
|
||||||
|
return len(b.buf) - b.off;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_capacity :: proc(b: ^Buffer) -> int {
|
||||||
|
return cap(b.buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_reset :: proc(b: ^Buffer) {
|
||||||
|
clear(&b.buf);
|
||||||
|
b.off = 0;
|
||||||
|
b.last_read = .Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
buffer_truncate :: proc(b: ^Buffer, n: int) {
|
||||||
|
if n == 0 {
|
||||||
|
buffer_reset(b);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
b.last_read = .Invalid;
|
||||||
|
if n < 0 || n > buffer_length(b) {
|
||||||
|
panic("bytes.truncate: truncation out of range");
|
||||||
|
}
|
||||||
|
resize(&b.buf, b.off+n);
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_buffer_try_grow :: proc(b: ^Buffer, n: int) -> (int, bool) {
|
||||||
|
if l := len(b.buf); n <= cap(b.buf)-l {
|
||||||
|
resize(&b.buf, l+n);
|
||||||
|
return l, true;
|
||||||
|
}
|
||||||
|
return 0, false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_buffer_grow :: proc(b: ^Buffer, n: int) -> int {
|
||||||
|
m := buffer_length(b);
|
||||||
|
if m == 0 && b.off != 0 {
|
||||||
|
buffer_reset(b);
|
||||||
|
}
|
||||||
|
if i, ok := _buffer_try_grow(b, n); ok {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
if b.buf == nil && n <= SMALL_BUFFER_SIZE {
|
||||||
|
b.buf = make([dynamic]byte, n, SMALL_BUFFER_SIZE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
c := cap(b.buf);
|
||||||
|
if n <= c/2 - m {
|
||||||
|
copy(b.buf[:], b.buf[b.off:]);
|
||||||
|
} else if c > max(int) - c - n {
|
||||||
|
panic("bytes.Buffer: too large");
|
||||||
|
} else {
|
||||||
|
resize(&b.buf, 2*c + n);
|
||||||
|
copy(b.buf[:], b.buf[b.off:]);
|
||||||
|
}
|
||||||
|
b.off = 0;
|
||||||
|
resize(&b.buf, m+n);
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_grow :: proc(b: ^Buffer, n: int) {
|
||||||
|
if n < 0 {
|
||||||
|
panic("bytes.buffer_grow: negative count");
|
||||||
|
}
|
||||||
|
m := _buffer_grow(b, n);
|
||||||
|
resize(&b.buf, m);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
buffer_write :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
|
||||||
|
b.last_read = .Invalid;
|
||||||
|
m, ok := _buffer_try_grow(b, len(p));
|
||||||
|
if !ok {
|
||||||
|
m = _buffer_grow(b, len(p));
|
||||||
|
}
|
||||||
|
return copy(b.buf[m:], p), nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_write_string :: proc(b: ^Buffer, s: string) -> (n: int, err: io.Error) {
|
||||||
|
b.last_read = .Invalid;
|
||||||
|
m, ok := _buffer_try_grow(b, len(s));
|
||||||
|
if !ok {
|
||||||
|
m = _buffer_grow(b, len(s));
|
||||||
|
}
|
||||||
|
return copy(b.buf[m:], s), nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_write_byte :: proc(b: ^Buffer, c: byte) -> io.Error {
|
||||||
|
b.last_read = .Invalid;
|
||||||
|
m, ok := _buffer_try_grow(b, 1);
|
||||||
|
if !ok {
|
||||||
|
m = _buffer_grow(b, 1);
|
||||||
|
}
|
||||||
|
b.buf[m] = c;
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_write_rune :: proc(b: ^Buffer, r: rune) -> (n: int, err: io.Error) {
|
||||||
|
if r < utf8.RUNE_SELF {
|
||||||
|
buffer_write_byte(b, byte(r));
|
||||||
|
return 1, nil;
|
||||||
|
}
|
||||||
|
b.last_read = .Invalid;
|
||||||
|
m, ok := _buffer_try_grow(b, utf8.UTF_MAX);
|
||||||
|
if !ok {
|
||||||
|
m = _buffer_grow(b, utf8.UTF_MAX);
|
||||||
|
}
|
||||||
|
res: [4]byte;
|
||||||
|
res, n = utf8.encode_rune(r);
|
||||||
|
copy(b.buf[m:][:utf8.UTF_MAX], res[:n]);
|
||||||
|
resize(&b.buf, m+n);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_next :: proc(b: ^Buffer, n: int) -> []byte {
|
||||||
|
n := n;
|
||||||
|
b.last_read = .Invalid;
|
||||||
|
m := buffer_length(b);
|
||||||
|
if n > m {
|
||||||
|
n = m;
|
||||||
|
}
|
||||||
|
data := b.buf[b.off : b.off + n];
|
||||||
|
b.off += n;
|
||||||
|
if n > 0 {
|
||||||
|
b.last_read = .Read;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_read :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
|
||||||
|
b.last_read = .Invalid;
|
||||||
|
if buffer_is_empty(b) {
|
||||||
|
buffer_reset(b);
|
||||||
|
if len(p) == 0 {
|
||||||
|
return 0, nil;
|
||||||
|
}
|
||||||
|
return 0, .EOF;
|
||||||
|
}
|
||||||
|
n = copy(p, b.buf[b.off:]);
|
||||||
|
b.off += n;
|
||||||
|
if n > 0 {
|
||||||
|
b.last_read = .Read;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_read_byte :: proc(b: ^Buffer) -> (byte, io.Error) {
|
||||||
|
if buffer_is_empty(b) {
|
||||||
|
buffer_reset(b);
|
||||||
|
return 0, .EOF;
|
||||||
|
}
|
||||||
|
c := b.buf[b.off];
|
||||||
|
b.off += 1;
|
||||||
|
b.last_read = .Read;
|
||||||
|
return c, nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_read_rune :: proc(b: ^Buffer) -> (r: rune, size: int, err: io.Error) {
|
||||||
|
if buffer_is_empty(b) {
|
||||||
|
buffer_reset(b);
|
||||||
|
return 0, 0, .EOF;
|
||||||
|
}
|
||||||
|
c := b.buf[b.off];
|
||||||
|
if c < utf8.RUNE_SELF {
|
||||||
|
b.off += 1;
|
||||||
|
b.last_read = .Read_Rune1;
|
||||||
|
return rune(c), 1, nil;
|
||||||
|
}
|
||||||
|
r, size = utf8.decode_rune(b.buf[b.off:]);
|
||||||
|
b.off += size;
|
||||||
|
b.last_read = Read_Op(i8(size));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_unread_byte :: proc(b: ^Buffer) -> io.Error {
|
||||||
|
if b.last_read == .Invalid {
|
||||||
|
return .Invalid_Unread;
|
||||||
|
}
|
||||||
|
b.last_read = .Invalid;
|
||||||
|
if b.off > 0 {
|
||||||
|
b.off -= 1;
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_unread_rune :: proc(b: ^Buffer) -> io.Error {
|
||||||
|
if b.last_read <= .Invalid {
|
||||||
|
return .Invalid_Unread;
|
||||||
|
}
|
||||||
|
if b.off >= int(b.last_read) {
|
||||||
|
b.off -= int(i8(b.last_read));
|
||||||
|
}
|
||||||
|
b.last_read = .Invalid;
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
buffer_read_bytes :: proc(b: ^Buffer, delim: byte) -> (line: []byte, err: io.Error) {
|
||||||
|
i := index_byte(b.buf[b.off:], delim);
|
||||||
|
end := b.off + i + 1;
|
||||||
|
if i < 0 {
|
||||||
|
end = len(b.buf);
|
||||||
|
err = .EOF;
|
||||||
|
}
|
||||||
|
line = b.buf[b.off:end];
|
||||||
|
b.off = end;
|
||||||
|
b.last_read = .Read;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_read_string :: proc(b: ^Buffer, delim: byte) -> (line: string, err: io.Error) {
|
||||||
|
slice: []byte;
|
||||||
|
slice, err = buffer_read_bytes(b, delim);
|
||||||
|
return string(slice), err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
buffer_to_stream :: proc(b: ^Buffer) -> (s: io.Stream) {
|
||||||
|
s.stream_data = b;
|
||||||
|
s.stream_vtable = _buffer_vtable;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_buffer_vtable := &io.Stream_VTable{
|
||||||
|
impl_size = proc(s: io.Stream) -> i64 {
|
||||||
|
b := (^Buffer)(s.stream_data);
|
||||||
|
return i64(buffer_capacity(b));
|
||||||
|
},
|
||||||
|
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||||
|
b := (^Buffer)(s.stream_data);
|
||||||
|
return buffer_read(b, p);
|
||||||
|
},
|
||||||
|
impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) {
|
||||||
|
b := (^Buffer)(s.stream_data);
|
||||||
|
return buffer_read_byte(b);
|
||||||
|
},
|
||||||
|
impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) {
|
||||||
|
b := (^Buffer)(s.stream_data);
|
||||||
|
return buffer_read_rune(b);
|
||||||
|
},
|
||||||
|
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||||
|
b := (^Buffer)(s.stream_data);
|
||||||
|
return buffer_write(b, p);
|
||||||
|
},
|
||||||
|
impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
|
||||||
|
b := (^Buffer)(s.stream_data);
|
||||||
|
return buffer_write_byte(b, c);
|
||||||
|
},
|
||||||
|
impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) {
|
||||||
|
b := (^Buffer)(s.stream_data);
|
||||||
|
return buffer_write_rune(b, r);
|
||||||
|
},
|
||||||
|
impl_unread_byte = proc(s: io.Stream) -> io.Error {
|
||||||
|
b := (^Buffer)(s.stream_data);
|
||||||
|
return buffer_unread_byte(b);
|
||||||
|
},
|
||||||
|
impl_unread_rune = proc(s: io.Stream) -> io.Error {
|
||||||
|
b := (^Buffer)(s.stream_data);
|
||||||
|
return buffer_unread_rune(b);
|
||||||
|
},
|
||||||
|
impl_destroy = proc(s: io.Stream) -> io.Error {
|
||||||
|
b := (^Buffer)(s.stream_data);
|
||||||
|
buffer_destroy(b);
|
||||||
|
return nil;
|
||||||
|
},
|
||||||
|
|
||||||
|
// TODO(bill): write_to and read_from
|
||||||
|
// impl_write_to = nil,
|
||||||
|
// impl_read_from = nil,
|
||||||
|
};
|
||||||
|
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
package bytes
|
||||||
|
|
||||||
|
import "core:io"
|
||||||
|
import "core:unicode/utf8"
|
||||||
|
|
||||||
|
Reader :: struct {
|
||||||
|
s: []byte, // read-only buffer
|
||||||
|
i: i64, // current reading index
|
||||||
|
prev_rune: int, // previous reading index of rune or < 0
|
||||||
|
}
|
||||||
|
|
||||||
|
reader_init :: proc(r: ^Reader, s: []byte) {
|
||||||
|
r.s = s;
|
||||||
|
r.i = 0;
|
||||||
|
r.prev_rune = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
|
||||||
|
s.stream_data = r;
|
||||||
|
s.stream_vtable = _reader_vtable;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
reader_length :: proc(r: ^Reader) -> int {
|
||||||
|
if r.i >= i64(len(r.s)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return int(i64(len(r.s)) - r.i);
|
||||||
|
}
|
||||||
|
|
||||||
|
reader_size :: proc(r: ^Reader) -> i64 {
|
||||||
|
return i64(len(r.s));
|
||||||
|
}
|
||||||
|
|
||||||
|
reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
|
||||||
|
if r.i >= i64(len(r.s)) {
|
||||||
|
return 0, .EOF;
|
||||||
|
}
|
||||||
|
r.prev_rune = -1;
|
||||||
|
n = copy(p, r.s[r.i:]);
|
||||||
|
r.i += i64(n);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Error) {
|
||||||
|
if off < 0 {
|
||||||
|
return 0, .Invalid_Offset;
|
||||||
|
}
|
||||||
|
if off >= i64(len(r.s)) {
|
||||||
|
return 0, .EOF;
|
||||||
|
}
|
||||||
|
n = copy(p, r.s[off:]);
|
||||||
|
if n < len(p) {
|
||||||
|
err = .EOF;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) {
|
||||||
|
r.prev_rune = -1;
|
||||||
|
if r.i >= i64(len(r.s)) {
|
||||||
|
return 0, .EOF;
|
||||||
|
}
|
||||||
|
b := r.s[r.i];
|
||||||
|
r.i += 1;
|
||||||
|
return b, nil;
|
||||||
|
}
|
||||||
|
reader_unread_byte :: proc(r: ^Reader) -> io.Error {
|
||||||
|
if r.i <= 0 {
|
||||||
|
return .Invalid_Unread;
|
||||||
|
}
|
||||||
|
r.prev_rune = -1;
|
||||||
|
r.i -= 1;
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) {
|
||||||
|
if r.i >= i64(len(r.s)) {
|
||||||
|
r.prev_rune = -1;
|
||||||
|
return 0, 0, .EOF;
|
||||||
|
}
|
||||||
|
r.prev_rune = int(r.i);
|
||||||
|
if c := r.s[r.i]; c < utf8.RUNE_SELF {
|
||||||
|
r.i += 1;
|
||||||
|
return rune(c), 1, nil;
|
||||||
|
}
|
||||||
|
ch, size = utf8.decode_rune(r.s[r.i:]);
|
||||||
|
r.i += i64(size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reader_unread_rune :: proc(r: ^Reader) -> io.Error {
|
||||||
|
if r.i <= 0 {
|
||||||
|
return .Invalid_Unread;
|
||||||
|
}
|
||||||
|
if r.prev_rune < 0 {
|
||||||
|
return .Invalid_Unread;
|
||||||
|
}
|
||||||
|
r.i = i64(r.prev_rune);
|
||||||
|
r.prev_rune = -1;
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
|
||||||
|
r.prev_rune = -1;
|
||||||
|
abs: i64;
|
||||||
|
switch whence {
|
||||||
|
case .Start:
|
||||||
|
abs = offset;
|
||||||
|
case .Current:
|
||||||
|
abs = r.i + offset;
|
||||||
|
case .End:
|
||||||
|
abs = i64(len(r.s)) + offset;
|
||||||
|
case:
|
||||||
|
return 0, .Invalid_Whence;
|
||||||
|
}
|
||||||
|
|
||||||
|
if abs < 0 {
|
||||||
|
return 0, .Invalid_Offset;
|
||||||
|
}
|
||||||
|
r.i = abs;
|
||||||
|
return abs, nil;
|
||||||
|
}
|
||||||
|
reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||||
|
r.prev_rune = -1;
|
||||||
|
if r.i >= i64(len(r.s)) {
|
||||||
|
return 0, nil;
|
||||||
|
}
|
||||||
|
s := r.s[r.i:];
|
||||||
|
m: int;
|
||||||
|
m, err = io.write(w, s);
|
||||||
|
if m > len(s) {
|
||||||
|
panic("bytes.Reader.write_to: invalid io.write_string count");
|
||||||
|
}
|
||||||
|
r.i += i64(m);
|
||||||
|
n = i64(m);
|
||||||
|
if m != len(s) && err == nil {
|
||||||
|
err = .Short_Write;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_reader_vtable := &io.Stream_VTable{
|
||||||
|
impl_size = proc(s: io.Stream) -> i64 {
|
||||||
|
r := (^Reader)(s.stream_data);
|
||||||
|
return reader_size(r);
|
||||||
|
},
|
||||||
|
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||||
|
r := (^Reader)(s.stream_data);
|
||||||
|
return reader_read(r, p);
|
||||||
|
},
|
||||||
|
impl_read_at = proc(s: io.Stream, p: []byte, off: i64) -> (n: int, err: io.Error) {
|
||||||
|
r := (^Reader)(s.stream_data);
|
||||||
|
return reader_read_at(r, p, off);
|
||||||
|
},
|
||||||
|
impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) {
|
||||||
|
r := (^Reader)(s.stream_data);
|
||||||
|
return reader_read_byte(r);
|
||||||
|
},
|
||||||
|
impl_unread_byte = proc(s: io.Stream) -> io.Error {
|
||||||
|
r := (^Reader)(s.stream_data);
|
||||||
|
return reader_unread_byte(r);
|
||||||
|
},
|
||||||
|
impl_read_rune = proc(s: io.Stream) -> (ch: rune, size: int, err: io.Error) {
|
||||||
|
r := (^Reader)(s.stream_data);
|
||||||
|
return reader_read_rune(r);
|
||||||
|
},
|
||||||
|
impl_unread_rune = proc(s: io.Stream) -> io.Error {
|
||||||
|
r := (^Reader)(s.stream_data);
|
||||||
|
return reader_unread_rune(r);
|
||||||
|
},
|
||||||
|
impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
|
||||||
|
r := (^Reader)(s.stream_data);
|
||||||
|
return reader_seek(r, offset, whence);
|
||||||
|
},
|
||||||
|
impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||||
|
r := (^Reader)(s.stream_data);
|
||||||
|
return reader_write_to(r, w);
|
||||||
|
},
|
||||||
|
};
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,3 @@
|
|||||||
package dynlib
|
package dynlib
|
||||||
|
|
||||||
Library :: opaque rawptr;
|
Library :: #opaque rawptr;
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ Marshal_Error :: enum {
|
|||||||
}
|
}
|
||||||
|
|
||||||
marshal :: proc(v: any, allocator := context.allocator) -> ([]byte, Marshal_Error) {
|
marshal :: proc(v: any, allocator := context.allocator) -> ([]byte, Marshal_Error) {
|
||||||
b := strings.make_builder(allocator);
|
b: strings.Builder;
|
||||||
|
strings.init_builder(&b, allocator);
|
||||||
|
|
||||||
err := marshal_arg(&b, v);
|
err := marshal_arg(&b, v);
|
||||||
|
|
||||||
@@ -129,7 +130,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
|||||||
case b32: val = bool(b);
|
case b32: val = bool(b);
|
||||||
case b64: val = bool(b);
|
case b64: val = bool(b);
|
||||||
}
|
}
|
||||||
write_string(b, val ? "true" : "false");
|
write_string_builder(b, val ? "true" : "false");
|
||||||
|
|
||||||
case Type_Info_Any:
|
case Type_Info_Any:
|
||||||
return .Unsupported_Type;
|
return .Unsupported_Type;
|
||||||
@@ -208,14 +209,12 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
|||||||
if i > 0 { write_string(b, ", "); }
|
if i > 0 { write_string(b, ", "); }
|
||||||
|
|
||||||
data := uintptr(entries.data) + uintptr(i*entry_size);
|
data := uintptr(entries.data) + uintptr(i*entry_size);
|
||||||
header := cast(^Map_Entry_Header)data;
|
key := rawptr(data + entry_type.offsets[2]);
|
||||||
|
value := rawptr(data + entry_type.offsets[3]);
|
||||||
marshal_arg(b, any{rawptr(&header.key.key.val), info.key.id});
|
|
||||||
|
|
||||||
|
marshal_arg(b, any{key, info.key.id});
|
||||||
write_string(b, ": ");
|
write_string(b, ": ");
|
||||||
|
marshal_arg(b, any{value, info.value.id});
|
||||||
value := data + entry_type.offsets[2];
|
|
||||||
marshal_arg(b, any{rawptr(value), info.value.id});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
write_byte(b, '}');
|
write_byte(b, '}');
|
||||||
|
|||||||
+311
-275
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,5 @@
|
|||||||
// This is purely for documentation
|
// This is purely for documentation
|
||||||
|
//+ignore
|
||||||
package intrinsics
|
package intrinsics
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
@@ -114,7 +115,7 @@ type_is_ordered_numeric :: proc($T: typeid) -> bool ---
|
|||||||
type_is_indexable :: proc($T: typeid) -> bool ---
|
type_is_indexable :: proc($T: typeid) -> bool ---
|
||||||
type_is_sliceable :: proc($T: typeid) -> bool ---
|
type_is_sliceable :: proc($T: typeid) -> bool ---
|
||||||
type_is_comparable :: proc($T: typeid) -> bool ---
|
type_is_comparable :: proc($T: typeid) -> bool ---
|
||||||
type_is_simple_compare :: proc($T: typeid) -> bool --- // easily compared using memcmp
|
type_is_simple_compare :: proc($T: typeid) -> bool --- // easily compared using memcmp (== and !=)
|
||||||
type_is_dereferenceable :: proc($T: typeid) -> bool ---
|
type_is_dereferenceable :: proc($T: typeid) -> bool ---
|
||||||
type_is_valid_map_key :: proc($T: typeid) -> bool ---
|
type_is_valid_map_key :: proc($T: typeid) -> bool ---
|
||||||
|
|
||||||
@@ -152,3 +153,6 @@ type_polymorphic_record_parameter_value :: proc($T: typeid, index: int) -> $V --
|
|||||||
|
|
||||||
|
|
||||||
type_field_index_of :: proc($T: typeid, $name: string) -> uintptr ---
|
type_field_index_of :: proc($T: typeid, $name: string) -> uintptr ---
|
||||||
|
|
||||||
|
type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, rawptr) -> bool) ---
|
||||||
|
type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) ---
|
||||||
|
|||||||
@@ -0,0 +1,200 @@
|
|||||||
|
package io
|
||||||
|
|
||||||
|
Conversion_Error :: enum {
|
||||||
|
None,
|
||||||
|
Missing_Procedure,
|
||||||
|
Fallback_Possible,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_reader :: proc(s: Stream) -> (r: Reader, err: Conversion_Error) {
|
||||||
|
r.stream = s;
|
||||||
|
if s.stream_vtable == nil || s.impl_read == nil {
|
||||||
|
err = .Missing_Procedure;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
to_writer :: proc(s: Stream) -> (w: Writer, err: Conversion_Error) {
|
||||||
|
w.stream = s;
|
||||||
|
if s.stream_vtable == nil || s.impl_write == nil {
|
||||||
|
err = .Missing_Procedure;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
to_closer :: proc(s: Stream) -> (c: Closer, err: Conversion_Error) {
|
||||||
|
c.stream = s;
|
||||||
|
if s.stream_vtable == nil || s.impl_close == nil {
|
||||||
|
err = .Missing_Procedure;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
to_flusher :: proc(s: Stream) -> (f: Flusher, err: Conversion_Error) {
|
||||||
|
f.stream = s;
|
||||||
|
if s.stream_vtable == nil || s.impl_flush == nil {
|
||||||
|
err = .Missing_Procedure;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
to_seeker :: proc(s: Stream) -> (seeker: Seeker, err: Conversion_Error) {
|
||||||
|
seeker.stream = s;
|
||||||
|
if s.stream_vtable == nil || s.impl_seek == nil {
|
||||||
|
err = .Missing_Procedure;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
to_read_writer :: proc(s: Stream) -> (r: Read_Writer, err: Conversion_Error) {
|
||||||
|
r.stream = s;
|
||||||
|
if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil {
|
||||||
|
err = .Missing_Procedure;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
to_read_closer :: proc(s: Stream) -> (r: Read_Closer, err: Conversion_Error) {
|
||||||
|
r.stream = s;
|
||||||
|
if s.stream_vtable == nil || s.impl_read == nil || s.impl_close == nil {
|
||||||
|
err = .Missing_Procedure;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
to_read_write_closer :: proc(s: Stream) -> (r: Read_Write_Closer, err: Conversion_Error) {
|
||||||
|
r.stream = s;
|
||||||
|
if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil || s.impl_close == nil {
|
||||||
|
err = .Missing_Procedure;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
to_read_write_seeker :: proc(s: Stream) -> (r: Read_Write_Seeker, err: Conversion_Error) {
|
||||||
|
r.stream = s;
|
||||||
|
if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil || s.impl_seek == nil {
|
||||||
|
err = .Missing_Procedure;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
to_write_flusher :: proc(s: Stream) -> (w: Write_Flusher, err: Conversion_Error) {
|
||||||
|
w.stream = s;
|
||||||
|
if s.stream_vtable == nil || s.impl_write == nil || s.impl_flush == nil {
|
||||||
|
err = .Missing_Procedure;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
to_write_flush_closer :: proc(s: Stream) -> (w: Write_Flush_Closer, err: Conversion_Error) {
|
||||||
|
w.stream = s;
|
||||||
|
if s.stream_vtable == nil || s.impl_write == nil || s.impl_flush == nil || s.impl_close == nil {
|
||||||
|
err = .Missing_Procedure;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
to_reader_at :: proc(s: Stream) -> (r: Reader_At, err: Conversion_Error) {
|
||||||
|
r.stream = s;
|
||||||
|
if s.stream_vtable == nil || s.impl_read_at == nil {
|
||||||
|
err = .Missing_Procedure;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
to_writer_at :: proc(s: Stream) -> (w: Writer_At, err: Conversion_Error) {
|
||||||
|
w.stream = s;
|
||||||
|
if s.stream_vtable == nil || s.impl_write_at == nil {
|
||||||
|
err = .Missing_Procedure;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
to_reader_from :: proc(s: Stream) -> (r: Reader_From, err: Conversion_Error) {
|
||||||
|
r.stream = s;
|
||||||
|
if s.stream_vtable == nil || s.impl_read_from == nil {
|
||||||
|
err = .Missing_Procedure;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
to_writer_to :: proc(s: Stream) -> (w: Writer_To, err: Conversion_Error) {
|
||||||
|
w.stream = s;
|
||||||
|
if s.stream_vtable == nil || s.impl_write_to == nil {
|
||||||
|
err = .Missing_Procedure;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
to_write_closer :: proc(s: Stream) -> (w: Write_Closer, err: Conversion_Error) {
|
||||||
|
w.stream = s;
|
||||||
|
if s.stream_vtable == nil || s.impl_write == nil || s.impl_close == nil {
|
||||||
|
err = .Missing_Procedure;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
to_write_seeker :: proc(s: Stream) -> (w: Write_Seeker, err: Conversion_Error) {
|
||||||
|
w.stream = s;
|
||||||
|
if s.stream_vtable == nil || s.impl_write == nil || s.impl_seek == nil {
|
||||||
|
err = .Missing_Procedure;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
to_byte_reader :: proc(s: Stream) -> (b: Byte_Reader, err: Conversion_Error) {
|
||||||
|
b.stream = s;
|
||||||
|
if s.stream_vtable == nil || s.impl_read_byte == nil {
|
||||||
|
err = .Missing_Procedure;
|
||||||
|
if s.stream_vtable != nil && s.impl_read != nil {
|
||||||
|
err = .Fallback_Possible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
to_byte_scanner :: proc(s: Stream) -> (b: Byte_Scanner, err: Conversion_Error) {
|
||||||
|
b.stream = s;
|
||||||
|
if s.stream_vtable != nil {
|
||||||
|
if s.impl_unread_byte == nil {
|
||||||
|
err = .Missing_Procedure;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if s.impl_read_byte != nil {
|
||||||
|
err = .None;
|
||||||
|
} else if s.impl_read != nil {
|
||||||
|
err = .Fallback_Possible;
|
||||||
|
} else {
|
||||||
|
err = .Missing_Procedure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
to_byte_writer :: proc(s: Stream) -> (b: Byte_Writer, err: Conversion_Error) {
|
||||||
|
b.stream = s;
|
||||||
|
if s.stream_vtable == nil || s.impl_write_byte == nil {
|
||||||
|
err = .Missing_Procedure;
|
||||||
|
if s.stream_vtable != nil && s.impl_write != nil {
|
||||||
|
err = .Fallback_Possible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
to_rune_reader :: proc(s: Stream) -> (r: Rune_Reader, err: Conversion_Error) {
|
||||||
|
r.stream = s;
|
||||||
|
if s.stream_vtable == nil || s.impl_read_rune == nil {
|
||||||
|
err = .Missing_Procedure;
|
||||||
|
if s.stream_vtable != nil && s.impl_read != nil {
|
||||||
|
err = .Fallback_Possible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
to_rune_scanner :: proc(s: Stream) -> (r: Rune_Scanner, err: Conversion_Error) {
|
||||||
|
r.stream = s;
|
||||||
|
if s.stream_vtable != nil {
|
||||||
|
if s.impl_unread_rune == nil {
|
||||||
|
err = .Missing_Procedure;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if s.impl_read_rune != nil {
|
||||||
|
err = .None;
|
||||||
|
} else if s.impl_read != nil {
|
||||||
|
err = .Fallback_Possible;
|
||||||
|
} else {
|
||||||
|
err = .Missing_Procedure;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = .Missing_Procedure;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
+504
@@ -0,0 +1,504 @@
|
|||||||
|
package io
|
||||||
|
|
||||||
|
import "intrinsics"
|
||||||
|
import "core:runtime"
|
||||||
|
import "core:unicode/utf8"
|
||||||
|
|
||||||
|
Seek_From :: enum {
|
||||||
|
Start = 0, // seek relative to the origin of the file
|
||||||
|
Current = 1, // seek relative to the current offset
|
||||||
|
End = 2, // seek relative to the end
|
||||||
|
}
|
||||||
|
|
||||||
|
Error :: enum i32 {
|
||||||
|
// No Error
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
// EOF is the error returned by `read` when no more input is available
|
||||||
|
EOF,
|
||||||
|
|
||||||
|
// Unexpected_EOF means that EOF was encountered in the middle of reading a fixed-sized block of data
|
||||||
|
Unexpected_EOF,
|
||||||
|
|
||||||
|
// Short_Write means that a write accepted fewer bytes than requested but failed to return an explicit error
|
||||||
|
Short_Write,
|
||||||
|
|
||||||
|
// Short_Buffer means that a read required a longer buffer than was provided
|
||||||
|
Short_Buffer,
|
||||||
|
|
||||||
|
// No_Progress is returned by some implementations of `io.Reader` when many calls
|
||||||
|
// to `read` have failed to return any data or error.
|
||||||
|
// This is usually a signed of a broken `io.Reader` implementation
|
||||||
|
No_Progress,
|
||||||
|
|
||||||
|
Invalid_Whence,
|
||||||
|
Invalid_Offset,
|
||||||
|
Invalid_Unread,
|
||||||
|
|
||||||
|
Negative_Read,
|
||||||
|
Negative_Write,
|
||||||
|
Negative_Count,
|
||||||
|
Buffer_Full,
|
||||||
|
|
||||||
|
// Empty is returned when a procedure has not been implemented for an io.Stream
|
||||||
|
Empty = -1,
|
||||||
|
}
|
||||||
|
|
||||||
|
Close_Proc :: proc(using s: Stream) -> Error;
|
||||||
|
Flush_Proc :: proc(using s: Stream) -> Error;
|
||||||
|
Seek_Proc :: proc(using s: Stream, offset: i64, whence: Seek_From) -> (n: i64, err: Error);
|
||||||
|
Size_Proc :: proc(using s: Stream) -> i64;
|
||||||
|
Read_Proc :: proc(using s: Stream, p: []byte) -> (n: int, err: Error);
|
||||||
|
Read_At_Proc :: proc(using s: Stream, p: []byte, off: i64) -> (n: int, err: Error);
|
||||||
|
Read_From_Proc :: proc(using s: Stream, r: Reader) -> (n: i64, err: Error);
|
||||||
|
Read_Byte_Proc :: proc(using s: Stream) -> (byte, Error);
|
||||||
|
Read_Rune_Proc :: proc(using s: Stream) -> (ch: rune, size: int, err: Error);
|
||||||
|
Unread_Byte_Proc :: proc(using s: Stream) -> Error;
|
||||||
|
Unread_Rune_Proc :: proc(using s: Stream) -> Error;
|
||||||
|
Write_Proc :: proc(using s: Stream, p: []byte) -> (n: int, err: Error);
|
||||||
|
Write_At_Proc :: proc(using s: Stream, p: []byte, off: i64) -> (n: int, err: Error);
|
||||||
|
Write_To_Proc :: proc(using s: Stream, w: Writer) -> (n: i64, err: Error);
|
||||||
|
Write_Byte_Proc :: proc(using s: Stream, c: byte) -> Error;
|
||||||
|
Write_Rune_Proc :: proc(using s: Stream, r: rune) -> (size: int, err: Error);
|
||||||
|
Destroy_Proc :: proc(using s: Stream) -> Error;
|
||||||
|
|
||||||
|
|
||||||
|
Stream :: struct {
|
||||||
|
using stream_vtable: ^Stream_VTable,
|
||||||
|
stream_data: rawptr,
|
||||||
|
}
|
||||||
|
Stream_VTable :: struct {
|
||||||
|
impl_close: Close_Proc,
|
||||||
|
impl_flush: Flush_Proc,
|
||||||
|
|
||||||
|
impl_seek: Seek_Proc,
|
||||||
|
impl_size: Size_Proc,
|
||||||
|
|
||||||
|
impl_read: Read_Proc,
|
||||||
|
impl_read_at: Read_At_Proc,
|
||||||
|
impl_read_byte: Read_Byte_Proc,
|
||||||
|
impl_read_rune: Read_Rune_Proc,
|
||||||
|
impl_write_to: Write_To_Proc,
|
||||||
|
|
||||||
|
impl_write: Write_Proc,
|
||||||
|
impl_write_at: Write_At_Proc,
|
||||||
|
impl_write_byte: Write_Byte_Proc,
|
||||||
|
impl_write_rune: Write_Rune_Proc,
|
||||||
|
impl_read_from: Read_From_Proc,
|
||||||
|
|
||||||
|
impl_unread_byte: Unread_Byte_Proc,
|
||||||
|
impl_unread_rune: Unread_Rune_Proc,
|
||||||
|
|
||||||
|
impl_destroy: Destroy_Proc,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Reader :: struct {using stream: Stream};
|
||||||
|
Writer :: struct {using stream: Stream};
|
||||||
|
Closer :: struct {using stream: Stream};
|
||||||
|
Flusher :: struct {using stream: Stream};
|
||||||
|
Seeker :: struct {using stream: Stream};
|
||||||
|
|
||||||
|
Read_Writer :: struct {using stream: Stream};
|
||||||
|
Read_Closer :: struct {using stream: Stream};
|
||||||
|
Read_Write_Closer :: struct {using stream: Stream};
|
||||||
|
Read_Write_Seeker :: struct {using stream: Stream};
|
||||||
|
|
||||||
|
Write_Closer :: struct {using stream: Stream};
|
||||||
|
Write_Seeker :: struct {using stream: Stream};
|
||||||
|
Write_Flusher :: struct {using stream: Stream};
|
||||||
|
Write_Flush_Closer :: struct {using stream: Stream};
|
||||||
|
|
||||||
|
Reader_At :: struct {using stream: Stream};
|
||||||
|
Writer_At :: struct {using stream: Stream};
|
||||||
|
Reader_From :: struct {using stream: Stream};
|
||||||
|
Writer_To :: struct {using stream: Stream};
|
||||||
|
|
||||||
|
Byte_Reader :: struct {using stream: Stream};
|
||||||
|
Byte_Scanner :: struct {using stream: Stream};
|
||||||
|
Byte_Writer :: struct {using stream: Stream};
|
||||||
|
|
||||||
|
Rune_Reader :: struct {using stream: Stream};
|
||||||
|
Rune_Scanner :: struct {using stream: Stream};
|
||||||
|
|
||||||
|
|
||||||
|
destroy :: proc(s: Stream) -> Error {
|
||||||
|
close_err := close({s});
|
||||||
|
if s.stream_vtable != nil && s.impl_destroy != nil {
|
||||||
|
return s->impl_destroy();
|
||||||
|
}
|
||||||
|
if close_err != .None {
|
||||||
|
return close_err;
|
||||||
|
}
|
||||||
|
return .Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
read :: proc(s: Reader, p: []byte) -> (n: int, err: Error) {
|
||||||
|
if s.stream_vtable != nil && s.impl_read != nil {
|
||||||
|
return s->impl_read(p);
|
||||||
|
}
|
||||||
|
return 0, .Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
write :: proc(s: Writer, p: []byte) -> (n: int, err: Error) {
|
||||||
|
if s.stream_vtable != nil && s.impl_write != nil {
|
||||||
|
return s->impl_write(p);
|
||||||
|
}
|
||||||
|
return 0, .Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
seek :: proc(s: Seeker, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
|
||||||
|
if s.stream_vtable != nil && s.impl_seek != nil {
|
||||||
|
return s->impl_seek(offset, whence);
|
||||||
|
}
|
||||||
|
return 0, .Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
close :: proc(s: Closer) -> Error {
|
||||||
|
if s.stream_vtable != nil && s.impl_close != nil {
|
||||||
|
return s->impl_close();
|
||||||
|
}
|
||||||
|
// Instead of .Empty, .None is fine in this case
|
||||||
|
return .None;
|
||||||
|
}
|
||||||
|
|
||||||
|
flush :: proc(s: Flusher) -> Error {
|
||||||
|
if s.stream_vtable != nil && s.impl_flush != nil {
|
||||||
|
return s->impl_flush();
|
||||||
|
}
|
||||||
|
// Instead of .Empty, .None is fine in this case
|
||||||
|
return .None;
|
||||||
|
}
|
||||||
|
|
||||||
|
size :: proc(s: Stream) -> i64 {
|
||||||
|
if s.stream_vtable == nil {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if s.impl_size != nil {
|
||||||
|
return s->impl_size();
|
||||||
|
}
|
||||||
|
if s.impl_seek == nil {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
curr, end: i64;
|
||||||
|
err: Error;
|
||||||
|
if curr, err = s->impl_seek(0, .Current); err != nil {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if end, err = s->impl_seek(0, .End); err != nil {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = s->impl_seek(curr, .Start); err != nil {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
read_at :: proc(r: Reader_At, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||||
|
if r.stream_vtable == nil {
|
||||||
|
return 0, .Empty;
|
||||||
|
}
|
||||||
|
if r.impl_read_at != nil {
|
||||||
|
return r->impl_read_at(p, offset);
|
||||||
|
}
|
||||||
|
if r.impl_seek == nil || r.impl_read == nil {
|
||||||
|
return 0, .Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
current_offset: i64;
|
||||||
|
current_offset, err = r->impl_seek(offset, .Current);
|
||||||
|
if err != nil {
|
||||||
|
return 0, err;
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err = r->impl_read(p);
|
||||||
|
if err != nil {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_, err = r->impl_seek(current_offset, .Start);
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
write_at :: proc(w: Writer_At, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||||
|
if w.stream_vtable == nil {
|
||||||
|
return 0, .Empty;
|
||||||
|
}
|
||||||
|
if w.impl_write_at != nil {
|
||||||
|
return w->impl_write_at(p, offset);
|
||||||
|
}
|
||||||
|
if w.impl_seek == nil || w.impl_write == nil {
|
||||||
|
return 0, .Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
current_offset: i64;
|
||||||
|
current_offset, err = w->impl_seek(offset, .Current);
|
||||||
|
if err != nil {
|
||||||
|
return 0, err;
|
||||||
|
}
|
||||||
|
defer w->impl_seek(current_offset, .Start);
|
||||||
|
|
||||||
|
return w->impl_write(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
write_to :: proc(r: Writer_To, w: Writer) -> (n: i64, err: Error) {
|
||||||
|
if r.stream_vtable == nil || w.stream_vtable == nil {
|
||||||
|
return 0, .Empty;
|
||||||
|
}
|
||||||
|
if r.impl_write_to != nil {
|
||||||
|
return r->impl_write_to(w);
|
||||||
|
}
|
||||||
|
return 0, .Empty;
|
||||||
|
}
|
||||||
|
read_from :: proc(w: Reader_From, r: Reader) -> (n: i64, err: Error) {
|
||||||
|
if r.stream_vtable == nil || w.stream_vtable == nil {
|
||||||
|
return 0, .Empty;
|
||||||
|
}
|
||||||
|
if r.impl_read_from != nil {
|
||||||
|
return w->impl_read_from(r);
|
||||||
|
}
|
||||||
|
return 0, .Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
read_byte :: proc(r: Byte_Reader) -> (byte, Error) {
|
||||||
|
if r.stream_vtable == nil {
|
||||||
|
return 0, .Empty;
|
||||||
|
}
|
||||||
|
if r.impl_read_byte != nil {
|
||||||
|
return r->impl_read_byte();
|
||||||
|
}
|
||||||
|
if r.impl_read == nil {
|
||||||
|
return 0, .Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
b: [1]byte;
|
||||||
|
_, err := r->impl_read(b[:]);
|
||||||
|
return b[0], err;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_byte :: proc{
|
||||||
|
write_byte_to_byte_writer,
|
||||||
|
write_byte_to_writer,
|
||||||
|
};
|
||||||
|
|
||||||
|
write_byte_to_byte_writer :: proc(w: Byte_Writer, c: byte) -> Error {
|
||||||
|
return _write_byte(auto_cast w, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
write_byte_to_writer :: proc(w: Writer, c: byte) -> Error {
|
||||||
|
return _write_byte(auto_cast w, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_write_byte :: proc(w: Byte_Writer, c: byte) -> Error {
|
||||||
|
if w.stream_vtable == nil {
|
||||||
|
return .Empty;
|
||||||
|
}
|
||||||
|
if w.impl_write_byte != nil {
|
||||||
|
return w->impl_write_byte(c);
|
||||||
|
}
|
||||||
|
if w.impl_write == nil {
|
||||||
|
return .Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
b := [1]byte{c};
|
||||||
|
_, err := w->impl_write(b[:]);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
read_rune :: proc(br: Rune_Reader) -> (ch: rune, size: int, err: Error) {
|
||||||
|
if br.stream_vtable == nil {
|
||||||
|
return 0, 0, .Empty;
|
||||||
|
}
|
||||||
|
if br.impl_read_rune != nil {
|
||||||
|
return br->impl_read_rune();
|
||||||
|
}
|
||||||
|
if br.impl_read == nil {
|
||||||
|
return 0, 0, .Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
b: [utf8.UTF_MAX]byte;
|
||||||
|
_, err = br->impl_read(b[:1]);
|
||||||
|
|
||||||
|
s0 := b[0];
|
||||||
|
ch = rune(s0);
|
||||||
|
size = 1;
|
||||||
|
if err != nil {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ch < utf8.RUNE_SELF {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
x := utf8.accept_sizes[s0];
|
||||||
|
if x >= 0xf0 {
|
||||||
|
mask := rune(x) << 31 >> 31;
|
||||||
|
ch = ch &~ mask | utf8.RUNE_ERROR&mask;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sz := int(x&7);
|
||||||
|
n: int;
|
||||||
|
n, err = br->impl_read(b[1:sz]);
|
||||||
|
if err != nil || n+1 < sz {
|
||||||
|
ch = utf8.RUNE_ERROR;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ch, size = utf8.decode_rune(b[:sz]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unread_byte :: proc(s: Byte_Scanner) -> Error {
|
||||||
|
if s.stream_vtable != nil && s.impl_unread_byte != nil {
|
||||||
|
return s->impl_unread_byte();
|
||||||
|
}
|
||||||
|
return .Empty;
|
||||||
|
}
|
||||||
|
unread_rune :: proc(s: Rune_Scanner) -> Error {
|
||||||
|
if s.stream_vtable != nil && s.impl_unread_rune != nil {
|
||||||
|
return s->impl_unread_rune();
|
||||||
|
}
|
||||||
|
return .Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
write_string :: proc(s: Writer, str: string) -> (n: int, err: Error) {
|
||||||
|
return write(s, transmute([]byte)str);
|
||||||
|
}
|
||||||
|
|
||||||
|
write_rune :: proc(s: Writer, r: rune) -> (size: int, err: Error) {
|
||||||
|
if s.stream_vtable != nil && s.impl_write_rune != nil {
|
||||||
|
return s->impl_write_rune(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
if r < utf8.RUNE_SELF {
|
||||||
|
err = write_byte(s, byte(r));
|
||||||
|
if err == nil {
|
||||||
|
size = 1;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
buf, w := utf8.encode_rune(r);
|
||||||
|
return write(s, buf[:w]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
read_full :: proc(r: Reader, buf: []byte) -> (n: int, err: Error) {
|
||||||
|
return read_at_least(r, buf, len(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
read_at_least :: proc(r: Reader, buf: []byte, min: int) -> (n: int, err: Error) {
|
||||||
|
if len(buf) < min {
|
||||||
|
return 0, .Short_Buffer;
|
||||||
|
}
|
||||||
|
for n < min && err == nil {
|
||||||
|
nn: int;
|
||||||
|
nn, err = read(r, buf[n:]);
|
||||||
|
n += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
if n >= min {
|
||||||
|
err = nil;
|
||||||
|
} else if n > 0 && err == .EOF {
|
||||||
|
err = .Unexpected_EOF;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy copies from src to dst till either EOF is reached on src or an error occurs
|
||||||
|
// It returns the number of bytes copied and the first error that occurred whilst copying, if any.
|
||||||
|
copy :: proc(dst: Writer, src: Reader) -> (written: i64, err: Error) {
|
||||||
|
return _copy_buffer(dst, src, nil);
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy_buffer is the same as copy except that it stages through the provided buffer (if one is required)
|
||||||
|
// rather than allocating a temporary one on the stack through `intrinsics.alloca`
|
||||||
|
// If buf is `nil`, it is allocate through `intrinsics.alloca`; otherwise if it has zero length, it will panic
|
||||||
|
copy_buffer :: proc(dst: Writer, src: Reader, buf: []byte) -> (written: i64, err: Error) {
|
||||||
|
if buf != nil && len(buf) == 0 {
|
||||||
|
panic("empty buffer in io.copy_buffer");
|
||||||
|
}
|
||||||
|
return _copy_buffer(dst, src, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// copy_n copies n bytes (or till an error) from src to dst.
|
||||||
|
// It returns the number of bytes copied and the first error that occurred whilst copying, if any.
|
||||||
|
// On return, written == n IFF err == nil
|
||||||
|
copy_n :: proc(dst: Writer, src: Reader, n: i64) -> (written: i64, err: Error) {
|
||||||
|
nsrc := inline_limited_reader(&Limited_Reader{}, src, n);
|
||||||
|
written, err = copy(dst, nsrc);
|
||||||
|
if written == n {
|
||||||
|
return n, nil;
|
||||||
|
}
|
||||||
|
if written < n && err == nil {
|
||||||
|
// src stopped early and must have been an EOF
|
||||||
|
err = .EOF;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_copy_buffer :: proc(dst: Writer, src: Reader, buf: []byte) -> (written: i64, err: Error) {
|
||||||
|
if dst.stream_vtable == nil || src.stream_vtable == nil {
|
||||||
|
return 0, .Empty;
|
||||||
|
}
|
||||||
|
if src.impl_write_to != nil {
|
||||||
|
return src->impl_write_to(dst);
|
||||||
|
}
|
||||||
|
if src.impl_read_from != nil {
|
||||||
|
return dst->impl_read_from(src);
|
||||||
|
}
|
||||||
|
buf := buf;
|
||||||
|
if buf == nil {
|
||||||
|
DEFAULT_SIZE :: 4 * 1024;
|
||||||
|
size := DEFAULT_SIZE;
|
||||||
|
if src.stream_vtable == _limited_reader_vtable {
|
||||||
|
l := (^Limited_Reader)(src.stream_data);
|
||||||
|
if i64(size) > l.n {
|
||||||
|
if l.n < 1 {
|
||||||
|
size = 1;
|
||||||
|
} else {
|
||||||
|
size = int(l.n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// NOTE(bill): alloca is fine here
|
||||||
|
buf = transmute([]byte)runtime.Raw_Slice{intrinsics.alloca(size, 2*align_of(rawptr)), size};
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
nr, er := read(src, buf);
|
||||||
|
if nr > 0 {
|
||||||
|
nw, ew := write(dst, buf[0:nr]);
|
||||||
|
if nw > 0 {
|
||||||
|
written += i64(nw);
|
||||||
|
}
|
||||||
|
if ew != nil {
|
||||||
|
err = ew;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if nr != nw {
|
||||||
|
err = .Short_Write;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if er != nil {
|
||||||
|
if er != .EOF {
|
||||||
|
err = er;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
package io
|
||||||
|
|
||||||
|
import "core:runtime"
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
Multi_Reader :: struct {
|
||||||
|
readers: [dynamic]Reader,
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_multi_reader_vtable := &Stream_VTable{
|
||||||
|
impl_read = proc(s: Stream, p: []byte) -> (n: int, err: Error) {
|
||||||
|
mr := (^Multi_Reader)(s.stream_data);
|
||||||
|
for len(mr.readers) > 0 {
|
||||||
|
r := mr.readers[0];
|
||||||
|
n, err = read(r, p);
|
||||||
|
if err == .EOF {
|
||||||
|
ordered_remove(&mr.readers, 0);
|
||||||
|
}
|
||||||
|
if n > 0 || err != .EOF {
|
||||||
|
if err == .EOF && len(mr.readers) > 0 {
|
||||||
|
// Don't return EOF yet, more readers remain
|
||||||
|
err = nil;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, .EOF;
|
||||||
|
},
|
||||||
|
impl_destroy = proc(s: Stream) -> Error {
|
||||||
|
mr := (^Multi_Reader)(s.stream_data);
|
||||||
|
context.allocator = mr.readers.allocator;
|
||||||
|
delete(mr.readers);
|
||||||
|
free(mr);
|
||||||
|
return .None;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
multi_reader :: proc(readers: ..Reader, allocator := context.allocator) -> (r: Reader) {
|
||||||
|
context.allocator = allocator;
|
||||||
|
mr := new(Multi_Reader);
|
||||||
|
all_readers := make([dynamic]Reader, 0, len(readers));
|
||||||
|
|
||||||
|
for w in readers {
|
||||||
|
if w.stream_vtable == _multi_reader_vtable {
|
||||||
|
other := (^Multi_Reader)(w.stream_data);
|
||||||
|
append(&all_readers, ..other.readers[:]);
|
||||||
|
} else {
|
||||||
|
append(&all_readers, w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mr.readers = all_readers;
|
||||||
|
|
||||||
|
r.stream_vtable = _multi_reader_vtable;
|
||||||
|
r.stream_data = mr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
Multi_Writer :: struct {
|
||||||
|
writers: []Writer,
|
||||||
|
allocator: runtime.Allocator,
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_multi_writer_vtable := &Stream_VTable{
|
||||||
|
impl_write = proc(s: Stream, p: []byte) -> (n: int, err: Error) {
|
||||||
|
mw := (^Multi_Writer)(s.stream_data);
|
||||||
|
for w in mw.writers {
|
||||||
|
n, err = write(w, p);
|
||||||
|
if err != nil {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if n != len(p) {
|
||||||
|
err = .Short_Write;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(p), nil;
|
||||||
|
},
|
||||||
|
impl_destroy = proc(s: Stream) -> Error {
|
||||||
|
mw := (^Multi_Writer)(s.stream_data);
|
||||||
|
context.allocator = mw.allocator;
|
||||||
|
delete(mw.writers);
|
||||||
|
free(mw);
|
||||||
|
return .None;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
multi_writer :: proc(writers: ..Writer, allocator := context.allocator) -> (out: Writer) {
|
||||||
|
context.allocator = allocator;
|
||||||
|
mw := new(Multi_Writer);
|
||||||
|
mw.allocator = allocator;
|
||||||
|
all_writers := make([dynamic]Writer, 0, len(writers));
|
||||||
|
|
||||||
|
for w in writers {
|
||||||
|
if w.stream_vtable == _multi_writer_vtable {
|
||||||
|
other := (^Multi_Writer)(w.stream_data);
|
||||||
|
append(&all_writers, ..other.writers);
|
||||||
|
} else {
|
||||||
|
append(&all_writers, w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mw.writers = all_writers[:];
|
||||||
|
|
||||||
|
out.stream_vtable = _multi_writer_vtable;
|
||||||
|
out.stream_data = mw;
|
||||||
|
return;
|
||||||
|
}
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
package io
|
||||||
|
|
||||||
|
import "core:runtime"
|
||||||
|
import "core:strconv"
|
||||||
|
|
||||||
|
write_u64 :: proc(w: Writer, i: u64, base: int = 10) -> (n: int, err: Error) {
|
||||||
|
buf: [32]byte;
|
||||||
|
s := strconv.append_bits(buf[:], u64(i), base, false, 64, strconv.digits, nil);
|
||||||
|
return write_string(w, s);
|
||||||
|
}
|
||||||
|
write_i64 :: proc(w: Writer, i: i64, base: int = 10) -> (n: int, err: Error) {
|
||||||
|
buf: [32]byte;
|
||||||
|
s := strconv.append_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil);
|
||||||
|
return write_string(w, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
write_uint :: proc(w: Writer, i: uint, base: int = 10) -> (n: int, err: Error) {
|
||||||
|
return write_u64(w, u64(i), base);
|
||||||
|
}
|
||||||
|
write_int :: proc(w: Writer, i: int, base: int = 10) -> (n: int, err: Error) {
|
||||||
|
return write_i64(w, i64(i), base);
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
Tee_Reader :: struct {
|
||||||
|
r: Reader,
|
||||||
|
w: Writer,
|
||||||
|
allocator: runtime.Allocator,
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_tee_reader_vtable := &Stream_VTable{
|
||||||
|
impl_read = proc(s: Stream, p: []byte) -> (n: int, err: Error) {
|
||||||
|
t := (^Tee_Reader)(s.stream_data);
|
||||||
|
n, err = read(t.r, p);
|
||||||
|
if n > 0 {
|
||||||
|
if wn, werr := write(t.w, p[:n]); werr != nil {
|
||||||
|
return wn, werr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
impl_destroy = proc(s: Stream) -> Error {
|
||||||
|
t := (^Tee_Reader)(s.stream_data);
|
||||||
|
allocator := t.allocator;
|
||||||
|
free(t, allocator);
|
||||||
|
return .None;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// tee_reader returns a Reader that writes to 'w' what it reads from 'r'
|
||||||
|
// All reads from 'r' performed through it are matched with a corresponding write to 'w'
|
||||||
|
// There is no internal buffering done
|
||||||
|
// The write must complete before th read completes
|
||||||
|
// Any error encountered whilst writing is reported as a 'read' error
|
||||||
|
// tee_reader must call io.destroy when done with
|
||||||
|
tee_reader :: proc(r: Reader, w: Writer, allocator := context.allocator) -> (out: Reader) {
|
||||||
|
t := new(Tee_Reader, allocator);
|
||||||
|
t.r, t.w = r, w;
|
||||||
|
t.allocator = allocator;
|
||||||
|
|
||||||
|
out.stream_data = t;
|
||||||
|
out.stream_vtable = _tee_reader_vtable;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// A Limited_Reader reads from r but limits the amount of data returned to just n bytes.
|
||||||
|
// Each call to read updates n to reflect the new amount remaining.
|
||||||
|
// read returns EOF when n <= 0 or when the underlying r returns EOF.
|
||||||
|
Limited_Reader :: struct {
|
||||||
|
r: Reader, // underlying reader
|
||||||
|
n: i64, // max_bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_limited_reader_vtable := &Stream_VTable{
|
||||||
|
impl_read = proc(s: Stream, p: []byte) -> (n: int, err: Error) {
|
||||||
|
l := (^Limited_Reader)(s.stream_data);
|
||||||
|
if l.n <= 0 {
|
||||||
|
return 0, .EOF;
|
||||||
|
}
|
||||||
|
p := p;
|
||||||
|
if i64(len(p)) > l.n {
|
||||||
|
p = p[0:l.n];
|
||||||
|
}
|
||||||
|
n, err = read(l.r, p);
|
||||||
|
l.n -= i64(n);
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
new_limited_reader :: proc(r: Reader, n: i64) -> ^Limited_Reader {
|
||||||
|
l := new(Limited_Reader);
|
||||||
|
l.r = r;
|
||||||
|
l.n = n;
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
limited_reader_to_reader :: proc(l: ^Limited_Reader) -> (r: Reader) {
|
||||||
|
r.stream_vtable = _limited_reader_vtable;
|
||||||
|
r.stream_data = l;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="package")
|
||||||
|
inline_limited_reader :: proc(l: ^Limited_Reader, r: Reader, n: i64) -> Reader {
|
||||||
|
l.r = r;
|
||||||
|
l.n = n;
|
||||||
|
return limited_reader_to_reader(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Section_Reader implements read, seek, and read_at on a section of an underlying Reader_At
|
||||||
|
Section_Reader :: struct {
|
||||||
|
r: Reader_At,
|
||||||
|
base: i64,
|
||||||
|
off: i64,
|
||||||
|
limit: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
init_section_reader :: proc(s: ^Section_Reader, r: Reader_At, off: i64, n: i64) {
|
||||||
|
s.r = r;
|
||||||
|
s.off = off;
|
||||||
|
s.limit = off + n;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
section_reader_to_stream :: proc(s: ^Section_Reader) -> (out: Stream) {
|
||||||
|
out.stream_data = s;
|
||||||
|
out.stream_vtable = _section_reader_vtable;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_section_reader_vtable := &Stream_VTable{
|
||||||
|
impl_read = proc(stream: Stream, p: []byte) -> (n: int, err: Error) {
|
||||||
|
s := (^Section_Reader)(stream.stream_data);
|
||||||
|
if s.off >= s.limit {
|
||||||
|
return 0, .EOF;
|
||||||
|
}
|
||||||
|
p := p;
|
||||||
|
if max := s.limit - s.off; i64(len(p)) > max {
|
||||||
|
p = p[0:max];
|
||||||
|
}
|
||||||
|
n, err = read_at(s.r, p, s.off);
|
||||||
|
s.off += i64(n);
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
impl_read_at = proc(stream: Stream, p: []byte, off: i64) -> (n: int, err: Error) {
|
||||||
|
s := (^Section_Reader)(stream.stream_data);
|
||||||
|
p, off := p, off;
|
||||||
|
|
||||||
|
if off < 0 || off >= s.limit - s.base {
|
||||||
|
return 0, .EOF;
|
||||||
|
}
|
||||||
|
off += s.base;
|
||||||
|
if max := s.limit - off; i64(len(p)) > max {
|
||||||
|
p = p[0:max];
|
||||||
|
n, err = read_at(s.r, p, off);
|
||||||
|
if err == nil {
|
||||||
|
err = .EOF;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return read_at(s.r, p, off);
|
||||||
|
},
|
||||||
|
impl_seek = proc(stream: Stream, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
|
||||||
|
s := (^Section_Reader)(stream.stream_data);
|
||||||
|
|
||||||
|
offset := offset;
|
||||||
|
switch whence {
|
||||||
|
case:
|
||||||
|
return 0, .Invalid_Whence;
|
||||||
|
case .Start:
|
||||||
|
offset += s.base;
|
||||||
|
case .Current:
|
||||||
|
offset += s.off;
|
||||||
|
case .End:
|
||||||
|
offset += s.limit;
|
||||||
|
}
|
||||||
|
if offset < s.base {
|
||||||
|
return 0, .Invalid_Offset;
|
||||||
|
}
|
||||||
|
s.off = offset;
|
||||||
|
n = offset - s.base;
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
impl_size = proc(stream: Stream) -> i64 {
|
||||||
|
s := (^Section_Reader)(stream.stream_data);
|
||||||
|
return s.limit - s.base;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
@@ -593,6 +593,30 @@ is_inf :: proc{is_inf_f32, is_inf_f64};
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
inf_f32 :: proc(sign: int) -> f32 {
|
||||||
|
return f32(inf_f64(sign));
|
||||||
|
}
|
||||||
|
inf_f64 :: proc(sign: int) -> f64 {
|
||||||
|
v: u64;
|
||||||
|
if sign >= 0 {
|
||||||
|
v = 0x7ff00000_00000000;
|
||||||
|
} else {
|
||||||
|
v = 0xfff00000_00000000;
|
||||||
|
}
|
||||||
|
return transmute(f64)v;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
nan_f32 :: proc() -> f32 {
|
||||||
|
return f32(nan_f64());
|
||||||
|
}
|
||||||
|
nan_f64 :: proc() -> f64 {
|
||||||
|
v: u64 = 0x7ff80000_00000001;
|
||||||
|
return transmute(f64)v;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
is_power_of_two :: proc(x: int) -> bool {
|
is_power_of_two :: proc(x: int) -> bool {
|
||||||
return x > 0 && (x & (x-1)) == 0;
|
return x > 0 && (x & (x-1)) == 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-4
@@ -146,6 +146,7 @@ Paren_Expr :: struct {
|
|||||||
Selector_Expr :: struct {
|
Selector_Expr :: struct {
|
||||||
using node: Expr,
|
using node: Expr,
|
||||||
expr: ^Expr,
|
expr: ^Expr,
|
||||||
|
op: tokenizer.Token,
|
||||||
field: ^Ident,
|
field: ^Ident,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,6 +155,13 @@ Implicit_Selector_Expr :: struct {
|
|||||||
field: ^Ident,
|
field: ^Ident,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Selector_Call_Expr :: struct {
|
||||||
|
using node: Expr,
|
||||||
|
expr: ^Expr,
|
||||||
|
call: ^Call_Expr,
|
||||||
|
modified_call: bool,
|
||||||
|
}
|
||||||
|
|
||||||
Index_Expr :: struct {
|
Index_Expr :: struct {
|
||||||
using node: Expr,
|
using node: Expr,
|
||||||
expr: ^Expr,
|
expr: ^Expr,
|
||||||
@@ -206,9 +214,9 @@ Ternary_Expr :: struct {
|
|||||||
|
|
||||||
Ternary_If_Expr :: struct {
|
Ternary_If_Expr :: struct {
|
||||||
using node: Expr,
|
using node: Expr,
|
||||||
x: ^Expr,
|
x: ^Expr,
|
||||||
op1: tokenizer.Token,
|
op1: tokenizer.Token,
|
||||||
cond: ^Expr,
|
cond: ^Expr,
|
||||||
op2: tokenizer.Token,
|
op2: tokenizer.Token,
|
||||||
y: ^Expr,
|
y: ^Expr,
|
||||||
}
|
}
|
||||||
@@ -217,7 +225,7 @@ Ternary_When_Expr :: struct {
|
|||||||
using node: Expr,
|
using node: Expr,
|
||||||
x: ^Expr,
|
x: ^Expr,
|
||||||
op1: tokenizer.Token,
|
op1: tokenizer.Token,
|
||||||
cond: ^Expr,
|
cond: ^Expr,
|
||||||
op2: tokenizer.Token,
|
op2: tokenizer.Token,
|
||||||
y: ^Expr,
|
y: ^Expr,
|
||||||
}
|
}
|
||||||
@@ -561,7 +569,6 @@ Distinct_Type :: struct {
|
|||||||
|
|
||||||
Opaque_Type :: struct {
|
Opaque_Type :: struct {
|
||||||
using node: Expr,
|
using node: Expr,
|
||||||
tok: tokenizer.Token_Kind,
|
|
||||||
type: ^Expr,
|
type: ^Expr,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+123
-44
@@ -190,6 +190,50 @@ peek_token_kind :: proc(p: ^Parser, kind: tokenizer.Token_Kind, lookahead := 0)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
peek_token :: proc(p: ^Parser, lookahead := 0) -> (tok: tokenizer.Token) {
|
||||||
|
prev_parser := p^;
|
||||||
|
defer p^ = prev_parser;
|
||||||
|
|
||||||
|
p.tok.err = nil;
|
||||||
|
for i := 0; i <= lookahead; i += 1 {
|
||||||
|
advance_token(p);
|
||||||
|
}
|
||||||
|
tok = p.curr_tok;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
skip_possible_newline :: proc(p: ^Parser) -> bool {
|
||||||
|
if .Insert_Semicolon not_in p.tok.flags {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev := p.curr_tok;
|
||||||
|
if tokenizer.is_newline(prev) {
|
||||||
|
advance_token(p);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_possible_newline_for_literal :: proc(p: ^Parser) -> bool {
|
||||||
|
if .Insert_Semicolon not_in p.tok.flags {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
curr_pos := p.curr_tok.pos;
|
||||||
|
if tokenizer.is_newline(p.curr_tok) {
|
||||||
|
next := peek_token(p);
|
||||||
|
if curr_pos.line+1 >= next.pos.line {
|
||||||
|
#partial switch next.kind {
|
||||||
|
case .Open_Brace, .Else, .Where:
|
||||||
|
advance_token(p);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
next_token0 :: proc(p: ^Parser) -> bool {
|
next_token0 :: proc(p: ^Parser) -> bool {
|
||||||
p.curr_tok = tokenizer.scan(&p.tok);
|
p.curr_tok = tokenizer.scan(&p.tok);
|
||||||
@@ -280,7 +324,7 @@ expect_token :: proc(p: ^Parser, kind: tokenizer.Token_Kind) -> tokenizer.Token
|
|||||||
prev := p.curr_tok;
|
prev := p.curr_tok;
|
||||||
if prev.kind != kind {
|
if prev.kind != kind {
|
||||||
e := tokenizer.to_string(kind);
|
e := tokenizer.to_string(kind);
|
||||||
g := tokenizer.to_string(prev.kind);
|
g := tokenizer.token_to_string(prev);
|
||||||
error(p, prev.pos, "expected '%s', got '%s'", e, g);
|
error(p, prev.pos, "expected '%s', got '%s'", e, g);
|
||||||
}
|
}
|
||||||
advance_token(p);
|
advance_token(p);
|
||||||
@@ -291,7 +335,7 @@ expect_token_after :: proc(p: ^Parser, kind: tokenizer.Token_Kind, msg: string)
|
|||||||
prev := p.curr_tok;
|
prev := p.curr_tok;
|
||||||
if prev.kind != kind {
|
if prev.kind != kind {
|
||||||
e := tokenizer.to_string(kind);
|
e := tokenizer.to_string(kind);
|
||||||
g := tokenizer.to_string(prev.kind);
|
g := tokenizer.token_to_string(prev);
|
||||||
error(p, prev.pos, "expected '%s' after %s, got '%s'", e, msg, g);
|
error(p, prev.pos, "expected '%s' after %s, got '%s'", e, msg, g);
|
||||||
}
|
}
|
||||||
advance_token(p);
|
advance_token(p);
|
||||||
@@ -300,8 +344,10 @@ expect_token_after :: proc(p: ^Parser, kind: tokenizer.Token_Kind, msg: string)
|
|||||||
|
|
||||||
expect_operator :: proc(p: ^Parser) -> tokenizer.Token {
|
expect_operator :: proc(p: ^Parser) -> tokenizer.Token {
|
||||||
prev := p.curr_tok;
|
prev := p.curr_tok;
|
||||||
if !tokenizer.is_operator(prev.kind) {
|
if prev.kind == .If || prev.kind == .When {
|
||||||
g := tokenizer.to_string(prev.kind);
|
// okay
|
||||||
|
} else if !tokenizer.is_operator(prev.kind) {
|
||||||
|
g := tokenizer.token_to_string(prev);
|
||||||
error(p, prev.pos, "expected an operator, got '%s'", g);
|
error(p, prev.pos, "expected an operator, got '%s'", g);
|
||||||
}
|
}
|
||||||
advance_token(p);
|
advance_token(p);
|
||||||
@@ -398,7 +444,16 @@ expect_semicolon :: proc(p: ^Parser, node: ^ast.Node) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if node != nil {
|
if node != nil {
|
||||||
if prev.pos.line != p.curr_tok.pos.line {
|
if .Insert_Semicolon in p.tok.flags {
|
||||||
|
#partial switch p.curr_tok.kind {
|
||||||
|
case .Close_Brace, .Close_Paren, .Else, .EOF:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_semicolon_optional_for_node(p, node) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if prev.pos.line != p.curr_tok.pos.line {
|
||||||
if is_semicolon_optional_for_node(p, node) {
|
if is_semicolon_optional_for_node(p, node) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -418,7 +473,7 @@ expect_semicolon :: proc(p: ^Parser, node: ^ast.Node) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
error(p, prev.pos, "expected ';', got %s", tokenizer.to_string(p.curr_tok.kind));
|
error(p, prev.pos, "expected ';', got %s", tokenizer.token_to_string(p.curr_tok));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -491,6 +546,7 @@ parse_when_stmt :: proc(p: ^Parser) -> ^ast.When_Stmt {
|
|||||||
body = convert_stmt_to_body(p, parse_stmt(p));
|
body = convert_stmt_to_body(p, parse_stmt(p));
|
||||||
} else {
|
} else {
|
||||||
body = parse_block_stmt(p, true);
|
body = parse_block_stmt(p, true);
|
||||||
|
skip_possible_newline_for_literal(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
if allow_token(p, .Else) {
|
if allow_token(p, .Else) {
|
||||||
@@ -566,6 +622,7 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt {
|
|||||||
body = convert_stmt_to_body(p, parse_stmt(p));
|
body = convert_stmt_to_body(p, parse_stmt(p));
|
||||||
} else {
|
} else {
|
||||||
body = parse_block_stmt(p, false);
|
body = parse_block_stmt(p, false);
|
||||||
|
skip_possible_newline_for_literal(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
if allow_token(p, .Else) {
|
if allow_token(p, .Else) {
|
||||||
@@ -627,6 +684,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
|
|||||||
body = convert_stmt_to_body(p, parse_stmt(p));
|
body = convert_stmt_to_body(p, parse_stmt(p));
|
||||||
} else {
|
} else {
|
||||||
body = parse_body(p);
|
body = parse_body(p);
|
||||||
|
skip_possible_newline_for_literal(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
range_stmt := ast.new(ast.Range_Stmt, tok.pos, body.end);
|
range_stmt := ast.new(ast.Range_Stmt, tok.pos, body.end);
|
||||||
@@ -661,6 +719,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
|
|||||||
body = convert_stmt_to_body(p, parse_stmt(p));
|
body = convert_stmt_to_body(p, parse_stmt(p));
|
||||||
} else {
|
} else {
|
||||||
body = parse_body(p);
|
body = parse_body(p);
|
||||||
|
skip_possible_newline_for_literal(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -838,6 +897,8 @@ parse_attribute :: proc(p: ^Parser, tok: tokenizer.Token, open_kind, close_kind:
|
|||||||
attribute.elems = elems[:];
|
attribute.elems = elems[:];
|
||||||
attribute.close = close.pos;
|
attribute.close = close.pos;
|
||||||
|
|
||||||
|
skip_possible_newline(p);
|
||||||
|
|
||||||
decl := parse_stmt(p);
|
decl := parse_stmt(p);
|
||||||
switch d in &decl.derived {
|
switch d in &decl.derived {
|
||||||
case ast.Value_Decl:
|
case ast.Value_Decl:
|
||||||
@@ -1026,10 +1087,11 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
|
|||||||
body = convert_stmt_to_body(p, parse_stmt(p));
|
body = convert_stmt_to_body(p, parse_stmt(p));
|
||||||
} else {
|
} else {
|
||||||
body = parse_block_stmt(p, false);
|
body = parse_block_stmt(p, false);
|
||||||
|
skip_possible_newline_for_literal(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
if bad_stmt {
|
if bad_stmt {
|
||||||
return ast.new(ast.Bad_Stmt, inline_tok.pos, end_pos(p.prev_tok));
|
return ast.new(ast.Bad_Stmt, inline_tok.pos, end_pos(p.prev_tok));
|
||||||
}
|
}
|
||||||
|
|
||||||
range_stmt := ast.new(ast.Inline_Range_Stmt, inline_tok.pos, body.end);
|
range_stmt := ast.new(ast.Inline_Range_Stmt, inline_tok.pos, body.end);
|
||||||
@@ -1204,7 +1266,7 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tok := advance_token(p);
|
tok := advance_token(p);
|
||||||
error(p, tok.pos, "expected a statement, got %s", tokenizer.to_string(tok.kind));
|
error(p, tok.pos, "expected a statement, got %s", tokenizer.token_to_string(tok));
|
||||||
s := ast.new(ast.Bad_Stmt, tok.pos, end_pos(tok));
|
s := ast.new(ast.Bad_Stmt, tok.pos, end_pos(tok));
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
@@ -1957,13 +2019,6 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
|||||||
bl.tok = tok;
|
bl.tok = tok;
|
||||||
return bl;
|
return bl;
|
||||||
|
|
||||||
|
|
||||||
case .Size_Of, .Align_Of, .Offset_Of:
|
|
||||||
tok := advance_token(p);
|
|
||||||
expr := ast.new(ast.Implicit, tok.pos, end_pos(tok));
|
|
||||||
expr.tok = tok;
|
|
||||||
return parse_call_expr(p, expr);
|
|
||||||
|
|
||||||
case .Open_Brace:
|
case .Open_Brace:
|
||||||
if !lhs {
|
if !lhs {
|
||||||
return parse_literal_value(p, nil);
|
return parse_literal_value(p, nil);
|
||||||
@@ -1992,15 +2047,22 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
|||||||
|
|
||||||
case .Opaque:
|
case .Opaque:
|
||||||
tok := advance_token(p);
|
tok := advance_token(p);
|
||||||
|
warn(p, tok.pos, "opaque is deprecated in favour of #opaque");
|
||||||
type := parse_type(p);
|
type := parse_type(p);
|
||||||
ot := ast.new(ast.Opaque_Type, tok.pos, type.end);
|
ot := ast.new(ast.Opaque_Type, tok.pos, type.end);
|
||||||
ot.tok = tok.kind;
|
|
||||||
ot.type = type;
|
ot.type = type;
|
||||||
return ot;
|
return ot;
|
||||||
|
|
||||||
case .Hash:
|
case .Hash:
|
||||||
tok := expect_token(p, .Hash);
|
tok := expect_token(p, .Hash);
|
||||||
name := expect_token(p, .Ident);
|
name := expect_token(p, .Ident);
|
||||||
switch name.text {
|
switch name.text {
|
||||||
|
case "opaque":
|
||||||
|
type := parse_type(p);
|
||||||
|
ot := ast.new(ast.Opaque_Type, tok.pos, type.end);
|
||||||
|
ot.type = type;
|
||||||
|
return ot;
|
||||||
|
|
||||||
case "type":
|
case "type":
|
||||||
type := parse_type(p);
|
type := parse_type(p);
|
||||||
hp := ast.new(ast.Helper_Type, tok.pos, type.end);
|
hp := ast.new(ast.Helper_Type, tok.pos, type.end);
|
||||||
@@ -2156,7 +2218,10 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
|||||||
|
|
||||||
where_token: tokenizer.Token;
|
where_token: tokenizer.Token;
|
||||||
where_clauses: []^ast.Expr;
|
where_clauses: []^ast.Expr;
|
||||||
if (p.curr_tok.kind == .Where) {
|
|
||||||
|
skip_possible_newline_for_literal(p);
|
||||||
|
|
||||||
|
if p.curr_tok.kind == .Where {
|
||||||
where_token = expect_token(p, .Where);
|
where_token = expect_token(p, .Where);
|
||||||
prev_level := p.expr_level;
|
prev_level := p.expr_level;
|
||||||
p.expr_level = -1;
|
p.expr_level = -1;
|
||||||
@@ -2225,25 +2290,6 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
|||||||
ti.specialization = nil;
|
ti.specialization = nil;
|
||||||
return ti;
|
return ti;
|
||||||
|
|
||||||
case .Type_Of:
|
|
||||||
tok := advance_token(p);
|
|
||||||
i := ast.new(ast.Implicit, tok.pos, end_pos(tok));
|
|
||||||
i.tok = tok;
|
|
||||||
type: ^ast.Expr = parse_call_expr(p, i);
|
|
||||||
for p.curr_tok.kind == .Period {
|
|
||||||
period := advance_token(p);
|
|
||||||
|
|
||||||
field := parse_ident(p);
|
|
||||||
sel := ast.new(ast.Selector_Expr, period.pos, field.end);
|
|
||||||
sel.expr = type;
|
|
||||||
sel.field = field;
|
|
||||||
|
|
||||||
type = sel;
|
|
||||||
}
|
|
||||||
|
|
||||||
return type;
|
|
||||||
|
|
||||||
|
|
||||||
case .Pointer:
|
case .Pointer:
|
||||||
tok := expect_token(p, .Pointer);
|
tok := expect_token(p, .Pointer);
|
||||||
elem := parse_type(p);
|
elem := parse_type(p);
|
||||||
@@ -2351,12 +2397,15 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
|||||||
|
|
||||||
where_token: tokenizer.Token;
|
where_token: tokenizer.Token;
|
||||||
where_clauses: []^ast.Expr;
|
where_clauses: []^ast.Expr;
|
||||||
if (p.curr_tok.kind == .Where) {
|
|
||||||
|
skip_possible_newline_for_literal(p);
|
||||||
|
|
||||||
|
if p.curr_tok.kind == .Where {
|
||||||
where_token = expect_token(p, .Where);
|
where_token = expect_token(p, .Where);
|
||||||
prev_level := p.expr_level;
|
where_prev_level := p.expr_level;
|
||||||
p.expr_level = -1;
|
p.expr_level = -1;
|
||||||
where_clauses = parse_rhs_expr_list(p);
|
where_clauses = parse_rhs_expr_list(p);
|
||||||
p.expr_level = prev_level;
|
p.expr_level = where_prev_level;
|
||||||
}
|
}
|
||||||
|
|
||||||
expect_token(p, .Open_Brace);
|
expect_token(p, .Open_Brace);
|
||||||
@@ -2414,12 +2463,15 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
|||||||
|
|
||||||
where_token: tokenizer.Token;
|
where_token: tokenizer.Token;
|
||||||
where_clauses: []^ast.Expr;
|
where_clauses: []^ast.Expr;
|
||||||
if (p.curr_tok.kind == .Where) {
|
|
||||||
|
skip_possible_newline_for_literal(p);
|
||||||
|
|
||||||
|
if p.curr_tok.kind == .Where {
|
||||||
where_token = expect_token(p, .Where);
|
where_token = expect_token(p, .Where);
|
||||||
prev_level := p.expr_level;
|
where_prev_level := p.expr_level;
|
||||||
p.expr_level = -1;
|
p.expr_level = -1;
|
||||||
where_clauses = parse_rhs_expr_list(p);
|
where_clauses = parse_rhs_expr_list(p);
|
||||||
p.expr_level = prev_level;
|
p.expr_level = where_prev_level;
|
||||||
}
|
}
|
||||||
|
|
||||||
variants: [dynamic]^ast.Expr;
|
variants: [dynamic]^ast.Expr;
|
||||||
@@ -2628,7 +2680,7 @@ parse_literal_value :: proc(p: ^Parser, type: ^ast.Expr) -> ^ast.Comp_Lit {
|
|||||||
return lit;
|
return lit;
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Call_Expr {
|
parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Expr {
|
||||||
args: [dynamic]^ast.Expr;
|
args: [dynamic]^ast.Expr;
|
||||||
|
|
||||||
ellipsis: tokenizer.Token;
|
ellipsis: tokenizer.Token;
|
||||||
@@ -2686,6 +2738,14 @@ parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Call_Expr {
|
|||||||
ce.ellipsis = ellipsis;
|
ce.ellipsis = ellipsis;
|
||||||
ce.close = close.pos;
|
ce.close = close.pos;
|
||||||
|
|
||||||
|
o := ast.unparen_expr(operand);
|
||||||
|
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.expr = o;
|
||||||
|
sce.call = ce;
|
||||||
|
return sce;
|
||||||
|
}
|
||||||
|
|
||||||
return ce;
|
return ce;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2739,7 +2799,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
|
|||||||
case .Colon:
|
case .Colon:
|
||||||
interval = advance_token(p);
|
interval = advance_token(p);
|
||||||
is_slice_op = true;
|
is_slice_op = true;
|
||||||
if (p.curr_tok.kind != .Close_Bracket && p.curr_tok.kind != .EOF) {
|
if p.curr_tok.kind != .Close_Bracket && p.curr_tok.kind != .EOF {
|
||||||
indicies[1] = parse_expr(p, false);
|
indicies[1] = parse_expr(p, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2776,6 +2836,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
|
|||||||
|
|
||||||
sel := ast.new(ast.Selector_Expr, operand.pos, field.end);
|
sel := ast.new(ast.Selector_Expr, operand.pos, field.end);
|
||||||
sel.expr = operand;
|
sel.expr = operand;
|
||||||
|
sel.op = tok;
|
||||||
sel.field = field;
|
sel.field = field;
|
||||||
|
|
||||||
operand = sel;
|
operand = sel;
|
||||||
@@ -2811,6 +2872,24 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
|
|||||||
operand = ast.new(ast.Bad_Expr, operand.pos, end_pos(tok));
|
operand = ast.new(ast.Bad_Expr, operand.pos, end_pos(tok));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case .Arrow_Right:
|
||||||
|
tok := expect_token(p, .Arrow_Right);
|
||||||
|
#partial switch p.curr_tok.kind {
|
||||||
|
case .Ident:
|
||||||
|
field := parse_ident(p);
|
||||||
|
|
||||||
|
sel := ast.new(ast.Selector_Expr, operand.pos, field.end);
|
||||||
|
sel.expr = operand;
|
||||||
|
sel.op = tok;
|
||||||
|
sel.field = field;
|
||||||
|
|
||||||
|
operand = sel;
|
||||||
|
case:
|
||||||
|
error(p, p.curr_tok.pos, "expected a selector");
|
||||||
|
advance_token(p);
|
||||||
|
operand = ast.new(ast.Bad_Expr, operand.pos, end_pos(tok));
|
||||||
|
}
|
||||||
|
|
||||||
case .Pointer:
|
case .Pointer:
|
||||||
op := expect_token(p, .Pointer);
|
op := expect_token(p, .Pointer);
|
||||||
deref := ast.new(ast.Deref_Expr, operand.pos, end_pos(op));
|
deref := ast.new(ast.Deref_Expr, operand.pos, end_pos(op));
|
||||||
|
|||||||
@@ -133,7 +133,6 @@ Token_Kind :: enum u32 {
|
|||||||
Defer,
|
Defer,
|
||||||
Return,
|
Return,
|
||||||
Proc,
|
Proc,
|
||||||
Macro,
|
|
||||||
Struct,
|
Struct,
|
||||||
Union,
|
Union,
|
||||||
Enum,
|
Enum,
|
||||||
@@ -150,11 +149,6 @@ Token_Kind :: enum u32 {
|
|||||||
Inline,
|
Inline,
|
||||||
No_Inline,
|
No_Inline,
|
||||||
Context,
|
Context,
|
||||||
Size_Of,
|
|
||||||
Align_Of,
|
|
||||||
Offset_Of,
|
|
||||||
Type_Of,
|
|
||||||
Const,
|
|
||||||
B_Keyword_End,
|
B_Keyword_End,
|
||||||
|
|
||||||
COUNT,
|
COUNT,
|
||||||
@@ -268,7 +262,6 @@ tokens := [Token_Kind.COUNT]string {
|
|||||||
"defer",
|
"defer",
|
||||||
"return",
|
"return",
|
||||||
"proc",
|
"proc",
|
||||||
"macro",
|
|
||||||
"struct",
|
"struct",
|
||||||
"union",
|
"union",
|
||||||
"enum",
|
"enum",
|
||||||
@@ -285,16 +278,24 @@ tokens := [Token_Kind.COUNT]string {
|
|||||||
"inline",
|
"inline",
|
||||||
"no_inline",
|
"no_inline",
|
||||||
"context",
|
"context",
|
||||||
"size_of",
|
|
||||||
"align_of",
|
|
||||||
"offset_of",
|
|
||||||
"type_of",
|
|
||||||
"const",
|
|
||||||
"",
|
"",
|
||||||
};
|
};
|
||||||
|
|
||||||
custom_keyword_tokens: []string;
|
custom_keyword_tokens: []string;
|
||||||
|
|
||||||
|
|
||||||
|
is_newline :: proc(tok: Token) -> bool {
|
||||||
|
return tok.kind == .Semicolon && tok.text == "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
token_to_string :: proc(tok: Token) -> string {
|
||||||
|
if is_newline(tok) {
|
||||||
|
return "newline";
|
||||||
|
}
|
||||||
|
return to_string(tok.kind);
|
||||||
|
}
|
||||||
|
|
||||||
to_string :: proc(kind: Token_Kind) -> string {
|
to_string :: proc(kind: Token_Kind) -> string {
|
||||||
if Token_Kind.Invalid <= kind && kind < Token_Kind.COUNT {
|
if Token_Kind.Invalid <= kind && kind < Token_Kind.COUNT {
|
||||||
return tokens[kind];
|
return tokens[kind];
|
||||||
|
|||||||
@@ -1,22 +1,31 @@
|
|||||||
package odin_tokenizer
|
package odin_tokenizer
|
||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
import "core:unicode"
|
||||||
import "core:unicode/utf8"
|
import "core:unicode/utf8"
|
||||||
|
|
||||||
Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any);
|
Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any);
|
||||||
|
|
||||||
|
Flag :: enum {
|
||||||
|
Insert_Semicolon,
|
||||||
|
}
|
||||||
|
Flags :: distinct bit_set[Flag; u32];
|
||||||
|
|
||||||
Tokenizer :: struct {
|
Tokenizer :: struct {
|
||||||
// Immutable data
|
// Immutable data
|
||||||
path: string,
|
path: string,
|
||||||
src: []byte,
|
src: []byte,
|
||||||
err: Error_Handler,
|
err: Error_Handler,
|
||||||
|
|
||||||
|
flags: Flags,
|
||||||
|
|
||||||
// Tokenizing state
|
// Tokenizing state
|
||||||
ch: rune,
|
ch: rune,
|
||||||
offset: int,
|
offset: int,
|
||||||
read_offset: int,
|
read_offset: int,
|
||||||
line_offset: int,
|
line_offset: int,
|
||||||
line_count: int,
|
line_count: int,
|
||||||
|
insert_semicolon: bool,
|
||||||
|
|
||||||
// Mutable data
|
// Mutable data
|
||||||
error_count: int,
|
error_count: int,
|
||||||
@@ -105,11 +114,18 @@ peek_byte :: proc(t: ^Tokenizer, offset := 0) -> byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
skip_whitespace :: proc(t: ^Tokenizer) {
|
skip_whitespace :: proc(t: ^Tokenizer) {
|
||||||
for t.ch == ' ' ||
|
for {
|
||||||
t.ch == '\t' ||
|
switch t.ch {
|
||||||
t.ch == '\n' ||
|
case ' ', '\t', '\r':
|
||||||
t.ch == '\r' {
|
advance_rune(t);
|
||||||
advance_rune(t);
|
case '\n':
|
||||||
|
if t.insert_semicolon {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
advance_rune(t);
|
||||||
|
case:
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,12 +138,13 @@ is_letter :: proc(r: rune) -> bool {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO(bill): Add unicode lookup tables
|
return unicode.is_letter(r);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
is_digit :: proc(r: rune) -> bool {
|
is_digit :: proc(r: rune) -> bool {
|
||||||
// TODO(bill): Add unicode lookup tables
|
if '0' <= r && r <= '9' {
|
||||||
return '0' <= r && r <= '9';
|
return true;
|
||||||
|
}
|
||||||
|
return unicode.is_digit(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -491,6 +508,8 @@ scan :: proc(t: ^Tokenizer) -> Token {
|
|||||||
lit: string;
|
lit: string;
|
||||||
pos := offset_to_pos(t, offset);
|
pos := offset_to_pos(t, offset);
|
||||||
|
|
||||||
|
insert_semicolon := false;
|
||||||
|
|
||||||
switch ch := t.ch; true {
|
switch ch := t.ch; true {
|
||||||
case is_letter(ch):
|
case is_letter(ch):
|
||||||
lit = scan_identifier(t);
|
lit = scan_identifier(t);
|
||||||
@@ -509,24 +528,39 @@ scan :: proc(t: ^Tokenizer) -> Token {
|
|||||||
break check_keyword;
|
break check_keyword;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if kind == .Ident && lit == "notin" {
|
|
||||||
kind = .Not_In;
|
#partial switch kind {
|
||||||
|
case .Ident, .Context, .Typeid, .Break, .Continue, .Fallthrough, .Return:
|
||||||
|
insert_semicolon = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case '0' <= ch && ch <= '9':
|
case '0' <= ch && ch <= '9':
|
||||||
|
insert_semicolon = true;
|
||||||
kind, lit = scan_number(t, false);
|
kind, lit = scan_number(t, false);
|
||||||
case:
|
case:
|
||||||
advance_rune(t);
|
advance_rune(t);
|
||||||
switch ch {
|
switch ch {
|
||||||
case -1:
|
case -1:
|
||||||
kind = .EOF;
|
kind = .EOF;
|
||||||
|
if t.insert_semicolon {
|
||||||
|
t.insert_semicolon = false;
|
||||||
|
kind = .Semicolon;
|
||||||
|
lit = "\n";
|
||||||
|
}
|
||||||
|
case '\n':
|
||||||
|
t.insert_semicolon = false;
|
||||||
|
kind = .Semicolon;
|
||||||
|
lit = "\n";
|
||||||
case '"':
|
case '"':
|
||||||
|
insert_semicolon = true;
|
||||||
kind = .String;
|
kind = .String;
|
||||||
lit = scan_string(t);
|
lit = scan_string(t);
|
||||||
case '\'':
|
case '\'':
|
||||||
|
insert_semicolon = true;
|
||||||
kind = .Rune;
|
kind = .Rune;
|
||||||
lit = scan_rune(t);
|
lit = scan_rune(t);
|
||||||
case '`':
|
case '`':
|
||||||
|
insert_semicolon = true;
|
||||||
kind = .String;
|
kind = .String;
|
||||||
lit = scan_raw_string(t);
|
lit = scan_raw_string(t);
|
||||||
case '=':
|
case '=':
|
||||||
@@ -540,10 +574,13 @@ scan :: proc(t: ^Tokenizer) -> Token {
|
|||||||
case '#':
|
case '#':
|
||||||
kind = .Hash;
|
kind = .Hash;
|
||||||
if t.ch == '!' {
|
if t.ch == '!' {
|
||||||
|
insert_semicolon = t.insert_semicolon;
|
||||||
kind = .Comment;
|
kind = .Comment;
|
||||||
lit = scan_comment(t);
|
lit = scan_comment(t);
|
||||||
}
|
}
|
||||||
case '?': kind = .Question;
|
case '?':
|
||||||
|
insert_semicolon = true;
|
||||||
|
kind = .Question;
|
||||||
case '@': kind = .At;
|
case '@': kind = .At;
|
||||||
case '$': kind = .Dollar;
|
case '$': kind = .Dollar;
|
||||||
case '^': kind = .Pointer;
|
case '^': kind = .Pointer;
|
||||||
@@ -562,6 +599,7 @@ scan :: proc(t: ^Tokenizer) -> Token {
|
|||||||
case '*': kind = switch2(t, .Mul, .Mul_Eq);
|
case '*': kind = switch2(t, .Mul, .Mul_Eq);
|
||||||
case '/':
|
case '/':
|
||||||
if t.ch == '/' || t.ch == '*' {
|
if t.ch == '/' || t.ch == '*' {
|
||||||
|
insert_semicolon = t.insert_semicolon;
|
||||||
kind = .Comment;
|
kind = .Comment;
|
||||||
lit = scan_comment(t);
|
lit = scan_comment(t);
|
||||||
} else {
|
} else {
|
||||||
@@ -604,11 +642,17 @@ scan :: proc(t: ^Tokenizer) -> Token {
|
|||||||
case ',': kind = .Comma;
|
case ',': kind = .Comma;
|
||||||
case ';': kind = .Semicolon;
|
case ';': kind = .Semicolon;
|
||||||
case '(': kind = .Open_Paren;
|
case '(': kind = .Open_Paren;
|
||||||
case ')': kind = .Close_Paren;
|
case ')':
|
||||||
|
insert_semicolon = true;
|
||||||
|
kind = .Close_Paren;
|
||||||
case '[': kind = .Open_Bracket;
|
case '[': kind = .Open_Bracket;
|
||||||
case ']': kind = .Close_Bracket;
|
case ']':
|
||||||
|
insert_semicolon = true;
|
||||||
|
kind = .Close_Bracket;
|
||||||
case '{': kind = .Open_Brace;
|
case '{': kind = .Open_Brace;
|
||||||
case '}': kind = .Close_Brace;
|
case '}':
|
||||||
|
insert_semicolon = true;
|
||||||
|
kind = .Close_Brace;
|
||||||
|
|
||||||
case '\\': kind = .Back_Slash;
|
case '\\': kind = .Back_Slash;
|
||||||
|
|
||||||
@@ -616,10 +660,15 @@ scan :: proc(t: ^Tokenizer) -> Token {
|
|||||||
if ch != utf8.RUNE_BOM {
|
if ch != utf8.RUNE_BOM {
|
||||||
error(t, t.offset, "illegal character '%r': %d", ch, ch);
|
error(t, t.offset, "illegal character '%r': %d", ch, ch);
|
||||||
}
|
}
|
||||||
|
insert_semicolon = t.insert_semicolon; // preserve insert_semicolon info
|
||||||
kind = .Invalid;
|
kind = .Invalid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if .Insert_Semicolon in t.flags {
|
||||||
|
t.insert_semicolon = insert_semicolon;
|
||||||
|
}
|
||||||
|
|
||||||
if lit == "" {
|
if lit == "" {
|
||||||
lit = string(t.src[offset : t.offset]);
|
lit = string(t.src[offset : t.offset]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package os
|
||||||
|
|
||||||
|
import "core:io"
|
||||||
|
|
||||||
|
stream_from_handle :: proc(fd: Handle) -> io.Stream {
|
||||||
|
s: io.Stream;
|
||||||
|
s.stream_data = rawptr(uintptr(fd));
|
||||||
|
s.stream_vtable = _file_stream_vtable;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_file_stream_vtable := &io.Stream_VTable{
|
||||||
|
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||||
|
fd := Handle(uintptr(s.stream_data));
|
||||||
|
os_err: Errno;
|
||||||
|
n, os_err = read(fd, p);
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
|
||||||
|
when ODIN_OS == "windows" {
|
||||||
|
fd := Handle(uintptr(s.stream_data));
|
||||||
|
os_err: Errno;
|
||||||
|
n, os_err = read_at(fd, p, offset);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||||
|
fd := Handle(uintptr(s.stream_data));
|
||||||
|
os_err: Errno;
|
||||||
|
n, os_err = write(fd, p);
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
|
||||||
|
when ODIN_OS == "windows" {
|
||||||
|
fd := Handle(uintptr(s.stream_data));
|
||||||
|
os_err: Errno;
|
||||||
|
n, os_err = write_at(fd, p, offset);
|
||||||
|
_ = os_err;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
|
||||||
|
fd := Handle(uintptr(s.stream_data));
|
||||||
|
n, os_err := seek(fd, offset, int(whence));
|
||||||
|
_ = os_err;
|
||||||
|
return n, nil;
|
||||||
|
},
|
||||||
|
impl_size = proc(s: io.Stream) -> i64 {
|
||||||
|
fd := Handle(uintptr(s.stream_data));
|
||||||
|
sz, _ := file_size(fd);
|
||||||
|
return sz;
|
||||||
|
},
|
||||||
|
impl_flush = proc(s: io.Stream) -> io.Error {
|
||||||
|
when ODIN_OS == "windows" {
|
||||||
|
fd := Handle(uintptr(s.stream_data));
|
||||||
|
flush(fd);
|
||||||
|
} else {
|
||||||
|
// TOOD(bill): other operating systems
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
},
|
||||||
|
impl_close = proc(s: io.Stream) -> io.Error {
|
||||||
|
fd := Handle(uintptr(s.stream_data));
|
||||||
|
close(fd);
|
||||||
|
return nil;
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -8,7 +8,8 @@ SEPARATOR :: '\\';
|
|||||||
SEPARATOR_STRING :: `\`;
|
SEPARATOR_STRING :: `\`;
|
||||||
LIST_SEPARATOR :: ';';
|
LIST_SEPARATOR :: ';';
|
||||||
|
|
||||||
reserved_names := []string{
|
@(private)
|
||||||
|
reserved_names := [?]string{
|
||||||
"CON", "PRN", "AUX", "NUL",
|
"CON", "PRN", "AUX", "NUL",
|
||||||
"COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
|
"COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
|
||||||
"LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
|
"LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
|
||||||
|
|||||||
@@ -28,8 +28,6 @@ Match_Error :: enum {
|
|||||||
// match requires that the pattern matches the entirety of the name, not just a substring
|
// match requires that the pattern matches the entirety of the name, not just a substring
|
||||||
// The only possible error returned is .Syntax_Error
|
// The only possible error returned is .Syntax_Error
|
||||||
//
|
//
|
||||||
// NOTE(bill): This is effectively the shell pattern matching system found
|
|
||||||
//
|
|
||||||
match :: proc(pattern, name: string) -> (matched: bool, err: Match_Error) {
|
match :: proc(pattern, name: string) -> (matched: bool, err: Match_Error) {
|
||||||
pattern, name := pattern, name;
|
pattern, name := pattern, name;
|
||||||
pattern_loop: for len(pattern) > 0 {
|
pattern_loop: for len(pattern) > 0 {
|
||||||
|
|||||||
@@ -1206,3 +1206,82 @@ as_raw_data :: proc(a: any) -> (value: rawptr, valid: bool) {
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
not_equal :: proc(a, b: any) -> bool {
|
||||||
|
return !equal(a, b);
|
||||||
|
}
|
||||||
|
equal :: proc(a, b: any) -> bool {
|
||||||
|
if a == nil && b == nil {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.id != b.id {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.data == b.data {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
t := type_info_of(a.id);
|
||||||
|
if .Comparable not_in t.flags {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.size == 0 {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if .Simple_Compare in t.flags {
|
||||||
|
return mem.compare_byte_ptrs((^byte)(a.data), (^byte)(b.data), t.size) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
t = runtime.type_info_core(t);
|
||||||
|
|
||||||
|
#partial switch v in t.variant {
|
||||||
|
case Type_Info_String:
|
||||||
|
if v.is_cstring {
|
||||||
|
x := string((^cstring)(a.data)^);
|
||||||
|
y := string((^cstring)(b.data)^);
|
||||||
|
return x == y;
|
||||||
|
} else {
|
||||||
|
x := (^string)(a.data)^;
|
||||||
|
y := (^string)(b.data)^;
|
||||||
|
return x == y;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Type_Info_Array:
|
||||||
|
for i in 0..<v.count {
|
||||||
|
x := rawptr(uintptr(a.data) + uintptr(v.elem_size*i));
|
||||||
|
y := rawptr(uintptr(b.data) + uintptr(v.elem_size*i));
|
||||||
|
if !equal(any{x, v.elem.id}, any{y, v.elem.id}) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Type_Info_Enumerated_Array:
|
||||||
|
for i in 0..<v.count {
|
||||||
|
x := rawptr(uintptr(a.data) + uintptr(v.elem_size*i));
|
||||||
|
y := rawptr(uintptr(b.data) + uintptr(v.elem_size*i));
|
||||||
|
if !equal(any{x, v.elem.id}, any{y, v.elem.id}) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Type_Info_Struct:
|
||||||
|
if v.equal != nil {
|
||||||
|
return v.equal(a.data, b.data);
|
||||||
|
} else {
|
||||||
|
for offset, i in v.offsets {
|
||||||
|
x := rawptr(uintptr(a.data) + offset);
|
||||||
|
y := rawptr(uintptr(b.data) + offset);
|
||||||
|
id := v.types[i].id;
|
||||||
|
if !equal(any{x, id}, any{y, id}) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|||||||
+161
-132
@@ -1,5 +1,6 @@
|
|||||||
package reflect
|
package reflect
|
||||||
|
|
||||||
|
import "core:io"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
|
||||||
are_types_identical :: proc(a, b: ^Type_Info) -> bool {
|
are_types_identical :: proc(a, b: ^Type_Info) -> bool {
|
||||||
@@ -218,6 +219,14 @@ is_unsigned :: proc(info: ^Type_Info) -> bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is_byte :: proc(info: ^Type_Info) -> bool {
|
||||||
|
if info == nil { return false; }
|
||||||
|
#partial switch i in type_info_base(info).variant {
|
||||||
|
case Type_Info_Integer: return info.size == 1;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
is_integer :: proc(info: ^Type_Info) -> bool {
|
is_integer :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false; }
|
if info == nil { return false; }
|
||||||
@@ -352,258 +361,278 @@ is_relative_slice :: proc(info: ^Type_Info) -> bool {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
write_typeid :: proc(buf: ^strings.Builder, id: typeid) {
|
write_typeid_builder :: proc(buf: ^strings.Builder, id: typeid) {
|
||||||
write_type(buf, type_info_of(id));
|
write_type(buf, type_info_of(id));
|
||||||
}
|
}
|
||||||
|
write_typeid_writer :: proc(writer: io.Writer, id: typeid) {
|
||||||
|
write_type(writer, type_info_of(id));
|
||||||
|
}
|
||||||
|
|
||||||
write_type :: proc(buf: ^strings.Builder, ti: ^Type_Info) {
|
write_typeid :: proc{
|
||||||
|
write_typeid_builder,
|
||||||
|
write_typeid_writer,
|
||||||
|
};
|
||||||
|
|
||||||
|
write_type :: proc{
|
||||||
|
write_type_builder,
|
||||||
|
write_type_writer,
|
||||||
|
};
|
||||||
|
|
||||||
|
write_type_builder :: proc(buf: ^strings.Builder, ti: ^Type_Info) -> int {
|
||||||
|
return write_type_writer(strings.to_writer(buf), ti);
|
||||||
|
}
|
||||||
|
write_type_writer :: proc(w: io.Writer, ti: ^Type_Info) -> (n: int) {
|
||||||
using strings;
|
using strings;
|
||||||
if ti == nil {
|
if ti == nil {
|
||||||
write_string(buf, "nil");
|
return write_string(w, "nil");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_n1 :: proc(err: io.Error) -> int { return 1 if err == nil else 0; };
|
||||||
|
_n2 :: proc(n: int, _: io.Error) -> int { return n; };
|
||||||
|
_n :: proc{_n1, _n2};
|
||||||
|
|
||||||
switch info in ti.variant {
|
switch info in ti.variant {
|
||||||
case Type_Info_Named:
|
case Type_Info_Named:
|
||||||
write_string(buf, info.name);
|
return write_string(w, info.name);
|
||||||
case Type_Info_Integer:
|
case Type_Info_Integer:
|
||||||
switch ti.id {
|
switch ti.id {
|
||||||
case int: write_string(buf, "int");
|
case int: return write_string(w, "int");
|
||||||
case uint: write_string(buf, "uint");
|
case uint: return write_string(w, "uint");
|
||||||
case uintptr: write_string(buf, "uintptr");
|
case uintptr: return write_string(w, "uintptr");
|
||||||
case:
|
case:
|
||||||
write_byte(buf, 'i' if info.signed else 'u');
|
n += _n(io.write_byte(w, 'i' if info.signed else 'u'));
|
||||||
write_i64(buf, i64(8*ti.size), 10);
|
n += _n(io.write_i64(w, i64(8*ti.size), 10));
|
||||||
switch info.endianness {
|
switch info.endianness {
|
||||||
case .Platform: // Okay
|
case .Platform: // Okay
|
||||||
case .Little: write_string(buf, "le");
|
case .Little: n += write_string(w, "le");
|
||||||
case .Big: write_string(buf, "be");
|
case .Big: n += write_string(w, "be");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case Type_Info_Rune:
|
case Type_Info_Rune:
|
||||||
write_string(buf, "rune");
|
n += _n(io.write_string(w, "rune"));
|
||||||
case Type_Info_Float:
|
case Type_Info_Float:
|
||||||
write_byte(buf, 'f');
|
n += _n(io.write_byte(w, 'f'));
|
||||||
write_i64(buf, i64(8*ti.size), 10);
|
n += _n(io.write_i64(w, i64(8*ti.size), 10));
|
||||||
switch info.endianness {
|
switch info.endianness {
|
||||||
case .Platform: // Okay
|
case .Platform: // Okay
|
||||||
case .Little: write_string(buf, "le");
|
case .Little: n += write_string(w, "le");
|
||||||
case .Big: write_string(buf, "be");
|
case .Big: n += write_string(w, "be");
|
||||||
}
|
}
|
||||||
case Type_Info_Complex:
|
case Type_Info_Complex:
|
||||||
write_string(buf, "complex");
|
n += _n(io.write_string(w, "complex"));
|
||||||
write_i64(buf, i64(8*ti.size), 10);
|
n += _n(io.write_i64(w, i64(8*ti.size), 10));
|
||||||
case Type_Info_Quaternion:
|
case Type_Info_Quaternion:
|
||||||
write_string(buf, "quaternion");
|
n += _n(io.write_string(w, "quaternion"));
|
||||||
write_i64(buf, i64(8*ti.size), 10);
|
n += _n(io.write_i64(w, i64(8*ti.size), 10));
|
||||||
case Type_Info_String:
|
case Type_Info_String:
|
||||||
if info.is_cstring {
|
if info.is_cstring {
|
||||||
write_string(buf, "cstring");
|
n += write_string(w, "cstring");
|
||||||
} else {
|
} else {
|
||||||
write_string(buf, "string");
|
n += write_string(w, "string");
|
||||||
}
|
}
|
||||||
case Type_Info_Boolean:
|
case Type_Info_Boolean:
|
||||||
switch ti.id {
|
switch ti.id {
|
||||||
case bool: write_string(buf, "bool");
|
case bool: n += write_string(w, "bool");
|
||||||
case:
|
case:
|
||||||
write_byte(buf, 'b');
|
n += _n(io.write_byte(w, 'b'));
|
||||||
write_i64(buf, i64(8*ti.size), 10);
|
n += _n(io.write_i64(w, i64(8*ti.size), 10));
|
||||||
}
|
}
|
||||||
case Type_Info_Any:
|
case Type_Info_Any:
|
||||||
write_string(buf, "any");
|
n += write_string(w, "any");
|
||||||
|
|
||||||
case Type_Info_Type_Id:
|
case Type_Info_Type_Id:
|
||||||
write_string(buf, "typeid");
|
n += write_string(w, "typeid");
|
||||||
|
|
||||||
case Type_Info_Pointer:
|
case Type_Info_Pointer:
|
||||||
if info.elem == nil {
|
if info.elem == nil {
|
||||||
write_string(buf, "rawptr");
|
write_string(w, "rawptr");
|
||||||
} else {
|
} else {
|
||||||
write_string(buf, "^");
|
write_string(w, "^");
|
||||||
write_type(buf, info.elem);
|
write_type(w, info.elem);
|
||||||
}
|
}
|
||||||
case Type_Info_Procedure:
|
case Type_Info_Procedure:
|
||||||
write_string(buf, "proc");
|
n += write_string(w, "proc");
|
||||||
if info.params == nil {
|
if info.params == nil {
|
||||||
write_string(buf, "()");
|
n += write_string(w, "()");
|
||||||
} else {
|
} else {
|
||||||
t := info.params.variant.(Type_Info_Tuple);
|
t := info.params.variant.(Type_Info_Tuple);
|
||||||
write_string(buf, "(");
|
n += write_string(w, "(");
|
||||||
for t, i in t.types {
|
for t, i in t.types {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
write_string(buf, ", ");
|
n += write_string(w, ", ");
|
||||||
}
|
}
|
||||||
write_type(buf, t);
|
n += write_type(w, t);
|
||||||
}
|
}
|
||||||
write_string(buf, ")");
|
n += write_string(w, ")");
|
||||||
}
|
}
|
||||||
if info.results != nil {
|
if info.results != nil {
|
||||||
write_string(buf, " -> ");
|
n += write_string(w, " -> ");
|
||||||
write_type(buf, info.results);
|
n += write_type(w, info.results);
|
||||||
}
|
}
|
||||||
case Type_Info_Tuple:
|
case Type_Info_Tuple:
|
||||||
count := len(info.names);
|
count := len(info.names);
|
||||||
if count != 1 { write_string(buf, "("); }
|
if count != 1 { n += write_string(w, "("); }
|
||||||
for name, i in info.names {
|
for name, i in info.names {
|
||||||
if i > 0 { write_string(buf, ", "); }
|
if i > 0 { n += write_string(w, ", "); }
|
||||||
|
|
||||||
t := info.types[i];
|
t := info.types[i];
|
||||||
|
|
||||||
if len(name) > 0 {
|
if len(name) > 0 {
|
||||||
write_string(buf, name);
|
n += write_string(w, name);
|
||||||
write_string(buf, ": ");
|
n += write_string(w, ": ");
|
||||||
}
|
}
|
||||||
write_type(buf, t);
|
n += write_type(w, t);
|
||||||
}
|
}
|
||||||
if count != 1 { write_string(buf, ")"); }
|
if count != 1 { n += write_string(w, ")"); }
|
||||||
|
|
||||||
case Type_Info_Array:
|
case Type_Info_Array:
|
||||||
write_string(buf, "[");
|
n += _n(io.write_string(w, "["));
|
||||||
write_i64(buf, i64(info.count), 10);
|
n += _n(io.write_i64(w, i64(info.count), 10));
|
||||||
write_string(buf, "]");
|
n += _n(io.write_string(w, "]"));
|
||||||
write_type(buf, info.elem);
|
n += write_type(w, info.elem);
|
||||||
|
|
||||||
case Type_Info_Enumerated_Array:
|
case Type_Info_Enumerated_Array:
|
||||||
write_string(buf, "[");
|
n += write_string(w, "[");
|
||||||
write_type(buf, info.index);
|
n += write_type(w, info.index);
|
||||||
write_string(buf, "]");
|
n += write_string(w, "]");
|
||||||
write_type(buf, info.elem);
|
n += write_type(w, info.elem);
|
||||||
|
|
||||||
case Type_Info_Dynamic_Array:
|
case Type_Info_Dynamic_Array:
|
||||||
write_string(buf, "[dynamic]");
|
n += _n(io.write_string(w, "[dynamic]"));
|
||||||
write_type(buf, info.elem);
|
n += write_type(w, info.elem);
|
||||||
case Type_Info_Slice:
|
case Type_Info_Slice:
|
||||||
write_string(buf, "[]");
|
n += _n(io.write_string(w, "[]"));
|
||||||
write_type(buf, info.elem);
|
n += write_type(w, info.elem);
|
||||||
|
|
||||||
case Type_Info_Map:
|
case Type_Info_Map:
|
||||||
write_string(buf, "map[");
|
n += _n(io.write_string(w, "map["));
|
||||||
write_type(buf, info.key);
|
n += write_type(w, info.key);
|
||||||
write_byte(buf, ']');
|
n += _n(io.write_byte(w, ']'));
|
||||||
write_type(buf, info.value);
|
n += write_type(w, info.value);
|
||||||
|
|
||||||
case Type_Info_Struct:
|
case Type_Info_Struct:
|
||||||
switch info.soa_kind {
|
switch info.soa_kind {
|
||||||
case .None: // Ignore
|
case .None: // Ignore
|
||||||
case .Fixed:
|
case .Fixed:
|
||||||
write_string(buf, "#soa[");
|
n += _n(io.write_string(w, "#soa["));
|
||||||
write_i64(buf, i64(info.soa_len));
|
n += _n(io.write_i64(w, i64(info.soa_len)));
|
||||||
write_byte(buf, ']');
|
n += _n(io.write_byte(w, ']'));
|
||||||
write_type(buf, info.soa_base_type);
|
n += write_type(w, info.soa_base_type);
|
||||||
return;
|
return;
|
||||||
case .Slice:
|
case .Slice:
|
||||||
write_string(buf, "#soa[]");
|
n += _n(io.write_string(w, "#soa[]"));
|
||||||
write_type(buf, info.soa_base_type);
|
n += write_type(w, info.soa_base_type);
|
||||||
return;
|
return;
|
||||||
case .Dynamic:
|
case .Dynamic:
|
||||||
write_string(buf, "#soa[dynamic]");
|
n += _n(io.write_string(w, "#soa[dynamic]"));
|
||||||
write_type(buf, info.soa_base_type);
|
n += write_type(w, info.soa_base_type);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
write_string(buf, "struct ");
|
n += write_string(w, "struct ");
|
||||||
if info.is_packed { write_string(buf, "#packed "); }
|
if info.is_packed { n += write_string(w, "#packed "); }
|
||||||
if info.is_raw_union { write_string(buf, "#raw_union "); }
|
if info.is_raw_union { n += write_string(w, "#raw_union "); }
|
||||||
if info.custom_align {
|
if info.custom_align {
|
||||||
write_string(buf, "#align ");
|
n += _n(io.write_string(w, "#align "));
|
||||||
write_i64(buf, i64(ti.align), 10);
|
n += _n(io.write_i64(w, i64(ti.align), 10));
|
||||||
write_byte(buf, ' ');
|
n += _n(io.write_byte(w, ' '));
|
||||||
}
|
}
|
||||||
write_byte(buf, '{');
|
n += _n(io.write_byte(w, '{'));
|
||||||
for name, i in info.names {
|
for name, i in info.names {
|
||||||
if i > 0 { write_string(buf, ", "); }
|
if i > 0 { n += write_string(w, ", "); }
|
||||||
write_string(buf, name);
|
n += _n(io.write_string(w, name));
|
||||||
write_string(buf, ": ");
|
n += _n(io.write_string(w, ": "));
|
||||||
write_type(buf, info.types[i]);
|
n += write_type(w, info.types[i]);
|
||||||
}
|
}
|
||||||
write_byte(buf, '}');
|
n += _n(io.write_byte(w, '}'));
|
||||||
|
|
||||||
case Type_Info_Union:
|
case Type_Info_Union:
|
||||||
write_string(buf, "union ");
|
n += write_string(w, "union ");
|
||||||
if info.custom_align {
|
if info.custom_align {
|
||||||
write_string(buf, "#align ");
|
n += write_string(w, "#align ");
|
||||||
write_i64(buf, i64(ti.align), 10);
|
n += _n(io.write_i64(w, i64(ti.align), 10));
|
||||||
write_byte(buf, ' ');
|
n += _n(io.write_byte(w, ' '));
|
||||||
}
|
}
|
||||||
write_byte(buf, '{');
|
n += _n(io.write_byte(w, '{'));
|
||||||
for variant, i in info.variants {
|
for variant, i in info.variants {
|
||||||
if i > 0 { write_string(buf, ", "); }
|
if i > 0 { n += write_string(w, ", "); }
|
||||||
write_type(buf, variant);
|
n += write_type(w, variant);
|
||||||
}
|
}
|
||||||
write_byte(buf, '}');
|
n += _n(io.write_byte(w, '}'));
|
||||||
|
|
||||||
case Type_Info_Enum:
|
case Type_Info_Enum:
|
||||||
write_string(buf, "enum ");
|
n += write_string(w, "enum ");
|
||||||
write_type(buf, info.base);
|
n += write_type(w, info.base);
|
||||||
write_string(buf, " {");
|
n += write_string(w, " {");
|
||||||
for name, i in info.names {
|
for name, i in info.names {
|
||||||
if i > 0 { write_string(buf, ", "); }
|
if i > 0 { n += write_string(w, ", "); }
|
||||||
write_string(buf, name);
|
n += write_string(w, name);
|
||||||
}
|
}
|
||||||
write_byte(buf, '}');
|
n += _n(io.write_byte(w, '}'));
|
||||||
|
|
||||||
case Type_Info_Bit_Field:
|
case Type_Info_Bit_Field:
|
||||||
write_string(buf, "bit_field ");
|
n += write_string(w, "bit_field ");
|
||||||
if ti.align != 1 {
|
if ti.align != 1 {
|
||||||
write_string(buf, "#align ");
|
n += write_string(w, "#align ");
|
||||||
write_i64(buf, i64(ti.align), 10);
|
n += _n(io.write_i64(w, i64(ti.align), 10));
|
||||||
write_byte(buf, ' ');
|
n += _n(io.write_byte(w, ' '));
|
||||||
}
|
}
|
||||||
write_string(buf, " {");
|
n += write_string(w, " {");
|
||||||
for name, i in info.names {
|
for name, i in info.names {
|
||||||
if i > 0 { write_string(buf, ", "); }
|
if i > 0 { n += write_string(w, ", "); }
|
||||||
write_string(buf, name);
|
n += write_string(w, name);
|
||||||
write_string(buf, ": ");
|
n += write_string(w, ": ");
|
||||||
write_i64(buf, i64(info.bits[i]), 10);
|
n += _n(io.write_i64(w, i64(info.bits[i]), 10));
|
||||||
}
|
}
|
||||||
write_byte(buf, '}');
|
n += _n(io.write_byte(w, '}'));
|
||||||
|
|
||||||
case Type_Info_Bit_Set:
|
case Type_Info_Bit_Set:
|
||||||
write_string(buf, "bit_set[");
|
n += write_string(w, "bit_set[");
|
||||||
switch {
|
switch {
|
||||||
case is_enum(info.elem):
|
case is_enum(info.elem):
|
||||||
write_type(buf, info.elem);
|
n += write_type(w, info.elem);
|
||||||
case is_rune(info.elem):
|
case is_rune(info.elem):
|
||||||
write_encoded_rune(buf, rune(info.lower));
|
n += write_encoded_rune(w, rune(info.lower));
|
||||||
write_string(buf, "..");
|
n += write_string(w, "..");
|
||||||
write_encoded_rune(buf, rune(info.upper));
|
n += write_encoded_rune(w, rune(info.upper));
|
||||||
case:
|
case:
|
||||||
write_i64(buf, info.lower, 10);
|
n += _n(io.write_i64(w, info.lower, 10));
|
||||||
write_string(buf, "..");
|
n += write_string(w, "..");
|
||||||
write_i64(buf, info.upper, 10);
|
n += _n(io.write_i64(w, info.upper, 10));
|
||||||
}
|
}
|
||||||
if info.underlying != nil {
|
if info.underlying != nil {
|
||||||
write_string(buf, "; ");
|
n += write_string(w, "; ");
|
||||||
write_type(buf, info.underlying);
|
n += write_type(w, info.underlying);
|
||||||
}
|
}
|
||||||
write_byte(buf, ']');
|
n += _n(io.write_byte(w, ']'));
|
||||||
|
|
||||||
case Type_Info_Opaque:
|
case Type_Info_Opaque:
|
||||||
write_string(buf, "opaque ");
|
n += write_string(w, "#opaque ");
|
||||||
write_type(buf, info.elem);
|
n += write_type(w, info.elem);
|
||||||
|
|
||||||
case Type_Info_Simd_Vector:
|
case Type_Info_Simd_Vector:
|
||||||
if info.is_x86_mmx {
|
if info.is_x86_mmx {
|
||||||
write_string(buf, "intrinsics.x86_mmx");
|
n += write_string(w, "intrinsics.x86_mmx");
|
||||||
} else {
|
} else {
|
||||||
write_string(buf, "#simd[");
|
n += write_string(w, "#simd[");
|
||||||
write_i64(buf, i64(info.count));
|
n += _n(io.write_i64(w, i64(info.count)));
|
||||||
write_byte(buf, ']');
|
n += _n(io.write_byte(w, ']'));
|
||||||
write_type(buf, info.elem);
|
n += write_type(w, info.elem);
|
||||||
}
|
}
|
||||||
|
|
||||||
case Type_Info_Relative_Pointer:
|
case Type_Info_Relative_Pointer:
|
||||||
write_string(buf, "#relative(");
|
n += write_string(w, "#relative(");
|
||||||
write_type(buf, info.base_integer);
|
n += write_type(w, info.base_integer);
|
||||||
write_string(buf, ") ");
|
n += write_string(w, ") ");
|
||||||
write_type(buf, info.pointer);
|
n += write_type(w, info.pointer);
|
||||||
|
|
||||||
case Type_Info_Relative_Slice:
|
case Type_Info_Relative_Slice:
|
||||||
write_string(buf, "#relative(");
|
n += write_string(w, "#relative(");
|
||||||
write_type(buf, info.base_integer);
|
n += write_type(w, info.base_integer);
|
||||||
write_string(buf, ") ");
|
n += write_string(w, ") ");
|
||||||
write_type(buf, info.slice);
|
n += write_type(w, info.slice);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+32
-1282
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,838 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
Maybe :: union(T: typeid) #maybe {T};
|
||||||
|
|
||||||
|
@thread_local global_default_temp_allocator_data: Default_Temp_Allocator;
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
init_global_temporary_allocator :: proc(size: int, backup_allocator := context.allocator) {
|
||||||
|
default_temp_allocator_init(&global_default_temp_allocator_data, size, backup_allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
copy_slice :: proc "contextless" (dst, src: $T/[]$E) -> int {
|
||||||
|
n := max(0, min(len(dst), len(src)));
|
||||||
|
if n > 0 {
|
||||||
|
mem_copy(raw_data(dst), raw_data(src), n*size_of(E));
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
@builtin
|
||||||
|
copy_from_string :: proc "contextless" (dst: $T/[]$E/u8, src: $S/string) -> int {
|
||||||
|
n := max(0, min(len(dst), len(src)));
|
||||||
|
if n > 0 {
|
||||||
|
mem_copy(raw_data(dst), raw_data(src), n);
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
@builtin
|
||||||
|
copy :: proc{copy_slice, copy_from_string};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) {
|
||||||
|
bounds_check_error_loc(loc, index, len(array));
|
||||||
|
n := len(array)-1;
|
||||||
|
if index != n {
|
||||||
|
array[index] = array[n];
|
||||||
|
}
|
||||||
|
pop(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
ordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) {
|
||||||
|
bounds_check_error_loc(loc, index, len(array));
|
||||||
|
if index+1 < len(array) {
|
||||||
|
copy(array[index:], array[index+1:]);
|
||||||
|
}
|
||||||
|
pop(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
remove_range :: proc(array: ^$D/[dynamic]$T, lo, hi: int, loc := #caller_location) {
|
||||||
|
slice_expr_error_lo_hi_loc(loc, lo, hi, len(array));
|
||||||
|
n := max(hi-lo, 0);
|
||||||
|
if n > 0 {
|
||||||
|
if hi != len(array) {
|
||||||
|
copy(array[lo:], array[hi:]);
|
||||||
|
}
|
||||||
|
(^Raw_Dynamic_Array)(array).len -= n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
|
||||||
|
assert(len(array) > 0, "", loc);
|
||||||
|
res = array[len(array)-1];
|
||||||
|
(^Raw_Dynamic_Array)(array).len -= 1;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
pop_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
|
||||||
|
if len(array) == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res, ok = array[len(array)-1], true;
|
||||||
|
(^Raw_Dynamic_Array)(array).len -= 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
pop_front :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
|
||||||
|
assert(len(array) > 0, "", loc);
|
||||||
|
res = array[0];
|
||||||
|
if len(array) > 1 {
|
||||||
|
copy(array[0:], array[1:]);
|
||||||
|
}
|
||||||
|
(^Raw_Dynamic_Array)(array).len -= 1;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
pop_front_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
|
||||||
|
if len(array) == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res, ok = array[0], true;
|
||||||
|
if len(array) > 1 {
|
||||||
|
copy(array[0:], array[1:]);
|
||||||
|
}
|
||||||
|
(^Raw_Dynamic_Array)(array).len -= 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
clear :: proc{clear_dynamic_array, clear_map};
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
reserve :: proc{reserve_dynamic_array, reserve_map};
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
resize :: proc{resize_dynamic_array};
|
||||||
|
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
free :: proc{mem_free};
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
free_all :: proc{mem_free_all};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) {
|
||||||
|
mem_free(raw_data(str), allocator, loc);
|
||||||
|
}
|
||||||
|
@builtin
|
||||||
|
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) {
|
||||||
|
mem_free((^byte)(str), allocator, loc);
|
||||||
|
}
|
||||||
|
@builtin
|
||||||
|
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) {
|
||||||
|
mem_free(raw_data(array), array.allocator, loc);
|
||||||
|
}
|
||||||
|
@builtin
|
||||||
|
delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) {
|
||||||
|
mem_free(raw_data(array), allocator, loc);
|
||||||
|
}
|
||||||
|
@builtin
|
||||||
|
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) {
|
||||||
|
raw := transmute(Raw_Map)m;
|
||||||
|
delete_slice(raw.hashes);
|
||||||
|
mem_free(raw.entries.data, raw.entries.allocator, loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
delete :: proc{
|
||||||
|
delete_string,
|
||||||
|
delete_cstring,
|
||||||
|
delete_dynamic_array,
|
||||||
|
delete_slice,
|
||||||
|
delete_map,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// The new built-in procedure allocates memory. The first argument is a type, not a value, and the value
|
||||||
|
// return is a pointer to a newly allocated value of that type using the specified allocator, default is context.allocator
|
||||||
|
@builtin
|
||||||
|
new :: inline proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> ^T {
|
||||||
|
ptr := (^T)(mem_alloc(size_of(T), align_of(T), allocator, loc));
|
||||||
|
if ptr != nil { ptr^ = T{}; }
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
new_clone :: inline proc(data: $T, allocator := context.allocator, loc := #caller_location) -> ^T {
|
||||||
|
ptr := (^T)(mem_alloc(size_of(T), align_of(T), allocator, loc));
|
||||||
|
if ptr != nil { ptr^ = data; }
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
make_aligned :: proc($T: typeid/[]$E, auto_cast len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> T {
|
||||||
|
make_slice_error_loc(loc, len);
|
||||||
|
data := mem_alloc(size_of(E)*len, alignment, allocator, loc);
|
||||||
|
if data == nil && size_of(E) != 0 {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
// mem_zero(data, size_of(E)*len);
|
||||||
|
s := Raw_Slice{data, len};
|
||||||
|
return transmute(T)s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
make_slice :: inline proc($T: typeid/[]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
|
||||||
|
return make_aligned(T, len, align_of(E), allocator, loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> T {
|
||||||
|
return make_dynamic_array_len_cap(T, 0, 16, allocator, loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
|
||||||
|
return make_dynamic_array_len_cap(T, len, len, allocator, loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, auto_cast len: int, auto_cast cap: int, allocator := context.allocator, loc := #caller_location) -> T {
|
||||||
|
make_dynamic_array_error_loc(loc, len, cap);
|
||||||
|
data := mem_alloc(size_of(E)*cap, align_of(E), allocator, loc);
|
||||||
|
s := Raw_Dynamic_Array{data, len, cap, allocator};
|
||||||
|
if data == nil && size_of(E) != 0 {
|
||||||
|
s.len, s.cap = 0, 0;
|
||||||
|
}
|
||||||
|
// mem_zero(data, size_of(E)*cap);
|
||||||
|
return transmute(T)s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
make_map :: proc($T: typeid/map[$K]$E, auto_cast cap: int = 16, allocator := context.allocator, loc := #caller_location) -> T {
|
||||||
|
make_map_expr_error_loc(loc, cap);
|
||||||
|
context.allocator = allocator;
|
||||||
|
|
||||||
|
m: T;
|
||||||
|
reserve_map(&m, cap);
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The make built-in procedure allocates and initializes a value of type slice, dynamic array, or map (only)
|
||||||
|
// Similar to new, the first argument is a type, not a value. Unlike new, make's return type is the same as the
|
||||||
|
// type of its argument, not a pointer to it.
|
||||||
|
// Make uses the specified allocator, default is context.allocator, default is context.allocator
|
||||||
|
@builtin
|
||||||
|
make :: proc{
|
||||||
|
make_slice,
|
||||||
|
make_dynamic_array,
|
||||||
|
make_dynamic_array_len,
|
||||||
|
make_dynamic_array_len_cap,
|
||||||
|
make_map,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
clear_map :: inline proc "contextless" (m: ^$T/map[$K]$V) {
|
||||||
|
if m == nil {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
raw_map := (^Raw_Map)(m);
|
||||||
|
entries := (^Raw_Dynamic_Array)(&raw_map.entries);
|
||||||
|
entries.len = 0;
|
||||||
|
for _, i in raw_map.hashes {
|
||||||
|
raw_map.hashes[i] = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int) {
|
||||||
|
if m != nil {
|
||||||
|
__dynamic_map_reserve(__get_map_header(m), capacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The delete_key built-in procedure deletes the element with the specified key (m[key]) from the map.
|
||||||
|
// If m is nil, or there is no such element, this procedure is a no-op
|
||||||
|
@builtin
|
||||||
|
delete_key :: proc(m: ^$T/map[$K]$V, key: K) {
|
||||||
|
if m != nil {
|
||||||
|
key := key;
|
||||||
|
__dynamic_map_delete_key(__get_map_header(m), __get_map_hash(&key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) {
|
||||||
|
if array == nil {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
arg_len := 1;
|
||||||
|
|
||||||
|
if cap(array) < len(array)+arg_len {
|
||||||
|
cap := 2 * cap(array) + max(8, arg_len);
|
||||||
|
_ = reserve(array, cap, loc);
|
||||||
|
}
|
||||||
|
arg_len = min(cap(array)-len(array), arg_len);
|
||||||
|
if arg_len > 0 {
|
||||||
|
a := (^Raw_Dynamic_Array)(array);
|
||||||
|
if size_of(E) != 0 {
|
||||||
|
data := (^E)(a.data);
|
||||||
|
assert(data != nil);
|
||||||
|
val := arg;
|
||||||
|
mem_copy(ptr_offset(data, a.len), &val, size_of(E));
|
||||||
|
}
|
||||||
|
a.len += arg_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) {
|
||||||
|
if array == nil {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
arg_len := len(args);
|
||||||
|
if arg_len <= 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if cap(array) < len(array)+arg_len {
|
||||||
|
cap := 2 * cap(array) + max(8, arg_len);
|
||||||
|
_ = reserve(array, cap, loc);
|
||||||
|
}
|
||||||
|
arg_len = min(cap(array)-len(array), arg_len);
|
||||||
|
if arg_len > 0 {
|
||||||
|
a := (^Raw_Dynamic_Array)(array);
|
||||||
|
if size_of(E) != 0 {
|
||||||
|
data := (^E)(a.data);
|
||||||
|
assert(data != nil);
|
||||||
|
mem_copy(ptr_offset(data, a.len), &args[0], size_of(E) * arg_len);
|
||||||
|
}
|
||||||
|
a.len += arg_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type
|
||||||
|
@builtin
|
||||||
|
append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) {
|
||||||
|
args := transmute([]E)arg;
|
||||||
|
append_elems(array=array, args=args, loc=loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_location) -> bool {
|
||||||
|
if array == nil {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
old_cap := cap(array);
|
||||||
|
if capacity <= old_cap {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if array.allocator.procedure == nil {
|
||||||
|
array.allocator = context.allocator;
|
||||||
|
}
|
||||||
|
assert(array.allocator.procedure != nil);
|
||||||
|
|
||||||
|
|
||||||
|
ti := type_info_of(typeid_of(T));
|
||||||
|
ti = type_info_base(ti);
|
||||||
|
si := &ti.variant.(Type_Info_Struct);
|
||||||
|
|
||||||
|
field_count := uintptr(len(si.offsets) - 3);
|
||||||
|
|
||||||
|
if field_count == 0 {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
cap_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 1)*size_of(rawptr));
|
||||||
|
assert(cap_ptr^ == old_cap);
|
||||||
|
|
||||||
|
|
||||||
|
old_size := 0;
|
||||||
|
new_size := 0;
|
||||||
|
|
||||||
|
max_align := 0;
|
||||||
|
for i in 0..<field_count {
|
||||||
|
type := si.types[i].variant.(Type_Info_Pointer).elem;
|
||||||
|
max_align = max(max_align, type.align);
|
||||||
|
|
||||||
|
old_size = align_forward_int(old_size, type.align);
|
||||||
|
new_size = align_forward_int(new_size, type.align);
|
||||||
|
|
||||||
|
old_size += type.size * old_cap;
|
||||||
|
new_size += type.size * capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
old_size = align_forward_int(old_size, max_align);
|
||||||
|
new_size = align_forward_int(new_size, max_align);
|
||||||
|
|
||||||
|
old_data := (^rawptr)(array)^;
|
||||||
|
|
||||||
|
new_data := array.allocator.procedure(
|
||||||
|
array.allocator.data, .Alloc, new_size, max_align,
|
||||||
|
nil, old_size, 0, loc,
|
||||||
|
);
|
||||||
|
if new_data == nil {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cap_ptr^ = capacity;
|
||||||
|
|
||||||
|
old_offset := 0;
|
||||||
|
new_offset := 0;
|
||||||
|
for i in 0..<field_count {
|
||||||
|
type := si.types[i].variant.(Type_Info_Pointer).elem;
|
||||||
|
max_align = max(max_align, type.align);
|
||||||
|
|
||||||
|
old_offset = align_forward_int(old_offset, type.align);
|
||||||
|
new_offset = align_forward_int(new_offset, type.align);
|
||||||
|
|
||||||
|
new_data_elem := rawptr(uintptr(new_data) + uintptr(new_offset));
|
||||||
|
old_data_elem := rawptr(uintptr(old_data) + uintptr(old_offset));
|
||||||
|
|
||||||
|
mem_copy(new_data_elem, old_data_elem, type.size * old_cap);
|
||||||
|
|
||||||
|
(^rawptr)(uintptr(array) + i*size_of(rawptr))^ = new_data_elem;
|
||||||
|
|
||||||
|
old_offset += type.size * old_cap;
|
||||||
|
new_offset += type.size * capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
array.allocator.procedure(
|
||||||
|
array.allocator.data, .Free, 0, max_align,
|
||||||
|
old_data, old_size, 0, loc,
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, arg: E, loc := #caller_location) {
|
||||||
|
if array == nil {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
arg_len := 1;
|
||||||
|
|
||||||
|
if cap(array) <= len(array)+arg_len {
|
||||||
|
cap := 2 * cap(array) + max(8, arg_len);
|
||||||
|
_ = reserve_soa(array, cap, loc);
|
||||||
|
}
|
||||||
|
arg_len = min(cap(array)-len(array), arg_len);
|
||||||
|
if arg_len > 0 {
|
||||||
|
ti := type_info_of(typeid_of(T));
|
||||||
|
ti = type_info_base(ti);
|
||||||
|
si := &ti.variant.(Type_Info_Struct);
|
||||||
|
field_count := uintptr(len(si.offsets) - 3);
|
||||||
|
|
||||||
|
if field_count == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data := (^rawptr)(array)^;
|
||||||
|
|
||||||
|
len_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 0)*size_of(rawptr));
|
||||||
|
|
||||||
|
|
||||||
|
soa_offset := 0;
|
||||||
|
item_offset := 0;
|
||||||
|
|
||||||
|
arg_copy := arg;
|
||||||
|
arg_ptr := &arg_copy;
|
||||||
|
|
||||||
|
max_align := 0;
|
||||||
|
for i in 0..<field_count {
|
||||||
|
type := si.types[i].variant.(Type_Info_Pointer).elem;
|
||||||
|
max_align = max(max_align, type.align);
|
||||||
|
|
||||||
|
soa_offset = align_forward_int(soa_offset, type.align);
|
||||||
|
item_offset = align_forward_int(item_offset, type.align);
|
||||||
|
|
||||||
|
dst := rawptr(uintptr(data) + uintptr(soa_offset) + uintptr(type.size * len_ptr^));
|
||||||
|
src := rawptr(uintptr(arg_ptr) + uintptr(item_offset));
|
||||||
|
mem_copy(dst, src, type.size);
|
||||||
|
|
||||||
|
soa_offset += type.size * cap(array);
|
||||||
|
item_offset += type.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
len_ptr^ += arg_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_location) {
|
||||||
|
if array == nil {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
arg_len := len(args);
|
||||||
|
if arg_len == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if cap(array) <= len(array)+arg_len {
|
||||||
|
cap := 2 * cap(array) + max(8, arg_len);
|
||||||
|
_ = reserve_soa(array, cap, loc);
|
||||||
|
}
|
||||||
|
arg_len = min(cap(array)-len(array), arg_len);
|
||||||
|
if arg_len > 0 {
|
||||||
|
ti := type_info_of(typeid_of(T));
|
||||||
|
ti = type_info_base(ti);
|
||||||
|
si := &ti.variant.(Type_Info_Struct);
|
||||||
|
field_count := uintptr(len(si.offsets) - 3);
|
||||||
|
|
||||||
|
if field_count == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data := (^rawptr)(array)^;
|
||||||
|
|
||||||
|
len_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 0)*size_of(rawptr));
|
||||||
|
|
||||||
|
|
||||||
|
soa_offset := 0;
|
||||||
|
item_offset := 0;
|
||||||
|
|
||||||
|
args_ptr := &args[0];
|
||||||
|
|
||||||
|
max_align := 0;
|
||||||
|
for i in 0..<field_count {
|
||||||
|
type := si.types[i].variant.(Type_Info_Pointer).elem;
|
||||||
|
max_align = max(max_align, type.align);
|
||||||
|
|
||||||
|
soa_offset = align_forward_int(soa_offset, type.align);
|
||||||
|
item_offset = align_forward_int(item_offset, type.align);
|
||||||
|
|
||||||
|
dst := uintptr(data) + uintptr(soa_offset) + uintptr(type.size * len_ptr^);
|
||||||
|
src := uintptr(args_ptr) + uintptr(item_offset);
|
||||||
|
for j in 0..<arg_len {
|
||||||
|
d := rawptr(dst + uintptr(j*type.size));
|
||||||
|
s := rawptr(src + uintptr(j*size_of(E)));
|
||||||
|
mem_copy(d, s, type.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
soa_offset += type.size * cap(array);
|
||||||
|
item_offset += type.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
len_ptr^ += arg_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The append_string built-in procedure appends multiple strings to the end of a [dynamic]u8 like type
|
||||||
|
@builtin
|
||||||
|
append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) {
|
||||||
|
for arg in args {
|
||||||
|
append(array = array, args = transmute([]E)(arg), loc = loc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The append built-in procedure appends elements to the end of a dynamic array
|
||||||
|
@builtin append :: proc{append_elem, append_elems, append_elem_string};
|
||||||
|
|
||||||
|
// The append_soa built-in procedure appends elements to the end of an #soa dynamic array
|
||||||
|
@builtin append_soa :: proc{append_soa_elem, append_soa_elems};
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) {
|
||||||
|
if array == nil {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resize(array, len(array)+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
insert_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
|
||||||
|
if array == nil {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
n := len(array);
|
||||||
|
m :: 1;
|
||||||
|
resize(array, n+m, loc);
|
||||||
|
if n+m <= len(array) {
|
||||||
|
when size_of(E) != 0 {
|
||||||
|
copy(array[index+m:], array[index:]);
|
||||||
|
array[index] = arg;
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
insert_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
|
||||||
|
if array == nil {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if len(args) == 0 {
|
||||||
|
ok = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
n := len(array);
|
||||||
|
m := len(args);
|
||||||
|
resize(array, n+m, loc);
|
||||||
|
if n+m <= len(array) {
|
||||||
|
when size_of(E) != 0 {
|
||||||
|
copy(array[index+m:], array[index:]);
|
||||||
|
copy(array[index:], args);
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
insert_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool) #no_bounds_check {
|
||||||
|
if array == nil {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if len(args) == 0 {
|
||||||
|
ok = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
n := len(array);
|
||||||
|
m := len(args);
|
||||||
|
resize(array, n+m, loc);
|
||||||
|
if n+m <= len(array) {
|
||||||
|
copy(array[index+m:], array[index:]);
|
||||||
|
copy(array[index:], args);
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin insert_at :: proc{insert_at_elem, insert_at_elems, insert_at_elem_string};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
clear_dynamic_array :: inline proc "contextless" (array: ^$T/[dynamic]$E) {
|
||||||
|
if array != nil {
|
||||||
|
(^Raw_Dynamic_Array)(array).len = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> bool {
|
||||||
|
if array == nil {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
a := (^Raw_Dynamic_Array)(array);
|
||||||
|
|
||||||
|
if capacity <= a.cap {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.allocator.procedure == nil {
|
||||||
|
a.allocator = context.allocator;
|
||||||
|
}
|
||||||
|
assert(a.allocator.procedure != nil);
|
||||||
|
|
||||||
|
old_size := a.cap * size_of(E);
|
||||||
|
new_size := capacity * size_of(E);
|
||||||
|
allocator := a.allocator;
|
||||||
|
|
||||||
|
new_data := allocator.procedure(
|
||||||
|
allocator.data, .Resize, new_size, align_of(E),
|
||||||
|
a.data, old_size, 0, loc,
|
||||||
|
);
|
||||||
|
if new_data == nil {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.data = new_data;
|
||||||
|
a.cap = capacity;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> bool {
|
||||||
|
if array == nil {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
a := (^Raw_Dynamic_Array)(array);
|
||||||
|
|
||||||
|
if length <= a.cap {
|
||||||
|
a.len = max(length, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.allocator.procedure == nil {
|
||||||
|
a.allocator = context.allocator;
|
||||||
|
}
|
||||||
|
assert(a.allocator.procedure != nil);
|
||||||
|
|
||||||
|
old_size := a.cap * size_of(E);
|
||||||
|
new_size := length * size_of(E);
|
||||||
|
allocator := a.allocator;
|
||||||
|
|
||||||
|
new_data := allocator.procedure(
|
||||||
|
allocator.data, .Resize, new_size, align_of(E),
|
||||||
|
a.data, old_size, 0, loc,
|
||||||
|
);
|
||||||
|
if new_data == nil {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.data = new_data;
|
||||||
|
a.len = length;
|
||||||
|
a.cap = length;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
incl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S {
|
||||||
|
s^ |= {elem};
|
||||||
|
return s^;
|
||||||
|
}
|
||||||
|
@builtin
|
||||||
|
incl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S {
|
||||||
|
for elem in elems {
|
||||||
|
s^ |= {elem};
|
||||||
|
}
|
||||||
|
return s^;
|
||||||
|
}
|
||||||
|
@builtin
|
||||||
|
incl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S {
|
||||||
|
s^ |= other;
|
||||||
|
return s^;
|
||||||
|
}
|
||||||
|
@builtin
|
||||||
|
excl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S {
|
||||||
|
s^ &~= {elem};
|
||||||
|
return s^;
|
||||||
|
}
|
||||||
|
@builtin
|
||||||
|
excl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S {
|
||||||
|
for elem in elems {
|
||||||
|
s^ &~= {elem};
|
||||||
|
}
|
||||||
|
return s^;
|
||||||
|
}
|
||||||
|
@builtin
|
||||||
|
excl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S {
|
||||||
|
s^ &~= other;
|
||||||
|
return s^;
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin incl :: proc{incl_elem, incl_elems, incl_bit_set};
|
||||||
|
@builtin excl :: proc{excl_elem, excl_elems, excl_bit_set};
|
||||||
|
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
card :: proc(s: $S/bit_set[$E; $U]) -> int {
|
||||||
|
when size_of(S) == 1 {
|
||||||
|
foreign { @(link_name="llvm.ctpop.i8") count_ones :: proc(i: u8) -> u8 --- }
|
||||||
|
return int(count_ones(transmute(u8)s));
|
||||||
|
} else when size_of(S) == 2 {
|
||||||
|
foreign { @(link_name="llvm.ctpop.i16") count_ones :: proc(i: u16) -> u16 --- }
|
||||||
|
return int(count_ones(transmute(u16)s));
|
||||||
|
} else when size_of(S) == 4 {
|
||||||
|
foreign { @(link_name="llvm.ctpop.i32") count_ones :: proc(i: u32) -> u32 --- }
|
||||||
|
return int(count_ones(transmute(u32)s));
|
||||||
|
} else when size_of(S) == 8 {
|
||||||
|
foreign { @(link_name="llvm.ctpop.i64") count_ones :: proc(i: u64) -> u64 --- }
|
||||||
|
return int(count_ones(transmute(u64)s));
|
||||||
|
} else when size_of(S) == 16 {
|
||||||
|
foreign { @(link_name="llvm.ctpop.i128") count_ones :: proc(i: u128) -> u128 --- }
|
||||||
|
return int(count_ones(transmute(u128)s));
|
||||||
|
} else {
|
||||||
|
#panic("Unhandled card bit_set size");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
raw_array_data :: proc "contextless" (a: $P/^($T/[$N]$E)) -> ^E {
|
||||||
|
return (^E)(a);
|
||||||
|
}
|
||||||
|
@builtin
|
||||||
|
raw_slice_data :: proc "contextless" (s: $S/[]$E) -> ^E {
|
||||||
|
ptr := (transmute(Raw_Slice)s).data;
|
||||||
|
return (^E)(ptr);
|
||||||
|
}
|
||||||
|
@builtin
|
||||||
|
raw_dynamic_array_data :: proc "contextless" (s: $S/[dynamic]$E) -> ^E {
|
||||||
|
ptr := (transmute(Raw_Dynamic_Array)s).data;
|
||||||
|
return (^E)(ptr);
|
||||||
|
}
|
||||||
|
@builtin
|
||||||
|
raw_string_data :: proc "contextless" (s: $S/string) -> ^u8 {
|
||||||
|
return (transmute(Raw_String)s).data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
raw_data :: proc{raw_array_data, raw_slice_data, raw_dynamic_array_data, raw_string_data};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
@(disabled=ODIN_DISABLE_ASSERT)
|
||||||
|
assert :: proc(condition: bool, message := "", loc := #caller_location) {
|
||||||
|
if !condition {
|
||||||
|
proc(message: string, loc: Source_Code_Location) {
|
||||||
|
p := context.assertion_failure_proc;
|
||||||
|
if p == nil {
|
||||||
|
p = default_assertion_failure_proc;
|
||||||
|
}
|
||||||
|
p("runtime assertion", message, loc);
|
||||||
|
}(message, loc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
@(disabled=ODIN_DISABLE_ASSERT)
|
||||||
|
panic :: proc(message: string, loc := #caller_location) -> ! {
|
||||||
|
p := context.assertion_failure_proc;
|
||||||
|
if p == nil {
|
||||||
|
p = default_assertion_failure_proc;
|
||||||
|
}
|
||||||
|
p("panic", message, loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
@(disabled=ODIN_DISABLE_ASSERT)
|
||||||
|
unimplemented :: proc(message := "", loc := #caller_location) -> ! {
|
||||||
|
p := context.assertion_failure_proc;
|
||||||
|
if p == nil {
|
||||||
|
p = default_assertion_failure_proc;
|
||||||
|
}
|
||||||
|
p("not yet implemented", message, loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
@(disabled=ODIN_DISABLE_ASSERT)
|
||||||
|
unreachable :: proc(message := "", loc := #caller_location) -> ! {
|
||||||
|
p := context.assertion_failure_proc;
|
||||||
|
if p == nil {
|
||||||
|
p = default_assertion_failure_proc;
|
||||||
|
}
|
||||||
|
if message != "" {
|
||||||
|
p("internal error", message, loc);
|
||||||
|
} else {
|
||||||
|
p("internal error", "entered unreachable code", loc);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
__dynamic_array_make :: proc(array_: rawptr, elem_size, elem_align: int, len, cap: int, loc := #caller_location) {
|
||||||
|
array := (^Raw_Dynamic_Array)(array_);
|
||||||
|
array.allocator = context.allocator;
|
||||||
|
assert(array.allocator.procedure != nil);
|
||||||
|
|
||||||
|
if cap > 0 {
|
||||||
|
__dynamic_array_reserve(array_, elem_size, elem_align, cap, loc);
|
||||||
|
array.len = len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap: int, loc := #caller_location) -> bool {
|
||||||
|
array := (^Raw_Dynamic_Array)(array_);
|
||||||
|
|
||||||
|
// NOTE(tetra, 2020-01-26): We set the allocator before earlying-out below, because user code is usually written
|
||||||
|
// assuming that appending/reserving will set the allocator, if it is not already set.
|
||||||
|
if array.allocator.procedure == nil {
|
||||||
|
array.allocator = context.allocator;
|
||||||
|
}
|
||||||
|
assert(array.allocator.procedure != nil);
|
||||||
|
|
||||||
|
if cap <= array.cap {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
old_size := array.cap * elem_size;
|
||||||
|
new_size := cap * elem_size;
|
||||||
|
allocator := array.allocator;
|
||||||
|
|
||||||
|
new_data := allocator.procedure(allocator.data, .Resize, new_size, elem_align, array.data, old_size, 0, loc);
|
||||||
|
if new_data != nil || elem_size == 0 {
|
||||||
|
array.data = new_data;
|
||||||
|
array.cap = cap;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
__dynamic_array_resize :: proc(array_: rawptr, elem_size, elem_align: int, len: int, loc := #caller_location) -> bool {
|
||||||
|
array := (^Raw_Dynamic_Array)(array_);
|
||||||
|
|
||||||
|
ok := __dynamic_array_reserve(array_, elem_size, elem_align, len, loc);
|
||||||
|
if ok {
|
||||||
|
array.len = len;
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int,
|
||||||
|
items: rawptr, item_count: int, loc := #caller_location) -> int {
|
||||||
|
array := (^Raw_Dynamic_Array)(array_);
|
||||||
|
|
||||||
|
if items == nil {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if item_count <= 0 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ok := true;
|
||||||
|
if array.cap <= array.len+item_count {
|
||||||
|
cap := 2 * array.cap + max(8, item_count);
|
||||||
|
ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc);
|
||||||
|
}
|
||||||
|
// TODO(bill): Better error handling for failed reservation
|
||||||
|
if !ok {
|
||||||
|
return array.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(array.data != nil);
|
||||||
|
data := uintptr(array.data) + uintptr(elem_size*array.len);
|
||||||
|
|
||||||
|
mem_copy(rawptr(data), items, elem_size * item_count);
|
||||||
|
array.len += item_count;
|
||||||
|
return array.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
__dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: int, loc := #caller_location) -> int {
|
||||||
|
array := (^Raw_Dynamic_Array)(array_);
|
||||||
|
|
||||||
|
ok := true;
|
||||||
|
if array.cap <= array.len+1 {
|
||||||
|
cap := 2 * array.cap + max(8, 1);
|
||||||
|
ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc);
|
||||||
|
}
|
||||||
|
// TODO(bill): Better error handling for failed reservation
|
||||||
|
if !ok {
|
||||||
|
return array.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(array.data != nil);
|
||||||
|
data := uintptr(array.data) + uintptr(elem_size*array.len);
|
||||||
|
mem_zero(rawptr(data), elem_size);
|
||||||
|
array.len += 1;
|
||||||
|
return array.len;
|
||||||
|
}
|
||||||
@@ -0,0 +1,394 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
import "intrinsics"
|
||||||
|
_ :: intrinsics;
|
||||||
|
|
||||||
|
INITIAL_MAP_CAP :: 16;
|
||||||
|
|
||||||
|
// Temporary data structure for comparing hashes and keys
|
||||||
|
Map_Hash :: struct {
|
||||||
|
hash: uintptr,
|
||||||
|
key_ptr: rawptr, // address of Map_Entry_Header.key
|
||||||
|
}
|
||||||
|
|
||||||
|
__get_map_hash :: proc "contextless" (k: ^$K) -> (map_hash: Map_Hash) {
|
||||||
|
hasher := intrinsics.type_hasher_proc(K);
|
||||||
|
map_hash.key_ptr = k;
|
||||||
|
map_hash.hash = hasher(k, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
__get_map_hash_from_entry :: proc "contextless" (h: Map_Header, entry: ^Map_Entry_Header) -> (hash: Map_Hash) {
|
||||||
|
hash.hash = entry.hash;
|
||||||
|
hash.key_ptr = rawptr(uintptr(entry) + h.key_offset);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Map_Find_Result :: struct {
|
||||||
|
hash_index: int,
|
||||||
|
entry_prev: int,
|
||||||
|
entry_index: int,
|
||||||
|
}
|
||||||
|
|
||||||
|
Map_Entry_Header :: struct {
|
||||||
|
hash: uintptr,
|
||||||
|
next: int,
|
||||||
|
/*
|
||||||
|
key: Key_Value,
|
||||||
|
value: Value_Type,
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
Map_Header :: struct {
|
||||||
|
m: ^Raw_Map,
|
||||||
|
equal: Equal_Proc,
|
||||||
|
|
||||||
|
entry_size: int,
|
||||||
|
entry_align: int,
|
||||||
|
|
||||||
|
key_offset: uintptr,
|
||||||
|
key_size: int,
|
||||||
|
|
||||||
|
value_offset: uintptr,
|
||||||
|
value_size: int,
|
||||||
|
}
|
||||||
|
|
||||||
|
INITIAL_HASH_SEED :: 0xcbf29ce484222325;
|
||||||
|
|
||||||
|
_fnv64a :: proc "contextless" (data: []byte, seed: u64 = INITIAL_HASH_SEED) -> u64 {
|
||||||
|
h: u64 = seed;
|
||||||
|
for b in data {
|
||||||
|
h = (h ~ u64(b)) * 0x100000001b3;
|
||||||
|
}
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
default_hash :: inline proc "contextless" (data: []byte) -> uintptr {
|
||||||
|
return uintptr(_fnv64a(data));
|
||||||
|
}
|
||||||
|
default_hash_string :: inline proc "contextless" (s: string) -> uintptr {
|
||||||
|
return default_hash(transmute([]byte)(s));
|
||||||
|
}
|
||||||
|
default_hash_ptr :: inline proc "contextless" (data: rawptr, size: int) -> uintptr {
|
||||||
|
s := Raw_Slice{data, size};
|
||||||
|
return default_hash(transmute([]byte)(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_default_hasher_const :: inline proc "contextless" (data: rawptr, seed: uintptr, $N: uint) -> uintptr where N <= 16 {
|
||||||
|
h := u64(seed) + 0xcbf29ce484222325;
|
||||||
|
p := uintptr(data);
|
||||||
|
inline for _ in 0..<N {
|
||||||
|
b := u64((^byte)(p)^);
|
||||||
|
h = (h ~ b) * 0x100000001b3;
|
||||||
|
p += 1;
|
||||||
|
}
|
||||||
|
return uintptr(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
default_hasher_n :: inline proc "contextless" (data: rawptr, seed: uintptr, N: int) -> uintptr {
|
||||||
|
h := u64(seed) + 0xcbf29ce484222325;
|
||||||
|
p := uintptr(data);
|
||||||
|
for _ in 0..<N {
|
||||||
|
b := u64((^byte)(p)^);
|
||||||
|
h = (h ~ b) * 0x100000001b3;
|
||||||
|
p += 1;
|
||||||
|
}
|
||||||
|
return uintptr(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(bill): There are loads of predefined ones to improve optimizations for small types
|
||||||
|
|
||||||
|
default_hasher1 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 1); }
|
||||||
|
default_hasher2 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 2); }
|
||||||
|
default_hasher3 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 3); }
|
||||||
|
default_hasher4 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 4); }
|
||||||
|
default_hasher5 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 5); }
|
||||||
|
default_hasher6 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 6); }
|
||||||
|
default_hasher7 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 7); }
|
||||||
|
default_hasher8 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 8); }
|
||||||
|
default_hasher9 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 9); }
|
||||||
|
default_hasher10 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 10); }
|
||||||
|
default_hasher11 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 11); }
|
||||||
|
default_hasher12 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 12); }
|
||||||
|
default_hasher13 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 13); }
|
||||||
|
default_hasher14 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 14); }
|
||||||
|
default_hasher15 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 15); }
|
||||||
|
default_hasher16 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 16); }
|
||||||
|
|
||||||
|
default_hasher_string :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr {
|
||||||
|
h := u64(seed) + 0xcbf29ce484222325;
|
||||||
|
str := (^[]byte)(data)^;
|
||||||
|
for b in str {
|
||||||
|
h = (h ~ u64(b)) * 0x100000001b3;
|
||||||
|
}
|
||||||
|
return uintptr(h);
|
||||||
|
}
|
||||||
|
default_hasher_cstring :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr {
|
||||||
|
h := u64(seed) + 0xcbf29ce484222325;
|
||||||
|
ptr := (^uintptr)(data)^;
|
||||||
|
for (^byte)(ptr)^ != 0 {
|
||||||
|
b := (^byte)(ptr)^;
|
||||||
|
h = (h ~ u64(b)) * 0x100000001b3;
|
||||||
|
ptr += 1;
|
||||||
|
}
|
||||||
|
return uintptr(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
source_code_location_hash :: proc(s: Source_Code_Location) -> uintptr {
|
||||||
|
hash := _fnv64a(transmute([]byte)s.file_path);
|
||||||
|
hash = hash ~ (u64(s.line) * 0x100000001b3);
|
||||||
|
hash = hash ~ (u64(s.column) * 0x100000001b3);
|
||||||
|
return uintptr(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
__get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header {
|
||||||
|
header := Map_Header{m = (^Raw_Map)(m)};
|
||||||
|
Entry :: struct {
|
||||||
|
hash: uintptr,
|
||||||
|
next: int,
|
||||||
|
key: K,
|
||||||
|
value: V,
|
||||||
|
};
|
||||||
|
|
||||||
|
header.equal = intrinsics.type_equal_proc(K);
|
||||||
|
|
||||||
|
header.entry_size = int(size_of(Entry));
|
||||||
|
header.entry_align = int(align_of(Entry));
|
||||||
|
|
||||||
|
header.key_offset = uintptr(offset_of(Entry, key));
|
||||||
|
header.key_size = int(size_of(K));
|
||||||
|
|
||||||
|
header.value_offset = uintptr(offset_of(Entry, value));
|
||||||
|
header.value_size = int(size_of(V));
|
||||||
|
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
__slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, loc := #caller_location) -> bool {
|
||||||
|
array := (^Raw_Slice)(array_);
|
||||||
|
|
||||||
|
if new_count < array.len {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(allocator.procedure != nil);
|
||||||
|
|
||||||
|
old_size := array.len*size_of(T);
|
||||||
|
new_size := new_count*size_of(T);
|
||||||
|
|
||||||
|
new_data := mem_resize(array.data, old_size, new_size, align_of(T), allocator, loc);
|
||||||
|
if new_data == nil {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
array.data = new_data;
|
||||||
|
array.len = new_count;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
__dynamic_map_reserve :: proc(using header: Map_Header, cap: int, loc := #caller_location) {
|
||||||
|
__dynamic_array_reserve(&m.entries, entry_size, entry_align, cap, loc);
|
||||||
|
|
||||||
|
old_len := len(m.hashes);
|
||||||
|
__slice_resize(&m.hashes, cap, m.entries.allocator, loc);
|
||||||
|
for i in old_len..<len(m.hashes) {
|
||||||
|
m.hashes[i] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
__dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #caller_location) #no_bounds_check {
|
||||||
|
new_header: Map_Header = header;
|
||||||
|
nm := Raw_Map{};
|
||||||
|
nm.entries.allocator = m.entries.allocator;
|
||||||
|
new_header.m = &nm;
|
||||||
|
|
||||||
|
c := context;
|
||||||
|
if m.entries.allocator.procedure != nil {
|
||||||
|
c.allocator = m.entries.allocator;
|
||||||
|
}
|
||||||
|
context = c;
|
||||||
|
|
||||||
|
new_count := new_count;
|
||||||
|
new_count = max(new_count, 2*m.entries.len);
|
||||||
|
|
||||||
|
__dynamic_array_reserve(&nm.entries, entry_size, entry_align, m.entries.len, loc);
|
||||||
|
__slice_resize(&nm.hashes, new_count, m.entries.allocator, loc);
|
||||||
|
for i in 0 ..< new_count {
|
||||||
|
nm.hashes[i] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0 ..< m.entries.len {
|
||||||
|
if len(nm.hashes) == 0 {
|
||||||
|
__dynamic_map_grow(new_header, loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
entry_header := __dynamic_map_get_entry(header, i);
|
||||||
|
entry_hash := __get_map_hash_from_entry(header, entry_header);
|
||||||
|
|
||||||
|
fr := __dynamic_map_find(new_header, entry_hash);
|
||||||
|
j := __dynamic_map_add_entry(new_header, entry_hash, loc);
|
||||||
|
if fr.entry_prev < 0 {
|
||||||
|
nm.hashes[fr.hash_index] = j;
|
||||||
|
} else {
|
||||||
|
e := __dynamic_map_get_entry(new_header, fr.entry_prev);
|
||||||
|
e.next = j;
|
||||||
|
}
|
||||||
|
|
||||||
|
e := __dynamic_map_get_entry(new_header, j);
|
||||||
|
__dynamic_map_copy_entry(header, e, entry_header);
|
||||||
|
e.next = fr.entry_index;
|
||||||
|
|
||||||
|
if __dynamic_map_full(new_header) {
|
||||||
|
__dynamic_map_grow(new_header, loc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(m.hashes, m.entries.allocator, loc);
|
||||||
|
free(m.entries.data, m.entries.allocator, loc);
|
||||||
|
header.m^ = nm;
|
||||||
|
}
|
||||||
|
|
||||||
|
__dynamic_map_get :: proc(h: Map_Header, hash: Map_Hash) -> rawptr {
|
||||||
|
index := __dynamic_map_find(h, hash).entry_index;
|
||||||
|
if index >= 0 {
|
||||||
|
data := uintptr(__dynamic_map_get_entry(h, index));
|
||||||
|
return rawptr(data + h.value_offset);
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
__dynamic_map_set :: proc(h: Map_Header, hash: Map_Hash, value: rawptr, loc := #caller_location) #no_bounds_check {
|
||||||
|
index: int;
|
||||||
|
assert(value != nil);
|
||||||
|
|
||||||
|
if len(h.m.hashes) == 0 {
|
||||||
|
__dynamic_map_reserve(h, INITIAL_MAP_CAP, loc);
|
||||||
|
__dynamic_map_grow(h, loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
fr := __dynamic_map_find(h, hash);
|
||||||
|
if fr.entry_index >= 0 {
|
||||||
|
index = fr.entry_index;
|
||||||
|
} else {
|
||||||
|
index = __dynamic_map_add_entry(h, hash, loc);
|
||||||
|
if fr.entry_prev >= 0 {
|
||||||
|
entry := __dynamic_map_get_entry(h, fr.entry_prev);
|
||||||
|
entry.next = index;
|
||||||
|
} else {
|
||||||
|
h.m.hashes[fr.hash_index] = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
e := __dynamic_map_get_entry(h, index);
|
||||||
|
e.hash = hash.hash;
|
||||||
|
|
||||||
|
key := rawptr(uintptr(e) + h.key_offset);
|
||||||
|
mem_copy(key, hash.key_ptr, h.key_size);
|
||||||
|
|
||||||
|
val := rawptr(uintptr(e) + h.value_offset);
|
||||||
|
mem_copy(val, value, h.value_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if __dynamic_map_full(h) {
|
||||||
|
__dynamic_map_grow(h, loc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__dynamic_map_grow :: proc(using h: Map_Header, loc := #caller_location) {
|
||||||
|
// TODO(bill): Determine an efficient growing rate
|
||||||
|
new_count := max(4*m.entries.cap + 7, INITIAL_MAP_CAP);
|
||||||
|
__dynamic_map_rehash(h, new_count, loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
__dynamic_map_full :: inline proc "contextless" (using h: Map_Header) -> bool {
|
||||||
|
return int(0.75 * f64(len(m.hashes))) <= m.entries.cap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__dynamic_map_hash_equal :: proc "contextless" (h: Map_Header, a, b: Map_Hash) -> bool {
|
||||||
|
if a.hash == b.hash {
|
||||||
|
return h.equal(a.key_ptr, b.key_ptr);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
__dynamic_map_find :: proc(using h: Map_Header, hash: Map_Hash) -> Map_Find_Result #no_bounds_check {
|
||||||
|
fr := Map_Find_Result{-1, -1, -1};
|
||||||
|
if n := uintptr(len(m.hashes)); n > 0 {
|
||||||
|
fr.hash_index = int(hash.hash % n);
|
||||||
|
fr.entry_index = m.hashes[fr.hash_index];
|
||||||
|
for fr.entry_index >= 0 {
|
||||||
|
entry := __dynamic_map_get_entry(h, fr.entry_index);
|
||||||
|
entry_hash := __get_map_hash_from_entry(h, entry);
|
||||||
|
if __dynamic_map_hash_equal(h, entry_hash, hash) {
|
||||||
|
return fr;
|
||||||
|
}
|
||||||
|
fr.entry_prev = fr.entry_index;
|
||||||
|
fr.entry_index = entry.next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
__dynamic_map_add_entry :: proc(using h: Map_Header, hash: Map_Hash, loc := #caller_location) -> int {
|
||||||
|
prev := m.entries.len;
|
||||||
|
c := __dynamic_array_append_nothing(&m.entries, entry_size, entry_align, loc);
|
||||||
|
if c != prev {
|
||||||
|
end := __dynamic_map_get_entry(h, c-1);
|
||||||
|
end.hash = hash.hash;
|
||||||
|
mem_copy(rawptr(uintptr(end) + key_offset), hash.key_ptr, key_size);
|
||||||
|
end.next = -1;
|
||||||
|
}
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
__dynamic_map_delete_key :: proc(using h: Map_Header, hash: Map_Hash) {
|
||||||
|
fr := __dynamic_map_find(h, hash);
|
||||||
|
if fr.entry_index >= 0 {
|
||||||
|
__dynamic_map_erase(h, fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__dynamic_map_get_entry :: proc(using h: Map_Header, index: int) -> ^Map_Entry_Header {
|
||||||
|
assert(0 <= index && index < m.entries.len);
|
||||||
|
return (^Map_Entry_Header)(uintptr(m.entries.data) + uintptr(index*entry_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
__dynamic_map_copy_entry :: proc "contextless" (h: Map_Header, new, old: ^Map_Entry_Header) {
|
||||||
|
mem_copy(new, old, h.entry_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
__dynamic_map_erase :: proc(using h: Map_Header, fr: Map_Find_Result) #no_bounds_check {
|
||||||
|
if fr.entry_prev < 0 {
|
||||||
|
m.hashes[fr.hash_index] = __dynamic_map_get_entry(h, fr.entry_index).next;
|
||||||
|
} else {
|
||||||
|
prev := __dynamic_map_get_entry(h, fr.entry_prev);
|
||||||
|
curr := __dynamic_map_get_entry(h, fr.entry_index);
|
||||||
|
prev.next = curr.next;
|
||||||
|
}
|
||||||
|
if (fr.entry_index == m.entries.len-1) {
|
||||||
|
// NOTE(bill): No need to do anything else, just pop
|
||||||
|
} else {
|
||||||
|
old := __dynamic_map_get_entry(h, fr.entry_index);
|
||||||
|
end := __dynamic_map_get_entry(h, m.entries.len-1);
|
||||||
|
__dynamic_map_copy_entry(h, old, end);
|
||||||
|
|
||||||
|
old_hash := __get_map_hash_from_entry(h, old);
|
||||||
|
|
||||||
|
if last := __dynamic_map_find(h, old_hash); last.entry_prev >= 0 {
|
||||||
|
last_entry := __dynamic_map_get_entry(h, last.entry_prev);
|
||||||
|
last_entry.next = fr.entry_index;
|
||||||
|
} else {
|
||||||
|
m.hashes[last.hash_index] = fr.entry_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m.entries.len -= 1;
|
||||||
|
}
|
||||||
@@ -23,7 +23,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: int, index
|
|||||||
}
|
}
|
||||||
handle_error :: proc "contextless" (file: string, line, column: int, index, count: int) {
|
handle_error :: proc "contextless" (file: string, line, column: int, index, count: int) {
|
||||||
context = default_context();
|
context = default_context();
|
||||||
print_caller_location(Source_Code_Location{file, line, column, "", 0});
|
print_caller_location(Source_Code_Location{file, line, column, ""});
|
||||||
print_string(" Index ");
|
print_string(" Index ");
|
||||||
print_i64(i64(index));
|
print_i64(i64(index));
|
||||||
print_string(" is out of bounds range 0:");
|
print_string(" is out of bounds range 0:");
|
||||||
@@ -36,7 +36,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: int, index
|
|||||||
|
|
||||||
slice_handle_error :: proc "contextless" (file: string, line, column: int, lo, hi: int, len: int) -> ! {
|
slice_handle_error :: proc "contextless" (file: string, line, column: int, lo, hi: int, len: int) -> ! {
|
||||||
context = default_context();
|
context = default_context();
|
||||||
print_caller_location(Source_Code_Location{file, line, column, "", 0});
|
print_caller_location(Source_Code_Location{file, line, column, ""});
|
||||||
print_string(" Invalid slice indices: ");
|
print_string(" Invalid slice indices: ");
|
||||||
print_i64(i64(lo));
|
print_i64(i64(lo));
|
||||||
print_string(":");
|
print_string(":");
|
||||||
@@ -67,7 +67,7 @@ dynamic_array_expr_error :: proc "contextless" (file: string, line, column: int,
|
|||||||
}
|
}
|
||||||
handle_error :: proc "contextless" (file: string, line, column: int, low, high, max: int) {
|
handle_error :: proc "contextless" (file: string, line, column: int, low, high, max: int) {
|
||||||
context = default_context();
|
context = default_context();
|
||||||
print_caller_location(Source_Code_Location{file, line, column, "", 0});
|
print_caller_location(Source_Code_Location{file, line, column, ""});
|
||||||
print_string(" Invalid dynamic array values: ");
|
print_string(" Invalid dynamic array values: ");
|
||||||
print_i64(i64(low));
|
print_i64(i64(low));
|
||||||
print_string(":");
|
print_string(":");
|
||||||
@@ -87,7 +87,7 @@ type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column
|
|||||||
}
|
}
|
||||||
handle_error :: proc "contextless" (file: string, line, column: int, from, to: typeid) {
|
handle_error :: proc "contextless" (file: string, line, column: int, from, to: typeid) {
|
||||||
context = default_context();
|
context = default_context();
|
||||||
print_caller_location(Source_Code_Location{file, line, column, "", 0});
|
print_caller_location(Source_Code_Location{file, line, column, ""});
|
||||||
print_string(" Invalid type assertion from ");
|
print_string(" Invalid type assertion from ");
|
||||||
print_typeid(from);
|
print_typeid(from);
|
||||||
print_string(" to ");
|
print_string(" to ");
|
||||||
@@ -98,6 +98,59 @@ type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column
|
|||||||
handle_error(file, line, column, from, to);
|
handle_error(file, line, column, from, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: int, from, to: typeid, from_data: rawptr) {
|
||||||
|
if ok {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
variant_type :: proc "contextless" (id: typeid, data: rawptr) -> typeid {
|
||||||
|
if id == nil || data == nil {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
ti := type_info_base(type_info_of(id));
|
||||||
|
#partial switch v in ti.variant {
|
||||||
|
case Type_Info_Any:
|
||||||
|
return (^any)(data).id;
|
||||||
|
case Type_Info_Union:
|
||||||
|
tag_ptr := uintptr(data) + v.tag_offset;
|
||||||
|
idx := 0;
|
||||||
|
switch v.tag_type.size {
|
||||||
|
case 1: idx = int((^u8)(tag_ptr)^) - 1;
|
||||||
|
case 2: idx = int((^u16)(tag_ptr)^) - 1;
|
||||||
|
case 4: idx = int((^u32)(tag_ptr)^) - 1;
|
||||||
|
case 8: idx = int((^u64)(tag_ptr)^) - 1;
|
||||||
|
case 16: idx = int((^u128)(tag_ptr)^) - 1;
|
||||||
|
}
|
||||||
|
if idx < 0 {
|
||||||
|
return nil;
|
||||||
|
} else if idx < len(v.variants) {
|
||||||
|
return v.variants[idx].id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_error :: proc "contextless" (file: string, line, column: int, from, to: typeid, from_data: rawptr) {
|
||||||
|
context = default_context();
|
||||||
|
|
||||||
|
actual := variant_type(from, from_data);
|
||||||
|
|
||||||
|
print_caller_location(Source_Code_Location{file, line, column, ""});
|
||||||
|
print_string(" Invalid type assertion from ");
|
||||||
|
print_typeid(from);
|
||||||
|
print_string(" to ");
|
||||||
|
print_typeid(to);
|
||||||
|
if actual != from {
|
||||||
|
print_string(", actual type: ");
|
||||||
|
print_typeid(actual);
|
||||||
|
}
|
||||||
|
print_byte('\n');
|
||||||
|
type_assertion_trap();
|
||||||
|
}
|
||||||
|
handle_error(file, line, column, from, to, from_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
make_slice_error_loc :: inline proc "contextless" (loc := #caller_location, len: int) {
|
make_slice_error_loc :: inline proc "contextless" (loc := #caller_location, len: int) {
|
||||||
if 0 <= len {
|
if 0 <= len {
|
||||||
return;
|
return;
|
||||||
|
|||||||
+24
-17
@@ -93,18 +93,18 @@ mem_copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
|
|||||||
when ODIN_USE_LLVM_API {
|
when ODIN_USE_LLVM_API {
|
||||||
when size_of(rawptr) == 8 {
|
when size_of(rawptr) == 8 {
|
||||||
@(link_name="llvm.memmove.p0i8.p0i8.i64")
|
@(link_name="llvm.memmove.p0i8.p0i8.i64")
|
||||||
llvm_memmove :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---;
|
llvm_memmove :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---;
|
||||||
} else {
|
} else {
|
||||||
@(link_name="llvm.memmove.p0i8.p0i8.i32")
|
@(link_name="llvm.memmove.p0i8.p0i8.i32")
|
||||||
llvm_memmove :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---;
|
llvm_memmove :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
when size_of(rawptr) == 8 {
|
when size_of(rawptr) == 8 {
|
||||||
@(link_name="llvm.memmove.p0i8.p0i8.i64")
|
@(link_name="llvm.memmove.p0i8.p0i8.i64")
|
||||||
llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
|
llvm_memmove :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
|
||||||
} else {
|
} else {
|
||||||
@(link_name="llvm.memmove.p0i8.p0i8.i32")
|
@(link_name="llvm.memmove.p0i8.p0i8.i32")
|
||||||
llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
|
llvm_memmove :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -121,18 +121,18 @@ mem_copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> r
|
|||||||
when ODIN_USE_LLVM_API {
|
when ODIN_USE_LLVM_API {
|
||||||
when size_of(rawptr) == 8 {
|
when size_of(rawptr) == 8 {
|
||||||
@(link_name="llvm.memcpy.p0i8.p0i8.i64")
|
@(link_name="llvm.memcpy.p0i8.p0i8.i64")
|
||||||
llvm_memcpy :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---;
|
llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---;
|
||||||
} else {
|
} else {
|
||||||
@(link_name="llvm.memcpy.p0i8.p0i8.i32")
|
@(link_name="llvm.memcpy.p0i8.p0i8.i32")
|
||||||
llvm_memcpy :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---;
|
llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
when size_of(rawptr) == 8 {
|
when size_of(rawptr) == 8 {
|
||||||
@(link_name="llvm.memcpy.p0i8.p0i8.i64")
|
@(link_name="llvm.memcpy.p0i8.p0i8.i64")
|
||||||
llvm_memcpy :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
|
llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
|
||||||
} else {
|
} else {
|
||||||
@(link_name="llvm.memcpy.p0i8.p0i8.i32")
|
@(link_name="llvm.memcpy.p0i8.p0i8.i32")
|
||||||
llvm_memcpy :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
|
llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -180,9 +180,16 @@ mem_resize :: inline proc(ptr: rawptr, old_size, new_size: int, alignment: int =
|
|||||||
}
|
}
|
||||||
return allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, 0, loc);
|
return allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, 0, loc);
|
||||||
}
|
}
|
||||||
|
memory_equal :: proc "contextless" (a, b: rawptr, n: int) -> bool {
|
||||||
|
return memory_compare(a, b, n) == 0;
|
||||||
|
}
|
||||||
memory_compare :: proc "contextless" (a, b: rawptr, n: int) -> int #no_bounds_check {
|
memory_compare :: proc "contextless" (a, b: rawptr, n: int) -> int #no_bounds_check {
|
||||||
|
switch {
|
||||||
|
case a == b: return 0;
|
||||||
|
case a == nil: return -1;
|
||||||
|
case b == nil: return +1;
|
||||||
|
}
|
||||||
|
|
||||||
x := uintptr(a);
|
x := uintptr(a);
|
||||||
y := uintptr(b);
|
y := uintptr(b);
|
||||||
n := uintptr(n);
|
n := uintptr(n);
|
||||||
@@ -389,45 +396,45 @@ string_decode_rune :: inline proc "contextless" (s: string) -> (rune, int) {
|
|||||||
return rune(s0&MASK4)<<18 | rune(b1&MASKX)<<12 | rune(b2&MASKX)<<6 | rune(b3&MASKX), 4;
|
return rune(s0&MASK4)<<18 | rune(b1&MASKX)<<12 | rune(b2&MASKX)<<6 | rune(b3&MASKX), 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
@(default_calling_convention = "c")
|
@(default_calling_convention = "none")
|
||||||
foreign {
|
foreign {
|
||||||
@(link_name="llvm.sqrt.f32") _sqrt_f32 :: proc(x: f32) -> f32 ---
|
@(link_name="llvm.sqrt.f32") _sqrt_f32 :: proc(x: f32) -> f32 ---
|
||||||
@(link_name="llvm.sqrt.f64") _sqrt_f64 :: proc(x: f64) -> f64 ---
|
@(link_name="llvm.sqrt.f64") _sqrt_f64 :: proc(x: f64) -> f64 ---
|
||||||
}
|
}
|
||||||
abs_f32 :: inline proc "contextless" (x: f32) -> f32 {
|
abs_f32 :: inline proc "contextless" (x: f32) -> f32 {
|
||||||
foreign {
|
foreign {
|
||||||
@(link_name="llvm.fabs.f32") _abs :: proc "c" (x: f32) -> f32 ---
|
@(link_name="llvm.fabs.f32") _abs :: proc "none" (x: f32) -> f32 ---
|
||||||
}
|
}
|
||||||
return _abs(x);
|
return _abs(x);
|
||||||
}
|
}
|
||||||
abs_f64 :: inline proc "contextless" (x: f64) -> f64 {
|
abs_f64 :: inline proc "contextless" (x: f64) -> f64 {
|
||||||
foreign {
|
foreign {
|
||||||
@(link_name="llvm.fabs.f64") _abs :: proc "c" (x: f64) -> f64 ---
|
@(link_name="llvm.fabs.f64") _abs :: proc "none" (x: f64) -> f64 ---
|
||||||
}
|
}
|
||||||
return _abs(x);
|
return _abs(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
min_f32 :: proc(a, b: f32) -> f32 {
|
min_f32 :: proc(a, b: f32) -> f32 {
|
||||||
foreign {
|
foreign {
|
||||||
@(link_name="llvm.minnum.f32") _min :: proc "c" (a, b: f32) -> f32 ---
|
@(link_name="llvm.minnum.f32") _min :: proc "none" (a, b: f32) -> f32 ---
|
||||||
}
|
}
|
||||||
return _min(a, b);
|
return _min(a, b);
|
||||||
}
|
}
|
||||||
min_f64 :: proc(a, b: f64) -> f64 {
|
min_f64 :: proc(a, b: f64) -> f64 {
|
||||||
foreign {
|
foreign {
|
||||||
@(link_name="llvm.minnum.f64") _min :: proc "c" (a, b: f64) -> f64 ---
|
@(link_name="llvm.minnum.f64") _min :: proc "none" (a, b: f64) -> f64 ---
|
||||||
}
|
}
|
||||||
return _min(a, b);
|
return _min(a, b);
|
||||||
}
|
}
|
||||||
max_f32 :: proc(a, b: f32) -> f32 {
|
max_f32 :: proc(a, b: f32) -> f32 {
|
||||||
foreign {
|
foreign {
|
||||||
@(link_name="llvm.maxnum.f32") _max :: proc "c" (a, b: f32) -> f32 ---
|
@(link_name="llvm.maxnum.f32") _max :: proc "none" (a, b: f32) -> f32 ---
|
||||||
}
|
}
|
||||||
return _max(a, b);
|
return _max(a, b);
|
||||||
}
|
}
|
||||||
max_f64 :: proc(a, b: f64) -> f64 {
|
max_f64 :: proc(a, b: f64) -> f64 {
|
||||||
foreign {
|
foreign {
|
||||||
@(link_name="llvm.maxnum.f64") _max :: proc "c" (a, b: f64) -> f64 ---
|
@(link_name="llvm.maxnum.f64") _max :: proc "none" (a, b: f64) -> f64 ---
|
||||||
}
|
}
|
||||||
return _max(a, b);
|
return _max(a, b);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
@(link_name="__umodti3")
|
||||||
|
umodti3 :: proc "c" (a, b: u128) -> u128 {
|
||||||
|
r: u128 = ---;
|
||||||
|
_ = udivmod128(a, b, &r);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@(link_name="__udivmodti4")
|
||||||
|
udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
|
||||||
|
return udivmod128(a, b, rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
@(link_name="__udivti3")
|
||||||
|
udivti3 :: proc "c" (a, b: u128) -> u128 {
|
||||||
|
return udivmodti4(a, b, nil);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@(link_name="__modti3")
|
||||||
|
modti3 :: proc "c" (a, b: i128) -> i128 {
|
||||||
|
s_a := a >> (128 - 1);
|
||||||
|
s_b := b >> (128 - 1);
|
||||||
|
an := (a ~ s_a) - s_a;
|
||||||
|
bn := (b ~ s_b) - s_b;
|
||||||
|
|
||||||
|
r: u128 = ---;
|
||||||
|
_ = udivmod128(transmute(u128)an, transmute(u128)bn, &r);
|
||||||
|
return (transmute(i128)r ~ s_a) - s_a;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@(link_name="__divmodti4")
|
||||||
|
divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 {
|
||||||
|
u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem);
|
||||||
|
return transmute(i128)u;
|
||||||
|
}
|
||||||
|
|
||||||
|
@(link_name="__divti3")
|
||||||
|
divti3 :: proc "c" (a, b: i128) -> i128 {
|
||||||
|
u := udivmodti4(transmute(u128)a, transmute(u128)b, nil);
|
||||||
|
return transmute(i128)u;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@(link_name="__fixdfti")
|
||||||
|
fixdfti :: proc(a: u64) -> i128 {
|
||||||
|
significandBits :: 52;
|
||||||
|
typeWidth :: (size_of(u64)*8);
|
||||||
|
exponentBits :: (typeWidth - significandBits - 1);
|
||||||
|
maxExponent :: ((1 << exponentBits) - 1);
|
||||||
|
exponentBias :: (maxExponent >> 1);
|
||||||
|
|
||||||
|
implicitBit :: (u64(1) << significandBits);
|
||||||
|
significandMask :: (implicitBit - 1);
|
||||||
|
signBit :: (u64(1) << (significandBits + exponentBits));
|
||||||
|
absMask :: (signBit - 1);
|
||||||
|
exponentMask :: (absMask ~ significandMask);
|
||||||
|
|
||||||
|
// Break a into sign, exponent, significand
|
||||||
|
aRep := a;
|
||||||
|
aAbs := aRep & absMask;
|
||||||
|
sign := i128(-1 if aRep & signBit != 0 else 1);
|
||||||
|
exponent := u64((aAbs >> significandBits) - exponentBias);
|
||||||
|
significand := u64((aAbs & significandMask) | implicitBit);
|
||||||
|
|
||||||
|
// If exponent is negative, the result is zero.
|
||||||
|
if exponent < 0 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the value is too large for the integer type, saturate.
|
||||||
|
if exponent >= size_of(i128) * 8 {
|
||||||
|
return max(i128) if sign == 1 else min(i128);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If 0 <= exponent < significandBits, right shift to get the result.
|
||||||
|
// Otherwise, shift left.
|
||||||
|
if exponent < significandBits {
|
||||||
|
return sign * i128(significand >> (significandBits - exponent));
|
||||||
|
} else {
|
||||||
|
return sign * (i128(significand) << (exponent - significandBits));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@(default_calling_convention = "none")
|
||||||
|
foreign {
|
||||||
|
@(link_name="llvm.ctlz.i128") _clz_i128 :: proc(x: i128, is_zero_undef := false) -> i128 ---
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@(link_name="__floattidf")
|
||||||
|
floattidf :: proc(a: i128) -> f64 {
|
||||||
|
DBL_MANT_DIG :: 53;
|
||||||
|
if a == 0 {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
a := a;
|
||||||
|
N :: size_of(i128) * 8;
|
||||||
|
s := a >> (N-1);
|
||||||
|
a = (a ~ s) - s;
|
||||||
|
sd: = N - _clz_i128(a); // number of significant digits
|
||||||
|
e := u32(sd - 1); // exponent
|
||||||
|
if sd > DBL_MANT_DIG {
|
||||||
|
switch sd {
|
||||||
|
case DBL_MANT_DIG + 1:
|
||||||
|
a <<= 1;
|
||||||
|
case DBL_MANT_DIG + 2:
|
||||||
|
// okay
|
||||||
|
case:
|
||||||
|
a = i128(u128(a) >> u128(sd - (DBL_MANT_DIG+2))) |
|
||||||
|
i128(u128(a) & (~u128(0) >> u128(N + DBL_MANT_DIG+2 - sd)) != 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
a |= i128((a & 4) != 0);
|
||||||
|
a += 1;
|
||||||
|
a >>= 2;
|
||||||
|
|
||||||
|
if a & (1 << DBL_MANT_DIG) != 0 {
|
||||||
|
a >>= 1;
|
||||||
|
e += 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
a <<= u128(DBL_MANT_DIG - sd);
|
||||||
|
}
|
||||||
|
fb: [2]u32;
|
||||||
|
fb[1] = (u32(s) & 0x80000000) | // sign
|
||||||
|
((e + 1023) << 20) | // exponent
|
||||||
|
((u32(a) >> 32) & 0x000FFFFF); // mantissa-high
|
||||||
|
fb[1] = u32(a); // mantissa-low
|
||||||
|
return transmute(f64)fb;
|
||||||
|
}
|
||||||
@@ -2,134 +2,134 @@ package runtime
|
|||||||
|
|
||||||
@(link_name="__umodti3")
|
@(link_name="__umodti3")
|
||||||
umodti3 :: proc "c" (a, b: u128) -> u128 {
|
umodti3 :: proc "c" (a, b: u128) -> u128 {
|
||||||
r: u128 = ---;
|
r: u128 = ---;
|
||||||
_ = udivmod128(a, b, &r);
|
_ = udivmod128(a, b, &r);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@(link_name="__udivmodti4")
|
@(link_name="__udivmodti4")
|
||||||
udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
|
udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
|
||||||
return udivmod128(a, b, rem);
|
return udivmod128(a, b, rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@(link_name="__udivti3")
|
@(link_name="__udivti3")
|
||||||
udivti3 :: proc "c" (a, b: u128) -> u128 {
|
udivti3 :: proc "c" (a, b: u128) -> u128 {
|
||||||
return udivmodti4(a, b, nil);
|
return udivmodti4(a, b, nil);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@(link_name="__modti3")
|
@(link_name="__modti3")
|
||||||
modti3 :: proc "c" (a, b: i128) -> i128 {
|
modti3 :: proc "c" (a, b: i128) -> i128 {
|
||||||
s_a := a >> (128 - 1);
|
s_a := a >> (128 - 1);
|
||||||
s_b := b >> (128 - 1);
|
s_b := b >> (128 - 1);
|
||||||
an := (a ~ s_a) - s_a;
|
an := (a ~ s_a) - s_a;
|
||||||
bn := (b ~ s_b) - s_b;
|
bn := (b ~ s_b) - s_b;
|
||||||
|
|
||||||
r: u128 = ---;
|
r: u128 = ---;
|
||||||
_ = udivmod128(transmute(u128)an, transmute(u128)bn, &r);
|
_ = udivmod128(transmute(u128)an, transmute(u128)bn, &r);
|
||||||
return (transmute(i128)r ~ s_a) - s_a;
|
return (transmute(i128)r ~ s_a) - s_a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@(link_name="__divmodti4")
|
@(link_name="__divmodti4")
|
||||||
divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 {
|
divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 {
|
||||||
u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem);
|
u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem);
|
||||||
return transmute(i128)u;
|
return transmute(i128)u;
|
||||||
}
|
}
|
||||||
|
|
||||||
@(link_name="__divti3")
|
@(link_name="__divti3")
|
||||||
divti3 :: proc "c" (a, b: i128) -> i128 {
|
divti3 :: proc "c" (a, b: i128) -> i128 {
|
||||||
u := udivmodti4(transmute(u128)a, transmute(u128)b, nil);
|
u := udivmodti4(transmute(u128)a, transmute(u128)b, nil);
|
||||||
return transmute(i128)u;
|
return transmute(i128)u;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@(link_name="__fixdfti")
|
@(link_name="__fixdfti")
|
||||||
fixdfti :: proc(a: u64) -> i128 {
|
fixdfti :: proc(a: u64) -> i128 {
|
||||||
significandBits :: 52;
|
significandBits :: 52;
|
||||||
typeWidth :: (size_of(u64)*8);
|
typeWidth :: (size_of(u64)*8);
|
||||||
exponentBits :: (typeWidth - significandBits - 1);
|
exponentBits :: (typeWidth - significandBits - 1);
|
||||||
maxExponent :: ((1 << exponentBits) - 1);
|
maxExponent :: ((1 << exponentBits) - 1);
|
||||||
exponentBias :: (maxExponent >> 1);
|
exponentBias :: (maxExponent >> 1);
|
||||||
|
|
||||||
implicitBit :: (u64(1) << significandBits);
|
implicitBit :: (u64(1) << significandBits);
|
||||||
significandMask :: (implicitBit - 1);
|
significandMask :: (implicitBit - 1);
|
||||||
signBit :: (u64(1) << (significandBits + exponentBits));
|
signBit :: (u64(1) << (significandBits + exponentBits));
|
||||||
absMask :: (signBit - 1);
|
absMask :: (signBit - 1);
|
||||||
exponentMask :: (absMask ~ significandMask);
|
exponentMask :: (absMask ~ significandMask);
|
||||||
|
|
||||||
// Break a into sign, exponent, significand
|
// Break a into sign, exponent, significand
|
||||||
aRep := a;
|
aRep := a;
|
||||||
aAbs := aRep & absMask;
|
aAbs := aRep & absMask;
|
||||||
sign := i128(-1 if aRep & signBit != 0 else 1);
|
sign := i128(-1 if aRep & signBit != 0 else 1);
|
||||||
exponent := u64((aAbs >> significandBits) - exponentBias);
|
exponent := u64((aAbs >> significandBits) - exponentBias);
|
||||||
significand := u64((aAbs & significandMask) | implicitBit);
|
significand := u64((aAbs & significandMask) | implicitBit);
|
||||||
|
|
||||||
// If exponent is negative, the result is zero.
|
// If exponent is negative, the result is zero.
|
||||||
if exponent < 0 {
|
if exponent < 0 {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the value is too large for the integer type, saturate.
|
// If the value is too large for the integer type, saturate.
|
||||||
if exponent >= size_of(i128) * 8 {
|
if exponent >= size_of(i128) * 8 {
|
||||||
return max(i128) if sign == 1 else min(i128);
|
return max(i128) if sign == 1 else min(i128);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If 0 <= exponent < significandBits, right shift to get the result.
|
// If 0 <= exponent < significandBits, right shift to get the result.
|
||||||
// Otherwise, shift left.
|
// Otherwise, shift left.
|
||||||
if exponent < significandBits {
|
if exponent < significandBits {
|
||||||
return sign * i128(significand >> (significandBits - exponent));
|
return sign * i128(significand >> (significandBits - exponent));
|
||||||
} else {
|
} else {
|
||||||
return sign * (i128(significand) << (exponent - significandBits));
|
return sign * (i128(significand) << (exponent - significandBits));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@(default_calling_convention = "none")
|
@(default_calling_convention = "none")
|
||||||
foreign {
|
foreign {
|
||||||
@(link_name="llvm.ctlz.i128") _clz_i128 :: proc(x: i128, is_zero_undef := false) -> i128 ---
|
@(link_name="llvm.ctlz.i128") _clz_i128 :: proc(x: i128, is_zero_undef := false) -> i128 ---
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@(link_name="__floattidf")
|
@(link_name="__floattidf")
|
||||||
floattidf :: proc(a: i128) -> f64 {
|
floattidf :: proc(a: i128) -> f64 {
|
||||||
DBL_MANT_DIG :: 53;
|
DBL_MANT_DIG :: 53;
|
||||||
if a == 0 {
|
if a == 0 {
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
a := a;
|
a := a;
|
||||||
N :: size_of(i128) * 8;
|
N :: size_of(i128) * 8;
|
||||||
s := a >> (N-1);
|
s := a >> (N-1);
|
||||||
a = (a ~ s) - s;
|
a = (a ~ s) - s;
|
||||||
sd: = N - _clz_i128(a); // number of significant digits
|
sd: = N - _clz_i128(a); // number of significant digits
|
||||||
e := u32(sd - 1); // exponent
|
e := u32(sd - 1); // exponent
|
||||||
if sd > DBL_MANT_DIG {
|
if sd > DBL_MANT_DIG {
|
||||||
switch sd {
|
switch sd {
|
||||||
case DBL_MANT_DIG + 1:
|
case DBL_MANT_DIG + 1:
|
||||||
a <<= 1;
|
a <<= 1;
|
||||||
case DBL_MANT_DIG + 2:
|
case DBL_MANT_DIG + 2:
|
||||||
// okay
|
// okay
|
||||||
case:
|
case:
|
||||||
a = i128(u128(a) >> u128(sd - (DBL_MANT_DIG+2))) |
|
a = i128(u128(a) >> u128(sd - (DBL_MANT_DIG+2))) |
|
||||||
i128(u128(a) & (~u128(0) >> u128(N + DBL_MANT_DIG+2 - sd)) != 0);
|
i128(u128(a) & (~u128(0) >> u128(N + DBL_MANT_DIG+2 - sd)) != 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
a |= i128((a & 4) != 0);
|
a |= i128((a & 4) != 0);
|
||||||
a += 1;
|
a += 1;
|
||||||
a >>= 2;
|
a >>= 2;
|
||||||
|
|
||||||
if a & (1 << DBL_MANT_DIG) != 0 {
|
if a & (1 << DBL_MANT_DIG) != 0 {
|
||||||
a >>= 1;
|
a >>= 1;
|
||||||
e += 1;
|
e += 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
a <<= u128(DBL_MANT_DIG - sd);
|
a <<= u128(DBL_MANT_DIG - sd);
|
||||||
}
|
}
|
||||||
fb: [2]u32;
|
fb: [2]u32;
|
||||||
fb[1] = (u32(s) & 0x80000000) | // sign
|
fb[1] = (u32(s) & 0x80000000) | // sign
|
||||||
((e + 1023) << 20) | // exponent
|
((e + 1023) << 20) | // exponent
|
||||||
((u32(a) >> 32) & 0x000FFFFF); // mantissa-high
|
((u32(a) >> 32) & 0x000FFFFF); // mantissa-high
|
||||||
fb[1] = u32(a); // mantissa-low
|
fb[1] = u32(a); // mantissa-low
|
||||||
return transmute(f64)fb;
|
return transmute(f64)fb;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -350,7 +350,7 @@ print_type :: proc "contextless" (ti: ^Type_Info) {
|
|||||||
print_byte(']');
|
print_byte(']');
|
||||||
|
|
||||||
case Type_Info_Opaque:
|
case Type_Info_Opaque:
|
||||||
print_string("opaque ");
|
print_string("#opaque ");
|
||||||
print_type(info.elem);
|
print_type(info.elem);
|
||||||
|
|
||||||
case Type_Info_Simd_Vector:
|
case Type_Info_Simd_Vector:
|
||||||
|
|||||||
@@ -2,15 +2,14 @@ package runtime
|
|||||||
|
|
||||||
foreign import kernel32 "system:Kernel32.lib"
|
foreign import kernel32 "system:Kernel32.lib"
|
||||||
|
|
||||||
windows_trap_array_bounds :: proc "contextless" () -> ! {
|
@(private)
|
||||||
DWORD :: u32;
|
foreign kernel32 {
|
||||||
ULONG_PTR :: uint;
|
RaiseException :: proc "stdcall" (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: u32, lpArguments: ^uint) -> ! ---
|
||||||
|
}
|
||||||
|
|
||||||
|
windows_trap_array_bounds :: proc "contextless" () -> ! {
|
||||||
EXCEPTION_ARRAY_BOUNDS_EXCEEDED :: 0xC000008C;
|
EXCEPTION_ARRAY_BOUNDS_EXCEEDED :: 0xC000008C;
|
||||||
|
|
||||||
foreign kernel32 {
|
|
||||||
RaiseException :: proc "stdcall" (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: DWORD, lpArguments: ^ULONG_PTR) -> ! ---
|
|
||||||
}
|
|
||||||
|
|
||||||
RaiseException(EXCEPTION_ARRAY_BOUNDS_EXCEEDED, 0, 0, nil);
|
RaiseException(EXCEPTION_ARRAY_BOUNDS_EXCEEDED, 0, 0, nil);
|
||||||
}
|
}
|
||||||
|
|||||||
+42
-1
@@ -216,7 +216,7 @@ split_last :: proc(array: $T/[]$E) -> (rest: T, last: E) {
|
|||||||
first :: proc(array: $T/[]$E) -> E {
|
first :: proc(array: $T/[]$E) -> E {
|
||||||
return array[0];
|
return array[0];
|
||||||
}
|
}
|
||||||
last :: proc(array: $T/[]$E) -> ^E {
|
last :: proc(array: $T/[]$E) -> E {
|
||||||
return array[len(array)-1];
|
return array[len(array)-1];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,3 +252,44 @@ get_ptr :: proc(array: $T/[]$E, index: int) -> (value: ^E, ok: bool) {
|
|||||||
as_ptr :: proc(array: $T/[]$E) -> ^E {
|
as_ptr :: proc(array: $T/[]$E) -> ^E {
|
||||||
return raw_data(array);
|
return raw_data(array);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
mapper :: proc(s: $S/[]$U, f: proc(U) -> $V, allocator := context.allocator) -> []V {
|
||||||
|
r := make([]V, len(s), allocator);
|
||||||
|
for v, i in s {
|
||||||
|
r[i] = f(v);
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
reduce :: proc(s: $S/[]$U, initializer: $V, f: proc(V, U) -> V) -> V {
|
||||||
|
r := initializer;
|
||||||
|
for v in s {
|
||||||
|
r = f(r, v);
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
filter :: proc(s: $S/[]$U, f: proc(U) -> bool, allocator := context.allocator) -> S {
|
||||||
|
r := make([dynamic]S, 0, 0, allocator);
|
||||||
|
for v in s {
|
||||||
|
if f(v) {
|
||||||
|
append(&r, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r[:];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
dot_product :: proc(a, b: $S/[]$T) -> T
|
||||||
|
where intrinsics.type_is_numeric(T) {
|
||||||
|
if len(a) != len(b) {
|
||||||
|
panic("slice.dot_product: slices of unequal length");
|
||||||
|
}
|
||||||
|
r: T;
|
||||||
|
#no_bounds_check for _, i in a {
|
||||||
|
r += a[i] * b[i];
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|||||||
@@ -54,17 +54,17 @@ reverse_sort :: proc(data: $T/[]$E) where ORD(E) {
|
|||||||
|
|
||||||
// TODO(bill): Should `sort_by_key` exist or is `sort_by` more than enough?
|
// TODO(bill): Should `sort_by_key` exist or is `sort_by` more than enough?
|
||||||
sort_by_key :: proc(data: $T/[]$E, key: proc(E) -> $K) where ORD(K) {
|
sort_by_key :: proc(data: $T/[]$E, key: proc(E) -> $K) where ORD(K) {
|
||||||
context.user_ptr = rawptr(key);
|
context._internal = rawptr(key);
|
||||||
sort_by(data, proc(i, j: E) -> bool {
|
sort_by(data, proc(i, j: E) -> bool {
|
||||||
k := (proc(E) -> K)(context.user_ptr);
|
k := (proc(E) -> K)(context._internal);
|
||||||
return k(i) < k(j);
|
return k(i) < k(j);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
reverse_sort_by_key :: proc(data: $T/[]$E, key: proc(E) -> $K) where ORD(K) {
|
reverse_sort_by_key :: proc(data: $T/[]$E, key: proc(E) -> $K) where ORD(K) {
|
||||||
context.user_ptr = rawptr(key);
|
context._internal = rawptr(key);
|
||||||
sort_by(data, proc(i, j: E) -> bool {
|
sort_by(data, proc(i, j: E) -> bool {
|
||||||
k := (proc(E) -> K)(context.user_ptr);
|
k := (proc(E) -> K)(context._internal);
|
||||||
return k(j) < k(i);
|
return k(j) < k(i);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+177
-82
@@ -3,15 +3,12 @@ package strings
|
|||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:unicode/utf8"
|
import "core:unicode/utf8"
|
||||||
import "core:strconv"
|
import "core:strconv"
|
||||||
|
import "core:io"
|
||||||
|
|
||||||
Builder_Flush_Proc :: #type proc(b: ^Builder) -> (do_reset: bool);
|
Builder_Flush_Proc :: #type proc(b: ^Builder) -> (do_reset: bool);
|
||||||
|
|
||||||
Builder :: struct {
|
Builder :: struct {
|
||||||
buf: [dynamic]byte,
|
buf: [dynamic]byte,
|
||||||
|
|
||||||
// The custom flush procedure allows for the ability to flush the buffer, i.e. write to file
|
|
||||||
flush_proc: Builder_Flush_Proc,
|
|
||||||
flush_data: rawptr,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
make_builder_none :: proc(allocator := context.allocator) -> Builder {
|
make_builder_none :: proc(allocator := context.allocator) -> Builder {
|
||||||
@@ -32,6 +29,61 @@ make_builder :: proc{
|
|||||||
make_builder_len_cap,
|
make_builder_len_cap,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
init_builder_none :: proc(b: ^Builder, allocator := context.allocator) {
|
||||||
|
b.buf = make([dynamic]byte, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
init_builder_len :: proc(b: ^Builder, len: int, allocator := context.allocator) {
|
||||||
|
b.buf = make([dynamic]byte, len, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
init_builder_len_cap :: proc(b: ^Builder, len, cap: int, allocator := context.allocator) {
|
||||||
|
b.buf = make([dynamic]byte, len, cap, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
init_builder :: proc{
|
||||||
|
init_builder_none,
|
||||||
|
init_builder_len,
|
||||||
|
init_builder_len_cap,
|
||||||
|
};
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_builder_stream_vtable := &io.Stream_VTable{
|
||||||
|
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||||
|
b := (^Builder)(s.stream_data);
|
||||||
|
n = write_bytes(b, p);
|
||||||
|
if len(b.buf) == cap(b.buf) {
|
||||||
|
err = .EOF;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
|
||||||
|
b := (^Builder)(s.stream_data);
|
||||||
|
_ = write_byte(b, c);
|
||||||
|
if len(b.buf) == cap(b.buf) {
|
||||||
|
return .EOF;
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
},
|
||||||
|
impl_size = proc(s: io.Stream) -> i64 {
|
||||||
|
b := (^Builder)(s.stream_data);
|
||||||
|
return i64(len(b.buf));
|
||||||
|
},
|
||||||
|
impl_destroy = proc(s: io.Stream) -> io.Error {
|
||||||
|
b := (^Builder)(s.stream_data);
|
||||||
|
delete(b.buf);
|
||||||
|
return .None;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
to_stream :: proc(b: ^Builder) -> io.Stream {
|
||||||
|
return io.Stream{stream_vtable=_builder_stream_vtable, stream_data=b};
|
||||||
|
}
|
||||||
|
to_writer :: proc(b: ^Builder) -> io.Writer {
|
||||||
|
w, _ := io.to_writer(to_stream(b));
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -48,24 +100,6 @@ reset_builder :: proc(b: ^Builder) {
|
|||||||
clear(&b.buf);
|
clear(&b.buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
flush_builder :: proc(b: ^Builder) -> (was_reset: bool) {
|
|
||||||
if b.flush_proc != nil {
|
|
||||||
was_reset = b.flush_proc(b);
|
|
||||||
if was_reset {
|
|
||||||
reset_builder(b);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
flush_builder_check_space :: proc(b: ^Builder, required: int) -> (was_reset: bool) {
|
|
||||||
if n := max(cap(b.buf) - len(b.buf), 0); n < required {
|
|
||||||
was_reset = flush_builder(b);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
builder_from_slice :: proc(backing: []byte) -> Builder {
|
builder_from_slice :: proc(backing: []byte) -> Builder {
|
||||||
s := transmute(mem.Raw_Slice)backing;
|
s := transmute(mem.Raw_Slice)backing;
|
||||||
@@ -94,7 +128,6 @@ builder_space :: proc(b: Builder) -> int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
write_byte :: proc(b: ^Builder, x: byte) -> (n: int) {
|
write_byte :: proc(b: ^Builder, x: byte) -> (n: int) {
|
||||||
flush_builder_check_space(b, 1);
|
|
||||||
if builder_space(b^) > 0 {
|
if builder_space(b^) > 0 {
|
||||||
append(&b.buf, x);
|
append(&b.buf, x);
|
||||||
n += 1;
|
n += 1;
|
||||||
@@ -105,7 +138,6 @@ write_byte :: proc(b: ^Builder, x: byte) -> (n: int) {
|
|||||||
write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) {
|
write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) {
|
||||||
x := x;
|
x := x;
|
||||||
for len(x) != 0 {
|
for len(x) != 0 {
|
||||||
flush_builder_check_space(b, len(x));
|
|
||||||
space := builder_space(b^);
|
space := builder_space(b^);
|
||||||
if space == 0 {
|
if space == 0 {
|
||||||
break; // No need to append
|
break; // No need to append
|
||||||
@@ -121,20 +153,56 @@ write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
write_rune :: proc(b: ^Builder, r: rune) -> int {
|
write_rune_builder :: proc(b: ^Builder, r: rune) -> (int, io.Error) {
|
||||||
if r < utf8.RUNE_SELF {
|
return io.write_rune(to_writer(b), r);
|
||||||
return write_byte(b, byte(r));
|
}
|
||||||
|
|
||||||
|
|
||||||
|
write_quoted_rune_builder :: proc(b: ^Builder, r: rune) -> (n: int) {
|
||||||
|
return write_quoted_rune(to_writer(b), r);
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_write_byte :: proc(w: io.Writer, c: byte) -> int {
|
||||||
|
err := io.write_byte(w, c);
|
||||||
|
return 1 if err == nil else 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
write_quoted_rune :: proc(w: io.Writer, r: rune) -> (n: int) {
|
||||||
|
quote := byte('\'');
|
||||||
|
n += _write_byte(w, quote);
|
||||||
|
buf, width := utf8.encode_rune(r);
|
||||||
|
if width == 1 && r == utf8.RUNE_ERROR {
|
||||||
|
n += _write_byte(w, '\\');
|
||||||
|
n += _write_byte(w, 'x');
|
||||||
|
n += _write_byte(w, DIGITS_LOWER[buf[0]>>4]);
|
||||||
|
n += _write_byte(w, DIGITS_LOWER[buf[0]&0xf]);
|
||||||
|
} else {
|
||||||
|
n += write_escaped_rune(w, r, quote);
|
||||||
}
|
}
|
||||||
|
n += _write_byte(w, quote);
|
||||||
s, n := utf8.encode_rune(r);
|
return;
|
||||||
write_bytes(b, s[:n]);
|
|
||||||
return n;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
write_string :: proc(b: ^Builder, s: string) -> (n: int) {
|
|
||||||
return write_bytes(b, transmute([]byte)s);
|
write_string :: proc{
|
||||||
|
write_string_builder,
|
||||||
|
write_string_writer,
|
||||||
|
};
|
||||||
|
|
||||||
|
write_string_builder :: proc(b: ^Builder, s: string) -> (n: int) {
|
||||||
|
return write_string_writer(to_writer(b), s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
write_string_writer :: proc(w: io.Writer, s: string) -> (n: int) {
|
||||||
|
n, _ = io.write(w, transmute([]byte)s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pop_byte :: proc(b: ^Builder) -> (r: byte) {
|
pop_byte :: proc(b: ^Builder) -> (r: byte) {
|
||||||
if len(b.buf) == 0 {
|
if len(b.buf) == 0 {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -156,8 +224,17 @@ pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) {
|
|||||||
@(private, static)
|
@(private, static)
|
||||||
DIGITS_LOWER := "0123456789abcdefx";
|
DIGITS_LOWER := "0123456789abcdefx";
|
||||||
|
|
||||||
write_quoted_string :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: int) {
|
write_quoted_string :: proc{
|
||||||
n += write_byte(b, quote);
|
write_quoted_string_builder,
|
||||||
|
write_quoted_string_writer,
|
||||||
|
};
|
||||||
|
|
||||||
|
write_quoted_string_builder :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: int) {
|
||||||
|
return write_quoted_string_writer(to_writer(b), str, quote);
|
||||||
|
}
|
||||||
|
|
||||||
|
write_quoted_string_writer :: proc(w: io.Writer, str: string, quote: byte = '"') -> (n: int) {
|
||||||
|
n += _write_byte(w, quote);
|
||||||
for width, s := 0, str; len(s) > 0; s = s[width:] {
|
for width, s := 0, str; len(s) > 0; s = s[width:] {
|
||||||
r := rune(s[0]);
|
r := rune(s[0]);
|
||||||
width = 1;
|
width = 1;
|
||||||
@@ -165,57 +242,75 @@ write_quoted_string :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n:
|
|||||||
r, width = utf8.decode_rune_in_string(s);
|
r, width = utf8.decode_rune_in_string(s);
|
||||||
}
|
}
|
||||||
if width == 1 && r == utf8.RUNE_ERROR {
|
if width == 1 && r == utf8.RUNE_ERROR {
|
||||||
n += write_byte(b, '\\');
|
n += _write_byte(w, '\\');
|
||||||
n += write_byte(b, 'x');
|
n += _write_byte(w, 'x');
|
||||||
n += write_byte(b, DIGITS_LOWER[s[0]>>4]);
|
n += _write_byte(w, DIGITS_LOWER[s[0]>>4]);
|
||||||
n += write_byte(b, DIGITS_LOWER[s[0]&0xf]);
|
n += _write_byte(w, DIGITS_LOWER[s[0]&0xf]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
n += write_escaped_rune(b, r, quote);
|
n += write_escaped_rune(w, r, quote);
|
||||||
|
|
||||||
}
|
}
|
||||||
n += write_byte(b, quote);
|
n += _write_byte(w, quote);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
write_encoded_rune :: proc{
|
||||||
|
write_encoded_rune_builder,
|
||||||
|
write_encoded_rune_writer,
|
||||||
|
};
|
||||||
|
|
||||||
write_encoded_rune :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int) {
|
write_encoded_rune_builder :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int) {
|
||||||
|
return write_encoded_rune_writer(to_writer(b), r, write_quote);
|
||||||
|
|
||||||
|
}
|
||||||
|
write_encoded_rune_writer :: proc(w: io.Writer, r: rune, write_quote := true) -> (n: int) {
|
||||||
if write_quote {
|
if write_quote {
|
||||||
n += write_byte(b, '\'');
|
n += _write_byte(w, '\'');
|
||||||
}
|
}
|
||||||
switch r {
|
switch r {
|
||||||
case '\a': n += write_string(b, `\a"`);
|
case '\a': n += write_string(w, `\a"`);
|
||||||
case '\b': n += write_string(b, `\b"`);
|
case '\b': n += write_string(w, `\b"`);
|
||||||
case '\e': n += write_string(b, `\e"`);
|
case '\e': n += write_string(w, `\e"`);
|
||||||
case '\f': n += write_string(b, `\f"`);
|
case '\f': n += write_string(w, `\f"`);
|
||||||
case '\n': n += write_string(b, `\n"`);
|
case '\n': n += write_string(w, `\n"`);
|
||||||
case '\r': n += write_string(b, `\r"`);
|
case '\r': n += write_string(w, `\r"`);
|
||||||
case '\t': n += write_string(b, `\t"`);
|
case '\t': n += write_string(w, `\t"`);
|
||||||
case '\v': n += write_string(b, `\v"`);
|
case '\v': n += write_string(w, `\v"`);
|
||||||
case:
|
case:
|
||||||
if r < 32 {
|
if r < 32 {
|
||||||
n += write_string(b, `\x`);
|
n += write_string(w, `\x`);
|
||||||
buf: [2]byte;
|
buf: [2]byte;
|
||||||
s := strconv.append_bits(buf[:], u64(r), 16, true, 64, strconv.digits, nil);
|
s := strconv.append_bits(buf[:], u64(r), 16, true, 64, strconv.digits, nil);
|
||||||
switch len(s) {
|
switch len(s) {
|
||||||
case 0: n += write_string(b, "00");
|
case 0: n += write_string(w, "00");
|
||||||
case 1: n += write_byte(b, '0');
|
case 1: n += _write_byte(w, '0');
|
||||||
case 2: n += write_string(b, s);
|
case 2: n += write_string(w, s);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
n += write_rune(b, r);
|
rn, _ := io.write_rune(w, r);
|
||||||
|
n += rn;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if write_quote {
|
if write_quote {
|
||||||
n += write_byte(b, '\'');
|
n += _write_byte(w, '\'');
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false) -> (n: int) {
|
write_escaped_rune :: proc{
|
||||||
|
write_escaped_rune_builder,
|
||||||
|
write_escaped_rune_writer,
|
||||||
|
};
|
||||||
|
|
||||||
|
write_escaped_rune_builder :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false) -> (n: int) {
|
||||||
|
return write_escaped_rune_writer(to_writer(b), r, quote, html_safe);
|
||||||
|
}
|
||||||
|
|
||||||
|
write_escaped_rune_writer :: proc(w: io.Writer, r: rune, quote: byte, html_safe := false) -> (n: int) {
|
||||||
is_printable :: proc(r: rune) -> bool {
|
is_printable :: proc(r: rune) -> bool {
|
||||||
if r <= 0xff {
|
if r <= 0xff {
|
||||||
switch r {
|
switch r {
|
||||||
@@ -233,54 +328,54 @@ write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false
|
|||||||
if html_safe {
|
if html_safe {
|
||||||
switch r {
|
switch r {
|
||||||
case '<', '>', '&':
|
case '<', '>', '&':
|
||||||
n += write_byte(b, '\\');
|
n += _write_byte(w, '\\');
|
||||||
n += write_byte(b, 'u');
|
n += _write_byte(w, 'u');
|
||||||
for s := 12; s >= 0; s -= 4 {
|
for s := 12; s >= 0; s -= 4 {
|
||||||
n += write_byte(b, DIGITS_LOWER[r>>uint(s) & 0xf]);
|
n += _write_byte(w, DIGITS_LOWER[r>>uint(s) & 0xf]);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if r == rune(quote) || r == '\\' {
|
if r == rune(quote) || r == '\\' {
|
||||||
n += write_byte(b, '\\');
|
n += _write_byte(w, '\\');
|
||||||
n += write_byte(b, byte(r));
|
n += _write_byte(w, byte(r));
|
||||||
return;
|
return;
|
||||||
} else if is_printable(r) {
|
} else if is_printable(r) {
|
||||||
n += write_encoded_rune(b, r, false);
|
n += write_encoded_rune(w, r, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch r {
|
switch r {
|
||||||
case '\a': n += write_string(b, `\a`);
|
case '\a': n += write_string(w, `\a`);
|
||||||
case '\b': n += write_string(b, `\b`);
|
case '\b': n += write_string(w, `\b`);
|
||||||
case '\e': n += write_string(b, `\e`);
|
case '\e': n += write_string(w, `\e`);
|
||||||
case '\f': n += write_string(b, `\f`);
|
case '\f': n += write_string(w, `\f`);
|
||||||
case '\n': n += write_string(b, `\n`);
|
case '\n': n += write_string(w, `\n`);
|
||||||
case '\r': n += write_string(b, `\r`);
|
case '\r': n += write_string(w, `\r`);
|
||||||
case '\t': n += write_string(b, `\t`);
|
case '\t': n += write_string(w, `\t`);
|
||||||
case '\v': n += write_string(b, `\v`);
|
case '\v': n += write_string(w, `\v`);
|
||||||
case:
|
case:
|
||||||
switch c := r; {
|
switch c := r; {
|
||||||
case c < ' ':
|
case c < ' ':
|
||||||
n += write_byte(b, '\\');
|
n += _write_byte(w, '\\');
|
||||||
n += write_byte(b, 'x');
|
n += _write_byte(w, 'x');
|
||||||
n += write_byte(b, DIGITS_LOWER[byte(c)>>4]);
|
n += _write_byte(w, DIGITS_LOWER[byte(c)>>4]);
|
||||||
n += write_byte(b, DIGITS_LOWER[byte(c)&0xf]);
|
n += _write_byte(w, DIGITS_LOWER[byte(c)&0xf]);
|
||||||
|
|
||||||
case c > utf8.MAX_RUNE:
|
case c > utf8.MAX_RUNE:
|
||||||
c = 0xfffd;
|
c = 0xfffd;
|
||||||
fallthrough;
|
fallthrough;
|
||||||
case c < 0x10000:
|
case c < 0x10000:
|
||||||
n += write_byte(b, '\\');
|
n += _write_byte(w, '\\');
|
||||||
n += write_byte(b, 'u');
|
n += _write_byte(w, 'u');
|
||||||
for s := 12; s >= 0; s -= 4 {
|
for s := 12; s >= 0; s -= 4 {
|
||||||
n += write_byte(b, DIGITS_LOWER[c>>uint(s) & 0xf]);
|
n += _write_byte(w, DIGITS_LOWER[c>>uint(s) & 0xf]);
|
||||||
}
|
}
|
||||||
case:
|
case:
|
||||||
n += write_byte(b, '\\');
|
n += _write_byte(w, '\\');
|
||||||
n += write_byte(b, 'U');
|
n += _write_byte(w, 'U');
|
||||||
for s := 28; s >= 0; s -= 4 {
|
for s := 28; s >= 0; s -= 4 {
|
||||||
n += write_byte(b, DIGITS_LOWER[c>>uint(s) & 0xf]);
|
n += _write_byte(w, DIGITS_LOWER[c>>uint(s) & 0xf]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,269 @@
|
|||||||
|
package strings
|
||||||
|
|
||||||
|
import "core:io"
|
||||||
|
import "core:unicode"
|
||||||
|
import "core:unicode/utf8"
|
||||||
|
|
||||||
|
to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> string {
|
||||||
|
if len(s) == 0 {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
b: Builder;
|
||||||
|
init_builder(&b, 0, 0, allocator);
|
||||||
|
|
||||||
|
s := s;
|
||||||
|
for c, i in s {
|
||||||
|
if c != utf8.RUNE_ERROR {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_, w := utf8.decode_rune_in_string(s[i:]);
|
||||||
|
if w == 1 {
|
||||||
|
grow_builder(&b, len(s) + len(replacement));
|
||||||
|
write_string(&b, s[:i]);
|
||||||
|
s = s[i:];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if builder_cap(b) == 0 {
|
||||||
|
return clone(s, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
invalid := false;
|
||||||
|
|
||||||
|
for i := 0; i < len(s); /**/ {
|
||||||
|
c := s[i];
|
||||||
|
if c < utf8.RUNE_SELF {
|
||||||
|
i += 1;
|
||||||
|
invalid = false;
|
||||||
|
write_byte(&b, c);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_, w := utf8.decode_rune_in_string(s[i:]);
|
||||||
|
if w == 1 {
|
||||||
|
i += 1;
|
||||||
|
if !invalid {
|
||||||
|
invalid = true;
|
||||||
|
write_string(&b, replacement);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
invalid = false;
|
||||||
|
write_string(&b, s[i:][:w]);
|
||||||
|
i += w;
|
||||||
|
}
|
||||||
|
return to_string(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
to_lower :: proc(s: string, allocator := context.allocator) -> string {
|
||||||
|
b: Builder;
|
||||||
|
init_builder(&b, 0, len(s), allocator);
|
||||||
|
for r in s {
|
||||||
|
write_rune_builder(&b, unicode.to_lower(r));
|
||||||
|
}
|
||||||
|
return to_string(b);
|
||||||
|
}
|
||||||
|
to_upper :: proc(s: string, allocator := context.allocator) -> string {
|
||||||
|
b: Builder;
|
||||||
|
init_builder(&b, 0, len(s), allocator);
|
||||||
|
for r in s {
|
||||||
|
write_rune_builder(&b, unicode.to_upper(r));
|
||||||
|
}
|
||||||
|
return to_string(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
is_delimiter :: proc(c: rune) -> bool {
|
||||||
|
return c == '-' || c == '_' || is_space(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
is_separator :: proc(r: rune) -> bool {
|
||||||
|
if r <= 0x7f {
|
||||||
|
switch r {
|
||||||
|
case '0'..'9': return false;
|
||||||
|
case 'a'..'z': return false;
|
||||||
|
case 'A'..'Z': return false;
|
||||||
|
case '_': return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(bill): unicode categories
|
||||||
|
// if unicode.is_letter(r) || unicode.is_digit(r) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
return unicode.is_space(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
string_case_iterator :: proc(w: io.Writer, s: string, callback: proc(w: io.Writer, prev, curr, next: rune)) {
|
||||||
|
prev, curr: rune;
|
||||||
|
for next in s {
|
||||||
|
if curr == 0 {
|
||||||
|
prev = curr;
|
||||||
|
curr = next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(w, prev, curr, next);
|
||||||
|
|
||||||
|
prev = curr;
|
||||||
|
curr = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(s) > 0 {
|
||||||
|
callback(w, prev, curr, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
to_lower_camel_case :: to_camel_case;
|
||||||
|
to_camel_case :: proc(s: string, allocator := context.allocator) -> string {
|
||||||
|
s := s;
|
||||||
|
s = trim_space(s);
|
||||||
|
b: Builder;
|
||||||
|
init_builder(&b, 0, len(s), allocator);
|
||||||
|
w := to_writer(&b);
|
||||||
|
|
||||||
|
string_case_iterator(w, s, proc(w: io.Writer, prev, curr, next: rune) {
|
||||||
|
if !is_delimiter(curr) {
|
||||||
|
if is_delimiter(prev) {
|
||||||
|
io.write_rune(w, unicode.to_upper(curr));
|
||||||
|
} else if unicode.is_lower(prev) {
|
||||||
|
io.write_rune(w, curr);
|
||||||
|
} else {
|
||||||
|
io.write_rune(w, unicode.to_lower(curr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return to_string(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
to_upper_camel_case :: to_pascal_case;
|
||||||
|
to_pascal_case :: proc(s: string, allocator := context.allocator) -> string {
|
||||||
|
s := s;
|
||||||
|
s = trim_space(s);
|
||||||
|
b: Builder;
|
||||||
|
init_builder(&b, 0, len(s), allocator);
|
||||||
|
w := to_writer(&b);
|
||||||
|
|
||||||
|
string_case_iterator(w, s, proc(w: io.Writer, prev, curr, next: rune) {
|
||||||
|
if !is_delimiter(curr) {
|
||||||
|
if is_delimiter(prev) || prev == 0 {
|
||||||
|
io.write_rune(w, unicode.to_upper(curr));
|
||||||
|
} else if unicode.is_lower(prev) {
|
||||||
|
io.write_rune(w, curr);
|
||||||
|
} else {
|
||||||
|
io.write_rune(w, unicode.to_lower(curr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return to_string(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allocator := context.allocator) -> string {
|
||||||
|
s := s;
|
||||||
|
s = trim_space(s);
|
||||||
|
b: Builder;
|
||||||
|
init_builder(&b, 0, len(s), allocator);
|
||||||
|
w := to_writer(&b);
|
||||||
|
|
||||||
|
adjust_case := unicode.to_upper if all_upper_case else unicode.to_lower;
|
||||||
|
|
||||||
|
prev, curr: rune;
|
||||||
|
|
||||||
|
for next in s {
|
||||||
|
if is_delimiter(curr) {
|
||||||
|
if !is_delimiter(prev) {
|
||||||
|
io.write_rune(w, delimiter);
|
||||||
|
}
|
||||||
|
} else if unicode.is_upper(curr) {
|
||||||
|
if unicode.is_lower(prev) || (unicode.is_upper(prev) && unicode.is_lower(next)) {
|
||||||
|
io.write_rune(w, delimiter);
|
||||||
|
}
|
||||||
|
io.write_rune(w, adjust_case(curr));
|
||||||
|
} else if curr != 0 {
|
||||||
|
io.write_rune(w, adjust_case(curr));
|
||||||
|
}
|
||||||
|
|
||||||
|
prev = curr;
|
||||||
|
curr = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(s) > 0 {
|
||||||
|
if unicode.is_upper(curr) && unicode.is_lower(prev) && prev != 0 {
|
||||||
|
io.write_rune(w, delimiter);
|
||||||
|
}
|
||||||
|
io.write_rune(w, adjust_case(curr));
|
||||||
|
}
|
||||||
|
|
||||||
|
return to_string(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
to_snake_case :: proc(s: string, allocator := context.allocator) -> string {
|
||||||
|
return to_delimiter_case(s, '_', false, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
to_screaming_snake_case :: to_upper_snake_case;
|
||||||
|
to_upper_snake_case :: proc(s: string, allocator := context.allocator) -> string {
|
||||||
|
return to_delimiter_case(s, '_', true, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
to_kebab_case :: proc(s: string, allocator := context.allocator) -> string {
|
||||||
|
return to_delimiter_case(s, '-', false, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
to_upper_case :: proc(s: string, allocator := context.allocator) -> string {
|
||||||
|
return to_delimiter_case(s, '-', true, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
to_ada_case :: proc(s: string, allocator := context.allocator) -> string {
|
||||||
|
delimiter :: '_';
|
||||||
|
|
||||||
|
s := s;
|
||||||
|
s = trim_space(s);
|
||||||
|
b: Builder;
|
||||||
|
init_builder(&b, 0, len(s), allocator);
|
||||||
|
w := to_writer(&b);
|
||||||
|
|
||||||
|
prev, curr: rune;
|
||||||
|
|
||||||
|
for next in s {
|
||||||
|
if is_delimiter(curr) {
|
||||||
|
if !is_delimiter(prev) {
|
||||||
|
io.write_rune(w, delimiter);
|
||||||
|
}
|
||||||
|
} else if unicode.is_upper(curr) {
|
||||||
|
if unicode.is_lower(prev) || (unicode.is_upper(prev) && unicode.is_lower(next)) {
|
||||||
|
io.write_rune(w, delimiter);
|
||||||
|
}
|
||||||
|
io.write_rune(w, unicode.to_upper(curr));
|
||||||
|
} else if curr != 0 {
|
||||||
|
io.write_rune(w, unicode.to_lower(curr));
|
||||||
|
}
|
||||||
|
|
||||||
|
prev = curr;
|
||||||
|
curr = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(s) > 0 {
|
||||||
|
if unicode.is_upper(curr) && unicode.is_lower(prev) && prev != 0 {
|
||||||
|
io.write_rune(w, delimiter);
|
||||||
|
io.write_rune(w, unicode.to_upper(curr));
|
||||||
|
} else {
|
||||||
|
io.write_rune(w, unicode.to_lower(curr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return to_string(b);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
package strings
|
||||||
|
|
||||||
|
import "core:io"
|
||||||
|
import "core:unicode/utf8"
|
||||||
|
|
||||||
|
Reader :: struct {
|
||||||
|
s: string, // read-only buffer
|
||||||
|
i: i64, // current reading index
|
||||||
|
prev_rune: int, // previous reading index of rune or < 0
|
||||||
|
}
|
||||||
|
|
||||||
|
reader_init :: proc(r: ^Reader, s: string) {
|
||||||
|
r.s = s;
|
||||||
|
r.i = 0;
|
||||||
|
r.prev_rune = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
|
||||||
|
s.stream_data = r;
|
||||||
|
s.stream_vtable = _reader_vtable;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
reader_length :: proc(r: ^Reader) -> int {
|
||||||
|
if r.i >= i64(len(r.s)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return int(i64(len(r.s)) - r.i);
|
||||||
|
}
|
||||||
|
|
||||||
|
reader_size :: proc(r: ^Reader) -> i64 {
|
||||||
|
return i64(len(r.s));
|
||||||
|
}
|
||||||
|
|
||||||
|
reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
|
||||||
|
if r.i >= i64(len(r.s)) {
|
||||||
|
return 0, .EOF;
|
||||||
|
}
|
||||||
|
r.prev_rune = -1;
|
||||||
|
n = copy(p, r.s[r.i:]);
|
||||||
|
r.i += i64(n);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Error) {
|
||||||
|
if off < 0 {
|
||||||
|
return 0, .Invalid_Offset;
|
||||||
|
}
|
||||||
|
if off >= i64(len(r.s)) {
|
||||||
|
return 0, .EOF;
|
||||||
|
}
|
||||||
|
n = copy(p, r.s[off:]);
|
||||||
|
if n < len(p) {
|
||||||
|
err = .EOF;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) {
|
||||||
|
r.prev_rune = -1;
|
||||||
|
if r.i >= i64(len(r.s)) {
|
||||||
|
return 0, .EOF;
|
||||||
|
}
|
||||||
|
b := r.s[r.i];
|
||||||
|
r.i += 1;
|
||||||
|
return b, nil;
|
||||||
|
}
|
||||||
|
reader_unread_byte :: proc(r: ^Reader) -> io.Error {
|
||||||
|
if r.i <= 0 {
|
||||||
|
return .Invalid_Unread;
|
||||||
|
}
|
||||||
|
r.prev_rune = -1;
|
||||||
|
r.i -= 1;
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) {
|
||||||
|
if r.i >= i64(len(r.s)) {
|
||||||
|
r.prev_rune = -1;
|
||||||
|
return 0, 0, .EOF;
|
||||||
|
}
|
||||||
|
r.prev_rune = int(r.i);
|
||||||
|
if c := r.s[r.i]; c < utf8.RUNE_SELF {
|
||||||
|
r.i += 1;
|
||||||
|
return rune(c), 1, nil;
|
||||||
|
}
|
||||||
|
ch, size = utf8.decode_rune_in_string(r.s[r.i:]);
|
||||||
|
r.i += i64(size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reader_unread_rune :: proc(r: ^Reader) -> io.Error {
|
||||||
|
if r.i <= 0 {
|
||||||
|
return .Invalid_Unread;
|
||||||
|
}
|
||||||
|
if r.prev_rune < 0 {
|
||||||
|
return .Invalid_Unread;
|
||||||
|
}
|
||||||
|
r.i = i64(r.prev_rune);
|
||||||
|
r.prev_rune = -1;
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
|
||||||
|
r.prev_rune = -1;
|
||||||
|
abs: i64;
|
||||||
|
switch whence {
|
||||||
|
case .Start:
|
||||||
|
abs = offset;
|
||||||
|
case .Current:
|
||||||
|
abs = r.i + offset;
|
||||||
|
case .End:
|
||||||
|
abs = i64(len(r.s)) + offset;
|
||||||
|
case:
|
||||||
|
return 0, .Invalid_Whence;
|
||||||
|
}
|
||||||
|
|
||||||
|
if abs < 0 {
|
||||||
|
return 0, .Invalid_Offset;
|
||||||
|
}
|
||||||
|
r.i = abs;
|
||||||
|
return abs, nil;
|
||||||
|
}
|
||||||
|
reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||||
|
r.prev_rune = -1;
|
||||||
|
if r.i >= i64(len(r.s)) {
|
||||||
|
return 0, nil;
|
||||||
|
}
|
||||||
|
s := r.s[r.i:];
|
||||||
|
m: int;
|
||||||
|
m, err = io.write_string(w, s);
|
||||||
|
if m > len(s) {
|
||||||
|
panic("bytes.Reader.write_to: invalid io.write_string count");
|
||||||
|
}
|
||||||
|
r.i += i64(m);
|
||||||
|
n = i64(m);
|
||||||
|
if m != len(s) && err == nil {
|
||||||
|
err = .Short_Write;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_reader_vtable := &io.Stream_VTable{
|
||||||
|
impl_size = proc(s: io.Stream) -> i64 {
|
||||||
|
r := (^Reader)(s.stream_data);
|
||||||
|
return reader_size(r);
|
||||||
|
},
|
||||||
|
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||||
|
r := (^Reader)(s.stream_data);
|
||||||
|
return reader_read(r, p);
|
||||||
|
},
|
||||||
|
impl_read_at = proc(s: io.Stream, p: []byte, off: i64) -> (n: int, err: io.Error) {
|
||||||
|
r := (^Reader)(s.stream_data);
|
||||||
|
return reader_read_at(r, p, off);
|
||||||
|
},
|
||||||
|
impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) {
|
||||||
|
r := (^Reader)(s.stream_data);
|
||||||
|
return reader_read_byte(r);
|
||||||
|
},
|
||||||
|
impl_unread_byte = proc(s: io.Stream) -> io.Error {
|
||||||
|
r := (^Reader)(s.stream_data);
|
||||||
|
return reader_unread_byte(r);
|
||||||
|
},
|
||||||
|
impl_read_rune = proc(s: io.Stream) -> (ch: rune, size: int, err: io.Error) {
|
||||||
|
r := (^Reader)(s.stream_data);
|
||||||
|
return reader_read_rune(r);
|
||||||
|
},
|
||||||
|
impl_unread_rune = proc(s: io.Stream) -> io.Error {
|
||||||
|
r := (^Reader)(s.stream_data);
|
||||||
|
return reader_unread_rune(r);
|
||||||
|
},
|
||||||
|
impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
|
||||||
|
r := (^Reader)(s.stream_data);
|
||||||
|
return reader_seek(r, offset, whence);
|
||||||
|
},
|
||||||
|
impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||||
|
r := (^Reader)(s.stream_data);
|
||||||
|
return reader_write_to(r, w);
|
||||||
|
},
|
||||||
|
};
|
||||||
+124
-278
@@ -1,5 +1,6 @@
|
|||||||
package strings
|
package strings
|
||||||
|
|
||||||
|
import "core:io"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:unicode"
|
import "core:unicode"
|
||||||
import "core:unicode/utf8"
|
import "core:unicode/utf8"
|
||||||
@@ -225,7 +226,7 @@ index_byte :: proc(s: string, c: byte) -> int {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns i1 if c is not present
|
// Returns -1 if c is not present
|
||||||
last_index_byte :: proc(s: string, c: byte) -> int {
|
last_index_byte :: proc(s: string, c: byte) -> int {
|
||||||
for i := len(s)-1; i >= 0; i -= 1 {
|
for i := len(s)-1; i >= 0; i -= 1 {
|
||||||
if s[i] == c {
|
if s[i] == c {
|
||||||
@@ -467,10 +468,12 @@ replace :: proc(s, old, new: string, n: int, allocator := context.allocator) ->
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(private) _ascii_space := [256]u8{'\t' = 1, '\n' = 1, '\v' = 1, '\f' = 1, '\r' = 1, ' ' = 1};
|
||||||
|
|
||||||
|
|
||||||
is_ascii_space :: proc(r: rune) -> bool {
|
is_ascii_space :: proc(r: rune) -> bool {
|
||||||
switch r {
|
if r < utf8.RUNE_SELF {
|
||||||
case '\t', '\n', '\v', '\f', '\r', ' ':
|
return _ascii_space[u8(r)] != 0;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -757,7 +760,8 @@ split_multi :: proc(s: string, substrs: []string, skip_empty := false, allocator
|
|||||||
// Adjacent invalid bytes are only replaced once
|
// Adjacent invalid bytes are only replaced once
|
||||||
scrub :: proc(s: string, replacement: string, allocator := context.allocator) -> string {
|
scrub :: proc(s: string, replacement: string, allocator := context.allocator) -> string {
|
||||||
str := s;
|
str := s;
|
||||||
b := make_builder(0, len(str), allocator);
|
b: Builder;
|
||||||
|
init_builder(&b, 0, len(s), allocator);
|
||||||
|
|
||||||
has_error := false;
|
has_error := false;
|
||||||
cursor := 0;
|
cursor := 0;
|
||||||
@@ -787,207 +791,6 @@ scrub :: proc(s: string, replacement: string, allocator := context.allocator) ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
to_lower :: proc(s: string, allocator := context.allocator) -> string {
|
|
||||||
b := make_builder(0, len(s), allocator);
|
|
||||||
for r in s {
|
|
||||||
write_rune(&b, unicode.to_lower(r));
|
|
||||||
}
|
|
||||||
return to_string(b);
|
|
||||||
}
|
|
||||||
to_upper :: proc(s: string, allocator := context.allocator) -> string {
|
|
||||||
b := make_builder(0, len(s), allocator);
|
|
||||||
for r in s {
|
|
||||||
write_rune(&b, unicode.to_upper(r));
|
|
||||||
}
|
|
||||||
return to_string(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
is_delimiter :: proc(c: rune) -> bool {
|
|
||||||
return c == '-' || c == '_' || is_space(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
is_separator :: proc(r: rune) -> bool {
|
|
||||||
if r <= 0x7f {
|
|
||||||
switch r {
|
|
||||||
case '0'..'9': return false;
|
|
||||||
case 'a'..'z': return false;
|
|
||||||
case 'A'..'Z': return false;
|
|
||||||
case '_': return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(bill): unicode categories
|
|
||||||
// if unicode.is_letter(r) || unicode.is_digit(r) {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
|
|
||||||
return unicode.is_space(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
string_case_iterator :: proc(b: ^Builder, s: string, callback: proc(b: ^Builder, prev, curr, next: rune)) {
|
|
||||||
prev, curr: rune;
|
|
||||||
for next in s {
|
|
||||||
if curr == 0 {
|
|
||||||
prev = curr;
|
|
||||||
curr = next;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(b, prev, curr, next);
|
|
||||||
|
|
||||||
prev = curr;
|
|
||||||
curr = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(s) > 0 {
|
|
||||||
callback(b, prev, curr, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
to_lower_camel_case :: to_camel_case;
|
|
||||||
to_camel_case :: proc(s: string, allocator := context.allocator) -> string {
|
|
||||||
s := s;
|
|
||||||
s = trim_space(s);
|
|
||||||
b := make_builder(0, len(s), allocator);
|
|
||||||
|
|
||||||
string_case_iterator(&b, s, proc(b: ^Builder, prev, curr, next: rune) {
|
|
||||||
if !is_delimiter(curr) {
|
|
||||||
if is_delimiter(prev) {
|
|
||||||
write_rune(b, unicode.to_upper(curr));
|
|
||||||
} else if unicode.is_lower(prev) {
|
|
||||||
write_rune(b, curr);
|
|
||||||
} else {
|
|
||||||
write_rune(b, unicode.to_lower(curr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return to_string(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
to_upper_camel_case :: to_pascal_case;
|
|
||||||
to_pascal_case :: proc(s: string, allocator := context.allocator) -> string {
|
|
||||||
s := s;
|
|
||||||
s = trim_space(s);
|
|
||||||
b := make_builder(0, len(s), allocator);
|
|
||||||
|
|
||||||
string_case_iterator(&b, s, proc(b: ^Builder, prev, curr, next: rune) {
|
|
||||||
if !is_delimiter(curr) {
|
|
||||||
if is_delimiter(prev) || prev == 0 {
|
|
||||||
write_rune(b, unicode.to_upper(curr));
|
|
||||||
} else if unicode.is_lower(prev) {
|
|
||||||
write_rune(b, curr);
|
|
||||||
} else {
|
|
||||||
write_rune(b, unicode.to_lower(curr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return to_string(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allocator := context.allocator) -> string {
|
|
||||||
s := s;
|
|
||||||
s = trim_space(s);
|
|
||||||
b := make_builder(0, len(s), allocator);
|
|
||||||
|
|
||||||
adjust_case := unicode.to_upper if all_upper_case else unicode.to_lower;
|
|
||||||
|
|
||||||
prev, curr: rune;
|
|
||||||
|
|
||||||
for next in s {
|
|
||||||
if is_delimiter(curr) {
|
|
||||||
if !is_delimiter(prev) {
|
|
||||||
write_rune(&b, delimiter);
|
|
||||||
}
|
|
||||||
} else if unicode.is_upper(curr) {
|
|
||||||
if unicode.is_lower(prev) || (unicode.is_upper(prev) && unicode.is_lower(next)) {
|
|
||||||
write_rune(&b, delimiter);
|
|
||||||
}
|
|
||||||
write_rune(&b, adjust_case(curr));
|
|
||||||
} else if curr != 0 {
|
|
||||||
write_rune(&b, adjust_case(curr));
|
|
||||||
}
|
|
||||||
|
|
||||||
prev = curr;
|
|
||||||
curr = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(s) > 0 {
|
|
||||||
if unicode.is_upper(curr) && unicode.is_lower(prev) && prev != 0 {
|
|
||||||
write_rune(&b, delimiter);
|
|
||||||
}
|
|
||||||
write_rune(&b, adjust_case(curr));
|
|
||||||
}
|
|
||||||
|
|
||||||
return to_string(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
to_snake_case :: proc(s: string, allocator := context.allocator) -> string {
|
|
||||||
return to_delimiter_case(s, '_', false, allocator);
|
|
||||||
}
|
|
||||||
|
|
||||||
to_screaming_snake_case :: to_upper_snake_case;
|
|
||||||
to_upper_snake_case :: proc(s: string, allocator := context.allocator) -> string {
|
|
||||||
return to_delimiter_case(s, '_', true, allocator);
|
|
||||||
}
|
|
||||||
|
|
||||||
to_kebab_case :: proc(s: string, allocator := context.allocator) -> string {
|
|
||||||
return to_delimiter_case(s, '-', false, allocator);
|
|
||||||
}
|
|
||||||
|
|
||||||
to_upper_case :: proc(s: string, allocator := context.allocator) -> string {
|
|
||||||
return to_delimiter_case(s, '-', true, allocator);
|
|
||||||
}
|
|
||||||
|
|
||||||
to_ada_case :: proc(s: string, allocator := context.allocator) -> string {
|
|
||||||
delimiter :: '_';
|
|
||||||
|
|
||||||
s := s;
|
|
||||||
s = trim_space(s);
|
|
||||||
b := make_builder(0, len(s), allocator);
|
|
||||||
|
|
||||||
prev, curr: rune;
|
|
||||||
|
|
||||||
for next in s {
|
|
||||||
if is_delimiter(curr) {
|
|
||||||
if !is_delimiter(prev) {
|
|
||||||
write_rune(&b, delimiter);
|
|
||||||
}
|
|
||||||
} else if unicode.is_upper(curr) {
|
|
||||||
if unicode.is_lower(prev) || (unicode.is_upper(prev) && unicode.is_lower(next)) {
|
|
||||||
write_rune(&b, delimiter);
|
|
||||||
}
|
|
||||||
write_rune(&b, unicode.to_upper(curr));
|
|
||||||
} else if curr != 0 {
|
|
||||||
write_rune(&b, unicode.to_lower(curr));
|
|
||||||
}
|
|
||||||
|
|
||||||
prev = curr;
|
|
||||||
curr = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(s) > 0 {
|
|
||||||
if unicode.is_upper(curr) && unicode.is_lower(prev) && prev != 0 {
|
|
||||||
write_rune(&b, delimiter);
|
|
||||||
write_rune(&b, unicode.to_upper(curr));
|
|
||||||
} else {
|
|
||||||
write_rune(&b, unicode.to_lower(curr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return to_string(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
reverse :: proc(s: string, allocator := context.allocator) -> string {
|
reverse :: proc(s: string, allocator := context.allocator) -> string {
|
||||||
str := s;
|
str := s;
|
||||||
n := len(str);
|
n := len(str);
|
||||||
@@ -1013,7 +816,9 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) ->
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
b := make_builder(allocator);
|
b: Builder;
|
||||||
|
init_builder(&b, allocator);
|
||||||
|
writer := to_writer(&b);
|
||||||
str := s;
|
str := s;
|
||||||
column: int;
|
column: int;
|
||||||
|
|
||||||
@@ -1024,7 +829,7 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) ->
|
|||||||
expand := tab_size - column%tab_size;
|
expand := tab_size - column%tab_size;
|
||||||
|
|
||||||
for i := 0; i < expand; i += 1 {
|
for i := 0; i < expand; i += 1 {
|
||||||
write_byte(&b, ' ');
|
io.write_byte(writer, ' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
column += expand;
|
column += expand;
|
||||||
@@ -1035,7 +840,7 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) ->
|
|||||||
column += w;
|
column += w;
|
||||||
}
|
}
|
||||||
|
|
||||||
write_rune(&b, r);
|
io.write_rune(writer, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
str = str[w:];
|
str = str[w:];
|
||||||
@@ -1070,12 +875,15 @@ centre_justify :: proc(str: string, length: int, pad: string, allocator := conte
|
|||||||
remains := length-1;
|
remains := length-1;
|
||||||
pad_len := rune_count(pad);
|
pad_len := rune_count(pad);
|
||||||
|
|
||||||
b := make_builder(allocator);
|
b: Builder;
|
||||||
|
init_builder(&b, allocator);
|
||||||
grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad));
|
grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad));
|
||||||
|
|
||||||
write_pad_string(&b, pad, pad_len, remains/2);
|
w := to_writer(&b);
|
||||||
write_string(&b, str);
|
|
||||||
write_pad_string(&b, pad, pad_len, (remains+1)/2);
|
write_pad_string(w, pad, pad_len, remains/2);
|
||||||
|
io.write_string(w, str);
|
||||||
|
write_pad_string(w, pad, pad_len, (remains+1)/2);
|
||||||
|
|
||||||
return to_string(b);
|
return to_string(b);
|
||||||
}
|
}
|
||||||
@@ -1090,11 +898,14 @@ left_justify :: proc(str: string, length: int, pad: string, allocator := context
|
|||||||
remains := length-1;
|
remains := length-1;
|
||||||
pad_len := rune_count(pad);
|
pad_len := rune_count(pad);
|
||||||
|
|
||||||
b := make_builder(allocator);
|
b: Builder;
|
||||||
|
init_builder(&b, allocator);
|
||||||
grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad));
|
grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad));
|
||||||
|
|
||||||
write_string(&b, str);
|
w := to_writer(&b);
|
||||||
write_pad_string(&b, pad, pad_len, remains);
|
|
||||||
|
io.write_string(w, str);
|
||||||
|
write_pad_string(w, pad, pad_len, remains);
|
||||||
|
|
||||||
return to_string(b);
|
return to_string(b);
|
||||||
}
|
}
|
||||||
@@ -1109,86 +920,121 @@ right_justify :: proc(str: string, length: int, pad: string, allocator := contex
|
|||||||
remains := length-1;
|
remains := length-1;
|
||||||
pad_len := rune_count(pad);
|
pad_len := rune_count(pad);
|
||||||
|
|
||||||
b := make_builder(allocator);
|
b: Builder;
|
||||||
|
init_builder(&b, allocator);
|
||||||
grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad));
|
grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad));
|
||||||
|
|
||||||
write_pad_string(&b, pad, pad_len, remains);
|
w := to_writer(&b);
|
||||||
write_string(&b, str);
|
|
||||||
|
write_pad_string(w, pad, pad_len, remains);
|
||||||
|
io.write_string(w, str);
|
||||||
|
|
||||||
return to_string(b);
|
return to_string(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> string {
|
|
||||||
if len(s) == 0 {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
b := make_builder_len_cap(0, 0, allocator);
|
|
||||||
|
|
||||||
s := s;
|
|
||||||
for c, i in s {
|
|
||||||
if c != utf8.RUNE_ERROR {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_, w := utf8.decode_rune_in_string(s[i:]);
|
|
||||||
if w == 1 {
|
|
||||||
grow_builder(&b, len(s) + len(replacement));
|
|
||||||
write_string(&b, s[:i]);
|
|
||||||
s = s[i:];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if builder_cap(b) == 0 {
|
|
||||||
return clone(s, allocator);
|
|
||||||
}
|
|
||||||
|
|
||||||
invalid := false;
|
|
||||||
|
|
||||||
for i := 0; i < len(s); /**/ {
|
|
||||||
c := s[i];
|
|
||||||
if c < utf8.RUNE_SELF {
|
|
||||||
i += 1;
|
|
||||||
invalid = false;
|
|
||||||
write_byte(&b, c);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_, w := utf8.decode_rune_in_string(s[i:]);
|
|
||||||
if w == 1 {
|
|
||||||
i += 1;
|
|
||||||
if !invalid {
|
|
||||||
invalid = true;
|
|
||||||
write_string(&b, replacement);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
invalid = false;
|
|
||||||
write_string(&b, s[i:][:w]);
|
|
||||||
i += w;
|
|
||||||
}
|
|
||||||
return to_string(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@private
|
@private
|
||||||
write_pad_string :: proc(b: ^Builder, pad: string, pad_len, remains: int) {
|
write_pad_string :: proc(w: io.Writer, pad: string, pad_len, remains: int) {
|
||||||
repeats := remains / pad_len;
|
repeats := remains / pad_len;
|
||||||
|
|
||||||
for i := 0; i < repeats; i += 1 {
|
for i := 0; i < repeats; i += 1 {
|
||||||
write_string(b, pad);
|
io.write_string(w, pad);
|
||||||
}
|
}
|
||||||
|
|
||||||
n := remains % pad_len;
|
n := remains % pad_len;
|
||||||
p := pad;
|
p := pad;
|
||||||
|
|
||||||
for i := 0; i < n; i += 1 {
|
for i := 0; i < n; i += 1 {
|
||||||
r, w := utf8.decode_rune_in_string(p);
|
r, width := utf8.decode_rune_in_string(p);
|
||||||
write_rune(b, r);
|
io.write_rune(w, r);
|
||||||
p = p[w:];
|
p = p[width:];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// fields splits the string s around each instance of one or more consecutive white space character, defined by unicode.is_space
|
||||||
|
// returning a slice of substrings of s or an empty slice if s only contains white space
|
||||||
|
fields :: proc(s: string, allocator := context.allocator) -> []string #no_bounds_check {
|
||||||
|
n := 0;
|
||||||
|
was_space := 1;
|
||||||
|
set_bits := u8(0);
|
||||||
|
|
||||||
|
// check to see
|
||||||
|
for i in 0..<len(s) {
|
||||||
|
r := s[i];
|
||||||
|
set_bits |= r;
|
||||||
|
is_space := int(_ascii_space[r]);
|
||||||
|
n += was_space & ~is_space;
|
||||||
|
was_space = is_space;
|
||||||
|
}
|
||||||
|
|
||||||
|
if set_bits >= utf8.RUNE_SELF {
|
||||||
|
return fields_proc(s, unicode.is_space, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
if n == 0 {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
a := make([]string, n, allocator);
|
||||||
|
na := 0;
|
||||||
|
field_start := 0;
|
||||||
|
i := 0;
|
||||||
|
for i < len(s) && _ascii_space[s[i]] != 0 {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
field_start = i;
|
||||||
|
for i < len(s) {
|
||||||
|
if _ascii_space[s[i]] == 0 {
|
||||||
|
i += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
a[na] = s[field_start : i];
|
||||||
|
na += 1;
|
||||||
|
i += 1;
|
||||||
|
for i < len(s) && _ascii_space[s[i]] != 0 {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
field_start = i;
|
||||||
|
}
|
||||||
|
if field_start < len(s) {
|
||||||
|
a[na] = s[field_start:];
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// fields_proc splits the string s at each run of unicode code points `ch` satisfying f(ch)
|
||||||
|
// returns a slice of substrings of s
|
||||||
|
// If all code points in s satisfy f(ch) or string is empty, an empty slice is returned
|
||||||
|
//
|
||||||
|
// fields_proc makes no guarantee about the order in which it calls f(ch)
|
||||||
|
// it assumes that `f` always returns the same value for a given ch
|
||||||
|
fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.allocator) -> []string #no_bounds_check {
|
||||||
|
substrings := make([dynamic]string, 0, 32, allocator);
|
||||||
|
|
||||||
|
start, end := -1, -1;
|
||||||
|
for r, offset in s {
|
||||||
|
end = offset;
|
||||||
|
if f(r) {
|
||||||
|
if start >= 0 {
|
||||||
|
append(&substrings, s[start : end]);
|
||||||
|
// -1 could be used, but just speed it up through bitwise not
|
||||||
|
// gotta love 2's complement
|
||||||
|
start = ~start;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if start < 0 {
|
||||||
|
start = end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if start >= 0 {
|
||||||
|
append(&substrings, s[start : end]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return substrings[:];
|
||||||
|
}
|
||||||
|
|||||||
+70
-43
@@ -2,60 +2,89 @@ package sync
|
|||||||
|
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:time"
|
import "core:time"
|
||||||
import "core:intrinsics"
|
import "intrinsics"
|
||||||
import "core:math/rand"
|
import "core:math/rand"
|
||||||
|
|
||||||
_, _ :: time, rand;
|
_, _ :: time, rand;
|
||||||
|
|
||||||
|
Channel_Direction :: enum i8 {
|
||||||
|
Both = 0,
|
||||||
|
Send = +1,
|
||||||
|
Recv = -1,
|
||||||
|
}
|
||||||
|
|
||||||
Channel :: struct(T: typeid) {
|
Channel :: struct(T: typeid, Direction := Channel_Direction.Both) {
|
||||||
using _internal: ^Raw_Channel,
|
using _internal: ^Raw_Channel,
|
||||||
}
|
}
|
||||||
|
|
||||||
channel_init :: proc(ch: ^$C/Channel($T), cap := 0, allocator := context.allocator) {
|
channel_init :: proc(ch: ^$C/Channel($T, $D), cap := 0, allocator := context.allocator) {
|
||||||
context.allocator = allocator;
|
context.allocator = allocator;
|
||||||
ch._internal = raw_channel_create(size_of(T), align_of(T), cap);
|
ch._internal = raw_channel_create(size_of(T), align_of(T), cap);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
channel_make :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T)) {
|
channel_make :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Both)) {
|
||||||
context.allocator = allocator;
|
context.allocator = allocator;
|
||||||
ch._internal = raw_channel_create(size_of(T), align_of(T), cap);
|
ch._internal = raw_channel_create(size_of(T), align_of(T), cap);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
channel_destroy :: proc(ch: $C/Channel($T)) {
|
channel_make_send :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Send)) {
|
||||||
|
context.allocator = allocator;
|
||||||
|
ch._internal = raw_channel_create(size_of(T), align_of(T), cap);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
channel_make_recv :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Recv)) {
|
||||||
|
context.allocator = allocator;
|
||||||
|
ch._internal = raw_channel_create(size_of(T), align_of(T), cap);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
channel_destroy :: proc(ch: $C/Channel($T, $D)) {
|
||||||
raw_channel_destroy(ch._internal);
|
raw_channel_destroy(ch._internal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
channel_as_send :: proc(ch: $C/Channel($T, .Both)) -> (res: Channel(T, .Send)) {
|
||||||
channel_len :: proc(ch: $C/Channel($T)) -> int {
|
res._internal = ch._internal;
|
||||||
return ch._internal.len;
|
return;
|
||||||
}
|
}
|
||||||
channel_cap :: proc(ch: $C/Channel($T)) -> int {
|
|
||||||
return ch._internal.cap;
|
channel_as_recv :: proc(ch: $C/Channel($T, .Both)) -> (res: Channel(T, .Recv)) {
|
||||||
|
res._internal = ch._internal;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
channel_send :: proc(ch: $C/Channel($T), msg: T, loc := #caller_location) {
|
channel_len :: proc(ch: $C/Channel($T, $D)) -> int {
|
||||||
|
return ch._internal.len if ch._internal != nil else 0;
|
||||||
|
}
|
||||||
|
channel_cap :: proc(ch: $C/Channel($T, $D)) -> int {
|
||||||
|
return ch._internal.cap if ch._internal != nil else 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
channel_send :: proc(ch: $C/Channel($T, $D), msg: T, loc := #caller_location) where D >= .Both {
|
||||||
msg := msg;
|
msg := msg;
|
||||||
_ = raw_channel_send_impl(ch._internal, &msg, /*block*/true, loc);
|
_ = raw_channel_send_impl(ch._internal, &msg, /*block*/true, loc);
|
||||||
}
|
}
|
||||||
channel_try_send :: proc(ch: $C/Channel($T), msg: T, loc := #caller_location) -> bool {
|
channel_try_send :: proc(ch: $C/Channel($T, $D), msg: T, loc := #caller_location) -> bool where D >= .Both {
|
||||||
msg := msg;
|
msg := msg;
|
||||||
return raw_channel_send_impl(ch._internal, &msg, /*block*/false, loc);
|
return raw_channel_send_impl(ch._internal, &msg, /*block*/false, loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
channel_recv :: proc(ch: $C/Channel($T), loc := #caller_location) -> (msg: T) {
|
channel_recv :: proc(ch: $C/Channel($T, $D), loc := #caller_location) -> (msg: T) where D <= .Both {
|
||||||
c := ch._internal;
|
c := ch._internal;
|
||||||
|
if c == nil {
|
||||||
|
panic(message="cannot recv message; channel is nil", loc=loc);
|
||||||
|
}
|
||||||
mutex_lock(&c.mutex);
|
mutex_lock(&c.mutex);
|
||||||
raw_channel_recv_impl(c, &msg, loc);
|
raw_channel_recv_impl(c, &msg, loc);
|
||||||
mutex_unlock(&c.mutex);
|
mutex_unlock(&c.mutex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
channel_try_recv :: proc(ch: $C/Channel($T), loc := #caller_location) -> (msg: T, ok: bool) {
|
channel_try_recv :: proc(ch: $C/Channel($T, $D), loc := #caller_location) -> (msg: T, ok: bool) where D <= .Both {
|
||||||
c := ch._internal;
|
c := ch._internal;
|
||||||
if mutex_try_lock(&c.mutex) {
|
if c != nil && mutex_try_lock(&c.mutex) {
|
||||||
if c.len > 0 {
|
if c.len > 0 {
|
||||||
raw_channel_recv_impl(c, &msg, loc);
|
raw_channel_recv_impl(c, &msg, loc);
|
||||||
ok = true;
|
ok = true;
|
||||||
@@ -64,7 +93,7 @@ channel_try_recv :: proc(ch: $C/Channel($T), loc := #caller_location) -> (msg: T
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
channel_try_recv_ptr :: proc(ch: $C/Channel($T), msg: ^T, loc := #caller_location) -> (ok: bool) {
|
channel_try_recv_ptr :: proc(ch: $C/Channel($T, $D), msg: ^T, loc := #caller_location) -> (ok: bool) where D <= .Both {
|
||||||
res: T;
|
res: T;
|
||||||
res, ok = channel_try_recv(ch, loc);
|
res, ok = channel_try_recv(ch, loc);
|
||||||
if ok && msg != nil {
|
if ok && msg != nil {
|
||||||
@@ -74,32 +103,32 @@ channel_try_recv_ptr :: proc(ch: $C/Channel($T), msg: ^T, loc := #caller_locatio
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
channel_is_nil :: proc(ch: $C/Channel($T)) -> bool {
|
channel_is_nil :: proc(ch: $C/Channel($T, $D)) -> bool {
|
||||||
return ch._internal == nil;
|
return ch._internal == nil;
|
||||||
}
|
}
|
||||||
channel_is_open :: proc(ch: $C/Channel($T)) -> bool {
|
channel_is_open :: proc(ch: $C/Channel($T, $D)) -> bool {
|
||||||
c := ch._internal;
|
c := ch._internal;
|
||||||
return c != nil && !c.closed;
|
return c != nil && !c.closed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
channel_eq :: proc(a, b: $C/Channel($T)) -> bool {
|
channel_eq :: proc(a, b: $C/Channel($T, $D)) -> bool {
|
||||||
return a._internal == b._internal;
|
return a._internal == b._internal;
|
||||||
}
|
}
|
||||||
channel_ne :: proc(a, b: $C/Channel($T)) -> bool {
|
channel_ne :: proc(a, b: $C/Channel($T, $D)) -> bool {
|
||||||
return a._internal != b._internal;
|
return a._internal != b._internal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
channel_can_send :: proc(ch: $C/Channel($T)) -> (ok: bool) {
|
channel_can_send :: proc(ch: $C/Channel($T, $D)) -> (ok: bool) where D >= .Both {
|
||||||
return raw_channel_can_send(ch._internal);
|
return raw_channel_can_send(ch._internal);
|
||||||
}
|
}
|
||||||
channel_can_recv :: proc(ch: $C/Channel($T)) -> (ok: bool) {
|
channel_can_recv :: proc(ch: $C/Channel($T, $D)) -> (ok: bool) where D <= .Both {
|
||||||
return raw_channel_can_recv(ch._internal);
|
return raw_channel_can_recv(ch._internal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
channel_peek :: proc(ch: $C/Channel($T)) -> int {
|
channel_peek :: proc(ch: $C/Channel($T, $D)) -> int {
|
||||||
c := ch._internal;
|
c := ch._internal;
|
||||||
if c == nil {
|
if c == nil {
|
||||||
return -1;
|
return -1;
|
||||||
@@ -111,12 +140,12 @@ channel_peek :: proc(ch: $C/Channel($T)) -> int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
channel_close :: proc(ch: $C/Channel($T), loc := #caller_location) {
|
channel_close :: proc(ch: $C/Channel($T, $D), loc := #caller_location) {
|
||||||
raw_channel_close(ch._internal, loc);
|
raw_channel_close(ch._internal, loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
channel_iterator :: proc(ch: $C/Channel($T)) -> (msg: T, ok: bool) {
|
channel_iterator :: proc(ch: $C/Channel($T, $D)) -> (msg: T, ok: bool) where D <= .Both {
|
||||||
c := ch._internal;
|
c := ch._internal;
|
||||||
if c == nil {
|
if c == nil {
|
||||||
return;
|
return;
|
||||||
@@ -127,12 +156,12 @@ channel_iterator :: proc(ch: $C/Channel($T)) -> (msg: T, ok: bool) {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
channel_drain :: proc(ch: $C/Channel($T)) {
|
channel_drain :: proc(ch: $C/Channel($T, $D)) where D >= .Both {
|
||||||
raw_channel_drain(ch._internal);
|
raw_channel_drain(ch._internal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
channel_move :: proc(dst, src: $C/Channel($T)) {
|
channel_move :: proc(dst: $C1/Channel($T, $D1) src: $C2/Channel(T, $D2)) where D1 <= .Both, D2 >= .Both {
|
||||||
for msg in channel_iterator(src) {
|
for msg in channel_iterator(src) {
|
||||||
channel_send(dst, msg);
|
channel_send(dst, msg);
|
||||||
}
|
}
|
||||||
@@ -258,18 +287,19 @@ raw_channel_send_impl :: proc(c: ^Raw_Channel, msg: rawptr, block: bool, loc :=
|
|||||||
for c.len >= c.cap {
|
for c.len >= c.cap {
|
||||||
condition_wait_for(&c.cond);
|
condition_wait_for(&c.cond);
|
||||||
}
|
}
|
||||||
} else if c.len > 0 {
|
} else if c.len > 0 { // TODO(bill): determine correct behaviour
|
||||||
if !block {
|
if !block {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
condition_wait_for(&c.cond);
|
condition_wait_for(&c.cond);
|
||||||
|
} else if c.len == 0 && !block {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
send(c, msg);
|
send(c, msg);
|
||||||
condition_signal(&c.cond);
|
condition_signal(&c.cond);
|
||||||
raw_channel_wait_queue_signal(c.recvq);
|
raw_channel_wait_queue_signal(c.recvq);
|
||||||
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -509,7 +539,7 @@ select_recv :: proc(channels: ..^Raw_Channel) -> (index: int) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
select_recv_msg :: proc(channels: ..$C/Channel($T)) -> (msg: T, index: int) {
|
select_recv_msg :: proc(channels: ..$C/Channel($T, $D)) -> (msg: T, index: int) {
|
||||||
switch len(channels) {
|
switch len(channels) {
|
||||||
case 0:
|
case 0:
|
||||||
panic("sync: select with no channels");
|
panic("sync: select with no channels");
|
||||||
@@ -535,7 +565,7 @@ select_recv_msg :: proc(channels: ..$C/Channel($T)) -> (msg: T, index: int) {
|
|||||||
q.state = &state;
|
q.state = &state;
|
||||||
raw_channel_wait_queue_insert(&c.recvq, q);
|
raw_channel_wait_queue_insert(&c.recvq, q);
|
||||||
}
|
}
|
||||||
raw_channel_wait_queue_wait_on(&state);
|
raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT);
|
||||||
for c, i in channels {
|
for c, i in channels {
|
||||||
q := &queues[i];
|
q := &queues[i];
|
||||||
raw_channel_wait_queue_remove(&c.recvq, q);
|
raw_channel_wait_queue_remove(&c.recvq, q);
|
||||||
@@ -560,7 +590,7 @@ select_recv_msg :: proc(channels: ..$C/Channel($T)) -> (msg: T, index: int) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
select_send_msg :: proc(msg: $T, channels: ..$C/Channel(T)) -> (index: int) {
|
select_send_msg :: proc(msg: $T, channels: ..$C/Channel(T, $D)) -> (index: int) {
|
||||||
switch len(channels) {
|
switch len(channels) {
|
||||||
case 0:
|
case 0:
|
||||||
panic("sync: select with no channels");
|
panic("sync: select with no channels");
|
||||||
@@ -589,7 +619,7 @@ select_send_msg :: proc(msg: $T, channels: ..$C/Channel(T)) -> (index: int) {
|
|||||||
q.state = &state;
|
q.state = &state;
|
||||||
raw_channel_wait_queue_insert(&c.recvq, q);
|
raw_channel_wait_queue_insert(&c.recvq, q);
|
||||||
}
|
}
|
||||||
raw_channel_wait_queue_wait_on(&state);
|
raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT);
|
||||||
for c, i in channels {
|
for c, i in channels {
|
||||||
q := &queues[i];
|
q := &queues[i];
|
||||||
raw_channel_wait_queue_remove(&c.recvq, q);
|
raw_channel_wait_queue_remove(&c.recvq, q);
|
||||||
@@ -781,16 +811,15 @@ select_try_send :: proc(channels: ..^Raw_Channel) -> (index: int) #no_bounds_che
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
select_try_recv_msg :: proc(channels: ..$C/Channel($T)) -> (msg: T, index: int) {
|
select_try_recv_msg :: proc(channels: ..$C/Channel($T, $D)) -> (msg: T, index: int) {
|
||||||
switch len(channels) {
|
switch len(channels) {
|
||||||
case 0:
|
case 0:
|
||||||
index = 0;
|
index = -1;
|
||||||
return;
|
return;
|
||||||
case 1:
|
case 1:
|
||||||
if c := channels[0]; channel_can_recv(c) {
|
ok: bool;
|
||||||
|
if msg, ok = channel_try_recv(channels[0]); ok {
|
||||||
index = 0;
|
index = 0;
|
||||||
msg = channel_recv(c);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -820,16 +849,14 @@ select_try_recv_msg :: proc(channels: ..$C/Channel($T)) -> (msg: T, index: int)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
select_try_send_msg :: proc(msg: $T, channels: ..$C/Channel(T)) -> (index: int) {
|
select_try_send_msg :: proc(msg: $T, channels: ..$C/Channel(T, $D)) -> (index: int) {
|
||||||
|
index = -1;
|
||||||
switch len(channels) {
|
switch len(channels) {
|
||||||
case 0:
|
case 0:
|
||||||
index = 0;
|
|
||||||
return;
|
return;
|
||||||
case 1:
|
case 1:
|
||||||
if c := channels[0]; channel_can_send(c) {
|
if channel_try_send(channels[0], msg) {
|
||||||
index = 0;
|
index = 0;
|
||||||
channel_send(c, msg);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package sys_cpu
|
||||||
|
|
||||||
|
#assert(ODIN_USE_LLVM_API);
|
||||||
|
|
||||||
|
Cache_Line_Pad :: struct {_: [_cache_line_size]byte};
|
||||||
|
|
||||||
|
initialized: bool;
|
||||||
|
|
||||||
|
x86: struct {
|
||||||
|
_: Cache_Line_Pad,
|
||||||
|
has_aes: bool, // AES hardware implementation (AES NI)
|
||||||
|
has_adx: bool, // Multi-precision add-carry instruction extensions
|
||||||
|
has_avx: bool, // Advanced vector extension
|
||||||
|
has_avx2: bool, // Advanced vector extension 2
|
||||||
|
has_bmi1: bool, // Bit manipulation instruction set 1
|
||||||
|
has_bmi2: bool, // Bit manipulation instruction set 2
|
||||||
|
has_erms: bool, // Enhanced REP for MOVSB and STOSB
|
||||||
|
has_fma: bool, // Fused-multiply-add instructions
|
||||||
|
has_os_xsave: bool, // OS supports XSAVE/XRESTOR for saving/restoring XMM registers.
|
||||||
|
has_pclmulqdq: bool, // PCLMULQDQ instruction - most often used for AES-GCM
|
||||||
|
has_popcnt: bool, // Hamming weight instruction POPCNT.
|
||||||
|
has_rdrand: bool, // RDRAND instruction (on-chip random number generator)
|
||||||
|
has_rdseed: bool, // RDSEED instruction (on-chip random number generator)
|
||||||
|
has_sse2: bool, // Streaming SIMD extension 2 (always available on amd64)
|
||||||
|
has_sse3: bool, // Streaming SIMD extension 3
|
||||||
|
has_ssse3: bool, // Supplemental streaming SIMD extension 3
|
||||||
|
has_sse41: bool, // Streaming SIMD extension 4 and 4.1
|
||||||
|
has_sse42: bool, // Streaming SIMD extension 4 and 4.2
|
||||||
|
_: Cache_Line_Pad,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
init :: proc() {
|
||||||
|
_init();
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
//+build 386, amd64
|
||||||
|
package sys_cpu
|
||||||
|
|
||||||
|
_cache_line_size :: 64;
|
||||||
|
|
||||||
|
cpuid :: proc(ax, cx: u32) -> (eax, ebc, ecx, edx: u32) {
|
||||||
|
return expand_to_tuple(asm(u32, u32) -> struct{eax, ebc, ecx, edx: u32} {
|
||||||
|
"cpuid",
|
||||||
|
"={ax},={bx},={cx},={dx},{ax},{cx}",
|
||||||
|
}(ax, cx));
|
||||||
|
}
|
||||||
|
|
||||||
|
xgetbv :: proc() -> (eax, edx: u32) {
|
||||||
|
return expand_to_tuple(asm(u32) -> struct{eax, edx: u32} {
|
||||||
|
"xgetbv",
|
||||||
|
"={ax},={dx},{cx}",
|
||||||
|
}(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
_init :: proc() {
|
||||||
|
is_set :: proc(hwc: u32, value: u32) -> bool {
|
||||||
|
return hwc&value != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
initialized = true;
|
||||||
|
|
||||||
|
max_id, _, _, _ := cpuid(0, 0);
|
||||||
|
|
||||||
|
if max_id < 1 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, ecx1, edx1 := cpuid(1, 0);
|
||||||
|
|
||||||
|
x86.has_sse2 = is_set(26, edx1);
|
||||||
|
|
||||||
|
x86.has_sse3 = is_set(0, ecx1);
|
||||||
|
x86.has_pclmulqdq = is_set(1, ecx1);
|
||||||
|
x86.has_ssse3 = is_set(9, ecx1);
|
||||||
|
x86.has_fma = is_set(12, ecx1);
|
||||||
|
x86.has_sse41 = is_set(19, ecx1);
|
||||||
|
x86.has_sse42 = is_set(20, ecx1);
|
||||||
|
x86.has_popcnt = is_set(23, ecx1);
|
||||||
|
x86.has_aes = is_set(25, ecx1);
|
||||||
|
x86.has_os_xsave = is_set(27, ecx1);
|
||||||
|
x86.has_rdrand = is_set(30, ecx1);
|
||||||
|
|
||||||
|
os_supports_avx := false;
|
||||||
|
if x86.has_os_xsave {
|
||||||
|
eax, _ := xgetbv();
|
||||||
|
os_supports_avx = is_set(1, eax) && is_set(2, eax);
|
||||||
|
}
|
||||||
|
|
||||||
|
x86.has_avx = is_set(28, ecx1) && os_supports_avx;
|
||||||
|
|
||||||
|
if max_id < 7 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_, ebx7, _, _ := cpuid(7, 0);
|
||||||
|
x86.has_bmi1 = is_set(3, ebx7);
|
||||||
|
x86.has_avx2 = is_set(5, ebx7) && os_supports_avx;
|
||||||
|
x86.has_bmi2 = is_set(8, ebx7);
|
||||||
|
x86.has_erms = is_set(9, ebx7);
|
||||||
|
x86.has_rdseed = is_set(18, ebx7);
|
||||||
|
x86.has_adx = is_set(19, ebx7);
|
||||||
|
}
|
||||||
@@ -14,44 +14,44 @@ PTHREAD_ONCE_SIZE :: 8;
|
|||||||
PTHREAD_RWLOCK_SIZE :: 192;
|
PTHREAD_RWLOCK_SIZE :: 192;
|
||||||
PTHREAD_RWLOCKATTR_SIZE :: 16;
|
PTHREAD_RWLOCKATTR_SIZE :: 16;
|
||||||
|
|
||||||
pthread_t :: opaque u64;
|
pthread_t :: #opaque u64;
|
||||||
|
|
||||||
pthread_attr_t :: opaque struct #align 16 {
|
pthread_attr_t :: #opaque struct #align 16 {
|
||||||
sig: c.long,
|
sig: c.long,
|
||||||
_: [PTHREAD_ATTR_SIZE] c.char,
|
_: [PTHREAD_ATTR_SIZE] c.char,
|
||||||
};
|
};
|
||||||
|
|
||||||
pthread_cond_t :: opaque struct #align 16 {
|
pthread_cond_t :: #opaque struct #align 16 {
|
||||||
sig: c.long,
|
sig: c.long,
|
||||||
_: [PTHREAD_COND_SIZE] c.char,
|
_: [PTHREAD_COND_SIZE] c.char,
|
||||||
};
|
};
|
||||||
|
|
||||||
pthread_condattr_t :: opaque struct #align 16 {
|
pthread_condattr_t :: #opaque struct #align 16 {
|
||||||
sig: c.long,
|
sig: c.long,
|
||||||
_: [PTHREAD_CONDATTR_SIZE] c.char,
|
_: [PTHREAD_CONDATTR_SIZE] c.char,
|
||||||
};
|
};
|
||||||
|
|
||||||
pthread_mutex_t :: opaque struct #align 16 {
|
pthread_mutex_t :: #opaque struct #align 16 {
|
||||||
sig: c.long,
|
sig: c.long,
|
||||||
_: [PTHREAD_MUTEX_SIZE] c.char,
|
_: [PTHREAD_MUTEX_SIZE] c.char,
|
||||||
};
|
};
|
||||||
|
|
||||||
pthread_mutexattr_t :: opaque struct #align 16 {
|
pthread_mutexattr_t :: #opaque struct #align 16 {
|
||||||
sig: c.long,
|
sig: c.long,
|
||||||
_: [PTHREAD_MUTEXATTR_SIZE] c.char,
|
_: [PTHREAD_MUTEXATTR_SIZE] c.char,
|
||||||
};
|
};
|
||||||
|
|
||||||
pthread_once_t :: opaque struct #align 16 {
|
pthread_once_t :: #opaque struct #align 16 {
|
||||||
sig: c.long,
|
sig: c.long,
|
||||||
_: [PTHREAD_ONCE_SIZE] c.char,
|
_: [PTHREAD_ONCE_SIZE] c.char,
|
||||||
};
|
};
|
||||||
|
|
||||||
pthread_rwlock_t :: opaque struct #align 16 {
|
pthread_rwlock_t :: #opaque struct #align 16 {
|
||||||
sig: c.long,
|
sig: c.long,
|
||||||
_: [PTHREAD_RWLOCK_SIZE] c.char,
|
_: [PTHREAD_RWLOCK_SIZE] c.char,
|
||||||
};
|
};
|
||||||
|
|
||||||
pthread_rwlockattr_t :: opaque struct #align 16 {
|
pthread_rwlockattr_t :: #opaque struct #align 16 {
|
||||||
sig: c.long,
|
sig: c.long,
|
||||||
_: [PTHREAD_RWLOCKATTR_SIZE] c.char,
|
_: [PTHREAD_RWLOCKATTR_SIZE] c.char,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -26,32 +26,32 @@ when size_of(int) == 8 {
|
|||||||
PTHREAD_BARRIER_T_SIZE :: 20;
|
PTHREAD_BARRIER_T_SIZE :: 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_cond_t :: opaque struct #align 16 {
|
pthread_cond_t :: #opaque struct #align 16 {
|
||||||
_: [PTHREAD_COND_T_SIZE] c.char,
|
_: [PTHREAD_COND_T_SIZE] c.char,
|
||||||
};
|
};
|
||||||
pthread_mutex_t :: opaque struct #align 16 {
|
pthread_mutex_t :: #opaque struct #align 16 {
|
||||||
_: [PTHREAD_MUTEX_T_SIZE] c.char,
|
_: [PTHREAD_MUTEX_T_SIZE] c.char,
|
||||||
};
|
};
|
||||||
pthread_rwlock_t :: opaque struct #align 16 {
|
pthread_rwlock_t :: #opaque struct #align 16 {
|
||||||
_: [PTHREAD_RWLOCK_T_SIZE] c.char,
|
_: [PTHREAD_RWLOCK_T_SIZE] c.char,
|
||||||
};
|
};
|
||||||
pthread_barrier_t :: opaque struct #align 16 {
|
pthread_barrier_t :: #opaque struct #align 16 {
|
||||||
_: [PTHREAD_BARRIER_T_SIZE] c.char,
|
_: [PTHREAD_BARRIER_T_SIZE] c.char,
|
||||||
};
|
};
|
||||||
|
|
||||||
pthread_attr_t :: opaque struct #align 16 {
|
pthread_attr_t :: #opaque struct #align 16 {
|
||||||
_: [PTHREAD_ATTR_T_SIZE] c.char,
|
_: [PTHREAD_ATTR_T_SIZE] c.char,
|
||||||
};
|
};
|
||||||
pthread_condattr_t :: opaque struct #align 16 {
|
pthread_condattr_t :: #opaque struct #align 16 {
|
||||||
_: [PTHREAD_CONDATTR_T_SIZE] c.char,
|
_: [PTHREAD_CONDATTR_T_SIZE] c.char,
|
||||||
};
|
};
|
||||||
pthread_mutexattr_t :: opaque struct #align 16 {
|
pthread_mutexattr_t :: #opaque struct #align 16 {
|
||||||
_: [PTHREAD_MUTEXATTR_T_SIZE] c.char,
|
_: [PTHREAD_MUTEXATTR_T_SIZE] c.char,
|
||||||
};
|
};
|
||||||
pthread_rwlockattr_t :: opaque struct #align 16 {
|
pthread_rwlockattr_t :: #opaque struct #align 16 {
|
||||||
_: [PTHREAD_RWLOCKATTR_T_SIZE] c.char,
|
_: [PTHREAD_RWLOCKATTR_T_SIZE] c.char,
|
||||||
};
|
};
|
||||||
pthread_barrierattr_t :: opaque struct #align 16 {
|
pthread_barrierattr_t :: #opaque struct #align 16 {
|
||||||
_: [PTHREAD_BARRIERATTR_T_SIZE] c.char,
|
_: [PTHREAD_BARRIERATTR_T_SIZE] c.char,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -33,32 +33,32 @@ when size_of(int) == 8 {
|
|||||||
PTHREAD_BARRIER_T_SIZE :: 20;
|
PTHREAD_BARRIER_T_SIZE :: 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_cond_t :: opaque struct #align 16 {
|
pthread_cond_t :: #opaque struct #align 16 {
|
||||||
_: [PTHREAD_COND_T_SIZE] c.char,
|
_: [PTHREAD_COND_T_SIZE] c.char,
|
||||||
};
|
};
|
||||||
pthread_mutex_t :: opaque struct #align 16 {
|
pthread_mutex_t :: #opaque struct #align 16 {
|
||||||
_: [PTHREAD_MUTEX_T_SIZE] c.char,
|
_: [PTHREAD_MUTEX_T_SIZE] c.char,
|
||||||
};
|
};
|
||||||
pthread_rwlock_t :: opaque struct #align 16 {
|
pthread_rwlock_t :: #opaque struct #align 16 {
|
||||||
_: [PTHREAD_RWLOCK_T_SIZE] c.char,
|
_: [PTHREAD_RWLOCK_T_SIZE] c.char,
|
||||||
};
|
};
|
||||||
pthread_barrier_t :: opaque struct #align 16 {
|
pthread_barrier_t :: #opaque struct #align 16 {
|
||||||
_: [PTHREAD_BARRIER_T_SIZE] c.char,
|
_: [PTHREAD_BARRIER_T_SIZE] c.char,
|
||||||
};
|
};
|
||||||
|
|
||||||
pthread_attr_t :: opaque struct #align 16 {
|
pthread_attr_t :: #opaque struct #align 16 {
|
||||||
_: [PTHREAD_ATTR_T_SIZE] c.char,
|
_: [PTHREAD_ATTR_T_SIZE] c.char,
|
||||||
};
|
};
|
||||||
pthread_condattr_t :: opaque struct #align 16 {
|
pthread_condattr_t :: #opaque struct #align 16 {
|
||||||
_: [PTHREAD_CONDATTR_T_SIZE] c.char,
|
_: [PTHREAD_CONDATTR_T_SIZE] c.char,
|
||||||
};
|
};
|
||||||
pthread_mutexattr_t :: opaque struct #align 16 {
|
pthread_mutexattr_t :: #opaque struct #align 16 {
|
||||||
_: [PTHREAD_MUTEXATTR_T_SIZE] c.char,
|
_: [PTHREAD_MUTEXATTR_T_SIZE] c.char,
|
||||||
};
|
};
|
||||||
pthread_rwlockattr_t :: opaque struct #align 16 {
|
pthread_rwlockattr_t :: #opaque struct #align 16 {
|
||||||
_: [PTHREAD_RWLOCKATTR_T_SIZE] c.char,
|
_: [PTHREAD_RWLOCKATTR_T_SIZE] c.char,
|
||||||
};
|
};
|
||||||
pthread_barrierattr_t :: opaque struct #align 16 {
|
pthread_barrierattr_t :: #opaque struct #align 16 {
|
||||||
_: [PTHREAD_BARRIERATTR_T_SIZE] c.char,
|
_: [PTHREAD_BARRIERATTR_T_SIZE] c.char,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ foreign kernel32 {
|
|||||||
@(link_name="CreateProcessA") create_process_a :: proc(application_name, command_line: cstring,
|
@(link_name="CreateProcessA") create_process_a :: proc(application_name, command_line: cstring,
|
||||||
process_attributes, thread_attributes: ^Security_Attributes,
|
process_attributes, thread_attributes: ^Security_Attributes,
|
||||||
inherit_handle: Bool, creation_flags: u32, environment: rawptr,
|
inherit_handle: Bool, creation_flags: u32, environment: rawptr,
|
||||||
current_direcotry: cstring, startup_info: ^Startup_Info,
|
current_directory: cstring, startup_info: ^Startup_Info,
|
||||||
process_information: ^Process_Information) -> Bool ---;
|
process_information: ^Process_Information) -> Bool ---;
|
||||||
@(link_name="CreateProcessW") create_process_w :: proc(application_name, command_line: Wstring,
|
@(link_name="CreateProcessW") create_process_w :: proc(application_name, command_line: Wstring,
|
||||||
process_attributes, thread_attributes: ^Security_Attributes,
|
process_attributes, thread_attributes: ^Security_Attributes,
|
||||||
inherit_handle: Bool, creation_flags: u32, environment: rawptr,
|
inherit_handle: Bool, creation_flags: u32, environment: rawptr,
|
||||||
current_direcotry: Wstring, startup_info: ^Startup_Info,
|
current_directory: Wstring, startup_info: ^Startup_Info,
|
||||||
process_information: ^Process_Information) -> Bool ---;
|
process_information: ^Process_Information) -> Bool ---;
|
||||||
@(link_name="GetExitCodeProcess") get_exit_code_process :: proc(process: Handle, exit: ^u32) -> Bool ---;
|
@(link_name="GetExitCodeProcess") get_exit_code_process :: proc(process: Handle, exit: ^u32) -> Bool ---;
|
||||||
@(link_name="ExitProcess") exit_process :: proc(exit_code: u32) ---;
|
@(link_name="ExitProcess") exit_process :: proc(exit_code: u32) ---;
|
||||||
|
|||||||
@@ -0,0 +1,585 @@
|
|||||||
|
package text_scanner
|
||||||
|
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:strings"
|
||||||
|
import "core:unicode"
|
||||||
|
import "core:unicode/utf8"
|
||||||
|
|
||||||
|
Position :: struct {
|
||||||
|
filename: string, // filename, if present
|
||||||
|
offset: int, // byte offset, starting @ 0
|
||||||
|
line: int, // line number, starting @ 1
|
||||||
|
column: int, // column number, starting @ 1 (character count per line)
|
||||||
|
}
|
||||||
|
|
||||||
|
position_is_valid :: proc(pos: Position) -> bool {
|
||||||
|
return pos.line > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
position_to_string :: proc(pos: Position, allocator := context.temp_allocator) -> string {
|
||||||
|
s := pos.filename;
|
||||||
|
if s == "" {
|
||||||
|
s = "<input>";
|
||||||
|
}
|
||||||
|
|
||||||
|
context.allocator = allocator;
|
||||||
|
if position_is_valid(pos) {
|
||||||
|
return fmt.aprintf("%s(%d:%d)", s, pos.line, pos.column);
|
||||||
|
} else {
|
||||||
|
return strings.clone(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EOF :: -1;
|
||||||
|
Ident :: -2;
|
||||||
|
Int :: -3;
|
||||||
|
Float :: -4;
|
||||||
|
Char :: -5;
|
||||||
|
String :: -6;
|
||||||
|
Raw_String :: -7;
|
||||||
|
Comment :: -8;
|
||||||
|
|
||||||
|
Scan_Flag :: enum u32 {
|
||||||
|
Scan_Idents,
|
||||||
|
Scan_Ints,
|
||||||
|
Scan_C_Int_Prefixes,
|
||||||
|
Scan_Floats,
|
||||||
|
Scan_Chars,
|
||||||
|
Scan_Strings,
|
||||||
|
Scan_Raw_Strings,
|
||||||
|
Scan_Comments,
|
||||||
|
Skip_Comments,
|
||||||
|
}
|
||||||
|
Scan_Flags :: bit_set[Scan_Flag; u32];
|
||||||
|
|
||||||
|
Odin_Like_Tokens :: Scan_Flags{.Scan_Idents, .Scan_Ints, .Scan_Floats, .Scan_Chars, .Scan_Strings, .Scan_Raw_Strings, .Scan_Comments, .Skip_Comments};
|
||||||
|
C_Like_Tokens :: Scan_Flags{.Scan_Idents, .Scan_Ints, .Scan_C_Int_Prefixes, .Scan_Floats, .Scan_Chars, .Scan_Strings, .Scan_Raw_Strings, .Scan_Comments, .Skip_Comments};
|
||||||
|
|
||||||
|
Odin_Whitespace :: 1<<'\t' | 1<<'\n' | 1<<'\r' | 1<<' ';
|
||||||
|
C_Whitespace :: 1<<'\t' | 1<<'\n' | 1<<'\r' | 1<<'\v' | 1<<'\f' | 1<<' ';
|
||||||
|
|
||||||
|
|
||||||
|
Scanner :: struct {
|
||||||
|
src: string,
|
||||||
|
|
||||||
|
src_pos: int,
|
||||||
|
src_end: int,
|
||||||
|
|
||||||
|
tok_pos: int,
|
||||||
|
tok_end: int,
|
||||||
|
|
||||||
|
ch: rune,
|
||||||
|
|
||||||
|
line: int,
|
||||||
|
column: int,
|
||||||
|
prev_line_len: int,
|
||||||
|
prev_char_len: int,
|
||||||
|
|
||||||
|
error: proc(s: ^Scanner, msg: string),
|
||||||
|
error_count: int,
|
||||||
|
|
||||||
|
flags: Scan_Flags,
|
||||||
|
whitespace: u64,
|
||||||
|
|
||||||
|
is_ident_rune: proc(ch: rune, i: int) -> bool,
|
||||||
|
|
||||||
|
pos: Position,
|
||||||
|
}
|
||||||
|
|
||||||
|
init :: proc(s: ^Scanner, src: string, filename := "") -> ^Scanner {
|
||||||
|
s^ = {};
|
||||||
|
|
||||||
|
s.src = src;
|
||||||
|
s.pos.filename = filename;
|
||||||
|
|
||||||
|
s.tok_pos = -1;
|
||||||
|
|
||||||
|
s.ch = -2; // no char read yet, not an EOF
|
||||||
|
|
||||||
|
s.line = 1;
|
||||||
|
|
||||||
|
s.flags = Odin_Like_Tokens;
|
||||||
|
s.whitespace = Odin_Whitespace;
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
advance :: proc(s: ^Scanner) -> rune {
|
||||||
|
if s.src_pos >= len(s.src) {
|
||||||
|
s.prev_char_len = 0;
|
||||||
|
return EOF;
|
||||||
|
}
|
||||||
|
ch, width := rune(s.src[s.src_pos]), 1;
|
||||||
|
|
||||||
|
if ch >= utf8.RUNE_SELF {
|
||||||
|
ch, width = utf8.decode_rune_in_string(s.src[s.src_pos:]);
|
||||||
|
if ch == utf8.RUNE_ERROR && width == 1 {
|
||||||
|
s.src_pos += width;
|
||||||
|
s.prev_char_len = width;
|
||||||
|
s.column += 1;
|
||||||
|
error(s, "invalid UTF-8 encoding");
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.src_pos += width;
|
||||||
|
s.prev_char_len = width;
|
||||||
|
s.column += 1;
|
||||||
|
|
||||||
|
switch ch {
|
||||||
|
case 0:
|
||||||
|
error(s, "invalid character NUL");
|
||||||
|
case '\n':
|
||||||
|
s.line += 1;
|
||||||
|
s.prev_line_len = s.column;
|
||||||
|
s.column = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
next :: proc(s: ^Scanner) -> rune {
|
||||||
|
s.tok_pos = -1;
|
||||||
|
s.pos.line = 0;
|
||||||
|
ch := peek(s);
|
||||||
|
if ch != EOF {
|
||||||
|
s.ch = advance(s);
|
||||||
|
}
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
peek :: proc(s: ^Scanner) -> rune {
|
||||||
|
if s.ch == -2 {
|
||||||
|
s.ch = advance(s);
|
||||||
|
if s.ch == '\ufeff' { // Ignore BOM
|
||||||
|
s.ch = advance(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s.ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
error :: proc(s: ^Scanner, msg: string) {
|
||||||
|
s.error_count += 1;
|
||||||
|
if s.error != nil {
|
||||||
|
s.error(s, msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
p := s.pos;
|
||||||
|
if !position_is_valid(p) {
|
||||||
|
p = position(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
s := p.filename;
|
||||||
|
if s == "" {
|
||||||
|
s = "<input>";
|
||||||
|
}
|
||||||
|
|
||||||
|
if position_is_valid(p) {
|
||||||
|
fmt.eprintf("%s(%d:%d): %s\n", s, p.line, p.column, msg);
|
||||||
|
} else {
|
||||||
|
fmt.eprintf("%s: %s\n", s, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errorf :: proc(s: ^Scanner, format: string, args: ..any) {
|
||||||
|
error(s, fmt.tprintf(format, ..args));
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
is_ident_rune :: proc(s: ^Scanner, ch: rune, i: int) -> bool {
|
||||||
|
if s.is_ident_rune != nil {
|
||||||
|
return s.is_ident_rune(ch, i);
|
||||||
|
}
|
||||||
|
return ch == '_' || unicode.is_letter(ch) || unicode.is_digit(ch) && i > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
scan_identifier :: proc(s: ^Scanner) -> rune {
|
||||||
|
ch := advance(s);
|
||||||
|
for i := 1; is_ident_rune(s, ch, i); i += 1 {
|
||||||
|
ch = advance(s);
|
||||||
|
}
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private) lower :: proc(ch: rune) -> rune { return ('a' - 'A') | ch; }
|
||||||
|
@(private) is_decimal :: proc(ch: rune) -> bool { return '0' <= ch && ch <= '9'; }
|
||||||
|
@(private) is_hex :: proc(ch: rune) -> bool { return '0' <= ch && ch <= '9' || 'a' <= lower(ch) && lower(ch) <= 'f'; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
scan_number :: proc(s: ^Scanner, ch: rune, seen_dot: bool) -> (rune, rune) {
|
||||||
|
lit_name :: proc(prefix: rune) -> string {
|
||||||
|
switch prefix {
|
||||||
|
case 'b': return "binary literal";
|
||||||
|
case 'o': return "octal literal";
|
||||||
|
case 'z': return "dozenal literal";
|
||||||
|
case 'x': return "hexadecimal literal";
|
||||||
|
}
|
||||||
|
return "decimal literal";
|
||||||
|
}
|
||||||
|
|
||||||
|
digits :: proc(s: ^Scanner, ch0: rune, base: int, invalid: ^rune) -> (ch: rune, digsep: int) {
|
||||||
|
ch = ch0;
|
||||||
|
if base <= 10 {
|
||||||
|
max := rune('0' + base);
|
||||||
|
for is_decimal(ch) || ch == '_' {
|
||||||
|
ds := 1;
|
||||||
|
if ch == '_' {
|
||||||
|
ds = 2;
|
||||||
|
} else if ch >= max && invalid^ == 0 {
|
||||||
|
invalid^ = ch;
|
||||||
|
}
|
||||||
|
digsep |= ds;
|
||||||
|
ch = advance(s);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for is_hex(ch) || ch == '_' {
|
||||||
|
ds := 1;
|
||||||
|
if ch == '_' {
|
||||||
|
ds = 2;
|
||||||
|
}
|
||||||
|
digsep |= ds;
|
||||||
|
ch = advance(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ch, seen_dot := ch, seen_dot;
|
||||||
|
|
||||||
|
base := 10;
|
||||||
|
prefix := rune(0);
|
||||||
|
digsep := 0;
|
||||||
|
invalid := rune(0);
|
||||||
|
|
||||||
|
tok: rune;
|
||||||
|
ds: int;
|
||||||
|
|
||||||
|
if !seen_dot {
|
||||||
|
tok = Int;
|
||||||
|
if ch == '0' {
|
||||||
|
ch = advance(s);
|
||||||
|
|
||||||
|
p := lower(ch);
|
||||||
|
if .Scan_C_Int_Prefixes in s.flags {
|
||||||
|
switch p {
|
||||||
|
case 'b':
|
||||||
|
ch = advance(s);
|
||||||
|
base, prefix = 2, 'b';
|
||||||
|
case 'x':
|
||||||
|
ch = advance(s);
|
||||||
|
base, prefix = 16, 'x';
|
||||||
|
case:
|
||||||
|
base, prefix = 8, 'o';
|
||||||
|
digsep = 1; // Leading zero
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch p {
|
||||||
|
case 'b':
|
||||||
|
ch = advance(s);
|
||||||
|
base, prefix = 2, 'b';
|
||||||
|
case 'o':
|
||||||
|
ch = advance(s);
|
||||||
|
base, prefix = 8, 'o';
|
||||||
|
case 'd':
|
||||||
|
ch = advance(s);
|
||||||
|
base, prefix = 10, 'd';
|
||||||
|
case 'z':
|
||||||
|
ch = advance(s);
|
||||||
|
base, prefix = 12, 'z';
|
||||||
|
case 'h':
|
||||||
|
tok = Float;
|
||||||
|
fallthrough;
|
||||||
|
case 'x':
|
||||||
|
ch = advance(s);
|
||||||
|
base, prefix = 16, 'x';
|
||||||
|
case:
|
||||||
|
digsep = 1; // Leading zero
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ch, ds = digits(s, ch, base, &invalid);
|
||||||
|
digsep |= ds;
|
||||||
|
if ch == '.' && .Scan_Floats in s.flags {
|
||||||
|
ch = advance(s);
|
||||||
|
seen_dot = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if seen_dot {
|
||||||
|
tok = Float;
|
||||||
|
if prefix != 0 && prefix != 'x' {
|
||||||
|
errorf(s, "invalid radix point in %s", lit_name(prefix));
|
||||||
|
}
|
||||||
|
ch, ds = digits(s, ch, base, &invalid);
|
||||||
|
digsep |= ds;
|
||||||
|
}
|
||||||
|
|
||||||
|
if digsep&1 == 0 {
|
||||||
|
errorf(s, "%s has no digits", lit_name(prefix));
|
||||||
|
}
|
||||||
|
|
||||||
|
if e := lower(ch); (e == 'e' || e == 'p') && .Scan_Floats in s.flags {
|
||||||
|
switch {
|
||||||
|
case e == 'e' && prefix != 0:
|
||||||
|
errorf(s, "%q exponent requires decimal mantissa", ch);
|
||||||
|
case e == 'p' && prefix != 'x':
|
||||||
|
errorf(s, "%q exponent requires hexadecimal mantissa", ch);
|
||||||
|
}
|
||||||
|
ch = advance(s);
|
||||||
|
tok = Float;
|
||||||
|
if ch == '+' || ch == '-' {
|
||||||
|
ch = advance(s);
|
||||||
|
}
|
||||||
|
ch, ds = digits(s, ch, 10, nil);
|
||||||
|
digsep |= ds;
|
||||||
|
if ds&1 == 0 {
|
||||||
|
error(s, "exponent has no digits");
|
||||||
|
}
|
||||||
|
} else if prefix == 'x' && tok == Float {
|
||||||
|
error(s, "hexadecimal mantissa requires a 'p' exponent");
|
||||||
|
}
|
||||||
|
|
||||||
|
if tok == Int && invalid != 0 {
|
||||||
|
errorf(s, "invalid digit %q in %s", invalid, lit_name(prefix));
|
||||||
|
}
|
||||||
|
|
||||||
|
if digsep&2 != 0 {
|
||||||
|
s.tok_end = s.src_pos - s.prev_char_len;
|
||||||
|
}
|
||||||
|
return tok, ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
scan_string :: proc(s: ^Scanner, quote: rune) -> (n: int) {
|
||||||
|
digit_val :: proc(ch: rune) -> int {
|
||||||
|
switch v := lower(ch); v {
|
||||||
|
case '0'..'9': return int(v - '0');
|
||||||
|
case 'a'..'z': return int(v - 'a');
|
||||||
|
}
|
||||||
|
return 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
scan_digits :: proc(s: ^Scanner, ch: rune, base, n: int) -> rune {
|
||||||
|
ch, n := ch, n;
|
||||||
|
for n > 0 && digit_val(ch) < base {
|
||||||
|
ch = advance(s);
|
||||||
|
n -= 1;
|
||||||
|
}
|
||||||
|
if n > 0 {
|
||||||
|
error(s, "invalid char escape");
|
||||||
|
}
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
ch := advance(s);
|
||||||
|
for ch != quote {
|
||||||
|
if ch == '\n' || ch < 0 {
|
||||||
|
error(s, "literal no terminated");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ch == '\\' {
|
||||||
|
ch = advance(s);
|
||||||
|
switch ch {
|
||||||
|
case quote, 'a', 'b', 'e', 'f', 'n', 'r', 't', 'v', '\\':
|
||||||
|
ch = advance(s);
|
||||||
|
case '0'..'7': ch = scan_digits(s, advance(s), 8, 3);
|
||||||
|
case 'x': ch = scan_digits(s, advance(s), 16, 2);
|
||||||
|
case 'u': ch = scan_digits(s, advance(s), 16, 4);
|
||||||
|
case 'U': ch = scan_digits(s, advance(s), 16, 8);
|
||||||
|
case:
|
||||||
|
error(s, "invalid char escape");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ch = advance(s);
|
||||||
|
}
|
||||||
|
n += 1;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
scan_raw_string :: proc(s: ^Scanner) {
|
||||||
|
ch := advance(s);
|
||||||
|
for ch != '`' {
|
||||||
|
if ch < 0 {
|
||||||
|
error(s, "literal not terminated");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ch = advance(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
scan_char :: proc(s: ^Scanner) {
|
||||||
|
if scan_string(s, '\'') != 1 {
|
||||||
|
error(s, "invalid char literal");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
scan_comment :: proc(s: ^Scanner, ch: rune) -> rune {
|
||||||
|
ch := ch;
|
||||||
|
if ch == '/' { // line comment
|
||||||
|
ch = advance(s);
|
||||||
|
for ch != '\n' && ch >= 0 {
|
||||||
|
ch = advance(s);
|
||||||
|
}
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
// block /**/ comment
|
||||||
|
ch = advance(s);
|
||||||
|
for {
|
||||||
|
if ch < 0 {
|
||||||
|
error(s, "comment not terminated");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ch0 := ch;
|
||||||
|
ch = advance(s);
|
||||||
|
if ch0 == '*' && ch == '/' {
|
||||||
|
return advance(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
scan :: proc(s: ^Scanner) -> (tok: rune) {
|
||||||
|
ch := peek(s);
|
||||||
|
if ch == EOF {
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset position
|
||||||
|
s.tok_pos = -1;
|
||||||
|
s.pos.line = 0;
|
||||||
|
|
||||||
|
redo: for {
|
||||||
|
for s.whitespace & (1<<uint(ch)) != 0 {
|
||||||
|
ch = advance(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
s.tok_pos = s.src_pos - s.prev_char_len;
|
||||||
|
s.pos.offset = s.tok_pos;
|
||||||
|
|
||||||
|
if s.column > 0 {
|
||||||
|
s.pos.line = s.line;
|
||||||
|
s.pos.column = s.column;
|
||||||
|
} else {
|
||||||
|
// previous character was newline
|
||||||
|
s.pos.line = s.line - 1;
|
||||||
|
s.pos.column = s.prev_line_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
tok = ch;
|
||||||
|
if is_ident_rune(s, ch, 0) {
|
||||||
|
if .Scan_Idents in s.flags {
|
||||||
|
tok = Ident;
|
||||||
|
ch = scan_identifier(s);
|
||||||
|
} else {
|
||||||
|
ch = advance(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if is_decimal(ch) {
|
||||||
|
if s.flags >= {.Scan_Ints, .Scan_Floats} {
|
||||||
|
tok, ch = scan_number(s, ch, false);
|
||||||
|
} else {
|
||||||
|
ch = advance(s);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch ch {
|
||||||
|
case EOF:
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
if .Scan_Strings in s.flags {
|
||||||
|
scan_string(s, '"');
|
||||||
|
tok = String;
|
||||||
|
}
|
||||||
|
ch = advance(s);
|
||||||
|
case '\'':
|
||||||
|
if .Scan_Chars in s.flags {
|
||||||
|
scan_string(s, '\'');
|
||||||
|
tok = Char;
|
||||||
|
}
|
||||||
|
ch = advance(s);
|
||||||
|
case '`':
|
||||||
|
if .Scan_Raw_Strings in s.flags {
|
||||||
|
scan_raw_string(s);
|
||||||
|
tok = Raw_String;
|
||||||
|
}
|
||||||
|
ch = advance(s);
|
||||||
|
case '.':
|
||||||
|
ch = advance(s);
|
||||||
|
if is_decimal(ch) && .Scan_Floats in s.flags {
|
||||||
|
tok, ch = scan_number(s, ch, true);
|
||||||
|
}
|
||||||
|
case '/':
|
||||||
|
ch = advance(s);
|
||||||
|
if (ch == '/' || ch == '*') && .Scan_Comments in s.flags {
|
||||||
|
if .Skip_Comments in s.flags {
|
||||||
|
s.tok_pos = -1;
|
||||||
|
ch = scan_comment(s, ch);
|
||||||
|
continue redo;
|
||||||
|
}
|
||||||
|
ch = scan_comment(s, ch);
|
||||||
|
tok = Comment;
|
||||||
|
}
|
||||||
|
case:
|
||||||
|
ch = advance(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break redo;
|
||||||
|
}
|
||||||
|
|
||||||
|
s.tok_end = s.src_pos - s.prev_char_len;
|
||||||
|
|
||||||
|
s.ch = ch;
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
|
||||||
|
position :: proc(s: ^Scanner) -> Position {
|
||||||
|
pos: Position;
|
||||||
|
pos.filename = s.pos.filename;
|
||||||
|
pos.offset = s.src_pos - s.prev_char_len;
|
||||||
|
switch {
|
||||||
|
case s.column > 0:
|
||||||
|
pos.line = s.line;
|
||||||
|
pos.column = s.column;
|
||||||
|
case s.prev_line_len > 0:
|
||||||
|
pos.line = s.line-1;
|
||||||
|
pos.column = s.prev_line_len;
|
||||||
|
case:
|
||||||
|
pos.line = 1;
|
||||||
|
pos.column = 1;
|
||||||
|
}
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
token_text :: proc(s: ^Scanner) -> string {
|
||||||
|
if s.tok_pos < 0 {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return string(s.src[s.tok_pos:s.tok_end]);
|
||||||
|
}
|
||||||
|
|
||||||
|
token_string :: proc(tok: rune, allocator := context.temp_allocator) -> string {
|
||||||
|
context.allocator = allocator;
|
||||||
|
switch tok {
|
||||||
|
case EOF: return strings.clone("EOF");
|
||||||
|
case Ident: return strings.clone("Ident");
|
||||||
|
case Int: return strings.clone("Int");
|
||||||
|
case Float: return strings.clone("Float");
|
||||||
|
case Char: return strings.clone("Char");
|
||||||
|
case String: return strings.clone("String");
|
||||||
|
case Raw_String: return strings.clone("Raw_String");
|
||||||
|
case Comment: return strings.clone("Comment");
|
||||||
|
}
|
||||||
|
return fmt.aprintf("%q", tok);
|
||||||
|
}
|
||||||
+107
-7
@@ -2,17 +2,26 @@ package thread
|
|||||||
|
|
||||||
import "core:runtime"
|
import "core:runtime"
|
||||||
import "core:sync"
|
import "core:sync"
|
||||||
import "core:intrinsics"
|
import "core:mem"
|
||||||
|
import "intrinsics"
|
||||||
|
|
||||||
|
_ :: intrinsics;
|
||||||
|
|
||||||
Thread_Proc :: #type proc(^Thread);
|
Thread_Proc :: #type proc(^Thread);
|
||||||
|
|
||||||
|
MAX_USER_ARGUMENTS :: 8;
|
||||||
|
|
||||||
Thread :: struct {
|
Thread :: struct {
|
||||||
using specific: Thread_Os_Specific,
|
using specific: Thread_Os_Specific,
|
||||||
procedure: Thread_Proc,
|
procedure: Thread_Proc,
|
||||||
data: rawptr,
|
data: rawptr,
|
||||||
user_index: int,
|
user_index: int,
|
||||||
|
user_args: [MAX_USER_ARGUMENTS]rawptr,
|
||||||
|
|
||||||
init_context: Maybe(runtime.Context),
|
init_context: Maybe(runtime.Context),
|
||||||
|
|
||||||
|
|
||||||
|
creation_allocator: mem.Allocator,
|
||||||
}
|
}
|
||||||
|
|
||||||
#assert(size_of(Thread{}.user_index) == size_of(uintptr));
|
#assert(size_of(Thread{}.user_index) == size_of(uintptr));
|
||||||
@@ -34,17 +43,108 @@ run :: proc(fn: proc(), init_context: Maybe(runtime.Context) = nil, priority :=
|
|||||||
run_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) {
|
run_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) {
|
||||||
thread_proc :: proc(t: ^Thread) {
|
thread_proc :: proc(t: ^Thread) {
|
||||||
fn := cast(proc(rawptr))t.data;
|
fn := cast(proc(rawptr))t.data;
|
||||||
data := rawptr(uintptr(t.user_index));
|
assert(t.user_index >= 1);
|
||||||
|
data := t.user_args[0];
|
||||||
fn(data);
|
fn(data);
|
||||||
destroy(t);
|
destroy(t);
|
||||||
}
|
}
|
||||||
t := create(thread_proc, priority);
|
t := create(thread_proc, priority);
|
||||||
t.data = rawptr(fn);
|
t.data = rawptr(fn);
|
||||||
t.user_index = int(uintptr(data));
|
t.user_index = 1;
|
||||||
|
t.user_args = data;
|
||||||
t.init_context = init_context;
|
t.init_context = init_context;
|
||||||
start(t);
|
start(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
run_with_poly_data :: proc(data: $T, fn: proc(data: T), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
|
||||||
|
where size_of(T) <= size_of(rawptr) {
|
||||||
|
thread_proc :: proc(t: ^Thread) {
|
||||||
|
fn := cast(proc(T))t.data;
|
||||||
|
assert(t.user_index >= 1);
|
||||||
|
data := (^T)(&t.user_args[0])^;
|
||||||
|
fn(data);
|
||||||
|
destroy(t);
|
||||||
|
}
|
||||||
|
t := create(thread_proc, priority);
|
||||||
|
t.data = rawptr(fn);
|
||||||
|
t.user_index = 1;
|
||||||
|
data := data;
|
||||||
|
mem.copy(&t.user_args[0], &data, size_of(data));
|
||||||
|
t.init_context = init_context;
|
||||||
|
start(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
run_with_poly_data2 :: proc(arg1: $T1, arg2: $T2, fn: proc(T1, T2), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
|
||||||
|
where size_of(T1) <= size_of(rawptr),
|
||||||
|
size_of(T2) <= size_of(rawptr) {
|
||||||
|
thread_proc :: proc(t: ^Thread) {
|
||||||
|
fn := cast(proc(T1, T2))t.data;
|
||||||
|
assert(t.user_index >= 2);
|
||||||
|
arg1 := (^T1)(&t.user_args[0])^;
|
||||||
|
arg2 := (^T2)(&t.user_args[1])^;
|
||||||
|
fn(arg1, arg2);
|
||||||
|
destroy(t);
|
||||||
|
}
|
||||||
|
t := create(thread_proc, priority);
|
||||||
|
t.data = rawptr(fn);
|
||||||
|
t.user_index = 2;
|
||||||
|
arg1, arg2 := arg1, arg2;
|
||||||
|
mem.copy(&t.user_args[0], &arg1, size_of(arg1));
|
||||||
|
mem.copy(&t.user_args[1], &arg2, size_of(arg2));
|
||||||
|
t.init_context = init_context;
|
||||||
|
start(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
run_with_poly_data3 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, fn: proc(arg1: T1, arg2: T2, arg3: T3), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
|
||||||
|
where size_of(T1) <= size_of(rawptr),
|
||||||
|
size_of(T2) <= size_of(rawptr),
|
||||||
|
size_of(T3) <= size_of(rawptr) {
|
||||||
|
thread_proc :: proc(t: ^Thread) {
|
||||||
|
fn := cast(proc(T1, T2, T3))t.data;
|
||||||
|
assert(t.user_index >= 3);
|
||||||
|
arg1 := (^T1)(&t.user_args[0])^;
|
||||||
|
arg2 := (^T2)(&t.user_args[1])^;
|
||||||
|
arg3 := (^T3)(&t.user_args[2])^;
|
||||||
|
fn(arg1, arg2, arg3);
|
||||||
|
destroy(t);
|
||||||
|
}
|
||||||
|
t := create(thread_proc, priority);
|
||||||
|
t.data = rawptr(fn);
|
||||||
|
t.user_index = 3;
|
||||||
|
arg1, arg2, arg3 := arg1, arg2, arg3;
|
||||||
|
mem.copy(&t.user_args[0], &arg1, size_of(arg1));
|
||||||
|
mem.copy(&t.user_args[1], &arg2, size_of(arg2));
|
||||||
|
mem.copy(&t.user_args[2], &arg3, size_of(arg3));
|
||||||
|
t.init_context = init_context;
|
||||||
|
start(t);
|
||||||
|
}
|
||||||
|
run_with_poly_data4 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, arg4: $T4, fn: proc(arg1: T1, arg2: T2, arg3: T3, arg4: T4), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
|
||||||
|
where size_of(T1) <= size_of(rawptr),
|
||||||
|
size_of(T2) <= size_of(rawptr),
|
||||||
|
size_of(T3) <= size_of(rawptr) {
|
||||||
|
thread_proc :: proc(t: ^Thread) {
|
||||||
|
fn := cast(proc(T1, T2, T3, T4))t.data;
|
||||||
|
assert(t.user_index >= 4);
|
||||||
|
arg1 := (^T1)(&t.user_args[0])^;
|
||||||
|
arg2 := (^T2)(&t.user_args[1])^;
|
||||||
|
arg3 := (^T3)(&t.user_args[2])^;
|
||||||
|
arg4 := (^T4)(&t.user_args[3])^;
|
||||||
|
fn(arg1, arg2, arg3, arg4);
|
||||||
|
destroy(t);
|
||||||
|
}
|
||||||
|
t := create(thread_proc, priority);
|
||||||
|
t.data = rawptr(fn);
|
||||||
|
t.user_index = 4;
|
||||||
|
arg1, arg2, arg3, arg4 := arg1, arg2, arg3, arg4;
|
||||||
|
mem.copy(&t.user_args[0], &arg1, size_of(arg1));
|
||||||
|
mem.copy(&t.user_args[1], &arg2, size_of(arg2));
|
||||||
|
mem.copy(&t.user_args[2], &arg3, size_of(arg3));
|
||||||
|
mem.copy(&t.user_args[3], &arg4, size_of(arg4));
|
||||||
|
t.init_context = init_context;
|
||||||
|
start(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
create_and_start :: proc(fn: Thread_Proc, init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) -> ^Thread {
|
create_and_start :: proc(fn: Thread_Proc, init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) -> ^Thread {
|
||||||
t := create(fn, priority);
|
t := create(fn, priority);
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^T
|
|||||||
if thread == nil {
|
if thread == nil {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
thread.creation_allocator = context.allocator;
|
||||||
|
|
||||||
// Set thread priority.
|
// Set thread priority.
|
||||||
policy: i32;
|
policy: i32;
|
||||||
@@ -106,7 +107,7 @@ create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^T
|
|||||||
sync.mutex_init(&thread.start_mutex);
|
sync.mutex_init(&thread.start_mutex);
|
||||||
sync.condition_init(&thread.start_gate, &thread.start_mutex);
|
sync.condition_init(&thread.start_gate, &thread.start_mutex);
|
||||||
if unix.pthread_create(&thread.unix_thread, &attrs, __linux_thread_entry_proc, thread) != 0 {
|
if unix.pthread_create(&thread.unix_thread, &attrs, __linux_thread_entry_proc, thread) != 0 {
|
||||||
free(thread);
|
free(thread, thread.creation_allocator);
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
thread.procedure = procedure;
|
thread.procedure = procedure;
|
||||||
@@ -172,7 +173,7 @@ join_multiple :: proc(threads: ..^Thread) {
|
|||||||
destroy :: proc(t: ^Thread) {
|
destroy :: proc(t: ^Thread) {
|
||||||
join(t);
|
join(t);
|
||||||
t.unix_thread = {};
|
t.unix_thread = {};
|
||||||
free(t);
|
free(t, t.creation_allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -49,10 +49,14 @@ create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^T
|
|||||||
|
|
||||||
|
|
||||||
thread := new(Thread);
|
thread := new(Thread);
|
||||||
|
if thread == nil {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
thread.creation_allocator = context.allocator;
|
||||||
|
|
||||||
win32_thread := win32.CreateThread(nil, 0, __windows_thread_entry_proc, thread, win32.CREATE_SUSPENDED, &win32_thread_id);
|
win32_thread := win32.CreateThread(nil, 0, __windows_thread_entry_proc, thread, win32.CREATE_SUSPENDED, &win32_thread_id);
|
||||||
if win32_thread == nil {
|
if win32_thread == nil {
|
||||||
free(thread);
|
free(thread, thread.creation_allocator);
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
thread.procedure = procedure;
|
thread.procedure = procedure;
|
||||||
@@ -111,7 +115,7 @@ join_multiple :: proc(threads: ..^Thread) {
|
|||||||
|
|
||||||
destroy :: proc(thread: ^Thread) {
|
destroy :: proc(thread: ^Thread) {
|
||||||
join(thread);
|
join(thread);
|
||||||
free(thread);
|
free(thread, thread.creation_allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
terminate :: proc(using thread : ^Thread, exit_code: u32) {
|
terminate :: proc(using thread : ^Thread, exit_code: u32) {
|
||||||
|
|||||||
+42
-996
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -350,3 +350,44 @@ rune_size :: proc(r: rune) -> int {
|
|||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// full_rune reports if the bytes in b begin with a full utf-8 encoding of a rune or not
|
||||||
|
// An invalid encoding is considered a full rune since it will convert as an error rune of width 1 (RUNE_ERROR)
|
||||||
|
full_rune :: proc(b: []byte) -> bool {
|
||||||
|
n := len(b);
|
||||||
|
if n == 0 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
x := _first[b[0]];
|
||||||
|
if n >= int(x & 7) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
accept := accept_ranges[x>>4];
|
||||||
|
if n > 1 && (b[1] < accept.lo || accept.hi < b[1]) {
|
||||||
|
return true;
|
||||||
|
} else if n > 2 && (b[2] < LOCB || HICB < b[2]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// full_rune_in_string reports if the bytes in s begin with a full utf-8 encoding of a rune or not
|
||||||
|
// An invalid encoding is considered a full rune since it will convert as an error rune of width 1 (RUNE_ERROR)
|
||||||
|
full_rune_in_string :: proc(s: string) -> bool {
|
||||||
|
return full_rune(transmute([]byte)s);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_first := [256]u8{
|
||||||
|
0x00..0x7f = 0xf0, // ascii, size 1
|
||||||
|
0x80..0xc1 = 0xf1, // invalid, size 1
|
||||||
|
0xc2..0xdf = 0x02, // accept 1, size 2
|
||||||
|
0xe0 = 0x13, // accept 1, size 3
|
||||||
|
0xe1..0xec = 0x03, // accept 0, size 3
|
||||||
|
0xed = 0x23, // accept 2, size 3
|
||||||
|
0xee..0xef = 0x03, // accept 0, size 3
|
||||||
|
0xf0 = 0x34, // accept 3, size 4
|
||||||
|
0xf1..0xf3 = 0x04, // accept 0, size 4
|
||||||
|
0xf4 = 0x44, // accept 4, size 4
|
||||||
|
0xf5..0xff = 0xf1, // ascii, size 1
|
||||||
|
};
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ the_basics :: proc() {
|
|||||||
// Binary literals are prefixed with 0b, octal literals with 0o, and hexadecimal
|
// Binary literals are prefixed with 0b, octal literals with 0o, and hexadecimal
|
||||||
// literals 0x. A leading zero does not produce an octal constant (unlike C).
|
// literals 0x. A leading zero does not produce an octal constant (unlike C).
|
||||||
|
|
||||||
// In Odin, if a number constant is possible to be represented by a type without
|
// In Odin, if a numeric constant can be represented by a type without
|
||||||
// precision loss, it will automatically convert to that type.
|
// precision loss, it will automatically convert to that type.
|
||||||
|
|
||||||
x: int = 1.0; // A float literal but it can be represented by an integer without precision loss
|
x: int = 1.0; // A float literal but it can be represented by an integer without precision loss
|
||||||
@@ -85,7 +85,7 @@ the_basics :: proc() {
|
|||||||
y = 1; // `1` is an untyped integer literal which can implicitly convert to `int`
|
y = 1; // `1` is an untyped integer literal which can implicitly convert to `int`
|
||||||
|
|
||||||
z: f64; // `z` is typed of type `f64` (64-bit floating point number)
|
z: f64; // `z` is typed of type `f64` (64-bit floating point number)
|
||||||
z = 1; // `1` is an untyped integer literals which can be implicity conver to `f64`
|
z = 1; // `1` is an untyped integer literal which can be implicitly converted to `f64`
|
||||||
// No need for any suffixes or decimal places like in other languages
|
// No need for any suffixes or decimal places like in other languages
|
||||||
// CONSTANTS JUST WORK!!!
|
// CONSTANTS JUST WORK!!!
|
||||||
|
|
||||||
@@ -150,7 +150,7 @@ control_flow :: proc() {
|
|||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the condition is omitted, this produces an infinite loop:
|
// If the condition is omitted, an infinite loop is produced:
|
||||||
for {
|
for {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
+87
-2
@@ -43,11 +43,96 @@ template <typename T> void array_set_capacity (Array<T> *array, isize capac
|
|||||||
template <typename T> Array<T> array_slice (Array<T> const &array, isize lo, isize hi);
|
template <typename T> Array<T> array_slice (Array<T> const &array, isize lo, isize hi);
|
||||||
template <typename T> Array<T> array_clone (gbAllocator const &a, Array<T> const &array);
|
template <typename T> Array<T> array_clone (gbAllocator const &a, Array<T> const &array);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
template <typename T> void array_ordered_remove (Array<T> *array, isize index);
|
template <typename T> void array_ordered_remove (Array<T> *array, isize index);
|
||||||
template <typename T> void array_unordered_remove(Array<T> *array, isize index);
|
template <typename T> void array_unordered_remove(Array<T> *array, isize index);
|
||||||
|
|
||||||
|
template <typename T> void array_copy(Array<T> *array, Array<T> const &data, isize offset);
|
||||||
|
template <typename T> void array_copy(Array<T> *array, Array<T> const &data, isize offset, isize count);
|
||||||
|
|
||||||
|
template <typename T> T *array_end_ptr(Array<T> *array);
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct Slice {
|
||||||
|
T *data;
|
||||||
|
isize count;
|
||||||
|
|
||||||
|
T &operator[](isize index) {
|
||||||
|
#if !defined(NO_ARRAY_BOUNDS_CHECK)
|
||||||
|
GB_ASSERT_MSG(0 <= index && index < count, "Index %td is out of bounds ranges 0..<%td", index, count);
|
||||||
|
#endif
|
||||||
|
return data[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
T const &operator[](isize index) const {
|
||||||
|
#if !defined(NO_ARRAY_BOUNDS_CHECK)
|
||||||
|
GB_ASSERT_MSG(0 <= index && index < count, "Index %td is out of bounds ranges 0..<%td", index, count);
|
||||||
|
#endif
|
||||||
|
return data[index];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> Slice<T> slice_from_array(Array<T> const &a);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Slice<T> slice_make(gbAllocator const &allocator, isize count) {
|
||||||
|
Slice<T> s = {};
|
||||||
|
s.data = gb_alloc_array(allocator, T, count);
|
||||||
|
s.count = count;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Slice<T> slice_from_array(Array<T> const &a) {
|
||||||
|
return {a.data, a.count};
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
Slice<T> slice_clone(gbAllocator const &allocator, Slice<T> const &a) {
|
||||||
|
T *data = cast(T *)gb_alloc_copy_align(allocator, a.data, a.count*gb_size_of(T), gb_align_of(T));
|
||||||
|
return {data, a.count};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Slice<T> slice_clone_from_array(gbAllocator const &allocator, Array<T> const &a) {
|
||||||
|
auto c = array_clone(allocator, a);
|
||||||
|
return {c.data, c.count};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void slice_copy(Slice<T> *slice, Slice<T> const &data, isize offset) {
|
||||||
|
gb_memmove(slice->data+offset, data.data, gb_size_of(T)*data.count);
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
void slice_copy(Slice<T> *slice, Slice<T> const &data, isize offset, isize count) {
|
||||||
|
gb_memmove(slice->data+offset, data.data, gb_size_of(T)*gb_min(data.count, count));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void slice_ordered_remove(Slice<T> *array, isize index) {
|
||||||
|
GB_ASSERT(0 <= index && index < array->count);
|
||||||
|
|
||||||
|
isize bytes = gb_size_of(T) * (array->count-(index+1));
|
||||||
|
gb_memmove(array->data+index, array->data+index+1, bytes);
|
||||||
|
array->count -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void slice_unordered_remove(Slice<T> *array, isize index) {
|
||||||
|
GB_ASSERT(0 <= index && index < array->count);
|
||||||
|
|
||||||
|
isize n = array->count-1;
|
||||||
|
if (index != n) {
|
||||||
|
gb_memmove(array->data+index, array->data+n, gb_size_of(T));
|
||||||
|
}
|
||||||
|
array->count -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void array_copy(Array<T> *array, Array<T> const &data, isize offset) {
|
void array_copy(Array<T> *array, Array<T> const &data, isize offset) {
|
||||||
|
|||||||
@@ -104,6 +104,37 @@ enum BuildModeKind {
|
|||||||
BuildMode_Assembly,
|
BuildMode_Assembly,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum CommandKind : u32 {
|
||||||
|
Command_run = 1<<0,
|
||||||
|
Command_build = 1<<1,
|
||||||
|
Command_check = 1<<3,
|
||||||
|
Command_query = 1<<4,
|
||||||
|
Command_doc = 1<<5,
|
||||||
|
Command_version = 1<<6,
|
||||||
|
Command_test = 1<<7,
|
||||||
|
|
||||||
|
Command__does_check = Command_run|Command_build|Command_check|Command_query|Command_doc|Command_test,
|
||||||
|
Command__does_build = Command_run|Command_build|Command_test,
|
||||||
|
Command_all = ~(u32)0,
|
||||||
|
};
|
||||||
|
|
||||||
|
char const *odin_command_strings[32] = {
|
||||||
|
"run",
|
||||||
|
"build",
|
||||||
|
"check",
|
||||||
|
"query",
|
||||||
|
"doc",
|
||||||
|
"version",
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
enum CmdDocFlag : u32 {
|
||||||
|
CmdDocFlag_Short = 1<<0,
|
||||||
|
CmdDocFlag_AllPackages = 1<<1,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// This stores the information for the specify architecture of this build
|
// This stores the information for the specify architecture of this build
|
||||||
struct BuildContext {
|
struct BuildContext {
|
||||||
@@ -124,6 +155,7 @@ struct BuildContext {
|
|||||||
i64 word_size; // Size of a pointer, must be >= 4
|
i64 word_size; // Size of a pointer, must be >= 4
|
||||||
i64 max_align; // max alignment, must be >= 1 (and typically >= word_size)
|
i64 max_align; // max alignment, must be >= 1 (and typically >= word_size)
|
||||||
|
|
||||||
|
CommandKind command_kind;
|
||||||
String command;
|
String command;
|
||||||
|
|
||||||
TargetMetrics metrics;
|
TargetMetrics metrics;
|
||||||
@@ -143,6 +175,8 @@ struct BuildContext {
|
|||||||
bool generate_docs;
|
bool generate_docs;
|
||||||
i32 optimization_level;
|
i32 optimization_level;
|
||||||
bool show_timings;
|
bool show_timings;
|
||||||
|
bool show_unused;
|
||||||
|
bool show_unused_with_location;
|
||||||
bool show_more_timings;
|
bool show_more_timings;
|
||||||
bool show_system_calls;
|
bool show_system_calls;
|
||||||
bool keep_temp_files;
|
bool keep_temp_files;
|
||||||
@@ -151,6 +185,7 @@ struct BuildContext {
|
|||||||
bool no_dynamic_literals;
|
bool no_dynamic_literals;
|
||||||
bool no_output_files;
|
bool no_output_files;
|
||||||
bool no_crt;
|
bool no_crt;
|
||||||
|
bool no_entry_point;
|
||||||
bool use_lld;
|
bool use_lld;
|
||||||
bool vet;
|
bool vet;
|
||||||
bool cross_compiling;
|
bool cross_compiling;
|
||||||
@@ -165,6 +200,9 @@ struct BuildContext {
|
|||||||
bool ignore_microsoft_magic;
|
bool ignore_microsoft_magic;
|
||||||
bool linker_map_file;
|
bool linker_map_file;
|
||||||
|
|
||||||
|
u32 cmd_doc_flags;
|
||||||
|
Array<String> extra_packages;
|
||||||
|
|
||||||
QueryDataSetSettings query_data_set_settings;
|
QueryDataSetSettings query_data_set_settings;
|
||||||
|
|
||||||
gbAffinity affinity;
|
gbAffinity affinity;
|
||||||
@@ -297,6 +335,19 @@ bool is_excluded_target_filename(String name) {
|
|||||||
String original_name = name;
|
String original_name = name;
|
||||||
name = remove_extension_from_path(name);
|
name = remove_extension_from_path(name);
|
||||||
|
|
||||||
|
if (string_starts_with(name, str_lit("."))) {
|
||||||
|
// Ignore .*.odin files
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
String test_suffix = str_lit("_test");
|
||||||
|
if (build_context.command_kind != Command_test) {
|
||||||
|
if (string_ends_with(name, test_suffix) && name != test_suffix) {
|
||||||
|
// Ignore *_test.odin files
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String str1 = {};
|
String str1 = {};
|
||||||
String str2 = {};
|
String str2 = {};
|
||||||
isize n = 0;
|
isize n = 0;
|
||||||
|
|||||||
+5
-8
@@ -113,7 +113,7 @@ Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *operand, Stri
|
|||||||
return e->type;
|
return e->type;
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_init_variables(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array<Ast *> const &inits, String context_name) {
|
void check_init_variables(CheckerContext *ctx, Entity **lhs, isize lhs_count, Slice<Ast *> const &inits, String context_name) {
|
||||||
if ((lhs == nullptr || lhs_count == 0) && inits.count == 0) {
|
if ((lhs == nullptr || lhs_count == 0) && inits.count == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -121,8 +121,7 @@ void check_init_variables(CheckerContext *ctx, Entity **lhs, isize lhs_count, Ar
|
|||||||
|
|
||||||
// NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be
|
// NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be
|
||||||
// an extra allocation
|
// an extra allocation
|
||||||
auto operands = array_make<Operand>(ctx->allocator, 0, 2*lhs_count);
|
auto operands = array_make<Operand>(temporary_allocator(), 0, 2*lhs_count);
|
||||||
defer (array_free(&operands));
|
|
||||||
check_unpack_arguments(ctx, lhs, lhs_count, &operands, inits, true, false);
|
check_unpack_arguments(ctx, lhs, lhs_count, &operands, inits, true, false);
|
||||||
|
|
||||||
isize rhs_count = operands.count;
|
isize rhs_count = operands.count;
|
||||||
@@ -317,7 +316,6 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def)
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
error(e->token, "Only struct types can have custom atom operations");
|
error(e->token, "Only struct types can have custom atom operations");
|
||||||
gb_free(heap_allocator(), ac.atom_op_table);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -638,7 +636,7 @@ String handle_link_name(CheckerContext *ctx, Token token, String link_name, Stri
|
|||||||
error(token, "'link_name' and 'link_prefix' cannot be used together");
|
error(token, "'link_name' and 'link_prefix' cannot be used together");
|
||||||
} else {
|
} else {
|
||||||
isize len = link_prefix.len + token.string.len;
|
isize len = link_prefix.len + token.string.len;
|
||||||
u8 *name = gb_alloc_array(ctx->allocator, u8, len+1);
|
u8 *name = gb_alloc_array(permanent_allocator(), u8, len+1);
|
||||||
gb_memmove(name, &link_prefix[0], link_prefix.len);
|
gb_memmove(name, &link_prefix[0], link_prefix.len);
|
||||||
gb_memmove(name+link_prefix.len, &token.string[0], token.string.len);
|
gb_memmove(name+link_prefix.len, &token.string[0], token.string.len);
|
||||||
name[len] = 0;
|
name[len] = 0;
|
||||||
@@ -975,7 +973,7 @@ void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d)
|
|||||||
|
|
||||||
ast_node(pg, ProcGroup, d->init_expr);
|
ast_node(pg, ProcGroup, d->init_expr);
|
||||||
|
|
||||||
pge->entities = array_make<Entity*>(ctx->allocator, 0, pg->args.count);
|
pge->entities = array_make<Entity*>(permanent_allocator(), 0, pg->args.count);
|
||||||
|
|
||||||
// NOTE(bill): This must be set here to prevent cycles in checking if someone
|
// NOTE(bill): This must be set here to prevent cycles in checking if someone
|
||||||
// places the entity within itself
|
// places the entity within itself
|
||||||
@@ -1009,11 +1007,10 @@ void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ptr_set_exists(&entity_set, e)) {
|
if (ptr_set_update(&entity_set, e)) {
|
||||||
error(arg, "Previous use of `%.*s` in procedure group", LIT(e->token.string));
|
error(arg, "Previous use of `%.*s` in procedure group", LIT(e->token.string));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ptr_set_add(&entity_set, e);
|
|
||||||
array_add(&pge->entities, e);
|
array_add(&pge->entities, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+349
-150
File diff suppressed because it is too large
Load Diff
+14
-21
@@ -15,7 +15,7 @@ bool is_divigering_stmt(Ast *stmt) {
|
|||||||
return t->kind == Type_Proc && t->Proc.diverging;
|
return t->kind == Type_Proc && t->Proc.diverging;
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_stmt_list(CheckerContext *ctx, Array<Ast *> const &stmts, u32 flags) {
|
void check_stmt_list(CheckerContext *ctx, Slice<Ast *> const &stmts, u32 flags) {
|
||||||
if (stmts.count == 0) {
|
if (stmts.count == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -78,7 +78,7 @@ void check_stmt_list(CheckerContext *ctx, Array<Ast *> const &stmts, u32 flags)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool check_is_terminating_list(Array<Ast *> const &stmts, String const &label) {
|
bool check_is_terminating_list(Slice<Ast *> const &stmts, String const &label) {
|
||||||
// Iterate backwards
|
// Iterate backwards
|
||||||
for (isize n = stmts.count-1; n >= 0; n--) {
|
for (isize n = stmts.count-1; n >= 0; n--) {
|
||||||
Ast *stmt = stmts[n];
|
Ast *stmt = stmts[n];
|
||||||
@@ -96,7 +96,7 @@ bool check_is_terminating_list(Array<Ast *> const &stmts, String const &label) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool check_has_break_list(Array<Ast *> const &stmts, String const &label, bool implicit) {
|
bool check_has_break_list(Slice<Ast *> const &stmts, String const &label, bool implicit) {
|
||||||
for_array(i, stmts) {
|
for_array(i, stmts) {
|
||||||
Ast *stmt = stmts[i];
|
Ast *stmt = stmts[i];
|
||||||
if (check_has_break(stmt, label, implicit)) {
|
if (check_has_break(stmt, label, implicit)) {
|
||||||
@@ -641,8 +641,7 @@ void add_constant_switch_case(CheckerContext *ctx, Map<TypeAndToken> *seen, Oper
|
|||||||
TypeAndToken *found = map_get(seen, key);
|
TypeAndToken *found = map_get(seen, key);
|
||||||
if (found != nullptr) {
|
if (found != nullptr) {
|
||||||
isize count = multi_map_count(seen, key);
|
isize count = multi_map_count(seen, key);
|
||||||
TypeAndToken *taps = gb_alloc_array(ctx->allocator, TypeAndToken, count);
|
TypeAndToken *taps = gb_alloc_array(temporary_allocator(), TypeAndToken, count);
|
||||||
defer (gb_free(ctx->allocator, taps));
|
|
||||||
|
|
||||||
multi_map_get_all(seen, key, taps);
|
multi_map_get_all(seen, key, taps);
|
||||||
for (isize i = 0; i < count; i++) {
|
for (isize i = 0; i < count; i++) {
|
||||||
@@ -859,8 +858,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
|
|||||||
token.pos = ast_token(ss->body).pos;
|
token.pos = ast_token(ss->body).pos;
|
||||||
token.string = str_lit("true");
|
token.string = str_lit("true");
|
||||||
|
|
||||||
x.expr = gb_alloc_item(ctx->allocator, Ast);
|
x.expr = alloc_ast_node(nullptr, Ast_Ident);
|
||||||
x.expr->kind = Ast_Ident;
|
|
||||||
x.expr->Ident.token = token;
|
x.expr->Ident.token = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1025,8 +1023,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
|
|||||||
GB_ASSERT(is_type_enum(et));
|
GB_ASSERT(is_type_enum(et));
|
||||||
auto fields = et->Enum.fields;
|
auto fields = et->Enum.fields;
|
||||||
|
|
||||||
auto unhandled = array_make<Entity *>(ctx->allocator, 0, fields.count);
|
auto unhandled = array_make<Entity *>(temporary_allocator(), 0, fields.count);
|
||||||
defer (array_free(&unhandled));
|
|
||||||
|
|
||||||
for_array(i, fields) {
|
for_array(i, fields) {
|
||||||
Entity *f = fields[i];
|
Entity *f = fields[i];
|
||||||
@@ -1265,8 +1262,7 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
|
|||||||
GB_ASSERT(is_type_union(ut));
|
GB_ASSERT(is_type_union(ut));
|
||||||
auto variants = ut->Union.variants;
|
auto variants = ut->Union.variants;
|
||||||
|
|
||||||
auto unhandled = array_make<Type *>(ctx->allocator, 0, variants.count);
|
auto unhandled = array_make<Type *>(temporary_allocator(), 0, variants.count);
|
||||||
defer (array_free(&unhandled));
|
|
||||||
|
|
||||||
for_array(i, variants) {
|
for_array(i, variants) {
|
||||||
Type *t = variants[i];
|
Type *t = variants[i];
|
||||||
@@ -1433,12 +1429,11 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be
|
// NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be
|
||||||
// an extra allocation
|
// an extra allocation
|
||||||
auto lhs_operands = array_make<Operand>(ctx->allocator, lhs_count);
|
auto lhs_operands = array_make<Operand>(temporary_allocator(), lhs_count);
|
||||||
auto rhs_operands = array_make<Operand>(ctx->allocator, 0, 2*lhs_count);
|
auto rhs_operands = array_make<Operand>(temporary_allocator(), 0, 2*lhs_count);
|
||||||
defer (array_free(&lhs_operands));
|
|
||||||
defer (array_free(&rhs_operands));
|
|
||||||
|
|
||||||
for_array(i, as->lhs) {
|
for_array(i, as->lhs) {
|
||||||
if (is_blank_ident(as->lhs[i])) {
|
if (is_blank_ident(as->lhs[i])) {
|
||||||
@@ -1462,8 +1457,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto lhs_to_ignore = array_make<bool>(ctx->allocator, lhs_count);
|
auto lhs_to_ignore = array_make<bool>(temporary_allocator(), lhs_count);
|
||||||
defer (array_free(&lhs_to_ignore));
|
|
||||||
|
|
||||||
isize max = gb_min(lhs_count, rhs_count);
|
isize max = gb_min(lhs_count, rhs_count);
|
||||||
// NOTE(bill, 2020-05-02): This is an utter hack to get these custom atom operations working
|
// NOTE(bill, 2020-05-02): This is an utter hack to get these custom atom operations working
|
||||||
@@ -1642,8 +1636,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
|
|||||||
} else if (operands.count != result_count) {
|
} else if (operands.count != result_count) {
|
||||||
error(node, "Expected %td return values, got %td", result_count, operands.count);
|
error(node, "Expected %td return values, got %td", result_count, operands.count);
|
||||||
} else {
|
} else {
|
||||||
isize max_count = rs->results.count;
|
for (isize i = 0; i < result_count; i++) {
|
||||||
for (isize i = 0; i < max_count; i++) {
|
|
||||||
Entity *e = pt->results->Tuple.variables[i];
|
Entity *e = pt->results->Tuple.variables[i];
|
||||||
check_assignment(ctx, &operands[i], e->type, str_lit("return statement"));
|
check_assignment(ctx, &operands[i], e->type, str_lit("return statement"));
|
||||||
}
|
}
|
||||||
@@ -1878,7 +1871,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
|
|||||||
DeclInfo *d = decl_info_of_entity(e);
|
DeclInfo *d = decl_info_of_entity(e);
|
||||||
GB_ASSERT(d == nullptr);
|
GB_ASSERT(d == nullptr);
|
||||||
add_entity(ctx->checker, ctx->scope, e->identifier, e);
|
add_entity(ctx->checker, ctx->scope, e->identifier, e);
|
||||||
d = make_decl_info(ctx->allocator, ctx->scope, ctx->decl);
|
d = make_decl_info(ctx->scope, ctx->decl);
|
||||||
add_entity_and_decl_info(ctx, e->identifier, e, d);
|
add_entity_and_decl_info(ctx, e->identifier, e, d);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2036,7 +2029,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
|
|||||||
|
|
||||||
case_ast_node(vd, ValueDecl, node);
|
case_ast_node(vd, ValueDecl, node);
|
||||||
if (vd->is_mutable) {
|
if (vd->is_mutable) {
|
||||||
Entity **entities = gb_alloc_array(ctx->allocator, Entity *, vd->names.count);
|
Entity **entities = gb_alloc_array(permanent_allocator(), Entity *, vd->names.count);
|
||||||
isize entity_count = 0;
|
isize entity_count = 0;
|
||||||
|
|
||||||
isize new_name_count = 0;
|
isize new_name_count = 0;
|
||||||
|
|||||||
+198
-86
@@ -1,3 +1,4 @@
|
|||||||
|
ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type **out_type_, Ast *expr, bool allow_caller_location);
|
||||||
|
|
||||||
void populate_using_array_index(CheckerContext *ctx, Ast *node, AstField *field, Type *t, String name, i32 idx) {
|
void populate_using_array_index(CheckerContext *ctx, Ast *node, AstField *field, Type *t, String name, i32 idx) {
|
||||||
t = base_type(t);
|
t = base_type(t);
|
||||||
@@ -116,7 +117,7 @@ bool does_field_type_allow_using(Type *t) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_struct_fields(CheckerContext *ctx, Ast *node, Array<Entity *> *fields, Array<String> *tags, Array<Ast *> const ¶ms,
|
void check_struct_fields(CheckerContext *ctx, Ast *node, Array<Entity *> *fields, Array<String> *tags, Slice<Ast *> const ¶ms,
|
||||||
isize init_field_capacity, Type *struct_type, String context) {
|
isize init_field_capacity, Type *struct_type, String context) {
|
||||||
*fields = array_make<Entity *>(heap_allocator(), 0, init_field_capacity);
|
*fields = array_make<Entity *>(heap_allocator(), 0, init_field_capacity);
|
||||||
*tags = array_make<String>(heap_allocator(), 0, init_field_capacity);
|
*tags = array_make<String>(heap_allocator(), 0, init_field_capacity);
|
||||||
@@ -388,7 +389,7 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<
|
|||||||
|
|
||||||
if (st->polymorphic_params != nullptr) {
|
if (st->polymorphic_params != nullptr) {
|
||||||
ast_node(field_list, FieldList, st->polymorphic_params);
|
ast_node(field_list, FieldList, st->polymorphic_params);
|
||||||
Array<Ast *> params = field_list->list;
|
Slice<Ast *> params = field_list->list;
|
||||||
if (params.count != 0) {
|
if (params.count != 0) {
|
||||||
isize variable_count = 0;
|
isize variable_count = 0;
|
||||||
for_array(i, params) {
|
for_array(i, params) {
|
||||||
@@ -399,7 +400,7 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto entities = array_make<Entity *>(ctx->allocator, 0, variable_count);
|
auto entities = array_make<Entity *>(permanent_allocator(), 0, variable_count);
|
||||||
|
|
||||||
for_array(i, params) {
|
for_array(i, params) {
|
||||||
Ast *param = params[i];
|
Ast *param = params[i];
|
||||||
@@ -408,32 +409,50 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<
|
|||||||
}
|
}
|
||||||
ast_node(p, Field, param);
|
ast_node(p, Field, param);
|
||||||
Ast *type_expr = p->type;
|
Ast *type_expr = p->type;
|
||||||
|
Ast *default_value = unparen_expr(p->default_value);
|
||||||
Type *type = nullptr;
|
Type *type = nullptr;
|
||||||
bool is_type_param = false;
|
bool is_type_param = false;
|
||||||
bool is_type_polymorphic_type = false;
|
bool is_type_polymorphic_type = false;
|
||||||
if (type_expr == nullptr) {
|
if (type_expr == nullptr && default_value == nullptr) {
|
||||||
error(param, "Expected a type for this parameter");
|
error(param, "Expected a type for this parameter");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (type_expr->kind == Ast_Ellipsis) {
|
|
||||||
type_expr = type_expr->Ellipsis.expr;
|
if (type_expr != nullptr) {
|
||||||
error(param, "A polymorphic parameter cannot be variadic");
|
if (type_expr->kind == Ast_Ellipsis) {
|
||||||
}
|
type_expr = type_expr->Ellipsis.expr;
|
||||||
if (type_expr->kind == Ast_TypeidType) {
|
error(param, "A polymorphic parameter cannot be variadic");
|
||||||
is_type_param = true;
|
|
||||||
Type *specialization = nullptr;
|
|
||||||
if (type_expr->TypeidType.specialization != nullptr) {
|
|
||||||
Ast *s = type_expr->TypeidType.specialization;
|
|
||||||
specialization = check_type(ctx, s);
|
|
||||||
}
|
}
|
||||||
type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization);
|
if (type_expr->kind == Ast_TypeidType) {
|
||||||
} else {
|
is_type_param = true;
|
||||||
type = check_type(ctx, type_expr);
|
Type *specialization = nullptr;
|
||||||
if (is_type_polymorphic(type)) {
|
if (type_expr->TypeidType.specialization != nullptr) {
|
||||||
is_type_polymorphic_type = true;
|
Ast *s = type_expr->TypeidType.specialization;
|
||||||
|
specialization = check_type(ctx, s);
|
||||||
|
}
|
||||||
|
type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization);
|
||||||
|
} else {
|
||||||
|
type = check_type(ctx, type_expr);
|
||||||
|
if (is_type_polymorphic(type)) {
|
||||||
|
is_type_polymorphic_type = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ParameterValue param_value = {};
|
||||||
|
if (default_value != nullptr) {
|
||||||
|
Type *out_type = nullptr;
|
||||||
|
param_value = handle_parameter_value(ctx, type, &out_type, default_value, false);
|
||||||
|
if (type == nullptr && out_type != nullptr) {
|
||||||
|
type = out_type;
|
||||||
|
}
|
||||||
|
if (param_value.kind != ParameterValue_Constant && param_value.kind != ParameterValue_Nil) {
|
||||||
|
error(default_value, "Invalid parameter value");
|
||||||
|
param_value = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (type == nullptr) {
|
if (type == nullptr) {
|
||||||
error(params[i], "Invalid parameter type");
|
error(params[i], "Invalid parameter type");
|
||||||
type = t_invalid;
|
type = t_invalid;
|
||||||
@@ -471,7 +490,14 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<
|
|||||||
Token token = name->Ident.token;
|
Token token = name->Ident.token;
|
||||||
|
|
||||||
if (poly_operands != nullptr) {
|
if (poly_operands != nullptr) {
|
||||||
Operand operand = (*poly_operands)[entities.count];
|
Operand operand = {};
|
||||||
|
operand.type = t_invalid;
|
||||||
|
if (entities.count < poly_operands->count) {
|
||||||
|
operand = (*poly_operands)[entities.count];
|
||||||
|
} else if (param_value.kind != ParameterValue_Invalid) {
|
||||||
|
operand.mode = Addressing_Constant;
|
||||||
|
operand.value = param_value.value;
|
||||||
|
}
|
||||||
if (is_type_param) {
|
if (is_type_param) {
|
||||||
if (is_type_polymorphic(base_type(operand.type))) {
|
if (is_type_polymorphic(base_type(operand.type))) {
|
||||||
is_polymorphic = true;
|
is_polymorphic = true;
|
||||||
@@ -486,6 +512,7 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<
|
|||||||
}
|
}
|
||||||
if (e == nullptr) {
|
if (e == nullptr) {
|
||||||
e = alloc_entity_constant(scope, token, operand.type, operand.value);
|
e = alloc_entity_constant(scope, token, operand.type, operand.value);
|
||||||
|
e->Constant.param_value = param_value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -493,7 +520,8 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<
|
|||||||
e = alloc_entity_type_name(scope, token, type);
|
e = alloc_entity_type_name(scope, token, type);
|
||||||
e->TypeName.is_type_alias = true;
|
e->TypeName.is_type_alias = true;
|
||||||
} else {
|
} else {
|
||||||
e = alloc_entity_constant(scope, token, type, empty_exact_value);
|
e = alloc_entity_constant(scope, token, type, param_value.value);
|
||||||
|
e->Constant.param_value = param_value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -568,7 +596,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
|
|||||||
|
|
||||||
Entity *using_index_expr = nullptr;
|
Entity *using_index_expr = nullptr;
|
||||||
|
|
||||||
auto variants = array_make<Type *>(ctx->allocator, 0, variant_count);
|
auto variants = array_make<Type *>(permanent_allocator(), 0, variant_count);
|
||||||
|
|
||||||
union_type->Union.scope = ctx->scope;
|
union_type->Union.scope = ctx->scope;
|
||||||
|
|
||||||
@@ -579,7 +607,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
|
|||||||
|
|
||||||
if (ut->polymorphic_params != nullptr) {
|
if (ut->polymorphic_params != nullptr) {
|
||||||
ast_node(field_list, FieldList, ut->polymorphic_params);
|
ast_node(field_list, FieldList, ut->polymorphic_params);
|
||||||
Array<Ast *> params = field_list->list;
|
Slice<Ast *> params = field_list->list;
|
||||||
if (params.count != 0) {
|
if (params.count != 0) {
|
||||||
isize variable_count = 0;
|
isize variable_count = 0;
|
||||||
for_array(i, params) {
|
for_array(i, params) {
|
||||||
@@ -590,7 +618,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto entities = array_make<Entity *>(ctx->allocator, 0, variable_count);
|
auto entities = array_make<Entity *>(permanent_allocator(), 0, variable_count);
|
||||||
|
|
||||||
for_array(i, params) {
|
for_array(i, params) {
|
||||||
Ast *param = params[i];
|
Ast *param = params[i];
|
||||||
@@ -599,29 +627,45 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
|
|||||||
}
|
}
|
||||||
ast_node(p, Field, param);
|
ast_node(p, Field, param);
|
||||||
Ast *type_expr = p->type;
|
Ast *type_expr = p->type;
|
||||||
|
Ast *default_value = unparen_expr(p->default_value);
|
||||||
Type *type = nullptr;
|
Type *type = nullptr;
|
||||||
bool is_type_param = false;
|
bool is_type_param = false;
|
||||||
bool is_type_polymorphic_type = false;
|
bool is_type_polymorphic_type = false;
|
||||||
if (type_expr == nullptr) {
|
if (type_expr == nullptr && default_value == nullptr) {
|
||||||
error(param, "Expected a type for this parameter");
|
error(param, "Expected a type for this parameter");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (type_expr->kind == Ast_Ellipsis) {
|
if (type_expr != nullptr) {
|
||||||
type_expr = type_expr->Ellipsis.expr;
|
if (type_expr->kind == Ast_Ellipsis) {
|
||||||
error(param, "A polymorphic parameter cannot be variadic");
|
type_expr = type_expr->Ellipsis.expr;
|
||||||
}
|
error(param, "A polymorphic parameter cannot be variadic");
|
||||||
if (type_expr->kind == Ast_TypeidType) {
|
|
||||||
is_type_param = true;
|
|
||||||
Type *specialization = nullptr;
|
|
||||||
if (type_expr->TypeidType.specialization != nullptr) {
|
|
||||||
Ast *s = type_expr->TypeidType.specialization;
|
|
||||||
specialization = check_type(ctx, s);
|
|
||||||
}
|
}
|
||||||
type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization);
|
if (type_expr->kind == Ast_TypeidType) {
|
||||||
} else {
|
is_type_param = true;
|
||||||
type = check_type(ctx, type_expr);
|
Type *specialization = nullptr;
|
||||||
if (is_type_polymorphic(type)) {
|
if (type_expr->TypeidType.specialization != nullptr) {
|
||||||
is_type_polymorphic_type = true;
|
Ast *s = type_expr->TypeidType.specialization;
|
||||||
|
specialization = check_type(ctx, s);
|
||||||
|
}
|
||||||
|
type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization);
|
||||||
|
} else {
|
||||||
|
type = check_type(ctx, type_expr);
|
||||||
|
if (is_type_polymorphic(type)) {
|
||||||
|
is_type_polymorphic_type = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ParameterValue param_value = {};
|
||||||
|
if (default_value != nullptr) {
|
||||||
|
Type *out_type = nullptr;
|
||||||
|
param_value = handle_parameter_value(ctx, type, &out_type, default_value, false);
|
||||||
|
if (type == nullptr && out_type != nullptr) {
|
||||||
|
type = out_type;
|
||||||
|
}
|
||||||
|
if (param_value.kind != ParameterValue_Constant && param_value.kind != ParameterValue_Nil) {
|
||||||
|
error(default_value, "Invalid parameter value");
|
||||||
|
param_value = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -662,7 +706,14 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
|
|||||||
Token token = name->Ident.token;
|
Token token = name->Ident.token;
|
||||||
|
|
||||||
if (poly_operands != nullptr) {
|
if (poly_operands != nullptr) {
|
||||||
Operand operand = (*poly_operands)[entities.count];
|
Operand operand = {};
|
||||||
|
operand.type = t_invalid;
|
||||||
|
if (entities.count < poly_operands->count) {
|
||||||
|
operand = (*poly_operands)[entities.count];
|
||||||
|
} else if (param_value.kind != ParameterValue_Invalid) {
|
||||||
|
operand.mode = Addressing_Constant;
|
||||||
|
operand.value = param_value.value;
|
||||||
|
}
|
||||||
if (is_type_param) {
|
if (is_type_param) {
|
||||||
GB_ASSERT(operand.mode == Addressing_Type ||
|
GB_ASSERT(operand.mode == Addressing_Type ||
|
||||||
operand.mode == Addressing_Invalid);
|
operand.mode == Addressing_Invalid);
|
||||||
@@ -675,6 +726,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
|
|||||||
} else {
|
} else {
|
||||||
// GB_ASSERT(operand.mode == Addressing_Constant);
|
// GB_ASSERT(operand.mode == Addressing_Constant);
|
||||||
e = alloc_entity_constant(scope, token, operand.type, operand.value);
|
e = alloc_entity_constant(scope, token, operand.type, operand.value);
|
||||||
|
e->Constant.param_value = param_value;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (is_type_param) {
|
if (is_type_param) {
|
||||||
@@ -682,6 +734,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
|
|||||||
e->TypeName.is_type_alias = true;
|
e->TypeName.is_type_alias = true;
|
||||||
} else {
|
} else {
|
||||||
e = alloc_entity_constant(scope, token, type, empty_exact_value);
|
e = alloc_entity_constant(scope, token, type, empty_exact_value);
|
||||||
|
e->Constant.param_value = param_value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -816,7 +869,7 @@ void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *named_type, Ast
|
|||||||
enum_type->Enum.base_type = base_type;
|
enum_type->Enum.base_type = base_type;
|
||||||
enum_type->Enum.scope = ctx->scope;
|
enum_type->Enum.scope = ctx->scope;
|
||||||
|
|
||||||
auto fields = array_make<Entity *>(ctx->allocator, 0, et->fields.count);
|
auto fields = array_make<Entity *>(permanent_allocator(), 0, et->fields.count);
|
||||||
|
|
||||||
Type *constant_type = enum_type;
|
Type *constant_type = enum_type;
|
||||||
if (named_type != nullptr) {
|
if (named_type != nullptr) {
|
||||||
@@ -933,9 +986,9 @@ void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, Ast *node)
|
|||||||
ast_node(bft, BitFieldType, node);
|
ast_node(bft, BitFieldType, node);
|
||||||
GB_ASSERT(is_type_bit_field(bit_field_type));
|
GB_ASSERT(is_type_bit_field(bit_field_type));
|
||||||
|
|
||||||
auto fields = array_make<Entity*>(ctx->allocator, 0, bft->fields.count);
|
auto fields = array_make<Entity*>(permanent_allocator(), 0, bft->fields.count);
|
||||||
auto sizes = array_make<u32> (ctx->allocator, 0, bft->fields.count);
|
auto sizes = array_make<u32> (permanent_allocator(), 0, bft->fields.count);
|
||||||
auto offsets = array_make<u32> (ctx->allocator, 0, bft->fields.count);
|
auto offsets = array_make<u32> (permanent_allocator(), 0, bft->fields.count);
|
||||||
|
|
||||||
scope_reserve(ctx->scope, bft->fields.count);
|
scope_reserve(ctx->scope, bft->fields.count);
|
||||||
|
|
||||||
@@ -1337,7 +1390,7 @@ Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Oper
|
|||||||
|
|
||||||
if (is_polymorphic_type_assignable(ctx, poly_type, operand.type, false, modify_type)) {
|
if (is_polymorphic_type_assignable(ctx, poly_type, operand.type, false, modify_type)) {
|
||||||
if (show_error) {
|
if (show_error) {
|
||||||
set_procedure_abi_types(ctx->allocator, poly_type);
|
set_procedure_abi_types(poly_type);
|
||||||
}
|
}
|
||||||
return poly_type;
|
return poly_type;
|
||||||
}
|
}
|
||||||
@@ -1463,7 +1516,7 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
|
|||||||
|
|
||||||
bool success = true;
|
bool success = true;
|
||||||
ast_node(field_list, FieldList, _params);
|
ast_node(field_list, FieldList, _params);
|
||||||
Array<Ast *> params = field_list->list;
|
Slice<Ast *> params = field_list->list;
|
||||||
|
|
||||||
if (params.count == 0) {
|
if (params.count == 0) {
|
||||||
if (success_) *success_ = success;
|
if (success_) *success_ = success;
|
||||||
@@ -1496,7 +1549,7 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
|
|||||||
bool is_variadic = false;
|
bool is_variadic = false;
|
||||||
isize variadic_index = -1;
|
isize variadic_index = -1;
|
||||||
bool is_c_vararg = false;
|
bool is_c_vararg = false;
|
||||||
auto variables = array_make<Entity *>(ctx->allocator, 0, variable_count);
|
auto variables = array_make<Entity *>(permanent_allocator(), 0, variable_count);
|
||||||
for_array(i, params) {
|
for_array(i, params) {
|
||||||
Ast *param = params[i];
|
Ast *param = params[i];
|
||||||
if (param->kind != Ast_Field) {
|
if (param->kind != Ast_Field) {
|
||||||
@@ -1822,7 +1875,7 @@ Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
ast_node(field_list, FieldList, _results);
|
ast_node(field_list, FieldList, _results);
|
||||||
Array<Ast *> results = field_list->list;
|
Slice<Ast *> results = field_list->list;
|
||||||
|
|
||||||
if (results.count == 0) {
|
if (results.count == 0) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -1838,7 +1891,7 @@ Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto variables = array_make<Entity *>(ctx->allocator, 0, variable_count);
|
auto variables = array_make<Entity *>(permanent_allocator(), 0, variable_count);
|
||||||
for_array(i, results) {
|
for_array(i, results) {
|
||||||
ast_node(field, Field, results[i]);
|
ast_node(field, Field, results[i]);
|
||||||
Ast *default_value = unparen_expr(field->default_value);
|
Ast *default_value = unparen_expr(field->default_value);
|
||||||
@@ -2209,6 +2262,11 @@ Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCall
|
|||||||
return new_type;
|
return new_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_type_proc(original_type)) {
|
||||||
|
// NOTE(bill): Force a cast to prevent a possible type cycle
|
||||||
|
return t_rawptr;
|
||||||
|
}
|
||||||
|
|
||||||
if (cc == ProcCC_None || cc == ProcCC_PureNone || cc == ProcCC_InlineAsm) {
|
if (cc == ProcCC_None || cc == ProcCC_PureNone || cc == ProcCC_InlineAsm) {
|
||||||
return new_type;
|
return new_type;
|
||||||
}
|
}
|
||||||
@@ -2221,7 +2279,11 @@ Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCall
|
|||||||
return new_type;
|
return new_type;
|
||||||
}
|
}
|
||||||
if (build_context.ODIN_ARCH == "amd64") {
|
if (build_context.ODIN_ARCH == "amd64") {
|
||||||
if (is_type_integer_128bit(original_type)) {
|
bool is_128 = is_type_integer_128bit(original_type);
|
||||||
|
if (!is_128 && is_type_bit_set(original_type) && type_size_of(original_type) == 16) {
|
||||||
|
// is_128 = true;
|
||||||
|
}
|
||||||
|
if (is_128) {
|
||||||
if (build_context.ODIN_OS == "windows") {
|
if (build_context.ODIN_OS == "windows") {
|
||||||
return alloc_type_simd_vector(2, t_u64);
|
return alloc_type_simd_vector(2, t_u64);
|
||||||
} else {
|
} else {
|
||||||
@@ -2332,6 +2394,11 @@ Type *type_to_abi_compat_result_type(gbAllocator a, Type *original_type, ProcCal
|
|||||||
return new_type;
|
return new_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_type_proc(single_type)) {
|
||||||
|
// NOTE(bill): Force a cast to prevent a possible type cycle
|
||||||
|
return t_rawptr;
|
||||||
|
}
|
||||||
|
|
||||||
if (is_type_simd_vector(single_type)) {
|
if (is_type_simd_vector(single_type)) {
|
||||||
return new_type;
|
return new_type;
|
||||||
}
|
}
|
||||||
@@ -2445,16 +2512,21 @@ bool abi_compat_return_by_pointer(gbAllocator a, ProcCallingConvention cc, Type
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_procedure_abi_types(gbAllocator allocator, Type *type) {
|
void set_procedure_abi_types(Type *type) {
|
||||||
type = base_type(type);
|
type = base_type(type);
|
||||||
if (type->kind != Type_Proc) {
|
if (type->kind != Type_Proc) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type->Proc.abi_types_set) {
|
if (type->Proc.abi_types_set || type->flags & TypeFlag_InProcessOfCheckingABI) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gbAllocator allocator = permanent_allocator();
|
||||||
|
|
||||||
|
u32 flags = type->flags;
|
||||||
|
type->flags |= TypeFlag_InProcessOfCheckingABI;
|
||||||
|
|
||||||
type->Proc.abi_compat_params = array_make<Type *>(allocator, cast(isize)type->Proc.param_count);
|
type->Proc.abi_compat_params = array_make<Type *>(allocator, cast(isize)type->Proc.param_count);
|
||||||
for (i32 i = 0; i < type->Proc.param_count; i++) {
|
for (i32 i = 0; i < type->Proc.param_count; i++) {
|
||||||
Entity *e = type->Proc.params->Tuple.variables[i];
|
Entity *e = type->Proc.params->Tuple.variables[i];
|
||||||
@@ -2466,7 +2538,7 @@ void set_procedure_abi_types(gbAllocator allocator, Type *type) {
|
|||||||
case ProcCC_Odin:
|
case ProcCC_Odin:
|
||||||
case ProcCC_Contextless:
|
case ProcCC_Contextless:
|
||||||
case ProcCC_Pure:
|
case ProcCC_Pure:
|
||||||
if (is_type_pointer(new_type) & !is_type_pointer(e->type)) {
|
if (is_type_pointer(new_type) && !is_type_pointer(e->type) && !is_type_proc(e->type)) {
|
||||||
e->flags |= EntityFlag_ImplicitReference;
|
e->flags |= EntityFlag_ImplicitReference;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -2474,7 +2546,7 @@ void set_procedure_abi_types(gbAllocator allocator, Type *type) {
|
|||||||
|
|
||||||
if (build_context.ODIN_OS == "linux" ||
|
if (build_context.ODIN_OS == "linux" ||
|
||||||
build_context.ODIN_OS == "darwin") {
|
build_context.ODIN_OS == "darwin") {
|
||||||
if (is_type_pointer(new_type) & !is_type_pointer(e->type)) {
|
if (is_type_pointer(new_type) & !is_type_pointer(e->type) && !is_type_proc(e->type)) {
|
||||||
e->flags |= EntityFlag_ByVal;
|
e->flags |= EntityFlag_ByVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2484,13 +2556,13 @@ void set_procedure_abi_types(gbAllocator allocator, Type *type) {
|
|||||||
for (i32 i = 0; i < type->Proc.param_count; i++) {
|
for (i32 i = 0; i < type->Proc.param_count; i++) {
|
||||||
Entity *e = type->Proc.params->Tuple.variables[i];
|
Entity *e = type->Proc.params->Tuple.variables[i];
|
||||||
if (e->kind == Entity_Variable) {
|
if (e->kind == Entity_Variable) {
|
||||||
set_procedure_abi_types(allocator, e->type);
|
set_procedure_abi_types(e->type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (i32 i = 0; i < type->Proc.result_count; i++) {
|
for (i32 i = 0; i < type->Proc.result_count; i++) {
|
||||||
Entity *e = type->Proc.results->Tuple.variables[i];
|
Entity *e = type->Proc.results->Tuple.variables[i];
|
||||||
if (e->kind == Entity_Variable) {
|
if (e->kind == Entity_Variable) {
|
||||||
set_procedure_abi_types(allocator, e->type);
|
set_procedure_abi_types(e->type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2499,6 +2571,7 @@ void set_procedure_abi_types(gbAllocator allocator, Type *type) {
|
|||||||
type->Proc.return_by_pointer = abi_compat_return_by_pointer(allocator, type->Proc.calling_convention, type->Proc.abi_compat_result_type);
|
type->Proc.return_by_pointer = abi_compat_return_by_pointer(allocator, type->Proc.calling_convention, type->Proc.abi_compat_result_type);
|
||||||
|
|
||||||
type->Proc.abi_types_set = true;
|
type->Proc.abi_types_set = true;
|
||||||
|
type->flags = flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE(bill): 'operands' is for generating non generic procedure type
|
// NOTE(bill): 'operands' is for generating non generic procedure type
|
||||||
@@ -2711,30 +2784,29 @@ void init_map_entry_type(Type *type) {
|
|||||||
if (type->Map.entry_type != nullptr) return;
|
if (type->Map.entry_type != nullptr) return;
|
||||||
|
|
||||||
// NOTE(bill): The preload types may have not been set yet
|
// NOTE(bill): The preload types may have not been set yet
|
||||||
GB_ASSERT(t_map_key != nullptr);
|
GB_ASSERT(t_map_hash != nullptr);
|
||||||
gbAllocator a = heap_allocator();
|
|
||||||
Type *entry_type = alloc_type_struct();
|
Type *entry_type = alloc_type_struct();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
struct {
|
struct {
|
||||||
hash: __MapKey;
|
hash: runtime.Map_Hash,
|
||||||
next: int;
|
next: int,
|
||||||
key: Key;
|
key: Key,
|
||||||
value: Value;
|
value: Value,
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
Ast *dummy_node = alloc_ast_node(nullptr, Ast_Invalid);
|
Ast *dummy_node = alloc_ast_node(nullptr, Ast_Invalid);
|
||||||
Scope *s = create_scope(builtin_pkg->scope, a);
|
Scope *s = create_scope(builtin_pkg->scope);
|
||||||
|
|
||||||
auto fields = array_make<Entity *>(a, 0, 3);
|
auto fields = array_make<Entity *>(permanent_allocator(), 0, 4);
|
||||||
array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("key")), t_map_key, false, 0, EntityState_Resolved));
|
array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("hash")), t_uintptr, false, cast(i32)fields.count, EntityState_Resolved));
|
||||||
array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("next")), t_int, false, 1, EntityState_Resolved));
|
array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("next")), t_int, false, cast(i32)fields.count, EntityState_Resolved));
|
||||||
array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("value")), type->Map.value, false, 2, EntityState_Resolved));
|
array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("key")), type->Map.key, false, cast(i32)fields.count, EntityState_Resolved));
|
||||||
|
array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("value")), type->Map.value, false, cast(i32)fields.count, EntityState_Resolved));
|
||||||
|
|
||||||
|
|
||||||
entry_type->Struct.fields = fields;
|
entry_type->Struct.fields = fields;
|
||||||
|
|
||||||
// type_set_offsets(a, entry_type);
|
|
||||||
type->Map.entry_type = entry_type;
|
type->Map.entry_type = entry_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2757,15 +2829,14 @@ void init_map_internal_types(Type *type) {
|
|||||||
entries: [dynamic]EntryType;
|
entries: [dynamic]EntryType;
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
gbAllocator a = heap_allocator();
|
|
||||||
Ast *dummy_node = alloc_ast_node(nullptr, Ast_Invalid);
|
Ast *dummy_node = alloc_ast_node(nullptr, Ast_Invalid);
|
||||||
Scope *s = create_scope(builtin_pkg->scope, a);
|
Scope *s = create_scope(builtin_pkg->scope);
|
||||||
|
|
||||||
Type *hashes_type = alloc_type_slice(t_int);
|
Type *hashes_type = alloc_type_slice(t_int);
|
||||||
Type *entries_type = alloc_type_dynamic_array(type->Map.entry_type);
|
Type *entries_type = alloc_type_dynamic_array(type->Map.entry_type);
|
||||||
|
|
||||||
|
|
||||||
auto fields = array_make<Entity *>(a, 0, 2);
|
auto fields = array_make<Entity *>(permanent_allocator(), 0, 2);
|
||||||
array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("hashes")), hashes_type, false, 0, EntityState_Resolved));
|
array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("hashes")), hashes_type, false, 0, EntityState_Resolved));
|
||||||
array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("entries")), entries_type, false, 1, EntityState_Resolved));
|
array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("entries")), entries_type, false, 1, EntityState_Resolved));
|
||||||
|
|
||||||
@@ -2777,6 +2848,47 @@ void init_map_internal_types(Type *type) {
|
|||||||
type->Map.lookup_result_type = make_optional_ok_type(value);
|
type->Map.lookup_result_type = make_optional_ok_type(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void add_map_key_type_dependencies(CheckerContext *ctx, Type *key) {
|
||||||
|
key = core_type(key);
|
||||||
|
|
||||||
|
if (is_type_cstring(key)) {
|
||||||
|
add_package_dependency(ctx, "runtime", "default_hasher_cstring");
|
||||||
|
} else if (is_type_string(key)) {
|
||||||
|
add_package_dependency(ctx, "runtime", "default_hasher_string");
|
||||||
|
} else if (!is_type_polymorphic(key)) {
|
||||||
|
if (!is_type_comparable(key)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_type_simple_compare(key)) {
|
||||||
|
i64 sz = type_size_of(key);
|
||||||
|
if (1 <= sz && sz <= 16) {
|
||||||
|
char buf[20] = {};
|
||||||
|
gb_snprintf(buf, 20, "default_hasher%d", cast(i32)sz);
|
||||||
|
add_package_dependency(ctx, "runtime", buf);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
add_package_dependency(ctx, "runtime", "default_hasher_n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key->kind == Type_Struct) {
|
||||||
|
add_package_dependency(ctx, "runtime", "default_hasher_n");
|
||||||
|
for_array(i, key->Struct.fields) {
|
||||||
|
Entity *field = key->Struct.fields[i];
|
||||||
|
add_map_key_type_dependencies(ctx, field->type);
|
||||||
|
}
|
||||||
|
} else if (key->kind == Type_EnumeratedArray) {
|
||||||
|
add_package_dependency(ctx, "runtime", "default_hasher_n");
|
||||||
|
add_map_key_type_dependencies(ctx, key->EnumeratedArray.elem);
|
||||||
|
} else if (key->kind == Type_Array) {
|
||||||
|
add_package_dependency(ctx, "runtime", "default_hasher_n");
|
||||||
|
add_map_key_type_dependencies(ctx, key->Array.elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void check_map_type(CheckerContext *ctx, Type *type, Ast *node) {
|
void check_map_type(CheckerContext *ctx, Type *type, Ast *node) {
|
||||||
GB_ASSERT(type->kind == Type_Map);
|
GB_ASSERT(type->kind == Type_Map);
|
||||||
ast_node(mt, MapType, node);
|
ast_node(mt, MapType, node);
|
||||||
@@ -2793,16 +2905,16 @@ void check_map_type(CheckerContext *ctx, Type *type, Ast *node) {
|
|||||||
gb_string_free(str);
|
gb_string_free(str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (type_size_of(key) == 0) {
|
||||||
|
gbString str = type_to_string(key);
|
||||||
|
error(node, "Invalid type of a key for a map of size 0, got '%s'", str);
|
||||||
|
gb_string_free(str);
|
||||||
|
}
|
||||||
|
|
||||||
type->Map.key = key;
|
type->Map.key = key;
|
||||||
type->Map.value = value;
|
type->Map.value = value;
|
||||||
|
|
||||||
if (is_type_string(key)) {
|
add_map_key_type_dependencies(ctx, key);
|
||||||
add_package_dependency(ctx, "runtime", "default_hash_string");
|
|
||||||
} else {
|
|
||||||
add_package_dependency(ctx, "runtime", "default_hash_ptr");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
init_core_map_type(ctx->checker);
|
init_core_map_type(ctx->checker);
|
||||||
init_map_internal_types(type);
|
init_map_internal_types(type);
|
||||||
@@ -2833,7 +2945,7 @@ Type *make_soa_struct_fixed(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_
|
|||||||
soa_struct->Struct.soa_elem = elem;
|
soa_struct->Struct.soa_elem = elem;
|
||||||
soa_struct->Struct.soa_count = count;
|
soa_struct->Struct.soa_count = count;
|
||||||
|
|
||||||
scope = create_scope(ctx->scope, ctx->allocator);
|
scope = create_scope(ctx->scope);
|
||||||
soa_struct->Struct.scope = scope;
|
soa_struct->Struct.scope = scope;
|
||||||
|
|
||||||
String params_xyzw[4] = {
|
String params_xyzw[4] = {
|
||||||
@@ -2866,7 +2978,7 @@ Type *make_soa_struct_fixed(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_
|
|||||||
soa_struct->Struct.soa_elem = elem;
|
soa_struct->Struct.soa_elem = elem;
|
||||||
soa_struct->Struct.soa_count = count;
|
soa_struct->Struct.soa_count = count;
|
||||||
|
|
||||||
scope = create_scope(old_struct->Struct.scope->parent, ctx->allocator);
|
scope = create_scope(old_struct->Struct.scope->parent);
|
||||||
soa_struct->Struct.scope = scope;
|
soa_struct->Struct.scope = scope;
|
||||||
|
|
||||||
for_array(i, old_struct->Struct.fields) {
|
for_array(i, old_struct->Struct.fields) {
|
||||||
@@ -2927,7 +3039,7 @@ Type *make_soa_struct_slice(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_
|
|||||||
soa_struct->Struct.soa_count = 0;
|
soa_struct->Struct.soa_count = 0;
|
||||||
soa_struct->Struct.is_polymorphic = true;
|
soa_struct->Struct.is_polymorphic = true;
|
||||||
|
|
||||||
scope = create_scope(ctx->scope, ctx->allocator);
|
scope = create_scope(ctx->scope);
|
||||||
soa_struct->Struct.scope = scope;
|
soa_struct->Struct.scope = scope;
|
||||||
} else if (is_type_array(elem)) {
|
} else if (is_type_array(elem)) {
|
||||||
Type *old_array = base_type(elem);
|
Type *old_array = base_type(elem);
|
||||||
@@ -2941,7 +3053,7 @@ Type *make_soa_struct_slice(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_
|
|||||||
soa_struct->Struct.soa_elem = elem;
|
soa_struct->Struct.soa_elem = elem;
|
||||||
soa_struct->Struct.soa_count = 0;
|
soa_struct->Struct.soa_count = 0;
|
||||||
|
|
||||||
scope = create_scope(ctx->scope, ctx->allocator);
|
scope = create_scope(ctx->scope);
|
||||||
soa_struct->Struct.scope = scope;
|
soa_struct->Struct.scope = scope;
|
||||||
|
|
||||||
String params_xyzw[4] = {
|
String params_xyzw[4] = {
|
||||||
@@ -2977,7 +3089,7 @@ Type *make_soa_struct_slice(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_
|
|||||||
soa_struct->Struct.soa_elem = elem;
|
soa_struct->Struct.soa_elem = elem;
|
||||||
soa_struct->Struct.soa_count = 0;
|
soa_struct->Struct.soa_count = 0;
|
||||||
|
|
||||||
scope = create_scope(old_struct->Struct.scope->parent, ctx->allocator);
|
scope = create_scope(old_struct->Struct.scope->parent);
|
||||||
soa_struct->Struct.scope = scope;
|
soa_struct->Struct.scope = scope;
|
||||||
|
|
||||||
for_array(i, old_struct->Struct.fields) {
|
for_array(i, old_struct->Struct.fields) {
|
||||||
@@ -3044,7 +3156,7 @@ Type *make_soa_struct_dynamic_array(CheckerContext *ctx, Ast *array_typ_expr, As
|
|||||||
soa_struct->Struct.soa_count = 0;
|
soa_struct->Struct.soa_count = 0;
|
||||||
soa_struct->Struct.is_polymorphic = true;
|
soa_struct->Struct.is_polymorphic = true;
|
||||||
|
|
||||||
scope = create_scope(ctx->scope, ctx->allocator);
|
scope = create_scope(ctx->scope);
|
||||||
soa_struct->Struct.scope = scope;
|
soa_struct->Struct.scope = scope;
|
||||||
} else if (is_type_array(elem)) {
|
} else if (is_type_array(elem)) {
|
||||||
Type *old_array = base_type(elem);
|
Type *old_array = base_type(elem);
|
||||||
@@ -3058,7 +3170,7 @@ Type *make_soa_struct_dynamic_array(CheckerContext *ctx, Ast *array_typ_expr, As
|
|||||||
soa_struct->Struct.soa_elem = elem;
|
soa_struct->Struct.soa_elem = elem;
|
||||||
soa_struct->Struct.soa_count = 0;
|
soa_struct->Struct.soa_count = 0;
|
||||||
|
|
||||||
scope = create_scope(ctx->scope, ctx->allocator);
|
scope = create_scope(ctx->scope);
|
||||||
soa_struct->Struct.scope = scope;
|
soa_struct->Struct.scope = scope;
|
||||||
|
|
||||||
String params_xyzw[4] = {
|
String params_xyzw[4] = {
|
||||||
@@ -3093,7 +3205,7 @@ Type *make_soa_struct_dynamic_array(CheckerContext *ctx, Ast *array_typ_expr, As
|
|||||||
soa_struct->Struct.soa_elem = elem;
|
soa_struct->Struct.soa_elem = elem;
|
||||||
soa_struct->Struct.soa_count = 0;
|
soa_struct->Struct.soa_count = 0;
|
||||||
|
|
||||||
scope = create_scope(old_struct->Struct.scope->parent, ctx->allocator);
|
scope = create_scope(old_struct->Struct.scope->parent);
|
||||||
soa_struct->Struct.scope = scope;
|
soa_struct->Struct.scope = scope;
|
||||||
|
|
||||||
for_array(i, old_struct->Struct.fields) {
|
for_array(i, old_struct->Struct.fields) {
|
||||||
|
|||||||
+188
-79
@@ -187,8 +187,8 @@ void init_decl_info(DeclInfo *d, Scope *scope, DeclInfo *parent) {
|
|||||||
array_init (&d->labels, heap_allocator());
|
array_init (&d->labels, heap_allocator());
|
||||||
}
|
}
|
||||||
|
|
||||||
DeclInfo *make_decl_info(gbAllocator a, Scope *scope, DeclInfo *parent) {
|
DeclInfo *make_decl_info(Scope *scope, DeclInfo *parent) {
|
||||||
DeclInfo *d = gb_alloc_item(a, DeclInfo);
|
DeclInfo *d = gb_alloc_item(permanent_allocator(), DeclInfo);
|
||||||
init_decl_info(d, scope, parent);
|
init_decl_info(d, scope, parent);
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
@@ -219,8 +219,8 @@ bool decl_info_has_init(DeclInfo *d) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
Scope *create_scope(Scope *parent, gbAllocator allocator, isize init_elements_capacity=DEFAULT_SCOPE_CAPACITY) {
|
Scope *create_scope(Scope *parent, isize init_elements_capacity=DEFAULT_SCOPE_CAPACITY) {
|
||||||
Scope *s = gb_alloc_item(allocator, Scope);
|
Scope *s = gb_alloc_item(permanent_allocator(), Scope);
|
||||||
s->parent = parent;
|
s->parent = parent;
|
||||||
string_map_init(&s->elements, heap_allocator(), init_elements_capacity);
|
string_map_init(&s->elements, heap_allocator(), init_elements_capacity);
|
||||||
ptr_set_init(&s->imported, heap_allocator(), 0);
|
ptr_set_init(&s->imported, heap_allocator(), 0);
|
||||||
@@ -244,7 +244,7 @@ Scope *create_scope_from_file(CheckerContext *c, AstFile *f) {
|
|||||||
GB_ASSERT(f->pkg != nullptr);
|
GB_ASSERT(f->pkg != nullptr);
|
||||||
GB_ASSERT(f->pkg->scope != nullptr);
|
GB_ASSERT(f->pkg->scope != nullptr);
|
||||||
|
|
||||||
Scope *s = create_scope(f->pkg->scope, c->allocator);
|
Scope *s = create_scope(f->pkg->scope);
|
||||||
|
|
||||||
array_reserve(&s->delayed_imports, f->imports.count);
|
array_reserve(&s->delayed_imports, f->imports.count);
|
||||||
array_reserve(&s->delayed_directives, f->directive_count);
|
array_reserve(&s->delayed_directives, f->directive_count);
|
||||||
@@ -264,7 +264,7 @@ Scope *create_scope_from_package(CheckerContext *c, AstPackage *pkg) {
|
|||||||
decl_count += pkg->files[i]->decls.count;
|
decl_count += pkg->files[i]->decls.count;
|
||||||
}
|
}
|
||||||
isize init_elements_capacity = 2*decl_count;
|
isize init_elements_capacity = 2*decl_count;
|
||||||
Scope *s = create_scope(builtin_pkg->scope, c->allocator, init_elements_capacity);
|
Scope *s = create_scope(builtin_pkg->scope, init_elements_capacity);
|
||||||
|
|
||||||
s->flags |= ScopeFlag_Pkg;
|
s->flags |= ScopeFlag_Pkg;
|
||||||
s->pkg = pkg;
|
s->pkg = pkg;
|
||||||
@@ -324,7 +324,7 @@ void check_open_scope(CheckerContext *c, Ast *node) {
|
|||||||
GB_ASSERT(node->kind == Ast_Invalid ||
|
GB_ASSERT(node->kind == Ast_Invalid ||
|
||||||
is_ast_stmt(node) ||
|
is_ast_stmt(node) ||
|
||||||
is_ast_type(node));
|
is_ast_type(node));
|
||||||
Scope *scope = create_scope(c->scope, c->allocator);
|
Scope *scope = create_scope(c->scope);
|
||||||
add_scope(c, node, scope);
|
add_scope(c, node, scope);
|
||||||
switch (node->kind) {
|
switch (node->kind) {
|
||||||
case Ast_ProcType:
|
case Ast_ProcType:
|
||||||
@@ -368,9 +368,14 @@ void scope_lookup_parent(Scope *scope, String const &name, Scope **scope_, Entit
|
|||||||
if (e->kind == Entity_Label) {
|
if (e->kind == Entity_Label) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (e->kind == Entity_Variable &&
|
if (e->kind == Entity_Variable) {
|
||||||
!(e->scope->flags&ScopeFlag_File)) {
|
if (e->scope->flags&ScopeFlag_File) {
|
||||||
continue;
|
// Global variables are file to access
|
||||||
|
} else if (e->flags&EntityFlag_Static) {
|
||||||
|
// Allow static/thread_local variables to be referenced
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -690,31 +695,33 @@ void add_global_type_entity(String name, Type *type) {
|
|||||||
|
|
||||||
void init_universal(void) {
|
void init_universal(void) {
|
||||||
BuildContext *bc = &build_context;
|
BuildContext *bc = &build_context;
|
||||||
|
|
||||||
// NOTE(bill): No need to free these
|
// NOTE(bill): No need to free these
|
||||||
gbAllocator a = heap_allocator();
|
// gbAllocator a = heap_allocator();
|
||||||
|
gbAllocator a = permanent_allocator();
|
||||||
|
|
||||||
builtin_pkg = gb_alloc_item(a, AstPackage);
|
builtin_pkg = gb_alloc_item(a, AstPackage);
|
||||||
builtin_pkg->name = str_lit("builtin");
|
builtin_pkg->name = str_lit("builtin");
|
||||||
builtin_pkg->kind = Package_Normal;
|
builtin_pkg->kind = Package_Normal;
|
||||||
|
|
||||||
builtin_pkg->scope = create_scope(nullptr, a);
|
builtin_pkg->scope = create_scope(nullptr);
|
||||||
builtin_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global;
|
builtin_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global | ScopeFlag_Builtin;
|
||||||
builtin_pkg->scope->pkg = builtin_pkg;
|
builtin_pkg->scope->pkg = builtin_pkg;
|
||||||
|
|
||||||
intrinsics_pkg = gb_alloc_item(a, AstPackage);
|
intrinsics_pkg = gb_alloc_item(a, AstPackage);
|
||||||
intrinsics_pkg->name = str_lit("intrinsics");
|
intrinsics_pkg->name = str_lit("intrinsics");
|
||||||
intrinsics_pkg->kind = Package_Normal;
|
intrinsics_pkg->kind = Package_Normal;
|
||||||
|
|
||||||
intrinsics_pkg->scope = create_scope(nullptr, a);
|
intrinsics_pkg->scope = create_scope(nullptr);
|
||||||
intrinsics_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global;
|
intrinsics_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global | ScopeFlag_Builtin;
|
||||||
intrinsics_pkg->scope->pkg = intrinsics_pkg;
|
intrinsics_pkg->scope->pkg = intrinsics_pkg;
|
||||||
|
|
||||||
config_pkg = gb_alloc_item(a, AstPackage);
|
config_pkg = gb_alloc_item(a, AstPackage);
|
||||||
config_pkg->name = str_lit("config");
|
config_pkg->name = str_lit("config");
|
||||||
config_pkg->kind = Package_Normal;
|
config_pkg->kind = Package_Normal;
|
||||||
|
|
||||||
config_pkg->scope = create_scope(nullptr, a);
|
config_pkg->scope = create_scope(nullptr);
|
||||||
config_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global;
|
config_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global | ScopeFlag_Builtin;
|
||||||
config_pkg->scope->pkg = config_pkg;
|
config_pkg->scope->pkg = config_pkg;
|
||||||
|
|
||||||
|
|
||||||
@@ -724,6 +731,18 @@ void init_universal(void) {
|
|||||||
}
|
}
|
||||||
add_global_type_entity(str_lit("byte"), &basic_types[Basic_u8]);
|
add_global_type_entity(str_lit("byte"), &basic_types[Basic_u8]);
|
||||||
|
|
||||||
|
{
|
||||||
|
void set_procedure_abi_types(Type *type);
|
||||||
|
|
||||||
|
Type *equal_args[2] = {t_rawptr, t_rawptr};
|
||||||
|
t_equal_proc = alloc_type_proc_from_types(equal_args, 2, t_bool, false, ProcCC_Contextless);
|
||||||
|
set_procedure_abi_types(t_equal_proc);
|
||||||
|
|
||||||
|
Type *hasher_args[2] = {t_rawptr, t_uintptr};
|
||||||
|
t_hasher_proc = alloc_type_proc_from_types(hasher_args, 2, t_uintptr, false, ProcCC_Contextless);
|
||||||
|
set_procedure_abi_types(t_hasher_proc);
|
||||||
|
}
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
add_global_constant(str_lit("true"), t_untyped_bool, exact_value_bool(true));
|
add_global_constant(str_lit("true"), t_untyped_bool, exact_value_bool(true));
|
||||||
add_global_constant(str_lit("false"), t_untyped_bool, exact_value_bool(false));
|
add_global_constant(str_lit("false"), t_untyped_bool, exact_value_bool(false));
|
||||||
@@ -742,6 +761,7 @@ void init_universal(void) {
|
|||||||
add_global_constant(str_lit("ODIN_DEFAULT_TO_NIL_ALLOCATOR"), t_untyped_bool, exact_value_bool(bc->ODIN_DEFAULT_TO_NIL_ALLOCATOR));
|
add_global_constant(str_lit("ODIN_DEFAULT_TO_NIL_ALLOCATOR"), t_untyped_bool, exact_value_bool(bc->ODIN_DEFAULT_TO_NIL_ALLOCATOR));
|
||||||
add_global_constant(str_lit("ODIN_USE_LLVM_API"), t_untyped_bool, exact_value_bool(bc->use_llvm_api));
|
add_global_constant(str_lit("ODIN_USE_LLVM_API"), t_untyped_bool, exact_value_bool(bc->use_llvm_api));
|
||||||
add_global_constant(str_lit("ODIN_NO_DYNAMIC_LITERALS"), t_untyped_bool, exact_value_bool(bc->no_dynamic_literals));
|
add_global_constant(str_lit("ODIN_NO_DYNAMIC_LITERALS"), t_untyped_bool, exact_value_bool(bc->no_dynamic_literals));
|
||||||
|
add_global_constant(str_lit("ODIN_TEST"), t_untyped_bool, exact_value_bool(bc->command_kind == Command_test));
|
||||||
|
|
||||||
|
|
||||||
// Builtin Procedures
|
// Builtin Procedures
|
||||||
@@ -837,6 +857,8 @@ void init_checker_info(CheckerInfo *i) {
|
|||||||
array_init(&i->variable_init_order, a);
|
array_init(&i->variable_init_order, a);
|
||||||
array_init(&i->required_foreign_imports_through_force, a);
|
array_init(&i->required_foreign_imports_through_force, a);
|
||||||
array_init(&i->required_global_variables, a);
|
array_init(&i->required_global_variables, a);
|
||||||
|
array_init(&i->testing_procedures, a, 0, 0);
|
||||||
|
|
||||||
|
|
||||||
i->allow_identifier_uses = build_context.query_data_set_settings.kind == QueryDataSet_GoToDefinitions;
|
i->allow_identifier_uses = build_context.query_data_set_settings.kind == QueryDataSet_GoToDefinitions;
|
||||||
if (i->allow_identifier_uses) {
|
if (i->allow_identifier_uses) {
|
||||||
@@ -870,7 +892,6 @@ CheckerContext make_checker_context(Checker *c) {
|
|||||||
CheckerContext ctx = c->init_ctx;
|
CheckerContext ctx = c->init_ctx;
|
||||||
ctx.checker = c;
|
ctx.checker = c;
|
||||||
ctx.info = &c->info;
|
ctx.info = &c->info;
|
||||||
ctx.allocator = c->allocator;
|
|
||||||
ctx.scope = builtin_pkg->scope;
|
ctx.scope = builtin_pkg->scope;
|
||||||
ctx.pkg = builtin_pkg;
|
ctx.pkg = builtin_pkg;
|
||||||
|
|
||||||
@@ -895,6 +916,7 @@ bool init_checker(Checker *c, Parser *parser) {
|
|||||||
gbAllocator a = heap_allocator();
|
gbAllocator a = heap_allocator();
|
||||||
|
|
||||||
init_checker_info(&c->info);
|
init_checker_info(&c->info);
|
||||||
|
c->info.checker = c;
|
||||||
|
|
||||||
array_init(&c->procs_to_check, a);
|
array_init(&c->procs_to_check, a);
|
||||||
array_init(&c->procs_with_deferred_to_check, a);
|
array_init(&c->procs_with_deferred_to_check, a);
|
||||||
@@ -904,8 +926,6 @@ bool init_checker(Checker *c, Parser *parser) {
|
|||||||
isize total_token_count = c->parser->total_token_count;
|
isize total_token_count = c->parser->total_token_count;
|
||||||
isize arena_size = 2 * item_size * total_token_count;
|
isize arena_size = 2 * item_size * total_token_count;
|
||||||
|
|
||||||
c->allocator = heap_allocator();
|
|
||||||
|
|
||||||
c->init_ctx = make_checker_context(c);
|
c->init_ctx = make_checker_context(c);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1502,11 +1522,10 @@ void add_min_dep_type_info(Checker *c, Type *t) {
|
|||||||
ti_index = type_info_index(&c->info, t, false);
|
ti_index = type_info_index(&c->info, t, false);
|
||||||
}
|
}
|
||||||
GB_ASSERT(ti_index >= 0);
|
GB_ASSERT(ti_index >= 0);
|
||||||
if (ptr_set_exists(set, ti_index)) {
|
if (ptr_set_update(set, ti_index)) {
|
||||||
// Type Already exists
|
// Type Already exists
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ptr_set_add(set, ti_index);
|
|
||||||
|
|
||||||
// Add nested types
|
// Add nested types
|
||||||
if (t->kind == Type_Named) {
|
if (t->kind == Type_Named) {
|
||||||
@@ -1680,8 +1699,6 @@ void add_dependency_to_set(Checker *c, Entity *entity) {
|
|||||||
CheckerInfo *info = &c->info;
|
CheckerInfo *info = &c->info;
|
||||||
auto *set = &info->minimum_dependency_set;
|
auto *set = &info->minimum_dependency_set;
|
||||||
|
|
||||||
String name = entity->token.string;
|
|
||||||
|
|
||||||
if (entity->type != nullptr &&
|
if (entity->type != nullptr &&
|
||||||
is_type_polymorphic(entity->type)) {
|
is_type_polymorphic(entity->type)) {
|
||||||
|
|
||||||
@@ -1691,12 +1708,10 @@ void add_dependency_to_set(Checker *c, Entity *entity) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ptr_set_exists(set, entity)) {
|
if (ptr_set_update(set, entity)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ptr_set_add(set, entity);
|
|
||||||
DeclInfo *decl = decl_info_of_entity(entity);
|
DeclInfo *decl = decl_info_of_entity(entity);
|
||||||
if (decl == nullptr) {
|
if (decl == nullptr) {
|
||||||
return;
|
return;
|
||||||
@@ -1715,16 +1730,15 @@ void add_dependency_to_set(Checker *c, Entity *entity) {
|
|||||||
if (fl != nullptr) {
|
if (fl != nullptr) {
|
||||||
GB_ASSERT_MSG(fl->kind == Entity_LibraryName &&
|
GB_ASSERT_MSG(fl->kind == Entity_LibraryName &&
|
||||||
(fl->flags&EntityFlag_Used),
|
(fl->flags&EntityFlag_Used),
|
||||||
"%.*s", LIT(name));
|
"%.*s", LIT(entity->token.string));
|
||||||
add_dependency_to_set(c, fl);
|
add_dependency_to_set(c, fl);
|
||||||
}
|
}
|
||||||
}
|
} else if (e->kind == Entity_Variable && e->Variable.is_foreign) {
|
||||||
if (e->kind == Entity_Variable && e->Variable.is_foreign) {
|
|
||||||
Entity *fl = e->Variable.foreign_library;
|
Entity *fl = e->Variable.foreign_library;
|
||||||
if (fl != nullptr) {
|
if (fl != nullptr) {
|
||||||
GB_ASSERT_MSG(fl->kind == Entity_LibraryName &&
|
GB_ASSERT_MSG(fl->kind == Entity_LibraryName &&
|
||||||
(fl->flags&EntityFlag_Used),
|
(fl->flags&EntityFlag_Used),
|
||||||
"%.*s", LIT(name));
|
"%.*s", LIT(entity->token.string));
|
||||||
add_dependency_to_set(c, fl);
|
add_dependency_to_set(c, fl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1733,7 +1747,10 @@ void add_dependency_to_set(Checker *c, Entity *entity) {
|
|||||||
|
|
||||||
|
|
||||||
void generate_minimum_dependency_set(Checker *c, Entity *start) {
|
void generate_minimum_dependency_set(Checker *c, Entity *start) {
|
||||||
ptr_set_init(&c->info.minimum_dependency_set, heap_allocator());
|
isize entity_count = c->info.entities.count;
|
||||||
|
isize min_dep_set_cap = next_pow2_isize(entity_count*4); // empirically determined factor
|
||||||
|
|
||||||
|
ptr_set_init(&c->info.minimum_dependency_set, heap_allocator(), min_dep_set_cap);
|
||||||
ptr_set_init(&c->info.minimum_dependency_type_info_set, heap_allocator());
|
ptr_set_init(&c->info.minimum_dependency_type_info_set, heap_allocator());
|
||||||
|
|
||||||
String required_runtime_entities[] = {
|
String required_runtime_entities[] = {
|
||||||
@@ -1769,6 +1786,7 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
|
|||||||
str_lit("memcpy"),
|
str_lit("memcpy"),
|
||||||
str_lit("memmove"),
|
str_lit("memmove"),
|
||||||
|
|
||||||
|
str_lit("memory_equal"),
|
||||||
str_lit("memory_compare"),
|
str_lit("memory_compare"),
|
||||||
str_lit("memory_compare_zero"),
|
str_lit("memory_compare_zero"),
|
||||||
|
|
||||||
@@ -1842,7 +1860,68 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
|
|||||||
add_dependency_to_set(c, e);
|
add_dependency_to_set(c, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
add_dependency_to_set(c, start);
|
for_array(i, c->info.entities) {
|
||||||
|
Entity *e = c->info.entities[i];
|
||||||
|
switch (e->kind) {
|
||||||
|
case Entity_Variable:
|
||||||
|
if (e->Variable.is_export) {
|
||||||
|
add_dependency_to_set(c, e);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Entity_Procedure:
|
||||||
|
if (e->Procedure.is_export) {
|
||||||
|
add_dependency_to_set(c, e);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (build_context.command_kind == Command_test) {
|
||||||
|
AstPackage *pkg = c->info.init_package;
|
||||||
|
Scope *s = pkg->scope;
|
||||||
|
for_array(i, s->elements.entries) {
|
||||||
|
Entity *e = s->elements.entries[i].value;
|
||||||
|
if (e->kind != Entity_Procedure) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e->file == nullptr || !e->file->is_test) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String name = e->token.string;
|
||||||
|
String prefix = str_lit("test_");
|
||||||
|
|
||||||
|
if (!string_starts_with(name, prefix)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_tester = false;
|
||||||
|
if (name != prefix) {
|
||||||
|
is_tester = true;
|
||||||
|
} else {
|
||||||
|
error(e->token, "Invalid testing procedure name: %.*s", LIT(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
Type *t = base_type(e->type);
|
||||||
|
GB_ASSERT(t->kind == Type_Proc);
|
||||||
|
if (t->Proc.param_count == 0 && t->Proc.result_count == 0) {
|
||||||
|
// Good
|
||||||
|
} else {
|
||||||
|
gbString str = type_to_string(t);
|
||||||
|
error(e->token, "Testing procedures must have a signature type of proc(), got %s", str);
|
||||||
|
gb_string_free(str);
|
||||||
|
is_tester = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_tester) {
|
||||||
|
add_dependency_to_set(c, e);
|
||||||
|
array_add(&c->info.testing_procedures, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
add_dependency_to_set(c, start);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_entity_a_dependency(Entity *e) {
|
bool is_entity_a_dependency(Entity *e) {
|
||||||
@@ -1881,19 +1960,17 @@ void add_entity_dependency_from_procedure_parameters(Map<EntityGraphNode *> *M,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Array<EntityGraphNode *> generate_entity_dependency_graph(CheckerInfo *info) {
|
Array<EntityGraphNode *> generate_entity_dependency_graph(CheckerInfo *info, gbAllocator allocator) {
|
||||||
#define TIME_SECTION(str) do { if (build_context.show_more_timings) timings_start_section(&global_timings, str_lit(str)); } while (0)
|
#define TIME_SECTION(str) do { if (build_context.show_more_timings) timings_start_section(&global_timings, str_lit(str)); } while (0)
|
||||||
|
|
||||||
gbAllocator a = heap_allocator();
|
|
||||||
|
|
||||||
Map<EntityGraphNode *> M = {}; // Key: Entity *
|
Map<EntityGraphNode *> M = {}; // Key: Entity *
|
||||||
map_init(&M, a, info->entities.count);
|
map_init(&M, allocator, info->entities.count);
|
||||||
defer (map_destroy(&M));
|
defer (map_destroy(&M));
|
||||||
for_array(i, info->entities) {
|
for_array(i, info->entities) {
|
||||||
Entity *e = info->entities[i];
|
Entity *e = info->entities[i];
|
||||||
DeclInfo *d = e->decl_info;
|
DeclInfo *d = e->decl_info;
|
||||||
if (is_entity_a_dependency(e)) {
|
if (is_entity_a_dependency(e)) {
|
||||||
EntityGraphNode *n = gb_alloc_item(a, EntityGraphNode);
|
EntityGraphNode *n = gb_alloc_item(allocator, EntityGraphNode);
|
||||||
n->entity = e;
|
n->entity = e;
|
||||||
map_set(&M, hash_pointer(e), n);
|
map_set(&M, hash_pointer(e), n);
|
||||||
}
|
}
|
||||||
@@ -1928,7 +2005,7 @@ Array<EntityGraphNode *> generate_entity_dependency_graph(CheckerInfo *info) {
|
|||||||
// This means that the entity graph node set will have to be thread safe
|
// This means that the entity graph node set will have to be thread safe
|
||||||
|
|
||||||
TIME_SECTION("generate_entity_dependency_graph: Calculate edges for graph M - Part 2");
|
TIME_SECTION("generate_entity_dependency_graph: Calculate edges for graph M - Part 2");
|
||||||
auto G = array_make<EntityGraphNode *>(a, 0, M.entries.count);
|
auto G = array_make<EntityGraphNode *>(allocator, 0, M.entries.count);
|
||||||
|
|
||||||
for_array(i, M.entries) {
|
for_array(i, M.entries) {
|
||||||
auto *entry = &M.entries[i];
|
auto *entry = &M.entries[i];
|
||||||
@@ -1949,17 +2026,27 @@ Array<EntityGraphNode *> generate_entity_dependency_graph(CheckerInfo *info) {
|
|||||||
EntityGraphNode *s = n->succ.entries[k].ptr;
|
EntityGraphNode *s = n->succ.entries[k].ptr;
|
||||||
// Ignore self-cycles
|
// Ignore self-cycles
|
||||||
if (s != n) {
|
if (s != n) {
|
||||||
|
if (p->entity->kind == Entity_Procedure &&
|
||||||
|
s->entity->kind == Entity_Procedure) {
|
||||||
|
// NOTE(bill, 2020-11-15): Only care about variable initialization ordering
|
||||||
|
// TODO(bill): This is probably wrong!!!!
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// IMPORTANT NOTE/TODO(bill, 2020-11-15): These three calls take the majority of the
|
||||||
|
// the time to process
|
||||||
|
|
||||||
entity_graph_node_set_add(&p->succ, s);
|
entity_graph_node_set_add(&p->succ, s);
|
||||||
entity_graph_node_set_add(&s->pred, p);
|
entity_graph_node_set_add(&s->pred, p);
|
||||||
// Remove edge to 'n'
|
// Remove edge to 'n'
|
||||||
entity_graph_node_set_remove(&s->pred, n);
|
entity_graph_node_set_remove(&s->pred, n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove edge to 'n'
|
// Remove edge to 'n'
|
||||||
entity_graph_node_set_remove(&p->succ, n);
|
entity_graph_node_set_remove(&p->succ, n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if (e->kind == Entity_Variable) {
|
||||||
array_add(&G, n);
|
array_add(&G, n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1972,6 +2059,28 @@ Array<EntityGraphNode *> generate_entity_dependency_graph(CheckerInfo *info) {
|
|||||||
GB_ASSERT(n->dep_count >= 0);
|
GB_ASSERT(n->dep_count >= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// f64 succ_count = 0.0;
|
||||||
|
// f64 pred_count = 0.0;
|
||||||
|
// f64 succ_capacity = 0.0;
|
||||||
|
// f64 pred_capacity = 0.0;
|
||||||
|
// f64 succ_max = 0.0;
|
||||||
|
// f64 pred_max = 0.0;
|
||||||
|
// for_array(i, G) {
|
||||||
|
// EntityGraphNode *n = G[i];
|
||||||
|
// succ_count += n->succ.entries.count;
|
||||||
|
// pred_count += n->pred.entries.count;
|
||||||
|
// succ_capacity += n->succ.entries.capacity;
|
||||||
|
// pred_capacity += n->pred.entries.capacity;
|
||||||
|
|
||||||
|
// succ_max = gb_max(succ_max, n->succ.entries.capacity);
|
||||||
|
// pred_max = gb_max(pred_max, n->pred.entries.capacity);
|
||||||
|
|
||||||
|
// }
|
||||||
|
// f64 count = cast(f64)G.count;
|
||||||
|
// gb_printf_err(">>>count pred: %f succ: %f\n", pred_count/count, succ_count/count);
|
||||||
|
// gb_printf_err(">>>capacity pred: %f succ: %f\n", pred_capacity/count, succ_capacity/count);
|
||||||
|
// gb_printf_err(">>>max pred: %f succ: %f\n", pred_max, succ_max);
|
||||||
|
|
||||||
return G;
|
return G;
|
||||||
|
|
||||||
#undef TIME_SECTION
|
#undef TIME_SECTION
|
||||||
@@ -2088,9 +2197,9 @@ void init_core_type_info(Checker *c) {
|
|||||||
t_type_info_enum_value = type_info_enum_value->type;
|
t_type_info_enum_value = type_info_enum_value->type;
|
||||||
t_type_info_enum_value_ptr = alloc_type_pointer(t_type_info_enum_value);
|
t_type_info_enum_value_ptr = alloc_type_pointer(t_type_info_enum_value);
|
||||||
|
|
||||||
GB_ASSERT(tis->fields.count == 4);
|
GB_ASSERT(tis->fields.count == 5);
|
||||||
|
|
||||||
Entity *type_info_variant = tis->fields[3];
|
Entity *type_info_variant = tis->fields[4];
|
||||||
Type *tiv_type = type_info_variant->type;
|
Type *tiv_type = type_info_variant->type;
|
||||||
GB_ASSERT(is_type_union(tiv_type));
|
GB_ASSERT(is_type_union(tiv_type));
|
||||||
|
|
||||||
@@ -2186,14 +2295,14 @@ void init_core_source_code_location(Checker *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void init_core_map_type(Checker *c) {
|
void init_core_map_type(Checker *c) {
|
||||||
if (t_map_key == nullptr) {
|
if (t_map_hash == nullptr) {
|
||||||
Entity *e = find_core_entity(c, str_lit("Map_Key"));
|
Entity *e = find_core_entity(c, str_lit("Map_Hash"));
|
||||||
if (e->state == EntityState_Unresolved) {
|
if (e->state == EntityState_Unresolved) {
|
||||||
auto ctx = c->init_ctx;
|
auto ctx = c->init_ctx;
|
||||||
check_entity_decl(&ctx, e, nullptr, nullptr);
|
check_entity_decl(&ctx, e, nullptr, nullptr);
|
||||||
}
|
}
|
||||||
t_map_key = e->type;
|
t_map_hash = e->type;
|
||||||
GB_ASSERT(t_map_key != nullptr);
|
GB_ASSERT(t_map_hash != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (t_map_header == nullptr) {
|
if (t_map_header == nullptr) {
|
||||||
@@ -2579,7 +2688,7 @@ DECL_ATTRIBUTE_PROC(type_decl_attribute) {
|
|||||||
|
|
||||||
if (valid && build_context.use_llvm_api) {
|
if (valid && build_context.use_llvm_api) {
|
||||||
if (ac->atom_op_table == nullptr) {
|
if (ac->atom_op_table == nullptr) {
|
||||||
ac->atom_op_table = gb_alloc_item(heap_allocator(), TypeAtomOpTable);
|
ac->atom_op_table = gb_alloc_item(permanent_allocator(), TypeAtomOpTable);
|
||||||
}
|
}
|
||||||
ac->atom_op_table->op[TypeAtomOp_index_get] = e;
|
ac->atom_op_table->op[TypeAtomOp_index_get] = e;
|
||||||
}
|
}
|
||||||
@@ -2638,7 +2747,7 @@ DECL_ATTRIBUTE_PROC(type_decl_attribute) {
|
|||||||
|
|
||||||
if (valid && build_context.use_llvm_api) {
|
if (valid && build_context.use_llvm_api) {
|
||||||
if (ac->atom_op_table == nullptr) {
|
if (ac->atom_op_table == nullptr) {
|
||||||
ac->atom_op_table = gb_alloc_item(heap_allocator(), TypeAtomOpTable);
|
ac->atom_op_table = gb_alloc_item(permanent_allocator(), TypeAtomOpTable);
|
||||||
}
|
}
|
||||||
ac->atom_op_table->op[TypeAtomOp_index_set] = e;
|
ac->atom_op_table->op[TypeAtomOp_index_set] = e;
|
||||||
}
|
}
|
||||||
@@ -2720,7 +2829,7 @@ DECL_ATTRIBUTE_PROC(type_decl_attribute) {
|
|||||||
|
|
||||||
if (valid && build_context.use_llvm_api) {
|
if (valid && build_context.use_llvm_api) {
|
||||||
if (ac->atom_op_table == nullptr) {
|
if (ac->atom_op_table == nullptr) {
|
||||||
ac->atom_op_table = gb_alloc_item(heap_allocator(), TypeAtomOpTable);
|
ac->atom_op_table = gb_alloc_item(permanent_allocator(), TypeAtomOpTable);
|
||||||
}
|
}
|
||||||
ac->atom_op_table->op[TypeAtomOp_slice] = e;
|
ac->atom_op_table->op[TypeAtomOp_slice] = e;
|
||||||
}
|
}
|
||||||
@@ -2809,7 +2918,7 @@ void check_decl_attributes(CheckerContext *c, Array<Ast *> const &attributes, De
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
isize get_total_value_count(Array<Ast *> const &values) {
|
isize get_total_value_count(Slice<Ast *> const &values) {
|
||||||
isize count = 0;
|
isize count = 0;
|
||||||
for_array(i, values) {
|
for_array(i, values) {
|
||||||
Type *t = type_of_expr(values[i]);
|
Type *t = type_of_expr(values[i]);
|
||||||
@@ -2967,8 +3076,8 @@ void check_builtin_attributes(CheckerContext *ctx, Entity *e, Array<Ast *> *attr
|
|||||||
}
|
}
|
||||||
|
|
||||||
void check_collect_value_decl(CheckerContext *c, Ast *decl) {
|
void check_collect_value_decl(CheckerContext *c, Ast *decl) {
|
||||||
if (decl->been_handled) return;
|
if (decl->state_flags & StateFlag_BeenHandled) return;
|
||||||
decl->been_handled = true;
|
decl->state_flags |= StateFlag_BeenHandled;
|
||||||
|
|
||||||
ast_node(vd, ValueDecl, decl);
|
ast_node(vd, ValueDecl, decl);
|
||||||
|
|
||||||
@@ -3023,7 +3132,7 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
|
|||||||
} else {
|
} else {
|
||||||
entity_visibility_kind = kind;
|
entity_visibility_kind = kind;
|
||||||
}
|
}
|
||||||
array_unordered_remove(elems, j);
|
slice_unordered_remove(elems, j);
|
||||||
j -= 1;
|
j -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3072,7 +3181,10 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ast *init_expr = value;
|
Ast *init_expr = value;
|
||||||
DeclInfo *d = make_decl_info(heap_allocator(), c->scope, c->decl);
|
DeclInfo *d = make_decl_info(c->scope, c->decl);
|
||||||
|
d->decl_node = decl;
|
||||||
|
d->comment = vd->comment;
|
||||||
|
d->docs = vd->docs;
|
||||||
d->entity = e;
|
d->entity = e;
|
||||||
d->type_expr = vd->type;
|
d->type_expr = vd->type;
|
||||||
d->init_expr = init_expr;
|
d->init_expr = init_expr;
|
||||||
@@ -3100,9 +3212,12 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
|
|||||||
Token token = name->Ident.token;
|
Token token = name->Ident.token;
|
||||||
|
|
||||||
Ast *fl = c->foreign_context.curr_library;
|
Ast *fl = c->foreign_context.curr_library;
|
||||||
DeclInfo *d = make_decl_info(c->allocator, c->scope, c->decl);
|
|
||||||
Entity *e = nullptr;
|
Entity *e = nullptr;
|
||||||
|
DeclInfo *d = make_decl_info(c->scope, c->decl);
|
||||||
|
|
||||||
|
d->decl_node = decl;
|
||||||
|
d->comment = vd->comment;
|
||||||
|
d->docs = vd->docs;
|
||||||
d->attributes = vd->attributes;
|
d->attributes = vd->attributes;
|
||||||
d->type_expr = vd->type;
|
d->type_expr = vd->type;
|
||||||
d->init_expr = init;
|
d->init_expr = init;
|
||||||
@@ -3186,8 +3301,8 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void check_add_foreign_block_decl(CheckerContext *ctx, Ast *decl) {
|
void check_add_foreign_block_decl(CheckerContext *ctx, Ast *decl) {
|
||||||
if (decl->been_handled) return;
|
if (decl->state_flags & StateFlag_BeenHandled) return;
|
||||||
decl->been_handled = true;
|
decl->state_flags |= StateFlag_BeenHandled;
|
||||||
|
|
||||||
ast_node(fb, ForeignBlockDecl, decl);
|
ast_node(fb, ForeignBlockDecl, decl);
|
||||||
Ast *foreign_library = fb->foreign_library;
|
Ast *foreign_library = fb->foreign_library;
|
||||||
@@ -3207,7 +3322,7 @@ void check_add_foreign_block_decl(CheckerContext *ctx, Ast *decl) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NOTE(bill): If file_scopes == nullptr, this will act like a local scope
|
// NOTE(bill): If file_scopes == nullptr, this will act like a local scope
|
||||||
void check_collect_entities(CheckerContext *c, Array<Ast *> const &nodes) {
|
void check_collect_entities(CheckerContext *c, Slice<Ast *> const &nodes) {
|
||||||
for_array(decl_index, nodes) {
|
for_array(decl_index, nodes) {
|
||||||
Ast *decl = nodes[decl_index];
|
Ast *decl = nodes[decl_index];
|
||||||
if (!is_ast_decl(decl) && !is_ast_when_stmt(decl)) {
|
if (!is_ast_decl(decl) && !is_ast_when_stmt(decl)) {
|
||||||
@@ -3522,11 +3637,9 @@ struct ImportPathItem {
|
|||||||
Array<ImportPathItem> find_import_path(Checker *c, AstPackage *start, AstPackage *end, PtrSet<AstPackage *> *visited) {
|
Array<ImportPathItem> find_import_path(Checker *c, AstPackage *start, AstPackage *end, PtrSet<AstPackage *> *visited) {
|
||||||
Array<ImportPathItem> empty_path = {};
|
Array<ImportPathItem> empty_path = {};
|
||||||
|
|
||||||
if (ptr_set_exists(visited, start)) {
|
if (ptr_set_update(visited, start)) {
|
||||||
return empty_path;
|
return empty_path;
|
||||||
}
|
}
|
||||||
ptr_set_add(visited, start);
|
|
||||||
|
|
||||||
|
|
||||||
String path = start->fullpath;
|
String path = start->fullpath;
|
||||||
AstPackage **found = string_map_get(&c->info.packages, path);
|
AstPackage **found = string_map_get(&c->info.packages, path);
|
||||||
@@ -3571,8 +3684,8 @@ Array<ImportPathItem> find_import_path(Checker *c, AstPackage *start, AstPackage
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
void check_add_import_decl(CheckerContext *ctx, Ast *decl) {
|
void check_add_import_decl(CheckerContext *ctx, Ast *decl) {
|
||||||
if (decl->been_handled) return;
|
if (decl->state_flags & StateFlag_BeenHandled) return;
|
||||||
decl->been_handled = true;
|
decl->state_flags |= StateFlag_BeenHandled;
|
||||||
|
|
||||||
ast_node(id, ImportDecl, decl);
|
ast_node(id, ImportDecl, decl);
|
||||||
Token token = id->relpath;
|
Token token = id->relpath;
|
||||||
@@ -3612,10 +3725,8 @@ void check_add_import_decl(CheckerContext *ctx, Ast *decl) {
|
|||||||
GB_ASSERT(scope->flags&ScopeFlag_Pkg);
|
GB_ASSERT(scope->flags&ScopeFlag_Pkg);
|
||||||
|
|
||||||
|
|
||||||
if (ptr_set_exists(&parent_scope->imported, scope)) {
|
if (ptr_set_update(&parent_scope->imported, scope)) {
|
||||||
// error(token, "Multiple import of the same file within this scope");
|
// error(token, "Multiple import of the same file within this scope");
|
||||||
} else {
|
|
||||||
ptr_set_add(&parent_scope->imported, scope);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String import_name = path_to_entity_name(id->import_name.string, id->fullpath, false);
|
String import_name = path_to_entity_name(id->import_name.string, id->fullpath, false);
|
||||||
@@ -3686,8 +3797,8 @@ DECL_ATTRIBUTE_PROC(foreign_import_decl_attribute) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) {
|
void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) {
|
||||||
if (decl->been_handled) return;
|
if (decl->state_flags & StateFlag_BeenHandled) return;
|
||||||
decl->been_handled = true;
|
decl->state_flags |= StateFlag_BeenHandled;
|
||||||
|
|
||||||
ast_node(fl, ForeignImportDecl, decl);
|
ast_node(fl, ForeignImportDecl, decl);
|
||||||
|
|
||||||
@@ -3738,7 +3849,7 @@ void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool collect_checked_packages_from_decl_list(Checker *c, Array<Ast *> const &decls) {
|
bool collect_checked_packages_from_decl_list(Checker *c, Slice<Ast *> const &decls) {
|
||||||
bool new_files = false;
|
bool new_files = false;
|
||||||
for_array(i, decls) {
|
for_array(i, decls) {
|
||||||
Ast *decl = decls[i];
|
Ast *decl = decls[i];
|
||||||
@@ -3760,7 +3871,7 @@ bool collect_checked_packages_from_decl_list(Checker *c, Array<Ast *> const &dec
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if a new package is present
|
// Returns true if a new package is present
|
||||||
bool collect_file_decls(CheckerContext *ctx, Array<Ast *> const &decls);
|
bool collect_file_decls(CheckerContext *ctx, Slice<Ast *> const &decls);
|
||||||
bool collect_file_decls_from_when_stmt(CheckerContext *ctx, AstWhenStmt *ws);
|
bool collect_file_decls_from_when_stmt(CheckerContext *ctx, AstWhenStmt *ws);
|
||||||
|
|
||||||
bool collect_when_stmt_from_file(CheckerContext *ctx, AstWhenStmt *ws) {
|
bool collect_when_stmt_from_file(CheckerContext *ctx, AstWhenStmt *ws) {
|
||||||
@@ -3835,7 +3946,7 @@ bool collect_file_decls_from_when_stmt(CheckerContext *ctx, AstWhenStmt *ws) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool collect_file_decls(CheckerContext *ctx, Array<Ast *> const &decls) {
|
bool collect_file_decls(CheckerContext *ctx, Slice<Ast *> const &decls) {
|
||||||
GB_ASSERT(ctx->scope->flags&ScopeFlag_File);
|
GB_ASSERT(ctx->scope->flags&ScopeFlag_File);
|
||||||
|
|
||||||
if (collect_checked_packages_from_decl_list(ctx->checker, decls)) {
|
if (collect_checked_packages_from_decl_list(ctx->checker, decls)) {
|
||||||
@@ -3968,10 +4079,9 @@ void check_import_entities(Checker *c) {
|
|||||||
if (pkg == nullptr) {
|
if (pkg == nullptr) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (ptr_set_exists(&emitted, pkg)) {
|
if (ptr_set_update(&emitted, pkg)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ptr_set_add(&emitted, pkg);
|
|
||||||
|
|
||||||
array_add(&package_order, n);
|
array_add(&package_order, n);
|
||||||
}
|
}
|
||||||
@@ -4162,7 +4272,7 @@ void calculate_global_init_order(Checker *c) {
|
|||||||
CheckerInfo *info = &c->info;
|
CheckerInfo *info = &c->info;
|
||||||
|
|
||||||
TIME_SECTION("calculate_global_init_order: generate entity dependency graph");
|
TIME_SECTION("calculate_global_init_order: generate entity dependency graph");
|
||||||
Array<EntityGraphNode *> dep_graph = generate_entity_dependency_graph(info);
|
Array<EntityGraphNode *> dep_graph = generate_entity_dependency_graph(info, heap_allocator());
|
||||||
defer ({
|
defer ({
|
||||||
for_array(i, dep_graph) {
|
for_array(i, dep_graph) {
|
||||||
entity_graph_node_destroy(dep_graph[i], heap_allocator());
|
entity_graph_node_destroy(dep_graph[i], heap_allocator());
|
||||||
@@ -4214,11 +4324,9 @@ void calculate_global_init_order(Checker *c) {
|
|||||||
// if (!decl_info_has_init(d)) {
|
// if (!decl_info_has_init(d)) {
|
||||||
// continue;
|
// continue;
|
||||||
// }
|
// }
|
||||||
if (ptr_set_exists(&emitted, d)) {
|
if (ptr_set_update(&emitted, d)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ptr_set_add(&emitted, d);
|
|
||||||
|
|
||||||
|
|
||||||
array_add(&info->variable_init_order, d);
|
array_add(&info->variable_init_order, d);
|
||||||
}
|
}
|
||||||
@@ -4299,10 +4407,11 @@ void check_parsed_files(Checker *c) {
|
|||||||
for_array(i, c->parser->packages) {
|
for_array(i, c->parser->packages) {
|
||||||
AstPackage *p = c->parser->packages[i];
|
AstPackage *p = c->parser->packages[i];
|
||||||
Scope *scope = create_scope_from_package(&c->init_ctx, p);
|
Scope *scope = create_scope_from_package(&c->init_ctx, p);
|
||||||
p->decl_info = make_decl_info(c->allocator, scope, c->init_ctx.decl);
|
p->decl_info = make_decl_info(scope, c->init_ctx.decl);
|
||||||
string_map_set(&c->info.packages, p->fullpath, p);
|
string_map_set(&c->info.packages, p->fullpath, p);
|
||||||
|
|
||||||
if (scope->flags&ScopeFlag_Init) {
|
if (scope->flags&ScopeFlag_Init) {
|
||||||
|
c->info.init_package = p;
|
||||||
c->info.init_scope = scope;
|
c->info.init_scope = scope;
|
||||||
}
|
}
|
||||||
if (p->kind == Package_Runtime) {
|
if (p->kind == Package_Runtime) {
|
||||||
@@ -4572,7 +4681,7 @@ void check_parsed_files(Checker *c) {
|
|||||||
|
|
||||||
|
|
||||||
TIME_SECTION("check entry point");
|
TIME_SECTION("check entry point");
|
||||||
if (build_context.build_mode == BuildMode_Executable) {
|
if (build_context.build_mode == BuildMode_Executable && !build_context.no_entry_point && build_context.command_kind != Command_test) {
|
||||||
Scope *s = c->info.init_scope;
|
Scope *s = c->info.init_scope;
|
||||||
GB_ASSERT(s != nullptr);
|
GB_ASSERT(s != nullptr);
|
||||||
GB_ASSERT(s->flags&ScopeFlag_Init);
|
GB_ASSERT(s->flags&ScopeFlag_Init);
|
||||||
|
|||||||
+20
-12
@@ -45,7 +45,7 @@ enum StmtFlag {
|
|||||||
|
|
||||||
Stmt_TypeSwitch = 1<<4,
|
Stmt_TypeSwitch = 1<<4,
|
||||||
|
|
||||||
Stmt_CheckScopeDecls = 1<<5,
|
Stmt_CheckScopeDecls = 1<<5,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum BuiltinProcPkg {
|
enum BuiltinProcPkg {
|
||||||
@@ -132,6 +132,7 @@ struct DeclInfo {
|
|||||||
|
|
||||||
Entity *entity;
|
Entity *entity;
|
||||||
|
|
||||||
|
Ast * decl_node;
|
||||||
Ast * type_expr;
|
Ast * type_expr;
|
||||||
Ast * init_expr;
|
Ast * init_expr;
|
||||||
Array<Ast *> attributes;
|
Array<Ast *> attributes;
|
||||||
@@ -140,6 +141,9 @@ struct DeclInfo {
|
|||||||
bool is_using;
|
bool is_using;
|
||||||
bool where_clauses_evaluated;
|
bool where_clauses_evaluated;
|
||||||
|
|
||||||
|
CommentGroup *comment;
|
||||||
|
CommentGroup *docs;
|
||||||
|
|
||||||
PtrSet<Entity *> deps;
|
PtrSet<Entity *> deps;
|
||||||
PtrSet<Type *> type_info_deps;
|
PtrSet<Type *> type_info_deps;
|
||||||
Array<BlockLabel> labels;
|
Array<BlockLabel> labels;
|
||||||
@@ -160,12 +164,13 @@ struct ProcInfo {
|
|||||||
|
|
||||||
|
|
||||||
enum ScopeFlag : i32 {
|
enum ScopeFlag : i32 {
|
||||||
ScopeFlag_Pkg = 1<<1,
|
ScopeFlag_Pkg = 1<<1,
|
||||||
ScopeFlag_Global = 1<<2,
|
ScopeFlag_Builtin = 1<<2,
|
||||||
ScopeFlag_File = 1<<3,
|
ScopeFlag_Global = 1<<3,
|
||||||
ScopeFlag_Init = 1<<4,
|
ScopeFlag_File = 1<<4,
|
||||||
ScopeFlag_Proc = 1<<5,
|
ScopeFlag_Init = 1<<5,
|
||||||
ScopeFlag_Type = 1<<6,
|
ScopeFlag_Proc = 1<<6,
|
||||||
|
ScopeFlag_Type = 1<<7,
|
||||||
|
|
||||||
ScopeFlag_HasBeenImported = 1<<10, // This is only applicable to file scopes
|
ScopeFlag_HasBeenImported = 1<<10, // This is only applicable to file scopes
|
||||||
|
|
||||||
@@ -247,8 +252,12 @@ struct AtomOpMapEntry {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct CheckerContext;
|
||||||
|
|
||||||
// CheckerInfo stores all the symbol information for a type-checked program
|
// CheckerInfo stores all the symbol information for a type-checked program
|
||||||
struct CheckerInfo {
|
struct CheckerInfo {
|
||||||
|
Checker *checker;
|
||||||
|
|
||||||
Map<ExprInfo> untyped; // Key: Ast * | Expression -> ExprInfo
|
Map<ExprInfo> untyped; // Key: Ast * | Expression -> ExprInfo
|
||||||
// NOTE(bill): This needs to be a map and not on the Ast
|
// NOTE(bill): This needs to be a map and not on the Ast
|
||||||
// as it needs to be iterated across
|
// as it needs to be iterated across
|
||||||
@@ -268,6 +277,7 @@ struct CheckerInfo {
|
|||||||
|
|
||||||
AstPackage * builtin_package;
|
AstPackage * builtin_package;
|
||||||
AstPackage * runtime_package;
|
AstPackage * runtime_package;
|
||||||
|
AstPackage * init_package;
|
||||||
Scope * init_scope;
|
Scope * init_scope;
|
||||||
Entity * entry_point;
|
Entity * entry_point;
|
||||||
PtrSet<Entity *> minimum_dependency_set;
|
PtrSet<Entity *> minimum_dependency_set;
|
||||||
@@ -278,6 +288,7 @@ struct CheckerInfo {
|
|||||||
|
|
||||||
Map<AtomOpMapEntry> atom_op_map; // Key: Ast *
|
Map<AtomOpMapEntry> atom_op_map; // Key: Ast *
|
||||||
|
|
||||||
|
Array<Entity *> testing_procedures;
|
||||||
|
|
||||||
bool allow_identifier_uses;
|
bool allow_identifier_uses;
|
||||||
Array<Ast *> identifier_uses; // only used by 'odin query'
|
Array<Ast *> identifier_uses; // only used by 'odin query'
|
||||||
@@ -301,7 +312,6 @@ struct CheckerContext {
|
|||||||
ProcCallingConvention curr_proc_calling_convention;
|
ProcCallingConvention curr_proc_calling_convention;
|
||||||
bool in_proc_sig;
|
bool in_proc_sig;
|
||||||
ForeignContext foreign_context;
|
ForeignContext foreign_context;
|
||||||
gbAllocator allocator;
|
|
||||||
|
|
||||||
CheckerTypePath *type_path;
|
CheckerTypePath *type_path;
|
||||||
isize type_level; // TODO(bill): Actually handle correctly
|
isize type_level; // TODO(bill): Actually handle correctly
|
||||||
@@ -317,6 +327,7 @@ struct CheckerContext {
|
|||||||
bool no_polymorphic_errors;
|
bool no_polymorphic_errors;
|
||||||
bool hide_polymorphic_errors;
|
bool hide_polymorphic_errors;
|
||||||
bool in_polymorphic_specialization;
|
bool in_polymorphic_specialization;
|
||||||
|
bool allow_arrow_right_selector_expr;
|
||||||
Scope * polymorphic_scope;
|
Scope * polymorphic_scope;
|
||||||
|
|
||||||
Ast *assignment_lhs_hint;
|
Ast *assignment_lhs_hint;
|
||||||
@@ -331,14 +342,11 @@ struct Checker {
|
|||||||
Array<Entity *> procs_with_deferred_to_check;
|
Array<Entity *> procs_with_deferred_to_check;
|
||||||
|
|
||||||
CheckerContext *curr_ctx;
|
CheckerContext *curr_ctx;
|
||||||
gbAllocator allocator;
|
|
||||||
CheckerContext init_ctx;
|
CheckerContext init_ctx;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
gb_global AstPackage *builtin_pkg = nullptr;
|
gb_global AstPackage *builtin_pkg = nullptr;
|
||||||
gb_global AstPackage *intrinsics_pkg = nullptr;
|
gb_global AstPackage *intrinsics_pkg = nullptr;
|
||||||
gb_global AstPackage *config_pkg = nullptr;
|
gb_global AstPackage *config_pkg = nullptr;
|
||||||
@@ -387,7 +395,7 @@ void check_add_foreign_import_decl(CheckerContext *c, Ast *decl);
|
|||||||
|
|
||||||
|
|
||||||
bool check_arity_match(CheckerContext *c, AstValueDecl *vd, bool is_global = false);
|
bool check_arity_match(CheckerContext *c, AstValueDecl *vd, bool is_global = false);
|
||||||
void check_collect_entities(CheckerContext *c, Array<Ast *> const &nodes);
|
void check_collect_entities(CheckerContext *c, Slice<Ast *> const &nodes);
|
||||||
void check_collect_entities_from_when_stmt(CheckerContext *c, AstWhenStmt *ws);
|
void check_collect_entities_from_when_stmt(CheckerContext *c, AstWhenStmt *ws);
|
||||||
void check_delayed_file_import_entity(CheckerContext *c, Ast *decl);
|
void check_delayed_file_import_entity(CheckerContext *c, Ast *decl);
|
||||||
|
|
||||||
|
|||||||
@@ -183,6 +183,9 @@ BuiltinProc__type_simple_boolean_end,
|
|||||||
|
|
||||||
BuiltinProc_type_field_index_of,
|
BuiltinProc_type_field_index_of,
|
||||||
|
|
||||||
|
BuiltinProc_type_equal_proc,
|
||||||
|
BuiltinProc_type_hasher_proc,
|
||||||
|
|
||||||
BuiltinProc__type_end,
|
BuiltinProc__type_end,
|
||||||
|
|
||||||
|
|
||||||
@@ -367,5 +370,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
|
|||||||
|
|
||||||
{STR_LIT("type_field_index_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
{STR_LIT("type_field_index_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||||
|
|
||||||
|
{STR_LIT("type_equal_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||||
|
{STR_LIT("type_hasher_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||||
|
|
||||||
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
|
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
|
||||||
};
|
};
|
||||||
|
|||||||
+132
-9
@@ -56,6 +56,14 @@ gb_inline isize align_formula_isize(isize size, isize align) {
|
|||||||
}
|
}
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
gb_inline void *align_formula_ptr(void *ptr, isize align) {
|
||||||
|
if (align > 0) {
|
||||||
|
uintptr result = (cast(uintptr)ptr) + align-1;
|
||||||
|
return (void *)(result - result%align);
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
GB_ALLOCATOR_PROC(heap_allocator_proc);
|
GB_ALLOCATOR_PROC(heap_allocator_proc);
|
||||||
|
|
||||||
@@ -373,13 +381,16 @@ typedef struct Arena {
|
|||||||
gbAllocator backing;
|
gbAllocator backing;
|
||||||
isize block_size;
|
isize block_size;
|
||||||
gbMutex mutex;
|
gbMutex mutex;
|
||||||
|
|
||||||
isize total_used;
|
isize total_used;
|
||||||
|
bool use_mutex;
|
||||||
} Arena;
|
} Arena;
|
||||||
|
|
||||||
#define ARENA_MIN_ALIGNMENT 16
|
#define ARENA_MIN_ALIGNMENT 16
|
||||||
#define ARENA_DEFAULT_BLOCK_SIZE (8*1024*1024)
|
#define ARENA_DEFAULT_BLOCK_SIZE (8*1024*1024)
|
||||||
|
|
||||||
|
|
||||||
|
gb_global Arena permanent_arena = {};
|
||||||
|
|
||||||
void arena_init(Arena *arena, gbAllocator backing, isize block_size=ARENA_DEFAULT_BLOCK_SIZE) {
|
void arena_init(Arena *arena, gbAllocator backing, isize block_size=ARENA_DEFAULT_BLOCK_SIZE) {
|
||||||
arena->backing = backing;
|
arena->backing = backing;
|
||||||
arena->block_size = block_size;
|
arena->block_size = block_size;
|
||||||
@@ -388,8 +399,9 @@ void arena_init(Arena *arena, gbAllocator backing, isize block_size=ARENA_DEFAUL
|
|||||||
}
|
}
|
||||||
|
|
||||||
void arena_grow(Arena *arena, isize min_size) {
|
void arena_grow(Arena *arena, isize min_size) {
|
||||||
// gb_mutex_lock(&arena->mutex);
|
if (arena->use_mutex) {
|
||||||
// defer (gb_mutex_unlock(&arena->mutex));
|
gb_mutex_lock(&arena->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
isize size = gb_max(arena->block_size, min_size);
|
isize size = gb_max(arena->block_size, min_size);
|
||||||
size = ALIGN_UP(size, ARENA_MIN_ALIGNMENT);
|
size = ALIGN_UP(size, ARENA_MIN_ALIGNMENT);
|
||||||
@@ -399,11 +411,16 @@ void arena_grow(Arena *arena, isize min_size) {
|
|||||||
GB_ASSERT(arena->ptr == ALIGN_DOWN_PTR(arena->ptr, ARENA_MIN_ALIGNMENT));
|
GB_ASSERT(arena->ptr == ALIGN_DOWN_PTR(arena->ptr, ARENA_MIN_ALIGNMENT));
|
||||||
arena->end = arena->ptr + size;
|
arena->end = arena->ptr + size;
|
||||||
array_add(&arena->blocks, arena->ptr);
|
array_add(&arena->blocks, arena->ptr);
|
||||||
|
|
||||||
|
if (arena->use_mutex) {
|
||||||
|
gb_mutex_unlock(&arena->mutex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void *arena_alloc(Arena *arena, isize size, isize alignment) {
|
void *arena_alloc(Arena *arena, isize size, isize alignment) {
|
||||||
// gb_mutex_lock(&arena->mutex);
|
if (arena->use_mutex) {
|
||||||
// defer (gb_mutex_unlock(&arena->mutex));
|
gb_mutex_lock(&arena->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
arena->total_used += size;
|
arena->total_used += size;
|
||||||
|
|
||||||
@@ -419,12 +436,17 @@ void *arena_alloc(Arena *arena, isize size, isize alignment) {
|
|||||||
GB_ASSERT(arena->ptr <= arena->end);
|
GB_ASSERT(arena->ptr <= arena->end);
|
||||||
GB_ASSERT(ptr == ALIGN_DOWN_PTR(ptr, align));
|
GB_ASSERT(ptr == ALIGN_DOWN_PTR(ptr, align));
|
||||||
// zero_size(ptr, size);
|
// zero_size(ptr, size);
|
||||||
|
|
||||||
|
if (arena->use_mutex) {
|
||||||
|
gb_mutex_unlock(&arena->mutex);
|
||||||
|
}
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void arena_free_all(Arena *arena) {
|
void arena_free_all(Arena *arena) {
|
||||||
// gb_mutex_lock(&arena->mutex);
|
if (arena->use_mutex) {
|
||||||
// defer (gb_mutex_unlock(&arena->mutex));
|
gb_mutex_lock(&arena->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
for_array(i, arena->blocks) {
|
for_array(i, arena->blocks) {
|
||||||
gb_free(arena->backing, arena->blocks[i]);
|
gb_free(arena->backing, arena->blocks[i]);
|
||||||
@@ -432,8 +454,11 @@ void arena_free_all(Arena *arena) {
|
|||||||
array_clear(&arena->blocks);
|
array_clear(&arena->blocks);
|
||||||
arena->ptr = nullptr;
|
arena->ptr = nullptr;
|
||||||
arena->end = nullptr;
|
arena->end = nullptr;
|
||||||
}
|
|
||||||
|
|
||||||
|
if (arena->use_mutex) {
|
||||||
|
gb_mutex_unlock(&arena->mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -460,7 +485,14 @@ GB_ALLOCATOR_PROC(arena_allocator_proc) {
|
|||||||
// GB_PANIC("gbAllocation_Free not supported");
|
// GB_PANIC("gbAllocation_Free not supported");
|
||||||
break;
|
break;
|
||||||
case gbAllocation_Resize:
|
case gbAllocation_Resize:
|
||||||
GB_PANIC("gbAllocation_Resize: not supported");
|
if (size == 0) {
|
||||||
|
ptr = nullptr;
|
||||||
|
} else if (size <= old_size) {
|
||||||
|
ptr = old_memory;
|
||||||
|
} else {
|
||||||
|
ptr = arena_alloc(arena, size, alignment);
|
||||||
|
gb_memmove(ptr, old_memory, old_size);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case gbAllocation_FreeAll:
|
case gbAllocation_FreeAll:
|
||||||
arena_free_all(arena);
|
arena_free_all(arena);
|
||||||
@@ -471,6 +503,97 @@ GB_ALLOCATOR_PROC(arena_allocator_proc) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
gbAllocator permanent_allocator() {
|
||||||
|
return arena_allocator(&permanent_arena);
|
||||||
|
// return heap_allocator();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct Temp_Allocator {
|
||||||
|
u8 *data;
|
||||||
|
isize len;
|
||||||
|
isize curr_offset;
|
||||||
|
gbAllocator backup_allocator;
|
||||||
|
Array<void *> leaked_allocations;
|
||||||
|
};
|
||||||
|
|
||||||
|
gb_global Temp_Allocator temporary_allocator_data = {};
|
||||||
|
|
||||||
|
void temp_allocator_init(Temp_Allocator *s, isize size) {
|
||||||
|
s->backup_allocator = heap_allocator();
|
||||||
|
s->data = cast(u8 *)gb_alloc_align(s->backup_allocator, size, 16);
|
||||||
|
s->curr_offset = 0;
|
||||||
|
s->leaked_allocations.allocator = s->backup_allocator;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *temp_allocator_alloc(Temp_Allocator *s, isize size, isize alignment) {
|
||||||
|
size = align_formula_isize(size, alignment);
|
||||||
|
if (s->curr_offset+size <= s->len) {
|
||||||
|
u8 *start = s->data;
|
||||||
|
u8 *ptr = start + s->curr_offset;
|
||||||
|
ptr = cast(u8 *)align_formula_ptr(ptr, alignment);
|
||||||
|
// assume memory is zero
|
||||||
|
|
||||||
|
isize offset = ptr - start;
|
||||||
|
s->curr_offset = offset + size;
|
||||||
|
return ptr;
|
||||||
|
} else if (size <= s->len) {
|
||||||
|
u8 *start = s->data;
|
||||||
|
u8 *ptr = cast(u8 *)align_formula_ptr(start, alignment);
|
||||||
|
// assume memory is zero
|
||||||
|
|
||||||
|
isize offset = ptr - start;
|
||||||
|
s->curr_offset = offset + size;
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *ptr = gb_alloc_align(s->backup_allocator, size, alignment);
|
||||||
|
array_add(&s->leaked_allocations, ptr);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void temp_allocator_free_all(Temp_Allocator *s) {
|
||||||
|
s->curr_offset = 0;
|
||||||
|
for_array(i, s->leaked_allocations) {
|
||||||
|
gb_free(s->backup_allocator, s->leaked_allocations[i]);
|
||||||
|
}
|
||||||
|
array_clear(&s->leaked_allocations);
|
||||||
|
gb_zero_size(s->data, s->len);
|
||||||
|
}
|
||||||
|
|
||||||
|
GB_ALLOCATOR_PROC(temp_allocator_proc) {
|
||||||
|
void *ptr = nullptr;
|
||||||
|
Temp_Allocator *s = cast(Temp_Allocator *)allocator_data;
|
||||||
|
GB_ASSERT_NOT_NULL(s);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case gbAllocation_Alloc:
|
||||||
|
return temp_allocator_alloc(s, size, alignment);
|
||||||
|
case gbAllocation_Free:
|
||||||
|
break;
|
||||||
|
case gbAllocation_Resize:
|
||||||
|
if (size == 0) {
|
||||||
|
ptr = nullptr;
|
||||||
|
} else if (size <= old_size) {
|
||||||
|
ptr = old_memory;
|
||||||
|
} else {
|
||||||
|
ptr = temp_allocator_alloc(s, size, alignment);
|
||||||
|
gb_memmove(ptr, old_memory, old_size);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case gbAllocation_FreeAll:
|
||||||
|
temp_allocator_free_all(s);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
gbAllocator temporary_allocator() {
|
||||||
|
return {temp_allocator_proc, &temporary_allocator_data};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+293
-81
@@ -1,22 +1,102 @@
|
|||||||
// Generates Documentation
|
// Generates Documentation
|
||||||
|
|
||||||
gbString expr_to_string(Ast *expression);
|
gb_global int print_entity_kind_ordering[Entity_Count] = {
|
||||||
|
/*Invalid*/ -1,
|
||||||
|
/*Constant*/ 0,
|
||||||
|
/*Variable*/ 1,
|
||||||
|
/*TypeName*/ 4,
|
||||||
|
/*Procedure*/ 2,
|
||||||
|
/*ProcGroup*/ 3,
|
||||||
|
/*Builtin*/ -1,
|
||||||
|
/*ImportName*/ -1,
|
||||||
|
/*LibraryName*/ -1,
|
||||||
|
/*Nil*/ -1,
|
||||||
|
/*Label*/ -1,
|
||||||
|
};
|
||||||
|
gb_global char const *print_entity_names[Entity_Count] = {
|
||||||
|
/*Invalid*/ "",
|
||||||
|
/*Constant*/ "constants",
|
||||||
|
/*Variable*/ "variables",
|
||||||
|
/*TypeName*/ "types",
|
||||||
|
/*Procedure*/ "procedures",
|
||||||
|
/*ProcGroup*/ "proc_group",
|
||||||
|
/*Builtin*/ "",
|
||||||
|
/*ImportName*/ "import names",
|
||||||
|
/*LibraryName*/ "library names",
|
||||||
|
/*Nil*/ "",
|
||||||
|
/*Label*/ "",
|
||||||
|
};
|
||||||
|
|
||||||
String alloc_comment_group_string(gbAllocator a, CommentGroup g) {
|
|
||||||
|
GB_COMPARE_PROC(cmp_entities_for_printing) {
|
||||||
|
GB_ASSERT(a != nullptr);
|
||||||
|
GB_ASSERT(b != nullptr);
|
||||||
|
Entity *x = *cast(Entity **)a;
|
||||||
|
Entity *y = *cast(Entity **)b;
|
||||||
|
int res = 0;
|
||||||
|
res = string_compare(x->pkg->name, y->pkg->name);
|
||||||
|
if (res != 0) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
int ox = print_entity_kind_ordering[x->kind];
|
||||||
|
int oy = print_entity_kind_ordering[y->kind];
|
||||||
|
res = ox - oy;
|
||||||
|
if (res != 0) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
res = string_compare(x->token.string, y->token.string);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
GB_COMPARE_PROC(cmp_ast_package_by_name) {
|
||||||
|
GB_ASSERT(a != nullptr);
|
||||||
|
GB_ASSERT(b != nullptr);
|
||||||
|
AstPackage *x = *cast(AstPackage **)a;
|
||||||
|
AstPackage *y = *cast(AstPackage **)b;
|
||||||
|
return string_compare(x->name, y->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_doc_line(i32 indent, char const *fmt, ...) {
|
||||||
|
while (indent --> 0) {
|
||||||
|
gb_printf("\t");
|
||||||
|
}
|
||||||
|
va_list va;
|
||||||
|
va_start(va, fmt);
|
||||||
|
gb_printf_va(fmt, va);
|
||||||
|
va_end(va);
|
||||||
|
gb_printf("\n");
|
||||||
|
}
|
||||||
|
void print_doc_line_no_newline(i32 indent, char const *fmt, ...) {
|
||||||
|
while (indent --> 0) {
|
||||||
|
gb_printf("\t");
|
||||||
|
}
|
||||||
|
va_list va;
|
||||||
|
va_start(va, fmt);
|
||||||
|
gb_printf_va(fmt, va);
|
||||||
|
va_end(va);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool print_doc_comment_group_string(i32 indent, CommentGroup *g) {
|
||||||
|
if (g == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
isize len = 0;
|
isize len = 0;
|
||||||
for_array(i, g.list) {
|
for_array(i, g->list) {
|
||||||
String comment = g.list[i].string;
|
String comment = g->list[i].string;
|
||||||
len += comment.len;
|
len += comment.len;
|
||||||
len += 1; // for \n
|
len += 1; // for \n
|
||||||
}
|
}
|
||||||
if (len == 0) {
|
if (len <= g->list.count) {
|
||||||
return make_string(nullptr, 0);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 *text = gb_alloc_array(a, u8, len+1);
|
isize count = 0;
|
||||||
len = 0;
|
for_array(i, g->list) {
|
||||||
for_array(i, g.list) {
|
String comment = g->list[i].string;
|
||||||
String comment = g.list[i].string;
|
String original_comment = comment;
|
||||||
|
|
||||||
|
bool slash_slash = comment[1] == '/';
|
||||||
|
bool slash_star = comment[1] == '*';
|
||||||
if (comment[1] == '/') {
|
if (comment[1] == '/') {
|
||||||
comment.text += 2;
|
comment.text += 2;
|
||||||
comment.len -= 2;
|
comment.len -= 2;
|
||||||
@@ -24,84 +104,216 @@ String alloc_comment_group_string(gbAllocator a, CommentGroup g) {
|
|||||||
comment.text += 2;
|
comment.text += 2;
|
||||||
comment.len -= 4;
|
comment.len -= 4;
|
||||||
}
|
}
|
||||||
comment = string_trim_whitespace(comment);
|
|
||||||
gb_memmove(text+len, comment.text, comment.len);
|
|
||||||
len += comment.len;
|
|
||||||
text[len++] = '\n';
|
|
||||||
}
|
|
||||||
return make_string(text, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
// Ignore the first space
|
||||||
void print_type_spec(Ast *spec) {
|
if (comment.len > 0 && comment[0] == ' ') {
|
||||||
ast_node(ts, TypeSpec, spec);
|
comment.text += 1;
|
||||||
GB_ASSERT(ts->name->kind == Ast_Ident);
|
comment.len -= 1;
|
||||||
String name = ts->name->Ident.string;
|
}
|
||||||
if (name.len == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (name[0] == '_') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
gb_printf("type %.*s\n", LIT(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_proc_decl(AstProcDecl *pd) {
|
if (slash_slash) {
|
||||||
GB_ASSERT(pd->name->kind == Ast_Ident);
|
if (string_starts_with(comment, str_lit("+"))) {
|
||||||
String name = pd->name->Ident.string;
|
continue;
|
||||||
if (name.len == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (name[0] == '_') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String docs = alloc_comment_group_string(heap_allocator(), pd->docs);
|
|
||||||
defer (gb_free(heap_allocator(), docs.text));
|
|
||||||
|
|
||||||
if (docs.len > 0) {
|
|
||||||
gb_file_write(&gb__std_files[gbFileStandard_Output], docs.text, docs.len);
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ast_node(proc_type, ProcType, pd->type);
|
|
||||||
|
|
||||||
gbString params = expr_to_string(proc_type->params);
|
|
||||||
defer (gb_string_free(params));
|
|
||||||
gb_printf("proc %.*s(%s)", LIT(name), params);
|
|
||||||
if (proc_type->results != nullptr) {
|
|
||||||
ast_node(fl, FieldList, proc_type->results);
|
|
||||||
isize count = fl->list.count;
|
|
||||||
if (count > 0) {
|
|
||||||
gbString results = expr_to_string(proc_type->results);
|
|
||||||
defer (gb_string_free(results));
|
|
||||||
gb_printf(" -> ");
|
|
||||||
if (count != 1) {
|
|
||||||
gb_printf("(");
|
|
||||||
}
|
}
|
||||||
gb_printf("%s", results);
|
if (string_starts_with(comment, str_lit("@("))) {
|
||||||
if (count != 1) {
|
continue;
|
||||||
gb_printf(")");
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slash_slash) {
|
||||||
|
print_doc_line(indent, "%.*s", LIT(comment));
|
||||||
|
count += 1;
|
||||||
|
} else {
|
||||||
|
isize pos = 0;
|
||||||
|
for (; pos < comment.len; pos++) {
|
||||||
|
isize end = pos;
|
||||||
|
for (; end < comment.len; end++) {
|
||||||
|
if (comment[end] == '\n') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String line = substring(comment, pos, end);
|
||||||
|
pos = end+1;
|
||||||
|
String trimmed_line = string_trim_whitespace(line);
|
||||||
|
if (trimmed_line.len == 0) {
|
||||||
|
if (count == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Remove comments with
|
||||||
|
* styles
|
||||||
|
* like this
|
||||||
|
*/
|
||||||
|
if (string_starts_with(line, str_lit("* "))) {
|
||||||
|
line = substring(line, 2, line.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
print_doc_line(indent, "%.*s", LIT(line));
|
||||||
|
count += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gb_printf("\n\n");
|
|
||||||
}
|
if (count > 0) {
|
||||||
#endif
|
print_doc_line(0, "");
|
||||||
void print_declaration(Ast *decl) {
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void generate_documentation(Parser *parser) {
|
|
||||||
// for_array(file_index, parser->files) {
|
|
||||||
// AstFile *file = parser->files[file_index];
|
|
||||||
// Tokenizer *tokenizer = &file->tokenizer;
|
|
||||||
// String fullpath = tokenizer->fullpath;
|
|
||||||
// gb_printf("%.*s\n", LIT(fullpath));
|
|
||||||
|
|
||||||
// for_array(decl_index, file->decls) {
|
|
||||||
// Ast *decl = file->decls[decl_index];
|
|
||||||
// print_declaration(decl);
|
void print_doc_expr(Ast *expr) {
|
||||||
// }
|
gbString s = nullptr;
|
||||||
// }
|
if (build_context.cmd_doc_flags & CmdDocFlag_Short) {
|
||||||
|
s = expr_to_string_shorthand(expr);
|
||||||
|
} else {
|
||||||
|
s = expr_to_string(expr);
|
||||||
|
}
|
||||||
|
gb_file_write(gb_file_get_standard(gbFileStandard_Output), s, gb_string_length(s));
|
||||||
|
gb_string_free(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void print_doc_package(CheckerInfo *info, AstPackage *pkg) {
|
||||||
|
if (pkg == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
print_doc_line(0, "package %.*s", LIT(pkg->name));
|
||||||
|
|
||||||
|
|
||||||
|
for_array(i, pkg->files) {
|
||||||
|
AstFile *f = pkg->files[i];
|
||||||
|
if (f->pkg_decl) {
|
||||||
|
GB_ASSERT(f->pkg_decl->kind == Ast_PackageDecl);
|
||||||
|
print_doc_comment_group_string(1, f->pkg_decl->PackageDecl.docs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pkg->scope != nullptr) {
|
||||||
|
auto entities = array_make<Entity *>(heap_allocator(), 0, pkg->scope->elements.entries.count);
|
||||||
|
defer (array_free(&entities));
|
||||||
|
for_array(i, pkg->scope->elements.entries) {
|
||||||
|
Entity *e = pkg->scope->elements.entries[i].value;
|
||||||
|
switch (e->kind) {
|
||||||
|
case Entity_Invalid:
|
||||||
|
case Entity_Builtin:
|
||||||
|
case Entity_Nil:
|
||||||
|
case Entity_Label:
|
||||||
|
continue;
|
||||||
|
case Entity_Constant:
|
||||||
|
case Entity_Variable:
|
||||||
|
case Entity_TypeName:
|
||||||
|
case Entity_Procedure:
|
||||||
|
case Entity_ProcGroup:
|
||||||
|
case Entity_ImportName:
|
||||||
|
case Entity_LibraryName:
|
||||||
|
// Fine
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
array_add(&entities, e);
|
||||||
|
}
|
||||||
|
gb_sort_array(entities.data, entities.count, cmp_entities_for_printing);
|
||||||
|
|
||||||
|
bool show_docs = (build_context.cmd_doc_flags & CmdDocFlag_Short) == 0;
|
||||||
|
|
||||||
|
EntityKind curr_entity_kind = Entity_Invalid;
|
||||||
|
for_array(i, entities) {
|
||||||
|
Entity *e = entities[i];
|
||||||
|
if (e->pkg != pkg) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!is_entity_exported(e)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (curr_entity_kind != e->kind) {
|
||||||
|
if (curr_entity_kind != Entity_Invalid) {
|
||||||
|
print_doc_line(0, "");
|
||||||
|
}
|
||||||
|
curr_entity_kind = e->kind;
|
||||||
|
print_doc_line(1, "%s", print_entity_names[e->kind]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast *type_expr = nullptr;
|
||||||
|
Ast *init_expr = nullptr;
|
||||||
|
Ast *decl_node = nullptr;
|
||||||
|
CommentGroup *comment = nullptr;
|
||||||
|
CommentGroup *docs = nullptr;
|
||||||
|
if (e->decl_info != nullptr) {
|
||||||
|
type_expr = e->decl_info->type_expr;
|
||||||
|
init_expr = e->decl_info->init_expr;
|
||||||
|
decl_node = e->decl_info->decl_node;
|
||||||
|
comment = e->decl_info->comment;
|
||||||
|
docs = e->decl_info->docs;
|
||||||
|
}
|
||||||
|
GB_ASSERT(type_expr != nullptr || init_expr != nullptr);
|
||||||
|
|
||||||
|
print_doc_line_no_newline(2, "%.*s", LIT(e->token.string));
|
||||||
|
if (type_expr != nullptr) {
|
||||||
|
gbString t = expr_to_string(type_expr);
|
||||||
|
gb_printf(": %s ", t);
|
||||||
|
gb_string_free(t);
|
||||||
|
} else {
|
||||||
|
gb_printf(" :");
|
||||||
|
}
|
||||||
|
if (e->kind == Entity_Variable) {
|
||||||
|
if (init_expr != nullptr) {
|
||||||
|
gb_printf("= ");
|
||||||
|
print_doc_expr(init_expr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
gb_printf(": ");
|
||||||
|
print_doc_expr(init_expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_printf(";\n");
|
||||||
|
|
||||||
|
if (show_docs) {
|
||||||
|
print_doc_comment_group_string(3, docs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print_doc_line(0, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pkg->fullpath.len != 0) {
|
||||||
|
print_doc_line(0, "");
|
||||||
|
print_doc_line(1, "fullpath:");
|
||||||
|
print_doc_line(2, "%.*s", LIT(pkg->fullpath));
|
||||||
|
print_doc_line(1, "files:");
|
||||||
|
for_array(i, pkg->files) {
|
||||||
|
AstFile *f = pkg->files[i];
|
||||||
|
String filename = remove_directory_from_path(f->fullpath);
|
||||||
|
print_doc_line(2, "%.*s", LIT(filename));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void generate_documentation(Checker *c) {
|
||||||
|
CheckerInfo *info = &c->info;
|
||||||
|
|
||||||
|
auto pkgs = array_make<AstPackage *>(permanent_allocator(), 0, info->packages.entries.count);
|
||||||
|
for_array(i, info->packages.entries) {
|
||||||
|
AstPackage *pkg = info->packages.entries[i].value;
|
||||||
|
if (build_context.cmd_doc_flags & CmdDocFlag_AllPackages) {
|
||||||
|
array_add(&pkgs, pkg);
|
||||||
|
} else {
|
||||||
|
if (pkg->kind == Package_Init) {
|
||||||
|
array_add(&pkgs, pkg);
|
||||||
|
} else if (pkg->is_extra) {
|
||||||
|
array_add(&pkgs, pkg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_sort_array(pkgs.data, pkgs.count, cmp_ast_package_by_name);
|
||||||
|
|
||||||
|
for_array(i, pkgs) {
|
||||||
|
print_doc_package(info, pkgs[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-3
@@ -120,6 +120,7 @@ struct Entity {
|
|||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
ExactValue value;
|
ExactValue value;
|
||||||
|
ParameterValue param_value;
|
||||||
} Constant;
|
} Constant;
|
||||||
struct {
|
struct {
|
||||||
Ast *init_expr; // only used for some variables within procedure bodies
|
Ast *init_expr; // only used for some variables within procedure bodies
|
||||||
@@ -164,7 +165,7 @@ struct Entity {
|
|||||||
Scope *scope;
|
Scope *scope;
|
||||||
} ImportName;
|
} ImportName;
|
||||||
struct {
|
struct {
|
||||||
Array<String> paths;
|
Slice<String> paths;
|
||||||
String name;
|
String name;
|
||||||
} LibraryName;
|
} LibraryName;
|
||||||
i32 Nil;
|
i32 Nil;
|
||||||
@@ -219,7 +220,7 @@ bool entity_has_deferred_procedure(Entity *e) {
|
|||||||
gb_global u64 global_entity_id = 0;
|
gb_global u64 global_entity_id = 0;
|
||||||
|
|
||||||
Entity *alloc_entity(EntityKind kind, Scope *scope, Token token, Type *type) {
|
Entity *alloc_entity(EntityKind kind, Scope *scope, Token token, Type *type) {
|
||||||
gbAllocator a = heap_allocator();
|
gbAllocator a = permanent_allocator();
|
||||||
Entity *entity = gb_alloc_item(a, Entity);
|
Entity *entity = gb_alloc_item(a, Entity);
|
||||||
entity->kind = kind;
|
entity->kind = kind;
|
||||||
entity->state = EntityState_Unresolved;
|
entity->state = EntityState_Unresolved;
|
||||||
@@ -332,7 +333,7 @@ Entity *alloc_entity_import_name(Scope *scope, Token token, Type *type,
|
|||||||
}
|
}
|
||||||
|
|
||||||
Entity *alloc_entity_library_name(Scope *scope, Token token, Type *type,
|
Entity *alloc_entity_library_name(Scope *scope, Token token, Type *type,
|
||||||
Array<String> paths, String name) {
|
Slice<String> paths, String name) {
|
||||||
Entity *entity = alloc_entity(Entity_LibraryName, scope, token, type);
|
Entity *entity = alloc_entity(Entity_LibraryName, scope, token, type);
|
||||||
entity->LibraryName.paths = paths;
|
entity->LibraryName.paths = paths;
|
||||||
entity->LibraryName.name = name;
|
entity->LibraryName.name = name;
|
||||||
|
|||||||
+59
-51
@@ -46,16 +46,16 @@ enum ExactValueKind {
|
|||||||
struct ExactValue {
|
struct ExactValue {
|
||||||
ExactValueKind kind;
|
ExactValueKind kind;
|
||||||
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; // NOTE(bill): This must be an integer and not a pointer
|
||||||
f64 value_float;
|
f64 value_float;
|
||||||
i64 value_pointer;
|
i64 value_pointer;
|
||||||
Complex128 value_complex;
|
Complex128 *value_complex;
|
||||||
Quaternion256 value_quaternion;
|
Quaternion256 *value_quaternion;
|
||||||
Ast * value_compound;
|
Ast * value_compound;
|
||||||
Ast * value_procedure;
|
Ast * value_procedure;
|
||||||
Type * value_typeid;
|
Type * value_typeid;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -85,9 +85,9 @@ HashKey hash_exact_value(ExactValue v) {
|
|||||||
case ExactValue_Pointer:
|
case ExactValue_Pointer:
|
||||||
return hash_integer(v.value_pointer);
|
return hash_integer(v.value_pointer);
|
||||||
case ExactValue_Complex:
|
case ExactValue_Complex:
|
||||||
return hashing_proc(&v.value_complex, gb_size_of(Complex128));
|
return hashing_proc(v.value_complex, gb_size_of(Complex128));
|
||||||
case ExactValue_Quaternion:
|
case ExactValue_Quaternion:
|
||||||
return hashing_proc(&v.value_quaternion, gb_size_of(Quaternion256));
|
return hashing_proc(v.value_quaternion, gb_size_of(Quaternion256));
|
||||||
case ExactValue_Compound:
|
case ExactValue_Compound:
|
||||||
return hash_pointer(v.value_compound);
|
return hash_pointer(v.value_compound);
|
||||||
case ExactValue_Procedure:
|
case ExactValue_Procedure:
|
||||||
@@ -139,17 +139,19 @@ ExactValue exact_value_float(f64 f) {
|
|||||||
|
|
||||||
ExactValue exact_value_complex(f64 real, f64 imag) {
|
ExactValue exact_value_complex(f64 real, f64 imag) {
|
||||||
ExactValue result = {ExactValue_Complex};
|
ExactValue result = {ExactValue_Complex};
|
||||||
result.value_complex.real = real;
|
result.value_complex = gb_alloc_item(permanent_allocator(), Complex128);
|
||||||
result.value_complex.imag = imag;
|
result.value_complex->real = real;
|
||||||
|
result.value_complex->imag = imag;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExactValue exact_value_quaternion(f64 real, f64 imag, f64 jmag, f64 kmag) {
|
ExactValue exact_value_quaternion(f64 real, f64 imag, f64 jmag, f64 kmag) {
|
||||||
ExactValue result = {ExactValue_Quaternion};
|
ExactValue result = {ExactValue_Quaternion};
|
||||||
result.value_quaternion.real = real;
|
result.value_quaternion = gb_alloc_item(permanent_allocator(), Quaternion256);
|
||||||
result.value_quaternion.imag = imag;
|
result.value_quaternion->real = real;
|
||||||
result.value_quaternion.jmag = jmag;
|
result.value_quaternion->imag = imag;
|
||||||
result.value_quaternion.kmag = kmag;
|
result.value_quaternion->jmag = jmag;
|
||||||
|
result.value_quaternion->kmag = kmag;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,6 +375,7 @@ ExactValue exact_value_to_complex(ExactValue v) {
|
|||||||
// return exact_value_complex(v.value_quaternion.real, v.value_quaternion.imag);
|
// return exact_value_complex(v.value_quaternion.real, v.value_quaternion.imag);
|
||||||
}
|
}
|
||||||
ExactValue r = {ExactValue_Invalid};
|
ExactValue r = {ExactValue_Invalid};
|
||||||
|
v.value_complex = gb_alloc_item(permanent_allocator(), Complex128);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
ExactValue exact_value_to_quaternion(ExactValue v) {
|
ExactValue exact_value_to_quaternion(ExactValue v) {
|
||||||
@@ -382,11 +385,12 @@ ExactValue exact_value_to_quaternion(ExactValue v) {
|
|||||||
case ExactValue_Float:
|
case ExactValue_Float:
|
||||||
return exact_value_quaternion(v.value_float, 0, 0, 0);
|
return exact_value_quaternion(v.value_float, 0, 0, 0);
|
||||||
case ExactValue_Complex:
|
case ExactValue_Complex:
|
||||||
return exact_value_quaternion(v.value_complex.real, v.value_complex.imag, 0, 0);
|
return exact_value_quaternion(v.value_complex->real, v.value_complex->imag, 0, 0);
|
||||||
case ExactValue_Quaternion:
|
case ExactValue_Quaternion:
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
ExactValue r = {ExactValue_Invalid};
|
ExactValue r = {ExactValue_Invalid};
|
||||||
|
v.value_quaternion = gb_alloc_item(permanent_allocator(), Quaternion256);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -396,9 +400,9 @@ ExactValue exact_value_real(ExactValue v) {
|
|||||||
case ExactValue_Float:
|
case ExactValue_Float:
|
||||||
return v;
|
return v;
|
||||||
case ExactValue_Complex:
|
case ExactValue_Complex:
|
||||||
return exact_value_float(v.value_complex.real);
|
return exact_value_float(v.value_complex->real);
|
||||||
case ExactValue_Quaternion:
|
case ExactValue_Quaternion:
|
||||||
return exact_value_float(v.value_quaternion.real);
|
return exact_value_float(v.value_quaternion->real);
|
||||||
}
|
}
|
||||||
ExactValue r = {ExactValue_Invalid};
|
ExactValue r = {ExactValue_Invalid};
|
||||||
return r;
|
return r;
|
||||||
@@ -410,9 +414,9 @@ ExactValue exact_value_imag(ExactValue v) {
|
|||||||
case ExactValue_Float:
|
case ExactValue_Float:
|
||||||
return exact_value_i64(0);
|
return exact_value_i64(0);
|
||||||
case ExactValue_Complex:
|
case ExactValue_Complex:
|
||||||
return exact_value_float(v.value_complex.imag);
|
return exact_value_float(v.value_complex->imag);
|
||||||
case ExactValue_Quaternion:
|
case ExactValue_Quaternion:
|
||||||
return exact_value_float(v.value_quaternion.imag);
|
return exact_value_float(v.value_quaternion->imag);
|
||||||
}
|
}
|
||||||
ExactValue r = {ExactValue_Invalid};
|
ExactValue r = {ExactValue_Invalid};
|
||||||
return r;
|
return r;
|
||||||
@@ -425,7 +429,7 @@ ExactValue exact_value_jmag(ExactValue v) {
|
|||||||
case ExactValue_Complex:
|
case ExactValue_Complex:
|
||||||
return exact_value_i64(0);
|
return exact_value_i64(0);
|
||||||
case ExactValue_Quaternion:
|
case ExactValue_Quaternion:
|
||||||
return exact_value_float(v.value_quaternion.jmag);
|
return exact_value_float(v.value_quaternion->jmag);
|
||||||
}
|
}
|
||||||
ExactValue r = {ExactValue_Invalid};
|
ExactValue r = {ExactValue_Invalid};
|
||||||
return r;
|
return r;
|
||||||
@@ -438,7 +442,7 @@ ExactValue exact_value_kmag(ExactValue v) {
|
|||||||
case ExactValue_Complex:
|
case ExactValue_Complex:
|
||||||
return exact_value_i64(0);
|
return exact_value_i64(0);
|
||||||
case ExactValue_Quaternion:
|
case ExactValue_Quaternion:
|
||||||
return exact_value_float(v.value_quaternion.kmag);
|
return exact_value_float(v.value_quaternion->kmag);
|
||||||
}
|
}
|
||||||
ExactValue r = {ExactValue_Invalid};
|
ExactValue r = {ExactValue_Invalid};
|
||||||
return r;
|
return r;
|
||||||
@@ -532,15 +536,15 @@ ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision,
|
|||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
case ExactValue_Complex: {
|
case ExactValue_Complex: {
|
||||||
f64 real = v.value_complex.real;
|
f64 real = v.value_complex->real;
|
||||||
f64 imag = v.value_complex.imag;
|
f64 imag = v.value_complex->imag;
|
||||||
return exact_value_complex(-real, -imag);
|
return exact_value_complex(-real, -imag);
|
||||||
}
|
}
|
||||||
case ExactValue_Quaternion: {
|
case ExactValue_Quaternion: {
|
||||||
f64 real = v.value_quaternion.real;
|
f64 real = v.value_quaternion->real;
|
||||||
f64 imag = v.value_quaternion.imag;
|
f64 imag = v.value_quaternion->imag;
|
||||||
f64 jmag = v.value_quaternion.jmag;
|
f64 jmag = v.value_quaternion->jmag;
|
||||||
f64 kmag = v.value_quaternion.kmag;
|
f64 kmag = v.value_quaternion->kmag;
|
||||||
return exact_value_quaternion(-real, -imag, -jmag, -kmag);
|
return exact_value_quaternion(-real, -imag, -jmag, -kmag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -685,6 +689,8 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y)
|
|||||||
case Token_CmpOr: return exact_value_bool(x.value_bool || y.value_bool);
|
case Token_CmpOr: return exact_value_bool(x.value_bool || y.value_bool);
|
||||||
case Token_And: return exact_value_bool(x.value_bool & y.value_bool);
|
case Token_And: return exact_value_bool(x.value_bool & y.value_bool);
|
||||||
case Token_Or: return exact_value_bool(x.value_bool | y.value_bool);
|
case Token_Or: return exact_value_bool(x.value_bool | y.value_bool);
|
||||||
|
case Token_AndNot: return exact_value_bool(x.value_bool & !y.value_bool);
|
||||||
|
case Token_Xor: return exact_value_bool((x.value_bool && !y.value_bool) || (!x.value_bool && y.value_bool));
|
||||||
default: goto error;
|
default: goto error;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -730,10 +736,10 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y)
|
|||||||
|
|
||||||
case ExactValue_Complex: {
|
case ExactValue_Complex: {
|
||||||
y = exact_value_to_complex(y);
|
y = exact_value_to_complex(y);
|
||||||
f64 a = x.value_complex.real;
|
f64 a = x.value_complex->real;
|
||||||
f64 b = x.value_complex.imag;
|
f64 b = x.value_complex->imag;
|
||||||
f64 c = y.value_complex.real;
|
f64 c = y.value_complex->real;
|
||||||
f64 d = y.value_complex.imag;
|
f64 d = y.value_complex->imag;
|
||||||
f64 real = 0;
|
f64 real = 0;
|
||||||
f64 imag = 0;
|
f64 imag = 0;
|
||||||
switch (op) {
|
switch (op) {
|
||||||
@@ -763,14 +769,14 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y)
|
|||||||
|
|
||||||
case ExactValue_Quaternion: {
|
case ExactValue_Quaternion: {
|
||||||
y = exact_value_to_quaternion(y);
|
y = exact_value_to_quaternion(y);
|
||||||
f64 xr = x.value_quaternion.real;
|
f64 xr = x.value_quaternion->real;
|
||||||
f64 xi = x.value_quaternion.imag;
|
f64 xi = x.value_quaternion->imag;
|
||||||
f64 xj = x.value_quaternion.jmag;
|
f64 xj = x.value_quaternion->jmag;
|
||||||
f64 xk = x.value_quaternion.kmag;
|
f64 xk = x.value_quaternion->kmag;
|
||||||
f64 yr = y.value_quaternion.real;
|
f64 yr = y.value_quaternion->real;
|
||||||
f64 yi = y.value_quaternion.imag;
|
f64 yi = y.value_quaternion->imag;
|
||||||
f64 yj = y.value_quaternion.jmag;
|
f64 yj = y.value_quaternion->jmag;
|
||||||
f64 yk = y.value_quaternion.kmag;
|
f64 yk = y.value_quaternion->kmag;
|
||||||
|
|
||||||
|
|
||||||
f64 real = 0;
|
f64 real = 0;
|
||||||
@@ -897,10 +903,10 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case ExactValue_Complex: {
|
case ExactValue_Complex: {
|
||||||
f64 a = x.value_complex.real;
|
f64 a = x.value_complex->real;
|
||||||
f64 b = x.value_complex.imag;
|
f64 b = x.value_complex->imag;
|
||||||
f64 c = y.value_complex.real;
|
f64 c = y.value_complex->real;
|
||||||
f64 d = y.value_complex.imag;
|
f64 d = y.value_complex->imag;
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case Token_CmpEq: return cmp_f64(a, c) == 0 && cmp_f64(b, d) == 0;
|
case Token_CmpEq: return cmp_f64(a, c) == 0 && cmp_f64(b, d) == 0;
|
||||||
case Token_NotEq: return cmp_f64(a, c) != 0 || cmp_f64(b, d) != 0;
|
case Token_NotEq: return cmp_f64(a, c) != 0 || cmp_f64(b, d) != 0;
|
||||||
@@ -945,7 +951,7 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
|
|||||||
Entity *strip_entity_wrapping(Ast *expr);
|
Entity *strip_entity_wrapping(Ast *expr);
|
||||||
Entity *strip_entity_wrapping(Entity *e);
|
Entity *strip_entity_wrapping(Entity *e);
|
||||||
|
|
||||||
gbString write_expr_to_string(gbString str, Ast *node);
|
gbString write_expr_to_string(gbString str, Ast *node, bool shorthand);
|
||||||
|
|
||||||
gbString write_exact_value_to_string(gbString str, ExactValue const &v, isize string_limit=36) {
|
gbString write_exact_value_to_string(gbString str, ExactValue const &v, isize string_limit=36) {
|
||||||
switch (v.kind) {
|
switch (v.kind) {
|
||||||
@@ -976,14 +982,16 @@ gbString write_exact_value_to_string(gbString str, ExactValue const &v, isize st
|
|||||||
case ExactValue_Float:
|
case ExactValue_Float:
|
||||||
return gb_string_append_fmt(str, "%f", v.value_float);
|
return gb_string_append_fmt(str, "%f", v.value_float);
|
||||||
case ExactValue_Complex:
|
case ExactValue_Complex:
|
||||||
return gb_string_append_fmt(str, "%f+%fi", v.value_complex.real, v.value_complex.imag);
|
return gb_string_append_fmt(str, "%f+%fi", v.value_complex->real, v.value_complex->imag);
|
||||||
|
case ExactValue_Quaternion:
|
||||||
|
return gb_string_append_fmt(str, "%f+%fi+%fj+%fk", v.value_quaternion->real, v.value_quaternion->imag, v.value_quaternion->jmag, v.value_quaternion->kmag);
|
||||||
|
|
||||||
case ExactValue_Pointer:
|
case ExactValue_Pointer:
|
||||||
return str;
|
return str;
|
||||||
case ExactValue_Compound:
|
case ExactValue_Compound:
|
||||||
return write_expr_to_string(str, v.value_compound);
|
return write_expr_to_string(str, v.value_compound, false);
|
||||||
case ExactValue_Procedure:
|
case ExactValue_Procedure:
|
||||||
return write_expr_to_string(str, v.value_procedure);
|
return write_expr_to_string(str, v.value_procedure, false);
|
||||||
}
|
}
|
||||||
return str;
|
return str;
|
||||||
};
|
};
|
||||||
|
|||||||
+101
-4
@@ -157,7 +157,7 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__64BIT__) || defined(__powerpc64__) || defined(__ppc64__)
|
#if defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__64BIT__) || defined(__powerpc64__) || defined(__ppc64__) || defined(__aarch64__)
|
||||||
#ifndef GB_ARCH_64_BIT
|
#ifndef GB_ARCH_64_BIT
|
||||||
#define GB_ARCH_64_BIT 1
|
#define GB_ARCH_64_BIT 1
|
||||||
#endif
|
#endif
|
||||||
@@ -230,7 +230,7 @@ extern "C" {
|
|||||||
#define GB_CACHE_LINE_SIZE 128
|
#define GB_CACHE_LINE_SIZE 128
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#elif defined(__arm__)
|
#elif defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64)
|
||||||
#ifndef GB_CPU_ARM
|
#ifndef GB_CPU_ARM
|
||||||
#define GB_CPU_ARM 1
|
#define GB_CPU_ARM 1
|
||||||
#endif
|
#endif
|
||||||
@@ -3702,6 +3702,12 @@ gb_inline void *gb_memcopy(void *dest, void const *source, isize n) {
|
|||||||
|
|
||||||
void *dest_copy = dest;
|
void *dest_copy = dest;
|
||||||
__asm__ __volatile__("rep movsb" : "+D"(dest_copy), "+S"(source), "+c"(n) : : "memory");
|
__asm__ __volatile__("rep movsb" : "+D"(dest_copy), "+S"(source), "+c"(n) : : "memory");
|
||||||
|
#elif defined(GB_CPU_ARM)
|
||||||
|
u8 *s = cast(u8 *)source;
|
||||||
|
u8 *d = cast(u8 *)dest;
|
||||||
|
for (isize i = 0; i < n; i++) {
|
||||||
|
*d++ = *s++;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
u8 *d = cast(u8 *)dest;
|
u8 *d = cast(u8 *)dest;
|
||||||
u8 const *s = cast(u8 const *)source;
|
u8 const *s = cast(u8 const *)source;
|
||||||
@@ -4438,6 +4444,76 @@ gb_inline i64 gb_atomic64_fetch_or(gbAtomic64 volatile *a, i64 operand) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#elif defined(GB_CPU_ARM)
|
||||||
|
|
||||||
|
gb_inline i32 gb_atomic32_load (gbAtomic32 const volatile *a) {
|
||||||
|
return __atomic_load_n(&a->value, __ATOMIC_SEQ_CST);
|
||||||
|
}
|
||||||
|
gb_inline void gb_atomic32_store(gbAtomic32 volatile *a, i32 value) {
|
||||||
|
__atomic_store_n(&a->value, value, __ATOMIC_SEQ_CST);
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_inline i32 gb_atomic32_compare_exchange(gbAtomic32 volatile *a, i32 expected, i32 desired) {
|
||||||
|
i32 expected_copy = expected;
|
||||||
|
auto result = __atomic_compare_exchange_n(&a->value, &expected_copy, desired, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
|
||||||
|
if (result) {
|
||||||
|
return expected;
|
||||||
|
} else {
|
||||||
|
return expected_copy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_inline i32 gb_atomic32_exchanged(gbAtomic32 volatile *a, i32 desired) {
|
||||||
|
return __atomic_exchange_n(&a->value, desired, __ATOMIC_SEQ_CST);
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_inline i32 gb_atomic32_fetch_add(gbAtomic32 volatile *a, i32 operand) {
|
||||||
|
return __atomic_fetch_add(&a->value, operand, __ATOMIC_SEQ_CST);
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_inline i32 gb_atomic32_fetch_and(gbAtomic32 volatile *a, i32 operand) {
|
||||||
|
return __atomic_fetch_and(&a->value, operand, __ATOMIC_SEQ_CST);
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_inline i32 gb_atomic32_fetch_or(gbAtomic32 volatile *a, i32 operand) {
|
||||||
|
return __atomic_fetch_or(&a->value, operand, __ATOMIC_SEQ_CST);
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_inline i64 gb_atomic64_load(gbAtomic64 const volatile *a) {
|
||||||
|
return __atomic_load_n(&a->value, __ATOMIC_SEQ_CST);
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_inline void gb_atomic64_store(gbAtomic64 volatile *a, i64 value) {
|
||||||
|
__atomic_store_n(&a->value, value, __ATOMIC_SEQ_CST);
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_inline i64 gb_atomic64_compare_exchange(gbAtomic64 volatile *a, i64 expected, i64 desired) {
|
||||||
|
i64 expected_copy = expected;
|
||||||
|
auto result = __atomic_compare_exchange_n(&a->value, &expected_copy, desired, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
|
||||||
|
if (result) {
|
||||||
|
return expected;
|
||||||
|
} else {
|
||||||
|
return expected_copy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_inline i64 gb_atomic64_exchanged(gbAtomic64 volatile *a, i64 desired) {
|
||||||
|
return __atomic_exchange_n(&a->value, desired, __ATOMIC_SEQ_CST);
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_inline i64 gb_atomic64_fetch_add(gbAtomic64 volatile *a, i64 operand) {
|
||||||
|
return __atomic_fetch_add(&a->value, operand, __ATOMIC_SEQ_CST);
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_inline i64 gb_atomic64_fetch_and(gbAtomic64 volatile *a, i64 operand) {
|
||||||
|
return __atomic_fetch_and(&a->value, operand, __ATOMIC_SEQ_CST);
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_inline i64 gb_atomic64_fetch_or(gbAtomic64 volatile *a, i64 operand) {
|
||||||
|
return __atomic_fetch_or(&a->value, operand, __ATOMIC_SEQ_CST);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#error TODO(bill): Implement Atomics for this CPU
|
#error TODO(bill): Implement Atomics for this CPU
|
||||||
#endif
|
#endif
|
||||||
@@ -4563,7 +4639,11 @@ gb_inline void gb_yield_thread(void) {
|
|||||||
#if defined(GB_SYSTEM_WINDOWS)
|
#if defined(GB_SYSTEM_WINDOWS)
|
||||||
_mm_pause();
|
_mm_pause();
|
||||||
#elif defined(GB_SYSTEM_OSX)
|
#elif defined(GB_SYSTEM_OSX)
|
||||||
|
#if defined(GB_CPU_X86)
|
||||||
__asm__ volatile ("" : : : "memory");
|
__asm__ volatile ("" : : : "memory");
|
||||||
|
#elif defined(GB_CPU_ARM)
|
||||||
|
__asm__ volatile ("yield" : : : "memory");
|
||||||
|
#endif
|
||||||
#elif defined(GB_CPU_X86)
|
#elif defined(GB_CPU_X86)
|
||||||
_mm_pause();
|
_mm_pause();
|
||||||
#else
|
#else
|
||||||
@@ -4575,7 +4655,11 @@ gb_inline void gb_mfence(void) {
|
|||||||
#if defined(GB_SYSTEM_WINDOWS)
|
#if defined(GB_SYSTEM_WINDOWS)
|
||||||
_ReadWriteBarrier();
|
_ReadWriteBarrier();
|
||||||
#elif defined(GB_SYSTEM_OSX)
|
#elif defined(GB_SYSTEM_OSX)
|
||||||
|
#if defined(GB_CPU_X86)
|
||||||
__sync_synchronize();
|
__sync_synchronize();
|
||||||
|
#elif defined(GB_CPU_ARM)
|
||||||
|
__atomic_thread_fence(__ATOMIC_SEQ_CST);
|
||||||
|
#endif
|
||||||
#elif defined(GB_CPU_X86)
|
#elif defined(GB_CPU_X86)
|
||||||
_mm_mfence();
|
_mm_mfence();
|
||||||
#else
|
#else
|
||||||
@@ -4587,7 +4671,12 @@ gb_inline void gb_sfence(void) {
|
|||||||
#if defined(GB_SYSTEM_WINDOWS)
|
#if defined(GB_SYSTEM_WINDOWS)
|
||||||
_WriteBarrier();
|
_WriteBarrier();
|
||||||
#elif defined(GB_SYSTEM_OSX)
|
#elif defined(GB_SYSTEM_OSX)
|
||||||
|
#if defined(GB_CPU_X86)
|
||||||
__asm__ volatile ("" : : : "memory");
|
__asm__ volatile ("" : : : "memory");
|
||||||
|
#elif defined(GB_CPU_ARM)
|
||||||
|
// TODO(bill): is this correct?
|
||||||
|
__atomic_thread_fence(__ATOMIC_SEQ_CST);
|
||||||
|
#endif
|
||||||
#elif defined(GB_CPU_X86)
|
#elif defined(GB_CPU_X86)
|
||||||
_mm_sfence();
|
_mm_sfence();
|
||||||
#else
|
#else
|
||||||
@@ -5156,7 +5245,7 @@ b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
|
|||||||
|
|
||||||
index = core * a->threads_per_core + thread_index;
|
index = core * a->threads_per_core + thread_index;
|
||||||
thread = pthread_self();
|
thread = pthread_self();
|
||||||
|
|
||||||
|
|
||||||
cpuset_t mn;
|
cpuset_t mn;
|
||||||
CPU_ZERO(&mn);
|
CPU_ZERO(&mn);
|
||||||
@@ -5202,7 +5291,7 @@ void gb_affinity_init(gbAffinity *a) {
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
// The 'temporary char'. Everything goes into this char,
|
// The 'temporary char'. Everything goes into this char,
|
||||||
// so that we can check against EOF at the end of this loop.
|
// so that we can check against EOF at the end of this loop.
|
||||||
char c;
|
int c;
|
||||||
|
|
||||||
#define AF__CHECK(letter) ((c = getc(cpu_info)) == letter)
|
#define AF__CHECK(letter) ((c = getc(cpu_info)) == letter)
|
||||||
if (AF__CHECK('c') && AF__CHECK('p') && AF__CHECK('u') && AF__CHECK(' ') &&
|
if (AF__CHECK('c') && AF__CHECK('p') && AF__CHECK('u') && AF__CHECK(' ') &&
|
||||||
@@ -8808,6 +8897,14 @@ gb_inline gbDllProc gb_dll_proc_address(gbDllHandle dll, char const *proc_name)
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
gb_inline u64 gb_rdtsc(void) {
|
||||||
|
int64_t virtual_timer_value;
|
||||||
|
asm volatile("mrs %0, cntvct_el0" : "=r"(virtual_timer_value));
|
||||||
|
return virtual_timer_value;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#error "gb_rdtsc not supported"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(GB_SYSTEM_WINDOWS)
|
#if defined(GB_SYSTEM_WINDOWS)
|
||||||
|
|||||||
+415
-109
@@ -25,6 +25,9 @@ struct irModule {
|
|||||||
Map<irDebugInfo *> debug_info; // Key: Unique pointer
|
Map<irDebugInfo *> debug_info; // Key: Unique pointer
|
||||||
Map<irValue *> anonymous_proc_lits; // Key: Ast *
|
Map<irValue *> anonymous_proc_lits; // Key: Ast *
|
||||||
|
|
||||||
|
Map<irValue *> equal_procs; // Key: Type *
|
||||||
|
Map<irValue *> hasher_procs; // Key: Type *
|
||||||
|
|
||||||
irDebugInfo * debug_compile_unit;
|
irDebugInfo * debug_compile_unit;
|
||||||
Array<irDebugInfo *> debug_location_stack;
|
Array<irDebugInfo *> debug_location_stack;
|
||||||
|
|
||||||
@@ -161,6 +164,7 @@ struct irProcedure {
|
|||||||
Ast * return_ptr_hint_ast;
|
Ast * return_ptr_hint_ast;
|
||||||
bool return_ptr_hint_used;
|
bool return_ptr_hint_used;
|
||||||
|
|
||||||
|
bool ignore_dead_instr;
|
||||||
|
|
||||||
Array<irBranchBlocks> branch_blocks;
|
Array<irBranchBlocks> branch_blocks;
|
||||||
|
|
||||||
@@ -454,7 +458,6 @@ struct irValueSourceCodeLocation {
|
|||||||
irValue *line;
|
irValue *line;
|
||||||
irValue *column;
|
irValue *column;
|
||||||
irValue *procedure;
|
irValue *procedure;
|
||||||
u64 hash;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -525,6 +528,11 @@ struct irAddr {
|
|||||||
|
|
||||||
Type *ir_type(irValue *value);
|
Type *ir_type(irValue *value);
|
||||||
irValue *ir_gen_anonymous_proc_lit(irModule *m, String prefix_name, Ast *expr, irProcedure *proc = nullptr);
|
irValue *ir_gen_anonymous_proc_lit(irModule *m, String prefix_name, Ast *expr, irProcedure *proc = nullptr);
|
||||||
|
void ir_begin_procedure_body(irProcedure *proc);
|
||||||
|
void ir_end_procedure_body(irProcedure *proc);
|
||||||
|
irValue *ir_get_equal_proc_for_type(irModule *m, Type *type);
|
||||||
|
irValue *ir_get_hasher_proc_for_type(irModule *m, Type *type);
|
||||||
|
|
||||||
|
|
||||||
irAddr ir_addr(irValue *addr) {
|
irAddr ir_addr(irValue *addr) {
|
||||||
irAddr v = {irAddr_Default, addr};
|
irAddr v = {irAddr_Default, addr};
|
||||||
@@ -1159,7 +1167,7 @@ irValue *ir_instr_atomic_cxchg(irProcedure *p, Type *type, irValue *address, irV
|
|||||||
GB_ASSERT(type->Tuple.variables.count == 2);
|
GB_ASSERT(type->Tuple.variables.count == 2);
|
||||||
Type *elem = type->Tuple.variables[0]->type;
|
Type *elem = type->Tuple.variables[0]->type;
|
||||||
// LEAK TODO(bill): LLVM returns {T, i1} whilst Odin does {T, bool}, fix this mapping hack
|
// LEAK TODO(bill): LLVM returns {T, i1} whilst Odin does {T, bool}, fix this mapping hack
|
||||||
gbAllocator a = heap_allocator();
|
gbAllocator a = permanent_allocator();
|
||||||
Type *llvm_type = alloc_type_tuple();
|
Type *llvm_type = alloc_type_tuple();
|
||||||
array_init(&llvm_type->Tuple.variables, a, 0, 2);
|
array_init(&llvm_type->Tuple.variables, a, 0, 2);
|
||||||
array_add (&llvm_type->Tuple.variables, alloc_entity_field(nullptr, blank_token, elem, false, 0));
|
array_add (&llvm_type->Tuple.variables, alloc_entity_field(nullptr, blank_token, elem, false, 0));
|
||||||
@@ -1799,7 +1807,7 @@ irValue *ir_add_local(irProcedure *proc, Entity *e, Ast *expr, bool zero_initial
|
|||||||
if (zero_initialized) {
|
if (zero_initialized) {
|
||||||
ir_emit_zero_init(proc, instr, expr);
|
ir_emit_zero_init(proc, instr, expr);
|
||||||
}
|
}
|
||||||
set_procedure_abi_types(heap_allocator(), e->type);
|
set_procedure_abi_types(e->type);
|
||||||
|
|
||||||
// if (proc->module->generate_debug_info && expr != nullptr && proc->entity != nullptr) {
|
// if (proc->module->generate_debug_info && expr != nullptr && proc->entity != nullptr) {
|
||||||
// if (proc->module->generate_debug_info && proc->entity != nullptr) {
|
// if (proc->module->generate_debug_info && proc->entity != nullptr) {
|
||||||
@@ -2132,7 +2140,7 @@ irDebugInfo *ir_add_debug_info_field(irModule *module, irDebugInfo *scope, Entit
|
|||||||
if (e->token.string.len == 0) {
|
if (e->token.string.len == 0) {
|
||||||
// If no name available for field, use its field index as its name.
|
// If no name available for field, use its field index as its name.
|
||||||
isize max_len = 8;
|
isize max_len = 8;
|
||||||
u8 *str = cast(u8 *)gb_alloc_array(heap_allocator(), u8, max_len);
|
u8 *str = cast(u8 *)gb_alloc_array(permanent_allocator(), u8, max_len);
|
||||||
isize len = gb_snprintf(cast(char *)str, 8, "%d", index);
|
isize len = gb_snprintf(cast(char *)str, 8, "%d", index);
|
||||||
di->DerivedType.name = make_string(str, len-1);
|
di->DerivedType.name = make_string(str, len-1);
|
||||||
}
|
}
|
||||||
@@ -3282,7 +3290,7 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> const &ar
|
|||||||
context_ptr = ir_find_or_generate_context_ptr(p);
|
context_ptr = ir_find_or_generate_context_ptr(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
set_procedure_abi_types(heap_allocator(), pt);
|
set_procedure_abi_types(pt);
|
||||||
|
|
||||||
bool is_c_vararg = pt->Proc.c_vararg;
|
bool is_c_vararg = pt->Proc.c_vararg;
|
||||||
isize param_count = pt->Proc.param_count;
|
isize param_count = pt->Proc.param_count;
|
||||||
@@ -3293,7 +3301,7 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> const &ar
|
|||||||
GB_ASSERT_MSG(param_count == args.count, "%.*s %td == %td", LIT(p->entity->token.string), param_count, args.count);
|
GB_ASSERT_MSG(param_count == args.count, "%.*s %td == %td", LIT(p->entity->token.string), param_count, args.count);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto processed_args = array_make<irValue *>(heap_allocator(), 0, args.count);
|
auto processed_args = array_make<irValue *>(permanent_allocator(), 0, args.count);
|
||||||
|
|
||||||
for (isize i = 0; i < param_count; i++) {
|
for (isize i = 0; i < param_count; i++) {
|
||||||
Entity *e = pt->Proc.params->Tuple.variables[i];
|
Entity *e = pt->Proc.params->Tuple.variables[i];
|
||||||
@@ -3416,7 +3424,7 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> const &ar
|
|||||||
case DeferredProcedure_in_out:
|
case DeferredProcedure_in_out:
|
||||||
{
|
{
|
||||||
auto out_args = ir_value_to_array(p, result);
|
auto out_args = ir_value_to_array(p, result);
|
||||||
array_init(&result_as_args, heap_allocator(), in_args.count + out_args.count);
|
array_init(&result_as_args, permanent_allocator(), in_args.count + out_args.count);
|
||||||
array_copy(&result_as_args, in_args, 0);
|
array_copy(&result_as_args, in_args, 0);
|
||||||
array_copy(&result_as_args, out_args, in_args.count);
|
array_copy(&result_as_args, out_args, in_args.count);
|
||||||
}
|
}
|
||||||
@@ -3587,65 +3595,69 @@ irValue *ir_gen_map_header(irProcedure *proc, irValue *map_val_ptr, Type *map_ty
|
|||||||
irValue *m = ir_emit_conv(proc, map_val_ptr, type_deref(ir_type(gep0)));
|
irValue *m = ir_emit_conv(proc, map_val_ptr, type_deref(ir_type(gep0)));
|
||||||
ir_emit_store(proc, gep0, m);
|
ir_emit_store(proc, gep0, m);
|
||||||
|
|
||||||
ir_emit_store(proc, ir_emit_struct_ep(proc, h, 1), ir_const_bool(is_type_string(key_type)));
|
|
||||||
|
|
||||||
i64 entry_size = type_size_of (map_type->Map.entry_type);
|
i64 entry_size = type_size_of (map_type->Map.entry_type);
|
||||||
i64 entry_align = type_align_of (map_type->Map.entry_type);
|
i64 entry_align = type_align_of (map_type->Map.entry_type);
|
||||||
i64 value_offset = type_offset_of(map_type->Map.entry_type, 2);
|
i64 key_offset = type_offset_of(map_type->Map.entry_type, 2);
|
||||||
|
i64 key_size = type_size_of (map_type->Map.key);
|
||||||
|
i64 value_offset = type_offset_of(map_type->Map.entry_type, 3);
|
||||||
i64 value_size = type_size_of (map_type->Map.value);
|
i64 value_size = type_size_of (map_type->Map.value);
|
||||||
|
|
||||||
|
ir_emit_store(proc, ir_emit_struct_ep(proc, h, 1), ir_get_equal_proc_for_type(proc->module, key_type));
|
||||||
ir_emit_store(proc, ir_emit_struct_ep(proc, h, 2), ir_const_int(entry_size));
|
ir_emit_store(proc, ir_emit_struct_ep(proc, h, 2), ir_const_int(entry_size));
|
||||||
ir_emit_store(proc, ir_emit_struct_ep(proc, h, 3), ir_const_int(entry_align));
|
ir_emit_store(proc, ir_emit_struct_ep(proc, h, 3), ir_const_int(entry_align));
|
||||||
ir_emit_store(proc, ir_emit_struct_ep(proc, h, 4), ir_const_uintptr(value_offset));
|
ir_emit_store(proc, ir_emit_struct_ep(proc, h, 4), ir_const_uintptr(key_offset));
|
||||||
ir_emit_store(proc, ir_emit_struct_ep(proc, h, 5), ir_const_int(value_size));
|
ir_emit_store(proc, ir_emit_struct_ep(proc, h, 5), ir_const_int(key_size));
|
||||||
|
ir_emit_store(proc, ir_emit_struct_ep(proc, h, 6), ir_const_uintptr(value_offset));
|
||||||
|
ir_emit_store(proc, ir_emit_struct_ep(proc, h, 7), ir_const_int(value_size));
|
||||||
|
|
||||||
return ir_emit_load(proc, h);
|
return ir_emit_load(proc, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
irValue *ir_gen_map_key(irProcedure *proc, irValue *key, Type *key_type) {
|
irValue *ir_const_hash(irModule *m, irValue *key, Type *key_type) {
|
||||||
|
irValue *hashed_key = nullptr;
|
||||||
|
|
||||||
|
if (key->kind == irValue_Constant) {
|
||||||
|
u64 hash = 0xcbf29ce484222325;
|
||||||
|
if (is_type_string(key_type)) {
|
||||||
|
GB_ASSERT(key->Constant.value.kind == ExactValue_String);
|
||||||
|
String s = key->Constant.value.value_string;
|
||||||
|
hash = fnv64a(s.text, s.len);
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
// TODO(bill): other const hash types
|
||||||
|
|
||||||
|
if (build_context.word_size == 4) {
|
||||||
|
hash &= 0xffffffffull;
|
||||||
|
}
|
||||||
|
hashed_key = ir_const_uintptr(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
return hashed_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
irValue *ir_gen_map_hash(irProcedure *proc, irValue *key, Type *key_type) {
|
||||||
Type *hash_type = t_u64;
|
Type *hash_type = t_u64;
|
||||||
irValue *v = ir_add_local_generated(proc, t_map_key, true);
|
irValue *v = ir_add_local_generated(proc, t_map_hash, true);
|
||||||
Type *t = base_type(ir_type(key));
|
Type *t = base_type(ir_type(key));
|
||||||
key = ir_emit_conv(proc, key, key_type);
|
key = ir_emit_conv(proc, key, key_type);
|
||||||
|
|
||||||
if (is_type_string(t)) {
|
irValue *key_ptr = ir_address_from_load_or_generate_local(proc, key);
|
||||||
irValue *str = ir_emit_conv(proc, key, t_string);
|
key_ptr = ir_emit_conv(proc, key_ptr, t_rawptr);
|
||||||
irValue *hashed_str = nullptr;
|
|
||||||
|
|
||||||
if (str->kind == irValue_Constant) {
|
irValue *hashed_key = ir_const_hash(proc->module, key, key_type);
|
||||||
ExactValue ev = str->Constant.value;
|
if (hashed_key == nullptr) {
|
||||||
GB_ASSERT(ev.kind == ExactValue_String);
|
irValue *hasher = ir_get_hasher_proc_for_type(proc->module, key_type);
|
||||||
u64 hs = fnv64a(ev.value_string.text, ev.value_string.len);
|
auto args = array_make<irValue *>(permanent_allocator(), 2);
|
||||||
hashed_str = ir_value_constant(t_u64, exact_value_u64(hs));
|
args[0] = key_ptr;
|
||||||
} else {
|
args[1] = ir_value_constant(t_uintptr, exact_value_i64(0));
|
||||||
auto args = array_make<irValue *>(ir_allocator(), 1);
|
hashed_key = ir_emit_call(proc, hasher, args);
|
||||||
args[0] = str;
|
|
||||||
hashed_str = ir_emit_runtime_call(proc, "default_hash_string", args);
|
|
||||||
}
|
|
||||||
ir_emit_store(proc, ir_emit_struct_ep(proc, v, 0), hashed_str);
|
|
||||||
|
|
||||||
irValue *key_data = ir_emit_struct_ep(proc, v, 1);
|
|
||||||
key_data = ir_emit_conv(proc, key_data, alloc_type_pointer(key_type));
|
|
||||||
ir_emit_store(proc, key_data, str);
|
|
||||||
} else {
|
|
||||||
i64 sz = type_size_of(t);
|
|
||||||
GB_ASSERT(sz <= 8);
|
|
||||||
if (sz != 0) {
|
|
||||||
auto args = array_make<irValue *>(ir_allocator(), 2);
|
|
||||||
args[0] = ir_address_from_load_or_generate_local(proc, key);
|
|
||||||
args[1] = ir_const_int(sz);
|
|
||||||
irValue *hash = ir_emit_runtime_call(proc, "default_hash_ptr", args);
|
|
||||||
|
|
||||||
|
|
||||||
irValue *hash_ptr = ir_emit_struct_ep(proc, v, 0);
|
|
||||||
irValue *key_data = ir_emit_struct_ep(proc, v, 1);
|
|
||||||
key_data = ir_emit_conv(proc, key_data, alloc_type_pointer(key_type));
|
|
||||||
|
|
||||||
ir_emit_store(proc, hash_ptr, hash);
|
|
||||||
ir_emit_store(proc, key_data, key);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ir_emit_store(proc, ir_emit_struct_ep(proc, v, 0), hashed_key);
|
||||||
|
ir_emit_store(proc, ir_emit_struct_ep(proc, v, 1), key_ptr);
|
||||||
|
|
||||||
return ir_emit_load(proc, v);
|
return ir_emit_load(proc, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3701,7 +3713,7 @@ irValue *ir_insert_dynamic_map_key_and_value(irProcedure *proc, irValue *addr, T
|
|||||||
map_type = base_type(map_type);
|
map_type = base_type(map_type);
|
||||||
|
|
||||||
irValue *h = ir_gen_map_header(proc, addr, map_type);
|
irValue *h = ir_gen_map_header(proc, addr, map_type);
|
||||||
irValue *key = ir_gen_map_key(proc, map_key, map_type->Map.key);
|
irValue *key = ir_gen_map_hash(proc, map_key, map_type->Map.key);
|
||||||
irValue *v = ir_emit_conv(proc, map_value, map_type->Map.value);
|
irValue *v = ir_emit_conv(proc, map_value, map_type->Map.value);
|
||||||
|
|
||||||
irValue *ptr = ir_add_local_generated(proc, ir_type(v), false);
|
irValue *ptr = ir_add_local_generated(proc, ir_type(v), false);
|
||||||
@@ -4058,7 +4070,7 @@ irValue *ir_addr_load(irProcedure *proc, irAddr const &addr) {
|
|||||||
Type *map_type = base_type(addr.map_type);
|
Type *map_type = base_type(addr.map_type);
|
||||||
irValue *v = ir_add_local_generated(proc, map_type->Map.lookup_result_type, true);
|
irValue *v = ir_add_local_generated(proc, map_type->Map.lookup_result_type, true);
|
||||||
irValue *h = ir_gen_map_header(proc, addr.addr, map_type);
|
irValue *h = ir_gen_map_header(proc, addr.addr, map_type);
|
||||||
irValue *key = ir_gen_map_key(proc, addr.map_key, map_type->Map.key);
|
irValue *key = ir_gen_map_hash(proc, addr.map_key, map_type->Map.key);
|
||||||
|
|
||||||
auto args = array_make<irValue *>(ir_allocator(), 2);
|
auto args = array_make<irValue *>(ir_allocator(), 2);
|
||||||
args[0] = h;
|
args[0] = h;
|
||||||
@@ -4226,7 +4238,7 @@ irValue *ir_addr_get_ptr(irProcedure *proc, irAddr const &addr, bool allow_refer
|
|||||||
if (allow_reference) {
|
if (allow_reference) {
|
||||||
Type *map_type = base_type(addr.map_type);
|
Type *map_type = base_type(addr.map_type);
|
||||||
irValue *h = ir_gen_map_header(proc, addr.addr, map_type);
|
irValue *h = ir_gen_map_header(proc, addr.addr, map_type);
|
||||||
irValue *key = ir_gen_map_key(proc, addr.map_key, map_type->Map.key);
|
irValue *key = ir_gen_map_hash(proc, addr.map_key, map_type->Map.key);
|
||||||
|
|
||||||
auto args = array_make<irValue *>(ir_allocator(), 2);
|
auto args = array_make<irValue *>(ir_allocator(), 2);
|
||||||
args[0] = h;
|
args[0] = h;
|
||||||
@@ -4537,7 +4549,7 @@ irValue *ir_emit_arith(irProcedure *proc, TokenKind op, irValue *left, irValue *
|
|||||||
Type *ft = base_complex_elem_type(t_left);
|
Type *ft = base_complex_elem_type(t_left);
|
||||||
|
|
||||||
if (op == Token_Quo) {
|
if (op == Token_Quo) {
|
||||||
auto args = array_make<irValue *>(heap_allocator(), 2);
|
auto args = array_make<irValue *>(permanent_allocator(), 2);
|
||||||
args[0] = left;
|
args[0] = left;
|
||||||
args[1] = right;
|
args[1] = right;
|
||||||
|
|
||||||
@@ -4615,7 +4627,7 @@ irValue *ir_emit_arith(irProcedure *proc, TokenKind op, irValue *left, irValue *
|
|||||||
|
|
||||||
return ir_emit_load(proc, res);
|
return ir_emit_load(proc, res);
|
||||||
} else if (op == Token_Mul) {
|
} else if (op == Token_Mul) {
|
||||||
auto args = array_make<irValue *>(heap_allocator(), 2);
|
auto args = array_make<irValue *>(permanent_allocator(), 2);
|
||||||
args[0] = left;
|
args[0] = left;
|
||||||
args[1] = right;
|
args[1] = right;
|
||||||
|
|
||||||
@@ -4625,7 +4637,7 @@ irValue *ir_emit_arith(irProcedure *proc, TokenKind op, irValue *left, irValue *
|
|||||||
default: GB_PANIC("Unknown float type"); break;
|
default: GB_PANIC("Unknown float type"); break;
|
||||||
}
|
}
|
||||||
} else if (op == Token_Quo) {
|
} else if (op == Token_Quo) {
|
||||||
auto args = array_make<irValue *>(heap_allocator(), 2);
|
auto args = array_make<irValue *>(permanent_allocator(), 2);
|
||||||
args[0] = left;
|
args[0] = left;
|
||||||
args[1] = right;
|
args[1] = right;
|
||||||
|
|
||||||
@@ -4828,7 +4840,7 @@ irValue *ir_emit_comp_against_nil(irProcedure *proc, TokenKind op_kind, irValue
|
|||||||
irValue *invalid_typeid = ir_value_constant(t_typeid, exact_value_i64(0));
|
irValue *invalid_typeid = ir_value_constant(t_typeid, exact_value_i64(0));
|
||||||
return ir_emit_comp(proc, op_kind, x, invalid_typeid);
|
return ir_emit_comp(proc, op_kind, x, invalid_typeid);
|
||||||
} else if (is_type_bit_field(t)) {
|
} else if (is_type_bit_field(t)) {
|
||||||
auto args = array_make<irValue *>(heap_allocator(), 2);
|
auto args = array_make<irValue *>(permanent_allocator(), 2);
|
||||||
irValue *lhs = ir_address_from_load_or_generate_local(proc, x);
|
irValue *lhs = ir_address_from_load_or_generate_local(proc, x);
|
||||||
args[0] = ir_emit_conv(proc, lhs, t_rawptr);
|
args[0] = ir_emit_conv(proc, lhs, t_rawptr);
|
||||||
args[1] = ir_const_int(type_size_of(t));
|
args[1] = ir_const_int(type_size_of(t));
|
||||||
@@ -4848,7 +4860,7 @@ irValue *ir_emit_comp_against_nil(irProcedure *proc, TokenKind op_kind, irValue
|
|||||||
return ir_emit_comp(proc, op_kind, cap, v_zero);
|
return ir_emit_comp(proc, op_kind, cap, v_zero);
|
||||||
}
|
}
|
||||||
} else if (is_type_struct(t) && type_has_nil(t)) {
|
} else if (is_type_struct(t) && type_has_nil(t)) {
|
||||||
auto args = array_make<irValue *>(heap_allocator(), 2);
|
auto args = array_make<irValue *>(permanent_allocator(), 2);
|
||||||
irValue *lhs = ir_address_from_load_or_generate_local(proc, x);
|
irValue *lhs = ir_address_from_load_or_generate_local(proc, x);
|
||||||
args[0] = ir_emit_conv(proc, lhs, t_rawptr);
|
args[0] = ir_emit_conv(proc, lhs, t_rawptr);
|
||||||
args[1] = ir_const_int(type_size_of(t));
|
args[1] = ir_const_int(type_size_of(t));
|
||||||
@@ -4859,6 +4871,244 @@ irValue *ir_emit_comp_against_nil(irProcedure *proc, TokenKind op_kind, irValue
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
irValue *ir_get_equal_proc_for_type(irModule *m, Type *type) {
|
||||||
|
Type *original_type = type;
|
||||||
|
type = base_type(type);
|
||||||
|
Type *pt = alloc_type_pointer(type);
|
||||||
|
|
||||||
|
auto key = hash_type(type);
|
||||||
|
irValue **found = map_get(&m->equal_procs, key);
|
||||||
|
if (found) {
|
||||||
|
return *found;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 proc_index = 0;
|
||||||
|
|
||||||
|
char buf[16] = {};
|
||||||
|
isize n = gb_snprintf(buf, 16, "__$equal%u", ++proc_index);
|
||||||
|
char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
|
||||||
|
String proc_name = make_string_c(str);
|
||||||
|
|
||||||
|
|
||||||
|
Ast *body = alloc_ast_node(nullptr, Ast_Invalid);
|
||||||
|
Entity *e = alloc_entity_procedure(nullptr, make_token_ident(proc_name), t_equal_proc, 0);
|
||||||
|
e->Procedure.link_name = proc_name;
|
||||||
|
irValue *p = ir_value_procedure(m, e, t_equal_proc, nullptr, body, proc_name);
|
||||||
|
map_set(&m->values, hash_entity(e), p);
|
||||||
|
string_map_set(&m->members, proc_name, p);
|
||||||
|
map_set(&m->equal_procs, key, p);
|
||||||
|
|
||||||
|
irProcedure *proc = &p->Proc;
|
||||||
|
proc->is_startup = true;
|
||||||
|
proc->ignore_dead_instr = true;
|
||||||
|
ir_begin_procedure_body(proc);
|
||||||
|
// ir_start_block(proc, proc->decl_block);
|
||||||
|
GB_ASSERT(proc->curr_block != nullptr);
|
||||||
|
|
||||||
|
irValue *x = proc->params[0];
|
||||||
|
irValue *y = proc->params[1];
|
||||||
|
irValue *lhs = ir_emit_conv(proc, x, pt);
|
||||||
|
irValue *rhs = ir_emit_conv(proc, y, pt);
|
||||||
|
|
||||||
|
irBlock *block_same_ptr = ir_new_block(proc, nullptr, "same_ptr");
|
||||||
|
irBlock *block_diff_ptr = ir_new_block(proc, nullptr, "diff_ptr");
|
||||||
|
|
||||||
|
irValue *same_ptr = ir_emit_comp(proc, Token_CmpEq, lhs, rhs);
|
||||||
|
ir_emit_if(proc, same_ptr, block_same_ptr, block_diff_ptr);
|
||||||
|
ir_start_block(proc, block_same_ptr);
|
||||||
|
ir_emit(proc, ir_instr_return(proc, ir_const_bool(true)));
|
||||||
|
|
||||||
|
ir_start_block(proc, block_diff_ptr);
|
||||||
|
|
||||||
|
if (type->kind == Type_Struct) {
|
||||||
|
type_set_offsets(type);
|
||||||
|
|
||||||
|
irBlock *done = ir_new_block(proc, nullptr, "done"); // NOTE(bill): Append later
|
||||||
|
|
||||||
|
irBlock *block_false = ir_new_block(proc, nullptr, "bfalse");
|
||||||
|
|
||||||
|
for_array(i, type->Struct.fields) {
|
||||||
|
irBlock *next_block = ir_new_block(proc, nullptr, "btrue");
|
||||||
|
|
||||||
|
irValue *pleft = ir_emit_struct_ep(proc, lhs, cast(i32)i);
|
||||||
|
irValue *pright = ir_emit_struct_ep(proc, rhs, cast(i32)i);
|
||||||
|
irValue *left = ir_emit_load(proc, pleft);
|
||||||
|
irValue *right = ir_emit_load(proc, pright);
|
||||||
|
irValue *ok = ir_emit_comp(proc, Token_CmpEq, left, right);
|
||||||
|
|
||||||
|
ir_emit_if(proc, ok, next_block, block_false);
|
||||||
|
|
||||||
|
ir_emit_jump(proc, next_block);
|
||||||
|
ir_start_block(proc, next_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
ir_emit_jump(proc, done);
|
||||||
|
ir_start_block(proc, block_false);
|
||||||
|
|
||||||
|
ir_emit(proc, ir_instr_return(proc, ir_const_bool(false)));
|
||||||
|
|
||||||
|
ir_emit_jump(proc, done);
|
||||||
|
ir_start_block(proc, done);
|
||||||
|
ir_emit(proc, ir_instr_return(proc, ir_const_bool(true)));
|
||||||
|
} else {
|
||||||
|
irValue *left = ir_emit_load(proc, lhs);
|
||||||
|
irValue *right = ir_emit_load(proc, rhs);
|
||||||
|
irValue *ok = ir_emit_comp(proc, Token_CmpEq, left, right);
|
||||||
|
ok = ir_emit_conv(proc, ok, t_bool);
|
||||||
|
ir_emit(proc, ir_instr_return(proc, ok));
|
||||||
|
}
|
||||||
|
|
||||||
|
ir_end_procedure_body(proc);
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
irValue *ir_simple_compare_hash(irProcedure *p, Type *type, irValue *data, irValue *seed) {
|
||||||
|
GB_ASSERT_MSG(is_type_simple_compare(type), "%s", type_to_string(type));
|
||||||
|
|
||||||
|
i64 sz = type_size_of(type);
|
||||||
|
if (1 <= sz && sz <= 16) {
|
||||||
|
char name[20] = {};
|
||||||
|
gb_snprintf(name, 20, "default_hasher%d", cast(i32)sz);
|
||||||
|
|
||||||
|
auto args = array_make<irValue *>(permanent_allocator(), 2);
|
||||||
|
args[0] = data;
|
||||||
|
args[1] = seed;
|
||||||
|
return ir_emit_runtime_call(p, name, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto args = array_make<irValue *>(permanent_allocator(), 3);
|
||||||
|
args[0] = data;
|
||||||
|
args[1] = seed;
|
||||||
|
args[2] = ir_const_int(type_size_of(type));
|
||||||
|
return ir_emit_runtime_call(p, "default_hasher_n", args);
|
||||||
|
}
|
||||||
|
|
||||||
|
irValue *ir_get_hasher_proc_for_type(irModule *m, Type *type) {
|
||||||
|
Type *original_type = type;
|
||||||
|
type = core_type(type);
|
||||||
|
Type *pt = alloc_type_pointer(type);
|
||||||
|
|
||||||
|
GB_ASSERT(is_type_valid_for_keys(type));
|
||||||
|
|
||||||
|
auto key = hash_type(type);
|
||||||
|
irValue **found = map_get(&m->hasher_procs, key);
|
||||||
|
if (found) {
|
||||||
|
return *found;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 proc_index = 0;
|
||||||
|
|
||||||
|
char buf[16] = {};
|
||||||
|
isize n = gb_snprintf(buf, 16, "__$hasher%u", ++proc_index);
|
||||||
|
char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
|
||||||
|
String proc_name = make_string_c(str);
|
||||||
|
|
||||||
|
|
||||||
|
Ast *body = alloc_ast_node(nullptr, Ast_Invalid);
|
||||||
|
Entity *e = alloc_entity_procedure(nullptr, make_token_ident(proc_name), t_hasher_proc, 0);
|
||||||
|
e->Procedure.link_name = proc_name;
|
||||||
|
irValue *p = ir_value_procedure(m, e, t_hasher_proc, nullptr, body, proc_name);
|
||||||
|
map_set(&m->values, hash_entity(e), p);
|
||||||
|
string_map_set(&m->members, proc_name, p);
|
||||||
|
map_set(&m->hasher_procs, key, p);
|
||||||
|
|
||||||
|
irProcedure *proc = &p->Proc;
|
||||||
|
proc->is_startup = true;
|
||||||
|
proc->ignore_dead_instr = true;
|
||||||
|
ir_begin_procedure_body(proc);
|
||||||
|
defer (ir_end_procedure_body(proc));
|
||||||
|
|
||||||
|
// ir_start_block(proc, proc->decl_block);
|
||||||
|
GB_ASSERT(proc->curr_block != nullptr);
|
||||||
|
|
||||||
|
irValue *data = proc->params[0];
|
||||||
|
irValue *seed = proc->params[1];
|
||||||
|
|
||||||
|
if (is_type_simple_compare(type)) {
|
||||||
|
irValue *res = ir_simple_compare_hash(proc, type, data, seed);
|
||||||
|
ir_emit(proc, ir_instr_return(proc, res));
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_type_cstring(type)) {
|
||||||
|
auto args = array_make<irValue *>(permanent_allocator(), 2);
|
||||||
|
args[0] = data;
|
||||||
|
args[1] = seed;
|
||||||
|
irValue *res = ir_emit_runtime_call(proc, "default_hasher_cstring", args);
|
||||||
|
ir_emit(proc, ir_instr_return(proc, res));
|
||||||
|
} else if (is_type_string(type)) {
|
||||||
|
auto args = array_make<irValue *>(permanent_allocator(), 2);
|
||||||
|
args[0] = data;
|
||||||
|
args[1] = seed;
|
||||||
|
irValue *res = ir_emit_runtime_call(proc, "default_hasher_string", args);
|
||||||
|
ir_emit(proc, ir_instr_return(proc, res));
|
||||||
|
} else if (type->kind == Type_Struct) {
|
||||||
|
type_set_offsets(type);
|
||||||
|
data = ir_emit_conv(proc, data, t_u8_ptr);
|
||||||
|
|
||||||
|
auto args = array_make<irValue *>(permanent_allocator(), 2);
|
||||||
|
for_array(i, type->Struct.fields) {
|
||||||
|
i64 offset = type->Struct.offsets[i];
|
||||||
|
Entity *field = type->Struct.fields[i];
|
||||||
|
irValue *field_hasher = ir_get_hasher_proc_for_type(m, field->type);
|
||||||
|
irValue *ptr = ir_emit_ptr_offset(proc, data, ir_const_uintptr(offset));
|
||||||
|
|
||||||
|
args[0] = ptr;
|
||||||
|
args[1] = seed;
|
||||||
|
seed = ir_emit_call(proc, field_hasher, args);
|
||||||
|
}
|
||||||
|
ir_emit(proc, ir_instr_return(proc, seed));
|
||||||
|
} else if (type->kind == Type_Array) {
|
||||||
|
irValue *pres = ir_add_local_generated(proc, t_uintptr, false);
|
||||||
|
ir_emit_store(proc, pres, seed);
|
||||||
|
|
||||||
|
auto args = array_make<irValue *>(permanent_allocator(), 2);
|
||||||
|
irValue *elem_hasher = ir_get_hasher_proc_for_type(m, type->Array.elem);
|
||||||
|
|
||||||
|
auto loop_data = ir_loop_start(proc, type->Array.count, t_i32);
|
||||||
|
|
||||||
|
data = ir_emit_conv(proc, data, pt);
|
||||||
|
|
||||||
|
irValue *ptr = ir_emit_array_ep(proc, data, loop_data.idx);
|
||||||
|
args[0] = ptr;
|
||||||
|
args[1] = ir_emit_load(proc, pres);
|
||||||
|
irValue *new_seed = ir_emit_call(proc, elem_hasher, args);
|
||||||
|
ir_emit_store(proc, pres, new_seed);
|
||||||
|
|
||||||
|
ir_loop_end(proc, loop_data);
|
||||||
|
|
||||||
|
irValue *res = ir_emit_load(proc, pres);
|
||||||
|
ir_emit(proc, ir_instr_return(proc, res));
|
||||||
|
} else if (type->kind == Type_EnumeratedArray) {
|
||||||
|
irValue *pres = ir_add_local_generated(proc, t_uintptr, false);
|
||||||
|
ir_emit_store(proc, pres, seed);
|
||||||
|
|
||||||
|
auto args = array_make<irValue *>(permanent_allocator(), 2);
|
||||||
|
irValue *elem_hasher = ir_get_hasher_proc_for_type(m, type->Array.elem);
|
||||||
|
|
||||||
|
auto loop_data = ir_loop_start(proc, type->Array.count, t_i32);
|
||||||
|
|
||||||
|
data = ir_emit_conv(proc, data, pt);
|
||||||
|
|
||||||
|
irValue *ptr = ir_emit_array_ep(proc, data, loop_data.idx);
|
||||||
|
args[0] = ptr;
|
||||||
|
args[1] = ir_emit_load(proc, pres);
|
||||||
|
irValue *new_seed = ir_emit_call(proc, elem_hasher, args);
|
||||||
|
ir_emit_store(proc, pres, new_seed);
|
||||||
|
|
||||||
|
ir_loop_end(proc, loop_data);
|
||||||
|
|
||||||
|
irValue *res = ir_emit_load(proc, pres);
|
||||||
|
ir_emit(proc, ir_instr_return(proc, res));
|
||||||
|
} else {
|
||||||
|
GB_PANIC("Unhandled type for hasher: %s", type_to_string(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irValue *right) {
|
irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irValue *right) {
|
||||||
Type *a = base_type(ir_type(left));
|
Type *a = base_type(ir_type(left));
|
||||||
Type *b = base_type(ir_type(right));
|
Type *b = base_type(ir_type(right));
|
||||||
@@ -4966,7 +5216,7 @@ irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irVal
|
|||||||
} else {
|
} else {
|
||||||
if (is_type_simple_compare(tl) && (op_kind == Token_CmpEq || op_kind == Token_NotEq)) {
|
if (is_type_simple_compare(tl) && (op_kind == Token_CmpEq || op_kind == Token_NotEq)) {
|
||||||
// TODO(bill): Test to see if this is actually faster!!!!
|
// TODO(bill): Test to see if this is actually faster!!!!
|
||||||
auto args = array_make<irValue *>(heap_allocator(), 3);
|
auto args = array_make<irValue *>(permanent_allocator(), 3);
|
||||||
args[0] = ir_emit_conv(proc, lhs, t_rawptr);
|
args[0] = ir_emit_conv(proc, lhs, t_rawptr);
|
||||||
args[1] = ir_emit_conv(proc, rhs, t_rawptr);
|
args[1] = ir_emit_conv(proc, rhs, t_rawptr);
|
||||||
args[2] = ir_const_int(type_size_of(tl));
|
args[2] = ir_const_int(type_size_of(tl));
|
||||||
@@ -4992,6 +5242,30 @@ irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irVal
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_type_struct(a) && is_type_comparable(a)) {
|
||||||
|
irValue *left_ptr = ir_address_from_load_or_generate_local(proc, left);
|
||||||
|
irValue *right_ptr = ir_address_from_load_or_generate_local(proc, right);
|
||||||
|
irValue *res = {};
|
||||||
|
if (is_type_simple_compare(a)) {
|
||||||
|
// TODO(bill): Test to see if this is actually faster!!!!
|
||||||
|
auto args = array_make<irValue *>(permanent_allocator(), 3);
|
||||||
|
args[0] = ir_emit_conv(proc, left_ptr, t_rawptr);
|
||||||
|
args[1] = ir_emit_conv(proc, right_ptr, t_rawptr);
|
||||||
|
args[2] = ir_const_int(type_size_of(a));
|
||||||
|
res = ir_emit_runtime_call(proc, "memory_equal", args);
|
||||||
|
} else {
|
||||||
|
irValue *value = ir_get_equal_proc_for_type(proc->module, a);
|
||||||
|
auto args = array_make<irValue *>(permanent_allocator(), 2);
|
||||||
|
args[0] = ir_emit_conv(proc, left_ptr, t_rawptr);
|
||||||
|
args[1] = ir_emit_conv(proc, right_ptr, t_rawptr);
|
||||||
|
res = ir_emit_call(proc, value, args);
|
||||||
|
}
|
||||||
|
if (op_kind == Token_NotEq) {
|
||||||
|
res = ir_emit_unary_arith(proc, Token_Not, res, ir_type(res));
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
if (is_type_string(a)) {
|
if (is_type_string(a)) {
|
||||||
if (is_type_cstring(a)) {
|
if (is_type_cstring(a)) {
|
||||||
left = ir_emit_conv(proc, left, t_string);
|
left = ir_emit_conv(proc, left, t_string);
|
||||||
@@ -6636,7 +6910,7 @@ void ir_mangle_add_sub_type_name(irModule *m, Entity *field, String parent) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (is_type_proc(field->type)) {
|
if (is_type_proc(field->type)) {
|
||||||
set_procedure_abi_types(heap_allocator(), field->type);
|
set_procedure_abi_types(field->type);
|
||||||
}
|
}
|
||||||
|
|
||||||
String cn = field->token.string;
|
String cn = field->token.string;
|
||||||
@@ -6733,7 +7007,7 @@ irValue *ir_gen_anonymous_proc_lit(irModule *m, String prefix_name, Ast *expr, i
|
|||||||
String name = make_string(name_text, name_len-1);
|
String name = make_string(name_text, name_len-1);
|
||||||
|
|
||||||
Type *type = type_of_expr(expr);
|
Type *type = type_of_expr(expr);
|
||||||
set_procedure_abi_types(heap_allocator(), type);
|
set_procedure_abi_types(type);
|
||||||
irValue *value = ir_value_procedure(m, nullptr, type, pl->type, pl->body, name);
|
irValue *value = ir_value_procedure(m, nullptr, type, pl->type, pl->body, name);
|
||||||
|
|
||||||
value->Proc.tags = pl->tags;
|
value->Proc.tags = pl->tags;
|
||||||
@@ -6789,6 +7063,9 @@ void ir_gen_global_type_name(irModule *m, Entity *e, String name) {
|
|||||||
if (!ir_min_dep_entity(m, e)) {
|
if (!ir_min_dep_entity(m, e)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (is_type_proc(e->type)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
irValue *t = ir_value_type_name(name, e->type);
|
irValue *t = ir_value_type_name(name, e->type);
|
||||||
ir_module_add_value(m, e, t);
|
ir_module_add_value(m, e, t);
|
||||||
string_map_set(&m->members, name, t);
|
string_map_set(&m->members, name, t);
|
||||||
@@ -6884,7 +7161,7 @@ irValue *ir_find_global_variable(irProcedure *proc, String name) {
|
|||||||
return *value;
|
return *value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ir_build_stmt_list(irProcedure *proc, Array<Ast *> stmts);
|
void ir_build_stmt_list(irProcedure *proc, Slice<Ast *> stmts);
|
||||||
void ir_build_assign_op(irProcedure *proc, irAddr const &lhs, irValue *value, TokenKind op);
|
void ir_build_assign_op(irProcedure *proc, irAddr const &lhs, irValue *value, TokenKind op);
|
||||||
|
|
||||||
bool is_double_pointer(Type *t) {
|
bool is_double_pointer(Type *t) {
|
||||||
@@ -6898,17 +7175,6 @@ bool is_double_pointer(Type *t) {
|
|||||||
return is_type_pointer(td);
|
return is_type_pointer(td);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
u64 ir_generate_source_code_location_hash(TokenPos pos) {
|
|
||||||
u64 h = 0xcbf29ce484222325;
|
|
||||||
for (isize i = 0; i < pos.file.len; i++) {
|
|
||||||
h = (h ^ u64(pos.file[i])) * 0x100000001b3;
|
|
||||||
}
|
|
||||||
h = h ^ (u64(pos.line) * 0x100000001b3);
|
|
||||||
h = h ^ (u64(pos.column) * 0x100000001b3);
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
|
|
||||||
irValue *ir_emit_source_code_location(irProcedure *proc, String procedure, TokenPos pos) {
|
irValue *ir_emit_source_code_location(irProcedure *proc, String procedure, TokenPos pos) {
|
||||||
gbAllocator a = ir_allocator();
|
gbAllocator a = ir_allocator();
|
||||||
irValue *v = ir_alloc_value(irValue_SourceCodeLocation);
|
irValue *v = ir_alloc_value(irValue_SourceCodeLocation);
|
||||||
@@ -6916,7 +7182,6 @@ irValue *ir_emit_source_code_location(irProcedure *proc, String procedure, Token
|
|||||||
v->SourceCodeLocation.line = ir_const_int(pos.line);
|
v->SourceCodeLocation.line = ir_const_int(pos.line);
|
||||||
v->SourceCodeLocation.column = ir_const_int(pos.column);
|
v->SourceCodeLocation.column = ir_const_int(pos.column);
|
||||||
v->SourceCodeLocation.procedure = ir_find_or_add_entity_string(proc->module, procedure);
|
v->SourceCodeLocation.procedure = ir_find_or_add_entity_string(proc->module, procedure);
|
||||||
v->SourceCodeLocation.hash = ir_generate_source_code_location_hash(pos);
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7355,7 +7620,7 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu
|
|||||||
// "Intrinsics"
|
// "Intrinsics"
|
||||||
case BuiltinProc_alloca:
|
case BuiltinProc_alloca:
|
||||||
{
|
{
|
||||||
auto args = array_make<irValue *>(heap_allocator(), 2);
|
auto args = array_make<irValue *>(permanent_allocator(), 2);
|
||||||
args[0] = ir_emit_conv(proc, ir_build_expr(proc, ce->args[0]), t_i32);
|
args[0] = ir_emit_conv(proc, ir_build_expr(proc, ce->args[0]), t_i32);
|
||||||
args[1] = ir_build_expr(proc, ce->args[1]);
|
args[1] = ir_build_expr(proc, ce->args[1]);
|
||||||
return ir_emit(proc, ir_instr_inline_code(proc, id, args, t_u8_ptr));
|
return ir_emit(proc, ir_instr_inline_code(proc, id, args, t_u8_ptr));
|
||||||
@@ -7459,7 +7724,11 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu
|
|||||||
return ir_emit(proc, ir_instr_atomic_cxchg(proc, type, address, old_value, new_value, id));
|
return ir_emit(proc, ir_instr_atomic_cxchg(proc, type, address, old_value, new_value, id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case BuiltinProc_type_equal_proc:
|
||||||
|
return ir_get_equal_proc_for_type(proc->module, ce->args[0]->tav.type);
|
||||||
|
|
||||||
|
case BuiltinProc_type_hasher_proc:
|
||||||
|
return ir_get_hasher_proc_for_type(proc->module, ce->args[0]->tav.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
GB_PANIC("Unhandled built-in procedure");
|
GB_PANIC("Unhandled built-in procedure");
|
||||||
@@ -7584,7 +7853,7 @@ irValue *ir_build_call_expr(irProcedure *proc, Ast *expr) {
|
|||||||
Type *proc_type_ = base_type(ir_type(value));
|
Type *proc_type_ = base_type(ir_type(value));
|
||||||
GB_ASSERT(proc_type_->kind == Type_Proc);
|
GB_ASSERT(proc_type_->kind == Type_Proc);
|
||||||
TypeProc *pt = &proc_type_->Proc;
|
TypeProc *pt = &proc_type_->Proc;
|
||||||
set_procedure_abi_types(heap_allocator(), proc_type_);
|
set_procedure_abi_types(proc_type_);
|
||||||
|
|
||||||
if (is_call_expr_field_value(ce)) {
|
if (is_call_expr_field_value(ce)) {
|
||||||
auto args = array_make<irValue *>(ir_allocator(), pt->param_count);
|
auto args = array_make<irValue *>(ir_allocator(), pt->param_count);
|
||||||
@@ -7801,7 +8070,11 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) {
|
|||||||
|
|
||||||
if (tv.value.kind != ExactValue_Invalid) {
|
if (tv.value.kind != ExactValue_Invalid) {
|
||||||
// NOTE(bill): Edge case
|
// NOTE(bill): Edge case
|
||||||
if (tv.value.kind != ExactValue_Compound &&
|
if (is_type_u8_array(tv.type) && tv.value.kind == ExactValue_String) {
|
||||||
|
return ir_add_module_constant(proc->module, tv.type, tv.value);
|
||||||
|
} else if (is_type_rune_array(tv.type) && tv.value.kind == ExactValue_String) {
|
||||||
|
return ir_add_module_constant(proc->module, tv.type, tv.value);
|
||||||
|
} else if (tv.value.kind != ExactValue_Compound &&
|
||||||
is_type_array(tv.type)) {
|
is_type_array(tv.type)) {
|
||||||
Type *elem = core_array_type(tv.type);
|
Type *elem = core_array_type(tv.type);
|
||||||
ExactValue value = convert_exact_value_for_type(tv.value, elem);
|
ExactValue value = convert_exact_value_for_type(tv.value, elem);
|
||||||
@@ -8207,7 +8480,7 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) {
|
|||||||
|
|
||||||
irValue *addr = ir_address_from_load_or_generate_local(proc, right);
|
irValue *addr = ir_address_from_load_or_generate_local(proc, right);
|
||||||
irValue *h = ir_gen_map_header(proc, addr, rt);
|
irValue *h = ir_gen_map_header(proc, addr, rt);
|
||||||
irValue *key = ir_gen_map_key(proc, left, rt->Map.key);
|
irValue *key = ir_gen_map_hash(proc, left, rt->Map.key);
|
||||||
|
|
||||||
auto args = array_make<irValue *>(ir_allocator(), 2);
|
auto args = array_make<irValue *>(ir_allocator(), 2);
|
||||||
args[0] = h;
|
args[0] = h;
|
||||||
@@ -9024,8 +9297,7 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) {
|
|||||||
if (cl->elems.count > 0) {
|
if (cl->elems.count > 0) {
|
||||||
ir_emit_store(proc, v, ir_add_module_constant(proc->module, type, exact_value_compound(expr)));
|
ir_emit_store(proc, v, ir_add_module_constant(proc->module, type, exact_value_compound(expr)));
|
||||||
|
|
||||||
auto temp_data = array_make<irCompoundLitElemTempData>(heap_allocator(), 0, cl->elems.count);
|
auto temp_data = array_make<irCompoundLitElemTempData>(temporary_allocator(), 0, cl->elems.count);
|
||||||
defer (array_free(&temp_data));
|
|
||||||
|
|
||||||
// NOTE(bill): Separate value, gep, store into their own chunks
|
// NOTE(bill): Separate value, gep, store into their own chunks
|
||||||
for_array(i, cl->elems) {
|
for_array(i, cl->elems) {
|
||||||
@@ -9123,8 +9395,7 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) {
|
|||||||
if (cl->elems.count > 0) {
|
if (cl->elems.count > 0) {
|
||||||
ir_emit_store(proc, v, ir_add_module_constant(proc->module, type, exact_value_compound(expr)));
|
ir_emit_store(proc, v, ir_add_module_constant(proc->module, type, exact_value_compound(expr)));
|
||||||
|
|
||||||
auto temp_data = array_make<irCompoundLitElemTempData>(heap_allocator(), 0, cl->elems.count);
|
auto temp_data = array_make<irCompoundLitElemTempData>(temporary_allocator(), 0, cl->elems.count);
|
||||||
defer (array_free(&temp_data));
|
|
||||||
|
|
||||||
// NOTE(bill): Separate value, gep, store into their own chunks
|
// NOTE(bill): Separate value, gep, store into their own chunks
|
||||||
for_array(i, cl->elems) {
|
for_array(i, cl->elems) {
|
||||||
@@ -9232,8 +9503,7 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) {
|
|||||||
|
|
||||||
irValue *data = ir_emit_array_ep(proc, slice->ConstantSlice.backing_array, v_zero32);
|
irValue *data = ir_emit_array_ep(proc, slice->ConstantSlice.backing_array, v_zero32);
|
||||||
|
|
||||||
auto temp_data = array_make<irCompoundLitElemTempData>(heap_allocator(), 0, cl->elems.count);
|
auto temp_data = array_make<irCompoundLitElemTempData>(temporary_allocator(), 0, cl->elems.count);
|
||||||
defer (array_free(&temp_data));
|
|
||||||
|
|
||||||
for_array(i, cl->elems) {
|
for_array(i, cl->elems) {
|
||||||
Ast *elem = cl->elems[i];
|
Ast *elem = cl->elems[i];
|
||||||
@@ -9574,7 +9844,7 @@ void ir_build_nested_proc(irProcedure *proc, AstProcLit *pd, Entity *e) {
|
|||||||
name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s.%.*s-%d", LIT(proc->name), LIT(pd_name), guid);
|
name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s.%.*s-%d", LIT(proc->name), LIT(pd_name), guid);
|
||||||
String name = make_string(name_text, name_len-1);
|
String name = make_string(name_text, name_len-1);
|
||||||
|
|
||||||
set_procedure_abi_types(heap_allocator(), e->type);
|
set_procedure_abi_types(e->type);
|
||||||
irValue *value = ir_value_procedure(proc->module, e, e->type, pd->type, pd->body, name);
|
irValue *value = ir_value_procedure(proc->module, e, e->type, pd->type, pd->body, name);
|
||||||
|
|
||||||
value->Proc.tags = pd->tags;
|
value->Proc.tags = pd->tags;
|
||||||
@@ -9673,7 +9943,7 @@ void ir_build_constant_value_decl(irProcedure *proc, AstValueDecl *vd) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_procedure_abi_types(heap_allocator(), e->type);
|
set_procedure_abi_types(e->type);
|
||||||
irValue *value = ir_value_procedure(proc->module, e, e->type, pl->type, pl->body, name);
|
irValue *value = ir_value_procedure(proc->module, e, e->type, pl->type, pl->body, name);
|
||||||
|
|
||||||
value->Proc.tags = pl->tags;
|
value->Proc.tags = pl->tags;
|
||||||
@@ -9692,7 +9962,7 @@ void ir_build_constant_value_decl(irProcedure *proc, AstValueDecl *vd) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ir_build_stmt_list(irProcedure *proc, Array<Ast *> stmts) {
|
void ir_build_stmt_list(irProcedure *proc, Slice<Ast *> stmts) {
|
||||||
// NOTE(bill): Precollect constant entities
|
// NOTE(bill): Precollect constant entities
|
||||||
for_array(i, stmts) {
|
for_array(i, stmts) {
|
||||||
Ast *stmt = stmts[i];
|
Ast *stmt = stmts[i];
|
||||||
@@ -9844,13 +10114,8 @@ void ir_build_range_indexed(irProcedure *proc, irValue *expr, Type *val_type, ir
|
|||||||
elem = ir_emit_load(proc, elem);
|
elem = ir_emit_load(proc, elem);
|
||||||
|
|
||||||
irValue *entry = ir_emit_ptr_offset(proc, elem, idx);
|
irValue *entry = ir_emit_ptr_offset(proc, elem, idx);
|
||||||
val = ir_emit_load(proc, ir_emit_struct_ep(proc, entry, 2));
|
idx = ir_emit_load(proc, ir_emit_struct_ep(proc, entry, 2));
|
||||||
|
val = ir_emit_load(proc, ir_emit_struct_ep(proc, entry, 3));
|
||||||
irValue *key_raw = ir_emit_struct_ep(proc, entry, 0);
|
|
||||||
key_raw = ir_emit_struct_ep(proc, key_raw, 1);
|
|
||||||
irValue *key = ir_emit_conv(proc, key_raw, alloc_type_pointer(expr_type->Map.key));
|
|
||||||
|
|
||||||
idx = ir_emit_load(proc, key);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -9995,7 +10260,7 @@ void ir_build_range_enum(irProcedure *proc, Type *enum_type, Type *val_type, irV
|
|||||||
irValue *max_count = ir_const_int(enum_count);
|
irValue *max_count = ir_const_int(enum_count);
|
||||||
|
|
||||||
irValue *ti = ir_type_info(proc, t);
|
irValue *ti = ir_type_info(proc, t);
|
||||||
irValue *variant = ir_emit_struct_ep(proc, ti, 3);
|
irValue *variant = ir_emit_struct_ep(proc, ti, 4);
|
||||||
irValue *eti_ptr = ir_emit_conv(proc, variant, t_type_info_enum_ptr);
|
irValue *eti_ptr = ir_emit_conv(proc, variant, t_type_info_enum_ptr);
|
||||||
irValue *values = ir_emit_load(proc, ir_emit_struct_ep(proc, eti_ptr, 2));
|
irValue *values = ir_emit_load(proc, ir_emit_struct_ep(proc, eti_ptr, 2));
|
||||||
irValue *values_data = ir_slice_elem(proc, values);
|
irValue *values_data = ir_slice_elem(proc, values);
|
||||||
@@ -10179,7 +10444,7 @@ void ir_build_stmt_internal(irProcedure *proc, Ast *node) {
|
|||||||
|
|
||||||
String mangled_name = {};
|
String mangled_name = {};
|
||||||
{
|
{
|
||||||
gbString str = gb_string_make_length(heap_allocator(), proc->name.text, proc->name.len);
|
gbString str = gb_string_make_length(permanent_allocator(), proc->name.text, proc->name.len);
|
||||||
str = gb_string_appendc(str, "-");
|
str = gb_string_appendc(str, "-");
|
||||||
str = gb_string_append_fmt(str, ".%.*s-%llu", LIT(name), cast(long long)e->id);
|
str = gb_string_append_fmt(str, ".%.*s-%llu", LIT(name), cast(long long)e->id);
|
||||||
mangled_name.text = cast(u8 *)str;
|
mangled_name.text = cast(u8 *)str;
|
||||||
@@ -10902,7 +11167,7 @@ void ir_build_stmt_internal(irProcedure *proc, Ast *node) {
|
|||||||
|
|
||||||
ast_node(body, BlockStmt, ss->body);
|
ast_node(body, BlockStmt, ss->body);
|
||||||
|
|
||||||
Array<Ast *> default_stmts = {};
|
Slice<Ast *> default_stmts = {};
|
||||||
irBlock *default_fall = nullptr;
|
irBlock *default_fall = nullptr;
|
||||||
irBlock *default_block = nullptr;
|
irBlock *default_block = nullptr;
|
||||||
|
|
||||||
@@ -11349,6 +11614,9 @@ void ir_begin_procedure_body(irProcedure *proc) {
|
|||||||
|
|
||||||
|
|
||||||
bool ir_remove_dead_instr(irProcedure *proc) {
|
bool ir_remove_dead_instr(irProcedure *proc) {
|
||||||
|
if (proc->ignore_dead_instr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
isize elimination_count = 0;
|
isize elimination_count = 0;
|
||||||
retry:
|
retry:
|
||||||
#if 1
|
#if 1
|
||||||
@@ -11471,11 +11739,11 @@ void ir_insert_code_before_proc(irProcedure* proc, irProcedure *parent) {
|
|||||||
void ir_build_proc(irValue *value, irProcedure *parent) {
|
void ir_build_proc(irValue *value, irProcedure *parent) {
|
||||||
irProcedure *proc = &value->Proc;
|
irProcedure *proc = &value->Proc;
|
||||||
|
|
||||||
set_procedure_abi_types(heap_allocator(), proc->type);
|
set_procedure_abi_types(proc->type);
|
||||||
|
|
||||||
proc->parent = parent;
|
proc->parent = parent;
|
||||||
|
|
||||||
if (proc->body != nullptr) {
|
if (proc->body != nullptr && proc->body->kind != Ast_Invalid) {
|
||||||
u64 prev_state_flags = proc->module->state_flags;
|
u64 prev_state_flags = proc->module->state_flags;
|
||||||
|
|
||||||
if (proc->tags != 0) {
|
if (proc->tags != 0) {
|
||||||
@@ -11577,6 +11845,8 @@ void ir_init_module(irModule *m, Checker *c) {
|
|||||||
map_init(&m->debug_info, heap_allocator());
|
map_init(&m->debug_info, heap_allocator());
|
||||||
map_init(&m->entity_names, heap_allocator());
|
map_init(&m->entity_names, heap_allocator());
|
||||||
map_init(&m->anonymous_proc_lits, heap_allocator());
|
map_init(&m->anonymous_proc_lits, heap_allocator());
|
||||||
|
map_init(&m->equal_procs, heap_allocator());
|
||||||
|
map_init(&m->hasher_procs, heap_allocator());
|
||||||
array_init(&m->procs, heap_allocator());
|
array_init(&m->procs, heap_allocator());
|
||||||
array_init(&m->procs_to_generate, heap_allocator());
|
array_init(&m->procs_to_generate, heap_allocator());
|
||||||
array_init(&m->foreign_library_paths, heap_allocator());
|
array_init(&m->foreign_library_paths, heap_allocator());
|
||||||
@@ -11860,6 +12130,8 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
|
|||||||
// Useful types
|
// Useful types
|
||||||
Type *t_i64_slice_ptr = alloc_type_pointer(alloc_type_slice(t_i64));
|
Type *t_i64_slice_ptr = alloc_type_pointer(alloc_type_slice(t_i64));
|
||||||
Type *t_string_slice_ptr = alloc_type_pointer(alloc_type_slice(t_string));
|
Type *t_string_slice_ptr = alloc_type_pointer(alloc_type_slice(t_string));
|
||||||
|
Entity *type_info_flags_entity = find_core_entity(info->checker, str_lit("Type_Info_Flags"));
|
||||||
|
Type *t_type_info_flags = type_info_flags_entity->type;
|
||||||
|
|
||||||
i32 type_info_member_types_index = 0;
|
i32 type_info_member_types_index = 0;
|
||||||
i32 type_info_member_names_index = 0;
|
i32 type_info_member_names_index = 0;
|
||||||
@@ -11879,11 +12151,14 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
|
|||||||
|
|
||||||
irValue *tag = nullptr;
|
irValue *tag = nullptr;
|
||||||
irValue *ti_ptr = ir_emit_array_epi(proc, ir_global_type_info_data, cast(i32)entry_index);
|
irValue *ti_ptr = ir_emit_array_epi(proc, ir_global_type_info_data, cast(i32)entry_index);
|
||||||
irValue *variant_ptr = ir_emit_struct_ep(proc, ti_ptr, 3);
|
irValue *variant_ptr = ir_emit_struct_ep(proc, ti_ptr, 4);
|
||||||
|
|
||||||
|
irValue *type_info_flags = ir_value_constant(t_type_info_flags, exact_value_i64(type_info_flags_of_type(t)));
|
||||||
|
|
||||||
ir_emit_store(proc, ir_emit_struct_ep(proc, ti_ptr, 0), ir_const_int(type_size_of(t)));
|
ir_emit_store(proc, ir_emit_struct_ep(proc, ti_ptr, 0), ir_const_int(type_size_of(t)));
|
||||||
ir_emit_store(proc, ir_emit_struct_ep(proc, ti_ptr, 1), ir_const_int(type_align_of(t)));
|
ir_emit_store(proc, ir_emit_struct_ep(proc, ti_ptr, 1), ir_const_int(type_align_of(t)));
|
||||||
ir_emit_store(proc, ir_emit_struct_ep(proc, ti_ptr, 2), ir_typeid(proc->module, t));
|
ir_emit_store(proc, ir_emit_struct_ep(proc, ti_ptr, 2), type_info_flags);
|
||||||
|
ir_emit_store(proc, ir_emit_struct_ep(proc, ti_ptr, 3), ir_typeid(proc->module, t));
|
||||||
|
|
||||||
|
|
||||||
switch (t->kind) {
|
switch (t->kind) {
|
||||||
@@ -11897,6 +12172,21 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
|
|||||||
|
|
||||||
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), name);
|
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), name);
|
||||||
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 1), gtip);
|
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 1), gtip);
|
||||||
|
|
||||||
|
if (t->Named.type_name->pkg) {
|
||||||
|
irValue *name = ir_const_string(proc->module, t->Named.type_name->pkg->name);
|
||||||
|
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 2), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
String proc_name = {};
|
||||||
|
if (t->Named.type_name->parent_proc_decl) {
|
||||||
|
DeclInfo *decl = t->Named.type_name->parent_proc_decl;
|
||||||
|
if (decl->entity && decl->entity->kind == Entity_Procedure) {
|
||||||
|
proc_name = decl->entity->token.string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
irValue *loc = ir_emit_source_code_location(proc, proc_name, t->Named.type_name->token.pos);
|
||||||
|
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 3), loc);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12234,8 +12524,13 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
|
|||||||
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 6), is_raw_union);
|
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 6), is_raw_union);
|
||||||
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 7), is_custom_align);
|
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 7), is_custom_align);
|
||||||
|
|
||||||
|
if (is_type_comparable(t) && !is_type_simple_compare(t)) {
|
||||||
|
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 8), ir_get_equal_proc_for_type(proc->module, t));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (t->Struct.soa_kind != StructSoa_None) {
|
if (t->Struct.soa_kind != StructSoa_None) {
|
||||||
irValue *kind = ir_emit_struct_ep(proc, tag, 8);
|
irValue *kind = ir_emit_struct_ep(proc, tag, 9);
|
||||||
Type *kind_type = type_deref(ir_type(kind));
|
Type *kind_type = type_deref(ir_type(kind));
|
||||||
|
|
||||||
irValue *soa_kind = ir_value_constant(kind_type, exact_value_i64(t->Struct.soa_kind));
|
irValue *soa_kind = ir_value_constant(kind_type, exact_value_i64(t->Struct.soa_kind));
|
||||||
@@ -12244,8 +12539,8 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
|
|||||||
|
|
||||||
|
|
||||||
ir_emit_store(proc, kind, soa_kind);
|
ir_emit_store(proc, kind, soa_kind);
|
||||||
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 9), soa_type);
|
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 10), soa_type);
|
||||||
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 10), soa_len);
|
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 11), soa_len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12308,10 +12603,14 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
|
|||||||
irValue *key = ir_emit_struct_ep(proc, tag, 0);
|
irValue *key = ir_emit_struct_ep(proc, tag, 0);
|
||||||
irValue *value = ir_emit_struct_ep(proc, tag, 1);
|
irValue *value = ir_emit_struct_ep(proc, tag, 1);
|
||||||
irValue *generated_struct = ir_emit_struct_ep(proc, tag, 2);
|
irValue *generated_struct = ir_emit_struct_ep(proc, tag, 2);
|
||||||
|
irValue *key_equal = ir_emit_struct_ep(proc, tag, 3);
|
||||||
|
irValue *key_hasher = ir_emit_struct_ep(proc, tag, 4);
|
||||||
|
|
||||||
ir_emit_store(proc, key, ir_get_type_info_ptr(proc, t->Map.key));
|
ir_emit_store(proc, key, ir_get_type_info_ptr(proc, t->Map.key));
|
||||||
ir_emit_store(proc, value, ir_get_type_info_ptr(proc, t->Map.value));
|
ir_emit_store(proc, value, ir_get_type_info_ptr(proc, t->Map.value));
|
||||||
ir_emit_store(proc, generated_struct, ir_get_type_info_ptr(proc, t->Map.generated_struct_type));
|
ir_emit_store(proc, generated_struct, ir_get_type_info_ptr(proc, t->Map.generated_struct_type));
|
||||||
|
ir_emit_store(proc, key_equal, ir_get_equal_proc_for_type(proc->module, t->Map.key));
|
||||||
|
ir_emit_store(proc, key_hasher, ir_get_hasher_proc_for_type(proc->module, t->Map.key));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12612,7 +12911,7 @@ void ir_gen_tree(irGen *s) {
|
|||||||
|
|
||||||
Ast *type_expr = pl->type;
|
Ast *type_expr = pl->type;
|
||||||
|
|
||||||
set_procedure_abi_types(heap_allocator(), e->type);
|
set_procedure_abi_types(e->type);
|
||||||
irValue *p = ir_value_procedure(m, e, e->type, type_expr, body, name);
|
irValue *p = ir_value_procedure(m, e, e->type, type_expr, body, name);
|
||||||
p->Proc.tags = pl->tags;
|
p->Proc.tags = pl->tags;
|
||||||
p->Proc.inlining = pl->inlining;
|
p->Proc.inlining = pl->inlining;
|
||||||
@@ -12646,7 +12945,7 @@ void ir_gen_tree(irGen *s) {
|
|||||||
|
|
||||||
|
|
||||||
#if defined(GB_SYSTEM_WINDOWS)
|
#if defined(GB_SYSTEM_WINDOWS)
|
||||||
if (build_context.build_mode == BuildMode_DynamicLibrary && !has_dll_main) {
|
if (build_context.build_mode == BuildMode_DynamicLibrary && !has_dll_main && !build_context.no_entry_point) {
|
||||||
// DllMain :: proc(inst: rawptr, reason: u32, reserved: rawptr) -> i32
|
// DllMain :: proc(inst: rawptr, reason: u32, reserved: rawptr) -> i32
|
||||||
String name = str_lit("DllMain");
|
String name = str_lit("DllMain");
|
||||||
Type *proc_params = alloc_type_tuple();
|
Type *proc_params = alloc_type_tuple();
|
||||||
@@ -12717,7 +13016,7 @@ void ir_gen_tree(irGen *s) {
|
|||||||
ir_emit_return(proc, v_one32);
|
ir_emit_return(proc, v_one32);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (!(build_context.build_mode == BuildMode_DynamicLibrary && !has_dll_main)) {
|
if (!(build_context.build_mode == BuildMode_DynamicLibrary && !has_dll_main) && !build_context.no_entry_point) {
|
||||||
// main :: proc(argc: i32, argv: ^^u8) -> i32
|
// main :: proc(argc: i32, argv: ^^u8) -> i32
|
||||||
String name = str_lit("main");
|
String name = str_lit("main");
|
||||||
|
|
||||||
@@ -12784,11 +13083,18 @@ void ir_gen_tree(irGen *s) {
|
|||||||
ir_fill_slice(proc, global_args, argv, ir_emit_conv(proc, argc, t_int));
|
ir_fill_slice(proc, global_args, argv, ir_emit_conv(proc, argc, t_int));
|
||||||
|
|
||||||
ir_emit(proc, ir_alloc_instr(proc, irInstr_StartupRuntime));
|
ir_emit(proc, ir_alloc_instr(proc, irInstr_StartupRuntime));
|
||||||
{
|
Array<irValue *> empty_args = {};
|
||||||
|
if (build_context.command_kind == Command_test) {
|
||||||
|
for_array(i, m->info->testing_procedures) {
|
||||||
|
Entity *e = m->info->testing_procedures[i];
|
||||||
|
irValue **found = map_get(&proc->module->values, hash_entity(e));
|
||||||
|
GB_ASSERT(found != nullptr);
|
||||||
|
ir_emit_call(proc, *found, empty_args);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
irValue **found = map_get(&proc->module->values, hash_entity(entry_point));
|
irValue **found = map_get(&proc->module->values, hash_entity(entry_point));
|
||||||
if (found != nullptr) {
|
if (found != nullptr) {
|
||||||
Array<irValue *> args = {};
|
ir_emit_call(proc, *found, empty_args);
|
||||||
ir_emit_call(proc, *found, args);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12796,7 +13102,7 @@ void ir_gen_tree(irGen *s) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(GB_SYSTEM_WINDOWS)
|
#if defined(GB_SYSTEM_WINDOWS)
|
||||||
if (build_context.build_mode != BuildMode_DynamicLibrary && build_context.no_crt) {
|
if (build_context.build_mode != BuildMode_DynamicLibrary && build_context.no_crt && !build_context.no_entry_point) {
|
||||||
s->print_chkstk = true;
|
s->print_chkstk = true;
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
+45
-20
@@ -76,7 +76,6 @@ void ir_write_u64(irFileBuffer *f, u64 i) {
|
|||||||
}
|
}
|
||||||
void ir_write_big_int(irFileBuffer *f, BigInt const &x, Type *type, bool swap_endian) {
|
void ir_write_big_int(irFileBuffer *f, BigInt const &x, Type *type, bool swap_endian) {
|
||||||
if (x.len == 2) {
|
if (x.len == 2) {
|
||||||
gbAllocator a = heap_allocator(); // TODO(bill): Change this allocator
|
|
||||||
u64 words[2] = {};
|
u64 words[2] = {};
|
||||||
BigInt y = x;
|
BigInt y = x;
|
||||||
if (swap_endian) {
|
if (swap_endian) {
|
||||||
@@ -88,9 +87,8 @@ void ir_write_big_int(irFileBuffer *f, BigInt const &x, Type *type, bool swap_en
|
|||||||
y.d.words = words;
|
y.d.words = words;
|
||||||
}
|
}
|
||||||
|
|
||||||
String s = big_int_to_string(a, &y, 10);
|
String s = big_int_to_string(temporary_allocator(), &y, 10);
|
||||||
ir_write_string(f, s);
|
ir_write_string(f, s);
|
||||||
gb_free(a, s.text);
|
|
||||||
} else {
|
} else {
|
||||||
i64 i = 0;
|
i64 i = 0;
|
||||||
if (x.neg) {
|
if (x.neg) {
|
||||||
@@ -296,7 +294,7 @@ void ir_print_alignment_prefix_hack(irFileBuffer *f, i64 alignment) {
|
|||||||
|
|
||||||
|
|
||||||
void ir_print_proc_results(irFileBuffer *f, irModule *m, Type *t) {
|
void ir_print_proc_results(irFileBuffer *f, irModule *m, Type *t) {
|
||||||
set_procedure_abi_types(heap_allocator(), t);
|
set_procedure_abi_types(t);
|
||||||
|
|
||||||
GB_ASSERT(is_type_proc(t));
|
GB_ASSERT(is_type_proc(t));
|
||||||
t = base_type(t);
|
t = base_type(t);
|
||||||
@@ -325,7 +323,7 @@ void ir_print_proc_results(irFileBuffer *f, irModule *m, Type *t) {
|
|||||||
|
|
||||||
|
|
||||||
void ir_print_proc_type_without_pointer(irFileBuffer *f, irModule *m, Type *t) {
|
void ir_print_proc_type_without_pointer(irFileBuffer *f, irModule *m, Type *t) {
|
||||||
set_procedure_abi_types(heap_allocator(), t);
|
set_procedure_abi_types(t);
|
||||||
|
|
||||||
i64 word_bits = 8*build_context.word_size;
|
i64 word_bits = 8*build_context.word_size;
|
||||||
t = base_type(t);
|
t = base_type(t);
|
||||||
@@ -736,6 +734,28 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
|
|||||||
if (is_type_array(type) && value.kind == ExactValue_String && !is_type_u8(core_array_type(type))) {
|
if (is_type_array(type) && value.kind == ExactValue_String && !is_type_u8(core_array_type(type))) {
|
||||||
i64 count = type->Array.count;
|
i64 count = type->Array.count;
|
||||||
Type *elem = type->Array.elem;
|
Type *elem = type->Array.elem;
|
||||||
|
|
||||||
|
if (is_type_rune_array(type)) {
|
||||||
|
Rune rune;
|
||||||
|
isize offset = 0;
|
||||||
|
isize width = 1;
|
||||||
|
String s = value.value_string;
|
||||||
|
ir_write_byte(f, '[');
|
||||||
|
for (i64 i = 0; i < count && offset < s.len; i++) {
|
||||||
|
width = gb_utf8_decode(s.text+offset, s.len-offset, &rune);
|
||||||
|
if (i > 0) ir_write_str_lit(f, ", ");
|
||||||
|
ir_print_type(f, m, elem);
|
||||||
|
ir_write_byte(f, ' ');
|
||||||
|
ir_print_exact_value(f, m, exact_value_i64(rune), elem);
|
||||||
|
offset += width;
|
||||||
|
}
|
||||||
|
GB_ASSERT(offset == s.len);
|
||||||
|
|
||||||
|
ir_write_byte(f, ']');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ir_write_byte(f, '[');
|
ir_write_byte(f, '[');
|
||||||
|
|
||||||
for (i64 i = 0; i < count; i++) {
|
for (i64 i = 0; i < count; i++) {
|
||||||
@@ -747,7 +767,7 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
|
|||||||
|
|
||||||
ir_write_byte(f, ']');
|
ir_write_byte(f, ']');
|
||||||
return;
|
return;
|
||||||
} else if (is_type_array(type) &&
|
} else if (is_type_array(type) &&
|
||||||
value.kind != ExactValue_Invalid &&
|
value.kind != ExactValue_Invalid &&
|
||||||
value.kind != ExactValue_String &&
|
value.kind != ExactValue_String &&
|
||||||
value.kind != ExactValue_Compound) {
|
value.kind != ExactValue_Compound) {
|
||||||
@@ -798,7 +818,11 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
|
|||||||
GB_ASSERT(is_type_array(type));
|
GB_ASSERT(is_type_array(type));
|
||||||
ir_write_str_lit(f, "c\"");
|
ir_write_str_lit(f, "c\"");
|
||||||
ir_print_escape_string(f, str, false, false);
|
ir_print_escape_string(f, str, false, false);
|
||||||
ir_write_str_lit(f, "\\00\"");
|
if (type->Array.count == str.len) {
|
||||||
|
ir_write_str_lit(f, "\"");
|
||||||
|
} else {
|
||||||
|
ir_write_str_lit(f, "\\00\"");
|
||||||
|
}
|
||||||
} else if (is_type_cstring(t)) {
|
} else if (is_type_cstring(t)) {
|
||||||
// HACK NOTE(bill): This is a hack but it works because strings are created at the very end
|
// HACK NOTE(bill): This is a hack but it works because strings are created at the very end
|
||||||
// of the .ll file
|
// of the .ll file
|
||||||
@@ -812,7 +836,7 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
|
|||||||
ir_write_str_lit(f, ", ");
|
ir_write_str_lit(f, ", ");
|
||||||
ir_print_type(f, m, t_i32);
|
ir_print_type(f, m, t_i32);
|
||||||
ir_write_str_lit(f, " 0, i32 0)");
|
ir_write_str_lit(f, " 0, i32 0)");
|
||||||
}else {
|
} else {
|
||||||
// HACK NOTE(bill): This is a hack but it works because strings are created at the very end
|
// HACK NOTE(bill): This is a hack but it works because strings are created at the very end
|
||||||
// of the .ll file
|
// of the .ll file
|
||||||
irValue *str_array = ir_add_global_string_array(m, str);
|
irValue *str_array = ir_add_global_string_array(m, str);
|
||||||
@@ -929,9 +953,9 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
|
|||||||
ir_write_byte(f, ' ');
|
ir_write_byte(f, ' ');
|
||||||
ir_write_byte(f, '{');
|
ir_write_byte(f, '{');
|
||||||
ir_print_type(f, m, ft); ir_write_byte(f, ' ');
|
ir_print_type(f, m, ft); ir_write_byte(f, ' ');
|
||||||
ir_print_exact_value(f, m, exact_value_float(value.value_complex.real), ft);
|
ir_print_exact_value(f, m, exact_value_float(value.value_complex->real), ft);
|
||||||
ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' ');
|
ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' ');
|
||||||
ir_print_exact_value(f, m, exact_value_float(value.value_complex.imag), ft);
|
ir_print_exact_value(f, m, exact_value_float(value.value_complex->imag), ft);
|
||||||
ir_write_byte(f, '}');
|
ir_write_byte(f, '}');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -944,13 +968,13 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
|
|||||||
ir_write_byte(f, ' ');
|
ir_write_byte(f, ' ');
|
||||||
ir_write_byte(f, '{');
|
ir_write_byte(f, '{');
|
||||||
ir_print_type(f, m, ft); ir_write_byte(f, ' ');
|
ir_print_type(f, m, ft); ir_write_byte(f, ' ');
|
||||||
ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.imag), ft);
|
ir_print_exact_value(f, m, exact_value_float(value.value_quaternion->imag), ft);
|
||||||
ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' ');
|
ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' ');
|
||||||
ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.jmag), ft);
|
ir_print_exact_value(f, m, exact_value_float(value.value_quaternion->jmag), ft);
|
||||||
ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' ');
|
ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' ');
|
||||||
ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.kmag), ft);
|
ir_print_exact_value(f, m, exact_value_float(value.value_quaternion->kmag), ft);
|
||||||
ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' ');
|
ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' ');
|
||||||
ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.real), ft);
|
ir_print_exact_value(f, m, exact_value_float(value.value_quaternion->real), ft);
|
||||||
ir_write_byte(f, '}');
|
ir_write_byte(f, '}');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1406,7 +1430,6 @@ void ir_print_value(irFileBuffer *f, irModule *m, irValue *value, Type *type_hin
|
|||||||
irValue *line = value->SourceCodeLocation.line;
|
irValue *line = value->SourceCodeLocation.line;
|
||||||
irValue *column = value->SourceCodeLocation.column;
|
irValue *column = value->SourceCodeLocation.column;
|
||||||
irValue *procedure = value->SourceCodeLocation.procedure;
|
irValue *procedure = value->SourceCodeLocation.procedure;
|
||||||
u64 hash = value->SourceCodeLocation.hash;
|
|
||||||
|
|
||||||
ir_write_byte(f, '{');
|
ir_write_byte(f, '{');
|
||||||
ir_print_type(f, m, t_string); ir_write_byte(f, ' '); ir_print_value(f, m, file, t_string);
|
ir_print_type(f, m, t_string); ir_write_byte(f, ' '); ir_print_value(f, m, file, t_string);
|
||||||
@@ -1416,8 +1439,6 @@ void ir_print_value(irFileBuffer *f, irModule *m, irValue *value, Type *type_hin
|
|||||||
ir_print_type(f, m, t_int); ir_write_byte(f, ' '); ir_print_value(f, m, column, t_int);
|
ir_print_type(f, m, t_int); ir_write_byte(f, ' '); ir_print_value(f, m, column, t_int);
|
||||||
ir_write_string(f, str_lit(", "));
|
ir_write_string(f, str_lit(", "));
|
||||||
ir_print_type(f, m, t_string); ir_write_byte(f, ' '); ir_print_value(f, m, procedure, t_string);
|
ir_print_type(f, m, t_string); ir_write_byte(f, ' '); ir_print_value(f, m, procedure, t_string);
|
||||||
ir_write_string(f, str_lit(", "));
|
|
||||||
ir_print_type(f, m, t_u64); ir_write_byte(f, ' '); ir_write_u64(f, hash);
|
|
||||||
ir_write_byte(f, '}');
|
ir_write_byte(f, '}');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1551,7 +1572,11 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case BuiltinProc_cpu_relax:
|
case BuiltinProc_cpu_relax:
|
||||||
ir_write_str_lit(f, "call void asm sideeffect \"pause\", \"\"()");
|
if (build_context.metrics.arch == TargetArch_amd64) {
|
||||||
|
ir_write_str_lit(f, "call void asm sideeffect \"pause\", \"\"()");
|
||||||
|
} else {
|
||||||
|
// ir_write_str_lit(f, "call void asm sideeffect \"yield\", \"\"()");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default: GB_PANIC("Unknown inline code %d", instr->InlineCode.id); break;
|
default: GB_PANIC("Unknown inline code %d", instr->InlineCode.id); break;
|
||||||
}
|
}
|
||||||
@@ -2189,7 +2214,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
|
|||||||
irInstrCall *call = &instr->Call;
|
irInstrCall *call = &instr->Call;
|
||||||
Type *proc_type = base_type(ir_type(call->value));
|
Type *proc_type = base_type(ir_type(call->value));
|
||||||
GB_ASSERT(is_type_proc(proc_type));
|
GB_ASSERT(is_type_proc(proc_type));
|
||||||
set_procedure_abi_types(heap_allocator(), proc_type);
|
set_procedure_abi_types(proc_type);
|
||||||
|
|
||||||
bool is_c_vararg = proc_type->Proc.c_vararg;
|
bool is_c_vararg = proc_type->Proc.c_vararg;
|
||||||
Type *result_type = call->type;
|
Type *result_type = call->type;
|
||||||
@@ -2396,7 +2421,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
|
|||||||
|
|
||||||
|
|
||||||
void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) {
|
void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) {
|
||||||
set_procedure_abi_types(heap_allocator(), proc->type);
|
set_procedure_abi_types(proc->type);
|
||||||
|
|
||||||
if (proc->body == nullptr) {
|
if (proc->body == nullptr) {
|
||||||
ir_write_str_lit(f, "declare ");
|
ir_write_str_lit(f, "declare ");
|
||||||
|
|||||||
@@ -0,0 +1,959 @@
|
|||||||
|
enum lbArgKind {
|
||||||
|
lbArg_Direct,
|
||||||
|
lbArg_Indirect,
|
||||||
|
lbArg_Ignore,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct lbArgType {
|
||||||
|
lbArgKind kind;
|
||||||
|
LLVMTypeRef type;
|
||||||
|
LLVMTypeRef cast_type; // Optional
|
||||||
|
LLVMTypeRef pad_type; // Optional
|
||||||
|
LLVMAttributeRef attribute; // Optional
|
||||||
|
};
|
||||||
|
|
||||||
|
lbArgType lb_arg_type_direct(LLVMTypeRef type, LLVMTypeRef cast_type, LLVMTypeRef pad_type, LLVMAttributeRef attr) {
|
||||||
|
return lbArgType{lbArg_Direct, type, cast_type, pad_type, attr};
|
||||||
|
}
|
||||||
|
lbArgType lb_arg_type_direct(LLVMTypeRef type) {
|
||||||
|
return lb_arg_type_direct(type, nullptr, nullptr, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
lbArgType lb_arg_type_indirect(LLVMTypeRef type, LLVMAttributeRef attr) {
|
||||||
|
return lbArgType{lbArg_Indirect, type, nullptr, nullptr, attr};
|
||||||
|
}
|
||||||
|
|
||||||
|
lbArgType lb_arg_type_ignore(LLVMTypeRef type) {
|
||||||
|
return lbArgType{lbArg_Ignore, type, nullptr, nullptr, nullptr};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct lbFunctionType {
|
||||||
|
LLVMContextRef ctx;
|
||||||
|
ProcCallingConvention calling_convention;
|
||||||
|
Array<lbArgType> args;
|
||||||
|
lbArgType ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
i64 llvm_align_formula(i64 off, i64 a) {
|
||||||
|
return (off + a - 1) / a * a;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool lb_is_type_kind(LLVMTypeRef type, LLVMTypeKind kind) {
|
||||||
|
if (type == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return LLVMGetTypeKind(type) == kind;
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVMTypeRef lb_function_type_to_llvm_ptr(lbFunctionType *ft, bool is_var_arg) {
|
||||||
|
unsigned arg_count = cast(unsigned)ft->args.count;
|
||||||
|
unsigned offset = 0;
|
||||||
|
|
||||||
|
LLVMTypeRef ret = nullptr;
|
||||||
|
if (ft->ret.kind == lbArg_Direct) {
|
||||||
|
if (ft->ret.cast_type != nullptr) {
|
||||||
|
ret = ft->ret.cast_type;
|
||||||
|
} else {
|
||||||
|
ret = ft->ret.type;
|
||||||
|
}
|
||||||
|
} else if (ft->ret.kind == lbArg_Indirect) {
|
||||||
|
offset += 1;
|
||||||
|
ret = LLVMVoidTypeInContext(ft->ctx);
|
||||||
|
} else if (ft->ret.kind == lbArg_Ignore) {
|
||||||
|
ret = LLVMVoidTypeInContext(ft->ctx);
|
||||||
|
}
|
||||||
|
GB_ASSERT_MSG(ret != nullptr, "%d", ft->ret.kind);
|
||||||
|
|
||||||
|
unsigned maximum_arg_count = offset+arg_count;
|
||||||
|
LLVMTypeRef *args = gb_alloc_array(heap_allocator(), LLVMTypeRef, maximum_arg_count);
|
||||||
|
if (offset == 1) {
|
||||||
|
GB_ASSERT(ft->ret.kind == lbArg_Indirect);
|
||||||
|
args[0] = LLVMPointerType(ft->ret.type, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned arg_index = offset;
|
||||||
|
for (unsigned i = 0; i < arg_count; i++) {
|
||||||
|
lbArgType *arg = &ft->args[i];
|
||||||
|
if (arg->kind == lbArg_Direct) {
|
||||||
|
LLVMTypeRef arg_type = nullptr;
|
||||||
|
if (ft->args[i].cast_type != nullptr) {
|
||||||
|
arg_type = arg->cast_type;
|
||||||
|
} else {
|
||||||
|
arg_type = arg->type;
|
||||||
|
}
|
||||||
|
args[arg_index++] = arg_type;
|
||||||
|
} else if (arg->kind == lbArg_Indirect) {
|
||||||
|
GB_ASSERT(!lb_is_type_kind(arg->type, LLVMPointerTypeKind));
|
||||||
|
args[arg_index++] = LLVMPointerType(arg->type, 0);
|
||||||
|
} else if (arg->kind == lbArg_Ignore) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsigned total_arg_count = arg_index;
|
||||||
|
LLVMTypeRef func_type = LLVMFunctionType(ret, args, total_arg_count, is_var_arg);
|
||||||
|
return LLVMPointerType(func_type, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void lb_add_function_type_attributes(LLVMValueRef fn, lbFunctionType *ft, ProcCallingConvention calling_convention) {
|
||||||
|
if (ft == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsigned arg_count = cast(unsigned)ft->args.count;
|
||||||
|
unsigned offset = 0;
|
||||||
|
if (ft->ret.kind == lbArg_Indirect) {
|
||||||
|
offset += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVMContextRef c = ft->ctx;
|
||||||
|
LLVMAttributeRef noalias_attr = lb_create_enum_attribute(c, "noalias", true);
|
||||||
|
LLVMAttributeRef nonnull_attr = lb_create_enum_attribute(c, "nonnull", true);
|
||||||
|
LLVMAttributeRef nocapture_attr = lb_create_enum_attribute(c, "nocapture", true);
|
||||||
|
|
||||||
|
unsigned arg_index = offset;
|
||||||
|
for (unsigned i = 0; i < arg_count; i++) {
|
||||||
|
lbArgType *arg = &ft->args[i];
|
||||||
|
if (arg->kind == lbArg_Ignore) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg->attribute) {
|
||||||
|
LLVMAddAttributeAtIndex(fn, arg_index+1, arg->attribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
arg_index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset != 0 && ft->ret.kind == lbArg_Indirect && ft->ret.attribute != nullptr) {
|
||||||
|
LLVMAddAttributeAtIndex(fn, offset, ft->ret.attribute);
|
||||||
|
LLVMAddAttributeAtIndex(fn, offset, noalias_attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
lbCallingConventionKind cc_kind = lbCallingConvention_C;
|
||||||
|
// TODO(bill): Clean up this logic
|
||||||
|
if (build_context.metrics.os != TargetOs_js) {
|
||||||
|
cc_kind = lb_calling_convention_map[calling_convention];
|
||||||
|
}
|
||||||
|
LLVMSetFunctionCallConv(fn, cc_kind);
|
||||||
|
if (calling_convention == ProcCC_Odin) {
|
||||||
|
unsigned context_index = offset+arg_count;
|
||||||
|
LLVMAddAttributeAtIndex(fn, context_index, noalias_attr);
|
||||||
|
LLVMAddAttributeAtIndex(fn, context_index, nonnull_attr);
|
||||||
|
LLVMAddAttributeAtIndex(fn, context_index, nocapture_attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
i64 lb_sizeof(LLVMTypeRef type);
|
||||||
|
i64 lb_alignof(LLVMTypeRef type);
|
||||||
|
|
||||||
|
i64 lb_sizeof(LLVMTypeRef type) {
|
||||||
|
LLVMTypeKind kind = LLVMGetTypeKind(type);
|
||||||
|
switch (kind) {
|
||||||
|
case LLVMVoidTypeKind:
|
||||||
|
return 0;
|
||||||
|
case LLVMIntegerTypeKind:
|
||||||
|
{
|
||||||
|
unsigned w = LLVMGetIntTypeWidth(type);
|
||||||
|
return (w + 7)/8;
|
||||||
|
}
|
||||||
|
case LLVMFloatTypeKind:
|
||||||
|
return 4;
|
||||||
|
case LLVMDoubleTypeKind:
|
||||||
|
return 8;
|
||||||
|
case LLVMPointerTypeKind:
|
||||||
|
return build_context.word_size;
|
||||||
|
case LLVMStructTypeKind:
|
||||||
|
{
|
||||||
|
unsigned field_count = LLVMCountStructElementTypes(type);
|
||||||
|
i64 offset = 0;
|
||||||
|
if (LLVMIsPackedStruct(type)) {
|
||||||
|
for (unsigned i = 0; i < field_count; i++) {
|
||||||
|
LLVMTypeRef field = LLVMStructGetTypeAtIndex(type, i);
|
||||||
|
offset += lb_sizeof(field);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (unsigned i = 0; i < field_count; i++) {
|
||||||
|
LLVMTypeRef field = LLVMStructGetTypeAtIndex(type, i);
|
||||||
|
i64 align = lb_alignof(field);
|
||||||
|
offset = llvm_align_formula(offset, align);
|
||||||
|
offset += lb_sizeof(field);
|
||||||
|
}
|
||||||
|
offset = llvm_align_formula(offset, lb_alignof(type));
|
||||||
|
}
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LLVMArrayTypeKind:
|
||||||
|
{
|
||||||
|
LLVMTypeRef elem = LLVMGetElementType(type);
|
||||||
|
i64 elem_size = lb_sizeof(elem);
|
||||||
|
i64 count = LLVMGetArrayLength(type);
|
||||||
|
i64 size = count * elem_size;
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LLVMX86_MMXTypeKind:
|
||||||
|
return 8;
|
||||||
|
case LLVMVectorTypeKind:
|
||||||
|
{
|
||||||
|
LLVMTypeRef elem = LLVMGetElementType(type);
|
||||||
|
i64 elem_size = lb_sizeof(elem);
|
||||||
|
i64 count = LLVMGetVectorSize(type);
|
||||||
|
i64 size = count * elem_size;
|
||||||
|
return gb_clamp(next_pow2(size), 1, build_context.max_align);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
GB_PANIC("Unhandled type for lb_sizeof -> %s", LLVMPrintTypeToString(type));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
i64 lb_alignof(LLVMTypeRef type) {
|
||||||
|
LLVMTypeKind kind = LLVMGetTypeKind(type);
|
||||||
|
switch (kind) {
|
||||||
|
case LLVMVoidTypeKind:
|
||||||
|
return 1;
|
||||||
|
case LLVMIntegerTypeKind:
|
||||||
|
{
|
||||||
|
unsigned w = LLVMGetIntTypeWidth(type);
|
||||||
|
return gb_clamp((w + 7)/8, 1, build_context.max_align);
|
||||||
|
}
|
||||||
|
case LLVMFloatTypeKind:
|
||||||
|
return 4;
|
||||||
|
case LLVMDoubleTypeKind:
|
||||||
|
return 8;
|
||||||
|
case LLVMPointerTypeKind:
|
||||||
|
return build_context.word_size;
|
||||||
|
case LLVMStructTypeKind:
|
||||||
|
{
|
||||||
|
if (LLVMIsPackedStruct(type)) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
unsigned field_count = LLVMCountStructElementTypes(type);
|
||||||
|
i64 max_align = 1;
|
||||||
|
for (unsigned i = 0; i < field_count; i++) {
|
||||||
|
LLVMTypeRef field = LLVMStructGetTypeAtIndex(type, i);
|
||||||
|
i64 field_align = lb_alignof(field);
|
||||||
|
max_align = gb_max(max_align, field_align);
|
||||||
|
}
|
||||||
|
return max_align;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LLVMArrayTypeKind:
|
||||||
|
return lb_alignof(LLVMGetElementType(type));
|
||||||
|
|
||||||
|
case LLVMX86_MMXTypeKind:
|
||||||
|
return 8;
|
||||||
|
case LLVMVectorTypeKind:
|
||||||
|
{
|
||||||
|
LLVMTypeRef elem = LLVMGetElementType(type);
|
||||||
|
i64 elem_size = lb_sizeof(elem);
|
||||||
|
i64 count = LLVMGetVectorSize(type);
|
||||||
|
i64 size = count * elem_size;
|
||||||
|
return gb_clamp(next_pow2(size), 1, build_context.max_align);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
GB_PANIC("Unhandled type for lb_sizeof -> %s", LLVMPrintTypeToString(type));
|
||||||
|
|
||||||
|
// LLVMValueRef v = LLVMAlignOf(type);
|
||||||
|
// GB_ASSERT(LLVMIsConstant(v));
|
||||||
|
// return LLVMConstIntGetSExtValue(v);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
Type *lb_abi_to_odin_type(lbModule *m, LLVMTypeRef type, bool is_return, u32 level = 0) {
|
||||||
|
Type **found = map_get(&m->llvm_types, hash_pointer(type));
|
||||||
|
if (found) {
|
||||||
|
return *found;
|
||||||
|
}
|
||||||
|
GB_ASSERT_MSG(level < 64, "%s %d", LLVMPrintTypeToString(type), is_return);
|
||||||
|
|
||||||
|
LLVMTypeKind kind = LLVMGetTypeKind(type);
|
||||||
|
switch (kind) {
|
||||||
|
case LLVMVoidTypeKind:
|
||||||
|
return nullptr;
|
||||||
|
case LLVMIntegerTypeKind:
|
||||||
|
{
|
||||||
|
unsigned w = LLVMGetIntTypeWidth(type);
|
||||||
|
if (w == 1) {
|
||||||
|
return t_llvm_bool;
|
||||||
|
}
|
||||||
|
unsigned bytes = (w + 7)/8;
|
||||||
|
switch (bytes) {
|
||||||
|
case 1: return t_u8;
|
||||||
|
case 2: return t_u16;
|
||||||
|
case 4: return t_u32;
|
||||||
|
case 8: return t_u64;
|
||||||
|
case 16: return t_u128;
|
||||||
|
}
|
||||||
|
GB_PANIC("Unhandled integer type");
|
||||||
|
}
|
||||||
|
case LLVMFloatTypeKind:
|
||||||
|
return t_f32;
|
||||||
|
case LLVMDoubleTypeKind:
|
||||||
|
return t_f64;
|
||||||
|
case LLVMPointerTypeKind:
|
||||||
|
{
|
||||||
|
LLVMTypeRef elem = LLVMGetElementType(type);
|
||||||
|
if (lb_is_type_kind(elem, LLVMFunctionTypeKind)) {
|
||||||
|
unsigned param_count = LLVMCountParamTypes(elem);
|
||||||
|
LLVMTypeRef *params = gb_alloc_array(heap_allocator(), LLVMTypeRef, param_count);
|
||||||
|
defer (gb_free(heap_allocator(), params));
|
||||||
|
LLVMGetParamTypes(elem, params);
|
||||||
|
|
||||||
|
Type **param_types = gb_alloc_array(heap_allocator(), Type *, param_count);
|
||||||
|
defer (gb_free(heap_allocator(), param_types));
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < param_count; i++) {
|
||||||
|
param_types[i] = lb_abi_to_odin_type(m, params[i], false, level+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVMTypeRef ret = LLVMGetReturnType(elem);
|
||||||
|
Type *ret_type = lb_abi_to_odin_type(m, ret, true, level+1);
|
||||||
|
|
||||||
|
bool is_c_vararg = !!LLVMIsFunctionVarArg(elem);
|
||||||
|
return alloc_type_proc_from_types(param_types, param_count, ret_type, is_c_vararg);
|
||||||
|
}
|
||||||
|
return alloc_type_pointer(lb_abi_to_odin_type(m, elem, false, level+1));
|
||||||
|
}
|
||||||
|
case LLVMFunctionTypeKind:
|
||||||
|
GB_PANIC("LLVMFunctionTypeKind should not be seen on its own");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LLVMStructTypeKind:
|
||||||
|
{
|
||||||
|
unsigned field_count = LLVMCountStructElementTypes(type);
|
||||||
|
Type **fields = gb_alloc_array(heap_allocator(), Type *, field_count);
|
||||||
|
for (unsigned i = 0; i < field_count; i++) {
|
||||||
|
LLVMTypeRef field_type = LLVMStructGetTypeAtIndex(type, i);
|
||||||
|
if (lb_is_type_kind(field_type, LLVMPointerTypeKind) && level > 0) {
|
||||||
|
fields[i] = t_rawptr;
|
||||||
|
} else {
|
||||||
|
fields[i] = lb_abi_to_odin_type(m, field_type, false, level+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_return) {
|
||||||
|
return alloc_type_tuple_from_field_types(fields, field_count, !!LLVMIsPackedStruct(type), false);
|
||||||
|
} else {
|
||||||
|
return alloc_type_struct_from_field_types(fields, field_count, !!LLVMIsPackedStruct(type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LLVMArrayTypeKind:
|
||||||
|
{
|
||||||
|
|
||||||
|
i64 count = LLVMGetArrayLength(type);
|
||||||
|
Type *elem = lb_abi_to_odin_type(m, LLVMGetElementType(type), false, level+1);
|
||||||
|
return alloc_type_array(elem, count);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LLVMX86_MMXTypeKind:
|
||||||
|
return t_vector_x86_mmx;
|
||||||
|
case LLVMVectorTypeKind:
|
||||||
|
{
|
||||||
|
i64 count = LLVMGetVectorSize(type);
|
||||||
|
Type *elem = lb_abi_to_odin_type(m, LLVMGetElementType(type), false, level+1);
|
||||||
|
return alloc_type_simd_vector(count, elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
GB_PANIC("Unhandled type for lb_abi_to_odin_type -> %s", LLVMPrintTypeToString(type));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#define LB_ABI_INFO(name) lbFunctionType *name(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, LLVMTypeRef return_type, bool return_is_defined, ProcCallingConvention calling_convention)
|
||||||
|
typedef LB_ABI_INFO(lbAbiInfoType);
|
||||||
|
|
||||||
|
|
||||||
|
// NOTE(bill): I hate `namespace` in C++ but this is just because I don't want to prefix everything
|
||||||
|
namespace lbAbi386 {
|
||||||
|
Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
|
||||||
|
lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined);
|
||||||
|
|
||||||
|
LB_ABI_INFO(abi_info) {
|
||||||
|
lbFunctionType *ft = gb_alloc_item(heap_allocator(), lbFunctionType);
|
||||||
|
ft->ctx = c;
|
||||||
|
ft->args = compute_arg_types(c, arg_types, arg_count);
|
||||||
|
ft->ret = compute_return_type(c, return_type, return_is_defined);
|
||||||
|
ft->calling_convention = calling_convention;
|
||||||
|
return ft;
|
||||||
|
}
|
||||||
|
|
||||||
|
lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type, bool is_return) {
|
||||||
|
if (!is_return && lb_sizeof(type) > 8) {
|
||||||
|
return lb_arg_type_indirect(type, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (build_context.metrics.os == TargetOs_windows &&
|
||||||
|
build_context.word_size == 8 &&
|
||||||
|
lb_is_type_kind(type, LLVMIntegerTypeKind) &&
|
||||||
|
type == LLVMIntTypeInContext(c, 128)) {
|
||||||
|
// NOTE(bill): Because Windows AMD64 is weird
|
||||||
|
LLVMTypeRef cast_type = LLVMVectorType(LLVMInt64TypeInContext(c), 2);
|
||||||
|
return lb_arg_type_direct(type, cast_type, nullptr, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVMAttributeRef attr = nullptr;
|
||||||
|
LLVMTypeRef i1 = LLVMInt1TypeInContext(c);
|
||||||
|
if (type == i1) {
|
||||||
|
attr = lb_create_enum_attribute(c, "zeroext", true);
|
||||||
|
}
|
||||||
|
return lb_arg_type_direct(type, nullptr, nullptr, attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
|
||||||
|
auto args = array_make<lbArgType>(heap_allocator(), arg_count);
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < arg_count; i++) {
|
||||||
|
LLVMTypeRef t = arg_types[i];
|
||||||
|
LLVMTypeKind kind = LLVMGetTypeKind(t);
|
||||||
|
i64 sz = lb_sizeof(t);
|
||||||
|
if (kind == LLVMStructTypeKind) {
|
||||||
|
if (sz == 0) {
|
||||||
|
args[i] = lb_arg_type_ignore(t);
|
||||||
|
} else {
|
||||||
|
args[i] = lb_arg_type_indirect(t, lb_create_enum_attribute(c, "byval", true));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
args[i] = non_struct(c, t, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined) {
|
||||||
|
if (!return_is_defined) {
|
||||||
|
return lb_arg_type_direct(LLVMVoidTypeInContext(c));
|
||||||
|
} else if (lb_is_type_kind(return_type, LLVMStructTypeKind) || lb_is_type_kind(return_type, LLVMArrayTypeKind)) {
|
||||||
|
i64 sz = lb_sizeof(return_type);
|
||||||
|
switch (sz) {
|
||||||
|
case 1: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 8), nullptr, nullptr);
|
||||||
|
case 2: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 16), nullptr, nullptr);
|
||||||
|
case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr);
|
||||||
|
case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr);
|
||||||
|
}
|
||||||
|
return lb_arg_type_indirect(return_type, lb_create_enum_attribute(c, "sret", true));
|
||||||
|
}
|
||||||
|
return non_struct(c, return_type, true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace lbAbiAmd64Win64 {
|
||||||
|
Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
|
||||||
|
|
||||||
|
|
||||||
|
LB_ABI_INFO(abi_info) {
|
||||||
|
lbFunctionType *ft = gb_alloc_item(heap_allocator(), lbFunctionType);
|
||||||
|
ft->ctx = c;
|
||||||
|
ft->args = compute_arg_types(c, arg_types, arg_count);
|
||||||
|
ft->ret = lbAbi386::compute_return_type(c, return_type, return_is_defined);
|
||||||
|
ft->calling_convention = calling_convention;
|
||||||
|
return ft;
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
|
||||||
|
auto args = array_make<lbArgType>(heap_allocator(), arg_count);
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < arg_count; i++) {
|
||||||
|
LLVMTypeRef t = arg_types[i];
|
||||||
|
LLVMTypeKind kind = LLVMGetTypeKind(t);
|
||||||
|
if (kind == LLVMStructTypeKind) {
|
||||||
|
i64 sz = lb_sizeof(t);
|
||||||
|
switch (sz) {
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
case 4:
|
||||||
|
case 8:
|
||||||
|
args[i] = lb_arg_type_direct(t, LLVMIntTypeInContext(c, 8*cast(unsigned)sz), nullptr, nullptr);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
args[i] = lb_arg_type_indirect(t, nullptr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
args[i] = lbAbi386::non_struct(c, t, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// NOTE(bill): I hate `namespace` in C++ but this is just because I don't want to prefix everything
|
||||||
|
namespace lbAbiAmd64SysV {
|
||||||
|
enum RegClass {
|
||||||
|
RegClass_NoClass,
|
||||||
|
RegClass_Int,
|
||||||
|
RegClass_SSEFs,
|
||||||
|
RegClass_SSEFv,
|
||||||
|
RegClass_SSEDs,
|
||||||
|
RegClass_SSEDv,
|
||||||
|
RegClass_SSEInt8,
|
||||||
|
RegClass_SSEInt16,
|
||||||
|
RegClass_SSEInt32,
|
||||||
|
RegClass_SSEInt64,
|
||||||
|
RegClass_SSEUp,
|
||||||
|
RegClass_X87,
|
||||||
|
RegClass_X87Up,
|
||||||
|
RegClass_ComplexX87,
|
||||||
|
RegClass_Memory,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool is_sse(RegClass reg_class) {
|
||||||
|
switch (reg_class) {
|
||||||
|
case RegClass_SSEFs:
|
||||||
|
case RegClass_SSEFv:
|
||||||
|
case RegClass_SSEDv:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void all_mem(Array<RegClass> *cs) {
|
||||||
|
for_array(i, *cs) {
|
||||||
|
(*cs)[i] = RegClass_Memory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Amd64TypeAttributeKind {
|
||||||
|
Amd64TypeAttribute_None,
|
||||||
|
Amd64TypeAttribute_ByVal,
|
||||||
|
Amd64TypeAttribute_StructRect,
|
||||||
|
};
|
||||||
|
|
||||||
|
Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
|
||||||
|
lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined);
|
||||||
|
void classify_with(LLVMTypeRef t, Array<RegClass> *cls, i64 ix, i64 off);
|
||||||
|
void fixup(LLVMTypeRef t, Array<RegClass> *cls);
|
||||||
|
lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind);
|
||||||
|
Array<RegClass> classify(LLVMTypeRef t);
|
||||||
|
LLVMTypeRef llreg(LLVMContextRef c, Array<RegClass> const ®_classes);
|
||||||
|
|
||||||
|
LB_ABI_INFO(abi_info) {
|
||||||
|
lbFunctionType *ft = gb_alloc_item(heap_allocator(), lbFunctionType);
|
||||||
|
ft->ctx = c;
|
||||||
|
ft->calling_convention = calling_convention;
|
||||||
|
|
||||||
|
ft->args = array_make<lbArgType>(heap_allocator(), arg_count);
|
||||||
|
for (unsigned i = 0; i < arg_count; i++) {
|
||||||
|
ft->args[i] = amd64_type(c, arg_types[i], Amd64TypeAttribute_ByVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (return_is_defined) {
|
||||||
|
ft->ret = amd64_type(c, return_type, Amd64TypeAttribute_StructRect);
|
||||||
|
} else {
|
||||||
|
ft->ret = lb_arg_type_direct(LLVMVoidTypeInContext(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ft;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_mem_cls(Array<RegClass> const &cls, Amd64TypeAttributeKind attribute_kind) {
|
||||||
|
if (attribute_kind == Amd64TypeAttribute_ByVal) {
|
||||||
|
if (cls.count == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto first = cls[0];
|
||||||
|
return first == RegClass_Memory || first == RegClass_X87 || first == RegClass_ComplexX87;
|
||||||
|
} else if (attribute_kind == Amd64TypeAttribute_StructRect) {
|
||||||
|
if (cls.count == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return cls[0] == RegClass_Memory;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_register(LLVMTypeRef type) {
|
||||||
|
LLVMTypeKind kind = LLVMGetTypeKind(type);
|
||||||
|
switch (kind) {
|
||||||
|
case LLVMIntegerTypeKind:
|
||||||
|
case LLVMFloatTypeKind:
|
||||||
|
case LLVMDoubleTypeKind:
|
||||||
|
case LLVMPointerTypeKind:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind) {
|
||||||
|
if (is_register(type)) {
|
||||||
|
LLVMAttributeRef attribute = nullptr;
|
||||||
|
if (type == LLVMInt1TypeInContext(c)) {
|
||||||
|
attribute = lb_create_enum_attribute(c, "zeroext", true);
|
||||||
|
}
|
||||||
|
return lb_arg_type_direct(type, nullptr, nullptr, attribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cls = classify(type);
|
||||||
|
if (is_mem_cls(cls, attribute_kind)) {
|
||||||
|
LLVMAttributeRef attribute = nullptr;
|
||||||
|
if (attribute_kind == Amd64TypeAttribute_ByVal) {
|
||||||
|
attribute = lb_create_enum_attribute(c, "byval", true);
|
||||||
|
} else if (attribute_kind == Amd64TypeAttribute_StructRect) {
|
||||||
|
attribute = lb_create_enum_attribute(c, "sret", true);
|
||||||
|
}
|
||||||
|
return lb_arg_type_indirect(type, attribute);
|
||||||
|
} else {
|
||||||
|
return lb_arg_type_direct(type, llreg(c, cls), nullptr, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type) {
|
||||||
|
LLVMAttributeRef attr = nullptr;
|
||||||
|
LLVMTypeRef i1 = LLVMInt1TypeInContext(c);
|
||||||
|
if (type == i1) {
|
||||||
|
attr = lb_create_enum_attribute(c, "zeroext", true);
|
||||||
|
}
|
||||||
|
return lb_arg_type_direct(type, nullptr, nullptr, attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<RegClass> classify(LLVMTypeRef t) {
|
||||||
|
i64 sz = lb_sizeof(t);
|
||||||
|
i64 words = (sz + 7)/8;
|
||||||
|
auto reg_classes = array_make<RegClass>(heap_allocator(), cast(isize)words);
|
||||||
|
if (words > 4) {
|
||||||
|
all_mem(®_classes);
|
||||||
|
} else {
|
||||||
|
classify_with(t, ®_classes, 0, 0);
|
||||||
|
fixup(t, ®_classes);
|
||||||
|
}
|
||||||
|
return reg_classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void unify(Array<RegClass> *cls, i64 i, RegClass newv) {
|
||||||
|
RegClass &oldv = (*cls)[i];
|
||||||
|
if (oldv == newv) {
|
||||||
|
return;
|
||||||
|
} else if (oldv == RegClass_NoClass) {
|
||||||
|
oldv = newv;
|
||||||
|
} else if (newv == RegClass_NoClass) {
|
||||||
|
return;
|
||||||
|
} else if (oldv == RegClass_Memory || newv == RegClass_Memory) {
|
||||||
|
return;
|
||||||
|
} else if (oldv == RegClass_Int || newv == RegClass_Int) {
|
||||||
|
return;
|
||||||
|
} else if (oldv == RegClass_X87 || oldv == RegClass_X87Up || oldv == RegClass_ComplexX87 ||
|
||||||
|
newv == RegClass_X87 || newv == RegClass_X87Up || newv == RegClass_ComplexX87) {
|
||||||
|
oldv = RegClass_Memory;
|
||||||
|
} else {
|
||||||
|
oldv = newv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fixup(LLVMTypeRef t, Array<RegClass> *cls) {
|
||||||
|
i64 i = 0;
|
||||||
|
i64 e = cls->count;
|
||||||
|
if (e > 2 && (lb_is_type_kind(t, LLVMStructTypeKind) || lb_is_type_kind(t, LLVMArrayTypeKind))) {
|
||||||
|
RegClass &oldv = (*cls)[i];
|
||||||
|
if (is_sse(oldv)) {
|
||||||
|
for (i++; i < e; i++) {
|
||||||
|
if (oldv != RegClass_SSEUp) {
|
||||||
|
all_mem(cls);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
all_mem(cls);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while (i < e) {
|
||||||
|
RegClass &oldv = (*cls)[i];
|
||||||
|
if (oldv == RegClass_Memory) {
|
||||||
|
all_mem(cls);
|
||||||
|
return;
|
||||||
|
} else if (oldv == RegClass_X87Up) {
|
||||||
|
// NOTE(bill): Darwin
|
||||||
|
all_mem(cls);
|
||||||
|
return;
|
||||||
|
} else if (oldv == RegClass_SSEUp) {
|
||||||
|
oldv = RegClass_SSEDv;
|
||||||
|
} else if (is_sse(oldv)) {
|
||||||
|
i++;
|
||||||
|
while (i != e && oldv == RegClass_SSEUp) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
} else if (oldv == RegClass_X87) {
|
||||||
|
i++;
|
||||||
|
while (i != e && oldv == RegClass_X87Up) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned llvec_len(Array<RegClass> const ®_classes, isize offset) {
|
||||||
|
unsigned len = 1;
|
||||||
|
for (isize i = offset+1; i < reg_classes.count; i++) {
|
||||||
|
if (reg_classes[offset] != RegClass_SSEFv && reg_classes[i] != RegClass_SSEUp) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LLVMTypeRef llreg(LLVMContextRef c, Array<RegClass> const ®_classes) {
|
||||||
|
auto types = array_make<LLVMTypeRef>(heap_allocator(), 0, reg_classes.count);
|
||||||
|
for_array(i, reg_classes) {
|
||||||
|
RegClass reg_class = reg_classes[i];
|
||||||
|
switch (reg_class) {
|
||||||
|
case RegClass_Int:
|
||||||
|
array_add(&types, LLVMIntTypeInContext(c, 64));
|
||||||
|
break;
|
||||||
|
case RegClass_SSEFv:
|
||||||
|
case RegClass_SSEDv:
|
||||||
|
case RegClass_SSEInt8:
|
||||||
|
case RegClass_SSEInt16:
|
||||||
|
case RegClass_SSEInt32:
|
||||||
|
case RegClass_SSEInt64:
|
||||||
|
{
|
||||||
|
unsigned elems_per_word = 0;
|
||||||
|
LLVMTypeRef elem_type = nullptr;
|
||||||
|
switch (reg_class) {
|
||||||
|
case RegClass_SSEFv:
|
||||||
|
elems_per_word = 2;
|
||||||
|
elem_type = LLVMFloatTypeInContext(c);
|
||||||
|
break;
|
||||||
|
case RegClass_SSEDv:
|
||||||
|
elems_per_word = 1;
|
||||||
|
elem_type = LLVMDoubleTypeInContext(c);
|
||||||
|
break;
|
||||||
|
case RegClass_SSEInt8:
|
||||||
|
elems_per_word = 64/8;
|
||||||
|
elem_type = LLVMIntTypeInContext(c, 8);
|
||||||
|
break;
|
||||||
|
case RegClass_SSEInt16:
|
||||||
|
elems_per_word = 64/16;
|
||||||
|
elem_type = LLVMIntTypeInContext(c, 16);
|
||||||
|
break;
|
||||||
|
case RegClass_SSEInt32:
|
||||||
|
elems_per_word = 64/32;
|
||||||
|
elem_type = LLVMIntTypeInContext(c, 32);
|
||||||
|
break;
|
||||||
|
case RegClass_SSEInt64:
|
||||||
|
elems_per_word = 64/64;
|
||||||
|
elem_type = LLVMIntTypeInContext(c, 64);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned vec_len = llvec_len(reg_classes, i);
|
||||||
|
LLVMTypeRef vec_type = LLVMVectorType(elem_type, vec_len * elems_per_word);
|
||||||
|
array_add(&types, vec_type);
|
||||||
|
i += vec_len;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case RegClass_SSEFs:
|
||||||
|
array_add(&types, LLVMFloatTypeInContext(c));
|
||||||
|
break;
|
||||||
|
case RegClass_SSEDs:
|
||||||
|
array_add(&types, LLVMDoubleTypeInContext(c));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
GB_PANIC("Unhandled RegClass");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GB_ASSERT(types.count != 0);
|
||||||
|
if (types.count == 1) {
|
||||||
|
return types[0];
|
||||||
|
}
|
||||||
|
return LLVMStructTypeInContext(c, types.data, cast(unsigned)types.count, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void classify_with(LLVMTypeRef t, Array<RegClass> *cls, i64 ix, i64 off) {
|
||||||
|
i64 t_align = lb_alignof(t);
|
||||||
|
i64 t_size = lb_sizeof(t);
|
||||||
|
|
||||||
|
i64 misalign = off % t_align;
|
||||||
|
if (misalign != 0) {
|
||||||
|
i64 e = (off + t_size + 7) / 8;
|
||||||
|
for (i64 i = off / 8; i < e; i++) {
|
||||||
|
unify(cls, ix+i, RegClass_Memory);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (LLVMGetTypeKind(t)) {
|
||||||
|
case LLVMIntegerTypeKind:
|
||||||
|
case LLVMPointerTypeKind:
|
||||||
|
unify(cls, ix + off/8, RegClass_Int);
|
||||||
|
break;
|
||||||
|
case LLVMFloatTypeKind:
|
||||||
|
unify(cls, ix + off/8, (off%8 == 4) ? RegClass_SSEFv : RegClass_SSEFs);
|
||||||
|
break;
|
||||||
|
case LLVMDoubleTypeKind:
|
||||||
|
unify(cls, ix + off/8, RegClass_SSEDs);
|
||||||
|
break;
|
||||||
|
case LLVMStructTypeKind:
|
||||||
|
{
|
||||||
|
LLVMBool packed = LLVMIsPackedStruct(t);
|
||||||
|
unsigned field_count = LLVMCountStructElementTypes(t);
|
||||||
|
|
||||||
|
i64 field_off = off;
|
||||||
|
for (unsigned field_index = 0; field_index < field_count; field_index++) {
|
||||||
|
LLVMTypeRef field_type = LLVMStructGetTypeAtIndex(t, field_index);
|
||||||
|
if (!packed) {
|
||||||
|
field_off = llvm_align_formula(field_off, lb_alignof(field_type));
|
||||||
|
}
|
||||||
|
classify_with(field_type, cls, ix, field_off);
|
||||||
|
field_off += lb_sizeof(field_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LLVMArrayTypeKind:
|
||||||
|
{
|
||||||
|
i64 len = LLVMGetArrayLength(t);
|
||||||
|
LLVMTypeRef elem = LLVMGetElementType(t);
|
||||||
|
i64 elem_sz = lb_sizeof(elem);
|
||||||
|
for (i64 i = 0; i < len; i++) {
|
||||||
|
classify_with(elem, cls, ix, off + i*elem_sz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LLVMVectorTypeKind:
|
||||||
|
{
|
||||||
|
i64 len = LLVMGetVectorSize(t);
|
||||||
|
LLVMTypeRef elem = LLVMGetElementType(t);
|
||||||
|
i64 elem_sz = lb_sizeof(elem);
|
||||||
|
LLVMTypeKind elem_kind = LLVMGetTypeKind(elem);
|
||||||
|
RegClass reg = RegClass_NoClass;
|
||||||
|
switch (elem_kind) {
|
||||||
|
case LLVMIntegerTypeKind:
|
||||||
|
switch (LLVMGetIntTypeWidth(elem)) {
|
||||||
|
case 8: reg = RegClass_SSEInt8;
|
||||||
|
case 16: reg = RegClass_SSEInt16;
|
||||||
|
case 32: reg = RegClass_SSEInt32;
|
||||||
|
case 64: reg = RegClass_SSEInt64;
|
||||||
|
default:
|
||||||
|
GB_PANIC("Unhandled integer width for vector type");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LLVMFloatTypeKind:
|
||||||
|
reg = RegClass_SSEFv;
|
||||||
|
break;
|
||||||
|
case LLVMDoubleTypeKind:
|
||||||
|
reg = RegClass_SSEDv;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
GB_PANIC("Unhandled vector element type");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i64 i = 0; i < len; i++) {
|
||||||
|
unify(cls, ix + (off + i*elem_sz)/8, reg);
|
||||||
|
// NOTE(bill): Everything after the first one is the upper
|
||||||
|
// half of a register
|
||||||
|
reg = RegClass_SSEUp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
GB_PANIC("Unhandled type");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
|
||||||
|
auto args = array_make<lbArgType>(heap_allocator(), arg_count);
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < arg_count; i++) {
|
||||||
|
LLVMTypeRef t = arg_types[i];
|
||||||
|
LLVMTypeKind kind = LLVMGetTypeKind(t);
|
||||||
|
if (kind == LLVMStructTypeKind) {
|
||||||
|
i64 sz = lb_sizeof(t);
|
||||||
|
if (sz == 0) {
|
||||||
|
args[i] = lb_arg_type_ignore(t);
|
||||||
|
} else {
|
||||||
|
args[i] = lb_arg_type_indirect(t, lb_create_enum_attribute(c, "byval", true));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
args[i] = non_struct(c, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined) {
|
||||||
|
if (!return_is_defined) {
|
||||||
|
return lb_arg_type_direct(LLVMVoidTypeInContext(c));
|
||||||
|
} else if (lb_is_type_kind(return_type, LLVMStructTypeKind)) {
|
||||||
|
i64 sz = lb_sizeof(return_type);
|
||||||
|
switch (sz) {
|
||||||
|
case 1: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 8), nullptr, nullptr);
|
||||||
|
case 2: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 16), nullptr, nullptr);
|
||||||
|
case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr);
|
||||||
|
case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr);
|
||||||
|
}
|
||||||
|
return lb_arg_type_indirect(return_type, lb_create_enum_attribute(c, "sret", true));
|
||||||
|
} else if (build_context.metrics.os == TargetOs_windows && lb_is_type_kind(return_type, LLVMIntegerTypeKind) && lb_sizeof(return_type) == 16) {
|
||||||
|
return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 128), nullptr, nullptr);
|
||||||
|
}
|
||||||
|
return non_struct(c, return_type);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
namespace lbAbiAarch64 {
|
||||||
|
LB_ABI_INFO(abi_info) {
|
||||||
|
lbFunctionType *ft = gb_alloc_item(heap_allocator(), lbFunctionType);
|
||||||
|
ft->ctx = c;
|
||||||
|
// ft->args = compute_arg_types(c, arg_types, arg_count);
|
||||||
|
// ft->ret = lbAbi386::compute_return_type(c, return_type, return_is_defined);
|
||||||
|
// ft->calling_convention = calling_convention;
|
||||||
|
return ft;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LB_ABI_INFO(lb_get_abi_info) {
|
||||||
|
switch (calling_convention) {
|
||||||
|
case ProcCC_None:
|
||||||
|
case ProcCC_PureNone:
|
||||||
|
case ProcCC_InlineAsm:
|
||||||
|
{
|
||||||
|
lbFunctionType *ft = gb_alloc_item(heap_allocator(), lbFunctionType);
|
||||||
|
ft->ctx = c;
|
||||||
|
ft->args = array_make<lbArgType>(heap_allocator(), arg_count);
|
||||||
|
for (unsigned i = 0; i < arg_count; i++) {
|
||||||
|
ft->args[i] = lb_arg_type_direct(arg_types[i]);
|
||||||
|
}
|
||||||
|
if (return_is_defined) {
|
||||||
|
ft->ret = lb_arg_type_direct(return_type);
|
||||||
|
} else {
|
||||||
|
ft->ret = lb_arg_type_direct(LLVMVoidTypeInContext(c));
|
||||||
|
}
|
||||||
|
ft->calling_convention = calling_convention;
|
||||||
|
return ft;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (build_context.metrics.arch == TargetArch_amd64) {
|
||||||
|
if (build_context.metrics.os == TargetOs_windows) {
|
||||||
|
return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
|
||||||
|
} else {
|
||||||
|
return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
|
||||||
|
}
|
||||||
|
} else if (build_context.metrics.arch == TargetArch_386) {
|
||||||
|
return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
|
||||||
|
} else if (build_context.metrics.arch == TargetArch_wasm32) {
|
||||||
|
return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
|
||||||
|
}
|
||||||
|
GB_PANIC("Unsupported ABI");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
+1453
-622
File diff suppressed because it is too large
Load Diff
+31
-2
@@ -1,3 +1,5 @@
|
|||||||
|
#if defined(LLVM_BACKEND_SUPPORT)
|
||||||
|
#if defined(GB_SYSTEM_WINDOWS)
|
||||||
#include "llvm-c/Core.h"
|
#include "llvm-c/Core.h"
|
||||||
#include "llvm-c/ExecutionEngine.h"
|
#include "llvm-c/ExecutionEngine.h"
|
||||||
#include "llvm-c/Target.h"
|
#include "llvm-c/Target.h"
|
||||||
@@ -12,6 +14,23 @@
|
|||||||
#include "llvm-c/Transforms/Scalar.h"
|
#include "llvm-c/Transforms/Scalar.h"
|
||||||
#include "llvm-c/Transforms/Utils.h"
|
#include "llvm-c/Transforms/Utils.h"
|
||||||
#include "llvm-c/Transforms/Vectorize.h"
|
#include "llvm-c/Transforms/Vectorize.h"
|
||||||
|
#else
|
||||||
|
#include <llvm-c/Core.h>
|
||||||
|
#include <llvm-c/ExecutionEngine.h>
|
||||||
|
#include <llvm-c/Target.h>
|
||||||
|
#include <llvm-c/Analysis.h>
|
||||||
|
#include <llvm-c/Object.h>
|
||||||
|
#include <llvm-c/BitWriter.h>
|
||||||
|
#include <llvm-c/DebugInfo.h>
|
||||||
|
#include <llvm-c/Transforms/AggressiveInstCombine.h>
|
||||||
|
#include <llvm-c/Transforms/InstCombine.h>
|
||||||
|
#include <llvm-c/Transforms/IPO.h>
|
||||||
|
#include <llvm-c/Transforms/PassManagerBuilder.h>
|
||||||
|
#include <llvm-c/Transforms/Scalar.h>
|
||||||
|
#include <llvm-c/Transforms/Utils.h>
|
||||||
|
#include <llvm-c/Transforms/Vectorize.h>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
struct lbProcedure;
|
struct lbProcedure;
|
||||||
|
|
||||||
@@ -74,6 +93,8 @@ struct lbModule {
|
|||||||
gbMutex mutex;
|
gbMutex mutex;
|
||||||
|
|
||||||
Map<LLVMTypeRef> types; // Key: Type *
|
Map<LLVMTypeRef> types; // Key: Type *
|
||||||
|
Map<Type *> llvm_types; // Key: LLVMTypeRef
|
||||||
|
i32 internal_type_level;
|
||||||
|
|
||||||
Map<lbValue> values; // Key: Entity *
|
Map<lbValue> values; // Key: Entity *
|
||||||
StringMap<lbValue> members;
|
StringMap<lbValue> members;
|
||||||
@@ -83,6 +104,10 @@ struct lbModule {
|
|||||||
StringMap<LLVMValueRef> const_strings;
|
StringMap<LLVMValueRef> const_strings;
|
||||||
|
|
||||||
Map<lbProcedure *> anonymous_proc_lits; // Key: Ast *
|
Map<lbProcedure *> anonymous_proc_lits; // Key: Ast *
|
||||||
|
Map<struct lbFunctionType *> function_type_map; // Key: Type *
|
||||||
|
|
||||||
|
Map<lbProcedure *> equal_procs; // Key: Type *
|
||||||
|
Map<lbProcedure *> hasher_procs; // Key: Type *
|
||||||
|
|
||||||
u32 global_array_index;
|
u32 global_array_index;
|
||||||
u32 global_generated_index;
|
u32 global_generated_index;
|
||||||
@@ -199,6 +224,7 @@ struct lbProcedure {
|
|||||||
bool is_entry_point;
|
bool is_entry_point;
|
||||||
bool is_startup;
|
bool is_startup;
|
||||||
|
|
||||||
|
lbFunctionType *abi_function_type;
|
||||||
|
|
||||||
LLVMValueRef value;
|
LLVMValueRef value;
|
||||||
LLVMBuilderRef builder;
|
LLVMBuilderRef builder;
|
||||||
@@ -301,7 +327,7 @@ lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e=nullptr, bool zero_ini
|
|||||||
|
|
||||||
void lb_add_foreign_library_path(lbModule *m, Entity *e);
|
void lb_add_foreign_library_path(lbModule *m, Entity *e);
|
||||||
|
|
||||||
lbValue lb_typeid(lbModule *m, Type *type, Type *typeid_type=t_typeid);
|
lbValue lb_typeid(lbModule *m, Type *type);
|
||||||
|
|
||||||
lbValue lb_address_from_load_or_generate_local(lbProcedure *p, lbValue value);
|
lbValue lb_address_from_load_or_generate_local(lbProcedure *p, lbValue value);
|
||||||
lbValue lb_address_from_load(lbProcedure *p, lbValue value);
|
lbValue lb_address_from_load(lbProcedure *p, lbValue value);
|
||||||
@@ -344,7 +370,7 @@ String lb_get_const_string(lbModule *m, lbValue value);
|
|||||||
lbValue lb_generate_local_array(lbProcedure *p, Type *elem_type, i64 count, bool zero_init=true);
|
lbValue lb_generate_local_array(lbProcedure *p, Type *elem_type, i64 count, bool zero_init=true);
|
||||||
lbValue lb_generate_global_array(lbModule *m, Type *elem_type, i64 count, String prefix, i64 id);
|
lbValue lb_generate_global_array(lbModule *m, Type *elem_type, i64 count, String prefix, i64 id);
|
||||||
lbValue lb_gen_map_header(lbProcedure *p, lbValue map_val_ptr, Type *map_type);
|
lbValue lb_gen_map_header(lbProcedure *p, lbValue map_val_ptr, Type *map_type);
|
||||||
lbValue lb_gen_map_key(lbProcedure *p, lbValue key, Type *key_type);
|
lbValue lb_gen_map_hash(lbProcedure *p, lbValue key, Type *key_type);
|
||||||
void lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbAddr addr, Type *map_type, lbValue map_key, lbValue map_value, Ast *node);
|
void lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbAddr addr, Type *map_type, lbValue map_key, lbValue map_value, Ast *node);
|
||||||
|
|
||||||
|
|
||||||
@@ -354,6 +380,9 @@ lbValue lb_emit_source_code_location(lbProcedure *p, String const &procedure, To
|
|||||||
|
|
||||||
lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const ¶m_value, TokenPos const &pos);
|
lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const ¶m_value, TokenPos const &pos);
|
||||||
|
|
||||||
|
lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type);
|
||||||
|
lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type);
|
||||||
|
lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t);
|
||||||
|
|
||||||
#define LB_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime"
|
#define LB_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime"
|
||||||
#define LB_STARTUP_TYPE_INFO_PROC_NAME "__$startup_type_info"
|
#define LB_STARTUP_TYPE_INFO_PROC_NAME "__$startup_type_info"
|
||||||
|
|||||||
+376
-181
File diff suppressed because it is too large
Load Diff
+184
-100
@@ -108,8 +108,25 @@ Token ast_token(Ast *node) {
|
|||||||
return empty_token;
|
return empty_token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
isize ast_node_size(AstKind kind) {
|
||||||
|
return align_formula_isize(gb_size_of(AstCommonStuff) + ast_variant_sizes[kind], gb_align_of(void *));
|
||||||
|
|
||||||
|
}
|
||||||
|
// 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) {
|
||||||
|
gbAllocator a = ast_allocator(f);
|
||||||
|
|
||||||
|
isize size = ast_node_size(kind);
|
||||||
|
|
||||||
|
Ast *node = cast(Ast *)gb_alloc(a, size);
|
||||||
|
node->kind = kind;
|
||||||
|
node->file = f;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
Ast *clone_ast(Ast *node);
|
Ast *clone_ast(Ast *node);
|
||||||
Array<Ast *> clone_ast_array(Array<Ast *> array) {
|
Array<Ast *> clone_ast_array(Array<Ast *> const &array) {
|
||||||
Array<Ast *> result = {};
|
Array<Ast *> result = {};
|
||||||
if (array.count > 0) {
|
if (array.count > 0) {
|
||||||
result = array_make<Ast *>(ast_allocator(nullptr), array.count);
|
result = array_make<Ast *>(ast_allocator(nullptr), array.count);
|
||||||
@@ -119,13 +136,23 @@ Array<Ast *> clone_ast_array(Array<Ast *> array) {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
Slice<Ast *> clone_ast_array(Slice<Ast *> const &array) {
|
||||||
|
Slice<Ast *> result = {};
|
||||||
|
if (array.count > 0) {
|
||||||
|
result = slice_clone(permanent_allocator(), array);
|
||||||
|
for_array(i, array) {
|
||||||
|
result[i] = clone_ast(array[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
Ast *clone_ast(Ast *node) {
|
Ast *clone_ast(Ast *node) {
|
||||||
if (node == nullptr) {
|
if (node == nullptr) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
Ast *n = alloc_ast_node(node->file, node->kind);
|
Ast *n = alloc_ast_node(node->file, node->kind);
|
||||||
gb_memmove(n, node, gb_size_of(Ast));
|
gb_memmove(n, node, ast_node_size(node->kind));
|
||||||
|
|
||||||
switch (n->kind) {
|
switch (n->kind) {
|
||||||
default: GB_PANIC("Unhandled Ast %.*s", LIT(ast_strings[n->kind])); break;
|
default: GB_PANIC("Unhandled Ast %.*s", LIT(ast_strings[n->kind])); break;
|
||||||
@@ -463,23 +490,6 @@ bool ast_node_expect(Ast *node, AstKind kind) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
gb_global gbAtomic64 total_allocated_node_memory = {0};
|
|
||||||
gb_global gbAtomic64 total_subtype_node_memory_test = {0};
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
gbAllocator a = ast_allocator(f);
|
|
||||||
|
|
||||||
gb_atomic64_fetch_add(&total_allocated_node_memory, cast(i64)(gb_size_of(Ast)));
|
|
||||||
gb_atomic64_fetch_add(&total_subtype_node_memory_test, cast(i64)(gb_size_of(AstCommonStuff) + ast_variant_sizes[kind]));
|
|
||||||
|
|
||||||
Ast *node = gb_alloc_item(a, Ast);
|
|
||||||
node->kind = kind;
|
|
||||||
node->file = f;
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ast *ast_bad_expr(AstFile *f, Token begin, Token end) {
|
Ast *ast_bad_expr(AstFile *f, Token begin, Token end) {
|
||||||
Ast *result = alloc_ast_node(f, Ast_BadExpr);
|
Ast *result = alloc_ast_node(f, Ast_BadExpr);
|
||||||
result->BadExpr.begin = begin;
|
result->BadExpr.begin = begin;
|
||||||
@@ -537,10 +547,10 @@ Ast *ast_paren_expr(AstFile *f, Ast *expr, Token open, Token close) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ast *ast_call_expr(AstFile *f, Ast *proc, Array<Ast *> args, Token open, Token close, Token ellipsis) {
|
Ast *ast_call_expr(AstFile *f, Ast *proc, Array<Ast *> const &args, Token open, Token close, Token ellipsis) {
|
||||||
Ast *result = alloc_ast_node(f, Ast_CallExpr);
|
Ast *result = alloc_ast_node(f, Ast_CallExpr);
|
||||||
result->CallExpr.proc = proc;
|
result->CallExpr.proc = proc;
|
||||||
result->CallExpr.args = args;
|
result->CallExpr.args = slice_from_array(args);
|
||||||
result->CallExpr.open = open;
|
result->CallExpr.open = open;
|
||||||
result->CallExpr.close = close;
|
result->CallExpr.close = close;
|
||||||
result->CallExpr.ellipsis = ellipsis;
|
result->CallExpr.ellipsis = ellipsis;
|
||||||
@@ -624,7 +634,8 @@ Ast *ast_undef(AstFile *f, Token token) {
|
|||||||
Ast *ast_basic_lit(AstFile *f, Token basic_lit) {
|
Ast *ast_basic_lit(AstFile *f, Token basic_lit) {
|
||||||
Ast *result = alloc_ast_node(f, Ast_BasicLit);
|
Ast *result = alloc_ast_node(f, Ast_BasicLit);
|
||||||
result->BasicLit.token = basic_lit;
|
result->BasicLit.token = basic_lit;
|
||||||
result->BasicLit.value = exact_value_from_basic_literal(basic_lit);
|
result->tav.mode = Addressing_Constant;
|
||||||
|
result->tav.value = exact_value_from_basic_literal(basic_lit);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -643,12 +654,12 @@ Ast *ast_ellipsis(AstFile *f, Token token, Ast *expr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Ast *ast_proc_group(AstFile *f, Token token, Token open, Token close, Array<Ast *> args) {
|
Ast *ast_proc_group(AstFile *f, Token token, Token open, Token close, Array<Ast *> const &args) {
|
||||||
Ast *result = alloc_ast_node(f, Ast_ProcGroup);
|
Ast *result = alloc_ast_node(f, Ast_ProcGroup);
|
||||||
result->ProcGroup.token = token;
|
result->ProcGroup.token = token;
|
||||||
result->ProcGroup.open = open;
|
result->ProcGroup.open = open;
|
||||||
result->ProcGroup.close = close;
|
result->ProcGroup.close = close;
|
||||||
result->ProcGroup.args = args;
|
result->ProcGroup.args = slice_from_array(args);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -658,7 +669,7 @@ Ast *ast_proc_lit(AstFile *f, Ast *type, Ast *body, u64 tags, Token where_token,
|
|||||||
result->ProcLit.body = body;
|
result->ProcLit.body = body;
|
||||||
result->ProcLit.tags = tags;
|
result->ProcLit.tags = tags;
|
||||||
result->ProcLit.where_token = where_token;
|
result->ProcLit.where_token = where_token;
|
||||||
result->ProcLit.where_clauses = where_clauses;
|
result->ProcLit.where_clauses = slice_from_array(where_clauses);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -670,10 +681,10 @@ Ast *ast_field_value(AstFile *f, Ast *field, Ast *value, Token eq) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ast *ast_compound_lit(AstFile *f, Ast *type, Array<Ast *> elems, Token open, Token close) {
|
Ast *ast_compound_lit(AstFile *f, Ast *type, Array<Ast *> const &elems, Token open, Token close) {
|
||||||
Ast *result = alloc_ast_node(f, Ast_CompoundLit);
|
Ast *result = alloc_ast_node(f, Ast_CompoundLit);
|
||||||
result->CompoundLit.type = type;
|
result->CompoundLit.type = type;
|
||||||
result->CompoundLit.elems = elems;
|
result->CompoundLit.elems = slice_from_array(elems);
|
||||||
result->CompoundLit.open = open;
|
result->CompoundLit.open = open;
|
||||||
result->CompoundLit.close = close;
|
result->CompoundLit.close = close;
|
||||||
return result;
|
return result;
|
||||||
@@ -736,7 +747,7 @@ Ast *ast_inline_asm_expr(AstFile *f, Token token, Token open, Token close,
|
|||||||
result->InlineAsmExpr.token = token;
|
result->InlineAsmExpr.token = token;
|
||||||
result->InlineAsmExpr.open = open;
|
result->InlineAsmExpr.open = open;
|
||||||
result->InlineAsmExpr.close = close;
|
result->InlineAsmExpr.close = close;
|
||||||
result->InlineAsmExpr.param_types = param_types;
|
result->InlineAsmExpr.param_types = slice_from_array(param_types);
|
||||||
result->InlineAsmExpr.return_type = return_type;
|
result->InlineAsmExpr.return_type = return_type;
|
||||||
result->InlineAsmExpr.asm_string = asm_string;
|
result->InlineAsmExpr.asm_string = asm_string;
|
||||||
result->InlineAsmExpr.constraints_string = constraints_string;
|
result->InlineAsmExpr.constraints_string = constraints_string;
|
||||||
@@ -768,18 +779,18 @@ Ast *ast_expr_stmt(AstFile *f, Ast *expr) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ast *ast_assign_stmt(AstFile *f, Token op, Array<Ast *> lhs, Array<Ast *> rhs) {
|
Ast *ast_assign_stmt(AstFile *f, Token op, Array<Ast *> const &lhs, Array<Ast *> const &rhs) {
|
||||||
Ast *result = alloc_ast_node(f, Ast_AssignStmt);
|
Ast *result = alloc_ast_node(f, Ast_AssignStmt);
|
||||||
result->AssignStmt.op = op;
|
result->AssignStmt.op = op;
|
||||||
result->AssignStmt.lhs = lhs;
|
result->AssignStmt.lhs = slice_from_array(lhs);
|
||||||
result->AssignStmt.rhs = rhs;
|
result->AssignStmt.rhs = slice_from_array(rhs);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Ast *ast_block_stmt(AstFile *f, Array<Ast *> stmts, Token open, Token close) {
|
Ast *ast_block_stmt(AstFile *f, Array<Ast *> const &stmts, Token open, Token close) {
|
||||||
Ast *result = alloc_ast_node(f, Ast_BlockStmt);
|
Ast *result = alloc_ast_node(f, Ast_BlockStmt);
|
||||||
result->BlockStmt.stmts = stmts;
|
result->BlockStmt.stmts = slice_from_array(stmts);
|
||||||
result->BlockStmt.open = open;
|
result->BlockStmt.open = open;
|
||||||
result->BlockStmt.close = close;
|
result->BlockStmt.close = close;
|
||||||
return result;
|
return result;
|
||||||
@@ -805,10 +816,10 @@ Ast *ast_when_stmt(AstFile *f, Token token, Ast *cond, Ast *body, Ast *else_stmt
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Ast *ast_return_stmt(AstFile *f, Token token, Array<Ast *> results) {
|
Ast *ast_return_stmt(AstFile *f, Token token, Array<Ast *> const &results) {
|
||||||
Ast *result = alloc_ast_node(f, Ast_ReturnStmt);
|
Ast *result = alloc_ast_node(f, Ast_ReturnStmt);
|
||||||
result->ReturnStmt.token = token;
|
result->ReturnStmt.token = token;
|
||||||
result->ReturnStmt.results = results;
|
result->ReturnStmt.results = slice_from_array(results);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -866,11 +877,11 @@ Ast *ast_type_switch_stmt(AstFile *f, Token token, Ast *tag, Ast *body) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ast *ast_case_clause(AstFile *f, Token token, Array<Ast *> list, Array<Ast *> stmts) {
|
Ast *ast_case_clause(AstFile *f, Token token, Array<Ast *> const &list, Array<Ast *> const &stmts) {
|
||||||
Ast *result = alloc_ast_node(f, Ast_CaseClause);
|
Ast *result = alloc_ast_node(f, Ast_CaseClause);
|
||||||
result->CaseClause.token = token;
|
result->CaseClause.token = token;
|
||||||
result->CaseClause.list = list;
|
result->CaseClause.list = slice_from_array(list);
|
||||||
result->CaseClause.stmts = stmts;
|
result->CaseClause.stmts = slice_from_array(stmts);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -889,10 +900,10 @@ Ast *ast_branch_stmt(AstFile *f, Token token, Ast *label) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ast *ast_using_stmt(AstFile *f, Token token, Array<Ast *> list) {
|
Ast *ast_using_stmt(AstFile *f, Token token, Array<Ast *> const &list) {
|
||||||
Ast *result = alloc_ast_node(f, Ast_UsingStmt);
|
Ast *result = alloc_ast_node(f, Ast_UsingStmt);
|
||||||
result->UsingStmt.token = token;
|
result->UsingStmt.token = token;
|
||||||
result->UsingStmt.list = list;
|
result->UsingStmt.list = slice_from_array(list);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -905,10 +916,10 @@ Ast *ast_bad_decl(AstFile *f, Token begin, Token end) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ast *ast_field(AstFile *f, Array<Ast *> names, Ast *type, Ast *default_value, u32 flags, Token tag,
|
Ast *ast_field(AstFile *f, Array<Ast *> const &names, Ast *type, Ast *default_value, u32 flags, Token tag,
|
||||||
CommentGroup *docs, CommentGroup *comment) {
|
CommentGroup *docs, CommentGroup *comment) {
|
||||||
Ast *result = alloc_ast_node(f, Ast_Field);
|
Ast *result = alloc_ast_node(f, Ast_Field);
|
||||||
result->Field.names = names;
|
result->Field.names = slice_from_array(names);
|
||||||
result->Field.type = type;
|
result->Field.type = type;
|
||||||
result->Field.default_value = default_value;
|
result->Field.default_value = default_value;
|
||||||
result->Field.flags = flags;
|
result->Field.flags = flags;
|
||||||
@@ -918,10 +929,10 @@ Ast *ast_field(AstFile *f, Array<Ast *> names, Ast *type, Ast *default_value, u3
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ast *ast_field_list(AstFile *f, Token token, Array<Ast *> list) {
|
Ast *ast_field_list(AstFile *f, Token token, Array<Ast *> const &list) {
|
||||||
Ast *result = alloc_ast_node(f, Ast_FieldList);
|
Ast *result = alloc_ast_node(f, Ast_FieldList);
|
||||||
result->FieldList.token = token;
|
result->FieldList.token = token;
|
||||||
result->FieldList.list = list;
|
result->FieldList.list = slice_from_array(list);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1002,7 +1013,7 @@ Ast *ast_dynamic_array_type(AstFile *f, Token token, Ast *elem) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ast *ast_struct_type(AstFile *f, Token token, Array<Ast *> fields, isize field_count,
|
Ast *ast_struct_type(AstFile *f, Token token, Slice<Ast *> fields, isize field_count,
|
||||||
Ast *polymorphic_params, bool is_packed, bool is_raw_union,
|
Ast *polymorphic_params, bool is_packed, bool is_raw_union,
|
||||||
Ast *align,
|
Ast *align,
|
||||||
Token where_token, Array<Ast *> const &where_clauses) {
|
Token where_token, Array<Ast *> const &where_clauses) {
|
||||||
@@ -1015,38 +1026,38 @@ Ast *ast_struct_type(AstFile *f, Token token, Array<Ast *> fields, isize field_c
|
|||||||
result->StructType.is_raw_union = is_raw_union;
|
result->StructType.is_raw_union = is_raw_union;
|
||||||
result->StructType.align = align;
|
result->StructType.align = align;
|
||||||
result->StructType.where_token = where_token;
|
result->StructType.where_token = where_token;
|
||||||
result->StructType.where_clauses = where_clauses;
|
result->StructType.where_clauses = slice_from_array(where_clauses);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Ast *ast_union_type(AstFile *f, Token token, Array<Ast *> variants, Ast *polymorphic_params, Ast *align, bool no_nil, bool maybe,
|
Ast *ast_union_type(AstFile *f, Token token, Array<Ast *> const &variants, Ast *polymorphic_params, Ast *align, bool no_nil, bool maybe,
|
||||||
Token where_token, Array<Ast *> const &where_clauses) {
|
Token where_token, Array<Ast *> const &where_clauses) {
|
||||||
Ast *result = alloc_ast_node(f, Ast_UnionType);
|
Ast *result = alloc_ast_node(f, Ast_UnionType);
|
||||||
result->UnionType.token = token;
|
result->UnionType.token = token;
|
||||||
result->UnionType.variants = variants;
|
result->UnionType.variants = slice_from_array(variants);
|
||||||
result->UnionType.polymorphic_params = polymorphic_params;
|
result->UnionType.polymorphic_params = polymorphic_params;
|
||||||
result->UnionType.align = align;
|
result->UnionType.align = align;
|
||||||
result->UnionType.no_nil = no_nil;
|
result->UnionType.no_nil = no_nil;
|
||||||
result->UnionType.maybe = maybe;
|
result->UnionType.maybe = maybe;
|
||||||
result->UnionType.where_token = where_token;
|
result->UnionType.where_token = where_token;
|
||||||
result->UnionType.where_clauses = where_clauses;
|
result->UnionType.where_clauses = slice_from_array(where_clauses);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Ast *ast_enum_type(AstFile *f, Token token, Ast *base_type, Array<Ast *> fields) {
|
Ast *ast_enum_type(AstFile *f, Token token, Ast *base_type, Array<Ast *> const &fields) {
|
||||||
Ast *result = alloc_ast_node(f, Ast_EnumType);
|
Ast *result = alloc_ast_node(f, Ast_EnumType);
|
||||||
result->EnumType.token = token;
|
result->EnumType.token = token;
|
||||||
result->EnumType.base_type = base_type;
|
result->EnumType.base_type = base_type;
|
||||||
result->EnumType.fields = fields;
|
result->EnumType.fields = slice_from_array(fields);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ast *ast_bit_field_type(AstFile *f, Token token, Array<Ast *> fields, Ast *align) {
|
Ast *ast_bit_field_type(AstFile *f, Token token, Array<Ast *> const &fields, Ast *align) {
|
||||||
Ast *result = alloc_ast_node(f, Ast_BitFieldType);
|
Ast *result = alloc_ast_node(f, Ast_BitFieldType);
|
||||||
result->BitFieldType.token = token;
|
result->BitFieldType.token = token;
|
||||||
result->BitFieldType.fields = fields;
|
result->BitFieldType.fields = slice_from_array(fields);
|
||||||
result->BitFieldType.align = align;
|
result->BitFieldType.align = align;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -1069,7 +1080,7 @@ Ast *ast_map_type(AstFile *f, Token token, Ast *key, Ast *value) {
|
|||||||
|
|
||||||
|
|
||||||
Ast *ast_foreign_block_decl(AstFile *f, Token token, Ast *foreign_library, Ast *body,
|
Ast *ast_foreign_block_decl(AstFile *f, Token token, Ast *foreign_library, Ast *body,
|
||||||
CommentGroup *docs) {
|
CommentGroup *docs) {
|
||||||
Ast *result = alloc_ast_node(f, Ast_ForeignBlockDecl);
|
Ast *result = alloc_ast_node(f, Ast_ForeignBlockDecl);
|
||||||
result->ForeignBlockDecl.token = token;
|
result->ForeignBlockDecl.token = token;
|
||||||
result->ForeignBlockDecl.foreign_library = foreign_library;
|
result->ForeignBlockDecl.foreign_library = foreign_library;
|
||||||
@@ -1087,12 +1098,12 @@ Ast *ast_label_decl(AstFile *f, Token token, Ast *name) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ast *ast_value_decl(AstFile *f, Array<Ast *> names, Ast *type, Array<Ast *> values, bool is_mutable,
|
Ast *ast_value_decl(AstFile *f, Array<Ast *> const &names, Ast *type, Array<Ast *> const &values, bool is_mutable,
|
||||||
CommentGroup *docs, CommentGroup *comment) {
|
CommentGroup *docs, CommentGroup *comment) {
|
||||||
Ast *result = alloc_ast_node(f, Ast_ValueDecl);
|
Ast *result = alloc_ast_node(f, Ast_ValueDecl);
|
||||||
result->ValueDecl.names = names;
|
result->ValueDecl.names = slice_from_array(names);
|
||||||
result->ValueDecl.type = type;
|
result->ValueDecl.type = type;
|
||||||
result->ValueDecl.values = values;
|
result->ValueDecl.values = slice_from_array(values);
|
||||||
result->ValueDecl.is_mutable = is_mutable;
|
result->ValueDecl.is_mutable = is_mutable;
|
||||||
result->ValueDecl.docs = docs;
|
result->ValueDecl.docs = docs;
|
||||||
result->ValueDecl.comment = comment;
|
result->ValueDecl.comment = comment;
|
||||||
@@ -1111,7 +1122,7 @@ Ast *ast_package_decl(AstFile *f, Token token, Token name, CommentGroup *docs, C
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ast *ast_import_decl(AstFile *f, Token token, bool is_using, Token relpath, Token import_name,
|
Ast *ast_import_decl(AstFile *f, Token token, bool is_using, Token relpath, Token import_name,
|
||||||
CommentGroup *docs, CommentGroup *comment) {
|
CommentGroup *docs, CommentGroup *comment) {
|
||||||
Ast *result = alloc_ast_node(f, Ast_ImportDecl);
|
Ast *result = alloc_ast_node(f, Ast_ImportDecl);
|
||||||
result->ImportDecl.token = token;
|
result->ImportDecl.token = token;
|
||||||
result->ImportDecl.is_using = is_using;
|
result->ImportDecl.is_using = is_using;
|
||||||
@@ -1123,10 +1134,10 @@ Ast *ast_import_decl(AstFile *f, Token token, bool is_using, Token relpath, Toke
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ast *ast_foreign_import_decl(AstFile *f, Token token, Array<Token> filepaths, Token library_name,
|
Ast *ast_foreign_import_decl(AstFile *f, Token token, Array<Token> filepaths, Token library_name,
|
||||||
CommentGroup *docs, CommentGroup *comment) {
|
CommentGroup *docs, CommentGroup *comment) {
|
||||||
Ast *result = alloc_ast_node(f, Ast_ForeignImportDecl);
|
Ast *result = alloc_ast_node(f, Ast_ForeignImportDecl);
|
||||||
result->ForeignImportDecl.token = token;
|
result->ForeignImportDecl.token = token;
|
||||||
result->ForeignImportDecl.filepaths = filepaths;
|
result->ForeignImportDecl.filepaths = slice_from_array(filepaths);
|
||||||
result->ForeignImportDecl.library_name = library_name;
|
result->ForeignImportDecl.library_name = library_name;
|
||||||
result->ForeignImportDecl.docs = docs;
|
result->ForeignImportDecl.docs = docs;
|
||||||
result->ForeignImportDecl.comment = comment;
|
result->ForeignImportDecl.comment = comment;
|
||||||
@@ -1136,11 +1147,11 @@ Ast *ast_foreign_import_decl(AstFile *f, Token token, Array<Token> filepaths, To
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Ast *ast_attribute(AstFile *f, Token token, Token open, Token close, Array<Ast *> elems) {
|
Ast *ast_attribute(AstFile *f, Token token, Token open, Token close, Array<Ast *> const &elems) {
|
||||||
Ast *result = alloc_ast_node(f, Ast_Attribute);
|
Ast *result = alloc_ast_node(f, Ast_Attribute);
|
||||||
result->Attribute.token = token;
|
result->Attribute.token = token;
|
||||||
result->Attribute.open = open;
|
result->Attribute.open = open;
|
||||||
result->Attribute.elems = elems;
|
result->Attribute.elems = slice_from_array(elems);
|
||||||
result->Attribute.close = close;
|
result->Attribute.close = close;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -1182,6 +1193,12 @@ CommentGroup *consume_comment_group(AstFile *f, isize n, isize *end_line_) {
|
|||||||
Array<Token> list = {};
|
Array<Token> list = {};
|
||||||
list.allocator = heap_allocator();
|
list.allocator = heap_allocator();
|
||||||
isize end_line = f->curr_token.pos.line;
|
isize end_line = f->curr_token.pos.line;
|
||||||
|
if (f->curr_token_index == 1 &&
|
||||||
|
f->prev_token.kind == Token_Comment &&
|
||||||
|
f->prev_token.pos.line+1 == f->curr_token.pos.line) {
|
||||||
|
// NOTE(bill): Special logic for the first comment in the file
|
||||||
|
array_add(&list, f->prev_token);
|
||||||
|
}
|
||||||
while (f->curr_token.kind == Token_Comment &&
|
while (f->curr_token.kind == Token_Comment &&
|
||||||
f->curr_token.pos.line <= end_line+n) {
|
f->curr_token.pos.line <= end_line+n) {
|
||||||
array_add(&list, consume_comment(f, &end_line));
|
array_add(&list, consume_comment(f, &end_line));
|
||||||
@@ -1192,7 +1209,7 @@ CommentGroup *consume_comment_group(AstFile *f, isize n, isize *end_line_) {
|
|||||||
CommentGroup *comments = nullptr;
|
CommentGroup *comments = nullptr;
|
||||||
if (list.count > 0) {
|
if (list.count > 0) {
|
||||||
comments = gb_alloc_item(heap_allocator(), CommentGroup);
|
comments = gb_alloc_item(heap_allocator(), CommentGroup);
|
||||||
comments->list = list;
|
comments->list = slice_from_array(list);
|
||||||
array_add(&f->comments, comments);
|
array_add(&f->comments, comments);
|
||||||
}
|
}
|
||||||
return comments;
|
return comments;
|
||||||
@@ -1894,12 +1911,18 @@ Ast *parse_operand(AstFile *f, bool lhs) {
|
|||||||
|
|
||||||
case Token_opaque: {
|
case Token_opaque: {
|
||||||
Token token = expect_token(f, Token_opaque);
|
Token token = expect_token(f, Token_opaque);
|
||||||
|
warning(token, "opaque is deprecated, please use #opaque");
|
||||||
Ast *type = parse_type(f);
|
Ast *type = parse_type(f);
|
||||||
return ast_opaque_type(f, token, type);
|
return ast_opaque_type(f, token, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
case Token_Hash: {
|
case Token_Hash: {
|
||||||
Token token = expect_token(f, Token_Hash);
|
Token token = expect_token(f, Token_Hash);
|
||||||
|
if (allow_token(f, Token_opaque)) {
|
||||||
|
Ast *type = parse_type(f);
|
||||||
|
return ast_opaque_type(f, token, type);
|
||||||
|
}
|
||||||
|
|
||||||
Token name = expect_token(f, Token_Ident);
|
Token name = expect_token(f, Token_Ident);
|
||||||
if (name.string == "type") {
|
if (name.string == "type") {
|
||||||
return ast_helper_type(f, token, parse_type(f));
|
return ast_helper_type(f, token, parse_type(f));
|
||||||
@@ -2201,7 +2224,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
|
|||||||
|
|
||||||
if (allow_token(f, Token_OpenParen)) {
|
if (allow_token(f, Token_OpenParen)) {
|
||||||
isize param_count = 0;
|
isize param_count = 0;
|
||||||
polymorphic_params = parse_field_list(f, ¶m_count, 0, Token_CloseParen, false, true);
|
polymorphic_params = parse_field_list(f, ¶m_count, 0, Token_CloseParen, true, true);
|
||||||
if (param_count == 0) {
|
if (param_count == 0) {
|
||||||
syntax_error(polymorphic_params, "Expected at least 1 polymorphic parameter");
|
syntax_error(polymorphic_params, "Expected at least 1 polymorphic parameter");
|
||||||
polymorphic_params = nullptr;
|
polymorphic_params = nullptr;
|
||||||
@@ -2262,7 +2285,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
|
|||||||
Ast *fields = parse_struct_field_list(f, &name_count);
|
Ast *fields = parse_struct_field_list(f, &name_count);
|
||||||
Token close = expect_token(f, Token_CloseBrace);
|
Token close = expect_token(f, Token_CloseBrace);
|
||||||
|
|
||||||
Array<Ast *> decls = {};
|
Slice<Ast *> decls = {};
|
||||||
if (fields != nullptr) {
|
if (fields != nullptr) {
|
||||||
GB_ASSERT(fields->kind == Ast_FieldList);
|
GB_ASSERT(fields->kind == Ast_FieldList);
|
||||||
decls = fields->FieldList.list;
|
decls = fields->FieldList.list;
|
||||||
@@ -2284,7 +2307,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
|
|||||||
|
|
||||||
if (allow_token(f, Token_OpenParen)) {
|
if (allow_token(f, Token_OpenParen)) {
|
||||||
isize param_count = 0;
|
isize param_count = 0;
|
||||||
polymorphic_params = parse_field_list(f, ¶m_count, 0, Token_CloseParen, false, true);
|
polymorphic_params = parse_field_list(f, ¶m_count, 0, Token_CloseParen, true, true);
|
||||||
if (param_count == 0) {
|
if (param_count == 0) {
|
||||||
syntax_error(polymorphic_params, "Expected at least 1 polymorphic parametric");
|
syntax_error(polymorphic_params, "Expected at least 1 polymorphic parametric");
|
||||||
polymorphic_params = nullptr;
|
polymorphic_params = nullptr;
|
||||||
@@ -2586,7 +2609,15 @@ Ast *parse_call_expr(AstFile *f, Ast *operand) {
|
|||||||
f->expr_level--;
|
f->expr_level--;
|
||||||
close_paren = expect_closing(f, Token_CloseParen, str_lit("argument list"));
|
close_paren = expect_closing(f, Token_CloseParen, str_lit("argument list"));
|
||||||
|
|
||||||
return ast_call_expr(f, operand, args, open_paren, close_paren, ellipsis);
|
|
||||||
|
Ast *call = ast_call_expr(f, operand, args, open_paren, close_paren, ellipsis);
|
||||||
|
|
||||||
|
Ast *o = unparen_expr(operand);
|
||||||
|
if (o->kind == Ast_SelectorExpr && o->SelectorExpr.token.kind == Token_ArrowRight) {
|
||||||
|
return ast_selector_call_expr(f, o->SelectorExpr.token, o, call);
|
||||||
|
}
|
||||||
|
|
||||||
|
return call;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) {
|
Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) {
|
||||||
@@ -2638,11 +2669,10 @@ Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) {
|
|||||||
|
|
||||||
case Token_ArrowRight: {
|
case Token_ArrowRight: {
|
||||||
Token token = advance_token(f);
|
Token token = advance_token(f);
|
||||||
// syntax_error(token, "Selector expressions use '.' rather than '->'");
|
|
||||||
|
|
||||||
Ast *sel = ast_selector_expr(f, token, operand, parse_ident(f));
|
operand = ast_selector_expr(f, token, operand, parse_ident(f));
|
||||||
Ast *call = parse_call_expr(f, sel);
|
// Ast *call = parse_call_expr(f, sel);
|
||||||
operand = ast_selector_call_expr(f, token, sel, call);
|
// operand = ast_selector_call_expr(f, token, sel, call);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3317,11 +3347,10 @@ FieldPrefixKind is_token_field_prefix(AstFile *f) {
|
|||||||
return FieldPrefix_no_alias;
|
return FieldPrefix_no_alias;
|
||||||
} else if (f->curr_token.string == "c_vararg") {
|
} else if (f->curr_token.string == "c_vararg") {
|
||||||
return FieldPrefix_c_var_arg;
|
return FieldPrefix_c_var_arg;
|
||||||
|
} else if (f->curr_token.string == "const") {
|
||||||
|
return FieldPrefix_const;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Token_const:
|
|
||||||
return FieldPrefix_const;
|
|
||||||
}
|
}
|
||||||
return FieldPrefix_Unknown;
|
return FieldPrefix_Unknown;
|
||||||
}
|
}
|
||||||
@@ -4723,18 +4752,14 @@ void parser_add_foreign_file_to_process(Parser *p, AstPackage *pkg, AstForeignFi
|
|||||||
|
|
||||||
|
|
||||||
// NOTE(bill): Returns true if it's added
|
// NOTE(bill): Returns true if it's added
|
||||||
bool try_add_import_path(Parser *p, String const &path, String const &rel_path, TokenPos pos, PackageKind kind = Package_Normal) {
|
AstPackage *try_add_import_path(Parser *p, String const &path, String const &rel_path, TokenPos pos, PackageKind kind = Package_Normal) {
|
||||||
if (build_context.generate_docs) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
String const FILE_EXT = str_lit(".odin");
|
String const FILE_EXT = str_lit(".odin");
|
||||||
|
|
||||||
gb_mutex_lock(&p->file_add_mutex);
|
gb_mutex_lock(&p->file_add_mutex);
|
||||||
defer (gb_mutex_unlock(&p->file_add_mutex));
|
defer (gb_mutex_unlock(&p->file_add_mutex));
|
||||||
|
|
||||||
if (string_set_exists(&p->imported_files, path)) {
|
if (string_set_exists(&p->imported_files, path)) {
|
||||||
return false;
|
return nullptr;
|
||||||
}
|
}
|
||||||
string_set_add(&p->imported_files, path);
|
string_set_add(&p->imported_files, path);
|
||||||
|
|
||||||
@@ -4757,7 +4782,7 @@ bool try_add_import_path(Parser *p, String const &path, String const &rel_path,
|
|||||||
pkg->is_single_file = true;
|
pkg->is_single_file = true;
|
||||||
parser_add_file_to_process(p, pkg, fi, pos);
|
parser_add_file_to_process(p, pkg, fi, pos);
|
||||||
parser_add_package(p, pkg);
|
parser_add_package(p, pkg);
|
||||||
return true;
|
return pkg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -4773,22 +4798,22 @@ bool try_add_import_path(Parser *p, String const &path, String const &rel_path,
|
|||||||
switch (rd_err) {
|
switch (rd_err) {
|
||||||
case ReadDirectory_InvalidPath:
|
case ReadDirectory_InvalidPath:
|
||||||
syntax_error(pos, "Invalid path: %.*s", LIT(rel_path));
|
syntax_error(pos, "Invalid path: %.*s", LIT(rel_path));
|
||||||
return false;
|
return nullptr;
|
||||||
case ReadDirectory_NotExists:
|
case ReadDirectory_NotExists:
|
||||||
syntax_error(pos, "Path does not exist: %.*s", LIT(rel_path));
|
syntax_error(pos, "Path does not exist: %.*s", LIT(rel_path));
|
||||||
return false;
|
return nullptr;
|
||||||
case ReadDirectory_Permission:
|
case ReadDirectory_Permission:
|
||||||
syntax_error(pos, "Unknown error whilst reading path %.*s", LIT(rel_path));
|
syntax_error(pos, "Unknown error whilst reading path %.*s", LIT(rel_path));
|
||||||
return false;
|
return nullptr;
|
||||||
case ReadDirectory_NotDir:
|
case ReadDirectory_NotDir:
|
||||||
syntax_error(pos, "Expected a directory for a package, got a file: %.*s", LIT(rel_path));
|
syntax_error(pos, "Expected a directory for a package, got a file: %.*s", LIT(rel_path));
|
||||||
return false;
|
return nullptr;
|
||||||
case ReadDirectory_Empty:
|
case ReadDirectory_Empty:
|
||||||
syntax_error(pos, "Empty directory: %.*s", LIT(rel_path));
|
syntax_error(pos, "Empty directory: %.*s", LIT(rel_path));
|
||||||
return false;
|
return nullptr;
|
||||||
case ReadDirectory_Unknown:
|
case ReadDirectory_Unknown:
|
||||||
syntax_error(pos, "Unknown error whilst reading path %.*s", LIT(rel_path));
|
syntax_error(pos, "Unknown error whilst reading path %.*s", LIT(rel_path));
|
||||||
return false;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
for_array(list_index, list) {
|
for_array(list_index, list) {
|
||||||
@@ -4810,7 +4835,7 @@ bool try_add_import_path(Parser *p, String const &path, String const &rel_path,
|
|||||||
|
|
||||||
parser_add_package(p, pkg);
|
parser_add_package(p, pkg);
|
||||||
|
|
||||||
return true;
|
return pkg;
|
||||||
}
|
}
|
||||||
|
|
||||||
gb_global Rune illegal_import_runes[] = {
|
gb_global Rune illegal_import_runes[] = {
|
||||||
@@ -4829,7 +4854,7 @@ bool is_import_path_valid(String path) {
|
|||||||
u8 *curr = start;
|
u8 *curr = start;
|
||||||
while (curr < end) {
|
while (curr < end) {
|
||||||
isize width = 1;
|
isize width = 1;
|
||||||
Rune r = curr[0];
|
Rune r = *curr;
|
||||||
if (r >= 0x80) {
|
if (r >= 0x80) {
|
||||||
width = gb_utf8_decode(curr, end-curr, &r);
|
width = gb_utf8_decode(curr, end-curr, &r);
|
||||||
if (r == GB_RUNE_INVALID && width == 1) {
|
if (r == GB_RUNE_INVALID && width == 1) {
|
||||||
@@ -4854,6 +4879,45 @@ bool is_import_path_valid(String path) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_build_flag_path_valid(String path) {
|
||||||
|
if (path.len > 0) {
|
||||||
|
u8 *start = path.text;
|
||||||
|
u8 *end = path.text + path.len;
|
||||||
|
u8 *curr = start;
|
||||||
|
isize index = 0;
|
||||||
|
while (curr < end) {
|
||||||
|
isize width = 1;
|
||||||
|
Rune r = *curr;
|
||||||
|
if (r >= 0x80) {
|
||||||
|
width = gb_utf8_decode(curr, end-curr, &r);
|
||||||
|
if (r == GB_RUNE_INVALID && width == 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (r == GB_RUNE_BOM && curr-start > 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (isize i = 0; i < gb_count_of(illegal_import_runes); i++) {
|
||||||
|
#if defined(GB_SYSTEM_WINDOWS)
|
||||||
|
if (r == '\\') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (r == illegal_import_runes[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
curr += width;
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool is_package_name_reserved(String const &name) {
|
bool is_package_name_reserved(String const &name) {
|
||||||
if (name == "builtin") {
|
if (name == "builtin") {
|
||||||
@@ -4974,7 +5038,7 @@ bool determine_path_from_string(gbMutex *file_mutex, Ast *node, String base_dir,
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array<Ast *> &decls);
|
void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Slice<Ast *> &decls);
|
||||||
|
|
||||||
void parse_setup_file_when_stmt(Parser *p, AstFile *f, String base_dir, AstWhenStmt *ws) {
|
void parse_setup_file_when_stmt(Parser *p, AstFile *f, String base_dir, AstWhenStmt *ws) {
|
||||||
if (ws->body != nullptr) {
|
if (ws->body != nullptr) {
|
||||||
@@ -4995,7 +5059,7 @@ void parse_setup_file_when_stmt(Parser *p, AstFile *f, String base_dir, AstWhenS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array<Ast *> &decls) {
|
void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Slice<Ast *> &decls) {
|
||||||
for_array(i, decls) {
|
for_array(i, decls) {
|
||||||
Ast *node = decls[i];
|
Ast *node = decls[i];
|
||||||
if (!is_ast_decl(node) &&
|
if (!is_ast_decl(node) &&
|
||||||
@@ -5034,8 +5098,7 @@ void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array<Ast *>
|
|||||||
} else if (node->kind == Ast_ForeignImportDecl) {
|
} else if (node->kind == Ast_ForeignImportDecl) {
|
||||||
ast_node(fl, ForeignImportDecl, node);
|
ast_node(fl, ForeignImportDecl, node);
|
||||||
|
|
||||||
fl->fullpaths.allocator = heap_allocator();
|
auto fullpaths = array_make<String>(permanent_allocator(), 0, fl->filepaths.count);
|
||||||
array_reserve(&fl->fullpaths, fl->filepaths.count);
|
|
||||||
|
|
||||||
for_array(fp_idx, fl->filepaths) {
|
for_array(fp_idx, fl->filepaths) {
|
||||||
String file_str = fl->filepaths[fp_idx].string;
|
String file_str = fl->filepaths[fp_idx].string;
|
||||||
@@ -5049,14 +5112,17 @@ void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array<Ast *>
|
|||||||
}
|
}
|
||||||
fullpath = foreign_path;
|
fullpath = foreign_path;
|
||||||
}
|
}
|
||||||
array_add(&fl->fullpaths, fullpath);
|
array_add(&fullpaths, fullpath);
|
||||||
}
|
}
|
||||||
if (fl->fullpaths.count == 0) {
|
if (fullpaths.count == 0) {
|
||||||
syntax_error(decls[i], "No foreign paths found");
|
syntax_error(decls[i], "No foreign paths found");
|
||||||
decls[i] = ast_bad_decl(f, fl->filepaths[0], fl->filepaths[fl->filepaths.count-1]);
|
decls[i] = ast_bad_decl(f, fl->filepaths[0], fl->filepaths[fl->filepaths.count-1]);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fl->fullpaths = slice_from_array(fullpaths);
|
||||||
|
|
||||||
|
|
||||||
} else if (node->kind == Ast_WhenStmt) {
|
} else if (node->kind == Ast_WhenStmt) {
|
||||||
ast_node(ws, WhenStmt, node);
|
ast_node(ws, WhenStmt, node);
|
||||||
parse_setup_file_when_stmt(p, f, base_dir, ws);
|
parse_setup_file_when_stmt(p, f, base_dir, ws);
|
||||||
@@ -5218,12 +5284,12 @@ bool parse_file(Parser *p, AstFile *f) {
|
|||||||
f->pkg_decl = pd;
|
f->pkg_decl = pd;
|
||||||
|
|
||||||
if (f->error_count == 0) {
|
if (f->error_count == 0) {
|
||||||
f->decls = array_make<Ast *>(heap_allocator());
|
auto decls = array_make<Ast *>(heap_allocator());
|
||||||
|
|
||||||
while (f->curr_token.kind != Token_EOF) {
|
while (f->curr_token.kind != Token_EOF) {
|
||||||
Ast *stmt = parse_stmt(f);
|
Ast *stmt = parse_stmt(f);
|
||||||
if (stmt && stmt->kind != Ast_EmptyStmt) {
|
if (stmt && stmt->kind != Ast_EmptyStmt) {
|
||||||
array_add(&f->decls, stmt);
|
array_add(&decls, stmt);
|
||||||
if (stmt->kind == Ast_ExprStmt &&
|
if (stmt->kind == Ast_ExprStmt &&
|
||||||
stmt->ExprStmt.expr != nullptr &&
|
stmt->ExprStmt.expr != nullptr &&
|
||||||
stmt->ExprStmt.expr->kind == Ast_ProcLit) {
|
stmt->ExprStmt.expr->kind == Ast_ProcLit) {
|
||||||
@@ -5232,6 +5298,8 @@ bool parse_file(Parser *p, AstFile *f) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f->decls = slice_from_array(decls);
|
||||||
|
|
||||||
parse_setup_file_decls(p, f, base_dir, f->decls);
|
parse_setup_file_decls(p, f, base_dir, f->decls);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5325,7 +5393,7 @@ ParseFileError parse_packages(Parser *p, String init_filename) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TokenPos init_pos = {};
|
TokenPos init_pos = {};
|
||||||
if (!build_context.generate_docs) {
|
{
|
||||||
String s = get_fullpath_core(heap_allocator(), str_lit("runtime"));
|
String s = get_fullpath_core(heap_allocator(), str_lit("runtime"));
|
||||||
try_add_import_path(p, s, s, init_pos, Package_Runtime);
|
try_add_import_path(p, s, s, init_pos, Package_Runtime);
|
||||||
}
|
}
|
||||||
@@ -5333,6 +5401,22 @@ ParseFileError parse_packages(Parser *p, String init_filename) {
|
|||||||
try_add_import_path(p, init_fullpath, init_fullpath, init_pos, Package_Init);
|
try_add_import_path(p, init_fullpath, init_fullpath, init_pos, Package_Init);
|
||||||
p->init_fullpath = init_fullpath;
|
p->init_fullpath = init_fullpath;
|
||||||
|
|
||||||
|
for_array(i, build_context.extra_packages) {
|
||||||
|
String path = build_context.extra_packages[i];
|
||||||
|
String fullpath = path_to_full_path(heap_allocator(), path); // LEAK?
|
||||||
|
if (!path_is_directory(fullpath)) {
|
||||||
|
String const ext = str_lit(".odin");
|
||||||
|
if (!string_ends_with(fullpath, ext)) {
|
||||||
|
error_line("Expected either a directory or a .odin file, got '%.*s'\n", LIT(fullpath));
|
||||||
|
return ParseFile_WrongExtension;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AstPackage *pkg = try_add_import_path(p, fullpath, fullpath, init_pos, Package_Normal);
|
||||||
|
if (pkg) {
|
||||||
|
pkg->is_extra = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
thread_pool_start(&parser_thread_pool);
|
thread_pool_start(&parser_thread_pool);
|
||||||
thread_pool_wait_to_process(&parser_thread_pool);
|
thread_pool_wait_to_process(&parser_thread_pool);
|
||||||
|
|
||||||
|
|||||||
+42
-39
@@ -46,7 +46,7 @@ enum ParseFileError {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct CommentGroup {
|
struct CommentGroup {
|
||||||
Array<Token> list; // Token_Comment
|
Slice<Token> list; // Token_Comment
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -98,8 +98,8 @@ struct AstFile {
|
|||||||
bool in_foreign_block;
|
bool in_foreign_block;
|
||||||
bool allow_type;
|
bool allow_type;
|
||||||
|
|
||||||
Array<Ast *> decls;
|
Slice<Ast *> decls;
|
||||||
Array<Ast *> imports; // 'import' 'using import'
|
Array<Ast *> imports; // 'import'
|
||||||
isize directive_count;
|
isize directive_count;
|
||||||
|
|
||||||
Ast * curr_proc;
|
Ast * curr_proc;
|
||||||
@@ -107,6 +107,8 @@ struct AstFile {
|
|||||||
f64 time_to_tokenize; // seconds
|
f64 time_to_tokenize; // seconds
|
||||||
f64 time_to_parse; // seconds
|
f64 time_to_parse; // seconds
|
||||||
|
|
||||||
|
bool is_test;
|
||||||
|
|
||||||
CommentGroup *lead_comment; // Comment (block) before the decl
|
CommentGroup *lead_comment; // Comment (block) before the decl
|
||||||
CommentGroup *line_comment; // Comment after the semicolon
|
CommentGroup *line_comment; // Comment after the semicolon
|
||||||
CommentGroup *docs; // current docs
|
CommentGroup *docs; // current docs
|
||||||
@@ -148,6 +150,7 @@ struct AstPackage {
|
|||||||
Scope * scope;
|
Scope * scope;
|
||||||
DeclInfo *decl_info;
|
DeclInfo *decl_info;
|
||||||
bool used;
|
bool used;
|
||||||
|
bool is_extra;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -217,14 +220,16 @@ enum ProcCallingConvention {
|
|||||||
ProcCC_ForeignBlockDefault = -1,
|
ProcCC_ForeignBlockDefault = -1,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum StateFlag {
|
enum StateFlag : u16 {
|
||||||
StateFlag_bounds_check = 1<<0,
|
StateFlag_bounds_check = 1<<0,
|
||||||
StateFlag_no_bounds_check = 1<<1,
|
StateFlag_no_bounds_check = 1<<1,
|
||||||
|
|
||||||
StateFlag_no_deferred = 1<<5,
|
StateFlag_no_deferred = 1<<5,
|
||||||
|
|
||||||
|
StateFlag_BeenHandled = 1<<15,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ViralStateFlag {
|
enum ViralStateFlag : u16 {
|
||||||
ViralStateFlag_ContainsDeferredProcedure = 1<<0,
|
ViralStateFlag_ContainsDeferredProcedure = 1<<0,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -275,7 +280,6 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = {
|
|||||||
AST_KIND(Undef, "undef", Token) \
|
AST_KIND(Undef, "undef", Token) \
|
||||||
AST_KIND(BasicLit, "basic literal", struct { \
|
AST_KIND(BasicLit, "basic literal", struct { \
|
||||||
Token token; \
|
Token token; \
|
||||||
ExactValue value; \
|
|
||||||
}) \
|
}) \
|
||||||
AST_KIND(BasicDirective, "basic directive", struct { \
|
AST_KIND(BasicDirective, "basic directive", struct { \
|
||||||
Token token; \
|
Token token; \
|
||||||
@@ -289,7 +293,7 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = {
|
|||||||
Token token; \
|
Token token; \
|
||||||
Token open; \
|
Token open; \
|
||||||
Token close; \
|
Token close; \
|
||||||
Array<Ast *> args; \
|
Slice<Ast *> args; \
|
||||||
}) \
|
}) \
|
||||||
AST_KIND(ProcLit, "procedure literal", struct { \
|
AST_KIND(ProcLit, "procedure literal", struct { \
|
||||||
Ast *type; \
|
Ast *type; \
|
||||||
@@ -297,12 +301,12 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = {
|
|||||||
u64 tags; \
|
u64 tags; \
|
||||||
ProcInlining inlining; \
|
ProcInlining inlining; \
|
||||||
Token where_token; \
|
Token where_token; \
|
||||||
Array<Ast *> where_clauses; \
|
Slice<Ast *> where_clauses; \
|
||||||
DeclInfo *decl; \
|
DeclInfo *decl; \
|
||||||
}) \
|
}) \
|
||||||
AST_KIND(CompoundLit, "compound literal", struct { \
|
AST_KIND(CompoundLit, "compound literal", struct { \
|
||||||
Ast *type; \
|
Ast *type; \
|
||||||
Array<Ast *> elems; \
|
Slice<Ast *> elems; \
|
||||||
Token open, close; \
|
Token open, close; \
|
||||||
i64 max_count; \
|
i64 max_count; \
|
||||||
}) \
|
}) \
|
||||||
@@ -325,7 +329,7 @@ AST_KIND(_ExprBegin, "", bool) \
|
|||||||
}) \
|
}) \
|
||||||
AST_KIND(CallExpr, "call expression", struct { \
|
AST_KIND(CallExpr, "call expression", struct { \
|
||||||
Ast * proc; \
|
Ast * proc; \
|
||||||
Array<Ast *> args; \
|
Slice<Ast *> args; \
|
||||||
Token open; \
|
Token open; \
|
||||||
Token close; \
|
Token close; \
|
||||||
Token ellipsis; \
|
Token ellipsis; \
|
||||||
@@ -342,7 +346,7 @@ AST_KIND(_ExprBegin, "", bool) \
|
|||||||
AST_KIND(InlineAsmExpr, "inline asm expression", struct { \
|
AST_KIND(InlineAsmExpr, "inline asm expression", struct { \
|
||||||
Token token; \
|
Token token; \
|
||||||
Token open, close; \
|
Token open, close; \
|
||||||
Array<Ast *> param_types; \
|
Slice<Ast *> param_types; \
|
||||||
Ast *return_type; \
|
Ast *return_type; \
|
||||||
Ast *asm_string; \
|
Ast *asm_string; \
|
||||||
Ast *constraints_string; \
|
Ast *constraints_string; \
|
||||||
@@ -362,11 +366,11 @@ AST_KIND(_StmtBegin, "", bool) \
|
|||||||
}) \
|
}) \
|
||||||
AST_KIND(AssignStmt, "assign statement", struct { \
|
AST_KIND(AssignStmt, "assign statement", struct { \
|
||||||
Token op; \
|
Token op; \
|
||||||
Array<Ast *> lhs, rhs; \
|
Slice<Ast *> lhs, rhs; \
|
||||||
}) \
|
}) \
|
||||||
AST_KIND(_ComplexStmtBegin, "", bool) \
|
AST_KIND(_ComplexStmtBegin, "", bool) \
|
||||||
AST_KIND(BlockStmt, "block statement", struct { \
|
AST_KIND(BlockStmt, "block statement", struct { \
|
||||||
Array<Ast *> stmts; \
|
Slice<Ast *> stmts; \
|
||||||
Ast *label; \
|
Ast *label; \
|
||||||
Token open, close; \
|
Token open, close; \
|
||||||
}) \
|
}) \
|
||||||
@@ -388,7 +392,7 @@ AST_KIND(_ComplexStmtBegin, "", bool) \
|
|||||||
}) \
|
}) \
|
||||||
AST_KIND(ReturnStmt, "return statement", struct { \
|
AST_KIND(ReturnStmt, "return statement", struct { \
|
||||||
Token token; \
|
Token token; \
|
||||||
Array<Ast *> results; \
|
Slice<Ast *> results; \
|
||||||
}) \
|
}) \
|
||||||
AST_KIND(ForStmt, "for statement", struct { \
|
AST_KIND(ForStmt, "for statement", struct { \
|
||||||
Token token; \
|
Token token; \
|
||||||
@@ -418,8 +422,8 @@ AST_KIND(_ComplexStmtBegin, "", bool) \
|
|||||||
}) \
|
}) \
|
||||||
AST_KIND(CaseClause, "case clause", struct { \
|
AST_KIND(CaseClause, "case clause", struct { \
|
||||||
Token token; \
|
Token token; \
|
||||||
Array<Ast *> list; \
|
Slice<Ast *> list; \
|
||||||
Array<Ast *> stmts; \
|
Slice<Ast *> stmts; \
|
||||||
Entity *implicit_entity; \
|
Entity *implicit_entity; \
|
||||||
}) \
|
}) \
|
||||||
AST_KIND(SwitchStmt, "switch statement", struct { \
|
AST_KIND(SwitchStmt, "switch statement", struct { \
|
||||||
@@ -436,12 +440,12 @@ AST_KIND(_ComplexStmtBegin, "", bool) \
|
|||||||
Ast *tag; \
|
Ast *tag; \
|
||||||
Ast *body; \
|
Ast *body; \
|
||||||
bool partial; \
|
bool partial; \
|
||||||
}) \
|
}) \
|
||||||
AST_KIND(DeferStmt, "defer statement", struct { Token token; Ast *stmt; }) \
|
AST_KIND(DeferStmt, "defer statement", struct { Token token; Ast *stmt; }) \
|
||||||
AST_KIND(BranchStmt, "branch statement", struct { Token token; Ast *label; }) \
|
AST_KIND(BranchStmt, "branch statement", struct { Token token; Ast *label; }) \
|
||||||
AST_KIND(UsingStmt, "using statement", struct { \
|
AST_KIND(UsingStmt, "using statement", struct { \
|
||||||
Token token; \
|
Token token; \
|
||||||
Array<Ast *> list; \
|
Slice<Ast *> list; \
|
||||||
}) \
|
}) \
|
||||||
AST_KIND(_ComplexStmtEnd, "", bool) \
|
AST_KIND(_ComplexStmtEnd, "", bool) \
|
||||||
AST_KIND(_StmtEnd, "", bool) \
|
AST_KIND(_StmtEnd, "", bool) \
|
||||||
@@ -459,9 +463,9 @@ AST_KIND(_DeclBegin, "", bool) \
|
|||||||
Ast *name; \
|
Ast *name; \
|
||||||
}) \
|
}) \
|
||||||
AST_KIND(ValueDecl, "value declaration", struct { \
|
AST_KIND(ValueDecl, "value declaration", struct { \
|
||||||
Array<Ast *> names; \
|
Slice<Ast *> names; \
|
||||||
Ast * type; \
|
Ast * type; \
|
||||||
Array<Ast *> values; \
|
Slice<Ast *> values; \
|
||||||
Array<Ast *> attributes; \
|
Array<Ast *> attributes; \
|
||||||
CommentGroup *docs; \
|
CommentGroup *docs; \
|
||||||
CommentGroup *comment; \
|
CommentGroup *comment; \
|
||||||
@@ -486,10 +490,10 @@ AST_KIND(_DeclBegin, "", bool) \
|
|||||||
}) \
|
}) \
|
||||||
AST_KIND(ForeignImportDecl, "foreign import declaration", struct { \
|
AST_KIND(ForeignImportDecl, "foreign import declaration", struct { \
|
||||||
Token token; \
|
Token token; \
|
||||||
Array<Token> filepaths; \
|
Slice<Token> filepaths; \
|
||||||
Token library_name; \
|
Token library_name; \
|
||||||
String collection_name; \
|
String collection_name; \
|
||||||
Array<String> fullpaths; \
|
Slice<String> fullpaths; \
|
||||||
Array<Ast *> attributes; \
|
Array<Ast *> attributes; \
|
||||||
CommentGroup *docs; \
|
CommentGroup *docs; \
|
||||||
CommentGroup *comment; \
|
CommentGroup *comment; \
|
||||||
@@ -497,11 +501,11 @@ AST_KIND(_DeclBegin, "", bool) \
|
|||||||
AST_KIND(_DeclEnd, "", bool) \
|
AST_KIND(_DeclEnd, "", bool) \
|
||||||
AST_KIND(Attribute, "attribute", struct { \
|
AST_KIND(Attribute, "attribute", struct { \
|
||||||
Token token; \
|
Token token; \
|
||||||
Array<Ast *> elems; \
|
Slice<Ast *> elems; \
|
||||||
Token open, close; \
|
Token open, close; \
|
||||||
}) \
|
}) \
|
||||||
AST_KIND(Field, "field", struct { \
|
AST_KIND(Field, "field", struct { \
|
||||||
Array<Ast *> names; \
|
Slice<Ast *> names; \
|
||||||
Ast * type; \
|
Ast * type; \
|
||||||
Ast * default_value; \
|
Ast * default_value; \
|
||||||
Token tag; \
|
Token tag; \
|
||||||
@@ -511,7 +515,7 @@ AST_KIND(_DeclEnd, "", bool) \
|
|||||||
}) \
|
}) \
|
||||||
AST_KIND(FieldList, "field list", struct { \
|
AST_KIND(FieldList, "field list", struct { \
|
||||||
Token token; \
|
Token token; \
|
||||||
Array<Ast *> list; \
|
Slice<Ast *> list; \
|
||||||
}) \
|
}) \
|
||||||
AST_KIND(_TypeBegin, "", bool) \
|
AST_KIND(_TypeBegin, "", bool) \
|
||||||
AST_KIND(TypeidType, "typeid", struct { \
|
AST_KIND(TypeidType, "typeid", struct { \
|
||||||
@@ -565,34 +569,34 @@ AST_KIND(_TypeBegin, "", bool) \
|
|||||||
}) \
|
}) \
|
||||||
AST_KIND(StructType, "struct type", struct { \
|
AST_KIND(StructType, "struct type", struct { \
|
||||||
Token token; \
|
Token token; \
|
||||||
Array<Ast *> fields; \
|
Slice<Ast *> fields; \
|
||||||
isize field_count; \
|
isize field_count; \
|
||||||
Ast *polymorphic_params; \
|
Ast *polymorphic_params; \
|
||||||
Ast *align; \
|
Ast *align; \
|
||||||
Token where_token; \
|
Token where_token; \
|
||||||
Array<Ast *> where_clauses; \
|
Slice<Ast *> where_clauses; \
|
||||||
bool is_packed; \
|
bool is_packed; \
|
||||||
bool is_raw_union; \
|
bool is_raw_union; \
|
||||||
}) \
|
}) \
|
||||||
AST_KIND(UnionType, "union type", struct { \
|
AST_KIND(UnionType, "union type", struct { \
|
||||||
Token token; \
|
Token token; \
|
||||||
Array<Ast *> variants; \
|
Slice<Ast *> variants; \
|
||||||
Ast *polymorphic_params; \
|
Ast *polymorphic_params; \
|
||||||
Ast * align; \
|
Ast * align; \
|
||||||
bool maybe; \
|
bool maybe; \
|
||||||
bool no_nil; \
|
bool no_nil; \
|
||||||
Token where_token; \
|
Token where_token; \
|
||||||
Array<Ast *> where_clauses; \
|
Slice<Ast *> where_clauses; \
|
||||||
}) \
|
}) \
|
||||||
AST_KIND(EnumType, "enum type", struct { \
|
AST_KIND(EnumType, "enum type", struct { \
|
||||||
Token token; \
|
Token token; \
|
||||||
Ast * base_type; \
|
Ast * base_type; \
|
||||||
Array<Ast *> fields; /* FieldValue */ \
|
Slice<Ast *> fields; /* FieldValue */ \
|
||||||
bool is_using; \
|
bool is_using; \
|
||||||
}) \
|
}) \
|
||||||
AST_KIND(BitFieldType, "bit field type", struct { \
|
AST_KIND(BitFieldType, "bit field type", struct { \
|
||||||
Token token; \
|
Token token; \
|
||||||
Array<Ast *> fields; /* FieldValue with : */ \
|
Slice<Ast *> fields; /* FieldValue with : */ \
|
||||||
Ast * align; \
|
Ast * align; \
|
||||||
}) \
|
}) \
|
||||||
AST_KIND(BitSetType, "bit set type", struct { \
|
AST_KIND(BitSetType, "bit set type", struct { \
|
||||||
@@ -638,23 +642,22 @@ isize const ast_variant_sizes[] = {
|
|||||||
|
|
||||||
struct AstCommonStuff {
|
struct AstCommonStuff {
|
||||||
AstKind kind;
|
AstKind kind;
|
||||||
u32 state_flags;
|
u16 state_flags;
|
||||||
u32 viral_state_flags;
|
u16 viral_state_flags;
|
||||||
bool been_handled;
|
|
||||||
AstFile * file;
|
AstFile * file;
|
||||||
Scope * scope;
|
Scope * scope;
|
||||||
TypeAndValue tav;
|
TypeAndValue tav; // TODO(bill): Make this a pointer to minimize pointer size
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Ast {
|
struct Ast {
|
||||||
AstKind kind;
|
AstKind kind;
|
||||||
u32 state_flags;
|
u16 state_flags;
|
||||||
u32 viral_state_flags;
|
u16 viral_state_flags;
|
||||||
bool been_handled;
|
|
||||||
AstFile * file;
|
AstFile * file;
|
||||||
Scope * scope;
|
Scope * scope;
|
||||||
TypeAndValue tav;
|
TypeAndValue tav; // TODO(bill): Make this a pointer to minimize pointer size
|
||||||
|
|
||||||
|
// IMPORTANT NOTE(bill): This must be at the end since the AST is allocated to be size of the variant
|
||||||
union {
|
union {
|
||||||
#define AST_KIND(_kind_name_, name, ...) GB_JOIN2(Ast, _kind_name_) _kind_name_;
|
#define AST_KIND(_kind_name_, name, ...) GB_JOIN2(Ast, _kind_name_) _kind_name_;
|
||||||
AST_KINDS
|
AST_KINDS
|
||||||
|
|||||||
+91
-46
@@ -1,25 +1,30 @@
|
|||||||
|
typedef u32 PtrSetIndex;
|
||||||
|
|
||||||
struct PtrSetFindResult {
|
struct PtrSetFindResult {
|
||||||
isize hash_index;
|
PtrSetIndex hash_index;
|
||||||
isize entry_prev;
|
PtrSetIndex entry_prev;
|
||||||
isize entry_index;
|
PtrSetIndex entry_index;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum : PtrSetIndex { PTR_SET_SENTINEL = ~(PtrSetIndex)0 };
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct PtrSetEntry {
|
struct PtrSetEntry {
|
||||||
T ptr;
|
T ptr;
|
||||||
isize next;
|
PtrSetIndex next;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct PtrSet {
|
struct PtrSet {
|
||||||
Array<isize> hashes;
|
Array<PtrSetIndex> hashes;
|
||||||
Array<PtrSetEntry<T>> entries;
|
Array<PtrSetEntry<T>> entries;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T> void ptr_set_init (PtrSet<T> *s, gbAllocator a, isize capacity = 16);
|
template <typename T> void ptr_set_init (PtrSet<T> *s, gbAllocator a, isize capacity = 16);
|
||||||
template <typename T> void ptr_set_destroy(PtrSet<T> *s);
|
template <typename T> void ptr_set_destroy(PtrSet<T> *s);
|
||||||
template <typename T> T ptr_set_add (PtrSet<T> *s, T ptr);
|
template <typename T> T ptr_set_add (PtrSet<T> *s, T ptr);
|
||||||
|
template <typename T> bool ptr_set_update (PtrSet<T> *s, T ptr); // returns true if it previously existsed
|
||||||
template <typename T> bool ptr_set_exists (PtrSet<T> *s, T ptr);
|
template <typename T> bool ptr_set_exists (PtrSet<T> *s, T ptr);
|
||||||
template <typename T> void ptr_set_remove (PtrSet<T> *s, T ptr);
|
template <typename T> void ptr_set_remove (PtrSet<T> *s, T ptr);
|
||||||
template <typename T> void ptr_set_clear (PtrSet<T> *s);
|
template <typename T> void ptr_set_clear (PtrSet<T> *s);
|
||||||
@@ -27,12 +32,31 @@ template <typename T> void ptr_set_grow (PtrSet<T> *s);
|
|||||||
template <typename T> void ptr_set_rehash (PtrSet<T> *s, isize new_count);
|
template <typename T> void ptr_set_rehash (PtrSet<T> *s, isize new_count);
|
||||||
|
|
||||||
|
|
||||||
|
isize next_pow2_isize(isize n) {
|
||||||
|
if (n <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
n--;
|
||||||
|
n |= n >> 1;
|
||||||
|
n |= n >> 2;
|
||||||
|
n |= n >> 4;
|
||||||
|
n |= n >> 8;
|
||||||
|
n |= n >> 16;
|
||||||
|
if (gb_size_of(isize) == 8) {
|
||||||
|
n |= n >> 32;
|
||||||
|
}
|
||||||
|
n++;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void ptr_set_init(PtrSet<T> *s, gbAllocator a, isize capacity) {
|
void ptr_set_init(PtrSet<T> *s, gbAllocator a, isize capacity) {
|
||||||
|
capacity = next_pow2_isize(gb_max(16, capacity));
|
||||||
|
|
||||||
array_init(&s->hashes, a, capacity);
|
array_init(&s->hashes, a, capacity);
|
||||||
array_init(&s->entries, a, 0, capacity);
|
array_init(&s->entries, a, 0, capacity);
|
||||||
for (isize i = 0; i < capacity; i++) {
|
for (isize i = 0; i < capacity; i++) {
|
||||||
s->hashes.data[i] = -1;
|
s->hashes.data[i] = PTR_SET_SENTINEL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,72 +67,69 @@ void ptr_set_destroy(PtrSet<T> *s) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
gb_internal isize ptr_set__add_entry(PtrSet<T> *s, T ptr) {
|
gb_internal PtrSetIndex ptr_set__add_entry(PtrSet<T> *s, T ptr) {
|
||||||
PtrSetEntry<T> e = {};
|
PtrSetEntry<T> e = {};
|
||||||
e.ptr = ptr;
|
e.ptr = ptr;
|
||||||
e.next = -1;
|
e.next = PTR_SET_SENTINEL;
|
||||||
array_add(&s->entries, e);
|
array_add(&s->entries, e);
|
||||||
return s->entries.count-1;
|
return cast(PtrSetIndex)(s->entries.count-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
gb_internal PtrSetFindResult ptr_set__find(PtrSet<T> *s, T ptr) {
|
gb_internal PtrSetFindResult ptr_set__find(PtrSet<T> *s, T ptr) {
|
||||||
PtrSetFindResult fr = {-1, -1, -1};
|
PtrSetFindResult fr = {PTR_SET_SENTINEL, PTR_SET_SENTINEL, PTR_SET_SENTINEL};
|
||||||
if (s->hashes.count > 0) {
|
if (s->hashes.count != 0) {
|
||||||
u64 hash = 0xcbf29ce484222325ull ^ cast(u64)cast(uintptr)ptr;
|
u64 hash = 0xcbf29ce484222325ull ^ cast(u64)cast(uintptr)ptr;
|
||||||
u64 n = cast(u64)s->hashes.count;
|
u64 n = cast(u64)s->hashes.count;
|
||||||
fr.hash_index = cast(isize)(hash % n);
|
fr.hash_index = cast(PtrSetIndex)(hash & (n-1));
|
||||||
fr.entry_index = s->hashes[fr.hash_index];
|
fr.entry_index = s->hashes.data[fr.hash_index];
|
||||||
while (fr.entry_index >= 0) {
|
while (fr.entry_index != PTR_SET_SENTINEL) {
|
||||||
if (s->entries[fr.entry_index].ptr == ptr) {
|
if (s->entries.data[fr.entry_index].ptr == ptr) {
|
||||||
return fr;
|
return fr;
|
||||||
}
|
}
|
||||||
fr.entry_prev = fr.entry_index;
|
fr.entry_prev = fr.entry_index;
|
||||||
fr.entry_index = s->entries[fr.entry_index].next;
|
fr.entry_index = s->entries.data[fr.entry_index].next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fr;
|
return fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
gb_internal b32 ptr_set__full(PtrSet<T> *s) {
|
gb_internal bool ptr_set__full(PtrSet<T> *s) {
|
||||||
return 0.75f * s->hashes.count <= s->entries.count;
|
return 0.75f * s->hashes.count <= s->entries.count;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define PTR_ARRAY_GROW_FORMULA(x) (4*(x) + 7)
|
|
||||||
GB_STATIC_ASSERT(PTR_ARRAY_GROW_FORMULA(0) > 0);
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
gb_inline void ptr_set_grow(PtrSet<T> *s) {
|
gb_inline void ptr_set_grow(PtrSet<T> *s) {
|
||||||
isize new_count = PTR_ARRAY_GROW_FORMULA(s->entries.count);
|
isize new_count = s->hashes.count*2;
|
||||||
ptr_set_rehash(s, new_count);
|
ptr_set_rehash(s, new_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void ptr_set_rehash(PtrSet<T> *s, isize new_count) {
|
void ptr_set_rehash(PtrSet<T> *s, isize new_count) {
|
||||||
isize i, j;
|
PtrSetIndex i, j;
|
||||||
PtrSet<T> ns = {};
|
PtrSet<T> ns = {};
|
||||||
ptr_set_init(&ns, s->hashes.allocator);
|
ptr_set_init(&ns, s->hashes.allocator);
|
||||||
array_resize(&ns.hashes, new_count);
|
array_resize(&ns.hashes, new_count);
|
||||||
array_reserve(&ns.entries, s->entries.count);
|
array_reserve(&ns.entries, s->entries.count);
|
||||||
for (i = 0; i < new_count; i++) {
|
for (i = 0; i < new_count; i++) {
|
||||||
ns.hashes[i] = -1;
|
ns.hashes.data[i] = PTR_SET_SENTINEL;
|
||||||
}
|
}
|
||||||
for (i = 0; i < s->entries.count; i++) {
|
for (i = 0; i < s->entries.count; i++) {
|
||||||
PtrSetEntry<T> *e = &s->entries[i];
|
PtrSetEntry<T> *e = &s->entries.data[i];
|
||||||
PtrSetFindResult fr;
|
PtrSetFindResult fr;
|
||||||
if (ns.hashes.count == 0) {
|
if (ns.hashes.count == 0) {
|
||||||
ptr_set_grow(&ns);
|
ptr_set_grow(&ns);
|
||||||
}
|
}
|
||||||
fr = ptr_set__find(&ns, e->ptr);
|
fr = ptr_set__find(&ns, e->ptr);
|
||||||
j = ptr_set__add_entry(&ns, e->ptr);
|
j = ptr_set__add_entry(&ns, e->ptr);
|
||||||
if (fr.entry_prev < 0) {
|
if (fr.entry_prev == PTR_SET_SENTINEL) {
|
||||||
ns.hashes[fr.hash_index] = j;
|
ns.hashes.data[fr.hash_index] = j;
|
||||||
} else {
|
} else {
|
||||||
ns.entries[fr.entry_prev].next = j;
|
ns.entries.data[fr.entry_prev].next = j;
|
||||||
}
|
}
|
||||||
ns.entries[j].next = fr.entry_index;
|
ns.entries.data[j].next = fr.entry_index;
|
||||||
if (ptr_set__full(&ns)) {
|
if (ptr_set__full(&ns)) {
|
||||||
ptr_set_grow(&ns);
|
ptr_set_grow(&ns);
|
||||||
}
|
}
|
||||||
@@ -120,26 +141,24 @@ void ptr_set_rehash(PtrSet<T> *s, isize new_count) {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
gb_inline bool ptr_set_exists(PtrSet<T> *s, T ptr) {
|
gb_inline bool ptr_set_exists(PtrSet<T> *s, T ptr) {
|
||||||
isize index = ptr_set__find(s, ptr).entry_index;
|
isize index = ptr_set__find(s, ptr).entry_index;
|
||||||
return index >= 0;
|
return index != PTR_SET_SENTINEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if it already exists
|
// Returns true if it already exists
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T ptr_set_add(PtrSet<T> *s, T ptr) {
|
T ptr_set_add(PtrSet<T> *s, T ptr) {
|
||||||
isize index;
|
PtrSetIndex index;
|
||||||
PtrSetFindResult fr;
|
PtrSetFindResult fr;
|
||||||
if (s->hashes.count == 0) {
|
if (s->hashes.count == 0) {
|
||||||
ptr_set_grow(s);
|
ptr_set_grow(s);
|
||||||
}
|
}
|
||||||
fr = ptr_set__find(s, ptr);
|
fr = ptr_set__find(s, ptr);
|
||||||
if (fr.entry_index >= 0) {
|
if (fr.entry_index == PTR_SET_SENTINEL) {
|
||||||
index = fr.entry_index;
|
|
||||||
} else {
|
|
||||||
index = ptr_set__add_entry(s, ptr);
|
index = ptr_set__add_entry(s, ptr);
|
||||||
if (fr.entry_prev >= 0) {
|
if (fr.entry_prev != PTR_SET_SENTINEL) {
|
||||||
s->entries[fr.entry_prev].next = index;
|
s->entries.data[fr.entry_prev].next = index;
|
||||||
} else {
|
} else {
|
||||||
s->hashes[fr.hash_index] = index;
|
s->hashes.data[fr.hash_index] = index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ptr_set__full(s)) {
|
if (ptr_set__full(s)) {
|
||||||
@@ -148,32 +167,58 @@ T ptr_set_add(PtrSet<T> *s, T ptr) {
|
|||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool ptr_set_update(PtrSet<T> *s, T ptr) { // returns true if it previously existsed
|
||||||
|
bool exists = false;
|
||||||
|
PtrSetIndex index;
|
||||||
|
PtrSetFindResult fr;
|
||||||
|
if (s->hashes.count == 0) {
|
||||||
|
ptr_set_grow(s);
|
||||||
|
}
|
||||||
|
fr = ptr_set__find(s, ptr);
|
||||||
|
if (fr.entry_index != PTR_SET_SENTINEL) {
|
||||||
|
exists = true;
|
||||||
|
} else {
|
||||||
|
index = ptr_set__add_entry(s, ptr);
|
||||||
|
if (fr.entry_prev != PTR_SET_SENTINEL) {
|
||||||
|
s->entries.data[fr.entry_prev].next = index;
|
||||||
|
} else {
|
||||||
|
s->hashes.data[fr.hash_index] = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ptr_set__full(s)) {
|
||||||
|
ptr_set_grow(s);
|
||||||
|
}
|
||||||
|
return exists;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void ptr_set__erase(PtrSet<T> *s, PtrSetFindResult fr) {
|
void ptr_set__erase(PtrSet<T> *s, PtrSetFindResult fr) {
|
||||||
PtrSetFindResult last;
|
PtrSetFindResult last;
|
||||||
if (fr.entry_prev < 0) {
|
if (fr.entry_prev == PTR_SET_SENTINEL) {
|
||||||
s->hashes[fr.hash_index] = s->entries[fr.entry_index].next;
|
s->hashes.data[fr.hash_index] = s->entries.data[fr.entry_index].next;
|
||||||
} else {
|
} else {
|
||||||
s->entries[fr.entry_prev].next = s->entries[fr.entry_index].next;
|
s->entries.data[fr.entry_prev].next = s->entries.data[fr.entry_index].next;
|
||||||
}
|
}
|
||||||
if (fr.entry_index == s->entries.count-1) {
|
if (fr.entry_index == s->entries.count-1) {
|
||||||
array_pop(&s->entries);
|
array_pop(&s->entries);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
s->entries[fr.entry_index] = s->entries[s->entries.count-1];
|
s->entries.data[fr.entry_index] = s->entries.data[s->entries.count-1];
|
||||||
last = ptr_set__find(s, s->entries[fr.entry_index].ptr);
|
last = ptr_set__find(s, s->entries.data[fr.entry_index].ptr);
|
||||||
if (last.entry_prev >= 0) {
|
if (last.entry_prev != PTR_SET_SENTINEL) {
|
||||||
s->entries[last.entry_prev].next = fr.entry_index;
|
s->entries.data[last.entry_prev].next = fr.entry_index;
|
||||||
} else {
|
} else {
|
||||||
s->hashes[last.hash_index] = fr.entry_index;
|
s->hashes.data[last.hash_index] = fr.entry_index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void ptr_set_remove(PtrSet<T> *s, T ptr) {
|
void ptr_set_remove(PtrSet<T> *s, T ptr) {
|
||||||
PtrSetFindResult fr = ptr_set__find(s, ptr);
|
PtrSetFindResult fr = ptr_set__find(s, ptr);
|
||||||
if (fr.entry_index >= 0) {
|
if (fr.entry_index != PTR_SET_SENTINEL) {
|
||||||
ptr_set__erase(s, fr);
|
ptr_set__erase(s, fr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,6 +164,7 @@ int string_compare(String const &x, String const &y) {
|
|||||||
return cast(int)x[offset] - cast(int)y[offset];
|
return cast(int)x[offset] - cast(int)y[offset];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return cast(int)(x.len - y.len);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,8 +118,6 @@ TOKEN_KIND(Token__KeywordBegin, ""), \
|
|||||||
TOKEN_KIND(Token_no_inline, "no_inline"), \
|
TOKEN_KIND(Token_no_inline, "no_inline"), \
|
||||||
TOKEN_KIND(Token_context, "context"), \
|
TOKEN_KIND(Token_context, "context"), \
|
||||||
TOKEN_KIND(Token_asm, "asm"), \
|
TOKEN_KIND(Token_asm, "asm"), \
|
||||||
TOKEN_KIND(Token_macro, "macro"), \
|
|
||||||
TOKEN_KIND(Token_const, "const"), \
|
|
||||||
TOKEN_KIND(Token__KeywordEnd, ""), \
|
TOKEN_KIND(Token__KeywordEnd, ""), \
|
||||||
TOKEN_KIND(Token_Count, "")
|
TOKEN_KIND(Token_Count, "")
|
||||||
|
|
||||||
|
|||||||
+224
-68
@@ -323,6 +323,8 @@ String const type_strings[] = {
|
|||||||
enum TypeFlag : u32 {
|
enum TypeFlag : u32 {
|
||||||
TypeFlag_Polymorphic = 1<<1,
|
TypeFlag_Polymorphic = 1<<1,
|
||||||
TypeFlag_PolySpecialized = 1<<2,
|
TypeFlag_PolySpecialized = 1<<2,
|
||||||
|
TypeFlag_InProcessOfCheckingPolymorphic = 1<<3,
|
||||||
|
TypeFlag_InProcessOfCheckingABI = 1<<4,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Type {
|
struct Type {
|
||||||
@@ -371,7 +373,28 @@ enum Typeid_Kind : u8 {
|
|||||||
Typeid_Relative_Slice,
|
Typeid_Relative_Slice,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// IMPORTANT NOTE(bill): This must match the same as the in core.odin
|
||||||
|
enum TypeInfoFlag : u32 {
|
||||||
|
TypeInfoFlag_Comparable = 1<<0,
|
||||||
|
TypeInfoFlag_Simple_Compare = 1<<1,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool is_type_comparable(Type *t);
|
||||||
|
bool is_type_simple_compare(Type *t);
|
||||||
|
|
||||||
|
u32 type_info_flags_of_type(Type *type) {
|
||||||
|
if (type == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
u32 flags = 0;
|
||||||
|
if (is_type_comparable(type)) {
|
||||||
|
flags |= TypeInfoFlag_Comparable;
|
||||||
|
}
|
||||||
|
if (is_type_simple_compare(type)) {
|
||||||
|
flags |= TypeInfoFlag_Comparable;
|
||||||
|
}
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO(bill): Should I add extra information here specifying the kind of selection?
|
// TODO(bill): Should I add extra information here specifying the kind of selection?
|
||||||
@@ -661,12 +684,15 @@ gb_global Type *t_context_ptr = nullptr;
|
|||||||
gb_global Type *t_source_code_location = nullptr;
|
gb_global Type *t_source_code_location = nullptr;
|
||||||
gb_global Type *t_source_code_location_ptr = nullptr;
|
gb_global Type *t_source_code_location_ptr = nullptr;
|
||||||
|
|
||||||
gb_global Type *t_map_key = nullptr;
|
gb_global Type *t_map_hash = nullptr;
|
||||||
gb_global Type *t_map_header = nullptr;
|
gb_global Type *t_map_header = nullptr;
|
||||||
|
|
||||||
gb_global Type *t_vector_x86_mmx = nullptr;
|
gb_global Type *t_vector_x86_mmx = nullptr;
|
||||||
|
|
||||||
|
|
||||||
|
gb_global Type *t_equal_proc = nullptr;
|
||||||
|
gb_global Type *t_hasher_proc = nullptr;
|
||||||
|
|
||||||
|
|
||||||
i64 type_size_of (Type *t);
|
i64 type_size_of (Type *t);
|
||||||
i64 type_align_of (Type *t);
|
i64 type_align_of (Type *t);
|
||||||
@@ -769,7 +795,8 @@ void set_base_type(Type *t, Type *base) {
|
|||||||
|
|
||||||
|
|
||||||
Type *alloc_type(TypeKind kind) {
|
Type *alloc_type(TypeKind kind) {
|
||||||
gbAllocator a = heap_allocator();
|
// gbAllocator a = heap_allocator();
|
||||||
|
gbAllocator a = permanent_allocator();
|
||||||
Type *t = gb_alloc_item(a, Type);
|
Type *t = gb_alloc_item(a, Type);
|
||||||
zero_item(t);
|
zero_item(t);
|
||||||
t->kind = kind;
|
t->kind = kind;
|
||||||
@@ -884,6 +911,25 @@ Type *alloc_type_named(String name, Type *base, Entity *type_name) {
|
|||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_calling_convention_none(ProcCallingConvention calling_convention) {
|
||||||
|
switch (calling_convention) {
|
||||||
|
case ProcCC_None:
|
||||||
|
case ProcCC_PureNone:
|
||||||
|
case ProcCC_InlineAsm:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_calling_convention_odin(ProcCallingConvention calling_convention) {
|
||||||
|
switch (calling_convention) {
|
||||||
|
case ProcCC_Odin:
|
||||||
|
case ProcCC_Contextless:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Type *alloc_type_tuple() {
|
Type *alloc_type_tuple() {
|
||||||
Type *t = alloc_type(Type_Tuple);
|
Type *t = alloc_type(Type_Tuple);
|
||||||
return t;
|
return t;
|
||||||
@@ -918,7 +964,6 @@ bool is_type_valid_for_keys(Type *t);
|
|||||||
|
|
||||||
Type *alloc_type_map(i64 count, Type *key, Type *value) {
|
Type *alloc_type_map(i64 count, Type *key, Type *value) {
|
||||||
if (key != nullptr) {
|
if (key != nullptr) {
|
||||||
GB_ASSERT(is_type_valid_for_keys(key));
|
|
||||||
GB_ASSERT(value != nullptr);
|
GB_ASSERT(value != nullptr);
|
||||||
}
|
}
|
||||||
Type *t = alloc_type(Type_Map);
|
Type *t = alloc_type(Type_Map);
|
||||||
@@ -1192,20 +1237,6 @@ bool is_type_slice(Type *t) {
|
|||||||
t = base_type(t);
|
t = base_type(t);
|
||||||
return t->kind == Type_Slice;
|
return t->kind == Type_Slice;
|
||||||
}
|
}
|
||||||
bool is_type_u8_slice(Type *t) {
|
|
||||||
t = base_type(t);
|
|
||||||
if (t->kind == Type_Slice) {
|
|
||||||
return is_type_u8(t->Slice.elem);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool is_type_u8_ptr(Type *t) {
|
|
||||||
t = base_type(t);
|
|
||||||
if (t->kind == Type_Pointer) {
|
|
||||||
return is_type_u8(t->Slice.elem);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool is_type_proc(Type *t) {
|
bool is_type_proc(Type *t) {
|
||||||
t = base_type(t);
|
t = base_type(t);
|
||||||
return t->kind == Type_Proc;
|
return t->kind == Type_Proc;
|
||||||
@@ -1249,6 +1280,37 @@ bool is_type_relative_slice(Type *t) {
|
|||||||
return t->kind == Type_RelativeSlice;
|
return t->kind == Type_RelativeSlice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_type_u8_slice(Type *t) {
|
||||||
|
t = base_type(t);
|
||||||
|
if (t->kind == Type_Slice) {
|
||||||
|
return is_type_u8(t->Slice.elem);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool is_type_u8_array(Type *t) {
|
||||||
|
t = base_type(t);
|
||||||
|
if (t->kind == Type_Array) {
|
||||||
|
return is_type_u8(t->Array.elem);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool is_type_u8_ptr(Type *t) {
|
||||||
|
t = base_type(t);
|
||||||
|
if (t->kind == Type_Pointer) {
|
||||||
|
return is_type_u8(t->Slice.elem);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool is_type_rune_array(Type *t) {
|
||||||
|
t = base_type(t);
|
||||||
|
if (t->kind == Type_Array) {
|
||||||
|
return is_type_rune(t->Array.elem);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Type *core_array_type(Type *t) {
|
Type *core_array_type(Type *t) {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
@@ -1261,53 +1323,7 @@ Type *core_array_type(Type *t) {
|
|||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE(bill): type can be easily compared using memcmp
|
|
||||||
bool is_type_simple_compare(Type *t) {
|
|
||||||
t = core_type(t);
|
|
||||||
switch (t->kind) {
|
|
||||||
case Type_Array:
|
|
||||||
return is_type_simple_compare(t->Array.elem);
|
|
||||||
|
|
||||||
case Type_EnumeratedArray:
|
|
||||||
return is_type_simple_compare(t->EnumeratedArray.elem);
|
|
||||||
|
|
||||||
case Type_Basic:
|
|
||||||
if (t->Basic.flags & BasicFlag_SimpleCompare) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
|
|
||||||
case Type_Pointer:
|
|
||||||
case Type_Proc:
|
|
||||||
case Type_BitSet:
|
|
||||||
case Type_BitField:
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case Type_Struct:
|
|
||||||
for_array(i, t->Struct.fields) {
|
|
||||||
Entity *f = t->Struct.fields[i];
|
|
||||||
if (!is_type_simple_compare(f->type)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case Type_Union:
|
|
||||||
for_array(i, t->Union.variants) {
|
|
||||||
Type *v = t->Union.variants[i];
|
|
||||||
if (!is_type_simple_compare(v)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case Type_SimdVector:
|
|
||||||
return is_type_simple_compare(t->SimdVector.elem);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Type *base_complex_elem_type(Type *t) {
|
Type *base_complex_elem_type(Type *t) {
|
||||||
t = core_type(t);
|
t = core_type(t);
|
||||||
@@ -1526,6 +1542,8 @@ bool is_type_valid_for_keys(Type *t) {
|
|||||||
if (is_type_untyped(t)) {
|
if (is_type_untyped(t)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return is_type_comparable(t);
|
||||||
|
#if 0
|
||||||
if (is_type_integer(t)) {
|
if (is_type_integer(t)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1541,8 +1559,15 @@ bool is_type_valid_for_keys(Type *t) {
|
|||||||
if (is_type_typeid(t)) {
|
if (is_type_typeid(t)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (is_type_simple_compare(t)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (is_type_comparable(t)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_type_valid_bit_set_elem(Type *t) {
|
bool is_type_valid_bit_set_elem(Type *t) {
|
||||||
@@ -1695,12 +1720,23 @@ TypeTuple *get_record_polymorphic_params(Type *t) {
|
|||||||
|
|
||||||
|
|
||||||
bool is_type_polymorphic(Type *t, bool or_specialized=false) {
|
bool is_type_polymorphic(Type *t, bool or_specialized=false) {
|
||||||
|
if (t->flags & TypeFlag_InProcessOfCheckingPolymorphic) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
switch (t->kind) {
|
switch (t->kind) {
|
||||||
case Type_Generic:
|
case Type_Generic:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case Type_Named:
|
case Type_Named:
|
||||||
return is_type_polymorphic(t->Named.base, or_specialized);
|
{
|
||||||
|
u32 flags = t->flags;
|
||||||
|
t->flags |= TypeFlag_InProcessOfCheckingPolymorphic;
|
||||||
|
bool ok = is_type_polymorphic(t->Named.base, or_specialized);
|
||||||
|
t->flags = flags;
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
case Type_Opaque:
|
case Type_Opaque:
|
||||||
return is_type_polymorphic(t->Opaque.elem, or_specialized);
|
return is_type_polymorphic(t->Opaque.elem, or_specialized);
|
||||||
case Type_Pointer:
|
case Type_Pointer:
|
||||||
@@ -1892,10 +1928,77 @@ bool is_type_comparable(Type *t) {
|
|||||||
|
|
||||||
case Type_Opaque:
|
case Type_Opaque:
|
||||||
return is_type_comparable(t->Opaque.elem);
|
return is_type_comparable(t->Opaque.elem);
|
||||||
|
|
||||||
|
case Type_Struct:
|
||||||
|
if (type_size_of(t) == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (t->Struct.is_raw_union) {
|
||||||
|
return is_type_simple_compare(t);
|
||||||
|
}
|
||||||
|
for_array(i, t->Struct.fields) {
|
||||||
|
Entity *f = t->Struct.fields[i];
|
||||||
|
if (!is_type_comparable(f->type)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE(bill): type can be easily compared using memcmp
|
||||||
|
bool is_type_simple_compare(Type *t) {
|
||||||
|
t = core_type(t);
|
||||||
|
switch (t->kind) {
|
||||||
|
case Type_Array:
|
||||||
|
return is_type_simple_compare(t->Array.elem);
|
||||||
|
|
||||||
|
case Type_EnumeratedArray:
|
||||||
|
return is_type_simple_compare(t->EnumeratedArray.elem);
|
||||||
|
|
||||||
|
case Type_Basic:
|
||||||
|
if (t->Basic.flags & BasicFlag_SimpleCompare) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (t->Basic.kind == Basic_typeid) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case Type_Pointer:
|
||||||
|
case Type_Proc:
|
||||||
|
case Type_BitSet:
|
||||||
|
case Type_BitField:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case Type_Struct:
|
||||||
|
for_array(i, t->Struct.fields) {
|
||||||
|
Entity *f = t->Struct.fields[i];
|
||||||
|
if (!is_type_simple_compare(f->type)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case Type_Union:
|
||||||
|
for_array(i, t->Union.variants) {
|
||||||
|
Type *v = t->Union.variants[i];
|
||||||
|
if (!is_type_simple_compare(v)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case Type_SimdVector:
|
||||||
|
return is_type_simple_compare(t->SimdVector.elem);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Type *strip_type_aliasing(Type *x) {
|
Type *strip_type_aliasing(Type *x) {
|
||||||
if (x == nullptr) {
|
if (x == nullptr) {
|
||||||
return x;
|
return x;
|
||||||
@@ -2317,7 +2420,7 @@ Selection lookup_field_from_index(Type *type, i64 index) {
|
|||||||
GB_ASSERT(is_type_struct(type) || is_type_union(type) || is_type_tuple(type));
|
GB_ASSERT(is_type_struct(type) || is_type_union(type) || is_type_tuple(type));
|
||||||
type = base_type(type);
|
type = base_type(type);
|
||||||
|
|
||||||
gbAllocator a = heap_allocator();
|
gbAllocator a = permanent_allocator();
|
||||||
isize max_count = 0;
|
isize max_count = 0;
|
||||||
switch (type->kind) {
|
switch (type->kind) {
|
||||||
case Type_Struct: max_count = type->Struct.fields.count; break;
|
case Type_Struct: max_count = type->Struct.fields.count; break;
|
||||||
@@ -2365,7 +2468,6 @@ Selection lookup_field_from_index(Type *type, i64 index) {
|
|||||||
return empty_selection;
|
return empty_selection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Entity *scope_lookup_current(Scope *s, String const &name);
|
Entity *scope_lookup_current(Scope *s, String const &name);
|
||||||
|
|
||||||
Selection lookup_field_with_selection(Type *type_, String field_name, bool is_type, Selection sel, bool allow_blank_ident) {
|
Selection lookup_field_with_selection(Type *type_, String field_name, bool is_type, Selection sel, bool allow_blank_ident) {
|
||||||
@@ -2375,7 +2477,6 @@ Selection lookup_field_with_selection(Type *type_, String field_name, bool is_ty
|
|||||||
return empty_selection;
|
return empty_selection;
|
||||||
}
|
}
|
||||||
|
|
||||||
gbAllocator a = heap_allocator();
|
|
||||||
Type *type = type_deref(type_);
|
Type *type = type_deref(type_);
|
||||||
bool is_ptr = type != type_;
|
bool is_ptr = type != type_;
|
||||||
sel.indirect = sel.indirect || is_ptr;
|
sel.indirect = sel.indirect || is_ptr;
|
||||||
@@ -2964,7 +3065,7 @@ i64 type_align_of_internal(Type *t, TypePath *path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Array<i64> type_set_offsets_of(Array<Entity *> const &fields, bool is_packed, bool is_raw_union) {
|
Array<i64> type_set_offsets_of(Array<Entity *> const &fields, bool is_packed, bool is_raw_union) {
|
||||||
gbAllocator a = heap_allocator();
|
gbAllocator a = permanent_allocator();
|
||||||
auto offsets = array_make<i64>(a, fields.count);
|
auto offsets = array_make<i64>(a, fields.count);
|
||||||
i64 curr_offset = 0;
|
i64 curr_offset = 0;
|
||||||
if (is_raw_union) {
|
if (is_raw_union) {
|
||||||
@@ -3353,6 +3454,58 @@ Type *reduce_tuple_to_single_type(Type *original_type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Type *alloc_type_struct_from_field_types(Type **field_types, isize field_count, bool is_packed) {
|
||||||
|
Type *t = alloc_type_struct();
|
||||||
|
t->Struct.fields = array_make<Entity *>(heap_allocator(), field_count);
|
||||||
|
|
||||||
|
Scope *scope = nullptr;
|
||||||
|
for_array(i, t->Struct.fields) {
|
||||||
|
t->Struct.fields[i] = alloc_entity_field(scope, blank_token, field_types[i], false, cast(i32)i, EntityState_Resolved);
|
||||||
|
}
|
||||||
|
t->Struct.is_packed = is_packed;
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
Type *alloc_type_tuple_from_field_types(Type **field_types, isize field_count, bool is_packed, bool must_be_tuple) {
|
||||||
|
if (field_count == 0) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (!must_be_tuple && field_count == 1) {
|
||||||
|
return field_types[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
Type *t = alloc_type_tuple();
|
||||||
|
t->Tuple.variables = array_make<Entity *>(heap_allocator(), field_count);
|
||||||
|
|
||||||
|
Scope *scope = nullptr;
|
||||||
|
for_array(i, t->Tuple.variables) {
|
||||||
|
t->Tuple.variables[i] = alloc_entity_param(scope, blank_token, field_types[i], false, false);
|
||||||
|
}
|
||||||
|
t->Tuple.is_packed = is_packed;
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
Type *alloc_type_proc_from_types(Type **param_types, unsigned param_count, Type *results, bool is_c_vararg, ProcCallingConvention calling_convention) {
|
||||||
|
|
||||||
|
Type *params = alloc_type_tuple_from_field_types(param_types, param_count, false, true);
|
||||||
|
isize results_count = 0;
|
||||||
|
if (results != nullptr) {
|
||||||
|
if (results->kind != Type_Tuple) {
|
||||||
|
results = alloc_type_tuple_from_field_types(&results, 1, false, true);
|
||||||
|
}
|
||||||
|
results_count = results->Tuple.variables.count;
|
||||||
|
}
|
||||||
|
|
||||||
|
Scope *scope = nullptr;
|
||||||
|
Type *t = alloc_type_proc(scope, params, param_count, results, results_count, false, calling_convention);
|
||||||
|
t->Proc.c_vararg = is_c_vararg;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
gbString write_type_to_string(gbString str, Type *type) {
|
gbString write_type_to_string(gbString str, Type *type) {
|
||||||
if (type == nullptr) {
|
if (type == nullptr) {
|
||||||
return gb_string_appendc(str, "<no type>");
|
return gb_string_appendc(str, "<no type>");
|
||||||
@@ -3671,3 +3824,6 @@ gbString type_to_string(Type *type) {
|
|||||||
return write_type_to_string(gb_string_make(heap_allocator(), ""), type);
|
return write_type_to_string(gb_string_make(heap_allocator(), ""), type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user