Lexilla |
최종 편집 21 April 2021 NH
렉실라는 신틸라용 어휘분석기 라이브러리이다. 정적라이브러로서 어플리케이션에 링크되어 들어가거나 공유 라이브러리로서 실행시간에 적재된다.
렉실라는 화면과 상호작용하지 않으므로 특별하게 GUI 툴킷에 대하여 컴파일할 필요가 없다. 그러므로 서로 다른 구이 툴킷을 사용하더라도 어플리케이션에서 공유하여 사용할 수 있다. 어떤 상황에서는 어플리케이션마다 따로 적용하기 위해 32-비트와 64-비트 버전 모두가 필요할 수 있다.
공유 라이브러는 서로 다른 확장이 적용된다: 리눅스라면 .so가 사용되고, macOS는 .dylib이 사용되며, 윈도우즈에서는 .DLL이 사용된다.
어플리케이션에 사용될 함수들이 렉실라에 정의되어 있다. 다음 함수들이 제공되는 라이브러리를 사용하면 렉실라를 대체하거나 렉실라의 기능을 넘어서는 새로운 어휘분석기를 추가할 수 있다.
렉실라 프로토콜은 외부 어휘분석기 프로토콜의 상위집합으로서 공유 라이브러리로부터 반출된 함수들이 다음과 같이 정의되어 있다:
int GetLexerCount()
void GetLexerName(unsigned int index, char *name, int buflength)
LexerFactoryFunction GetLexerFactory(unsigned int index)
ILexer5 *CreateLexer(const char *name)
const char *LexerNameFromID(int identifier)
const char *GetLibraryPropertyNames()
void SetLibraryProperty(const char *key, const char *value)
const char *GetNameSpace()
ILexer5는 실틸라에서 include/ILexer.h에 정의되어 있다. 어휘분석기가 제공하는 인터페이스로서 신틸라가 호출한다. 많은 클라이언트는 실제로는 ILexer5에 메쏘드를 요청하지 않는다 - 그저 CreateLexer로부터 반환값을 받아서 그것을 신틸라에 끼워 넣기만 하면 머신 포인터 (void *)로 취급할 수 있다.
LexerFactoryFunction는 인자가 없는 ILexer5 *이다:
ILexer5 *(*LexerFactoryFunction)()
를 돌려주는 함수로 정의된다. 그러나 대부분의 클라이언트는 이것을 무시해도 된다.
렉실라 프로토콜은 이전의 어휘분석기 프로토콜의 상위집합이다. 이전에는 앞의 세개의 (GetLexerCount, GetLexerName, GetLexerFactory) 함수가 정의되어 있으므로 이전 프로토콜만 지원한다면 렉실라를 어플리케이션에서 적재할 수 있다. GetLexerFactory는 이제 거의 사용되지 않는다. CreateLexer를 호출하는 것이 더 쉽기 때문이다.
CreateLexer는 메인 호출로서 특정 언어에 대하여 어휘분석기를 생성한다. 그러면 SCI_SETILEXER를 호출하여 어휘분석기를 돌려받아 현재 어휘분석기로 신틸라에 설정할 수 있다.
LexerNameFromID는 선택적 함수로서 어휘분석기 식별자의 이름을 돌려준다. LexerNameFromID(SCLEX_CPP) → "cpp"
. 이것은 일시적으로 허용된다. 어플리케이션이 렉실라를 사용하도록 쉽게 전환시키기 위함이다. 어플리케이션은 ID 대신에 어휘분석기 이름을 사용해야 한다. 이 함수는 추천되지 않는다. 컴파일러에서 경고를 보여주기도 하며, 미래의 렉실라 버전에서는 제거될 것이다.
SetLibraryProperty와 GetLibraryPropertyNames는 다른 메소드를 호출하기 전에 먼저 라이브러리가 초기화를 요구하는 경우에 정의할 수 있는 선택적 함수이다. 예를 들어, 어휘분석기 라이브러리가 언어 정의를 XML 파일로부터 읽는다면 SetLibraryProperty("definitions.directory", "/usr/share/xeditor/language-definitions")
과 같이 이 파일들이 들어 있는 디렉토리를 먼저 설정해야 CreateLexer를 호출할 수 있다. 라이브러리에 SetLibraryProperty가 구현되어 있다면 GetLibraryPropertyNames로 특성 이름들을 제공할 수 있다. 그러면 어플리케이션에서 이를 사용하여 환경구성 파일 특성 이름이나 옵션 대화상자에 사용자 인터페이스 요소들을 정의할 수 있다.
GetNameSpace는 이름공간을 문자열로 돌려주는 선택적 함수이다. 이름은 같은데 제공자는 다른 어휘분석기를 구별하는데 사용할 수 있다. Lexilla와 XMLLexer는 둘 다 "cpp" 어휘분석기를 제공한다. "cpp"만 요청하더라도 어느 쪽이든 만족한다. 그러나 "xmllexers.cpp"가 더 명료하게 XMLLexer로부터 "cpp" 어휘분석기를 참조한다.
렉실라를 사용하려면 먼저 내려받아 빌드해야 한다.
렉실라는 빌드하려면 신틸라 헤더를 요구한다. 그리고 "scintilla"라는 이름의 디렉토리를 기대한다. 여기에 신탈라 Scintilla 5+ 사본이 보통 "lexilla"라고 부르는 최상위 디렉토리에 같은 레벨로 존재해야 한다.
Lexing ...
"를 보여주며 에러가 나면 진단 결과를 화면에 보여준다. 보통 실제와 예상 사이의 차이를 결과로 보여준다:C:\u\hg\lexilla\test\examples\python\x.py:1: is different
RunTest.sh / RunTest.bat 스크립트가 scripts 디렉토리에 있으므로 렉실라를 빌드한 다음 테스트를 실행해 볼 수 있다. 둘 다 MSVC가 아니라 gcc/clang를 사용한다.
Microsoft Visual C++ 그리고 Xcode 프로젝트를 사용하여 렉실라를 빌드할 수 있다. Visual C++이라면 src/Lexilla.vcxproj로, Xcode는 src/Lexilla/Lexilla.xcodeproj로 빌드한다. 또 Visual C++로는 test/TestLexers.vcxproj로 테스트 모둠을 빌드할 수 있다.
C와 C++로 렉실라를 사용하는 방법은 lexilla/include/Lexilla.h에 정의되어 있다.
C++은 scintilla/include/ILexer.h를 먼저 포함하고 다음에 Lexilla.h를 포함해야 한다. ILexer5
유형을 사용하듯이 말이다.
C에서는 ILexer.h를 포함시키면 안된다. C가 그 헤더를 이해하지 못하며 C에서는 ILexer5*
대신에 void*
를 사용해야 하기 때문이다.
많은 어플리케이션에 대하여 렉실라 처리는 렉실라 라이브러리를 적재하고, 어휘분석기를 생성하고 신틸라에 있는 그 어휘분석기를 사용하는 것이다. 어플리케이션은 렉실라 프로토콜을 지원하는 라이브러리나 렉실라를 찾기 위하여 그의 위치를 정의할 필요가 있다. 또한 특정 어휘분석기를 어떻게 요청할 지 정의할 필요도 있다. 파일 확장자와 어휘분석기 이름을 짝짓는 것과 같이 말이다.
렉실라에 접근하기 위한 C 예제는 lexilla/examples/CheckLexilla에 제공된다. make로 빌드하고 make check로 실행하자.
C++ 모듈로, LexillaAccess.cxx / LexillaAccess.h를 lexilla/access에 제공한다. 그 자체로 만족한다면 이것을 어플리케이션 안에 컴파일해 넣으면 된다. 그렇지 않고 어플리케이션이 (코드 서명을 확인하는 것 같이) 추가 요구사항이 있으면 소스 코드를 어플리케이션 안으로 복사해 넣어서 맞춤 재단할 수 있다. SciTE는 LexillaAccess를 사용한다.
LexillaAccess는 Lexilla 프로토콜을 구현하고 있는 여러 공유 라이브러리를 한 번에 적재할 수 있도록 지원한다.
Qt에서는 위와 같이 LexillaAccess를 사용하거나 Qt의 QLibrary 클래스를 사용한다. Scintilla API를 호출하도록 정의된 'Call'을 사용하면 된다.
#if _WIN32
typedef void *(__stdcall *CreateLexerFn)(const char *name);
#else
typedef void *(*CreateLexerFn)(const char *name);
#endif
QFunctionPointer fn = QLibrary::resolve("lexilla", "CreateLexer");
void *lexCpp = ((CreateLexerFn)fn)("cpp");
Call(SCI_SETILEXER, 0, (sptr_t)(void *)lexCpp);
어플리케이션은 라이브러리가 제공하는 어휘분석기들을 알아낼 수 있다. 먼저 GetLexerCount를 호출하여 라이브러리 안에 구현된 어휘분석기의 갯수를 세고 다음에 GetLexerName을 호출하여 0부터 GetLexerCount()-1
까지 회돌이하면 된다.
어플리케이션은 SetLibraryProperty를 호출하면 라이브러리에 특성을 설정할 수도 있다. 이것은 GetLexerCount이나 CreateLexer를 호출하기 전에 먼저 초기화 하기 위해 필요할 수 있다. 특성 이름의 모음은 GetLibraryPropertyNames으로 얻을 수 있다. 문자열 포인터를 돌려주는데 그 안에 특성 이름들이 '\n'으로 구분되어 들어있다. 사용자 인터페이스와 환경구성 파일 안에 특성을 어떻게 정의하고 저장할 지는 어플리케이션에 달려 있다.
렉실라를 수정하거나 아니면 새로 라이브러리를 만들어서 렉실라를 대체하거나 거기에 덧붙일 수 있다.
신틸라는 어휘분석기 라이브러리가 제공하는 어휘분석기가 렉실라와 기능이 같다면 이를 사용할 수 있다. 신틸라는 렉실라가 제공하는 어휘분석기들을 이것으로 대체하거나 더 추가해 사용할 수 있다. 어휘분석기 라이브러리를 초기화하려면, SetLibraryProperty(const char *key, const char *value)
를 선택적으로 구현할 수도 있다. 예를 들어, XML 기반의 정의를 사용하는 어휘분석기 라이브러리에 그러한 정의를 검색할 디렉토리를 제공할 수 있다. 어휘분석기 라이브러리는 자신이 이해하지 못하는 특성들을 무시해야 한다. 어휘분석기 라이브러리가 지원하는 특성 집합은 선택적인 const char *GetLibraryPropertyNames()
함수로 지정되며 특성 이름은 '\n'으로 분리된다.
어휘분석기와 그 안에 포함된 어휘분석기는 lexilla/test에 있는 TestLexers 프로그램으로 테스트할 수 있다. 빌드하기와 TestLexers 사용법에 관한 더 자세한 정보는 lexilla/test/README를 읽어보자.
렉실라 프로토콜과 호환되는 공유 라이브러리에 들어가는 간단한 어휘분석기의 예제는 lexilla/examples/SimpleLexer에서 볼 수 있다. C++로 구현되어 있다. make로 빌드하고 make check하여 CheckLexilla를 실행하여 점검하자.