RAPL

Artifact [2756381d5d]
Login

Artifact [2756381d5d]

Artifact 2756381d5de4718ee04335184a0d75f2ac868bad:


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

  this.debug       = 0;
  this.text        = null;
  this.orig_text   = null;
  this.start       = 0;
  this.end         = 0;
  this.brace_level = 0;
  this.in_braces   = 0;
  this.is_command  = 0;
  this.insidequote = false;
  this.no_expr_parsing = false;
  this.was_in_quote = false;
  this.index       = 0;
  this.len         = 0;
  this.token        = this.TOKEN_EOL;
  this.cur         = null;
  this.expand_level = 0;
  this.expand_parsers = new Array();
  var var_name_part1 = null;
  var var_name_part2 = null;
  this.array_parts = null;
  this.nsParts = new Array();
  this.expand_type = 0;
  this.interp = null;
  this.escape_chars = "$[{ \t\n\r;";
  this.expr_operator_chars = "+->=<!*/%&|()eniNI";
  this.expr_escape_chars = "$[{ \t\n\r;"+this.expr_operator_chars;
  this.curr_escape_chars = this.escape_chars;
  this.expr_parser = false;
  this.last_index = 0;
  this.last_token = 0;
  this.line_no = 1;
  this.had_word_sep = false;
  this.quoted_str_comment = false;
  this.meta = new R.Meta();
  R.Base.parser_cnt++;
  this.parser_cnt = R.Base.parser_cnt;
var obj = new R.RaplObject(null, null, "test");
print("obj!"+obj.OBJECT_TYPE_STMT+"!"+obj.objectTypes2String[obj.OBJECT_TYPE_LIST]+"!"+obj.objectTypeStrings2ObjectType["EXPR_TREE"]+"!");
for (var z in obj.objectTypes2String) {
print("z!"+z+"!");
}
var cf = new R.Callframe(null, null);
print("cf!"+cf.mySelf()+"!");
var v1 = new R.Variable(null, "v1", "VAR_NAMESPACE_VAR VAR_UNDEFINED");
print("v1!"+v1.mySelf()+"!");
var ns1 = new R.Namespace(null, "::ns1", null);
print("ns1!"+ns1.mySelf()+"!");

  /* ==================== mySelf ===================================== */
  this.mySelf = function () {
    return "RaplParser!"+this.parser_cnt;
  }

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

}

R.extend(Parser, R.Token, {
  /* =============================== parse ================================== */
  parse: function(file_name, code) {
print("PARSE!"+file_name+"!"+code+"!");
    this.text = code;
    this.orig_text = code;
    this.len = code.length;
print("call getToken");
    var result = this.getToken();
    var file_id = this.meta.addFileName(file_name, file_name.length);
    var word_start_id = 0;
    var id = 0;
    while (result == this.OK) {
print("result!"+this.getResultString(result)+"!"+this.getTokenString(this.token)+"!"+this.getText()+"!");
print("IS!"+this.getText()+"!"+this.isVarName.test(this.getText())+"!");
      id = this.meta.addWord(this.token, id, this.getText(), this.getText().length, this.line_no);
//      this.meta.addCharacters(this.getText(), this.getText().length);
      if (this.token == this.TOKEN_EOF) {
        break;
      }
      if (this.token == this.TOKEN_EOL) {
        this.meta.addStatement(word_start_id, this.meta.words_len - 1, file_id);
	word_start_id = this.meta.words_len;
      }
      tok = this.getToken();
    }
    this.meta.dumpCharacters();
    this.meta.dumpWords();
    this.meta.dumpFileNames();
    this.meta.dumpStatements();
    return "";
  },

  /* ==================== parseString ===================================== */
  parseString: function () {
if (this.debug > 1) {
print("parseString1!"+this.cur+"!type!"+this.getTokenString(this.token)+"!"+this.text.substring(this.index, this.index+20)+"!");
}
    var last_ch = "";
    this.was_in_quote = false;
    var newword = (this.token==this.TOKEN_WORD_SEP ||
                   this.token == this.TOKEN_EOL ||
		   this.token == this.TOKEN_STR ||
		   this.token == this.TOKEN_EXPAND ||
		   this.token == this.TOKEN_COMMENT ||
		   this.token == this.TOKEN_VAR);
    if ((this.token == this.TOKEN_EOL) && (this.cur == "#")) {
      return this.parseComment();
    }
    if ((newword || (this.token == this.TOKEN_ESC)) && this.cur == "{") {
      return this.parseBrace();
    } else {
      if (newword && this.cur == "(") {
        var result = this.parseParen();
        this.token = this.TOKEN_LP;
	return result;
      } else {
        if (newword && this.cur == '"') {
	  if (this.insidequote) {
            this.start = this.index+1;
            this.end   = this.index;
            this.token  = this.TOKEN_ESC;
            this.feedchar();
	    this.was_in_quote = true;
            this.insidequote = false;
if (this.debug) {
print("  parseStr0 ESC!"+this.getText()+"!start!"+this.start+"!end!"+this.end+"!index ch!"+this.text.charAt(this.index)+"!");
}
            return this.OK;
          } else {
            this.insidequote = true;
            this.feedchar();
          }
        }
      }
    }
//print("STRWH!"+this.start+"!"+this.text.substring(this.index, this.index+20)+"!");
    this.start = this.index;
    while (true) {
if (this.debug) {
print("    cur!"+this.cur+"!len!"+this.len+"!index!"+this.index+"!insideq!"+this.insidequote+"!last_ch!"+last_ch+"!");
}
      if (this.len == 0) {
        this.end = this.index-1;
        this.token = this.TOKEN_ESC;
if (this.debug) {
print("  parseStr1 ESC!"+this.getText()+"!");
}
        return this.OK;
      }
      if (this.cur == '\n' && !this.insidequote && (last_ch != "\\")) {
        this.end  = this.index-1;
        this.token = this.TOKEN_ESC;
if (this.debug) {
print("  parseStr3a ESC!"+this.getText()+"!"+this.start+"!"+this.end+"!"+this.text.charAt(this.index)+"!");
}
        return this.OK;
      }
      if (this.cur == "\\") {
	last_ch = this.cur;
	if (this.insidequote) {
	  this.feedchar();
        } else {
          if (this.len >= 2) {
            this.feedSequence();
	  }
	}
      } else {
        if (!this.expr_parser && (this.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(this.text.charAt(this.start)) >= 0) {
	    i = this.start;
	    while (" \t".indexOf(this.text.charAt(i)) >= 0) {
              i++;
	    }
	    this.end = i - 1;
	    this.index = i;
	    this.cur = this.text.charAt(this.index);
	    this.token = this.TOKEN_ESC;
//print("WS!"+this.cur+"!"+this.getText()+"!"+this.text.substring(this.index, this.index+20)+"!");
	    return this.OK;
	  }
          this.end = this.index - 1;
          // FIXME !! avoid wrong part1 when last token was a this.TOKEN_ARRAY* !!
          this.token = this.TOKEN_ESC;
          this.var_name_part1 = this.getText();
          result = this.parseParen();
          this.var_name_part2 = this.getText();
          this.token = this.TOKEN_ARRAY_NAME;
	  return this.OK;
	}
        if (this.curr_escape_chars.indexOf(this.cur)>=0) {
if (this.debug) {
print("  parseStr1a ESC!"+"!cur!"+this.cur+"!");
}
          if ("$[{".indexOf(this.cur)>=0) {
            if (last_ch != "\\") {
              this.end = this.index-1;
              this.token = this.TOKEN_ESC;
if (this.debug) {
print("  parseStr2a ESC!"+this.getText()+"!cur!"+this.cur+"!type!"+this.token+"!");
}
              return this.OK;
            }
	  }
          if (!this.insidequote && !this.no_expr_parsing) {
if (this.debug) {
print("  parseStr1b ESC!"+"!cur!"+this.cur+"!"+'$[('.indexOf(this.cur)+"!");
}
            var do_return = true;
            if (this.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 (this.len > 0) {
                switch (this.cur) {
                case "e":
                  if (this.text.charAt(this.index+1) != "q") {
                    do_return = false;
                  }
                  break;
                case "i":
                  if (this.text.charAt(this.index+1) != "n") {
                    do_return = false;
                  }
                  break;
                case "n":
                  if ((this.text.charAt(this.index+1) != "e") && (this.text.charAt(this.index+1) != "i")) {
                    do_return = false;
                  }
                  break;
                }
	      }
            }
            if (do_return && (last_ch != "\\")) {
              this.end = this.index-1;
              this.token = this.TOKEN_ESC;
if (this.debug) {
print("  parseStr2 ESC!"+this.getText()+"!cur!"+this.cur+"!type!"+this.getTokenString(this.token)+"!");
}
              return this.OK;
            }
          }
        } else {
	  if (this.cur == '"' && this.insidequote && (last_ch != "\\")) {
            this.end  = this.index-1;
            this.token = this.TOKEN_ESC;
            this.feedchar();
	    this.was_in_quote = true;
            this.insidequote = false;
if (this.debug) {
print("  parseStr3 ESC!"+this.getText()+"!"+this.start+"!"+this.end+"!"+this.text.charAt(this.index)+"!");
}
            return this.OK;
          }
	}
        last_ch = this.cur;
        this.feedchar();
      }
    }
if (this.debug) {
print("  parseStr4!"+this.getText()+"!");
}
    return this.OK;
  },

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

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

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

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

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

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

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

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

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

  /* ==================== feedSequence ===================================== */
  feedSequence: function () {
    if (this.cur != "\\") {
      throw "Invalid escape sequence";
    }
    var cur = this.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 = this.steal(4);
      if (hex != this.isHexSeq.exec(hex)) {
        throw "Invalid unicode escape sequence: "+hex;
      }
      cur = String.fromCharCode(parseInt(hex,16));
      break;
    case 'x':
      var hex = this.steal(2);
      if (hex != this.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!"+this.text.substring(this.index)+"!"+this.text+"!");
      var tail = this.text.substring(this.index+1);
      var head = this.text.substring(0,this.index-1);
      this.text = head+" "+tail;
      this.len--;
      this.cur = this.text.charAt(this.index);
//print("FEED escaped LF2!"+this.cur+"!"+this.text.substring(this.index)+"!"+this.text+"!");
      return;
    default:
      if ("0123456789".indexOf(cur) >= 0) {
        cur = cur + this.steal(2);
        if (cur != this.isOctalSeq.exec(cur)) {
          throw "Invalid octal escape sequence: "+cur;
	}
        cur = String.fromCharCode(parseInt(cur, 8));
      }
      break;
    }
    var head = this.text.substring(0,this.index);
    var tail = this.text.substring(this.index+1);
    this.text = head+cur+tail;
    this.cur = this.text.charAt(this.index);
  },

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

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

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

  /* ==================== feedchar ===================================== */
  feedchar: function () {
    this.index++;
    this.len--;
    if (this.len < 0) {
      throw "End of file reached";
    }
    this.cur = this.text.charAt(this.index);
  }

});

function PP () {

  /* ==================== setExprParser ===================================== */
  this.setExprParser = function () {
    this.curr_escape_chars = this.expr_escape_chars;
    this.expr_parser = true;
  }

  /* ==================== parseList ===================================== */
  this.parseList = function () {
    var level = 0;
    this.start = this.index;
    while (true) {
      if (this.len == 0) {
        this.end = this.index;
        this.token = this.TOKEN_EOL;
        return;
      }
      switch (this.cur) {
      case "\\":
        if (this.len >= 2) {
          this.feedSequence();
	}
        break;
      case " ": 
      case "\t":
      case "\n":
      case "\r":
        if (level > 0) {
          break;
	}
        this.end  = this.index - 1;
        this.token = this.TOKEN_WORD_SEP;
        this.feedchar();
        return;
      case '{':
        level++;
        break;
      case '}':
        level--;
        break;
      }
      this.feedchar();
    }
    if (level != 0) {
      throw "Not a list";
    }
    this.end = this.index;
    return;
  }

  /* ==================== getNumLines ===================================== */
  this.getNumLines = function (str) {
    var my_parser = new TclParser(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 () {
//print("parseExprOperator!"+this.index+"!"+this.start+"!"+this.text.substring(this.index)+"!");
    if (this.expr_parser) {
      /* FIXME !!! need to determine length of the following checked string parts !! */
      if (this.isInteger.test(this.text.substr(this.index))) {
        this.token = this.TOKEN_INTEGER;
      } else {
        if (this.isReal.test(this.text.substr(this.index))) {
          this.token = this.TOKEN_REAL;
        } else {
          if (this.isHex.test(this.text.substr(this.index))) {
            this.token = this.TOKEN_HEX;
          } else {
            if (this.isOctal.test(this.text.substr(this.index))) {
              this.token = this.TOKEN_OCTAL;
            } else {
//print("parseExprOp cur!"+this.cur+"!");
              switch (this.cur) {
              case '+':
                this.token = this.TOKEN_PLUS;
                break;
              case '-':
                this.token = this.TOKEN_MINUS;
                break;
              case '*':
                this.token = this.TOKEN_MUL;
                break;
              case '/':
                this.token = this.TOKEN_DIV;
                break;
              case '%':
                this.token = this.TOKEN_MOD;
                break;
              case '!':
                if ((this.len > 1) && (this.text.charAt(this.index+1) == '=')) {
                  this.feedchar();
                  this.token = this.TOKEN_NE;
                } else {
                  this.token = this.TOKEN_NOT;
                }
                break;
              case '=':
                if ((this.len > 1) && (this.text.charAt(this.index+1) == '=')) {
                  this.feedchar();
                  this.token = this.TOKEN_EQ;
                }
                break;
              case '<':
                if ((this.len > 1) && (this.text.charAt(this.index+1) == '=')) {
                  this.feedchar();
                  this.token = this.TOKEN_LE;
                } else {
                  this.token = this.TOKEN_LT;
                }
                break;
              case '>':
                if ((this.len > 1) && (this.text.charAt(this.index+1) == '=')) {
                  this.feedchar();
                  this.token = this.TOKEN_GE;
                } else {
                  this.token = this.TOKEN_GT;
                }
                break;
              case '&':
                if ((this.len > 1) && (this.text.charAt(this.index+1) == '&')) {
                  this.feedchar();
                  this.token = this.TOKEN_AND_IF;
                } else {
                  this.token = this.TOKEN_AND;
                }
                break;
              case '|':
                if ((this.len > 1) && (this.text.charAt(this.index+1) == '|')) {
                  this.feedchar();
                  this.token = this.TOKEN_OR_IF;
                } else {
                  this.token = this.TOKEN_OR;
                }
                break;
              case '^':
                this.token = this.TOKEN_EXOR;
                break;
              case '(':
                this.token = this.TOKEN_LP;
                break;
              case ')':
                this.token = this.TOKEN_RP;
                break;
              case 'e':
                if (this.len > 1) {
                  if (this.text.charAt(this.index+1) == 'q') {
                    this.feedchar();
                    this.token = this.TOKEN_STR_EQ;
                  } else {
                    return this.parseString();
                  }
                }
                break;
              case 'n':
                if (this.len > 1) {
                  if (this.text.charAt(this.index+1) == 'e') {
                    this.feedchar();
                    this.token = this.TOKEN_STR_NE;
                  } else {
                    if (this.text.charAt(this.index+1) == 'i') {
                      this.feedchar();
                      this.token = this.TOKEN_STR_NI;
                    } else {
                      return this.parseString();
                    }
                  }
                } else {
                  return this.parseString();
                }
                break;
              case 'i':
                if (this.len > 1) {
                  if (this.text.charAt(this.index+1) == 'n') {
                    this.feedchar();
                    this.token = this.TOKEN_STR_IN;
                  } else {
                    this.no_expr_parsing = true;
                    return this.parseString();
                  }
                } else {
                  return this.parseString();
                }
                break;
              default: 
//print("expr op str!"+this.text.substring(this.index)+"!");
                if (this.isDecimal.test(this.text.substr(this.index))) {
                  this.token = this.TOKEN_DECIMAL;
                } else {
                  return this.parseString();
                }
              }
            }
          }
        }
      }
    }
    this.feedchar();
    this.end  = this.index - 1;
if (this.debug) {
print("  parseExprOperator!"+this.getText()+"!"+this.getTokenString(this.token)+"!");
}
    return this.OK;
  }

};

Parser.prototype.constructor = Parser;

R.Parser = Parser;

}, "0.0.1", {});