scripting challenge, easy mode |
scripting challenge, easy mode |
Aug 22 2009, 04:59 PM
Post
#1
|
|
/人◕‿‿◕人\ Group: Official Member Posts: 8,283 Joined: Dec 2007 Member No: 602,927 |
1. think up your own esoteric language.
2. write a hello world script with it. 3. explain the code. 4. show the output. 5. ??? 6. profit! Mine: a brainfuck-esque language that uses four commands and seven characters. Useful for learning binary in an extreme way. CODE .[->+>+>->+>->->->->+>+>->->+>->+>->+>+>->+>+>->->->+>+>->+>+>->->->+>+>->+>+>+>+>->->+>->->->->->->+>+>+>->+>+>->+>+>->+>+>+>+>->+>+>+>->->+>->->+>+>->+>+>->->->+>+>->->+>->->->->+>->->->->+]~[->+>+>+>+>+>+>->->->+>->+>+>+>+>->+>+>->+>->->->->+>+>->->+>->+>->+>+>->+>+>->->->+>+>->+>+>->->->+>+>->+>+>+>+>->+>+>+>->+>+>+>->+>+>->+>+>+>+>->+>+>+>->->+>->->+>+>->+>+>->->->+>+>->->+>->->->->+>->+>+>+>->->+>+>+>->+>->->->+>+>+>+>->->->->+>+>+>->+>->-] This code creates "~/helloworld.txt" and writes "hello world!" to it. .[(string)]~[(location)] = write (string) to (location) - = turn bit off + = turn bit on > = next bit You can make a compiler if you really want to. |
|
|
Aug 24 2009, 01:05 PM
Post
#2
|
|
Senior Member Group: Administrator Posts: 2,648 Joined: Apr 2008 Member No: 639,265 |
So how does it work? At first I assumed that the bit-flipping operations turned single bits in a byte on/off, and that the strings were encoded using ASCII, but that doesn't seem to be the case.
|
|
|
Aug 24 2009, 04:31 PM
Post
#3
|
|
/人◕‿‿◕人\ Group: Official Member Posts: 8,283 Joined: Dec 2007 Member No: 602,927 |
The bit-flipping operations turn single bits on and off.
+ = 1 and - = 0, and the strings translate directly from binary to ascii. |
|
|
Aug 24 2009, 07:08 PM
Post
#4
|
|
Senior Member Group: Administrator Posts: 2,648 Joined: Apr 2008 Member No: 639,265 |
Oh, I see. I was looking for an encoded "~", but there isn't one (I guess that's just assumed).
|
|
|
Aug 24 2009, 07:28 PM
Post
#5
|
|
Senior Member Group: Official Designer Posts: 5,880 Joined: Nov 2007 Member No: 593,382 |
How do you run it? And where is the file created?
|
|
|
Aug 24 2009, 07:29 PM
Post
#6
|
|
/人◕‿‿◕人\ Group: Official Member Posts: 8,283 Joined: Dec 2007 Member No: 602,927 |
QUOTE .[->+>+>->+>->->->->+>+>->->+>->+>->+>+>->+>+>->->->+>+>->+>+>->->->+>+>->+>+>+>+>->->+>->->->->->->+>+>+>->+>+>->+>+>->+>+>+>+>->+>+>+>->->+>->->+>+>->+>+>->->->+>+>->->+>->->->->+>->->->->+]~[->+>+>+>+>+>+>->->->+>->+>+>+>+>->+>+>->+>->->->->+>+>->->+>->+>->+>+>->+>+>->->->+>+>->+>+>->->->+>+>->+>+>+>+>->+>+>+>->+>+>+>->+>+>->+>+>+>+>->+>+>+>->->+>->->+>+>->+>+>->->->+>+>->->+>->->->->+>->+>+>+>->->+>+>+>->+>->->->+>+>+>+>->->->->+>+>+>->+>->-] fyi also joseph, you need to write a compiler or an instruction set for a virtual machine. |
|
|
Aug 24 2009, 07:33 PM
Post
#7
|
|
Senior Member Group: Administrator Posts: 2,648 Joined: Apr 2008 Member No: 639,265 |
|
|
|
Aug 24 2009, 07:39 PM
Post
#8
|
|
/人◕‿‿◕人\ Group: Official Member Posts: 8,283 Joined: Dec 2007 Member No: 602,927 |
Oh, alright. Makes sense.
|
|
|
Aug 24 2009, 10:31 PM
Post
#9
|
|
Senior Member Group: Administrator Posts: 2,648 Joined: Apr 2008 Member No: 639,265 |
I wrote an interpreter in C for this language. I've named the interpreter "buttf*ck" since this is a f*cked-up version of brainf*ck. The interpreter is pretty minimal. I made no attempt to write "good" code, I used unsafe C functions like strcpy(), and I made no attempt to free memory (but it gets freed after the program exits anyway). The parser also doesn't really report errors correctly, unless you compile a debug version. Oh, and in some cases it'll break on really large programs/strings. But here it is anyway, all 413 lines of sexy interpreter code:
CODE /* * buttf*ck.c - Interpreter for the Buttf*ck programming language. * <http://www.createblog.com/forums/index.php?showtopic=240400> * Compile: * $ gcc -o buttf*ck buttf*ck.c */ #include <ctype.h> #include <math.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define TKN_BUF_SIZE 256 #define MAX_PATH 256 #define FIRST_LINE 1 #define FIRST_COL 1 typedef enum {ERR_BAD_ARGS = 1, ERR_OUT_OF_MEMORY, ERR_BAD_FILE} bf_memory_error; typedef enum {TKN_STRING = 0, TKN_LOCATION, TKN_EOF, TKN_ERROR} bf_token_type; typedef enum { S_START, S_DONE, S_EOF, S_STRING, S_LOCATION, S_TILDE, S_DATA, S_ERROR } bf_token_state; const char *TOKEN_NAMES[] = {"STRING", "LOCATION", "EOF", "ERROR"}; #define PLUS_MASK 0xffffffff #define MINUS_MASK 0x0 typedef struct { bf_token_type type; unsigned int line; unsigned int col; char *val; } bf_token; typedef struct { const char *path; FILE *stream; unsigned int line; unsigned int col; } bf_tokenizer; typedef struct { bf_tokenizer *stream; char *path; char *str; } bf_parser; static char *expand_tildes(char *origpath, char *newpath) { char *home = NULL; char *xpath = NULL; home = getenv("HOME"); xpath = strchr(origpath, '~'); xpath++; sprintf(newpath, "%s%s", home, xpath); return newpath; } static bf_token *bf_create_token(bf_token_type type, unsigned int line, unsigned int col) { bf_token *tkn = NULL; tkn = (bf_token *) malloc(sizeof(bf_token)); if (!tkn) { perror("bf_create_token"); exit(ERR_OUT_OF_MEMORY); } tkn->type = type; tkn->line = FIRST_LINE; tkn->col = FIRST_COL; tkn->val = NULL; return tkn; } static void bf_free_token(bf_token *t) { free(t->val); free(t); } static char *bf_token_format(bf_token *t) { char *s; s = (char *) malloc(64); if (!s) { perror("bf_token_format"); exit(ERR_OUT_OF_MEMORY); } s = strcpy(s, TOKEN_NAMES[t->type]); if (t->val) { sprintf(s, "%s(%s)", s, t->val); } sprintf(s, "%s [%u]", s, t->line); return s; } static bf_tokenizer *bf_create_tokenizer(const char *path) { bf_tokenizer *t = NULL; FILE *fh = NULL; t = (bf_tokenizer *) malloc(sizeof(bf_tokenizer)); if (!t) { perror("bf_create_tokenizer"); exit(ERR_OUT_OF_MEMORY); } t->path = path; fh = fopen(path, "r"); if (!fh) { perror("bf_create_tokenizer"); exit(ERR_BAD_FILE); } t->stream = fh; t->line = 0; t->col = 0; return t; } static void bf_free_tokenizer(bf_tokenizer *t) { fclose(t->stream); t->stream = NULL; free(t); } static unsigned char bf_getc(bf_tokenizer *t) { int c; c = getc(t->stream); t->col++; switch (c) { case '\t': c = ' '; t->col += 7; break; case '\n': c = ' '; t->line++; break; case EOF: c = '\'; break; } return (unsigned char) c; } static unsigned char bf_ungetc(bf_tokenizer *t, unsigned char ch) { int c; c = ungetc(ch, t->stream); return c != EOF ? (unsigned char) c : '\'; } static bf_token *bf_next_token(bf_tokenizer *t) { bf_token_type type; bf_token *tkn = NULL; bf_token_state state = S_START; char *buf = NULL; unsigned int bufpos = 0; unsigned int bitpos = 0; unsigned int masked = 0; unsigned int bitmask = 0; buf = (char *) malloc(TKN_BUF_SIZE); if (!buf) { perror("bf_next_token"); exit(ERR_OUT_OF_MEMORY); } while (state != S_DONE) { unsigned char ch = bf_getc(t); switch (state) { case S_START: switch (ch) { case '\': state = S_DONE; tkn = bf_create_token(TKN_EOF, t->line, t->col); break; case '.': state = S_START; break; case '[': state = S_STRING; break; case '~': state = S_TILDE; break; default: #ifdef DEBUG fprintf(stderr, "Error (START), expected EOF, '.', '[', or '~', got %c\n", ch); #endif state = S_ERROR; buf[bufpos++] = ch; break; } break; case S_DATA: switch (ch) { case '[': state = S_DATA; break; case '>': bitpos++; if (bitpos > 7) { unsigned char bc = (unsigned char) masked; if (!isprint(bc)) { fprintf(stderr, "Warning: non-printable character %#x, " "line %u, col %u\n", bc, t->line, t->col); } buf[bufpos++] = bc; bitpos = 0; masked = 0; } bitmask = (unsigned int) pow(2, (7 - bitpos)); break; case '+': state = S_DATA; masked |= (PLUS_MASK & bitmask); break; case '-': state = S_DATA; masked |= (MINUS_MASK & bitmask); break; case ']': state = S_DONE; buf[bufpos++] = (unsigned char) masked; tkn = bf_create_token(type, t->line, t->col); break; default: #ifdef DEBUG fprintf(stderr, "Error (%s), expected '[', '>', '+', '-', ']', got %c\n", (type == TKN_STRING ? "STRING" : "LOCATION"), ch); #endif state = S_ERROR; buf[bufpos++] = ch; break; } break; case S_TILDE: switch (ch) { case '[': state = S_LOCATION; break; default: #ifdef DEBUG fprintf(stderr, "Error, expected '~', got %c\n", ch); #endif state = S_ERROR; buf[bufpos++] = ch; break; } break; case S_STRING: state = S_DATA; type = TKN_STRING; break; case S_LOCATION: state = S_DATA; type = TKN_LOCATION; break; case S_ERROR: if (ch == '\') { state = S_DONE; tkn = bf_create_token(TKN_ERROR, t->line, t->col); } else { buf[bufpos++] = ch; } break; } } buf[bufpos] = '\'; /* Terminate that string, son! */ tkn->val = buf; return tkn; } static bf_parser *bf_create_parser(bf_tokenizer *t) { bf_parser *p = NULL; p = (bf_parser *) malloc(sizeof(bf_parser)); if (!p) { perror("bf_create_parser"); exit(ERR_OUT_OF_MEMORY); } p->stream = t; p->path = NULL; p->str = NULL; return p; } static void bf_free_parser(bf_parser *p) { bf_free_tokenizer(p->stream); free(p->path); free(p->str); free(p); } static void bf_parse_string(bf_parser *p) { bf_token *t = NULL; t = bf_next_token(p->stream); switch (t->type) { case TKN_STRING: p->str = t->val; break; default: fprintf(stderr, "Error: Line %u, Col %u\n", t->line, t->col); break; } } static void bf_parse_location(bf_parser *p) { bf_token *t = NULL; t = bf_next_token(p->stream); switch (t->type) { case TKN_LOCATION: p->path = t->val; break; case TKN_EOF: break; default: fprintf(stderr, "Error: Line %u, Col %u\n", t->line, t->col); break; } } static int bf_parser_run(bf_parser *p) { FILE *fh = NULL; char path[MAX_PATH]; size_t len = 0; bf_parse_string(p); bf_parse_location(p); fprintf(stderr, "Path: %s\n", p->path); fprintf(stderr, "String: %s\n", p->str); expand_tildes(p->path, path); fh = fopen(path, "w"); if (!fh) { perror("bf_parser_run"); exit(ERR_BAD_FILE); } len = strlen(p->str); if (fwrite(p->str, len, 1, fh) < 1) { perror("bf_parser_run"); exit(ERR_BAD_FILE); } fclose(fh); return 0; } int main(int argc, char **argv) { bf_tokenizer *tokenizer = NULL; bf_parser *parser = NULL; if (argc < 2) { fprintf(stderr, "Usage: buttf*ck source.bf\n"); exit(ERR_BAD_ARGS); } tokenizer = bf_create_tokenizer(argv[1]); parser = bf_create_parser(tokenizer); return bf_parser_run(parser); } Source file: buttf*ck.c (because CB's code box messes up the format a bit) (That URL doesn't really have a '*' in it; I assume you know how to change it.) P.S. Your code is a bit off in some places -- "hello, world!" isn't printed. "hello, v????B" is printed instead, where '?' are unprintable characters. I think the problem starts with the 'v' -- looks like you may have skipped a bit. This post has been edited by mipadi: Oct 29 2009, 11:28 PM
Reason for edit: use bit.ly for urls
|
|
|
Aug 24 2009, 10:56 PM
Post
#10
|
|
Senior Member Group: Official Designer Posts: 5,880 Joined: Nov 2007 Member No: 593,382 |
PHP:
CODE <?php Print "Hello, World!"; ?> HTML: CODE <html><head><title></title></head><body>Hello, World!</body></html> java script: CODE <script type="text/javascript"> document.write('<b>Hello World</b>'); </script> Text format: Hello, World! Im hardcore. Win? |
|
|
Aug 25 2009, 04:10 PM
Post
#11
|
|
/人◕‿‿◕人\ Group: Official Member Posts: 8,283 Joined: Dec 2007 Member No: 602,927 |
You're softcore, lose.
|
|
|
Oct 29 2009, 02:18 PM
Post
#12
|
|
Senior Member Group: Administrator Posts: 2,648 Joined: Apr 2008 Member No: 639,265 |
Boring day at work, wrote a Python version of the Buttfuck VM:
CODE #!/usr/bin/env python import os import sys class Buttf*ckToken(object): TYPES = ("STRING", "LOCATION", "EOF", "ERROR") def __init__(self, type, line, col, val=None): if type not in Buttf*ckToken.TYPES: raise ValueError("type must be one of %s" % ", ".join(Buttf*ckToken.TYPES)) self.type = type self.line = line self.col = col self.val = val def __str__(self): if self.val: return "%s(%s)" % (self.type, self.val) else: return "%s [%d]" % (self.type, self.line) class Buttf*ckTokenizer(object): def __init__(self, stream): self.stream = stream self.line = 0 self.col = 0 self.buffer = [] def getc(self): if self.buffer: ch = self.buffer[0] self.buffer = self.buffer[1:] else: ch = self.stream.read(1) self.col += 1 if not ch: ch = '\' elif ch == '\t': ch = ' ' self.col += 7 elif ch == '\n': ch = ' ' self.line += 1 return ch def ungetc(self, ch): self.buffer.append(ch) def next(self): state = "START" buf = "" bitpos = 0 masked = 0 tkntype = None while state != "DONE": ch = self.getc() if state == "START": if ch == '\': state = "DONE" tkn = Buttf*ckToken("EOF", self.line, self.col) elif ch == '.': state = "START" elif ch == '[': state = "STRING" elif ch == '~': state = "TILDE" else: state = "ERROR" buf += ch elif state == "DATA": if ch == '[': state = "DATA" elif ch == '>': bitpos += 1 if bitpos > 7: buf += chr(masked) bitpos = 0 masked = 0 bitmask = 2 ** (7 - bitpos) elif ch == '+': state = "DATA" masked |= (0xffffffff & bitmask) elif ch == '-': state = "DATA" masked |= (0x0 & bitmask) elif ch == ']': state = "DONE" buf += chr(masked) tkn = Buttf*ckToken(tkntype, self.line, self.col) else: state = "ERROR" buf += ch elif state == "TILDE": if ch == '[': state = "LOCATION" else: state = "ERROR" buf += ch elif state == "STRING": state = "DATA" tkntype = "STRING" elif state == "LOCATION": state = "DATA" tkntype = "LOCATION" elif state == "ERROR": if ch == '\': state = "DONE" tkn = Buttf*ckToken("ERROR", self.line, self.col) else: buf += ch else: assert False, "Invalid state: " + state tkn.val = buf return tkn class Buttf*ckParser(object): def __init__(self, tokenizer): self.tokenizer = tokenizer self.path = None self.str = None def parse(self): self._parse_string() self._parse_location() perror("Path: " + self.path) perror("String: " + self.str) self.path = os.path.expanduser(self.path) self.path = os.path.realpath(self.path) with open(self.path, "w") as fh: fh.write(self.str) return True def _parse_string(self): tkn = self.tokenizer.next() if tkn.type == "STRING": self.str = tkn.val else: perror("Error: Line %d, Col %d" % (tkn.line, tkn.col)) def _parse_location(self): tkn = self.tokenizer.next() if tkn.type == "LOCATION": self.path = tkn.val elif tkn.type == "EOF": pass else: perror("Error: Line %d, Col %d" % (tkn.line, tkn.col)) def perror(msg): print >>sys.stderr, "buttf*ck: " + msg def main(argv): if len(argv) > 0: try: stream = open(argv[0]) except IOError as e: perror(str(e)) return 1 else: stream = sys.stdin parser = Buttf*ckParser(Buttf*ckTokenizer(stream)) if parser.parse(): stream.close() return 0 else: stream.close() return 2 if __name__ == "__main__": sys.exit(main(sys.argv[1:])) Source: buttuck.py (usual problem with the URL applies) This post has been edited by mipadi: Oct 29 2009, 11:29 PM
Reason for edit: use bit.ly for the urls
|
|
|
Oct 29 2009, 08:24 PM
Post
#13
|
|
Senior Member Group: Head Staff Posts: 18,173 Joined: Mar 2005 Member No: 108,478 |
|
|
|
Oct 29 2009, 10:36 PM
Post
#14
|
|
Senior Member Group: Administrator Posts: 2,648 Joined: Apr 2008 Member No: 639,265 |
Use a URL shortener, like bit.ly? I thought those were only used to disguise really nasty porn links. |
|
|
Oct 29 2009, 10:39 PM
Post
#15
|
|
Senior Member Group: Official Designer Posts: 5,880 Joined: Nov 2007 Member No: 593,382 |
I thought those were only used to disguise really nasty porn links. not only that. maybe you should get https://addons.mozilla.org/en-US/firefox/addon/2207 whats so wrong tinyurl? |
|
|
Oct 29 2009, 11:29 PM
Post
#16
|
|
Senior Member Group: Administrator Posts: 2,648 Joined: Apr 2008 Member No: 639,265 |
Ha!
Well, I used bit.ly to generate better URLs. |
|
|
Nov 8 2009, 01:50 AM
Post
#17
|
|
Senior Member Group: Administrator Posts: 2,648 Joined: Apr 2008 Member No: 639,265 |
Wrote one for Ruby. Thought I'd have more fun with the Ruby version, so I used Treetop to generate a grammar.
Here's the grammar (buttf*ck.tt): CODE grammar Buttf*ck rule program '.[' string ']~[' location ']' end rule string bit+ end rule location bit+ end rule bit [+-] '>'? end end And here's the program that uses that grammar to parse a file (bf.rb): CODE #!/usr/bin/env ruby require 'rubygems' require 'treetop' require 'buttf*ck' class String def from_buttf*ck data = {:bitpos => 0, :masked => 0, :bitmask => 0} buf = '' each_char do |ch| case ch when '>' data[:bitpos] += 1 if data[:bitpos] > 7 buf << data[:masked] data[:bitpos] = 0 data[:masked] = 0 end data[:bitmask] = 2 ** (7 - data[:bitpos]) when '+' data[:masked] |= (0xffffffff & data[:bitmask]) when '-' data[:masked] |= (0x0 & data[:bitmask]) end end buf end def to_path File.expand_path self end end if $0 == __FILE__ if $*.length < 1 $stderr.puts 'Usage: ruby bf.rb file.bf' exit 1 end string = nil location = nil open($*[0], 'r') do |fh| nodes = Buttf*ckParser.new.parse fh.read.strip string = nodes.string.text_value.from_buttf*ck location = nodes.location.text_value.from_buttf*ck end open(location.to_path, 'w') { |fh| fh.print string } end Grammar: buttf*ck.tt Source: bf.rb |
|
|
Nov 8 2009, 02:38 PM
Post
#18
|
|
Tick tock, Bill Group: Administrator Posts: 8,764 Joined: Dec 2005 Member No: 333,948 |
I honestly don't know what any of this means, but it sure is funny reading all this code with "buttf*ck" being used throughout. hahaha
|
|
|