crepl

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

commit 55078ff21d52c4ba961f6a33eae4af60bdd2d05f
parent 3b01d7d6fc61e75421d6133e505b709f7c605c58
Author: Demonstrandum <moi@knutsen.co>
Date:   Wed, 24 Jun 2020 12:54:13 +0100

Added execution and addition capability

Diffstat:
MMakefile | 10+++++++---
Msrc/displays.c | 47+++++++++++++++++++++++++++++++++++++++++++++++
Msrc/displays.h | 4++++
Msrc/error.c | 4++++
Msrc/error.h | 1+
Msrc/execute.c | 124+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/execute.h | 40++++++++++++++++++++++++++++++++++++++++
Msrc/main.c | 18++++++++++++++----
8 files changed, 241 insertions(+), 7 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,5 +1,7 @@ CC := gcc -CFLAGS := -Wall -Wpedantic +OPT := -O3 +WARN := -Wall -Wpedantic -Wextra -Wshadow +CFLAGS := $(WARN) $(OPT) TARGET := crepl OBJS := main.o prelude.o parse.o displays.o error.o execute.o LINKS := -lm -lreadline @@ -11,8 +13,11 @@ endif all: clean $(TARGET) @printf "\033[1mBuilt \`$(TARGET)' successfully.\033[0m\n" +debug: $(OBJS) + $(CC) -Og -o $(TARGET) $(LINKS) $(OBJS) + $(TARGET): $(OBJS) - $(CC) -o $(TARGET) $(LINKS) $(OBJS) + $(CC) $(OPT) -o $(TARGET) $(LINKS) $(OBJS) install: $(TARGET) @echo "Installing to $(PREFIX)/bin/$(TARGET)." @@ -25,7 +30,6 @@ main.o: prelude.o parse.o error.o prelude.o: error.o $(CC) -c $(CFLAGS) src/prelude.c - parse.o: error.o $(CC) -c $(CFLAGS) src/parse.c diff --git a/src/displays.c b/src/displays.c @@ -4,8 +4,35 @@ #include "prelude.h" #include "parse.h" +#include "execute.h" #include "displays.h" +char *display_parampos(ParamPos pos) +{ + switch (pos) { + case LHS: + return "left-hand-side"; + case RHS: + return "right-hand-side"; + default: + return "argument"; + } +} + +char *display_datatype(DataType type) +{ + switch (type) { + case T_NUMBER: + return "number"; + case T_FUNCTION_PTR: + return "function"; + case T_STRING: + return "text-string"; + default: + return "unknown-type"; + } +} + char *display_parsetree(const ParseNode *tree) { if (tree == NULL) @@ -53,3 +80,23 @@ char *display_parsetree(const ParseNode *tree) return "[Unknown Parse Node]"; } } + +char *display_datavalue(const DataValue *data) +{ + // Safe bet. + char *string = malloc(sizeof(char) * 512); + + switch (data->type) { + case T_NUMBER: { + NumberNode *num = data->value; + // TODO: Handle more than just INT type. + sprintf(string, "%ld", num->value.i); + break; + } + default: + sprintf(string, "<%s at 0x%p>", + display_datatype(data->type), + data->value); + } + return string; +} diff --git a/src/displays.h b/src/displays.h @@ -1,3 +1,7 @@ #include "parse.h" +#include "execute.h" +char *display_parampos(ParamPos _); +char *display_datatype(DataType _); char *display_parsetree(const ParseNode *); +char *display_datavalue(const DataValue *); diff --git a/src/error.c b/src/error.c @@ -12,6 +12,8 @@ const char *error_name(error_t err) return "Syntax Error"; case PARSE_ERROR: return "Grammar Error"; + case TYPE_ERROR: + return "Type Error"; case EXECUTION_ERROR: return "Error while executing"; default: @@ -26,6 +28,8 @@ char ERROR_MSG[256] = DEFAULT_ERROR_MSG; void handle_error() { + if (ERROR_TYPE == NO_ERROR) + return; // Display error. printf("\033[31;1m%s\033[0m: %s\n", error_name(ERROR_TYPE), diff --git a/src/error.h b/src/error.h @@ -4,6 +4,7 @@ typedef enum { NO_ERROR, SYNTAX_ERROR, PARSE_ERROR, + TYPE_ERROR, EXECUTION_ERROR, } error_t; diff --git a/src/execute.c b/src/execute.c @@ -1 +1,125 @@ #include "execute.h" +#include "error.h" +#include "parse.h" +#include "displays.h" + +/// Takes in an execution context (ctx) and a +/// statement as produced by the parser (stmt). +/// Returns what it evaluates to. +DataValue *execute(Context *ctx, const ParseNode *stmt) +{ + DataValue *data = malloc(sizeof(DataValue)); + switch (stmt->type) { + case NUMBER_NODE: { + data->type = T_NUMBER; + data->value = malloc(sizeof(NumberNode)); + memcpy(data->value, &stmt->node.number, sizeof(NumberNode)); + break; + } + case BINARY_NODE: { + IdentNode ident = stmt->node.binary.callee->node.ident; + if (stmt->node.binary.callee->type != IDENT_NODE) { + ERROR_TYPE = EXECUTION_ERROR; + strcpy(ERROR_MSG, "Binary operation has non-ident callee."); + return NULL; + } + // How to evaluate specific operators. + char *op = ident.value; + DataValue *lhs = execute(ctx, stmt->node.binary.left); + DataValue *rhs = execute(ctx, stmt->node.binary.right); + + if (strcmp(op, "+") == 0) { + NumberNode *l_num = type_check("+", LHS, T_NUMBER, lhs); + 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; + } else if (strcmp(op, "-") == 0) { + + } else { + ERROR_TYPE = EXECUTION_ERROR; + sprintf(ERROR_MSG, "Do not know how to evaluate" + " use of `%s' operator.", op); + return NULL; + } + break; + } + default: { + ERROR_TYPE = EXECUTION_ERROR; + strcpy(ERROR_MSG, + "Could not execute statement for unknown reason."); + return NULL; + } + } + return data; +} + +DataValue *wrap_data(DataType type, void *value) +{ + DataValue *data = malloc(sizeof(DataValue)); + data->type = type; + data->value = value; + return data; +} + +void *type_check(char *function_name, ParamPos pos, + DataType type, DataValue *value) +{ + if (value->type == type) { + return value->value; + } + ERROR_TYPE = TYPE_ERROR; + sprintf(ERROR_MSG, "Wrong type for %s of `%s' operation," + " needed type of `%s'.", + display_parampos(pos), + function_name, + display_datatype(type)); + return NULL; +} + +Local *make_local(char *name, DataType type, void *value) +{ + Local *local = malloc(sizeof(Local)); + local->name = name; + local->value.type = type; + local->value.value = value; + return local; +} + +Context *init_context() +{ + Context *ctx = malloc(sizeof(Context)); + ctx->superior = NULL; // There is no context superior to this one. + ctx->function = "<main>"; // Main function/scope. + + // Initialise with 16 free spaces for local variables. + // This may have to be reallocated if more than 16 + // variables need to exist :^). + ctx->locals_count = 1; + ctx->locals_capacity = 16; + ctx->locals = malloc(sizeof(Local) * ctx->locals_capacity); + + // Create an initial local varaible with the value of the + // name of the function/scope. + Local *scope_name = make_local( + "__this_scope", T_STRING, ctx->function); + ctx->locals = scope_name; + // ^ Sets the first variable, default in every scope + // (good for debuggin purposes). + + return ctx; +} + +Context *make_context(char *scope_name, Context *super_scope) +{ + Context *ctx = init_context(); + ctx->function = scope_name; + ctx->superior = super_scope; + return ctx; +} diff --git a/src/execute.h b/src/execute.h @@ -1 +1,41 @@ +#pragma once + #include "prelude.h" +#include "parse.h" + +typedef enum { + T_NUMBER, // NumberNode (ParsNode). + T_STRING, // (Native?) char pointer. + T_FUNCTION_PTR, // Native function pointer. +} DataType; + +typedef struct { + DataType type; + void *value; +} DataValue; + +typedef struct { + char *name; + DataValue value; +} Local; + +struct _context; + +typedef struct _context { + struct _context *superior; + char *function; + // `locals` works as a dynamic array; + usize locals_count; + usize locals_capacity; + Local *locals; +} Context; + +typedef enum { + ARG, LHS, RHS +} ParamPos; + +void *type_check(char *, ParamPos, DataType, DataValue *); +DataValue *execute(Context *, const ParseNode *); +Local *make_local(char *, DataType, void *); +Context *init_context(); +Context *make_context(char *, Context *); diff --git a/src/main.c b/src/main.c @@ -10,6 +10,7 @@ #include "prelude.h" #include "error.h" #include "parse.h" +#include "execute.h" #include "displays.h" static const char *PROMPT = "::> "; @@ -34,7 +35,7 @@ int main(int argc, char **argv) // Configure readline. rl_clear_signals(); - rl_bind_key('\t', rl_insert); + rl_bind_key('\t', rl_complete); signal(SIGINT, sigint_handle); // Create or fetch history file. @@ -50,6 +51,8 @@ int main(int argc, char **argv) strcat(cache_loc, "/crepl.history"); read_history(cache_loc); + Context *ctx = init_context(); + char *response = NULL; do { char *line = readline(PROMPT); @@ -65,18 +68,25 @@ int main(int argc, char **argv) // Try to lex & parse the input. ParseNode *tree = parse(response); - if (ERROR_TYPE != NO_ERROR) { + if (tree == NULL || ERROR_TYPE != NO_ERROR) { handle_error(); continue; } - if (tree == NULL) continue; + DataValue *result = execute(ctx, tree); + + if (result == NULL || ERROR_TYPE != NO_ERROR) { + handle_error(); + continue; + } printf("\033[%luC\033[1A", strlen(PROMPT) + strlen(response)); printf(" ≡ %s\n", display_parsetree(tree)); - printf("#=> %s\n", response); + printf("#=> %s\n", display_datavalue(result)); + //free_datavalue(result); free_parsenode(tree); + free(response); } while (true); write_history(cache_loc);