#include "data.h" #include "printer.h" #include // #define DEBUG_PARSE_INDEX static int lparam_cmp(PgfLParam *p1, PgfLParam *p2) { if (p1->i0 < p2->i0) return -1; else if (p1->i0 > p2->i0) return 1; for (size_t i = 0; ; i++) { if (i >= p1->n_terms) return -(i < p2->n_terms); if (i >= p2->n_terms) return 1; if (p1->terms[i].factor > p2->terms[i].factor) return 1; else if (p1->terms[i].factor < p2->terms[i].factor) return -1; else if (p1->terms[i].var > p2->terms[i].var) return 1; else if (p1->terms[i].var < p2->terms[i].var) return -1; } return 0; } PGF_INTERNAL int text_symbol_cmp(PgfText *sentence, PgfTextSpot *spot, ref sym_ks, bool case_sensitive) { int res1 = 0; const uint8_t *s1 = (uint8_t *) &sentence->text[spot->byte_pos]; const uint8_t *e1 = (uint8_t *) &sentence->text[sentence->size]; const uint8_t *s2 = (uint8_t *) &sym_ks->token.text; const uint8_t *e2 = s2+sym_ks->token.size; for (;;) { if (s1 >= e1) { if (s2 < e2) return -1; return case_sensitive ? res1 : 0; } if (s2 >= e2) { return case_sensitive ? res1 : 0; } uint32_t ucs1 = pgf_utf8_decode(&s1); uint32_t ucs1i = pgf_utf8_to_upper(ucs1); spot->pos++; spot->byte_pos = s1-(uint8_t*) sentence->text; uint32_t ucs2 = pgf_utf8_decode(&s2); uint32_t ucs2i = pgf_utf8_to_upper(ucs2); if (ucs1i > ucs2i) { return 1; } else if (ucs1i < ucs2i) { return -1; } else if (res1 == 0) { if (ucs1 > ucs2) { res1 = 1; } else if (ucs1 < ucs2) { res1 = -1; } } } } static bool text_symbols_match(PgfText *sentence, PgfTextSpot *spot, vector syms, size_t dot, bool *bind, bool case_sensitive) { while (dot < syms.size()) { PgfSymbol sym = syms[dot]; switch (ref::get_tag(sym)) { case PgfSymbolKS::tag: { size_t start = spot->byte_pos; for (;;) { const uint8_t *ptr = (uint8_t *) sentence->text+spot->byte_pos; uint32_t ucs = pgf_utf8_decode(&ptr); if (!pgf_utf8_is_space(ucs)) break; spot->byte_pos = ptr-(uint8_t *)sentence->text; spot->pos++; } if (*bind != (start == spot->byte_pos)) return false; if (text_symbol_cmp(sentence,spot,sym,case_sensitive) != 0) return false; break; } case PgfSymbolKP::tag: { auto symkp = ref::untagged(syms[dot]); PgfTextSpot current = *spot; if (text_symbols_match(sentence, ¤t, symkp->default_form, 0, bind, case_sensitive)) { goto matched; } for (size_t i = 0; i < symkp->alts.size(); i++) { current = *spot; if (text_symbols_match(sentence, ¤t, symkp->alts[i].form, 0, bind, case_sensitive)) { goto matched; } } return false; matched: *spot = current; break; } case PgfSymbolBIND::tag: { *bind = true; break; } case PgfSymbolSOFTBIND::tag: case PgfSymbolSOFTSPACE::tag: { *bind = true; break; } case PgfSymbolCAPIT::tag: case PgfSymbolALLCAPIT::tag: // skip break; default: return false; } dot++; } return true; } static bool text_item_match(PgfText *sentence, PgfTextSpot *spot, ref item, bool case_sensitive) { bool bind = false; size_t dot = item->dot+1; vector syms = item->rule->syms.as_vector(); if (item->pre_alt > 0) { auto symkp = ref::untagged(syms[item->pre_dot]); if (item->pre_alt == 1) { if (!text_symbols_match(sentence, spot, symkp->default_form, item->dot, &bind, case_sensitive)) return false; } else { if (!text_symbols_match(sentence, spot, symkp->alts[item->pre_alt-2].form, item->dot, &bind, case_sensitive)) return false; } dot = item->pre_dot+1; } return text_symbols_match(sentence, spot, syms, dot, &bind, case_sensitive); } PGF_INTERNAL_DECL size_t get_next_padovan(size_t min); template PGF_INTERNAL vector> phrasetable_lookup(PgfPhrasetable phrasetable, ref key, size_t *n_items) { while (phrasetable != 0) { int cmp = compare_key(key, phrasetable->value.key); if (cmp < 0) phrasetable = phrasetable->left; else if (cmp > 0) phrasetable = phrasetable->right; else { *n_items = phrasetable->value.n_items; return phrasetable->value.items; } } *n_items = 0; return 0; } PGF_INTERNAL void phrasetable_lookup(PgfPhrasetable table, PgfText *sentence, bool case_sensitive, PgfPhraseScanner *scanner, PgfExn* err) { if (table == 0) return; PgfTextSpot spot; spot.pos = 0; spot.byte_pos = 0; int cmp = text_symbol_cmp(sentence,&spot,table->value.key,case_sensitive); if (cmp < 0) { phrasetable_lookup(table->left,sentence,case_sensitive,scanner,err); } else if (cmp > 0) { phrasetable_lookup(table->right,sentence,case_sensitive,scanner,err); } else { if (!case_sensitive) { phrasetable_lookup(table->left,sentence,case_sensitive,scanner,err); if (err->type != PGF_EXN_NONE) return; } for (size_t i = 0; i < table->value.n_items; i++) { ref item = table->value.items[i]; switch (ref::get_tag(item->rule->container)) { case PgfConcrLin::tag: { ref lin = ref::untagged(item->rule->container); if (lin->absfun->type->hypos.size() == 0) { PgfTextSpot current = spot; if (text_item_match(sentence, ¤t, item, case_sensitive) && current.byte_pos == sentence->size) { scanner->match(lin, item->rule->lin_idx->i0, err); if (err->type != PGF_EXN_NONE) return; } } break; } case PgfConcrLincat::tag: { //ignore break; } } } if (!case_sensitive) { phrasetable_lookup(table->right,sentence,case_sensitive,scanner,err); if (err->type != PGF_EXN_NONE) return; } } } struct PGF_INTERNAL_DECL PgfCohortsState { class PgfTextSpotComparator : std::less { public: bool operator()(PgfTextSpot &lhs, PgfTextSpot &rhs) const { return lhs.pos > rhs.pos; } }; PgfText *sentence; PgfTextSpot spot; std::priority_queue, PgfTextSpotComparator> queue; PgfTextSpot last; bool skipping; bool case_sensitive; PgfPhraseScanner *scanner; PgfExn* err; }; static void finish_skipping(PgfCohortsState *state) { if (state->skipping) { while (!state->queue.empty()) { PgfTextSpot spot = state->queue.top(); if (spot.pos >= state->spot.pos) break; if (spot.pos != state->last.pos) { if (state->last.pos > 0) { state->scanner->space(&spot, &spot, state->err); if (state->err->type != PGF_EXN_NONE) return; } state->scanner->start_matches(&state->spot, state->err); if (state->err->type != PGF_EXN_NONE) return; state->scanner->end_matches(&state->spot, state->err); if (state->err->type != PGF_EXN_NONE) return; state->last = spot; } state->queue.pop(); } /* state->scanner->space(&state->spot, &state->spot, state->err); */ state->last.pos = 0; state->last.byte_pos = 0; state->skipping = false; } } static void phrasetable_lookup_prefixes(PgfCohortsState *state, PgfPhrasetable table, size_t min, size_t max) { if (table == 0) return; PgfTextSpot current = state->spot; int cmp = text_symbol_cmp(state->sentence,¤t,table->value.key,state->case_sensitive); if (cmp < 0) { phrasetable_lookup_prefixes(state,table->left,min,max); } else if (cmp > 0) { size_t len = current.byte_pos - state->spot.byte_pos; if (min <= len-1) phrasetable_lookup_prefixes(state,table->left,min,len-1); if (len <= max) phrasetable_lookup_prefixes(state,table->right,len,max); } else { size_t len = current.byte_pos - state->spot.byte_pos; finish_skipping(state); if (state->err->type != PGF_EXN_NONE) return; if (min <= len) phrasetable_lookup_prefixes(state,table->left,min,len); if (len > 0) { if (state->last.pos != current.pos) { if (state->last.pos > 0) { state->scanner->end_matches(&state->last, state->err); if (state->err->type != PGF_EXN_NONE) return; } state->scanner->start_matches(¤t, state->err); if (state->err->type != PGF_EXN_NONE) return; state->last = current; } state->queue.push(current); for (size_t i = 0; i < table->value.n_items; i++) { auto rule = table->value.items[i]->rule; switch (ref::get_tag(rule->container)) { case PgfConcrLin::tag: { ref lin = ref::untagged(rule->container); if (lin->absfun->type->hypos.size() == 0) { state->scanner->match(lin, rule->lin_idx->i0, state->err); if (state->err->type != PGF_EXN_NONE) return; } break; } case PgfConcrLincat::tag: { //ignore break; } } } } if (len <= max) phrasetable_lookup_prefixes(state,table->right,len,max); } } PGF_INTERNAL void phrasetable_lookup_cohorts(PgfPhrasetable table, PgfText *sentence, bool case_sensitive, PgfPhraseScanner *scanner, PgfExn* err) { PgfTextSpot spot; spot.pos = 0; spot.byte_pos = 0; PgfCohortsState state; state.spot.pos = -1; state.spot.byte_pos = -1; state.sentence = sentence; state.queue.push(spot); state.last = spot; state.skipping = false; state.case_sensitive = case_sensitive; state.scanner = scanner; state.err = err; while (!state.queue.empty()) { PgfTextSpot spot = state.queue.top(); state.queue.pop(); if (spot.pos != state.spot.pos) { state.spot = spot; // skip leading spaces while (state.spot.byte_pos < state.sentence->size) { const uint8_t *ptr = (uint8_t *) &sentence->text[state.spot.byte_pos]; uint32_t ucs = pgf_utf8_decode(&ptr); if (!pgf_utf8_is_space(ucs)) break; state.spot.pos++; state.spot.byte_pos = ptr-(uint8_t *) sentence->text; } state.scanner->space(&spot,&state.spot,state.err); if (state.err->type != PGF_EXN_NONE) return; while (state.spot.byte_pos < state.sentence->size) { phrasetable_lookup_prefixes(&state, table, 1, sentence->size); if (state.err->type != PGF_EXN_NONE) return; if (state.last.pos > 0) { // We found at least one match. // The last range is yet to be reported. state.scanner->end_matches(&state.last, state.err); if (state.err->type != PGF_EXN_NONE) return; state.last.pos = 0; state.last.byte_pos = 0; break; } else { // No matches were found, try the next position if (!state.skipping) { while (!state.queue.empty() && state.queue.top().pos < state.spot.pos) { state.queue.pop(); } state.queue.push(state.spot); state.skipping = true; } const uint8_t *ptr = (uint8_t *) &state.sentence->text[state.spot.byte_pos]; uint32_t ucs = pgf_utf8_decode(&ptr); if (pgf_utf8_is_space(ucs)) { state.queue.push(state.spot); break; } state.spot.pos++; state.spot.byte_pos = ptr-(uint8_t *) state.sentence->text; } } finish_skipping(&state); if (state.err->type != PGF_EXN_NONE) return; state.spot = spot; } } } template PGF_INTERNAL PgfPhrasetable phrasetable_insert(PgfPhrasetable table, ref key, ref item) { if (table == 0) { auto items = vector>::alloc(1); items[0] = item; return Node>::new_node({.key=key,.n_items=1,.items=items}); } int cmp = compare_key(key, table->value.key); if (cmp < 0) { PgfPhrasetable left = phrasetable_insert(table->left, key, item); table = Node>::upd_node(table,left,table->right); return Node>::balanceL(table); } else if (cmp > 0) { PgfPhrasetable right = phrasetable_insert(table->right, key, item); table = Node>::upd_node(table, table->left, right); return Node>::balanceR(table); } else { PgfPhrasetable new_table = Node>::upd_node(table, table->left, table->right); auto items = new_table->value.items; if (new_table->value.n_items >= items.size()) { size_t new_len = get_next_padovan(new_table->value.n_items+1); items = items.realloc(new_len, new_table->txn_id); } items[new_table->value.n_items] = item; new_table->value.n_items++; new_table->value.items = items; return new_table; } } static int compare_key(ref symks1, ref symks2) { int res[2] = {0,0}; texticmp(&symks1->token, &symks2->token, res); if (res[0] != 0) return res[0]; return res[1]; } template PgfPhrasetable phrasetable_insert(PgfPhrasetable table, ref key, ref item); static int compare_key(ref lincat1, ref lincat2) { return textcmp(&lincat1->name, &lincat2->name); } template PGF_INTERNAL PgfPhrasetable phrasetable_insert(PgfPhrasetable table, ref key, ref item); template PGF_INTERNAL vector> phrasetable_lookup(PgfPhrasetable phrasetable, ref key, size_t *n_items); static int compare_key(ref ccat1, ref ccat2) { return ((int) ccat1->fid) - ((int) ccat2->fid); } template PgfPhrasetable phrasetable_insert(PgfPhrasetable table, ref key, ref item); template PGF_INTERNAL vector> phrasetable_lookup(PgfPhrasetable phrasetable, ref key, size_t *n_items); static int compare_key(ref symbind1, ref symbind2) { return 0; } template PgfPhrasetable phrasetable_insert(PgfPhrasetable table, ref key, ref item); template PGF_INTERNAL vector> phrasetable_lookup(PgfPhrasetable phrasetable, ref key, size_t *n_items); PGF_INTERNAL PgfEpsilontable epsilontable_insert(PgfEpsilontable table, ref lincat, PgfMetaId prev_fid, interval_t value, interval_t lin_idx, PgfMetaId fid, prob_t viterbi_prob, ref item, ref *pepsilon) { if (table == 0) { auto items = vector>::alloc(1); items[0] = item; PgfEpsilontable new_table = Node::new_node({.lincat=lincat, .prev_fid=prev_fid, .fid=fid, .value=value, .lin_idx=lin_idx, .viterbi_prob=viterbi_prob, .n_items=1, .items=items}); *pepsilon = ref::from_ptr(&new_table->value); return new_table; } int cmp = textcmp(&lincat->name, &table->value.lincat->name); if (cmp == 0) { cmp = ((int)fid) - ((int)table->value.fid); } if (cmp < 0) { PgfEpsilontable left = epsilontable_insert(table->left, lincat, prev_fid, value, lin_idx, fid, viterbi_prob, item, pepsilon); table = Node::upd_node(table,left,table->right); return Node::balanceL(table); } else if (cmp > 0) { PgfEpsilontable right = epsilontable_insert(table->right, lincat, prev_fid, value, lin_idx, fid, viterbi_prob, item, pepsilon); table = Node::upd_node(table, table->left, right); return Node::balanceR(table); } else { PgfEpsilontable new_table = Node::upd_node(table, table->left, table->right); auto items = table->value.items; if (table->value.n_items >= items.size()) { size_t new_len = get_next_padovan(table->value.n_items+1); items = items.realloc(new_len, table->txn_id); } items[table->value.n_items] = item; new_table->value.n_items++; new_table->value.items = items; *pepsilon = ref::from_ptr(&new_table->value); return new_table; } } PGF_INTERNAL void epsilontable_add(ref epsilon,ref item) { auto items = epsilon->items; if (epsilon->n_items >= items.size()) { size_t new_len = get_next_padovan(epsilon->n_items+1); items = items.realloc(new_len, PgfDB::get_txn_id()); } items[epsilon->n_items] = item; epsilon->n_items++; epsilon->items = items; } PGF_INTERNAL ref epsilontable_get(PgfEpsilontable table, PgfText *name, PgfMetaId fid) { if (table == 0) { return 0; } int cmp = textcmp(name, &table->value.lincat->name); if (cmp == 0) { cmp = ((int)fid) - ((int)table->value.fid); } if (cmp < 0) { return epsilontable_get(table->left,name,fid); } else if (cmp > 0) { return epsilontable_get(table->right,name,fid); } else { return ref::from_ptr(&table->value); } } PGF_INTERNAL void epsilontable_iter(PgfEpsilontable table, ref lincat, PgfMetaId prev_fid, std::function arg)> &f) { if (table == 0) return; int cmp = textcmp(&lincat->name, &table->value.lincat->name); if (cmp < 0) epsilontable_iter(table->left, lincat, prev_fid, f); else if (cmp > 0) epsilontable_iter(table->right, lincat, prev_fid, f); else { epsilontable_iter(table->left, lincat, prev_fid, f); if (table->value.prev_fid == prev_fid) { f(ref::from_ptr(&table->value)); } epsilontable_iter(table->right, lincat, prev_fid, f); } } PGF_INTERNAL void epsilontable_release(PgfEpsilontable table) { if (table == 0) return; epsilontable_release(table->left); epsilontable_release(table->right); for (size_t i = 0; i < table->value.n_items; i++) { PgfItem::release(table->value.items[i]); } vector>::release(table->value.items); Node::release(table); }