about summary refs log tree commit diff stats
path: root/068random.mu
blob: 3d73356c6167d697834c32cdfb242027cb76fba9 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
def random generator:&:stream:num -> result:num, fail?:bool, generator:&:stream:num [
  local-scope
  load-inputs
  {
    break-if generator
    # generator is 0? use real random-number generator
    result <- real-random
    return result, false
  }
  result, fail?, generator <- read generator
]

# helper for tests
def assume-random-numbers -> result:&:stream:num [
  local-scope
  load-inputs
  # compute result-len, space to allocate in result
  result-len:num <- copy 0
  {
    _, arg-received?:bool <- next-input
    break-unless arg-received?
    result-len <- add result-len, 1
    loop
  }
  rewind-inputs
  result-data:&:@:num <- new number:type, result-len
  idx:num <- copy 0
  {
    curr:num, arg-received?:bool <- next-input
    break-unless arg-received?
    *result-data <- put-index *result-data, idx, curr
    idx <- add idx, 1
    loop
  }
  result <- new-stream result-data
]

scenario random-numbers-in-scenario [
  local-scope
  source:&:stream:num <- assume-random-numbers 34, 35, 37
  1:num/raw, 2:bool/raw <- random source
  3:num/raw, 4:bool/raw <- random source
  5:num/raw, 6:bool/raw <- random source
  7:num/raw, 8:bool/raw <- random source
  memory-should-contain [
    1 <- 34
    2 <- 0  # everything went well
    3 <- 35
    4 <- 0  # everything went well
    5 <- 37
    6 <- 0  # everything went well
    7 <- 0  # empty result
    8 <- 1  # end of stream
  ]
]

# generate a random integer in the semi-open interval [start, end)
def random-in-range generator:&:stream:num, start:num, end:num -> result:num, fail?:bool, generator:&:stream:num [
  local-scope
  load-inputs
  result, fail?, generator <- random generator
  return-if fail?
  delta:num <- subtract end, start
  _, result <- divide-with-remainder result, delta
  result <- add result, start
]

scenario random-in-range [
  local-scope
  source:&:stream:num <- assume-random-numbers 91
  1:num/raw <- random-in-range source, 40, 50
  memory-should-contain [
    1 <- 41
  ]
]
an class="w"> (!in_token) { if (curr_uni == ' ') { continue; } else { in_token = TRUE; if (with_freetext) { num_tokens++; } if (with_freetext && (num_tokens == max + 1) && (curr_uni != '"')) { in_freetext = TRUE; } else if (curr_uni == '"') { in_quotes = TRUE; i++; gchar* next_ch = g_utf8_next_char(curr_ch); gunichar next_uni = g_utf8_get_char(next_ch); if (next_uni == '"') { tokens = g_slist_append(tokens, g_strndup(curr_ch, 0)); token_size = 0; in_token = FALSE; in_quotes = FALSE; } else { token_start = next_ch; token_size += g_unichar_to_utf8(next_uni, NULL); } } if (curr_uni == '"') { gchar* next_ch = g_utf8_next_char(curr_ch); token_start = next_ch; } else { token_start = curr_ch; token_size += g_unichar_to_utf8(curr_uni, NULL); } } } else { if (in_quotes) { if (curr_uni == '"') { tokens = g_slist_append(tokens, g_strndup(token_start, token_size)); token_size = 0; in_token = FALSE; in_quotes = FALSE; } else { if (curr_uni != '"') { token_size += g_unichar_to_utf8(curr_uni, NULL); } } } else { if (with_freetext && in_freetext) { token_size += g_unichar_to_utf8(curr_uni, NULL); } else if (curr_uni == ' ') { tokens = g_slist_append(tokens, g_strndup(token_start, token_size)); token_size = 0; in_token = FALSE; } else { token_size += g_unichar_to_utf8(curr_uni, NULL); } } } } if (in_token) { tokens = g_slist_append(tokens, g_strndup(token_start, token_size)); } free(copy); int num = g_slist_length(tokens) - 1; // if num args not valid return NULL if ((num < min) || (num > max)) { g_slist_free_full(tokens, free); *result = FALSE; return NULL; // if min allowed is 0 and 0 found, return empty char* array } else if (min == 0 && num == 0) { g_slist_free_full(tokens, free); gchar** args = g_malloc((num + 1) * sizeof(*args)); args[0] = NULL; *result = TRUE; return args; // otherwise return args array } else { gchar** args = g_malloc((num + 1) * sizeof(*args)); GSList* token = tokens; token = g_slist_next(token); int arg_count = 0; while (token) { args[arg_count++] = strdup(token->data); token = g_slist_next(token); } args[arg_count] = NULL; g_slist_free_full(tokens, free); *result = TRUE; return args; } } /* * Take a full line of input and return an array of strings representing * the arguments of a command. * If the number of arguments found is less than min, or more than max * NULL is returned. * * inp - The line of input * min - The minimum allowed number of arguments * max - The maximum allowed number of arguments * * Returns - An NULL terminated array of strings representing the arguments * of the command, or NULL if the validation fails. * * E.g. the following input line: * * /cmd arg1 arg2 * * Will return a pointer to the following array: * * { "arg1", "arg2", NULL } * */ gchar** parse_args(const char* const inp, int min, int max, gboolean* result) { return _parse_args_helper(inp, min, max, result, FALSE); } /* * Take a full line of input and return an array of strings representing * the arguments of a command. This function handles when the last parameter * to the command is free text e.g. * * /msg user@host here is a message * * If the number of arguments found is less than min, or more than max * NULL is returned. * * inp - The line of input * min - The minimum allowed number of arguments * max - The maximum allowed number of arguments * * Returns - An NULL terminated array of strings representing the arguments * of the command, or NULL if the validation fails. * * E.g. the following input line: * * /cmd arg1 arg2 some free text * * Will return a pointer to the following array: * * { "arg1", "arg2", "some free text", NULL } * */ gchar** parse_args_with_freetext(const char* const inp, int min, int max, gboolean* result) { return _parse_args_helper(inp, min, max, result, TRUE); } /* * Will just take everything after the first space as the argument. * Used for `/correct` so that we also include quotation marks. */ gchar** parse_args_as_one(const char* const inp, int min, int max, gboolean* result) { gchar** args = g_malloc0(2 * sizeof(*args)); int length = g_utf8_strlen(inp, -1); gchar* space = g_utf8_strchr(inp, length, ' '); if (space) { int sub_length = g_utf8_strlen(space, -1); if (sub_length > 1) { args[0] = g_strdup(space + 1); *result = TRUE; return args; } else { g_free(args); } } else { g_free(args); } *result = FALSE; return NULL; } int count_tokens(const char* const string) { int length = g_utf8_strlen(string, -1); gboolean in_quotes = FALSE; int num_tokens = 0; // include first token num_tokens++; for (int i = 0; i < length; i++) { gchar* curr_ch = g_utf8_offset_to_pointer(string, i); gunichar curr_uni = g_utf8_get_char(curr_ch); if (curr_uni == ' ') { if (!in_quotes) { num_tokens++; } } else if (curr_uni == '"') { if (in_quotes) { in_quotes = FALSE; } else { in_quotes = TRUE; } } } return num_tokens; } char* get_start(const char* const string, int tokens) { GString* result = g_string_new(""); int length = g_utf8_strlen(string, -1); gboolean in_quotes = FALSE; int num_tokens = 0; // include first token num_tokens++; for (int i = 0; i < length; i++) { gchar* curr_ch = g_utf8_offset_to_pointer(string, i); gunichar curr_uni = g_utf8_get_char(curr_ch); if (num_tokens < tokens) { gchar* uni_char = g_malloc(7); int len = g_unichar_to_utf8(curr_uni, uni_char); uni_char[len] = '\0'; g_string_append(result, uni_char); g_free(uni_char); } if (curr_uni == ' ') { if (!in_quotes) { num_tokens++; } } else if (curr_uni == '"') { if (in_quotes) { in_quotes = FALSE; } else { in_quotes = TRUE; } } } return g_string_free(result, FALSE); } GHashTable* parse_options(gchar** args, gchar** opt_keys, gboolean* res) { GList* keys = NULL; for (int i = 0; i < g_strv_length(opt_keys); i++) { keys = g_list_append(keys, opt_keys[i]); } GHashTable* options = NULL; // no options found, success if (args[0] == NULL) { options = g_hash_table_new(g_str_hash, g_str_equal); *res = TRUE; g_list_free(keys); return options; } // validate options GList* found_keys = NULL; for (int curr = 0; curr < g_strv_length(args); curr += 2) { // check if option valid if (g_list_find_custom(keys, args[curr], (GCompareFunc)g_strcmp0) == NULL) { *res = FALSE; g_list_free(found_keys); g_list_free(keys); return options; } // check if duplicate if (g_list_find_custom(found_keys, args[curr], (GCompareFunc)g_strcmp0)) { *res = FALSE; g_list_free(found_keys); g_list_free(keys); return options; } // check value given if (args[curr + 1] == NULL) { *res = FALSE; g_list_free(found_keys); g_list_free(keys); return options; } found_keys = g_list_append(found_keys, args[curr]); } g_list_free(found_keys); g_list_free(keys); // create map options = g_hash_table_new(g_str_hash, g_str_equal); *res = TRUE; for (int curr = 0; curr < g_strv_length(args); curr += 2) { g_hash_table_insert(options, args[curr], args[curr + 1]); } return options; } void options_destroy(GHashTable* options) { if (options) { g_hash_table_destroy(options); } }