This is to try and outline some of my ideas of how mod (modules) and pkg (packages) work together and are integrated into the build system.
A mod is a sub-class of TyDict which can have constants, functions, etc. It can also act as a "syn type" in that it can be used to dynamically create types depending on some arguments Arr[20, MyType]. Basically it is something the compiler itself understands and uses directly in the syntax of fngi.
Source files can add modules, modify modules, etc by using with syn function to affect "one token", or filewith:mod syn function at the top will make the whole file that module.
pkg is a different concept -- fundamentally it is just a config file which directs the compiler on how to compile a set of files. It is technically a fngi file, but anything defined in it does not affect anything outside the following global variables:
deps: a list of dependencies + metadata (i.e. version)
srcs: a list of source files
data: a list of data files to be accessible during compilation
A typical .fnc (fngi config) for a pkg might look like (recall that fngi string syntax is |hello world|):
\ deps { ... } must be the first token.
\ It has it's own syntax that constructs Pkg objects
deps {
|regex| \ same as: Pkg { name = |regex| }
|zoa|
Pkg { path = |subPkg/pkg.fsc| } \ local package
\ pkgs accessible at build-time can be specified by
\ setting build=true or buildOnly=true
Pkg { name = |fsutil|, buildOnly=true }
Pkg { path = |buildHelper.fsc|, buildOnly=true }
\ '+' can be used to access values or even call functions defined
\ within build-time dependencies. The output must be
\ an &Slc[Pkg].
\ The + will not be accessed until all previous deps steps are complete
+ buildHelper.extraDeps
}
var _srcs: Arr[CStr] = {
|types.fn|,
|firstThing.fn|,
|secondThing.fn|,
};
_srcs.extend(buildHelper.otherSrcs);
\ export srcs by making all src paths start with "src/"
var srcs: Arr(Slc) = fsutil.joinall(|src|, &_srcs);
\ Regular expressions of raw data files to include
var data: Arr(Slc) = fs.list(|data/.*|)
The compiler will construct the dependency tree and compile each package according to the DAG it creates. Once all the deps (and their deps, recursively) have been compiled it compiles the srcsin order.
Needless to say, this is a radical departure from how many programming languages handle their imports. Most have elaborate processes for how to discover dependencies. C gives an extraordinarily brute-force solution and simply expands all macros and imports for every source file. Like many other features of compiler design, fngi eschews these common practices in favor of the simplest possible approach -- simply compile the files in the order the programmer specifies. No magic. Fngi has to do this since all things in fngi are compiled immediately as they are discovered -- but regardless, it also makes things much simpler.
Because of this:
aliases (aka imports) defined in a module don't need to be re-defined at the top of every file. You can have one base-file that defines the types and imports you need and then use them freely.
the directory structure is whatever the user wants, although using sub-modules for sub-directories will probably help with understand ability.
limitations
pkg's cannot create files -- they only read files and define state for building the pkg. Generating files / etc is the job of some other build system (i.e. make, bazel, scripts, etc) which civboot will define independent of fngi pkgs.
This is to try and outline some of my ideas of how mod (modules) and pkg (packages) work together and are integrated into the build system.
A mod is a sub-class of TyDict which can have constants, functions, etc. It can also act as a "syn type" in that it can be used to dynamically create types depending on some arguments
Arr[20, MyType]
. Basically it is something the compiler itself understands and uses directly in the syntax of fngi.Source files can add modules, modify modules, etc by using
with
syn function to affect "one token", orfilewith:mod
syn function at the top will make the whole file that module.pkg is a different concept -- fundamentally it is just a config file which directs the compiler on how to compile a set of files. It is technically a fngi file, but anything defined in it does not affect anything outside the following global variables:
deps
: a list of dependencies + metadata (i.e. version)srcs
: a list of source filesdata
: a list of data files to be accessible during compilationA typical
.fnc
(fngi config) for a pkg might look like (recall that fngi string syntax is|hello world|
):The compiler will construct the dependency tree and compile each package according to the DAG it creates. Once all the deps (and their deps, recursively) have been compiled it compiles the
srcs
in order.Needless to say, this is a radical departure from how many programming languages handle their imports. Most have elaborate processes for how to discover dependencies. C gives an extraordinarily brute-force solution and simply expands all macros and imports for every source file. Like many other features of compiler design, fngi eschews these common practices in favor of the simplest possible approach -- simply compile the files in the order the programmer specifies. No magic. Fngi has to do this since all things in fngi are compiled immediately as they are discovered -- but regardless, it also makes things much simpler.
Because of this:
limitations
pkg's cannot create files -- they only read files and define state for building the pkg. Generating files / etc is the job of some other build system (i.e. make, bazel, scripts, etc) which civboot will define independent of fngi pkgs.