Deprecate custom printf specifiers in favor of print() function that

uses _Generic() to generically convert any value to a string or print as
a string.
This commit is contained in:
Bruce Hill 2025-03-27 17:26:51 -04:00
parent 2186e84de0
commit 3c52a75633
35 changed files with 867 additions and 725 deletions

View File

@ -73,7 +73,7 @@ int run_command(Text_t exe, Array_t arg_array, Table_t env_table,
for (int64_t i = 0; i < env_table.entries.length; i++) { for (int64_t i = 0; i < env_table.entries.length; i++) {
struct { Text_t key, value; } *entry = env_table.entries.data + env_table.entries.stride*i; struct { Text_t key, value; } *entry = env_table.entries.data + env_table.entries.stride*i;
const char *env_entry = heap_strf("%k=%k", &entry->key, &entry->value); const char *env_entry = String(entry->key, "=", entry->value);
Array$insert(&env_array, &env_entry, I(0), sizeof(char*)); Array$insert(&env_array, &env_entry, I(0), sizeof(char*));
} }
Array$insert_value(&env_array, NULL, I(0), sizeof(char*)); Array$insert_value(&env_array, NULL, I(0), sizeof(char*));
@ -254,7 +254,7 @@ OptionalClosure_t command_by_line(Text_t exe, Array_t arg_array, Table_t env_tab
for (int64_t i = 0; i < env_table.entries.length; i++) { for (int64_t i = 0; i < env_table.entries.length; i++) {
struct { Text_t key, value; } *entry = env_table.entries.data + env_table.entries.stride*i; struct { Text_t key, value; } *entry = env_table.entries.data + env_table.entries.stride*i;
const char *env_entry = heap_strf("%k=%k", &entry->key, &entry->value); const char *env_entry = String(entry->key, "=", entry->value);
Array$insert(&env_array, &env_entry, I(0), sizeof(char*)); Array$insert(&env_array, &env_entry, I(0), sizeof(char*));
} }
Array$insert_value(&env_array, NULL, I(0), sizeof(char*)); Array$insert_value(&env_array, NULL, I(0), sizeof(char*));

View File

@ -1,7 +1,6 @@
// Some basic operations defined on AST nodes, mainly converting to // Some basic operations defined on AST nodes, mainly converting to
// strings for debugging. // strings for debugging.
#include <gc/cord.h> #include <gc/cord.h>
#include <printf.h>
#include <stdarg.h> #include <stdarg.h>
#include "ast.h" #include "ast.h"
@ -174,6 +173,11 @@ CORD ast_to_xml(ast_t *ast)
} }
} }
const char *ast_to_str(ast_t *ast)
{
return CORD_to_const_char_star(ast_to_xml(ast));
}
CORD type_ast_to_xml(type_ast_t *t) CORD type_ast_to_xml(type_ast_t *t)
{ {
if (!t) return "NULL"; if (!t) return "NULL";
@ -195,19 +199,6 @@ CORD type_ast_to_xml(type_ast_t *t)
} }
} }
int printf_ast(FILE *stream, const struct printf_info *info, const void *const args[])
{
ast_t *ast = *(ast_t**)(args[0]);
if (ast) {
if (info->alt)
return fprintf(stream, "%.*s", (int)(ast->end - ast->start), ast->start);
else
return CORD_put(ast_to_xml(ast), stream);
} else {
return fputs("(null)", stream);
}
}
PUREFUNC bool is_idempotent(ast_t *ast) PUREFUNC bool is_idempotent(ast_t *ast)
{ {
switch (ast->tag) { switch (ast->tag) {

View File

@ -4,7 +4,6 @@
#include <err.h> #include <err.h>
#include <gc/cord.h> #include <gc/cord.h>
#include <printf.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
@ -344,8 +343,8 @@ struct ast_s {
}; };
CORD ast_to_xml(ast_t *ast); CORD ast_to_xml(ast_t *ast);
const char *ast_to_str(ast_t *ast);
CORD type_ast_to_xml(type_ast_t *ast); CORD 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); PUREFUNC bool is_idempotent(ast_t *ast);
void visit_topologically(ast_list_t *ast, Closure_t fn); void visit_topologically(ast_list_t *ast, Closure_t fn);

View File

@ -47,7 +47,7 @@ CORD promote_to_optional(type_t *t, CORD code)
case TYPE_IBITS16: return CORD_all("((OptionalInt16_t){", code, "})"); case TYPE_IBITS16: return CORD_all("((OptionalInt16_t){", code, "})");
case TYPE_IBITS32: return CORD_all("((OptionalInt32_t){", code, "})"); case TYPE_IBITS32: return CORD_all("((OptionalInt32_t){", code, "})");
case TYPE_IBITS64: return CORD_all("((OptionalInt64_t){", code, "})"); case TYPE_IBITS64: return CORD_all("((OptionalInt64_t){", code, "})");
default: errx(1, "Unsupported in type: %T", t); default: errx(1, "Unsupported in type: ", type_to_str(t));
} }
} else if (t->tag == ByteType) { } else if (t->tag == ByteType) {
return CORD_all("((OptionalByte_t){", code, "})"); return CORD_all("((OptionalByte_t){", code, "})");
@ -383,7 +383,7 @@ static void add_closed_vars(Table_t *closed_vars, env_t *enclosing_scope, env_t
else if (clause->pattern->tag == FunctionCall && Match(clause->pattern, FunctionCall)->fn->tag == Var) else if (clause->pattern->tag == FunctionCall && Match(clause->pattern, FunctionCall)->fn->tag == Var)
clause_tag_name = Match(Match(clause->pattern, FunctionCall)->fn, Var)->name; clause_tag_name = Match(Match(clause->pattern, FunctionCall)->fn, Var)->name;
else else
code_err(clause->pattern, "This is not a valid pattern for a %T enum", subject_t); code_err(clause->pattern, "This is not a valid pattern for a ", type_to_str(subject_t), " enum");
type_t *tag_type = NULL; type_t *tag_type = NULL;
for (tag_t *tag = enum_t->tags; tag; tag = tag->next) { for (tag_t *tag = enum_t->tags; tag; tag = tag->next) {
@ -407,7 +407,7 @@ static void add_closed_vars(Table_t *closed_vars, env_t *enclosing_scope, env_t
case Reduction: { case Reduction: {
auto reduction = Match(ast, Reduction); auto reduction = Match(ast, Reduction);
static int64_t next_id = 1; static int64_t next_id = 1;
ast_t *item = FakeAST(Var, heap_strf("$it%ld", next_id++)); ast_t *item = FakeAST(Var, String("$it", next_id++));
ast_t *loop = FakeAST(For, .vars=new(ast_list_t, .ast=item), .iter=reduction->iter, .body=FakeAST(Pass)); ast_t *loop = FakeAST(For, .vars=new(ast_list_t, .ast=item), .iter=reduction->iter, .body=FakeAST(Pass));
env_t *scope = for_scope(env, loop); env_t *scope = for_scope(env, loop);
add_closed_vars(closed_vars, enclosing_scope, scope, reduction->key ? reduction->key : item); add_closed_vars(closed_vars, enclosing_scope, scope, reduction->key ? reduction->key : item);
@ -578,11 +578,11 @@ CORD compile_type(type_t *t)
return CORD_all(namespace_prefix(s->env, s->env->namespace->parent), "$Optional", s->name, "$$type"); return CORD_all(namespace_prefix(s->env, s->env->namespace->parent), "$Optional", s->name, "$$type");
} }
default: default:
compiler_err(NULL, NULL, NULL, "Optional types are not supported for: %T", t); compiler_err(NULL, NULL, NULL, "Optional types are not supported for: ", type_to_str(t));
} }
} }
case TypeInfoType: return "TypeInfo_t"; case TypeInfoType: return "TypeInfo_t";
default: compiler_err(NULL, NULL, NULL, "Compiling type is not implemented for type with tag %d", t->tag); default: compiler_err(NULL, NULL, NULL, "Compiling type is not implemented for type with tag ", t->tag);
} }
} }
@ -595,9 +595,9 @@ static CORD compile_lvalue(env_t *env, ast_t *ast)
} else if (ast->tag == FieldAccess) { } else if (ast->tag == FieldAccess) {
ast_t *subject = Match(ast, FieldAccess)->fielded; ast_t *subject = Match(ast, FieldAccess)->fielded;
type_t *t = get_type(env, subject); type_t *t = get_type(env, subject);
code_err(subject, "This is an immutable %T value, you can't assign to its fields", t); code_err(subject, "This is an immutable ", type_to_str(t), " value, you can't assign to its fields");
} else { } else {
code_err(ast, "This is a value of type %T and can't be used as an assignment target", get_type(env, ast)); code_err(ast, "This is a value of type ", type_to_str(get_type(env, ast)), " and can't be used as an assignment target");
} }
} }
@ -626,8 +626,8 @@ static CORD compile_lvalue(env_t *env, ast_t *ast)
} else { } else {
return CORD_all("Array_lvalue(", compile_type(item_type), ", ", target_code, ", ", return CORD_all("Array_lvalue(", compile_type(item_type), ", ", target_code, ", ",
index_code, index_code,
", ", heap_strf("%ld", ast->start - ast->file->text), ", ", String((int)(ast->start - ast->file->text)),
", ", heap_strf("%ld", ast->end - ast->file->text), ")"); ", ", String((int)(ast->end - ast->file->text)), ")");
} }
} else if (container_t->tag == TableType) { } else if (container_t->tag == TableType) {
auto table_type = Match(container_t, TableType); auto table_type = Match(container_t, TableType);
@ -732,7 +732,7 @@ CORD check_none(type_t *t, CORD value)
return CORD_all("((", value, ").tv_usec < 0)"); return CORD_all("((", value, ").tv_usec < 0)");
else if (t->tag == MutexedType) else if (t->tag == MutexedType)
return CORD_all("(", value, " == NULL)"); return CORD_all("(", value, " == NULL)");
errx(1, "Optional check not implemented for: %T", t); print_err("Optional check not implemented for: ", type_to_str(t));
} }
static CORD compile_condition(env_t *env, ast_t *ast) static CORD compile_condition(env_t *env, ast_t *ast)
@ -751,7 +751,7 @@ static CORD compile_condition(env_t *env, ast_t *ast)
} else if (t->tag == PointerType) { } else if (t->tag == PointerType) {
code_err(ast, "This pointer will always be non-none, so it should not be used in a conditional."); code_err(ast, "This pointer will always be non-none, so it should not be used in a conditional.");
} else { } else {
code_err(ast, "%T values cannot be used for conditionals", t); code_err(ast, type_to_str(t), " values cannot be used for conditionals");
} }
} }
@ -802,7 +802,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
} }
if (clause->pattern->tag != FunctionCall || Match(clause->pattern, FunctionCall)->fn->tag != Var) if (clause->pattern->tag != FunctionCall || Match(clause->pattern, FunctionCall)->fn->tag != Var)
code_err(clause->pattern, "This is not a valid pattern for a %T enum type", subject_t); code_err(clause->pattern, "This is not a valid pattern for a ", type_to_str(subject_t), " enum type");
const char *clause_tag_name = Match(Match(clause->pattern, FunctionCall)->fn, Var)->name; const char *clause_tag_name = Match(Match(clause->pattern, FunctionCall)->fn, Var)->name;
code = CORD_all(code, "case ", namespace_prefix(enum_t->env, enum_t->env->namespace), "tag$", clause_tag_name, ": {\n"); code = CORD_all(code, "case ", namespace_prefix(enum_t->env, enum_t->env->namespace), "tag$", clause_tag_name, ": {\n");
@ -832,9 +832,9 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
arg_t *field = tag_struct->fields; arg_t *field = tag_struct->fields;
for (arg_ast_t *arg = args; arg || field; arg = arg->next) { for (arg_ast_t *arg = args; arg || field; arg = arg->next) {
if (!arg) if (!arg)
code_err(ast, "The field %T.%s.%s wasn't accounted for", subject_t, clause_tag_name, field->name); code_err(ast, "The field ", type_to_str(subject_t), ".", clause_tag_name, ".", field->name, " wasn't accounted for");
if (!field) if (!field)
code_err(arg->value, "This is one more field than %T has", subject_t); code_err(arg->value, "This is one more field than ", type_to_str(subject_t), " has");
if (arg->name) if (arg->name)
code_err(arg->value, "Named arguments are not currently supported"); code_err(arg->value, "Named arguments are not currently supported");
@ -961,8 +961,8 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
if (test->expected) { if (test->expected) {
type_t *expected_type = get_type(env, test->expected); type_t *expected_type = get_type(env, test->expected);
if (!type_eq(expr_t, expected_type)) if (!type_eq(expr_t, expected_type))
code_err(ast, "The type on the top of this test (%T) is different from the type on the bottom (%T)", code_err(ast, "The type on the top of this test (", type_to_str(expr_t),
expr_t, expected_type); ") is different from the type on the bottom (", type_to_str(expected_type), ")");
return CORD_asprintf( return CORD_asprintf(
"%rtest(%r, %r, %r, %ld, %ld);", "%rtest(%r, %r, %r, %ld, %ld);",
setup, test_code, setup, test_code,
@ -987,7 +987,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
} else { } else {
type_t *t = get_type(env, decl->value); type_t *t = get_type(env, decl->value);
if (t->tag == AbortType || t->tag == VoidType || t->tag == ReturnType) if (t->tag == AbortType || t->tag == VoidType || t->tag == ReturnType)
code_err(ast, "You can't declare a variable with a %T value", t); code_err(ast, "You can't declare a variable with a ", type_to_str(t), " value");
CORD val_code = compile_maybe_incref(env, decl->value, t); CORD val_code = compile_maybe_incref(env, decl->value, t);
if (t->tag == FunctionType) { if (t->tag == FunctionType) {
@ -1069,14 +1069,14 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
if (update->rhs->tag == Int && is_numeric_type(non_optional(lhs_t))) { if (update->rhs->tag == Int && is_numeric_type(non_optional(lhs_t))) {
rhs = compile_int_to_type(env, update->rhs, lhs_t); rhs = compile_int_to_type(env, update->rhs, lhs_t);
} else if (!promote(env, update->rhs, &rhs, rhs_t, lhs_t)) { } else if (!promote(env, update->rhs, &rhs, rhs_t, lhs_t)) {
code_err(ast, "I can't do operations between %T and %T", lhs_t, rhs_t); code_err(ast, "I can't do operations between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t));
} }
bool lhs_is_optional_num = (lhs_t->tag == OptionalType && Match(lhs_t, OptionalType)->type && Match(lhs_t, OptionalType)->type->tag == NumType); bool lhs_is_optional_num = (lhs_t->tag == OptionalType && Match(lhs_t, OptionalType)->type && Match(lhs_t, OptionalType)->type->tag == NumType);
switch (update->op) { switch (update->op) {
case BINOP_MULT: case BINOP_MULT:
if (lhs_t->tag != IntType && lhs_t->tag != NumType && lhs_t->tag != ByteType && !lhs_is_optional_num) if (lhs_t->tag != IntType && lhs_t->tag != NumType && lhs_t->tag != ByteType && !lhs_is_optional_num)
code_err(ast, "I can't do a multiply assignment with this operator between %T and %T", lhs_t, rhs_t); code_err(ast, "I can't do a multiply assignment with this operator between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t));
if (lhs_t->tag == NumType) { // 0*INF -> NaN, needs checking if (lhs_t->tag == NumType) { // 0*INF -> NaN, needs checking
return CORD_asprintf("%r *= %r;\n" return CORD_asprintf("%r *= %r;\n"
"if (isnan(%r))\n" "if (isnan(%r))\n"
@ -1089,7 +1089,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
return CORD_all(lhs, " *= ", rhs, ";"); return CORD_all(lhs, " *= ", rhs, ";");
case BINOP_DIVIDE: case BINOP_DIVIDE:
if (lhs_t->tag != IntType && lhs_t->tag != NumType && lhs_t->tag != ByteType && !lhs_is_optional_num) if (lhs_t->tag != IntType && lhs_t->tag != NumType && lhs_t->tag != ByteType && !lhs_is_optional_num)
code_err(ast, "I can't do a divide assignment with this operator between %T and %T", lhs_t, rhs_t); code_err(ast, "I can't do a divide assignment with this operator between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t));
if (lhs_t->tag == NumType) { // 0/0 or INF/INF -> NaN, needs checking if (lhs_t->tag == NumType) { // 0/0 or INF/INF -> NaN, needs checking
return CORD_asprintf("%r /= %r;\n" return CORD_asprintf("%r /= %r;\n"
"if (isnan(%r))\n" "if (isnan(%r))\n"
@ -1102,19 +1102,19 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
return CORD_all(lhs, " /= ", rhs, ";"); return CORD_all(lhs, " /= ", rhs, ";");
case BINOP_MOD: case BINOP_MOD:
if (lhs_t->tag != IntType && lhs_t->tag != NumType && lhs_t->tag != ByteType && !lhs_is_optional_num) if (lhs_t->tag != IntType && lhs_t->tag != NumType && lhs_t->tag != ByteType && !lhs_is_optional_num)
code_err(ast, "I can't do a mod assignment with this operator between %T and %T", lhs_t, rhs_t); code_err(ast, "I can't do a mod assignment with this operator between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t));
return CORD_all(lhs, " = ", lhs, " % ", rhs); return CORD_all(lhs, " = ", lhs, " % ", rhs);
case BINOP_MOD1: case BINOP_MOD1:
if (lhs_t->tag != IntType && lhs_t->tag != NumType && lhs_t->tag != ByteType && !lhs_is_optional_num) if (lhs_t->tag != IntType && lhs_t->tag != NumType && lhs_t->tag != ByteType && !lhs_is_optional_num)
code_err(ast, "I can't do a mod assignment with this operator between %T and %T", lhs_t, rhs_t); code_err(ast, "I can't do a mod assignment with this operator between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t));
return CORD_all(lhs, " = (((", lhs, ") - 1) % ", rhs, ") + 1;"); return CORD_all(lhs, " = (((", lhs, ") - 1) % ", rhs, ") + 1;");
case BINOP_PLUS: case BINOP_PLUS:
if (lhs_t->tag != IntType && lhs_t->tag != NumType && lhs_t->tag != ByteType && !lhs_is_optional_num) if (lhs_t->tag != IntType && lhs_t->tag != NumType && lhs_t->tag != ByteType && !lhs_is_optional_num)
code_err(ast, "I can't do an addition assignment with this operator between %T and %T", lhs_t, rhs_t); code_err(ast, "I can't do an addition assignment with this operator between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t));
return CORD_all(lhs, " += ", rhs, ";"); return CORD_all(lhs, " += ", rhs, ";");
case BINOP_MINUS: case BINOP_MINUS:
if (lhs_t->tag != IntType && lhs_t->tag != NumType && lhs_t->tag != ByteType && !lhs_is_optional_num) if (lhs_t->tag != IntType && lhs_t->tag != NumType && lhs_t->tag != ByteType && !lhs_is_optional_num)
code_err(ast, "I can't do a subtraction assignment with this operator between %T and %T", lhs_t, rhs_t); code_err(ast, "I can't do a subtraction assignment with this operator between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t));
return CORD_all(lhs, " -= ", rhs, ";"); return CORD_all(lhs, " -= ", rhs, ";");
case BINOP_POWER: { case BINOP_POWER: {
if (lhs_t->tag != NumType && !lhs_is_optional_num) if (lhs_t->tag != NumType && !lhs_is_optional_num)
@ -1126,19 +1126,19 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
} }
case BINOP_LSHIFT: case BINOP_LSHIFT:
if (lhs_t->tag != IntType && lhs_t->tag != ByteType) if (lhs_t->tag != IntType && lhs_t->tag != ByteType)
code_err(ast, "I can't do a shift assignment with this operator between %T and %T", lhs_t, rhs_t); code_err(ast, "I can't do a shift assignment with this operator between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t));
return CORD_all(lhs, " <<= ", rhs, ";"); return CORD_all(lhs, " <<= ", rhs, ";");
case BINOP_RSHIFT: case BINOP_RSHIFT:
if (lhs_t->tag != IntType && lhs_t->tag != ByteType) if (lhs_t->tag != IntType && lhs_t->tag != ByteType)
code_err(ast, "I can't do a shift assignment with this operator between %T and %T", lhs_t, rhs_t); code_err(ast, "I can't do a shift assignment with this operator between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t));
return CORD_all(lhs, " >>= ", rhs, ";"); return CORD_all(lhs, " >>= ", rhs, ";");
case BINOP_ULSHIFT: case BINOP_ULSHIFT:
if (lhs_t->tag != IntType && lhs_t->tag != ByteType) if (lhs_t->tag != IntType && lhs_t->tag != ByteType)
code_err(ast, "I can't do a shift assignment with this operator between %T and %T", lhs_t, rhs_t); code_err(ast, "I can't do a shift assignment with this operator between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t));
return CORD_all("{ ", compile_unsigned_type(lhs_t), " *dest = (void*)&(", lhs, "); *dest <<= ", rhs, "; }"); return CORD_all("{ ", compile_unsigned_type(lhs_t), " *dest = (void*)&(", lhs, "); *dest <<= ", rhs, "; }");
case BINOP_URSHIFT: case BINOP_URSHIFT:
if (lhs_t->tag != IntType && lhs_t->tag != ByteType) if (lhs_t->tag != IntType && lhs_t->tag != ByteType)
code_err(ast, "I can't do a shift assignment with this operator between %T and %T", lhs_t, rhs_t); code_err(ast, "I can't do a shift assignment with this operator between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t));
return CORD_all("{ ", compile_unsigned_type(lhs_t), " *dest = (void*)&(", lhs, "); *dest >>= ", rhs, "; }"); return CORD_all("{ ", compile_unsigned_type(lhs_t), " *dest = (void*)&(", lhs, "); *dest >>= ", rhs, "; }");
case BINOP_AND: { case BINOP_AND: {
if (lhs_t->tag == BoolType) if (lhs_t->tag == BoolType)
@ -1148,7 +1148,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
else if (lhs_t->tag == OptionalType) else if (lhs_t->tag == OptionalType)
return CORD_all("if (!(", check_none(lhs_t, lhs), ")) ", lhs, " = ", promote_to_optional(rhs_t, rhs), ";"); return CORD_all("if (!(", check_none(lhs_t, lhs), ")) ", lhs, " = ", promote_to_optional(rhs_t, rhs), ";");
else else
code_err(ast, "'or=' is not implemented for %T types", lhs_t); code_err(ast, "'or=' is not implemented for ", type_to_str(lhs_t), " types");
} }
case BINOP_OR: { case BINOP_OR: {
if (lhs_t->tag == BoolType) if (lhs_t->tag == BoolType)
@ -1158,11 +1158,11 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
else if (lhs_t->tag == OptionalType) else if (lhs_t->tag == OptionalType)
return CORD_all("if (", check_none(lhs_t, lhs), ") ", lhs, " = ", promote_to_optional(rhs_t, rhs), ";"); return CORD_all("if (", check_none(lhs_t, lhs), ") ", lhs, " = ", promote_to_optional(rhs_t, rhs), ";");
else else
code_err(ast, "'or=' is not implemented for %T types", lhs_t); code_err(ast, "'or=' is not implemented for ", type_to_str(lhs_t), " types");
} }
case BINOP_XOR: case BINOP_XOR:
if (lhs_t->tag != IntType && lhs_t->tag != BoolType && lhs_t->tag != ByteType) if (lhs_t->tag != IntType && lhs_t->tag != BoolType && lhs_t->tag != ByteType)
code_err(ast, "I can't do an xor assignment with this operator between %T and %T", lhs_t, rhs_t); code_err(ast, "I can't do an xor assignment with this operator between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t));
return CORD_all(lhs, " ^= ", rhs, ";"); return CORD_all(lhs, " ^= ", rhs, ";");
case BINOP_CONCAT: { case BINOP_CONCAT: {
if (lhs_t->tag == TextType) { if (lhs_t->tag == TextType) {
@ -1175,7 +1175,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
else else
return CORD_all(lhs, " = Array$concat(", lhs, ", ", rhs, ", ", padded_item_size, ");"); return CORD_all(lhs, " = Array$concat(", lhs, ", ", rhs, ", ", padded_item_size, ");");
} else { } else {
code_err(ast, "'++=' is not implemented for %T types", lhs_t); code_err(ast, "'++=' is not implemented for ", type_to_str(lhs_t), " types");
} }
} }
default: code_err(ast, "Update assignments are not implemented for this operation"); default: code_err(ast, "Update assignments are not implemented for this operation");
@ -1209,7 +1209,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
if (env->loop_ctx) if (env->loop_ctx)
code_err(ast, "This 'skip' is not inside any loop"); code_err(ast, "This 'skip' is not inside any loop");
else if (target) else if (target)
code_err(ast, "No loop target named '%s' was found", target); code_err(ast, "No loop target named '", target, "' was found");
else else
return "continue;"; return "continue;";
} }
@ -1238,7 +1238,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
if (env->loop_ctx) if (env->loop_ctx)
code_err(ast, "This 'stop' is not inside any loop"); code_err(ast, "This 'stop' is not inside any loop");
else if (target) else if (target)
code_err(ast, "No loop target named '%s' was found", target); code_err(ast, "No loop target named '", target, "' was found");
else else
return "break;"; return "break;";
} }
@ -1307,7 +1307,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
return CORD_all(code, "return ", value, ";"); return CORD_all(code, "return ", value, ";");
} else { } else {
if (env->fn_ret->tag != VoidType) if (env->fn_ret->tag != VoidType)
code_err(ast, "This function expects you to return a %T value", env->fn_ret); code_err(ast, "This function expects you to return a ", type_to_str(env->fn_ret), " value");
return CORD_all(code, "return;"); return CORD_all(code, "return;");
} }
} }
@ -1349,7 +1349,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
ast_t *held = Match(ast, Holding)->mutexed; ast_t *held = Match(ast, Holding)->mutexed;
type_t *held_type = get_type(env, held); type_t *held_type = get_type(env, held);
if (held_type->tag != MutexedType) if (held_type->tag != MutexedType)
code_err(held, "This is a %t, not a mutexed value", held_type); code_err(held, "This is a ", type_to_str(held_type), ", not a mutexed value");
CORD code = CORD_all( CORD code = CORD_all(
"{ // Holding\n", "{ // Holding\n",
"MutexedData_t mutexed = ", compile(env, held), ";\n", "MutexedData_t mutexed = ", compile(env, held), ";\n",
@ -1421,7 +1421,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
last = compile_to_type(env, args->value, INT_TYPE); last = compile_to_type(env, args->value, INT_TYPE);
if (args->next) { if (args->next) {
if (args->next->name && !streq(args->next->name, "step")) if (args->next->name && !streq(args->next->name, "step"))
code_err(args->next->value, "Invalid argument name: %s", args->next->name); code_err(args->next->value, "Invalid argument name: ", args->next->name);
if (get_type(env, args->next->value)->tag == OptionalType) if (get_type(env, args->next->value)->tag == OptionalType)
optional_step = compile_to_type(env, args->next->value, Type(OptionalType, .type=INT_TYPE)); optional_step = compile_to_type(env, args->next->value, Type(OptionalType, .type=INT_TYPE));
else else
@ -1434,7 +1434,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
step = compile_to_type(env, args->value, INT_TYPE); step = compile_to_type(env, args->value, INT_TYPE);
if (args->next) { if (args->next) {
if (args->next->name && !streq(args->next->name, "last")) if (args->next->name && !streq(args->next->name, "last"))
code_err(args->next->value, "Invalid argument name: %s", args->next->name); code_err(args->next->value, "Invalid argument name: ", args->next->name);
last = compile_to_type(env, args->next->value, INT_TYPE); last = compile_to_type(env, args->next->value, INT_TYPE);
} }
} }
@ -1692,7 +1692,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
return code; return code;
} }
default: code_err(for_->iter, "Iteration is not implemented for type: %T", iter_t); default: code_err(for_->iter, "Iteration is not implemented for type: ", type_to_str(iter_t));
} }
} }
case If: { case If: {
@ -1766,7 +1766,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
return with_source_info(ast, CORD_all("_$", name, "$$initialize();\n")); return with_source_info(ast, CORD_all("_$", name, "$$initialize();\n"));
} else if (use->what == USE_MODULE) { } else if (use->what == USE_MODULE) {
glob_t tm_files; glob_t tm_files;
if (glob(heap_strf("~/.local/share/tomo/installed/%s/[!._0-9]*.tm", use->path), GLOB_TILDE, NULL, &tm_files) != 0) if (glob(String("~/.local/share/tomo/installed/", use->path, "/[!._0-9]*.tm"), GLOB_TILDE, NULL, &tm_files) != 0)
code_err(ast, "Could not find library"); code_err(ast, "Could not find library");
CORD initialization = CORD_EMPTY; CORD initialization = CORD_EMPTY;
@ -1786,7 +1786,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
} }
default: default:
if (!is_discardable(env, ast)) if (!is_discardable(env, ast))
code_err(ast, "The %T result of this statement cannot be discarded", get_type(env, ast)); code_err(ast, "The ", type_to_str(get_type(env, ast)), " result of this statement cannot be discarded");
return CORD_asprintf("(void)%r;", compile(env, ast)); return CORD_asprintf("(void)%r;", compile(env, ast));
} }
} }
@ -1818,7 +1818,7 @@ CORD expr_as_text(CORD expr, type_t *t, CORD color)
case OptionalType: return CORD_asprintf("Optional$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(t)); case OptionalType: return CORD_asprintf("Optional$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(t));
case StructType: case EnumType: case MutexedType: case StructType: case EnumType: case MutexedType:
return CORD_asprintf("generic_as_text(stack(%r), %r, %r)", expr, color, compile_type_info(t)); return CORD_asprintf("generic_as_text(stack(%r), %r, %r)", expr, color, compile_type_info(t));
default: compiler_err(NULL, NULL, NULL, "Stringifying is not supported for %T", t); default: compiler_err(NULL, NULL, NULL, "Stringifying is not supported for ", type_to_str(t));
} }
} }
@ -1847,7 +1847,7 @@ CORD compile_to_pointer_depth(env_t *env, ast_t *ast, int64_t target_depth, bool
if (ast->tag == Var && target_depth == 1) if (ast->tag == Var && target_depth == 1)
val = CORD_all("(&", val, ")"); val = CORD_all("(&", val, ")");
else else
code_err(ast, "This should be a pointer, not %T", get_type(env, ast)); code_err(ast, "This should be a pointer, not ", type_to_str(get_type(env, ast)));
t = Type(PointerType, .pointed=t, .is_stack=true); t = Type(PointerType, .pointed=t, .is_stack=true);
++depth; ++depth;
} else { } else {
@ -1894,7 +1894,7 @@ CORD compile_to_type(env_t *env, ast_t *ast, type_t *t)
CORD code = compile(env, ast); CORD code = compile(env, ast);
if (!promote(env, ast, &code, actual, t)) if (!promote(env, ast, &code, actual, t))
code_err(ast, "I expected a %T here, but this is a %T", t, actual); code_err(ast, "I expected a ", type_to_str(t), " here, but this is a ", type_to_str(actual));
return code; return code;
} }
@ -1904,7 +1904,7 @@ CORD compile_int_to_type(env_t *env, ast_t *ast, type_t *target)
CORD code = compile(env, ast); CORD code = compile(env, ast);
type_t *actual_type = get_type(env, ast); type_t *actual_type = get_type(env, ast);
if (!promote(env, ast, &code, actual_type, target)) if (!promote(env, ast, &code, actual_type, target))
code_err(ast, "I couldn't promote this %T to a %T", actual_type, target); code_err(ast, "I couldn't promote this ", type_to_str(actual_type), " to a ", type_to_str(target));
return code; return code;
} }
@ -1964,9 +1964,9 @@ CORD compile_int_to_type(env_t *env, ast_t *ast, type_t *target)
break; break;
default: break; default: break;
} }
code_err(ast, "This integer cannot fit in a %d-bit value", target_bits); code_err(ast, "This integer cannot fit in a ", target_bits, "-bit value");
} else { } else {
code_err(ast, "I don't know how to compile this to a %T", target); code_err(ast, "I don't know how to compile this to a ", type_to_str(target));
} }
} }
@ -2006,7 +2006,7 @@ CORD compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_t
// Find positional: // Find positional:
for (arg_ast_t *call_arg = call_args; call_arg; call_arg = call_arg->next) { for (arg_ast_t *call_arg = call_args; call_arg; call_arg = call_arg->next) {
if (call_arg->name) continue; if (call_arg->name) continue;
const char *pseudoname = heap_strf("%ld", i++); const char *pseudoname = String(i++);
if (!Table$str_get(used_args, pseudoname)) { if (!Table$str_get(used_args, pseudoname)) {
CORD value; CORD value;
if (spec_arg->type->tag == IntType && call_arg->value->tag == Int) { if (spec_arg->type->tag == IntType && call_arg->value->tag == Int) {
@ -2038,7 +2038,7 @@ CORD compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_t
} }
assert(spec_arg->name); assert(spec_arg->name);
code_err(call_ast, "The required argument '%s' was not provided", spec_arg->name); code_err(call_ast, "The required argument '", spec_arg->name, "' was not provided");
found_it: continue; found_it: continue;
} }
@ -2046,9 +2046,9 @@ CORD compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_t
for (arg_ast_t *call_arg = call_args; call_arg; call_arg = call_arg->next) { for (arg_ast_t *call_arg = call_args; call_arg; call_arg = call_arg->next) {
if (call_arg->name) { if (call_arg->name) {
if (!Table$str_get(used_args, call_arg->name)) if (!Table$str_get(used_args, call_arg->name))
code_err(call_arg->value, "There is no argument with the name '%s'", call_arg->name); code_err(call_arg->value, "There is no argument with the name '", call_arg->name, "'");
} else { } else {
const char *pseudoname = heap_strf("%ld", i++); const char *pseudoname = String(i++);
if (!Table$str_get(used_args, pseudoname)) if (!Table$str_get(used_args, pseudoname))
code_err(call_arg->value, "This is one argument too many!"); code_err(call_arg->value, "This is one argument too many!");
} }
@ -2230,7 +2230,7 @@ CORD compile_none(type_t *t)
return CORD_all("((", compile_type(t), "){", namespace_prefix(enum_env, enum_env->namespace), "null})"); return CORD_all("((", compile_type(t), "){", namespace_prefix(enum_env, enum_env->namespace), "null})");
} }
case MutexedType: return "NONE_MUTEXED_DATA"; case MutexedType: return "NONE_MUTEXED_DATA";
default: compiler_err(NULL, NULL, NULL, "none isn't implemented for this type: %T", t); default: compiler_err(NULL, NULL, NULL, "none isn't implemented for this type: ", type_to_str(t));
} }
} }
@ -2314,7 +2314,7 @@ CORD compile(env_t *env, ast_t *ast)
else if (t->tag == OptionalType) else if (t->tag == OptionalType)
return check_none(t, compile(env, value)); return check_none(t, compile(env, value));
code_err(ast, "I don't know how to negate values of type %T", t); code_err(ast, "I don't know how to negate values of type ", type_to_str(t));
} }
case Negative: { case Negative: {
ast_t *value = Match(ast, Negative)->value; ast_t *value = Match(ast, Negative)->value;
@ -2329,7 +2329,7 @@ CORD compile(env_t *env, ast_t *ast)
if (t->tag == IntType || t->tag == NumType) if (t->tag == IntType || t->tag == NumType)
return CORD_all("-(", compile(env, value), ")"); return CORD_all("-(", compile(env, value), ")");
code_err(ast, "I don't know how to get the negative value of type %T", t); code_err(ast, "I don't know how to get the negative value of type ", type_to_str(t));
} }
// TODO: for constructors, do new(T, ...) instead of heap((T){...}) // TODO: for constructors, do new(T, ...) instead of heap((T){...})
@ -2349,7 +2349,7 @@ CORD compile(env_t *env, ast_t *ast)
ast_t *held = Match(ast, Holding)->mutexed; ast_t *held = Match(ast, Holding)->mutexed;
type_t *held_type = get_type(env, held); type_t *held_type = get_type(env, held);
if (held_type->tag != MutexedType) if (held_type->tag != MutexedType)
code_err(held, "This is a %t, not a mutexed value", held_type); code_err(held, "This is a ", type_to_str(held_type), ", not a mutexed value");
CORD code = CORD_all( CORD code = CORD_all(
"({ // Holding\n", "({ // Holding\n",
"MutexedData_t mutexed = ", compile(env, held), ";\n", "MutexedData_t mutexed = ", compile(env, held), ";\n",
@ -2412,7 +2412,7 @@ CORD compile(env_t *env, ast_t *ast)
} else if (rhs_t->tag == BoolType) { } else if (rhs_t->tag == BoolType) {
return CORD_all("((!", check_none(lhs_t, compile(env, binop->lhs)), ") || ", compile(env, binop->rhs), ")"); return CORD_all("((!", check_none(lhs_t, compile(env, binop->lhs)), ") || ", compile(env, binop->rhs), ")");
} else { } else {
code_err(ast, "I don't know how to do an 'or' operation between %T and %T", lhs_t, rhs_t); code_err(ast, "I don't know how to do an 'or' operation between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t));
} }
} else if (binop->op == BINOP_AND && lhs_t->tag == OptionalType) { } else if (binop->op == BINOP_AND && lhs_t->tag == OptionalType) {
if (rhs_t->tag == AbortType || rhs_t->tag == ReturnType) { if (rhs_t->tag == AbortType || rhs_t->tag == ReturnType) {
@ -2425,7 +2425,7 @@ CORD compile(env_t *env, ast_t *ast)
} else if (rhs_t->tag == BoolType) { } else if (rhs_t->tag == BoolType) {
return CORD_all("((!", check_none(lhs_t, compile(env, binop->lhs)), ") && ", compile(env, binop->rhs), ")"); return CORD_all("((!", check_none(lhs_t, compile(env, binop->lhs)), ") && ", compile(env, binop->rhs), ")");
} else { } else {
code_err(ast, "I don't know how to do an 'or' operation between %T and %T", lhs_t, rhs_t); code_err(ast, "I don't know how to do an 'or' operation between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t));
} }
} }
@ -2468,12 +2468,12 @@ CORD compile(env_t *env, ast_t *ast)
else if (promote(env, binop->lhs, &lhs, lhs_t, rhs_t)) else if (promote(env, binop->lhs, &lhs, lhs_t, rhs_t))
operand_t = rhs_t; operand_t = rhs_t;
else else
code_err(ast, "I can't do operations between %T and %T", lhs_t, rhs_t); code_err(ast, "I can't do operations between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t));
switch (binop->op) { switch (binop->op) {
case BINOP_POWER: { case BINOP_POWER: {
if (operand_t->tag != NumType) if (operand_t->tag != NumType)
code_err(ast, "Exponentiation is only supported for Num types, not %T", operand_t); code_err(ast, "Exponentiation is only supported for Num types, not ", type_to_str(operand_t));
if (operand_t->tag == NumType && Match(operand_t, NumType)->bits == TYPE_NBITS32) if (operand_t->tag == NumType && Match(operand_t, NumType)->bits == TYPE_NBITS32)
return CORD_all("powf(", lhs, ", ", rhs, ")"); return CORD_all("powf(", lhs, ", ", rhs, ")");
else else
@ -2481,52 +2481,52 @@ CORD compile(env_t *env, ast_t *ast)
} }
case BINOP_MULT: { case BINOP_MULT: {
if (operand_t->tag != IntType && operand_t->tag != NumType && operand_t->tag != ByteType) if (operand_t->tag != IntType && operand_t->tag != NumType && operand_t->tag != ByteType)
code_err(ast, "Math operations are only supported for values of the same numeric type, not %T and %T", lhs_t, rhs_t); code_err(ast, "Math operations are only supported for values of the same numeric type, not ", type_to_str(lhs_t), " and ", type_to_str(rhs_t));
return CORD_all("(", lhs, " * ", rhs, ")"); return CORD_all("(", lhs, " * ", rhs, ")");
} }
case BINOP_DIVIDE: { case BINOP_DIVIDE: {
if (operand_t->tag != IntType && operand_t->tag != NumType && operand_t->tag != ByteType) if (operand_t->tag != IntType && operand_t->tag != NumType && operand_t->tag != ByteType)
code_err(ast, "Math operations are only supported for values of the same numeric type, not %T and %T", lhs_t, rhs_t); code_err(ast, "Math operations are only supported for values of the same numeric type, not ", type_to_str(lhs_t), " and ", type_to_str(rhs_t));
return CORD_all("(", lhs, " / ", rhs, ")"); return CORD_all("(", lhs, " / ", rhs, ")");
} }
case BINOP_MOD: { case BINOP_MOD: {
if (operand_t->tag != IntType && operand_t->tag != NumType && operand_t->tag != ByteType) if (operand_t->tag != IntType && operand_t->tag != NumType && operand_t->tag != ByteType)
code_err(ast, "Math operations are only supported for values of the same numeric type, not %T and %T", lhs_t, rhs_t); code_err(ast, "Math operations are only supported for values of the same numeric type, not ", type_to_str(lhs_t), " and ", type_to_str(rhs_t));
return CORD_all("(", lhs, " % ", rhs, ")"); return CORD_all("(", lhs, " % ", rhs, ")");
} }
case BINOP_MOD1: { case BINOP_MOD1: {
if (operand_t->tag != IntType && operand_t->tag != NumType && operand_t->tag != ByteType) if (operand_t->tag != IntType && operand_t->tag != NumType && operand_t->tag != ByteType)
code_err(ast, "Math operations are only supported for values of the same numeric type, not %T and %T", lhs_t, rhs_t); code_err(ast, "Math operations are only supported for values of the same numeric type, not ", type_to_str(lhs_t), " and ", type_to_str(rhs_t));
return CORD_all("((((", lhs, ")-1) % (", rhs, ")) + 1)"); return CORD_all("((((", lhs, ")-1) % (", rhs, ")) + 1)");
} }
case BINOP_PLUS: { case BINOP_PLUS: {
if (operand_t->tag != IntType && operand_t->tag != NumType && operand_t->tag != ByteType) if (operand_t->tag != IntType && operand_t->tag != NumType && operand_t->tag != ByteType)
code_err(ast, "Math operations are only supported for values of the same numeric type, not %T and %T", lhs_t, rhs_t); code_err(ast, "Math operations are only supported for values of the same numeric type, not ", type_to_str(lhs_t), " and ", type_to_str(rhs_t));
return CORD_all("(", lhs, " + ", rhs, ")"); return CORD_all("(", lhs, " + ", rhs, ")");
} }
case BINOP_MINUS: { case BINOP_MINUS: {
if (operand_t->tag != IntType && operand_t->tag != NumType && operand_t->tag != ByteType) if (operand_t->tag != IntType && operand_t->tag != NumType && operand_t->tag != ByteType)
code_err(ast, "Math operations are only supported for values of the same numeric type, not %T and %T", lhs_t, rhs_t); code_err(ast, "Math operations are only supported for values of the same numeric type, not ", type_to_str(lhs_t), " and ", type_to_str(rhs_t));
return CORD_all("(", lhs, " - ", rhs, ")"); return CORD_all("(", lhs, " - ", rhs, ")");
} }
case BINOP_LSHIFT: { case BINOP_LSHIFT: {
if (operand_t->tag != IntType && operand_t->tag != NumType && operand_t->tag != ByteType) if (operand_t->tag != IntType && operand_t->tag != NumType && operand_t->tag != ByteType)
code_err(ast, "Math operations are only supported for values of the same numeric type, not %T and %T", lhs_t, rhs_t); code_err(ast, "Math operations are only supported for values of the same numeric type, not ", type_to_str(lhs_t), " and ", type_to_str(rhs_t));
return CORD_all("(", lhs, " << ", rhs, ")"); return CORD_all("(", lhs, " << ", rhs, ")");
} }
case BINOP_RSHIFT: { case BINOP_RSHIFT: {
if (operand_t->tag != IntType && operand_t->tag != NumType && operand_t->tag != ByteType) if (operand_t->tag != IntType && operand_t->tag != NumType && operand_t->tag != ByteType)
code_err(ast, "Math operations are only supported for values of the same numeric type, not %T and %T", lhs_t, rhs_t); code_err(ast, "Math operations are only supported for values of the same numeric type, not ", type_to_str(lhs_t), " and ", type_to_str(rhs_t));
return CORD_all("(", lhs, " >> ", rhs, ")"); return CORD_all("(", lhs, " >> ", rhs, ")");
} }
case BINOP_ULSHIFT: { case BINOP_ULSHIFT: {
if (operand_t->tag != IntType && operand_t->tag != NumType && operand_t->tag != ByteType) if (operand_t->tag != IntType && operand_t->tag != NumType && operand_t->tag != ByteType)
code_err(ast, "Math operations are only supported for values of the same numeric type, not %T and %T", lhs_t, rhs_t); code_err(ast, "Math operations are only supported for values of the same numeric type, not ", type_to_str(lhs_t), " and ", type_to_str(rhs_t));
return CORD_all("(", compile_type(operand_t), ")((", compile_unsigned_type(lhs_t), ")", lhs, " << ", rhs, ")"); return CORD_all("(", compile_type(operand_t), ")((", compile_unsigned_type(lhs_t), ")", lhs, " << ", rhs, ")");
} }
case BINOP_URSHIFT: { case BINOP_URSHIFT: {
if (operand_t->tag != IntType && operand_t->tag != NumType && operand_t->tag != ByteType) if (operand_t->tag != IntType && operand_t->tag != NumType && operand_t->tag != ByteType)
code_err(ast, "Math operations are only supported for values of the same numeric type, not %T and %T", lhs_t, rhs_t); code_err(ast, "Math operations are only supported for values of the same numeric type, not ", type_to_str(lhs_t), " and ", type_to_str(rhs_t));
return CORD_all("(", compile_type(operand_t), ")((", compile_unsigned_type(lhs_t), ")", lhs, " >> ", rhs, ")"); return CORD_all("(", compile_type(operand_t), ")((", compile_unsigned_type(lhs_t), ")", lhs, " >> ", rhs, ")");
} }
case BINOP_EQ: { case BINOP_EQ: {
@ -2607,7 +2607,7 @@ CORD compile(env_t *env, ast_t *ast)
else if (operand_t->tag == IntType || operand_t->tag == ByteType) else if (operand_t->tag == IntType || operand_t->tag == ByteType)
return CORD_all("(", lhs, " & ", rhs, ")"); return CORD_all("(", lhs, " & ", rhs, ")");
else else
code_err(ast, "The 'and' operator isn't supported between %T and %T values", lhs_t, rhs_t); code_err(ast, "The 'and' operator isn't supported between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t), " values");
} }
case BINOP_CMP: { case BINOP_CMP: {
if (lhs_is_optional_num || rhs_is_optional_num) if (lhs_is_optional_num || rhs_is_optional_num)
@ -2620,14 +2620,14 @@ CORD compile(env_t *env, ast_t *ast)
else if (operand_t->tag == IntType || operand_t->tag == ByteType) else if (operand_t->tag == IntType || operand_t->tag == ByteType)
return CORD_all("(", lhs, " | ", rhs, ")"); return CORD_all("(", lhs, " | ", rhs, ")");
else else
code_err(ast, "The 'or' operator isn't supported between %T and %T values", lhs_t, rhs_t); code_err(ast, "The 'or' operator isn't supported between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t), " values");
} }
case BINOP_XOR: { case BINOP_XOR: {
// TODO: support optional values in `xor` expressions // TODO: support optional values in `xor` expressions
if (operand_t->tag == BoolType || operand_t->tag == IntType || operand_t->tag == ByteType) if (operand_t->tag == BoolType || operand_t->tag == IntType || operand_t->tag == ByteType)
return CORD_all("(", lhs, " ^ ", rhs, ")"); return CORD_all("(", lhs, " ^ ", rhs, ")");
else else
code_err(ast, "The 'xor' operator isn't supported between %T and %T values", lhs_t, rhs_t); code_err(ast, "The 'xor' operator isn't supported between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t), " values");
} }
case BINOP_CONCAT: { case BINOP_CONCAT: {
if (operand_t == PATH_TYPE) if (operand_t == PATH_TYPE)
@ -2640,7 +2640,7 @@ CORD compile(env_t *env, ast_t *ast)
return CORD_all("Array$concat(", lhs, ", ", rhs, ", sizeof(", compile_type(Match(operand_t, ArrayType)->item_type), "))"); return CORD_all("Array$concat(", lhs, ", ", rhs, ", sizeof(", compile_type(Match(operand_t, ArrayType)->item_type), "))");
} }
default: default:
code_err(ast, "Concatenation isn't supported between %T and %T values", lhs_t, rhs_t); code_err(ast, "Concatenation isn't supported between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t), " values");
} }
} }
default: break; default: break;
@ -2662,7 +2662,7 @@ CORD compile(env_t *env, ast_t *ast)
type_t *text_t = lang ? Table$str_get(*env->types, lang) : TEXT_TYPE; type_t *text_t = lang ? Table$str_get(*env->types, lang) : TEXT_TYPE;
if (!text_t || text_t->tag != TextType) if (!text_t || text_t->tag != TextType)
code_err(ast, "%s is not a valid text language name", lang); code_err(ast, quoted(lang), " is not a valid text language name");
CORD lang_constructor; CORD lang_constructor;
if (!lang || streq(lang, "Text")) if (!lang || streq(lang, "Text"))
@ -2699,7 +2699,7 @@ CORD compile(env_t *env, ast_t *ast)
else else
chunk_code = compile_string(env, chunk->ast, "no"); chunk_code = compile_string(env, chunk->ast, "no");
} else { } else {
code_err(chunk->ast, "I don't know how to convert %T to %T", chunk_t, text_t); code_err(chunk->ast, "I don't know how to convert ", type_to_str(chunk_t), " to ", type_to_str(text_t));
} }
} }
code = CORD_cat(code, chunk_code); code = CORD_cat(code, chunk_code);
@ -2773,9 +2773,6 @@ CORD compile(env_t *env, ast_t *ast)
case Array: { case Array: {
type_t *array_type = get_type(env, ast); type_t *array_type = get_type(env, ast);
type_t *item_type = Match(array_type, ArrayType)->item_type; type_t *item_type = Match(array_type, ArrayType)->item_type;
// if (type_size(Match(array_type, ArrayType)->item_type) > ARRAY_MAX_STRIDE)
// code_err(ast, "This array holds items that take up %ld bytes, but the maximum supported size is %ld bytes. Consider using an array of pointers instead.",
// type_size(item_type), ARRAY_MAX_STRIDE);
auto array = Match(ast, Array); auto array = Match(ast, Array);
if (!array->items) if (!array->items)
@ -2801,7 +2798,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); env_t *scope = item_type->tag == EnumType ? with_enum_scope(env, item_type) : fresh_scope(env);
static int64_t comp_num = 1; static int64_t comp_num = 1;
const char *comprehension_name = heap_strf("arr$%ld", comp_num++); const char *comprehension_name = String("arr$", comp_num++);
ast_t *comprehension_var = FakeAST(InlineCCode, .code=CORD_all("&", comprehension_name), ast_t *comprehension_var = FakeAST(InlineCCode, .code=CORD_all("&", comprehension_name),
.type=Type(PointerType, .pointed=array_type, .is_stack=true)); .type=Type(PointerType, .pointed=array_type, .is_stack=true));
Closure_t comp_action = {.fn=add_to_array_comprehension, .userdata=comprehension_var}; Closure_t comp_action = {.fn=add_to_array_comprehension, .userdata=comprehension_var};
@ -2832,7 +2829,7 @@ CORD compile(env_t *env, ast_t *ast)
type_t *value_t = Match(table_type, TableType)->value_type; type_t *value_t = Match(table_type, TableType)->value_type;
if (value_t->tag == OptionalType) if (value_t->tag == OptionalType)
code_err(ast, "Tables whose values are optional (%T) are not currently supported.", value_t); code_err(ast, "Tables whose values are optional (", type_to_str(value_t), ") are not currently supported.");
for (ast_list_t *entry = table->entries; entry; entry = entry->next) { for (ast_list_t *entry = table->entries; entry; entry = entry->next) {
if (entry->ast->tag == Comprehension) if (entry->ast->tag == Comprehension)
@ -2869,7 +2866,7 @@ CORD compile(env_t *env, ast_t *ast)
{ {
static int64_t comp_num = 1; static int64_t comp_num = 1;
env_t *scope = fresh_scope(env); env_t *scope = fresh_scope(env);
const char *comprehension_name = heap_strf("table$%ld", comp_num++); const char *comprehension_name = String("table$", comp_num++);
ast_t *comprehension_var = FakeAST(InlineCCode, .code=CORD_all("&", comprehension_name), ast_t *comprehension_var = FakeAST(InlineCCode, .code=CORD_all("&", comprehension_name),
.type=Type(PointerType, .pointed=table_type, .is_stack=true)); .type=Type(PointerType, .pointed=table_type, .is_stack=true));
@ -2923,7 +2920,7 @@ CORD compile(env_t *env, ast_t *ast)
{ {
static int64_t comp_num = 1; static int64_t comp_num = 1;
env_t *scope = item_type->tag == EnumType ? with_enum_scope(env, item_type) : fresh_scope(env); 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++); const char *comprehension_name = String("set$", comp_num++);
ast_t *comprehension_var = FakeAST(InlineCCode, .code=CORD_all("&", comprehension_name), ast_t *comprehension_var = FakeAST(InlineCCode, .code=CORD_all("&", comprehension_name),
.type=Type(PointerType, .pointed=set_type, .is_stack=true)); .type=Type(PointerType, .pointed=set_type, .is_stack=true));
CORD code = CORD_all("({ Table_t ", comprehension_name, " = {};"); CORD code = CORD_all("({ Table_t ", comprehension_name, " = {};");
@ -2975,8 +2972,8 @@ CORD compile(env_t *env, ast_t *ast)
if (can_promote(ret_t, declared)) if (can_promote(ret_t, declared))
ret_t = declared; ret_t = declared;
else else
code_err(ast, "This function was declared to return a value of type %T, but actually returns a value of type %T", code_err(ast, "This function was declared to return a value of type ", type_to_str(declared),
declared, ret_t); ", but actually returns a value of type ", type_to_str(ret_t));
} }
body_scope->fn_ret = ret_t; body_scope->fn_ret = ret_t;
@ -2987,8 +2984,8 @@ CORD compile(env_t *env, ast_t *ast)
for (int64_t i = 1; i <= Table$length(closed_vars); i++) { for (int64_t i = 1; i <= Table$length(closed_vars); i++) {
struct { const char *name; binding_t *b; } *entry = Table$entry(closed_vars, i); struct { const char *name; binding_t *b; } *entry = Table$entry(closed_vars, i);
if (has_stack_memory(entry->b->type)) if (has_stack_memory(entry->b->type))
code_err(ast, "This function is holding onto a reference to %T stack memory in the variable `%s`, but the function may outlive the stack memory", code_err(ast, "This function is holding onto a reference to ", type_to_str(entry->b->type),
entry->b->type, entry->name); " stack memory in the variable `", entry->name, "`, but the function may outlive the stack memory");
if (entry->b->type->tag == ModuleType) if (entry->b->type->tag == ModuleType)
continue; continue;
set_binding(body_scope, entry->name, entry->b->type, CORD_cat("userdata->", entry->name)); set_binding(body_scope, entry->name, entry->b->type, CORD_cat("userdata->", entry->name));
@ -3238,7 +3235,7 @@ CORD compile(env_t *env, ast_t *ast)
self = compile_to_pointer_depth(env, call->self, 0, false); self = compile_to_pointer_depth(env, call->self, 0, false);
(void)compile_arguments(env, ast, NULL, call->args); (void)compile_arguments(env, ast, NULL, call->args);
return CORD_all("Array$counts(", self, ", ", compile_type_info(self_value_t), ")"); return CORD_all("Array$counts(", self, ", ", compile_type_info(self_value_t), ")");
} else code_err(ast, "There is no '%s' method for arrays", call->name); } else code_err(ast, "There is no '", call->name, "' method for arrays");
} }
case SetType: { case SetType: {
auto set = Match(self_value_t, SetType); auto set = Match(self_value_t, SetType);
@ -3304,7 +3301,7 @@ CORD compile(env_t *env, ast_t *ast)
.next=new(arg_t, .name="strict", .type=Type(BoolType), .default_val=FakeAST(Bool, false))); .next=new(arg_t, .name="strict", .type=Type(BoolType), .default_val=FakeAST(Bool, false)));
return CORD_all("Table$is_superset_of(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), return CORD_all("Table$is_superset_of(", self, ", ", compile_arguments(env, ast, arg_spec, call->args),
", ", compile_type_info(self_value_t), ")"); ", ", compile_type_info(self_value_t), ")");
} else code_err(ast, "There is no '%s' method for tables", call->name); } else code_err(ast, "There is no '", call->name, "' method for tables");
} }
case TableType: { case TableType: {
auto table = Match(self_value_t, TableType); auto table = Match(self_value_t, TableType);
@ -3330,7 +3327,7 @@ CORD compile(env_t *env, ast_t *ast)
} else if (streq(call->name, "bump")) { } else if (streq(call->name, "bump")) {
EXPECT_POINTER("a", "table"); EXPECT_POINTER("a", "table");
if (!(table->value_type->tag == IntType || table->value_type->tag == NumType)) if (!(table->value_type->tag == IntType || table->value_type->tag == NumType))
code_err(ast, "bump() is only supported for tables with numeric value types, not %T", self_value_t); code_err(ast, "bump() is only supported for tables with numeric value types, not ", type_to_str(self_value_t));
ast_t *one = table->value_type->tag == IntType ast_t *one = table->value_type->tag == IntType
? FakeAST(Int, .str="1") ? FakeAST(Int, .str="1")
: FakeAST(Num, .n=1); : FakeAST(Num, .n=1);
@ -3351,7 +3348,7 @@ CORD compile(env_t *env, ast_t *ast)
self = compile_to_pointer_depth(env, call->self, 0, false); self = compile_to_pointer_depth(env, call->self, 0, false);
(void)compile_arguments(env, ast, NULL, call->args); (void)compile_arguments(env, ast, NULL, call->args);
return CORD_all("Table$sorted(", self, ", ", compile_type_info(self_value_t), ")"); return CORD_all("Table$sorted(", self, ", ", compile_type_info(self_value_t), ")");
} else code_err(ast, "There is no '%s' method for tables", call->name); } else code_err(ast, "There is no '", call->name, "' method for tables");
} }
default: { default: {
auto methodcall = Match(ast, MethodCall); auto methodcall = Match(ast, MethodCall);
@ -3414,7 +3411,7 @@ CORD compile(env_t *env, ast_t *ast)
compile_arguments(env, ast, struct_->fields, call->args), "})"); compile_arguments(env, ast, struct_->fields, call->args), "})");
} }
} }
code_err(ast, "I could not find a constructor matching these arguments for %T", t); code_err(ast, "I could not find a constructor matching these arguments for ", type_to_str(t));
} else if (fn_t->tag == ClosureType) { } else if (fn_t->tag == ClosureType) {
fn_t = Match(fn_t, ClosureType)->fn; fn_t = Match(fn_t, ClosureType)->fn;
arg_t *type_args = Match(fn_t, FunctionType)->args; arg_t *type_args = Match(fn_t, FunctionType)->args;
@ -3436,14 +3433,14 @@ CORD compile(env_t *env, ast_t *ast)
arg_code, "closure.userdata); })"); arg_code, "closure.userdata); })");
} }
} else { } else {
code_err(call->fn, "This is not a function, it's a %T", fn_t); code_err(call->fn, "This is not a function, it's a ", type_to_str(fn_t));
} }
} }
case Deserialize: { case Deserialize: {
ast_t *value = Match(ast, Deserialize)->value; ast_t *value = Match(ast, Deserialize)->value;
type_t *value_type = get_type(env, value); type_t *value_type = get_type(env, value);
if (!type_eq(value_type, Type(ArrayType, Type(ByteType)))) if (!type_eq(value_type, Type(ArrayType, Type(ByteType))))
code_err(value, "This value should be an array of bytes, not a %T", value_type); code_err(value, "This value should be an array of bytes, not a ", type_to_str(value_type));
type_t *t = parse_type_ast(env, Match(ast, Deserialize)->type); type_t *t = parse_type_ast(env, Match(ast, Deserialize)->type);
return CORD_all("({ ", compile_declaration(t, "deserialized"), ";\n" return CORD_all("({ ", compile_declaration(t, "deserialized"), ";\n"
"generic_deserialize(", compile(env, value), ", &deserialized, ", compile_type_info(t), ");\n" "generic_deserialize(", compile(env, value), ", &deserialized, ", compile_type_info(t), ");\n"
@ -3494,7 +3491,7 @@ CORD compile(env_t *env, ast_t *ast)
if (condition->tag == Declare) { if (condition->tag == Declare) {
type_t *condition_type = get_type(env, Match(condition, Declare)->value); type_t *condition_type = get_type(env, Match(condition, Declare)->value);
if (condition_type->tag != OptionalType) if (condition_type->tag != OptionalType)
code_err(condition, "This `if var := ...:` declaration should be an optional type, not %T", condition_type); code_err(condition, "This `if var := ...:` declaration should be an optional type, not ", type_to_str(condition_type));
decl_code = compile_statement(env, condition); decl_code = compile_statement(env, condition);
ast_t *var = Match(condition, Declare)->var; ast_t *var = Match(condition, Declare)->var;
@ -3538,10 +3535,10 @@ CORD compile(env_t *env, ast_t *ast)
type_t *iter_t = get_type(env, reduction->iter); type_t *iter_t = get_type(env, reduction->iter);
type_t *item_t = get_iterated_type(iter_t); type_t *item_t = get_iterated_type(iter_t);
if (!item_t) code_err(reduction->iter, "I couldn't figure out how to iterate over this type: %T", iter_t); if (!item_t) code_err(reduction->iter, "I couldn't figure out how to iterate over this type: ", type_to_str(iter_t));
static int64_t next_id = 1; static int64_t next_id = 1;
ast_t *item = FakeAST(Var, heap_strf("$it%ld", next_id++)); ast_t *item = FakeAST(Var, String("$it", next_id++));
ast_t *body = FakeAST(InlineCCode, .code="{}"); // placeholder ast_t *body = FakeAST(InlineCCode, .code="{}"); // placeholder
ast_t *loop = FakeAST(For, .vars=new(ast_list_t, .ast=item), .iter=reduction->iter, .body=body); ast_t *loop = FakeAST(For, .vars=new(ast_list_t, .ast=item), .iter=reduction->iter, .body=body);
env_t *body_scope = for_scope(env, loop); env_t *body_scope = for_scope(env, loop);
@ -3661,11 +3658,11 @@ CORD compile(env_t *env, ast_t *ast)
if (locals == info->env->locals) if (locals == info->env->locals)
goto is_inside_type; goto is_inside_type;
} }
code_err(ast, "Fields that start with underscores are not accessible on types outside of the type definition.", f->field); code_err(ast, "Fields that start with underscores are not accessible on types outside of the type definition.");
is_inside_type:; is_inside_type:;
} }
binding_t *b = get_binding(info->env, f->field); binding_t *b = get_binding(info->env, f->field);
if (!b) code_err(ast, "I couldn't find the field '%s' on this type", f->field); if (!b) code_err(ast, "I couldn't find the field '", f->field, "' on this type");
if (!b->code) code_err(ast, "I couldn't figure out how to compile this field"); if (!b->code) code_err(ast, "I couldn't figure out how to compile this field");
return b->code; return b->code;
} }
@ -3677,7 +3674,7 @@ CORD compile(env_t *env, ast_t *ast)
} else if (streq(f->field, "length")) { } else if (streq(f->field, "length")) {
return CORD_all("Int$from_int64((", compile_to_pointer_depth(env, f->fielded, 0, false), ").length)"); return CORD_all("Int$from_int64((", compile_to_pointer_depth(env, f->fielded, 0, false), ").length)");
} }
code_err(ast, "There is no '%s' field on %T values", f->field, value_t); code_err(ast, "There is no '", f->field, "' field on ", type_to_str(value_t), " values");
} }
case StructType: { case StructType: {
for (arg_t *field = Match(value_t, StructType)->fields; field; field = field->next) { for (arg_t *field = Match(value_t, StructType)->fields; field; field = field->next) {
@ -3691,7 +3688,7 @@ CORD compile(env_t *env, ast_t *ast)
} }
} }
} }
code_err(ast, "The field '%s' is not a valid field name of %T", f->field, value_t); code_err(ast, "The field '", f->field, "' is not a valid field name of ", type_to_str(value_t));
} }
case EnumType: { case EnumType: {
auto e = Match(value_t, EnumType); auto e = Match(value_t, EnumType);
@ -3707,19 +3704,19 @@ CORD compile(env_t *env, ast_t *ast)
} }
} }
} }
code_err(ast, "The field '%s' is not a valid tag name of %T", f->field, value_t); code_err(ast, "The field '", f->field, "' is not a valid tag name of ", type_to_str(value_t));
} }
case ArrayType: { case ArrayType: {
if (streq(f->field, "length")) if (streq(f->field, "length"))
return CORD_all("Int$from_int64((", compile_to_pointer_depth(env, f->fielded, 0, false), ").length)"); return CORD_all("Int$from_int64((", compile_to_pointer_depth(env, f->fielded, 0, false), ").length)");
code_err(ast, "There is no %s field on arrays", f->field); code_err(ast, "There is no ", f->field, " field on arrays");
} }
case SetType: { case SetType: {
if (streq(f->field, "items")) if (streq(f->field, "items"))
return CORD_all("ARRAY_COPY((", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries)"); return CORD_all("ARRAY_COPY((", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries)");
else if (streq(f->field, "length")) else if (streq(f->field, "length"))
return CORD_all("Int$from_int64((", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries.length)"); return CORD_all("Int$from_int64((", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries.length)");
code_err(ast, "There is no '%s' field on sets", f->field); code_err(ast, "There is no '", f->field, "' field on sets");
} }
case TableType: { case TableType: {
if (streq(f->field, "length")) { if (streq(f->field, "length")) {
@ -3737,7 +3734,7 @@ CORD compile(env_t *env, ast_t *ast)
} else if (streq(f->field, "fallback")) { } else if (streq(f->field, "fallback")) {
return CORD_all("({ Table_t *_fallback = (", compile_to_pointer_depth(env, f->fielded, 0, false), ").fallback; _fallback ? *_fallback : NONE_TABLE; })"); return CORD_all("({ Table_t *_fallback = (", compile_to_pointer_depth(env, f->fielded, 0, false), ").fallback; _fallback ? *_fallback : NONE_TABLE; })");
} }
code_err(ast, "There is no '%s' field on tables", f->field); code_err(ast, "There is no '", f->field, "' field on tables");
} }
case ModuleType: { case ModuleType: {
const char *name = Match(value_t, ModuleType)->name; const char *name = Match(value_t, ModuleType)->name;
@ -3750,10 +3747,10 @@ CORD compile(env_t *env, ast_t *ast)
} else if (streq(f->field, "microseconds")) { } else if (streq(f->field, "microseconds")) {
return CORD_all("I64((", compile_to_pointer_depth(env, f->fielded, 0, false), ").tv_usec)"); return CORD_all("I64((", compile_to_pointer_depth(env, f->fielded, 0, false), ").tv_usec)");
} }
code_err(ast, "There is no '%s' field on Moments", f->field); code_err(ast, "There is no '", f->field, "' field on Moments");
} }
default: default:
code_err(ast, "Field accesses are not supported on %T values", fielded_t); code_err(ast, "Field accesses are not supported on ", type_to_str(fielded_t), " values");
} }
} }
case Index: { case Index: {
@ -3776,7 +3773,7 @@ CORD compile(env_t *env, ast_t *ast)
type_t *index_t = get_type(env, indexing->index); type_t *index_t = get_type(env, indexing->index);
if (container_t->tag == ArrayType) { if (container_t->tag == ArrayType) {
if (index_t->tag != IntType && index_t->tag != BigIntType && index_t->tag != ByteType) if (index_t->tag != IntType && index_t->tag != BigIntType && index_t->tag != ByteType)
code_err(indexing->index, "Arrays can only be indexed by integers, not %T", index_t); code_err(indexing->index, "Arrays can only be indexed by integers, not ", type_to_str(index_t));
type_t *item_type = Match(container_t, ArrayType)->item_type; type_t *item_type = Match(container_t, ArrayType)->item_type;
CORD arr = compile_to_pointer_depth(env, indexing->indexed, 0, false); CORD arr = compile_to_pointer_depth(env, indexing->indexed, 0, false);
file_t *f = indexing->index->file; file_t *f = indexing->index->file;
@ -3819,7 +3816,7 @@ CORD compile(env_t *env, ast_t *ast)
} else if (container_t->tag == TextType) { } else if (container_t->tag == TextType) {
return CORD_all("Text$cluster(", compile_to_pointer_depth(env, indexing->indexed, 0, false), ", ", compile_to_type(env, indexing->index, Type(BigIntType)), ")"); return CORD_all("Text$cluster(", compile_to_pointer_depth(env, indexing->indexed, 0, false), ", ", compile_to_type(env, indexing->index, Type(BigIntType)), ")");
} else { } else {
code_err(ast, "Indexing is not supported for type: %T", container_t); code_err(ast, "Indexing is not supported for type: ", type_to_str(container_t));
} }
} }
case InlineCCode: { case InlineCCode: {
@ -3911,7 +3908,7 @@ CORD compile_type_info(type_t *t)
case MemoryType: return "&Memory$info"; case MemoryType: return "&Memory$info";
case VoidType: return "&Void$info"; case VoidType: return "&Void$info";
default: default:
compiler_err(NULL, 0, 0, "I couldn't convert to a type info: %T", t); compiler_err(NULL, 0, 0, "I couldn't convert to a type info: ", type_to_str(t));
} }
} }
@ -4050,7 +4047,7 @@ CORD compile_function(env_t *env, CORD name_code, ast_t *ast, CORD *staticdefs)
ret_t = convertdef->ret_type ? parse_type_ast(env, convertdef->ret_type) : Type(VoidType); ret_t = convertdef->ret_type ? parse_type_ast(env, convertdef->ret_type) : Type(VoidType);
function_name = get_type_name(ret_t); function_name = get_type_name(ret_t);
if (!function_name) if (!function_name)
code_err(ast, "Conversions are only supported for text, struct, and enum types, not %T", ret_t); code_err(ast, "Conversions are only supported for text, struct, and enum types, not ", type_to_str(ret_t));
body = convertdef->body; body = convertdef->body;
cache = convertdef->cache; cache = convertdef->cache;
is_inline = convertdef->is_inline; is_inline = convertdef->is_inline;
@ -4063,7 +4060,7 @@ CORD compile_function(env_t *env, CORD name_code, ast_t *ast, CORD *staticdefs)
arg_signature = CORD_cat(arg_signature, compile_declaration(arg_type, CORD_cat("_$", arg->name))); arg_signature = CORD_cat(arg_signature, compile_declaration(arg_type, CORD_cat("_$", arg->name)));
if (arg->next) arg_signature = CORD_cat(arg_signature, ", "); if (arg->next) arg_signature = CORD_cat(arg_signature, ", ");
if (Table$str_get(used_names, arg->name)) if (Table$str_get(used_names, arg->name))
code_err(ast, "The argument name '%s' is used more than once", arg->name); code_err(ast, "The argument name '", arg->name, "' is used more than once");
Table$str_set(&used_names, arg->name, arg->name); Table$str_set(&used_names, arg->name, arg->name);
} }
arg_signature = CORD_cat(arg_signature, ")"); arg_signature = CORD_cat(arg_signature, ")");
@ -4110,7 +4107,7 @@ CORD compile_function(env_t *env, CORD name_code, ast_t *ast, CORD *staticdefs)
code_err(ast, "This function will always abort before it reaches the end, but it's declared as having a Void return. It should be declared as an Abort return instead."); code_err(ast, "This function will always abort before it reaches the end, but it's declared as having a Void return. It should be declared as an Abort return instead.");
} else { } else {
if (body_type->tag != ReturnType && body_type->tag != AbortType) if (body_type->tag != ReturnType && body_type->tag != AbortType)
code_err(ast, "This function can reach the end without returning a %T value!", ret_t); code_err(ast, "This function can reach the end without returning a ", type_to_str(ret_t), " value!");
} }
CORD body_code = CORD_all("{\n", compile_inline_block(body_scope, body), "}\n"); CORD body_code = CORD_all("{\n", compile_inline_block(body_scope, body), "}\n");
@ -4158,7 +4155,7 @@ CORD compile_function(env_t *env, CORD name_code, ast_t *ast, CORD *staticdefs)
for (arg_ast_t *arg = args; arg; arg = arg->next) for (arg_ast_t *arg = args; arg; arg = arg->next)
fields = new(arg_t, .name=arg->name, .type=get_arg_ast_type(env, arg), .next=fields); fields = new(arg_t, .name=arg->name, .type=get_arg_ast_type(env, arg), .next=fields);
REVERSE_LIST(fields); REVERSE_LIST(fields);
type_t *t = Type(StructType, .name=heap_strf("func$%ld$args", get_line_number(ast->file, ast->start)), .fields=fields, .env=env); type_t *t = Type(StructType, .name=String("func$", get_line_number(ast->file, ast->start), "$args"), .fields=fields, .env=env);
int64_t num_fields = used_names.entries.length; int64_t num_fields = used_names.entries.length;
const char *metamethods = is_packed_data(t) ? "PackedData$metamethods" : "Struct$metamethods"; const char *metamethods = is_packed_data(t) ? "PackedData$metamethods" : "Struct$metamethods";
@ -4235,7 +4232,7 @@ CORD compile_top_level_code(env_t *env, ast_t *ast)
CORD full_name = CORD_all(namespace_prefix(env, env->namespace), decl_name); CORD full_name = CORD_all(namespace_prefix(env, env->namespace), decl_name);
type_t *t = get_type(env, decl->value); type_t *t = get_type(env, decl->value);
if (t->tag == AbortType || t->tag == VoidType || t->tag == ReturnType) if (t->tag == AbortType || t->tag == VoidType || t->tag == ReturnType)
code_err(ast, "You can't declare a variable with a %T value", t); code_err(ast, "You can't declare a variable with a ", type_to_str(t), " value");
CORD val_code = compile_maybe_incref(env, decl->value, t); CORD val_code = compile_maybe_incref(env, decl->value, t);
if (t->tag == FunctionType) { if (t->tag == FunctionType) {
@ -4267,7 +4264,7 @@ CORD compile_top_level_code(env_t *env, ast_t *ast)
type_t *type = get_function_def_type(env, ast); type_t *type = get_function_def_type(env, ast);
const char *name = get_type_name(Match(type, FunctionType)->ret); const char *name = get_type_name(Match(type, FunctionType)->ret);
if (!name) if (!name)
code_err(ast, "Conversions are only supported for text, struct, and enum types, not %T", Match(type, FunctionType)->ret); code_err(ast, "Conversions are only supported for text, struct, and enum types, not ", type_to_str(Match(type, FunctionType)->ret));
CORD name_code = CORD_asprintf("%r%s$%ld", namespace_prefix(env, env->namespace), name, get_line_number(ast->file, ast->start)); CORD name_code = CORD_asprintf("%r%s$%ld", namespace_prefix(env, env->namespace), name, get_line_number(ast->file, ast->start));
return compile_function(env, name_code, ast, &env->code->staticdefs); return compile_function(env, name_code, ast, &env->code->staticdefs);
} }
@ -4320,7 +4317,7 @@ static void initialize_vars_and_statics(env_t *env, ast_t *ast)
CORD full_name = CORD_all(namespace_prefix(env, env->namespace), decl_name); CORD full_name = CORD_all(namespace_prefix(env, env->namespace), decl_name);
type_t *t = get_type(env, decl->value); type_t *t = get_type(env, decl->value);
if (t->tag == AbortType || t->tag == VoidType || t->tag == ReturnType) if (t->tag == AbortType || t->tag == VoidType || t->tag == ReturnType)
code_err(stmt->ast, "You can't declare a variable with a %T value", t); code_err(stmt->ast, "You can't declare a variable with a ", type_to_str(t), " value");
CORD val_code = compile_maybe_incref(env, decl->value, t); CORD val_code = compile_maybe_incref(env, decl->value, t);
if (t->tag == FunctionType) { if (t->tag == FunctionType) {
@ -4489,7 +4486,7 @@ CORD compile_statement_namespace_header(env_t *env, ast_t *ast)
t = Type(ClosureType, t); t = Type(ClosureType, t);
assert(t->tag != ModuleType); assert(t->tag != ModuleType);
if (t->tag == AbortType || t->tag == VoidType || t->tag == ReturnType) if (t->tag == AbortType || t->tag == VoidType || t->tag == ReturnType)
code_err(ast, "You can't declare a variable with a %T value", t); code_err(ast, "You can't declare a variable with a ", type_to_str(t), " value");
return CORD_all( return CORD_all(
compile_statement_type_header(env, decl->value), compile_statement_type_header(env, decl->value),
@ -4532,7 +4529,7 @@ CORD compile_statement_namespace_header(env_t *env, ast_t *ast)
CORD ret_type_code = compile_type(ret_t); CORD ret_type_code = compile_type(ret_t);
CORD name = get_type_name(ret_t); CORD name = get_type_name(ret_t);
if (!name) if (!name)
code_err(ast, "Conversions are only supported for text, struct, and enum types, not %T", ret_t); code_err(ast, "Conversions are only supported for text, struct, and enum types, not ", type_to_str(ret_t));
name = CORD_all(namespace_prefix(env, env->namespace), name); name = CORD_all(namespace_prefix(env, env->namespace), name);
CORD name_code = CORD_asprintf("%r$%ld", name, get_line_number(ast->file, ast->start)); CORD name_code = CORD_asprintf("%r$%ld", name, get_line_number(ast->file, ast->start));
return CORD_all(ret_type_code, " ", name_code, arg_signature, ";\n"); return CORD_all(ret_type_code, " ", name_code, arg_signature, ";\n");

View File

@ -458,7 +458,7 @@ env_t *global_env(void)
for (int64_t j = 0; j < global_types[i].namespace.length; j++) { for (int64_t j = 0; j < global_types[i].namespace.length; j++) {
ns_entry_t *entry = global_types[i].namespace.data + j*global_types[i].namespace.stride; ns_entry_t *entry = global_types[i].namespace.data + j*global_types[i].namespace.stride;
type_t *type = parse_type_string(ns_env, entry->type_str); type_t *type = parse_type_string(ns_env, entry->type_str);
if (!type) compiler_err(NULL, NULL, NULL, "Couldn't parse type string: %s", entry->type_str); if (!type) compiler_err(NULL, NULL, NULL, "Couldn't parse type string: ", entry->type_str);
if (type->tag == ClosureType) type = Match(type, ClosureType)->fn; if (type->tag == ClosureType) type = Match(type, ClosureType)->fn;
set_binding(ns_env, entry->name, type, entry->code); set_binding(ns_env, entry->name, type, entry->code);
} }
@ -596,7 +596,7 @@ env_t *global_env(void)
for (size_t i = 0; i < sizeof(global_vars)/sizeof(global_vars[0]); i++) { for (size_t i = 0; i < sizeof(global_vars)/sizeof(global_vars[0]); i++) {
type_t *type = parse_type_string(env, global_vars[i].type_str); type_t *type = parse_type_string(env, global_vars[i].type_str);
if (!type) compiler_err(NULL, NULL, NULL, "Couldn't parse type string for %s: %s", global_vars[i].name, global_vars[i].type_str); if (!type) compiler_err(NULL, NULL, NULL, "Couldn't parse type string for ", global_vars[i].name, ": ", global_vars[i].type_str);
if (type->tag == ClosureType) type = Match(type, ClosureType)->fn; if (type->tag == ClosureType) type = Match(type, ClosureType)->fn;
Table$str_set(env->globals, global_vars[i].name, new(binding_t, .type=type, .code=global_vars[i].code)); Table$str_set(env->globals, global_vars[i].name, new(binding_t, .type=type, .code=global_vars[i].code));
} }
@ -736,8 +736,6 @@ env_t *for_scope(env_t *env, ast_t *ast)
} }
case FunctionType: case ClosureType: { case FunctionType: case ClosureType: {
auto fn = iter_t->tag == ClosureType ? Match(Match(iter_t, ClosureType)->fn, FunctionType) : Match(iter_t, FunctionType); auto fn = iter_t->tag == ClosureType ? Match(Match(iter_t, ClosureType)->fn, FunctionType) : Match(iter_t, FunctionType);
// if (fn->ret->tag != OptionalType)
// code_err(for_->iter, "Iterator functions must return an optional type, not %T", fn->ret);
if (for_->vars) { if (for_->vars) {
if (for_->vars->next) if (for_->vars->next)
@ -748,7 +746,7 @@ env_t *for_scope(env_t *env, ast_t *ast)
} }
return scope; return scope;
} }
default: code_err(for_->iter, "Iteration is not implemented for type: %T", iter_t); default: code_err(for_->iter, "Iteration is not implemented for type: ", type_to_str(iter_t));
} }
} }
@ -838,29 +836,4 @@ void set_binding(env_t *env, const char *name, type_t *type, CORD code)
Table$str_set(env->locals, name, new(binding_t, .type=type, .code=code)); Table$str_set(env->locals, name, new(binding_t, .type=type, .code=code));
} }
__attribute__((format(printf, 4, 5)))
_Noreturn void compiler_err(file_t *f, const char *start, const char *end, const char *fmt, ...)
{
if (USE_COLOR)
fputs("\x1b[31;7;1m", stderr);
if (f && start && end)
fprintf(stderr, "%s:%ld.%ld: ", f->relative_filename, get_line_number(f, start),
get_line_column(f, start));
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (USE_COLOR)
fputs(" \x1b[m", stderr);
fputs("\n\n", stderr);
if (f && start && end)
highlight_error(f, start, end, "\x1b[31;1m", 2, USE_COLOR);
if (getenv("TOMO_STACKTRACE"))
print_stack_trace(stderr, 1, 3);
raise(SIGABRT);
exit(1);
}
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0

View File

@ -5,6 +5,8 @@
#include <gc/cord.h> #include <gc/cord.h>
#include "types.h" #include "types.h"
#include "stdlib/print.h"
#include "stdlib/stdlib.h"
#include "stdlib/tables.h" #include "stdlib/tables.h"
typedef struct { typedef struct {
@ -64,8 +66,23 @@ env_t *fresh_scope(env_t *env);
env_t *for_scope(env_t *env, ast_t *ast); env_t *for_scope(env_t *env, ast_t *ast);
env_t *with_enum_scope(env_t *env, type_t *t); env_t *with_enum_scope(env_t *env, type_t *t);
env_t *namespace_env(env_t *env, const char *namespace_name); env_t *namespace_env(env_t *env, const char *namespace_name);
__attribute__((format(printf, 4, 5))) #define compiler_err(f, start, end, ...) ({ \
_Noreturn void compiler_err(file_t *f, const char *start, const char *end, const char *fmt, ...); file_t *_f = f; \
if (USE_COLOR) \
fputs("\x1b[31;7;1m", stderr); \
if (_f && start && end) \
fprint_inline(stderr, _f->relative_filename, ":", get_line_number(_f, start), ".", get_line_column(_f, start), ": "); \
fprint(stderr, __VA_ARGS__); \
if (USE_COLOR) \
fputs(" \x1b[m", stderr); \
fputs("\n\n", stderr); \
if (_f && start && end) \
highlight_error(_f, start, end, "\x1b[31;1m", 2, USE_COLOR); \
if (getenv("TOMO_STACKTRACE")) \
print_stack_trace(stderr, 1, 3); \
raise(SIGABRT); \
exit(1); \
})
binding_t *get_binding(env_t *env, const char *name); binding_t *get_binding(env_t *env, const char *name);
binding_t *get_constructor(env_t *env, type_t *t, arg_ast_t *args); binding_t *get_constructor(env_t *env, type_t *t, arg_ast_t *args);
void set_binding(env_t *env, const char *name, type_t *type, CORD code); void set_binding(env_t *env, const char *name, type_t *type, CORD code);

View File

@ -15,6 +15,8 @@
#include "cordhelpers.h" #include "cordhelpers.h"
#include "stdlib/integers.h" #include "stdlib/integers.h"
#include "stdlib/patterns.h" #include "stdlib/patterns.h"
#include "stdlib/print.h"
#include "stdlib/stdlib.h"
#include "stdlib/tables.h" #include "stdlib/tables.h"
#include "stdlib/text.h" #include "stdlib/text.h"
#include "stdlib/util.h" #include "stdlib/util.h"
@ -64,8 +66,6 @@ static INLINE size_t some_not(const char **pos, const char *forbid);
static INLINE size_t spaces(const char **pos); static INLINE size_t spaces(const char **pos);
static INLINE void whitespace(const char **pos); static INLINE void whitespace(const char **pos);
static INLINE size_t match(const char **pos, const char *target); static INLINE size_t match(const char **pos, const char *target);
static void expect_str(parse_ctx_t *ctx, const char *start, const char **pos, const char *target, const char *fmt, ...);
static void expect_closing(parse_ctx_t *ctx, const char **pos, const char *target, const char *fmt, ...);
static INLINE size_t match_word(const char **pos, const char *word); static INLINE size_t match_word(const char **pos, const char *word);
static INLINE const char* get_word(const char **pos); static INLINE const char* get_word(const char **pos);
static INLINE const char* get_id(const char **pos); static INLINE const char* get_id(const char **pos);
@ -147,40 +147,78 @@ static PARSER(parse_deserialize);
// //
// Print a parse error and exit (or use the on_err longjmp) // Print a parse error and exit (or use the on_err longjmp)
// //
__attribute__((noreturn, format(printf, 4, 0))) #define parser_err(ctx, start, end, ...) ({ \
static _Noreturn void vparser_err(parse_ctx_t *ctx, const char *start, const char *end, const char *fmt, va_list args) { if (USE_COLOR) \
if (USE_COLOR) fputs("\x1b[31;1;7m", stderr); \
fputs("\x1b[31;1;7m", stderr); fprint_inline(stderr, (ctx)->file->relative_filename, ":", get_line_number((ctx)->file, (start)), \
fprintf(stderr, "%s:%ld.%ld: ", ctx->file->relative_filename, get_line_number(ctx->file, start), ".", get_line_column((ctx)->file, (start)), ": ", __VA_ARGS__); \
get_line_column(ctx->file, start)); if (USE_COLOR) \
vfprintf(stderr, fmt, args); fputs(" \x1b[m", stderr); \
if (USE_COLOR) fputs("\n\n", stderr); \
fputs(" \x1b[m", stderr); highlight_error((ctx)->file, (start), (end), "\x1b[31;1;7m", 2, USE_COLOR); \
fputs("\n\n", stderr); fputs("\n", stderr); \
if (getenv("TOMO_STACKTRACE")) \
highlight_error(ctx->file, start, end, "\x1b[31;1;7m", 2, USE_COLOR); print_stack_trace(stderr, 1, 3); \
fputs("\n", stderr); if ((ctx)->on_err) \
longjmp(*((ctx)->on_err), 1); \
if (getenv("TOMO_STACKTRACE")) raise(SIGABRT); \
print_stack_trace(stderr, 1, 3); exit(1); \
})
if (ctx->on_err)
longjmp(*ctx->on_err, 1);
raise(SIGABRT);
exit(1);
}
// //
// Wrapper for vparser_err // Expect a string (potentially after whitespace) and emit a parser error if it's not there
// //
__attribute__((noreturn, format(printf, 4, 5))) #define expect_str(ctx, start, pos, target, ...) ({ \
static _Noreturn void parser_err(parse_ctx_t *ctx, const char *start, const char *end, const char *fmt, ...) { spaces(pos); \
va_list args; if (!match(pos, target)) { \
va_start(args, fmt); if (USE_COLOR) \
vparser_err(ctx, start, end, fmt, args); fputs("\x1b[31;1;7m", stderr); \
va_end(args); parser_err(ctx, start, *pos, __VA_ARGS__); \
} } \
char _lastchar = target[strlen(target)-1]; \
if (isalpha(_lastchar) || isdigit(_lastchar) || _lastchar == '_') { \
if (is_xid_continue_next(*pos)) { \
if (USE_COLOR) \
fputs("\x1b[31;1;7m", stderr); \
parser_err(ctx, start, *pos, __VA_ARGS__); \
} \
} \
})
//
// Helper for matching closing parens with good error messages
//
#define expect_closing(ctx, pos, close_str, ...) ({ \
const char *_start = *pos; \
spaces(pos); \
if (!match(pos, (close_str))) { \
const char *_eol = strchr(*pos, '\n'); \
const char *_next = strstr(*pos, (close_str)); \
const char *_end = _eol < _next ? _eol : _next; \
if (USE_COLOR) \
fputs("\x1b[31;1;7m", stderr); \
parser_err(ctx, _start, _end, __VA_ARGS__); \
} \
})
#define expect(ctx, start, pos, parser, ...) ({ \
const char **_pos = pos; \
spaces(_pos); \
auto _result = parser(ctx, *_pos); \
if (!_result) { \
if (USE_COLOR) \
fputs("\x1b[31;1;7m", stderr); \
parser_err(ctx, start, *_pos, __VA_ARGS__); \
} \
*_pos = _result->end; \
_result; })
#define optional(ctx, pos, parser) ({ \
const char **_pos = pos; \
spaces(_pos); \
auto _result = parser(ctx, *_pos); \
if (_result) *_pos = _result->end; \
_result; })
// //
// Convert an escape sequence like \n to a string // Convert an escape sequence like \n to a string
@ -213,7 +251,7 @@ static const char *unescape(parse_ctx_t *ctx, const char **out) {
uint32_t codepoint = unicode_name_character(name); uint32_t codepoint = unicode_name_character(name);
if (codepoint == UNINAME_INVALID) if (codepoint == UNINAME_INVALID)
parser_err(ctx, escape, escape + 3 + len, parser_err(ctx, escape, escape + 3 + len,
"Invalid unicode codepoint name: \"%s\"", name); "Invalid unicode codepoint name: ", quoted(name));
*endpos = escape + 3 + len; *endpos = escape + 3 + len;
char *str = GC_MALLOC_ATOMIC(16); char *str = GC_MALLOC_ATOMIC(16);
size_t u8_len = 16; size_t u8_len = 16;
@ -306,72 +344,6 @@ static INLINE bool is_xid_continue_next(const char *pos) {
return uc_is_property_xid_continue(point); return uc_is_property_xid_continue(point);
} }
//
// Expect a string (potentially after whitespace) and emit a parser error if it's not there
//
__attribute__((format(printf, 5, 6)))
static void expect_str(
parse_ctx_t *ctx, const char *start, const char **pos, const char *target, const char *fmt, ...) {
spaces(pos);
if (match(pos, target)) {
char lastchar = target[strlen(target)-1];
if (!(isalpha(lastchar) || isdigit(lastchar) || lastchar == '_'))
return;
if (!is_xid_continue_next(*pos))
return;
}
if (USE_COLOR)
fputs("\x1b[31;1;7m", stderr);
va_list args;
va_start(args, fmt);
vparser_err(ctx, start, *pos, fmt, args);
va_end(args);
}
//
// Helper for matching closing parens with good error messages
//
__attribute__((format(printf, 4, 5)))
static void expect_closing(
parse_ctx_t *ctx, const char **pos, const char *close_str, const char *fmt, ...) {
const char *start = *pos;
spaces(pos);
if (match(pos, close_str))
return;
const char *eol = strchr(*pos, '\n');
const char *next = strstr(*pos, close_str);
const char *end = eol < next ? eol : next;
if (USE_COLOR)
fputs("\x1b[31;1;7m", stderr);
va_list args;
va_start(args, fmt);
vparser_err(ctx, start, end, fmt, args);
va_end(args);
}
#define expect(ctx, start, pos, parser, ...) ({ \
const char **_pos = pos; \
spaces(_pos); \
auto _result = parser(ctx, *_pos); \
if (!_result) { \
if (USE_COLOR) \
fputs("\x1b[31;1;7m", stderr); \
parser_err(ctx, start, *_pos, __VA_ARGS__); \
} \
*_pos = _result->end; \
_result; })
#define optional(ctx, pos, parser) ({ \
const char **_pos = pos; \
spaces(_pos); \
auto _result = parser(ctx, *_pos); \
if (_result) *_pos = _result->end; \
_result; })
size_t match_word(const char **out, const char *word) { size_t match_word(const char **out, const char *word) {
const char *pos = *out; const char *pos = *out;
spaces(&pos); spaces(&pos);
@ -1445,7 +1417,8 @@ PARSER(parse_text) {
} }
REVERSE_LIST(chunks); REVERSE_LIST(chunks);
expect_closing(ctx, &pos, (char[]){close_quote, 0}, "I was expecting a '%c' to finish this string", close_quote); char close_str[2] = {close_quote, 0};
expect_closing(ctx, &pos, close_str, "I was expecting a ", close_quote, " to finish this string");
return NewAST(ctx->file, start, pos, TextJoin, .lang=lang, .children=chunks); return NewAST(ctx->file, start, pos, TextJoin, .lang=lang, .children=chunks);
} }
@ -2283,8 +2256,7 @@ arg_ast_t *parse_args(parse_ctx_t *ctx, const char **pos)
} }
if (!names) break; if (!names) break;
if (!default_val && !type) if (!default_val && !type)
parser_err(ctx, batch_start, *pos, "I expected a ':' and type, or '=' and a default value after this parameter (%s)", parser_err(ctx, batch_start, *pos, "I expected a ':' and type, or '=' and a default value after this parameter (", names->name, ")");
names->name);
REVERSE_LIST(names); REVERSE_LIST(names);
for (; names; names = names->next) for (; names; names = names->next)
@ -2512,12 +2484,12 @@ PARSER(parse_use) {
Array_t m = Text$matches(text, Pattern("{url}")); Array_t m = Text$matches(text, Pattern("{url}"));
if (m.length >= 0) { if (m.length >= 0) {
text = Text$trim(text, Pattern("http{0-1 s}://"), true, false); text = Text$trim(text, Pattern("http{0-1 s}://"), true, false);
FILE *shasum = popen(heap_strf("echo -n '%s' | sha256sum", Text$as_c_string(text)), "r"); FILE *shasum = popen(String("echo -n '", text, "' | sha256sum"), "r");
const size_t HASH_LEN = 32; const size_t HASH_LEN = 32;
char *hash = GC_MALLOC_ATOMIC(HASH_LEN + 1); char *hash = GC_MALLOC_ATOMIC(HASH_LEN + 1);
size_t just_read = fread(hash, sizeof(char), HASH_LEN, shasum); size_t just_read = fread(hash, sizeof(char), HASH_LEN, shasum);
if (just_read < HASH_LEN) if (just_read < HASH_LEN)
errx(1, "Failed to get SHA sum for 'use': %s", name); print_err("Failed to get SHA sum for 'use': ", name);
name = hash; name = hash;
} }
} }
@ -2528,7 +2500,7 @@ ast_t *parse_file(const char *path, jmp_buf *on_err) {
if (path[0] != '<') { if (path[0] != '<') {
const char *resolved = resolve_path(path, ".", "."); const char *resolved = resolve_path(path, ".", ".");
if (!resolved) if (!resolved)
errx(1, "Could not resolve path: %s", path); print_err("Could not resolve path: ", path);
path = resolved; path = resolved;
} }
// NOTE: this cache leaks a bounded amount of memory. The cache will never // NOTE: this cache leaks a bounded amount of memory. The cache will never

View File

@ -10,6 +10,7 @@
#include "stdlib/tomo.h" #include "stdlib/tomo.h"
#include "stdlib/util.h" #include "stdlib/util.h"
#include "stdlib/print.h"
#include "typecheck.h" #include "typecheck.h"
#include "parse.h" #include "parse.h"
@ -43,7 +44,7 @@ void repl(void)
{ {
env_t *env = global_env(); env_t *env = global_env();
void *dl = dlopen("libtomo.so", RTLD_LAZY); void *dl = dlopen("libtomo.so", RTLD_LAZY);
if (!dl) errx(1, "I couldn't find libtomo.so in your library paths"); if (!dl) print_err("I couldn't find libtomo.so in your library paths");
size_t buf_size = 0; size_t buf_size = 0;
char *line = NULL; char *line = NULL;
@ -84,33 +85,33 @@ void repl(void)
printf("\n"); printf("\n");
} }
__attribute__((noreturn, format(printf, 2, 3))) // __attribute__((noreturn, format(printf, 2, 3)))
static void repl_err(ast_t *node, const char *fmt, ...) // static void repl_err(ast_t *node, const char *fmt, ...)
{ // {
bool color = isatty(STDERR_FILENO) && !getenv("NO_COLOR"); // bool color = isatty(STDERR_FILENO) && !getenv("NO_COLOR");
if (color) // if (color)
fputs("\x1b[31;7;1m", stderr); // fputs("\x1b[31;7;1m", stderr);
if (node) // if (node)
fprintf(stderr, "%s:%ld.%ld: ", node->file->relative_filename, get_line_number(node->file, node->start), // fprintf(stderr, "%s:%ld.%ld: ", node->file->relative_filename, get_line_number(node->file, node->start),
get_line_column(node->file, node->start)); // get_line_column(node->file, node->start));
va_list args; // va_list args;
va_start(args, fmt); // va_start(args, fmt);
vfprintf(stderr, fmt, args); // vfprintf(stderr, fmt, args);
va_end(args); // va_end(args);
if (color) // if (color)
fputs(" \x1b[m", stderr); // fputs(" \x1b[m", stderr);
fputs("\n\n", stderr); // fputs("\n\n", stderr);
if (node) // if (node)
highlight_error(node->file, node->start, node->end, "\x1b[31;1m", 2, color); // highlight_error(node->file, node->start, node->end, "\x1b[31;1m", 2, color);
longjmp(on_err, 1); // longjmp(on_err, 1);
} // }
const TypeInfo_t *type_to_type_info(type_t *t) const TypeInfo_t *type_to_type_info(type_t *t)
{ {
switch (t->tag) { switch (t->tag) {
case AbortType: return &Abort$info; case AbortType: return &Abort$info;
case ReturnType: errx(1, "Shouldn't be getting a typeinfo for ReturnType"); case ReturnType: print_err("Shouldn't be getting a typeinfo for ReturnType");
case VoidType: return &Void$info; case VoidType: return &Void$info;
case MemoryType: return &Memory$info; case MemoryType: return &Memory$info;
case BoolType: return &Bool$info; case BoolType: return &Bool$info;
@ -121,13 +122,13 @@ const TypeInfo_t *type_to_type_info(type_t *t)
case TYPE_IBITS32: return &Int32$info; case TYPE_IBITS32: return &Int32$info;
case TYPE_IBITS16: return &Int16$info; case TYPE_IBITS16: return &Int16$info;
case TYPE_IBITS8: return &Int8$info; case TYPE_IBITS8: return &Int8$info;
default: errx(1, "Invalid bits"); default: print_err("Invalid bits");
} }
case NumType: case NumType:
switch (Match(t, NumType)->bits) { switch (Match(t, NumType)->bits) {
case TYPE_NBITS64: return &Num$info; case TYPE_NBITS64: return &Num$info;
case TYPE_NBITS32: return &Num32$info; case TYPE_NBITS32: return &Num32$info;
default: errx(1, "Invalid bits"); default: print_err("Invalid bits");
} }
case TextType: return &Text$info; case TextType: return &Text$info;
case ArrayType: { case ArrayType: {
@ -148,7 +149,7 @@ const TypeInfo_t *type_to_type_info(type_t *t)
const TypeInfo_t pointer_info = *Pointer$info(sigil, pointed_info); const TypeInfo_t pointer_info = *Pointer$info(sigil, pointed_info);
return memcpy(GC_MALLOC(sizeof(TypeInfo_t)), &pointer_info, sizeof(TypeInfo_t)); return memcpy(GC_MALLOC(sizeof(TypeInfo_t)), &pointer_info, sizeof(TypeInfo_t));
} }
default: errx(1, "Unsupported type: %T", t); default: print_err("Unsupported type: ", type_to_str(t));
} }
} }
@ -157,10 +158,10 @@ static PUREFUNC void *get_address(env_t *env, ast_t *ast)
switch (ast->tag) { switch (ast->tag) {
case Var: { case Var: {
repl_binding_t *b = get_repl_binding(env, Match(ast, Var)->name); repl_binding_t *b = get_repl_binding(env, Match(ast, Var)->name);
if (!b) repl_err(ast, "No such variable"); if (!b) print_err("No such variable");
return b->value; return b->value;
} }
default: errx(1, "Address not implemented for %W", ast); default: print_err("Address not implemented for ", ast_to_str(ast));
} }
} }
@ -181,10 +182,10 @@ static Int_t ast_to_int(env_t *env, ast_t *ast)
case TYPE_IBITS32: return Int$from_int32(num.i32); case TYPE_IBITS32: return Int$from_int32(num.i32);
case TYPE_IBITS16: return Int$from_int16(num.i16); case TYPE_IBITS16: return Int$from_int16(num.i16);
case TYPE_IBITS8: return Int$from_int8(num.i8); case TYPE_IBITS8: return Int$from_int8(num.i8);
default: errx(1, "Invalid int bits"); default: print_err("Invalid int bits");
} }
} }
default: repl_err(NULL, "Cannot convert to integer"); default: print_err("Cannot convert to integer");
} }
} }
@ -202,7 +203,7 @@ static double ast_to_num(env_t *env, ast_t *ast)
case TYPE_IBITS32: return Num$from_int32(num.i32); case TYPE_IBITS32: return Num$from_int32(num.i32);
case TYPE_IBITS16: return Num$from_int16(num.i16); case TYPE_IBITS16: return Num$from_int16(num.i16);
case TYPE_IBITS8: return Num$from_int8(num.i8); case TYPE_IBITS8: return Num$from_int8(num.i8);
default: errx(1, "Invalid int bits"); default: print_err("Invalid int bits");
} }
} }
case NumType: { case NumType: {
@ -210,7 +211,7 @@ static double ast_to_num(env_t *env, ast_t *ast)
eval(env, ast, &num); eval(env, ast, &num);
return Match(t, NumType)->bits == TYPE_NBITS32 ? (double)num.n32 : (double)num.n64; return Match(t, NumType)->bits == TYPE_NBITS32 ? (double)num.n32 : (double)num.n64;
} }
default: repl_err(NULL, "Cannot convert to number"); default: print_err("Cannot convert to number");
} }
} }
@ -241,14 +242,14 @@ void run(env_t *env, ast_t *ast)
type_t *t_target = get_type(env, target->ast); type_t *t_target = get_type(env, target->ast);
type_t *t_val = get_type(env, val->ast); type_t *t_val = get_type(env, val->ast);
if (!type_eq(t_val, t_target)) if (!type_eq(t_val, t_target))
repl_err(target->ast, "This value has type %T but I expected a %T", t_val, t_target); print_err("This value has type ", type_to_str(t_val), " but I expected a ", type_to_str(t_target));
if (!can_be_mutated(env, target->ast)) { if (!can_be_mutated(env, target->ast)) {
if (target->ast->tag == Index || target->ast->tag == FieldAccess) { if (target->ast->tag == Index || target->ast->tag == FieldAccess) {
ast_t *subject = target->ast->tag == Index ? Match(target->ast, Index)->indexed : Match(target->ast, FieldAccess)->fielded; // ast_t *subject = target->ast->tag == Index ? Match(target->ast, Index)->indexed : Match(target->ast, FieldAccess)->fielded;
repl_err(subject, "This is an immutable value, you can't assign to it"); print_err("This is an immutable value, you can't assign to it");
} else { } else {
repl_err(target->ast, "This is a value of type %T and can't be assigned to", get_type(env, target->ast)); print_err("This is a value of type ", type_to_str(get_type(env, target->ast)), " and can't be assigned to");
} }
} }
} }
@ -264,7 +265,7 @@ void run(env_t *env, ast_t *ast)
// type_t *obj_t = get_type(env, index->indexed); // type_t *obj_t = get_type(env, index->indexed);
// TypeInfo_t *table_info = type_to_type_info(t); // TypeInfo_t *table_info = type_to_type_info(t);
// } // }
default: errx(1, "Assignment not implemented: %W", target->ast); default: print_err("Assignment not implemented: ", ast_to_str(target->ast));
} }
} }
break; break;
@ -279,7 +280,7 @@ void run(env_t *env, ast_t *ast)
void *value = GC_MALLOC(size); void *value = GC_MALLOC(size);
eval(env, doctest->expr, value); eval(env, doctest->expr, value);
Text_t text = obj_to_text(t, value, true); Text_t text = obj_to_text(t, value, true);
printf("= %k \x1b[2m: %T\x1b[m\n", &text, t); print("= ", text, " \x1b[2m: ", type_to_str(t), "\x1b[m");
fflush(stdout); fflush(stdout);
} }
break; break;
@ -354,7 +355,7 @@ void eval(env_t *env, ast_t *ast, void *dest)
if (!dest) return; if (!dest) return;
repl_binding_t *b = get_repl_binding(env, Match(ast, Var)->name); repl_binding_t *b = get_repl_binding(env, Match(ast, Var)->name);
if (!b) if (!b)
repl_err(ast, "No such variable: %s", Match(ast, Var)->name); print_err("No such variable: ", Match(ast, Var)->name);
memcpy(dest, b->value, size); memcpy(dest, b->value, size);
break; break;
} }
@ -405,7 +406,7 @@ void eval(env_t *env, ast_t *ast, void *dest)
case 32: *(int32_t*)dest = Int32$from_int(result, false); return; \ case 32: *(int32_t*)dest = Int32$from_int(result, false); return; \
case 16: *(int16_t*)dest = Int16$from_int(result, false); return; \ case 16: *(int16_t*)dest = Int16$from_int(result, false); return; \
case 8: *(int8_t*)dest = Int8$from_int(result, false); return; \ case 8: *(int8_t*)dest = Int8$from_int(result, false); return; \
default: errx(1, "Invalid int bits"); \ default: print_err("Invalid int bits"); \
} \ } \
break; \ break; \
} }
@ -437,7 +438,7 @@ void eval(env_t *env, ast_t *ast, void *dest)
case BINOP_EQ: case BINOP_NE: case BINOP_LT: case BINOP_LE: case BINOP_GT: case BINOP_GE: { case BINOP_EQ: case BINOP_NE: case BINOP_LT: case BINOP_LE: case BINOP_GT: case BINOP_GE: {
type_t *t_lhs = get_type(env, binop->lhs); type_t *t_lhs = get_type(env, binop->lhs);
if (!type_eq(t_lhs, get_type(env, binop->rhs))) if (!type_eq(t_lhs, get_type(env, binop->rhs)))
repl_err(ast, "Comparisons between different types aren't supported"); print_err("Comparisons between different types aren't supported");
const TypeInfo_t *info = type_to_type_info(t_lhs); const TypeInfo_t *info = type_to_type_info(t_lhs);
size_t value_size = type_size(t_lhs); size_t value_size = type_size(t_lhs);
char lhs[value_size], rhs[value_size]; char lhs[value_size], rhs[value_size];
@ -455,7 +456,7 @@ void eval(env_t *env, ast_t *ast, void *dest)
} }
break; break;
} }
default: errx(1, "Binary op not implemented for %T: %W", t, ast); default: print_err(1, "Binary op not implemented for ", type_to_str(t), ": ", ast_to_str(ast));
} }
break; break;
} }
@ -471,8 +472,8 @@ void eval(env_t *env, ast_t *ast, void *dest)
int64_t index_int = raw_index; int64_t index_int = raw_index;
if (index_int < 1) index_int = arr.length + index_int + 1; if (index_int < 1) index_int = arr.length + index_int + 1;
if (index_int < 1 || index_int > arr.length) if (index_int < 1 || index_int > arr.length)
repl_err(index->index, "%ld is an invalid index for an array with length %ld", print_err(raw_index,
raw_index, arr.length); " is an invalid index for an array with length ", (int64_t)arr.length);
size_t item_size = type_size(Match(indexed_t, ArrayType)->item_type); size_t item_size = type_size(Match(indexed_t, ArrayType)->item_type);
memcpy(dest, arr.data + arr.stride*(index_int-1), item_size); memcpy(dest, arr.data + arr.stride*(index_int-1), item_size);
break; break;
@ -496,7 +497,7 @@ void eval(env_t *env, ast_t *ast, void *dest)
memcpy(dest, pointer, pointed_size); memcpy(dest, pointer, pointed_size);
break; break;
} }
default: errx(1, "Indexing is not supported for %T", indexed_t); default: print_err("Indexing is not supported for ", type_to_str(indexed_t));
} }
break; break;
} }
@ -544,7 +545,7 @@ void eval(env_t *env, ast_t *ast, void *dest)
break; break;
} }
default: default:
errx(1, "Eval not implemented for %W", ast); print_err("Eval not implemented for ", ast_to_str(ast));
} }
} }
#pragma GCC diagnostic pop #pragma GCC diagnostic pop

View File

@ -57,7 +57,7 @@ public void Array$insert(Array_t *arr, const void *item, Int_t int_index, int64_
if (index < 1) index = 1; if (index < 1) index = 1;
else if (index > (int64_t)arr->length + 1) else if (index > (int64_t)arr->length + 1)
fail("Invalid insertion index %ld for an array with length %ld", index, arr->length); fail("Invalid insertion index ", index, " for an array with length ", (int64_t)arr->length);
if (!arr->data) { if (!arr->data) {
arr->free = 4; arr->free = 4;
@ -105,7 +105,7 @@ public void Array$insert_all(Array_t *arr, Array_t to_insert, Int_t int_index, i
if (index < 1) index = 1; if (index < 1) index = 1;
else if (index > (int64_t)arr->length + 1) else if (index > (int64_t)arr->length + 1)
fail("Invalid insertion index %ld for an array with length %ld", index, arr->length); fail("Invalid insertion index ", index, " for an array with length ", (int64_t)arr->length);
if ((int64_t)arr->free >= (int64_t)to_insert.length // Adequate free space if ((int64_t)arr->free >= (int64_t)to_insert.length // Adequate free space
&& arr->data_refcount == 0 // Not aliased memory && arr->data_refcount == 0 // Not aliased memory
@ -322,7 +322,7 @@ public Array_t Array$sample(Array_t arr, Int_t int_n, Array_t weights, RNG_t rng
} }
if (weights.length != arr.length) if (weights.length != arr.length)
fail("Array has %ld elements, but there are %ld weights given", arr.length, weights.length); fail("Array has ", (int64_t)arr.length, " elements, but there are ", (int64_t)weights.length, " weights given");
double total = 0.0; double total = 0.0;
for (int64_t i = 0; i < weights.length && i < arr.length; i++) { for (int64_t i = 0; i < weights.length && i < arr.length; i++) {

View File

@ -14,7 +14,7 @@
const Array_t arr = arr_expr; int64_t index = index_expr; \ const Array_t arr = arr_expr; int64_t index = index_expr; \
int64_t off = index + (index < 0) * (arr.length + 1) - 1; \ int64_t off = index + (index < 0) * (arr.length + 1) - 1; \
if (unlikely(off < 0 || off >= arr.length)) \ if (unlikely(off < 0 || off >= arr.length)) \
fail_source(__SOURCE_FILE__, start, end, "Invalid array index: %s (array has length %ld)\n", Text$as_c_string(Int64$as_text(&index, no, NULL)), arr.length); \ fail_source(__SOURCE_FILE__, start, end, "Invalid array index: ", index, " (array has length ", (int64_t)arr.length, ")\n"); \
(item_type*)(arr.data + arr.stride * off);}) (item_type*)(arr.data + arr.stride * off);})
#define Array_get_unchecked(type, x, i) *({ const Array_t arr = x; int64_t index = i; \ #define Array_get_unchecked(type, x, i) *({ const Array_t arr = x; int64_t index = i; \
int64_t off = index + (index < 0) * (arr.length + 1) - 1; \ int64_t off = index + (index < 0) * (arr.length + 1) - 1; \
@ -23,7 +23,7 @@
Array_t *arr = arr_expr; int64_t index = index_expr; \ Array_t *arr = arr_expr; int64_t index = index_expr; \
int64_t off = index + (index < 0) * (arr->length + 1) - 1; \ int64_t off = index + (index < 0) * (arr->length + 1) - 1; \
if (unlikely(off < 0 || off >= arr->length)) \ if (unlikely(off < 0 || off >= arr->length)) \
fail_source(__SOURCE_FILE__, start, end, "Invalid array index: %s (array has length %ld)\n", Text$as_c_string(Int64$as_text(&index, no, NULL)), arr->length); \ fail_source(__SOURCE_FILE__, start, end, "Invalid array index: ", index, " (array has length ", (int64_t)arr->length, ")\n"); \
if (arr->data_refcount > 0) \ if (arr->data_refcount > 0) \
Array$compact(arr, sizeof(item_type)); \ Array$compact(arr, sizeof(item_type)); \
(item_type*)(arr->data + arr->stride * off); }) (item_type*)(arr->data + arr->stride * off); })

View File

@ -32,24 +32,24 @@ public Text_t Byte$hex(Byte_t byte, bool uppercase, bool prefix) {
public PUREFUNC Byte_t Byte$from_int(Int_t i, bool truncate) { public PUREFUNC Byte_t Byte$from_int(Int_t i, bool truncate) {
if unlikely (truncate && Int$compare_value(i, I_small(0xFF)) > 0) if unlikely (truncate && Int$compare_value(i, I_small(0xFF)) > 0)
fail("This value is too large to convert to a byte without truncation: %k", (Text_t[1]){Int$value_as_text(i)}); fail("This value is too large to convert to a byte without truncation: ", i);
else if unlikely (truncate && Int$compare_value(i, I_small(0)) < 0) else if unlikely (truncate && Int$compare_value(i, I_small(0)) < 0)
fail("Negative values can't be converted to bytes: %k", (Text_t[1]){Int$value_as_text(i)}); fail("Negative values can't be converted to bytes: ", i);
return (i.small != 0); return (i.small != 0);
} }
public PUREFUNC Byte_t Byte$from_int64(Int64_t i, bool truncate) { public PUREFUNC Byte_t Byte$from_int64(Int64_t i, bool truncate) {
if unlikely (truncate && i != (Int64_t)(Byte_t)i) if unlikely (truncate && i != (Int64_t)(Byte_t)i)
fail("This value can't be converted to a byte without truncation: %ld", i); fail("This value can't be converted to a byte without truncation: ", i);
return (Byte_t)i; return (Byte_t)i;
} }
public PUREFUNC Byte_t Byte$from_int32(Int32_t i, bool truncate) { public PUREFUNC Byte_t Byte$from_int32(Int32_t i, bool truncate) {
if unlikely (truncate && i != (Int32_t)(Byte_t)i) if unlikely (truncate && i != (Int32_t)(Byte_t)i)
fail("This value can't be converted to a byte without truncation: %d", i); fail("This value can't be converted to a byte without truncation: ", i);
return (Byte_t)i; return (Byte_t)i;
} }
public PUREFUNC Byte_t Byte$from_int16(Int16_t i, bool truncate) { public PUREFUNC Byte_t Byte$from_int16(Int16_t i, bool truncate) {
if unlikely (truncate && i != (Int16_t)(Byte_t)i) if unlikely (truncate && i != (Int16_t)(Byte_t)i)
fail("This value can't be converted to a byte without truncation: %d", i); fail("This value can't be converted to a byte without truncation: ", i);
return (Byte_t)i; return (Byte_t)i;
} }

View File

@ -34,6 +34,8 @@ typedef union {
mpz_t *big; mpz_t *big;
} Int_t; } Int_t;
#define OptionalInt_t Int_t
typedef struct { typedef struct {
void *data; void *data;
// All of the following fields add up to 64 bits, which means that array // All of the following fields add up to 64 bits, which means that array
@ -117,4 +119,10 @@ typedef struct MutexedData_s {
void *data; void *data;
} *MutexedData_t; } *MutexedData_t;
#define OptionalBool_t uint8_t
#define OptionalArray_t Array_t
#define OptionalTable_t Table_t
#define OptionalText_t Text_t
#define OptionalClosure_t Closure_t
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0

View File

@ -82,7 +82,7 @@ public Text_t Func$as_text(const void *fn, bool colorize, const TypeInfo_t *type
OptionalText_t filename = get_function_filename(*(void**)fn); OptionalText_t filename = get_function_filename(*(void**)fn);
int64_t line_num = get_function_line_num(*(void**)fn); int64_t line_num = get_function_line_num(*(void**)fn);
if (filename.length >= 0) if (filename.length >= 0)
text = Text$format("%k [%k:%ld]", &text, &filename, line_num); text = Texts(text, Text(" ["), filename, Text$format(":%ld]", line_num));
} }
if (fn && colorize) if (fn && colorize)
text = Text$concat(Text("\x1b[32;1m"), text, Text("\x1b[m")); text = Text$concat(Text("\x1b[32;1m"), text, Text("\x1b[m"));

View File

@ -6,6 +6,7 @@
#include <gmp.h> #include <gmp.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include "arrays.h" #include "arrays.h"
@ -16,9 +17,18 @@
#include "text.h" #include "text.h"
#include "types.h" #include "types.h"
public int Int$print(FILE *f, Int_t i) {
if (likely(i.small & 1L)) {
return fprintf(f, "%ld", (i.small)>>2L);
} else {
char *str = mpz_get_str(NULL, 10, *i.big);
return fputs(str, f);
}
}
public Text_t Int$value_as_text(Int_t i) { public Text_t Int$value_as_text(Int_t i) {
if (likely(i.small & 1L)) { if (likely(i.small & 1L)) {
return Text$format("%ld", (i.small)>>2L); return Text$format("%ld", i.small>>2L);
} else { } else {
char *str = mpz_get_str(NULL, 10, *i.big); char *str = mpz_get_str(NULL, 10, *i.big);
return Text$from_str(str); return Text$from_str(str);
@ -416,7 +426,7 @@ public Int_t Int$prev_prime(Int_t x)
mpz_t p; mpz_t p;
mpz_init_set_int(p, x); mpz_init_set_int(p, x);
if (unlikely(mpz_prevprime(p, p) == 0)) if (unlikely(mpz_prevprime(p, p) == 0))
fail("There is no prime number before %k", (Text_t[1]){Int$as_text(&x, false, &Int$info)}); fail("There is no prime number before ", x);
return Int$from_mpz(p); return Int$from_mpz(p);
} }
#endif #endif

View File

@ -7,6 +7,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <gmp.h> #include <gmp.h>
#include "print.h"
#include "datatypes.h" #include "datatypes.h"
#include "stdlib.h" #include "stdlib.h"
#include "types.h" #include "types.h"
@ -87,8 +88,7 @@ void Int64$deserialize(FILE *in, void *outval, Array_t*, const TypeInfo_t*);
void Int32$serialize(const void *obj, FILE *out, Table_t*, const TypeInfo_t*); void Int32$serialize(const void *obj, FILE *out, Table_t*, const TypeInfo_t*);
void Int32$deserialize(FILE *in, void *outval, Array_t*, const TypeInfo_t*); void Int32$deserialize(FILE *in, void *outval, Array_t*, const TypeInfo_t*);
#define OptionalInt_t Int_t int Int$print(FILE *f, Int_t i);
Text_t Int$as_text(const void *i, bool colorize, const TypeInfo_t *type); Text_t Int$as_text(const void *i, bool colorize, const TypeInfo_t *type);
Text_t Int$value_as_text(Int_t i); Text_t Int$value_as_text(Int_t i);
PUREFUNC uint64_t Int$hash(const void *x, const TypeInfo_t *type); PUREFUNC uint64_t Int$hash(const void *x, const TypeInfo_t *type);
@ -283,7 +283,7 @@ MACROLIKE PUREFUNC Int_t Int$from_num(double n, bool truncate) {
mpz_t result; mpz_t result;
mpz_init_set_d(result, n); mpz_init_set_d(result, n);
if (!truncate && unlikely(mpz_get_d(result) != n)) if (!truncate && unlikely(mpz_get_d(result) != n))
fail("Could not convert to an integer without truncation: %g", n); fail("Could not convert to an integer without truncation: ", n);
return Int$from_mpz(result); return Int$from_mpz(result);
} }
MACROLIKE PUREFUNC Int_t Int$from_num32(float n, bool truncate) { return Int$from_num((double)n, truncate); } MACROLIKE PUREFUNC Int_t Int$from_num32(float n, bool truncate) { return Int$from_num((double)n, truncate); }
@ -304,20 +304,20 @@ MACROLIKE CONSTFUNC Int_t Int$from_bool(Bool_t b) { return I_small(b); }
MACROLIKE PUREFUNC Int64_t Int64$from_num(Num_t n, bool truncate) { MACROLIKE PUREFUNC Int64_t Int64$from_num(Num_t n, bool truncate) {
int64_t i64 = (int64_t)n; int64_t i64 = (int64_t)n;
if (!truncate && unlikely((Num_t)i64 != n)) if (!truncate && unlikely((Num_t)i64 != n))
fail("Could not convert Num to Int64 without truncation: %g\n", n); fail("Could not convert Num to Int64 without truncation: ", n);
return i64; return i64;
} }
MACROLIKE PUREFUNC Int64_t Int64$from_num32(Num32_t n, bool truncate) { MACROLIKE PUREFUNC Int64_t Int64$from_num32(Num32_t n, bool truncate) {
int64_t i64 = (int64_t)n; int64_t i64 = (int64_t)n;
if (!truncate && unlikely((Num32_t)i64 != n)) if (!truncate && unlikely((Num32_t)i64 != n))
fail("Could not convert Num32 to Int64 without truncation: %g\n", (double)n); fail("Could not convert Num32 to Int64 without truncation: ", n);
return i64; return i64;
} }
MACROLIKE PUREFUNC Int64_t Int64$from_int(Int_t i, bool truncate) { MACROLIKE PUREFUNC Int64_t Int64$from_int(Int_t i, bool truncate) {
if likely (i.small & 1L) if likely (i.small & 1L)
return (int64_t)(i.small >> 2L); return (int64_t)(i.small >> 2L);
if (!truncate && unlikely(!mpz_fits_slong_p(*i.big))) if (!truncate && unlikely(!mpz_fits_slong_p(*i.big)))
fail("Integer is too big to fit in a 64-bit integer: %k", (Text_t[1]){Int$value_as_text(i)}); fail("Integer is too big to fit in a 64-bit integer: ", i);
return mpz_get_si(*i.big); return mpz_get_si(*i.big);
} }
MACROLIKE CONSTFUNC Int64_t Int64$from_int32(Int32_t i) { return (Int64_t)i; } MACROLIKE CONSTFUNC Int64_t Int64$from_int32(Int32_t i) { return (Int64_t)i; }
@ -328,26 +328,26 @@ MACROLIKE CONSTFUNC Int64_t Int64$from_int8(Int8_t i) { return (Int64_t)i; }
MACROLIKE PUREFUNC Int32_t Int32$from_num(Num_t n, bool truncate) { MACROLIKE PUREFUNC Int32_t Int32$from_num(Num_t n, bool truncate) {
int32_t i32 = (int32_t)n; int32_t i32 = (int32_t)n;
if (!truncate && unlikely((Num_t)i32 != n)) if (!truncate && unlikely((Num_t)i32 != n))
fail("Could not convert Num to Int32 without truncation: %g\n", n); fail("Could not convert Num to Int32 without truncation: ", n);
return i32; return i32;
} }
MACROLIKE PUREFUNC Int32_t Int32$from_num32(Num32_t n, bool truncate) { MACROLIKE PUREFUNC Int32_t Int32$from_num32(Num32_t n, bool truncate) {
int32_t i32 = (int32_t)n; int32_t i32 = (int32_t)n;
if (!truncate && unlikely((Num32_t)i32 != n)) if (!truncate && unlikely((Num32_t)i32 != n))
fail("Could not convert Num32 to Int32 without truncation: %g\n", (double)n); fail("Could not convert Num32 to Int32 without truncation: ", n);
return i32; return i32;
} }
MACROLIKE PUREFUNC Int32_t Int32$from_int(Int_t i, bool truncate) { MACROLIKE PUREFUNC Int32_t Int32$from_int(Int_t i, bool truncate) {
int64_t i64 = Int64$from_int(i, truncate); int64_t i64 = Int64$from_int(i, truncate);
int32_t i32 = (int32_t)i64; int32_t i32 = (int32_t)i64;
if (!truncate && unlikely((int64_t)i32 != i64)) if (!truncate && unlikely((int64_t)i32 != i64))
fail("Integer is too big to fit in a 32-bit integer: %k", (Text_t[1]){Int$value_as_text(i)}); fail("Integer is too big to fit in a 32-bit integer: ", i);
return i32; return i32;
} }
MACROLIKE PUREFUNC Int32_t Int32$from_int64(Int64_t i64, bool truncate) { MACROLIKE PUREFUNC Int32_t Int32$from_int64(Int64_t i64, bool truncate) {
int32_t i32 = (int32_t)i64; int32_t i32 = (int32_t)i64;
if (!truncate && unlikely((int64_t)i32 != i64)) if (!truncate && unlikely((int64_t)i32 != i64))
fail("Integer is too big to fit in a 32-bit integer: %ld", i64); fail("Integer is too big to fit in a 32-bit integer: ", i64);
return i32; return i32;
} }
MACROLIKE CONSTFUNC Int32_t Int32$from_int16(Int16_t i) { return (Int32_t)i; } MACROLIKE CONSTFUNC Int32_t Int32$from_int16(Int16_t i) { return (Int32_t)i; }
@ -357,13 +357,13 @@ MACROLIKE CONSTFUNC Int32_t Int32$from_int8(Int8_t i) { return (Int32_t)i; }
MACROLIKE PUREFUNC Int16_t Int16$from_num(Num_t n, bool truncate) { MACROLIKE PUREFUNC Int16_t Int16$from_num(Num_t n, bool truncate) {
int16_t i16 = (int16_t)n; int16_t i16 = (int16_t)n;
if (!truncate && unlikely((Num_t)i16 != n)) if (!truncate && unlikely((Num_t)i16 != n))
fail("Could not convert Num to Int16 without truncation: %g\n", n); fail("Could not convert Num to Int16 without truncation: ", n);
return i16; return i16;
} }
MACROLIKE PUREFUNC Int16_t Int16$from_num32(Num32_t n, bool truncate) { MACROLIKE PUREFUNC Int16_t Int16$from_num32(Num32_t n, bool truncate) {
int16_t i16 = (int16_t)n; int16_t i16 = (int16_t)n;
if (!truncate && unlikely((Num32_t)i16 != n)) if (!truncate && unlikely((Num32_t)i16 != n))
fail("Could not convert Num32 to Int16 without truncation: %g\n", (double)n); fail("Could not convert Num32 to Int16 without truncation: ", (double)n);
return i16; return i16;
} }
MACROLIKE PUREFUNC Int16_t Int16$from_int(Int_t i, bool truncate) { MACROLIKE PUREFUNC Int16_t Int16$from_int(Int_t i, bool truncate) {
@ -376,13 +376,13 @@ MACROLIKE PUREFUNC Int16_t Int16$from_int(Int_t i, bool truncate) {
MACROLIKE PUREFUNC Int16_t Int16$from_int64(Int64_t i64, bool truncate) { MACROLIKE PUREFUNC Int16_t Int16$from_int64(Int64_t i64, bool truncate) {
int16_t i16 = (int16_t)i64; int16_t i16 = (int16_t)i64;
if (!truncate && unlikely((int64_t)i16 != i64)) if (!truncate && unlikely((int64_t)i16 != i64))
fail("Integer is too big to fit in a 16-bit integer: %ld", i64); fail("Integer is too big to fit in a 16-bit integer: ", i64);
return i16; return i16;
} }
MACROLIKE PUREFUNC Int16_t Int16$from_int32(Int32_t i32, bool truncate) { MACROLIKE PUREFUNC Int16_t Int16$from_int32(Int32_t i32, bool truncate) {
int16_t i16 = (int16_t)i32; int16_t i16 = (int16_t)i32;
if (!truncate && unlikely((int32_t)i16 != i32)) if (!truncate && unlikely((int32_t)i16 != i32))
fail("Integer is too big to fit in a 16-bit integer: %ld", i32); fail("Integer is too big to fit in a 16-bit integer: ", i32);
return i16; return i16;
} }
MACROLIKE CONSTFUNC Int16_t Int16$from_int8(Int8_t i) { return (Int16_t)i; } MACROLIKE CONSTFUNC Int16_t Int16$from_int8(Int8_t i) { return (Int16_t)i; }
@ -391,13 +391,13 @@ MACROLIKE CONSTFUNC Int16_t Int16$from_int8(Int8_t i) { return (Int16_t)i; }
MACROLIKE PUREFUNC Int8_t Int8$from_num(Num_t n, bool truncate) { MACROLIKE PUREFUNC Int8_t Int8$from_num(Num_t n, bool truncate) {
int8_t i8 = (int8_t)n; int8_t i8 = (int8_t)n;
if (!truncate && unlikely((Num_t)i8 != n)) if (!truncate && unlikely((Num_t)i8 != n))
fail("Could not convert Num to Int8 without truncation: %g\n", n); fail("Could not convert Num to Int8 without truncation: ", n);
return i8; return i8;
} }
MACROLIKE PUREFUNC Int8_t Int8$from_num32(Num32_t n, bool truncate) { MACROLIKE PUREFUNC Int8_t Int8$from_num32(Num32_t n, bool truncate) {
int8_t i8 = (int8_t)n; int8_t i8 = (int8_t)n;
if (!truncate && unlikely((Num32_t)i8 != n)) if (!truncate && unlikely((Num32_t)i8 != n))
fail("Could not convert Num32 to Int8 without truncation: %g\n", (double)n); fail("Could not convert Num32 to Int8 without truncation: ", n);
return i8; return i8;
} }
MACROLIKE PUREFUNC Int8_t Int8$from_int(Int_t i, bool truncate) { MACROLIKE PUREFUNC Int8_t Int8$from_int(Int_t i, bool truncate) {
@ -410,19 +410,19 @@ MACROLIKE PUREFUNC Int8_t Int8$from_int(Int_t i, bool truncate) {
MACROLIKE PUREFUNC Int8_t Int8$from_int64(Int64_t i64, bool truncate) { MACROLIKE PUREFUNC Int8_t Int8$from_int64(Int64_t i64, bool truncate) {
int8_t i8 = (int8_t)i64; int8_t i8 = (int8_t)i64;
if (!truncate && unlikely((int64_t)i8 != i64)) if (!truncate && unlikely((int64_t)i8 != i64))
fail("Integer is too big to fit in a 8-bit integer: %ld", i64); fail("Integer is too big to fit in a 8-bit integer: ", i64);
return i8; return i8;
} }
MACROLIKE PUREFUNC Int8_t Int8$from_int32(Int32_t i32, bool truncate) { MACROLIKE PUREFUNC Int8_t Int8$from_int32(Int32_t i32, bool truncate) {
int8_t i8 = (int8_t)i32; int8_t i8 = (int8_t)i32;
if (!truncate && unlikely((int32_t)i8 != i32)) if (!truncate && unlikely((int32_t)i8 != i32))
fail("Integer is too big to fit in a 8-bit integer: %ld", i32); fail("Integer is too big to fit in a 8-bit integer: ", i32);
return i8; return i8;
} }
MACROLIKE PUREFUNC Int8_t Int8$from_int16(Int16_t i16, bool truncate) { MACROLIKE PUREFUNC Int8_t Int8$from_int16(Int16_t i16, bool truncate) {
int8_t i8 = (int8_t)i16; int8_t i8 = (int8_t)i16;
if (!truncate && unlikely((int16_t)i8 != i16)) if (!truncate && unlikely((int16_t)i8 != i16))
fail("Integer is too big to fit in a 8-bit integer: %ld", i16); fail("Integer is too big to fit in a 8-bit integer: ", i16);
return i8; return i8;
} }
#pragma GCC diagnostic pop #pragma GCC diagnostic pop

View File

@ -111,14 +111,14 @@ __attribute__((noreturn))
public void cannot_serialize(const void*, FILE*, Table_t*, const TypeInfo_t *type) public void cannot_serialize(const void*, FILE*, Table_t*, const TypeInfo_t *type)
{ {
Text_t typestr = generic_as_text(NULL, false, type); Text_t typestr = generic_as_text(NULL, false, type);
fail("Values of type %k cannot be serialized or deserialized!", &typestr); fail("Values of type ", typestr, " cannot be serialized or deserialized!");
} }
__attribute__((noreturn)) __attribute__((noreturn))
public void cannot_deserialize(FILE*, void*, Array_t*, const TypeInfo_t *type) public void cannot_deserialize(FILE*, void*, Array_t*, const TypeInfo_t *type)
{ {
Text_t typestr = generic_as_text(NULL, false, type); Text_t typestr = generic_as_text(NULL, false, type);
fail("Values of type %k cannot be serialized or deserialized!", &typestr); fail("Values of type ", typestr, " cannot be serialized or deserialized!");
} }
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0

View File

@ -222,7 +222,7 @@ public OptionalMoment_t Moment$parse(Text_t text, Text_t format)
const char *str = Text$as_c_string(text); const char *str = Text$as_c_string(text);
const char *fmt = Text$as_c_string(format); const char *fmt = Text$as_c_string(format);
if (strstr(fmt, "%Z")) if (strstr(fmt, "%Z"))
fail("The %%Z specifier is not supported for time parsing!"); fail("The %Z specifier is not supported for time parsing!");
char *invalid = strptime(str, fmt, &info); char *invalid = strptime(str, fmt, &info);
if (!invalid || invalid[0] != '\0') if (!invalid || invalid[0] != '\0')

View File

@ -20,7 +20,8 @@ static Text_t MutexedData$as_text(const void *m, bool colorize, const TypeInfo_t
if (!m) { if (!m) {
return Texts(colorize ? Text("\x1b[34;1mmutexed\x1b[m(") : Text("mutexed("), typename, Text(")")); return Texts(colorize ? Text("\x1b[34;1mmutexed\x1b[m(") : Text("mutexed("), typename, Text(")"));
} }
return Text$format(colorize ? "\x1b[34;1mmutexed %k<%p>\x1b[m" : "mutexed %k<%p>", &typename, *((MutexedData_t*)m)); return Texts(colorize ? Text("\x1b[34;1mmutexed ") : Text("mutexed "), typename,
Text$format(colorize ? "<%p>\x1b[m" : "<%p>", *((MutexedData_t*)m)));
} }
static bool MutexedData$is_none(const void *m, const TypeInfo_t *) static bool MutexedData$is_none(const void *m, const TypeInfo_t *)

View File

@ -42,7 +42,7 @@ MACROLIKE CONSTFUNC double Num$from_int(Int_t i, bool truncate) {
if likely (i.small & 0x1) { if likely (i.small & 0x1) {
double ret = (double)(i.small >> 2); double ret = (double)(i.small >> 2);
if unlikely (!truncate && (int64_t)ret != (i.small >> 2)) if unlikely (!truncate && (int64_t)ret != (i.small >> 2))
fail("Could not convert integer to 64-bit floating point without losing precision: %ld", i.small >> 2); fail("Could not convert integer to 64-bit floating point without losing precision: ", i.small >> 2);
return ret; return ret;
} else { } else {
double ret = mpz_get_d(*i.big); double ret = mpz_get_d(*i.big);
@ -50,7 +50,7 @@ MACROLIKE CONSTFUNC double Num$from_int(Int_t i, bool truncate) {
mpz_t roundtrip; mpz_t roundtrip;
mpz_init_set_d(roundtrip, ret); mpz_init_set_d(roundtrip, ret);
if unlikely (mpz_cmp(*i.big, roundtrip) != 0) if unlikely (mpz_cmp(*i.big, roundtrip) != 0)
fail("Could not convert integer to 64-bit floating point without losing precision: %k", (Text_t[1]){Int$value_as_text(i)}); fail("Could not convert integer to 64-bit floating point without losing precision: ", i);
} }
return ret; return ret;
} }
@ -59,7 +59,7 @@ MACROLIKE CONSTFUNC double Num$from_int(Int_t i, bool truncate) {
MACROLIKE CONSTFUNC double Num$from_int64(Int64_t i, bool truncate) { MACROLIKE CONSTFUNC double Num$from_int64(Int64_t i, bool truncate) {
double n = (double)i; double n = (double)i;
if unlikely (!truncate && (Int64_t)n != i) if unlikely (!truncate && (Int64_t)n != i)
fail("Could not convert integer to 64-bit floating point without losing precision: %ld", i); fail("Could not convert integer to 64-bit floating point without losing precision: ", i);
return n; return n;
} }
MACROLIKE CONSTFUNC double Num$from_int32(Int32_t i) { return (double)i; } MACROLIKE CONSTFUNC double Num$from_int32(Int32_t i) { return (double)i; }
@ -94,7 +94,7 @@ MACROLIKE CONSTFUNC float Num32$from_int(Int_t i, bool truncate) {
if likely (i.small & 0x1) { if likely (i.small & 0x1) {
float ret = (float)(i.small >> 2); float ret = (float)(i.small >> 2);
if unlikely (!truncate && (int64_t)ret != (i.small >> 2)) if unlikely (!truncate && (int64_t)ret != (i.small >> 2))
fail("Could not convert integer to 32-bit floating point without losing precision: %ld", i.small >> 2); fail("Could not convert integer to 32-bit floating point without losing precision: ", i.small >> 2);
return ret; return ret;
} else { } else {
float ret = (float)mpz_get_d(*i.big); float ret = (float)mpz_get_d(*i.big);
@ -102,7 +102,7 @@ MACROLIKE CONSTFUNC float Num32$from_int(Int_t i, bool truncate) {
mpz_t roundtrip; mpz_t roundtrip;
mpz_init_set_d(roundtrip, ret); mpz_init_set_d(roundtrip, ret);
if unlikely (mpz_cmp(*i.big, roundtrip) != 0) if unlikely (mpz_cmp(*i.big, roundtrip) != 0)
fail("Could not convert integer to 32-bit floating point without losing precision: %k", (Text_t[1]){Int$value_as_text(i)}); fail("Could not convert integer to 32-bit floating point without losing precision: ", i);
} }
return ret; return ret;
} }
@ -111,13 +111,13 @@ MACROLIKE CONSTFUNC float Num32$from_int(Int_t i, bool truncate) {
MACROLIKE CONSTFUNC float Num32$from_int64(Int64_t i, bool truncate) { MACROLIKE CONSTFUNC float Num32$from_int64(Int64_t i, bool truncate) {
float n = (float)i; float n = (float)i;
if unlikely (!truncate && (Int64_t)n != i) if unlikely (!truncate && (Int64_t)n != i)
fail("Could not convert integer to 32-bit floating point without losing precision: %ld", i); fail("Could not convert integer to 32-bit floating point without losing precision: ", i);
return n; return n;
} }
MACROLIKE CONSTFUNC float Num32$from_int32(Int32_t i, bool truncate) { MACROLIKE CONSTFUNC float Num32$from_int32(Int32_t i, bool truncate) {
float n = (float)i; float n = (float)i;
if unlikely (!truncate && (Int32_t)n != i) if unlikely (!truncate && (Int32_t)n != i)
fail("Could not convert integer to 32-bit floating point without losing precision: %d", i); fail("Could not convert integer to 32-bit floating point without losing precision: ", i);
return n; return n;
} }
MACROLIKE CONSTFUNC float Num32$from_int16(Int16_t i) { return (float)i; } MACROLIKE CONSTFUNC float Num32$from_int16(Int16_t i) { return (float)i; }

View File

@ -10,12 +10,6 @@
#include "types.h" #include "types.h"
#include "util.h" #include "util.h"
#define OptionalBool_t uint8_t
#define OptionalArray_t Array_t
#define OptionalTable_t Table_t
#define OptionalText_t Text_t
#define OptionalClosure_t Closure_t
#define NONE_ARRAY ((Array_t){.length=-1}) #define NONE_ARRAY ((Array_t){.length=-1})
#define NONE_BOOL ((OptionalBool_t)2) #define NONE_BOOL ((OptionalBool_t)2)
#define NONE_INT ((OptionalInt_t){.small=0}) #define NONE_INT ((OptionalInt_t){.small=0})

View File

@ -17,6 +17,7 @@
#include <unistd.h> #include <unistd.h>
#include <unistr.h> #include <unistr.h>
#include "print.h"
#include "arrays.h" #include "arrays.h"
#include "enums.h" #include "enums.h"
#include "files.h" #include "files.h"
@ -122,8 +123,7 @@ public Path_t Path$_concat(int n, Path_t items[n])
ARRAY_INCREF(result.components); ARRAY_INCREF(result.components);
for (int i = 1; i < n; i++) { for (int i = 1; i < n; i++) {
if (items[i].type.$tag != PATH_RELATIVE) if (items[i].type.$tag != PATH_RELATIVE)
fail("Cannot concatenate an absolute or home-based path onto another path: (%s)\n", fail("Cannot concatenate an absolute or home-based path onto another path: (", items[i], ")");
Path$as_c_string(items[i]));
Array$insert_all(&result.components, items[i].components, I(0), sizeof(Text_t)); Array$insert_all(&result.components, items[i].components, I(0), sizeof(Text_t));
} }
clean_components(&result.components); clean_components(&result.components);
@ -146,8 +146,7 @@ public Path_t Path$resolved(Path_t path, Path_t relative_to)
public Path_t Path$relative_to(Path_t path, Path_t relative_to) public Path_t Path$relative_to(Path_t path, Path_t relative_to)
{ {
if (path.type.$tag != relative_to.type.$tag) if (path.type.$tag != relative_to.type.$tag)
fail("Cannot create a path relative to a different path with a mismatching type: (%k) relative to (%k)", fail("Cannot create a path relative to a different path with a mismatching type: (", path, ") relative to (", relative_to, ")");
(Text_t[1]){Path$as_text(&path, false, &Path$info)}, (Text_t[1]){Path$as_text(&relative_to, false, &Path$info)});
Path_t result = {.type.$tag=PATH_RELATIVE}; Path_t result = {.type.$tag=PATH_RELATIVE};
int64_t shared = 0; int64_t shared = 0;
@ -274,13 +273,13 @@ static void _write(Path_t path, Array_t bytes, int mode, int permissions)
const char *path_str = Path$as_c_string(path); const char *path_str = Path$as_c_string(path);
int fd = open(path_str, mode, permissions); int fd = open(path_str, mode, permissions);
if (fd == -1) if (fd == -1)
fail("Could not write to file: %s\n%s", path_str, strerror(errno)); fail("Could not write to file: ", path_str, "\n", strerror(errno));
if (bytes.stride != 1) if (bytes.stride != 1)
Array$compact(&bytes, 1); Array$compact(&bytes, 1);
ssize_t written = write(fd, bytes.data, (size_t)bytes.length); ssize_t written = write(fd, bytes.data, (size_t)bytes.length);
if (written != (ssize_t)bytes.length) if (written != (ssize_t)bytes.length)
fail("Could not write to file: %s\n%s", path_str, strerror(errno)); fail("Could not write to file: ", path_str, "\n", strerror(errno));
close(fd); close(fd);
} }
@ -328,7 +327,7 @@ public OptionalArray_t Path$read_bytes(Path_t path, OptionalInt_t count)
content[sb.st_size] = '\0'; content[sb.st_size] = '\0';
close(fd); close(fd);
if (count.small && (int64_t)sb.st_size < target_count) if (count.small && (int64_t)sb.st_size < target_count)
fail("Could not read %ld bytes from %k (only got %zu)", target_count, &path, sb.st_size); fail("Could not read ", target_count, " bytes from ", path, " (only got ", sb.st_size, ")");
int64_t len = count.small ? target_count : (int64_t)sb.st_size; int64_t len = count.small ? target_count : (int64_t)sb.st_size;
return (Array_t){.data=content, .atomic=1, .stride=1, .length=len}; return (Array_t){.data=content, .atomic=1, .stride=1, .length=len};
} else { } else {
@ -358,7 +357,7 @@ public OptionalArray_t Path$read_bytes(Path_t path, OptionalInt_t count)
} }
close(fd); close(fd);
if (count.small != 0 && (int64_t)len < target_count) if (count.small != 0 && (int64_t)len < target_count)
fail("Could not read %ld bytes from %k (only got %zu)", target_count, &path, len); fail("Could not read ", target_count, " bytes from ", path, " (only got ", len, ")");
return (Array_t){.data=content, .atomic=1, .stride=1, .length=len}; return (Array_t){.data=content, .atomic=1, .stride=1, .length=len};
} }
} }
@ -393,14 +392,14 @@ public void Path$set_owner(Path_t path, OptionalText_t owner, OptionalText_t gro
uid_t owner_id = (uid_t)-1; uid_t owner_id = (uid_t)-1;
if (owner.length >= 0) { if (owner.length >= 0) {
struct passwd *pwd = getpwnam(Text$as_c_string(owner)); struct passwd *pwd = getpwnam(Text$as_c_string(owner));
if (pwd == NULL) fail("Not a valid user: %k", &owner); if (pwd == NULL) fail("Not a valid user: ", owner);
owner_id = pwd->pw_uid; owner_id = pwd->pw_uid;
} }
gid_t group_id = (gid_t)-1; gid_t group_id = (gid_t)-1;
if (group.length >= 0) { if (group.length >= 0) {
struct group *grp = getgrnam(Text$as_c_string(group)); struct group *grp = getgrnam(Text$as_c_string(group));
if (grp == NULL) fail("Not a valid group: %k", &group); if (grp == NULL) fail("Not a valid group: ", group);
group_id = grp->gr_gid; group_id = grp->gr_gid;
} }
const char *path_str = Path$as_c_string(path); const char *path_str = Path$as_c_string(path);
@ -415,17 +414,16 @@ static int _remove_files(const char *path, const struct stat *sbuf, int type, st
switch (type) { switch (type) {
case FTW_F: case FTW_SL: case FTW_SLN: case FTW_F: case FTW_SL: case FTW_SLN:
if (remove(path) < 0) { if (remove(path) < 0) {
fail("Could not remove file: %s (%s)", path, strerror(errno)); fail("Could not remove file: ", path, " (", strerror(errno), ")");
return -1; return -1;
} }
return 0; return 0;
case FTW_DP: case FTW_DP:
if (rmdir(path) != 0) if (rmdir(path) != 0)
fail("Could not remove directory: %s (%s)", path, strerror(errno)); fail("Could not remove directory: ", path, " (", strerror(errno), ")");
return 0; return 0;
default: default:
printf("Type: %d\n", type); fail("Could not remove path: ", path, " (not a file or directory)");
fail("Could not remove path: %s (not a file or directory)", path, strerror(errno));
return -1; return -1;
} }
} }
@ -437,19 +435,19 @@ public void Path$remove(Path_t path, bool ignore_missing)
struct stat sb; struct stat sb;
if (lstat(path_str, &sb) != 0) { if (lstat(path_str, &sb) != 0) {
if (!ignore_missing) if (!ignore_missing)
fail("Could not remove file: %s (%s)", path_str, strerror(errno)); fail("Could not remove file: ", path_str, " (", strerror(errno), ")");
return; return;
} }
if ((sb.st_mode & S_IFMT) == S_IFREG || (sb.st_mode & S_IFMT) == S_IFLNK) { if ((sb.st_mode & S_IFMT) == S_IFREG || (sb.st_mode & S_IFMT) == S_IFLNK) {
if (unlink(path_str) != 0 && !ignore_missing) if (unlink(path_str) != 0 && !ignore_missing)
fail("Could not remove file: %s (%s)", path_str, strerror(errno)); fail("Could not remove file: ", path_str, " (", strerror(errno), ")");
} else if ((sb.st_mode & S_IFMT) == S_IFDIR) { } else if ((sb.st_mode & S_IFMT) == S_IFDIR) {
const int num_open_fd = 10; const int num_open_fd = 10;
if (nftw(path_str, _remove_files, num_open_fd, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) < 0) if (nftw(path_str, _remove_files, num_open_fd, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) < 0)
fail("Could not remove directory: %s (%s)", path_str, strerror(errno)); fail("Could not remove directory: %s (%s)", path_str, strerror(errno));
} else { } else {
fail("Could not remove path: %s (not a file or directory)", path_str, strerror(errno)); fail("Could not remove path: ", path_str, " (not a file or directory)");
} }
} }
@ -459,7 +457,7 @@ public void Path$create_directory(Path_t path, int permissions)
const char *c_path = Path$as_c_string(path); const char *c_path = Path$as_c_string(path);
int status = mkdir(c_path, (mode_t)permissions); int status = mkdir(c_path, (mode_t)permissions);
if (status != 0 && errno != EEXIST) if (status != 0 && errno != EEXIST)
fail("Could not create directory: %s (%s)", c_path, strerror(errno)); fail("Could not create directory: ", c_path, " (", strerror(errno), ")");
} }
static Array_t _filtered_children(Path_t path, bool include_hidden, mode_t filter) static Array_t _filtered_children(Path_t path, bool include_hidden, mode_t filter)
@ -471,7 +469,7 @@ static Array_t _filtered_children(Path_t path, bool include_hidden, mode_t filte
size_t path_len = strlen(path_str); size_t path_len = strlen(path_str);
DIR *d = opendir(path_str); DIR *d = opendir(path_str);
if (!d) if (!d)
fail("Could not open directory: %k (%s)", &path, strerror(errno)); fail("Could not open directory: ", path, " (", strerror(errno), ")");
if (path_str[path_len-1] == '/') if (path_str[path_len-1] == '/')
--path_len; --path_len;
@ -516,13 +514,13 @@ public Path_t Path$unique_directory(Path_t path)
path = Path$expand_home(path); path = Path$expand_home(path);
const char *path_str = Path$as_c_string(path); const char *path_str = Path$as_c_string(path);
size_t len = strlen(path_str); size_t len = strlen(path_str);
if (len >= PATH_MAX) fail("Path is too long: %s", path_str); if (len >= PATH_MAX) fail("Path is too long: ", path_str);
char buf[PATH_MAX] = {}; char buf[PATH_MAX] = {};
strcpy(buf, path_str); strcpy(buf, path_str);
if (buf[len-1] == '/') if (buf[len-1] == '/')
buf[--len] = '\0'; buf[--len] = '\0';
char *created = mkdtemp(buf); char *created = mkdtemp(buf);
if (!created) fail("Failed to create temporary directory: %s (%s)", path_str, strerror(errno)); if (!created) fail("Failed to create temporary directory: ", path_str, " (", strerror(errno), ")");
return Path$from_str(created); return Path$from_str(created);
} }
@ -531,7 +529,7 @@ public Path_t Path$write_unique_bytes(Path_t path, Array_t bytes)
path = Path$expand_home(path); path = Path$expand_home(path);
const char *path_str = Path$as_c_string(path); const char *path_str = Path$as_c_string(path);
size_t len = strlen(path_str); size_t len = strlen(path_str);
if (len >= PATH_MAX) fail("Path is too long: %s", path_str); if (len >= PATH_MAX) fail("Path is too long: ", path_str);
char buf[PATH_MAX] = {}; char buf[PATH_MAX] = {};
strcpy(buf, path_str); strcpy(buf, path_str);
@ -543,14 +541,14 @@ public Path_t Path$write_unique_bytes(Path_t path, Array_t bytes)
int fd = mkstemps(buf, suffixlen); int fd = mkstemps(buf, suffixlen);
if (fd == -1) if (fd == -1)
fail("Could not write to unique file: %s\n%s", buf, strerror(errno)); fail("Could not write to unique file: ", buf, "\n", strerror(errno));
if (bytes.stride != 1) if (bytes.stride != 1)
Array$compact(&bytes, 1); Array$compact(&bytes, 1);
ssize_t written = write(fd, bytes.data, (size_t)bytes.length); ssize_t written = write(fd, bytes.data, (size_t)bytes.length);
if (written != (ssize_t)bytes.length) if (written != (ssize_t)bytes.length)
fail("Could not write to file: %s\n%s", buf, strerror(errno)); fail("Could not write to file: ", buf, "\n", strerror(errno));
close(fd); close(fd);
return Path$from_str(buf); return Path$from_str(buf);
} }
@ -736,42 +734,36 @@ public PUREFUNC bool Path$equal_values(Path_t a, Path_t b)
return Array$equal(&a.components, &b.components, Array$info(&Text$info)); return Array$equal(&a.components, &b.components, Array$info(&Text$info));
} }
public const char *Path$as_c_string(Path_t path) public int Path$print(FILE *f, Path_t path)
{ {
if (path.components.length == 0) { if (path.components.length == 0) {
if (path.type.$tag == PATH_ABSOLUTE) return "/"; if (path.type.$tag == PATH_ABSOLUTE) return fputs("/", f);
else if (path.type.$tag == PATH_RELATIVE) return "."; else if (path.type.$tag == PATH_RELATIVE) return fputs(".", f);
else if (path.type.$tag == PATH_HOME) return "~"; else if (path.type.$tag == PATH_HOME) return fputs("~", f);
} }
size_t len = 0, capacity = 16; int n = 0;
char *buf = GC_MALLOC_ATOMIC(capacity);
if (path.type.$tag == PATH_ABSOLUTE) { if (path.type.$tag == PATH_ABSOLUTE) {
buf[len++] = '/'; n += fputc('/', f);
} else if (path.type.$tag == PATH_HOME) { } else if (path.type.$tag == PATH_HOME) {
buf[len++] = '~'; n += fputs("~/", f);
buf[len++] = '/';
} else if (path.type.$tag == PATH_RELATIVE) { } else if (path.type.$tag == PATH_RELATIVE) {
if (!Text$equal_values(*(Text_t*)path.components.data, Text(".."))) { if (!Text$equal_values(*(Text_t*)path.components.data, Text("..")))
buf[len++] = '.'; n += fputs("./", f);
buf[len++] = '/';
}
} }
for (int64_t i = 0; i < path.components.length; i++) { for (int64_t i = 0; i < path.components.length; i++) {
Text_t *comp = (Text_t*)(path.components.data + i*path.components.stride); Text_t *comp = (Text_t*)(path.components.data + i*path.components.stride);
const char *comp_str = Text$as_c_string(*comp); n += Text$print(f, *comp);
size_t comp_len = strlen(comp_str);
if (len + comp_len + 1 > capacity) {
buf = GC_REALLOC(buf, (capacity += MIN(comp_len + 2, 16)));
}
memcpy(&buf[len], comp_str, comp_len);
len += comp_len;
if (i + 1 < path.components.length) if (i + 1 < path.components.length)
buf[len++] = '/'; n += fputc('/', f);
} }
buf[len++] = '\0'; return n;
return buf; }
public const char *Path$as_c_string(Path_t path)
{
return String(path);
} }
public Text_t Path$as_text(const void *obj, bool color, const TypeInfo_t *type) public Text_t Path$as_text(const void *obj, bool color, const TypeInfo_t *type)

View File

@ -11,6 +11,7 @@
Path_t Path$from_str(const char *str); Path_t Path$from_str(const char *str);
Path_t Path$from_text(Text_t text); Path_t Path$from_text(Text_t text);
// int Path$print(FILE *f, Path_t path);
const char *Path$as_c_string(Path_t path); const char *Path$as_c_string(Path_t path);
#define Path(str) Path$from_str(str) #define Path(str) Path$from_str(str)
Path_t Path$_concat(int n, Path_t items[n]); Path_t Path$_concat(int n, Path_t items[n]);

View File

@ -518,7 +518,7 @@ static pat_t parse_next_pat(TextIter_t *state, int64_t *index)
int32_t close = open; int32_t close = open;
uc_mirror_char((ucs4_t)open, (ucs4_t*)&close); uc_mirror_char((ucs4_t)open, (ucs4_t*)&close);
if (!match_grapheme(state, index, close)) if (!match_grapheme(state, index, close))
fail("Pattern's closing quote is missing: %k", &state->stack[0].text); fail("Pattern's closing quote is missing: ", state->stack[0].text);
return (pat_t){ return (pat_t){
.tag=PAT_QUOTE, .tag=PAT_QUOTE,
@ -533,7 +533,7 @@ static pat_t parse_next_pat(TextIter_t *state, int64_t *index)
int32_t close = open; int32_t close = open;
uc_mirror_char((ucs4_t)open, (ucs4_t*)&close); uc_mirror_char((ucs4_t)open, (ucs4_t*)&close);
if (!match_grapheme(state, index, close)) if (!match_grapheme(state, index, close))
fail("Pattern's closing brace is missing: %k", &state->stack[0].text); fail("Pattern's closing brace is missing: ", state->stack[0].text);
return (pat_t){ return (pat_t){
.tag=PAT_PAIR, .tag=PAT_PAIR,
@ -553,7 +553,7 @@ static pat_t parse_next_pat(TextIter_t *state, int64_t *index)
} else { } else {
max = min; max = min;
} }
if (min > max) fail("Minimum repetitions (%ld) is less than the maximum (%ld)", min, max); if (min > max) fail("Minimum repetitions (", min, ") is less than the maximum (", max, ")");
} else { } else {
min = -1, max = -1; min = -1, max = -1;
} }
@ -573,19 +573,19 @@ static pat_t parse_next_pat(TextIter_t *state, int64_t *index)
skip_whitespace(state, index); skip_whitespace(state, index);
int32_t grapheme = Text$get_grapheme_fast(state, (*index)++); int32_t grapheme = Text$get_grapheme_fast(state, (*index)++);
if (!match_grapheme(state, index, '}')) if (!match_grapheme(state, index, '}'))
fail("Missing closing '}' in pattern: %k", &state->stack[0].text); fail("Missing closing '}' in pattern: ", state->stack[0].text);
return PAT(PAT_GRAPHEME, .grapheme=grapheme); return PAT(PAT_GRAPHEME, .grapheme=grapheme);
} else if (strlen(prop_name) == 1) { } else if (strlen(prop_name) == 1) {
// Single letter names: {1+ A} // Single letter names: {1+ A}
skip_whitespace(state, index); skip_whitespace(state, index);
if (!match_grapheme(state, index, '}')) if (!match_grapheme(state, index, '}'))
fail("Missing closing '}' in pattern: %k", &state->stack[0].text); fail("Missing closing '}' in pattern: ", state->stack[0].text);
return PAT(PAT_GRAPHEME, .grapheme=prop_name[0]); return PAT(PAT_GRAPHEME, .grapheme=prop_name[0]);
} }
skip_whitespace(state, index); skip_whitespace(state, index);
if (!match_grapheme(state, index, '}')) if (!match_grapheme(state, index, '}'))
fail("Missing closing '}' in pattern: %k", &state->stack[0].text); fail("Missing closing '}' in pattern: ", state->stack[0].text);
switch (tolower(prop_name[0])) { switch (tolower(prop_name[0])) {
case '.': case '.':
@ -673,7 +673,7 @@ static pat_t parse_next_pat(TextIter_t *state, int64_t *index)
ucs4_t grapheme = unicode_name_character(prop_name); ucs4_t grapheme = unicode_name_character(prop_name);
if (grapheme == UNINAME_INVALID) if (grapheme == UNINAME_INVALID)
fail("Not a valid property or character name: %s", prop_name); fail("Not a valid property or character name: ", prop_name);
return PAT(PAT_GRAPHEME, .grapheme=(int32_t)grapheme); return PAT(PAT_GRAPHEME, .grapheme=(int32_t)grapheme);
#undef PAT #undef PAT
} else { } else {
@ -942,14 +942,14 @@ static Text_t apply_backrefs(Text_t text, Array_t recursive_replacements, Text_t
pos += 1; pos += 1;
continue; continue;
} }
if (backref < 0 || backref > 9) fail("Invalid backref index: %ld (only 0-%d are allowed)", backref, MAX_BACKREFS-1); if (backref < 0 || backref > 9) fail("Invalid backref index: ", backref, " (only 0-", MAX_BACKREFS-1, " are allowed)");
backref_len = (after_backref - pos); backref_len = (after_backref - pos);
if (Text$get_grapheme_fast(&replacement_state, pos + backref_len) == ';') if (Text$get_grapheme_fast(&replacement_state, pos + backref_len) == ';')
backref_len += 1; // skip optional semicolon backref_len += 1; // skip optional semicolon
if (!captures[backref].occupied) if (!captures[backref].occupied)
fail("There is no capture number %ld!", backref); fail("There is no capture number ", backref, "!");
Text_t backref_text = Text$slice(text, I(captures[backref].index+1), I(captures[backref].index + captures[backref].length)); Text_t backref_text = Text$slice(text, I(captures[backref].index+1), I(captures[backref].index + captures[backref].length));

View File

@ -3,7 +3,6 @@
// The type representing text patterns for pattern matching. // The type representing text patterns for pattern matching.
#include <stdbool.h> #include <stdbool.h>
#include <printf.h>
#include <stdint.h> #include <stdint.h>
#include "datatypes.h" #include "datatypes.h"

76
src/stdlib/print.c Normal file
View File

@ -0,0 +1,76 @@
// This file defines some of the helper functions used for printing values
#include "print.h"
#include <stdio.h>
int _print_char(FILE *f, char c)
{
#if PRINT_COLOR
#define ESC(e) "\033[35m'\033[34;1m\\" e "\033[0;35m'\033[m"
#else
#define ESC(e) "'\\" e "'"
#endif
const char *named[256] = {['\n']=ESC("n"), ['\t']=ESC("t"), ['\r']=ESC("r"),
['\033']=ESC("e"), ['\v']=ESC("v"), ['\a']=ESC("a"), ['\b']=ESC("b")};
const char *name = named[(uint8_t)c];
if (name != NULL)
return fputs(name, f);
else if (isprint(c))
#if PRINT_COLOR
return fprintf(f, "\033[35m'%c'\033[m"), c);
#else
return fprintf(f, "'%c'", c);
#endif
else
return fprintf(f, ESC("x%02X"), (uint8_t)c);
#undef ESC
}
int _print_quoted(FILE *f, quoted_t quoted)
{
#if PRINT_COLOR
#define ESC(e) "\033[34;1m\\" e "\033[0;35m"
#else
#define ESC(e) "\\" e
#endif
const char *named[256] = {['\n']=ESC("n"), ['\t']=ESC("t"), ['\r']=ESC("r"),
['\033']=ESC("e"), ['\v']=ESC("v"), ['\a']=ESC("a"), ['\b']=ESC("b")};
int printed = fputs("\033[35m\"", f);
for (const char *p = quoted.str; *p; p++) {
const char *name = named[(uint8_t)*p];
if (name != NULL) {
printed += fputs(name, f);
} else if (isprint(*p) || (uint8_t)*p > 0x7F) {
printed += fputc(*p, f);
} else {
printed += fprintf(f, ESC("x%02X"), (uint8_t)*p);
}
}
printed += fputs("\"\033[m", f);
#undef ESC
return printed;
}
static ssize_t _gc_stream_write(void *cookie, const char *buf, size_t size) {
gc_stream_t *stream = (gc_stream_t *)cookie;
if (stream->position + size + 1 > *stream->size)
*stream->buffer = GC_REALLOC(*stream->buffer, (*stream->size += MAX(MAX(16, *stream->size/2), size + 1)));
memcpy(&(*stream->buffer)[stream->position], buf, size);
stream->position += size;
(*stream->buffer)[stream->position] = '\0';
return (ssize_t)size;
}
FILE *gc_memory_stream(char **buf, size_t *size) {
gc_stream_t *stream = GC_MALLOC(sizeof(gc_stream_t));
stream->size = size;
stream->buffer = buf;
*stream->size = 16;
*stream->buffer = GC_MALLOC_ATOMIC(*stream->size);
(*stream->buffer)[0] = '\0';
stream->position = 0;
cookie_io_functions_t functions = {.write = _gc_stream_write};
return fopencookie(stream, "w", functions);
}
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0

166
src/stdlib/print.h Normal file
View File

@ -0,0 +1,166 @@
#pragma once
// This file defines some functions to make it easy to do formatted text
// without using printf style specifiers:
//
// print(...) - print text
// fprint(file, ...) - print text to file
// print_err(...) - print an error message and exit with EXIT_FAILURE
// String(...) - return an allocated string
//
// If you put `#define PRINT_COLOR 1` before the import, text will be printed
// with terminal colors.
#include <assert.h>
#include <ctype.h>
#include <gc.h>
#include <gc/cord.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <unistd.h>
#include "datatypes.h"
#ifndef PRINT_COLOR
#define PRINT_COLOR 0
#endif
#define EVAL0(...) __VA_ARGS__
#define EVAL1(...) EVAL0(EVAL0(EVAL0(__VA_ARGS__)))
#define EVAL2(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__)))
#define EVAL3(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))
#define EVAL4(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__)))
#define EVAL(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__)))
#define MAP_END(...)
#define MAP_OUT
#define MAP_COMMA ,
#define MAP_GET_END2() 0, MAP_END
#define MAP_GET_END1(...) MAP_GET_END2
#define MAP_GET_END(...) MAP_GET_END1
#define MAP_NEXT0(test, next, ...) next MAP_OUT
#define MAP_LIST_NEXT1(test, next) MAP_NEXT0(test, MAP_COMMA next, 0)
#define MAP_LIST_NEXT(test, next) MAP_LIST_NEXT1(MAP_GET_END test, next)
#define MAP_LIST0(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST1)(f, peek, __VA_ARGS__)
#define MAP_LIST1(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST0)(f, peek, __VA_ARGS__)
#define MAP_LIST(f, ...) EVAL(MAP_LIST1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
// GCC lets you define macro-like functions which are always inlined and never
// compiled using this combination of flags. See: https://gcc.gnu.org/onlinedocs/gcc/Inline.html
#ifndef PRINT_FN
#define PRINT_FN extern inline __attribute__((gnu_inline, always_inline)) int
#endif
typedef struct {
uint64_t n;
bool no_prefix;
bool uppercase;
int digits;
} hex_format_t;
#define hex(x, ...) ((hex_format_t){.n=x, __VA_ARGS__})
typedef struct {
uint64_t n;
bool no_prefix;
bool uppercase;
int digits;
} oct_format_t;
#define oct(x, ...) ((oct_format_t){.n=x, __VA_ARGS__})
typedef struct {
double n;
int precision;
} num_format_t;
#define num_fmt(x, ...) ((num_format_t){.n=x, __VA_ARGS__})
typedef struct {
const char *str;
} quoted_t;
#define quoted(s) ((quoted_t){s})
#if PRINT_COLOR
#define hl(s) "\033[35m" s "\033[m"
#else
#define hl(s) s
#endif
PRINT_FN _print_int(FILE *f, int64_t x) { return fprintf(f, hl("%ld"), x); }
PRINT_FN _print_uint(FILE *f, uint64_t x) { return fprintf(f, hl("%lu"), x); }
PRINT_FN _print_float(FILE *f, float x) { return fprintf(f, hl("%g"), (double)x); }
PRINT_FN _print_double(FILE *f, double x) { return fprintf(f, hl("%g"), x); }
PRINT_FN _print_pointer(FILE *f, void *p) { return fprintf(f, hl("%p"), p); }
PRINT_FN _print_bool(FILE *f, bool b) { return fputs(b ? hl("yes") : hl("no"), f); }
PRINT_FN _print_str(FILE *f, const char *s) { return fputs(s, f); }
int _print_char(FILE *f, char c);
int _print_quoted(FILE *f, quoted_t quoted);
PRINT_FN _print_hex(FILE *f, hex_format_t hex) {
return fprintf(f, hex.no_prefix ? (hex.uppercase ? hl("%0*lX") : hl("%0*lx")) : (hex.uppercase ? hl("0x%0*lX") : hl("%#0*lx")), hex.digits, hex.n);
}
PRINT_FN _print_oct(FILE *f, oct_format_t oct) {
return fprintf(f, oct.no_prefix ? (oct.uppercase ? hl("%0*lO") : hl("%0*lo")) : (oct.uppercase ? hl("%#0*lO") : hl("%#0*lo")), oct.digits, oct.n);
}
PRINT_FN _print_num_format(FILE *f, num_format_t num) {
return fprintf(f, hl("%.*lf"), num.precision, num.n);
}
#undef hl
extern int Text$print(FILE *stream, Text_t text);
extern int Path$print(FILE *stream, Path_t path);
#ifndef _fprint1
#define _fprint1(f, x) _Generic((x), \
char*: _print_str, \
const char*: _print_str, \
char: _print_char, \
bool: _print_bool, \
int64_t: _print_int, \
int32_t: _print_int, \
int16_t: _print_int, \
int8_t: _print_int, \
uint64_t: _print_uint, \
uint32_t: _print_uint, \
uint16_t: _print_uint, \
uint8_t: _print_uint, \
float: _print_float, \
double: _print_double, \
hex_format_t: _print_hex, \
oct_format_t: _print_oct, \
num_format_t: _print_num_format, \
quoted_t: _print_quoted, \
Text_t: Text$print, \
Path_t: Path$print, \
Int_t: Int$print, \
void*: _print_pointer)(f, x)
#endif
typedef struct {
char **buffer;
size_t *size;
size_t position;
} gc_stream_t;
FILE *gc_memory_stream(char **buf, size_t *size);
#define _print(x) _n += _fprint1(_printing, x)
#define _fprint(f, ...) ({ FILE *_printing = f; int _n = 0; MAP_LIST(_print, __VA_ARGS__); _n; })
#define fprint(f, ...) _fprint(f, __VA_ARGS__, "\n")
#define print(...) fprint(stdout, __VA_ARGS__)
#define fprint_inline(f, ...) _fprint(f, __VA_ARGS__)
#define print_inline(...) fprint_inline(stdout, __VA_ARGS__)
#define String(...) ({ \
char *_buf = NULL; \
size_t _size = 0; \
FILE *_stream = gc_memory_stream(&_buf, &_size); \
assert(_stream); \
_fprint(_stream, __VA_ARGS__); \
fflush(_stream); \
_buf; })
#define print_err(...) ({ fprint(stderr, __VA_ARGS__); exit(EXIT_FAILURE); })
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0

View File

@ -109,11 +109,8 @@ public Int_t RNG$int(RNG_t rng, Int_t min, Int_t max)
} }
int32_t cmp = Int$compare_value(min, max); int32_t cmp = Int$compare_value(min, max);
if (cmp > 0) { if (cmp > 0)
Text_t min_text = Int$as_text(&min, false, &Int$info), max_text = Int$as_text(&max, false, &Int$info); fail("Random minimum value (", min, ") is larger than the maximum value (", max, ")");
fail("Random minimum value (%k) is larger than the maximum value (%k)",
&min_text, &max_text);
}
if (cmp == 0) return min; if (cmp == 0) return min;
mpz_t range_size; mpz_t range_size;
@ -140,7 +137,7 @@ public Int_t RNG$int(RNG_t rng, Int_t min, Int_t max)
public Int64_t RNG$int64(RNG_t rng, Int64_t min, Int64_t max) public Int64_t RNG$int64(RNG_t rng, Int64_t min, Int64_t max)
{ {
if (min > max) fail("Random minimum value (%ld) is larger than the maximum value (%ld)", min, max); if (min > max) fail("Random minimum value (", min, ") is larger than the maximum value (", max, ")");
if (min == max) return min; if (min == max) return min;
if (min == INT64_MIN && max == INT64_MAX) { if (min == INT64_MIN && max == INT64_MAX) {
int64_t r; int64_t r;
@ -159,7 +156,7 @@ public Int64_t RNG$int64(RNG_t rng, Int64_t min, Int64_t max)
public Int32_t RNG$int32(RNG_t rng, Int32_t min, Int32_t max) public Int32_t RNG$int32(RNG_t rng, Int32_t min, Int32_t max)
{ {
if (min > max) fail("Random minimum value (%d) is larger than the maximum value (%d)", min, max); if (min > max) fail("Random minimum value (", min, ") is larger than the maximum value (", max, ")");
if (min == max) return min; if (min == max) return min;
if (min == INT32_MIN && max == INT32_MAX) { if (min == INT32_MIN && max == INT32_MAX) {
int32_t r; int32_t r;
@ -178,7 +175,7 @@ public Int32_t RNG$int32(RNG_t rng, Int32_t min, Int32_t max)
public Int16_t RNG$int16(RNG_t rng, Int16_t min, Int16_t max) public Int16_t RNG$int16(RNG_t rng, Int16_t min, Int16_t max)
{ {
if (min > max) fail("Random minimum value (%d) is larger than the maximum value (%d)", min, max); if (min > max) fail("Random minimum value (", min, ") is larger than the maximum value (", max, ")");
if (min == max) return min; if (min == max) return min;
if (min == INT16_MIN && max == INT16_MAX) { if (min == INT16_MIN && max == INT16_MAX) {
int16_t r; int16_t r;
@ -197,7 +194,7 @@ public Int16_t RNG$int16(RNG_t rng, Int16_t min, Int16_t max)
public Int8_t RNG$int8(RNG_t rng, Int8_t min, Int8_t max) public Int8_t RNG$int8(RNG_t rng, Int8_t min, Int8_t max)
{ {
if (min > max) fail("Random minimum value (%d) is larger than the maximum value (%d)", min, max); if (min > max) fail("Random minimum value (", min, ") is larger than the maximum value (", max, ")");
if (min == max) return min; if (min == max) return min;
if (min == INT8_MIN && max == INT8_MAX) { if (min == INT8_MIN && max == INT8_MAX) {
int8_t r; int8_t r;
@ -216,7 +213,7 @@ public Int8_t RNG$int8(RNG_t rng, Int8_t min, Int8_t max)
public Num_t RNG$num(RNG_t rng, Num_t min, Num_t max) public Num_t RNG$num(RNG_t rng, Num_t min, Num_t max)
{ {
if (min > max) fail("Random minimum value (%g) is larger than the maximum value (%g)", min, max); if (min > max) fail("Random minimum value (", min, ") is larger than the maximum value (", max, ")");
if (min == max) return min; if (min == max) return min;
union { union {

View File

@ -12,6 +12,7 @@
#include <sys/random.h> #include <sys/random.h>
#include <time.h> #include <time.h>
#include "print.h"
#include "bools.h" #include "bools.h"
#include "files.h" #include "files.h"
#include "functiontype.h" #include "functiontype.h"
@ -57,9 +58,6 @@ public void tomo_init(void)
Array_t rng_seed = {.length=sizeof(random_bytes), .data=random_bytes, .stride=1, .atomic=1}; Array_t rng_seed = {.length=sizeof(random_bytes), .data=random_bytes, .stride=1, .atomic=1};
RNG$set_seed(default_rng, rng_seed); RNG$set_seed(default_rng, rng_seed);
if (register_printf_specifier('k', printf_text, printf_text_size))
errx(1, "Couldn't set printf specifier");
struct sigaction sigact; struct sigaction sigact;
sigact.sa_sigaction = signal_handler; sigact.sa_sigaction = signal_handler;
sigemptyset(&sigact.sa_mask); sigemptyset(&sigact.sa_mask);
@ -136,7 +134,7 @@ static bool parse_single_arg(const TypeInfo_t *info, char *arg, void *dest)
// Single-argument tag: // Single-argument tag:
if (arg[len] != ':') if (arg[len] != ':')
errx(1, "Invalid value for %k.%s: %s", &t, named.name, arg); print_err("Invalid value for ", t, ".", named.name, ": ", arg);
size_t offset = sizeof(int32_t); size_t offset = sizeof(int32_t);
if (named.type->align > 0 && offset % (size_t)named.type->align > 0) if (named.type->align > 0 && offset % (size_t)named.type->align > 0)
offset += (size_t)named.type->align - (offset % (size_t)named.type->align); offset += (size_t)named.type->align - (offset % (size_t)named.type->align);
@ -145,7 +143,7 @@ static bool parse_single_arg(const TypeInfo_t *info, char *arg, void *dest)
return true; return true;
} }
} }
errx(1, "Invalid value for %s: %s", info->EnumInfo.name, arg); print_err("Invalid value for ", info->EnumInfo.name, ": ", arg);
} else if (info->tag == StructInfo) { } else if (info->tag == StructInfo) {
if (info->StructInfo.num_fields == 0) if (info->StructInfo.num_fields == 0)
return true; return true;
@ -153,14 +151,14 @@ static bool parse_single_arg(const TypeInfo_t *info, char *arg, void *dest)
return parse_single_arg(info->StructInfo.fields[0].type, arg, dest); return parse_single_arg(info->StructInfo.fields[0].type, arg, dest);
Text_t t = generic_as_text(NULL, false, info); Text_t t = generic_as_text(NULL, false, info);
errx(1, "Unsupported multi-argument struct type for argument parsing: %k", &t); print_err("Unsupported multi-argument struct type for argument parsing: ", t);
} else if (info->tag == ArrayInfo) { } else if (info->tag == ArrayInfo) {
errx(1, "Array arguments must be specified as `--flag ...` not `--flag=...`"); print_err("Array arguments must be specified as `--flag ...` not `--flag=...`");
} else if (info->tag == TableInfo) { } else if (info->tag == TableInfo) {
errx(1, "Table arguments must be specified as `--flag ...` not `--flag=...`"); print_err("Table arguments must be specified as `--flag ...` not `--flag=...`");
} else { } else {
Text_t t = generic_as_text(NULL, false, info); Text_t t = generic_as_text(NULL, false, info);
errx(1, "Unsupported type for argument parsing: %k", &t); print_err("Unsupported type for argument parsing: ", t);
} }
} }
@ -178,7 +176,7 @@ static Array_t parse_array(const TypeInfo_t *item_info, int n, char *args[])
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
bool success = parse_single_arg(item_info, args[i], items.data + items.stride*i); bool success = parse_single_arg(item_info, args[i], items.data + items.stride*i);
if (!success) if (!success)
errx(1, "Couldn't parse argument: %s", args[i]); print_err("Couldn't parse argument: ", args[i]);
} }
return items; return items;
} }
@ -209,11 +207,11 @@ static Table_t parse_table(const TypeInfo_t *table, int n, char *args[])
bool success = parse_single_arg(key, key_arg, entries.data + entries.stride*i); bool success = parse_single_arg(key, key_arg, entries.data + entries.stride*i);
if (!success) if (!success)
errx(1, "Couldn't parse table key: %s", key_arg); print_err("Couldn't parse table key: ", key_arg);
success = parse_single_arg(value, value_arg, entries.data + entries.stride*i + value_offset); success = parse_single_arg(value, value_arg, entries.data + entries.stride*i + value_offset);
if (!success) if (!success)
errx(1, "Couldn't parse table value: %s", value_arg); print_err("Couldn't parse table value: ", value_arg);
*equals = '='; *equals = '=';
} }
@ -281,18 +279,18 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help,
*(OptionalBool_t*)spec[s].dest = true; *(OptionalBool_t*)spec[s].dest = true;
} else { } else {
if (i + 1 >= argc) if (i + 1 >= argc)
errx(1, "Missing argument: %s\n%k", argv[i], &usage); print_err("Missing argument: ", argv[i], "\n", usage);
used_args[i+1] = true; used_args[i+1] = true;
populated_args[s] = parse_single_arg(spec[s].type, argv[i+1], spec[s].dest); populated_args[s] = parse_single_arg(spec[s].type, argv[i+1], spec[s].dest);
if (!populated_args[s]) if (!populated_args[s])
errx(1, "Couldn't parse argument: %s %s\n%k", argv[i], argv[i+1], &usage); print_err("Couldn't parse argument: ", argv[i], " ", argv[i+1], "\n", usage);
} }
goto next_arg; goto next_arg;
} else if (after_name == '=') { // --foo=val } else if (after_name == '=') { // --foo=val
used_args[i] = true; used_args[i] = true;
populated_args[s] = parse_single_arg(spec[s].type, 2 + argv[i] + strlen(spec[s].name) + 1, spec[s].dest); populated_args[s] = parse_single_arg(spec[s].type, 2 + argv[i] + strlen(spec[s].name) + 1, spec[s].dest);
if (!populated_args[s]) if (!populated_args[s])
errx(1, "Couldn't parse argument: %s\n%k", argv[i], &usage); print_err("Couldn't parse argument: ", argv[i], "\n", usage);
goto next_arg; goto next_arg;
} else { } else {
continue; continue;
@ -303,10 +301,11 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help,
say(help, true); say(help, true);
exit(0); exit(0);
} }
errx(1, "Unrecognized argument: %s\n%k", argv[i], &usage); print_err("Unrecognized argument: ", argv[i], "\n", usage);
} else if (argv[i][0] == '-' && argv[i][1] && argv[i][1] != '-') { // Single flag args } else if (argv[i][0] == '-' && argv[i][1] && argv[i][1] != '-') { // Single flag args
used_args[i] = true; used_args[i] = true;
for (char *f = argv[i] + 1; *f; f++) { for (char *f = argv[i] + 1; *f; f++) {
char flag[] = {'-', *f, 0};
for (int s = 0; s < spec_len; s++) { for (int s = 0; s < spec_len; s++) {
if (spec[s].name[0] != *f || strlen(spec[s].name) > 1) if (spec[s].name[0] != *f || strlen(spec[s].name) > 1)
continue; continue;
@ -316,7 +315,7 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help,
non_opt_type = non_opt_type->OptionalInfo.type; non_opt_type = non_opt_type->OptionalInfo.type;
if (non_opt_type->tag == ArrayInfo) { if (non_opt_type->tag == ArrayInfo) {
if (f[1]) errx(1, "No value provided for -%c\n%k", *f, &usage); if (f[1]) print_err("No value provided for ", flag, "\n", usage);
int num_args = 0; int num_args = 0;
while (i + 1 + num_args < argc) { while (i + 1 + num_args < argc) {
if (argv[i+1+num_args][0] == '-') if (argv[i+1+num_args][0] == '-')
@ -340,11 +339,11 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help,
populated_args[s] = true; populated_args[s] = true;
*(OptionalBool_t*)spec[s].dest = true; *(OptionalBool_t*)spec[s].dest = true;
} else { } else {
if (f[1] || i+1 >= argc) errx(1, "No value provided for -%c\n%k", *f, &usage); if (f[1] || i+1 >= argc) print_err("No value provided for ", flag, "\n", usage);
used_args[i+1] = true; used_args[i+1] = true;
populated_args[s] = parse_single_arg(spec[s].type, argv[i+1], spec[s].dest); populated_args[s] = parse_single_arg(spec[s].type, argv[i+1], spec[s].dest);
if (!populated_args[s]) if (!populated_args[s])
errx(1, "Couldn't parse argument: %s %s\n%k", argv[i], argv[i+1], &usage); print_err("Couldn't parse argument: ", argv[i], " ", argv[i+1], "\n", usage);
} }
goto next_flag; goto next_flag;
} }
@ -353,7 +352,7 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help,
say(help, true); say(help, true);
exit(0); exit(0);
} }
errx(1, "Unrecognized flag: -%c\n%k", *f, &usage); print_err("Unrecognized flag: ", flag, "\n", usage);
next_flag:; next_flag:;
} }
} else { } else {
@ -380,7 +379,7 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help,
next_non_bool_flag: next_non_bool_flag:
++s; ++s;
if (s >= spec_len) if (s >= spec_len)
errx(1, "Extra argument: %s\n%k", argv[i], &usage); print_err("Extra argument: ", argv[i], "\n", usage);
} }
const TypeInfo_t *non_opt_type = spec[s].type; const TypeInfo_t *non_opt_type = spec[s].type;
@ -416,7 +415,7 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help,
} }
if (!populated_args[s]) if (!populated_args[s])
errx(1, "Invalid value for %s: %s\n%k", spec[s].name, argv[i], &usage); print_err("Invalid value for ", spec[s].name, ": ", argv[i], "\n", usage);
} }
for (int s = 0; s < spec_len; s++) { for (int s = 0; s < spec_len; s++) {
@ -426,7 +425,7 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help,
else if (spec[s].type->tag == TableInfo) else if (spec[s].type->tag == TableInfo)
*(OptionalTable_t*)spec[s].dest = (Table_t){}; *(OptionalTable_t*)spec[s].dest = (Table_t){};
else else
errx(1, "The required argument '%s' was not provided\n%k", spec[s].name, &usage); print_err("The required argument '", spec[s].name, "' was not provided\n", usage);
} }
} }
} }
@ -438,14 +437,17 @@ static void print_stack_line(FILE *out, OptionalText_t fn_name, const char *file
// do a linear scan through the whole file. However, performance shouldn't // do a linear scan through the whole file. However, performance shouldn't
// really matter if we only print stack lines when there's a crash. // really matter if we only print stack lines when there's a crash.
if (filename) { if (filename) {
fprintf(out, "\033[34mFile\033[m \033[35;1m%s\033[m", filename); fprint_inline(out, "\033[34mFile\033[m \033[35;1m", filename, "\033[m");
if (line_num >= 1) if (line_num >= 1)
fprintf(out, "\033[34m line\033[m \033[35;1m%ld\033[m", line_num); fprint_inline(out, "\033[34m line\033[m \033[35;1m", line_num, "\033[m");
} }
if (fn_name.length > 0) { if (fn_name.length > 0) {
fprintf(out, filename ? "\033[34m, in \033[m \033[36;1m%k\033[m" : "\033[36;1m%k\033[m", &fn_name); if (filename)
fprint_inline(out, "\033[34m, in \033[m \033[36;1m", fn_name, "\033[m");
else
fprint_inline(out, "\033[36;1m", fn_name, "\033[m");
} }
fprintf(out, "\n"); fprint_inline(out, "\n");
FILE *f = fopen(filename, "r"); FILE *f = fopen(filename, "r");
if (!f) return; if (!f) return;
@ -458,7 +460,7 @@ static void print_stack_line(FILE *out, OptionalText_t fn_name, const char *file
line[strlen(line)-1] = '\0'; line[strlen(line)-1] = '\0';
if (cur_line >= line_num) if (cur_line >= line_num)
fprintf(out, "\033[33;1m%s\033[m\n", line); fprint(out, "\033[33;1m", line, "\033[m");
cur_line += 1; cur_line += 1;
if (cur_line > line_num) if (cur_line > line_num)
@ -468,7 +470,7 @@ static void print_stack_line(FILE *out, OptionalText_t fn_name, const char *file
fclose(f); fclose(f);
} }
void print_stack_trace(FILE *out, int start, int stop) public void print_stack_trace(FILE *out, int start, int stop)
{ {
// Print stack trace: // Print stack trace:
void *stack[1024]; void *stack[1024];
@ -498,52 +500,9 @@ void print_stack_trace(FILE *out, int start, int stop)
} }
} }
__attribute__((format(printf, 1, 2)))
public _Noreturn void fail(const char *fmt, ...)
{
fflush(stdout);
if (USE_COLOR) fputs("\x1b[31;7m ==================== ERROR ==================== \n\n\x1b[0;1m", stderr);
else fputs("==================== ERROR ====================\n\n", stderr);
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
if (USE_COLOR) fputs("\x1b[m", stderr);
fputs("\n\n", stderr);
va_end(args);
print_stack_trace(stderr, 2, 4);
fflush(stderr);
raise(SIGABRT);
_exit(1);
}
public _Noreturn void fail_text(Text_t message) public _Noreturn void fail_text(Text_t message)
{ {
fail("%k", &message); fail(message);
}
__attribute__((format(printf, 4, 5)))
public _Noreturn void fail_source(const char *filename, int64_t start, int64_t end, const char *fmt, ...)
{
if (USE_COLOR) fputs("\n\x1b[31;7m ==================== ERROR ==================== \n\n\x1b[0;1m", stderr);
else fputs("\n==================== ERROR ====================\n\n", stderr);
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
file_t *file = filename ? load_file(filename) : NULL;
if (filename && file) {
fputs("\n", stderr);
highlight_error(file, file->text+start, file->text+end, "\x1b[31;1m", 2, USE_COLOR);
fputs("\n", stderr);
}
if (USE_COLOR) fputs("\x1b[m", stderr);
print_stack_trace(stderr, 2, 4);
fflush(stderr);
raise(SIGABRT);
_exit(1);
} }
public Text_t builtin_last_err() public Text_t builtin_last_err()
@ -595,9 +554,8 @@ public void end_inspect(const void *expr, const TypeInfo_t *type)
if (type->metamethods.as_text) { if (type->metamethods.as_text) {
Text_t expr_text = generic_as_text(expr, USE_COLOR, type); Text_t expr_text = generic_as_text(expr, USE_COLOR, type);
Text_t type_name = generic_as_text(NULL, false, type); Text_t type_name = generic_as_text(NULL, false, type);
for (int i = 0; i < 3*_inspect_depth; i++) fputc(' ', stdout); for (int i = 0; i < 3*_inspect_depth; i++) fputc(' ', stdout);
fprintf(stdout, USE_COLOR ? "\x1b[33;1m=\x1b[0m %k \x1b[2m: \x1b[36m%k\x1b[m\n" : "= %k : %k\n", &expr_text, &type_name); fprint(stdout, USE_COLOR ? "\x1b[33;1m=\x1b[0m " : " =", expr_text, USE_COLOR ? " \x1b[2m: \x1b[36m" : " : ", type_name);
} }
} }
@ -610,11 +568,17 @@ public void test_value(const void *expr, const void *expected, const TypeInfo_t
bool success = Text$equal_values(expr_text, expected_text); bool success = Text$equal_values(expr_text, expected_text);
if (!success) { if (!success) {
print_stack_trace(stderr, 2, 4); print_stack_trace(stderr, 2, 4);
fprintf(stderr, if (USE_COLOR) {
USE_COLOR fprint(stderr,
? "\n\x1b[31;7m ==================== TEST FAILED ==================== \x1b[0;1m\n\nYou expected: \x1b[m%k\x1b[0m\n\x1b[1m But I got:\x1b[m %k\n\n" "\n\x1b[31;7m ==================== TEST FAILED ==================== \x1b[0;1m\n\n"
: "\n==================== TEST FAILED ====================\n\nYou expected: %k\n But I got: %k\n\n", "You expected: \x1b[m", expected_text, "\x1b[0m\n"
&expected_text, &expr_text); "\x1b[1m But I got:\x1b[m ", expr_text, "\n");
} else {
fprint(stderr,
"\n==================== TEST FAILED ====================\n\n"
"You expected: ", expected_text, "\n"
" But I got: ", expr_text, "\n");
}
fflush(stderr); fflush(stderr);
raise(SIGABRT); raise(SIGABRT);

View File

@ -7,6 +7,8 @@
#include <stdio.h> #include <stdio.h>
#include "datatypes.h" #include "datatypes.h"
#include "files.h"
#include "print.h"
#include "types.h" #include "types.h"
#include "util.h" #include "util.h"
@ -23,11 +25,42 @@ void tomo_init(void);
void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, int spec_len, cli_arg_t spec[spec_len]); void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, int spec_len, cli_arg_t spec[spec_len]);
#define tomo_parse_args(argc, argv, usage, help, ...) \ #define tomo_parse_args(argc, argv, usage, help, ...) \
_tomo_parse_args(argc, argv, usage, help, sizeof((cli_arg_t[]){__VA_ARGS__})/sizeof(cli_arg_t), (cli_arg_t[]){__VA_ARGS__}) _tomo_parse_args(argc, argv, usage, help, sizeof((cli_arg_t[]){__VA_ARGS__})/sizeof(cli_arg_t), (cli_arg_t[]){__VA_ARGS__})
__attribute__((format(printf, 1, 2)))
_Noreturn void fail(const char *fmt, ...); #define fail(...) ({ \
fflush(stdout); \
if (USE_COLOR) fputs("\x1b[31;7m ==================== ERROR ==================== \n\n\x1b[0;1m", stderr); \
else fputs("==================== ERROR ====================\n\n", stderr); \
fprint_inline(stderr, __VA_ARGS__); \
if (USE_COLOR) fputs("\x1b[m", stderr); \
fputs("\n\n", stderr); \
print_stack_trace(stderr, 2, 4); \
fflush(stderr); \
raise(SIGABRT); \
_exit(1); \
})
#define fail_source(filename, start, end, ...) ({ \
fflush(stdout); \
if (USE_COLOR) fputs("\x1b[31;7m ==================== ERROR ==================== \n\n\x1b[0;1m", stderr); \
else fputs("==================== ERROR ====================\n\n", stderr); \
fprint_inline(stderr, __VA_ARGS__); \
if (USE_COLOR) fputs("\x1b[m", stderr); \
file_t *_file = (filename) ? load_file(filename) : NULL; \
if ((filename) && _file) { \
fputs("\n", stderr); \
highlight_error(_file, _file->text+(start), _file->text+(end), "\x1b[31;1m", 2, USE_COLOR); \
fputs("\n", stderr); \
} \
if (USE_COLOR) fputs("\x1b[m", stderr); \
print_stack_trace(stderr, 2, 4); \
fputs("\n\n", stderr); \
print_stack_trace(stderr, 2, 4); \
fflush(stderr); \
raise(SIGABRT); \
_exit(1); \
})
_Noreturn void fail_text(Text_t message); _Noreturn void fail_text(Text_t message);
__attribute__((format(printf, 4, 5)))
_Noreturn void fail_source(const char *filename, int64_t start, int64_t end, const char *fmt, ...);
Text_t builtin_last_err(); Text_t builtin_last_err();
__attribute__((nonnull)) __attribute__((nonnull))
void start_inspect(const char *filename, int64_t start, int64_t end); void start_inspect(const char *filename, int64_t start, int64_t end);

View File

@ -55,7 +55,6 @@
#include <assert.h> #include <assert.h>
#include <ctype.h> #include <ctype.h>
#include <gc.h> #include <gc.h>
#include <printf.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
@ -681,8 +680,7 @@ public PUREFUNC Text_t Text$cluster(Text_t text, Int_t index_int)
if (index < 0) index = text.length + index + 1; if (index < 0) index = text.length + index + 1;
if (index > text.length || index < 1) if (index > text.length || index < 1)
fail("Invalid index: %ld is beyond the length of the text (length = %ld)", fail("Invalid index: ", index_int, " is beyond the length of the text (length = ", (int64_t)text.length, ")");
Int64$from_int(index_int, false), text.length);
while (text.tag == TEXT_CONCAT) { while (text.tag == TEXT_CONCAT) {
if (index <= text.left->length) if (index <= text.left->length)
@ -1104,24 +1102,6 @@ public Text_t Text$title(Text_t text, Text_t language)
return ret; return ret;
} }
public int printf_text_size(const struct printf_info *info, size_t n, int argtypes[n], int sizes[n])
{
if (n < 1) return -1;
(void)info;
argtypes[0] = PA_POINTER;
sizes[0] = sizeof(Text_t);
return 1;
}
public int printf_text(FILE *stream, const struct printf_info *info, const void *const args[])
{
Text_t *t = *(Text_t**)args[0];
if (info->alt)
return text_visualize(stream, *t, 0);
else
return Text$print(stream, *t);
}
static INLINE Text_t _quoted(Text_t text, bool colorize, char quote_char) static INLINE Text_t _quoted(Text_t text, bool colorize, char quote_char)
{ {
Text_t ret = colorize ? Text("\x1b[35m") : EMPTY_TEXT; Text_t ret = colorize ? Text("\x1b[35m") : EMPTY_TEXT;
@ -1273,13 +1253,14 @@ public Text_t Text$format(const char *fmt, ...)
{ {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
int len = vsnprintf(NULL, 0, fmt, args);
char buf[9];
int len = vsnprintf(buf, sizeof(buf), fmt, args);
char *str = GC_MALLOC_ATOMIC((size_t)(len+1));
vsnprintf(str, (size_t)(len+1), fmt, args);
Text_t ret = Text$from_str(str);
va_end(args); va_end(args);
char *str = GC_MALLOC_ATOMIC((size_t)(len+1));
va_start(args, fmt);
vsnprintf(str, (size_t)(len+1), fmt, args);
va_end(args);
Text_t ret = Text$from_strn(str, (size_t)len);
return ret; return ret;
} }

View File

@ -4,7 +4,6 @@
// Raku's string representation and libunistr // Raku's string representation and libunistr
#include <stdbool.h> #include <stdbool.h>
#include <printf.h>
#include <stdint.h> #include <stdint.h>
#include "datatypes.h" #include "datatypes.h"
@ -25,12 +24,9 @@ typedef struct {
#define NEW_TEXT_ITER_STATE(t) (TextIter_t){.stack={{t, 0}}, .stack_index=0} #define NEW_TEXT_ITER_STATE(t) (TextIter_t){.stack={{t, 0}}, .stack_index=0}
int printf_text(FILE *stream, const struct printf_info *info, const void *const args[]);
int printf_text_size(const struct printf_info *info, size_t n, int argtypes[n], int sizes[n]);
#define Text(str) ((Text_t){.length=sizeof(str)-1, .tag=TEXT_ASCII, .ascii="" str}) #define Text(str) ((Text_t){.length=sizeof(str)-1, .tag=TEXT_ASCII, .ascii="" str})
int Text$print(FILE *stream, Text_t t); //int Text$print(FILE *stream, Text_t t);
Text_t Text$_concat(int n, Text_t items[n]); Text_t Text$_concat(int n, Text_t items[n]);
#define Text$concat(...) Text$_concat(sizeof((Text_t[]){__VA_ARGS__})/sizeof(Text_t), (Text_t[]){__VA_ARGS__}) #define Text$concat(...) Text$_concat(sizeof((Text_t[]){__VA_ARGS__})/sizeof(Text_t), (Text_t[]){__VA_ARGS__})
#define Texts(...) Text$concat(__VA_ARGS__) #define Texts(...) Text$concat(__VA_ARGS__)

View File

@ -4,7 +4,6 @@
#include <gc.h> #include <gc.h>
#include <gc/cord.h> #include <gc/cord.h>
#include <libgen.h> #include <libgen.h>
#include <printf.h>
#include <spawn.h> #include <spawn.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -24,12 +23,13 @@
#include "stdlib/optionals.h" #include "stdlib/optionals.h"
#include "stdlib/patterns.h" #include "stdlib/patterns.h"
#include "stdlib/paths.h" #include "stdlib/paths.h"
#include "stdlib/print.h"
#include "stdlib/text.h" #include "stdlib/text.h"
#include "typecheck.h" #include "typecheck.h"
#include "types.h" #include "types.h"
#define run_cmd(...) ({ const char *_cmd = heap_strf(__VA_ARGS__); if (verbose) printf("\033[34;1m%s\033[m\n", _cmd); popen(_cmd, "w"); }) #define run_cmd(...) ({ const char *_cmd = String(__VA_ARGS__); if (verbose) print("\033[34;1m", _cmd, "\033[m"); popen(_cmd, "w"); })
#define array_str(arr) Text$as_c_string(Text$join(Text(" "), arr)) #define array_text(arr) Text$join(Text(" "), arr)
static struct stat compiler_stat; static struct stat compiler_stat;
@ -100,20 +100,13 @@ int main(int argc, char *argv[])
if (getenv("NO_COLOR") && getenv("NO_COLOR")[0] != '\0') if (getenv("NO_COLOR") && getenv("NO_COLOR")[0] != '\0')
USE_COLOR = false; USE_COLOR = false;
if (register_printf_specifier('T', printf_type, printf_pointer_size))
errx(1, "Couldn't set printf specifier");
if (register_printf_specifier('W', printf_ast, printf_pointer_size))
errx(1, "Couldn't set printf specifier");
if (register_printf_specifier('k', printf_text, printf_text_size))
errx(1, "Couldn't set printf specifier");
// Run a tool: // Run a tool:
if ((streq(argv[1], "-r") || streq(argv[1], "--run")) && argc >= 3) { if ((streq(argv[1], "-r") || streq(argv[1], "--run")) && argc >= 3) {
if (strcspn(argv[2], "/;$") == strlen(argv[2])) { if (strcspn(argv[2], "/;$") == strlen(argv[2])) {
const char *program = heap_strf("%s/.local/share/tomo/installed/%s/%s", getenv("HOME"), argv[2], argv[2]); const char *program = String(getenv("HOME"), "/.local/share/tomo/installed/", argv[2], "/", argv[2]);
execv(program, &argv[2]); execv(program, &argv[2]);
} }
errx(1, "This is not an installed tomo program: \033[31;1m%s\033[m", argv[2]); print_err("This is not an installed tomo program: \033[31;1m", argv[2], "\033[m");
} }
Text_t usage = Text("\x1b[33;4;1mUsage:\x1b[m\n" Text_t usage = Text("\x1b[33;4;1mUsage:\x1b[m\n"
@ -168,8 +161,8 @@ int main(int argc, char *argv[])
for (int64_t i = 0; i < uninstall.length; i++) { for (int64_t i = 0; i < uninstall.length; i++) {
Text_t *u = (Text_t*)(uninstall.data + i*uninstall.stride); Text_t *u = (Text_t*)(uninstall.data + i*uninstall.stride);
system(heap_strf("rm -rvf ~/.local/share/tomo/installed/%k ~/.local/share/tomo/lib/lib%k.so", u, u)); system(String("rm -rvf ~/.local/share/tomo/installed/", *u, " ~/.local/share/tomo/lib/lib", *u, ".so"));
printf("Uninstalled %k\n", u); print("Uninstalled ", *u);
} }
for (int64_t i = 0; i < libraries.length; i++) { for (int64_t i = 0; i < libraries.length; i++) {
@ -177,7 +170,7 @@ int main(int argc, char *argv[])
const char *lib_str = Path$as_c_string(*lib); const char *lib_str = Path$as_c_string(*lib);
char *cwd = get_current_dir_name(); char *cwd = get_current_dir_name();
if (chdir(lib_str) != 0) if (chdir(lib_str) != 0)
errx(1, "Could not enter directory: %s", lib_str); print_err("Could not enter directory: ", lib_str);
char *libdir = get_current_dir_name(); char *libdir = get_current_dir_name();
char *libdirname = basename(libdir); char *libdirname = basename(libdir);
@ -205,9 +198,9 @@ int main(int argc, char *argv[])
} }
if (files.length < 1) if (files.length < 1)
errx(1, "No file specified!"); print_err("No file specified!");
else if (files.length != 1) else if (files.length != 1)
errx(1, "Too many files specified!"); print_err("Too many files specified!");
quiet = !verbose; quiet = !verbose;
@ -233,7 +226,7 @@ int main(int argc, char *argv[])
prog_args[j + 1] = Text$as_c_string(*(Text_t*)(args.data + j*args.stride)); prog_args[j + 1] = Text$as_c_string(*(Text_t*)(args.data + j*args.stride));
prog_args[1 + args.length] = NULL; prog_args[1 + args.length] = NULL;
execv(prog_args[0], prog_args); execv(prog_args[0], prog_args);
err(1, "Could not execute program: %s", prog_args[0]); print_err("Could not execute program: ", prog_args[0]);
} }
int status; int status;
@ -253,7 +246,7 @@ int main(int argc, char *argv[])
for (int64_t i = 0; i < files.length; i++) { for (int64_t i = 0; i < files.length; i++) {
Path_t path = *(Path_t*)(files.data + i*files.stride); Path_t path = *(Path_t*)(files.data + i*files.stride);
Path_t exe = Path$with_extension(path, Text(""), true); Path_t exe = Path$with_extension(path, Text(""), true);
system(heap_strf("cp -v '%s' ~/.local/bin/", Path$as_c_string(exe))); system(String("cp -v '", exe, "' ~/.local/bin/"));
} }
} }
return 0; return 0;
@ -332,7 +325,7 @@ static void _compile_file_header_for_library(env_t *env, Path_t path, Table_t *v
Table$set(visited_files, &path, ((Bool_t[1]){1}), Table$info(&Path$info, &Bool$info)); Table$set(visited_files, &path, ((Bool_t[1]){1}), Table$info(&Path$info, &Bool$info));
ast_t *file_ast = parse_file(Path$as_c_string(path), NULL); ast_t *file_ast = parse_file(Path$as_c_string(path), NULL);
if (!file_ast) errx(1, "Could not parse file %s", Path$as_c_string(path)); if (!file_ast) print_err("Could not parse file ", path);
env_t *module_env = load_module_env(env, file_ast); env_t *module_env = load_module_env(env, file_ast);
libheader_info_t info = { libheader_info_t info = {
@ -376,7 +369,7 @@ void build_library(Text_t lib_dir_name)
env->libname = Text$as_c_string(escape_lib_name(lib_dir_name)); env->libname = Text$as_c_string(escape_lib_name(lib_dir_name));
// Build a "whatever.h" header that loads all the headers: // Build a "whatever.h" header that loads all the headers:
FILE *header = fopen(heap_strf("%k.h", &lib_dir_name), "w"); FILE *header = fopen(String(lib_dir_name, ".h"), "w");
fputs("#pragma once\n", header); fputs("#pragma once\n", header);
fputs("#include <tomo/tomo.h>\n", header); fputs("#include <tomo/tomo.h>\n", header);
Table_t visited_files = {}; Table_t visited_files = {};
@ -387,39 +380,39 @@ void build_library(Text_t lib_dir_name)
_compile_file_header_for_library(env, resolved, &visited_files, &used_imports, header); _compile_file_header_for_library(env, resolved, &visited_files, &used_imports, header);
} }
if (fclose(header) == -1) if (fclose(header) == -1)
errx(1, "Failed to write header file: %k.h", &lib_dir_name); print_err("Failed to write header file: ", lib_dir_name, ".h");
// Build up a list of symbol renamings: // Build up a list of symbol renamings:
unlink(".build/symbol_renames.txt"); unlink(".build/symbol_renames.txt");
FILE *prog; FILE *prog;
for (int64_t i = 0; i < tm_files.length; i++) { for (int64_t i = 0; i < tm_files.length; i++) {
Path_t f = *(Path_t*)(tm_files.data + i*tm_files.stride); Path_t f = *(Path_t*)(tm_files.data + i*tm_files.stride);
prog = run_cmd("nm -Ug -fjust-symbols '%s' | sed -n 's/_\\$\\(.*\\)/\\0 _$%s$\\1/p' >>.build/symbol_renames.txt", prog = run_cmd("nm -Ug -fjust-symbols '", build_file(f, ".o"), "' "
Path$as_c_string(build_file(f, ".o")), CORD_to_const_char_star(env->libname)); "| sed -n 's/_\\$\\(.*\\)/\\0 _$", CORD_to_const_char_star(env->libname),
if (!prog) errx(1, "Could not find symbols!"); "$\\1/p' >>.build/symbol_renames.txt");
if (!prog) print_err("Could not find symbols!");
int status = pclose(prog); int status = pclose(prog);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
errx(WEXITSTATUS(status), "Failed to create symbol rename table with `nm` and `sed`"); errx(WEXITSTATUS(status), "Failed to create symbol rename table with `nm` and `sed`");
} }
prog = run_cmd("%k -O%k %k %k %k %s -Wl,-soname='lib%k.so' -shared %s -o 'lib%k.so'", prog = run_cmd(cc, " -O", optimization, " ", cflags, " ", ldflags, " ", ldlibs, " ", array_text(extra_ldlibs),
&cc, &optimization, &cflags, &ldflags, &ldlibs, array_str(extra_ldlibs), &lib_dir_name, " -Wl,-soname='lib", lib_dir_name, ".so' -shared ", paths_str(object_files), " -o 'lib", lib_dir_name, ".so'");
paths_str(object_files), &lib_dir_name);
if (!prog) if (!prog)
errx(1, "Failed to run C compiler: %k", &cc); print_err("Failed to run C compiler: ", cc);
int status = pclose(prog); int status = pclose(prog);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
if (!quiet) if (!quiet)
printf("Compiled library:\tlib%k.so\n", &lib_dir_name); print("Compiled library:\tlib", lib_dir_name, ".so");
prog = run_cmd("objcopy --redefine-syms=.build/symbol_renames.txt 'lib%k.so'", &lib_dir_name); prog = run_cmd("objcopy --redefine-syms=.build/symbol_renames.txt 'lib", lib_dir_name, ".so'");
status = pclose(prog); status = pclose(prog);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
errx(WEXITSTATUS(status), "Failed to run `objcopy` to add library prefix to symbols"); errx(WEXITSTATUS(status), "Failed to run `objcopy` to add library prefix to symbols");
prog = run_cmd("patchelf --rename-dynamic-symbols .build/symbol_renames.txt 'lib%k.so'", &lib_dir_name); prog = run_cmd("patchelf --rename-dynamic-symbols .build/symbol_renames.txt 'lib", lib_dir_name, ".so'");
status = pclose(prog); status = pclose(prog);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
errx(WEXITSTATUS(status), "Failed to run `patchelf` to rename dynamic symbols with library prefix"); errx(WEXITSTATUS(status), "Failed to run `patchelf` to rename dynamic symbols with library prefix");
@ -431,16 +424,16 @@ void build_library(Text_t lib_dir_name)
if (should_install) { if (should_install) {
char *library_directory = get_current_dir_name(); char *library_directory = get_current_dir_name();
const char *dest = heap_strf("%s/.local/share/tomo/installed/%k", getenv("HOME"), &lib_dir_name); const char *dest = String(getenv("HOME"), "/.local/share/tomo/installed/", lib_dir_name);
if (!streq(library_directory, dest)) { if (!streq(library_directory, dest)) {
system(heap_strf("rm -rf '%s'", dest)); system(String("rm -rf '", dest, "'"));
system(heap_strf("mkdir -p '%s'", dest)); system(String("mkdir -p '", dest, "'"));
system(heap_strf("cp -r * '%s/'", dest)); system(String("cp -r * '", dest, "/'"));
} }
system("mkdir -p ~/.local/share/tomo/lib/"); system("mkdir -p ~/.local/share/tomo/lib/");
system(heap_strf("ln -f -s ../installed/'%k'/lib'%k'.so ~/.local/share/tomo/lib/lib'%k'.so", system(String("ln -f -s ../installed/'", lib_dir_name, "'/lib'", lib_dir_name,
&lib_dir_name, &lib_dir_name, &lib_dir_name)); "'.so ~/.local/share/tomo/lib/lib'", lib_dir_name, "'.so"));
printf("Installed \033[1m%k\033[m to ~/.local/share/tomo/installed\n", &lib_dir_name); print("Installed \033[1m", lib_dir_name, "\033[m to ~/.local/share/tomo/installed");
free(library_directory); free(library_directory);
} }
} }
@ -453,10 +446,10 @@ void compile_files(env_t *env, Array_t to_compile, Array_t *object_files, Array_
Path_t filename = *(Path_t*)(to_compile.data + i*to_compile.stride); Path_t filename = *(Path_t*)(to_compile.data + i*to_compile.stride);
Text_t extension = Path$extension(filename, true); Text_t extension = Path$extension(filename, true);
if (!Text$equal_values(extension, Text("tm"))) if (!Text$equal_values(extension, Text("tm")))
errx(1, "Not a valid .tm file: \x1b[31;1m%s\x1b[m", Path$as_c_string(filename)); print_err("Not a valid .tm file: \x1b[31;1m", filename, "\x1b[m");
Path_t resolved = Path$resolved(filename, Path("./")); Path_t resolved = Path$resolved(filename, Path("./"));
if (!Path$is_file(resolved, true)) if (!Path$is_file(resolved, true))
errx(1, "Couldn't find file: %s", Path$as_c_string(resolved)); print_err("Couldn't find file: ", resolved);
build_file_dependency_graph(resolved, &dependency_files, &to_link); build_file_dependency_graph(resolved, &dependency_files, &to_link);
} }
@ -542,7 +535,7 @@ void build_file_dependency_graph(Path_t path, Table_t *to_compile, Table_t *to_l
ast_t *ast = parse_file(Path$as_c_string(path), NULL); ast_t *ast = parse_file(Path$as_c_string(path), NULL);
if (!ast) if (!ast)
errx(1, "Could not parse file %s", Path$as_c_string(path)); print_err("Could not parse file: ", path);
for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) { for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) {
ast_t *stmt_ast = stmt->ast; ast_t *stmt_ast = stmt->ast;
@ -569,7 +562,7 @@ void build_file_dependency_graph(Path_t path, Table_t *to_compile, Table_t *to_l
Text_t lib = Text$format("'%s/.local/share/tomo/installed/%s/lib%s.so'", getenv("HOME"), use->path, use->path); Text_t lib = Text$format("'%s/.local/share/tomo/installed/%s/lib%s.so'", getenv("HOME"), use->path, use->path);
Table$set(to_link, &lib, ((Bool_t[1]){1}), Table$info(&Text$info, &Bool$info)); Table$set(to_link, &lib, ((Bool_t[1]){1}), Table$info(&Text$info, &Bool$info));
Array_t children = Path$glob(Path$from_str(heap_strf("%s/.local/share/tomo/installed/%s/*.tm", getenv("HOME"), use->path))); Array_t children = Path$glob(Path$from_str(String(getenv("HOME"), "/.local/share/tomo/installed/", use->path, "/*.tm")));
for (int64_t i = 0; i < children.length; i++) { for (int64_t i = 0; i < children.length; i++) {
Path_t *child = (Path_t*)(children.data + i*children.stride); Path_t *child = (Path_t*)(children.data + i*children.stride);
Table_t discarded = {.fallback=to_compile}; Table_t discarded = {.fallback=to_compile};
@ -604,7 +597,7 @@ bool is_stale(Path_t path, Path_t relative_to)
struct stat relative_to_stat; struct stat relative_to_stat;
if (stat(Path$as_c_string(relative_to), &relative_to_stat) != 0) if (stat(Path$as_c_string(relative_to), &relative_to_stat) != 0)
errx(1, "File doesn't exist: %s", Path$as_c_string(relative_to)); print_err("File doesn't exist: ", relative_to);
return target_stat.st_mtime < relative_to_stat.st_mtime; return target_stat.st_mtime < relative_to_stat.st_mtime;
} }
@ -613,7 +606,7 @@ void transpile_header(env_t *base_env, Path_t path)
Path_t h_filename = build_file(path, ".h"); Path_t h_filename = build_file(path, ".h");
ast_t *ast = parse_file(Path$as_c_string(path), NULL); ast_t *ast = parse_file(Path$as_c_string(path), NULL);
if (!ast) if (!ast)
errx(1, "Could not parse file %s", Path$as_c_string(path)); print_err("Could not parse file: ", path);
env_t *module_env = load_module_env(base_env, ast); env_t *module_env = load_module_env(base_env, ast);
@ -621,16 +614,16 @@ void transpile_header(env_t *base_env, Path_t path)
FILE *header = fopen(Path$as_c_string(h_filename), "w"); FILE *header = fopen(Path$as_c_string(h_filename), "w");
if (!header) if (!header)
errx(1, "Failed to open header file: %s", Path$as_c_string(h_filename)); print_err("Failed to open header file: ", h_filename);
CORD_put(h_code, header); CORD_put(h_code, header);
if (fclose(header) == -1) if (fclose(header) == -1)
errx(1, "Failed to write header file: %s", Path$as_c_string(h_filename)); print_err("Failed to write header file: ", h_filename);
if (!quiet) if (!quiet)
printf("Transpiled header:\t%s\n", Path$as_c_string(h_filename)); print("Transpiled header:\t", h_filename);
if (show_codegen.length > 0) if (show_codegen.length > 0)
system(heap_strf("<%s %k", Path$as_c_string(h_filename), &show_codegen)); system(String(show_codegen, " <", h_filename));
} }
void transpile_code(env_t *base_env, Path_t path) void transpile_code(env_t *base_env, Path_t path)
@ -638,7 +631,7 @@ void transpile_code(env_t *base_env, Path_t path)
Path_t c_filename = build_file(path, ".c"); Path_t c_filename = build_file(path, ".c");
ast_t *ast = parse_file(Path$as_c_string(path), NULL); ast_t *ast = parse_file(Path$as_c_string(path), NULL);
if (!ast) if (!ast)
errx(1, "Could not parse file %s", Path$as_c_string(path)); print_err("Could not parse file: ", path);
env_t *module_env = load_module_env(base_env, ast); env_t *module_env = load_module_env(base_env, ast);
@ -646,7 +639,7 @@ void transpile_code(env_t *base_env, Path_t path)
FILE *c_file = fopen(Path$as_c_string(c_filename), "w"); FILE *c_file = fopen(Path$as_c_string(c_filename), "w");
if (!c_file) if (!c_file)
errx(1, "Failed to write C file: %s", Path$as_c_string(c_filename)); print_err("Failed to write C file: ", c_filename);
CORD_put(c_code, c_file); CORD_put(c_code, c_file);
@ -654,7 +647,9 @@ void transpile_code(env_t *base_env, Path_t path)
if (main_binding && main_binding->type->tag == FunctionType) { if (main_binding && main_binding->type->tag == FunctionType) {
type_t *ret = Match(main_binding->type, FunctionType)->ret; type_t *ret = Match(main_binding->type, FunctionType)->ret;
if (ret->tag != VoidType && ret->tag != AbortType) if (ret->tag != VoidType && ret->tag != AbortType)
compiler_err(ast->file, ast->start, ast->end, "The main() function in this file has a return type of %T, but it should not have any return value!", ret); compiler_err(ast->file, ast->start, ast->end,
"The main() function in this file has a return type of ", type_to_str(ret),
", but it should not have any return value!");
CORD_put(CORD_all( CORD_put(CORD_all(
"int ", main_binding->code, "$parse_and_run(int argc, char *argv[]) {\n" "int ", main_binding->code, "$parse_and_run(int argc, char *argv[]) {\n"
@ -668,13 +663,13 @@ void transpile_code(env_t *base_env, Path_t path)
} }
if (fclose(c_file) == -1) if (fclose(c_file) == -1)
errx(1, "Failed to output C code to %s", Path$as_c_string(c_filename)); print_err("Failed to output C code to ", c_filename);
if (!quiet) if (!quiet)
printf("Transpiled code:\t%s\n", Path$as_c_string(c_filename)); print("Transpiled code:\t", c_filename);
if (show_codegen.length > 0) if (show_codegen.length > 0)
system(heap_strf("<%s %k", Path$as_c_string(c_filename), &show_codegen)); system(String(show_codegen, " <", c_filename));
} }
void compile_object_file(Path_t path) void compile_object_file(Path_t path)
@ -682,31 +677,29 @@ void compile_object_file(Path_t path)
Path_t obj_file = build_file(path, ".o"); Path_t obj_file = build_file(path, ".o");
Path_t c_file = build_file(path, ".c"); Path_t c_file = build_file(path, ".c");
FILE *prog = run_cmd("%k %k -O%k -c %s -o %s", FILE *prog = run_cmd(cc, " ", cflags, " -O", optimization, " -c ", c_file, " -o ", obj_file);
&cc, &cflags, &optimization, Path$as_c_string(c_file), Path$as_c_string(obj_file));
if (!prog) if (!prog)
errx(1, "Failed to run C compiler: %k", &cc); print_err("Failed to run C compiler: ", cc);
int status = pclose(prog); int status = pclose(prog);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
if (!quiet) if (!quiet)
printf("Compiled object:\t%s\n", Path$as_c_string(obj_file)); print("Compiled object:\t", obj_file);
} }
Path_t compile_executable(env_t *base_env, Path_t path, Path_t exe_path, Array_t object_files, Array_t extra_ldlibs) Path_t compile_executable(env_t *base_env, Path_t path, Path_t exe_path, Array_t object_files, Array_t extra_ldlibs)
{ {
ast_t *ast = parse_file(Path$as_c_string(path), NULL); ast_t *ast = parse_file(Path$as_c_string(path), NULL);
if (!ast) if (!ast)
errx(1, "Could not parse file %s", Path$as_c_string(path)); print_err("Could not parse file ", path);
env_t *env = load_module_env(base_env, ast); env_t *env = load_module_env(base_env, ast);
binding_t *main_binding = get_binding(env, "main"); binding_t *main_binding = get_binding(env, "main");
if (!main_binding || main_binding->type->tag != FunctionType) if (!main_binding || main_binding->type->tag != FunctionType)
errx(1, "No main() function has been defined for %s, so it can't be run!", Path$as_c_string(path)); print_err("No main() function has been defined for ", path, ", so it can't be run!");
FILE *runner = run_cmd("%k %k -O%k %k %k %s %s -x c - -o %s", FILE *runner = run_cmd(cc, " ", cflags, " -O", optimization, " ", ldflags, " ", ldlibs, " ", array_text(extra_ldlibs), " ",
&cc, &cflags, &optimization, &ldflags, &ldlibs, paths_str(object_files), " -x c - -o ", exe_path);
array_str(extra_ldlibs), paths_str(object_files), Path$as_c_string(exe_path));
CORD program = CORD_all( CORD program = CORD_all(
"extern int ", main_binding->code, "$parse_and_run(int argc, char *argv[]);\n" "extern int ", main_binding->code, "$parse_and_run(int argc, char *argv[]);\n"
"int main(int argc, char *argv[]) {\n" "int main(int argc, char *argv[]) {\n"
@ -715,7 +708,7 @@ Path_t compile_executable(env_t *base_env, Path_t path, Path_t exe_path, Array_t
); );
if (show_codegen.length > 0) { if (show_codegen.length > 0) {
FILE *out = run_cmd("%k", &show_codegen); FILE *out = run_cmd(show_codegen);
CORD_put(program, out); CORD_put(program, out);
pclose(out); pclose(out);
} }
@ -726,7 +719,7 @@ Path_t compile_executable(env_t *base_env, Path_t path, Path_t exe_path, Array_t
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
if (!quiet) if (!quiet)
printf("Compiled executable:\t%s\n", Path$as_c_string(exe_path)); print("Compiled executable:\t", exe_path);
return exe_path; return exe_path;
} }

View File

@ -32,7 +32,7 @@ type_t *parse_type_ast(env_t *env, type_ast_t *ast)
char *module_name = GC_strndup(name, strcspn(name, ".")); char *module_name = GC_strndup(name, strcspn(name, "."));
binding_t *b = get_binding(env, module_name); binding_t *b = get_binding(env, module_name);
if (!b || b->type->tag != ModuleType) if (!b || b->type->tag != ModuleType)
code_err(ast, "I don't know a module with the name '%s'", module_name); code_err(ast, "I don't know a module with the name '", module_name, "'");
env_t *imported = Table$str_get(*env->imports, Match(b->type, ModuleType)->name); env_t *imported = Table$str_get(*env->imports, Match(b->type, ModuleType)->name);
assert(imported); assert(imported);
@ -41,7 +41,7 @@ type_t *parse_type_ast(env_t *env, type_ast_t *ast)
t = Table$str_get(*env->types, name); t = Table$str_get(*env->types, name);
if (t) return t; if (t) return t;
} }
code_err(ast, "I don't know a type with the name '%s'", name); code_err(ast, "I don't know a type with the name '", name, "'");
} }
case PointerTypeAST: { case PointerTypeAST: {
auto ptr = Match(ast, PointerTypeAST); auto ptr = Match(ast, PointerTypeAST);
@ -57,8 +57,8 @@ type_t *parse_type_ast(env_t *env, type_ast_t *ast)
if (has_stack_memory(item_t)) if (has_stack_memory(item_t))
code_err(item_type, "Arrays can't have stack references because the array may outlive the stack frame."); code_err(item_type, "Arrays can't have stack references because the array may outlive the stack frame.");
if (type_size(item_t) > ARRAY_MAX_STRIDE) if (type_size(item_t) > ARRAY_MAX_STRIDE)
code_err(ast, "This array holds items that take up %ld bytes, but the maximum supported size is %ld bytes. Consider using an array of pointers instead.", code_err(ast, "This array holds items that take up ", type_size(item_t),
type_size(item_t), ARRAY_MAX_STRIDE); " bytes, but the maximum supported size is ", ARRAY_MAX_STRIDE, " bytes. Consider using an array of pointers instead.");
return Type(ArrayType, .item_type=item_t); return Type(ArrayType, .item_type=item_t);
} }
case SetTypeAST: { case SetTypeAST: {
@ -68,8 +68,8 @@ type_t *parse_type_ast(env_t *env, type_ast_t *ast)
if (has_stack_memory(item_t)) if (has_stack_memory(item_t))
code_err(item_type, "Sets can't have stack references because the array may outlive the stack frame."); code_err(item_type, "Sets can't have stack references because the array may outlive the stack frame.");
if (type_size(item_t) > ARRAY_MAX_STRIDE) if (type_size(item_t) > ARRAY_MAX_STRIDE)
code_err(ast, "This set holds items that take up %ld bytes, but the maximum supported size is %ld bytes. Consider using an set of pointers instead.", code_err(ast, "This set holds items that take up ", type_size(item_t),
type_size(item_t), ARRAY_MAX_STRIDE); " bytes, but the maximum supported size is ", ARRAY_MAX_STRIDE, " bytes. Consider using an set of pointers instead.");
return Type(SetType, .item_type=item_t); return Type(SetType, .item_type=item_t);
} }
case TableTypeAST: { case TableTypeAST: {
@ -120,7 +120,7 @@ type_t *parse_type_ast(env_t *env, type_ast_t *ast)
auto opt = Match(ast, OptionalTypeAST); auto opt = Match(ast, OptionalTypeAST);
type_t *t = parse_type_ast(env, opt->type); type_t *t = parse_type_ast(env, opt->type);
if (t->tag == VoidType || t->tag == AbortType || t->tag == ReturnType) if (t->tag == VoidType || t->tag == AbortType || t->tag == ReturnType)
code_err(ast, "Optional %T types are not supported.", t); code_err(ast, "Optional ", type_to_str(t), " types are not supported.");
else if (t->tag == OptionalType) else if (t->tag == OptionalType)
code_err(ast, "Nested optional types are not currently supported"); code_err(ast, "Nested optional types are not currently supported");
return Type(OptionalType, .type=t); return Type(OptionalType, .type=t);
@ -129,7 +129,7 @@ type_t *parse_type_ast(env_t *env, type_ast_t *ast)
type_ast_t *mutexed = Match(ast, MutexedTypeAST)->type; type_ast_t *mutexed = Match(ast, MutexedTypeAST)->type;
type_t *t = parse_type_ast(env, mutexed); type_t *t = parse_type_ast(env, mutexed);
if (t->tag == VoidType || t->tag == AbortType || t->tag == ReturnType) if (t->tag == VoidType || t->tag == AbortType || t->tag == ReturnType)
code_err(ast, "Mutexed %T types are not supported.", t); code_err(ast, "Mutexed ", type_to_str(t), " types are not supported.");
return Type(MutexedType, .type=t); return Type(MutexedType, .type=t);
} }
case UnknownTypeAST: code_err(ast, "I don't know how to get this type"); case UnknownTypeAST: code_err(ast, "I don't know how to get this type");
@ -166,7 +166,7 @@ PUREFUNC type_t *get_math_type(env_t *env, ast_t *ast, type_t *lhs_t, type_t *rh
switch (compare_precision(lhs_t, rhs_t)) { switch (compare_precision(lhs_t, rhs_t)) {
case NUM_PRECISION_EQUAL: case NUM_PRECISION_MORE: return lhs_t; case NUM_PRECISION_EQUAL: case NUM_PRECISION_MORE: return lhs_t;
case NUM_PRECISION_LESS: return rhs_t; case NUM_PRECISION_LESS: return rhs_t;
default: code_err(ast, "Math operations between %T and %T are not supported", lhs_t, rhs_t); default: code_err(ast, "Math operations between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t), " are not supported");
} }
} }
@ -181,15 +181,15 @@ static env_t *load_module(env_t *env, ast_t *module_ast)
return module_env; return module_env;
if (!resolved_path) if (!resolved_path)
code_err(module_ast, "No such file exists: \"%s\"", use->path); code_err(module_ast, "No such file exists: ", quoted(use->path));
ast_t *ast = parse_file(resolved_path, NULL); ast_t *ast = parse_file(resolved_path, NULL);
if (!ast) errx(1, "Could not compile file %s", resolved_path); if (!ast) print_err("Could not compile file ", resolved_path);
return load_module_env(env, ast); return load_module_env(env, ast);
} }
case USE_MODULE: { case USE_MODULE: {
glob_t tm_files; glob_t tm_files;
if (glob(heap_strf("~/.local/share/tomo/installed/%s/[!._0-9]*.tm", use->path), GLOB_TILDE, NULL, &tm_files) != 0) if (glob(String("~/.local/share/tomo/installed/", use->path, "/[!._0-9]*.tm"), GLOB_TILDE, NULL, &tm_files) != 0)
code_err(module_ast, "Could not find library"); code_err(module_ast, "Could not find library");
env_t *module_env = fresh_scope(env); env_t *module_env = fresh_scope(env);
@ -200,7 +200,7 @@ static env_t *load_module(env_t *env, ast_t *module_ast)
for (size_t i = 0; i < tm_files.gl_pathc; i++) { for (size_t i = 0; i < tm_files.gl_pathc; i++) {
const char *filename = tm_files.gl_pathv[i]; const char *filename = tm_files.gl_pathv[i];
ast_t *ast = parse_file(filename, NULL); ast_t *ast = parse_file(filename, NULL);
if (!ast) errx(1, "Could not compile file %s", filename); if (!ast) print_err("Could not compile file ", filename);
env_t *module_file_env = fresh_scope(module_env); env_t *module_file_env = fresh_scope(module_env);
char *file_prefix = file_base_id(filename); char *file_prefix = file_base_id(filename);
module_file_env->namespace = new(namespace_t, .name=file_prefix); module_file_env->namespace = new(namespace_t, .name=file_prefix);
@ -229,7 +229,7 @@ void prebind_statement(env_t *env, ast_t *statement)
case StructDef: { case StructDef: {
auto def = Match(statement, StructDef); auto def = Match(statement, StructDef);
if (get_binding(env, def->name)) if (get_binding(env, def->name))
code_err(statement, "A %T called '%s' has already been defined", get_binding(env, def->name)->type, def->name); code_err(statement, "A ", type_to_str(get_binding(env, def->name)->type), " called ", quoted(def->name), " has already been defined");
env_t *ns_env = namespace_env(env, def->name); env_t *ns_env = namespace_env(env, def->name);
type_t *type = Type(StructType, .name=def->name, .opaque=true, .external=def->external, .env=ns_env); // placeholder type_t *type = Type(StructType, .name=def->name, .opaque=true, .external=def->external, .env=ns_env); // placeholder
@ -243,7 +243,7 @@ void prebind_statement(env_t *env, ast_t *statement)
case EnumDef: { case EnumDef: {
auto def = Match(statement, EnumDef); auto def = Match(statement, EnumDef);
if (get_binding(env, def->name)) if (get_binding(env, def->name))
code_err(statement, "A %T called '%s' has already been defined", get_binding(env, def->name)->type, def->name); code_err(statement, "A ", type_to_str(get_binding(env, def->name)->type), " called ", quoted(def->name), " has already been defined");
env_t *ns_env = namespace_env(env, def->name); env_t *ns_env = namespace_env(env, def->name);
type_t *type = Type(EnumType, .name=def->name, .opaque=true, .env=ns_env); // placeholder type_t *type = Type(EnumType, .name=def->name, .opaque=true, .env=ns_env); // placeholder
@ -257,7 +257,7 @@ void prebind_statement(env_t *env, ast_t *statement)
case LangDef: { case LangDef: {
auto def = Match(statement, LangDef); auto def = Match(statement, LangDef);
if (get_binding(env, def->name)) if (get_binding(env, def->name))
code_err(statement, "A %T called '%s' has already been defined", get_binding(env, def->name)->type, def->name); code_err(statement, "A ", type_to_str(get_binding(env, def->name)->type), " called ", quoted(def->name), " has already been defined");
env_t *ns_env = namespace_env(env, def->name); env_t *ns_env = namespace_env(env, def->name);
type_t *type = Type(TextType, .lang=def->name, .env=ns_env); type_t *type = Type(TextType, .lang=def->name, .env=ns_env);
@ -285,7 +285,7 @@ void bind_statement(env_t *env, ast_t *statement)
if (streq(name, "_")) // Explicit discard if (streq(name, "_")) // Explicit discard
return; return;
if (get_binding(env, name)) if (get_binding(env, name))
code_err(decl->var, "A %T called '%s' has already been defined", get_binding(env, name)->type, name); code_err(decl->var, "A ", type_to_str(get_binding(env, name)->type), " called ", quoted(name), " has already been defined");
bind_statement(env, decl->value); bind_statement(env, decl->value);
type_t *type = get_type(env, decl->value); type_t *type = get_type(env, decl->value);
if (!type) if (!type)
@ -301,9 +301,6 @@ void bind_statement(env_t *env, ast_t *statement)
auto def = Match(statement, FunctionDef); auto def = Match(statement, FunctionDef);
const char *name = Match(def->name, Var)->name; const char *name = Match(def->name, Var)->name;
type_t *type = get_function_def_type(env, statement); type_t *type = get_function_def_type(env, statement);
// binding_t *clobber = get_binding(env, name);
// if (clobber)
// code_err(def->name, "A %T called '%s' has already been defined", clobber->type, name);
CORD code = CORD_all(namespace_prefix(env, env->namespace), name); CORD code = CORD_all(namespace_prefix(env, env->namespace), name);
set_binding(env, name, type, code); set_binding(env, name, type, code);
break; break;
@ -313,7 +310,7 @@ void bind_statement(env_t *env, ast_t *statement)
type_t *ret_t = Match(type, FunctionType)->ret; type_t *ret_t = Match(type, FunctionType)->ret;
const char *name = get_type_name(ret_t); const char *name = get_type_name(ret_t);
if (!name) if (!name)
code_err(statement, "Conversions are only supported for text, struct, and enum types, not %T", ret_t); code_err(statement, "Conversions are only supported for text, struct, and enum types, not ", type_to_str(ret_t));
CORD code = CORD_asprintf("%r%r$%ld", namespace_prefix(env, env->namespace), name, CORD code = CORD_asprintf("%r%r$%ld", namespace_prefix(env, env->namespace), name,
get_line_number(statement->file, statement->start)); get_line_number(statement->file, statement->start));
@ -344,15 +341,14 @@ void bind_statement(env_t *env, ast_t *statement)
file = field_ast->value->file, start = field_ast->value->start, end = field_ast->value->end; file = field_ast->value->file, start = field_ast->value->start, end = field_ast->value->end;
} }
if (non_opt_field_t == type) if (non_opt_field_t == type)
compiler_err(file, start, end, "This is a recursive struct that would be infinitely large. Maybe you meant to use an optional '@%T?' pointer instead?", type); compiler_err(file, start, end, "This is a recursive struct that would be infinitely large. Maybe you meant to use an optional '@", type_to_str(type), "?' pointer instead?");
else if (non_opt_field_t->tag == StructType && Match(non_opt_field_t, StructType)->external) else if (non_opt_field_t->tag == StructType && Match(non_opt_field_t, StructType)->external)
compiler_err(file, start, end, "This is an opaque externally defined struct.\n" compiler_err(file, start, end, "This is an opaque externally defined struct.\n"
"I can't use it as a member without knowing what its fields are.\n" "I can't use it as a member without knowing what its fields are.\n"
"Either specify its fields and remove the `opaque` qualifier, or use something like a @%T pointer.", non_opt_field_t); "Either specify its fields and remove the `opaque` qualifier, or use something like a @", type_to_str(non_opt_field_t), " pointer.");
else else
compiler_err(file, start, end, "I'm still in the process of defining the fields of %T, so I don't know how to use it as a member." compiler_err(file, start, end, "I'm still in the process of defining the fields of ", type_to_str(field_t), ", so I don't know how to use it as a member."
"\nTry using a @%T pointer for this field.", "\nTry using a @", type_to_str(field_t), " pointer for this field.");
field_t, field_t);
} }
fields = new(arg_t, .name=field_ast->name, .type=field_t, .default_val=field_ast->value, .next=fields); fields = new(arg_t, .name=field_ast->name, .type=field_t, .default_val=field_ast->value, .next=fields);
} }
@ -387,21 +383,21 @@ void bind_statement(env_t *env, ast_t *statement)
file = field_ast->value->file, start = field_ast->value->start, end = field_ast->value->end; file = field_ast->value->file, start = field_ast->value->start, end = field_ast->value->end;
} }
if (non_opt_field_t == type) if (non_opt_field_t == type)
compiler_err(file, start, end, "This is a recursive enum that would be infinitely large. Maybe you meant to use an optional '@%T?' pointer instead?", type); compiler_err(file, start, end, "This is a recursive enum that would be infinitely large. Maybe you meant to use an optional '@", type_to_str(type), "?' pointer instead?");
else if (non_opt_field_t->tag == StructType && Match(non_opt_field_t, StructType)->external) else if (non_opt_field_t->tag == StructType && Match(non_opt_field_t, StructType)->external)
compiler_err(file, start, end, "This is an opaque externally defined struct.\n" compiler_err(file, start, end, "This is an opaque externally defined struct.\n"
"I can't use it as a member without knowing what its fields are.\n" "I can't use it as a member without knowing what its fields are.\n"
"Either specify its fields and remove the `opaque` qualifier, or use something like a @%T pointer.", non_opt_field_t); "Either specify its fields and remove the `opaque` qualifier, or use something like a @", type_to_str(non_opt_field_t), " pointer.");
else else
compiler_err(file, start, end, "I'm still in the process of defining the fields of %T, so I don't know how to use it as a member." compiler_err(file, start, end, "I'm still in the process of defining the fields of ", type_to_str(field_t),
"\nTry using a @%T pointer for this field.", ", so I don't know how to use it as a member."
field_t, field_t); "\nTry using a @", type_to_str(field_t), " pointer for this field.");
} }
fields = new(arg_t, .name=field_ast->name, .type=field_t, .default_val=field_ast->value, .next=fields); fields = new(arg_t, .name=field_ast->name, .type=field_t, .default_val=field_ast->value, .next=fields);
} }
REVERSE_LIST(fields); REVERSE_LIST(fields);
env_t *member_ns = namespace_env(env, heap_strf("%s$%s", def->name, tag_ast->name)); env_t *member_ns = namespace_env(env, String(def->name, "$", tag_ast->name));
type_t *tag_type = Type(StructType, .name=heap_strf("%s$%s", def->name, tag_ast->name), .fields=fields, .env=member_ns); type_t *tag_type = Type(StructType, .name=String(def->name, "$", tag_ast->name), .fields=fields, .env=member_ns);
tags = new(tag_t, .name=tag_ast->name, .tag_value=(next_tag++), .type=tag_type, .next=tags); tags = new(tag_t, .name=tag_ast->name, .tag_value=(next_tag++), .type=tag_type, .next=tags);
} }
REVERSE_LIST(tags); REVERSE_LIST(tags);
@ -416,7 +412,7 @@ void bind_statement(env_t *env, ast_t *statement)
CORD code = CORD_all("((", namespace_prefix(env, env->namespace), def->name, "$$type){", namespace_prefix(env, env->namespace), def->name, "$tag$", tag->name, "})"); CORD code = CORD_all("((", namespace_prefix(env, env->namespace), def->name, "$$type){", namespace_prefix(env, env->namespace), def->name, "$tag$", tag->name, "})");
set_binding(ns_env, tag->name, type, code); set_binding(ns_env, tag->name, type, code);
} }
Table$str_set(env->types, heap_strf("%s$%s", def->name, tag->name), tag->type); Table$str_set(env->types, String(def->name, "$", tag->name), tag->type);
} }
for (ast_list_t *stmt = def->namespace ? Match(def->namespace, Block)->statements : NULL; stmt; stmt = stmt->next) { for (ast_list_t *stmt = def->namespace ? Match(def->namespace, Block)->statements : NULL; stmt; stmt = stmt->next) {
@ -450,7 +446,7 @@ void bind_statement(env_t *env, ast_t *statement)
if (!b) if (!b)
Table$str_set(env->locals, entry->name, entry->binding); Table$str_set(env->locals, entry->name, entry->binding);
else if (b != entry->binding) else if (b != entry->binding)
code_err(statement, "This module imports a symbol called '%s', which would clobber another variable", entry->name); code_err(statement, "This module imports a symbol called '", entry->name, "', which would clobber another variable");
} }
} }
for (int64_t i = 1; i <= Table$length(*module_env->types); i++) { for (int64_t i = 1; i <= Table$length(*module_env->types); i++) {
@ -506,7 +502,7 @@ type_t *get_method_type(env_t *env, ast_t *self, const char *name)
{ {
binding_t *b = get_namespace_binding(env, self, name); binding_t *b = get_namespace_binding(env, self, name);
if (!b || !b->type) if (!b || !b->type)
code_err(self, "No such method: %T:%s(...)", get_type(env, self), name); code_err(self, "No such method: ", type_to_str(get_type(env, self)), ":", name, "(...)");
return b->type; return b->type;
} }
@ -516,7 +512,7 @@ env_t *when_clause_scope(env_t *env, type_t *subject_t, when_clause_t *clause)
return env; return env;
if (clause->pattern->tag != FunctionCall || Match(clause->pattern, FunctionCall)->fn->tag != Var) if (clause->pattern->tag != FunctionCall || Match(clause->pattern, FunctionCall)->fn->tag != Var)
code_err(clause->pattern, "I only support variables and constructors for pattern matching %T types in a 'when' block", subject_t); code_err(clause->pattern, "I only support variables and constructors for pattern matching ", type_to_str(subject_t), " types in a 'when' block");
auto fn = Match(clause->pattern, FunctionCall); auto fn = Match(clause->pattern, FunctionCall);
const char *tag_name = Match(fn->fn, Var)->name; const char *tag_name = Match(fn->fn, Var)->name;
@ -530,7 +526,7 @@ env_t *when_clause_scope(env_t *env, type_t *subject_t, when_clause_t *clause)
} }
if (!tag_type) if (!tag_type)
code_err(clause->pattern, "There is no tag '%s' for the type %T", tag_name, subject_t); code_err(clause->pattern, "There is no tag ", quoted(tag_name), " for the type ", type_to_str(subject_t));
if (!fn->args) if (!fn->args)
return env; return env;
@ -547,9 +543,9 @@ env_t *when_clause_scope(env_t *env, type_t *subject_t, when_clause_t *clause)
arg_t *field = tag_struct->fields; arg_t *field = tag_struct->fields;
for (arg_ast_t *var = fn->args; var || field; var = var ? var->next : var) { for (arg_ast_t *var = fn->args; var || field; var = var ? var->next : var) {
if (!var) if (!var)
code_err(clause->pattern, "The field %T.%s.%s wasn't accounted for", subject_t, tag_name, field->name); code_err(clause->pattern, "The field ", type_to_str(subject_t), ".", tag_name, ".", field->name, " wasn't accounted for");
if (!field) if (!field)
code_err(var->value, "This is one more field than %T has", subject_t); code_err(var->value, "This is one more field than ", type_to_str(subject_t), " has");
if (var->value->tag != Var) if (var->value->tag != Var)
code_err(var->value, "I expected this to be a plain variable so I could bind it to a value"); code_err(var->value, "I expected this to be a plain variable so I could bind it to a value");
if (!streq(Match(var->value, Var)->name, "_")) if (!streq(Match(var->value, Var)->name, "_"))
@ -576,7 +572,7 @@ type_t *get_type(env_t *env, ast_t *ast)
return Type(OptionalType, .type=NULL); return Type(OptionalType, .type=NULL);
type_t *t = parse_type_ast(env, Match(ast, None)->type); type_t *t = parse_type_ast(env, Match(ast, None)->type);
if (t->tag == OptionalType) if (t->tag == OptionalType)
code_err(ast, "Nested optional types are not supported. This should be: `none:%T`", Match(t, OptionalType)->type); code_err(ast, "Nested optional types are not supported. This should be: `none:", type_to_str(Match(t, OptionalType)->type), "`");
return Type(OptionalType, .type=t); return Type(OptionalType, .type=t);
} }
case Bool: { case Bool: {
@ -654,7 +650,7 @@ type_t *get_type(env_t *env, ast_t *ast)
if (lang) { if (lang) {
binding_t *b = get_binding(env, lang); binding_t *b = get_binding(env, lang);
if (!b || b->type->tag != TypeInfoType || Match(b->type, TypeInfoType)->type->tag != TextType) if (!b || b->type->tag != TypeInfoType || Match(b->type, TypeInfoType)->type->tag != TextType)
code_err(ast, "There is no text language called '%s'", lang); code_err(ast, "There is no text language called '", lang, "'");
return Match(get_binding(env, lang)->type, TypeInfoType)->type; return Match(get_binding(env, lang)->type, TypeInfoType)->type;
} else { } else {
return TEXT_TYPE; return TEXT_TYPE;
@ -664,7 +660,7 @@ type_t *get_type(env_t *env, ast_t *ast)
auto var = Match(ast, Var); auto var = Match(ast, Var);
binding_t *b = get_binding(env, var->name); binding_t *b = get_binding(env, var->name);
if (b) return b->type; if (b) return b->type;
code_err(ast, "I don't know what \"%s\" refers to", var->name); code_err(ast, "I don't know what ", quoted(var->name), " refers to");
} }
case Array: { case Array: {
auto array = Match(ast, Array); auto array = Match(ast, Array);
@ -685,8 +681,8 @@ type_t *get_type(env_t *env, ast_t *ast)
type_t *merged = item_type ? type_or_type(item_type, t2) : t2; type_t *merged = item_type ? type_or_type(item_type, t2) : t2;
if (!merged) if (!merged)
code_err(item->ast, code_err(item->ast,
"This array item has type %T, which is different from earlier array items which have type %T", "This array item has type ", type_to_str(t2),
t2, item_type); ", which is different from earlier array items which have type ", type_to_str(item_type));
item_type = merged; item_type = merged;
} }
} else { } else {
@ -716,8 +712,8 @@ type_t *get_type(env_t *env, ast_t *ast)
type_t *item_merged = type_or_type(item_type, this_item_type); type_t *item_merged = type_or_type(item_type, this_item_type);
if (!item_merged) if (!item_merged)
code_err(item_ast, code_err(item_ast,
"This set item has type %T, which is different from earlier set items which have type %T", "This set item has type ", type_to_str(this_item_type),
this_item_type, item_type); ", which is different from earlier set items which have type ", type_to_str(item_type));
item_type = item_merged; item_type = item_merged;
} }
} }
@ -752,15 +748,15 @@ type_t *get_type(env_t *env, ast_t *ast)
type_t *key_merged = key_type ? type_or_type(key_type, key_t) : key_t; type_t *key_merged = key_type ? type_or_type(key_type, key_t) : key_t;
if (!key_merged) if (!key_merged)
code_err(entry->ast, code_err(entry->ast,
"This table entry has type %T, which is different from earlier table entries which have type %T", "This table entry has type ", type_to_str(key_t),
key_t, key_type); ", which is different from earlier table entries which have type ", type_to_str(key_type));
key_type = key_merged; key_type = key_merged;
type_t *val_merged = value_type ? type_or_type(value_type, value_t) : value_t; type_t *val_merged = value_type ? type_or_type(value_type, value_t) : value_t;
if (!val_merged) if (!val_merged)
code_err(entry->ast, code_err(entry->ast,
"This table entry has type %T, which is different from earlier table entries which have type %T", "This table entry has type ", type_to_str(value_t),
value_t, value_type); ", which is different from earlier table entries which have type ", type_to_str(value_type));
value_type = val_merged; value_type = val_merged;
} }
} }
@ -789,18 +785,18 @@ type_t *get_type(env_t *env, ast_t *ast)
if (fielded_t->tag == ModuleType) { if (fielded_t->tag == ModuleType) {
const char *name = Match(fielded_t, ModuleType)->name; const char *name = Match(fielded_t, ModuleType)->name;
env_t *module_env = Table$str_get(*env->imports, name); env_t *module_env = Table$str_get(*env->imports, name);
if (!module_env) code_err(access->fielded, "I couldn't find the environment for the module %s", name); if (!module_env) code_err(access->fielded, "I couldn't find the environment for the module ", name);
return get_type(module_env, WrapAST(ast, Var, access->field)); return get_type(module_env, WrapAST(ast, Var, access->field));
} else if (fielded_t->tag == TypeInfoType) { } else if (fielded_t->tag == TypeInfoType) {
auto info = Match(fielded_t, TypeInfoType); auto info = Match(fielded_t, TypeInfoType);
assert(info->env); assert(info->env);
binding_t *b = get_binding(info->env, access->field); binding_t *b = get_binding(info->env, access->field);
if (!b) code_err(ast, "I couldn't find the field '%s' on this type", access->field); if (!b) code_err(ast, "I couldn't find the field '", access->field, "' on this type");
return b->type; return b->type;
} }
type_t *field_t = get_field_type(fielded_t, access->field); type_t *field_t = get_field_type(fielded_t, access->field);
if (!field_t) if (!field_t)
code_err(ast, "%T objects don't have a field called '%s'", fielded_t, access->field); code_err(ast, type_to_str(fielded_t), " objects don't have a field called '", access->field, "'");
return field_t; return field_t;
} }
case Index: { case Index: {
@ -818,7 +814,7 @@ type_t *get_type(env_t *env, ast_t *ast)
type_t *index_t = get_type(env, indexing->index); type_t *index_t = get_type(env, indexing->index);
if (index_t->tag == IntType || index_t->tag == BigIntType || index_t->tag == ByteType) if (index_t->tag == IntType || index_t->tag == BigIntType || index_t->tag == ByteType)
return Match(value_t, ArrayType)->item_type; return Match(value_t, ArrayType)->item_type;
code_err(indexing->index, "I only know how to index lists using integers, not %T", index_t); code_err(indexing->index, "I only know how to index lists using integers, not ", type_to_str(index_t));
} else if (value_t->tag == TableType) { } else if (value_t->tag == TableType) {
auto table_type = Match(value_t, TableType); auto table_type = Match(value_t, TableType);
if (table_type->default_value) if (table_type->default_value)
@ -830,7 +826,7 @@ type_t *get_type(env_t *env, ast_t *ast)
} else if (value_t->tag == TextType) { } else if (value_t->tag == TextType) {
return value_t; return value_t;
} else { } else {
code_err(ast, "I don't know how to index %T values", indexed_t); code_err(ast, "I don't know how to index ", type_to_str(indexed_t), " values");
} }
} }
case FunctionCall: { case FunctionCall: {
@ -853,7 +849,7 @@ type_t *get_type(env_t *env, ast_t *ast)
if (fn_type_t->tag == ClosureType) if (fn_type_t->tag == ClosureType)
fn_type_t = Match(fn_type_t, ClosureType)->fn; fn_type_t = Match(fn_type_t, ClosureType)->fn;
if (fn_type_t->tag != FunctionType) if (fn_type_t->tag != FunctionType)
code_err(call->fn, "This isn't a function, it's a %T", fn_type_t); code_err(call->fn, "This isn't a function, it's a ", type_to_str(fn_type_t));
auto fn_type = Match(fn_type_t, FunctionType); auto fn_type = Match(fn_type_t, FunctionType);
return fn_type->ret; return fn_type->ret;
} }
@ -893,7 +889,7 @@ type_t *get_type(env_t *env, ast_t *ast)
else if (streq(call->name, "sorted")) return self_value_t; else if (streq(call->name, "sorted")) return self_value_t;
else if (streq(call->name, "to")) return self_value_t; else if (streq(call->name, "to")) return self_value_t;
else if (streq(call->name, "unique")) return Type(SetType, .item_type=item_type); else if (streq(call->name, "unique")) return Type(SetType, .item_type=item_type);
else code_err(ast, "There is no '%s' method for arrays", call->name); else code_err(ast, "There is no '", call->name, "' method for arrays");
} }
case SetType: { case SetType: {
if (streq(call->name, "add")) return Type(VoidType); if (streq(call->name, "add")) return Type(VoidType);
@ -907,7 +903,7 @@ type_t *get_type(env_t *env, ast_t *ast)
else if (streq(call->name, "remove_all")) return Type(VoidType); else if (streq(call->name, "remove_all")) return Type(VoidType);
else if (streq(call->name, "with")) return self_value_t; else if (streq(call->name, "with")) return self_value_t;
else if (streq(call->name, "without")) return self_value_t; else if (streq(call->name, "without")) return self_value_t;
else code_err(ast, "There is no '%s' method for sets", call->name); else code_err(ast, "There is no '", call->name, "' method for sets");
} }
case TableType: { case TableType: {
auto table = Match(self_value_t, TableType); auto table = Match(self_value_t, TableType);
@ -918,14 +914,14 @@ type_t *get_type(env_t *env, ast_t *ast)
else if (streq(call->name, "remove")) return Type(VoidType); else if (streq(call->name, "remove")) return Type(VoidType);
else if (streq(call->name, "set")) return Type(VoidType); else if (streq(call->name, "set")) return Type(VoidType);
else if (streq(call->name, "sorted")) return self_value_t; else if (streq(call->name, "sorted")) return self_value_t;
code_err(ast, "There is no '%s' method for %T tables", call->name, self_value_t); code_err(ast, "There is no '", call->name, "' method for ", type_to_str(self_value_t), " tables");
} }
default: { default: {
type_t *fn_type_t = get_method_type(env, call->self, call->name); type_t *fn_type_t = get_method_type(env, call->self, call->name);
if (!fn_type_t) if (!fn_type_t)
code_err(ast, "No such method!"); code_err(ast, "No such method!");
if (fn_type_t->tag != FunctionType) if (fn_type_t->tag != FunctionType)
code_err(ast, "This isn't a method, it's a %T", fn_type_t); code_err(ast, "This isn't a method, it's a ", type_to_str(fn_type_t));
auto fn_type = Match(fn_type_t, FunctionType); auto fn_type = Match(fn_type_t, FunctionType);
return fn_type->ret; return fn_type->ret;
} }
@ -999,7 +995,7 @@ type_t *get_type(env_t *env, ast_t *ast)
return t; return t;
} }
code_err(ast, "I don't know how to get the negative value of type %T", t); code_err(ast, "I don't know how to get the negative value of type ", type_to_str(t));
} }
case Not: { case Not: {
type_t *t = get_type(env, Match(ast, Not)->value); type_t *t = get_type(env, Match(ast, Not)->value);
@ -1015,7 +1011,7 @@ type_t *get_type(env_t *env, ast_t *ast)
if (fn->args && type_eq(t, get_arg_type(env, fn->args)) && type_eq(t, fn->ret)) if (fn->args && type_eq(t, get_arg_type(env, fn->args)) && type_eq(t, fn->ret))
return t; return t;
} }
code_err(ast, "I only know how to get 'not' of boolean, numeric, and optional pointer types, not %T", t); code_err(ast, "I only know how to get 'not' of boolean, numeric, and optional pointer types, not ", type_to_str(t));
} }
case Mutexed: { case Mutexed: {
type_t *item_type = get_type(env, Match(ast, Mutexed)->value); type_t *item_type = get_type(env, Match(ast, Mutexed)->value);
@ -1025,7 +1021,7 @@ type_t *get_type(env_t *env, ast_t *ast)
ast_t *held = Match(ast, Holding)->mutexed; ast_t *held = Match(ast, Holding)->mutexed;
type_t *held_type = get_type(env, held); type_t *held_type = get_type(env, held);
if (held_type->tag != MutexedType) if (held_type->tag != MutexedType)
code_err(held, "This is a %t, not a mutexed value", held_type); code_err(held, "This is a ", type_to_str(held_type), ", not a mutexed value");
if (held->tag == Var) { if (held->tag == Var) {
env = fresh_scope(env); env = fresh_scope(env);
set_binding(env, Match(held, Var)->name, Type(PointerType, .pointed=Match(held_type, MutexedType)->type, .is_stack=true), CORD_EMPTY); set_binding(env, Match(held, Var)->name, Type(PointerType, .pointed=Match(held_type, MutexedType)->type, .is_stack=true), CORD_EMPTY);
@ -1106,7 +1102,7 @@ type_t *get_type(env_t *env, ast_t *ast)
|| (lhs_t->tag == ByteType && rhs_t->tag == ByteType)) { || (lhs_t->tag == ByteType && rhs_t->tag == ByteType)) {
return get_math_type(env, ast, lhs_t, rhs_t); return get_math_type(env, ast, lhs_t, rhs_t);
} }
code_err(ast, "I can't figure out the type of this `and` expression between a %T and a %T", lhs_t, rhs_t); code_err(ast, "I can't figure out the type of this `and` expression between a ", type_to_str(lhs_t), " and a ", type_to_str(rhs_t));
} }
case BINOP_OR: { case BINOP_OR: {
if (lhs_t->tag == BoolType && rhs_t->tag == BoolType) { if (lhs_t->tag == BoolType && rhs_t->tag == BoolType) {
@ -1136,7 +1132,7 @@ type_t *get_type(env_t *env, ast_t *ast)
} else if (rhs_t->tag == OptionalType) { } else if (rhs_t->tag == OptionalType) {
return type_or_type(lhs_t, rhs_t); return type_or_type(lhs_t, rhs_t);
} }
code_err(ast, "I can't figure out the type of this `or` expression between a %T and a %T", lhs_t, rhs_t); code_err(ast, "I can't figure out the type of this `or` expression between a ", type_to_str(lhs_t), " and a ", type_to_str(rhs_t));
} }
case BINOP_XOR: { case BINOP_XOR: {
if (lhs_t->tag == BoolType && rhs_t->tag == BoolType) { if (lhs_t->tag == BoolType && rhs_t->tag == BoolType) {
@ -1149,20 +1145,20 @@ type_t *get_type(env_t *env, ast_t *ast)
return get_math_type(env, ast, lhs_t, rhs_t); return get_math_type(env, ast, lhs_t, rhs_t);
} }
code_err(ast, "I can't figure out the type of this `xor` expression between a %T and a %T", lhs_t, rhs_t); code_err(ast, "I can't figure out the type of this `xor` expression between a ", type_to_str(lhs_t), " and a ", type_to_str(rhs_t));
} }
case BINOP_CONCAT: { case BINOP_CONCAT: {
if (!type_eq(lhs_t, rhs_t)) if (!type_eq(lhs_t, rhs_t))
code_err(ast, "The type on the left side of this concatenation doesn't match the right side: %T vs. %T", code_err(ast, "The type on the left side of this concatenation doesn't match the right side: ", type_to_str(lhs_t),
lhs_t, rhs_t); " vs. ", type_to_str(rhs_t));
if (lhs_t->tag == ArrayType || lhs_t->tag == TextType || lhs_t->tag == SetType) if (lhs_t->tag == ArrayType || lhs_t->tag == TextType || lhs_t->tag == SetType)
return lhs_t; return lhs_t;
code_err(ast, "Only array/set/text value types support concatenation, not %T", lhs_t); code_err(ast, "Only array/set/text value types support concatenation, not ", type_to_str(lhs_t));
} }
case BINOP_EQ: case BINOP_NE: case BINOP_LT: case BINOP_LE: case BINOP_GT: case BINOP_GE: { case BINOP_EQ: case BINOP_NE: case BINOP_LT: case BINOP_LE: case BINOP_GT: case BINOP_GE: {
if (!can_promote(lhs_t, rhs_t) && !can_promote(rhs_t, lhs_t)) if (!can_promote(lhs_t, rhs_t) && !can_promote(rhs_t, lhs_t))
code_err(ast, "I can't compare these two different types: %T vs %T", lhs_t, rhs_t); code_err(ast, "I can't compare these two different types: ", type_to_str(lhs_t), " vs ", type_to_str(rhs_t));
return Type(BoolType); return Type(BoolType);
} }
case BINOP_CMP: case BINOP_CMP:
@ -1199,7 +1195,7 @@ type_t *get_type(env_t *env, ast_t *ast)
type_t *iterated = get_iterated_type(iter_t); type_t *iterated = get_iterated_type(iter_t);
if (!iterated) if (!iterated)
code_err(reduction->iter, "I don't know how to do a reduction over %T values", iter_t); code_err(reduction->iter, "I don't know how to do a reduction over ", type_to_str(iter_t), " values");
return iterated->tag == OptionalType ? iterated : Type(OptionalType, .type=iterated); return iterated->tag == OptionalType ? iterated : Type(OptionalType, .type=iterated);
} }
@ -1214,7 +1210,7 @@ type_t *get_type(env_t *env, ast_t *ast)
type_t *lhs_t = get_type(env, lhs), *rhs_t = get_type(env, rhs); type_t *lhs_t = get_type(env, lhs), *rhs_t = get_type(env, rhs);
type_t *t = type_or_type(lhs_t, rhs_t); type_t *t = type_or_type(lhs_t, rhs_t);
if (!t) if (!t)
code_err(ast, "The two sides of this operation are not compatible: %T vs %T", lhs_t, rhs_t); code_err(ast, "The two sides of this operation are not compatible: ", type_to_str(lhs_t), " vs ", type_to_str(rhs_t));
return t; return t;
} }
@ -1243,8 +1239,8 @@ type_t *get_type(env_t *env, ast_t *ast)
if (can_promote(ret, declared)) if (can_promote(ret, declared))
ret = declared; ret = declared;
else else
code_err(ast, "This function was declared to return a value of type %T, but actually returns a value of type %T", code_err(ast, "This function was declared to return a value of type ", type_to_str(declared),
declared, ret); ", but actually returns a value of type ", type_to_str(ret));
} }
if (has_stack_memory(ret)) if (has_stack_memory(ret))
@ -1290,8 +1286,8 @@ type_t *get_type(env_t *env, ast_t *ast)
type_t *t_either = type_or_type(true_t, false_t); type_t *t_either = type_or_type(true_t, false_t);
if (!t_either) if (!t_either)
code_err(if_->else_body, code_err(if_->else_body,
"I was expecting this block to have a %T value (based on earlier clauses), but it actually has a %T value.", "I was expecting this block to have a ", type_to_str(true_t),
true_t, false_t); " value (based on earlier clauses), but it actually has a ", type_to_str(false_t), " value.");
return t_either; return t_either;
} }
@ -1329,7 +1325,7 @@ type_t *get_type(env_t *env, ast_t *ast)
else if (clause->pattern->tag == FunctionCall && Match(clause->pattern, FunctionCall)->fn->tag == Var) else if (clause->pattern->tag == FunctionCall && Match(clause->pattern, FunctionCall)->fn->tag == Var)
tag_name = Match(Match(clause->pattern, FunctionCall)->fn, Var)->name; tag_name = Match(Match(clause->pattern, FunctionCall)->fn, Var)->name;
else else
code_err(clause->pattern, "This is not a valid pattern for a %T enum", subject_t); code_err(clause->pattern, "This is not a valid pattern for a ", type_to_str(subject_t), " enum");
CORD valid_tags = CORD_EMPTY; CORD valid_tags = CORD_EMPTY;
for (match_t *m = matches; m; m = m->next) { for (match_t *m = matches; m; m = m->next) {
@ -1343,8 +1339,8 @@ type_t *get_type(env_t *env, ast_t *ast)
valid_tags = CORD_cat(valid_tags, m->tag->name); valid_tags = CORD_cat(valid_tags, m->tag->name);
} }
code_err(clause->pattern, "There is no tag '%s' for the type %T (valid tags: %s)", code_err(clause->pattern, "There is no tag '", tag_name,
tag_name, subject_t, CORD_to_char_star(valid_tags)); "' for the type ", type_to_str(subject_t), " (valid tags: ", CORD_to_char_star(valid_tags), ")");
found_matching_tag:; found_matching_tag:;
} }
@ -1353,8 +1349,8 @@ type_t *get_type(env_t *env, ast_t *ast)
type_t *clause_type = get_type(clause_scope, clause->body); type_t *clause_type = get_type(clause_scope, clause->body);
type_t *merged = type_or_type(overall_t, clause_type); type_t *merged = type_or_type(overall_t, clause_type);
if (!merged) if (!merged)
code_err(clause->body, "The type of this branch is %T, which conflicts with the earlier branch type of %T", code_err(clause->body, "The type of this branch is ", type_to_str(clause_type),
clause_type, overall_t); ", which conflicts with the earlier branch type of ", type_to_str(overall_t));
overall_t = merged; overall_t = merged;
} }
@ -1375,8 +1371,8 @@ type_t *get_type(env_t *env, ast_t *ast)
type_t *merged = type_or_type(overall_t, else_t); type_t *merged = type_or_type(overall_t, else_t);
if (!merged) if (!merged)
code_err(when->else_body, code_err(when->else_body,
"I was expecting this block to have a %T value (based on earlier clauses), but it actually has a %T value.", "I was expecting this block to have a ", type_to_str(overall_t),
overall_t, else_t); " value (based on earlier clauses), but it actually has a ", type_to_str(else_t), " value.");
return merged; return merged;
} else { } else {
CORD unhandled = CORD_EMPTY; CORD unhandled = CORD_EMPTY;
@ -1385,7 +1381,7 @@ type_t *get_type(env_t *env, ast_t *ast)
unhandled = unhandled ? CORD_all(unhandled, ", ", m->tag->name) : m->tag->name; unhandled = unhandled ? CORD_all(unhandled, ", ", m->tag->name) : m->tag->name;
} }
if (unhandled) if (unhandled)
code_err(ast, "This 'when' statement doesn't handle the tags: %s", CORD_to_const_char_star(unhandled)); code_err(ast, "This 'when' statement doesn't handle the tags: ", CORD_to_const_char_star(unhandled));
return overall_t; return overall_t;
} }
} }
@ -1399,11 +1395,11 @@ type_t *get_type(env_t *env, ast_t *ast)
return type_ast ? parse_type_ast(env, type_ast) : Type(VoidType); return type_ast ? parse_type_ast(env, type_ast) : Type(VoidType);
} }
case Moment: return Type(MomentType); case Moment: return Type(MomentType);
case Unknown: code_err(ast, "I can't figure out the type of: %W", ast); case Unknown: code_err(ast, "I can't figure out the type of: ", ast_to_str(ast));
case Deserialize: return parse_type_ast(env, Match(ast, Deserialize)->type); case Deserialize: return parse_type_ast(env, Match(ast, Deserialize)->type);
} }
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
code_err(ast, "I can't figure out the type of: %W", ast); code_err(ast, "I can't figure out the type of: ", ast_to_str(ast));
} }
PUREFUNC bool is_discardable(env_t *env, ast_t *ast) PUREFUNC bool is_discardable(env_t *env, ast_t *ast)

View File

@ -105,6 +105,11 @@ CORD type_to_cord(type_t *t) {
} }
} }
const char *type_to_str(type_t *t)
{
return CORD_to_const_char_star(type_to_cord(t));
}
PUREFUNC const char *get_type_name(type_t *t) PUREFUNC const char *get_type_name(type_t *t)
{ {
switch (t->tag) { switch (t->tag) {
@ -115,23 +120,6 @@ PUREFUNC const char *get_type_name(type_t *t)
} }
} }
int printf_pointer_size(const struct printf_info *info, size_t n, int argtypes[n], int sizes[n])
{
if (n < 1) return -1;
(void)info;
argtypes[0] = PA_POINTER;
sizes[0] = sizeof(void*);
return 1;
}
int printf_type(FILE *stream, const struct printf_info *info, const void *const args[])
{
(void)info;
type_t *t = *(type_t**)args[0];
if (!t) return fputs("(null)", stream);
return CORD_put(type_to_cord(t), stream);
}
bool type_eq(type_t *a, type_t *b) bool type_eq(type_t *a, type_t *b)
{ {
if (a == b) return true; if (a == b) return true;

View File

@ -2,11 +2,9 @@
// Logic for defining and working with types // Logic for defining and working with types
#include <printf.h>
#include <stdlib.h> #include <stdlib.h>
#include "ast.h" #include "ast.h"
#include "stdlib/arrays.h"
typedef struct type_s type_t; typedef struct type_s type_t;
@ -135,9 +133,8 @@ struct type_s {
#define INT_TYPE Type(BigIntType) #define INT_TYPE Type(BigIntType)
#define NUM_TYPE Type(NumType, .bits=TYPE_NBITS64) #define NUM_TYPE Type(NumType, .bits=TYPE_NBITS64)
int printf_pointer_size(const struct printf_info *info, size_t n, int argtypes[n], int size[n]);
int printf_type(FILE *stream, const struct printf_info *info, const void *const args[]);
CORD type_to_cord(type_t *t); CORD type_to_cord(type_t *t);
const char *type_to_str(type_t *t);
const char *get_type_name(type_t *t); const char *get_type_name(type_t *t);
PUREFUNC bool type_eq(type_t *a, type_t *b); PUREFUNC bool type_eq(type_t *a, type_t *b);
PUREFUNC bool type_is_a(type_t *t, type_t *req); PUREFUNC bool type_is_a(type_t *t, type_t *req);