From 91b6746fe1315aa9c09936b69cea3892c04c11af Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Mon, 25 Aug 2025 23:53:03 -0400 Subject: Split out formatter into subfiles --- src/formatter/args.c | 55 +++++ src/formatter/args.h | 15 ++ src/formatter/enums.c | 45 ++++ src/formatter/enums.h | 11 + src/formatter/formatter.c | 576 ++++++++++++++++++++++++++++++++++++++++++++++ src/formatter/formatter.h | 26 +++ src/formatter/types.c | 30 +++ src/formatter/types.h | 9 + 8 files changed, 767 insertions(+) create mode 100644 src/formatter/args.c create mode 100644 src/formatter/args.h create mode 100644 src/formatter/enums.c create mode 100644 src/formatter/enums.h create mode 100644 src/formatter/formatter.c create mode 100644 src/formatter/formatter.h create mode 100644 src/formatter/types.c create mode 100644 src/formatter/types.h (limited to 'src/formatter') diff --git a/src/formatter/args.c b/src/formatter/args.c new file mode 100644 index 00000000..9467c9fe --- /dev/null +++ b/src/formatter/args.c @@ -0,0 +1,55 @@ +// Logic for formatting arguments and argument lists + +#include "../ast.h" +#include "../stdlib/datatypes.h" +#include "../stdlib/optionals.h" +#include "../stdlib/text.h" +#include "formatter.h" +#include "types.h" + +OptionalText_t format_inline_arg(arg_ast_t *arg, Table_t comments) { + if (range_has_comment(arg->start, arg->end, comments)) return NONE_TEXT; + if (arg->name == NULL && arg->value) return must(format_inline_code(arg->value, comments)); + Text_t code = Text$from_str(arg->name); + if (arg->type) code = Texts(code, ":", must(format_inline_type(arg->type, comments))); + if (arg->value) code = Texts(code, " = ", must(format_inline_code(arg->value, comments))); + return code; +} + +Text_t format_arg(arg_ast_t *arg, Table_t comments, Text_t indent) { + OptionalText_t inline_arg = format_inline_arg(arg, comments); + if (inline_arg.length >= 0 && inline_arg.length <= MAX_WIDTH) return inline_arg; + if (arg->name == NULL && arg->value) return format_code(arg->value, comments, indent); + Text_t code = Text$from_str(arg->name); + if (arg->type) code = Texts(code, ":", format_type(arg->type, comments, indent)); + if (arg->value) code = Texts(code, " = ", format_code(arg->value, comments, indent)); + return code; +} + +OptionalText_t format_inline_args(arg_ast_t *args, Table_t comments) { + Text_t code = EMPTY_TEXT; + for (; args; args = args->next) { + if (args->name && args->next && args->type == args->next->type && args->value == args->next->value) { + code = Texts(code, Text$from_str(args->name), ","); + } else { + code = Texts(code, must(format_inline_arg(args, comments))); + if (args->next) code = Texts(code, ", "); + } + if (args->next && range_has_comment(args->end, args->next->start, comments)) return NONE_TEXT; + } + return code; +} + +Text_t format_args(arg_ast_t *args, Table_t comments, Text_t indent) { + OptionalText_t inline_args = format_inline_args(args, comments); + if (inline_args.length >= 0 && inline_args.length <= MAX_WIDTH) return inline_args; + Text_t code = EMPTY_TEXT; + for (; args; args = args->next) { + if (args->name && args->next && args->type == args->next->type && args->value == args->next->value) { + code = Texts(code, Text$from_str(args->name), ","); + } else { + add_line(&code, Texts(format_arg(args, comments, indent), ","), indent); + } + } + return code; +} diff --git a/src/formatter/args.h b/src/formatter/args.h new file mode 100644 index 00000000..815069a4 --- /dev/null +++ b/src/formatter/args.h @@ -0,0 +1,15 @@ +// Logic for formatting arguments and argument lists + +#pragma once + +#include "../ast.h" +#include "../stdlib/datatypes.h" + +OptionalText_t format_inline_arg(arg_ast_t *arg, Table_t comments); +Text_t format_arg(arg_ast_t *arg, Table_t comments, Text_t indent); +OptionalText_t format_inline_args(arg_ast_t *args, Table_t comments); +Text_t format_args(arg_ast_t *args, Table_t comments, Text_t indent); +OptionalText_t format_inline_tag(tag_ast_t *tag, Table_t comments); +Text_t format_tag(tag_ast_t *tag, Table_t comments, Text_t indent); +OptionalText_t format_inline_tags(tag_ast_t *tags, Table_t comments); +Text_t format_tags(tag_ast_t *tags, Table_t comments, Text_t indent); diff --git a/src/formatter/enums.c b/src/formatter/enums.c new file mode 100644 index 00000000..430e72ea --- /dev/null +++ b/src/formatter/enums.c @@ -0,0 +1,45 @@ +// Logic for formatting enums and enum tags + +#include "../ast.h" +#include "../stdlib/datatypes.h" +#include "../stdlib/optionals.h" +#include "../stdlib/text.h" +#include "args.h" +#include "formatter.h" +#include "types.h" + +OptionalText_t format_inline_tag(tag_ast_t *tag, Table_t comments) { + if (range_has_comment(tag->start, tag->end, comments)) return NONE_TEXT; + Text_t code = Texts(Text$from_str(tag->name), "(", must(format_inline_args(tag->fields, comments))); + if (tag->secret) code = Texts(code, "; secret"); + return Texts(code, ")"); +} + +Text_t format_tag(tag_ast_t *tag, Table_t comments, Text_t indent) { + OptionalText_t inline_tag = format_inline_tag(tag, comments); + if (inline_tag.length >= 0) return inline_tag; + Text_t code = + Texts(Text$from_str(tag->name), "(", format_args(tag->fields, comments, Texts(indent, single_indent))); + if (tag->secret) code = Texts(code, "; secret"); + return Texts(code, ")"); +} + +OptionalText_t format_inline_tags(tag_ast_t *tags, Table_t comments) { + Text_t code = EMPTY_TEXT; + for (; tags; tags = tags->next) { + code = Texts(code, must(format_inline_tag(tags, comments))); + if (tags->next) code = Texts(code, ", "); + if (tags->next && range_has_comment(tags->end, tags->next->start, comments)) return NONE_TEXT; + } + return code; +} + +Text_t format_tags(tag_ast_t *tags, Table_t comments, Text_t indent) { + OptionalText_t inline_tags = format_inline_tags(tags, comments); + if (inline_tags.length >= 0) return inline_tags; + Text_t code = EMPTY_TEXT; + for (; tags; tags = tags->next) { + add_line(&code, Texts(format_tag(tags, comments, indent), ","), indent); + } + return code; +} diff --git a/src/formatter/enums.h b/src/formatter/enums.h new file mode 100644 index 00000000..e7233df4 --- /dev/null +++ b/src/formatter/enums.h @@ -0,0 +1,11 @@ +// Logic for formatting enums and enum tags + +#pragma once + +#include "../ast.h" +#include "../stdlib/datatypes.h" + +OptionalText_t format_inline_tag(tag_ast_t *tag, Table_t comments); +Text_t format_tag(tag_ast_t *tag, Table_t comments, Text_t indent); +OptionalText_t format_inline_tags(tag_ast_t *tags, Table_t comments); +Text_t format_tags(tag_ast_t *tags, Table_t comments, Text_t indent); diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c new file mode 100644 index 00000000..d2168231 --- /dev/null +++ b/src/formatter/formatter.c @@ -0,0 +1,576 @@ +// This code defines functions for transforming ASTs back into Tomo source text + +#include +#include + +#include "../ast.h" +#include "../parse/context.h" +#include "../parse/files.h" +#include "../parse/utils.h" +#include "../stdlib/datatypes.h" +#include "../stdlib/integers.h" +#include "../stdlib/optionals.h" +#include "../stdlib/stdlib.h" +#include "../stdlib/tables.h" +#include "../stdlib/text.h" +#include "args.h" +#include "enums.h" +#include "formatter.h" +#include "types.h" + +const Text_t single_indent = Text(" "); + +void add_line(Text_t *code, Text_t line, Text_t indent) { + if (code->length == 0) { + *code = line; + } else { + if (line.length > 0) *code = Texts(*code, "\n", indent, line); + else *code = Texts(*code, "\n"); + } +} + +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; +} + +bool range_has_comment(const char *start, const char *end, Table_t comments) { + OptionalText_t comment = next_comment(comments, &start, end); + return (comment.length >= 0); +} + +static bool should_have_blank_line(ast_t *ast) { + switch (ast->tag) { + case If: + case When: + case Repeat: + case While: + case For: + case Block: + case FunctionDef: + case StructDef: + case EnumDef: + case LangDef: + case ConvertDef: + case Extend: return true; + default: return false; + } +} + +static Text_t indent_code(Text_t code) { + if (code.length <= 0) return code; + return Texts(single_indent, Text$replace(code, Text("\n"), Texts("\n", single_indent))); +} + +static Text_t parenthesize(Text_t code, Text_t indent) { + if (Text$has(code, Text("\n"))) return Texts("(\n", indent, indent_code(code), "\n", indent, ")"); + else return Texts("(", code, ")"); +} + +static CONSTFUNC ast_t *unwrap_block(ast_t *ast) { + if (ast == NULL) return NULL; + while (ast->tag == Block && Match(ast, Block)->statements && Match(ast, Block)->statements->next == NULL) { + ast = Match(ast, Block)->statements->ast; + } + return ast; +} + +static Text_t format_namespace(ast_t *namespace, Table_t comments, Text_t indent) { + if (unwrap_block(namespace) == NULL) return EMPTY_TEXT; + return Texts("\n", indent, single_indent, format_code(namespace, comments, Texts(indent, single_indent))); +} + +static CONSTFUNC const char *binop_tomo_operator(ast_e tag) { + switch (tag) { + case Power: return "^"; + case PowerUpdate: return "^="; + case Concat: return "++"; + case ConcatUpdate: return "++="; + case Multiply: return "*"; + case MultiplyUpdate: return "*="; + case Divide: return "/"; + case DivideUpdate: return "/="; + case Mod: return "mod"; + case ModUpdate: return "mod="; + case Mod1: return "mod1"; + case Mod1Update: return "mod1="; + case Plus: return "+"; + case PlusUpdate: return "+="; + case Minus: return "-"; + case MinusUpdate: return "-="; + case LeftShift: return "<<"; + case LeftShiftUpdate: return "<<="; + case RightShift: return ">>"; + case RightShiftUpdate: return ">>="; + case And: return "and"; + case AndUpdate: return "and="; + case Or: return "or"; + case OrUpdate: return "or="; + case Xor: return "xor"; + case XorUpdate: return "xor="; + case Equals: return "=="; + case NotEquals: return "!="; + case LessThan: return "<"; + case LessThanOrEquals: return "<="; + case GreaterThan: return ">"; + case GreaterThanOrEquals: return ">="; + default: return NULL; + } +} + +OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { + if (range_has_comment(ast->start, ast->end, comments)) return NONE_TEXT; + switch (ast->tag) { + case Unknown: fail("Invalid AST"); + case Block: { + ast_list_t *statements = Match(ast, Block)->statements; + if (statements == NULL) return Text("pass"); + else if (statements->next == NULL) return format_inline_code(statements->ast, comments); + else return NONE_TEXT; + } + case StructDef: + case EnumDef: + case LangDef: + case Extend: + case FunctionDef: + case DocTest: return NONE_TEXT; + case If: { + DeclareMatch(if_, ast, If); + + if (if_->else_body == NULL && if_->condition->tag != Declare) { + ast_t *stmt = unwrap_block(if_->body); + switch (stmt->tag) { + case Return: + case Skip: + case Stop: + return Texts(must(format_inline_code(stmt, comments)), " if ", + must(format_inline_code(if_->condition, comments))); + default: break; + } + } + + Text_t code = Texts("if ", must(format_inline_code(if_->condition, comments)), " then ", + must(format_inline_code(if_->body, comments))); + if (if_->else_body) code = Texts(code, " else ", must(format_inline_code(if_->else_body, comments))); + return code; + } + case Repeat: return Texts("repeat ", must(format_inline_code(Match(ast, Repeat)->body, comments))); + case While: { + DeclareMatch(loop, ast, While); + return Texts("while ", must(format_inline_code(loop->condition, comments)), " do ", + must(format_inline_code(loop->body, comments))); + } + case List: + case Set: { + ast_list_t *items = ast->tag == List ? Match(ast, List)->items : Match(ast, Set)->items; + Text_t code = EMPTY_TEXT; + for (ast_list_t *item = items; item; item = item->next) { + code = Texts(code, must(format_inline_code(item->ast, comments))); + if (item->next) code = Texts(code, ", "); + } + return ast->tag == List ? Texts("[", code, "]") : Texts("|", code, "|"); + } + case Declare: { + DeclareMatch(decl, ast, Declare); + Text_t code = must(format_inline_code(decl->var, comments)); + if (decl->type) code = Texts(code, " : ", must(format_inline_type(decl->type, comments))); + if (decl->value) + code = + Texts(code, decl->type ? Text(" = ") : Text(" := "), must(format_inline_code(decl->value, comments))); + return code; + } + case Assign: { + DeclareMatch(assign, ast, Assign); + Text_t code = EMPTY_TEXT; + for (ast_list_t *target = assign->targets; target; target = target->next) { + code = Texts(code, must(format_inline_code(target->ast, comments))); + if (target->next) code = Texts(code, ", "); + } + code = Texts(code, " = "); + for (ast_list_t *value = assign->values; value; value = value->next) { + code = Texts(code, must(format_inline_code(value->ast, comments))); + if (value->next) code = Texts(code, ", "); + } + return code; + } + case Return: { + ast_t *value = Match(ast, Return)->value; + return value ? Texts("return ", must(format_inline_code(value, comments))) : Text("return"); + } + case Optional: { + ast_t *val = Match(ast, Optional)->value; + return Texts(must(format_inline_code(val, comments)), "?"); + } + case NonOptional: { + ast_t *val = Match(ast, NonOptional)->value; + return Texts(must(format_inline_code(val, comments)), "!"); + } + case FieldAccess: { + DeclareMatch(access, ast, FieldAccess); + return Texts(must(format_inline_code(access->fielded, comments)), ".", Text$from_str(access->field)); + } + case Index: { + DeclareMatch(index, ast, Index); + if (index->index) + return Texts(must(format_inline_code(index->indexed, comments)), "[", + must(format_inline_code(index->index, comments)), "]"); + else return Texts(must(format_inline_code(index->indexed, comments)), "[]"); + } + case TextJoin: { + // TODO: choose quotation mark more smartly + Text_t source = Text$from_strn(ast->start, (int64_t)(ast->end - ast->start)); + Text_t quote = Text$to(source, I(1)); + const char *lang = Match(ast, TextJoin)->lang; + Text_t code = lang ? Texts("$", Text$from_str(lang), quote) : quote; + for (ast_list_t *chunk = Match(ast, TextJoin)->children; chunk; chunk = chunk->next) { + if (chunk->ast->tag == TextLiteral) { + Text_t literal = Match(chunk->ast, TextLiteral)->text; + code = Texts(code, Text$slice(Text$quoted(literal, false, quote), I(2), I(-2))); + } else { + code = Texts(code, "$(", must(format_inline_code(chunk->ast, comments)), ")"); + } + } + return Texts(code, quote); + } + case TextLiteral: { + fail("Something went wrong, we shouldn't be formatting text literals directly"); + } + case Stop: { + const char *target = Match(ast, Stop)->target; + return target ? Texts("stop ", Text$from_str(target)) : Text("stop"); + } + case Skip: { + const char *target = Match(ast, Skip)->target; + return target ? Texts("skip ", Text$from_str(target)) : Text("skip"); + } + case None: + case Bool: + case Int: + case Num: + case Var: { + Text_t code = Text$from_strn(ast->start, (int64_t)(ast->end - ast->start)); + if (Text$has(code, Text("\n"))) return NONE_TEXT; + return code; + } + case FunctionCall: { + DeclareMatch(call, ast, FunctionCall); + return Texts(must(format_inline_code(call->fn, comments)), "(", must(format_inline_args(call->args, comments)), + ")"); + } + case BINOP_CASES: { + binary_operands_t operands = BINARY_OPERANDS(ast); + const char *op = binop_tomo_operator(ast->tag); + + Text_t lhs = must(format_inline_code(operands.lhs, comments)); + Text_t rhs = must(format_inline_code(operands.rhs, comments)); + + if (is_update_assignment(ast)) { + return Texts(lhs, " ", Text$from_str(op), " ", rhs); + } + + if (is_binary_operation(operands.lhs) && op_tightness[operands.lhs->tag] < op_tightness[ast->tag]) + lhs = parenthesize(lhs, EMPTY_TEXT); + if (is_binary_operation(operands.rhs) && op_tightness[operands.rhs->tag] < op_tightness[ast->tag]) + rhs = parenthesize(rhs, EMPTY_TEXT); + + Text_t space = op_tightness[ast->tag] >= op_tightness[Multiply] ? EMPTY_TEXT : Text(" "); + return Texts(lhs, space, Text$from_str(binop_tomo_operator(ast->tag)), space, rhs); + } + default: { + fail("Formatting not implemented for: ", ast_to_sexp(ast)); + } + } +} + +Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { + OptionalText_t inlined = format_inline_code(ast, comments); + bool inlined_fits = (inlined.length >= 0 && indent.length + inlined.length <= MAX_WIDTH); + + switch (ast->tag) { + case Unknown: fail("Invalid AST"); + case Block: { + Text_t code = EMPTY_TEXT; + bool gap_before_comment = false; + const char *comment_pos = ast->start; + for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) { + if (should_have_blank_line(stmt->ast)) add_line(&code, Text(""), indent); + + for (OptionalText_t comment; + (comment = next_comment(comments, &comment_pos, stmt->ast->start)).length > 0;) { + if (gap_before_comment) { + add_line(&code, Text(""), indent); + gap_before_comment = false; + } + add_line(&code, Text$trim(comment, Text(" \t\r\n"), false, true), indent); + } + + add_line(&code, format_code(stmt->ast, comments, indent), indent); + comment_pos = stmt->ast->end; + + if (should_have_blank_line(stmt->ast) && stmt->next && !should_have_blank_line(stmt->next->ast)) + add_line(&code, Text(""), indent); + else gap_before_comment = true; + } + + for (OptionalText_t comment; (comment = next_comment(comments, &comment_pos, ast->end)).length > 0;) { + if (gap_before_comment) { + add_line(&code, Text(""), indent); + gap_before_comment = false; + } + add_line(&code, Text$trim(comment, Text(" \t\r\n"), false, true), indent); + } + return code; + } + case If: { + DeclareMatch(if_, ast, If); + if (inlined_fits && if_->else_body == NULL) return inlined; + + Text_t code = Texts("if ", format_code(if_->condition, comments, indent), "\n", indent, single_indent, + format_code(if_->body, comments, Texts(indent, single_indent))); + if (if_->else_body) + code = Texts(code, "\n", indent, "else \n", indent, single_indent, + format_code(if_->else_body, comments, Texts(indent, single_indent))); + return code; + } + case Repeat: { + return Texts("repeat\n", indent, single_indent, + format_code(Match(ast, Repeat)->body, comments, Texts(indent, single_indent))); + } + case While: { + DeclareMatch(loop, ast, While); + return Texts("while ", format_code(loop->condition, comments, indent), "\n", indent, single_indent, + format_code(loop->body, comments, Texts(indent, single_indent))); + } + case FunctionDef: { + DeclareMatch(func, ast, FunctionDef); + // ast_t *name; + // arg_ast_t *args; + // type_ast_t *ret_type; + // ast_t *body; + // ast_t *cache; + // bool is_inline; + Text_t code = + Texts("func ", format_code(func->name, comments, indent), "(", format_args(func->args, comments, indent)); + if (func->ret_type) + code = Texts(code, func->args ? Text(" -> ") : Text("-> "), format_type(func->ret_type, comments, indent)); + if (func->cache) code = Texts(code, "; cache=", format_code(func->cache, comments, indent)); + if (func->is_inline) code = Texts(code, "; inline"); + code = + Texts(code, ")\n", indent, single_indent, format_code(func->body, comments, Texts(indent, single_indent))); + return Texts(code); + } + case StructDef: { + DeclareMatch(def, ast, StructDef); + Text_t code = Texts("struct ", Text$from_str(def->name), "(", format_args(def->fields, comments, indent)); + if (def->secret) code = Texts(code, "; secret"); + if (def->external) code = Texts(code, "; external"); + if (def->opaque) code = Texts(code, "; opaque"); + return Texts(code, ")", format_namespace(def->namespace, comments, indent)); + } + case EnumDef: { + DeclareMatch(def, ast, EnumDef); + Text_t code = Texts("enum ", Text$from_str(def->name), "(", format_tags(def->tags, comments, indent)); + return Texts(code, ")", format_namespace(def->namespace, comments, indent)); + } + case LangDef: { + DeclareMatch(def, ast, LangDef); + return Texts("lang ", Text$from_str(def->name), format_namespace(def->namespace, comments, indent)); + } + case Extend: { + DeclareMatch(extend, ast, Extend); + return Texts("lang ", Text$from_str(extend->name), format_namespace(extend->body, comments, indent)); + } + case List: + case Set: { + if (inlined_fits) return inlined; + ast_list_t *items = ast->tag == List ? Match(ast, List)->items : Match(ast, Set)->items; + Text_t code = EMPTY_TEXT; + const char *comment_pos = ast->start; + for (ast_list_t *item = items; item; item = item->next) { + for (OptionalText_t comment; + (comment = next_comment(comments, &comment_pos, item->ast->start)).length > 0;) { + add_line(&code, Text$trim(comment, Text(" \t\r\n"), false, true), Texts(indent, single_indent)); + } + add_line(&code, Texts(format_code(item->ast, comments, Texts(indent, single_indent)), ","), + Texts(indent, single_indent)); + } + for (OptionalText_t comment; (comment = next_comment(comments, &comment_pos, ast->end)).length > 0;) { + add_line(&code, Text$trim(comment, Text(" \t\r\n"), false, true), Texts(indent, single_indent)); + } + return ast->tag == List ? Texts("[\n", indent, single_indent, code, "\n", indent, "]") + : Texts("|\n", indent, single_indent, code, "\n", indent, "|"); + } + case Declare: { + DeclareMatch(decl, ast, Declare); + Text_t code = format_code(decl->var, comments, indent); + if (decl->type) code = Texts(code, " : ", format_type(decl->type, comments, indent)); + if (decl->value) + code = Texts(code, decl->type ? Text(" = ") : Text(" := "), format_code(decl->value, comments, indent)); + return code; + } + case Assign: { + DeclareMatch(assign, ast, Assign); + Text_t code = EMPTY_TEXT; + for (ast_list_t *target = assign->targets; target; target = target->next) { + code = Texts(code, format_code(target->ast, comments, indent)); + if (target->next) code = Texts(code, ", "); + } + code = Texts(code, " = "); + for (ast_list_t *value = assign->values; value; value = value->next) { + code = Texts(code, format_code(value->ast, comments, indent)); + if (value->next) code = Texts(code, ", "); + } + return code; + } + case Return: { + ast_t *value = Match(ast, Return)->value; + return value ? Texts("return ", format_code(value, comments, indent)) : Text("return"); + } + case Optional: { + if (inlined_fits) return inlined; + ast_t *val = Match(ast, Optional)->value; + return Texts("(", format_code(val, comments, indent), ")?"); + } + case NonOptional: { + if (inlined_fits) return inlined; + ast_t *val = Match(ast, NonOptional)->value; + return Texts("(", format_code(val, comments, indent), ")!"); + } + case FieldAccess: { + DeclareMatch(access, ast, FieldAccess); + return Texts(format_code(access->fielded, comments, indent), ".", Text$from_str(access->field)); + } + case Index: { + DeclareMatch(index, ast, Index); + if (index->index) + return Texts(format_code(index->indexed, comments, indent), "[", + format_code(index->index, comments, indent), "]"); + else return Texts(format_code(index->indexed, comments, indent), "[]"); + } + case TextJoin: { + if (inlined_fits) return inlined; + // TODO: choose quotation mark more smartly + Text_t source = Text$from_strn(ast->start, (int64_t)(ast->end - ast->start)); + Text_t quote = Text$to(source, I(1)); + const char *lang = Match(ast, TextJoin)->lang; + Text_t code = EMPTY_TEXT; + Text_t current_line = EMPTY_TEXT; + for (ast_list_t *chunk = Match(ast, TextJoin)->children; chunk; chunk = chunk->next) { + if (chunk->ast->tag == TextLiteral) { + Text_t literal = Match(chunk->ast, TextLiteral)->text; + List_t lines = Text$lines(literal); + if (lines.length == 0) continue; + current_line = Texts(current_line, *(Text_t *)lines.data); + for (int64_t i = 1; i < lines.length; i += 1) { + add_line(&code, current_line, Texts(indent, single_indent)); + current_line = *(Text_t *)(lines.data + i * lines.stride); + } + } else { + code = Texts(code, "$(", must(format_inline_code(chunk->ast, comments)), ")"); + } + } + add_line(&code, current_line, Texts(indent, single_indent)); + code = Texts(quote, "\n", indent, single_indent, code, "\n", indent, quote); + if (lang) code = Texts("$", Text$from_str(lang), code); + return code; + } + case TextLiteral: { + fail("Something went wrong, we shouldn't be formatting text literals directly"); + } + case Stop: + case Skip: + case None: + case Bool: + case Int: + case Num: + case Var: { + assert(inlined.length >= 0); + return inlined; + } + case FunctionCall: { + if (inlined_fits) return inlined; + DeclareMatch(call, ast, FunctionCall); + return Texts(format_code(call->fn, comments, indent), "(\n", indent, single_indent, + format_args(call->args, comments, Texts(indent, single_indent)), "\n", indent, ")"); + } + case DocTest: { + DeclareMatch(test, ast, DocTest); + Text_t expr = format_code(test->expr, comments, indent); + Text_t code = Texts(">> ", Text$replace(expr, Texts("\n", indent), Texts("\n", indent, ".. "))); + if (test->expected) { + Text_t expected = format_code(test->expected, comments, indent); + code = Texts(code, "\n", indent, "= ", + Text$replace(expected, Texts("\n", indent), Texts("\n", indent, ".. "))); + } + return code; + } + case BINOP_CASES: { + if (inlined_fits) return inlined; + binary_operands_t operands = BINARY_OPERANDS(ast); + const char *op = binop_tomo_operator(ast->tag); + Text_t lhs = format_code(operands.lhs, comments, indent); + Text_t rhs = format_code(operands.rhs, comments, indent); + + if (is_update_assignment(ast)) { + return Texts(lhs, " ", Text$from_str(op), " ", rhs); + } + + if (is_binary_operation(operands.lhs) && op_tightness[operands.lhs->tag] < op_tightness[ast->tag]) + lhs = parenthesize(lhs, indent); + if (is_binary_operation(operands.rhs) && op_tightness[operands.rhs->tag] < op_tightness[ast->tag]) + rhs = parenthesize(rhs, indent); + + Text_t space = op_tightness[ast->tag] >= op_tightness[Multiply] ? EMPTY_TEXT : Text(" "); + return Texts(lhs, space, Text$from_str(binop_tomo_operator(ast->tag)), space, rhs); + } + default: { + if (inlined_fits) return inlined; + fail("Formatting not implemented for: ", ast_to_sexp(ast)); + } + } +} + +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, Text$trim(comment, Text(" \t\r\n"), false, true), "\n"); + } + code = Texts(code, format_code(ast, ctx.comments, EMPTY_TEXT)); + for (OptionalText_t comment; (comment = next_comment(ctx.comments, &fmt_pos, ast->start)).length > 0;) { + code = Texts(code, Text$trim(comment, Text(" \t\r\n"), false, true), "\n"); + } + return code; +} diff --git a/src/formatter/formatter.h b/src/formatter/formatter.h new file mode 100644 index 00000000..83bb9e56 --- /dev/null +++ b/src/formatter/formatter.h @@ -0,0 +1,26 @@ +// This code defines functions for transforming ASTs back into Tomo source text + +#pragma once + +#include "../ast.h" +#include "../stdlib/datatypes.h" + +#define MAX_WIDTH 100 + +#define must(expr) \ + ({ \ + OptionalText_t _expr = expr; \ + if (_expr.length < 0) return NONE_TEXT; \ + (Text_t) _expr; \ + }) + +extern const Text_t single_indent; + +Text_t format_file(const char *path); +Text_t format_code(ast_t *ast, Table_t comments, Text_t indentation); +OptionalText_t format_inline_code(ast_t *ast, Table_t comments); + +OptionalText_t next_comment(Table_t comments, const char **pos, const char *end); +bool range_has_comment(const char *start, const char *end, Table_t comments); +OptionalText_t next_comment(Table_t comments, const char **pos, const char *end); +void add_line(Text_t *code, Text_t line, Text_t indent); diff --git a/src/formatter/types.c b/src/formatter/types.c new file mode 100644 index 00000000..282a299f --- /dev/null +++ b/src/formatter/types.c @@ -0,0 +1,30 @@ +// Logic for formatting types + +#include "../ast.h" +#include "../stdlib/datatypes.h" +#include "../stdlib/optionals.h" +#include "../stdlib/text.h" +#include "formatter.h" + +OptionalText_t format_inline_type(type_ast_t *type, Table_t comments) { + if (range_has_comment(type->start, type->end, comments)) return NONE_TEXT; + switch (type->tag) { + default: { + Text_t code = Text$from_strn(type->start, (int64_t)(type->end - type->start)); + if (Text$has(code, Text("\n"))) return NONE_TEXT; + return Text$replace(code, Text("\t"), single_indent); + } + } +} + +Text_t format_type(type_ast_t *type, Table_t comments, Text_t indent) { + (void)comments, (void)indent; + switch (type->tag) { + default: { + OptionalText_t inline_type = format_inline_type(type, comments); + if (inline_type.length >= 0) return inline_type; + Text_t code = Text$from_strn(type->start, (int64_t)(type->end - type->start)); + return Text$replace(code, Text("\t"), single_indent); + } + } +} diff --git a/src/formatter/types.h b/src/formatter/types.h new file mode 100644 index 00000000..1f0698b3 --- /dev/null +++ b/src/formatter/types.h @@ -0,0 +1,9 @@ +// Logic for formatting types + +#pragma once + +#include "../ast.h" +#include "../stdlib/datatypes.h" + +OptionalText_t format_inline_type(type_ast_t *type, Table_t comments); +Text_t format_type(type_ast_t *type, Table_t comments, Text_t indent); -- cgit v1.2.3 From 978835e3dd8dd59a1efaa869f2f603eb9eea5a3f Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Mon, 25 Aug 2025 23:59:09 -0400 Subject: Split out utility functions --- src/formatter/args.c | 1 + src/formatter/enums.c | 3 +- src/formatter/formatter.c | 107 ++----------------------------------------- src/formatter/formatter.h | 19 ++------ src/formatter/types.c | 2 +- src/formatter/utils.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++ src/formatter/utils.h | 28 ++++++++++++ 7 files changed, 150 insertions(+), 122 deletions(-) create mode 100644 src/formatter/utils.c create mode 100644 src/formatter/utils.h (limited to 'src/formatter') diff --git a/src/formatter/args.c b/src/formatter/args.c index 9467c9fe..2bd115cd 100644 --- a/src/formatter/args.c +++ b/src/formatter/args.c @@ -6,6 +6,7 @@ #include "../stdlib/text.h" #include "formatter.h" #include "types.h" +#include "utils.h" OptionalText_t format_inline_arg(arg_ast_t *arg, Table_t comments) { if (range_has_comment(arg->start, arg->end, comments)) return NONE_TEXT; diff --git a/src/formatter/enums.c b/src/formatter/enums.c index 430e72ea..7a89705d 100644 --- a/src/formatter/enums.c +++ b/src/formatter/enums.c @@ -5,8 +5,7 @@ #include "../stdlib/optionals.h" #include "../stdlib/text.h" #include "args.h" -#include "formatter.h" -#include "types.h" +#include "utils.h" OptionalText_t format_inline_tag(tag_ast_t *tag, Table_t comments) { if (range_has_comment(tag->start, tag->end, comments)) return NONE_TEXT; diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c index d2168231..e4c92542 100644 --- a/src/formatter/formatter.c +++ b/src/formatter/formatter.c @@ -2,6 +2,8 @@ #include #include +#include +#include #include "../ast.h" #include "../parse/context.h" @@ -11,119 +13,18 @@ #include "../stdlib/integers.h" #include "../stdlib/optionals.h" #include "../stdlib/stdlib.h" -#include "../stdlib/tables.h" #include "../stdlib/text.h" #include "args.h" #include "enums.h" #include "formatter.h" #include "types.h" +#include "utils.h" -const Text_t single_indent = Text(" "); - -void add_line(Text_t *code, Text_t line, Text_t indent) { - if (code->length == 0) { - *code = line; - } else { - if (line.length > 0) *code = Texts(*code, "\n", indent, line); - else *code = Texts(*code, "\n"); - } -} - -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; -} - -bool range_has_comment(const char *start, const char *end, Table_t comments) { - OptionalText_t comment = next_comment(comments, &start, end); - return (comment.length >= 0); -} - -static bool should_have_blank_line(ast_t *ast) { - switch (ast->tag) { - case If: - case When: - case Repeat: - case While: - case For: - case Block: - case FunctionDef: - case StructDef: - case EnumDef: - case LangDef: - case ConvertDef: - case Extend: return true; - default: return false; - } -} - -static Text_t indent_code(Text_t code) { - if (code.length <= 0) return code; - return Texts(single_indent, Text$replace(code, Text("\n"), Texts("\n", single_indent))); -} - -static Text_t parenthesize(Text_t code, Text_t indent) { - if (Text$has(code, Text("\n"))) return Texts("(\n", indent, indent_code(code), "\n", indent, ")"); - else return Texts("(", code, ")"); -} - -static CONSTFUNC ast_t *unwrap_block(ast_t *ast) { - if (ast == NULL) return NULL; - while (ast->tag == Block && Match(ast, Block)->statements && Match(ast, Block)->statements->next == NULL) { - ast = Match(ast, Block)->statements->ast; - } - return ast; -} - -static Text_t format_namespace(ast_t *namespace, Table_t comments, Text_t indent) { +Text_t format_namespace(ast_t *namespace, Table_t comments, Text_t indent) { if (unwrap_block(namespace) == NULL) return EMPTY_TEXT; return Texts("\n", indent, single_indent, format_code(namespace, comments, Texts(indent, single_indent))); } -static CONSTFUNC const char *binop_tomo_operator(ast_e tag) { - switch (tag) { - case Power: return "^"; - case PowerUpdate: return "^="; - case Concat: return "++"; - case ConcatUpdate: return "++="; - case Multiply: return "*"; - case MultiplyUpdate: return "*="; - case Divide: return "/"; - case DivideUpdate: return "/="; - case Mod: return "mod"; - case ModUpdate: return "mod="; - case Mod1: return "mod1"; - case Mod1Update: return "mod1="; - case Plus: return "+"; - case PlusUpdate: return "+="; - case Minus: return "-"; - case MinusUpdate: return "-="; - case LeftShift: return "<<"; - case LeftShiftUpdate: return "<<="; - case RightShift: return ">>"; - case RightShiftUpdate: return ">>="; - case And: return "and"; - case AndUpdate: return "and="; - case Or: return "or"; - case OrUpdate: return "or="; - case Xor: return "xor"; - case XorUpdate: return "xor="; - case Equals: return "=="; - case NotEquals: return "!="; - case LessThan: return "<"; - case LessThanOrEquals: return "<="; - case GreaterThan: return ">"; - case GreaterThanOrEquals: return ">="; - default: return NULL; - } -} - OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { if (range_has_comment(ast->start, ast->end, comments)) return NONE_TEXT; switch (ast->tag) { diff --git a/src/formatter/formatter.h b/src/formatter/formatter.h index 83bb9e56..a8f9013a 100644 --- a/src/formatter/formatter.h +++ b/src/formatter/formatter.h @@ -2,25 +2,12 @@ #pragma once +#include + #include "../ast.h" #include "../stdlib/datatypes.h" -#define MAX_WIDTH 100 - -#define must(expr) \ - ({ \ - OptionalText_t _expr = expr; \ - if (_expr.length < 0) return NONE_TEXT; \ - (Text_t) _expr; \ - }) - -extern const Text_t single_indent; - Text_t format_file(const char *path); Text_t format_code(ast_t *ast, Table_t comments, Text_t indentation); +Text_t format_namespace(ast_t *namespace, Table_t comments, Text_t indent); OptionalText_t format_inline_code(ast_t *ast, Table_t comments); - -OptionalText_t next_comment(Table_t comments, const char **pos, const char *end); -bool range_has_comment(const char *start, const char *end, Table_t comments); -OptionalText_t next_comment(Table_t comments, const char **pos, const char *end); -void add_line(Text_t *code, Text_t line, Text_t indent); diff --git a/src/formatter/types.c b/src/formatter/types.c index 282a299f..34128b0b 100644 --- a/src/formatter/types.c +++ b/src/formatter/types.c @@ -4,7 +4,7 @@ #include "../stdlib/datatypes.h" #include "../stdlib/optionals.h" #include "../stdlib/text.h" -#include "formatter.h" +#include "utils.h" OptionalText_t format_inline_type(type_ast_t *type, Table_t comments) { if (range_has_comment(type->start, type->end, comments)) return NONE_TEXT; diff --git a/src/formatter/utils.c b/src/formatter/utils.c new file mode 100644 index 00000000..472829d9 --- /dev/null +++ b/src/formatter/utils.c @@ -0,0 +1,112 @@ +// This file defines utility functions for autoformatting code + +#include +#include + +#include "../ast.h" +#include "../parse/context.h" +#include "../stdlib/datatypes.h" +#include "../stdlib/optionals.h" +#include "../stdlib/tables.h" +#include "../stdlib/text.h" + +const Text_t single_indent = Text(" "); + +void add_line(Text_t *code, Text_t line, Text_t indent) { + if (code->length == 0) { + *code = line; + } else { + if (line.length > 0) *code = Texts(*code, "\n", indent, line); + else *code = Texts(*code, "\n"); + } +} + +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; +} + +bool range_has_comment(const char *start, const char *end, Table_t comments) { + OptionalText_t comment = next_comment(comments, &start, end); + return (comment.length >= 0); +} + +CONSTFUNC bool should_have_blank_line(ast_t *ast) { + switch (ast->tag) { + case If: + case When: + case Repeat: + case While: + case For: + case Block: + case FunctionDef: + case StructDef: + case EnumDef: + case LangDef: + case ConvertDef: + case Extend: return true; + default: return false; + } +} + +Text_t indent_code(Text_t code) { + if (code.length <= 0) return code; + return Texts(single_indent, Text$replace(code, Text("\n"), Texts("\n", single_indent))); +} + +Text_t parenthesize(Text_t code, Text_t indent) { + if (Text$has(code, Text("\n"))) return Texts("(\n", indent, indent_code(code), "\n", indent, ")"); + else return Texts("(", code, ")"); +} + +CONSTFUNC ast_t *unwrap_block(ast_t *ast) { + if (ast == NULL) return NULL; + while (ast->tag == Block && Match(ast, Block)->statements && Match(ast, Block)->statements->next == NULL) { + ast = Match(ast, Block)->statements->ast; + } + return ast; +} + +CONSTFUNC const char *binop_tomo_operator(ast_e tag) { + switch (tag) { + case Power: return "^"; + case PowerUpdate: return "^="; + case Concat: return "++"; + case ConcatUpdate: return "++="; + case Multiply: return "*"; + case MultiplyUpdate: return "*="; + case Divide: return "/"; + case DivideUpdate: return "/="; + case Mod: return "mod"; + case ModUpdate: return "mod="; + case Mod1: return "mod1"; + case Mod1Update: return "mod1="; + case Plus: return "+"; + case PlusUpdate: return "+="; + case Minus: return "-"; + case MinusUpdate: return "-="; + case LeftShift: return "<<"; + case LeftShiftUpdate: return "<<="; + case RightShift: return ">>"; + case RightShiftUpdate: return ">>="; + case And: return "and"; + case AndUpdate: return "and="; + case Or: return "or"; + case OrUpdate: return "or="; + case Xor: return "xor"; + case XorUpdate: return "xor="; + case Equals: return "=="; + case NotEquals: return "!="; + case LessThan: return "<"; + case LessThanOrEquals: return "<="; + case GreaterThan: return ">"; + case GreaterThanOrEquals: return ">="; + default: return NULL; + } +} diff --git a/src/formatter/utils.h b/src/formatter/utils.h new file mode 100644 index 00000000..a8def627 --- /dev/null +++ b/src/formatter/utils.h @@ -0,0 +1,28 @@ +// This file defines utility functions for autoformatting code + +#pragma once + +#include + +#include "../ast.h" +#include "../stdlib/datatypes.h" + +#define MAX_WIDTH 100 + +#define must(expr) \ + ({ \ + OptionalText_t _expr = expr; \ + if (_expr.length < 0) return NONE_TEXT; \ + (Text_t) _expr; \ + }) + +extern const Text_t single_indent; + +void add_line(Text_t *code, Text_t line, Text_t indent); +OptionalText_t next_comment(Table_t comments, const char **pos, const char *end); +bool range_has_comment(const char *start, const char *end, Table_t comments); +CONSTFUNC bool should_have_blank_line(ast_t *ast); +Text_t indent_code(Text_t code); +Text_t parenthesize(Text_t code, Text_t indent); +CONSTFUNC ast_t *unwrap_block(ast_t *ast); +CONSTFUNC const char *binop_tomo_operator(ast_e tag); -- cgit v1.2.3 From f192834a6405d9a568782b637943588c6af7a8de Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 26 Aug 2025 00:32:13 -0400 Subject: Add more formatting --- src/formatter/args.h | 2 - src/formatter/formatter.c | 376 ++++++++++++++++++++++++++++++---------------- 2 files changed, 250 insertions(+), 128 deletions(-) (limited to 'src/formatter') diff --git a/src/formatter/args.h b/src/formatter/args.h index 815069a4..42722f32 100644 --- a/src/formatter/args.h +++ b/src/formatter/args.h @@ -11,5 +11,3 @@ OptionalText_t format_inline_args(arg_ast_t *args, Table_t comments); Text_t format_args(arg_ast_t *args, Table_t comments, Text_t indent); OptionalText_t format_inline_tag(tag_ast_t *tag, Table_t comments); Text_t format_tag(tag_ast_t *tag, Table_t comments, Text_t indent); -OptionalText_t format_inline_tags(tag_ast_t *tags, Table_t comments); -Text_t format_tags(tag_ast_t *tags, Table_t comments, Text_t indent); diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c index e4c92542..b0fad38a 100644 --- a/src/formatter/formatter.c +++ b/src/formatter/formatter.c @@ -20,28 +20,33 @@ #include "types.h" #include "utils.h" +#define fmt_inline(...) must(format_inline_code(__VA_ARGS__)) +#define fmt(...) format_code(__VA_ARGS__) + Text_t format_namespace(ast_t *namespace, Table_t comments, Text_t indent) { if (unwrap_block(namespace) == NULL) return EMPTY_TEXT; - return Texts("\n", indent, single_indent, format_code(namespace, comments, Texts(indent, single_indent))); + return Texts("\n", indent, single_indent, fmt(namespace, comments, Texts(indent, single_indent))); } OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { if (range_has_comment(ast->start, ast->end, comments)) return NONE_TEXT; switch (ast->tag) { - case Unknown: fail("Invalid AST"); - case Block: { + /*inline*/ case Unknown: + fail("Invalid AST"); + /*inline*/ case Block: { ast_list_t *statements = Match(ast, Block)->statements; if (statements == NULL) return Text("pass"); - else if (statements->next == NULL) return format_inline_code(statements->ast, comments); + else if (statements->next == NULL) return fmt_inline(statements->ast, comments); else return NONE_TEXT; } - case StructDef: - case EnumDef: - case LangDef: - case Extend: - case FunctionDef: - case DocTest: return NONE_TEXT; - case If: { + /*inline*/ case StructDef: + /*inline*/ case EnumDef: + /*inline*/ case LangDef: + /*inline*/ case Extend: + /*inline*/ case FunctionDef: + /*inline*/ case DocTest: + return NONE_TEXT; + /*inline*/ case If: { DeclareMatch(if_, ast, If); if (if_->else_body == NULL && if_->condition->tag != Declare) { @@ -49,81 +54,122 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { switch (stmt->tag) { case Return: case Skip: - case Stop: - return Texts(must(format_inline_code(stmt, comments)), " if ", - must(format_inline_code(if_->condition, comments))); + case Stop: return Texts(fmt_inline(stmt, comments), " if ", fmt_inline(if_->condition, comments)); default: break; } } - Text_t code = Texts("if ", must(format_inline_code(if_->condition, comments)), " then ", - must(format_inline_code(if_->body, comments))); - if (if_->else_body) code = Texts(code, " else ", must(format_inline_code(if_->else_body, comments))); + Text_t code = Texts("if ", fmt_inline(if_->condition, comments), " then ", fmt_inline(if_->body, comments)); + if (if_->else_body) code = Texts(code, " else ", fmt_inline(if_->else_body, comments)); return code; } - case Repeat: return Texts("repeat ", must(format_inline_code(Match(ast, Repeat)->body, comments))); - case While: { + /*inline*/ case Repeat: + return Texts("repeat ", fmt_inline(Match(ast, Repeat)->body, comments)); + /*inline*/ case While: { DeclareMatch(loop, ast, While); - return Texts("while ", must(format_inline_code(loop->condition, comments)), " do ", - must(format_inline_code(loop->body, comments))); + return Texts("while ", fmt_inline(loop->condition, comments), " do ", fmt_inline(loop->body, comments)); + } + /*inline*/ case For: { + DeclareMatch(loop, ast, For); + Text_t code = Text("for "); + for (ast_list_t *var = loop->vars; var; var = var->next) { + code = Texts(code, fmt_inline(var->ast, comments)); + if (var->next) code = Texts(code, ", "); + } + code = Texts(code, " in ", fmt_inline(loop->iter, comments), " do ", fmt_inline(loop->body, comments)); + if (loop->empty) code = Texts(code, " else ", fmt_inline(loop->empty, comments)); + return code; } - case List: - case Set: { + /*inline*/ case Comprehension: { + DeclareMatch(comp, ast, Comprehension); + Text_t code = Texts(fmt_inline(comp->expr, comments), " for "); + for (ast_list_t *var = comp->vars; var; var = var->next) { + code = Texts(code, fmt_inline(var->ast, comments)); + if (var->next) code = Texts(code, ", "); + } + code = Texts(code, " in ", fmt_inline(comp->iter, comments)); + if (comp->filter) code = Texts(code, " if ", fmt_inline(comp->filter, comments)); + return code; + } + /*inline*/ case List: + /*inline*/ case Set: { ast_list_t *items = ast->tag == List ? Match(ast, List)->items : Match(ast, Set)->items; Text_t code = EMPTY_TEXT; for (ast_list_t *item = items; item; item = item->next) { - code = Texts(code, must(format_inline_code(item->ast, comments))); + code = Texts(code, fmt_inline(item->ast, comments)); if (item->next) code = Texts(code, ", "); } return ast->tag == List ? Texts("[", code, "]") : Texts("|", code, "|"); } - case Declare: { + /*inline*/ case Table: { + DeclareMatch(table, ast, Table); + Text_t code = EMPTY_TEXT; + for (ast_list_t *entry = table->entries; entry; entry = entry->next) { + code = Texts(code, fmt_inline(entry->ast, comments)); + if (entry->next) code = Texts(code, ", "); + } + if (table->fallback) code = Texts(code, "; fallback=", fmt_inline(table->fallback, comments)); + if (table->default_value) code = Texts(code, "; default=", fmt_inline(table->default_value, comments)); + return Texts("{", code, "}"); + } + /*inline*/ case TableEntry: { + DeclareMatch(entry, ast, TableEntry); + return Texts(fmt_inline(entry->key, comments), "=", fmt_inline(entry->value, comments)); + } + /*inline*/ case Declare: { DeclareMatch(decl, ast, Declare); - Text_t code = must(format_inline_code(decl->var, comments)); + Text_t code = fmt_inline(decl->var, comments); if (decl->type) code = Texts(code, " : ", must(format_inline_type(decl->type, comments))); - if (decl->value) - code = - Texts(code, decl->type ? Text(" = ") : Text(" := "), must(format_inline_code(decl->value, comments))); + if (decl->value) code = Texts(code, decl->type ? Text(" = ") : Text(" := "), fmt_inline(decl->value, comments)); return code; } - case Assign: { + /*inline*/ case Assign: { DeclareMatch(assign, ast, Assign); Text_t code = EMPTY_TEXT; for (ast_list_t *target = assign->targets; target; target = target->next) { - code = Texts(code, must(format_inline_code(target->ast, comments))); + code = Texts(code, fmt_inline(target->ast, comments)); if (target->next) code = Texts(code, ", "); } code = Texts(code, " = "); for (ast_list_t *value = assign->values; value; value = value->next) { - code = Texts(code, must(format_inline_code(value->ast, comments))); + code = Texts(code, fmt_inline(value->ast, comments)); if (value->next) code = Texts(code, ", "); } return code; } - case Return: { + /*inline*/ case Pass: + return Text("pass"); + /*inline*/ case Return: { ast_t *value = Match(ast, Return)->value; - return value ? Texts("return ", must(format_inline_code(value, comments))) : Text("return"); + return value ? Texts("return ", fmt_inline(value, comments)) : Text("return"); + } + /*inline*/ case HeapAllocate: { + ast_t *val = Match(ast, HeapAllocate)->value; + return Texts("@", fmt_inline(val, comments)); + } + /*inline*/ case StackReference: { + ast_t *val = Match(ast, StackReference)->value; + return Texts("&", fmt_inline(val, comments)); } - case Optional: { + /*inline*/ case Optional: { ast_t *val = Match(ast, Optional)->value; - return Texts(must(format_inline_code(val, comments)), "?"); + return Texts(fmt_inline(val, comments), "?"); } - case NonOptional: { + /*inline*/ case NonOptional: { ast_t *val = Match(ast, NonOptional)->value; - return Texts(must(format_inline_code(val, comments)), "!"); + return Texts(fmt_inline(val, comments), "!"); } - case FieldAccess: { + /*inline*/ case FieldAccess: { DeclareMatch(access, ast, FieldAccess); - return Texts(must(format_inline_code(access->fielded, comments)), ".", Text$from_str(access->field)); + return Texts(fmt_inline(access->fielded, comments), ".", Text$from_str(access->field)); } - case Index: { + /*inline*/ case Index: { DeclareMatch(index, ast, Index); if (index->index) - return Texts(must(format_inline_code(index->indexed, comments)), "[", - must(format_inline_code(index->index, comments)), "]"); - else return Texts(must(format_inline_code(index->indexed, comments)), "[]"); + return Texts(fmt_inline(index->indexed, comments), "[", fmt_inline(index->index, comments), "]"); + else return Texts(fmt_inline(index->indexed, comments), "[]"); } - case TextJoin: { + /*inline*/ case TextJoin: { // TODO: choose quotation mark more smartly Text_t source = Text$from_strn(ast->start, (int64_t)(ast->end - ast->start)); Text_t quote = Text$to(source, I(1)); @@ -134,42 +180,44 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { Text_t literal = Match(chunk->ast, TextLiteral)->text; code = Texts(code, Text$slice(Text$quoted(literal, false, quote), I(2), I(-2))); } else { - code = Texts(code, "$(", must(format_inline_code(chunk->ast, comments)), ")"); + code = Texts(code, "$(", fmt_inline(chunk->ast, comments), ")"); } } return Texts(code, quote); } - case TextLiteral: { - fail("Something went wrong, we shouldn't be formatting text literals directly"); - } - case Stop: { + /*inline*/ case TextLiteral: { fail("Something went wrong, we shouldn't be formatting text literals directly"); } + /*inline*/ case Stop: { const char *target = Match(ast, Stop)->target; return target ? Texts("stop ", Text$from_str(target)) : Text("stop"); } - case Skip: { + /*inline*/ case Skip: { const char *target = Match(ast, Skip)->target; return target ? Texts("skip ", Text$from_str(target)) : Text("skip"); } - case None: - case Bool: - case Int: - case Num: - case Var: { + /*inline*/ case None: + /*inline*/ case Bool: + /*inline*/ case Int: + /*inline*/ case Num: + /*inline*/ case Var: { Text_t code = Text$from_strn(ast->start, (int64_t)(ast->end - ast->start)); if (Text$has(code, Text("\n"))) return NONE_TEXT; return code; } - case FunctionCall: { + /*inline*/ case FunctionCall: { DeclareMatch(call, ast, FunctionCall); - return Texts(must(format_inline_code(call->fn, comments)), "(", must(format_inline_args(call->args, comments)), - ")"); + return Texts(fmt_inline(call->fn, comments), "(", must(format_inline_args(call->args, comments)), ")"); + } + /*inline*/ case MethodCall: { + DeclareMatch(call, ast, MethodCall); + return Texts(fmt_inline(call->self, comments), ".", Text$from_str(call->name), "(", + must(format_inline_args(call->args, comments)), ")"); } - case BINOP_CASES: { + /*inline*/ case BINOP_CASES: { binary_operands_t operands = BINARY_OPERANDS(ast); const char *op = binop_tomo_operator(ast->tag); - Text_t lhs = must(format_inline_code(operands.lhs, comments)); - Text_t rhs = must(format_inline_code(operands.rhs, comments)); + Text_t lhs = fmt_inline(operands.lhs, comments); + Text_t rhs = fmt_inline(operands.rhs, comments); if (is_update_assignment(ast)) { return Texts(lhs, " ", Text$from_str(op), " ", rhs); @@ -194,8 +242,9 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { bool inlined_fits = (inlined.length >= 0 && indent.length + inlined.length <= MAX_WIDTH); switch (ast->tag) { - case Unknown: fail("Invalid AST"); - case Block: { + /*multiline*/ case Unknown: + fail("Invalid AST"); + /*multiline*/ case Block: { Text_t code = EMPTY_TEXT; bool gap_before_comment = false; const char *comment_pos = ast->start; @@ -211,7 +260,7 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { add_line(&code, Text$trim(comment, Text(" \t\r\n"), false, true), indent); } - add_line(&code, format_code(stmt->ast, comments, indent), indent); + add_line(&code, fmt(stmt->ast, comments, indent), indent); comment_pos = stmt->ast->end; if (should_have_blank_line(stmt->ast) && stmt->next && !should_have_blank_line(stmt->next->ast)) @@ -228,27 +277,59 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { } return code; } - case If: { + /*multiline*/ case If: { DeclareMatch(if_, ast, If); if (inlined_fits && if_->else_body == NULL) return inlined; - Text_t code = Texts("if ", format_code(if_->condition, comments, indent), "\n", indent, single_indent, - format_code(if_->body, comments, Texts(indent, single_indent))); + Text_t code = Texts("if ", fmt(if_->condition, comments, indent), "\n", indent, single_indent, + fmt(if_->body, comments, Texts(indent, single_indent))); if (if_->else_body) code = Texts(code, "\n", indent, "else \n", indent, single_indent, - format_code(if_->else_body, comments, Texts(indent, single_indent))); + fmt(if_->else_body, comments, Texts(indent, single_indent))); return code; } - case Repeat: { + /*multiline*/ case Repeat: { return Texts("repeat\n", indent, single_indent, - format_code(Match(ast, Repeat)->body, comments, Texts(indent, single_indent))); + fmt(Match(ast, Repeat)->body, comments, Texts(indent, single_indent))); } - case While: { + /*multiline*/ case While: { DeclareMatch(loop, ast, While); - return Texts("while ", format_code(loop->condition, comments, indent), "\n", indent, single_indent, - format_code(loop->body, comments, Texts(indent, single_indent))); + return Texts("while ", fmt(loop->condition, comments, indent), "\n", indent, single_indent, + fmt(loop->body, comments, Texts(indent, single_indent))); + } + /*multiline*/ case For: { + DeclareMatch(loop, ast, For); + Text_t code = Text("for "); + for (ast_list_t *var = loop->vars; var; var = var->next) { + code = Texts(code, fmt(var->ast, comments, indent)); + if (var->next) code = Texts(code, ", "); + } + code = Texts(code, " in ", fmt(loop->iter, comments, indent), format_namespace(loop->body, comments, indent)); + if (loop->empty) code = Texts(code, "\n", indent, "else", format_namespace(loop->empty, comments, indent)); + return code; + } + /*multiline*/ case Comprehension: { + if (inlined_fits) return inlined; + DeclareMatch(comp, ast, Comprehension); + Text_t code = Texts("(", fmt(comp->expr, comments, indent)); + if (code.length >= MAX_WIDTH) code = Texts(code, "\n", indent, "for "); + else code = Texts(code, " for "); + + for (ast_list_t *var = comp->vars; var; var = var->next) { + code = Texts(code, fmt(var->ast, comments, indent)); + if (var->next) code = Texts(code, ", "); + } + + code = Texts(code, " in ", fmt(comp->iter, comments, indent)); + + if (comp->filter) { + if (code.length >= MAX_WIDTH) code = Texts(code, "\n", indent, "if "); + else code = Texts(code, " if "); + code = Texts(code, fmt(comp->filter, comments, indent)); + } + return code; } - case FunctionDef: { + /*multiline*/ case FunctionDef: { DeclareMatch(func, ast, FunctionDef); // ast_t *name; // arg_ast_t *args; @@ -256,17 +337,15 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { // ast_t *body; // ast_t *cache; // bool is_inline; - Text_t code = - Texts("func ", format_code(func->name, comments, indent), "(", format_args(func->args, comments, indent)); + Text_t code = Texts("func ", fmt(func->name, comments, indent), "(", format_args(func->args, comments, indent)); if (func->ret_type) code = Texts(code, func->args ? Text(" -> ") : Text("-> "), format_type(func->ret_type, comments, indent)); - if (func->cache) code = Texts(code, "; cache=", format_code(func->cache, comments, indent)); + if (func->cache) code = Texts(code, "; cache=", fmt(func->cache, comments, indent)); if (func->is_inline) code = Texts(code, "; inline"); - code = - Texts(code, ")\n", indent, single_indent, format_code(func->body, comments, Texts(indent, single_indent))); + code = Texts(code, ")\n", indent, single_indent, fmt(func->body, comments, Texts(indent, single_indent))); return Texts(code); } - case StructDef: { + /*multiline*/ case StructDef: { DeclareMatch(def, ast, StructDef); Text_t code = Texts("struct ", Text$from_str(def->name), "(", format_args(def->fields, comments, indent)); if (def->secret) code = Texts(code, "; secret"); @@ -274,21 +353,21 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { if (def->opaque) code = Texts(code, "; opaque"); return Texts(code, ")", format_namespace(def->namespace, comments, indent)); } - case EnumDef: { + /*multiline*/ case EnumDef: { DeclareMatch(def, ast, EnumDef); Text_t code = Texts("enum ", Text$from_str(def->name), "(", format_tags(def->tags, comments, indent)); return Texts(code, ")", format_namespace(def->namespace, comments, indent)); } - case LangDef: { + /*multiline*/ case LangDef: { DeclareMatch(def, ast, LangDef); return Texts("lang ", Text$from_str(def->name), format_namespace(def->namespace, comments, indent)); } - case Extend: { + /*multiline*/ case Extend: { DeclareMatch(extend, ast, Extend); return Texts("lang ", Text$from_str(extend->name), format_namespace(extend->body, comments, indent)); } - case List: - case Set: { + /*multiline*/ case List: + /*multiline*/ case Set: { if (inlined_fits) return inlined; ast_list_t *items = ast->tag == List ? Match(ast, List)->items : Match(ast, Set)->items; Text_t code = EMPTY_TEXT; @@ -298,7 +377,7 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { (comment = next_comment(comments, &comment_pos, item->ast->start)).length > 0;) { add_line(&code, Text$trim(comment, Text(" \t\r\n"), false, true), Texts(indent, single_indent)); } - add_line(&code, Texts(format_code(item->ast, comments, Texts(indent, single_indent)), ","), + add_line(&code, Texts(fmt(item->ast, comments, Texts(indent, single_indent)), ","), Texts(indent, single_indent)); } for (OptionalText_t comment; (comment = next_comment(comments, &comment_pos, ast->end)).length > 0;) { @@ -307,54 +386,95 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { return ast->tag == List ? Texts("[\n", indent, single_indent, code, "\n", indent, "]") : Texts("|\n", indent, single_indent, code, "\n", indent, "|"); } - case Declare: { + /*multiline*/ case Table: { + if (inlined_fits) return inlined; + DeclareMatch(table, ast, Table); + Text_t code = EMPTY_TEXT; + const char *comment_pos = ast->start; + for (ast_list_t *entry = table->entries; entry; entry = entry->next) { + for (OptionalText_t comment; + (comment = next_comment(comments, &comment_pos, entry->ast->start)).length > 0;) { + add_line(&code, Text$trim(comment, Text(" \t\r\n"), false, true), Texts(indent, single_indent)); + } + add_line(&code, Texts(fmt(entry->ast, comments, Texts(indent, single_indent)), ","), + Texts(indent, single_indent)); + } + for (OptionalText_t comment; (comment = next_comment(comments, &comment_pos, ast->end)).length > 0;) { + add_line(&code, Text$trim(comment, Text(" \t\r\n"), false, true), Texts(indent, single_indent)); + } + + if (table->fallback) + code = Texts(code, ";\n", indent, single_indent, "fallback=", fmt(table->fallback, comments, indent)); + + if (table->default_value) + code = Texts(code, ";\n", indent, single_indent, "default=", fmt(table->default_value, comments, indent)); + + return Texts("{\n", indent, single_indent, code, "\n", indent, "}"); + } + /*multiline*/ case TableEntry: { + if (inlined_fits) return inlined; + DeclareMatch(entry, ast, TableEntry); + return Texts(fmt(entry->key, comments, indent), "=", fmt(entry->value, comments, indent)); + } + /*multiline*/ case Declare: { DeclareMatch(decl, ast, Declare); - Text_t code = format_code(decl->var, comments, indent); + Text_t code = fmt(decl->var, comments, indent); if (decl->type) code = Texts(code, " : ", format_type(decl->type, comments, indent)); if (decl->value) - code = Texts(code, decl->type ? Text(" = ") : Text(" := "), format_code(decl->value, comments, indent)); + code = Texts(code, decl->type ? Text(" = ") : Text(" := "), fmt(decl->value, comments, indent)); return code; } - case Assign: { + /*multiline*/ case Assign: { DeclareMatch(assign, ast, Assign); Text_t code = EMPTY_TEXT; for (ast_list_t *target = assign->targets; target; target = target->next) { - code = Texts(code, format_code(target->ast, comments, indent)); + code = Texts(code, fmt(target->ast, comments, indent)); if (target->next) code = Texts(code, ", "); } code = Texts(code, " = "); for (ast_list_t *value = assign->values; value; value = value->next) { - code = Texts(code, format_code(value->ast, comments, indent)); + code = Texts(code, fmt(value->ast, comments, indent)); if (value->next) code = Texts(code, ", "); } return code; } - case Return: { + /*multiline*/ case Pass: + return Text("pass"); + /*multiline*/ case Return: { ast_t *value = Match(ast, Return)->value; - return value ? Texts("return ", format_code(value, comments, indent)) : Text("return"); + return value ? Texts("return ", fmt(value, comments, indent)) : Text("return"); + } + /*multiline*/ case HeapAllocate: { + if (inlined_fits) return inlined; + ast_t *val = Match(ast, HeapAllocate)->value; + return Texts("@(", fmt(val, comments, indent), ")"); } - case Optional: { + /*multiline*/ case StackReference: { + if (inlined_fits) return inlined; + ast_t *val = Match(ast, StackReference)->value; + return Texts("&(", fmt(val, comments, indent), ")"); + } + /*multiline*/ case Optional: { if (inlined_fits) return inlined; ast_t *val = Match(ast, Optional)->value; - return Texts("(", format_code(val, comments, indent), ")?"); + return Texts("(", fmt(val, comments, indent), ")?"); } - case NonOptional: { + /*multiline*/ case NonOptional: { if (inlined_fits) return inlined; ast_t *val = Match(ast, NonOptional)->value; - return Texts("(", format_code(val, comments, indent), ")!"); + return Texts("(", fmt(val, comments, indent), ")!"); } - case FieldAccess: { + /*multiline*/ case FieldAccess: { DeclareMatch(access, ast, FieldAccess); - return Texts(format_code(access->fielded, comments, indent), ".", Text$from_str(access->field)); + return Texts(fmt(access->fielded, comments, indent), ".", Text$from_str(access->field)); } - case Index: { + /*multiline*/ case Index: { DeclareMatch(index, ast, Index); if (index->index) - return Texts(format_code(index->indexed, comments, indent), "[", - format_code(index->index, comments, indent), "]"); - else return Texts(format_code(index->indexed, comments, indent), "[]"); + return Texts(fmt(index->indexed, comments, indent), "[", fmt(index->index, comments, indent), "]"); + else return Texts(fmt(index->indexed, comments, indent), "[]"); } - case TextJoin: { + /*multiline*/ case TextJoin: { if (inlined_fits) return inlined; // TODO: choose quotation mark more smartly Text_t source = Text$from_strn(ast->start, (int64_t)(ast->end - ast->start)); @@ -373,7 +493,7 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { current_line = *(Text_t *)(lines.data + i * lines.stride); } } else { - code = Texts(code, "$(", must(format_inline_code(chunk->ast, comments)), ")"); + code = Texts(code, "$(", fmt_inline(chunk->ast, comments), ")"); } } add_line(&code, current_line, Texts(indent, single_indent)); @@ -381,42 +501,46 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { if (lang) code = Texts("$", Text$from_str(lang), code); return code; } - case TextLiteral: { - fail("Something went wrong, we shouldn't be formatting text literals directly"); - } - case Stop: - case Skip: - case None: - case Bool: - case Int: - case Num: - case Var: { + /*multiline*/ case TextLiteral: { fail("Something went wrong, we shouldn't be formatting text literals directly"); } + /*multiline*/ case Stop: + /*multiline*/ case Skip: + /*multiline*/ case None: + /*multiline*/ case Bool: + /*multiline*/ case Int: + /*multiline*/ case Num: + /*multiline*/ case Var: { assert(inlined.length >= 0); return inlined; } - case FunctionCall: { + /*multiline*/ case FunctionCall: { if (inlined_fits) return inlined; DeclareMatch(call, ast, FunctionCall); - return Texts(format_code(call->fn, comments, indent), "(\n", indent, single_indent, + return Texts(fmt(call->fn, comments, indent), "(\n", indent, single_indent, + format_args(call->args, comments, Texts(indent, single_indent)), "\n", indent, ")"); + } + /*multiline*/ case MethodCall: { + if (inlined_fits) return inlined; + DeclareMatch(call, ast, MethodCall); + return Texts(fmt(call->self, comments, indent), ".", Text$from_str(call->name), "(\n", indent, single_indent, format_args(call->args, comments, Texts(indent, single_indent)), "\n", indent, ")"); } - case DocTest: { + /*multiline*/ case DocTest: { DeclareMatch(test, ast, DocTest); - Text_t expr = format_code(test->expr, comments, indent); + Text_t expr = fmt(test->expr, comments, indent); Text_t code = Texts(">> ", Text$replace(expr, Texts("\n", indent), Texts("\n", indent, ".. "))); if (test->expected) { - Text_t expected = format_code(test->expected, comments, indent); + Text_t expected = fmt(test->expected, comments, indent); code = Texts(code, "\n", indent, "= ", Text$replace(expected, Texts("\n", indent), Texts("\n", indent, ".. "))); } return code; } - case BINOP_CASES: { + /*multiline*/ case BINOP_CASES: { if (inlined_fits) return inlined; binary_operands_t operands = BINARY_OPERANDS(ast); const char *op = binop_tomo_operator(ast->tag); - Text_t lhs = format_code(operands.lhs, comments, indent); - Text_t rhs = format_code(operands.rhs, comments, indent); + Text_t lhs = fmt(operands.lhs, comments, indent); + Text_t rhs = fmt(operands.rhs, comments, indent); if (is_update_assignment(ast)) { return Texts(lhs, " ", Text$from_str(op), " ", rhs); @@ -469,7 +593,7 @@ Text_t format_file(const char *path) { for (OptionalText_t comment; (comment = next_comment(ctx.comments, &fmt_pos, ast->start)).length > 0;) { code = Texts(code, Text$trim(comment, Text(" \t\r\n"), false, true), "\n"); } - code = Texts(code, format_code(ast, ctx.comments, EMPTY_TEXT)); + code = Texts(code, fmt(ast, ctx.comments, EMPTY_TEXT)); for (OptionalText_t comment; (comment = next_comment(ctx.comments, &fmt_pos, ast->start)).length > 0;) { code = Texts(code, Text$trim(comment, Text(" \t\r\n"), false, true), "\n"); } -- cgit v1.2.3 From b5dee4f7ed1b8181df22a9824b7026a125ebbc53 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 26 Aug 2025 00:45:56 -0400 Subject: Actual type formatting --- src/formatter/args.c | 4 ++-- src/formatter/formatter.c | 7 +++---- src/formatter/types.c | 51 ++++++++++++++++++++++++++++++----------------- src/formatter/types.h | 3 +-- 4 files changed, 39 insertions(+), 26 deletions(-) (limited to 'src/formatter') diff --git a/src/formatter/args.c b/src/formatter/args.c index 2bd115cd..6bd07b28 100644 --- a/src/formatter/args.c +++ b/src/formatter/args.c @@ -12,7 +12,7 @@ OptionalText_t format_inline_arg(arg_ast_t *arg, Table_t comments) { if (range_has_comment(arg->start, arg->end, comments)) return NONE_TEXT; if (arg->name == NULL && arg->value) return must(format_inline_code(arg->value, comments)); Text_t code = Text$from_str(arg->name); - if (arg->type) code = Texts(code, ":", must(format_inline_type(arg->type, comments))); + if (arg->type) code = Texts(code, ":", must(format_type(arg->type))); if (arg->value) code = Texts(code, " = ", must(format_inline_code(arg->value, comments))); return code; } @@ -22,7 +22,7 @@ Text_t format_arg(arg_ast_t *arg, Table_t comments, Text_t indent) { if (inline_arg.length >= 0 && inline_arg.length <= MAX_WIDTH) return inline_arg; if (arg->name == NULL && arg->value) return format_code(arg->value, comments, indent); Text_t code = Text$from_str(arg->name); - if (arg->type) code = Texts(code, ":", format_type(arg->type, comments, indent)); + if (arg->type) code = Texts(code, ":", format_type(arg->type)); if (arg->value) code = Texts(code, " = ", format_code(arg->value, comments, indent)); return code; } diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c index b0fad38a..f0a1c125 100644 --- a/src/formatter/formatter.c +++ b/src/formatter/formatter.c @@ -119,7 +119,7 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { /*inline*/ case Declare: { DeclareMatch(decl, ast, Declare); Text_t code = fmt_inline(decl->var, comments); - if (decl->type) code = Texts(code, " : ", must(format_inline_type(decl->type, comments))); + if (decl->type) code = Texts(code, " : ", format_type(decl->type)); if (decl->value) code = Texts(code, decl->type ? Text(" = ") : Text(" := "), fmt_inline(decl->value, comments)); return code; } @@ -338,8 +338,7 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { // ast_t *cache; // bool is_inline; Text_t code = Texts("func ", fmt(func->name, comments, indent), "(", format_args(func->args, comments, indent)); - if (func->ret_type) - code = Texts(code, func->args ? Text(" -> ") : Text("-> "), format_type(func->ret_type, comments, indent)); + if (func->ret_type) code = Texts(code, func->args ? Text(" -> ") : Text("-> "), format_type(func->ret_type)); if (func->cache) code = Texts(code, "; cache=", fmt(func->cache, comments, indent)); if (func->is_inline) code = Texts(code, "; inline"); code = Texts(code, ")\n", indent, single_indent, fmt(func->body, comments, Texts(indent, single_indent))); @@ -419,7 +418,7 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { /*multiline*/ case Declare: { DeclareMatch(decl, ast, Declare); Text_t code = fmt(decl->var, comments, indent); - if (decl->type) code = Texts(code, " : ", format_type(decl->type, comments, indent)); + if (decl->type) code = Texts(code, " : ", format_type(decl->type)); if (decl->value) code = Texts(code, decl->type ? Text(" = ") : Text(" := "), fmt(decl->value, comments, indent)); return code; diff --git a/src/formatter/types.c b/src/formatter/types.c index 34128b0b..24171984 100644 --- a/src/formatter/types.c +++ b/src/formatter/types.c @@ -2,29 +2,44 @@ #include "../ast.h" #include "../stdlib/datatypes.h" -#include "../stdlib/optionals.h" +#include "../stdlib/stdlib.h" #include "../stdlib/text.h" -#include "utils.h" +#include "args.h" +#include "formatter.h" -OptionalText_t format_inline_type(type_ast_t *type, Table_t comments) { - if (range_has_comment(type->start, type->end, comments)) return NONE_TEXT; +Text_t format_type(type_ast_t *type) { switch (type->tag) { - default: { - Text_t code = Text$from_strn(type->start, (int64_t)(type->end - type->start)); - if (Text$has(code, Text("\n"))) return NONE_TEXT; - return Text$replace(code, Text("\t"), single_indent); + case VarTypeAST: return Text$from_str(Match(type, VarTypeAST)->name); + case PointerTypeAST: { + DeclareMatch(ptr, type, PointerTypeAST); + return Texts(ptr->is_stack ? Text("&") : Text("@"), format_type(ptr->pointed)); } + case ListTypeAST: { + return Texts("[", format_type(Match(type, ListTypeAST)->item), "]"); } -} - -Text_t format_type(type_ast_t *type, Table_t comments, Text_t indent) { - (void)comments, (void)indent; - switch (type->tag) { - default: { - OptionalText_t inline_type = format_inline_type(type, comments); - if (inline_type.length >= 0) return inline_type; - Text_t code = Text$from_strn(type->start, (int64_t)(type->end - type->start)); - return Text$replace(code, Text("\t"), single_indent); + case SetTypeAST: { + return Texts("|", format_type(Match(type, ListTypeAST)->item), "|"); + } + case TableTypeAST: { + DeclareMatch(table, type, TableTypeAST); + Text_t code = Texts("{", format_type(table->key), "=", format_type(table->value)); + if (table->default_value) { + OptionalText_t val = format_inline_code(table->default_value, (Table_t){}); + assert(val.length >= 0); + code = Texts(code, "; default=", val); + } + return Texts(code, "}"); + } + case FunctionTypeAST: { + DeclareMatch(func, type, FunctionTypeAST); + Text_t code = Texts("func(", format_inline_args(func->args, (Table_t){})); + if (func->ret) code = Texts(code, func->args ? Text(" -> ") : Text("-> "), format_type(func->ret)); + return Texts(code, ")"); + } + case OptionalTypeAST: { + return Texts(format_type(Match(type, OptionalTypeAST)->type), "?"); } + case UnknownTypeAST: + default: fail("Invalid Type AST"); } } diff --git a/src/formatter/types.h b/src/formatter/types.h index 1f0698b3..2571f880 100644 --- a/src/formatter/types.h +++ b/src/formatter/types.h @@ -5,5 +5,4 @@ #include "../ast.h" #include "../stdlib/datatypes.h" -OptionalText_t format_inline_type(type_ast_t *type, Table_t comments); -Text_t format_type(type_ast_t *type, Table_t comments, Text_t indent); +Text_t format_type(type_ast_t *type); -- cgit v1.2.3 From 794cffd4da6694d06d1a7c6f77c157e58354ac25 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 26 Aug 2025 00:46:31 -0400 Subject: Fix typo --- src/formatter/types.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/formatter') diff --git a/src/formatter/types.c b/src/formatter/types.c index 24171984..e52faf70 100644 --- a/src/formatter/types.c +++ b/src/formatter/types.c @@ -18,7 +18,7 @@ Text_t format_type(type_ast_t *type) { return Texts("[", format_type(Match(type, ListTypeAST)->item), "]"); } case SetTypeAST: { - return Texts("|", format_type(Match(type, ListTypeAST)->item), "|"); + return Texts("|", format_type(Match(type, SetTypeAST)->item), "|"); } case TableTypeAST: { DeclareMatch(table, type, TableTypeAST); -- cgit v1.2.3 From 8166e4c194b039f0a1e5c011286fb156a12c82c2 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 26 Aug 2025 01:01:42 -0400 Subject: More formatting --- src/formatter/formatter.c | 52 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 6 deletions(-) (limited to 'src/formatter') diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c index f0a1c125..b62d3fbe 100644 --- a/src/formatter/formatter.c +++ b/src/formatter/formatter.c @@ -46,6 +46,14 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { /*inline*/ case FunctionDef: /*inline*/ case DocTest: return NONE_TEXT; + /*inline*/ case Lambda: { + DeclareMatch(lambda, ast, Lambda); + Text_t code = Texts("func(", format_inline_args(lambda->args, comments)); + if (lambda->ret_type) + code = Texts(code, lambda->args ? Text(" -> ") : Text("-> "), format_type(lambda->ret_type)); + code = Texts(code, ") ", fmt_inline(lambda->body, comments)); + return Texts(code); + } /*inline*/ case If: { DeclareMatch(if_, ast, If); @@ -63,6 +71,20 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { if (if_->else_body) code = Texts(code, " else ", fmt_inline(if_->else_body, comments)); return code; } + /*inline*/ case When: { + DeclareMatch(when, ast, When); + Text_t code = Texts("when ", fmt_inline(when->subject, comments)); + for (when_clause_t *clause = when->clauses; clause; clause = clause->next) { + code = Texts(code, " is ", fmt_inline(clause->pattern, comments)); + while (clause->next && clause->next->body == clause->body) { + clause = clause->next; + code = Texts(code, ", ", fmt_inline(clause->pattern, comments)); + } + code = Texts(code, " then ", fmt_inline(clause->body, comments)); + } + if (when->else_body) code = Texts(code, " else ", fmt_inline(when->else_body, comments)); + return code; + } /*inline*/ case Repeat: return Texts("repeat ", fmt_inline(Match(ast, Repeat)->body, comments)); /*inline*/ case While: { @@ -288,6 +310,21 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { fmt(if_->else_body, comments, Texts(indent, single_indent))); return code; } + /*multiline*/ case When: { + DeclareMatch(when, ast, When); + Text_t code = Texts("when ", fmt(when->subject, comments, indent)); + for (when_clause_t *clause = when->clauses; clause; clause = clause->next) { + code = Texts(code, "\n", indent, "is ", fmt(clause->pattern, comments, indent)); + while (clause->next && clause->next->body == clause->body) { + clause = clause->next; + code = Texts(code, ", ", fmt(clause->pattern, comments, indent)); + } + code = Texts(code, format_namespace(clause->body, comments, indent)); + } + if (when->else_body) + code = Texts(code, "\n", indent, "else", format_namespace(when->else_body, comments, indent)); + return code; + } /*multiline*/ case Repeat: { return Texts("repeat\n", indent, single_indent, fmt(Match(ast, Repeat)->body, comments, Texts(indent, single_indent))); @@ -331,12 +368,6 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { } /*multiline*/ case FunctionDef: { DeclareMatch(func, ast, FunctionDef); - // ast_t *name; - // arg_ast_t *args; - // type_ast_t *ret_type; - // ast_t *body; - // ast_t *cache; - // bool is_inline; Text_t code = Texts("func ", fmt(func->name, comments, indent), "(", format_args(func->args, comments, indent)); if (func->ret_type) code = Texts(code, func->args ? Text(" -> ") : Text("-> "), format_type(func->ret_type)); if (func->cache) code = Texts(code, "; cache=", fmt(func->cache, comments, indent)); @@ -344,6 +375,15 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { code = Texts(code, ")\n", indent, single_indent, fmt(func->body, comments, Texts(indent, single_indent))); return Texts(code); } + /*multiline*/ case Lambda: { + if (inlined_fits) return inlined; + DeclareMatch(lambda, ast, Lambda); + Text_t code = Texts("func(", format_args(lambda->args, comments, indent)); + if (lambda->ret_type) + code = Texts(code, lambda->args ? Text(" -> ") : Text("-> "), format_type(lambda->ret_type)); + code = Texts(code, ")\n", indent, single_indent, fmt(lambda->body, comments, Texts(indent, single_indent))); + return Texts(code); + } /*multiline*/ case StructDef: { DeclareMatch(def, ast, StructDef); Text_t code = Texts("struct ", Text$from_str(def->name), "(", format_args(def->fields, comments, indent)); -- cgit v1.2.3 From fc126b1103a8b6b6cbde7cf10ee6e25a3279cd9f Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 26 Aug 2025 01:05:25 -0400 Subject: Fix for 'else if' --- src/formatter/formatter.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'src/formatter') diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c index b62d3fbe..7c27d053 100644 --- a/src/formatter/formatter.c +++ b/src/formatter/formatter.c @@ -305,9 +305,14 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { Text_t code = Texts("if ", fmt(if_->condition, comments, indent), "\n", indent, single_indent, fmt(if_->body, comments, Texts(indent, single_indent))); - if (if_->else_body) - code = Texts(code, "\n", indent, "else \n", indent, single_indent, - fmt(if_->else_body, comments, Texts(indent, single_indent))); + if (if_->else_body) { + if (if_->else_body->tag != If) { + code = Texts(code, "\n", indent, "else\n", indent, single_indent, + fmt(if_->else_body, comments, Texts(indent, single_indent))); + } else { + code = Texts(code, "\n", indent, "else ", fmt(if_->else_body, comments, indent)); + } + } return code; } /*multiline*/ case When: { -- cgit v1.2.3 From 5b06702dde3f53510a3d64a8156a349914afa605 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 26 Aug 2025 02:54:56 -0400 Subject: Fixes for unops --- src/formatter/formatter.c | 58 +++++++++++++++++++++++++++++++---------------- src/formatter/utils.c | 20 ++++++++++++++++ src/formatter/utils.h | 2 ++ 3 files changed, 61 insertions(+), 19 deletions(-) (limited to 'src/formatter') diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c index 7c27d053..96bdf632 100644 --- a/src/formatter/formatter.c +++ b/src/formatter/formatter.c @@ -165,31 +165,39 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { ast_t *value = Match(ast, Return)->value; return value ? Texts("return ", fmt_inline(value, comments)) : Text("return"); } + /*inline*/ case Not: { + ast_t *val = Match(ast, Not)->value; + return Texts("not ", must(termify_inline(val, comments))); + } + /*inline*/ case Negative: { + ast_t *val = Match(ast, Negative)->value; + return Texts("-", must(termify_inline(val, comments))); + } /*inline*/ case HeapAllocate: { ast_t *val = Match(ast, HeapAllocate)->value; - return Texts("@", fmt_inline(val, comments)); + return Texts("@", must(termify_inline(val, comments))); } /*inline*/ case StackReference: { ast_t *val = Match(ast, StackReference)->value; - return Texts("&", fmt_inline(val, comments)); + return Texts("&", must(termify_inline(val, comments))); } /*inline*/ case Optional: { ast_t *val = Match(ast, Optional)->value; - return Texts(fmt_inline(val, comments), "?"); + return Texts(must(termify_inline(val, comments)), "?"); } /*inline*/ case NonOptional: { ast_t *val = Match(ast, NonOptional)->value; - return Texts(fmt_inline(val, comments), "!"); + return Texts(must(termify_inline(val, comments)), "!"); } /*inline*/ case FieldAccess: { DeclareMatch(access, ast, FieldAccess); - return Texts(fmt_inline(access->fielded, comments), ".", Text$from_str(access->field)); + return Texts(must(termify_inline(access->fielded, comments)), ".", Text$from_str(access->field)); } /*inline*/ case Index: { DeclareMatch(index, ast, Index); - if (index->index) - return Texts(fmt_inline(index->indexed, comments), "[", fmt_inline(index->index, comments), "]"); - else return Texts(fmt_inline(index->indexed, comments), "[]"); + Text_t indexed = must(termify_inline(index->indexed, comments)); + if (index->index) return Texts(indexed, "[", fmt_inline(index->index, comments), "]"); + else return Texts(indexed, "[]"); } /*inline*/ case TextJoin: { // TODO: choose quotation mark more smartly @@ -231,8 +239,10 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { } /*inline*/ case MethodCall: { DeclareMatch(call, ast, MethodCall); - return Texts(fmt_inline(call->self, comments), ".", Text$from_str(call->name), "(", - must(format_inline_args(call->args, comments)), ")"); + Text_t self = fmt_inline(call->self, comments); + if (is_binary_operation(call->self) || call->self->tag == Negative || call->self->tag == Not) + self = parenthesize(self, EMPTY_TEXT); + return Texts(self, ".", Text$from_str(call->name), "(", must(format_inline_args(call->args, comments)), ")"); } /*inline*/ case BINOP_CASES: { binary_operands_t operands = BINARY_OPERANDS(ast); @@ -488,35 +498,45 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { ast_t *value = Match(ast, Return)->value; return value ? Texts("return ", fmt(value, comments, indent)) : Text("return"); } + /*inline*/ case Not: { + ast_t *val = Match(ast, Not)->value; + if (is_binary_operation(val)) return Texts("not ", termify(val, comments, indent)); + else return Texts("not ", fmt(val, comments, indent)); + } + /*inline*/ case Negative: { + ast_t *val = Match(ast, Negative)->value; + if (is_binary_operation(val)) return Texts("-", termify(val, comments, indent)); + else return Texts("-", fmt(val, comments, indent)); + } /*multiline*/ case HeapAllocate: { if (inlined_fits) return inlined; ast_t *val = Match(ast, HeapAllocate)->value; - return Texts("@(", fmt(val, comments, indent), ")"); + return Texts("@", termify(val, comments, indent), ""); } /*multiline*/ case StackReference: { if (inlined_fits) return inlined; ast_t *val = Match(ast, StackReference)->value; - return Texts("&(", fmt(val, comments, indent), ")"); + return Texts("&(", termify(val, comments, indent), ")"); } /*multiline*/ case Optional: { if (inlined_fits) return inlined; ast_t *val = Match(ast, Optional)->value; - return Texts("(", fmt(val, comments, indent), ")?"); + return Texts(termify(val, comments, indent), "?"); } /*multiline*/ case NonOptional: { if (inlined_fits) return inlined; ast_t *val = Match(ast, NonOptional)->value; - return Texts("(", fmt(val, comments, indent), ")!"); + return Texts(termify(val, comments, indent), "!"); } /*multiline*/ case FieldAccess: { DeclareMatch(access, ast, FieldAccess); - return Texts(fmt(access->fielded, comments, indent), ".", Text$from_str(access->field)); + return Texts(termify(access->fielded, comments, indent), ".", Text$from_str(access->field)); } /*multiline*/ case Index: { DeclareMatch(index, ast, Index); if (index->index) - return Texts(fmt(index->indexed, comments, indent), "[", fmt(index->index, comments, indent), "]"); - else return Texts(fmt(index->indexed, comments, indent), "[]"); + return Texts(termify(index->indexed, comments, indent), "[", fmt(index->index, comments, indent), "]"); + else return Texts(termify(index->indexed, comments, indent), "[]"); } /*multiline*/ case TextJoin: { if (inlined_fits) return inlined; @@ -565,8 +585,8 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { /*multiline*/ case MethodCall: { if (inlined_fits) return inlined; DeclareMatch(call, ast, MethodCall); - return Texts(fmt(call->self, comments, indent), ".", Text$from_str(call->name), "(\n", indent, single_indent, - format_args(call->args, comments, Texts(indent, single_indent)), "\n", indent, ")"); + return Texts(termify(call->self, comments, indent), ".", Text$from_str(call->name), "(\n", indent, + single_indent, format_args(call->args, comments, Texts(indent, single_indent)), "\n", indent, ")"); } /*multiline*/ case DocTest: { DeclareMatch(test, ast, DocTest); diff --git a/src/formatter/utils.c b/src/formatter/utils.c index 472829d9..37751377 100644 --- a/src/formatter/utils.c +++ b/src/formatter/utils.c @@ -9,6 +9,7 @@ #include "../stdlib/optionals.h" #include "../stdlib/tables.h" #include "../stdlib/text.h" +#include "formatter.h" const Text_t single_indent = Text(" "); @@ -110,3 +111,22 @@ CONSTFUNC const char *binop_tomo_operator(ast_e tag) { default: return NULL; } } + +OptionalText_t termify_inline(ast_t *ast, Table_t comments) { + if (range_has_comment(ast->start, ast->end, comments)) return NONE_TEXT; + switch (ast->tag) { + case BINOP_CASES: + case Not: + case Negative: return parenthesize(format_inline_code(ast, comments), EMPTY_TEXT); + default: return format_inline_code(ast, comments); + } +} + +Text_t termify(ast_t *ast, Table_t comments, Text_t indent) { + switch (ast->tag) { + case BINOP_CASES: + case Not: + case Negative: return parenthesize(format_code(ast, comments, indent), indent); + default: return format_inline_code(ast, comments); + } +} diff --git a/src/formatter/utils.h b/src/formatter/utils.h index a8def627..df9c9b92 100644 --- a/src/formatter/utils.h +++ b/src/formatter/utils.h @@ -26,3 +26,5 @@ Text_t indent_code(Text_t code); Text_t parenthesize(Text_t code, Text_t indent); CONSTFUNC ast_t *unwrap_block(ast_t *ast); CONSTFUNC const char *binop_tomo_operator(ast_e tag); +OptionalText_t termify_inline(ast_t *ast, Table_t comments); +Text_t termify(ast_t *ast, Table_t comments, Text_t indent); -- cgit v1.2.3 From 4715487a4be9f03ad58cb3199e95573e262f6387 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 26 Aug 2025 02:56:51 -0400 Subject: More termify fixes --- src/formatter/utils.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/formatter') diff --git a/src/formatter/utils.c b/src/formatter/utils.c index 37751377..a032ec2f 100644 --- a/src/formatter/utils.c +++ b/src/formatter/utils.c @@ -117,7 +117,9 @@ OptionalText_t termify_inline(ast_t *ast, Table_t comments) { switch (ast->tag) { case BINOP_CASES: case Not: - case Negative: return parenthesize(format_inline_code(ast, comments), EMPTY_TEXT); + case Negative: + case HeapAllocate: + case StackReference: return parenthesize(format_inline_code(ast, comments), EMPTY_TEXT); default: return format_inline_code(ast, comments); } } @@ -126,7 +128,9 @@ Text_t termify(ast_t *ast, Table_t comments, Text_t indent) { switch (ast->tag) { case BINOP_CASES: case Not: - case Negative: return parenthesize(format_code(ast, comments, indent), indent); + case Negative: + case HeapAllocate: + case StackReference: return parenthesize(format_code(ast, comments, indent), indent); default: return format_inline_code(ast, comments); } } -- cgit v1.2.3 From d25d5642392f93623d1eb4d11156d293fe6df546 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 26 Aug 2025 14:39:11 -0400 Subject: Formatting for min/max and cleanup for ints/nums --- src/formatter/formatter.c | 37 +++++++++++++++++++++++++++++++------ src/formatter/utils.h | 1 + 2 files changed, 32 insertions(+), 6 deletions(-) (limited to 'src/formatter') diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c index 96bdf632..a48a1f33 100644 --- a/src/formatter/formatter.c +++ b/src/formatter/formatter.c @@ -216,6 +216,7 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { return Texts(code, quote); } /*inline*/ case TextLiteral: { fail("Something went wrong, we shouldn't be formatting text literals directly"); } + /*inline*/ case Path: { return Texts("(", Text$from_str(Match(ast, Path)->path), ")"); } /*inline*/ case Stop: { const char *target = Match(ast, Stop)->target; return target ? Texts("stop ", Text$from_str(target)) : Text("stop"); @@ -224,15 +225,27 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { const char *target = Match(ast, Skip)->target; return target ? Texts("skip ", Text$from_str(target)) : Text("skip"); } + /*inline*/ case Min: + /*inline*/ case Max: { + Text_t lhs = fmt_inline(ast->tag == Min ? Match(ast, Min)->lhs : Match(ast, Max)->lhs, comments); + Text_t rhs = fmt_inline(ast->tag == Min ? Match(ast, Min)->rhs : Match(ast, Max)->rhs, comments); + ast_t *key = ast->tag == Min ? Match(ast, Min)->key : Match(ast, Max)->key; + return Texts(lhs, key ? fmt_inline(key, comments) : (ast->tag == Min ? Text(" _min_ ") : Text(" _max_ ")), rhs); + } /*inline*/ case None: + return Text("none"); /*inline*/ case Bool: - /*inline*/ case Int: - /*inline*/ case Num: - /*inline*/ case Var: { - Text_t code = Text$from_strn(ast->start, (int64_t)(ast->end - ast->start)); - if (Text$has(code, Text("\n"))) return NONE_TEXT; - return code; + return Match(ast, Bool)->b ? Text("yes") : Text("no"); + /*inline*/ case Int: { + OptionalText_t source = ast_source(ast); + return source.length > 0 ? source : Text$from_str(Match(ast, Int)->str); + } + /*inline*/ case Num: { + OptionalText_t source = ast_source(ast); + return source.length > 0 ? source : Text$from_str(String(Match(ast, Num)->n)); } + /*inline*/ case Var: + return Text$from_str(Match(ast, Var)->name); /*inline*/ case FunctionCall: { DeclareMatch(call, ast, FunctionCall); return Texts(fmt_inline(call->fn, comments), "(", must(format_inline_args(call->args, comments)), ")"); @@ -566,6 +579,18 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { return code; } /*multiline*/ case TextLiteral: { fail("Something went wrong, we shouldn't be formatting text literals directly"); } + /*multiline*/ case Path: { + assert(inlined.length > 0); + return inlined; + } + /*inline*/ case Min: + /*inline*/ case Max: { + Text_t lhs = termify(ast->tag == Min ? Match(ast, Min)->lhs : Match(ast, Max)->lhs, comments, indent); + Text_t rhs = termify(ast->tag == Min ? Match(ast, Min)->rhs : Match(ast, Max)->rhs, comments, indent); + ast_t *key = ast->tag == Min ? Match(ast, Min)->key : Match(ast, Max)->key; + Text_t op = key ? fmt(key, comments, indent) : (ast->tag == Min ? Text("_min_") : Text("_max_")); + return Texts(lhs, " ", op, " ", rhs); + } /*multiline*/ case Stop: /*multiline*/ case Skip: /*multiline*/ case None: diff --git a/src/formatter/utils.h b/src/formatter/utils.h index df9c9b92..9dc230e4 100644 --- a/src/formatter/utils.h +++ b/src/formatter/utils.h @@ -6,6 +6,7 @@ #include "../ast.h" #include "../stdlib/datatypes.h" +#include "../stdlib/optionals.h" #define MAX_WIDTH 100 -- cgit v1.2.3 From cb1d36c6d8bc84f3422c71ab3eb29606e80f7837 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 26 Aug 2025 14:58:51 -0400 Subject: Formatting for reductions --- src/formatter/formatter.c | 50 +++++++++++++++++++++++++++++++++++++++++------ src/formatter/utils.c | 38 ----------------------------------- src/formatter/utils.h | 1 - 3 files changed, 44 insertions(+), 45 deletions(-) (limited to 'src/formatter') diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c index a48a1f33..5dd65d04 100644 --- a/src/formatter/formatter.c +++ b/src/formatter/formatter.c @@ -44,6 +44,7 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { /*inline*/ case LangDef: /*inline*/ case Extend: /*inline*/ case FunctionDef: + /*inline*/ case ConvertDef: /*inline*/ case DocTest: return NONE_TEXT; /*inline*/ case Lambda: { @@ -232,6 +233,14 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { ast_t *key = ast->tag == Min ? Match(ast, Min)->key : Match(ast, Max)->key; return Texts(lhs, key ? fmt_inline(key, comments) : (ast->tag == Min ? Text(" _min_ ") : Text(" _max_ ")), rhs); } + /*inline*/ case Reduction: { + DeclareMatch(reduction, ast, Reduction); + if (reduction->key) { + return Texts("(", fmt_inline(reduction->key, comments), ": ", fmt_inline(reduction->iter, comments)); + } else { + return Texts("(", binop_operator(reduction->op), ": ", fmt_inline(reduction->iter, comments)); + } + } /*inline*/ case None: return Text("none"); /*inline*/ case Bool: @@ -259,7 +268,7 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { } /*inline*/ case BINOP_CASES: { binary_operands_t operands = BINARY_OPERANDS(ast); - const char *op = binop_tomo_operator(ast->tag); + const char *op = binop_operator(ast->tag); Text_t lhs = fmt_inline(operands.lhs, comments); Text_t rhs = fmt_inline(operands.rhs, comments); @@ -274,7 +283,7 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { rhs = parenthesize(rhs, EMPTY_TEXT); Text_t space = op_tightness[ast->tag] >= op_tightness[Multiply] ? EMPTY_TEXT : Text(" "); - return Texts(lhs, space, Text$from_str(binop_tomo_operator(ast->tag)), space, rhs); + return Texts(lhs, space, Text$from_str(binop_operator(ast->tag)), space, rhs); } default: { fail("Formatting not implemented for: ", ast_to_sexp(ast)); @@ -412,6 +421,16 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { code = Texts(code, ")\n", indent, single_indent, fmt(lambda->body, comments, Texts(indent, single_indent))); return Texts(code); } + /*multiline*/ case ConvertDef: { + DeclareMatch(convert, ast, ConvertDef); + Text_t code = Texts("convert (", format_args(convert->args, comments, indent)); + if (convert->ret_type) + code = Texts(code, convert->args ? Text(" -> ") : Text("-> "), format_type(convert->ret_type)); + if (convert->cache) code = Texts(code, "; cache=", fmt(convert->cache, comments, indent)); + if (convert->is_inline) code = Texts(code, "; inline"); + code = Texts(code, ")\n", indent, single_indent, fmt(convert->body, comments, Texts(indent, single_indent))); + return Texts(code); + } /*multiline*/ case StructDef: { DeclareMatch(def, ast, StructDef); Text_t code = Texts("struct ", Text$from_str(def->name), "(", format_args(def->fields, comments, indent)); @@ -484,6 +503,7 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { return Texts(fmt(entry->key, comments, indent), "=", fmt(entry->value, comments, indent)); } /*multiline*/ case Declare: { + if (inlined_fits) return inlined; DeclareMatch(decl, ast, Declare); Text_t code = fmt(decl->var, comments, indent); if (decl->type) code = Texts(code, " : ", format_type(decl->type)); @@ -492,6 +512,7 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { return code; } /*multiline*/ case Assign: { + if (inlined_fits) return inlined; DeclareMatch(assign, ast, Assign); Text_t code = EMPTY_TEXT; for (ast_list_t *target = assign->targets; target; target = target->next) { @@ -508,15 +529,18 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { /*multiline*/ case Pass: return Text("pass"); /*multiline*/ case Return: { + if (inlined_fits) return inlined; ast_t *value = Match(ast, Return)->value; return value ? Texts("return ", fmt(value, comments, indent)) : Text("return"); } /*inline*/ case Not: { + if (inlined_fits) return inlined; ast_t *val = Match(ast, Not)->value; if (is_binary_operation(val)) return Texts("not ", termify(val, comments, indent)); else return Texts("not ", fmt(val, comments, indent)); } /*inline*/ case Negative: { + if (inlined_fits) return inlined; ast_t *val = Match(ast, Negative)->value; if (is_binary_operation(val)) return Texts("-", termify(val, comments, indent)); else return Texts("-", fmt(val, comments, indent)); @@ -542,10 +566,12 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { return Texts(termify(val, comments, indent), "!"); } /*multiline*/ case FieldAccess: { + if (inlined_fits) return inlined; DeclareMatch(access, ast, FieldAccess); return Texts(termify(access->fielded, comments, indent), ".", Text$from_str(access->field)); } /*multiline*/ case Index: { + if (inlined_fits) return inlined; DeclareMatch(index, ast, Index); if (index->index) return Texts(termify(index->indexed, comments, indent), "[", fmt(index->index, comments, indent), "]"); @@ -583,14 +609,26 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { assert(inlined.length > 0); return inlined; } - /*inline*/ case Min: - /*inline*/ case Max: { + /*multiline*/ case Min: + /*multiline*/ case Max: { + if (inlined_fits) return inlined; Text_t lhs = termify(ast->tag == Min ? Match(ast, Min)->lhs : Match(ast, Max)->lhs, comments, indent); Text_t rhs = termify(ast->tag == Min ? Match(ast, Min)->rhs : Match(ast, Max)->rhs, comments, indent); ast_t *key = ast->tag == Min ? Match(ast, Min)->key : Match(ast, Max)->key; Text_t op = key ? fmt(key, comments, indent) : (ast->tag == Min ? Text("_min_") : Text("_max_")); return Texts(lhs, " ", op, " ", rhs); } + /*multiline*/ case Reduction: { + if (inlined_fits) return inlined; + DeclareMatch(reduction, ast, Reduction); + if (reduction->key) { + return Texts("(", fmt(reduction->key, comments, Texts(indent, single_indent)), ": ", + fmt(reduction->iter, comments, Texts(indent, single_indent))); + } else { + return Texts("(", binop_operator(reduction->op), ": ", + fmt(reduction->iter, comments, Texts(indent, single_indent))); + } + } /*multiline*/ case Stop: /*multiline*/ case Skip: /*multiline*/ case None: @@ -627,7 +665,7 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { /*multiline*/ case BINOP_CASES: { if (inlined_fits) return inlined; binary_operands_t operands = BINARY_OPERANDS(ast); - const char *op = binop_tomo_operator(ast->tag); + const char *op = binop_operator(ast->tag); Text_t lhs = fmt(operands.lhs, comments, indent); Text_t rhs = fmt(operands.rhs, comments, indent); @@ -641,7 +679,7 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { rhs = parenthesize(rhs, indent); Text_t space = op_tightness[ast->tag] >= op_tightness[Multiply] ? EMPTY_TEXT : Text(" "); - return Texts(lhs, space, Text$from_str(binop_tomo_operator(ast->tag)), space, rhs); + return Texts(lhs, space, Text$from_str(binop_operator(ast->tag)), space, rhs); } default: { if (inlined_fits) return inlined; diff --git a/src/formatter/utils.c b/src/formatter/utils.c index a032ec2f..fa4ec806 100644 --- a/src/formatter/utils.c +++ b/src/formatter/utils.c @@ -74,44 +74,6 @@ CONSTFUNC ast_t *unwrap_block(ast_t *ast) { return ast; } -CONSTFUNC const char *binop_tomo_operator(ast_e tag) { - switch (tag) { - case Power: return "^"; - case PowerUpdate: return "^="; - case Concat: return "++"; - case ConcatUpdate: return "++="; - case Multiply: return "*"; - case MultiplyUpdate: return "*="; - case Divide: return "/"; - case DivideUpdate: return "/="; - case Mod: return "mod"; - case ModUpdate: return "mod="; - case Mod1: return "mod1"; - case Mod1Update: return "mod1="; - case Plus: return "+"; - case PlusUpdate: return "+="; - case Minus: return "-"; - case MinusUpdate: return "-="; - case LeftShift: return "<<"; - case LeftShiftUpdate: return "<<="; - case RightShift: return ">>"; - case RightShiftUpdate: return ">>="; - case And: return "and"; - case AndUpdate: return "and="; - case Or: return "or"; - case OrUpdate: return "or="; - case Xor: return "xor"; - case XorUpdate: return "xor="; - case Equals: return "=="; - case NotEquals: return "!="; - case LessThan: return "<"; - case LessThanOrEquals: return "<="; - case GreaterThan: return ">"; - case GreaterThanOrEquals: return ">="; - default: return NULL; - } -} - OptionalText_t termify_inline(ast_t *ast, Table_t comments) { if (range_has_comment(ast->start, ast->end, comments)) return NONE_TEXT; switch (ast->tag) { diff --git a/src/formatter/utils.h b/src/formatter/utils.h index 9dc230e4..d847c2fb 100644 --- a/src/formatter/utils.h +++ b/src/formatter/utils.h @@ -26,6 +26,5 @@ CONSTFUNC bool should_have_blank_line(ast_t *ast); Text_t indent_code(Text_t code); Text_t parenthesize(Text_t code, Text_t indent); CONSTFUNC ast_t *unwrap_block(ast_t *ast); -CONSTFUNC const char *binop_tomo_operator(ast_e tag); OptionalText_t termify_inline(ast_t *ast, Table_t comments); Text_t termify(ast_t *ast, Table_t comments, Text_t indent); -- cgit v1.2.3 From e5677cb9a670ca9e1e4c5d47f3c130622c619772 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 26 Aug 2025 15:51:52 -0400 Subject: Add formatting for asserts --- src/formatter/formatter.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'src/formatter') diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c index 5dd65d04..6f0fc5aa 100644 --- a/src/formatter/formatter.c +++ b/src/formatter/formatter.c @@ -47,6 +47,13 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { /*inline*/ case ConvertDef: /*inline*/ case DocTest: return NONE_TEXT; + /*inline*/ case Assert: { + DeclareMatch(assert, ast, Assert); + Text_t expr = fmt_inline(assert->expr, comments); + if (!assert->message) return Texts("assert ", expr); + Text_t message = fmt_inline(assert->message, comments); + return Texts("assert ", expr, ", ", message); + } /*inline*/ case Lambda: { DeclareMatch(lambda, ast, Lambda); Text_t code = Texts("func(", format_inline_args(lambda->args, comments)); @@ -662,6 +669,13 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { } return code; } + /*multiline*/ case Assert: { + DeclareMatch(assert, ast, Assert); + Text_t expr = fmt(assert->expr, comments, indent); + if (!assert->message) return Texts("assert ", expr); + Text_t message = fmt(assert->message, comments, indent); + return Texts("assert ", expr, ", ", message); + } /*multiline*/ case BINOP_CASES: { if (inlined_fits) return inlined; binary_operands_t operands = BINARY_OPERANDS(ast); -- cgit v1.2.3 From 76aac20d8ed7482f4ee7f3b3a69e3e08138251fa Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 26 Aug 2025 15:54:54 -0400 Subject: Add formatting for defer --- src/formatter/formatter.c | 4 ++++ src/formatter/utils.c | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'src/formatter') diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c index 6f0fc5aa..64158876 100644 --- a/src/formatter/formatter.c +++ b/src/formatter/formatter.c @@ -54,6 +54,8 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { Text_t message = fmt_inline(assert->message, comments); return Texts("assert ", expr, ", ", message); } + /*inline*/ case Defer: + return Texts("defer ", fmt_inline(Match(ast, Defer)->body, comments)); /*inline*/ case Lambda: { DeclareMatch(lambda, ast, Lambda); Text_t code = Texts("func(", format_inline_args(lambda->args, comments)); @@ -459,6 +461,8 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { DeclareMatch(extend, ast, Extend); return Texts("lang ", Text$from_str(extend->name), format_namespace(extend->body, comments, indent)); } + /*inline*/ case Defer: + return Texts("defer ", format_namespace(Match(ast, Defer)->body, comments, indent)); /*multiline*/ case List: /*multiline*/ case Set: { if (inlined_fits) return inlined; diff --git a/src/formatter/utils.c b/src/formatter/utils.c index fa4ec806..084a6667 100644 --- a/src/formatter/utils.c +++ b/src/formatter/utils.c @@ -51,7 +51,8 @@ CONSTFUNC bool should_have_blank_line(ast_t *ast) { case EnumDef: case LangDef: case ConvertDef: - case Extend: return true; + case Extend: + case Defer: return true; default: return false; } } -- cgit v1.2.3 From bc619548d87c1a7c588998a76719f85a7c809306 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 26 Aug 2025 16:57:13 -0400 Subject: Add extern formatter --- src/formatter/formatter.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/formatter') diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c index 64158876..07974e3c 100644 --- a/src/formatter/formatter.c +++ b/src/formatter/formatter.c @@ -46,6 +46,7 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { /*inline*/ case FunctionDef: /*inline*/ case ConvertDef: /*inline*/ case DocTest: + /*inline*/ case Extern: return NONE_TEXT; /*inline*/ case Assert: { DeclareMatch(assert, ast, Assert); @@ -461,7 +462,11 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { DeclareMatch(extend, ast, Extend); return Texts("lang ", Text$from_str(extend->name), format_namespace(extend->body, comments, indent)); } - /*inline*/ case Defer: + /*multiline*/ case Extern: { + DeclareMatch(ext, ast, Extern); + return Texts("extern ", Text$from_str(ext->name), " : ", format_type(ext->type)); + } + /*multiline*/ case Defer: return Texts("defer ", format_namespace(Match(ast, Defer)->body, comments, indent)); /*multiline*/ case List: /*multiline*/ case Set: { -- cgit v1.2.3 From 442e841cfa89b3f3f7d3cf54c0c8fd5af4b48940 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 26 Aug 2025 17:36:44 -0400 Subject: Inline C code formatting --- src/formatter/formatter.c | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) (limited to 'src/formatter') diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c index 07974e3c..49670e0d 100644 --- a/src/formatter/formatter.c +++ b/src/formatter/formatter.c @@ -226,6 +226,20 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { } return Texts(code, quote); } + /*inline*/ case InlineCCode: { + DeclareMatch(c_code, ast, InlineCCode); + Text_t code = c_code->type_ast ? Texts("C_code:", format_type(c_code->type_ast), "(") : Text("C_code{"); + for (ast_list_t *chunk = c_code->chunks; chunk; chunk = chunk->next) { + if (chunk->ast->tag == TextLiteral) { + Text_t literal = Match(chunk->ast, TextLiteral)->text; + if (Text$has(literal, Text("\n"))) return NONE_TEXT; + code = Texts(code, Text$slice(Text$quoted(literal, false, Text("`")), I(2), I(-2))); + } else { + code = Texts(code, "@(", fmt_inline(chunk->ast, comments), ")"); + } + } + return Texts(code, c_code->type_ast ? Text(")") : Text("}")); + } /*inline*/ case TextLiteral: { fail("Something went wrong, we shouldn't be formatting text literals directly"); } /*inline*/ case Path: { return Texts("(", Text$from_str(Match(ast, Path)->path), ")"); } /*inline*/ case Stop: { @@ -612,7 +626,7 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { current_line = *(Text_t *)(lines.data + i * lines.stride); } } else { - code = Texts(code, "$(", fmt_inline(chunk->ast, comments), ")"); + current_line = Texts(current_line, "$(", fmt_inline(chunk->ast, comments), ")"); } } add_line(&code, current_line, Texts(indent, single_indent)); @@ -620,6 +634,35 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { if (lang) code = Texts("$", Text$from_str(lang), code); return code; } + /*multiline*/ case InlineCCode: { + DeclareMatch(c_code, ast, InlineCCode); + if (inlined_fits && c_code->type_ast) return inlined; + + Text_t code = EMPTY_TEXT; + Text_t current_line = EMPTY_TEXT; + for (ast_list_t *chunk = c_code->chunks; chunk; chunk = chunk->next) { + if (chunk->ast->tag == TextLiteral) { + Text_t literal = Match(chunk->ast, TextLiteral)->text; + List_t lines = Text$lines(literal); + if (lines.length == 0) continue; + current_line = Texts(current_line, *(Text_t *)lines.data); + for (int64_t i = 1; i < lines.length; i += 1) { + add_line(&code, current_line, Texts(indent, single_indent)); + current_line = *(Text_t *)(lines.data + i * lines.stride); + } + } else { + current_line = Texts(current_line, "@(", fmt_inline(chunk->ast, comments), ")"); + } + } + add_line(&code, current_line, Texts(indent, single_indent)); + + if (c_code->type_ast) { + return Texts("C_code:", format_type(c_code->type_ast), "(\n", indent, single_indent, code, "\n", indent, + ")"); + } else { + return Texts("C_code{\n", indent, single_indent, code, "\n", indent, "}"); + } + } /*multiline*/ case TextLiteral: { fail("Something went wrong, we shouldn't be formatting text literals directly"); } /*multiline*/ case Path: { assert(inlined.length > 0); -- cgit v1.2.3 From 43105107b9d1e985e9c182b904f2ac79b17fb460 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Fri, 29 Aug 2025 13:32:17 -0400 Subject: Improvements to text and inline C code formatting/parsing --- src/formatter/formatter.c | 152 +++++++++++++++++++++++----------------------- 1 file changed, 77 insertions(+), 75 deletions(-) (limited to 'src/formatter') diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c index 49670e0d..2d88b486 100644 --- a/src/formatter/formatter.c +++ b/src/formatter/formatter.c @@ -10,7 +10,6 @@ #include "../parse/files.h" #include "../parse/utils.h" #include "../stdlib/datatypes.h" -#include "../stdlib/integers.h" #include "../stdlib/optionals.h" #include "../stdlib/stdlib.h" #include "../stdlib/text.h" @@ -28,6 +27,66 @@ Text_t format_namespace(ast_t *namespace, Table_t comments, Text_t indent) { return Texts("\n", indent, single_indent, fmt(namespace, comments, Texts(indent, single_indent))); } +typedef struct { + Text_t quote, unquote, interp; +} text_opts_t; + +text_opts_t choose_text_options(ast_list_t *chunks) { + int double_quotes = 0, single_quotes = 0, backticks = 0; + for (ast_list_t *chunk = chunks; chunk; chunk = chunk->next) { + if (chunk->ast->tag == TextLiteral) { + Text_t literal = Match(chunk->ast, TextLiteral)->text; + if (Text$has(literal, Text("\""))) double_quotes += 1; + if (Text$has(literal, Text("'"))) single_quotes += 1; + if (Text$has(literal, Text("`"))) backticks += 1; + } + } + Text_t quote; + if (double_quotes == 0) quote = Text("\""); + else if (single_quotes == 0) quote = Text("'"); + else if (backticks == 0) quote = Text("`"); + else quote = Text("\""); + + text_opts_t opts = {.quote = quote, .unquote = quote, .interp = Text("$")}; + return opts; +} + +static OptionalText_t format_inline_text(text_opts_t opts, ast_list_t *chunks, Table_t comments) { + Text_t code = opts.quote; + for (ast_list_t *chunk = chunks; chunk; chunk = chunk->next) { + if (chunk->ast->tag == TextLiteral) { + Text_t literal = Match(chunk->ast, TextLiteral)->text; + Text_t segment = Text$escaped(literal, false, Texts(opts.unquote, opts.interp)); + code = Texts(code, segment); + } else { + code = Texts(code, opts.interp, "(", fmt_inline(chunk->ast, comments), ")"); + } + } + return Texts(code, opts.unquote); +} + +static Text_t format_text(text_opts_t opts, ast_list_t *chunks, Table_t comments, Text_t indent) { + Text_t code = EMPTY_TEXT; + Text_t current_line = EMPTY_TEXT; + for (ast_list_t *chunk = chunks; chunk; chunk = chunk->next) { + if (chunk->ast->tag == TextLiteral) { + Text_t literal = Match(chunk->ast, TextLiteral)->text; + List_t lines = Text$lines(literal); + if (lines.length == 0) continue; + current_line = Texts(current_line, Text$escaped(*(Text_t *)lines.data, false, opts.interp)); + for (int64_t i = 1; i < lines.length; i += 1) { + add_line(&code, current_line, Texts(indent, single_indent)); + current_line = Text$escaped(*(Text_t *)(lines.data + i * lines.stride), false, opts.interp); + } + } else { + current_line = Texts(current_line, opts.interp, "(", fmt(chunk->ast, comments, indent), ")"); + } + } + add_line(&code, current_line, Texts(indent, single_indent)); + code = Texts(opts.quote, "\n", indent, single_indent, code, "\n", indent, opts.unquote); + return code; +} + OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { if (range_has_comment(ast->start, ast->end, comments)) return NONE_TEXT; switch (ast->tag) { @@ -211,37 +270,21 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { else return Texts(indexed, "[]"); } /*inline*/ case TextJoin: { - // TODO: choose quotation mark more smartly - Text_t source = Text$from_strn(ast->start, (int64_t)(ast->end - ast->start)); - Text_t quote = Text$to(source, I(1)); + text_opts_t opts = choose_text_options(Match(ast, TextJoin)->children); + Text_t ret = must(format_inline_text(opts, Match(ast, TextJoin)->children, comments)); const char *lang = Match(ast, TextJoin)->lang; - Text_t code = lang ? Texts("$", Text$from_str(lang), quote) : quote; - for (ast_list_t *chunk = Match(ast, TextJoin)->children; chunk; chunk = chunk->next) { - if (chunk->ast->tag == TextLiteral) { - Text_t literal = Match(chunk->ast, TextLiteral)->text; - code = Texts(code, Text$slice(Text$quoted(literal, false, quote), I(2), I(-2))); - } else { - code = Texts(code, "$(", fmt_inline(chunk->ast, comments), ")"); - } - } - return Texts(code, quote); + return lang ? Texts("$", Text$from_str(lang), ret) : ret; } /*inline*/ case InlineCCode: { DeclareMatch(c_code, ast, InlineCCode); - Text_t code = c_code->type_ast ? Texts("C_code:", format_type(c_code->type_ast), "(") : Text("C_code{"); - for (ast_list_t *chunk = c_code->chunks; chunk; chunk = chunk->next) { - if (chunk->ast->tag == TextLiteral) { - Text_t literal = Match(chunk->ast, TextLiteral)->text; - if (Text$has(literal, Text("\n"))) return NONE_TEXT; - code = Texts(code, Text$slice(Text$quoted(literal, false, Text("`")), I(2), I(-2))); - } else { - code = Texts(code, "@(", fmt_inline(chunk->ast, comments), ")"); - } - } - return Texts(code, c_code->type_ast ? Text(")") : Text("}")); + Text_t code = c_code->type_ast ? Texts("C_code:", format_type(c_code->type_ast)) : Text("C_code"); + text_opts_t opts = {.quote = Text("`"), .unquote = Text("`"), .interp = Text("@")}; + return Texts(code, must(format_inline_text(opts, Match(ast, InlineCCode)->chunks, comments))); } /*inline*/ case TextLiteral: { fail("Something went wrong, we shouldn't be formatting text literals directly"); } - /*inline*/ case Path: { return Texts("(", Text$from_str(Match(ast, Path)->path), ")"); } + /*inline*/ case Path: { + return Texts("(", Text$escaped(Text$from_str(Match(ast, Path)->path), false, Text("()")), ")"); + } /*inline*/ case Stop: { const char *target = Match(ast, Stop)->target; return target ? Texts("stop ", Text$from_str(target)) : Text("stop"); @@ -609,59 +652,18 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { } /*multiline*/ case TextJoin: { if (inlined_fits) return inlined; - // TODO: choose quotation mark more smartly - Text_t source = Text$from_strn(ast->start, (int64_t)(ast->end - ast->start)); - Text_t quote = Text$to(source, I(1)); + + text_opts_t opts = choose_text_options(Match(ast, TextJoin)->children); + Text_t ret = format_text(opts, Match(ast, TextJoin)->children, comments, indent); const char *lang = Match(ast, TextJoin)->lang; - Text_t code = EMPTY_TEXT; - Text_t current_line = EMPTY_TEXT; - for (ast_list_t *chunk = Match(ast, TextJoin)->children; chunk; chunk = chunk->next) { - if (chunk->ast->tag == TextLiteral) { - Text_t literal = Match(chunk->ast, TextLiteral)->text; - List_t lines = Text$lines(literal); - if (lines.length == 0) continue; - current_line = Texts(current_line, *(Text_t *)lines.data); - for (int64_t i = 1; i < lines.length; i += 1) { - add_line(&code, current_line, Texts(indent, single_indent)); - current_line = *(Text_t *)(lines.data + i * lines.stride); - } - } else { - current_line = Texts(current_line, "$(", fmt_inline(chunk->ast, comments), ")"); - } - } - add_line(&code, current_line, Texts(indent, single_indent)); - code = Texts(quote, "\n", indent, single_indent, code, "\n", indent, quote); - if (lang) code = Texts("$", Text$from_str(lang), code); - return code; + return lang ? Texts("$", Text$from_str(lang), ret) : ret; } /*multiline*/ case InlineCCode: { DeclareMatch(c_code, ast, InlineCCode); - if (inlined_fits && c_code->type_ast) return inlined; - - Text_t code = EMPTY_TEXT; - Text_t current_line = EMPTY_TEXT; - for (ast_list_t *chunk = c_code->chunks; chunk; chunk = chunk->next) { - if (chunk->ast->tag == TextLiteral) { - Text_t literal = Match(chunk->ast, TextLiteral)->text; - List_t lines = Text$lines(literal); - if (lines.length == 0) continue; - current_line = Texts(current_line, *(Text_t *)lines.data); - for (int64_t i = 1; i < lines.length; i += 1) { - add_line(&code, current_line, Texts(indent, single_indent)); - current_line = *(Text_t *)(lines.data + i * lines.stride); - } - } else { - current_line = Texts(current_line, "@(", fmt_inline(chunk->ast, comments), ")"); - } - } - add_line(&code, current_line, Texts(indent, single_indent)); - - if (c_code->type_ast) { - return Texts("C_code:", format_type(c_code->type_ast), "(\n", indent, single_indent, code, "\n", indent, - ")"); - } else { - return Texts("C_code{\n", indent, single_indent, code, "\n", indent, "}"); - } + if (inlined_fits && c_code->type != NULL) return inlined; + Text_t code = c_code->type_ast ? Texts("C_code:", format_type(c_code->type_ast)) : Text("C_code"); + text_opts_t opts = {.quote = Text("`"), .unquote = Text("`"), .interp = Text("@")}; + return Texts(code, format_text(opts, Match(ast, InlineCCode)->chunks, comments, indent)); } /*multiline*/ case TextLiteral: { fail("Something went wrong, we shouldn't be formatting text literals directly"); } /*multiline*/ case Path: { -- cgit v1.2.3 From 190810fcb1144cd29a676e15dd8d1a414072862d Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Fri, 29 Aug 2025 13:38:32 -0400 Subject: Add formatting for the last few cases --- src/formatter/formatter.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'src/formatter') diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c index 2d88b486..89535071 100644 --- a/src/formatter/formatter.c +++ b/src/formatter/formatter.c @@ -352,6 +352,22 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { Text_t space = op_tightness[ast->tag] >= op_tightness[Multiply] ? EMPTY_TEXT : Text(" "); return Texts(lhs, space, Text$from_str(binop_operator(ast->tag)), space, rhs); } + /*inline*/ case Deserialize: { + DeclareMatch(deserialize, ast, Deserialize); + return Texts("deserialize(", fmt_inline(deserialize->value, comments), " -> ", format_type(deserialize->type), + ")"); + } + /*inline*/ case Use: { + DeclareMatch(use, ast, Use); + // struct { + // ast_t *var; + // const char *path; + // enum { USE_LOCAL, USE_MODULE, USE_SHARED_OBJECT, USE_HEADER, USE_C_CODE, USE_ASM } what; + // } Use; + return Texts("use ", use->path); + } + /*inline*/ case ExplicitlyTyped: + fail("Explicitly typed AST nodes are only meant to be used internally."); default: { fail("Formatting not implemented for: ", ast_to_sexp(ast)); } @@ -749,6 +765,18 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { Text_t space = op_tightness[ast->tag] >= op_tightness[Multiply] ? EMPTY_TEXT : Text(" "); return Texts(lhs, space, Text$from_str(binop_operator(ast->tag)), space, rhs); } + /*multiline*/ case Deserialize: { + if (inlined_fits) return inlined; + DeclareMatch(deserialize, ast, Deserialize); + return Texts("deserialize(", fmt(deserialize->value, comments, indent), " -> ", format_type(deserialize->type), + ")"); + } + /*multiline*/ case Use: { + assert(inlined.length > 0); + return inlined; + } + /*multiline*/ case ExplicitlyTyped: + fail("Explicitly typed AST nodes are only meant to be used internally."); default: { if (inlined_fits) return inlined; fail("Formatting not implemented for: ", ast_to_sexp(ast)); -- cgit v1.2.3 From 2d3021728bf5c74f8bf6854c1ae4951a6ebbf9c7 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Fri, 29 Aug 2025 14:04:55 -0400 Subject: Formatting tweaks/fixes --- src/formatter/formatter.c | 11 +++++++---- src/formatter/utils.c | 13 +++++++------ src/formatter/utils.h | 2 +- 3 files changed, 15 insertions(+), 11 deletions(-) (limited to 'src/formatter') diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c index 89535071..161c70d4 100644 --- a/src/formatter/formatter.c +++ b/src/formatter/formatter.c @@ -386,7 +386,8 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { bool gap_before_comment = false; const char *comment_pos = ast->start; for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) { - if (should_have_blank_line(stmt->ast)) add_line(&code, Text(""), indent); + for (int blanks = suggested_blank_lines(stmt->ast); blanks > 0; blanks--) + add_line(&code, Text(""), indent); for (OptionalText_t comment; (comment = next_comment(comments, &comment_pos, stmt->ast->start)).length > 0;) { @@ -400,9 +401,11 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { add_line(&code, fmt(stmt->ast, comments, indent), indent); comment_pos = stmt->ast->end; - if (should_have_blank_line(stmt->ast) && stmt->next && !should_have_blank_line(stmt->next->ast)) - add_line(&code, Text(""), indent); - else gap_before_comment = true; + if (stmt->next) { + for (int blanks = suggested_blank_lines(stmt->ast) - suggested_blank_lines(stmt->next->ast); blanks > 0; + blanks--) + add_line(&code, Text(""), indent); + } else gap_before_comment = true; } for (OptionalText_t comment; (comment = next_comment(comments, &comment_pos, ast->end)).length > 0;) { diff --git a/src/formatter/utils.c b/src/formatter/utils.c index 084a6667..1f4e64d8 100644 --- a/src/formatter/utils.c +++ b/src/formatter/utils.c @@ -38,7 +38,7 @@ bool range_has_comment(const char *start, const char *end, Table_t comments) { return (comment.length >= 0); } -CONSTFUNC bool should_have_blank_line(ast_t *ast) { +CONSTFUNC int suggested_blank_lines(ast_t *ast) { switch (ast->tag) { case If: case When: @@ -46,14 +46,15 @@ CONSTFUNC bool should_have_blank_line(ast_t *ast) { case While: case For: case Block: - case FunctionDef: + case Defer: return 1; + case Use: return 1; + case ConvertDef: + case FunctionDef: return 1; case StructDef: case EnumDef: case LangDef: - case ConvertDef: - case Extend: - case Defer: return true; - default: return false; + case Extend: return 2; + default: return 0; } } diff --git a/src/formatter/utils.h b/src/formatter/utils.h index d847c2fb..0a00be6c 100644 --- a/src/formatter/utils.h +++ b/src/formatter/utils.h @@ -22,7 +22,7 @@ extern const Text_t single_indent; void add_line(Text_t *code, Text_t line, Text_t indent); OptionalText_t next_comment(Table_t comments, const char **pos, const char *end); bool range_has_comment(const char *start, const char *end, Table_t comments); -CONSTFUNC bool should_have_blank_line(ast_t *ast); +CONSTFUNC int suggested_blank_lines(ast_t *ast); Text_t indent_code(Text_t code); Text_t parenthesize(Text_t code, Text_t indent); CONSTFUNC ast_t *unwrap_block(ast_t *ast); -- cgit v1.2.3 From 16abb9016866782840127240279c42122a60d5d8 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sat, 30 Aug 2025 13:52:59 -0400 Subject: More compact multi-line lists/sets/tables --- src/formatter/formatter.c | 44 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 9 deletions(-) (limited to 'src/formatter') diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c index 161c70d4..a120b9de 100644 --- a/src/formatter/formatter.c +++ b/src/formatter/formatter.c @@ -374,6 +374,17 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { } } +static int64_t trailing_line_len(Text_t text) { + TextIter_t state = NEW_TEXT_ITER_STATE(text); + int64_t len = 0; + for (int64_t i = text.length - 1; i >= 0; i--) { + int32_t g = Text$get_grapheme_fast(&state, i); + if (g == '\n' || g == '\r') break; + len += 1; + } + return len; +} + Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { OptionalText_t inlined = format_inline_code(ast, comments); bool inlined_fits = (inlined.length >= 0 && indent.length + inlined.length <= MAX_WIDTH); @@ -548,34 +559,49 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { /*multiline*/ case Set: { if (inlined_fits) return inlined; ast_list_t *items = ast->tag == List ? Match(ast, List)->items : Match(ast, Set)->items; - Text_t code = EMPTY_TEXT; + Text_t code = ast->tag == List ? Text("[") : Text("|"); const char *comment_pos = ast->start; for (ast_list_t *item = items; item; item = item->next) { for (OptionalText_t comment; (comment = next_comment(comments, &comment_pos, item->ast->start)).length > 0;) { add_line(&code, Text$trim(comment, Text(" \t\r\n"), false, true), Texts(indent, single_indent)); } - add_line(&code, Texts(fmt(item->ast, comments, Texts(indent, single_indent)), ","), - Texts(indent, single_indent)); + Text_t item_text = fmt(item->ast, comments, Texts(indent, single_indent)); + if (Text$ends_with(code, Text(","), NULL)) { + if (!Text$has(item_text, Text("\n")) && trailing_line_len(code) + 1 + item_text.length + 1 <= MAX_WIDTH) + code = Texts(code, " ", item_text, ","); + else code = Texts(code, "\n", indent, single_indent, item_text, ","); + } else { + add_line(&code, Texts(item_text, ","), Texts(indent, single_indent)); + } } for (OptionalText_t comment; (comment = next_comment(comments, &comment_pos, ast->end)).length > 0;) { add_line(&code, Text$trim(comment, Text(" \t\r\n"), false, true), Texts(indent, single_indent)); } - return ast->tag == List ? Texts("[\n", indent, single_indent, code, "\n", indent, "]") - : Texts("|\n", indent, single_indent, code, "\n", indent, "|"); + return ast->tag == List ? Texts(code, "\n", indent, "]") : Texts(code, "\n", indent, "|"); } /*multiline*/ case Table: { if (inlined_fits) return inlined; DeclareMatch(table, ast, Table); - Text_t code = EMPTY_TEXT; + Text_t code = Texts("{"); const char *comment_pos = ast->start; for (ast_list_t *entry = table->entries; entry; entry = entry->next) { for (OptionalText_t comment; (comment = next_comment(comments, &comment_pos, entry->ast->start)).length > 0;) { add_line(&code, Text$trim(comment, Text(" \t\r\n"), false, true), Texts(indent, single_indent)); } - add_line(&code, Texts(fmt(entry->ast, comments, Texts(indent, single_indent)), ","), - Texts(indent, single_indent)); + + Text_t entry_text = fmt(entry->ast, comments, Texts(indent, single_indent)); + if (Text$ends_with(code, Text(","), NULL)) { + if (!Text$has(entry_text, Text("\n")) + && trailing_line_len(code) + 1 + entry_text.length + 1 <= MAX_WIDTH) + code = Texts(code, " ", entry_text, ","); + else code = Texts(code, "\n", indent, single_indent, entry_text, ","); + } else { + add_line(&code, Texts(entry_text, ","), Texts(indent, single_indent)); + } + + add_line(&code, Texts(entry_text, ","), Texts(indent, single_indent)); } for (OptionalText_t comment; (comment = next_comment(comments, &comment_pos, ast->end)).length > 0;) { add_line(&code, Text$trim(comment, Text(" \t\r\n"), false, true), Texts(indent, single_indent)); @@ -587,7 +613,7 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { if (table->default_value) code = Texts(code, ";\n", indent, single_indent, "default=", fmt(table->default_value, comments, indent)); - return Texts("{\n", indent, single_indent, code, "\n", indent, "}"); + return Texts(code, "\n", indent, "}"); } /*multiline*/ case TableEntry: { if (inlined_fits) return inlined; -- cgit v1.2.3 From 565527c9a6c5aa0161f421066ac2e07357b34571 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sat, 30 Aug 2025 13:54:42 -0400 Subject: Avoid inline ifs --- src/formatter/formatter.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/formatter') diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c index a120b9de..5e34960f 100644 --- a/src/formatter/formatter.c +++ b/src/formatter/formatter.c @@ -430,8 +430,6 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { } /*multiline*/ case If: { DeclareMatch(if_, ast, If); - if (inlined_fits && if_->else_body == NULL) return inlined; - Text_t code = Texts("if ", fmt(if_->condition, comments, indent), "\n", indent, single_indent, fmt(if_->body, comments, Texts(indent, single_indent))); if (if_->else_body) { -- cgit v1.2.3 From db4048734179c047d64284281f72a531f166f11e Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sat, 30 Aug 2025 13:57:48 -0400 Subject: Tweak arg formatting --- src/formatter/args.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/formatter') diff --git a/src/formatter/args.c b/src/formatter/args.c index 6bd07b28..8bbbbd58 100644 --- a/src/formatter/args.c +++ b/src/formatter/args.c @@ -13,7 +13,7 @@ OptionalText_t format_inline_arg(arg_ast_t *arg, Table_t comments) { if (arg->name == NULL && arg->value) return must(format_inline_code(arg->value, comments)); Text_t code = Text$from_str(arg->name); if (arg->type) code = Texts(code, ":", must(format_type(arg->type))); - if (arg->value) code = Texts(code, " = ", must(format_inline_code(arg->value, comments))); + if (arg->value) code = Texts(code, "=", must(format_inline_code(arg->value, comments))); return code; } @@ -23,7 +23,7 @@ Text_t format_arg(arg_ast_t *arg, Table_t comments, Text_t indent) { if (arg->name == NULL && arg->value) return format_code(arg->value, comments, indent); Text_t code = Text$from_str(arg->name); if (arg->type) code = Texts(code, ":", format_type(arg->type)); - if (arg->value) code = Texts(code, " = ", format_code(arg->value, comments, indent)); + if (arg->value) code = Texts(code, "=", format_code(arg->value, comments, indent)); return code; } -- cgit v1.2.3 From 351a79b5d7c20f806b66fed19407677402ad0805 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sat, 30 Aug 2025 13:59:51 -0400 Subject: Formatting tweak for enums --- src/formatter/enums.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'src/formatter') diff --git a/src/formatter/enums.c b/src/formatter/enums.c index 7a89705d..893f055b 100644 --- a/src/formatter/enums.c +++ b/src/formatter/enums.c @@ -9,18 +9,25 @@ OptionalText_t format_inline_tag(tag_ast_t *tag, Table_t comments) { if (range_has_comment(tag->start, tag->end, comments)) return NONE_TEXT; - Text_t code = Texts(Text$from_str(tag->name), "(", must(format_inline_args(tag->fields, comments))); - if (tag->secret) code = Texts(code, "; secret"); - return Texts(code, ")"); + Text_t code = Text$from_str(tag->name); + if (tag->fields || tag->secret) { + code = Texts(code, "(", must(format_inline_args(tag->fields, comments))); + if (tag->secret) code = Texts(code, "; secret"); + code = Texts(code, ")"); + } + return code; } Text_t format_tag(tag_ast_t *tag, Table_t comments, Text_t indent) { OptionalText_t inline_tag = format_inline_tag(tag, comments); if (inline_tag.length >= 0) return inline_tag; - Text_t code = - Texts(Text$from_str(tag->name), "(", format_args(tag->fields, comments, Texts(indent, single_indent))); - if (tag->secret) code = Texts(code, "; secret"); - return Texts(code, ")"); + Text_t code = Text$from_str(tag->name); + if (tag->fields || tag->secret) { + code = Texts(code, "(", format_args(tag->fields, comments, Texts(indent, single_indent))); + if (tag->secret) code = Texts(code, "; secret"); + code = Texts(code, ")"); + } + return code; } OptionalText_t format_inline_tags(tag_ast_t *tags, Table_t comments) { -- cgit v1.2.3 From 08a85f2450d9ccaced45322b63c8ac234c2df9e4 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sat, 30 Aug 2025 14:11:02 -0400 Subject: Use `"$var"` for interps instead of `"$(var)"` when possible --- src/formatter/formatter.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'src/formatter') diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c index 5e34960f..e22fc609 100644 --- a/src/formatter/formatter.c +++ b/src/formatter/formatter.c @@ -4,12 +4,14 @@ #include #include #include +#include #include "../ast.h" #include "../parse/context.h" #include "../parse/files.h" #include "../parse/utils.h" #include "../stdlib/datatypes.h" +#include "../stdlib/integers.h" #include "../stdlib/optionals.h" #include "../stdlib/stdlib.h" #include "../stdlib/text.h" @@ -51,6 +53,11 @@ text_opts_t choose_text_options(ast_list_t *chunks) { return opts; } +static bool starts_with_id(Text_t text) { + List_t codepoints = Text$utf32_codepoints(Text$slice(text, I_small(1), I_small(1))); + return uc_is_property_xid_continue(*(ucs4_t *)codepoints.data); +} + static OptionalText_t format_inline_text(text_opts_t opts, ast_list_t *chunks, Table_t comments) { Text_t code = opts.quote; for (ast_list_t *chunk = chunks; chunk; chunk = chunk->next) { @@ -59,7 +66,13 @@ static OptionalText_t format_inline_text(text_opts_t opts, ast_list_t *chunks, T Text_t segment = Text$escaped(literal, false, Texts(opts.unquote, opts.interp)); code = Texts(code, segment); } else { - code = Texts(code, opts.interp, "(", fmt_inline(chunk->ast, comments), ")"); + if (chunk->ast->tag == Var + && (!chunk->next || chunk->next->ast->tag != TextLiteral + || !starts_with_id(Match(chunk->next->ast, TextLiteral)->text))) { + code = Texts(code, opts.interp, fmt_inline(chunk->ast, comments)); + } else { + code = Texts(code, opts.interp, "(", fmt_inline(chunk->ast, comments), ")"); + } } } return Texts(code, opts.unquote); -- cgit v1.2.3 From 9f17271a07f10f05e63e0064d8ab4a92267f5f66 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sat, 30 Aug 2025 14:14:42 -0400 Subject: Use `unless` instead of `if not` --- src/formatter/formatter.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'src/formatter') diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c index e22fc609..8b8aeb1b 100644 --- a/src/formatter/formatter.c +++ b/src/formatter/formatter.c @@ -140,17 +140,21 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { /*inline*/ case If: { DeclareMatch(if_, ast, If); + Text_t if_condition = if_->condition->tag == Not + ? Texts("unless ", fmt_inline(Match(if_->condition, Not)->value, comments)) + : Texts("if ", fmt_inline(if_->condition, comments)); + if (if_->else_body == NULL && if_->condition->tag != Declare) { ast_t *stmt = unwrap_block(if_->body); switch (stmt->tag) { case Return: case Skip: - case Stop: return Texts(fmt_inline(stmt, comments), " if ", fmt_inline(if_->condition, comments)); + case Stop: return Texts(fmt_inline(stmt, comments), " ", if_condition); default: break; } } - Text_t code = Texts("if ", fmt_inline(if_->condition, comments), " then ", fmt_inline(if_->body, comments)); + Text_t code = Texts(if_condition, " then ", fmt_inline(if_->body, comments)); if (if_->else_body) code = Texts(code, " else ", fmt_inline(if_->else_body, comments)); return code; } @@ -443,8 +447,11 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { } /*multiline*/ case If: { DeclareMatch(if_, ast, If); - Text_t code = Texts("if ", fmt(if_->condition, comments, indent), "\n", indent, single_indent, - fmt(if_->body, comments, Texts(indent, single_indent))); + Text_t code = if_->condition->tag == Not + ? Texts("unless ", fmt(Match(if_->condition, Not)->value, comments, indent)) + : Texts("if ", fmt(if_->condition, comments, indent)); + + code = Texts(code, "\n", indent, single_indent, fmt(if_->body, comments, Texts(indent, single_indent))); if (if_->else_body) { if (if_->else_body->tag != If) { code = Texts(code, "\n", indent, "else\n", indent, single_indent, -- cgit v1.2.3 From f2ad722780f96dc5fb7d93f536c7267741be3f6f Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sat, 30 Aug 2025 14:29:50 -0400 Subject: Fix line gaps --- src/formatter/formatter.c | 6 +----- src/formatter/utils.c | 36 +++++++++++++++++++++++++++++------- src/formatter/utils.h | 2 +- 3 files changed, 31 insertions(+), 13 deletions(-) (limited to 'src/formatter') diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c index 8b8aeb1b..98f0f59b 100644 --- a/src/formatter/formatter.c +++ b/src/formatter/formatter.c @@ -414,9 +414,6 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { bool gap_before_comment = false; const char *comment_pos = ast->start; for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) { - for (int blanks = suggested_blank_lines(stmt->ast); blanks > 0; blanks--) - add_line(&code, Text(""), indent); - for (OptionalText_t comment; (comment = next_comment(comments, &comment_pos, stmt->ast->start)).length > 0;) { if (gap_before_comment) { @@ -430,8 +427,7 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { comment_pos = stmt->ast->end; if (stmt->next) { - for (int blanks = suggested_blank_lines(stmt->ast) - suggested_blank_lines(stmt->next->ast); blanks > 0; - blanks--) + for (int blanks = suggested_blank_lines(stmt->ast, stmt->next->ast); blanks > 0; blanks--) add_line(&code, Text(""), indent); } else gap_before_comment = true; } diff --git a/src/formatter/utils.c b/src/formatter/utils.c index 1f4e64d8..5619e6ce 100644 --- a/src/formatter/utils.c +++ b/src/formatter/utils.c @@ -38,24 +38,45 @@ bool range_has_comment(const char *start, const char *end, Table_t comments) { return (comment.length >= 0); } -CONSTFUNC int suggested_blank_lines(ast_t *ast) { - switch (ast->tag) { +CONSTFUNC int suggested_blank_lines(ast_t *first, ast_t *second) { + if (second == NULL) return 0; + + if (first->tag == Use && second->tag != Use) return 1; + + switch (first->tag) { + case If: + case When: + case Repeat: + case While: + case For: + case Block: + case Defer: + case ConvertDef: + case FunctionDef: + case StructDef: + case EnumDef: + case LangDef: + case Extend: return 1; + default: break; + } + + switch (second->tag) { case If: case When: case Repeat: case While: case For: case Block: - case Defer: return 1; - case Use: return 1; + case Defer: case ConvertDef: - case FunctionDef: return 1; + case FunctionDef: case StructDef: case EnumDef: case LangDef: - case Extend: return 2; - default: return 0; + case Extend: return 1; + default: break; } + return 0; } Text_t indent_code(Text_t code) { @@ -73,6 +94,7 @@ CONSTFUNC ast_t *unwrap_block(ast_t *ast) { while (ast->tag == Block && Match(ast, Block)->statements && Match(ast, Block)->statements->next == NULL) { ast = Match(ast, Block)->statements->ast; } + if (ast->tag == Block && Match(ast, Block)->statements == NULL) return NULL; return ast; } diff --git a/src/formatter/utils.h b/src/formatter/utils.h index 0a00be6c..880da0a9 100644 --- a/src/formatter/utils.h +++ b/src/formatter/utils.h @@ -22,7 +22,7 @@ extern const Text_t single_indent; void add_line(Text_t *code, Text_t line, Text_t indent); OptionalText_t next_comment(Table_t comments, const char **pos, const char *end); bool range_has_comment(const char *start, const char *end, Table_t comments); -CONSTFUNC int suggested_blank_lines(ast_t *ast); +CONSTFUNC int suggested_blank_lines(ast_t *first, ast_t *second); Text_t indent_code(Text_t code); Text_t parenthesize(Text_t code, Text_t indent); CONSTFUNC ast_t *unwrap_block(ast_t *ast); -- cgit v1.2.3 From 430b1f0a7df8055027fbfe006a7285cc9438e5ac Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sat, 30 Aug 2025 14:31:25 -0400 Subject: Fix indent for doctests --- src/formatter/formatter.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/formatter') diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c index 98f0f59b..675a88a9 100644 --- a/src/formatter/formatter.c +++ b/src/formatter/formatter.c @@ -774,11 +774,11 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { /*multiline*/ case DocTest: { DeclareMatch(test, ast, DocTest); Text_t expr = fmt(test->expr, comments, indent); - Text_t code = Texts(">> ", Text$replace(expr, Texts("\n", indent), Texts("\n", indent, ".. "))); + Text_t code = Texts(">> ", Text$replace(expr, Texts("\n", indent), Texts("\n", indent, single_indent))); if (test->expected) { Text_t expected = fmt(test->expected, comments, indent); code = Texts(code, "\n", indent, "= ", - Text$replace(expected, Texts("\n", indent), Texts("\n", indent, ".. "))); + Text$replace(expected, Texts("\n", indent), Texts("\n", indent, single_indent))); } return code; } -- cgit v1.2.3 From a9df553f3765710c6833fe9705832b1a3bc5b64c Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sat, 30 Aug 2025 14:32:25 -0400 Subject: Further fix for doctests --- src/formatter/formatter.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/formatter') diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c index 675a88a9..88eb210a 100644 --- a/src/formatter/formatter.c +++ b/src/formatter/formatter.c @@ -774,11 +774,10 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { /*multiline*/ case DocTest: { DeclareMatch(test, ast, DocTest); Text_t expr = fmt(test->expr, comments, indent); - Text_t code = Texts(">> ", Text$replace(expr, Texts("\n", indent), Texts("\n", indent, single_indent))); + Text_t code = Texts(">> ", expr); if (test->expected) { Text_t expected = fmt(test->expected, comments, indent); - code = Texts(code, "\n", indent, "= ", - Text$replace(expected, Texts("\n", indent), Texts("\n", indent, single_indent))); + code = Texts(code, "\n", indent, "= ", expected); } return code; } -- cgit v1.2.3 From 1824f840c1564c59eaecfaa25556d4e830d867bc Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sat, 30 Aug 2025 14:36:28 -0400 Subject: Add 'do' wrapper on blocks --- src/formatter/formatter.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/formatter') diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c index 88eb210a..266525e8 100644 --- a/src/formatter/formatter.c +++ b/src/formatter/formatter.c @@ -423,7 +423,13 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { add_line(&code, Text$trim(comment, Text(" \t\r\n"), false, true), indent); } - add_line(&code, fmt(stmt->ast, comments, indent), indent); + if (stmt->ast->tag == Block) { + add_line(&code, + Texts("do\n", indent, single_indent, fmt(stmt->ast, comments, Texts(indent, single_indent))), + indent); + } else { + add_line(&code, fmt(stmt->ast, comments, indent), indent); + } comment_pos = stmt->ast->end; if (stmt->next) { -- cgit v1.2.3 From ca3b72d1f5a230cc5b25edd55918864c2df248b5 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sat, 30 Aug 2025 14:44:13 -0400 Subject: Tweaks for blank lines --- src/formatter/utils.c | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) (limited to 'src/formatter') diff --git a/src/formatter/utils.c b/src/formatter/utils.c index 5619e6ce..80445ec3 100644 --- a/src/formatter/utils.c +++ b/src/formatter/utils.c @@ -41,7 +41,21 @@ bool range_has_comment(const char *start, const char *end, Table_t comments) { CONSTFUNC int suggested_blank_lines(ast_t *first, ast_t *second) { if (second == NULL) return 0; - if (first->tag == Use && second->tag != Use) return 1; + for (;;) { + if (first->tag == Declare && Match(first, Declare)->value) { + first = Match(first, Declare)->value; + } else if (first->tag == DocTest && Match(first, DocTest)->expr && Match(first, DocTest)->expected == NULL) { + first = Match(first, DocTest)->expr; + } else break; + } + + for (;;) { + if (second->tag == Declare && Match(second, Declare)->value) { + second = Match(second, Declare)->value; + } else if (second->tag == DocTest && Match(second, DocTest)->expr && Match(second, DocTest)->expected == NULL) { + second = Match(second, DocTest)->expr; + } else break; + } switch (first->tag) { case If: @@ -53,10 +67,27 @@ CONSTFUNC int suggested_blank_lines(ast_t *first, ast_t *second) { case Defer: case ConvertDef: case FunctionDef: + case Lambda: case StructDef: case EnumDef: case LangDef: case Extend: return 1; + case Use: { + if (second->tag != Use) return 1; + break; + } + case Declare: { + DeclareMatch(decl, first, Declare); + if (decl->value) return suggested_blank_lines(decl->value, second); + break; + } + case Assign: { + DeclareMatch(assign, first, Assign); + for (ast_list_t *val = assign->values; val; val = val->next) { + if (suggested_blank_lines(val->ast, second) > 0) return 1; + } + break; + } default: break; } @@ -70,6 +101,7 @@ CONSTFUNC int suggested_blank_lines(ast_t *first, ast_t *second) { case Defer: case ConvertDef: case FunctionDef: + case Lambda: case StructDef: case EnumDef: case LangDef: -- cgit v1.2.3 From 688b0ae7bd433c4d7daf96505581f90e719f033c Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sat, 30 Aug 2025 15:04:44 -0400 Subject: Formatting tweaks to argument lists --- src/formatter/args.c | 3 ++- src/formatter/formatter.c | 22 ++++++++++++++-------- 2 files changed, 16 insertions(+), 9 deletions(-) (limited to 'src/formatter') diff --git a/src/formatter/args.c b/src/formatter/args.c index 8bbbbd58..34d234df 100644 --- a/src/formatter/args.c +++ b/src/formatter/args.c @@ -49,7 +49,8 @@ Text_t format_args(arg_ast_t *args, Table_t comments, Text_t indent) { if (args->name && args->next && args->type == args->next->type && args->value == args->next->value) { code = Texts(code, Text$from_str(args->name), ","); } else { - add_line(&code, Texts(format_arg(args, comments, indent), ","), indent); + code = Texts(code, "\n", indent, single_indent, + format_arg(args, comments, Texts(indent, single_indent, single_indent)), ","); } } return code; diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c index 266525e8..addf8445 100644 --- a/src/formatter/formatter.c +++ b/src/formatter/formatter.c @@ -526,7 +526,8 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { if (func->ret_type) code = Texts(code, func->args ? Text(" -> ") : Text("-> "), format_type(func->ret_type)); if (func->cache) code = Texts(code, "; cache=", fmt(func->cache, comments, indent)); if (func->is_inline) code = Texts(code, "; inline"); - code = Texts(code, ")\n", indent, single_indent, fmt(func->body, comments, Texts(indent, single_indent))); + code = Texts(code, Text$has(code, Text("\n")) ? Texts("\n", indent, ")") : Text(")"), "\n", indent, + single_indent, fmt(func->body, comments, Texts(indent, single_indent))); return Texts(code); } /*multiline*/ case Lambda: { @@ -535,7 +536,8 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { Text_t code = Texts("func(", format_args(lambda->args, comments, indent)); if (lambda->ret_type) code = Texts(code, lambda->args ? Text(" -> ") : Text("-> "), format_type(lambda->ret_type)); - code = Texts(code, ")\n", indent, single_indent, fmt(lambda->body, comments, Texts(indent, single_indent))); + code = Texts(code, Text$has(code, Text("\n")) ? Texts("\n", indent, ")") : Text(")"), "\n", indent, + single_indent, fmt(lambda->body, comments, Texts(indent, single_indent))); return Texts(code); } /*multiline*/ case ConvertDef: { @@ -545,21 +547,25 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { code = Texts(code, convert->args ? Text(" -> ") : Text("-> "), format_type(convert->ret_type)); if (convert->cache) code = Texts(code, "; cache=", fmt(convert->cache, comments, indent)); if (convert->is_inline) code = Texts(code, "; inline"); - code = Texts(code, ")\n", indent, single_indent, fmt(convert->body, comments, Texts(indent, single_indent))); + code = Texts(code, Text$has(code, Text("\n")) ? Texts("\n", indent, ")") : Text(")"), "\n", indent, + single_indent, fmt(convert->body, comments, Texts(indent, single_indent))); return Texts(code); } /*multiline*/ case StructDef: { DeclareMatch(def, ast, StructDef); - Text_t code = Texts("struct ", Text$from_str(def->name), "(", format_args(def->fields, comments, indent)); + Text_t args = format_args(def->fields, comments, indent); + Text_t code = Texts("struct ", Text$from_str(def->name), "(", args); if (def->secret) code = Texts(code, "; secret"); if (def->external) code = Texts(code, "; external"); if (def->opaque) code = Texts(code, "; opaque"); - return Texts(code, ")", format_namespace(def->namespace, comments, indent)); + code = Texts(code, Text$has(code, Text("\n")) ? Texts("\n", indent, ")") : Text(")")); + return Texts(code, format_namespace(def->namespace, comments, indent)); } /*multiline*/ case EnumDef: { DeclareMatch(def, ast, EnumDef); Text_t code = Texts("enum ", Text$from_str(def->name), "(", format_tags(def->tags, comments, indent)); - return Texts(code, ")", format_namespace(def->namespace, comments, indent)); + return Texts(code, Text$has(code, Text("\n")) ? Texts("\n", indent, ")") : Text(")"), + format_namespace(def->namespace, comments, indent)); } /*multiline*/ case LangDef: { DeclareMatch(def, ast, LangDef); @@ -769,13 +775,13 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { if (inlined_fits) return inlined; DeclareMatch(call, ast, FunctionCall); return Texts(fmt(call->fn, comments, indent), "(\n", indent, single_indent, - format_args(call->args, comments, Texts(indent, single_indent)), "\n", indent, ")"); + format_args(call->args, comments, indent), "\n", Texts(indent, single_indent), ")"); } /*multiline*/ case MethodCall: { if (inlined_fits) return inlined; DeclareMatch(call, ast, MethodCall); return Texts(termify(call->self, comments, indent), ".", Text$from_str(call->name), "(\n", indent, - single_indent, format_args(call->args, comments, Texts(indent, single_indent)), "\n", indent, ")"); + single_indent, format_args(call->args, comments, indent), "\n", Texts(indent, single_indent), ")"); } /*multiline*/ case DocTest: { DeclareMatch(test, ast, DocTest); -- cgit v1.2.3 From f38fa3389c881ef8341011c05d7b2435686068e7 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sat, 30 Aug 2025 15:54:15 -0400 Subject: Arg indentation tweak --- src/formatter/args.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/formatter') diff --git a/src/formatter/args.c b/src/formatter/args.c index 34d234df..9809645f 100644 --- a/src/formatter/args.c +++ b/src/formatter/args.c @@ -49,8 +49,8 @@ Text_t format_args(arg_ast_t *args, Table_t comments, Text_t indent) { if (args->name && args->next && args->type == args->next->type && args->value == args->next->value) { code = Texts(code, Text$from_str(args->name), ","); } else { - code = Texts(code, "\n", indent, single_indent, - format_arg(args, comments, Texts(indent, single_indent, single_indent)), ","); + code = + Texts(code, "\n", indent, single_indent, format_arg(args, comments, Texts(indent, single_indent)), ","); } } return code; -- cgit v1.2.3 From 9090a2a09f119510f40c5385f3790cc6b6539abc Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sat, 30 Aug 2025 15:59:57 -0400 Subject: Arg formatting tweaks --- src/formatter/args.c | 22 +++++++++++----------- src/formatter/formatter.c | 10 ++++++---- 2 files changed, 17 insertions(+), 15 deletions(-) (limited to 'src/formatter') diff --git a/src/formatter/args.c b/src/formatter/args.c index 9809645f..997a1e39 100644 --- a/src/formatter/args.c +++ b/src/formatter/args.c @@ -29,14 +29,14 @@ Text_t format_arg(arg_ast_t *arg, Table_t comments, Text_t indent) { OptionalText_t format_inline_args(arg_ast_t *args, Table_t comments) { Text_t code = EMPTY_TEXT; - for (; args; args = args->next) { - if (args->name && args->next && args->type == args->next->type && args->value == args->next->value) { - code = Texts(code, Text$from_str(args->name), ","); + for (arg_ast_t *arg = args; arg; arg = arg->next) { + if (arg->name && arg->next && arg->type == arg->next->type && arg->value == arg->next->value) { + code = Texts(code, Text$from_str(arg->name), ","); } else { - code = Texts(code, must(format_inline_arg(args, comments))); - if (args->next) code = Texts(code, ", "); + code = Texts(code, must(format_inline_arg(arg, comments))); + if (arg->next) code = Texts(code, ", "); } - if (args->next && range_has_comment(args->end, args->next->start, comments)) return NONE_TEXT; + if (arg->next && range_has_comment(arg->end, arg->next->start, comments)) return NONE_TEXT; } return code; } @@ -45,12 +45,12 @@ Text_t format_args(arg_ast_t *args, Table_t comments, Text_t indent) { OptionalText_t inline_args = format_inline_args(args, comments); if (inline_args.length >= 0 && inline_args.length <= MAX_WIDTH) return inline_args; Text_t code = EMPTY_TEXT; - for (; args; args = args->next) { - if (args->name && args->next && args->type == args->next->type && args->value == args->next->value) { - code = Texts(code, Text$from_str(args->name), ","); + for (arg_ast_t *arg = args; arg; arg = arg->next) { + if (arg->name && arg->next && arg->type == arg->next->type && arg->value == arg->next->value) { + code = Texts(code, Text$from_str(arg->name), ","); } else { - code = - Texts(code, "\n", indent, single_indent, format_arg(args, comments, Texts(indent, single_indent)), ","); + code = Texts(code, "\n", indent, single_indent, format_arg(arg, comments, Texts(indent, single_indent))); + if (args->next) code = Texts(code, ","); } } return code; diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c index addf8445..1719a294 100644 --- a/src/formatter/formatter.c +++ b/src/formatter/formatter.c @@ -774,14 +774,16 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { /*multiline*/ case FunctionCall: { if (inlined_fits) return inlined; DeclareMatch(call, ast, FunctionCall); - return Texts(fmt(call->fn, comments, indent), "(\n", indent, single_indent, - format_args(call->args, comments, indent), "\n", Texts(indent, single_indent), ")"); + Text_t args = format_args(call->args, comments, indent); + return Texts(fmt(call->fn, comments, indent), "(", args, + Text$has(args, Text("\n")) ? Texts("\n", indent) : EMPTY_TEXT, ")"); } /*multiline*/ case MethodCall: { if (inlined_fits) return inlined; DeclareMatch(call, ast, MethodCall); - return Texts(termify(call->self, comments, indent), ".", Text$from_str(call->name), "(\n", indent, - single_indent, format_args(call->args, comments, indent), "\n", Texts(indent, single_indent), ")"); + Text_t args = format_args(call->args, comments, indent); + return Texts(termify(call->self, comments, indent), ".", Text$from_str(call->name), "(", args, + Text$has(args, Text("\n")) ? Texts("\n", indent) : EMPTY_TEXT, ")"); } /*multiline*/ case DocTest: { DeclareMatch(test, ast, DocTest); -- cgit v1.2.3 From b025a65b45e8f2e8a56eb087743f9e9e7109b9f8 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sat, 30 Aug 2025 16:02:17 -0400 Subject: Tweak empty gaps --- src/formatter/formatter.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/formatter') diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c index 1719a294..824a936a 100644 --- a/src/formatter/formatter.c +++ b/src/formatter/formatter.c @@ -433,8 +433,10 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { comment_pos = stmt->ast->end; if (stmt->next) { - for (int blanks = suggested_blank_lines(stmt->ast, stmt->next->ast); blanks > 0; blanks--) + int suggested_blanks = suggested_blank_lines(stmt->ast, stmt->next->ast); + for (int blanks = suggested_blanks; blanks > 0; blanks--) add_line(&code, Text(""), indent); + gap_before_comment = (suggested_blanks == 0); } else gap_before_comment = true; } -- cgit v1.2.3 From 0eae0e70b3475ab2cf3d8486dc3a609a22faa75a Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Mon, 1 Sep 2025 17:41:55 -0400 Subject: Fixes --- src/formatter/args.h | 2 -- src/formatter/formatter.c | 5 +++-- src/formatter/utils.c | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) (limited to 'src/formatter') diff --git a/src/formatter/args.h b/src/formatter/args.h index 42722f32..c902684b 100644 --- a/src/formatter/args.h +++ b/src/formatter/args.h @@ -9,5 +9,3 @@ OptionalText_t format_inline_arg(arg_ast_t *arg, Table_t comments); Text_t format_arg(arg_ast_t *arg, Table_t comments, Text_t indent); OptionalText_t format_inline_args(arg_ast_t *args, Table_t comments); Text_t format_args(arg_ast_t *args, Table_t comments, Text_t indent); -OptionalText_t format_inline_tag(tag_ast_t *tag, Table_t comments); -Text_t format_tag(tag_ast_t *tag, Table_t comments, Text_t indent); diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c index 824a936a..60714945 100644 --- a/src/formatter/formatter.c +++ b/src/formatter/formatter.c @@ -33,7 +33,7 @@ typedef struct { Text_t quote, unquote, interp; } text_opts_t; -text_opts_t choose_text_options(ast_list_t *chunks) { +PUREFUNC text_opts_t choose_text_options(ast_list_t *chunks) { int double_quotes = 0, single_quotes = 0, backticks = 0; for (ast_list_t *chunk = chunks; chunk; chunk = chunk->next) { if (chunk->ast->tag == TextLiteral) { @@ -146,6 +146,7 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { if (if_->else_body == NULL && if_->condition->tag != Declare) { ast_t *stmt = unwrap_block(if_->body); + if (!stmt) return Texts("pass ", if_condition); switch (stmt->tag) { case Return: case Skip: @@ -391,7 +392,7 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { } } -static int64_t trailing_line_len(Text_t text) { +PUREFUNC static int64_t trailing_line_len(Text_t text) { TextIter_t state = NEW_TEXT_ITER_STATE(text); int64_t len = 0; for (int64_t i = text.length - 1; i >= 0; i--) { diff --git a/src/formatter/utils.c b/src/formatter/utils.c index 80445ec3..bbe74d7f 100644 --- a/src/formatter/utils.c +++ b/src/formatter/utils.c @@ -27,7 +27,7 @@ OptionalText_t next_comment(Table_t comments, const char **pos, const char *end) 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 Text$from_strn(p, (size_t)(*comment_end - p)); } } return NONE_TEXT; -- cgit v1.2.3 From cdb326a6e74e80bea9bffbeaaff1c5d1f4c9e733 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sat, 6 Sep 2025 14:34:38 -0400 Subject: Code cleanup --- src/formatter/formatter.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'src/formatter') diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c index 60714945..b68b3874 100644 --- a/src/formatter/formatter.c +++ b/src/formatter/formatter.c @@ -323,7 +323,8 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { if (reduction->key) { return Texts("(", fmt_inline(reduction->key, comments), ": ", fmt_inline(reduction->iter, comments)); } else { - return Texts("(", binop_operator(reduction->op), ": ", fmt_inline(reduction->iter, comments)); + return Texts("(", Text$from_str(binop_info[reduction->op].operator), ": ", + fmt_inline(reduction->iter, comments)); } } /*inline*/ case None: @@ -353,7 +354,7 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { } /*inline*/ case BINOP_CASES: { binary_operands_t operands = BINARY_OPERANDS(ast); - const char *op = binop_operator(ast->tag); + const char *op = binop_info[ast->tag].operator; Text_t lhs = fmt_inline(operands.lhs, comments); Text_t rhs = fmt_inline(operands.rhs, comments); @@ -368,7 +369,7 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { rhs = parenthesize(rhs, EMPTY_TEXT); Text_t space = op_tightness[ast->tag] >= op_tightness[Multiply] ? EMPTY_TEXT : Text(" "); - return Texts(lhs, space, Text$from_str(binop_operator(ast->tag)), space, rhs); + return Texts(lhs, space, Text$from_str(binop_info[ast->tag].operator), space, rhs); } /*inline*/ case Deserialize: { DeclareMatch(deserialize, ast, Deserialize); @@ -760,7 +761,7 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { return Texts("(", fmt(reduction->key, comments, Texts(indent, single_indent)), ": ", fmt(reduction->iter, comments, Texts(indent, single_indent))); } else { - return Texts("(", binop_operator(reduction->op), ": ", + return Texts("(", binop_info[reduction->op].operator, ": ", fmt(reduction->iter, comments, Texts(indent, single_indent))); } } @@ -808,7 +809,7 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { /*multiline*/ case BINOP_CASES: { if (inlined_fits) return inlined; binary_operands_t operands = BINARY_OPERANDS(ast); - const char *op = binop_operator(ast->tag); + const char *op = binop_info[ast->tag].operator; Text_t lhs = fmt(operands.lhs, comments, indent); Text_t rhs = fmt(operands.rhs, comments, indent); @@ -822,7 +823,7 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { rhs = parenthesize(rhs, indent); Text_t space = op_tightness[ast->tag] >= op_tightness[Multiply] ? EMPTY_TEXT : Text(" "); - return Texts(lhs, space, Text$from_str(binop_operator(ast->tag)), space, rhs); + return Texts(lhs, space, Text$from_str(binop_info[ast->tag].operator), space, rhs); } /*multiline*/ case Deserialize: { if (inlined_fits) return inlined; -- cgit v1.2.3