From a92021f40b2489e0a6d915f02b6d6ad912fd3d77 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Fri, 11 Oct 2024 00:26:15 -0400 Subject: Convert ASTs to text --- ast.c | 222 ++++++++++++++++++++++++++++++++---------------------------- ast.h | 9 ++- compile.c | 48 ++++++------- parse.c | 65 +++++++++--------- repl.c | 14 ++-- typecheck.c | 10 +-- 6 files changed, 188 insertions(+), 180 deletions(-) diff --git a/ast.c b/ast.c index af0aea5a..2d6b3bc7 100644 --- a/ast.c +++ b/ast.c @@ -1,15 +1,19 @@ // Some basic operations defined on AST nodes, mainly converting to // strings for debugging. -#include #include #include #include "ast.h" +#include "stdlib/arrays.h" #include "stdlib/datatypes.h" #include "stdlib/integers.h" +#include "stdlib/patterns.h" #include "stdlib/tables.h" #include "stdlib/text.h" -#include "cordhelpers.h" +#include "stdlib/util.h" + +#define ast_to_xml_ptr(ast) stack(ast_to_xml(ast)) +#define type_ast_to_xml_ptr(ast) stack(type_ast_to_xml(ast)) static const char *OP_NAMES[] = { [BINOP_UNKNOWN]="unknown", @@ -28,160 +32,172 @@ const char *binop_method_names[BINOP_XOR+1] = { [BINOP_AND]="bit_and", [BINOP_OR]="bit_or", [BINOP_XOR]="bit_xor", }; -static CORD ast_list_to_xml(ast_list_t *asts); -static CORD arg_list_to_xml(arg_ast_t *args); -static CORD when_clauses_to_xml(when_clause_t *clauses); -static CORD tags_to_xml(tag_ast_t *tags); -static CORD xml_escape(CORD text); -static CORD optional_tagged(const char *tag, ast_t *ast); -static CORD optional_tagged_type(const char *tag, type_ast_t *ast); +static Text_t _ast_list_to_xml(ast_list_t *asts); +#define ast_list_to_xml(ast) stack(_ast_list_to_xml(ast)) +static Text_t _arg_list_to_xml(arg_ast_t *args); +#define arg_list_to_xml(ast) stack(_arg_list_to_xml(ast)) +static Text_t _when_clauses_to_xml(when_clause_t *clauses); +#define when_clauses_to_xml(...) stack(_when_clauses_to_xml(__VA_ARGS__)) +static Text_t _tags_to_xml(tag_ast_t *tags); +#define tags_to_xml(...) stack(_tags_to_xml(__VA_ARGS__)) +static Text_t _xml_escape(Text_t text); +#define xml_escape(...) stack(_xml_escape(__VA_ARGS__)) +static Text_t _optional_tagged(const char *tag, ast_t *ast); +#define optional_tagged(...) stack(_optional_tagged(__VA_ARGS__)) +static Text_t _optional_tagged_type(const char *tag, type_ast_t *ast); +#define optional_tagged_type(...) stack(_optional_tagged_type(__VA_ARGS__)) -CORD xml_escape(CORD text) +Text_t _xml_escape(Text_t text) { - text = CORD_replace(text, "&", "&"); - text = CORD_replace(text, "<", "<"); - text = CORD_replace(text, ">", ">"); - return text; + typedef struct {Text_t k,v;} replacement_t; + Array_t replacements = TypedArray( + replacement_t, + {Text("&"), Text("&")}, + {Text("<"), Text("<")}, + {Text(">"), Text(">")}); + return Text$replace_all(text, Table$from_entries(replacements, Table$info(&Pattern$info, &Text$info)), Text(""), false); } -CORD ast_list_to_xml(ast_list_t *asts) +Text_t _ast_list_to_xml(ast_list_t *asts) { - CORD c = CORD_EMPTY; + Text_t text = Text(""); for (; asts; asts = asts->next) { - c = CORD_cat(c, ast_to_xml(asts->ast)); + text = Texts(text, ast_to_xml(asts->ast)); } - return c; + return text; } -CORD arg_list_to_xml(arg_ast_t *args) { - CORD c = ""; +Text_t _arg_list_to_xml(arg_ast_t *args) { + Text_t text = Text(""); for (; args; args = args->next) { - CORD arg_cord = args->name ? CORD_all("name, "\">") : ""; + Text_t arg_text = args->name ? Text$format("", args->name) : Text(""); if (args->type) - arg_cord = CORD_all(arg_cord, "", type_ast_to_xml(args->type), ""); + arg_text = Texts(arg_text, Text(""), type_ast_to_xml(args->type), Text("")); if (args->value) - arg_cord = CORD_all(arg_cord, "", ast_to_xml(args->value), ""); - c = CORD_all(c, arg_cord, ""); + arg_text = Texts(text, Text(""), ast_to_xml(args->value), Text("")); + text = Texts(text, arg_text, Text("")); } - return CORD_cat(c, ""); + return Texts(text, Text("")); } -CORD when_clauses_to_xml(when_clause_t *clauses) { - CORD c = CORD_EMPTY; +Text_t _when_clauses_to_xml(when_clause_t *clauses) { + Text_t text = Text(""); for (; clauses; clauses = clauses->next) { - c = CORD_all(c, "tag_name), "\">", ast_list_to_xml(clauses->args), ast_to_xml(clauses->body), ""); + text = Texts(text, Text("tag_name), Text("\">"), + _ast_list_to_xml(clauses->args), ast_to_xml(clauses->body), Text("")); } - return c; + return text; } -CORD tags_to_xml(tag_ast_t *tags) { - CORD c = CORD_EMPTY; +Text_t _tags_to_xml(tag_ast_t *tags) { + Text_t text = Text(""); for (; tags; tags = tags->next) { - c = CORD_all(c, "name, "\">", arg_list_to_xml(tags->fields), ""); + text = Texts(text, Text$format("%k", tags->name, arg_list_to_xml(tags->fields))); } - return c; + return text; } -CORD optional_tagged(const char *tag, ast_t *ast) +Text_t _optional_tagged(const char *tag, ast_t *ast) { - return ast ? CORD_all("<", tag, ">", ast_to_xml(ast), "") : CORD_EMPTY; + return ast ? Text$format("<%s>%k", tag, ast_to_xml_ptr(ast), tag) : Text(""); } -CORD optional_tagged_type(const char *tag, type_ast_t *ast) +Text_t _optional_tagged_type(const char *tag, type_ast_t *ast) { - return ast ? CORD_all("<", tag, ">", type_ast_to_xml(ast), "") : CORD_EMPTY; + return ast ? Text$format("<%s>%k", tag, stack(type_ast_to_xml(ast)), tag) : Text(""); } -CORD ast_to_xml(ast_t *ast) +Text_t ast_to_xml(ast_t *ast) { - if (!ast) return CORD_EMPTY; + if (!ast) return Text(""); switch (ast->tag) { -#define T(type, ...) case type: { auto data = ast->__data.type; (void)data; return CORD_asprintf(__VA_ARGS__); } +#define T(type, ...) case type: { auto data = ast->__data.type; (void)data; return Text$format(__VA_ARGS__); } T(Unknown, "") - T(Null, "%r", type_ast_to_xml(data.type)) + T(Null, "%k", stack(type_ast_to_xml(data.type))) T(Bool, "", data.b ? "yes" : "no") T(Var, "%s", data.name) T(Int, "%s", data.bits, data.str) T(Num, "%g", data.bits, data.n) - T(TextLiteral, "%r", xml_escape(data.cord)) - T(TextJoin, "%r", data.lang ? CORD_all(" lang=\"", data.lang, "\"") : CORD_EMPTY, ast_list_to_xml(data.children)) - T(Declare, "%r", ast_to_xml(data.var), ast_to_xml(data.value)) - T(Assign, "%r%r", ast_list_to_xml(data.targets), ast_list_to_xml(data.values)) - T(BinaryOp, "%r %r", xml_escape(OP_NAMES[data.op]), ast_to_xml(data.lhs), ast_to_xml(data.rhs)) - T(UpdateAssign, "%r %r", xml_escape(OP_NAMES[data.op]), ast_to_xml(data.lhs), ast_to_xml(data.rhs)) - T(Negative, "%r", ast_to_xml(data.value)) - T(Not, "%r", ast_to_xml(data.value)) - T(HeapAllocate, "%r", ast_to_xml(data.value)) - T(StackReference, "%r", ast_to_xml(data.value)) - T(Min, "%r%r%r", ast_to_xml(data.lhs), ast_to_xml(data.rhs), optional_tagged("key", data.key)) - T(Max, "%r%r%r", ast_to_xml(data.lhs), ast_to_xml(data.rhs), optional_tagged("key", data.key)) - T(Array, "%r%r", optional_tagged_type("item-type", data.item_type), ast_list_to_xml(data.items)) - T(Set, "%r%r", - optional_tagged_type("item-type", data.item_type), - ast_list_to_xml(data.items)) - T(Table, "%r%r%r%r
", + T(TextLiteral, "%k", xml_escape(data.text)) + T(TextJoin, "%k", stack(data.lang ? Text$format(" lang=\"%s\"", data.lang) : Text("")), ast_list_to_xml(data.children)) + T(Declare, "%k", ast_to_xml_ptr(data.var), ast_to_xml_ptr(data.value)) + T(Assign, "%k%k", ast_list_to_xml(data.targets), ast_list_to_xml(data.values)) + T(BinaryOp, "%k %k", xml_escape(Text$from_str(OP_NAMES[data.op])), + ast_to_xml_ptr(data.lhs), ast_to_xml_ptr(data.rhs)) + T(UpdateAssign, "%k %k", xml_escape(Text$from_str(OP_NAMES[data.op])), + ast_to_xml_ptr(data.lhs), ast_to_xml_ptr(data.rhs)) + T(Negative, "%k", ast_to_xml_ptr(data.value)) + T(Not, "%k", ast_to_xml_ptr(data.value)) + T(HeapAllocate, "%k", ast_to_xml_ptr(data.value)) + T(StackReference, "%k", ast_to_xml_ptr(data.value)) + T(Min, "%k%k%k", ast_to_xml_ptr(data.lhs), ast_to_xml_ptr(data.rhs), optional_tagged("key", data.key)) + T(Max, "%k%k%k", ast_to_xml_ptr(data.lhs), ast_to_xml_ptr(data.rhs), optional_tagged("key", data.key)) + T(Array, "%k%k", optional_tagged_type("item-type", data.item_type), ast_list_to_xml(data.items)) + T(Set, "%k%k", + optional_tagged_type("item-type", data.item_type), ast_list_to_xml(data.items)) + T(Table, "%k%k%k%k
", optional_tagged_type("key-type", data.key_type), optional_tagged_type("value-type", data.value_type), ast_list_to_xml(data.entries), optional_tagged("fallback", data.fallback)) - T(TableEntry, "%r%r", ast_to_xml(data.key), ast_to_xml(data.value)) - T(Channel, "%r%r", type_ast_to_xml(data.item_type), optional_tagged("max-size", data.max_size)) - T(Comprehension, "%r%r%r%r%r", optional_tagged("expr", data.expr), + T(TableEntry, "%k%k", ast_to_xml_ptr(data.key), ast_to_xml_ptr(data.value)) + T(Channel, "%k%k", type_ast_to_xml_ptr(data.item_type), optional_tagged("max-size", data.max_size)) + T(Comprehension, "%k%k%k%k%k", optional_tagged("expr", data.expr), ast_list_to_xml(data.vars), optional_tagged("iter", data.iter), optional_tagged("filter", data.filter)) - T(FunctionDef, "%r%r%r", ast_to_xml(data.name), - arg_list_to_xml(data.args), optional_tagged_type("return-type", data.ret_type), ast_to_xml(data.body)) - T(Lambda, "%r%r%r)", arg_list_to_xml(data.args), - optional_tagged_type("return-type", data.ret_type), ast_to_xml(data.body)) - T(FunctionCall, "%r%r", ast_to_xml(data.fn), arg_list_to_xml(data.args)) - T(MethodCall, "%r%s%r", ast_to_xml(data.self), data.name, arg_list_to_xml(data.args)) - T(Block, "%r", ast_list_to_xml(data.statements)) - T(For, "%r%r%r%r%r", ast_list_to_xml(data.vars), optional_tagged("iterable", data.iter), + T(FunctionDef, "%k%k%k", ast_to_xml_ptr(data.name), + arg_list_to_xml(data.args), optional_tagged_type("return-type", data.ret_type), ast_to_xml_ptr(data.body)) + T(Lambda, "%k%k%k)", arg_list_to_xml(data.args), + optional_tagged_type("return-type", data.ret_type), ast_to_xml_ptr(data.body)) + T(FunctionCall, "%k%k", ast_to_xml_ptr(data.fn), arg_list_to_xml(data.args)) + T(MethodCall, "%k%s%k", ast_to_xml_ptr(data.self), data.name, arg_list_to_xml(data.args)) + T(Block, "%k", ast_list_to_xml(data.statements)) + T(For, "%k%k%k%k%k", ast_list_to_xml(data.vars), optional_tagged("iterable", data.iter), optional_tagged("body", data.body), optional_tagged("empty", data.empty)) - T(While, "%r%r", optional_tagged("condition", data.condition), optional_tagged("body", data.body)) - T(If, "%r%r%r", optional_tagged("condition", data.condition), optional_tagged("body", data.body), optional_tagged("else", data.else_body)) - T(When, "%r%r%r", ast_to_xml(data.subject), when_clauses_to_xml(data.clauses), optional_tagged("else", data.else_body)) - T(Reduction, "%r%r%r", optional_tagged("iterable", data.iter), + T(While, "%k%k", optional_tagged("condition", data.condition), optional_tagged("body", data.body)) + T(If, "%k%k%k", optional_tagged("condition", data.condition), optional_tagged("body", data.body), optional_tagged("else", data.else_body)) + T(When, "%k%k%k", ast_to_xml_ptr(data.subject), when_clauses_to_xml(data.clauses), optional_tagged("else", data.else_body)) + T(Reduction, "%k%k%k", optional_tagged("iterable", data.iter), optional_tagged("combination", data.combination), optional_tagged("fallback", data.fallback)) - T(Skip, "%r", data.target) - T(Stop, "%r", data.target) - T(PrintStatement, "%r", ast_list_to_xml(data.to_print)) + T(Skip, "%s", data.target) + T(Stop, "%s", data.target) + T(PrintStatement, "%k", ast_list_to_xml(data.to_print)) T(Pass, "") - T(Defer, "%r", ast_to_xml(data.body)) - T(Return, "%r", ast_to_xml(data.value)) - T(Extern, "%r", data.name, type_ast_to_xml(data.type)) - T(StructDef, "%r%r", data.name, arg_list_to_xml(data.fields), ast_to_xml(data.namespace)) - T(EnumDef, "%r%r", data.name, tags_to_xml(data.tags), ast_to_xml(data.namespace)) - T(LangDef, "%r", data.name, ast_to_xml(data.namespace)) - T(Index, "%r%r", optional_tagged("indexed", data.indexed), optional_tagged("index", data.index)) - T(FieldAccess, "%r", data.field, ast_to_xml(data.fielded)) - T(Optional, "%r", ast_to_xml(data.value)) - T(NonOptional, "%r", ast_to_xml(data.value)) - T(DocTest, "%r%r", optional_tagged("expression", data.expr), xml_escape(data.output)) - T(Use, "%r%r", optional_tagged("var", data.var), xml_escape(data.path)) - T(InlineCCode, "%r", xml_escape(data.code)) - default: return "???"; + T(Defer, "%k", ast_to_xml_ptr(data.body)) + T(Return, "%k", ast_to_xml_ptr(data.value)) + T(Extern, "%k", data.name, type_ast_to_xml_ptr(data.type)) + T(StructDef, "%k%k", data.name, arg_list_to_xml(data.fields), ast_to_xml_ptr(data.namespace)) + T(EnumDef, "%k%k", data.name, tags_to_xml(data.tags), ast_to_xml_ptr(data.namespace)) + T(LangDef, "%k", data.name, ast_to_xml_ptr(data.namespace)) + T(Index, "%k%k", optional_tagged("indexed", data.indexed), optional_tagged("index", data.index)) + T(FieldAccess, "%k", data.field, ast_to_xml_ptr(data.fielded)) + T(Optional, "%k", ast_to_xml_ptr(data.value)) + T(NonOptional, "%k", ast_to_xml_ptr(data.value)) + T(DocTest, "%k%k", optional_tagged("expression", data.expr), xml_escape(Text$from_str(data.output))) + T(Use, "%k%k", optional_tagged("var", data.var), xml_escape(Text$from_str(data.path))) + T(InlineCCode, "%k", xml_escape(data.code)) + default: return Text("???"); #undef T } } -CORD type_ast_to_xml(type_ast_t *t) +Text_t type_ast_to_xml(type_ast_t *t) { - if (!t) return "NULL"; + if (!t) return Text("NULL"); switch (t->tag) { -#define T(type, ...) case type: { auto data = t->__data.type; (void)data; return CORD_asprintf(__VA_ARGS__); } +#define T(type, ...) case type: { auto data = t->__data.type; (void)data; return Text$format(__VA_ARGS__); } T(UnknownTypeAST, "") T(VarTypeAST, "%s", data.name) - T(PointerTypeAST, "%r", - data.is_stack ? "yes" : "no", type_ast_to_xml(data.pointed)) - T(ArrayTypeAST, "%r", type_ast_to_xml(data.item)) - T(SetTypeAST, "%r", type_ast_to_xml(data.item)) - T(ChannelTypeAST, "%r", type_ast_to_xml(data.item)) - T(TableTypeAST, "%r %r", type_ast_to_xml(data.key), type_ast_to_xml(data.value)) - T(FunctionTypeAST, "%r %r", arg_list_to_xml(data.args), type_ast_to_xml(data.ret)) - T(OptionalTypeAST, "%r", data.type) + T(PointerTypeAST, "%k", + data.is_stack ? "yes" : "no", type_ast_to_xml_ptr(data.pointed)) + T(ArrayTypeAST, "%k", type_ast_to_xml_ptr(data.item)) + T(SetTypeAST, "%k", type_ast_to_xml_ptr(data.item)) + T(ChannelTypeAST, "%k", type_ast_to_xml_ptr(data.item)) + T(TableTypeAST, "%k %k", type_ast_to_xml_ptr(data.key), type_ast_to_xml_ptr(data.value)) + T(FunctionTypeAST, "%k %k", arg_list_to_xml(data.args), type_ast_to_xml_ptr(data.ret)) + T(OptionalTypeAST, "%k", type_ast_to_xml_ptr(data.type)) #undef T - default: return CORD_EMPTY; + default: return Text(""); } } @@ -192,7 +208,7 @@ int printf_ast(FILE *stream, const struct printf_info *info, const void *const a if (info->alt) return fprintf(stream, "%.*s", (int)(ast->end - ast->start), ast->start); else - return CORD_put(ast_to_xml(ast), stream); + return Text$print(stream, ast_to_xml(ast)); } else { return fputs("(null)", stream); } diff --git a/ast.h b/ast.h index 4b8e03dc..2fb2c328 100644 --- a/ast.h +++ b/ast.h @@ -3,7 +3,6 @@ // Logic defining ASTs (abstract syntax trees) to represent code #include -#include #include #include #include @@ -172,7 +171,7 @@ struct ast_s { enum { NBITS_UNSPECIFIED=0, NBITS32=32, NBITS64=64 } bits; } Num; struct { - CORD cord; + Text_t text; } TextLiteral; struct { const char *lang; @@ -321,15 +320,15 @@ struct ast_s { enum { USE_LOCAL, USE_MODULE, USE_SHARED_OBJECT, USE_HEADER, USE_C_CODE, USE_ASM } what; } Use; struct { - CORD code; + Text_t code; struct type_s *type; type_ast_t *type_ast; } InlineCCode; } __data; }; -CORD ast_to_xml(ast_t *ast); -CORD type_ast_to_xml(type_ast_t *ast); +Text_t ast_to_xml(ast_t *ast); +Text_t type_ast_to_xml(type_ast_t *ast); int printf_ast(FILE *stream, const struct printf_info *info, const void *const args[]); PUREFUNC bool is_idempotent(ast_t *ast); void visit_topologically(ast_list_t *ast, Closure_t fn); diff --git a/compile.c b/compile.c index 22416939..35d5b815 100644 --- a/compile.c +++ b/compile.c @@ -1424,9 +1424,9 @@ CORD compile_statement(env_t *env, ast_t *ast) case InlineCCode: { auto inline_code = Match(ast, InlineCCode); if (inline_code->type) - return CORD_all("({ ", inline_code->code, "; })"); + return CORD_all("({ ", Text$as_c_string(inline_code->code), "; })"); else - return inline_code->code; + return Text$as_c_string(inline_code->code); } case Use: { auto use = Match(ast, Use); @@ -2197,7 +2197,7 @@ CORD compile(env_t *env, ast_t *ast) code_err(ast, "unimplemented binop"); } case TextLiteral: { - CORD literal = Match(ast, TextLiteral)->cord; + CORD literal = Text$as_c_string(Match(ast, TextLiteral)->text); if (literal == CORD_EMPTY) return "Text(\"\")"; @@ -2225,7 +2225,7 @@ CORD compile(env_t *env, ast_t *ast) if (!chunks) { return CORD_all(lang_constructor, "(\"\")"); } else if (!chunks->next && chunks->ast->tag == TextLiteral) { - CORD literal = Match(chunks->ast, TextLiteral)->cord; + CORD literal = Text$as_c_string(Match(chunks->ast, TextLiteral)->text); if (string_literal_is_all_ascii(literal)) return CORD_all(lang_constructor, "(", compile_string_literal(literal), ")"); return CORD_all("((", compile_type(text_t), ")", compile(env, chunks->ast), ")"); @@ -2353,7 +2353,7 @@ CORD compile(env_t *env, ast_t *ast) env_t *scope = item_type->tag == EnumType ? with_enum_scope(env, item_type) : fresh_scope(env); static int64_t comp_num = 1; const char *comprehension_name = heap_strf("arr$%ld", comp_num++); - ast_t *comprehension_var = FakeAST(InlineCCode, .code=CORD_all("&", comprehension_name), + ast_t *comprehension_var = FakeAST(InlineCCode, .code=Text$format("&%s", comprehension_name), .type=Type(PointerType, .pointed=array_type, .is_stack=true)); Closure_t comp_action = {.fn=add_to_array_comprehension, .userdata=comprehension_var}; scope->comprehension_action = &comp_action; @@ -2432,7 +2432,7 @@ CORD compile(env_t *env, ast_t *ast) static int64_t comp_num = 1; env_t *scope = fresh_scope(env); const char *comprehension_name = heap_strf("table$%ld", comp_num++); - ast_t *comprehension_var = FakeAST(InlineCCode, .code=CORD_all("&", comprehension_name), + ast_t *comprehension_var = FakeAST(InlineCCode, .code=Text$format("&%s", comprehension_name), .type=Type(PointerType, .pointed=table_type, .is_stack=true)); CORD code = CORD_all("({ Table_t ", comprehension_name, " = {"); @@ -2486,7 +2486,7 @@ CORD compile(env_t *env, ast_t *ast) static int64_t comp_num = 1; env_t *scope = item_type->tag == EnumType ? with_enum_scope(env, item_type) : fresh_scope(env); const char *comprehension_name = heap_strf("set$%ld", comp_num++); - ast_t *comprehension_var = FakeAST(InlineCCode, .code=CORD_all("&", comprehension_name), + ast_t *comprehension_var = FakeAST(InlineCCode, .code=Text$format("&%s", comprehension_name), .type=Type(PointerType, .pointed=set_type, .is_stack=true)); CORD code = CORD_all("({ Table_t ", comprehension_name, " = {};"); Closure_t comp_action = {.fn=add_to_set_comprehension, .userdata=comprehension_var}; @@ -2702,8 +2702,8 @@ CORD compile(env_t *env, ast_t *ast) type_t *fn_t = Type(FunctionType, .args=new(arg_t, .name="x", .type=item_ptr, .next=new(arg_t, .name="y", .type=item_ptr)), .ret=Type(IntType, .bits=TYPE_IBITS32)); ast_t *default_cmp = FakeAST(InlineCCode, - .code=CORD_all("((Closure_t){.fn=generic_compare, .userdata=(void*)", - compile_type_info(env, item_t), "})"), + .code=Text$format("((Closure_t){.fn=generic_compare, .userdata=(void*)%s})", + CORD_to_const_char_star(compile_type_info(env, item_t))), .type=Type(ClosureType, .fn=fn_t)); arg_t *arg_spec = new(arg_t, .name="item", .type=item_t, .next=new(arg_t, .name="by", .type=Type(ClosureType, .fn=fn_t), .default_val=default_cmp)); @@ -2715,8 +2715,8 @@ CORD compile(env_t *env, ast_t *ast) type_t *fn_t = Type(FunctionType, .args=new(arg_t, .name="x", .type=item_ptr, .next=new(arg_t, .name="y", .type=item_ptr)), .ret=Type(IntType, .bits=TYPE_IBITS32)); ast_t *default_cmp = FakeAST(InlineCCode, - .code=CORD_all("((Closure_t){.fn=generic_compare, .userdata=(void*)", - compile_type_info(env, item_t), "})"), + .code=Text$format("((Closure_t){.fn=generic_compare, .userdata=(void*)%s})", + CORD_to_const_char_star(compile_type_info(env, item_t))), .type=Type(ClosureType, .fn=fn_t)); arg_t *arg_spec = new(arg_t, .name="by", .type=Type(ClosureType, .fn=fn_t), .default_val=default_cmp); CORD arg_code = compile_arguments(env, ast, arg_spec, call->args); @@ -2727,8 +2727,8 @@ CORD compile(env_t *env, ast_t *ast) type_t *fn_t = Type(FunctionType, .args=new(arg_t, .name="x", .type=item_ptr, .next=new(arg_t, .name="y", .type=item_ptr)), .ret=Type(IntType, .bits=TYPE_IBITS32)); ast_t *default_cmp = FakeAST(InlineCCode, - .code=CORD_all("((Closure_t){.fn=generic_compare, .userdata=(void*)", - compile_type_info(env, item_t), "})"), + .code=Text$format("((Closure_t){.fn=generic_compare, .userdata=(void*)%s})", + CORD_to_const_char_star(compile_type_info(env, item_t))), .type=Type(ClosureType, .fn=fn_t)); arg_t *arg_spec = new(arg_t, .name="target", .type=item_t, .next=new(arg_t, .name="by", .type=Type(ClosureType, .fn=fn_t), .default_val=default_cmp)); @@ -3002,11 +3002,11 @@ CORD compile(env_t *env, ast_t *ast) if (!call->args || call->args->next) code_err(call->fn, "This constructor takes exactly 1 argument"); if (call->args->value->tag == TextLiteral) - return compile_string_literal(Match(call->args->value, TextLiteral)->cord); + return compile_string_literal(Text$as_c_string(Match(call->args->value, TextLiteral)->text)); else if (call->args->value->tag == TextJoin && Match(call->args->value, TextJoin)->children == NULL) return "\"\""; else if (call->args->value->tag == TextJoin && Match(call->args->value, TextJoin)->children->next == NULL) - return compile_string_literal(Match(Match(call->args->value, TextJoin)->children->ast, TextLiteral)->cord); + return compile_string_literal(Text$as_c_string(Match(Match(call->args->value, TextJoin)->children->ast, TextLiteral)->text)); type_t *actual = get_type(env, call->args->value); return CORD_all("Text$as_c_string(", expr_as_text(env, compile(env, call->args->value), actual, "no"), ")"); } else if (t->tag == DateTimeType) { @@ -3156,7 +3156,7 @@ CORD compile(env_t *env, ast_t *ast) env_t *scope = fresh_scope(env); set_binding(scope, "$reduction", new(binding_t, .type=t, .code="reduction")); ast_t *item = FakeAST(Var, "$iter_value"); - ast_t *body = FakeAST(InlineCCode, .code="{}"); // placeholder + ast_t *body = FakeAST(InlineCCode, .code=Text("{}")); // placeholder ast_t *loop = FakeAST(For, .vars=new(ast_list_t, .ast=item), .iter=reduction->iter, .body=body); env_t *body_scope = for_scope(scope, loop); @@ -3170,14 +3170,16 @@ CORD compile(env_t *env, ast_t *ast) early_out = "if (reduction) break;"; } - body->__data.InlineCCode.code = CORD_all( + body->__data.InlineCCode.code = Text$format( "if (!has_value) {\n" - " reduction = ", compile(body_scope, item), ";\n" + " reduction = %s;\n" " has_value = yes;\n" "} else {\n" - " reduction = ", compile(body_scope, reduction->combination), ";\n", - early_out, - "}\n"); + " reduction = %s;\n" + "}\n", + CORD_to_const_char_star(compile(body_scope, item)), + CORD_to_const_char_star(compile(body_scope, reduction->combination)), + CORD_to_const_char_star(early_out)); CORD empty_handling; if (reduction->fallback) { @@ -3343,9 +3345,9 @@ CORD compile(env_t *env, ast_t *ast) case InlineCCode: { type_t *t = get_type(env, ast); if (t->tag == VoidType) - return CORD_all("{\n", Match(ast, InlineCCode)->code, "\n}"); + return CORD_all("{\n", Text$as_c_string(Match(ast, InlineCCode)->code), "\n}"); else - return CORD_all("({ ", Match(ast, InlineCCode)->code, "; })"); + return CORD_all("({ ", Text$as_c_string(Match(ast, InlineCCode)->code), "; })"); } case Use: code_err(ast, "Compiling 'use' as expression!"); case Defer: code_err(ast, "Compiling 'defer' as expression!"); diff --git a/parse.c b/parse.c index 5d2cc6cc..5e127037 100644 --- a/parse.c +++ b/parse.c @@ -1310,13 +1310,12 @@ PARSER(parse_text) { // Escape sequence, e.g. \r\n if (*pos == '\\') { - CORD cord = CORD_EMPTY; + Text_t text = Text(""); do { const char *c = unescape(ctx, &pos); - cord = CORD_cat(cord, c); - // cord = CORD_cat_char(cord, c); + text = Texts(text, Text$from_str(c)); } while (*pos == '\\'); - return NewAST(ctx->file, start, pos, TextLiteral, .cord=cord); + return NewAST(ctx->file, start, pos, TextLiteral, .text=text); } char open_quote, close_quote, open_interp = '$'; @@ -1357,17 +1356,17 @@ PARSER(parse_text) { int64_t string_indent = starting_indent + 1; ast_list_t *chunks = NULL; - CORD chunk = CORD_EMPTY; + Text_t chunk = Text(""); const char *chunk_start = pos; int depth = 1; bool leading_newline = false; for (; pos < ctx->file->text + ctx->file->len && depth > 0; ) { if (*pos == open_interp) { // Interpolation const char *interp_start = pos; - if (chunk) { - ast_t *literal = NewAST(ctx->file, chunk_start, pos, TextLiteral, .cord=chunk); + if (chunk.length > 0) { + ast_t *literal = NewAST(ctx->file, chunk_start, pos, TextLiteral, .text=Text$format("%k", &chunk)); // Collapse text chunks = new(ast_list_t, .ast=literal, .next=chunks); - chunk = NULL; + chunk = Text(""); } ++pos; ast_t *interp; @@ -1380,7 +1379,7 @@ PARSER(parse_text) { if (get_indent(ctx, pos) == starting_indent) { ++depth; } - chunk = CORD_cat_char(chunk, *pos); + chunk = Texts(chunk, Text$format("%c", *pos)); ++pos; } else if (!leading_newline && *pos == close_quote) { // Nested pair end if (get_indent(ctx, pos) == starting_indent) { @@ -1388,13 +1387,13 @@ PARSER(parse_text) { if (depth == 0) break; } - chunk = CORD_cat_char(chunk, *pos); + chunk = Texts(chunk, Text$format("%c", *pos)); ++pos; } else if (newline_with_indentation(&pos, string_indent)) { // Newline - if (!leading_newline && !(chunk || chunks)) { + if (!leading_newline && !(chunk.length > 0 || chunks)) { leading_newline = true; } else { - chunk = CORD_cat_char(chunk, '\n'); + chunk = Texts(chunk, Text("\n")); } } else if (newline_with_indentation(&pos, starting_indent)) { // Line continuation (..) if (*pos == close_quote) { @@ -1406,15 +1405,14 @@ PARSER(parse_text) { parser_err(ctx, pos, strchrnul(pos, '\n'), "This multi-line string should be either indented or have '..' at the front"); } } else { // Plain character - chunk = CORD_cat_char(chunk, *pos); + chunk = Texts(chunk, Text$format("%c", *pos)); ++pos; } } - if (chunk) { - ast_t *literal = NewAST(ctx->file, chunk_start, pos, TextLiteral, .cord=chunk); + if (chunk.length > 0) { + ast_t *literal = NewAST(ctx->file, chunk_start, pos, TextLiteral, .text=Text$format("%k", &chunk)); // Collapse text chunks = new(ast_list_t, .ast=literal, .next=chunks); - chunk = NULL; } REVERSE_LIST(chunks); @@ -1434,13 +1432,13 @@ PARSER(parse_path) { const char *chunk_start = start + 1; ast_list_t *chunks = NULL; - CORD chunk_text = CORD_EMPTY; + Text_t chunk_text = Text(""); int paren_depth = 1; while (pos < ctx->file->text + ctx->file->len) { switch (*pos) { case '\\': { ++pos; - chunk_text = CORD_asprintf("%r%.*s%c", chunk_text, (size_t)(pos - chunk_start), chunk_start, *pos); + chunk_text = Text$format("%k%.*s%c", &chunk_text, (size_t)(pos - chunk_start), chunk_start, *pos); ++pos; chunk_start = pos; continue; @@ -1449,12 +1447,12 @@ PARSER(parse_path) { const char *interp_start = pos; if (pos > chunk_start) - chunk_text = CORD_asprintf("%r%.*s", chunk_text, (size_t)(pos - chunk_start), chunk_start); + chunk_text = Text$format("%k%.*s", &chunk_text, (size_t)(pos - chunk_start), chunk_start); - if (chunk_text) { - ast_t *literal = NewAST(ctx->file, chunk_start, pos, TextLiteral, .cord=chunk_text); + if (chunk_text.length > 0) { + ast_t *literal = NewAST(ctx->file, chunk_start, pos, TextLiteral, .text=chunk_text); chunks = new(ast_list_t, .ast=literal, .next=chunks); - chunk_text = CORD_EMPTY; + chunk_text = Text(""); } ++pos; if (*pos == ' ' || *pos == '\t') @@ -1482,10 +1480,10 @@ PARSER(parse_path) { end_of_path:; if (pos > chunk_start) - chunk_text = CORD_asprintf("%r%.*s", chunk_text, (size_t)(pos - chunk_start), chunk_start); + chunk_text = Text$format("%k%.*s", &chunk_text, (size_t)(pos - chunk_start), chunk_start); - if (chunk_text != CORD_EMPTY) { - ast_t *literal = NewAST(ctx->file, chunk_start, pos, TextLiteral, .cord=chunk_text); + if (chunk_text.length > 0) { + ast_t *literal = NewAST(ctx->file, chunk_start, pos, TextLiteral, .text=chunk_text); chunks = new(ast_list_t, .ast=literal, .next=chunks); } @@ -2311,7 +2309,7 @@ PARSER(parse_inline_c) { if (depth != 0) parser_err(ctx, start, start+1, "I couldn't find the closing '}' for this inline C code"); - CORD c_code = GC_strndup(c_code_start, (size_t)((pos-1) - c_code_start)); + Text_t c_code = Text$format("%.*s", (size_t)((pos-1) - c_code_start), c_code_start); return NewAST(ctx->file, start, pos, InlineCCode, .code=c_code, .type_ast=type); } @@ -2345,16 +2343,16 @@ PARSER(parse_say) { spaces(&pos); ast_list_t *chunks = NULL; - CORD chunk = CORD_EMPTY; + Text_t chunk = Text(""); const char *chunk_start = pos; const char open_interp = '$'; while (pos < ctx->file->text + ctx->file->len) { if (*pos == open_interp) { // Interpolation const char *interp_start = pos; - if (chunk) { - ast_t *literal = NewAST(ctx->file, chunk_start, pos, TextLiteral, .cord=chunk); + if (chunk.length > 0) { + ast_t *literal = NewAST(ctx->file, chunk_start, pos, TextLiteral, .text=Text$format("%k", &chunk)); chunks = new(ast_list_t, .ast=literal, .next=chunks); - chunk = NULL; + chunk = Text(""); } ++pos; ast_t *interp; @@ -2366,15 +2364,14 @@ PARSER(parse_say) { } else if (*pos == '\r' || *pos == '\n') { // Newline break; } else { // Plain character - chunk = CORD_cat_char(chunk, *pos); + chunk = Texts(chunk, Text$format("%c", *pos)); ++pos; } } - if (chunk) { - ast_t *literal = NewAST(ctx->file, chunk_start, pos, TextLiteral, .cord=chunk); + if (chunk.length > 0) { + ast_t *literal = NewAST(ctx->file, chunk_start, pos, TextLiteral, .text=Text$format("%k", &chunk)); chunks = new(ast_list_t, .ast=literal, .next=chunks); - chunk = NULL; } REVERSE_LIST(chunks); diff --git a/repl.c b/repl.c index 601231fe..0d453ad4 100644 --- a/repl.c +++ b/repl.c @@ -372,24 +372,24 @@ void eval(env_t *env, ast_t *ast, void *dest) break; } case TextLiteral: - if (dest) *(CORD*)dest = Match(ast, TextLiteral)->cord; + if (dest) *(Text_t*)dest = Match(ast, TextLiteral)->text; break; case TextJoin: { - CORD ret = CORD_EMPTY; + Text_t ret = Text(""); for (ast_list_t *chunk = Match(ast, TextJoin)->children; chunk; chunk = chunk->next) { type_t *chunk_t = get_type(env, chunk->ast); if (chunk_t->tag == TextType) { - CORD c; - eval(env, chunk->ast, &c); - ret = CORD_cat(ret, c); + Text_t chunk_text; + eval(env, chunk->ast, &chunk_text); + ret = Texts(ret, chunk_text); } else { size_t chunk_size = type_size(chunk_t); char buf[chunk_size]; eval(env, chunk->ast, buf); - ret = CORD_cat(ret, Text$as_c_string(obj_to_text(chunk_t, buf, false))); + ret = Texts(ret, obj_to_text(chunk_t, buf, false)); } } - if (dest) *(CORD*)dest = ret; + if (dest) *(Text_t*)dest = ret; break; } case BinaryOp: { diff --git a/typecheck.c b/typecheck.c index aefd94cf..6d01359f 100644 --- a/typecheck.c +++ b/typecheck.c @@ -1361,14 +1361,8 @@ PUREFUNC bool is_constant(env_t *env, ast_t *ast) return is_constant(env, text->children->ast); } case TextLiteral: { - CORD literal = Match(ast, TextLiteral)->cord; - CORD_pos i; -#pragma GCC diagnostic ignored "-Wsign-conversion" - CORD_FOR(i, literal) { - if (!isascii(CORD_pos_fetch(i))) - return false; // Non-ASCII requires grapheme logic, not constant - } - return true; // Literal ASCII string, OK + Text_t text = Match(ast, TextLiteral)->text; + return (text.tag == TEXT_SHORT_ASCII || text.tag == TEXT_ASCII); } case Not: return is_constant(env, Match(ast, Not)->value); case Negative: return is_constant(env, Match(ast, Negative)->value); -- cgit v1.2.3