static ast_result_t sugar_update(ast_t** astp) { ast_t* ast = *astp; assert(ast_id(ast) == TK_ASSIGN); AST_GET_CHILDREN(ast, value, call); if(ast_id(call) != TK_CALL) return AST_OK; // We are of the form: x(y) = z // Replace us with: x.update(y where value = z) AST_EXTRACT_CHILDREN(call, positional, named, expr); // If there are no named arguments yet, named will be a TK_NONE. ast_setid(named, TK_NAMEDARGS); // Build a new namedarg. BUILD_NO_DEBUG(namedarg, ast, NODE(TK_UPDATEARG, ID("value") NODE(TK_SEQ, TREE(value)))); // Append the named arg to our existing list. ast_append(named, namedarg); // Replace with the update call. REPLACE(astp, NODE(TK_CALL, TREE(positional) TREE(named) NODE(TK_DOT, TREE(expr) ID("update")))); return AST_OK; }
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); } } }
// If the given tree is a TK_NONE expand it to a source None static void expand_none(ast_t* ast, bool is_scope) { if(ast_id(ast) != TK_NONE) return; if(is_scope) ast_scope(ast); ast_setid(ast, TK_SEQ); BUILD_NO_DEBUG(ref, ast, NODE(TK_REFERENCE, ID("None"))); ast_add(ast, ref); }
// Add eq() and ne() functions to the given entity. static bool add_comparable(ast_t* ast, pass_opt_t* options) { assert(ast != NULL); AST_GET_CHILDREN(ast, id, typeparams, defcap, traits, members); ast_t* typeargs = ast_from(typeparams, TK_NONE); bool r = true; for(ast_t* p = ast_child(typeparams); p != NULL; p = ast_sibling(p)) { ast_t* p_id = ast_child(p); BUILD_NO_DEBUG(type, p_id, NODE(TK_NOMINAL, NONE TREE(p_id) NONE NONE NONE)); ast_append(typeargs, type); ast_setid(typeargs, TK_TYPEARGS); } if(!has_member(members, "eq")) { BUILD_NO_DEBUG(eq, members, NODE(TK_FUN, AST_SCOPE NODE(TK_BOX) ID("eq") NONE NODE(TK_PARAMS, NODE(TK_PARAM, ID("that") NODE(TK_NOMINAL, NONE TREE(id) TREE(typeargs) NONE NONE) NONE)) NODE(TK_NOMINAL, NONE ID("Bool") NONE NONE NONE) NONE NODE(TK_SEQ, NODE(TK_IS, NODE(TK_THIS) NODE(TK_REFERENCE, ID("that")))) NONE NONE)); // Need to set function data field to point to originating type, ie ast. // This won't be done when we catch up the passes since we've already // processed that type. ast_setdata(eq, ast); ast_append(members, eq); ast_set(ast, stringtab("eq"), eq, SYM_DEFINED); if(!ast_passes_subtree(&eq, options, PASS_TRAITS)) r = false; } if(!has_member(members, "ne")) { BUILD_NO_DEBUG(ne, members, NODE(TK_FUN, AST_SCOPE NODE(TK_BOX) ID("ne") NONE NODE(TK_PARAMS, NODE(TK_PARAM, ID("that") NODE(TK_NOMINAL, NONE TREE(id) TREE(typeargs) NONE NONE) NONE)) NODE(TK_NOMINAL, NONE ID("Bool") NONE NONE NONE) NONE NODE(TK_SEQ, NODE(TK_ISNT, NODE(TK_THIS) NODE(TK_REFERENCE, ID("that")))) NONE NONE)); // Need to set function data field to point to originating type, ie ast. // This won't be done when we catch up the passes since we've already // processed that type. ast_setdata(ne, ast); ast_append(members, ne); ast_set(ast, stringtab("ne"), ne, SYM_DEFINED); if(!ast_passes_subtree(&ne, options, PASS_TRAITS)) r = false; } ast_free_unattached(typeargs); return r; }
static ast_result_t sugar_object(pass_opt_t* opt, ast_t** astp) { typecheck_t* t = &opt->check; ast_t* ast = *astp; ast_result_t r = AST_OK; AST_GET_CHILDREN(ast, cap, provides, members); ast_t* c_id = ast_from_string(ast, package_hygienic_id(t)); ast_t* t_params; ast_t* t_args; collect_type_params(ast, &t_params, &t_args); // Create a new anonymous type. BUILD(def, ast, NODE(TK_CLASS, AST_SCOPE TREE(c_id) TREE(t_params) NONE TREE(provides) NODE(TK_MEMBERS) NONE NONE)); // We will have a create method in the type. BUILD_NO_DEBUG(create, members, NODE(TK_NEW, AST_SCOPE NONE ID("create") NONE NONE NONE NONE NODE(TK_SEQ) NONE NONE)); BUILD(type_ref, ast, NODE(TK_REFERENCE, TREE(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, 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: { AST_GET_CHILDREN(member, id, type, init); ast_t* p_id = ast_from_string(id, package_hygienic_id(t)); // The field is: var/let/embed id: type BUILD(field, member, NODE(ast_id(member), TREE(id) TREE(type) NONE NONE)); // The param is: $0: type BUILD(param, member, 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_NO_DEBUG(assign, init, NODE(TK_ASSIGN, NODE(TK_CONSUME, NODE(TK_NONE) NODE(TK_REFERENCE, TREE(p_id))) NODE(TK_REFERENCE, TREE(id)))); ast_setid(create_params, TK_PARAMS); ast_setid(call_args, TK_POSITIONALARGS); ast_append(class_members, field); ast_append(create_params, param); ast_append(create_body, assign); ast_append(call_args, arg); 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); } if(!has_fields) { // End the constructor with 'true', since it has no parameters. BUILD_NO_DEBUG(true_node, ast, NODE(TK_TRUE)); ast_append(create_body, true_node); } // 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(cap, "object literals with behaviours are actors and so must " "have tag capability"); r = AST_ERROR; } } 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); } else { // Type is a class, set the create capability as specified ast_setid(ast_child(create), cap_id); } // Add the create function at the end. ast_append(class_members, create); // Replace object..end with $0.create(...) ast_replace(astp, call); // 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); if(!ast_passes_type(&def, opt)) return AST_FATAL; // Sugar the call. if(!ast_passes_subtree(astp, opt, PASS_SUGAR)) return AST_FATAL; return r; }