codepad
[
create a new paste
]
login
|
about
Language:
C
C++
D
Haskell
Lua
OCaml
PHP
Perl
Plain Text
Python
Ruby
Scheme
Tcl
/* * ===================================================================================== * * Filename: nscript.c * * Description: A simple stack-based scriping language. * * Created: 10/23/2009 10:47:52 PM * Compiler: gcc * * Author: Nikhilesh S (nikki) * * ===================================================================================== */ #include <stdio.h> #include <ctype.h> #include <stdlib.h> #include <string.h> /* * The implementation is very simple. Check out ns_print, ns_add etc. for the C functions * that get called on 'print', '+', etc. It does need better error handling (right now, * for errors, just prints an error message and exits). */ /* * Syntax: * * Literals: * A number pushes an integer constant onto the stack. * * A string delimited by 's pushes a string onto the stack. * * Blocks: * { ... } pushes a block of code with the code between the "{" and "}" onto the stack, * without executing it. * * &<name> would push in the block of code referenced by '<name>' without executing it. * So, * * 1 2 3 4 &add 3 repeat print * * prints the sum of 1, 2, 3, 4. You can also use '&+' instead of '&add'. * * Variables: * $<name> sets a variable <name> to the last value on the stack. Can also assign blocks, * so, * * { &print rot repeat } $repeatPrint * * would create a new executable 'repeatPrint' that calls 'print' the number of times given * by the last value on the stack. Then, * * 3 repeatPrint * * Would print the last 3 values on the stack (and pop them too). * * Comments: * Comments start with '#' and continue till the end of the line (like '//' in C++). * * 'Pre-defined's: * Here are some things that come with the language: * * Executables: * print: Prints the last value on the stack. * * add: Pops of the last two values on the stack and pushes their sum. * * exit: Exits with an exit code equal to the last value on the stack. * * repeat: Repeats the executable block at 2 on the stack, the number of times given * by the last value of the stack. * * dup: Pushes the last value onto the stack again, without popping it. * * rot: Swaps the positions of last two elements on stack. * * if: Executes the executable block at 2 on the stack, if the boolean at 1 on the * stack is true. * * ifelse: Executes the executable block at 2 on the stack, if the boolean at 1 on the * stack is true, else executes the block at 3. * * equals: Pops of the last two values on the stack, and pushes whether they're equal. * * printStack: Dumps all the values of the stack to standard output, but without popping * them * * at: Pushes onto the stack, the object at position on the stack given by last value * on stack (ignoring the last value). So, * * 1 2 3 at 1 print * * Would print '2'. * * Constants: * true: A 'true' boolean value. * * false: A 'false' boolean value. * * Operators: * +: Like executable 'add'. * * ==: Like executable 'equals'. * */ #define MAX_VARNAME_LENGTH 32 #define MAX_STR_LENGTH 1024 #define MAX_BLOCK_LENGTH 1024 /* * Shows an error message. */ #define ns_error(fmt, ...) \ do \ { \ printf(fmt, ##__VA_ARGS__); \ putchar('\n'); \ exit(1); \ } while (0) /* * Variable/constant types. */ enum { TY_EMPTY, TY_BOOL, TY_INT, TY_STR, TY_FUNC, TY_BLOCK }; /* * Represents a script object of any type. */ struct ns_obj { short type; union { int bo; //TY_BOOL int i; //TY_INT char s[MAX_STR_LENGTH]; //TY_STR void (*f) (); //TY_FUNC char b[MAX_BLOCK_LENGTH]; //TY_BLOCK } u; }; /* * The stack. */ struct ns_stack { struct ns_obj obj; struct ns_stack *next; } *ns_stack; int ns_stackSize; /* * Executes an object. */ void ns_execute(struct ns_obj obj); /* * Return and remove the last added object from the stack. */ struct ns_obj ns_pop() { if (ns_stackSize <= 0) ns_error("pop: Nothing on stack!"); struct ns_obj top = ns_stack->obj; struct ns_stack *next = ns_stack->next; free(ns_stack); ns_stack = next; --ns_stackSize; return top; } /* * Add an object to the stack. */ void ns_push(struct ns_obj obj) { struct ns_stack *new = (struct ns_stack *) malloc(sizeof(struct ns_stack)); new->obj = obj; new->next = ns_stack; ns_stack = new; ++ns_stackSize; } /* * Print an object on the stack to standard output. */ void ns_print() { struct ns_obj obj = ns_pop(); switch (obj.type) { case TY_BOOL: if (obj.u.bo) printf("true"); else printf("false"); break; case TY_INT: printf("%d", obj.u.i); break; case TY_STR: printf("%s", obj.u.s); break; case TY_FUNC: printf("A function object"); break; case TY_BLOCK: printf("{%s}", obj.u.b); break; } } /* * Add two integers */ void ns_add() { struct ns_obj a = ns_pop(); if (a.type != TY_INT || ns_stack->obj.type != TY_INT) ns_error("add: Attempted to add non-integers!"); ns_stack->obj.u.i += a.u.i; } /* * Exit the program. */ void ns_exit() { struct ns_obj code = ns_pop(); if (code.type != TY_INT) ns_error("exit: Need an integer exit code!"); exit(code.u.i); } /* * Repeat an executable a given number of times. */ void ns_repeat() { struct ns_obj num = ns_pop(); struct ns_obj func = ns_pop(); if (num.type != TY_INT) ns_error("repeat: Need an integer repeat count!"); if (func.type != TY_FUNC && func.type != TY_BLOCK) ns_error("repeat: Need an executable to run!"); while (num.u.i--) ns_execute(func); } /* * Push the last object onto the stack again. */ void ns_dup() { ns_push(ns_stack->obj); } /* * Swap positions of last two objects on stack. */ void ns_rot() { struct ns_stack *tmp1 = ns_stack->next; struct ns_stack *tmp2 = ns_stack; tmp2->next = tmp1->next;; ns_stack = tmp1; ns_stack->next = tmp2; } /* * Execute a block/function if a condition is matched. */ void ns_if() { struct ns_obj cond = ns_pop(); struct ns_obj code = ns_pop(); if (cond.type != TY_BOOL) ns_error("if: Need boolean for condition!"); if (code.type != TY_FUNC && code.type != TY_BLOCK) ns_error("if: Need an executable to run!"); if (cond.u.bo) ns_execute(code); } /* * Execute a block/function if a condition is matched, else execute an * 'else' block. */ void ns_ifelse() { struct ns_obj cond = ns_pop(); struct ns_obj code1 = ns_pop(); struct ns_obj code2 = ns_pop(); if (cond.type != TY_BOOL) ns_error("ifelse: Need boolean for condition!"); if (code1.type != TY_FUNC && code1.type != TY_BLOCK) ns_error("ifelse: Need an executable to run!"); if (code2.type != TY_FUNC && code2.type != TY_BLOCK) ns_error("ifelse: Need an executable to run!"); if (cond.u.bo) ns_execute(code1); else ns_execute(code2); } /* * Checks last two objects on stack for equality. */ void ns_equals() { struct ns_obj obj1 = ns_pop(); struct ns_obj obj2 = ns_pop(); struct ns_obj ans; ans.type = TY_BOOL; if (obj1.type != obj2.type) { ans.u.bo = 0; ns_push(ans); } else { switch (obj1.type) { case TY_BOOL: ans.u.bo = obj2.u.bo == obj2.u.bo; break; case TY_INT: ans.u.bo = obj1.u.i == obj2.u.i; ns_push(ans); break; case TY_STR: ans.u.bo = !strcmp(obj1.u.s, obj2.u.s); ns_push(ans); break; case TY_FUNC: ans.u.bo = obj1.u.f == obj2.u.f; ns_push(ans); break; case TY_BLOCK: ans.u.bo = !strcmp(obj1.u.b, obj2.u.b); break; } } } /* * Dumps the stack. */ void ns_printStack() { int num = ns_stackSize; struct ns_stack *curr = ns_stack; while (num--) { printf("%d -> ", ns_stackSize - num - 1); ns_push(curr->obj); ns_print(); printf("\n"); curr = curr->next; } printf("--"); } /* * Pushes object at position on stack. */ void ns_at() { struct ns_obj pos = ns_pop(); if (pos.type != TY_INT) ns_error("at: Need integer position!"); if (pos.u.i >= ns_stackSize) ns_error("at: Position out of bounds!"); struct ns_stack *curr = ns_stack; while (pos.u.i--) curr = curr->next; ns_push(curr->obj); } /* * The name->object mappings. */ struct ns_namemap { char *key; struct ns_obj obj; } ns_funcmap[] = //Functions. { { "print", { TY_FUNC, { .f = ns_print } } }, { "add", { TY_FUNC, { .f = ns_add } } }, { "exit", { TY_FUNC, { .f = ns_exit } } }, { "repeat", { TY_FUNC, { .f = ns_repeat } } }, { "dup", { TY_FUNC, { .f = ns_dup } } }, { "rot", { TY_FUNC, { .f = ns_rot } } }, { "if", { TY_FUNC, { .f = ns_if } } }, { "ifelse", { TY_FUNC, { .f = ns_ifelse } } }, { "equals", { TY_FUNC, { .f = ns_equals } } }, { "printStack", { TY_FUNC, { .f = ns_printStack } } }, { "at", { TY_FUNC, { .f = ns_at } } }, { 0, { TY_EMPTY, { .i = 0 } } } }, ns_operatormap[] = //Operators. { { "+", { TY_FUNC, { .f = ns_add } } }, { "==", { TY_FUNC, { .f = ns_equals } } }, { 0, { TY_EMPTY, { .i = 0 } } } }, ns_constantmap[] = //Constants. { { "true", { TY_BOOL, { .bo = 1 } } }, { "false", { TY_BOOL, { .bo = 0 } } }, { 0, { TY_EMPTY, { .i = 0 } } } }; int ns_isOperatorPrefix(char c) { struct ns_namemap *curr = ns_operatormap; for (; curr->key; ++curr) if (*curr->key == c) return 1; return 0; } struct ns_obj ns_findInNameMap(struct ns_namemap *map, const char *key) { for (; map->key && strcmp(map->key, key); ++map); return map->obj; } struct ns_obj ns_findFunc(const char *key) { return ns_findInNameMap(ns_funcmap, key); } struct ns_obj ns_findOperator(const char *key) { return ns_findInNameMap(ns_operatormap, key); } struct ns_obj ns_findConstant(const char *key) { return ns_findInNameMap(ns_constantmap, key); } /* * The variable list. */ struct ns_variable { char name[MAX_VARNAME_LENGTH]; struct ns_obj obj; struct ns_variable *next; } *ns_variable; struct ns_obj *ns_findVariable(const char *name) { struct ns_variable *curr = ns_variable; for (; curr->next && strcmp(curr->name, name); curr = curr->next); return &(curr->obj); } void ns_addVariable(const char *name, struct ns_obj obj) { struct ns_variable *new = (struct ns_variable *) malloc(sizeof(struct ns_variable)); strcpy(new->name, name); new->obj = obj; new->next = ns_variable; ns_variable = new; } /* * Initialises the interpretter. */ void ns_init() { ns_stack = (struct ns_stack *) malloc(sizeof(struct ns_stack)); ns_stack->next = 0; ns_stack->obj.type = TY_EMPTY; ns_stackSize = 0; ns_variable = (struct ns_variable *) malloc(sizeof(struct ns_variable)); strcpy(ns_variable->name, ""); ns_variable->next = 0; ns_variable->obj.type = TY_EMPTY; } /* * Interprets some code. */ void ns_interpret(const char *code) { const char *curr; char buf[MAX_BLOCK_LENGTH]; char *bufptr; enum { MD_READINT, MD_READSTR, MD_READNAME, MD_READBLOCK, MD_READVARNAME, MD_NONE }; int mode = MD_NONE; int callFunc = 1; int isOperator = 0; int blockDepth = 0; do { curr = code++; switch (mode) { case MD_NONE: //Integer constant. if (isdigit(*curr)) { mode = MD_READINT; struct ns_obj obj = { TY_INT, { .i = 0 } }; ns_push(obj); goto do_int; } //String constant. else if (*curr == '\'') { mode = MD_READSTR; bufptr = buf; *bufptr = 0; break; } //Name, operator. else if (isalpha(*curr) || *curr == '_' || (ns_isOperatorPrefix(*curr) && (isOperator = 1))) { mode = MD_READNAME; bufptr = buf; *bufptr = '\0'; goto do_name; } //Function reference. else if (*curr == '&') { callFunc = 0; mode = MD_READNAME; bufptr = buf; *bufptr = '\0'; if (ns_isOperatorPrefix(*(curr + 1))) isOperator = 1; break; } //Block of code. else if (*curr == '{') { mode = MD_READBLOCK; blockDepth = 1; bufptr = buf; *bufptr = 0; break; } //Comment. else if (*curr == '#') while (*++code && (*code != '\n')); else if (*curr == '$') { mode = MD_READVARNAME; bufptr = buf; *bufptr = '\0'; break; } break; case MD_READINT: do_int: if (isdigit(*curr)) ns_stack->obj.u.i = ns_stack->obj.u.i * 10 + *curr - '0'; else mode = MD_NONE; break; case MD_READSTR: if (*curr != '\'') *bufptr++ = *curr; else { *bufptr = '\0'; struct ns_obj obj; obj.type = TY_STR; strcpy(obj.u.s, buf); ns_push(obj); mode = MD_NONE; } break; case MD_READNAME: do_name: //Operators end on space, names end on non-alphanumeric. if ((isOperator && !isspace(*curr)) || isalnum(*curr) || *curr == '_') *bufptr++ = *curr; else { *bufptr = '\0'; mode = MD_NONE; struct ns_obj obj; obj = ns_findFunc(buf); if (obj.type != TY_EMPTY) { if (callFunc) obj.u.f(); else ns_push(obj); goto finish; } obj = ns_findOperator(buf); if (obj.type != TY_EMPTY) { if (callFunc) obj.u.f(); else ns_push(obj); goto finish; } obj = ns_findConstant(buf); if (obj.type != TY_EMPTY) { ns_push(obj); goto finish; } obj = *ns_findVariable(buf); if (obj.type != TY_EMPTY) { if (callFunc && obj.type == TY_BLOCK) ns_execute(obj); else ns_push(obj); goto finish; } ns_error("Name '%s' not found!", buf); finish: //Reset for next time. callFunc = 1; isOperator = 0; } break; case MD_READBLOCK: if (*curr == '{') ++blockDepth; if (*curr == '}') --blockDepth; if (blockDepth > 0) *bufptr++ = *curr; else { *bufptr = '\0'; struct ns_obj obj; obj.type = TY_BLOCK; strcpy(obj.u.b, buf); ns_push(obj); mode = MD_NONE; } break; case MD_READVARNAME: if (isalnum(*curr) || *curr == '_') *bufptr++ = *curr; else { *bufptr = '\0'; struct ns_obj *obj = ns_findVariable(buf); if (obj->type != TY_EMPTY) *obj = ns_pop(); else ns_addVariable(buf, ns_pop()); mode = MD_NONE; } break; } } while (*curr); } void ns_execute(struct ns_obj obj) { switch (obj.type) { case TY_FUNC: obj.u.f(); break; case TY_BLOCK: ns_interpret(obj.u.b); break; } } int main() { ns_init(); /* * Some example n-script code. */ //* char code[] = " 3 $three #Sets 'three' to 3 \n" " 'three is ' print three print #Prints the value of 'three' \n" " { print ', ' print } $commaPrint #Prints something and adds a comma after it \n" " { 3 + commaPrint } $add3Print #Prints 3 + last thing on stack \n" " ', and 3 plus 3, 2, 1 is ' print \n" " 1 2 3 &add3Print 3 repeat #Repeats 'add3Print' over 3, 2 and 1 \n" " 'lambda test: ' print \n" " 6 5 4 { 2 + print ', ' print } 1 2 add repeat #Repeats a 'lambda' block 1+2 times over 4, 5, 6 \n" " #Print the Fibonacci sequence. \n" " { \n" " 1 at 1 at + #Add last two on stack, but keep them on stack. \n" " dup commaPrint #Print it (duplicate so it's not lost). \n" " { \n" " '(that was three), ' print \n" " } 1 at 3 == if #We print '(that was three)' if the value was 3 (just for fun!) \n" " } $_fib \n" " { \n" " '0, 1, ' print #Print first two terms. \n" " 0 rot 1 rot #Put first two terms, but 'rot' to keep the repeat index last. \n" " &_fib rot #Put reference to _fib, but 'rot' to keep the repeat index last. \n" " repeat #Runs _fib as many times as on the stack before running 'fib'. \n" " } $fib \n" " 'first 12 Fibonacci numbers are: ' print \n" " 10 fib #Now print the first 12 numbers in the sequence. \n" ; ns_interpret(code); // */ /* * An n-script interactive interpretter. */ /* char buf[1024]; for (;;) { printf("> "); fgets(buf, sizeof(buf), stdin); printf("\n"); ns_interpret(buf); printf("\n\n"); } // */ return 0; }
Private
[
?
]
Run code
Submit