diff --git a/ast.c b/ast.c index 60cd27b..3e054cb 100644 --- a/ast.c +++ b/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)) diff --git a/ast.h b/ast.h index ded91f9..3cc93f4 100644 --- a/ast.h +++ b/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; diff --git a/compile.c b/compile.c index c412065..d18445d 100644 --- a/compile.c +++ b/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; } diff --git a/environment.c b/environment.c index 87fe8a1..d76118c 100644 --- a/environment.c +++ b/environment.c @@ -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); diff --git a/environment.h b/environment.h index 5c2e964..cf7bc54 100644 --- a/environment.h +++ b/environment.h @@ -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 diff --git a/parse.c b/parse.c index 5d1880c..b349bcb 100644 --- a/parse.c +++ b/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; } diff --git a/typecheck.c b/typecheck.c index 1f79d30..0cfa11c 100644 --- a/typecheck.c +++ b/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; diff --git a/typecheck.h b/typecheck.h index 4df9a8a..c99d4fb 100644 --- a/typecheck.h +++ b/typecheck.h @@ -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