/*====================================================
 * 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) {

/* =============================== Parse ================================== */

function Parser() {
  R.log('constructor called', 'life', 'Parser', true);
  
  // kweight
  var parse = this;
  var constructor = parse.constructor;
  Parser.superclass.constructor.apply(parse, arguments);

  parse.debug       = 0;
  parse.text        = null;
  parse.orig_text   = null;
  parse.start       = 0;
  parse.end         = 0;
  parse.brace_level = 0;
  parse.in_braces   = 0;
  parse.is_command  = 0;
  parse.insidequote = false;
  parse.no_expr_parsing = false;
  parse.was_in_quote = false;
  parse.index       = 0;
  parse.len         = 0;
  parse.token       = parse.TOKEN_EOL;
  parse.cur         = null;
  parse.expand_level = 0;
  parse.expand_parsers = new Array();
  parse.array_parts = null;
  parse.nsParts = new Array();
  parse.expand_type = 0;
  parse.interp = null;
  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.expr_parser = false;
  parse.last_index = 0;
  parse.last_token = 0;
  parse.line_no = 1;
  parse.had_word_sep = false;
  parse.quoted_str_comment = false;
  parse.meta = new R.Meta();
  R.Base.parser_cnt++;
  parse.cnt = R.Base.parser_cnt;
  parse.my_name = "RaplParser";

  R.log('constructor end', 'life', 'Parser', true);
}

R.extend(Parser, R.Token, {
  /* =============================== parse ================================== */
  parse: function(file_name, line_no, code) {
    var parse = this;
print("PARSE!"+file_name+"!"+line_no+"!"+parse.mySelf()+"!"+code+"!");
    parse.text = code;
    parse.orig_text = code;
    parse.len = code.length;

    var result = parse.getToken();
    var file_id = parse.meta.addFileName(file_name, file_name.length);
    var word_start_id = 0;
    var id = 0;
    while (result == parse.OK) {
print("result!"+parse.getResultString(result)+"!"+parse.getTokenString(parse.token)+"!"+parse.getText()+"!");
      id = parse.meta.addWord(parse.token, id, parse.getText(), parse.getText().length, parse.line_no);
      if (parse.token == parse.TOKEN_EOF) {
        break;
      }
      if (parse.token == parse.TOKEN_EOL) {
        parse.meta.addStatement(word_start_id, parse.meta.words_len - 1, file_id);
	word_start_id = parse.meta.words_len;
      }
      tok = parse.getToken();
    }
    parse.meta.dumpCharacters();
    parse.meta.dumpWords();
    parse.meta.dumpFileNames();
    parse.meta.dumpStatements();
    return parse.meta;
  },

  /* ==================== parseString ===================================== */
  parseString: function () {
    var parse = this;
if (parse.debug > 1) {
print("parseString1!"+parse.cur+"!type!"+parse.getTokenString(parse.token)+"!"+parse.text.substring(parse.index, parse.index+20)+"!");
}
    var last_ch = "";
    parse.was_in_quote = false;
    var newword = (parse.token == parse.TOKEN_WORD_SEP ||
                   parse.token == parse.TOKEN_EOL ||
		   parse.token == parse.TOKEN_STR ||
		   parse.token == parse.TOKEN_EXPAND ||
		   parse.token == parse.TOKEN_COMMENT ||
		   parse.token == parse.TOKEN_VAR);
    if ((parse.token == parse.TOKEN_EOL) && (parse.cur == "#")) {
      return parse.parseComment();
    }
    if ((newword || (parse.token == parse.TOKEN_ESC)) && parse.cur == "{") {
      return parse.parseBrace();
    } else {
      if (newword && parse.cur == "(") {
        var result = parse.parseParen();
        parse.token = parse.TOKEN_LP;
	return result;
      } else {
        if (newword && parse.cur == '"') {
	  if (parse.insidequote) {
            parse.start = parse.index+1;
            parse.end   = parse.index;
            parse.token = parse.TOKEN_ESC;
            parse.feedchar();
	    parse.was_in_quote = true;
            parse.insidequote = false;
if (parse.debug) {
print("  parseStr0 ESC!"+parse.getText()+"!start!"+parse.start+"!end!"+parse.end+"!index ch!"+parse.text.charAt(parse.index)+"!");
}
            return parse.OK;
          } else {
            parse.insidequote = true;
            parse.feedchar();
          }
        }
      }
    }
//print("STRWH!"+parse.start+"!"+parse.text.substring(parse.index, parse.index+20)+"!");
    parse.start = parse.index;
    while (true) {
if (parse.debug) {
print("    cur!"+parse.cur+"!len!"+parse.len+"!index!"+parse.index+"!insideq!"+parse.insidequote+"!last_ch!"+last_ch+"!");
}
      if (parse.len == 0) {
        parse.end = parse.index-1;
        parse.token = parse.TOKEN_ESC;
if (parse.debug) {
print("  parseStr1 ESC!"+parse.getText()+"!");
}
        return parse.OK;
      }
      if (parse.cur == '\n' && !parse.insidequote && (last_ch != "\\")) {
        parse.end  = parse.index-1;
        parse.token = parse.TOKEN_ESC;
if (parse.debug) {
print("  parseStr3a ESC!"+parse.getText()+"!"+parse.start+"!"+parse.end+"!"+parse.text.charAt(parse.index)+"!");
}
        return parse.OK;
      }
      if (parse.cur == "\\") {
	last_ch = parse.cur;
	if (parse.insidequote) {
	  parse.feedchar();
        } else {
          if (parse.len >= 2) {
            parse.feedSequence();
	  }
	}
      } else {
        if (!parse.expr_parser && (parse.cur == "(")) {
          // FIXME !!! eventually temporary!!
	  // if we have white space in the parsed part, return the white space
	  // and then start parsing again for the this.TOKEN_ARRAY_NAME
	  if (" \t".indexOf(parse.text.charAt(parse.start)) >= 0) {
	    i = parse.start;
	    while (" \t".indexOf(parse.text.charAt(i)) >= 0) {
              i++;
	    }
	    parse.end = i - 1;
	    parse.index = i;
	    parse.cur = parse.text.charAt(parse.index);
	    parse.token = parse.TOKEN_ESC;
//print("WS!"+parse.cur+"!"+parse.getText()+"!"+parse.text.substring(parse.index, parse.index+20)+"!");
	    return parse.OK;
	  }
          parse.end = parse.index - 1;
          // FIXME !! avoid wrong part1 when last token was a this.TOKEN_ARRAY* !!
          parse.token = parse.TOKEN_ESC;
          parse.var_name_part1 = parse.getText();
          result = parse.parseParen();
          parse.var_name_part2 = parse.getText();
          parse.token = parse.TOKEN_ARRAY_NAME;
	  return parse.OK;
	}
        if (parse.curr_escape_chars.indexOf(parse.cur)>=0) {
if (parse.debug) {
print("  parseStr1a ESC!"+"!cur!"+parse.cur+"!");
}
          if ("$[{".indexOf(parse.cur)>=0) {
            if (last_ch != "\\") {
              parse.end = parse.index-1;
              parse.token = parse.TOKEN_ESC;
if (parse.debug) {
print("  parseStr2a ESC!"+parse.getText()+"!cur!"+parse.cur+"!type!"+parse.token+"!");
}
              return parse.OK;
            }
	  }
          if (!parse.insidequote && !parse.no_expr_parsing) {
if (parse.debug) {
print("  parseStr1b ESC!"+"!cur!"+parse.cur+"!"+'$[('.indexOf(parse.cur)+"!");
}
            var do_return = true;
            if (parse.expr_parser) {
              /* check if it is on of i/e/n and then if it is one of the expr keywords
	       * in/ni/eq/ne. If not no break necessary
	       */
	      if (parse.len > 0) {
                switch (parse.cur) {
                case "e":
                  if (parse.text.charAt(parse.index+1) != "q") {
                    do_return = false;
                  }
                  break;
                case "i":
                  if (parse.text.charAt(parse.index+1) != "n") {
                    do_return = false;
                  }
                  break;
                case "n":
                  if ((parse.text.charAt(parse.index+1) != "e") && (parse.text.charAt(parse.index+1) != "i")) {
                    do_return = false;
                  }
                  break;
                }
	      }
            }
            if (do_return && (last_ch != "\\")) {
              parse.end = parse.index-1;
              parse.token = parse.TOKEN_ESC;
if (parse.debug) {
print("  parseStr2 ESC!"+parse.getText()+"!cur!"+parse.cur+"!type!"+parse.getTokenString(parse.token)+"!");
}
              return parse.OK;
            }
          }
        } else {
	  if (parse.cur == '"' && parse.insidequote && (last_ch != "\\")) {
            parse.end  = parse.index-1;
            parse.token = parse.TOKEN_ESC;
            parse.feedchar();
	    parse.was_in_quote = true;
            parse.insidequote = false;
if (parse.debug) {
print("  parseStr3 ESC!"+parse.getText()+"!"+parse.start+"!"+parse.end+"!"+parse.text.charAt(parse.index)+"!");
}
            return parse.OK;
          }
	}
        last_ch = parse.cur;
        parse.feedchar();
      }
    }
if (parse.debug) {
print("  parseStr4!"+parse.getText()+"!");
}
    return parse.OK;
  },

  /* ==================== parseWordSep ===================================== */
  parseWordSep: function () {
    var parse = this;
    parse.start = parse.index;
    parse.had_word_sep = true;
    var had_eol = 0;
    var had_backslash = false;
    while (" \t\r\n\\".indexOf(parse.cur)>=0) {
      had_back_slash = false;
      if (parse.cur == '\\') {
        if (parse.len > 0) {
          if ((parse.text.charAt(parse.index+1) == '\r') || (parse.text.charAt(parse.index+1) == '\n')) {
            had_back_slash = true;
            parse.feedchar();
	  } else {
            break;
	  }
        } else {
          break;
        }
      }
      if (parse.cur == '\n') {
	if (! had_back_slash) {
          had_eol = 1;
        }
	parse.line_no++;
      }
      parse.feedchar();
    }
    parse.end  = parse.index - 1;
    if (had_eol) {
      parse.token = parse.TOKEN_EOL;
    } else {
      parse.token = parse.TOKEN_WORD_SEP;
    }
if (parse.debug) {
print("  parseWordSEP!"+parse.getText()+"!");
}
    return parse.OK;
  },

  /* ==================== parseEol ===================================== */
  parseEol: function () {
    var parse = this;
    parse.start = parse.index;
    while(1) {
      if (parse.cur == "\n") {
        parse.line_no++;
      }
      switch(parse.cur) {
      case " ":
      case ";":
      case "\t":
      case "\n":
      case "\r":
        parse.feedchar();
        continue;
      }
      break;
    }
    parse.end = parse.index - 1;
    parse.token = parse.TOKEN_EOL;
if (parse.debug ) {
print("  parseEol!");
print("  parseEol!"+parse.getText()+"!"+parse.cur+"!"+parse.len+"!");
}
    return this.OK;
  },

  /* ==================== parseCommand ===================================== */
  parseCommand: function () {
    var level = 1;
    var blevel = 0;
    var parse = this;
    parse.feedcharstart();
    while (true) {
      if (parse.len == 0) {
        break;
      }
      if (parse.cur == "[" && blevel == 0) {
        level++;
      } else {
        if (parse.cur == "]" && blevel == 0) {
          level--;
          if (level == 0) {
	    break;
	  }
        } else {
	  if (parse.cur == "\\") {
            if (blevel == 0) {
              if (parse.cur == "]" && blevel == 0) {
                level--;
                if (level == 0) {
	          break;
	        }
	      }
	    }
          } else {
	    if (parse.cur == "{") {
              blevel++;
            } else {
	      if (parse.cur == "}") {
                if (blevel != 0) {
		  blevel--;
		}
	      }
	    }
	  }
        }
      }
      parse.feedchar();
    }
    parse.end  = parse.index-1;
    parse.token = parse.TOKEN_CMD;
    if (parse.cur == "]") {
      parse.feedchar();
    }
if (parse.debug) {
print("  parseCommand!"+parse.getText()+"!");
}
    return parse.OK;
  },

  /* ==================== parseVar ===================================== */
  parseVar: function () {
    var result = this.OK;
    var have_brace = 0;
    var match_str2 = null;
    var parse = this;
    parse.feedcharstart();
//print("parseVar!"+parse.cur+"!");
    if (parse.cur == "{") {
      parse.feedcharstart();
      while (parse.cur != "}") {
        parse.feedchar();
      }
      parse.end = parse.index-1;
      if (parse.text.charAt(parse.index+1) == "(") {
        parse.var_name_part1 = parse.getText();
        parse.feedchar();
        result = parse.parseParen();
        parse.var_name_part2 = parse.getText();
        parse.token = parse.TOKEN_VAR_ARRAY_NAME;
      } else {
        parse.end = parse.index-1;
        parse.token = parse.TOKEN_BRACED_VAR;
        parse.feedchar();
      }
if (parse.debug) {
print("  parseVarBrace!"+parse.getText()+"!"+parse.getTokenString(parse.token)+"!");
}
      return parse.OK;
    }
    while(1) {
      var sub_str;
      var found = 0;
      var match_str;
      var match_len = 0;
      sub_str  = parse.text.substring(parse.index);
      match_str = parse.text.substring(parse.index).match(parse.getVar);
      if (match_str != null) {
	found = 1;
	match_len = match_str.toString().length-1;
        parse.end = parse.index + match_len;
	parse.len = parse.len - match_len - 1;
	parse.index = parse.end+1;
	parse.cur = parse.text.charAt(parse.index);
      }
      sub_str  = parse.text.substring(parse.index);
      if ((parse.cur == ":") && (parse.text.charAt(parse.index+1) == ":")) {
	found = 1;
	parse.feedchar();
	parse.feedchar();
        parse.end = parse.index-1;
      }
      sub_str  = parse.text.substring(parse.index);
      if (!found) {
        break;
      }
    }
    parse.cur = parse.text.charAt(parse.index);
    if (parse.cur == "(") {
      parse.var_name_part1 = parse.getText();
      result = parse.parseParen();
      parse.var_name_part2 = parse.getText();
      parse.token = parse.TOKEN_VAR_ARRAY;
    } else {
//      if (parse.end == parse.index-1) {  // what is that for ??? a single DOLLAR ?
//        parse.end = --parse.index;
//        parse.token = parse.STR;
//      } else {
	if (have_brace == 0) {
          parse.token = parse.TOKEN_VAR;
        }
//      }
    }
if (parse.debug) {
print("  parseVar!"+parse.getText()+"!"+parse.token+"!");
}
    return parse.OK;
  },

  /* ==================== parseBrace ===================================== */
  parseBrace: function () {
    var level = 1;
    var parse = this;
    parse.feedcharstart();
if (parse.debug) {
print("  parseBrace start!"+parse.cur+"!"+parse.text.substring(parse.start, parse.start+40)+"!");
}
    while (true) {
if (parse.debug) {
print("LEN!"+parse.len+"!"+parse.cur+"!"+parse.index+"!");
}
      if (parse.cur == "\n") {
        parse.line_no++;
      }
      if (parse.len == 0 || parse.cur == "}") {
        level--;
        if (level == 0 || parse.len == 0) {
          parse.end = parse.index-1;
          if (parse.len > 0) {
            parse.feedchar();
            /* check for EXPAND operator {*} */
            if ((parse.text.charAt(parse.end) == "*") && (parse.start == parse.end)) {
              parse.token = parse.TOKEN_EXPAND;
            } else {
              parse.token = parse.TOKEN_LC;
            }
          }
if (parse.debug) {
print("LEN2!"+parse.len+"!"+parse.cur+"!"+parse.index+"!");
print("CUR!"+parse.cur+"!"+parse.text.substring(parse.start, parse.start+40)+"!");
print("  parseBrace!"+parse.getText()+"!"+parse.token+"!"+parse.index+"!"+parse.len+"!"+parse.end+"!");
}
          return parse.OK;
        }
      } else {
        if (parse.cur == "{") {
          level++;
        }
      }
      parse.feedchar();
    }
if (parse.debug) {
print("  parseBrace!"+parse.getText()+"!");
}
    return parse.OK; // unreached
  },

  /* ==================== parseParen ===================================== */
  parseParen: function () {
    var level = 1;
    var parse = this;
    parse.feedcharstart();
    while (true) {
      if (parse.len > 1 && parse.cur == "\\") {
        parse.feedSequence();
      } else {
	if ((parse.len == 0) || (parse.cur == ")")) {
          level--;
          if (level == 0 || parse.len == 0) {
            parse.end = parse.index-1;
            if (parse.len > 0) {
	      parse.feedchar();
              parse.token = parse.TOKEN_LP;
	    }
if (parse.debug) {
print("  parseParen!"+parse.getText()+"!"+parse.text.charAt(parse.index)+"!"+parse.cur+"!");
}
            return parse.OK;
          }
        } else {
	  if (parse.cur == "(") {
	    level++;
	  }
	}
      }
      parse.feedchar();
    }
    return parse.OK; // unreached
  },

  /* ==================== parseComment ===================================== */
  parseComment: function () {
    var parse = this;
    parse.quoted_str_comment = false;
    parse.start = parse.index;
    while (1) {
      switch (parse.cur) {
      case "\n":
      case "\r": 
if (parse.debug) {
print("  parseComment1!"+parse.insidequote+"!"+parse.text.substring(parse.index, parse.index+20)+"!");
}
        parse.token = parse.TOKEN_COMMENT;
	parse.end = parse.index -1;
        return parse.OK;
      case "\\":
        parse.feedSequence();
	break;
      default: 
        parse.feedchar();
	break;
      }
    }
if (parse.debug) {
print("  parseComment2");
}
    return parse.OK; // unreached
  },

  /* ==================== getText ===================================== */
  getText: function () {
    var parse = this;
//print("getText!"+parse.getTokenString(parse.token)+"!");
    if ((parse.token == parse.TOKEN_VAR_ARRAY) || (parse.token == parse.TOKEN_VAR_ARRAY_NAME) || (parse.token == parse.TOKEN_ARRAY_NAME)) {
      return parse.var_name_part1+"("+parse.var_name_part2+")";
    } else {
//print("getText text!"+parse.text.substring(parse.start,parse.end+1)+"!type!"+parse.getTokenString(parse.token)+"!");
      return parse.text.substring(parse.start, parse.end + 1);
    }
  },

  /* ==================== getToken ===================================== */
  getToken: function () {
    var parse = this;
//print("getToken!"+parse.getTokenString(parse.token)+"!"+parse.len+"!"+parse.cur+"!");
    while (true) {
//print("getToken 0!"+parse.len+"!cur!"+parse.cur+"!tok!"+parse.getTokenString(parse.token)+"!");
      if (parse.len == 0) {
//print("getToken2 len == 0 type!"+parse.token+"!"+parse.start+"!end!"+parse.end+"!index!"+parse.index+"!text!"+parse.text+"!");
        /* parse.TOKEN_ESC is for a statement without a "\n" at the end !! */
        if ((parse.token == parse.TOKEN_EOL) || (parse.token == parse.TOKEN_ESC)) {
          parse.end = parse.start-1;
	  parse.token = parse.TOKEN_EOF;
	}
        if (parse.token != parse.TOKEN_EOF) {
	  parse.token = parse.TOKEN_EOL;
	}
        return parse.OK;
      }
//print("get_token cur!"+parse.cur+"!");
      var had_word_sep = parse.had_word_sep;
      /* reset it */
      parse.had_word_sep = false;
      switch (parse.cur) {
      case ' ':
      case '\t':
        if (parse.insidequote) {
	  return parse.parseString();
	}
        return parse.parseWordSep();
      case '\n':
      case '\r':
      case ';':
        if (parse.insidequote) {
	  return parse.parseString();
	}
        return parse.parseEol();
      case '[':
        return parse.parseCommand();
      case '$':
        return parse.parseVar();
      case '(':
	if (!parse.expr_parser) {
          return parse.parseParen();
	}
	/* fall through ! */
      default:
        if (parse.cur == '#') {
          if (parse.token == parse.TOKEN_EOL) {
            return parse.parseComment();
	  }
	}
        if (parse.expr_parser) {
          parse.start = parse.index;
          if (parse.expr_operator_chars.indexOf(parse.cur)>=0) {
            if (parse.insidequote) {
	      var result = parse.parseString();
if (parse.debug) {
print("expr str!"+parse.getText()+"!"+parse.token+"!");
}
	      return result;
	    }
            var result =  parse.parseExprOperator();
//print("get_token parseExprOperator!"+parse.cur+"!"+parse.getText()+"!"+parse.token+"!");
	    parse.last_index = parse.index;
	    return result;
	  }
	}
      }
      if ((parse.cur == "#") && (parse.token == parse.TOKEN_EOL)) {
        parse.parseComment();
        return result;
      }
      var result = parse.parseString();
      if (parse.token == parse.TOKEN_EXPAND) {
//print("getToken expand end!"+parse.getText()+"!"+parse.text.substring(parse.index)+"!start!"+parse.start+"!end!"+parse.end+"!index!"+parse.index+"!");
        return parse.OK;
      }
//print("getToken end!"+parser.getText()+"!"+parser.type+"!"+parser.expand_level+"!"+result+"!");
      parse.last_index = parse.index;
      return result;
    }
    return parse.OK; // unreached
  },

  /* ==================== feedSequence ===================================== */
  feedSequence: function () {
    var parse = this;
    if (parse.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.text = head+" "+tail;
      parse.len--;
      parse.cur = parse.text.charAt(parse.index);
//print("FEED escaped LF2!"+parse.cur+"!"+parse.text.substring(parse.index)+"!"+parse.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.text.substring(0, parse.index);
    var tail = parse.text.substring(parse.index + 1);
    parse.text = head+cur+tail;
    parse.cur = parse.text.charAt(parse.index);
  },

  /* ==================== steal ===================================== */
  steal: function (n) {
    var parse = this;
    var tail = parse.text.substring(parse.index+1);
    var word = tail.substr(0, n);
    parse.text = parse.text.substring(0, parse.index) + tail.substring(n-1);
    parse.len = parse.len - n;
    return word;
  },

  /* ==================== feedcharstart ===================================== */
  feedcharstart: function () {
    var parse = this;
    parse.feedchar();
    parse.start = parse.index;
  },

  /* ==================== setPos ===================================== */
  setPos: function (index) {
    var parse = this;
    var d = index - parse.index;
    parse.index = index;
    parse.len -= d;
    parse.cur = parse.text.charAt(parse.index);
  },

  /* ==================== feedchar ===================================== */
  feedchar: function () {
    var parse = this;
    parse.index++;
    parse.len--;
    if (parse.len < 0) {
      throw "End of file reached";
    }
    parse.cur = parse.text.charAt(parse.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;
    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", {});


