我有以下模板:
#foo(args)# // START CONTAINER1
#foo(foo <- foos)(args)# // BLOCK STARTS HERE (`args` can be on either side of `block`)
#bar(args)# // START CONTAINER2
#.bar# // END CONTAINER2
#.foo# // END BLOCK
#.foo# // END CONTAINER1
*注意#.foo#
如何关闭每个容器/块
我在这里看到的问题是,没有任何独特的id
代表每个块,所以我必须跟踪有多少个容器开启/关闭器(#foo#
/ #.foo#
)以便具有内部容器的END CONTAINER
哈希的块不会将解析器混淆为结束块。
我如何使用Scala的解析器以这样的语言解析块?
我从这开始:
def maybeBlockMaybeJustContainer:Content = {
(openingHash ~ identifier ~ opt(args) ~> opt(blockName) <~ opt(args) ~ closingHash) ~
opt(content) ~
openHash ~ dot ~ identifier ~ closingHash ^^ ...
}
我也在考虑预处理但不知道从哪里开始。
答案 0 :(得分:2)
对于您的语言构造类似于BNF的形式
//Each of these is of type Parser (or String, which will be implicity converted to Parser when needed).
lazy val container = containerHeader ~ containerBody ~ containerEnd
lazy val containerHeader = hash ~ identifier ~ opt(args) ~ hash
lazy val containerBody = rep(block)
....
lazy val identifier = regex(new Regex("[a-zA-Z0-9-]+"))
lazy val hash = "#"
如果您的解析器接受一个字符串,那么该解析器定义的语言成员中的字符串。
这是context-free language的解析器。无上下文语言包括a[x]Sb[x]
形式的语言,其中[x]
表示前一个符号已存在x
次,其中x
未被语法定义,而是不同对于每个字符串。 (如果为语法定义x
,那么语言将是有限的,并且所有有限语言都是常规的。)
这意味着该语言允许嵌套或递归组件,例如块和容器。
如果您开始解析容器,然后解析该容器内的块,则在完全解析块之前,您将无法完成解析包含。对于您所用语言的所有字符串都是如此。
一旦你定义了语法,并且它正确地接受并拒绝了测试用例,那么你可以将它连接到你的AST。
lazy val identifier:Parser[Identifier] = regex(new Regex("[a-zA-Z0-9-]+")) ^^ {case s => Identifier(s)}
请注意,现在它的类型为Parser[Identifier]
,即如果解析正确,将返回Identifier
的解析器。
lazy val container:Parser[Container] = containerHeader ~ containerBody ~ containerEnd ^^ {case head ~ body ~ end => Container(head.identifier,body)}
如果有任何需求扩展,请告诉我。