summaryrefslogtreecommitdiffstats
path: root/tis-as.c
diff options
context:
space:
mode:
authorLouis Burda <quent.burda@gmail.com>2023-07-24 00:43:33 +0200
committerLouis Burda <quent.burda@gmail.com>2023-07-24 00:43:33 +0200
commitd7865d956f9fe3a08ebe4429dce4428a9f74bc6c (patch)
treeafcc976934add55eed727a3f782eea8912646122 /tis-as.c
downloadtis100-d7865d956f9fe3a08ebe4429dce4428a9f74bc6c.tar.gz
tis100-d7865d956f9fe3a08ebe4429dce4428a9f74bc6c.zip
Add initial rough outline
Diffstat (limited to 'tis-as.c')
-rw-r--r--tis-as.c362
1 files changed, 362 insertions, 0 deletions
diff --git a/tis-as.c b/tis-as.c
new file mode 100644
index 0000000..0d3625c
--- /dev/null
+++ b/tis-as.c
@@ -0,0 +1,362 @@
+#include "tpu.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#define WHITESPACE " \t\v\r\n"
+
+enum tok {
+ /* Global */
+ TOK_STDIN, TOK_STDOUT, TOK_TPU, TOK_END,
+
+ /* Operands (order like OP_*) */
+ TOK_ACC, TOK_BAK, TOK_NIL, TOK_LEFT, TOK_RIGHT,
+ TOK_UP, TOK_DOWN, TOK_ANY, TOK_LAST, TOK_LIT,
+
+ /* Instructions (order like INST_*) */
+ TOK_NOP, TOK_MOV, TOK_SWP, TOK_SAV, TOK_ADD, TOK_SUB,
+ TOK_NEG, TOK_JMP, TOK_JEZ, TOK_JNZ, TOK_JLZ, TOK_JRO,
+
+ /* Misc */
+ TOK_COMMENT, TOK_LABEL, TOK_XPOS, TOK_YPOS, TOK_NL, TOK_EOF
+};
+
+struct tokenizer {
+ FILE *file;
+ enum tok tok;
+ char *tokstr;
+ size_t lineno, off;
+ char linebuf[256];
+};
+
+static int stdin_x = 0, stdin_y = 0;
+static int stdout_x = 0, stdout_y = 0;
+
+struct tpu_map map;
+
+static const char *tok_reprs[] = {
+ /* Global */
+ "STDIN", "STDOUT", "BLOCK", "END",
+
+ /* Operands */
+ "ACC", "BAK", "NIL", "LEFT", "RIGHT", "UP",
+ "DOWN", "ANY", "LAST", "<LIT>",
+
+ /* Instructions */
+ "NOP", "MOV", "SWP", "SAV", "ADD", "SUB",
+ "NEG", "JMP", "JEZ", "JNZ", "JLZ", "JRO",
+
+ /* Misc */
+ "#<COMMENT>", "<LABEL>", "X<INT>", "Y<INT>", "<NL>", "<EOF>"
+};
+
+static void
+__attribute__((format(printf, 1, 2)))
+__attribute__((noreturn))
+die(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ fprintf(stderr, "tis: ");
+ vfprintf(stderr, fmt, ap);
+ if (*fmt && fmt[strlen(fmt)-1] == ':') {
+ fputc(' ', stderr);
+ perror(NULL);
+ } else {
+ fputc('\n', stderr);
+ }
+ va_end(ap);
+
+ exit(1);
+}
+
+bool
+is_lit(const char *str)
+{
+ unsigned long v;
+ char *end;
+
+ v = strtoul(str, &end, 10);
+ if (!end || *end) return false;
+
+ return v < 256;
+}
+
+uint8_t
+str2lit(const char *str)
+{
+ return (uint8_t) atoi(str);
+}
+
+enum tpu_inst_type
+tok_to_inst(enum tok tok)
+{
+ if (tok < TOK_NOP || tok >= TOK_JRO) abort();
+ return tok - TOK_NOP + INST_NOP;
+}
+
+enum tpu_inst_op_type
+tok_to_optype(enum tok tok)
+{
+ if (tok < TOK_ACC || tok >= TOK_LIT) abort();
+ return tok - TOK_ACC + OP_ACC;
+}
+
+enum tok
+tok_next(struct tokenizer *tok)
+{
+ size_t len;
+ char *s;
+
+ if (!tok->linebuf[tok->off]) {
+ if (feof(tok->file)) return TOK_EOF;
+ s = fgets(tok->linebuf, sizeof(tok->linebuf), tok->file);
+ if (!s && !feof(tok->file)) die("fgets:");
+ if (!s) return TOK_EOF;
+ if (*s && s[strlen(s)-1] != '\n')
+ die("tokenizer: line %lu too long", tok->lineno);
+ tok->off = 0;
+ }
+
+ s = tok->linebuf + tok->off;
+ len = strspn(s, WHITESPACE);
+ tok->off += len;
+ s += len;
+
+ len = strcspn(s, WHITESPACE);
+ s[len] = '\0';
+ tok->off += len;
+ if (!len) return TOK_NL;
+
+ if (!strcasecmp(s, "stdin")) {
+ return TOK_STDIN;
+ } else if (!strcasecmp(s, "stdout")) {
+ return TOK_STDIN;
+ } else if (!strcasecmp(s, "block")) {
+ return TOK_TPU;
+ } else if (!strcasecmp(s, "end")) {
+ return TOK_END;
+ } else if (!strcasecmp(s, "acc")) {
+ return TOK_ACC;
+ } else if (!strcasecmp(s, "bak")) {
+ return TOK_BAK;
+ } else if (!strcasecmp(s, "nil")) {
+ return TOK_NIL;
+ } else if (!strcasecmp(s, "left")) {
+ return TOK_LEFT;
+ } else if (!strcasecmp(s, "right")) {
+ return TOK_RIGHT;
+ } else if (!strcasecmp(s, "up")) {
+ return TOK_UP;
+ } else if (!strcasecmp(s, "down")) {
+ return TOK_DOWN;
+ } else if (!strcasecmp(s, "any")) {
+ return TOK_ANY;
+ } else if (!strcasecmp(s, "last")) {
+ return TOK_LAST;
+ } else if (is_lit(s)) {
+ return TOK_LIT;
+ } else if (!strcasecmp(s, "nop")) {
+ return TOK_NOP;
+ } else if (!strcasecmp(s, "mov")) {
+ return TOK_MOV;
+ } else if (!strcasecmp(s, "swp")) {
+ return TOK_SWP;
+ } else if (!strcasecmp(s, "sav")) {
+ return TOK_SAV;
+ } else if (!strcasecmp(s, "add")) {
+ return TOK_ADD;
+ } else if (!strcasecmp(s, "sub")) {
+ return TOK_SUB;
+ } else if (!strcasecmp(s, "neg")) {
+ return TOK_NEG;
+ } else if (!strcasecmp(s, "jmp")) {
+ return TOK_JMP;
+ } else if (!strcasecmp(s, "jez")) {
+ return TOK_JEZ;
+ } else if (!strcasecmp(s, "jnz")) {
+ return TOK_JNZ;
+ } else if (!strcasecmp(s, "jlz")) {
+ return TOK_JLZ;
+ } else if (!strcasecmp(s, "jro")) {
+ return TOK_JRO;
+ } else if (*s == '#') {
+ tok->off = strlen(s);
+ return TOK_COMMENT;
+ } else if (len && s[len-1] == ':') {
+ return TOK_LABEL;
+ } else if (*s == 'X' && atoi(s+1) > 0) {
+ return TOK_XPOS;
+ } else if (*s == 'Y' && atoi(s+1) > 0) {
+ return TOK_YPOS;
+ } else {
+ die("tokenizer: line %lu, invalid token '%s'", tok->lineno, s);
+ }
+}
+
+enum tok
+tok_next_in(struct tokenizer *tokenizer, ...)
+{
+ va_list ap, cpy;
+ enum tok tok;
+ bool first;
+ int arg;
+
+ tok = tok_next(tokenizer);
+
+ va_copy(cpy, ap);
+
+ va_start(cpy, tokenizer);
+ while ((arg = va_arg(cpy, int)) > 0) {
+ if (tok == arg) return tok;
+ }
+ va_end(cpy);
+
+ fprintf(stderr, "tis-as: tokenizer: ");
+ fprintf(stderr, "line %lu, got tok '%s', expected one of ",
+ tokenizer->lineno, tok_reprs[tok]);
+
+ first = true;
+ va_start(ap, tokenizer);
+ while ((arg = va_arg(ap, int)) > 0) {
+ if (!first) fputc(',', stderr);
+ fputs(tok_reprs[arg], stderr);
+ first = false;
+ }
+ va_end(ap);
+
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+static void
+load(const char *fname)
+{
+ struct tokenizer tokenizer;
+ enum tpu_inst_op_type op1, op2;
+ enum tok op1_tok, op2_tok;
+ uint8_t op1_lit, op2_lit;
+ enum tpu_inst_type inst;
+ struct tpu *tpu = NULL;
+ enum tok tok;
+ FILE *file;
+
+ file = fopen(fname, "r");
+ if (!file) die("fopen:");
+
+ tokenizer.lineno = 1;
+ tokenizer.file = file;
+ tokenizer.off = 0;
+ tokenizer.tokstr = NULL;
+ tokenizer.linebuf[tokenizer.off] = '\0';
+ while ((tok = tok_next(&tokenizer)) != TOK_EOF) {
+ switch (tok) {
+ case TOK_STDIN:
+ if (tpu) goto disallowed;
+ tok_next_in(&tokenizer, TOK_XPOS, -1);
+ stdin_x = atoi(tokenizer.tokstr + 1);
+ tok_next_in(&tokenizer, TOK_YPOS, -1);
+ stdin_y = atoi(tokenizer.tokstr + 1);
+ tok_next_in(&tokenizer, TOK_NL, -1);
+ break;
+ case TOK_STDOUT:
+ if (tpu) goto disallowed;
+ tok_next_in(&tokenizer, TOK_XPOS, -1);
+ stdout_x = atoi(tokenizer.tokstr + 1);
+ tok_next_in(&tokenizer, TOK_YPOS, -1);
+ stdout_y = atoi(tokenizer.tokstr + 1);
+ tok_next_in(&tokenizer, TOK_NL, -1);
+ break;
+ case TOK_TPU:
+ if (tpu) goto disallowed;
+ tpu = malloc(sizeof(struct tpu));
+ if (!tpu) die("malloc:");
+ tpu_init(tpu);
+ tok_next_in(&tokenizer, TOK_XPOS, -1);
+ tpu->x = atoi(tokenizer.tokstr + 1);
+ tok_next_in(&tokenizer, TOK_YPOS, -1);
+ tpu->y = atoi(tokenizer.tokstr + 1);
+ tok_next_in(&tokenizer, TOK_NL, -1);
+ /* TODO insert tpu */
+ break;
+ case TOK_END:
+ if (!tpu) goto disallowed;
+ tpu = NULL;
+ break;
+ case TOK_NOP: case TOK_MOV: case TOK_SWP: case TOK_SAV:
+ case TOK_ADD: case TOK_SUB: case TOK_NEG: case TOK_JMP:
+ case TOK_JEZ: case TOK_JNZ: case TOK_JLZ: case TOK_JRO:
+ if (!tpu) goto disallowed;
+ inst = tok_to_inst(tok);
+
+ op1_tok = tok_next_in(&tokenizer, TOK_ACC,
+ TOK_BAK, TOK_UP, TOK_DOWN,
+ TOK_LEFT, TOK_RIGHT, TOK_NL, -1);
+ if (op1_tok == TOK_NL) {
+ tok_next_in(&tokenizer, TOK_NL, -1);
+ tpu_add_inst(tpu, inst, -1, 0, -1, 0);
+ break;
+ }
+
+ op1 = tok_to_optype(op1_tok);
+ if (op1 == OP_LIT)
+ op1_lit = str2lit(tokenizer.tokstr);
+
+ op2_tok = tok_next_in(&tokenizer, TOK_ACC,
+ TOK_BAK, TOK_UP, TOK_DOWN,
+ TOK_LEFT, TOK_RIGHT, TOK_NL, -1);
+ if (op2_tok == TOK_NL) {
+ tok_next_in(&tokenizer, TOK_NL, -1);
+ tpu_add_inst(tpu, inst,
+ (int) op1, op1_lit, -1, 0);
+ break;
+ }
+
+ op2 = tok_to_optype(op2_tok);
+ if (op2 == OP_LIT)
+ op2_lit = str2lit(tokenizer.tokstr);
+ tok_next_in(&tokenizer, TOK_NL, -1);
+ tpu_add_inst(tpu, inst, (int) op1, op1_lit,
+ (int) op2, op2_lit);
+ break;
+ case TOK_COMMENT:
+ tok_next_in(&tokenizer, TOK_NL, -1);
+ break;
+ case TOK_LABEL:
+ /* TODO */
+ break;
+ default:
+ goto disallowed;
+ }
+ }
+
+ fclose(file);
+
+disallowed:
+ die("tokenizer: line %lu, token %s not allowed here",
+ tokenizer.lineno, tok_reprs[tok]);
+}
+
+void
+run(void)
+{
+
+}
+
+int
+main(int argc, const char **argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "Usage: tis-as FILE\n");
+ exit(1);
+ }
+
+ load(argv[1]);
+
+ run();
+}