diff --git a/.gitignore b/.gitignore
index 92eb308d..c935d8c3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -74,6 +74,9 @@
/lttoolbox/lt-append
/lttoolbox/lsx-comp
/lttoolbox/lt-paradigm
+/lttoolbox/lt-invert
+/lttoolbox/lt-restrict
+/lttoolbox/lt-apply-acx
/python/Makefile
/python/Makefile.in
/python/lttoolbox.i
diff --git a/configure.ac b/configure.ac
index 1a329446..9a1da36b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,7 +2,7 @@ AC_PREREQ(2.52)
m4_define([PKG_VERSION_MAJOR], [3])
m4_define([PKG_VERSION_MINOR], [6])
-m4_define([PKG_VERSION_PATCH], [9])
+m4_define([PKG_VERSION_PATCH], [10])
AC_INIT([lttoolbox], [PKG_VERSION_MAJOR.PKG_VERSION_MINOR.PKG_VERSION_PATCH], [apertium-stuff@lists.sourceforge.net], [lttoolbox], [https://wiki.apertium.org/wiki/Lttoolbox])
diff --git a/lttoolbox/Makefile.am b/lttoolbox/Makefile.am
index 4f031cd0..ea4c7289 100644
--- a/lttoolbox/Makefile.am
+++ b/lttoolbox/Makefile.am
@@ -1,11 +1,11 @@
-h_sources = alphabet.h att_compiler.h buffer.h compiler.h compression.h \
+h_sources = acx.h alphabet.h att_compiler.h buffer.h cli.h compiler.h compression.h \
deserialiser.h entry_token.h expander.h file_utils.h fst_processor.h input_file.h lt_locale.h \
match_exe.h match_node.h match_state.h my_stdio.h node.h \
pattern_list.h regexp_compiler.h serialiser.h sorted_vector.h state.h string_utils.h \
transducer.h trans_exe.h xml_parse_util.h xml_walk_util.h exception.h tmx_compiler.h \
ustring.h sorted_vector.hpp
-cc_sources = alphabet.cc att_compiler.cc compiler.cc compression.cc entry_token.cc \
+cc_sources = acx.cc alphabet.cc att_compiler.cc cli.cc compiler.cc compression.cc entry_token.cc \
expander.cc file_utils.cc fst_processor.cc input_file.cc lt_locale.cc match_exe.cc \
match_node.cc match_state.cc node.cc pattern_list.cc \
regexp_compiler.cc sorted_vector.cc state.cc string_utils.cc transducer.cc \
@@ -14,7 +14,7 @@ cc_sources = alphabet.cc att_compiler.cc compiler.cc compression.cc entry_token.
library_includedir = $(includedir)/$(PACKAGE_NAME)-$(VERSION_API)/$(PACKAGE_NAME)
library_include_HEADERS = $(h_sources)
-bin_PROGRAMS = lt-comp lt-proc lt-expand lt-paradigm lt-tmxcomp lt-tmxproc lt-print lt-trim lt-append lsx-comp
+bin_PROGRAMS = lt-comp lt-proc lt-expand lt-paradigm lt-tmxcomp lt-tmxproc lt-print lt-trim lt-append lsx-comp lt-invert lt-restrict lt-apply-acx
instdir = lttoolbox
lib_LTLIBRARIES= liblttoolbox3.la
@@ -41,6 +41,9 @@ lt_paradigm_SOURCES = lt_paradigm.cc
lt_tmxcomp_SOURCES = lt_tmxcomp.cc
lt_tmxproc_SOURCES = lt_tmxproc.cc
lsx_comp_SOURCES = lt_comp.cc
+lt_invert_SOURCES = lt_invert.cc
+lt_restrict_SOURCES = lt_restrict.cc
+lt_apply_acx_SOURCES = lt_apply_acx.cc
#lt-validate-dictionary: Makefile.am validate-header.sh
# @echo "Creating lt-validate-dictionary script"
diff --git a/lttoolbox/acx.cc b/lttoolbox/acx.cc
new file mode 100644
index 00000000..8de7a7ee
--- /dev/null
+++ b/lttoolbox/acx.cc
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2022 Apertium
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see .
+ */
+#include
+#include
+
+const xmlChar* CHAR_NODE = (const xmlChar*)"char";
+const xmlChar* EQUIV_NODE = (const xmlChar*)"equiv-char";
+const char* VALUE_ATTR = "value";
+
+int32_t get_val(xmlNode* node)
+{
+ UString s = getattr(node, VALUE_ATTR);
+ if (s.empty()) {
+ error_and_die(node, "Missing value attribute.");
+ }
+ std::vector v;
+ ustring_to_vec32(s, v);
+ if (v.size() > 1) {
+ error_and_die(node, "Expected a single character in value attribute, but found %d.", v.size());
+ }
+ return v[0];
+}
+
+std::map> readACX(const char* file)
+{
+ std::map> acx;
+ xmlNode* top_node = load_xml(file);
+ for (auto char_node : children(top_node)) {
+ if (!xmlStrEqual(char_node->name, CHAR_NODE)) {
+ error_and_die(char_node, "Expected but found <%s>.",
+ (const char*)char_node->name);
+ }
+ int32_t key = get_val(char_node);
+ sorted_vector vec;
+ for (auto equiv_node : children(char_node)) {
+ if (!xmlStrEqual(equiv_node->name, EQUIV_NODE)) {
+ error_and_die(char_node, "Expected but found <%s>.",
+ (const char*)equiv_node->name);
+ }
+ vec.insert(get_val(equiv_node));
+ }
+ if (!vec.empty()) {
+ acx.insert(std::make_pair(key, vec));
+ }
+ }
+ return acx;
+}
diff --git a/lttoolbox/acx.h b/lttoolbox/acx.h
new file mode 100644
index 00000000..3f7223d7
--- /dev/null
+++ b/lttoolbox/acx.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 Apertium
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see .
+ */
+#ifndef _ACXPARSEUTIL_
+#define _ACXPARSEUTIL_
+
+#include
+#include