@@ -64,9 +64,10 @@ trait SchemaParser extends RegexParsers {
6464 def versionDecl : Parser [String ] = (" version" ~> Schema .version <~ eol).withFailureMessage(s " Schema version declaration 'version ${Schema .version}' missing or incorrect " )
6565
6666 /**
67- * [4] GlobalDirectives ::= SeparatorDirective? QuotedDirective? TotalColumnsDirective? (NoHeaderDirective | IgnoreColumnNameCaseDirective)?
67+ * [4] GlobalDirectives ::= SeparatorDirective? QuotedDirective? TotalColumnsDirective? (NoHeaderDirective | IgnoreColumnNameCaseDirective)? /* expr: unordered */
6868 */
69- def globalDirectives : Parser [List [GlobalDirective ]] = rep(positioned((separatorDirective | quotedDirective | totalColumnsDirective | (noHeaderDirective | ignoreColumnNameCaseDirective)) <~ opt(eol)))
69+ def globalDirectives : Parser [List [GlobalDirective ]] = opt(mingle(List (separatorDirective, quotedDirective, totalColumnsDirective, noHeaderDirective | ignoreColumnNameCaseDirective).map(positioned(_) <~ opt(eol)))
70+ .withFailureMessage(" Invalid global directive" )) ^^ { _.getOrElse(List .empty) }
7071
7172 /**
7273 * [5] DirectivePrefix ::= "@"
@@ -96,17 +97,20 @@ trait SchemaParser extends RegexParsers {
9697 /**
9798 * [10] TotalColumnsDirective ::= DirectivePrefix "totalColumns" PositiveNonZeroIntegerLiteral
9899 */
100+ // def totalColumnsDirective: Parser[TotalColumns] = (directivePrefix ~> "totalColumns" ~> positiveNonZeroIntegerLiteral ^^ { TotalColumns(_) }).withFailureMessage("@totalColumns invalid")
99101 def totalColumnsDirective : Parser [TotalColumns ] = (directivePrefix ~> " totalColumns" ~> positiveNonZeroIntegerLiteral ^^ { TotalColumns (_) }).withFailureMessage(" @totalColumns invalid" )
100102
101103 /**
102104 * [11] NoHeaderDirective ::= DirectivePrefix "noHeader"
103105 */
106+ // def noHeaderDirective: Parser[NoHeader] = directivePrefix ~> "noHeader" ^^^ NoHeader()
104107 def noHeaderDirective : Parser [NoHeader ] = directivePrefix ~> " noHeader" ^^^ NoHeader ()
105108
106109 /**
107110 * [12] IgnoreColumnNameCaseDirective ::= DirectivePrefix "ignoreColumnNameCase"
108111 */
109112 def ignoreColumnNameCaseDirective : Parser [IgnoreColumnNameCase ] = directivePrefix ~> " ignoreColumnNameCase" ^^^ IgnoreColumnNameCase ()
113+ // def ignoreColumnNameCaseDirective: Parser[IgnoreColumnNameCase] = "ignoreColumnNameCase" ^^^ IgnoreColumnNameCase()
110114
111115 /**
112116 * [13] Body ::= BodyPart+
@@ -418,6 +422,58 @@ trait SchemaParser extends RegexParsers {
418422 val nonBreakingCharPattern = """ [^\r\n\f]"""
419423 // </editor-fold>
420424
425+ /**
426+ * Given 1 or more Parsers
427+ * this function produces
428+ * all permutations of
429+ * all combinations.
430+ *
431+ * Put more simply if you have a List
432+ * of Parsers, we create a Parser
433+ * that matches n of those parsers
434+ * in any order
435+ *
436+ * @param parsers A list of parsers to mingle
437+ * @return A parser that represents all permutations of
438+ * all combinations of the parsers
439+ */
440+ private def mingle [T , U ](parsers : List [Parser [T ]]): Parser [List [T ]] = {
441+
442+ /**
443+ * All permutations of all combinations
444+ * of a List
445+ */
446+ def mingle [T ](data : List [T ]): List [List [T ]] = {
447+ (for (i <- 1 to data.length) yield
448+ data.combinations(i).flatMap(_.permutations)
449+ ).toList.flatten
450+ }
451+
452+ /**
453+ * Combines n parsers together
454+ * in the same manner as p1 ~ p2 ~ ... pN
455+ */
456+ def combine [T ](parsers : List [Parser [T ]]): Parser [List [T ]] = {
457+ parsers.foldRight(success(List .empty[T ])) {
458+ case (p, acc) => for {
459+ pRes <- p
460+ accRes <- acc
461+ } yield pRes :: accRes
462+ }
463+ }
464+
465+ def longestFirst (l1 : List [_], l2 : List [_]) = l1.length > l2.length
466+
467+ val mingled = mingle[Parser [T ]](parsers)
468+ .sortWith(longestFirst)
469+ // we sort longest first here,
470+ // to make sure the parser that matches
471+ // the most input will always be put first
472+
473+ val alternates = mingled.map(combine(_))
474+ alternates.reduceLeft(_ | _)
475+ }
476+
421477
422478 def parseAndValidate (reader : Reader ): ValidationNel [FailMessage , Schema ] = {
423479
0 commit comments