Line data Source code
1 : #include "cfoInterfaceLib/CFO_Compiler.hh"
2 :
3 : #include <iostream>
4 : #include <set>
5 :
6 : #define TRACE_NAME "CFO_Compiler"
7 :
8 : #include "dtcInterfaceLib/otsStyleCoutMacros.h"
9 :
10 : template<class T>
11 : std::string vectorToString( // defined in included .icc source
12 : const std::vector<T>& setToReturn,
13 : const std::string& delimeter = ", ");
14 :
15 : void getVectorFromString(
16 : const std::string& inputString,
17 : std::vector<std::string>& listToReturn,
18 : const std::set<char>& delimiter = {',', '|', '&'},
19 : const std::set<char>& whitespace = {' ', '\t', '\n', '\r'},
20 : std::vector<char>* listOfDelimiters = 0 /*,
21 : //bool decodeURIComponents = false */
22 : );
23 :
24 : template<class T>
25 : bool getNumber(const std::string& s, T& retValue);
26 :
27 : const std::string CFOLib::CFO_Compiler::MAIN_GOTO_LABEL = "MAIN";
28 :
29 : const uint64_t CFOLib::CFO_Compiler::FPGAClock_ = (1e9 / (40e6) /* 40MHz FPGAClock for calculating delays */); // period of FPGA clock in ns
30 :
31 : //========================================================================
32 0 : std::string CFOLib::CFO_Compiler::processFile(const std::string& sourceCodeFile,
33 : const std::string& binaryOutputFile)
34 : try
35 : {
36 0 : __COUT_INFO__ << "CFO_Compiler::processFile BEGIN";
37 :
38 0 : FILE* fp = fopen(sourceCodeFile.c_str(), "r");
39 0 : if (!fp)
40 : {
41 0 : __SS__ << "Input File (" << sourceCodeFile << ") didn't open. Does it exist?" << __E__;
42 0 : __SS_THROW__;
43 0 : }
44 :
45 0 : std::string line;
46 : char lineChars[100];
47 :
48 0 : txtLineNumber_ = 0;
49 0 : binLineNumber_ = 0;
50 0 : hasRequiredPlanEndOp_ = false; // require at least one END, REPEAT, or GOTO MAIN command to give a handle on switch Run Plan buffers to the CFO firmware
51 :
52 0 : loopStack_.clear();
53 0 : labelMap_.clear();
54 :
55 0 : output_.clear();
56 :
57 : // main line processing loop
58 0 : while (fgets(lineChars, 100, fp))
59 : {
60 0 : line = lineChars;
61 0 : while (strlen(lineChars) && lineChars[strlen(lineChars) - 1] != '\n' && fgets(lineChars, 100, fp))
62 0 : line += lineChars;
63 :
64 0 : ++txtLineNumber_;
65 0 : __COUTT__ << "Line number " << txtLineNumber_ << ": " << line << __E__;
66 :
67 0 : if (isComment(line))
68 : {
69 0 : __COUTT__ << txtLineNumber_ << ": is comment" << __E__;
70 0 : continue;
71 0 : }
72 :
73 : // read all arguments
74 0 : opArguments_.clear();
75 0 : getVectorFromString(line, opArguments_,
76 : {',', ' ', '\t', ';', '='} /*delimiter*/,
77 : {'\n', '\r'} /*white space (empty because white space is a delimiter)*/);
78 :
79 : // clean up empty strings (because of white space delimiter) and comments
80 0 : for (size_t i = 0; i < opArguments_.size(); ++i)
81 0 : if (opArguments_[i].length() == 0)
82 0 : opArguments_.erase(opArguments_.begin() + i--); // erase and rewind
83 0 : else if (opArguments_[i].length() >= 2 &&
84 0 : opArguments_[i][0] == '/' && opArguments_[i][1] == '/') // comment to the end
85 : {
86 : // erase remainder of arguments because they are commented out
87 0 : while (i < opArguments_.size())
88 0 : opArguments_.erase(opArguments_.begin() + i); // erase
89 0 : break;
90 : }
91 :
92 0 : __COUTTV__(opArguments_.size());
93 0 : __COUTTV__(vectorToString(opArguments_));
94 :
95 0 : if (!opArguments_.size()) // skip no arguments
96 : {
97 0 : __COUTT__ << txtLineNumber_ << ": is empty" << __E__;
98 0 : continue;
99 0 : }
100 :
101 : // CFOLib::CFO_Compiler::CFO_MACRO macroOpTest = parseMacro(opArguments_[0]);
102 : // if(macroOpTest == CFO_MACRO::NON_MACRO)
103 0 : processOp();
104 : // else
105 : // processMacro(macroOpTest);
106 :
107 : } // end main processing loop
108 :
109 0 : if (!hasRequiredPlanEndOp_)
110 : {
111 0 : __SS__ << "The Run Plan is missing an concluding operation that can be used as a moment to dynamically swith to the next run plan."
112 0 : " At least one of these commands is required in your run plan: END, REPEAT, or GOTO MAIN."
113 0 : << __E__;
114 0 : __SS_THROW__;
115 0 : }
116 :
117 0 : std::stringstream resultSs;
118 0 : resultSs << "Run plan text file: " << sourceCodeFile << __E__ << "was compiled to binary: " << binaryOutputFile << __E__;
119 0 : resultSs << "\n\nBinary Result (Op count = " << output_.size() / 8 << "):\n";
120 0 : int cnt = 0;
121 :
122 : // to view output file with 8-byte rows
123 : // hexdump -e '"%08_ax " 1/8 "%016x "' -e '"\n"' srcs/mu2e_pcie_utils/cfoInterfaceLib/Commands.bin
124 0 : std::ofstream outFile;
125 0 : outFile.open(binaryOutputFile.c_str(), std::ios::out | std::ios::binary);
126 :
127 0 : if (!(outFile.is_open()))
128 : {
129 0 : __SS__ << "Output File (" << binaryOutputFile << ") didn't open. Does it exist?" << __E__;
130 0 : __SS_THROW__;
131 0 : }
132 : char outStr[20];
133 0 : std::string binaryLine; // build least-significant byte on right display
134 0 : std::string tabStr = "";
135 0 : int markerCnt = 0;
136 0 : int binaryLineNumber = 0;
137 0 : for (auto c : output_)
138 : {
139 0 : outFile << c;
140 0 : if (cnt % 8 == 0)
141 : {
142 0 : binaryLine = ""; // clear
143 0 : sprintf(outStr, "%5d", (cnt / 8 + 1));
144 : resultSs << "\n"
145 0 : << tabStr << outStr << ": 0x";
146 : }
147 0 : else if (cnt % 4 == 0)
148 0 : binaryLine = " " + binaryLine;
149 :
150 0 : sprintf(outStr, "%2.2x", (uint16_t)c & 0x0FF);
151 0 : binaryLine = outStr + binaryLine;
152 :
153 : // show command for easier human understanding of binary
154 0 : if (cnt % 8 == 7) // last byte in output is most-significant (and the opcode)
155 : {
156 0 : auto instr = CFOLib::CFO_Compiler::CFO_INSTR(c);
157 0 : resultSs << binaryLine << " // " << translateOpCode(instr);
158 0 : if (instr == CFO_INSTR::MARKER)
159 0 : resultSs << " -----> #" << ++markerCnt;
160 0 : __COUTT__ << "binaryLine #" << ++binaryLineNumber << " = " << binaryLine << __E__;
161 0 : if (instr == CFO_INSTR::LOOP)
162 0 : tabStr += '\t';
163 0 : else if (instr == CFO_INSTR::DO_LOOP && tabStr.length())
164 0 : tabStr = tabStr.substr(0, tabStr.length() - 1);
165 : }
166 :
167 0 : ++cnt;
168 : }
169 0 : resultSs << "\n";
170 0 : outFile.close();
171 :
172 : // to view output file with 8-byte rows
173 : // hexdump -e '"%08_ax " 1/8 "%016x "' -e '"\n"' srcs/mu2e_pcie_utils/cfoInterfaceLib/Commands.bin
174 :
175 0 : __COUT_INFO__ << "CFO_Compiler::processFile Parsing Complete!";
176 0 : return resultSs.str();
177 0 : } // end processFile()
178 0 : catch (const std::runtime_error& e)
179 : {
180 0 : __SS__ << "Error caught wile compiling source text at '"
181 : << "<FILE>" << sourceCodeFile << "</FILE>' into binary run plan at '"
182 : << "<FILE>" << binaryOutputFile << "</FILE>.'\n"
183 0 : << e.what() << __E__;
184 :
185 0 : __SS_THROW__;
186 0 : }
187 0 : catch (...)
188 : {
189 0 : __SS__ << "Unknown error caught wile compiling source text at '"
190 : << "<FILE>" << sourceCodeFile << "</FILE>' into binary run plan at '"
191 0 : << "<FILE>" << binaryOutputFile << "</FILE>.'\n"
192 0 : << __E__;
193 : try
194 : {
195 0 : throw;
196 : } // one more try to printout extra info
197 0 : catch (const std::exception& e)
198 : {
199 0 : ss << "Exception message: " << e.what();
200 0 : }
201 0 : catch (...)
202 0 : {}
203 0 : __COUT_ERR__ << "\n"
204 0 : << ss.str();
205 0 : throw;
206 0 : } // end processFile() error handling
207 :
208 : //========================================================================
209 : // Boolean Operators
210 0 : bool CFOLib::CFO_Compiler::isComment(const std::string& line) // Checks if line has a comment.
211 : {
212 0 : for (size_t i = 0; i < line.length() - 1; ++i)
213 : {
214 0 : if (line[i] == ' ' ||
215 0 : line[i] == '\t' ||
216 0 : line[i] == '\r' ||
217 0 : line[i] == '\n') continue; // skip white space
218 :
219 0 : if (line[i] != '/') return false; // some non-comment char found
220 0 : if (line[i + 1] != '/')
221 : {
222 0 : __SS__ << "Single slash found; missing double slash for comment. (Line " << txtLineNumber_ << ")" << __E__;
223 0 : __SS_THROW__;
224 0 : }
225 0 : return true; // comment // string found
226 : }
227 0 : return true; // for empty string, consider as comment
228 : } // end isComment()
229 :
230 : const std::map<std::string, CFOLib::CFO_Compiler::CFO_INSTR> CFOLib::CFO_Compiler::OP_to_CODE_TRANSLATION = {
231 : {"HEARTBEAT", CFO_INSTR::HEARTBEAT},
232 : {"MARKER", CFO_INSTR::MARKER},
233 : {"DATA_REQUEST", CFO_INSTR::DATA_REQUEST},
234 : {"SET_TAG", CFO_INSTR::SET_TAG},
235 : {"INC_TAG", CFO_INSTR::INC_TAG},
236 : {"LOOP", CFO_INSTR::LOOP},
237 : {"DO_LOOP", CFO_INSTR::DO_LOOP},
238 : {"REPEAT", CFO_INSTR::REPEAT},
239 : {"WAIT", CFO_INSTR::WAIT},
240 : {"END", CFO_INSTR::END},
241 : {"GOTO_LABEL", CFO_INSTR::GOTO},
242 : {"LABEL", CFO_INSTR::LABEL},
243 : {"CLEAR_MODE_BITS", CFO_INSTR::CLEAR_MODE_BITS},
244 : {"SET_MODE_BITS", CFO_INSTR::SET_MODE_BITS},
245 : {"AND_MODE_BITS", CFO_INSTR::AND_MODE_BITS},
246 : {"OR_MODE_BITS", CFO_INSTR::OR_MODE_BITS},
247 : {"OR_SINGLESHOT_MODE_BITS", CFO_INSTR::OR_SINGLESHOT_MODE_BITS},
248 : {"SET_MODE", CFO_INSTR::SET_MODE},
249 : };
250 :
251 : //========================================================================
252 : // Switch Conversions
253 : // Turns strings into integers for switch statements.
254 0 : CFOLib::CFO_Compiler::CFO_INSTR CFOLib::CFO_Compiler::parseInstruction(const std::string& op)
255 : {
256 : try
257 : {
258 0 : return OP_to_CODE_TRANSLATION.at(op);
259 : }
260 0 : catch (...)
261 : {
262 0 : return CFO_INSTR::INVALID;
263 0 : }
264 : } // end parseInstruction()
265 :
266 : const std::map<uint8_t, std::string> CFOLib::CFO_Compiler::CODE_to_OP_TRANSLATION = {
267 : {(uint8_t)CFOLib::CFO_Compiler::CFO_INSTR::HEARTBEAT, "HEARTBEAT"},
268 : {(uint8_t)CFOLib::CFO_Compiler::CFO_INSTR::MARKER, "MARKER"},
269 : {(uint8_t)CFOLib::CFO_Compiler::CFO_INSTR::DATA_REQUEST, "DATA_REQUEST"},
270 : {(uint8_t)CFOLib::CFO_Compiler::CFO_INSTR::SET_TAG, "SET_TAG"},
271 : {(uint8_t)CFOLib::CFO_Compiler::CFO_INSTR::INC_TAG, "INC_TAG"},
272 : {(uint8_t)CFOLib::CFO_Compiler::CFO_INSTR::LOOP, "LOOP"},
273 : {(uint8_t)CFOLib::CFO_Compiler::CFO_INSTR::DO_LOOP, "DO_LOOP"},
274 : {(uint8_t)CFOLib::CFO_Compiler::CFO_INSTR::REPEAT, "REPEAT"},
275 : {(uint8_t)CFOLib::CFO_Compiler::CFO_INSTR::WAIT, "WAIT"},
276 : {(uint8_t)CFOLib::CFO_Compiler::CFO_INSTR::END, "END"},
277 : {(uint8_t)CFOLib::CFO_Compiler::CFO_INSTR::GOTO, "GOTO_LABEL"},
278 : {(uint8_t)CFOLib::CFO_Compiler::CFO_INSTR::LABEL, "LABEL"},
279 : {(uint8_t)CFOLib::CFO_Compiler::CFO_INSTR::NOOP, "LABEL"},
280 : {(uint8_t)CFOLib::CFO_Compiler::CFO_INSTR::CLEAR_MODE_BITS, "CLEAR_MODE_BITS"},
281 : {(uint8_t)CFOLib::CFO_Compiler::CFO_INSTR::SET_MODE_BITS, "SET_MODE_BITS"},
282 : {(uint8_t)CFOLib::CFO_Compiler::CFO_INSTR::AND_MODE_BITS, "AND_MODE_BITS"},
283 : {(uint8_t)CFOLib::CFO_Compiler::CFO_INSTR::OR_MODE_BITS, "OR_MODE_BITS"},
284 : {(uint8_t)CFOLib::CFO_Compiler::CFO_INSTR::OR_SINGLESHOT_MODE_BITS, "OR_SINGLESHOT_MODE_BITS"},
285 : {(uint8_t)CFOLib::CFO_Compiler::CFO_INSTR::SET_MODE, "SET_MODE"},
286 : };
287 :
288 : //========================================================================
289 0 : const std::string& CFOLib::CFO_Compiler::translateOpCode(CFOLib::CFO_Compiler::CFO_INSTR opCode)
290 : {
291 : try
292 : {
293 0 : return CODE_to_OP_TRANSLATION.at((uint8_t)opCode);
294 : }
295 0 : catch (...)
296 : {
297 0 : __SS__ << "No tranlation found for opCode " << (int)opCode << __E__;
298 0 : __SS_THROW__;
299 0 : }
300 : } // end translateOpCode()
301 :
302 : //========================================================================
303 0 : void CFOLib::CFO_Compiler::outParameter(uint64_t paramBuf) // Writes the 6 byte parameter out based on a given integer.
304 : {
305 0 : paramBuf &= 0xFFFFFFFFFFFF; // Enforce 6 bytes
306 0 : auto paramBufPtr = reinterpret_cast<int8_t*>(¶mBuf);
307 0 : for (int i = 0; i < 6; ++i)
308 : {
309 0 : output_.push_back(paramBufPtr[i]);
310 0 : __COUTT__ << "[" << output_.size() << "] ==> output_ 0x" << std::hex << ((uint16_t)output_.back() & 0x0FF) << __E__;
311 : }
312 0 : } // end outParameter()
313 :
314 : //========================================================================
315 : // processOp
316 : // Outputs a byte stream based on the buffers.
317 : // opArguments_ must be checked before to be size >= 1
318 0 : void CFOLib::CFO_Compiler::processOp()
319 : {
320 : // calculate line numbers that match this hexdump call (all usage of bin is relative/differences):
321 : // hexdump -e '"%08_ax | " 1/8 "%016x "' -e '"\n"' CFOCommands.bin | cat -n
322 0 : binLineNumber_ = 1 + output_.size() / 8;
323 0 : __COUTT__ << "binLineNumber_: " << (int)binLineNumber_ << __E__;
324 :
325 0 : CFO_INSTR instructionOpcode = parseInstruction(opArguments_[0]);
326 0 : if (instructionOpcode == CFO_INSTR::INVALID)
327 : {
328 0 : __SS__ << "On Line " << txtLineNumber_ << ", invalid instruction '" << opArguments_[0] << "' found. " << __E__;
329 0 : __SS_THROW__;
330 0 : }
331 :
332 0 : int64_t parameterCalc = calculateParameterAndErrorCheck(instructionOpcode);
333 0 : __COUTTV__((int)instructionOpcode);
334 0 : __COUTTV__(parameterCalc);
335 :
336 0 : if (instructionOpcode == CFO_INSTR::SET_MODE_BITS ||
337 : instructionOpcode == CFO_INSTR::SET_MODE)
338 : {
339 0 : __COUTT__ << "MASK off 0x" << std::hex << modeClearMask_ << __E__;
340 : // treat as two ops: an AND and an OR
341 0 : outParameter(modeClearMask_);
342 0 : output_.push_back(0x00);
343 0 : __COUTT__ << "[" << output_.size() << "] ==> output_ 0x" << std::hex << std::setfill('0') << std::setprecision(2) << (uint16_t)output_.back() << __E__;
344 0 : output_.push_back(static_cast<char>(CFO_INSTR::AND_MODE_BITS));
345 0 : __COUTT__ << "[" << output_.size() << "] ==> output_ 0x" << std::hex << std::setfill('0') << std::setprecision(2) << (uint16_t)output_.back() << __E__;
346 :
347 0 : instructionOpcode = CFO_INSTR::OR_MODE_BITS;
348 0 : __COUTT__ << "MASK on 0x" << std::hex << parameterCalc << __E__;
349 : }
350 :
351 0 : outParameter(parameterCalc);
352 :
353 0 : if (instructionOpcode == CFO_INSTR::REPEAT ||
354 0 : instructionOpcode == CFO_INSTR::END ||
355 : instructionOpcode == CFO_INSTR::GOTO)
356 0 : hasRequiredPlanEndOp_ = true;
357 0 : else if (instructionOpcode == CFO_INSTR::LABEL) // in binary, treat as NOOP
358 0 : instructionOpcode = CFO_INSTR::NOOP;
359 0 : else if (instructionOpcode == CFO_INSTR::CLEAR_MODE_BITS) // treat clear as AND
360 0 : instructionOpcode = CFO_INSTR::AND_MODE_BITS;
361 :
362 0 : output_.push_back(0x00);
363 0 : __COUTT__ << "[" << output_.size() << "] ==> output_ 0x" << std::hex << std::setfill('0') << std::setprecision(2) << (uint16_t)output_.back() << __E__;
364 0 : output_.push_back(static_cast<char>(instructionOpcode));
365 0 : __COUTT__ << "[" << output_.size() << "] ==> output_ 0x" << std::hex << std::setfill('0') << std::setprecision(2) << (uint16_t)output_.back() << __E__;
366 :
367 0 : } // end processOp()
368 :
369 : //========================================================================
370 : // Parameter Calculations
371 : // Calculates the parameter for the instruction (if needed) and does error checking
372 0 : uint64_t CFOLib::CFO_Compiler::calculateParameterAndErrorCheck(CFO_INSTR instructionOpcode)
373 : {
374 0 : size_t opArgCount = opArguments_.size();
375 0 : modeClearMask_ = 0; // clear
376 :
377 0 : __COUTT__ << "calculateParameterAndErrorCheck... Instruction: " << opArguments_[0] << ", Parameter count: " << opArgCount << __E__;
378 :
379 : uint64_t value;
380 :
381 0 : switch (instructionOpcode)
382 : {
383 0 : case CFO_INSTR::HEARTBEAT:
384 0 : if (opArgCount != 3 || opArguments_[1] != "event_mode")
385 : {
386 0 : __SS__ << "On Line " << txtLineNumber_ << ", invalid '" << opArguments_[0] << "' arguments found. There must be 1 named parameter, and " << opArgCount / 2 << " were found. Here is the syntax := \"" << opArguments_[0] << " event_mode=[value]\" ... "
387 : "The source instruction was \""
388 0 : << vectorToString(opArguments_, " " /*delimieter*/) << ".\"\n\n"
389 : "The event_mode value can be the string \"registered\" to use the locally maintained event_mode value of the compiler,"
390 0 : " otherwise a number (hex 0x### and binary b### syntax allowed) for event_mode should be specified (0 for null HEARTBEAT)."
391 0 : << __E__;
392 0 : __SS_THROW__;
393 0 : }
394 :
395 0 : if (opArguments_[2] == "registered")
396 0 : return -1; // indicate to FPGA to use registered event mode
397 :
398 0 : if (!getNumber(opArguments_[2], value))
399 : {
400 0 : __SS__ << "On Line (" << txtLineNumber_ << "), for instruction '" << opArguments_[0] << ",' "
401 0 : << "the parameter value '" << opArguments_[1] << " = " << opArguments_[2] << "' is not a valid number. "
402 0 : << "Use 0x### to indicate hex and b### to indicate binary; otherwise, decimal is inferred." << __E__;
403 0 : __SS_THROW__;
404 0 : }
405 0 : return value;
406 :
407 0 : case CFO_INSTR::MARKER:
408 0 : if (opArgCount != 1)
409 : {
410 0 : __SS__ << "On Line " << txtLineNumber_ << ", '" << opArguments_[0] << "' has extraneous arguments; there must be no additional arguments." << std::endl;
411 0 : __SS_THROW__;
412 0 : }
413 0 : break;
414 :
415 0 : case CFO_INSTR::DATA_REQUEST:
416 0 : if (opArgCount != 3 || opArguments_[1] != "request_tag")
417 : {
418 0 : __SS__ << "On Line " << txtLineNumber_ << ", invalid '" << opArguments_[0] << "' arguments found. There must be 1 named parameter, and " << opArgCount / 2 << " were found. Here is the syntax := \"" << opArguments_[0] << " request_tag=[value]\n\n\""
419 : " The request_tag value can be the string \"current\" to request the most recent Event Window Tag data,"
420 0 : " otherwise a number (hex 0x### and binary b### syntax allowed) for request_tag should be specified."
421 0 : << __E__;
422 0 : __SS_THROW__;
423 0 : }
424 0 : if (opArguments_[2] == "current")
425 0 : return -1;
426 0 : if (!getNumber(opArguments_[2], value))
427 : {
428 0 : __SS__ << "On Line (" << txtLineNumber_ << "), for instruction '" << opArguments_[0] << ",' "
429 0 : << "the parameter value '" << opArguments_[1] << " = " << opArguments_[2] << "' is not a valid number. "
430 0 : << "Use 0x### to indicate hex and b### to indicate binary; otherwise, decimal is inferred." << __E__;
431 0 : __SS_THROW__;
432 0 : }
433 0 : return value;
434 :
435 0 : case CFO_INSTR::SET_TAG:
436 0 : if (opArgCount != 2)
437 : {
438 0 : __SS__ << "On Line " << txtLineNumber_ << ", invalid '" << opArguments_[0] << "' arguments found. There must be 2 arguments, and " << opArgCount << " were found. Here is the syntax := \"" << opArguments_[0] << " [value]\" ... "
439 : "The source instruction was \""
440 0 : << vectorToString(opArguments_, " " /*delimieter*/) << ".\"\n\n"
441 0 : "For the set tag number, hex 0x### and binary b### syntax is allowed, otherwise decimal is inferred."
442 0 : << __E__;
443 0 : __SS_THROW__;
444 0 : }
445 0 : if (!getNumber(opArguments_[1], value))
446 : {
447 0 : __SS__ << "On Line (" << txtLineNumber_ << "), for instruction '" << opArguments_[0] << ",' "
448 0 : << "the parameter value '" << opArguments_[1] << "' is not a valid number. "
449 0 : << "Use 0x### to indicate hex and b### to indicate binary; otherwise, decimal is inferred." << __E__;
450 0 : __SS_THROW__;
451 0 : }
452 0 : return value;
453 :
454 0 : case CFO_INSTR::INC_TAG:
455 0 : if (opArgCount == 1) // default to increment by 1 if no value
456 0 : return 1;
457 0 : if (opArgCount != 3 || opArguments_[1] != "add_value")
458 : {
459 0 : __SS__ << "On Line " << txtLineNumber_ << ", invalid '" << opArguments_[0] << "' arguments found. There must be 0 or 1 named parameter, and " << opArgCount << " were found. Here is the syntax := \"" << opArguments_[0] << " add_value=[value]\" ... "
460 : "The source instruction was \""
461 0 : << vectorToString(opArguments_, " " /*delimieter*/) << ".\"\n\n"
462 : " If no named parameter, then increment by 1 is assumed,"
463 0 : " otherwise a number (hex 0x### and binary b### syntax allowed) for add_value should be specified."
464 0 : << __E__;
465 0 : __SS_THROW__;
466 0 : }
467 :
468 0 : if (!getNumber(opArguments_[2], value))
469 : {
470 0 : __SS__ << "On Line (" << txtLineNumber_ << "), for instruction '" << opArguments_[0] << ",' "
471 0 : << "the parameter value '" << opArguments_[1] << " = " << opArguments_[2] << "' is not a valid number. "
472 0 : << "Use 0x### to indicate hex and b### to indicate binary; otherwise, decimal is inferred." << __E__;
473 0 : __SS_THROW__;
474 0 : }
475 0 : return value;
476 :
477 0 : case CFO_INSTR::WAIT: {
478 0 : if (opArgCount != 3 ||
479 0 : (opArguments_[2] != "clocks" && opArguments_[2] != "ns" &&
480 0 : opArguments_[2] != "s" && opArguments_[2] != "ms" && opArguments_[2] != "us" &&
481 0 : opArguments_[2] != "RF0") ||
482 0 : (opArguments_[1] != "NEXT" && opArguments_[2] == "RF0"))
483 : {
484 0 : __SS__ << "On Line " << txtLineNumber_ << ", invalid '" << opArguments_[0] << "' arguments found. There must be 3 arguments, and " << opArgCount << " were found. Here is the syntax := \"" << opArguments_[0] << " [value] [units]\" or \"" << opArguments_[0] << " NEXT RF0\" ... "
485 : "The source instruction was \""
486 0 : << vectorToString(opArguments_, " " /*delimieter*/) << ".\"\n\n"
487 : "For the units, accepted unit strings are clocks, ns, us, ms, and s.\n\n"
488 0 : << "If NEXT RF0 is provided, then the '" << opArguments_[0] << "' will last until the next RF-0 accelerator marker is received.\n\n"
489 0 : << "For the wait value, hex 0x### and binary b### syntax is allowed, otherwise decimal is inferred." << __E__;
490 0 : __SS_THROW__;
491 0 : }
492 0 : if (opArguments_[1] == "NEXT")
493 0 : return -1; // use all 1s to indicate wait for next RF-0 marker
494 :
495 0 : if (!getNumber(opArguments_[1], value))
496 : {
497 0 : __SS__ << "On Line (" << txtLineNumber_ << "), for instruction '" << opArguments_[0] << ",' "
498 0 : << "the parameter value '" << opArguments_[1] << " " << opArguments_[2] << "' is not a valid number. "
499 0 : << "Use 0x### to indicate hex and b### to indicate binary; otherwise, decimal is inferred." << __E__;
500 0 : __SS_THROW__;
501 0 : }
502 : // test floating point in case integer conversion dropped something
503 0 : double timeValue = strtod(opArguments_[1].c_str(), 0);
504 0 : __COUTTV__(timeValue);
505 0 : if (timeValue < value)
506 0 : timeValue = value;
507 :
508 0 : __COUTTV__(FPGAClock_);
509 0 : __COUTTV__(value);
510 0 : __COUTTV__(timeValue);
511 :
512 0 : if (opArguments_[2] == "s") // Wait wanted in seconds
513 0 : return timeValue * 1e9 / FPGAClock_;
514 0 : else if (opArguments_[2] == "ms") // Wait wanted in milliseconds
515 0 : return timeValue * 1e6 / FPGAClock_;
516 0 : else if (opArguments_[2] == "us") // Wait wanted in microseconds
517 0 : return timeValue * 1e3 / FPGAClock_;
518 0 : else if (opArguments_[2] == "ns") // Wait wanted in nanoseconds
519 : {
520 0 : if ((value % FPGAClock_) != 0)
521 : {
522 0 : __SS__ << "FPGA can only wait in multiples of " << FPGAClock_ << " ns: the input value '" << value << "' yields a remainder of " << (value % FPGAClock_) << __E__;
523 0 : __SS_THROW__;
524 0 : }
525 0 : return value / FPGAClock_;
526 : }
527 0 : else if (opArguments_[2] == "clocks") // Wait wanted in FPGA clocks
528 0 : return value;
529 : else // impossible
530 : {
531 0 : __SS__ << "WAIT command is missing unit type after parameter. Accepted unit types are clocks, ns, us, ms, and s." << __E__;
532 0 : __SS_THROW__;
533 0 : }
534 : }
535 0 : case CFO_INSTR::LOOP:
536 0 : if (opArgCount != 2)
537 : {
538 0 : __SS__ << "On Line " << txtLineNumber_ << ", invalid '" << opArguments_[0] << "' arguments found. There must be 2 arguments, and " << opArgCount << " were found. Here is the syntax := \"" << opArguments_[0] << " [value]\" ... "
539 : "The source instruction was \""
540 0 : << vectorToString(opArguments_, " " /*delimieter*/) << ".\"\n\n"
541 0 : "For the loop count, hex 0x### and binary b### syntax is allowed, otherwise decimal is inferred."
542 0 : << __E__;
543 0 : __SS_THROW__;
544 0 : }
545 0 : if (!getNumber(opArguments_[1], value))
546 : {
547 0 : __SS__ << "On Line (" << txtLineNumber_ << "), for instruction '" << opArguments_[0] << ",' "
548 0 : << "the parameter value '" << opArguments_[1] << "' is not a valid number. "
549 0 : << "Use 0x### to indicate hex and b### to indicate binary; otherwise, decimal is inferred." << __E__;
550 0 : __SS_THROW__;
551 0 : }
552 0 : __COUTT__ << "loopStack_ = " << binLineNumber_ << " at " << loopStack_.size() << __E__;
553 0 : loopStack_.push_back(binLineNumber_);
554 0 : return value;
555 :
556 0 : case CFO_INSTR::DO_LOOP:
557 0 : if (opArgCount != 1)
558 : {
559 0 : __SS__ << "On Line " << txtLineNumber_ << ", '" << opArguments_[0] << "' has extraneous arguments; there must be no additional arguments." << std::endl;
560 0 : __SS_THROW__;
561 0 : }
562 0 : if (!loopStack_.empty())
563 : {
564 : uint64_t loopLine;
565 0 : loopLine = loopStack_.back();
566 0 : value = (binLineNumber_ - loopLine);
567 0 : __COUTT__ << "DO_LOOP from [" << binLineNumber_ << "], loop line popped = " << loopLine << ", must go back " << value << " lines.";
568 0 : loopStack_.pop_back();
569 0 : __COUTT__ << "DO_LOOP loopStack_ " << loopStack_.size() << " w/parameterCalc=" << value;
570 0 : return value;
571 : }
572 : else
573 : {
574 0 : __SS__ << "Identified at Line " << txtLineNumber_ << ", LOOP/DO_LOOP counts don't match. (There are more DO_LOOP's)";
575 0 : __SS_THROW__;
576 0 : }
577 : break; // impossible to get here
578 0 : case CFO_INSTR::REPEAT:
579 : case CFO_INSTR::END:
580 0 : if (opArgCount != 1)
581 : {
582 0 : __SS__ << "On Line " << txtLineNumber_ << ", '" << opArguments_[0] << "' has extraneous arguments; there must be no additional arguments." << std::endl;
583 0 : __SS_THROW__;
584 0 : }
585 0 : if (!loopStack_.empty()) // loop stack must be empty at END or REPEAT
586 : {
587 0 : __SS__ << "Identified at Line " << txtLineNumber_ << ", LOOP/DO_LOOP counts don't match (There are less DO_LOOP's)";
588 0 : __SS_THROW__;
589 0 : }
590 0 : break;
591 :
592 0 : case CFO_INSTR::GOTO:
593 0 : if (opArgCount != 1)
594 : {
595 0 : __SS__ << "On Line " << txtLineNumber_ << ", '" << opArguments_[0] << "' has extraneous arguments; there must be no additional arguments." << std::endl;
596 0 : __SS_THROW__;
597 0 : }
598 :
599 0 : if (!loopStack_.empty()) // loop stack must be empty at GOTO
600 : {
601 0 : __SS__ << "Identified at Line " << txtLineNumber_ << ", LOOP/DO_LOOP counts don't match (There are less DO_LOOP's); a GOTO can not be used to exit a loop.";
602 0 : __SS_THROW__;
603 0 : }
604 0 : opArguments_.push_back(MAIN_GOTO_LABEL); // force label MAIN because only one GOTO ever makes sense if jumping in and out of loops is not allowed (since there are on IF statements)
605 0 : opArguments_[1] = MAIN_GOTO_LABEL; // in case of comments, push_back doesnt work
606 0 : __COUTT__ << "Goto lookup label '" << opArguments_[1] << "'" << __E__;
607 :
608 0 : if (labelMap_.find(opArguments_[1]) == labelMap_.end())
609 : {
610 0 : for (auto& labelPair : labelMap_)
611 0 : __COUTTV__(labelPair.first);
612 0 : __SS__ << "Missing label '" << opArguments_[1] << "' needed for GOTO at line " << txtLineNumber_ << __E__;
613 0 : __SS_THROW__;
614 0 : }
615 0 : return labelMap_.at(opArguments_[1]);
616 :
617 0 : case CFO_INSTR::LABEL:
618 0 : if (opArgCount != 1)
619 : {
620 0 : __SS__ << "On Line " << txtLineNumber_ << ", '" << opArguments_[0] << "' has extraneous arguments; there must be no additional arguments." << std::endl;
621 0 : __SS_THROW__;
622 0 : }
623 :
624 0 : if (!loopStack_.empty()) // loop stack must be empty at GOTO
625 : {
626 0 : __SS__ << "Identified at Line " << txtLineNumber_ << ", LOOP/DO_LOOP counts don't match (There are less DO_LOOP's); a GOTO_LABEL can not be used to enter a loop.";
627 0 : __SS_THROW__;
628 0 : }
629 :
630 : // for now, leave label map in case if statements exist in the future, and more lables are allowed.
631 0 : opArguments_.push_back(MAIN_GOTO_LABEL);
632 0 : opArguments_[1] = MAIN_GOTO_LABEL; // in case of comments, push_back doesnt work
633 :
634 0 : __COUTT__ << "Saving map for label '" << opArguments_[1] << "' to line " << binLineNumber_ << __E__;
635 0 : if (labelMap_.find(opArguments_[1]) != labelMap_.end())
636 : {
637 0 : __SS__ << "On Line " << txtLineNumber_ << ", " << opArguments_[0] << " encountered when one already exists in the run plan. Only one " << opArguments_[0] << " allowed in a run plan." << __E__;
638 0 : __SS_THROW__;
639 0 : }
640 0 : labelMap_[opArguments_[1]] = binLineNumber_;
641 0 : return 1; // indicate MAIN LABEL to CFO firmware as valid location to dynamically switch run plans
642 : // otherwise, a normal noop label
643 0 : case CFO_INSTR::SET_MODE_BITS:
644 : case CFO_INSTR::AND_MODE_BITS:
645 : case CFO_INSTR::OR_MODE_BITS:
646 : case CFO_INSTR::OR_SINGLESHOT_MODE_BITS: {
647 0 : if (opArgCount != 7 ||
648 0 : opArguments_[1] != "start_bit" ||
649 0 : opArguments_[3] != "bit_count" ||
650 0 : opArguments_[5] != "value")
651 : {
652 0 : __SS__ << "On Line " << txtLineNumber_ << ", invalid '" << opArguments_[0] << "' arguments found. There must be 7 arguments, and " << opArgCount << " were found. Here is the syntax := \"" << opArguments_[0] << " start_bit=[value] bit_count=[value] value=[value]\" ... "
653 : "The source instruction was \""
654 0 : << vectorToString(opArguments_, " " /*delimieter*/) << ".\"\n\n"
655 0 : "For the number values, hex 0x### and binary b### syntax is allowed, otherwise decimal is inferred."
656 0 : << __E__;
657 0 : __SS_THROW__;
658 0 : }
659 :
660 : uint16_t startBit;
661 0 : if (!getNumber(opArguments_[2], startBit))
662 : {
663 0 : __SS__ << "On Line (" << txtLineNumber_ << "), the "
664 0 : << "start_bit value '" << opArguments_[2] << "' is not a valid number. "
665 0 : << "Use 0x### to indicate hex and b### to indicate binary; otherwise, decimal is inferred." << __E__;
666 0 : __SS_THROW__;
667 0 : }
668 :
669 0 : if (startBit > 47)
670 : {
671 0 : __SS__ << "On Line (" << txtLineNumber_ << "), "
672 0 : << "start_bit value must be an integer between 0 and 47." << __E__;
673 0 : __SS_THROW__;
674 0 : }
675 :
676 : uint16_t bitCount;
677 0 : if (!getNumber(opArguments_[4], bitCount))
678 : {
679 0 : __SS__ << "On Line (" << txtLineNumber_ << "), the "
680 0 : << "bit_count value '" << opArguments_[4] << "' is not a valid number. "
681 0 : << "Use 0x### to indicate hex and b### to indicate binary; otherwise, decimal is inferred." << __E__;
682 0 : __SS_THROW__;
683 0 : }
684 :
685 0 : if (startBit + bitCount > 48 || bitCount == 0)
686 : {
687 0 : __SS__ << "On Line (" << txtLineNumber_ << "), "
688 0 : << "bit_count value must be a positive integer and fit within 48-bits range 0-to-47 with offset start_bit=" << startBit << ". The end bit was calculated as end_bit=" << startBit + bitCount - 1 << __E__;
689 0 : __SS_THROW__;
690 0 : }
691 :
692 0 : if (!getNumber(opArguments_[6], value))
693 : {
694 0 : __SS__ << "On Line (" << txtLineNumber_ << "), the "
695 0 : << "bit_count value '" << opArguments_[6] << "' is not a valid number. "
696 0 : << "Use 0x### to indicate hex and b### to indicate binary; otherwise, decimal is inferred." << __E__;
697 0 : __SS_THROW__;
698 0 : }
699 :
700 0 : if (value >= (uint64_t(1) << bitCount) &&
701 0 : opArguments_[6][0] != '~') // only if not an inverted value
702 : {
703 0 : __SS__ << "On Line (" << txtLineNumber_ << "), "
704 : << "the set value range is defined by bit_count, and so must be an integer between 0 and 2^"
705 0 : << "(bit_count=" << bitCount << ") - 1. The value is " << value << " which is greater than or equal to max range = " << (uint64_t(1) << bitCount) << __E__;
706 : ss << "\n\n"
707 0 : << "If an inverted value is intended, use '~' before the number to indicate inversion, and the value will be masked to fit within the bit_count range.";
708 0 : __SS_THROW__;
709 0 : }
710 :
711 0 : __COUTTV__(startBit);
712 0 : __COUTTV__(bitCount);
713 0 : __COUTTV__(value);
714 0 : uint64_t bitmask = 0;
715 0 : for (uint16_t i = startBit; i < startBit + bitCount; ++i)
716 0 : bitmask |= (uint64_t(1) << i);
717 :
718 0 : switch (instructionOpcode)
719 : {
720 0 : case CFO_INSTR::SET_MODE_BITS: // treat SET_MODE_BITS as two ops in hardware: an AND and an OR
721 :
722 0 : __COUTT__ << "bitmask 0x" << std::hex << bitmask << __E__;
723 0 : value <<= startBit; // shift then mask
724 0 : value &= bitmask; // force bitcount in case of ~ inverted value
725 0 : modeClearMask_ = ~bitmask; // CLEAR
726 0 : __COUTT__ << "modeClearMask_ 0x" << std::hex << modeClearMask_ << __E__;
727 0 : return value; // SET
728 :
729 0 : case CFO_INSTR::AND_MODE_BITS:
730 :
731 0 : value <<= startBit; // shift then mask
732 0 : value |= ~bitmask; // force ignore outside of bitcount in case of ~ inverted value
733 0 : return value; // AND
734 :
735 0 : case CFO_INSTR::OR_MODE_BITS:
736 : case CFO_INSTR::OR_SINGLESHOT_MODE_BITS:
737 :
738 0 : value <<= startBit; // shift then mask
739 0 : value &= bitmask; // force ignore outside of bitcount in case of ~ inverted value
740 0 : return value; // OR / OR (single-shot)
741 :
742 0 : default: {
743 0 : __COUTTV__((int)instructionOpcode);
744 0 : __SS__ << "On Line " << txtLineNumber_ << ", invalid instruction '" << opArguments_[0] << "' encountered.'" << __E__;
745 0 : __SS_THROW__;
746 0 : }
747 : }
748 : }
749 0 : case CFO_INSTR::CLEAR_MODE_BITS: {
750 0 : if (opArgCount != 5 ||
751 0 : opArguments_[1] != "start_bit" ||
752 0 : opArguments_[3] != "bit_count")
753 : {
754 0 : __SS__ << "On Line " << txtLineNumber_ << ", invalid '" << opArguments_[0] << "' arguments found. There must be 5 arguments, and " << opArgCount << " were found. Here is the syntax := \"" << opArguments_[0] << " start_bit=[value] bit_count=[value]\" ... "
755 : "The source instruction was \""
756 0 : << vectorToString(opArguments_, " " /*delimieter*/) << ".\"\n\n"
757 0 : "For the number values, hex 0x### and binary b### syntax is allowed, otherwise decimal is inferred."
758 0 : << __E__;
759 0 : __SS_THROW__;
760 0 : }
761 :
762 : uint16_t startBit;
763 0 : if (!getNumber(opArguments_[2], startBit))
764 : {
765 0 : __SS__ << "On Line (" << txtLineNumber_ << "), the "
766 0 : << "start_bit value '" << opArguments_[2] << "' is not a valid number. "
767 0 : << "Use 0x### to indicate hex and b### to indicate binary; otherwise, decimal is inferred." << __E__;
768 0 : __SS_THROW__;
769 0 : }
770 :
771 0 : if (startBit > 47)
772 : {
773 0 : __SS__ << "On Line (" << txtLineNumber_ << "), "
774 0 : << "start_bit value must be an integer between 0 and 47." << __E__;
775 0 : __SS_THROW__;
776 0 : }
777 :
778 : uint16_t bitCount;
779 0 : if (!getNumber(opArguments_[4], bitCount))
780 : {
781 0 : __SS__ << "On Line (" << txtLineNumber_ << "), the "
782 0 : << "bit_count value '" << opArguments_[4] << "' is not a valid number. "
783 0 : << "Use 0x### to indicate hex and b### to indicate binary; otherwise, decimal is inferred." << __E__;
784 0 : __SS_THROW__;
785 0 : }
786 :
787 0 : if (startBit + bitCount > 48 || bitCount == 0)
788 : {
789 0 : __SS__ << "On Line (" << txtLineNumber_ << "), "
790 0 : << "bit_count value must be a positive integer and fit within 48-bits range 0:47 with offset start_bit=" << startBit << ". The end bit was calculated as end_bit=" << startBit + bitCount - 1 << __E__;
791 0 : __SS_THROW__;
792 0 : }
793 :
794 0 : uint64_t bitmask = 0;
795 0 : for (uint16_t i = startBit; i < startBit + bitCount; ++i)
796 0 : bitmask |= (uint64_t(1) << i);
797 0 : return ~bitmask; // CLEAR
798 : }
799 0 : case CFO_INSTR::SET_MODE: // treat SET_MODE as two ops in hardware: an AND and an OR
800 : {
801 0 : if (opArgCount != 2)
802 : {
803 0 : __SS__ << "On Line " << txtLineNumber_ << ", invalid '" << opArguments_[0] << "' arguments found. There must be 5 arguments, and " << opArgCount << " were found. Here is the syntax := \"" << opArguments_[0] << " [value]\" ... "
804 : "The source instruction was \""
805 0 : << vectorToString(opArguments_, " " /*delimieter*/) << ".\"\n\n"
806 0 : "For the Event Mode value, hex 0x### and binary b### syntax is allowed, otherwise decimal is inferred."
807 0 : << __E__;
808 0 : __SS_THROW__;
809 0 : }
810 :
811 0 : if (!getNumber(opArguments_[1], value))
812 : {
813 0 : __SS__ << "On Line (" << txtLineNumber_ << "), the "
814 0 : << "start_bit value '" << opArguments_[1] << "' is not a valid number. "
815 0 : << "Use 0x### to indicate hex and b### to indicate binary; otherwise, decimal is inferred." << __E__;
816 0 : __SS_THROW__;
817 0 : }
818 :
819 : // treat SET_MODE as two ops in hardware: an AND and an OR
820 0 : modeClearMask_ = 0; // CLEAR
821 0 : return value; // SET
822 : }
823 0 : default: {
824 0 : __COUTTV__((int)instructionOpcode);
825 0 : __SS__ << "On Line " << txtLineNumber_ << ", invalid instruction '" << opArguments_[0] << "' encountered.'" << __E__;
826 0 : __SS_THROW__;
827 0 : }
828 : } // end primary switch statement
829 :
830 0 : return 0; // default parameter value
831 : } // end calculateParameterAndErrorCheck()
832 :
833 : //==============================================================================
834 : // static template function (copied from ots::StringMacros)
835 : // for all numbers, but not bools (bools has a specialized template definition)
836 : // return false if string is not a number
837 : template<class T>
838 0 : bool getNumber(const std::string& s, T& retValue)
839 : {
840 0 : __COUTVS__(2, s);
841 :
842 : // extract set of potential numbers and operators
843 0 : std::vector<std::string> numbers;
844 0 : std::vector<char> ops;
845 :
846 0 : getVectorFromString(s,
847 : numbers,
848 0 : /*delimiter*/ std::set<char>({'+', '-', '*', '/', '~'}),
849 0 : /*whitespace*/ std::set<char>({' ', '\t', '\n', '\r'}),
850 : &ops);
851 :
852 0 : __COUTVS__(2, vectorToString(numbers));
853 0 : __COUTVS__(2, vectorToString(ops));
854 :
855 0 : retValue = 0; // initialize
856 :
857 : T tmpValue;
858 :
859 0 : unsigned int i = 0;
860 0 : unsigned int opsi = 0;
861 0 : unsigned int blankNumberCount = 0;
862 : bool verified;
863 0 : for (const auto& number : numbers)
864 : {
865 0 : if (number.size() == 0)
866 : {
867 0 : ++blankNumberCount;
868 0 : continue; // skip empty numbers
869 : }
870 :
871 : // verify that this number looks like a number
872 : // for integer types, allow hex and binary
873 : // for all types allow base10
874 :
875 0 : verified = false;
876 :
877 0 : __COUTVS__(2, number);
878 :
879 : // check integer types
880 0 : if (typeid(unsigned int) == typeid(retValue) || typeid(int) == typeid(retValue) || typeid(unsigned long long) == typeid(retValue) ||
881 0 : typeid(long long) == typeid(retValue) || typeid(unsigned long) == typeid(retValue) || typeid(long) == typeid(retValue) ||
882 0 : typeid(unsigned short) == typeid(retValue) || typeid(short) == typeid(retValue) || typeid(uint8_t) == typeid(retValue))
883 : {
884 0 : if (number.find("0x") == 0) // indicates hex
885 : {
886 0 : __COUTS__(2) << "0x found" << __E__;
887 0 : for (unsigned int i = 2; i < number.size(); ++i)
888 : {
889 0 : if (!((number[i] >= '0' && number[i] <= '9') || (number[i] >= 'A' && number[i] <= 'F') || (number[i] >= 'a' && number[i] <= 'f')))
890 : {
891 0 : __COUT__ << "prob " << number[i] << __E__;
892 0 : return false;
893 : }
894 : }
895 0 : verified = true;
896 : }
897 0 : else if (number[0] == 'b') // indicates binary
898 : {
899 0 : __COUTS__(2) << "b found" << __E__;
900 :
901 0 : for (unsigned int i = 1; i < number.size(); ++i)
902 : {
903 0 : if (!((number[i] >= '0' && number[i] <= '1')))
904 : {
905 0 : __COUT__ << "prob " << number[i] << __E__;
906 0 : return false;
907 : }
908 : }
909 0 : verified = true;
910 : }
911 : }
912 :
913 : // if not verified above, for all types, check base10
914 0 : if (!verified)
915 0 : for (unsigned int i = 0; i < number.size(); ++i)
916 0 : if (!((number[i] >= '0' && number[i] <= '9') || number[i] == '.' || number[i] == '+' || number[i] == '-'))
917 0 : return false;
918 :
919 : // at this point, this number is confirmed to be a number of some sort
920 : // so convert to temporary number
921 0 : if (typeid(double) == typeid(retValue))
922 0 : tmpValue = strtod(number.c_str(), 0);
923 0 : else if (typeid(float) == typeid(retValue))
924 0 : tmpValue = strtof(number.c_str(), 0);
925 0 : else if (typeid(unsigned int) == typeid(retValue) || typeid(int) == typeid(retValue) || typeid(unsigned long long) == typeid(retValue) ||
926 0 : typeid(long long) == typeid(retValue) || typeid(unsigned long) == typeid(retValue) || typeid(long) == typeid(retValue) ||
927 0 : typeid(unsigned short) == typeid(retValue) || typeid(short) == typeid(retValue) || typeid(uint8_t) == typeid(retValue))
928 : {
929 0 : __COUTVS__(2, number);
930 0 : if (number.size() > 2 && number[1] == 'x') // assume hex value
931 0 : tmpValue = (T)strtol(number.c_str(), 0, 16);
932 0 : else if (number.size() > 1 && number[0] == 'b') // assume binary value
933 0 : tmpValue = (T)strtol(number.substr(1).c_str(), 0, 2); // skip first 'b' character
934 : else
935 0 : tmpValue = (T)strtol(number.c_str(), 0, 10);
936 : }
937 : else // just try!
938 : {
939 0 : __COUTVS__(2, number);
940 0 : if (number.size() > 2 && number[1] == 'x') // assume hex value
941 0 : tmpValue = (T)strtol(number.c_str(), 0, 16);
942 0 : else if (number.size() > 1 && number[0] == 'b') // assume binary value
943 0 : tmpValue = (T)strtol(number.substr(1).c_str(), 0, 2); // skip first 'b' character
944 : else
945 0 : tmpValue = (T)strtol(number.c_str(), 0, 10);
946 : // __SS__ << "Invalid type '" << StringMacros::demangleTypeName(typeid(retValue).name()) << "' requested for a numeric string. Data was '" << number
947 : // << "'" << __E__;
948 : // __SS_THROW__;
949 : }
950 :
951 0 : __COUTVS__(2, tmpValue);
952 :
953 : // apply operation
954 0 : if (i == 0) // first value, no operation, just take value
955 : {
956 0 : retValue = tmpValue;
957 :
958 0 : if (ops.size() == numbers.size() - blankNumberCount) // then there is a leading operation, so apply
959 : {
960 0 : if (ops[opsi] == '-') // only meaningful op is negative sign
961 0 : retValue *= -1;
962 0 : else if (ops[opsi] == '~') // handle bit invert
963 0 : retValue = ~tmpValue;
964 0 : __COUTT__ << "Op " << (ops.size() ? ops[opsi] : '_') << " intermediate value = " << std::dec << retValue << " 0x" << std::hex << retValue << __E__;
965 0 : opsi++; // jump to first internal op
966 : }
967 : }
968 : else // there is some sort of op
969 : {
970 : if (0 && i == 1) // display what we are dealing with
971 : {
972 : __COUTTV__(vectorToString(numbers));
973 : __COUTTV__(vectorToString(ops));
974 : }
975 0 : __COUTTV__(opsi);
976 0 : __COUTTV__(ops[opsi]);
977 0 : __COUTTV__(tmpValue);
978 0 : __COUTT__ << "Intermediate value = " << std::dec << retValue << " 0x" << std::hex << retValue << __E__;
979 :
980 0 : switch (ops[opsi])
981 : {
982 0 : case '+':
983 0 : retValue += tmpValue;
984 0 : break;
985 0 : case '-':
986 0 : retValue -= tmpValue;
987 0 : break;
988 0 : case '*':
989 0 : retValue *= tmpValue;
990 0 : break;
991 0 : case '/':
992 0 : retValue /= tmpValue;
993 0 : break;
994 0 : default:
995 0 : __SS__ << "Unrecognized operation '" << ops[opsi] << "' found!" << __E__ << "Numbers: " << vectorToString(numbers) << __E__
996 0 : << "Operations: " << vectorToString(ops) << __E__;
997 0 : __SS_THROW__;
998 0 : }
999 :
1000 0 : __COUTT__ << "Op " << (ops.size() ? ops[opsi] : '_') << " intermediate value = " << std::dec << retValue << " 0x" << std::hex << retValue << __E__;
1001 0 : ++opsi;
1002 : }
1003 0 : __COUTT__ << i << ": Op intermediate value = " << std::dec << retValue << " 0x" << std::hex << retValue << __E__;
1004 :
1005 0 : ++i; // increment index for next number/op
1006 :
1007 : } // end number loop
1008 :
1009 0 : return true; // number was valid and is passed by reference in retValue
1010 0 : } // end static getNumber<T>()
1011 :
1012 : //==============================================================================
1013 : // getVectorFromString (copied from ots::StringMacros)
1014 : // extracts the list of elements from string that uses a delimiter
1015 : // ignoring whitespace
1016 : // optionally returns the list of delimiters encountered, which may be useful
1017 : // for extracting which operator was used.
1018 : //
1019 : //
1020 : // Note: lists are returned as vectors
1021 : // Note: the size() of delimiters will be one less than the size() of the returned values
1022 : // unless there is a leading delimiter, in which case vectors will have the same
1023 : // size.
1024 0 : void getVectorFromString(const std::string& inputString,
1025 : std::vector<std::string>& listToReturn,
1026 : const std::set<char>& delimiter,
1027 : const std::set<char>& whitespace,
1028 : std::vector<char>* listOfDelimiters
1029 : /* dont care about URI in compiler ,
1030 : bool decodeURIComponents */
1031 : )
1032 : {
1033 0 : unsigned int i = 0;
1034 0 : unsigned int j = 0;
1035 0 : unsigned int c = 0;
1036 0 : std::set<char>::iterator delimeterSearchIt;
1037 0 : char lastDelimiter = 0;
1038 : bool isDelimiter;
1039 : // bool foundLeadingDelimiter = false;
1040 :
1041 : // __COUT__ << inputString << __E__;
1042 : // __COUTV__(inputString.length());
1043 :
1044 : // go through the full string extracting elements
1045 : // add each found element to set
1046 0 : for (; c < inputString.size(); ++c)
1047 : {
1048 : // __COUT__ << (char)inputString[c] << __E__;
1049 :
1050 0 : delimeterSearchIt = delimiter.find(inputString[c]);
1051 0 : isDelimiter = delimeterSearchIt != delimiter.end();
1052 :
1053 : // __COUT__ << (char)inputString[c] << " " << isDelimiter << __E__;
1054 :
1055 0 : if (whitespace.find(inputString[c]) != whitespace.end() // ignore leading white space
1056 0 : && i == j)
1057 : {
1058 0 : ++i;
1059 0 : ++j;
1060 : // if(isDelimiter)
1061 : // foundLeadingDelimiter = true;
1062 : }
1063 0 : else if (whitespace.find(inputString[c]) != whitespace.end() && i != j) // trailing white space, assume possible end of element
1064 : {
1065 : // do not change j or i
1066 : }
1067 0 : else if (isDelimiter) // delimiter is end of element
1068 : {
1069 : // __COUT__ << "Set element found: " <<
1070 : // inputString.substr(i,j-i) << " sz=" << inputString.substr(i,j-i).length() << std::endl;
1071 :
1072 0 : if (listOfDelimiters && listToReturn.size()) // || foundLeadingDelimiter))
1073 : // //accept leading delimiter
1074 : // (especially for case of
1075 : // leading negative in math
1076 : // parsing)
1077 : {
1078 : //__COUTV__(lastDelimiter);
1079 0 : listOfDelimiters->push_back(lastDelimiter);
1080 : }
1081 0 : listToReturn.push_back( // decodeURIComponents ? StringMacros::decodeURIComponent(inputString.substr(i, j - i)) :
1082 0 : inputString.substr(i, j - i));
1083 :
1084 : // setup i and j for next find
1085 0 : i = c + 1;
1086 0 : j = c + 1;
1087 : }
1088 : else // part of element, so move j, not i
1089 0 : j = c + 1;
1090 :
1091 0 : if (isDelimiter)
1092 0 : lastDelimiter = *delimeterSearchIt;
1093 : //__COUTV__(lastDelimiter);
1094 : }
1095 :
1096 : if (1) // i != j) //last element check (for case when no concluding ' ' or delimiter)
1097 : {
1098 : // __COUT__ << "Last element found: " <<
1099 : // inputString.substr(i,j-i) << std::endl;
1100 :
1101 0 : if (listOfDelimiters && listToReturn.size()) // || foundLeadingDelimiter))
1102 : // //accept leading delimiter
1103 : // (especially for case of leading
1104 : // negative in math parsing)
1105 : {
1106 : //__COUTV__(lastDelimiter);
1107 0 : listOfDelimiters->push_back(lastDelimiter);
1108 : }
1109 0 : listToReturn.push_back( // decodeURIComponents ? StringMacros::decodeURIComponent(inputString.substr(i, j - i)) :
1110 0 : inputString.substr(i, j - i));
1111 : }
1112 :
1113 : // assert that there is one less delimiter than values
1114 0 : if (listOfDelimiters && listToReturn.size() - 1 != listOfDelimiters->size() && listToReturn.size() != listOfDelimiters->size())
1115 : {
1116 0 : __SS__ << "There is a mismatch in delimiters to entries (should be equal or one "
1117 0 : "less delimiter): "
1118 0 : << listOfDelimiters->size() << " vs " << listToReturn.size() << __E__ << "Entries: " << vectorToString(listToReturn) << __E__
1119 0 : << "Delimiters: " << vectorToString(*listOfDelimiters) << __E__;
1120 0 : __SS_THROW__;
1121 0 : }
1122 :
1123 0 : } // end getVectorFromString()
1124 :
1125 : //==============================================================================
1126 : // static template function (copied from ots::StringMacros)
1127 : template<class T>
1128 0 : std::string vectorToString(const std::vector<T>& setToReturn, const std::string& delimeter /*= ", "*/)
1129 : {
1130 0 : std::stringstream ss;
1131 0 : bool first = true;
1132 0 : for (auto& setValue : setToReturn)
1133 : {
1134 0 : if (first)
1135 0 : first = false;
1136 : else
1137 0 : ss << delimeter;
1138 0 : ss << setValue;
1139 : }
1140 0 : return ss.str();
1141 0 : } // end vectorToString<T>()
|