From 4e838231c0a9469b2b904d6f0f86a172206184d5 Mon Sep 17 00:00:00 2001 From: Lucas Hosseini Date: Tue, 8 Jul 2025 02:39:12 +0200 Subject: [PATCH 1/3] [skiplang/skc] Remove redundant `pkg_opt`. --- skiplang/compiler/src/convertTree.sk | 1 - skiplang/compiler/src/skipAst.sk | 2 +- skiplang/compiler/src/skipAstPp.sk | 2 +- skiplang/compiler/src/skipExpand.sk | 17 +++++++++++------ 4 files changed, 13 insertions(+), 9 deletions(-) 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/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..9a9f95c69 100644 --- a/skiplang/compiler/src/skipExpand.sk +++ b/skiplang/compiler/src/skipExpand.sk @@ -3419,17 +3419,19 @@ fun expr_( functionName, expr(context, env, body), ) - | A.EnvMacro(varName, pkg_opt) -> + | A.EnvMacro(varName) -> + (pos2, name) = varName; 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")}/`), + SKStore.DirName::create( + `/packageEnv/${pos2.file.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.`) + SkipError.error(pos2, `Environment variable ${name} is not set.`) | Some(v) -> v.value }; // Record access to env variable. @@ -3441,8 +3443,11 @@ fun expr_( FileCache.kEnvAccessGlobal, FileCache.EnvMapFile( cur_env_map.set( - pkg_opt, - cur_env_map.maybeGet(pkg_opt).default(SortedSet[]).set(name), + pos2.file.pkg_opt, + cur_env_map + .maybeGet(pos2.file.pkg_opt) + .default(SortedSet[]) + .set(name), ), ), ); From 525ed8ffe99783a7da90ed6c7511e25437d13205 Mon Sep 17 00:00:00 2001 From: Lucas Hosseini Date: Tue, 8 Jul 2025 02:39:40 +0200 Subject: [PATCH 2/3] [skiplang/skc] Move `#env()` macro expansion to naming phase. --- skiplang/compiler/src/skipExpand.sk | 34 +-------------------------- skiplang/compiler/src/skipNaming.sk | 36 ++++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 36 deletions(-) diff --git a/skiplang/compiler/src/skipExpand.sk b/skiplang/compiler/src/skipExpand.sk index 9a9f95c69..4c6b6ff84 100644 --- a/skiplang/compiler/src/skipExpand.sk +++ b/skiplang/compiler/src/skipExpand.sk @@ -3419,39 +3419,7 @@ fun expr_( functionName, expr(context, env, body), ) - | A.EnvMacro(varName) -> - (pos2, name) = varName; - 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/${pos2.file.pkg_opt.default("_default")}/`, - ), - ); - value = pkg_env.maybeGet(context, SKStore.SID(name)) match { - | None() -> - SkipError.error(pos2, `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( - pos2.file.pkg_opt, - cur_env_map - .maybeGet(pos2.file.pkg_opt) - .default(SortedSet[]) - .set(name), - ), - ), - ); - A.Literal(A.StringLiteral(value)) + | A.EnvMacro(varName) -> A.EnvMacro(varName) } } diff --git a/skiplang/compiler/src/skipNaming.sk b/skiplang/compiler/src/skipNaming.sk index 5b3b1f4cc..2c860aab1 100644 --- a/skiplang/compiler/src/skipNaming.sk +++ b/skiplang/compiler/src/skipNaming.sk @@ -3660,6 +3660,37 @@ fun expandForEachMacro( N.Bind(iteratorn, getIterator, flagged) } +fun expandEnvMacro(context: mutable SKStore.Context, varName: A.Name): N.Expr_ { + (pos, name) = varName; + 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/${pos.file.pkg_opt.default("_default")}/`, + ), + ); + value = pkg_env.maybeGet(context, SKStore.SID(name)) match { + | None() -> SkipError.error(pos, `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( + pos.file.pkg_opt, + cur_env_map.maybeGet(pos.file.pkg_opt).default(SortedSet[]).set(name), + ), + ), + ); + N.Literal(A.StringLiteral(value)) +} + fun reportDuplicateMacro( pos: FileRange, id: String, @@ -3816,9 +3847,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), From 02eb83990c8bed2f1318188db510a5545354e518 Mon Sep 17 00:00:00 2001 From: Lucas Hosseini Date: Tue, 8 Jul 2025 16:14:56 +0200 Subject: [PATCH 3/3] [skiplang/skc] Fix `#env()` variable invalidation. --- skiplang/compiler/src/SkipParser.sk | 15 +++++++- skiplang/compiler/src/compile.sk | 41 ++++++++------------ skiplang/compiler/src/skfmt.sk | 2 +- skiplang/compiler/src/skipMain.sk | 34 +++++++++++++++-- skiplang/compiler/src/skipNaming.sk | 27 ++----------- skiplang/compiler/src/skipParse.sk | 59 +++++++++++++++++++++-------- 6 files changed, 108 insertions(+), 70 deletions(-) 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/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/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 2c860aab1..8edadce28 100644 --- a/skiplang/compiler/src/skipNaming.sk +++ b/skiplang/compiler/src/skipNaming.sk @@ -3662,32 +3662,13 @@ fun expandForEachMacro( fun expandEnvMacro(context: mutable SKStore.Context, varName: A.Name): N.Expr_ { (pos, name) = varName; - 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/${pos.file.pkg_opt.default("_default")}/`, - ), - ); - value = pkg_env.maybeGet(context, SKStore.SID(name)) match { + 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 }; - // 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( - pos.file.pkg_opt, - cur_env_map.maybeGet(pos.file.pkg_opt).default(SortedSet[]).set(name), - ), - ), - ); N.Literal(A.StringLiteral(value)) } 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(