interpreter

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit 51885365d7eb6e606ced209cdce6ec87941cc099
Author: Samdal <samdal@protonmail.com>
Date:   Thu, 18 Apr 2024 00:13:22 +0200

initial commit

Diffstat:
A.gitignore | 5+++++
AREADME.md | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acommon.h | 226+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainterpreter.h | 336+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amain.c | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aparser.h | 138+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 884 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,5 @@ +*.out +*.exe +*.o +*.awa +awaparser diff --git a/README.md b/README.md @@ -0,0 +1,77 @@ +# AWA 5.0 interpreter in C (with debugging utils) +currently tested on both hello world and the fibonacci script, hopefully it works. +### Compiling +The program only needs libc as a dependency, it's a terminal program. + +So just compile with for example: +- unix: `cc main.c -o awaparser` +- windows: `cl /Fe: awaparser.exe main.c` + +### usage +``` +command line options: + '--quiet' : disables printing inbetween operations + + '--verbose-parser' : prints opcodes and parameters of the awatalk + '--verbose-interpreter' : prints current instruction and stack while executing + '--verbose' : enables both of the above + + '--help' : prints this message + + '-string' : the next provided argument will be awatalk to be parsed + '-file' : the next provided argument will be awatalk file to be parsed +example usage: + awaparser --verbose -string 'awa awa awawa awawa awa awa awa awa awa awawa awa awa awawa awawa awa awa awa awa awa awawawa awa awawa awawa awa awa awa awa awa awawa awa awawa awa awawa awa awa awawawa awa awa awa awawa' + awaparser --quiet -file hello_world.awa + awaparser --verbose-parser -file hello_world.awa +``` +### debug output example +``` +$ ./awaparser --verbose -string 'awa awa awawa awawa awa awa awa awa awa awawa awa awa awawa awawa awa awa awa awa awa awawawa awa awawa awawa awa awa awa awa awa awawa awa awawa awa awawa awa awa awawawa awa awa awa awawa' +blo 2 [a] +blo 3 [w] +blo 2 [a] +srn 3 +prn +parsed file succesfully +running program: +- + <<<() | blo 2 [a]>>> + <<<(2) | blo 3 [w]>>> + <<<(3, 2) | blo 2 [a]>>> + <<<(2, 3, 2) | srn 3>>> + <<<((2, 3, 2)) | prn>>> +awa +- +program done! +``` + +### error messages +``` +$ ./awaparser --verbose -string 'awa awawawww' +0:10 error: awa parser got unexpected input starting here +awa awawawww + ^~~ +``` +``` +$ ./awaparser --verbose -file test.awa +blo 0 [A] +blo 0 [A] +blo 0 [A] +blo 0 [A] +blo 0 [A] +blo 0 [A] +blo 0 [A] +blo 0 [A] +blo 39 [f] +prn +blo 21 [i] +prn +blo 41 [r] +prn +14:32 error: awa parser got unexpected input starting here + awa__hello_ebebiben!__ awawa awwa awawa awawa awa awa + ^~~ +NOTE: awa parser error above happened while parsing an opcode +error during parsing, exiting. +``` diff --git a/common.h b/common.h @@ -0,0 +1,226 @@ +#ifndef AWA_COMMON_H_ +#define AWA_COMMON_H_ + +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <stdio.h> +#include <stdbool.h> +#include <ctype.h> +#include <errno.h> +#include <assert.h> + +static char awascii_table[64] = { + 'A', 'W', 'a', 'w', + 'J', 'E', 'L', 'Y', + 'H', 'O', 'S', 'I', 'U', 'M', + 'j', 'e', 'l', 'y', + 'h', 'o', 's', 'i', 'u', 'm', + 'P', 'C', 'N', 'T', + 'p', 'c', 'n', 't', + 'B', 'D', 'F', 'G', 'R', + 'b', 'd', 'f', 'g', 'r', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + ' ', '.', ',', '!', '\'', '(', ')', '~', '_', '/', ';', '\n' +}; +static uint8_t awascii_table_inverse[256] = { + ['A'] = 0, ['W'] = 1, ['a'] = 2, ['w'] = 3, + ['J'] = 4, ['E'] = 5, ['L'] = 6, ['Y'] = 7, + ['H'] = 8, ['O'] = 9, ['S'] = 10, ['I'] = 11, ['U'] = 12, ['M'] = 13, + ['j'] = 14, ['e'] = 15, ['l'] = 16, ['y'] = 17, + ['h'] = 18, ['o'] = 19, ['s'] = 20, ['i'] = 21, ['u'] = 22, ['m'] = 23, + ['P'] = 24, ['C'] = 25, ['N'] = 26, ['T'] = 27, + ['p'] = 28, ['c'] = 29, ['n'] = 30, ['t'] = 31, + ['B'] = 32, ['D'] = 33, ['F'] = 34, ['G'] = 35, ['R'] = 36, + ['b'] = 37, ['d'] = 38, ['f'] = 39, ['g'] = 40, ['r'] = 41, + ['0'] = 42, ['1'] = 43, ['2'] = 44, ['3'] = 45, ['4'] = 46, ['5'] = 47, ['6'] = 48, ['7'] = 49, ['8'] = 50, ['9'] = 51, + [' '] = 52, ['.'] = 53, [','] = 54, ['!'] = 55, ['\''] = 56, ['('] = 57, [')'] = 58, ['~'] = 59, ['_'] = 60, ['/'] = 61, [';'] = 62, ['\n'] = 63, +}; + +enum awatisms { + AWA_NOP = 0x00, + AWA_PRINT = 0x01, + AWA_PRINT_NUM = 0x02, + AWA_READ = 0x03, + AWA_READ_NUM = 0x04, + AWA_TERMINATE = 0x1F, + + AWA_BLOW = 0x05, + AWA_SUBMERGE = 0x06, + AWA_POP = 0x07, + AWA_DUPLICATE = 0x08, + AWA_SURROUND = 0x09, + AWA_MERGE = 0x0A, + + AWA_ADD = 0x0B, + AWA_SUBTRACT = 0x0C, + AWA_MULTIPLY = 0x0D, + AWA_DIVIDE = 0x0E, + AWA_COUNT = 0x0F, + + AWA_LABEL = 0x10, + AWA_JUMP = 0x11, + AWA_EQUAL_TO = 0x12, + AWA_LESS_THAN = 0x13, + AWA_GREATER_THAN = 0x14, +}; +#define AWA_OPCODE_MAX 32 + +static char* awatism_opcode_names[AWA_OPCODE_MAX] = { + [AWA_NOP] = "nop", + [AWA_PRINT] = "prn", + [AWA_PRINT_NUM] = "pr1", + [AWA_READ] = "red", + [AWA_READ_NUM] = "r3d", + [AWA_TERMINATE] = "trm", + + [AWA_BLOW] = "blo", + [AWA_SUBMERGE] = "sbm", + [AWA_POP] = "pop", + [AWA_DUPLICATE] = "dpl", + [AWA_SURROUND] = "srn", + [AWA_MERGE] = "mrg", + + [AWA_ADD] = "4dd", + [AWA_SUBTRACT] = "sub", + [AWA_MULTIPLY] = "mul", + [AWA_DIVIDE] = "div", + [AWA_COUNT] = "cnt", + + [AWA_LABEL] = "lbl", + [AWA_JUMP] = "jmp", + [AWA_EQUAL_TO] = "eql", + [AWA_LESS_THAN] = "lss", + [AWA_GREATER_THAN] = "gr8", +}; + +static uint8_t awatism_param_sizes_bits[AWA_OPCODE_MAX] = { + [AWA_BLOW] = 8, + [AWA_SUBMERGE] = 5, + [AWA_SURROUND] = 5, + [AWA_LABEL] = 5, + [AWA_JUMP] = 5, +}; + +typedef struct awatism awatism_t; + struct awatism { + uint8_t opcode; + union { + uint8_t u8; + int8_t s8; + }; + + awatism_t* next; + + int current_pos_bytes; +}; + +#define llist_queue_push(f,l,n) ((f)==0) ? (f)=(l)=(n) : ((l)->next=(n), (l)=(n), (n)->next=0) +#define llist_queue_pop(f,l) ((f)==(l)) ? ((f)=(l)=0) : ((f)=(f)->next) + +#define llist_stack_push(f,n) ((n)->next=(f), (f)=(n)) +#define llist_stack_pop(f) ((f)==0) ? 0 : ((f)=(f->next)) + +#define awa_debug_printf(...) printf(__VA_ARGS__) + +typedef struct awa_bubble awa_bubble_t; +struct awa_bubble { + awa_bubble_t* next; + awa_bubble_t* first; + awa_bubble_t* last; + int val; +}; + +typedef struct { + bool failed_parse; + + char* source; + + awatism_t* awatism_first; + awatism_t* awatism_last; + + awatism_t* awalabel_table[64]; + + awa_bubble_t* bubble_first; + + bool cmp_skip_next_flag; + bool last_print_was_number_flag; +} awa_program_t; + +typedef struct { + const char* awatalk; + int pos; + int total_size; +} awa_parser_t; + +typedef struct { + const char* line_start; + int col; + int line; + int line_len; +} awa_line_info_t; + +static awa_line_info_t awa_get_line_and_col(awa_parser_t parser) +{ + awa_line_info_t res = {0}; + res.line_start = parser.awatalk; + for (;;) { + const char* current_newline = strchr(res.line_start == parser.awatalk ? parser.awatalk : res.line_start, '\n'); + if (!current_newline) break; + if (parser.pos < current_newline - parser.awatalk) break; + res.line_start = current_newline + 1; + res.line++; + } + const char* newline_after = strchr(res.line_start, '\n'); + if (newline_after) res.line_len = newline_after - res.line_start - 1; + else res.line_len = strlen(res.line_start); + res.col = parser.pos - (res.line_start - parser.awatalk); + return res; +} + +static void awa_print_line(awa_line_info_t line) +{ + awa_debug_printf("%.*s\n", line.line_len+1, line.line_start); +} + +static void awa_print_awatism(awatism_t awatism) { + awa_debug_printf("%s", awatism_opcode_names[awatism.opcode]); + switch (awatism.opcode) { + case AWA_BLOW: + { + if (awatism.s8 < 64) { + if (awascii_table[awatism.s8] == '\n') + awa_debug_printf(" %d [\\n]", (int)awatism.s8); + else + awa_debug_printf(" %d [%c]", (int)awatism.s8, awascii_table[awatism.s8]); + } else { + awa_debug_printf(" %d", (int)awatism.s8); + } + break; + } break; + case AWA_SUBMERGE: + case AWA_SURROUND: + case AWA_LABEL: + case AWA_JUMP: + { + awa_debug_printf(" %d", (int)awatism.u8); + break; + } break; + } +} + +static void awa_recursive_debug_print_bubble(awa_bubble_t* bubble) { + if (bubble->first) { + printf("("); + for (awa_bubble_t* b = bubble->first; b; b = b->next) { + if (b != bubble->first) printf(", "); + awa_recursive_debug_print_bubble(b); + } + printf(")"); + } else { + printf("%d", bubble->val); + } +} + + +#endif // AWA_COMMON_H_ diff --git a/interpreter.h b/interpreter.h @@ -0,0 +1,336 @@ +#ifndef AWA_INTERPRETER_H_ +#define AWA_INTERPRETER_H_ + +#include "common.h" + +static void awa_recursive_bubble_free(awa_bubble_t* bubble) { + if (bubble) { + for (awa_bubble_t *next, *b = bubble->first; b; b = next) { + next = b->next; + awa_recursive_bubble_free(b); + } + free(bubble); + } +} +#define bubble_pop(prog) do { \ + awa_bubble_t* tmp = (prog)->bubble_first; \ + llist_stack_pop((prog)->bubble_first); \ + awa_recursive_bubble_free(tmp); \ + } while (0); + + +static void awa_recursive_print_bubble(awa_bubble_t* bubble) { + if (bubble->first) { + for (awa_bubble_t* b = bubble->first; b; b = b->next) { + awa_recursive_print_bubble(b); + } + } else { + putchar(awascii_table[bubble->val & 63]); + } +} +static void awa_recursive_print_num_bubble(awa_bubble_t* bubble, bool first) { + if (bubble->first) { + for (awa_bubble_t* b = bubble->first; b; b = b->next) { + awa_recursive_print_num_bubble(b, false); + } + } else { + if (!first) putchar(' '); + printf("%d", bubble->val); + } +} + +static void awa_recursive_bubble_deep_copy(awa_bubble_t* to, awa_bubble_t* from) { + for (awa_bubble_t* from_inside = from->first; from_inside; from_inside = from_inside->next) { + awa_bubble_t* b = calloc(sizeof(*b), 1); + awa_recursive_bubble_deep_copy(b, from_inside); + llist_queue_push(to->first, to->last, b); + } + to->val = from->val; +} + +enum awa_arithmetic_op { + AWA_ARITH_ADD, + AWA_ARITH_SUB, + AWA_ARITH_MUL, + AWA_ARITH_DIV, +}; +static int awa_bubble_arithmetic_val(awa_bubble_t* a, awa_bubble_t* b, enum awa_arithmetic_op op) { + switch(op) { + case AWA_ARITH_ADD: return a->val + b->val; + case AWA_ARITH_SUB: return a->val - b->val; + case AWA_ARITH_MUL: return a->val * b->val; + case AWA_ARITH_DIV: return a->val / b->val; + } + return 0; +} + +static awa_bubble_t* awa_recursive_bubble_arithmetic(awa_bubble_t* a, awa_bubble_t* b, enum awa_arithmetic_op op) { + awa_bubble_t* new = calloc(sizeof(*new), 1); + if (!a->first && !b->first) { + new->val = awa_bubble_arithmetic_val(a, b, op); + } else if (a->first && !b->first) { + for (awa_bubble_t* a_inside = a->first; a_inside; a_inside = a_inside->next) { + awa_bubble_t* new_inside = awa_recursive_bubble_arithmetic(a_inside, b, op); + llist_queue_push(new->first, new->last, new_inside); + } + } else if (!a->first && b->first) { + for (awa_bubble_t* b_inside = b->first; b_inside; b_inside = b_inside->next) { + awa_bubble_t* new_inside = awa_recursive_bubble_arithmetic(a, b_inside, op); + llist_queue_push(new->first, new->last, new_inside); + } + } else { + for (awa_bubble_t* b_inside = b->first, *a_inside = a->first; b_inside && a_inside; b_inside = b_inside->next, a_inside = a_inside->next) { + awa_bubble_t* new_inside = awa_recursive_bubble_arithmetic(a_inside, b_inside, op); + llist_queue_push(new->first, new->last, new_inside); + } + } + return new; +} + +static void awa_bubble_arithmetic(awa_program_t* program, enum awa_arithmetic_op op) { + if (program->bubble_first) { + if (program->bubble_first->next) { + awa_bubble_t* new = awa_recursive_bubble_arithmetic(program->bubble_first, program->bubble_first->next, op); + new->next = program->bubble_first->next->next; + + awa_recursive_bubble_free(program->bubble_first->next); + awa_recursive_bubble_free(program->bubble_first); + program->bubble_first = new; + } + } +} + +enum awa_cmp_op { + AWA_CMP_EQ, + AWA_CMP_LESS, + AWA_CMP_GRTR, +}; +static bool awa_bubble_cmp_val(awa_bubble_t* a, awa_bubble_t* b, enum awa_cmp_op op) { + switch(op) { + case AWA_CMP_EQ: return a->val == b->val; + case AWA_CMP_LESS: return a->val < b->val; + case AWA_CMP_GRTR: return a->val > b->val; + } + return false; +} +static bool awa_recursive_bubble_cmp(awa_bubble_t* a, awa_bubble_t* b, enum awa_cmp_op op) { + if (!a->first && !b->first) { + if (!awa_bubble_cmp_val(a, b, op)) + return false; + } else if (a->first && !b->first) { + for (awa_bubble_t* a_inside = a->first; a_inside; a_inside = a_inside->next) { + if (!awa_recursive_bubble_cmp(a_inside, b, op)) + return false; + } + } else if (!a->first && b->first) { + for (awa_bubble_t* b_inside = b->first; b_inside; b_inside = b_inside->next) { + if (!awa_recursive_bubble_cmp(a, b_inside, op)) + return false; + } + } else { + awa_bubble_t* b_inside = b->first, *a_inside = a->first; + for (; b_inside && a_inside; b_inside = b_inside->next, a_inside = a_inside->next) { + if (!awa_recursive_bubble_cmp(a_inside, b_inside, op)) + return false; + } + if (a_inside != b_inside) + return false; + } + return true; +} +static void awa_bubble_cmp(awa_program_t* program, enum awa_cmp_op op) { + if (program->bubble_first) { + if (program->bubble_first->next) { + if (!awa_recursive_bubble_cmp(program->bubble_first, program->bubble_first->next, op)) { + program->cmp_skip_next_flag = true; + } + } + } +} + +static void awa_program_run(awa_program_t program, bool print_debug) { + for (awatism_t* current_awatism = program.awatism_first; current_awatism; current_awatism = current_awatism->next) { + rerun_current_awatism:; + if (print_debug) { + printf("\t<<<"); + printf("("); + for (awa_bubble_t* b = program.bubble_first; b; b = b->next) { + if (b != program.bubble_first) printf(", "); + awa_recursive_debug_print_bubble(b); + } + printf(")"); + printf(" | "); + awa_print_awatism(*current_awatism); + printf(">>>\n"); + } + + if (program.cmp_skip_next_flag) { + program.cmp_skip_next_flag = false; + continue; + } + switch((enum awatisms)current_awatism->opcode) { + case AWA_NOP: { + // nothing + } break; + case AWA_PRINT: { + awa_recursive_print_bubble(program.bubble_first); + program.last_print_was_number_flag = false; + bubble_pop(&program); + } break; + case AWA_PRINT_NUM: { + awa_recursive_print_num_bubble(program.bubble_first, !program.last_print_was_number_flag); + program.last_print_was_number_flag = true; + bubble_pop(&program); + } break; + case AWA_READ: { + printf(">>>AWA INTERPRETER IS REQUESTING INPUT(text): "); + char buf[1024] = {0}; + fgets(buf, sizeof(buf), stdin); + for (int i = 0; buf[i]; i++) { + uint8_t c = awascii_table_inverse[(uint8_t)buf[i]]; + if (c && buf[i] != 'A') { + awa_bubble_t* b = calloc(sizeof(*b), 1); + b->val = c; + llist_stack_push(program.bubble_first, b); + } + } + printf(">>>AWA INTERPRETER IS READ INPUT [%s]\n", buf); + } break; + case AWA_READ_NUM: { + printf(">>>AWA INTERPRETER IS REQUESTING INPUT(numb): "); + char buf[1024] = {0}; + fgets(buf, sizeof(buf), stdin); + awa_bubble_t* b = calloc(sizeof(*b), 1); + sscanf(buf, "%d", &b->val); + llist_stack_push(program.bubble_first, b); + printf(">>>AWA INTERPRETER IS READ INPUT [%d]\n", b->val); + } break; + case AWA_TERMINATE: { + goto program_end; + } break; + case AWA_BLOW: { + awa_bubble_t* b = calloc(sizeof(*b), 1); + b->val = current_awatism->s8; + llist_stack_push(program.bubble_first, b); + } break; + case AWA_SUBMERGE: { + if (program.bubble_first) { + awa_bubble_t* sub = program.bubble_first; + int depth = current_awatism->u8; + program.bubble_first = program.bubble_first->next; + + awa_bubble_t* last = program.bubble_first; + int i = 1; + for (awa_bubble_t* b = program.bubble_first->next; b && i != depth; b = b->next, i++) { + last = b; + } + sub->next = last->next; + last->next = sub; + } + } break; + case AWA_POP: { + bubble_pop(&program); + } break; + case AWA_DUPLICATE: { + if (program.bubble_first) { + awa_bubble_t* b = calloc(sizeof(*b), 1); + awa_recursive_bubble_deep_copy(b, program.bubble_first); + llist_stack_push(program.bubble_first, b); + } + } break; + case AWA_SURROUND: { + awa_bubble_t* b = calloc(sizeof(*b), 1); + for (int i = 0; i < current_awatism->u8; i++) { + awa_bubble_t* b_inside = program.bubble_first; + if (!b_inside) break; + llist_stack_pop(program.bubble_first); + llist_queue_push(b->first, b->last, b_inside); + } + llist_stack_push(program.bubble_first, b); + } break; + case AWA_MERGE: { + awa_bubble_t* two[2]; + if (program.bubble_first) { + two[0] = program.bubble_first; + if (program.bubble_first->next) { + two[1] = program.bubble_first->next; + + if (!two[0]->first && !two[1]->first) { + two[0]->val += two[1]->val; + two[0]->next = two[1]->next; + free(two[1]); + } else { + awa_bubble_t* new = calloc(sizeof(*new), 1); + new->next = two[1]->next; + program.bubble_first = new; + + for (int i = 0; i < 2; i++) { + if (two[i]->first) { + for (awa_bubble_t* next, *b = two[i]->first; b; b = next) { + next = b->next; + llist_queue_push(new->first, new->last, b); + } + free(two[i]); + } else { + llist_queue_push(new->first, new->last, two[i]); + } + } + } + } + } + } break; + case AWA_ADD: { + awa_bubble_arithmetic(&program, AWA_ARITH_ADD); + } break; + case AWA_SUBTRACT: { + awa_bubble_arithmetic(&program, AWA_ARITH_SUB); + } break; + case AWA_MULTIPLY: { + awa_bubble_arithmetic(&program, AWA_ARITH_MUL); + } break; + case AWA_DIVIDE: { + awa_bubble_arithmetic(&program, AWA_ARITH_DIV); + } break; + case AWA_COUNT: { + if (program.bubble_first) { + int count = 0; + for (awa_bubble_t* b = program.bubble_first; b; b = b->next) { + count++; + } + awa_bubble_t* b = calloc(sizeof(*b), 1); + b->val = count; + llist_stack_push(program.bubble_first, b); + } + } break; + case AWA_LABEL: { + // nothing + } break; + case AWA_JUMP: { + if (program.awalabel_table[current_awatism->u8]) { + current_awatism = program.awalabel_table[current_awatism->u8]; + goto rerun_current_awatism; + } else { + awa_debug_printf("interpreter: unable to find label %d\n", current_awatism->u8); + } + } break; + case AWA_EQUAL_TO: { + awa_bubble_cmp(&program, AWA_CMP_EQ); + } break; + case AWA_LESS_THAN: { + awa_bubble_cmp(&program, AWA_CMP_LESS); + } break; + case AWA_GREATER_THAN: { + awa_bubble_cmp(&program, AWA_CMP_GRTR); + } break; + default: { + awa_debug_printf("interpreter: invalid opcode %d\n", current_awatism->opcode); + } break; + } + } +program_end:; + while(program.bubble_first) + bubble_pop(&program); +} + + +#endif // AWA_INTERPRETER_H_ diff --git a/main.c b/main.c @@ -0,0 +1,102 @@ +#include "parser.h" +#include "interpreter.h" + +void print_help() { + printf("command line options:\n"); + printf("\t'--quiet' : disables printing inbetween operations\n"); + printf("\t\n"); + printf("\t'--verbose-parser' : prints opcodes and parameters of the awatalk\n"); + printf("\t'--verbose-interpreter' : prints current instruction and stack while executing\n"); + printf("\t'--verbose' : enables both of the above\n"); + printf("\t\n"); + printf("\t'--help' : prints this message\n"); + printf("\t\n"); + printf("\t'-string' : the next provided argument will be awatalk to be parsed\n"); + printf("\t'-file' : the next provided argument will be awatalk file to be parsed\n"); + printf("example usage:\n"); + printf("\t'awaparser -verbose -string 'awa awa awawa awawa awa awa awa awa awa awawa awa awa awawa awawa awa awa awa awa awa awawawa awa awawa awawa awa awa awa awa awa awawa awa awawa awa awawa awa awa awawawa awa awa awa awawa'\n"); + printf("\t'awaparser --quiet -file hello_world.awa\n"); + printf("\t'awaparser --verbose-parser -file hello_world.awa\n"); +} + +int main(int argc, char** argv) +{ + bool verbose_parser = false; + bool verbose_interpreter = false; + bool quiet = false; + char* file = NULL; + char* string = NULL; + bool file_next = false; + bool string_next = false; + for (int i = 1; i < argc; i++) { + if (file_next) { + file_next = false; + file = argv[i]; + if (*argv[i] == '-') + printf("WARNING: -file argument might not be a propper filepath '%s'\n", argv[i]); + continue; + } + if (string_next) { + string_next = false; + string = argv[i]; + if (*argv[i] == '-') + printf("WARNING: -string argument might not be a propper awatalk '%s'\n", argv[i]); + continue; + } + if (strcmp(argv[i], "--help") == 0) { + print_help(); + return 0; + } + if (strcmp(argv[i], "--quiet") == 0) + quiet = true; + else if (strcmp(argv[i], "--verbose") == 0) + verbose_parser = verbose_interpreter = true; + else if (strcmp(argv[i], "--verbose-parser") == 0) + verbose_parser = true; + else if (strcmp(argv[i], "--verbose-interpreter") == 0) + verbose_interpreter = true; + else if (strcmp(argv[i], "-file") == 0) + file_next = true; + else if (strcmp(argv[i], "-string") == 0) + string_next = true; + else + printf("WARNING: unknown command line argument '%s'\n", argv[i]); + } + if (string_next) { + puts("ERROR: '-string' was provided but the next argument didn't exist"); + return 2; + } + if (file_next) { + puts("ERROR: '-file' was provided but the next argument didn't exist"); + return 2; + } + if (file && string) { + puts("ERROR: '-file' and '-string' were provided at the same time"); + return 2; + } + if (!file && !string) { + puts("ERROR: neither '-file' nor '-string' were provided"); + print_help(); + return 0; + } + awa_program_t prog = {0}; + if (file) { + prog = awatalk_file_to_program(file, verbose_parser); + } + if (string) { + int l = strlen(string); + char* prog_source = malloc(l+1); + memcpy(prog_source, string, l+1); + prog = awatalk_to_program(prog_source, verbose_parser); + } + if (prog.failed_parse) { + puts("error during parsing, exiting."); + awa_program_free(&prog); + return 1; + } + if (!quiet) puts("parsed file succesfully"); + if (!quiet) puts("running program:\n-"); + awa_program_run(prog, verbose_interpreter); + if (!quiet) puts("\n-\nprogram done!"); + awa_program_free(&prog); +} diff --git a/parser.h b/parser.h @@ -0,0 +1,138 @@ +#ifndef AWA_PARSER_H_ +#define AWA_PARSER_H_ + +#include "common.h" + +static void awa_skip_to_next_valid(awa_parser_t* parser) +{ + while (!strchr(" AWaw\0", parser->awatalk[parser->pos])) parser->pos++; +} + +// returns true on error +static bool awa_parse_bits(awa_parser_t* parser, uint8_t bits, uint8_t* out) +{ + *out = 0; + + for (int bit_iter = 0; bit_iter < bits; bit_iter++) { + const char* awa_values[2] = {" awa", "wa"}; + int awa_v = 0; + bool found = false; + + for (; awa_v < 2; awa_v++) { + for (int i = 0; i < strlen(awa_values[awa_v]); i++) { + awa_skip_to_next_valid(parser); + if (tolower(parser->awatalk[parser->pos]) != awa_values[awa_v][i]) { + if (i == 0) goto continue_awa_v; + else goto break_awa_v; + } + parser->pos++; + } + found = true; + break; + continue_awa_v:; + } + break_awa_v:; + + if (!found) { + awa_line_info_t line = awa_get_line_and_col(*parser); + if (parser->pos == parser->total_size) + awa_debug_printf("%d:%d error: awa parser reached end of input\n", line.line+1, line.col); + else + awa_debug_printf("%d:%d error: awa parser got unexpected input starting here\n", line.line+1, line.col); + awa_print_line(line); + for (int i = 0; i<line.col;i++) awa_debug_printf(" "); + awa_debug_printf("^~~\n"); + return true; + } + + *out <<= 1; + *out |= awa_v; + } + + return false; +} + +static awa_program_t awatalk_to_program(char* awatalk_string, bool print_program) +{ + awa_program_t res = {.source = awatalk_string}; + if (!awatalk_string) { + awa_debug_printf("awatalk string was null!\n"); + res.failed_parse = true; + return res; + } + + awa_parser_t parser = { + .awatalk = awatalk_string, + .total_size = strlen(awatalk_string), + }; + + const char* awa_start = "awa"; + for (int i = 0; i < 3; i++) { + awa_skip_to_next_valid(&parser); + if (tolower(parser.awatalk[parser.pos]) != awa_start[i]) { + awa_debug_printf("awatalk string does not start with \"awa\"!\n"); + res.failed_parse = true; + return res; + } + parser.pos++; + } + + parser.pos = 3; + awa_skip_to_next_valid(&parser); + while (parser.pos < parser.total_size) { + awatism_t awatism = {0}; + bool opcode_err = awa_parse_bits(&parser, 5, &awatism.opcode); + if (opcode_err) { + awa_debug_printf("NOTE: awa parser error above happened while parsing an opcode\n"); + res.failed_parse = true; + break; + } + bool parameter_err = awa_parse_bits(&parser, awatism_param_sizes_bits[awatism.opcode], &awatism.u8); + if (parameter_err) { + awa_debug_printf("NOTE: awa parser error above happened while parsing a paramter of %s\n", awatism_opcode_names[awatism.opcode]); + res.failed_parse = true; + break; + } + if (print_program) { + awa_print_awatism(awatism); awa_debug_printf("\n"); + } + awa_skip_to_next_valid(&parser); + + awatism_t* n = malloc(sizeof(*n)); + *n = awatism; + llist_queue_push(res.awatism_first, res.awatism_last, n); + if (n->opcode == AWA_LABEL) + res.awalabel_table[n->u8] = n; + } + + return res; +} + +awa_program_t awatalk_file_to_program(const char* filename, bool print_program) { + char* buffer = 0; + FILE* fp = fopen(filename, "rb"); + size_t sz = 0; + if (fp) { + fseek(fp, 0, SEEK_END); + sz = ftell(fp); + fseek(fp, 0, SEEK_SET); + buffer = malloc(sz + 1); + if (buffer) fread(buffer, 1, sz, fp); + fclose(fp); + buffer[sz] = '\0'; + } else { + fprintf(stderr, "unable to open file %s: %s\n", filename, strerror(errno)); + } + return awatalk_to_program(buffer, print_program); +} + +void awa_program_free(awa_program_t* prog) { + free(prog->source); + for (awatism_t *next, *a = prog->awatism_first; a; a = next) { + next = a->next; + free(a); + } + *prog = (awa_program_t){0}; +} + +#endif // AWA_PARSER_H_