/ / Scala: analisando vários arquivos usando os combinadores do Scala - scala, parser-combinators

Scala: analisando vários arquivos usando os combinadores do Scala - scala, parser-combinators

Eu estou escrevendo um DSL usando combinadores de analisador Scalae ter uma versão funcional que possa ler um único arquivo e analisá-lo. No entanto, eu gostaria de dividir minha entrada em vários arquivos, onde alguns arquivos são "padrão" e podem ser usados ​​com qualquer arquivo de nível superior. O que eu gostaria é algo como:

import "a.dsl"
import "b.dsl"
// resto do arquivo usando {a, b}

Não é importante a ordem em que os arquivos são lidosem ou que algo é necessariamente "definido" antes de ser referido, portanto, analisar primeiro o arquivo de nível superior e, em seguida, analisar o fechamento de todas as importações em um único modelo é suficiente. Posteriormente, processarei o modelo resultante para minhas próprias finalidades.

A pergunta que eu tenho é, há uma maneira razoávelde realizar isso? Se necessário, posso iterar sobre o encerramento, analisar cada arquivo em um modelo separado e "mesclar" manualmente os modelos resultantes, mas isso parece desajeitado e me parece feio.

BTW, estou usando uma extensão de StandardTokenParsers, se isso importa.

Respostas:

2 para resposta № 1

Eu acho que a única abordagem seria abrir eanalisar o arquivo indicado pela importação diretamente. A partir daí, você pode criar uma árvore de subexpressão para o módulo. Talvez você não precise mesclar manualmente as árvores ao analisar, por exemplo, se você já estiver usando ^^ e / ou ^^^ Para retornar suas próprias expressões, você deve ser capaz de simplesmente emitir um tipo de expressão relevante no local correto da árvore, por exemplo:

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)
}

}

}

Você poderia simplesmente adicionar um eval ao seu traço de expressão, implementar cada eval comonecessário nas subclasses e depois fazer um visitante recursivamente descer sua AST. Dessa maneira, você não precisaria mesclar manualmente as árvores de expressão.