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