Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
/*
* 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