From c74aea420c662039072f606b2d5ef1c73426e481 Mon Sep 17 00:00:00 2001 From: Nakidai Date: Sat, 24 Aug 2024 14:29:55 +0300 Subject: Add more code Add some funcitons to work with variables, add more instructions and add ability to stop the code from `ccl_instruction` --- CMakeLists.txt | 5 ++++ include/3cl.h | 2 ++ include/instruction.h | 13 +++++----- include/instructions.h | 21 ++++++++++++++++ include/main.h | 2 +- include/readchar.h | 44 +++++++++++++++++++++++++++++++++ include/readfile.h | 5 ++++ include/stack.h | 12 +++++++++ include/utils.h | 8 ++++++ include/variable.h | 24 ++++++++++++++++++ src/3cl.c | 8 +++++- src/instruction.c | 28 +++++++++++++++++---- src/instruction/add.c | 14 +++++++++++ src/instruction/decrement.c | 5 +++- src/instruction/increment.c | 5 +++- src/instruction/nop.c | 1 - src/instruction/pushzero.c | 1 - src/instruction/reverse.c | 51 +++++++++++++++++++++++++++++++++++++++ src/instruction/subtract.c | 12 +++++++++ src/readchar.c | 59 +++++++++++++++++++++++++++++++++++++++++++++ src/variable.c | 32 ++++++++++++++++++++++++ 21 files changed, 335 insertions(+), 17 deletions(-) create mode 100644 include/instructions.h create mode 100644 include/readchar.h create mode 100644 include/variable.h create mode 100644 src/instruction/add.c create mode 100644 src/instruction/reverse.c create mode 100644 src/instruction/subtract.c create mode 100644 src/readchar.c create mode 100644 src/variable.c 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 #include #include @@ -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 + +/** + * Show message: + * program_name: \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 +#include #include #include @@ -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 + +#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 +#include +#include +#include +#include + +#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 +#include + +#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 +#include + + +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; +} -- cgit 1.4.1