1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
#r "nuget: FParsec"
open FParsec
// 数字と演算子を解析するための型
type Expr =
| Number of float
| Unary of char * Expr
| Binary of Expr * char * Expr
let ws = spaces
let str_ws s = pstring s >>. ws
// 数値リテラルのパーサー
let number: Parser<Expr, unit> =
pfloat .>> ws |>> Number
// 文字列を囲む括弧のパーサー
let parens p: Parser<Expr, unit> =
between (str_ws "(") (str_ws ")") p
// 演算子の優先順位を定義
let opp = new OperatorPrecedenceParser<Expr, unit, unit>()
type PrefixOperator = PrefixOperator<Expr, unit, unit>
type InfixOperator = InfixOperator<Expr, unit, unit>
// 式のパーサーを初期化
let expr = opp.ExpressionParser
// 演算子を定義
// 前置演算子(符号変換など)
opp.AddOperator(PrefixOperator("-", ws, 1, true, fun x -> Unary('-', x)))
opp.AddOperator(PrefixOperator("+", ws, 1, true, fun x -> x))
// 加算、減算
opp.AddOperator(InfixOperator("+", ws, 1, Associativity.Left, fun x y -> Binary(x, '+', y)))
opp.AddOperator(InfixOperator("-", ws, 1, Associativity.Left, fun x y -> Binary(x, '-', y)))
// 乗算、除算
opp.AddOperator(InfixOperator("*", ws, 2, Associativity.Left, fun x y -> Binary(x, '*', y)))
opp.AddOperator(InfixOperator("/", ws, 2, Associativity.Left, fun x y -> Binary(x, '/', y)))
// 冪乗(右結合)
opp.AddOperator(InfixOperator("^", ws, 3, Associativity.Right, fun x y -> Binary(x, '^', y)))
// 括弧または数値リテラルを式として扱う
opp.TermParser <- (number <|> parens expr)
// 評価関数
let rec eval = function
| Number n -> n
| Unary('-', x) -> -eval x
| Unary('+', x) -> eval x
| Binary(x, '+', y) -> eval x + eval y
| Binary(x, '-', y) -> eval x - eval y
| Binary(x, '*', y) -> eval x * eval y
| Binary(x, '/', y) -> eval x / eval y
| Binary(x, '^', y) -> eval x ** eval y
| _ -> failwith "Unsupported operation"
// 入力文字列を解析して評価する
let parseAndEval input =
match run (ws >>. expr .>> eof) input with
| Success(result, _, _) -> eval result
| Failure(errorMsg, _, _) -> failwith errorMsg
let tests = [
"1 + 2 * 3" // 7
"4 * (2 + 3)" // 20
"3 ^ 2 ^ 2" // 81 (右結合)
"-4 + 2" // -2
"(3 + 5) / 2" // 4
]
for test in tests do
printfn "Input: %s = %f" test (parseAndEval test)
|