Skip to content

Instantly share code, notes, and snippets.

@sogaiu
Last active December 8, 2025 04:31
Show Gist options
  • Select an option

  • Save sogaiu/36d04c3f33dbc56461bcd16fbfe57b73 to your computer and use it in GitHub Desktop.

Select an option

Save sogaiu/36d04c3f33dbc56461bcd16fbfe57b73 to your computer and use it in GitHub Desktop.
* code to help with "compiling" / "cat-ing" multiple files into a
self-contained single script
* develop with files split, but "compile" to single script
* "include" files that have their "effectively" top-level
definition names (and uses) renamed to use `<ns>/` as a prefix.
more detail elsewhere in document.
* remove certain import forms from "main" script
* rationale
* single file can be copied and used more easily as a script or
library
* easier to "vendor" into projects
* simple version of "stages" idea
* prepare
given a starting file:
0. perform some checking of starting file
1. identify all relevant input files and the order in which their
modified counterparts need to be concatenated in the next stage
2. transform each one appropriately for the next stage
X. check transformed content a bit
step 0. might be done by running a linter on the starting file
before proceeding, but also may be run existing tests. if either
turn up issues, likely it's better to address those before
proceeding further.
step 1. is accomplished by a traversal that begins at a starting
file. `import` forms are analyzed to determine other files which
are "followed into" and the process continues recursively. as a
side effect, this step also identifies the order in which the
modified content should be concatenated together in the next
"assemble" stage.
step 2. is accomplished by first associating a unique prefix to
each file and then producing modified versions of the input files
determined in the previous step.
the modifications consist of:
* appropriately changing names in each of the input files. this
is done by creating new names by prepending prefixes to relevant
existing names.
* modifying the associated `import` forms to refer to the
corresponding prefixes
typically only some names in each file need to be changed
(e.g. local names are typically left alone). relevant locations
for changing a given name include definition "site" and all use
"sites" (e.g. calls or passing as arguments).
step X. might be done by running a linter across the products. if
the comment-form style testing method is in use, those tests ought
to have been transformed appropriately as well so running them to
check on behavior might be a good idea too.
note that the transformed versions of the input files should still
function together and produce the same results as for the original
input files. well, mostly -- excepting things like self-analyzing
and other such self-referential bits.
* assemble
given transformed input files from previous stage, produce final
product out of the inputs:
1. concatenate together the modified input files' contents (from
the previous stage) in an appropriate order
2. remove or comment out each of the pre-existing import forms
X. check final product a bit
step X. might be done in a manner similar to that of the previous
prepare stage, i.e. running a linter and any included comment-form
tests.
* on the necessity of renaming certain names and the terms / phrases
"prefixing", "external names", "internal names", and "effectively
top-level"
* since content from multiple files is being brought together into a
single file, renaming is needed to avoid "collisions" between
instances of certain names being used differently among two or
more files, e.g.
file x:
(def a 1)
file y:
(def a 2)
combined file:
(def a 1)
(def a 2) # oops
"prefixing" of names with `<something>/` is one way of renaming,
e.g.
combined file:
(def i/a 1)
(def j/a 2) # no collision now
prefixing can be done using characters other than slash,
e.g. `<something>^` seems like it could work too.
combined file:
(def i^a 1)
(def j^a 2) # no collision now
postfixing might be another possibility.
combined file:
(def a^i 1)
(def a^j 2) # no collision now
fwiw, prefixing with slash is already what happens when janet
applies `import`, e.g. suppose there's a file with content:
# assume there is a file `my-fun.janet` in the same directory
(import ./my-fun :as mf)
then public top-level definitions from `my-fun.janet` will be
available but via names that begin with `mf/`. so if
`my-fun.janet` has content:
(def j 8)
then within the file with the `import` form from above (assuming
no other arrangements), `mf/j` will evaluate to `8`.
* for a given name which requires renaming, it needs to be renamed
where it is defined as well as where it is used, e.g.
before renaming:
(defn f [x]
(if (zero? x)
0
(+ 1 (f (dec x)))))
(f 2)
after renaming:
(defn a^f [x]
(if (zero? x)
0
(+ 1 (a^f (dec x)))))
(a^f 2)
* there are at least three sorts of names that may need to be
renamed.
only one is syntactically "top-level", so the phrase "effectively
top-level" will be used to mean all three:
* plain top-level definitions, e.g. `a` and `main` in:
(def a 8)
(defn main
[& args]
(print a))
* names that are defined within the "top-level" of a use of
`comment`, e.g. `a` in:
(comment
# top-level within `comment`
(def a 1)
# top-level within `comment`
a
# =>
1
)
the reason this type of name is being considered is to support a
certain type of testing convention.
* names that are defined within the "top-level" of a use of
`upscope`, e.g. `b` in:
(upscope
# top-level within `upscope`;
(def b 2)
)
# `b` can then be used as if defined at the syntactic
# top-level
(pp (inc b))
note that `compwhen` also ends up using `upscope`. what about
`compif` and `comptime`?
* there are other names that may not need to be renamed from the
perspective of "merging" file content together. some of these
include:
* names introduced via parameter tuples, e.g.
(defn my-fn
[a-name b-name]
8)
`a-name` and `b-name` are local to `my-fn` and thus should not
be affected by "merging".
* non-top-level names introduced by `def`, `let`, etc., e.g.
(defn my-other-fn
[x y]
(def a 1)
(let [b 2]
(+ a b)))
`a` and `b` are local to `my-other-fn` and thus should not
be affected by "merging".
* are there any other kinds of names that would be unaffected?
* the names that need to be renamed and those that don't need to be
renamed (because of merging concerns) might overlap
(e.g. shadowing) in some cases, e.g.
(def a 1)
(defn f
[x]
(def a 2)
a)
there is a top-level `a` and a local `a` that have the same name.
from the perspective of program transformation, it would probably
make things easier if this kind of situation did not arise. if
practical, it might be nice to detect if there are any instances
in the code about to be manipulated and give appropriate feedback
and/or abort processing.
* some names within a file become "available" via the use of a
construct like `import`. these will be referred to as "external
names". the names within a file which are defined within the file
will be called "internal names". the distinction is made partly
to improve thinking and communicating about renaming.
for a given file, renaming the internal names involves all three
types of effectively top-level names. for external names, only
the plain top-level and upscope types of top-level names are the
target of renaming, i.e. effectively top-level names within
comment forms from another file aren't supposed to be visible
outisde of the files they are defined in.
* constructs that introduce names apart from the effectively
top-level things mentioned elsewhere
* parameter tuples
* straight-forward macros
* single name that is easy to detect
* label, with, when-with, if-with, forv, for, as->, as?->,
ffi/defbind-alias, ffi/defbind
* possibly multiple names, but no destructuring
* catch
* possiblly multiple names that might get complicated because of
destructuring
* each, eachk, eachp, let, if-let, when-let
* complicated (but all the same?)
* loop, seq, catseq, tabseq, generate
* most complicated?
* match
* user-defined macros
* provide hooks for handling these - definitely not at first
* this might allow handling of things like path.janet
eventually
* other things which are not relevant?
* names introduced but typically used like ,name
* with-syms
* involves keywords so not needed?
* prompt, with-dyns, with-vars
* is it practical to detect shadowing? it is related to the
"constructs that introduce names" items and as such a better
question might be "how much shadowing would be practical to
detect?".
* misc notes
* use leftmost and path for ascertaining whether in comment or
upscope or ?
* that jeat doesn't look in `( comment ...)` or `(#\ncomment ...)`
can be seen as a feature, as those can be quick ways to
disable an entire comment form worth of tests?
* organizational challenge of too many notes. perhaps making a
section of "may be later" items would help declutter a bit.
* self-extraction step for native and other (e.g. resources) bits?
* may be unprefixing can be avoided entirely by using a non-slash
(and non-problematic) separator character. ^ is not used very
much atm (according to some research). as mentioned elsewhere may
be better to allow customization.
* apply zippers for analysis and other tasks
* zipper editing does not update location information though
multiple edits appear to be supported (see modify-test.janet).
modifications should then be made after all location information
has been obtained and utilized?
* consider the idea of "normalization" (of source) being applied to
simplify later manipulation (in the context of the "stages"
mentioned elsewhere). an example is: `(import ./hello)` could be
re-expressed as `(import ./hello :as hello)` or `(import ./hello
:prefix "hello")`. however, for this specific case, if `import`
forms are going to be commented out anyway it seems rather
pointless. normalization might be good for other situations
through.
* handy to be able to determine whether a given form is at the
top-level (or effectively top-level)?
* top-level - use j/up on a zipper created using zip-down and
check the node type. if it's :code, then the zipper was at the
top-level.
* top-level of top-level comment - j/up is a comment form and j/up
one more has node :code.
* top-level of upscope - similar to comment, but need to also
ensure the upscope is "positioned" to allow top-level
definitions to occur. for example, if at the top-level, one
evaluates `(do (upscope (def a 1)))`, this won't result in `a`
being defined at the top-level.
* handy to be able to find the first non-whitespace / non-comment
element of an indexed-type or a dictionary. right-skip-wsc
doesn't check the current location so that needs to be checked
beforehand.
* limits and questions
* there isn't yet a "study" mode that can signal the use of
constructs that are not supported (or potentially problematic).
such a mode could be used as a kind of a linter in ordinary
development to keep one's code in a shape that's more likely to be
reusable. it's likely to be a fair bit of work to create
something comprehensive all at once. it's probably worth having
some limited version at first and then gradually add to this over
time.
* only .janet files (no c). this limitation seems very unlikely to
be one that can be lifted.
* only handle input files that are siblings within the same
directory initially. related to "relative paths that are single
level" mentioned in import form limitations.
* only handling files with certain types of import forms. need to
spell out more clearly the constraints. should detect and warn.
* only relative paths that are single level (e.g. ./ok is fine but
not ./this/is/not/ok). also "directory with init.janet" types
of imports are not handled either.
* might not handle arbitrary characters in :as / :prefix values,
e.g. may want to avoid "/".
* within the same file, two import forms should not use the same
:as / :prefix value. jeat/to-test.janet has an example where
this is done.
* if there is no :as or :prefix, rewrite the form to use one or
the other (normalize?)
* :only is not handled
* :export true is not handled
* not (yet) handling code that has a top-level definition that has a
name that collides with a non-top-level definition's name (at
least in the same file). can warn about this probably via initial
scan. jipper.janet may have one of these (`make-state`).
effectively top-level?
* renaming names within macro definitions may be naive. this may
need to be examined more closely but it isn't so pressing atm
because there are no macro definitions in the code being used for
exploration.
* not (yet) handling files with `use` forms. should detect and
warn?
* not (yet) handling shadowing of definitions (even between things
within `comment` forms and outside). is there really an issue /
limitation here? warn if an effectively top-level definition
within a `comment` form "collides" with a non-comment effectively
top-level definition?
* not (yet) handling import forms that span multiple lines. can
probably use zippers to overcome this later if needed. don't
recall having seen an import form across multiple lines. could
probably detect and warn.
* not (yet) handling code that uses `upscope`. there is some
similarity with the handling of "top-level" definitions within
`comment` forms, though it's not quite the same. should detect
and warn.
* not handling certain kinds of defining, e.g. like what spork's
path.janet does. the symbol renaming that might be needed seems
impractical? path.janet has defmacros that contain defs and
defns. the macros are used later in the file. possibly could
detect and warn?
* no effectively top-level destructuring defines (don't do this in
comment blocks either if the comment blocks contain tests). could
probably support this eventually. probably could warn via an
initial scan? possibly support very simple things first such as
flat tuples?
* the first non-whitespace / non-comment "thing" after a defining
symbol (e.g. def, def-, defn, etc.) is one of:
* symbol - most common
* tuple or array - destructuring
* struct or table - destructuring
the content of the indexed type / dictionary can be arbitrarily
nested though it rarely is something more than a single level?
can probably do a "query" to assess this.
* no handling of defglobal or varglobal. they don't come up much so
not much of an issue? detect?
* no handling of defdyn?
* stages (seems like there are three at this point)
* "analysis" - determine if some preconditions are not met and/or
warn of use of certain constructs? would be nice to be
comprehensive in checking, but this might be quite a lot of work.
try to figure out what is essential and what might be postponed.
see limits described elsewhere for candidates of what to check for
and create list of things to check:
* running through a linter (e.g. janet -k) before attempting
anything else seems worthwhile.
* punt on warnings initially -- see may be later section
things to do in preparation for the next stage:
* identify all relevant input files (traverse import form paths
starting at the starting file?)
* identify files that have import forms in them and those that
don't. record the details (e.g. file a imports b and c using
such and such aliases).
* verify that all input files are siblings in the same
directory?
* as a side-effect of this process, the order in which files
need to be "joined" / "concatenated" in the final stage can be
identified (the traversal order).
* settle on prefix names
* one idea is to sort the list of import paths first (longest
first, then lexicographically). try successively longer
substrings of each filename. try to detect if unsuccessful.
* even simpler would be to just start at "a" and proceed up
through "z", then consider using capital letters, etc.
could eventually allow user to specify a mapping of file names
(or paths in the future?) to aliases as well.
* find definitions and uses and produce a summary?
possibly one could generate some kind of "data" that describes
all changes needed in the next ("prepare inputs") stage? may be
the following stage or stages can be all about just "realizing"
(via execution?) the described changes?
* seems non-trivial to express multiple changes because one
change might refer to location x, which one performed might
affect locations of subsequent changes.
* other things?
should "analysis" results be written out in some file that can be
used by next stage?
* "prepare inputs" - generate new files from source files such that
they are ready for the "assemble" stage.
since the content of all of the input files will end up in the
same destination file, some renaming is likely needed for names in
each of the input files. the renaming should be such that for
each relevant name:
1. the new name should end up referring to what the old name
referred to before the renaming, and
2. the new name should not "collide" with any other name from
input files (other ones as well as ones from the file the
original name was from)
for point 1, each effectively top-level defintion needs to change
as well as each use of such names.
for point 2, in a previous stage appropriate prefixes should have
been generated for each input file so this prefix (+ a separator
character) can be prepended to each name to avoid collisions.
* two kinds of files to operate on:
* no import forms
* prefix all:
* effectively top-level definition names and
* associated uses determined for this file
there should be no effectively top-level definitions that
have prefixed names based on having passed the previous
stage.
* there are three types of effectively top-level
definitions, but initially only handle plain and those
within `comment`. finding these two types seems
straight-forward. for each found name record:
* name - used for identifying what to rename (but also
whether it collides with an existing definition?)
* location - may matter when figuring out what counts as a
use. only later locations in the file count as uses?
* def type (e.g. def, defn, etc.) - not sure if this is
needed, but store anyway?
* finding uses of top-level definitions seems
straight-forward (as long as there is no clashing /
shadowing going on). may be a simple depth-first search
through the whole tree is sufficient? though is it
possible that some symbols found this way could be false
positives? yes, if someone has used the name of a
callable as the name of a variable, for example
(i.e. shadowing). might be other cases too.
when looking for uses of effectively top-level definition
names within `comment` forms, only forms within `comment`
forms should be considered. these names should not leak
"outside" of comment forms (though they can be defined in
one comment form and be used in another).
* at least one import form
* check import forms for use of :as / :prefix. use results to
identify which names in file are names from elsewhere, i.e.
"external" names. rename the determined "external" names
using the new names established via prefixing and update if
needed the corresponding import form. for example:
(import ./args :as arg)
(def b arg/fun)
might become:
(import ./args :prefix "")
(def b a/fun)
assuming "a" was chosen as the prefix for args.janet, and at
some point effectively top-level definition names in
args.janet and uses are renamed to have the prefix "a".
alternatively, rewriting using `use` might work?
(use ./args)
(def b a/fun)
may be rewrite `(import ./some-path)` as either `(import
./some-path :as some-path)` or `(import ./some-path :prefix
"some-path")`? some kind of "normalization"...
* the above bits are needed in addition to the processing
that is necessary for the "no imports" case.
after generating the new files, might be good to run tests. could
compare the number of tests in the input files with the number in
the final result. probably want the number of passed tests to
match.
* "assemble" - final stage is the recursive minimal concatenation of
the files prepared in the previous stage. i.e. basically
concatenating in the right order but only doing each "included"
file once. a preliminary implementation of this portion has been
made by(modifying judge-gen's build.janet.
* optionally the resulting file can be run through some kind of
indenter / formatter.
* might be good to run tests. could compare the total number of
tests in the input files with the number in the final result.
* optionally provide way to strip out comment blocks. this can
reduce the final size of the file but at the cost of removing
tests.
* notes on specific renaming cases
* forms like ->, ->>, etc. can contain function names in
argument positions
* similar remark for map, filter, etc.
* macro definitions may also contain things? possibly these
are harder to detect? better to examine some actual
cases:
(defmacro toggle
"..."
[value]
~(set ,value (,not ,value)))
things prefixed with `,` will be within :unquote nodes?
* any other "macro reader" things to be concerned with?
* :fn?
* :quote?
* :quasiquote?
* :splice?
* any other cases?
* observations from rewriting bin/jog to use prefixed identifiers that
reflect which file they might have originated from
* renaming some function names within ->, ->>, -?>, and friends
is more work because of things like:
(->> (map + [1 2 3])
invert)
i.e. not all function names appear as first elements of tuples.
want to handle this.
* functions being passed as arguments, like for map, filter, etc.
can probably be handled, though unclear whether trying to handle
this will accidentally rename things in cases where there is a
tuple that isn't a call...want to handle the ordinary case.
* in zipper.janet there is an internal function named make-state and
a top-level function of the same name. may be don't want to
rename the internal one. how to tell apart the uses...need to
track scope? better to have the limitation to not allow the same
name for top-level and internal things? (change code in
zipper.janet to not have this characteristic) or at least warn if
detected?
* probably do want to "prefix" "top-level" things within comment
blocks so that:
* tests from one file do not collide with test from another file
* tests run after "compilation"
* trickier to rename destructure-defined things, e.g.
(def [a b] [1 1])
as:
(def [z/a z/b] [1 1])
perhaps tuple case is relatively straight-forward? except
recursion...struct case is more complicated...may be better to
rewrite code to not use these constructs. could probably warn.
* import forms and the identifiers that use them (see
loc-jipper.janet for examples) seem not straight-forward to handle
in terms of renaming. is this referring to the content of comment
blocks?
(comment
(import ./location :as l)
(l/gen ...)
)
* zipper.janet and loc-jipper.janet are kind of weird birds...the
latter kind of redefines stuff from the former...can this be
avoided or reworked somehow to begin with (before trying to
process them with highlander)? zipper.janet and loc-jipper.janet
have been merged as jipper.janet to avoid this issue.
* possibly by warning of various issues, could support the idea of
having "patches" that are manually constructed by the author and
can be applied automatically upon each rebuild?
* after renaming, want to reindent?
* linter (janet -k) seems pretty good at finding spots that
need renaming (outside of comment blocks)
* overall approach notes
* try breaking up bin/jog into files, then make modifications until
"original" files are "recovered". take notes in the process.
this approach was followed at least for part of the overall
development activity.
* studied bin/jog's current form and compared with the files it
was built out of to see what changes were needed.
* "doctored" files that jog can be built from:
* args.janet - a
* completion.janet - c
* jipper.janet - j
* location.janet - l
* search.janet - s
* report.janet - r
* search.janet
* find.janet
* find.janet - f
* location.janet
* jipper.janet
* main.janet (don't prefix anything in this file...has main)
* args.janet
* completion.janet
* report.janet
* figure out what "doctoring" is needed to arrive at files in their
forms above. manually create versions of files that would have
existed before doctoring and then compare.
* may be later
* warnings during analysis phase:
* warn if the separator character (default ^) is in use in any
symbol name? possibly it's a non-issue. mull over.
* warn of effectively top-level definition names that collide with
non-top-level definition names or other names (in the same
file).
more generally(?) name collision / shadowing detection?
* checking names in argument tuples
* checking non-top-level definition names
* any other place that names might be introduced...
it may not be problematic for there to be a non-top-level
definition with name x preceding a top-level definition with
name x (so the jipper.janet case of `make-state` is fine).
the reverse case might be an issue though. however, it might
depend on whether all names are identified before any renaming
is attempted. perhaps better to arrange for renaming of
top-level names and their uses such that only "uses" that appear
later in the file are targets of renaming. perhaps having
available locations of definitions and uses would be a good
idea.
probably better to do the same within comment blocks too
(because of tests)?
* warn of `import` forms that might be problematic.
* simplest acceptable case is something like `(import
./some-path :as sp)` or `(import ./some-path :prefix "sp")`
* the path portion needs to start with `./`
* the path portion should not be "multi-level", e.g.
things like `./this/is/not/ok` are out-of-scope atm
* "directory with init.janet in it" type of import is not
supported either.
* so syspath, working directory, and @-prefixed imports are
not supported.
* things that probably should cause halting of processing:
* :export true (does anyone use `:export false`?)
* non-relative import paths
* "multi-level" import paths
* values for :as and :prefix which contain potentially
problematic characters (e.g. may be :prefix values should
not have slashes?)
* warn of import forms in the same file that use the same value
for :as or :prefix. note that one could be specified with :as
and another with :prefix so "cross-comparison" is needed.
* warn of top-level definitions that have names that are prefixed.
better to not support such names initially. possibly in future.
* warn of `use` forms. possibly allow in future.
* warn of top-level destructuring defines. probably better to do
the same within comment blocks too (because of tests)? may be
support in future -- because of use in tests in comment blocks.
simple destructuring may be relatively easy to support
(e.g. flat tuples), but arbitrarily recursive destructuring may
be too involved?
* warn of import forms that span multiple lines. possibly could
support in future.
* warn of code that uses ordinary tuples (i.e. () vs []) for the
parameter portion of defn, defmacro, etc. probably not worth
supporting in the future because in practice almost no one
writes code like this?
* warn of defining like is done in path.janet. not sure if this
is worth it...look for defmacros that contain def, defn,
etc. along with actually calls to such macros? quite a bit of
work to do?
* other things to warn about?
* replacing import lines with commented versions in appropriate
locations turns out to be a bit trickier than expected. an
alternative approach is to have an initial pass that comments them
out with a special marking and then look for these "special"
import lines during "compilation". not sure if it's worth it at
this point.
inserting `comment` at the beginning of an import form or putting
a single quote in front of the import tuple would work if the
import form is a top-level form.
non-top-level import forms seem like they could be more work to
handle. punt for now, but detect and warn?
* providing a way to strip out comment blocks. may want to always
keep this optional even if supported eventually because the
comment block tests being transformed along with the code can be
helfpul in some cases to detect whether the transformation broke
some of the code.
* make the single file work nicely as just a library. selectively
prefixing might have been a way to do this but it seems
complicated.
another option might be to prefix pervasively and add a bit at the
end of the file that explicitly defines some "export" names that
are not prefixed. these could be specified via configuration.
all other top-level definitions (except main?) might be made
private.
* maintain enough information within result to make "splitting"
easier later. possibly adding comments in various parts could
make splitting more tractable.
* "tree-shaking" -- by tracking which symbols are used in calls
(note: function names can be passed as parameters too!), it might
be possible to do this, but there is stuff like eval, macros,
etc. to contend with so may be not worth the trouble?
* not (yet) trying to handle unprefixing of top-level names being
defined within source files (this is different from prefixed names
that result from use of import). should detect and warn.
in the future might be nice to unprefix if possible. warn if not
possible. perhaps better to note it and avoid using the prefix
for other things?
this was an in issue in zipper.janet (used s/ prefix), but rewrote
to eliminate issue.
jeep has imports which have :as values that use slashes:
(import ./subs/install :as cmd/install)
possibly unprefixing these would be worth considering at some
point? but may be this is a slightly different issue?
possibly unprefixing overall can be avoided by choosing to use a
non-slash character instead of a slash as a separator between a
prefix and an existing name.
* considered but have not yet pursued
* make simplified sample files to help sketch out necessities?
* phases idea
* similarity to compiler phases
* start at end at work backwards to come up with phases?
* possible to have a zipper-like api on top of just an ordinary
nested data structure of janet bits? not so easy?
* name ideas
* jell (current choice)
* highlander (jaylander or jighlander)
* joust (only one remains after?)
* chimera (collection of different things)
* article (a, an, the -> one)
* single
* assimilator, absorber
* old planning notes for related idea of doing use -> import
rewrite below
# 0. parse all files to find all symbols?
# * will use in process of choosing appropriate alias...
# this might be relevant because someone might
# do `(defn my/name [] ...)` so we'd want to avoid
# choosing `my` as an alias.
#
# 1. find all definitions across all files
# * require that all relevant files be specified? or can
# the code make an attempt to manually located "installed"
# code? may not work for things defined in .c files...
# * what about things defined in .c? possibly could work
# if source is available.
#
# 2. find all `(use ...)` forms in all files
#
# 3. replace each `(use ...)` form with appropriate `(import ...)` form
# * avoid alias (i.e. `:as <name>`) collisions
#
# 4. find all uses of things brought in via `use`
# * check all symbols in file for potential matches
# * edge case when later `(use ...)` overrides earlier one.
# * try to identify potential "clashes" earlier in process,
# e.g. after enumerating all symbols across all files
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment