# Modules for Simple Goals: - 1-liner valid program (for easy tutorial/newbie starts) - - Compile and run with minimal effort - Separate compilation and namespaces; external libs - - Compile to ELF or other linkable/executable format - Easy private fields & classes - Output files are compatible with existing .o/.exe/lib/dll infrastructure - No `#include` or `import` files - Structure / file system heirarchy per module - - Perhaps many files make a module - - easy namespaces within a module - - Nested names in parent file or nested file - Class initializers guarenteed: - - execute exactly once - - lazily on first touch - - complete on or before first instances are made - - - (an exception is allowed for a golden instance in ``) --- ### Goal: Valid 1-liner, minimal effort This makes a module and a class `hello` out of the current working directory name. Compiling without more arguments also links and executes. File: `hello.smp` ``` sys.io.p("Hello World!"); ``` Compile and run: ``` > smp hello.smp Hello World! ``` Module and class name is `hello` extracted from file name, compiled in memory and `exec'd`. No files made, this is strictly a run and gun mode. --- ### Goal: separate compilation Name a build directory target during compilation to get `.o` files. The build target directory structure will match the source, and can be placed anywhere. As before, since no module name is mentioned on the compile line, `Pet` becomes both the module and a class. File: Pet.smp ``` // Class initializer is any code outside a struct declaration // and is guaranteed to run-once on first touch. // static fields listed here: int default_leggies = 4; // A default (no argument) is code inside struct with same name as the // file; this is the same behavior as field initializers for Java, although // any code is allowed, not just expressions. struct Pet { str name; int leggies = default_leggies; } float yet_another_static_field; ``` Compile to `Pet.o` in the `build/` directory: ``` > smp Pet.smp -o build/ > ls build Pet.o ``` --- ### Goal: nested namespaces This makes a module and class `Pet` based on the file name in `myRepo`. It also makes a nested class `Pet.Vet`. These are used from a different module `Owner`. Directory: `~/myRepo/` ``` ~/myRepo/ Pet.smp build/ ~/otherRepo/ Owner.smp ``` File: `myRepo/Pet.smp` ``` int default_leggies = 4; struct Pet { str name; int leggies = default_leggies; } // Nested class Vet inside of file Pet struct Vet { str name; str address; long credit_card_num; } ``` File: `otherRepo/Owner.smp` ``` Pet dog = Pet{name="Fido"}; Pet snake = Pet{name="slither", leggies = 0; }; Pet.Vet vet = Pet.Vet{name="Linda"; address="101 Main St"; credit_card_num=1234}; sys.io.p("I took "+dog.name+" to "+vet.name+"\n"); ``` Compile to `Pet.o` in the `build/` directory; since the compile line does not mention some other module root, `Pet` must be the module (and is also a class). Since `Pet` is the module, `Vet` is a class within the module `Pet`. ``` > smp Pet.smp -o build/ > ls build Pet.o ``` The code for both `Pet` and `Pet.Vet` are in the same `Pet.o` file. Compile and run `Owner.smp`, specifying the `Pet` repo to satisfy the free variables `Pet` and `Pet.Vet`. ``` > smp Owner.smp -lib ~/myRepo/build I took Fido to Linda ``` --- ### Goal: nested namespace in a nested file Same as the last example but with nested files: ``` ~/myRepo Pet.smp Pet/ Vet.smp build/ Pet.o Pet/ Vet.o ~/otherRepo Owner.smp ``` File: Pet.smp ``` int default_leggies = 4; struct Pet { str name; int leggies = default_leggies; } ``` File: Pet/Vet.smp ``` // Nested class Vet inside of directory Pet struct Vet { str name; str address; long credit_card_num; } ``` Compile and run `Owner.smp`: ``` > smp Owner.smp -lib ~/myRepo/build I took Fido to Linda ``` --- ### Goal: Class Initializers are Lazy ``` Parent.smp Parent/ ChildA.smp ChildB.smp ``` File: `Parent.smp` ``` sys.io.p("In Parent "); sys.io.p(ChildB.str); ``` File: `ChildA.smp` ``` // for ChildA sys.io.p("In ChildA "); str str = "A"; ``` File: `ChildB.smp` ``` // for ChildB sys.io.p("In ChildB "); str str = "B"; ``` Compile and run: ``` > smp Parent.smp In Parent In ChildB B ``` ### Goal: Easy private fields Leading underscore makes field or class private. ### Goal: similar to C compiler driver syntax ``` Simple --cpu x86_64_v2 --abi Win64 --norun -o lib/lib/sys_x86_64_v2_win64.o src/main/smp/sys.smp ``` Module not mentioned, so top-level file is module: `sys` Module directory not mentioned, so is prefix of file name `src/main/smp`. Build dir not mentioned, so is prefix of output file: `lib/` Output file is default `smp.o`, here renamed. One can refer to another, and will be executed as first touched. Parents must be touched before children by design. However a child A can refer to a child B. Touching Parent.B means - Parent is touched first, and begins - Anything it touches, gets recursively including e.g. A or B - Parent completes. - Nothing in a can refer to another *module*. - Static functions *can* refer to another module, but then can be executed... Goal: deterministic within a module. History: plenty of times I've had complex orders, and then discovered some referred static-final-fields are not yet init'd when child uses. SubGoal: deterministic *order*, Parent before Child, etc. Issue: mid-Parent , refers to Child, so Child before Parent completes. SubAnswer: Since parent first, and "parent knows child", this is OK. SubIssue: this does not work across modules; one module is not a parent of another Issue: Parent fcns called during can non-determistic init child. "fcn = { -> rand ? A.fld : B.fld }" Answer?: Parent *may not* direct or indirect refer to a child. Example: `B b = new B; // Illegal ref to child in Parent.` Example: `B? b = fcn(); fcn = { -> rand ? null : new B; } // Illegal call to fcn referring to B inside Parent ` Example: `B? b; fcn = { -> new B; } // OK, no B. during Parent.` Goal: external linkage also enjoys the 1-shot init feature. Answer?: extern call to 'Foo.foo()' first checks Foo. has run, before calling `Foo.foo()`. Optional: Caller is modified to bypass check after success.