/*====================================================
* rapl_parse.js "A Tcl like language implementation in Javascript named WebRAPL
* (Web Rapid Application Programming Language)"
*
* parsing of RAPL tokens for tokenizing input
*
* Released under BSD license.
* (BSD license found at <http://www.tcl.tk/software/tcltk/license.html>)
*
* Arnulf Wiedemann 2011
*/
RP.add("rapl-parse", function(R, name) {
/* =============================== Parser ================================== */
function Parser() {
R.log('constructor called', '2.life', 'Parser', true);
// kweight
var parse = this;
var constructor = parse.constructor;
Parser.superclass.constructor.apply(parse, arguments);
R.Base.parser_oid++;
parse.oid = R.Base.parser_oid;
parse.parse_info = {
text: null, /* program being parsed */
len: 0, /* Remaining length */
start: 0,
end: 0, /* Returned token is at start-end in 'text'. */
index: 0, /* index of the point of the program we are parsing */
cur: null, /* current charcter handled in parsing */
token: parse.TOKEN_EOL, /* Token type */
eof: false, /* Non zero if EOF condition is true. */
state: parse.PARSER_STATE_DEFAULT, /* Parser state */
line: null, /* Line number of the returned token */
line_no: null, /* Current line number */
comment: true, /* Non zero if the next chars may be a comment. */
};
parse.parse_result = {
missing: " ", /* At end of parse, ' ' if complete, '{' if braces */
/* incomplete, '"' if quotes incomplete */
missing_line: null /* Line number starting the missing token */
};
parse.debug = 0;
parse.escape_chars = "$[{ \t\n\r;";
parse.expr_operator_chars = "+->=<!*/%&|()eniNI";
parse.expr_escape_chars = "$[{ \t\n\r;"+parse.expr_operator_chars;
parse.curr_escape_chars = parse.escape_chars;
parse.interp = null;
parse.no_expr_parsing = false;
R.log('constructor end', '2.life', 'Parser', true);
}
R.extend(Parser, R.Token, {
my_name: "RaplParser",
/* ==================== parserInit ===================================== */
parserInit: function (code, len, line_no) {
var parse = this;
parse.parse_info.text = code;
parse.parse_info.len = len + 1;
parse.parse_info.start = 0;
parse.parse_info.end = 0;
parse.parse_info.end = 0;
parse.parse_info.line_no = line_no;
parse.parse_info.index = -1;
parse.parse_info.eof = false;
parse.parse_info.line = 0;
parse.parse_info.token = parse.TOKEN_NONE;
parse.parse_info.state = parse.PARSER_STATE_DEFAULT;
parse.parse_info.comment = true;
parse.parse_info.cur = null;
parse.parse_result.missing = " ";
parse.parse_result.missing_line = null;
parse.feedcharstart();
},
/* =============================== parseScript ================================== */
parseScript: function() {
var parse = this;
while(true) { /* the while is used to reiterate with continue if needed */
if (parse.parse_info.len <= 0) {
parse.parse_info.start = parse.parse_info.index;
parse.parse_info.end = parse.parse_info.index;
parse.parse_info.line = parse.parse_info.line_no;
parse.parse_info.token = parse.TOKEN_EOL;
parse.parse_info.eof = true;
return parse.OK;
}
switch (parse.parse_info.cur) {
case "\\":
if (parse.text.charAt(parse.parse_info.index + 1) == "\n" && parse.parse_info.state == parse.PARSER_STATE_DEFAULT) {
return parse.parseWordSep();
} else {
parse.parse_info.comment = false;
return parse.parseString();
}
break;
case " ":
case "\t":
case "\r":
if (parse.parse_info.state == parse.PARSER_STATE_DEFAULT) {
return parse.parseWordSep();
} else {
parse.parse_info.comment = false;
return parse.parseString();
}
break;
case "\n":
case ";":
parse.parse_info.comment = true;
if (parse.parse_info.state == parse.PARSER_STATE_DEFAULT) {
return parse.parseEol();
} else {
return parse.parseString();
}
break;
case "[":
parse.parse_info.comment = false;
return parse.parseCommand();
break;
case '$':
parse.parse_info.comment = false;
if (parse.parseVar() == parse.ERROR) {
parse.parse_info.end = parse.parse_info.index - 1;
parse.parse_info.len--;
parse.parse_info.line = parse.parse_info.line_no;
parse.parse_info.token = parse.TOKEN_STR;
return parse.OK;
} else {
return parse.OK;
}
break;
case "#":
if (parse.parse_info.comment) {
return parse.parseComment();
} else {
return parse.parseString();
}
break;
default:
parse.parse_info.comment = false;
return parse.parseString();
break;
}
return parse.OK;
}
},
/* ==================== parseWordSep ===================================== */
parseWordSep: function () {
var parse = this;
parse.parse_info.start = parse.parse_info.index;
while (" \t\r\\".indexOf(parse.parse_info.cur)>=0) {
if (parse.parse_info.cur == '\\') {
if (parse.parse_info.len > 0) {
if (parse.text.charAt(parse.parse_info.index + 1) == '\n') {
parse.feedchar();
} else {
break;
}
} else {
break;
}
}
parse.feedchar();
}
parse.parse_info.end = parse.parse_info.index - 1;
parse.parse_info.token = parse.TOKEN_WORD_SEP;
R.log(' parseWordSEP!'+parse.getText()+"!", '2.debug', 'Parser', true);
return parse.OK;
},
/* ==================== parseEol ===================================== */
parseEol: function () {
var parse = this;
parse.parse_info.start = parse.parse_info.index;
parse.parse_info.line = parse.parse_info.line_no;
//print("parseEol!"+parse.parse_info.index+"!"+parse.parse_info.len+"!");
while(1) {
if (parse.parse_info.cur == "\n") {
parse.parse_info.line_no++;
}
switch(parse.parse_info.cur) {
case " ":
case "\n":
case "\t":
case "\r":
case ";":
parse.feedchar();
if (parse.parse_info.len == 0) {
break;
}
continue;
}
break;
}
parse.parse_info.end = parse.parse_info.index - 1;
parse.parse_info.token = parse.TOKEN_EOL;
R.log(' parseEol!'+parse.getText()+"!"+parse.parse_info.cur+"!"+parse.parse_info.len+"!", '2.debug', 'Parser', true);
return this.OK;
},
/*
** Here are the rules for parsing:
** {braced expression}
** - Count open and closing braces
** - Backslash escapes meaning of braces
**
** "quoted expression"
** - First double quote at start of word terminates the expression
** - Backslash escapes quote and bracket
** - [commands brackets] are counted/nested
** - command rules apply within [brackets], not quoting rules (i.e. quotes have their own rules)
**
** [command expression]
** - Count open and closing brackets
** - Backslash escapes quote, bracket and brace
** - [commands brackets] are counted/nested
** - "quoted expressions" are parsed according to quoting rules
** - {braced expressions} are parsed according to brace rules
**
** For everything, backslash escapes the next char, newline increments current line
*/
/* ==================== parseSubBrace ===================================== */
/**
* Parses a braced expression starting at parse.index.
*
* Positions the parser at the end of the braced expression,
* sets parse.end and possibly parse.missing.
*/
parseSubBrace: function() {
var parse = this;
var level = 1;
/* Skip the brace */
parse.feedchar();
while (parse.parse_info.len) {
switch (parse.parse_info.cur) {
case '\\':
if (parse.parse_info.len > 1) {
if (parse.parse_info.text.charAt(parse.parse_info.index + 1) == '\n') {
parse.parse_info.line_no++;
}
parse.feedchar();
}
break;
case '{':
level++;
break;
case '}':
if (--level == 0) {
parse.parse_info.end = parse.parse_info.index - 1;
parse.feedchar();
return;
}
break;
case '\n':
parse.parse_info.line_no++;
break;
}
parse.feedchar();
}
parse.parse_result.missing = '{';
parse.parse_result.missing_line = parse.parse_info.line;
parse.parse_info.end = parse.parse_info.index - 1;
},
/* ==================== parseSubQuote ===================================== */
/**
* Parses a quoted expression starting at parse.index.
*
* Positions the parser at the end of the quoted expression,
* sets parse.end and possibly parse.missing.
*
* Returns the type of the token of the string,
* either TOKEN_ESC (if it contains values which need to be [subst]ed)
* or TOKEN_QUOTE.
*/
parseSubQuote: function() {
var parse = this;
var token = parse.TOKEN_QUOTE;
var line = parse.parse_info.line;
/* Skip the quote */
parse.feedchar();
while (parse.parse_info.len) {
switch (parse.parse_info.cur) {
case '\\':
if (parse.parse_info.len > 1) {
if (parse.parse_info.text.charAt(parse.parse_info.index + 1) == '\n') {
parse.parse_info.line_no++;
}
parse.feedchar();
token = parse.TOKEN_QUOTE_ESC;
}
break;
case '"':
parse.parse_info.end = parse.parse_info.index - 1;
parse.feedchar();
return token;
case '[':
parseSubCmd();
token = parse.TOKEN_QUOTE_ESC;
continue;
case '\n':
parse.parse_info.line_no++;
break;
case '$':
token = parse.TOKEN_QUOTE_ESC;
break;
}
parse.feedchar();
}
parse.parse_result.missing = '"';
parse.parse_result.missing_line = line;
parse.parse_info.end = parse.parse_info.index - 1;
return token;
},
/* ==================== parseSubCmd ===================================== */
/**
* Parses a [command] expression starting at parse.index.
*
* Positions the parser at the end of the command expression,
* sets parse.end and possibly parse.missing.
*/
parseSubCmd: function() {
var parse = this;
var level = 1;
var start_of_word = 1;
var line = parse.parse_info.line;
/* Skip the bracket */
parse.feedchar();
while (parse.parse_info.len) {
switch (parse.parse_info.cur) {
case '\\':
if (parse.parse_info.len > 1) {
if (parse.parse_info.text.charAt(parse.parse_info.index + 1) == '\n') {
parse.parse_info.line_no++;
}
parse.feedchar();
}
break;
case '[':
level++;
break;
case ']':
if (--level == 0) {
parse.parse_info.end = parse.parse_info.index - 1;
parse.feedchar();
return;
}
break;
case '"':
if (start_of_word) {
parse.parseSubQuote();
continue;
}
break;
case '{':
parse.parseSubBrace();
start_of_word = 0;
continue;
case '\n':
parse.parse_info.line_no++;
break;
}
start_of_word = parse.hasSpace.test(parse.cur);
parse.feedchar();
}
parse.parse_result.missing = '[';
parse.parse_result.missing_line = line;
parse.parse_info.end = parse.parse_info.index - 1;
},
/* ==================== parseBrace ===================================== */
parseBrace: function() {
var parse = this;
parse.parse_info.start = parse.parse_info.index + 1;
parse.parse_info.line = parse.parse_info.line_no;
parse.parse_info.token = parse.TOKEN_BRACE;
parse.parseSubBrace();
return parse.OK;
},
/* ==================== parseCommand ===================================== */
parseCommand: function() {
var parse = this;
parse.parse_info.start = parse.parse_info.index + 1;
parse.parse_info.line = parse.parse_info.line_no;
parse.parse_info.token = parse.TOKEN_CMD;
parse.parseSubCmd();
return parse.OK;
},
/* ==================== parseQuote ===================================== */
parseQuote: function() {
var parse = this;
parse.parse_info.start = parse.parse_info.index + 1;
parse.parse_info.line = parse.parse_info.line_no;
parse.parse_info.token = parse.parseSubQuote();
return parse.OK;
},
/* ==================== parseVar ===================================== */
parseVar: function() {
var parse = this;
/* skip the $ */
parse.feedchar();
if (parse.parse_info.cur == '[') {
/* Parse $[...] expr shorthand syntax */
parseCmd();
parse.parse_info.token = parse.TOKEN_EXPRSUGAR;
return parse.OK;
}
parse.parse_info.start = parse.parse_info.index;
parse.parse_info.token = parse.TOKEN_VAR;
parse.parse_info.line = parse.parse_info.line_no;
if (parse.parse_info.cur == '{') {
parse.feedcharstart();
while (parse.parse_info.len && parse.parse_info.cur != '}') {
if (parse.parse_info.cur == '\n') {
parse.parse_info.line_no++;
}
parse.feedchar();
}
parse.parse_info.end = parse.parse_info.index - 1;
if (parse.parse_info.len) {
parse.feedchar();
}
} else {
while (1) {
/* Skip double colon, but not single colon! */
if (parse.parse_info.cur == ':' && parse.parse_info.text.charAt(parse.parse_info.index + 1) == ':') {
parse.feedchar();
parse.feedchar();
continue;
}
if (parse.matchVarName.test(parse.parse_info.cur)) {
parse.feedchar();
continue;
}
break;
}
/* Parse [dict get] syntax sugar. */
if (parse.parse_info.cur == '(') {
var count = 1;
var paren = null;
parse.parse_info.token = parse.TOKEN_VAR_ARRAY_NAME
while (count && parse.parse_info.len) {
parse.feedchar();
if (parse.parse_info.cur == '\\' && parse.parse_info.len >= 1) {
parse.feedchar();
} else {
if (parse.parse_info.cur == '(') {
count++;
} else {
if (parse.parse_info.cur == ')') {
paren = parse.parse_info.index;
count--;
}
}
}
if (count == 0) {
parse.feedchar();
} else {
if (paren) {
/* Did not find a matching paren. Back up */
paren++;
parse.parse_info.len += (parse.parse_info.index - paren);
parse.parse_info.index = paren;
}
}
if (parse.parse_info.text.charAt(parse.parse_info.start) == '(') {
parse.parse_info.token = parse.TOKEN_EXPRSUGAR;
}
}
}
parse.parse_info.end = parse.parse_info.index - 1;
}
/* Check if we parsed just the '$' character.
* That's not a variable so an error is returned
* to tell the state machine to consider this '$' just
* a string. */
if (parse.parse_info.start == parse.parse_info.index) {
parse.parse_info.index--;
parse.parse_info.len++;
return parse.ERROR;
}
return parse.OK;
},
/* ==================== parseString ===================================== */
parseString: function() {
var parse = this;
//print("parseString!"+parse.parse_info.cur+"!");
var newword = (parse.parse_info.token == parse.TOKEN_WORD_SEP ||
parse.parse_info.token == parse.TOKEN_EOL ||
parse.parse_info.token == parse.TOKEN_NONE ||
parse.parse_info.token == parse.TOKEN_STR);
if (newword && parse.parse_info.cur == '{') {
return parse.parseBrace();
} else {
if (newword && parse.parse_info.cur == '"') {
parse.parse_info.state = parse.PARSER_STATE_QUOTE;
parse.feedchar();
/* In case the end quote is missing */
parse.parse_result.missing_line = parse.parse_infoline;
}
}
parse.parse_info.start = parse.parse_info.index;
parse.parse_info.line = parse.parse_info.line_no;
while (1) {
if (parse.parse_info.len == 0) {
if (parse.parse_info.state == parse.PARSER_STATE_QUOTE) {
parse.parse_result.missing = '"';
}
parse.parse_info.end = parse.parse_info.index - 1;
if (parse.parse_info.state == parse.PARSER_STATE_QUOTE) {
parse.parse_info.token = parse.TOKEN_QUOTE_ESC;
} else {
parse.parse_info.token = parse.TOKEN_ESC;
}
return parse.OK;
}
switch (parse.parse_info.cur) {
case '\\':
if (parse.parse_info.state == parse.PARSER_STATE_DEFAULT && parse.parse_info.text.charAt(parse.parse_info.index + 1) == '\n') {
parse.parse_info.end = parse.parse_info.index - 1;
if (parse.parse_info.state == parse.PARSER_STATE_QUOTE) {
parse.parse_info.token = parse.TOKEN_QUOTE_ESC;
} else {
parse.parse_info.token = parse.TOKEN_ESC;
}
return parse.OK;
}
if (parse.parse_info.len >= 2) {
if (parse.parse_info.text.charAt(parse.parse_info.index + 1) == '\n') {
parse.parse_info.line_no++;
}
parse.feedchar();
}
break;
case '(':
/* If the following token is not '$' just keep going */
if (parse.parse_info.len > 1 && parse.parse_info.text.charAt(parse.parse_info.index + 1) != '$') {
break;
}
case ')':
/* Only need a separate ')' token if the previous was a var */
if (parse.parse_info.cur == '(' || parse.parse_info.token == parse.TOKEN_VAR) {
if (parse.parse_info.index == parse.parse_info.start) {
/* At the start of the token, so just return this char */
parse.feedchar();
}
parse.parse_info.end = parse.parse_info.idx - 1;
if (parse.parse_info.state == parse.PARSER_STATE_QUOTE) {
parse.parse_info.token = parse.TOKEN_QUOTE_ESC;
} else {
parse.parse_info.token = parse.TOKEN_ESC;
}
return parse.OK;
}
break;
case '$':
case '[':
parse.parse_info.end = parse.parse_info.index - 1;
if (parse.parse_info.state == parse.PARSER_STATE_QUOTE) {
parse.parse_info.token = parse.TOKEN_QUOTE_ESC;
} else {
parse.parse_info.token = parse.TOKEN_ESC;
}
return parse.OK;
case ' ':
case '\t':
case '\n':
case '\r':
case ';':
if (parse.parse_info.state == parse.PARSER_STATE_DEFAULT) {
parse.parse_info.end = parse.parse_info.index - 1;
if (parse.parse_info.state == parse.PARSER_STATE_QUOTE) {
parse.parse_info.token = parse.TOKEN_QUOTE_ESC;
} else {
parse.parse_info.token = parse.TOKEN_ESC;
}
return parse.OK;
} else {
if (parse.parse_info.cur == '\n') {
parse.parse_info.line_no++;
}
}
break;
case '"':
if (parse.parse_info.state == parse.PARSER_STATE_QUOTE) {
parse.parse_info.end = parse.parse_info.index - 1;
parse.parse_info.token = parse.TOKEN_QUOTE_ESC;
parse.feedchar();
parse.parse_info.state = parse.PARSER_STATE_DEFAULT;
return parse.OK;
}
break;
}
parse.feedchar();
}
return parse.OK; /* unreached */
},
/* ==================== parseComment ===================================== */
parseComment: function() {
var parse = this;
while (parse.parse_info.len) {
if (parse.parse_info.cur == '\n') {
parse.parse_info.line_no++;
if (parse.parse_info.text.charAt(parse.parse_info.index - 1) != '\\') {
parse.feedchar();
return parse.OK;
}
}
parse.feedchar();
}
return parse.OK;
},
/* ==================== parseList ===================================== */
parseList: function() {
var parse = this;
switch (parse.parse_info.cur) {
case ' ':
case '\n':
case '\t':
case '\r':
return parse.parseListSep();
case '"':
return parse.parseListQuote();
case '{':
return parse.parseBrace();
default:
if (parse.parse_info.len) {
return parse.parseListStr();
}
break;
}
parse.parse_info.end = parse.parse_info.index;
parse.parse_info.start = parse.parse_info.index;
parse.parse_info.line = parse.parse_info.line_no;
parse.parse_info.token = parse.TOKEN_EOL;
parse.parse_info.eof = true;
return parse.OK;
},
/* ==================== parseListSep ===================================== */
parseListSep: function() {
var parse = this;
parse.parse_info.start = parse.parse_info.index;
parse.parse_info.line = parse.parse_info.line_no;
while (parse.parse_info.cur == ' ' || parse.parse_info.cur == '\t' ||
parse.parse_info.cur == '\r' || parse.parse_info.cur == '\n') {
if (parse.parse_info.cur == '\n') {
parse.parse_info.line_no++;
}
parse.feedchar();
}
parse.parse_info.end = parse.parse_info.index - 1;
parse.parse_info.token = parse.TOKEN_SEP;
return parse.OK;
},
/* ==================== parseListQuote ===================================== */
parseListQuote: function() {
var parse = this;
parse.feedchar();
parse.parse_info.start = parse.parse_info.index;
parse.parse_info.line = parse.parse_info.line_no;
parse.parse_info.token = parse.TOKEN_STR;
while (parse.parse_info.len) {
switch (parse.parse_info.cur) {
case '\\':
parse.parse_info.token = parse.TOKEN_ESC;
if (--parse.parse_info.len == 0) {
/* Trailing backslash */
parse.parse_info.end = parse.parse_info.index;
return parse.OK;
}
parse.parse_info.index++;
break;
case '\n':
parse.parse_info.line_no++;
break;
case '"':
parse.parse_info.end = parse.parse_info.index - 1;
parse.feedchar();
return parse.OK;
}
parse.feedchar();
}
parse.parse_info.end = parse.parse_info.index - 1;
return parse.OK;
},
/* ==================== parseListStr ===================================== */
parseListStr: function() {
var parse = this;
parse.parse_info.start = parse.parse_info.index;
parse.parse_info.line = parse.parse_info.line_no;
parse.parse_info.token = parse.TOKEN_STR;
while (parse.parse_info.len) {
switch (parse.parse_info.cur) {
case '\\':
if (--parse.parse_info.len == 0) {
/* Trailing backslash */
parse.parse_info.end = parse.parse_info.index;
return parse.OK;
}
parse.parse_info.token = parse.TOKEN_ESC;
parse.parse_info.index++;
break;
case ' ':
case '\t':
case '\n':
case '\r':
parse.parse_info.end = parse.parse_info.index - 1;
return JIM_OK;
}
parse.feedchar();
}
parse.parse_info.end = parse.parse_info.index - 1;
return parse.OK;
},
/* ==================== parseParen ===================================== */
parseParen: function () {
var parse = this;
var level = 1;
parse.feedcharstart();
while (true) {
if (parse.parse_info.len > 1 && parse.parse_info.cur == "\\") {
parse.feedSequence();
} else {
if ((parse.parse_info.len == 0) || (parse.parse_info.cur == ")")) {
level--;
if (level == 0 || parse.parse_info.len == 0) {
parse.parse_info.end = parse.parse_info.index - 1;
if (parse.parse_info.len > 0) {
parse.feedchar();
parse.parse_info.token = parse.TOKEN_LP;
}
if (parse.debug) {
print(" parseParen!"+parse.getText()+"!"+parse.parse_info.text.charAt(parse.parse_info.index)+"!"+parse.parse_info.cur+"!");
}
return parse.OK;
}
} else {
if (parse.parse_info.cur == "(") {
level++;
}
}
}
parse.feedchar();
}
return parse.OK; // unreached
},
/* ==================== parseExpression ================================== */
parseExpression: function(exp) {
var parse = this;
/* Discard spaces and quoted newline */
while (parse.hasSpace.test(parse.UCHAR(parse.parse_info.cur)) || (parse.parse_info.cur == '\\' && (parse.parse_info.text.charAt(parse.parse_info.index + 1) == '\n'))) {
if (parse.parse_info.cur == '\n') {
parse.parse_info.line_no++;
}
parse.feedchar();
}
if (parse.parse_info.len == 0) {
parse.parse_info.start = parse.parseinfo.index;
parse.parse_info.end = parse.parseinfo.index;
parse.parse_info.line = parse.parse_info.line_no;
parse.parse_info.token = parse.EOL;
parse.parse_info.eof = true;
return parse.OK;
}
switch (parse.parse_info.cur) {
case '(':
parse.parse_info.token = parse.TOKEN_SUBEXPR_START;
parse.parse_info.start = parse.parse_info.index
parse.parse_info.end = parse.parse_info.index;
parse.parse_info.line = parse.parse_info.line_no;
parse.feedchar();
break;
case ')':
parse.parse_info.token = parse.TOKEN_SUBEXPR_END;
parse.parse_info.start = parse.parse_info.index
parse.parse_info.end = parse.parse_info.index;
parse.parse_info.line = parse.parse_info.line_no;
parse.feedchar();
break;
case ',':
parse.parse_info.token = parse.TOKEN_SUBEXPR_COMMA;
parse.parse_info.start = parse.parse_info.index
parse.parse_info.end = parse.parse_info.index;
parse.parse_info.line = parse.parse_info.line_no;
parse.feedchar();
break;
case '[':
return parse.parseCmd();
case '$':
if (parse.parseVar() == parse.ERROR) {
return parse.parseExprOperator(exp);
} else {
/* Don't allow expr sugar in expressions */
if (parse.parse_info.token == parse.TOKEN_EXPRSUGAR) {
return parse.ERROR;
}
return parse.OK;
}
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '.':
return parse.parseExprNumber(exp);
case '"':
return parse.parseQuote();
case '{':
return parse.parseBrace();
case 'N':
case 'I':
case 'n':
case 'i':
if (parse.parseExprIrrational() == parse.ERROR)
return parse.parseExprOperator(exp);
break;
default:
return parse.parseExprOperator(exp);
break;
}
return parse.OK;
},
/* ==================== parseExprNumber ================================== */
parseExprNumber: function(exp) {
var parse = this;
var allowdot = 1;
var allowhex = 0;
/* Assume an integer for now */
parse.parse_info.token = parse.TOKEN_EXPR_INT;
parse.parse_info.start = parse.parse_info.index;
parse.parse_info.line = parse.parse_info.line_no;
while (parse.isInteger.test(parse.UCHAR(parse.parse_info.cur))
|| (allowhex && parse.isHex.test(parse.UCHAR(parse.parse_info.cur)))
|| (allowdot && parse.parse_info.cur == '.')
|| (parse.parse_info.index - parse.parse_info.start == 1 && parse.parse_info.text.charAt(parse.parse_info.start) == '0' && (parse.parse_info.cur == 'x' || parse.parse_info.cur == 'X'))
) {
if ((parse.parse_info.cur == 'x') || (parse.parse_info.cur == 'X')) {
allowhex = 1;
allowdot = 0;
}
if (parse.parse_info.cur == '.') {
allowdot = 0;
parse.parse_info.token = parse.TOKEN_EXPR_DOUBLE;
}
parse.feedchar();
if (!allowhex && (parse.parse_info.cur == 'e' || parse.parse_info.cur == 'E') && (parse.parse_info.text.charAt(parse.parse_info.index + 1) == '-' || parse.parse_info.text.charAt(parse.parse_info.index + 1) == '+'
|| parse.isInteger.test(parse.UCHAR(parse.parse_info.text.charAt(parse.parse_info.index + 1))))) {
parse.feedchar();
parse.feedchar();
parse.parse_info.token = parse.TOKEN_EXPR_DOUBLE;
}
}
parse.parse_info.end = parse.parse_info.index - 1;
return parse.OK;
},
/* ==================== parseExprIrrational ================================== */
parseExprIrrational: function(exp) {
var tokens = ["NaN", "nan", "NAN", "Inf", "inf", "INF"];
var token;
for (var i = 0; i < tokens.length; i++) {
len = tokens[i].length;
if (parse.parse_info.text.substring(parse.parse_info.index, parse.parse_info.index + len) == tokens[i]){
parse.parse_info.start = parse.parse_info.index;
parse.parse_info.end = parse.parse_info.index + len - 1;
parse.parse_info.index += len;
parse.parse_info.len -= len;
parse.parse_info.line = parse.parse_info.line_no;
parse.parse_info.token = parse.TOKEN_EXPR_DOUBLE;
return parse.OK;
}
}
return parse.ERROR;
},
UCHAR: function(str) {
//FIXME !!! need code here for handling of unicode !!!
return str
},
/* ==================== parseExprOperator ================================== */
parseExprOperator: function(exp) {
var parse = this;
var i;
var bestIdx = -1;
var bestLen = 0;
print ("E!"+exp+"!");
var lgth = exp.exprOperators.length;
print ("L!"+lgth+"!");
/* Try to get the longest match. */
for (i = 0; i < lgth; i++) {
var op_name;
var op_len;
opname = exp.exprOperators[i].name;
if (op_name == null) {
continue;
}
op_len = op_name.length;
if (parse.parse_info.text.substring(parse.parse_info.index, parse.parse_info.index + op_len) == op_name && op_len > bestLen) {
bestIdx = i + parse.TOKEN_EXPR_OP;
bestLen = op_len;
}
}
if (bestIdx == -1) {
return parse.ERROR;
}
/* Validate paretheses around function arguments */
if (bestIdx >= exp.TOKEN_EXPROP_FUNC_FIRST) {
var idx = parse.parse_info.index + bestLen;
var p = parse.parse_info.text.charAt(idx);
var len = parse.parse_info.len - bestLen;
while (len && parse.hasSpace.test(UCHAR(parse.parse_info.textcharAt(idx)))) {
len--;
idx++;
}
if (parse.parse_info.textcharAt(idx) != '(') {
return pasre.ERROR;
}
}
parse.parse_info.start = parse.parse_info.index;
parse.parse_info.end = parse.parse_info.index + bestLen - 1;
parse.parse_info.index += bestLen;
parse.parse_info.len -= bestLen;
parse.parse_info.line = parse.parse_info.line_no;
parse.parse_info.token = bestIdx;
return parse.OK;
},
/* ==================== getText ===================================== */
getText: function () {
var parse = this;
//print("getText!"+parse.getTokenString(parse.parse_info.token)+"!");
//print("getText text!"+parse.parse_info.text.substring(parse.parse_info.start,parse.parse_info.end+1)+"!type!"+parse.getTokenString(parse.parse_info.token)+"!");
return parse.parse_info.text.substring(parse.parse_info.start, parse.parse_info.end + 1);
},
/* ==================== feedSequence ===================================== */
feedSequence: function () {
var parse = this;
if (parse.parse_info.cur != "\\") {
throw "Invalid escape sequence";
}
var cur = parse.steal(1);
var specials = new Object();
specials.a = "\a";
specials.b = "\b";
specials.f = "\f";
specials.n = "\n";
specials.r = "\r";
specials.t = "\t";
specials.v = "\v";
switch (cur) {
case 'u':
var hex = parse.steal(4);
if (hex != parse.isHexSeq.exec(hex)) {
throw "Invalid unicode escape sequence: "+hex;
}
cur = String.fromCharCode(parseInt(hex,16));
break;
case 'x':
var hex = parse.steal(2);
if (hex != parse.isHexSeq.exec(hex)) {
throw "Invalid unicode escape sequence: "+hex;
}
cur = String.fromCharCode(parseInt(hex,16));
break;
case "a":
case "b":
case "f":
case "n":
case "r":
case "t":
case "v":
cur = specials[cur];
break;
case "\n":
//print("FEED escaped LF1!"+parse.text.substring(parse.index)+"!"+parse.text+"!");
var tail = parse.text.substring(parse.index+1);
var head = parse.text.substring(0,parse.index-1);
parse.parse_info.text = head+" "+tail;
parse.parse_info.len--;
parse.parse_info.cur = parse.parse_info.text.charAt(parse.parse_info.index);
//print("FEED escaped LF2!"+parse.parse_info.cur+"!"+parse.parse_info.text.substring(parse.parse_info.index)+"!"+parse.parse_info.text+"!");
return;
default:
if ("0123456789".indexOf(cur) >= 0) {
cur = cur + parse.steal(2);
if (cur != parse.isOctalSeq.exec(cur)) {
throw "Invalid octal escape sequence: "+cur;
}
cur = String.fromCharCode(parseInt(cur, 8));
}
break;
}
var head = parse.parse_info.text.substring(0, parse.parse_info.index);
var tail = parse.parse_info.text.substring(parse.parse_info.index + 1);
parse.parse_info.text = head+cur+tail;
parse.parse_info.cur = parse.parse_info.text.charAt(parse.parse_info.index);
},
/* ==================== steal ===================================== */
steal: function (n) {
var parse = this;
var tail = parse.parse_info.text.substring(parse.parse_info.index+1);
var word = tail.substr(0, n);
parse.parse_info.text = parse.parse_info.text.substring(0, parse.parse_info.index) + tail.substring(n-1);
parse.parse_info.len = parse.parse_info.len - n;
return word;
},
/* ==================== feedcharstart ===================================== */
feedcharstart: function () {
var parse = this;
parse.feedchar();
parse.parse_info.start = parse.parse_info.index;
},
/* ==================== setPos ===================================== */
setPos: function (index) {
var parse = this;
var d = index - parse.parse_info.index;
parse.parse_info.index = index;
parse.parse_info.len -= d;
parse.parse_info.cur = parse.parse_info.text.charAt(parse.parse_info.index);
},
/* ==================== feedchar ===================================== */
feedchar: function () {
var parse = this;
parse.parse_info.index++;
parse.parse_info.len--;
if (parse.parse_info.len < 0) {
throw "End of file reached";
}
parse.parse_info.cur = parse.parse_info.text.charAt(parse.parse_info.index);
}
});
function PP () {
/* ==================== setExprParser ===================================== */
this.setExprParser = function () {
var parse = this;
parse.curr_escape_chars = parse.expr_escape_chars;
parse.expr_parser = true;
}
/* ==================== parseList ===================================== */
this.parseList = function () {
var parse = this;
var level = 0;
parse.start = parse.index;
parse.parse_info.start = parse.parse_info.index;
while (true) {
if (parse.len == 0) {
parse.end = parse.index;
parse.token = parse.TOKEN_EOL;
return;
}
switch (parse.cur) {
case "\\":
if (parse.len >= 2) {
parse.feedSequence();
}
break;
case " ":
case "\t":
case "\n":
case "\r":
if (level > 0) {
break;
}
parse.end = parse.index - 1;
parse.token = parse.TOKEN_WORD_SEP;
parse.feedchar();
return;
case '{':
level++;
break;
case '}':
level--;
break;
}
parse.feedchar();
}
if (level != 0) {
throw "Not a list";
}
parse.end = parse.index;
return parse.OK;
}
/* ==================== getNumLines ===================================== */
this.getNumLines = function (str) {
var my_parser = new R.Parser("unknown", 1, str);
var had_eol = 0;
my_parser.cur = my_parser.text.charAt(my_parser.index);
while (true) {
if (my_parser.len == 0) {
break;
}
if (my_parser.cur == '\n') {
had_eol++;
}
my_parser.feedchar();
}
return had_eol;
}
/* ==================== parseExprOperator ===================================== */
this.parseExprOperator = function () {
var parse = this;
//print("parseExprOperator!"+parse.index+"!"+parse.start+"!"+parse.text.substring(parse.index)+"!");
if (parse.expr_parser) {
/* FIXME !!! need to determine length of the following checked string parts !! */
if (parse.isInteger.test(parse.text.substr(parse.index))) {
parse.token = parse.TOKEN_INTEGER;
} else {
if (parse.isReal.test(parse.text.substr(parse.index))) {
parse.token = parse.TOKEN_REAL;
} else {
if (parse.isHex.test(parse.text.substr(parse.index))) {
parse.token = parse.TOKEN_HEX;
} else {
if (parse.isOctal.test(parse.text.substr(parse.index))) {
parse.token = parse.TOKEN_OCTAL;
} else {
//print("parseExprOp cur!"+this.cur+"!");
switch (parse.cur) {
case '+':
parse.token = parse.TOKEN_PLUS;
break;
case '-':
parse.token = parse.TOKEN_MINUS;
break;
case '*':
parse.token = parse.TOKEN_MUL;
break;
case '/':
parse.token = parse.TOKEN_DIV;
break;
case '%':
parse.token = parse.TOKEN_MOD;
break;
case '!':
if ((parse.len > 1) && (parse.text.charAt(parse.index+1) == '=')) {
parse.feedchar();
parse.token = parse.TOKEN_NE;
} else {
parse.token = parse.TOKEN_NOT;
}
break;
case '=':
if ((parse.len > 1) && (parse.text.charAt(parse.index+1) == '=')) {
parse.feedchar();
parse.token = parse.TOKEN_EQ;
}
break;
case '<':
if ((parse.len > 1) && (parse.text.charAt(parse.index+1) == '=')) {
parse.feedchar();
parse.token = parse.TOKEN_LE;
} else {
parse.token = parse.TOKEN_LT;
}
break;
case '>':
if ((parse.len > 1) && (parse.text.charAt(parse.index+1) == '=')) {
parse.feedchar();
parse.token = parse.TOKEN_GE;
} else {
parse.token = parse.TOKEN_GT;
}
break;
case '&':
if ((parse.len > 1) && (parse.text.charAt(parse.index+1) == '&')) {
parse.feedchar();
parse.token = parse.TOKEN_AND_IF;
} else {
parse.token = parse.TOKEN_AND;
}
break;
case '|':
if ((tparse.len > 1) && (parse.text.charAt(parse.index+1) == '|')) {
parse.feedchar();
parse.token = parse.TOKEN_OR_IF;
} else {
parse.token = parse.TOKEN_OR;
}
break;
case '^':
parse.token = parse.TOKEN_EXOR;
break;
case '(':
parse.token = parse.TOKEN_LP;
break;
case ')':
parse.token = parse.TOKEN_RP;
break;
case 'e':
if (parse.len > 1) {
if (parse.text.charAt(parse.index+1) == 'q') {
parse.feedchar();
parse.token = parse.TOKEN_STR_EQ;
} else {
return parse.parseString();
}
}
break;
case 'n':
if (parse.len > 1) {
if (parse.text.charAt(parse.index+1) == 'e') {
parse.feedchar();
parse.token = parse.TOKEN_STR_NE;
} else {
if (parse.text.charAt(parse.index+1) == 'i') {
parse.feedchar();
parse.token = parse.TOKEN_STR_NI;
} else {
return parse.parseString();
}
}
} else {
return parse.parseString();
}
break;
case 'i':
if (parse.len > 1) {
if (parse.text.charAt(parse.index+1) == 'n') {
parse.feedchar();
parse.token = parse.TOKEN_STR_IN;
} else {
parse.no_expr_parsing = true;
return parse.parseString();
}
} else {
return parse.parseString();
}
break;
default:
//print("expr op str!"+parse.text.substring(parse.index)+"!");
if (parse.isDecimal.test(parse.text.substr(parse.index))) {
parse.token = parse.TOKEN_DECIMAL;
} else {
return parse.parseString();
}
}
}
}
}
}
}
parse.feedchar();
parse.end = parse.index - 1;
if (parse.debug) {
print(" parseExprOperator!"+parse.getText()+"!"+parse.getTokenString(parse.token)+"!");
}
return parse.OK;
}
};
Parser.prototype.constructor = Parser;
R.Parser = Parser;
}, "0.0.1", {});