split builtin random into its own module

This commit is contained in:
Kurtis Rader 2017-06-12 22:26:24 -07:00
parent e7f87c08e1
commit 1c91ec9dfa
4 changed files with 238 additions and 165 deletions

View File

@ -101,6 +101,7 @@ HAVE_DOXYGEN=@HAVE_DOXYGEN@
FISH_OBJS := obj/autoload.o obj/builtin.o obj/builtin_bind.o obj/builtin_block.o \
obj/builtin_commandline.o obj/builtin_emit.o obj/builtin_functions.o \
obj/builtin_history.o obj/builtin_status.o obj/builtin_read.o \
obj/builtin_random.o \
obj/builtin_complete.o obj/builtin_jobs.o obj/builtin_printf.o \
obj/builtin_set.o obj/builtin_set_color.o obj/builtin_string.o \
obj/builtin_test.o obj/builtin_ulimit.o obj/color.o obj/common.o \
@ -946,17 +947,18 @@ v = $(V$(V))
obj/autoload.o: config.h src/autoload.h src/common.h src/fallback.h
obj/autoload.o: src/signal.h src/lru.h src/env.h src/exec.h src/wutil.h
obj/builtin.o: config.h src/signal.h src/builtin.h src/common.h
obj/builtin.o: src/fallback.h src/builtin_bind.h src/builtin_block.h src/builtin_functions.h
obj/builtin.o: src/builtin_commandline.h src/builtin_complete.h src/builtin_history.h
obj/builtin.o: src/builtin_emit.h src/builtin_jobs.h src/builtin_printf.h
obj/builtin.o: src/builtin_set.h src/builtin_set_color.h src/builtin_string.h
obj/builtin.o: src/builtin_test.h src/builtin_ulimit.h src/complete.h
obj/builtin.o: src/env.h src/event.h src/exec.h src/expand.h
obj/builtin.o: src/parse_constants.h src/function.h src/highlight.h
obj/builtin.o: src/color.h src/history.h src/wutil.h src/intern.h src/io.h
obj/builtin.o: src/parse_util.h src/tokenizer.h src/parser.h src/parse_tree.h
obj/builtin.o: src/fallback.h src/builtin_bind.h src/builtin_block.h
obj/builtin.o: src/builtin_commandline.h src/builtin_complete.h
obj/builtin.o: src/builtin_emit.h src/builtin_functions.h
obj/builtin.o: src/builtin_history.h src/builtin_jobs.h src/builtin_printf.h
obj/builtin.o: src/builtin_random.h src/builtin_read.h src/builtin_set.h
obj/builtin.o: src/builtin_set_color.h src/builtin_status.h
obj/builtin.o: src/builtin_string.h src/builtin_test.h src/builtin_ulimit.h
obj/builtin.o: src/complete.h src/env.h src/event.h src/exec.h src/function.h
obj/builtin.o: src/intern.h src/io.h src/parse_constants.h src/parse_util.h
obj/builtin.o: src/tokenizer.h src/parser.h src/expand.h src/parse_tree.h
obj/builtin.o: src/proc.h src/parser_keywords.h src/path.h src/reader.h
obj/builtin.o: src/wcstringutil.h src/wgetopt.h
obj/builtin.o: src/highlight.h src/color.h src/wgetopt.h src/wutil.h
obj/builtin_bind.o: config.h src/builtin.h src/common.h src/fallback.h
obj/builtin_bind.o: src/signal.h src/builtin_bind.h src/input.h src/env.h
obj/builtin_bind.o: src/io.h src/wgetopt.h src/wutil.h
@ -980,15 +982,33 @@ obj/builtin_complete.o: src/parse_tree.h src/proc.h src/reader.h
obj/builtin_complete.o: src/highlight.h src/color.h src/wgetopt.h src/wutil.h
obj/builtin_emit.o: config.h src/builtin.h src/common.h src/fallback.h
obj/builtin_emit.o: src/signal.h src/builtin_emit.h src/event.h src/io.h
obj/builtin_emit.o: src/parser.h src/expand.h src/parse_constants.h
obj/builtin_emit.o: src/parse_tree.h src/tokenizer.h src/proc.h
obj/builtin_emit.o: src/wgetopt.h src/wutil.h
obj/builtin_functions.o: config.h src/builtin.h src/common.h src/fallback.h
obj/builtin_functions.o: src/signal.h src/builtin_functions.h src/env.h
obj/builtin_functions.o: src/event.h src/function.h src/io.h
obj/builtin_functions.o: src/parser_keywords.h src/proc.h src/parse_tree.h
obj/builtin_functions.o: src/parse_constants.h src/tokenizer.h src/wgetopt.h
obj/builtin_functions.o: src/wutil.h
obj/builtin_history.o: config.h src/builtin.h src/common.h src/fallback.h
obj/builtin_history.o: src/signal.h src/builtin_history.h src/history.h
obj/builtin_history.o: src/wutil.h src/io.h src/reader.h src/complete.h
obj/builtin_history.o: src/highlight.h src/color.h src/env.h
obj/builtin_history.o: src/parse_constants.h src/wgetopt.h
obj/builtin_jobs.o: config.h src/builtin.h src/common.h src/fallback.h
obj/builtin_jobs.o: src/signal.h src/io.h src/proc.h src/parse_tree.h
obj/builtin_jobs.o: src/parse_constants.h src/tokenizer.h src/wgetopt.h
obj/builtin_jobs.o: src/wutil.h
obj/builtin_printf.o: config.h src/builtin.h src/common.h src/fallback.h
obj/builtin_printf.o: src/signal.h src/io.h src/wutil.h
obj/builtin_random.o: config.h src/builtin.h src/common.h src/fallback.h
obj/builtin_random.o: src/signal.h src/builtin_random.h src/io.h
obj/builtin_random.o: src/wgetopt.h src/wutil.h
obj/builtin_read.o: config.h src/builtin.h src/common.h src/fallback.h
obj/builtin_read.o: src/signal.h src/builtin_read.h src/complete.h src/env.h
obj/builtin_read.o: src/event.h src/expand.h src/parse_constants.h
obj/builtin_read.o: src/highlight.h src/color.h src/io.h src/proc.h
obj/builtin_read.o: src/parse_tree.h src/tokenizer.h src/reader.h
obj/builtin_read.o: src/wcstringutil.h src/wgetopt.h src/wutil.h
obj/builtin_set.o: config.h src/builtin.h src/common.h src/fallback.h
obj/builtin_set.o: src/signal.h src/env.h src/expand.h src/parse_constants.h
obj/builtin_set.o: src/io.h src/proc.h src/parse_tree.h src/tokenizer.h
@ -996,6 +1016,11 @@ obj/builtin_set.o: src/wgetopt.h src/wutil.h
obj/builtin_set_color.o: config.h src/builtin.h src/common.h src/fallback.h
obj/builtin_set_color.o: src/signal.h src/color.h src/env.h src/io.h
obj/builtin_set_color.o: src/output.h src/wgetopt.h src/wutil.h
obj/builtin_status.o: config.h src/builtin.h src/common.h src/fallback.h
obj/builtin_status.o: src/signal.h src/builtin_status.h src/io.h src/parser.h
obj/builtin_status.o: src/event.h src/expand.h src/parse_constants.h
obj/builtin_status.o: src/parse_tree.h src/tokenizer.h src/proc.h
obj/builtin_status.o: src/wgetopt.h src/wutil.h
obj/builtin_string.o: config.h src/builtin.h src/common.h src/fallback.h
obj/builtin_string.o: src/signal.h src/io.h src/parse_util.h
obj/builtin_string.o: src/parse_constants.h src/tokenizer.h src/wgetopt.h

View File

@ -21,7 +21,6 @@
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -31,7 +30,6 @@
#include <algorithm>
#include <memory>
#include <random>
#include <set>
#include <string>
@ -45,6 +43,7 @@
#include "builtin_history.h"
#include "builtin_jobs.h"
#include "builtin_printf.h"
#include "builtin_random.h"
#include "builtin_read.h"
#include "builtin_set.h"
#include "builtin_set_color.h"
@ -897,157 +896,6 @@ int builtin_function(parser_t &parser, io_streams_t &streams, const wcstring_lis
return STATUS_CMD_OK;
}
/// The random builtin generates random numbers.
static int builtin_random(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
int argc = builtin_count_args(argv);
static bool seeded = false;
static std::minstd_rand engine;
if (!seeded) {
// seed engine with 2*32 bits of random data
// for the 64 bits of internal state of minstd_rand
std::random_device rd;
std::seed_seq seed{rd(), rd()};
engine.seed(seed);
seeded = true;
}
static const wchar_t *short_options = L"h";
static const struct woption long_options[] = {{L"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}};
int opt;
wgetopter_t w;
while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
switch (opt) { //!OCLINT(too few branches)
case 'h': {
builtin_print_help(parser, streams, argv[0], streams.out);
return STATUS_CMD_OK;
}
case '?': {
builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
return STATUS_INVALID_ARGS;
}
default: {
DIE("unexpected retval from wgetopt_long");
break;
}
}
}
int arg_count = argc - w.woptind;
long long start, end;
unsigned long long step;
bool choice = false;
if (arg_count >= 1 && !wcscmp(argv[w.woptind], L"choice")) {
if (arg_count == 1) {
streams.err.append_format(L"%ls: nothing to choose from\n", argv[0]);
return STATUS_INVALID_ARGS;
}
choice = true;
start = 1;
step = 1;
end = arg_count - 1;
} else {
bool parse_error = false;
auto parse_ll = [&](const wchar_t *str) {
long long ll = fish_wcstoll(str);
if (errno) {
streams.err.append_format(L"%ls: %ls is not a valid integer\n", argv[0], str);
parse_error = true;
}
return ll;
};
auto parse_ull = [&](const wchar_t *str) {
unsigned long long ull = fish_wcstoull(str);
if (errno) {
streams.err.append_format(L"%ls: %ls is not a valid integer\n", argv[0], str);
parse_error = true;
}
return ull;
};
if (arg_count == 0) {
start = 0;
end = 32767;
step = 1;
} else if (arg_count == 1) {
long long seed = parse_ll(argv[w.woptind]);
if (parse_error) return STATUS_INVALID_ARGS;
engine.seed(static_cast<uint32_t>(seed));
return STATUS_CMD_OK;
} else if (arg_count == 2) {
start = parse_ll(argv[w.woptind]);
step = 1;
end = parse_ll(argv[w.woptind + 1]);
} else if (arg_count == 3) {
start = parse_ll(argv[w.woptind]);
step = parse_ull(argv[w.woptind + 1]);
end = parse_ll(argv[w.woptind + 2]);
} else {
streams.err.append_format(BUILTIN_ERR_TOO_MANY_ARGUMENTS, argv[0]);
return STATUS_INVALID_ARGS;
}
if (parse_error) {
return STATUS_INVALID_ARGS;
} else if (start >= end) {
streams.err.append_format(L"%ls: END must be greater than START\n", argv[0]);
return STATUS_INVALID_ARGS;
} else if (step == 0) {
streams.err.append_format(L"%ls: STEP must be a positive integer\n", argv[0]);
return STATUS_INVALID_ARGS;
}
}
// only for negative argument
auto safe_abs = [](long long ll) -> unsigned long long {
return -static_cast<unsigned long long>(ll);
};
long long real_end;
if (start >= 0 || end < 0) {
// 0 <= start <= end
long long diff = end - start;
// 0 <= diff <= LL_MAX
real_end = start + static_cast<long long>(diff / step);
} else {
// start < 0 <= end
unsigned long long abs_start = safe_abs(start);
unsigned long long diff = (end + abs_start);
real_end = diff / step - abs_start;
}
if (!choice && start == real_end) {
streams.err.append_format(L"%ls: range contains only one possible value\n", argv[0]);
return STATUS_INVALID_ARGS;
}
std::uniform_int_distribution<long long> dist(start, real_end);
long long random = dist(engine);
long long result;
if (start >= 0) {
// 0 <= start <= random <= end
long long diff = random - start;
// 0 < step * diff <= end - start <= LL_MAX
result = start + static_cast<long long>(diff * step);
} else if (random < 0) {
// start <= random < 0
long long diff = random - start;
result = diff * step - safe_abs(start);
} else {
// start < 0 <= random
unsigned long long abs_start = safe_abs(start);
unsigned long long diff = (random + abs_start);
result = diff * step - abs_start;
}
if (choice) {
streams.out.append_format(L"%ls\n", argv[w.woptind + result]);
} else {
streams.out.append_format(L"%lld\n", result);
}
return STATUS_CMD_OK;
}
/// The exit builtin. Calls reader_exit to exit and returns the value specified.
static int builtin_exit(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
int argc = builtin_count_args(argv);

191
src/builtin_random.cpp Normal file
View File

@ -0,0 +1,191 @@
// Implementation of the random builtin.
#include "config.h" // IWYU pragma: keep
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <wchar.h>
#include <algorithm>
#include <random>
#include "builtin.h"
#include "builtin_random.h"
#include "common.h"
#include "fallback.h" // IWYU pragma: keep
#include "io.h"
#include "wgetopt.h"
#include "wutil.h" // IWYU pragma: keep
struct random_opts {
bool print_help = false;
};
static const wchar_t *short_options = L"h";
static const struct woption long_options[] = {{L"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}};
static int parse_random_opts(struct random_opts *opts, int *optind, //!OCLINT(high ncss method)
int argc, wchar_t **argv, parser_t &parser, io_streams_t &streams) {
wchar_t *cmd = argv[0];
int opt;
wgetopter_t w;
while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
switch (opt) { //!OCLINT(too few branches)
case 'h': {
opts->print_help = true;
return STATUS_CMD_OK;
}
case '?': {
builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
return STATUS_INVALID_ARGS;
}
default: {
DIE("unexpected retval from wgetopt_long");
break;
}
}
}
*optind = w.woptind;
return STATUS_CMD_OK;
}
/// The random builtin generates random numbers.
int builtin_random(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
wchar_t *cmd = argv[0];
int argc = builtin_count_args(argv);
struct random_opts opts;
int optind;
int retval = parse_random_opts(&opts, &optind, argc, argv, parser, streams);
if (retval != STATUS_CMD_OK) return retval;
if (opts.print_help) {
builtin_print_help(parser, streams, cmd, streams.out);
return STATUS_CMD_OK;
}
static bool seeded = false;
static std::minstd_rand engine;
if (!seeded) {
// seed engine with 2*32 bits of random data
// for the 64 bits of internal state of minstd_rand
std::random_device rd;
std::seed_seq seed{rd(), rd()};
engine.seed(seed);
seeded = true;
}
int arg_count = argc - optind;
long long start, end;
unsigned long long step;
bool choice = false;
if (arg_count >= 1 && !wcscmp(argv[optind], L"choice")) {
if (arg_count == 1) {
streams.err.append_format(L"%ls: nothing to choose from\n", cmd);
return STATUS_INVALID_ARGS;
}
choice = true;
start = 1;
step = 1;
end = arg_count - 1;
} else {
bool parse_error = false;
auto parse_ll = [&](const wchar_t *str) {
long long ll = fish_wcstoll(str);
if (errno) {
streams.err.append_format(L"%ls: %ls is not a valid integer\n", cmd, str);
parse_error = true;
}
return ll;
};
auto parse_ull = [&](const wchar_t *str) {
unsigned long long ull = fish_wcstoull(str);
if (errno) {
streams.err.append_format(L"%ls: %ls is not a valid integer\n", cmd, str);
parse_error = true;
}
return ull;
};
if (arg_count == 0) {
start = 0;
end = 32767;
step = 1;
} else if (arg_count == 1) {
long long seed = parse_ll(argv[optind]);
if (parse_error) return STATUS_INVALID_ARGS;
engine.seed(static_cast<uint32_t>(seed));
return STATUS_CMD_OK;
} else if (arg_count == 2) {
start = parse_ll(argv[optind]);
step = 1;
end = parse_ll(argv[optind + 1]);
} else if (arg_count == 3) {
start = parse_ll(argv[optind]);
step = parse_ull(argv[optind + 1]);
end = parse_ll(argv[optind + 2]);
} else {
streams.err.append_format(BUILTIN_ERR_TOO_MANY_ARGUMENTS, cmd);
return STATUS_INVALID_ARGS;
}
if (parse_error) {
return STATUS_INVALID_ARGS;
} else if (start >= end) {
streams.err.append_format(L"%ls: END must be greater than START\n", cmd);
return STATUS_INVALID_ARGS;
} else if (step == 0) {
streams.err.append_format(L"%ls: STEP must be a positive integer\n", cmd);
return STATUS_INVALID_ARGS;
}
}
// only for negative argument
auto safe_abs = [](long long ll) -> unsigned long long {
return -static_cast<unsigned long long>(ll);
};
long long real_end;
if (start >= 0 || end < 0) {
// 0 <= start <= end
long long diff = end - start;
// 0 <= diff <= LL_MAX
real_end = start + static_cast<long long>(diff / step);
} else {
// start < 0 <= end
unsigned long long abs_start = safe_abs(start);
unsigned long long diff = (end + abs_start);
real_end = diff / step - abs_start;
}
if (!choice && start == real_end) {
streams.err.append_format(L"%ls: range contains only one possible value\n", cmd);
return STATUS_INVALID_ARGS;
}
std::uniform_int_distribution<long long> dist(start, real_end);
long long random = dist(engine);
long long result;
if (start >= 0) {
// 0 <= start <= random <= end
long long diff = random - start;
// 0 < step * diff <= end - start <= LL_MAX
result = start + static_cast<long long>(diff * step);
} else if (random < 0) {
// start <= random < 0
long long diff = random - start;
result = diff * step - safe_abs(start);
} else {
// start < 0 <= random
unsigned long long abs_start = safe_abs(start);
unsigned long long diff = (random + abs_start);
result = diff * step - abs_start;
}
if (choice) {
streams.out.append_format(L"%ls\n", argv[optind + result]);
} else {
streams.out.append_format(L"%lld\n", result);
}
return STATUS_CMD_OK;
}

9
src/builtin_random.h Normal file
View File

@ -0,0 +1,9 @@
// Prototypes for executing builtin_random function.
#ifndef FISH_BUILTIN_RANDOM_H
#define FISH_BUILTIN_RANDOM_H
class parser_t;
struct io_streams_t;
int builtin_random(parser_t &parser, io_streams_t &streams, wchar_t **argv);
#endif