정규식 콜아웃

정규식 콜아웃 (Regular Expression Callouts)

AutoHotkey의 RegEx 콜아웃(정규식 매칭 도중 일시적으로 제어권을 스크립트에 넘기는 기능)의 문법과 사용법을 설명하는 문서예요.

출처: 문서

본문

RegEx 콜아웃(callout)은 정규식 패턴 매칭 도중에 일시적으로 제어권을 스크립트로 넘기는 수단을 제공해요. PCRE 표준 콜아웃 기능에 대한 자세한 정보는 pcre.txt를 참조해요.

RegEx 콜아웃은 현재 RegExMatchRegExReplace에서만 지원돼요.

목차 (Table of Contents)

  • Syntax (문법)
  • RegEx Callout Functions (RegEx 콜아웃 함수)
  • EventInfo
  • Auto-Callout (자동 콜아웃)
  • Remarks (주의 사항)

문법 (Syntax)

AutoHotkey에서 RegEx 콜아웃의 문법은 (?CNumber:Function)이에요. NumberFunction은 둘 다 선택 사항이에요. 콜론 ':'은 Function을 지정할 때만 허용되며, Number를 생략하면 생략할 수 있어요.

콜아웃 함수는 공식적으로 정의하거나, RegExMatch나 RegExReplace를 호출한 함수의 범위(지역 또는 전역) 안에서 변수에 할당함으로써 제공해야 해요. Function을 생략하면 기본값은 pcre_callout이에요. 변수를 찾지 못하거나 그 값이 함수 객체가 아니면 오류가 던져져요.

RegEx 콜아웃 함수 (RegEx Callout Functions)

*MyFunction*(Match, CalloutNumber, FoundPos, Haystack, NeedleRegEx)
{
    ...
}

RegEx 콜아웃 함수는 최대 5개의 매개변수를 정의할 수 있어요:

  • Match: 지금까지의 매치에 대한 정보를 담은 RegExMatchInfo 객체를 받아요.
  • CalloutNumber: RegEx 콜아웃의 Number를 받아요.
  • FoundPos: 현재 잠재적 매치의 위치를 받아요.
  • Haystack: RegExMatch 또는 RegExReplace에 전달된 Haystack을 받아요.
  • NeedleRegEx: RegExMatch 또는 RegExReplace에 전달된 NeedleRegEx를 받아요.

이 이름들은 단지 제안일 뿐이에요. 실제 이름은 달라질 수 있어요.

경고: 콜아웃 중 RegExReplace 또는 RegExMatch의 입력 매개변수를 변경하는 것은 지원되지 않으며 예측할 수 없는 동작을 일으킬 수 있어요.

RegEx 콜아웃 함수의 반환 값에 따라 패턴 매칭이 진행되거나 실패할 수 있어요:

  • 함수가 0을 반환하거나 숫자 값을 반환하지 않으면, 매칭이 평소대로 진행돼요.
  • 함수가 1 이상을 반환하면, 현재 지점에서 매칭이 실패하지만 다른 매칭 가능성의 테스트는 계속돼요.
  • 함수가 -1을 반환하면, 매칭이 중단돼요.
  • 함수가 -1보다 작은 값을 반환하면, 그것은 PCRE 오류 코드로 취급되고 매칭이 중단돼요. 이것은 RegExMatch와 RegExReplace가 예외를 던지게 하며, 예외 객체의 Extra 프로퍼티가 오류 코드를 담아요.

예를 들어:

Haystack := "The quick brown fox jumps over the lazy dog."
RegExMatch(Haystack, "i)(The) (\\w+)\\b(?CCallout)")
Callout(m, *) {
    MsgBox "m[0]=" m[0] "`nm[1]=" m[1] "`nm[2]=" m[2]
    return 1
}

위 예시에서 Callout은 RegEx 콜아웃 앞의 패턴 부분과 일치하는 각 하위 문자열에 대해 한 번씩 호출돼요. \bThe quic, The qui, The qu 같은 불완전한 단어가 매치에 들어가는 것을 제외하는 데 사용돼요.

콜아웃 중에 RegEx 함수의 입력 매개변수 중 하나라도 수정되면, 동작은 정의되지 않아요.

EventInfo

A_EventInfo를 통해 pcre_callout_block 구조체에 접근하면 추가 정보를 얻을 수 있어요.

version           := NumGet(A_EventInfo,  0, "Int")
callout_number    := NumGet(A_EventInfo,  4, "Int")
offset_vector     := NumGet(A_EventInfo,  8, "Ptr")
subject           := NumGet(A_EventInfo,  8 + A_PtrSize, "Ptr")
subject_length    := NumGet(A_EventInfo,  8 + A_PtrSize*2, "Int")
start_match       := NumGet(A_EventInfo, 12 + A_PtrSize*2, "Int")
current_position  := NumGet(A_EventInfo, 16 + A_PtrSize*2, "Int")
capture_top       := NumGet(A_EventInfo, 20 + A_PtrSize*2, "Int")
capture_last      := NumGet(A_EventInfo, 24 + A_PtrSize*2, "Int")
pad := A_PtrSize=8 ? 4 : 0  *; 64비트 데이터 정렬 보정.*
callout_data      := NumGet(A_EventInfo, 28 + pad + A_PtrSize*2, "Ptr")
pattern_position  := NumGet(A_EventInfo, 28 + pad + A_PtrSize*3, "Int")
next_item_length  := NumGet(A_EventInfo, 32 + pad + A_PtrSize*3, "Int")
if (version >= 2)
    mark   := StrGet(NumGet(A_EventInfo, 36 + pad + A_PtrSize*3, "Int"), "UTF-8")

자세한 내용은 pcre.txt, NumGetA_PtrSize를 참조해요.

자동 콜아웃 (Auto-Callout)

패턴의 옵션에 C를 포함하면 자동 콜아웃 모드가 활성화돼요. 이 모드에서 (?C255)와 동일한 RegEx 콜아웃이 패턴의 각 항목 앞에 삽입돼요. 예를 들어, 다음 템플릿은 정규식을 디버깅하는 데 사용될 수 있어요:

*; 자동 콜아웃 옵션 C로 RegExMatch 호출.*
RegExMatch("xxxabc123xyz", "C)abc.*xyz")

*; 기본 RegEx 콜아웃 함수 정의.*
pcre_callout(Match, CalloutNumber, FoundPos, Haystack, NeedleRegEx)
{
    *; 이 필드들의 설명은 pcre.txt 참조.*
    start_match       := NumGet(A_EventInfo, 12 + A_PtrSize*2, "Int")
    current_position  := NumGet(A_EventInfo, 16 + A_PtrSize*2, "Int")
    pad := A_PtrSize=8 ? 4 : 0
    pattern_position  := NumGet(A_EventInfo, 28 + pad + A_PtrSize*3, "Int")
    next_item_length  := NumGet(A_EventInfo, 32 + pad + A_PtrSize*3, "Int")

    *; >>현재 매치<<를 가리킴.*
    _HAYSTACK:=SubStr(Haystack, 1, start_match)
        . ">>" SubStr(Haystack, start_match + 1, current_position - start_match)
        . "<<" SubStr(Haystack, current_position + 1)
    
    *; >>다음에 평가될 항목<<을 가리킴.*
    _NEEDLE:=  SubStr(NeedleRegEx, 1, pattern_position)
        . ">>" SubStr(NeedleRegEx, pattern_position + 1, next_item_length)
        . "<<" SubStr(NeedleRegEx, pattern_position + 1 + next_item_length)
    
    ListVars
    *; 계속하려면 Pause를 누름.*
    Pause
}

주의 사항 (Remarks)

RegEx 콜아웃은 현재 유사 스레드(quasi-thread)에서 실행되지만, RegEx 콜아웃 함수가 반환된 후에는 A_EventInfo의 이전 값이 복원돼요.

PCRE는 어떤 경우 매치가 불가능하다고 판단하면 일찍 중단하도록 최적화돼 있어요. 그런 경우 모든 RegEx 콜아웃이 호출되게 하려면, 패턴의 시작 부분에 (*NO_START_OPT)를 지정해 이 최적화를 비활성화해야 할 수 있어요.

더 알아보기 (Learn more)