static bool insert_apply(pass_opt_t* opt, ast_t** astp) { // Sugar .apply() ast_t* ast = *astp; AST_GET_CHILDREN(ast, positional, namedargs, lhs); ast_t* dot = ast_from(ast, TK_DOT); ast_add(dot, ast_from_string(ast, "apply")); ast_swap(lhs, dot); ast_add(dot, lhs); if(!expr_dot(opt, &dot)) return false; return expr_call(opt, astp); }
static bool type_access(pass_opt_t* opt, ast_t** astp) { ast_t* ast = *astp; // Left is a typeref, right is an id. ast_t* left = ast_child(ast); ast_t* right = ast_sibling(left); ast_t* type = ast_type(left); if(is_typecheck_error(type)) return false; assert(ast_id(left) == TK_TYPEREF); assert(ast_id(right) == TK_ID); ast_t* find = lookup(opt, ast, type, ast_name(right)); if(find == NULL) return false; bool ret = true; switch(ast_id(find)) { case TK_TYPEPARAM: ast_error(opt->check.errors, right, "can't look up a typeparam on a type"); ret = false; break; case TK_NEW: ret = method_access(opt, ast, find); break; case TK_FVAR: case TK_FLET: case TK_EMBED: case TK_BE: case TK_FUN: { // Make this a lookup on a default constructed object. ast_free_unattached(find); if(!strcmp(ast_name(right), "create")) { ast_error(opt->check.errors, right, "create is not a constructor on this type"); return false; } ast_t* dot = ast_from(ast, TK_DOT); ast_add(dot, ast_from_string(ast, "create")); ast_swap(left, dot); ast_add(dot, left); ast_t* call = ast_from(ast, TK_CALL); ast_swap(dot, call); ast_add(call, dot); // the LHS goes at the end, not the beginning ast_add(call, ast_from(ast, TK_NONE)); // named ast_add(call, ast_from(ast, TK_NONE)); // positional if(!expr_dot(opt, &dot)) return false; if(!expr_call(opt, &call)) return false; return expr_dot(opt, astp); } default: assert(0); ret = false; break; } ast_free_unattached(find); return ret; }
bool expr_typeref(pass_opt_t* opt, ast_t** astp) { ast_t* ast = *astp; assert(ast_id(ast) == TK_TYPEREF); ast_t* type = ast_type(ast); if(is_typecheck_error(type)) return false; switch(ast_id(ast_parent(ast))) { case TK_QUALIFY: // Doesn't have to be valid yet. break; case TK_DOT: // Has to be valid. if(!expr_nominal(opt, &type)) { ast_settype(ast, ast_from(type, TK_ERRORTYPE)); ast_free_unattached(type); return false; } break; case TK_CALL: { // Has to be valid. if(!expr_nominal(opt, &type)) { ast_settype(ast, ast_from(type, TK_ERRORTYPE)); ast_free_unattached(type); return false; } // Transform to a default constructor. ast_t* dot = ast_from(ast, TK_DOT); ast_add(dot, ast_from_string(ast, "create")); ast_swap(ast, dot); *astp = dot; ast_add(dot, ast); if(!expr_dot(opt, astp)) { ast_settype(ast, ast_from(type, TK_ERRORTYPE)); ast_free_unattached(type); return false; } ast_t* ast = *astp; // If the default constructor has no parameters, transform to an apply // call. if((ast_id(ast) == TK_NEWREF) || (ast_id(ast) == TK_NEWBEREF)) { type = ast_type(ast); if(is_typecheck_error(type)) return false; assert(ast_id(type) == TK_FUNTYPE); AST_GET_CHILDREN(type, cap, typeparams, params, result); if(ast_id(params) == TK_NONE) { // Add a call node. ast_t* call = ast_from(ast, TK_CALL); ast_add(call, ast_from(call, TK_NONE)); // Named ast_add(call, ast_from(call, TK_NONE)); // Positional ast_swap(ast, call); ast_append(call, ast); if(!expr_call(opt, &call)) { ast_settype(ast, ast_from(type, TK_ERRORTYPE)); ast_free_unattached(type); return false; } // Add a dot node. ast_t* apply = ast_from(call, TK_DOT); ast_add(apply, ast_from_string(call, "apply")); ast_swap(call, apply); ast_add(apply, call); if(!expr_dot(opt, &apply)) { ast_settype(ast, ast_from(type, TK_ERRORTYPE)); ast_free_unattached(type); return false; } } } return true; } default: { // Has to be valid. if(!expr_nominal(opt, &type)) { ast_settype(ast, ast_from(type, TK_ERRORTYPE)); ast_free_unattached(type); return false; } // Transform to a default constructor. ast_t* dot = ast_from(ast, TK_DOT); ast_add(dot, ast_from_string(ast, "create")); ast_swap(ast, dot); ast_add(dot, ast); // Call the default constructor with no arguments. ast_t* call = ast_from(ast, TK_CALL); ast_swap(dot, call); ast_add(call, dot); // Receiver comes last. ast_add(call, ast_from(ast, TK_NONE)); // Named args. ast_add(call, ast_from(ast, TK_NONE)); // Positional args. *astp = call; if(!expr_dot(opt, &dot)) { ast_settype(ast, ast_from(type, TK_ERRORTYPE)); ast_free_unattached(type); return false; } if(!expr_call(opt, astp)) { ast_settype(ast, ast_from(type, TK_ERRORTYPE)); ast_free_unattached(type); return false; } break; } } return true; }
ast_result_t pass_expr(ast_t** astp, pass_opt_t* options) { typecheck_t* t = &options->check; ast_t* ast = *astp; bool r = true; switch(ast_id(ast)) { case TK_NOMINAL: r = expr_nominal(options, astp); break; case TK_FVAR: case TK_FLET: case TK_PARAM: r = expr_field(options, ast); break; case TK_NEW: case TK_BE: case TK_FUN: r = expr_fun(options, ast); break; case TK_SEQ: r = expr_seq(ast); break; case TK_VAR: case TK_LET: r = expr_local(t, ast); break; case TK_BREAK: r = expr_break(t, ast); break; case TK_CONTINUE: r = expr_continue(t, ast); break; case TK_RETURN: r = expr_return(options, ast); break; case TK_IS: case TK_ISNT: r = expr_identity(options, ast); break; case TK_ASSIGN: r = expr_assign(options, ast); break; case TK_CONSUME: r = expr_consume(t, ast); break; case TK_RECOVER: r = expr_recover(ast); break; case TK_DOT: r = expr_dot(options, astp); break; case TK_TILDE: r = expr_tilde(options, astp); break; case TK_QUALIFY: r = expr_qualify(options, astp); break; case TK_CALL: r = expr_call(options, astp); break; case TK_IF: r = expr_if(options, ast); break; case TK_WHILE: r = expr_while(options, ast); break; case TK_REPEAT: r = expr_repeat(options, ast); break; case TK_TRY_NO_CHECK: case TK_TRY: r = expr_try(options, ast); break; case TK_MATCH: r = expr_match(options, ast); break; case TK_CASES: r = expr_cases(ast); break; case TK_CASE: r = expr_case(options, ast); break; case TK_TUPLE: r = expr_tuple(ast); break; case TK_ARRAY: r = expr_array(options, astp); break; case TK_REFERENCE: r = expr_reference(options, astp); break; case TK_THIS: r = expr_this(options, ast); break; case TK_TRUE: case TK_FALSE: r = expr_literal(options, ast, "Bool"); break; case TK_ERROR: r = expr_error(ast); break; case TK_COMPILER_INTRINSIC: r = expr_compiler_intrinsic(t, ast); break; case TK_POSITIONALARGS: case TK_NAMEDARGS: case TK_NAMEDARG: case TK_UPDATEARG: ast_inheritflags(ast); break; case TK_AMP: r = expr_addressof(options, ast); break; case TK_DONTCARE: r = expr_dontcare(ast); break; case TK_INT: // Integer literals can be integers or floats make_literal_type(ast); break; case TK_FLOAT: make_literal_type(ast); break; case TK_STRING: if(ast_id(ast_parent(ast)) == TK_PACKAGE) return AST_OK; r = expr_literal(options, ast, "String"); break; case TK_FFICALL: return expr_ffi(options, ast); default: {} } if(!r) { assert(get_error_count() > 0); return AST_ERROR; } // Can't use ast here, it might have changed symtab_t* symtab = ast_get_symtab(*astp); if(symtab != NULL && !symtab_check_all_defined(symtab)) return AST_ERROR; return AST_OK; }
static bool partial_application(pass_opt_t* opt, ast_t** astp) { ast_t* ast = *astp; typecheck_t* t = &opt->check; if(!method_application(opt, ast, true)) return false; AST_GET_CHILDREN(ast, positional, namedargs, lhs); // LHS must be a TK_TILDE, possibly contained in a TK_QUALIFY. AST_GET_CHILDREN(lhs, receiver, method); switch(ast_id(receiver)) { case TK_NEWAPP: case TK_BEAPP: case TK_FUNAPP: AST_GET_CHILDREN_NO_DECL(receiver, receiver, method); break; default: {} } // The TK_FUNTYPE of the LHS. ast_t* type = ast_type(lhs); if(is_typecheck_error(type)) return false; token_id apply_cap = partial_application_cap(type, receiver, positional); AST_GET_CHILDREN(type, cap, typeparams, params, result); // Create a new anonymous type. ast_t* c_id = ast_from_string(ast, package_hygienic_id(t)); BUILD(def, ast, NODE(TK_CLASS, AST_SCOPE TREE(c_id) NONE NONE NONE NODE(TK_MEMBERS) NONE NONE)); // We will have a create method in the type. BUILD(create, ast, NODE(TK_NEW, AST_SCOPE NONE ID("create") NONE NODE(TK_PARAMS) NONE NONE NODE(TK_SEQ) NONE)); // We will have an apply method in the type. token_id can_error = ast_canerror(lhs) ? TK_QUESTION : TK_NONE; BUILD(apply, ast, NODE(TK_FUN, AST_SCOPE NODE(apply_cap) ID("apply") NONE NODE(TK_PARAMS) TREE(result) NODE(can_error) NODE(TK_SEQ) NONE)); // We will replace partial application with $0.create(...) BUILD(call_receiver, ast, NODE(TK_REFERENCE, TREE(c_id))); BUILD(call_dot, ast, NODE(TK_DOT, TREE(call_receiver) ID("create"))); BUILD(call, ast, NODE(TK_CALL, NONE NODE(TK_NAMEDARGS) TREE(call_dot))); ast_t* class_members = ast_childidx(def, 4); ast_t* create_params = ast_childidx(create, 3); ast_t* create_body = ast_childidx(create, 6); ast_t* apply_params = ast_childidx(apply, 3); ast_t* apply_body = ast_childidx(apply, 6); ast_t* call_namedargs = ast_childidx(call, 1); // Add the receiver to the anonymous type. ast_t* r_id = ast_from_string(receiver, package_hygienic_id(t)); ast_t* r_field_id = ast_from_string(receiver, package_hygienic_id(t)); ast_t* r_type = ast_type(receiver); if(is_typecheck_error(r_type)) return false; // A field in the type. BUILD(r_field, receiver, NODE(TK_FLET, TREE(r_field_id) TREE(r_type) NONE)); // A parameter of the constructor. BUILD(r_ctor_param, receiver, NODE(TK_PARAM, TREE(r_id) TREE(r_type) NONE)); // An assignment in the constructor body. BUILD(r_assign, receiver, NODE(TK_ASSIGN, NODE(TK_CONSUME, NODE(TK_NONE) NODE(TK_REFERENCE, TREE(r_id))) NODE(TK_REFERENCE, TREE(r_field_id)))); // A named argument at the call site. BUILD(r_call_seq, receiver, NODE(TK_SEQ, TREE(receiver))); BUILD(r_call_arg, receiver, NODE(TK_NAMEDARG, TREE(r_id) TREE(r_call_seq))); ast_settype(r_call_seq, r_type); ast_append(class_members, r_field); ast_append(create_params, r_ctor_param); ast_append(create_body, r_assign); ast_append(call_namedargs, r_call_arg); // Add a call to the original method to the apply body. BUILD(apply_call, ast, NODE(TK_CALL, NODE(TK_POSITIONALARGS) NONE NODE(TK_DOT, NODE(TK_REFERENCE, TREE(r_field_id)) TREE(method)))); ast_append(apply_body, apply_call); ast_t* apply_args = ast_child(apply_call); // Add the arguments to the anonymous type. ast_t* arg = ast_child(positional); ast_t* param = ast_child(params); while(arg != NULL) { AST_GET_CHILDREN(param, id, p_type); if(ast_id(arg) == TK_NONE) { // A parameter of the apply method, using the same name, type and default // argument. ast_append(apply_params, param); // An arg in the call to the original method. BUILD(apply_arg, param, NODE(TK_SEQ, NODE(TK_CONSUME, NODE(TK_NONE) NODE(TK_REFERENCE, TREE(id))))); ast_append(apply_args, apply_arg); } else { ast_t* p_id = ast_from_string(id, package_hygienic_id(t)); // A field in the type. BUILD(field, arg, NODE(TK_FLET, TREE(id) TREE(p_type) NONE)); // A parameter of the constructor. BUILD(ctor_param, arg, NODE(TK_PARAM, TREE(p_id) TREE(p_type) NONE)); // An assignment in the constructor body. BUILD(assign, arg, NODE(TK_ASSIGN, NODE(TK_CONSUME, NODE(TK_NONE) NODE(TK_REFERENCE, TREE(p_id))) NODE(TK_REFERENCE, TREE(id)))); // A named argument at the call site. BUILD(call_arg, arg, NODE(TK_NAMEDARG, TREE(p_id) TREE(arg))); // An arg in the call to the original method. BUILD(apply_arg, arg, NODE(TK_SEQ, NODE(TK_REFERENCE, TREE(id)))); ast_append(class_members, field); ast_append(create_params, ctor_param); ast_append(create_body, assign); ast_append(call_namedargs, call_arg); ast_append(apply_args, apply_arg); } arg = ast_sibling(arg); param = ast_sibling(param); } // Add create and apply to the anonymous type. ast_append(class_members, create); ast_append(class_members, apply); // Typecheck the anonymous type. ast_add(t->frame->module, def); if(!type_passes(def, opt)) return false; // Typecheck the create call. if(!expr_reference(opt, &call_receiver)) return false; if(!expr_dot(opt, &call_dot)) return false; if(!expr_call(opt, &call)) return false; // Replace the partial application with the create call. ast_replace(astp, call); return true; }