// Check whether the given entity has illegal parts static ast_result_t syntax_entity(ast_t* ast, int entity_def_index) { assert(ast != NULL); assert(entity_def_index >= 0 && entity_def_index < DEF_ENTITY_COUNT); ast_result_t r = AST_OK; const permission_def_t* def = &_entity_def[entity_def_index]; AST_GET_CHILDREN(ast, id, typeparams, defcap, provides, members, c_api); // Check if we're called Main if(def->permissions[ENTITY_MAIN] == 'N' && ast_name(id) == stringtab("Main")) { ast_error(ast, "Main must be an actor"); r = AST_ERROR; } if(!check_id_type(id, def->desc)) r = AST_ERROR; if(!check_permission(def, ENTITY_CAP, defcap, "default capability", defcap)) r = AST_ERROR; if(!check_permission(def, ENTITY_C_API, c_api, "C api", c_api)) r = AST_ERROR; if(ast_id(c_api) == TK_AT) { if(ast_id(typeparams) != TK_NONE) { ast_error(typeparams, "generic actor cannot specify C api"); r = AST_ERROR; } } if(entity_def_index != DEF_TYPEALIAS) { // Check referenced traits if(ast_id(provides) != TK_NONE && !check_provides_type(provides, "provides")) r = AST_ERROR; } else { // Check for a type alias if(ast_id(provides) == TK_NONE) { ast_error(provides, "a type alias must specify a type"); r = AST_ERROR; } } // Check for illegal members if(!check_members(members, entity_def_index)) r = AST_ERROR; return r; }
// Check whether the given node is a valid provides type static bool check_provides_type(pass_opt_t* opt, ast_t* type, const char* description) { assert(type != NULL); assert(description != NULL); switch(ast_id(type)) { case TK_NOMINAL: { AST_GET_CHILDREN(type, ignore0, ignore1, ignore2, cap, ephemeral); if(ast_id(cap) != TK_NONE) { ast_error(opt->check.errors, cap, "can't specify a capability in a provides type"); return false; } if(ast_id(ephemeral) != TK_NONE) { ast_error(opt->check.errors, ephemeral, "can't specify ephemeral in a provides type"); return false; } return true; } case TK_PROVIDES: case TK_ISECTTYPE: // Check all our children are also legal for(ast_t* p = ast_child(type); p != NULL; p = ast_sibling(p)) { if(!check_provides_type(opt, p, description)) return false; } return true; default: ast_error(opt->check.errors, type, "invalid %s type. Can only be " "interfaces, traits and intersects of those.", description); return false; } }
// Check whether the given entity members are legal in their entity static bool check_members(pass_opt_t* opt, ast_t* members, int entity_def_index) { assert(members != NULL); assert(entity_def_index >= 0 && entity_def_index < DEF_ENTITY_COUNT); bool r = true; const permission_def_t* def = &_entity_def[entity_def_index]; ast_t* member = ast_child(members); while(member != NULL) { switch(ast_id(member)) { case TK_FLET: case TK_FVAR: case TK_EMBED: { if(def->permissions[ENTITY_FIELD] == 'N') { ast_error(opt->check.errors, member, "Can't have fields in %s", def->desc); r = false; } if((ast_id(ast_parent(members)) == TK_OBJECT) && \ (ast_id(ast_childidx(member, 2)) == TK_NONE)) { ast_error(opt->check.errors, member, "object literal fields must be initialized"); r = false; } if(!check_id_field(opt, ast_child(member))) r = false; ast_t* delegate_type = ast_childidx(member, 3); if(ast_id(delegate_type) != TK_NONE && !check_provides_type(opt, delegate_type, "delegate")) r = false; break; } case TK_NEW: if(!check_method(opt, member, entity_def_index + DEF_NEW)) r = false; break; case TK_BE: { if(!check_method(opt, member, entity_def_index + DEF_BE)) r = false; break; } case TK_FUN: { if(!check_method(opt, member, entity_def_index + DEF_FUN)) r = false; break; } default: ast_print(members); assert(0); return false; } member = ast_sibling(member); } return r; }