├── .gitignore ├── LICENSE ├── Package.swift ├── README.md ├── Sources ├── Info.plist ├── Parser.swift ├── Reader.swift ├── StringReader.swift └── SwiftParser.h ├── SwiftParser.xcodeproj ├── Configs │ └── Project.xcconfig ├── SwiftParserTests_Info.plist ├── SwiftParser_Info.plist ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── SwiftParser.xccheckout └── xcshareddata │ └── xcschemes │ ├── SwiftParser-Package.xcscheme │ ├── SwiftParser.xcscheme │ └── xcschememanagement.plist └── Tests └── SwiftParserTests ├── Info.plist └── SwiftParserTests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | xcuserdata 2 | .DS_Store 3 | .build 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:4.0 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "SwiftParser", 6 | 7 | products: [ 8 | .library(name: "SwiftParser", targets: ["SwiftParser"]), 9 | ], 10 | 11 | dependencies: [ 12 | ], 13 | 14 | targets: [ 15 | .testTarget(name: "SwiftParserTests", dependencies: ["SwiftParser"]), 16 | .target(name: "SwiftParser", path: "Sources") 17 | ] 18 | ) 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | swift-parser-generator 2 | ====================== 3 | 4 | This code contains an attempt to make something like the Scala parser combinators in Swift. It has been 5 | partially successful in that it can be used to make simple parsers. Intermediate parsing results are cached 6 | for complex rules, so that parsing of complex expressions should still be linear-time (cf. Packrat). 7 | 8 | How it works 9 | ============ 10 | 11 | Using operator overloading a nested set of functions is created that forms the parser. The functions take 12 | a parser instance and a reader object that provides the function with characters to parse. 13 | 14 | The following parsing operations are currently supported: 15 | 16 | ```swift 17 | // "a" followed by "b" 18 | let rule = "a" ~ "b" 19 | 20 | // "a" followed by "b", with possible whitespace in between 21 | // to change what is considered whitespace, change the parser.whitespace rule 22 | let rule = "a" ~~ "b" 23 | 24 | // at least one "a", but possibly more 25 | let rule = "a"++ 26 | 27 | // "a" or "b" 28 | let rule = "a" | "b" 29 | 30 | // "a" followed by something other than "b" 31 | let rule = "a" ~ !"b" 32 | 33 | // "a" followed by one or more "b" 34 | let rule = "a" ~ "b"+ 35 | 36 | // "a" followed by zero or more "b" 37 | let rule = "a" ~ "b"* 38 | 39 | // "a" followed by a numeric digit 40 | let rule = "a" ~ ("0"-"9") 41 | 42 | // "a" followed by the rule named "blah" 43 | let rule = "a" ~ ^"blah" 44 | 45 | // "a" optionally followed by "b" 46 | let rule = "a" ~ "b"/~ 47 | 48 | // "a" followed by the end of input 49 | let rule = "a"*!* 50 | 51 | // a single "," 52 | let rule = %"," 53 | 54 | // regular expression: consecutive word or space characters 55 | let rule = %!"[\\w\\s]+" 56 | ``` 57 | To have the parser call your code when a rule matches use the => operator. For example: 58 | 59 | ```swift 60 | import SwiftParser 61 | class Adder : Parser { 62 | var stack: Int[] = [] 63 | 64 | func push(_ text: String) { 65 | stack.append(text.toInt()!) 66 | } 67 | 68 | func add() { 69 | let left = stack.removeLast() 70 | let right = stack.removeLast() 71 | 72 | stack.append(left + right) 73 | } 74 | 75 | override init() { 76 | super.init() 77 | 78 | self.grammar = Grammar { [unowned self] g in 79 | g["number"] = ("0"-"9")+ => { [unowned self] parser in self.push(parser.text) } 80 | return (^"number" ~ "+" ~ ^"number") => add 81 | } 82 | } 83 | } 84 | ``` 85 | 86 | This example displays several details about how to work with the parser. The parser is defined in an object called `grammar` which defines named rules as well as a start rule, to tell the parser where to begin. 87 | 88 | The following code snippet is taken from one of the unit tests. It show how to implement a parser containing mutually recursive rules: 89 | 90 | ```swift 91 | self.grammar = Grammar { [unowned self] g in 92 | g["number"] = ("0"-"9")+ => { [unowned self] parser in self.push(parser.text) } 93 | 94 | g["primary"] = ^"secondary" ~ (("+" ~ ^"secondary" => add) | ("-" ~ ^"secondary" => sub))* 95 | g["secondary"] = ^"tertiary" ~ (("*" ~ ^"tertiary" => mul) | ("/" ~ ^"tertiary" => div))* 96 | g["tertiary"] = ("(" ~ ^"primary" ~ ")") | ^"number" 97 | 98 | return (^"primary")*!* 99 | } 100 | ``` 101 | 102 | ### Installation 103 | 104 | #### Swift Package Manager (SPM) 105 | 106 | You can install the driver using Swift Package Manager by adding the following line to your ```Package.swift``` as a dependency: 107 | 108 | ``` 109 | .Package(url: "https://github.com/dparnell/swift-parser-generator.git", majorVersion: 1) 110 | ``` 111 | 112 | To use the driver in an Xcode project, generate an Xcode project file using SPM: 113 | ``` 114 | swift package generate-xcodeproj 115 | ``` 116 | 117 | #### Manual 118 | 119 | Drag SwiftParser.xcodeproj into your own project, then add SwiftParser as dependency (build targets) and link to it. 120 | You should then be able to simply 'import SwiftParser' from Swift code. 121 | -------------------------------------------------------------------------------- /Sources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSHumanReadableCopyright 24 | Copyright © 2014 Daniel Parnell. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Sources/Parser.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public typealias ParserFunction = (_ parser: Parser, _ reader: Reader) throws -> Bool 4 | public typealias ParserAction = (_ parser: Parser) throws -> () 5 | public typealias ParserActionWithoutParameter = () throws -> () 6 | 7 | /** Definition of a grammar for the parser. Can be reused between multiple parsings. */ 8 | public class Grammar { 9 | /** This rule determines what is seen as 'whitespace' by the '~~' operator, which allows 10 | whitespace between two following items.*/ 11 | public var whitespace: ParserRule = (" " | "\t" | "\r\n" | "\r" | "\n")* 12 | 13 | public var nestingDepthLimit: Int? = nil 14 | 15 | /** The start rule for this grammar. */ 16 | public var startRule: ParserRule! = nil 17 | internal var namedRules: [String: ParserRule] = [:] 18 | 19 | public init() { 20 | } 21 | 22 | public init(_ creator: (Grammar) -> (ParserRule)) { 23 | self.startRule = creator(self) 24 | } 25 | 26 | public subscript(name: String) -> ParserRule { 27 | get { 28 | return self.namedRules[name]! 29 | } 30 | set(newValue) { 31 | self.namedRules[name] = newValue 32 | } 33 | } 34 | } 35 | 36 | open class Parser { 37 | public struct ParserCapture : CustomStringConvertible { 38 | public var start: Int 39 | public var end: Int 40 | public var action: ParserAction 41 | let reader: Reader 42 | 43 | var text: String { 44 | return reader.substring(start, ending_at:end) 45 | } 46 | 47 | public var description: String { 48 | return "[\(start),\(end):\(text)]" 49 | } 50 | } 51 | 52 | public var debugRules = false 53 | public var captures: [ParserCapture] = [] 54 | public var currentCapture: ParserCapture? 55 | public var lastCapture: ParserCapture? 56 | public var currentReader: Reader? 57 | public var grammar: Grammar 58 | internal var matches: [ParserRule: [Int: Bool]] = [:] 59 | internal var nestingDepth = 0 60 | 61 | public var text: String { 62 | get { 63 | if let capture = currentCapture { 64 | return capture.text 65 | } 66 | 67 | return "" 68 | } 69 | } 70 | 71 | public init() { 72 | self.grammar = Grammar() 73 | } 74 | 75 | public init(grammar: Grammar) { 76 | self.grammar = grammar 77 | } 78 | 79 | public func parse(_ string: String) throws -> Bool { 80 | matches.removeAll(keepingCapacity: false) 81 | captures.removeAll(keepingCapacity: false) 82 | currentCapture = nil 83 | lastCapture = nil 84 | 85 | defer { 86 | currentReader = nil 87 | currentCapture = nil 88 | lastCapture = nil 89 | matches.removeAll(keepingCapacity: false) 90 | captures.removeAll(keepingCapacity:false) 91 | } 92 | 93 | let reader = StringReader(string: string) 94 | 95 | if try grammar.startRule!.matches(self, reader) { 96 | currentReader = reader 97 | 98 | for capture in captures { 99 | lastCapture = currentCapture 100 | currentCapture = capture 101 | try capture.action(self) 102 | } 103 | return true 104 | } 105 | 106 | return false 107 | } 108 | 109 | var depth = 0 110 | 111 | func leave(_ name: String) { 112 | if(debugRules) { 113 | self.out("-- \(name)") 114 | } 115 | depth -= 1 116 | } 117 | 118 | func leave(_ name: String, _ res: Bool) { 119 | if(debugRules) { 120 | self.out("-- \(name):\t\(res)") 121 | } 122 | depth -= 1 123 | } 124 | 125 | func enter(_ name: String) { 126 | depth += 1 127 | if(debugRules) { 128 | self.out("++ \(name)") 129 | } 130 | } 131 | 132 | func out(_ name: String) { 133 | var spaces = "" 134 | for _ in 0.. Bool { 149 | return try self.function(parser, reader) 150 | } 151 | 152 | public var hashValue: Int { 153 | return ObjectIdentifier(self).hashValue 154 | } 155 | 156 | public static func ==(lhs: ParserRule, rhs: ParserRule) -> Bool { 157 | return ObjectIdentifier(lhs) == ObjectIdentifier(rhs) 158 | } 159 | } 160 | 161 | public final class ParserMemoizingRule: ParserRule { 162 | public override func matches(_ parser: Parser, _ reader: Reader) throws -> Bool { 163 | let position = reader.position 164 | if let m = parser.matches[self]?[position] { 165 | return m 166 | } 167 | let r = try self.function(parser, reader) 168 | 169 | if parser.matches[self] == nil { 170 | parser.matches[self] = [position: r] 171 | } 172 | else { 173 | parser.matches[self]![position] = r 174 | } 175 | return r 176 | } 177 | } 178 | 179 | // EOF operator 180 | postfix operator *!* 181 | 182 | public postfix func *!* (rule: ParserRule) -> ParserRule { 183 | return ParserRule { (parser: Parser, reader: Reader) -> Bool in 184 | return try rule.matches(parser, reader) && reader.eof() 185 | } 186 | } 187 | 188 | // call a named rule - this allows for cycles, so be careful! 189 | prefix operator ^ 190 | public prefix func ^(name:String) -> ParserRule { 191 | return ParserMemoizingRule { (parser: Parser, reader: Reader) -> Bool in 192 | // TODO: check stack to see if this named rule is already on there to prevent loops 193 | parser.enter("named rule: \(name)") 194 | let result = try parser.grammar[name].function(parser, reader) 195 | parser.leave("named rule: \(name)",result) 196 | return result 197 | } 198 | } 199 | 200 | // match a regex 201 | #if !os(Linux) 202 | prefix operator %! 203 | public prefix func %!(pattern:String) -> ParserRule { 204 | return ParserRule { (parser: Parser, reader: Reader) -> Bool in 205 | parser.enter("regex '\(pattern)'") 206 | 207 | let pos = reader.position 208 | 209 | var found = true 210 | let remainder = reader.remainder() 211 | do { 212 | let re = try NSRegularExpression(pattern: pattern, options: []) 213 | let target = remainder as NSString 214 | let match = re.firstMatch(in: remainder, options: [], range: NSMakeRange(0, target.length)) 215 | if let m = match { 216 | let res = target.substring(with: m.range) 217 | // reset to end of match 218 | reader.seek(pos + res.characters.count) 219 | 220 | parser.leave("regex", true) 221 | return true 222 | } 223 | } catch { 224 | found = false 225 | } 226 | 227 | if(!found) { 228 | reader.seek(pos) 229 | parser.leave("regex", false) 230 | } 231 | return false 232 | } 233 | } 234 | #endif 235 | 236 | public enum ParserError: LocalizedError { 237 | case nestingDepthLimitExceeded 238 | 239 | public var errorDescription: String? { 240 | switch self { 241 | case .nestingDepthLimitExceeded: return "nesting depth limit exceeded" 242 | } 243 | } 244 | } 245 | 246 | /** Wrap the rule with a nesting depth check. All rules wrapped with this check also increase the 247 | current nesting depth. */ 248 | public func nest(_ rule: ParserRule) -> ParserRule { 249 | return ParserRule { (parser: Parser, reader: Reader) throws -> Bool in 250 | parser.nestingDepth += 1 251 | defer { parser.nestingDepth -= 1 } 252 | if let nd = parser.grammar.nestingDepthLimit, parser.nestingDepth > nd { 253 | throw ParserError.nestingDepthLimitExceeded 254 | } 255 | 256 | return try rule.matches(parser, reader) 257 | } 258 | } 259 | 260 | // match a literal string 261 | prefix operator % 262 | public prefix func %(lit:String) -> ParserRule { 263 | return literal(lit) 264 | } 265 | 266 | public func literal(_ string:String) -> ParserRule { 267 | return ParserRule { (parser: Parser, reader: Reader) -> Bool in 268 | parser.enter("literal '\(string)'") 269 | 270 | let pos = reader.position 271 | 272 | for ch in string.characters { 273 | let flag = ch == reader.read() 274 | if !flag { 275 | reader.seek(pos) 276 | 277 | parser.leave("literal", false) 278 | return false 279 | } 280 | } 281 | 282 | parser.leave("literal", true) 283 | return true 284 | } 285 | } 286 | 287 | // match a range of characters eg: "0"-"9" 288 | public func - (left: Character, right: Character) -> ParserRule { 289 | return ParserRule { (parser: Parser, reader: Reader) -> Bool in 290 | parser.enter("range [\(left)-\(right)]") 291 | 292 | let pos = reader.position 293 | 294 | let lower = String(left) 295 | let upper = String(right) 296 | let ch = String(reader.read()) 297 | let found = (lower <= ch) && (ch <= upper) 298 | parser.leave("range \t\t\(ch)", found) 299 | 300 | if(!found) { 301 | reader.seek(pos) 302 | } 303 | 304 | return found 305 | } 306 | } 307 | 308 | // invert match 309 | public prefix func !(rule: ParserRule) -> ParserRule { 310 | return ParserRule { (parser: Parser, reader: Reader) -> Bool in 311 | return try !rule.matches(parser, reader) 312 | } 313 | } 314 | 315 | public prefix func !(lit: String) -> ParserRule { 316 | return !literal(lit) 317 | } 318 | 319 | // match one or more 320 | postfix operator + 321 | public postfix func + (rule: ParserRule) -> ParserRule { 322 | return ParserRule { (parser: Parser, reader: Reader) -> Bool in 323 | let pos = reader.position 324 | var found = false 325 | var flag: Bool 326 | 327 | parser.enter("one or more") 328 | 329 | repeat { 330 | flag = try rule.matches(parser, reader) 331 | found = found || flag 332 | } while(flag) 333 | 334 | if(!found) { 335 | reader.seek(pos) 336 | } 337 | 338 | parser.leave("one or more", found) 339 | return found 340 | } 341 | } 342 | 343 | public postfix func + (lit: String) -> ParserRule { 344 | return literal(lit)+ 345 | } 346 | 347 | 348 | // match zero or more 349 | postfix operator * 350 | public postfix func * (rule: ParserRule) -> ParserRule { 351 | return ParserRule { (parser: Parser, reader: Reader) -> Bool in 352 | var flag: Bool 353 | var matched = false 354 | parser.enter("zero or more") 355 | 356 | repeat { 357 | let pos = reader.position 358 | flag = try rule.matches(parser, reader) 359 | if(!flag) { 360 | reader.seek(pos) 361 | } else { 362 | matched = true 363 | } 364 | } while(flag) 365 | 366 | parser.leave("zero or more", matched) 367 | return true 368 | } 369 | } 370 | 371 | public postfix func * (lit: String) -> ParserRule { 372 | return literal(lit)* 373 | } 374 | 375 | // optional 376 | postfix operator /~ 377 | public postfix func /~ (rule: ParserRule) -> ParserRule { 378 | return ParserRule { (parser: Parser, reader: Reader) -> Bool in 379 | parser.enter("optionally") 380 | 381 | let pos = reader.position 382 | if(try !rule.matches(parser, reader)) { 383 | reader.seek(pos) 384 | } 385 | 386 | parser.leave("optionally", true) 387 | return true 388 | } 389 | } 390 | 391 | public postfix func /~ (lit: String) -> ParserRule { 392 | return literal(lit)/~ 393 | } 394 | 395 | // match either 396 | public func | (left: String, right: String) -> ParserRule { 397 | return literal(left) | literal(right) 398 | } 399 | 400 | public func | (left: String, right: ParserRule) -> ParserRule { 401 | return literal(left) | right 402 | } 403 | 404 | public func | (left: ParserRule, right: String) -> ParserRule { 405 | return left | literal(right) 406 | } 407 | 408 | public func | (left: ParserRule, right: ParserRule) -> ParserRule { 409 | return ParserMemoizingRule { (parser: Parser, reader: Reader) -> Bool in 410 | parser.enter("|") 411 | let pos = reader.position 412 | var result = try left.matches(parser, reader) 413 | if(!result) { 414 | reader.seek(pos) 415 | result = try right.matches(parser, reader) 416 | } 417 | 418 | if(!result) { 419 | reader.seek(pos) 420 | } 421 | 422 | parser.leave("|", result) 423 | return result 424 | } 425 | } 426 | 427 | precedencegroup MinPrecedence { 428 | associativity: left 429 | higherThan: AssignmentPrecedence 430 | } 431 | 432 | precedencegroup MaxPrecedence { 433 | associativity: left 434 | higherThan: MinPrecedence 435 | } 436 | 437 | // match all 438 | infix operator ~ : MinPrecedence 439 | 440 | public func ~ (left: String, right: String) -> ParserRule { 441 | return literal(left) ~ literal(right) 442 | } 443 | 444 | public func ~ (left: String, right: ParserRule) -> ParserRule { 445 | return literal(left) ~ right 446 | } 447 | 448 | public func ~ (left: ParserRule, right: String) -> ParserRule { 449 | return left ~ literal(right) 450 | } 451 | 452 | public func ~ (left : ParserRule, right: ParserRule) -> ParserRule { 453 | return ParserRule { (parser: Parser, reader: Reader) -> Bool in 454 | parser.enter("~") 455 | let res = try left.matches(parser, reader) && right.matches(parser, reader) 456 | parser.leave("~", res) 457 | return res 458 | } 459 | } 460 | 461 | // on match 462 | infix operator => : MaxPrecedence 463 | 464 | public func => (rule : ParserRule, action: @escaping ParserAction) -> ParserRule { 465 | return ParserRule { (parser: Parser, reader: Reader) -> Bool in 466 | let start = reader.position 467 | let capture_count = parser.captures.count 468 | 469 | parser.enter("=>") 470 | 471 | if(try rule.matches(parser, reader)) { 472 | let capture = Parser.ParserCapture(start: start, end: reader.position, action: action, reader: reader) 473 | 474 | parser.captures.append(capture) 475 | parser.leave("=>", true) 476 | return true 477 | } 478 | 479 | while(parser.captures.count > capture_count) { 480 | parser.captures.removeLast() 481 | } 482 | parser.leave("=>", false) 483 | return false 484 | } 485 | } 486 | 487 | public func => (rule : ParserRule, action: @escaping ParserActionWithoutParameter) -> ParserRule { 488 | return rule => { _ in 489 | try action() 490 | } 491 | } 492 | 493 | /** The ~~ operator matches two following elements, optionally with whitespace (Parser.whitespace) in between. */ 494 | infix operator ~~ : MinPrecedence 495 | 496 | public func ~~ (left: String, right: String) -> ParserRule { 497 | return literal(left) ~~ literal(right) 498 | } 499 | 500 | public func ~~ (left: String, right: ParserRule) -> ParserRule { 501 | return literal(left) ~~ right 502 | } 503 | 504 | public func ~~ (left: ParserRule, right: String) -> ParserRule { 505 | return left ~~ literal(right) 506 | } 507 | 508 | public func ~~ (left : ParserRule, right: ParserRule) -> ParserRule { 509 | return ParserRule { (parser: Parser, reader: Reader) -> Bool in 510 | return try left.matches(parser, reader) 511 | && parser.grammar.whitespace.matches(parser, reader) 512 | && right.matches(parser, reader) 513 | } 514 | } 515 | 516 | /** Parser rule that matches the given parser rule at least once, but possibly more */ 517 | public postfix func ++ (left: ParserRule) -> ParserRule { 518 | return left ~~ left* 519 | } 520 | 521 | -------------------------------------------------------------------------------- /Sources/Reader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Reader.swift 3 | // SwiftParser 4 | // 5 | // Created by Daniel Parnell on 17/06/2014. 6 | // Copyright (c) 2014 Daniel Parnell. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol Reader { 12 | var position: Int { get } 13 | 14 | func seek(_ position: Int) 15 | func read() -> Character 16 | func substring(_ starting_at:Int, ending_at:Int) -> String 17 | func eof() -> Bool 18 | func remainder() -> String 19 | } 20 | -------------------------------------------------------------------------------- /Sources/StringReader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StringReader.swift 3 | // SwiftParser 4 | // 5 | // Created by Daniel Parnell on 17/06/2014. 6 | // Copyright (c) 2014 Daniel Parnell. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class StringReader : Reader { 12 | var string: String 13 | var index: String.Index 14 | 15 | public var position: Int { 16 | get { 17 | return string.distance(from: string.startIndex, to: index) 18 | } 19 | } 20 | 21 | init(string: String) { 22 | self.string = string 23 | index = string.startIndex; 24 | } 25 | 26 | public func seek(_ position:Int) { 27 | index = string.index(string.startIndex, offsetBy: position) 28 | } 29 | 30 | public func read() -> Character { 31 | if index != string.endIndex { 32 | let result = string[index] 33 | index = string.index(after: index) 34 | 35 | return result; 36 | } 37 | 38 | return "\u{2004}"; 39 | } 40 | 41 | public func eof() -> Bool { 42 | return index == string.endIndex 43 | } 44 | 45 | public func remainder() -> String { 46 | return String(string[index...]) 47 | } 48 | 49 | public func substring(_ starting_at:Int, ending_at:Int) -> String { 50 | return String(string[string.index(string.startIndex, offsetBy: starting_at).. 10 | 11 | //! Project version number for SwiftParser. 12 | FOUNDATION_EXPORT double SwiftParserVersionNumber; 13 | 14 | //! Project version string for SwiftParser. 15 | FOUNDATION_EXPORT const unsigned char SwiftParserVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /SwiftParser.xcodeproj/Configs/Project.xcconfig: -------------------------------------------------------------------------------- 1 | PRODUCT_NAME = $(TARGET_NAME) 2 | SUPPORTED_PLATFORMS = macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator 3 | MACOSX_DEPLOYMENT_TARGET = 10.10 4 | DYLIB_INSTALL_NAME_BASE = @rpath 5 | OTHER_SWIFT_FLAGS = -DXcode 6 | COMBINE_HIDPI_IMAGES = YES 7 | USE_HEADERMAP = NO 8 | -------------------------------------------------------------------------------- /SwiftParser.xcodeproj/SwiftParserTests_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | BNDL 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /SwiftParser.xcodeproj/SwiftParser_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /SwiftParser.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXAggregateTarget section */ 10 | "SwiftParser::SwiftParserPackageTests::ProductTarget" /* SwiftParserPackageTests */ = { 11 | isa = PBXAggregateTarget; 12 | buildConfigurationList = OBJ_42 /* Build configuration list for PBXAggregateTarget "SwiftParserPackageTests" */; 13 | buildPhases = ( 14 | ); 15 | dependencies = ( 16 | OBJ_45 /* PBXTargetDependency */, 17 | ); 18 | name = SwiftParserPackageTests; 19 | productName = SwiftParserPackageTests; 20 | }; 21 | /* End PBXAggregateTarget section */ 22 | 23 | /* Begin PBXBuildFile section */ 24 | OBJ_22 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_6 /* Package.swift */; }; 25 | OBJ_28 /* SwiftParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_13 /* SwiftParserTests.swift */; }; 26 | OBJ_30 /* SwiftParser.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "SwiftParser::SwiftParser::Product" /* SwiftParser.framework */; }; 27 | OBJ_37 /* Parser.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_8 /* Parser.swift */; }; 28 | OBJ_38 /* Reader.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* Reader.swift */; }; 29 | OBJ_39 /* StringReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_10 /* StringReader.swift */; }; 30 | /* End PBXBuildFile section */ 31 | 32 | /* Begin PBXContainerItemProxy section */ 33 | 657BA80B1FA8BC8800AC9AE0 /* PBXContainerItemProxy */ = { 34 | isa = PBXContainerItemProxy; 35 | containerPortal = OBJ_1 /* Project object */; 36 | proxyType = 1; 37 | remoteGlobalIDString = "SwiftParser::SwiftParser"; 38 | remoteInfo = SwiftParser; 39 | }; 40 | 657BA80C1FA8BC8900AC9AE0 /* PBXContainerItemProxy */ = { 41 | isa = PBXContainerItemProxy; 42 | containerPortal = OBJ_1 /* Project object */; 43 | proxyType = 1; 44 | remoteGlobalIDString = "SwiftParser::SwiftParserTests"; 45 | remoteInfo = SwiftParserTests; 46 | }; 47 | /* End PBXContainerItemProxy section */ 48 | 49 | /* Begin PBXFileReference section */ 50 | OBJ_10 /* StringReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringReader.swift; sourceTree = ""; }; 51 | OBJ_13 /* SwiftParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftParserTests.swift; sourceTree = ""; }; 52 | OBJ_6 /* Package.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; 53 | OBJ_8 /* Parser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Parser.swift; sourceTree = ""; }; 54 | OBJ_9 /* Reader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Reader.swift; sourceTree = ""; }; 55 | "SwiftParser::SwiftParser::Product" /* SwiftParser.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SwiftParser.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 56 | "SwiftParser::SwiftParserTests::Product" /* SwiftParserTests.xctest */ = {isa = PBXFileReference; lastKnownFileType = file; path = SwiftParserTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 57 | /* End PBXFileReference section */ 58 | 59 | /* Begin PBXFrameworksBuildPhase section */ 60 | OBJ_29 /* Frameworks */ = { 61 | isa = PBXFrameworksBuildPhase; 62 | buildActionMask = 0; 63 | files = ( 64 | OBJ_30 /* SwiftParser.framework in Frameworks */, 65 | ); 66 | runOnlyForDeploymentPostprocessing = 0; 67 | }; 68 | OBJ_40 /* Frameworks */ = { 69 | isa = PBXFrameworksBuildPhase; 70 | buildActionMask = 0; 71 | files = ( 72 | ); 73 | runOnlyForDeploymentPostprocessing = 0; 74 | }; 75 | /* End PBXFrameworksBuildPhase section */ 76 | 77 | /* Begin PBXGroup section */ 78 | OBJ_11 /* Tests */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | OBJ_12 /* SwiftParserTests */, 82 | ); 83 | name = Tests; 84 | sourceTree = SOURCE_ROOT; 85 | }; 86 | OBJ_12 /* SwiftParserTests */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | OBJ_13 /* SwiftParserTests.swift */, 90 | ); 91 | name = SwiftParserTests; 92 | path = Tests/SwiftParserTests; 93 | sourceTree = SOURCE_ROOT; 94 | }; 95 | OBJ_14 /* Products */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | "SwiftParser::SwiftParserTests::Product" /* SwiftParserTests.xctest */, 99 | "SwiftParser::SwiftParser::Product" /* SwiftParser.framework */, 100 | ); 101 | name = Products; 102 | sourceTree = BUILT_PRODUCTS_DIR; 103 | }; 104 | OBJ_5 /* */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | OBJ_6 /* Package.swift */, 108 | OBJ_7 /* Sources */, 109 | OBJ_11 /* Tests */, 110 | OBJ_14 /* Products */, 111 | ); 112 | name = ""; 113 | sourceTree = ""; 114 | }; 115 | OBJ_7 /* Sources */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | OBJ_8 /* Parser.swift */, 119 | OBJ_9 /* Reader.swift */, 120 | OBJ_10 /* StringReader.swift */, 121 | ); 122 | path = Sources; 123 | sourceTree = SOURCE_ROOT; 124 | }; 125 | /* End PBXGroup section */ 126 | 127 | /* Begin PBXNativeTarget section */ 128 | "SwiftParser::SwiftPMPackageDescription" /* SwiftParserPackageDescription */ = { 129 | isa = PBXNativeTarget; 130 | buildConfigurationList = OBJ_18 /* Build configuration list for PBXNativeTarget "SwiftParserPackageDescription" */; 131 | buildPhases = ( 132 | OBJ_21 /* Sources */, 133 | ); 134 | buildRules = ( 135 | ); 136 | dependencies = ( 137 | ); 138 | name = SwiftParserPackageDescription; 139 | productName = SwiftParserPackageDescription; 140 | productType = "com.apple.product-type.framework"; 141 | }; 142 | "SwiftParser::SwiftParser" /* SwiftParser */ = { 143 | isa = PBXNativeTarget; 144 | buildConfigurationList = OBJ_33 /* Build configuration list for PBXNativeTarget "SwiftParser" */; 145 | buildPhases = ( 146 | OBJ_36 /* Sources */, 147 | OBJ_40 /* Frameworks */, 148 | ); 149 | buildRules = ( 150 | ); 151 | dependencies = ( 152 | ); 153 | name = SwiftParser; 154 | productName = SwiftParser; 155 | productReference = "SwiftParser::SwiftParser::Product" /* SwiftParser.framework */; 156 | productType = "com.apple.product-type.framework"; 157 | }; 158 | "SwiftParser::SwiftParserTests" /* SwiftParserTests */ = { 159 | isa = PBXNativeTarget; 160 | buildConfigurationList = OBJ_24 /* Build configuration list for PBXNativeTarget "SwiftParserTests" */; 161 | buildPhases = ( 162 | OBJ_27 /* Sources */, 163 | OBJ_29 /* Frameworks */, 164 | ); 165 | buildRules = ( 166 | ); 167 | dependencies = ( 168 | OBJ_31 /* PBXTargetDependency */, 169 | ); 170 | name = SwiftParserTests; 171 | productName = SwiftParserTests; 172 | productReference = "SwiftParser::SwiftParserTests::Product" /* SwiftParserTests.xctest */; 173 | productType = "com.apple.product-type.bundle.unit-test"; 174 | }; 175 | /* End PBXNativeTarget section */ 176 | 177 | /* Begin PBXProject section */ 178 | OBJ_1 /* Project object */ = { 179 | isa = PBXProject; 180 | attributes = { 181 | LastUpgradeCheck = 9999; 182 | }; 183 | buildConfigurationList = OBJ_2 /* Build configuration list for PBXProject "SwiftParser" */; 184 | compatibilityVersion = "Xcode 3.2"; 185 | developmentRegion = English; 186 | hasScannedForEncodings = 0; 187 | knownRegions = ( 188 | en, 189 | ); 190 | mainGroup = OBJ_5 /* */; 191 | productRefGroup = OBJ_14 /* Products */; 192 | projectDirPath = ""; 193 | projectRoot = ""; 194 | targets = ( 195 | "SwiftParser::SwiftPMPackageDescription" /* SwiftParserPackageDescription */, 196 | "SwiftParser::SwiftParserTests" /* SwiftParserTests */, 197 | "SwiftParser::SwiftParser" /* SwiftParser */, 198 | "SwiftParser::SwiftParserPackageTests::ProductTarget" /* SwiftParserPackageTests */, 199 | ); 200 | }; 201 | /* End PBXProject section */ 202 | 203 | /* Begin PBXSourcesBuildPhase section */ 204 | OBJ_21 /* Sources */ = { 205 | isa = PBXSourcesBuildPhase; 206 | buildActionMask = 0; 207 | files = ( 208 | OBJ_22 /* Package.swift in Sources */, 209 | ); 210 | runOnlyForDeploymentPostprocessing = 0; 211 | }; 212 | OBJ_27 /* Sources */ = { 213 | isa = PBXSourcesBuildPhase; 214 | buildActionMask = 0; 215 | files = ( 216 | OBJ_28 /* SwiftParserTests.swift in Sources */, 217 | ); 218 | runOnlyForDeploymentPostprocessing = 0; 219 | }; 220 | OBJ_36 /* Sources */ = { 221 | isa = PBXSourcesBuildPhase; 222 | buildActionMask = 0; 223 | files = ( 224 | OBJ_37 /* Parser.swift in Sources */, 225 | OBJ_38 /* Reader.swift in Sources */, 226 | OBJ_39 /* StringReader.swift in Sources */, 227 | ); 228 | runOnlyForDeploymentPostprocessing = 0; 229 | }; 230 | /* End PBXSourcesBuildPhase section */ 231 | 232 | /* Begin PBXTargetDependency section */ 233 | OBJ_31 /* PBXTargetDependency */ = { 234 | isa = PBXTargetDependency; 235 | target = "SwiftParser::SwiftParser" /* SwiftParser */; 236 | targetProxy = 657BA80B1FA8BC8800AC9AE0 /* PBXContainerItemProxy */; 237 | }; 238 | OBJ_45 /* PBXTargetDependency */ = { 239 | isa = PBXTargetDependency; 240 | target = "SwiftParser::SwiftParserTests" /* SwiftParserTests */; 241 | targetProxy = 657BA80C1FA8BC8900AC9AE0 /* PBXContainerItemProxy */; 242 | }; 243 | /* End PBXTargetDependency section */ 244 | 245 | /* Begin XCBuildConfiguration section */ 246 | OBJ_19 /* Debug */ = { 247 | isa = XCBuildConfiguration; 248 | buildSettings = { 249 | LD = /usr/bin/true; 250 | OTHER_SWIFT_FLAGS = "-swift-version 4 -I /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/pm/4 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk"; 251 | SWIFT_VERSION = 4.0; 252 | }; 253 | name = Debug; 254 | }; 255 | OBJ_20 /* Release */ = { 256 | isa = XCBuildConfiguration; 257 | buildSettings = { 258 | LD = /usr/bin/true; 259 | OTHER_SWIFT_FLAGS = "-swift-version 4 -I /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/pm/4 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk"; 260 | SWIFT_VERSION = 4.0; 261 | }; 262 | name = Release; 263 | }; 264 | OBJ_25 /* Debug */ = { 265 | isa = XCBuildConfiguration; 266 | buildSettings = { 267 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; 268 | FRAMEWORK_SEARCH_PATHS = ( 269 | "$(inherited)", 270 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 271 | ); 272 | HEADER_SEARCH_PATHS = "$(inherited)"; 273 | INFOPLIST_FILE = SwiftParser.xcodeproj/SwiftParserTests_Info.plist; 274 | LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks @loader_path/Frameworks"; 275 | OTHER_LDFLAGS = "$(inherited)"; 276 | OTHER_SWIFT_FLAGS = "$(inherited)"; 277 | SWIFT_VERSION = 4.0; 278 | TARGET_NAME = SwiftParserTests; 279 | }; 280 | name = Debug; 281 | }; 282 | OBJ_26 /* Release */ = { 283 | isa = XCBuildConfiguration; 284 | buildSettings = { 285 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; 286 | FRAMEWORK_SEARCH_PATHS = ( 287 | "$(inherited)", 288 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 289 | ); 290 | HEADER_SEARCH_PATHS = "$(inherited)"; 291 | INFOPLIST_FILE = SwiftParser.xcodeproj/SwiftParserTests_Info.plist; 292 | LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks @loader_path/Frameworks"; 293 | OTHER_LDFLAGS = "$(inherited)"; 294 | OTHER_SWIFT_FLAGS = "$(inherited)"; 295 | SWIFT_VERSION = 4.0; 296 | TARGET_NAME = SwiftParserTests; 297 | }; 298 | name = Release; 299 | }; 300 | OBJ_3 /* Debug */ = { 301 | isa = XCBuildConfiguration; 302 | buildSettings = { 303 | CLANG_ENABLE_OBJC_ARC = YES; 304 | COMBINE_HIDPI_IMAGES = YES; 305 | COPY_PHASE_STRIP = NO; 306 | DEBUG_INFORMATION_FORMAT = dwarf; 307 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 308 | ENABLE_NS_ASSERTIONS = YES; 309 | GCC_OPTIMIZATION_LEVEL = 0; 310 | MACOSX_DEPLOYMENT_TARGET = 10.10; 311 | ONLY_ACTIVE_ARCH = YES; 312 | OTHER_SWIFT_FLAGS = "-DXcode"; 313 | PRODUCT_NAME = "$(TARGET_NAME)"; 314 | SDKROOT = macosx; 315 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; 316 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = SWIFT_PACKAGE; 317 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 318 | USE_HEADERMAP = NO; 319 | }; 320 | name = Debug; 321 | }; 322 | OBJ_34 /* Debug */ = { 323 | isa = XCBuildConfiguration; 324 | buildSettings = { 325 | ENABLE_TESTABILITY = YES; 326 | FRAMEWORK_SEARCH_PATHS = ( 327 | "$(inherited)", 328 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 329 | ); 330 | HEADER_SEARCH_PATHS = "$(inherited)"; 331 | INFOPLIST_FILE = SwiftParser.xcodeproj/SwiftParser_Info.plist; 332 | LD_RUNPATH_SEARCH_PATHS = "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; 333 | OTHER_LDFLAGS = "$(inherited)"; 334 | OTHER_SWIFT_FLAGS = "$(inherited)"; 335 | PRODUCT_BUNDLE_IDENTIFIER = SwiftParser; 336 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; 337 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 338 | SKIP_INSTALL = YES; 339 | SWIFT_VERSION = 4.0; 340 | TARGET_NAME = SwiftParser; 341 | }; 342 | name = Debug; 343 | }; 344 | OBJ_35 /* Release */ = { 345 | isa = XCBuildConfiguration; 346 | buildSettings = { 347 | ENABLE_TESTABILITY = YES; 348 | FRAMEWORK_SEARCH_PATHS = ( 349 | "$(inherited)", 350 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 351 | ); 352 | HEADER_SEARCH_PATHS = "$(inherited)"; 353 | INFOPLIST_FILE = SwiftParser.xcodeproj/SwiftParser_Info.plist; 354 | LD_RUNPATH_SEARCH_PATHS = "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; 355 | OTHER_LDFLAGS = "$(inherited)"; 356 | OTHER_SWIFT_FLAGS = "$(inherited)"; 357 | PRODUCT_BUNDLE_IDENTIFIER = SwiftParser; 358 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; 359 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 360 | SKIP_INSTALL = YES; 361 | SWIFT_VERSION = 4.0; 362 | TARGET_NAME = SwiftParser; 363 | }; 364 | name = Release; 365 | }; 366 | OBJ_4 /* Release */ = { 367 | isa = XCBuildConfiguration; 368 | buildSettings = { 369 | CLANG_ENABLE_OBJC_ARC = YES; 370 | COMBINE_HIDPI_IMAGES = YES; 371 | COPY_PHASE_STRIP = YES; 372 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 373 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 374 | GCC_OPTIMIZATION_LEVEL = s; 375 | MACOSX_DEPLOYMENT_TARGET = 10.10; 376 | OTHER_SWIFT_FLAGS = "-DXcode"; 377 | PRODUCT_NAME = "$(TARGET_NAME)"; 378 | SDKROOT = macosx; 379 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; 380 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = SWIFT_PACKAGE; 381 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 382 | USE_HEADERMAP = NO; 383 | }; 384 | name = Release; 385 | }; 386 | OBJ_43 /* Debug */ = { 387 | isa = XCBuildConfiguration; 388 | buildSettings = { 389 | }; 390 | name = Debug; 391 | }; 392 | OBJ_44 /* Release */ = { 393 | isa = XCBuildConfiguration; 394 | buildSettings = { 395 | }; 396 | name = Release; 397 | }; 398 | /* End XCBuildConfiguration section */ 399 | 400 | /* Begin XCConfigurationList section */ 401 | OBJ_18 /* Build configuration list for PBXNativeTarget "SwiftParserPackageDescription" */ = { 402 | isa = XCConfigurationList; 403 | buildConfigurations = ( 404 | OBJ_19 /* Debug */, 405 | OBJ_20 /* Release */, 406 | ); 407 | defaultConfigurationIsVisible = 0; 408 | defaultConfigurationName = Debug; 409 | }; 410 | OBJ_2 /* Build configuration list for PBXProject "SwiftParser" */ = { 411 | isa = XCConfigurationList; 412 | buildConfigurations = ( 413 | OBJ_3 /* Debug */, 414 | OBJ_4 /* Release */, 415 | ); 416 | defaultConfigurationIsVisible = 0; 417 | defaultConfigurationName = Debug; 418 | }; 419 | OBJ_24 /* Build configuration list for PBXNativeTarget "SwiftParserTests" */ = { 420 | isa = XCConfigurationList; 421 | buildConfigurations = ( 422 | OBJ_25 /* Debug */, 423 | OBJ_26 /* Release */, 424 | ); 425 | defaultConfigurationIsVisible = 0; 426 | defaultConfigurationName = Debug; 427 | }; 428 | OBJ_33 /* Build configuration list for PBXNativeTarget "SwiftParser" */ = { 429 | isa = XCConfigurationList; 430 | buildConfigurations = ( 431 | OBJ_34 /* Debug */, 432 | OBJ_35 /* Release */, 433 | ); 434 | defaultConfigurationIsVisible = 0; 435 | defaultConfigurationName = Debug; 436 | }; 437 | OBJ_42 /* Build configuration list for PBXAggregateTarget "SwiftParserPackageTests" */ = { 438 | isa = XCConfigurationList; 439 | buildConfigurations = ( 440 | OBJ_43 /* Debug */, 441 | OBJ_44 /* Release */, 442 | ); 443 | defaultConfigurationIsVisible = 0; 444 | defaultConfigurationName = Debug; 445 | }; 446 | /* End XCConfigurationList section */ 447 | }; 448 | rootObject = OBJ_1 /* Project object */; 449 | } 450 | -------------------------------------------------------------------------------- /SwiftParser.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwiftParser.xcodeproj/project.xcworkspace/xcshareddata/SwiftParser.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 97EF38C5-0FD1-4192-929D-3E8DAFE62AF7 9 | IDESourceControlProjectName 10 | SwiftParser 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 4BD143F3028DB19A4BC92FFD0429EF2FB566A6FA 14 | ssh://github.com/dparnell/swift-parser-generator.git 15 | 16 | IDESourceControlProjectPath 17 | SwiftParser.xcodeproj/project.xcworkspace 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | 4BD143F3028DB19A4BC92FFD0429EF2FB566A6FA 21 | ../.. 22 | 23 | IDESourceControlProjectURL 24 | ssh://github.com/dparnell/swift-parser-generator.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | 4BD143F3028DB19A4BC92FFD0429EF2FB566A6FA 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | 4BD143F3028DB19A4BC92FFD0429EF2FB566A6FA 36 | IDESourceControlWCCName 37 | SwiftParser 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /SwiftParser.xcodeproj/xcshareddata/xcschemes/SwiftParser-Package.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 57 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 75 | 76 | 78 | 79 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /SwiftParser.xcodeproj/xcshareddata/xcschemes/SwiftParser.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 55 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 74 | 76 | 77 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /SwiftParser.xcodeproj/xcshareddata/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SchemeUserState 5 | 6 | SwiftParser-Package.xcscheme 7 | 8 | 9 | SuppressBuildableAutocreation 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Tests/SwiftParserTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Tests/SwiftParserTests/SwiftParserTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftParserTests.swift 3 | // SwiftParserTests 4 | // 5 | // Created by Daniel Parnell on 17/06/2014. 6 | // Copyright (c) 2014 Daniel Parnell. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import SwiftParser 11 | 12 | class SwiftParserTests: XCTestCase { 13 | class Calculator { 14 | var stack: [Double] = [] 15 | var _negative = false 16 | 17 | var result: Double { 18 | get { return stack[stack.count-1] } 19 | } 20 | 21 | func performBinaryOperation(_ op: (_ left: Double, _ right: Double) -> Double) { 22 | let right = stack.removeLast() 23 | let left = stack.removeLast() 24 | 25 | stack.append(op(left, right)) 26 | } 27 | 28 | func add() { 29 | performBinaryOperation({(left: Double, right: Double) -> Double in 30 | return left + right 31 | }) 32 | } 33 | 34 | func divide() { 35 | performBinaryOperation({(left: Double, right: Double) -> Double in 36 | return left / right 37 | }) 38 | } 39 | 40 | func exponent() { 41 | performBinaryOperation({(left: Double, right: Double) -> Double in 42 | return pow(left, right) 43 | }) 44 | } 45 | 46 | func multiply() { 47 | performBinaryOperation({(left: Double, right: Double) -> Double in 48 | return left * right 49 | }) 50 | 51 | } 52 | 53 | func subtract() { 54 | performBinaryOperation({(left: Double, right: Double) -> Double in 55 | return left - right 56 | }) 57 | 58 | } 59 | 60 | func negative() { 61 | _negative = !_negative 62 | } 63 | 64 | func pushNumber(_ text: String) { 65 | var value: Double = 0 66 | var decimal = -1 67 | var counter = 0 68 | for ch in text.utf8 { 69 | if(ch == 46) { 70 | decimal = counter 71 | } else { 72 | let digit: Int = Int(ch) - 48 73 | value = value * Double(10.0) + Double(digit) 74 | counter = counter + 1 75 | } 76 | } 77 | 78 | if(decimal >= 0) { 79 | value = value / pow(10.0, Double(counter - decimal)) 80 | } 81 | 82 | if(_negative) { 83 | value = -value 84 | } 85 | 86 | stack.append(value) 87 | } 88 | 89 | } 90 | 91 | class Arith: Parser { 92 | var calculator = Calculator() 93 | 94 | func push(_ text: String) { 95 | calculator.pushNumber(text) 96 | } 97 | 98 | func add() { 99 | calculator.add() 100 | } 101 | 102 | func sub() { 103 | calculator.subtract() 104 | } 105 | 106 | func mul() { 107 | calculator.multiply() 108 | } 109 | 110 | func div() { 111 | calculator.divide() 112 | } 113 | 114 | override init() { 115 | super.init() 116 | 117 | self.grammar = Grammar { [unowned self] g in 118 | g["number"] = ("0"-"9")+ => { [unowned self] parser in self.push(parser.text) } 119 | 120 | g["primary"] = ^"secondary" ~ (("+" ~ ^"secondary" => add) | ("-" ~ ^"secondary" => sub))* 121 | g["secondary"] = ^"tertiary" ~ (("*" ~ ^"tertiary" => mul) | ("/" ~ ^"tertiary" => div))* 122 | g["tertiary"] = ("(" ~ ^"primary" ~ ")") | ^"number" 123 | 124 | return (^"primary")*!* 125 | } 126 | } 127 | } 128 | 129 | func testSimple() { 130 | let a = Arith() 131 | XCTAssert(try a.parse("1+2")) 132 | XCTAssertEqual(a.calculator.result, 3) 133 | } 134 | 135 | func testComplex() { 136 | let a = Arith() 137 | XCTAssert(try a.parse("6*7-3+20/2-12+(30-5)/5")) 138 | XCTAssertEqual(a.calculator.result, 42) 139 | } 140 | 141 | func testShouldNotParse() { 142 | let a = Arith() 143 | XCTAssertFalse(try a.parse("1+")) 144 | XCTAssertFalse(try a.parse("xxx")) 145 | } 146 | } 147 | --------------------------------------------------------------------------------