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

312 строки
8.7 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 unescape_string(char* in) -> bool {
  28. if (!in)
  29. return true;
  30. char *out = in, *p = in;
  31. const char *int_err = nullptr;
  32. while (*p && !int_err) {
  33. if (*p != '\\') {
  34. /* normal case */
  35. *out++ = *p++;
  36. } else {
  37. /* escape sequence */
  38. switch (*++p) {
  39. case '0': *out++ = '\a'; ++p; break;
  40. case 'a': *out++ = '\a'; ++p; break;
  41. case 'b': *out++ = '\b'; ++p; break;
  42. case 'f': *out++ = '\f'; ++p; break;
  43. case 'n': *out++ = '\n'; ++p; break;
  44. case 'r': *out++ = '\r'; ++p; break;
  45. case 't': *out++ = '\t'; ++p; break;
  46. case 'v': *out++ = '\v'; ++p; break;
  47. case '"':
  48. case '\'':
  49. case '\\':
  50. *out++ = *p++;
  51. case '?':
  52. break;
  53. case 'x':
  54. case 'X':
  55. if (!isxdigit(p[1]) || !isxdigit(p[2])) {
  56. int_err = "Invalid character on hexadecimal escape.";
  57. } else {
  58. *out++ = (char)(get_nibble(p[1]) * 0x10 + get_nibble(p[2]));
  59. p += 3;
  60. }
  61. break;
  62. default:
  63. int_err = "Unexpected '\\' with no escape sequence.";
  64. break;
  65. }
  66. }
  67. }
  68. /* Set the end of string. */
  69. *out = '\0';
  70. if (int_err)
  71. return false;
  72. return true;
  73. }
  74. proc read_entire_file(char* filename) -> char* {
  75. char *fileContent = nullptr;
  76. FILE *fp = fopen(filename, "r");
  77. if (fp) {
  78. /* Go to the end of the file. */
  79. if (fseek(fp, 0L, SEEK_END) == 0) {
  80. /* Get the size of the file. */
  81. long bufsize = ftell(fp) + 1;
  82. if (bufsize == 0) {
  83. fputs("Empty file", stderr);
  84. goto closeFile;
  85. }
  86. /* Go back to the start of the file. */
  87. if (fseek(fp, 0L, SEEK_SET) != 0) {
  88. fputs("Error reading file", stderr);
  89. goto closeFile;
  90. }
  91. /* Allocate our buffer to that size. */
  92. fileContent = (char*)calloc(bufsize, sizeof(char));
  93. /* Read the entire file into memory. */
  94. size_t newLen = fread(fileContent, sizeof(char), bufsize, fp);
  95. fileContent[newLen] = '\0';
  96. if (ferror(fp) != 0) {
  97. fputs("Error reading file", stderr);
  98. }
  99. }
  100. closeFile:
  101. fclose(fp);
  102. } else {
  103. create_generic_error("The file '%s' could not be read.", filename);
  104. }
  105. return fileContent;
  106. /* Don't forget to call free() later! */
  107. }
  108. proc read_expression() -> char* {
  109. char* line = (char*)malloc(100);
  110. if(line == nullptr)
  111. return nullptr;
  112. char* linep = line;
  113. size_t lenmax = 100, len = lenmax;
  114. int c;
  115. int nesting = 0;
  116. while (true) {
  117. c = fgetc(stdin);
  118. if(c == EOF)
  119. break;
  120. if(--len == 0) {
  121. len = lenmax;
  122. char * linen = (char*)realloc(linep, lenmax *= 2);
  123. if(linen == nullptr) {
  124. free(linep);
  125. return nullptr;
  126. }
  127. line = linen + (line - linep);
  128. linep = linen;
  129. }
  130. *line = (char)c;
  131. if(*line == '(')
  132. ++nesting;
  133. else if(*line == ')')
  134. --nesting;
  135. else if(*line == '\n')
  136. if (nesting == 0)
  137. break;
  138. line++;
  139. }
  140. (*line)--; // we dont want the \n actually
  141. *line = '\0';
  142. return linep;
  143. }
  144. proc read_line() -> char* {
  145. char* line = (char*)malloc(100), * linep = line;
  146. size_t lenmax = 100, len = lenmax;
  147. int c;
  148. int nesting = 0;
  149. if(line == nullptr)
  150. return nullptr;
  151. for(;;) {
  152. c = fgetc(stdin);
  153. if(c == EOF)
  154. break;
  155. if(--len == 0) {
  156. len = lenmax;
  157. char* linen = (char*)realloc(linep, lenmax *= 2);
  158. if(linen == nullptr) {
  159. free(linep);
  160. return nullptr;
  161. }
  162. line = linen + (line - linep);
  163. linep = linen;
  164. }
  165. *line = (char)c;
  166. if(*line == '(')
  167. ++nesting;
  168. else if(*line == ')')
  169. --nesting;
  170. else if(*line == '\n')
  171. if (nesting == 0)
  172. break;
  173. line++;
  174. }
  175. (*line)--; // we dont want the \n actually
  176. *line = '\0';
  177. return linep;
  178. }
  179. proc log_message(Log_Level type, const char* message) -> void {
  180. if (type > Globals::log_level)
  181. return;
  182. const char* prefix;
  183. switch (type) {
  184. case Log_Level::Critical: prefix = "CRITICAL"; break;
  185. case Log_Level::Warning: prefix = "WARNING"; break;
  186. case Log_Level::Info: prefix = "INFO"; break;
  187. case Log_Level::Debug: prefix = "DEBUG"; break;
  188. default: return;
  189. }
  190. printf("%s: %s\n",prefix, message);
  191. }
  192. proc panic(char* message) -> void {
  193. log_message(Log_Level::Critical, message);
  194. exit(1);
  195. }
  196. proc print(Lisp_Object* node, bool print_quotes = false, FILE* file = stdout) -> void {
  197. switch (Memory::get_type(node)) {
  198. case (Lisp_Object_Type::Nil): fputs("()", file); break;
  199. case (Lisp_Object_Type::T): fputs("t", file); break;
  200. case (Lisp_Object_Type::Number): fprintf(file, "%f", node->value.number); break;
  201. case (Lisp_Object_Type::Keyword): fputs(":", file); // NOTE(Felix): intentionall fallthough
  202. case (Lisp_Object_Type::Symbol): fprintf(file, "%s", Memory::get_c_str(node->value.symbol.identifier)); break;
  203. case (Lisp_Object_Type::CFunction): fputs("[C-function]", file); break;
  204. case (Lisp_Object_Type::String): {
  205. if (print_quotes) {
  206. putc('\"', file);
  207. fputs(Memory::get_c_str(node->value.string), file);
  208. putc('\"', file);
  209. }
  210. else
  211. fputs(Memory::get_c_str(node->value.string), file);
  212. } break;
  213. case (Lisp_Object_Type::Function): {
  214. if (node->value.function.type == Function_Type::Lambda)
  215. fputs("[lambda]", file);
  216. else if (node->value.function.type == Function_Type::Special_Lambda)
  217. fputs("[special-lambda]", file);
  218. else if (node->value.function.type == Function_Type::Macro)
  219. fputs("[macro]", file);
  220. else
  221. assert(false);
  222. } break;
  223. case (Lisp_Object_Type::Pair): {
  224. Lisp_Object* head = node;
  225. putc('(', file);
  226. // NOTE(Felix): We cold do a while true here, however in case
  227. // we want to print a broken list (for logging the error) we
  228. // should do mo checks.
  229. while (head) {
  230. print(head->value.pair.first, print_quotes, file);
  231. head = head->value.pair.rest;
  232. if (!head)
  233. return;
  234. if (Memory::get_type(head) != Lisp_Object_Type::Pair)
  235. break;
  236. putc(' ', file);
  237. }
  238. if (Memory::get_type(head) != Lisp_Object_Type::Nil) {
  239. fputs(" . ", file);
  240. print(head);
  241. }
  242. putc(')', file);
  243. } break;
  244. }
  245. }
  246. proc print_error_location() -> void {
  247. if (Globals::current_source_code) {
  248. printf("%s (line %d, position %d) code:" console_red "\n ",
  249. Memory::get_c_str(
  250. Globals::current_source_code->sourceCodeLocation->file),
  251. Globals::current_source_code->sourceCodeLocation->line,
  252. Globals::current_source_code->sourceCodeLocation->column);
  253. print(Globals::current_source_code);
  254. } else {
  255. fputs("no source code location avaliable", stdout);
  256. }
  257. }
  258. proc log_error() -> void {
  259. fputs(console_red, stdout);
  260. fputs(Memory::get_c_str(Globals::error->message), stdout);
  261. puts(console_normal);
  262. fputs(" in: " console_cyan, stdout);
  263. print_error_location();
  264. puts(console_normal);
  265. }