crepl

An intuitive calculator REPL.
git clone git://git.knutsen.co/crepl
Log | Files | Refs | README | LICENSE

commit 2371441cbf6ff1ee4ebbbcece350721798bb8b57
parent 544607481e0d7f0a5b595fd4c30d0481e46a446b
Author: Demonstrandum <moi@knutsen.co>
Date:   Mon, 17 Aug 2020 01:37:37 +0100

Ability to cancel execution.

Diffstat:
MMakefile | 4++--
Msrc/builtin.c | 13+++++++++++++
Msrc/builtin.h | 3+++
Msrc/defaults.c | 17+++++++++++++++--
Msrc/defaults.h | 5+++--
Msrc/main.c | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Msrc/parse.c | 25+++++++++++++------------
7 files changed, 107 insertions(+), 46 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,7 +1,7 @@ CC := gcc OPT := -O3 -WARN := -Wall -Wpedantic -Wextra -Wshadow -fcompare-debug-second -LINKS := -lm -lreadline +WARN := -Wall -Wpedantic -Wextra -Wshadow -Wno-psabi +LINKS := -lm -lreadline -lpthread CFLAGS := $(WARN) $(OPT) TARGET := crepl OBJS := main.o defaults.o error.o parse.o displays.o builtin.o execute.o prelude.o diff --git a/src/builtin.c b/src/builtin.c @@ -76,6 +76,19 @@ NumberNode *upcast_pair(NumberNode lhs, NumberNode rhs) return pair; } +DataValue *builtin_sleep(DataValue seconds) +{ + NumberNode *num = type_check("sleep", ARG, T_NUMBER, &seconds); + if (num == NULL) return NULL; + + NumberNode *time = malloc(sizeof(NumberNode)); + *time = num_to_int(*num); + if (time->value.i < 0) time->value.i = 0; + + sleep((unsigned)time->value.i); + return wrap_data(T_NUMBER, time); +} + #define MATH_WRAPPER(NAME, FUNC)\ DataValue *builtin_ ##NAME (DataValue input) \ { \ diff --git a/src/builtin.h b/src/builtin.h @@ -1,6 +1,7 @@ #pragma once #include <math.h> +#include <unistd.h> #include "defaults.h" #include "parse.h" @@ -13,6 +14,7 @@ NumberNode *upcast_pair(NumberNode, NumberNode); fsize gamma_func(float, fsize); fsize gammae(fsize); +DataValue *builtin_sleep(DataValue); DataValue *builtin_sin(DataValue); DataValue *builtin_sinh(DataValue); DataValue *builtin_cos(DataValue); @@ -52,6 +54,7 @@ struct _func_name_pair { }; static const struct _func_name_pair builtin_fns[] = { + FUNC_PAIR(sleep), FUNC_PAIR(sin), FUNC_PAIR(sinh), FUNC_PAIR(cos), diff --git a/src/defaults.c b/src/defaults.c @@ -15,7 +15,20 @@ ssize ipow(ssize base, usize exp) return result; } -char *trim(char *str) +char *remove_all_char(const char *str, char chr) +{ + char *new = strdup(str); + size_t str_len = strlen(str); + size_t new_len = 0; + + for (size_t i = 0; i < str_len; ++i) + if (str[i] != chr) new[new_len++] = str[i]; + + new[new_len] = '\0'; + return new; +} + +char *trim(const char *str) { char *p = strdup(str); while (isspace(*p)) @@ -30,7 +43,7 @@ char *trim(char *str) } -char *downcase(char *str) +char *downcase(const char *str) { char *p = strdup(str); char *start = p; diff --git a/src/defaults.h b/src/defaults.h @@ -38,8 +38,9 @@ typedef long double fsize; ssize ipow(ssize, usize); -char *trim(char *); -char *downcase(char *); +char *remove_all_char(const char *, char); +char *trim(const char *); +char *downcase(const char *); #define STR_HELPER(x) #x #define STR(x) STR_HELPER(x) diff --git a/src/main.c b/src/main.c @@ -1,7 +1,9 @@ #include <stdio.h> #include <stdlib.h> +#include <unistd.h> #include <string.h> #include <signal.h> +#include <pthread.h> #include <sys/stat.h> #include <wchar.h> @@ -16,13 +18,61 @@ static const char *PROMPT = "::> "; +char *response = NULL; +pthread_t thread_id = 0; + void sigint_handle(int sig) { printf("\b\b "); // Obsucre '^C' output. - printf("\nInterrupted (%d), [Ctrl-D] to stop inputting.\n", sig); - rl_on_new_line(); - rl_replace_line("", 0); - rl_redisplay(); + if (thread_id == 0) { + puts("\b\b\033[90m//----\033[0m"); + printf("\033[1m"); + printf("Signal (%d), [Ctrl-D] to stop inputting.\n", sig); + printf("\033[0m"); + rl_on_new_line(); + rl_replace_line("", 0); + rl_redisplay(); + } else { + pthread_cancel(thread_id); + printf("\b\b\033[1mInterrputed expression evaluation"); + printf(" (thread 0x%lX).\033[0m\n", thread_id); + thread_id = 0; + } + +} + +void *evaluation_thread(void *ctx_void) +{ + Context *ctx = ctx_void; + + ParseNode *tree = NULL; + DataValue *result = NULL; + + tree = parse(response); + + if (tree == NULL || ERROR_TYPE != NO_ERROR) { + handle_error(); + return (void *)EXIT_FAILURE; + } + + printf("\033[%luC\033[1A", + strlen(PROMPT) + + strlen(response)); + printf("\033[2m ≡ %s\033[0m\n", display_parsetree(tree)); + + result = execute(ctx, tree); + + if (result == NULL || ERROR_TYPE != NO_ERROR) { + handle_error(); + return (void *)EXIT_FAILURE; + } + + if (result != NULL) + printf("#=> %s\n", display_datavalue(result)); + if (tree != NULL) + free_parsenode(tree); + + return (void *)EXIT_SUCCESS; } int main(int argc, char **argv) @@ -65,7 +115,6 @@ int main(int argc, char **argv) Context *ctx = base_context(); - char *response = NULL; do { char *line = readline(PROMPT); @@ -77,29 +126,10 @@ int main(int argc, char **argv) add_history(line); response = line; - // Try to lex & parse the input. - ParseNode *tree = parse(response); - - if (tree == NULL || ERROR_TYPE != NO_ERROR) { - handle_error(); - continue; - } - - printf("\033[%luC\033[1A", - strlen(PROMPT) - + strlen(response)); - printf("\033[2m ≡ %s\033[0m\n", display_parsetree(tree)); - - DataValue *result = execute(ctx, tree); - - if (result == NULL || ERROR_TYPE != NO_ERROR) { - handle_error(); - continue; - } - - printf("#=> %s\n", display_datavalue(result)); - - free_parsenode(tree); + // Evaluation of input is done in thread. + pthread_create(&thread_id, NULL, evaluation_thread, ctx); + pthread_join(thread_id, NULL); + thread_id = 0; } while (true); write_history(cache_loc); diff --git a/src/parse.c b/src/parse.c @@ -49,23 +49,22 @@ TokenType char_token_type(char c, char last_char, TokenType last_token_type) if (c <= '9' && c >= '0') { if (last_token_type == TT_IDENTIFIER) return TT_IDENTIFIER; - else - return TT_NUMERIC; + return TT_NUMERIC; } if (c == '_' - && (last_token_type == TT_IDENTIFIER + && (last_token_type == TT_IDENTIFIER || last_token_type == TT_NUMERIC)) return last_token_type; if (c == '.' && (last_token_type == TT_OPERATOR || last_token_type == TT_NONE)) return TT_NUMERIC; - if ((c == '.' || c == 'E') // Scientific notation. + if ((c == '.' || c == 'E' || c == 'P') // Scientific notation. && last_token_type == TT_NUMERIC) return TT_NUMERIC; if ((c == '+' || c == '-') && last_char == 'E') return TT_NUMERIC; - if ((c == 'x' || c == 'o') && last_char == '0') + if ((c == 'x' || c == 'X' || c == 'o' || c == 'O') && last_char == '0') return TT_NUMERIC; if (c == '(') return TT_LPAREN; @@ -75,12 +74,11 @@ TokenType char_token_type(char c, char last_char, TokenType last_token_type) return TT_NONE; // All possible operator/special-symbol characters: - if ((c >= '!' && c <= '/') - || (c >= ':' && c <= '@') - || (c >= '{' && c <= '~') - || (c >= '[' && c <= '`')) { - return TT_OPERATOR; - } + if (((c >= '!' && c <= '/') + || (c >= ':' && c <= '@') + || (c >= '{' && c <= '~') + || (c >= '[' && c <= '`')) + && c != '_') return TT_OPERATOR; // Anything else is treated as an identifier. return TT_IDENTIFIER; @@ -200,11 +198,14 @@ NumberNode *make_number(NumberType type, void *val) // Parse number literals: // e.g. 3, 8.2, 2E32, 3E+4, 1.6E-19, 0b010110, 0xff32a1, 0o0774, etc. -// TODO: Parse binary, hexadecimal and octal literals (0b, 0x, 0o). +// TODO: Parse binary, hexadecimal and octal literals (0b, 0x, 0o) +// as well as hex/binary `P' power notation.. NumberNode *parse_number(const char *str) { NumberNode *number = malloc(sizeof(NumberNode)); + str = remove_all_char(str, '_'); + char *exponent_ptr = strstr(str, "E"); char *neg_exponent_ptr = strstr(str, "E-"); char *decimal_point_ptr = strstr(str, ".");