(prefix: S) -> Bool where S: Collection, S.Element == Element { 24 | guard starts(with: prefix) else { return false } 25 | removeFirst(prefix.count) 26 | return true 27 | } 28 | 29 | mutating func remove(keyword: String) -> Bool { 30 | guard hasPrefix(keyword) else { return false } 31 | let index = self.index(startIndex, offsetBy: keyword.count) 32 | guard index < endIndex, !self[index].isIdentifier else { return false } 33 | _ = remove(prefix: keyword) 34 | return true 35 | } 36 | 37 | mutating func skipWS() { 38 | _ = remove(while: { $0.isWhitespace }) 39 | } 40 | } 41 | 42 | extension Substring { 43 | func err(_ reason: Reason) -> ParseError { 44 | ParseError(position: position, reason: reason) 45 | } 46 | 47 | mutating func parseExpression() throws -> Expression { 48 | return try parseDefinition() 49 | } 50 | 51 | mutating func parseDefinition() throws -> Expression { 52 | guard self.remove(keyword: "let") else { return try parseFunctionCall() } 53 | skipWS() 54 | let name = try parseIdentifier() 55 | skipWS() 56 | guard self.parse(operator: "=") else { 57 | throw err(Reason.expected("=")) 58 | } 59 | skipWS() 60 | let value = try parseExpression() 61 | skipWS() 62 | guard self.remove(keyword: "in") else { 63 | throw err(Reason.expectedKeyword("in")) 64 | } 65 | skipWS() 66 | let body = try parseExpression() 67 | return .let(name: name, value: value, in: body) 68 | } 69 | 70 | mutating func parseFunctionCall() throws -> Expression { 71 | var result = try parseAtom() 72 | while remove(prefix: "(") { 73 | skipWS() 74 | var arguments: [Expression] = [] 75 | while let f = first, f != ")" { 76 | arguments.append(try parseExpression()) 77 | skipWS() 78 | if !remove(prefix: ",") { 79 | break 80 | } 81 | skipWS() 82 | } 83 | 84 | guard remove(prefix: ")") else { 85 | throw err(.expected(")")) 86 | } 87 | result = .call(result, arguments: arguments) 88 | } 89 | return result 90 | } 91 | 92 | mutating func parseFunc() throws -> Expression { 93 | guard remove(keyword: "func") else { return try parseAtom() } 94 | skipWS() 95 | var parameters: [String] = [] 96 | guard remove(prefix: "(") else { throw err(.expected("(")) } 97 | while !remove(prefix: ")") { 98 | let identifier = try parseIdentifier() 99 | parameters.append(identifier) 100 | guard remove(prefix: ",") || first == ")" else { 101 | throw err(.expected(", or )")) 102 | } 103 | skipWS() 104 | } 105 | skipWS() 106 | guard remove(prefix: "{") else { throw err(Reason.expected("{")) } 107 | skipWS() 108 | let body = try parseExpression() 109 | skipWS() 110 | guard remove(prefix: "}") else { 111 | throw err(Reason.expected("}")) 112 | } 113 | return .function(parameters: parameters, body: body) 114 | } 115 | 116 | mutating func parseAtom() throws -> Expression { 117 | if let p = first { 118 | if p.isDecimalDigit { 119 | let int = parseInt() 120 | return .intLiteral(int) 121 | } else if p == "\"" { 122 | removeFirst() 123 | let value = remove(while: { $0 != "\"" }) // todo escaping 124 | guard remove(prefix: "\"") else { 125 | throw err(Reason.expected("\"")) 126 | } 127 | return .stringLiteral(String(value)) 128 | } else if p.isIdentifierStart { 129 | let name = try parseIdentifier() 130 | return .variable(name) 131 | } else if p == "<" { 132 | return try parseTag() 133 | } else { 134 | throw err(.expectedAtom) 135 | } 136 | } 137 | throw err(.unexpectedEOF) 138 | } 139 | 140 | mutating func parseInt() -> Int { 141 | return Int(String(remove(while: { $0.isDecimalDigit })))! 142 | } 143 | 144 | mutating func parseTag() throws -> Expression { 145 | if remove(prefix: "<") { 146 | skipWS() 147 | let name = try parseIdentifier() 148 | guard remove(prefix: ">") else { 149 | throw err(Reason.expected(">")) 150 | } 151 | skipWS() 152 | var body: [Expression] = [] 153 | while !remove(prefix: "\(name)>") { 154 | try body.append(parseTag()) 155 | } 156 | return .tag(name: name, body: body) 157 | } else if remove(prefix: "{") { 158 | skipWS() 159 | let result = try parseExpression() 160 | skipWS() 161 | guard remove(prefix: "}") else { 162 | throw err(Reason.expected("}")) 163 | } 164 | return result 165 | } else { 166 | throw err(Reason.expected("{ or <")) 167 | } 168 | } 169 | 170 | mutating func parseIdentifier() throws -> String { 171 | let name = remove(while: { $0.isIdentifier }) 172 | guard !name.isEmpty else { 173 | throw err(.expectedIdentifier) 174 | } 175 | return String(name) 176 | } 177 | 178 | mutating func parseOperator() throws -> String { 179 | let name = remove(while: { $0.isOperator }) 180 | guard !name.isEmpty else { 181 | throw err(.expectedOperator) 182 | } 183 | return String(name) 184 | } 185 | 186 | mutating func parse(operator expected: String) -> Bool { 187 | var copy = self 188 | do { 189 | let op = try copy.parseOperator() 190 | guard op == expected else { return false } 191 | self = copy 192 | return true 193 | } catch { 194 | return false 195 | } 196 | } 197 | } 198 | 199 | extension Character { 200 | var isDecimalDigit: Bool { 201 | return isHexDigit && hexDigitValue! < 10 202 | } 203 | 204 | var isOperator: Bool { 205 | return self == "=" // todo 206 | } 207 | 208 | var isIdentifierStart: Bool { 209 | return isLetter 210 | } 211 | 212 | var isIdentifier: Bool { 213 | return isLetter || self == "_" 214 | } 215 | } 216 | 217 | public struct ParseError: Error, Hashable { 218 | public var position: String.Index 219 | public var reason: Reason 220 | } 221 | 222 | public enum Reason: Hashable { 223 | case unexpectedEOF 224 | case expectedAtom 225 | case expectedIdentifier 226 | case expectedOperator 227 | case expectedKeyword(String) 228 | case expected(String) 229 | case unexpectedRemainder(String) 230 | } 231 | 232 | extension String { 233 | public func parse() throws -> Expression { 234 | var remainder = self[...] 235 | let result = try remainder.parseExpression() 236 | guard remainder.isEmpty else { 237 | throw remainder.err(.unexpectedRemainder(String(remainder))) 238 | } 239 | return result 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /Sources/Step1Evaluation/Parsing.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by Chris Eidhof on 09.05.20. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Substring { 11 | var position: Index { startIndex } 12 | 13 | mutating func remove(while cond: (Element) -> Bool) -> SubSequence { 14 | var p = position 15 | while p < endIndex, cond(self[p]) { 16 | formIndex(after: &p) 17 | } 18 | let result = self[position..
(prefix: S) -> Bool where S: Collection, S.Element == Element {
24 | guard starts(with: prefix) else { return false }
25 | removeFirst(prefix.count)
26 | return true
27 | }
28 |
29 | mutating func remove(keyword: String) -> Bool {
30 | guard hasPrefix(keyword) else { return false }
31 | let index = self.index(startIndex, offsetBy: keyword.count)
32 | guard index < endIndex, !self[index].isIdentifier else { return false }
33 | _ = remove(prefix: keyword)
34 | return true
35 | }
36 |
37 | var remainder: String {
38 | return String(self)
39 | }
40 |
41 | mutating func skipWS() {
42 | _ = remove(while: { $0.isWhitespace })
43 | }
44 | }
45 |
46 | extension Substring {
47 | func err(_ reason: Reason) -> ParseError {
48 | ParseError(position: position, reason: reason)
49 | }
50 |
51 | mutating func parseExpression() throws -> Expression {
52 | return try parseDefinition()
53 | }
54 |
55 | mutating func parseDefinition() throws -> Expression {
56 | guard self.remove(keyword: "let") else { return try parseFunctionCall() }
57 | skipWS()
58 | let name = try parseIdentifier()
59 | skipWS()
60 | guard self.parse(operator: "=") else {
61 | throw err(Reason.expected("="))
62 | }
63 | skipWS()
64 | let value = try parseExpression()
65 | skipWS()
66 | guard self.remove(keyword: "in") else {
67 | throw err(Reason.expectedKeyword("in"))
68 | }
69 | skipWS()
70 | let body = try parseExpression()
71 | return .let(name: name, value: value, in: body)
72 | }
73 |
74 | mutating func parseFunctionCall() throws -> Expression {
75 | var result = try parseAtom()
76 | while remove(prefix: "(") {
77 | skipWS()
78 | var arguments: [Expression] = []
79 | while let f = first, f != ")" {
80 | arguments.append(try parseExpression())
81 | skipWS()
82 | if !remove(prefix: ",") {
83 | break
84 | }
85 | skipWS()
86 | }
87 |
88 | guard remove(prefix: ")") else {
89 | throw err(.expected(")"))
90 | }
91 | result = .call(result, arguments: arguments)
92 | }
93 | return result
94 | }
95 |
96 |
97 | mutating func parseAtom() throws -> Expression {
98 | if let p = first {
99 | if p.isDecimalDigit {
100 | let int = parseInt()
101 | return .intLiteral(int)
102 | } else if p == "\"" {
103 | removeFirst()
104 | let value = remove(while: { $0 != "\"" }) // todo escaping
105 | guard remove(prefix: "\"") else {
106 | throw err(Reason.expected("\""))
107 | }
108 | return .stringLiteral(String(value))
109 | } else if remove(keyword: "func") {
110 | skipWS()
111 | var parameters: [String] = []
112 | guard remove(prefix: "(") else { throw err(.expected("(")) }
113 | while !remove(prefix: ")") {
114 | let identifier = try parseIdentifier()
115 | parameters.append(identifier)
116 | guard remove(prefix: ",") || first == ")" else {
117 | throw err(.expected(", or )"))
118 | }
119 | skipWS()
120 | }
121 | skipWS()
122 | guard remove(prefix: "{") else { throw err(Reason.expected("{")) }
123 | skipWS()
124 | let body = try parseExpression()
125 | skipWS()
126 | guard remove(prefix: "}") else {
127 | throw err(Reason.expected("}"))
128 | }
129 | return .function(parameters: parameters, body: body)
130 | } else if p.isIdentifierStart {
131 | let name = try parseIdentifier()
132 | return .variable(name)
133 | } else if p == "<" {
134 | return try parseTag()
135 | } else {
136 | throw err(.expectedAtom)
137 | }
138 | }
139 | throw err(.unexpectedEOF)
140 | }
141 |
142 | mutating func parseInt() -> Int {
143 | return Int(String(remove(while: { $0.isDecimalDigit })))!
144 | }
145 |
146 | mutating func parseTag() throws -> Expression {
147 | if remove(prefix: "<") {
148 | skipWS()
149 | let name = try parseIdentifier()
150 | guard remove(prefix: ">") else {
151 | throw err(Reason.expected(">"))
152 | }
153 | skipWS()
154 | var body: [Expression] = []
155 | while !remove(prefix: "\(name)>") {
156 | try body.append(parseTag())
157 | }
158 | return .tag(name: name, body: body)
159 | } else if remove(prefix: "{") {
160 | skipWS()
161 | let result = try parseExpression()
162 | skipWS()
163 | guard remove(prefix: "}") else {
164 | throw err(Reason.expected("}"))
165 | }
166 | return result
167 | } else {
168 | throw err(Reason.expected("{ or <"))
169 | }
170 | }
171 |
172 | mutating func parseIdentifier() throws -> String {
173 | let name = remove(while: { $0.isIdentifier })
174 | guard !name.isEmpty else {
175 | throw err(.expectedIdentifier)
176 | }
177 | return String(name)
178 | }
179 |
180 | mutating func parseOperator() throws -> String {
181 | let name = remove(while: { $0.isOperator })
182 | guard !name.isEmpty else {
183 | throw err(.expectedOperator)
184 | }
185 | return String(name)
186 | }
187 |
188 | mutating func parse(operator expected: String) -> Bool {
189 | var copy = self
190 | do {
191 | let op = try copy.parseOperator()
192 | guard op == expected else { return false }
193 | self = copy
194 | return true
195 | } catch {
196 | return false
197 | }
198 | }
199 | }
200 |
201 | extension Character {
202 | var isDecimalDigit: Bool {
203 | return isHexDigit && hexDigitValue! < 10
204 | }
205 |
206 | var isOperator: Bool {
207 | return self == "=" // todo
208 | }
209 |
210 | var isIdentifierStart: Bool {
211 | return isLetter
212 | }
213 |
214 | var isIdentifier: Bool {
215 | return isLetter || self == "_"
216 | }
217 | }
218 |
219 | public struct ParseError: Error, Hashable {
220 | public var position: String.Index
221 | public var reason: Reason
222 | }
223 |
224 | public enum Reason: Hashable {
225 | case unexpectedEOF
226 | case expectedAtom
227 | case expectedIdentifier
228 | case expectedOperator
229 | case expectedKeyword(String)
230 | case expected(String)
231 | case unexpectedRemainder(String)
232 | }
233 |
234 | extension String {
235 | public func parse() throws -> Expression {
236 | var context = self[...]
237 | let result = try context.parseExpression()
238 | guard context.isEmpty else {
239 | throw context.err(.unexpectedRemainder(String(context)))
240 | }
241 | return result
242 | }
243 |
244 | }
245 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/xcshareddata/xcschemes/uikonf2020.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
(prefix: S) -> Bool where S: Collection, S.Element == Element {
25 | guard starts(with: prefix) else { return false }
26 | removeFirst(prefix.count)
27 | return true
28 | }
29 | }
30 |
31 | extension Substring {
32 | var remainder: String {
33 | return String(self)
34 | }
35 |
36 | mutating func skipWS() {
37 | _ = remove(while: { $0.isWhitespace })
38 | }
39 |
40 | func err(_ reason: Reason) -> ParseError {
41 | ParseError(position: position, reason: reason)
42 | }
43 |
44 | mutating func parseExpression() throws -> AnnotatedExpression {
45 | return try parseDefinition()
46 | }
47 |
48 | mutating func parseDefinition() throws -> AnnotatedExpression {
49 | let start = position
50 | guard self.remove(keyword: "let") else { return try parseFunctionCall() }
51 | skipWS()
52 | let name = try parseIdentifier()
53 | skipWS()
54 | guard self.parse(operator: "=") else {
55 | throw err(Reason.expected("="))
56 | }
57 | skipWS()
58 | let value = try parseExpression()
59 | skipWS()
60 | guard self.remove(keyword: "in") else {
61 | throw err(Reason.expectedKeyword("in"))
62 | }
63 | skipWS()
64 | let body = try parseExpression()
65 | let end = position
66 | return AnnotatedExpression(SourceRange(startIndex: start, endIndex: end), .let(name: name, value: value, in: body))
67 | }
68 |
69 | mutating func parseFunctionCall() throws -> AnnotatedExpression {
70 | var result = try parseFunc()
71 | while remove(prefix: "(") {
72 | let start = position
73 | skipWS()
74 | var arguments: [AnnotatedExpression] = []
75 | while let f = first, f != ")" {
76 | arguments.append(try parseExpression())
77 | skipWS()
78 | if !remove(prefix: ",") {
79 | break
80 | }
81 | skipWS()
82 | }
83 |
84 | guard remove(prefix: ")") else {
85 | throw err(.expected(")"))
86 | }
87 | result = AnnotatedExpression(SourceRange(startIndex: start, endIndex: position), .call(result, arguments: arguments))
88 | }
89 | return result
90 | }
91 |
92 | mutating func parseFunc() throws -> AnnotatedExpression {
93 | let start = position
94 | guard remove(keyword: "func") else { return try parseAtom() }
95 | skipWS()
96 | var parameters: [String] = []
97 | guard remove(prefix: "(") else { throw err(.expected("(")) }
98 | // Parse 0 or more identifiers separated by commas
99 | while !remove(prefix: ")") {
100 | let identifier = try parseIdentifier()
101 | parameters.append(identifier)
102 | guard remove(prefix: ",") || first == ")" else {
103 | throw err(.expected(", or )"))
104 | }
105 | skipWS()
106 | }
107 | skipWS()
108 | guard remove(prefix: "{") else { throw err(Reason.expected("{")) }
109 | skipWS()
110 | let body = try parseExpression()
111 | skipWS()
112 | guard remove(prefix: "}") else {
113 | throw err(Reason.expected("}"))
114 | }
115 | let end = position
116 | return AnnotatedExpression(SourceRange(startIndex: start, endIndex: end), .function(parameters: parameters, body: body))
117 | }
118 |
119 | mutating func parseAtom() throws -> AnnotatedExpression {
120 | if let p = first {
121 | if p.isDecimalDigit {
122 | let (range, int) = try annotate { $0.parseInt() }
123 | return AnnotatedExpression(range, .intLiteral(int))
124 | } else if p == "\"" {
125 | let start = position
126 | removeFirst()
127 | let value = remove(while: { $0 != "\"" }) // todo escaping
128 | guard remove(prefix: "\"") else {
129 | throw err(Reason.expected("\""))
130 | }
131 | return AnnotatedExpression(SourceRange(startIndex: start, endIndex: position), .stringLiteral(String(value)))
132 | } else if p.isIdentifierStart {
133 | let (range, name) = try annotate { try $0.parseIdentifier() }
134 | return AnnotatedExpression(range, .variable(name))
135 | } else if p == "<" {
136 | return try parseTag()
137 | } else {
138 | throw err(.expectedAtom)
139 | }
140 | }
141 | throw err(.unexpectedEOF)
142 | }
143 |
144 | mutating func parseInt() -> Int {
145 | return Int(String(remove(while: { $0.isDecimalDigit })))!
146 | }
147 |
148 | mutating func parseTag() throws -> AnnotatedExpression {
149 | let start = position
150 | if remove(prefix: "<") {
151 | skipWS()
152 | let name = try parseIdentifier()
153 | guard remove(prefix: ">") else {
154 | throw err(Reason.expected(">"))
155 | }
156 | skipWS()
157 | var body: [AnnotatedExpression] = []
158 | while !remove(prefix: "\(name)>") {
159 | try body.append(parseTag())
160 | }
161 | return AnnotatedExpression(SourceRange(startIndex: start, endIndex: position), .tag(name: name, body: body))
162 | } else if remove(prefix: "{") {
163 | skipWS()
164 | let result = try parseExpression()
165 | skipWS()
166 | guard remove(prefix: "}") else {
167 | throw err(Reason.expected("}"))
168 | }
169 | return result
170 | } else {
171 | throw err(Reason.expected("{ or <"))
172 | }
173 | }
174 |
175 | mutating func parseIdentifier() throws -> String {
176 | let name = remove(while: { $0.isIdentifier })
177 | guard !name.isEmpty else {
178 | throw err(.expectedIdentifier)
179 | }
180 | return String(name)
181 | }
182 |
183 | mutating func parseOperator() throws -> String {
184 | let name = remove(while: { $0.isOperator })
185 | guard !name.isEmpty else {
186 | throw err(.expectedOperator)
187 | }
188 | return String(name)
189 | }
190 |
191 | mutating func annotate(_ f: (inout Self) throws -> A) throws -> (SourceRange, A) {
192 | let start = position
193 | let result = try f(&self)
194 | let end = position
195 | return (SourceRange(startIndex: start, endIndex: end), result)
196 | }
197 |
198 | mutating func remove(keyword: String) -> Bool {
199 | guard hasPrefix(keyword) else { return false }
200 | let index = self.index(startIndex, offsetBy: keyword.count)
201 | guard index < endIndex, !self[index].isIdentifier else { return false }
202 | _ = remove(prefix: keyword)
203 | return true
204 | }
205 |
206 |
207 | mutating func parse(operator expected: String) -> Bool {
208 | var copy = self
209 | do {
210 | let op = try copy.parseOperator()
211 | guard op == expected else { return false }
212 | self = copy
213 | return true
214 | } catch {
215 | return false
216 | }
217 | }
218 | }
219 |
220 | extension Character {
221 | var isDecimalDigit: Bool {
222 | return isHexDigit && hexDigitValue! < 10
223 | }
224 |
225 | var isOperator: Bool {
226 | return self == "=" // todo
227 | }
228 |
229 | var isIdentifierStart: Bool {
230 | return isLetter
231 | }
232 |
233 | var isIdentifier: Bool {
234 | return isLetter || self == "_"
235 | }
236 | }
237 |
238 | public struct ParseError: Error, Hashable {
239 | public var position: String.Index
240 | public var reason: Reason
241 | }
242 |
243 | public enum Reason: Hashable {
244 | case unexpectedEOF
245 | case expectedAtom
246 | case expectedIdentifier
247 | case expectedOperator
248 | case expectedKeyword(String)
249 | case expected(String)
250 | case unexpectedRemainder(String)
251 | }
252 |
253 | extension String {
254 | public func parse() throws -> AnnotatedExpression {
255 | var context = self[...]
256 | let result = try context.parseExpression()
257 | guard context.isEmpty else {
258 | throw context.err(.unexpectedRemainder(String(context)))
259 | }
260 | return result
261 | }
262 |
263 | }
264 |
--------------------------------------------------------------------------------
/Sources/Step2Annotation/Parsing.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Chris Eidhof on 09.05.20.
6 | //
7 |
8 | import Foundation
9 |
10 | extension Substring {
11 | var position: Index { startIndex }
12 | mutating func remove(while cond: (Element) -> Bool) -> SubSequence {
13 | var p = position
14 | while p < endIndex, cond(self[p]) {
15 | formIndex(after: &p)
16 | }
17 | let result = self[position.. (prefix: S) -> Bool where S: Collection, S.Element == Element {
25 | guard starts(with: prefix) else { return false }
26 | removeFirst(prefix.count)
27 | return true
28 | }
29 | }
30 |
31 | extension Substring {
32 | var remainder: String {
33 | return String(self)
34 | }
35 |
36 | mutating func skipWS() {
37 | _ = remove(while: { $0.isWhitespace })
38 | }
39 |
40 | func err(_ reason: Reason) -> ParseError {
41 | ParseError(position: position, reason: reason)
42 | }
43 |
44 | mutating func parseExpression() throws -> AnnotatedExpression {
45 | return try parseDefinition()
46 | }
47 |
48 | mutating func parseDefinition() throws -> AnnotatedExpression {
49 | let start = position
50 | guard self.remove(keyword: "let") else { return try parseFunctionCall() }
51 | skipWS()
52 | let name = try parseIdentifier()
53 | skipWS()
54 | guard self.parse(operator: "=") else {
55 | throw err(Reason.expected("="))
56 | }
57 | skipWS()
58 | let value = try parseExpression()
59 | skipWS()
60 | guard self.remove(keyword: "in") else {
61 | throw err(Reason.expectedKeyword("in"))
62 | }
63 | skipWS()
64 | let body = try parseExpression()
65 | let end = position
66 | return AnnotatedExpression(SourceRange(startIndex: start, endIndex: end), .let(name: name, value: value, in: body))
67 | }
68 |
69 | mutating func parseFunctionCall() throws -> AnnotatedExpression {
70 | var result = try parseFunc()
71 | while remove(prefix: "(") {
72 | let start = position
73 | skipWS()
74 | var arguments: [AnnotatedExpression] = []
75 | while let f = first, f != ")" {
76 | arguments.append(try parseExpression())
77 | skipWS()
78 | if !remove(prefix: ",") {
79 | break
80 | }
81 | skipWS()
82 | }
83 |
84 | guard remove(prefix: ")") else {
85 | throw err(.expected(")"))
86 | }
87 | result = AnnotatedExpression(SourceRange(startIndex: start, endIndex: position), .call(result, arguments: arguments))
88 | }
89 | return result
90 | }
91 |
92 | mutating func parseFunc() throws -> AnnotatedExpression {
93 | let start = position
94 | guard remove(keyword: "func") else { return try parseAtom() }
95 | skipWS()
96 | var parameters: [String] = []
97 | guard remove(prefix: "(") else { throw err(.expected("(")) }
98 | // Parse 0 or more identifiers separated by commas
99 | while !remove(prefix: ")") {
100 | let identifier = try parseIdentifier()
101 | parameters.append(identifier)
102 | guard remove(prefix: ",") || first == ")" else {
103 | throw err(.expected(", or )"))
104 | }
105 | skipWS()
106 | }
107 | skipWS()
108 | guard remove(prefix: "{") else { throw err(Reason.expected("{")) }
109 | skipWS()
110 | let body = try parseExpression()
111 | skipWS()
112 | guard remove(prefix: "}") else {
113 | throw err(Reason.expected("}"))
114 | }
115 | let end = position
116 | return AnnotatedExpression(SourceRange(startIndex: start, endIndex: end), .function(parameters: parameters, body: body))
117 | }
118 |
119 | mutating func parseAtom() throws -> AnnotatedExpression {
120 | if let p = first {
121 | if p.isDecimalDigit {
122 | let (range, int) = try annotate { $0.parseInt() }
123 | return AnnotatedExpression(range, .intLiteral(int))
124 | } else if p == "\"" {
125 | let start = position
126 | removeFirst()
127 | let value = remove(while: { $0 != "\"" }) // todo escaping
128 | guard remove(prefix: "\"") else {
129 | throw err(Reason.expected("\""))
130 | }
131 | return AnnotatedExpression(SourceRange(startIndex: start, endIndex: position), .stringLiteral(String(value)))
132 | } else if p.isIdentifierStart {
133 | let (range, name) = try annotate { try $0.parseIdentifier() }
134 | return AnnotatedExpression(range, .variable(name))
135 | } else if p == "<" {
136 | return try parseTag()
137 | } else {
138 | throw err(.expectedAtom)
139 | }
140 | }
141 | throw err(.unexpectedEOF)
142 | }
143 |
144 | mutating func parseInt() -> Int {
145 | return Int(String(remove(while: { $0.isDecimalDigit })))!
146 | }
147 |
148 | mutating func parseTag() throws -> AnnotatedExpression {
149 | let start = position
150 | if remove(prefix: "<") {
151 | skipWS()
152 | let name = try parseIdentifier()
153 | guard remove(prefix: ">") else {
154 | throw err(Reason.expected(">"))
155 | }
156 | skipWS()
157 | var body: [AnnotatedExpression] = []
158 | while !remove(prefix: "\(name)>") {
159 | try body.append(parseTag())
160 | }
161 | return AnnotatedExpression(SourceRange(startIndex: start, endIndex: position), .tag(name: name, body: body))
162 | } else if remove(prefix: "{") {
163 | skipWS()
164 | let result = try parseExpression()
165 | skipWS()
166 | guard remove(prefix: "}") else {
167 | throw err(Reason.expected("}"))
168 | }
169 | return result
170 | } else {
171 | throw err(Reason.expected("{ or <"))
172 | }
173 | }
174 |
175 | mutating func parseIdentifier() throws -> String {
176 | let name = remove(while: { $0.isIdentifier })
177 | guard !name.isEmpty else {
178 | throw err(.expectedIdentifier)
179 | }
180 | return String(name)
181 | }
182 |
183 | mutating func parseOperator() throws -> String {
184 | let name = remove(while: { $0.isOperator })
185 | guard !name.isEmpty else {
186 | throw err(.expectedOperator)
187 | }
188 | return String(name)
189 | }
190 |
191 | mutating func annotate(_ f: (inout Self) throws -> A) throws -> (SourceRange, A) {
192 | let start = position
193 | let result = try f(&self)
194 | let end = position
195 | return (SourceRange(startIndex: start, endIndex: end), result)
196 | }
197 |
198 | mutating func remove(keyword: String) -> Bool {
199 | guard hasPrefix(keyword) else { return false }
200 | let index = self.index(startIndex, offsetBy: keyword.count)
201 | guard index < endIndex, !self[index].isIdentifier else { return false }
202 | _ = remove(prefix: keyword)
203 | return true
204 | }
205 |
206 |
207 | mutating func parse(operator expected: String) -> Bool {
208 | var copy = self
209 | do {
210 | let op = try copy.parseOperator()
211 | guard op == expected else { return false }
212 | self = copy
213 | return true
214 | } catch {
215 | return false
216 | }
217 | }
218 | }
219 |
220 | extension Character {
221 | var isDecimalDigit: Bool {
222 | return isHexDigit && hexDigitValue! < 10
223 | }
224 |
225 | var isOperator: Bool {
226 | return self == "=" // todo
227 | }
228 |
229 | var isIdentifierStart: Bool {
230 | return isLetter
231 | }
232 |
233 | var isIdentifier: Bool {
234 | return isLetter || self == "_"
235 | }
236 | }
237 |
238 | public struct ParseError: Error, Hashable {
239 | public var position: String.Index
240 | public var reason: Reason
241 | }
242 |
243 | public enum Reason: Hashable {
244 | case unexpectedEOF
245 | case expectedAtom
246 | case expectedIdentifier
247 | case expectedOperator
248 | case expectedKeyword(String)
249 | case expected(String)
250 | case unexpectedRemainder(String)
251 | }
252 |
253 | extension String {
254 | public func parse() throws -> AnnotatedExpression {
255 | var context = self[...]
256 | let result = try context.parseExpression()
257 | guard context.isEmpty else {
258 | throw context.err(.unexpectedRemainder(String(context)))
259 | }
260 | return result
261 | }
262 |
263 | }
264 |
--------------------------------------------------------------------------------