Skip to content
This repository has been archived by the owner on Dec 5, 2019. It is now read-only.

Fixes comma separated siblings and parenthesized groups #12

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Cartfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
github "robrix/Madness" "significant-indentation"
github "robrix/Either" ~> 1.1
github "robrix/Box" ~> 1.0.1
github "robrix/Prelude" ~> 1.4
github "robrix/Madness" "master"
github "robrix/Either" ~> 1.2.2
github "robrix/Box" ~> 1.2.2
github "robrix/Prelude" ~> 1.5
4 changes: 2 additions & 2 deletions Cartfile.private
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
github "jspahrsummers/xcconfigs"
github "Quick/Quick" ~> 0.2.2
github "Quick/Nimble" ~> 0.2
github "Quick/Quick" ~> 0.3.1
github "Quick/Nimble" ~> 1.0.0
14 changes: 7 additions & 7 deletions Cartfile.resolved
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
github "robrix/Box" "1.0.1"
github "robrix/Either" "1.1"
github "robrix/Madness" "b5e2882e924b2377da83adcdb59592ba7fdab52a"
github "Quick/Nimble" "v0.2.0"
github "robrix/Prelude" "1.4"
github "Quick/Quick" "v0.2.2"
github "jspahrsummers/xcconfigs" "0.7.1"
github "robrix/Box" "1.2.2"
github "Quick/Nimble" "v1.0.0"
github "robrix/Prelude" "1.5.0"
github "Quick/Quick" "v0.3.1"
github "jspahrsummers/xcconfigs" "0.8.1"
github "robrix/Either" "1.2.2"
github "robrix/Madness" "fa440dd21e7341f687f748afd98ccb12574e1bd0"
2 changes: 1 addition & 1 deletion Carthage/Checkouts/Madness
Submodule Madness updated 55 files
+11 −2 .gitmodules
+1 −0 Cartfile
+1 −0 Cartfile.private
+4 −0 Cartfile.resolved
+1 −0 Carthage/Checkouts/Assertions
+1 −0 Carthage/Checkouts/Box
+1 −0 Carthage/Checkouts/Either
+1 −0 Carthage/Checkouts/Prelude
+8 −0 Documentation/Collections.playground/contents.xcplayground
+1 −0 Documentation/Collections.playground/section-1.swift
+13 −0 Documentation/Collections.playground/section-2.swift
+0 −0 Documentation/Collections.playground/timeline.xctimeline
+8 −0 Documentation/Colours.playground/contents.xcplayground
+5 −0 Documentation/Colours.playground/section-1.swift
+29 −0 Documentation/Colours.playground/section-2.swift
+6 −0 Documentation/Colours.playground/timeline.xctimeline
+8 −0 Documentation/Lambda Calculus.playground/contents.xcplayground
+4 −0 Documentation/Lambda Calculus.playground/section-1.swift
+32 −0 Documentation/Lambda Calculus.playground/section-2.swift
+6 −0 Documentation/Lambda Calculus.playground/timeline.xctimeline
+0 −7 Documentation/Madness.playground/contents.xcplayground
+0 −64 Documentation/Madness.playground/section-1.swift
+0 −12 Documentation/Madness.playground/timeline.xctimeline
+0 −2 Documentation/Significant Indentation.playground/section-2.swift
+0 −0 Documentation/Subset of Common Markdown.playground/contents.xcplayground
+2 −0 Documentation/Subset of Common Markdown.playground/section-1.swift
+67 −0 Documentation/Subset of Common Markdown.playground/section-2.swift
+11 −0 Documentation/Subset of Common Markdown.playground/timeline.xctimeline
+0 −1 External/Either
+356 −36 Madness.xcodeproj/project.pbxproj
+8 −8 Madness.xcodeproj/xcshareddata/xcschemes/Madness-Mac.xcscheme
+110 −0 Madness.xcodeproj/xcshareddata/xcschemes/Madness-iOS.xcscheme
+14 −5 Madness.xcworkspace/contents.xcworkspacedata
+97 −0 Madness/Alternation.swift
+36 −0 Madness/Concatenation.swift
+58 −0 Madness/Error.swift
+22 −0 Madness/Ignore.swift
+63 −0 Madness/Map.swift
+52 −196 Madness/Parser.swift
+19 −0 Madness/Reduction.swift
+83 −0 Madness/Repetition.swift
+0 −11 Madness/String.swift
+102 −0 MadnessTests/AlternationTests.swift
+0 −59 MadnessTests/Assertions.swift
+0 −69 MadnessTests/BindTests.swift
+27 −0 MadnessTests/CollectionTests.swift
+54 −0 MadnessTests/ConcatenationTests.swift
+33 −0 MadnessTests/ErrorTests.swift
+35 −0 MadnessTests/Fixtures.swift
+44 −0 MadnessTests/IgnoreTests.swift
+111 −0 MadnessTests/MapTests.swift
+25 −226 MadnessTests/ParserTests.swift
+35 −0 MadnessTests/ReductionTests.swift
+201 −0 MadnessTests/RepetitionTests.swift
+18 −0 README.md
2 changes: 1 addition & 1 deletion Carthage/Checkouts/Nimble
Submodule Nimble updated 88 files
+1 −0 .gitignore
+0 −9 .travis.yml
+18 −4 CONTRIBUTING.md
+8 −7 Nimble.podspec
+191 −49 Nimble.xcodeproj/project.pbxproj
+2 −2 Nimble.xcodeproj/project.xcworkspace/xcshareddata/Nimble.xccheckout
+2 −3 Nimble/Adapters/AdapterProtocols.swift
+20 −0 Nimble/Adapters/AssertionDispatcher.swift
+54 −6 Nimble/Adapters/AssertionRecorder.swift
+28 −0 Nimble/Adapters/NimbleXCTestHandler.swift
+0 −10 Nimble/Adapters/XCTestHandler.swift
+43 −0 Nimble/DSL+Wait.swift
+8 −37 Nimble/DSL.swift
+31 −18 Nimble/Expectation.swift
+51 −5 Nimble/Expression.swift
+32 −7 Nimble/FailureMessage.swift
+88 −0 Nimble/Matchers/AllPass.swift
+11 −1 Nimble/Matchers/BeAKindOf.swift
+11 −1 Nimble/Matchers/BeAnInstanceOf.swift
+52 −4 Nimble/Matchers/BeCloseTo.swift
+16 −4 Nimble/Matchers/BeEmpty.swift
+2 −2 Nimble/Matchers/BeGreaterThan.swift
+2 −2 Nimble/Matchers/BeGreaterThanOrEqualTo.swift
+1 −1 Nimble/Matchers/BeIdenticalTo.swift
+2 −2 Nimble/Matchers/BeLessThan.swift
+1 −1 Nimble/Matchers/BeLessThanOrEqual.swift
+38 −21 Nimble/Matchers/BeLogical.swift
+1 −1 Nimble/Matchers/BeNil.swift
+2 −2 Nimble/Matchers/BeginWith.swift
+16 −2 Nimble/Matchers/Contain.swift
+5 −4 Nimble/Matchers/EndWith.swift
+63 −1 Nimble/Matchers/Equal.swift
+2 −2 Nimble/Matchers/Match.swift
+4 −35 Nimble/Matchers/MatcherProtocols.swift
+137 −54 Nimble/Matchers/RaisesException.swift
+79 −0 Nimble/ObjCExpectation.swift
+4 −4 Nimble/Utils/Stringers.swift
+48 −55 Nimble/Wrappers/AsyncMatcherWrapper.swift
+0 −28 Nimble/Wrappers/BasicMatcherWrapper.swift
+0 −18 Nimble/Wrappers/FullMatcherWrapper.swift
+56 −16 Nimble/Wrappers/MatcherFunc.swift
+0 −56 Nimble/Wrappers/NonNilMatcherWrapper.swift
+17 −93 Nimble/Wrappers/ObjCMatcher.swift
+23 −2 Nimble/objc/DSL.h
+27 −0 Nimble/objc/DSL.m
+1 −1 Nimble/objc/NMBExceptionCapture.h
+6 −7 NimbleTests/AsynchronousTest.swift
+44 −21 NimbleTests/Helpers/utils.swift
+74 −0 NimbleTests/Matchers/AllPassTest.swift
+18 −0 NimbleTests/Matchers/BeAKindOfTest.swift
+20 −0 NimbleTests/Matchers/BeAnInstanceOfTest.swift
+45 −0 NimbleTests/Matchers/BeCloseToTest.swift
+1 −1 NimbleTests/Matchers/BeEmptyTest.swift
+9 −9 NimbleTests/Matchers/BeIdenticalToObjectTest.swift
+2 −2 NimbleTests/Matchers/BeIdenticalToTest.swift
+22 −0 NimbleTests/Matchers/BeLogicalTest.swift
+1 −1 NimbleTests/Matchers/BeNilTest.swift
+2 −2 NimbleTests/Matchers/BeginWithTest.swift
+2 −2 NimbleTests/Matchers/EndWithTest.swift
+31 −0 NimbleTests/Matchers/EqualTest.swift
+131 −27 NimbleTests/Matchers/RaisesExceptionTest.swift
+0 −376 NimbleTests/objc/CompatibilityTest.m
+15 −0 NimbleTests/objc/NimbleSpecHelper.h
+38 −0 NimbleTests/objc/ObjCAllPassTest.m
+53 −0 NimbleTests/objc/ObjCAsyncTest.m
+34 −0 NimbleTests/objc/ObjCBeAnInstanceOfTest.m
+36 −0 NimbleTests/objc/ObjCBeCloseToTest.m
+80 −0 NimbleTests/objc/ObjCBeEmptyTest.m
+24 −0 NimbleTests/objc/ObjCBeFalseTest.m
+28 −0 NimbleTests/objc/ObjCBeFalsyTest.m
+33 −0 NimbleTests/objc/ObjCBeGreaterThanOrEqualToTest.m
+33 −0 NimbleTests/objc/ObjCBeGreaterThanTest.m
+36 −0 NimbleTests/objc/ObjCBeIdenticalToTest.m
+34 −0 NimbleTests/objc/ObjCBeKindOfTest.m
+33 −0 NimbleTests/objc/ObjCBeLessThanOrEqualToTest.m
+33 −0 NimbleTests/objc/ObjCBeLessThanTest.m
+24 −0 NimbleTests/objc/ObjCBeNilTest.m
+25 −0 NimbleTests/objc/ObjCBeTrueTest.m
+28 −0 NimbleTests/objc/ObjCBeTruthyTest.m
+37 −0 NimbleTests/objc/ObjCBeginWithTest.m
+50 −0 NimbleTests/objc/ObjCContainTest.m
+37 −0 NimbleTests/objc/ObjCEndWithTest.m
+35 −0 NimbleTests/objc/ObjCEqualTest.m
+33 −0 NimbleTests/objc/ObjCMatchTest.m
+176 −0 NimbleTests/objc/ObjCRaiseExceptionTest.m
+158 −56 README.md
+37 −0 circle.yml
+5 −4 test
2 changes: 1 addition & 1 deletion Carthage/Checkouts/Quick
Submodule Quick updated 37 files
+0 −5 .travis.yml
+4 −0 CONTRIBUTING.md
+220 −0 Documentation/ArrangeActAssert.md
+87 −0 Documentation/BehavioralTesting.md
+103 −0 Documentation/ConfiguringQuick.md
+28 −0 Documentation/InstallingFileTemplates.md
+141 −0 Documentation/InstallingQuick.md
+28 −0 Documentation/MoreResources.md
+106 −0 Documentation/NimbleAssertions.md
+462 −0 Documentation/QuickExamplesAndGroups.md
+53 −0 Documentation/QuickInObjectiveC.md
+42 −0 Documentation/README.md
+73 −0 Documentation/SettingUpYourXcodeProject.md
+125 −0 Documentation/SharedExamples.md
+179 −0 Documentation/TestingApps.md
+1 −1 Externals/Nimble
+4 −3 Quick.podspec
+44 −70 Quick.xcodeproj/project.pbxproj
+0 −1,348 Quick.xcodeproj/project.pbxproj.orig
+17 −17 Quick/DSL/DSL.swift
+1 −1 Quick/DSL/QCKDSL.h
+3 −3 Quick/DSL/QCKDSL.m
+9 −9 Quick/DSL/World+DSL.swift
+0 −25 QuickTests/ExampleMetadataFunctionalTests.swift
+1 −1 QuickTests/Fixtures/FunctionalTests_SharedExamplesTests_SharedExamples.swift
+0 −27 QuickTests/Fixtures/Person.swift
+0 −11 QuickTests/Fixtures/Poet.swift
+0 −65 QuickTests/FunctionalTests+ObjC.m
+0 −156 QuickTests/FunctionalTests.swift
+42 −0 QuickTests/FunctionalTests/AfterSuiteTests+ObjC.m
+1 −1 QuickTests/FunctionalTests/AfterSuiteTests.swift
+0 −0 QuickTests/FunctionalTests/BeforeSuiteTests+ObjC.m
+1 −1 QuickTests/FunctionalTests/BeforeSuiteTests.swift
+59 −0 QuickTests/FunctionalTests/SharedExamples+BeforeEachTests+ObjC.m
+36 −0 QuickTests/FunctionalTests/SharedExamplesTests+ObjC.m
+0 −21 QuickTests/FunctionalTests/WorldExampleMetadataFunctionalTests.swift
+9 −944 README.md
2 changes: 1 addition & 1 deletion Carthage/Checkouts/xcconfigs
112 changes: 55 additions & 57 deletions OGDL/Parser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,27 @@ import Madness
import Prelude

/// Returns a parser which parses one character from the given set.
internal prefix func % (characterSet: NSCharacterSet) -> Parser<String>.Function {
return { string in
let scalars = string.unicodeScalars

if let scalar = first(scalars) {
if characterSet.longCharacterIsMember(scalar.value) {
return (String(scalar), String(dropFirst(scalars)))
}
}

return nil
internal prefix func % <C: CollectionType where C.Generator.Element == Character>(characterSet: NSCharacterSet) -> Parser<C, String>.Function {
return { collection, index in
if index != collection.endIndex {
if let scalar = first(String(collection[index]).unicodeScalars) where characterSet.longCharacterIsMember(scalar.value) {
return .right(String(scalar), index.successor())
}
}
return .left(.leaf("Character is not present in NSCharacterSet", index))
}
}

/// Removes the characters in the given string from the character set.
internal func - (characterSet: NSCharacterSet, characters: String) -> NSCharacterSet {
let mutableSet = characterSet.mutableCopy() as NSMutableCharacterSet
let mutableSet = characterSet.mutableCopy() as! NSMutableCharacterSet
mutableSet.removeCharactersInString(characters)
return mutableSet
}

/// Removes characters in the latter set from the former.
internal func - (characterSet: NSCharacterSet, subtrahend: NSCharacterSet) -> NSCharacterSet {
let mutableSet = characterSet.mutableCopy() as NSMutableCharacterSet
let mutableSet = characterSet.mutableCopy() as! NSMutableCharacterSet
mutableSet.formIntersectionWithCharacterSet(subtrahend.invertedSet)
return mutableSet
}
Expand All @@ -44,8 +41,8 @@ internal func - (characterSet: NSCharacterSet, subtrahend: NSCharacterSet) -> NS
postfix operator |? {}

/// Matches zero or one occurrence of the given parser.
internal postfix func |? <T>(parser: Parser<T>.Function) -> Parser<T?>.Function {
return (parser * (0..<2)) --> first
internal postfix func |? <C: CollectionType, T where C.Generator.Element: Equatable>(parser: Parser<C, T>.Function) -> Parser<C, T?>.Function {
return first <^> (parser * (0..<2))
}

private let char_control = NSCharacterSet.controlCharacterSet()
Expand All @@ -57,43 +54,46 @@ private let char_break = NSCharacterSet.newlineCharacterSet()
// TODO: Use this somewhere.
private let char_end = char_control - NSCharacterSet.whitespaceAndNewlineCharacterSet()

private let wordStart: Parser<String>.Function = %(char_word - "#'\"")
private let wordChars: Parser<String>.Function = (%(char_word - "'\""))* --> { strings in join("", strings) }
private let word: Parser<String>.Function = wordStart ++ wordChars --> (+)
private let br: Parser<()>.Function = ignore(%char_break)
private let eof: Parser<()>.Function = { $0 == "" ? ((), "") : nil }
private let comment: Parser<()>.Function = ignore(%"#" ++ (%char_text)+ ++ (br | eof))
private let wordStart: Parser<String, String>.Function = %(char_word - "#'\"")
private let wordChars: Parser<String, String>.Function = { join("", $0) } <^> (%(char_word - "'\""))*
private let word: Parser<String, String>.Function = (+) <^> wordStart ++ wordChars
private let br: Parser<String, Ignore>.Function = ignore(%char_break)
private let eof: Parser<String, Ignore>.Function = ignore("")//{ $0 == "" ? ((), "") : nil }

private let br_eof = br | eof
private let comment: Parser<String, Ignore>.Function = ignore(%"#" ++ (%char_text)+ ++ br_eof)

// TODO: Escape sequences.
private let singleQuotedChars: Parser<String>.Function = (%(char_text - "'"))* --> { strings in join("", strings) }
private let singleQuoted: Parser<String>.Function = ignore(%"'") ++ singleQuotedChars ++ ignore(%"'")
private let doubleQuotedChars: Parser<String>.Function = (%(char_text - "\""))* --> { strings in join("", strings) }
private let doubleQuoted: Parser<String>.Function = ignore(%"\"") ++ doubleQuotedChars ++ ignore(%"\"")
private let quoted: Parser<String>.Function = singleQuoted | doubleQuoted
private let requiredSpace: Parser<()>.Function = ignore((comment | %char_space)+)
private let optionalSpace: Parser<()>.Function = ignore((comment | %char_space)*)
private let separator: Parser<()>.Function = ignore(optionalSpace ++ %"," ++ optionalSpace)
private let singleQuotedChars: Parser<String, String>.Function = { join("", $0) } <^> (%(char_text - "'"))*
private let singleQuoted: Parser<String, String>.Function = ignore(%"'") ++ singleQuotedChars ++ ignore(%"'")
private let doubleQuotedChars: Parser<String, String>.Function = { join("", $0) } <^> (%(char_text - "\""))*
private let doubleQuoted: Parser<String, String>.Function = ignore(%"\"") ++ doubleQuotedChars ++ ignore(%"\"")
private let quoted: Parser<String, String>.Function = singleQuoted | doubleQuoted
private let requiredSpace: Parser<String, Ignore>.Function = ignore((comment | %char_space)+)
private let optionalSpace: Parser<String, Ignore>.Function = ignore((comment | %char_space)*)
private let separator: Parser<String, Ignore>.Function = ignore(optionalSpace ++ %"," ++ optionalSpace)

private let value: Parser<String>.Function = word | quoted
private let value: Parser<String, String>.Function = word | quoted

/// A function taking an Int and returning a parser which parses at least that many
/// indentation characters.
func indentation(n: Int) -> Parser<Int>.Function {
return (%char_space * (n..<Int.max)) --> { $0.count }
func indentation(n: Int) -> Parser<String, Int>.Function {
return count <^> (%char_space * (n..<Int.max))
}

// MARK: Generic combinators
// FIXME: move these into Madness.

/// Delays the evaluation of a parser so that it can be used in a recursive grammar without deadlocking Swift at runtime.
private func lazy<T>(parser: () -> Parser<T>.Function) -> Parser<T>.Function {
private func lazy<C: CollectionType, T where C.Generator.Element: Equatable>(parser: () -> Parser<C, T>.Function) -> Parser<C, T>.Function {
return { parser()($0) }
}

/// Returns a parser which produces an array of parse trees produced by `parser` interleaved with ignored parses of `separator`.
///
/// This is convenient for e.g. comma-separated lists.
private func interleave<T, U>(separator: Parser<U>.Function, parser: Parser<T>.Function) -> Parser<[T]>.Function {
return (parser ++ (ignore(separator) ++ parser)*) --> { [$0] + $1 }
private func interleave<C: CollectionType, T, U where C.Generator.Element: Equatable>(separator: Parser<C, U>.Function, parser: Parser<C, T>.Function) -> Parser<C, [T]>.Function {
return { [$0] + $1 } <^> (parser ++ (ignore(separator) ++ parser)*)
}

private func foldr<S: SequenceType, Result>(sequence: S, initial: Result, combine: (S.Generator.Element, Result) -> Result) -> Result {
Expand All @@ -105,12 +105,12 @@ private func foldr<G: GeneratorType, Result>(inout generator: G, initial: Result
return generator.next().map { combine($0, foldr(&generator, initial, combine)) } ?? initial
}

private func | <T, U> (left: Parser<T>.Function, right: String -> U) -> Parser<Either<T, U>>.Function {
return left | { (right($0), $0) }
private func | <C: CollectionType, T, U where C.Generator.Element: Equatable>(left: Parser<C, T>.Function, right: () -> U) -> Parser<C, Either<T, U>>.Function {
return left | { .right(right(), $1 == $0.endIndex ? $1 : $1.successor()) }
}

private func | <T> (left: Parser<T>.Function, right: String -> T) -> Parser<T>.Function {
return left | { (right($0), $0) }
private func | <C: CollectionType, T where C.Generator.Element: Equatable>(left: Parser<C, T>.Function, right: () -> T) -> Parser<C, T>.Function {
return left | { .right(right(), $1 == $0.endIndex ? $1 : $1.successor()) }
}

private func flatMap<T, U>(x: [T], f: T -> [U]) -> [U] {
Expand All @@ -119,45 +119,43 @@ private func flatMap<T, U>(x: [T], f: T -> [U]) -> [U] {

// MARK: OGDL

private let children: Parser<[Node]>.Function = lazy { group | (element --> { elem in [ elem ] }) }

private let element = lazy { value ++ (optionalSpace ++ children)|? --> { value, children in Node(value: value, children: children ?? []) } }

// TODO: See Carthage/ogdl-swift#3.
private let block: Int -> Parser<()>.Function = { n in const(nil) }
private let block: Int -> Parser<String, Ignore>.Function = { _ in ignore(any) }

/// Parses a single descendent element.
///
/// This is an element which may be an in-line descendent, and which may further have in-line descendents of its own.
private let descendent = value --> { Node(value: $0) }
private let descendent = { Node(value: $0) } <^> value

/// Parses a sequence of hierarchically descending elements, e.g.:
///
/// x y z # => Node(x, [Node(y, Node(z))])
public let descendents: Parser<Node>.Function = interleave(requiredSpace, descendent) --> {
foldr(dropLast($0), last($0)!) { $0.nodeByAppendingChildren([ $1 ]) }
}
public let descendents: Parser<String, Node>.Function =
{ foldr(dropLast($0), last($0)!) { $0.nodeByAppendingChildren([ $1 ]) } }
<^> interleave(requiredSpace, descendent)

private let children: Parser<String, [Node]>.Function = lazy { group | ({ [$0] } <^> descendentChain) }

/// Parses a chain of descendents, optionally ending in a group.
///
/// x y (u, v) # => Node(x, [ Node(y, [ Node(u), Node(v) ]) ])
private let descendentChain: Parser<Node>.Function = (descendents ++ ((optionalSpace ++ group) | const([]))) --> uncurry(Node.nodeByAppendingChildren)
private let descendentChain = lazy { { Node(value: $0, children: $1 ?? []) } <^> value ++ (optionalSpace ++ children)|? }

/// Parses a sequence of adjacent sibling elements, e.g.:
///
/// x, y z, w (u, v) # => [ Node(x), Node(y, Node(z)), Node(w, [ Node(u), Node(v) ]) ]
public let adjacent: Parser<[Node]>.Function = lazy { interleave(separator, descendentChain) }
public let adjacent: Parser<String, [Node]>.Function = lazy { interleave(separator, descendentChain) }

/// Parses a parenthesized sequence of sibling elements, e.g.:
///
/// (x, y z, w) # => [ Node(x), Node(y, Node(z)), Node(w) ]
private let group = lazy { ignore(%"(") ++ optionalSpace ++ adjacent ++ optionalSpace ++ ignore(%")") }

private let subgraph: Int -> Parser<[Node]>.Function = { n in
(descendents ++ lines(n + 1) --> { [ $0.nodeByAppendingChildren($1) ] }) | adjacent
private let subgraph: Int -> Parser<String, [Node]>.Function = { n in
adjacent | ({ [ $0.nodeByAppendingChildren($1) ] } <^> descendents ++ lines(n + 1))
}

private let line: Int -> Parser<[Node]>.Function = fix { line in
private let line: Int -> Parser<String, [Node]>.Function = fix { line in
{ n in
// TODO: block parsing: ignore(%char_space+ ++ block(n))|?) ++
// See Carthage/ogdl-swift#3.
Expand All @@ -167,14 +165,14 @@ private let line: Int -> Parser<[Node]>.Function = fix { line in
}
}

private let followingLine: Int -> Parser<[Node]>.Function = { n in (ignore(comment | br)+ ++ line(n)) }
private let lines: Int -> Parser<[Node]>.Function = { n in
(line(n)|? ++ followingLine(n)*) --> { ($0 ?? []) + flatMap($1, id) }
private let followingLine: Int -> Parser<String, [Node]>.Function = { n in (ignore(comment | br)+ ++ line(n)) }
private let lines: Int -> Parser<String, [Node]>.Function = { n in
{ ($0 ?? []) + flatMap($1, id) } <^> (line(n)|? ++ followingLine(n)*)
}

/// Parses a textual OGDL graph into a list of nodes (and their descendants).
///
/// Example:
///
/// let nodes = parse(graph, "foo (bar, buzz baz)")
public let graph: Parser<[Node]>.Function = ignore(comment | br)* ++ (lines(0) | adjacent) ++ ignore(comment | br)*
public let graph: Parser<String, [Node]>.Function = ignore(comment | br)* ++ (lines(0) | adjacent) ++ ignore(comment | br)*
36 changes: 17 additions & 19 deletions OGDLTests/ParserSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,34 +15,34 @@ import Quick
class ParserSpec: QuickSpec {
override func spec() {
it("should parse the empty string") {
expect(parse(graph, "")).to(equal([]))
expect(parse(graph, "").right).to(equal([]))
}

it("should parse a line break") {
expect(parse(graph, "\n")).to(equal([]))
expect(parse(graph, "\n").right).to(equal([]))
}

it("should parse a series of line breaks") {
expect(parse(graph, "\n\n\n")).to(equal([]))
expect(parse(graph, "\n\n\n").right).to(equal([]))
}

it("should parse a single node with descendents") {
expect(parse(descendents, "foobar")).to(equal(Node(value: "foobar")))
expect(parse(descendents, "foobar").right).to(equal(Node(value: "foobar")))
}

it("should parse a single node with adjacent") {
expect(parse(adjacent, "foobar")).to(equal([ Node(value: "foobar") ]))
expect(parse(adjacent, "foobar").right).to(equal([ Node(value: "foobar") ]))
}

it("should parse a single node") {
let expectedGraph = [ Node(value: "foobar") ]
let parsedGraph = parse(graph, "foobar")
let parsedGraph = parse(graph, "foobar").right
expect(parsedGraph).to(equal(expectedGraph))
}

it("should parse a single node ending with a newline") {
let expectedGraph = [ Node(value: "foobar") ]
let parsedGraph = parse(graph, "foobar\n")
let parsedGraph = parse(graph, "foobar\n").right
expect(parsedGraph).to(equal(expectedGraph))
}

Expand All @@ -57,7 +57,7 @@ class ParserSpec: QuickSpec {
])
]

let parsedGraph = parse(graph, "foo bar fuzz buzz")
let parsedGraph = parse(graph, "foo bar fuzz buzz").right
expect(parsedGraph).to(equal(expectedGraph))
}

Expand All @@ -70,12 +70,11 @@ class ParserSpec: QuickSpec {
])
]

let parsedGraph = parse(graph, "foo \"bar\" \"fuzz buzz\"")
let parsedGraph = parse(graph, "foo \"bar\" \"fuzz buzz\"").right
expect(parsedGraph).to(equal(expectedGraph))
}

// TODO: Not yet supported. See Carthage/ogdl-swift#6.
pending("should parse siblings") {
it("should parse siblings") {
let expectedGraph = [
Node(value: "foo", children: [
Node(value: "bar")
Expand All @@ -86,20 +85,19 @@ class ParserSpec: QuickSpec {
])
]

let parsedGraph = parse(graph, "foo bar, fuzz buzz")
let parsedGraph = parse(graph, "foo bar, fuzz buzz").right
expect(parsedGraph).to(equal(expectedGraph))
}

// TODO: Not yet supported. See Carthage/ogdl-swift#7.
pending("should parse grouped siblings") {
it("should parse grouped siblings") {
let expectedGraph = [
Node(value: "foo", children: [
Node(value: "bar"),
Node(value: "quux"),
])
]

let parsedGraph = parse(graph, "foo (bar, quux)")
let parsedGraph = parse(graph, "foo (bar, quux)").right
expect(parsedGraph).to(equal(expectedGraph))
}

Expand All @@ -112,12 +110,12 @@ class ParserSpec: QuickSpec {
])
]

let parsedGraph = parse(graph, "foo ( bar.o 1.2, quux.o 2.1 )")
let parsedGraph = parse(graph, "foo ( bar.o 1.2, quux.o 2.1 )").right
expect(parsedGraph).to(equal(expectedGraph))
}

it("should parse comments") {
let parsedGraph = parse(graph, "#foo")
let parsedGraph = parse(graph, "#foo").right
expect(parsedGraph).to(equal([]))
}

Expand All @@ -142,10 +140,10 @@ class ParserSpec: QuickSpec {
for i in 1...5 {
let URL = NSBundle(forClass: self.dynamicType).URLForResource("Example2-\(i)", withExtension: "ogdl", subdirectory: "Samples")!

let sample = NSString(contentsOfURL: URL, encoding: NSUTF8StringEncoding, error: nil)
let sample = NSString(contentsOfURL: URL, encoding: NSUTF8StringEncoding, error: nil) as? String
expect(sample).notTo(beNil())

let parsedGraph = parse(graph, sample ?? "")
let parsedGraph = parse(graph, sample ?? "").right
if let parsedGraph = parsedGraph {
println("graph \(i):\n\(parsedGraph)\n")
}
Expand Down