aboutsummaryrefslogtreecommitdiffhomepage
path: root/builtin_test.cpp
diff options
context:
space:
mode:
authorGravatar ridiculousfish <corydoras@ridiculousfish.com>2013-03-03 13:22:00 -0800
committerGravatar ridiculousfish <corydoras@ridiculousfish.com>2013-03-03 13:22:00 -0800
commit1d75c232ffa361256164f659929902aa16ef621c (patch)
tree6b5bb177da0c69e0ad9d66de457e62b7bc4862f2 /builtin_test.cpp
parentcbcc439e037ae07d3408f677a07f67002f5d4ad8 (diff)
Teach builtin_test about the special precedence rules in IEEE 1003.1
Diffstat (limited to 'builtin_test.cpp')
-rw-r--r--builtin_test.cpp177
1 files changed, 135 insertions, 42 deletions
diff --git a/builtin_test.cpp b/builtin_test.cpp
index a130ab99..3ed79a29 100644
--- a/builtin_test.cpp
+++ b/builtin_test.cpp
@@ -177,6 +177,8 @@ public:
{ }
expression *parse_expression(unsigned int start, unsigned int end);
+ expression *parse_3_arg_expression(unsigned int start, unsigned int end);
+ expression *parse_4_arg_expression(unsigned int start, unsigned int end);
expression *parse_combining_expression(unsigned int start, unsigned int end);
expression *parse_unary_expression(unsigned int start, unsigned int end);
@@ -539,14 +541,102 @@ expression *test_parser::parse_primary(unsigned int start, unsigned int end)
return expr;
}
+// See IEEE 1003.1 breakdown of the behavior for different parameter counts
+expression *test_parser::parse_3_arg_expression(unsigned int start, unsigned int end)
+{
+ assert(end - start == 3);
+ expression *result = NULL;
+
+ const token_info_t *center_token = token_for_string(arg(start + 1));
+ if (center_token->flags & BINARY_PRIMARY)
+ {
+ result = parse_binary_primary(start, end);
+ }
+ else if (center_token->tok == test_combine_and || center_token->tok == test_combine_or)
+ {
+ expr_ref_t left(parse_unary_expression(start, start + 1));
+ expr_ref_t right(parse_unary_expression(start + 2, start + 3));
+ if (left.get() && right.get())
+ {
+ // Transfer ownership to the vector of subjects
+ std::vector<token_t> combiners(1, center_token->tok);
+ std::vector<expression *> subjects;
+ subjects.push_back(left.release());
+ subjects.push_back(right.release());
+ result = new combining_expression(center_token->tok, range_t(start, end), subjects, combiners);
+ }
+ }
+ else
+ {
+ result = parse_unary_expression(start, end);
+ }
+ return result;
+}
+
+expression *test_parser::parse_4_arg_expression(unsigned int start, unsigned int end)
+{
+ assert(end - start == 4);
+ expression *result = NULL;
+
+ token_t first_token = token_for_string(arg(start))->tok;
+ if (first_token == test_bang)
+ {
+ expr_ref_t subject(parse_3_arg_expression(start + 1, end));
+ if (subject.get())
+ {
+ result = new unary_operator(first_token, range_t(start, subject->range.end), subject);
+ }
+ }
+ else if (first_token == test_paren_open)
+ {
+ result = parse_parenthentical(start, end);
+ }
+ else
+ {
+ result = parse_combining_expression(start, end);
+ }
+ return result;
+}
+
+
expression *test_parser::parse_expression(unsigned int start, unsigned int end)
{
if (start >= end)
{
return error(L"Missing argument at index %u", start);
}
-
- return parse_combining_expression(start, end);
+
+ unsigned int argc = end - start;
+ switch (argc)
+ {
+ case 0:
+ assert(0); //should have been caught by the above test
+ return NULL;
+
+ case 1:
+ {
+ return error(L"Missing argument at index %u", start + 1);
+ }
+ case 2:
+ {
+ return parse_unary_expression(start, end);
+ }
+
+ case 3:
+ {
+ return parse_3_arg_expression(start, end);
+ }
+
+ case 4:
+ {
+ return parse_4_arg_expression(start, end);
+ }
+
+ default:
+ {
+ return parse_combining_expression(start, end);
+ }
+ }
}
expression *test_parser::parse_args(const wcstring_list_t &args, wcstring &err)
@@ -798,18 +888,18 @@ static bool unary_primary_evaluate(test_expressions::token_t token, const wcstri
int builtin_test(parser_t &parser, wchar_t **argv)
{
using namespace test_expressions;
-
+
/* The first argument should be the name of the command ('test') */
if (! argv[0])
return BUILTIN_TEST_FAIL;
-
+
/* Whether we are invoked with bracket '[' or not */
const bool is_bracket = ! wcscmp(argv[0], L"[");
-
+
size_t argc = 0;
while (argv[argc + 1])
argc++;
-
+
/* If we're bracket, the last argument ought to be ]; we ignore it. Note that argc is the number of arguments after the command name; thus argv[argc] is the last argument. */
if (is_bracket)
{
@@ -823,54 +913,57 @@ int builtin_test(parser_t &parser, wchar_t **argv)
builtin_show_error(L"[: the last argument must be ']'\n");
return BUILTIN_TEST_FAIL;
}
-
+
}
-
+
/* Collect the arguments into a list */
const wcstring_list_t args(argv + 1, argv + 1 + argc);
-
- if (argc == 0)
- {
- // Per 1003.1, exit false
- return BUILTIN_TEST_FAIL;
- }
- else if (argc == 1)
- {
- // Per 1003.1, exit true if the arg is non-empty
- return args.at(0).empty() ? BUILTIN_TEST_FAIL : BUILTIN_TEST_SUCCESS;
- }
- else
+
+ switch (argc)
{
- // Try parsing. If expr is not nil, we are responsible for deleting it.
- wcstring err;
- expression *expr = test_parser::parse_args(args, err);
- if (! expr)
+ case 0:
{
-#if 0
- printf("Oops! test was given args:\n");
- for (size_t i=0; i < argc; i++)
- {
- printf("\t%ls\n", args.at(i).c_str());
- }
- printf("and returned parse error: %ls\n", err.c_str());
-#endif
- builtin_show_error(err);
+ // Per 1003.1, exit false
return BUILTIN_TEST_FAIL;
}
- else
+ case 1:
+ {
+ // Per 1003.1, exit true if the arg is non-empty
+ return args.at(0).empty() ? BUILTIN_TEST_FAIL : BUILTIN_TEST_SUCCESS;
+ }
+ default:
{
- wcstring_list_t eval_errors;
- bool result = expr->evaluate(eval_errors);
- if (! eval_errors.empty())
+ // Try parsing. If expr is not nil, we are responsible for deleting it.
+ wcstring err;
+ expression *expr = test_parser::parse_args(args, err);
+ if (! expr)
{
- printf("test returned eval errors:\n");
- for (size_t i=0; i < eval_errors.size(); i++)
+#if 0
+ printf("Oops! test was given args:\n");
+ for (size_t i=0; i < argc; i++)
{
- printf("\t%ls\n", eval_errors.at(i).c_str());
+ printf("\t%ls\n", args.at(i).c_str());
+ }
+ printf("and returned parse error: %ls\n", err.c_str());
+#endif
+ builtin_show_error(err);
+ return BUILTIN_TEST_FAIL;
+ }
+ else
+ {
+ wcstring_list_t eval_errors;
+ bool result = expr->evaluate(eval_errors);
+ if (! eval_errors.empty())
+ {
+ printf("test returned eval errors:\n");
+ for (size_t i=0; i < eval_errors.size(); i++)
+ {
+ printf("\t%ls\n", eval_errors.at(i).c_str());
+ }
}
+ delete expr;
+ return result ? BUILTIN_TEST_SUCCESS : BUILTIN_TEST_FAIL;
}
- delete expr;
- return result ? BUILTIN_TEST_SUCCESS : BUILTIN_TEST_FAIL;
}
}
return 1;