summaryrefslogtreecommitdiff
path: root/src/main.c
diff options
context:
space:
mode:
authorZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-01-11 15:11:00 +0000
committerZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-01-11 15:11:00 +0000
commit55247a3f12a9eee7ba3fd7ca6d8fcea7a82c20f3 (patch)
treea2a71e2eb8ca0b2c483518c1902d89d18709c9ab /src/main.c
parent2e7abed7cfe84a2c0df371cde35f8f68cfdca16c (diff)
Added src/ folder. Now I will add the rest.
Diffstat (limited to 'src/main.c')
-rw-r--r--src/main.c301
1 files changed, 301 insertions, 0 deletions
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..a455943
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,301 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "zprep.h"
+#include "parser/parser.h"
+#include "codegen/codegen.h"
+#include "repl/repl.h"
+#include "plugins/plugin_manager.h"
+
+// Forward decl for LSP
+int lsp_main(int argc, char **argv);
+
+void print_search_paths()
+{
+ printf("Search paths:\n");
+ printf(" ./\n");
+ printf(" ./std/\n");
+ printf(" /usr/local/lib/zen/\n");
+}
+
+void print_usage()
+{
+ printf("Usage: zc [command] [options] <file.zc>\n");
+ printf("Commands:\n");
+ printf(" run Compile and run the program\n");
+ printf(" build Compile to executable\n");
+ printf(" check Check for errors only\n");
+ printf(" repl Start Interactive REPL\n");
+ printf(" lsp Start Language Server\n");
+ printf("Options:\n");
+ printf(" -o <file> Output executable name\n");
+ printf(" --emit-c Keep generated C file (out.c)\n");
+ printf(" --freestanding Freestanding mode (no stdlib)\n");
+ printf(" --cc <compiler> C compiler to use (gcc, clang, tcc)\n");
+ printf(" -O<level> Optimization level\n");
+ printf(" -g Debug info\n");
+ printf(" -v, --verbose Verbose output\n");
+ printf(" -q, --quiet Quiet output\n");
+ printf(" -c Compile only (produce .o)\n");
+}
+
+int main(int argc, char **argv)
+{
+ // Defaults
+ memset(&g_config, 0, sizeof(g_config));
+ strcpy(g_config.cc, "gcc");
+
+ if (argc < 2)
+ {
+ print_usage();
+ return 1;
+ }
+
+ // Parse command
+ char *command = argv[1];
+ int arg_start = 2;
+
+ if (strcmp(command, "lsp") == 0)
+ {
+ return lsp_main(argc, argv);
+ }
+ else if (strcmp(command, "repl") == 0)
+ {
+ run_repl(argv[0]); // Pass self path for recursive calls
+ return 0;
+ }
+ else if (strcmp(command, "run") == 0)
+ {
+ g_config.mode_run = 1;
+ }
+ else if (strcmp(command, "check") == 0)
+ {
+ g_config.mode_check = 1;
+ }
+ else if (strcmp(command, "build") == 0)
+ {
+ // default mode
+ }
+ else if (command[0] == '-')
+ {
+ // implicit build or run? assume build if starts with flag, but usually command first
+ // If file provided directly: "zc file.zc" -> build
+ if (strchr(command, '.'))
+ {
+ // treat as filename
+ g_config.input_file = command;
+ arg_start = 2; // already consumed
+ }
+ else
+ {
+ // Flags
+ arg_start = 1;
+ }
+ }
+ else
+ {
+ // Check if file
+ if (strchr(command, '.'))
+ {
+ g_config.input_file = command;
+ arg_start = 2;
+ }
+ }
+
+ // Parse args
+ for (int i = arg_start; i < argc; i++)
+ {
+ char *arg = argv[i];
+ if (strcmp(arg, "--emit-c") == 0)
+ {
+ g_config.emit_c = 1;
+ }
+ else if (strcmp(arg, "--verbose") == 0 || strcmp(arg, "-v") == 0)
+ {
+ g_config.verbose = 1;
+ }
+ else if (strcmp(arg, "--quiet") == 0 || strcmp(arg, "-q") == 0)
+ {
+ g_config.quiet = 1;
+ }
+ else if (strcmp(arg, "--freestanding") == 0)
+ {
+ g_config.is_freestanding = 1;
+ }
+ else if (strcmp(arg, "--check") == 0)
+ {
+ g_config.mode_check = 1;
+ }
+ else if (strcmp(arg, "--cc") == 0)
+ {
+ if (i + 1 < argc)
+ {
+ strcpy(g_config.cc, argv[++i]);
+ }
+ }
+ else if (strcmp(arg, "-o") == 0)
+ {
+ if (i + 1 < argc)
+ {
+ g_config.output_file = argv[++i];
+ }
+ }
+ else if (strncmp(arg, "-O", 2) == 0)
+ {
+ // Add to CFLAGS
+ strcat(g_config.gcc_flags, " ");
+ strcat(g_config.gcc_flags, arg);
+ }
+ else if (strcmp(arg, "-g") == 0)
+ {
+ strcat(g_config.gcc_flags, " -g");
+ }
+ else if (arg[0] == '-')
+ {
+ // Unknown flag or C flag
+ strcat(g_config.gcc_flags, " ");
+ strcat(g_config.gcc_flags, arg);
+ }
+ else
+ {
+ if (!g_config.input_file)
+ {
+ g_config.input_file = arg;
+ }
+ else
+ {
+ printf("Multiple input files not supported yet.\n");
+ return 1;
+ }
+ }
+ }
+
+ if (!g_config.input_file)
+ {
+ printf("Error: No input file specified.\n");
+ return 1;
+ }
+
+ g_current_filename = g_config.input_file;
+
+ // Load file
+ char *src = load_file(g_config.input_file);
+ if (!src)
+ {
+ printf("Error: Could not read file %s\n", g_config.input_file);
+ return 1;
+ }
+
+ init_builtins();
+
+ // Initialize Plugin Manager
+ zptr_plugin_mgr_init();
+
+ // Register built-in plugins
+ extern ZPlugin brainfuck_plugin;
+ extern ZPlugin befunge_plugin;
+ extern ZPlugin lisp_plugin;
+ extern ZPlugin forth_plugin;
+ extern ZPlugin regex_plugin;
+ extern ZPlugin sql_plugin;
+
+ zptr_register_plugin(&brainfuck_plugin);
+ zptr_register_plugin(&befunge_plugin);
+ zptr_register_plugin(&lisp_plugin);
+ zptr_register_plugin(&forth_plugin);
+ zptr_register_plugin(&regex_plugin);
+ zptr_register_plugin(&sql_plugin);
+
+ Lexer l;
+ lexer_init(&l, src);
+
+ // Parse
+ ParserContext ctx;
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.hoist_out = tmpfile(); // Temp file for plugin hoisting
+ if (!ctx.hoist_out)
+ {
+ perror("tmpfile for hoisting");
+ return 1;
+ }
+ g_parser_ctx = &ctx;
+
+ if (!g_config.quiet)
+ {
+ printf("[zc] Compiling %s...\n", g_config.input_file);
+ }
+
+ ASTNode *root = parse_program(&ctx, &l);
+ if (!root)
+ {
+ // Parse failed
+ return 1;
+ }
+
+ // Checking mode?
+ // analyze(root); // Implicit in parsing or separate step? Assuming separate if check_mode
+
+ if (g_config.mode_check)
+ {
+ // Just verify
+ printf("Check passed.\n");
+ return 0;
+ }
+
+ // Codegen to C
+ FILE *out = fopen("out.c", "w");
+ if (!out)
+ {
+ perror("fopen out.c");
+ return 1;
+ }
+
+ codegen_node(&ctx, root, out);
+ fclose(out);
+
+ // Compile C
+ char cmd[8192];
+ char *outfile = g_config.output_file ? g_config.output_file : "a.out";
+
+ // TCC-specific adjustments?
+ // Already handled by user passing --cc tcc
+
+ snprintf(cmd, sizeof(cmd), "%s %s %s -o %s out.c -lm -lpthread -I./src", g_config.cc,
+ g_config.gcc_flags, g_config.is_freestanding ? "-ffreestanding" : "", outfile);
+
+ if (g_config.verbose)
+ {
+ printf("[CMD] %s\n", cmd);
+ }
+
+ int ret = system(cmd);
+ if (ret != 0)
+ {
+ printf("C compilation failed.\n");
+ if (!g_config.emit_c)
+ {
+ remove("out.c");
+ }
+ return 1;
+ }
+
+ if (!g_config.emit_c)
+ {
+ // remove("out.c"); // Keep it for debugging for now or follow flag
+ remove("out.c");
+ }
+
+ if (g_config.mode_run)
+ {
+ char run_cmd[2048];
+ sprintf(run_cmd, "./%s", outfile);
+ ret = system(run_cmd);
+ // Clean up executable if temp run?
+ // ZPrep behavior: keeps it.
+ return ret;
+ }
+
+ zptr_plugin_mgr_cleanup();
+ return 0;
+}