diff options
author | Gavin Howard <gavin@yzena.com> | 2021-06-24 10:49:00 -0600 |
---|---|---|
committer | Gavin Howard <gavin@yzena.com> | 2021-06-24 10:49:00 -0600 |
commit | 6110b6c977dbca97a5d3b084503705b544de3604 (patch) | |
tree | 09ad90cd135c6935cd8ddd7d02bbc025a764d947 | |
parent | 29e4d559c2bcfb8168ad1f124aa2758e7697c5d1 (diff) | |
download | platform_external_bc-6110b6c977dbca97a5d3b084503705b544de3604.tar.gz platform_external_bc-6110b6c977dbca97a5d3b084503705b544de3604.tar.bz2 platform_external_bc-6110b6c977dbca97a5d3b084503705b544de3604.zip |
More documentation and cleanup work
Signed-off-by: Gavin Howard <gavin@yzena.com>
-rw-r--r-- | NEWS.md | 21 | ||||
-rwxr-xr-x | configure.sh | 6 | ||||
-rw-r--r-- | include/args.h | 4 | ||||
-rw-r--r-- | include/file.h | 79 | ||||
-rw-r--r-- | include/history.h | 47 | ||||
-rw-r--r-- | include/opt.h | 1 | ||||
-rw-r--r-- | include/read.h | 29 | ||||
-rw-r--r-- | include/version.h | 3 | ||||
-rw-r--r-- | manuals/bc.1.md.in | 16 | ||||
-rw-r--r-- | manuals/dc.1.md.in | 44 | ||||
-rw-r--r-- | manuals/development.md | 25 | ||||
-rw-r--r-- | src/args.c | 38 | ||||
-rw-r--r-- | src/bc.c | 5 | ||||
-rw-r--r-- | src/data.c | 26 | ||||
-rw-r--r-- | src/dc.c | 5 | ||||
-rw-r--r-- | src/dc_lex.c | 48 | ||||
-rw-r--r-- | src/history.c | 2 | ||||
-rw-r--r-- | src/read.c | 26 | ||||
-rw-r--r-- | src/vm.c | 4 |
19 files changed, 371 insertions, 58 deletions
@@ -1,12 +1,12 @@ # News -## 4.1.0 +## 5.0.0 This is a production release with several changes: * Add support for OpenBSD's `pledge()` and `unveil()`. * Fix print bug where a backslash newline combo was printed even if only one - digit was left, something I blindly did as well, like a fool. + digit was left, something I blindly copied from GNU `bc`, like a fool. * Fix bugs in the manuals. * Fix a possible multiplication overflow in power. * Temporary numbers are garbage collected if allocation fails, and the @@ -15,6 +15,23 @@ This is a production release with several changes: * `bc` and `dc` now do not give fatal errors when trying to allocate more space for temporary numbers. Instead, more temporary numbers are just freed. * The number of temporary numbers is now limited. +* Allow integers with non-zero `scale` to be used with power, places, and shift + operators. +* Added greatest common divisor and least common multiple to `lib2.bc`. +* Added `SIGQUIT` handling to history. +* Added a command to `dc` (`y`) to get the length of register stacks. +* Fixed multi-digit bugs in `lib2.bc`. +* Removed the no prompt build option. +* Created settings that builders can set defaults for and users can set their + preferences for. This includes the `bc` banner, resetting on `SIGINT`, TTY + mode, and prompt. +* Added history support to Windows. +* Made using `[` as a register name an error because it just cannot easily be + checked for without doing weird things on `stdin`. +* Clarify the above situation in the manuals, since it can't really be avoided + with the terrible interaction of REPL and language constraints. +* Add a new error type and message for `dc` when register stacks don't have + enough items. ## 4.0.2 diff --git a/configure.sh b/configure.sh index f1de8a63..c146b84d 100755 --- a/configure.sh +++ b/configure.sh @@ -891,6 +891,10 @@ elif [ -z "${HOSTCFLAGS+set}" ]; then HOSTCFLAGS="$HOST_CFLAGS" fi +# Store these for the cross compilation detection later. +OLDCFLAGS="$CFLAGS" +OLDHOSTCFLAGS="$HOSTCFLAGS" + link="@printf 'No link necessary\\\\n'" main_exec="BC" executable="BC_EXEC" @@ -1179,7 +1183,7 @@ if [ "$nls" -ne 0 ]; then # It turns out that POSIX locales are really terrible, and running # gencat on one machine is not guaranteed to make those cat files # portable to another machine, so we had better warn the user here. - if [ "$HOSTCC" != "$CC" ] || [ "$HOSTCFLAGS" != "$CFLAGS" ]; then + if [ "$HOSTCC" != "$CC" ] || [ "$OLDHOSTCFLAGS" != "$OLDCFLAGS" ]; then printf 'Cross-compile detected.\n\n' printf 'WARNING: Catalog files generated with gencat may not be portable\n' printf ' across different architectures.\n\n' diff --git a/include/args.h b/include/args.h index 7aef80a9..6db7ff5e 100644 --- a/include/args.h +++ b/include/args.h @@ -37,6 +37,7 @@ #define BC_ARGS_H #include <status.h> +#include <opt.h> #include <vm.h> /** @@ -48,4 +49,7 @@ */ void bc_args(int argc, char *argv[], bool exit_exprs); +// A reference to the list of long options. +extern const BcOptLong bc_args_lopt[]; + #endif // BC_ARGS_H diff --git a/include/file.h b/include/file.h index fbd4b9b9..db087d68 100644 --- a/include/file.h +++ b/include/file.h @@ -52,38 +52,117 @@ typedef struct BcFile { } BcFile; #if BC_ENABLE_HISTORY + +/// Types of flushing. These are important because of history and printing +/// strings without newlines, something that users could use as their own +/// prompts. typedef enum BcFlushType { + /// Do not clear the stored partial line, but don't add to it. BC_FLUSH_NO_EXTRAS_NO_CLEAR, + + /// Do not clear the stored partial line and add to it. BC_FLUSH_SAVE_EXTRAS_NO_CLEAR, + + /// Clear the stored partial line and do not save the new stuff either. BC_FLUSH_NO_EXTRAS_CLEAR, + + /// Clear the stored partial line, but save the new stuff. BC_FLUSH_SAVE_EXTRAS_CLEAR, } BcFlushType; + #else // BC_ENABLE_HISTORY + +// These make sure that the BcFlushType parameter disappears if history is not +// used. + #define bc_file_putchar(f, t, c) bc_file_putchar(f, c) #define bc_file_flushErr(f, t) bc_file_flushErr(f) #define bc_file_flush(f, t) bc_file_flush(f) #define bc_file_write(f, t, b, n) bc_file_write(f, b, n) #define bc_file_puts(f, t, s) bc_file_puts(f, s) + #endif // BC_ENABLE_HISTORY +/** + * Initialize a file. + * @param f The file to initialize. + * @param fd The file descriptor. + * @param buf The buffer for the file. + * @param cap The capacity of the buffer. + */ void bc_file_init(BcFile *f, int fd, char *buf, size_t cap); + +/** + * Frees a file, including flushing it. + * @param f The file to free. + */ void bc_file_free(BcFile *f); +/** + * Print a char into the file. + * @param f The file to print to. + * @param type The flush type. + * @param c The character to write. + */ void bc_file_putchar(BcFile *restrict f, BcFlushType type, uchar c); + +/** + * Flush and return an error if it failed. This is meant to be used when needing + * to flush in error situations when an error is already in flight. It would be + * a very bad deal to throw another error. + * @param f The file to flush. + * @param type The flush type. + */ BcStatus bc_file_flushErr(BcFile *restrict f, BcFlushType type); + +/** + * Flush and throw an error on failure. + * @param f The file to flush. + * @param type The flush type. + */ void bc_file_flush(BcFile *restrict f, BcFlushType type); + +/** + * Write the contents of buf to the file. + * @param f The file to flush. + * @param type The flush type. + * @param buf The buffer whose contents will be written to the file. + * @param n The length of buf. + */ void bc_file_write(BcFile *restrict f, BcFlushType type, const char *buf, size_t n); + +/** + * Write to the file like fprintf would. This is very rudimentary. + * @param f The file to flush. + * @param fmt The format string. + */ void bc_file_printf(BcFile *restrict f, const char *fmt, ...); + +/** + * Write to the file like vfprintf would. This is very rudimentary. + * @param f The file to flush. + * @param fmt The format string. + */ void bc_file_vprintf(BcFile *restrict f, const char *fmt, va_list args); + +/** + * Write str to the file. + * @param f The file to flush. + * @param type The flush type. + * @param str The string to write to the file. + */ void bc_file_puts(BcFile *restrict f, BcFlushType type, const char *str); #if BC_ENABLE_HISTORY + +// Some constant flush types for ease of use. extern const BcFlushType bc_flush_none; extern const BcFlushType bc_flush_err; extern const BcFlushType bc_flush_save; + #endif // BC_ENABLE_HISTORY #endif // BC_FILE_H diff --git a/include/history.h b/include/history.h index 15a28e06..b0a2ab94 100644 --- a/include/history.h +++ b/include/history.h @@ -122,19 +122,31 @@ #include <file.h> #endif // BC_DEBUG_CODE +/// Default columns. #define BC_HIST_DEF_COLS (80) + +/// Max number of history entries. #define BC_HIST_MAX_LEN (128) + +/// Max length of a line. #define BC_HIST_MAX_LINE (4095) + +/// Max size for cursor position buffer. #define BC_HIST_SEQ_SIZE (64) +/// The number of entries in the history. #define BC_HIST_BUF_LEN(h) ((h)->buf.len - 1) + +/// Read n characters into s and check the error. #define BC_HIST_READ(s, n) (bc_history_read((s), (n)) == -1) +/// Markers for direction when using arrow keys. #define BC_HIST_NEXT (false) #define BC_HIST_PREV (true) #if BC_DEBUG_CODE +// These are just for debugging. #define BC_HISTORY_DEBUG_BUF_SIZE (1024) #define lndebug(...) \ @@ -157,6 +169,7 @@ #define lndebug(fmt, ...) #endif // BC_DEBUG_CODE +/// An enum of useful actions. typedef enum BcHistoryAction { BC_ACTION_NULL = 0, @@ -246,23 +259,55 @@ typedef struct BcHistory { } BcHistory; +/** + * Get a line from stdin using history. This returns a status because I don't + * want to throw errors while the terminal is in raw mode. + * @param h The history data. + * @param vec A vector to put the line into. + * @param prompt The prompt to display, if desired. + */ BcStatus bc_history_line(BcHistory *h, BcVec *vec, const char *prompt); +/** + * Initialize history data. + * @param h The struct to initialize. + */ void bc_history_init(BcHistory *h); + +/** + * Free history data (and recook the terminal). + * @param h The struct to free. + */ void bc_history_free(BcHistory *h); +// A list of terminals that don't work. extern const char *bc_history_bad_terms[]; + +// A tab in history and its length. extern const char bc_history_tab[]; extern const size_t bc_history_tab_len; + +// A ctrl+c string. extern const char bc_history_ctrlc[]; + +// UTF-8 data arrays. extern const uint32_t bc_history_wchars[][2]; extern const size_t bc_history_wchars_len; extern const uint32_t bc_history_combo_chars[]; extern const size_t bc_history_combo_chars_len; + #if BC_DEBUG_CODE + +// Debug data. extern BcFile bc_history_debug_fp; extern char *bc_history_debug_buf; -void bc_history_printKeyCodes(BcHistory* l); + +/** + * A function to print keycodes for debugging. + * @param h The history data. + */ +void bc_history_printKeyCodes(BcHistory* h); + #endif // BC_DEBUG_CODE #endif // BC_ENABLE_HISTORY diff --git a/include/opt.h b/include/opt.h index e828a92c..e14b8431 100644 --- a/include/opt.h +++ b/include/opt.h @@ -41,6 +41,7 @@ #define BC_OPT_H #include <stdbool.h> +#include <stdlib.h> typedef struct BcOpt { char **argv; diff --git a/include/read.h b/include/read.h index 102cc5cd..007dcaef 100644 --- a/include/read.h +++ b/include/read.h @@ -41,11 +41,38 @@ #include <status.h> #include <vector.h> +/// Returns true if c is a non-ASCII char. #define BC_READ_BIN_CHAR(c) (((c) < ' ' && !isspace((c))) || ((uchar) c) > '~') +/** + * Reads a line from stdin after printing prompt, if desired. + * @param vec The vector to put the stdin data into. + * @param prompt The prompt to print, if desired. + */ BcStatus bc_read_line(BcVec *vec, const char *prompt); -void bc_read_file(const char *path, char **buf); + +/** + * Read a file and return a buffer with the data. The buffer must be freed by + * the caller. + * @param path The path to the file to read. + */ +char* bc_read_file(const char *path); + +/** + * Helper function for reading characters from stdin. This takes care of a bunch + * of complex error handling. Thus, it returns a status instead of throwing an + * error, except for fatal errors. + * @param vec The vec to put the stdin into. + * @param prompt The prompt to print, if desired. + */ BcStatus bc_read_chars(BcVec *vec, const char *prompt); + +/** + * Read a line from buf into vec. + * @param vec The vector to read data into. + * @param buf The buffer to read from. + * @param buf_len The length of the buffer. + */ bool bc_read_buf(BcVec *vec, char *buf, size_t *buf_len); #endif // BC_READ_H diff --git a/include/version.h b/include/version.h index dfbb26e6..e29bbf69 100644 --- a/include/version.h +++ b/include/version.h @@ -36,6 +36,7 @@ #ifndef BC_VERSION_H #define BC_VERSION_H -#define VERSION 4.1.0 +// The current version. +#define VERSION 5.0.0 #endif // BC_VERSION_H diff --git a/manuals/bc.1.md.in b/manuals/bc.1.md.in index 94484a4a..bb710680 100644 --- a/manuals/bc.1.md.in +++ b/manuals/bc.1.md.in @@ -264,6 +264,22 @@ The following are the options that bc(1) accepts. All long options are **non-portable extensions**. +# STDIN + +If no files or expressions are given by the **-f**, **-\-file**, **-e**, or +**-\-expression** options, then bc(1) read from **stdin**. + +However, there are a few caveats to this. + +First, **stdin** is evaluated a line at a time. The only exception to this is if +the parse cannot complete. That means that starting a string without ending it +or starting a function, **if** statement, or loop without ending it will also +cause bc(1) to not execute. + +Second, after an **if** statement, bc(1) doesn't know if an **else** statement +will follow, so it will not execute until it knows there will not be an **else** +statement. + # STDOUT Any non-error output is written to **stdout**. In addition, if history (see the diff --git a/manuals/dc.1.md.in b/manuals/dc.1.md.in index f47d7c59..6916adb9 100644 --- a/manuals/dc.1.md.in +++ b/manuals/dc.1.md.in @@ -42,18 +42,14 @@ dc(1) is an arbitrary-precision calculator. It uses a stack (reverse Polish notation) to store numbers and results of computations. Arithmetic operations pop arguments off of the stack and push the results. -If no files are given on the command-line as extra arguments (i.e., not as -**-f** or **-\-file** arguments), then dc(1) reads from **stdin**. Otherwise, -those files are processed, and dc(1) will then exit. - -This is different from the dc(1) on OpenBSD and possibly other dc(1) -implementations, where **-e** (**-\-expression**) and **-f** (**-\-file**) -arguments cause dc(1) to execute them and exit. The reason for this is that this -dc(1) allows users to set arguments in the environment variable **DC_ENV_ARGS** -(see the **ENVIRONMENT VARIABLES** section). Any expressions given on the -command-line should be used to set up a standard environment. For example, if a -user wants the **scale** always set to **10**, they can set **DC_ENV_ARGS** to -**-e 10k**, and this dc(1) will always start with a **scale** of **10**. +If no files are given on the command-line, then dc(1) reads from **stdin** (see +the **STDIN** section). Otherwise, those files are processed, and dc(1) will +then exit. + +If a user wants to set up a standard environment, they can use **DC_ENV_ARGS** +(see the **ENVIRONMENT VARIABLES** section). For example, if a user wants the +**scale** always set to **10**, they can set **DC_ENV_ARGS** to **-e 10k**, and +this dc(1) will always start with a **scale** of **10**. # OPTIONS @@ -137,6 +133,25 @@ The following are the options that dc(1) accepts. All long options are **non-portable extensions**. +# STDIN + +If no files are given on the command-line and no files or expressions are given +by the **-f**, **-\-file**, **-e**, or **-\-expression** options, then dc(1) +read from **stdin**. + +However, there are a few caveats to this. + +First, **stdin** is evaluated a line at a time. The only exception to this is if +a string has been finished, but not ended. This means that, except for escaped +brackets, all brackets must be balanced before dc(1) parses and executes. + +Second, as a consequence of the above, if the user attempts to use the left +bracket character, **[**, as a register name, dc(1) will not execute until a +balancing right bracket, **]** is given. Then it will give an error since it is +an error to use the left bracket as register name (see the **REGISTERS** +section). In fact, this is why it is an error. + + # STDOUT Any non-error output is written to **stdout**. In addition, if history (see the @@ -958,8 +973,9 @@ the stack for the register. All registers, when first referenced, have one value off of the register stack. In non-extended register mode, a register name is just the single character that -follows any command that needs a register name. The only exception is a newline -(**'\\n'**); it is a parse error for a newline to be used as a register name. +follows any command that needs a register name. The only exceptions are: a +newline (**'\\n'**) and a left bracket (**'['**); it is a parse error for a +newline or a left bracket to be used as a register name. ## Extended Register Mode diff --git a/manuals/development.md b/manuals/development.md index 373cea47..9b91ddbf 100644 --- a/manuals/development.md +++ b/manuals/development.md @@ -553,6 +553,9 @@ The code associated with this header is in [`src/file.c`][47]. This header is for `bc`'s implementation of command-line editing/history, which is based on a [UTF-8-aware fork][28] of [`linenoise`][29]. +At one point, I attempted to get history to work on Windows. It did not work +out. Windows + The code associated with this header is in [`src/history.c`][48]. #### `lang.h` @@ -1087,6 +1090,28 @@ A list of the various settings combos to be used by [`test_settings.sh`][104]. ### `src/` +This folder is, obviously, where the actual meat, the source code, is. + +#### `args.c` + +Code for processing command-line arguments. + +#### `bc.c` + +The code for the `bc` main function `bc_main()`. + +#### `bc_lex.c` + +The code for lexing that only `bc` needs. + +#### `dc.c` + +The code for the `dc` main function `dc_main()`. + +#### `dc_lex.c` + +The code for lexing that only `dc` needs. + ### `tests/` ## POSIX Shell Scripts @@ -48,31 +48,6 @@ #include <args.h> #include <opt.h> -/// The list of long options. -static const BcOptLong bc_args_lopt[] = { - - { "expression", BC_OPT_REQUIRED, 'e' }, - { "file", BC_OPT_REQUIRED, 'f' }, - { "help", BC_OPT_NONE, 'h' }, - { "interactive", BC_OPT_NONE, 'i' }, - { "no-prompt", BC_OPT_NONE, 'P' }, - { "no-read-prompt", BC_OPT_NONE, 'R' }, -#if BC_ENABLED - { "global-stacks", BC_OPT_BC_ONLY, 'g' }, - { "mathlib", BC_OPT_BC_ONLY, 'l' }, - { "quiet", BC_OPT_BC_ONLY, 'q' }, - { "standard", BC_OPT_BC_ONLY, 's' }, - { "warn", BC_OPT_BC_ONLY, 'w' }, -#endif // BC_ENABLED - { "version", BC_OPT_NONE, 'v' }, - { "version", BC_OPT_NONE, 'V' }, -#if DC_ENABLED - { "extended-register", BC_OPT_DC_ONLY, 'x' }, -#endif // DC_ENABLED - { NULL, 0, 0 }, - -}; - /** * Adds @a str to the list of expressions to execute later. * @param str The string to add to the list of expressions. @@ -97,11 +72,20 @@ static void bc_args_file(const char *file) { vm.file = file; - bc_read_file(file, &buf); + buf = bc_read_file(file); + + assert(buf != NULL); + bc_args_exprs(buf); free(buf); } +/** + * Processes command-line arguments. + * @param argc The number of arguments. + * @param argv The arguments. + * @param exit_exprs Whether to exit if expressions are encountered. + */ void bc_args(int argc, char *argv[], bool exit_exprs) { int c; @@ -113,6 +97,8 @@ void bc_args(int argc, char *argv[], bool exit_exprs) { bc_opt_init(&opts, argv); + // This loop should look familiar to anyone who has used getopt() or + // getopt_long() in C. while ((c = bc_opt_parse(&opts, bc_args_lopt)) != -1) { switch (c) { @@ -40,6 +40,11 @@ #include <bc.h> #include <vm.h> +/** + * The main function for bc. + * @param argc The number of arguments. + * @param argv The arguments. + */ void bc_main(int argc, char *argv[]) { vm.read_ret = BC_INST_RET; @@ -33,6 +33,7 @@ * */ +#include <opt.h> #include <args.h> #include <lex.h> #include <parse.h> @@ -93,6 +94,31 @@ const char bc_pledge_end[] = ""; #endif // __OpenBSD__ +/// The list of long options. +const BcOptLong bc_args_lopt[] = { + + { "expression", BC_OPT_REQUIRED, 'e' }, + { "file", BC_OPT_REQUIRED, 'f' }, + { "help", BC_OPT_NONE, 'h' }, + { "interactive", BC_OPT_NONE, 'i' }, + { "no-prompt", BC_OPT_NONE, 'P' }, + { "no-read-prompt", BC_OPT_NONE, 'R' }, +#if BC_ENABLED + { "global-stacks", BC_OPT_BC_ONLY, 'g' }, + { "mathlib", BC_OPT_BC_ONLY, 'l' }, + { "quiet", BC_OPT_BC_ONLY, 'q' }, + { "standard", BC_OPT_BC_ONLY, 's' }, + { "warn", BC_OPT_BC_ONLY, 'w' }, +#endif // BC_ENABLED + { "version", BC_OPT_NONE, 'v' }, + { "version", BC_OPT_NONE, 'V' }, +#if DC_ENABLED + { "extended-register", BC_OPT_DC_ONLY, 'x' }, +#endif // DC_ENABLED + { NULL, 0, 0 }, + +}; + const char* const bc_err_func_header = "Function:"; const char* const bc_err_line = ":%zu"; @@ -40,6 +40,11 @@ #include <dc.h> #include <vm.h> +/** + * The main function for dc. + * @param argc The number of arguments. + * @param argv The arguments. + */ void dc_main(int argc, char *argv[]) { vm.read_ret = BC_INST_POP_EXEC; diff --git a/src/dc_lex.c b/src/dc_lex.c index da0b6668..7ccc1176 100644 --- a/src/dc_lex.c +++ b/src/dc_lex.c @@ -45,24 +45,40 @@ bool dc_lex_negCommand(BcLex *l) { return !BC_LEX_NUM_CHAR(c, false, false); } +/** + * Processes a dc command that needs a register. This is where the + * extended-register extension is implemented. + * @param l The lexer. + */ static void dc_lex_register(BcLex *l) { + // If extended register is enabled and the character is whitespace... if (DC_X && isspace(l->buf[l->i - 1])) { char c; + // Eat the whitespace. bc_lex_whitespace(l); c = l->buf[l->i]; - if (BC_ERR(!isalnum(c) && c != '_')) + // Check for a letter or underscore. + if (BC_ERR(!isalpha(c) && c != '_')) bc_lex_verr(l, BC_ERR_PARSE_CHAR, c); + // Parse a normal identifier. l->i += 1; bc_lex_name(l); } else { - if (BC_ERR(l->buf[l->i - 1] == '\n')) - bc_lex_verr(l, BC_ERR_PARSE_CHAR, '\n'); + + // I don't allow newlines because newlines are used for controlling when + // execution happens, and allowing newlines would just be complex. For + // the same reason, I don't allow the '[' character; it would just be + // too complex. + if (BC_ERR(l->buf[l->i - 1] == '\n' || l->buf[l->i - 1] == '[')) + bc_lex_verr(l, BC_ERR_PARSE_CHAR, l->buf[l->i - 1]); + + // Set the lexer string and token. bc_vec_popAll(&l->str); bc_vec_pushByte(&l->str, (uchar) l->buf[l->i - 1]); bc_vec_pushByte(&l->str, '\0'); @@ -70,16 +86,27 @@ static void dc_lex_register(BcLex *l) { } } +/** + * Parse a dc string. Since dc's strings need to check for balanced brackets, we + * can't just parse bc and dc strings with different start and end characters. + * Oh, and dc strings need to check for escaped brackets. + * @param l The lexer. + */ static void dc_lex_string(BcLex *l) { size_t depth = 1, nls = 0, i = l->i; char c; + // Set the token and clear the string. l->t = BC_LEX_STR; bc_vec_popAll(&l->str); + // This is the meat. As long as we don't run into the NUL byte, and we have + // "depth", which means we haven't completely balanced brackets yet, we + // continue eating the string. for (; (c = l->buf[i]) && depth; ++i) { + // Check for escaped brackets and set the depths as appropriate. if (c == '\\') { c = l->buf[++i]; if (!c) break; @@ -89,11 +116,13 @@ static void dc_lex_string(BcLex *l) { depth -= (c == ']'); } + // We want to adjust the line in the lexer as necessary. nls += (c == '\n'); if (depth) bc_vec_push(&l->str, &c); } + // Obviously, if we didn't balance, that's an error. if (BC_ERR(c == '\0' && depth)) { l->i = i; bc_lex_err(l, BC_ERR_PARSE_STRING); @@ -105,11 +134,17 @@ static void dc_lex_string(BcLex *l) { l->line += nls; } +/** + * Lex a dc token. This is the dc implementation of BcLexNext. + * @param l The lexer. + */ void dc_lex_token(BcLex *l) { char c = l->buf[l->i++], c2; size_t i; + // If the last token was a command that needs a register, we need to parse a + // register, so do so. for (i = 0; i < dc_lex_regs_len; ++i) { if (l->last == dc_lex_regs[i]) { dc_lex_register(l); @@ -117,13 +152,16 @@ void dc_lex_token(BcLex *l) { } } + // These lines are for tokens that easily correspond to one character. We + // just set the token. if (c >= '"' && c <= '~' && (l->t = dc_lex_tokens[(c - '"')]) != BC_LEX_INVALID) { return; } - // This is the workhorse of the lexer. + // This is the workhorse of the lexer when more complicated things are + // needed. switch (c) { case '\0': @@ -138,6 +176,8 @@ void dc_lex_token(BcLex *l) { break; } + // We don't have the ! command, so we always expect certain things + // after the exclamation point. case '!': { c2 = l->buf[l->i]; diff --git a/src/history.c b/src/history.c index 5d5fa559..5c1a8c4b 100644 --- a/src/history.c +++ b/src/history.c @@ -174,6 +174,7 @@ static void bc_history_add_empty(BcHistory *h); /** * Check if the code is a wide character. + * @param cp The codepoint to check. */ static bool bc_history_wchar(uint32_t cp) { @@ -196,6 +197,7 @@ static bool bc_history_wchar(uint32_t cp) { /** * Check if the code is a combining character. + * @param cp The codepoint to check. */ static bool bc_history_comboChar(uint32_t cp) { @@ -53,6 +53,11 @@ #include <program.h> #include <vm.h> +/** + * A portability file open function. + * @param path The path to the file to open. + * @param mode The mode to open in. + */ static int bc_read_open(const char* path, int mode) { int fd; @@ -67,6 +72,11 @@ static int bc_read_open(const char* path, int mode) { return fd; } +/** + * Returns true if the buffer data is non-ASCII. + * @param buf The buffer to test. + * @param size The size of the buffer. + */ static bool bc_read_binary(const char *buf, size_t size) { size_t i; @@ -197,12 +207,13 @@ BcStatus bc_read_line(BcVec *vec, const char *prompt) { return s; } -void bc_read_file(const char *path, char **buf) { +char* bc_read_file(const char *path) { BcErr e = BC_ERR_FATAL_IO_ERR; size_t size, r; struct stat pstat; int fd; + char* buf; BC_SIG_ASSERT_LOCKED; @@ -224,25 +235,26 @@ void bc_read_file(const char *path, char **buf) { } size = (size_t) pstat.st_size; - *buf = bc_vm_malloc(size + 1); + buf = bc_vm_malloc(size + 1); - r = (size_t) read(fd, *buf, size); + r = (size_t) read(fd, buf, size); if (BC_ERR(r != size)) goto read_err; - (*buf)[size] = '\0'; + buf[size] = '\0'; - if (BC_ERR(bc_read_binary(*buf, size))) { + if (BC_ERR(bc_read_binary(buf, size))) { e = BC_ERR_FATAL_BIN_FILE; goto read_err; } close(fd); - return; + return buf; read_err: - free(*buf); + free(buf); malloc_err: close(fd); bc_vm_verr(e, path); + return NULL; } @@ -742,7 +742,9 @@ static void bc_vm_file(const char *file) { BC_SIG_LOCK; - bc_read_file(file, &data); + data = bc_read_file(file); + + assert(data != NULL); BC_SETJMP_LOCKED(err); |