Skip to content

Commit

Permalink
refactor argument parser implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
Rico Schiekel committed Nov 24, 2010
1 parent 979d01d commit 289ea2b
Showing 1 changed file with 56 additions and 37 deletions.
93 changes: 56 additions & 37 deletions core/src/main/scala/de/downgra/scarg/ArgumentParser.scala
Expand Up @@ -80,37 +80,36 @@ abstract class ArgumentParser[T](configFactory: ValueMap => T) extends ArgumentC
* @return a `ParseResult` * @return a `ParseResult`
*/ */
def parse(args: Seq[String]): ParseResult = { def parse(args: Seq[String]): ParseResult = {
val options = Map() ++ (optionArguments filter (_.valueName.isDefined) flatMap (o => o.names map ((_ -> o)))) val options = Map() ++ (optionArguments.filter(_.valueName.isDefined)
val flags = Map() ++ (optionArguments filter (_.valueName.isEmpty) flatMap (o => o.names map ((_ -> o)))) .flatMap (o => o.names map ((_ -> o))))
val flags = Map() ++ (optionArguments.filter(_.valueName.isEmpty)
.flatMap (o => o.names map ((_ -> o))))
val positionals = new MStack[PositionalArgument].pushAll(positionalArguments.reverse) val positionals = new MStack[PositionalArgument].pushAll(positionalArguments.reverse)


var repeatedPositionalsFound: Set[PositionalArgument] = Set() var repeatedPositionalsFound: Set[PositionalArgument] = Set()
var argumentsFound: Set[OptionArgument] = Set() var optionalsFound: Set[OptionArgument] = Set()
var errors: List[ParseError] = List() var errors: List[ParseError] = List()
var result: ValueMap = Map() var result: ValueMap = Map()


/** found new optional */
def newOptional(arg: Option[OptionArgument], value: String) = arg map { a =>
result += (a.key -> (value :: result.getOrElse(a.key, Nil)))
optionalsFound += a
}

@tailrec def _parse(args: Seq[String]): Unit = args.toList match { @tailrec def _parse(args: Seq[String]): Unit = args.toList match {
// ___ -f value // ___ -f value
case o :: v :: t if(options.contains(o) && v(0) != '-') => case o :: v :: t if(options.contains(o) && v(0) != '-') =>
options get(o) map { a => newOptional(options.get(o), v)
result += (a.key -> (v :: result.getOrElse(a.key, Nil)))
argumentsFound += a
}
_parse(t) _parse(t)
// ___ -f[:=]value // ___ -f[:=]value
case Delimiter(o, v) :: t if(options contains o) => case Delimiter(o, v) :: t if(options contains o) =>
options get(o) map { a => newOptional(options.get(o), v)
result += (a.key -> (v :: result.getOrElse(a.key, Nil)))
argumentsFound += a
}
_parse(t) _parse(t)
// ___ -f // ___ -f
case f :: t if(flags contains f) => case f :: t if(flags contains f) =>
flags get(f) map { a => // flags are booleans per default
// flags are booleans per default newOptional(flags.get(f), flagDefaults._1)
result += (a.key -> (flagDefaults._1 :: result.getOrElse(a.key, Nil)))
argumentsFound += a
}
_parse(t) _parse(t)
// ___ positionalParam // ___ positionalParam
case p :: t if(positionals.nonEmpty && p(0) != '-') => case p :: t if(positionals.nonEmpty && p(0) != '-') =>
Expand All @@ -130,30 +129,14 @@ abstract class ArgumentParser[T](configFactory: ValueMap => T) extends ArgumentC


_parse(args) _parse(args)


val notFoundOptions = optionArguments.toSet -- argumentsFound val missingOptionals = optionArguments.toSet -- optionalsFound

// need to set default for not given flags with value "false"
notFoundOptions filter (_.valueName.isEmpty) foreach { a =>
result += (a.key -> (flagDefaults._2 :: result.getOrElse(a.key, Nil)))
}

// set default values for all option arguments
notFoundOptions filter (o => o.default.isDefined && o.valueName.nonEmpty) foreach { a =>
result += (a.key -> (a.default.get :: result.getOrElse(a.key, Nil)))
}

// set default values for optional repeated positional values
val missingPositionals = positionals.toSet -- repeatedPositionalsFound // remove found repeated arguments val missingPositionals = positionals.toSet -- repeatedPositionalsFound // remove found repeated arguments
missingPositionals filter(p => p.optional && p.repeated) foreach { a =>
result += (a.key -> Nil)
}


// check if all necessary arguments are given result = (defValsOptionals(missingOptionals)_ andThen
errors = (notFoundOptions filter (o => o.default.isEmpty && o.valueName.isDefined) // only options which are not flags defValsPositionals(missingPositionals) _)(result)
).foldLeft(errors)((a,v) => MissingPositional(v.names(0)) :: a)


// check missing positional arguments without the optional or found repeated errors = (checkOptionals(missingOptionals)_ andThen
errors = (missingPositionals filter(p => p.optional == false)).foldLeft(errors)((a,v) => MissingPositional(v.name) :: a) checkPositionals(missingPositionals)_ )(errors)


if(errors.nonEmpty) { if(errors.nonEmpty) {
if(showErrors) { if(showErrors) {
Expand All @@ -163,4 +146,40 @@ abstract class ArgumentParser[T](configFactory: ValueMap => T) extends ArgumentC
Left(errors.reverse) Left(errors.reverse)
} else Right(configFactory(result mapValues (_.reverse))) } else Right(configFactory(result mapValues (_.reverse)))
} }

/** set default values for option arguments */
private def defValsOptionals(missingOptionals: Set[OptionArgument])(foundValues: ValueMap): ValueMap = {
def append(a: OptionArgument, v: String) = (a.key -> (v :: foundValues.getOrElse(a.key, Nil)))

// need to set default for not given flags with value "false"
val a = missingOptionals.filter(_.valueName.isEmpty)
.map( a => append(a, flagDefaults._2) )
.toMap

// set default values for all option arguments
val b = missingOptionals.filter(o => o.default.isDefined && o.valueName.nonEmpty)
.map( a => append(a, a.default.get) )
.toMap
foundValues ++ a ++ b
}

/** set default values for optional repeated positional values */
private def defValsPositionals(missingPositionals: Set[PositionalArgument])(foundValues: ValueMap): ValueMap = {
val mp = missingPositionals.filter(p => p.optional && p.repeated)
.map( a => (a.key -> Nil) )
.toMap
foundValues ++ mp
}

/** check if all necessary arguments are given */
private def checkOptionals(missingOptionals: Set[OptionArgument])(errors: List[ParseError]): List[ParseError] = {
missingOptionals.filter(o => o.default.isEmpty && o.valueName.isDefined) // only options which are not flags
.foldLeft(errors)((a,v) => MissingPositional(v.names(0)) :: a)
}

/** check missing positional arguments without the optional or found repeated */
private def checkPositionals(missingPositionals: Set[PositionalArgument])(errors: List[ParseError]): List[ParseError] = {
missingPositionals.filter(p => p.optional == false)
.foldLeft(errors)((a,v) => MissingPositional(v.name) :: a)
}
} }

0 comments on commit 289ea2b

Please sign in to comment.