diff --git a/src/runtime/c/Makefile.am b/src/runtime/c/Makefile.am index 1912a89be..030d4b627 100644 --- a/src/runtime/c/Makefile.am +++ b/src/runtime/c/Makefile.am @@ -38,7 +38,9 @@ libpgf_la_SOURCES = \ pgf/expr.h \ pgf/namespace.h \ pgf/phrasetable.cxx \ - pgf/phrasetable.h + pgf/phrasetable.h \ + pgf/probspace.cxx \ + pgf/probspace.h libpgf_la_LDFLAGS = -no-undefined -version-info 4:0:0 libpgf_la_CXXFLAGS = -fno-rtti -std=c++11 -DCOMPILING_PGF diff --git a/src/runtime/c/pgf/data.cxx b/src/runtime/c/pgf/data.cxx index f7ba0b3fb..e0d8b7cce 100644 --- a/src/runtime/c/pgf/data.cxx +++ b/src/runtime/c/pgf/data.cxx @@ -30,6 +30,7 @@ void PgfPGF::release(ref pgf) namespace_release(pgf->abstract.aflags); namespace_release(pgf->abstract.funs); namespace_release(pgf->abstract.cats); + probspace_release(pgf->abstract.funs_by_cat); namespace_release(pgf->concretes); PgfDB::free(pgf); } diff --git a/src/runtime/c/pgf/data.h b/src/runtime/c/pgf/data.h index 04c84d1bc..bf8b497b5 100644 --- a/src/runtime/c/pgf/data.h +++ b/src/runtime/c/pgf/data.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "pgf.h" @@ -75,6 +76,7 @@ private: }; struct PgfPGF; +struct PgfAbsFun; struct PgfConcr; #include "db.h" @@ -82,6 +84,7 @@ struct PgfConcr; #include "vector.h" #include "namespace.h" #include "phrasetable.h" +#include "probspace.h" #include "expr.h" struct PGF_INTERNAL_DECL PgfFlag { @@ -114,6 +117,7 @@ typedef struct { Namespace aflags; Namespace funs; Namespace cats; + PgfProbspace funs_by_cat; } PgfAbstr; struct PGF_INTERNAL_DECL PgfLParam { @@ -289,12 +293,6 @@ struct PGF_INTERNAL_DECL PgfConcr { PgfPhrasetable phrasetable; Namespace printnames; - // If there are references from the host language to this concrete, - // then it is included in a double-linked list. If a process - // dies without releasing the reference, it will be released by - // the first process who have an exclusive access to the database. - ref prev, next; - PgfText name; static void release(ref pgf); diff --git a/src/runtime/c/pgf/namespace.h b/src/runtime/c/pgf/namespace.h index a74c950fa..a5cd0ea96 100644 --- a/src/runtime/c/pgf/namespace.h +++ b/src/runtime/c/pgf/namespace.h @@ -18,6 +18,9 @@ using Namespace = ref>>; template class PGF_INTERNAL_DECL Node { + const static size_t DELTA = 3; + const static size_t RATIO = 2; + public: txn_t txn_id; @@ -89,7 +92,7 @@ public: left->left, right); } else { - if (node->left->right->sz < 2 * node->left->left->sz) { + if (node->left->right->sz < RATIO * node->left->left->sz) { ref> left = node->left; ref> right = upd_node(node, @@ -119,8 +122,8 @@ public: if (node->left == 0) { return node; } else { - if (node->left->sz > 3*node->right->sz) { - if (node->left->right->sz < 2*node->left->left->sz) { + if (node->left->sz > DELTA*node->right->sz) { + if (node->left->right->sz < RATIO*node->left->left->sz) { ref> left = node->left; ref> right = upd_node(node, @@ -181,7 +184,7 @@ public: left, right); } else { - if (node->right->left->sz < 2 * node->right->right->sz) { + if (node->right->left->sz < RATIO * node->right->right->sz) { ref> right = node->right; ref> left = upd_node(node, @@ -211,8 +214,8 @@ public: if (node->right == 0) { return node; } else { - if (node->right->sz > 3*node->left->sz) { - if (node->right->left->sz < 2*node->right->right->sz) { + if (node->right->sz > DELTA*node->left->sz) { + if (node->right->left->sz < RATIO*node->right->right->sz) { ref> right = node->right; ref> left = upd_node(node, @@ -280,6 +283,82 @@ public: } } + static + ref link(ref node, ref left, ref right) + { + if (left == 0) + return insert_min(node,right); + if (right == 0) + return insert_max(node,left); + + if (DELTA*left->sz < right->sz) { + left = link(node,left,right->left); + ref node = upd_node(node,left,right->right); + return balanceL(node); + } + + if (left->sz > DELTA*right->sz) { + right = link(node,left->right,right); + ref node = upd_node(node,left->left,right); + return balanceR(node); + } + + return upd_node(node,left,right); + } + + static + ref insert_max(ref node, ref t) + { + if (t == 0) + return upd_node(node,0,0); + ref right = insert_max(node,t->right); + node = upd_node(node,t->left,right); + return balanceR(node); + } + + static + ref insert_min(ref node, ref t) + { + if (t == 0) + return upd_node(node,0,0); + ref left = insert_min(node,t->left); + node = upd_node(node,left,t->right); + return balanceL(node); + } + + static + ref link(ref left, ref right) + { + if (left == 0) + return right; + if (right == 0) + return left; + + if (DELTA*left->sz < right->sz) { + left = link(left,right->left); + ref node = upd_node(right,left,right->right); + return balanceL(node); + } + + if (left->sz > DELTA*right->sz) { + right = link(left->right,right); + ref node = upd_node(left,left->left,right); + return balanceR(node); + } + + if (left->sz > right->sz) { + ref node; + left = pop_last(left, &node); + node = upd_node(node, left, right); + return balanceR(node); + } else { + ref node; + right = pop_first(right, &node); + node = upd_node(node, left, right); + return balanceL(node); + } + } + static void release(ref node) { @@ -311,10 +390,34 @@ Namespace namespace_insert(Namespace map, ref value) int cmp = textcmp(&value->name,&map->value->name); if (cmp < 0) { Namespace left = namespace_insert(map->left, value); + if (left != 0) { + map = Node>::upd_node(map,left,map->right); + return Node>::balanceL(map); + } + } else if (cmp > 0) { + Namespace right = namespace_insert(map->right, value); + if (right != 0) { + map = Node>::upd_node(map,map->left,right); + return Node>::balanceR(map); + } + } + + return 0; +} + +template +Namespace namespace_update(Namespace map, ref value) +{ + if (map == 0) + return Node>::new_node(value); + + int cmp = textcmp(&value->name,&map->value->name); + if (cmp < 0) { + Namespace left = namespace_update(map->left, value); map = Node>::upd_node(map,left,map->right); return Node>::balanceL(map); } else if (cmp > 0) { - Namespace right = namespace_insert(map->right, value); + Namespace right = namespace_update(map->right, value); map = Node>::upd_node(map,map->left,right); return Node>::balanceR(map); } else { @@ -416,6 +519,9 @@ public: if (new_map != 0) return new_map; + if (base == 0) + return 0; + if (name->size >= available) { size_t new_size = name->size + 10; PgfText *new_name = (PgfText *) @@ -521,6 +627,21 @@ void namespace_iter(Namespace map, PgfItor* itor, PgfExn *err) return; } +template +Namespace namespace_map(Namespace map, std::function(ref)> f) +{ + if (map != 0) { + auto left = namespace_map(map->left, f); + auto value = f(map->value); + auto right = namespace_map(map->right, f); + + map = Node>::upd_node(map,left,right); + map->value = value; + } + + return map; +} + template void namespace_vec_fill_names(Namespace node, size_t offs, Vector *vec) { diff --git a/src/runtime/c/pgf/pgf.cxx b/src/runtime/c/pgf/pgf.cxx index f9136b03b..1a51d0203 100644 --- a/src/runtime/c/pgf/pgf.cxx +++ b/src/runtime/c/pgf/pgf.cxx @@ -144,8 +144,8 @@ PgfDB *pgf_boot_ngf(const char* pgf_path, const char* ngf_path, PGF_API PgfDB *pgf_read_ngf(const char *fpath, - PgfRevision *revision, - PgfExn* err) + PgfRevision *revision, + PgfExn* err) { PgfDB *db = NULL; @@ -487,21 +487,6 @@ void pgf_iter_functions(PgfDB *db, PgfRevision revision, } PGF_API_END } -struct PgfItorCatHelper : PgfItor -{ - PgfText *cat; - PgfItor *itor; -}; - -static -void iter_by_cat_helper(PgfItor *itor, PgfText *key, object value, PgfExn *err) -{ - PgfItorCatHelper* helper = (PgfItorCatHelper*) itor; - ref absfun = value; - if (textcmp(helper->cat, &absfun->type->name) == 0) - helper->itor->fn(helper->itor, key, value, err); -} - PGF_API void pgf_iter_functions_by_cat(PgfDB *db, PgfRevision revision, PgfText *cat, PgfItor *itor, PgfExn *err) @@ -510,12 +495,7 @@ void pgf_iter_functions_by_cat(PgfDB *db, PgfRevision revision, DB_scope scope(db, READER_SCOPE); ref pgf = db->revision2pgf(revision); - PgfItorCatHelper helper; - helper.fn = iter_by_cat_helper; - helper.cat = cat; - helper.itor = itor; - - namespace_iter(pgf->abstract.funs, &helper, err); + probspace_iter(pgf->abstract.funs_by_cat, cat, itor, false, err); } PGF_API_END } @@ -1217,6 +1197,7 @@ PgfRevision pgf_start_transaction(PgfDB *db, PgfExn *err) new_pgf->abstract.aflags = pgf->abstract.aflags; new_pgf->abstract.funs = pgf->abstract.funs; new_pgf->abstract.cats = pgf->abstract.cats; + new_pgf->abstract.funs_by_cat = pgf->abstract.funs_by_cat; new_pgf->concretes = pgf->concretes; db->set_transaction_object(new_pgf.as_object()); @@ -1279,6 +1260,8 @@ PgfText *pgf_create_function(PgfDB *db, PgfRevision revision, PgfNameAllocator nalloc(name_pattern); Namespace funs = nalloc.allocate(pgf->abstract.funs); + if (funs == 0) + throw pgf_error("A function with that name already exists"); PgfText *name; ref absfun; nalloc.fetch_name_value(&name, &absfun); @@ -1290,12 +1273,57 @@ PgfText *pgf_create_function(PgfDB *db, PgfRevision revision, pgf->abstract.funs = funs; + PgfProbspace funs_by_cat = + probspace_insert(pgf->abstract.funs_by_cat, absfun); + pgf->abstract.funs_by_cat = funs_by_cat; + return name; } PGF_API_END return NULL; } +static +ref clone_concrete(ref pgf, ref concr) +{ + ref clone = concr; + if (!current_db->is_transient_object(clone.as_object())) { + clone = PgfDB::malloc(concr->name.size+1); + clone->cflags = concr->cflags; + clone->lins = concr->lins; + clone->lincats = concr->lincats; + clone->phrasetable = concr->phrasetable; + clone->printnames = concr->printnames; + memcpy(&clone->name, &concr->name, sizeof(PgfText)+concr->name.size+1); + + Namespace concrs = + namespace_update(pgf->concretes, clone); + pgf->concretes = concrs; + + PgfDB::free(concr, concr->name.size+1); + } + return clone; +} + +static +void drop_lin(ref concr, PgfText *name) +{ + ref lin; + Namespace lins = + namespace_delete(concr->lins, name, &lin); + if (lin != 0) { + object container = lin.tagged(); + for (size_t i = 0; i < lin->seqs->len; i++) { + ref seq = *vector_elem(lin->seqs, i); + PgfPhrasetable phrasetable = + phrasetable_delete(concr->phrasetable,container,i,seq); + concr->phrasetable = phrasetable; + } + PgfConcrLin::release(lin); + } + concr->lins = lins; +} + PGF_API void pgf_drop_function(PgfDB *db, PgfRevision revision, PgfText *name, @@ -1309,8 +1337,21 @@ void pgf_drop_function(PgfDB *db, PgfRevision revision, ref fun; Namespace funs = namespace_delete(pgf->abstract.funs, name, &fun); - if (fun != 0) + if (fun != 0) { + PgfProbspace funs_by_cat = + probspace_delete(pgf->abstract.funs_by_cat, fun); + pgf->abstract.funs_by_cat = funs_by_cat; + + std::function(ref)> f = + [name,pgf](ref concr){ + concr = clone_concrete(pgf, concr); + drop_lin(concr,name); + return concr; + }; + namespace_map(pgf->concretes, f); + PgfAbsFun::release(fun); + } pgf->abstract.funs = funs; } PGF_API_END } @@ -1341,10 +1382,49 @@ void pgf_create_category(PgfDB *db, PgfRevision revision, Namespace cats = namespace_insert(pgf->abstract.cats, abscat); + if (cats == 0) { + throw pgf_error("A category with that name already exists"); + } pgf->abstract.cats = cats; } PGF_API_END } +struct PGF_INTERNAL_DECL PgfDropItor : PgfItor +{ + ref pgf; + ref concrete; + PgfText *name; +}; + +static +void iter_drop_cat_helper2(PgfItor *itor, PgfText *key, object value, PgfExn *err) +{ + ref concr = value; + PgfText* name = ((PgfDropItor*) itor)->name; + + drop_lin(concr, name); +} + +static +void iter_drop_cat_helper(PgfItor *itor, PgfText *key, object value, PgfExn *err) +{ + ref pgf = ((PgfDropItor*) itor)->pgf; + + PgfDropItor itor2; + itor2.fn = iter_drop_cat_helper2; + itor2.pgf = 0; + itor2.concrete = 0; + itor2.name = key; + namespace_iter(pgf->concretes, &itor2, err); + + ref fun; + Namespace funs = + namespace_delete(pgf->abstract.funs, key, &fun); + fun = value; + PgfAbsFun::release(fun); + pgf->abstract.funs = funs; +} + PGF_API void pgf_drop_category(PgfDB *db, PgfRevision revision, PgfText *name, @@ -1358,8 +1438,31 @@ void pgf_drop_category(PgfDB *db, PgfRevision revision, ref cat; Namespace cats = namespace_delete(pgf->abstract.cats, name, &cat); - if (cat != 0) + if (cat != 0) { + std::function(ref)> f = + [name,pgf](ref concr){ + concr = clone_concrete(pgf, concr); + + ref lincat; + Namespace lincats = + namespace_delete(concr->lincats, name, &lincat); + concr->lincats = lincats; + + return concr; + }; + namespace_map(pgf->concretes, f); + + PgfDropItor itor; + itor.fn = iter_drop_cat_helper; + itor.pgf = pgf; + itor.concrete = 0; + itor.name = name; + PgfProbspace funs_by_cat = + probspace_delete_by_cat(pgf->abstract.funs_by_cat, &cat->name, + &itor, err); + pgf->abstract.funs_by_cat = funs_by_cat; PgfAbsCat::release(cat); + } pgf->abstract.cats = cats; } PGF_API_END } @@ -1385,14 +1488,15 @@ PgfConcrRevision pgf_create_concrete(PgfDB *db, PgfRevision revision, concr->lincats = 0; concr->phrasetable = 0; concr->printnames = 0; - concr->prev = 0; - concr->next = 0; memcpy(&concr->name, name, sizeof(PgfText)+name->size+1); object rev = db->register_revision(concr.tagged(), PgfDB::get_txn_id()); Namespace concrs = namespace_insert(pgf->concretes, concr); + if (concrs == 0) { + throw pgf_error("A concrete language with that name already exists"); + } pgf->concretes = concrs; db->ref_count++; @@ -1416,26 +1520,9 @@ PgfConcrRevision pgf_clone_concrete(PgfDB *db, PgfRevision revision, if (concr == 0) throw pgf_error("Unknown concrete syntax"); - ref clone = concr; - if (!current_db->is_transient_object(clone.as_object())) { - clone = PgfDB::malloc(name->size+1); - clone->cflags = concr->cflags; - clone->lins = concr->lins; - clone->lincats = concr->lincats; - clone->phrasetable = concr->phrasetable; - clone->printnames = concr->printnames; - clone->prev = 0; - clone->next = 0; - memcpy(&clone->name, name, sizeof(PgfText)+name->size+1); + concr = clone_concrete(pgf, concr); - Namespace concrs = - namespace_insert(pgf->concretes, clone); - pgf->concretes = concrs; - - PgfDB::free(concr, concr->name.size+1); - } - - object rev = db->register_revision(clone.tagged(), PgfDB::get_txn_id()); + object rev = db->register_revision(concr.tagged(), PgfDB::get_txn_id()); db->ref_count++; return rev; } PGF_API_END @@ -2052,32 +2139,59 @@ void pgf_create_lincat(PgfDB *db, if (lincat != 0) { Namespace lincats = namespace_insert(concr->lincats, lincat); + if (lincats == 0) { + throw pgf_error("A linearization category with that name already exists"); + } concr->lincats = lincats; } } PGF_API_END } +static +void iter_drop_lincat_helper(PgfItor *itor, PgfText *key, object value, PgfExn *err) +{ + ref concr = ((PgfDropItor*) itor)->concrete; + drop_lin(concr, key); +} + PGF_API void pgf_drop_lincat(PgfDB *db, - PgfConcrRevision revision, + PgfRevision revision,PgfConcrRevision cnc_revision, PgfText *name, PgfExn *err) { PGF_API_BEGIN { DB_scope scope(db, WRITER_SCOPE); - ref concr = db->revision2concr(revision); + ref pgf = db->revision2pgf(revision); + ref concr = db->revision2concr(cnc_revision); ref lincat; Namespace lincats = namespace_delete(concr->lincats, name, &lincat); if (lincat != 0) { + // The lincat was indeed in the concrete syntax. + + // Remove the linearizations of all functions that + // depend on it. + PgfDropItor itor; + itor.fn = iter_drop_lincat_helper; + itor.pgf = pgf; + itor.concrete = concr; + itor.name = name; + probspace_iter(pgf->abstract.funs_by_cat, name, + &itor, true, err); + + // Remove the sequences comprizing the lindef and linref object container = lincat.tagged(); + PgfPhrasetable phrasetable = concr->phrasetable; for (size_t i = 0; i < lincat->seqs->len; i++) { ref seq = *vector_elem(lincat->seqs, i); - PgfPhrasetable phrasetable = - phrasetable_delete(concr->phrasetable,container,i,seq); - concr->phrasetable = phrasetable; + phrasetable = + phrasetable_delete(phrasetable,container,i,seq); } + concr->phrasetable = phrasetable; + + // Finaly remove the lincat object itself. PgfConcrLincat::release(lincat); } concr->lincats = lincats; @@ -2111,6 +2225,9 @@ void pgf_create_lin(PgfDB *db, if (lin != 0) { Namespace lins = namespace_insert(concr->lins, lin); + if (lins == 0) { + throw pgf_error("A linearization function with that name already exists"); + } concr->lins = lins; } } PGF_API_END @@ -2118,28 +2235,16 @@ void pgf_create_lin(PgfDB *db, PGF_API void pgf_drop_lin(PgfDB *db, - PgfConcrRevision revision, + PgfRevision revision, PgfConcrRevision cnc_revision, PgfText *name, PgfExn *err) { PGF_API_BEGIN { DB_scope scope(db, WRITER_SCOPE); - ref concr = db->revision2concr(revision); + ref pgf = db->revision2pgf(revision); + ref concr = db->revision2concr(cnc_revision); - ref lin; - Namespace lins = - namespace_delete(concr->lins, name, &lin); - if (lin != 0) { - object container = lin.tagged(); - for (size_t i = 0; i < lin->seqs->len; i++) { - ref seq = *vector_elem(lin->seqs, i); - PgfPhrasetable phrasetable = - phrasetable_delete(concr->phrasetable,container,i,seq); - concr->phrasetable = phrasetable; - } - PgfConcrLin::release(lin); - } - concr->lins = lins; + drop_lin(concr, name); } PGF_API_END } @@ -2463,7 +2568,7 @@ void pgf_set_printname(PgfDB *db, PgfConcrRevision revision, printname->printname = textdup_db(name); Namespace printnames = - namespace_insert(concr->printnames, printname); + namespace_update(concr->printnames, printname); concr->printnames = printnames; } PGF_API_END } @@ -2508,7 +2613,7 @@ void pgf_set_global_flag(PgfDB *db, PgfRevision revision, PgfLiteral lit = m->match_lit(&u, value); flag->value = lit; Namespace gflags = - namespace_insert(pgf->gflags, flag); + namespace_update(pgf->gflags, flag); pgf->gflags = gflags; } PGF_API_END } @@ -2553,7 +2658,7 @@ void pgf_set_abstract_flag(PgfDB *db, PgfRevision revision, PgfLiteral lit = m->match_lit(&u, value); flag->value = lit; Namespace aflags = - namespace_insert(pgf->abstract.aflags, flag); + namespace_update(pgf->abstract.aflags, flag); pgf->abstract.aflags = aflags; } PGF_API_END } @@ -2598,7 +2703,7 @@ void pgf_set_concrete_flag(PgfDB *db, PgfConcrRevision revision, PgfLiteral lit = m->match_lit(&u, value); flag->value = lit; Namespace cflags = - namespace_insert(concr->cflags, flag); + namespace_update(concr->cflags, flag); concr->cflags = cflags; } PGF_API_END } diff --git a/src/runtime/c/pgf/pgf.h b/src/runtime/c/pgf/pgf.h index fc82a2f16..9ab9f733f 100644 --- a/src/runtime/c/pgf/pgf.h +++ b/src/runtime/c/pgf/pgf.h @@ -635,7 +635,8 @@ void pgf_create_lincat(PgfDB *db, PgfExn *err); PGF_API_DECL -void pgf_drop_lincat(PgfDB *db, PgfConcrRevision revision, +void pgf_drop_lincat(PgfDB *db, + PgfRevision revision, PgfConcrRevision cnc_revision, PgfText *name, PgfExn *err); PGF_API_DECL @@ -646,7 +647,8 @@ void pgf_create_lin(PgfDB *db, PgfExn *err); PGF_API_DECL -void pgf_drop_lin(PgfDB *db, PgfConcrRevision revision, +void pgf_drop_lin(PgfDB *db, + PgfRevision revision, PgfConcrRevision cnc_revision, PgfText *name, PgfExn *err); PGF_API_DECL diff --git a/src/runtime/c/pgf/reader.cxx b/src/runtime/c/pgf/reader.cxx index cc9019e93..1459525c5 100644 --- a/src/runtime/c/pgf/reader.cxx +++ b/src/runtime/c/pgf/reader.cxx @@ -329,6 +329,11 @@ ref PgfReader::read_absfun() throw pgf_error("Unknown tag, 0 or 1 expected"); } absfun->prob = read_prob(&absfun->name); + + PgfProbspace funs_by_cat = + probspace_insert(abstract->funs_by_cat, absfun); + abstract->funs_by_cat = funs_by_cat; + return absfun; } @@ -410,6 +415,7 @@ void pad_probs(PgfItor *itor, PgfText *key, object value, PgfExn *err) void PgfReader::read_abstract(ref abstract) { this->abstract = abstract; + abstract->funs_by_cat = 0; auto name = read_name(); auto aflags = read_namespace(&PgfReader::read_flag); @@ -842,9 +848,6 @@ ref PgfReader::read_concrete() auto printnames = read_namespace(&PgfReader::read_printname); concrete->printnames = printnames; - concrete->prev = 0; - concrete->next = 0; - return concrete; } @@ -889,6 +892,6 @@ void PgfReader::merge_pgf(ref pgf) for (size_t i = 0; i < len; i++) { ref concr = PgfReader::read_concrete(); pgf->concretes = - namespace_insert(pgf->concretes, concr); + namespace_update(pgf->concretes, concr); } } diff --git a/src/runtime/haskell/PGF2.hsc b/src/runtime/haskell/PGF2.hsc index fe0d1f181..9d256d2f3 100644 --- a/src/runtime/haskell/PGF2.hsc +++ b/src/runtime/haskell/PGF2.hsc @@ -340,7 +340,7 @@ abstractName :: PGF -> AbsName abstractName p = unsafePerformIO $ withForeignPtr (a_revision p) $ \c_revision -> - bracket (withPgfExn "abstractName" (pgf_abstract_name (a_db p) c_revision)) free $ \c_text -> + bracket (withPgfExn "abstractName" (pgf_abstract_name (a_db p) c_revision)) free $ \c_text -> do peekText c_text -- | The start category is defined in the grammar with diff --git a/src/runtime/haskell/PGF2/FFI.hsc b/src/runtime/haskell/PGF2/FFI.hsc index d05f82708..a6fd70044 100644 --- a/src/runtime/haskell/PGF2/FFI.hsc +++ b/src/runtime/haskell/PGF2/FFI.hsc @@ -233,11 +233,11 @@ foreign import ccall "dynamic" callLinBuilder7 :: Dynamic (Ptr PgfLinBuilderIfac foreign import ccall pgf_create_lincat :: Ptr PgfDB -> Ptr PGF -> Ptr Concr -> Ptr PgfText -> CSize -> Ptr (Ptr PgfText) -> CSize -> CSize -> Ptr PgfBuildLinIface -> Ptr PgfExn -> IO () -foreign import ccall pgf_drop_lincat :: Ptr PgfDB -> Ptr Concr -> Ptr PgfText -> Ptr PgfExn -> IO () +foreign import ccall pgf_drop_lincat :: Ptr PgfDB -> Ptr PGF -> Ptr Concr -> Ptr PgfText -> Ptr PgfExn -> IO () foreign import ccall pgf_create_lin :: Ptr PgfDB -> Ptr PGF -> Ptr Concr -> Ptr PgfText -> CSize -> Ptr PgfBuildLinIface -> Ptr PgfExn -> IO () -foreign import ccall pgf_drop_lin :: Ptr PgfDB -> Ptr Concr -> Ptr PgfText -> Ptr PgfExn -> IO () +foreign import ccall pgf_drop_lin :: Ptr PgfDB -> Ptr PGF -> Ptr Concr -> Ptr PgfText -> Ptr PgfExn -> IO () foreign import ccall pgf_has_linearization :: Ptr PgfDB -> Ptr Concr -> Ptr PgfText -> Ptr PgfExn -> IO CInt @@ -371,7 +371,7 @@ utf8Length s = count 0 s -- Exceptions data PGFError = PGFError String String - deriving Typeable + deriving (Eq,Typeable) instance Show PGFError where show (PGFError loc msg) = loc++": "++msg diff --git a/src/runtime/haskell/PGF2/Transactions.hsc b/src/runtime/haskell/PGF2/Transactions.hsc index 849ad5431..0a848b5b0 100644 --- a/src/runtime/haskell/PGF2/Transactions.hsc +++ b/src/runtime/haskell/PGF2/Transactions.hsc @@ -150,6 +150,8 @@ checkoutPGF p = do contains %d, %x or %a then the pattern is replaced with a random number in base 10, 16, or 36, which guarantees that the name is unique. The returned name is the final name after the substitution. + If there is no substitution pattern in the name, and there is + already a function with the same name then an exception is thrown. -} createFunction :: Fun -> Type -> Int -> [[Instr]] -> Float -> Transaction PGF Fun createFunction name ty arity bytecode prob = Transaction $ \c_db _ c_revision c_exn -> @@ -284,9 +286,9 @@ createLincat name fields lindefs linrefs seqtbl = Transaction $ \c_db c_abstr c_ withTexts p (i+1) ss f dropLincat :: Cat -> Transaction Concr () -dropLincat name = Transaction $ \c_db _ c_revision c_exn -> +dropLincat name = Transaction $ \c_db c_abstr c_revision c_exn -> withText name $ \c_name -> - pgf_drop_lincat c_db c_revision c_name c_exn + pgf_drop_lincat c_db c_abstr c_revision c_name c_exn createLin :: Fun -> [Production] -> SeqTable -> Transaction Concr SeqTable createLin name prods seqtbl = Transaction $ \c_db c_abstr c_revision c_exn -> @@ -406,9 +408,9 @@ withBuildLinIface prods seqtbl f = do pokeTerms (c_terms `plusPtr` (2*(#size size_t))) terms dropLin :: Fun -> Transaction Concr () -dropLin name = Transaction $ \c_db _ c_revision c_exn -> +dropLin name = Transaction $ \c_db c_abstr c_revision c_exn -> withText name $ \c_name -> - pgf_drop_lin c_db c_revision c_name c_exn + pgf_drop_lin c_db c_abstr c_revision c_name c_exn setPrintName :: Fun -> String -> Transaction Concr () setPrintName fun name = Transaction $ \c_db _ c_revision c_exn -> diff --git a/src/runtime/haskell/tests/transactions.hs b/src/runtime/haskell/tests/transactions.hs index 800f1b104..5a9ea95e8 100644 --- a/src/runtime/haskell/tests/transactions.hs +++ b/src/runtime/haskell/tests/transactions.hs @@ -4,34 +4,54 @@ import PGF2.Transactions import System.Mem import System.Exit (exitSuccess, exitFailure) import qualified Data.Map as Map +import Control.Exception (try) main = do gr1 <- readPGF "tests/basic.pgf" let Just ty = readType "(N -> N) -> P (s z)" + excpt1 <- try (modifyPGF gr1 (createFunction "c" ty 0 [] pi) >> return ()) + excpt2 <- try (modifyPGF gr1 (createCategory "N" [] pi) >> return ()) + gr2 <- modifyPGF gr1 (createFunction "foo" ty 0 [] pi >> createCategory "Q" [(Explicit,"x",ty)] pi) gr4 <- checkoutPGF gr1 gr6 <- modifyPGF gr1 (dropFunction "ind" >> dropCategory "S") + let Just cnc6 = Map.lookup "basic_cnc" (languages gr6) gr7 <- modifyPGF gr1 $ createConcrete "basic_eng" $ do setConcreteFlag "test_flag" (LStr "test") + let Just cnc7 = Map.lookup "basic_eng" (languages gr7) + + gr8 <- modifyPGF gr1 $ + alterConcrete "basic_cnc" $ do + dropLin "z" + let Just cnc8 = Map.lookup "basic_cnc" (languages gr8) + + gr9 <- modifyPGF gr1 $ + alterConcrete "basic_cnc" $ do + dropLincat "N" + let Just cnc9 = Map.lookup "basic_cnc" (languages gr9) + + excpt3 <- try (modifyPGF gr1 (alterConcrete "basic_foo" (return ())) >> return ()) - let Just cnc = Map.lookup "basic_eng" (languages gr7) c <- runTestTT $ TestList $ [TestCase (assertEqual "original functions" ["c","floatLit","ind","intLit","nat","s","stringLit","z"] (functions gr1)) + ,TestCase (assertEqual "existing function" (Left (PGFError "modifyPGF" "A function with that name already exists")) excpt1) + ,TestCase (assertEqual "existing category" (Left (PGFError "modifyPGF" "A category with that name already exists")) excpt2) ,TestCase (assertEqual "extended functions" ["c","floatLit","foo","ind","intLit","nat","s","stringLit","z"] (functions gr2)) ,TestCase (assertEqual "checked-out extended functions" ["c","floatLit","foo","ind","intLit","nat","s","stringLit","z"] (functions gr4)) ,TestCase (assertEqual "original categories" ["Float","Int","N","P","S","String"] (categories gr1)) ,TestCase (assertEqual "extended categories" ["Float","Int","N","P","Q","S","String"] (categories gr2)) ,TestCase (assertEqual "Q context" (Just [(Explicit,"x",ty)]) (categoryContext gr2 "Q")) - ,TestCase (assertEqual "reduced functions" ["c","floatLit","foo","intLit","nat","s","stringLit","z"] (functions gr6)) + ,TestCase (assertEqual "reduced functions" ["foo","nat","s","z"] (functions gr6)) ,TestCase (assertEqual "reduced categories" ["Float","Int","N","P","Q","String"] (categories gr6)) + ,TestCase (assertEqual "reduced lins" [False,False,False,False,True,True,False,True] (map (hasLinearization cnc6) ["c","floatLit","foo","intLit","nat","s","stringLit","z"])) ,TestCase (assertEqual "old function type" Nothing (functionType gr1 "foo")) ,TestCase (assertEqual "new function type" (Just ty) (functionType gr2 "foo")) ,TestCase (assertEqual "old function prob" (-log 0) (functionProbability gr1 "foo")) @@ -40,7 +60,10 @@ main = do ,TestCase (assertEqual "new category prob" pi (categoryProbability gr2 "Q")) ,TestCase (assertEqual "empty concretes" ["basic_cnc"] (Map.keys (languages gr1))) ,TestCase (assertEqual "extended concretes" ["basic_cnc","basic_eng"] (Map.keys (languages gr7))) - ,TestCase (assertEqual "added concrete flag" (Just (LStr "test")) (concreteFlag cnc "test_flag")) + ,TestCase (assertEqual "added concrete flag" (Just (LStr "test")) (concreteFlag cnc7 "test_flag")) + ,TestCase (assertEqual "alter missing concrete" (Left (PGFError "modifyPGF" "Unknown concrete syntax")) excpt3) + ,TestCase (assertEqual "drop lin" (True,False) (hasLinearization cnc8 "s",hasLinearization cnc8 "z")) + ,TestCase (assertEqual "drop lincat" (False,False) (hasLinearization cnc9 "s",hasLinearization cnc9 "z")) ] performMajorGC