2 2

[PATCH 1/2] Introduce function definition task

Details
Message ID
<20181103231756.24362-1-sir@cmpwn.com>
DKIM signature
permerror
Download raw message
Patch: +79 -3
---
 include/mrsh/shell.h             |  5 ++++
 include/shell/task.h             |  7 +++--
 include/shell/tasks.h            |  1 +
 meson.build                      |  1 +
 shell/shell.c                    | 13 +++++++++
 shell/task/function_definition.c | 46 ++++++++++++++++++++++++++++++++
 shell/tasks.c                    |  9 ++++++-
 7 files changed, 79 insertions(+), 3 deletions(-)
 create mode 100644 shell/task/function_definition.c

diff --git a/include/mrsh/shell.h b/include/mrsh/shell.h
index 2d830eb..5b73562 100644
--- a/include/mrsh/shell.h
+++ b/include/mrsh/shell.h
@@ -69,6 +69,10 @@ struct mrsh_variable {
 	uint32_t attribs;
 };
 
+struct mrsh_function {
+	struct mrsh_command *body;
+};
+
 struct mrsh_state {
 	int exit;
 	uint32_t options; // enum mrsh_option
@@ -78,6 +82,7 @@ struct mrsh_state {
 	bool interactive;
 	struct mrsh_hashtable variables; // mrsh_variable *
 	struct mrsh_hashtable aliases; // char *
+	struct mrsh_hashtable functions; // mrsh_function *
 	int last_status;
 };
 
diff --git a/include/shell/task.h b/include/shell/task.h
index 2cb5baa..e84f4f9 100644
--- a/include/shell/task.h
+++ b/include/shell/task.h
@@ -70,8 +70,11 @@ struct task *task_if_clause_create(struct task *condition, struct task *body,
 struct task *task_loop_clause_create(struct mrsh_array *condition,
 		struct mrsh_array *body, bool until);
 
-struct task *task_for_clause_create(char *name, struct mrsh_array* word_list,
-		struct mrsh_array* body);
+struct task *task_for_clause_create(char *name, struct mrsh_array *word_list,
+		struct mrsh_array *body);
+
+struct task *task_function_definition_create(
+		char *name, struct mrsh_command *body);
 
 struct task *task_pipeline_create(void);
 void task_pipeline_add(struct task *task, struct task *child);
diff --git a/include/shell/tasks.h b/include/shell/tasks.h
index 9d0fd35..0c30bc2 100644
--- a/include/shell/tasks.h
+++ b/include/shell/tasks.h
@@ -12,6 +12,7 @@ struct task *task_for_if_clause(struct mrsh_if_clause *ic);
 struct task *task_for_loop_clause(struct mrsh_loop_clause *lc);
 struct task *task_for_command(struct mrsh_command *cmd);
 struct task *task_for_for_loop(struct mrsh_for_clause *fc);
+struct task *task_for_function_definition(struct mrsh_function_definition *fn);
 struct task *task_for_pipeline(struct mrsh_pipeline *pl);
 struct task *task_for_binop(struct mrsh_binop *binop);
 struct task *task_for_node(struct mrsh_node *node);
diff --git a/meson.build b/meson.build
index c324b48..63e7f4e 100644
--- a/meson.build
+++ b/meson.build
@@ -72,6 +72,7 @@ lib_mrsh = library(
 		'shell/task/list.c',
 		'shell/task/loop_clause.c',
 		'shell/task/for_clause.c',
+		'shell/task/function_definition.c',
 		'shell/task/pipeline.c',
 		'shell/task/subshell.c',
 		'shell/task/task.c',
diff --git a/shell/shell.c b/shell/shell.c
index 4fa9bba..7872745 100644
--- a/shell/shell.c
+++ b/shell/shell.c
@@ -33,9 +33,22 @@ static void state_var_finish_iterator(const char *key, void *value,
 	variable_destroy((struct mrsh_variable *)value);
 }
 
+static void function_destroy(struct mrsh_function *fn) {
+	if (!fn) {
+		return;
+	}
+	free(fn);
+}
+
+static void state_fn_finish_iterator(const char *key, void *value, void *_) {
+	function_destroy((struct mrsh_function *)value);
+}
+
 void mrsh_state_finish(struct mrsh_state *state) {
 	mrsh_hashtable_for_each(&state->variables, state_var_finish_iterator, NULL);
 	mrsh_hashtable_finish(&state->variables);
+	mrsh_hashtable_for_each(&state->functions, state_fn_finish_iterator, NULL);
+	mrsh_hashtable_finish(&state->functions);
 	mrsh_hashtable_for_each(&state->aliases,
 		state_string_finish_iterator, NULL);
 	mrsh_hashtable_finish(&state->aliases);
diff --git a/shell/task/function_definition.c b/shell/task/function_definition.c
new file mode 100644
index 0000000..a8693f5
--- /dev/null
+++ b/shell/task/function_definition.c
@@ -0,0 +1,46 @@
+#include "shell/task.h"
+#include "shell/tasks.h"
+#include <mrsh/hashtable.h>
+#include <stdlib.h>
+
+struct task_function_definition {
+	struct task task;
+	char *name;
+	struct mrsh_command *body;
+};
+
+static void task_function_definition_destroy(struct task *task) {
+	struct task_function_definition *tfn =
+		(struct task_function_definition *)task;
+	free(tfn->name);
+	free(tfn);
+}
+
+static int task_function_definition_poll(
+		struct task *task, struct context *ctx) {
+	struct task_function_definition *tfn =
+		(struct task_function_definition *)task;
+	struct mrsh_function *fn = calloc(1, sizeof(struct mrsh_function));
+	fn->body = tfn->body;
+	struct mrsh_function *oldfn = mrsh_hashtable_set(
+			&ctx->state->functions, tfn->name, fn);
+	if (oldfn) {
+		free(oldfn);
+	}
+	return 0;
+}
+
+static const struct task_interface task_function_definition_impl = {
+	.destroy = task_function_definition_destroy,
+	.poll = task_function_definition_poll,
+};
+
+struct task *task_function_definition_create(
+		char *name, struct mrsh_command *body) {
+	struct task_function_definition *tfn = calloc(
+			1, sizeof(struct task_function_definition));
+	task_init(&tfn->task, &task_function_definition_impl);
+	tfn->name = name;
+	tfn->body = body;
+	return (struct task *)tfn;
+}
diff --git a/shell/tasks.c b/shell/tasks.c
index a0e646a..6b4fb25 100644
--- a/shell/tasks.c
+++ b/shell/tasks.c
@@ -106,6 +106,10 @@ struct task *task_for_for_clause(struct mrsh_for_clause *fc) {
 	return task_for_clause_create(fc->name, &fc->word_list, &fc->body);
 }
 
+struct task *task_for_function_definition(struct mrsh_function_definition *fn) {
+	return task_function_definition_create(fn->name, fn->body);
+}
+
 struct task *task_for_command(struct mrsh_command *cmd) {
 	switch (cmd->type) {
 	case MRSH_SIMPLE_COMMAND:;
@@ -126,8 +130,11 @@ struct task *task_for_command(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_FUNCTION_DEFINITION:;
+		struct mrsh_function_definition *fn =
+			mrsh_command_get_function_definition(cmd);
+		return task_for_function_definition(fn);
 	case MRSH_CASE_CLAUSE:
-	case MRSH_FUNCTION_DEFINITION:
 		assert(false); // TODO: implement this
 	}
 	assert(false);
-- 
-- 
2.19.1

[PATCH 2/2] Initial implementation of functions

Details
Message ID
<20181103231756.24362-2-sir@cmpwn.com>
In-Reply-To
<20181103231756.24362-1-sir@cmpwn.com> (view parent)
DKIM signature
permerror
Download raw message
Patch: +62 -7
Still to come:

- I/O redirection
- Pushing argv/argc
- return
---
 include/shell/task.h             |  2 +-
 shell/task/command.c             | 30 ++++++++++++++++++++++++++++--
 shell/task/function_definition.c |  7 +++----
 test/function.sh                 | 29 +++++++++++++++++++++++++++++
 test/meson.build                 |  1 +
 5 files changed, 62 insertions(+), 7 deletions(-)
 create mode 100644 test/function.sh

diff --git a/include/shell/task.h b/include/shell/task.h
index e84f4f9..932a86f 100644
--- a/include/shell/task.h
+++ b/include/shell/task.h
@@ -74,7 +74,7 @@ struct task *task_for_clause_create(char *name, struct mrsh_array *word_list,
 		struct mrsh_array *body);
 
 struct task *task_function_definition_create(
-		char *name, struct mrsh_command *body);
+		const char *name, struct mrsh_command *body);
 
 struct task *task_pipeline_create(void);
 void task_pipeline_add(struct task *task, struct task *child);
diff --git a/shell/task/command.c b/shell/task/command.c
index 4404226..07b852e 100644
--- a/shell/task/command.c
+++ b/shell/task/command.c
@@ -11,6 +11,7 @@
 #include "shell/process.h"
 #include "shell/shm.h"
 #include "shell/task.h"
+#include "shell/tasks.h"
 
 struct task_command {
 	struct task task;
@@ -19,8 +20,11 @@ struct task_command {
 	bool builtin;
 	struct mrsh_array args;
 
-	// only if not a builtin
+	// if a process
 	struct process process;
+	// if a function
+	struct mrsh_function *fn_def;
+	struct task *fn_task;
 };
 
 static void task_command_destroy(struct task *task) {
@@ -110,6 +114,25 @@ static void get_args(struct mrsh_array *args, struct mrsh_simple_command *sc,
 	mrsh_array_add(args, NULL);
 }
 
+static int task_function_start(struct task_command *tc, struct context *ctx) {
+	// TODO: Push new $@/$#
+	tc->fn_task = task_for_command(tc->fn_def->body);
+	return tc->fn_task != NULL;
+}
+
+static int task_function_poll(struct task *task, struct context *ctx) {
+	struct task_command *tc = (struct task_command *)task;
+
+	if (!tc->started) {
+		if (!task_function_start(tc, ctx)) {
+			return TASK_STATUS_ERROR;
+		}
+		tc->started = true;
+	}
+
+	return task_poll(tc->fn_task, ctx);
+}
+
 static int task_builtin_poll(struct task *task, struct context *ctx) {
 	struct task_command *tc = (struct task_command *)task;
 
@@ -271,9 +294,12 @@ static int task_command_poll(struct task *task, struct context *ctx) {
 		get_args(&tc->args, sc, ctx);
 		const char *argv_0 = (char *)tc->args.data[0];
 		tc->builtin = mrsh_has_builtin(argv_0);
+		tc->fn_def = mrsh_hashtable_get(&ctx->state->functions, argv_0);
 	}
 
-	if (tc->builtin) {
+	if (tc->fn_def) {
+		return task_function_poll(task, ctx);
+	} else if (tc->builtin) {
 		return task_builtin_poll(task, ctx);
 	} else {
 		return task_process_poll(task, ctx);
diff --git a/shell/task/function_definition.c b/shell/task/function_definition.c
index a8693f5..82c6b30 100644
--- a/shell/task/function_definition.c
+++ b/shell/task/function_definition.c
@@ -5,14 +5,13 @@
 
 struct task_function_definition {
 	struct task task;
-	char *name;
+	const char *name;
 	struct mrsh_command *body;
 };
 
 static void task_function_definition_destroy(struct task *task) {
 	struct task_function_definition *tfn =
 		(struct task_function_definition *)task;
-	free(tfn->name);
 	free(tfn);
 }
 
@@ -21,7 +20,7 @@ static int task_function_definition_poll(
 	struct task_function_definition *tfn =
 		(struct task_function_definition *)task;
 	struct mrsh_function *fn = calloc(1, sizeof(struct mrsh_function));
-	fn->body = tfn->body;
+	fn->body = mrsh_command_copy(tfn->body);
 	struct mrsh_function *oldfn = mrsh_hashtable_set(
 			&ctx->state->functions, tfn->name, fn);
 	if (oldfn) {
@@ -36,7 +35,7 @@ static const struct task_interface task_function_definition_impl = {
 };
 
 struct task *task_function_definition_create(
-		char *name, struct mrsh_command *body) {
+		const char *name, struct mrsh_command *body) {
 	struct task_function_definition *tfn = calloc(
 			1, sizeof(struct task_function_definition));
 	task_init(&tfn->task, &task_function_definition_impl);
diff --git a/test/function.sh b/test/function.sh
new file mode 100644
index 0000000..631074f
--- /dev/null
+++ b/test/function.sh
@@ -0,0 +1,29 @@
+#!/bin/sh -e
+func_a() {
+	echo func_a
+}
+
+func_b() {
+	echo func_b
+}
+
+func_b() {
+	echo func_b revised
+}
+
+func_c() {
+	echo func_c
+
+	func_c() {
+		echo func_c revised
+	}
+}
+
+func_a
+func_b
+func_c
+func_c
+
+output=$(func_a)
+
+[ "$output" = "func_a" ]
diff --git a/test/meson.build b/test/meson.build
index ecf3de6..29516f7 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -8,6 +8,7 @@ test_files = [
 	'subshell.sh',
 	'word.sh',
 	'for.sh',
+	'function.sh',
 ]
 
 foreach test_file : test_files
-- 
-- 
2.19.1

Re: [PATCH 2/2] Initial implementation of functions

Details
Message ID
<S9R4IKKHiKhdx7f1Htg1gkGawxd8l51F6rYujk-LDss6y-TZi4dGqXOdRQ4sqZJqnxlmQFxmHELoCpoqn9KvAEcLGFBiHE4o7tCoR7yoFLQ=@emersion.fr>
In-Reply-To
<20181103231756.24362-2-sir@cmpwn.com> (view parent)
DKIM signature
pass
Download raw message
LGTM, both pushed:

To git.sr.ht:~emersion/mrsh
   3d50633..fd9074d  master -> master

With a minor fix to task_function_start's return type (int → bool).

Thanks!