aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--builtins/shell.c4
-rw-r--r--builtins/tomo.h1
-rw-r--r--compile.c2
-rw-r--r--environment.c5
-rw-r--r--parse.c113
6 files changed, 103 insertions, 24 deletions
diff --git a/Makefile b/Makefile
index ea0cc6f5..03e1436e 100644
--- a/Makefile
+++ b/Makefile
@@ -30,7 +30,7 @@ CFLAGS_PLACEHOLDER="$$(echo -e '\033[2m<flags...>\033[m')"
LDLIBS=-lgc -lcord -lm -lunistring -lgmp -ldl
BUILTIN_OBJS=builtins/siphash.o builtins/array.o builtins/bool.o builtins/channel.o builtins/nums.o builtins/functions.o builtins/integers.o \
builtins/pointer.o builtins/memory.o builtins/text.o builtins/thread.o builtins/c_string.o builtins/table.o \
- builtins/types.o builtins/util.o builtins/files.o builtins/range.o builtins/shell.o
+ builtins/types.o builtins/util.o builtins/files.o builtins/range.o builtins/shell.o builtins/path.o
TESTS=$(patsubst %.tm,%.tm.testresult,$(wildcard test/*.tm))
all: libtomo.so tomo
diff --git a/builtins/shell.c b/builtins/shell.c
index 91cf792f..5bb34e83 100644
--- a/builtins/shell.c
+++ b/builtins/shell.c
@@ -1,4 +1,4 @@
-// Boolean methods/type info
+// A lang for Shell Command Language
#include <stdbool.h>
#include <stdint.h>
@@ -10,7 +10,7 @@
#include "types.h"
#include "util.h"
-public Pattern_t Shell$escape_text(Text_t text)
+public Shell_t Shell$escape_text(Text_t text)
{
// TODO: optimize for ASCII and short strings
Array_t shell_graphemes = {.atomic=1};
diff --git a/builtins/tomo.h b/builtins/tomo.h
index 68e115bb..c7814376 100644
--- a/builtins/tomo.h
+++ b/builtins/tomo.h
@@ -20,6 +20,7 @@
#include "macros.h"
#include "memory.h"
#include "nums.h"
+#include "path.h"
#include "pointer.h"
#include "range.h"
#include "shell.h"
diff --git a/compile.c b/compile.c
index 2c38ea77..9fd8dd59 100644
--- a/compile.c
+++ b/compile.c
@@ -3041,6 +3041,8 @@ CORD compile_type_info(env_t *env, type_t *t)
return "&Pattern$info";
else if (streq(text->lang, "Shell"))
return "&Shell$info";
+ else if (streq(text->lang, "Path"))
+ return "&Path$info";
return CORD_all("(&", namespace_prefix(text->env->libname, text->env->namespace->parent), text->lang, ")");
}
case StructType: {
diff --git a/environment.c b/environment.c
index 3cb47b90..d7f6c6f7 100644
--- a/environment.c
+++ b/environment.c
@@ -248,6 +248,11 @@ env_t *new_compilation_unit(CORD *libname)
{"Pattern", Type(TextType, .lang="Pattern", .env=namespace_env(env, "Pattern")), "Pattern_t", "Pattern$info", TypedArray(ns_entry_t,
{"escape_text", "Pattern$escape_text", "func(text:Text)->Pattern"},
)},
+ {"Path", Type(TextType, .lang="Path", .env=namespace_env(env, "Path")), "Text_t", "Text$info", TypedArray(ns_entry_t,
+ {"escape_text", "Path$escape_text", "func(text:Text)->Path"},
+ {"resolved", "Path$resolved", "func(path:Path, relative_to=./)->Path"},
+ {"relative", "Path$relative", "func(path:Path, relative_to=./)->Path"},
+ )},
{"Shell", Type(TextType, .lang="Shell", .env=namespace_env(env, "Shell")), "Shell_t", "Shell$info", TypedArray(ns_entry_t,
{"escape_text", "Shell$escape_text", "func(text:Text)->Shell"},
{"run", "Shell$run", "func(command:Shell, status=!&Int32?)->Text"},
diff --git a/parse.c b/parse.c
index c3490056..454df469 100644
--- a/parse.c
+++ b/parse.c
@@ -84,37 +84,37 @@ static ast_t *parse_comprehension_suffix(parse_ctx_t *ctx, ast_t *lhs);
static ast_t *parse_optional_conditional_suffix(parse_ctx_t *ctx, ast_t *lhs);
static ast_t *parse_optional_suffix(parse_ctx_t *ctx, ast_t *lhs);
static arg_ast_t *parse_args(parse_ctx_t *ctx, const char **pos, bool allow_unnamed);
+static type_ast_t *parse_type(parse_ctx_t *ctx, const char *pos);
static PARSER(parse_array);
+static PARSER(parse_block);
static PARSER(parse_channel);
-static PARSER(parse_for);
+static PARSER(parse_declaration);
static PARSER(parse_do);
-static PARSER(parse_while);
-static PARSER(parse_if);
-static PARSER(parse_when);
+static PARSER(parse_doctest);
+static PARSER(parse_enum_def);
static PARSER(parse_expr);
static PARSER(parse_extended_expr);
-static PARSER(parse_term_no_suffix);
-static PARSER(parse_term);
+static PARSER(parse_extern);
+static PARSER(parse_file_body);
+static PARSER(parse_for);
+static PARSER(parse_func_def);
+static PARSER(parse_if);
+static PARSER(parse_inline_c);
+static PARSER(parse_lang_def);
+static PARSER(parse_linker);
+static PARSER(parse_namespace);
+static PARSER(parse_path);
+static PARSER(parse_say);
static PARSER(parse_statement);
-static PARSER(parse_block);
-static PARSER(parse_var);
-static PARSER(parse_enum_def);
static PARSER(parse_struct_def);
-static PARSER(parse_lang_def);
+static PARSER(parse_term);
+static PARSER(parse_term_no_suffix);
static PARSER(parse_text);
-static PARSER(parse_func_def);
-static PARSER(parse_extern);
-static PARSER(parse_inline_c);
-static PARSER(parse_declaration);
static PARSER(parse_top_declaration);
-static PARSER(parse_doctest);
-static PARSER(parse_say);
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);
+static PARSER(parse_var);
+static PARSER(parse_when);
+static PARSER(parse_while);
//
// Print a parse error and exit (or use the on_err longjmp)
@@ -630,6 +630,7 @@ PARSER(parse_num) {
const char *start = pos;
bool negative = match(&pos, "-");
if (!isdigit(*pos) && *pos != '.') return NULL;
+ else if (*pos == '.' && !isdigit(pos[1])) return NULL;
size_t len = strspn(pos, "0123456789_");
if (strncmp(pos+len, "..", 2) == 0)
@@ -1295,6 +1296,75 @@ PARSER(parse_text) {
return NewAST(ctx->file, start, pos, TextJoin, .lang=lang, .children=chunks);
}
+PARSER(parse_path) {
+ // ("~/" / "./" / "../" / "/") *([^ \t\r\n\\;] / "\" .)
+ const char *start = pos;
+
+ if (!(match(&pos, "~/")
+ || match(&pos, "./")
+ || match(&pos, "../")
+ || match(&pos, "/")))
+ return NULL;
+
+ const char *chunk_start = start;
+ ast_list_t *chunks = NULL;
+ CORD chunk_text = CORD_EMPTY;
+ int depths[] = {[(int)'('] = 0, [(int)'{'] = 0, [(int)'['] = 0};
+ while (pos < ctx->file->text + ctx->file->len) {
+ switch (*pos) {
+ case '\r': case '\n': case ';': case ':': goto end_of_path;
+ case '\\': {
+ ++pos;
+ chunk_text = CORD_asprintf("%r%.*s%c", chunk_text, (size_t)(pos - chunk_start), chunk_start, *pos);
+ ++pos;
+ continue;
+ }
+ case '$': {
+ const char *interp_start = pos;
+ if (chunk_text) {
+ ast_t *literal = NewAST(ctx->file, chunk_start, pos, TextLiteral, .cord=chunk_text);
+ chunks = new(ast_list_t, .ast=literal, .next=chunks);
+ chunk_text = CORD_EMPTY;
+ }
+ ++pos;
+ if (*pos == ' ' || *pos == '\t')
+ parser_err(ctx, pos, pos+1, "Whitespace is not allowed before an interpolation here");
+ ast_t *interp = expect(ctx, interp_start, &pos, parse_term_no_suffix, "I expected an interpolation term here");
+ chunks = new(ast_list_t, .ast=interp, .next=chunks);
+ chunk_start = pos;
+ continue;
+ }
+ case ')': case '}': case ']': {
+ if (depths[(int)*pos] <= 0)
+ goto end_of_path;
+ depths[(int)*pos] -= 1;
+ ++pos;
+ continue;
+ }
+ case '(': case '{': case '[': {
+ depths[(int)*pos] += 1;
+ ++pos;
+ continue;
+ }
+ default: ++pos; continue;
+ }
+ }
+ end_of_path:;
+
+ if (pos > chunk_start)
+ chunk_text = CORD_asprintf("%r%.*s", chunk_text, (size_t)(pos - chunk_start), chunk_start);
+
+ if (chunk_text != CORD_EMPTY) {
+ ast_t *literal = NewAST(ctx->file, chunk_start, pos, TextLiteral, .cord=chunk_text);
+ chunks = new(ast_list_t, .ast=literal, .next=chunks);
+ }
+
+ match(&pos, ";"); // optional trailing semicolon
+
+ REVERSE_LIST(chunks);
+ return NewAST(ctx->file, start, pos, TextJoin, .lang="Path", .children=chunks);
+}
+
PARSER(parse_pass) {
const char *start = pos;
return match_word(&pos, "pass") ? NewAST(ctx->file, start, pos, Pass) : NULL;
@@ -1382,6 +1452,7 @@ PARSER(parse_term_no_suffix) {
|| (term=parse_stack_reference(ctx, pos))
|| (term=parse_bool(ctx, pos))
|| (term=parse_text(ctx, pos))
+ || (term=parse_path(ctx, pos))
|| (term=parse_lambda(ctx, pos))
|| (term=parse_parens(ctx, pos))
|| (term=parse_table(ctx, pos))