summary refs log tree commit diff
diff options
context:
space:
mode:
authorNakidai <plaza521@inbox.ru>2024-07-05 07:16:05 +0300
committerNakidai <plaza521@inbox.ru>2024-07-05 07:19:48 +0300
commitaea40f3877e36e95f2a70e3eeb0569fd156e3841 (patch)
treee6bd202ac4e5499db3b9983fc46e3b880a20f5ff
parenta9c159f5f7bf3479c3236735960597b4bc36a204 (diff)
download3cl-aea40f3877e36e95f2a70e3eeb0569fd156e3841.tar.gz
3cl-aea40f3877e36e95f2a70e3eeb0569fd156e3841.zip
Add almost all
Only continue operation and "blocks" of code are left. Now I'm gonna
sleep

And yes, I understand that code need to be refactored, and if I will not
get bored with writing this thing I will rewrite it in a better way, cuz
now code is awful
-rw-r--r--CMakeLists.txt2
-rw-r--r--include/cccl.h21
-rw-r--r--include/platform/getch.h13
-rw-r--r--include/types.h2
-rw-r--r--src/cccl.c228
-rw-r--r--src/main.c9
-rw-r--r--src/platform/getch.c53
-rw-r--r--src/utils.c2
8 files changed, 315 insertions, 15 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index aa41599..a9ddeda 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -7,8 +7,10 @@ endif()
 
 add_executable(
     "${PROJECT_NAME}"
+    src/cccl.c
     src/main.c
     src/utils.c
+    src/platform/getch.c
 )
 
 set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "")
diff --git a/include/cccl.h b/include/cccl.h
index 6652453..9e41730 100644
--- a/include/cccl.h
+++ b/include/cccl.h
@@ -5,8 +5,6 @@
 
 #include <stdbool.h>
 
-#include "cvector.h"
-
 
 typedef struct cccl_varpair
 {
@@ -28,15 +26,16 @@ typedef struct cccl_pointer
 
 typedef struct cccl
 {
-    cccl_varpair   *variables;  /* Array with variables      */
-    cccl_procpair  *procedures; /* Array with procedures     */
-    i16            *stack;      /* User stack                */
-    s8             *br_stack;   /* Stack for brackets        */
-    i32            *ep_stack;   /* Call stack                */
-    i32             ep;         /* Pointer to current symbol */
-    s8             *code;       /* Code being executed       */
-    s8             *filename;   /* File being executed       */
-    i32             size;       /* File size                 */
+    cccl_varpair    *variables;  /* Array with variables      */
+    cccl_procpair   *procedures; /* Array with procedures     */
+    i16             *stack;      /* User stack                */
+    s8              *br_stack;   /* Stack for brackets        */
+    cccl_varpair   **lv_stack;   /* Local variable stack      */
+    cccl_pointer    *ep_stack;   /* Call stack                */
+    i32              ep;         /* Pointer to current symbol */
+    s8              *code;       /* Code being executed       */
+    s8              *filename;   /* File being executed       */
+    i32              size;       /* File size                 */
 } cccl;
 
 void cccl_init(s8 *filename);
diff --git a/include/platform/getch.h b/include/platform/getch.h
new file mode 100644
index 0000000..490b536
--- /dev/null
+++ b/include/platform/getch.h
@@ -0,0 +1,13 @@
+#ifndef __GETCH_H__
+#define __GETCH_H__
+
+#ifdef _WIN32
+#include <conio.h>
+#define getch _getche
+#else
+int getch(void);
+#endif /* _WIN32 */
+
+void getch_init(void);
+
+#endif /* __GETCH_H__ */
diff --git a/include/types.h b/include/types.h
index 9c92d57..4bf1da9 100644
--- a/include/types.h
+++ b/include/types.h
@@ -9,9 +9,11 @@ typedef char     s8;
 typedef uint8_t  u8;
 typedef uint16_t u16;
 typedef uint32_t u32;
+typedef uint64_t i64;
 
 typedef int8_t   i8;
 typedef int16_t  i16;
 typedef int32_t  i32;
+typedef uint64_t i64;
 
 #endif /* __TYPES_H__ */
diff --git a/src/cccl.c b/src/cccl.c
index 1803fcf..4734620 100644
--- a/src/cccl.c
+++ b/src/cccl.c
@@ -4,22 +4,34 @@
 #include <errno.h>
 #include <stdbool.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 
 #include "cvector.h"
+#include "platform/getch.h"
 #include "utils.h"
 
 
 static cccl pc = {0};
+static const s8 *const name_allowed  = "abcdefghijklmopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+static const s8 *const name_allowed_ = "abcdefghijklmopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_";
+static const s8 *const all_allowed   = "abcdefghijklmopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_{}[]()?;@=!&$%<>^+-*~#:/ \n\t";
+static const s8 *const skippable     = " \t\n";
+
+static void cccl_varpair_free(void *p)
+{
+    cvector_free(*(cccl_varpair **)p);
+}
 
 void cccl_init(s8 *filename)
 {
     memset(&pc, 0, sizeof(pc));
 
-    cvector_init(pc.stack, 1, NULL);
-    cvector_init(pc.variables, 1, NULL);
+    cvector_init(pc.stack,      1, NULL);
+    cvector_init(pc.variables,  1, NULL);
     cvector_init(pc.procedures, 1, NULL);
-    cvector_init(pc.ep_stack, 1, NULL);
+    cvector_init(pc.lv_stack,   1, cccl_varpair_free);
+    cvector_init(pc.ep_stack,   1, NULL);
     if (errno) die(1, strerror(errno));
 
     pc.filename = filename;
@@ -30,6 +42,7 @@ void cccl_free()
     cvector_free(pc.stack);
     cvector_free(pc.variables);
     cvector_free(pc.procedures);
+    cvector_free(pc.lv_stack);
     cvector_free(pc.ep_stack);
     free(pc.code);
 }
@@ -63,18 +76,227 @@ void cccl_read(void)
     fclose(file);
 }
 
+static void check_symbol(void)
+{
+    if (!strchr(all_allowed, pc.code[pc.ep]))
+        die(1, "illegal character (%u at %d)", pc.code[pc.ep], pc.ep);
+}
+
+static void require_stack_size(u32 size, i32 start)
+{
+    if (cvector_size(pc.stack) < size)
+        die(1, "stack size is %d, but %d is required (%c at %d)", cvector_size(pc.stack), size, pc.code[start], start);
+}
+
+static i32 require_variable_local(s8 name)
+{
+    if (cvector_size(pc.lv_stack) > 0)
+        for (i32 i = 0; i < cvector_size(pc.lv_stack[cvector_size(pc.lv_stack) - 1]); ++i)
+            if (pc.lv_stack[cvector_size(pc.lv_stack) - 1][i].name == name)
+                return i;
+    return -1;
+}
+
+static i32 require_variable(s8 name)
+{
+    for (i32 i = 0; i < cvector_size(pc.variables); ++i)
+        if (pc.variables[i].name == name)
+            return i;
+    return -1;
+}
+
+static i32 require_procedure(s8 name)
+{
+    for (i32 i = 0; i < cvector_size(pc.procedures); ++i)
+        if (pc.procedures[i].name == name)
+            return i;
+    return -1;
+}
+
+static s8 read_operand(bool allow_underscore)
+{
+    i32 start = pc.ep;
+    for (++pc.ep; pc.ep < pc.size; ++pc.ep)
+    {
+        check_symbol();
+        if (strchr(skippable, pc.code[pc.ep])) continue;
+        if (!strchr(allow_underscore ? name_allowed_ : name_allowed, pc.code[pc.ep]))
+            die(1, "invalid operand name (%c at %d for %c at %d)", pc.code[pc.ep], pc.ep, pc.code[start], start);
+        return pc.code[pc.ep];
+    }
+    die(1, "no operand (for %c at %d)", pc.code[start], start);
+}
+
 void cccl_run(void)
 {
+    i32 variable, start, var;
+    cccl_varpair varpair;
+    cccl_pointer pointer;
+    s8 operand;
     bool is_comment;
     for (pc.ep = 0; pc.ep < pc.size; ++pc.ep)
     {
         if (is_comment && pc.code[pc.ep] == '\n')
             is_comment = false;
+        else if (is_comment)
+            continue;
+        check_symbol();
+        start = pc.ep;
         switch (pc.code[pc.ep])
         {
+        case ' ':
+        case '\t':
+            break;
         case '/':
             is_comment = true;
             break;
+        case '^':
+            cvector_push_back(pc.stack, 0);
+            break;
+        case '+':
+            require_stack_size(1, pc.ep);
+            ++pc.stack[cvector_size(pc.stack) - 1];
+            break;
+        case '-':
+            require_stack_size(1, pc.ep);
+            --pc.stack[cvector_size(pc.stack) - 1];
+            break;
+        case '*':
+            require_stack_size(2, pc.ep);
+            pc.stack[cvector_size(pc.stack) - 2] += pc.stack[cvector_size(pc.stack) - 1];
+            cvector_pop_back(pc.stack);
+            break;
+        case '~':
+            require_stack_size(2, pc.ep);
+            pc.stack[cvector_size(pc.stack) - 2] -= pc.stack[cvector_size(pc.stack) - 1];
+            cvector_pop_back(pc.stack);
+            break;
+        case '%':
+            operand = read_operand(true);
+            i16 *to_reverse = NULL;
+            i32 size;
+            if (operand == '_')
+            {
+                size = cvector_size(pc.stack);
+            }
+            else
+            {
+                if ((var = require_variable_local(operand)) == -1)
+                {
+                    if ((var = require_variable(operand)) == -1)
+                        die(1, "no %c variable found (for %% at %d)", operand, start);
+                    else
+                        size = pc.lv_stack[cvector_size(pc.lv_stack) - 1][var].value;
+                } else
+                {
+                    size = pc.variables[var].value;
+                }
+            }
+            if (size < 1)
+                die(1, "invalid argument %d, should be not less than 1 (for %% at %d)", size, start);
+            require_stack_size(size, start);
+            cvector_init(to_reverse, size, NULL);
+            for (int i = 0; i < size; ++i)
+            {
+                cvector_push_back(to_reverse, pc.stack[cvector_size(pc.stack) - 1]);
+                cvector_pop_back(pc.stack);
+            }
+            for (int i = 0; i < size; ++i)
+                cvector_push_back(pc.stack, to_reverse[i]);
+            cvector_free(to_reverse);
+            break;
+        case '=':
+            operand = read_operand(true);
+            require_stack_size(1, start);
+            varpair = (cccl_varpair){.name=operand, .value=pc.stack[cvector_size(pc.stack) - 1]};
+            if (operand != '_')
+            {
+                if ((var = require_variable_local(operand)) == -1)
+                    if ((var = require_variable(operand)) == -1)
+                        cvector_push_back(pc.variables, varpair);
+                    else
+                        pc.variables[var] = varpair;
+                else
+                    pc.lv_stack[cvector_size(pc.lv_stack) - 1][var] = varpair;
+            }
+            cvector_pop_back(pc.stack);
+            break;
+        case '!':
+            operand = read_operand(false);
+            if ((var = require_variable_local(operand)) == -1)
+                if ((var = require_variable(operand)) == -1)
+                    die(1, "no %c variable found (for ! at %d)", operand, start);
+                else
+                    cvector_erase(pc.variables, var);
+            else
+                cvector_erase(pc.lv_stack[cvector_size(pc.lv_stack) - 1], var);
+            break;
+        case '$':
+            operand = read_operand(false);
+            i16 value;
+            if ((var = require_variable_local(operand)) == -1)
+                if ((var = require_variable(operand)) == -1)
+                    die(1, "no %c variable found (for ! at %d)", operand, start);
+                else
+                    value = pc.variables[var].value;
+            else
+                value = pc.lv_stack[cvector_size(pc.lv_stack) - 1][var].value;
+            cvector_push_back(pc.stack, value);
+            break;
+        case '&':
+            operand = read_operand(false);
+            if (cvector_size(pc.lv_stack) == 0)
+                die(1, "local variables cannot be created in global scope (& at %d)", start);
+            varpair = (cccl_varpair){.name=operand, .value=0};
+            if (require_variable_local(operand) == -1)
+                cvector_push_back(pc.lv_stack[cvector_size(pc.lv_stack) - 1], varpair);
+            break;
+        case '<':
+            operand = read_operand(false);
+            if ((var = require_variable_local(operand)) == -1)
+                if ((var = require_variable(operand)) == -1)
+                    die(1, "no %c variable found (for < at %d)", operand, start);
+                else
+                    printf("%c", pc.variables[var].value);
+            else
+                printf("%c", pc.lv_stack[cvector_size(pc.lv_stack) - 1][var].value);
+            break;
+        case '>':
+            operand = read_operand(false);
+            i16 *to_store;
+            if ((var = require_variable_local(operand)) == -1)
+                if ((var = require_variable(operand)) == -1)
+                    die(1, "no %c variable found (for > at %d)", operand, start);
+                else
+                    to_store = &pc.variables[var].value;
+            else
+                to_store = &pc.lv_stack[cvector_size(pc.lv_stack) - 1][var].value;
+            *to_store = getch();
+            break;
+        case '@':
+            operand = read_operand(false);
+            if ((var = require_procedure(operand)) == -1)
+                die(1, "no %c procedure found (for @ at %d)", operand, start);
+            pointer = (cccl_pointer){.value=pc.ep, .is_loop=false};
+            cvector_push_back(pc.ep_stack, pointer);
+            pc.ep = pc.procedures[var].value;
+            break;
+        case '#':
+            if (cvector_size(pc.lv_stack) == 0)
+            {
+                exit(0);
+            }
+            pc.ep = pc.ep_stack[cvector_size(pc.ep_stack) - 1].value;
+            if (pc.ep_stack[cvector_size(pc.ep_stack) - 1].is_loop)
+                cvector_pop_back(pc.ep_stack);
+            cvector_pop_back(pc.ep_stack);
+            break;
         }
     }
+    printf("Stack: ^^^\n");
+    for (int i = 0; i < cvector_size(pc.stack); ++i)
+        printf("%d\n", pc.stack[i]);
+    printf("Variables:\n");
+    for (int i = 0; i < cvector_size(pc.variables); ++i)
+        printf("%c:%d\n", pc.variables[i].name, pc.variables[i].value);
 }
diff --git a/src/main.c b/src/main.c
index 36cdf5b..0d69855 100644
--- a/src/main.c
+++ b/src/main.c
@@ -7,6 +7,9 @@
 #include <stdlib.h>
 #include <stdnoreturn.h>
 
+#include "cccl.h"
+#include "platform/getch.h"
+
 
 const s8 *program_name;
 
@@ -35,6 +38,7 @@ noreturn void usage(bool full)
 int main(i32 argc, s8 **argv)
 {
     program_name = argv[0];
+    getch_init();
 
     i32 ch;
     while ((ch = getopt_long(argc, argv, "h", long_options, NULL)) != EOF)
@@ -52,5 +56,10 @@ int main(i32 argc, s8 **argv)
     if (argv[optind] == NULL)
         usage(false);
 
+    cccl_init(argv[optind]);
+        cccl_read();
+        cccl_run();
+    cccl_free();
+
     return 0;
 }
diff --git a/src/platform/getch.c b/src/platform/getch.c
new file mode 100644
index 0000000..48c846e
--- /dev/null
+++ b/src/platform/getch.c
@@ -0,0 +1,53 @@
+#ifdef _WIN32
+void getch_init(void) {}
+#else
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdnoreturn.h>
+#include <termios.h>
+#include <unistd.h>
+
+static struct termios default_state = {0};
+
+int getch(void)
+{
+    int ch = 0;
+    struct termios old = {0};
+    fflush(stdout);
+
+    tcgetattr(0, &old);
+    old.c_lflag &= ~ICANON;
+
+    tcsetattr(0, TCSANOW, &old);
+    ch = getchar();
+    tcsetattr(0, TCSADRAIN, &default_state);
+
+    return ch;
+}
+
+static void get_reset_terminal_state(void)
+{
+    tcsetattr(0, TCSANOW, &default_state);
+}
+
+static noreturn void get_reset_terminal_state_handler(int sig)
+{
+    get_reset_terminal_state();
+    _Exit(sig);
+}
+
+void getch_init(void)
+{
+    tcgetattr(0, &default_state);
+
+    atexit(get_reset_terminal_state);
+
+    signal(SIGINT,  get_reset_terminal_state_handler);
+    signal(SIGABRT, get_reset_terminal_state_handler);
+    signal(SIGFPE,  get_reset_terminal_state_handler);
+    signal(SIGILL,  get_reset_terminal_state_handler);
+    signal(SIGSEGV, get_reset_terminal_state_handler);
+    signal(SIGTERM, get_reset_terminal_state_handler);
+}
+#endif /* _WIN32 */
diff --git a/src/utils.c b/src/utils.c
index ee37110..978396c 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -14,10 +14,10 @@ noreturn void die(i32 code, s8 *fmt, ...)
     va_list args;
 
     fprintf(stderr, "%s: ", program_name);
-
     va_start(args, fmt);
     vfprintf(stderr, fmt, args);
     va_end(args);
+    putc('\n', stderr);
 
     exit(code);
 }