mirror of
https://github.com/enso-org/enso.git
synced 2024-11-26 08:52:58 +03:00
Implement imports and exports for Main.enso (#1098)
This commit is contained in:
parent
8e764f957b
commit
5b6ce5b31f
@ -1,3 +1,7 @@
|
||||
import Builtins
|
||||
|
||||
from Builtins export Nil, Cons
|
||||
|
||||
## PRIVATE
|
||||
A helper for the `map` function.
|
||||
|
||||
|
3
distribution/std-lib/Base/src/Main.enso
Normal file
3
distribution/std-lib/Base/src/Main.enso
Normal file
@ -0,0 +1,3 @@
|
||||
import Base.List
|
||||
|
||||
from Base.List export Nil, Cons
|
@ -34,7 +34,7 @@ import org.enso.pkg.QualifiedName;
|
||||
|
||||
/** Container class for static predefined atoms, methods, and their containing scope. */
|
||||
public class Builtins {
|
||||
public static final String MODULE_NAME = "Builtins";
|
||||
public static final String MODULE_NAME = "Builtins.Main";
|
||||
|
||||
/** Container for method names needed outside this class. */
|
||||
public static class MethodNames {
|
||||
@ -66,7 +66,7 @@ public class Builtins {
|
||||
public Builtins(Context context) {
|
||||
Language language = context.getLanguage();
|
||||
|
||||
module = Module.empty(QualifiedName.simpleName(MODULE_NAME));
|
||||
module = Module.empty(QualifiedName.fromString(MODULE_NAME).get());
|
||||
scope = module.compileScope(context);
|
||||
unit = new AtomConstructor("Unit", scope).initializeFields();
|
||||
any = new AtomConstructor("Any", scope).initializeFields();
|
||||
|
@ -22,6 +22,7 @@ class Passes(passes: Option[List[PassGroup]] = None) {
|
||||
val moduleDiscoveryPasses = new PassGroup(
|
||||
List(
|
||||
DocumentationComments,
|
||||
MainImportAndExport,
|
||||
ComplexType,
|
||||
FunctionBinding,
|
||||
GenerateMethodBodies,
|
||||
|
@ -95,7 +95,7 @@ case class BindingsMap(
|
||||
private def handleAmbiguity(
|
||||
candidates: List[ResolvedName]
|
||||
): Either[ResolutionError, ResolvedName] = {
|
||||
candidates match {
|
||||
candidates.distinct match {
|
||||
case List() => Left(ResolutionNotFound)
|
||||
case List(it) => Right(it)
|
||||
case items => Left(ResolutionAmbiguous(items))
|
||||
|
@ -0,0 +1,86 @@
|
||||
package org.enso.compiler.pass.desugar
|
||||
|
||||
import org.enso.compiler.context.{InlineContext, ModuleContext}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.pass.IRPass
|
||||
|
||||
/**
|
||||
* Desugars imports and exports mentioning only the project name to refer
|
||||
* to the `Main` module of the mentioned project instead.
|
||||
*/
|
||||
case object MainImportAndExport extends IRPass {
|
||||
|
||||
/** The type of the metadata object that the pass writes to the IR. */
|
||||
override type Metadata = IRPass.Metadata.Empty
|
||||
|
||||
/** The type of configuration for the pass. */
|
||||
override type Config = IRPass.Configuration.Default
|
||||
|
||||
/** The passes that this pass depends _directly_ on to run. */
|
||||
override val precursorPasses: Seq[IRPass] = Seq()
|
||||
|
||||
/** The passes that are invalidated by running this pass. */
|
||||
override val invalidatedPasses: Seq[IRPass] = Seq()
|
||||
|
||||
private val mainModuleName =
|
||||
IR.Name.Literal("Main", isReferent = true, location = None)
|
||||
|
||||
/** Executes the pass on the provided `ir`, and returns a possibly transformed
|
||||
* or annotated version of `ir`.
|
||||
*
|
||||
* @param ir the Enso IR to process
|
||||
* @param moduleContext a context object that contains the information needed
|
||||
* to process a module
|
||||
* @return `ir`, possibly having made transformations or annotations to that
|
||||
* IR.
|
||||
*/
|
||||
override def runModule(
|
||||
ir: IR.Module,
|
||||
moduleContext: ModuleContext
|
||||
): IR.Module = {
|
||||
val newImports = ir.imports.map {
|
||||
case i: IR.Module.Scope.Import.Module =>
|
||||
val parts = i.name.parts
|
||||
if (parts.length == 1) {
|
||||
i.copy(
|
||||
name = i.name.copy(parts = parts :+ mainModuleName),
|
||||
rename =
|
||||
computeRename(i.rename, parts.head.asInstanceOf[IR.Name.Literal])
|
||||
)
|
||||
} else { i }
|
||||
case other => other
|
||||
}
|
||||
val newExports = ir.exports.map { ex =>
|
||||
val parts = ex.name.parts
|
||||
if (parts.length == 1) {
|
||||
ex.copy(
|
||||
name = ex.name.copy(parts = parts :+ mainModuleName),
|
||||
rename =
|
||||
computeRename(ex.rename, parts.head.asInstanceOf[IR.Name.Literal])
|
||||
)
|
||||
} else {
|
||||
ex
|
||||
}
|
||||
}
|
||||
ir.copy(imports = newImports, exports = newExports)
|
||||
}
|
||||
|
||||
/** Executes the pass on the provided `ir`, and returns a possibly transformed
|
||||
* or annotated version of `ir` in an inline context.
|
||||
*
|
||||
* @param ir the Enso IR to process
|
||||
* @param inlineContext a context object that contains the information needed
|
||||
* for inline evaluation
|
||||
* @return `ir`, possibly having made transformations or annotations to that
|
||||
* IR.
|
||||
*/
|
||||
override def runExpression(
|
||||
ir: IR.Expression,
|
||||
inlineContext: InlineContext
|
||||
): IR.Expression = ir
|
||||
|
||||
private def computeRename(
|
||||
originalRename: Option[IR.Name.Literal],
|
||||
qualName: IR.Name.Literal
|
||||
): Some[IR.Name.Literal] = Some(originalRename.getOrElse(qualName))
|
||||
}
|
@ -64,10 +64,13 @@ class ImportResolver(compiler: Compiler) {
|
||||
IR.Module.Scope.Import
|
||||
.Module(
|
||||
IR.Name.Qualified(
|
||||
List(IR.Name.Literal("Builtins", isReferent = true, None)),
|
||||
List(
|
||||
IR.Name.Literal("Builtins", isReferent = true, None),
|
||||
IR.Name.Literal("Main", isReferent = true, None)
|
||||
),
|
||||
None
|
||||
),
|
||||
None,
|
||||
Some(IR.Name.Literal("Builtins", isReferent = true, None)),
|
||||
isAll = true,
|
||||
None,
|
||||
None,
|
||||
@ -82,9 +85,9 @@ class ImportResolver(compiler: Compiler) {
|
||||
current.unsafeSetCompilationStage(
|
||||
Module.CompilationStage.AFTER_IMPORT_RESOLUTION
|
||||
)
|
||||
seen += current
|
||||
stack = importedModules.map(_.module) ++ stack
|
||||
stack = currentLocal.resolvedImports.map(_.module) ++ stack
|
||||
}
|
||||
seen += current
|
||||
}
|
||||
seen.toList
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ class PassesTest extends CompilerTest {
|
||||
passes.getPrecursors(AliasAnalysis).map(_.passes) shouldEqual Some(
|
||||
List(
|
||||
DocumentationComments,
|
||||
MainImportAndExport,
|
||||
ComplexType,
|
||||
FunctionBinding,
|
||||
GenerateMethodBodies,
|
||||
|
@ -0,0 +1,90 @@
|
||||
package org.enso.compiler.test.pass.desugar
|
||||
|
||||
import org.enso.compiler.Passes
|
||||
import org.enso.compiler.context.{FreshNameSupply, ModuleContext}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.pass.desugar.MainImportAndExport
|
||||
import org.enso.compiler.pass.{PassConfiguration, PassGroup, PassManager}
|
||||
import org.enso.compiler.test.CompilerTest
|
||||
|
||||
class MainImportAndExportTest extends CompilerTest {
|
||||
|
||||
// === Test Setup ===========================================================
|
||||
|
||||
def mkModuleContext: ModuleContext =
|
||||
buildModuleContext(
|
||||
freshNameSupply = Some(new FreshNameSupply)
|
||||
)
|
||||
|
||||
val passes = new Passes
|
||||
|
||||
val precursorPasses: PassGroup =
|
||||
passes.getPrecursors(MainImportAndExport).get
|
||||
|
||||
val passConfiguration: PassConfiguration = PassConfiguration()
|
||||
|
||||
implicit val passManager: PassManager =
|
||||
new PassManager(List(precursorPasses), passConfiguration)
|
||||
|
||||
/** Adds an extension method to analyse an Enso module.
|
||||
*
|
||||
* @param ir the ir to analyse
|
||||
*/
|
||||
implicit class AnalyseModule(ir: IR.Module) {
|
||||
|
||||
/** Performs tail call analysis on [[ir]].
|
||||
*
|
||||
* @param context the module context in which analysis takes place
|
||||
* @return [[ir]], with tail call analysis metadata attached
|
||||
*/
|
||||
def analyse(implicit context: ModuleContext) = {
|
||||
MainImportAndExport.runModule(ir, context)
|
||||
}
|
||||
}
|
||||
|
||||
// === The Tests ============================================================
|
||||
|
||||
"Main import and export desugaring" should {
|
||||
implicit val ctx: ModuleContext = mkModuleContext
|
||||
|
||||
val ir =
|
||||
"""
|
||||
|import Foo
|
||||
|import Foo as Bar
|
||||
|from Foo import Bar, Baz
|
||||
|from Foo as Bar import Baz, Spam
|
||||
|
|
||||
|export Foo
|
||||
|export Foo as Bar
|
||||
|from Foo export Bar, Baz
|
||||
|from Foo as Bar export all
|
||||
|
|
||||
|import Foo.Bar
|
||||
|export Foo.Bar
|
||||
|""".stripMargin.preprocessModule.analyse
|
||||
|
||||
"desugar project name imports correctly" in {
|
||||
ir.imports.take(4).map(_.showCode()) shouldEqual List(
|
||||
"import Foo.Main as Foo",
|
||||
"import Foo.Main as Bar",
|
||||
"from Foo.Main as Foo import Bar, Baz",
|
||||
"from Foo.Main as Bar import Baz, Spam"
|
||||
)
|
||||
}
|
||||
|
||||
"desugar project name exports correctly" in {
|
||||
ir.exports.take(4).map(_.showCode()) shouldEqual List(
|
||||
"export Foo.Main as Foo",
|
||||
"export Foo.Main as Bar",
|
||||
"from Foo.Main as Foo export Bar, Baz",
|
||||
"from Foo.Main as Bar export all"
|
||||
)
|
||||
}
|
||||
|
||||
"leave module imports and exports untouched" in {
|
||||
ir.imports.last.showCode() shouldEqual "import Foo.Bar"
|
||||
ir.exports.last.showCode() shouldEqual "export Foo.Bar"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
import Base.Test
|
||||
import Base.List
|
||||
from Base import all
|
||||
|
||||
spec = describe "List" <|
|
||||
l = Cons 1 <| Cons 2 <| Cons 3 <| Nil
|
||||
l = Base.Cons 1 <| Base.Cons 2 <| Base.Cons 3 <| Base.Nil
|
||||
it "should have properly defined length" <|
|
||||
l.length.should_equal 3
|
||||
it "should have well defined length when empty" <|
|
||||
|
Loading…
Reference in New Issue
Block a user