diff --git a/skiplang/compiler/src/SkipParser.sk b/skiplang/compiler/src/SkipParser.sk index ac3aa622a..731c175da 100644 --- a/skiplang/compiler/src/SkipParser.sk +++ b/skiplang/compiler/src/SkipParser.sk @@ -33,6 +33,7 @@ mutable class SkipParser{ lazy: Bool, tokenLexer: mutable SkipLexer.SkipCoreLexer, templateLexer: mutable SkipLexer.TemplateLexer, + accessedEnvVars: mutable Set = mutable Set[], } extends Parser.Parser { static fun create(source: String, lazy: Bool): mutable this { lexingPosition = Lexer.LexingPosition::create(source); @@ -54,6 +55,10 @@ mutable class SkipParser{ }; } + readonly fun getAccessedEnvVars(): SortedSet { + SortedSet::createFromIterator(this.accessedEnvVars.values()) + } + mutable fun createPeekParser(): mutable this { mutable static{ lazy => this.lazy, @@ -4791,6 +4796,8 @@ mutable class SkipParser{ }; closeParen = this.eatTree(TokenKind.CLOSE_PAREN()); + this.accessedEnvVars.insert(varName.getToken().stringLiteralValue()); + ParseTree.EnvMacroTree{ range => this.createRange(start), envKeyword, @@ -5108,7 +5115,7 @@ fun parseSource( source: String, isLazy: Bool, filename: String = "", -): Parser.ParseResults { +): (Parser.ParseResults, SortedSet) { parser = SkipParser::create(source, isLazy); tree = parser.parseSourceUnit(); allErrors = parser.getErrors(); @@ -5124,7 +5131,11 @@ fun parseSource( if (errors.isEmpty() && !isLazy) { Parser.validateParse(tokens, tree, filename); }; - Parser.ParseResults{tokens, comments => parser.getComments(), tree, errors}; + + ( + Parser.ParseResults{tokens, comments => parser.getComments(), tree, errors}, + parser.getAccessedEnvVars(), + ) } fun getNamedParameterDefaultValue(tree: ParseTree): ParseTree { diff --git a/skiplang/compiler/src/compile.sk b/skiplang/compiler/src/compile.sk index 282ccb9b3..6bb3a3cf6 100644 --- a/skiplang/compiler/src/compile.sk +++ b/skiplang/compiler/src/compile.sk @@ -245,28 +245,7 @@ fun getOrInitializeBackend(context: mutable SKStore.Context): SKStore.EagerDir { }; if (conf.emit == "sklib") { - accessed_env_vars = context.getGlobal( - FileCache.kEnvAccessGlobal, - ) match { - | None() -> SortedSet[] - | Some(envMapFile) -> - FileCache.EnvMapFile::type(envMapFile) - .value.maybeGet(conf.lib_name) - .default(SortedSet[]) - }; - env_dir = SKStore.EHandle( - SKStore.SID::keyType, - SKStore.StringFile::type, - SKStore.DirName::create( - `/packageEnv/${conf.lib_name.default("_default")}/`, - ), - ); - env = SortedMap::createFromIterator( - accessed_env_vars - .map(v -> (v, env_dir.unsafeGet(context, SKStore.SID(v)).value)) - .iterator(), - ); - compile_sklib(conf, env); + compile_sklib(context, conf); return void }; @@ -423,11 +402,21 @@ fun compile_binary( }) } -fun compile_sklib( - conf: Config.Config, - env_vars: SortedMap, -): void { +fun compile_sklib(context: mutable SKStore.Context, conf: Config.Config): void { runCompilerPhase("native/write_sklib", () -> { + accessed_env_vars = SortedSet::createFromItems( + FileCache.packageEnvAccessDir.getArray( + context, + SKStore.SID(conf.lib_name.fromSome()), + ).map(v -> v.value), + ); + env_dir = FileCache.packageEnvDir(conf.lib_name); + env_vars = SortedMap::createFromIterator( + accessed_env_vars + .map(v -> (v, env_dir.unsafeGet(context, SKStore.SID(v)).value)) + .iterator(), + ); + // NOTE: Static libraries (`.a` archives) provided as input files are // bundled into the `sklib`. sklib = mutable SklibBuilder( diff --git a/skiplang/compiler/src/convertTree.sk b/skiplang/compiler/src/convertTree.sk index 68f26fa4e..030b8432d 100644 --- a/skiplang/compiler/src/convertTree.sk +++ b/skiplang/compiler/src/convertTree.sk @@ -884,7 +884,6 @@ class Converter{file: FileCache.InputSource} { fun convertEnvMacro(_range: FileRange, varName: ParseTree): SkipAst.Expr_ { SkipAst.EnvMacro( this.createName(varName, varName.getToken().stringLiteralValue()), - this.file.pkg_opt, ) } diff --git a/skiplang/compiler/src/skfmt.sk b/skiplang/compiler/src/skfmt.sk index 456b807de..f9fb6e710 100644 --- a/skiplang/compiler/src/skfmt.sk +++ b/skiplang/compiler/src/skfmt.sk @@ -11,7 +11,7 @@ fun prettyPrint( contents: String, filename: String, ): Result { - parsed = SkipParser.parseSource(contents, false); + (parsed, _) = SkipParser.parseSource(contents, false); if (parsed.errors.size() > 0) { // Parse errors are expected. Exceptions are bugs. Failure( diff --git a/skiplang/compiler/src/skipAst.sk b/skiplang/compiler/src/skipAst.sk index 4dfcaaed2..4b84bb07a 100644 --- a/skiplang/compiler/src/skipAst.sk +++ b/skiplang/compiler/src/skipAst.sk @@ -365,7 +365,7 @@ base class Expr_ { functionName: ?Name, body: Expr, ) - | EnvMacro(Name, ?String) + | EnvMacro(Name) | MacroDot(Expr, Name) | MacroStaticDot(Expr, Name) } diff --git a/skiplang/compiler/src/skipAstPp.sk b/skiplang/compiler/src/skipAstPp.sk index 972ab5b50..82fc4e8f1 100644 --- a/skiplang/compiler/src/skipAstPp.sk +++ b/skiplang/compiler/src/skipAstPp.sk @@ -861,7 +861,7 @@ fun expr(o: mutable BufferedPrinter.Out, e: SkipAst.Expr): void { }; o.out(") "); expr(o, body) - | SkipAst.EnvMacro(varName, _) -> + | SkipAst.EnvMacro(varName) -> o.out("#env ("); o.out(literal_to_string(SkipAst.StringLiteral(varName.i1))); o.out(")") diff --git a/skiplang/compiler/src/skipExpand.sk b/skiplang/compiler/src/skipExpand.sk index fdad9bb8d..4c6b6ff84 100644 --- a/skiplang/compiler/src/skipExpand.sk +++ b/skiplang/compiler/src/skipExpand.sk @@ -3419,34 +3419,7 @@ fun expr_( functionName, expr(context, env, body), ) - | A.EnvMacro(varName, pkg_opt) -> - pkg_env = SKStore.EHandle( - SKStore.SID::keyType, - SKStore.StringFile::type, - // FIXME: Ensure package names cannot start with an underscore to avoid clash with `_default`. - SKStore.DirName::create(`/packageEnv/${pkg_opt.default("_default")}/`), - ); - name = varName.i1; - value = pkg_env.maybeGet(context, SKStore.SID(name)) match { - | None() -> - SkipError.error(varName.i0, `Environment variable ${name} is not set.`) - | Some(v) -> v.value - }; - // Record access to env variable. - cur_env_map = context.getGlobal(FileCache.kEnvAccessGlobal) match { - | None() -> SortedMap[] - | Some(envMapFile) -> FileCache.EnvMapFile::type(envMapFile).value - }; - context.setGlobal( - FileCache.kEnvAccessGlobal, - FileCache.EnvMapFile( - cur_env_map.set( - pkg_opt, - cur_env_map.maybeGet(pkg_opt).default(SortedSet[]).set(name), - ), - ), - ); - A.Literal(A.StringLiteral(value)) + | A.EnvMacro(varName) -> A.EnvMacro(varName) } } diff --git a/skiplang/compiler/src/skipMain.sk b/skiplang/compiler/src/skipMain.sk index c43c769d7..3b8fc96df 100644 --- a/skiplang/compiler/src/skipMain.sk +++ b/skiplang/compiler/src/skipMain.sk @@ -15,9 +15,9 @@ fun type_program( ): SKStore.EHandle { parsed_program = FileCache.fileDir.map( FileCache.InputSource::keyType, - SkipParse.DefsFile::type, + SkipParse.DefsWithAccessedEnvVarsFile::type, context, - SkipParse.astDirName, + SKStore.DirName::create("/astAndAccessedEnvVarsCache/"), (context, writer, key, sources) ~> { source = sources.first; SkipError.catchErrors(0, context, () -> { @@ -26,7 +26,35 @@ fun type_program( }, ); - (defsDir, childDir) = SkipExpand.program(context, parsed_program); + _ = parsed_program.map( + SKStore.SID::keyType, + SKStore.StringFile::type, + context, + FileCache.packageEnvAccessDirName, + (_context, writer, key, values) ~> { + writer.setArray( + SKStore.SID(key.pkg_opt.default(FileCache.kAnonymousPackageName)), + values + .getUnique() + .fromSome() + .accessed_env_vars.values() + .map(v -> SKStore.StringFile(v)) + .collect(Array), + ) + }, + ); + + ast = parsed_program.map( + FileCache.InputSource::keyType, + SkipParse.DefsFile::type, + context, + SkipParse.astDirName, + (_context, writer, key, values) ~> { + writer.set(key, SkipParse.DefsFile(values.getUnique().fromSome().defs)) + }, + ); + + (defsDir, childDir) = SkipExpand.program(context, ast); inhDir = SkipInherit.populateClassesDir(context); SkipNaming.populateClasses(context, defsDir, inhDir, childDir); SkipNaming.populateFuns(context, defsDir); diff --git a/skiplang/compiler/src/skipNaming.sk b/skiplang/compiler/src/skipNaming.sk index 5b3b1f4cc..8edadce28 100644 --- a/skiplang/compiler/src/skipNaming.sk +++ b/skiplang/compiler/src/skipNaming.sk @@ -3660,6 +3660,18 @@ fun expandForEachMacro( N.Bind(iteratorn, getIterator, flagged) } +fun expandEnvMacro(context: mutable SKStore.Context, varName: A.Name): N.Expr_ { + (pos, name) = varName; + value = FileCache.packageEnvDir(pos.file.pkg_opt).maybeGet( + context, + SKStore.SID(name), + ) match { + | None() -> SkipError.error(pos, `Environment variable ${name} is not set.`) + | Some(v) -> v.value + }; + N.Literal(A.StringLiteral(value)) +} + fun reportDuplicateMacro( pos: FileRange, id: String, @@ -3816,9 +3828,8 @@ fun expr_( | A.Literal(l) -> N.Literal(l) | A.Var(n) -> N.Var(n) | A.MacroVar(n) -> expandExpressionMacro(env, n) - | A.Seq _ - | A.EnvMacro _ -> - invariant_violation("assert false") + | A.Seq _ -> invariant_violation("assert false") + | A.EnvMacro(n) -> expandEnvMacro(context, n) | A.If(e1, e2, e3) -> N.If( expr(context, acc, env, e1), diff --git a/skiplang/compiler/src/skipParse.sk b/skiplang/compiler/src/skipParse.sk index f7b7c2319..eb139a2d5 100644 --- a/skiplang/compiler/src/skipParse.sk +++ b/skiplang/compiler/src/skipParse.sk @@ -68,6 +68,18 @@ const packageDir: SKStore.EHandle< packageDirName, ); +const packageEnvAccessDirName: SKStore.DirName = SKStore.DirName::create( + "/packageEnvAccess/", +); +const packageEnvAccessDir: SKStore.EHandle< + SKStore.SID, + SKStore.StringFile, +> = SKStore.EHandle( + SKStore.SID::keyType, + SKStore.StringFile::type, + packageEnvAccessDirName, +); + fun pkgDelta( old_pkg: InputPackageFiles, new_pkg: InputPackageFiles, @@ -205,17 +217,33 @@ fun writeFiles( } } +// FIXME: Ensure package names cannot start with an underscore to avoid clash with `_anonymous`. +const kAnonymousPackageName: String = "_anonymous"; + +fun packageEnvDirName(pkg_opt: ?String): SKStore.DirName { + SKStore.DirName::create( + `/packageEnv/${pkg_opt.default(kAnonymousPackageName)}/`, + ) +} + +fun packageEnvDir( + pkg_opt: ?String, +): SKStore.EHandle { + SKStore.EHandle( + SKStore.SID::keyType, + SKStore.StringFile::type, + packageEnvDirName(pkg_opt), + ) +} + fun updatePackageEnv( context: mutable SKStore.Context, pkg_opt: ?String, env: Array<(String, String)>, ): void { - envDirName = SKStore.DirName::create( - `/packageEnv/${pkg_opt.default("_default")}/`, - ); + envDirName = packageEnvDirName(pkg_opt); envDir = context.unsafeMaybeGetEagerDir(envDirName) match { - | Some _ -> - SKStore.EHandle(SKStore.SID::keyType, SKStore.StringFile::type, envDirName) + | Some _ -> packageEnvDir(pkg_opt) | None _ -> context.mkdir(SKStore.SID::keyType, SKStore.StringFile::type, envDirName) }; @@ -236,12 +264,6 @@ fun updatePackageEnv( }; } -const kEnvAccessGlobal: String = "ACCESSED_ENV_MAP"; - -class EnvMapFile( - value: SortedMap>, -) extends SKStore.File - module end; module SkipParse; @@ -254,11 +276,18 @@ const astDir: SKStore.EHandle = SKStore.EHandle( ); class DefsFile(value: List) extends SKStore.File +class DefsWithAccessedEnvVarsFile( + defs: List, + accessed_env_vars: SortedSet, +) extends SKStore.File -fun parseFile(file: FileCache.InputSource, source: String): DefsFile { - fileResult = SkipParser.parseSource(source, true); - ast = parseToAst(file, fileResult); - DefsFile(ast) +fun parseFile( + file: FileCache.InputSource, + source: String, +): DefsWithAccessedEnvVarsFile { + (fileResult, accessed_env_vars) = SkipParser.parseSource(source, true); + defs = parseToAst(file, fileResult); + DefsWithAccessedEnvVarsFile(defs, accessed_env_vars) } fun parseToAst(