Implement 'extern' functionality

This commit is contained in:
Bruce Hill 2024-03-24 15:06:59 -04:00
parent a29d2ed6d1
commit 5157988efa
5 changed files with 39 additions and 27 deletions

2
ast.h
View File

@ -194,7 +194,6 @@ struct ast_s {
struct {
ast_t *fn;
arg_ast_t *args;
type_ast_t *extern_return_type;
} FunctionCall;
struct {
const char *name;
@ -231,7 +230,6 @@ struct ast_s {
struct {
const char *name;
type_ast_t *type;
bool address;
} Extern;
struct {
const char *name;

View File

@ -58,7 +58,6 @@ static bool promote(env_t *env, CORD *code, type_t *actual, type_t *needed)
return false;
}
CORD compile_declaration(env_t *env, type_t *t, const char *name)
{
if (t->tag == FunctionType) {
@ -735,6 +734,25 @@ CORD compile_statement(env_t *env, ast_t *ast)
return compile_statement(env, loop);
}
}
case Extern: {
auto ext = Match(ast, Extern);
type_t *t = parse_type_ast(env, ext->type);
CORD decl;
if (t->tag == ClosureType) {
t = Match(t, ClosureType)->fn;
auto fn = Match(t, FunctionType);
decl = CORD_all(compile_type(env, fn->ret), " ", ext->name, "(");
for (arg_t *arg = fn->args; arg; arg = arg->next) {
decl = CORD_all(decl, compile_type(env, arg->type));
if (arg->next) decl = CORD_cat(decl, ", ");
}
decl = CORD_cat(decl, ")");
} else {
decl = compile_declaration(env, t, ext->name);
}
env->code->fndefs = CORD_all(env->code->fndefs, "extern ", decl, ";\n");
return CORD_EMPTY;
}
case InlineCCode: return Match(ast, InlineCCode)->code;
default:
return CORD_asprintf("(void)%r;", compile(env, ast));

28
parse.c
View File

@ -65,7 +65,7 @@ static inline const char* get_id(const char **pos);
static inline bool comment(const char **pos);
static inline bool indent(parse_ctx_t *ctx, const char **pos);
static inline binop_e match_binary_operator(const char **pos);
static ast_t *parse_fncall_suffix(parse_ctx_t *ctx, ast_t *fn, bool is_extern);
static ast_t *parse_fncall_suffix(parse_ctx_t *ctx, ast_t *fn);
static ast_t *parse_method_call_suffix(parse_ctx_t *ctx, ast_t *self);
static ast_t *parse_field_suffix(parse_ctx_t *ctx, ast_t *lhs);
static ast_t *parse_index_suffix(parse_ctx_t *ctx, ast_t *lhs);
@ -753,7 +753,7 @@ PARSER(parse_reduction) {
|| (new_term=parse_index_suffix(ctx, key))
|| (new_term=parse_field_suffix(ctx, key))
|| (new_term=parse_method_call_suffix(ctx, key))
|| (new_term=parse_fncall_suffix(ctx, key, NORMAL_FUNCTION))
|| (new_term=parse_fncall_suffix(ctx, key))
);
if (progress) key = new_term;
}
@ -1211,7 +1211,7 @@ PARSER(parse_term) {
|| (new_term=parse_index_suffix(ctx, term))
|| (new_term=parse_field_suffix(ctx, term))
|| (new_term=parse_method_call_suffix(ctx, term))
|| (new_term=parse_fncall_suffix(ctx, term, NORMAL_FUNCTION))
|| (new_term=parse_fncall_suffix(ctx, term))
);
if (progress) term = new_term;
}
@ -1260,7 +1260,7 @@ ast_t *parse_method_call_suffix(parse_ctx_t *ctx, ast_t *self) {
return NewAST(ctx->file, start, pos, MethodCall, .self=self, .name=fn, .args=args);
}
ast_t *parse_fncall_suffix(parse_ctx_t *ctx, ast_t *fn, bool is_extern) {
ast_t *parse_fncall_suffix(parse_ctx_t *ctx, ast_t *fn) {
if (!fn) return NULL;
const char *start = fn->start;
@ -1296,15 +1296,8 @@ ast_t *parse_fncall_suffix(parse_ctx_t *ctx, ast_t *fn, bool is_extern) {
if (!match(&pos, ")"))
parser_err(ctx, start, pos, "This parenthesis is unclosed");
type_ast_t *extern_return_type = NULL;
if (is_extern) {
if (match(&pos, ":"))
extern_return_type = expect(ctx, start, &pos, parse_type, "I couldn't parse the return type of this external function call");
else
extern_return_type = NewTypeAST(ctx->file, pos, pos, VarTypeAST, .name="Void");
}
REVERSE_LIST(args);
return NewAST(ctx->file, start, pos, FunctionCall, .fn=fn, .args=args, .extern_return_type=extern_return_type);
return NewAST(ctx->file, start, pos, FunctionCall, .fn=fn, .args=args);
}
binop_e match_binary_operator(const char **pos)
@ -1355,7 +1348,7 @@ static ast_t *parse_infix_expr(parse_ctx_t *ctx, const char *pos, int min_tightn
|| (new_term=parse_index_suffix(ctx, key))
|| (new_term=parse_field_suffix(ctx, key))
|| (new_term=parse_method_call_suffix(ctx, key))
|| (new_term=parse_fncall_suffix(ctx, key, NORMAL_FUNCTION))
|| (new_term=parse_fncall_suffix(ctx, key))
);
if (progress) key = new_term;
}
@ -1469,7 +1462,7 @@ PARSER(parse_statement) {
if (stmt->tag == Var)
progress = (false
|| (new_stmt=parse_method_call_suffix(ctx, stmt))
|| (new_stmt=parse_fncall_suffix(ctx, stmt, NORMAL_FUNCTION))
|| (new_stmt=parse_fncall_suffix(ctx, stmt))
);
if (progress) stmt = new_stmt;
@ -1828,17 +1821,12 @@ PARSER(parse_extern) {
const char *start = pos;
if (!match_word(&pos, "extern")) return NULL;
spaces(&pos);
bool address = (match(&pos, "&") != 0);
const char* name = get_id(&pos);
spaces(&pos);
// extern function call:
if (match(&pos, "(")) {
return parse_fncall_suffix(ctx, NewAST(ctx->file, start, pos-1, Var, .name=name), EXTERN_FUNCTION);
}
if (!match(&pos, ":"))
parser_err(ctx, start, pos, "I couldn't get a type for this extern");
type_ast_t *type = expect(ctx, start, &pos, parse_type, "I couldn't parse the type for this extern");
return NewAST(ctx->file, start, pos, Extern, .name=name, .type=type, .address=address);
return NewAST(ctx->file, start, pos, Extern, .name=name, .type=type);
}
PARSER(parse_doctest) {

3
test/extern.tm Normal file
View File

@ -0,0 +1,3 @@
extern CORD_cat:func(a:Text, b:Text)->Text
>> CORD_cat("hello ", "world")
= "hello world"

View File

@ -233,6 +233,14 @@ void bind_statement(env_t *env, ast_t *statement)
Table_str_set(env->imports, name, module_env);
break;
}
case Extern: {
auto ext = Match(statement, Extern);
type_t *t = parse_type_ast(env, ext->type);
if (t->tag == ClosureType)
t = Match(t, ClosureType)->fn;
set_binding(env, ext->name, new(binding_t, .type=t, .code=ext->name));
break;
}
default: break;
}
}
@ -471,8 +479,6 @@ type_t *get_type(env_t *env, ast_t *ast)
}
case FunctionCall: {
auto call = Match(ast, FunctionCall);
if (call->extern_return_type)
return parse_type_ast(env, call->extern_return_type);
type_t *fn_type_t = get_type(env, call->fn);
if (!fn_type_t)
code_err(call->fn, "I couldn't find this function");
@ -547,8 +553,7 @@ type_t *get_type(env_t *env, ast_t *ast)
return get_type(block_env, last->ast);
}
case Extern: {
type_t *t = parse_type_ast(env, Match(ast, Extern)->type);
return Match(ast, Extern)->address ? Type(PointerType, .pointed=t, .is_optional=false) : t;
return parse_type_ast(env, Match(ast, Extern)->type);
}
case Declare: case Assign: case DocTest: case LinkerDirective: {
return Type(VoidType);