Examples

The following examples are designed to get you started using Cowl. They are ordered by complexity, so it is recommended that you read them sequentially.

Reading ontologies

Related documentation: ontology reading

 1/*
 2 * This introductory example shows how to read an ontology
 3 * and log its axioms and annotations.
 4 *
 5 * @note Memory allocation failures are not handled for the sake of simplicity.
 6 *
 7 * @author Ivano Bilenchi
 8 *
 9 * @copyright Copyright (c) 2019 SisInf Lab, Polytechnic University of Bari
10 * @copyright <http://swot.sisinflab.poliba.it>
11 * @copyright SPDX-License-Identifier: EPL-2.0
12 */
13#include "cowl.h"
14#include "ulib.h"
15#include <stdio.h>
16#include <stdlib.h>
17
18#define ONTO "example_pizza.owl"
19
20int main(void) {
21    // You must always initialize the library before use.
22    cowl_init();
23
24    // Instantiate a manager and deserialize an ontology from file.
25    CowlManager *manager = cowl_manager();
26    CowlOntology *onto = cowl_manager_read_path(manager, ustring_literal(ONTO));
27
28    if (!onto) {
29        // The ontology could not be read.
30        fprintf(stderr, "Failed to load ontology " ONTO "\n");
31        return EXIT_FAILURE;
32    }
33
34    // Do stuff with the ontology. In this case we are just logging it
35    // to the standard output using the default writer.
36    cowl_manager_write_file(manager, onto, stdout);
37
38    // Release the manager and the ontology.
39    cowl_release(manager);
40    cowl_release(onto);
41
42    return EXIT_SUCCESS;
43}

Handling of errors and imports

Related documentation: error handling, import handling

 1/*
 2 * This example is the same as the previous one, except all errors are handled
 3 * and logged, and ontology imports are resolved.
 4 *
 5 * @author Ivano Bilenchi
 6 *
 7 * @copyright Copyright (c) 2019 SisInf Lab, Polytechnic University of Bari
 8 * @copyright <http://swot.sisinflab.poliba.it>
 9 * @copyright SPDX-License-Identifier: EPL-2.0
10 */
11#include "cowl.h"
12#include "ulib.h"
13#include <stdio.h>
14#include <stdlib.h>
15
16#define ONTO "example_pizza.owl"
17#define IMPORT "import.owl"
18#define LOG "errors.log"
19
20/*
21 * You should return the appropriate ontology given the specified IRI.
22 * This may involve making network requests or simply retrieving
23 * the imported ontology from the local filesystem. In this example
24 * we just return a generic local "import.owl" ontology, disregarding its IRI.
25 */
26static CowlOntology *load_import(cowl_unused void *ctx, cowl_unused CowlIRI *iri) {
27    CowlOntology *import = NULL;
28    CowlManager *manager = cowl_manager();
29
30    if (manager) {
31        import = cowl_manager_read_path(manager, ustring_literal(IMPORT));
32        cowl_release(manager);
33    }
34
35    return import;
36}
37
38/*
39 * In general, it is very reasonable to just check that the ontology returned
40 * by the manager is not NULL. The error handler mechanism is only needed if you wish
41 * to implement more fine-grained error handling. In this example, errors are logged.
42 */
43static void handle_error(void *ctx, CowlError const *error) {
44    cowl_write_error(ctx, error);
45    cowl_write_static(ctx, "\n");
46}
47
48int main(void) {
49    // API initialization can fail due to low memory.
50    if (cowl_init()) {
51        return EXIT_FAILURE;
52    }
53
54    // Setup a global error handler and import loader.
55    CowlImportLoader loader = { NULL, load_import };
56    cowl_set_import_loader(loader);
57
58    UOStream stream;
59    if (uostream_to_path(&stream, LOG)) {
60        fprintf(stderr, "Failed to open " LOG "\n");
61        return EXIT_FAILURE;
62    }
63
64    CowlErrorHandler handler = { &stream, handle_error };
65    cowl_set_error_handler(handler);
66
67    // Read the ontology from file.
68    CowlManager *manager = cowl_manager();
69
70    if (!manager) {
71        return EXIT_FAILURE;
72    }
73
74    CowlOntology *onto = cowl_manager_read_path(manager, ustring_literal(ONTO));
75
76    if (!onto) {
77        fprintf(stderr, "Failed to load ontology " ONTO "\n");
78        return EXIT_FAILURE;
79    }
80
81    // Do stuff with the ontology.
82    cowl_manager_write_file(manager, onto, stdout);
83
84    cowl_release_all(manager, onto);
85    uostream_deinit(&stream);
86
87    return EXIT_SUCCESS;
88}

Querying ontologies

Related documentation: ontology querying

Basic queries

 1/*
 2 * In this example we will be logging the direct atomic subclasses of a class.
 3 *
 4 * @note Memory allocation failures are not handled for the sake of simplicity.
 5 *
 6 * @author Ivano Bilenchi
 7 *
 8 * @copyright Copyright (c) 2019 SisInf Lab, Polytechnic University of Bari
 9 * @copyright <http://swot.sisinflab.poliba.it>
10 * @copyright SPDX-License-Identifier: EPL-2.0
11 */
12#include "cowl.h"
13#include "ulib.h"
14#include <stdio.h>
15#include <stdlib.h>
16
17#define ONTO "example_pizza.owl"
18#define NS "http://www.co-ode.org/ontologies/pizza/pizza.owl#"
19#define CLASS_NAME "Food"
20
21// Iterator body, invoked for each class expression matching the query.
22static bool for_each_cls(void *std_out, CowlAny *cls) {
23    // We are only interested in atomic classes. Note that due to pseudo-inheritance
24    // this check ensures that the concrete type of 'exp' is CowlClass.
25    if (cowl_cls_exp_get_type(cls) != COWL_CET_CLASS) return true;
26
27    // Log the IRI remainder.
28    cowl_write_string(std_out, cowl_iri_get_rem(cowl_class_get_iri(cls)));
29    cowl_write_static(std_out, "\n");
30
31    return true;
32}
33
34int main(void) {
35    cowl_init();
36
37    CowlManager *manager = cowl_manager();
38    CowlOntology *onto = cowl_manager_read_path(manager, ustring_literal(ONTO));
39    cowl_release(manager);
40
41    if (!onto) {
42        fprintf(stderr, "Failed to load ontology " ONTO "\n");
43        return EXIT_FAILURE;
44    }
45
46    // Query the ontology.
47    UOStream *std_out = uostream_std();
48    cowl_write_static(std_out, "Atomic subclasses of " CLASS_NAME ":\n");
49
50    // Get the class whose atomic subclasses we are interested in.
51    CowlClass *cls = cowl_class_from_static(NS CLASS_NAME);
52
53    // Run the query.
54    CowlIterator iter = { std_out, for_each_cls };
55    cowl_ontology_iterate_sub_classes(onto, cls, &iter, false);
56
57    // Cleanup.
58    cowl_release_all(cls, onto);
59
60    return EXIT_SUCCESS;
61}

Recursive queries

 1/*
 2 * In this example we will be logging the atomic subclasses of a class recursively.
 3 *
 4 * @note Memory allocation failures are not handled for the sake of simplicity.
 5 *
 6 * @author Ivano Bilenchi
 7 *
 8 * @copyright Copyright (c) 2019 SisInf Lab, Polytechnic University of Bari
 9 * @copyright <http://swot.sisinflab.poliba.it>
10 * @copyright SPDX-License-Identifier: EPL-2.0
11 */
12#include "cowl.h"
13#include "ulib.h"
14#include <stdio.h>
15#include <stdlib.h>
16
17#define ONTO "example_pizza.owl"
18#define NS "http://www.co-ode.org/ontologies/pizza/pizza.owl#"
19#define CLASS_NAME "Food"
20
21// Custom context struct for the query.
22typedef struct CustomContext {
23    CowlOntology *onto;
24    UOStream *stream;
25} CustomContext;
26
27// Iterator body, invoked for each class expression matching the query.
28static bool for_each_cls(void *ptr, CowlAny *cls) {
29    if (cowl_cls_exp_get_type(cls) != COWL_CET_CLASS) return true;
30
31    // Log the IRI remainder.
32    CustomContext *ctx = ptr;
33    cowl_write_string(ctx->stream, cowl_iri_get_rem(cowl_class_get_iri(cls)));
34    cowl_write_static(ctx->stream, "\n");
35
36    // Recurse.
37    CowlIterator iter = { ctx, for_each_cls };
38    return cowl_ontology_iterate_sub_classes(ctx->onto, cls, &iter, false);
39}
40
41int main(void) {
42    cowl_init();
43
44    CowlManager *manager = cowl_manager();
45    CowlOntology *onto = cowl_manager_read_path(manager, ustring_literal(ONTO));
46    cowl_release(manager);
47
48    if (!onto) {
49        fprintf(stderr, "Failed to load ontology " ONTO "\n");
50        return EXIT_FAILURE;
51    }
52
53    UOStream *std_out = uostream_std();
54    cowl_write_static(std_out, "Recursive atomic subclasses of " CLASS_NAME ":\n");
55    CowlClass *cls = cowl_class_from_static(NS CLASS_NAME);
56
57    // Since we are going to perform a recursive query,
58    // we need the ontology to be part of the context.
59    CustomContext ctx = { onto, std_out };
60    CowlIterator iter = { &ctx, for_each_cls };
61    cowl_ontology_iterate_sub_classes(onto, cls, &iter, false);
62
63    cowl_release_all(cls, onto);
64    return EXIT_SUCCESS;
65}

Advanced queries

 1/*
 2 * In this example we will be logging axioms of different types referencing
 3 * multiple entities.
 4 *
 5 * @note Memory allocation failures are not handled for the sake of simplicity.
 6 *
 7 * @author Ivano Bilenchi
 8 *
 9 * @copyright Copyright (c) 2024 SisInf Lab, Polytechnic University of Bari
10 * @copyright <http://swot.sisinflab.poliba.it>
11 * @copyright SPDX-License-Identifier: EPL-2.0
12 */
13#include "cowl.h"
14#include "cowl_axiom_filter.h"
15#include "cowl_axiom_flags.h"
16#include "cowl_obj_prop.h"
17#include "cowl_writer.h"
18#include "ulib.h"
19#include <stdio.h>
20#include <stdlib.h>
21
22#define ONTO "example_pizza.owl"
23#define NS "http://www.co-ode.org/ontologies/pizza/pizza.owl#"
24#define CLASS_NAME "ThinAndCrispyBase"
25#define PROPERTY_NAME "hasBase"
26
27// Iterator body, invoked for each axiom matching the query.
28static bool for_each_axiom(void *stream, CowlAny *axiom) {
29    cowl_write(stream, axiom);
30    cowl_write_static(stream, "\n");
31    return true;
32}
33
34int main(void) {
35    cowl_init();
36
37    CowlManager *manager = cowl_manager();
38    CowlOntology *onto = cowl_manager_read_path(manager, ustring_literal(ONTO));
39    cowl_release(manager);
40
41    if (!onto) {
42        fprintf(stderr, "Failed to load ontology " ONTO "\n");
43        return EXIT_FAILURE;
44    }
45
46    UOStream *std_out = uostream_std();
47    cowl_write_static(std_out, "Matching axioms:\n");
48    CowlClass *cls = cowl_class_from_static(NS CLASS_NAME);
49    CowlObjProp *prop = cowl_obj_prop_from_static(NS PROPERTY_NAME);
50
51    // We want to log all SubClassOf and EquivalentClasses axioms that reference
52    // both the class and the property.
53
54    // Note that this can be done via other query functions as well, though
55    // using a CowlAxiomFilter is usually more efficient, as it is used
56    // internally to determine the best indexing strategy for the query.
57    CowlAxiomFlags types = COWL_AF_SUB_CLASS | COWL_AF_EQUIV_CLASSES;
58    CowlAxiomFilter filter = cowl_axiom_filter(types);
59    cowl_axiom_filter_add_primitive(&filter, cls);
60    cowl_axiom_filter_add_primitive(&filter, prop);
61
62    CowlIterator iter = { std_out, for_each_axiom };
63    cowl_ontology_iterate_axioms_matching(onto, &filter, &iter, false);
64
65    cowl_axiom_filter_deinit(&filter);
66    cowl_release_all(cls, prop, onto);
67    return EXIT_SUCCESS;
68}

Editing and writing ontologies

Related documentation: ontology editing, ontology writing

  1/*
  2 * This example demonstrates ontology editing and serialization to file.
  3 *
  4 * @note Memory allocation failures are not handled for the sake of simplicity.
  5 *
  6 * @author Ivano Bilenchi
  7 *
  8 * @copyright Copyright (c) 2022 SisInf Lab, Polytechnic University of Bari
  9 * @copyright <http://swot.sisinflab.poliba.it>
 10 * @copyright SPDX-License-Identifier: EPL-2.0
 11 */
 12#include "cowl.h"
 13#include "ulib.h"
 14#include <stdio.h>
 15#include <stdlib.h>
 16
 17#define IN_PATH "example_pizza.owl"
 18#define OUT_PATH "example_pizza_new.owl"
 19#define NS "http://www.co-ode.org/ontologies/pizza/pizza.owl#"
 20
 21int main(void) {
 22    cowl_init();
 23
 24    // We will be editing the pizza ontology by adding the Porcini pizza.
 25    printf("Reading ontology " IN_PATH "... ");
 26    CowlManager *manager = cowl_manager();
 27    CowlOntology *onto = cowl_manager_read_path(manager, ustring_literal(IN_PATH));
 28
 29    if (onto) {
 30        puts("done!");
 31    } else {
 32        puts("failed");
 33        return EXIT_FAILURE;
 34    }
 35
 36    // Declaration(Class(pizza:PorciniTopping))
 37    CowlClass *porcini_topping = cowl_class_from_static(NS "PorciniTopping");
 38    CowlAnyAxiom *axiom = cowl_decl_axiom(porcini_topping, NULL);
 39    cowl_ontology_add_axiom(onto, axiom);
 40    cowl_release(axiom);
 41
 42    // Declaration(Class(pizza:Porcini))
 43    CowlClass *porcini = cowl_class_from_static(NS "Porcini");
 44    axiom = cowl_decl_axiom(porcini, NULL);
 45    cowl_ontology_add_axiom(onto, axiom);
 46    cowl_release(axiom);
 47
 48    // SubClassOf(pizza:PorciniTopping pizza:MushroomTopping)
 49    CowlClass *mushroom_topping = cowl_class_from_static(NS "MushroomTopping");
 50    axiom = cowl_sub_cls_axiom(porcini_topping, mushroom_topping, NULL);
 51    cowl_ontology_add_axiom(onto, axiom);
 52    cowl_release_all(axiom, mushroom_topping);
 53
 54    // SubClassOf(pizza:Porcini pizza:NamedPizza)
 55    CowlClass *named_pizza = cowl_class_from_static(NS "NamedPizza");
 56    axiom = cowl_sub_cls_axiom(porcini, named_pizza, NULL);
 57    cowl_ontology_add_axiom(onto, axiom);
 58    cowl_release_all(axiom, named_pizza);
 59
 60    // SubClassOf(pizza:Porcini
 61    // ObjectSomeValuesFrom(pizza:hasTopping pizza:MozzarellaTopping))
 62    CowlObjProp *has_topping = cowl_obj_prop_from_static(NS "hasTopping");
 63    CowlClass *mozzarella_topping = cowl_class_from_static(NS "MozzarellaTopping");
 64    CowlObjQuant *obj_quant = cowl_obj_quant(COWL_QT_SOME, has_topping,
 65                                             mozzarella_topping);
 66    axiom = cowl_sub_cls_axiom(porcini, obj_quant, NULL);
 67    cowl_ontology_add_axiom(onto, axiom);
 68    cowl_release_all(axiom, obj_quant, mozzarella_topping);
 69
 70    // SubClassOf(pizza:Porcini
 71    // ObjectSomeValuesFrom(pizza:hasTopping pizza:PorciniTopping))
 72    obj_quant = cowl_obj_quant(COWL_QT_SOME, has_topping, porcini_topping);
 73    axiom = cowl_sub_cls_axiom(porcini, obj_quant, NULL);
 74    cowl_ontology_add_axiom(onto, axiom);
 75    cowl_release_all(axiom, obj_quant);
 76
 77    // SubClassOf(pizza:Porcini ObjectAllValuesFrom(pizza:hasTopping
 78    // ObjectUnionOf(pizza:MozzarellaTopping pizza:PorciniTopping)))
 79    UVec(CowlObjectPtr) vec = uvec(CowlObjectPtr);
 80    uvec_push(CowlObjectPtr, &vec, mozzarella_topping);
 81    uvec_push(CowlObjectPtr, &vec, porcini_topping);
 82    CowlVector *operands = cowl_vector(&vec);
 83    CowlNAryBool *closure = cowl_nary_bool(COWL_NT_UNION, operands);
 84    obj_quant = cowl_obj_quant(COWL_QT_ALL, has_topping, closure);
 85    axiom = cowl_sub_cls_axiom(porcini, obj_quant, NULL);
 86    cowl_ontology_add_axiom(onto, axiom);
 87    cowl_release_all(axiom, obj_quant, closure, operands, porcini_topping, porcini,
 88                     has_topping);
 89
 90    // Serialize the edited ontology to a new file.
 91    printf("Writing ontology " OUT_PATH "... ");
 92
 93    if (cowl_manager_write_path(manager, onto, ustring_literal(OUT_PATH))) {
 94        puts("failed");
 95    } else {
 96        puts("done!");
 97    }
 98
 99    cowl_release_all(onto, manager);
100    return EXIT_SUCCESS;
101}

Ontology streams

Related documentation: input streams, output streams

Input streams

 1/*
 2 * In this example we will be logging the direct atomic subclasses
 3 * of a certain class, but we will do so without instantiating a
 4 * CowlOntology object.
 5 *
 6 * @note Memory allocation failures are not handled for the sake of simplicity.
 7 *
 8 * @author Ivano Bilenchi
 9 *
10 * @copyright Copyright (c) 2023 SisInf Lab, Polytechnic University of Bari
11 * @copyright <http://swot.sisinflab.poliba.it>
12 * @copyright SPDX-License-Identifier: EPL-2.0
13 */
14#include "cowl.h"
15#include "ulib.h"
16#include <stdio.h>
17#include <stdlib.h>
18
19#define ONTO "example_pizza.owl"
20#define NS "http://www.co-ode.org/ontologies/pizza/pizza.owl#"
21#define CLASS_NAME "Food"
22
23// Axiom handler, invoked for each axiom in the ontology document.
24static cowl_ret handle_axiom(void *target_class, CowlAnyAxiom *axiom) {
25    // We are only interested in subclass axioms.
26    if (cowl_axiom_get_type(axiom) != COWL_AT_SUB_CLASS) return COWL_OK;
27
28    // We are only interested in axioms where the superclass is the target class.
29    CowlAnyClsExp *cls = cowl_sub_cls_axiom_get_super(axiom);
30    if (!cowl_equals(target_class, cls)) return COWL_OK;
31
32    // We are only interested in axioms where the subclass is atomic.
33    cls = cowl_sub_cls_axiom_get_sub(axiom);
34    if (cowl_cls_exp_get_type(cls) != COWL_CET_CLASS) return COWL_OK;
35
36    // Log the IRI remainder.
37    puts(cowl_string_get_cstring(cowl_iri_get_rem(cowl_class_get_iri(cls))));
38    return COWL_OK;
39}
40
41int main(void) {
42    cowl_init();
43
44    CowlManager *manager = cowl_manager();
45    CowlClass *target_class = cowl_class_from_static(NS CLASS_NAME);
46
47    // Configure the ontology input stream.
48    CowlIStreamHandlers handlers = { .ctx = target_class, .axiom = handle_axiom };
49    CowlIStream *stream = cowl_manager_get_istream(manager, handlers);
50
51    // Process the ontology as a stream.
52    puts("Atomic subclasses of " CLASS_NAME ":");
53    if (cowl_istream_process_path(stream, ustring_literal(ONTO))) {
54        return EXIT_FAILURE;
55    }
56
57    cowl_release_all(manager, target_class, stream);
58    return EXIT_SUCCESS;
59}

Output streams

  1/*
  2 * In this example we will be creating a new ontology document by using an
  3 * ontology output stream.
  4 *
  5 * @note Memory allocation failures are not handled for the sake of simplicity.
  6 *
  7 * @author Ivano Bilenchi
  8 *
  9 * @copyright Copyright (c) 2023 SisInf Lab, Polytechnic University of Bari
 10 * @copyright <http://swot.sisinflab.poliba.it>
 11 * @copyright SPDX-License-Identifier: EPL-2.0
 12 *
 13 * @file
 14 */
 15
 16#include "cowl.h"
 17#include "ulib.h"
 18#include <stdio.h>
 19#include <stdlib.h>
 20
 21#define PATH "porcini_pizza.owl"
 22
 23#define IMPORT_IRI "http://www.co-ode.org/ontologies/pizza"
 24#define IMPORT_NS IMPORT_IRI "/pizza.owl#"
 25
 26#define IRI "http://foo.com/ontologies/porcini_pizza"
 27#define NS IRI "/porcini_pizza.owl#"
 28
 29int main(void) {
 30    cowl_init();
 31
 32    printf("Generating ontology " PATH "... ");
 33
 34    UOStream ostream;
 35    if (uostream_to_path(&ostream, PATH)) {
 36        // Initializing and writing to the stream may fail.
 37        // You should handle IO errors as fit for your application.
 38        goto err_io;
 39    }
 40
 41    CowlManager *manager = cowl_manager();
 42    CowlOStream *stream = cowl_manager_get_ostream(manager, &ostream);
 43    cowl_release(manager);
 44
 45    // Optional: setup prefixes so that IRIs can be rendered in their prefixed form.
 46    CowlSymTable *st = cowl_ostream_get_sym_table(stream);
 47    cowl_sym_table_register_prefix_raw(st, ustring_literal(""), ustring_literal(NS),
 48                                       false);
 49    cowl_sym_table_register_prefix_raw(st, ustring_literal("pizza"),
 50                                       ustring_literal(IMPORT_NS), false);
 51
 52    // Write the ontology header.
 53    CowlIRI *iri = cowl_iri_from_static(IRI);
 54    CowlIRI *import_iri = cowl_iri_from_static(IMPORT_IRI);
 55    UVec(CowlObjectPtr) imports = uvec(CowlObjectPtr);
 56    uvec_push(CowlObjectPtr, &imports, import_iri);
 57
 58    CowlOntologyHeader header = {
 59        .id = { iri },
 60        .imports = &imports,
 61    };
 62
 63    if (cowl_ostream_write_header(stream, header)) goto err_io;
 64
 65    cowl_release_all(iri, import_iri);
 66    uvec_deinit(CowlObjectPtr, &imports);
 67
 68    // Write the axioms.
 69    // Declaration(Class(:PorciniTopping))
 70    CowlClass *porcini = cowl_class_from_static(NS "PorciniTopping");
 71    CowlAnyAxiom *axiom = cowl_decl_axiom(porcini, NULL);
 72    if (cowl_ostream_write_axiom(stream, axiom)) goto err_io;
 73    cowl_release(axiom);
 74
 75    // Declaration(Class(:Porcini))
 76    CowlClass *porcini_pizza = cowl_class_from_static(NS "Porcini");
 77    axiom = cowl_decl_axiom(porcini_pizza, NULL);
 78    if (cowl_ostream_write_axiom(stream, axiom)) goto err_io;
 79    cowl_release(axiom);
 80
 81    // SubClassOf(:PorciniTopping pizza:MushroomTopping)
 82    CowlClass *mushroom = cowl_class_from_static(IMPORT_NS "MushroomTopping");
 83    axiom = cowl_sub_cls_axiom(porcini, mushroom, NULL);
 84    if (cowl_ostream_write_axiom(stream, axiom)) goto err_io;
 85    cowl_release_all(axiom, mushroom);
 86
 87    // SubClassOf(:Porcini pizza:NamedPizza)
 88    CowlClass *named_pizza = cowl_class_from_static(IMPORT_NS "NamedPizza");
 89    axiom = cowl_sub_cls_axiom(porcini_pizza, named_pizza, NULL);
 90    if (cowl_ostream_write_axiom(stream, axiom)) goto err_io;
 91    cowl_release_all(axiom, named_pizza);
 92
 93    // SubClassOf(:Porcini
 94    // ObjectSomeValuesFrom(pizza:hasTopping pizza:MozzarellaTopping))
 95    CowlObjProp *has_topping = cowl_obj_prop_from_static(IMPORT_NS "hasTopping");
 96    CowlClass *mozzarella = cowl_class_from_static(IMPORT_NS "MozzarellaTopping");
 97    CowlObjQuant *obj_quant = cowl_obj_quant(COWL_QT_SOME, has_topping, mozzarella);
 98    axiom = cowl_sub_cls_axiom(porcini_pizza, obj_quant, NULL);
 99    if (cowl_ostream_write_axiom(stream, axiom)) goto err_io;
100    cowl_release_all(axiom, obj_quant, mozzarella);
101
102    // SubClassOf(:Porcini
103    // ObjectSomeValuesFrom(pizza:hasTopping :PorciniTopping))
104    obj_quant = cowl_obj_quant(COWL_QT_SOME, has_topping, porcini);
105    axiom = cowl_sub_cls_axiom(porcini_pizza, obj_quant, NULL);
106    if (cowl_ostream_write_axiom(stream, axiom)) goto err_io;
107    cowl_release_all(axiom, obj_quant);
108
109    // SubClassOf(:Porcini ObjectAllValuesFrom(pizza:hasTopping
110    // ObjectUnionOf(pizza:MozzarellaTopping :PorciniTopping)))
111    UVec(CowlObjectPtr) vec = uvec(CowlObjectPtr);
112    uvec_push(CowlObjectPtr, &vec, mozzarella);
113    uvec_push(CowlObjectPtr, &vec, porcini);
114    CowlVector *operands = cowl_vector(&vec);
115    CowlNAryBool *closure = cowl_nary_bool(COWL_NT_UNION, operands);
116    obj_quant = cowl_obj_quant(COWL_QT_ALL, has_topping, closure);
117    axiom = cowl_sub_cls_axiom(porcini_pizza, obj_quant, NULL);
118    if (cowl_ostream_write_axiom(stream, axiom)) goto err_io;
119    cowl_release_all(axiom, obj_quant, closure, operands, porcini, porcini_pizza,
120                     has_topping);
121
122    // Finally, write the footer.
123    if (cowl_ostream_write_footer(stream)) goto err_io;
124
125    puts("done!");
126    cowl_release(stream);
127    return EXIT_SUCCESS;
128
129err_io:
130    puts("failed");
131    return EXIT_FAILURE;
132}