From f46466365be8ce2ee2c3507633ecb6485e279f2e Mon Sep 17 00:00:00 2001 From: Karol Stasiak Date: Fri, 14 Jun 2019 23:20:03 +0200 Subject: [PATCH] Compilation order should be decided by the import statements --- docs/lang/syntax.md | 13 +++++++- .../parser/AbstractSourceLoadingQueue.scala | 30 ++++++++++++++++--- .../parser/MosSourceLoadingQueue.scala | 2 ++ .../millfork/parser/ZSourceLoadingQueue.scala | 2 ++ 4 files changed, 42 insertions(+), 5 deletions(-) diff --git a/docs/lang/syntax.md b/docs/lang/syntax.md index 886ae4dc..fdd178f5 100644 --- a/docs/lang/syntax.md +++ b/docs/lang/syntax.md @@ -143,7 +143,18 @@ A function can be declared at the top level. For more details, see [Functions](. ## `import` statements -TODO + import + +Adds a module to the program. + +The module is looked up first in the current working directory, and then in the include directories. + +Usually, the imported module will undergo the first phase of compilation first. +This means that the constants in the imported module will be resolved first, allowing you to use them in the importing module. + +The only exception to this rule is when the importing graph has a cycle, in which case the order of modules within the cycle is unspecified. + +All starting modules are considered to be imported by all source files explicitly mentioned on the command line. ## Statements diff --git a/src/main/scala/millfork/parser/AbstractSourceLoadingQueue.scala b/src/main/scala/millfork/parser/AbstractSourceLoadingQueue.scala index 43b2beb7..0cdf121a 100644 --- a/src/main/scala/millfork/parser/AbstractSourceLoadingQueue.scala +++ b/src/main/scala/millfork/parser/AbstractSourceLoadingQueue.scala @@ -4,8 +4,7 @@ import java.nio.charset.StandardCharsets import java.nio.file.{Files, Paths} import fastparse.core.Parsed.{Failure, Success} -import millfork.{CompilationFlag, CompilationOptions} -import millfork.error.ConsoleLogger +import millfork.{CompilationFlag, CompilationOptions, Tarjan} import millfork.node.{ImportStatement, Position, Program} import scala.collection.mutable @@ -16,12 +15,32 @@ abstract class AbstractSourceLoadingQueue[T](val initialFilenames: List[String], val options: CompilationOptions) { protected val parsedModules: mutable.Map[String, Program] = mutable.Map[String, Program]() + protected val moduleDependecies: mutable.Set[(String, String)] = mutable.Set[(String, String)]() protected val moduleQueue: mutable.Queue[() => Unit] = mutable.Queue[() => Unit]() val extension: String = ".mfk" + def standardModules: IndexedSeq[String] + def enqueueStandardModules(): Unit def run(): Program = { + for { + initialFilename <- initialFilenames + startingModule <- options.platform.startingModules + } { + val initialModule = extractName(initialFilename) + moduleDependecies += initialModule -> startingModule + for (standardModule <- standardModules) { + moduleDependecies += initialModule -> standardModule + moduleDependecies += startingModule -> standardModule + } + } + for { + earlier <- standardModules.indices + later <- (earlier + 1) until standardModules.length + } { + moduleDependecies += standardModules(later) -> standardModules(earlier) + } initialFilenames.foreach { i => parseModule(extractName(i), includePath, Right(i)) } @@ -37,7 +56,9 @@ abstract class AbstractSourceLoadingQueue[T](val initialFilenames: List[String], } } options.log.assertNoErrors("Parse failed") - parsedModules.values.reduce(_ + _).applyImportantAliases + val compilationOrder = Tarjan.sort(parsedModules.keys, moduleDependecies) + options.log.debug("Compilation order: " + compilationOrder.mkString(", ")) + compilationOrder.filter(parsedModules.contains).map(parsedModules).reduce(_ + _).applyImportantAliases } def lookupModuleFile(includePath: List[String], moduleName: String, position: Option[Position]): String = { @@ -75,6 +96,7 @@ abstract class AbstractSourceLoadingQueue[T](val initialFilenames: List[String], parsedModules.put(moduleName, prog) prog.declarations.foreach { case s@ImportStatement(m) => + moduleDependecies += moduleName -> m if (!parsedModules.contains(m)) { moduleQueue.enqueue(() => parseModule(m, parentDir :: includePath, Left(s.position))) } @@ -94,6 +116,6 @@ abstract class AbstractSourceLoadingQueue[T](val initialFilenames: List[String], 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 + if (lastSlash >= 0) noExt.substring(lastSlash + 1) else noExt } } diff --git a/src/main/scala/millfork/parser/MosSourceLoadingQueue.scala b/src/main/scala/millfork/parser/MosSourceLoadingQueue.scala index 7477ce19..5248b373 100644 --- a/src/main/scala/millfork/parser/MosSourceLoadingQueue.scala +++ b/src/main/scala/millfork/parser/MosSourceLoadingQueue.scala @@ -13,6 +13,8 @@ class MosSourceLoadingQueue(initialFilenames: List[String], override def createParser(filename: String, src: String, parentDir: String, featureConstants: Map[String, Long], pragmas: Set[String]): MfParser[AssemblyLine] = MosParser(filename, src, parentDir, options, featureConstants) + override def standardModules: IndexedSeq[String] = IndexedSeq("zp_reg", "bcd_6502") + def enqueueStandardModules(): Unit = { if (options.zpRegisterSize > 0) { moduleQueue.enqueue(() => parseModule("zp_reg", includePath, Left(None))) diff --git a/src/main/scala/millfork/parser/ZSourceLoadingQueue.scala b/src/main/scala/millfork/parser/ZSourceLoadingQueue.scala index a61bd295..30ced90f 100644 --- a/src/main/scala/millfork/parser/ZSourceLoadingQueue.scala +++ b/src/main/scala/millfork/parser/ZSourceLoadingQueue.scala @@ -19,6 +19,8 @@ class ZSourceLoadingQueue(initialFilenames: List[String], Z80Parser(filename, src, parentDir, options, featureConstants, useIntelSyntax) } + override def standardModules: IndexedSeq[String] = IndexedSeq.empty + def enqueueStandardModules(): Unit = { // TODO }