Repository: robrix/Madness Branch: master Commit: add3ee4bf05d Files: 49 Total size: 112.8 KB Directory structure: gitextract_t4_kha8o/ ├── .gitignore ├── .gitmodules ├── .travis.yml ├── Cartfile ├── Cartfile.resolved ├── Documentation/ │ ├── Collections.playground/ │ │ ├── Contents.swift │ │ └── contents.xcplayground │ ├── Colours.playground/ │ │ ├── Contents.swift │ │ └── contents.xcplayground │ ├── Lambda Calculus.playground/ │ │ ├── Contents.swift │ │ └── contents.xcplayground │ └── Subset of Common Markdown.playground/ │ ├── Contents.swift │ └── contents.xcplayground ├── LICENSE ├── Madness/ │ ├── Alternation.swift │ ├── Combinator.swift │ ├── Concatenation.swift │ ├── Error.swift │ ├── Info.plist │ ├── Madness.h │ ├── Map.swift │ ├── Negation.swift │ ├── Parser.swift │ ├── Reduction.swift │ ├── Repetition.swift │ ├── SourcePos.swift │ └── String.swift ├── Madness.xcodeproj/ │ ├── project.pbxproj │ ├── project.xcworkspace/ │ │ └── contents.xcworkspacedata │ └── xcshareddata/ │ └── xcschemes/ │ ├── Madness-Mac.xcscheme │ └── Madness-iOS.xcscheme ├── Madness.xcworkspace/ │ └── contents.xcworkspacedata ├── MadnessTests/ │ ├── AlternationTests.swift │ ├── CollectionTests.swift │ ├── CombinatorTests.swift │ ├── ConcatenationTests.swift │ ├── ErrorTests.swift │ ├── Fixtures.swift │ ├── IgnoreTests.swift │ ├── Info.plist │ ├── MapTests.swift │ ├── NegationTests.swift │ ├── ParserTests.swift │ ├── ReductionTests.swift │ ├── RepetitionTests.swift │ └── StringTests.swift ├── README.md └── script/ ├── cibuild └── validate-playground.sh ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .DS_Store build xcuserdata *.mode* *.pbxuser *.xcuserdatad *.xccheckout *.xcscmblueprint *.xctimeline ================================================ FILE: .gitmodules ================================================ [submodule "Carthage/Checkouts/Result"] path = Carthage/Checkouts/Result url = https://github.com/antitypical/Result.git ================================================ FILE: .travis.yml ================================================ language: objective-c osx_image: xcode9 branches: only: - master xcode_workspace: Madness.xcworkspace script: - script/cibuild matrix: include: - xcode_scheme: Madness-Mac env: - XCODE_SDK=macosx - XCODE_ACTION="build test" - XCODE_DESTINATION="arch=x86_64" - xcode_scheme: Madness-Mac env: - XCODE_SDK=macosx - XCODE_ACTION="build" - XCODE_DESTINATION="arch=x86_64" - XCODE_PLAYGROUND="Documentation/Collections.playground" - JOB=Collections.playground - xcode_scheme: Madness-Mac env: - XCODE_SDK=macosx - XCODE_ACTION="build" - XCODE_DESTINATION="arch=x86_64" - XCODE_PLAYGROUND="Documentation/Colours.playground" - JOB=Colours.playground - xcode_scheme: Madness-Mac env: - XCODE_SDK=macosx - XCODE_ACTION="build" - XCODE_DESTINATION="arch=x86_64" - XCODE_PLAYGROUND="Documentation/Lambda Calculus.playground" - JOB=Lambda Calculus.playground - xcode_scheme: Madness-Mac env: - XCODE_SDK=macosx - XCODE_ACTION="build" - XCODE_DESTINATION="arch=x86_64" - XCODE_PLAYGROUND="Documentation/Subset of Common Markdown.playground" - JOB=Subset of Common Markdown.playground - xcode_scheme: Madness-iOS env: - XCODE_SDK=iphonesimulator - XCODE_ACTION="build-for-testing test-without-building" - XCODE_DESTINATION="platform=iOS Simulator,name=iPhone X" notifications: email: false ================================================ FILE: Cartfile ================================================ github "antitypical/Result" ~> 3.2.4 ================================================ FILE: Cartfile.resolved ================================================ github "antitypical/Result" "3.2.4" ================================================ FILE: Documentation/Collections.playground/Contents.swift ================================================ import Madness import Result let input = [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144] typealias Fibonacci = Parser<[Int], [Int]>.Function func fibonacci(_ x: Int, _ y: Int) -> Fibonacci { let combined: Fibonacci = %(x + y) >>- { (xy: Int) -> Fibonacci in { [ xy ] + $0 } <^> fibonacci(y, xy) } return combined <|> pure([]) } parse(fibonacci(0, 1), input: input).value ================================================ FILE: Documentation/Collections.playground/contents.xcplayground ================================================ ================================================ FILE: Documentation/Colours.playground/Contents.swift ================================================ import Cocoa import Darwin import Madness func toComponent(_ string: String) -> CGFloat { return CGFloat(strtol(string, nil, 16)) / 255 } let digit = %("0"..."9") let lower = %("a"..."f") let upper = %("A"..."F") let hex = digit <|> lower <|> upper let hex2 = lift(+) <*> hex <*> hex let component1: Parser.Function = { toComponent($0 + $0) } <^> hex let component2: Parser.Function = toComponent <^> hex2 let three: Parser.Function = component1 * 3 let six: Parser.Function = component2 * 3 let colour: Parser.Function = map({ NSColor(calibratedRed: $0[0], green: $0[1], blue: $0[2], alpha: 1) })(%"#" *> (six <|> three)) let reddish = parse(colour, input: "#d52a41").value let greenish = parse(colour, input: "#5a2").value let blueish = parse(colour, input: "#5e8ca1").value ================================================ FILE: Documentation/Colours.playground/contents.xcplayground ================================================ ================================================ FILE: Documentation/Lambda Calculus.playground/Contents.swift ================================================ import Madness indirect enum Lambda: CustomStringConvertible { case variable(String) case abstraction(String, Lambda) case application(Lambda, Lambda) var description: String { switch self { case let .variable(symbol): return symbol case let .abstraction(symbol, body): return "λ\(symbol).\(body.description)" case let .application(x, y): return "(\(x.description) \(y.description))" } } } typealias LambdaParser = Parser func lambda(_ input: String.CharacterView, sourcePos: SourcePos) -> LambdaParser.Result { let symbol: StringParser = %("a"..."z") let variable: LambdaParser.Function = Lambda.variable <^> symbol let abstraction: LambdaParser.Function = Lambda.abstraction <^> ( lift(pair) <*> (%"λ" *> symbol) <*> (%"." *> lambda) ) let application: LambdaParser.Function = Lambda.application <^> ( lift(pair) <*> (%"(" *> lambda) <*> (%" " *> lambda) <* %")" ) let parser: LambdaParser.Function = variable <|> abstraction <|> application return parser(input, sourcePos) } parse(lambda, input: "λx.(x x)".characters).value?.description parse(lambda, input: "(λx.(x x) λx.(x x))".characters).value?.description ================================================ FILE: Documentation/Lambda Calculus.playground/contents.xcplayground ================================================ ================================================ FILE: Documentation/Subset of Common Markdown.playground/Contents.swift ================================================ import Madness // MARK: - Lexing rules let newline = %"\n" let ws = %" " <|> %"\t" let lower = %("a"..."z") let upper = %("A"..."Z") let digit = %("0"..."9") let text = lower <|> upper <|> digit <|> ws let restOfLine = { $0.joined(separator: "") } <^> many(text) <* newline let texts = { $0.joined(separator: "") } <^> some(text <|> (%"" <* newline)) // MARK: - AST enum Node: CustomStringConvertible { case blockquote([Node]) case header(Int, String) case paragraph(String) func analysis(ifBlockquote: ([Node]) -> T, ifHeader: (Int, String) -> T, ifParagraph: (String) -> T) -> T { switch self { case let .blockquote(nodes): return ifBlockquote(nodes) case let .header(level, text): return ifHeader(level, text) case let .paragraph(text): return ifParagraph(text) } } // MARK: Printable var description: String { return analysis( ifBlockquote: { "
" + $0.lazy.map{ $0.description }.joined(separator: "") + "
" }, ifHeader: { "\($1)" }, ifParagraph: { "

\($0)

" }) } } // MARK: - Parsing rules typealias NodeParser = Parser.Function typealias ElementParser = (@escaping StringParser) -> NodeParser func fix(_ f: @escaping (@escaping (T) -> U) -> (T) -> U) -> (T) -> U { return { f(fix(f))($0) } } let element: ElementParser = fix { element in { prefix in let octothorpes: IntParser = { $0.count } <^> (%"#" * (1..<7)) let header: NodeParser = prefix *> ( Node.header <^> (lift(pair) <*> octothorpes <*> (%" " *> restOfLine)) ) let paragraph: NodeParser = prefix *> ( Node.paragraph <^> texts ) let blockquote: NodeParser = prefix *> { ( Node.blockquote <^> some(element(prefix *> %"> ")) )($0, $1) } return header <|> paragraph <|> blockquote } } let parser = many(element(pure(""))) if let parsed = parse(parser, input: "> # hello\n> \n> hello\n> there\n> \n> \n").value { let description = parsed.reduce(""){ $0 + $1.description } print(description) } if let parsed = parse(parser, input: "This is a \nparagraph\n> # title\n> ### subtitle\n> a").value { let description = parsed.reduce(""){ $0 + $1.description } print(description) } ================================================ FILE: Documentation/Subset of Common Markdown.playground/contents.xcplayground ================================================ ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2014 Rob Rix Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Madness/Alternation.swift ================================================ // Copyright (c) 2015 Rob Rix. All rights reserved. /// Parses `parser` 0 or one time. public postfix func |? (parser: @escaping Parser.Function) -> Parser.Function { return { $0.first } <^> parser * (0...1) } /// Parses either `left` or `right` and coalesces their trees. public func <|> (left: @escaping Parser.Function, right: @escaping Parser.Function) -> Parser.Function { return alternate(left, right) } // MARK: - n-ary alternation /// Alternates over a sequence of literals, coalescing their parse trees. public func oneOf(_ input: S) -> Parser.Function where C.Element: Equatable, S.Element == C.Element { return satisfy(input.contains) } /// Given a set of literals, parses an array of any matches in the order they were found. /// /// Each literal will only match the first time. public func anyOf(_ set: Set) -> Parser.Function { return oneOf(set) >>- { match in var rest = set rest.remove(match) return prepend(match) <^> anyOf(rest) <|> pure([match]) } } /// Given a set of literals, parses an array of all matches in the order they were found. /// /// Each literal will be matched as many times as it is found. public func allOf(_ input: Set) -> Parser.Function { return oneOf(input) >>- { match in prepend(match) <^> allOf(input) <|> pure([match]) } } // MARK: - Private /// Defines alternation for use in the `<|>` operator definitions above. private func alternate(_ left: @escaping Parser.Function, _ right: @escaping Parser.Function) -> Parser.Function { return { input, sourcePos in return left(input, sourcePos) .flatMapError { left in return right(input, sourcePos) .mapError { right in return Error.withReason("no alternative matched:", sourcePos)(left, right) } } } } /// Curried function that prepends a value to an array. func prepend(_ value: T) -> ([T]) -> [T] { return { [value] + $0 } } // MARK: - Operators /// Optional alternation operator. postfix operator |? precedencegroup AlternationPrecedence { associativity: left higherThan: ChainingPrecedence lowerThan: MultiplicationPrecedence } infix operator <|> : AlternationPrecedence // MARK: - Imports import Result ================================================ FILE: Madness/Combinator.swift ================================================ // Copyright © 2015 Rob Rix. All rights reserved. /// Parses `open`, followed by `parser` and `close`. Returns the value returned by `parser`. public func between(_ open: @escaping Parser.Function, _ close: @escaping Parser.Function) -> (@escaping Parser.Function) -> Parser.Function { return { open *> $0 <* close } } /// Parses 0 or more `parser` until `end`. Returns the list of values returned by `parser`. public func manyTill(_ parser: @escaping Parser.Function, _ end: @escaping Parser.Function) -> Parser.Function { return many(parser) <* end } ================================================ FILE: Madness/Concatenation.swift ================================================ // Copyright (c) 2015 Rob Rix. All rights reserved. import Result precedencegroup ConcatenationPrecedence { associativity: left higherThan: AlternationPrecedence lowerThan: MultiplicationPrecedence } /// Parses the concatenation of `left` and `right`, pairing their parse trees. public func <*> (left: @escaping Parser U>.Function, right: @escaping Parser.Function) -> Parser.Function { return left >>- { $0 <^> right } } /// Parses the concatenation of `left` and `right`, dropping `right`’s parse tree. public func <* (left: @escaping Parser.Function, right: @escaping Parser.Function) -> Parser.Function { return left >>- { x in { _ in x } <^> right } } /// Parses the concatenation of `left` and `right`, dropping `left`’s parse tree. public func *> (left: @escaping Parser.Function, right: @escaping Parser.Function) -> Parser.Function { return left >>- { _ in right } } infix operator <*> : ConcatenationPrecedence infix operator *> : ConcatenationPrecedence infix operator <* : ConcatenationPrecedence ================================================ FILE: Madness/Error.swift ================================================ // Copyright (c) 2015 Rob Rix. All rights reserved. import Result /// A composite error. public enum Error: Swift.Error, CustomStringConvertible { indirect case branch(String, SourcePos, [Error]) /// Constructs a leaf error, e.g. for terminal parsers. public static func leaf(_ reason: String, _ sourcePos: SourcePos) -> Error { return .branch(reason, sourcePos, []) } public static func withReason(_ reason: String, _ sourcePos: SourcePos) -> (Error, Error) -> Error { return { Error(reason: reason, sourcePos: sourcePos, children: [$0, $1]) } } public init(reason: String, sourcePos: SourcePos, children: [Error]) { self = .branch(reason, sourcePos, children) } public var reason: String { switch self { case let .branch(s, _, _): return s } } public var sourcePos: SourcePos { switch self { case let .branch(_, sourcePos, _): return sourcePos } } public var children: [Error] { switch self { case let .branch(_, _, c): return c } } public var depth: Int { return 1 + ((children.sorted { $0.depth < $1.depth }).last?.depth ?? 0) } // MARK: Printable public var description: String { return describe(0) } fileprivate func describe(_ n: Int) -> String { let description = String(repeating: "\t", count: n) + "\(sourcePos.index): \(reason)" if children.count > 0 { return description + "\n" + children.lazy.map { $0.describe(n + 1) }.joined(separator: "\n") } return description } } /// MARK: - Annotations /// Annotate a parser with a name. public func (parser: @escaping Parser.Function, name: String) -> Parser.Function { return describeAs(name)(parser) } /// Adds a name to parse errors. public func describeAs(_ name: String) -> (@escaping Parser.Function) -> Parser.Function { return { parser in { input, index in return parser(input, index).mapError { Error(reason: "\(name): \($0.reason)", sourcePos: $0.sourcePos, children: $0.children) } } } } // MARK: - Operators precedencegroup AnnotationPrecedence { associativity: left lowerThan: ChainingPrecedence } infix operator : AnnotationPrecedence ================================================ FILE: Madness/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType FMWK CFBundleShortVersionString 0.0.1 CFBundleSignature ???? CFBundleVersion $(CURRENT_PROJECT_VERSION) NSHumanReadableCopyright Copyright © 2014 Rob Rix. All rights reserved. NSPrincipalClass ================================================ FILE: Madness/Madness.h ================================================ // Copyright (c) 2014 Rob Rix. All rights reserved. /// Project version number for Madness. extern double MadnessVersionNumber; /// Project version string for Madness. extern const unsigned char MadnessVersionString[]; ================================================ FILE: Madness/Map.swift ================================================ // Copyright (c) 2015 Rob Rix. All rights reserved. // MARK: - flatMap /// Returns a parser which requires `parser` to parse, passes its parsed trees to a function `f`, and then requires the result of `f` to parse. /// /// This can be used to conveniently make a parser which depends on earlier parsed input, for example to parse exactly the same number of characters, or to parse structurally significant indentation. public func >>- (parser: @escaping Parser.Function, f: @escaping (T) -> Parser.Function) -> Parser.Function { return { input, index in parser(input, index).flatMap { f($0)(input, $1) } } } // MARK: - map /// Returns a parser which applies `f` to transform the output of `parser`. public func <^> (f: @escaping (T) -> U, parser: @escaping Parser.Function) -> Parser.Function { return parser >>- { pure(f($0)) } } /// Returns a parser which first parses `right`, replacing successful parses with `left`. public func <^ (left: T, right: @escaping Parser.Function) -> Parser.Function { return { (_: U) -> T in left } <^> right } /// Curried `<^>`. Returns a parser which applies `f` to transform the output of `parser`. public func map(_ f: @escaping (T) -> U) -> (@escaping Parser.Function) -> Parser.Function { return { f <^> $0 } } // MARK: - pure /// Returns a parser which always ignores its input and produces a constant value. /// /// When combining parsers with `>>-`, allows constant values to be injected into the parser chain. public func pure(_ value: T) -> Parser.Function { return { _, index in .success((value, index)) } } // MARK: - lift public func lift(_ f: @escaping (T, U) -> V) -> Parser (U) -> V>.Function { return pure({ t in { u in f(t, u) } }) } public func lift(_ f: @escaping (T, U, V) -> W) -> Parser (U) -> (V) -> W>.Function { return pure({ t in { u in { v in f(t, u, v) } } }) } // MARK: - pair public func pair(_ a: A, b: B) -> (A, B) { return (a, b) } // MARK: - Operators /// Flat map operator. infix operator >>- : ChainingPrecedence /// Map operator. infix operator <^> : ConcatenationPrecedence /// Replace operator. infix operator <^ : ConcatenationPrecedence import Result ================================================ FILE: Madness/Negation.swift ================================================ // Copyright (c) 2015 Rob Rix. All rights reserved. /// This parser succeeds iff `parser` fails. This parser does not consume any input. public func not (_ parser: @escaping Parser.Function) -> Parser.Function { return { input, index in return parser(input, index).analysis( ifSuccess: { (_, endIndex) in Result.failure(Error.leaf("failed negative lookahead ending at \(endIndex)", index)) }, ifFailure: { _ in Result.success(((), index)) } ) } } // MARK: - Imports import Result ================================================ FILE: Madness/Parser.swift ================================================ // Copyright (c) 2014 Rob Rix. All rights reserved. // Swift has no way to resolve to `Result.Result` inside `Parser.Result`. public typealias ParserResult = Result<(Tree, SourcePos), Error> /// Convenience for describing the types of parser combinators. /// /// \param Tree The type of parse tree generated by the parser. public enum Parser { /// The type of parser combinators. public typealias Function = (C, SourcePos) -> Result /// The type produced by parser combinators. public typealias Result = ParserResult } /// Parses `input` with `parser`, returning the parse trees or `nil` if nothing could be parsed, or if parsing did not consume the entire input. public func parse(_ parser: Parser.Function, input: C) -> Result> { let result = parser(input, SourcePos(index: input.startIndex)) return result.flatMap { tree, sourcePos in return sourcePos.index == input.endIndex ? .success(tree) : .failure(.leaf("finished parsing before end of input", sourcePos)) } } public func parse(_ parser: Parser.Function, input: String) -> Result> { return parse(parser, input: input.characters) } // MARK: - Terminals /// Returns a parser which never parses its input. public func none(_ string: String = "no way forward") -> Parser.Function { return { _, sourcePos in .failure(.leaf(string, sourcePos)) } } // Returns a parser which parses any single character. public func any(_ input: C, sourcePos: SourcePos) -> Parser.Result { return satisfy { _ in true }(input, sourcePos) } public func any(_ input: String.CharacterView, sourcePos: SourcePos) -> Parser.Result { return satisfy { _ in true }(input, sourcePos) } /// Returns a parser which parses a `literal` sequence of elements from the input. /// /// This overload enables e.g. `%"xyz"` to produce `String -> (String, String)`. public prefix func % (literal: C) -> Parser.Function where C.Element: Equatable { return { input, sourcePos in if input[sourcePos.index...].starts(with: literal) { return .success((literal, sourcePos.advanced(by: literal.count, from: input))) } else { return .failure(.leaf("expected \(literal)", sourcePos)) } } } public prefix func %(literal: String) -> Parser.Function { return { input, sourcePos in if input[sourcePos.index...].starts(with: literal.characters) { return .success((literal, sourcePos.advanced(by: literal, from: input))) } else { return .failure(.leaf("expected \(literal)", sourcePos)) } } } /// Returns a parser which parses a `literal` element from the input. public prefix func % (literal: C.Element) -> Parser.Function where C.Element: Equatable { return { input, sourcePos in if sourcePos.index != input.endIndex && input[sourcePos.index] == literal { return .success((literal, sourcePos.advanced(by: 1, from: input))) } else { return .failure(.leaf("expected \(literal)", sourcePos)) } } } /// Returns a parser which parses any character in `range`. public prefix func %(range: ClosedRange) -> Parser.Function { return { (input: String.CharacterView, sourcePos: SourcePos) in let index = sourcePos.index if index < input.endIndex && range.contains(input[index]) { let string = String(input[index]) return .success((string, sourcePos.advanced(by: 1, from: input))) } else { return .failure(.leaf("expected an element in range \(range)", sourcePos)) } } } // MARK: - Nonterminals private func memoize(_ f: @escaping () -> T) -> () -> T { var memoized: T! return { if memoized == nil { memoized = f() } return memoized } } public func delay(_ parser: @escaping () -> Parser.Function) -> Parser.Function { let memoized = memoize(parser) return { memoized()($0, $1) } } // Returns a parser that satisfies the given predicate public func satisfy(_ pred: @escaping (Character) -> Bool) -> Parser.Function { return tokenPrim(pred) { $0.advanced(by: $1, from: $2) } } // Returns a parser that satisfies the given predicate public func satisfy (_ pred: @escaping (C.Element) -> Bool) -> Parser.Function { return tokenPrim(pred) { $0.advanced(by: 1, from: $2) } } public func tokenPrim (_ pred: @escaping (C.Element) -> Bool, _ nextPos: @escaping (SourcePos, C.Element, C) -> SourcePos) -> Parser.Function { return { input, sourcePos in let index = sourcePos.index if index != input.endIndex { let parsed = input[index] if pred(parsed) { return .success((parsed, nextPos(sourcePos, parsed, input))) } else { return .failure(Error.leaf("Failed to parse \(String(describing: parsed)) with predicate at index", sourcePos)) } } else { return .failure(Error.leaf("Failed to parse at end of input", sourcePos)) } } } // MARK: - Operators /// Map operator. infix operator --> : ChainingPrecedence /// Literal operator. prefix operator % // MARK: - Imports import Result ================================================ FILE: Madness/Reduction.swift ================================================ // Copyright (c) 2015 Rob Rix. All rights reserved. /// Returns a parser which maps parse trees into another type. public func --> (parser: @escaping Parser.Function, f: @escaping (C, CountableClosedRange, CountableClosedRange, Range, T) -> U) -> Parser.Function { return { input, inputPos in return parser(input, inputPos).map { output, outputPos in (f(input, inputPos.line...outputPos.line, outputPos.column...outputPos.column, inputPos.index.. (parser: @escaping Parser.Function, transform: @escaping (Parser.Result) -> Parser.Result) -> Parser.Function { return { transform(parser($0, $1)) } } ================================================ FILE: Madness/Repetition.swift ================================================ // Copyright (c) 2015 Rob Rix. All rights reserved. /// Parser `parser` 1 or more times. public func some (_ parser: @escaping Parser.Function) -> Parser.Function { return prepend <^> require(parser) <*> many(parser) } /// Parses 1 or more `parser` separated by `separator`. public func sepBy1(_ parser: @escaping Parser.Function, _ separator: @escaping Parser.Function) -> Parser.Function { return prepend <^> parser <*> many(separator *> parser) } /// Parses 0 or more `parser` separated by `separator`. public func sepBy(_ parser: @escaping Parser.Function, _ separator: @escaping Parser.Function) -> Parser.Function { return sepBy1(parser, separator) <|> pure([]) } /// Parses 1 or more `parser` ended by `terminator`. public func endBy1(_ parser: @escaping Parser.Function, _ terminator: @escaping Parser.Function) -> Parser.Function { return some(parser <* terminator) } /// Parses 0 or more `parser` ended by `terminator`. public func endBy(_ parser: @escaping Parser.Function, _ terminator: @escaping Parser.Function) -> Parser.Function { return many(parser <* terminator) } /// Parses `parser` the number of times specified in `interval`. /// /// \param interval An interval specifying the number of repetitions to perform. `0...n` means at most `n` repetitions; `m...Int.max` means at least `m` repetitions; and `m...n` means between `m` and `n` repetitions (inclusive). public func * (parser: @escaping Parser.Function, interval: CountableClosedRange) -> Parser.Function { if interval.upperBound <= 0 { return { .success(([], $1)) } } return (parser >>- { x in { [x] + $0 } <^> (parser * decrement(interval)) }) <|> { interval.lowerBound <= 0 ? .success(([], $1)) : .failure(.leaf("expected at least \(interval.lowerBound) matches", $1)) } } /// Parses `parser` exactly `n` times. /// /// `n` must be > 0 to make any sense. public func * (parser: @escaping Parser.Function, n: Int) -> Parser.Function { return ntimes(parser, n) } /// Parses `parser` the number of times specified in `interval`. /// /// \param interval An interval specifying the number of repetitions to perform. `0.. (parser: @escaping Parser.Function, interval: Range) -> Parser.Function { if interval.isEmpty { return { .failure(.leaf("cannot parse an empty interval of repetitions", $1)) } } return parser * (interval.lowerBound...decrement(interval.upperBound)) } /// Parses `parser` 0 or more times. public func many (_ p: @escaping Parser.Function) -> Parser.Function { return prepend <^> require(p) <*> delay { many(p) } <|> pure([]) } /// Parses `parser` `n` number of times. public func ntimes (_ p: @escaping Parser.Function, _ n: Int) -> Parser.Function { guard n > 0 else { return pure([]) } return prepend <^> p <*> delay { ntimes(p, n - 1) } } // MARK: - Private /// Decrements `x` iff it is not equal to `Int.max`. private func decrement(_ x: Int) -> Int { return (x == Int.max ? Int.max : x - 1) } private func decrement(_ x: CountableClosedRange) -> CountableClosedRange { return decrement(x.lowerBound)...decrement(x.upperBound) } /// Fails iff `parser` does not consume input, otherwise pass through its results private func require (_ parser: @escaping Parser.Function) -> Parser.Function { return { (input, sourcePos) in return parser(input, sourcePos).flatMap { resultInput, resultPos in if sourcePos.index == resultPos.index { return Result.failure(Error.leaf("parser did not consume input when required", sourcePos)) } return Result.success((resultInput, resultPos)) } } } // MARK: - Imports import Result ================================================ FILE: Madness/SourcePos.swift ================================================ // Copyright (c) 2014 Josh Vera. All rights reserved. public typealias Line = Int public typealias Column = Int var DefaultTabWidth = 8 public struct SourcePos { public let line: Line public let column: Column public let index: Index public init(index: Index) { line = 1 column = 1 self.index = index } public init(line: Line, column: Column, index: Index) { self.line = line self.column = column self.index = index } } extension SourcePos: Equatable { /// Returns whether two SourcePos are equal. public static func ==(first: SourcePos, other: SourcePos) -> Bool { return first.line == other.line && first.column == other.column && first.index == other.index } } extension SourcePos { /// Returns a new SourcePos advanced by the given index. public func advanced(to index: Index) -> SourcePos { return SourcePos(line: line, column: column, index: index) } /// Returns a new SourcePos advanced by `count`. public func advanced(by distance: C.IndexDistance, from input: C) -> SourcePos where C.Index == Index { return advanced(to: input.index(index, offsetBy: distance)) } } extension SourcePos where Index == String.Index { /// Returns a new SourcePos with its line, column, and index advanced by the given character. public func advanced(by char: Character, from input: String.CharacterView) -> SourcePos { let nextIndex = input.index(after: index) if char == "\n" { return SourcePos(line: line + 1, column: 0, index: nextIndex) } else if char == "\t" { return SourcePos(line: line, column: column + DefaultTabWidth - ((column - 1) % DefaultTabWidth), index: nextIndex) } else { return SourcePos(line: line, column: column + 1, index: nextIndex) } } /// Returns a new SourcePos with its line, column, and index advanced by the given string. func advanced(by string: String, from input: String.CharacterView) -> SourcePos { return string.characters.reduce(self) { $0.advanced(by: $1, from: input) } } } ================================================ FILE: Madness/String.swift ================================================ // // String.swift // Madness // // Created by Josh Vera on 10/19/15. // Copyright © 2015 Rob Rix. All rights reserved. // import Foundation public typealias CharacterParser = Parser.Function public typealias CharacterArrayParser = Parser.Function public typealias StringParser = Parser.Function public typealias DoubleParser = Parser.Function public typealias IntParser = Parser.Function private func maybePrepend(_ value: T?) -> ([T]) -> [T] { return { value != nil ? [value!] + $0 : $0 } } private func concat(_ value: [T]) -> ([T]) -> [T] { return { value + $0 } } private func concat2(_ value: [T]) -> ([T]) -> ([T]) -> [T] { return { value2 in { value + value2 + $0 } } } private let someDigits: CharacterArrayParser = some(digit) // Parses integers as an array of characters public let int: CharacterArrayParser = { let minus: Parser.Function = char("-")|? return maybePrepend <^> minus <*> someDigits }() private let decimal: CharacterArrayParser = prepend <^> %"." <*> someDigits private let exp: StringParser = %"e+" <|> %"e-" <|> %"e" <|> %"E+" <|> %"E-" <|> %"E" private let exponent: CharacterArrayParser = { s in { s.characters + $0 } } <^> exp <*> someDigits // Parses floating point numbers as doubles public let number: DoubleParser = { characters in Double(String(characters))! } <^> ((concat2 <^> int <*> decimal <*> exponent) <|> (concat <^> int <*> decimal) <|> (concat <^> int <*> exponent) <|> int) public let digit: CharacterParser = oneOf("0123456789") public let space: CharacterParser = char(" ") public let newline: CharacterParser = char("\n") public let cr = char("\r") public let crlf: CharacterParser = char("\r\n") public let endOfLine: CharacterParser = newline <|> crlf public let tab: CharacterParser = char("\t") public func oneOf(_ input: String) -> CharacterParser { return satisfy { input.characters.contains($0) } } public func noneOf(_ input: String) -> CharacterParser { return satisfy { !input.characters.contains($0) } } public func char(_ input: Character) -> CharacterParser { return satisfy { $0 == input } } ================================================ FILE: Madness.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 849DC2381C0F21D0004C1A1E /* Combinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849DC2371C0F21D0004C1A1E /* Combinator.swift */; }; 849DC2391C0F21D0004C1A1E /* Combinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849DC2371C0F21D0004C1A1E /* Combinator.swift */; }; 849DC23A1C0F21E1004C1A1E /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1E153551BD5B78E00627E39 /* String.swift */; }; 849DC23C1C0F224D004C1A1E /* CombinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849DC23B1C0F224D004C1A1E /* CombinatorTests.swift */; }; 849DC23D1C0F224D004C1A1E /* CombinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849DC23B1C0F224D004C1A1E /* CombinatorTests.swift */; }; B8219A941BF1DF05000006F1 /* Negation.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8219A931BF1DF05000006F1 /* Negation.swift */; }; B8219A951BF1DF0A000006F1 /* Negation.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8219A931BF1DF05000006F1 /* Negation.swift */; }; B8219A981BF1ED07000006F1 /* NegationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8219A971BF1ED07000006F1 /* NegationTests.swift */; }; B88CCA091BF2C17700979677 /* NegationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8219A971BF1ED07000006F1 /* NegationTests.swift */; }; B8E0AB871BF098DD002B5C8B /* StringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8E0AB861BF098DD002B5C8B /* StringTests.swift */; }; B8E0AB881BF098DD002B5C8B /* StringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8E0AB861BF098DD002B5C8B /* StringTests.swift */; }; BECD3BD21F8D918E006FF13E /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BECD3BD11F8D918E006FF13E /* Result.framework */; }; BECD3BD51F8D91A8006FF13E /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BECD3BD41F8D91A8006FF13E /* Result.framework */; }; D1A6B7F91BE01B8F00B4858C /* SourcePos.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1A6B7F81BE01B8F00B4858C /* SourcePos.swift */; }; D1A6B7FA1BE01B8F00B4858C /* SourcePos.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1A6B7F81BE01B8F00B4858C /* SourcePos.swift */; }; D1E153561BD5B78E00627E39 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1E153551BD5B78E00627E39 /* String.swift */; }; D421A2A91A9A8E33009AC3B1 /* IgnoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D421A2A81A9A8E33009AC3B1 /* IgnoreTests.swift */; }; D421A2AA1A9A8E33009AC3B1 /* IgnoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D421A2A81A9A8E33009AC3B1 /* IgnoreTests.swift */; }; D421A2AC1A9A9540009AC3B1 /* ReductionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D421A2AB1A9A9540009AC3B1 /* ReductionTests.swift */; }; D421A2AD1A9A9540009AC3B1 /* ReductionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D421A2AB1A9A9540009AC3B1 /* ReductionTests.swift */; }; D47B10761A9A9A1C006701A8 /* Reduction.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47B10751A9A9A1C006701A8 /* Reduction.swift */; }; D47B10771A9A9A1C006701A8 /* Reduction.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47B10751A9A9A1C006701A8 /* Reduction.swift */; }; D490927A1A98F11A00275C79 /* CollectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D49092791A98F11A00275C79 /* CollectionTests.swift */; }; D490927B1A98F11A00275C79 /* CollectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D49092791A98F11A00275C79 /* CollectionTests.swift */; }; D4BC5E021A98C8B4008C6851 /* Madness.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D4BC5DF71A98C8B4008C6851 /* Madness.framework */; }; D4BC5E101A98C978008C6851 /* Parser.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4FC47CD1A37E48800D23A6F /* Parser.swift */; }; D4BC5E121A98C97C008C6851 /* ParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4FC47C31A37E47C00D23A6F /* ParserTests.swift */; }; D4BC5E131A98C97C008C6851 /* MapTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C8B02D1A69B1A900943303 /* MapTests.swift */; }; D4C0FB011AC5EDA500936032 /* Fixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C0FAFE1AC5ECCE00936032 /* Fixtures.swift */; }; D4C0FB021AC5EDA600936032 /* Fixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C0FAFE1AC5ECCE00936032 /* Fixtures.swift */; }; D4C2EDA71A98D38E00054FAA /* Concatenation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C2EDA61A98D38E00054FAA /* Concatenation.swift */; }; D4C2EDAA1A98D4D400054FAA /* ConcatenationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C2EDA81A98D49500054FAA /* ConcatenationTests.swift */; }; D4C2EDAB1A98D4D600054FAA /* ConcatenationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C2EDA81A98D49500054FAA /* ConcatenationTests.swift */; }; D4C2EDAC1A98D4DE00054FAA /* Concatenation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C2EDA61A98D38E00054FAA /* Concatenation.swift */; }; D4C2EDAE1A98D52B00054FAA /* Alternation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C2EDAD1A98D52B00054FAA /* Alternation.swift */; }; D4C2EDAF1A98D53400054FAA /* Alternation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C2EDAD1A98D52B00054FAA /* Alternation.swift */; }; D4C2EDB11A98D5DB00054FAA /* AlternationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C2EDB01A98D5DB00054FAA /* AlternationTests.swift */; }; D4C2EDB21A98D5DB00054FAA /* AlternationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C2EDB01A98D5DB00054FAA /* AlternationTests.swift */; }; D4C2EDB61A98D65300054FAA /* Repetition.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C2EDB31A98D63600054FAA /* Repetition.swift */; }; D4C2EDB71A98D65400054FAA /* Repetition.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C2EDB31A98D63600054FAA /* Repetition.swift */; }; D4C2EDB91A98D82200054FAA /* RepetitionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C2EDB81A98D82200054FAA /* RepetitionTests.swift */; }; D4C2EDBA1A98D82200054FAA /* RepetitionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C2EDB81A98D82200054FAA /* RepetitionTests.swift */; }; D4C2EDBC1A98D8F800054FAA /* Map.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C2EDBB1A98D8F800054FAA /* Map.swift */; }; D4C2EDBD1A98D8F800054FAA /* Map.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C2EDBB1A98D8F800054FAA /* Map.swift */; }; D4C2EDFC1A98DEE800054FAA /* Madness.h in Headers */ = {isa = PBXBuildFile; fileRef = D4FC47B61A37E47C00D23A6F /* Madness.h */; settings = {ATTRIBUTES = (Public, ); }; }; D4C8B02E1A69B1A900943303 /* MapTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C8B02D1A69B1A900943303 /* MapTests.swift */; }; D4D328491A9AFE2700216D7E /* ErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4D328481A9AFE2700216D7E /* ErrorTests.swift */; }; D4D9F28A1A9C42A7002BEFF2 /* ErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4D328481A9AFE2700216D7E /* ErrorTests.swift */; }; D4DE2EE61ABCB2D000D3D70A /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4D3284B1A9AFE6000216D7E /* Error.swift */; }; D4DE2EE71ABCB2D100D3D70A /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4D3284B1A9AFE6000216D7E /* Error.swift */; }; D4FC47B71A37E47C00D23A6F /* Madness.h in Headers */ = {isa = PBXBuildFile; fileRef = D4FC47B61A37E47C00D23A6F /* Madness.h */; settings = {ATTRIBUTES = (Public, ); }; }; D4FC47BD1A37E47C00D23A6F /* Madness.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D4FC47B11A37E47C00D23A6F /* Madness.framework */; }; D4FC47C41A37E47C00D23A6F /* ParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4FC47C31A37E47C00D23A6F /* ParserTests.swift */; }; D4FC47CE1A37E48800D23A6F /* Parser.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4FC47CD1A37E48800D23A6F /* Parser.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ D4BC5E031A98C8B4008C6851 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D4FC47A81A37E47C00D23A6F /* Project object */; proxyType = 1; remoteGlobalIDString = D4BC5DF61A98C8B4008C6851; remoteInfo = Madness; }; D4FC47BE1A37E47C00D23A6F /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D4FC47A81A37E47C00D23A6F /* Project object */; proxyType = 1; remoteGlobalIDString = D4FC47B01A37E47C00D23A6F; remoteInfo = Madness; }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ 849DC2371C0F21D0004C1A1E /* Combinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Combinator.swift; sourceTree = ""; }; 849DC23B1C0F224D004C1A1E /* CombinatorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CombinatorTests.swift; sourceTree = ""; }; B8219A931BF1DF05000006F1 /* Negation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Negation.swift; sourceTree = ""; }; B8219A971BF1ED07000006F1 /* NegationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NegationTests.swift; sourceTree = ""; }; B8E0AB861BF098DD002B5C8B /* StringTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringTests.swift; sourceTree = ""; }; BECD3BD11F8D918E006FF13E /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BECD3BD41F8D91A8006FF13E /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D1A6B7F81BE01B8F00B4858C /* SourcePos.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SourcePos.swift; sourceTree = ""; }; D1E153551BD5B78E00627E39 /* String.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = ""; }; D421A2A81A9A8E33009AC3B1 /* IgnoreTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IgnoreTests.swift; sourceTree = ""; }; D421A2AB1A9A9540009AC3B1 /* ReductionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ReductionTests.swift; path = MadnessTests/ReductionTests.swift; sourceTree = SOURCE_ROOT; }; D47B10751A9A9A1C006701A8 /* Reduction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reduction.swift; sourceTree = ""; }; D49092791A98F11A00275C79 /* CollectionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionTests.swift; sourceTree = ""; }; D4BC5DF71A98C8B4008C6851 /* Madness.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Madness.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D4BC5E011A98C8B4008C6851 /* Madness-iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Madness-iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; D4C0FAFE1AC5ECCE00936032 /* Fixtures.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Fixtures.swift; sourceTree = ""; }; D4C2EDA61A98D38E00054FAA /* Concatenation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Concatenation.swift; sourceTree = ""; }; D4C2EDA81A98D49500054FAA /* ConcatenationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConcatenationTests.swift; sourceTree = ""; }; D4C2EDAD1A98D52B00054FAA /* Alternation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Alternation.swift; sourceTree = ""; }; D4C2EDB01A98D5DB00054FAA /* AlternationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlternationTests.swift; sourceTree = ""; }; D4C2EDB31A98D63600054FAA /* Repetition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Repetition.swift; sourceTree = ""; }; D4C2EDB81A98D82200054FAA /* RepetitionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RepetitionTests.swift; sourceTree = ""; }; D4C2EDBB1A98D8F800054FAA /* Map.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Map.swift; sourceTree = ""; }; D4C8B02D1A69B1A900943303 /* MapTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapTests.swift; sourceTree = ""; }; D4D328481A9AFE2700216D7E /* ErrorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ErrorTests.swift; sourceTree = ""; }; D4D3284B1A9AFE6000216D7E /* Error.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Error.swift; sourceTree = ""; }; D4FC47B11A37E47C00D23A6F /* Madness.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Madness.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D4FC47B51A37E47C00D23A6F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D4FC47B61A37E47C00D23A6F /* Madness.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Madness.h; sourceTree = ""; }; D4FC47BC1A37E47C00D23A6F /* Madness-MacTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Madness-MacTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; D4FC47C21A37E47C00D23A6F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D4FC47C31A37E47C00D23A6F /* ParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParserTests.swift; sourceTree = ""; }; D4FC47CD1A37E48800D23A6F /* Parser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Parser.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ D4BC5DF31A98C8B4008C6851 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( BECD3BD51F8D91A8006FF13E /* Result.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; D4BC5DFE1A98C8B4008C6851 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( D4BC5E021A98C8B4008C6851 /* Madness.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; D4FC47AD1A37E47C00D23A6F /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( BECD3BD21F8D918E006FF13E /* Result.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; D4FC47B91A37E47C00D23A6F /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( D4FC47BD1A37E47C00D23A6F /* Madness.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ BECD3BD31F8D9199006FF13E /* Frameworks */ = { isa = PBXGroup; children = ( BECD3BD41F8D91A8006FF13E /* Result.framework */, BECD3BD11F8D918E006FF13E /* Result.framework */, ); name = Frameworks; sourceTree = ""; }; D4FC47A71A37E47C00D23A6F = { isa = PBXGroup; children = ( D4FC47B31A37E47C00D23A6F /* Madness */, D4FC47C01A37E47C00D23A6F /* MadnessTests */, BECD3BD31F8D9199006FF13E /* Frameworks */, D4FC47B21A37E47C00D23A6F /* Products */, ); sourceTree = ""; usesTabs = 1; }; D4FC47B21A37E47C00D23A6F /* Products */ = { isa = PBXGroup; children = ( D4FC47B11A37E47C00D23A6F /* Madness.framework */, D4FC47BC1A37E47C00D23A6F /* Madness-MacTests.xctest */, D4BC5DF71A98C8B4008C6851 /* Madness.framework */, D4BC5E011A98C8B4008C6851 /* Madness-iOSTests.xctest */, ); name = Products; sourceTree = ""; }; D4FC47B31A37E47C00D23A6F /* Madness */ = { isa = PBXGroup; children = ( D4FC47B61A37E47C00D23A6F /* Madness.h */, D4FC47CD1A37E48800D23A6F /* Parser.swift */, D1A6B7F81BE01B8F00B4858C /* SourcePos.swift */, D4C2EDAD1A98D52B00054FAA /* Alternation.swift */, D4C2EDA61A98D38E00054FAA /* Concatenation.swift */, D4D3284B1A9AFE6000216D7E /* Error.swift */, D4C2EDBB1A98D8F800054FAA /* Map.swift */, B8219A931BF1DF05000006F1 /* Negation.swift */, D47B10751A9A9A1C006701A8 /* Reduction.swift */, D4C2EDB31A98D63600054FAA /* Repetition.swift */, 849DC2371C0F21D0004C1A1E /* Combinator.swift */, D4FC47B41A37E47C00D23A6F /* Supporting Files */, D1E153551BD5B78E00627E39 /* String.swift */, ); path = Madness; sourceTree = ""; }; D4FC47B41A37E47C00D23A6F /* Supporting Files */ = { isa = PBXGroup; children = ( D4FC47B51A37E47C00D23A6F /* Info.plist */, ); name = "Supporting Files"; sourceTree = ""; }; D4FC47C01A37E47C00D23A6F /* MadnessTests */ = { isa = PBXGroup; children = ( D4FC47C31A37E47C00D23A6F /* ParserTests.swift */, D4C2EDB01A98D5DB00054FAA /* AlternationTests.swift */, D49092791A98F11A00275C79 /* CollectionTests.swift */, D4C2EDA81A98D49500054FAA /* ConcatenationTests.swift */, 849DC23B1C0F224D004C1A1E /* CombinatorTests.swift */, D4D328481A9AFE2700216D7E /* ErrorTests.swift */, D4C8B02D1A69B1A900943303 /* MapTests.swift */, B8219A971BF1ED07000006F1 /* NegationTests.swift */, D421A2A81A9A8E33009AC3B1 /* IgnoreTests.swift */, D421A2AB1A9A9540009AC3B1 /* ReductionTests.swift */, D4C2EDB81A98D82200054FAA /* RepetitionTests.swift */, B8E0AB861BF098DD002B5C8B /* StringTests.swift */, D4C0FAFE1AC5ECCE00936032 /* Fixtures.swift */, D4FC47C11A37E47C00D23A6F /* Supporting Files */, ); path = MadnessTests; sourceTree = ""; }; D4FC47C11A37E47C00D23A6F /* Supporting Files */ = { isa = PBXGroup; children = ( D4FC47C21A37E47C00D23A6F /* Info.plist */, ); name = "Supporting Files"; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ D4BC5DF41A98C8B4008C6851 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( D4C2EDFC1A98DEE800054FAA /* Madness.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; D4FC47AE1A37E47C00D23A6F /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( D4FC47B71A37E47C00D23A6F /* Madness.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ D4BC5DF61A98C8B4008C6851 /* Madness-iOS */ = { isa = PBXNativeTarget; buildConfigurationList = D4BC5E0A1A98C8B4008C6851 /* Build configuration list for PBXNativeTarget "Madness-iOS" */; buildPhases = ( D4BC5DF21A98C8B4008C6851 /* Sources */, D4BC5DF31A98C8B4008C6851 /* Frameworks */, D4BC5DF41A98C8B4008C6851 /* Headers */, D4BC5DF51A98C8B4008C6851 /* Resources */, ); buildRules = ( ); dependencies = ( ); name = "Madness-iOS"; productName = Madness; productReference = D4BC5DF71A98C8B4008C6851 /* Madness.framework */; productType = "com.apple.product-type.framework"; }; D4BC5E001A98C8B4008C6851 /* Madness-iOSTests */ = { isa = PBXNativeTarget; buildConfigurationList = D4BC5E0D1A98C8B4008C6851 /* Build configuration list for PBXNativeTarget "Madness-iOSTests" */; buildPhases = ( D4BC5DFD1A98C8B4008C6851 /* Sources */, D4BC5DFE1A98C8B4008C6851 /* Frameworks */, D4BC5DFF1A98C8B4008C6851 /* Resources */, ); buildRules = ( ); dependencies = ( D4BC5E041A98C8B4008C6851 /* PBXTargetDependency */, ); name = "Madness-iOSTests"; productName = MadnessTests; productReference = D4BC5E011A98C8B4008C6851 /* Madness-iOSTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; D4FC47B01A37E47C00D23A6F /* Madness-Mac */ = { isa = PBXNativeTarget; buildConfigurationList = D4FC47C71A37E47C00D23A6F /* Build configuration list for PBXNativeTarget "Madness-Mac" */; buildPhases = ( D4FC47AC1A37E47C00D23A6F /* Sources */, D4FC47AD1A37E47C00D23A6F /* Frameworks */, D4FC47AE1A37E47C00D23A6F /* Headers */, D4FC47AF1A37E47C00D23A6F /* Resources */, ); buildRules = ( ); dependencies = ( ); name = "Madness-Mac"; productName = Madness; productReference = D4FC47B11A37E47C00D23A6F /* Madness.framework */; productType = "com.apple.product-type.framework"; }; D4FC47BB1A37E47C00D23A6F /* Madness-MacTests */ = { isa = PBXNativeTarget; buildConfigurationList = D4FC47CA1A37E47C00D23A6F /* Build configuration list for PBXNativeTarget "Madness-MacTests" */; buildPhases = ( D4FC47B81A37E47C00D23A6F /* Sources */, D4FC47B91A37E47C00D23A6F /* Frameworks */, D4FC47BA1A37E47C00D23A6F /* Resources */, ); buildRules = ( ); dependencies = ( D4FC47BF1A37E47C00D23A6F /* PBXTargetDependency */, ); name = "Madness-MacTests"; productName = MadnessTests; productReference = D4FC47BC1A37E47C00D23A6F /* Madness-MacTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ D4FC47A81A37E47C00D23A6F /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0700; LastUpgradeCheck = 0900; ORGANIZATIONNAME = "Rob Rix"; TargetAttributes = { D4BC5DF61A98C8B4008C6851 = { CreatedOnToolsVersion = 6.3; }; D4BC5E001A98C8B4008C6851 = { CreatedOnToolsVersion = 6.3; }; D4FC47B01A37E47C00D23A6F = { CreatedOnToolsVersion = 6.1.1; LastSwiftMigration = 0900; }; D4FC47BB1A37E47C00D23A6F = { CreatedOnToolsVersion = 6.1.1; LastSwiftMigration = 0900; }; }; }; buildConfigurationList = D4FC47AB1A37E47C00D23A6F /* Build configuration list for PBXProject "Madness" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( en, ); mainGroup = D4FC47A71A37E47C00D23A6F; productRefGroup = D4FC47B21A37E47C00D23A6F /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( D4FC47B01A37E47C00D23A6F /* Madness-Mac */, D4FC47BB1A37E47C00D23A6F /* Madness-MacTests */, D4BC5DF61A98C8B4008C6851 /* Madness-iOS */, D4BC5E001A98C8B4008C6851 /* Madness-iOSTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ D4BC5DF51A98C8B4008C6851 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; D4BC5DFF1A98C8B4008C6851 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; D4FC47AF1A37E47C00D23A6F /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; D4FC47BA1A37E47C00D23A6F /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ D4BC5DF21A98C8B4008C6851 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 849DC23A1C0F21E1004C1A1E /* String.swift in Sources */, D4DE2EE71ABCB2D100D3D70A /* Error.swift in Sources */, 849DC2391C0F21D0004C1A1E /* Combinator.swift in Sources */, D4C2EDAF1A98D53400054FAA /* Alternation.swift in Sources */, D4BC5E101A98C978008C6851 /* Parser.swift in Sources */, D4C2EDBD1A98D8F800054FAA /* Map.swift in Sources */, D4C2EDAC1A98D4DE00054FAA /* Concatenation.swift in Sources */, D1A6B7FA1BE01B8F00B4858C /* SourcePos.swift in Sources */, D47B10771A9A9A1C006701A8 /* Reduction.swift in Sources */, B8219A951BF1DF0A000006F1 /* Negation.swift in Sources */, D4C2EDB61A98D65300054FAA /* Repetition.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; D4BC5DFD1A98C8B4008C6851 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( D4C2EDAB1A98D4D600054FAA /* ConcatenationTests.swift in Sources */, D4C2EDBA1A98D82200054FAA /* RepetitionTests.swift in Sources */, 849DC23D1C0F224D004C1A1E /* CombinatorTests.swift in Sources */, D490927B1A98F11A00275C79 /* CollectionTests.swift in Sources */, D4C0FB011AC5EDA500936032 /* Fixtures.swift in Sources */, B8E0AB881BF098DD002B5C8B /* StringTests.swift in Sources */, D4BC5E131A98C97C008C6851 /* MapTests.swift in Sources */, B88CCA091BF2C17700979677 /* NegationTests.swift in Sources */, D4BC5E121A98C97C008C6851 /* ParserTests.swift in Sources */, D4C2EDB21A98D5DB00054FAA /* AlternationTests.swift in Sources */, D421A2AA1A9A8E33009AC3B1 /* IgnoreTests.swift in Sources */, D4D9F28A1A9C42A7002BEFF2 /* ErrorTests.swift in Sources */, D421A2AD1A9A9540009AC3B1 /* ReductionTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; D4FC47AC1A37E47C00D23A6F /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( D4DE2EE61ABCB2D000D3D70A /* Error.swift in Sources */, D4C2EDAE1A98D52B00054FAA /* Alternation.swift in Sources */, D4FC47CE1A37E48800D23A6F /* Parser.swift in Sources */, B8219A941BF1DF05000006F1 /* Negation.swift in Sources */, D4C2EDBC1A98D8F800054FAA /* Map.swift in Sources */, D4C2EDA71A98D38E00054FAA /* Concatenation.swift in Sources */, D1A6B7F91BE01B8F00B4858C /* SourcePos.swift in Sources */, 849DC2381C0F21D0004C1A1E /* Combinator.swift in Sources */, D47B10761A9A9A1C006701A8 /* Reduction.swift in Sources */, D1E153561BD5B78E00627E39 /* String.swift in Sources */, D4C2EDB71A98D65400054FAA /* Repetition.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; D4FC47B81A37E47C00D23A6F /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( D4C2EDAA1A98D4D400054FAA /* ConcatenationTests.swift in Sources */, D4C8B02E1A69B1A900943303 /* MapTests.swift in Sources */, 849DC23C1C0F224D004C1A1E /* CombinatorTests.swift in Sources */, D490927A1A98F11A00275C79 /* CollectionTests.swift in Sources */, D4C0FB021AC5EDA600936032 /* Fixtures.swift in Sources */, B8E0AB871BF098DD002B5C8B /* StringTests.swift in Sources */, D4C2EDB91A98D82200054FAA /* RepetitionTests.swift in Sources */, B8219A981BF1ED07000006F1 /* NegationTests.swift in Sources */, D4FC47C41A37E47C00D23A6F /* ParserTests.swift in Sources */, D4D328491A9AFE2700216D7E /* ErrorTests.swift in Sources */, D4C2EDB11A98D5DB00054FAA /* AlternationTests.swift in Sources */, D421A2A91A9A8E33009AC3B1 /* IgnoreTests.swift in Sources */, D421A2AC1A9A9540009AC3B1 /* ReductionTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ D4BC5E041A98C8B4008C6851 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = D4BC5DF61A98C8B4008C6851 /* Madness-iOS */; targetProxy = D4BC5E031A98C8B4008C6851 /* PBXContainerItemProxy */; }; D4FC47BF1A37E47C00D23A6F /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = D4FC47B01A37E47C00D23A6F /* Madness-Mac */; targetProxy = D4FC47BE1A37E47C00D23A6F /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ D4BC5E0B1A98C8B4008C6851 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); INFOPLIST_FILE = Madness/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.antitypical.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = Madness; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; D4BC5E0C1A98C8B4008C6851 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; COPY_PHASE_STRIP = NO; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Madness/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.antitypical.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = Madness; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; name = Release; }; D4BC5E0E1A98C8B4008C6851 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; DEBUG_INFORMATION_FORMAT = dwarf; FRAMEWORK_SEARCH_PATHS = ( "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", ); GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); INFOPLIST_FILE = MadnessTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.antitypical.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; SWIFT_VERSION = 3.0; }; name = Debug; }; D4BC5E0F1A98C8B4008C6851 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; FRAMEWORK_SEARCH_PATHS = ( "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", ); INFOPLIST_FILE = MadnessTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.antitypical.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; SWIFT_VERSION = 3.0; VALIDATE_PRODUCT = YES; }; name = Release; }; D4FC47C51A37E47C00D23A6F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; MACOSX_DEPLOYMENT_TARGET = 10.9; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; D4FC47C61A37E47C00D23A6F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = YES; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; MACOSX_DEPLOYMENT_TARGET = 10.9; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; D4FC47C81A37E47C00D23A6F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; COMBINE_HIDPI_IMAGES = YES; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_VERSION = A; INFOPLIST_FILE = Madness/Info.plist; INSTALL_PATH = "@rpath"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.antitypical.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = Madness; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; }; name = Debug; }; D4FC47C91A37E47C00D23A6F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; COMBINE_HIDPI_IMAGES = YES; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_VERSION = A; INFOPLIST_FILE = Madness/Info.plist; INSTALL_PATH = "@rpath"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.antitypical.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = Madness; SKIP_INSTALL = YES; SWIFT_VERSION = 4.0; }; name = Release; }; D4FC47CB1A37E47C00D23A6F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { COMBINE_HIDPI_IMAGES = YES; FRAMEWORK_SEARCH_PATHS = ( "$(DEVELOPER_FRAMEWORKS_DIR)", "$(inherited)", ); GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); INFOPLIST_FILE = MadnessTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.antitypical.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 4.0; }; name = Debug; }; D4FC47CC1A37E47C00D23A6F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { COMBINE_HIDPI_IMAGES = YES; FRAMEWORK_SEARCH_PATHS = ( "$(DEVELOPER_FRAMEWORKS_DIR)", "$(inherited)", ); INFOPLIST_FILE = MadnessTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.antitypical.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 4.0; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ D4BC5E0A1A98C8B4008C6851 /* Build configuration list for PBXNativeTarget "Madness-iOS" */ = { isa = XCConfigurationList; buildConfigurations = ( D4BC5E0B1A98C8B4008C6851 /* Debug */, D4BC5E0C1A98C8B4008C6851 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; D4BC5E0D1A98C8B4008C6851 /* Build configuration list for PBXNativeTarget "Madness-iOSTests" */ = { isa = XCConfigurationList; buildConfigurations = ( D4BC5E0E1A98C8B4008C6851 /* Debug */, D4BC5E0F1A98C8B4008C6851 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; D4FC47AB1A37E47C00D23A6F /* Build configuration list for PBXProject "Madness" */ = { isa = XCConfigurationList; buildConfigurations = ( D4FC47C51A37E47C00D23A6F /* Debug */, D4FC47C61A37E47C00D23A6F /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; D4FC47C71A37E47C00D23A6F /* Build configuration list for PBXNativeTarget "Madness-Mac" */ = { isa = XCConfigurationList; buildConfigurations = ( D4FC47C81A37E47C00D23A6F /* Debug */, D4FC47C91A37E47C00D23A6F /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; D4FC47CA1A37E47C00D23A6F /* Build configuration list for PBXNativeTarget "Madness-MacTests" */ = { isa = XCConfigurationList; buildConfigurations = ( D4FC47CB1A37E47C00D23A6F /* Debug */, D4FC47CC1A37E47C00D23A6F /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = D4FC47A81A37E47C00D23A6F /* Project object */; } ================================================ FILE: Madness.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: Madness.xcodeproj/xcshareddata/xcschemes/Madness-Mac.xcscheme ================================================ ================================================ FILE: Madness.xcodeproj/xcshareddata/xcschemes/Madness-iOS.xcscheme ================================================ ================================================ FILE: Madness.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: MadnessTests/AlternationTests.swift ================================================ // Copyright (c) 2015 Rob Rix. All rights reserved. final class AlternationTests: XCTestCase { // MARK: Alternation func testAlternationParsesEitherAlternative() { assertAdvancedBy(alternation, input: "xy".characters, lineOffset: 0, columnOffset: 1, offset: 1) assertAdvancedBy(alternation, input: "yx".characters, lineOffset: 0, columnOffset: 1, offset: 1) } func testAlternationOfASingleTypeCoalescesTheParsedValue() { assertTree(alternation, "xy".characters, ==, "x") } // MARK: Optional func testOptionalProducesWhenPresent() { assertTree(optional, "y".characters, ==, "y") assertTree(prefixed, "xy".characters, ==, "xy") assertTree(suffixed, "yzsandwiched".characters, ==, "yz") } func testOptionalProducesWhenAbsent() { assertTree(optional, "".characters, ==, "") assertTree(prefixed, "x".characters, ==, "x") assertTree(suffixed, "z".characters, ==, "z") assertTree(sandwiched, "xz".characters, ==, "xz") } // MARK: One-of func testOneOfParsesFirstMatch() { assertTree(one, "xyz".characters, ==, "x") assertTree(one, "yzx".characters, ==, "y") assertTree(one, "zxy".characters, ==, "z") } // MARK: Any-of func testAnyOfParsesAnArrayOfMatchesPreservingOrder() { assertTree(any, "xy".characters, ==, ["x", "y"]) assertTree(any, "yx".characters, ==, ["y", "x"]) assertTree(any, "zxy".characters, ==, ["z", "x", "y"]) } func testAnyOfRejectsWhenNoneMatch() { assertUnmatched(anyOf(Set("x")), Set("y".characters)) } func testAnyOfOnlyParsesFirstMatch() { assertTree(any, "xyy".characters, ==, ["x", "y"]) } // MARK: All-of func testAllOfParsesAnArrayOfMatchesPreservingOrder() { assertTree(all, "xy".characters, ==, ["x", "y"]) assertTree(all, "yx".characters, ==, ["y", "x"]) assertTree(all, "zxy".characters, ==, ["z", "x", "y"]) } func testAllOfRejectsWhenNoneMatch() { assertUnmatched(allOf(Set("x")), Set(["y"])) } } // MARK: - Fixtures private let alternation = %"x" <|> %"y" private let optional = map({ $0 ?? "" })((%"y")|?) private let prefixed = { x in { y in x + y } } <^> %"x" <*> optional private let suffixed = { x in { y in x + y } } <^> optional <*> %"z" private let sandwiched = { x in { y in x + y } } <^> prefixed <*> %"z" private let arrayOfChars: Set = ["x", "y", "z"] private let chars: String = "xyz" private let one = oneOf(chars) private let any: Parser.Function = anyOf(arrayOfChars) private let all: Parser.Function = allOf(arrayOfChars) // MARK: - Imports import Madness import Result import XCTest ================================================ FILE: MadnessTests/CollectionTests.swift ================================================ // Copyright (c) 2015 Rob Rix. All rights reserved. final class CollectionTests: XCTestCase { func testParsingCollections() { let input = [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144] typealias Fibonacci = Parser<[Int], [Int]>.Function func fibonacci(_ x: Int, _ y: Int) -> Fibonacci { let combined: Fibonacci = %(x + y) >>- { (xy: Int) -> Fibonacci in { [ xy ] + $0 } <^> fibonacci(y, xy) } return combined <|> pure([]) } XCTAssertEqual(parse(fibonacci(0, 1), input: input).value!, input) } } // MARK: - Imports import Madness import XCTest ================================================ FILE: MadnessTests/CombinatorTests.swift ================================================ // Copyright © 2015 Rob Rix. All rights reserved. final class CombinatorTests: XCTestCase { // MARK: - between let braces: (@escaping StringParser) -> StringParser = between(%"{", %"}") func testBetweenCombinatorParsesSandwichedString(){ assertTree(braces(%"a"), "{a}".characters, ==, "a") } func testBetweenCombinatorAcceptsEmptyString(){ assertTree(braces(%""), "{}".characters, ==, "") } // MARK: - manyTill let digits = manyTill(digit, %",") func testManyTillCombinatorParsesElementsUntilEndParser(){ assertTree(digits, "123,".characters, ==, ["1", "2", "3"]) } func testManyTillCombinatorAcceptsEmptyString(){ assertTree(digits, ",".characters, ==, []) } } // MARK: - Imports import Madness import XCTest ================================================ FILE: MadnessTests/ConcatenationTests.swift ================================================ // Copyright (c) 2015 Rob Rix. All rights reserved. final class ConcatenationTests: XCTestCase { let concatenation = lift(pair) <*> %"x" <*> %"y" func testConcatenationRejectsPartialParses() { assertUnmatched(concatenation, "x".characters) } func testConcatenationParsesBothOperands() { assertAdvancedBy(concatenation, input: "xyz".characters, lineOffset: 0, columnOffset: 2, offset: 2) } func testConcatenationProducesPairsOfTerms() { let input = "xy".characters let parsed = concatenation(input, SourcePos(index: input.startIndex)) XCTAssertEqual(parsed.value?.0.0, "x") XCTAssertEqual(parsed.value?.0.1, "y") } } func matches(_ parser: Parser.Function, input: C) -> Bool { return parser(input, SourcePos(index: input.startIndex)).value != nil } func doesNotMatch(_ parser: Parser.Function, input: C) -> Bool { return parser(input, SourcePos(index: input.startIndex)).value == nil } func assertUnmatched(_ parser: Parser.Function, _ input: C, message: String = "", file: StaticString = #file, line: UInt = #line) { XCTAssertNil(parser(input, SourcePos(index: input.startIndex)).value, "should not have matched \(input). " + message, file: file, line: line) } func assertMatched(_ parser: Parser.Function, input: C, message: String = "", file: StaticString = #file, line: UInt = #line) { XCTAssertNotNil(parser(input, SourcePos(index: input.startIndex)).value, "should have matched \(input). " + message, file: file, line: line) } func assertTree(_ parser: Parser.Function, _ input: C, _ match: @escaping (T, T) -> Bool, _ tree: T, message: String = "", file: StaticString = #file, line: UInt = #line) { let parsed: Parser.Result = parser(input, SourcePos(index: input.startIndex)) let value = parsed.value?.0 XCTAssert(value.map { match($0, tree) } ?? false, "should have parsed \(input) as \(tree). " + message, file: file, line: line) } func assertAdvancedBy(_ parser: Parser.Function, input: C, offset: C.IndexDistance, message: String = "", file: StaticString = #file, line: UInt = #line) { let pos = SourcePos(index: input.startIndex) let newSourcePos: SourcePos? = SourcePos(line: pos.line, column: pos.column, index: input.index(pos.index, offsetBy: offset)) let value = parser(input, pos).value XCTAssertNotNil(value, "should have parsed \(input) and advanced by \(offset). " + message, file: file, line: line) XCTAssertEqual(value?.1, newSourcePos, "should have parsed \(input) and advanced by \(offset). " + message, file: file, line: line) } func assertAdvancedBy(_ parser: Parser.Function, input: C, lineOffset: Line, columnOffset: Column, offset: C.IndexDistance, message: String = "", file: StaticString = #file, line: UInt = #line) { let pos = SourcePos(index: input.startIndex) let newSourcePos: SourcePos? = SourcePos(line: pos.line + lineOffset, column: pos.column + columnOffset, index: input.index(pos.index, offsetBy: offset)) let value = parser(input, pos).value XCTAssertNotNil(value, "should have parsed \(String(describing: input)) and advanced by \(offset). " + message, file: file, line: line) XCTAssertEqual(value?.1, newSourcePos, "should have parsed \(String(describing: input)) and advanced by \(offset). " + message, file: file, line: line) } // MARK: - Imports import Madness import XCTest ================================================ FILE: MadnessTests/ErrorTests.swift ================================================ // Copyright (c) 2015 Rob Rix. All rights reserved. final class ErrorTests: XCTestCase { func testLiftedParsersDoNotReportErrorsWhenTheyMatch() { let parser = %"x" let input = "x".characters let sourcePos = SourcePos(index: input.startIndex) XCTAssertNotNil(parser(input, sourcePos).value) XCTAssertNil(parser(input, sourcePos).error) } func testLiftedParsersReportErrorsWhenTheyDoNotMatch() { let parser = %"x" let input = "y" let sourcePos = SourcePos(index: input.startIndex) XCTAssertNil(parser(input.characters, sourcePos).value) XCTAssertNotNil(parser(input.characters, sourcePos).error) } func testParseError() { XCTAssertEqual(parse(lambda, input: "λx.").error?.depth, 5) } func testParseNaming() { XCTAssertNotNil(parse(describeAs("lambda")(lambda), input: "λx.").error) } } // MARK: - Imports import Madness import XCTest ================================================ FILE: MadnessTests/Fixtures.swift ================================================ // Copyright (c) 2015 Rob Rix. All rights reserved. extension String { public static func lift(_ parser: @escaping Parser.Function) -> Parser.Function { return { parser($0.characters, $1) } } } /// Returns the least fixed point of the function returned by `f`. /// /// This is useful for e.g. making recursive closures without using the two-step assignment dance. /// /// \param f - A function which takes a parameter function, and returns a result function. The result function may recur by calling the parameter function. /// /// \return A recursive function. func fix(_ f: @escaping (@escaping (T) -> U) -> (T) -> U) -> (T) -> U { return { f(fix(f))($0) } } typealias LambdaParser = Parser.Function func lambda(_ input: String, sourcePos: SourcePos) -> Parser.Result { let symbol: Parser.Function = String.lift(%("a"..."z")) let variable: LambdaParser = Lambda.variable <^> symbol let abstraction: LambdaParser = { x in { y in Lambda.abstraction(x, y) } } <^> (%"λ" *> symbol) <*> (%"." *> lambda) let application: LambdaParser = { x in { y in Lambda.application(x, y) } } <^> (%"(" *> lambda) <*> (%" " *> lambda) <* %")" let parser: LambdaParser = variable <|> abstraction <|> application return parser(input, sourcePos) } enum Lambda: CustomStringConvertible { case variable(String) indirect case abstraction(String, Lambda) indirect case application(Lambda, Lambda) var description: String { switch self { case let .variable(symbol): return symbol case let .abstraction(symbol, body): return "λ\(symbol).\(body.description)" case let .application(x, y): return "(\(x.description) \(y.description))" } } } import Madness ================================================ FILE: MadnessTests/IgnoreTests.swift ================================================ // Copyright (c) 2015 Rob Rix. All rights reserved. final class IgnoreTests: XCTestCase { let ignored = %"x" func testIgnoredInputDoesNotGetConcatenatedAtLeft() { assertTree(ignored *> %"y", "xy".characters, ==, "y") } func testIgnoredInputDoesNotGetConcatenatedAtRight() { assertTree(%"y" <* ignored, "yx".characters, ==, "y") } func testRepeatedIgnoredEmptyParsesAreDropped() { assertTree(many(ignored) *> %"y", "y".characters, ==, "y") } func testRepeatedIgnoredParsesAreDropped() { assertTree(many(ignored) *> %"y", "xxy".characters, ==, "y") } } // MARK: - Imports import Madness import XCTest ================================================ FILE: MadnessTests/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType BNDL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 ================================================ FILE: MadnessTests/MapTests.swift ================================================ // Copyright (c) 2015 Rob Rix. All rights reserved. private struct Tree: Equatable, CustomStringConvertible { init(_ value: T, _ children: [Tree] = []) { self.values = [ value ] self.children = children } let values: [T] let children: [Tree] // MARK: Printable var description: String { let space = " " let valueString = values.map({ String(describing: $0) }).joined(separator: space) return children.count > 0 ? "(\(valueString) \(children.map({ String(describing: $0) }).joined(separator: space)))" : "(\(valueString))" } } private func == (left: Tree, right: Tree) -> Bool { return left.values == right.values && left.children == right.children } private func == (l: (T, U), r: (T, U)) -> Bool { return l.0 == r.0 && l.1 == r.1 } final class MapTests: XCTestCase { // MARK: flatMap func testFlatMap() { let item: Parser.Function = %"-" *> String.lift(%("a"..."z")) <* %"\n" let tree: (Int) -> Parser>.Function = fix { tree in { n in let line: Parser.Function = (%"\t" * n) *> item return line >>- { itemContent in map({ children in Tree(itemContent, children) })(many(tree(n + 1))) } } } let fixtures: [String: Tree] = [ "-a\n": Tree("a"), "-a\n\t-b\n": Tree("a", [ Tree("b") ]), "-a\n\t-b\n\t-c\n": Tree("a", [ Tree("b"), Tree("c") ]), "-a\n\t-b\n\t\t-c\n\t-d\n": Tree("a", [ Tree("b", [ Tree("c") ]), Tree("d") ]), ] for (input, actual) in fixtures { if let parsed = parse(tree(0), input: input).value { XCTAssertEqual(parsed, actual) } else { XCTFail("expected to parse \(input) as \(actual) but failed to parse") } } let failures: [String] = [ "-a\n-a\n", "-a\n\t\t-b\n", "-a\n\t-b\n-c\n" ] for input in failures { XCTAssert(parse(tree(0), input: input).value == nil) } } // MARK: map func testMapTransformsParserOutput() { assertTree(String.init <^> %123, [123], ==, "123") } func testMapHasHigherPrecedenceThanFlatMap() { let addTwo = { $0 + 2 } let triple = { $0 * 3 } let parser: Parser<[Int], Int>.Function = addTwo <^> %2 >>- { i in triple <^> pure(i) } assertTree(parser, [2], ==, 12) } func testReplaceConsumesItsInput() { assertTree(lift(pair) <*> ("abc" <^ %123) <*> %0, [123, 0], ==, ("abc", 0)) } func testCurriedMap() { assertTree(map({ String($0) })(%123), [123], ==, "123") } // MARK: pure func testPureIgnoresItsInput() { assertTree(pure("a"), "b".characters, ==, "a") } } // MARK: - Imports import Madness import XCTest ================================================ FILE: MadnessTests/NegationTests.swift ================================================ // Copyright (c) 2015 Rob Rix. All rights reserved. class NegationTests: XCTestCase { let notA: CharacterParser = not(%"a") *> any func testNegativeLookaheadRejectsMatches() { assertUnmatched(notA, "a".characters) } func testNegativeLookaheadAcceptsNonMatches() { assertTree(notA, "b".characters, ==, "b") } let upToBang: CharacterArrayParser = many(not(%"!") *> any) <* many(any) func testNegativeLooaheadAccumulation() { assertTree(upToBang, "xy!z".characters, ==, ["x", "y"]) } func testNegativeLooaheadAccumulationWithoutMatch() { assertTree(upToBang, "xyz".characters, ==, ["x", "y", "z"]) } } // MARK: - Imports import Madness import XCTest ================================================ FILE: MadnessTests/ParserTests.swift ================================================ // Copyright (c) 2014 Rob Rix. All rights reserved. import Madness import Result import XCTest final class ParserTests: XCTestCase { // MARK: - Operations func testParseRejectsPartialParses() { XCTAssertNil(parse(%("x".characters), input: "xy".characters).value) } func testParseProducesParseTreesForFullParses() { XCTAssertEqual(parse(%"x", input: "x").value, "x") } // MARK: - Terminals // MARK: Literals func testLiteralParsersParseAPrefixOfTheInput() { let parser = %"foo" assertAdvancedBy(parser, input: "foot".characters, lineOffset: 0, columnOffset: 3, offset: 3) assertUnmatched(parser, "fo".characters) } func testLiteralParsersProduceTheirArgument() { assertTree(%"foo", "foot".characters, ==, "foo") } // MARK: Ranges let digits = %("0"..."9") func testRangeParsersParseAnyCharacterInTheirRange() { assertTree(digits, "0".characters, ==, "0") assertTree(digits, "5".characters, ==, "5") assertTree(digits, "9".characters, ==, "9") } func testRangeParsersRejectCharactersOutsideTheRange() { assertUnmatched(digits, "a".characters) } // MARK: None func testNoneDoesNotConsumeItsInput() { assertTree(none() <|> %"a", "a", ==, "a") } func testNoneIsIdentityForAlternation() { let parser = [%"a", %"b", %"c"].reduce(none(), <|>) assertTree(parser, "a".characters, ==, "a") assertTree(parser, "b".characters, ==, "b") assertTree(parser, "c".characters, ==, "c") } // MARK: Any func testAnyRejectsTheEmptyString() { assertUnmatched(any, "".characters) } func testAnyParsesAnySingleCharacter() { assertTree(any, "🔥".characters, ==, "🔥") } // MARK: satisfy func testSatisfyIncrementsLinesOverNewlineCharacters() { let parser = any *> %"foo" assertAdvancedBy(parser, input: "\nfoot".characters, lineOffset: 1, columnOffset: 2, offset: 4) } } ================================================ FILE: MadnessTests/ReductionTests.swift ================================================ // Copyright (c) 2015 Rob Rix. All rights reserved. final class ReductionTests: XCTestCase { let reduction = %"x" --> { $4.uppercased() } func testMapsParseTreesWithAFunction() { assertTree(reduction, "x".characters, ==, "X") } func testRejectsInputRejectedByItsParser() { assertUnmatched(reduction, "y".characters) } enum Value { case null } let constReduction = %"null" --> { _, _, _, _, _ in Value.null } func testMapsConstFunctionOverInput() { assertTree(constReduction, "null".characters, ==, Value.null) } let reductionWithIndex = %"x" --> { "\($4.uppercased()):\($0.distance(from: $0.startIndex, to: $3.lowerBound))..<\($0.distance(from: $0.startIndex, to: $3.upperBound))" } func testMapsParseTreesWithAFunctionWhichTakesTheSourceIndex() { assertTree(reductionWithIndex, "x".characters, ==, "X:0..<1") } } // MARK: - Imports import Madness import XCTest ================================================ FILE: MadnessTests/RepetitionTests.swift ================================================ // Copyright (c) 2015 Rob Rix. All rights reserved. final class RepetitionTests: XCTestCase { let zeroOrMore: Parser.Function = many(%"x") func testZeroOrMoreRepetitionAcceptsTheEmptyString() { assertMatched(zeroOrMore, input: "".characters) } func testZeroOrMoreRepetitionAcceptsUnmatchedStrings() { assertMatched(zeroOrMore, input: "y".characters) } func testZeroOrMoreRepetitionDoesNotAdvanceWithUnmatchedStrings() { assertAdvancedBy(zeroOrMore, input: "y".characters, offset: 0) } func testZeroOrMoreRepetitionParsesUnmatchedStringsAsEmptyArrays() { assertTree(zeroOrMore, "y".characters, ==, []) } func testZeroOrMoreRepetitionParsesAMatchedString() { assertTree(zeroOrMore, "x".characters, ==, ["x"]) } func testZeroOrMoreRepetitionParsesMatchedStrings() { assertTree(zeroOrMore, "xx".characters, ==, ["x", "x"]) } let oneOrMore = some(%"x") func testOneOrMoreRepetitionRejectsTheEmptyString() { assertUnmatched(oneOrMore, "".characters) } func testOneOrMoreRepetitionParsesASingleMatchedString() { assertTree(oneOrMore, "x".characters, ==, ["x"]) } func testOneOrMoreRepetitonParsesMultipleMatchedStrings() { assertTree(oneOrMore, "xxy".characters, ==, ["x", "x"]) } let exactlyN = %"x" * 3 func testExactlyNRepetitionParsesNTrees() { assertTree(exactlyN, "xxx".characters, ==, ["x", "x", "x"]) } func testExactlyNRepetitionParsesRejectsFewerMatches() { assertUnmatched(exactlyN, "xx".characters) } func testExactlyNRepetitionParsesStopsAtN() { assertAdvancedBy(exactlyN, input: "xxxx".characters, lineOffset: 0, columnOffset: 3, offset: 3) } let zeroToN = %"x" * (0..<2) func testZeroToNRepetitionParsesZeroTrees() { assertTree(zeroToN, "y".characters, ==, []) } func testZeroToNRepetitionParsesUpToButNotIncludingNTrees() { assertTree(zeroToN, "xxx".characters, ==, ["x"]) assertAdvancedBy(zeroToN, input: "xxx".characters, lineOffset: 0, columnOffset: 1, offset: 1) } let atLeastN = %"x" * (2.. Bool { return left.count == right.count } // MARK: - Imports import Madness import XCTest ================================================ FILE: MadnessTests/StringTests.swift ================================================ // Copyright (c) 2015 Rob Rix. All rights reserved. final class StringTests: XCTestCase { func testSimpleIntegers() { assertNumber("1") assertNumber("45") assertNumber("-13") } func testSimpleFloats() { assertNumber("1.0") assertNumber("45.3") assertNumber("-2.53") } func testIntegerUnsignedExponents() { assertNumber("2E1") assertNumber("1e2") assertNumber("0e3") assertNumber("-1e4") assertNumber("-2E5") assertNumber("1e21") } func testIntegerSignedExponents() { assertNumber("8e+1") assertNumber("7e-2") assertNumber("6E+3") assertNumber("5E-4") assertNumber("-4e+5") assertNumber("-3e-6") assertNumber("-2E+7") assertNumber("-1E-8") } func testFloatUnsignedExponents() { assertNumber("1.2e1") assertNumber("4.567E2") assertNumber("1.0e4") assertNumber("-6.21e3") assertNumber("-1.5E2") } func testFloatSignedExponents() { assertNumber("1.4e+5") assertNumber("2.5e-6") assertNumber("3.6E+7") assertNumber("4.7E-8") assertNumber("-5.8E-9") } } func assertNumber(_ input: String, message: String = "", file: StaticString = #file, line: UInt = #line) { return XCTAssertEqual(parse(number, input: input).value, Double(input)!, message, file: file, line: line) } // MARK: - Imports import Madness import XCTest ================================================ FILE: README.md ================================================ # Recursive Descent into Madness Madness is a Swift µframework for parsing strings in simple context-free grammars. Combine parsers from simple Swift expressions and parse away: ```swift let digit = %("0"..."9") <|> %("a"..."f") <|> %("A"..."F") let hex = digit+ |> map { strtol(join("", $0), nil, 16) } parse(%"0x" *> hex, "0xdeadbeef") // => 3,735,928,559 ``` Your parsers can produce your own model objects directly, making Madness ideal for experimenting with grammars, for example in a playground. ![screenshot of parsing HTML colours in an Xcode playground: `let reddish = parse(colour, "#d52a41")!`](https://cloud.githubusercontent.com/assets/59671/5415280/1453c774-81f4-11e4-8726-b51423bb06f9.png) See `Madness.playground` for some examples of parsing with Madness. ## Use - **Lexing** Madness can be used to write lexers, lexeme parsers, and scannerless parsers. @bencochran has built a [lexer and parser for the LLVM tutorial language, Kaleidoscope](https://github.com/bencochran/KaleidoscopeLang). - **Any** ```swift any ``` parses any single character. - **Strings** ```swift %"hello" ``` parses the string “hello”. - **Ranges** ```swift %("a"..."z") ``` parses any lowercase letter from “a” to “z” inclusive. - **Concatenation** ```swift x <*> y <*> z ``` parses `x` followed by `y` and produces parses as `(X, Y)`. - **Alternation** ```swift x <|> y ``` parses `x`, and if it fails, `y`, and produces parses as `Either`. If `x` and `y` are of the same type, then it produces parses as `X`. ```swift oneOf([x1, x2, x3]) ``` tries a sequence of parsers until the first success, producing parses as `X`. ```swift anyOf(["x", "y", "z"]) ``` tries to parse one each of a set of literals in sequence, collecting each successful parse into an array until none match. ```swift allOf(["x", "y", "z"]) ``` greedier than `anyOf`, parsing every match from a set of literals in sequence, including duplicates. - **Repetition** ```swift x* ``` parses `x` 0 or more times, producing parses as `[X]`. ```swift x+ ``` parses `x` one or more times. ```swift x * 3 ``` parses `x` exactly three times. ```swift x * (3..<6) ``` parses `x` three to five times. Use `Int.max` for the upper bound to parse three or more times. - **Mapping** ```swift x |> map { $0 } { $0 } <^> x x --> { _, _, y in y } ``` parses `x` and maps its parse trees using the passed function. Use mapping to build your model objects. `-->` passes the input and parsed range as well as the parsed data for e.g. error reporting or AST construction. - **Ignoring** Some text is just decoration. `x *> y` parses `x` and then `y` just like `<*>`, but drops the result of `x`. `x <* y` does the same, but drops the result of `y`. API documentation is in the source. ## This way Madness lies ### ∞ loop de loops Madness employs simple—naïve, even—recursive descent parsing. Among other things, that means that it can’t parse any arbitrary grammar that you could construct with it. In particular, it can’t parse left-recursive grammars: ```swift let number = %("0"..."9") let addition = expression <*> %"+" <*> expression let expression = addition <|> number ``` `expression` is left-recursive: its first term is `addition`, whose first term is `expression`. This will cause infinite loops every time `expression` is invoked; try to avoid it. ### I love ambiguity more than [@numist](https://twitter.com/numist/status/423722622031908864) Alternations try their left operand before their operand, and are short-circuiting. This means that they disambiguate (arbitrarily) to the left, which can be handy; but this can have unintended consequences. For example, this parser: ```swift %"x" <|> %"xx" ``` will not parse “xx” completely. ## Integration 1. Add this repository as a submodule and check out its dependencies, and/or [add it to your Cartfile](https://github.com/Carthage/Carthage/blob/master/Documentation/Artifacts.md#cartfile) if you’re using [carthage](https://github.com/Carthage/Carthage/) to manage your dependencies. 2. Drag `Madness.xcodeproj` into your project or workspace, and do the same with its dependencies (i.e. the other `.xcodeproj` files included in `Madness.xcworkspace`). NB: `Madness.xcworkspace` is for standalone development of Madness, while `Madness.xcodeproj` is for targets using Madness as a dependency. 3. Link your target against `Madness.framework` and each of the dependency frameworks. 4. Application targets should ensure that the framework gets copied into their application bundle. (Framework targets should instead require the application linking them to include Madness and its dependencies.) ================================================ FILE: script/cibuild ================================================ #!/bin/bash BUILD_DIRECTORY="build" CONFIGURATION=Release if [[ -z $TRAVIS_XCODE_WORKSPACE ]]; then echo "Error: \$TRAVIS_XCODE_WORKSPACE is not set." exit 1 fi if [[ -z $TRAVIS_XCODE_SCHEME ]]; then echo "Error: \$TRAVIS_XCODE_SCHEME is not set!" exit 1 fi if [[ -z $XCODE_ACTION ]]; then echo "Error: \$XCODE_ACTION is not set!" exit 1 fi if [[ -z $XCODE_SDK ]]; then echo "Error: \$XCODE_SDK is not set!" exit 1 fi if [[ -z $XCODE_DESTINATION ]]; then echo "Error: \$XCODE_DESTINATION is not set!" exit 1 fi set -o pipefail xcodebuild $XCODE_ACTION \ -workspace "$TRAVIS_XCODE_WORKSPACE" \ -scheme "$TRAVIS_XCODE_SCHEME" \ -sdk "$XCODE_SDK" \ -destination "$XCODE_DESTINATION" \ -derivedDataPath "${BUILD_DIRECTORY}" \ -configuration $CONFIGURATION \ ENABLE_TESTABILITY=YES \ GCC_GENERATE_DEBUGGING_SYMBOLS=NO \ RUN_CLANG_STATIC_ANALYZER=NO | xcpretty result=$? if [ "$result" -ne 0 ]; then exit $result fi # Compile code in playgrounds if [[ -n $XCODE_PLAYGROUND ]]; then echo "Validating playground..." . script/validate-playground.sh fi ================================================ FILE: script/validate-playground.sh ================================================ #!/bin/bash # Bash script to lint the content of playgrounds # Heavily based on RxSwift's # https://github.com/ReactiveX/RxSwift/blob/master/scripts/validate-playgrounds.sh if [ -z "$BUILD_DIRECTORY" ]; then echo "\$BUILD_DIRECTORY is not set. Are you trying to run \`validate-playgrounds.sh\` without building Logician first?\n" echo "To validate the playground, run \`script/build\`." exit 1 fi if [ -z "$XCODE_PLAYGROUND" ]; then echo "\$XCODE_PLAYGROUND is not set." exit 1 fi PAGES_PATH=${BUILD_DIRECTORY}/Build/Products/${CONFIGURATION}/all-playground-pages.swift cat ${XCODE_PLAYGROUND}/Sources/*.swift ${XCODE_PLAYGROUND}.playground/Pages/**/*.swift > ${PAGES_PATH} swift -v -target "x86_64-apple-macosx10.10" -D NOT_IN_PLAYGROUND -F ${BUILD_DIRECTORY}/Build/Products/${CONFIGURATION} ${PAGES_PATH} > /dev/null result=$? # Cleanup rm -Rf $BUILD_DIRECTORY exit $result