LIMA
Libre Multilingual Analyzer — C++ API
Loading...
Searching...
No Matches
mwt_dict.cpp
Go to the documentation of this file.
1// Copyright 2021 CEA LIST
2// SPDX-FileCopyrightText: 2026 CEA LIST <gael.de-chalendar@cea.fr>
3//
4// SPDX-License-Identifier: MIT
5
6#include "mwt_dict.h"
7
8#include <fstream>
9#include <iostream>
10#include <map>
11#include <stdexcept>
12
13#include "line.h"
14
15using namespace std;
16
17namespace deeplima
18{
19namespace CoNLLU
20{
21
22size_t extract_mwt_dict(const vector<string>& conllu_files, ostream& out)
23{
24 // surface -> (expansion -> count)
25 map<string, map<vector<string>, size_t>> dict;
26
27 for (const auto& fn : conllu_files)
28 {
29 ifstream input(fn);
30 if (!input.is_open())
31 {
32 throw runtime_error("extract_mwt_dict: can't open \"" + fn + "\"");
33 }
34
35 string line;
36 while (getline(input, line))
37 {
38 if (line.empty() || line[0] == '#')
39 {
40 continue;
41 }
42
43 CoNLLULine l(line);
44 if (!l.is_token_line() || !l.idx().is_multiword())
45 {
46 continue;
47 }
48
49 const size_t n_sub = size_t(l.idx()._last - l.idx()._first + 1);
50 const string surface = l.form();
51
52 // Collect the forms of the (n_sub) real-word lines that compose this MWT.
53 // They immediately follow the range line in UD; bail out cleanly on any
54 // malformed span rather than over-consuming into the next sentence.
55 vector<string> expansion;
56 expansion.reserve(n_sub);
57 size_t collected = 0;
58 while (collected < n_sub && getline(input, line))
59 {
60 if (line.empty())
61 {
62 break; // sentence boundary reached too early: malformed span
63 }
64 if (line[0] == '#')
65 {
66 continue;
67 }
68 CoNLLULine w(line);
69 if (!w.is_token_line() || w.idx().is_empty())
70 {
71 continue;
72 }
73 if (w.idx().is_multiword())
74 {
75 break; // a nested range here means the corpus is malformed
76 }
77 expansion.push_back(w.form());
78 ++collected;
79 }
80
81 if (collected == n_sub && !surface.empty() && expansion.size() >= 2)
82 {
83 dict[surface][expansion]++;
84 }
85 }
86 }
87
88 size_t written = 0;
89 for (const auto& [surface, expansions] : dict)
90 {
91 const vector<string>* best = nullptr;
92 size_t best_count = 0;
93 size_t total = 0;
94 for (const auto& [exp, count] : expansions)
95 {
96 total += count;
97 if (count > best_count)
98 {
99 best_count = count;
100 best = &exp;
101 }
102 }
103 if (nullptr == best)
104 {
105 continue;
106 }
107
108 if (expansions.size() > 1)
109 {
110 cerr << "extract_mwt_dict: ambiguous surface \"" << surface << "\" ("
111 << expansions.size() << " expansions); keeping the most frequent ("
112 << best_count << "/" << total << ")." << endl;
113 }
114
115 out << surface << "\t" << best_count;
116 for (const auto& w : *best)
117 {
118 out << "\t" << w;
119 }
120 out << "\n";
121 ++written;
122 }
123
124 return written;
125}
126
127} // namespace CoNLLU
128} // namespace deeplima
const std::string & form() const
Definition line.h:153
const idx_t & idx() const
Definition line.h:148
bool is_token_line() const
Definition line.h:128
size_t extract_mwt_dict(const vector< string > &conllu_files, ostream &out)
Definition mwt_dict.cpp:22
STL namespace.
base_int_t _first
Definition line.h:30
bool is_multiword() const
Definition line.h:48
bool is_empty() const
Definition line.h:53
base_int_t _last
Definition line.h:31