From 13a9304decb86450a2a0e9cc756da4c2d373c929 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Mon, 9 Sep 2024 00:22:12 -0400 Subject: Initial working version --- Makefile | 2 +- builtins/shell.c | 4 +- builtins/tomo.h | 1 + compile.c | 2 + environment.c | 5 +++ parse.c | 113 ++++++++++++++++++++++++++++++++++++++++++++----------- 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\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 #include @@ -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)) -- cgit v1.2.3