저는 광산 프로젝트에 특정 도메인 특정 언어를 구현하는 파서를 구현하고 있습니다.OperatorPrecedenceParser가 선택적 (opt) 식을 구문 분석하게 만들기
나는 어려움을 겪고있는 한 가지 측면은 전체식이 선택 사항이되도록 식 (FParsec의 OperatorPrecedenceParser를 사용하여 구현 됨)을 만드는 것입니다.
필자는 파서 OPP를 그물 주변의 많은 예제와 거의 같은 방식으로 구현했습니다. 나는 또한 공백이 라인 주석의 끝 부분에 소비되는 곳을 이동하려고 시도했다. 내 시도 중 하나도 봇의 경우에는 작동하지 않는 것 같습니다 (표현식과 끝의 주석이 모두 선택 사항 임)
특히 (아래 예에서 구현 된 것처럼) 다음 구문을 성공적으로 파싱하려고합니다.
KeyValue: expression # comment
KeyValue:
KeyValue: # comment
여기서 expression은 선택 사항이며 표현식 다음에 선택적 주석이 있습니다. "KeyValue :"는이 예제에서는 하드 코딩되었지만, 주 파서 코드에서는 식별자입니다.
N.B. 아래 샘플을보십시오. 나는 2 개의 부동 소수점을 더한 간단한 삽입을 구문 분석하는 최소한의 표현식을 구현했다. 전체 구문 분석기는 훨씬 더 많은 기능을 구현합니다.
N.B.2. 표현식은 :과 표현식 사이에 1 개의 공백이 있어야합니다. # 주석 문자는 표현식에 전혀 표시되지 않습니다.
어떻게 표현식을 '선택적'으로 만들 수 있습니까? 아래의 eKeyValue FParsec Parser 유형을 참조하십시오.
아래 예제에는 6 가지 사례가 들어 있습니다. 모든 경우가 효과가 있습니다. 나는 조건의 끝 부분에 wsBeforeEOL을 추가하려고 시도했다. 표현식을 선택적으로 사용 (opt)했지만 아무 것도 작동하지 않는 것 같다. OPP는 항상 소비되고 결코 실패하지 않는 것처럼 보입니다.
아래의 샘플 프로그램의 출력은 보이는 같은 :
은 샘플 프로그램입니다Test1 - Failure:
Error in Test1 - No Expression but comment: Ln: 1 Col: 15
KeyName: # No Expression but comment
^
Expecting: floating-point number or '('
Test2 - Success: Key (Some (Arithmetic (Number 2.0,Plus,Number 2.0)))
Test3 - Success: Key (Some (Arithmetic (Number 3.0,Plus,Number 3.0)))
Test4 - Success: Key (Some (Arithmetic (Number 3.0,Plus,Number 4.0)))
Test5 - Success: Key (Some (Arithmetic (Number 4.0,Plus,Number 4.0)))
Test6 - Success: Key null
Press any key to continue . . .
(위의 테스트 케이스는 main() 함수에 있습니다
open FParsec
// Placeholder for state...
type UserState =
{
dummy: int
}
with
static member Create() = {dummy = -1}
type Operator =
| Plus
type Expression =
| Number of float
| Arithmetic of Expression * Operator * Expression // Composes 2 primatives
type Statement =
| Key of Expression option // Optional expression name
// very simple parsers which handles a simple string on one line.
// White space handling
let isBlank = fun c -> c = ' ' || c = '\t'
let ws1 = skipMany1SatisfyL isBlank "whitespace"
let ws = skipManySatisfy isBlank
let comment = pstring "#" >>. skipRestOfLine false
let wsBeforeEOL = skipManySatisfy isBlank >>. optional comment
// Parse a number
let sNumber = pfloat .>> wsBeforeEOL |>> Number // Test wsExpression ending
// The expression reference
let expression, expressionRef = createParserForwardedToRef()
let expressionFragment = choice [sNumber] //;sBool;sNull;sString;sIdentifier]
let bracketedExpressionFragment = between (pstring "(" .>> ws) (pstring ")" .>> ws) expression
// The parser for addition only
let oppa = new OperatorPrecedenceParser<Expression, unit, UserState>()
let parithmetic = oppa.ExpressionParser
//oppa.TermParser <- (expressionFragment .>> wsBeforeEOL) <|> (bracketedExpressionFragment .>> wsBeforeEOL)
oppa.TermParser <- choice[expressionFragment;bracketedExpressionFragment]
oppa.AddOperator(InfixOperator("+", ws, 1, Associativity.Left, fun x y -> Arithmetic(x, Plus, y)))
expressionRef := oppa.ExpressionParser
// *** HERE: Define the Key, with optional expression, which must have at lease 1 WS,, then followed by wsBeforeEOL, which is multiple blanks and optional comment.
let eKeyValue = pstringCI "KeyName:" >>. (opt (ws1 >>. expression)) .>> wsBeforeEOL |>> Key
// Define the parser for the whole string...in this case a single line
let htmlProgramParser = spaces >>. eKeyValue .>> spaces .>> eof
// test harnes on a string
let parseHtmlProgramString programName str =
runParserOnString htmlProgramParser (UserState.Create()) programName str //with
[<EntryPoint>]
let main argv =
printfn "%A" argv
let test1 =
" KeyName: # No Expression but comment"
|> parseHtmlProgramString "Test1 - No Expression but comment"
|> printfn "Test1 - %A"
let test2 =
" KeyName: 2+2 # Expression and Comment"
|> parseHtmlProgramString "Test2 - 2+2 # Expression and Comment"
|> printfn "Test2 - %A"
let test3 =
" KeyName: 3 + 3 # # Expression and Comment2"
|> parseHtmlProgramString "Test3 - 3 + 3 # Expression and Comment2 (Spaces)"
|> printfn "Test3 - %A"
let test4 =
" KeyName: (3 + 4) # Bracketed Expression and Comment"
|> parseHtmlProgramString "Test4 - (3 + 4) # Bracketed Expression and Comment"
|> printfn "Test4 - %A"
let test5 =
" KeyName: (4 + 4) "
|> parseHtmlProgramString "Test5 - (4 + 4) # Expression + <no comment>"
|> printfn "Test5 - %A"
let test6 =
" KeyName:"
|> parseHtmlProgramString "Test6 - <no expression> <no comment>"
|> printfn "Test6 - %A"
0 // return an integer exit code
처럼 리팩토링에 의해, 예를 들어이 문제를 해결할 수 있습니다. 나는 FParsec (그리고 F # 일반적으로)에 대해 아주 익숙하다. 내 큰 DSL에서 내 문제를 해결할 것입니다. 신속한 도움에 감사드립니다. – Nathan
사실 OPP가 제대로 종료되지 않아서 문제가 더 많았지 만. >>를 지적 해 주셔서 고마워요? 및 조합 자 % <|>. – Nathan