// Write a description of the given method to the current type file static void doc_method(docgen_t* docgen, ast_t* method) { assert(docgen != NULL); assert(docgen->type_file != NULL); assert(method != NULL); AST_GET_CHILDREN(method, cap, id, t_params, params, ret, error, body, doc); const char* name = ast_name(id); assert(name != NULL); // Sub heading fprintf(docgen->type_file, "## %s %s()\n", ast_get_print(method), name); // Reconstruct signature fprintf(docgen->type_file, "%s", ast_get_print(method)); if(ast_id(method) == TK_FUN) { const char* cap_text = doc_get_cap(cap); if(cap_text != NULL) fprintf(docgen->type_file, " %s\n", cap_text); } fprintf(docgen->type_file, " %s", name); doc_type_params(docgen, t_params); doc_params(docgen, params); if(ast_id(method) == TK_FUN) { fprintf(docgen->type_file, ": "); doc_type(docgen, ret); } if(ast_id(error) == TK_QUESTION) fprintf(docgen->type_file, " ?"); // Further information fprintf(docgen->type_file, "\n\n"); fprintf(docgen->type_file, "%s", (name[0] == '_') ? "Private" : "Public"); if(ast_id(error) == TK_QUESTION) fprintf(docgen->type_file, ", may raise an error"); fprintf(docgen->type_file, ".\n\n"); // Finally the docstring, if any if(ast_id(doc) != TK_NONE) fprintf(docgen->type_file, "%s\n\n", ast_name(doc)); }
// Print an error preamble for the given node. static void error_preamble(ast_t* ast) { assert(ast != NULL); printf("Internal error: AST node %d (%s), ", ast_id(ast), ast_get_print(ast)); }
// Report the human readable description for the given capability node. // The returned string is valid forever and should not be freed. // NULL is returned for no description. static const char* doc_get_cap(ast_t* cap) { if(cap == NULL) return NULL; switch(ast_id(cap)) { case TK_ISO: case TK_TRN: case TK_REF: case TK_VAL: case TK_BOX: case TK_TAG: return ast_get_print(cap); case TK_BOX_GENERIC: return "box"; case TK_TAG_GENERIC: return "tag"; default: return NULL; } }
static void list_doc_params(docgen_t* docgen, ast_t* params) { assert(docgen != NULL); assert(docgen->type_file != NULL); assert(params != NULL); ast_t* first = ast_child(params); for(ast_t* param = first; param != NULL; param = ast_sibling(param)) { if(param == first) fprintf(docgen->type_file, "#### Parameters\n\n"); fprintf(docgen->type_file, "* "); AST_GET_CHILDREN(param, id, type, def_val); const char* name = ast_name(id); assert(name != NULL); fprintf(docgen->type_file, " %s: ", name); doc_type(docgen, type, true); // if we have a default value, add it to the documentation if(ast_id(def_val) != TK_NONE) { switch(ast_id(def_val)) { case TK_STRING: fprintf(docgen->type_file, "= \"%s\"", ast_get_print(def_val)); break; default: fprintf(docgen->type_file, " = %s", ast_get_print(def_val)); break; } } fprintf(docgen->type_file, "\n"); } fprintf(docgen->type_file, "\n"); }
static void type_append(printbuf_t* buf, ast_t* type, bool first) { switch(ast_id(type)) { case TK_UNIONTYPE: { // u3_Arg1_Arg2_Arg3 printbuf(buf, "u%d", ast_childcount(type)); types_append(buf, type); return; } case TK_ISECTTYPE: { // i3_Arg1_Arg2_Arg3 printbuf(buf, "i%d", ast_childcount(type)); types_append(buf, type); return; } case TK_TUPLETYPE: { // t3_Arg1_Arg2_Arg3 printbuf(buf, "t%d", ast_childcount(type)); types_append(buf, type); return; } case TK_NOMINAL: { // pkg_Type[_Arg1_Arg2]_cap AST_GET_CHILDREN(type, package, name, typeargs, cap, eph); ast_t* def = (ast_t*)ast_data(type); ast_t* pkg = ast_nearest(def, TK_PACKAGE); const char* pkg_name = package_symbol(pkg); if(pkg_name != NULL) printbuf(buf, "%s_", pkg_name); printbuf(buf, "%s", ast_name(name)); types_append(buf, typeargs); if(!first) printbuf(buf, "_%s", ast_get_print(cap)); return; } default: {} } assert(0); }
// Report the human readable description for the given capability node. // The returned string is valid forever and should not be freed. // NULL is returned for no description. static const char* doc_get_cap(ast_t* cap) { if(cap == NULL) return NULL; switch(ast_id(cap)) { case TK_ISO: case TK_TRN: case TK_REF: case TK_VAL: case TK_BOX: case TK_TAG: case TK_CAP_READ: case TK_CAP_SEND: case TK_CAP_SHARE: return ast_get_print(cap); default: return NULL; } }
// Check that all the methods in the given list are compatible with the given // method static bool methods_compatible(ast_t* list, ast_t* method, const char* name, ast_t* entity) { assert(list != NULL); assert(method != NULL); assert(name != NULL); assert(entity != NULL); bool r = true; for(ast_t* p = ast_child(list); p != NULL; p = ast_sibling(p)) { if(!is_subtype(method, p)) { ast_error(method, "Clashing type for method %s provided by trait to %s %s", name, ast_get_print(entity), ast_name(ast_child(entity))); ast_error(p, "clashing method here"); r = false; } } return r; }
// Coerce a literal expression to given tuple or non-tuple types static bool coerce_literal_to_type(ast_t** astp, ast_t* target_type, lit_chain_t* chain, pass_opt_t* options, bool report_errors) { assert(astp != NULL); ast_t* literal_expr = *astp; assert(literal_expr != NULL); ast_t* lit_type = ast_type(literal_expr); if(lit_type == NULL || (ast_id(lit_type) != TK_LITERAL && ast_id(lit_type) != TK_OPERATORLITERAL)) { // Not a literal return true; } if(ast_child(lit_type) != NULL) { // Control block literal return coerce_control_block(astp, target_type, chain, options, report_errors); } switch(ast_id(literal_expr)) { case TK_TUPLE: // Tuple literal { size_t cardinality = ast_childcount(literal_expr); if(!coerce_group(astp, target_type, chain, cardinality, options, report_errors)) return false; break; } case TK_INT: return uif_type_from_chain(options, literal_expr, target_type, chain, false, report_errors); case TK_FLOAT: return uif_type_from_chain(options, literal_expr, target_type, chain, true, report_errors); case TK_ARRAY: if(!coerce_group(astp, target_type, chain, CHAIN_CARD_ARRAY, options, report_errors)) return false; break; case TK_SEQ: { // Only coerce the last expression in the sequence ast_t* last = ast_childlast(literal_expr); if(!coerce_literal_to_type(&last, target_type, chain, options, report_errors)) return false; ast_settype(literal_expr, ast_type(last)); return true; } case TK_CALL: { AST_GET_CHILDREN(literal_expr, positional, named, receiver); ast_t* arg = ast_child(positional); if(!coerce_literal_to_type(&receiver, target_type, chain, options, report_errors)) return false; if(arg != NULL && !coerce_literal_to_type(&arg, target_type, chain, options, report_errors)) return false; ast_settype(literal_expr, ast_type(ast_child(receiver))); return true; } case TK_DOT: { ast_t* receiver = ast_child(literal_expr); if(!coerce_literal_to_type(&receiver, target_type, chain, options, report_errors)) return false; break; } default: ast_error(literal_expr, "Internal error, coerce_literal_to_type node %s", ast_get_print(literal_expr)); assert(0); return false; } // Need to reprocess node now all the literals have types ast_settype(literal_expr, NULL); return (pass_expr(astp, options) == AST_OK); }
// Write a description of the given entity to its own type file. // The containing package is handed in to save looking it up again. static void doc_entity(docgen_t* docgen, ast_t* ast, ast_t* package) { assert(docgen != NULL); assert(docgen->index_file != NULL); assert(docgen->type_file == NULL); assert(ast != NULL); assert(package != NULL); // First open a file size_t tqfn_len; char* tqfn = write_tqfn(ast, NULL, &tqfn_len); docgen->type_file = doc_open_file(docgen, true, tqfn, ".md"); if(docgen->type_file == NULL) return; // Add reference to new file to index file AST_GET_CHILDREN(ast, id, tparams, cap, provides, members, c_api, doc); const char* name = ast_name(id); assert(name != NULL); fprintf(docgen->index_file, " - %s %s: \"%s.md\"\n", ast_get_print(ast), name, tqfn); pool_free_size(tqfn_len, tqfn); // Now we can write the actual documentation for the entity fprintf(docgen->type_file, "%s %s", ast_get_print(ast), name); doc_type_params(docgen, tparams); doc_type_list(docgen, provides, " is ", ", ", ""); fprintf(docgen->type_file, "\n\nIn package \"%s\".\n\n", package_qualified_name(package)); fprintf(docgen->type_file, "%s", (name[0] == '_') ? "Private" : "Public"); const char* cap_text = doc_get_cap(cap); if(cap_text != NULL) fprintf(docgen->type_file, ", default capability %s", cap_text); fprintf(docgen->type_file, ".\n\n"); if(ast_id(c_api) == TK_AT) fprintf(docgen->type_file, "May be called from C.\n"); if(ast_id(doc) != TK_NONE) fprintf(docgen->type_file, "%s\n\n", ast_name(doc)); else fprintf(docgen->type_file, "No doc string provided.\n\n"); // Sort members into varieties ast_list_t pub_fields = { NULL, NULL, NULL }; ast_list_t news = { NULL, NULL, NULL }; ast_list_t bes = { NULL, NULL, NULL }; ast_list_t funs = { NULL, NULL, NULL }; for(ast_t* p = ast_child(members); p != NULL; p = ast_sibling(p)) { switch(ast_id(p)) { case TK_FVAR: case TK_FLET: doc_list_add_named(&pub_fields, p, 0, true, false); break; case TK_NEW: doc_list_add_named(&news, p, 1, true, true); break; case TK_BE: doc_list_add_named(&bes, p, 1, true, true); break; case TK_FUN: doc_list_add_named(&funs, p, 1, true, true); break; default: assert(0); break; } } // Handle member variety lists doc_fields(docgen, &pub_fields, "Public fields"); doc_methods(docgen, &news, "Constructors"); doc_methods(docgen, &bes, "Behaviours"); doc_methods(docgen, &funs, "Functions"); doc_list_free(&pub_fields); doc_list_free(&news); doc_list_free(&bes); doc_list_free(&funs); fclose(docgen->type_file); docgen->type_file = NULL; }
// Write the given type to the current type file static void doc_type(docgen_t* docgen, ast_t* type) { assert(docgen != NULL); assert(docgen->type_file != NULL); assert(type != NULL); switch(ast_id(type)) { case TK_NOMINAL: { AST_GET_CHILDREN(type, package, id, tparams, cap, ephemeral); // Find type we reference so we can link to it ast_t* target = (ast_t*)ast_data(type); assert(target != NULL); size_t link_len; char* tqfn = write_tqfn(target, NULL, &link_len); // Links are of the form: [text](target) fprintf(docgen->type_file, "[%s](%s)", ast_name(id), tqfn); pool_free_size(link_len, tqfn); doc_type_list(docgen, tparams, "\\[", ", ", "\\]"); const char* cap_text = doc_get_cap(cap); if(cap_text != NULL) fprintf(docgen->type_file, " %s", cap_text); if(ast_id(ephemeral) != TK_NONE) fprintf(docgen->type_file, "%s", ast_get_print(ephemeral)); break; } case TK_UNIONTYPE: doc_type_list(docgen, type, "(", " | ", ")"); break; case TK_ISECTTYPE: doc_type_list(docgen, type, "(", " & ", ")"); break; case TK_TUPLETYPE: doc_type_list(docgen, type, "(", " , ", ")"); break; case TK_TYPEPARAMREF: { AST_GET_CHILDREN(type, id, cap, ephemeral); fprintf(docgen->type_file, "%s", ast_name(id)); const char* cap_text = doc_get_cap(cap); if(cap_text != NULL) fprintf(docgen->type_file, " %s", cap_text); if(ast_id(ephemeral) != TK_NONE) fprintf(docgen->type_file, "%s", ast_get_print(ephemeral)); break; } case TK_ARROW: { AST_GET_CHILDREN(type, left, right); doc_type(docgen, left); fprintf(docgen->type_file, "->"); doc_type(docgen, right); break; } case TK_THISTYPE: fprintf(docgen->type_file, "this"); break; case TK_BOXTYPE: fprintf(docgen->type_file, "box"); break; default: assert(0); } }
// Check resulting methods are compatible with the containing entity and patch // up symbol tables static bool post_process_methods(ast_t* entity, pass_opt_t* options, bool is_concrete) { assert(entity != NULL); bool r = true; ast_t* members = ast_childidx(entity, 4); for(ast_t* m = ast_child(members); m != NULL; m = ast_sibling(m)) { token_id variety = ast_id(m); // Check behaviour compatability if(variety == TK_BE) { switch(ast_id(entity)) { case TK_PRIMITIVE: ast_error(entity, "primitives can't provide traits that have behaviours"); r = false; break; case TK_CLASS: ast_error(entity, "classes can't have provide that have behaviours"); r = false; break; default: break; } } if(variety == TK_BE || variety == TK_FUN || variety == TK_NEW) { // Check concrete method bodies if(ast_data(m) == BODY_AMBIGUOUS) { if(is_concrete) { ast_error(m, "multiple possible method bodies from traits"); r = false; } } else if(ast_data(m) == NULL) { if(is_concrete) { assert(ast_id(ast_childidx(m, 6)) == TK_NONE); ast_error(m, "no body found for method %d %s %s", is_concrete, ast_get_print(entity), ast_name(ast_child(entity))); r = false; } } else { assert(ast_id(ast_childidx(m, 6)) != TK_NONE); if(ast_data(m) != entity) { // Sort out copied symbol tables ast_visit(&m, rescope, NULL, options); } } } } return r; }
static bool compare_asts(ast_t* prev, ast_t* expected, ast_t* actual, bool check_siblings) { assert(prev != NULL); if(expected == NULL && actual == NULL) return true; if(actual == NULL) { ast_error(expected, "Expected AST %s not found", ast_get_print(expected)); return false; } if(expected == NULL) { ast_error(prev, "Unexpected AST node found, %s", ast_get_print(actual)); return false; } token_id expected_id = ast_id(expected); token_id actual_id = ast_id(actual); if(expected_id != actual_id) { ast_error(expected, "AST ID mismatch, got %d (%s), expected %d (%s)", ast_id(actual), ast_get_print(actual), ast_id(expected), ast_get_print(expected)); return false; } if(ast_id(expected) == TK_ID && ast_name(actual)[0] == '$' && strcmp(ast_name(expected), "hygid") == 0) { // Allow expected "hygid" to match any hygenic ID } else if(strcmp(ast_get_print(expected), ast_get_print(actual)) != 0) { ast_error(expected, "AST text mismatch, got %s, expected %s", ast_get_print(actual), ast_get_print(expected)); return false; } if(ast_has_scope(expected) && !ast_has_scope(actual)) { ast_error(expected, "AST missing scope"); return false; } if(!ast_has_scope(expected) && ast_has_scope(actual)) { ast_error(actual, "Unexpected AST scope"); return false; } if(!compare_asts(expected, ast_child(expected), ast_child(actual), true) || !compare_asts(expected, ast_type(expected), ast_type(actual), true)) return false; return !check_siblings || compare_asts(expected, ast_sibling(expected), ast_sibling(actual), true); }
// Write a description of the given entity to its own type file. static void doc_entity(docgen_t* docgen, ast_t* ast) { assert(docgen != NULL); assert(docgen->index_file != NULL); assert(docgen->package_file != NULL); assert(docgen->test_types != NULL); assert(docgen->public_types != NULL); assert(docgen->private_types != NULL); assert(docgen->type_file == NULL); assert(ast != NULL); // First open a file size_t tqfn_len; char* tqfn = write_tqfn(ast, NULL, &tqfn_len); docgen->type_file = doc_open_file(docgen, true, tqfn, ".md"); if(docgen->type_file == NULL) return; // Add reference to new file to index file AST_GET_CHILDREN(ast, id, tparams, cap, provides, members, c_api, doc); const char* name = ast_name(id); assert(name != NULL); fprintf(docgen->index_file, " - %s %s: \"%s.md\"\n", ast_get_print(ast), name, tqfn); // Add to appropriate package types buffer printbuf_t* buffer = docgen->public_types; if(is_for_testing(name, provides)) buffer = docgen->test_types; else if(name[0] == '_') buffer = docgen->private_types; printbuf(buffer, "* [%s %s](%s.md)\n", ast_get_print(ast), name, tqfn); ponyint_pool_free_size(tqfn_len, tqfn); // Now we can write the actual documentation for the entity fprintf(docgen->type_file, "# %s", name); doc_type_params(docgen, tparams, true); fprintf(docgen->type_file, "\n\n"); if(ast_id(doc) != TK_NONE) fprintf(docgen->type_file, "%s\n\n", ast_name(doc)); // code block fprintf(docgen->type_file, "```pony\n"); fprintf(docgen->type_file, "%s ",ast_get_print(ast)); const char* cap_text = doc_get_cap(cap); if(cap_text != NULL) fprintf(docgen->type_file, "%s ", cap_text); fprintf(docgen->type_file, "%s", name); doc_type_params(docgen, tparams, false); doc_type_list(docgen, provides, " is\n ", ",\n ", "", false); fprintf(docgen->type_file, "\n```\n\n"); if (ast_id(ast) != TK_TYPE) doc_type_list(docgen, provides, "#### Implements\n\n* ", "\n* ", "\n\n---\n\n", true); else doc_type_list(docgen, provides, "#### Type Alias For\n\n* ", "\n* ", "\n\n---\n\n", true); // Sort members into varieties ast_list_t pub_fields = { NULL, NULL, NULL }; ast_list_t news = { NULL, NULL, NULL }; ast_list_t pub_bes = { NULL, NULL, NULL }; ast_list_t priv_bes = { NULL, NULL, NULL }; ast_list_t pub_funs = { NULL, NULL, NULL }; ast_list_t priv_funs = { NULL, NULL, NULL }; for(ast_t* p = ast_child(members); p != NULL; p = ast_sibling(p)) { switch(ast_id(p)) { case TK_FVAR: case TK_FLET: case TK_EMBED: doc_list_add_named(&pub_fields, p, 0, true, false); break; case TK_NEW: doc_list_add_named(&news, p, 1, true, true); break; case TK_BE: doc_list_add_named(&pub_bes, p, 1, true, false); doc_list_add_named(&priv_bes, p, 1, false, true); break; case TK_FUN: doc_list_add_named(&pub_funs, p, 1, true, false); doc_list_add_named(&priv_funs, p, 1, false, true); break; default: assert(0); break; } } // Handle member variety lists doc_methods(docgen, &news, "Constructors"); doc_fields(docgen, &pub_fields, "Public fields"); doc_methods(docgen, &pub_bes, "Public Behaviours"); doc_methods(docgen, &pub_funs, "Public Functions"); doc_methods(docgen, &priv_bes, "Private Behaviours"); doc_methods(docgen, &priv_funs, "Private Functions"); doc_list_free(&pub_fields); doc_list_free(&news); doc_list_free(&pub_bes); doc_list_free(&priv_bes); doc_list_free(&pub_funs); doc_list_free(&priv_funs); fclose(docgen->type_file); docgen->type_file = NULL; }
// Write a description of the given method to the current type file static void doc_method(docgen_t* docgen, ast_t* method) { assert(docgen != NULL); assert(docgen->type_file != NULL); assert(method != NULL); AST_GET_CHILDREN(method, cap, id, t_params, params, ret, error, body, doc); const char* name = ast_name(id); assert(name != NULL); // Method fprintf(docgen->type_file, "### %s", name); doc_type_params(docgen, t_params, true); fprintf(docgen->type_file, "\n\n"); // The docstring, if any if(ast_id(doc) != TK_NONE) fprintf(docgen->type_file, "%s\n\n", ast_name(doc)); // SYLVAN'S FULL CODE BLOCK HERE fprintf(docgen->type_file, "```pony\n"); fprintf(docgen->type_file, "%s ", ast_get_print(method)); if(ast_id(method) == TK_FUN || ast_id(method) == TK_NEW) { const char* cap_text = doc_get_cap(cap); if(cap_text != NULL) fprintf(docgen->type_file, "%s ", cap_text); } fprintf(docgen->type_file, "%s", name); doc_type_params(docgen, t_params, false); // parameters of the code block code_block_doc_params(docgen, params); // return type if(ast_id(method) == TK_FUN || ast_id(method) == TK_NEW) { fprintf(docgen->type_file, "\n: "); doc_type(docgen, ret, false); if(ast_id(error) == TK_QUESTION) fprintf(docgen->type_file, " ?"); } // close the block fprintf(docgen->type_file, "\n```\n"); // Parameters list_doc_params(docgen, params); // Return value if(ast_id(method) == TK_FUN || ast_id(method) == TK_NEW) { fprintf(docgen->type_file, "#### Returns\n\n"); fprintf(docgen->type_file, "* "); doc_type(docgen, ret, true); if(ast_id(error) == TK_QUESTION) fprintf(docgen->type_file, " ?"); fprintf(docgen->type_file, "\n\n"); } // horizontal rule at the end // separate us from the next method visually fprintf(docgen->type_file, "---\n\n"); }
// Write the given type to the current type file static void doc_type(docgen_t* docgen, ast_t* type, bool generate_links) { assert(docgen != NULL); assert(docgen->type_file != NULL); assert(type != NULL); switch(ast_id(type)) { case TK_NOMINAL: { AST_GET_CHILDREN(type, package, id, tparams, cap, ephemeral); // Generate links only if directed to and if the type is not anonymous (as // indicated by a name created by package_hygienic_id). if(generate_links && *ast_name(id) != '$') { // Find type we reference so we can link to it ast_t* target = (ast_t*)ast_data(type); assert(target != NULL); size_t link_len; char* tqfn = write_tqfn(target, NULL, &link_len); // Links are of the form: [text](target) fprintf(docgen->type_file, "[%s](%s)", ast_nice_name(id), tqfn); ponyint_pool_free_size(link_len, tqfn); doc_type_list(docgen, tparams, "\\[", ", ", "\\]", true, false); } else { fprintf(docgen->type_file, "%s", ast_nice_name(id)); doc_type_list(docgen, tparams, "[", ", ", "]", false, false); } const char* cap_text = doc_get_cap(cap); if(cap_text != NULL) fprintf(docgen->type_file, " %s", cap_text); if(ast_id(ephemeral) != TK_NONE) fprintf(docgen->type_file, "%s", ast_get_print(ephemeral)); break; } case TK_UNIONTYPE: doc_type_list(docgen, type, "(", " | ", ")", generate_links, true); break; case TK_ISECTTYPE: doc_type_list(docgen, type, "(", " & ", ")", generate_links, false); break; case TK_TUPLETYPE: doc_type_list(docgen, type, "(", " , ", ")", generate_links, false); break; case TK_TYPEPARAMREF: { AST_GET_CHILDREN(type, id, cap, ephemeral); fprintf(docgen->type_file, "%s", ast_nice_name(id)); const char* cap_text = doc_get_cap(cap); if(cap_text != NULL) fprintf(docgen->type_file, " %s", cap_text); if(ast_id(ephemeral) != TK_NONE) fprintf(docgen->type_file, "%s", ast_get_print(ephemeral)); break; } case TK_ARROW: { AST_GET_CHILDREN(type, left, right); doc_type(docgen, left, generate_links); fprintf(docgen->type_file, "->"); doc_type(docgen, right, generate_links); break; } case TK_THISTYPE: fprintf(docgen->type_file, "this"); break; case TK_ISO: case TK_TRN: case TK_REF: case TK_VAL: case TK_BOX: case TK_TAG: fprintf(docgen->type_file, "%s", ast_get_print(type)); break; default: assert(0); } }
// Check the extra information. This includes: // * legal data pointer // * legal scope symbol table // * no extra children static check_res_t check_extras(ast_t* ast, check_state_t* state, errors_t* errors) { assert(ast != NULL); assert(state != NULL); ast_t* type_field = ast_type(ast); if(type_field != NULL) { if(state->type == NULL) { error_preamble(ast); printf("unexpected type\n"); ast_error(state->errors, ast, "Here"); ast_print(ast); #ifdef IMMEDIATE_FAIL assert(false); #endif return CHK_ERROR; } check_res_t r = state->type(type_field, errors); if(r == CHK_ERROR) // Propogate error return CHK_ERROR; if(r == CHK_NOT_FOUND) { error_preamble(ast); printf("type field has invalid id %d\n", ast_id(type_field)); ast_error(state->errors, ast, "Here"); ast_print(ast); #ifdef IMMEDIATE_FAIL assert(false); #endif return CHK_ERROR; } } if(state->child != NULL) { error_preamble(ast); printf("child " __zu " (id %d, %s) unexpected\n", state->child_index, ast_id(state->child), ast_get_print(state->child)); ast_error(state->errors, ast, "Here"); ast_print(ast); #ifdef IMMEDIATE_FAIL assert(false); #endif return CHK_ERROR; } if(ast_data(ast) != NULL && !state->has_data) { error_preamble(ast); printf("unexpected data %p\n", ast_data(ast)); ast_error(state->errors, ast, "Here"); ast_print(ast); #ifdef IMMEDIATE_FAIL assert(false); #endif return CHK_ERROR; } if(ast_has_scope(ast) && !state->is_scope) { error_preamble(ast); printf("unexpected scope\n"); ast_error(state->errors, ast, "Here"); ast_print(ast); #ifdef IMMEDIATE_FAIL assert(false); #endif return CHK_ERROR; } if(!ast_has_scope(ast) && state->is_scope) { error_preamble(ast); printf("expected scope not found\n"); ast_error(state->errors, ast, "Here"); ast_print(ast); #ifdef IMMEDIATE_FAIL assert(false); #endif return CHK_ERROR; } return CHK_OK; }