aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2024-04-12 13:09:31 -0400
committerBruce Hill <bruce@bruce-hill.com>2024-04-12 13:09:31 -0400
commit6c01eef851439549018267fdc439e4884af0c624 (patch)
tree0599dd071b8a5effb67e6a87ed1c34777eb8d8c7
parent17cb6ffd88c4464c513b045f4b06c4e6e46e8f22 (diff)
Introducing the main() function
-rw-r--r--builtins/bool.c6
-rw-r--r--builtins/functions.c29
-rw-r--r--builtins/functions.h1
-rw-r--r--builtins/text.c2
-rw-r--r--compile.c185
-rw-r--r--environment.h1
-rw-r--r--parse.c43
-rw-r--r--test/arrays.tm189
-rw-r--r--test/corecursive_func.tm5
-rw-r--r--test/enums.tm63
-rw-r--r--test/extern.tm6
-rw-r--r--test/for.tm37
-rw-r--r--test/functions.tm15
-rw-r--r--test/integers.tm84
-rw-r--r--test/lambdas.tm47
-rw-r--r--test/lang.tm27
-rw-r--r--test/minmax.tm33
-rw-r--r--test/nums.tm73
-rw-r--r--test/reductions.tm28
-rw-r--r--test/structs.tm26
-rw-r--r--test/tables.tm116
-rw-r--r--test/text.tm99
-rw-r--r--tomo.c32
23 files changed, 692 insertions, 455 deletions
diff --git a/builtins/bool.c b/builtins/bool.c
index ff5d0ec4..1e741be1 100644
--- a/builtins/bool.c
+++ b/builtins/bool.c
@@ -26,10 +26,12 @@ public CORD Bool$as_text(const bool *b, bool colorize, const TypeInfo *type)
public Bool_t Bool$from_text(CORD text, bool *success)
{
CORD lower = Text$lower(text);
- if (CORD_cmp(lower, "yes") == 0 || CORD_cmp(lower, "on") == 0 || CORD_cmp(lower, "true") == 0) {
+ if (CORD_cmp(lower, "yes") == 0 || CORD_cmp(lower, "on") == 0
+ || CORD_cmp(lower, "true") == 0 || CORD_cmp(lower, "1") == 0) {
if (success) *success = yes;
return yes;
- } else if (CORD_cmp(lower, "no") == 0 || CORD_cmp(lower, "off") == 0 || CORD_cmp(lower, "false") == 0) {
+ } else if (CORD_cmp(lower, "no") == 0 || CORD_cmp(lower, "off") == 0
+ || CORD_cmp(lower, "false") == 0 || CORD_cmp(lower, "0") == 0) {
if (success) *success = yes;
return no;
} else {
diff --git a/builtins/functions.c b/builtins/functions.c
index acc0c758..4ff7a1a5 100644
--- a/builtins/functions.c
+++ b/builtins/functions.c
@@ -8,9 +8,9 @@
#include <sys/param.h>
#include <uninorm.h>
-#include "files.h"
-#include "util.h"
#include "array.h"
+#include "bool.h"
+#include "files.h"
#include "functions.h"
#include "halfsiphash.h"
#include "pointer.h"
@@ -18,6 +18,7 @@
#include "table.h"
#include "text.h"
#include "types.h"
+#include "util.h"
extern bool USE_COLOR;
@@ -186,4 +187,28 @@ public void say(CORD text)
}
}
+public bool pop_flag(char **argv, int *i, const char *flag, CORD *result)
+{
+ if (argv[*i][0] != '-' || argv[*i][1] != '-') {
+ return false;
+ } else if (streq(argv[*i] + 2, flag)) {
+ *result = CORD_EMPTY;
+ argv[*i] = NULL;
+ *i += 1;
+ return true;
+ } else if (strncmp(argv[*i] + 2, "no-", 3) == 0 && streq(argv[*i] + 5, flag)) {
+ *result = "no";
+ argv[*i] = NULL;
+ *i += 1;
+ return true;
+ } else if (strncmp(argv[*i] + 2, flag, strlen(flag)) == 0 && argv[*i][2 + strlen(flag)] == '=') {
+ *result = CORD_from_char_star(argv[*i] + 2 + strlen(flag) + 1);
+ argv[*i] = NULL;
+ *i += 1;
+ return true;
+ } else {
+ return false;
+ }
+}
+
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/builtins/functions.h b/builtins/functions.h
index 7deaf2b0..53a6de2f 100644
--- a/builtins/functions.h
+++ b/builtins/functions.h
@@ -20,5 +20,6 @@ uint32_t generic_hash(const void *obj, const TypeInfo *type);
int32_t generic_compare(const void *x, const void *y, const TypeInfo *type);
bool generic_equal(const void *x, const void *y, const TypeInfo *type);
CORD generic_as_text(const void *obj, bool colorize, const TypeInfo *type);
+bool pop_flag(char **argv, int *i, const char *flag, CORD *result);
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/builtins/text.c b/builtins/text.c
index 0450e9b2..096c3447 100644
--- a/builtins/text.c
+++ b/builtins/text.c
@@ -225,7 +225,7 @@ public CORD Text$replace(CORD text, CORD pat, CORD replacement, int64_t limit)
if (!text || !pat) return text;
CORD ret = CORD_EMPTY;
size_t pos = 0, pat_len = CORD_len(pat);
- for (size_t found; limit > 0 && (found=CORD_str(text, pos, pat)) != CORD_NOT_FOUND; --limit) {
+ for (size_t found; limit != 0 && (found=CORD_str(text, pos, pat)) != CORD_NOT_FOUND; --limit) {
ret = CORD_all(ret, CORD_substr(text, pos, found - pos), replacement);
pos = found + pat_len;
}
diff --git a/compile.c b/compile.c
index 0e23c948..58ba82ce 100644
--- a/compile.c
+++ b/compile.c
@@ -294,7 +294,7 @@ CORD compile_statement(env_t *env, ast_t *ast)
env->code->imports = CORD_all(env->code->imports, "#include <", path, ".h>\n");
env->code->object_files = CORD_all(env->code->object_files, "-l", name, " ");
}
- return CORD_all(name, "$use();\n");
+ return CORD_EMPTY;
} else {
type_t *t = get_type(env, decl->value);
if (t->tag == AbortType || t->tag == VoidType)
@@ -917,7 +917,10 @@ static CORD compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg
CORD compile(env_t *env, ast_t *ast)
{
switch (ast->tag) {
- case Nil: return CORD_asprintf("$Null(%r)", compile_type_ast(env, Match(ast, Nil)->type));
+ case Nil: {
+ type_t *t = parse_type_ast(env, Match(ast, Nil)->type);
+ return CORD_all("((", compile_type(env, t), "*)NULL)");
+ }
case Bool: return Match(ast, Bool)->b ? "yes" : "no";
case Var: {
binding_t *b = get_binding(env, Match(ast, Var)->name);
@@ -1816,18 +1819,17 @@ void compile_namespace(env_t *env, const char *ns_name, ast_t *block)
auto decl = Match(ast, Declare);
type_t *t = get_type(ns_env, decl->value);
- CORD var_decl = CORD_all(compile_type(env, t), " ", compile(ns_env, decl->var), ";\n");
+ if (!is_constant(decl->value))
+ code_err(decl->value, "This value is supposed to be a compile-time constant, but I can't figure out how to make it one");
+ CORD var_decl = CORD_all(compile_type(env, t), " ", compile(ns_env, decl->var), " = ", compile(ns_env, decl->value), ";\n");
env->code->staticdefs = CORD_cat(env->code->staticdefs, var_decl);
- CORD init = CORD_all(compile(ns_env, decl->var), " = ", compile(ns_env, decl->value), ";\n");
- env->code->main = CORD_cat(env->code->main, init);
-
env->code->fndefs = CORD_all(env->code->fndefs, "extern ", compile_type(env, t), " ", compile(ns_env, decl->var), ";\n");
break;
}
default: {
CORD code = compile_statement(ns_env, ast);
- env->code->main = CORD_cat(env->code->main, code);
+ assert(!code);
break;
}
}
@@ -1877,6 +1879,144 @@ CORD compile_type_info(env_t *env, type_t *t)
}
}
+static CORD compile_main_arg_parser(env_t *env, const char *module_name, type_t *main_fn_type)
+{
+ CORD code = CORD_all("void ", module_name, "$main$run(int argc, char *argv[]) {\n");
+ auto fn_info = Match(main_fn_type, FunctionType);
+ env_t *main_env = fresh_scope(env);
+
+ CORD usage = CORD_EMPTY;
+ for (arg_t *arg = fn_info->args; arg; arg = arg->next) {
+ usage = CORD_cat(usage, " ");
+ type_t *t = get_arg_type(main_env, arg);
+ CORD flag = Text$replace(arg->name, "_", "-", INT64_MAX);
+ if (arg->default_val) {
+ if (t->tag == BoolType)
+ usage = CORD_all(usage, "[--", flag, "]");
+ else
+ usage = CORD_all(usage, "[--", flag, "=...]");
+ } else {
+ if (t->tag == BoolType)
+ usage = CORD_all(usage, "[--", flag, "|--no-", flag, "]");
+ else
+ usage = CORD_all(usage, "<", flag, ">");
+ }
+ }
+ code = CORD_all(code, "CORD $usage = CORD_all(\"Usage: \", argv[0], ", Text$quoted(usage, false), ");\n",
+ "#define $USAGE_ERR(...) errx(1, CORD_to_const_char_star(CORD_all(__VA_ARGS__)))\n"
+ "#define $IS_FLAG(str, flag) (strncmp(str, flag, strlen(flag) == 0 && (str[strlen(flag)] == 0 || str[strlen(flag)] == '=')) == 0)\n");
+
+ // Declare args:
+ for (arg_t *arg = fn_info->args; arg; arg = arg->next) {
+ type_t *t = get_arg_type(main_env, arg);
+ assert(arg->name);
+ code = CORD_all(
+ code, compile_declaration(env, t, arg->name), ";\n",
+ "bool ", arg->name, "$is_set = no;\n");
+ set_binding(env, arg->name, new(binding_t, .type=t, .code=arg->name));
+ }
+ // Provide --flags:
+ code = CORD_all(code, "CORD $flag;\n"
+ "for (int i = 1; i < argc; ) {\n"
+ "if (streq(argv[i], \"--\")) {\n"
+ "argv[i] = NULL;\n"
+ "break;\n"
+ "}\n"
+ "if (strncmp(argv[i], \"--\", 2) != 0) {\n++i;\ncontinue;\n}\n");
+ for (arg_t *arg = fn_info->args; arg; arg = arg->next) {
+ type_t *t = get_arg_type(main_env, arg);
+ CORD flag = Text$replace(arg->name, "_", "-", INT64_MAX);
+ switch (t->tag) {
+ case BoolType: {
+ code = CORD_all(code, "else if (pop_flag(argv, &i, \"", flag, "\", &$flag)) {\n"
+ "if ($flag) {\n",
+ arg->name, " = Bool$from_text($flag, &", arg->name, "$is_set", ");\n"
+ "if (!", arg->name, "$is_set) \n"
+ "$USAGE_ERR(\"Invalid argument for '--", flag, "'\\n\", $usage);\n",
+ "} else {\n",
+ arg->name, " = yes;\n",
+ arg->name, "$is_set = yes;\n"
+ "}\n"
+ "}\n");
+ break;
+ }
+ case TextType: {
+ code = CORD_all(code, "else if (pop_flag(argv, &i, \"", flag, "\", &$flag)) {\n",
+ arg->name, " = CORD_to_const_char_star($flag);\n",
+ arg->name, "$is_set = yes;\n"
+ "}\n");
+ break;
+ }
+ case IntType: case NumType: {
+ CORD type_name = type_to_cord(t);
+ code = CORD_all(code, "else if (pop_flag(argv, &i, \"", flag, "\", &$flag)) {\n",
+ "if ($flag == CORD_EMPTY)\n"
+ "$USAGE_ERR(\"No value provided for '--", flag, "'\\n\", $usage);\n"
+ "CORD $invalid = CORD_EMPTY;\n",
+ arg->name, " = ", type_name, "$from_text($flag, &$invalid);\n"
+ "if ($invalid != CORD_EMPTY)\n"
+ "$USAGE_ERR(\"Invalid value provided for '--", flag, "'\\n\", $usage);\n",
+ arg->name, "$is_set = yes;\n"
+ "}\n");
+ break;
+ }
+ default:
+ compiler_err(NULL, NULL, NULL, "Main function has unsupported argument type: %T", t);
+ }
+ }
+
+ code = CORD_all(
+ code, "else {\n"
+ "$USAGE_ERR(\"Unrecognized argument: \", argv[i], \"\\n\", $usage);\n"
+ "}\n"
+ "}\n"
+ "int i = 1;\n"
+ "while (i < argc && argv[i] == NULL)\n"
+ "++i;\n");
+
+ for (arg_t *arg = fn_info->args; arg; arg = arg->next) {
+ code = CORD_all(code, "if (!", arg->name, "$is_set) {\n");
+ type_t *t = get_arg_type(env, arg);
+ if (arg->default_val) {
+ code = CORD_all(code, arg->name, " = ", compile(env, arg->default_val), ";\n");
+ } else {
+ code = CORD_all(
+ code,
+ "if (i < argc) {");
+ if (t->tag == TextType) {
+ code = CORD_all(code, arg->name, " = CORD_from_char_star(argv[i]);\n");
+ } else {
+ code = CORD_all(
+ code,
+ "CORD $invalid;\n",
+ arg->name, " = ", type_to_cord(t), "$from_text(argv[i], &$invalid)", ";\n"
+ "if ($invalid != CORD_EMPTY)\n"
+ "$USAGE_ERR(\"Unable to parse this argument as a ", type_to_cord(t), ": \", CORD_from_char_star(argv[i]));\n");
+ }
+ code = CORD_all(
+ code,
+ "argv[i++] = NULL;\n"
+ "while (i < argc && argv[i] == NULL)\n"
+ "++i;\n} else {\n"
+ "$USAGE_ERR(\"Required argument '", arg->name, "' was not provided!\\n\", $usage);\n",
+ "}\n");
+ }
+ code = CORD_all(code, "}\n");
+ }
+
+
+ code = CORD_all(code, "for (; i < argc; i++) {\n"
+ "if (argv[i])\n$USAGE_ERR(\"Unexpected argument: \", Text$quoted(argv[i], false), \"\\n\", $usage);\n}\n");
+
+ code = CORD_all(code, module_name, "$main(");
+ for (arg_t *arg = fn_info->args; arg; arg = arg->next) {
+ code = CORD_all(code, arg->name);
+ if (arg->next) code = CORD_all(code, ", ");
+ }
+ code = CORD_all(code, ");\n}\n");
+ return code;
+}
+
module_code_t compile_file(ast_t *ast)
{
env_t *env = new_compilation_unit();
@@ -1905,11 +2045,26 @@ module_code_t compile_file(ast_t *ast)
compile(env, decl->value), ";\n");
} else {
CORD code = compile_statement(env, stmt->ast);
- if (code)
- CORD_appendf(&env->code->main, "%r\n", code);
+ assert(!code);
+ // if (code)
+ // env->code->main = CORD_all(env->code->main, code, "\n");
}
}
+ env->code->fndefs = CORD_all(env->code->fndefs, "void ", name, "$main$run(int argc, char *argv[]);\n");
+
+ binding_t *main_fn = get_binding(env, "main");
+ if (!main_fn) {
+ env->code->funcs = CORD_all(env->code->funcs, "public void ", name, "$main$run(int argc, char *argv[]) {\n"
+ "(void)argc;\n"
+ "(void)argv;\n"
+ "}\n");
+ } else if (main_fn->type->tag != FunctionType) {
+ compiler_err(NULL, NULL, NULL, "The name 'main' is bound to something that isn't a function, it's %T", main_fn->type);
+ } else {
+ env->code->funcs = CORD_all(env->code->funcs, compile_main_arg_parser(env, name, main_fn->type));
+ }
+
return (module_code_t){
.module_name=name,
.object_files=env->code->object_files,
@@ -1918,22 +2073,14 @@ module_code_t compile_file(ast_t *ast)
"#include <tomo/tomo.h>\n",
env->code->typedefs, "\n",
env->code->typecode, "\n",
- env->code->fndefs, "\n",
- "public void ", env->file_prefix, "use(void);\n"
+ env->code->fndefs, "\n"
),
.c_file=CORD_all(
// CORD_asprintf("#line 0 %r\n", Text$quoted(ast->file->filename, false)),
env->code->imports, "\n",
env->code->staticdefs, "\n",
env->code->funcs, "\n",
- env->code->typeinfos, "\n",
- "\n"
- "public void ", env->file_prefix, "use(void) {\n"
- "static bool $loaded = no;\n"
- "if ($loaded) return;\n"
- "$loaded = yes;\n\n",
- env->code->main,
- "}\n"
+ env->code->typeinfos, "\n"
),
};
}
diff --git a/environment.h b/environment.h
index dc7f527c..301ffbb5 100644
--- a/environment.h
+++ b/environment.h
@@ -15,7 +15,6 @@ typedef struct {
CORD staticdefs;
CORD funcs;
CORD typeinfos;
- CORD main;
CORD object_files;
} compilation_unit_t;
diff --git a/parse.c b/parse.c
index 8b2e9840..4d283918 100644
--- a/parse.c
+++ b/parse.c
@@ -96,6 +96,7 @@ static PARSER(parse_doctest);
static PARSER(parse_use);
static PARSER(parse_linker);
static PARSER(parse_namespace);
+static PARSER(parse_file_body);
static type_ast_t *parse_type(parse_ctx_t *ctx, const char *pos);
@@ -509,7 +510,7 @@ type_ast_t *parse_pointer_type(parse_ctx_t *ctx, const char *pos) {
bool is_readonly = match(&pos, "%");
spaces(&pos);
type_ast_t *type = expect(ctx, start, &pos, parse_type,
- "I couldn't parse a pointer type after this point");
+ "I couldn't parse a pointer type after this point");
return NewTypeAST(ctx->file, start, pos, PointerTypeAST, .pointed=type, .is_optional=optional, .is_stack=is_stack, .is_readonly=is_readonly);
}
@@ -1548,7 +1549,8 @@ PARSER(parse_namespace) {
||(stmt=optional(ctx, &pos, parse_func_def))
||(stmt=optional(ctx, &pos, parse_use))
||(stmt=optional(ctx, &pos, parse_linker))
- ||(stmt=optional(ctx, &pos, parse_statement)))
+ ||(stmt=optional(ctx, &pos, parse_extern))
+ ||(stmt=optional(ctx, &pos, parse_declaration)))
{
statements = new(ast_list_t, .ast=stmt, .next=statements);
pos = stmt->end;
@@ -1559,7 +1561,7 @@ PARSER(parse_namespace) {
// }
} else {
if (get_indent(ctx->file, next) > indent && next < strchrnul(next, '\n'))
- parser_err(ctx, next, strchrnul(next, '\n'), "I couldn't parse this namespace statement");
+ parser_err(ctx, next, strchrnul(next, '\n'), "I couldn't parse this namespace declaration");
break;
}
}
@@ -1567,6 +1569,39 @@ PARSER(parse_namespace) {
return NewAST(ctx->file, start, pos, Block, .statements=statements);
}
+PARSER(parse_file_body) {
+ const char *start = pos;
+ whitespace(&pos);
+ ast_list_t *statements = NULL;
+ for (;;) {
+ const char *next = pos;
+ whitespace(&next);
+ if (get_indent(ctx->file, next) != 0) break;
+ ast_t *stmt;
+ if ((stmt=optional(ctx, &pos, parse_struct_def))
+ ||(stmt=optional(ctx, &pos, parse_enum_def))
+ ||(stmt=optional(ctx, &pos, parse_lang_def))
+ ||(stmt=optional(ctx, &pos, parse_func_def))
+ ||(stmt=optional(ctx, &pos, parse_use))
+ ||(stmt=optional(ctx, &pos, parse_linker))
+ ||(stmt=optional(ctx, &pos, parse_extern))
+ ||(stmt=optional(ctx, &pos, parse_declaration)))
+ {
+ statements = new(ast_list_t, .ast=stmt, .next=statements);
+ pos = stmt->end;
+ whitespace(&pos); // TODO: check for newline
+ } else {
+ break;
+ }
+ }
+ whitespace(&pos);
+ if (pos < ctx->file->text + ctx->file->len && *pos != '\0') {
+ parser_err(ctx, pos, strchrnul(pos, '\n'), "I expect all top-level statements to be declarations of some kind");
+ }
+ REVERSE_LIST(statements);
+ return NewAST(ctx->file, start, pos, Block, .statements=statements);
+}
+
PARSER(parse_struct_def) {
// struct Foo(...) \n body
const char *start = pos;
@@ -1902,7 +1937,7 @@ ast_t *parse_file(file_t *file, jmp_buf *on_err) {
some_not(&pos, "\r\n");
whitespace(&pos);
- ast_t *ast = parse_namespace(&ctx, pos);
+ ast_t *ast = parse_file_body(&ctx, pos);
pos = ast->end;
whitespace(&pos);
if (pos < file->text + file->len && *pos != '\0') {
diff --git a/test/arrays.tm b/test/arrays.tm
index 17aeee53..c8e34af4 100644
--- a/test/arrays.tm
+++ b/test/arrays.tm
@@ -1,96 +1,97 @@
-if yes
- >> [:Num32]
- = [] : [Num32]
-
-if yes
- >> arr := [10, 20, 30]
- = [10, 20, 30]
-
- >> arr[1]
- = 10
- >> arr[-1]
- = 30
-
- >> #arr
- = 3
-
- sum := 0
- for x in arr
- sum += x
- >> sum
- = 60
-
- str := ""
- for i,x in arr
- str ++= "({i},{x})"
- >> str
- = "(1,10)(2,20)(3,30)"
-
-if yes
- >> arr := [10, 20] ++ [30, 40]
- = [10, 20, 30, 40]
-
- >> arr ++= [50, 60]
- >> arr
- = [10, 20, 30, 40, 50, 60]
-
- >> arr ++= 70
- >> arr
- = [10, 20, 30, 40, 50, 60, 70]
-
-if yes
- >> arr := [10, 20]
- >> copy := arr
- >> arr ++= 30
- >> arr
- = [10, 20, 30]
- >> copy
- = [10, 20]
-
-if yes
- >> [10*i for i in 5]
+func main()
+ if yes
+ >> [:Num32]
+ = [] : [Num32]
+
+ if yes
+ >> arr := [10, 20, 30]
+ = [10, 20, 30]
+
+ >> arr[1]
+ = 10
+ >> arr[-1]
+ = 30
+
+ >> #arr
+ = 3
+
+ sum := 0
+ for x in arr
+ sum += x
+ >> sum
+ = 60
+
+ str := ""
+ for i,x in arr
+ str ++= "({i},{x})"
+ >> str
+ = "(1,10)(2,20)(3,30)"
+
+ if yes
+ >> arr := [10, 20] ++ [30, 40]
+ = [10, 20, 30, 40]
+
+ >> arr ++= [50, 60]
+ >> arr
+ = [10, 20, 30, 40, 50, 60]
+
+ >> arr ++= 70
+ >> arr
+ = [10, 20, 30, 40, 50, 60, 70]
+
+ if yes
+ >> arr := [10, 20]
+ >> copy := arr
+ >> arr ++= 30
+ >> arr
+ = [10, 20, 30]
+ >> copy
+ = [10, 20]
+
+ if yes
+ >> [10*i for i in 5]
+ = [10, 20, 30, 40, 50]
+
+ >> [i*10 for i in 5]
= [10, 20, 30, 40, 50]
->> [i*10 for i in 5]
-= [10, 20, 30, 40, 50]
-
->> [i*10 for i in 5 if i mod 2 != 0]
-= [10, 30, 50]
-
->> [x for x in y if x > 1 for y in [3, 4, 5] if y < 5]
-= [2, 3, 2, 3, 4]
-
-if yes
- >> arr := @[10, 20]
- >> copy := arr[]
- >> arr:insert(30)
- >> arr
- = @[10, 20, 30]
- >> copy
- = [10, 20]
-
-if yes
- >> arr := [10, 20, 30]
- >> arr:reversed()
- = [30, 20, 10]
-
-if yes
- >> nums := [10, -20, 30]
- // Sorted function doesn't mutate original:
- >> nums:sorted()
- = [-20, 10, 30]
- >> nums
- = [10, -20, 30]
- // Sort function does mutate in place:
- >> nums:sort()
- >> nums
- = [-20, 10, 30]
- // Custom sort functions:
- >> nums:sort(func(x:&%Int,y:&%Int) x:abs() <> y:abs())
- >> nums
- = [10, -20, 30]
- >> nums:sort(func(x:&%Int,y:&%Int) y[] <> x[])
- >> nums
- = [30, 10, -20]
-
->> ["A", "B", "C"]:sample(10, [1.0, 0.5, 0.0])
+ >> [i*10 for i in 5 if i mod 2 != 0]
+ = [10, 30, 50]
+
+ >> [x for x in y if x > 1 for y in [3, 4, 5] if y < 5]
+ = [2, 3, 2, 3, 4]
+
+ if yes
+ >> arr := @[10, 20]
+ >> copy := arr[]
+ >> arr:insert(30)
+ >> arr
+ = @[10, 20, 30]
+ >> copy
+ = [10, 20]
+
+ if yes
+ >> arr := [10, 20, 30]
+ >> arr:reversed()
+ = [30, 20, 10]
+
+ if yes
+ >> nums := [10, -20, 30]
+ // Sorted function doesn't mutate original:
+ >> nums:sorted()
+ = [-20, 10, 30]
+ >> nums
+ = [10, -20, 30]
+ // Sort function does mutate in place:
+ >> nums:sort()
+ >> nums
+ = [-20, 10, 30]
+ // Custom sort functions:
+ >> nums:sort(func(x:&%Int,y:&%Int) x:abs() <> y:abs())
+ >> nums
+ = [10, -20, 30]
+ >> nums:sort(func(x:&%Int,y:&%Int) y[] <> x[])
+ >> nums
+ = [30, 10, -20]
+
+ >> ["A", "B", "C"]:sample(10, [1.0, 0.5, 0.0])
diff --git a/test/corecursive_func.tm b/test/corecursive_func.tm
index 0ff92e53..22ffd627 100644
--- a/test/corecursive_func.tm
+++ b/test/corecursive_func.tm
@@ -10,5 +10,6 @@ func pong(x:Int)->[Text]
else
return ["pong: {x}"]
->> ping(3)
-= ["ping: 3", "pong: 2", "ping: 1", "pong: 0"]
+func main()
+ >> ping(3)
+ = ["ping: 3", "pong: 2", "ping: 1", "pong: 0"]
diff --git a/test/enums.tm b/test/enums.tm
index aea18948..f321af8c 100644
--- a/test/enums.tm
+++ b/test/enums.tm
@@ -1,34 +1,35 @@
enum Foo(Zero, One(x:Int), Two(x,y:Int))
->> Foo.Zero
-= Foo.Zero
->> Foo.One(123)
-= Foo.One(x=123)
->> Foo.Two(123, 456)
-= Foo.Two(x=123, y=456)
-
->> Foo.One(10) == Foo.One(10)
-= yes
-
->> Foo.One(10) == Foo.Zero
-= no
-
->> Foo.One(10) == Foo.One(-1)
-= no
-
->> Foo.One(10) < Foo.Two(1, 2)
-= yes
-
->> x := Foo.One(123)
->> t := {x=>"found"; default="missing"}
->> t[x]
-= "found"
->> t[Foo.Zero]
-= "missing"
-
-when x is o:One
- >> o.x
- = 123
-else
- fail("Oops")
+func main()
+ >> Foo.Zero
+ = Foo.Zero
+ >> Foo.One(123)
+ = Foo.One(x=123)
+ >> Foo.Two(123, 456)
+ = Foo.Two(x=123, y=456)
+
+ >> Foo.One(10) == Foo.One(10)
+ = yes
+
+ >> Foo.One(10) == Foo.Zero
+ = no
+
+ >> Foo.One(10) == Foo.One(-1)
+ = no
+
+ >> Foo.One(10) < Foo.Two(1, 2)
+ = yes
+
+ >> x := Foo.One(123)
+ >> t := {x=>"found"; default="missing"}
+ >> t[x]
+ = "found"
+ >> t[Foo.Zero]
+ = "missing"
+
+ when x is o:One
+ >> o.x
+ = 123
+ else
+ fail("Oops")
diff --git a/test/extern.tm b/test/extern.tm
index a02a6a85..896c5730 100644
--- a/test/extern.tm
+++ b/test/extern.tm
@@ -1,3 +1,5 @@
extern CORD_cat:func(a:Text, b:Text)->Text
->> CORD_cat("hello ", "world")
-= "hello world"
+
+func main()
+ >> CORD_cat("hello ", "world")
+ = "hello world"
diff --git a/test/for.tm b/test/for.tm
index 05990d37..51940973 100644
--- a/test/for.tm
+++ b/test/for.tm
@@ -7,11 +7,6 @@ func all_nums(nums:[Int])->Text
return "EMPTY"
return result
->> all_nums([10,20,30])
-= "10,20,30,"
->> all_nums([:Int])
-= "EMPTY"
-
func labeled_nums(nums:[Int])->Text
result := ""
for i,num in nums
@@ -20,11 +15,6 @@ func labeled_nums(nums:[Int])->Text
return "EMPTY"
return result
->> labeled_nums([10,20,30])
-= "1:10,2:20,3:30,"
->> labeled_nums([:Int])
-= "EMPTY"
-
func table_str(t:{Text=>Text})->Text
str := ""
for k,v in t
@@ -32,12 +22,6 @@ func table_str(t:{Text=>Text})->Text
else return "EMPTY"
return str
->> t := {"key1"=>"value1", "key2"=>"value2"}
->> table_str(t)
-= "key1=>value1,key2=>value2,"
->> table_str({:Text=>Text})
-= "EMPTY"
-
func table_key_str(t:{Text=>Text})->Text
str := ""
for k in t
@@ -45,5 +29,22 @@ func table_key_str(t:{Text=>Text})->Text
else return "EMPTY"
return str
->> table_key_str(t)
-= "key1,key2,"
+func main()
+ >> all_nums([10,20,30])
+ = "10,20,30,"
+ >> all_nums([:Int])
+ = "EMPTY"
+
+ >> labeled_nums([10,20,30])
+ = "1:10,2:20,3:30,"
+ >> labeled_nums([:Int])
+ = "EMPTY"
+
+ >> t := {"key1"=>"value1", "key2"=>"value2"}
+ >> table_str(t)
+ = "key1=>value1,key2=>value2,"
+ >> table_str({:Text=>Text})
+ = "EMPTY"
+
+ >> table_key_str(t)
+ = "key1,key2,"
diff --git a/test/functions.tm b/test/functions.tm
index 487e9467..13cdee55 100644
--- a/test/functions.tm
+++ b/test/functions.tm
@@ -1,14 +1,15 @@
func add(x:Int, y:Int)->Int
return x + y
->> add(3, 5)
-= 8
-
func cached_heap(x:Int; cached)->@Int
return @x
->> cached_heap(1) == cached_heap(1)
-= yes
->> cached_heap(1) == cached_heap(2)
-= no
+func main()
+ >> add(3, 5)
+ = 8
+
+ >> cached_heap(1) == cached_heap(1)
+ = yes
+ >> cached_heap(1) == cached_heap(2)
+ = no
diff --git a/test/integers.tm b/test/integers.tm
index a3ec7fe2..376e0319 100644
--- a/test/integers.tm
+++ b/test/integers.tm
@@ -1,55 +1,55 @@
+func main()
+ >> 2 + 3
+ = 5
->> 2 + 3
-= 5
+ >> 2 * 3
+ = 6
->> 2 * 3
-= 6
+ >> 2 + 3 * 4
+ = 14
->> 2 + 3 * 4
-= 14
+ >> 2 * 3 + 4
+ = 10
->> 2 * 3 + 4
-= 10
+ >> 1i8 + 2i16
+ = 3_i16
->> 1i8 + 2i16
-= 3_i16
+ >> 2 ^ 10
+ = 1024 : Num
->> 2 ^ 10
-= 1024 : Num
+ >> 3 and 2
+ = 2
->> 3 and 2
-= 2
+ >> 3 or 4
+ = 7
->> 3 or 4
-= 7
+ >> 3 xor 2
+ = 1
->> 3 xor 2
-= 1
+ nums := ""
+ for x in 5
+ nums ++= "{x},"
+ >> nums
+ = "1,2,3,4,5,"
-nums := ""
-for x in 5
- nums ++= "{x},"
->> nums
-= "1,2,3,4,5,"
+ >> x := 123
+ >> x:format(digits=5)
+ = "00123"
+ >> x:hex()
+ = "0x7B"
+ >> x:octal()
+ = "0o173"
->> x := 123
->> x:format(digits=5)
-= "00123"
->> x:hex()
-= "0x7B"
->> x:octal()
-= "0o173"
+ >> Int.random()
+ >> Int.min
+ = -9223372036854775808
+ >> Int.max
+ = 9223372036854775807
->> Int.random()
->> Int.min
-= -9223372036854775808
->> Int.max
-= 9223372036854775807
-
->> 123_i32:hex()
-= "0x7B"
->> 123_i16:hex()
-= "0x7B"
->> 123_i8:hex()
-= "0x7B"
+ >> 123_i32:hex()
+ = "0x7B"
+ >> 123_i16:hex()
+ = "0x7B"
+ >> 123_i8:hex()
+ = "0x7B"
diff --git a/test/lambdas.tm b/test/lambdas.tm
index ca2acb5d..a445540b 100644
--- a/test/lambdas.tm
+++ b/test/lambdas.tm
@@ -1,33 +1,32 @@
->> add_one := func(x:Int) x + 1
->> add_one(10)
-= 11
-
->> shout := func(msg:Text) say("{msg:upper()}!")
->> shout("hello")
-
->> asdf := add_one
->> asdf(99)
-= 100
-
-
func make_adder(x:Int)-> func(y:Int)->Int
return func(y:Int) x + y
->> add_100 := make_adder(100)
->> add_100(5)
-= 105
-
-
func suffix_fn(fn:func(t:Text)->Text, suffix:Text)->func(t:Text)->Text
return func(t:Text) fn(t)++suffix
->> shout2 := suffix_fn(Text.upper, "!")
->> shout2("hello")
-= "HELLO!"
-
func mul_func(n:Int, fn:func(x:Int)->Int)-> func(x:Int)->Int
return func(x:Int) n*fn(x)
->> abs100 := mul_func(100, Int.abs)
->> abs100(-5)
-= 500
+func main()
+ >> add_one := func(x:Int) x + 1
+ >> add_one(10)
+ = 11
+
+ >> shout := func(msg:Text) say("{msg:upper()}!")
+ >> shout("hello")
+
+ >> asdf := add_one
+ >> asdf(99)
+ = 100
+
+ >> add_100 := make_adder(100)
+ >> add_100(5)
+ = 105
+
+ >> shout2 := suffix_fn(Text.upper, "!")
+ >> shout2("hello")
+ = "HELLO!"
+
+ >> abs100 := mul_func(100, Int.abs)
+ >> abs100(-5)
+ = 500
diff --git a/test/lang.tm b/test/lang.tm
index 509a4ec6..ad09b605 100644
--- a/test/lang.tm
+++ b/test/lang.tm
@@ -14,20 +14,21 @@ lang HTML
func paragraph(content:HTML)->HTML
return $HTML{}"<p>{content}</p>"
->> HTML.HEADER
-= $HTML"<!DOCTYPE HTML>"
+func main()
+ >> HTML.HEADER
+ = $HTML"<!DOCTYPE HTML>"
->> user := "I <3 hax"
->> html := $HTML{}"Hello {user}!"
-= $HTML"Hello I &lt;3 hax!"
->> html ++ $HTML{}"<br>"
-= $HTML"Hello I &lt;3 hax!<br>"
+ >> user := "I <3 hax"
+ >> html := $HTML{}"Hello {user}!"
+ = $HTML"Hello I &lt;3 hax!"
+ >> html ++ $HTML{}"<br>"
+ = $HTML"Hello I &lt;3 hax!<br>"
->> $HTML{}"{1 + 2}"
-= $HTML"3"
+ >> $HTML{}"{1 + 2}"
+ = $HTML"3"
->> $HTML{}"{3_i8}"
-= $HTML"3"
+ >> $HTML{}"{3_i8}"
+ = $HTML"3"
->> html:paragraph()
-= $HTML"<p>Hello I &lt;3 hax!</p>"
+ >> html:paragraph()
+ = $HTML"<p>Hello I &lt;3 hax!</p>"
diff --git a/test/minmax.tm b/test/minmax.tm
index 58d9661d..c3af68d6 100644
--- a/test/minmax.tm
+++ b/test/minmax.tm
@@ -1,26 +1,27 @@
->> 3 _min_ 5
-= 3
->> 5 _min_ 3
-= 3
-
struct Foo(x:Int, y:Int)
func len(f:Foo)->Num
return Num.sqrt(f.x*f.x + f.y*f.y)
->> Foo(5, 1) _min_ Foo(5, 999)
-= Foo(x=5, y=1)
+func main()
+ >> 3 _min_ 5
+ = 3
+ >> 5 _min_ 3
+ = 3
+
+ >> Foo(5, 1) _min_ Foo(5, 999)
+ = Foo(x=5, y=1)
->> Foo(5, 999) _min_.x Foo(5, 1)
-= Foo(x=5, y=999)
+ >> Foo(5, 999) _min_.x Foo(5, 1)
+ = Foo(x=5, y=999)
->> Foo(999, 1) _min_.y Foo(1, 10)
-= Foo(x=999, y=1)
+ >> Foo(999, 1) _min_.y Foo(1, 10)
+ = Foo(x=999, y=1)
->> Foo(-999, -999) _max_:len() Foo(10, 10)
-= Foo(x=-999, y=-999)
+ >> Foo(-999, -999) _max_:len() Foo(10, 10)
+ = Foo(x=-999, y=-999)
->> foos := [Foo(5, 1), Foo(5, 99), Foo(-999, -999)]
->> (_max_) foos
-= Foo(x=5, y=99)
+ >> foos := [Foo(5, 1), Foo(5, 99), Foo(-999, -999)]
+ >> (_max_) foos
+ = Foo(x=5, y=99)
diff --git a/test/nums.tm b/test/nums.tm
index 14926aa0..766cfb48 100644
--- a/test/nums.tm
+++ b/test/nums.tm
@@ -1,49 +1,50 @@
->> n := 1.5
-= 1.5
+func main()
+ >> n := 1.5
+ = 1.5
->> n + n
-= 3
+ >> n + n
+ = 3
->> n * 2
-= 3
+ >> n * 2
+ = 3
->> n - n
-= 0
+ >> n - n
+ = 0
->> Num.PI
-= 3.14159
+ >> Num.PI
+ = 3.14159
->> Num.PI:format(precision=10)
-= "3.1415926536"
+ >> Num.PI:format(precision=10)
+ = "3.1415926536"
->> Num.random()
+ >> Num.random()
->> Num.INF
-= inf
->> Num.INF:isinf()
-= yes
+ >> Num.INF
+ = inf
+ >> Num.INF:isinf()
+ = yes
->> Num.nan()
-= nan
->> nan := Num.nan()
->> nan:isnan()
-= yes
->> nan == nan
-= no
+ >> Num.nan()
+ = nan
+ >> nan := Num.nan()
+ >> nan:isnan()
+ = yes
+ >> nan == nan
+ = no
->> Num.PI:cos():near(-1)
-= yes
->> Num.PI:sin():near(0)
-= yes
+ >> Num.PI:cos():near(-1)
+ = yes
+ >> Num.PI:sin():near(0)
+ = yes
->> 10.0:pow(3)
-= 1000
+ >> 10.0:pow(3)
+ = 1000
->> Num.nan():near(Num.nan())
-= no
+ >> Num.nan():near(Num.nan())
+ = no
->> Num.INF:near(-Num.INF)
-= no
+ >> Num.INF:near(-Num.INF)
+ = no
->> Num32.sqrt(16f32)
-= 4_f32
+ >> Num32.sqrt(16f32)
+ = 4_f32
diff --git a/test/reductions.tm b/test/reductions.tm
index e6b78539..6ed7823f 100644
--- a/test/reductions.tm
+++ b/test/reductions.tm
@@ -1,16 +1,18 @@
->> (+) [10, 20, 30]
-= 60
+struct Foo(x,y:Int)
->> (_max_) [3, 5, 2, 1, 4]
-= 5
+func main()
+ >> (+) [10, 20, 30]
+ = 60
->> (_max_:abs()) [1, -10, 5]
-= -10
+ >> (_max_) [3, 5, 2, 1, 4]
+ = 5
-struct Foo(x,y:Int)
->> (_max_) [Foo(0, 0), Foo(1, 0), Foo(0, 10)]
-= Foo(x=1, y=0)
->> (_max_.y) [Foo(0, 0), Foo(1, 0), Foo(0, 10)]
-= Foo(x=0, y=10)
->> (_max_.y:abs()) [Foo(0, 0), Foo(1, 0), Foo(0, 10), Foo(0, -999)]
-= Foo(x=0, y=-999)
+ >> (_max_:abs()) [1, -10, 5]
+ = -10
+
+ >> (_max_) [Foo(0, 0), Foo(1, 0), Foo(0, 10)]
+ = Foo(x=1, y=0)
+ >> (_max_.y) [Foo(0, 0), Foo(1, 0), Foo(0, 10)]
+ = Foo(x=0, y=10)
+ >> (_max_.y:abs()) [Foo(0, 0), Foo(1, 0), Foo(0, 10), Foo(0, -999)]
+ = Foo(x=0, y=-999)
diff --git a/test/structs.tm b/test/structs.tm
index c038bd07..1d9de8d0 100644
--- a/test/structs.tm
+++ b/test/structs.tm
@@ -1,6 +1,8 @@
struct Pair(x,y:Int)
struct Mixed(x:Int, text:Text)
+struct LinkedList(x:Int, next=!LinkedList)
+struct Password(text:Text; secret)
func test_literals()
>> x := Pair(10, 20)
@@ -11,7 +13,6 @@ func test_literals()
= yes
>> x == Pair(-1, -2)
= no
-test_literals()
func test_metamethods()
>> x := Pair(10, 20)
@@ -30,7 +31,6 @@ func test_metamethods()
= "found"
>> t2[y]
= "missing"
-test_metamethods()
func test_mixed()
>> x := Mixed(10, "Hello")
@@ -48,16 +48,18 @@ func test_mixed()
= "found"
>> t[y]
= "missing"
-test_mixed()
-struct LinkedList(x:Int, next=!LinkedList)
->> @LinkedList(10, @LinkedList(20))
+func main()
+ test_literals()
+ test_metamethods()
+ test_mixed()
-struct Password(text:Text; secret)
->> my_pass := Password("Swordfish")
-= Password(...)
->> users_by_password := {my_pass=> "User1", Password("xxx")=>"User2"}
-= {Password(...)=>"User1", Password(...)=>"User2"}
->> users_by_password[my_pass]
-= "User1"
+ >> @LinkedList(10, @LinkedList(20))
+
+ >> my_pass := Password("Swordfish")
+ = Password(...)
+ >> users_by_password := {my_pass=> "User1", Password("xxx")=>"User2"}
+ = {Password(...)=>"User1", Password(...)=>"User2"}
+ >> users_by_password[my_pass]
+ = "User1"
diff --git a/test/tables.tm b/test/tables.tm
index 74123f88..a55e4238 100644
--- a/test/tables.tm
+++ b/test/tables.tm
@@ -1,58 +1,58 @@
-
->> t := {"one"=>1, "two"=>2; default=999}
-= {"one"=>1, "two"=>2; default=999}
-
->> t["one"]
-= 1
->> t["two"]
-= 2
->> t["???"]
-= 999
-
-t_str := ""
-for k,v in t
- t_str ++= "({k}=>{v})"
->> t_str
-= "(one=>1)(two=>2)"
-
->> #t
-= 2
->> t.default
-= ?%999
->> t.fallback
-= !{Text=>Int}
-
->> t.keys
-= ["one", "two"]
->> t.values
-= [1, 2]
-
->> t2 := {"three"=>3; fallback=t}
-= {"three"=>3; fallback={"one"=>1, "two"=>2; default=999}}
-
->> t2["one"]
-= 1
->> t2["three"]
-= 3
->> t2["???"]
-= 999
-
->> #t2
-= 1
->> t2.default
-= !Int
->> t2.fallback
-= ?%{"one"=>1, "two"=>2; default=999}
-
-t2_str := ""
-for k,v in t2
- t2_str ++= "({k}=>{v})"
->> t2_str
-= "(three=>3)"
-
->> {i=>10*i for i in 5}
-= {1=>10, 2=>20, 3=>30, 4=>40, 5=>50}
->> {i=>10*i for i in 5 if i mod 2 != 0}
-= {1=>10, 3=>30, 5=>50}
->> {x=>10*x for x in y if x > 1 for y in [3, 4, 5] if y < 5}
-= {2=>20, 3=>30, 4=>40}
+func main()
+ >> t := {"one"=>1, "two"=>2; default=999}
+ = {"one"=>1, "two"=>2; default=999}
+
+ >> t["one"]
+ = 1
+ >> t["two"]
+ = 2
+ >> t["???"]
+ = 999
+
+ t_str := ""
+ for k,v in t
+ t_str ++= "({k}=>{v})"
+ >> t_str
+ = "(one=>1)(two=>2)"
+
+ >> #t
+ = 2
+ >> t.default
+ = ?%999
+ >> t.fallback
+ = !{Text=>Int}
+
+ >> t.keys
+ = ["one", "two"]
+ >> t.values
+ = [1, 2]
+
+ >> t2 := {"three"=>3; fallback=t}
+ = {"three"=>3; fallback={"one"=>1, "two"=>2; default=999}}
+
+ >> t2["one"]
+ = 1
+ >> t2["three"]
+ = 3
+ >> t2["???"]
+ = 999
+
+ >> #t2
+ = 1
+ >> t2.default
+ = !Int
+ >> t2.fallback
+ = ?%{"one"=>1, "two"=>2; default=999}
+
+ t2_str := ""
+ for k,v in t2
+ t2_str ++= "({k}=>{v})"
+ >> t2_str
+ = "(three=>3)"
+
+ >> {i=>10*i for i in 5}
+ = {1=>10, 2=>20, 3=>30, 4=>40, 5=>50}
+ >> {i=>10*i for i in 5 if i mod 2 != 0}
+ = {1=>10, 3=>30, 5=>50}
+ >> {x=>10*x for x in y if x > 1 for y in [3, 4, 5] if y < 5}
+ = {2=>20, 3=>30, 4=>40}
diff --git a/test/text.tm b/test/text.tm
index fee7e563..bb2cc14a 100644
--- a/test/text.tm
+++ b/test/text.tm
@@ -1,56 +1,57 @@
->> str := "Hello Amélie!"
->> str:upper()
-= "HELLO AMÉLIE!"
->> str:lower()
-= "hello amélie!"
->> str:lower():title()
-= "Hello Amélie!"
+func main()
+ >> str := "Hello Amélie!"
+ >> str:upper()
+ = "HELLO AMÉLIE!"
+ >> str:lower()
+ = "hello amélie!"
+ >> str:lower():title()
+ = "Hello Amélie!"
->> \UE9
-= "é"
+ >> \UE9
+ = "é"
->> \U65\U301
-= "é"
+ >> \U65\U301
+ = "é"
->> \UE9 == \U65\U301
-= yes
+ >> \UE9 == \U65\U301
+ = yes
->> amelie := "Am{\UE9}lie"
->> amelie:clusters()
-= ["A", "m", "é", "l", "i", "e"] : [Text]
->> amelie:codepoints()
-= [65_i32, 109_i32, 101_i32, 769_i32, 108_i32, 105_i32, 101_i32] : [Int32]
->> amelie:bytes()
-= [65_i8, 109_i8, 101_i8, -52_i8, -127_i8, 108_i8, 105_i8, 101_i8] : [Int8]
->> #amelie
-= 6
->> amelie:num_clusters()
-= 6
->> amelie:num_codepoints()
-= 7
->> amelie:num_bytes()
-= 8
+ >> amelie := "Am{\UE9}lie"
+ >> amelie:clusters()
+ = ["A", "m", "é", "l", "i", "e"] : [Text]
+ >> amelie:codepoints()
+ = [65_i32, 109_i32, 101_i32, 769_i32, 108_i32, 105_i32, 101_i32] : [Int32]
+ >> amelie:bytes()
+ = [65_i8, 109_i8, 101_i8, -52_i8, -127_i8, 108_i8, 105_i8, 101_i8] : [Int8]
+ >> #amelie
+ = 6
+ >> amelie:num_clusters()
+ = 6
+ >> amelie:num_codepoints()
+ = 7
+ >> amelie:num_bytes()
+ = 8
->> amelie2 := "Am{\U65\U301}lie"
->> amelie2:clusters()
-= ["A", "m", "é", "l", "i", "e"] : [Text]
->> amelie2:codepoints()
-= [65_i32, 109_i32, 101_i32, 769_i32, 108_i32, 105_i32, 101_i32] : [Int32]
->> amelie2:bytes()
-= [65_i8, 109_i8, 101_i8, -52_i8, -127_i8, 108_i8, 105_i8, 101_i8] : [Int8]
->> #amelie
-= 6
->> amelie2:num_clusters()
-= 6
->> amelie2:num_codepoints()
-= 7
->> amelie2:num_bytes()
-= 8
+ >> amelie2 := "Am{\U65\U301}lie"
+ >> amelie2:clusters()
+ = ["A", "m", "é", "l", "i", "e"] : [Text]
+ >> amelie2:codepoints()
+ = [65_i32, 109_i32, 101_i32, 769_i32, 108_i32, 105_i32, 101_i32] : [Int32]
+ >> amelie2:bytes()
+ = [65_i8, 109_i8, 101_i8, -52_i8, -127_i8, 108_i8, 105_i8, 101_i8] : [Int8]
+ >> #amelie
+ = 6
+ >> amelie2:num_clusters()
+ = 6
+ >> amelie2:num_codepoints()
+ = 7
+ >> amelie2:num_bytes()
+ = 8
->> amelie:character_names()
-= ["LATIN CAPITAL LETTER A", "LATIN SMALL LETTER M", "LATIN SMALL LETTER E", "COMBINING ACUTE ACCENT", "LATIN SMALL LETTER L", "LATIN SMALL LETTER I", "LATIN SMALL LETTER E"]
->> amelie2:character_names()
-= ["LATIN CAPITAL LETTER A", "LATIN SMALL LETTER M", "LATIN SMALL LETTER E", "COMBINING ACUTE ACCENT", "LATIN SMALL LETTER L", "LATIN SMALL LETTER I", "LATIN SMALL LETTER E"]
+ >> amelie:character_names()
+ = ["LATIN CAPITAL LETTER A", "LATIN SMALL LETTER M", "LATIN SMALL LETTER E", "COMBINING ACUTE ACCENT", "LATIN SMALL LETTER L", "LATIN SMALL LETTER I", "LATIN SMALL LETTER E"]
+ >> amelie2:character_names()
+ = ["LATIN CAPITAL LETTER A", "LATIN SMALL LETTER M", "LATIN SMALL LETTER E", "COMBINING ACUTE ACCENT", "LATIN SMALL LETTER L", "LATIN SMALL LETTER I", "LATIN SMALL LETTER E"]
->> "Hello":replace("e", "X")
-= "HXllo"
+ >> "Hello":replace("e", "X")
+ = "HXllo"
diff --git a/tomo.c b/tomo.c
index 37c6d4ee..07e9a1a6 100644
--- a/tomo.c
+++ b/tomo.c
@@ -31,12 +31,13 @@ static const char *cc;
static array_t get_file_dependencies(const char *filename);
static int transpile(const char *filename, bool force_retranspile);
static int compile_object_file(const char *filename, bool force_recompile);
-static int run_program(const char *filename, const char *object_files);
+static int compile_executable(const char *filename, const char *object_files);
int main(int argc, char *argv[])
{
mode_e mode = MODE_RUN;
const char *filename = NULL;
+ int program_arg_index = argc + 1;
for (int i = 1; i < argc; i++) {
if (streq(argv[i], "-t")) {
mode = MODE_TRANSPILE;
@@ -52,6 +53,7 @@ int main(int argc, char *argv[])
setenv(argv[i], eq + 1, 1);
} else {
filename = argv[i];
+ program_arg_index = i + 1;
break;
}
}
@@ -107,11 +109,23 @@ int main(int argc, char *argv[])
object_files_cord = object_files_cord ? CORD_all(object_files_cord, " ", dep, ".o") : CORD_cat(dep, ".o");
}
+ const char *object_files = CORD_to_const_char_star(object_files_cord);
+ assert(object_files);
+ int executable_status = compile_executable(filename, object_files);
+ if (executable_status != 0)
+ return executable_status;
+
if (mode == MODE_RUN) {
- const char *object_files = CORD_to_const_char_star(object_files_cord);
- assert(object_files);
- return run_program(filename, object_files);
+ char *exe_name = file_base_name(filename);
+ int num_args = argc - program_arg_index;
+ char *prog_args[num_args + 2];
+ prog_args[0] = exe_name;
+ for (int i = 0; i < num_args; i++)
+ prog_args[i+1] = argv[program_arg_index+i];
+ prog_args[num_args+1] = NULL;
+ execv(exe_name, prog_args);
}
+
return 0;
}
@@ -280,11 +294,10 @@ int compile_object_file(const char *filename, bool force_recompile)
return WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE;
}
-int run_program(const char *filename, const char *object_files)
+int compile_executable(const char *filename, const char *object_files)
{
const char *bin_name = file_base_name(filename);
- const char *run = streq(cc, "tcc") ? heap_strf("%s | tcc %s %s %s %s -run -", autofmt, cflags, ldflags, ldlibs, object_files)
- : heap_strf("%s | %s %s %s %s %s -x c - -o %s && ./%s", autofmt, cc, cflags, ldflags, ldlibs, object_files, bin_name, bin_name);
+ const char *run = heap_strf("%s | %s %s %s %s %s -x c - -o %s", autofmt, cc, cflags, ldflags, ldlibs, object_files, bin_name);
if (verbose)
printf("%s\n", run);
FILE *runner = popen(run, "w");
@@ -294,17 +307,18 @@ int run_program(const char *filename, const char *object_files)
"#include <tomo/tomo.h>\n"
"#include \"", filename, ".h\"\n"
"\n"
- "int main(int argc, const char *argv[]) {\n"
+ "int main(int argc, char *argv[]) {\n"
"(void)argc;\n"
"(void)argv;\n"
"GC_INIT();\n"
"srand(arc4random_uniform(UINT32_MAX));\n"
"srand48(arc4random_uniform(UINT32_MAX));\n"
"detect_color();\n",
- module_name, "$use();\n"
+ module_name, "$main$run(argc, argv);\n",
"return 0;\n"
"}\n"
);
+
if (verbose) {
FILE *out = popen(heap_strf("%s | bat -P --file-name=run.c", autofmt), "w");
CORD_put(program, out);