Estoy escribiendo un DSL usando los combinadores de analizador de Scalay tiene una versión funcional que puede leer un solo archivo y analizarlo. Sin embargo, me gustaría dividir mi entrada en varios archivos donde algunos archivos son "estándar" y se pueden usar con cualquier archivo de nivel superior. Lo que me gustaría es algo como:
importar "a.dsl"
importar "b.dsl"
// resto del archivo usando {a, b}
No es importante el orden en que se leen los archivos.en o que algo está necesariamente "definido" antes de referirse, por lo que analizar el archivo de nivel superior primero y luego analizar el cierre de todas las importaciones en un solo modelo es suficiente. Luego, postprocesaré el modelo resultante para mis propios propósitos.
La pregunta que tengo es, ¿hay una manera razonable?de lograr esto? Si fuera necesario, podría recorrer el cierre, analizar cada archivo en un modelo separado y "fusionar" manualmente los modelos resultantes, pero esto me parece torpe y me parece feo.
Por cierto, estoy usando una extensión de StandardTokenParsers
, si eso importa.
Respuestas
2 para la respuesta № 1Creo que el único enfoque sería abrir yanalizar el archivo indicado por la importación directamente. Desde allí puede crear un árbol de sub-expresión para el módulo. Es posible que no necesite combinar manualmente los árboles al analizar, por ejemplo, si ya está utilizando ^^
y / o ^^^
para devolver sus propias Expresiones, entonces debería poder emitir simplemente un tipo de expresión relevante en el lugar correcto dentro del árbol, por ejemplo:
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)
}
}
}
Usted podría simplemente agregar un eval
a su rasgo de Expresión, implemente cada eval comonecesario en las subclases y luego hacer que un visitante descienda recursivamente su AST. De esta manera, no sería necesario fusionar manualmente los árboles de expresión.