static ast_result_t sugar_binop(ast_t** astp, const char* fn_name) { AST_GET_CHILDREN(*astp, left, right); ast_t* positional = ast_from(right, TK_POSITIONALARGS); if(ast_id(right) == TK_TUPLE) { ast_t* value = ast_child(right); while(value != NULL) { BUILD(arg, right, NODE(TK_SEQ, TREE(value))); ast_append(positional, arg); value = ast_sibling(value); } } else { BUILD(arg, right, NODE(TK_SEQ, TREE(right))); ast_add(positional, arg); } REPLACE(astp, NODE(TK_CALL, TREE(positional) NONE NODE(TK_DOT, TREE(left) ID(fn_name)) )); return AST_OK; }
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); }
//=============================================================================================== // FUNCTION: ReadFileInfo // PURPOSE: Reads the file info from the data file. // BOOL CABF2ProtocolReader::ReadFileInfo() { MEMBERASSERT(); BOOL bOK = TRUE; short nMajor = MAJOR( m_FileInfo.uFileVersionNumber ); short nMinor = MINOR( m_FileInfo.uFileVersionNumber ); m_pFH->fFileVersionNumber = nMajor + nMinor/100.0F; m_pFH->fHeaderVersionNumber = ABF_CURRENTVERSION; m_pFH->nFileType = m_FileInfo.nFileType; m_pFH->nDataFormat = m_FileInfo.nDataFormat; m_pFH->nSimultaneousScan = m_FileInfo.nSimultaneousScan; m_pFH->FileGUID = m_FileInfo.FileGUID; m_pFH->ulFileCRC = m_FileInfo.uFileCRC; m_pFH->nCRCEnable = m_FileInfo.nCRCEnable; m_pFH->nCreatorMajorVersion = MAJOR ( m_FileInfo.uCreatorVersion ); m_pFH->nCreatorMinorVersion = MINOR ( m_FileInfo.uCreatorVersion ); m_pFH->nCreatorBugfixVersion = BUGFIX( m_FileInfo.uCreatorVersion ); m_pFH->nCreatorBuildVersion = BUILD ( m_FileInfo.uCreatorVersion ); bOK &= GetString( m_FileInfo.uCreatorNameIndex, m_pFH->sCreatorInfo, ELEMENTS_IN( m_pFH->sCreatorInfo ) ); m_pFH->nModifierMajorVersion = MAJOR ( m_FileInfo.uModifierVersion ); m_pFH->nModifierMinorVersion = MINOR ( m_FileInfo.uModifierVersion ); m_pFH->nModifierBugfixVersion = BUGFIX( m_FileInfo.uModifierVersion ); m_pFH->nModifierBuildVersion = BUILD ( m_FileInfo.uModifierVersion ); bOK &= GetString( m_FileInfo.uModifierNameIndex, m_pFH->sModifierInfo, ELEMENTS_IN( m_pFH->sModifierInfo ) ); m_pFH->nNumPointsIgnored = 0; m_pFH->uFileStartDate = m_FileInfo.uFileStartDate; m_pFH->uFileStartTimeMS = m_FileInfo.uFileStartTimeMS; m_pFH->lStopwatchTime = m_FileInfo.uStopwatchTime; m_pFH->lActualEpisodes = m_FileInfo.uActualEpisodes; m_pFH->lActualAcqLength = m_FileInfo.DataSection.GetNumEntries(); m_pFH->lDataSectionPtr = m_FileInfo.DataSection.uBlockIndex; m_pFH->lScopeConfigPtr = m_FileInfo.ScopeSection.uBlockIndex; m_pFH->lNumScopes = m_FileInfo.ScopeSection.GetNumEntries(); m_pFH->lStatisticsConfigPtr = m_FileInfo.StatsSection.uBlockIndex; m_pFH->lTagSectionPtr = m_FileInfo.TagSection.uBlockIndex; m_pFH->lNumTagEntries = m_FileInfo.TagSection.GetNumEntries(); m_pFH->lDeltaArrayPtr = m_FileInfo.DeltaSection.uBlockIndex; m_pFH->lNumDeltas = m_FileInfo.DeltaSection.GetNumEntries(); m_pFH->lVoiceTagPtr = m_FileInfo.VoiceTagSection.uBlockIndex; m_pFH->lVoiceTagEntries = m_FileInfo.VoiceTagSection.GetNumEntries(); m_pFH->lSynchArrayPtr = m_FileInfo.SynchArraySection.uBlockIndex; m_pFH->lSynchArraySize = m_FileInfo.SynchArraySection.GetNumEntries(); m_pFH->lAnnotationSectionPtr = m_FileInfo.AnnotationSection.uBlockIndex; m_pFH->lNumAnnotations = m_FileInfo.AnnotationSection.GetNumEntries(); bOK &= GetString( m_FileInfo.uProtocolPathIndex, m_pFH->sProtocolPath, ELEMENTS_IN( m_pFH->sProtocolPath ) ); return bOK; }
static void add_as_type(typecheck_t* t, ast_t* type, ast_t* pattern, ast_t* body) { assert(type != NULL); switch(ast_id(type)) { case TK_TUPLETYPE: { BUILD(tuple_pattern, pattern, NODE(TK_SEQ, NODE(TK_TUPLE))); ast_append(pattern, tuple_pattern); ast_t* pattern_child = ast_child(tuple_pattern); BUILD(tuple_body, body, NODE(TK_SEQ, NODE(TK_TUPLE))); ast_t* body_child = ast_child(tuple_body); for(ast_t* p = ast_child(type); p != NULL; p = ast_sibling(p)) add_as_type(t, p, pattern_child, body_child); if(ast_childcount(body_child) == 1) { // Only one child, not actually a tuple ast_t* t = ast_pop(body_child); ast_free(tuple_body); tuple_body = t; } ast_append(body, tuple_body); break; } case TK_DONTCARE: ast_append(pattern, type); break; default: { const char* name = package_hygienic_id(t); ast_t* a_type = alias(type); BUILD(pattern_elem, pattern, NODE(TK_SEQ, NODE(TK_LET, ID(name) TREE(a_type)))); BUILD(body_elem, body, NODE(TK_SEQ, NODE(TK_CONSUME, NODE(TK_BORROWED) NODE(TK_REFERENCE, ID(name))))); ast_append(pattern, pattern_elem); ast_append(body, body_elem); break; } } }
// 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; }
static void build_with_dispose(ast_t* dispose_clause, ast_t* idseq) { assert(dispose_clause != NULL); assert(idseq != NULL); if(ast_id(idseq) == TK_LET) { // Just a single variable ast_t* id = ast_child(idseq); assert(id != NULL); // Don't call dispose() on don't cares if(ast_id(id) == TK_DONTCARE) return; assert(ast_id(id) == TK_ID); BUILD(dispose, idseq, NODE(TK_CALL, NONE NONE NODE(TK_DOT, NODE(TK_REFERENCE, TREE(id)) ID("dispose")))); ast_add(dispose_clause, dispose); return; } // We have a list of variables assert(ast_id(idseq) == TK_TUPLE); for(ast_t* p = ast_child(idseq); p != NULL; p = ast_sibling(p)) build_with_dispose(dispose_clause, p); }
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); }
static bool push_assume(ast_t* sub, ast_t* super) { // Returns true if we have already assumed sub is a subtype of super. if(subtype_assume != NULL) { ast_t* assumption = ast_child(subtype_assume); while(assumption != NULL) { AST_GET_CHILDREN(assumption, assume_sub, assume_super); if(exact_nominal(sub, assume_sub) && exact_nominal(super, assume_super)) return true; assumption = ast_sibling(assumption); } } else { subtype_assume = ast_from(sub, TK_NONE); } BUILD(assume, sub, NODE(TK_NONE, TREE(ast_dup(sub)) TREE(ast_dup(super)))); ast_add(subtype_assume, assume); return false; }
// Handle the given case method parameter, which does not have a type // specified. // Check the case parameter and generate the pattern element. // Returns: true on success, false on error. static bool param_without_type(ast_t* case_param, ast_t* pattern) { assert(case_param != NULL); assert(pattern != NULL); AST_GET_CHILDREN(case_param, value, type, def_arg); assert(ast_id(type) == TK_NONE); if(ast_id(def_arg) != TK_NONE) { ast_error(type, "cannot specify default argument for match value parameter"); return false; } // Add value to match pattern. Pop it first to avoid pointless copy. ast_t* popped_value = ast_pop(case_param); if(ast_id(popped_value) == TK_DONTCARE) { // Value is just `don't care`. ast_append(pattern, popped_value); } else { // Value in an expression, need a containing sequence. BUILD(value_ast, value, NODE(TK_SEQ, TREE(popped_value))); ast_append(pattern, value_ast); } return true; }
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; }
ast_t* type_for_this(typecheck_t* t, ast_t* ast, token_id cap, token_id ephemeral) { bool make_arrow = false; if(cap == TK_BOX) { cap = TK_REF; make_arrow = true; } AST_GET_CHILDREN(t->frame->type, id, typeparams); BUILD(typeargs, ast, NODE(TK_NONE)); BUILD(type, ast, NODE(TK_NOMINAL, NODE(TK_NONE) TREE(id) TREE(typeargs) NODE(cap) NODE(ephemeral))); if(ast_id(typeparams) == TK_TYPEPARAMS) { ast_setid(typeargs, TK_TYPEARGS); ast_t* typeparam = ast_child(typeparams); while(typeparam != NULL) { ast_t* typeparam_id = ast_child(typeparam); ast_t* typearg = type_sugar(ast, NULL, ast_name(typeparam_id)); ast_append(typeargs, typearg); typeparam = ast_sibling(typeparam); } } if(make_arrow) { BUILD(arrow, ast, NODE(TK_ARROW, NODE(TK_THISTYPE) TREE(type))); return arrow; } return type; }
void main() { BUILD(); SORT(); OUT(); system("pause"); return 0; }
// Fill the given UIF type cache static bool uif_type(pass_opt_t* opt, ast_t* literal, ast_t* type, lit_chain_t* chain_head, bool report_errors) { pony_assert(chain_head != NULL); pony_assert(chain_head->cardinality == CHAIN_CARD_BASE); chain_head->formal = NULL; int r = uifset(opt, type, chain_head->next); if(r == UIF_ERROR) return false; if(r == UIF_NO_TYPES) { if(report_errors) ast_error(opt->check.errors, literal, "could not infer literal type, no valid types found"); return false; } pony_assert(type != NULL); if((r & UIF_CONSTRAINED) != 0) { // Type is a formal parameter pony_assert(chain_head->formal != NULL); pony_assert(chain_head->name != NULL); pony_assert(chain_head->cached_uif_index < 0); BUILD(uif_type, type, NODE(TK_TYPEPARAMREF, DATA(chain_head->formal) ID(chain_head->name) NODE(TK_VAL) NONE)); chain_head->cached_type = uif_type; chain_head->valid_for_float = ((r & UIF_INT_MASK) == 0); return true; } // Type is one or more UIFs for(int i = 0; i < UIF_COUNT; i++) { if(r == (1 << i)) { chain_head->valid_for_float = (((1 << i) & UIF_INT_MASK) == 0); chain_head->cached_type = type_builtin(opt, type, _str_uif_types[i].name); //ast_setid(ast_childidx(chain_head->cached_type, 4), TK_EPHEMERAL); chain_head->name = _str_uif_types[i].name; chain_head->cached_uif_index = i; return true; } } ast_error(opt->check.errors, literal, "Multiple possible types for literal"); return false; }
ast_t* type_isect_fun(ast_t* a, ast_t* b) { token_id ta = ast_id(a); token_id tb = ast_id(b); if(((ta == TK_NEW) || (tb == TK_NEW)) && (ta != tb)) return NULL; AST_GET_CHILDREN(a, a_cap, a_id, a_typeparams, a_params, a_result, a_throw); AST_GET_CHILDREN(b, b_cap, b_id, b_typeparams, b_params, b_result, b_throw); // Must have the same name. if(ast_name(a_id) != ast_name(b_id)) return NULL; // Must have the same number of type parameters and parameters. if((ast_childcount(a_typeparams) != ast_childcount(b_typeparams)) || (ast_childcount(a_params) != ast_childcount(b_params))) return NULL; // Contravariant receiver cap. token_id tcap; token_id a_tcap = ast_id(a_cap); token_id b_tcap = ast_id(b_cap); if(is_cap_sub_cap(b_tcap, TK_NONE, a_tcap, TK_NONE)) tcap = a_tcap; else if(is_cap_sub_cap(a_tcap, TK_NONE, b_tcap, TK_NONE)) tcap = b_tcap; else tcap = TK_BOX; // Result is the intersection of the results. ast_t* result = type_isect(a_result, b_result); // Covariant throws. token_id throws; if((ast_id(a_throw) == TK_NONE) || (ast_id(b_throw) == TK_NONE)) throws = TK_NONE; else throws = TK_QUESTION; BUILD(fun, a, NODE(tcap) TREE(a_id) NODE(TK_TYPEPARAMS) NODE(TK_PARAMS) TREE(result) NODE(throws) ); // TODO: union typeparams and params // handling typeparam names is tricky return fun; }
static ast_t* type_base(ast_t* from, const char* package, const char* name) { BUILD(ast, from, NODE(TK_NOMINAL, ID(package) ID(name) NONE NONE NONE)); return ast; }
// Collect the given type parameter static void collect_type_param(ast_t* orig_param, ast_t* params, ast_t* args) { assert(orig_param != NULL); // Get original type parameter info AST_GET_CHILDREN(orig_param, id, constraint, deflt); const char* name = ast_name(id); constraint = sanitise_type(constraint); assert(constraint != NULL); // New type parameter has the same constraint as the old one (sanitised) if(params != NULL) { BUILD(new_param, orig_param, NODE(TK_TYPEPARAM, ID(name) TREE(constraint) NONE)); ast_append(params, new_param); ast_setid(params, TK_TYPEPARAMS); } // New type arguments binds to old type parameter if(args != NULL) { BUILD(new_arg, orig_param, NODE(TK_NOMINAL, NONE // Package ID(name) NONE // Type args NONE // cap NONE)); // ephemeral ast_append(args, new_arg); ast_setid(args, TK_TYPEARGS); } }
// Collect the given type parameter static void collect_type_param(ast_t* orig_param, ast_t* params, ast_t* args) { assert(orig_param != NULL); assert(params != NULL); assert(args != NULL); // Get original type parameter info AST_GET_CHILDREN(orig_param, id, constraint, deflt); const char* name = ast_name(id); constraint = sanitise_type(constraint); assert(constraint != NULL); // New type parameter has the same constraint as the old one (sanitised) BUILD(new_param, orig_param, NODE(TK_TYPEPARAM, ID(name) TREE(constraint) NONE)); ast_append(params, new_param); // New type arguments binds to old type parameter BUILD(new_arg, orig_param, NODE(TK_TYPEPARAMREF, DATA(orig_param) ID(name) NONE // cap NONE)); // ephemeral ast_append(args, new_arg); // Since we have a type parameter the params and args node should not be // TK_NONE ast_setid(params, TK_TYPEPARAMS); ast_setid(args, TK_TYPEARGS); }
void build_scale_mode (UBYTE * scale, UBYTE tonic, UBYTE mode) { switch (mode) { case 0: BUILD (ionian); case 1: BUILD (aeolian); case 2: BUILD (harmonic); case 3: BUILD (dorian); case 4: BUILD (lydian); case 5: BUILD (wholetone); case 6: BUILD (blues); } }
// Check type parameters and build the all type parameter structures for a // complete set of case methods with the given name. // Generate type parameter list for worker call. static void build_t_params(ast_t* match_t_params, ast_t* worker_t_params) { assert(match_t_params != NULL); assert(worker_t_params != NULL); for(ast_t* p = ast_child(match_t_params); p != NULL; p = ast_sibling(p)) { AST_GET_CHILDREN(p, id, constraint, def_type); assert(ast_id(id) == TK_ID); // Add type parameter name to worker call list. BUILD(type, p, NODE(TK_NOMINAL, NONE TREE(id) NONE NONE NONE)); ast_append(worker_t_params, type); ast_setid(worker_t_params, TK_TYPEARGS); } }
static ast_result_t sugar_for(typecheck_t* t, ast_t** astp) { AST_EXTRACT_CHILDREN(*astp, for_idseq, for_iter, for_body, for_else); expand_none(for_else, true); const char* iter_name = package_hygienic_id(t); BUILD(try_next, for_iter, NODE(TK_TRY_NO_CHECK, NODE(TK_SEQ, AST_SCOPE NODE(TK_CALL, NONE NONE NODE(TK_DOT, NODE(TK_REFERENCE, ID(iter_name)) ID("next")))) NODE(TK_SEQ, AST_SCOPE NODE(TK_CONTINUE, NONE)) NONE)); sugar_try(try_next); REPLACE(astp, NODE(TK_SEQ, NODE(TK_ASSIGN, AST_NODEBUG TREE(for_iter) NODE(TK_LET, NICE_ID(iter_name, "for loop iterator") NONE)) NODE(TK_WHILE, AST_SCOPE NODE(TK_SEQ, NODE_ERROR_AT(TK_CALL, for_iter, NONE NONE NODE(TK_DOT, NODE(TK_REFERENCE, ID(iter_name)) ID("has_next")))) NODE(TK_SEQ, AST_SCOPE NODE_ERROR_AT(TK_ASSIGN, for_idseq, AST_NODEBUG TREE(try_next) TREE(for_idseq)) TREE(for_body)) TREE(for_else)))); return AST_OK; }
static ast_t* make_create(ast_t* ast) { assert(ast != NULL); // Default constructors on classes can be iso, on actors they must be tag token_id cap = (ast_id(ast) == TK_CLASS) ? TK_ISO : TK_NONE; BUILD(create, ast, NODE(TK_NEW, AST_SCOPE NODE(cap) ID("create") // name NONE // typeparams NONE // params NONE // return type NONE // error NODE(TK_SEQ, NODE(TK_TRUE)) NONE NONE )); return create; }
ast_t* type_pointer_to(pass_opt_t* opt, ast_t* to) { BUILD(pointer, to, NODE(TK_NOMINAL, NONE // Package ID("Pointer") NODE(TK_TYPEARGS, TREE(to) ) NONE // Capability NONE // Ephemeral )); if(!names_nominal(opt, to, &pointer, false)) { ast_error(to, "unable to create Pointer[%s]", ast_print_type(to)); ast_free(pointer); return NULL; } return pointer; }
ast_t* type_for_fun(ast_t* ast) { AST_GET_CHILDREN(ast, cap, name, typeparams, params, result); token_id fcap = ast_id(cap); if(fcap == TK_NONE) fcap = TK_TAG; // The params may already have types attached. If we build the function type // directly from those we'll get nested types which can mess things up. To // avoid this make a clean version of the params without types. ast_t* clean_params = ast_dup(params); for(ast_t* p = ast_child(clean_params); p != NULL; p = ast_sibling(p)) ast_settype(p, NULL); BUILD(fun, ast, NODE(TK_FUNTYPE, NODE(fcap) TREE(typeparams) TREE(clean_params) TREE(result))); return fun; }
static ast_result_t sugar_module(ast_t* ast) { ast_t* docstring = ast_child(ast); ast_t* package = ast_parent(ast); assert(ast_id(package) == TK_PACKAGE); if(strcmp(package_name(package), "$0") != 0) { // Every module not in builtin has an implicit use builtin command. // Since builtin is always the first package processed it is $0. BUILD(builtin, ast, NODE(TK_USE, NONE STRING(stringtab("builtin")) NONE)); ast_add(ast, builtin); } if((docstring == NULL) || (ast_id(docstring) != TK_STRING)) return AST_OK; ast_t* package_docstring = ast_childlast(package); if(ast_id(package_docstring) == TK_STRING) { ast_error(docstring, "the package already has a docstring"); ast_error(package_docstring, "the existing docstring is here"); return AST_ERROR; } ast_append(package, docstring); ast_remove(docstring); return AST_OK; }
// Determine the UIF types that the given formal parameter may be static int uifset_formal_param(pass_opt_t* opt, ast_t* type_param_ref, lit_chain_t* chain) { assert(type_param_ref != NULL); assert(ast_id(type_param_ref) == TK_TYPEPARAMREF); ast_t* type_param = (ast_t*)ast_data(type_param_ref); assert(type_param != NULL); assert(ast_id(type_param) == TK_TYPEPARAM); assert(chain != NULL); ast_t* constraint = ast_childidx(type_param, 1); assert(constraint != NULL); // If the constraint is not a subtype of (Real[A] & Number) then there are no // legal types in the set ast_t* number = type_builtin(opt, type_param, "Number"); ast_t* real = type_builtin(opt, type_param, "Real"); ast_setid(ast_childidx(real, 3), TK_BOX); ast_t* p_ref = ast_childidx(real, 2); REPLACE(&p_ref, NODE(TK_TYPEARGS, NODE(TK_TYPEPARAMREF, DATA(type_param) ID(ast_name(ast_child(type_param))) NODE(TK_VAL) NONE))); bool is_real = is_subtype(constraint, real); bool is_number = is_subtype(constraint, number); ast_free(number); ast_free(real); if(!is_real || !is_number) // The formal param is not a subset of (Real[A] & Number) return UIF_NO_TYPES; int uif_set = 0; for(int i = 0; i < UIF_COUNT; i++) { ast_t* uif = type_builtin(opt, type_param, _str_uif_types[i].name); BUILD(params, type_param, NODE(TK_TYPEPARAMS, TREE(ast_dup(type_param)))); BUILD(args, type_param, NODE(TK_TYPEARGS, TREE(uif))); if(check_constraints(params, args, false)) uif_set |= (1 << i); ast_free(args); ast_free(params); } if(uif_set == 0) // No legal types return UIF_NO_TYPES; // Given formal parameter is legal to coerce to if(chain->formal != NULL && chain->formal != type_param) { ast_error(type_param_ref, "Cannot infer a literal type with multiple formal parameters"); return UIF_ERROR; } chain->formal = type_param; chain->name = ast_name(ast_child(type_param)); return uif_set | UIF_CONSTRAINED; }
bool expr_this(pass_opt_t* opt, ast_t* ast) { typecheck_t* t = &opt->check; if(t->frame->def_arg != NULL) { ast_error(ast, "can't reference 'this' in a default argument"); return false; } sym_status_t status; ast_get(ast, stringtab("this"), &status); if(status == SYM_CONSUMED) { ast_error(ast, "can't use a consumed 'this' in an expression"); return false; } assert(status == SYM_NONE); token_id cap = cap_for_this(t); if(!cap_sendable(cap) && (t->frame->recover != NULL)) cap = TK_TAG; bool make_arrow = false; if(cap == TK_BOX) { cap = TK_REF; make_arrow = true; } ast_t* type = type_for_this(opt, ast, cap, TK_NONE, false); if(make_arrow) { BUILD(arrow, ast, NODE(TK_ARROW, NODE(TK_THISTYPE) TREE(type))); type = arrow; } // Get the nominal type, which may be the right side of an arrow type. ast_t* nominal; bool arrow; if(ast_id(type) == TK_NOMINAL) { nominal = type; arrow = false; } else { nominal = ast_childidx(type, 1); arrow = true; } ast_t* typeargs = ast_childidx(nominal, 2); ast_t* typearg = ast_child(typeargs); while(typearg != NULL) { if(!expr_nominal(opt, &typearg)) { ast_error(ast, "couldn't create a type for 'this'"); ast_free(type); return false; } typearg = ast_sibling(typearg); } if(!expr_nominal(opt, &nominal)) { ast_error(ast, "couldn't create a type for 'this'"); ast_free(type); return false; } if(arrow) type = ast_parent(nominal); else type = nominal; ast_settype(ast, type); return true; }
static bool is_fun_sub_fun(ast_t* sub, ast_t* super, ast_t* isub, ast_t* isuper) { token_id tsub = ast_id(sub); token_id tsuper = ast_id(super); switch(tsub) { case TK_NEW: case TK_BE: case TK_FUN: break; default: return false; } switch(tsuper) { case TK_NEW: case TK_BE: case TK_FUN: break; default: return false; } // A constructor can only be a subtype of a constructor. if(((tsub == TK_NEW) || (tsuper == TK_NEW)) && (tsub != tsuper)) return false; AST_GET_CHILDREN(sub, sub_cap, sub_id, sub_typeparams, sub_params); AST_GET_CHILDREN(super, super_cap, super_id, super_typeparams, super_params); // Must have the same name. if(ast_name(sub_id) != ast_name(super_id)) return false; // Must have the same number of type parameters and parameters. if((ast_childcount(sub_typeparams) != ast_childcount(super_typeparams)) || (ast_childcount(sub_params) != ast_childcount(super_params))) return false; ast_t* r_sub = sub; if(ast_id(super_typeparams) != TK_NONE) { // Reify sub with the type parameters of super. BUILD(typeargs, super_typeparams, NODE(TK_TYPEARGS)); ast_t* super_typeparam = ast_child(super_typeparams); while(super_typeparam != NULL) { AST_GET_CHILDREN(super_typeparam, super_id, super_constraint); token_id cap = cap_from_constraint(super_constraint); BUILD(typearg, super_typeparam, NODE(TK_TYPEPARAMREF, TREE(super_id) NODE(cap) NONE)); ast_t* def = ast_get(super_typeparam, ast_name(super_id), NULL); ast_setdata(typearg, def); ast_append(typeargs, typearg); super_typeparam = ast_sibling(super_typeparam); } r_sub = reify(sub, sub, sub_typeparams, typeargs); ast_free_unattached(typeargs); } bool ok = is_reified_fun_sub_fun(r_sub, super, isub, isuper); if(r_sub != sub) ast_free_unattached(r_sub); return ok; }
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); }
// Process the given capture and create the AST for the corresponding field. // Returns the create field AST, which must be freed by the caller. // Returns NULL on error. static ast_t* make_capture_field(pass_opt_t* opt, ast_t* capture) { assert(capture != NULL); AST_GET_CHILDREN(capture, id_node, type, value); const char* name = ast_name(id_node); // There are 3 varieties of capture: // x -> capture variable x, type from defn of x // x = y -> capture expression y, type inferred from expression type // x: T = y -> capture expression y, type T if(ast_id(value) == TK_NONE) { // Variable capture assert(ast_id(type) == TK_NONE); ast_t* def = ast_get(capture, name, NULL); if(def == NULL) { ast_error(opt->check.errors, id_node, "cannot capture \"%s\", variable not defined", name); return NULL; } token_id def_id = ast_id(def); if(def_id != TK_ID && def_id != TK_FVAR && def_id != TK_FLET && def_id != TK_PARAM) { ast_error(opt->check.errors, id_node, "cannot capture \"%s\", can only " "capture fields, parameters and local variables", name); return NULL; } BUILD(capture_rhs, id_node, NODE(TK_REFERENCE, ID(name))); type = alias(ast_type(def)); value = capture_rhs; } else if(ast_id(type) == TK_NONE) { // No type specified, use type of the captured expression type = alias(ast_type(value)); } else { // Type given, infer literals if(!coerce_literals(&value, type, opt)) return NULL; } if(is_typecheck_error(type)) return NULL; type = sanitise_type(type); BUILD(field, id_node, NODE(TK_FVAR, TREE(id_node) TREE(type) TREE(value) NONE)); // Delegate type return field; }