M include/shell/task.h => include/shell/task.h +2 -0
@@ 66,6 66,8 @@ struct task *task_loop_clause_create(const struct mrsh_array *condition,
const struct mrsh_array *body, bool until);
struct task *task_for_clause_create(const char *name,
const struct mrsh_array *word_list, const struct mrsh_array *body);
+struct task *task_case_clause_create(
+ const struct mrsh_word *word, const struct mrsh_array *cases);
struct task *task_function_definition_create(const char *name,
const struct mrsh_command *body);
struct task *task_binop_create(enum mrsh_binop_type type,
M meson.build => meson.build +1 -0
@@ 83,6 83,7 @@ lib_mrsh = library(
'shell/task/ast.c',
'shell/task/async.c',
'shell/task/binop.c',
+ 'shell/task/case_clause.c',
'shell/task/command_builtin.c',
'shell/task/command_function.c',
'shell/task/command_process.c',
M shell/task/ast.c => shell/task/ast.c +8 -2
@@ 107,6 107,10 @@ static struct task *task_for_for_clause(const struct mrsh_for_clause *fc) {
return task_for_clause_create(fc->name, &fc->word_list, &fc->body);
}
+static struct task *task_for_case_clause(const struct mrsh_case_clause *cc) {
+ return task_case_clause_create(cc->word, &cc->items);
+}
+
static struct task *task_for_function_definition(
const struct mrsh_function_definition *fn) {
return task_function_definition_create(fn->name, fn->body);
@@ 132,12 136,14 @@ struct task *task_for_command(const struct mrsh_command *cmd) {
case MRSH_FOR_CLAUSE:;
struct mrsh_for_clause *fc = mrsh_command_get_for_clause(cmd);
return task_for_for_clause(fc);
+ case MRSH_CASE_CLAUSE:;
+ struct mrsh_case_clause *cc =
+ mrsh_command_get_case_clause(cmd);
+ return task_for_case_clause(cc);
case MRSH_FUNCTION_DEFINITION:;
struct mrsh_function_definition *fn =
mrsh_command_get_function_definition(cmd);
return task_for_function_definition(fn);
- case MRSH_CASE_CLAUSE:
- assert(false); // TODO: implement this
}
assert(false);
}
A shell/task/case_clause.c => shell/task/case_clause.c +110 -0
@@ 0,0 1,110 @@
+#include "shell/task.h"
+#include <fnmatch.h>
+#include <stdlib.h>
+
+struct task_case_item {
+ struct task *body;
+ struct mrsh_array *patterns; // struct mrsh_word *
+ size_t index;
+ struct task *word;
+};
+
+struct task_case_clause {
+ struct task task;
+
+ struct {
+ struct mrsh_word *word;
+ } ast;
+ struct {
+ struct task *word;
+ } tasks;
+
+ char *word;
+ struct task_case_item *selected;
+ struct mrsh_array cases; // struct task_case_item *
+ size_t index;
+};
+
+static void task_case_clause_destroy(struct task *task) {
+ struct task_case_clause *tcc = (struct task_case_clause *)task;
+ for (size_t i = 0; i < tcc->cases.len; ++i) {
+ struct task_case_item *tci = tcc->cases.data[i];
+ task_destroy(tci->body);
+ free(tci);
+ }
+ mrsh_array_finish(&tcc->cases);
+ task_destroy(tcc->tasks.word);
+ mrsh_word_destroy(tcc->ast.word);
+ free(tcc->word);
+ free(tcc);
+}
+
+static int task_case_clause_poll(struct task *task, struct context *ctx) {
+ struct task_case_clause *tcc = (struct task_case_clause *)task;
+ if (tcc->tasks.word) {
+ int word_status = task_poll(tcc->tasks.word, ctx);
+ if (word_status < 0) {
+ return word_status;
+ }
+ tcc->word = mrsh_word_str(tcc->ast.word);
+ task_destroy(tcc->tasks.word);
+ tcc->tasks.word = NULL;
+ }
+
+ while (!tcc->selected && tcc->index < tcc->cases.len) {
+ struct task_case_item *tci =
+ (struct task_case_item *)tcc->cases.data[tcc->index];
+ if (tci->word) {
+ int word_status = task_poll(tci->word, ctx);
+ if (word_status < 0) {
+ return word_status;
+ }
+ struct mrsh_word_string *word = (struct mrsh_word_string *)
+ tci->patterns->data[tci->index - 1];
+ task_destroy(tci->word);
+ tci->word = NULL;
+ if (fnmatch(word->str, tcc->word, 0) == 0) {
+ tcc->selected = tci;
+ break;
+ }
+ if (tci->index == tci->patterns->len) {
+ ++tcc->index;
+ }
+ } else {
+ struct mrsh_word **word_ptr =
+ (struct mrsh_word **)&tci->patterns->data[tci->index++];
+ tci->word = task_word_create(word_ptr, TILDE_EXPANSION_NAME);
+ }
+ }
+
+ if (tcc->selected) {
+ return task_poll(tcc->selected->body, ctx);
+ }
+
+ return 0;
+}
+
+static const struct task_interface task_case_clause_impl = {
+ .destroy = task_case_clause_destroy,
+ .poll = task_case_clause_poll,
+};
+
+struct task *task_case_clause_create(
+ const struct mrsh_word *word, const struct mrsh_array *cases) {
+ struct task_case_clause *task = calloc(1, sizeof(struct task_case_clause));
+ task_init(&task->task, &task_case_clause_impl);
+ if (!mrsh_array_reserve(&task->cases, cases->len)) {
+ free(task);
+ return NULL;
+ }
+ for (size_t i = 0; i < cases->len; ++i) {
+ struct mrsh_case_item *mci = cases->data[i];
+ struct task_case_item *tci = calloc(1, sizeof(struct task_case_item));
+ mrsh_array_add(&task->cases, tci);
+ tci->patterns = &mci->patterns;
+ tci->body = task_for_command_list_array(&mci->body);
+ }
+ task->ast.word = mrsh_word_copy(word);
+ task->tasks.word = task_word_create(&task->ast.word, TILDE_EXPANSION_NAME);
+ return (struct task *)task;
+}
A test/case.sh => test/case.sh +74 -0
@@ 0,0 1,74 @@
+#!/bin/sh
+x=hello
+
+case "$x" in
+ hello)
+ echo pass
+ ;;
+ world)
+ echo fail
+ ;;
+esac
+
+case "$x" in
+ he*)
+ echo pass
+ ;;
+ *)
+ echo fail
+ ;;
+esac
+
+case "$x" in
+ foo)
+ echo fail
+ ;;
+ he??o)
+ echo pass
+ ;;
+esac
+
+case "$x" in
+ foo)
+ echo fail
+ ;;
+ *)
+ echo pass
+ ;;
+esac
+
+case "$x" in
+ world|hello)
+ echo pass
+ ;;
+ *)
+ echo fail
+ ;;
+esac
+
+case "$x" in
+ hell[a-z])
+ echo pass
+ ;;
+ *)
+ echo fail
+ ;;
+esac
+
+y=hello
+
+# Expanding patterns
+case "$x" in
+ $y)
+ echo pass
+ ;;
+ *)
+ echo fail
+ ;;
+esac
+
+# ;; optional for last item
+case hello in
+ *)
+ echo pass
+esac
M test/meson.build => test/meson.build +1 -0
@@ 4,6 4,7 @@ ref_sh = find_program('sh', required: false)
test_files = [
'conformance/if.sh',
+ 'case.sh',
'loop.sh',
'subshell.sh',
'word.sh',