static void names_applycap_index(ast_t* ast, ast_t* cap, ast_t* ephemeral, int index) { ast_t* a_cap = ast_childidx(ast, index); ast_t* a_ephemeral = ast_sibling(a_cap); if(ast_id(cap) != TK_NONE) ast_replace(&a_cap, cap); if(ast_id(ephemeral) != TK_NONE) ast_replace(&a_ephemeral, ephemeral); }
static bool reify_typeparamref(ast_t** astp, ast_t* typeparam, ast_t* typearg) { ast_t* ast = *astp; assert(ast_id(ast) == TK_TYPEPARAMREF); ast_t* ref_name = ast_child(ast); ast_t* param_name = ast_child(typeparam); if(ast_name(ref_name) != ast_name(param_name)) return false; // Keep ephemerality. switch(ast_id(ast_childidx(ast, 2))) { case TK_EPHEMERAL: typearg = consume_type(typearg, TK_NONE); break; case TK_NONE: break; case TK_BORROWED: typearg = alias(typearg); break; default: assert(0); return false; } ast_replace(astp, typearg); return true; }
static ast_result_t sugar_new(pass_opt_t* opt, ast_t* ast) { typecheck_t* t = &opt->check; AST_GET_CHILDREN(ast, cap, id, typeparams, params, result); // Return type default to ref^ for classes, val^ for primitives, and // tag^ for actors. if(ast_id(result) == TK_NONE) { token_id tcap = ast_id(cap); if(tcap == TK_NONE) { switch(ast_id(t->frame->type)) { case TK_PRIMITIVE: tcap = TK_VAL; break; case TK_ACTOR: tcap = TK_TAG; break; default: tcap = TK_REF; break; } ast_setid(cap, tcap); } ast_replace(&result, type_for_this(opt, ast, tcap, TK_EPHEMERAL, false)); } sugar_docstring(ast); return check_method(ast); }
static void reify_typeparamref(ast_t** astp, ast_t* typeparam, ast_t* typearg) { ast_t* ast = *astp; assert(ast_id(ast) == TK_TYPEPARAMREF); ast_t* ref_def = (ast_t*)ast_data(ast); ast_t* param_def = (ast_t*)ast_data(typeparam); assert(ref_def != NULL); assert(param_def != NULL); if(ref_def != param_def) return; // Keep ephemerality. switch(ast_id(ast_childidx(ast, 2))) { case TK_EPHEMERAL: typearg = consume_type(typearg, TK_NONE); break; case TK_NONE: break; case TK_BORROWED: typearg = alias(typearg); break; default: assert(0); } ast_replace(astp, typearg); }
static ast_result_t sugar_ffi(ast_t* ast) { AST_GET_CHILDREN(ast, id, typeargs, args, named_args); const char* name = ast_name(id); size_t len = ast_name_len(id); // Check for \0 in ffi name (it may be a string literal) if(memchr(name, '\0', len) != NULL) { ast_error(ast, "FFI function names cannot include nul characters"); return AST_ERROR; } // Prefix '@' to the name char* new_name = (char*)pool_alloc_size(len + 2); new_name[0] = '@'; memcpy(new_name + 1, name, len); new_name[len + 1] = '\0'; ast_t* new_id = ast_from_string(id, stringtab_consume(new_name, len + 2)); ast_replace(&id, new_id); if(ast_id(ast) == TK_FFIDECL) return check_params(args); return AST_OK; }
static bool check_type_params(ast_t** astp) { ast_t* lhs = *astp; ast_t* type = ast_type(lhs); if(is_typecheck_error(type)) return false; ast_t* typeparams = ast_childidx(type, 1); assert(ast_id(type) == TK_FUNTYPE); if(ast_id(typeparams) == TK_NONE) return true; BUILD(typeargs, typeparams, NODE(TK_TYPEARGS)); if(!check_constraints(typeparams, typeargs, true)) { ast_free_unattached(typeargs); return false; } type = reify(type, typeparams, typeargs); typeparams = ast_childidx(type, 1); ast_replace(&typeparams, ast_from(typeparams, TK_NONE)); REPLACE(astp, NODE(ast_id(lhs), TREE(lhs) TREE(typeargs))); ast_settype(*astp, type); return true; }
static bool names_type(typecheck_t* t, ast_t** astp, ast_t* def) { ast_t* ast = *astp; AST_GET_CHILDREN(ast, package, id, typeparams, cap, eph); token_id tcap = ast_id(cap); if((tcap == TK_NONE) && (ast_id(def) == TK_PRIMITIVE)) { // A primitive without a capability is a val, even if it is a constraint. tcap = TK_VAL; } else if(t->frame->constraint != NULL) { // A constraint is modified to a generic capability. switch(tcap) { case TK_NONE: tcap = TK_ANY_GENERIC; break; case TK_BOX: tcap = TK_BOX_GENERIC; break; case TK_TAG: tcap = TK_TAG_GENERIC; break; default: {} } } else if(tcap == TK_NONE) { // Otherwise, we use the default capability. tcap = ast_id(ast_childidx(def, 2)); } ast_setid(cap, tcap); // Keep the actual package id. ast_append(ast, package); ast_replace(&package, package_id(def)); // Store our definition for later use. ast_setdata(ast, def); return true; }
static ast_result_t flatten_arrow(pass_opt_t* opt, ast_t** astp) { AST_GET_CHILDREN(*astp, left, right); switch(ast_id(left)) { case TK_ISO: case TK_TRN: case TK_REF: case TK_VAL: case TK_BOX: case TK_TAG: case TK_THISTYPE: case TK_TYPEPARAMREF: { ast_t* r_ast = viewpoint_type(left, right); ast_replace(astp, r_ast); return AST_OK; } default: {} } ast_error(opt->check.errors, left, "only 'this', refcaps, and type parameters can be viewpoints"); return AST_ERROR; }
void AST_t::replace_with(AST_t ast) { if (ast._ast == NULL) { internal_error("Trying to replace a tree with an empty tree.", 0); } if (this->_ast == NULL) { internal_error("Trying to replace an empty tree with another tree", 0); } AST previous_parent = ASTParent(this->_ast); ast_replace(this->_ast, ast._ast); ast_set_parent(this->_ast, previous_parent); // Relink sons for (int i = 0; i < ASTNumChildren(this->_ast); i++) { if (ASTChild(this->_ast, i) != NULL) { ast_set_parent(ASTChild(this->_ast, i), this->_ast); } } }
static ast_t* make_single_arrow(ast_t* left, ast_t* right) { switch(ast_id(left)) { case TK_ARROW: { ast_t* arrow = ast_dup(left); ast_t* child = ast_childidx(arrow, 1); // Arrow is right associative. while(ast_id(child) == TK_ARROW) child = ast_childidx(child, 1); ast_t* view = viewpoint_type(child, right); if(view == NULL) { ast_free_unattached(arrow); return NULL; } ast_replace(&child, view); return arrow; } default: {} } ast_t* arrow = ast_from(left, TK_ARROW); ast_add(arrow, right); ast_add(arrow, left); return arrow; }
static bool names_typealias(pass_opt_t* opt, ast_t** astp, ast_t* def) { ast_t* ast = *astp; AST_GET_CHILDREN(ast, pkg, id, typeargs, cap, eph); // Make sure the alias is resolved, AST_GET_CHILDREN(def, alias_id, typeparams, def_cap, provides); ast_t* alias = ast_child(provides); if(!names_resolvealias(opt, def, &alias)) return false; // Reify the alias. ast_t* r_alias = reify(typeparams, alias, typeparams, typeargs); if(r_alias == NULL) return false; // Apply our cap and ephemeral to the result. if(!names_applycap(r_alias, cap, eph)) { ast_free_unattached(r_alias); return false; } // Maintain the position info of the original reference to aid error // reporting. ast_setpos(r_alias, ast_line(ast), ast_pos(ast)); // Replace this with the alias. ast_replace(astp, r_alias); return true; }
// Attach the appropriate body (if any) from the given method list to the given // entity method static void attach_body_from_list(ast_t* list, ast_t* entity_method) { assert(list != NULL); assert(entity_method != NULL); void* data = ast_data(entity_method); for(ast_t* p = ast_child(list); p != NULL; p = ast_sibling(p)) { void* p_data = ast_data(p); if(p_data != NULL && p_data != data && is_eqtype(entity_method, p)) { // p has a valid (and different) body if(data != NULL || p_data == BODY_AMBIGUOUS) { // Multiple possible bodies ast_setdata(entity_method, BODY_AMBIGUOUS); return; } // This is the first valid body, use it ast_t* old_body = ast_childidx(entity_method, 6); assert(ast_id(old_body) == TK_NONE); ast_replace(&old_body, ast_childidx(p, 6)); ast_setdata(entity_method, p_data); data = p_data; } } }
static void add_field_to_object(pass_opt_t* opt, ast_t* field, ast_t* class_members, ast_t* create_params, ast_t* create_body, ast_t* call_args) { AST_GET_CHILDREN(field, id, type, init); ast_t* p_id = ast_from_string(id, package_hygienic_id(&opt->check)); // The param is: $0: type BUILD(param, field, NODE(TK_PARAM, TREE(p_id) TREE(type) NONE)); // The arg is: $seq init BUILD(arg, init, NODE(TK_SEQ, TREE(init))); // The body of create contains: id = consume $0 BUILD(assign, init, NODE(TK_ASSIGN, NODE(TK_CONSUME, NODE(TK_NONE) NODE(TK_REFERENCE, TREE(p_id))) NODE(TK_REFERENCE, TREE(id)))); // Remove the initialiser from the field ast_replace(&init, ast_from(init, TK_NONE)); ast_add(class_members, field); ast_append(create_params, param); ast_append(create_body, assign); ast_append(call_args, arg); }
void fun_defaults(ast_t* ast) { assert(ast != NULL); AST_GET_CHILDREN(ast, cap, id, typeparams, params, result, can_error, body, docstring); // If the receiver cap is not specified, set it to box. if(ast_id(cap) == TK_NONE) ast_setid(cap, TK_BOX); // If the return value is not specified, set it to None. if(ast_id(result) == TK_NONE) { ast_t* type = type_sugar(ast, NULL, "None"); ast_replace(&result, type); } // If the return type is None, add a None at the end of the body, unless it // already ends with an error or return statement if(is_none(result) && (ast_id(body) != TK_NONE)) { ast_t* last_cmd = ast_childlast(body); if(ast_id(last_cmd) != TK_ERROR && ast_id(last_cmd) != TK_RETURN) { BUILD_NO_DEBUG(ref, body, NODE(TK_REFERENCE, ID("None"))); ast_append(body, ref); } } }
static bool apply_named_args(pass_opt_t* opt, ast_t* params, ast_t* positional, ast_t* namedargs) { ast_t* namedarg = ast_pop(namedargs); while(namedarg != NULL) { AST_GET_CHILDREN(namedarg, arg_id, arg); ast_t* param = ast_child(params); size_t param_index = 0; while(param != NULL) { AST_GET_CHILDREN(param, param_id); if(ast_name(arg_id) == ast_name(param_id)) break; param = ast_sibling(param); param_index++; } if(param == NULL) { if(ast_id(namedarg) == TK_UPDATEARG) { ast_error(opt->check.errors, arg_id, "cannot use sugar, update() has no parameter named \"value\""); return false; } ast_error(opt->check.errors, arg_id, "not a parameter name"); return false; } ast_t* arg_replace = ast_childidx(positional, param_index); if(ast_id(arg_replace) != TK_NONE) { ast_error(opt->check.errors, arg_id, "named argument is already supplied"); ast_error_continue(opt->check.errors, arg_replace, "supplied argument is here"); return false; } // Extract named argument expression to avoid copying it ast_free(ast_pop(namedarg)); // ID arg = ast_pop(namedarg); // Expression ast_replace(&arg_replace, arg); namedarg = ast_pop(namedargs); } ast_setid(namedargs, TK_NONE); return true; }
// 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; }
// Process the given provided method for the given entity. // The method passed should be reified already and will be freed by this // function. static bool provided_method(pass_opt_t* opt, ast_t* entity, ast_t* reified_method, ast_t* raw_method, ast_t** last_method) { assert(entity != NULL); assert(reified_method != NULL); assert(last_method != NULL); AST_GET_CHILDREN(reified_method, cap, id, typeparams, params, result, can_error, body, doc); if(ast_id(reified_method) == TK_BE || ast_id(reified_method) == TK_NEW) { // Modify return type to the inheriting type ast_t* this_type = type_for_this(opt, entity, ast_id(cap), TK_EPHEMERAL, true); ast_replace(&result, this_type); } // Ignore docstring if(ast_id(doc) == TK_STRING) { ast_set_name(doc, ""); ast_setid(doc, TK_NONE); } // Check for existing method of the same name const char* name = ast_name(id); assert(name != NULL); ast_t* existing = ast_get(entity, name, NULL); if(existing != NULL && is_field(existing)) { ast_error(existing, "field '%s' clashes with provided method", name); ast_error(raw_method, "method is defined here"); ast_free_unattached(reified_method); return false; } existing = add_method(entity, existing, reified_method, last_method); if(existing == NULL) { ast_free_unattached(reified_method); return false; } method_t* info = (method_t*)ast_data(existing); assert(info != NULL); if(!record_default_body(reified_method, raw_method, info)) ast_free_unattached(reified_method); return true; }
// 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; }
static ast_result_t sugar_with(typecheck_t* t, ast_t** astp) { AST_EXTRACT_CHILDREN(*astp, withexpr, body, else_clause); token_id try_token; if(ast_id(else_clause) == TK_NONE) try_token = TK_TRY_NO_CHECK; else try_token = TK_TRY; expand_none(else_clause, false); // First build a skeleton try block without the "with" variables BUILD(replace, *astp, NODE(TK_SEQ, NODE(try_token, NODE(TK_SEQ, AST_SCOPE TREE(body)) NODE(TK_SEQ, AST_SCOPE TREE(else_clause)) NODE(TK_SEQ, AST_SCOPE)))); ast_t* tryexpr = ast_child(replace); AST_GET_CHILDREN(tryexpr, try_body, try_else, try_then); // Add the "with" variables from each with element for(ast_t* p = ast_child(withexpr); p != NULL; p = ast_sibling(p)) { assert(ast_id(p) == TK_SEQ); AST_GET_CHILDREN(p, idseq, init); const char* init_name = package_hygienic_id(t); BUILD(assign, idseq, NODE(TK_ASSIGN, AST_NODEBUG TREE(init) NODE(TK_LET, ID(init_name) NONE))); BUILD(local, idseq, NODE(TK_ASSIGN, AST_NODEBUG NODE(TK_REFERENCE, ID(init_name)) TREE(idseq))); ast_add(replace, assign); ast_add(try_body, local); ast_add(try_else, local); build_with_dispose(try_then, idseq); ast_add(try_then, local); } ast_replace(astp, replace); return AST_OK; }
// 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; }
static ast_result_t sugar_be(pass_opt_t* opt, ast_t* ast) { AST_GET_CHILDREN(ast, cap, id, typeparams, params, result, can_error, body); ast_setid(cap, TK_TAG); if(ast_id(result) == TK_NONE) { // Return type is This tag ast_replace(&result, type_for_this(opt, ast, TK_TAG, TK_NONE, false)); } sugar_docstring(ast); return check_method(ast); }
bool flatten_arrows(ast_t** astp, bool errors) { ast_t* ast = *astp; ast_t* node_type = ast_type(ast); if(node_type != NULL) { if(!flatten_arrows(&node_type, errors)) return false; } if(ast_id(ast) == TK_ARROW) { AST_GET_CHILDREN(ast, left, right); ast_t* flat; if(!flatten_arrows(&right, errors)) return false; if((ast_id(left) == TK_BOXTYPE) && (ast_id(right) != TK_ARROW)) flat = viewpoint_cap(TK_BOX, TK_NONE, right); else flat = viewpoint_type(left, right); if(flat == NULL) { if(errors) ast_error(ast, "can't flatten arrow type"); return false; } ast_replace(astp, flat); return true; } ast_t* child = ast_child(ast); while(child != NULL) { if(!flatten_arrows(&child, errors)) return false; child = ast_sibling(child); } return true; }
static bool reify_one(ast_t** astp, ast_t* typeparam, ast_t* typearg) { ast_t* ast = *astp; ast_t* type = ast_type(ast); if(type != NULL) reify_one(&type, typeparam, typearg); if(ast_id(ast) == TK_TYPEPARAMREF) return reify_typeparamref(astp, typeparam, typearg); ast_t* child = ast_child(ast); bool flatten = false; while(child != NULL) { flatten |= reify_one(&child, typeparam, typearg); child = ast_sibling(child); } // Flatten type expressions after reifying them. if(flatten) { switch(ast_id(ast)) { case TK_ARROW: { AST_GET_CHILDREN(ast, left, right); ast = viewpoint_type(left, right); if(ast == NULL) return false; if(ast == right) ast = ast_dup(ast); ast_replace(astp, ast); return true; } default: {} } } return false; }
// Flatten a provides type into a list, checking all types are traits or // interfaces static ast_result_t flatten_provides_list(ast_t* provider, int index) { assert(provider != NULL); ast_t* provides = ast_childidx(provider, index); ast_t* list = ast_from(provides, TK_PROVIDES); ast_t* list_end = NULL; if(ast_id(provides) != TK_NONE && !flatten_provided_type(provides, provider, list, &list_end)) { ast_free(list); return AST_ERROR; } ast_replace(&provides, list); return AST_OK; }
static void reify_arrow(ast_t** astp) { ast_t* ast = *astp; assert(ast_id(ast) == TK_ARROW); AST_GET_CHILDREN(ast, left, right); ast_t* r_left = left; ast_t* r_right = right; if(ast_id(left) == TK_ARROW) { AST_GET_CHILDREN(left, l_left, l_right); r_left = l_left; r_right = viewpoint_type(l_right, right); } ast_t* r_type = viewpoint_type(r_left, r_right); ast_replace(astp, r_type); }
static bool make_tuple_index(ast_t** astp) { ast_t* ast = *astp; const char* name = ast_name(ast); if(!is_name_private(name)) return false; for(size_t i = 1; name[i] != '\0'; i++) { if((name[i] < '0') || (name[i] > '9')) return false; } size_t index = strtol(&name[1], NULL, 10) - 1; ast_t* node = ast_from_int(ast, index); ast_replace(astp, node); return true; }
static void reify_typeparamref(ast_t** astp, ast_t* typeparam, ast_t* typearg) { ast_t* ast = *astp; pony_assert(ast_id(ast) == TK_TYPEPARAMREF); pony_assert(ast_id(typeparam) == TK_TYPEPARAM); ast_t* ref_def = (ast_t*)ast_data(ast); // We can't compare ref_def and typeparam, as they could be a copy or // a iftype shadowing. However, their data points back to the original // typeparam definition, which can be compared. ref_def = (ast_t*)ast_data(ref_def); typeparam = (ast_t*)ast_data(typeparam); pony_assert(ref_def != NULL); pony_assert(typeparam != NULL); if(ref_def != typeparam) return; // Keep ephemerality. switch(ast_id(ast_childidx(ast, 2))) { case TK_EPHEMERAL: typearg = consume_type(typearg, TK_NONE); break; case TK_NONE: break; case TK_ALIASED: typearg = alias(typearg); break; default: pony_assert(0); } ast_replace(astp, typearg); }
static void sugar_docstring(ast_t* ast) { assert(ast != NULL); AST_GET_CHILDREN(ast, cap, id, type_params, params, return_type, error, body, docstring); if(ast_id(docstring) == TK_NONE) { ast_t* first = ast_child(body); // First expression in body is a docstring if it is a string literal and // there are any other expressions in the body sequence if((first != NULL) && (ast_id(first) == TK_STRING) && (ast_sibling(first) != NULL)) { ast_pop(body); ast_replace(&docstring, first); } } }
static ast_result_t sugar_case(ast_t* ast) { ast_t* body = ast_childidx(ast, 2); if(ast_id(body) != TK_NONE) return AST_OK; // We have no body, take a copy of the next case with a body ast_t* next = ast; ast_t* next_body = body; while(ast_id(next_body) == TK_NONE) { next = ast_sibling(next); assert(next != NULL); assert(ast_id(next) == TK_CASE); next_body = ast_childidx(next, 2); } ast_replace(&body, next_body); return AST_OK; }
void replace_thistype(ast_t** astp, ast_t* type) { ast_t* ast = *astp; if(ast_id(ast) == TK_THISTYPE) { ast_replace(astp, type); return; } ast_t* node_type = ast_type(ast); if(node_type != NULL) replace_thistype(&node_type, type); ast_t* child = ast_child(ast); while(child != NULL) { replace_thistype(&child, type); child = ast_sibling(child); } }