Last active
May 20, 2017 14:25
-
-
Save satoru-takeuchi/166502b3a4052f7a5cd1a78b8943a055 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* a light weight programming language interpreter | |
* | |
* usage: ./lang <code> | |
* | |
* - prefix notation | |
* - 'A' to 'Z' can bs used for functions. 'P' is a specitial function to print | |
* the result of a evaluation | |
* - 'a' to 'z' can be used for the arguments of a function. 'a' is the first | |
* argument, 'b' is the second one, and so on. | |
* | |
* example: | |
* ``` | |
* $ ./lang 1 | |
* => 1 | |
* $ ./lang "+ 1 2" | |
* => 3 | |
* $ ./lang "P(+ 1 2)" | |
* 3 | |
* => 3 | |
* $ ./lang "A[* a a]A(10)" | |
* => 0 | |
* => 100 | |
* ``` | |
*/ | |
#include <stdio.h> | |
#include <ctype.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <err.h> | |
#define NALPHA 26 | |
#define MAXFUNCSIZE 1000 | |
static char *code_ptr; | |
struct func { | |
int nargs; | |
char code[MAXFUNCSIZE]; | |
}; | |
static struct func funcs[NALPHA]; | |
struct args { | |
int size; | |
int value[NALPHA]; | |
}; | |
static void define_func(char name) | |
{ | |
struct func *f = &funcs[name - 'A']; | |
int i; | |
char last_arg = 'a' - 1; | |
for (i = 0; *code_ptr != ']' && i < MAXFUNCSIZE - 1; code_ptr++, i++) { | |
f->code[i] = *code_ptr; | |
if (*code_ptr > last_arg) | |
last_arg = *code_ptr; | |
} | |
if (i == MAXFUNCSIZE - 1) { | |
fprintf(stderr, "%c: function size is too large\n", name); | |
exit(EXIT_FAILURE); | |
} | |
f->nargs = last_arg - 'a' + 1; | |
code_ptr++; | |
f->code[i] = '\0'; | |
} | |
static void skip_spaces(void) | |
{ | |
while (isspace(*code_ptr)) | |
code_ptr++; | |
} | |
static void expect(char c) | |
{ | |
if (*code_ptr != c) | |
err(EXIT_FAILURE, "%c expected: %c", *code_ptr, c); | |
code_ptr++; | |
} | |
static int eval(struct args *args); | |
static int eval_func(char name, struct args *args) | |
{ | |
struct func *f = &funcs[name - 'A']; | |
if (f->nargs != args->size) { | |
fprintf(stderr, "%c takes %d arguments, but %d arguments are passed\n", | |
name, f->nargs, args->size); | |
} | |
char *orig = code_ptr; | |
code_ptr = f->code; | |
int val; | |
while (*code_ptr) | |
val = eval(args); | |
code_ptr = orig; | |
return val; | |
} | |
static int eval(struct args *args) | |
{ | |
skip_spaces(); | |
if (islower(*code_ptr)) { | |
// Function arguments | |
if ((*code_ptr - 'a') >= args->size) { | |
fprintf(stderr, "argument %c is not defined\n", *code_ptr); | |
exit(EXIT_FAILURE); | |
} | |
return args->value[*code_ptr++ - 'a']; | |
} else if (isupper(*code_ptr)) { | |
if (code_ptr[1] == '[') { | |
// Function definition | |
char funcname = *code_ptr; | |
if (funcname == 'P') { | |
fprintf(stderr, "P is reserved for print function"); | |
exit(EXIT_FAILURE); | |
} | |
code_ptr += 2; | |
if (funcs[funcname - 'A'].code[0] != '\0') { | |
fprintf(stderr, "function %c is already defined\n", funcname); | |
exit(EXIT_FAILURE); | |
} | |
define_func(funcname); | |
return 0; | |
} else if (code_ptr[1] == '(') { | |
// Function application | |
char funcname = *code_ptr; | |
if (funcname == 'P') { | |
// "P" print primitive | |
code_ptr++; | |
expect('('); | |
int val = eval(args); | |
expect(')'); | |
printf("%d\n", val); | |
return val; | |
} else { | |
struct args newargs; | |
code_ptr += 2; | |
if (funcs[funcname - 'A'].code[0] == '\0') { | |
fprintf(stderr, "undefined function %c\n", funcname); | |
exit(EXIT_FAILURE); | |
} | |
int i = 0; | |
for (skip_spaces(); *code_ptr != ')'; skip_spaces()) | |
newargs.value[i++] = eval(args); | |
newargs.size = i; | |
expect(')'); | |
return eval_func(funcname, &newargs); | |
} | |
} | |
} else if (isdigit(*code_ptr)) { | |
// literal numbers | |
int val = *code_ptr++ - '0'; | |
while (isdigit(*code_ptr)) | |
val = val * 10 + (*code_ptr++ - '0'); | |
return val; | |
} else if (*code_ptr != '\0' && strchr("+-*/", *code_ptr)) { | |
// Arithmetic operators | |
int op = *code_ptr++; | |
int x = eval(args); | |
int y = eval(args); | |
switch(op) { | |
case '+': return x + y; | |
case '-': return x - y; | |
case '*': return x * y; | |
case '/': return x / y; | |
} | |
} | |
// shouldn't reach here | |
fprintf(stderr, "invalid character: %c\n", *code_ptr); | |
exit(EXIT_FAILURE); | |
} | |
static char *progname; | |
int main(int argc, char *argv[]) | |
{ | |
progname = argv[0]; | |
struct args toplevel_args = { | |
.size = 0, | |
}; | |
if (argc < 2) { | |
fprintf(stderr, "usage: %s <code>\n", progname); | |
exit(EXIT_FAILURE); | |
} | |
code_ptr = argv[1]; | |
while (*code_ptr) | |
printf("=> %d\n", eval(&toplevel_args)); | |
exit(EXIT_SUCCESS); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment