Skip to content

Commit

Permalink
Improve documentation and style
Browse files Browse the repository at this point in the history
* Remove redundant followed(by:) function
* Improve documentation
* Clean up style
* Spaces after commas
* Colon placement
  • Loading branch information
fcanas authored Mar 18, 2019
1 parent 8372b20 commit 9ca75aa
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 58 deletions.
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ let package = Package(
// Products define the executables and libraries produced by a package, and make them visible to other packages.
.library(
name: "FFCParserCombinator",
targets: ["FFCParserCombinator"]),
targets: ["FFCParserCombinator"])
],
dependencies: [
// Dependencies declare other packages that this package depends on.
Expand All @@ -23,6 +23,6 @@ let package = Package(
dependencies: []),
.testTarget(
name: "FFCParserCombinatorTests",
dependencies: ["FFCParserCombinator"]),
dependencies: ["FFCParserCombinator"])
]
)
98 changes: 57 additions & 41 deletions Sources/FFCParserCombinator/FFCParserCombinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,42 +6,49 @@
// Adapted from https://github.com/objcio/s01e13-parsing-techniques
//

public struct Parser<S,A> {
public struct Parser<S, A> {
let parse: (S) -> (A, S)?
}

public extension Parser {

func map<Result>(_ f: @escaping (A) -> Result) -> Parser<S,Result> {
return Parser<S,Result> { stream in
/// Returns a Parser mapping the given closure over the receiving Parser's match.
///
/// - Parameter transform: A mapping closure. transform accepts an match from this
/// parser as its parameter and returns a transformed value
/// of the same or of a different type.
/// - Returns: A parser that matches according to the receiver with transform applied to
/// the matched results.
func map<Result>(_ transform: @escaping (A) -> Result) -> Parser<S, Result> {
return Parser<S, Result> { stream in
guard let (result, newStream) = self.parse(stream) else { return nil }
return (f(result), newStream)
return (transform(result), newStream)
}
}

func flatMap<Result>(_ f: @escaping (A) -> Result?) -> Parser<S,Result> {
return Parser<S,Result> { stream in
func flatMap<Result>(_ f: @escaping (A) -> Result?) -> Parser<S, Result> {
return Parser<S, Result> { stream in
guard let (result, newStream) = self.parse(stream) else { return nil }
guard let mappedResult = f(result) else { return nil }
return (mappedResult, newStream)
}
}

/// Parses zero or more consecutive elements into an array
var many: Parser<S,[A]> {
var many: Parser<S, [A]> {
return atLeast(0)
}

/// Parses one or more consecutive elements into an array
var many1: Parser<S,[A]> {
var many1: Parser<S, [A]> {
return atLeast(1)
}

/// Parses `min` or more elements into an array
///
/// - Parameter min: the minimum number of elements to match
/// - Returns: A parser that matches the receiver at least `min` times.
func atLeast(_ min: UInt) -> Parser<S,[A]> {
func atLeast(_ min: UInt) -> Parser<S, [A]> {
return from(min, upTo: nil)
}

Expand All @@ -53,12 +60,12 @@ public extension Parser {
/// - min: the minimum number of elements to match
/// - max: the maximum number of elements to match
/// - Returns: A parser that matches the receiver at least `min` times.
func between(_ min: UInt, and max: UInt) -> Parser<S,[A]> {
func between(_ min: UInt, and max: UInt) -> Parser<S, [A]> {
return from(min, upTo: max)
}

private func from(_ min: UInt, upTo max: UInt?) -> Parser<S,[A]> {
return Parser<S,[A]> { stream in
private func from(_ min: UInt, upTo max: UInt?) -> Parser<S, [A]> {
return Parser<S, [A]> { stream in
var result: [A] = []
var remainder = stream
while max == nil || result.count < max!, let (element, newRemainder) = self.parse(remainder) {
Expand All @@ -72,34 +79,30 @@ public extension Parser {
}
}

func or(_ other: Parser<S,A>) -> Parser<S,A> {
func or(_ other: Parser<S, A>) -> Parser<S, A> {
return Parser { stream in
return self.parse(stream) ?? other.parse(stream)
}
}

func followed<B, C>(by other: Parser<S,B>, combine: @escaping (A, B) -> C) -> Parser<S,C> {
return Parser<S,C> { stream in
func followed<B, C>(by other: Parser<S, B>, combine: @escaping (A, B) -> C) -> Parser<S, C> {
return Parser<S, C> { stream in
guard let (result, remainder) = self.parse(stream) else { return nil }
guard let (result2, remainder2) = other.parse(remainder) else { return nil }
return (combine(result,result2), remainder2)
return (combine(result, result2), remainder2)
}
}

func followed<B>(by other: Parser<S,B>) -> Parser<S,(A, B)> {
return followed(by: other, combine: { ($0, $1) })
}

func group<B, C>(into other: Parser<S,(B, C)>) -> Parser<S,(B, C, A)> {
return Parser<S,(B, C, A)> { stream in
func group<B, C>(into other: Parser<S, (B, C)>) -> Parser<S, (B, C, A)> {
return Parser<S, (B, C, A)> { stream in
guard let (resultBC, remainderBC) = other.parse(stream) else { return nil }
guard let (result, remainder) = self.parse(remainderBC) else { return nil }
return ((resultBC.0, resultBC.1, result), remainder)
}
}

func group<B, C, D>(into other: Parser<S,(B, C, D)>) -> Parser<S,(B, C, D, A)> {
return Parser<S,(B, C, D, A)> { stream in
func group<B, C, D>(into other: Parser<S, (B, C, D)>) -> Parser<S, (B, C, D, A)> {
return Parser<S, (B, C, D, A)> { stream in
guard let (resultBCD, remainderBCD) = other.parse(stream) else { return nil }
guard let (result, remainder) = self.parse(remainderBCD) else { return nil }
return ((resultBCD.0, resultBCD.1, resultBCD.2, result), remainder)
Expand All @@ -110,12 +113,11 @@ public extension Parser {
parse = { stream in (result, stream) }
}

var optional: Parser<S,A?> {
return self.map({ .some($0) }).or(Parser<S,A?>(result: nil))
var optional: Parser<S, A?> {
return self.map({ .some($0) }).or(Parser<S, A?>(result: nil))
}
}


func curry<A, B, C>(_ f: @escaping (A, B) -> C) -> (A) -> (B) -> C {
return { x in { y in f(x, y) } }
}
Expand Down Expand Up @@ -151,34 +153,41 @@ infix operator <* : ParserPrecedence
infix operator <|> : ParserConjuctionPrecedence
infix operator <<& : ParserGroupPrecendence

public func <^><S, A, B>(f: @escaping (A) -> B, rhs: Parser<S,A>) -> Parser<S,B> {
public func <^><S, A, B>(f: @escaping (A) -> B, rhs: Parser<S, A>) -> Parser<S, B> {
return rhs.map(f)
}

public func <^!><S, A, B>(f: @escaping (A) -> B?, rhs: Parser<S,A>) -> Parser<S,B> {
public func <^!><S, A, B>(f: @escaping (A) -> B?, rhs: Parser<S, A>) -> Parser<S, B> {
return rhs.flatMap(f)
}

public func <^><S, A, B, R>(f: @escaping (A, B) -> R, rhs: Parser<S,A>) -> Parser<S,(B) -> R> {
public func <^><S, A, B, R>(f: @escaping (A, B) -> R, rhs: Parser<S, A>) -> Parser<S, (B) -> R> {
return Parser(result: curry(f)) <*> rhs
}

public func <*><S, A, B>(lhs: Parser<S, (A) -> B>, rhs: Parser<S,A>) -> Parser<S,B> {
public func <*><S, A, B>(lhs: Parser<S, (A) -> B>, rhs: Parser<S, A>) -> Parser<S, B> {
return lhs.followed(by: rhs, combine: { $0($1) })
}

public func <&><S, A, B>(lhs: Parser<S,A>, rhs: Parser<S,B>) -> Parser<S,(A,B)> {
/// Combines two parsers to a single parser matching the lhs followed by
/// the rhs, returning the values matched by each in a tuple.
///
/// - Parameters:
/// - lhs: The first matching parser
/// - rhs: The second matching parser
/// - Returns: A parser matching the lhs, then the rhs
public func <&><S, A, B>(lhs: Parser<S, A>, rhs: Parser<S, B>) -> Parser<S, (A, B)> {
return lhs.followed(by: rhs, combine: { ($0, $1) })
}

/// Returns a parser matching the lhs following the rhs, only returning the
/// value matched by the lhs parser in the case both match.
/// Returns a parser matching the lhs followed by the rhs, only returning
/// the value matched by the lhs parser in the case both match.
///
/// - Parameters:
/// - lhs: The first matching parser, the result of the expression
/// - rhs: The second matching parser
/// - Returns: The lhs parser in the case both lhs and rhs match
public func <*<S, A, B>(lhs: Parser<S,A>, rhs: Parser<S,B>) -> Parser<S,A> {
public func <*<S, A, B>(lhs: Parser<S, A>, rhs: Parser<S, B>) -> Parser<S, A> {
return lhs.followed(by: rhs, combine: { x, _ in x })
}

Expand All @@ -189,18 +198,25 @@ public func <*<S, A, B>(lhs: Parser<S,A>, rhs: Parser<S,B>) -> Parser<S,A> {
/// - lhs: The first matching parser
/// - rhs: The second matching parser, the result of the expression
/// - Returns: The rhs parser in the case both lhs and rhs match
public func *><S, A, B>(lhs: Parser<S,A>, rhs: Parser<S,B>) -> Parser<S,B> {
public func *><S, A, B>(lhs: Parser<S, A>, rhs: Parser<S, B>) -> Parser<S, B> {
return lhs.followed(by: rhs, combine: { _, x in x })
}

public func <|><S, A>(lhs: Parser<S,A>, rhs: Parser<S,A>) -> Parser<S,A> {
/// Returns a parser matching the lhs or the rhs, only returning the
/// first value matched.
///
/// - Parameters:
/// - lhs: The first matching parser
/// - rhs: The second matching parser
/// - Returns: A parser matching lhs or rhs, with precendence given to lhs
public func <|><S, A>(lhs: Parser<S, A>, rhs: Parser<S, A>) -> Parser<S, A> {
return lhs.or(rhs)
}

public func <<&<S, A, B, C>(lhs: Parser<S,(A, B)>, rhs: Parser<S,C>) -> Parser<S,(A, B, C)> {
return rhs.group(into:lhs)
public func <<&<S, A, B, C>(lhs: Parser<S, (A, B)>, rhs: Parser<S, C>) -> Parser<S, (A, B, C)> {
return rhs.group(into: lhs)
}

public func <<&<S, A, B, C, D>(lhs: Parser<S,(A, B, C)>, rhs: Parser<S,D>) -> Parser<S,(A, B, C, D)> {
return rhs.group(into:lhs)
public func <<&<S, A, B, C, D>(lhs: Parser<S, (A, B, C)>, rhs: Parser<S, D>) -> Parser<S, (A, B, C, D)> {
return rhs.group(into: lhs)
}
24 changes: 10 additions & 14 deletions Sources/FFCParserCombinator/StringParsing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public extension Parser where S == Substring {
*/
public func character( condition: @escaping (Character) -> Bool) -> Parser<Substring, Character> {
return Parser { stream in
guard let char :Character = stream.first, condition(char) else { return nil }
guard let char: Character = stream.first, condition(char) else { return nil }
return (char, stream.dropFirst())
}
}
Expand Down Expand Up @@ -95,10 +95,8 @@ extension String {
}
}

public var fullRange :Range<Index> {
get {
return Range(uncheckedBounds: (lower: startIndex, upper: endIndex))
}
public var fullRange: Range<Index> {
return Range(uncheckedBounds: (lower: startIndex, upper: endIndex))
}
}

Expand All @@ -114,21 +112,21 @@ public protocol ParsableType {
}

extension UInt: ParsableType {
public static var parser: Parser<Substring, UInt> { get {
public static var parser: Parser<Substring, UInt> {
return { UInt(String($0))! } <^> BasicParser.digit.many1
}
}
}

extension Int: ParsableType {
public static var parser: Parser<Substring, Int> { get {
return { characters in Int(String(characters))! } <^> BasicParser.negation.optional.followed(by:BasicParser.numericString, combine: { ($0 ?? "") + $1 } )
}
public static var parser: Parser<Substring, Int> {
return { characters in
Int(String(characters))!
} <^> BasicParser.negation.optional.followed(by: BasicParser.numericString, combine: { ($0 ?? "") + $1 } )
}
}

extension Double: ParsableType {
public static var parser: Parser<Substring, Double> { get {
public static var parser: Parser<Substring, Double> {

let m = { _ in FloatingPointSign.minus } <^> "-"
let p = { _ in FloatingPointSign.plus } <^> "+"
Expand All @@ -141,7 +139,7 @@ extension Double: ParsableType {

let decimalFraction = "." *> UInt.parser

let decimalExponent = { (s,m) in Int(m) * (s == .minus ? -1 : 1) } <^> floatingPointE *> sign <&> UInt.parser
let decimalExponent = { (s, m) in Int(m) * (s == .minus ? -1 : 1) } <^> floatingPointE *> sign <&> UInt.parser

let doubleParser = sign <&> decimalLiteral <<& decimalFraction.optional <<& decimalExponent.optional

Expand Down Expand Up @@ -180,8 +178,6 @@ extension Double: ParsableType {
// - fcanas
return Double("\(s)\(integerpart)\(frac)\(exp)")!
} <^> doubleParser

}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,6 @@ class FFCParserCombinatorTests: XCTestCase {
static var allTests = [
("testMultiMatching", testMultiMatching),
("testOr", testOr),
("testFlatMap", testFlatMap)
]
}
3 changes: 2 additions & 1 deletion Tests/FFCParserCombinatorTests/StringParsing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,13 @@ class StringParsingTests: XCTestCase {
}

static var allTests = [
("testFloatingPoint",testFloatingPoint),
("testFloatingPoint", testFloatingPoint),
("testSignedFloatingPoint", testSignedFloatingPoint),
("testInt", testInt),
("testUInt", testUInt),
("testDouble", testDouble),
("testNewlines", testNewlines),
("testComposition", testComposition)
]

}
1 change: 1 addition & 0 deletions Tests/LinuxMain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ import XCTest

XCTMain([
testCase(FFCParserCombinatorTests.allTests),
testCase(StringParsingTests.allTests)
])

0 comments on commit 9ca75aa

Please sign in to comment.