summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt5
-rw-r--r--include/3cl.h2
-rw-r--r--include/instruction.h13
-rw-r--r--include/instructions.h21
-rw-r--r--include/main.h2
-rw-r--r--include/readchar.h44
-rw-r--r--include/readfile.h5
-rw-r--r--include/stack.h12
-rw-r--r--include/utils.h8
-rw-r--r--include/variable.h24
-rw-r--r--src/3cl.c8
-rw-r--r--src/instruction.c28
-rw-r--r--src/instruction/add.c14
-rw-r--r--src/instruction/decrement.c5
-rw-r--r--src/instruction/increment.c5
-rw-r--r--src/instruction/nop.c1
-rw-r--r--src/instruction/pushzero.c1
-rw-r--r--src/instruction/reverse.c51
-rw-r--r--src/instruction/subtract.c12
-rw-r--r--src/readchar.c59
-rw-r--r--src/variable.c32
21 files changed, 335 insertions, 17 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 45742b9..f3d53ea 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -13,11 +13,16 @@ add_executable(
     src/instruction/pushzero.c
     src/instruction/increment.c
     src/instruction/decrement.c
+    src/instruction/add.c
+    src/instruction/subtract.c
+    src/instruction/reverse.c
     src/main.c
     src/platform/getch.c
+    src/readchar.c
     src/readfile.c
     src/stack.c
     src/utils.c
+    src/variable.c
 )
 
 set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "")
diff --git a/include/3cl.h b/include/3cl.h
index a6b9065..4466490 100644
--- a/include/3cl.h
+++ b/include/3cl.h
@@ -1,6 +1,7 @@
 #ifndef __3CL_H__
 #define __3CL_H__
 
+#include <stdbool.h>
 #include <stddef.h>
 #include <stdint.h>
 
@@ -85,6 +86,7 @@ struct CCL
     struct CCLStack   stack;
     struct CCLFrame   rootframe;
     const char       *code;
+    bool              stopped;
     int             (*in)();
     void            (*out)(int);
 };
diff --git a/include/instruction.h b/include/instruction.h
index 449c760..63f28b2 100644
--- a/include/instruction.h
+++ b/include/instruction.h
@@ -3,14 +3,15 @@
 
 #include "3cl.h"
 
-
+/**< Type for every instruction in 3cl */
 typedef struct CCLFrame *(*CCLInstruction)(struct CCL *ccl, struct CCLFrame *frame);
 
-struct CCLFrame *ccl_instruction_nop(struct CCL *ccl, struct CCLFrame *frame);
-struct CCLFrame *ccl_instruction_pushzero(struct CCL *ccl, struct CCLFrame *frame);
-struct CCLFrame *ccl_instruction_increment(struct CCL *ccl, struct CCLFrame *frame);
-struct CCLFrame *ccl_instruction_decrement(struct CCL *ccl, struct CCLFrame *frame);
-
+/**
+ * Execute next instruction
+ * @param ccl CCL instance
+ * @param frame Current frame
+ * @return Updated frame
+ */
 struct CCLFrame *ccl_instruction(struct CCL *ccl, struct CCLFrame *frame);
 
 #endif /* __CCL_INSTRUCTION_H__ */
diff --git a/include/instructions.h b/include/instructions.h
new file mode 100644
index 0000000..adada87
--- /dev/null
+++ b/include/instructions.h
@@ -0,0 +1,21 @@
+#ifndef __CCL_INSTRUCTIONS_H__
+#define __CCL_INSTRUCTIONS_H__
+
+#include "3cl.h"
+
+
+#define INST(NAME) \
+    struct CCLFrame *ccl_instruction_##NAME(struct CCL *ccl, struct CCLFrame *frame)
+
+INST(nop);
+INST(pushzero);
+INST(increment);
+INST(decrement);
+INST(add);
+INST(subtract);
+INST(reverse);
+
+#undef INST
+#define INST(NAME) ccl_instruction_##NAME
+
+#endif /* __CCL_INSTRUCTIONS_H__ */
diff --git a/include/main.h b/include/main.h
index ad3bdc6..64abb24 100644
--- a/include/main.h
+++ b/include/main.h
@@ -1,6 +1,6 @@
 #ifndef __MAIN_H__
 #define __MAIN_H__
 
-extern const char *program_name;
+extern const char *program_name; /**< argv[0] */
 
 #endif /* __MAIN_H__ */
diff --git a/include/readchar.h b/include/readchar.h
new file mode 100644
index 0000000..e881dcb
--- /dev/null
+++ b/include/readchar.h
@@ -0,0 +1,44 @@
+#ifndef __CCL_READCHAR_H__
+#define __CCL_READCHAR_H__
+
+#include "3cl.h"
+
+
+/**
+ * Flags to tell ccl_readchar how to read next character
+ *
+ * Bits
+ * 4 Die if char is not whitelisted
+ * 3 Brackets
+ * 2 Instruction symbols
+ * 1 English alphabet
+ * 0 Underscore
+ *
+ * @see ccl_readchar
+ */
+enum CCLRCFlags
+{
+    CCL_RC_DIE   = 0b10000, /**< Die if char is not whitelisted */
+    CCL_RC_BRACK = 0b01000, /**< Brackes                        */
+    CCL_RC_IS    = 0b00100, /**< Instruction symbols            */
+    CCL_RC_ALP   = 0b00010, /**< English alphabet               */
+    CCL_RC_US    = 0b00001, /**< Underscore                     */
+    CCL_RC_VAR       = 0b11010, /**< (Used by 3CL) Variable names                      */
+    CCL_RC_CCL_VARUS = 0b11011, /**< (Used by 3CL) Variable names including underscore */
+    CCL_RC_CCL_BRACK = 0b01000, /**< (Used by 3CL) Brackets                            */
+    CCL_RC_CCL_INSTR = 0b11111, /**< (Used by 3CL) Instruction                         */
+};
+
+/**
+ * Function to read next character
+ * Skips whitespace and comments, returns '\0' if reaches EOF
+ * ccl_readchar will die on invalid symbol (e.g. cyrillic one) even if CCL_RC_DIE is set
+ * @see CCLRCFlags
+ * @param ccl CCL instance
+ * @param frame Current frame
+ * @param flags CCLRCFlags
+ * @return Next character
+ */
+char ccl_readchar(struct CCL *ccl, struct CCLFrame *frame, enum CCLRCFlags flags);
+
+#endif /* __CCL_READCHAR_H__ */
diff --git a/include/readfile.h b/include/readfile.h
index 5358fc7..dcb47eb 100644
--- a/include/readfile.h
+++ b/include/readfile.h
@@ -1,6 +1,11 @@
 #ifndef __READFILE_H__
 #define __READFILE_H__
 
+/**
+ * Read file by filename, die on error
+ * @param name Path to file
+ * @return Contents of file with additional '\0' at the end
+ */
 char *readfile(const char *name);
 
 #endif /* __READFILE_H__ */
diff --git a/include/stack.h b/include/stack.h
index 27cc2f0..ac17ed3 100644
--- a/include/stack.h
+++ b/include/stack.h
@@ -3,7 +3,19 @@
 
 #include "3cl.h"
 
+
+/**
+ * Push num to the stack
+ * @param stack Stack where to push
+ * @param num Number to push
+ */
 void ccl_stack_push(struct CCLStack *stack, CCLNum num);
+
+/**
+ * Pop number from the stack
+ * @param stack Stack from where to pop
+ * @return Popped number
+ */
 CCLNum ccl_stack_pop(struct CCLStack *stack);
 
 #endif /* __CCL_STACK_H__ */
diff --git a/include/utils.h b/include/utils.h
index 40bb642..f415abc 100644
--- a/include/utils.h
+++ b/include/utils.h
@@ -3,6 +3,14 @@
 
 #include <stdnoreturn.h>
 
+
+/**
+ * Show message:
+ * program_name: <formattedstring>\n
+ * and exit with code
+ * @param code Code to exit
+ * @param fmt Format string
+ */
 noreturn void die(int code, char *fmt, ...);
 
 #endif /* __UTILS_H__ */
diff --git a/include/variable.h b/include/variable.h
new file mode 100644
index 0000000..4480855
--- /dev/null
+++ b/include/variable.h
@@ -0,0 +1,24 @@
+#ifndef __CCL_VARIABLE_H__
+#define __CCL_VARIABLE_H__
+
+#include "3cl.h"
+
+
+/**
+ * Tries to find variable in list, NULL if not found.
+ * @see ccl_variable_getany
+ * @param vars Variable list
+ * @param name Variable name
+ */
+struct CCLVariable *ccl_variable_get(struct CCLVariable *vars, char name);
+
+/**
+ * Tries to find variable in current frame, then in root, NULL if not found.
+ * @see ccl_variable_get
+ * @param ccl CCL instance
+ * @param frame Current frame
+ * @param name Variable name
+ */
+struct CCLVariable *ccl_variable_getany(struct CCL *ccl, struct CCLFrame *frame, char name);
+
+#endif /* __CCL_VARIABLE_H__ */
diff --git a/src/3cl.c b/src/3cl.c
index c8016a9..bcadf4d 100644
--- a/src/3cl.c
+++ b/src/3cl.c
@@ -1,6 +1,7 @@
 #include "3cl.h"
 
 #include <errno.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 
@@ -12,6 +13,7 @@ int ccl_init(struct CCL *ccl, const char *code, int (*in)(), void (*out)(int))
     *ccl = (struct CCL)
     {
         .code = code,
+        .stopped = false,
         .in = in,
         .out = out,
         .rootframe = (struct CCLFrame)
@@ -69,6 +71,10 @@ void ccl_exec(struct CCL *ccl)
 {
     struct CCLFrame *curframe = &ccl->rootframe;
 
-    for (;;++curframe->ep)
+    for (;;)
+    {
         curframe = ccl_instruction(ccl, curframe);
+        if (ccl->stopped)
+            break;
+    }
 }
diff --git a/src/instruction.c b/src/instruction.c
index 8597d74..72cca6c 100644
--- a/src/instruction.c
+++ b/src/instruction.c
@@ -1,18 +1,36 @@
 #include "instruction.h"
+#include "instructions.h"
 
+#include <stdbool.h>
+
+#include "readchar.h"
 #include "3cl.h"
 
+
 struct CCLFrame *ccl_instruction(struct CCL *ccl, struct CCLFrame *frame)
 {
     CCLInstruction instruction;
-    switch (ccl->code[frame->ep])
+    char chr = ccl_readchar(ccl, frame, CCL_RC_CCL_INSTR);
+
+    if (chr == '\0')
+    {
+        ccl->stopped = true;
+        return INST(nop)(ccl, frame);
+    }
+
+#define ISSW(NAME) instruction = INST(NAME); break
+    switch (chr)
     {
     case '\n': /* FALLTHROUGH */
     case ' ' : /* FALLTHROUGH */
-    case '\t': instruction = ccl_instruction_nop;
-    case '^' : instruction = ccl_instruction_pushzero;
-    case '+' : instruction = ccl_instruction_increment;
-    case '-' : instruction = ccl_instruction_decrement;
+    case '\t': ISSW(nop);
+    case '^' : ISSW(pushzero);
+    case '+' : ISSW(increment);
+    case '-' : ISSW(decrement);
+    case '*' : ISSW(add);
+    case '~' : ISSW(subtract);
+    case '%' : ISSW(reverse);
     }
+#undef INSW
     return instruction(ccl, frame);
 }
diff --git a/src/instruction/add.c b/src/instruction/add.c
new file mode 100644
index 0000000..81bef71
--- /dev/null
+++ b/src/instruction/add.c
@@ -0,0 +1,14 @@
+#include "3cl.h"
+
+#include "readchar.h"
+#include "stack.h"
+#include "utils.h"
+
+struct CCLFrame *ccl_instruction_add(struct CCL *ccl, struct CCLFrame *frame)
+{
+    if (ccl->stack.cur < 2)
+        die(1, "stack size is %d (%d required)", ccl->stack.cur, 2);
+    ccl_stack_push(&ccl->stack, ccl_stack_pop(&ccl->stack) + ccl_stack_pop(&ccl->stack));
+
+    return frame;
+}
diff --git a/src/instruction/decrement.c b/src/instruction/decrement.c
index f7f54dd..ce3af87 100644
--- a/src/instruction/decrement.c
+++ b/src/instruction/decrement.c
@@ -1,8 +1,11 @@
 #include "3cl.h"
-#include "instruction.h"
+
+#include "utils.h"
 
 struct CCLFrame *ccl_instruction_decrement(struct CCL *ccl, struct CCLFrame *frame)
 {
+    if (ccl->stack.cur < 1)
+        die(1, "stack size is %d (%d required)", ccl->stack.cur, 1);
     --ccl->stack.stack[ccl->stack.cur];
     return frame;
 }
diff --git a/src/instruction/increment.c b/src/instruction/increment.c
index 51697f1..d2d51a5 100644
--- a/src/instruction/increment.c
+++ b/src/instruction/increment.c
@@ -1,8 +1,11 @@
 #include "3cl.h"
-#include "instruction.h"
+
+#include "utils.h"
 
 struct CCLFrame *ccl_instruction_increment(struct CCL *ccl, struct CCLFrame *frame)
 {
+    if (ccl->stack.cur < 1)
+        die(1, "stack size is %d (%d required)", ccl->stack.cur, 1);
     ++ccl->stack.stack[ccl->stack.cur];
     return frame;
 }
diff --git a/src/instruction/nop.c b/src/instruction/nop.c
index 8e05396..778b8ed 100644
--- a/src/instruction/nop.c
+++ b/src/instruction/nop.c
@@ -1,5 +1,4 @@
 #include "3cl.h"
-#include "instruction.h"
 
 struct CCLFrame *ccl_instruction_nop(struct CCL *ccl, struct CCLFrame *frame)
 {
diff --git a/src/instruction/pushzero.c b/src/instruction/pushzero.c
index ef380cd..cd0a023 100644
--- a/src/instruction/pushzero.c
+++ b/src/instruction/pushzero.c
@@ -1,5 +1,4 @@
 #include "3cl.h"
-#include "instruction.h"
 
 #include "stack.h"
 
diff --git a/src/instruction/reverse.c b/src/instruction/reverse.c
new file mode 100644
index 0000000..bd863ae
--- /dev/null
+++ b/src/instruction/reverse.c
@@ -0,0 +1,51 @@
+#include "3cl.h"
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "readchar.h"
+#include "utils.h"
+#include "variable.h"
+
+
+struct CCLFrame *ccl_instruction_reverse(struct CCL *ccl, struct CCLFrame *frame)
+{
+    char varname;
+    size_t toreverse;
+
+    varname = ccl_readchar(ccl, frame, CCL_RC_CCL_VARUS);
+    if (varname == '\0')
+    {
+        die(1, "Unexpected EOF");
+    } else if (varname == '_')
+    {
+        toreverse = ccl->stack.cur;
+    } else
+    {
+        struct CCLVariable *var = ccl_variable_getany(ccl, frame, varname);
+        if (var == NULL)
+            die(1, "Unknown variable '%c' at %d", varname, frame->ep);
+        toreverse = var->value;
+    }
+
+    if (ccl->stack.cur < toreverse)
+        die(1, "stack size is %d (%d required)", ccl->stack.cur, toreverse);
+
+    CCLNum *temp = malloc(sizeof(*temp) * toreverse);
+    if (temp == NULL)
+        die(1, "malloc(): %s", strerror(errno));
+
+    size_t start;
+    for (start = 0; start < toreverse; ++start)
+        temp[start] = ccl->stack.stack[ccl->stack.cur - start];
+    start = ccl->stack.cur - start;
+    for (int i = 0; i < toreverse; ++i)
+        ccl->stack.stack[start + i] = temp[i];
+
+    free(temp);
+
+    return frame;
+}
diff --git a/src/instruction/subtract.c b/src/instruction/subtract.c
new file mode 100644
index 0000000..f140cfb
--- /dev/null
+++ b/src/instruction/subtract.c
@@ -0,0 +1,12 @@
+#include "3cl.h"
+
+#include "stack.h"
+#include "utils.h"
+
+struct CCLFrame *ccl_instruction_subtract(struct CCL *ccl, struct CCLFrame *frame)
+{
+    if (ccl->stack.cur < 2)
+        die(1, "stack size is %d (%d required)", ccl->stack.cur, 2);
+    ccl_stack_push(&ccl->stack, -ccl_stack_pop(&ccl->stack) + ccl_stack_pop(&ccl->stack));
+    return frame;
+}
diff --git a/src/readchar.c b/src/readchar.c
new file mode 100644
index 0000000..51e1784
--- /dev/null
+++ b/src/readchar.c
@@ -0,0 +1,59 @@
+#include "3cl.h"
+#include "readchar.h"
+
+#include <stdbool.h>
+#include <string.h>
+
+#include "utils.h"
+
+
+static const char *const space    = " \n\t";
+static const char *const brackets = "{[(?;)]}";
+static const char *const instr    = "^+-*~%=!$&<>@#:";
+static const char *const alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+char ccl_readchar(struct CCL *ccl, struct CCLFrame *frame, enum CCLRCFlags flags)
+{
+    bool iscomment;
+
+    char chr;
+    for (;(chr = ccl->code[frame->ep]) != '\0'; ++frame->ep)
+    {
+        if (iscomment && chr == '\n')
+            iscomment = false;
+        if (iscomment)
+            continue;
+        if (chr == '/')
+            iscomment = true;
+
+        if (strchr(space, chr))
+            continue;
+
+        if (strchr(brackets, chr))
+            if (!(flags & CCL_RC_BRACK) && flags & CCL_RC_DIE)
+                goto invalid;
+            else
+                goto ok;
+        else if (strchr(instr, chr))
+            if (!(flags & CCL_RC_IS) && flags & CCL_RC_DIE)
+                goto invalid;
+            else
+                goto ok;
+        else if (strchr(alphabet, chr))
+            if (!(flags & CCL_RC_ALP) && flags & CCL_RC_DIE)
+                goto invalid;
+            else
+                goto ok;
+        else if (chr == '_')
+            if (!(flags & CCL_RC_US) && flags & CCL_RC_DIE)
+                goto invalid;
+            else
+                goto ok;
+        else
+            goto invalid;
+    }
+ok:
+    return chr;
+invalid:
+    die(1, "Invalid symbol at %d", frame->ep);
+}
diff --git a/src/variable.c b/src/variable.c
new file mode 100644
index 0000000..38ea2e3
--- /dev/null
+++ b/src/variable.c
@@ -0,0 +1,32 @@
+#include "3cl.h"
+#include "variable.h"
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+
+struct CCLVariable *ccl_variable_get(struct CCLVariable *vars, char name)
+{
+    for (struct CCLVariable *var = vars;;var = var->next)
+        if (var->name == name)
+            return var;
+        else if (var->name == '_' || var->next == NULL)
+            return NULL;
+}
+
+struct CCLVariable *ccl_variable_getany(struct CCL *ccl, struct CCLFrame *frame, char name)
+{
+    bool root = false;
+    struct CCLFrame *curframe;
+    struct CCLVariable *found;
+
+    for (curframe = frame; curframe->type != CCL_PROC && curframe->type != CCL_ROOT; curframe = curframe->prev);
+    if (curframe->type == CCL_ROOT)
+        root = true;
+
+    found = ccl_variable_get(&curframe->vars, name);
+    if (!found && !root)
+        return ccl_variable_get(&ccl->rootframe.vars, name);
+
+    return NULL;
+}