yuxshao/makergame

multi-file programs/modules/namespaces

yuxshao opened this issue · 7 comments

It'd make sense for makergame programs to include a bunch of built-in functions declared in some file. In this respect it'd be nice to allow files to include other files, and then possibly have the compiler always include some default library file. For now at least it's annoying to include end_game in every test, but it needs to be declared since the compiler has hardly any built-in functions (so far just print, but that will be removed shortly too).

If we're going to have library functions it might be nice to separate them from regular ones, say with namespaces like in C++. I propose the following syntax:

// lib.mg
// declaring namespaces
namespace ns = {
  int x;
  int compute() { ... }
  player { int y; ... }
}

// using things in namespaces
void main () {
  ns::player p = create ns::player;
  p.y = 3;
  ns::x = 4;
}

// putting namespace contents into scope
// (if it's not hard maybe make it so that this doesn't put
// ns in scope for the entire file, but only starting here)
using namespace ns;
void compute () {
  player p = create player;
  p.y = 3;
  x = 4;
}

And with separate files:

// fills namespace lib with contents of lib.mg (sort of literally replaces
// 'open ...' with the contents of the file, surrounded by braces)
namespace lib = open "lib.mg";
void hello () { lib::compute(); }
// puts everything in lib in scope of this file. equivalent to:
// namespace lib = open "lib.mg";
// using namespace lib;
using open "lib.mg";

void hello () { compute(); }

Like in C++, if any identifier could possibly resolve to multiple globals we raise an error.

Okay, current state of things:

  • You can define namespaces in other namespaces. This is called a concrete namespace.
    • The inner namespace only has access to things defined in the inner namespace.
    • The outer namespace can access inner namespace members with ::
  • You can set namespaces as aliases of other namespaces (namespace a = b so that a::x points to the same thing as b::x). Compiler will check for loops.
  • In another branch load-file:
    • You can load files into namespaces through syntax like namespace n = load "lib.mg".
    • All files are relative to the directory in which the compiler was run, which is probably bad.
    • Files with the same path and filename point to the same namespace, which is nice. If files don't become relative to the compiler directory, implementation might have to change to keep this property.
    • Every namespace includes an implicit namespace std = open "std.mg" which is nice for getting everything access but a pain because you can then do things like a::b::std::end_game() since namespace a::b now also includes std.

It'd be nice to discuss paths forward, or maybe alternative solutions since this isn't exactly the cleanest.

Private namespaces have been added to prevent things like a::b::std but there's no using yet.

Ugh... file namespaces are a mess right now. Need to somehow load files in folder relative to current file, and maybe get rid of the stdin input.

I think this should be priority. Getting a good file system thing going. It's a little fragile right now but I think it's stable. Just gotta be directory-aware (and have library directories like paths in C++)

Okay actually not that tough to do given JS's Core.Core_filename. Filename resolution works by

  1. loading the absolute path if it's absolute
  2. load the file in the current directory of the loader file
  3. load the file in the directory MAKERGAME_PATH, an environment variable

Still might be nice to have a using keyword though to dump namespace contents into another.

not using core filename any more but copied the realpath C interface over

using implemented but doesn't pass namespaces over (since that complicates private/public permissions)