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:
parent
2186e84de0
commit
3c52a75633
@ -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++) {
|
||||
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_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++) {
|
||||
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_value(&env_array, NULL, I(0), sizeof(char*));
|
||||
|
19
src/ast.c
19
src/ast.c
@ -1,7 +1,6 @@
|
||||
// Some basic operations defined on AST nodes, mainly converting to
|
||||
// strings for debugging.
|
||||
#include <gc/cord.h>
|
||||
#include <printf.h>
|
||||
#include <stdarg.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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
switch (ast->tag) {
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
#include <err.h>
|
||||
#include <gc/cord.h>
|
||||
#include <printf.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
@ -344,8 +343,8 @@ struct ast_s {
|
||||
};
|
||||
|
||||
CORD ast_to_xml(ast_t *ast);
|
||||
const char *ast_to_str(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);
|
||||
void visit_topologically(ast_list_t *ast, Closure_t fn);
|
||||
|
||||
|
233
src/compile.c
233
src/compile.c
@ -47,7 +47,7 @@ CORD promote_to_optional(type_t *t, CORD code)
|
||||
case TYPE_IBITS16: return CORD_all("((OptionalInt16_t){", code, "})");
|
||||
case TYPE_IBITS32: return CORD_all("((OptionalInt32_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) {
|
||||
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)
|
||||
clause_tag_name = Match(Match(clause->pattern, FunctionCall)->fn, Var)->name;
|
||||
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;
|
||||
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: {
|
||||
auto reduction = Match(ast, Reduction);
|
||||
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));
|
||||
env_t *scope = for_scope(env, loop);
|
||||
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");
|
||||
}
|
||||
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";
|
||||
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) {
|
||||
ast_t *subject = Match(ast, FieldAccess)->fielded;
|
||||
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 {
|
||||
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 {
|
||||
return CORD_all("Array_lvalue(", compile_type(item_type), ", ", target_code, ", ",
|
||||
index_code,
|
||||
", ", heap_strf("%ld", ast->start - ast->file->text),
|
||||
", ", heap_strf("%ld", ast->end - ast->file->text), ")");
|
||||
", ", String((int)(ast->start - ast->file->text)),
|
||||
", ", String((int)(ast->end - ast->file->text)), ")");
|
||||
}
|
||||
} else if (container_t->tag == 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)");
|
||||
else if (t->tag == MutexedType)
|
||||
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)
|
||||
@ -751,7 +751,7 @@ static CORD compile_condition(env_t *env, ast_t *ast)
|
||||
} else if (t->tag == PointerType) {
|
||||
code_err(ast, "This pointer will always be non-none, so it should not be used in a conditional.");
|
||||
} 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)
|
||||
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;
|
||||
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;
|
||||
for (arg_ast_t *arg = args; arg || field; arg = arg->next) {
|
||||
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)
|
||||
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)
|
||||
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) {
|
||||
type_t *expected_type = get_type(env, test->expected);
|
||||
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)",
|
||||
expr_t, expected_type);
|
||||
code_err(ast, "The type on the top of this test (", type_to_str(expr_t),
|
||||
") is different from the type on the bottom (", type_to_str(expected_type), ")");
|
||||
return CORD_asprintf(
|
||||
"%rtest(%r, %r, %r, %ld, %ld);",
|
||||
setup, test_code,
|
||||
@ -987,7 +987,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
|
||||
} else {
|
||||
type_t *t = get_type(env, decl->value);
|
||||
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);
|
||||
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))) {
|
||||
rhs = compile_int_to_type(env, update->rhs, 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);
|
||||
switch (update->op) {
|
||||
case BINOP_MULT:
|
||||
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
|
||||
return CORD_asprintf("%r *= %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, ";");
|
||||
case BINOP_DIVIDE:
|
||||
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
|
||||
return CORD_asprintf("%r /= %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, ";");
|
||||
case BINOP_MOD:
|
||||
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);
|
||||
case BINOP_MOD1:
|
||||
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;");
|
||||
case BINOP_PLUS:
|
||||
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, ";");
|
||||
case BINOP_MINUS:
|
||||
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, ";");
|
||||
case BINOP_POWER: {
|
||||
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:
|
||||
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, ";");
|
||||
case BINOP_RSHIFT:
|
||||
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, ";");
|
||||
case BINOP_ULSHIFT:
|
||||
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, "; }");
|
||||
case BINOP_URSHIFT:
|
||||
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, "; }");
|
||||
case BINOP_AND: {
|
||||
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)
|
||||
return CORD_all("if (!(", check_none(lhs_t, lhs), ")) ", lhs, " = ", promote_to_optional(rhs_t, rhs), ";");
|
||||
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: {
|
||||
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)
|
||||
return CORD_all("if (", check_none(lhs_t, lhs), ") ", lhs, " = ", promote_to_optional(rhs_t, rhs), ";");
|
||||
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:
|
||||
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, ";");
|
||||
case BINOP_CONCAT: {
|
||||
if (lhs_t->tag == TextType) {
|
||||
@ -1175,7 +1175,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
|
||||
else
|
||||
return CORD_all(lhs, " = Array$concat(", lhs, ", ", rhs, ", ", padded_item_size, ");");
|
||||
} 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");
|
||||
@ -1209,7 +1209,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
|
||||
if (env->loop_ctx)
|
||||
code_err(ast, "This 'skip' is not inside any loop");
|
||||
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
|
||||
return "continue;";
|
||||
}
|
||||
@ -1238,7 +1238,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
|
||||
if (env->loop_ctx)
|
||||
code_err(ast, "This 'stop' is not inside any loop");
|
||||
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
|
||||
return "break;";
|
||||
}
|
||||
@ -1307,7 +1307,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
|
||||
return CORD_all(code, "return ", value, ";");
|
||||
} else {
|
||||
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;");
|
||||
}
|
||||
}
|
||||
@ -1349,7 +1349,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
|
||||
ast_t *held = Match(ast, Holding)->mutexed;
|
||||
type_t *held_type = get_type(env, held);
|
||||
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(
|
||||
"{ // Holding\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);
|
||||
if (args->next) {
|
||||
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)
|
||||
optional_step = compile_to_type(env, args->next->value, Type(OptionalType, .type=INT_TYPE));
|
||||
else
|
||||
@ -1434,7 +1434,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
|
||||
step = compile_to_type(env, args->value, INT_TYPE);
|
||||
if (args->next) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -1692,7 +1692,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
|
||||
|
||||
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: {
|
||||
@ -1766,7 +1766,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
|
||||
return with_source_info(ast, CORD_all("_$", name, "$$initialize();\n"));
|
||||
} else if (use->what == USE_MODULE) {
|
||||
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");
|
||||
|
||||
CORD initialization = CORD_EMPTY;
|
||||
@ -1786,7 +1786,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
|
||||
}
|
||||
default:
|
||||
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));
|
||||
}
|
||||
}
|
||||
@ -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 StructType: case EnumType: case MutexedType:
|
||||
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)
|
||||
val = CORD_all("(&", val, ")");
|
||||
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);
|
||||
++depth;
|
||||
} else {
|
||||
@ -1894,7 +1894,7 @@ CORD compile_to_type(env_t *env, ast_t *ast, type_t *t)
|
||||
|
||||
CORD code = compile(env, ast);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1904,7 +1904,7 @@ CORD compile_int_to_type(env_t *env, ast_t *ast, type_t *target)
|
||||
CORD code = compile(env, ast);
|
||||
type_t *actual_type = get_type(env, ast);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1964,9 +1964,9 @@ CORD compile_int_to_type(env_t *env, ast_t *ast, type_t *target)
|
||||
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 {
|
||||
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:
|
||||
for (arg_ast_t *call_arg = call_args; call_arg; call_arg = call_arg->next) {
|
||||
if (call_arg->name) continue;
|
||||
const char *pseudoname = heap_strf("%ld", i++);
|
||||
const char *pseudoname = String(i++);
|
||||
if (!Table$str_get(used_args, pseudoname)) {
|
||||
CORD value;
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
if (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 {
|
||||
const char *pseudoname = heap_strf("%ld", i++);
|
||||
const char *pseudoname = String(i++);
|
||||
if (!Table$str_get(used_args, pseudoname))
|
||||
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})");
|
||||
}
|
||||
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)
|
||||
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: {
|
||||
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)
|
||||
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){...})
|
||||
@ -2349,7 +2349,7 @@ CORD compile(env_t *env, ast_t *ast)
|
||||
ast_t *held = Match(ast, Holding)->mutexed;
|
||||
type_t *held_type = get_type(env, held);
|
||||
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(
|
||||
"({ // Holding\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) {
|
||||
return CORD_all("((!", check_none(lhs_t, compile(env, binop->lhs)), ") || ", compile(env, binop->rhs), ")");
|
||||
} 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) {
|
||||
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) {
|
||||
return CORD_all("((!", check_none(lhs_t, compile(env, binop->lhs)), ") && ", compile(env, binop->rhs), ")");
|
||||
} 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))
|
||||
operand_t = rhs_t;
|
||||
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) {
|
||||
case BINOP_POWER: {
|
||||
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)
|
||||
return CORD_all("powf(", lhs, ", ", rhs, ")");
|
||||
else
|
||||
@ -2481,52 +2481,52 @@ CORD compile(env_t *env, ast_t *ast)
|
||||
}
|
||||
case BINOP_MULT: {
|
||||
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, ")");
|
||||
}
|
||||
case BINOP_DIVIDE: {
|
||||
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, ")");
|
||||
}
|
||||
case BINOP_MOD: {
|
||||
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, ")");
|
||||
}
|
||||
case BINOP_MOD1: {
|
||||
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)");
|
||||
}
|
||||
case BINOP_PLUS: {
|
||||
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, ")");
|
||||
}
|
||||
case BINOP_MINUS: {
|
||||
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, ")");
|
||||
}
|
||||
case BINOP_LSHIFT: {
|
||||
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, ")");
|
||||
}
|
||||
case BINOP_RSHIFT: {
|
||||
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, ")");
|
||||
}
|
||||
case BINOP_ULSHIFT: {
|
||||
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, ")");
|
||||
}
|
||||
case BINOP_URSHIFT: {
|
||||
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, ")");
|
||||
}
|
||||
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)
|
||||
return CORD_all("(", lhs, " & ", rhs, ")");
|
||||
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: {
|
||||
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)
|
||||
return CORD_all("(", lhs, " | ", rhs, ")");
|
||||
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: {
|
||||
// TODO: support optional values in `xor` expressions
|
||||
if (operand_t->tag == BoolType || operand_t->tag == IntType || operand_t->tag == ByteType)
|
||||
return CORD_all("(", lhs, " ^ ", rhs, ")");
|
||||
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: {
|
||||
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), "))");
|
||||
}
|
||||
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;
|
||||
@ -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;
|
||||
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;
|
||||
if (!lang || streq(lang, "Text"))
|
||||
@ -2699,7 +2699,7 @@ CORD compile(env_t *env, ast_t *ast)
|
||||
else
|
||||
chunk_code = compile_string(env, chunk->ast, "no");
|
||||
} 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);
|
||||
@ -2773,9 +2773,6 @@ CORD compile(env_t *env, ast_t *ast)
|
||||
case Array: {
|
||||
type_t *array_type = get_type(env, ast);
|
||||
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);
|
||||
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);
|
||||
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),
|
||||
.type=Type(PointerType, .pointed=array_type, .is_stack=true));
|
||||
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;
|
||||
|
||||
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) {
|
||||
if (entry->ast->tag == Comprehension)
|
||||
@ -2869,7 +2866,7 @@ CORD compile(env_t *env, ast_t *ast)
|
||||
{
|
||||
static int64_t comp_num = 1;
|
||||
env_t *scope = fresh_scope(env);
|
||||
const char *comprehension_name = heap_strf("table$%ld", comp_num++);
|
||||
const char *comprehension_name = String("table$", comp_num++);
|
||||
ast_t *comprehension_var = FakeAST(InlineCCode, .code=CORD_all("&", comprehension_name),
|
||||
.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;
|
||||
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),
|
||||
.type=Type(PointerType, .pointed=set_type, .is_stack=true));
|
||||
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))
|
||||
ret_t = declared;
|
||||
else
|
||||
code_err(ast, "This function was declared to return a value of type %T, but actually returns a value of type %T",
|
||||
declared, ret_t);
|
||||
code_err(ast, "This function was declared to return a value of type ", type_to_str(declared),
|
||||
", but actually returns a value of type ", type_to_str(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++) {
|
||||
struct { const char *name; binding_t *b; } *entry = Table$entry(closed_vars, i);
|
||||
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",
|
||||
entry->b->type, entry->name);
|
||||
code_err(ast, "This function is holding onto a reference to ", type_to_str(entry->b->type),
|
||||
" stack memory in the variable `", entry->name, "`, but the function may outlive the stack memory");
|
||||
if (entry->b->type->tag == ModuleType)
|
||||
continue;
|
||||
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);
|
||||
(void)compile_arguments(env, ast, NULL, call->args);
|
||||
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: {
|
||||
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)));
|
||||
return CORD_all("Table$is_superset_of(", self, ", ", compile_arguments(env, ast, arg_spec, call->args),
|
||||
", ", 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: {
|
||||
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")) {
|
||||
EXPECT_POINTER("a", "table");
|
||||
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
|
||||
? FakeAST(Int, .str="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);
|
||||
(void)compile_arguments(env, ast, NULL, call->args);
|
||||
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: {
|
||||
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), "})");
|
||||
}
|
||||
}
|
||||
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) {
|
||||
fn_t = Match(fn_t, ClosureType)->fn;
|
||||
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); })");
|
||||
}
|
||||
} 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: {
|
||||
ast_t *value = Match(ast, Deserialize)->value;
|
||||
type_t *value_type = get_type(env, value);
|
||||
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);
|
||||
return CORD_all("({ ", compile_declaration(t, "deserialized"), ";\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) {
|
||||
type_t *condition_type = get_type(env, Match(condition, Declare)->value);
|
||||
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);
|
||||
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 *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;
|
||||
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 *loop = FakeAST(For, .vars=new(ast_list_t, .ast=item), .iter=reduction->iter, .body=body);
|
||||
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)
|
||||
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:;
|
||||
}
|
||||
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");
|
||||
return b->code;
|
||||
}
|
||||
@ -3677,7 +3674,7 @@ CORD compile(env_t *env, ast_t *ast)
|
||||
} else if (streq(f->field, "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: {
|
||||
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: {
|
||||
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: {
|
||||
if (streq(f->field, "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: {
|
||||
if (streq(f->field, "items"))
|
||||
return CORD_all("ARRAY_COPY((", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries)");
|
||||
else if (streq(f->field, "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: {
|
||||
if (streq(f->field, "length")) {
|
||||
@ -3737,7 +3734,7 @@ CORD compile(env_t *env, ast_t *ast)
|
||||
} 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; })");
|
||||
}
|
||||
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: {
|
||||
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")) {
|
||||
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:
|
||||
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: {
|
||||
@ -3776,7 +3773,7 @@ CORD compile(env_t *env, ast_t *ast)
|
||||
type_t *index_t = get_type(env, indexing->index);
|
||||
if (container_t->tag == ArrayType) {
|
||||
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;
|
||||
CORD arr = compile_to_pointer_depth(env, indexing->indexed, 0, false);
|
||||
file_t *f = indexing->index->file;
|
||||
@ -3819,7 +3816,7 @@ CORD compile(env_t *env, ast_t *ast)
|
||||
} 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)), ")");
|
||||
} 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: {
|
||||
@ -3911,7 +3908,7 @@ CORD compile_type_info(type_t *t)
|
||||
case MemoryType: return "&Memory$info";
|
||||
case VoidType: return "&Void$info";
|
||||
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);
|
||||
function_name = get_type_name(ret_t);
|
||||
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;
|
||||
cache = convertdef->cache;
|
||||
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)));
|
||||
if (arg->next) arg_signature = CORD_cat(arg_signature, ", ");
|
||||
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);
|
||||
}
|
||||
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.");
|
||||
} else {
|
||||
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");
|
||||
@ -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)
|
||||
fields = new(arg_t, .name=arg->name, .type=get_arg_ast_type(env, arg), .next=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;
|
||||
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);
|
||||
type_t *t = get_type(env, decl->value);
|
||||
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);
|
||||
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);
|
||||
const char *name = get_type_name(Match(type, FunctionType)->ret);
|
||||
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));
|
||||
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);
|
||||
type_t *t = get_type(env, decl->value);
|
||||
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);
|
||||
if (t->tag == FunctionType) {
|
||||
@ -4489,7 +4486,7 @@ CORD compile_statement_namespace_header(env_t *env, ast_t *ast)
|
||||
t = Type(ClosureType, t);
|
||||
assert(t->tag != ModuleType);
|
||||
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(
|
||||
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 name = get_type_name(ret_t);
|
||||
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);
|
||||
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");
|
||||
|
@ -458,7 +458,7 @@ env_t *global_env(void)
|
||||
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;
|
||||
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;
|
||||
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++) {
|
||||
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;
|
||||
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: {
|
||||
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->next)
|
||||
@ -748,7 +746,7 @@ env_t *for_scope(env_t *env, ast_t *ast)
|
||||
}
|
||||
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));
|
||||
}
|
||||
|
||||
__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
|
||||
|
@ -5,6 +5,8 @@
|
||||
#include <gc/cord.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "stdlib/print.h"
|
||||
#include "stdlib/stdlib.h"
|
||||
#include "stdlib/tables.h"
|
||||
|
||||
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 *with_enum_scope(env_t *env, type_t *t);
|
||||
env_t *namespace_env(env_t *env, const char *namespace_name);
|
||||
__attribute__((format(printf, 4, 5)))
|
||||
_Noreturn void compiler_err(file_t *f, const char *start, const char *end, const char *fmt, ...);
|
||||
#define compiler_err(f, start, end, ...) ({ \
|
||||
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_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);
|
||||
|
184
src/parse.c
184
src/parse.c
@ -15,6 +15,8 @@
|
||||
#include "cordhelpers.h"
|
||||
#include "stdlib/integers.h"
|
||||
#include "stdlib/patterns.h"
|
||||
#include "stdlib/print.h"
|
||||
#include "stdlib/stdlib.h"
|
||||
#include "stdlib/tables.h"
|
||||
#include "stdlib/text.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 void whitespace(const char **pos);
|
||||
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 const char* get_word(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)
|
||||
//
|
||||
__attribute__((noreturn, format(printf, 4, 0)))
|
||||
static _Noreturn void vparser_err(parse_ctx_t *ctx, const char *start, const char *end, const char *fmt, va_list args) {
|
||||
if (USE_COLOR)
|
||||
fputs("\x1b[31;1;7m", stderr);
|
||||
fprintf(stderr, "%s:%ld.%ld: ", ctx->file->relative_filename, get_line_number(ctx->file, start),
|
||||
get_line_column(ctx->file, start));
|
||||
vfprintf(stderr, fmt, args);
|
||||
if (USE_COLOR)
|
||||
fputs(" \x1b[m", stderr);
|
||||
fputs("\n\n", stderr);
|
||||
|
||||
highlight_error(ctx->file, start, end, "\x1b[31;1;7m", 2, USE_COLOR);
|
||||
fputs("\n", stderr);
|
||||
|
||||
if (getenv("TOMO_STACKTRACE"))
|
||||
print_stack_trace(stderr, 1, 3);
|
||||
|
||||
if (ctx->on_err)
|
||||
longjmp(*ctx->on_err, 1);
|
||||
|
||||
raise(SIGABRT);
|
||||
exit(1);
|
||||
}
|
||||
#define parser_err(ctx, start, end, ...) ({ \
|
||||
if (USE_COLOR) \
|
||||
fputs("\x1b[31;1;7m", stderr); \
|
||||
fprint_inline(stderr, (ctx)->file->relative_filename, ":", get_line_number((ctx)->file, (start)), \
|
||||
".", get_line_column((ctx)->file, (start)), ": ", __VA_ARGS__); \
|
||||
if (USE_COLOR) \
|
||||
fputs(" \x1b[m", stderr); \
|
||||
fputs("\n\n", stderr); \
|
||||
highlight_error((ctx)->file, (start), (end), "\x1b[31;1;7m", 2, USE_COLOR); \
|
||||
fputs("\n", stderr); \
|
||||
if (getenv("TOMO_STACKTRACE")) \
|
||||
print_stack_trace(stderr, 1, 3); \
|
||||
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)))
|
||||
static _Noreturn void parser_err(parse_ctx_t *ctx, const char *start, const char *end, const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vparser_err(ctx, start, end, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
#define expect_str(ctx, start, pos, target, ...) ({ \
|
||||
spaces(pos); \
|
||||
if (!match(pos, target)) { \
|
||||
if (USE_COLOR) \
|
||||
fputs("\x1b[31;1;7m", stderr); \
|
||||
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
|
||||
@ -213,7 +251,7 @@ static const char *unescape(parse_ctx_t *ctx, const char **out) {
|
||||
uint32_t codepoint = unicode_name_character(name);
|
||||
if (codepoint == UNINAME_INVALID)
|
||||
parser_err(ctx, escape, escape + 3 + len,
|
||||
"Invalid unicode codepoint name: \"%s\"", name);
|
||||
"Invalid unicode codepoint name: ", quoted(name));
|
||||
*endpos = escape + 3 + len;
|
||||
char *str = GC_MALLOC_ATOMIC(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);
|
||||
}
|
||||
|
||||
//
|
||||
// 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) {
|
||||
const char *pos = *out;
|
||||
spaces(&pos);
|
||||
@ -1445,7 +1417,8 @@ PARSER(parse_text) {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -2283,8 +2256,7 @@ arg_ast_t *parse_args(parse_ctx_t *ctx, const char **pos)
|
||||
}
|
||||
if (!names) break;
|
||||
if (!default_val && !type)
|
||||
parser_err(ctx, batch_start, *pos, "I expected a ':' and type, or '=' and a default value after this parameter (%s)",
|
||||
names->name);
|
||||
parser_err(ctx, batch_start, *pos, "I expected a ':' and type, or '=' and a default value after this parameter (", names->name, ")");
|
||||
|
||||
REVERSE_LIST(names);
|
||||
for (; names; names = names->next)
|
||||
@ -2512,12 +2484,12 @@ PARSER(parse_use) {
|
||||
Array_t m = Text$matches(text, Pattern("{url}"));
|
||||
if (m.length >= 0) {
|
||||
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;
|
||||
char *hash = GC_MALLOC_ATOMIC(HASH_LEN + 1);
|
||||
size_t just_read = fread(hash, sizeof(char), HASH_LEN, shasum);
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -2528,7 +2500,7 @@ ast_t *parse_file(const char *path, jmp_buf *on_err) {
|
||||
if (path[0] != '<') {
|
||||
const char *resolved = resolve_path(path, ".", ".");
|
||||
if (!resolved)
|
||||
errx(1, "Could not resolve path: %s", path);
|
||||
print_err("Could not resolve path: ", path);
|
||||
path = resolved;
|
||||
}
|
||||
// NOTE: this cache leaks a bounded amount of memory. The cache will never
|
||||
|
91
src/repl.c
91
src/repl.c
@ -10,6 +10,7 @@
|
||||
|
||||
#include "stdlib/tomo.h"
|
||||
#include "stdlib/util.h"
|
||||
#include "stdlib/print.h"
|
||||
#include "typecheck.h"
|
||||
#include "parse.h"
|
||||
|
||||
@ -43,7 +44,7 @@ void repl(void)
|
||||
{
|
||||
env_t *env = global_env();
|
||||
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;
|
||||
char *line = NULL;
|
||||
@ -84,33 +85,33 @@ void repl(void)
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
__attribute__((noreturn, format(printf, 2, 3)))
|
||||
static void repl_err(ast_t *node, const char *fmt, ...)
|
||||
{
|
||||
bool color = isatty(STDERR_FILENO) && !getenv("NO_COLOR");
|
||||
if (color)
|
||||
fputs("\x1b[31;7;1m", stderr);
|
||||
if (node)
|
||||
fprintf(stderr, "%s:%ld.%ld: ", node->file->relative_filename, get_line_number(node->file, node->start),
|
||||
get_line_column(node->file, node->start));
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vfprintf(stderr, fmt, args);
|
||||
va_end(args);
|
||||
if (color)
|
||||
fputs(" \x1b[m", stderr);
|
||||
fputs("\n\n", stderr);
|
||||
if (node)
|
||||
highlight_error(node->file, node->start, node->end, "\x1b[31;1m", 2, color);
|
||||
// __attribute__((noreturn, format(printf, 2, 3)))
|
||||
// static void repl_err(ast_t *node, const char *fmt, ...)
|
||||
// {
|
||||
// bool color = isatty(STDERR_FILENO) && !getenv("NO_COLOR");
|
||||
// if (color)
|
||||
// fputs("\x1b[31;7;1m", stderr);
|
||||
// if (node)
|
||||
// fprintf(stderr, "%s:%ld.%ld: ", node->file->relative_filename, get_line_number(node->file, node->start),
|
||||
// get_line_column(node->file, node->start));
|
||||
// va_list args;
|
||||
// va_start(args, fmt);
|
||||
// vfprintf(stderr, fmt, args);
|
||||
// va_end(args);
|
||||
// if (color)
|
||||
// fputs(" \x1b[m", stderr);
|
||||
// fputs("\n\n", stderr);
|
||||
// if (node)
|
||||
// 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)
|
||||
{
|
||||
switch (t->tag) {
|
||||
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 MemoryType: return &Memory$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_IBITS16: return &Int16$info;
|
||||
case TYPE_IBITS8: return &Int8$info;
|
||||
default: errx(1, "Invalid bits");
|
||||
default: print_err("Invalid bits");
|
||||
}
|
||||
case NumType:
|
||||
switch (Match(t, NumType)->bits) {
|
||||
case TYPE_NBITS64: return &Num$info;
|
||||
case TYPE_NBITS32: return &Num32$info;
|
||||
default: errx(1, "Invalid bits");
|
||||
default: print_err("Invalid bits");
|
||||
}
|
||||
case TextType: return &Text$info;
|
||||
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);
|
||||
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) {
|
||||
case Var: {
|
||||
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;
|
||||
}
|
||||
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_IBITS16: return Int$from_int16(num.i16);
|
||||
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_IBITS16: return Num$from_int16(num.i16);
|
||||
case TYPE_IBITS8: return Num$from_int8(num.i8);
|
||||
default: errx(1, "Invalid int bits");
|
||||
default: print_err("Invalid int bits");
|
||||
}
|
||||
}
|
||||
case NumType: {
|
||||
@ -210,7 +211,7 @@ static double ast_to_num(env_t *env, ast_t *ast)
|
||||
eval(env, ast, &num);
|
||||
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_val = get_type(env, val->ast);
|
||||
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 (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;
|
||||
repl_err(subject, "This is an immutable value, you can't assign to it");
|
||||
// ast_t *subject = target->ast->tag == Index ? Match(target->ast, Index)->indexed : Match(target->ast, FieldAccess)->fielded;
|
||||
print_err("This is an immutable value, you can't assign to it");
|
||||
} 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);
|
||||
// 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;
|
||||
@ -279,7 +280,7 @@ void run(env_t *env, ast_t *ast)
|
||||
void *value = GC_MALLOC(size);
|
||||
eval(env, doctest->expr, value);
|
||||
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);
|
||||
}
|
||||
break;
|
||||
@ -354,7 +355,7 @@ void eval(env_t *env, ast_t *ast, void *dest)
|
||||
if (!dest) return;
|
||||
repl_binding_t *b = get_repl_binding(env, Match(ast, Var)->name);
|
||||
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);
|
||||
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 16: *(int16_t*)dest = Int16$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; \
|
||||
}
|
||||
@ -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: {
|
||||
type_t *t_lhs = get_type(env, binop->lhs);
|
||||
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);
|
||||
size_t value_size = type_size(t_lhs);
|
||||
char lhs[value_size], rhs[value_size];
|
||||
@ -455,7 +456,7 @@ void eval(env_t *env, ast_t *ast, void *dest)
|
||||
}
|
||||
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;
|
||||
}
|
||||
@ -471,8 +472,8 @@ void eval(env_t *env, ast_t *ast, void *dest)
|
||||
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)
|
||||
repl_err(index->index, "%ld is an invalid index for an array with length %ld",
|
||||
raw_index, arr.length);
|
||||
print_err(raw_index,
|
||||
" 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);
|
||||
memcpy(dest, arr.data + arr.stride*(index_int-1), item_size);
|
||||
break;
|
||||
@ -496,7 +497,7 @@ void eval(env_t *env, ast_t *ast, void *dest)
|
||||
memcpy(dest, pointer, pointed_size);
|
||||
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;
|
||||
}
|
||||
@ -544,7 +545,7 @@ void eval(env_t *env, ast_t *ast, void *dest)
|
||||
break;
|
||||
}
|
||||
default:
|
||||
errx(1, "Eval not implemented for %W", ast);
|
||||
print_err("Eval not implemented for ", ast_to_str(ast));
|
||||
}
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
|
@ -57,7 +57,7 @@ public void Array$insert(Array_t *arr, const void *item, Int_t int_index, int64_
|
||||
|
||||
if (index < 1) index = 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) {
|
||||
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;
|
||||
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
|
||||
&& 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)
|
||||
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;
|
||||
for (int64_t i = 0; i < weights.length && i < arr.length; i++) {
|
||||
|
@ -14,7 +14,7 @@
|
||||
const Array_t arr = arr_expr; int64_t index = index_expr; \
|
||||
int64_t off = index + (index < 0) * (arr.length + 1) - 1; \
|
||||
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);})
|
||||
#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; \
|
||||
@ -23,7 +23,7 @@
|
||||
Array_t *arr = arr_expr; int64_t index = index_expr; \
|
||||
int64_t off = index + (index < 0) * (arr->length + 1) - 1; \
|
||||
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) \
|
||||
Array$compact(arr, sizeof(item_type)); \
|
||||
(item_type*)(arr->data + arr->stride * off); })
|
||||
|
@ -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) {
|
||||
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)
|
||||
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);
|
||||
}
|
||||
public PUREFUNC Byte_t Byte$from_int64(Int64_t i, bool truncate) {
|
||||
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;
|
||||
}
|
||||
public PUREFUNC Byte_t Byte$from_int32(Int32_t i, bool truncate) {
|
||||
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;
|
||||
}
|
||||
public PUREFUNC Byte_t Byte$from_int16(Int16_t i, bool truncate) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,8 @@ typedef union {
|
||||
mpz_t *big;
|
||||
} Int_t;
|
||||
|
||||
#define OptionalInt_t Int_t
|
||||
|
||||
typedef struct {
|
||||
void *data;
|
||||
// All of the following fields add up to 64 bits, which means that array
|
||||
@ -117,4 +119,10 @@ typedef struct MutexedData_s {
|
||||
void *data;
|
||||
} *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
|
||||
|
@ -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);
|
||||
int64_t line_num = get_function_line_num(*(void**)fn);
|
||||
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)
|
||||
text = Text$concat(Text("\x1b[32;1m"), text, Text("\x1b[m"));
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <gmp.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "arrays.h"
|
||||
@ -16,9 +17,18 @@
|
||||
#include "text.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) {
|
||||
if (likely(i.small & 1L)) {
|
||||
return Text$format("%ld", (i.small)>>2L);
|
||||
return Text$format("%ld", i.small>>2L);
|
||||
} else {
|
||||
char *str = mpz_get_str(NULL, 10, *i.big);
|
||||
return Text$from_str(str);
|
||||
@ -416,7 +426,7 @@ public Int_t Int$prev_prime(Int_t x)
|
||||
mpz_t p;
|
||||
mpz_init_set_int(p, x);
|
||||
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);
|
||||
}
|
||||
#endif
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <gmp.h>
|
||||
|
||||
#include "print.h"
|
||||
#include "datatypes.h"
|
||||
#include "stdlib.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$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$value_as_text(Int_t i);
|
||||
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_init_set_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);
|
||||
}
|
||||
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) {
|
||||
int64_t i64 = (int64_t)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;
|
||||
}
|
||||
MACROLIKE PUREFUNC Int64_t Int64$from_num32(Num32_t n, bool truncate) {
|
||||
int64_t i64 = (int64_t)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;
|
||||
}
|
||||
MACROLIKE PUREFUNC Int64_t Int64$from_int(Int_t i, bool truncate) {
|
||||
if likely (i.small & 1L)
|
||||
return (int64_t)(i.small >> 2L);
|
||||
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);
|
||||
}
|
||||
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) {
|
||||
int32_t i32 = (int32_t)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;
|
||||
}
|
||||
MACROLIKE PUREFUNC Int32_t Int32$from_num32(Num32_t n, bool truncate) {
|
||||
int32_t i32 = (int32_t)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;
|
||||
}
|
||||
MACROLIKE PUREFUNC Int32_t Int32$from_int(Int_t i, bool truncate) {
|
||||
int64_t i64 = Int64$from_int(i, truncate);
|
||||
int32_t i32 = (int32_t)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;
|
||||
}
|
||||
MACROLIKE PUREFUNC Int32_t Int32$from_int64(Int64_t i64, bool truncate) {
|
||||
int32_t i32 = (int32_t)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;
|
||||
}
|
||||
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) {
|
||||
int16_t i16 = (int16_t)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;
|
||||
}
|
||||
MACROLIKE PUREFUNC Int16_t Int16$from_num32(Num32_t n, bool truncate) {
|
||||
int16_t i16 = (int16_t)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;
|
||||
}
|
||||
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) {
|
||||
int16_t i16 = (int16_t)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;
|
||||
}
|
||||
MACROLIKE PUREFUNC Int16_t Int16$from_int32(Int32_t i32, bool truncate) {
|
||||
int16_t i16 = (int16_t)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;
|
||||
}
|
||||
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) {
|
||||
int8_t i8 = (int8_t)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;
|
||||
}
|
||||
MACROLIKE PUREFUNC Int8_t Int8$from_num32(Num32_t n, bool truncate) {
|
||||
int8_t i8 = (int8_t)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;
|
||||
}
|
||||
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) {
|
||||
int8_t i8 = (int8_t)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;
|
||||
}
|
||||
MACROLIKE PUREFUNC Int8_t Int8$from_int32(Int32_t i32, bool truncate) {
|
||||
int8_t i8 = (int8_t)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;
|
||||
}
|
||||
MACROLIKE PUREFUNC Int8_t Int8$from_int16(Int16_t i16, bool truncate) {
|
||||
int8_t i8 = (int8_t)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;
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
|
@ -111,14 +111,14 @@ __attribute__((noreturn))
|
||||
public void cannot_serialize(const void*, FILE*, Table_t*, const TypeInfo_t *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))
|
||||
public void cannot_deserialize(FILE*, void*, Array_t*, const TypeInfo_t *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
|
||||
|
@ -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 *fmt = Text$as_c_string(format);
|
||||
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);
|
||||
if (!invalid || invalid[0] != '\0')
|
||||
|
@ -20,7 +20,8 @@ static Text_t MutexedData$as_text(const void *m, bool colorize, const TypeInfo_t
|
||||
if (!m) {
|
||||
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 *)
|
||||
|
@ -42,7 +42,7 @@ MACROLIKE CONSTFUNC double Num$from_int(Int_t i, bool truncate) {
|
||||
if likely (i.small & 0x1) {
|
||||
double ret = (double)(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;
|
||||
} else {
|
||||
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_init_set_d(roundtrip, ret);
|
||||
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;
|
||||
}
|
||||
@ -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) {
|
||||
double n = (double)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;
|
||||
}
|
||||
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) {
|
||||
float ret = (float)(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;
|
||||
} else {
|
||||
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_init_set_d(roundtrip, ret);
|
||||
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;
|
||||
}
|
||||
@ -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) {
|
||||
float n = (float)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;
|
||||
}
|
||||
MACROLIKE CONSTFUNC float Num32$from_int32(Int32_t i, bool truncate) {
|
||||
float n = (float)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;
|
||||
}
|
||||
MACROLIKE CONSTFUNC float Num32$from_int16(Int16_t i) { return (float)i; }
|
||||
|
@ -10,12 +10,6 @@
|
||||
#include "types.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_BOOL ((OptionalBool_t)2)
|
||||
#define NONE_INT ((OptionalInt_t){.small=0})
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <unistd.h>
|
||||
#include <unistr.h>
|
||||
|
||||
#include "print.h"
|
||||
#include "arrays.h"
|
||||
#include "enums.h"
|
||||
#include "files.h"
|
||||
@ -122,8 +123,7 @@ public Path_t Path$_concat(int n, Path_t items[n])
|
||||
ARRAY_INCREF(result.components);
|
||||
for (int i = 1; i < n; i++) {
|
||||
if (items[i].type.$tag != PATH_RELATIVE)
|
||||
fail("Cannot concatenate an absolute or home-based path onto another path: (%s)\n",
|
||||
Path$as_c_string(items[i]));
|
||||
fail("Cannot concatenate an absolute or home-based path onto another path: (", items[i], ")");
|
||||
Array$insert_all(&result.components, items[i].components, I(0), sizeof(Text_t));
|
||||
}
|
||||
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)
|
||||
{
|
||||
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)",
|
||||
(Text_t[1]){Path$as_text(&path, false, &Path$info)}, (Text_t[1]){Path$as_text(&relative_to, false, &Path$info)});
|
||||
fail("Cannot create a path relative to a different path with a mismatching type: (", path, ") relative to (", relative_to, ")");
|
||||
|
||||
Path_t result = {.type.$tag=PATH_RELATIVE};
|
||||
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);
|
||||
int fd = open(path_str, mode, permissions);
|
||||
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)
|
||||
Array$compact(&bytes, 1);
|
||||
ssize_t written = write(fd, bytes.data, (size_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);
|
||||
}
|
||||
|
||||
@ -328,7 +327,7 @@ public OptionalArray_t Path$read_bytes(Path_t path, OptionalInt_t count)
|
||||
content[sb.st_size] = '\0';
|
||||
close(fd);
|
||||
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;
|
||||
return (Array_t){.data=content, .atomic=1, .stride=1, .length=len};
|
||||
} else {
|
||||
@ -358,7 +357,7 @@ public OptionalArray_t Path$read_bytes(Path_t path, OptionalInt_t count)
|
||||
}
|
||||
close(fd);
|
||||
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};
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
if (owner.length >= 0) {
|
||||
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;
|
||||
}
|
||||
|
||||
gid_t group_id = (gid_t)-1;
|
||||
if (group.length >= 0) {
|
||||
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;
|
||||
}
|
||||
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) {
|
||||
case FTW_F: case FTW_SL: case FTW_SLN:
|
||||
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 0;
|
||||
case FTW_DP:
|
||||
if (rmdir(path) != 0)
|
||||
fail("Could not remove directory: %s (%s)", path, strerror(errno));
|
||||
fail("Could not remove directory: ", path, " (", strerror(errno), ")");
|
||||
return 0;
|
||||
default:
|
||||
printf("Type: %d\n", type);
|
||||
fail("Could not remove path: %s (not a file or directory)", path, strerror(errno));
|
||||
fail("Could not remove path: ", path, " (not a file or directory)");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -437,19 +435,19 @@ public void Path$remove(Path_t path, bool ignore_missing)
|
||||
struct stat sb;
|
||||
if (lstat(path_str, &sb) != 0) {
|
||||
if (!ignore_missing)
|
||||
fail("Could not remove file: %s (%s)", path_str, strerror(errno));
|
||||
fail("Could not remove file: ", path_str, " (", strerror(errno), ")");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((sb.st_mode & S_IFMT) == S_IFREG || (sb.st_mode & S_IFMT) == S_IFLNK) {
|
||||
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) {
|
||||
const int num_open_fd = 10;
|
||||
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));
|
||||
} 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);
|
||||
int status = mkdir(c_path, (mode_t)permissions);
|
||||
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)
|
||||
@ -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);
|
||||
DIR *d = opendir(path_str);
|
||||
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] == '/')
|
||||
--path_len;
|
||||
@ -516,13 +514,13 @@ public Path_t Path$unique_directory(Path_t path)
|
||||
path = Path$expand_home(path);
|
||||
const char *path_str = Path$as_c_string(path);
|
||||
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] = {};
|
||||
strcpy(buf, path_str);
|
||||
if (buf[len-1] == '/')
|
||||
buf[--len] = '\0';
|
||||
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);
|
||||
}
|
||||
|
||||
@ -531,7 +529,7 @@ public Path_t Path$write_unique_bytes(Path_t path, Array_t bytes)
|
||||
path = Path$expand_home(path);
|
||||
const char *path_str = Path$as_c_string(path);
|
||||
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] = {};
|
||||
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);
|
||||
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)
|
||||
Array$compact(&bytes, 1);
|
||||
|
||||
ssize_t written = write(fd, bytes.data, (size_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);
|
||||
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));
|
||||
}
|
||||
|
||||
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.type.$tag == PATH_ABSOLUTE) return "/";
|
||||
else if (path.type.$tag == PATH_RELATIVE) return ".";
|
||||
else if (path.type.$tag == PATH_HOME) return "~";
|
||||
if (path.type.$tag == PATH_ABSOLUTE) return fputs("/", f);
|
||||
else if (path.type.$tag == PATH_RELATIVE) return fputs(".", f);
|
||||
else if (path.type.$tag == PATH_HOME) return fputs("~", f);
|
||||
}
|
||||
|
||||
size_t len = 0, capacity = 16;
|
||||
char *buf = GC_MALLOC_ATOMIC(capacity);
|
||||
int n = 0;
|
||||
if (path.type.$tag == PATH_ABSOLUTE) {
|
||||
buf[len++] = '/';
|
||||
n += fputc('/', f);
|
||||
} else if (path.type.$tag == PATH_HOME) {
|
||||
buf[len++] = '~';
|
||||
buf[len++] = '/';
|
||||
n += fputs("~/", f);
|
||||
} else if (path.type.$tag == PATH_RELATIVE) {
|
||||
if (!Text$equal_values(*(Text_t*)path.components.data, Text(".."))) {
|
||||
buf[len++] = '.';
|
||||
buf[len++] = '/';
|
||||
}
|
||||
if (!Text$equal_values(*(Text_t*)path.components.data, Text("..")))
|
||||
n += fputs("./", f);
|
||||
}
|
||||
|
||||
for (int64_t i = 0; i < path.components.length; i++) {
|
||||
Text_t *comp = (Text_t*)(path.components.data + i*path.components.stride);
|
||||
const char *comp_str = Text$as_c_string(*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;
|
||||
n += Text$print(f, *comp);
|
||||
if (i + 1 < path.components.length)
|
||||
buf[len++] = '/';
|
||||
n += fputc('/', f);
|
||||
}
|
||||
buf[len++] = '\0';
|
||||
return buf;
|
||||
return n;
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
Path_t Path$from_str(const char *str);
|
||||
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);
|
||||
#define Path(str) Path$from_str(str)
|
||||
Path_t Path$_concat(int n, Path_t items[n]);
|
||||
|
@ -518,7 +518,7 @@ static pat_t parse_next_pat(TextIter_t *state, int64_t *index)
|
||||
int32_t close = open;
|
||||
uc_mirror_char((ucs4_t)open, (ucs4_t*)&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){
|
||||
.tag=PAT_QUOTE,
|
||||
@ -533,7 +533,7 @@ static pat_t parse_next_pat(TextIter_t *state, int64_t *index)
|
||||
int32_t close = open;
|
||||
uc_mirror_char((ucs4_t)open, (ucs4_t*)&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){
|
||||
.tag=PAT_PAIR,
|
||||
@ -553,7 +553,7 @@ static pat_t parse_next_pat(TextIter_t *state, int64_t *index)
|
||||
} else {
|
||||
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 {
|
||||
min = -1, max = -1;
|
||||
}
|
||||
@ -573,19 +573,19 @@ static pat_t parse_next_pat(TextIter_t *state, int64_t *index)
|
||||
skip_whitespace(state, index);
|
||||
int32_t grapheme = Text$get_grapheme_fast(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);
|
||||
} else if (strlen(prop_name) == 1) {
|
||||
// Single letter names: {1+ A}
|
||||
skip_whitespace(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]);
|
||||
}
|
||||
|
||||
skip_whitespace(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])) {
|
||||
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);
|
||||
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);
|
||||
#undef PAT
|
||||
} else {
|
||||
@ -942,14 +942,14 @@ static Text_t apply_backrefs(Text_t text, Array_t recursive_replacements, Text_t
|
||||
pos += 1;
|
||||
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);
|
||||
|
||||
if (Text$get_grapheme_fast(&replacement_state, pos + backref_len) == ';')
|
||||
backref_len += 1; // skip optional semicolon
|
||||
|
||||
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));
|
||||
|
||||
|
@ -3,7 +3,6 @@
|
||||
// The type representing text patterns for pattern matching.
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <printf.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "datatypes.h"
|
||||
|
76
src/stdlib/print.c
Normal file
76
src/stdlib/print.c
Normal 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
166
src/stdlib/print.h
Normal 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
|
@ -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);
|
||||
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 (%k) is larger than the maximum value (%k)",
|
||||
&min_text, &max_text);
|
||||
}
|
||||
if (cmp > 0)
|
||||
fail("Random minimum value (", min, ") is larger than the maximum value (", max, ")");
|
||||
if (cmp == 0) return min;
|
||||
|
||||
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)
|
||||
{
|
||||
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 == INT64_MIN && max == INT64_MAX) {
|
||||
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)
|
||||
{
|
||||
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 == INT32_MIN && max == INT32_MAX) {
|
||||
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)
|
||||
{
|
||||
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 == INT16_MIN && max == INT16_MAX) {
|
||||
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)
|
||||
{
|
||||
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 == INT8_MIN && max == INT8_MAX) {
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
||||
union {
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <sys/random.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "print.h"
|
||||
#include "bools.h"
|
||||
#include "files.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};
|
||||
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;
|
||||
sigact.sa_sigaction = signal_handler;
|
||||
sigemptyset(&sigact.sa_mask);
|
||||
@ -136,7 +134,7 @@ static bool parse_single_arg(const TypeInfo_t *info, char *arg, void *dest)
|
||||
|
||||
// Single-argument tag:
|
||||
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);
|
||||
if (named.type->align > 0 && offset % (size_t)named.type->align > 0)
|
||||
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;
|
||||
}
|
||||
}
|
||||
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) {
|
||||
if (info->StructInfo.num_fields == 0)
|
||||
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);
|
||||
|
||||
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) {
|
||||
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) {
|
||||
errx(1, "Table arguments must be specified as `--flag ...` not `--flag=...`");
|
||||
print_err("Table arguments must be specified as `--flag ...` not `--flag=...`");
|
||||
} else {
|
||||
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++) {
|
||||
bool success = parse_single_arg(item_info, args[i], items.data + items.stride*i);
|
||||
if (!success)
|
||||
errx(1, "Couldn't parse argument: %s", args[i]);
|
||||
print_err("Couldn't parse argument: ", args[i]);
|
||||
}
|
||||
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);
|
||||
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);
|
||||
if (!success)
|
||||
errx(1, "Couldn't parse table value: %s", value_arg);
|
||||
print_err("Couldn't parse table value: ", value_arg);
|
||||
|
||||
*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;
|
||||
} else {
|
||||
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;
|
||||
populated_args[s] = parse_single_arg(spec[s].type, argv[i+1], spec[s].dest);
|
||||
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;
|
||||
} else if (after_name == '=') { // --foo=val
|
||||
used_args[i] = true;
|
||||
populated_args[s] = parse_single_arg(spec[s].type, 2 + argv[i] + strlen(spec[s].name) + 1, spec[s].dest);
|
||||
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;
|
||||
} else {
|
||||
continue;
|
||||
@ -303,10 +301,11 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help,
|
||||
say(help, true);
|
||||
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
|
||||
used_args[i] = true;
|
||||
for (char *f = argv[i] + 1; *f; f++) {
|
||||
char flag[] = {'-', *f, 0};
|
||||
for (int s = 0; s < spec_len; s++) {
|
||||
if (spec[s].name[0] != *f || strlen(spec[s].name) > 1)
|
||||
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;
|
||||
|
||||
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;
|
||||
while (i + 1 + num_args < argc) {
|
||||
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;
|
||||
*(OptionalBool_t*)spec[s].dest = true;
|
||||
} 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;
|
||||
populated_args[s] = parse_single_arg(spec[s].type, argv[i+1], spec[s].dest);
|
||||
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;
|
||||
}
|
||||
@ -353,7 +352,7 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help,
|
||||
say(help, true);
|
||||
exit(0);
|
||||
}
|
||||
errx(1, "Unrecognized flag: -%c\n%k", *f, &usage);
|
||||
print_err("Unrecognized flag: ", flag, "\n", usage);
|
||||
next_flag:;
|
||||
}
|
||||
} else {
|
||||
@ -380,7 +379,7 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help,
|
||||
next_non_bool_flag:
|
||||
++s;
|
||||
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;
|
||||
@ -416,7 +415,7 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help,
|
||||
}
|
||||
|
||||
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++) {
|
||||
@ -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)
|
||||
*(OptionalTable_t*)spec[s].dest = (Table_t){};
|
||||
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
|
||||
// really matter if we only print stack lines when there's a crash.
|
||||
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)
|
||||
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) {
|
||||
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");
|
||||
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';
|
||||
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
void print_stack_trace(FILE *out, int start, int stop)
|
||||
public void print_stack_trace(FILE *out, int start, int stop)
|
||||
{
|
||||
// Print stack trace:
|
||||
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)
|
||||
{
|
||||
fail("%k", &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);
|
||||
fail(message);
|
||||
}
|
||||
|
||||
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) {
|
||||
Text_t expr_text = generic_as_text(expr, USE_COLOR, type);
|
||||
Text_t type_name = generic_as_text(NULL, false, type);
|
||||
|
||||
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);
|
||||
if (!success) {
|
||||
print_stack_trace(stderr, 2, 4);
|
||||
fprintf(stderr,
|
||||
USE_COLOR
|
||||
? "\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==================== TEST FAILED ====================\n\nYou expected: %k\n But I got: %k\n\n",
|
||||
&expected_text, &expr_text);
|
||||
if (USE_COLOR) {
|
||||
fprint(stderr,
|
||||
"\n\x1b[31;7m ==================== TEST FAILED ==================== \x1b[0;1m\n\n"
|
||||
"You expected: \x1b[m", expected_text, "\x1b[0m\n"
|
||||
"\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);
|
||||
raise(SIGABRT);
|
||||
|
@ -7,6 +7,8 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "datatypes.h"
|
||||
#include "files.h"
|
||||
#include "print.h"
|
||||
#include "types.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]);
|
||||
#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__})
|
||||
__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);
|
||||
__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();
|
||||
__attribute__((nonnull))
|
||||
void start_inspect(const char *filename, int64_t start, int64_t end);
|
||||
|
@ -55,7 +55,6 @@
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <gc.h>
|
||||
#include <printf.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.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 > text.length || index < 1)
|
||||
fail("Invalid index: %ld is beyond the length of the text (length = %ld)",
|
||||
Int64$from_int(index_int, false), text.length);
|
||||
fail("Invalid index: ", index_int, " is beyond the length of the text (length = ", (int64_t)text.length, ")");
|
||||
|
||||
while (text.tag == TEXT_CONCAT) {
|
||||
if (index <= text.left->length)
|
||||
@ -1104,24 +1102,6 @@ public Text_t Text$title(Text_t text, Text_t language)
|
||||
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)
|
||||
{
|
||||
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_start(args, fmt);
|
||||
|
||||
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);
|
||||
int len = vsnprintf(NULL, 0, fmt, 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;
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
// Raku's string representation and libunistr
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <printf.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "datatypes.h"
|
||||
@ -25,12 +24,9 @@ typedef struct {
|
||||
|
||||
#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})
|
||||
|
||||
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]);
|
||||
#define Text$concat(...) Text$_concat(sizeof((Text_t[]){__VA_ARGS__})/sizeof(Text_t), (Text_t[]){__VA_ARGS__})
|
||||
#define Texts(...) Text$concat(__VA_ARGS__)
|
||||
|
125
src/tomo.c
125
src/tomo.c
@ -4,7 +4,6 @@
|
||||
#include <gc.h>
|
||||
#include <gc/cord.h>
|
||||
#include <libgen.h>
|
||||
#include <printf.h>
|
||||
#include <spawn.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -24,12 +23,13 @@
|
||||
#include "stdlib/optionals.h"
|
||||
#include "stdlib/patterns.h"
|
||||
#include "stdlib/paths.h"
|
||||
#include "stdlib/print.h"
|
||||
#include "stdlib/text.h"
|
||||
#include "typecheck.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 array_str(arr) Text$as_c_string(Text$join(Text(" "), arr))
|
||||
#define run_cmd(...) ({ const char *_cmd = String(__VA_ARGS__); if (verbose) print("\033[34;1m", _cmd, "\033[m"); popen(_cmd, "w"); })
|
||||
#define array_text(arr) Text$join(Text(" "), arr)
|
||||
|
||||
static struct stat compiler_stat;
|
||||
|
||||
@ -100,20 +100,13 @@ int main(int argc, char *argv[])
|
||||
if (getenv("NO_COLOR") && getenv("NO_COLOR")[0] != '\0')
|
||||
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:
|
||||
if ((streq(argv[1], "-r") || streq(argv[1], "--run")) && argc >= 3) {
|
||||
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]);
|
||||
}
|
||||
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"
|
||||
@ -168,8 +161,8 @@ int main(int argc, char *argv[])
|
||||
|
||||
for (int64_t i = 0; i < uninstall.length; i++) {
|
||||
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));
|
||||
printf("Uninstalled %k\n", u);
|
||||
system(String("rm -rvf ~/.local/share/tomo/installed/", *u, " ~/.local/share/tomo/lib/lib", *u, ".so"));
|
||||
print("Uninstalled ", *u);
|
||||
}
|
||||
|
||||
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);
|
||||
char *cwd = get_current_dir_name();
|
||||
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 *libdirname = basename(libdir);
|
||||
@ -205,9 +198,9 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
if (files.length < 1)
|
||||
errx(1, "No file specified!");
|
||||
print_err("No file specified!");
|
||||
else if (files.length != 1)
|
||||
errx(1, "Too many files specified!");
|
||||
print_err("Too many files specified!");
|
||||
|
||||
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[1 + args.length] = NULL;
|
||||
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;
|
||||
@ -253,7 +246,7 @@ int main(int argc, char *argv[])
|
||||
for (int64_t i = 0; i < files.length; i++) {
|
||||
Path_t path = *(Path_t*)(files.data + i*files.stride);
|
||||
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;
|
||||
@ -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));
|
||||
|
||||
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);
|
||||
|
||||
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));
|
||||
|
||||
// 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("#include <tomo/tomo.h>\n", header);
|
||||
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);
|
||||
}
|
||||
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:
|
||||
unlink(".build/symbol_renames.txt");
|
||||
FILE *prog;
|
||||
for (int64_t i = 0; i < tm_files.length; i++) {
|
||||
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",
|
||||
Path$as_c_string(build_file(f, ".o")), CORD_to_const_char_star(env->libname));
|
||||
if (!prog) errx(1, "Could not find symbols!");
|
||||
prog = run_cmd("nm -Ug -fjust-symbols '", build_file(f, ".o"), "' "
|
||||
"| sed -n 's/_\\$\\(.*\\)/\\0 _$", CORD_to_const_char_star(env->libname),
|
||||
"$\\1/p' >>.build/symbol_renames.txt");
|
||||
if (!prog) print_err("Could not find symbols!");
|
||||
int status = pclose(prog);
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
|
||||
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'",
|
||||
&cc, &optimization, &cflags, &ldflags, &ldlibs, array_str(extra_ldlibs), &lib_dir_name,
|
||||
paths_str(object_files), &lib_dir_name);
|
||||
prog = run_cmd(cc, " -O", optimization, " ", cflags, " ", ldflags, " ", ldlibs, " ", array_text(extra_ldlibs),
|
||||
" -Wl,-soname='lib", lib_dir_name, ".so' -shared ", paths_str(object_files), " -o 'lib", lib_dir_name, ".so'");
|
||||
if (!prog)
|
||||
errx(1, "Failed to run C compiler: %k", &cc);
|
||||
print_err("Failed to run C compiler: ", cc);
|
||||
int status = pclose(prog);
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
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);
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
|
||||
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);
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
|
||||
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) {
|
||||
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)) {
|
||||
system(heap_strf("rm -rf '%s'", dest));
|
||||
system(heap_strf("mkdir -p '%s'", dest));
|
||||
system(heap_strf("cp -r * '%s/'", dest));
|
||||
system(String("rm -rf '", dest, "'"));
|
||||
system(String("mkdir -p '", dest, "'"));
|
||||
system(String("cp -r * '", dest, "/'"));
|
||||
}
|
||||
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",
|
||||
&lib_dir_name, &lib_dir_name, &lib_dir_name));
|
||||
printf("Installed \033[1m%k\033[m to ~/.local/share/tomo/installed\n", &lib_dir_name);
|
||||
system(String("ln -f -s ../installed/'", lib_dir_name, "'/lib'", lib_dir_name,
|
||||
"'.so ~/.local/share/tomo/lib/lib'", lib_dir_name, "'.so"));
|
||||
print("Installed \033[1m", lib_dir_name, "\033[m to ~/.local/share/tomo/installed");
|
||||
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);
|
||||
Text_t extension = Path$extension(filename, true);
|
||||
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("./"));
|
||||
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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
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) {
|
||||
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);
|
||||
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++) {
|
||||
Path_t *child = (Path_t*)(children.data + i*children.stride);
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -613,7 +606,7 @@ void transpile_header(env_t *base_env, Path_t path)
|
||||
Path_t h_filename = build_file(path, ".h");
|
||||
ast_t *ast = parse_file(Path$as_c_string(path), NULL);
|
||||
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);
|
||||
|
||||
@ -621,16 +614,16 @@ void transpile_header(env_t *base_env, Path_t path)
|
||||
|
||||
FILE *header = fopen(Path$as_c_string(h_filename), "w");
|
||||
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);
|
||||
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)
|
||||
printf("Transpiled header:\t%s\n", Path$as_c_string(h_filename));
|
||||
print("Transpiled header:\t", h_filename);
|
||||
|
||||
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)
|
||||
@ -638,7 +631,7 @@ void transpile_code(env_t *base_env, Path_t path)
|
||||
Path_t c_filename = build_file(path, ".c");
|
||||
ast_t *ast = parse_file(Path$as_c_string(path), NULL);
|
||||
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);
|
||||
|
||||
@ -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");
|
||||
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);
|
||||
|
||||
@ -654,7 +647,9 @@ void transpile_code(env_t *base_env, Path_t path)
|
||||
if (main_binding && main_binding->type->tag == FunctionType) {
|
||||
type_t *ret = Match(main_binding->type, FunctionType)->ret;
|
||||
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(
|
||||
"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)
|
||||
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)
|
||||
printf("Transpiled code:\t%s\n", Path$as_c_string(c_filename));
|
||||
print("Transpiled code:\t", c_filename);
|
||||
|
||||
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)
|
||||
@ -682,31 +677,29 @@ void compile_object_file(Path_t path)
|
||||
Path_t obj_file = build_file(path, ".o");
|
||||
Path_t c_file = build_file(path, ".c");
|
||||
|
||||
FILE *prog = run_cmd("%k %k -O%k -c %s -o %s",
|
||||
&cc, &cflags, &optimization, Path$as_c_string(c_file), Path$as_c_string(obj_file));
|
||||
FILE *prog = run_cmd(cc, " ", cflags, " -O", optimization, " -c ", c_file, " -o ", obj_file);
|
||||
if (!prog)
|
||||
errx(1, "Failed to run C compiler: %k", &cc);
|
||||
print_err("Failed to run C compiler: ", cc);
|
||||
int status = pclose(prog);
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
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)
|
||||
{
|
||||
ast_t *ast = parse_file(Path$as_c_string(path), NULL);
|
||||
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);
|
||||
binding_t *main_binding = get_binding(env, "main");
|
||||
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",
|
||||
&cc, &cflags, &optimization, &ldflags, &ldlibs,
|
||||
array_str(extra_ldlibs), paths_str(object_files), Path$as_c_string(exe_path));
|
||||
FILE *runner = run_cmd(cc, " ", cflags, " -O", optimization, " ", ldflags, " ", ldlibs, " ", array_text(extra_ldlibs), " ",
|
||||
paths_str(object_files), " -x c - -o ", exe_path);
|
||||
CORD program = CORD_all(
|
||||
"extern int ", main_binding->code, "$parse_and_run(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) {
|
||||
FILE *out = run_cmd("%k", &show_codegen);
|
||||
FILE *out = run_cmd(show_codegen);
|
||||
CORD_put(program, 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);
|
||||
|
||||
if (!quiet)
|
||||
printf("Compiled executable:\t%s\n", Path$as_c_string(exe_path));
|
||||
print("Compiled executable:\t", exe_path);
|
||||
return exe_path;
|
||||
}
|
||||
|
||||
|
170
src/typecheck.c
170
src/typecheck.c
@ -32,7 +32,7 @@ type_t *parse_type_ast(env_t *env, type_ast_t *ast)
|
||||
char *module_name = GC_strndup(name, strcspn(name, "."));
|
||||
binding_t *b = get_binding(env, module_name);
|
||||
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);
|
||||
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);
|
||||
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: {
|
||||
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))
|
||||
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)
|
||||
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_t), ARRAY_MAX_STRIDE);
|
||||
code_err(ast, "This array holds items that take up ", type_size(item_t),
|
||||
" 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);
|
||||
}
|
||||
case SetTypeAST: {
|
||||
@ -68,8 +68,8 @@ type_t *parse_type_ast(env_t *env, type_ast_t *ast)
|
||||
if (has_stack_memory(item_t))
|
||||
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)
|
||||
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.",
|
||||
type_size(item_t), ARRAY_MAX_STRIDE);
|
||||
code_err(ast, "This set holds items that take up ", type_size(item_t),
|
||||
" 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);
|
||||
}
|
||||
case TableTypeAST: {
|
||||
@ -120,7 +120,7 @@ type_t *parse_type_ast(env_t *env, type_ast_t *ast)
|
||||
auto opt = Match(ast, OptionalTypeAST);
|
||||
type_t *t = parse_type_ast(env, opt->type);
|
||||
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)
|
||||
code_err(ast, "Nested optional types are not currently supported");
|
||||
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_t *t = parse_type_ast(env, mutexed);
|
||||
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);
|
||||
}
|
||||
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)) {
|
||||
case NUM_PRECISION_EQUAL: case NUM_PRECISION_MORE: return lhs_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;
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
case USE_MODULE: {
|
||||
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");
|
||||
|
||||
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++) {
|
||||
const char *filename = tm_files.gl_pathv[i];
|
||||
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);
|
||||
char *file_prefix = file_base_id(filename);
|
||||
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: {
|
||||
auto def = Match(statement, StructDef);
|
||||
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);
|
||||
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: {
|
||||
auto def = Match(statement, EnumDef);
|
||||
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);
|
||||
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: {
|
||||
auto def = Match(statement, LangDef);
|
||||
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);
|
||||
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
|
||||
return;
|
||||
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);
|
||||
type_t *type = get_type(env, decl->value);
|
||||
if (!type)
|
||||
@ -301,9 +301,6 @@ void bind_statement(env_t *env, ast_t *statement)
|
||||
auto def = Match(statement, FunctionDef);
|
||||
const char *name = Match(def->name, Var)->name;
|
||||
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);
|
||||
set_binding(env, name, type, code);
|
||||
break;
|
||||
@ -313,7 +310,7 @@ void bind_statement(env_t *env, ast_t *statement)
|
||||
type_t *ret_t = Match(type, FunctionType)->ret;
|
||||
const char *name = get_type_name(ret_t);
|
||||
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,
|
||||
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;
|
||||
}
|
||||
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)
|
||||
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"
|
||||
"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
|
||||
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."
|
||||
"\nTry using a @%T pointer for this field.",
|
||||
field_t, field_t);
|
||||
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 @", 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);
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
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)
|
||||
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"
|
||||
"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
|
||||
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."
|
||||
"\nTry using a @%T pointer for this field.",
|
||||
field_t, field_t);
|
||||
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 @", 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);
|
||||
}
|
||||
REVERSE_LIST(fields);
|
||||
env_t *member_ns = namespace_env(env, heap_strf("%s$%s", 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);
|
||||
env_t *member_ns = namespace_env(env, String(def->name, "$", tag_ast->name));
|
||||
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);
|
||||
}
|
||||
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, "})");
|
||||
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) {
|
||||
@ -450,7 +446,7 @@ void bind_statement(env_t *env, ast_t *statement)
|
||||
if (!b)
|
||||
Table$str_set(env->locals, entry->name, 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++) {
|
||||
@ -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);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -516,7 +512,7 @@ env_t *when_clause_scope(env_t *env, type_t *subject_t, when_clause_t *clause)
|
||||
return env;
|
||||
|
||||
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);
|
||||
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)
|
||||
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)
|
||||
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;
|
||||
for (arg_ast_t *var = fn->args; var || field; var = var ? var->next : 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)
|
||||
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)
|
||||
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, "_"))
|
||||
@ -576,7 +572,7 @@ type_t *get_type(env_t *env, ast_t *ast)
|
||||
return Type(OptionalType, .type=NULL);
|
||||
type_t *t = parse_type_ast(env, Match(ast, None)->type);
|
||||
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);
|
||||
}
|
||||
case Bool: {
|
||||
@ -654,7 +650,7 @@ type_t *get_type(env_t *env, ast_t *ast)
|
||||
if (lang) {
|
||||
binding_t *b = get_binding(env, lang);
|
||||
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;
|
||||
} else {
|
||||
return TEXT_TYPE;
|
||||
@ -664,7 +660,7 @@ type_t *get_type(env_t *env, ast_t *ast)
|
||||
auto var = Match(ast, Var);
|
||||
binding_t *b = get_binding(env, var->name);
|
||||
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: {
|
||||
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;
|
||||
if (!merged)
|
||||
code_err(item->ast,
|
||||
"This array item has type %T, which is different from earlier array items which have type %T",
|
||||
t2, item_type);
|
||||
"This array item has type ", type_to_str(t2),
|
||||
", which is different from earlier array items which have type ", type_to_str(item_type));
|
||||
item_type = merged;
|
||||
}
|
||||
} 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);
|
||||
if (!item_merged)
|
||||
code_err(item_ast,
|
||||
"This set item has type %T, which is different from earlier set items which have type %T",
|
||||
this_item_type, item_type);
|
||||
"This set item has type ", type_to_str(this_item_type),
|
||||
", which is different from earlier set items which have type ", type_to_str(item_type));
|
||||
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;
|
||||
if (!key_merged)
|
||||
code_err(entry->ast,
|
||||
"This table entry has type %T, which is different from earlier table entries which have type %T",
|
||||
key_t, key_type);
|
||||
"This table entry has type ", type_to_str(key_t),
|
||||
", which is different from earlier table entries which have type ", type_to_str(key_type));
|
||||
key_type = key_merged;
|
||||
|
||||
type_t *val_merged = value_type ? type_or_type(value_type, value_t) : value_t;
|
||||
if (!val_merged)
|
||||
code_err(entry->ast,
|
||||
"This table entry has type %T, which is different from earlier table entries which have type %T",
|
||||
value_t, value_type);
|
||||
"This table entry has type ", type_to_str(value_t),
|
||||
", which is different from earlier table entries which have type ", type_to_str(value_type));
|
||||
value_type = val_merged;
|
||||
}
|
||||
}
|
||||
@ -789,18 +785,18 @@ type_t *get_type(env_t *env, ast_t *ast)
|
||||
if (fielded_t->tag == ModuleType) {
|
||||
const char *name = Match(fielded_t, ModuleType)->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));
|
||||
} else if (fielded_t->tag == TypeInfoType) {
|
||||
auto info = Match(fielded_t, TypeInfoType);
|
||||
assert(info->env);
|
||||
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;
|
||||
}
|
||||
type_t *field_t = get_field_type(fielded_t, access->field);
|
||||
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;
|
||||
}
|
||||
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);
|
||||
if (index_t->tag == IntType || index_t->tag == BigIntType || index_t->tag == ByteType)
|
||||
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) {
|
||||
auto table_type = Match(value_t, TableType);
|
||||
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) {
|
||||
return value_t;
|
||||
} 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: {
|
||||
@ -853,7 +849,7 @@ type_t *get_type(env_t *env, ast_t *ast)
|
||||
if (fn_type_t->tag == ClosureType)
|
||||
fn_type_t = Match(fn_type_t, ClosureType)->fn;
|
||||
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);
|
||||
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, "to")) return self_value_t;
|
||||
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: {
|
||||
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, "with")) 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: {
|
||||
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, "set")) return Type(VoidType);
|
||||
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: {
|
||||
type_t *fn_type_t = get_method_type(env, call->self, call->name);
|
||||
if (!fn_type_t)
|
||||
code_err(ast, "No such method!");
|
||||
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);
|
||||
return fn_type->ret;
|
||||
}
|
||||
@ -999,7 +995,7 @@ type_t *get_type(env_t *env, ast_t *ast)
|
||||
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: {
|
||||
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))
|
||||
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: {
|
||||
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;
|
||||
type_t *held_type = get_type(env, held);
|
||||
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) {
|
||||
env = fresh_scope(env);
|
||||
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)) {
|
||||
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: {
|
||||
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) {
|
||||
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: {
|
||||
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);
|
||||
}
|
||||
|
||||
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: {
|
||||
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",
|
||||
lhs_t, rhs_t);
|
||||
code_err(ast, "The type on the left side of this concatenation doesn't match the right side: ", type_to_str(lhs_t),
|
||||
" vs. ", type_to_str(rhs_t));
|
||||
if (lhs_t->tag == ArrayType || lhs_t->tag == TextType || lhs_t->tag == SetType)
|
||||
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: {
|
||||
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);
|
||||
}
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
@ -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 *t = type_or_type(lhs_t, rhs_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;
|
||||
}
|
||||
|
||||
@ -1243,8 +1239,8 @@ type_t *get_type(env_t *env, ast_t *ast)
|
||||
if (can_promote(ret, declared))
|
||||
ret = declared;
|
||||
else
|
||||
code_err(ast, "This function was declared to return a value of type %T, but actually returns a value of type %T",
|
||||
declared, ret);
|
||||
code_err(ast, "This function was declared to return a value of type ", type_to_str(declared),
|
||||
", but actually returns a value of type ", type_to_str(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);
|
||||
if (!t_either)
|
||||
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.",
|
||||
true_t, false_t);
|
||||
"I was expecting this block to have a ", type_to_str(true_t),
|
||||
" value (based on earlier clauses), but it actually has a ", type_to_str(false_t), " value.");
|
||||
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)
|
||||
tag_name = Match(Match(clause->pattern, FunctionCall)->fn, Var)->name;
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
code_err(clause->pattern, "There is no tag '%s' for the type %T (valid tags: %s)",
|
||||
tag_name, subject_t, CORD_to_char_star(valid_tags));
|
||||
code_err(clause->pattern, "There is no tag '", tag_name,
|
||||
"' for the type ", type_to_str(subject_t), " (valid tags: ", CORD_to_char_star(valid_tags), ")");
|
||||
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 *merged = type_or_type(overall_t, clause_type);
|
||||
if (!merged)
|
||||
code_err(clause->body, "The type of this branch is %T, which conflicts with the earlier branch type of %T",
|
||||
clause_type, overall_t);
|
||||
code_err(clause->body, "The type of this branch is ", type_to_str(clause_type),
|
||||
", which conflicts with the earlier branch type of ", type_to_str(overall_t));
|
||||
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);
|
||||
if (!merged)
|
||||
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.",
|
||||
overall_t, else_t);
|
||||
"I was expecting this block to have a ", type_to_str(overall_t),
|
||||
" value (based on earlier clauses), but it actually has a ", type_to_str(else_t), " value.");
|
||||
return merged;
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
#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)
|
||||
|
22
src/types.c
22
src/types.c
@ -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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (a == b) return true;
|
||||
|
@ -2,11 +2,9 @@
|
||||
|
||||
// Logic for defining and working with types
|
||||
|
||||
#include <printf.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "ast.h"
|
||||
#include "stdlib/arrays.h"
|
||||
|
||||
typedef struct type_s type_t;
|
||||
|
||||
@ -135,9 +133,8 @@ struct type_s {
|
||||
#define INT_TYPE Type(BigIntType)
|
||||
#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);
|
||||
const char *type_to_str(type_t *t);
|
||||
const char *get_type_name(type_t *t);
|
||||
PUREFUNC bool type_eq(type_t *a, type_t *b);
|
||||
PUREFUNC bool type_is_a(type_t *t, type_t *req);
|
||||
|
Loading…
Reference in New Issue
Block a user