Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added proof of concept for module imports #99

Closed

Conversation

clsource
Copy link

This is just a simple prototype for testing the concept of the importmap discussed in #98

  • dome.ini file stores the mapping for the imports.
# Dome Configuration
[imports]
code = example/
code2 = example/

Usage

import "code" for Code // maps to example/code.wren
import "code2" for Code2 //maps to example/code2.wren

@avivbeeri
Copy link
Collaborator

How does this behave if those submodules want to depend on their own dependancies?

@clsource
Copy link
Author

import "code" for Code

class Code {
    static init() {
        System.print("Code 1 Called")
        Code2.init()
    }
}

Reading from filesystem: /Users/clsource/Code/dome/dome/example/code.wren
code:4: Error at 'Code': Module variable is already defined.
code:7: Error at 'Code2': Variable is used but not defined.
Runtime error: Could not compile module 'code'.
2: main

@clsource
Copy link
Author

This is just a simple map of "key = path".
were key is the import value.

For a more complex scenario like:

import "code/code2" for Code2

It would be needed more rules for parsing the import string.
For example detects if it contains /, @ and other rules.
And then map the corresponding key to the final path to look for the file.

@clsource
Copy link
Author

clsource commented Oct 11, 2020

If submodules would like to depend on other submodules I think.
The least complex way of solving is having the single source of truth of the root dome.ini.

Submodules would require the final user to define the values inside dome.ini which they will use inside their codebase.

Example:

DomePunk would require that the path domepunk/ is defined in dome.ini

Then the game in main.wren Would use:

import "domepunk" for Dp

In domepunk/dp.wren Would use

import "domepunk/utils/clock" for Clock

And dome.ini would be defined as

[imports]
domepunk = modules/domepunk/dp
domepunk/ = modules/domepunk

So everytime when import "domepunk" for Dp will be mapped as: modules/domepunk/dp.wren
And when import "domepunk/utils/clock" for Clock is used inside dp.wren it would be mapped to:
modules/domepunk/utils/clock.wren

@clsource
Copy link
Author

Now the module loader supports using solidus to define a namespace.

internal WrenForeignMethodFn VM_bind_foreign_method(
    WrenVM* vm,
    const char* module,
    const char* className,
    bool isStatic,
    const char* signature) {

  // This file is seperate because it has a Copyright notice with it.
#include "signature.c.inc"

  ENGINE* engine = (ENGINE*)wrenGetUserData(vm);
  MAP moduleMap = engine->moduleMap;
  return MAP_getFunction(&moduleMap, module, fullName);
}

internal char* VM_load_module(WrenVM* vm, const char* name) {
  ENGINE* engine = (ENGINE*)wrenGetUserData(vm);
  MAP moduleMap = engine->moduleMap;

  if (DEBUG_MODE) {
    ENGINE_printLog(engine, "Loading module %s from ", name);
  }

  // Check against wren optional modules
#if WREN_OPT_META
  if (strcmp(name, "meta") == 0) {
    if (DEBUG_MODE) {
      ENGINE_printLog(engine, "wren\n", name);
    }
    return NULL;
  }
#endif
#if WREN_OPT_RANDOM
  if (strcmp(name, "random") == 0) {
    if (DEBUG_MODE) {
      ENGINE_printLog(engine, "wren\n", name);
    }
    return NULL;
  }
#endif

  // Check against dome modules
  char* module = (char*)MAP_getSource(&moduleMap, name);

  if (module != NULL) {
    if (DEBUG_MODE) {
      ENGINE_printLog(engine, "dome\n", name);
    }
    return module;
  }

  // Otherwise, search on filesystem
  char* extension = ".wren";
  char* path;
  path = malloc(strlen(name)+strlen(extension)+1); /* make space for the new string (should check the return value ...) */
  strcpy(path, name); /* add the extension */
  strcat(path, extension); /* add the extension */

  if (DEBUG_MODE) {
    ENGINE_printLog(engine, "%s\n", engine->tar ? "egg bundle" : "filesystem");
  }

  // This pointer becomes owned by the WrenVM and freed later.
  char* file = ENGINE_readFile(engine, path, NULL);
  if (file == NULL) {
    
    if (DEBUG_MODE) {
      ENGINE_printLog(engine, "Path not found. Looking in dome.ini\n", path);
    }

    // Search for / in the name
    char * solidus = "/";
    char * full = strdup(name);
    
    #define MAX_PATHS 255

    char paths[MAX_PATHS][MAX_PATHS];
    int index = 0;
    
    char * token = strtok(name, solidus);
    while (token != NULL && index < MAX_PATHS) {
      strcpy(paths[index], token);
      token = strtok(NULL, solidus);
      index++;
    }

    // Search for the import key
    char * key = strdup(full);

    // There are more values after solidus
    // We append the solidus for the key
    int isSolidus = 0;
    if (index > 1) {
      strcpy(key, paths[0]);
      strcat(key, solidus);
      isSolidus = 1;
    }

    // Look for the modules inside config
    char * importPath = CONFIG_readValue(&engine->config, "imports", key);
    if (importPath != NULL) {
      path = malloc(strlen(importPath) + strlen(full) + strlen(extension) + 1);
      strcpy(path, importPath);

      if (isSolidus == 1) {
        // We dont need to append the part with solidus just the next one
        for(int i = 1; i < index; i++) {
          strcat(path, paths[i]);
          if (i < index - 1) {
            strcat(path, solidus);
          }
        }
      } else {
        strcat(path, full);
      }

      strcat(path, extension);
      file = ENGINE_readFile(engine, path, NULL);

    } else {
      ENGINE_printLog(engine, "No import Path Found for Module %s\n", name);
    }
  }

  free(path);
  return file;
}

@clsource
Copy link
Author

Example Log

Reading from filesystem: /Users/clsource/Code/dome/dome/dome.ini
Reading from filesystem: /Users/clsource/Code/dome/dome/main.wren
Loading module graphics from dome
Loading module vector from dome
Loading module image from dome
Loading module font from dome
Loading module io from dome
Loading module code from filesystem
Reading from filesystem: /Users/clsource/Code/dome/dome/code.wren
Path not found. Looking in dome.ini
Reading from filesystem: /Users/clsource/Code/dome/dome/example/code.wren
Loading module code/code2 from filesystem
Reading from filesystem: /Users/clsource/Code/dome/dome/code/code2.wren
Path not found. Looking in dome.ini
Reading from filesystem: /Users/clsource/Code/dome/dome/example/code2.wren
Code 1 Called
Code 2 Called

@avivbeeri avivbeeri changed the base branch from master to release/1.4.0 October 17, 2020 14:07
@avivbeeri avivbeeri closed this Dec 3, 2020
@avivbeeri avivbeeri deleted the branch domeengine:release/1.4.0 December 3, 2020 23:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants