diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2024-09-06 11:37:33 -0400 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2024-09-06 11:37:33 -0400 |
| commit | 4b352f89a309dd2405f782062476202829beee37 (patch) | |
| tree | 113295ef0d7d875c4bab07c4386b633939789868 | |
| parent | 5221584d2836769cb6bd714a601351117eb35a70 (diff) | |
Add Shell dsl type
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | builtins/shell.c | 67 | ||||
| -rw-r--r-- | builtins/shell.h | 22 | ||||
| -rw-r--r-- | builtins/tomo.h | 1 | ||||
| -rw-r--r-- | compile.c | 2 | ||||
| -rw-r--r-- | environment.c | 4 | ||||
| -rw-r--r-- | parse.c | 4 |
7 files changed, 101 insertions, 1 deletions
@@ -26,7 +26,7 @@ CFLAGS=$(CCONFIG) $(EXTRA) $(CWARN) $(G) $(O) $(OSFLAGS) 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/types.o builtins/util.o builtins/files.o builtins/range.o builtins/shell.o TESTS=$(patsubst %.tm,%.tm.testresult,$(wildcard test/*.tm)) all: libtomo.so tomo diff --git a/builtins/shell.c b/builtins/shell.c new file mode 100644 index 00000000..da6cbe5b --- /dev/null +++ b/builtins/shell.c @@ -0,0 +1,67 @@ +// Boolean methods/type info +#include <stdbool.h> +#include <stdint.h> + +#include "array.h" +#include "functions.h" +#include "integers.h" +#include "shell.h" +#include "text.h" +#include "types.h" +#include "util.h" + +public Pattern_t Shell$escape_text(Text_t text) +{ + // TODO: optimize for ASCII and short strings + Array_t shell_graphemes = {.atomic=1}; +#define add_char(c) Array$insert(&shell_graphemes, (uint32_t[1]){c}, I_small(0), sizeof(uint32_t)) + add_char('\''); + const char *text_utf8 = Text$as_c_string(text); + for (const char *p = text_utf8; *p; p++) { + if (*p == '\'') { + add_char('\''); + add_char('"'); + add_char('\''); + add_char('"'); + add_char('\''); + } else + add_char(*p); + } + add_char('\''); +#undef add_char + return (Text_t){.length=shell_graphemes.length, .tag=TEXT_GRAPHEMES, .graphemes=shell_graphemes.data}; +} + +public Text_t Shell$run(Shell_t command, int32_t *status) +{ + const char *cmd_str = Text$as_c_string(command); + FILE *prog = popen(cmd_str, "r"); + + const int chunk_size = 256; + char *buf = GC_MALLOC_ATOMIC(chunk_size); + Text_t output = Text(""); + size_t just_read; + do { + just_read = fread(buf, sizeof(char), chunk_size, prog); + if (just_read > 0) { + output = Texts(output, Text$from_strn(buf, just_read)); + buf = GC_MALLOC_ATOMIC(chunk_size); + } + } while (just_read > 0); + + if (status) + *status = WEXITSTATUS(pclose(prog)); + else + pclose(prog); + + return Text$trim(output, Pattern("{1 nl}"), false, true); +} + +public const TypeInfo Shell$info = { + .size=sizeof(Shell_t), + .align=__alignof__(Shell_t), + .tag=TextInfo, + .TextInfo={.lang="Shell"}, +}; + +// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/builtins/shell.h b/builtins/shell.h new file mode 100644 index 00000000..0429d496 --- /dev/null +++ b/builtins/shell.h @@ -0,0 +1,22 @@ +#pragma once + +// A lang for Shell Command Language + +#include <gc/cord.h> +#include <stdbool.h> +#include <stdint.h> + +#include "types.h" +#include "datatypes.h" + +#define Shell_t Text_t +#define Shell(text) ((Shell_t)Text(text)) +#define Shells(...) ((Shell_t)Texts(__VA_ARGS__)) + +Text_t Shell$run(Shell_t command, int32_t *status); +Shell_t Shell$escape_text(Text_t text); + +extern const TypeInfo Shell$info; + +// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 + diff --git a/builtins/tomo.h b/builtins/tomo.h index 8d6661ca..68e115bb 100644 --- a/builtins/tomo.h +++ b/builtins/tomo.h @@ -22,6 +22,7 @@ #include "nums.h" #include "pointer.h" #include "range.h" +#include "shell.h" #include "siphash.h" #include "table.h" #include "text.h" @@ -2994,6 +2994,8 @@ CORD compile_type_info(env_t *env, type_t *t) return "&Text$info"; else if (streq(text->lang, "Pattern")) return "&Pattern$info"; + else if (streq(text->lang, "Shell")) + return "&Shell$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 666149cc..39a3e24a 100644 --- a/environment.c +++ b/environment.c @@ -243,6 +243,10 @@ 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"}, )}, + {"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"}, + )}, {"Text", TEXT_TYPE, "Text_t", "Text$info", TypedArray(ns_entry_t, {"find", "Text$find", "func(text:Text, pattern:Pattern, start=1, length=!&Int64)->Int"}, {"find_all", "Text$find_all", "func(text:Text, pattern:Pattern)->[Text]"}, @@ -1185,6 +1185,8 @@ PARSER(parse_text) { } else if (strchr(interp_chars, *pos)) { open_interp = *pos; ++pos; + } else if (*pos == '(') { + open_interp = '@'; // For shell commands } static const char *quote_chars = "\"'`|/;([{<"; if (!strchr(quote_chars, *pos)) @@ -1195,6 +1197,8 @@ PARSER(parse_text) { if (!lang && open_quote == '/') lang = "Pattern"; + else if (!lang && open_quote == '(') + lang = "Shell"; } else { return NULL; } |
