forked from pointfreeco/swift-parsing
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Arithmetic.swift
129 lines (119 loc) · 3.16 KB
/
Arithmetic.swift
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import Benchmark
import Foundation
import Parsing
/// This benchmark demonstrates how to parse a recursive grammar: arithmetic.
let arithmeticSuite = BenchmarkSuite(name: "Arithmetic") { suite in
struct AdditionAndSubtraction: Parser {
func parse(_ input: inout Substring.UTF8View) throws -> Double {
try InfixOperator(associativity: .left) {
OneOf {
"+".utf8.map { (+) }
"-".utf8.map { (-) }
}
} lowerThan: {
MultiplicationAndDivision()
}
.parse(&input)
}
}
struct MultiplicationAndDivision: Parser {
func parse(_ input: inout Substring.UTF8View) throws -> Double {
try InfixOperator(associativity: .left) {
OneOf {
"*".utf8.map { (*) }
"/".utf8.map { (/) }
}
} lowerThan: {
Exponent()
}
.parse(&input)
}
}
struct Exponent: Parser {
func parse(_ input: inout Substring.UTF8View) throws -> Double {
try InfixOperator(associativity: .left) {
"^".utf8.map { pow }
} lowerThan: {
Factor()
}
.parse(&input)
}
}
struct Factor: Parser {
func parse(_ input: inout Substring.UTF8View) throws -> Double {
try OneOf {
Parse {
"(".utf8
AdditionAndSubtraction()
")".utf8
}
Double.parser()
}
.parse(&input)
}
}
let input = "1+2*3/4-5^2"
var output: Double!
suite.benchmark("Parser") {
var input = input[...].utf8
output = try AdditionAndSubtraction().parse(&input)
} tearDown: {
precondition(output == -22.5)
}
}
public struct InfixOperator<Operator: Parser, Operand: Parser>: Parser
where
Operator.Input == Operand.Input,
Operator.Output == (Operand.Output, Operand.Output) -> Operand.Output
{
public let `associativity`: Associativity
public let operand: Operand
public let `operator`: Operator
@inlinable
public init(
associativity: Associativity,
@ParserBuilder _ operator: () -> Operator,
@ParserBuilder lowerThan operand: () -> Operand // Should this be called `precedes operand:`?
) {
self.associativity = `associativity`
self.operand = operand()
self.operator = `operator`()
}
@inlinable
public func parse(_ input: inout Operand.Input) rethrows -> Operand.Output {
switch associativity {
case .left:
var lhs = try self.operand.parse(&input)
var rest = input
while true {
do {
let operation = try self.operator.parse(&input)
let rhs = try self.operand.parse(&input)
rest = input
lhs = operation(lhs, rhs)
} catch {
input = rest
return lhs
}
}
case .right:
var lhs: [(Operand.Output, Operator.Output)] = []
while true {
let rhs = try self.operand.parse(&input)
do {
let operation = try self.operator.parse(&input)
lhs.append((rhs, operation))
} catch {
return lhs.reversed().reduce(rhs) { rhs, pair in
let (lhs, operation) = pair
return operation(lhs, rhs)
}
}
}
}
}
}
public enum Associativity {
case left
case right
}