First working method calls
This commit is contained in:
parent
8171a38b71
commit
4dc70c84d4
1
ast.c
1
ast.c
@ -120,6 +120,7 @@ CORD ast_to_cord(ast_t *ast)
|
||||
arg_list_to_cord(data.args), type_ast_to_cord(data.ret_type), ast_to_cord(data.body))
|
||||
T(Lambda, "(args=%r, body=%r)", arg_list_to_cord(data.args), ast_to_cord(data.body))
|
||||
T(FunctionCall, "(fn=%r, args=%r)", ast_to_cord(data.fn), arg_list_to_cord(data.args))
|
||||
T(MethodCall, "(self=%r, name=%s, args=%r)", ast_to_cord(data.self), data.name, arg_list_to_cord(data.args))
|
||||
T(Block, "(%r)", ast_list_to_cord(data.statements))
|
||||
T(For, "(index=%r, value=%r, iter=%r, body=%r, empty=%r)", ast_to_cord(data.index), ast_to_cord(data.value),
|
||||
ast_to_cord(data.iter), ast_to_cord(data.body), ast_to_cord(data.empty))
|
||||
|
7
ast.h
7
ast.h
@ -98,7 +98,7 @@ typedef enum {
|
||||
Min, Max,
|
||||
Array, Table, TableEntry,
|
||||
FunctionDef, Lambda,
|
||||
FunctionCall,
|
||||
FunctionCall, MethodCall,
|
||||
Block,
|
||||
For, While, If, When,
|
||||
Reduction,
|
||||
@ -190,6 +190,11 @@ struct ast_s {
|
||||
arg_ast_t *args;
|
||||
type_ast_t *extern_return_type;
|
||||
} FunctionCall;
|
||||
struct {
|
||||
const char *name;
|
||||
ast_t *self;
|
||||
arg_ast_t *args;
|
||||
} MethodCall;
|
||||
struct {
|
||||
ast_list_t *statements;
|
||||
} Block;
|
||||
|
49
compile.c
49
compile.c
@ -150,7 +150,8 @@ CORD compile(env_t *env, ast_t *ast)
|
||||
binding_t *b = get_binding(env, Match(ast, Var)->name);
|
||||
if (b)
|
||||
return b->code ? b->code : Match(ast, Var)->name;
|
||||
code_err(ast, "I don't know of any variable by this name");
|
||||
return Match(ast, Var)->name;
|
||||
// code_err(ast, "I don't know of any variable by this name");
|
||||
}
|
||||
case Int: return CORD_asprintf("I%ld(%ld)", Match(ast, Int)->bits, Match(ast, Int)->i);
|
||||
case Num: {
|
||||
@ -601,22 +602,36 @@ CORD compile(env_t *env, ast_t *ast)
|
||||
CORD_appendf(&env->code->funcs, ") %r", body);
|
||||
return CORD_EMPTY;
|
||||
}
|
||||
case FunctionCall: {
|
||||
auto call = Match(ast, FunctionCall);
|
||||
type_t *fn_t = get_type(env, call->fn);
|
||||
if (fn_t->tag != FunctionType)
|
||||
code_err(call->fn, "This is not a function, it's a %T", fn_t);
|
||||
|
||||
CORD code = CORD_cat_char(compile(env, call->fn), '(');
|
||||
case FunctionCall: case MethodCall: {
|
||||
type_t *fn_t;
|
||||
arg_ast_t *args;
|
||||
CORD fn;
|
||||
if (ast->tag == FunctionCall) {
|
||||
auto call = Match(ast, FunctionCall);
|
||||
fn_t = get_type(env, call->fn);
|
||||
if (fn_t->tag != FunctionType)
|
||||
code_err(call->fn, "This is not a function, it's a %T", fn_t);
|
||||
args = call->args;
|
||||
fn = compile(env, call->fn);
|
||||
} else {
|
||||
auto method = Match(ast, MethodCall);
|
||||
fn_t = get_method_type(env, method->self, method->name);
|
||||
args = new(arg_ast_t, .value=method->self, .next=method->args);
|
||||
binding_t *b = get_method_binding(env, method->self, method->name);
|
||||
if (!b) code_err(ast, "No such method");
|
||||
fn = b->code;
|
||||
}
|
||||
|
||||
CORD code = CORD_cat_char(fn, '(');
|
||||
// Pass 1: assign keyword args
|
||||
// Pass 2: assign positional args
|
||||
// Pass 3: compile and typecheck each arg
|
||||
table_t arg_bindings = {};
|
||||
for (arg_ast_t *arg = call->args; arg; arg = arg->next) {
|
||||
for (arg_ast_t *arg = args; arg; arg = arg->next) {
|
||||
if (arg->name)
|
||||
Table_str_set(&arg_bindings, arg->name, arg->value);
|
||||
}
|
||||
for (arg_ast_t *call_arg = call->args; call_arg; call_arg = call_arg->next) {
|
||||
for (arg_ast_t *call_arg = args; call_arg; call_arg = call_arg->next) {
|
||||
if (call_arg->name)
|
||||
continue;
|
||||
|
||||
@ -658,7 +673,6 @@ CORD compile(env_t *env, ast_t *ast)
|
||||
|
||||
return CORD_cat_char(code, ')');
|
||||
}
|
||||
// Lambda,
|
||||
case If: {
|
||||
auto if_ = Match(ast, If);
|
||||
CORD code;
|
||||
@ -717,7 +731,7 @@ CORD compile(env_t *env, ast_t *ast)
|
||||
CORD value = compile(env, for_->value);
|
||||
set_binding(scope, CORD_to_const_char_star(value), new(binding_t, .type=item_t));
|
||||
return CORD_all("$ARRAY_FOREACH(", compile(env, for_->iter), ", ", index, ", ", compile_type(item_t), ", ", value,
|
||||
", ", compile(scope, for_->body), ", ", for_->empty ? compile(scope, for_->empty) : "{}", ")");
|
||||
", ", compile(scope, for_->body), ", ", for_->empty ? compile(env, for_->empty) : "{}", ")");
|
||||
}
|
||||
case TableType: {
|
||||
type_t *key_t = Match(iter_t, TableType)->key_type;
|
||||
@ -735,12 +749,12 @@ CORD compile(env_t *env, ast_t *ast)
|
||||
value_offset += type_align(value_t) - (value_offset % type_align(value_t)); // padding
|
||||
return CORD_all("$TABLE_FOREACH(", compile(env, for_->iter), ", ", compile_type(key_t), ", ", key, ", ",
|
||||
compile_type(value_t), ", ", value, ", ", heap_strf("%zu", value_offset),
|
||||
", ", compile(scope, for_->body), ", ", for_->empty ? compile(scope, for_->empty) : "{}", ")");
|
||||
", ", compile(scope, for_->body), ", ", for_->empty ? compile(env, for_->empty) : "{}", ")");
|
||||
} else {
|
||||
key = compile(env, for_->value);
|
||||
set_binding(scope, CORD_to_const_char_star(key), new(binding_t, .type=key_t));
|
||||
return CORD_all("$ARRAY_FOREACH((", compile(env, for_->iter), ").entries, $i, ", compile_type(key_t), ", ", key, ", ",
|
||||
compile(scope, for_->body), ", ", for_->empty ? compile(scope, for_->empty) : "{}", ")");
|
||||
compile(scope, for_->body), ", ", for_->empty ? compile(env, for_->empty) : "{}", ")");
|
||||
}
|
||||
}
|
||||
case IntType: {
|
||||
@ -992,8 +1006,13 @@ CORD compile(env_t *env, ast_t *ast)
|
||||
// LinkerDirective,
|
||||
case InlineCCode: return Match(ast, InlineCCode)->code;
|
||||
case Unknown: code_err(ast, "Unknown AST");
|
||||
default: break;
|
||||
case Lambda: code_err(ast, "Lambdas are not supported yet");
|
||||
case Use: code_err(ast, "Uses are not supported yet");
|
||||
case LinkerDirective: code_err(ast, "Linker directives are not supported yet");
|
||||
case Extern: code_err(ast, "Externs are not supported yet");
|
||||
case TableEntry: code_err(ast, "Table entries should not be compiled directly");
|
||||
}
|
||||
code_err(ast, "Unknown AST: %W", ast);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -104,6 +104,35 @@ binding_t *get_binding(env_t *env, const char *name)
|
||||
return Table_str_get(env->locals, name);
|
||||
}
|
||||
|
||||
binding_t *get_method_binding(env_t *env, ast_t *self, const char *name)
|
||||
{
|
||||
type_t *self_type = get_type(env, self);
|
||||
if (!self_type)
|
||||
code_err(self, "I couldn't get this type");
|
||||
type_t *cls_type = value_type(self_type);
|
||||
switch (cls_type->tag) {
|
||||
case ArrayType: {
|
||||
errx(1, "Array methods not implemented");
|
||||
}
|
||||
case TableType: {
|
||||
errx(1, "Table methods not implemented");
|
||||
}
|
||||
case StringType: {
|
||||
table_t *ns = Table_str_get(env->type_namespaces, "Str");
|
||||
return Table_str_get(ns, name);
|
||||
}
|
||||
case StructType: case EnumType: {
|
||||
errx(1, "Struct/enum methods not implemented");
|
||||
// const char *name = cls_type->tag == StructType ? Match(cls_type, StructType)->name : Match(cls_type, EnumType)->name;
|
||||
// table_t *namespace = Table_str_get(env->type_namespaces, name);
|
||||
// if (!name)
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
code_err(self, "No such method!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void set_binding(env_t *env, const char *name, binding_t *binding)
|
||||
{
|
||||
Table_str_set(env->locals, name, binding);
|
||||
|
@ -32,6 +32,7 @@ __attribute__((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);
|
||||
void set_binding(env_t *env, const char *name, binding_t *binding);
|
||||
binding_t *get_method_binding(env_t *env, ast_t *self, const char *name);
|
||||
#define code_err(ast, ...) compiler_err((ast)->file, (ast)->start, (ast)->end, __VA_ARGS__)
|
||||
|
||||
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
|
||||
|
48
parse.c
48
parse.c
@ -65,6 +65,7 @@ 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_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);
|
||||
static arg_ast_t *parse_args(parse_ctx_t *ctx, const char **pos, bool allow_unnamed);
|
||||
@ -1150,6 +1151,7 @@ PARSER(parse_term) {
|
||||
progress = (false
|
||||
|| (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))
|
||||
);
|
||||
if (progress) term = new_term;
|
||||
@ -1157,6 +1159,46 @@ PARSER(parse_term) {
|
||||
return term;
|
||||
}
|
||||
|
||||
ast_t *parse_method_call_suffix(parse_ctx_t *ctx, ast_t *self) {
|
||||
if (!self) return NULL;
|
||||
|
||||
const char *start = self->start;
|
||||
const char *pos = self->end;
|
||||
|
||||
spaces(&pos);
|
||||
if (!match(&pos, ":")) return NULL;
|
||||
const char *fn = get_id(&pos);
|
||||
spaces(&pos);
|
||||
if (!match(&pos, "(")) return NULL;
|
||||
whitespace(&pos);
|
||||
|
||||
arg_ast_t *args = NULL;
|
||||
for (;;) {
|
||||
const char *arg_start = pos;
|
||||
const char *name = get_id(&pos);
|
||||
whitespace(&pos);
|
||||
if (!name || !match(&pos, "="))
|
||||
pos = arg_start;
|
||||
|
||||
ast_t *arg = optional(ctx, &pos, parse_expr);
|
||||
if (!arg) {
|
||||
if (name) parser_err(ctx, arg_start, pos, "I expected an argument here");
|
||||
break;
|
||||
}
|
||||
args = new(arg_ast_t, .name=name, .value=arg, .next=args);
|
||||
if (!match_separator(&pos))
|
||||
break;
|
||||
}
|
||||
REVERSE_LIST(args);
|
||||
|
||||
whitespace(&pos);
|
||||
|
||||
if (!match(&pos, ")"))
|
||||
parser_err(ctx, start, pos, "This parenthesis is unclosed");
|
||||
|
||||
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) {
|
||||
if (!fn) return NULL;
|
||||
|
||||
@ -1249,6 +1291,7 @@ static ast_t *parse_infix_expr(parse_ctx_t *ctx, const char *pos, int min_tightn
|
||||
progress = (false
|
||||
|| (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))
|
||||
);
|
||||
if (progress) key = new_term;
|
||||
@ -1361,7 +1404,10 @@ PARSER(parse_statement) {
|
||||
ast_t *new_stmt;
|
||||
progress = false;
|
||||
if (stmt->tag == Var)
|
||||
progress = (new_stmt=parse_fncall_suffix(ctx, stmt, NORMAL_FUNCTION));
|
||||
progress = (false
|
||||
|| (new_stmt=parse_method_call_suffix(ctx, stmt))
|
||||
|| (new_stmt=parse_fncall_suffix(ctx, stmt, NORMAL_FUNCTION))
|
||||
);
|
||||
|
||||
if (progress) stmt = new_stmt;
|
||||
}
|
||||
|
21
typecheck.c
21
typecheck.c
@ -174,6 +174,17 @@ type_t *get_function_def_type(env_t *env, ast_t *ast)
|
||||
return Type(FunctionType, .args=args, .ret=ret);
|
||||
}
|
||||
|
||||
type_t *get_method_type(env_t *env, ast_t *self, const char *name)
|
||||
{
|
||||
type_t *self_type = get_type(env, self);
|
||||
if (!self_type)
|
||||
code_err(self, "I couldn't get this type");
|
||||
binding_t *b = get_method_binding(env, self, name);
|
||||
if (!b || !b->type)
|
||||
code_err(self, "No such method: %s", name);
|
||||
return b->type;
|
||||
}
|
||||
|
||||
type_t *get_type(env_t *env, ast_t *ast)
|
||||
{
|
||||
if (!ast) return NULL;
|
||||
@ -351,6 +362,16 @@ type_t *get_type(env_t *env, ast_t *ast)
|
||||
auto fn_type = Match(fn_type_t, FunctionType);
|
||||
return fn_type->ret;
|
||||
}
|
||||
case MethodCall: {
|
||||
auto call = Match(ast, MethodCall);
|
||||
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 function, it's a %T", fn_type_t);
|
||||
auto fn_type = Match(fn_type_t, FunctionType);
|
||||
return fn_type->ret;
|
||||
}
|
||||
case Block: {
|
||||
auto block = Match(ast, Block);
|
||||
ast_list_t *last = block->statements;
|
||||
|
@ -19,5 +19,6 @@ type_t *get_arg_type(env_t *env, arg_t *arg);
|
||||
type_t *get_arg_ast_type(env_t *env, arg_ast_t *arg);
|
||||
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);
|
||||
|
||||
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
|
||||
|
Loading…
Reference in New Issue
Block a user