static bool special_case_call(compile_t* c, ast_t* ast, LLVMValueRef* value) { AST_GET_CHILDREN(ast, positional, named, postfix); if((ast_id(postfix) != TK_FUNREF) || (ast_id(named) != TK_NONE)) return false; AST_GET_CHILDREN(postfix, receiver, method); ast_t* receiver_type = ast_type(receiver); if(ast_id(receiver_type) != TK_NOMINAL) return false; AST_GET_CHILDREN(receiver_type, package, id); if(ast_name(package) != c->str_builtin) return false; const char* name = ast_name(id); if(name == c->str_Bool) return special_case_operator(c, ast, value, true, true); if((name == c->str_I8) || (name == c->str_I16) || (name == c->str_I32) || (name == c->str_I64) || (name == c->str_ILong) || (name == c->str_ISize) || (name == c->str_U8) || (name == c->str_U16) || (name == c->str_U32) || (name == c->str_U64) || (name == c->str_ULong) || (name == c->str_USize) || (name == c->str_F32) || (name == c->str_F64) ) { return special_case_operator(c, ast, value, false, true); } if((name == c->str_I128) || (name == c->str_U128)) { bool native128; os_is_target(OS_NATIVE128_NAME, c->opt->release, &native128); return special_case_operator(c, ast, value, false, native128); } if(name == c->str_Platform) { *value = special_case_platform(c, ast); return true; } return false; }
// Evaluate the given ifdef condition for the given config, or our target build // if no config is given. static bool cond_eval(ast_t* ast, buildflagset_t* config, bool release, pass_opt_t* opt) { pony_assert(ast != NULL); switch(ast_id(ast)) { case TK_NONE: // No condition to evaluate return true; case TK_IFDEFAND: { AST_GET_CHILDREN(ast, left, right); return cond_eval(left, config, release, opt) && cond_eval(right, config, release, opt); } case TK_IFDEFOR: { AST_GET_CHILDREN(ast, left, right); return cond_eval(left, config, release, opt) || cond_eval(right, config, release, opt); } case TK_IFDEFNOT: return !cond_eval(ast_child(ast), config, release, opt); case TK_IFDEFFLAG: { const char* name = ast_name(ast_child(ast)); if(config != NULL) // Lookup flag in given config. return buildflagset_get(config, name); // No config given, lookup platform flag for current build. bool val; if(os_is_target(name, release, &val, opt)) { return val; } // Not a platform flag, must be a user flag. return is_build_flag_defined(name); } default: pony_assert(0); return false; } }
static LLVMValueRef special_case_platform(compile_t* c, ast_t* ast) { AST_GET_CHILDREN(ast, positional, named, postfix); AST_GET_CHILDREN(postfix, receiver, method); const char* method_name = ast_name(method); bool is_target; if(os_is_target(method_name, c->opt->release, &is_target, c->opt)) return LLVMConstInt(c->ibool, is_target ? 1 : 0, false); ast_error(c->opt->check.errors, ast, "unknown Platform setting"); return NULL; }
// Check the given ast is a valid ifdef condition. // The context parameter is for error messages and should be a literal string // such as "ifdef condition" or "use guard". static bool syntax_ifdef_cond(pass_opt_t* opt, ast_t* ast, const char* context) { assert(ast != NULL); assert(context != NULL); switch(ast_id(ast)) { case TK_AND: case TK_OR: case TK_NOT: // Valid node. break; case TK_STRING: { // Check user flag is not also a platform, or outlawed, flags const char* name = ast_name(ast); // Create an all lower case version of the name for comparisons. size_t len = strlen(name) + 1; char* lower_case = (char*)ponyint_pool_alloc_size(len); for(size_t i = 0; i < len; i++) lower_case[i] = (char)tolower(name[i]); bool r = true; bool result; if(os_is_target(lower_case, true, &result, opt)) r = false; for(int i = 0; _illegal_flags[i] != NULL; i++) if(strcmp(lower_case, _illegal_flags[i]) == 0) r = false; ponyint_pool_free_size(len, lower_case); if(!r) { ast_error(opt->check.errors, ast, "\"%s\" is not a valid user build flag\n", name); return false; } // TODO: restrict case? break; } case TK_REFERENCE: { const char* name = ast_name(ast_child(ast)); bool result; if(!os_is_target(name, true, &result, opt)) { ast_error(opt->check.errors, ast, "\"%s\" is not a valid platform flag\n", name); return false; } // Don't recurse into children, that'll hit the ID node return true; } case TK_SEQ: if(ast_childcount(ast) != 1) { ast_error(opt->check.errors, ast, "Sequence not allowed in %s", context); return false; } break; default: ast_error(opt->check.errors, ast, "Invalid %s", context); return false; } for(ast_t* p = ast_child(ast); p != NULL; p = ast_sibling(p)) { if(!syntax_ifdef_cond(opt, p, context)) return false; } return true; }