val uninterestingthings = ".".r
val parser = "(?ui)(regexvalue)".r | (uninterestingthings~>parser)
Run Code Online (Sandbox Code Playgroud)
这个递归解析器将尝试解析"(?ui)(regexvalue)".r直到输入结束.scala是一种在"不感兴趣"消耗某些定义数量的字符时禁止解析的方法吗?
UPD:我有一个糟糕的解决方案:
object NonRecursiveParser extends RegexParsers with PackratParsers{
var max = -1
val maxInput2Consume = 25
def uninteresting:Regex ={
if(max<maxInput2Consume){
max+=1
("."+"{0,"+max.toString+"}").r
}else{
throw new Exception("I am tired")
}
}
lazy val value = "itt".r
def parser:Parser[Any] = (uninteresting~>value)|parser
def parseQuery(input:String) = {
try{
parse(parser, input)
}catch{
case e:Exception =>
}
}
}
Run Code Online (Sandbox Code Playgroud)
缺点:
- 并非所有成员都是懒惰的,所以PackratParser会有一些时间惩罚
- 在每个"无趣的"方法调用上构建正则表达式- 时间惩罚
- 使用异常来控制程序 - 代码风格和时间惩罚
我需要用文本解析占位符abc $$FOO$$ cba.我和Scala的解析器组合器一起攻击了一些东西,但我对解决方案并不满意.
特别是,我在正则表达式中使用零宽度匹配器(?=(\$\$|\z))来停止解析文本并开始解析占位符.这听起来非常接近于scala邮件列表中讨论过的那些恶作剧,并且被多彩地解散了(这激发了这个问题的标题.)
所以,挑战:修复我的解析器没有这个黑客的工作.我希望看到从问题到解决方案的明确进展,因此我可以替换我的随机组装组合器的策略,直到测试通过.
import scala.util.parsing.combinator.RegexParsers
object PlaceholderParser extends RegexParsers {
sealed abstract class Element
case class Text(text: String) extends Element
case class Placeholder(key: String) extends Element
override def skipWhitespace = false
def parseElements(text: String): List[Element] = parseAll(elements, text) match {
case Success(es, _) => es
case NoSuccess(msg, _) => error("Could not parse: [%s]. Error: %s".format(text, msg))
}
def parseElementsOpt(text: String): ParseResult[List[Element]] = parseAll(elements, text)
lazy val elements: Parser[List[Element]] = rep(element)
lazy val …Run Code Online (Sandbox Code Playgroud) 我目前正在Scala中实现一个小编译器,当我在进行上下文分析的组件时,我发现了Binders包中的特征scala.util.parsing.ast(我正在使用Scala 2.9 RC),它是Documented以在解析时启用名称绑定.这听起来很有趣,我一直在谷歌搜索,但我仍然不知道如何使用它.虽然我当然能够让我的抽象语法脱离,但Binders我不知道如何从那里开始.网上有什么用法的例子吗?
compiler-construction binding functional-programming scala parser-combinators
考虑这样的解析器示例:
object TestParser extends RegexParsers {
override protected val whiteSpace = """[ \t]*""".r
def eol = """(\r?\n)+""".r
def item = "[a-zA-Z][a-zA-Z0-9-]*".r
def list = "items:" ~> rep1sep(item,",")
def constraints = "exclude:" ~> item
def itemsDefinition = (rep1sep(list, eol) ~ repsep(constraints,eol))
}
Run Code Online (Sandbox Code Playgroud)
如果我尝试解析此输入(没有两行包含排除工作正常):
items: item1, item2, item3, item3, item4
items: item2, item3, item3, item5, item4
items: item4, item5, item6, item10
items: item1, item2, item3
exclude: item1
exclude: item2
Run Code Online (Sandbox Code Playgroud)
我收到以下错误:
[5.5] failure: `items:' expected but `e' found
exclude: item1
^
Run Code Online (Sandbox Code Playgroud)
问题很明显这一行:
def …Run Code Online (Sandbox Code Playgroud) 是否可以使用Scala解析器组合器反转匹配?我试图将行与不以一组关键字开头的解析器匹配.我可以用一个恼人的零宽度负先行的正则表达式(例如,做到这一点"(?!h1|h2).*"),但我宁愿用Scala的解析器做到这一点.我能想出的最好的是:
def keyword = "h1." | "h2."
def alwaysfails = "(?=a)b".r
def linenotstartingwithkeyword = keyword ~! alwaysfails | ".*".r
Run Code Online (Sandbox Code Playgroud)
这个想法在这里我用〜!禁止回溯到所有匹配的正则表达式,然后用正则表达式继续"(?= A)B" .R匹配什么.(顺便说一下,有一个预定义的解析器,总是失败?)这样,如果一个关键字被发现,但如果关键字不匹配将匹配行不会匹配.
我想知道是否有更好的方法来做到这一点.在那儿?
让我们看看代码片段:
pSegmentBegin p i = pIndentExact i *> ((:) <$> p i <*> ((pEOL *> pSegment p i) <|> pure []))
Run Code Online (Sandbox Code Playgroud)
如果我在我的解析器中将此代码更改为:
pSegmentBegin p i = do
pIndentExact i
((:) <$> p i <*> ((pEOL *> pSegment p i) <|> pure []))
Run Code Online (Sandbox Code Playgroud)
我有一个错误:
canot compute minmal length of a parser due to occurrence of a moadic bind, use addLength to override
Run Code Online (Sandbox Code Playgroud)
我认为上面的解析器应该以相同的方式运行.为什么会出现此错误?
编辑
上面的例子很简单(为了简化问题),如下所述,这里没有必要使用do notation,但我希望它使用的实际情况如下:
pSegmentBegin p i = do
j <- pIndentAtLast i
(:) <$> p j <*> ((pEOL …Run Code Online (Sandbox Code Playgroud) 我有一个工作的解析器,但我刚刚意识到我不满足于评论.在我解析的DSL中,注释以;字符开头.如果;遇到a,则忽略该行的其余部分(但不是全部,除非第一个字符是;).
我正在扩展RegexParsers我的解析器并忽略空格(默认方式),所以我无论如何都会丢失新的行字符.我不希望修改我必须满足注释可能性的每个解析器,因为语句可以跨越多行(因此每个语句的每个部分可能以注释结束).是否有任何干净的方法来实现这一目标?
我需要对解析器匹配进行比标准符号允许的更复杂的语法检查,并且我目前正在函数应用程序中执行它^^.示例简化方案是检查重复的关键字:
def keywords: Parser[List[String]] = "[" ~ repsep(keyword, ",") ~ "]" ^^ {
case _ ~ ks ~ _ =>
ks.groupBy(x => x).filter(_._2.length > 1).keys.toList match {
case Nil => ks
case x => throw new DuplicateKeywordsException(x)
}
}
Run Code Online (Sandbox Code Playgroud)
这是有效的,因为在我的解析器中会抛出异常,但我希望将失败捕获为ParseResult.Failure捕获它发生的位置的输入.我无法弄清楚如何从一个^^块内发出信号或使用其他一些构造来达到同样的目的.
我正在尝试解析一个可以包含转义字符的字符串,这是一个例子:
import qualified Data.Text as T
exampleParser :: Parser T.Text
exampleParser = T.pack <$> many (char '\\' *> escaped <|> anyChar)
where escaped = satisfy (\c -> c `elem` ['\\', '"', '[', ']'])
Run Code Online (Sandbox Code Playgroud)
上面的解析器创建一个String然后将其打包Text.是否有任何方法使用attoparsec提供的高效字符串处理函数解析带有类似上述转义的字符串?像string,scan,runScanner,takeWhile,...
解析类似的东西"one \"two\" \[three\]"会产生one "two" [three].
更新:
感谢@epsilonhalbe,我能够提出一个完美满足我需求的通用解决方案; 注意,以下功能不查找匹配转义字符像[..],"..",(..),等; 而且,如果它找到一个无效的转义字符,则将其视为\文字字符.
takeEscapedWhile :: (Char -> Bool) -> (Char -> Bool) -> Parser …Run Code Online (Sandbox Code Playgroud) 考虑以下玩具语法和解析器:
(* in EBNF:
ap = "a", { "ba" }
bp = ap, "bc"
*)
let ap = sepBy1 (pstring "a") (pstring "b")
let bp = ap .>> (pstring "bc")
let test = run bp "abababc"
Run Code Online (Sandbox Code Playgroud)
我得到以下输出:
Error in Ln: 1 Col: 7
abababc
^
Expecting: 'a'
Run Code Online (Sandbox Code Playgroud)
清楚地sepBy1看到最后一个b并期望它导致另一个a,当它找不到时失败.是否有一个变sepBy1体会回溯b并使这个解析成功?我不应该使用它吗?