/* -*-C++-*- */ %{ #include #include #include #include #include #include #include #include #include #include #include #include "ReadData.h" #include #include int zzerror(const char *); int zzlex(); #define YYERROR_VERBOSE 0 static Console *console; static bool interactive = true; void setName(ParseTree *p, std::string *name); std::map _data_table; bool open_data_buffer(std::string const *name); void return_to_main_buffer(); void setMonitor(ParseTree const *var, int thin); void clearMonitor(ParseTree const *var); void doCoda (ParseTree const *var, std::string const &stem); void doAllCoda (std::string const &stem); void doDump (std::string const &file, bool observed); static void errordump(); static void updatestar(long niter, long refresh, int width); static void setParameters(ParseTree *p, ParseTree *param1); static void setParameters(ParseTree *p, std::vector *parameters); static void setParameters(ParseTree *p, ParseTree *param1, ParseTree *param2); %} %defines %name-prefix="zz" %union { int intval; double val; std::string *stringptr; ParseTree *ptree; std::vector *pvec; std::vector *vec; std::vector *ivec; } %token INT %token DOUBLE %token NAME %token STRING %token ENDCMD %token MODEL %token DATA %token IN %token TO %token INITS %token PARAMETERS %token COMPILE %token INITIALIZE %token UPDATE %token BY %token MONITOR %token SET %token CLEAR %token THIN %token CODA %token STEM %token SEED %token EXIT %token NCHAINS %token CHAIN %token LIST %token STRUCTURE %token DIM %token NA %token R_NULL %token DIMNAMES %token ITER %token ARROW %token ENDDATA %token ASINTEGER %type var index %type r_assignment r_structure %type range_element r_dim %type r_attribute_list %type r_value %type r_value_list r_assignment_list range_list %type r_value_collection r_integer_collection r_collection %type file_name; %% input: {if (interactive) std::cout << ". " << std::flush;} | input line {if (interactive) std::cout << ". " << std::flush;} ; line: ENDCMD {} | command ENDCMD {} | error ENDCMD {if(interactive) yyerrok; else exit(1);} ; command: model | data_in | data_to | data_clear | parameters_in | parameters_to | compile | initialize | update | monitor | coda | seed | exit ; model: MODEL IN file_name { console->checkModel(*$3); delete $3; } ; data_in: data r_assignment_list ENDDATA { readRData($2, _data_table); delete $2; } | data {} // Failed to open the data file ; data_to: DATA TO file_name { doDump(*$3, true); delete $3; } ; data: DATA IN file_name { if(open_data_buffer($3)) { std::cout << "Reading data file " << *$3 << std::endl; } else { std::cerr << "Unable to open file " << *$3 << std::endl << std::flush; } delete $3; } ; data_clear: DATA CLEAR { std::cout << "Clearing data table " << std::endl; _data_table.clear(); } ; parameters_in: parameters r_assignment_list ENDDATA { std::map parameter_table; readRData($2, parameter_table); delete $2; console->setParameters(parameter_table, 1); } | parameters r_assignment_list ENDDATA ',' CHAIN '(' INT ')' { std::map parameter_table; readRData($2, parameter_table); delete $2; console->setParameters(parameter_table, $7); } | parameters {} // Failed to open the file ; parameters_to: PARAMETERS TO file_name { doDump(*$3, false); delete $3; } ; parameters: PARAMETERS IN file_name { if(open_data_buffer($3)) { std::cout << "Reading parameter file " << *$3 << std::endl; } else { std::cerr << "Unable to open file " << *$3 << std::endl << std::flush; } delete $3; } | INITS IN file_name { /* Legacy option to not break existing scripts */ if(open_data_buffer($3)) { std::cout << "Reading initial values file " << *$3 << std::endl; } else { std::cerr << "Unable to open file " << *$3 << std::endl << std::flush; } delete $3; } ; compile: COMPILE {console->compile(_data_table, 1);} | COMPILE ',' NCHAINS '(' INT ')' { console->compile(_data_table, $5); } ; initialize: INITIALIZE { if (!console->initialize()) { errordump(); } } ; update: UPDATE INT { //fixme: put refresh option long refresh = $2/40; updatestar($2, refresh, 40); } | UPDATE INT ',' BY '(' INT ')' { updatestar($2,$6, 40); } ; exit: EXIT {return 0;} ; var: NAME { $$ = new ParseTree(P_VAR); setName($$, $1); } | NAME '[' range_list ']' { $$ = new ParseTree(P_VAR); setName($$, $1); setParameters($$, $3); } ; range_list: range_element { $$ = new std::vector(1, $1); } | range_list ',' range_element { $$=$1; $$->push_back($3); } ; range_element: index { $$ = new ParseTree(P_RANGE); setParameters($$,$1); } | index ':' index { $$ = new ParseTree(P_RANGE); setParameters($$, $1, $3); } ; /* FIXME: Use integer value here */ index: INT {$$ = new ParseTree(P_VALUE); $$->setValue($1);} ; monitor: monitor_set | monitor_clear ; monitor_set: MONITOR SET var { setMonitor($3, 1); delete $3; } | MONITOR SET var ',' THIN '(' INT ')' { setMonitor($3, $7); delete $3; } | MONITOR var { setMonitor($2, 1); delete $2; } | MONITOR var ',' THIN '(' INT ')' { setMonitor($2, $6); delete $2; } ; monitor_clear: MONITOR CLEAR var { clearMonitor($3); delete $3; } ; /* File names may optionally be enclosed in quotes, and this is required if the name includes spaces. */ file_name: NAME { $$ = $1;} | STRING { $$ = $1; } ; coda: CODA var { doCoda ($2, "jags"); delete $2; } | CODA var ',' STEM '(' file_name ')' { doCoda ($2, *$6); delete $2; delete $6; } | CODA '*' { doAllCoda ("jags"); } | CODA '*' ',' STEM '(' file_name ')' { doAllCoda (*$6); delete $6; } ; seed: SEED INT { console->setSeed(static_cast($2)); } ; /* Rules for scanning dumped R datasets */ r_assignment_list: r_assignment { $$ = new std::vector(1, $1); } | r_assignment_list r_assignment { $$ = $1; $$->push_back($2); } | r_assignment_list ';' r_assignment { $$ = $1; $$->push_back($3); } ; r_assignment: STRING ARROW r_structure { $$ = $3; setName($$, $1); } | STRING ARROW r_collection { $$ = new ParseTree(P_ARRAY); setName($$, $1); setParameters($$, $3); } ; r_structure: STRUCTURE '(' r_collection ',' r_attribute_list ')' { $$ = new ParseTree(P_ARRAY); if ($5) setParameters($$, $3, $5); else setParameters($$, $3); } ; /* The only attribute we are interested in is .Dim. The rest are simply discarded - see below */ r_attribute_list: r_dim | r_generic_attribute {$$=0;} | r_attribute_list ',' r_generic_attribute | r_attribute_list ',' r_dim {$$=$3;} ; r_dim: DIM '=' r_collection { $$ = $3; } ; r_collection: r_integer_collection | r_value_collection ; r_integer_collection: ASINTEGER '(' r_value_collection ')' {$$ = $3;} ; r_value_collection: r_value { $$ = new ParseTree(P_VECTOR); setParameters($$, $1); } | 'c' '(' r_value_list ')' { $$ = new ParseTree(P_VECTOR); setParameters($$, $3); } ; r_value_list: r_value {$$ = new std::vector(1, $1); } | r_value_list ',' r_value {$$ = $1; $$->push_back($3);} ; r_value: DOUBLE {$$ = new ParseTree(P_VALUE); $$->setValue($1);} | NA {$$ = new ParseTree(P_VALUE); $$->setValue(JAGS_NA);} ; /* Rules for parsing generic attributes. We don't want to do anything with the results, just have the parser accept them */ r_generic_attribute: NAME '=' r_generic_vector {;} ; r_generic_list: r_generic_list_element {;} | r_generic_list ',' r_generic_list_element {;} ; r_generic_list_element: r_generic_vector {;} | NAME '=' r_generic_vector {;} ; r_generic_vector: r_numeric_vector {;} | ASINTEGER '(' r_numeric_vector ')' {;} | r_character_vector {;} | LIST '(' r_generic_list ')' {;} | STRUCTURE '(' r_generic_list ')' {;} | R_NULL {;} ; r_numeric_vector: DOUBLE {;} | 'c' '(' r_double_list ')' ; r_double_list: DOUBLE {;} | r_double_list ',' DOUBLE {;} ; r_character_vector: STRING {;} | 'c' '(' r_string_list ')' {;} ; r_string_list: STRING {;} | r_string_list ',' STRING {;} ; %% int zzerror (const char *s) { return_to_main_buffer(); std::cerr << s << std::endl; return 0; } int main (int argc, char **argv) { extern FILE *zzin; FILE *cmdfile = 0; if (argc > 2) { std::cerr << "Too many arguments" << std::endl; } else if (argc == 2) { interactive = false; cmdfile = fopen(argv[1],"r"); if (cmdfile) { zzin = cmdfile; } else { std::cerr << "Unable to open command file " << argv[1] << std::endl; return 1; } } time_t t; time(&t); std::cout << "Welcome to JAGS " << JAGS_VERSION << " on " << ctime(&t); unsigned int seed = static_cast(t); std::cout << "(seed = " << seed << ")" << std::endl; std::cout << "JAGS is free software and comes with ABSOLUTELY NO WARRANTY" << std::endl; console = new Console(std::cout, std::cerr); console->setSeed(seed); zzparse(); if (argc==2) { fclose(cmdfile); } _data_table.clear(); //Need this to avoid segfault delete console; } static Range getRange(ParseTree const *var) { /* Blank arguments, e.g. foo[] or bar[,1] are not allowed. */ unsigned int size = var->parameters().size(); Index ind_lower(size), ind_upper(size); for (unsigned int i = 0; i < size; ++i) { ParseTree const *range_element = var->parameters()[i]; switch(range_element->parameters().size()) { case 1: ind_lower[i] = (long) (range_element->parameters()[0]->value() + 1.0E-6); ind_upper[i] = ind_lower[i]; break; case 2: ind_lower[i] = (long) (range_element->parameters()[0]->value() + 1.0E-6); ind_upper[i] = (long) (range_element->parameters()[1]->value() + 1.0E-6); break; default: //Error! FIXME break; } } return Range(ind_lower, ind_upper); } void setMonitor(ParseTree const *var, int thin) { std::string const &name = var->name(); if (var->parameters().empty()) { /* Requesting the whole node */ console->setMonitor(name, thin); } else { /* Requesting subset of a multivariate node */ Range range = getRange(var); console->setMonitor(name, range, thin); } } void clearMonitor(ParseTree const *var) { std::string const &name = var->name(); if (var->parameters().empty()) { /* Requesting the whole node */ console->clearMonitor(name); } else { /* Requesting subset of a multivariate node */ Range range = getRange(var); console->clearMonitor(name, range); } } void doCoda (ParseTree const *var, std::string const &stem) { /* Open output file */ std::string outname(stem); outname.append(".out"); std::ofstream out(outname.c_str()); if (!out) { std::cerr << "Failed to open file " << outname << std::endl; return; } /* Open index file */ std::string indname(stem); indname.append(".ind"); std::ofstream ind(indname.c_str()); if (!ind) { std::cerr << "Failed to open file " << indname << std::endl; out.close(); return; } if (var->parameters().empty()) { /* Requesting the whole node */ console->coda(var->name(), out, ind); } else { /* Requesting subset of a multivariate node */ console->coda(var->name(), getRange(var), out, ind); } out.close(); ind.close(); } void doAllCoda (std::string const &stem) { /* Open output file */ std::string outname(stem); outname.append(".out"); std::ofstream out(outname.c_str()); if (!out) { std::cerr << "Failed to open file " << outname << std::endl; return; } /* Open indemax file */ std::string indname(stem); indname.append(".ind"); std::ofstream ind(indname.c_str()); if (!ind) { std::cerr << "Failed to open file " << indname << std::endl; out.close(); return; } console->coda(out, ind); out.close(); ind.close(); } /* Helper function for doDump that handles all the special cases (missing values etc) when writing a double value */ static void writeDouble(double x, std::ostream &out) { if (x == JAGS_NA) { out << "NA"; } else if (ISNAN(x)) { out << "NaN"; } else if (!R_FINITE(x)) { if (x > 0) { out << "Inf"; } else { out << "-Inf"; } } else { out << x; } } void doDump(std::string const &file, bool observed) { std::map data_table; if (!console->dumpState(data_table, 1, observed)) return; /* Open output file */ std::ofstream out(file.c_str()); if (!out) { std::cerr << "Failed to open file " << file << std::endl; return; } for (std::map::const_iterator p = data_table.begin(); p != data_table.end(); ++p) { std::string const &name = p->first; SArray const &sarray = p->second; double const *value = sarray.value(); long length = sarray.length(); out << "\"" << name << "\" <- " << std::endl; Index const &dim = sarray.dim(false); if (dim.size() == 1) { // Vector if (dim[0] == 1) { // Scalar writeDouble(value[0], out); out << std::endl; } else { // Vector of length > 1 out << "c("; for (int i = 0; i < length; ++i) { if (i > 0) { out << ","; } writeDouble(value[i], out); } out << ")" << std::endl; } } else { // Array out << "structure(c("; for (int i = 0; i < length; ++i) { if (i > 0) { out << ","; } writeDouble(value[i], out); } out << "), .Dim = c("; for (unsigned int j = 0; j < dim.size(); ++j) { if (j > 0) { out << ","; } out << dim[j]; } out << "))" << std::endl; } } out.close(); } void setParameters(ParseTree *p, std::vector *parameters) { /* The parser dynamically allocates vectors of (pointers to) parameters. These vectors must be deleted when we are done with them. */ p->setParameters(*parameters); delete parameters; } void setParameters(ParseTree *p, ParseTree *param1) { /* Wrapper function that creates a vector containing param1 to be passed to ParseTree::setParameters. */ std::vector parameters(1, param1); p->setParameters(parameters); } void setParameters(ParseTree *p, ParseTree *param1, ParseTree *param2) { /* Wrapper function that creates a vector containing param1 and param2, to be passed to ParseTree::setParameters */ std::vector parameters; parameters.push_back(param1); parameters.push_back(param2); p->setParameters(parameters); } void setName(ParseTree *p, std::string *name) { p->setName(*name); delete name; } static void errordump() { if (console->model()) { std::cout << "Dumping state at iteration " << console->iter() << " to file jags.dump" << std::endl; doDump("jags.dump", false); } } static void updatestar(long niter, long refresh, int width) { if (refresh == 0) { console->update(niter); return; } if (width > niter / refresh + 1) width = niter / refresh + 1; std::cout << "Updating " << niter << std::endl; for (int i = 0; i < width - 1; ++i) { std::cout << "-"; } std::cout << "| " << std::min(width * refresh, niter) << std::endl << std::flush; int col = 0; for (long n = niter; n > 0; n -= refresh) { long nupdate = std::min(n, refresh); if(console->update(nupdate)) std::cout << "*" << std::flush; else { std::cout << std::endl; errordump(); return; } col++; if (col == width || n <= nupdate) { int percent = 100 - (n-nupdate) * 100/niter; std::cout << " " << percent << "%" << std::endl; if (n > nupdate) { col = 0; } } } }