Diff for /parser3/src/classes/op.C between versions 1.89 and 1.128

version 1.89, 2002/04/16 08:22:19 version 1.128, 2003/04/04 09:59:56
Line 1 Line 1
 /** @file  /** @file
         Parser: parser @b operators.          Parser: parser @b operators.
   
         Copyright (c) 2001, 2002 ArtLebedev Group (http://www.artlebedev.com)          Copyright (c) 2001, 2003 ArtLebedev Group (http://www.artlebedev.com)
         Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)          Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
   
         $Id$  
 */  */
   
   static const char* IDENT_OP_C="$Date$";
   
 #include "classes.h"  #include "classes.h"
 #include "pa_common.h"  #include "pa_common.h"
 #include "pa_request.h"  #include "pa_request.h"
 #include "pa_vint.h"  #include "pa_vint.h"
 #include "pa_sql_connection.h"  #include "pa_sql_connection.h"
 #include "pa_vdate.h"  #include "pa_vdate.h"
   #include "pa_vmethod_frame.h"
   
 // limits  // limits
   
Line 20 Line 21
   
 // defines  // defines
   
 #define OP_CLASS_NAME "OP"  
 #define CASE_DEFAULT_VALUE "DEFAULT"  #define CASE_DEFAULT_VALUE "DEFAULT"
   
 // class  // class
   
 class MOP : public Methoded {  class VClassMAIN: public VClass {
 public:  public:
         MOP(Pool& pool);          VClassMAIN(Pool& apool);
 public: // Methoded  
         bool used_directly() { return true; }  
         void configure_user(Request& r);  
   
 private:  
         String main_sql_name;  
         String main_sql_drivers_name;  
 };  };
   
 // methods  // methods
Line 92  static void _taint(Request& r, const Str Line 85  static void _taint(Request& r, const Str
         {          {
                 Value& vbody=params->as_no_junction(params->size()-1, "body must not be code");                  Value& vbody=params->as_no_junction(params->size()-1, "body must not be code");
                                   
                 String result(r.pool());                  String& result=*new(pool) String(pool);
                 result.append(                  result.append(
                         vbody.as_string(),  // process marking tainted with that lang                          vbody.as_string(),  // process marking tainted with that lang
                         lang, true);  // force result language to specified                          lang, true);  // force result language to specified
Line 101  static void _taint(Request& r, const Str Line 94  static void _taint(Request& r, const Str
 }  }
   
 static void _process(Request& r, const String& method_name, MethodParams *params) {  static void _process(Request& r, const String& method_name, MethodParams *params) {
         // calculate pseudo file name of processed chars          Pool& pool=r.pool();
         // would be something like "/some/file(4) process"  
         char local_place[MAX_STRING];  
 #ifndef NO_STRING_ORIGIN  
         const Origin& origin=method_name.origin();  
         size_t place_size=snprintf(local_place, MAX_STRING, "%s(%d) %s",   
                 origin.file?origin.file:"unknown_file", 1+origin.line,  
                 method_name.cstr())+1;  
 #else  
         strncpy(local_place, method_name.cstr(), MAX_STRING-1); place[MAX_STRING-1]=0;  
         size_t place_size=strlen(local_place)+1;  
 #endif  
         char *heap_place=(char *)r.malloc(place_size);  
         memcpy(heap_place, local_place, place_size);  
   
   
         VStateless_class& self_class=*r.self->get_class();  
         const Method *main_method;          const Method *main_method;
           Value& target_self=params->size()>1?
                   params->as_no_junction(0, "target must not be code")
                   :r.get_method_frame()->caller()->self();
         {          {
                   Value& vjunction=params->as_junction(params->size()-1, "body must be code");
   
                   VStateless_class *target_class=target_self.get_last_derived_class();
                   if(!target_class)
                           throw Exception("parser.runtime",
                                   &method_name,
                                   "no target class");
                   
                   // evaluate source to process
                   const String& source=r.process_to_string(vjunction);
   
                 // temporary remove language change                  // temporary remove language change
                 Temp_lang temp_lang(r, String::UL_PASS_APPENDED);                  Temp_lang temp_lang(r, String::UL_PASS_APPENDED);
                 // temporary zero @main so to maybe-replace it in processed code                  // temporary zero @main so to maybe-replace it in processed code
                 Temp_method temp_method_main(self_class, *main_method_name, 0);                  Temp_method temp_method_main(*target_class, r.main_method_name, 0);
                 // temporary zero @auto so it wouldn't be auto-called in Request::use_buf                  // temporary zero @auto so it wouldn't be auto-called in Request::use_buf
                 Temp_method temp_method_auto(self_class, *auto_method_name, 0);                  Temp_method temp_method_auto(*target_class, *auto_method_name, 0);
                                   
                 // evaluate source to process                  // calculate pseudo file name of processed chars
                 const String& source=r.process_to_string(params->as_junction(0, "body must be code"));                  // would be something like "/some/file(4) process"
                   char local_place[MAX_STRING];
   #ifndef NO_STRING_ORIGIN
                   const Origin& source_origin=source.origin();
                   const Origin& method_origin=method_name.origin();
                   size_t place_size;
                   if(source_origin.file==method_origin.file)
                           place_size=snprintf(local_place, MAX_STRING, "%s(%d) %s",  // same file
                                   source_origin.file?source_origin.file:"unknown_file", 1+source_origin.line, 
                                   method_name.cstr())+1;
                   else // different files ^process{external__file_text__or__sql}
                           place_size=snprintf(local_place, MAX_STRING, "%s", 
                                   source_origin.file?source_origin.file:"unknown_file")+1;
   #else
                   strncpy(local_place, method_name.cstr(), MAX_STRING-1); place[MAX_STRING-1]=0;
                   size_t place_size=strlen(local_place)+1;
   #endif
                   char *heap_place=(char *)r.malloc(place_size);
                   memcpy(heap_place, local_place, place_size);
   
                 // process source code, append processed methods to 'self' class                  // process source code, append processed methods to 'self' class
                 // maybe-define new @main                  // maybe-define new @main
                 r.use_buf(                  r.use_buf(*target_class,
                         source.cstr(String::UL_UNSPECIFIED, r.connection(0)),                           source.cstr(String::UL_UNSPECIFIED, r.connection(0)), 
                         heap_place,                           *new(pool) String(pool, heap_place, place_size, true /*tainted*/),
                         &self_class);                          heap_place);
                                   
                 // main_method                  // main_method
                 main_method=self_class.get_method(*main_method_name);                  main_method=target_class->get_method(r.main_method_name);
         }          }
         // after restoring current-request-lang          // after restoring current-request-lang
         // maybe-execute @main[]          // maybe-execute @main[]
         if(main_method) {          if(main_method) {
                   // temporarily set method_frame's self to target_self
                   Temp_method_frame_self tmfs(*r.get_method_frame(), target_self);
                 // execute!                       // execute!     
                 r.execute(*main_method->parser_code);                  r.execute(*main_method->parser_code);
         }          }
Line 179  static void _while(Request& r, const Str Line 190  static void _while(Request& r, const Str
   
 static void _use(Request& r, const String& method_name, MethodParams *params) {  static void _use(Request& r, const String& method_name, MethodParams *params) {
         Value& vfile=params->as_no_junction(0, "file name must not be code");          Value& vfile=params->as_no_junction(0, "file name must not be code");
         r.use_file(vfile.as_string());          r.use_file(r.main_class, vfile.as_string());
 }  }
   
 static void _for(Request& r, const String& method_name, MethodParams *params) {  static void _for(Request& r, const String& method_name, MethodParams *params) {
Line 197  static void _for(Request& r, const Strin Line 208  static void _for(Request& r, const Strin
   
         bool need_delim=false;          bool need_delim=false;
         VInt *vint=new(pool) VInt(pool, 0);          VInt *vint=new(pool) VInt(pool, 0);
           r.get_method_frame()->caller()->put_element(var_name, vint, false);
         for(int i=from; i<=to; i++) {          for(int i=from; i<=to; i++) {
                 vint->set_int(i);                  vint->set_int(i);
                 r.root->put_element(var_name, vint);  
   
                 StringOrValue processed_body=r.process(body_code);                  StringOrValue sv_processed=r.process(body_code);
                 if(delim_maybe_code) { // delimiter set?                  const String *s_processed=sv_processed.get_string();
                         const String *string=processed_body.get_string();                  if(delim_maybe_code && s_processed && s_processed->size()) { // delimiter set and we have body
                         if(need_delim && string && string->size()) // need delim & iteration produced string?                          if(need_delim) // need delim & iteration produced string?
                                 r.write_pass_lang(r.process(*delim_maybe_code));                                  r.write_pass_lang(r.process(*delim_maybe_code));
                         need_delim=true;                          need_delim=true;
                 }                  }
                 r.write_pass_lang(processed_body);                  r.write_pass_lang(sv_processed);
         }          }
 }  }
   
 static void _eval(Request& r, const String& method_name, MethodParams *params) {  static void _eval(Request& r, const String& method_name, MethodParams *params) {
         Value& expr=params->as_junction(0, "need expression");          Value& expr=params->as_junction(0, "need expression");
         // evaluate expresion          // evaluate expresion
         Value *result=r.process_to_value(expr,           Value *value_result=r.process_to_value(expr, 
                 /*0/*no name YET* /,*/                  /*0/*no name YET* /,*/
                 true/*don't intercept string*/).as_expr_result();                  true/*don't intercept string*/).as_expr_result();
         if(params->size()>1) {          if(params->size()>1) {
Line 223  static void _eval(Request& r, const Stri Line 234  static void _eval(Request& r, const Stri
   
                 Pool& pool=r.pool();                  Pool& pool=r.pool();
                 String& string=*new(pool) String(pool);                  String& string=*new(pool) String(pool);
                 string.APPEND_CONST(format(pool, result->as_double(), fmt.as_string().cstr()));                  string.APPEND_CONST(format(pool, value_result->as_double(), fmt.as_string().cstr()));
                 result=new(pool) VString(string);                  r.write_no_lang(string);
         }          } else
         result->set_name(method_name);                  r.write_no_lang(*value_result);
         r.write_no_lang(*result);  
 }  }
   
 static void _connect(Request& r, const String& method_name, MethodParams *params) {  static void _connect(Request& r, const String& method_name, MethodParams *params) {
Line 238  struct timeval mt[2]; Line 248  struct timeval mt[2];
         Value& url=params->as_no_junction(0, "url must not be code");          Value& url=params->as_no_junction(0, "url must not be code");
         Value& body_code=params->as_junction(1, "body must be code");          Value& body_code=params->as_junction(1, "body must be code");
   
         Table *protocol2driver_and_client=          Table *protocol2driver_and_client=0;
                 static_cast<Table *>(r.classes_conf.get(r.OP.name()));          if(Value *sql=r.main_class.get_element(*new(pool) String(pool, MAIN_SQL_NAME), r.main_class, false)) {
                   if(Value *element=sql->get_element(*new(pool) String(pool, MAIN_SQL_DRIVERS_NAME), *sql, false)) {
                           protocol2driver_and_client=element->get_table();
                   }
           }
   
 #ifdef RESOURCES_DEBUG  #ifdef RESOURCES_DEBUG
 //measure:before  //measure:before
Line 287  static void _switch(Request& r, const St Line 301  static void _switch(Request& r, const St
         r.process(cases_code, true/*intercept_string*/);          r.process(cases_code, true/*intercept_string*/);
         if(Value *selected_code=data.found ? data.found : data._default) {          if(Value *selected_code=data.found ? data.found : data._default) {
                 // setting code context, would execute in ^switch[...]{>>context<<}                  // setting code context, would execute in ^switch[...]{>>context<<}
                 selected_code->get_junction()->change_context(cases_code.get_junction());                  //selected_code->get_junction()->change_context(cases_code.get_junction());
                 r.write_pass_lang(r.process(*selected_code));                  r.write_pass_lang(r.process(*selected_code));
         }          }
 }  }
Line 306  static void _case(Request& r, const Stri Line 320  static void _case(Request& r, const Stri
                   
         // killing context for safety, would execute in ^switch[...]{>>context<<}          // killing context for safety, would execute in ^switch[...]{>>context<<}
         // reason: context is stacked, and it would become invalid afterwards          // reason: context is stacked, and it would become invalid afterwards
         code->get_junction()->change_context(0);          //code->get_junction()->change_context(0);
   
         for(int i=0; i<count; i++) {          for(int i=0; i<count; i++) {
                 Value& value=r.process_to_value(params->get(i));                  Value& value=r.process_to_value(params->get(i));
Line 388  static void locked_process_and_cache_put Line 402  static void locked_process_and_cache_put
                 info.data->expires=0; // flag it so that could be easily checked by caller                  info.data->expires=0; // flag it so that could be easily checked by caller
 }  }
 const String *locked_process_and_cache_put(Request& r,   const String *locked_process_and_cache_put(Request& r, 
                                                                         Value& body_code,                                             Value& body_code,
                                                                         Cache_data& data,                                             Cache_data& data,
                                                                         const String& file_spec) {                                             const String& file_spec) {
         Locked_process_and_cache_put_action_info info={          Locked_process_and_cache_put_action_info info={
                 &r,                  &r,
                 &data,                  &data,
Line 402  const String *locked_process_and_cache_p Line 416  const String *locked_process_and_cache_p
                 "cache_put", locked_process_and_cache_put_action, &info,                  "cache_put", locked_process_and_cache_put_action, &info,
                 false/*as_text*/,                  false/*as_text*/,
                 false/*do_append*/,                  false/*do_append*/,
                 false/*block*/) ? info.evaluated_body: 0;                  false/*block*/,
         if(data.expires==0)                  false/*fail on lock problem*/) ? info.evaluated_body: 0;
           time_t now=time(0);
           if(data.expires<=now)
                 cache_delete(file_spec);                  cache_delete(file_spec);
         return result;          return result;
 }  }
Line 412  String *cache_get(Pool& pool, const Stri Line 428  String *cache_get(Pool& pool, const Stri
         if(file_read(pool, file_spec,           if(file_read(pool, file_spec, 
                            data, data_size,                              data, data_size, 
                            false/*as_text*/,                              false/*as_text*/, 
                              0, 0, //no params&out_fields
                            false/*fail_on_read_problem*/)                             false/*fail_on_read_problem*/)
             && data_size/* ignore reads which are empty due to               && data_size/* ignore reads which are empty due to 
                         non-unary open+lockEX conflict with lockSH */) {                          non-unary open+lockEX conflict with lockSH */) {
Line 435  static time_t as_expires(Request& r, con Line 452  static time_t as_expires(Request& r, con
                                                 int index, time_t now) {                                                  int index, time_t now) {
         time_t result;          time_t result;
         Value& vlifespan_or_expires=params->get(index);          Value& vlifespan_or_expires=params->get(index);
         if(strcmp(vlifespan_or_expires.type(), VDATE_TYPE)==0)          if(Value *vdate=vlifespan_or_expires.as(VDATE_TYPE, false))
                 result=static_cast<VDate&>(vlifespan_or_expires).get_time();                  result=static_cast<VDate *>(vdate)->get_time();
         else          else
                 result=now+(time_t)params->as_double(index, "lifespan must be date or number", r);                  result=now+(time_t)params->as_double(index, "lifespan must be date or number", r);
                   
Line 535  static void _cache(Request& r, const Str Line 552  static void _cache(Request& r, const Str
 VHash& exception2vhash(Pool& pool, const Exception& e) {  VHash& exception2vhash(Pool& pool, const Exception& e) {
         VHash& result=*new(pool) VHash(pool);          VHash& result=*new(pool) VHash(pool);
         Hash& hash=result.hash(0);          Hash& hash=result.hash(0);
         if(const char *type=e.type())          if(const char *type=e.type(true))
                 hash.put(*exception_type_part_name, new(pool) VString(*new(pool) String(pool, type)));                  hash.put(*exception_type_part_name, new(pool) VString(*new(pool) String(pool, type)));
         if(const String *asource=e.problem_source()) {          if(const String *asource=e.problem_source()) {
                 String& source=*new(pool) String(pool);                   String& source=*new(pool) String(pool); 
                 source.append(*asource, String::UL_TAINTED, true/*forced*/);                  source.append(*asource, String::UL_TAINTED, true/*forced*/);
                 result.set_name(source);  
   
                 hash.put(*exception_source_part_name, new(pool) VString(source));                  hash.put(*exception_source_part_name, new(pool) VString(source));
 #ifndef NO_STRING_ORIGIN  #ifndef NO_STRING_ORIGIN
Line 551  VHash& exception2vhash(Pool& pool, const Line 567  VHash& exception2vhash(Pool& pool, const
                         new(pool) VInt(pool, 1+origin.line));                          new(pool) VInt(pool, 1+origin.line));
 #endif  #endif
         }          }
         if(const char *ecomment=e.comment()) {          if(const char *ecomment=e.comment(true)) {
                 int comment_size=strlen(ecomment);                  int comment_size=strlen(ecomment);
                 char *pcomment=(char *)pool.malloc(comment_size);                  char *pcomment=(char *)pool.malloc(comment_size);
                 memcpy(pcomment, ecomment, comment_size);                  memcpy(pcomment, ecomment, comment_size);
Line 571  static void _try_operator(Request& r, co Line 587  static void _try_operator(Request& r, co
         Value& catch_code=params->as_junction(1, "catch_code must be code");          Value& catch_code=params->as_junction(1, "catch_code must be code");
   
         StringOrValue result;          StringOrValue result;
           // taking snapshot of try-context
         // taking snapshot of request processing status          Request_context_saver try_context(r);
         //int ssexception_trace=r.exception_trace.top_index();  
         int sstack=r.stack.top_index();  
         Value *sself=r.self, *sroot=r.root, *srcontext=r.rcontext;    
         WContext *swcontext=r.wcontext;   
         try {          try {
                 result=r.process(body_code);                  result=r.process(body_code);
         } catch(const Exception& e) {          } catch(const Exception& e) {
                 // restoring request processing status                  Request_context_saver throw_context(r); // taking snapshot of throw-context [stack trace contains error]
                 //r.exception_trace.top_index(ssexception_trace);                  try_context.restore(); // restoring try-context to perform catch-code
                 r.stack.top_index(sstack);  
                 r.self=sself; r.root=sroot, r.rcontext=srcontext; r.wcontext=swcontext;  
                   
                   
                 VHash& vhash=exception2vhash(pool, e);                  VHash& vhash=exception2vhash(pool, e);
   
                 Junction *junction=catch_code.get_junction();                  Junction *junction=catch_code.get_junction();
                 Value *saved_exception_var_value=junction->root->get_element(*exception_var_name);                  Value *method_frame=junction->method_frame;
                 junction->root->put_element(*exception_var_name, &vhash);                  Value *saved_exception_var_value=method_frame->get_element(*exception_var_name, *method_frame, false);
                   junction->method_frame->put_element(*exception_var_name, &vhash, false);
                 result=r.process(catch_code);                  result=r.process(catch_code);
                 bool handled=false;                  bool handled=false;
                 if(Value *value=static_cast<Value *>(vhash.hash(0).get(*exception_handled_part_name)))                  if(Value *value=static_cast<Value *>(vhash.hash(0).get(*exception_handled_part_name)))
                         handled=value->as_bool();                                         handled=value->as_bool();               
                 junction->root->put_element(*exception_var_name, saved_exception_var_value);                  junction->method_frame->put_element(*exception_var_name, saved_exception_var_value, false);
   
                 if(!handled)                  if(!handled) {
                           throw_context.restore(); // restoring throw-context [exception were not handled]
                         throw(e); // rethrow                          throw(e); // rethrow
                   }
         }          }
         // write it out           // write out result
         r.write_pass_lang(result);          r.write_pass_lang(result);
 }  }
   
Line 631  static void _throw_operator(Request& r, Line 643  static void _throw_operator(Request& r,
         } else {          } else {
                 const char *type=params->as_string(0, "type must be string").cstr();                  const char *type=params->as_string(0, "type must be string").cstr();
                 const String& source=params->as_string(1, "source must be string");                  const String& source=params->as_string(1, "source must be string");
                 const char *comment=params->as_string(2, "comment must be string").cstr();                  const char *comment=params->size()>2?params->as_string(2, "comment must be string").cstr():0;
                 throw Exception(type, &source, comment);                  throw Exception(type, &source, "%s", comment?comment:"");
         }          }
 }  }
                   
 // constructor  // constructor
   
 MOP::MOP(Pool& apool) : Methoded(apool),  VClassMAIN::VClassMAIN(Pool& apool): VClass(apool) {
         main_sql_name(apool, MAIN_SQL_NAME),          set_name(*NEW String(pool(), MAIN_CLASS_NAME));
         main_sql_drivers_name(apool, MAIN_SQL_DRIVERS_NAME)  
 {  
         set_name(*NEW String(pool(), OP_CLASS_NAME));  
   
         // ^if(condition){code-when-true}          // ^if(condition){code-when-true}
         // ^if(condition){code-when-true}{code-when-false}          // ^if(condition){code-when-true}{code-when-false}
Line 655  MOP::MOP(Pool& apool) : Methoded(apool), Line 664  MOP::MOP(Pool& apool) : Methoded(apool),
         add_native_method("taint", Method::CT_ANY, _taint, 1, 2);          add_native_method("taint", Method::CT_ANY, _taint, 1, 2);
   
         // ^process[code]          // ^process[code]
         add_native_method("process", Method::CT_ANY, _process, 1, 1);          add_native_method("process", Method::CT_ANY, _process, 1, 2);
   
         // ^rem{code}          // ^rem{code}
         add_native_method("rem", Method::CT_ANY, _rem, 1, 10000);          add_native_method("rem", Method::CT_ANY, _rem, 1, 10000);
Line 701  MOP::MOP(Pool& apool) : Methoded(apool), Line 710  MOP::MOP(Pool& apool) : Methoded(apool),
   
 // constructor & configurator  // constructor & configurator
   
 Methoded *MOP_create(Pool& pool) {  VClass& VClassMAIN_create(Pool& pool) {
         return new(pool) MOP(pool);          return *new(pool) VClassMAIN(pool);
 }  
   
 void MOP::configure_user(Request& r) {  
         Pool& pool=r.pool();  
   
         // $MAIN:SQL.drivers  
         if(Value *sql=r.main_class->get_element(main_sql_name))  
                 if(Value *element=sql->get_element(main_sql_drivers_name))  
                         if(Table *protocol2library=element->get_table())  
                                 r.classes_conf.put(name(), protocol2library);  
 }  }

Removed from v.1.89  
changed lines
  Added in v.1.128


E-mail: