crepl

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

commit 2dc0d6830530017506ff961a6a6ad2f3b530cf98
parent 55078ff21d52c4ba961f6a33eae4af60bdd2d05f
Author: Demonstrandum <moi@knutsen.co>
Date:   Wed, 24 Jun 2020 17:10:23 +0100

Fix how functions are parsed.

Diffstat:
MMakefile | 7+++++--
Asrc/builtin.c | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/builtin.h | 11+++++++++++
Msrc/displays.c | 1+
Msrc/execute.c | 7++-----
Msrc/main.c | 28+++++++++++++++++++++-------
Msrc/parse.c | 21++++++++++++++++++++-
Msrc/parse.h | 2+-
8 files changed, 164 insertions(+), 16 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,9 +1,9 @@ CC := gcc OPT := -O3 -WARN := -Wall -Wpedantic -Wextra -Wshadow +WARN := -Wall -Wpedantic -Wextra -Wshadow -fcompare-debug-second CFLAGS := $(WARN) $(OPT) TARGET := crepl -OBJS := main.o prelude.o parse.o displays.o error.o execute.o +OBJS := main.o prelude.o error.o parse.o displays.o builtin.o execute.o LINKS := -lm -lreadline ifeq ($(PREFIX),) @@ -36,6 +36,9 @@ parse.o: error.o displays.o: parse.o $(CC) -c $(CFLAGS) src/displays.c +builtin.o: + $(CC) -c $(CFLAGS) src/builtin.c + execute.o: parse.o error.o $(CC) -c $(CFLAGS) src/execute.c diff --git a/src/builtin.c b/src/builtin.c @@ -0,0 +1,103 @@ +#include "builtin.h" + +NumberNode num_to_float(NumberNode num) +{ + NumberNode result = num; // Copy. + + switch (num.type) { + case INT: + result.type = FLOAT; + result.value.f = (fsize)num.value.i; + break; + case FLOAT: + break; + default: { + ERROR_TYPE = EXECUTION_ERROR; + strcpy(ERROR_MSG, "Unsupported number type."); + } + } + return result; +} + +NumberNode num_to_int(NumberNode num) +{ + NumberNode result = num; // Copy. + + switch (num.type) { + case FLOAT: + result.type = INT; + result.value.i = (ssize)num.value.f; + break; + case INT: + break; + default: { + ERROR_TYPE = EXECUTION_ERROR; + strcpy(ERROR_MSG, "Unsupported number type."); + } + } + return result; +} + +NumberNode *upcast_pair(NumberNode lhs, NumberNode rhs) +{ + NumberNode *pair = malloc(2 * sizeof(NumberNode)); + + switch (lhs.type) { + case FLOAT: { + pair[0] = lhs; + pair[1] = num_to_float(rhs); + break; + } + case INT: { + switch (rhs.type) { + case INT: { + pair[0] = lhs; + pair[1] = rhs; + break; + } + case FLOAT: { + pair[0] = num_to_float(lhs); + pair[1] = rhs; + break; + } + default: + goto error; + } + break; + } + error: + default: { + ERROR_TYPE = EXECUTION_ERROR; + strcpy(ERROR_MSG, "Unsupported number type."); + return NULL; + } + } + return pair; +} + +NumberNode *num_add(NumberNode lhs, NumberNode rhs) +{ + NumberNode *upcasted = upcast_pair(lhs, rhs); + if (upcasted == NULL) + return NULL; + + NumberNode *result = upcasted + 0; + + switch (result->type) { + case FLOAT: + result->value.f = upcasted[0].value.f + upcasted[1].value.f; + break; + case INT: + result->value.i = upcasted[0].value.i + upcasted[1].value.i; + break; + default: { + ERROR_TYPE = EXECUTION_ERROR; + strcpy(ERROR_MSG, "Unsupported number type."); + return NULL; + } + } + result = realloc(result, sizeof(NumberNode)); + return result; +} + + diff --git a/src/builtin.h b/src/builtin.h @@ -0,0 +1,11 @@ +#pragma once + +#include "prelude.h" +#include "parse.h" +#include "error.h" + +NumberNode num_to_float(NumberNode); +NumberNode num_to_int(NumberNode); +NumberNode *upcast_pair(NumberNode, NumberNode); +NumberNode *num_add(NumberNode, NumberNode); + diff --git a/src/displays.c b/src/displays.c @@ -1,6 +1,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <wchar.h> #include "prelude.h" #include "parse.h" diff --git a/src/execute.c b/src/execute.c @@ -1,6 +1,7 @@ #include "execute.h" #include "error.h" #include "parse.h" +#include "builtin.h" #include "displays.h" /// Takes in an execution context (ctx) and a @@ -33,13 +34,9 @@ DataValue *execute(Context *ctx, const ParseNode *stmt) NumberNode *r_num = type_check("+", RHS, T_NUMBER, rhs); if (l_num == NULL || r_num == NULL) return NULL; - // TODO: Handle more numbers tham just INTs. - NumberNode *result_num = malloc(sizeof(NumberNode)); - memcpy(result_num, l_num, sizeof(NumberNode)); // Finally, the addition is performed. - result_num->value.i = l_num->value.i + r_num->value.i; data->type = T_NUMBER; - data->value = result_num; + data->value = num_add(*l_num, *r_num); } else if (strcmp(op, "-") == 0) { } else { diff --git a/src/main.c b/src/main.c @@ -3,6 +3,7 @@ #include <string.h> #include <signal.h> #include <sys/stat.h> +#include <wchar.h> #include <readline/readline.h> #include <readline/history.h> @@ -15,10 +16,10 @@ static const char *PROMPT = "::> "; -void sigint_handle(int _) +void sigint_handle(int sig) { printf("\b\b "); // Obsucre '^C' output. - printf("\nInterrupted, [Ctrl-D] to stop inputting.\n"); + printf("\nInterrupted (%d), [Ctrl-D] to stop inputting.\n", sig); rl_on_new_line(); rl_replace_line("", 0); rl_redisplay(); @@ -33,9 +34,16 @@ int main(int argc, char **argv) puts(" (" COMPILER ") (" __DATE__ ")"); puts("Type \"exit\" or [Ctrl-D] (i.e. EOF) to quit."); + bool verbose = false; + // Parse command line arguments. + for (int i = 0; i < argc; ++i) { + if (strcmp(argv[i], "-v") == 0) + verbose = true; + } + // Configure readline. rl_clear_signals(); - rl_bind_key('\t', rl_complete); + rl_bind_key('\t', rl_insert); signal(SIGINT, sigint_handle); // Create or fetch history file. @@ -45,12 +53,16 @@ int main(int argc, char **argv) if ((tmp = getenv("XDG_CACHE_HOME")) != NULL) strcpy(cache_loc, tmp); else if ((tmp = getenv("HOME")) != NULL) - sprintf(cache_loc, "%s/.cache/", tmp); + sprintf(cache_loc, "%s/.cache", tmp); mkdir(cache_loc, 0777); strcat(cache_loc, "/crepl.history"); read_history(cache_loc); + if (verbose) { + printf("Reading history from `%s'.\n", cache_loc); + } + Context *ctx = init_context(); char *response = NULL; @@ -73,6 +85,11 @@ int main(int argc, char **argv) continue; } + printf("\033[%luC\033[1A", + strlen(PROMPT) + + strlen(response)); + printf(" ≡ %s\n", display_parsetree(tree)); + DataValue *result = execute(ctx, tree); if (result == NULL || ERROR_TYPE != NO_ERROR) { @@ -80,13 +97,10 @@ int main(int argc, char **argv) continue; } - printf("\033[%luC\033[1A", strlen(PROMPT) + strlen(response)); - printf(" ≡ %s\n", display_parsetree(tree)); printf("#=> %s\n", display_datavalue(result)); //free_datavalue(result); free_parsenode(tree); - free(response); } while (true); write_history(cache_loc); diff --git a/src/parse.c b/src/parse.c @@ -5,6 +5,7 @@ #include <string.h> #include "error.h" +#include "displays.h" #include "parse.h" void free_token(Token *token) @@ -232,6 +233,8 @@ ParseNode *parse_prefix(const Token *token, char **rest) node->type = UNARY_NODE; node->node.unary = *unary; + printf("Parsed prefix as: %s\n\n", display_parsetree(node)); + break; } case TT_LPAREN: { @@ -372,23 +375,39 @@ ParseNode *parse_expr(char **slice, u16 precedence) ParseNode *left = parse_prefix(token, slice); + if (left == NULL) + return NULL; + Token *token_ahead = peek(slice); + if (token_ahead == NULL) return left; u16 current_precedence = token_precedence(token_ahead); u16 previous_precedence = 0; + usize count = 0; while (precedence < current_precedence) { + count += 1; + if (count > 301) { + ERROR_TYPE = PARSE_ERROR; + strcpy(ERROR_MSG, "Could not finish parsing expression."); + break; + } if (current_precedence != FUNCTION_PRECEDENCE) token = lex(slice); + else + token = peek(slice); + + if (token == NULL) + break; left = parse_infix(left, token, slice, previous_precedence); if (left == NULL || **slice == '\0') break; token_ahead = peek(slice); - if (token == NULL) + if (token_ahead == NULL) break; previous_precedence = current_precedence; diff --git a/src/parse.h b/src/parse.h @@ -60,7 +60,7 @@ static const Operator KNOWN_OPERATORS[] = { { ">", 4, LEFT_ASSOC, INFIX }, { "<", 4, LEFT_ASSOC, INFIX }, { "=", 2, RIGHT_ASSOC, INFIX }, - { ",", 1, LEFT_ASSOC, INFIX }, + { ",", 1, RIGHT_ASSOC, INFIX }, }; // Parse tree nodes: