All Downloads are FREE. Search and download functionalities are using the official Maven repository.

water.rapids.Exec Maven / Gradle / Ivy

There is a newer version: 3.8.2.9
Show newest version
package water.rapids;

import water.fvec.Frame;

/**
 * Exec is an interpreter of abstract syntax trees.
 *
 * This file contains the AST parser and parser helper functions.  
 * AST Execution starts in the ASTExec file, but spreads throughout Rapids.
 *
 * Trees have a Lisp-like structure with the following "reserved" special
 * characters:
 *
 *     '('   a nested function application expression ')
 *     '{'   a nested function definition  expression '}'
 *     '#'   a double: attached_token
 *     '['   a numeric or string list expression, till ']'
 *     '%'   an ID: attached_token
 *     '"'   a String (double quote): attached_token
 *     "'"   a String (single quote): attached_token
 *     digits: a double
 *     letters or other specials: an ID
 *
 * In the above, attached_token signals that the special char has extra chars
 * that must be parsed separately.  These are variable names (in the case of
 * %), doubles (in the case of #), Strings (in the case of ' and "), or number
 * lists (in the case of '[' till ']')
 * 
 * Variables are lexically scoped inside 'let' expressions or at the top-level
 * looked-up in the DKV directly (and must refer to a known type that is valid
 * on the execution stack)
 */
public class Exec {
  final String _str;            // Statement to parse and execute
  int _x;                       // Parse pointer

  public Exec(String str) { _str = str; }

  public static Val exec( String rapids ) {
    // Execute a single rapids call in a short-lived session
    Session ses = new Session();
    try {
      AST ast = new Exec(rapids).parse();
      Val val = ses.exec(ast,null);
      // Any returned Frame has it's REFCNT raised by +1, and the end(val) call
      // will account for that, copying Vecs as needed so that the returned
      // Frame is independent of the Session (which is disappearing).
      return ses.end(val);
    } catch( Throwable ex ) {
      throw ses.endQuietly(ex);
    }
  }

  // Compute and return a value in this session.  Any returned frame shares
  // Vecs with the session (is not deep copied), and so must be deleted by the
  // caller (with a Rapids "rm" call) or will disappear on session exit, or is
  // a normal global frame.
  static public Val exec( String rapids, Session ses ) {
    AST ast = new Exec(rapids).parse();
    // Synchronize the session, to stop back-to-back overlapping Rapids calls
    // on the same session, which Flow sometimes does
    synchronized(ses) { 
      Val val = ses.exec(ast, null);
      // Any returned Frame has it's REFCNT raised by +1, but is exiting the
      // session.  If it's a global, we simply need to lower the internal refcnts
      // (which won't delete on zero cnts because of the global).  If it's a
      // named temp, the ref cnts are accounted for by being in the temp table.
      if( val.isFrame() ) {
        Frame fr = val.getFrame();
        assert fr._key != null; // No nameless Frame returns, as these are hard to cleanup
        if( ses.FRAMES.containsKey(fr) ) {
          throw water.H2O.unimpl();
        } else {
          ses.addRefCnt(fr, -1);
        }
      }
      return val;
    }
  }

  // Parse an expression
  //   '('   a nested function application expression ')
  //   '{'   a nested function definition  expression '}'
  //   '#'   a double: attached_token
  //   '['   a numeric list expression, till ']'
  //   '"'   a String (double quote): attached_token
  //   "'"   a String (single quote): attached_token
  //   digits: a double
  //   letters or other specials: an ID
  @SuppressWarnings({"fallthrough"})
  public AST parse( ) {
    switch( skipWS() ) {
    case '(':  return new ASTExec(this); // function application
    case '{':  return new ASTFun(this);  // function definition
    case '#':  _x++;                     // Skip before double, FALL THRU
    case '0':  case '1':  case '2':  case '3':  case '4':
    case '5':  case '6':  case '7':  case '8':  case '9':
      return new ASTNum(this);
    case '\"': return new ASTStr(this,'\"');
    case '\'': return new ASTStr(this,'\'');
    case '[':  return isQuote(xpeek('[').skipWS()) ? new ASTStrList(this) : new ASTNumList(this);
    case ' ':  throw new IllegalASTException("Expected an expression but ran out of text");
    case '-':  return (peek(1)>='0' && peek(1) <='9') ? new ASTNum(this) : new ASTId(this);
    default:  return new ASTId(this);
    }    
  }

  char peek() { return _x < _str.length() ? _str.charAt(_x) : ' '; } // peek ahead
  char peek(int off) { return _x+off < _str.length() ? _str.charAt(_x+off) : ' '; } // peek ahead
  // Peek, and throw if not found an expected character
  Exec xpeek(char c) {
    if( peek() != c )
      throw new IllegalASTException("Expected '"+c+"'. Got: '"+peek()+"'.  unparsed: "+ unparsed() + " ; _x = "+_x);
    _x++;
    return this;                // Flow coding
  }
  // Skip white space, return the 1st non-whitespace char or ' ' if out of text
  char skipWS() {
    char c=' ';
    while( _x < _str.length() && isWS(c=peek()) ) _x++;
    return c;
  }

  // Parse till whitespace or close-paren
  String token() {
    int start = _x;
    char c;
    while( !isWS(c=peek()) && c!=')' && c!='}') _x++;
    if( start == _x ) throw new IllegalArgumentException("Missing token");
    return _str.substring(start,_x);
  }

  // Parse while number-like, and return the number
  double number() {
    int start = _x;
    char c;
    while( !isWS(c=peek()) && c!=')' && c!=']' && c!=',' && c!=':' ) _x++;
    return Double.valueOf(_str.substring(start,_x));
  }

  // Parse till matching
  String match(char c) {
    int start = ++_x;
    while( peek() != c ) _x++;
    _x++;                       // Skip the match
    return _str.substring(start,_x-1);
  }

  // Return unparsed text, useful in error messages and debugging
  String unparsed() { return _str.substring(_x,_str.length()); }

  static boolean isWS(char c) { return c==' '; }
  static boolean isQuote(char c) { return c=='\'' || c=='\"'; }


  AST throwErr( String msg ) {
    int idx = _str.length()-1;
    int lo = _x, hi=idx;

    String str = _str;
    if( idx < lo ) { lo = idx; hi=lo; }
    String s = msg+ '\n'+str+'\n';
    int i;
    for( i=0; i




© 2015 - 2025 Weber Informatics LLC | Privacy Policy