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.

207 lines
11 KiB

1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
  1. #include "molasses/lexer.h"
  2. #include "molasses/parser_primitives.h"
  3. #include "molasses/errors.h"
  4. #include <cstring>
  5. #include <filesystem>
  6. #include <fstream>
  7. #include <iostream>
  8. #include <stack>
  9. #include <string>
  10. #include <variant>
  11. using compile_element = std::variant<molasses::lexed_output, molasses::generate_context, std::string>;
  12. int main(int argc, char** argv) {
  13. std::vector<std::string> arguments;
  14. while(argc > 1) {
  15. argv++;
  16. arguments.emplace_back(*argv, strlen(*argv));
  17. argc--;
  18. }
  19. try {
  20. std::stack<compile_element> compile_stack;
  21. for(auto elem : arguments) {
  22. if(elem == "generate") {
  23. if(not compile_stack.empty() and std::holds_alternative<std::string>(compile_stack.top())) {
  24. auto filename = std::get<std::string>(compile_stack.top());
  25. compile_stack.pop();
  26. if(not compile_stack.empty() and std::holds_alternative<molasses::generate_context>(compile_stack.top())) {
  27. auto generator = std::get<molasses::generate_context>(compile_stack.top());
  28. compile_stack.pop();
  29. auto assembler = molasses::generate(generator);
  30. std::ofstream output(filename + ".s");
  31. for(const auto& line : assembler) {
  32. output << line;
  33. }
  34. compile_stack.emplace(filename);
  35. } else
  36. throw molasses::build_system_error("generate expects a parsed output\n");
  37. } else
  38. throw molasses::build_system_error("generate expects a filename\n");
  39. } else if(elem == "parse") {
  40. molasses::parser_context ctx;
  41. ctx = molasses::register_integers(ctx);
  42. ctx = molasses::register_i32_operations(ctx);
  43. if(not compile_stack.empty() and std::holds_alternative<molasses::lexed_output>(compile_stack.top())) {
  44. auto lexer = std::get<molasses::lexed_output>(compile_stack.top());
  45. compile_stack.pop();
  46. auto generator = molasses::parse(ctx, lexer);
  47. compile_stack.emplace(generator);
  48. } else
  49. throw molasses::build_system_error("parse expects a lexed output\n");
  50. } else if(elem == "lex") {
  51. if(not compile_stack.empty() and std::holds_alternative<std::string>(compile_stack.top())) {
  52. auto filename = std::get<std::string>(compile_stack.top());
  53. compile_stack.pop();
  54. if(not std::filesystem::exists(filename))
  55. throw molasses::build_system_error("file " + filename + " does not exist\n");
  56. std::ifstream t(filename);
  57. std::stringstream buffer;
  58. buffer << t.rdbuf();
  59. auto lexed = molasses::lex(filename, buffer.str());
  60. compile_stack.emplace(lexed);
  61. } else
  62. throw molasses::build_system_error("lex expects a filename\n");
  63. } else if(elem == "lex-all") {
  64. std::vector<molasses::lexed_output> lexed_list;
  65. while(not compile_stack.empty() and std::holds_alternative<std::string>(compile_stack.top())) {
  66. auto filename = std::get<std::string>(compile_stack.top());
  67. compile_stack.pop();
  68. if(not std::filesystem::exists(filename))
  69. throw molasses::build_system_error("file " + filename + " does not exist\n");
  70. std::ifstream t(filename);
  71. std::stringstream buffer;
  72. buffer << t.rdbuf();
  73. auto lexed = molasses::lex(filename, buffer.str());
  74. lexed_list.emplace_back(lexed);
  75. }
  76. for(auto& lexed : lexed_list) {
  77. compile_stack.emplace(std::move(lexed));
  78. }
  79. } else if(elem == "merge") {
  80. if(not compile_stack.empty() and std::holds_alternative<molasses::lexed_output>(compile_stack.top())) {
  81. auto lexer_1 = std::get<molasses::lexed_output>(compile_stack.top());
  82. compile_stack.pop();
  83. if(not compile_stack.empty() and std::holds_alternative<molasses::lexed_output>(compile_stack.top())) {
  84. auto lexer_2 = std::get<molasses::lexed_output>(compile_stack.top());
  85. compile_stack.pop();
  86. compile_stack.emplace(molasses::concatenate(lexer_1, lexer_2));
  87. } else
  88. throw molasses::build_system_error("merge expects 2 lexed outputs\n");
  89. } else
  90. throw molasses::build_system_error("merge expects 2 lexed outputs\n");
  91. } else if(elem == "merge-all") {
  92. if(not compile_stack.empty() and std::holds_alternative<molasses::lexed_output>(compile_stack.top())) {
  93. auto lexer_1 = std::get<molasses::lexed_output>(compile_stack.top());
  94. compile_stack.pop();
  95. while(not compile_stack.empty() and std::holds_alternative<molasses::lexed_output>(compile_stack.top())) {
  96. auto lexer_2 = std::get<molasses::lexed_output>(compile_stack.top());
  97. compile_stack.pop();
  98. lexer_1 = molasses::concatenate(lexer_1, lexer_2);
  99. }
  100. compile_stack.emplace(lexer_1);
  101. } else
  102. throw molasses::build_system_error("merge-all expects at least 1 lexed outputs\n");
  103. } else if(elem == "interpret") {
  104. if(not compile_stack.empty() and std::holds_alternative<molasses::generate_context>(compile_stack.top())) {
  105. auto context = std::get<molasses::generate_context>(compile_stack.top());
  106. compile_stack.pop();
  107. molasses::interpreter_stack stack;
  108. while(true) {
  109. std::cout << "%> ";
  110. std::string contents;
  111. std::getline(std::cin, contents);
  112. molasses::interpret(stack, context, contents);
  113. }
  114. } else
  115. throw molasses::build_system_error("interpreter expects 1 generated context\n");
  116. } else if(elem == "assemble") {
  117. if(not compile_stack.empty() and std::holds_alternative<std::string>(compile_stack.top())) {
  118. auto filename = std::get<std::string>(compile_stack.top());
  119. compile_stack.pop();
  120. std::stringstream compile;
  121. compile << "clang -c " << filename << ".s -o " << filename << ".o";
  122. std::stringstream link;
  123. link << "ld -e _start " << filename << ".o -o " << filename;
  124. std::cout << compile.str() << std::endl;
  125. system(compile.str().c_str());
  126. std::cout << link.str() << std::endl;
  127. system(link.str().c_str());
  128. } else
  129. throw molasses::build_system_error("assemble expects an assembly file\n");
  130. } else if(elem == "help" or elem == "--help") {
  131. std::cout << "# Sugar\n\n";
  132. std::cout << "## Commands\n\n";
  133. std::cout << "lex : string ➔ lexed_output\n";
  134. std::cout << "> takes a filename to a file that must be compiled\n\n";
  135. std::cout << "lex-all : string* ➔ lexed_output*\n";
  136. std::cout << "> takes as many filenames to files that must be compiled as can be read and passes them through the lexed\n\n";
  137. std::cout << "merge : lexed_output lexed_output ➔ lexed_output\n";
  138. std::cout << "> merges two lexed modules together\n\n";
  139. std::cout << "merge-all : lexed_output* ➔ lexed_output\n";
  140. std::cout << "> merges as many lexed modules together as present on the top of the stack\n\n";
  141. std::cout << "parse : lexed_output ➔ parsed_output\n";
  142. std::cout << "> prepares code for generation\n\n";
  143. std::cout << "generate : parsed_output string ➔ string\n";
  144. std::cout << "> takes a root filename, it will be appended with \".s\" and that will be the generated assembly file,\n";
  145. std::cout << "> the filename will not be consumed\n\n";
  146. std::cout << "assemble : string ➔ _ \n";
  147. std::cout
  148. << "> takes a root filename, it will be appended with \".s\" and that file will be compiled,\n";
  149. std::cout << "> the compiled output will be the given filename\n\n";
  150. std::cout << "help : _ ➔ _ \n";
  151. std::cout << "> prints this help\n\n";
  152. std::cout << "## Examples\n\n";
  153. std::cout << "- compile the file \"example.mol\" into the \"potato.s\" assembly file\n";
  154. std::cout << "> `$ sugar example.mol lex parse potato generate`\n";
  155. std::cout << "\n";
  156. std::cout << "- compile the file \"example.mol\" into the \"potato\" executable\n";
  157. std::cout << "> `$ sugar example.mol lex parse potato generate assemble`\n";
  158. std::cout << "\n";
  159. std::cout << "- compile the file \"example.mol\" and \"2.mol\" into the \"potato\" executable\n";
  160. std::cout << "> `$ sugar example.mol lex 2.mol lex merge parse potato generate assemble`\n";
  161. } else
  162. compile_stack.emplace(elem);
  163. }
  164. if(compile_stack.size() > 1) throw std::runtime_error("build left unfinished operations");
  165. if(not compile_stack.empty()) {
  166. if(std::holds_alternative<molasses::lexed_output>(compile_stack.top())) {
  167. auto lexer = std::get<molasses::lexed_output>(compile_stack.top());
  168. for(const auto& elem : lexer.symbols) {
  169. std::cout << elem << " ";
  170. }
  171. std::cout << "\n\n";
  172. for(auto [id, name] : lexer.dictionary) {
  173. std::cout << id << ": " << name << "\n";
  174. }
  175. } else if(std::holds_alternative<molasses::generate_context>(compile_stack.top())) {
  176. auto generator = std::get<molasses::generate_context>(compile_stack.top());
  177. for(const auto& elem : generator.procedures) {
  178. std::cout << elem->_name << " : ";
  179. for(const auto& args : elem->_args) {
  180. std::cout << args << " ";
  181. }
  182. std::cout << "->";
  183. for(const auto& rets : elem->_rets) {
  184. std::cout << " " << rets;
  185. }
  186. std::cout << "\n";
  187. }
  188. }
  189. }
  190. } catch (molasses::parser_error& error) {
  191. std::cerr << "COMPILER ERROR:\n\t" << error.what();
  192. return 1;
  193. } catch (molasses::build_system_error& error) {
  194. std::cerr << "BUILD SYSTEM ERROR:\n\t" << error.what();
  195. return 1;
  196. } catch (...) {
  197. std::cerr << "ERREUR INCONNUE\n";
  198. return 1;
  199. }
  200. }