-
Notifications
You must be signed in to change notification settings - Fork 75
/
Arithmetic.swift
126 lines (116 loc) · 3.12 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
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 {
var body: some Parser<Substring.UTF8View, Double> {
InfixOperator(associativity: .left) {
OneOf {
"+".utf8.map { (+) as (Double, Double) -> Double }
"-".utf8.map { (-) }
}
} lowerThan: {
MultiplicationAndDivision()
}
}
}
struct MultiplicationAndDivision: Parser {
var body: some Parser<Substring.UTF8View, Double> {
InfixOperator(associativity: .left) {
OneOf {
"*".utf8.map { (*) as (Double, Double) -> Double }
"/".utf8.map { (/) }
}
} lowerThan: {
Exponent()
}
}
}
struct Exponent: Parser {
var body: some Parser<Substring.UTF8View, Double> {
InfixOperator(associativity: .left) {
"^".utf8.map { pow as (Double, Double) -> Double }
} lowerThan: {
Factor()
}
}
}
struct Factor: Parser {
var body: some Parser<Substring.UTF8View, Double> {
OneOf {
Parse {
"(".utf8
AdditionAndSubtraction()
")".utf8
}
Double.parser()
}
}
}
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<Input, Operator: Parser, Operand: Parser>: Parser
where
Operator.Input == Input,
Operand.Input == 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<Input> _ operator: () -> Operator,
@ParserBuilder<Input> lowerThan operand: () -> Operand // Should this be called `precedes:`?
) {
self.associativity = `associativity`
self.operand = operand()
self.operator = `operator`()
}
@inlinable
public func parse(_ input: inout 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
}