Last active

Embed URL

HTTPS clone URL

SSH clone URL

You can clone with HTTPS or SSH.

Download Gist

Scala parser for programm args

View ArgsOps.scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
import scala.language.implicitConversions
 
/**
* @author gwenzek
*
*/
 
class ArgsOps(ops: Map[String, OptionalParam], val args: Array[String]){
def apply(op: String) = ops(op)
}
 
class OptionalParam(val name: String) {
def asInt() = this match { case IntParam(_, i) => i }
def asString() = this match { case StringParam(_, s) => s }
def asBoolean() = this match { case FlagParam(_, b) => b}
 
def length() = this match {
case IntParam(_, _) | StringParam(_, _) => 2
case FlagParam(_, _) => 1
}
 
def toPairs() = name.split("\\|").map( _ -> this )
}
 
sealed case class IntParam(override val name: String, val value: Int) extends OptionalParam(name)
sealed case class StringParam(override val name: String, val value: String) extends OptionalParam(name)
sealed case class FlagParam(override val name: String, val value: Boolean = false) extends OptionalParam(name)
 
object ArgsOps {
 
case class OptionWithoutExpectedParam(name: String) extends Exception
case class UnknownParam(name: String) extends Exception
 
class ArgsOpsParser(default: Map[String, OptionalParam]){
 
def parse(args: Array[String]) = {
 
def parseOne(i: Int, l: List[OptionalParam]): (Int, List[OptionalParam]) = {
if(i == args.length || args(i)(0) != '-') (i, l)
else{
val partialName = args(i)
val op = default.getOrElse(partialName, throw UnknownParam(partialName))
if(i + op.length > args.length) throw OptionWithoutExpectedParam(op.name)
val received : OptionalParam = op match {
case _ : IntParam => try { op.name -> args(i+1).toInt } catch
{case e: java.lang.NumberFormatException => throw OptionWithoutExpectedParam(op.name)}
case _ : StringParam =>
if(args(i+1)(0) == '-') throw OptionWithoutExpectedParam(op.name)
else op.name -> args(i+1)
case _ : FlagParam => FlagParam(op.name, true)
}
parseOne(i+op.length, received :: l)
}
}
val (i, l) = parseOne(0, Nil)
new ArgsOps(default ++ l.flatMap(_.toPairs).toMap, args.slice(i, args.length))
}
 
def <<|(args: Array[String]) = parse(args)
}
 
object ArgsOpsParser {
def apply(ops: OptionalParam*) = new ArgsOpsParser(ops.flatMap(_.toPairs).toMap)
}
 
implicit def asInt(op: OptionalParam) = op match { case IntParam(_, i) => i }
implicit def asString(op: OptionalParam) = op match { case StringParam(_, s) => s }
implicit def asBoolean(op: OptionalParam) = op match { case FlagParam(_, b) => b}
 
implicit def fromPairSS(kv: (String, String)): OptionalParam= StringParam(kv._1, kv._2)
implicit def fromPairSI(kv: (String, Int)): OptionalParam = IntParam(kv._1, kv._2)
implicit def fromString(k: String): OptionalParam = FlagParam(k)
}
View ArgsOps.scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
import org.scalatest.FunSuite
 
 
import ArgsOps._
 
val parser = ArgsOpsParser("--someInt|-i" -> 4, "--someFlag|-f", "--someWord|-w" -> "hello")
 
test("the ArgsOps parser rejects unknow options") {
intercept[UnknownParam](parser <<| Array("--unknown"))
}
 
test("the ArgsOps parser rejects options without args"){
intercept[OptionWithoutExpectedParam](parser <<| Array("--someInt","--someFlag"))
intercept[OptionWithoutExpectedParam](parser <<| Array("--someInt", "3x"))
intercept[OptionWithoutExpectedParam](parser <<| Array("--someInt"))
intercept[OptionWithoutExpectedParam](parser <<| Array("--someWord", "--someFlag"))
intercept[OptionWithoutExpectedParam](parser <<| Array("--someWord"))
}
test("the ArgsOps parser reads the correct values"){
val argsOps = parser <<| Array("--someInt", "3", "--someFlag", "--someWord", "goodbye")
assert(argsOps("--someInt").asInt == 3)
assert(argsOps("--someFlag").asBoolean == true)
assert(argsOps("--someWord").asString == "goodbye")
}
 
test("the ArgsOps parser accepts shorted name"){
val argsOps = parser <<| Array("-i", "3", "-f", "-w", "goodbye")
assert(argsOps("-i").asInt == 3)
assert(argsOps("-f").asBoolean == true)
assert(argsOps("-w").asString == "goodbye")
}
 
test("the ArgsOps parser is coherent between shorted and complet names"){
val argsOps = parser <<| Array("-i", "3", "-f", "-w", "goodbye")
assert(argsOps("--someInt") == argsOps("-i"))
assert(argsOps("--someFlag") == argsOps("-f"))
assert(argsOps("--someWord") == argsOps("-w"))
}
 
test("the ArgsOps parser keeps the correct default values"){
val argsOps = parser <<| Array("arg")
val someInt : Int = argsOps("--someInt")
val someFlag : Boolean = argsOps("--someFlag")
val someWord : String = argsOps("--someWord")
assert(someInt == 4)
assert(someFlag == false)
assert(someWord == "hello")
}
 
test("the ArgsOps parser keeps the correct number of arg"){
val argsOps = parser <<| Array("--someInt", "3", "--someFlag", "--someWord", "goodbye", "arg0", "arg1", "arg2")
assert(argsOps.args(0) == "arg0")
assert(argsOps.args(1) == "arg1")
assert(argsOps.args(2) == "arg2")
}
 
}
View ArgsOps.scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
import ***.ArgsOps._
 
 
object Example {
val parser = ArgsOpsParser("--someInt|-i" -> 4, "--someFlag|-f", "--someWord" -> "hello")
def main(args: Array[String]){
val argsOps = parser <<| args
val someInt : Int = argsOps("--someInt")
val someFlag : Boolean = argsOps("--someFlag")
val someWord : String = argsOps("--someWord")
val otherArgs = argsOps.args
foo(someWord, someInt, someFlag)
}
def foo(s: String, a: Int, insertLine: Boolean) = {
for(i <- 1 to a){
if(insertLine) println(s)
else print(s + ' ')
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.