diff --git a/Makefile b/Makefile index cd543f6..46d4a72 100644 --- a/Makefile +++ b/Makefile @@ -27,6 +27,9 @@ srcs = \ src/get_command.c \ src/parser/matchers/identifier.c \ src/parser/matchers/metacharacter.c \ + src/parser/worddesc/worddesc.c \ + src/parser/wordlist/wordlist.c \ + src/parser/wordsplit/wordsplit.c \ objs = $(srcs:.c=.o) export objs diff --git a/src/parser/worddesc/worddesc.c b/src/parser/worddesc/worddesc.c new file mode 100644 index 0000000..e46f617 --- /dev/null +++ b/src/parser/worddesc/worddesc.c @@ -0,0 +1,33 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* worddesc.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: khais +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/02/13 17:20:36 by khais #+# #+# */ +/* Updated: 2025/02/13 17:23:11 by khais ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "worddesc.h" +#include "libft.h" +#include + +/* +** allocate a new worddesc with zeroed flags and the given word as word. +** +** return null in case of error, or if word is null +*/ +t_worddesc *worddesc_create(char *word) +{ + t_worddesc *retvalue; + + if (word == NULL) + return (NULL); + retvalue = ft_calloc(1, sizeof(t_worddesc)); + if (retvalue == NULL) + return (NULL); + retvalue->word = word; + return (retvalue); +} diff --git a/src/parser/worddesc/worddesc.h b/src/parser/worddesc/worddesc.h new file mode 100644 index 0000000..fbcb86a --- /dev/null +++ b/src/parser/worddesc/worddesc.h @@ -0,0 +1,33 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* worddesc.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: khais +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/02/13 15:47:58 by khais #+# #+# */ +/* Updated: 2025/02/13 17:20:25 by khais ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef WORDDESC_H +# define WORDDESC_H + +/* +** A logical word for the parser. +** +** See section 3.2.4 https://aosabook.org/en/v1/bash.html for more details. +** +** We are basically duplicating the way bash does it. +*/ +typedef struct s_worddesc +{ + /* + ** The word itself + */ + char *word; +} t_worddesc; + +t_worddesc *worddesc_create(char *word); + +#endif diff --git a/src/parser/wordlist/wordlist.c b/src/parser/wordlist/wordlist.c new file mode 100644 index 0000000..be888e3 --- /dev/null +++ b/src/parser/wordlist/wordlist.c @@ -0,0 +1,57 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* wordlist.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: khais +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/02/13 17:07:01 by khais #+# #+# */ +/* Updated: 2025/02/14 13:29:16 by khais ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "wordlist.h" +#include "libft.h" +#include + +/* +** create a new wordlist element, with next set to null and word set to the +** passed worddesc. +** +** in case of error, return null +*/ +t_wordlist *wordlist_create(t_worddesc *word) +{ + t_wordlist *retvalue; + + retvalue = ft_calloc(1, sizeof(t_wordlist)); + if (retvalue == NULL) + return (NULL); + retvalue->next = NULL; + retvalue->word = word; + return (retvalue); +} + +/* +** free all memory associated with this wordlist. +*/ +void wordlist_destroy(t_wordlist *wordlist) +{ + free(wordlist); +} + +/* +** get the worddesc at position idx in the given wordlist. +** +** return null if out of range or wordlist is null +*/ +t_worddesc *wordlist_get(t_wordlist *wordlist, int idx) +{ + if (wordlist == NULL || idx < 0) + return (NULL); + while (idx > 0 && wordlist != NULL) + wordlist = wordlist->next; + if (wordlist == NULL) + return (NULL); + return (wordlist->word); +} diff --git a/src/parser/wordlist/wordlist.h b/src/parser/wordlist/wordlist.h new file mode 100644 index 0000000..fbf8b46 --- /dev/null +++ b/src/parser/wordlist/wordlist.h @@ -0,0 +1,41 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* wordlist.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: khais +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/02/13 15:46:02 by khais #+# #+# */ +/* Updated: 2025/02/14 13:29:05 by khais ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef WORDLIST_H +# define WORDLIST_H + +# include "../worddesc/worddesc.h" + +/* +** Linked-list of WORDS +** +** cf. section 3.2.4 of https://aosabook.org/en/v1/bash.html +** +** we are copying how bash does it. +*/ +typedef struct s_wordlist +{ + /* + ** pointer to the next element in the list + */ + struct s_wordlist *next; + /* + ** pointer to the word at this position in the list + */ + struct s_worddesc *word; +} t_wordlist; + +t_wordlist *wordlist_create(t_worddesc *word); +void wordlist_destroy(t_wordlist *wordlist); +t_worddesc *wordlist_get(t_wordlist *wordlist, int idx); + +#endif diff --git a/src/parser/wordsplit/wordsplit.c b/src/parser/wordsplit/wordsplit.c new file mode 100644 index 0000000..a497995 --- /dev/null +++ b/src/parser/wordsplit/wordsplit.c @@ -0,0 +1,25 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* wordsplit.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: khais +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/02/13 17:02:32 by khais #+# #+# */ +/* Updated: 2025/02/14 13:28:38 by khais ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "wordsplit.h" +#include + +/* +** split a string into words, respecting quotes etc. +** +** cf. Token Recognition section at +** https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html +*/ +t_wordlist *minishell_wordsplit(char *original) +{ + return (wordlist_create(worddesc_create(original))); +} diff --git a/src/parser/wordsplit/wordsplit.h b/src/parser/wordsplit/wordsplit.h new file mode 100644 index 0000000..58e5d80 --- /dev/null +++ b/src/parser/wordsplit/wordsplit.h @@ -0,0 +1,20 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* wordsplit.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: khais +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/02/13 15:52:48 by khais #+# #+# */ +/* Updated: 2025/02/13 15:54:30 by khais ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef WORDSPLIT_H +# define WORDSPLIT_H + +# include "../wordlist/wordlist.h" + +t_wordlist *minishell_wordsplit(char *original); + +#endif diff --git a/tests/Makefile b/tests/Makefile index 6f7d52d..585b288 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -3,6 +3,7 @@ rawtests = \ test_env_manip \ metacharacters \ + word_splitting \ ifeq ($(CFLAGS),) CFLAGS = -Wall -Wextra -Werror -g diff --git a/tests/word_splitting.c b/tests/word_splitting.c new file mode 100644 index 0000000..12a50d8 --- /dev/null +++ b/tests/word_splitting.c @@ -0,0 +1,49 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* word_splitting.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: khais +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/02/13 15:17:56 by khais #+# #+# */ +/* Updated: 2025/02/13 17:24:33 by khais ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include +#include "testutil.h" +#include "../src/parser/wordsplit/wordsplit.h" +#include + +/* +** https://bash-hackers.gabe565.com/syntax/words/ +*/ +void test_wordsplit_singleword(void) +{ + t_wordlist *words; + + words = minishell_wordsplit("echo"); + assert_strequal("echo", wordlist_get(words, 0)->word); + assert(NULL == wordlist_get(words, 1)); + wordlist_destroy(words); +} + +void test_wordsplit_basic(void) +{ + t_wordlist *words; + + words = minishell_wordsplit("echo The file is named $MYFILE"); + assert_strequal("echo", wordlist_get(words, 0)->word); + assert_strequal("The", wordlist_get(words, 1)->word); + assert_strequal("file", wordlist_get(words, 2)->word); + assert_strequal("is", wordlist_get(words, 3)->word); + assert_strequal("named", wordlist_get(words, 4)->word); + assert_strequal("$MYFILE", wordlist_get(words, 5)->word); + assert(NULL == wordlist_get(words, 6)); + wordlist_destroy(words); +} + +int main(void) { + test_wordsplit_singleword(); + return (0); +}