Skip to content

Starting a new filesystem/filesystem-internal library #1780

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jun 5, 2025
Merged
27 changes: 26 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"ascii-table": "^0.0.9",
"benchmark": "^2.1.4",
"browserify": "^17.0.1",
"buffer": "^6.0.3",
"canvas": "^3.1.0",
"command-line-args": "^3.0.5",
"command-line-usage": "^4.0.1",
Expand Down
72 changes: 35 additions & 37 deletions src/arr/compiler/cli-module-loader.arr
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@ import load-lib as L
import either as E
import json as JSON
import ast as A
import pathlib as P
import sha as crypto
import string-dict as SD
import render-error-display as RED
import filesystem as Filesystem
import file as F
import filelib as FS
import error as ERR
import system as SYS
import file("js-ast.arr") as J
Expand Down Expand Up @@ -100,21 +99,21 @@ end
# with the compiled version of the file.

fun cached-available(basedir, uri, name, modified-time) -> Option<CachedType>:
saved-path = P.join(basedir, uri-to-path(uri, name))
saved-path = Filesystem.join(basedir, uri-to-path(uri, name))

if (F.file-exists(saved-path + "-static.js") and
(F.file-times(saved-path + "-static.js").mtime > modified-time)):
if (Filesystem.exists(saved-path + "-static.js") and
(Filesystem.stat(saved-path + "-static.js").mtime > modified-time)):
some(split)
else if (F.file-exists(saved-path + ".js") and
(F.file-times(saved-path + ".js").mtime > modified-time)):
else if (Filesystem.exists(saved-path + ".js") and
(Filesystem.stat(saved-path + ".js").mtime > modified-time)):
some(single-file)
else:
none
end
end

fun get-cached(basedir, uri, name, cache-type):
saved-path = P.join(basedir, uri-to-path(uri, name))
saved-path = Filesystem.join(basedir, uri-to-path(uri, name))
{static-path; module-path} = cases(CachedType) cache-type:
# NOTE(joe): leaving off .js because builtin-raw-locator below
# expects no extension
Expand All @@ -127,7 +126,6 @@ fun get-cached(basedir, uri, name, cache-type):
method needs-compile(_, _): false end,
method get-modified-time(self):
0
# F.file-times(static-path + ".js").mtime
end,
method get-options(self, options):
options.{ checks: "none" }
Expand Down Expand Up @@ -163,7 +161,7 @@ fun get-cached(basedir, uri, name, cache-type):
modules: raw-array-to-list(raw.get-raw-module-provides())
})
some(CL.module-as-string(provs, CS.no-builtins, CS.computed-none,
CS.ok(JSP.ccp-file(F.real-path(module-path + ".js")))))
CS.ok(JSP.ccp-file(Filesystem.resolve(module-path + ".js")))))
end,

method _equals(self, other, req-eq):
Expand All @@ -176,7 +174,7 @@ fun get-cached-if-available(basedir, loc) block:
get-cached-if-available-known-mtimes(basedir, loc, [SD.string-dict:])
end
fun get-cached-if-available-known-mtimes(basedir, loc, max-dep-times) block:
saved-path = P.join(basedir, uri-to-path(loc.uri(), loc.name()))
saved-path = Filesystem.join(basedir, uri-to-path(loc.uri(), loc.name()))
dependency-based-mtime =
if max-dep-times.has-key(loc.uri()): max-dep-times.get-value(loc.uri())
else: loc.get-modified-time()
Expand Down Expand Up @@ -237,7 +235,7 @@ fun get-loadable(basedir, read-only-basedirs, l, max-dep-times) -> Option<Loadab
| none => none
| some(found-basedir) =>
c = cached-available(found-basedir, l.locator.uri(), l.locator.name(), max-dep-times.get-value(locuri))
saved-path = P.join(found-basedir, uri-to-path(locuri, l.locator.name()))
saved-path = Filesystem.join(found-basedir, uri-to-path(locuri, l.locator.name()))
{static-path; module-path} = cases(CachedType) c.or-else(single-file):
| split =>
{saved-path + "-static"; saved-path + "-module.js"}
Expand All @@ -258,14 +256,14 @@ end

fun set-loadable(basedir, locator, loadable) -> String block:
doc: "Returns the module path of the cached file"
when not(FS.exists(basedir)):
FS.create-dir(basedir)
when not(Filesystem.exists(basedir)):
Filesystem.create-dir(basedir)
end
locuri = loadable.provides.from-uri
cases(CS.CompileResult) loadable.result-printer block:
| ok(ccp) =>
save-static-path = P.join(basedir, uri-to-path(locuri, locator.name()) + "-static.js")
save-module-path = P.join(basedir, uri-to-path(locuri, locator.name()) + "-module.js")
save-static-path = Filesystem.join(basedir, uri-to-path(locuri, locator.name()) + "-static.js")
save-module-path = Filesystem.join(basedir, uri-to-path(locuri, locator.name()) + "-module.js")
fs = F.output-file(save-static-path, false)
fm = F.output-file(save-module-path, false)

Expand Down Expand Up @@ -302,18 +300,18 @@ type CLIContext = {
}

fun get-real-path(current-load-path :: String, this-path :: String):
if P.is-absolute(this-path):
P.relative(current-load-path, this-path)
if Filesystem.is-absolute(this-path):
Filesystem.relative(current-load-path, this-path)
else:
P.join(current-load-path, this-path)
Filesystem.join(current-load-path, this-path)
end
end

fun locate-file(ctxt :: CLIContext, rel-path :: String):
clp = ctxt.current-load-path
real-path = get-real-path(clp, rel-path)
new-context = ctxt.{current-load-path: P.dirname(real-path)}
if F.file-exists(real-path):
new-context = ctxt.{current-load-path: Filesystem.dirname(real-path)}
if Filesystem.exists(real-path):
some(CL.located(get-file-locator(ctxt.cache-base-dir, real-path), new-context))
else:
none
Expand Down Expand Up @@ -361,16 +359,16 @@ fun module-finder(ctxt :: CLIContext, dep :: CS.Dependency):
else if protocol == "file-no-cache":
clp = ctxt.current-load-path
real-path = get-real-path(clp, args.get(0))
new-context = ctxt.{current-load-path: P.dirname(real-path)}
if F.file-exists(real-path):
new-context = ctxt.{current-load-path: Filesystem.dirname(real-path)}
if Filesystem.exists(real-path):
CL.located(FL.file-locator(real-path, CS.standard-globals), new-context)
else:
raise("Cannot find import " + torepr(dep))
end
else if protocol == "js-file":
clp = ctxt.current-load-path
real-path = get-real-path(clp, args.get(0))
new-context = ctxt.{current-load-path: P.dirname(real-path)}
new-context = ctxt.{current-load-path: Filesystem.dirname(real-path)}
locator = JSF.make-jsfile-locator(real-path)
CL.located(locator, new-context)
else:
Expand All @@ -382,25 +380,25 @@ fun module-finder(ctxt :: CLIContext, dep :: CS.Dependency):
end

default-start-context = {
current-load-path: P.resolve("./"),
cache-base-dir: P.resolve("./compiled"),
current-load-path: Filesystem.resolve("./"),
cache-base-dir: Filesystem.resolve("./compiled"),
compiled-read-only-dirs: empty,
url-file-mode: CS.all-remote
}

default-test-context = {
current-load-path: P.resolve("./"),
cache-base-dir: P.resolve("./tests/compiled"),
current-load-path: Filesystem.resolve("./"),
cache-base-dir: Filesystem.resolve("./tests/compiled"),
compiled-read-only-dirs: empty,
url-file-mode: CS.all-remote
}

fun compile(path, options):
base-module = CS.dependency("file", [list: path])
base = module-finder({
current-load-path: P.resolve(options.base-dir),
current-load-path: Filesystem.resolve(options.base-dir),
cache-base-dir: options.compiled-cache,
compiled-read-only-dirs: options.compiled-read-only.map(P.resolve),
compiled-read-only-dirs: options.compiled-read-only.map(Filesystem.resolve),
url-file-mode: options.url-file-mode
}, base-module)
wl = CL.compile-worklist(module-finder, base.locator, base.context)
Expand Down Expand Up @@ -463,9 +461,9 @@ fun build-program(path, options, stats) block:
print-progress(str)
base-module = CS.dependency("file", [list: path])
base = module-finder({
current-load-path: P.resolve(options.base-dir),
current-load-path: Filesystem.resolve(options.base-dir),
cache-base-dir: options.compiled-cache,
compiled-read-only-dirs: options.compiled-read-only.map(P.resolve),
compiled-read-only-dirs: options.compiled-read-only.map(Filesystem.resolve),
url-file-mode: options.url-file-mode
}, base-module)
clear-and-print("Compiling worklist...")
Expand All @@ -479,7 +477,7 @@ fun build-program(path, options, stats) block:

clear-and-print("Loading existing compiled modules...")

starter-modules = CL.modules-from-worklist(wl, get-loadable(options.compiled-cache, options.compiled-read-only.map(P.resolve), _, _))
starter-modules = CL.modules-from-worklist(wl, get-loadable(options.compiled-cache, options.compiled-read-only.map(Filesystem.resolve), _, _))

cached-modules = starter-modules.count-now()
total-modules = wl.length() - cached-modules
Expand Down Expand Up @@ -529,7 +527,7 @@ end

fun build-runnable-standalone(path, require-config-path, outfile, options) block:
stats = SD.make-mutable-string-dict()
config = JSON.read-json(F.file-to-string(require-config-path)).dict.unfreeze()
config = JSON.read-json(Filesystem.read-file-string(require-config-path)).dict.unfreeze()
cases(Option) config.get-now("typable-builtins"):
| none => nothing
| some(tb) =>
Expand All @@ -544,11 +542,11 @@ fun build-runnable-standalone(path, require-config-path, outfile, options) block
| left(problems) =>
handle-compilation-errors(problems, options)
| right(program) =>
shadow require-config-path = if not( P.is-absolute( require-config-path ) ):
P.resolve(P.join(options.base-dir, require-config-path))
shadow require-config-path = if not( Filesystem.is-absolute( require-config-path ) ):
Filesystem.resolve(Filesystem.join(options.base-dir, require-config-path))
else: require-config-path
end
config.set-now("out", JSON.j-str(P.resolve(P.join(options.base-dir, outfile))))
config.set-now("out", JSON.j-str(Filesystem.resolve(Filesystem.join(options.base-dir, outfile))))
when not(config.has-key-now("baseUrl")):
config.set-now("baseUrl", JSON.j-str(options.compiled-cache))
end
Expand Down
36 changes: 18 additions & 18 deletions src/arr/compiler/locators/builtin.arr
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
provide *
import builtin-modules as B
import string-dict as SD
import file as F
import filesystem as FS
import pathlib as P
import parse-pyret as PP
import file("../compile-lib.arr") as CL
Expand Down Expand Up @@ -51,12 +51,12 @@ fun set-typable-builtins(uris :: List<String>):
end

fun make-builtin-js-locator(basedir, builtin-name):
raw = B.builtin-raw-locator(P.join(basedir, builtin-name))
raw = B.builtin-raw-locator(FS.join(basedir, builtin-name))
{
method needs-compile(_, _): false end,
method get-uncached(_): none end,
method get-modified-time(self):
F.file-times(P.join(basedir, builtin-name + ".js")).mtime
FS.stat(FS.join(basedir, builtin-name + ".js")).mtime
end,
method get-options(self, options):
options.{ check-mode: false, type-check: false }
Expand Down Expand Up @@ -92,7 +92,7 @@ fun make-builtin-js-locator(basedir, builtin-name):
datatypes: raw-array-to-list(raw.get-raw-datatype-provides())
})
some(CL.module-as-string(provs, CM.no-builtins, CM.computed-none,
CM.ok(JSP.ccp-file(P.join(basedir, builtin-name + ".js")))))
CM.ok(JSP.ccp-file(FS.join(basedir, builtin-name + ".js")))))
end,

method _equals(self, other, req-eq):
Expand All @@ -102,11 +102,11 @@ fun make-builtin-js-locator(basedir, builtin-name):
end

fun make-builtin-arr-locator(basedir, builtin-name):
path = P.join(basedir, builtin-name + ".arr")
path = FS.join(basedir, builtin-name + ".arr")
var ast = nothing
{
method get-modified-time(self):
F.file-times(path).mtime
FS.stat(path).mtime
end,
method get-uncached(_): none end,
method get-options(self, options):
Expand All @@ -115,10 +115,10 @@ fun make-builtin-arr-locator(basedir, builtin-name):
end,
method get-module(self) block:
when ast == nothing block:
when not(F.file-exists(path)):
when not(FS.exists(path)):
raise("File " + path + " does not exist")
end
ast := CL.pyret-ast(PP.surface-parse(F.file-to-string(path), self.uri()))
ast := CL.pyret-ast(PP.surface-parse(FS.read-file-string(path), self.uri()))
end
ast
end,
Expand All @@ -142,25 +142,25 @@ fun make-builtin-arr-locator(basedir, builtin-name):
# does not handle provides from dependencies currently
# NOTE(joe): Until we serialize provides correctly, just return false here
cpath = path + ".js"
if F.file-exists(path) and F.file-exists(cpath):
stimes = F.file-times(path)
ctimes = F.file-times(cpath)
if FS.exists(path) and FS.exists(cpath):
stimes = FS.stat(path)
ctimes = FS.stat(cpath)
ctimes.mtime <= stimes.mtime
else:
true
end
end,
method get-compiled(self):
cpath = path + ".js"
if F.file-exists(path) and F.file-exists(cpath):
if FS.exists(path) and FS.exists(cpath):
# NOTE(joe):
# Since we're not explicitly acquiring locks on files, there is a race
# condition in the next few lines – a user could potentially delete or
# overwrite the original file for the source while this method is
# running. We can explicitly open and lock files with appropriate
# APIs to mitigate this in the happy, sunny future.
stimes = F.file-times(path)
ctimes = F.file-times(cpath)
stimes = FS.stat(path)
ctimes = FS.stat(cpath)
if ctimes.mtime > stimes.mtime:
raw = B.builtin-raw-locator(path)
provs = convert-provides(self.uri(), {
Expand All @@ -185,16 +185,16 @@ end

fun maybe-make-builtin-locator(builtin-name :: String) -> Option<CL.Locator> block:
matching-arr-files = for map(p from builtin-arr-dirs):
full-path = P.join(p, builtin-name + ".arr")
if F.file-exists(full-path):
full-path = FS.join(p, builtin-name + ".arr")
if FS.exists(full-path):
some(full-path)
else:
none
end
end.filter(is-some).map(_.value)
matching-js-files = for map(p from builtin-js-dirs):
full-path = P.join(p, builtin-name + ".js")
if F.file-exists(full-path):
full-path = FS.join(p, builtin-name + ".js")
if FS.exists(full-path):
some(full-path)
else:
none
Expand Down
Loading