aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/compile.c19
-rw-r--r--src/environment.c40
-rw-r--r--src/environment.h1
-rw-r--r--src/modules.c121
-rw-r--r--src/modules.h14
-rw-r--r--src/tomo.c10
-rw-r--r--src/typecheck.c12
7 files changed, 162 insertions, 55 deletions
diff --git a/src/compile.c b/src/compile.c
index cd6aef72..2ecb8c9e 100644
--- a/src/compile.c
+++ b/src/compile.c
@@ -12,6 +12,7 @@
#include "cordhelpers.h"
#include "enums.h"
#include "environment.h"
+#include "modules.h"
#include "parse.h"
#include "stdlib/integers.h"
#include "stdlib/nums.h"
@@ -1976,10 +1977,13 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
CORD suffix = get_id_suffix(Path$as_c_string(path));
return with_source_info(env, ast, CORD_all("$initialize", suffix, "();\n"));
} else if (use->what == USE_MODULE) {
- const char *name = module_alias(ast);
+ module_info_t mod = get_module_info(ast);
glob_t tm_files;
- if (glob(String(TOMO_PREFIX"/share/tomo_"TOMO_VERSION"/installed/", name, "/[!._0-9]*.tm"), GLOB_TILDE, NULL, &tm_files) != 0)
- code_err(ast, "Could not find library");
+ const char *folder = mod.version ? String(mod.name, "_", mod.version) : mod.name;
+ if (glob(String(TOMO_PREFIX"/share/tomo_"TOMO_VERSION"/installed/", folder, "/[!._0-9]*.tm"), GLOB_TILDE, NULL, &tm_files) != 0) {
+ if (!try_install_module(mod))
+ code_err(ast, "Could not find library");
+ }
CORD initialization = CORD_EMPTY;
@@ -4485,10 +4489,13 @@ CORD compile_statement_type_header(env_t *env, Path_t header_path, ast_t *ast)
Path_t build_dir = Path$resolved(Path$parent(header_path), Path$current_dir());
switch (use->what) {
case USE_MODULE: {
- const char *name = module_alias(ast);
+ module_info_t mod = get_module_info(ast);
glob_t tm_files;
- if (glob(String(TOMO_PREFIX"/share/tomo_"TOMO_VERSION"/installed/", name, "/[!._0-9]*.tm"), GLOB_TILDE, NULL, &tm_files) != 0)
- code_err(ast, "Could not find library");
+ const char *folder = mod.version ? String(mod.name, "_", mod.version) : mod.name;
+ if (glob(String(TOMO_PREFIX"/share/tomo_"TOMO_VERSION"/installed/", folder, "/[!._0-9]*.tm"), GLOB_TILDE, NULL, &tm_files) != 0) {
+ if (!try_install_module(mod))
+ code_err(ast, "Could not find library");
+ }
CORD includes = CORD_EMPTY;
for (size_t i = 0; i < tm_files.gl_pathc; i++) {
diff --git a/src/environment.c b/src/environment.c
index 4fdfbd4e..05ebed5a 100644
--- a/src/environment.c
+++ b/src/environment.c
@@ -7,12 +7,8 @@
#include "cordhelpers.h"
#include "environment.h"
#include "parse.h"
-#include "stdlib/c_strings.h"
#include "stdlib/datatypes.h"
-#include "stdlib/memory.h"
#include "stdlib/paths.h"
-#include "stdlib/pointers.h"
-#include "stdlib/simpleparse.h"
#include "stdlib/tables.h"
#include "stdlib/text.h"
#include "stdlib/util.h"
@@ -793,40 +789,4 @@ void set_binding(env_t *env, const char *name, type_t *type, CORD code)
Table$str_set(env->locals, name, new(binding_t, .type=type, .code=code));
}
-const char *module_alias(ast_t *use)
-{
- static Table_t cache = {};
- const char **cached = Table$get(cache, &use, Table$info(Pointer$info("@", &Memory$info), &CString$info));
- if (cached) return *cached;
- const char *name = Match(use, Use)->path;
- const char *alias = name;
- if (streq(name, "commands")) alias = "commands_v1.0";
- else if (streq(name, "random")) alias = "random_v1.0";
- else if (streq(name, "base64")) alias = "base64_v1.0";
- else if (streq(name, "core")) alias = "core_v1.0";
- else if (streq(name, "patterns")) alias = "patterns_v1.0";
- else if (streq(name, "pthreads")) alias = "pthreads_v1.0";
- else if (streq(name, "shell")) alias = "shell_v1.0";
- else if (streq(name, "time")) alias = "time_v1.0";
- else if (streq(name, "uuid")) alias = "uuid_v1.0";
- else {
- Path_t alias_file = Path$sibling(Path$from_str(use->file->filename), Text("modules.ini"));
- OptionalClosure_t by_line = Path$by_line(alias_file);
- if (by_line.fn) {
- OptionalText_t (*next_line)(void*) = by_line.fn;
- for (Text_t line; (line=next_line(by_line.userdata)).length >= 0; ) {
- char *line_str = Text$as_c_string(line);
- const char *line_alias = NULL, *full_version = NULL;
- if (!strparse(line_str, &line_alias, "=", &full_version)
- && streq(line_alias, name)) {
- alias = full_version;
- break;
- }
- }
- }
- }
- Table$set(&cache, &use, &alias, Table$info(Pointer$info("@", &Memory$info), &CString$info));
- return alias;
-}
-
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/src/environment.h b/src/environment.h
index 474bab61..a89935e6 100644
--- a/src/environment.h
+++ b/src/environment.h
@@ -94,6 +94,5 @@ binding_t *get_namespace_binding(env_t *env, ast_t *self, const char *name);
extern type_t *TEXT_TYPE;
extern type_t *PATH_TYPE;
extern type_t *PATH_TYPE_TYPE;
-const char *module_alias(ast_t *use);
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/src/modules.c b/src/modules.c
new file mode 100644
index 00000000..71e33eec
--- /dev/null
+++ b/src/modules.c
@@ -0,0 +1,121 @@
+#include "modules.h"
+#include "stdlib/memory.h"
+#include "stdlib/paths.h"
+#include "stdlib/simpleparse.h"
+#include "stdlib/pointers.h"
+#include "stdlib/tables.h"
+#include "stdlib/text.h"
+#include "stdlib/types.h"
+
+#define xsystem(...) ({ int _status = system(String(__VA_ARGS__)); if (!WIFEXITED(_status) || WEXITSTATUS(_status) != 0) errx(1, "Failed to run command: %s", String(__VA_ARGS__)); })
+
+module_info_t get_module_info(ast_t *use)
+{
+ static Table_t cache = {};
+ TypeInfo_t *cache_type = Table$info(Pointer$info("@", &Memory$info), Pointer$info("@", &Memory$info));
+ module_info_t **cached = Table$get(cache, &use, cache_type);
+ if (cached) return **cached;
+ const char *name = Match(use, Use)->path;
+ module_info_t *info = new(module_info_t, .name=name);
+ if (streq(name, "commands")) info->version = "v1.0";
+ else if (streq(name, "random")) info->version = "v1.0";
+ else if (streq(name, "base64")) info->version = "v1.0";
+ else if (streq(name, "core")) info->version = "v1.0";
+ else if (streq(name, "patterns")) info->version = "v1.0";
+ else if (streq(name, "pthreads")) info->version = "v1.0";
+ else if (streq(name, "shell")) info->version = "v1.0";
+ else if (streq(name, "time")) info->version = "v1.0";
+ else if (streq(name, "uuid")) info->version = "v1.0";
+ else {
+ Path_t alias_file = Path$sibling(Path$from_str(use->file->filename), Text("modules.ini"));
+ OptionalClosure_t by_line = Path$by_line(alias_file);
+ if (by_line.fn) {
+ OptionalText_t (*next_line)(void*) = by_line.fn;
+ find_section:;
+ for (Text_t line; (line=next_line(by_line.userdata)).length >= 0; ) {
+ char *line_str = Text$as_c_string(line);
+ if (line_str[0] == '[' && strncmp(line_str+1, name, strlen(name)) == 0
+ && line_str[1+strlen(name)] == ']')
+ break;
+ }
+ for (Text_t line; (line=next_line(by_line.userdata)).length >= 0; ) {
+ char *line_str = Text$as_c_string(line);
+ if (line_str[0] == '[') goto find_section;
+ if (!strparse(line_str, "version=", &info->version)
+ || !strparse(line_str, "url=", &info->url)
+ || !strparse(line_str, "git=", &info->git)
+ || !strparse(line_str, "path=", &info->path)
+ || !strparse(line_str, "revision=", &info->revision))
+ continue;
+ }
+ }
+ }
+ Table$set(&cache, &use, &info, cache_type);
+ return *info;
+
+}
+
+bool try_install_module(module_info_t mod)
+{
+ if (mod.git) {
+ OptionalText_t answer = ask(
+ Texts(Text("The module \""), Text$from_str(mod.name), Text("\" is not installed.\nDo you want to install it from git URL "),
+ Text$from_str(mod.git), Text("? [Y/n] ")),
+ true, true);
+ if (!(answer.length == 0 || Text$equal_values(answer, Text("Y")) || Text$equal_values(answer, Text("y"))))
+ return false;
+ print("Installing ", mod.name, " from git...");
+ Path_t tmpdir = Path$unique_directory(Path("/tmp/tomo-module-XXXXXX"));
+ if (mod.revision) xsystem("git clone --depth=1 --revision ", mod.revision, " ", mod.git, " ", tmpdir);
+ else xsystem("git clone --depth=1 ", mod.git, " ", tmpdir);
+ if (mod.path) xsystem("tomo -IL ", tmpdir, "/", mod.path);
+ else xsystem("tomo -IL ", tmpdir);
+ Path$remove(tmpdir, true);
+ return true;
+ } else if (mod.url) {
+ OptionalText_t answer = ask(
+ Texts(Text("The module "), Text$from_str(mod.name), Text(" is not installed.\nDo you want to install it from URL "),
+ Text$from_str(mod.url), Text("? [Y/n] ")),
+ true, true);
+ if (!(answer.length == 0 || Text$equal_values(answer, Text("Y")) || Text$equal_values(answer, Text("y"))))
+ return false;
+
+ print("Installing ", mod.name, " from URL...");
+
+ const char *p = strrchr(mod.url, '/');
+ if (!p) return false;
+ const char *filename = p + 1;
+ p = strchr(filename, '.');
+ if (!p) return false;
+ const char *extension = p + 1;
+ Path_t tmpdir = Path$unique_directory(Path("/tmp/tomo-module-XXXXXX"));
+ xsystem("curl ", mod.url, " -o ", tmpdir);
+ if (streq(extension, ".zip"))
+ xsystem("unzip ", tmpdir, "/", filename);
+ else if (streq(extension, ".tar.gz") || streq(extension, ".tar"))
+ xsystem("tar xf ", tmpdir, "/", filename);
+ else
+ return false;
+ const char *basename = String(string_slice(filename, strcspn(filename, ".")));
+ if (mod.path) xsystem("tomo -IL ", tmpdir, "/", basename, "/", mod.path);
+ else xsystem("tomo -IL ", tmpdir, "/", basename);
+ Path$remove(tmpdir, true);
+ return true;
+ } else if (mod.path) {
+ OptionalText_t answer = ask(
+ Texts(Text("The module "), Text$from_str(mod.name), Text(" is not installed.\nDo you want to install it from path "),
+ Text$from_str(mod.path), Text("? [Y/n] ")),
+ true, true);
+ if (!(answer.length == 0 || Text$equal_values(answer, Text("Y")) || Text$equal_values(answer, Text("y"))))
+ return false;
+
+ print("Installing ", mod.name, " from path...");
+ xsystem("tomo -IL ", mod.path);
+ return true;
+ }
+
+ return false;
+}
+
+
+// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/src/modules.h b/src/modules.h
new file mode 100644
index 00000000..36b05d3d
--- /dev/null
+++ b/src/modules.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include <stdbool.h>
+
+#include "ast.h"
+
+typedef struct {
+ const char *name, *version, *url, *git, *revision, *path;
+} module_info_t;
+
+module_info_t get_module_info(ast_t *use);
+bool try_install_module(module_info_t mod);
+
+// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/src/tomo.c b/src/tomo.c
index a2ccc84a..b47cd8e1 100644
--- a/src/tomo.c
+++ b/src/tomo.c
@@ -16,6 +16,7 @@
#include "ast.h"
#include "compile.h"
#include "cordhelpers.h"
+#include "modules.h"
#include "parse.h"
#include "stdlib/bools.h"
#include "stdlib/bytes.h"
@@ -614,14 +615,15 @@ void build_file_dependency_graph(Path_t path, Table_t *to_compile, Table_t *to_l
break;
}
case USE_MODULE: {
- const char *name = module_alias(stmt_ast);
+ module_info_t mod = get_module_info(stmt_ast);
+ const char *full_name = mod.version ? String(mod.name, "_", mod.version) : mod.name;
Text_t lib = Texts(Text("-Wl,-rpath,'"),
- Text(TOMO_PREFIX "/share/tomo_"TOMO_VERSION"/installed/"), Text$from_str(name),
+ Text(TOMO_PREFIX "/share/tomo_"TOMO_VERSION"/installed/"), Text$from_str(full_name),
Text("' '" TOMO_PREFIX "/share/tomo_"TOMO_VERSION"/installed/"),
- Text$from_str(name), Text("/lib"), Text$from_str(name), Text(SHARED_SUFFIX "'"));
+ Text$from_str(full_name), Text("/lib"), Text$from_str(full_name), Text(SHARED_SUFFIX "'"));
Table$set(to_link, &lib, NULL, Table$info(&Text$info, &Void$info));
- List_t children = Path$glob(Path$from_str(String(TOMO_PREFIX"/share/tomo_"TOMO_VERSION"/installed/", name, "/*.tm")));
+ List_t children = Path$glob(Path$from_str(String(TOMO_PREFIX"/share/tomo_"TOMO_VERSION"/installed/", full_name, "/[!._0-9]*.tm")));
for (int64_t i = 0; i < children.length; i++) {
Path_t *child = (Path_t*)(children.data + i*children.stride);
Table_t discarded = {.fallback=to_compile};
diff --git a/src/typecheck.c b/src/typecheck.c
index 60cef3fc..f97235b4 100644
--- a/src/typecheck.c
+++ b/src/typecheck.c
@@ -11,6 +11,7 @@
#include "ast.h"
#include "cordhelpers.h"
#include "environment.h"
+#include "modules.h"
#include "parse.h"
#include "stdlib/paths.h"
#include "stdlib/tables.h"
@@ -182,13 +183,16 @@ static env_t *load_module(env_t *env, ast_t *module_ast)
return load_module_env(env, ast);
}
case USE_MODULE: {
- const char *name = module_alias(module_ast);
+ module_info_t mod = get_module_info(module_ast);
glob_t tm_files;
- if (glob(String(TOMO_PREFIX"/share/tomo_"TOMO_VERSION"/installed/", name, "/[!._0-9]*.tm"), GLOB_TILDE, NULL, &tm_files) != 0)
- code_err(module_ast, "Could not find library");
+ const char *folder = mod.version ? String(mod.name, "_", mod.version) : mod.name;
+ if (glob(String(TOMO_PREFIX"/share/tomo_"TOMO_VERSION"/installed/", folder, "/[!._0-9]*.tm"), GLOB_TILDE, NULL, &tm_files) != 0) {
+ if (!try_install_module(mod))
+ code_err(module_ast, "Could not find or install library");
+ }
env_t *module_env = fresh_scope(env);
- Table$str_set(env->imports, name, module_env);
+ Table$str_set(env->imports, mod.name, module_env);
for (size_t i = 0; i < tm_files.gl_pathc; i++) {
const char *filename = tm_files.gl_pathv[i];