# HG changeset patch # User HackBot # Date 1360912688 0 # Node ID bd08a41fcbe28bc586fae318fbfabd634b8c4fd9 # Parent a548af0c699d7d55bcdcb6aeba59c05fefb3ba99 mv fueue.c emmental.hs src diff -r a548af0c699d -r bd08a41fcbe2 emmental.hs --- a/emmental.hs Fri Feb 15 07:17:29 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,431 +0,0 @@ --- --- emmental.hs --- Interpreter for the Emmental Programming Language --- Chris Pressey, Cat's Eye Technologies --- This work is in the public domain. See UNLICENSE for more information. --- --- Ørjan Johansen added main --- - - -import qualified Data.Map as Map -import qualified Data.Char as Char -import System.Environment (getArgs) - -main = do - args <- getArgs - case args of - "-d" : as -> debug =<< parse return as - _ -> emmental =<< parse return args - where - parse :: (String -> IO String) -> [String] -> IO String - parse _ ("-f":as) = parse readFile as - parse _ ("-e":as) = parse return as - parse op (a:as) = op a >>= (`fmap` parse op as) . (++) - parse _ [] = return "" - - ------------------------------------------------------------------------ --- ============================ Symbols ============================ -- ------------------------------------------------------------------------ - -type Symbol = Char - - ------------------------------------------------------------------------ --- ======================== Program States ========================= -- ------------------------------------------------------------------------ - -data State = State [Symbol] [Symbol] - deriving (Ord, Eq, Show) - -pop (State (head:tail) queue) = (head, State tail queue) -push (State list queue) sym = (State (sym:list) queue) - -popString (State (';':tail) queue) = ([], State tail queue) -popString (State (head:tail) queue) = - let - (string, state') = popString (State tail queue) - in - (string ++ [head], state') - -enqueue (State stack queue) symbol = State stack (symbol:queue) -dequeue (State stack queue) = - let - symbol = last queue - queue' = init queue - in - (symbol, State stack queue') - - ------------------------------------------------------------------------ --- ========================= Interpreters ========================== -- ------------------------------------------------------------------------ - -data Interpreter = Interp (Map.Map Symbol Operation) - -fetch (Interp map) sym = Map.findWithDefault (fitRegOp opNop) sym map -supplant (Interp map) sym op = (Interp (Map.insert sym op map)) - - ------------------------------------------------------------------------ --- ========================== Operations =========================== -- ------------------------------------------------------------------------ - -type Operation = State -> Interpreter -> IO (State, Interpreter) - -composeOps :: Operation -> Operation -> Operation - -composeOps op1 op2 = f where - f state interpreter = do - (state', interpreter') <- op1 state interpreter - op2 state' interpreter' - -createOp :: Interpreter -> [Symbol] -> Operation - -createOp interpreter [] = - (fitRegOp opNop) -createOp interpreter (head:tail) = - composeOps (fetch interpreter head) (createOp interpreter tail) - --- --- It's useful for us to express a lot of our operators as non-monadic --- functions that don't affect the interpreter. This is a little "adapter" --- function that lets us create monadic functions with the right signature --- from them. --- - -fitRegOp :: (State -> State) -> Operation - -fitRegOp regop = f where - f state interpreter = - let - state' = regop state - in - do return (state', interpreter) - - ------------------------------------------------------------- ---------------- The operations themselves. ----------------- ------------------------------------------------------------- - --- --- Redefine the meaning of the symbol on the stack with --- a mini-program also popped off the stack. --- - -opSupplant state interpreter = - let - (opSym, state') = pop state - (newOpDefn, state'') = popString state' - newOp = createOp interpreter newOpDefn - in - do return (state'', supplant interpreter opSym newOp) - --- --- Execute the symbol on the stack with the current interpreter. --- - -opEval state interpreter = - let - (opSym, state') = pop state - newOp = createOp interpreter [opSym] - in - newOp state' interpreter - --- --- I/O. --- - -opInput state interpreter = do - symbol <- getChar - do return (push state symbol, interpreter) - -opOutput state interpreter = - let - (symbol, state') = pop state - in do - putChar symbol - return (state', interpreter) - --- --- Primitive arithmetic. --- - -opAdd state = - let - (symA, state') = pop state - (symB, state'') = pop state' - in - push state'' (Char.chr (((Char.ord symB) + (Char.ord symA)) `mod` 256)) - -opSubtract state = - let - (symA, state') = pop state - (symB, state'') = pop state' - in - push state'' (Char.chr (((Char.ord symB) - (Char.ord symA)) `mod` 256)) - -discreteLog 0 = 8 -discreteLog 1 = 0 -discreteLog 2 = 1 -discreteLog n = (discreteLog (n `div` 2)) + 1 - -opDiscreteLog state = - let - (symbol, state') = pop state - in - push state' (Char.chr (discreteLog (Char.ord symbol))) - --- --- Stack manipulation. --- - --- --- Pop the top symbol of the stack, make a copy of it, push it back onto the --- stack, and enqueue the copy onto the queue. --- - -opEnqueueCopy state = - let - (sym, _) = pop state - in - enqueue state sym - --- --- Dequeue a symbol from the queue and push it onto the stack. --- - -opDequeue state = - let - (sym, state') = dequeue state - in - push state' sym - --- --- Duplicate the top symbol of the stack. --- - -opDuplicate state = - let - (symbol, _) = pop state - in - push state symbol - --- --- Miscellaneous operations. --- - -opNop state = - state - --- --- Parameterizable operations. --- - -opPushValue value state = - push state (Char.chr value) - -opAccumValue value state = - let - (sym, state') = pop state - value' = ((Char.ord sym) * 10) + value - in - push state' (Char.chr (value' `mod` 256)) - - ------------------------------------------------------------------------ --- ===================== Debugging Functions ======================= -- ------------------------------------------------------------------------ - -type Debugger = State -> Interpreter -> IO () - -debugNop s i = do - return () - -debugPrintState s i = do - putStr ((show s) ++ "\n") - return () - - ------------------------------------------------------------------------ --- ============================ Executor =========================== -- ------------------------------------------------------------------------ - -execute :: [Symbol] -> State -> Interpreter -> Debugger -> IO (State, Interpreter) - -execute [] state interpreter debugger = - return (state, interpreter) -execute (opSym:program') state interpreter debugger = - let - operation = fetch interpreter opSym - in do - (state', interpreter') <- operation state interpreter - debugger state' interpreter' - execute program' state' interpreter' debugger - - ------------------------------------------------------------------------ --- ====================== Top-Level Function ======================= -- ------------------------------------------------------------------------ - -initialInterpreter = Interp - (Map.fromList - [ - ('.', opOutput), - (',', opInput), - - ('#', fitRegOp (opPushValue 0)), - ('0', fitRegOp (opAccumValue 0)), - ('1', fitRegOp (opAccumValue 1)), - ('2', fitRegOp (opAccumValue 2)), - ('3', fitRegOp (opAccumValue 3)), - ('4', fitRegOp (opAccumValue 4)), - ('5', fitRegOp (opAccumValue 5)), - ('6', fitRegOp (opAccumValue 6)), - ('7', fitRegOp (opAccumValue 7)), - ('8', fitRegOp (opAccumValue 8)), - ('9', fitRegOp (opAccumValue 9)), - - ('+', fitRegOp opAdd), - ('-', fitRegOp opSubtract), - ('~', fitRegOp opDiscreteLog), - - ('^', fitRegOp opEnqueueCopy), - ('v', fitRegOp opDequeue), - (':', fitRegOp opDuplicate), - - ('!', opSupplant), - ('?', opEval), - - (';', fitRegOp (opPushValue (Char.ord ';'))) - ] - ) - -initialState = State [] [] - -emmental string = do - (state, interpreter) <- execute string initialState initialInterpreter debugNop - return state - -debug string = do - (state, interpreter) <- execute string initialState initialInterpreter debugPrintState - return state - - ------------------------------------------------------------------------ --- ========================== Test Cases =========================== -- ------------------------------------------------------------------------ - --- --- Drivers for test cases. 'demo' runs them straight, whereas 'test' --- uses the debugger. --- - -demo n = emmental (testProg n) - -test n = debug (testProg n) - --- --- Here we introduce a bit of a cheat, in order to make writing --- complex Emmental programs tolerable. You can still see the --- programs in their fully glory by executing "show (testProg n)". --- - -quote [] = [] -quote (symbol:rest) = "#" ++ (show (Char.ord symbol)) ++ (quote rest) - --- --- Add one and one. --- - -testProg 1 = "#1#1+" - --- --- Redefine & as "+". --- - -testProg 2 = ";#43#38!#1#1&" -- 59,43,38 ==> ";+&" - --- --- Redefine 0 as "9". --- - -testProg 3 = ";#57#48!#0" -- 59,57,48 ==> ";90" - --- --- Redefine 0 as "#48?". This results in an infinite loop when 0 is executed. --- - -testProg 4 = ";#35#52#56#63#48!0" -- 59,35,52,56,63,48 ==> ";#48?0" - --- --- Redefine $ as ".#36?". This results in a loop that pops symbols and --- and prints them, until the stack underflows, when $ is executed. --- - -testProg 5 = ";#46#35#51#54#63#36! #65#66#67#68#69$" - --- --- Duplicate the top stack element (assuming an empty queue.) --- This shows that the : operation is not strictly necessary --- (when you know the size of the queue.) --- - -testProg 6 = "#65^v" - --- --- Discard the top stack element (assuming more than one element --- on the stack, and an empty queue.) --- - -testProg 7 = "#33#123^v-+" - --- --- Swap the top two elements of the stack (assuming an empty queue.) --- - -testProg 8 = "#67#66#65^v^-+^^v^v^v-+^v-+^v-+vv" - --- --- Input a symbol. Report whether its ASCII value is even or odd. --- - -testProg 9 = (quote ";^v:") ++ "!" ++ -- : = dup - (quote ";#69.") ++ "#!" ++ -- NUL = print "E" - (quote ";#79.") ++ "#128!" ++ -- \128 = print "O" - (quote (";" ++ (take 127 [':',':'..]) ++ -- m = mul by 128 - (take 127 ['+','+'..]) ++ "m")) ++ "!" ++ - ",m?" - --- --- Input a symbol. Report whether it is M or not. --- - -testProg 10 = (quote ";#78.") ++ "#!" ++ -- NUL = print "N" - ";##1!" ++ -- SOH = same as NUL - ";##2!" ++ -- STX = same as NUL - ";##3!" ++ -- ETX = same as NUL - ";##4!" ++ -- EOT = same as NUL - ";##5!" ++ -- ENQ = same as NUL - ";##6!" ++ -- ACK = same as NUL - ";##7!" ++ -- BEL = same as NUL - (quote ";#89.") ++ "#8!" ++ -- BS = print "Y" - ",#77-~?" - --- --- Same as testProg 5, except stop printing when a NUL is --- encountered, instead of just underflowing the stack. --- - -testProg 11 = ";" ++ (quote ":~?$") ++ "!" ++ -- $ = dup & test - ";" ++ (quote ".$") ++ "#!" ++ -- NUL = print & repeat - ";#0#1!" ++ -- SOH = same as NUL - ";#0#2!" ++ -- STX = same as NUL - ";#0#3!" ++ -- ETX = same as NUL - ";#0#4!" ++ -- EOT = same as NUL - ";#0#5!" ++ -- ENQ = same as NUL - ";#0#6!" ++ -- ACK = same as NUL - ";#0#7!" ++ -- BEL = same as NUL - -- BS = stop (nop) - "#0" ++ (quote (reverse "Hello!")) ++ "$" diff -r a548af0c699d -r bd08a41fcbe2 fueue.c --- a/fueue.c Fri Feb 15 07:17:29 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,650 +0,0 @@ -#include -#include -#include -#define TRUE 1 -#define FALSE 0 - -/* - * Fueue Interpreter in language C - * the esoteric programming language Fueue was designed in 2012 by Taneb. - * - * The following program was written by Stephan Kunne on august 29 2012. - * It is public domain; you are free to use it, copy it, distribute it, - * or do whatever you'd like with it. - * Credit and feedback are always appreciated ; you can email me at - * firstname dot name at gmail dot com with all your questions or remarks. - * - * Examples: - * - * ./fueuec '72 101 108 108 111 44 32 119 111 114 108 100 33 10 H' - * Hello, world! - * - * ./fueuec --print '):[):]' - * ):[):] - * :[):]) - * )[):][):] - * [):]): - * ):[):] - * (...and so on) - */ - -enum Typet -{ - NUM, FUN, BLOCK -}; - -typedef struct Queue Queue; -struct Queue -{ - int size; - struct Token *top; - struct Token *bottom; -}; - -union value -{ - int num; - char fun; - Queue block; -}; - -struct Token -{ - enum Typet what; - union value val; - struct Token *next; -}; - - -int is_empty(const Queue *q); // bool -void initQueue(Queue *q); // make it an empty queue -void push(struct Token *x, Queue *q); -struct Token* copyToken(const struct Token *x); -Queue copyQueue(const Queue *q); -void initToken(struct Token *x); -void pushnum(int num, Queue* q); // create NUM token and pushes it -void pushfun(char f, Queue* q); // create FUN token and pushes it -void pushblock(Queue newq, Queue* q); // create BLOCK token and pushes it -void deletetop(Queue* q); // suppose q not empty -void deleteQueue(Queue *q); -void sendback(Queue* q); // suppose q not empty, pop then push -struct Token* pop(Queue* q); // suppose q not empty -void append(Queue *q, const Queue *r); -int matchwhat(const Queue* q, const char s[]); // bool peek at first 2 "nn" "n" "." ".." "n." "b." "b" -void processFueue(Queue* q, int printmode); // the recursive function that does everything -Queue strtoqueue(const char s[], int *k); // transforms a string program into a queue program -void print_queue(const Queue *q); // prints a queue program -void error_empty(const char s[]); // raised by functions that "suppose q not empty" when q is empty - - -void processFueue(Queue* q, int printmode) -{ - int time = 0; // catching input - int i = 0; // to be used for input - int a = 0, b = 0; // to be used for some FUN - struct Token* p = NULL; // even more FUN - Queue newq; // having FUN with BLOCKs (specifically '(') - initQueue(&newq); // this is done at every iteration where newq is used, though - - while (TRUE) // stops thanks to a return; when 'H' is met - { - - if (printmode) - { - print_queue(q); - printf("\n"); - } - - if (time == q->size) // if time == q->size then input char and push ascii/unicode value - { - fflush(stdout); - i = getchar(); - pushnum(i, q); - time = 0; - } - - if (q->top->what == NUM) // q not empty because of the time != q->size requirement - { - // print char with ascii/unicode value q->top->val.num - printf("%c", (char) q->top->val.num); - fflush(stdout); - deletetop(q); - time = 0; - } - else if (q->top->what == FUN) - { - char op = q->top->val.fun; - deletetop(q); - switch (op) - { - case '+': - case '*': - case '/': - if (matchwhat(q, "nn")) - { - a = q->top->val.num; - deletetop(q); - b = q->top->val.num; - deletetop(q); - if (op == '+') - { pushnum(a+b, q); } - else if (op == '*') - { pushnum(a*b, q); } - else // op == '/' - { pushnum(a/b, q); } - time = 0; - } - else - { - pushfun(op, q); - time++; - } - break; - case '-': - case '%': - if (matchwhat(q, "n")) - { - a = q->top->val.num; - deletetop(q); - pushnum( ((op == '-')?(-a):(!a)) , q); - time = 0; - } - else - { - pushfun(op, q); - time++; - } - break; - case ':': - if (!is_empty(q)) - { - push(copyToken(q->top), q); // push copy - sendback(q); // push original - time = 0; - } - else - { - pushfun(op, q); - time++; - } - break; - case '~': - if (matchwhat(q, "..")) // if q has at least two items - { - p = pop(q); - sendback(q); - push(p, q); - time = 0; - } - else - { - pushfun(op, q); - time++; - } - break; - case '!': - if (is_empty(q)) - { - pushfun(op, q); - time++; - } - else - { - deletetop(q); - time = 0; - } - break; - case '$': - if (matchwhat(q, "n.")) - { - a = q->top->val.num; - deletetop(q); - for (; a > 0; a--) - { - push(copyToken(q->top), q); - } - - deletetop(q); - - time = 0; - } - else - { - pushfun(op, q); - time++; - } - break; - case '(': - if (is_empty(q)) - { - pushfun(op, q); - time++; - } - else - { - initQueue(&newq); - push(pop(q), &newq); - pushblock(newq, q); - time = 0; - // newq is the queue inside the block - } - break; - case '<': - if (matchwhat(q, "b.")) - { - sendback(q); - push(pop(q), &(q->bottom->val.block)); - time = 0; - } - else - { - pushfun(op, q); - time++; - } - break; - case ')': - if (matchwhat(q, "b")) - { - append(q, &(q->top->val.block)); - initQueue(&(q->top->val.block)); // mandatory since - deletetop(q); // deletetop does destroy the block it contains - time = 0; - } - else - { - pushfun(op, q); - time++; - } - break; - case 'H': - // don't forget to delete the remaining of the queue here, will you? - return; - default: - // raise an error - break; - } - } - else // if q->top->what == BLOCK - { - sendback(q); - time++; - } - } -} - - - -int main(int argc, char *argv[]) -{ - Queue q; - initQueue(&q); // q is empty now - char s[10000] = "72 101 108 108 111 44 32 119 111 114 108 100 33 10 H"; - int printmode = FALSE; // a debug mode that will print the fueue program at each step - int k = 0; - - switch (argc) - { - case 1: - break; - case 2: - if (strcmp(argv[1], "--print") == 0) - { - printmode = TRUE; - } - else - { - strncpy(s, argv[1], 10000); - } - break; - case 3: - strncpy(s, argv[2], 10000); - printmode = (strcmp(argv[1], "--print") == 0); - break; - default: - fprintf(stderr, "Error: %s received too many arguments. The Hello world program\n", argv[0]); - break; - } - - q = strtoqueue(s, &k); - - processFueue(&q, printmode); - return 0; -} - - -Queue strtoqueue(const char s[], int *k) // takes a fueue program as a string, and gives a queue -{ - // *k is loop counter - Queue q; // the queue to be returned - initQueue(&q); - int n = 0; // decimals (usually n * 10 + 0-9) - int intmode = FALSE; // bool "we're reading a number right now" - - if (*k == -1) - { - printf("FUEUE: UNMATCHED OPENING SQUARE BRACKET PROBABLY FORGOT A CLOSING SQUARE BRACKET\n"); - return q; - } - - while (s[*k] != '\0' && s[*k] != ']') - { - if (intmode && (s[*k] > '9' || s[*k] < '0')) // if intmode ends - { - pushnum(n, &q); - n = 0; - intmode = FALSE; - } - - switch (s[*k]) - { - case '+': - case '-': - case '*': - case '/': - case '%': - case ':': - case '~': - case '!': - case '$': - case '(': - case '<': - case ')': - case 'H': - pushfun(s[*k], &q); - (*k)++; - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - n = n * 10 + (int) (s[*k] - '0'); - intmode = TRUE; - (*k)++; - break; - case '\n': - case '\t': - case ' ': // whitespace - (*k)++; - break; - case '[': - (*k)++; - pushblock(strtoqueue(s, k), &q); - (*k)++; - break; - default: - printf("FUEUE: UNKNOWN %c OP\n", s[*k]); - (*k)++; - break; - } - } - if (intmode) - { - pushnum(n, &q); - } - - if (s[*k] == '\0') - *k = -1; - return q; -} - -void print_queue(const Queue *q) -{ - struct Token *ptmp = q->top; - while (ptmp != NULL) - { - if (ptmp->what == NUM) - { - printf(" %d", ptmp->val.num); - } - else if (ptmp->what == FUN) - { - printf("%c", ptmp->val.fun); - } - else if (ptmp->what == BLOCK) - { - printf("["); - print_queue(&(ptmp->val.block)); - printf("]"); - } - else - { - printf("That's impossible...Neither num nor fun nor block...\n"); - } - ptmp = ptmp->next; - } -} - - - - -int is_empty(const Queue *q) // bool -{ - if (q->top == NULL) - { - if (q->bottom == NULL && q->size == 0) - { - return TRUE; - } - else - { - error_empty("is_empty"); - } - } - return FALSE; -} - -void initQueue(Queue *q) -{ - q->size = 0; - q->top = NULL; - q->bottom = NULL; -} - -void push(struct Token *x, Queue *q) -{ - if (is_empty(q)) - { - q->top = x; - } - else - { - q->bottom->next = x; - } - q->bottom = x; - q->size++; - x->next = NULL; // just in case -} - -Queue copyQueue(const Queue *q) -{ - Queue c; - struct Token* ptmp = q->top; - initQueue(&c); - - while (ptmp != NULL) - { - push(copyToken(ptmp), &c); - ptmp = ptmp->next; - } - - return c; -} - -void initToken(struct Token *x) -{ - x->what = NUM; - x->val.num = 0; - x->val.fun = '\0'; - initQueue(&(x->val.block)); - x->next = NULL; -} - -struct Token* copyToken(const struct Token *x) -{ - struct Token *c = malloc(sizeof(struct Token)); - c->what = x->what; - - switch (x->what) - { - case NUM: - case FUN: - c->val = x->val; - break; - case BLOCK: - c->val.block = copyQueue(&(x->val.block)); - break; - default: - fprintf(stderr, "Error: found a %d in my soup\n", x->what); - break; - } - - c->next = NULL; - return c; -} - -void pushnum(int num, Queue* q) -{ - struct Token *t = malloc(sizeof(struct Token)); - initToken(t); - t->what = NUM; - t->val.num = num; - - push(t, q); -} - -void pushfun(char f, Queue* q) -{ - struct Token *t = malloc(sizeof(struct Token)); - initToken(t); - t->what = FUN; - t->val.fun = f; - push(t, q); -} - -void pushblock(Queue newq, Queue* q) -{ - struct Token *t = malloc(sizeof(struct Token)); - initToken(t); - t->what = BLOCK; - t->val.block = newq; - push(t, q); -} - -void deletetop(Queue* q) // suppose q not empty -{ - if (is_empty(q)) - error_empty("deletetop"); - - struct Token *todelete = NULL; - - if (q->top->what == BLOCK) // has to free the Queue inside - { - deleteQueue(&(q->top->val.block)); - } - - if (q->top->next == NULL) - { - free(q->top); - q->top = NULL; - q->bottom = NULL; - } - else - { - todelete = q->top; - q->top = q->top->next; - free(todelete); - } - q->size--; -} - -void deleteQueue(Queue *q) -{ - while (!is_empty(q)) - { - deletetop(q); - } -} - -void sendback(Queue* q) // suppose q not empty, pop then push -{ - if (is_empty(q)) - error_empty("sendback"); - - q->bottom->next = q->top; - q->top = q->top->next; - q->bottom = q->bottom->next; - q->bottom->next = NULL; -} - -struct Token* pop(Queue* q) // suppose q not empty -{ - if (is_empty(q)) - error_empty("sendback"); - - struct Token* t = q->top; // note that t->next is equal to q->top->next now - q->top = q->top->next; - q->size--; - - return t; -} - -void append(Queue *q, const Queue *r) -{ - if (is_empty(q)) - { - q->top = r->top; - q->bottom = r->bottom; - } - else if (!is_empty(r)) - { - q->bottom->next = r->top; - q->bottom = r->bottom; - } - q->size += r->size; -} - -int matchwhat(const Queue* q, const char s[]) // bool "nn" "n" "." ".." "n." "b." "b" -{ - int itsok = TRUE; - if ((s[0] != '\0') && !(is_empty(q))) // if neither s nor q is empty - { - if (s[0] == 'n' && q->top->what != NUM) // if top should be num - itsok = FALSE; - if (s[0] == 'b' && q->top->what != BLOCK) // if top should be block - itsok = FALSE; - if (s[1] != '\0' && q->top->next == NULL) // if should have second element - { - itsok = FALSE; - } - else // so it indeed has a second element, or it doesn't need to have one - { - if (s[1] == 'n' && q->top->next->what != NUM) // second should be num - itsok = FALSE; - if (s[1] == 'b' && q->top->next->what != BLOCK) // second should be block - itsok = FALSE; - } - } - else - { - if (s[0] != '\0') // if s is not empty but q is - itsok = FALSE; - } - - // printf("matchwhat: %s\n", (itsok?"TRUE":"FALSE")); - return itsok; -} - -void error_empty(const char s[]) -{ - fprintf(stderr, "Error: queue was empty in %s\n", s); - exit(EXIT_FAILURE); -} - - - diff -r a548af0c699d -r bd08a41fcbe2 src/emmental.hs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/emmental.hs Fri Feb 15 07:18:08 2013 +0000 @@ -0,0 +1,431 @@ +-- +-- emmental.hs +-- Interpreter for the Emmental Programming Language +-- Chris Pressey, Cat's Eye Technologies +-- This work is in the public domain. See UNLICENSE for more information. +-- +-- Ørjan Johansen added main +-- + + +import qualified Data.Map as Map +import qualified Data.Char as Char +import System.Environment (getArgs) + +main = do + args <- getArgs + case args of + "-d" : as -> debug =<< parse return as + _ -> emmental =<< parse return args + where + parse :: (String -> IO String) -> [String] -> IO String + parse _ ("-f":as) = parse readFile as + parse _ ("-e":as) = parse return as + parse op (a:as) = op a >>= (`fmap` parse op as) . (++) + parse _ [] = return "" + + +----------------------------------------------------------------------- +-- ============================ Symbols ============================ -- +----------------------------------------------------------------------- + +type Symbol = Char + + +----------------------------------------------------------------------- +-- ======================== Program States ========================= -- +----------------------------------------------------------------------- + +data State = State [Symbol] [Symbol] + deriving (Ord, Eq, Show) + +pop (State (head:tail) queue) = (head, State tail queue) +push (State list queue) sym = (State (sym:list) queue) + +popString (State (';':tail) queue) = ([], State tail queue) +popString (State (head:tail) queue) = + let + (string, state') = popString (State tail queue) + in + (string ++ [head], state') + +enqueue (State stack queue) symbol = State stack (symbol:queue) +dequeue (State stack queue) = + let + symbol = last queue + queue' = init queue + in + (symbol, State stack queue') + + +----------------------------------------------------------------------- +-- ========================= Interpreters ========================== -- +----------------------------------------------------------------------- + +data Interpreter = Interp (Map.Map Symbol Operation) + +fetch (Interp map) sym = Map.findWithDefault (fitRegOp opNop) sym map +supplant (Interp map) sym op = (Interp (Map.insert sym op map)) + + +----------------------------------------------------------------------- +-- ========================== Operations =========================== -- +----------------------------------------------------------------------- + +type Operation = State -> Interpreter -> IO (State, Interpreter) + +composeOps :: Operation -> Operation -> Operation + +composeOps op1 op2 = f where + f state interpreter = do + (state', interpreter') <- op1 state interpreter + op2 state' interpreter' + +createOp :: Interpreter -> [Symbol] -> Operation + +createOp interpreter [] = + (fitRegOp opNop) +createOp interpreter (head:tail) = + composeOps (fetch interpreter head) (createOp interpreter tail) + +-- +-- It's useful for us to express a lot of our operators as non-monadic +-- functions that don't affect the interpreter. This is a little "adapter" +-- function that lets us create monadic functions with the right signature +-- from them. +-- + +fitRegOp :: (State -> State) -> Operation + +fitRegOp regop = f where + f state interpreter = + let + state' = regop state + in + do return (state', interpreter) + + +------------------------------------------------------------ +--------------- The operations themselves. ----------------- +------------------------------------------------------------ + +-- +-- Redefine the meaning of the symbol on the stack with +-- a mini-program also popped off the stack. +-- + +opSupplant state interpreter = + let + (opSym, state') = pop state + (newOpDefn, state'') = popString state' + newOp = createOp interpreter newOpDefn + in + do return (state'', supplant interpreter opSym newOp) + +-- +-- Execute the symbol on the stack with the current interpreter. +-- + +opEval state interpreter = + let + (opSym, state') = pop state + newOp = createOp interpreter [opSym] + in + newOp state' interpreter + +-- +-- I/O. +-- + +opInput state interpreter = do + symbol <- getChar + do return (push state symbol, interpreter) + +opOutput state interpreter = + let + (symbol, state') = pop state + in do + putChar symbol + return (state', interpreter) + +-- +-- Primitive arithmetic. +-- + +opAdd state = + let + (symA, state') = pop state + (symB, state'') = pop state' + in + push state'' (Char.chr (((Char.ord symB) + (Char.ord symA)) `mod` 256)) + +opSubtract state = + let + (symA, state') = pop state + (symB, state'') = pop state' + in + push state'' (Char.chr (((Char.ord symB) - (Char.ord symA)) `mod` 256)) + +discreteLog 0 = 8 +discreteLog 1 = 0 +discreteLog 2 = 1 +discreteLog n = (discreteLog (n `div` 2)) + 1 + +opDiscreteLog state = + let + (symbol, state') = pop state + in + push state' (Char.chr (discreteLog (Char.ord symbol))) + +-- +-- Stack manipulation. +-- + +-- +-- Pop the top symbol of the stack, make a copy of it, push it back onto the +-- stack, and enqueue the copy onto the queue. +-- + +opEnqueueCopy state = + let + (sym, _) = pop state + in + enqueue state sym + +-- +-- Dequeue a symbol from the queue and push it onto the stack. +-- + +opDequeue state = + let + (sym, state') = dequeue state + in + push state' sym + +-- +-- Duplicate the top symbol of the stack. +-- + +opDuplicate state = + let + (symbol, _) = pop state + in + push state symbol + +-- +-- Miscellaneous operations. +-- + +opNop state = + state + +-- +-- Parameterizable operations. +-- + +opPushValue value state = + push state (Char.chr value) + +opAccumValue value state = + let + (sym, state') = pop state + value' = ((Char.ord sym) * 10) + value + in + push state' (Char.chr (value' `mod` 256)) + + +----------------------------------------------------------------------- +-- ===================== Debugging Functions ======================= -- +----------------------------------------------------------------------- + +type Debugger = State -> Interpreter -> IO () + +debugNop s i = do + return () + +debugPrintState s i = do + putStr ((show s) ++ "\n") + return () + + +----------------------------------------------------------------------- +-- ============================ Executor =========================== -- +----------------------------------------------------------------------- + +execute :: [Symbol] -> State -> Interpreter -> Debugger -> IO (State, Interpreter) + +execute [] state interpreter debugger = + return (state, interpreter) +execute (opSym:program') state interpreter debugger = + let + operation = fetch interpreter opSym + in do + (state', interpreter') <- operation state interpreter + debugger state' interpreter' + execute program' state' interpreter' debugger + + +----------------------------------------------------------------------- +-- ====================== Top-Level Function ======================= -- +----------------------------------------------------------------------- + +initialInterpreter = Interp + (Map.fromList + [ + ('.', opOutput), + (',', opInput), + + ('#', fitRegOp (opPushValue 0)), + ('0', fitRegOp (opAccumValue 0)), + ('1', fitRegOp (opAccumValue 1)), + ('2', fitRegOp (opAccumValue 2)), + ('3', fitRegOp (opAccumValue 3)), + ('4', fitRegOp (opAccumValue 4)), + ('5', fitRegOp (opAccumValue 5)), + ('6', fitRegOp (opAccumValue 6)), + ('7', fitRegOp (opAccumValue 7)), + ('8', fitRegOp (opAccumValue 8)), + ('9', fitRegOp (opAccumValue 9)), + + ('+', fitRegOp opAdd), + ('-', fitRegOp opSubtract), + ('~', fitRegOp opDiscreteLog), + + ('^', fitRegOp opEnqueueCopy), + ('v', fitRegOp opDequeue), + (':', fitRegOp opDuplicate), + + ('!', opSupplant), + ('?', opEval), + + (';', fitRegOp (opPushValue (Char.ord ';'))) + ] + ) + +initialState = State [] [] + +emmental string = do + (state, interpreter) <- execute string initialState initialInterpreter debugNop + return state + +debug string = do + (state, interpreter) <- execute string initialState initialInterpreter debugPrintState + return state + + +----------------------------------------------------------------------- +-- ========================== Test Cases =========================== -- +----------------------------------------------------------------------- + +-- +-- Drivers for test cases. 'demo' runs them straight, whereas 'test' +-- uses the debugger. +-- + +demo n = emmental (testProg n) + +test n = debug (testProg n) + +-- +-- Here we introduce a bit of a cheat, in order to make writing +-- complex Emmental programs tolerable. You can still see the +-- programs in their fully glory by executing "show (testProg n)". +-- + +quote [] = [] +quote (symbol:rest) = "#" ++ (show (Char.ord symbol)) ++ (quote rest) + +-- +-- Add one and one. +-- + +testProg 1 = "#1#1+" + +-- +-- Redefine & as "+". +-- + +testProg 2 = ";#43#38!#1#1&" -- 59,43,38 ==> ";+&" + +-- +-- Redefine 0 as "9". +-- + +testProg 3 = ";#57#48!#0" -- 59,57,48 ==> ";90" + +-- +-- Redefine 0 as "#48?". This results in an infinite loop when 0 is executed. +-- + +testProg 4 = ";#35#52#56#63#48!0" -- 59,35,52,56,63,48 ==> ";#48?0" + +-- +-- Redefine $ as ".#36?". This results in a loop that pops symbols and +-- and prints them, until the stack underflows, when $ is executed. +-- + +testProg 5 = ";#46#35#51#54#63#36! #65#66#67#68#69$" + +-- +-- Duplicate the top stack element (assuming an empty queue.) +-- This shows that the : operation is not strictly necessary +-- (when you know the size of the queue.) +-- + +testProg 6 = "#65^v" + +-- +-- Discard the top stack element (assuming more than one element +-- on the stack, and an empty queue.) +-- + +testProg 7 = "#33#123^v-+" + +-- +-- Swap the top two elements of the stack (assuming an empty queue.) +-- + +testProg 8 = "#67#66#65^v^-+^^v^v^v-+^v-+^v-+vv" + +-- +-- Input a symbol. Report whether its ASCII value is even or odd. +-- + +testProg 9 = (quote ";^v:") ++ "!" ++ -- : = dup + (quote ";#69.") ++ "#!" ++ -- NUL = print "E" + (quote ";#79.") ++ "#128!" ++ -- \128 = print "O" + (quote (";" ++ (take 127 [':',':'..]) ++ -- m = mul by 128 + (take 127 ['+','+'..]) ++ "m")) ++ "!" ++ + ",m?" + +-- +-- Input a symbol. Report whether it is M or not. +-- + +testProg 10 = (quote ";#78.") ++ "#!" ++ -- NUL = print "N" + ";##1!" ++ -- SOH = same as NUL + ";##2!" ++ -- STX = same as NUL + ";##3!" ++ -- ETX = same as NUL + ";##4!" ++ -- EOT = same as NUL + ";##5!" ++ -- ENQ = same as NUL + ";##6!" ++ -- ACK = same as NUL + ";##7!" ++ -- BEL = same as NUL + (quote ";#89.") ++ "#8!" ++ -- BS = print "Y" + ",#77-~?" + +-- +-- Same as testProg 5, except stop printing when a NUL is +-- encountered, instead of just underflowing the stack. +-- + +testProg 11 = ";" ++ (quote ":~?$") ++ "!" ++ -- $ = dup & test + ";" ++ (quote ".$") ++ "#!" ++ -- NUL = print & repeat + ";#0#1!" ++ -- SOH = same as NUL + ";#0#2!" ++ -- STX = same as NUL + ";#0#3!" ++ -- ETX = same as NUL + ";#0#4!" ++ -- EOT = same as NUL + ";#0#5!" ++ -- ENQ = same as NUL + ";#0#6!" ++ -- ACK = same as NUL + ";#0#7!" ++ -- BEL = same as NUL + -- BS = stop (nop) + "#0" ++ (quote (reverse "Hello!")) ++ "$" diff -r a548af0c699d -r bd08a41fcbe2 src/fueue.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/fueue.c Fri Feb 15 07:18:08 2013 +0000 @@ -0,0 +1,650 @@ +#include +#include +#include +#define TRUE 1 +#define FALSE 0 + +/* + * Fueue Interpreter in language C + * the esoteric programming language Fueue was designed in 2012 by Taneb. + * + * The following program was written by Stephan Kunne on august 29 2012. + * It is public domain; you are free to use it, copy it, distribute it, + * or do whatever you'd like with it. + * Credit and feedback are always appreciated ; you can email me at + * firstname dot name at gmail dot com with all your questions or remarks. + * + * Examples: + * + * ./fueuec '72 101 108 108 111 44 32 119 111 114 108 100 33 10 H' + * Hello, world! + * + * ./fueuec --print '):[):]' + * ):[):] + * :[):]) + * )[):][):] + * [):]): + * ):[):] + * (...and so on) + */ + +enum Typet +{ + NUM, FUN, BLOCK +}; + +typedef struct Queue Queue; +struct Queue +{ + int size; + struct Token *top; + struct Token *bottom; +}; + +union value +{ + int num; + char fun; + Queue block; +}; + +struct Token +{ + enum Typet what; + union value val; + struct Token *next; +}; + + +int is_empty(const Queue *q); // bool +void initQueue(Queue *q); // make it an empty queue +void push(struct Token *x, Queue *q); +struct Token* copyToken(const struct Token *x); +Queue copyQueue(const Queue *q); +void initToken(struct Token *x); +void pushnum(int num, Queue* q); // create NUM token and pushes it +void pushfun(char f, Queue* q); // create FUN token and pushes it +void pushblock(Queue newq, Queue* q); // create BLOCK token and pushes it +void deletetop(Queue* q); // suppose q not empty +void deleteQueue(Queue *q); +void sendback(Queue* q); // suppose q not empty, pop then push +struct Token* pop(Queue* q); // suppose q not empty +void append(Queue *q, const Queue *r); +int matchwhat(const Queue* q, const char s[]); // bool peek at first 2 "nn" "n" "." ".." "n." "b." "b" +void processFueue(Queue* q, int printmode); // the recursive function that does everything +Queue strtoqueue(const char s[], int *k); // transforms a string program into a queue program +void print_queue(const Queue *q); // prints a queue program +void error_empty(const char s[]); // raised by functions that "suppose q not empty" when q is empty + + +void processFueue(Queue* q, int printmode) +{ + int time = 0; // catching input + int i = 0; // to be used for input + int a = 0, b = 0; // to be used for some FUN + struct Token* p = NULL; // even more FUN + Queue newq; // having FUN with BLOCKs (specifically '(') + initQueue(&newq); // this is done at every iteration where newq is used, though + + while (TRUE) // stops thanks to a return; when 'H' is met + { + + if (printmode) + { + print_queue(q); + printf("\n"); + } + + if (time == q->size) // if time == q->size then input char and push ascii/unicode value + { + fflush(stdout); + i = getchar(); + pushnum(i, q); + time = 0; + } + + if (q->top->what == NUM) // q not empty because of the time != q->size requirement + { + // print char with ascii/unicode value q->top->val.num + printf("%c", (char) q->top->val.num); + fflush(stdout); + deletetop(q); + time = 0; + } + else if (q->top->what == FUN) + { + char op = q->top->val.fun; + deletetop(q); + switch (op) + { + case '+': + case '*': + case '/': + if (matchwhat(q, "nn")) + { + a = q->top->val.num; + deletetop(q); + b = q->top->val.num; + deletetop(q); + if (op == '+') + { pushnum(a+b, q); } + else if (op == '*') + { pushnum(a*b, q); } + else // op == '/' + { pushnum(a/b, q); } + time = 0; + } + else + { + pushfun(op, q); + time++; + } + break; + case '-': + case '%': + if (matchwhat(q, "n")) + { + a = q->top->val.num; + deletetop(q); + pushnum( ((op == '-')?(-a):(!a)) , q); + time = 0; + } + else + { + pushfun(op, q); + time++; + } + break; + case ':': + if (!is_empty(q)) + { + push(copyToken(q->top), q); // push copy + sendback(q); // push original + time = 0; + } + else + { + pushfun(op, q); + time++; + } + break; + case '~': + if (matchwhat(q, "..")) // if q has at least two items + { + p = pop(q); + sendback(q); + push(p, q); + time = 0; + } + else + { + pushfun(op, q); + time++; + } + break; + case '!': + if (is_empty(q)) + { + pushfun(op, q); + time++; + } + else + { + deletetop(q); + time = 0; + } + break; + case '$': + if (matchwhat(q, "n.")) + { + a = q->top->val.num; + deletetop(q); + for (; a > 0; a--) + { + push(copyToken(q->top), q); + } + + deletetop(q); + + time = 0; + } + else + { + pushfun(op, q); + time++; + } + break; + case '(': + if (is_empty(q)) + { + pushfun(op, q); + time++; + } + else + { + initQueue(&newq); + push(pop(q), &newq); + pushblock(newq, q); + time = 0; + // newq is the queue inside the block + } + break; + case '<': + if (matchwhat(q, "b.")) + { + sendback(q); + push(pop(q), &(q->bottom->val.block)); + time = 0; + } + else + { + pushfun(op, q); + time++; + } + break; + case ')': + if (matchwhat(q, "b")) + { + append(q, &(q->top->val.block)); + initQueue(&(q->top->val.block)); // mandatory since + deletetop(q); // deletetop does destroy the block it contains + time = 0; + } + else + { + pushfun(op, q); + time++; + } + break; + case 'H': + // don't forget to delete the remaining of the queue here, will you? + return; + default: + // raise an error + break; + } + } + else // if q->top->what == BLOCK + { + sendback(q); + time++; + } + } +} + + + +int main(int argc, char *argv[]) +{ + Queue q; + initQueue(&q); // q is empty now + char s[10000] = "72 101 108 108 111 44 32 119 111 114 108 100 33 10 H"; + int printmode = FALSE; // a debug mode that will print the fueue program at each step + int k = 0; + + switch (argc) + { + case 1: + break; + case 2: + if (strcmp(argv[1], "--print") == 0) + { + printmode = TRUE; + } + else + { + strncpy(s, argv[1], 10000); + } + break; + case 3: + strncpy(s, argv[2], 10000); + printmode = (strcmp(argv[1], "--print") == 0); + break; + default: + fprintf(stderr, "Error: %s received too many arguments. The Hello world program\n", argv[0]); + break; + } + + q = strtoqueue(s, &k); + + processFueue(&q, printmode); + return 0; +} + + +Queue strtoqueue(const char s[], int *k) // takes a fueue program as a string, and gives a queue +{ + // *k is loop counter + Queue q; // the queue to be returned + initQueue(&q); + int n = 0; // decimals (usually n * 10 + 0-9) + int intmode = FALSE; // bool "we're reading a number right now" + + if (*k == -1) + { + printf("FUEUE: UNMATCHED OPENING SQUARE BRACKET PROBABLY FORGOT A CLOSING SQUARE BRACKET\n"); + return q; + } + + while (s[*k] != '\0' && s[*k] != ']') + { + if (intmode && (s[*k] > '9' || s[*k] < '0')) // if intmode ends + { + pushnum(n, &q); + n = 0; + intmode = FALSE; + } + + switch (s[*k]) + { + case '+': + case '-': + case '*': + case '/': + case '%': + case ':': + case '~': + case '!': + case '$': + case '(': + case '<': + case ')': + case 'H': + pushfun(s[*k], &q); + (*k)++; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + n = n * 10 + (int) (s[*k] - '0'); + intmode = TRUE; + (*k)++; + break; + case '\n': + case '\t': + case ' ': // whitespace + (*k)++; + break; + case '[': + (*k)++; + pushblock(strtoqueue(s, k), &q); + (*k)++; + break; + default: + printf("FUEUE: UNKNOWN %c OP\n", s[*k]); + (*k)++; + break; + } + } + if (intmode) + { + pushnum(n, &q); + } + + if (s[*k] == '\0') + *k = -1; + return q; +} + +void print_queue(const Queue *q) +{ + struct Token *ptmp = q->top; + while (ptmp != NULL) + { + if (ptmp->what == NUM) + { + printf(" %d", ptmp->val.num); + } + else if (ptmp->what == FUN) + { + printf("%c", ptmp->val.fun); + } + else if (ptmp->what == BLOCK) + { + printf("["); + print_queue(&(ptmp->val.block)); + printf("]"); + } + else + { + printf("That's impossible...Neither num nor fun nor block...\n"); + } + ptmp = ptmp->next; + } +} + + + + +int is_empty(const Queue *q) // bool +{ + if (q->top == NULL) + { + if (q->bottom == NULL && q->size == 0) + { + return TRUE; + } + else + { + error_empty("is_empty"); + } + } + return FALSE; +} + +void initQueue(Queue *q) +{ + q->size = 0; + q->top = NULL; + q->bottom = NULL; +} + +void push(struct Token *x, Queue *q) +{ + if (is_empty(q)) + { + q->top = x; + } + else + { + q->bottom->next = x; + } + q->bottom = x; + q->size++; + x->next = NULL; // just in case +} + +Queue copyQueue(const Queue *q) +{ + Queue c; + struct Token* ptmp = q->top; + initQueue(&c); + + while (ptmp != NULL) + { + push(copyToken(ptmp), &c); + ptmp = ptmp->next; + } + + return c; +} + +void initToken(struct Token *x) +{ + x->what = NUM; + x->val.num = 0; + x->val.fun = '\0'; + initQueue(&(x->val.block)); + x->next = NULL; +} + +struct Token* copyToken(const struct Token *x) +{ + struct Token *c = malloc(sizeof(struct Token)); + c->what = x->what; + + switch (x->what) + { + case NUM: + case FUN: + c->val = x->val; + break; + case BLOCK: + c->val.block = copyQueue(&(x->val.block)); + break; + default: + fprintf(stderr, "Error: found a %d in my soup\n", x->what); + break; + } + + c->next = NULL; + return c; +} + +void pushnum(int num, Queue* q) +{ + struct Token *t = malloc(sizeof(struct Token)); + initToken(t); + t->what = NUM; + t->val.num = num; + + push(t, q); +} + +void pushfun(char f, Queue* q) +{ + struct Token *t = malloc(sizeof(struct Token)); + initToken(t); + t->what = FUN; + t->val.fun = f; + push(t, q); +} + +void pushblock(Queue newq, Queue* q) +{ + struct Token *t = malloc(sizeof(struct Token)); + initToken(t); + t->what = BLOCK; + t->val.block = newq; + push(t, q); +} + +void deletetop(Queue* q) // suppose q not empty +{ + if (is_empty(q)) + error_empty("deletetop"); + + struct Token *todelete = NULL; + + if (q->top->what == BLOCK) // has to free the Queue inside + { + deleteQueue(&(q->top->val.block)); + } + + if (q->top->next == NULL) + { + free(q->top); + q->top = NULL; + q->bottom = NULL; + } + else + { + todelete = q->top; + q->top = q->top->next; + free(todelete); + } + q->size--; +} + +void deleteQueue(Queue *q) +{ + while (!is_empty(q)) + { + deletetop(q); + } +} + +void sendback(Queue* q) // suppose q not empty, pop then push +{ + if (is_empty(q)) + error_empty("sendback"); + + q->bottom->next = q->top; + q->top = q->top->next; + q->bottom = q->bottom->next; + q->bottom->next = NULL; +} + +struct Token* pop(Queue* q) // suppose q not empty +{ + if (is_empty(q)) + error_empty("sendback"); + + struct Token* t = q->top; // note that t->next is equal to q->top->next now + q->top = q->top->next; + q->size--; + + return t; +} + +void append(Queue *q, const Queue *r) +{ + if (is_empty(q)) + { + q->top = r->top; + q->bottom = r->bottom; + } + else if (!is_empty(r)) + { + q->bottom->next = r->top; + q->bottom = r->bottom; + } + q->size += r->size; +} + +int matchwhat(const Queue* q, const char s[]) // bool "nn" "n" "." ".." "n." "b." "b" +{ + int itsok = TRUE; + if ((s[0] != '\0') && !(is_empty(q))) // if neither s nor q is empty + { + if (s[0] == 'n' && q->top->what != NUM) // if top should be num + itsok = FALSE; + if (s[0] == 'b' && q->top->what != BLOCK) // if top should be block + itsok = FALSE; + if (s[1] != '\0' && q->top->next == NULL) // if should have second element + { + itsok = FALSE; + } + else // so it indeed has a second element, or it doesn't need to have one + { + if (s[1] == 'n' && q->top->next->what != NUM) // second should be num + itsok = FALSE; + if (s[1] == 'b' && q->top->next->what != BLOCK) // second should be block + itsok = FALSE; + } + } + else + { + if (s[0] != '\0') // if s is not empty but q is + itsok = FALSE; + } + + // printf("matchwhat: %s\n", (itsok?"TRUE":"FALSE")); + return itsok; +} + +void error_empty(const char s[]) +{ + fprintf(stderr, "Error: queue was empty in %s\n", s); + exit(EXIT_FAILURE); +} + + +