Support arbitrary argument constructors
This commit is contained in:
parent
43a2959be3
commit
91f66d80bb
39
compile.c
39
compile.c
@ -2681,7 +2681,7 @@ CORD compile(env_t *env, ast_t *ast)
|
||||
if (chunk->ast->tag == TextLiteral) {
|
||||
chunk_code = compile(env, chunk->ast);
|
||||
} else if (chunk_t->tag == TextType && streq(Match(chunk_t, TextType)->lang, lang)) {
|
||||
binding_t *esc = get_lang_escape_function(env, lang, chunk_t);
|
||||
binding_t *esc = get_constructor(env, text_t, new(arg_ast_t, .value=chunk->ast));
|
||||
if (esc) {
|
||||
arg_t *arg_spec = Match(esc->type, FunctionType)->args;
|
||||
arg_ast_t *args = new(arg_ast_t, .value=chunk->ast);
|
||||
@ -2690,7 +2690,7 @@ CORD compile(env_t *env, ast_t *ast)
|
||||
chunk_code = compile(env, chunk->ast);
|
||||
}
|
||||
} else if (lang) {
|
||||
binding_t *esc = get_lang_escape_function(env, lang, chunk_t);
|
||||
binding_t *esc = get_constructor(env, text_t, new(arg_ast_t, .value=chunk->ast));
|
||||
if (!esc)
|
||||
code_err(chunk->ast, "I don't know how to convert %T to %T", chunk_t, text_t);
|
||||
|
||||
@ -3368,6 +3368,12 @@ CORD compile(env_t *env, ast_t *ast)
|
||||
} else if (fn_t->tag == TypeInfoType) {
|
||||
type_t *t = Match(fn_t, TypeInfoType)->type;
|
||||
|
||||
binding_t *constructor = get_constructor(env, t, call->args);
|
||||
if (constructor) {
|
||||
arg_t *arg_spec = Match(constructor->type, FunctionType)->args;
|
||||
return CORD_all(constructor->code, "(", compile_arguments(env, ast, arg_spec, call->args), ")");
|
||||
}
|
||||
|
||||
type_t *actual = call->args ? get_type(env, call->args->value) : NULL;
|
||||
if (t->tag == StructType) {
|
||||
// Struct constructor:
|
||||
@ -3430,25 +3436,14 @@ CORD compile(env_t *env, ast_t *ast)
|
||||
} else if (t->tag == TextType) {
|
||||
if (!call->args) code_err(ast, "This constructor needs a value");
|
||||
const char *lang = Match(t, TextType)->lang;
|
||||
if (lang) { // Escape for DSL
|
||||
type_t *first_type = get_type(env, call->args->value);
|
||||
if (type_eq(first_type, t))
|
||||
return compile(env, call->args->value);
|
||||
|
||||
binding_t *esc = get_lang_escape_function(env, lang, first_type);
|
||||
if (!esc)
|
||||
code_err(ast, "I don't know how to convert %T to %T", first_type, t);
|
||||
|
||||
arg_t *arg_spec = Match(esc->type, FunctionType)->args;
|
||||
return CORD_all(esc->code, "(", compile_arguments(env, ast, arg_spec, call->args), ")");
|
||||
} else {
|
||||
// Text constructor:
|
||||
if (!call->args || call->args->next)
|
||||
code_err(call->fn, "This constructor takes exactly 1 argument");
|
||||
if (type_eq(actual, t))
|
||||
return compile(env, call->args->value);
|
||||
return expr_as_text(env, compile(env, call->args->value), actual, "no");
|
||||
}
|
||||
if (lang)
|
||||
code_err(call->fn, "I don't have a constructor defined for these arguments");
|
||||
// Text constructor:
|
||||
if (!call->args || call->args->next)
|
||||
code_err(call->fn, "This constructor takes exactly 1 argument");
|
||||
if (type_eq(actual, t))
|
||||
return compile(env, call->args->value);
|
||||
return expr_as_text(env, compile(env, call->args->value), actual, "no");
|
||||
} else if (t->tag == CStringType) {
|
||||
// C String constructor:
|
||||
if (!call->args || call->args->next)
|
||||
@ -4489,7 +4484,7 @@ CORD compile_statement_namespace_header(env_t *env, ast_t *ast)
|
||||
CORD ret_type_code = compile_type(ret_t);
|
||||
CORD name = CORD_all(namespace_prefix(env, env->namespace), decl_name);
|
||||
if (env->namespace && env->namespace->parent && env->namespace->name && streq(decl_name, env->namespace->name))
|
||||
name = CORD_asprintf("%r$%ld", name, get_line_number(ast->file, ast->start));
|
||||
name = CORD_asprintf("%r%ld", namespace_prefix(env, env->namespace), get_line_number(ast->file, ast->start));
|
||||
return CORD_all(ret_type_code, " ", name, arg_signature, ";\n");
|
||||
}
|
||||
default: return CORD_EMPTY;
|
||||
|
@ -690,22 +690,50 @@ binding_t *get_namespace_binding(env_t *env, ast_t *self, const char *name)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PUREFUNC binding_t *get_lang_escape_function(env_t *env, const char *lang_name, type_t *type_to_escape)
|
||||
PUREFUNC binding_t *get_constructor(env_t *env, type_t *t, arg_ast_t *args)
|
||||
{
|
||||
if (!lang_name) lang_name = "Text";
|
||||
binding_t *typeinfo = get_binding(env, lang_name);
|
||||
const char *type_name;
|
||||
t = value_type(t);
|
||||
switch (t->tag) {
|
||||
case TextType: {
|
||||
type_name = Match(t, TextType)->lang;
|
||||
if (type_name == NULL) type_name = "Text";
|
||||
break;
|
||||
}
|
||||
case StructType: {
|
||||
type_name = Match(t, StructType)->name;
|
||||
break;
|
||||
}
|
||||
case EnumType: {
|
||||
type_name = Match(t, EnumType)->name;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
type_name = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!type_name)
|
||||
return NULL;
|
||||
|
||||
binding_t *typeinfo = get_binding(env, type_name);
|
||||
assert(typeinfo && typeinfo->type->tag == TypeInfoType);
|
||||
env_t *lang_env = Match(typeinfo->type, TypeInfoType)->env;
|
||||
Array_t constructors = lang_env->namespace->constructors;
|
||||
env_t *type_env = Match(typeinfo->type, TypeInfoType)->env;
|
||||
Array_t constructors = type_env->namespace->constructors;
|
||||
// Prioritize exact matches:
|
||||
for (int64_t i = 0; i < constructors.length; i++) {
|
||||
binding_t *b = constructors.data + i*constructors.stride;
|
||||
auto fn = Match(b->type, FunctionType);
|
||||
if (!fn->args || fn->args->next) continue;
|
||||
if (fn->ret->tag != TextType || !streq(Match(fn->ret, TextType)->lang, lang_name))
|
||||
continue;
|
||||
if (!can_promote(type_to_escape, fn->args->type))
|
||||
continue;
|
||||
return b;
|
||||
if (is_valid_call(env, fn->args, args, false))
|
||||
return b;
|
||||
}
|
||||
// Fall back to promotion:
|
||||
for (int64_t i = 0; i < constructors.length; i++) {
|
||||
binding_t *b = constructors.data + i*constructors.stride;
|
||||
auto fn = Match(b->type, FunctionType);
|
||||
if (is_valid_call(env, fn->args, args, true))
|
||||
return b;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ 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, ...);
|
||||
binding_t *get_binding(env_t *env, const char *name);
|
||||
binding_t *get_lang_escape_function(env_t *env, const char *lang_name, type_t *type_to_escape);
|
||||
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);
|
||||
binding_t *get_namespace_binding(env_t *env, ast_t *self, const char *name);
|
||||
#define code_err(ast, ...) compiler_err((ast)->file, (ast)->start, (ast)->end, __VA_ARGS__)
|
||||
|
58
typecheck.c
58
typecheck.c
@ -791,7 +791,11 @@ type_t *get_type(env_t *env, ast_t *ast)
|
||||
|
||||
if (fn_type_t->tag == TypeInfoType) {
|
||||
type_t *t = Match(fn_type_t, TypeInfoType)->type;
|
||||
if (t->tag == StructType || t->tag == IntType || t->tag == BigIntType || t->tag == NumType
|
||||
|
||||
binding_t *constructor = get_constructor(env, t, call->args);
|
||||
if (constructor)
|
||||
return t;
|
||||
else if (t->tag == StructType || t->tag == IntType || t->tag == BigIntType || t->tag == NumType
|
||||
|| t->tag == ByteType || t->tag == TextType || t->tag == CStringType || t->tag == MomentType)
|
||||
return t; // Constructor
|
||||
code_err(call->fn, "This is not a type that has a constructor");
|
||||
@ -1363,6 +1367,58 @@ type_t *get_arg_type(env_t *env, arg_t *arg)
|
||||
return get_type(env, arg->default_val);
|
||||
}
|
||||
|
||||
bool is_valid_call(env_t *env, arg_t *spec_args, arg_ast_t *call_args, bool promotion_allowed)
|
||||
{
|
||||
Table_t used_args = {};
|
||||
for (arg_t *spec_arg = spec_args; spec_arg; spec_arg = spec_arg->next) {
|
||||
type_t *spec_type = get_arg_type(env, spec_arg);
|
||||
int64_t i = 1;
|
||||
// Find keyword:
|
||||
if (spec_arg->name) {
|
||||
for (arg_ast_t *call_arg = call_args; call_arg; call_arg = call_arg->next) {
|
||||
if (call_arg->name && streq(call_arg->name, spec_arg->name)) {
|
||||
type_t *call_type = get_arg_ast_type(env, call_arg);
|
||||
if (!(type_eq(call_type, spec_type) || (promotion_allowed && can_promote(call_type, spec_type))))
|
||||
return false;
|
||||
Table$str_set(&used_args, call_arg->name, call_arg);
|
||||
goto found_it;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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++);
|
||||
if (!Table$str_get(used_args, pseudoname)) {
|
||||
type_t *call_type = get_arg_ast_type(env, call_arg);
|
||||
if (!(type_eq(call_type, spec_type) || (promotion_allowed && can_promote(call_type, spec_type))))
|
||||
return false;
|
||||
Table$str_set(&used_args, pseudoname, call_arg);
|
||||
goto found_it;
|
||||
}
|
||||
}
|
||||
|
||||
if (spec_arg->default_val)
|
||||
goto found_it;
|
||||
|
||||
return false;
|
||||
found_it: continue;
|
||||
}
|
||||
|
||||
int64_t i = 1;
|
||||
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))
|
||||
return false;
|
||||
} else {
|
||||
const char *pseudoname = heap_strf("%ld", i++);
|
||||
if (!Table$str_get(used_args, pseudoname))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
PUREFUNC bool can_be_mutated(env_t *env, ast_t *ast)
|
||||
{
|
||||
switch (ast->tag) {
|
||||
|
@ -25,5 +25,6 @@ PUREFUNC bool can_be_mutated(env_t *env, ast_t *ast);
|
||||
type_t *parse_type_string(env_t *env, const char *str);
|
||||
type_t *get_method_type(env_t *env, ast_t *self, const char *name);
|
||||
PUREFUNC bool is_constant(env_t *env, ast_t *ast);
|
||||
bool is_valid_call(env_t *env, arg_t *spec_args, arg_ast_t *call_args, bool promotion_allowed);
|
||||
|
||||
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
|
||||
|
Loading…
Reference in New Issue
Block a user