static void on_add(mj_binary_operation *op, void *result) { int left, right = 0; ast_visit(op->left_operand, &left); ast_visit(op->right_operand, &right); *((int *) result) = left + right; }
static bool apply_default_arg(pass_opt_t* opt, ast_t* param, ast_t* arg) { // Pick up a default argument. ast_t* def_arg = ast_childidx(param, 2); if(ast_id(def_arg) == TK_NONE) { ast_error(arg, "not enough arguments"); return false; } ast_setid(arg, TK_SEQ); ast_add(arg, def_arg); // Type check the arg. if(ast_type(def_arg) == NULL) { if(ast_visit(&arg, NULL, pass_expr, opt) != AST_OK) return false; ast_visit(&arg, NULL, pass_nodebug, opt); } else { if(!expr_seq(arg)) return false; } return true; }
ast_result_t ast_visit_scope(ast_t** ast, ast_visit_t pre, ast_visit_t post, pass_opt_t* options, pass_id pass) { typecheck_t* t = &options->check; bool pop = frame_push(t, NULL); ast_result_t ret = ast_visit(ast, pre, post, options, pass); if(pop) frame_pop(t); return ret; }
// Determine which body to use for the given method static bool resolve_body(ast_t* entity, ast_t* method, pass_opt_t* options) { assert(entity != NULL); assert(method != NULL); method_t* info = (method_t*)ast_data(method); assert(info != NULL); if(info->local_def) // Local defs just use their own body return true; token_id e_id = ast_id(entity); bool concrete = (e_id == TK_PRIMITIVE) || (e_id == TK_STRUCT) || (e_id == TK_CLASS) || (e_id == TK_ACTOR); const char* name = ast_name(ast_childidx(method, 1)); assert(name != NULL); // NExt try a delegate body ast_t* r = resolve_delegate_body(entity, method, info, name); if(r == BODY_ERROR) return false; if(r == NULL) // And finally a default body r = resolve_default_body(entity, method, info, name, concrete); if(r == BODY_ERROR) return false; if(r != NULL) { // We have a body, use it and patch up symbol tables ast_t* old_body = ast_childidx(method, 6); ast_replace(&old_body, r); ast_visit(&method, rescope, NULL, options, PASS_ALL); return true; } // Nowhere left to get a body from if(concrete) { ast_error(entity, "no body found for method %s", name); ast_error(method, "provided from here"); return false; } return true; }
bool module_passes(ast_t* package, pass_opt_t* options, source_t* source) { if(!pass_parse(package, source)) return false; if(options->limit < PASS_SYNTAX) return true; ast_t* module = ast_child(package); if(ast_visit(&module, pass_syntax, NULL, options, PASS_SYNTAX) != AST_OK) return false; check_tree(module); return true; }
// Do a single pass, if the limit allows static bool do_pass(ast_t** astp, bool* out_result, pass_opt_t* options, pass_id pass, ast_visit_t pre_fn, ast_visit_t post_fn) { if(options->limit < pass) { *out_result = true; return true; } if(ast_visit(astp, pre_fn, post_fn, options) != AST_OK) { *out_result = false; return true; } return false; }
ast_result_t ast_visit_scope(ast_t** ast, ast_visit_t pre, ast_visit_t post, pass_opt_t* options, pass_id pass) { typecheck_t* t = &options->check; ast_t* module = ast_nearest(*ast, TK_MODULE); ast_t* package = ast_parent(module); assert(module != NULL); assert(package != NULL); frame_push(t, NULL); frame_push(t, package); frame_push(t, module); ast_result_t ret = ast_visit(ast, pre, post, options, pass); frame_pop(t); frame_pop(t); frame_pop(t); return ret; }
// Perform an ast_visit pass, after checking the pass limits. // Returns true to continue, false to stop processing and return the value in // out_r. static bool visit_pass(ast_t** astp, pass_opt_t* options, pass_id last_pass, bool* out_r, pass_id pass, ast_visit_t pre_fn, ast_visit_t post_fn) { assert(out_r != NULL); if(!check_limit(astp, options, pass, last_pass)) { *out_r = true; return false; } //fprintf(stderr, "Pass %s (last %s) on %s\n", pass_name(pass), // pass_name(last_pass), ast_get_print(*astp)); if(ast_visit(astp, pre_fn, post_fn, options, pass) != AST_OK) { *out_r = false; return false; } return true; }
static ast_result_t sugar_as(pass_opt_t* opt, ast_t** astp) { typecheck_t* t = &opt->check; ast_t* ast = *astp; AST_GET_CHILDREN(ast, expr, type); ast_t* pattern_root = ast_from(type, TK_LEX_ERROR); ast_t* body_root = ast_from(type, TK_LEX_ERROR); add_as_type(t, type, pattern_root, body_root); ast_t* body = ast_pop(body_root); ast_free(body_root); if(body == NULL) { // No body implies all types are "don't care" ast_error(ast, "Cannot treat value as \"don't care\""); ast_free(pattern_root); return AST_ERROR; } // Don't need top sequence in pattern assert(ast_id(ast_child(pattern_root)) == TK_SEQ); ast_t* pattern = ast_pop(ast_child(pattern_root)); ast_free(pattern_root); REPLACE(astp, NODE(TK_MATCH, AST_SCOPE NODE(TK_SEQ, TREE(expr)) NODE(TK_CASES, AST_SCOPE NODE(TK_CASE, AST_SCOPE TREE(pattern) NONE TREE(body))) NODE(TK_SEQ, AST_SCOPE NODE(TK_ERROR, NONE)))); return ast_visit(astp, pass_sugar, NULL, opt, PASS_SUGAR); }
bool expr_lambda(pass_opt_t* opt, ast_t** astp) { assert(astp != NULL); ast_t* ast = *astp; assert(ast != NULL); AST_GET_CHILDREN(ast, cap, name, t_params, params, captures, ret_type, raises, body); ast_t* members = ast_from(ast, TK_MEMBERS); ast_t* last_member = NULL; bool failed = false; // Process captures for(ast_t* p = ast_child(captures); p != NULL; p = ast_sibling(p)) { ast_t* field = make_capture_field(opt, p); if(field != NULL) ast_list_append(members, &last_member, field); else // An error occurred, just keep going to potentially find more errors failed = true; } if(failed) { ast_free(members); return false; } // Stop the various elements being marked as preserve ast_clearflag(t_params, AST_FLAG_PRESERVE); ast_clearflag(params, AST_FLAG_PRESERVE); ast_clearflag(ret_type, AST_FLAG_PRESERVE); ast_clearflag(body, AST_FLAG_PRESERVE); const char* fn_name = "apply"; if(ast_id(name) == TK_ID) fn_name = ast_name(name); // Make the apply function BUILD(apply, ast, NODE(TK_FUN, AST_SCOPE TREE(cap) ID(fn_name) TREE(t_params) TREE(params) TREE(ret_type) TREE(raises) TREE(body) NONE // Doc string NONE)); // Guard ast_list_append(members, &last_member, apply); printbuf_t* buf = printbuf_new(); printbuf(buf, "lambda("); bool first = true; for(ast_t* p = ast_child(params); p != NULL; p = ast_sibling(p)) { if(first) first = false; else printbuf(buf, ", "); printbuf(buf, "%s", ast_print_type(ast_childidx(p, 1))); } printbuf(buf, ")"); if(ast_id(ret_type) != TK_NONE) printbuf(buf, ": %s", ast_print_type(ret_type)); if(ast_id(raises) != TK_NONE) printbuf(buf, " ?"); printbuf(buf, " end"); // Replace lambda with object literal REPLACE(astp, NODE(TK_OBJECT, DATA(stringtab(buf->m)) NONE NONE // Provides list TREE(members))); printbuf_free(buf); // Catch up passes if(ast_visit(astp, pass_syntax, NULL, opt, PASS_SYNTAX) != AST_OK) return false; return ast_passes_subtree(astp, opt, PASS_EXPR); }
// Check resulting methods are compatible with the containing entity and patch // up symbol tables static bool post_process_methods(ast_t* entity, pass_opt_t* options, bool is_concrete) { assert(entity != NULL); bool r = true; ast_t* members = ast_childidx(entity, 4); for(ast_t* m = ast_child(members); m != NULL; m = ast_sibling(m)) { token_id variety = ast_id(m); // Check behaviour compatability if(variety == TK_BE) { switch(ast_id(entity)) { case TK_PRIMITIVE: ast_error(entity, "primitives can't provide traits that have behaviours"); r = false; break; case TK_CLASS: ast_error(entity, "classes can't have provide that have behaviours"); r = false; break; default: break; } } if(variety == TK_BE || variety == TK_FUN || variety == TK_NEW) { // Check concrete method bodies if(ast_data(m) == BODY_AMBIGUOUS) { if(is_concrete) { ast_error(m, "multiple possible method bodies from traits"); r = false; } } else if(ast_data(m) == NULL) { if(is_concrete) { assert(ast_id(ast_childidx(m, 6)) == TK_NONE); ast_error(m, "no body found for method %d %s %s", is_concrete, ast_get_print(entity), ast_name(ast_child(entity))); r = false; } } else { assert(ast_id(ast_childidx(m, 6)) != TK_NONE); if(ast_data(m) != entity) { // Sort out copied symbol tables ast_visit(&m, rescope, NULL, options); } } } } return r; }
// Combine the given inherited method with the existing one, if any, in the // given entity. // The provided method must already be reified. // The trait_ref is the entry in the provides list that causes this method // inclusion. Needed for error reporting. // Returns true on success, false on failure in which case an error will have // been reported. static bool add_method_from_trait(ast_t* entity, ast_t* method, ast_t* trait_ref, pass_opt_t* opt) { assert(entity != NULL); assert(method != NULL); assert(trait_ref != NULL); AST_GET_CHILDREN(method, cap, id, t_params, params, result, error, method_body); const char* method_name = ast_name(id); ast_t* existing_method = find_method(entity, method_name); if(existing_method == NULL) { // We don't have a method yet with this name, add the one from this trait. ast_t* m = add_method(entity, trait_ref, method, "provided", opt); if(m == NULL) return false; if(ast_id(ast_childidx(m, 6)) != TK_NONE) ast_visit(&m, rescope, NULL, opt, PASS_ALL); return true; } // A method with this name already exists. method_t* info = (method_t*)ast_data(existing_method); assert(info != NULL); // Method has already caused an error, do nothing. if(info->failed) return false; if(info->local_define || info->delegated_field != NULL) return true; // Existing method is also provided, signatures must match exactly. if(!compare_signatures(existing_method, method)) { assert(info->trait_ref != NULL); ast_error(opt->check.errors, trait_ref, "clashing definitions for method '%s' provided, local disambiguation " "required", method_name); ast_error_continue(opt->check.errors, trait_ref, "provided here, type: %s", ast_print_type(method)); ast_error_continue(opt->check.errors, info->trait_ref, "and here, type: %s", ast_print_type(existing_method)); info->failed = true; return false; } // Resolve bodies, if any. ast_t* existing_body = ast_childidx(existing_method, 6); bool multiple_bodies = (info->body_donor != NULL) && (ast_id(method_body) != TK_NONE) && (info->body_donor != (ast_t*)ast_data(method)); if(multiple_bodies || ast_checkflag(existing_method, AST_FLAG_AMBIGUOUS) || ast_checkflag(method, AST_FLAG_AMBIGUOUS)) { // This method body ambiguous, which is not necessarily an error. ast_setflag(existing_method, AST_FLAG_AMBIGUOUS); if(ast_id(existing_body) != TK_NONE) // Ditch existing body. ast_replace(&existing_body, ast_from(existing_method, TK_NONE)); info->body_donor = NULL; return true; } // No new body to resolve. if((ast_id(method_body) == TK_NONE) || (info->body_donor == (ast_t*)ast_data(method))) return true; // Trait provides default body. Use it and patch up symbol tables. assert(ast_id(existing_body) == TK_NONE); ast_replace(&existing_body, method_body); ast_visit(&method_body, rescope, NULL, opt, PASS_ALL); info->body_donor = (ast_t*)ast_data(method); info->trait_ref = trait_ref; return true; }
ast_result_t ast_visit(ast_t** ast, ast_visit_t pre, ast_visit_t post, pass_opt_t* options, pass_id pass) { assert(ast != NULL); assert(*ast != NULL); pass_id ast_pass = (pass_id)ast_checkflag(*ast, AST_FLAG_PASS_MASK); if(ast_pass >= pass) // This pass already done for this AST node return AST_OK; if(ast_checkflag(*ast, AST_FLAG_PRESERVE)) // Do not process this subtree return AST_OK; typecheck_t* t = &options->check; bool pop = frame_push(t, *ast); ast_result_t ret = AST_OK; bool ignore = false; if(pre != NULL) { switch(pre(ast, options)) { case AST_OK: break; case AST_IGNORE: ignore = true; break; case AST_ERROR: ret = AST_ERROR; break; case AST_FATAL: record_ast_pass(*ast, pass); return AST_FATAL; } } if(!ignore && ((pre != NULL) || (post != NULL))) { ast_t* child = ast_child(*ast); while(child != NULL) { switch(ast_visit(&child, pre, post, options, pass)) { case AST_OK: break; case AST_IGNORE: // Can never happen assert(0); break; case AST_ERROR: ret = AST_ERROR; break; case AST_FATAL: record_ast_pass(*ast, pass); return AST_FATAL; } child = ast_sibling(child); } } if(!ignore && post != NULL) { switch(post(ast, options)) { case AST_OK: case AST_IGNORE: break; case AST_ERROR: ret = AST_ERROR; break; case AST_FATAL: record_ast_pass(*ast, pass); return AST_FATAL; } } if(pop) frame_pop(t); record_ast_pass(*ast, pass); return ret; }
bool expr_object(pass_opt_t* opt, ast_t** astp) { ast_t* ast = *astp; bool ok = true; AST_GET_CHILDREN(ast, cap, provides, members); ast_clearflag(cap, AST_FLAG_PRESERVE); ast_clearflag(provides, AST_FLAG_PRESERVE); ast_clearflag(members, AST_FLAG_PRESERVE); ast_t* annotation = ast_consumeannotation(ast); const char* c_id = package_hygienic_id(&opt->check); ast_t* t_params; ast_t* t_args; collect_type_params(ast, &t_params, &t_args); const char* nice_id = (const char*)ast_data(ast); if(nice_id == NULL) nice_id = "object literal"; // Create a new anonymous type. BUILD(def, ast, NODE(TK_CLASS, AST_SCOPE ANNOTATE(annotation) NICE_ID(c_id, nice_id) TREE(t_params) NONE TREE(provides) NODE(TK_MEMBERS) NONE NONE)); // We will have a create method in the type. BUILD(create, members, NODE(TK_NEW, AST_SCOPE NONE ID("create") NONE NODE(TK_PARAMS) NONE NONE NODE(TK_SEQ, NODE(TK_TRUE)) NONE NONE)); BUILD(type_ref, ast, NODE(TK_REFERENCE, ID(c_id))); if(ast_id(t_args) != TK_NONE) { // Need to add type args to our type reference BUILD(t, ast, NODE(TK_QUALIFY, TREE(type_ref) TREE(t_args))); type_ref = t; } ast_free_unattached(t_args); // We will replace object..end with $0.create(...) BUILD(call, ast, NODE(TK_CALL, NODE(TK_POSITIONALARGS) NONE NONE NODE(TK_DOT, TREE(type_ref) ID("create")))); ast_t* create_params = ast_childidx(create, 3); ast_t* create_body = ast_childidx(create, 6); ast_t* call_args = ast_child(call); ast_t* class_members = ast_childidx(def, 4); ast_t* member = ast_child(members); bool has_fields = false; bool has_behaviours = false; while(member != NULL) { switch(ast_id(member)) { case TK_FVAR: case TK_FLET: case TK_EMBED: { add_field_to_object(opt, member, class_members, create_params, create_body, call_args); has_fields = true; break; } case TK_BE: // If we have behaviours, we must be an actor. ast_append(class_members, member); has_behaviours = true; break; default: // Keep all the methods as they are. ast_append(class_members, member); break; } member = ast_sibling(member); } // Add the create function at the end. ast_append(class_members, create); // Add new type to current module and bring it up to date with passes. ast_t* module = ast_nearest(ast, TK_MODULE); ast_append(module, def); // Turn any free variables into fields. ast_t* captures = ast_from(ast, TK_MEMBERS); ast_t* last_capture = NULL; if(!capture_from_type(opt, *astp, &def, captures, &last_capture)) ok = false; for(ast_t* p = ast_child(captures); p != NULL; p = ast_sibling(p)) { add_field_to_object(opt, p, class_members, create_params, create_body, call_args); has_fields = true; } ast_free_unattached(captures); ast_resetpass(def, PASS_SUGAR); // Handle capability and whether the anonymous type is a class, primitive or // actor. token_id cap_id = ast_id(cap); if(has_behaviours) { // Change the type to an actor. ast_setid(def, TK_ACTOR); if(cap_id != TK_NONE && cap_id != TK_TAG) { ast_error(opt->check.errors, cap, "object literals with behaviours are " "actors and so must have tag capability"); ok = false; } cap_id = TK_TAG; } else if(!has_fields && (cap_id == TK_NONE || cap_id == TK_TAG || cap_id == TK_BOX || cap_id == TK_VAL)) { // Change the type from a class to a primitive. ast_setid(def, TK_PRIMITIVE); cap_id = TK_VAL; } if(ast_id(def) != TK_PRIMITIVE) pony_assert(!ast_has_annotation(def, "ponyint_bare")); // Reset constructor to pick up the correct defaults. ast_setid(ast_child(create), cap_id); ast_t* result = ast_childidx(create, 4); ast_replace(&result, type_for_class(opt, def, result, cap_id, TK_EPHEMERAL, false)); // Catch up provides before catching up the entire type. if(!catch_up_provides(opt, provides)) return false; // Type check the anonymous type. if(!ast_passes_type(&def, opt, PASS_EXPR)) return false; // Replace object..end with $0.create(...) ast_replace(astp, call); if(ast_visit(astp, pass_syntax, NULL, opt, PASS_SYNTAX) != AST_OK) return false; if(!ast_passes_subtree(astp, opt, PASS_EXPR)) return false; return ok; }
bool expr_lambda(pass_opt_t* opt, ast_t** astp) { pony_assert(astp != NULL); ast_t* ast = *astp; pony_assert(ast != NULL); AST_GET_CHILDREN(ast, receiver_cap, name, t_params, params, captures, ret_type, raises, body, reference_cap); ast_t* annotation = ast_consumeannotation(ast); bool bare = ast_id(ast) == TK_BARELAMBDA; ast_t* members = ast_from(ast, TK_MEMBERS); ast_t* last_member = NULL; bool failed = false; if(bare) pony_assert(ast_id(captures) == TK_NONE); // Process captures for(ast_t* p = ast_child(captures); p != NULL; p = ast_sibling(p)) { ast_t* field = NULL; bool ok = make_capture_field(opt, p, &field); if(field != NULL) ast_list_append(members, &last_member, field); else if(!ok) // An error occurred, just keep going to potentially find more errors failed = true; } if(failed) { ast_free(members); return false; } // Stop the various elements being marked as preserve ast_clearflag(t_params, AST_FLAG_PRESERVE); ast_clearflag(params, AST_FLAG_PRESERVE); ast_clearflag(ret_type, AST_FLAG_PRESERVE); ast_clearflag(body, AST_FLAG_PRESERVE); const char* fn_name = "apply"; if(ast_id(name) == TK_ID) fn_name = ast_name(name); // Make the apply function BUILD(apply, ast, NODE(TK_FUN, AST_SCOPE ANNOTATE(annotation) TREE(receiver_cap) ID(fn_name) TREE(t_params) TREE(params) TREE(ret_type) TREE(raises) TREE(body) NONE // Doc string NONE)); // Guard ast_list_append(members, &last_member, apply); ast_setflag(members, AST_FLAG_PRESERVE); printbuf_t* buf = printbuf_new(); printbuf(buf, bare ? "@{(" : "{("); bool first = true; for(ast_t* p = ast_child(params); p != NULL; p = ast_sibling(p)) { if(first) first = false; else printbuf(buf, ", "); printbuf(buf, "%s", ast_print_type(ast_childidx(p, 1))); } printbuf(buf, ")"); if(ast_id(ret_type) != TK_NONE) printbuf(buf, ": %s", ast_print_type(ret_type)); if(ast_id(raises) != TK_NONE) printbuf(buf, " ?"); printbuf(buf, "}"); // Replace lambda with object literal REPLACE(astp, NODE(TK_OBJECT, DATA(stringtab(buf->m)) TREE(reference_cap) NONE // Provides list TREE(members))); printbuf_free(buf); if(bare) { BUILD(bare_annotation, *astp, NODE(TK_ANNOTATION, ID("ponyint_bare"))); // Record the syntax pass as done to avoid the error about internal // annotations. ast_pass_record(bare_annotation, PASS_SYNTAX); ast_setannotation(*astp, bare_annotation); } // Catch up passes if(ast_visit(astp, pass_syntax, NULL, opt, PASS_SYNTAX) != AST_OK) return false; return ast_passes_subtree(astp, opt, PASS_EXPR); }
bool expr_lambda(pass_opt_t* opt, ast_t** astp) { pony_assert(astp != NULL); ast_t* ast = *astp; pony_assert(ast != NULL); AST_GET_CHILDREN(ast, receiver_cap, name, t_params, params, captures, ret_type, raises, body, obj_cap); ast_t* annotation = ast_consumeannotation(ast); // Try to find an antecedent type, and find possible lambda interfaces in it. ast_t* antecedent_type = find_antecedent_type(opt, ast, NULL); astlist_t* possible_fun_defs = NULL; astlist_t* possible_obj_caps = NULL; if(!is_typecheck_error(antecedent_type)) find_possible_fun_defs(opt, antecedent_type, &possible_fun_defs, &possible_obj_caps); // If there's more than one possible fun defs, rule out impossible ones by // comparing each fun def by some basic criteria against the lambda, // creating a new list containing only the remaining possibilities. if(astlist_length(possible_fun_defs) > 1) { astlist_t* new_fun_defs = NULL; astlist_t* new_obj_caps = NULL; astlist_t* fun_def_cursor = possible_fun_defs; astlist_t* obj_cap_cursor = possible_obj_caps; for(; (fun_def_cursor != NULL) && (obj_cap_cursor != NULL); fun_def_cursor = astlist_next(fun_def_cursor), obj_cap_cursor = astlist_next(obj_cap_cursor)) { ast_t* fun_def = astlist_data(fun_def_cursor); ast_t* def_obj_cap = astlist_data(obj_cap_cursor); if(is_typecheck_error(fun_def)) continue; AST_GET_CHILDREN(fun_def, def_receiver_cap, def_name, def_t_params, def_params, def_ret_type, def_raises); // Must have the same number of parameters. if(ast_childcount(params) != ast_childcount(def_params)) continue; // Must have a supercap of the def's receiver cap (if present). if((ast_id(receiver_cap) != TK_NONE) && !is_cap_sub_cap( ast_id(def_receiver_cap), TK_NONE, ast_id(receiver_cap), TK_NONE) ) continue; // Must have a supercap of the def's object cap (if present). if((ast_id(obj_cap) != TK_NONE) && !is_cap_sub_cap(ast_id(obj_cap), TK_NONE, ast_id(def_obj_cap), TK_NONE)) continue; // TODO: This logic could potentially be expanded to do deeper // compatibility checks, but checks involving subtyping here would be // difficult, because the lambda's AST is not caught up yet in the passes. new_fun_defs = astlist_push(new_fun_defs, fun_def); new_obj_caps = astlist_push(new_obj_caps, def_obj_cap); } astlist_free(possible_fun_defs); astlist_free(possible_obj_caps); possible_fun_defs = new_fun_defs; possible_obj_caps = new_obj_caps; } if(astlist_length(possible_fun_defs) == 1) { ast_t* fun_def = astlist_data(possible_fun_defs); ast_t* def_obj_cap = astlist_data(possible_obj_caps); // Try to complete the lambda's type info by inferring from the lambda type. if(!is_typecheck_error(fun_def)) { // Infer the object cap, receiver cap, and return type if unspecified. if(ast_id(obj_cap) == TK_NONE) ast_replace(&obj_cap, def_obj_cap); if(ast_id(receiver_cap) == TK_NONE) ast_replace(&receiver_cap, ast_child(fun_def)); if(ast_id(ret_type) == TK_NONE) ast_replace(&ret_type, ast_childidx(fun_def, 4)); // Infer the type of any parameters that were left unspecified. ast_t* param = ast_child(params); ast_t* def_param = ast_child(ast_childidx(fun_def, 3)); while((param != NULL) && (def_param != NULL)) { ast_t* param_id = ast_child(param); ast_t* param_type = ast_sibling(param_id); // Convert a "_" parameter to whatever the expected parameter is. if(is_name_dontcare(ast_name(param_id))) { ast_replace(¶m_id, ast_child(def_param)); ast_replace(¶m_type, ast_childidx(def_param, 1)); } // Give a type-unspecified parameter the type of the expected parameter. else if(ast_id(param_type) == TK_NONE) { ast_replace(¶m_type, ast_childidx(def_param, 1)); } param = ast_sibling(param); def_param = ast_sibling(def_param); } } ast_free_unattached(fun_def); } astlist_free(possible_obj_caps); // If any parameters still have no type specified, it's an error. ast_t* param = ast_child(params); while(param != NULL) { if(ast_id(ast_childidx(param, 1)) == TK_NONE) { ast_error(opt->check.errors, param, "a lambda parameter must specify a type or be inferable from context"); if(astlist_length(possible_fun_defs) > 1) { for(astlist_t* fun_def_cursor = possible_fun_defs; fun_def_cursor != NULL; fun_def_cursor = astlist_next(fun_def_cursor)) { ast_error_continue(opt->check.errors, astlist_data(fun_def_cursor), "this lambda interface is inferred, but it's not the only one"); } } astlist_free(possible_fun_defs); return false; } param = ast_sibling(param); } astlist_free(possible_fun_defs); bool bare = ast_id(ast) == TK_BARELAMBDA; ast_t* members = ast_from(ast, TK_MEMBERS); ast_t* last_member = NULL; bool failed = false; if(bare) pony_assert(ast_id(captures) == TK_NONE); // Process captures for(ast_t* p = ast_child(captures); p != NULL; p = ast_sibling(p)) { ast_t* field = NULL; bool ok = make_capture_field(opt, p, &field); if(field != NULL) ast_list_append(members, &last_member, field); else if(!ok) // An error occurred, just keep going to potentially find more errors failed = true; } if(failed) { ast_free(members); return false; } // Stop the various elements being marked as preserve ast_clearflag(t_params, AST_FLAG_PRESERVE); ast_clearflag(params, AST_FLAG_PRESERVE); ast_clearflag(ret_type, AST_FLAG_PRESERVE); ast_clearflag(body, AST_FLAG_PRESERVE); const char* fn_name = "apply"; if(ast_id(name) == TK_ID) fn_name = ast_name(name); // Make the apply function BUILD(apply, ast, NODE(TK_FUN, AST_SCOPE ANNOTATE(annotation) TREE(receiver_cap) ID(fn_name) TREE(t_params) TREE(params) TREE(ret_type) TREE(raises) TREE(body) NONE)); // Doc string ast_list_append(members, &last_member, apply); ast_setflag(members, AST_FLAG_PRESERVE); printbuf_t* buf = printbuf_new(); printbuf(buf, bare ? "@{(" : "{("); bool first = true; for(ast_t* p = ast_child(params); p != NULL; p = ast_sibling(p)) { if(first) first = false; else printbuf(buf, ", "); printbuf(buf, "%s", ast_print_type(ast_childidx(p, 1))); } printbuf(buf, ")"); if(ast_id(ret_type) != TK_NONE) printbuf(buf, ": %s", ast_print_type(ret_type)); if(ast_id(raises) != TK_NONE) printbuf(buf, " ?"); printbuf(buf, "}"); // Replace lambda with object literal REPLACE(astp, NODE(TK_OBJECT, DATA(stringtab(buf->m)) TREE(obj_cap) NONE // Provides list TREE(members))); printbuf_free(buf); if(bare) { BUILD(bare_annotation, *astp, NODE(TK_ANNOTATION, ID("ponyint_bare"))); // Record the syntax pass as done to avoid the error about internal // annotations. ast_pass_record(bare_annotation, PASS_SYNTAX); ast_setannotation(*astp, bare_annotation); } // Catch up passes if(ast_visit(astp, pass_syntax, NULL, opt, PASS_SYNTAX) != AST_OK) return false; return ast_passes_subtree(astp, opt, PASS_EXPR); }