Parser combinator optional parts

1.9k Views Asked by At

Its my first time using scala's parser combinators. I have this situation where I have a list of 'types' and they can extend other types, or not. If they do I just create a map between the type and its parent type. If not I just map the type to 'object'. (Types are just string names)

They are written like this:

type1 type2 type3 - parentType

or

type0a type0b

in which case they will implicitly be - object

I tried to implement it in the following way, but its not compiling. Its saying that it requires an Option[~List[String, String] and it found ~[a,b]. Its also saying it cannot find the value names in the first for comprehension, when in fact its specified in the case pattern match. A bit confused now what is actually happening.

def type_list = ((name+) ~ ("-" ~> parent_type)?) ^^ {
  case names ~ parent_type => for (name <- names) yield name -> parent_type
  case names => for (name <- names) yield name -> "object"
}

 def name = """[a-zA-Z\d]+""".r

Actually I just want this to return a Map[String, String], if the parent_type is missing it should default to "object". How is it best to approach this?

1

There are 1 best solutions below

1
On BEST ANSWER

There's a couple of things going on here.

Firstly the precedence of ~ and ? means that instead of combining (name+) which is a Parser[List[String]] with ( "-" ~> parent_type )? which is a Parser[Option[String]] you're actually wrapping the whole thing in ? and so getting a Parser[Option[~[List[String],String]] if that makes sense.

The easiest solution is to use an extra set of brackets.

So you want to start with:

def type_list = ((name+) ~ (("-" ~> parent_type)?))

Then in the mapping function what'll you'll receive is a ~[List[String],Option[String]] with the list of names and an optional parent_type. In terms of pattern matching you always get name ~ parent_type where the latter is an Option[String]. So basically your second pattern is invalid.

So you could either do this:

def type_list = ((name+) ~ (("-" ~> parent_type)?)) ^^ {
   case names ~ Some(parent_type) => for (name <- names) yield name -> parent_type
   case names ~ None => for (name <- names) yield name -> "object"
}

Or you could simplify this to:

def type_list = ((name+) ~ (("-" ~> parent_type)?)) ^^ {
   case names ~ parent_type => for (name <- names) yield name -> parent_type.getOrElse("object)
}