// Add the given case method into the given match method wrapper and check the // are compatible. // Returns: match case for worker method or NULL on error. static ast_t* add_case_method(ast_t* match_method, ast_t* case_method) { assert(match_method != NULL); assert(case_method != NULL); // We need default capabality and return value if not provided explicitly. if(ast_id(case_method) == TK_FUN) fun_defaults(case_method); AST_GET_CHILDREN(match_method, match_cap, match_id, match_t_params, match_params, match_ret_type, match_question); AST_GET_CHILDREN(case_method, case_cap, case_id, case_t_params, case_params, case_ret_type, case_question, case_body, case_docstring, case_guard); bool ok = true; if(ast_id(case_method) != ast_id(match_method)) { ast_error(case_method, "cannot mix fun and be cases in a single match method"); ast_error(match_method, "clashing method here"); ok = false; } if(ast_id(case_method) == TK_FUN) { if(ast_id(case_cap) != ast_id(match_cap)) { ast_error(case_cap, "differing receiver capabilities on case methods"); ast_error(match_cap, "clashing capability here"); ok = false; } if(ast_id(match_ret_type) == TK_NONE) { // Use case method return type. ast_replace(&match_ret_type, case_ret_type); } else { // Union this case method's return type with the existing match one. REPLACE(&match_ret_type, NODE(TK_UNIONTYPE, TREE(match_ret_type) TREE(case_ret_type))); } } if(ast_id(case_question) == TK_QUESTION) // If any case throws the match does too. ast_setid(match_question, TK_QUESTION); if(!process_t_params(match_t_params, case_t_params)) ok = false; ast_t* pattern = process_params(match_params, case_params); if(!ok || pattern == NULL) { ast_free(pattern); return NULL; } // Extract case body and guard condition (if any) to avoid copying. ast_t* body = ast_from(case_body, TK_NONE); ast_swap(case_body, body); ast_t* guard = ast_from(case_guard, TK_NONE); ast_swap(case_guard, guard); // Make match case. BUILD(match_case, pattern, NODE(TK_CASE, AST_SCOPE TREE(pattern) TREE(case_guard) TREE(case_body))); return match_case; }
// 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; } }
static ast_result_t sugar_fun(ast_t* ast) { fun_defaults(ast); sugar_docstring(ast); return check_method(ast); }