2013-05-14 3 views
2

OperatorPrecedenceParser을 사용하여 대소 문자를 구별하지 않는 중절 연산자를 얻으려면 입력을 사전 처리하여 문자열 리터럴로 구분 된 텍스트로 구문 분석합니다. 텍스트 부분은 대문자로 표기 할 필요가있는 삽입 연산자를 검색합니다 (OPP에 알려진 연산자와 일치하도록). 실제 구문 분석이 수행됩니다.전체 입력을 두 번 파싱하기

제 질문은 두 단계를 단일 구문 분석기로 결합 할 수 있습니까? 나는

// preprocess: Parser<string,_> 
// scalarExpr: Parser<ScalarExpr,_> 
let filter = (preprocess .>> eof) >>. (scalarExpr .>> eof) 

을 시도했지만 겉보기 scalarExpr를 기대하고, 입력의 끝에 실패합니다. 입력은 preprocessscalarExpr에 의해 개별적으로 파싱 될 수 있으므로 eof의 문제 일 것이라고 추측합니다.하지만 올바르게 이해할 수없는 것 같습니다. 이것이 가능한가?

다음은 참조 할 수있는 다른 파서입니다.

let stringLiteral = 
    let subString = manySatisfy ((<>) '"') 
    let escapedQuote = stringReturn "\"\"" "\"" 
    (between (pstring "\"") (pstring "\"") (stringsSepBy subString escapedQuote)) 

let canonicalizeKeywords = 
    let keywords = 
    [ 
     "OR" 
     "AND" 
     "CONTAINS" 
     "STARTSWITH" 
     "ENDSWITH" 
    ] 
    let caseInsensitiveKeywords = HashSet(keywords, StringComparer.InvariantCultureIgnoreCase) 
    fun text -> 
    let re = Regex(@"([\w][\w']*\w)") 
    re.Replace(text, MatchEvaluator(fun m -> 
     if caseInsensitiveKeywords.Contains(m.Value) then m.Value.ToUpperInvariant() 
     else m.Value)) 

let preprocess = 
    stringsSepBy 
    ((manySatisfy ((<>) '"')) |>> canonicalizeKeywords) 
    (stringLiteral |>> (fun s -> "\"" + s + "\"")) 
+0

운영자가 너무 오래되어 관심있는 모든 대소 문자 조합을 OPP에 추가 할 수 없습니다 (예 : 약간의 도우미 기능 사용). 전처리 중에 연산자를 어떻게 대문자로합니까? 입력 버퍼를 직접 변경합니까? 입력을 두 번 파싱하려면 시작 부분으로 되돌아 가거나 새 CharStream을 만들어야합니다. –

+0

다섯 가지 연산자가 있습니다. (아마도 앞으로는 더 많을 것입니다.) 가장 긴 문자는 10 자입니다. 'preprocess'는 대문자로 새로운 문자열을 반환합니다. – Daniel

+0

다른 파서로 내 질문을 업데이트했습니다. 방금''.''대신''=''이 필요하다는 것을 알았지 만 유형이 제대로 작동하지 않으므로 지금 당황 스럽습니다. – Daniel

답변

1

FParsec의 OperatorPrecedenceParser와 대소 문자를 구분 연산자를 구문 분석하는 가장 간단한 방법은 지원하려는 모든 케이스에 대한 운영자의 정의를 추가하는 것입니다. "and"또는 "or"와 같이 짧은 운영자 이름 만 지원해야하는 경우 가능한 모든 대소 문자 조합을 간단하게 추가 할 수 있습니다. 이 접근법에 비해 너무 긴 연산자 이름을 사용하려면 소문자, 대문자, 대문자, camelCase 및 PascalCase 만 지원하면됩니다. 여러 개의 케이스를 지원하려면 일반적으로 표준 케이스에서 필요한 모든 케이스를 자동으로 생성하는 도우미 함수를 작성하는 것이 편리합니다.

  1. 검색 : 당신이 긴 운영자 이름을 가지고 있고 당신이 정말로 모든 케이스를 지원하려면

    OperatorPrecedenceParser의 동적 구성 기능은 입력을 변환하는 것보다 더 쉽고 효율적으로해야 다음 접근을 허용 지원되는 연산자의 대소 문자를 구분하지 않는 입력에 대한 입력 이 검색은 어떤 발생도 놓치지 않아야합니다. 그러나 예를 들어 다음과 같은 거짓 긍정을 발견하면 아무런 문제가 없습니다. 연산자 이름은 함수 이름 내에서 또는 문자열 리터럴 내에서 사용됩니다.

  2. 단계 1에서 찾은 모든 고유 케이싱을 OperatorPrecedenceParser에 추가하십시오. (대개 동일한 연산자의 케이스는 많지 않습니다.)
  3. OperatorPrecedenceParser으로 입력을 구문 분석하십시오.

여러 입력을 구문 분석 할 때, 당신은 주위 OperatorPrecedenceParser 인스턴스를 유지할 수 있습니다 당신이 그들을 필요로하는 단지 느리게 새로운 사업자 케이스를 추가합니다.