Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 
 
 

491 строка
14 KiB

  1. proc string_equal(const char input[], const char check[]) -> bool {
  2. if (input == check) return true;
  3. for(int i = 0; input[i] == check[i]; i++) {
  4. if (input[i] == '\0')
  5. return true;
  6. }
  7. return false;
  8. }
  9. proc string_equal(String* str, const char check[]) -> bool {
  10. return string_equal(Memory::get_c_str(str), check);
  11. }
  12. proc string_equal(const char check[], String* str) -> bool {
  13. return string_equal(Memory::get_c_str(str), check);
  14. }
  15. proc string_equal(String* str1, String* str2) -> bool {
  16. if (str1 == str2)
  17. return true;
  18. return string_equal(Memory::get_c_str(str1), Memory::get_c_str(str2));
  19. }
  20. proc get_nibble(char c) -> char {
  21. if (c >= 'A' && c <= 'F')
  22. return (c - 'A') + 10;
  23. else if (c >= 'a' && c <= 'f')
  24. return (c - 'a') + 10;
  25. return (c - '0');
  26. }
  27. proc escape_string(char* in) -> char* {
  28. // TODO(Felix): add more escape sequences
  29. int i = 0, count = 0;
  30. while (in[i] != '\0') {
  31. switch (in[i]) {
  32. case '\\':
  33. case '\n':
  34. case '\t':
  35. ++count;
  36. default: break;
  37. }
  38. ++i;
  39. }
  40. char* ret = (char*)malloc((i+count+1)*sizeof(char));
  41. // copy in
  42. i = 0;
  43. int j = 0;
  44. while (in[i] != '\0') {
  45. switch (in[i]) {
  46. case '\\': ret[j++] = '\\'; ret[j++] = '\\'; break;
  47. case '\n': ret[j++] = '\\'; ret[j++] = 'n'; break;
  48. case '\t': ret[j++] = '\\'; ret[j++] = 't'; break;
  49. default: ret[j++] = in[i];
  50. }
  51. ++i;
  52. }
  53. ret[j++] = '\0';
  54. return ret;
  55. }
  56. proc unescape_string(char* in) -> bool {
  57. if (!in)
  58. return true;
  59. char *out = in, *p = in;
  60. const char *int_err = nullptr;
  61. while (*p && !int_err) {
  62. if (*p != '\\') {
  63. /* normal case */
  64. *out++ = *p++;
  65. } else {
  66. /* escape sequence */
  67. switch (*++p) {
  68. case '0': *out++ = '\a'; ++p; break;
  69. case 'a': *out++ = '\a'; ++p; break;
  70. case 'b': *out++ = '\b'; ++p; break;
  71. case 'f': *out++ = '\f'; ++p; break;
  72. case 'n': *out++ = '\n'; ++p; break;
  73. case 'r': *out++ = '\r'; ++p; break;
  74. case 't': *out++ = '\t'; ++p; break;
  75. case 'v': *out++ = '\v'; ++p; break;
  76. case '"':
  77. case '\'':
  78. case '\\':
  79. *out++ = *p++;
  80. case '?':
  81. break;
  82. case 'x':
  83. case 'X':
  84. if (!isxdigit(p[1]) || !isxdigit(p[2])) {
  85. create_parsing_error(
  86. "The string '%s' at %s:%d:%d could not be unescaped. "
  87. "(Invalid character on hexadecimal escape at char %d)",
  88. in, Parser::parser_file, Parser::parser_line, Parser::parser_col,
  89. (p+1)-in);
  90. } else {
  91. *out++ = (char)(get_nibble(p[1]) * 0x10 + get_nibble(p[2]));
  92. p += 3;
  93. }
  94. break;
  95. default:
  96. create_parsing_error(
  97. "The string '%s' at %s:%d:%d could not be unescaped. "
  98. "(Unexpected '\\' with no escape sequence at char %d)",
  99. in, Parser::parser_file, Parser::parser_line, Parser::parser_col,
  100. (p+1)-in);
  101. }
  102. }
  103. }
  104. /* Set the end of string. */
  105. *out = '\0';
  106. if (int_err)
  107. return false;
  108. return true;
  109. }
  110. proc read_entire_file(char* filename) -> char* {
  111. char *fileContent = nullptr;
  112. FILE *fp = fopen(filename, "r");
  113. if (fp) {
  114. /* Go to the end of the file. */
  115. if (fseek(fp, 0L, SEEK_END) == 0) {
  116. /* Get the size of the file. */
  117. long bufsize = ftell(fp) + 1;
  118. if (bufsize == 0) {
  119. fputs("Empty file", stderr);
  120. goto closeFile;
  121. }
  122. /* Go back to the start of the file. */
  123. if (fseek(fp, 0L, SEEK_SET) != 0) {
  124. fputs("Error reading file", stderr);
  125. goto closeFile;
  126. }
  127. /* Allocate our buffer to that size. */
  128. fileContent = (char*)calloc(bufsize, sizeof(char));
  129. /* Read the entire file into memory. */
  130. size_t newLen = fread(fileContent, sizeof(char), bufsize, fp);
  131. fileContent[newLen] = '\0';
  132. if (ferror(fp) != 0) {
  133. fputs("Error reading file", stderr);
  134. }
  135. }
  136. closeFile:
  137. fclose(fp);
  138. }
  139. return fileContent;
  140. /* Don't forget to call free() later! */
  141. }
  142. proc read_expression() -> char* {
  143. char* line = (char*)malloc(100);
  144. if(line == nullptr)
  145. return nullptr;
  146. char* linep = line;
  147. size_t lenmax = 100, len = lenmax;
  148. int c;
  149. int nesting = 0;
  150. while (true) {
  151. c = fgetc(stdin);
  152. if(c == EOF)
  153. break;
  154. if(--len == 0) {
  155. len = lenmax;
  156. char * linen = (char*)realloc(linep, lenmax *= 2);
  157. if(linen == nullptr) {
  158. free(linep);
  159. return nullptr;
  160. }
  161. line = linen + (line - linep);
  162. linep = linen;
  163. }
  164. *line = (char)c;
  165. if(*line == '(')
  166. ++nesting;
  167. else if(*line == ')')
  168. --nesting;
  169. else if(*line == '\n')
  170. if (nesting == 0)
  171. break;
  172. line++;
  173. }
  174. (*line)--; // we dont want the \n actually
  175. *line = '\0';
  176. return linep;
  177. }
  178. proc read_line() -> char* {
  179. char* line = (char*)malloc(100), * linep = line;
  180. size_t lenmax = 100, len = lenmax;
  181. int c;
  182. int nesting = 0;
  183. if(line == nullptr)
  184. return nullptr;
  185. for(;;) {
  186. c = fgetc(stdin);
  187. if(c == EOF)
  188. break;
  189. if(--len == 0) {
  190. len = lenmax;
  191. char* linen = (char*)realloc(linep, lenmax *= 2);
  192. if(linen == nullptr) {
  193. free(linep);
  194. return nullptr;
  195. }
  196. line = linen + (line - linep);
  197. linep = linen;
  198. }
  199. *line = (char)c;
  200. if(*line == '(')
  201. ++nesting;
  202. else if(*line == ')')
  203. --nesting;
  204. else if(*line == '\n')
  205. if (nesting == 0)
  206. break;
  207. line++;
  208. }
  209. (*line)--; // we dont want the \n actually
  210. *line = '\0';
  211. return linep;
  212. }
  213. proc log_message(Log_Level type, const char* message) -> void {
  214. if (type > Globals::log_level)
  215. return;
  216. const char* prefix;
  217. switch (type) {
  218. case Log_Level::Critical: prefix = "CRITICAL"; break;
  219. case Log_Level::Warning: prefix = "WARNING"; break;
  220. case Log_Level::Info: prefix = "INFO"; break;
  221. case Log_Level::Debug: prefix = "DEBUG"; break;
  222. default: return;
  223. }
  224. printf("%s: %s\n",prefix, message);
  225. }
  226. proc panic(char* message) -> void {
  227. log_message(Log_Level::Critical, message);
  228. exit(1);
  229. }
  230. proc print(Lisp_Object* node, bool print_repr = false, FILE* file = stdout) -> void {
  231. switch (Memory::get_type(node)) {
  232. case (Lisp_Object_Type::Nil): fputs("()", file); break;
  233. case (Lisp_Object_Type::T): fputs("t", file); break;
  234. case (Lisp_Object_Type::Number): {
  235. if (abs(node->value.number - (int)node->value.number) < 0.000001f)
  236. fprintf(file, "%d", (int)node->value.number);
  237. else
  238. fprintf(file, "%f", node->value.number);
  239. } break;
  240. case (Lisp_Object_Type::Keyword): fputs(":", file); // NOTE(Felix): intentionall fallthough
  241. case (Lisp_Object_Type::Symbol): fprintf(file, "%s", Memory::get_c_str(node->value.symbol.identifier)); break;
  242. case (Lisp_Object_Type::CFunction): fputs("[C-function]", file); break;
  243. case (Lisp_Object_Type::String): {
  244. if (print_repr) {
  245. putc('\"', file);
  246. char* escaped = escape_string(Memory::get_c_str(node->value.string));
  247. fputs(escaped, file);
  248. putc('\"', file);
  249. free(escaped);
  250. }
  251. else
  252. fputs(Memory::get_c_str(node->value.string), file);
  253. } break;
  254. case (Lisp_Object_Type::Function): {
  255. if (node->userType) {
  256. fprintf(file, "[%s]", Memory::get_c_str(node->userType->value.symbol.identifier));
  257. break;
  258. }
  259. if (node->value.function.type == Function_Type::Lambda)
  260. fputs("[lambda]", file);
  261. else if (node->value.function.type == Function_Type::Special_Lambda)
  262. fputs("[special-lambda]", file);
  263. else if (node->value.function.type == Function_Type::Macro)
  264. fputs("[macro]", file);
  265. else
  266. assert(false);
  267. } break;
  268. case (Lisp_Object_Type::Pair): {
  269. Lisp_Object* head = node;
  270. // first check if it is a quotation form, in that case we want
  271. // to print it prettier
  272. if (Memory::get_type(head->value.pair.first) == Lisp_Object_Type::Symbol) {
  273. String* identifier = head->value.pair.first->value.symbol.identifier;
  274. // TODO(Felix): Lisp_Node* symbol = head->value.pair.first;
  275. // TODO(Felix): if (symbol == Memory::get_or_create_symbol("quote"))
  276. if (string_equal(identifier, "quote") ||
  277. string_equal(identifier, "unquote"))
  278. {
  279. putc((string_equal(identifier, "quote"))
  280. ? '\''
  281. : ',', file);
  282. assert_type(head->value.pair.rest, Lisp_Object_Type::Pair);
  283. assert(head->value.pair.rest->value.pair.rest == Memory::nil);
  284. print(head->value.pair.rest->value.pair.first, print_repr, file);
  285. break;
  286. }
  287. else if (string_equal(identifier, "quasiquote")) {
  288. putc('`', file);
  289. assert_type(head->value.pair.rest, Lisp_Object_Type::Pair);
  290. print(head->value.pair.rest->value.pair.first, print_repr, file);
  291. break;
  292. }
  293. }
  294. putc('(', file);
  295. // NOTE(Felix): We cold do a while true here, however in case
  296. // we want to print a broken list (for logging the error) we
  297. // should do mo checks.
  298. while (head) {
  299. print(head->value.pair.first, print_repr, file);
  300. head = head->value.pair.rest;
  301. if (!head)
  302. return;
  303. if (Memory::get_type(head) != Lisp_Object_Type::Pair)
  304. break;
  305. putc(' ', file);
  306. }
  307. if (Memory::get_type(head) != Lisp_Object_Type::Nil) {
  308. fputs(" . ", file);
  309. print(head);
  310. }
  311. putc(')', file);
  312. } break;
  313. }
  314. }
  315. proc print_error_location() -> void {
  316. if (Globals::current_source_code) {
  317. printf("%s (line %d, position %d) code:" console_red "\n ",
  318. Memory::get_c_str(
  319. Globals::current_source_code->sourceCodeLocation->file),
  320. Globals::current_source_code->sourceCodeLocation->line,
  321. Globals::current_source_code->sourceCodeLocation->column);
  322. print(Globals::current_source_code);
  323. } else {
  324. fputs("no source code location avaliable", stdout);
  325. }
  326. }
  327. proc log_error() -> void {
  328. fputs(console_red, stdout);
  329. fputs(Memory::get_c_str(Globals::error->message), stdout);
  330. puts(console_normal);
  331. fputs(" in: " console_cyan, stdout);
  332. print_error_location();
  333. puts(console_normal);
  334. }
  335. proc exe_dir() -> char* {
  336. if_linux {
  337. size_t size = 512, i, n;
  338. char *path, *temp;
  339. while (1) {
  340. size_t used;
  341. path = (char*)malloc(size);
  342. if (!path) {
  343. errno = ENOMEM;
  344. return NULL;
  345. }
  346. used = readlink("/proc/self/exe", path, size);
  347. if (used == -1) {
  348. const int saved_errno = errno;
  349. free(path);
  350. errno = saved_errno;
  351. return NULL;
  352. } else
  353. if (used < 1) {
  354. free(path);
  355. errno = EIO;
  356. return NULL;
  357. }
  358. if ((size_t)used >= size) {
  359. free(path);
  360. size = (size | 2047) + 2049;
  361. continue;
  362. }
  363. size = (size_t)used;
  364. break;
  365. }
  366. /* Find final slash. */
  367. n = 0;
  368. for (i = 0; i < size; i++)
  369. if (path[i] == '/')
  370. n = i;
  371. /* Optimize allocated size,
  372. ensuring there is room for
  373. a final slash and a
  374. string-terminating '\0', */
  375. temp = path;
  376. path = (char*)realloc(temp, n + 2);
  377. if (!path) {
  378. free(temp);
  379. errno = ENOMEM;
  380. return NULL;
  381. }
  382. /* and properly trim and terminate the path string. */
  383. path[n+0] = '/';
  384. path[n+1] = '\0';
  385. return path;
  386. }
  387. if_windows {
  388. DWORD last_error;
  389. DWORD result;
  390. DWORD path_size = 1024;
  391. char* path = (char*)malloc(1024);
  392. while (true) {
  393. memset(path, 0, path_size);
  394. result = GetModuleFileName(0, path, path_size - 1);
  395. last_error = GetLastError();
  396. if (0 == result) {
  397. free(path);
  398. path = 0;
  399. break;
  400. }
  401. else if (result == path_size - 1) {
  402. free(path);
  403. /* May need to also check for ERROR_SUCCESS here if XP/2K */
  404. if (ERROR_INSUFFICIENT_BUFFER != last_error) {
  405. path = 0;
  406. break;
  407. }
  408. path_size = path_size * 2;
  409. path = (char*)malloc(path_size);
  410. }
  411. else
  412. break;
  413. }
  414. if (!path) {
  415. fprintf(stderr, "Failure: %d\n", last_error);
  416. return "";
  417. }
  418. else
  419. return path;
  420. }
  421. }