mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-05 15:18:49 +00:00
Merge remote-tracking branch 'origin' into wsapoll
This commit is contained in:
@@ -163,6 +163,13 @@ jobs:
|
|||||||
cd tests\internal
|
cd tests\internal
|
||||||
call build.bat
|
call build.bat
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
|
- name: Odin documentation tests
|
||||||
|
shell: cmd
|
||||||
|
run: |
|
||||||
|
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||||
|
cd tests\documentation
|
||||||
|
call build.bat
|
||||||
|
timeout-minutes: 10
|
||||||
- name: core:math/big tests
|
- name: core:math/big tests
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ package util
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
// Keep vet happy
|
||||||
|
_ :: mem
|
||||||
|
|
||||||
// @note(bp): this can replace the other two
|
// @note(bp): this can replace the other two
|
||||||
cast_slice :: #force_inline proc "contextless" ($D: typeid/[]$DE, src: $S/[]$SE) -> D {
|
cast_slice :: #force_inline proc "contextless" ($D: typeid/[]$DE, src: $S/[]$SE) -> D {
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ Ascii_Set :: distinct [8]u32
|
|||||||
/*
|
/*
|
||||||
Creates an Ascii_Set with unique characters from the input string.
|
Creates an Ascii_Set with unique characters from the input string.
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- chars: A string containing characters to include in the Ascii_Set.
|
- chars: A string containing characters to include in the Ascii_Set.
|
||||||
|
|
||||||
**Returns**
|
Returns:
|
||||||
- as: An Ascii_Set with unique characters from the input string.
|
- as: An Ascii_Set with unique characters from the input string.
|
||||||
- ok: false if any character in the input string is not a valid ASCII character.
|
- ok: false if any character in the input string is not a valid ASCII character.
|
||||||
*/
|
*/
|
||||||
@@ -33,11 +33,12 @@ ascii_set_make :: proc(chars: string) -> (as: Ascii_Set, ok: bool) #no_bounds_ch
|
|||||||
/*
|
/*
|
||||||
Determines if a given char is contained within an Ascii_Set.
|
Determines if a given char is contained within an Ascii_Set.
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- as: The Ascii_Set to search.
|
- as: The Ascii_Set to search.
|
||||||
- c: The char to check for in the Ascii_Set.
|
- c: The char to check for in the Ascii_Set.
|
||||||
|
|
||||||
**Returns** A boolean indicating if the byte is contained in the Ascii_Set (true) or not (false).
|
Returns:
|
||||||
|
A boolean indicating if the byte is contained in the Ascii_Set (true) or not (false).
|
||||||
*/
|
*/
|
||||||
ascii_set_contains :: proc(as: Ascii_Set, c: byte) -> bool #no_bounds_check {
|
ascii_set_contains :: proc(as: Ascii_Set, c: byte) -> bool #no_bounds_check {
|
||||||
return as[c>>5] & (1<<(c&31)) != 0
|
return as[c>>5] & (1<<(c&31)) != 0
|
||||||
|
|||||||
+133
-91
@@ -7,10 +7,11 @@ import "core:io"
|
|||||||
/*
|
/*
|
||||||
Type definition for a procedure that flushes a Builder
|
Type definition for a procedure that flushes a Builder
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- b: A pointer to the Builder
|
- b: A pointer to the Builder
|
||||||
|
|
||||||
**Returns** A boolean indicating whether the Builder should be reset
|
Returns:
|
||||||
|
A boolean indicating whether the Builder should be reset
|
||||||
*/
|
*/
|
||||||
Builder_Flush_Proc :: #type proc(b: ^Builder) -> (do_reset: bool)
|
Builder_Flush_Proc :: #type proc(b: ^Builder) -> (do_reset: bool)
|
||||||
/*
|
/*
|
||||||
@@ -26,10 +27,11 @@ Produces a Builder with a default length of 0 and cap of 16
|
|||||||
|
|
||||||
*Allocates Using Provided Allocator*
|
*Allocates Using Provided Allocator*
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- allocator: (default is context.allocator)
|
- allocator: (default is context.allocator)
|
||||||
|
|
||||||
**Returns** A new Builder
|
Returns:
|
||||||
|
A new Builder
|
||||||
*/
|
*/
|
||||||
builder_make_none :: proc(allocator := context.allocator) -> Builder {
|
builder_make_none :: proc(allocator := context.allocator) -> Builder {
|
||||||
return Builder{buf=make([dynamic]byte, allocator)}
|
return Builder{buf=make([dynamic]byte, allocator)}
|
||||||
@@ -39,11 +41,12 @@ Produces a Builder with a specified length and cap of max(16,len) byte buffer
|
|||||||
|
|
||||||
*Allocates Using Provided Allocator*
|
*Allocates Using Provided Allocator*
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- len: The desired length of the Builder's buffer
|
- len: The desired length of the Builder's buffer
|
||||||
- allocator: (default is context.allocator)
|
- allocator: (default is context.allocator)
|
||||||
|
|
||||||
**Returns** A new Builder
|
Returns:
|
||||||
|
A new Builder
|
||||||
*/
|
*/
|
||||||
builder_make_len :: proc(len: int, allocator := context.allocator) -> Builder {
|
builder_make_len :: proc(len: int, allocator := context.allocator) -> Builder {
|
||||||
return Builder{buf=make([dynamic]byte, len, allocator)}
|
return Builder{buf=make([dynamic]byte, len, allocator)}
|
||||||
@@ -53,12 +56,13 @@ Produces a Builder with a specified length and cap
|
|||||||
|
|
||||||
*Allocates Using Provided Allocator*
|
*Allocates Using Provided Allocator*
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- len: The desired length of the Builder's buffer
|
- len: The desired length of the Builder's buffer
|
||||||
- cap: The desired capacity of the Builder's buffer, cap is max(cap, len)
|
- cap: The desired capacity of the Builder's buffer, cap is max(cap, len)
|
||||||
- allocator: (default is context.allocator)
|
- allocator: (default is context.allocator)
|
||||||
|
|
||||||
**Returns** A new Builder
|
Returns:
|
||||||
|
A new Builder
|
||||||
*/
|
*/
|
||||||
builder_make_len_cap :: proc(len, cap: int, allocator := context.allocator) -> Builder {
|
builder_make_len_cap :: proc(len, cap: int, allocator := context.allocator) -> Builder {
|
||||||
return Builder{buf=make([dynamic]byte, len, cap, allocator)}
|
return Builder{buf=make([dynamic]byte, len, cap, allocator)}
|
||||||
@@ -75,11 +79,12 @@ It replaces the existing `buf`
|
|||||||
|
|
||||||
*Allocates Using Provided Allocator*
|
*Allocates Using Provided Allocator*
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- b: A pointer to the Builder
|
- b: A pointer to the Builder
|
||||||
- allocator: (default is context.allocator)
|
- allocator: (default is context.allocator)
|
||||||
|
|
||||||
**Returns** initialized ^Builder
|
Returns:
|
||||||
|
initialized ^Builder
|
||||||
*/
|
*/
|
||||||
builder_init_none :: proc(b: ^Builder, allocator := context.allocator) -> ^Builder {
|
builder_init_none :: proc(b: ^Builder, allocator := context.allocator) -> ^Builder {
|
||||||
b.buf = make([dynamic]byte, allocator)
|
b.buf = make([dynamic]byte, allocator)
|
||||||
@@ -91,12 +96,13 @@ It replaces the existing `buf`
|
|||||||
|
|
||||||
*Allocates Using Provided Allocator*
|
*Allocates Using Provided Allocator*
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- b: A pointer to the Builder
|
- b: A pointer to the Builder
|
||||||
- len: The desired length of the Builder's buffer
|
- len: The desired length of the Builder's buffer
|
||||||
- allocator: (default is context.allocator)
|
- allocator: (default is context.allocator)
|
||||||
|
|
||||||
**Returns** Initialized ^Builder
|
Returns:
|
||||||
|
Initialized ^Builder
|
||||||
*/
|
*/
|
||||||
builder_init_len :: proc(b: ^Builder, len: int, allocator := context.allocator) -> ^Builder {
|
builder_init_len :: proc(b: ^Builder, len: int, allocator := context.allocator) -> ^Builder {
|
||||||
b.buf = make([dynamic]byte, len, allocator)
|
b.buf = make([dynamic]byte, len, allocator)
|
||||||
@@ -106,13 +112,14 @@ builder_init_len :: proc(b: ^Builder, len: int, allocator := context.allocator)
|
|||||||
Initializes a Builder with a specified length and cap
|
Initializes a Builder with a specified length and cap
|
||||||
It replaces the existing `buf`
|
It replaces the existing `buf`
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- b: A pointer to the Builder
|
- b: A pointer to the Builder
|
||||||
- len: The desired length of the Builder's buffer
|
- len: The desired length of the Builder's buffer
|
||||||
- cap: The desired capacity of the Builder's buffer, actual max(len,cap)
|
- cap: The desired capacity of the Builder's buffer, actual max(len,cap)
|
||||||
- allocator: (default is context.allocator)
|
- allocator: (default is context.allocator)
|
||||||
|
|
||||||
**Returns** A pointer to the initialized Builder
|
Returns:
|
||||||
|
A pointer to the initialized Builder
|
||||||
*/
|
*/
|
||||||
builder_init_len_cap :: proc(b: ^Builder, len, cap: int, allocator := context.allocator) -> ^Builder {
|
builder_init_len_cap :: proc(b: ^Builder, len, cap: int, allocator := context.allocator) -> ^Builder {
|
||||||
b.buf = make([dynamic]byte, len, cap, allocator)
|
b.buf = make([dynamic]byte, len, cap, allocator)
|
||||||
@@ -158,10 +165,11 @@ _builder_stream_vtable := &_builder_stream_vtable_obj
|
|||||||
/*
|
/*
|
||||||
Returns an io.Stream from a Builder
|
Returns an io.Stream from a Builder
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- b: A pointer to the Builder
|
- b: A pointer to the Builder
|
||||||
|
|
||||||
**Returns** An io.Stream
|
Returns:
|
||||||
|
An io.Stream
|
||||||
*/
|
*/
|
||||||
to_stream :: proc(b: ^Builder) -> io.Stream {
|
to_stream :: proc(b: ^Builder) -> io.Stream {
|
||||||
return io.Stream{stream_vtable=_builder_stream_vtable, stream_data=b}
|
return io.Stream{stream_vtable=_builder_stream_vtable, stream_data=b}
|
||||||
@@ -169,10 +177,11 @@ to_stream :: proc(b: ^Builder) -> io.Stream {
|
|||||||
/*
|
/*
|
||||||
Returns an io.Writer from a Builder
|
Returns an io.Writer from a Builder
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- b: A pointer to the Builder
|
- b: A pointer to the Builder
|
||||||
|
|
||||||
**Returns** An io.Writer
|
Returns:
|
||||||
|
An io.Writer
|
||||||
*/
|
*/
|
||||||
to_writer :: proc(b: ^Builder) -> io.Writer {
|
to_writer :: proc(b: ^Builder) -> io.Writer {
|
||||||
return io.to_writer(to_stream(b))
|
return io.to_writer(to_stream(b))
|
||||||
@@ -180,7 +189,7 @@ to_writer :: proc(b: ^Builder) -> io.Writer {
|
|||||||
/*
|
/*
|
||||||
Deletes the Builder byte buffer content
|
Deletes the Builder byte buffer content
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- b: A pointer to the Builder
|
- b: A pointer to the Builder
|
||||||
*/
|
*/
|
||||||
builder_destroy :: proc(b: ^Builder) {
|
builder_destroy :: proc(b: ^Builder) {
|
||||||
@@ -190,7 +199,7 @@ builder_destroy :: proc(b: ^Builder) {
|
|||||||
/*
|
/*
|
||||||
Reserves the Builder byte buffer to a specific capacity, when it's higher than before
|
Reserves the Builder byte buffer to a specific capacity, when it's higher than before
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- b: A pointer to the Builder
|
- b: A pointer to the Builder
|
||||||
- cap: The desired capacity for the Builder's buffer
|
- cap: The desired capacity for the Builder's buffer
|
||||||
*/
|
*/
|
||||||
@@ -200,7 +209,7 @@ builder_grow :: proc(b: ^Builder, cap: int) {
|
|||||||
/*
|
/*
|
||||||
Clears the Builder byte buffer content (sets len to zero)
|
Clears the Builder byte buffer content (sets len to zero)
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- b: A pointer to the Builder
|
- b: A pointer to the Builder
|
||||||
*/
|
*/
|
||||||
builder_reset :: proc(b: ^Builder) {
|
builder_reset :: proc(b: ^Builder) {
|
||||||
@@ -211,18 +220,23 @@ Creates a Builder from a slice of bytes with the same slice length as its capaci
|
|||||||
|
|
||||||
*Uses Nil Allocator - Does NOT allocate*
|
*Uses Nil Allocator - Does NOT allocate*
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- backing: A slice of bytes to be used as the backing buffer
|
- backing: A slice of bytes to be used as the backing buffer
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A new Builder
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
strings_builder_from_bytes_example :: proc() {
|
builder_from_bytes_example :: proc() {
|
||||||
bytes: [8]byte // <-- gets filled
|
bytes: [8]byte // <-- gets filled
|
||||||
builder := strings.builder_from_bytes(bytes[:])
|
builder := strings.builder_from_bytes(bytes[:])
|
||||||
fmt.println(strings.write_byte(&builder, 'a')) // -> "a"
|
strings.write_byte(&builder, 'a')
|
||||||
fmt.println(strings.write_byte(&builder, 'b')) // -> "ab"
|
fmt.println(strings.to_string(builder)) // -> "a"
|
||||||
|
strings.write_byte(&builder, 'b')
|
||||||
|
fmt.println(strings.to_string(builder)) // -> "ab"
|
||||||
}
|
}
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
@@ -230,7 +244,6 @@ Output:
|
|||||||
a
|
a
|
||||||
ab
|
ab
|
||||||
|
|
||||||
**Returns** A new Builder
|
|
||||||
*/
|
*/
|
||||||
builder_from_bytes :: proc(backing: []byte) -> Builder {
|
builder_from_bytes :: proc(backing: []byte) -> Builder {
|
||||||
s := transmute(runtime.Raw_Slice)backing
|
s := transmute(runtime.Raw_Slice)backing
|
||||||
@@ -249,10 +262,11 @@ builder_from_slice :: builder_from_bytes
|
|||||||
/*
|
/*
|
||||||
Casts the Builder byte buffer to a string and returns it
|
Casts the Builder byte buffer to a string and returns it
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- b: A Builder
|
- b: A Builder
|
||||||
|
|
||||||
**Returns** The contents of the Builder's buffer, as a string
|
Returns:
|
||||||
|
The contents of the Builder's buffer, as a string
|
||||||
*/
|
*/
|
||||||
to_string :: proc(b: Builder) -> string {
|
to_string :: proc(b: Builder) -> string {
|
||||||
return string(b.buf[:])
|
return string(b.buf[:])
|
||||||
@@ -260,10 +274,11 @@ to_string :: proc(b: Builder) -> string {
|
|||||||
/*
|
/*
|
||||||
Returns the length of the Builder's buffer, in bytes
|
Returns the length of the Builder's buffer, in bytes
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- b: A Builder
|
- b: A Builder
|
||||||
|
|
||||||
**Returns** The length of the Builder's buffer
|
Returns:
|
||||||
|
The length of the Builder's buffer
|
||||||
*/
|
*/
|
||||||
builder_len :: proc(b: Builder) -> int {
|
builder_len :: proc(b: Builder) -> int {
|
||||||
return len(b.buf)
|
return len(b.buf)
|
||||||
@@ -271,10 +286,11 @@ builder_len :: proc(b: Builder) -> int {
|
|||||||
/*
|
/*
|
||||||
Returns the capacity of the Builder's buffer, in bytes
|
Returns the capacity of the Builder's buffer, in bytes
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- b: A Builder
|
- b: A Builder
|
||||||
|
|
||||||
**Returns** The capacity of the Builder's buffer
|
Returns:
|
||||||
|
The capacity of the Builder's buffer
|
||||||
*/
|
*/
|
||||||
builder_cap :: proc(b: Builder) -> int {
|
builder_cap :: proc(b: Builder) -> int {
|
||||||
return cap(b.buf)
|
return cap(b.buf)
|
||||||
@@ -282,10 +298,11 @@ builder_cap :: proc(b: Builder) -> int {
|
|||||||
/*
|
/*
|
||||||
The free space left in the Builder's buffer, in bytes
|
The free space left in the Builder's buffer, in bytes
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- b: A Builder
|
- b: A Builder
|
||||||
|
|
||||||
**Returns** The available space left in the Builder's buffer
|
Returns:
|
||||||
|
The available space left in the Builder's buffer
|
||||||
*/
|
*/
|
||||||
builder_space :: proc(b: Builder) -> int {
|
builder_space :: proc(b: Builder) -> int {
|
||||||
return cap(b.buf) - len(b.buf)
|
return cap(b.buf) - len(b.buf)
|
||||||
@@ -293,16 +310,21 @@ builder_space :: proc(b: Builder) -> int {
|
|||||||
/*
|
/*
|
||||||
Appends a byte to the Builder and returns the number of bytes appended
|
Appends a byte to the Builder and returns the number of bytes appended
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- b: A pointer to the Builder
|
- b: A pointer to the Builder
|
||||||
- x: The byte to be appended
|
- x: The byte to be appended
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The number of bytes appended
|
||||||
|
|
||||||
|
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
|
||||||
strings_write_byte_example :: proc() {
|
write_byte_example :: proc() {
|
||||||
builder := strings.builder_make()
|
builder := strings.builder_make()
|
||||||
strings.write_byte(&builder, 'a') // 1
|
strings.write_byte(&builder, 'a') // 1
|
||||||
strings.write_byte(&builder, 'b') // 1
|
strings.write_byte(&builder, 'b') // 1
|
||||||
@@ -313,9 +335,6 @@ Output:
|
|||||||
|
|
||||||
ab
|
ab
|
||||||
|
|
||||||
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
|
||||||
|
|
||||||
**Returns** The number of bytes appended
|
|
||||||
*/
|
*/
|
||||||
write_byte :: proc(b: ^Builder, x: byte) -> (n: int) {
|
write_byte :: proc(b: ^Builder, x: byte) -> (n: int) {
|
||||||
n0 := len(b.buf)
|
n0 := len(b.buf)
|
||||||
@@ -326,7 +345,7 @@ write_byte :: proc(b: ^Builder, x: byte) -> (n: int) {
|
|||||||
/*
|
/*
|
||||||
Appends a slice of bytes to the Builder and returns the number of bytes appended
|
Appends a slice of bytes to the Builder and returns the number of bytes appended
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- b: A pointer to the Builder
|
- b: A pointer to the Builder
|
||||||
- x: The slice of bytes to be appended
|
- x: The slice of bytes to be appended
|
||||||
|
|
||||||
@@ -335,7 +354,7 @@ Example:
|
|||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
|
||||||
strings_write_bytes_example :: proc() {
|
write_bytes_example :: proc() {
|
||||||
builder := strings.builder_make()
|
builder := strings.builder_make()
|
||||||
bytes := [?]byte { 'a', 'b', 'c' }
|
bytes := [?]byte { 'a', 'b', 'c' }
|
||||||
strings.write_bytes(&builder, bytes[:]) // 3
|
strings.write_bytes(&builder, bytes[:]) // 3
|
||||||
@@ -344,7 +363,8 @@ Example:
|
|||||||
|
|
||||||
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
||||||
|
|
||||||
**Returns** The number of bytes appended
|
Returns:
|
||||||
|
The number of bytes appended
|
||||||
*/
|
*/
|
||||||
write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) {
|
write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) {
|
||||||
n0 := len(b.buf)
|
n0 := len(b.buf)
|
||||||
@@ -355,16 +375,21 @@ write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) {
|
|||||||
/*
|
/*
|
||||||
Appends a single rune to the Builder and returns the number of bytes written and an `io.Error`
|
Appends a single rune to the Builder and returns the number of bytes written and an `io.Error`
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- b: A pointer to the Builder
|
- b: A pointer to the Builder
|
||||||
- r: The rune to be appended
|
- r: The rune to be appended
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The number of bytes written and an io.Error (if any)
|
||||||
|
|
||||||
|
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
|
||||||
strings_write_rune_example :: proc() {
|
write_rune_example :: proc() {
|
||||||
builder := strings.builder_make()
|
builder := strings.builder_make()
|
||||||
strings.write_rune(&builder, 'ä') // 2 None
|
strings.write_rune(&builder, 'ä') // 2 None
|
||||||
strings.write_rune(&builder, 'b') // 1 None
|
strings.write_rune(&builder, 'b') // 1 None
|
||||||
@@ -375,9 +400,6 @@ Output:
|
|||||||
|
|
||||||
äb
|
äb
|
||||||
|
|
||||||
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
|
||||||
|
|
||||||
**Returns** The number of bytes written and an io.Error (if any)
|
|
||||||
*/
|
*/
|
||||||
write_rune :: proc(b: ^Builder, r: rune) -> (int, io.Error) {
|
write_rune :: proc(b: ^Builder, r: rune) -> (int, io.Error) {
|
||||||
return io.write_rune(to_writer(b), r)
|
return io.write_rune(to_writer(b), r)
|
||||||
@@ -385,16 +407,21 @@ write_rune :: proc(b: ^Builder, r: rune) -> (int, io.Error) {
|
|||||||
/*
|
/*
|
||||||
Appends a quoted rune to the Builder and returns the number of bytes written
|
Appends a quoted rune to the Builder and returns the number of bytes written
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- b: A pointer to the Builder
|
- b: A pointer to the Builder
|
||||||
- r: The rune to be appended
|
- r: The rune to be appended
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The number of bytes written
|
||||||
|
|
||||||
|
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
|
||||||
strings_write_quoted_rune_example :: proc() {
|
write_quoted_rune_example :: proc() {
|
||||||
builder := strings.builder_make()
|
builder := strings.builder_make()
|
||||||
strings.write_string(&builder, "abc") // 3
|
strings.write_string(&builder, "abc") // 3
|
||||||
strings.write_quoted_rune(&builder, 'ä') // 4
|
strings.write_quoted_rune(&builder, 'ä') // 4
|
||||||
@@ -406,9 +433,6 @@ Output:
|
|||||||
|
|
||||||
abc'ä'abc
|
abc'ä'abc
|
||||||
|
|
||||||
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
|
||||||
|
|
||||||
**Returns** The number of bytes written
|
|
||||||
*/
|
*/
|
||||||
write_quoted_rune :: proc(b: ^Builder, r: rune) -> (n: int) {
|
write_quoted_rune :: proc(b: ^Builder, r: rune) -> (n: int) {
|
||||||
return io.write_quoted_rune(to_writer(b), r)
|
return io.write_quoted_rune(to_writer(b), r)
|
||||||
@@ -416,16 +440,21 @@ write_quoted_rune :: proc(b: ^Builder, r: rune) -> (n: int) {
|
|||||||
/*
|
/*
|
||||||
Appends a string to the Builder and returns the number of bytes written
|
Appends a string to the Builder and returns the number of bytes written
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- b: A pointer to the Builder
|
- b: A pointer to the Builder
|
||||||
- s: The string to be appended
|
- s: The string to be appended
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The number of bytes written
|
||||||
|
|
||||||
|
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
|
||||||
strings_write_string_example :: proc() {
|
write_string_example :: proc() {
|
||||||
builder := strings.builder_make()
|
builder := strings.builder_make()
|
||||||
strings.write_string(&builder, "a") // 1
|
strings.write_string(&builder, "a") // 1
|
||||||
strings.write_string(&builder, "bc") // 2
|
strings.write_string(&builder, "bc") // 2
|
||||||
@@ -436,9 +465,6 @@ Output:
|
|||||||
|
|
||||||
abc
|
abc
|
||||||
|
|
||||||
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
|
||||||
|
|
||||||
**Returns** The number of bytes written
|
|
||||||
*/
|
*/
|
||||||
write_string :: proc(b: ^Builder, s: string) -> (n: int) {
|
write_string :: proc(b: ^Builder, s: string) -> (n: int) {
|
||||||
n0 := len(b.buf)
|
n0 := len(b.buf)
|
||||||
@@ -449,10 +475,11 @@ write_string :: proc(b: ^Builder, s: string) -> (n: int) {
|
|||||||
/*
|
/*
|
||||||
Pops and returns the last byte in the Builder or 0 when the Builder is empty
|
Pops and returns the last byte in the Builder or 0 when the Builder is empty
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- b: A pointer to the Builder
|
- b: A pointer to the Builder
|
||||||
|
|
||||||
**Returns** The last byte in the Builder or 0 if empty
|
Returns:
|
||||||
|
The last byte in the Builder or 0 if empty
|
||||||
*/
|
*/
|
||||||
pop_byte :: proc(b: ^Builder) -> (r: byte) {
|
pop_byte :: proc(b: ^Builder) -> (r: byte) {
|
||||||
if len(b.buf) == 0 {
|
if len(b.buf) == 0 {
|
||||||
@@ -467,10 +494,11 @@ pop_byte :: proc(b: ^Builder) -> (r: byte) {
|
|||||||
/*
|
/*
|
||||||
Pops the last rune in the Builder and returns the popped rune and its rune width or (0, 0) if empty
|
Pops the last rune in the Builder and returns the popped rune and its rune width or (0, 0) if empty
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- b: A pointer to the Builder
|
- b: A pointer to the Builder
|
||||||
|
|
||||||
**Returns** The popped rune and its rune width or (0, 0) if empty
|
Returns:
|
||||||
|
The popped rune and its rune width or (0, 0) if empty
|
||||||
*/
|
*/
|
||||||
pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) {
|
pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) {
|
||||||
if len(b.buf) == 0 {
|
if len(b.buf) == 0 {
|
||||||
@@ -485,17 +513,22 @@ pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) {
|
|||||||
@(private)
|
@(private)
|
||||||
DIGITS_LOWER := "0123456789abcdefx"
|
DIGITS_LOWER := "0123456789abcdefx"
|
||||||
/*
|
/*
|
||||||
**Inputs**
|
Inputs:
|
||||||
- b: A pointer to the Builder
|
- b: A pointer to the Builder
|
||||||
- str: The string to be quoted and appended
|
- str: The string to be quoted and appended
|
||||||
- quote: The optional quote character (default is double quotes)
|
- quote: The optional quote character (default is double quotes)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The number of bytes written
|
||||||
|
|
||||||
|
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
|
||||||
strings_write_quoted_string_example :: proc() {
|
write_quoted_string_example :: proc() {
|
||||||
builder := strings.builder_make()
|
builder := strings.builder_make()
|
||||||
strings.write_quoted_string(&builder, "a") // 3
|
strings.write_quoted_string(&builder, "a") // 3
|
||||||
strings.write_quoted_string(&builder, "bc", '\'') // 4
|
strings.write_quoted_string(&builder, "bc", '\'') // 4
|
||||||
@@ -507,9 +540,6 @@ Output:
|
|||||||
|
|
||||||
"a"'bc'"xyz"
|
"a"'bc'"xyz"
|
||||||
|
|
||||||
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
|
||||||
|
|
||||||
**Returns** The number of bytes written
|
|
||||||
*/
|
*/
|
||||||
write_quoted_string :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: int) {
|
write_quoted_string :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: int) {
|
||||||
n, _ = io.write_quoted_string(to_writer(b), str, quote)
|
n, _ = io.write_quoted_string(to_writer(b), str, quote)
|
||||||
@@ -518,11 +548,16 @@ write_quoted_string :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n:
|
|||||||
/*
|
/*
|
||||||
Appends a rune to the Builder and returns the number of bytes written
|
Appends a rune to the Builder and returns the number of bytes written
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- b: A pointer to the Builder
|
- b: A pointer to the Builder
|
||||||
- r: The rune to be appended
|
- r: The rune to be appended
|
||||||
- write_quote: Optional boolean flag to wrap in single-quotes (') (default is true)
|
- write_quote: Optional boolean flag to wrap in single-quotes (') (default is true)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The number of bytes written
|
||||||
|
|
||||||
|
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
@@ -540,9 +575,6 @@ Output:
|
|||||||
|
|
||||||
a'"'x
|
a'"'x
|
||||||
|
|
||||||
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
|
||||||
|
|
||||||
**Returns** The number of bytes written
|
|
||||||
*/
|
*/
|
||||||
write_encoded_rune :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int) {
|
write_encoded_rune :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int) {
|
||||||
n, _ = io.write_encoded_rune(to_writer(b), r, write_quote)
|
n, _ = io.write_encoded_rune(to_writer(b), r, write_quote)
|
||||||
@@ -552,7 +584,7 @@ write_encoded_rune :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int
|
|||||||
/*
|
/*
|
||||||
Appends an escaped rune to the Builder and returns the number of bytes written
|
Appends an escaped rune to the Builder and returns the number of bytes written
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- b: A pointer to the Builder
|
- b: A pointer to the Builder
|
||||||
- r: The rune to be appended
|
- r: The rune to be appended
|
||||||
- quote: The quote character
|
- quote: The quote character
|
||||||
@@ -565,7 +597,8 @@ Appends an escaped rune to the Builder and returns the number of bytes written
|
|||||||
|
|
||||||
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
||||||
|
|
||||||
**Returns** The number of bytes written
|
Returns:
|
||||||
|
The number of bytes written
|
||||||
*/
|
*/
|
||||||
write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false) -> (n: int) {
|
write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false) -> (n: int) {
|
||||||
n, _ = io.write_escaped_rune(to_writer(b), r, quote, html_safe)
|
n, _ = io.write_escaped_rune(to_writer(b), r, quote, html_safe)
|
||||||
@@ -574,7 +607,7 @@ write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false
|
|||||||
/*
|
/*
|
||||||
Writes a f64 value to the Builder and returns the number of characters written
|
Writes a f64 value to the Builder and returns the number of characters written
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- b: A pointer to the Builder
|
- b: A pointer to the Builder
|
||||||
- f: The f64 value to be appended
|
- f: The f64 value to be appended
|
||||||
- fmt: The format byte
|
- fmt: The format byte
|
||||||
@@ -584,7 +617,8 @@ Writes a f64 value to the Builder and returns the number of characters written
|
|||||||
|
|
||||||
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
||||||
|
|
||||||
**Returns** The number of characters written
|
Returns:
|
||||||
|
The number of characters written
|
||||||
*/
|
*/
|
||||||
write_float :: proc(b: ^Builder, f: f64, fmt: byte, prec, bit_size: int, always_signed := false) -> (n: int) {
|
write_float :: proc(b: ^Builder, f: f64, fmt: byte, prec, bit_size: int, always_signed := false) -> (n: int) {
|
||||||
buf: [384]byte
|
buf: [384]byte
|
||||||
@@ -599,7 +633,7 @@ write_float :: proc(b: ^Builder, f: f64, fmt: byte, prec, bit_size: int, always_
|
|||||||
/*
|
/*
|
||||||
Writes a f16 value to the Builder and returns the number of characters written
|
Writes a f16 value to the Builder and returns the number of characters written
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- b: A pointer to the Builder
|
- b: A pointer to the Builder
|
||||||
- f: The f16 value to be appended
|
- f: The f16 value to be appended
|
||||||
- fmt: The format byte
|
- fmt: The format byte
|
||||||
@@ -607,7 +641,8 @@ Writes a f16 value to the Builder and returns the number of characters written
|
|||||||
|
|
||||||
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
||||||
|
|
||||||
**Returns** The number of characters written
|
Returns:
|
||||||
|
The number of characters written
|
||||||
*/
|
*/
|
||||||
write_f16 :: proc(b: ^Builder, f: f16, fmt: byte, always_signed := false) -> (n: int) {
|
write_f16 :: proc(b: ^Builder, f: f16, fmt: byte, always_signed := false) -> (n: int) {
|
||||||
buf: [384]byte
|
buf: [384]byte
|
||||||
@@ -620,18 +655,23 @@ write_f16 :: proc(b: ^Builder, f: f16, fmt: byte, always_signed := false) -> (n:
|
|||||||
/*
|
/*
|
||||||
Writes a f32 value to the Builder and returns the number of characters written
|
Writes a f32 value to the Builder and returns the number of characters written
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- b: A pointer to the Builder
|
- b: A pointer to the Builder
|
||||||
- f: The f32 value to be appended
|
- f: The f32 value to be appended
|
||||||
- fmt: The format byte
|
- fmt: The format byte
|
||||||
- always_signed: Optional boolean flag to always include the sign
|
- always_signed: Optional boolean flag to always include the sign
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The number of characters written
|
||||||
|
|
||||||
|
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
|
||||||
strings_write_f32_example :: proc() {
|
write_f32_example :: proc() {
|
||||||
builder := strings.builder_make()
|
builder := strings.builder_make()
|
||||||
strings.write_f32(&builder, 3.14159, 'f') // 6
|
strings.write_f32(&builder, 3.14159, 'f') // 6
|
||||||
strings.write_string(&builder, " - ") // 3
|
strings.write_string(&builder, " - ") // 3
|
||||||
@@ -643,9 +683,6 @@ Output:
|
|||||||
|
|
||||||
3.14159012 - -1.23000003e-01
|
3.14159012 - -1.23000003e-01
|
||||||
|
|
||||||
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
|
||||||
|
|
||||||
**Returns** The number of characters written
|
|
||||||
*/
|
*/
|
||||||
write_f32 :: proc(b: ^Builder, f: f32, fmt: byte, always_signed := false) -> (n: int) {
|
write_f32 :: proc(b: ^Builder, f: f32, fmt: byte, always_signed := false) -> (n: int) {
|
||||||
buf: [384]byte
|
buf: [384]byte
|
||||||
@@ -658,7 +695,7 @@ write_f32 :: proc(b: ^Builder, f: f32, fmt: byte, always_signed := false) -> (n:
|
|||||||
/*
|
/*
|
||||||
Writes a f32 value to the Builder and returns the number of characters written
|
Writes a f32 value to the Builder and returns the number of characters written
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- b: A pointer to the Builder
|
- b: A pointer to the Builder
|
||||||
- f: The f32 value to be appended
|
- f: The f32 value to be appended
|
||||||
- fmt: The format byte
|
- fmt: The format byte
|
||||||
@@ -666,7 +703,8 @@ Writes a f32 value to the Builder and returns the number of characters written
|
|||||||
|
|
||||||
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
||||||
|
|
||||||
**Returns** The number of characters written
|
Returns:
|
||||||
|
The number of characters written
|
||||||
*/
|
*/
|
||||||
write_f64 :: proc(b: ^Builder, f: f64, fmt: byte, always_signed := false) -> (n: int) {
|
write_f64 :: proc(b: ^Builder, f: f64, fmt: byte, always_signed := false) -> (n: int) {
|
||||||
buf: [384]byte
|
buf: [384]byte
|
||||||
@@ -679,14 +717,15 @@ write_f64 :: proc(b: ^Builder, f: f64, fmt: byte, always_signed := false) -> (n:
|
|||||||
/*
|
/*
|
||||||
Writes a u64 value to the Builder and returns the number of characters written
|
Writes a u64 value to the Builder and returns the number of characters written
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- b: A pointer to the Builder
|
- b: A pointer to the Builder
|
||||||
- i: The u64 value to be appended
|
- i: The u64 value to be appended
|
||||||
- base: The optional base for the numeric representation
|
- base: The optional base for the numeric representation
|
||||||
|
|
||||||
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
||||||
|
|
||||||
**Returns** The number of characters written
|
Returns:
|
||||||
|
The number of characters written
|
||||||
*/
|
*/
|
||||||
write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) -> (n: int) {
|
write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) -> (n: int) {
|
||||||
buf: [32]byte
|
buf: [32]byte
|
||||||
@@ -696,14 +735,15 @@ write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) -> (n: int) {
|
|||||||
/*
|
/*
|
||||||
Writes a i64 value to the Builder and returns the number of characters written
|
Writes a i64 value to the Builder and returns the number of characters written
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- b: A pointer to the Builder
|
- b: A pointer to the Builder
|
||||||
- i: The i64 value to be appended
|
- i: The i64 value to be appended
|
||||||
- base: The optional base for the numeric representation
|
- base: The optional base for the numeric representation
|
||||||
|
|
||||||
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
||||||
|
|
||||||
**Returns** The number of characters written
|
Returns:
|
||||||
|
The number of characters written
|
||||||
*/
|
*/
|
||||||
write_i64 :: proc(b: ^Builder, i: i64, base: int = 10) -> (n: int) {
|
write_i64 :: proc(b: ^Builder, i: i64, base: int = 10) -> (n: int) {
|
||||||
buf: [32]byte
|
buf: [32]byte
|
||||||
@@ -713,14 +753,15 @@ write_i64 :: proc(b: ^Builder, i: i64, base: int = 10) -> (n: int) {
|
|||||||
/*
|
/*
|
||||||
Writes a uint value to the Builder and returns the number of characters written
|
Writes a uint value to the Builder and returns the number of characters written
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- b: A pointer to the Builder
|
- b: A pointer to the Builder
|
||||||
- i: The uint value to be appended
|
- i: The uint value to be appended
|
||||||
- base: The optional base for the numeric representation
|
- base: The optional base for the numeric representation
|
||||||
|
|
||||||
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
||||||
|
|
||||||
**Returns** The number of characters written
|
Returns:
|
||||||
|
The number of characters written
|
||||||
*/
|
*/
|
||||||
write_uint :: proc(b: ^Builder, i: uint, base: int = 10) -> (n: int) {
|
write_uint :: proc(b: ^Builder, i: uint, base: int = 10) -> (n: int) {
|
||||||
return write_u64(b, u64(i), base)
|
return write_u64(b, u64(i), base)
|
||||||
@@ -728,14 +769,15 @@ write_uint :: proc(b: ^Builder, i: uint, base: int = 10) -> (n: int) {
|
|||||||
/*
|
/*
|
||||||
Writes a int value to the Builder and returns the number of characters written
|
Writes a int value to the Builder and returns the number of characters written
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- b: A pointer to the Builder
|
- b: A pointer to the Builder
|
||||||
- i: The int value to be appended
|
- i: The int value to be appended
|
||||||
- base: The optional base for the numeric representation
|
- base: The optional base for the numeric representation
|
||||||
|
|
||||||
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
|
||||||
|
|
||||||
**Returns** The number of characters written
|
Returns:
|
||||||
|
The number of characters written
|
||||||
*/
|
*/
|
||||||
write_int :: proc(b: ^Builder, i: int, base: int = 10) -> (n: int) {
|
write_int :: proc(b: ^Builder, i: int, base: int = 10) -> (n: int) {
|
||||||
return write_i64(b, i64(i), base)
|
return write_i64(b, i64(i), base)
|
||||||
|
|||||||
@@ -9,14 +9,15 @@ Converts invalid UTF-8 sequences in the input string `s` to the `replacement` st
|
|||||||
|
|
||||||
*Allocates Using Provided Allocator*
|
*Allocates Using Provided Allocator*
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- s: Input string that may contain invalid UTF-8 sequences.
|
- s: Input string that may contain invalid UTF-8 sequences.
|
||||||
- replacement: String to replace invalid UTF-8 sequences with.
|
- replacement: String to replace invalid UTF-8 sequences with.
|
||||||
- allocator: (default: context.allocator).
|
- allocator: (default: context.allocator).
|
||||||
|
|
||||||
WARNING: Allocation does not occur when len(s) == 0
|
WARNING: Allocation does not occur when len(s) == 0
|
||||||
|
|
||||||
**Returns** A valid UTF-8 string with invalid sequences replaced by `replacement`.
|
Returns:
|
||||||
|
A valid UTF-8 string with invalid sequences replaced by `replacement`.
|
||||||
*/
|
*/
|
||||||
to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> string {
|
to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> string {
|
||||||
if len(s) == 0 {
|
if len(s) == 0 {
|
||||||
@@ -76,16 +77,19 @@ Converts the input string `s` to all lowercase characters.
|
|||||||
|
|
||||||
*Allocates Using Provided Allocator*
|
*Allocates Using Provided Allocator*
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- s: Input string to be converted.
|
- s: Input string to be converted.
|
||||||
- allocator: (default: context.allocator).
|
- allocator: (default: context.allocator).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A new string with all characters converted to lowercase.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
|
||||||
strings_to_lower_example :: proc() {
|
to_lower_example :: proc() {
|
||||||
fmt.println(strings.to_lower("TeST"))
|
fmt.println(strings.to_lower("TeST"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,7 +97,6 @@ Output:
|
|||||||
|
|
||||||
test
|
test
|
||||||
|
|
||||||
**Returns** A new string with all characters converted to lowercase.
|
|
||||||
*/
|
*/
|
||||||
to_lower :: proc(s: string, allocator := context.allocator) -> string {
|
to_lower :: proc(s: string, allocator := context.allocator) -> string {
|
||||||
b: Builder
|
b: Builder
|
||||||
@@ -108,16 +111,19 @@ Converts the input string `s` to all uppercase characters.
|
|||||||
|
|
||||||
*Allocates Using Provided Allocator*
|
*Allocates Using Provided Allocator*
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- s: Input string to be converted.
|
- s: Input string to be converted.
|
||||||
- allocator: (default: context.allocator).
|
- allocator: (default: context.allocator).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A new string with all characters converted to uppercase.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
|
||||||
strings_to_upper_example :: proc() {
|
to_upper_example :: proc() {
|
||||||
fmt.println(strings.to_upper("Test"))
|
fmt.println(strings.to_upper("Test"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +131,6 @@ Output:
|
|||||||
|
|
||||||
TEST
|
TEST
|
||||||
|
|
||||||
**Returns** A new string with all characters converted to uppercase.
|
|
||||||
*/
|
*/
|
||||||
to_upper :: proc(s: string, allocator := context.allocator) -> string {
|
to_upper :: proc(s: string, allocator := context.allocator) -> string {
|
||||||
b: Builder
|
b: Builder
|
||||||
@@ -138,10 +143,11 @@ to_upper :: proc(s: string, allocator := context.allocator) -> string {
|
|||||||
/*
|
/*
|
||||||
Checks if the rune `r` is a delimiter (' ', '-', or '_').
|
Checks if the rune `r` is a delimiter (' ', '-', or '_').
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- r: Rune to check for delimiter status.
|
- r: Rune to check for delimiter status.
|
||||||
|
|
||||||
**Returns** True if `r` is a delimiter, false otherwise.
|
Returns:
|
||||||
|
True if `r` is a delimiter, false otherwise.
|
||||||
*/
|
*/
|
||||||
is_delimiter :: proc(r: rune) -> bool {
|
is_delimiter :: proc(r: rune) -> bool {
|
||||||
return r == '-' || r == '_' || is_space(r)
|
return r == '-' || r == '_' || is_space(r)
|
||||||
@@ -149,10 +155,11 @@ is_delimiter :: proc(r: rune) -> bool {
|
|||||||
/*
|
/*
|
||||||
Checks if the rune `r` is a non-alphanumeric or space character.
|
Checks if the rune `r` is a non-alphanumeric or space character.
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- r: Rune to check for separator status.
|
- r: Rune to check for separator status.
|
||||||
|
|
||||||
**Returns** True if `r` is a non-alpha or `unicode.is_space` rune.
|
Returns:
|
||||||
|
True if `r` is a non-alpha or `unicode.is_space` rune.
|
||||||
*/
|
*/
|
||||||
is_separator :: proc(r: rune) -> bool {
|
is_separator :: proc(r: rune) -> bool {
|
||||||
if r <= 0x7f {
|
if r <= 0x7f {
|
||||||
@@ -179,7 +186,7 @@ is_separator :: proc(r: rune) -> bool {
|
|||||||
/*
|
/*
|
||||||
Iterates over a string, calling a callback for each rune with the previous, current, and next runes as arguments.
|
Iterates over a string, calling a callback for each rune with the previous, current, and next runes as arguments.
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- w: An io.Writer to be used by the callback for writing output.
|
- w: An io.Writer to be used by the callback for writing output.
|
||||||
- s: The input string to be iterated over.
|
- s: The input string to be iterated over.
|
||||||
- callback: A procedure to be called for each rune in the string, with arguments (w: io.Writer, prev, curr, next: rune).
|
- callback: A procedure to be called for each rune in the string, with arguments (w: io.Writer, prev, curr, next: rune).
|
||||||
@@ -191,7 +198,7 @@ Example:
|
|||||||
import "core:strings"
|
import "core:strings"
|
||||||
import "core:io"
|
import "core:io"
|
||||||
|
|
||||||
strings_string_case_iterator_example :: proc() {
|
string_case_iterator_example :: proc() {
|
||||||
my_callback :: proc(w: io.Writer, prev, curr, next: rune) {
|
my_callback :: proc(w: io.Writer, prev, curr, next: rune) {
|
||||||
fmt.println("my_callback", curr) // <-- Custom logic here
|
fmt.println("my_callback", curr) // <-- Custom logic here
|
||||||
}
|
}
|
||||||
@@ -241,11 +248,12 @@ Converts the input string `s` to "lowerCamelCase".
|
|||||||
|
|
||||||
*Allocates Using Provided Allocator*
|
*Allocates Using Provided Allocator*
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- s: Input string to be converted.
|
- s: Input string to be converted.
|
||||||
- allocator: (default: context.allocator).
|
- allocator: (default: context.allocator).
|
||||||
|
|
||||||
**Returns** A "lowerCamelCase" formatted string.
|
Returns:
|
||||||
|
A "lowerCamelCase" formatted string.
|
||||||
*/
|
*/
|
||||||
to_camel_case :: proc(s: string, allocator := context.allocator) -> string {
|
to_camel_case :: proc(s: string, allocator := context.allocator) -> string {
|
||||||
s := s
|
s := s
|
||||||
@@ -275,11 +283,12 @@ Converts the input string `s` to "UpperCamelCase" (PascalCase).
|
|||||||
|
|
||||||
*Allocates Using Provided Allocator*
|
*Allocates Using Provided Allocator*
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- s: Input string to be converted.
|
- s: Input string to be converted.
|
||||||
- allocator: (default: context.allocator).
|
- allocator: (default: context.allocator).
|
||||||
|
|
||||||
**Returns** A "PascalCase" formatted string.
|
Returns:
|
||||||
|
A "PascalCase" formatted string.
|
||||||
*/
|
*/
|
||||||
to_pascal_case :: proc(s: string, allocator := context.allocator) -> string {
|
to_pascal_case :: proc(s: string, allocator := context.allocator) -> string {
|
||||||
s := s
|
s := s
|
||||||
@@ -307,18 +316,21 @@ Returns a string converted to a delimiter-separated case with configurable casin
|
|||||||
|
|
||||||
*Allocates Using Provided Allocator*
|
*Allocates Using Provided Allocator*
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- s: The input string to be converted
|
- s: The input string to be converted
|
||||||
- delimiter: The rune to be used as the delimiter between words
|
- delimiter: The rune to be used as the delimiter between words
|
||||||
- all_upper_case: A boolean indicating if the output should be all uppercased (true) or lowercased (false)
|
- all_upper_case: A boolean indicating if the output should be all uppercased (true) or lowercased (false)
|
||||||
- allocator: (default: context.allocator).
|
- allocator: (default: context.allocator).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The converted string
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
|
||||||
strings_to_delimiter_case_example :: proc() {
|
to_delimiter_case_example :: proc() {
|
||||||
fmt.println(strings.to_delimiter_case("Hello World", '_', false))
|
fmt.println(strings.to_delimiter_case("Hello World", '_', false))
|
||||||
fmt.println(strings.to_delimiter_case("Hello World", ' ', true))
|
fmt.println(strings.to_delimiter_case("Hello World", ' ', true))
|
||||||
fmt.println(strings.to_delimiter_case("aBC", '_', false))
|
fmt.println(strings.to_delimiter_case("aBC", '_', false))
|
||||||
@@ -328,9 +340,8 @@ Output:
|
|||||||
|
|
||||||
hello_world
|
hello_world
|
||||||
HELLO WORLD
|
HELLO WORLD
|
||||||
a_b_c
|
a_bc
|
||||||
|
|
||||||
**Returns** The converted string
|
|
||||||
*/
|
*/
|
||||||
to_delimiter_case :: proc(
|
to_delimiter_case :: proc(
|
||||||
s: string,
|
s: string,
|
||||||
@@ -380,16 +391,19 @@ Converts a string to "snake_case" with all runes lowercased
|
|||||||
|
|
||||||
*Allocates Using Provided Allocator*
|
*Allocates Using Provided Allocator*
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- s: The input string to be converted
|
- s: The input string to be converted
|
||||||
- allocator: (default: context.allocator).
|
- allocator: (default: context.allocator).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The converted string
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
|
||||||
strings_to_snake_case_example :: proc() {
|
to_snake_case_example :: proc() {
|
||||||
fmt.println(strings.to_snake_case("HelloWorld"))
|
fmt.println(strings.to_snake_case("HelloWorld"))
|
||||||
fmt.println(strings.to_snake_case("Hello World"))
|
fmt.println(strings.to_snake_case("Hello World"))
|
||||||
}
|
}
|
||||||
@@ -399,8 +413,6 @@ Output:
|
|||||||
hello_world
|
hello_world
|
||||||
hello_world
|
hello_world
|
||||||
|
|
||||||
```
|
|
||||||
**Returns** The converted string
|
|
||||||
*/
|
*/
|
||||||
to_snake_case :: proc(s: string, allocator := context.allocator) -> string {
|
to_snake_case :: proc(s: string, allocator := context.allocator) -> string {
|
||||||
return to_delimiter_case(s, '_', false, allocator)
|
return to_delimiter_case(s, '_', false, allocator)
|
||||||
@@ -412,16 +424,19 @@ Converts a string to "SNAKE_CASE" with all runes uppercased
|
|||||||
|
|
||||||
*Allocates Using Provided Allocator*
|
*Allocates Using Provided Allocator*
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- s: The input string to be converted
|
- s: The input string to be converted
|
||||||
- allocator: (default: context.allocator).
|
- allocator: (default: context.allocator).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The converted string
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
|
||||||
strings_to_upper_snake_case_example :: proc() {
|
to_upper_snake_case_example :: proc() {
|
||||||
fmt.println(strings.to_upper_snake_case("HelloWorld"))
|
fmt.println(strings.to_upper_snake_case("HelloWorld"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,7 +444,6 @@ Output:
|
|||||||
|
|
||||||
HELLO_WORLD
|
HELLO_WORLD
|
||||||
|
|
||||||
**Returns** The converted string
|
|
||||||
*/
|
*/
|
||||||
to_upper_snake_case :: proc(s: string, allocator := context.allocator) -> string {
|
to_upper_snake_case :: proc(s: string, allocator := context.allocator) -> string {
|
||||||
return to_delimiter_case(s, '_', true, allocator)
|
return to_delimiter_case(s, '_', true, allocator)
|
||||||
@@ -439,16 +453,19 @@ Converts a string to "kebab-case" with all runes lowercased
|
|||||||
|
|
||||||
*Allocates Using Provided Allocator*
|
*Allocates Using Provided Allocator*
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- s: The input string to be converted
|
- s: The input string to be converted
|
||||||
- allocator: (default: context.allocator).
|
- allocator: (default: context.allocator).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The converted string
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
|
||||||
strings_to_kebab_case_example :: proc() {
|
to_kebab_case_example :: proc() {
|
||||||
fmt.println(strings.to_kebab_case("HelloWorld"))
|
fmt.println(strings.to_kebab_case("HelloWorld"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -456,7 +473,6 @@ Output:
|
|||||||
|
|
||||||
hello-world
|
hello-world
|
||||||
|
|
||||||
**Returns** The converted string
|
|
||||||
*/
|
*/
|
||||||
to_kebab_case :: proc(s: string, allocator := context.allocator) -> string {
|
to_kebab_case :: proc(s: string, allocator := context.allocator) -> string {
|
||||||
return to_delimiter_case(s, '-', false, allocator)
|
return to_delimiter_case(s, '-', false, allocator)
|
||||||
@@ -466,16 +482,19 @@ Converts a string to "KEBAB-CASE" with all runes uppercased
|
|||||||
|
|
||||||
*Allocates Using Provided Allocator*
|
*Allocates Using Provided Allocator*
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- s: The input string to be converted
|
- s: The input string to be converted
|
||||||
- allocator: (default: context.allocator).
|
- allocator: (default: context.allocator).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The converted string
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
|
||||||
strings_to_upper_kebab_case_example :: proc() {
|
to_upper_kebab_case_example :: proc() {
|
||||||
fmt.println(strings.to_upper_kebab_case("HelloWorld"))
|
fmt.println(strings.to_upper_kebab_case("HelloWorld"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -483,7 +502,6 @@ Output:
|
|||||||
|
|
||||||
HELLO-WORLD
|
HELLO-WORLD
|
||||||
|
|
||||||
**Returns** The converted string
|
|
||||||
*/
|
*/
|
||||||
to_upper_kebab_case :: proc(s: string, allocator := context.allocator) -> string {
|
to_upper_kebab_case :: proc(s: string, allocator := context.allocator) -> string {
|
||||||
return to_delimiter_case(s, '-', true, allocator)
|
return to_delimiter_case(s, '-', true, allocator)
|
||||||
@@ -493,16 +511,19 @@ Converts a string to "Ada_Case"
|
|||||||
|
|
||||||
*Allocates Using Provided Allocator*
|
*Allocates Using Provided Allocator*
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- s: The input string to be converted
|
- s: The input string to be converted
|
||||||
- allocator: (default: context.allocator).
|
- allocator: (default: context.allocator).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The converted string
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
|
||||||
strings_to_upper_kebab_case_example :: proc() {
|
to_ada_case_example :: proc() {
|
||||||
fmt.println(strings.to_ada_case("HelloWorld"))
|
fmt.println(strings.to_ada_case("HelloWorld"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,7 +531,6 @@ Output:
|
|||||||
|
|
||||||
Hello_World
|
Hello_World
|
||||||
|
|
||||||
**Returns** The converted string
|
|
||||||
*/
|
*/
|
||||||
to_ada_case :: proc(s: string, allocator := context.allocator) -> string {
|
to_ada_case :: proc(s: string, allocator := context.allocator) -> string {
|
||||||
s := s
|
s := s
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ Initializes the entries map and sets the allocator for the string entries
|
|||||||
|
|
||||||
*Allocates Using Provided Allocators*
|
*Allocates Using Provided Allocators*
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- m: A pointer to the Intern struct to be initialized
|
- m: A pointer to the Intern struct to be initialized
|
||||||
- allocator: The allocator for the Intern_Entry strings (Default: context.allocator)
|
- allocator: The allocator for the Intern_Entry strings (Default: context.allocator)
|
||||||
- map_allocator: The allocator for the map of entries (Default: context.allocator)
|
- map_allocator: The allocator for the map of entries (Default: context.allocator)
|
||||||
@@ -37,7 +37,7 @@ intern_init :: proc(m: ^Intern, allocator := context.allocator, map_allocator :=
|
|||||||
/*
|
/*
|
||||||
Frees the map and all its content allocated using the `.allocator`.
|
Frees the map and all its content allocated using the `.allocator`.
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- m: A pointer to the Intern struct to be destroyed
|
- m: A pointer to the Intern struct to be destroyed
|
||||||
*/
|
*/
|
||||||
intern_destroy :: proc(m: ^Intern) {
|
intern_destroy :: proc(m: ^Intern) {
|
||||||
@@ -51,13 +51,14 @@ Returns an interned copy of the given text, adding it to the map if not already
|
|||||||
|
|
||||||
*Allocate using the Intern's Allocator (First time string is seen only)*
|
*Allocate using the Intern's Allocator (First time string is seen only)*
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- m: A pointer to the Intern struct
|
- m: A pointer to the Intern struct
|
||||||
- text: The string to be interned
|
- text: The string to be interned
|
||||||
|
|
||||||
NOTE: The returned string lives as long as the map entry lives.
|
NOTE: The returned string lives as long as the map entry lives.
|
||||||
|
|
||||||
**Returns** The interned string and an allocator error if any
|
Returns:
|
||||||
|
The interned string and an allocator error if any
|
||||||
*/
|
*/
|
||||||
intern_get :: proc(m: ^Intern, text: string) -> (str: string, err: runtime.Allocator_Error) {
|
intern_get :: proc(m: ^Intern, text: string) -> (str: string, err: runtime.Allocator_Error) {
|
||||||
entry := _intern_get_entry(m, text) or_return
|
entry := _intern_get_entry(m, text) or_return
|
||||||
@@ -68,13 +69,14 @@ Returns an interned copy of the given text as a cstring, adding it to the map if
|
|||||||
|
|
||||||
*Allocate using the Intern's Allocator (First time string is seen only)*
|
*Allocate using the Intern's Allocator (First time string is seen only)*
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- m: A pointer to the Intern struct
|
- m: A pointer to the Intern struct
|
||||||
- text: The string to be interned
|
- text: The string to be interned
|
||||||
|
|
||||||
NOTE: The returned cstring lives as long as the map entry lives
|
NOTE: The returned cstring lives as long as the map entry lives
|
||||||
|
|
||||||
**Returns** The interned cstring and an allocator error if any
|
Returns:
|
||||||
|
The interned cstring and an allocator error if any
|
||||||
*/
|
*/
|
||||||
intern_get_cstring :: proc(m: ^Intern, text: string) -> (str: cstring, err: runtime.Allocator_Error) {
|
intern_get_cstring :: proc(m: ^Intern, text: string) -> (str: cstring, err: runtime.Allocator_Error) {
|
||||||
entry := _intern_get_entry(m, text) or_return
|
entry := _intern_get_entry(m, text) or_return
|
||||||
@@ -86,11 +88,12 @@ Sets and allocates the entry if it wasn't set yet
|
|||||||
|
|
||||||
*Allocate using the Intern's Allocator (First time string is seen only)*
|
*Allocate using the Intern's Allocator (First time string is seen only)*
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- m: A pointer to the Intern struct
|
- m: A pointer to the Intern struct
|
||||||
- text: The string to be looked up or interned
|
- text: The string to be looked up or interned
|
||||||
|
|
||||||
**Returns** The new or existing interned entry and an allocator error if any
|
Returns:
|
||||||
|
The new or existing interned entry and an allocator error if any
|
||||||
*/
|
*/
|
||||||
_intern_get_entry :: proc(m: ^Intern, text: string) -> (new_entry: ^Intern_Entry, err: runtime.Allocator_Error) #no_bounds_check {
|
_intern_get_entry :: proc(m: ^Intern, text: string) -> (new_entry: ^Intern_Entry, err: runtime.Allocator_Error) #no_bounds_check {
|
||||||
if prev, ok := m.entries[text]; ok {
|
if prev, ok := m.entries[text]; ok {
|
||||||
|
|||||||
+34
-27
@@ -16,7 +16,7 @@ Reader :: struct {
|
|||||||
/*
|
/*
|
||||||
Initializes a string Reader with the provided string
|
Initializes a string Reader with the provided string
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- r: A pointer to a Reader struct
|
- r: A pointer to a Reader struct
|
||||||
- s: The input string to be read
|
- s: The input string to be read
|
||||||
*/
|
*/
|
||||||
@@ -28,10 +28,11 @@ reader_init :: proc(r: ^Reader, s: string) {
|
|||||||
/*
|
/*
|
||||||
Converts a Reader into an `io.Stream`
|
Converts a Reader into an `io.Stream`
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- r: A pointer to a Reader struct
|
- r: A pointer to a Reader struct
|
||||||
|
|
||||||
**Returns** An io.Stream for the given Reader
|
Returns:
|
||||||
|
An io.Stream for the given Reader
|
||||||
*/
|
*/
|
||||||
reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
|
reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
|
||||||
s.stream_data = r
|
s.stream_data = r
|
||||||
@@ -41,11 +42,12 @@ reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
|
|||||||
/*
|
/*
|
||||||
Initializes a string Reader and returns an `io.Reader` for the given string
|
Initializes a string Reader and returns an `io.Reader` for the given string
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- r: A pointer to a Reader struct
|
- r: A pointer to a Reader struct
|
||||||
- s: The input string to be read
|
- s: The input string to be read
|
||||||
|
|
||||||
**Returns** An io.Reader for the given string
|
Returns:
|
||||||
|
An io.Reader for the given string
|
||||||
*/
|
*/
|
||||||
to_reader :: proc(r: ^Reader, s: string) -> io.Reader {
|
to_reader :: proc(r: ^Reader, s: string) -> io.Reader {
|
||||||
reader_init(r, s)
|
reader_init(r, s)
|
||||||
@@ -55,11 +57,12 @@ to_reader :: proc(r: ^Reader, s: string) -> io.Reader {
|
|||||||
/*
|
/*
|
||||||
Initializes a string Reader and returns an `io.Reader_At` for the given string
|
Initializes a string Reader and returns an `io.Reader_At` for the given string
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- r: A pointer to a Reader struct
|
- r: A pointer to a Reader struct
|
||||||
- s: The input string to be read
|
- s: The input string to be read
|
||||||
|
|
||||||
**Returns** An `io.Reader_At` for the given string
|
Returns:
|
||||||
|
An `io.Reader_At` for the given string
|
||||||
*/
|
*/
|
||||||
to_reader_at :: proc(r: ^Reader, s: string) -> io.Reader_At {
|
to_reader_at :: proc(r: ^Reader, s: string) -> io.Reader_At {
|
||||||
reader_init(r, s)
|
reader_init(r, s)
|
||||||
@@ -69,10 +72,11 @@ to_reader_at :: proc(r: ^Reader, s: string) -> io.Reader_At {
|
|||||||
/*
|
/*
|
||||||
Returns the remaining length of the Reader
|
Returns the remaining length of the Reader
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- r: A pointer to a Reader struct
|
- r: A pointer to a Reader struct
|
||||||
|
|
||||||
**Returns** The remaining length of the Reader
|
Returns:
|
||||||
|
The remaining length of the Reader
|
||||||
*/
|
*/
|
||||||
reader_length :: proc(r: ^Reader) -> int {
|
reader_length :: proc(r: ^Reader) -> int {
|
||||||
if r.i >= i64(len(r.s)) {
|
if r.i >= i64(len(r.s)) {
|
||||||
@@ -83,10 +87,11 @@ reader_length :: proc(r: ^Reader) -> int {
|
|||||||
/*
|
/*
|
||||||
Returns the length of the string stored in the Reader
|
Returns the length of the string stored in the Reader
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- r: A pointer to a Reader struct
|
- r: A pointer to a Reader struct
|
||||||
|
|
||||||
**Returns** The length of the string stored in the Reader
|
Returns:
|
||||||
|
The length of the string stored in the Reader
|
||||||
*/
|
*/
|
||||||
reader_size :: proc(r: ^Reader) -> i64 {
|
reader_size :: proc(r: ^Reader) -> i64 {
|
||||||
return i64(len(r.s))
|
return i64(len(r.s))
|
||||||
@@ -94,11 +99,11 @@ reader_size :: proc(r: ^Reader) -> i64 {
|
|||||||
/*
|
/*
|
||||||
Reads len(p) bytes from the Reader's string and copies into the provided slice.
|
Reads len(p) bytes from the Reader's string and copies into the provided slice.
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- r: A pointer to a Reader struct
|
- r: A pointer to a Reader struct
|
||||||
- p: A byte slice to copy data into
|
- p: A byte slice to copy data into
|
||||||
|
|
||||||
**Returns**
|
Returns:
|
||||||
- n: The number of bytes read
|
- n: The number of bytes read
|
||||||
- err: An `io.Error` if an error occurs while reading, including `.EOF`, otherwise `nil` denotes success.
|
- err: An `io.Error` if an error occurs while reading, including `.EOF`, otherwise `nil` denotes success.
|
||||||
*/
|
*/
|
||||||
@@ -114,12 +119,12 @@ reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
|
|||||||
/*
|
/*
|
||||||
Reads len(p) bytes from the Reader's string and copies into the provided slice, at the specified offset from the current index.
|
Reads len(p) bytes from the Reader's string and copies into the provided slice, at the specified offset from the current index.
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- r: A pointer to a Reader struct
|
- r: A pointer to a Reader struct
|
||||||
- p: A byte slice to copy data into
|
- p: A byte slice to copy data into
|
||||||
- off: The offset from which to read
|
- off: The offset from which to read
|
||||||
|
|
||||||
**Returns**
|
Returns:
|
||||||
- n: The number of bytes read
|
- n: The number of bytes read
|
||||||
- err: An `io.Error` if an error occurs while reading, including `.EOF`, otherwise `nil` denotes success.
|
- err: An `io.Error` if an error occurs while reading, including `.EOF`, otherwise `nil` denotes success.
|
||||||
*/
|
*/
|
||||||
@@ -139,10 +144,10 @@ reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Erro
|
|||||||
/*
|
/*
|
||||||
Reads and returns a single byte from the Reader's string
|
Reads and returns a single byte from the Reader's string
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- r: A pointer to a Reader struct
|
- r: A pointer to a Reader struct
|
||||||
|
|
||||||
**Returns**
|
Returns:
|
||||||
- The byte read from the Reader
|
- The byte read from the Reader
|
||||||
- err: An `io.Error` if an error occurs while reading, including `.EOF`, otherwise `nil` denotes success.
|
- err: An `io.Error` if an error occurs while reading, including `.EOF`, otherwise `nil` denotes success.
|
||||||
*/
|
*/
|
||||||
@@ -158,10 +163,11 @@ reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) {
|
|||||||
/*
|
/*
|
||||||
Decrements the Reader's index (i) by 1
|
Decrements the Reader's index (i) by 1
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- r: A pointer to a Reader struct
|
- r: A pointer to a Reader struct
|
||||||
|
|
||||||
**Returns** An `io.Error` if `r.i <= 0` (`.Invalid_Unread`), otherwise `nil` denotes success.
|
Returns:
|
||||||
|
An `io.Error` if `r.i <= 0` (`.Invalid_Unread`), otherwise `nil` denotes success.
|
||||||
*/
|
*/
|
||||||
reader_unread_byte :: proc(r: ^Reader) -> io.Error {
|
reader_unread_byte :: proc(r: ^Reader) -> io.Error {
|
||||||
if r.i <= 0 {
|
if r.i <= 0 {
|
||||||
@@ -174,10 +180,10 @@ reader_unread_byte :: proc(r: ^Reader) -> io.Error {
|
|||||||
/*
|
/*
|
||||||
Reads and returns a single rune and its `size` from the Reader's string
|
Reads and returns a single rune and its `size` from the Reader's string
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- r: A pointer to a Reader struct
|
- r: A pointer to a Reader struct
|
||||||
|
|
||||||
**Returns**
|
Returns:
|
||||||
- rr: The rune read from the Reader
|
- rr: The rune read from the Reader
|
||||||
- size: The size of the rune in bytes
|
- size: The size of the rune in bytes
|
||||||
- err: An `io.Error` if an error occurs while reading
|
- err: An `io.Error` if an error occurs while reading
|
||||||
@@ -199,12 +205,13 @@ reader_read_rune :: proc(r: ^Reader) -> (rr: rune, size: int, err: io.Error) {
|
|||||||
/*
|
/*
|
||||||
Decrements the Reader's index (i) by the size of the last read rune
|
Decrements the Reader's index (i) by the size of the last read rune
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- r: A pointer to a Reader struct
|
- r: A pointer to a Reader struct
|
||||||
|
|
||||||
WARNING: May only be used once and after a valid `read_rune` call
|
WARNING: May only be used once and after a valid `read_rune` call
|
||||||
|
|
||||||
**Returns** An `io.Error` if an error occurs while unreading (`.Invalid_Unread`), else `nil` denotes success.
|
Returns:
|
||||||
|
An `io.Error` if an error occurs while unreading (`.Invalid_Unread`), else `nil` denotes success.
|
||||||
*/
|
*/
|
||||||
reader_unread_rune :: proc(r: ^Reader) -> io.Error {
|
reader_unread_rune :: proc(r: ^Reader) -> io.Error {
|
||||||
if r.i <= 0 {
|
if r.i <= 0 {
|
||||||
@@ -220,12 +227,12 @@ reader_unread_rune :: proc(r: ^Reader) -> io.Error {
|
|||||||
/*
|
/*
|
||||||
Seeks the Reader's index to a new position
|
Seeks the Reader's index to a new position
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- r: A pointer to a Reader struct
|
- r: A pointer to a Reader struct
|
||||||
- offset: The new offset position
|
- offset: The new offset position
|
||||||
- whence: The reference point for the new position (`.Start`, `.Current`, or `.End`)
|
- whence: The reference point for the new position (`.Start`, `.Current`, or `.End`)
|
||||||
|
|
||||||
**Returns**
|
Returns:
|
||||||
- The absolute offset after seeking
|
- The absolute offset after seeking
|
||||||
- err: An `io.Error` if an error occurs while seeking (`.Invalid_Whence`, `.Invalid_Offset`)
|
- err: An `io.Error` if an error occurs while seeking (`.Invalid_Whence`, `.Invalid_Offset`)
|
||||||
*/
|
*/
|
||||||
@@ -252,13 +259,13 @@ reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.E
|
|||||||
/*
|
/*
|
||||||
Writes the remaining content of the Reader's string into the provided `io.Writer`
|
Writes the remaining content of the Reader's string into the provided `io.Writer`
|
||||||
|
|
||||||
**Inputs**
|
Inputs:
|
||||||
- r: A pointer to a Reader struct
|
- r: A pointer to a Reader struct
|
||||||
- w: The io.Writer to write the remaining content into
|
- w: The io.Writer to write the remaining content into
|
||||||
|
|
||||||
WARNING: Panics if writer writes more bytes than remainig length of string.
|
WARNING: Panics if writer writes more bytes than remainig length of string.
|
||||||
|
|
||||||
**Returns**
|
Returns:
|
||||||
- n: The number of bytes written
|
- n: The number of bytes written
|
||||||
- err: An io.Error if an error occurs while writing (`.Short_Write`)
|
- err: An io.Error if an error occurs while writing (`.Short_Write`)
|
||||||
*/
|
*/
|
||||||
|
|||||||
+409
-251
File diff suppressed because it is too large
Load Diff
@@ -145,8 +145,6 @@ PCONDITION_VARIABLE :: ^CONDITION_VARIABLE
|
|||||||
PLARGE_INTEGER :: ^LARGE_INTEGER
|
PLARGE_INTEGER :: ^LARGE_INTEGER
|
||||||
PSRWLOCK :: ^SRWLOCK
|
PSRWLOCK :: ^SRWLOCK
|
||||||
|
|
||||||
MMRESULT :: UINT
|
|
||||||
|
|
||||||
CREATE_WAITABLE_TIMER_MANUAL_RESET :: 0x00000001
|
CREATE_WAITABLE_TIMER_MANUAL_RESET :: 0x00000001
|
||||||
CREATE_WAITABLE_TIMER_HIGH_RESOLUTION :: 0x00000002
|
CREATE_WAITABLE_TIMER_HIGH_RESOLUTION :: 0x00000002
|
||||||
|
|
||||||
@@ -261,26 +259,6 @@ GET_FILEEX_INFO_LEVELS :: distinct i32
|
|||||||
GetFileExInfoStandard: GET_FILEEX_INFO_LEVELS : 0
|
GetFileExInfoStandard: GET_FILEEX_INFO_LEVELS : 0
|
||||||
GetFileExMaxInfoLevel: GET_FILEEX_INFO_LEVELS : 1
|
GetFileExMaxInfoLevel: GET_FILEEX_INFO_LEVELS : 1
|
||||||
|
|
||||||
// String resource number bases (internal use)
|
|
||||||
|
|
||||||
MMSYSERR_BASE :: 0
|
|
||||||
WAVERR_BASE :: 32
|
|
||||||
MIDIERR_BASE :: 64
|
|
||||||
TIMERR_BASE :: 96
|
|
||||||
JOYERR_BASE :: 160
|
|
||||||
MCIERR_BASE :: 256
|
|
||||||
MIXERR_BASE :: 1024
|
|
||||||
|
|
||||||
MCI_STRING_OFFSET :: 512
|
|
||||||
MCI_VD_OFFSET :: 1024
|
|
||||||
MCI_CD_OFFSET :: 1088
|
|
||||||
MCI_WAVE_OFFSET :: 1152
|
|
||||||
MCI_SEQ_OFFSET :: 1216
|
|
||||||
|
|
||||||
// timer error return values
|
|
||||||
TIMERR_NOERROR :: 0 // no error
|
|
||||||
TIMERR_NOCANDO :: TIMERR_BASE + 1 // request not completed
|
|
||||||
TIMERR_STRUCT :: TIMERR_BASE + 33 // time struct size
|
|
||||||
|
|
||||||
DIAGNOSTIC_REASON_VERSION :: 0
|
DIAGNOSTIC_REASON_VERSION :: 0
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,170 @@ package sys_windows
|
|||||||
|
|
||||||
foreign import winmm "system:Winmm.lib"
|
foreign import winmm "system:Winmm.lib"
|
||||||
|
|
||||||
|
MMRESULT :: UINT
|
||||||
|
|
||||||
@(default_calling_convention="stdcall")
|
@(default_calling_convention="stdcall")
|
||||||
foreign winmm {
|
foreign winmm {
|
||||||
|
timeGetDevCaps :: proc(ptc: LPTIMECAPS, cbtc: UINT) -> MMRESULT ---
|
||||||
timeBeginPeriod :: proc(uPeriod: UINT) -> MMRESULT ---
|
timeBeginPeriod :: proc(uPeriod: UINT) -> MMRESULT ---
|
||||||
timeEndPeriod :: proc(uPeriod: UINT) -> MMRESULT ---
|
timeEndPeriod :: proc(uPeriod: UINT) -> MMRESULT ---
|
||||||
timeGetTime :: proc() -> DWORD ---
|
timeGetTime :: proc() -> DWORD ---
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LPTIMECAPS :: ^TIMECAPS
|
||||||
|
TIMECAPS :: struct {
|
||||||
|
wPeriodMin: UINT,
|
||||||
|
wPeriodMax: UINT,
|
||||||
|
}
|
||||||
|
|
||||||
|
// String resource number bases (internal use)
|
||||||
|
MMSYSERR_BASE :: 0
|
||||||
|
WAVERR_BASE :: 32
|
||||||
|
MIDIERR_BASE :: 64
|
||||||
|
TIMERR_BASE :: 96
|
||||||
|
JOYERR_BASE :: 160
|
||||||
|
MCIERR_BASE :: 256
|
||||||
|
MIXERR_BASE :: 1024
|
||||||
|
|
||||||
|
MCI_STRING_OFFSET :: 512
|
||||||
|
MCI_VD_OFFSET :: 1024
|
||||||
|
MCI_CD_OFFSET :: 1088
|
||||||
|
MCI_WAVE_OFFSET :: 1152
|
||||||
|
MCI_SEQ_OFFSET :: 1216
|
||||||
|
|
||||||
|
/* general error return values */
|
||||||
|
MMSYSERR_NOERROR :: 0 /* no error */
|
||||||
|
MMSYSERR_ERROR :: MMSYSERR_BASE + 1 /* unspecified error */
|
||||||
|
MMSYSERR_BADDEVICEID :: MMSYSERR_BASE + 2 /* device ID out of range */
|
||||||
|
MMSYSERR_NOTENABLED :: MMSYSERR_BASE + 3 /* driver failed enable */
|
||||||
|
MMSYSERR_ALLOCATED :: MMSYSERR_BASE + 4 /* device already allocated */
|
||||||
|
MMSYSERR_INVALHANDLE :: MMSYSERR_BASE + 5 /* device handle is invalid */
|
||||||
|
MMSYSERR_NODRIVER :: MMSYSERR_BASE + 6 /* no device driver present */
|
||||||
|
MMSYSERR_NOMEM :: MMSYSERR_BASE + 7 /* memory allocation error */
|
||||||
|
MMSYSERR_NOTSUPPORTED :: MMSYSERR_BASE + 8 /* function isn't supported */
|
||||||
|
MMSYSERR_BADERRNUM :: MMSYSERR_BASE + 9 /* error value out of range */
|
||||||
|
MMSYSERR_INVALFLAG :: MMSYSERR_BASE + 10 /* invalid flag passed */
|
||||||
|
MMSYSERR_INVALPARAM :: MMSYSERR_BASE + 11 /* invalid parameter passed */
|
||||||
|
MMSYSERR_HANDLEBUSY :: MMSYSERR_BASE + 12 /* handle being used simultaneously on another thread (eg callback) */
|
||||||
|
MMSYSERR_INVALIDALIAS :: MMSYSERR_BASE + 13 /* specified alias not found */
|
||||||
|
MMSYSERR_BADDB :: MMSYSERR_BASE + 14 /* bad registry database */
|
||||||
|
MMSYSERR_KEYNOTFOUND :: MMSYSERR_BASE + 15 /* registry key not found */
|
||||||
|
MMSYSERR_READERROR :: MMSYSERR_BASE + 16 /* registry read error */
|
||||||
|
MMSYSERR_WRITEERROR :: MMSYSERR_BASE + 17 /* registry write error */
|
||||||
|
MMSYSERR_DELETEERROR :: MMSYSERR_BASE + 18 /* registry delete error */
|
||||||
|
MMSYSERR_VALNOTFOUND :: MMSYSERR_BASE + 19 /* registry value not found */
|
||||||
|
MMSYSERR_NODRIVERCB :: MMSYSERR_BASE + 20 /* driver does not call DriverCallback */
|
||||||
|
MMSYSERR_MOREDATA :: MMSYSERR_BASE + 21 /* more data to be returned */
|
||||||
|
MMSYSERR_LASTERROR :: MMSYSERR_BASE + 21 /* last error in range */
|
||||||
|
|
||||||
|
/* waveform audio error return values */
|
||||||
|
WAVERR_BADFORMAT :: WAVERR_BASE + 0 /* unsupported wave format */
|
||||||
|
WAVERR_STILLPLAYING :: WAVERR_BASE + 1 /* still something playing */
|
||||||
|
WAVERR_UNPREPARED :: WAVERR_BASE + 2 /* header not prepared */
|
||||||
|
WAVERR_SYNC :: WAVERR_BASE + 3 /* device is synchronous */
|
||||||
|
WAVERR_LASTERROR :: WAVERR_BASE + 3 /* last error in range */
|
||||||
|
|
||||||
|
/* MIDI error return values */
|
||||||
|
MIDIERR_UNPREPARED :: MIDIERR_BASE + 0 /* header not prepared */
|
||||||
|
MIDIERR_STILLPLAYING :: MIDIERR_BASE + 1 /* still something playing */
|
||||||
|
MIDIERR_NOMAP :: MIDIERR_BASE + 2 /* no configured instruments */
|
||||||
|
MIDIERR_NOTREADY :: MIDIERR_BASE + 3 /* hardware is still busy */
|
||||||
|
MIDIERR_NODEVICE :: MIDIERR_BASE + 4 /* port no longer connected */
|
||||||
|
MIDIERR_INVALIDSETUP :: MIDIERR_BASE + 5 /* invalid MIF */
|
||||||
|
MIDIERR_BADOPENMODE :: MIDIERR_BASE + 6 /* operation unsupported w/ open mode */
|
||||||
|
MIDIERR_DONT_CONTINUE :: MIDIERR_BASE + 7 /* thru device 'eating' a message */
|
||||||
|
MIDIERR_LASTERROR :: MIDIERR_BASE + 7 /* last error in range */
|
||||||
|
|
||||||
|
/* timer error return values */
|
||||||
|
TIMERR_NOERROR :: 0 /* no error */
|
||||||
|
TIMERR_NOCANDO :: TIMERR_BASE + 1 /* request not completed */
|
||||||
|
TIMERR_STRUCT :: TIMERR_BASE + 33 /* time struct size */
|
||||||
|
|
||||||
|
/* joystick error return values */
|
||||||
|
JOYERR_NOERROR :: 0 /* no error */
|
||||||
|
JOYERR_PARMS :: JOYERR_BASE + 5 /* bad parameters */
|
||||||
|
JOYERR_NOCANDO :: JOYERR_BASE + 6 /* request not completed */
|
||||||
|
JOYERR_UNPLUGGED :: JOYERR_BASE + 7 /* joystick is unplugged */
|
||||||
|
|
||||||
|
/* MCI error return values */
|
||||||
|
MCIERR_INVALID_DEVICE_ID :: MCIERR_BASE + 1
|
||||||
|
MCIERR_UNRECOGNIZED_KEYWORD :: MCIERR_BASE + 3
|
||||||
|
MCIERR_UNRECOGNIZED_COMMAND :: MCIERR_BASE + 5
|
||||||
|
MCIERR_HARDWARE :: MCIERR_BASE + 6
|
||||||
|
MCIERR_INVALID_DEVICE_NAME :: MCIERR_BASE + 7
|
||||||
|
MCIERR_OUT_OF_MEMORY :: MCIERR_BASE + 8
|
||||||
|
MCIERR_DEVICE_OPEN :: MCIERR_BASE + 9
|
||||||
|
MCIERR_CANNOT_LOAD_DRIVER :: MCIERR_BASE + 10
|
||||||
|
MCIERR_MISSING_COMMAND_STRING :: MCIERR_BASE + 11
|
||||||
|
MCIERR_PARAM_OVERFLOW :: MCIERR_BASE + 12
|
||||||
|
MCIERR_MISSING_STRING_ARGUMENT :: MCIERR_BASE + 13
|
||||||
|
MCIERR_BAD_INTEGER :: MCIERR_BASE + 14
|
||||||
|
MCIERR_PARSER_INTERNAL :: MCIERR_BASE + 15
|
||||||
|
MCIERR_DRIVER_INTERNAL :: MCIERR_BASE + 16
|
||||||
|
MCIERR_MISSING_PARAMETER :: MCIERR_BASE + 17
|
||||||
|
MCIERR_UNSUPPORTED_FUNCTION :: MCIERR_BASE + 18
|
||||||
|
MCIERR_FILE_NOT_FOUND :: MCIERR_BASE + 19
|
||||||
|
MCIERR_DEVICE_NOT_READY :: MCIERR_BASE + 20
|
||||||
|
MCIERR_INTERNAL :: MCIERR_BASE + 21
|
||||||
|
MCIERR_DRIVER :: MCIERR_BASE + 22
|
||||||
|
MCIERR_CANNOT_USE_ALL :: MCIERR_BASE + 23
|
||||||
|
MCIERR_MULTIPLE :: MCIERR_BASE + 24
|
||||||
|
MCIERR_EXTENSION_NOT_FOUND :: MCIERR_BASE + 25
|
||||||
|
MCIERR_OUTOFRANGE :: MCIERR_BASE + 26
|
||||||
|
MCIERR_FLAGS_NOT_COMPATIBLE :: MCIERR_BASE + 28
|
||||||
|
MCIERR_FILE_NOT_SAVED :: MCIERR_BASE + 30
|
||||||
|
MCIERR_DEVICE_TYPE_REQUIRED :: MCIERR_BASE + 31
|
||||||
|
MCIERR_DEVICE_LOCKED :: MCIERR_BASE + 32
|
||||||
|
MCIERR_DUPLICATE_ALIAS :: MCIERR_BASE + 33
|
||||||
|
MCIERR_BAD_CONSTANT :: MCIERR_BASE + 34
|
||||||
|
MCIERR_MUST_USE_SHAREABLE :: MCIERR_BASE + 35
|
||||||
|
MCIERR_MISSING_DEVICE_NAME :: MCIERR_BASE + 36
|
||||||
|
MCIERR_BAD_TIME_FORMAT :: MCIERR_BASE + 37
|
||||||
|
MCIERR_NO_CLOSING_QUOTE :: MCIERR_BASE + 38
|
||||||
|
MCIERR_DUPLICATE_FLAGS :: MCIERR_BASE + 39
|
||||||
|
MCIERR_INVALID_FILE :: MCIERR_BASE + 40
|
||||||
|
MCIERR_NULL_PARAMETER_BLOCK :: MCIERR_BASE + 41
|
||||||
|
MCIERR_UNNAMED_RESOURCE :: MCIERR_BASE + 42
|
||||||
|
MCIERR_NEW_REQUIRES_ALIAS :: MCIERR_BASE + 43
|
||||||
|
MCIERR_NOTIFY_ON_AUTO_OPEN :: MCIERR_BASE + 44
|
||||||
|
MCIERR_NO_ELEMENT_ALLOWED :: MCIERR_BASE + 45
|
||||||
|
MCIERR_NONAPPLICABLE_FUNCTION :: MCIERR_BASE + 46
|
||||||
|
MCIERR_ILLEGAL_FOR_AUTO_OPEN :: MCIERR_BASE + 47
|
||||||
|
MCIERR_FILENAME_REQUIRED :: MCIERR_BASE + 48
|
||||||
|
MCIERR_EXTRA_CHARACTERS :: MCIERR_BASE + 49
|
||||||
|
MCIERR_DEVICE_NOT_INSTALLED :: MCIERR_BASE + 50
|
||||||
|
MCIERR_GET_CD :: MCIERR_BASE + 51
|
||||||
|
MCIERR_SET_CD :: MCIERR_BASE + 52
|
||||||
|
MCIERR_SET_DRIVE :: MCIERR_BASE + 53
|
||||||
|
MCIERR_DEVICE_LENGTH :: MCIERR_BASE + 54
|
||||||
|
MCIERR_DEVICE_ORD_LENGTH :: MCIERR_BASE + 55
|
||||||
|
MCIERR_NO_INTEGER :: MCIERR_BASE + 56
|
||||||
|
MCIERR_WAVE_OUTPUTSINUSE :: MCIERR_BASE + 64
|
||||||
|
MCIERR_WAVE_SETOUTPUTINUSE :: MCIERR_BASE + 65
|
||||||
|
MCIERR_WAVE_INPUTSINUSE :: MCIERR_BASE + 66
|
||||||
|
MCIERR_WAVE_SETINPUTINUSE :: MCIERR_BASE + 67
|
||||||
|
MCIERR_WAVE_OUTPUTUNSPECIFIED :: MCIERR_BASE + 68
|
||||||
|
MCIERR_WAVE_INPUTUNSPECIFIED :: MCIERR_BASE + 69
|
||||||
|
MCIERR_WAVE_OUTPUTSUNSUITABLE :: MCIERR_BASE + 70
|
||||||
|
MCIERR_WAVE_SETOUTPUTUNSUITABLE :: MCIERR_BASE + 71
|
||||||
|
MCIERR_WAVE_INPUTSUNSUITABLE :: MCIERR_BASE + 72
|
||||||
|
MCIERR_WAVE_SETINPUTUNSUITABLE :: MCIERR_BASE + 73
|
||||||
|
MCIERR_SEQ_DIV_INCOMPATIBLE :: MCIERR_BASE + 80
|
||||||
|
MCIERR_SEQ_PORT_INUSE :: MCIERR_BASE + 81
|
||||||
|
MCIERR_SEQ_PORT_NONEXISTENT :: MCIERR_BASE + 82
|
||||||
|
MCIERR_SEQ_PORT_MAPNODEVICE :: MCIERR_BASE + 83
|
||||||
|
MCIERR_SEQ_PORT_MISCERROR :: MCIERR_BASE + 84
|
||||||
|
MCIERR_SEQ_TIMER :: MCIERR_BASE + 85
|
||||||
|
MCIERR_SEQ_PORTUNSPECIFIED :: MCIERR_BASE + 86
|
||||||
|
MCIERR_SEQ_NOMIDIPRESENT :: MCIERR_BASE + 87
|
||||||
|
MCIERR_NO_WINDOW :: MCIERR_BASE + 90
|
||||||
|
MCIERR_CREATEWINDOW :: MCIERR_BASE + 91
|
||||||
|
MCIERR_FILE_READ :: MCIERR_BASE + 92
|
||||||
|
MCIERR_FILE_WRITE :: MCIERR_BASE + 93
|
||||||
|
MCIERR_NO_IDENTITY :: MCIERR_BASE + 94
|
||||||
|
|
||||||
|
/* MMRESULT error return values specific to the mixer API */
|
||||||
|
MIXERR_INVALLINE :: (MIXERR_BASE + 0)
|
||||||
|
MIXERR_INVALCONTROL :: (MIXERR_BASE + 1)
|
||||||
|
MIXERR_INVALVALUE :: (MIXERR_BASE + 2)
|
||||||
|
MIXERR_LASTERROR :: (MIXERR_BASE + 2)
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
package table implements ascii/markdown/html/custom rendering of tables.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Custom rendering example:
|
||||||
|
|
||||||
|
```odin
|
||||||
|
tbl := init(&Table{})
|
||||||
|
padding(tbl, 0, 1)
|
||||||
|
row(tbl, "A_LONG_ENUM", "= 54,", "// A comment about A_LONG_ENUM")
|
||||||
|
row(tbl, "AN_EVEN_LONGER_ENUM", "= 1,", "// A comment about AN_EVEN_LONGER_ENUM")
|
||||||
|
build(tbl)
|
||||||
|
for row in 0..<tbl.nr_rows {
|
||||||
|
for col in 0..<tbl.nr_cols {
|
||||||
|
write_table_cell(stdio_writer(), tbl, row, col)
|
||||||
|
}
|
||||||
|
io.write_byte(stdio_writer(), '\n')
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This outputs:
|
||||||
|
```
|
||||||
|
A_LONG_ENUM = 54, // A comment about A_LONG_ENUM
|
||||||
|
AN_EVEN_LONGER_ENUM = 1, // A comment about AN_EVEN_LONGER_ENUM
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
ASCII rendering example:
|
||||||
|
|
||||||
|
```odin
|
||||||
|
tbl := init(&Table{})
|
||||||
|
defer destroy(tbl)
|
||||||
|
|
||||||
|
caption(tbl, "This is a table caption and it is very long")
|
||||||
|
|
||||||
|
padding(tbl, 1, 1) // Left/right padding of cells
|
||||||
|
|
||||||
|
header(tbl, "AAAAAAAAA", "B")
|
||||||
|
header(tbl, "C") // Appends to previous header row. Same as if done header("AAAAAAAAA", "B", "C") from start.
|
||||||
|
|
||||||
|
// Create a row with two values. Since there are three columns the third
|
||||||
|
// value will become the empty string.
|
||||||
|
//
|
||||||
|
// NOTE: header() is not allowed anymore after this.
|
||||||
|
row(tbl, 123, "foo")
|
||||||
|
|
||||||
|
// Use `format()` if you need custom formatting. This will allocate into
|
||||||
|
// the arena specified at init.
|
||||||
|
row(tbl,
|
||||||
|
format(tbl, "%09d", 5),
|
||||||
|
format(tbl, "%.6f", 6.28318530717958647692528676655900576))
|
||||||
|
|
||||||
|
// A row with zero values is allowed as long as a previous row or header
|
||||||
|
// exist. The value and alignment of each cell can then be set
|
||||||
|
// individually.
|
||||||
|
row(tbl)
|
||||||
|
set_cell_value_and_alignment(tbl, last_row(tbl), 0, "a", .Center)
|
||||||
|
set_cell_value(tbl, last_row(tbl), 1, "bbb")
|
||||||
|
set_cell_value(tbl, last_row(tbl), 2, "c")
|
||||||
|
|
||||||
|
// Headers are regular cells, too. Use header_row() as row index to modify
|
||||||
|
// header cells.
|
||||||
|
set_cell_alignment(tbl, header_row(tbl), 1, .Center) // Sets alignment of 'B' column to Center.
|
||||||
|
set_cell_alignment(tbl, header_row(tbl), 2, .Right) // Sets alignment of 'C' column to Right.
|
||||||
|
|
||||||
|
build(tbl)
|
||||||
|
|
||||||
|
write_ascii_table(stdio_writer(), tbl)
|
||||||
|
write_markdown_table(stdio_writer(), tbl)
|
||||||
|
```
|
||||||
|
|
||||||
|
This outputs:
|
||||||
|
```
|
||||||
|
+-----------------------------------------------+
|
||||||
|
| This is a table caption and it is very long |
|
||||||
|
+------------------+-----------------+----------+
|
||||||
|
| AAAAAAAAA | B | C |
|
||||||
|
+------------------+-----------------+----------+
|
||||||
|
| 123 | foo | |
|
||||||
|
| 000000005 | 6.283185 | |
|
||||||
|
| a | bbb | c |
|
||||||
|
+------------------+-----------------+----------+
|
||||||
|
```
|
||||||
|
|
||||||
|
and
|
||||||
|
|
||||||
|
```
|
||||||
|
| AAAAAAAAA | B | C |
|
||||||
|
|:-----------------|:---------------:|---------:|
|
||||||
|
| 123 | foo | |
|
||||||
|
| 000000005 | 6.283185 | |
|
||||||
|
| a | bbb | c |
|
||||||
|
```
|
||||||
|
|
||||||
|
respectively.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package text_table
|
||||||
@@ -0,0 +1,384 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2023 oskarnp <oskarnp@proton.me>
|
||||||
|
Made available under Odin's BSD-3 license.
|
||||||
|
|
||||||
|
List of contributors:
|
||||||
|
oskarnp: Initial implementation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package text_table
|
||||||
|
|
||||||
|
import "core:io"
|
||||||
|
import "core:os"
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:mem"
|
||||||
|
import "core:mem/virtual"
|
||||||
|
import "core:runtime"
|
||||||
|
import "core:strings"
|
||||||
|
|
||||||
|
Cell :: struct {
|
||||||
|
text: string,
|
||||||
|
alignment: Cell_Alignment,
|
||||||
|
}
|
||||||
|
|
||||||
|
Cell_Alignment :: enum {
|
||||||
|
Left,
|
||||||
|
Center,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
|
Table :: struct {
|
||||||
|
lpad, rpad: int, // Cell padding (left/right)
|
||||||
|
cells: [dynamic]Cell,
|
||||||
|
caption: string,
|
||||||
|
nr_rows, nr_cols: int,
|
||||||
|
has_header_row: bool,
|
||||||
|
table_allocator: runtime.Allocator, // Used for allocating cells/colw
|
||||||
|
format_allocator: runtime.Allocator, // Used for allocating Cell.text when applicable
|
||||||
|
|
||||||
|
dirty: bool, // True if build() needs to be called before rendering
|
||||||
|
|
||||||
|
// The following are computed on build()
|
||||||
|
colw: [dynamic]int, // Width of each column (including padding, excluding borders)
|
||||||
|
tblw: int, // Width of entire table (including padding, excluding borders)
|
||||||
|
}
|
||||||
|
|
||||||
|
init :: proc{init_with_allocator, init_with_virtual_arena, init_with_mem_arena}
|
||||||
|
|
||||||
|
init_with_allocator :: proc(tbl: ^Table, format_allocator := context.temp_allocator, table_allocator := context.allocator) -> ^Table {
|
||||||
|
tbl.table_allocator = table_allocator
|
||||||
|
tbl.cells = make([dynamic]Cell, tbl.table_allocator)
|
||||||
|
tbl.colw = make([dynamic]int, tbl.table_allocator)
|
||||||
|
tbl.format_allocator = format_allocator
|
||||||
|
return tbl
|
||||||
|
}
|
||||||
|
init_with_virtual_arena :: proc(tbl: ^Table, format_arena: ^virtual.Arena, table_allocator := context.allocator) -> ^Table {
|
||||||
|
return init_with_allocator(tbl, virtual.arena_allocator(format_arena), table_allocator)
|
||||||
|
}
|
||||||
|
init_with_mem_arena :: proc(tbl: ^Table, format_arena: ^mem.Arena, table_allocator := context.allocator) -> ^Table {
|
||||||
|
return init_with_allocator(tbl, mem.arena_allocator(format_arena), table_allocator)
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy :: proc(tbl: ^Table) {
|
||||||
|
free_all(tbl.format_allocator)
|
||||||
|
delete(tbl.cells)
|
||||||
|
delete(tbl.colw)
|
||||||
|
}
|
||||||
|
|
||||||
|
caption :: proc(tbl: ^Table, value: string) {
|
||||||
|
tbl.caption = value
|
||||||
|
tbl.dirty = true
|
||||||
|
}
|
||||||
|
|
||||||
|
padding :: proc(tbl: ^Table, lpad, rpad: int) {
|
||||||
|
tbl.lpad = lpad
|
||||||
|
tbl.rpad = rpad
|
||||||
|
tbl.dirty = true
|
||||||
|
}
|
||||||
|
|
||||||
|
get_cell :: proc(tbl: ^Table, row, col: int, loc := #caller_location) -> ^Cell {
|
||||||
|
assert(col >= 0 && col < tbl.nr_cols, "cell column out of range", loc)
|
||||||
|
assert(row >= 0 && row < tbl.nr_rows, "cell row out of range", loc)
|
||||||
|
resize(&tbl.cells, tbl.nr_cols * tbl.nr_rows)
|
||||||
|
return &tbl.cells[row*tbl.nr_cols + col]
|
||||||
|
}
|
||||||
|
|
||||||
|
set_cell_value_and_alignment :: proc(tbl: ^Table, row, col: int, value: string, alignment: Cell_Alignment) {
|
||||||
|
cell := get_cell(tbl, row, col)
|
||||||
|
cell.text = format(tbl, "%v", value)
|
||||||
|
cell.alignment = alignment
|
||||||
|
tbl.dirty = true
|
||||||
|
}
|
||||||
|
|
||||||
|
set_cell_value :: proc(tbl: ^Table, row, col: int, value: any, loc := #caller_location) {
|
||||||
|
cell := get_cell(tbl, row, col, loc)
|
||||||
|
switch val in value {
|
||||||
|
case nil:
|
||||||
|
cell.text = ""
|
||||||
|
case string:
|
||||||
|
cell.text = string(val)
|
||||||
|
case cstring:
|
||||||
|
cell.text = string(val)
|
||||||
|
case:
|
||||||
|
cell.text = format(tbl, "%v", val)
|
||||||
|
if cell.text == "" {
|
||||||
|
fmt.eprintf("{} text/table: format() resulted in empty string (arena out of memory?)\n", loc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tbl.dirty = true
|
||||||
|
}
|
||||||
|
|
||||||
|
set_cell_alignment :: proc(tbl: ^Table, row, col: int, alignment: Cell_Alignment, loc := #caller_location) {
|
||||||
|
cell := get_cell(tbl, row, col, loc)
|
||||||
|
cell.alignment = alignment
|
||||||
|
tbl.dirty = true
|
||||||
|
}
|
||||||
|
|
||||||
|
format :: proc(tbl: ^Table, _fmt: string, args: ..any, loc := #caller_location) -> string {
|
||||||
|
context.allocator = tbl.format_allocator
|
||||||
|
return fmt.aprintf(fmt = _fmt, args = args)
|
||||||
|
}
|
||||||
|
|
||||||
|
header :: proc(tbl: ^Table, values: ..any, loc := #caller_location) {
|
||||||
|
if (tbl.has_header_row && tbl.nr_rows != 1) || (!tbl.has_header_row && tbl.nr_rows != 0) {
|
||||||
|
panic("Cannot add headers after rows have been added", loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tbl.nr_rows == 0 {
|
||||||
|
tbl.nr_rows += 1
|
||||||
|
tbl.has_header_row = true
|
||||||
|
}
|
||||||
|
|
||||||
|
col := tbl.nr_cols
|
||||||
|
tbl.nr_cols += len(values)
|
||||||
|
for val in values {
|
||||||
|
set_cell_value(tbl, header_row(tbl), col, val, loc)
|
||||||
|
col += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
tbl.dirty = true
|
||||||
|
}
|
||||||
|
|
||||||
|
row :: proc(tbl: ^Table, values: ..any, loc := #caller_location) {
|
||||||
|
if tbl.nr_cols == 0 {
|
||||||
|
if len(values) == 0 {
|
||||||
|
panic("Cannot create row without values unless knowing amount of columns in advance")
|
||||||
|
} else {
|
||||||
|
tbl.nr_cols = len(values)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tbl.nr_rows += 1
|
||||||
|
for col in 0..<tbl.nr_cols {
|
||||||
|
val := values[col] if col < len(values) else nil
|
||||||
|
set_cell_value(tbl, last_row(tbl), col, val)
|
||||||
|
}
|
||||||
|
tbl.dirty = true
|
||||||
|
}
|
||||||
|
|
||||||
|
last_row :: proc(tbl: ^Table) -> int {
|
||||||
|
return tbl.nr_rows - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
header_row :: proc(tbl: ^Table) -> int {
|
||||||
|
return 0 if tbl.has_header_row else -1
|
||||||
|
}
|
||||||
|
|
||||||
|
first_row :: proc(tbl: ^Table) -> int {
|
||||||
|
return header_row(tbl)+1 if tbl.has_header_row else 0
|
||||||
|
}
|
||||||
|
|
||||||
|
build :: proc(tbl: ^Table) {
|
||||||
|
tbl.dirty = false
|
||||||
|
|
||||||
|
resize(&tbl.colw, tbl.nr_cols)
|
||||||
|
mem.zero_slice(tbl.colw[:])
|
||||||
|
|
||||||
|
for row in 0..<tbl.nr_rows {
|
||||||
|
for col in 0..<tbl.nr_cols {
|
||||||
|
cell := get_cell(tbl, row, col)
|
||||||
|
if w := len(cell.text) + tbl.lpad + tbl.rpad; w > tbl.colw[col] {
|
||||||
|
tbl.colw[col] = w
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
colw_sum := 0
|
||||||
|
for v in tbl.colw {
|
||||||
|
colw_sum += v
|
||||||
|
}
|
||||||
|
|
||||||
|
tbl.tblw = max(colw_sum, len(tbl.caption) + tbl.lpad + tbl.rpad)
|
||||||
|
|
||||||
|
// Resize columns to match total width of table
|
||||||
|
remain := tbl.tblw-colw_sum
|
||||||
|
for col := 0; remain > 0; col = (col + 1) % tbl.nr_cols {
|
||||||
|
tbl.colw[col] += 1
|
||||||
|
remain -= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
write_html_table :: proc(w: io.Writer, tbl: ^Table) {
|
||||||
|
if tbl.dirty {
|
||||||
|
build(tbl)
|
||||||
|
}
|
||||||
|
|
||||||
|
io.write_string(w, "<table>\n")
|
||||||
|
if tbl.caption != "" {
|
||||||
|
io.write_string(w, "<caption>")
|
||||||
|
io.write_string(w, tbl.caption)
|
||||||
|
io.write_string(w, "</caption>\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
align_attribute :: proc(cell: ^Cell) -> string {
|
||||||
|
switch cell.alignment {
|
||||||
|
case .Left: return ` align="left"`
|
||||||
|
case .Center: return ` align="center"`
|
||||||
|
case .Right: return ` align="right"`
|
||||||
|
}
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
|
||||||
|
if tbl.has_header_row {
|
||||||
|
io.write_string(w, "<thead>\n")
|
||||||
|
io.write_string(w, " <tr>\n")
|
||||||
|
for col in 0..<tbl.nr_cols {
|
||||||
|
cell := get_cell(tbl, header_row(tbl), col)
|
||||||
|
io.write_string(w, " <th")
|
||||||
|
io.write_string(w, align_attribute(cell))
|
||||||
|
io.write_string(w, ">")
|
||||||
|
io.write_string(w, cell.text)
|
||||||
|
io.write_string(w, "</th>\n")
|
||||||
|
}
|
||||||
|
io.write_string(w, " </tr>\n")
|
||||||
|
io.write_string(w, "</thead>\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
io.write_string(w, "<tbody>\n")
|
||||||
|
for row in 0..<tbl.nr_rows {
|
||||||
|
if tbl.has_header_row && row == header_row(tbl) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
io.write_string(w, " <tr>\n")
|
||||||
|
for col in 0..<tbl.nr_cols {
|
||||||
|
cell := get_cell(tbl, row, col)
|
||||||
|
io.write_string(w, " <td")
|
||||||
|
io.write_string(w, align_attribute(cell))
|
||||||
|
io.write_string(w, ">")
|
||||||
|
io.write_string(w, cell.text)
|
||||||
|
io.write_string(w, "</td>\n")
|
||||||
|
}
|
||||||
|
io.write_string(w, " </tr>\n")
|
||||||
|
}
|
||||||
|
io.write_string(w, " </tbody>\n")
|
||||||
|
|
||||||
|
io.write_string(w, "</table>\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
write_ascii_table :: proc(w: io.Writer, tbl: ^Table) {
|
||||||
|
if tbl.dirty {
|
||||||
|
build(tbl)
|
||||||
|
}
|
||||||
|
|
||||||
|
write_caption_separator :: proc(w: io.Writer, tbl: ^Table) {
|
||||||
|
io.write_byte(w, '+')
|
||||||
|
write_byte_repeat(w, tbl.tblw + tbl.nr_cols - 1, '-')
|
||||||
|
io.write_byte(w, '+')
|
||||||
|
io.write_byte(w, '\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
write_table_separator :: proc(w: io.Writer, tbl: ^Table) {
|
||||||
|
for col in 0..<tbl.nr_cols {
|
||||||
|
if col == 0 {
|
||||||
|
io.write_byte(w, '+')
|
||||||
|
}
|
||||||
|
write_byte_repeat(w, tbl.colw[col], '-')
|
||||||
|
io.write_byte(w, '+')
|
||||||
|
}
|
||||||
|
io.write_byte(w, '\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
if tbl.caption != "" {
|
||||||
|
write_caption_separator(w, tbl)
|
||||||
|
io.write_byte(w, '|')
|
||||||
|
write_text_align(w, tbl.tblw - tbl.lpad - tbl.rpad + tbl.nr_cols - 1,
|
||||||
|
tbl.lpad, tbl.rpad, tbl.caption, .Center)
|
||||||
|
io.write_byte(w, '|')
|
||||||
|
io.write_byte(w, '\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
write_table_separator(w, tbl)
|
||||||
|
for row in 0..<tbl.nr_rows {
|
||||||
|
for col in 0..<tbl.nr_cols {
|
||||||
|
if col == 0 {
|
||||||
|
io.write_byte(w, '|')
|
||||||
|
}
|
||||||
|
write_table_cell(w, tbl, row, col)
|
||||||
|
io.write_byte(w, '|')
|
||||||
|
}
|
||||||
|
io.write_byte(w, '\n')
|
||||||
|
if tbl.has_header_row && row == header_row(tbl) {
|
||||||
|
write_table_separator(w, tbl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
write_table_separator(w, tbl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Renders table according to GitHub Flavored Markdown (GFM) specification
|
||||||
|
write_markdown_table :: proc(w: io.Writer, tbl: ^Table) {
|
||||||
|
// NOTE(oskar): Captions or colspans are not supported by GFM as far as I can tell.
|
||||||
|
|
||||||
|
if tbl.dirty {
|
||||||
|
build(tbl)
|
||||||
|
}
|
||||||
|
|
||||||
|
for row in 0..<tbl.nr_rows {
|
||||||
|
for col in 0..<tbl.nr_cols {
|
||||||
|
cell := get_cell(tbl, row, col)
|
||||||
|
if col == 0 {
|
||||||
|
io.write_byte(w, '|')
|
||||||
|
}
|
||||||
|
write_text_align(w, tbl.colw[col] - tbl.lpad - tbl.rpad, tbl.lpad, tbl.rpad, cell.text,
|
||||||
|
.Center if tbl.has_header_row && row == header_row(tbl) else .Left)
|
||||||
|
io.write_string(w, "|")
|
||||||
|
}
|
||||||
|
io.write_byte(w, '\n')
|
||||||
|
|
||||||
|
if tbl.has_header_row && row == header_row(tbl) {
|
||||||
|
for col in 0..<tbl.nr_cols {
|
||||||
|
cell := get_cell(tbl, row, col)
|
||||||
|
if col == 0 {
|
||||||
|
io.write_byte(w, '|')
|
||||||
|
}
|
||||||
|
switch cell.alignment {
|
||||||
|
case .Left:
|
||||||
|
io.write_byte(w, ':')
|
||||||
|
write_byte_repeat(w, max(1, tbl.colw[col]-1), '-')
|
||||||
|
case .Center:
|
||||||
|
io.write_byte(w, ':')
|
||||||
|
write_byte_repeat(w, max(1, tbl.colw[col]-2), '-')
|
||||||
|
io.write_byte(w, ':')
|
||||||
|
case .Right:
|
||||||
|
write_byte_repeat(w, max(1, tbl.colw[col]-1), '-')
|
||||||
|
io.write_byte(w, ':')
|
||||||
|
}
|
||||||
|
io.write_byte(w, '|')
|
||||||
|
}
|
||||||
|
io.write_byte(w, '\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
write_byte_repeat :: proc(w: io.Writer, n: int, b: byte) {
|
||||||
|
for _ in 0..<n {
|
||||||
|
io.write_byte(w, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
write_table_cell :: proc(w: io.Writer, tbl: ^Table, row, col: int) {
|
||||||
|
if tbl.dirty {
|
||||||
|
build(tbl)
|
||||||
|
}
|
||||||
|
cell := get_cell(tbl, row, col)
|
||||||
|
write_text_align(w, tbl.colw[col]-tbl.lpad-tbl.rpad, tbl.lpad, tbl.rpad, cell.text, cell.alignment)
|
||||||
|
}
|
||||||
|
|
||||||
|
write_text_align :: proc(w: io.Writer, colw, lpad, rpad: int, text: string, alignment: Cell_Alignment) {
|
||||||
|
write_byte_repeat(w, lpad, ' ')
|
||||||
|
switch alignment {
|
||||||
|
case .Left:
|
||||||
|
io.write_string(w, text)
|
||||||
|
write_byte_repeat(w, colw - len(text), ' ')
|
||||||
|
case .Center:
|
||||||
|
pad := colw - len(text)
|
||||||
|
odd := pad & 1 != 0
|
||||||
|
write_byte_repeat(w, pad/2, ' ')
|
||||||
|
io.write_string(w, text)
|
||||||
|
write_byte_repeat(w, pad/2 + 1 if odd else pad/2, ' ')
|
||||||
|
case .Right:
|
||||||
|
write_byte_repeat(w, colw - len(text), ' ')
|
||||||
|
io.write_string(w, text)
|
||||||
|
}
|
||||||
|
write_byte_repeat(w, rpad, ' ')
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package text_table
|
||||||
|
|
||||||
|
import "core:io"
|
||||||
|
import "core:os"
|
||||||
|
import "core:strings"
|
||||||
|
|
||||||
|
stdio_writer :: proc() -> io.Writer {
|
||||||
|
return io.to_writer(os.stream_from_handle(os.stdout))
|
||||||
|
}
|
||||||
|
|
||||||
|
strings_builder_writer :: proc(b: ^strings.Builder) -> io.Writer {
|
||||||
|
return strings.to_writer(b)
|
||||||
|
}
|
||||||
+3
-3
@@ -594,13 +594,13 @@ gb_internal Array<String> setup_args(int argc, char const **argv) {
|
|||||||
|
|
||||||
gb_internal void print_usage_line(i32 indent, char const *fmt, ...) {
|
gb_internal void print_usage_line(i32 indent, char const *fmt, ...) {
|
||||||
while (indent --> 0) {
|
while (indent --> 0) {
|
||||||
gb_printf_err("\t");
|
gb_printf("\t");
|
||||||
}
|
}
|
||||||
va_list va;
|
va_list va;
|
||||||
va_start(va, fmt);
|
va_start(va, fmt);
|
||||||
gb_printf_err_va(fmt, va);
|
gb_printf_va(fmt, va);
|
||||||
va_end(va);
|
va_end(va);
|
||||||
gb_printf_err("\n");
|
gb_printf("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
gb_internal void usage(String argv0) {
|
gb_internal void usage(String argv0) {
|
||||||
|
|||||||
+5
-3
@@ -3693,9 +3693,11 @@ gb_internal bool allow_field_separator(AstFile *f) {
|
|||||||
if (allow_token(f, Token_Comma)) {
|
if (allow_token(f, Token_Comma)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (ALLOW_NEWLINE && token.kind == Token_Semicolon && !token_is_newline(token)) {
|
if (ALLOW_NEWLINE && token.kind == Token_Semicolon) {
|
||||||
String p = token_to_string(token);
|
if (!token_is_newline(token)) {
|
||||||
syntax_error(token_end_of_line(f, f->prev_token), "Expected a comma, got a %.*s", LIT(p));
|
String p = token_to_string(token);
|
||||||
|
syntax_error(token_end_of_line(f, f->prev_token), "Expected a comma, got a %.*s", LIT(p));
|
||||||
|
}
|
||||||
advance_token(f);
|
advance_token(f);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-24
@@ -6,87 +6,82 @@ python3 download_assets.py
|
|||||||
echo ---
|
echo ---
|
||||||
echo Running core:image tests
|
echo Running core:image tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run image %COMMON% -out:test_core_image.exe
|
%PATH_TO_ODIN% run image %COMMON% -out:test_core_image.exe || exit /b
|
||||||
|
|
||||||
echo ---
|
echo ---
|
||||||
echo Running core:compress tests
|
echo Running core:compress tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run compress %COMMON% -out:test_core_compress.exe
|
%PATH_TO_ODIN% run compress %COMMON% -out:test_core_compress.exe || exit /b
|
||||||
|
|
||||||
echo ---
|
echo ---
|
||||||
echo Running core:strings tests
|
echo Running core:strings tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run strings %COMMON% -out:test_core_strings.exe
|
%PATH_TO_ODIN% run strings %COMMON% -out:test_core_strings.exe || exit /b
|
||||||
|
|
||||||
echo ---
|
echo ---
|
||||||
echo Running core:hash tests
|
echo Running core:hash tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run hash %COMMON% -o:size -out:test_core_hash.exe
|
%PATH_TO_ODIN% run hash %COMMON% -o:size -out:test_core_hash.exe || exit /b
|
||||||
|
|
||||||
echo ---
|
echo ---
|
||||||
echo Running core:odin tests
|
echo Running core:odin tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run odin %COMMON% -o:size -out:test_core_odin.exe
|
%PATH_TO_ODIN% run odin %COMMON% -o:size -out:test_core_odin.exe || exit /b
|
||||||
|
|
||||||
echo ---
|
echo ---
|
||||||
echo Running core:crypto hash tests
|
echo Running core:crypto hash tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run crypto %COMMON% -out:test_crypto_hash.exe
|
%PATH_TO_ODIN% run crypto %COMMON% -out:test_crypto_hash.exe || exit /b
|
||||||
|
|
||||||
echo ---
|
echo ---
|
||||||
echo Running core:encoding tests
|
echo Running core:encoding tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run encoding/hxa %COMMON% %COLLECTION% -out:test_hxa.exe
|
%PATH_TO_ODIN% run encoding/hxa %COMMON% %COLLECTION% -out:test_hxa.exe || exit /b
|
||||||
%PATH_TO_ODIN% run encoding/json %COMMON% -out:test_json.exe
|
%PATH_TO_ODIN% run encoding/json %COMMON% -out:test_json.exe || exit /b
|
||||||
%PATH_TO_ODIN% run encoding/varint %COMMON% -out:test_varint.exe
|
%PATH_TO_ODIN% run encoding/varint %COMMON% -out:test_varint.exe || exit /b
|
||||||
%PATH_TO_ODIN% run encoding/xml %COMMON% -out:test_xml.exe
|
%PATH_TO_ODIN% run encoding/xml %COMMON% -out:test_xml.exe || exit /b
|
||||||
|
|
||||||
echo ---
|
echo ---
|
||||||
echo Running core:math/noise tests
|
echo Running core:math/noise tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run math/noise %COMMON% -out:test_noise.exe
|
%PATH_TO_ODIN% run math/noise %COMMON% -out:test_noise.exe || exit /b
|
||||||
|
|
||||||
echo ---
|
echo ---
|
||||||
echo Running core:math tests
|
echo Running core:math tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run math %COMMON% %COLLECTION% -out:test_core_math.exe
|
%PATH_TO_ODIN% run math %COMMON% %COLLECTION% -out:test_core_math.exe || exit /b
|
||||||
|
|
||||||
echo ---
|
echo ---
|
||||||
echo Running core:math/linalg/glsl tests
|
echo Running core:math/linalg/glsl tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run math/linalg/glsl %COMMON% %COLLECTION% -out:test_linalg_glsl.exe
|
%PATH_TO_ODIN% run math/linalg/glsl %COMMON% %COLLECTION% -out:test_linalg_glsl.exe || exit /b
|
||||||
|
|
||||||
echo ---
|
echo ---
|
||||||
echo Running core:path/filepath tests
|
echo Running core:path/filepath tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run path/filepath %COMMON% %COLLECTION% -out:test_core_filepath.exe
|
%PATH_TO_ODIN% run path/filepath %COMMON% %COLLECTION% -out:test_core_filepath.exe || exit /b
|
||||||
|
|
||||||
echo ---
|
echo ---
|
||||||
echo Running core:reflect tests
|
echo Running core:reflect tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run reflect %COMMON% %COLLECTION% -out:test_core_reflect.exe
|
%PATH_TO_ODIN% run reflect %COMMON% %COLLECTION% -out:test_core_reflect.exe || exit /b
|
||||||
|
|
||||||
echo ---
|
echo ---
|
||||||
echo Running core:text/i18n tests
|
echo Running core:text/i18n tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run text\i18n %COMMON% -out:test_core_i18n.exe
|
%PATH_TO_ODIN% run text\i18n %COMMON% -out:test_core_i18n.exe || exit /b
|
||||||
|
|
||||||
echo ---
|
echo ---
|
||||||
echo Running core:net
|
echo Running core:net
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run net %COMMON% -out:test_core_net.exe
|
%PATH_TO_ODIN% run net %COMMON% -out:test_core_net.exe || exit /b
|
||||||
|
|
||||||
echo ---
|
|
||||||
echo Running core:text/lua tests
|
|
||||||
echo ---
|
|
||||||
%PATH_TO_ODIN% run text\lua %COMMON% -out:test_core_lua_strlib.exe
|
|
||||||
|
|
||||||
echo ---
|
echo ---
|
||||||
echo Running core:slice tests
|
echo Running core:slice tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run slice %COMMON% -out:test_core_slice.exe
|
%PATH_TO_ODIN% run slice %COMMON% -out:test_core_slice.exe || exit /b
|
||||||
|
|
||||||
echo ---
|
echo ---
|
||||||
echo Running core:container tests
|
echo Running core:container tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run container %COMMON% %COLLECTION% -out:test_core_container.exe
|
%PATH_TO_ODIN% run container %COMMON% %COLLECTION% -out:test_core_container.exe || exit /b
|
||||||
|
|||||||
@@ -151,6 +151,13 @@ shoco_test :: proc(t: ^testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for v in Shoco_Tests {
|
for v in Shoco_Tests {
|
||||||
|
when ODIN_OS == .Windows {
|
||||||
|
v := v
|
||||||
|
// Compressed source files are not encoded with carriage returns but git replaces raw files lf with crlf on commit (on windows only)
|
||||||
|
// So replace crlf with lf on windows
|
||||||
|
v.raw, _ = bytes.replace_all(v.raw, { 0xD, 0xA }, { 0xA })
|
||||||
|
}
|
||||||
|
|
||||||
expected_raw := len(v.raw)
|
expected_raw := len(v.raw)
|
||||||
expected_compressed := len(v.compressed)
|
expected_compressed := len(v.compressed)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
@echo off
|
||||||
|
set PATH_TO_ODIN==..\..\odin
|
||||||
|
|
||||||
|
echo ---
|
||||||
|
echo Building Documentation File
|
||||||
|
echo ---
|
||||||
|
%PATH_TO_ODIN% doc ..\..\examples\all -all-packages -doc-format || exit /b
|
||||||
|
|
||||||
|
|
||||||
|
echo ---
|
||||||
|
echo Running Documentation Tester
|
||||||
|
echo ---
|
||||||
|
%PATH_TO_ODIN% run documentation_tester.odin -file -vet -strict-style -- %PATH_TO_ODIN% || exit /b
|
||||||
@@ -0,0 +1,421 @@
|
|||||||
|
package documentation_tester
|
||||||
|
|
||||||
|
import "core:os"
|
||||||
|
import "core:io"
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:strings"
|
||||||
|
import "core:odin/ast"
|
||||||
|
import "core:odin/parser"
|
||||||
|
import "core:c/libc"
|
||||||
|
import doc "core:odin/doc-format"
|
||||||
|
|
||||||
|
Example_Test :: struct {
|
||||||
|
entity_name: string,
|
||||||
|
package_name: string,
|
||||||
|
example_code: []string,
|
||||||
|
expected_output: []string,
|
||||||
|
}
|
||||||
|
|
||||||
|
g_header: ^doc.Header
|
||||||
|
g_bad_doc: bool
|
||||||
|
g_examples_to_verify: [dynamic]Example_Test
|
||||||
|
g_path_to_odin: string
|
||||||
|
|
||||||
|
array :: proc(a: $A/doc.Array($T)) -> []T {
|
||||||
|
return doc.from_array(g_header, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
str :: proc(s: $A/doc.String) -> string {
|
||||||
|
return doc.from_string(g_header, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
common_prefix :: proc(strs: []string) -> string {
|
||||||
|
if len(strs) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
n := max(int)
|
||||||
|
for str in strs {
|
||||||
|
n = min(n, len(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
prefix := strs[0][:n]
|
||||||
|
for str in strs[1:] {
|
||||||
|
for len(prefix) != 0 && str[:len(prefix)] != prefix {
|
||||||
|
prefix = prefix[:len(prefix)-1]
|
||||||
|
}
|
||||||
|
if len(prefix) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return prefix
|
||||||
|
}
|
||||||
|
|
||||||
|
errorf :: proc(format: string, args: ..any) -> ! {
|
||||||
|
fmt.eprintf("%s ", os.args[0])
|
||||||
|
fmt.eprintf(format, ..args)
|
||||||
|
fmt.eprintln()
|
||||||
|
os.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
main :: proc() {
|
||||||
|
if len(os.args) != 2 {
|
||||||
|
errorf("expected path to odin executable")
|
||||||
|
}
|
||||||
|
g_path_to_odin = os.args[1]
|
||||||
|
data, ok := os.read_entire_file("all.odin-doc")
|
||||||
|
if !ok {
|
||||||
|
errorf("unable to read file: all.odin-doc")
|
||||||
|
}
|
||||||
|
err: doc.Reader_Error
|
||||||
|
g_header, err = doc.read_from_bytes(data)
|
||||||
|
switch err {
|
||||||
|
case .None:
|
||||||
|
case .Header_Too_Small:
|
||||||
|
errorf("file is too small for the file format")
|
||||||
|
case .Invalid_Magic:
|
||||||
|
errorf("invalid magic for the file format")
|
||||||
|
case .Data_Too_Small:
|
||||||
|
errorf("data is too small for the file format")
|
||||||
|
case .Invalid_Version:
|
||||||
|
errorf("invalid file format version")
|
||||||
|
}
|
||||||
|
pkgs := array(g_header.pkgs)
|
||||||
|
entities := array(g_header.entities)
|
||||||
|
|
||||||
|
path_prefix: string
|
||||||
|
{
|
||||||
|
fullpaths: [dynamic]string
|
||||||
|
defer delete(fullpaths)
|
||||||
|
|
||||||
|
for pkg in pkgs[1:] {
|
||||||
|
append(&fullpaths, str(pkg.fullpath))
|
||||||
|
}
|
||||||
|
path_prefix = common_prefix(fullpaths[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
for pkg in pkgs[1:] {
|
||||||
|
entries_array := array(pkg.entries)
|
||||||
|
fullpath := str(pkg.fullpath)
|
||||||
|
path := strings.trim_prefix(fullpath, path_prefix)
|
||||||
|
if ! strings.has_prefix(path, "core/") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
trimmed_path := strings.trim_prefix(path, "core/")
|
||||||
|
if strings.has_prefix(trimmed_path, "sys") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.contains(trimmed_path, "/_") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for entry in entries_array {
|
||||||
|
entity := entities[entry.entity]
|
||||||
|
find_and_add_examples(
|
||||||
|
docs = str(entity.docs),
|
||||||
|
package_name = str(pkg.name),
|
||||||
|
entity_name = str(entity.name),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
write_test_suite(g_examples_to_verify[:])
|
||||||
|
if g_bad_doc {
|
||||||
|
errorf("We created bad documentation!")
|
||||||
|
}
|
||||||
|
|
||||||
|
if ! run_test_suite() {
|
||||||
|
errorf("Test suite failed!")
|
||||||
|
}
|
||||||
|
fmt.println("Examples verified")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: this is a pretty close copy paste from the website pkg documentation on parsing the docs
|
||||||
|
find_and_add_examples :: proc(docs: string, package_name: string, entity_name: string) {
|
||||||
|
if docs == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Block_Kind :: enum {
|
||||||
|
Other,
|
||||||
|
Example,
|
||||||
|
Output,
|
||||||
|
}
|
||||||
|
Block :: struct {
|
||||||
|
kind: Block_Kind,
|
||||||
|
lines: []string,
|
||||||
|
}
|
||||||
|
lines := strings.split_lines(docs)
|
||||||
|
curr_block_kind := Block_Kind.Other
|
||||||
|
start := 0
|
||||||
|
|
||||||
|
example_block: Block // when set the kind should be Example
|
||||||
|
output_block: Block // when set the kind should be Output
|
||||||
|
// rely on zii that the kinds have not been set
|
||||||
|
assert(example_block.kind != .Example)
|
||||||
|
assert(output_block.kind != .Output)
|
||||||
|
|
||||||
|
insert_block :: proc(block: Block, example: ^Block, output: ^Block, name: string) {
|
||||||
|
switch block.kind {
|
||||||
|
case .Other:
|
||||||
|
case .Example:
|
||||||
|
if example.kind == .Example {
|
||||||
|
fmt.eprintf("The documentation for %q has multiple examples which is not allowed\n", name)
|
||||||
|
g_bad_doc = true
|
||||||
|
}
|
||||||
|
example^ = block
|
||||||
|
case .Output: output^ = block
|
||||||
|
if example.kind == .Output {
|
||||||
|
fmt.eprintf("The documentation for %q has multiple output which is not allowed\n", name)
|
||||||
|
g_bad_doc = true
|
||||||
|
}
|
||||||
|
output^ = block
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for line, i in lines {
|
||||||
|
text := strings.trim_space(line)
|
||||||
|
next_block_kind := curr_block_kind
|
||||||
|
|
||||||
|
switch curr_block_kind {
|
||||||
|
case .Other:
|
||||||
|
switch {
|
||||||
|
case strings.has_prefix(line, "Example:"): next_block_kind = .Example
|
||||||
|
case strings.has_prefix(line, "Output:"): next_block_kind = .Output
|
||||||
|
}
|
||||||
|
case .Example:
|
||||||
|
switch {
|
||||||
|
case strings.has_prefix(line, "Output:"): next_block_kind = .Output
|
||||||
|
case ! (text == "" || strings.has_prefix(line, "\t")): next_block_kind = .Other
|
||||||
|
}
|
||||||
|
case .Output:
|
||||||
|
switch {
|
||||||
|
case strings.has_prefix(line, "Example:"): next_block_kind = .Example
|
||||||
|
case ! (text == "" || strings.has_prefix(line, "\t")): next_block_kind = .Other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if i-start > 0 && (curr_block_kind != next_block_kind) {
|
||||||
|
insert_block(Block{curr_block_kind, lines[start:i]}, &example_block, &output_block, entity_name)
|
||||||
|
curr_block_kind, start = next_block_kind, i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if start < len(lines) {
|
||||||
|
insert_block(Block{curr_block_kind, lines[start:]}, &example_block, &output_block, entity_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if output_block.kind == .Output && example_block.kind != .Example {
|
||||||
|
fmt.eprintf("The documentation for %q has an output block but no example\n", entity_name)
|
||||||
|
g_bad_doc = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write example and output block if they're both present
|
||||||
|
if example_block.kind == .Example && output_block.kind == .Output {
|
||||||
|
{
|
||||||
|
// Example block starts with
|
||||||
|
// `Example:` and a number of white spaces,
|
||||||
|
lines := &example_block.lines
|
||||||
|
for len(lines) > 0 && (strings.trim_space(lines[0]) == "" || strings.has_prefix(lines[0], "Example:")) {
|
||||||
|
lines^ = lines[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Output block starts with
|
||||||
|
// `Output:` and a number of white spaces,
|
||||||
|
lines := &output_block.lines
|
||||||
|
for len(lines) > 0 && (strings.trim_space(lines[0]) == "" || strings.has_prefix(lines[0], "Output:")) {
|
||||||
|
lines^ = lines[1:]
|
||||||
|
}
|
||||||
|
// Additionally we need to strip all empty lines at the end of output to not include those in the expected output
|
||||||
|
for len(lines) > 0 && (strings.trim_space(lines[len(lines) - 1]) == "") {
|
||||||
|
lines^ = lines[:len(lines) - 1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Remove first layer of tabs which are always present
|
||||||
|
for line in &example_block.lines {
|
||||||
|
line = strings.trim_prefix(line, "\t")
|
||||||
|
}
|
||||||
|
for line in &output_block.lines {
|
||||||
|
line = strings.trim_prefix(line, "\t")
|
||||||
|
}
|
||||||
|
append(&g_examples_to_verify, Example_Test {
|
||||||
|
entity_name = entity_name,
|
||||||
|
package_name = package_name,
|
||||||
|
example_code = example_block.lines,
|
||||||
|
expected_output = output_block.lines,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
write_test_suite :: proc(example_tests: []Example_Test) {
|
||||||
|
TEST_SUITE_DIRECTORY :: "verify"
|
||||||
|
os.remove_directory(TEST_SUITE_DIRECTORY)
|
||||||
|
os.make_directory(TEST_SUITE_DIRECTORY)
|
||||||
|
|
||||||
|
example_build := strings.builder_make()
|
||||||
|
test_runner := strings.builder_make()
|
||||||
|
|
||||||
|
strings.write_string(&test_runner,
|
||||||
|
`//+private
|
||||||
|
package documentation_verification
|
||||||
|
|
||||||
|
import "core:os"
|
||||||
|
import "core:mem"
|
||||||
|
import "core:io"
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:thread"
|
||||||
|
import "core:sync"
|
||||||
|
import "core:intrinsics"
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
_read_pipe: os.Handle
|
||||||
|
@(private="file")
|
||||||
|
_write_pipe: os.Handle
|
||||||
|
@(private="file")
|
||||||
|
_pipe_reader_semaphore: sync.Sema
|
||||||
|
@(private="file")
|
||||||
|
_out_data: string
|
||||||
|
@(private="file")
|
||||||
|
_out_buffer: [mem.Megabyte]byte
|
||||||
|
@(private="file")
|
||||||
|
_bad_test_found: bool
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
_spawn_pipe_reader :: proc() {
|
||||||
|
thread.create_and_start(proc(^thread.Thread) {
|
||||||
|
stream := os.stream_from_handle(_read_pipe)
|
||||||
|
reader := io.to_reader(stream)
|
||||||
|
sync.post(&_pipe_reader_semaphore) // notify thread is ready
|
||||||
|
for {
|
||||||
|
n_read := 0
|
||||||
|
read_to_null_byte := 0
|
||||||
|
finished_reading := false
|
||||||
|
for ! finished_reading {
|
||||||
|
just_read, err := io.read(reader, _out_buffer[n_read:], &n_read); if err != .None {
|
||||||
|
panic("We got an IO error!")
|
||||||
|
}
|
||||||
|
for b in _out_buffer[n_read - just_read: n_read] {
|
||||||
|
if b == 0 {
|
||||||
|
finished_reading = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
read_to_null_byte += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intrinsics.volatile_store(&_out_data, transmute(string)_out_buffer[:read_to_null_byte])
|
||||||
|
sync.post(&_pipe_reader_semaphore) // notify we read the null byte
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
sync.wait(&_pipe_reader_semaphore) // wait for thread to be ready
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
_check :: proc(test_name: string, expected: string) {
|
||||||
|
null_byte: [1]byte
|
||||||
|
os.write(_write_pipe, null_byte[:])
|
||||||
|
os.flush(_write_pipe)
|
||||||
|
sync.wait(&_pipe_reader_semaphore)
|
||||||
|
output := intrinsics.volatile_load(&_out_data) // wait for thread to read null byte
|
||||||
|
if expected != output {
|
||||||
|
fmt.eprintf("Test %q got unexpected output:\n%q\n", test_name, output)
|
||||||
|
fmt.eprintf("Expected:\n%q\n", expected)
|
||||||
|
_bad_test_found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main :: proc() {
|
||||||
|
_read_pipe, _write_pipe, _ = os.pipe()
|
||||||
|
os.stdout = _write_pipe
|
||||||
|
_spawn_pipe_reader()
|
||||||
|
`)
|
||||||
|
for test in example_tests {
|
||||||
|
strings.builder_reset(&example_build)
|
||||||
|
strings.write_string(&example_build, "package documentation_verification\n\n")
|
||||||
|
for line in test.example_code {
|
||||||
|
strings.write_string(&example_build, line)
|
||||||
|
strings.write_byte(&example_build, '\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
code_string := strings.to_string(example_build)
|
||||||
|
|
||||||
|
example_ast := ast.File { src = code_string }
|
||||||
|
odin_parser := parser.default_parser()
|
||||||
|
|
||||||
|
if ! parser.parse_file(&odin_parser, &example_ast) {
|
||||||
|
g_bad_doc = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if odin_parser.error_count > 0 {
|
||||||
|
fmt.eprintf("Errors on the following code generated for %q:\n%v\n", test.entity_name, code_string)
|
||||||
|
g_bad_doc = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
enforced_name := fmt.tprintf("%v_example", test.entity_name)
|
||||||
|
index_of_proc_name: int
|
||||||
|
code_test_name: string
|
||||||
|
|
||||||
|
for d in example_ast.decls {
|
||||||
|
value_decl, is_value := d.derived.(^ast.Value_Decl); if ! is_value {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(value_decl.values) != 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
proc_lit, is_proc_lit := value_decl.values[0].derived_expr.(^ast.Proc_Lit); if ! is_proc_lit {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(proc_lit.type.params.list) > 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
this_procedure_name := code_string[value_decl.names[0].pos.offset:value_decl.names[0].end.offset]
|
||||||
|
if this_procedure_name != enforced_name {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
index_of_proc_name = value_decl.names[0].pos.offset
|
||||||
|
code_test_name = this_procedure_name
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if code_test_name == "" {
|
||||||
|
fmt.eprintf("We could not any find procedure literals with no arguments with the identifier %q for the example for %q\n", enforced_name, test.entity_name)
|
||||||
|
g_bad_doc = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.sbprintf(&test_runner, "\t%v_%v()\n", test.package_name, code_test_name)
|
||||||
|
fmt.sbprintf(&test_runner, "\t_check(%q, `", code_test_name)
|
||||||
|
for line in test.expected_output {
|
||||||
|
strings.write_string(&test_runner, line)
|
||||||
|
strings.write_string(&test_runner, "\n")
|
||||||
|
}
|
||||||
|
strings.write_string(&test_runner, "`)\n")
|
||||||
|
save_path := fmt.tprintf("verify/test_%v_%v.odin", test.package_name, code_test_name)
|
||||||
|
|
||||||
|
test_file_handle, err := os.open(save_path, os.O_WRONLY | os.O_CREATE); if err != 0 {
|
||||||
|
fmt.eprintf("We could not open the file to the path %q for writing\n", save_path)
|
||||||
|
g_bad_doc = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
defer os.close(test_file_handle)
|
||||||
|
stream := os.stream_from_handle(test_file_handle)
|
||||||
|
writer, ok := io.to_writer(stream); if ! ok {
|
||||||
|
fmt.eprintf("We could not make the writer for the path %q\n", save_path)
|
||||||
|
g_bad_doc = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.wprintf(writer, "%v%v_%v", code_string[:index_of_proc_name], test.package_name, code_string[index_of_proc_name:])
|
||||||
|
}
|
||||||
|
|
||||||
|
strings.write_string(&test_runner,
|
||||||
|
`
|
||||||
|
if _bad_test_found {
|
||||||
|
fmt.eprintln("One or more tests failed")
|
||||||
|
os.exit(1)
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
os.write_entire_file("verify/main.odin", transmute([]byte)strings.to_string(test_runner))
|
||||||
|
}
|
||||||
|
|
||||||
|
run_test_suite :: proc() -> bool {
|
||||||
|
return libc.system(fmt.caprintf("%v run verify", g_path_to_odin)) == 0
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
@echo off
|
@echo off
|
||||||
set PATH_TO_ODIN==..\..\odin
|
set PATH_TO_ODIN==..\..\odin
|
||||||
%PATH_TO_ODIN% run test_map.odin -file -vet -strict-style -o:minimal
|
%PATH_TO_ODIN% run test_map.odin -file -vet -strict-style -o:minimal || exit /b
|
||||||
rem -define:SEED=42
|
rem -define:SEED=42
|
||||||
@@ -5,19 +5,14 @@ pushd build
|
|||||||
|
|
||||||
set COMMON=-collection:tests=..\..
|
set COMMON=-collection:tests=..\..
|
||||||
|
|
||||||
set ERROR_DID_OCCUR=0
|
|
||||||
|
|
||||||
@echo on
|
@echo on
|
||||||
|
|
||||||
..\..\..\odin test ..\test_issue_829.odin %COMMON% -file
|
..\..\..\odin test ..\test_issue_829.odin %COMMON% -file || exit /b
|
||||||
..\..\..\odin test ..\test_issue_1592.odin %COMMON% -file
|
..\..\..\odin test ..\test_issue_1592.odin %COMMON% -file || exit /b
|
||||||
..\..\..\odin test ..\test_issue_2087.odin %COMMON% -file
|
..\..\..\odin test ..\test_issue_2087.odin %COMMON% -file || exit /b
|
||||||
..\..\..\odin build ..\test_issue_2113.odin %COMMON% -file -debug
|
..\..\..\odin build ..\test_issue_2113.odin %COMMON% -file -debug || exit /b
|
||||||
|
|
||||||
@echo off
|
@echo off
|
||||||
|
|
||||||
if %ERRORLEVEL% NEQ 0 set ERROR_DID_OCCUR=1
|
|
||||||
|
|
||||||
popd
|
popd
|
||||||
rmdir /S /Q build
|
rmdir /S /Q build
|
||||||
if %ERROR_DID_OCCUR% NEQ 0 EXIT /B 1
|
|
||||||
|
|||||||
Vendored
+2
-2
@@ -5,9 +5,9 @@ set PATH_TO_ODIN==..\..\odin
|
|||||||
echo ---
|
echo ---
|
||||||
echo Running vendor:botan tests
|
echo Running vendor:botan tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run botan %COMMON% -out:vendor_botan.exe
|
%PATH_TO_ODIN% run botan %COMMON% -out:vendor_botan.exe || exit /b
|
||||||
|
|
||||||
echo ---
|
echo ---
|
||||||
echo Running vendor:glfw tests
|
echo Running vendor:glfw tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run glfw %COMMON% -out:vendor_glfw.exe
|
%PATH_TO_ODIN% run glfw %COMMON% -out:vendor_glfw.exe || exit /b
|
||||||
Reference in New Issue
Block a user