我正在编写一个将接收各种“命令”字符串的应用程序。我一直在看Scala组合器库来标记命令。我发现在很多情况下我想说:“这些令牌是无序的,因此它们可以按任何顺序出现,有些可能不会出现”。
根据我目前对语法的了解,我必须定义序列的所有组合(伪语法):
command = action~content
action = alphanum
content = (tokenA~tokenB~tokenC | tokenB~tokenC~tokenA | tokenC~tokenB~tokenA ....... )
所以我的问题是,考虑到tokenA-C是唯一的,是否有更短的方法来使用语法定义任何订单的集合?
答案 0 :(得分:4)
您可以使用“Parser。^?”运算符检查一组解析元素是否重复。
def tokens = tokenA | tokenB | tokenC
def uniqueTokens = (tokens*) ^? (
{ case t if (t == t.removeDuplicates) => t },
{ "duplicate tokens found: " + _ })
以下示例允许您以任何顺序输入四个傀儡中的任何一个,但如果遇到重复则无法解析:
package blevins.example
import scala.util.parsing.combinator._
case class Stooge(name: String)
object StoogesParser extends RegexParsers {
def moe = "Moe".r
def larry = "Larry".r
def curly = "Curly".r
def shemp = "Shemp".r
def stooge = ( moe | larry | curly | shemp ) ^^ { case s => Stooge(s) }
def certifiedStooge = stooge | """\w+""".r ^? (
{ case s: Stooge => s },
{ "not a stooge: " + _ })
def stooges = (certifiedStooge*) ^? (
{ case x if (x == x.removeDuplicates) => x.toSet },
{ "duplicate stooge in: " + _ })
def parse(s: String): String = {
parseAll(stooges, new scala.util.parsing.input.CharSequenceReader(s)) match {
case Success(r,_) => r.mkString(" ")
case Failure(r,_) => "failure: " + r
case Error(r,_) => "error: " + r
}
}
}
以及一些示例用法:
package blevins.example
object App extends Application {
def printParse(s: String): Unit = println(StoogesParser.parse(s))
printParse("Moe Shemp Larry")
printParse("Moe Shemp Shemp")
printParse("Curly Beyonce")
/* Output:
Stooge(Moe) Stooge(Shemp) Stooge(Larry)
failure: duplicate stooge in: List(Stooge(Moe), Stooge(Shemp), Stooge(Shemp))
failure: not a stooge: Beyonce
*/
}
答案 1 :(得分:3)
有很多方法可以解决它。例如,查看解析器here。它接受4个预定义的数字,这些数字可能出现在任何其他数字中,但必须出现一次,且只出现一次。
如果这种模式经常发生,你可以写一个组合器:def comb3[A](a: Parser[A], b: Parser[A], c: Parser[A]) =
a ~ b ~ c | a ~ c ~ b | b ~ a ~ c | b ~ c ~ a | c ~ a ~ b | c ~ b ~ a
答案 2 :(得分:1)
我不会尝试在语法上强制执行此要求。我写了一个允许从允许的集合中接受多个令牌的作品,然后使用非解析方法来确定实际给出的关键字的可接受性。除了允许更简单的语法之外,它还允许您在发出有关错误用法的诊断后更容易地继续解析。
兰德尔舒尔茨答案 3 :(得分:0)
如果您经常遇到这种情况,您当然可以编写一个组合规则来为您执行此操作。
另一方面,也许存在使“tokenA..C”只是“令牌”然后在“令牌”的处理程序内区分的选项
答案 4 :(得分:0)
我不知道你想支持哪种结构,但我知道你应该指定一个更具体的语法。从您的评论到另一个答案:
todo message:将Todo类链接到数据库
我猜你不想接受像
这样的东西todo消息:数据库Todo链接类
所以你可能想要定义一些消息级关键字,比如“link”和“to”......
def token = alphanum~':'~ "link" ~ alphanum ~ "class" ~ "to" ~ alphanum
^^ { (a:String,b:String,c:String) => /* a == "message", b="Todo", c="database" */ }
我想你必须在那个级别定义你的语法。