Document core:math/rand and add 'possible output'

Possible output allows us to just type check a test
and have some sort of output field in the docs but
not actually verify it matches stdout
This commit is contained in:
Lucas Perlind
2023-05-31 09:49:12 +10:00
parent 6fe2df1d7d
commit ebe5636307
2 changed files with 668 additions and 42 deletions
+636 -24
View File
@@ -1,6 +1,10 @@
/*
Package core:math/rand implements various random number generators
*/
package rand package rand
import "core:intrinsics" import "core:intrinsics"
import "core:mem"
Rand :: struct { Rand :: struct {
state: u64, state: u64,
@@ -12,17 +16,82 @@ Rand :: struct {
@(private) @(private)
global_rand := create(u64(intrinsics.read_cycle_counter())) global_rand := create(u64(intrinsics.read_cycle_counter()))
/*
Sets the seed used by the global random number generator.
Inputs:
- seed: The seed value
Example:
import "core:math/rand"
import "core:fmt"
set_global_seed_example :: proc() {
rand.set_global_seed(1)
fmt.println(rand.uint64())
}
Possible Output:
10
*/
set_global_seed :: proc(seed: u64) { set_global_seed :: proc(seed: u64) {
init(&global_rand, seed) init(&global_rand, seed)
} }
/*
Creates a new random number generator.
Inputs:
- seed: The seed value to create the random number generator with
Returns:
- res: The created random number generator
Example:
import "core:math/rand"
import "core:fmt"
create_example :: proc() {
my_rand := rand.create(1)
fmt.println(rand.uint64(&my_rand))
}
Possible Output:
10
*/
@(require_results) @(require_results)
create :: proc(seed: u64) -> Rand { create :: proc(seed: u64) -> (res: Rand) {
r: Rand r: Rand
init(&r, seed) init(&r, seed)
return r return r
} }
/*
Initialises a random number generator.
Inputs:
- r: The random number generator to initialise
- seed: The seed value to initialise this random number generator
Example:
import "core:math/rand"
import "core:fmt"
init_example :: proc() {
my_rand: rand.Rand
rand.init(&my_rand, 1)
fmt.println(rand.uint64(&my_rand))
}
Possible Output:
10
*/
init :: proc(r: ^Rand, seed: u64) { init :: proc(r: ^Rand, seed: u64) {
r.state = 0 r.state = 0
r.inc = (seed << 1) | 1 r.inc = (seed << 1) | 1
@@ -31,6 +100,35 @@ init :: proc(r: ^Rand, seed: u64) {
_random(r) _random(r)
} }
/*
Initialises a random number generator to use the system random number generator.
The system random number generator is platform specific.
On `linux` refer to the `getrandom` syscall.
On `darwin` refer to `getentropy`.
On `windows` refer to `BCryptGenRandom`.
All other platforms wi
Inputs:
- r: The random number generator to use the system random number generator
WARNING: Panics if the system is not either `windows`, `darwin` or `linux`
Example:
import "core:math/rand"
import "core:fmt"
init_as_system_example :: proc() {
my_rand: rand.Rand
rand.init_as_system(&my_rand)
fmt.println(rand.uint64(&my_rand))
}
Possible Output:
10
*/
init_as_system :: proc(r: ^Rand) { init_as_system :: proc(r: ^Rand) {
if !#defined(_system_random) { if !#defined(_system_random) {
panic(#procedure + " is not supported on this platform yet") panic(#procedure + " is not supported on this platform yet")
@@ -61,18 +159,99 @@ _random :: proc(r: ^Rand) -> u32 {
return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 31)) return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 31))
} }
@(require_results) /*
uint32 :: proc(r: ^Rand = nil) -> u32 { return _random(r) } Generates a random 32 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
Inputs:
- r: The random number generator to use, or nil for the global generator
Returns:
- val: A random unsigned 32 bit value
Example:
import "core:math/rand"
import "core:fmt"
uint32_example :: proc() {
// Using the global random number generator
fmt.println(rand.uint32())
// Using local random number generator
my_rand := rand.create(1)
fmt.println(rand.uint32(&my_rand))
}
Possible Output:
10
389
*/
@(require_results) @(require_results)
uint64 :: proc(r: ^Rand = nil) -> u64 { uint32 :: proc(r: ^Rand = nil) -> (val: u32) { return _random(r) }
/*
Generates a random 64 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
Inputs:
- r: The random number generator to use, or nil for the global generator
Returns:
- val: A random unsigned 64 bit value
Example:
import "core:math/rand"
import "core:fmt"
uint64_example :: proc() {
// Using the global random number generator
fmt.println(rand.uint64())
// Using local random number generator
my_rand := rand.create(1)
fmt.println(rand.uint64(&my_rand))
}
Possible Output:
10
389
*/
@(require_results)
uint64 :: proc(r: ^Rand = nil) -> (val: u64) {
a := u64(_random(r)) a := u64(_random(r))
b := u64(_random(r)) b := u64(_random(r))
return (a<<32) | b return (a<<32) | b
} }
/*
Generates a random 128 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
Inputs:
- r: The random number generator to use, or nil for the global generator
Returns:
- val: A random unsigned 128 bit value
Example:
import "core:math/rand"
import "core:fmt"
uint128_example :: proc() {
// Using the global random number generator
fmt.println(rand.uint128())
// Using local random number generator
my_rand := rand.create(1)
fmt.println(rand.uint128(&my_rand))
}
Possible Output:
10
389
*/
@(require_results) @(require_results)
uint128 :: proc(r: ^Rand = nil) -> u128 { uint128 :: proc(r: ^Rand = nil) -> (val: u128) {
a := u128(_random(r)) a := u128(_random(r))
b := u128(_random(r)) b := u128(_random(r))
c := u128(_random(r)) c := u128(_random(r))
@@ -80,12 +259,126 @@ uint128 :: proc(r: ^Rand = nil) -> u128 {
return (a<<96) | (b<<64) | (c<<32) | d return (a<<96) | (b<<64) | (c<<32) | d
} }
@(require_results) int31 :: proc(r: ^Rand = nil) -> i32 { return i32(uint32(r) << 1 >> 1) } /*
@(require_results) int63 :: proc(r: ^Rand = nil) -> i64 { return i64(uint64(r) << 1 >> 1) } Generates a random 31 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
@(require_results) int127 :: proc(r: ^Rand = nil) -> i128 { return i128(uint128(r) << 1 >> 1) } The sign bit will always be set to 0, thus all generated numbers will be positive.
Inputs:
- r: The random number generator to use, or nil for the global generator
Returns:
- val: A random 31 bit value
Example:
import "core:math/rand"
import "core:fmt"
int31_example :: proc() {
// Using the global random number generator
fmt.println(rand.int31())
// Using local random number generator
my_rand := rand.create(1)
fmt.println(rand.int31(&my_rand))
}
Possible Output:
10
389
*/
@(require_results) int31 :: proc(r: ^Rand = nil) -> (val: i32) { return i32(uint32(r) << 1 >> 1) }
/*
Generates a random 63 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
The sign bit will always be set to 0, thus all generated numbers will be positive.
Inputs:
- r: The random number generator to use, or nil for the global generator
Returns:
- val: A random 63 bit value
Example:
import "core:math/rand"
import "core:fmt"
int63_example :: proc() {
// Using the global random number generator
fmt.println(rand.int63())
// Using local random number generator
my_rand := rand.create(1)
fmt.println(rand.int63(&my_rand))
}
Possible Output:
10
389
*/
@(require_results) int63 :: proc(r: ^Rand = nil) -> (val: i64) { return i64(uint64(r) << 1 >> 1) }
/*
Generates a random 127 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
The sign bit will always be set to 0, thus all generated numbers will be positive.
Inputs:
- r: The random number generator to use, or nil for the global generator
Returns:
- val: A random 127 bit value
Example:
import "core:math/rand"
import "core:fmt"
int127_example :: proc() {
// Using the global random number generator
fmt.println(rand.int127())
// Using local random number generator
my_rand := rand.create(1)
fmt.println(rand.int127(&my_rand))
}
Possible Output:
10
389
*/
@(require_results) int127 :: proc(r: ^Rand = nil) -> (val: i128) { return i128(uint128(r) << 1 >> 1) }
/*
Generates a random 31 bit value in the range `(0, n]` using the provided random number generator. If no generator is provided the global random number generator will be used.
Inputs:
- n: The upper bound of the generated number, this value is exclusive
- r: The random number generator to use, or nil for the global generator
Returns:
- val: A random 31 bit value in the range `(0, n]`
WARNING: Panics if n is less than 0
Example:
import "core:math/rand"
import "core:fmt"
int31_max_example :: proc() {
// Using the global random number generator
fmt.println(rand.int31_max(16))
// Using local random number generator
my_rand := rand.create(1)
fmt.println(rand.int31_max(1024, &my_rand))
}
Possible Output:
6
500
*/
@(require_results) @(require_results)
int31_max :: proc(n: i32, r: ^Rand = nil) -> i32 { int31_max :: proc(n: i32, r: ^Rand = nil) -> (val: i32) {
if n <= 0 { if n <= 0 {
panic("Invalid argument to int31_max") panic("Invalid argument to int31_max")
} }
@@ -99,9 +392,38 @@ int31_max :: proc(n: i32, r: ^Rand = nil) -> i32 {
} }
return v % n return v % n
} }
/*
Generates a random 63 bit value in the range `(0, n]` using the provided random number generator. If no generator is provided the global random number generator will be used.
Inputs:
- n: The upper bound of the generated number, this value is exclusive
- r: The random number generator to use, or nil for the global generator
Returns:
- val: A random 63 bit value in the range `(0, n]`
WARNING: Panics if n is less than 0
Example:
import "core:math/rand"
import "core:fmt"
int63_max_example :: proc() {
// Using the global random number generator
fmt.println(rand.int63_max(16))
// Using local random number generator
my_rand := rand.create(1)
fmt.println(rand.int63_max(1024, &my_rand))
}
Possible Output:
6
500
*/
@(require_results) @(require_results)
int63_max :: proc(n: i64, r: ^Rand = nil) -> i64 { int63_max :: proc(n: i64, r: ^Rand = nil) -> (val: i64) {
if n <= 0 { if n <= 0 {
panic("Invalid argument to int63_max") panic("Invalid argument to int63_max")
} }
@@ -115,9 +437,38 @@ int63_max :: proc(n: i64, r: ^Rand = nil) -> i64 {
} }
return v % n return v % n
} }
/*
Generates a random 127 bit value in the range `(0, n]` using the provided random number generator. If no generator is provided the global random number generator will be used.
Inputs:
- n: The upper bound of the generated number, this value is exclusive
- r: The random number generator to use, or nil for the global generator
Returns:
- val: A random 127 bit value in the range `(0, n]`
WARNING: Panics if n is less than 0
Example:
import "core:math/rand"
import "core:fmt"
int127_max_example :: proc() {
// Using the global random number generator
fmt.println(rand.int127_max(16))
// Using local random number generator
my_rand := rand.create(1)
fmt.println(rand.int127_max(1024, &my_rand))
}
Possible Output:
6
500
*/
@(require_results) @(require_results)
int127_max :: proc(n: i128, r: ^Rand = nil) -> i128 { int127_max :: proc(n: i128, r: ^Rand = nil) -> (val: i128) {
if n <= 0 { if n <= 0 {
panic("Invalid argument to int127_max") panic("Invalid argument to int127_max")
} }
@@ -131,9 +482,38 @@ int127_max :: proc(n: i128, r: ^Rand = nil) -> i128 {
} }
return v % n return v % n
} }
/*
Generates a random integer value in the range `(0, n]` using the provided random number generator. If no generator is provided the global random number generator will be used.
Inputs:
- n: The upper bound of the generated number, this value is exclusive
- r: The random number generator to use, or nil for the global generator
Returns:
- val: A random integer value in the range `(0, n]`
WARNING: Panics if n is less than 0
Example:
import "core:math/rand"
import "core:fmt"
int_max_example :: proc() {
// Using the global random number generator
fmt.println(rand.int_max(16))
// Using local random number generator
my_rand := rand.create(1)
fmt.println(rand.int_max(1024, &my_rand))
}
Possible Output:
6
500
*/
@(require_results) @(require_results)
int_max :: proc(n: int, r: ^Rand = nil) -> int { int_max :: proc(n: int, r: ^Rand = nil) -> (val: int) {
if n <= 0 { if n <= 0 {
panic("Invalid argument to int_max") panic("Invalid argument to int_max")
} }
@@ -144,14 +524,153 @@ int_max :: proc(n: int, r: ^Rand = nil) -> int {
} }
} }
// Uniform random distribution [0, 1) /*
@(require_results) float64 :: proc(r: ^Rand = nil) -> f64 { return f64(int63_max(1<<53, r)) / (1 << 53) } Generates a random double floating point value in the range `(0, 1]` using the provided random number generator. If no generator is provided the global random number generator will be used.
// Uniform random distribution [0, 1)
@(require_results) float32 :: proc(r: ^Rand = nil) -> f32 { return f32(float64(r)) }
@(require_results) float64_range :: proc(lo, hi: f64, r: ^Rand = nil) -> f64 { return (hi-lo)*float64(r) + lo } Inputs:
@(require_results) float32_range :: proc(lo, hi: f32, r: ^Rand = nil) -> f32 { return (hi-lo)*float32(r) + lo } - r: The random number generator to use, or nil for the global generator
Returns:
- val: A random double floating point value in the range `(0, 1]`
Example:
import "core:math/rand"
import "core:fmt"
float64_example :: proc() {
// Using the global random number generator
fmt.println(rand.float64())
// Using local random number generator
my_rand := rand.create(1)
fmt.println(rand.float64(&my_rand))
}
Possible Output:
0.043
0.511
*/
@(require_results) float64 :: proc(r: ^Rand = nil) -> (val: f64) { return f64(int63_max(1<<53, r)) / (1 << 53) }
/*
Generates a random single floating point value in the range `(0, 1]` using the provided random number generator. If no generator is provided the global random number generator will be used.
Inputs:
- r: The random number generator to use, or nil for the global generator
Returns:
- val: A random single floating point value in the range `(0, 1]`
Example:
import "core:math/rand"
import "core:fmt"
float32_example :: proc() {
// Using the global random number generator
fmt.println(rand.float32())
// Using local random number generator
my_rand := rand.create(1)
fmt.println(rand.float32(&my_rand))
}
Possible Output:
0.043
0.511
*/
@(require_results) float32 :: proc(r: ^Rand = nil) -> (val: f32) { return f32(float64(r)) }
/*
Generates a random double floating point value in the range `(low, high]` using the provided random number generator. If no generator is provided the global random number generator will be used.
Inputs:
- low: The lower bounds of the value, this value is inclusive
- high: The upper bounds of the value, this value is exclusive
- r: The random number generator to use, or nil for the global generator
Returns:
- val: A random double floating point value in the range `(low, high]`
Example:
import "core:math/rand"
import "core:fmt"
float64_range_example :: proc() {
// Using the global random number generator
fmt.println(rand.float64_range(-10, 300))
// Using local random number generator
my_rand := rand.create(1)
fmt.println(rand.float64_range(600, 900, &my_rand))
}
Possible Output:
15.312
673.130
*/
@(require_results) float64_range :: proc(low, high: f64, r: ^Rand = nil) -> (val: f64) { return (high-low)*float64(r) + low }
/*
Generates a random single floating point value in the range `(low, high]` using the provided random number generator. If no generator is provided the global random number generator will be used.
Inputs:
- low: The lower bounds of the value, this value is inclusive
- high: The upper bounds of the value, this value is exclusive
- r: The random number generator to use, or nil for the global generator
Returns:
- val: A random single floating point value in the range `(low, high]`
Example:
import "core:math/rand"
import "core:fmt"
float32_range_example :: proc() {
// Using the global random number generator
fmt.println(rand.float32_range(-10, 300))
// Using local random number generator
my_rand := rand.create(1)
fmt.println(rand.float32_range(600, 900, &my_rand))
}
Possible Output:
15.312
673.130
*/
@(require_results) float32_range :: proc(low, high: f32, r: ^Rand = nil) -> (val: f32) { return (high-low)*float32(r) + low }
/*
Fills a byte slice with random values using the provided random number generator. If no generator is provided the global random number generator will be used.
Inputs:
- p: The byte slice to fill
- r: The random number generator to use, or nil for the global generator
Returns:
- n: The number of bytes generated
Example:
import "core:math/rand"
import "core:fmt"
read_example :: proc() {
// Using the global random number generator
data: [8]byte
n := rand.read(data[:])
fmt.println(n)
fmt.println(data)
}
Possible Output:
8
[32, 4, 59, 7, 1, 2, 2, 119]
*/
@(require_results) @(require_results)
read :: proc(p: []byte, r: ^Rand = nil) -> (n: int) { read :: proc(p: []byte, r: ^Rand = nil) -> (n: int) {
pos := i8(0) pos := i8(0)
@@ -168,19 +687,81 @@ read :: proc(p: []byte, r: ^Rand = nil) -> (n: int) {
return return
} }
// perm returns a slice of n ints in a pseudo-random permutation of integers in the range [0, n) /*
Creates a slice of `int` filled with random values using the provided random number generator. If no generator is provided the global random number generator will be used.
*Allocates Using Provided Allocator*
Inputs:
- n: The size of the created slice
- r: The random number generator to use, or nil for the global generator
- allocator: (default: context.allocator)
Returns:
- res: A slice filled with random values
- err: An allocator error if one occured, `nil` otherwise
Example:
import "core:math/rand"
import "core:mem"
import "core:fmt"
perm_example :: proc() -> (err: mem.Allocator_Error) {
// Using the global random number generator and using the context allocator
data := rand.perm(4) or_return
fmt.println(data)
defer delete(data, context.allocator)
// Using local random number generator and temp allocator
my_rand := rand.create(1)
data_tmp := rand.perm(4, &my_rand, context.temp_allocator) or_return
fmt.println(data_tmp)
return
}
Possible Output:
[7201011, 3, 9123, 231131]
[19578, 910081, 131, 7]
*/
@(require_results) @(require_results)
perm :: proc(n: int, r: ^Rand = nil, allocator := context.allocator) -> []int { perm :: proc(n: int, r: ^Rand = nil, allocator := context.allocator) -> (res: []int, err: mem.Allocator_Error) #optional_allocator_error {
m := make([]int, n, allocator) m := make([]int, n, allocator) or_return
for i := 0; i < n; i += 1 { for i := 0; i < n; i += 1 {
j := int_max(i+1, r) j := int_max(i+1, r)
m[i] = m[j] m[i] = m[j]
m[j] = i m[j] = i
} }
return m return m, {}
} }
/*
Randomizes the ordering of elements for the provided slice. If no generator is provided the global random number generator will be used.
Inputs:
- array: The slice to randomize
- r: The random number generator to use, or nil for the global generator
Example:
import "core:math/rand"
import "core:fmt"
shuffle_example :: proc() {
// Using the global random number generator
data: [4]int = { 1, 2, 3, 4 }
fmt.println(data) // the contents are in order
rand.shuffle(data[:])
fmt.println(data) // the contents have been shuffled
}
Possible Output:
[1, 2, 3, 4]
[2, 4, 3, 1]
*/
shuffle :: proc(array: $T/[]$E, r: ^Rand = nil) { shuffle :: proc(array: $T/[]$E, r: ^Rand = nil) {
n := i64(len(array)) n := i64(len(array))
if n < 2 { if n < 2 {
@@ -193,7 +774,38 @@ shuffle :: proc(array: $T/[]$E, r: ^Rand = nil) {
} }
} }
// Returns a random element from the given slice /*
Returns a random element from the provided slice. If no generator is provided the global random number generator will be used.
Inputs:
- array: The slice to choose an element from
- r: The random number generator to use, or nil for the global generator
Returns:
- res: A random element from `array`
Example:
import "core:math/rand"
import "core:fmt"
choice_example :: proc() {
// Using the global random number generator
data: [4]int = { 1, 2, 3, 4 }
fmt.println(rand.choice(data[:]))
fmt.println(rand.choice(data[:]))
fmt.println(rand.choice(data[:]))
fmt.println(rand.choice(data[:]))
}
Possible Output:
3
2
2
4
*/
@(require_results) @(require_results)
choice :: proc(array: $T/[]$E, r: ^Rand = nil) -> (res: E) { choice :: proc(array: $T/[]$E, r: ^Rand = nil) -> (res: E) {
n := i64(len(array)) n := i64(len(array))
@@ -201,4 +813,4 @@ choice :: proc(array: $T/[]$E, r: ^Rand = nil) -> (res: E) {
return E{} return E{}
} }
return array[int63_max(n, r)] return array[int63_max(n, r)]
} }
+32 -18
View File
@@ -14,6 +14,7 @@ Example_Test :: struct {
package_name: string, package_name: string,
example_code: []string, example_code: []string,
expected_output: []string, expected_output: []string,
skip_output_check: bool,
} }
g_header: ^doc.Header g_header: ^doc.Header
@@ -145,6 +146,7 @@ find_and_add_examples :: proc(docs: string, package_name: string, entity_name: s
curr_block_kind := Block_Kind.Other curr_block_kind := Block_Kind.Other
start := 0 start := 0
found_possible_output: bool
example_block: Block // when set the kind should be Example example_block: Block // when set the kind should be Example
output_block: Block // when set the kind should be Output output_block: Block // when set the kind should be Output
// rely on zii that the kinds have not been set // rely on zii that the kinds have not been set
@@ -178,10 +180,16 @@ find_and_add_examples :: proc(docs: string, package_name: string, entity_name: s
switch { switch {
case strings.has_prefix(line, "Example:"): next_block_kind = .Example case strings.has_prefix(line, "Example:"): next_block_kind = .Example
case strings.has_prefix(line, "Output:"): next_block_kind = .Output case strings.has_prefix(line, "Output:"): next_block_kind = .Output
case strings.has_prefix(line, "Possible Output:"):
next_block_kind = .Output
found_possible_output = true
} }
case .Example: case .Example:
switch { switch {
case strings.has_prefix(line, "Output:"): next_block_kind = .Output case strings.has_prefix(line, "Output:"): next_block_kind = .Output
case strings.has_prefix(line, "Possible Output:"):
next_block_kind = .Output
found_possible_output = true
case ! (text == "" || strings.has_prefix(line, "\t")): next_block_kind = .Other case ! (text == "" || strings.has_prefix(line, "\t")): next_block_kind = .Other
} }
case .Output: case .Output:
@@ -219,8 +227,9 @@ find_and_add_examples :: proc(docs: string, package_name: string, entity_name: s
{ {
// Output block starts with // Output block starts with
// `Output:` and a number of white spaces, // `Output:` and a number of white spaces,
// `Possible Output:` and a number of white spaces,
lines := &output_block.lines lines := &output_block.lines
for len(lines) > 0 && (strings.trim_space(lines[0]) == "" || strings.has_prefix(lines[0], "Output:")) { for len(lines) > 0 && (strings.trim_space(lines[0]) == "" || strings.has_prefix(lines[0], "Output:") || strings.has_prefix(lines[0], "Possible Output:")) {
lines^ = lines[1:] lines^ = lines[1:]
} }
// Additionally we need to strip all empty lines at the end of output to not include those in the expected output // Additionally we need to strip all empty lines at the end of output to not include those in the expected output
@@ -240,6 +249,7 @@ find_and_add_examples :: proc(docs: string, package_name: string, entity_name: s
package_name = package_name, package_name = package_name,
example_code = example_block.lines, example_code = example_block.lines,
expected_output = output_block.lines, expected_output = output_block.lines,
skip_output_check = found_possible_output,
}) })
} }
} }
@@ -404,25 +414,29 @@ main :: proc() {
continue continue
} }
fmt.sbprintf(&test_runner, "\t%v_%v()\n", test.package_name, code_test_name) // NOTE: packages like 'rand' are random by nature, in these cases we cannot verify against the output string
fmt.sbprintf(&test_runner, "\t_check(%q, `", code_test_name) // in these cases we just mark the output as 'Possible Output' and we simply skip checking against the output
had_line_error: bool if ! test.skip_output_check {
for line in test.expected_output { fmt.sbprintf(&test_runner, "\t%v_%v()\n", test.package_name, code_test_name)
// NOTE: this will escape the multiline string. Even with a backslash it still escapes due to the semantics of ` fmt.sbprintf(&test_runner, "\t_check(%q, `", code_test_name)
// I don't think any examples would really need this specific character so let's just make it forbidden and change had_line_error: bool
// in the future if we really need to for line in test.expected_output {
if strings.contains_rune(line, '`') { // NOTE: this will escape the multiline string. Even with a backslash it still escapes due to the semantics of `
fmt.eprintf("The line %q in the output for \"%s.%s\" contains a ` which is not allowed\n", line, test.package_name, test.entity_name) // I don't think any examples would really need this specific character so let's just make it forbidden and change
g_bad_doc = true // in the future if we really need to
had_line_error = true if strings.contains_rune(line, '`') {
fmt.eprintf("The line %q in the output for \"%s.%s\" contains a ` which is not allowed\n", line, test.package_name, test.entity_name)
g_bad_doc = true
had_line_error = true
}
strings.write_string(&test_runner, line)
strings.write_string(&test_runner, "\n")
} }
strings.write_string(&test_runner, line) if had_line_error {
strings.write_string(&test_runner, "\n") continue
}
strings.write_string(&test_runner, "`)\n")
} }
if had_line_error {
continue
}
strings.write_string(&test_runner, "`)\n")
save_path := fmt.tprintf("verify/test_%v_%v.odin", test.package_name, code_test_name) 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 { test_file_handle, err := os.open(save_path, os.O_WRONLY | os.O_CREATE); if err != 0 {