Разбор на пълния вход два пъти

За да постигна нечувствителни към регистъра инфикс оператори, използвайки OperatorPrecedenceParser, обработвам предварително входа, анализирайки го като текст, разделен от низови литерали. След това текстовата част се търси за инфикс оператори, които трябва да бъдат с главни букви (за да съответстват на оператора, както е известен на OPP). След това се извършва действителният анализ.

Въпросът ми е, могат ли и двете фази да бъдат комбинирани в един анализатор? опитах

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

но се проваля в края на входа, привидно очаквайки scalarExpr. Входът може да бъде анализиран от preprocess и scalarExpr независимо, така че предполагам, че това е проблем с 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 + "\"")) 

person Daniel    schedule 14.05.2013    source източник
comment
Вашите оператори толкова дълги ли са, че не можете просто да добавите всички комбинации от главни и малки букви, които ви интересуват, към OPP (напр. като използвате малка помощна функция)? Как добавяте операторите с главни букви по време на предварителната обработка, директно ли променяте входния буфер? Ако искате да анализирате входа два пъти, трябва да се върнете към началото или да създадете нов CharStream.   -  person Stephan Tolksdorf    schedule 15.05.2013
comment
Има пет такива оператора (може би повече в бъдеще), като най-дългият е 10 знака. preprocess връща нов низ с операторите с главни букви.   -  person Daniel    schedule 15.05.2013
comment
Актуализирах въпроса си с другите парсери. Току-що разбрах, че вероятно ми трябва >>= вместо >>., но типовете не работят, така че в момента съм объркан.   -  person Daniel    schedule 15.05.2013
comment
@StephanTolksdorf: Безопасно ли е да се каже, че трябва да извикам parse два пъти, веднъж за всеки анализатор? Няма начин да ги комбинирате?   -  person Daniel    schedule 15.05.2013
comment
Ако прочетох това правилно, preprocess е анализатор, който връща предварително обработения низ. Ако това е правилно, трябва да създадете нов CharStream за този низ и да стартирате втория анализатор на този CharStream, напр. с помощта на runParserOnString. Въпреки това, целият подход е доста грозен. Бихте ли могли да се измъкнете само с поддържането на разумните комбинации от главни и малки букви, т.е.   -  person Stephan Tolksdorf    schedule 15.05.2013
comment
Това е език за крайните потребители за изразяване на филтри за заявки към бази данни, така че донякъде очаквам чувствителността към малки и главни букви да ги спъне. Съгласен съм, че е грозно.   -  person Daniel    schedule 15.05.2013
comment
Вместо да трансформирате входа, можете също така да направите просто търсене с инвариантен регистър за всички срещания на ключовите думи във входа и след това да добавите OPP записи за всички комбинации от регистър, които намерите (които вероятно не са много). Това първоначално търсене не трябва да е прецизно, тъй като добавянето на надмножество от необходимите комбинации от главни и малки букви не е проблем.   -  person Stephan Tolksdorf    schedule 15.05.2013
comment
Това е интересна идея, но все пак изисква предварително анализиране на низа, за да се избегнат оператори в литералите, нали, например, или в свобода или смърт?   -  person Daniel    schedule 15.05.2013
comment
Ако първоначалното търсене открие твърде много ключови думи, напр. в литерали, това не би трябвало да е проблем за по-късния анализ, защото в най-лошия случай просто добавяте някои излишни комбинации от главни и малки букви към OPP. Това предполага, че имате фиксиран набор от оператори, напр. петте от вашата извадка.   -  person Stephan Tolksdorf    schedule 15.05.2013
comment
Това изглежда като разумен (и много по-чист) подход. Бих искал да публикуваш това като отговор, който ще приема. Благодаря за вашата помощ.   -  person Daniel    schedule 15.05.2013


Отговори (1)


Най-простият начин за синтактичен анализ на оператори без значение за главни и малки букви с OperatorPrecedenceParser на FParsec е да добавите дефиниции на оператори за всеки корпус, който искате да поддържате. Ако трябва да поддържате само кратки имена на оператори, като "и" или "или", можете просто да добавите всички възможни комбинации от главни и малки букви. Ако искате да използвате имена на оператори, които са твърде дълги за този подход, можете да помислите дали да поддържате само нормалните малки букви, т.е. малки букви, ГЛАВНИ, CamelCase и PascalCase. Когато искате да поддържате множество обвивки, обикновено е удобно да напишете помощна функция, която автоматично генерира всички необходими обвивки за вас от стандартна.

Ако имате дълги имена на оператори и наистина искате да поддържате всички малки букви, динамичната конфигурируемост на OperatorPrecedenceParser също позволява следния подход, който би трябвало да е по-лесен и по-ефективен от трансформирането на входа:

  1. Търсене във входа за всички нечувствителни към главни и малки букви срещания на поддържаните оператори. Това търсене не трябва да пропуска никакви събития, но не е проблем, ако открие фалшиви положителни резултати, ако напр. името на оператора се използва в име на функция или в низов литерал.
  2. Добавете всички уникални обвивки, които сте намерили в стъпка 1, към OperatorPrecedenceParser. (Обикновено няма да има много обвивки на един и същ оператор.)
  3. Анализирайте входа с конфигурирания OperatorPrecedenceParser.

Когато анализирате няколко входа, можете да запазите екземпляра OperatorPrecedenceParser наоколо и просто лениво да добавяте нови малки оператори, когато имате нужда от тях.

person Stephan Tolksdorf    schedule 15.05.2013