Skip to content

Instantly share code, notes, and snippets.

@mmzeeman
Created November 4, 2012 22:37
Show Gist options
  • Select an option

  • Save mmzeeman/4014100 to your computer and use it in GitHub Desktop.

Select an option

Save mmzeeman/4014100 to your computer and use it in GitHub Desktop.
Javascript ubf(a) encoder/decoder
/* ubf(a) decoder */
;(function(window) {
var ubf = {};
// ubf(a) encode javascript to ubf.
//
function encode(value, buffer) {
buf = buffer || [];
console.log(value.ubf_type);
switch(value.ubf_type) {
case "string":
buf.push(['"', value, '"'].join("")); // TODO: string escape
break;
case "constant":
buf.push(["'", value, "'"].join("")); // TODO: constant escape
break;
case "binary":
buf.push([value.length, "~", value, "~"].join());
break;
case "list":
buf.push("#");
console.log(value.length);
for(var i=value.length-1; i >= 0; i--) {
console.log(value[i]);
encode(value[i], buf);
buf.push("&");
}
break;
case "tuple":
buf.push("{");
for(var i=0; i > value.length; i++) {
encode(value[i], buf);
}
buf.push("{");
break;
case undefined:
// TODO, do something sensible here.
buf.push(value);
}
if(!buffer) {
buf.push("$");
return buf.join(" ");
}
}
// map standard javascript values to ubf
function encode_js(value, buffer) {
switch(typeof(value)) {
case "string":
buffer.push(['"', value, '"']);
}
}
ubf.encode = encode;
// ubf(a) decoder
//
//
ubf.decode = function(bytes, env, stack) {
var j, opcode;
stack = stack || [];
env = env || {};
while(true) {
opcode = bytes[0];
j = handle_operation(opcode, bytes, env, stack);
if(j == 0) return stack[0];
bytes = bytes.slice(j);
}
}
function read_binary(bytes, terminator, type, stack) {
var current, buf = [], i = 0;
while(true) {
current = bytes[i];
if(current == undefined) throw "missing terminator"
if(current == "/") {
switch(bytes[i+1]) {
case "/":
buf.push("/");
break;
case terminator:
buf.push(terminator);
default:
throw "wrong escape sequence"
};
i += 2;
continue;
}
if(current === terminator) {
if(stack) {
var obj = new String(buf.join(""));
if(type)
obj.ubf_type = type;
stack.push(obj);
}
return i + 1;
}
buf.push(current);
i += 1;
}
}
function skip_ws(bytes) {
if(!bytes) return 0;
var ws = bytes.match(/(\s|,)+/);
if(ws) return ws[0].length;
return 0;
}
function handle_integer_or_binary_data(bytes, stack) {
var found = bytes.match(/^\-?[0-9]+/)[0],
integer = Number(found), length = found.length,
rest = bytes.slice(length), ws_length = skip_ws(rest);
if(rest[ws_length] != "~") {
stack.push(integer);
return length + ws_length ;
}
var binary = rest.slice(ws_length+1, ws_length+integer+1)
stack.push(binary);
if(rest[ws_length+1+integer] != "~")
throw "missing closing ~";
return length+ws_length+integer + 2;
};
function handle_string(bytes, stack) {
return read_binary(bytes.slice(1), '"', "string", stack) + 1;
};
function handle_constant(bytes, stack) {
return read_binary(bytes.slice(1), "'", "constant", stack) + 1;
};
function handle_start_tuple(stack) {
stack.push(NaN); // marker for building a tuple
return 1;
}
function handle_end_tuple(stack) {
// pop items from the stack until we find NaN
var obj = stack.pop(), tuple = [];
tuple.ubf_type = "tuple";
while(true) {
if(isNaN(obj)) break;
if(obj === undefined) throw "not a tuple";
tuple.unshift(obj);
obj = stack.pop();
};
stack.push(tuple);
return 1;
};
function handle_push_nil(stack) {
var list = [];
list.ubf_type = "list";
stack.push(list);
return 1;
};
function handle_push_element(stack) {
var obj = stack.pop();
var list = stack.pop();
if(list.ubf_type != "list")
throw "not a list";
list.unshift(obj);
stack.push(list);
return 1;
};
function handle_comment(bytes) {
return read_binary(bytes.slice(1), "%") + 1;
};
function handle_cache(bytes, env, stack) {
var code = bytes[1];
if(env.hasOwnProperty(code))
stack.push(env[code]);
else
env[code] = stack.pop();
return 2;
}
function handle_operation(opcode, bytes, env, stack) {
switch(opcode) {
case " ":case "\r":case"\n":case"\t":case",": return skip_ws(bytes);
case "-":case"0":case"1":case"2":case"3":case"4":case"5":case"6":case"7":case"8":case"9":
return handle_integer_or_binary_data(bytes, stack);
case '"': return handle_string(bytes, stack);
case "'": return handle_constant(bytes, stack);
case "{": return handle_start_tuple(stack);
case "}": return handle_end_tuple(stack);
case "#": return handle_push_nil(stack);
case "&": return handle_push_element(stack);
case "%": return handle_comment(bytes);
case ">": return handle_cache(bytes, env, stack);
case "$":
if(stack.length == 1) return 0;
throw "The stack should contain one item";
case undefined: throw "error";
}
}
window.ubf = ubf;
})(window);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment