$search
00001 // Aseba 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 // C++ 00011 #include <string> 00012 #include <iostream> 00013 #include <locale> 00014 #include <fstream> 00015 #include <sstream> 00016 #include <valarray> 00017 00018 // C 00019 #include <getopt.h> // getopt_long() 00020 #include <stdlib.h> // exit() 00021 00022 // helper function 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 // create VM 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 // fill description accordingly 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 /*d.namedVariables.push_back(TargetDescription::NamedVariable("id", 1)); 00184 d.namedVariables.push_back(TargetDescription::NamedVariable("source", 1)); 00185 d.namedVariables.push_back(TargetDescription::NamedVariable("args", 32));*/ 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 // run VM 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 // errors 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 // this was expected 00255 std::cerr << "Failure was expected" << std::endl; 00256 exit(EXIT_SUCCESS); 00257 } 00258 else 00259 { 00260 // oops 00261 exit(EXIT_FAILURE); 00262 } 00263 } 00264 else 00265 { 00266 // no errors 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 // parse the arguments 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: // getopt_long() flag 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 // check for the source file 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 // read source 00333 const std::wstring wSource = read_source(filename); 00334 00335 // dump source 00336 if (source) 00337 dump_source(wSource); 00338 00339 // parse source 00340 std::wistringstream ifs(wSource); 00341 00342 Compiler compiler; 00343 00344 // fake target description 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 // compile 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 //ifs.close(); 00364 00365 checkForError("Compilation", should_fail, (outError.message != L"not defined"), outError.toWString()); 00366 00367 // run 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 // read source code to a string 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 /*for (size_t i = 0; i < utf8Source.length(); ++i) 00451 std::cerr << "source char " << i << " is 0x" << std::hex << (unsigned)(unsigned char)utf8Source[i] << " (" << char(utf8Source[i]) << ")" << std::endl; 00452 */ 00453 const std::wstring s = UTF8ToWString(utf8Source); 00454 00455 /*std::cerr << "len utf8 " << utf8Source.length() << " len final " << s.length() << std::endl; 00456 for (size_t i = 0; i < s.length(); ++i) 00457 std::wcerr << "dest char " << i << " is 0x" << std::hex << (unsigned)s[i] << " (" << s[i] << ")"<< std::endl; 00458 */ 00459 return s; 00460 } 00461 00462 // dump program source 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