Introduction
ASH scripts can import other ASH scripts. As implemented, conceptually, the imported file is textually inserted at the import statement. This can cause name clashes: if multiple scripts define identically named types, variables, or functions, you end up with compile errors.
I've been considering how to add "namespaces" to ASH: a way for scripts to define types, variables, and functions which do not cause name clashes when the script is imported.
We have the infrastructure: every "scope" - a block of code within {} - has its own symbol table of types, variables, and functions.
For example, a function has a scope:
Code:
void myFunc()
{
typdef type1 ...;
record record1 ...;
int var1 ...;
void func1()
{
...
}
...code using the above...
}
None of the "local" symbols are visible or usable outside of the function.
I do this All The Time.
What if we could assign a name to a scope and reference the "local" symbols by referencing that name?
That's what we'll investigate here.
Script Scopes
Every script has an implicit "global" scope: all types, variables, and functions within it are part of an unnamed scope.
(There is also an implicit "static scope" intended primarily for variables - whose names are added to the global scope - but who are initialized only once.)
Imported files, as implemented, add their symbols to the global scope of the script that imported them.
The structure of an ASH script:
Code:
script "name";
notify "user";
since rNNNNN;
import "lib1.ash";
import "dir/lib2.ash";
....code that can use everything imported, since it has been added to the global scope...
Note that imported files have their own scope - which we save and associate with the file name.
Imported files can import other files - and if they import a previously imported file, they get the same scope.
Note also that you can import multiple scripts with a "main" function - and that is the only point of the "script" directive: you can call main@script(...) in the outer script.
What if we called a "namespace" a package, and could define one like this:
Code:
package package1
{
typdef type1 ...;
record type2 ...;
int var1 ...;
void func1()
{
...
}
...code using the above...
}
and any script that has access to package1 could use the local symbols via
package1.type1
, package1.rec1
, package1.var1
, package1.func1
, etc.?Lexically nested packages could be a thing, but have some issues I'll discuss later, but consider this:
Code:
package name;
Would associate a name to the script's global scope and importing that script would not add its symbols to its own global scope.
Voila! No name clashes.
Example Script
Let's consider how this might look for a complicated package of scripts.
Let's call it, for the sake of argument, "autoscend".
(I know literally nothing about how the actual script of that name is structured.)
Code:
scripts/autoscend.ash
scripts/autoscend/adventuring.ash
scripts/autoscend/combat.ash
scripts/autoscend/consumption.ash
scripts/autoscend/utils.ash
autoscend.ash:
Code:
package autoscend;
import <autoscend/adventuring.ash>
import <autoscend/combat.ash>
import <autoscend/consumption.ash>
import <autoscend/utils.ash>
void func1();
autoscend/adventuring.ash:
Code:
package autoscend.adventuring;
import <autoscend/utils.ash>
void func1();
autoscend/utils.ash:
Code:
package autoscend.utils;
void func1();
And so on.
autoscend.ash could call
func1()
, adventuring.func1()
and utils.func1()
with no conflict.adventuring.ash could call
func1()
and utils.func1()
with no conflict.If you had some sort of "wrapper" script and wanted to import autoscend's utility package:
Code:
import <autoscend/utils.ash>
Since you are not in the "autoscend" package, you'd have to call
autoscend.utils.func1()
And if, gawds forbid, you wanted to import all of autoscend, you could call
autoscend.main(...)
, whether or not autoscend used a "program" directive.Nested Packages
What if we actually implemented a "package" keyword and allowed a package to be nested within another script?
If you wanted a local package which had access to types, variables and functions of the enclosing script, it could be useful, although I wouldn't necessarily do that as an alternative to function-local types, variables, and functions.
But here we run into the fact that ASH is a single pass compiler, and where do you put the local package?
We could allow something like:
Code:
package local;
int local.func1();
Alternatively, soup up the ASH Parser to, somehow, not require forward declarations at all (like Java), possibly by pre-parsing types, variables, function signatures, and automatically updating them when the package is declared. That's a project of its own.
Your turn!
Thoughts?