Difference between revisions of "Lttoolbox API"

From Apertium
Jump to navigation Jump to search
Line 202: Line 202:
   
 
==Real-world examples===
 
==Real-world examples===
  +
* apertium-transfer uses lttoolbox from C++
+
* apertium-transfer uses lttoolbox from C++ to look up words in the bidix
 
** http://apertium.svn.sourceforge.net/viewvc/apertium/trunk/apertium/apertium/transfer.cc?view=markup
 
** http://apertium.svn.sourceforge.net/viewvc/apertium/trunk/apertium/apertium/transfer.cc?view=markup
  +
* libvoikko uses lttoolbox from C++, and also makes a Python module:
+
* libvoikko uses lttoolbox from C++ to analyse words, and also makes a Python module:
 
** http://sourceforge.net/apps/trac/voikko/browser/trunk/libvoikko/src/morphology/LttoolboxAnalyzer.cpp
 
** http://sourceforge.net/apps/trac/voikko/browser/trunk/libvoikko/src/morphology/LttoolboxAnalyzer.cpp
 
** http://sourceforge.net/apps/trac/voikko/browser/trunk/libvoikko/src/morphology/interface.cpp
 
** http://sourceforge.net/apps/trac/voikko/browser/trunk/libvoikko/src/morphology/interface.cpp
 
** http://sourceforge.net/apps/trac/voikko/browser/trunk/libvoikko/python/libvoikko.py
 
** http://sourceforge.net/apps/trac/voikko/browser/trunk/libvoikko/python/libvoikko.py
  +
   
   

Revision as of 10:48, 17 August 2011

You can use lttoolbox as a library in C++ by with #include <lttoolbox/fst_processor.h>, and with a bit more work, you can also use it from Python.

Using as a library in C++

For this example you will need two files, test.dix:

<dictionary>
  <alphabet/>
  <sdefs>
    <sdef n="n"/>
    <sdef n="pl"/>
  </sdefs>
  <section id="main" type="standard">
    <e><p><l>cars</l><r>car<s n="n"/><s n="pl"/></r></p></e>
  </section>
</dictionary>

And test.cc:

#include <stdio.h>
#include <string>
#include <iostream>

#include <lttoolbox/fst_processor.h>
#include <lttoolbox/ltstr.h>

using namespace std;

FSTProcessor fstp;

int main(int argc, char **argv)
{
	if(argc < 2) { 
		wcout << L"Please specify a transducer" << endl;
		exit(-1);
	}

	FILE *t_rl = fopen(argv[1], "r");

	fstp.load(t_rl);
	fclose(t_rl);
	fstp.initBiltrans();

	wstring input = L"^car<n><pl>$";
	wstring trad = fstp.biltrans(input);

	wcout << input << L" --> " << trad << endl;

	return 0;
}

Compile both of the files:

$ lt-comp rl test.dix test_rl.bin
main@standard 6 5

$ g++ -Wall test.cc -o test -llttoolbox3

And then test:

$ ./test test_rl.bin 
^car<n><pl>$ --> ^cars$

Using as a module from Python

(This example is based on how libvoikko uses lttoolbox for analysis)

Define the following files:

ltpy.h:

#include <lttoolbox/fst_processor.h>

class Analyser {
	public:
		Analyser(const std::string & directoryName) throw(exception);
		wstring analyse(wstring const &word);
	private:
		FSTProcessor fst;
};

ltpy.cc:

#include "ltpy.hpp"
#include <lttoolbox/fst_processor.h>

using namespace std;

Analyser::Analyser(const string & analyserpath) throw(exception) {
	cerr << "analyser:  " << analyserpath << endl;
	FILE * file = fopen(analyserpath.c_str(), "r");
	if (!file) {
		cerr << "Couldn't open analyser file " << analyserpath << endl;
		//throw LoadException("Couldn't open analyser file");
		throw exception();
	}
	fst.load(file);
	fclose(file);
	fst.setCaseSensitiveMode(false);
	fst.setDictionaryCaseMode(true);
	fst.initBiltrans();
}
    
wstring Analyser::analyse(wstring const &word) {
	pair <wstring,int> analysis = fst.biltransWithQueue(word, false);
	// or fst.biltrans, which just returns the string. The 'false'
	// means no ^ or $ in input/output
	if (analysis.second == 0) {
		return analysis.first;
	}
	else {
		// a partial match:
		return L"@"+analysis.first;
	}
}

extern "C" wstring * analyse(Analyser * a, const wchar_t * word) {
	// TODO: there must be a way to just send in a wstring in the first p
	size_t wlen = wcslen(word);
	wstring inputString = L"";
	for (size_t i = 0; i < wlen; i++) {
		inputString.append(1, word[i]);
	}
	wstring * out = new wstring(a->analyse(inputString));
	return out;
}

extern "C" Analyser * init(const char ** error, const char * path) {
	Analyser * a = 0;
	try {
		a = new Analyser(path);
		
	}
	catch (exception & e) {
		delete a;
		a = 0;
		*error = e.what();
		return 0;
	}
	*error = 0;
	return a;
}

extern "C" void terminate(Analyser * a) {
	delete a;
}

lt.py:

# -*- coding: utf-8 -*-
from ctypes import byref, CDLL, c_char_p, c_wchar_p, c_void_p, POINTER

class FST(object):
	def __init__(self, libpath, fstpath):
		self.__lib = CDLL(libpath)

		self.__lib.init.argtypes = [POINTER(c_char_p), c_char_p]
		self.__lib.init.restype = c_void_p

		self.__lib.terminate.argtypes = [c_void_p]
		self.__lib.terminate.restype = None

		self.__lib.analyse.argtypes = [c_void_p, c_wchar_p]
		self.__lib.analyse.restype = POINTER(c_wchar_p)

		error = c_char_p()
		self.__handle = self.__lib.init(byref(error), fstpath)
		if error.value != None:
			self.__handle = 0
			raise Exception(u"Initialization of fst failed: " + unicode(error.value, "UTF-8"))
	
	def __del__(self):
		if (self.__handle != 0):
			self.__handle = 0
			class DummyLib:
				def __getattr__(obj, name):
					raise Exception("Attempt to use library after terminate() was called")
			self.__lib = DummyLib()
		
	def analyse(self, word):
		analyses = self.__lib.analyse(self.__handle, word)
		return analyses.contents.value

Compile the C++ files so you get a file like ltpy.so, then use it like:

import lt
fst = lt.FST(".libs/ltpy.so", "analyser.bin")
print fst.analyse(u"cars") # should print car<n><pl>

Real-world examples=