feat(expansion): parse_var_name and tests
This commit is contained in:
parent
ccb9438d69
commit
dadbebfceb
3 changed files with 223 additions and 28 deletions
|
|
@ -1,3 +1,4 @@
|
||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
@ -6,13 +7,75 @@
|
||||||
|
|
||||||
#include "../utils/ast/ast.h"
|
#include "../utils/ast/ast.h"
|
||||||
|
|
||||||
// static size_t var_len(char *start)
|
static bool is_var_start_char(char c)
|
||||||
// {
|
{
|
||||||
// char *iter = start;
|
return isalpha(c) || c == '_';
|
||||||
// while (*iter != ' ' && *iter != 0)
|
}
|
||||||
// *iter++;
|
|
||||||
// return iter - start;
|
static bool is_var_char(char c)
|
||||||
// }
|
{
|
||||||
|
return isalnum(c) || c == '_';
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_special_var_char(char c)
|
||||||
|
{
|
||||||
|
return c == '@' || c == '*' || c == '?' || c == '$' || isdigit(c)
|
||||||
|
|| c == '#';
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t parse_var_name(char *str, char **res)
|
||||||
|
{
|
||||||
|
char *brace = NULL;
|
||||||
|
size_t i = 1; // skip the '$'
|
||||||
|
|
||||||
|
if (str[i] == '{' && str[i + 1] != 0 && str[i + 1] != '}')
|
||||||
|
{
|
||||||
|
if (is_special_var_char(str[i + 1]) && str[i + 2] == '}')
|
||||||
|
{
|
||||||
|
// Special variable like ${1}, ${?}
|
||||||
|
*res = strndup(str + i + 1, 1);
|
||||||
|
return 4; // length of ${X}
|
||||||
|
}
|
||||||
|
|
||||||
|
brace = str + i;
|
||||||
|
i++; // skip the '{'
|
||||||
|
}
|
||||||
|
else if (is_special_var_char(str[i]))
|
||||||
|
{
|
||||||
|
*res = strndup(str + i, 1);
|
||||||
|
return 2; // length of $X
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_var_start_char(str[i]))
|
||||||
|
{
|
||||||
|
// Not a valid variable start
|
||||||
|
*res = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
if (str[i] == '}' && *brace == '{')
|
||||||
|
{
|
||||||
|
*res = strndup(str + 2, i - 2);
|
||||||
|
return i + 1;
|
||||||
|
}
|
||||||
|
else if (!is_var_char(str[i]))
|
||||||
|
{
|
||||||
|
if (brace != NULL)
|
||||||
|
{
|
||||||
|
// Missing closing '}'
|
||||||
|
*res = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
*res = strndup(str + 1, i - 1);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
struct ast_command *expand(struct ast_command *command)
|
struct ast_command *expand(struct ast_command *command)
|
||||||
{
|
{
|
||||||
|
|
@ -34,21 +97,18 @@ struct ast_command *expand(struct ast_command *command)
|
||||||
{
|
{
|
||||||
if (in_quotes)
|
if (in_quotes)
|
||||||
{
|
{
|
||||||
// do nothing
|
continue; // do nothing
|
||||||
}
|
}
|
||||||
else if (str[i] == '\'')
|
else if (str[i] == '\'')
|
||||||
{
|
{
|
||||||
|
// remove quote
|
||||||
in_quotes = !in_quotes;
|
in_quotes = !in_quotes;
|
||||||
memmove(&str[i], &str[i + 1], strlen(&str[i + 1]) + 1);
|
memmove(&str[i], &str[i + 1], strlen(&str[i + 1]) + 1);
|
||||||
|
i--;
|
||||||
}
|
}
|
||||||
else if (str[i] == '$' && str[i + 1] != 0 && !isspace(str[i + 1]))
|
else if (str[i] == '$' && str[i + 1] != 0 && !isspace(str[i + 1]))
|
||||||
{
|
{
|
||||||
// size_t len = var_len(str + i + 1);
|
// variable expansion
|
||||||
// char *end = str + i + len + 1;
|
|
||||||
// char c = *end;
|
|
||||||
// *end = 0;
|
|
||||||
// printf("var: %s\n", str + i + 1);
|
|
||||||
// *end = c;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,17 +131,3 @@ struct ast_command *expand(struct ast_command *command)
|
||||||
}
|
}
|
||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
// int main()
|
|
||||||
// {
|
|
||||||
// printf("Expansion module test\n");
|
|
||||||
// struct ast_command ast_command;
|
|
||||||
// // char str[] = "echo Hello $?";
|
|
||||||
// char str[] = "echo Hello $AE86";
|
|
||||||
// ast_command.command = list_append(NULL, str);
|
|
||||||
|
|
||||||
// struct ast_command *command2 = expand(&ast_command);
|
|
||||||
// printf("command2: %s\n", (char *)command2->command->data);
|
|
||||||
|
|
||||||
// return 0;
|
|
||||||
// }
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,15 @@
|
||||||
#ifndef EXPANSION_H
|
#ifndef EXPANSION_H
|
||||||
#define EXPANSION_H
|
#define EXPANSION_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a variable from a string starting with '$'.
|
||||||
|
* @param str The input string starting with '$'. It must start with '$'.
|
||||||
|
* @param res Pointer to a char pointer that will be set to the extracted
|
||||||
|
* variable name.
|
||||||
|
* @return The number of characters processed in the input string.
|
||||||
|
*/
|
||||||
|
size_t parse_var_name(char *str, char **res);
|
||||||
|
|
||||||
#endif /* ! EXPANSION_H */
|
#endif /* ! EXPANSION_H */
|
||||||
|
|
|
||||||
138
tests/unit/expansion/parse_var.c
Normal file
138
tests/unit/expansion/parse_var.c
Normal file
|
|
@ -0,0 +1,138 @@
|
||||||
|
#include <criterion/criterion.h>
|
||||||
|
#include <criterion/new/assert.h>
|
||||||
|
#include <criterion/redirect.h>
|
||||||
|
|
||||||
|
#include "../../../src/expansion/expansion.h"
|
||||||
|
|
||||||
|
TestSuite(parse_var_name);
|
||||||
|
|
||||||
|
// char *input = "$MY$VAR";
|
||||||
|
// char *input = "$MY$VAR$";
|
||||||
|
// char *input = "$MY$VAR${}";
|
||||||
|
// char *input = "$MY$VAR${1}";
|
||||||
|
|
||||||
|
Test(parse_var_name, basic_variable)
|
||||||
|
{
|
||||||
|
char *input = "$MY_VAR";
|
||||||
|
char *extracted_var = NULL;
|
||||||
|
size_t r = parse_var_name(input, &extracted_var);
|
||||||
|
|
||||||
|
cr_expect(r == 7);
|
||||||
|
cr_expect_str_eq(extracted_var, "MY_VAR");
|
||||||
|
free(extracted_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(parse_var_name, multi_basic_variable)
|
||||||
|
{
|
||||||
|
char *input = "$MY$VAR";
|
||||||
|
char *extracted_var = NULL;
|
||||||
|
size_t r = parse_var_name(input, &extracted_var);
|
||||||
|
|
||||||
|
cr_expect(r == 3);
|
||||||
|
cr_expect_str_eq(extracted_var, "MY");
|
||||||
|
free(extracted_var);
|
||||||
|
|
||||||
|
input += r;
|
||||||
|
r = parse_var_name(input, &extracted_var);
|
||||||
|
|
||||||
|
cr_expect(r == 4);
|
||||||
|
cr_expect_str_eq(extracted_var, "VAR");
|
||||||
|
free(extracted_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(parse_var_name, variable_with_braces)
|
||||||
|
{
|
||||||
|
char *input = "${MY_VAR}";
|
||||||
|
char *extracted_var = NULL;
|
||||||
|
size_t r = parse_var_name(input, &extracted_var);
|
||||||
|
|
||||||
|
cr_expect(r == 9);
|
||||||
|
cr_expect_str_eq(extracted_var, "MY_VAR");
|
||||||
|
free(extracted_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(parse_var_name, special_variable)
|
||||||
|
{
|
||||||
|
char *input = "$1";
|
||||||
|
char *extracted_var = NULL;
|
||||||
|
size_t r = parse_var_name(input, &extracted_var);
|
||||||
|
|
||||||
|
cr_expect(r == 2);
|
||||||
|
cr_expect_str_eq(extracted_var, "1");
|
||||||
|
free(extracted_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(parse_var_name, incomplete_braces)
|
||||||
|
{
|
||||||
|
char *input = "${MY_VAR";
|
||||||
|
char *extracted_var = NULL;
|
||||||
|
size_t r = parse_var_name(input, &extracted_var);
|
||||||
|
|
||||||
|
cr_expect(r == 0);
|
||||||
|
cr_expect(extracted_var == NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(parse_var_name, empty_braces)
|
||||||
|
{
|
||||||
|
char *input = "${}";
|
||||||
|
char *extracted_var = NULL;
|
||||||
|
size_t r = parse_var_name(input, &extracted_var);
|
||||||
|
|
||||||
|
cr_expect(r == 0);
|
||||||
|
cr_expect(extracted_var == NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(parse_var_name, dollar_sign_only)
|
||||||
|
{
|
||||||
|
char *input = "$";
|
||||||
|
char *extracted_var = NULL;
|
||||||
|
size_t r = parse_var_name(input, &extracted_var);
|
||||||
|
|
||||||
|
cr_expect(r == 0);
|
||||||
|
cr_expect(extracted_var == NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(parse_var_name, variable_followed_by_dollar)
|
||||||
|
{
|
||||||
|
char *input = "$MY$VAR$";
|
||||||
|
char *extracted_var = NULL;
|
||||||
|
size_t r = parse_var_name(input, &extracted_var);
|
||||||
|
|
||||||
|
cr_expect(r == 3);
|
||||||
|
cr_expect_str_eq(extracted_var, "MY");
|
||||||
|
free(extracted_var);
|
||||||
|
|
||||||
|
input += r;
|
||||||
|
r = parse_var_name(input, &extracted_var);
|
||||||
|
|
||||||
|
cr_expect(r == 4);
|
||||||
|
cr_expect_str_eq(extracted_var, "VAR");
|
||||||
|
free(extracted_var);
|
||||||
|
|
||||||
|
input += r;
|
||||||
|
r = parse_var_name(input, &extracted_var);
|
||||||
|
|
||||||
|
cr_expect(r == 0);
|
||||||
|
cr_expect(extracted_var == NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(parse_var_name, special_variable_followed_by_text)
|
||||||
|
{
|
||||||
|
char *input = "$1VAR";
|
||||||
|
char *extracted_var = NULL;
|
||||||
|
size_t r = parse_var_name(input, &extracted_var);
|
||||||
|
|
||||||
|
cr_expect(r == 2);
|
||||||
|
cr_expect_str_eq(extracted_var, "1");
|
||||||
|
free(extracted_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(parse_var_name, bad_variable_with_braces)
|
||||||
|
{
|
||||||
|
char *input = "${1VAR}";
|
||||||
|
char *extracted_var = NULL;
|
||||||
|
size_t r = parse_var_name(input, &extracted_var);
|
||||||
|
|
||||||
|
cr_expect(r == 0);
|
||||||
|
cr_expect(extracted_var == NULL);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue