You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

623 lines
23 KiB

  1. namespace Parser {
  2. String* standard_in;
  3. String* parser_file;
  4. int parser_line;
  5. int parser_col;
  6. proc maybe_expand_reader_macro() -> Lisp_Object* {
  7. // static Lisp_Object* quote_sym = Memory::get_or_create_lisp_object_symbol("quote");
  8. // static Lisp_Object* qquote_sym = Memory::get_or_create_lisp_object_symbol("quasiquote");
  9. // static Lisp_Object* unquote_sym = Memory::get_or_create_lisp_object_symbol("unquote");
  10. // static Lisp_Object* us_sym = Memory::get_or_create_lisp_object_symbol("unquote-splicing");
  11. // if (text[*index_in_text] == '\'') {
  12. // *index_in_text++;
  13. // return quote_sym;
  14. // }
  15. // if (text[*index_in_text] == '`') {
  16. // *index_in_text++;
  17. // return qquote_sym;
  18. // }
  19. // if (text[*index_in_text] == ',') {
  20. // *index_in_text++;
  21. // return unquote_sym;
  22. // }
  23. // if (text[*index_in_text] == ',' && text[*index_in_text] == '@') {
  24. // *index_in_text += 2;
  25. // return us_sym;
  26. // }
  27. return nullptr;
  28. }
  29. proc inject_scl(Lisp_Object* lo) -> void {
  30. lo->sourceCodeLocation = new(Source_Code_Location);
  31. lo->sourceCodeLocation->file = parser_file;
  32. lo->sourceCodeLocation->line = parser_line;
  33. lo->sourceCodeLocation->column = parser_col;
  34. }
  35. proc eat_comment_line(char* text, int* index_in_text) -> void {
  36. // safety check if we are actually starting a comment here
  37. if (text[*index_in_text] != ';')
  38. return;
  39. // eat the comment line
  40. do {
  41. ++(*index_in_text);
  42. ++parser_col;
  43. } while (text[(*index_in_text)] != '\n' &&
  44. text[(*index_in_text)] != '\r' &&
  45. text[(*index_in_text)] != '\0');
  46. }
  47. proc eat_whitespace(char* text, int* index_in_text) -> void {
  48. // skip whitespaces
  49. while (text[(*index_in_text)] == ' ' ||
  50. text[(*index_in_text)] == '\t' ||
  51. text[(*index_in_text)] == '\n' ||
  52. text[(*index_in_text)] == '\r')
  53. {
  54. if (text[(*index_in_text)] == '\n') {
  55. ++parser_line;
  56. parser_col = 0;
  57. }
  58. ++parser_col;
  59. ++(*index_in_text);
  60. }
  61. }
  62. proc eat_until_code(char* text, int* index_in_text) -> void {
  63. int position_before;
  64. do {
  65. position_before = *index_in_text;
  66. eat_comment_line(text, index_in_text);
  67. eat_whitespace(text, index_in_text);
  68. } while (position_before != *index_in_text);
  69. }
  70. proc read_atom(char* text, int* index_in_text) -> String* {
  71. int atom_length = 0;
  72. while (text[*index_in_text+atom_length] != ' ' &&
  73. text[*index_in_text+atom_length] != ')' &&
  74. text[*index_in_text+atom_length] != '(' &&
  75. text[*index_in_text+atom_length] != '\0' &&
  76. text[*index_in_text+atom_length] != '\n' &&
  77. text[*index_in_text+atom_length] != '\r' &&
  78. text[*index_in_text+atom_length] != '\t')
  79. {
  80. ++atom_length;
  81. }
  82. // let's mark the end of the atom there quickly, so the string can
  83. // be copied from there easily and then put the char that was
  84. // before there back
  85. char before = text[*index_in_text+atom_length];
  86. text[*index_in_text+atom_length] = '\0';
  87. // get the atom
  88. String* ret = Memory::create_string("", atom_length);
  89. // char* atom = (char*)malloc(atom_length*sizeof(char)+1); // plus null char
  90. strcpy(&ret->data, text+(*index_in_text));
  91. // restore the original string
  92. text[*index_in_text+atom_length] = before;
  93. // update the index to point to the character after the atom
  94. // ended
  95. *index_in_text += atom_length;
  96. parser_col += atom_length;
  97. return ret;
  98. }
  99. proc parse_number(char* text, int* index_in_text) -> Lisp_Object* {
  100. double number;
  101. // TODO(Felix): parse the number direcrly from the string and
  102. // dont create a String first
  103. String* str_number = read_atom(text, index_in_text);
  104. sscanf(Memory::get_c_str(str_number), "%lf", &number);
  105. Lisp_Object* ret;
  106. try ret = Memory::create_lisp_object_number(number);
  107. inject_scl(ret);
  108. return ret;
  109. }
  110. proc parse_keyword(char* text, int* index_in_text) -> Lisp_Object* {
  111. // we are now on the colon
  112. ++(*index_in_text);
  113. ++parser_col;
  114. String* str_keyword = read_atom(text, index_in_text);
  115. Lisp_Object* ret;
  116. try ret = Memory::get_or_create_lisp_object_keyword(str_keyword);
  117. inject_scl(ret);
  118. return ret;
  119. }
  120. proc parse_symbol(char* text, int* index_in_text) -> Lisp_Object* {
  121. // we are now at the first char of the symbol
  122. String* str_symbol = read_atom(text, index_in_text);
  123. Lisp_Object* ret;
  124. try ret = Memory::get_or_create_lisp_object_symbol(str_symbol);
  125. inject_scl(ret);
  126. return ret;
  127. }
  128. proc parse_string(char* text, int* index_in_text) -> Lisp_Object*{
  129. // the first character is the '"'
  130. ++(*index_in_text);
  131. ++parser_col;
  132. // now we are at the first letter, if this is the closing '"' then
  133. // it's easy
  134. if (text[*index_in_text] == '"') {
  135. Lisp_Object* ret;
  136. try ret = Memory::create_lisp_object_string(
  137. Memory::create_string("", 0));
  138. inject_scl(ret);
  139. // plus one because we want to go after the quotes
  140. *index_in_text += 1;
  141. ++parser_col;
  142. return ret;
  143. }
  144. // okay so the first letter was not actually closing the string...
  145. int string_length = 0;
  146. bool escaping = false;
  147. while (!(text[*index_in_text+string_length] == '"' && !escaping))
  148. {
  149. if (escaping)
  150. escaping = false;
  151. else
  152. if (text[*index_in_text+string_length] == '\\')
  153. escaping = true;
  154. ++string_length;
  155. }
  156. // we found the end of the string
  157. text[*index_in_text+string_length] = '\0';
  158. // NOTE(Felix): Tactic: Through unescaping the string will
  159. // only get shorter, so we replace it inplace and later jump
  160. // to the original end of the string.
  161. try unescape_string(text+(*index_in_text));
  162. String* string = Memory::create_string("", string_length);
  163. // TODO(Felix): manually copy to parse control sequences
  164. // correctly without the need to unescape the string, also
  165. // better for keeping track of the encountered new lines and
  166. // characters since last new line so we can update the parser
  167. // location more easily
  168. strcpy(&string->data, text+(*index_in_text));
  169. text[*index_in_text+string_length] = '"';
  170. // plus one because we want to go after the quotes
  171. *index_in_text += string_length +1;
  172. // NOTE(Felix): this only has to be done until we manually
  173. // copy the string and we can do some bookeeping:
  174. /* recalculate the parser cursors position: */
  175. /* new col = (count chars since last \n) + 1 */
  176. for (int i = 0; i < string->length; ++i) {
  177. if (*((&string->data)+i) == '\n') {
  178. ++parser_line;
  179. parser_col = 0;
  180. } else {
  181. ++parser_col;
  182. }
  183. }
  184. Lisp_Object* ret;
  185. try ret = Memory::create_lisp_object_string(string);
  186. inject_scl(ret);
  187. return ret;
  188. }
  189. proc parse_atom(char* text, int* index_in_text) -> Lisp_Object* {
  190. // numbers
  191. if ((text[*index_in_text] <= 57 && // if number
  192. text[*index_in_text] >= 48)
  193. ||
  194. ((text[*index_in_text] == '+' || // or if sign and then number
  195. text[*index_in_text] == '-')
  196. &&
  197. (text[*index_in_text +1] <= 57 &&
  198. text[*index_in_text +1] >= 48))
  199. ||
  200. ((text[*index_in_text] == '.') // or if . and then number
  201. &&
  202. (text[*index_in_text +1] <= 57 &&
  203. text[*index_in_text +1] >= 48)))
  204. return parse_number(text, index_in_text);
  205. // keywords
  206. if (text[*index_in_text] == ':')
  207. return parse_keyword(text, index_in_text);
  208. // strings
  209. if (text[*index_in_text] == '"')
  210. return parse_string(text, index_in_text);
  211. return parse_symbol(text, index_in_text);
  212. }
  213. proc parse_expression(char* text, int* index_in_text) -> Lisp_Object* {
  214. // if it is quoted
  215. // TODO(Felix): This looks totally broken..
  216. if (text[*index_in_text] == '\'' ||
  217. text[*index_in_text] == '`' ||
  218. text[*index_in_text] == '@' ||
  219. text[*index_in_text] == ',')
  220. {
  221. char quoteType = text[*index_in_text];
  222. ++(*index_in_text);
  223. ++parser_col;
  224. Lisp_Object* result;
  225. if (text[*index_in_text] == '(' ||
  226. text[*index_in_text] == '\'' ||
  227. text[*index_in_text] == '`' ||
  228. text[*index_in_text] == '@' ||
  229. text[*index_in_text] == ',')
  230. {
  231. try result = parse_expression(text, index_in_text);
  232. } else {
  233. try result = parse_atom(text, index_in_text);
  234. }
  235. Lisp_Object* ret = nullptr;
  236. if (quoteType == '\'')
  237. try ret = Memory::create_list(Memory::get_or_create_lisp_object_symbol("quote"), result);
  238. else if (quoteType == '`')
  239. try ret = Memory::create_list(Memory::get_or_create_lisp_object_symbol("quasiquote"), result);
  240. else if (quoteType == ',')
  241. try ret = Memory::create_list(Memory::get_or_create_lisp_object_symbol("unquote"), result);
  242. else if (quoteType == '@') {
  243. try ret = Memory::create_list(Memory::get_or_create_lisp_object_symbol("unquote-splicing"), result);
  244. }
  245. if (ret) inject_scl(ret);
  246. return ret;
  247. }
  248. // if it is not quoted
  249. ++(*index_in_text);
  250. ++parser_col;
  251. eat_whitespace(text, index_in_text);
  252. // if there was actually nothing in the list, we define here,
  253. // that that means nil
  254. if (text[(*index_in_text)] == ')') {
  255. ++(*index_in_text);
  256. ++parser_col;
  257. return Memory::nil;
  258. }
  259. // okay there is something
  260. Lisp_Object* head;
  261. try head = Memory::create_lisp_object();
  262. Memory::set_type(head, Lisp_Object_Type::Pair);
  263. // head->value.pair = new(Pair);
  264. Lisp_Object* expression = head;
  265. while (true) {
  266. inject_scl(head);
  267. if (text[*index_in_text] == '(' ||
  268. text[*index_in_text] == '\''||
  269. text[*index_in_text] == '`' ||
  270. text[*index_in_text] == '@' ||
  271. text[*index_in_text] == ',')
  272. {
  273. try head->value.pair.first = parse_expression(text, index_in_text);
  274. } else {
  275. try head->value.pair.first = parse_atom(text, index_in_text);
  276. }
  277. eat_until_code(text, index_in_text);
  278. if (text[(*index_in_text)] == '\0') {
  279. create_parsing_error(
  280. "Unexpected EOF in %s:%d:%d",
  281. parser_file, parser_line, parser_col);
  282. return nullptr;
  283. }
  284. if (text[(*index_in_text)] == ')') {
  285. head->value.pair.rest = Memory::nil;
  286. ++parser_col;
  287. ++(*index_in_text);
  288. break;
  289. } else if (text[(*index_in_text)] == '.') {
  290. ++parser_col;
  291. ++(*index_in_text);
  292. eat_until_code(text, index_in_text);
  293. if (text[(*index_in_text)] == '(')
  294. head->value.pair.rest = parse_expression(text, index_in_text);
  295. else
  296. head->value.pair.rest = parse_atom(text, index_in_text);
  297. eat_until_code(text, index_in_text);
  298. if (text[(*index_in_text)] != ')')
  299. create_parsing_error(
  300. "Expected ')' after the element after the '.' in %s:%d:%d",
  301. create_source_code_location(parser_file, parser_line, parser_col));
  302. ++parser_col;
  303. ++(*index_in_text);
  304. break;
  305. } else {
  306. try head->value.pair.rest = Memory::create_lisp_object_pair(Memory::nil, Memory::nil);
  307. head = head->value.pair.rest;
  308. }
  309. }
  310. // check if we have to create or delete or run macros
  311. // while (Memory::get_type(expression->value.pair.first) == Lisp_Object_Type::Symbol) {
  312. // Lisp_Object* parsed_symbol = expression->value.pair.first;
  313. // if (string_equal("define-syntax", parsed_symbol->value.symbol.identifier)) {
  314. // // create a new macro
  315. // Lisp_Object* arguments = expression->value.pair.rest;
  316. // Lisp_Object* body;
  317. // int arguments_length;
  318. // // HACK(Felix): almost code duplicate from
  319. // // `built_ins.cpp`: special-lambda
  320. // try arguments_length = list_length(arguments);
  321. // // (define-syntax (defun name args :rest body) (...))
  322. // if (arguments_length < 2) {
  323. // create_wrong_number_of_arguments_error(3, arguments_length);
  324. // return nullptr;
  325. // }
  326. // assert_type(arguments->value.pair.first, Lisp_Object_Type::Pair);
  327. // // extract the name
  328. // Lisp_Object* symbol_for_macro = arguments->value.pair.first->value.pair.first;
  329. // body = arguments->value.pair.rest;
  330. // arguments = arguments->value.pair.first->value.pair.rest;
  331. // // Function* function = new(Function);
  332. // Lisp_Object* macro;
  333. // try macro = Memory::create_lisp_object();
  334. // Memory::set_type(macro, Lisp_Object_Type::Function);
  335. // macro->value.function.parent_environment = get_current_environment();
  336. // macro->value.function.type = Function_Type::Macro;
  337. // // if parameters were specified
  338. // if (arguments != Memory::nil) {
  339. // try assert_type(arguments, Lisp_Object_Type::Pair);
  340. // try create_arguments_from_lambda_list_and_inject(arguments, macro);
  341. // } else {
  342. // macro->value.function.args.positional = create_positional_argument_list(1);
  343. // macro->value.function.args.keyword = create_keyword_argument_list(1);
  344. // macro->value.function.args.rest = nullptr;
  345. // }
  346. // // arguments = arguments->value.pair.rest;
  347. // // if there is a docstring, use it
  348. // if (Memory::get_type(body->value.pair.first) == Lisp_Object_Type::String) {
  349. // macro->docstring = body->value.pair.first->value.string;
  350. // body = body->value.pair.rest;
  351. // } else {
  352. // macro->docstring = nullptr;
  353. // }
  354. // // we are now in the function body, just wrap it in an
  355. // // implicit begin
  356. // try macro->value.function.body = Memory::create_lisp_object_pair(
  357. // Memory::get_or_create_lisp_object_symbol("begin"),
  358. // body);
  359. // inject_scl(macro);
  360. // // macro->value.function = function;
  361. // define_symbol(symbol_for_macro, macro);
  362. // // print_environment(environment_for_macros);
  363. // return Memory::nil;
  364. // } else if (string_equal("delete-syntax", parsed_symbol->value.symbol.identifier)) {
  365. // /* --- deleting an existing macro --- */
  366. // // TODO(Felix): this is a hard one because when
  367. // // environments will be made from hashmaps, how can we
  368. // // delete stuff from hashmaps? If we do probing on
  369. // // collision and then delte the first colliding entry,
  370. // // how can we find the second one? How many probes do
  371. // // we have to do to know for sure that an elemenet is
  372. // // not in the hashmap? It would be much easier if we
  373. // // never deleted any elements from the hashmap, so
  374. // // that, when an entry is not found immidiately, we
  375. // // know for sure that it does not exist in the table.
  376. // create_generic_error("deleting macros has not yet be implemented,"
  377. // "and I don't know if it is a good idea to do so.");
  378. // return nullptr;
  379. // } else {
  380. // // if threre is a macro named like this, then macroexpand
  381. // // if not it is regular code, dont touch.
  382. // break;
  383. // Lisp_Object* macro = try_lookup_symbol(parsed_symbol, get_current_environment());
  384. // if (macro &&
  385. // Memory::get_type(macro) == Lisp_Object_Type::Function &&
  386. // macro->value.function.type == Function_Type::Macro)
  387. // {
  388. // // printf("pretending to expand macro at %s %d %d: ",
  389. // // Memory::get_c_str(parser_file),
  390. // // parser_line, parser_col);
  391. // // print(parsed_symbol);
  392. // // printf("\n");
  393. // // NOTE(Felix): Execute it as a special lambda,
  394. // // because if we keep it as a macro, the evaluator
  395. // // will think it is a stray macro that was not yet
  396. // // expanded, and attempt to evaluate it twice (1.
  397. // // for expanding, and 2. for evaluating)
  398. // macro->value.function.type = Function_Type::Special_Lambda;
  399. // // NOTE(Felix): deferred so even if eval expr
  400. // // fails, and returns, the type will be be
  401. // // resetted to macro.
  402. // defer {
  403. // macro->value.function.type = Function_Type::Macro;
  404. // };
  405. // try expression = eval_expr(expression);
  406. // break;
  407. // } else break;
  408. // }
  409. // }
  410. return expression;
  411. }
  412. proc parse_single_expression(char* text) -> Lisp_Object* {
  413. parser_file = standard_in;
  414. parser_line = 1;
  415. parser_col = 1;
  416. int index_in_text = 0;
  417. Lisp_Object* result;
  418. eat_until_code(text, &index_in_text);
  419. if (text[(index_in_text)] == '\0')
  420. return Memory::nil;
  421. if (text[index_in_text] == '(' ||
  422. text[index_in_text] == '\'' ||
  423. text[index_in_text] == '@' ||
  424. text[index_in_text] == '`' ||
  425. text[index_in_text] == ',')
  426. {
  427. try {
  428. result = parse_expression(text, &index_in_text);
  429. }
  430. }
  431. else
  432. try {
  433. result = parse_atom(text, &index_in_text);
  434. }
  435. eat_until_code(text, &index_in_text);
  436. if (text[(index_in_text)] == '\0')
  437. return result;
  438. create_parsing_error("Trainling garbage after expression at %s:%d:%d",
  439. parser_file, parser_line, parser_col);
  440. return nullptr;
  441. }
  442. proc parse_single_expression_or_bare_words(char* text, char* bare) -> Lisp_Object* {
  443. parser_file = standard_in;
  444. parser_line = 1;
  445. parser_col = 1;
  446. int index_in_text = 0;
  447. Lisp_Object* result;
  448. eat_until_code(text, &index_in_text);
  449. if (text[(index_in_text)] == '\0')
  450. return Memory::nil;
  451. if (text[index_in_text] == '(' ||
  452. text[index_in_text] == '\'' ||
  453. text[index_in_text] == '@' ||
  454. text[index_in_text] == '`' ||
  455. text[index_in_text] == ',')
  456. {
  457. try {
  458. result = parse_expression(text, &index_in_text);
  459. }
  460. return result;
  461. }
  462. else {
  463. // TODO(Felix): What is going on, why do we not have to
  464. // increase the index_in_text
  465. int end_pos = index_in_text;
  466. while (text[end_pos] != '\n')
  467. ++end_pos;
  468. text[end_pos] = '\0';
  469. Lisp_Object* str;
  470. try str = Memory::create_lisp_object_string(
  471. Memory::create_string(text+index_in_text));
  472. text[end_pos] = '\n';
  473. return Memory::create_list(Memory::get_or_create_lisp_object_symbol(bare), str);
  474. }
  475. }
  476. proc write_expanded_file(String* file_name, Lisp_Object_Array_List program) -> void {
  477. const char* ext = ".expanded";
  478. char* newName = (char*)calloc(10 + file_name->length, sizeof(char));
  479. strcpy(newName, Memory::get_c_str(file_name));
  480. strcat(newName, ext);
  481. FILE *f = fopen(newName, "w");
  482. defer {
  483. fclose(f);
  484. free(newName);
  485. };
  486. if (f == NULL) {
  487. printf("Error opening .expanded file for writing!\n");
  488. exit(1);
  489. }
  490. for (int i = 0; i < program.next_index; ++i) {
  491. // a macro will parse as nil for now, so we skip those
  492. if (program.data[i] == Memory::nil)
  493. continue;
  494. print(program.data[i], true, f);
  495. fprintf(f, "\n\n");
  496. }
  497. }
  498. proc parse_program(String* file_name, char* text) -> Lisp_Object_Array_List {
  499. parser_file = file_name;
  500. parser_line = 1;
  501. parser_col = 0;
  502. Lisp_Object_Array_List program = create_Lisp_Object_array_list();
  503. int index_in_text = 0;
  504. while (text[index_in_text] != '\0') {
  505. switch (text[index_in_text]) {
  506. case '(': {
  507. Lisp_Object* parsed;
  508. try_struct {
  509. parsed = parse_expression(text, &index_in_text);
  510. }
  511. append_to_array_list(&program, parsed);
  512. } break;
  513. case ';':
  514. case ' ':
  515. case '\t':
  516. case '\n':
  517. case '\r': {
  518. eat_until_code(text, &index_in_text);
  519. } break;
  520. default:
  521. /* syntax error */
  522. create_parsing_error("Garbage in file scope at %s:%d:%d",
  523. parser_file, parser_line, parser_col);
  524. return {};
  525. }
  526. }
  527. write_expanded_file(file_name, program);
  528. return program;
  529. }
  530. }