--- parser3/src/classes/table.C 2013/10/04 21:21:54 1.296 +++ parser3/src/classes/table.C 2015/08/02 21:21:25 1.308 @@ -5,10 +5,6 @@ Author: Alexandr Petrosian (http://paf.design.ru) */ -#if (!defined(NO_STRINGSTREAM) && !defined(FREEBSD4)) -#include -#endif - #include "classes.h" #include "pa_vmethod_frame.h" @@ -21,7 +17,12 @@ #include "pa_vbool.h" #include "pa_array.h" -volatile const char * IDENT_TABLE_C="$Id: table.C,v 1.296 2013/10/04 21:21:54 moko Exp $"; +#if (!defined(NO_STRINGSTREAM) && !defined(FREEBSD4)) +#include +#define USE_STRINGSTREAM +#endif + +volatile const char * IDENT_TABLE_C="$Id: table.C,v 1.308 2015/08/02 21:21:25 moko Exp $"; // class @@ -94,14 +95,6 @@ static Table::Action_options get_action_ return result; } -static void check_option_param(bool options_defined, - MethodParams& params, size_t next_param_index, - const char *msg) { - if(next_param_index+(options_defined?1:0) != params.count()) - throw Exception(PARSER_RUNTIME, - 0, - "%s", msg); -} struct TableSeparators { char column; const String* scolumn; @@ -124,11 +117,15 @@ struct TableSeparators { } if(Value* vencloser=options.get(PA_COLUMN_ENCLOSER_NAME)) { sencloser=&vencloser->as_string(); + if(sencloser->is_empty()){ + encloser=0; + } else { if(sencloser->length()!=1) throw Exception(PARSER_RUNTIME, sencloser, "encloser must be one character long"); encloser=sencloser->first_char(); + } result++; } return result; @@ -139,8 +136,8 @@ static void _create(Request& r, MethodPa // clone/copy part? if(Table *source=params[0].get_table()) { Table::Action_options o=get_action_options(r, params, 1, *source); - check_option_param(o.defined, params, 1, - "too many parameters"); + if(params.count()>2) + throw Exception(PARSER_RUNTIME, 0, "too many parameters"); GET_SELF(r, VTable).set_table(*new Table(*source, o)); return; } @@ -181,7 +178,7 @@ static void _create(Request& r, MethodPa // data Temp_lang temp_lang(r, String::L_PASS_APPENDED); const String& data= - r.process_to_string(params.as_junction(data_param_index, "body must be code")); + r.process_to_string(params.as_junction(data_param_index, "body must be table or code")); // parse columns size_t raw_pos_after=0; @@ -392,57 +389,170 @@ static void _load(Request& r, MethodPara GET_SELF(r, VTable).set_table(table); } -#if (!defined(NO_STRINGSTREAM) && !defined(FREEBSD4)) - +#ifdef USE_STRINGSTREAM #include "gc_allocator.h" typedef std::basic_stringstream, gc_allocator > pa_stringstream; typedef std::basic_string, gc_allocator > pa_string; -void maybe_enclose( pa_stringstream& to, const String& from, char encloser ) { - if(encloser) { +static void enclose( pa_stringstream& to, const String* from, char encloser ) { + if(from){ to<pos( encloser, pos_after ))!=STRING_NOT_FOUND; pos_after=pos_before) { pos_before++; // including first encloser (and skipping it for next pos) - to<mid(pos_after, pos_before).cstr(); to<length(); if(pos_aftermid(pos_after, from_length).cstr(); to< i(*table.columns()); i.has_next(); ) { + enclose( result, i.next(), separators.encloser ); + if(i.has_next()) + result< i(*table.columns()); i.has_next(); ) { + result<cstr(); + if(i.has_next()) + result<count():0) + for(int column=0; column i(table); + if(separators.encloser){ + while(i.has_next()) { + for(Array_iterator c(*i.next()); c.has_next(); ) { + enclose( result, c.next(), separators.encloser ); + if(c.has_next()) + result< c(*i.next()); c.has_next(); ) { + result<cstr(); + if(c.has_next()) + result<pos( encloser, pos_after ))!=STRING_NOT_FOUND; pos_after=pos_before) { pos_before++; // including first encloser (and skipping it for next pos) - to<mid(pos_after, pos_before); to<<*sencloser; // doubling encloser } // last piece - size_t from_length=from.length(); + size_t from_length=from->length(); if(pos_aftermid(pos_after, from_length); to<<*sencloser; - } else - to< i(*table.columns()); i.has_next(); ) { + enclose( result, i.next(), separators.encloser, separators.sencloser ); + if(i.has_next()) + result<<*separators.scolumn; + } + } else { + for(Array_iterator i(*table.columns()); i.has_next(); ) { + result<<*i.next(); + if(i.has_next()) + result<<*separators.scolumn; + } + } + } else { // nameless table [we were asked to output column names] + if(int lsize=table.count()?table[0]->count():0) + for(int column=0; column i(table); + if(separators.encloser){ + while(i.has_next()) { + for(Array_iterator c(*i.next()); c.has_next(); ) { + enclose( result, c.next(), separators.encloser, separators.sencloser ); + if(c.has_next()) + result<<*separators.scolumn; + } + result.append_know_length("\n", 1, String::L_CLEAN); + } + } else { + while(i.has_next()) { + for(Array_iterator c(*i.next()); c.has_next(); ) { + result<<*c.next(); + if(c.has_next()) + result<<*separators.scolumn; + } + result.append_know_length("\n", 1, String::L_CLEAN); + } + } +} #endif // don't use stringstream + static void _save(Request& r, MethodParams& params) { const String& first_arg=params.as_string(0, FIRST_ARG_MUST_NOT_BE_CODE); size_t param_index=1; @@ -479,101 +589,65 @@ static void _save(Request& r, MethodPara Table& table=GET_SELF(r, VTable).table(); -#if (!defined(NO_STRINGSTREAM) && !defined(FREEBSD4)) - +#ifdef USE_STRINGSTREAM pa_stringstream ost(std::stringstream::out); - // process header - if(output_column_names) { - if(table.columns()) { // named table - for(Array_iterator i(*table.columns()); i.has_next(); ) { - maybe_enclose( ost, *i.next(), separators.encloser ); - if(i.has_next()){ - ost<count():0) - for(int column=0; column i(table); - while(i.has_next()) { - for(Array_iterator c(*i.next()); c.has_next(); ) { - maybe_enclose( ost, *c.next(), separators.encloser ); - if(c.has_next()) - ost< i(*table.columns()); i.has_next(); ) { - maybe_enclose( sdata, *i.next(), separators.encloser, separators.sencloser ); - if(i.has_next()) - sdata<<*separators.scolumn; - } - } else { // nameless table [we were asked to output column names] - if(int lsize=table.count()?table[0]->count():0) - for(int column=0; column i(table); - while(i.has_next()) { - for(Array_iterator c(*i.next()); c.has_next(); ) { - maybe_enclose( sdata, *c.next(), separators.encloser, separators.sencloser ); - if(c.has_next()) - sdata<<*separators.scolumn; - } - sdata.append_know_length("\n", 1, String::L_CLEAN); - } + table_to_csv(sdata, table, separators, output_column_names); // write - { const char* data_cstr=sdata.cstr(); - file_write(r.charsets, file_spec, data_cstr, sdata.length(), true, do_append); + file_write(r.charsets, file_spec, data_cstr, sdata.length(), true /* as text */, do_append); if(*data_cstr) // not empty (when empty it's not heap memory) pa_free((void*)data_cstr); // not needed anymore +#endif +} + +static void _csv_string(Request& r, MethodParams& params) { + bool output_column_names=true; + size_t param_index=0; + if(params.count()>0 && params[0].is_string()) { + if(params.as_string(0, FIRST_ARG_MUST_NOT_BE_CODE)=="nameless") { + output_column_names=false; + param_index++; + } else { + throw Exception(PARSER_RUNTIME, + 0, + "bad mode (must be nameless)"); + } } -#endif // don't use stringstream + TableSeparators separators; + if(param_indexcount()) + throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION); + } + + Table& table=GET_SELF(r, VTable).table(); + +#ifdef USE_STRINGSTREAM + pa_stringstream ost(std::stringstream::out); + + table_to_csv(ost, table, separators, output_column_names); + + r.write_no_lang(*new VString(*new String(pa_strdup(ost.str().c_str()), String::L_CLEAN))); +#else + String sdata; + + table_to_csv(sdata, table, separators, output_column_names); + + r.write_no_lang(*new VString(*new String(sdata.cstr(), String::L_CLEAN))); +#endif } static void _count(Request& r, MethodParams& params) { @@ -730,7 +804,9 @@ static void table_row_to_hash(Table::ele table=new Table(*info->table, table_options/*no rows, just structure*/); info->hash->put(*key, new VTable(table)); } - *table+=row; + Table::element_type row_copy(new ArrayString(row->count())); + row_copy->append(*row); + *table+=row_copy; break; } } @@ -958,10 +1034,9 @@ static bool expression_is_true(Table&, E static bool _locate_expression(Table& table, Request& r, MethodParams& params) { Value& expression_code=params.as_junction(0, "must be expression"); - const size_t options_index=1; - Table::Action_options o=get_action_options(r, params, options_index, table); - check_option_param(o.defined, params, options_index, "locate by expression only has parameters: expression and, maybe, options"); - + Table::Action_options o=get_action_options(r, params, 1, table); + if(params.count()>2) + throw Exception(PARSER_RUNTIME, 0, "locate by expression only has parameters: expression and, maybe, options"); Expression_is_true_info info={&r, &expression_code}; return table.table_first_that(expression_is_true, &info, o); } @@ -969,10 +1044,7 @@ static bool _locate_expression(Table& ta static bool _locate_name_value(Table& table, Request& r, MethodParams& params) { const String& name=params.as_string(0, "column name must be string"); const String& value=params.as_string(1, VALUE_MUST_BE_STRING); - const size_t options_index=2; - Table::Action_options o=get_action_options(r, params, options_index, table); - check_option_param(o.defined, params, options_index, "locate by name has parameters: name, value and, maybe, options"); - + Table::Action_options o=get_action_options(r, params, 2, table); return table.locate(name, value, o); } @@ -1028,9 +1100,9 @@ static void _foreach(Request& r, MethodP table.set_current(row); if(rownum_var_name) - var_context->put_element(*rownum_var_name, new VString(*new String(String::Body::Format(row), String::L_CLEAN))); + r.put_element(*var_context, *rownum_var_name, new VString(*new String(String::Body::Format(row), String::L_CLEAN))); if(value_var_name) - var_context->put_element(*value_var_name, new VTable(&table)); + r.put_element(*var_context, *value_var_name, new VTable(&table)); StringOrValue sv_processed=r.process(body_code); Request::Skip lskip=r.get_skip(); r.set_skip(Request::SKIP_NOTHING); @@ -1053,9 +1125,9 @@ static void _foreach(Request& r, MethodP table.set_current(row); if(rownum_var_name) - var_context->put_element(*rownum_var_name, new VString(*new String(String::Body::Format(row), String::L_CLEAN))); + r.put_element(*var_context, *rownum_var_name, new VString(*new String(String::Body::Format(row), String::L_CLEAN))); if(value_var_name) - var_context->put_element(*value_var_name, new VTable(&table)); + r.put_element(*var_context, *value_var_name, new VTable(&table)); r.process_write(body_code); Request::Skip lskip=r.get_skip(); r.set_skip(Request::SKIP_NOTHING); @@ -1079,6 +1151,24 @@ static void _append(Request& r, MethodPa GET_SELF(r, VTable).table()+=row; } +static void _insert(Request& r, MethodParams& params) { + Temp_lang temp_lang(r, String::L_PASS_APPENDED); + const String& string=r.process_to_string(params[0]); + + // parse cells + Table::element_type row=new ArrayString; + size_t pos_after=0; + string.split(*row, pos_after, "\t", String::L_AS_IS); + + Table& table=GET_SELF(r, VTable).table(); + table.insert(table.current(), row); +} + +static void _delete(Request& r, MethodParams& params) { + Table& table=GET_SELF(r, VTable).table(); + table.remove_current(); +} + static void join_named_row(Table& src, Table* dest) { Table::columns_type dest_columns=dest->columns(); size_t dest_columns_count=dest_columns->count(); @@ -1096,7 +1186,6 @@ static void _join(Request& r, MethodPara Table& src=*params.as_table(0, "source"); Table::Action_options o=get_action_options(r, params, 1, src); - check_option_param(o.defined, params, 1, "invalid extra parameter"); Table& dest=GET_SELF(r, VTable).table(); if(&src == &dest) @@ -1179,7 +1268,7 @@ static void marshal_bind( // not static, used elsewhere int marshal_binds(HashStringValue& hash, SQL_Driver::Placeholder*& placeholders) { int hash_count=hash.count(); - placeholders=new(UseGC) SQL_Driver::Placeholder[hash_count]; + placeholders=new SQL_Driver::Placeholder[hash_count]; SQL_Driver::Placeholder* ptr=placeholders; hash.for_each(marshal_bind, &ptr); return hash_count; @@ -1359,6 +1448,11 @@ MTable::MTable(): Methoded("table") { // add_native_method("save_old", Method::CT_DYNAMIC, _save_old, 1, 3); + // ^table.csv-string[] + // ^table.csv-string[nameless] + // ^table.csv-string[nameless;$.encloser["] $.separator[,]] + add_native_method("csv-string", Method::CT_DYNAMIC, _csv_string, 0, 2); + // ^table.count[] // ^table.count[rows] // ^table.count[columns] @@ -1395,9 +1489,15 @@ MTable::MTable(): Methoded("table") { // ^table.foreach[row-num;value]{code}[delim] add_native_method("foreach", Method::CT_DYNAMIC, _foreach, 3, 4); - // ^table.append{r{tab}e{tab}c{tab}o{tab}r{tab}d} + // ^table.append{row{tab}data} add_native_method("append", Method::CT_DYNAMIC, _append, 1, 1); + // ^table.insert{row{tab}data} before current row + add_native_method("insert", Method::CT_DYNAMIC, _insert, 1, 1); + + // ^table.delete[] current row + add_native_method("delete", Method::CT_DYNAMIC, _delete, 0, 0); + // ^table.join[table][$.limit(10) $.offset(1) $.offset[cur] ] add_native_method("join", Method::CT_DYNAMIC, _join, 1, 2);