This patch exercises the new expression infrastructure to implement syntax to concatenate and repeat strings. We use syntax inspired by Python, with '+' overloaded for string concatenation and '*' overloaded for string repeat. Normally we'd use C syntax to inspire dts syntax, but C has no obvious candidates for these string operators. Signed-off-by: David Gibson <david@xxxxxxxxxxxxxxxxxxxxx> --- dtc.h | 4 ++ expression.c | 117 ++++++++++++++++++++++++++++++++++++++++++++- tests/.gitignore | 1 + tests/Makefile.tests | 2 +- tests/run_tests.sh | 5 ++ tests/string-expressions.c | 97 +++++++++++++++++++++++++++++++++++++ 6 files changed, 223 insertions(+), 3 deletions(-) create mode 100644 tests/string-expressions.c diff --git a/dtc.h b/dtc.h index d270626..d3f8ad0 100644 --- a/dtc.h +++ b/dtc.h @@ -302,6 +302,10 @@ DEF_BINARY_OP(logic_and); DEF_BINARY_OP(logic_or); +struct expression *expression_concat(struct srcpos *pos, + struct expression *arg0, + struct expression *arg1); + struct expression *expression_conditional(struct srcpos *pos, struct expression *, struct expression *, diff --git a/expression.c b/expression.c index 8d6474b..2b86fed 100644 --- a/expression.c +++ b/expression.c @@ -294,9 +294,7 @@ INT_UNARY_OP(logic_not, !) INT_BINARY_OP(mod, %) INT_BINARY_OP(div, /) -INT_BINARY_OP(mul, *) -INT_BINARY_OP(add, +) INT_BINARY_OP(sub, -) INT_BINARY_OP(lshift, <<) @@ -317,6 +315,121 @@ INT_BINARY_OP(bit_or, |) INT_BINARY_OP(logic_and, &&) INT_BINARY_OP(logic_or, ||) +/* + * We need to write out add and mul in full, since they can be used on + * both integer and string arguments with different meanings + */ + +static struct expression_value op_eval_mul(struct expression *expr, + enum expr_type context) +{ + struct expression_value arg0, arg1; + struct expression_value v; + uint64_t n, i; + struct data s; + struct data d = empty_data; + + assert(expr->nargs == 2); + EVALUATE(arg0, expr->arg[0], EXPR_VOID); + EVALUATE(arg1, expr->arg[1], EXPR_VOID); + + if ((arg0.type == EXPR_INTEGER) && (arg1.type == EXPR_INTEGER)) { + v.type = EXPR_INTEGER; + v.value.integer = arg0.value.integer * arg1.value.integer; + return v; + } else if ((arg0.type != EXPR_INTEGER) && (arg0.type != EXPR_STRING)) { + return type_error(expr->arg[0], "Expected integer or string" + " expression (found %s)", + expression_typename(arg0.type)); + } else if (arg0.type == EXPR_INTEGER) { + if (arg1.type != EXPR_STRING) + return type_error(expr->arg[1], "Expected string" + " expression (found %s)", + expression_typename(arg1.type)); + n = arg0.value.integer; + s = arg1.value.d; + } else { + assert(arg0.type == EXPR_STRING); + if (arg1.type != EXPR_INTEGER) + return type_error(expr->arg[1], "Expected integer" + " expression (found %s)", + expression_typename(arg1.type)); + n = arg1.value.integer; + s = arg0.value.d; + } + + for (i = 0; i < n; i++) + d = data_append_data(d, s.val, s.len - 1); + + v.type = EXPR_STRING; + v.value.d = data_append_byte(d, 0); /* Terminating \0 */ + + return v; +} +static struct operator op_mul = { + .name = "*", + .evaluate = op_eval_mul, +}; +struct expression *expression_mul(struct srcpos *loc, + struct expression *arg0, + struct expression *arg1) +{ + return expression_build(loc, &op_mul, arg0, arg1); +} + +static struct expression_value op_eval_add(struct expression *expr, + enum expr_type context) +{ + struct expression_value arg0, arg1; + struct expression_value v; + + assert(expr->nargs == 2); + EVALUATE(arg0, expr->arg[0], EXPR_VOID); + EVALUATE(arg1, expr->arg[1], EXPR_VOID); + if ((arg0.type != EXPR_INTEGER) && (arg0.type != EXPR_STRING)) + return type_error(expr->arg[0], "Expected integer or string" + " expression (found %s)", + expression_typename(arg0.type)); + if ((arg1.type != EXPR_INTEGER) && (arg1.type != EXPR_STRING)) + return type_error(expr->arg[0], "Expected integer or string" + " expression (found %s)", + expression_typename(arg1.type)); + + if (arg0.type != arg1.type) + return type_error(expr, "Operand types to + (%s, %s) don't match", + expression_typename(arg0.type), + expression_typename(arg1.type)); + + v.type = arg0.type; + + switch (v.type) { + case EXPR_INTEGER: + v.value.integer = arg0.value.integer + arg1.value.integer; + break; + + case EXPR_STRING: + v.value.d = data_copy_mem(arg0.value.d.val, + arg0.value.d.len - 1); + v.value.d = data_append_data(v.value.d, arg1.value.d.val, + arg1.value.d.len); + break; + + default: + assert(0); + } + return v; +} +static struct operator op_add = { + .name = "+", + .evaluate = op_eval_add, +}; +struct expression *expression_add(struct srcpos *loc, + struct expression *arg0, + struct expression *arg1) +{ + return expression_build(loc, &op_add, arg0, arg1); +} + static struct expression_value op_eval_conditional(struct expression *expr, enum expr_type context) { diff --git a/tests/.gitignore b/tests/.gitignore index bb5e33a..7931067 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -48,6 +48,7 @@ tmp.* /setprop_inplace /sized_cells /string_escapes +/string-expressions /subnode_iterate /subnode_offset /supernode_atdepth_offset diff --git a/tests/Makefile.tests b/tests/Makefile.tests index dafb618..fa4e2d2 100644 --- a/tests/Makefile.tests +++ b/tests/Makefile.tests @@ -20,7 +20,7 @@ LIB_TESTS_L = get_mem_rsv \ dtb_reverse dtbs_equal_unordered \ add_subnode_with_nops path_offset_aliases \ utilfdt_test \ - integer-expressions \ + integer-expressions string-expressions \ subnode_iterate LIB_TESTS = $(LIB_TESTS_L:%=$(TESTS_PREFIX)%) diff --git a/tests/run_tests.sh b/tests/run_tests.sh index 97e016b..44de059 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -446,6 +446,11 @@ dtc_tests () { run_dtc_test -I dts -O dtb -o integer-expressions.test.dtb integer-expressions.test.dts run_test integer-expressions integer-expressions.test.dtb + # Check string expresisons + run_test string-expressions -g string-expressions.test.dts + run_dtc_test -I dts -O dtb -o string-expressions.test.dtb string-expressions.test.dts + run_test string-expressions string-expressions.test.dtb + # Check for graceful failure in some error conditions run_sh_test dtc-fatal.sh -I dts -O dtb nosuchfile.dts run_sh_test dtc-fatal.sh -I dtb -O dtb nosuchfile.dtb diff --git a/tests/string-expressions.c b/tests/string-expressions.c new file mode 100644 index 0000000..da6854f --- /dev/null +++ b/tests/string-expressions.c @@ -0,0 +1,97 @@ +/* + * Testcase for dtc string expression support + * + * Copyright (C) 2013 David Gibson <david@xxxxxxxxxxxxxxxxxxxxx> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <errno.h> + + +#include <libfdt.h> + +#include "tests.h" +#include "testdata.h" + +struct test_expr { + const char *expr; + const char *result; +} expr_table[] = { +#define TE(expr, res) { #expr, (res) } + TE("hello", "hello"), + TE("hello " + "world", "hello world"), + TE("hello" + " " + "world", "hello world"), + TE("hello" * 2 + " world", "hellohello world"), + TE("hello " + 2 * "world", "hello worldworld"), + TE(("hello"), "hello"), + TE(0 ? "hello" : "goodbye", "goodbye"), + TE(1 ? "hello" : "goodbye", "hello"), +}; + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +int main(int argc, char *argv[]) +{ + void *fdt; + const char *res; + int reslen; + int i; + + test_init(argc, argv); + + if ((argc == 3) && (strcmp(argv[1], "-g") == 0)) { + FILE *f = fopen(argv[2], "w"); + + if (!f) + FAIL("Couldn't open \"%s\" for output: %s\n", + argv[2], strerror(errno)); + + fprintf(f, "/dts-v1/;\n"); + fprintf(f, "/ {\n"); + for (i = 0; i < ARRAY_SIZE(expr_table); i++) + fprintf(f, "\texpression-%d = %s;\n", i, + expr_table[i].expr); + fprintf(f, "};\n"); + fclose(f); + } else { + fdt = load_blob_arg(argc, argv); + + for (i = 0; i < ARRAY_SIZE(expr_table); i++) { + char propname[16]; + int len = strlen(expr_table[i].result) + 1; + + sprintf(propname, "expression-%d", i); + res = fdt_getprop(fdt, 0, propname, &reslen); + + if (reslen != len) + FAIL("Incorrect length for expression %s," + " %d instead of %d\n", + expr_table[i].expr, reslen, len); + + if (memcmp(res, expr_table[i].result, len) != 0) + FAIL("Incorrect result for expression %s," + " \"%s\" instead of \"%s\"\n", + expr_table[i].expr, res, + expr_table[i].result); + } + } + + PASS(); +} -- 1.8.5.3 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html