123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 | // This file defines utility functions for autoformatting code
#include <stdbool.h>
#include <stdint.h>
#include "../ast.h"
#include "../parse/context.h"
#include "../stdlib/datatypes.h"
#include "../stdlib/optionals.h"
#include "../stdlib/tables.h"
#include "../stdlib/text.h"
#include "formatter.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, (size_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.tag != TEXT_NONE);
}
CONSTFUNC int suggested_blank_lines(ast_t *first, ast_t *second) {
if (second == NULL) return 0;
for (;;) {
if (first->tag == Declare && Match(first, Declare)->value) {
first = Match(first, Declare)->value;
} else if (first->tag == DebugLog) {
return 1;
} else break;
}
for (;;) {
if (second->tag == Declare && Match(second, Declare)->value) {
second = Match(second, Declare)->value;
} else if (second->tag == DebugLog) {
return 1;
} else break;
}
switch (first->tag) {
case If:
case When:
case Repeat:
case While:
case For:
case Block:
case Defer:
case ConvertDef:
case FunctionDef:
case Lambda:
case StructDef:
case EnumDef:
case LangDef: 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;
}
switch (second->tag) {
case If:
case When:
case Repeat:
case While:
case For:
case Block:
case Defer:
case ConvertDef:
case FunctionDef:
case Lambda:
case StructDef:
case EnumDef:
case LangDef: return 1;
default: break;
}
return 0;
}
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;
}
if (ast->tag == Block && Match(ast, Block)->statements == NULL) return NULL;
return ast;
}
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:
case HeapAllocate:
case StackReference: 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:
case HeapAllocate:
case StackReference: return parenthesize(format_code(ast, comments, indent), indent);
default: return format_inline_code(ast, comments);
}
}
|