/*====================================================
* rapl_dict_obj_type.js "A Tcl like language implementation in Javascript named WebRAPL
* (Web Rapid Application Programming Language)"
*
* RAPL dict type implementation
*
* Released under BSD license.
* (BSD license found at <http://www.tcl.tk/software/tcltk/license.html>)
*
* Arnulf Wiedemann 2011
*/
RP.add("rapl-dict-obj-type", function(R, name) {
function DictObjType(interp) {
R.log('constructor called', '2.life', 'DictObjType', true);
// kweight
var dict_obj = this;
var constructor = dict_obj.constructor;
DictObjType.superclass.constructor.apply(dict_obj, arguments);
R.Base.dict_obj_type_oid++;
dict_obj.oid = R.Base.dict_obj_type_oid;
dict_obj.interp = interp;
R.log('constructor end', '2.life', 'DictObjType', true);
}
R.extend(DictObjType, R.Token, {
my_name: "DictObjType",
type_name: "dict",
flags: 0,
/* ==================== setFromAny ================================== */
setFromAny: function(obj_ptr) {
var dct = this;
var list_len;
/* Get the string representation. Do this first so we don't
* change order in case of fast conversion to dict.
*/
obj_ptr.getString();
/* For simplicity, convert a non-list object to a list and then to a dict */
list_len = dct.interp.list_obj_type.listLength(obj_ptr);
if (list_len % 2) {
dct.interp.setResultString("invalid dictionary value: must be a list with an even number of elements", -1);
return dct.ERROR;
} else {
/* Now it is easy to convert to a dict from a list, and it can't fail */
var ht = new Object();
var i;
for (i = 0; i < list_len; i += 2) {
var key_obj_ptr = new Array();
var val_obj_ptr = new Array();
dct.interp.list_obj_type.listIndex(obj_ptr, i, key_obj_ptr, dct.FUNCTION_FLAGS_NONE);
dct.interp.list_obj_type.listIndex(obj_ptr, i + 1, val_obj_ptr, dct.FUNCTION_FLAGS_NONE);
key_obj_ptr = key_obj_ptr[0];
val_obj_ptr = val_obj_ptr[0];
key_obj_ptr.incrRefCount();
val_obj_ptr.incrRefCount();
ht[key_obj_ptr.getString()] = val_obj_ptr;
key_obj_ptr.decrRefCount();
}
obj_ptr.freeIntRep();
obj_ptr.obj_type = dct.interp.dict_obj_type;
obj_ptr.ptr(ht);
return dct.OK;
}
},
/* ==================== freeInternalRep ===================================== */
freeInternalRep: function(obj_ptr) {
var dct = this;
obj_ptr.ptr(null);
},
/* ==================== dupInternalRep ===================================== */
dupInternalRep: function(src_ptr, dup_ptr) {
var dct = this;
var ht = src_ptr.ptr();
var dup_ht = new Object();
for (var z in ht) {
dup_ht[z] = ht[z];
dup_ht[z].incrRefCount();
}
dup_ptr.ptr(dup_ht);
dup_ptr.obj_type = dct.interp.dict_obj_type;
},
/* ==================== updateString ===================================== */
updateString: function(obj_ptr) {
var dct = this;
var i;
var buf_len;
var real_length;
var str_rep;
var p;
var quoting_type;
var ht;
var objc = 0;
var objv;
/* Turn the hash table into a flat vector of RaplObjects. */
ht = obj_ptr.ptr();
objv = new Array();
i = 0;
for (var z in ht) {
objv.push(z);
objv.push(ht[z]);
objc += 2;
}
/* (Over) Estimate the space needed. */
quoting_type = new Array();
buf_len = 0;
for (i = 0; i < objc; i++) {
var len;
if (typeof objv[i] == "string") {
str_rep = objv[i];
len = str_rep.length;
} else {
str_rep = objv[i].getString();
len = objv[i].getStringLength();
}
quoting_type.push(dct.interp.list_obj_type.listElementQuotingType(str_rep, len));
switch (quoting_type[i]) {
case dct.interp.list_obj_type.LIST_ELEM_STR_SIMPLE:
buf_len += len;
break;
case dct.interp.list_obj_type.LIST_ELEM_STR_BRACE:
buf_len += len + 2;
break;
case dct.interp.list_obj_type.LIST_ELEM_STR_QUOTE:
buf_len += len * 2;
break;
}
buf_len++; /* elements separator. */
}
buf_len++;
/* Generate the string rep. */
p = "";
real_length = 0;
for (i = 0; i < objc; i++) {
var len;
var qlen = new Array();
var q;
if (typeof objv[i] == "string") {
str_rep = objv[i];
len = str_rep.length;
} else {
str_rep = objv[i].getString();
len = objv[i].getStringLength();
}
switch (quoting_type[i]) {
case dct.interp.list_obj_type.LIST_ELEM_STR_SIMPLE:
p += str_rep;
real_length += len;
break;
case dct.interp.list_obj_type.LIST_ELEM_STR_BRACE:
p += '{'+ str_rep+'}';
real_length += len + 2;
break;
case dct.interp.list_obj_type.LIST_ELEM_STR_QUOTE:
q = dct.interp.list_obj_type.backslashQuoteString(str_rep, len, qlen);
p += q;
real_length += qlen[0];
break;
}
/* Add a separating space */
if (i + 1 != objc) {
p += ' ';
real_length++;
}
}
p += '\0'; /* nul term. */
obj_ptr.bytes = p;
obj_ptr.len = real_length;
},
/* ==================== addElement ===================================== */
/* Add an element to a dict. obj_ptr must be of the "dict" type.
* The higer-level exported function is dictAddElement().
* If an element with the specified key already exists, the value
* associated is replaced with the new one.
*
* if value_obj_ptr == null, the key is instead removed if it exists.
*/
addElement: function(obj_ptr, key_obj_ptr, value_obj_ptr) {
var dct = this;
var ht = obj_ptr.ptr ();
if (value_obj_ptr == null) { /* unset */
return key_obj_ptr.deleteHashEntry(ht, key_obj_ptr);
}
key_obj_ptr.incrRefCount();
value_obj_ptr.incrRefCount();
if (key_obj_ptr.addHashEntry(ht, key_obj_ptr, value_obj_ptr) != dct.OK) {
// var he = key_obj_ptr.findHashEntry(ht, key_obj_ptr);
// key_obj_ptr.decrRefCount();
// he.decrRefCount();
}
return dct.OK;
},
/* ==================== dictAddElement ===================================== */
dictAddElement: function(obj_ptr, key_obj_ptr, value_obj_ptr) {
var dct = this;
var ret_code;
dct.interp.panic(obj_ptr.isShared(), "dictAddElement called with shared object");
if (obj_ptr.obj_type != dct.interp.dict_obj_type) {
if (dct.setFromAny(obj_ptr) != dct.OK) {
return dct.ERROR;
}
}
ret_code = dct.addElement(obj_ptr, key_obj_ptr, value_obj_ptr);
obj_ptr.invalidateStringRep();
return ret_code;
},
/* ==================== newDictObj ===================================== */
newDictObj: function(elements, len) {
var dct = this;
var obj_ptr;
var i;
dct.interp.panic(len % 2, "newDictObj() 'len' argument must be even");
obj_ptr = dct.interp.default_obj.newObj();
obj_ptr.obj_type = dct.interp.dict_obj_type;
obj_ptr.bytes = null;
obj_ptr.ptr(new Object());
for (i = 0; i < len; i += 2) {
dct.addElement(obj_ptr, elements[i], elements[i + 1]);
}
return obj_ptr;
},
/* ==================== dictKey ===================================== */
/* Return the value associated to the specified dict key
* Note: Returns dct.OK if OK, dct.ERROR if entry not found or -1 if can't create dict value
*/
dictKey: function(dict_ptr, key_ptr, obj_ptr_ptr, flags) {
var dct = this;
var he;
var ht;
if (dict_ptr.obj_type != dct.interp.dict_obj_type) {
if (dct.setFromAny(dict_ptr) != dct.OK) {
return -1;
}
}
ht = dict_ptr.ptr();
if ((he = dict_ptr.findHashEntry(ht, key_ptr)) == null) {
if (flags & dct.FUNCTION_FLAGS_LEAVE_ERR_MSG) {
dct.interp.setResultFormatted("key \"%#s\" not found in dictionary", key_ptr);
}
return dct.ERROR;
}
obj_ptr_ptr[0] = he;
return dct.OK;
},
/* ==================== dictPairs ===================================== */
dictPairs: function(dict_ptr, obj_ptr_ptr, flags) {
var dct = this;
var ht;
var he;
var objv;
var i;
if (dict_ptr.obj_type != dct.interp.dict_obj_type) {
if (dcit_prt.setFromAny(dict_ptr) != dct.OK) {
return dct.ERROR;
}
}
ht = dict_ptr.ptr();
/* Turn the hash table into a flat vector of RaplObjects. */
objv = new Array();
i = 0;
for (var z in ht) {
objv.push(z);
objv.push(ht[z]);
}
len[0] = i;
obj_ptr_ptr[0] = objv;
return dct.OK;
},
/* ==================== dictKeysVector ===================================== */
dictKeysVector: function(dict_ptr, keyv, obj_ptr_ptr, flags) {
var dct = this;
var i;
if (keyv.length == 0) {
ob_ptr_ptr[0] = dict_ptr;
return dct.OK;
}
for (i = 0; i < keyv.length; i++) {
var obj_ptr = new Array();
if (dct.dictKey(dict_ptr, keyv[i], obj_ptr, flags) != dct.OK) {
return dct.ERROR;
}
dict_ptr = obj_ptr[0];
}
obj_ptr_ptr[0] = dict_ptr;
return dct.OK;
},
/* ==================== setDictKeysVector ===================================== */
/* Modify the dict stored into the variable named 'var_name_ptr'
* setting the element specified by the 'keyc' keys objects in 'result_ptr',
* with the new value of the element 'new_obj_ptr'.
*
* If new_obj_ptr == null the operation is to remove the given key
* from the dictionary.
*
* If flags & dct.FUNCTION_FLAGS_LEAVE_ERR_MSG, then failure to remove the key is considered an error
* and dct.ERROR is returned. Otherwise it is ignored and dct.OK is returned.
*/
setDictKeysVector: function(var_name_ptr, keyv, keyc, new_obj_ptr, flags) {
var dct = this;
var var_obj_ptr;
var obj_ptr;
var dict_obj_ptr;
var shared;
var i;
var err = false;
var my_obj_ptr;
var_obj_ptr = dct.interp.variable_obj_type.getVariable(var_name_ptr, new_obj_ptr == null ? dct.FUNCTION_FLAGS_LEAVE_ERR_MSG : dct.FUNCTION_FLAGS_NONE);
obj_ptr = var_obj_ptr;
if (obj_ptr == null) {
if (new_obj_ptr == null) { /* Cannot remove a key from non existing var */
return dct.ERROR;
}
var_obj_ptr = dct.newDictObj(null, 0);
obj_ptr = var_obj_ptr;
if (dct.interp.variable_obj_type.setVariable(var_name_ptr, obj_ptr) != dct.OK) {
var_obj_ptr.freeNewObj();
return dct.ERROR;
}
}
if ((shared = obj_ptr.isShared())) {
var_obj_ptr = obj_ptr.duplicateObj();
objPtr = var_obj_ptr;
}
for (i = 0; i < keyc - 1; i++) {
dict_obj_ptr = obj_ptr;
/* Check if it's a valid dictionary */
if (dict_obj_ptr.obj_type != dct.interp.dict_obj_type) {
if (dct.setFromAny(dict_obj_ptr) != dct.OK) {
err = true;
break;
}
}
/* Check if the given key exists. */
dict_obj_ptr.invalidateStringRep();
if (dct.dictKey(dict_obj_ptr, keyv[i], obj_ptr, new_obj_ptr ? dct.FUNCTION_FLAGS_NONE : dct.FUNCTION_FLAGS_LEAVE_ERR_MSG) == dct.OK) {
/* This key exists at the current level.
* Make sure it's not shared!.
*/
if (obj_ptr.isShared()) {
obj_ptr = obj_ptr.duplicateObj();
dct.addElement(dict_obj_ptr, keyv[i], obj_ptr);
}
} else {
/* Key not found. If it's an [unset] operation
* this is an error. Only the last key may not
* exist.
*/
if (new_obj_ptr == null) {
err = true;
break;
}
/* Otherwise set an empty dictionary
* as key's value.
*/
obj_ptr = dct.newDictObj(null, 0);
dct.addElement(dict_obj_ptr, keyv[i], obj_ptr);
}
}
/* Note error on unset with missing last key is OK */
if (keyc == 1) {
my_obj_ptr = keyv;
} else {
my_obj_ptr = keyv[keyc - 1];
}
if (dct.dictAddElement(obj_ptr, my_obj_ptr, new_obj_ptr) != dct.OK) {
if (new_obj_ptr || (flags & dct.FUNCTION_FLAGS_LEAVE_ERR_MSG)) {
err = true;
}
}
if (!err) {
obj_ptr.invalidateStringRep();
var_obj_ptr.invalidateStringRep();
if (dct.interp.variable_obj_type.setVariable(var_name_ptr, var_obj_ptr) != dct.OK) {
err = true;
}
}
if (!err) {
dct.interp.setResult(var_obj_ptr);
return dct.OK;
}
if (shared) {
var_obj_ptr.freeNewObj();
}
return dct.ERROR;
},
/* ==================== expandArrayVariable ================================== */
/*
* Expands the array variable and returns the result, or null on error.
*
* If array_obj.FUNCTION_FLAGS_UNSHARED is set and the dictionary is shared,
* it will be duplicated
* and stored back to the variable before expansion.
*/
expandArrayVariable: function(var_obj_ptr, key_obj_ptr, flags) {
var dct = this;
var dict_obj_ptr;
var res_obj_ptr = new Array();
var ret;
dict_obj_ptr = dct.interp.variable_obj_type.getVariable(var_obj_ptr, dct.FUNCTION_FLAGS_LEAVE_ERR_MSG);
if (!dict_obj_ptr) {
return null;
}
ret = dct.interp.dict_obj_type.dictKey(dict_obj_ptr, key_obj_ptr, res_obj_ptr, dct.FUNCTION_FLAGS_NONE);
res_obj_ptr = res_obj_ptr[0];
if (ret != dct.OK) {
res_obj_ptr = null;
if (ret < 0) {
dct.interp.setResultFormatted("can't read \"%#s(%#s)\": variable isn't array", var_obj_ptr, key_obj_ptr);
} else {
dct.interp.setResultFormatted("can't read \"%#s(%#s)\": no such element in array", var_obj_ptr, key_obj_ptr);
}
} else {
if ((flags & dct.FUNCTION_FLAGS_UNSHARED) && dict_obj_ptr.isShared()) {
dict_obj_ptr = dict_obj_ptr.duplicateObj();
if (dct.interp.variable_obj_type.setVariable(var_obj_ptr, dict_obj_ptr) != dct.OK) {
/* This can probably never happen */
dct.interp.panic(1, "setVariable failed for FUNCTION_FLAGS_UNSHARED");
}
/* We know that the key exists. Get the result in the now-unshared dictionary */
dct.interp.dict_obj_type.dictKey(dict_obj_ptr, key_obj_ptr, res_obj_ptr, dct.FUNCTION_FLAGS_NONE);
}
}
return res_obj_ptr;
},
});
DictObjType.prototype.constructor = DictObjType;
R.DictObjType = DictObjType;
}, "0.0.1", {});