00001
00002 #include "../compiler/compiler.h"
00003 #include "../vm/vm.h"
00004 #include "../vm/natives.h"
00005 #include "../common/consts.h"
00006 #include "../utils/utils.h"
00007 #include "../utils/FormatableString.h"
00008 using namespace Aseba;
00009
00010
00011 #include <string>
00012 #include <iostream>
00013 #include <locale>
00014 #include <fstream>
00015 #include <sstream>
00016 #include <valarray>
00017
00018
00019 #include <getopt.h>
00020 #include <stdlib.h>
00021
00022
00023 std::wstring read_source(const std::string& filename);
00024 void dump_source(const std::wstring& source);
00025
00026 static const char short_options [] = "fsdm:";
00027 static const struct option long_options[] = {
00028 { "fail", no_argument, NULL, 'f'},
00029 { "source", no_argument, NULL, 's'},
00030 { "dump", no_argument, NULL, 'd'},
00031 { "memdump", no_argument, NULL, 'u'},
00032 { "memcmp", required_argument, NULL, 'm'},
00033 { 0, 0, 0, 0 }
00034 };
00035
00036 static void usage (int argc, char** argv)
00037 {
00038 std::cerr << "Usage: " << argv[0] << " [options] source" << std::endl << std::endl
00039 << "Options:" << std::endl
00040 << " -f | --fail Return EXIT_SUCCESS if compilation fail" << std::endl
00041 << " -s | --source Dump the source code" << std::endl
00042 << " -d | --dump Dump the compilation result (tokens, tree, bytecode)" << std::endl
00043 << " -u | --memdump Dump the memory content at the end of the execution" << std::endl
00044 << " -m | --memcmp file Compare result of the VM execution with file" << std::endl;
00045 }
00046
00047 static bool executionError(false);
00048
00049 extern "C" void AsebaSendMessage(AsebaVMState *vm, uint16 type, const void *data, uint16 size)
00050 {
00051 switch (type)
00052 {
00053 case ASEBA_MESSAGE_DIVISION_BY_ZERO:
00054 std::cerr << "Division by zero" << std::endl;
00055 executionError = true;
00056 break;
00057
00058 case ASEBA_MESSAGE_ARRAY_ACCESS_OUT_OF_BOUNDS:
00059 std::cerr << "Array access out of bounds" << std::endl;
00060 executionError = true;
00061 break;
00062
00063 default:
00064 std::cerr << "AsebaSendMessage of type " << type << ", size " << size << std::endl;
00065 break;
00066 }
00067 }
00068
00069 #ifdef __BIG_ENDIAN__
00070 extern "C" void AsebaSendMessageWords(AsebaVMState *vm, uint16 type, const uint16* data, uint16 count)
00071 {
00072 AsebaSendMessage(vm, type, data, count*2);
00073 }
00074 #endif
00075
00076 extern "C" void AsebaSendVariables(AsebaVMState *vm, uint16 start, uint16 length)
00077 {
00078 std::cerr << "AsebaSendVariables at pos " << start << ", length " << length << std::endl;
00079 }
00080
00081 extern "C" void AsebaSendDescription(AsebaVMState *vm)
00082 {
00083 std::cerr << "AsebaSendDescription" << std::endl;
00084 }
00085
00086 extern "C" void AsebaPutVmToSleep(AsebaVMState *vm)
00087 {
00088 std::cerr << "AsebaPutVmToSleep" << std::endl;
00089 }
00090
00091 static AsebaNativeFunctionPointer nativeFunctions[] =
00092 {
00093 ASEBA_NATIVES_STD_FUNCTIONS,
00094 };
00095
00096 static const AsebaNativeFunctionDescription* nativeFunctionsDescriptions[] =
00097 {
00098 ASEBA_NATIVES_STD_DESCRIPTIONS,
00099 0
00100 };
00101
00102 extern "C" const AsebaNativeFunctionDescription * const * AsebaGetNativeFunctionsDescriptions(AsebaVMState *vm)
00103 {
00104 return nativeFunctionsDescriptions;
00105 }
00106
00107 extern "C" void AsebaNativeFunction(AsebaVMState *vm, uint16 id)
00108 {
00109 nativeFunctions[id](vm);
00110 }
00111
00112 extern "C" void AsebaWriteBytecode(AsebaVMState *vm)
00113 {
00114 std::cerr << "AsebaWriteBytecode" << std::endl;
00115 }
00116
00117 extern "C" void AsebaResetIntoBootloader(AsebaVMState *vm)
00118 {
00119 std::cerr << "AsebaResetIntoBootloader" << std::endl;
00120 }
00121
00122 extern "C" void AsebaAssert(AsebaVMState *vm, AsebaAssertReason reason)
00123 {
00124 std::cerr << "\nFatal error, internal VM exception: ";
00125 switch (reason)
00126 {
00127 case ASEBA_ASSERT_UNKNOWN: std::cerr << "undefined"; break;
00128 case ASEBA_ASSERT_UNKNOWN_UNARY_OPERATOR: std::cerr << "unknown unary operator"; break;
00129 case ASEBA_ASSERT_UNKNOWN_BINARY_OPERATOR: std::cerr << "unknown binary operator"; break;
00130 case ASEBA_ASSERT_UNKNOWN_BYTECODE: std::cerr << "unknown bytecode"; break;
00131 case ASEBA_ASSERT_STACK_OVERFLOW: std::cerr << "stack overflow"; break;
00132 case ASEBA_ASSERT_STACK_UNDERFLOW: std::cerr << "stack underflow"; break;
00133 case ASEBA_ASSERT_OUT_OF_VARIABLES_BOUNDS: std::cerr << "out of variables bounds"; break;
00134 case ASEBA_ASSERT_OUT_OF_BYTECODE_BOUNDS: std::cerr << "out of bytecode bounds"; break;
00135 case ASEBA_ASSERT_STEP_OUT_OF_RUN: std::cerr << "step out of run"; break;
00136 case ASEBA_ASSERT_BREAKPOINT_OUT_OF_BYTECODE_BOUNDS: std::cerr << "breakpoint out of bytecode bounds"; break;
00137 case ASEBA_ASSERT_EMIT_BUFFER_TOO_LONG: std::cerr << "tried to emit a buffer too long"; break;
00138 default: std::cerr << "unknown exception"; break;
00139 }
00140 std::cerr << ".\npc = " << vm->pc << ", sp = " << vm->sp;
00141 std::cerr << "\nResetting VM" << std::endl;
00142 executionError = true;
00143 AsebaVMInit(vm);
00144 }
00145
00146 struct AsebaNode
00147 {
00148 AsebaVMState vm;
00149 std::valarray<unsigned short> bytecode;
00150 std::valarray<signed short> stack;
00151 TargetDescription d;
00152
00153 struct Variables
00154 {
00155 sint16 user[256];
00156 } variables;
00157
00158 AsebaNode()
00159 {
00160
00161 vm.nodeId = 0;
00162 bytecode.resize(512);
00163 vm.bytecode = &bytecode[0];
00164 vm.bytecodeSize = bytecode.size();
00165
00166 stack.resize(64);
00167 vm.stack = &stack[0];
00168 vm.stackSize = stack.size();
00169
00170 vm.variables = reinterpret_cast<sint16 *>(&variables);
00171 vm.variablesSize = sizeof(variables) / sizeof(sint16);
00172
00173 AsebaVMInit(&vm);
00174
00175
00176 d.name = L"testvm";
00177 d.protocolVersion = ASEBA_PROTOCOL_VERSION;
00178
00179 d.bytecodeSize = vm.bytecodeSize;
00180 d.variablesSize = vm.variablesSize;
00181 d.stackSize = vm.stackSize;
00182
00183
00184
00185
00186
00187 const AsebaNativeFunctionDescription** nativeDescs(nativeFunctionsDescriptions);
00188 while (*nativeDescs)
00189 {
00190 const AsebaNativeFunctionDescription* nativeDesc(*nativeDescs);
00191 std::string name(nativeDesc->name);
00192 std::string doc(nativeDesc->doc);
00193
00194 TargetDescription::NativeFunction native(
00195 std::wstring(name.begin(), name.end()),
00196 std::wstring(doc.begin(), doc.end())
00197 );
00198
00199 const AsebaNativeFunctionArgumentDescription* params(nativeDesc->arguments);
00200 while (params->size)
00201 {
00202 AsebaNativeFunctionArgumentDescription param(*params);
00203 name = param.name;
00204 int size = param.size;
00205 native.parameters.push_back(
00206 TargetDescription::NativeFunctionParameter(std::wstring(name.begin(), name.end()), size)
00207 );
00208 ++params;
00209 }
00210
00211 d.nativeFunctions.push_back(native);
00212
00213 ++nativeDescs;
00214 }
00215 }
00216
00217 const TargetDescription* getTargetDescription() const
00218 {
00219 return &d;
00220 }
00221
00222 bool loadBytecode(const BytecodeVector& bytecode)
00223 {
00224 size_t i = 0;
00225 for (BytecodeVector::const_iterator it(bytecode.begin()); it != bytecode.end(); ++it)
00226 {
00227 if (i == vm.bytecodeSize)
00228 return false;
00229 const BytecodeElement& be(*it);
00230 vm.bytecode[i++] = be.bytecode;
00231 }
00232 return true;
00233 }
00234
00235 void run()
00236 {
00237
00238 AsebaVMSetupEvent(&vm, ASEBA_EVENT_INIT);
00239 AsebaVMRun(&vm, 1000);
00240 }
00241 };
00242
00243 void checkForError(const std::string& module, bool shouldFail, bool wasError, const std::wstring& errorMessage = L"")
00244 {
00245 if (wasError)
00246 {
00247
00248 std::cerr << "An error in " << module << " occured";
00249 if (!errorMessage.empty())
00250 std::wcerr << ':' << std::endl << errorMessage;
00251 std::cerr << std::endl;
00252 if (shouldFail)
00253 {
00254
00255 std::cerr << "Failure was expected" << std::endl;
00256 exit(EXIT_SUCCESS);
00257 }
00258 else
00259 {
00260
00261 exit(EXIT_FAILURE);
00262 }
00263 }
00264 else
00265 {
00266
00267 std::cerr << module << " was successful" << std::endl;
00268 }
00269 }
00270
00271 int main(int argc, char** argv)
00272 {
00273 bool should_fail = false;
00274 bool source = false;
00275 bool dump = false;
00276 bool memDump = false;
00277 bool memCmp = false;
00278 std::string memCmpFileName;
00279
00280 std::locale::global(std::locale(""));
00281
00282
00283 for(;;)
00284 {
00285 int index;
00286 int c;
00287
00288 c = getopt_long(argc, argv, short_options, long_options, &index);
00289
00290 if (c==-1)
00291 break;
00292
00293 switch(c)
00294 {
00295 case 0:
00296 break;
00297 case 'f':
00298 should_fail = true;
00299 break;
00300 case 's':
00301 source = true;
00302 break;
00303 case 'd':
00304 dump = true;
00305 break;
00306 case 'u':
00307 memDump = true;
00308 break;
00309 case 'm':
00310 memCmp = true;
00311 memCmpFileName = optarg;
00312 break;
00313 default:
00314 usage(argc, argv);
00315 exit(EXIT_FAILURE);
00316 }
00317 }
00318
00319 std::string filename;
00320
00321
00322 if (optind == (argc-1))
00323 {
00324 filename.assign(argv[optind]);
00325 }
00326 else
00327 {
00328 usage(argc, argv);
00329 exit(EXIT_FAILURE);
00330 }
00331
00332
00333 const std::wstring wSource = read_source(filename);
00334
00335
00336 if (source)
00337 dump_source(wSource);
00338
00339
00340 std::wistringstream ifs(wSource);
00341
00342 Compiler compiler;
00343
00344
00345 AsebaNode node;
00346 CommonDefinitions definitions;
00347 definitions.events.push_back(NamedValue(L"event1", 0));
00348 definitions.events.push_back(NamedValue(L"event2", 3));
00349 definitions.constants.push_back(NamedValue(L"FOO", 2));
00350
00351 BytecodeVector bytecode;
00352 unsigned int varCount;
00353 Error outError;
00354
00355
00356 compiler.setTargetDescription(node.getTargetDescription());
00357 compiler.setCommonDefinitions(&definitions);
00358 if (dump)
00359 compiler.compile(ifs, bytecode, varCount, outError, &(std::wcout));
00360 else
00361 compiler.compile(ifs, bytecode, varCount, outError, NULL);
00362
00363
00364
00365 checkForError("Compilation", should_fail, (outError.message != L"not defined"), outError.toWString());
00366
00367
00368 if (!node.loadBytecode(bytecode))
00369 {
00370 std::cerr << "Load bytecode failure" << std::endl;
00371 return EXIT_FAILURE;
00372 }
00373 node.run();
00374
00375 checkForError("Execution", should_fail, executionError);
00376
00377 if (memDump)
00378 {
00379 std::wcout << L"Memory dump:" << std::endl;
00380 for (int i = 0; i < node.vm.variablesSize; i++)
00381 {
00382 std::wcout << node.vm.variables[i] << std::endl;
00383 }
00384 }
00385
00386 if (memCmp)
00387 {
00388 std::ifstream ifs;
00389 ifs.open(memCmpFileName.data(), std::ifstream::in);
00390 if (!ifs.is_open())
00391 {
00392 std::cerr << "Error opening mem dump file " << memCmpFileName << std::endl;
00393 exit(EXIT_FAILURE);
00394 }
00395 size_t i = 0;
00396 while (!ifs.eof())
00397 {
00398 int v;
00399 ifs >> v;
00400 if (ifs.eof())
00401 break;
00402 if (i >= node.vm.variablesSize)
00403 break;
00404 if (node.vm.variables[i] != v)
00405 {
00406 std::cerr << "VM variable value at pos " << i << " after execution differs from dump; expected: " << v << ", found: " << node.vm.variables[i] << std::endl;
00407 if (should_fail)
00408 {
00409 std::cerr << "Failure was expected" << std::endl;
00410 exit(EXIT_SUCCESS);
00411 }
00412 else
00413 exit(EXIT_FAILURE);
00414 }
00415 ++i;
00416 }
00417 ifs.close();
00418 }
00419
00420
00421 if (should_fail)
00422 {
00423 std::cerr << "All tests passed successfully, but failure was expected" << std::endl;
00424 exit(EXIT_FAILURE);
00425 }
00426
00427 return EXIT_SUCCESS;
00428 }
00429
00430
00431 std::wstring read_source(const std::string& filename)
00432 {
00433 std::ifstream ifs;
00434 ifs.open( filename.c_str(),std::ifstream::binary);
00435 if (!ifs.is_open())
00436 {
00437 std::cerr << "Error opening source file " << filename << std::endl;
00438 exit(EXIT_FAILURE);
00439 }
00440
00441 ifs.seekg (0, std::ios::end);
00442 std::streampos length = ifs.tellg();
00443 ifs.seekg (0, std::ios::beg);
00444
00445 std::string utf8Source;
00446 utf8Source.resize(length);
00447 ifs.read(&utf8Source[0], length);
00448 ifs.close();
00449
00450
00451
00452
00453 const std::wstring s = UTF8ToWString(utf8Source);
00454
00455
00456
00457
00458
00459 return s;
00460 }
00461
00462
00463 void dump_source(const std::wstring& source)
00464 {
00465 std::wistringstream is(source);
00466 wchar_t c;
00467 std::cout << "Program:" << std::endl;
00468 int line = 1;
00469 bool header = true;
00470 while (is)
00471 {
00472 if (header)
00473 {
00474 std::cout << line << " ";
00475 header = false;
00476 }
00477 c = is.get();
00478 std::wcout << c;
00479 if (c == '\n')
00480 {
00481 header = true;
00482 line++;
00483 }
00484 }
00485 std::cout << std::endl;
00486 }
00487