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, 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(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); // Make the apply function BUILD(apply, ast, NODE(TK_FUN, AST_SCOPE NONE // Capability ID("apply") TREE(t_params) TREE(params) TREE(ret_type) TREE(raises) TREE(body) NONE)); // Doc string ast_list_append(members, &last_member, apply); // Replace lambda with object literal REPLACE(astp, NODE(TK_OBJECT, TREE(cap); NONE // Provides list TREE(members))); // Catch up passes return ast_passes_subtree(astp, opt, PASS_EXPR); }
// Check that the given method is compatible with the existing definition. // Return the method that ends up being in the entity or NULL on error. // Stages 2A, 2B and 2C. static ast_t* add_method(ast_t* entity, ast_t* existing_method, ast_t* new_method, ast_t** last_method) { assert(entity != NULL); assert(new_method != NULL); assert(last_method != NULL); const char* name = ast_name(ast_childidx(new_method, 1)); const char* entity_name = ast_name(ast_child(entity)); if(existing_method == NULL) { // This method is new to the entity. // Stage 2C. ast_t* case_clash = ast_get_case(entity, name, NULL); if(case_clash != NULL) { ast_error(case_clash, "in %s method name %s differs only in case", entity_name, name); ast_error(new_method, "clashing method is here"); return NULL; } attach_method_t(new_method, NULL, false); ast_list_append(ast_childidx(entity, 4), last_method, new_method); ast_set(entity, name, new_method, SYM_DEFINED); return *last_method; } method_t* info = (method_t*)ast_data(existing_method); assert(info != NULL); if(info->local_def) { // Existing method is a local definition, new method must be a subtype // Stage 2A if(is_subtype(existing_method, new_method, true)) return existing_method; ast_error(existing_method, "local method %s is not compatible with provided version", name); ast_error(new_method, "clashing method is here"); return NULL; } // Both method versions came from the provides list, their signatures must // match exactly // Stage 2B if(compare_signatures(existing_method, new_method)) return existing_method; ast_error(entity, "clashing definitions of method %s provided, local " "disambiguation required", name); ast_error(existing_method, "provided here"); ast_error(new_method, "and here"); return NULL; }
// Sugar any case methods in the given entity. ast_result_t sugar_case_methods(typecheck_t* t, ast_t* entity) { assert(entity != NULL); ast_result_t result = AST_OK; ast_t* members = ast_childidx(entity, 4); bool cases_found = false; // Check each method to see if its name is repeated, which indicates a case // method. for(ast_t* p = ast_child(members); p != NULL; p = ast_sibling(p)) { if(ast_id(p) == TK_FUN || ast_id(p) == TK_BE) { const char* p_name = ast_name(ast_childidx(p, 1)); // Check if any subsequent methods share p's name. for(ast_t* q = ast_sibling(p); q != NULL; q = ast_sibling(q)) { if(ast_id(q) == TK_FUN || ast_id(q) == TK_BE) { const char* q_name = ast_name(ast_childidx(q, 1)); if(q_name == p_name) { // p's name is repeated, it's a case method, get a match method. if(!sugar_case_method(p, members, p_name, t)) result = AST_ERROR; cases_found = true; break; } } } } } if(cases_found) { // Remove nodes marked during processing. ast_t* filtered_members = ast_from(members, TK_MEMBERS); ast_t* list = NULL; ast_t* member; while((member = ast_pop(members)) != NULL) { if(ast_id(member) == TK_NONE) // Marked for removal. ast_free(member); else // Put back in filtered list. ast_list_append(filtered_members, &list, member); } ast_replace(&members, filtered_members); } return result; }
// Convert the given method into a delegation indirection to the specified // field. static void make_delegation(ast_t* method, ast_t* field, ast_t* delegate_ref, ast_t* body_donor) { assert(method != NULL); assert(field != NULL); assert(delegate_ref != NULL); // Make a redirection method body. ast_t* args = ast_from(delegate_ref, TK_NONE); ast_t* last_arg = NULL; AST_GET_CHILDREN(method, cap, id, t_params, params, result, error, old_body); for(ast_t* p = ast_child(params); p != NULL; p = ast_sibling(p)) { const char* param_name = ast_name(ast_child(p)); BUILD(arg, delegate_ref, NODE(TK_SEQ, NODE(TK_CONSUME, NONE NODE(TK_REFERENCE, ID(param_name))))); ast_list_append(args, &last_arg, arg); ast_setid(args, TK_POSITIONALARGS); } BUILD(body, delegate_ref, NODE(TK_SEQ, NODE(TK_CALL, TREE(args) // Positional args. NODE(TK_NONE) // Named args. NODE(TK_DOT, // Receiver. NODE(TK_REFERENCE, ID(ast_name(ast_child(field)))) ID(ast_name(ast_childidx(method, 1))))))); if(is_none(result)) { // Add None to end of body. Whilst the call generated above will return // None anyway in this case, without this extra None testing is very hard // since a directly written version of this body will have the None. BUILD(none, delegate_ref, NODE(TK_REFERENCE, ID("None"))); ast_append(body, none); } ast_replace(&old_body, body); // Setup method info. method_t* info = (method_t*)ast_data(method); assert(info != NULL); info->body_donor = body_donor; info->delegated_field = field; }
// Process the given provides type static bool flatten_provided_type(pass_opt_t* opt, ast_t* provides_type, ast_t* error_at, ast_t* list_parent, ast_t** list_end) { pony_assert(error_at != NULL); pony_assert(provides_type != NULL); pony_assert(list_parent != NULL); pony_assert(list_end != NULL); switch(ast_id(provides_type)) { case TK_PROVIDES: case TK_ISECTTYPE: // Flatten all children for(ast_t* p = ast_child(provides_type); p != NULL; p = ast_sibling(p)) { if(!flatten_provided_type(opt, p, error_at, list_parent, list_end)) return false; } return true; case TK_NOMINAL: { // Check type is a trait or interface ast_t* def = (ast_t*)ast_data(provides_type); pony_assert(def != NULL); if(ast_id(def) != TK_TRAIT && ast_id(def) != TK_INTERFACE) { ast_error(opt->check.errors, error_at, "can only provide traits and interfaces"); ast_error_continue(opt->check.errors, provides_type, "invalid type here"); return false; } // Add type to new provides list ast_list_append(list_parent, list_end, provides_type); ast_setdata(*list_end, ast_data(provides_type)); return true; } default: ast_error(opt->check.errors, error_at, "provides type may only be an intersect of traits and interfaces"); ast_error_continue(opt->check.errors, provides_type, "invalid type here"); return false; } }
/* ast_decl_new: */ static AST *ast_decl_new ( ASTType type, AST *context, CIdentType ident_type, AST *ident ) { AST *node; ASSERT(ast_type_isa(type, AST_TYPE_DECL)); ASSERT(AST_IS_IDENT(ident)); node = ast_new(type); AST_DECL(node)->ident = ident; if (context) { ASSERT(AST_IS_DECL(context)); AST_DECL(node)->context = context; AST_DECL(context)->members = ast_list_append(AST_DECL(context)->members, node); AST_DECL(node)->cident = c_ident_new(ident_type, AST_IDENT_NAME(ident), AST_DECL(context)->cident); } else { AST_DECL(node)->cident = c_ident_new(ident_type, AST_IDENT_NAME(ident), NULL); } return node; }
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); }
// Resolve the field delegate body to use for the given method, if any. // Return the body to use, NULL if none found or BODY_ERROR on error. static ast_t* resolve_delegate_body(ast_t* entity, ast_t* method, method_t* info, const char* name) { assert(entity != NULL); assert(method != NULL); assert(info != NULL); assert(name != NULL); if(info->delegate_field_2 != NULL) { // Ambiguous delegate assert(info->delegate_field_1 != NULL); assert(info->delegate_target_1 != NULL); assert(info->delegate_target_2 != NULL); ast_error(entity, "clashing delegates for method %s, local disambiguation required", name); ast_error(info->delegate_field_1, "field %s delegates to %s via %s", ast_name(ast_child(info->delegate_field_1)), name, ast_name(ast_child(info->delegate_target_1))); ast_error(info->delegate_field_2, "field %s delegates to %s via %s", ast_name(ast_child(info->delegate_field_2)), name, ast_name(ast_child(info->delegate_target_2))); return BODY_ERROR; } if(info->delegate_field_1 == NULL) // No delegation needed return NULL; // We have a delegation, make a redirection body const char* field_name = ast_name(ast_child(info->delegate_field_1)); ast_t* args = ast_from(info->delegate_field_1, TK_NONE); ast_t* last_arg = NULL; AST_GET_CHILDREN(method, cap, id, t_params, params, result, error, old_body); for(ast_t* p = ast_child(params); p != NULL; p = ast_sibling(p)) { const char* param_name = ast_name(ast_child(p)); BUILD(arg, info->delegate_field_1, NODE(TK_SEQ, NODE(TK_CONSUME, NONE NODE(TK_REFERENCE, ID(param_name))))); ast_list_append(args, &last_arg, arg); ast_setid(args, TK_POSITIONALARGS); } BUILD(body, info->delegate_field_1, NODE(TK_SEQ, NODE(TK_CALL, TREE(args) // Positional args NODE(TK_NONE) // Named args NODE(TK_DOT, // Receiver NODE(TK_REFERENCE, ID(field_name)) ID(name))))); if(is_none(result)) { // Add None to end of body. Whilst the call generated above will return // None anyway in this case, without this extra None testing is very hard // since a directly written version of this body will have the None. BUILD(none, info->delegate_field_1, NODE(TK_REFERENCE, ID("None"))); ast_append(body, none); } info->body_donor = entity; return body; }
static bool capture_from_reference(pass_opt_t* opt, ast_t* ctx, ast_t* ast, ast_t* captures, ast_t** last_capture) { const char* name = ast_name(ast_child(ast)); if(is_name_dontcare(name)) return true; ast_t* refdef = ast_get(ast, name, NULL); if(refdef != NULL) return true; refdef = ast_get(ctx, name, NULL); if(refdef == NULL) { ast_error(opt->check.errors, ast, "cannot capture \"%s\", variable not defined", name); return false; } if(!def_before_use(opt, refdef, ctx, name)) return false; switch(ast_id(refdef)) { case TK_VAR: case TK_LET: case TK_PARAM: case TK_MATCH_CAPTURE: case TK_FVAR: case TK_FLET: case TK_EMBED: break; default: ast_error(opt->check.errors, ast, "cannot capture \"%s\", can only " "capture fields, parameters and local variables", name); return NULL; } // Check if we've already captured it for(ast_t* p = ast_child(captures); p != NULL; p = ast_sibling(p)) { AST_GET_CHILDREN(p, c_name, c_type); if(name == ast_name(c_name)) return true; } ast_t* type = alias(ast_type(refdef)); if(is_typecheck_error(type)) return false; type = sanitise_type(type); BUILD(field, ast, NODE(TK_FVAR, ID(name) TREE(type) NODE(TK_REFERENCE, ID(name)))); ast_list_append(captures, last_capture, field); return true; }
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); }
// Make a skeleton wrapper method based on the given case method. // Returns: created method, NULL on error. static ast_t* make_match_wrapper(ast_t* case_method) { assert(case_method != NULL); if(ast_id(case_method) == TK_FUN) fun_defaults(case_method); AST_GET_CHILDREN(case_method, cap, id, t_params, params, ret_type, question, body, docstring); if(ast_child(params) == NULL) { ast_error(params, "case method must have at least one parameter"); return NULL; } ast_t* new_params = ast_from(params, TK_PARAMS); ast_t* param_list_end = NULL; for(ast_t* p = ast_child(params); p != NULL; p = ast_sibling(p)) { // Set all parameter info to none now and fill it in later. BUILD(new_param, p, NODE(TK_PARAM, NONE NONE NONE)); ast_list_append(new_params, ¶m_list_end, new_param); } ast_t* new_t_params = ast_from(params, TK_NONE); ast_t* t_param_list_end = NULL; for(ast_t* p = ast_child(t_params); p != NULL; p = ast_sibling(p)) { // Set all type parameter info to none now and fill it in later. BUILD(new_t_param, p, NODE(TK_TYPEPARAM, NONE NONE NONE)); ast_list_append(new_t_params, &t_param_list_end, new_t_param); ast_setid(new_t_params, TK_TYPEPARAMS); } if(ast_id(case_method) == TK_FUN) { // Function case. // TODO: For now always need a `None |` in the return type to allow for // match else clause. We won't need that once exhaustive matching is done. BUILD(wrapper, case_method, NODE(ast_id(case_method), AST_SCOPE TREE(cap) TREE(id) TREE(new_t_params) TREE(new_params) NODE(TK_NOMINAL, NONE ID("None") NONE NONE NONE) // Return value. NONE // Error. NODE(TK_SEQ) // Body. NONE // Doc string. NONE)); // Guard. return wrapper; } else { // Behaviour case. BUILD(wrapper, case_method, NODE(ast_id(case_method), AST_SCOPE NONE // Capability. TREE(id) TREE(new_t_params) TREE(new_params) NONE // Return value. NONE // Error. NODE(TK_SEQ) // Body. NONE // Doc string. NONE)); // Guard. return wrapper; } }
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); }