aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2025-08-25 12:43:34 -0400
committerBruce Hill <bruce@bruce-hill.com>2025-08-25 12:43:34 -0400
commit3e2c911ce260907e3f8f4a4278a7c0ec4793e78a (patch)
tree93a3d7a8bb469df926e4407ac996aa606692039f
parent55479cbf9e4a8f36afe41d84df687f05fc7661f0 (diff)
First pass at formatter
-rw-r--r--src/formatter.c72
-rw-r--r--src/formatter.h6
-rw-r--r--src/parse/context.c2
-rw-r--r--src/parse/context.h2
-rw-r--r--src/tomo.c62
5 files changed, 116 insertions, 28 deletions
diff --git a/src/formatter.c b/src/formatter.c
index 81a5e126..83d1b7a0 100644
--- a/src/formatter.c
+++ b/src/formatter.c
@@ -1,23 +1,87 @@
// This code defines functions for transforming ASTs back into Tomo source text
+#include <gc.h>
+#include <setjmp.h>
+#include <string.h>
+
#include "ast.h"
+#include "formatter.h"
+#include "parse/context.h"
+#include "parse/files.h"
+#include "parse/utils.h"
#include "stdlib/datatypes.h"
#include "stdlib/optionals.h"
+#include "stdlib/tables.h"
#include "stdlib/text.h"
-Text_t code_format(ast_t *ast, Table_t *comments) {
+OptionalText_t next_comment(Table_t comments, const char **pos, const char *end) {
+ for (const char *p = *pos; p < end; p++) {
+ const char **comment_end = Table$get(comments, &p, parse_comments_info);
+ if (comment_end) {
+ *pos = *comment_end;
+ return Text$from_strn(p, (int64_t)(*comment_end - p));
+ }
+ }
+ return NONE_TEXT;
+}
+
+Text_t format_code(ast_t *ast, Table_t comments) {
(void)comments;
switch (ast->tag) {
- default: return Text$from_strn(ast->start, (int64_t)(ast->end - ast->start));
+ default: {
+ Text_t code = Text$from_strn(ast->start, (int64_t)(ast->end - ast->start));
+ return Text$replace(code, Text("\t"), Text(" "));
+ }
}
}
-OptionalText_t code_format_inline(ast_t *ast, Table_t *comments) {
- (void)comments;
+OptionalText_t format_inline_code(ast_t *ast, Table_t comments) {
for (const char *p = ast->start; p < ast->end; p++) {
if (*p == '\n' || *p == '\r') return NONE_TEXT;
}
+ const char *pos = ast->start;
+ OptionalText_t comment = next_comment(comments, &pos, ast->end);
+ if (comment.length >= 0) return NONE_TEXT;
switch (ast->tag) {
default: return Text$from_strn(ast->start, (int64_t)(ast->end - ast->start));
}
}
+
+Text_t format_file(const char *path) {
+ file_t *file = load_file(path);
+ if (!file) return EMPTY_TEXT;
+
+ jmp_buf on_err;
+ if (setjmp(on_err) != 0) {
+ return Text$from_str(file->text);
+ }
+ parse_ctx_t ctx = {
+ .file = file,
+ .on_err = &on_err,
+ .comments = {},
+ };
+
+ const char *pos = file->text;
+ if (match(&pos, "#!")) // shebang
+ some_not(&pos, "\r\n");
+
+ whitespace(&ctx, &pos);
+ ast_t *ast = parse_file_body(&ctx, pos);
+ if (!ast) return Text$from_str(file->text);
+ pos = ast->end;
+ whitespace(&ctx, &pos);
+ if (pos < file->text + file->len && *pos != '\0') {
+ return Text$from_str(file->text);
+ }
+
+ const char *fmt_pos = file->text;
+ Text_t code = EMPTY_TEXT;
+ for (OptionalText_t comment; (comment = next_comment(ctx.comments, &fmt_pos, ast->start)).length > 0;) {
+ code = Texts(code, comment, "\n");
+ }
+ code = Texts(code, format_code(ast, ctx.comments));
+ for (OptionalText_t comment; (comment = next_comment(ctx.comments, &fmt_pos, ast->start)).length > 0;) {
+ code = Texts(code, comment, "\n");
+ }
+ return code;
+}
diff --git a/src/formatter.h b/src/formatter.h
index c3941de9..dd4c7815 100644
--- a/src/formatter.h
+++ b/src/formatter.h
@@ -5,5 +5,7 @@
#include "ast.h"
#include "stdlib/datatypes.h"
-Text_t code_format(ast_t *ast, Table_t *comments);
-OptionalText_t code_format_inline(ast_t *ast, Table_t *comments);
+Text_t format_file(const char *path);
+Text_t format_code(ast_t *ast, Table_t comments);
+OptionalText_t format_inline_code(ast_t *ast, Table_t comments);
+OptionalText_t next_comment(Table_t comments, const char **pos, const char *end);
diff --git a/src/parse/context.c b/src/parse/context.c
index 689fa1a4..cd8d16bc 100644
--- a/src/parse/context.c
+++ b/src/parse/context.c
@@ -5,4 +5,4 @@
#include "../stdlib/tables.h"
#include "../stdlib/types.h"
-TypeInfo_t *parse_comments_info = Table$info(Pointer$info("@", &Memory$info), Pointer$info("@", &Memory$info));
+const TypeInfo_t *parse_comments_info = Table$info(Pointer$info("@", &Memory$info), Pointer$info("@", &Memory$info));
diff --git a/src/parse/context.h b/src/parse/context.h
index 4d519a83..f1e3be2f 100644
--- a/src/parse/context.h
+++ b/src/parse/context.h
@@ -8,7 +8,7 @@
#include "../stdlib/files.h"
#include "../stdlib/types.h"
-const TypeInfo_t *parse_comments_info;
+extern const TypeInfo_t *parse_comments_info;
typedef struct {
file_t *file;
diff --git a/src/tomo.c b/src/tomo.c
index d02cce74..36a47b7f 100644
--- a/src/tomo.c
+++ b/src/tomo.c
@@ -19,6 +19,7 @@
#include "compile/files.h"
#include "compile/headers.h"
#include "config.h"
+#include "formatter.h"
#include "modules.h"
#include "naming.h"
#include "parse/files.h"
@@ -74,9 +75,9 @@ static const char *paths_str(List_t paths) {
static OptionalList_t files = NONE_LIST, args = NONE_LIST, uninstall = NONE_LIST, libraries = NONE_LIST;
static OptionalBool_t verbose = false, quiet = false, show_version = false, show_parse_tree = false,
- show_prefix = false, stop_at_transpile = false, stop_at_obj_compilation = false,
- compile_exe = false, should_install = false, clean_build = false, source_mapping = true,
- show_changelog = false;
+ do_format_code = false, show_prefix = false, stop_at_transpile = false,
+ stop_at_obj_compilation = false, compile_exe = false, should_install = false, clean_build = false,
+ source_mapping = true, show_changelog = false;
static OptionalText_t show_codegen = NONE_TEXT,
cflags = Text("-Werror -fdollars-in-identifiers -std=c2x -Wno-trigraphs "
@@ -185,23 +186,38 @@ int main(int argc, char *argv[]) {
" --optimization|-O <level>: set optimization level\n"
" --run|-r: run a program from " TOMO_PREFIX "/share/tomo_" TOMO_VERSION "/installed\n");
Text_t help = Texts(Text("\x1b[1mtomo\x1b[m: a compiler for the Tomo programming language"), Text("\n\n"), usage);
- tomo_parse_args(
- argc, argv, usage, help, TOMO_VERSION, {"files", true, List$info(&Path$info), &files},
- {"args", true, List$info(&Text$info), &args}, {"verbose", false, &Bool$info, &verbose},
- {"v", false, &Bool$info, &verbose}, {"version", false, &Bool$info, &show_version},
- {"parse", false, &Bool$info, &show_parse_tree}, {"p", false, &Bool$info, &show_parse_tree},
- {"prefix", false, &Bool$info, &show_prefix}, {"quiet", false, &Bool$info, &quiet},
- {"q", false, &Bool$info, &quiet}, {"transpile", false, &Bool$info, &stop_at_transpile},
- {"t", false, &Bool$info, &stop_at_transpile}, {"compile-obj", false, &Bool$info, &stop_at_obj_compilation},
- {"c", false, &Bool$info, &stop_at_obj_compilation}, {"compile-exe", false, &Bool$info, &compile_exe},
- {"e", false, &Bool$info, &compile_exe}, {"uninstall", false, List$info(&Text$info), &uninstall},
- {"u", false, List$info(&Text$info), &uninstall}, {"library", false, List$info(&Path$info), &libraries},
- {"L", false, List$info(&Path$info), &libraries}, {"show-codegen", false, &Text$info, &show_codegen},
- {"C", false, &Text$info, &show_codegen}, {"install", false, &Bool$info, &should_install},
- {"I", false, &Bool$info, &should_install}, {"optimization", false, &Text$info, &optimization},
- {"O", false, &Text$info, &optimization}, {"force-rebuild", false, &Bool$info, &clean_build},
- {"f", false, &Bool$info, &clean_build}, {"source-mapping", false, &Bool$info, &source_mapping},
- {"m", false, &Bool$info, &source_mapping}, {"changelog", false, &Bool$info, &show_changelog}, );
+ tomo_parse_args(argc, argv, usage, help, TOMO_VERSION, //
+ {"files", true, List$info(&Path$info), &files}, //
+ {"args", true, List$info(&Text$info), &args}, //
+ {"verbose", false, &Bool$info, &verbose}, //
+ {"v", false, &Bool$info, &verbose}, //
+ {"version", false, &Bool$info, &show_version}, //
+ {"parse", false, &Bool$info, &show_parse_tree}, //
+ {"p", false, &Bool$info, &show_parse_tree}, //
+ {"format", false, &Bool$info, &do_format_code}, //
+ {"prefix", false, &Bool$info, &show_prefix}, //
+ {"quiet", false, &Bool$info, &quiet}, //
+ {"q", false, &Bool$info, &quiet}, //
+ {"transpile", false, &Bool$info, &stop_at_transpile}, //
+ {"t", false, &Bool$info, &stop_at_transpile}, //
+ {"compile-obj", false, &Bool$info, &stop_at_obj_compilation}, //
+ {"c", false, &Bool$info, &stop_at_obj_compilation}, //
+ {"compile-exe", false, &Bool$info, &compile_exe}, //
+ {"e", false, &Bool$info, &compile_exe}, //
+ {"uninstall", false, List$info(&Text$info), &uninstall}, //
+ {"u", false, List$info(&Text$info), &uninstall}, //
+ {"library", false, List$info(&Path$info), &libraries}, //
+ {"L", false, List$info(&Path$info), &libraries}, //
+ {"show-codegen", false, &Text$info, &show_codegen}, //
+ {"C", false, &Text$info, &show_codegen}, //
+ {"install", false, &Bool$info, &should_install}, //
+ {"I", false, &Bool$info, &should_install}, //
+ {"optimization", false, &Text$info, &optimization}, //
+ {"O", false, &Text$info, &optimization}, //
+ {"force-rebuild", false, &Bool$info, &clean_build}, {"f", false, &Bool$info, &clean_build}, //
+ {"source-mapping", false, &Bool$info, &source_mapping},
+ {"m", false, &Bool$info, &source_mapping}, //
+ {"changelog", false, &Bool$info, &show_changelog}, );
if (show_prefix) {
print(TOMO_PREFIX);
@@ -299,6 +315,12 @@ int main(int argc, char *argv[]) {
continue;
}
+ if (do_format_code) {
+ Text_t formatted = format_file(Path$as_c_string(path));
+ print(formatted);
+ continue;
+ }
+
Path_t exe_path = compile_exe ? Path$with_extension(path, Text(""), true)
: build_file(Path$with_extension(path, Text(""), true), "");