/*====================================================
* rapl_script.js "A Tcl like language implementation in Javascript named WebRAPL
* (Web Rapid Application Programming Language)"
*
* RAPL script implementation
*
* Released under BSD license.
* (BSD license found at <http://www.tcl.tk/software/tcltk/license.html>)
*
* Arnulf Wiedemann 2011
*/
/* The token_info container below is the script object internal representation. An array of
* token_info structures, including a pre-computed representation of the
* command length and arguments.
*
* For example the script:
*
* puts hello
* set $i $x$y [foo]BAR
*
* will produce a ScriptObj with the following Tokens:
*
* LIN 2
* ESC puts
* ESC hello
* LIN 4
* ESC set
* VAR i
* WRD 2
* VAR x
* VAR y
* WRD 2
* CMD foo
* ESC BAR
*
* "puts hello" has two args (LIN 2), composed of single tokens.
* (Note that the WRD token is omitted for the common case of a single token.)
*
* "set $i $x$y [foo]BAR" has four (LIN 4) args, the first word
* has 1 token (ESC SET), and the last has two tokens (WRD 2 CMD foo ESC BAR)
*
* The precomputation of the command structure makes Eval() faster,
* and simpler because there aren't dynamic lengths / allocations.
*
* -- {expand}/{*} handling --
*
* Expand is handled in a special way.
*
* If a "word" begins with {*}, the word token count is -ve.
*
* For example the command:
*
* list {*}{a b}
*
* Will produce the following cmdstruct array:
*
* LIN 2
* ESC list
* WRD -1
* STR a b
*
* Note that the 'LIN' token also contains the source information for the
* first word of the line for error reporting purposes
*
* -- the substFlags field of the structure --
*
* The scriptObj structure is used to represent both "script" objects
* and "subst" objects. In the second case, the there are no LIN and WRD
* tokens. Instead SEP and EOL tokens are added as-is.
* In addition, the field 'substFlags' is used to represent the flags used to turn
* the string into the internal representation used to perform the
* substitution. If this flags are not what the application requires
* the scriptObj is created again. For example the script:
*
* subst -nocommands $string
* subst -novariables $string
*
* Will recreate the internal representation of the $string object
* two times.
*/
RP.add("rapl-script", function(R, name) {
function RaplScript(interp) {
R.log('constructor called', '2.life', 'RaplScript', true);
// kweight
var script = this;
var constructor = script.constructor;
RaplScript.superclass.constructor.apply(script, arguments);
R.Base.script_oid++;
script.oid = R.Base.script_oid;
script.interp = interp;
script.parse_token_list = null;
script.result = null;
script.token_idx = null;
script.text = null;
script.text_len = null;
script.script_object = {
len: null, /* Length as number of tokens. */
tokens: null, /* Tokens array. */
subst_flags: null, /* flags used for the compilation of "subst" objects */
in_use: null, /* Used to share a ScriptObj. Currently
only used by evalObj() as protection against
shimmering of the currently evaluated object. */
file_name_obj: null,
line: null /* Line number of the first line */
};
R.log('constructor end', '2.life', 'RaplScript', true);
}
R.extend(RaplScript, R.RaplObject, {
my_name: "RaplScript",
/* ==================== TOKEN_IS_SEP ================================== */
TOKEN_IS_SEP: function(token) {
var script = this;
return (token >= script.TOKEN_WORD_SEP && token <= script.TOKEN_EOF);
},
/* ==================== toString ================================== */
toString: function() {
var script = this;
return script.text;
},
/* ==================== scriptTokenListInit ================================== */
scriptTokenListInit: function() {
var script = this;
script.parse_token_list = new Array();
},
/* ==================== scriptTokenListFree ================================== */
scriptTokenListFree: function() {
var script = this;
script.parse_token_list = null;
},
/* ==================== scriptAddToken ================================== */
scriptAddToken: function(start, len, token, line) {
var script = this;
//var str = script.text.substring(start, start +len);
//print("Add!"+start+"!"+len+"!"+script.getTokenString(token)+"!"+line+"!str!"+str+"!");
var token_info = {
start: start,
len: len,
token: token,
line: line
};
//print("To!"+script.getTokenString(token_info.token)+"!"+token_info.start+"!");
script.parse_token_list.push(token_info);
},
/* ==================== scriptObjAddTokens ================================== */
/**
* Takes a tokenlist and creates the allocated list of script tokens
* in script.script_object.tokens, of length script.script_object.len.
*
* Unnecessary tokens are discarded, and LINE and WORD tokens are inserted
* as required.
*
* Also sets script.script_object.line to the line number of the first token
*/
scriptObjAddTokens: function() {
var script = this;
var line_args = 0; /* Number of tokens so far for the current command */
var line_first_idx; /* This is the first token for the current command */
var count;
var line_no;
var debug_script_tokens = 0;
var parse_token;
var token_list_idx;
var token_info;
if (debug_script_tokens) {
print("======== Tokens ======");
for (var i = 0; i < script.parse_token_list.length; i++) {
parse_token = script.parse_token_list[i];
print("["+i+"]@"+parse_token.line+" "+script.getTokenString(parse_token.token)+" '"+script.text.substring(parse_token.start, parse_token.start + parse_token.len)+"'");
}
print("======== Tokens END ======");
}
/* May need up to one extra script token for each EOL in the worst case */
count = script.parse_token_list.length;
for (var i = 0; i < script.parse_token_list.length; i++) {
parse_token = script.parse_token_list[i];
if (parse_token.token == script.TOKEN_EOL) {
count++;
}
}
line_no = script.parse_token_list[0].line;
script.script_object.line = line_no;
script.script_object.tokens = new Array();
for (var i = 0; i < count; i++) {
token_info = {
token: null,
obj_ptr: null
};
script.script_object.tokens.push(token_info);
}
script.token_idx = 0;
/* This is the first token for the current command */
token_list_idx = 0;
line_first_idx = script.token_idx;
script.token_idx++;
while (token_list_idx < script.parse_token_list.length) {
/* Look ahead to find out how many tokens make up the next word */
var word_tokens;
parse_token = script.parse_token_list[token_list_idx];
//print("11["+token_list_idx+"]@"+parse_token.line+" "+script.getTokenString(parse_token.token)+" '"+script.text.substring(parse_token.start, (parse_token.start + parse_token.len))+"'");
/* Skip any leading separators */
while (parse_token.token == script.TOKEN_WORD_SEP) {
token_list_idx++;
parse_token = script.parse_token_list[token_list_idx];
}
word_tokens = script.countWordTokens(token_list_idx);
if (word_tokens == 0) {
/* None, so at end of line */
if (line_args) {
var my_token_info = script.script_object.tokens[line_first_idx];
my_token_info.token = script.TOKEN_LINE;
my_token_info.obj_ptr = script.interp.script_line_obj_type.newScriptLineObject(line_args, line_no);
my_token_info.obj_ptr.incrRefCount();
/* Reset for new line */
line_args = 0;
line_first_idx = script.token_idx;
script.token_idx++;
}
token_list_idx++;
continue;
} else {
if (word_tokens != 1) {
/* More than 1, or {expand}, so insert a WORD token */
token_info = script.script_object.tokens[script.token_idx];
token_info.token = script.TOKEN_WORD;
token_info.obj_ptr = script.interp.int_obj_type.newIntObj(word_tokens);
token_info.obj_ptr.incrRefCount();
script.token_idx++;
if (word_tokens < 0) {
/* Skip the expand token */
token_list_idx++;
word_tokens = -word_tokens - 1;
line_args--;
}
}
if (line_args == 0) {
/* First real token on the line, so record the line number */
line_no = script.parse_token_list[token_list_idx].line;
}
line_args++;
/* Add each non-separator word token to the line */
while (word_tokens--) {
parse_token = script.parse_token_list[token_list_idx];
token_list_idx++;
token_info = script.script_object.tokens[script.token_idx];
token_info.token = parse_token.token;
/* put the token info into a new string obj in th ebytes part */
token_info.obj_ptr = script.interp.script_obj_type.makeScriptObj(script.interp, parse_token, script.text);
token_info.obj_ptr.incrRefCount();
/* Every object is initially a string, but the
* internal type may be specialized during execution of the
* script.
* The bytes part still contains the token info!!
*/
script.interp.source_obj_type.setSourceInfo(token_info.obj_ptr, script.script_object.file_name_obj, parse_token.line);
script.token_idx++;
}
}
}
if (line_args == 0) {
script.token_idx--;
}
script.len = script.token_idx;
if (debug_script_tokens) {
print("scriptObjAddTokens!"+script.script_object.file_name_obj+"!");
var fname = script.interp.string_obj_type.getString(script.script_object.file_name_obj);
print("======== Script ("+fname+") ======");
for (var i = 0; i < script.token_idx; i++) {
var token_info = script.script_object.tokens[i];
print("["+i+"]@"+script.getTokenString(token_info.token)+"!"+script.interp.string_obj_type.getString(token_info.obj_ptr)+"!");
}
print("======== Script END ======");
}
script.panic(script.len > count, "scriptObjAddTokens: script.len > count: "+script.len+"!"+count);
},
/* ==================== getText ================================== */
getText: function(start_idx, len) {
var script = this;
return script.text.substring(start_idx, start_idx + len);
},
/* ==================== countWordTokens ================================== */
/* Counts the number of adjoining non-separator.
*
* Returns -ve if the first token is the expansion
* operator (in which case the count doesn't include
* that token).
*/
countWordTokens: function(idx) {
var script = this;
var expand = 1;
var count = 0;
/* Is the first word {*} or {expand}? */
var parse_token = script.parse_token_list[idx];
if (parse_token.token == script.TOKEN_BRACE && !script.TOKEN_IS_SEP(script.parse_token_list[idx + 1].token)) {
if ((parse_token.len == 1 && script.text.charAt(parse_token.start) == '*')
|| (parse_token.len == 6 && script.text.substring(parse_token.start, parse_token.start + 6) == "expand")) {
/* Create an expand token */
expand = -1;
idx++;
parse_token = script.parse_token_list[idx];
}
}
/* Now count non-separator words */
while (!script.TOKEN_IS_SEP(parse_token.token)) {
idx++;
parse_token = script.parse_token_list[idx];
count++;
}
return count * expand;
},
/* ==================== substObjAddTokens ================================== */
substObjAddTokens: function(idx, token_info) {
var script = this;
var i;
var token;
var count;
var debug_script_tokens = 1;
var token_list_idx = 0;
if (debug_script_tokens) {
print("======== Subst ======");
for (var i = 0; i < script.parse_token_list.length; i++) {
parse_token = script.parse_token_list[i];
print("["+i+"]@"+parse_token.line+" "+script.getTokenString(parse_token.token)+" '"+script.text.substring(parse_token.start, parse_token.start + parse_token.len)+"'");
}
print("======== Subst END ======");
}
script.script_object.tokens = new Array();
count = script.parse_token_list.length;
for (var i = 0; i < count; i++) {
token_info = {
token: null,
obj_ptr: null
};
script.script_object.tokens.push(token_info);
}
script.token_idx = 0;
while (token_list_idx < script.parse_token_list.length) {
parse_token = script.parse_token_list[token_list_idx];
token_list_idx++;
token_info = script.script_object.tokens[script.token_idx];
token_info.token = parse_token.token;
token_info.obj_ptr = script.interp.script_obj_type.makeScriptObj(script.interp, parse_token, script.text);
token_info.obj_ptr.incrRefCount();
script.token_idx++;
}
script.len = script.token_idx;
if (debug_script_tokens) {
print("scriptObjAddTokens!"+script.script_object.file_name_obj+"!");
var fname = script.interp.string_obj_type.getString(script.script_object.file_name_obj);
print("======== Script ("+fname+") ======");
for (var i = 0; i < script.token_idx; i++) {
var token_info = script.script_object.tokens[i];
print("["+i+"]@"+script.getTokenString(token_info.token)+"!"+script.interp.string_obj_type.getString(token_info.obj_ptr)+"!");
}
print("======== Script END ======");
}
},
/* ==================== dumpToken ================================== */
dumpToken: function(idx, token_info) {
var script = this;
print("dumpToken["+idx+"]"+script.getTokenString(token_info.token)+"!");
print("["+idx+"]@"+script.getTokenString(token_info.token)+"!"+script.interp.string_obj_type.getString(token_info.obj_ptr)+"!");
},
});
RaplScript.prototype.constructor = RaplScript;
R.RaplScript = RaplScript;
}, "0.0.1", {});