我正在使用Scala解析器组合器编写DSL,并且有一个可以读取单个文件并解析它的工作版本。但是,我想将输入分成几个文件,其中一些文件是“标准的”,可以与任何顶级文件一起使用。我想要的是:
导入“a.dsl”
的文件的其余部分
导入“b.dsl”
//使用{a,b}
读取文件的顺序或者在被引用之前必须“定义”某些内容并不重要,因此首先解析顶级文件然后将所有导入的关闭解析为单个模型就足够了。然后,我将根据自己的目的对结果模型进行后处理。
我的问题是,是否有合理的方法来实现这一目标?如果有必要,我可以迭代闭包,将每个文件解析为一个单独的模型,并手动“合并”生成的模型,但这对我来说感觉很笨,看起来很难看。
顺便说一句,如果重要的话,我正在使用StandardTokenParsers
的扩展名。
答案 0 :(得分:2)
我认为唯一的方法是直接打开并解析导入指示的文件。从那里,您可以为模块创建子表达式树。解析时可能不需要手动合并树,例如,如果您已经使用^^
和/或^^^
返回自己的表达式,那么您应该能够简单地在表单中发出相关的表达式类型。树中的正确位置,例如:
import scala.util.parsing.combinator.syntactical.StandardTokenParsers
import scala.io.Source
object Example {
sealed trait Expr
case class Imports(modules: List[Module]) extends Expr
case class Module(modulePath: String, root: Option[Expr]) extends Expr
case class BracedExpr(x: String, y: String) extends Expr
case class Main(imports: Imports, braced: BracedExpr) extends Expr
class BlahTest extends StandardTokenParsers {
def importExpr: Parser[Module] = "import" ~> "\"" ~> stringLit <~ "\"" ^^ {
case modulePath =>
//you could use something other than `expr` below if you
//wanted to limit the expressions available in modules
//e.g. you could stop one module importing another.
phrase(expr)(new lexical.Scanner(Source.fromFile(modulePath).mkString)) match {
case Success(result, _) =>
Module(modulePath, Some(result))
case failure : NoSuccess =>
//TODO log or act on failure
Module(modulePath, None)
}
}
def prologExprs = rep(importExpr) ^^ {
case modules =>
Imports(modules)
}
def bracedExpr = "{" ~> stringLit ~ "," ~ stringLit <~ "}" ^^ {
case x ~ "," ~ y =>
BracedExpr(x, y)
}
def bodyExprs = bracedExpr
def expr = prologExprs ~ bodyExprs ^^ {
case prolog ~ body =>
Main(prolog, body)
}
}
}
您可以简单地将eval
添加到Expression特征中,根据需要在子类上实现每个eval,然后让访问者以递归方式降低您的AST。通过这种方式,您无需手动将表达式树合并在一起。