Планирам да използвам FParsec за прототипа на по-голям мой проект. Затова реших да получа първия си опит с тази библиотека с помощта на тестовата програма, посочена по-долу. Но изглежда, че комбинацията от моите основни парсери (които изглежда работят) с помощта на функцията fparsec 'choice' води до неочаквано поведение.
По принцип целта е целият този прост код на анализатора на калкулатора винаги да връща сума от продукти на числа или подизрази. Подизразите от своя страна ще имат същата структура като целия израз.
Както разбрах от документацията на 'choice', алтернативите се опитват отляво надясно, както е посочено в списъка с анализатори, дадени на 'choice'. Разбрах, че ако анализатор, който е по-наляво в списъка, се провали, но е консумирал вход, следващите анализатори няма да бъдат опитани.
И все пак изглежда има нещо повече, отколкото мога да разбера в момента, сякаш беше както казах по-горе, кодът трябва да работи. Но не става.
Ще бъда много благодарен, ако някой може да ми обясни а) какво се обърка и защо и б) как да го поправя.
В основния си проект планирам да изчисля парсери от някакъв вход и затова трябва да разбера как точно да комбинирам анализатори по надежден начин без изненади.
(*
SimpleAOSCalculator
Should implement the following grammar:
SimpleAOSCalculator := SUM
SUM := SUMMAND [ '+' SUMMAND ]*
SUMMAND := PRODUCT | SUBEXPR
PRODUCT := FACTOR [ '*' FACTOR ]*
FACTOR := NUMBER | SUBEXPR
SUBEXPR := '(' SUM ')'
NUMBER := pfloat
*)
// NOTE: If you try this in fsi, you have to change the 2 lines below to point to the spot you have your fparsec dlls stored at.
#r @"C:\hgprojects\fparsec\Build\VS11\bin\Debug\FParsecCS.dll"
#r @"C:\hgprojects\fparsec\Build\VS11\bin\Debug\FParsec.dll"
open FParsec
let testParser p input =
match run p input with
| Success(result, _, _) -> printfn "Success: %A" result
| Failure(errorMsg, _, _) -> printfn "Failure %s" errorMsg
input
type Node =
| Sum of SumNode
| Product of ProductNode
| Number of NumberNode
| SubExpression of SubExpressionNode
and SumNode =
{
Summands : Node list
}
and ProductNode =
{
Factors : Node list
}
and NumberNode =
{
Value : float
}
and SubExpressionNode =
{
N : Node
}
let CreateSubExpression (n : Node) : Node =
let s : SubExpressionNode = { N = n }
SubExpression s
let (PrimitiveAOSCalculator : Parser<Node,unit>), (PrimitiveAOSCalculatorImpl : Parser<Node,unit> ref) = createParserForwardedToRef()
let SubExpression : Parser<Node,unit> =
between (pchar '(') (pchar ')') PrimitiveAOSCalculator |>> CreateSubExpression
let Number : Parser<Node,unit> =
pfloat |>> (fun v -> Number { Value = v })
let Product : Parser<Node,unit> =
let Factor : Parser<Node,unit> = choice [Number; SubExpression]
let Mult = spaces >>. pchar '*' .>> spaces
sepBy1 Factor Mult |>> (fun l -> Product { Factors = l})
let Summand : Parser<Node,unit> =
choice [ attempt Product; attempt SubExpression ]
let Sum =
let Add = (spaces >>. pchar '+' .>> spaces)
sepBy1 Summand Add |>> (fun l -> Sum { Summands = l })
do PrimitiveAOSCalculatorImpl :=
Sum
let rec Eval (n : Node) : float =
match n with
| Number(v) -> v.Value
| Product(p) -> List.map (fun n -> Eval n) p.Factors |> List.fold (fun a b -> a * b) 1.0
| Sum(s) -> List.map (fun t -> Eval t) s.Summands |> List.fold (fun a b -> a + b) 0.0
| SubExpression(x) -> Eval x.N
let Calculate (term : string) : float =
let parseResult = run PrimitiveAOSCalculator term
match parseResult with
| Success(ast,_,_) -> Eval ast
| Failure(errorMessage,_,_) -> failwith ("Parsing of the expression failed: " + errorMessage)
let Show (s : string) : string =
printfn "%s" s
s
let test p i =
testParser p i |> Show |> Calculate |> printfn "result = %f"
do test Product "5.1 * 2"
do test Product "5.1"
do test Product "5.1"
do test Sum "(4 * 3) + (5 * 2)"
do test Sum "4 * 3 + 5 * 2"
do test PrimitiveAOSCalculator "42"
do test PrimitiveAOSCalculator "42 * 42"
do test PrimitiveAOSCalculator "42 + 42"
do test PrimitiveAOSCalculator "42 * 42 + 47.11"
do test PrimitiveAOSCalculator "5.1 * (32 + 88 * 3) + 1.4"
Тук $do test Sum "4 * 3 + 5 * 2" се проваля със следния изход:
Failure Error in Ln: 1 Col: 1
4 * 3 + 5 * 2
^
Expecting: '('
The parser backtracked after:
Error in Ln: 1 Col: 7
4 * 3 + 5 * 2
^
Expecting: '*'
4 * 3 + 5 * 2
System.Exception: Parsing of the expression failed: Error in Ln: 1 Col: 1
4 * 3 + 5 * 2
^
Expecting: '('
The parser backtracked after:
Error in Ln: 1 Col: 7
4 * 3 + 5 * 2
^
Expecting: '*'
И нямам дори най-мъглявата представа защо би очаквал "*" тук.
Product
изглежда грешен, тъй като използваsepBy1
и по този начин не позволява обработката на единNumber
. Още нещо: защо направихте синтактичен анализ и анализ на синтактично дърво в един код? Няма ли да е по-лесно да анализиратеstring
вElement list
, къдетоtype Element = | Number of int | Operation of char
и след това да стартирате синтактичен анализ? Представете си колко трудно би било да добавите числово деление към вашата съществуваща кодова база. - person bytebuster   schedule 26.05.2014sepBy1 Factor Mult
би означавало в EBNF:Factor [ Mult Factor ]*
докатоsepBy Factor Mult
би означавало в EBNF:[Factor [ '*' Factor ]*]
. Структурата на това (само 150 реда ;) ) приложение е както предлагате.SimpleAOSParser
произвежда AST (дърво на възли),Eval
оценява AST. - person BitTickler   schedule 26.05.2014