User:Marinus/Pointy interpreter
Jump to navigation
Jump to search
This is a Pointy interpreter written in C.
/* pointy.c * Pointy interpreter * by Marinus Oosters * * see http://esoteric.voxelperfect.net/wiki/Pointy * * Memory cells are 32-bit, unsigned integers. * Labels are not case-sensitive. * Comments start with ; (because that's what they did on the esolang wiki page), * or with # (so I can do #!/), and end with a newline. */ #include <stdio.h> #define MEMSZ 0x10000 #define N_LBL 0x100 // max number of labels in a program #define LBL_LEN 20 // max label length #define LINE_LEN 200 // max line length #define N_LINES 0x10000 // max number of lines in a program; #define ARG_LEN 140 // max argument length /* err */ void die(char *errmsg) { fprintf(stderr, "error: %s\n", errmsg); exit(0); } /* labels */ typedef struct { char name [LBL_LEN]; int line; } label; char c[50]; int lbl_amt=0; label lbls[N_LBL]; int getlbl(char *l) { // get line no for label name int i,r=-1; for (i=0; i<lbl_amt; i++) { if (!strcasecmp(lbls[i].name,l)) { r = lbls[i].line; break; } } if (r == -1) { sprintf(c,"%s does not exist.",l); die(c); } return r; } void setlbl(char *l, int ln) { // set label if (lbl_amt < N_LBL) { lbls[lbl_amt].line = ln; strcpy(lbls[lbl_amt++].name,l); } else { sprintf(c,"cannot store %s, too many labels (max %n).",l,N_LBL); die(c); } } // memory unsigned int memory[MEMSZ]; // dereference int value(char *n) { // gets value for number/pointer in n int r=0,i,a=0; char *t=n; while (*t && !isdigit(*t)) t++; r = atoi(t); // got the number, start dereferencing while (*n && !isdigit(*n)) if (*(n++)=='*') a++; for (;a;a--) { if (r<0||r>MEMSZ) { sprintf(c,"cannot dereference, %d is outside allocated memory (%d-%d)",r,0,MEMSZ); die(c); } r = memory[r]; } return r; } // some more error checking int memval(int n) { if (n<0||n>MEMSZ) { sprintf(c,"cannot dereference, %d is outside allocated memory (%d-%d)",n,0,MEMSZ); die(c); } return memory[n]; } void setval(int n, int v) { if (n<0||n>MEMSZ) { sprintf(c,"cannot assign, %d is outside allocated memory (%d-%d)",n,0,MEMSZ); die(c); } memory[n]=v; } // program code typedef enum { LBL, CPY, INC, DEC, OUT, INP } instr_t; typedef struct { instr_t instruction; char op1[ARG_LEN]; char op2[ARG_LEN]; } line; line pgm [N_LINES]; int n_of_lines=0, ip=0; // read program void readprogram(char *filename){ char buf[LINE_LEN], *lc=buf, ch, *ptr; bzero(pgm,sizeof(pgm)); bzero(buf,LINE_LEN); // read in the program FILE *f; f=fopen(filename, "r"); if (!f) { sprintf(c,"cannot open %s for reading",filename); die(c); } while (!feof(f)) { ch = fgetc(f); if (ch==';'||ch=='#') while (!feof(f) && (ch=fgetc(f))!='\n'); if (ch=='\n' || feof(f)) { // try to parse. *lc = 0; lc = buf; while (*lc && isspace(*lc)) lc++; if (!*lc) { // empty line or only a comment bzero(buf,LINE_LEN); lc=buf; continue; } // get instruction if (!strncasecmp(lc,"LBL",3)) pgm[n_of_lines].instruction = LBL; else if (!strncasecmp(lc,"CPY",3)) pgm[n_of_lines].instruction = CPY; else if (!strncasecmp(lc,"INC",3)) pgm[n_of_lines].instruction = INC; else if (!strncasecmp(lc,"DEC",3)) pgm[n_of_lines].instruction = DEC; else if (!strncasecmp(lc,"OUT",3)) pgm[n_of_lines].instruction = OUT; else if (!strncasecmp(lc,"INP",3)) pgm[n_of_lines].instruction = INP; else { strncpy(c,lc,3); c[3] = 0; sprintf(c,"%d: '%s' is not a valid instruction.",n_of_lines+1,c); die(c); } // get first argument lc += 3; while (*lc && isspace(*lc)) lc++; if (!*lc) { sprintf(c,"%d: no arguments given.",n_of_lines); die(c); } ptr = pgm[n_of_lines].op1; while (*lc && !isspace(*lc)) *(ptr++) = *(lc++); // if CPY or DEC, get second output. if (pgm[n_of_lines].instruction == CPY || pgm[n_of_lines].instruction == DEC) { while (*lc && isspace(*lc)) lc++; if (!*lc) { sprintf(c,"%d: instruction requires two arguments, but only one was given.",n_of_lines+1); die(c); } ptr = pgm[n_of_lines].op2; while (*lc && !isspace(*lc)) *(ptr++) = *(lc++); } // we're done. n_of_lines++; bzero(buf,LINE_LEN); lc = buf; } else *(lc++)=ch; } // find all the labels and store them for (ip = 0; ip < n_of_lines; ip++) if (pgm[ip].instruction == LBL) setlbl(pgm[ip].op1, ip); } /* Of a 209-line interpreter, the actual interpreting function is only 17 lines... */ void run() { // run the program for (ip = 0; ip < n_of_lines; ip++) { switch (pgm[ip].instruction) { case LBL: break; // don't do anything, labels have already been stored. case CPY: setval(value(pgm[ip].op2),value(pgm[ip].op1)); break; case INC: setval(value(pgm[ip].op1),memval(value(pgm[ip].op1))+1); break; case DEC: if (memval(value(pgm[ip].op1))) { setval(value(pgm[ip].op1), memval(value(pgm[ip].op1))-1); } else { ip = getlbl(pgm[ip].op2); }; break; case OUT: putchar(value(pgm[ip].op1));break; case INP: setval(value(pgm[ip].op1),feof(stdin)?0:getchar());break; } } } int main(int argc, char **argv) { if (argc != 2) { fprintf(stderr,"%s - Pointy interpreter\nWritten by Marinus Oosters\n\tusage: %s program\n\n", argv[0],argv[0]); exit(0); } readprogram(argv[1]); run(); return 0; }