function XMLReader(array $t_args, array $output) { $my_output = []; foreach ($output as $key => $out) { $name = $key; $my_output[$name] = $out; } $xpath_row = $t_args['paths']['row']; $xpath_columns = $t_args['paths']['columns']; $className = generate_name('XMLReader'); ?> class <?php echo $className; ?> { std::istream& my_stream; pugi::xml_document doc; const char* xpath_row; std::vector<const char*> xpath_columns; pugi::xpath_node_set rows; pugi::xpath_node_set::const_iterator start; pugi::xpath_node_set::const_iterator end; <?php \grokit\declareDictionaries($my_output); ?> public: <?php echo $className; ?> ( GIStreamProxy& _stream ) : my_stream(_stream.get_stream( )) { doc.load( my_stream ); xpath_row = "<?php echo $xpath_row; ?> "; rows = doc.select_nodes(xpath_row); <?php $push = 'xpath_columns.push_back("'; $close_brace = '");'; foreach ($xpath_columns as $col) { ?> <?php echo $push; echo $col; echo $close_brace; ?> <?php } ?> // Capture the first and last row start = rows.begin(); end = rows.end(); } bool ProduceTuple( <?php echo typed_ref_args($my_output); ?> ) { if( start == end ) { return false; } else { pugi::xml_node xmlnode = start->node(); std::vector<const char*>::const_iterator it = xpath_columns.begin(); <?php $col_num = 0; $node_type = 'auto '; $select_single_node = ' = xmlnode.select_single_node('; $close = ');'; $iterator_increment = '++it;'; foreach ($my_output as $name => $type) { ?> <?php echo $node_type; echo ' col_'; echo $col_num; echo $select_single_node; ?> *it<?php echo $close; ?> <?php echo \grokit\fromStringDict($name, $type, 'col_' . $col_num . '.node().child_value()'); ?> ; <?php $col_num = $col_num + 1; ?> <?php echo $iterator_increment; ?> <?php } ?> ++start; return true; } } }; <?php $sys_headers = ['vector', 'string', 'iostream', 'fstream']; return ['name' => $className, 'kind' => 'GI', 'output' => $my_output, 'system_headers' => $sys_headers, 'lib_headers' => ['pugixml.hpp']]; }
function CSVReader(array $t_args, array $output) { $my_output = []; // Handle case where outputs are given as template arguments // and not implied. if (\count($output) == 0) { grokit_assert(array_key_exists('output', $t_args), 'Did not receive any description of my output!'); $output_list = $t_args['output']; grokit_assert(is_array($output_list), 'Expected list of types for template argument "output"'); $i = 1; foreach ($outputs_list as $name => $out_type) { grokit_assert(is_datatype($out_type) || is_identifier($out_type), 'Expected only types in the "output" list'); if (is_identifier($out_type)) { $out_type = lookupType($out_type->value()); } $name = 'val_' . $i; $my_output[$name] = $out_type; $i += 1; } } else { foreach ($output as $key => $out) { $name = $key; $my_output[$name] = $out; } } $debug = get_default($t_args, 'debug', 0); $simple = get_default($t_args, 'simple', false); $trimCR = get_default($t_args, 'trim.cr', false); // Handle separator $separator = ','; if (array_key_exists('sep', $t_args) || array_key_exists('separator', $t_args)) { $sep = get_first_key($t_args, ['sep', 'separator']); grokit_assert(is_string($sep), "Got " . gettype($sep) . " instead of string for separator."); if (strtolower($sep) === 'tab') { $sep = '\\t'; } grokit_assert($sep != "\n", 'CSV column delimiter cannot be new line'); // Scream if separator is longer than one character grokit_assert(\strlen($sep) == 1 || $sep == '\\t', 'Expected string of length 1 for separator, got string <' . $sep . '> instead'); $separator = $sep; } // Handle quote character $quotechar = '"'; if (array_key_exists('quote', $t_args) && !is_null($t_args['quote'])) { grokit_assert(!$simple, 'Quote option not available for simple CSVReader'); $quote = $t_args['quote']; grokit_assert(is_string($quote), "Got " . gettype($quote) . " instead of string for quote."); // Scream if separator is longer than one character grokit_assert(\strlen($quote) == 1, 'Expected string of length 1 for quote character, got string <' . $quote . '> instead'); $quotechar = $quote; } $quotechar = addcslashes($quotechar, '\\\''); // Handle escape character $escapeChar = '\\'; if (array_key_exists('escape', $t_args) && !is_null($t_args['escape'])) { grokit_assert(!$simple, 'Escape option not available for simple CSVReader'); $escape = $t_args['escape']; grokit_assert(is_string($escape), 'Got ' . gettype($escape) . ' instead of string for escape character.'); grokit_assert(\strlen($escape) == 1, 'Expected string of length 1 for escape character, got string <' . $escape . '> instead'); $escapeChar = $escape; } $escapeChar = addcslashes($escapeChar, '\\\''); // Handle header lines $headerLines = 0; if (array_key_exists('skip', $t_args)) { $headerLines = $t_args['skip']; grokit_assert(is_int($headerLines), 'Got ' . gettype($headerLines) . ' instead of int for number of lines to skip.'); grokit_assert($headerLines >= 0, 'Cannot skip a negative number of lines.'); } // Maximum number of lines to read $maxLines = get_default($t_args, 'n', -1); grokit_assert(is_int($maxLines), 'Got ' . gettype($maxLines) . ' instead of int for template argument "n"'); $nullArg = get_first_key_default($t_args, ['nullable'], false); $nullable = []; $nullStr = []; foreach ($my_output as $name => $type) { $nullable[$name] = false; } if ($nullArg === true) { foreach ($my_output as $name => $type) { $nullable[$name] = true; $nullStr[$name] = 'NULL'; } } else { if (is_array($nullArg)) { foreach ($nullArg as $n => $v) { // If nullable value is an associative mapping, the value is either true/false // or the value of the null string if (is_string($n)) { grokit_assert(is_string($v) || is_bool($v), 'CSVReader: nullable associative mapping must have string or boolean values'); grokit_assert(array_key_exists($n, $nullable), 'CSVReader: cannot make unknown attribute ' . $n . ' nullable'); if (is_bool($v)) { $nullable[$n] = $v; $nullStr[$n] = 'NULL'; } else { $nullable[$n] = true; $nullStr[$n] = $v; } } else { if (is_array($v)) { grokit_assert(array_key_exists('attr', $v), 'CSVReader: Name of nullable attribute not specified'); $attrName = $v['attr']->name(); $nullable[$attrName] = true; $nullStr[$attrName] = array_key_exists('null', $v) ? $v['null'] : 'NULL'; } else { // Otherwise, it's just nullable $attrName = $v->name(); grokit_assert(array_key_exists($attrName, $nullable), 'CSVReader: cannot make unknown attribute ' . $v . ' nullable'); $nullable[$attrName] = true; $nullStr[$attrName] = 'NULL'; } } } } else { if ($nullArg === false) { // Nothing } else { if (is_string($nullArg)) { foreach ($my_output as $name => $type) { $nullable[$name] = true; $nullStr[$name] = $nullArg; } } else { grokit_error('Template argument "nullable" must be boolean or array, ' . typeof($nullArg) . ' given'); } } } } // Come up with a name for ourselves $className = generate_name('CSVReader'); if ($debug >= 2) { foreach ($my_output as $name => $type) { fwrite(STDERR, "CSVReader: {$name} is nullable: " . ($nullable[$name] ? 'true' : 'false') . PHP_EOL); } } ?> class <?php echo $className; ?> { std::istream& my_stream; std::string fileName; // Template parameters static constexpr size_t MAX_LINES = <?php echo $maxLines; ?> ; static constexpr size_t HEADER_LINES = <?php echo $headerLines; ?> ; static constexpr char DELIMITER = '<?php echo $separator; ?> '; <?php if (!$simple) { ?> static constexpr char QUOTE_CHAR = '<?php echo $quotechar; ?> '; static constexpr char ESCAPE_CHAR = '<?php echo $escapeChar; ?> '; typedef boost::escaped_list_separator<char> separator; typedef boost::tokenizer< separator > Tokenizer; separator my_separator; Tokenizer my_tokenizer; <?php } ?> // Prevent having to allocate this every time. std::string line; std::vector<std::string> tokens; size_t count; <?php \grokit\declareDictionaries($my_output); ?> public: <?php echo $className; ?> ( GIStreamProxy& _stream ) : my_stream(_stream.get_stream()) , fileName(_stream.get_file_name()) <?php if (!$simple) { ?> , my_separator(ESCAPE_CHAR, DELIMITER, QUOTE_CHAR) , my_tokenizer(std::string("")) <?php } ?> , count(0) { <?php if ($headerLines > 0) { ?> for( size_t i = 0; i < HEADER_LINES; ++i ) { FATALIF( !getline( my_stream, line ), "CSV Reader reached end of file before finishing header.\n" ); } <?php } // If headerLines > 0 ?> } // > bool ProduceTuple( <?php echo typed_ref_args($my_output); ?> ) { if (count < MAX_LINES) { //> count++; } else { return false; } if( getline( my_stream, line ) ) { <?php if ($trimCR) { ?> if( line.back() == '\r' ) { line.pop_back(); } <?php } // if trimCR if (!$simple) { if ($debug >= 1) { ?> try { <?php } // if debug >= 1 ?> my_tokenizer.assign( line, my_separator ); <?php if ($debug >= 1) { ?> } catch(...) { FATAL("CSVReader for file %s failed on line: %s", fileName.c_str(), line.c_str()); } <?php } // if debug >= 1 ?> Tokenizer::iterator it = my_tokenizer.begin(); <?php foreach ($my_output as $name => $type) { if ($nullable[$name]) { // nullable ?> <?php \grokit\fromStringNullable($name, $type, 'it->c_str()', true, $nullStr[$name]); ?> <?php } else { // not nullable ?> <?php echo \grokit\fromStringDict($name, $type, 'it->c_str()'); ?> ; <?php } // end nullable check ?> ++it; <?php } // foreach output } else { ?> for( char & c : line ) { if( c == DELIMITER ) c = '\0'; } const char * ptr = line.c_str(); <?php $first = true; foreach ($my_output as $name => $type) { if ($first) { $first = false; } else { ?> while( *(ptr++) != '\0' ) ; // Advance past next delimiter <?php } // not first output if ($nullable[$name]) { ?> <?php echo \grokit\fromStringNullable($name, $type, 'ptr', true, $nullStr[$name]); } else { // not nullable ?> <?php echo \grokit\fromStringDict($name, $type, 'ptr'); ?> ; <?php } // if nullable } // foreach output } // if simple reader ?> return true; } else { return false; } } <?php \grokit\declareDictionaryGetters($my_output); ?> }; <?php $sys_headers = ['vector', 'string', 'iostream', 'cstdint']; if (!$simple) { $sys_headers[] = 'boost/tokenizer.hpp'; } return ['name' => $className, 'kind' => 'GI', 'output' => $my_output, 'system_headers' => $sys_headers, 'user_headers' => ['GIStreamInfo.h', 'Dictionary.h', 'DictionaryManager.h']]; }