commit 4455d5b57facdc9e0d359e064db22923d683f7f7
parent 2371441cbf6ff1ee4ebbbcece350721798bb8b57
Author: knutsen <samuel@knutsen.co>
Date: Fri, 12 Mar 2021 15:02:06 +0000
Begin work for basic (gtk) GUI.
Diffstat:
12 files changed, 476 insertions(+), 56 deletions(-)
diff --git a/Makefile b/Makefile
@@ -2,10 +2,10 @@ CC := gcc
OPT := -O3
WARN := -Wall -Wpedantic -Wextra -Wshadow -Wno-psabi
LINKS := -lm -lreadline -lpthread
-CFLAGS := $(WARN) $(OPT)
+CFLAGS = $(WARN) $(OPT) -funsigned-char
TARGET := crepl
-OBJS := main.o defaults.o error.o parse.o displays.o builtin.o execute.o prelude.o
-
+CDIR := ./src
+OBJS := $(patsubst $(CDIR)/%.c,%.o,$(wildcard $(CDIR)/*.c))
ifeq ($(PREFIX),)
PREFIX := /usr/local
@@ -14,10 +14,15 @@ endif
all: clean $(TARGET)
@printf "\033[1mBuilt \`$(TARGET)' successfully.\033[0m\n"
-debug: CFLAGS := $(WARN) -Og
+debug: OPT := -Og
debug: $(OBJS)
$(CC) -Og -o $(TARGET) $(OBJS) $(LINKS)
+gui: CFLAGS := $(CFLAGS) -DGUI $(shell pkg-config --cflags gtk+-3.0)
+gui: LINKS := $(LINKS) $(shell pkg-config --libs gtk+-3.0)
+gui: clean gui.o $(TARGET)
+ @printf "Built with GUI available, use -g/--gui flag.\n"
+
$(TARGET): $(OBJS)
$(CC) $(OPT) -o $(TARGET) $(OBJS) $(LINKS)
@@ -27,30 +32,12 @@ install: $(TARGET)
install -d $(PREFIX)/bin
install -m 755 $(TARGET) $(PREFIX)/bin
-main.o: defaults.o parse.o error.o
- $(CC) -c $(CFLAGS) src/main.c $(LINKS)
-
-defaults.o: error.o
- $(CC) -c $(CFLAGS) src/defaults.c $(LINKS)
-
-prelude.o:
- $(CC) -c $(CFLAGS) src/prelude.c $(LINKS)
-
-parse.o: error.o
- $(CC) -c $(CFLAGS) src/parse.c $(LINKS)
-
-displays.o: parse.o
- $(CC) -c $(CFLAGS) src/displays.c $(LINKS)
-
-builtin.o:
- $(CC) -c $(CFLAGS) src/builtin.c $(LINKS)
-
-execute.o: parse.o error.o prelude.o
- $(CC) -c $(CFLAGS) src/execute.c $(LINKS)
-
-error.o:
- $(CC) -c $(CFLAGS) src/error.c $(LINKS)
+%.o: $(CDIR)/%.c
+ $(CC) -c $(CFLAGS) -c $< -o $@ $(LINKS)
clean:
@echo "Cleaning previous build."
rm -f $(TARGET) $(OBJS)
+
+
+.PHONY: all clean test debug gui
diff --git a/README.md b/README.md
@@ -27,6 +27,7 @@ sudo make install # Installs the program system wide.
```
## TODO
+ - [ ] A `ref(.)` function, for referencing/aliasing other variables.
- [ ] Throw errors on overflows until we implement bignums.
- [ ] Imaginary numbers (using `complex.h`).
- [ ] User defined functions.
diff --git a/src/defaults.c b/src/defaults.c
@@ -15,9 +15,9 @@ ssize ipow(ssize base, usize exp)
return result;
}
-char *remove_all_char(const char *str, char chr)
+byte *remove_all_bytes(const byte *str, byte chr)
{
- char *new = strdup(str);
+ byte *new = strdup(str);
size_t str_len = strlen(str);
size_t new_len = 0;
@@ -28,13 +28,13 @@ char *remove_all_char(const char *str, char chr)
return new;
}
-char *trim(const char *str)
+byte *trim(const byte *str)
{
- char *p = strdup(str);
+ byte *p = strdup(str);
while (isspace(*p))
++p;
- char *end = p + strlen(p) - 1;
+ byte *end = p + strlen(p) - 1;
while (end > p && isspace(*end))
--end;
@@ -43,10 +43,10 @@ char *trim(const char *str)
}
-char *downcase(const char *str)
+byte *downcase(const byte *str)
{
- char *p = strdup(str);
- char *start = p;
+ byte *p = strdup(str);
+ byte *start = p;
for (; *p; ++p) *p = tolower(*p);
return start;
}
diff --git a/src/defaults.h b/src/defaults.h
@@ -7,24 +7,29 @@
#include <stdbool.h>
#include <string.h>
#include <ctype.h>
+#include <limits.h>
#include <math.h>
#include "error.h"
+#define VERSION "0.1.0"
+
#define len(array) (sizeof(array) / sizeof((array)[0]))
+#define UNUSED(var) (void)(var)
+
#ifdef INFINITY
#define INF INFINITY
#else
#define INF (1.0 / 0.0)
#endif
-typedef uint8_t u8 ;
+typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
-typedef int8_t s8 ;
+typedef int8_t s8;
typedef int16_t s16;
typedef int32_t s32;
typedef int64_t s64;
@@ -32,15 +37,25 @@ typedef int64_t s64;
typedef size_t usize;
typedef ptrdiff_t ssize;
+#if (CHAR_BIT == 8)
+ #if (CHAR_MIN < 0)
+ typedef unsigned char byte;
+ #else
+ typedef char byte;
+ #endif
+#else
+ typedef u8 byte;
+#endif
+
typedef float f32;
typedef double f64;
typedef long double fsize;
ssize ipow(ssize, usize);
-char *remove_all_char(const char *, char);
-char *trim(const char *);
-char *downcase(const char *);
+byte *remove_all_bytes(const byte *, byte);
+byte *trim(const byte *);
+byte *downcase(const byte *);
#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)
diff --git a/src/displays.c b/src/displays.c
@@ -60,6 +60,15 @@ char *display_parsetree(const ParseNode *tree)
case NUMBER_NODE: {
return display_numbernode(tree->node.number);
}
+ case STRING_NODE: { // TODO: Escape the string.
+ usize l = strlen(tree->node.str.value);
+ byte *str = malloc(l + 2);
+ str[0] = '"';
+ strcpy(str + 1, tree->node.str.value);
+ str[l + 1] = '"';
+ str[l + 2] = '\0';
+ return str;
+ }
case UNARY_NODE: {
UnaryNode unary = tree->node.unary;
char *operand_str = display_parsetree(unary.operand);
@@ -120,7 +129,7 @@ char *display_datavalue(const DataValue *data)
}
default:
string = malloc(sizeof(char) * 128); // Safe bet.
- sprintf(string, "<%s at 0x%p>",
+ sprintf(string, "<%s at %p>",
display_datatype(data->type),
data->value);
}
diff --git a/src/execute.c b/src/execute.c
@@ -8,6 +8,7 @@
#include <math.h>
static const f32 LOCALS_REALLOC_GROWTH_FACTOR = 1.5;
+static const fsize ZERO = 0.0;
#define NUMERICAL_BINARY_OPERATION(OPERATION) do { \
NumberNode *l_num = type_check(op, LHS, T_NUMBER, lhs); \
@@ -24,11 +25,28 @@ void free_datavalue(DataValue *data)
free(data);
}
+static DataValue *recursive_execute(Context *ctx, const ParseNode *stmt);
+
/// 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)
{
+ // Recurse dowm parse tree, execute each node, bottom up.
+ DataValue *data = recursive_execute(ctx, stmt);
+
+ // When line/statement is finished evaluating, bind `Ans'.
+ if (data != NULL && ERROR_TYPE == NO_ERROR) {
+ bind_local(ctx, "Ans", data->type, data->value);
+ bind_local(ctx, "ans", data->type, data->value);
+ bind_local(ctx, "_", data->type, data->value);
+ }
+
+ return data;
+}
+
+static DataValue *recursive_execute(Context *ctx, const ParseNode *stmt)
+{
DataValue *data = malloc(sizeof(DataValue));
switch (stmt->type) {
case IDENT_NODE: {
@@ -66,9 +84,15 @@ finished_search:
memcpy(data->value, &stmt->node.number, sizeof(NumberNode));
break;
}
+ case STRING_NODE: {
+ data->type = T_STRING;
+ data->value = malloc(stmt->node.str.len + 1);
+ memcpy(data->value, stmt->node.str.value, stmt->node.str.len + 1);
+ break;
+ }
case UNARY_NODE: { // Functions, essentially.
- DataValue *callee = execute(ctx, stmt->node.unary.callee);
- DataValue *operand = execute(ctx, stmt->node.unary.operand);
+ DataValue *callee = recursive_execute(ctx, stmt->node.unary.callee);
+ DataValue *operand = recursive_execute(ctx, stmt->node.unary.operand);
if (callee == NULL || operand == NULL)
return NULL;
@@ -112,16 +136,16 @@ finished_search:
}
char *lvalue = stmt->node.binary.left->node.ident.value;
free(data);
- data = execute(ctx, stmt->node.binary.right);
+ data = recursive_execute(ctx, stmt->node.binary.right);
bind_local(ctx, lvalue, data->type, data->value);
break;
}
// How to evaluate specific operators.
- DataValue *lhs = execute(ctx, stmt->node.binary.left);
+ DataValue *lhs = recursive_execute(ctx, stmt->node.binary.left);
if (lhs == NULL)
return NULL;
- DataValue *rhs = execute(ctx, stmt->node.binary.right);
+ DataValue *rhs = recursive_execute(ctx, stmt->node.binary.right);
if (rhs == NULL)
return NULL;
@@ -151,6 +175,7 @@ finished_search:
return NULL;
}
}
+
return data;
}
@@ -263,7 +288,10 @@ Context *init_context()
// name of the function/scope.
Local *scope_name = make_local(
"__this_scope", T_STRING, (void *)ctx->function);
+ Local *ans = make_local(
+ "Ans", T_NUMBER, (void *)make_number(FLOAT, (fsize *)&ZERO));
ctx->locals[0] = *scope_name;
+ ctx->locals[0] = *ans;
// ^ Sets the first variable, default in every scope
// (good for debuggin purposes).
diff --git a/src/gui.c b/src/gui.c
@@ -0,0 +1,314 @@
+#include "gui.h"
+
+#ifdef GUI
+
+void grid_fit(GtkWidget *grid)
+{
+ g_object_set(G_OBJECT(grid),
+ "column-homogeneous", TRUE,
+ "row-homogeneous", TRUE,
+ "column-spacing", 10,
+ "row-spacing", 10,
+ NULL);
+}
+
+typedef struct {
+ GtkWidget *layout;
+ GtkWidget *display;
+ GtkWidget *output;
+ Context *exe_ctx;
+} Calculator;
+
+typedef enum {
+ // Num keys
+ ACT_NUMBER_0 = 0,
+ ACT_NUMBER_1,
+ ACT_NUMBER_2,
+ ACT_NUMBER_3,
+ ACT_NUMBER_4,
+ ACT_NUMBER_5,
+ ACT_NUMBER_6,
+ ACT_NUMBER_7,
+ ACT_NUMBER_8,
+ ACT_NUMBER_9,
+ // Operators
+ ACT_MUL, ACT_ADD,
+ ACT_DIV, ACT_SUB,
+ // Actions
+ ACT_M_PLUS, ACT_M_MINUS,
+ ACT_EXEC,
+ ACT_DEL,
+
+ ACTIONS_COUNT //< Always last!
+} Action;
+
+typedef struct {
+ Action action;
+ GtkWidget *button;
+ Calculator *calculator;
+} ButtonPress;
+
+static void write_to_editable(GtkEditable *editable, byte *input, gint len, gint *pos)
+{
+ // Wipe selection if inserting text.
+ gtk_editable_delete_selection(editable);
+ // Insert the string.
+ gtk_editable_insert_text(editable, input, len, pos);
+ // Move cursor forwards.
+ *pos += 1;
+ gtk_editable_set_position(editable, *pos);
+}
+
+static void button_press(GtkWidget *widget, gpointer data)
+{
+ UNUSED(widget);
+
+ ButtonPress *press = (ButtonPress *)data;
+ Action action = press->action;
+ Calculator *ctx = press->calculator;
+
+ printf("Action Number: %d.\n", action);
+
+ GtkEntry *entry = GTK_ENTRY(ctx->display);
+ GtkEntryBuffer *entry_buf = gtk_entry_get_buffer(entry);
+ UNUSED(entry_buf);
+
+ gint curs_pos = gtk_editable_get_position(GTK_EDITABLE(ctx->display));
+
+ if (action <= 9 && action >= 0) {
+ byte digit = '0' + action;
+ write_to_editable(GTK_EDITABLE(ctx->display), &digit, 1, &curs_pos);
+ return;
+ }
+
+ switch (action) {
+ case ACT_EXEC: {
+ const byte *source = gtk_entry_get_text(entry);
+ printf("Executing input: %s\n", source);
+
+ ParseNode *tree = NULL;
+ DataValue *result = NULL;
+
+ tree = parse(source);
+ if (tree == NULL || ERROR_TYPE != NO_ERROR) {
+ handle_error();
+ return;
+ }
+ result = execute(ctx->exe_ctx, tree);
+ if (result == NULL || ERROR_TYPE != NO_ERROR) {
+ handle_error();
+ return;
+ }
+
+ if (result != NULL)
+ gtk_entry_set_text(GTK_ENTRY(ctx->output), display_datavalue(result));
+ if (tree != NULL)
+ free_parsenode(tree);
+ } break;
+ case ACT_DEL: {
+ gboolean has_selection = gtk_editable_get_selection_bounds(
+ GTK_EDITABLE(ctx->display), NULL, NULL);
+
+ if (has_selection) {
+ gtk_editable_delete_selection(GTK_EDITABLE(ctx->display));
+ } else {
+ printf("Delete char at %d -- %d.\n", curs_pos - 1, curs_pos);
+ gtk_editable_delete_text(GTK_EDITABLE(ctx->display),
+ curs_pos - 1, curs_pos);
+ }
+ } break;
+ default:
+ printf("Unhandled action: %d.\n", action);
+ }
+}
+
+static Calculator ctx
+ = { .layout = NULL
+ , .display = NULL
+ , .output = NULL
+ , .exe_ctx = NULL
+ };
+
+static ButtonPress keys[ACTIONS_COUNT] = { 0 };
+
+static void on_activate(GtkApplication *app)
+{
+ GtkWidget *window = gtk_application_window_new(app);
+
+ GtkWidget *header = gtk_header_bar_new();
+ gtk_header_bar_set_title((GtkHeaderBar *)header, "CREPL — Calculator");
+ gtk_header_bar_set_show_close_button((GtkHeaderBar *)header, TRUE);
+
+ // Init calculator keys.
+ for (Action act = ACT_NUMBER_0; act < ACTIONS_COUNT; ++act) {
+ keys[act].action = act;
+ keys[act].calculator = &ctx;
+ }
+ // Init execution context.
+ ctx.exe_ctx = base_context();
+
+ // Calculator main layout.
+ GtkWidget *layout = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
+ gtk_widget_set_margin_top (layout, 20);
+ gtk_widget_set_margin_start (layout, 20);
+ gtk_widget_set_margin_bottom(layout, 20);
+ gtk_widget_set_margin_end (layout, 20);
+ ctx.layout = layout;
+
+ // Calculator input display.
+ GtkWidget *input_display = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(input_display), "crepl");
+ g_signal_connect(input_display, "activate",
+ G_CALLBACK(button_press), (gpointer)(&keys[ACT_EXEC]));
+ ctx.display = input_display;
+
+ // Output/result display.
+ GtkWidget *output_display = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(output_display), "...");
+ gtk_editable_set_editable(GTK_EDITABLE(output_display), FALSE);
+ gtk_entry_set_alignment(GTK_ENTRY(output_display), 1);
+ gtk_entry_set_has_frame(GTK_ENTRY(output_display), FALSE);
+ gtk_widget_set_can_focus(output_display, FALSE);
+ ctx.output = output_display;
+
+ gtk_widget_set_margin_top(output_display, 0);
+ gtk_box_pack_start(GTK_BOX(layout), input_display, TRUE, TRUE, 5);
+ gtk_box_pack_start(GTK_BOX(layout), output_display, TRUE, TRUE, 2);
+
+ // Calculator mode keys.
+ GtkWidget *settings_grid = gtk_grid_new();
+ grid_fit(settings_grid);
+
+ GtkWidget *M_plus = gtk_button_new_with_label("M+"); // ACT_M_PLUS.
+ GtkWidget *M_minus = gtk_button_new_with_label("M-"); // ACT_M_MINUS.
+ GtkWidget *execute = gtk_button_new_with_label("EXE"); // ACT_EXEC.
+ GtkWidget *delete = gtk_button_new_with_label("DEL"); // ACT_DEL.
+
+ keys[ACT_EXEC].button = execute;
+ g_signal_connect(execute, "clicked",
+ G_CALLBACK(button_press), (gpointer)(&keys[ACT_EXEC]));
+ keys[ACT_DEL].button = delete;
+ g_signal_connect(delete, "clicked",
+ G_CALLBACK(button_press), (gpointer)(&keys[ACT_DEL]));
+
+ gtk_grid_attach(GTK_GRID(settings_grid), M_plus, 1, 1, 1, 1);
+ gtk_grid_attach(GTK_GRID(settings_grid), M_minus, 2, 1, 1, 1);
+ gtk_grid_attach(GTK_GRID(settings_grid), execute, 3, 1, 1, 1);
+ gtk_grid_attach(GTK_GRID(settings_grid), delete, 4, 1, 1, 1);
+
+ gtk_box_pack_start(GTK_BOX(layout), settings_grid, TRUE, TRUE, 10);
+
+ /* Main Keys */
+ GtkWidget *main_keys = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 15);
+
+ // Calculator numpad.
+ GtkWidget *numbers = gtk_grid_new();
+ grid_fit(numbers);
+
+ for (u8 n = 0; n < 9; ++n) {
+ byte digit[2] = { '1' + n, '\0' };
+ GtkWidget *button = gtk_button_new_with_label(digit);
+
+ keys[n + 1].button = button;
+ g_signal_connect(button, "clicked",
+ G_CALLBACK(button_press), (gpointer)(&keys[n + 1]));
+
+ gtk_grid_attach(GTK_GRID(numbers), button,
+ n % 3 + 1, n / 3 + 1, 1, 1);
+ }
+ GtkWidget *number_zero = gtk_button_new_with_label("0");
+ keys[ACT_NUMBER_0].button = number_zero;
+ g_signal_connect(number_zero, "clicked",
+ G_CALLBACK(button_press), (gpointer)(&keys[ACT_NUMBER_0]));
+ gtk_grid_attach(GTK_GRID(numbers), number_zero, 2, 4, 1, 1);
+
+ gtk_box_pack_start(GTK_BOX(main_keys), numbers, TRUE, TRUE, 10);
+
+ // Calculator function keys.
+ GtkWidget *operators_grid = gtk_grid_new();
+ grid_fit(operators_grid);
+ GtkWidget *functions_grid = gtk_grid_new();
+ grid_fit(functions_grid);
+
+ // Operators, column #1.
+ GtkWidget *b_mul = gtk_button_new_with_label("*");
+ GtkWidget *b_add = gtk_button_new_with_label("+");
+ GtkWidget *b_div = gtk_button_new_with_label("/");
+ GtkWidget *b_sub = gtk_button_new_with_label("-");
+
+ // Operators column #2.
+ GtkWidget *b_pow = gtk_button_new_with_label("^");
+ GtkWidget *b_fac = gtk_button_new_with_label("!");
+ GtkWidget *b_sqr = gtk_button_new_with_label("√");
+ GtkWidget *b_equ = gtk_button_new_with_label("=");
+
+ // Functions, column #3.
+ GtkWidget *b_log = gtk_button_new_with_label("log");
+ GtkWidget *b_sin = gtk_button_new_with_label("sin");
+ GtkWidget *b_cos = gtk_button_new_with_label("cos");
+ GtkWidget *b_abs = gtk_button_new_with_label("abs");
+
+ // Constants, column #4.
+ GtkWidget *b_pi = gtk_button_new_with_label("π");
+ GtkWidget *b_e = gtk_button_new_with_label("e");
+ GtkWidget *b_phi = gtk_button_new_with_label("φ");
+ GtkWidget *b_ans = gtk_button_new_with_label("Ans");
+
+ // Column #1.
+ gtk_grid_attach(GTK_GRID(operators_grid), b_mul, 1, 1, 1, 1);
+ gtk_grid_attach(GTK_GRID(operators_grid), b_add, 1, 2, 1, 1);
+ gtk_grid_attach(GTK_GRID(operators_grid), b_div, 1, 3, 1, 1);
+ gtk_grid_attach(GTK_GRID(operators_grid), b_sub, 1, 4, 1, 1);
+ // Column #2.
+ gtk_grid_attach(GTK_GRID(operators_grid), b_pow, 2, 1, 1, 1);
+ gtk_grid_attach(GTK_GRID(operators_grid), b_fac, 2, 2, 1, 1);
+ gtk_grid_attach(GTK_GRID(operators_grid), b_sqr, 2, 3, 1, 1);
+ gtk_grid_attach(GTK_GRID(operators_grid), b_equ, 2, 4, 1, 1);
+ // Column #3.
+ gtk_grid_attach(GTK_GRID(functions_grid), b_log, 1, 1, 1, 1);
+ gtk_grid_attach(GTK_GRID(functions_grid), b_sin, 1, 2, 1, 1);
+ gtk_grid_attach(GTK_GRID(functions_grid), b_cos, 1, 3, 1, 1);
+ gtk_grid_attach(GTK_GRID(functions_grid), b_abs, 1, 4, 1, 1);
+ // Column #4.
+ gtk_grid_attach(GTK_GRID(functions_grid), b_pi, 2, 1, 1, 1);
+ gtk_grid_attach(GTK_GRID(functions_grid), b_e, 2, 2, 1, 1);
+ gtk_grid_attach(GTK_GRID(functions_grid), b_phi, 2, 3, 1, 1);
+ gtk_grid_attach(GTK_GRID(functions_grid), b_ans, 2, 4, 1, 1);
+
+ gtk_box_pack_end(GTK_BOX(main_keys), functions_grid, TRUE, TRUE, 10);
+ gtk_box_pack_end(GTK_BOX(main_keys), operators_grid, TRUE, TRUE, 10);
+
+ // Set all keys/buttons to be unfocusable.
+ for (Action act = ACT_NUMBER_0; act < ACTIONS_COUNT; ++act)
+ if (keys[act].button != NULL)
+ gtk_widget_set_can_focus(keys[act].button, FALSE);
+
+ gtk_window_set_titlebar(GTK_WINDOW(window), header);
+ gtk_box_pack_end(GTK_BOX(layout), main_keys, TRUE, TRUE, 10);
+ gtk_container_add(GTK_CONTAINER(window), layout);
+
+ gtk_widget_show_all(window);
+}
+
+int start_gui(void)
+{
+ printf("Start GUI.\n");
+
+ GtkApplication *app = gtk_application_new("com.crepl.GtkApplication",
+ G_APPLICATION_FLAGS_NONE);
+
+ g_signal_connect(app, "activate", G_CALLBACK(on_activate), NULL);
+ printf("Started application.\n");
+ return g_application_run(G_APPLICATION(app), 0, NULL);
+}
+
+#else
+
+#include <stdio.h>
+int start_gui(void)
+{
+ fputs("Not compiled with GUI support.", stderr);
+ return -1;
+}
+
+#endif
diff --git a/src/gui.h b/src/gui.h
@@ -0,0 +1,13 @@
+#ifdef GUI
+#include "defaults.h"
+#include "error.h"
+#include "parse.h"
+#include "execute.h"
+#include "displays.h"
+
+#include <gtk/gtk.h>
+#endif
+
+int start_gui(void);
+
+
diff --git a/src/main.c b/src/main.c
@@ -15,6 +15,7 @@
#include "parse.h"
#include "execute.h"
#include "displays.h"
+#include "gui.h"
static const char *PROMPT = "::> ";
@@ -81,15 +82,38 @@ int main(int argc, char **argv)
printf("\033[1m");
printf("CREPL — Calculator Read Eval Print Loop");
printf("\033[0m");
- puts(" (" COMPILER ") (" __DATE__ ")");
- puts("Type \"exit\" or [Ctrl-D] (i.e. EOF) to quit.");
+ puts(" (v" VERSION ") (" COMPILER ") (" __DATE__ ")");
bool verbose = false;
+ bool gui_mode = false;
// Parse command line arguments.
for (int i = 0; i < argc; ++i) {
- if (strcmp(argv[i], "-v") == 0)
+ if (strcmp(argv[i], "-v") == 0
+ || strcmp(argv[i], "--verbose") == 0)
verbose = true;
+ else if (strcmp(argv[i], "-V") == 0
+ || strcmp(argv[i], "--version") == 0)
+ { puts("Version " VERSION "."); return EXIT_SUCCESS; }
+ else if (strcmp(argv[i], "-g") == 0
+ || strcmp(argv[i], "--gui") == 0)
+ gui_mode = true;
+ }
+
+#ifndef GUI
+ if (gui_mode) {
+ puts("This CREPL executable was not compiled with GUI support.");
+ return EXIT_FAILURE;
}
+#else
+ if (gui_mode) {
+ int success = start_gui();
+ if (success == 0)
+ return EXIT_SUCCESS;
+ return EXIT_FAILURE;
+ }
+#endif
+
+ puts("Type \"exit\" or [Ctrl-D] (i.e. EOF) to quit.");
// Configure readline.
rl_clear_signals();
diff --git a/src/parse.c b/src/parse.c
@@ -19,6 +19,9 @@ void free_parsenode(ParseNode *node)
case IDENT_NODE:
free((char *)node->node.ident.value);
break;
+ case STRING_NODE:
+ free((char *)node->node.str.value);
+ break;
case NUMBER_NODE:
break;
case UNARY_NODE:
@@ -72,6 +75,8 @@ TokenType char_token_type(char c, char last_char, TokenType last_token_type)
return TT_RPAREN;
if (c == '\0' || c == ' ' || c == '\t' || c == '\n' || c == '\r')
return TT_NONE;
+ if (c == '"')
+ return TT_STRING;
// All possible operator/special-symbol characters:
if (((c >= '!' && c <= '/')
@@ -138,6 +143,14 @@ Token *lex(char **source)
// Do not coalesce parentheses.
if (tt == TT_RPAREN || tt == TT_LPAREN) {
span++;
+ } else if (tt == TT_STRING) { // String literals are not like others.
+ ++span; // Skip opening quote.
+ while ((*source)[span] != '"') {
+ if ((*source)[span] == '\\' && (*source)[span + 1] == '"')
+ ++span; // Don't stop at escaped quote.
+ ++span;
+ }
+ ++span; // Skip ending quote.
} else {
while (tt == previous_tt) {
span++;
@@ -154,6 +167,8 @@ Token *lex(char **source)
*source += span;
Token *token = new_token(tt, sub_str);
+
+ free(sub_str);
return token;
}
@@ -204,7 +219,7 @@ NumberNode *parse_number(const char *str)
{
NumberNode *number = malloc(sizeof(NumberNode));
- str = remove_all_char(str, '_');
+ str = remove_all_bytes(str, '_');
char *exponent_ptr = strstr(str, "E");
char *neg_exponent_ptr = strstr(str, "E-");
@@ -282,6 +297,13 @@ ParseNode *parse_prefix(const Token *token, char **rest)
node_into_ident(token->value, node);
break;
}
+ case TT_STRING: {
+ node->type = STRING_NODE; // TODO: Parse string escapes etc.
+ node->node.str.value = strdup(token->value + 1);
+ node->node.str.len = strlen(token->value) - 2;
+ node->node.str.value[node->node.str.len] = '\0';
+ break;
+ }
case TT_OPERATOR: {
// Verify this is a prefix operator.
bool is_prefix = false;
@@ -329,7 +351,6 @@ ParseNode *parse_prefix(const Token *token, char **rest)
ERROR_TYPE = PARSE_ERROR;
sprintf(ERROR_MSG, "Unclosed paranthetical expression.\n"
" Missing `)' closing parenthesis.");
- free_token((Token *)token);
return NULL;
}
free_token((Token *)token);
@@ -490,10 +511,9 @@ ParseNode *parse_expr(char **slice, u16 precedence)
strcpy(ERROR_MSG, "Could not finish parsing expression.");
break;
}
- if (current_precedence != FUNCTION_PRECEDENCE)
- token = lex(slice);
- else
- token = peek(slice);
+ token = current_precedence == FUNCTION_PRECEDENCE
+ ? peek(slice)
+ : lex(slice);
if (token == NULL)
break;
diff --git a/src/parse.h b/src/parse.h
@@ -8,6 +8,7 @@ typedef enum {
TT_IDENTIFIER,
TT_NUMERIC,
TT_OPERATOR,
+ TT_STRING,
TT_NONE,
} TokenType;
@@ -69,6 +70,7 @@ static const Operator KNOWN_OPERATORS[] = {
typedef enum {
IDENT_NODE,
NUMBER_NODE,
+ STRING_NODE,
UNARY_NODE,
BINARY_NODE,
} NodeType;
@@ -79,6 +81,11 @@ typedef struct {
char *value;
} IdentNode;
+typedef struct {
+ usize len;
+ byte *value;
+} StringNode;
+
typedef enum {
FLOAT,
INT,
@@ -111,6 +118,7 @@ typedef struct _parse_node {
NodeType type;
union {
IdentNode ident;
+ StringNode str;
NumberNode number;
UnaryNode unary;
BinaryNode binary;
diff --git a/src/prelude.c b/src/prelude.c
@@ -3,6 +3,7 @@
char *PRELUDE_STATEMENTS[] = {
"tau = 2pi",
"phi = 1.61803398875",
+ "crepl = \"Calculator REPL, v" VERSION ".\""
};
void execute_prelude(Context *ctx)
@@ -25,6 +26,6 @@ void execute_prelude(Context *ctx)
fatality:
handle_error();
fprintf(stderr, "\nFATAL: Prelude failed to run without error.\n");
- fprintf(stderr, "ABORTING\n!");
+ fprintf(stderr, "ABORTING!\n");
exit(1);
}