millfork/src/main/scala/millfork/parser/SourceLoadingQueue.scala

90 lines
3.4 KiB
Scala

package millfork.parser
import java.nio.file.{Files, Paths}
import fastparse.core.Parsed.{Failure, Success}
import millfork.CompilationOptions
import millfork.error.ErrorReporting
import millfork.node.{ImportStatement, Position, Program}
import scala.collection.mutable
/**
* @author Karol Stasiak
*/
class SourceLoadingQueue(val initialFilenames: List[String], val includePath: List[String], val options: CompilationOptions) {
private val parsedModules = mutable.Map[String, Program]()
private val moduleQueue = mutable.Queue[() => Unit]()
val extension: String = ".ml"
def run(): Program = {
initialFilenames.foreach { i =>
parseModule(extractName(i), includePath, Right(i), options)
}
options.platform.startingModules.foreach {m =>
moduleQueue.enqueue(() => parseModule(m, includePath, Left(None), options))
}
while (moduleQueue.nonEmpty) {
moduleQueue.dequeueAll(_ => true).par.foreach(_())
}
ErrorReporting.assertNoErrors("Parse failed")
parsedModules.values.reduce(_ + _)
}
def lookupModuleFile(includePath: List[String], moduleName: String, position: Option[Position]): String = {
includePath.foreach { dir =>
val file = Paths.get(dir, moduleName + extension).toFile
ErrorReporting.debug("Checking " + file)
if (file.exists()) {
return file.getAbsolutePath
}
}
ErrorReporting.fatal(s"Module `$moduleName` not found", position)
}
def parseModule(moduleName: String, includePath: List[String], why: Either[Option[Position], String], options: CompilationOptions): Unit = {
val filename: String = why.fold(p => lookupModuleFile(includePath, moduleName, p), s => s)
ErrorReporting.debug(s"Parsing $filename")
val path = Paths.get(filename)
val parentDir = path.toFile.getAbsoluteFile.getParent
val src = new String(Files.readAllBytes(path))
val parser = MfParser(filename, src, parentDir, options)
parser.toAst match {
case Success(prog, _) =>
parsedModules.synchronized {
parsedModules.put(moduleName, prog)
prog.declarations.foreach {
case s@ImportStatement(m) =>
if (!parsedModules.contains(m)) {
moduleQueue.enqueue(() => parseModule(m, parentDir :: includePath, Left(s.position), options))
}
case _ => ()
}
}
case f@Failure(a, b, d) =>
ErrorReporting.error(s"Failed to parse the module `$moduleName` in $filename", Some(parser.indexToPosition(f.index, parser.lastLabel)))
// ErrorReporting.error(a.toString)
// ErrorReporting.error(b.toString)
// ErrorReporting.error(d.toString)
// ErrorReporting.error(d.traced.expected)
// ErrorReporting.error(d.traced.stack.toString)
// ErrorReporting.error(d.traced.traceParsers.toString)
// ErrorReporting.error(d.traced.fullStack.toString)
// ErrorReporting.error(f.toString)
if (parser.lastLabel != "") {
ErrorReporting.error(s"Syntax error: ${parser.lastLabel} expected", Some(parser.lastPosition))
} else {
ErrorReporting.error("Syntax error", Some(parser.lastPosition))
}
}
}
def extractName(i: String): String = {
val noExt = i.stripSuffix(extension)
val lastSlash = noExt.lastIndexOf('/') max noExt.lastIndexOf('\\')
if (lastSlash >= 0) i.substring(lastSlash + 1) else i
}
}