1 /*
2 Gri - A language for scientific graphics programming
3 Copyright (C) 2008 Daniel Kelley
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include <string.h>
21 #include <stdio.h>
22 #include "gr.hh"
23 #include "extern.hh"
24 #include "private.hh"
25 #include "Variable.hh"
26
27
28 std::vector<GriVariable> variableStack;
29
30
31 // Get index of variable
32 // RETURN non-negative integer if 'name' is an existing variable, or -1 if not.
33 // BUG: only finds single-dotted variables (stored in the stack)
34 int
index_of_variable(const char * name,int mark)35 index_of_variable(const char *name, int mark)
36 {
37 if (!is_var(name))
38 return -1;
39 unsigned int stackLen = variableStack.size();
40
41 // Look up normally (global scope) or privately (with mark >= 0)
42 if (mark == -1) {
43 for (int i = stackLen - 1; i >= 0; i--)
44 if (!strcmp(name, variableStack[i].get_name()))
45 return i;
46 return -1;
47 } else {
48 int mark_above = mark + 1;
49 unsigned int index;
50 int this_mark = 0;
51 for (index = 0; index < stackLen; index++) {
52 const char *n = variableStack[index].get_name();
53 if (*n == '\0')
54 if (++this_mark == mark_above)
55 break;
56 }
57 if (this_mark != mark_above) {
58 //printf("DEBUG %s:%d no match for <%s>\n",__FILE__,__LINE__,name);
59 return -1;
60 }
61 //printf("DEBUG %s:%d index %d\n",__FILE__,__LINE__,index);
62 for (int i = index - 1; i >= 0; i--) {
63 //printf("check <%s> to see if <%s>\n",variableStack[i].get_name(),name);
64 if (!strcmp(variableStack[i].get_name(), name)) {
65 return i;
66 }
67 }
68 return -1;
69 }
70 return -1;
71 }
72
73 // Make new variable
74 bool
create_variable(const char * name,double value)75 create_variable(const char *name, double value)
76 {
77 GriVariable newVariable(name, value);
78 variableStack.push_back(newVariable);
79 return true;
80 }
81
82 bool
show_variablesCmd()83 show_variablesCmd()
84 {
85 bool have_some = false;
86 ShowStr("Variables...\n");
87 int n = variableStack.size();
88 for (int i = 0; i < n; i++) {
89 const char *n = variableStack[i].get_name();
90 if (*n == '\0') {
91 printf(" ------------------------------------------------\n");
92 } else {
93 extern char _grTempString[];
94 sprintf(_grTempString, " %-25s = %g\n", variableStack[i].get_name(), variableStack[i].get_value());
95 ShowStr(_grTempString);
96 have_some = true;
97 }
98
99 }
100 if (!have_some)
101 ShowStr(" ... none exist\n");
102 return true;
103 }
104
105 // display unused user variables
106 void
display_unused_var()107 display_unused_var()
108 {
109 unsigned int stackLen = variableStack.size();
110 extern char _grTempString[];
111 if (stackLen > 0) {
112 for (int i = stackLen - 1; i >= 0; i--) {
113 if (0 == variableStack[i].getCount()) {
114 const char* name = variableStack[i].get_name();
115 if (*(name + 1) != '.') { // avoid builtins
116 sprintf(_grTempString, "\
117 Warning: variable `%s' defined but not used\n", name);
118 ShowStr(_grTempString);
119 }
120 }
121 }
122 }
123 }
124
125 // is_var - return 0 if not a variable, or 1 if is
126 bool
is_var(const char * w)127 is_var(const char *w)
128 {
129 int len = strlen(w);
130 return (len > 2 && w[0] == '.' && w[-1 + len] == '.' ? true : false);
131 }
132 // is_var - return 0 if not a variable, or 1 if is
133 bool
is_var(const std::string & w)134 is_var(const std::string& w)
135 {
136 int len = w.size();
137 return (len > 2 && w[0] == '.' && w[-1 + len] == '.' ? true : false);
138 }
139
140 // for internal debugging
141 void
show_var_stack()142 show_var_stack()
143 {
144 unsigned stackLen = variableStack.size();
145 if (stackLen > 0) {
146 printf("Variable stack [\n");
147 for (int i = stackLen - 1; i >= 0; i--) {
148 printf(" %s = %f\n",
149 variableStack[i].get_name(),
150 variableStack[i].get_value());
151 }
152 printf("]\n");
153 }
154 }
155
156 // Delete variable, searching from end of stack
157 bool
delete_var(const std::string & name)158 delete_var(const std::string& name)
159 {
160 unsigned stackLen = variableStack.size();
161 for (int i = stackLen - 1; i >= 0; i--) {
162 if (name == variableStack[i].get_name()) {
163 //printf("DEBUG %s:%d DELETING var %d named <%s>\n",__FILE__,__LINE__,i,name.c_str());
164 for (unsigned int j = i; j < stackLen - 1; j++)
165 variableStack[j] = variableStack[j + 1];
166 variableStack.pop_back();
167 //printf("DEBUG %s:%d after handling 'delete var', the list is...\n",__FILE__,__LINE__);
168 return true;
169 }
170 }
171 return false;
172 }
173
174 // get_var() - get value of variable (incrementing the 'uses' flag)
175 //
176 // RETURN true if variable is defined and has a value
177 // RETURN false otherwise
178 bool
get_var(const char * name,double * value)179 get_var(const char *name, double *value)
180 {
181 *value = 0.0; // store something in case not found
182 if (!is_var(name))
183 return false;
184 // Following are special cases. They are not stored in the stack because
185 // it would take far too much time.
186 //
187 // If more variables are added to this list, be sure to make changes to
188 // gri.cc and extern.h, under comments which appear as follows:
189 //
190 // The following globals have symbolic names associated with them, and
191 // MUST be updated whenever these names are assigned to. See the note in
192 // put_var() in variable.c. The reason for the parallel C storage is
193 // that the following are accessed for every data point plotted. Certain
194 // other symbolic variables (like ..publication.. for example) are not
195 // accessed frequently, and hence have no parallel C storage as the
196 // following do. Thus they are safe against breakage.
197 if (!strcmp(name, "..trace..")) {
198 *value = (int) _griState.trace();
199 return true;
200 } else if (!strcmp(name, "..use_default_for_query..")) {
201 *value = (int) _use_default_for_query;
202 return true;
203 } else if (!strcmp(name, "..linewidth..")) {
204 *value = _griState.linewidth_line();
205 return true;
206 } else if (!strcmp(name, "..linewidthaxis..")) {
207 *value = _griState.linewidth_axis();
208 return true;
209 } else if (!strcmp(name, "..linewidthsymbol..")) {
210 *value = _griState.linewidth_symbol();
211 return true;
212 } else if (!strcmp(name, "..superuser..")) {
213 *value = double(_griState.superuser());
214 return true;
215 } else {
216 // Look it up in stack
217 int i;
218 unsigned stackLen = variableStack.size();
219 if (stackLen > 0) {
220 for (i = stackLen - 1; i >= 0; i--) {
221 #ifdef DEBUG_VARIABLE
222 printf("debug: check [%s] vs %d-th [%s]\n", name, i, variableStack[i].get_name());
223 #endif
224 if (!strcmp(name, variableStack[i].get_name())) {
225 *value = variableStack[i].get_value();
226 variableStack[i].incrementCount(); // record the usage
227 return true;
228 }
229 }
230 }
231 }
232 return false;
233 }
234
235 // put_var() -- assign value to name, creating new variable if necessary.
236 // If replace_existing=0 replace existing value.
237 // If replace_existing=1 create a new variable with the old name.
238 // RETURN NULL if can't do it.
239 bool
put_var(const char * name,double value,bool replace_existing)240 put_var(const char *name, double value, bool replace_existing)
241 {
242 void reset_top_of_plot(void);
243 int i;
244 unsigned stackLen = variableStack.size();
245 // put_var (): Certain special cases are stored in C variables too,
246 // for speed. (An example is ..xleft.., which is stored as _xleft and
247 // used all over the code.) Capture these and store the values. Also,
248 // some operations should be intercepted for general use later: e.g.
249 // changing ..ymargin.. requires calling reset_top_of_plot(). Note
250 // that this code fragment is easy to break, because addition of new
251 // internals means addition of code here. See also the note in gri.cc,
252 // where the internals are segmented.
253 if (!strcmp(name, "..trace..")) {
254 _griState.set_trace((floor(0.5 + value)) ? true : false);
255 } else if (!strcmp(name, "..use_default_for_query..")) {
256 _use_default_for_query = int(floor(0.5 + value)) ? true : false;
257 } else if (!strcmp(name, "..linewidth..")) {
258 _griState.set_linewidth_line(value);
259 } else if (!strcmp(name, "..linewidthaxis..")) {
260 _griState.set_linewidth_axis(value);
261 } else if (!strcmp(name, "..linewidthsymbol..")) {
262 _griState.set_linewidth_symbol(value);
263 } else if (!strcmp(name, "..superuser.."))
264 _griState.set_superuser((unsigned int)(floor(0.5 + value)));
265 else if (!strcmp(name, "..missingvalue.."))
266 gr_set_missing_value(value);
267 else if (!strcmp(name, "..xleft.."))
268 _xleft = value;
269 else if (!strcmp(name, "..xright.."))
270 _xright = value;
271 else if (!strcmp(name, "..xinc.."))
272 _xinc = value;
273 else if (!strcmp(name, "..ybottom.."))
274 _ybottom = value;
275 else if (!strcmp(name, "..ytop.."))
276 _ytop = value;
277 else if (!strcmp(name, "..yinc.."))
278 _yinc = value;
279 // Replace if on stack already.
280 if (replace_existing) {
281 if (stackLen) {
282 for (i = stackLen - 1; i >= 0; i--) {
283 if (!strcmp(name, variableStack[i].get_name())) {
284 variableStack[i].set_value(value);
285 return true;
286 }
287 }
288 }
289 }
290 // Store on end of stack.
291 GriVariable newVariable(name, value);
292 variableStack.push_back(newVariable);
293 if (!strcmp(name, "..ymargin..") || !strcmp(name, "..ysize.."))
294 reset_top_of_plot();
295 return true;
296 }
297