| /* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include <ctype.h> |
| #include <stdlib.h> |
| #include <syslog.h> |
| |
| #include "array.h" |
| #include "cras_expr.h" |
| |
| static const char *copy_str(const char *begin, const char *end) |
| { |
| char *s = malloc(end - begin + 1); |
| memcpy(s, begin, end - begin); |
| s[end - begin] = '\0'; |
| return s; |
| } |
| |
| static void value_set_boolean(struct cras_expr_value *value, char boolean) |
| { |
| cras_expr_value_free(value); |
| value->type = CRAS_EXPR_VALUE_TYPE_BOOLEAN; |
| value->u.boolean = !!boolean; |
| } |
| |
| static void value_set_integer(struct cras_expr_value *value, int integer) |
| { |
| cras_expr_value_free(value); |
| value->type = CRAS_EXPR_VALUE_TYPE_INT; |
| value->u.integer = integer; |
| } |
| |
| static void value_set_string2(struct cras_expr_value *value, const char *begin, |
| const char *end) |
| { |
| cras_expr_value_free(value); |
| value->type = CRAS_EXPR_VALUE_TYPE_STRING; |
| value->u.string = copy_str(begin, end); |
| } |
| |
| static void value_set_string(struct cras_expr_value *value, const char *str) |
| { |
| value_set_string2(value, str, str + strlen(str)); |
| } |
| |
| static void cras_expr_value_set_function(struct cras_expr_value *value, |
| cras_expr_function_type function) |
| { |
| cras_expr_value_free(value); |
| value->type = CRAS_EXPR_VALUE_TYPE_FUNCTION; |
| value->u.function = function; |
| } |
| |
| static void copy_value(struct cras_expr_value *value, |
| struct cras_expr_value *original) |
| { |
| cras_expr_value_free(value); /* free the original value first */ |
| value->type = original->type; |
| switch (value->type) { |
| case CRAS_EXPR_VALUE_TYPE_NONE: |
| break; |
| case CRAS_EXPR_VALUE_TYPE_BOOLEAN: |
| value->u.boolean = original->u.boolean; |
| break; |
| case CRAS_EXPR_VALUE_TYPE_INT: |
| value->u.integer = original->u.integer; |
| break; |
| case CRAS_EXPR_VALUE_TYPE_STRING: |
| value->u.string = strdup(original->u.string); |
| break; |
| case CRAS_EXPR_VALUE_TYPE_FUNCTION: |
| value->u.function = original->u.function; |
| break; |
| } |
| } |
| |
| void cras_expr_value_free(struct cras_expr_value *value) |
| { |
| switch (value->type) { |
| case CRAS_EXPR_VALUE_TYPE_STRING: |
| free((char *)value->u.string); |
| value->u.string = NULL; |
| break; |
| case CRAS_EXPR_VALUE_TYPE_NONE: |
| case CRAS_EXPR_VALUE_TYPE_BOOLEAN: |
| case CRAS_EXPR_VALUE_TYPE_INT: |
| case CRAS_EXPR_VALUE_TYPE_FUNCTION: |
| break; |
| } |
| value->type = CRAS_EXPR_VALUE_TYPE_NONE; |
| } |
| |
| static struct cras_expr_value *find_value(struct cras_expr_env *env, |
| const char *name) |
| { |
| int i; |
| const char **key; |
| |
| FOR_ARRAY_ELEMENT(&env->keys, i, key) { |
| if (strcmp(*key, name) == 0) |
| return ARRAY_ELEMENT(&env->values, i); |
| } |
| return NULL; |
| } |
| |
| /* Insert a (key, value) pair to the environment. The value is |
| * initialized to zero. Return the pointer to value so it can be set |
| * to the proper value. */ |
| static struct cras_expr_value *insert_value(struct cras_expr_env *env, |
| const char *key) |
| { |
| *ARRAY_APPEND_ZERO(&env->keys) = strdup(key); |
| return ARRAY_APPEND_ZERO(&env->values); |
| } |
| |
| static struct cras_expr_value *find_or_insert_value(struct cras_expr_env *env, |
| const char *key) |
| { |
| struct cras_expr_value *value = find_value(env, key); |
| if (!value) |
| value = insert_value(env, key); |
| return value; |
| } |
| |
| static void function_not(cras_expr_value_array *operands, |
| struct cras_expr_value *result) |
| { |
| struct cras_expr_value *value; |
| int is_false; |
| |
| if (ARRAY_COUNT(operands) != 2) { |
| cras_expr_value_free(result); |
| syslog(LOG_ERR, "not takes one argument"); |
| return; |
| } |
| |
| value = ARRAY_ELEMENT(operands, 1); |
| is_false = (value->type == CRAS_EXPR_VALUE_TYPE_BOOLEAN && |
| !value->u.boolean); |
| value_set_boolean(result, is_false); |
| } |
| |
| static void function_and(cras_expr_value_array *operands, |
| struct cras_expr_value *result) |
| { |
| int i; |
| struct cras_expr_value *value; |
| int n = ARRAY_COUNT(operands); |
| |
| /* no operands -- return #t */ |
| if (n <= 1) { |
| value_set_boolean(result, 1); |
| return; |
| } |
| |
| /* if there is any #f, return it */ |
| FOR_ARRAY_ELEMENT(operands, i, value) { |
| if (i == 0) |
| continue; /* ignore "and" itself */ |
| if (value->type == CRAS_EXPR_VALUE_TYPE_BOOLEAN && |
| !value->u.boolean) { |
| value_set_boolean(result, 0); |
| return; |
| } |
| } |
| |
| /* otherwise return the last element */ |
| copy_value(result, ARRAY_ELEMENT(operands, n - 1)); |
| } |
| |
| static void function_or(cras_expr_value_array *operands, |
| struct cras_expr_value *result) |
| { |
| int i; |
| struct cras_expr_value *value; |
| |
| FOR_ARRAY_ELEMENT(operands, i, value) { |
| if (i == 0) |
| continue; /* ignore "or" itself */ |
| if (value->type != CRAS_EXPR_VALUE_TYPE_BOOLEAN || |
| value->u.boolean) { |
| copy_value(result, value); |
| return; |
| } |
| } |
| |
| value_set_boolean(result, 0); |
| } |
| |
| static char function_equal_real(cras_expr_value_array *operands) |
| { |
| int i; |
| struct cras_expr_value *value, *prev; |
| |
| FOR_ARRAY_ELEMENT(operands, i, value) { |
| if (i <= 1) |
| continue; /* ignore equal? and first operand */ |
| /* compare with the previous operand */ |
| |
| prev = ARRAY_ELEMENT(operands, i - 1); |
| |
| if (prev->type != value->type) |
| return 0; |
| |
| switch (prev->type) { |
| case CRAS_EXPR_VALUE_TYPE_NONE: |
| break; |
| case CRAS_EXPR_VALUE_TYPE_BOOLEAN: |
| if (prev->u.boolean != value->u.boolean) |
| return 0; |
| break; |
| case CRAS_EXPR_VALUE_TYPE_INT: |
| if (prev->u.integer != value->u.integer) |
| return 0; |
| break; |
| case CRAS_EXPR_VALUE_TYPE_STRING: |
| if (strcmp(prev->u.string, value->u.string) != 0) |
| return 0; |
| break; |
| case CRAS_EXPR_VALUE_TYPE_FUNCTION: |
| if (prev->u.function != value->u.function) |
| return 0; |
| break; |
| } |
| } |
| |
| return 1; |
| } |
| |
| static void function_equal(cras_expr_value_array *operands, |
| struct cras_expr_value *result) |
| { |
| value_set_boolean(result, function_equal_real(operands)); |
| } |
| |
| static void env_set_variable(struct cras_expr_env *env, const char *name, |
| struct cras_expr_value *new_value) |
| { |
| struct cras_expr_value *value = find_or_insert_value(env, name); |
| copy_value(value, new_value); |
| } |
| |
| void cras_expr_env_install_builtins(struct cras_expr_env *env) |
| { |
| struct cras_expr_value value = CRAS_EXPR_VALUE_INIT; |
| |
| /* initialize env with builtin functions */ |
| cras_expr_value_set_function(&value, &function_not); |
| env_set_variable(env, "not", &value); |
| |
| cras_expr_value_set_function(&value, &function_and); |
| env_set_variable(env, "and", &value); |
| |
| cras_expr_value_set_function(&value, &function_or); |
| env_set_variable(env, "or", &value); |
| |
| cras_expr_value_set_function(&value, &function_equal); |
| env_set_variable(env, "equal?", &value); |
| |
| cras_expr_value_free(&value); |
| } |
| |
| void cras_expr_env_set_variable_boolean(struct cras_expr_env *env, |
| const char *name, char boolean) |
| { |
| struct cras_expr_value *value = find_or_insert_value(env, name); |
| value_set_boolean(value, boolean); |
| } |
| |
| void cras_expr_env_set_variable_integer(struct cras_expr_env *env, |
| const char *name, int integer) |
| { |
| struct cras_expr_value *value = find_or_insert_value(env, name); |
| value_set_integer(value, integer); |
| } |
| |
| void cras_expr_env_set_variable_string(struct cras_expr_env *env, |
| const char *name, const char *str) |
| { |
| struct cras_expr_value *value = find_or_insert_value(env, name); |
| value_set_string(value, str); |
| } |
| |
| void cras_expr_env_free(struct cras_expr_env *env) |
| { |
| int i; |
| const char **key; |
| struct cras_expr_value *value; |
| |
| FOR_ARRAY_ELEMENT(&env->keys, i, key) { |
| free((char *)*key); |
| } |
| |
| FOR_ARRAY_ELEMENT(&env->values, i, value) { |
| cras_expr_value_free(value); |
| } |
| |
| ARRAY_FREE(&env->keys); |
| ARRAY_FREE(&env->values); |
| } |
| |
| static struct cras_expr_expression *new_boolean_literal(char boolean) |
| { |
| struct cras_expr_expression *expr; |
| expr = calloc(1, sizeof(struct cras_expr_expression)); |
| expr->type = EXPR_TYPE_LITERAL; |
| value_set_boolean(&expr->u.literal, boolean); |
| return expr; |
| } |
| |
| static struct cras_expr_expression *new_integer_literal(int integer) |
| { |
| struct cras_expr_expression *expr; |
| expr = calloc(1, sizeof(struct cras_expr_expression)); |
| expr->type = EXPR_TYPE_LITERAL; |
| value_set_integer(&expr->u.literal, integer); |
| return expr; |
| } |
| |
| static struct cras_expr_expression *new_string_literal(const char *begin, |
| const char *end) |
| { |
| struct cras_expr_expression *expr; |
| expr = calloc(1, sizeof(struct cras_expr_expression)); |
| expr->type = EXPR_TYPE_LITERAL; |
| value_set_string2(&expr->u.literal, begin, end); |
| return expr; |
| } |
| |
| static struct cras_expr_expression *new_variable(const char *begin, |
| const char *end) |
| { |
| struct cras_expr_expression *expr; |
| expr = calloc(1, sizeof(struct cras_expr_expression)); |
| expr->type = EXPR_TYPE_VARIABLE; |
| expr->u.variable = copy_str(begin, end); |
| return expr; |
| } |
| |
| static struct cras_expr_expression *new_compound_expression() |
| { |
| struct cras_expr_expression *expr; |
| expr = calloc(1, sizeof(struct cras_expr_expression)); |
| expr->type = EXPR_TYPE_COMPOUND; |
| return expr; |
| } |
| |
| static void add_sub_expression(struct cras_expr_expression *expr, |
| struct cras_expr_expression *sub) |
| { |
| ARRAY_APPEND(&expr->u.children, sub); |
| } |
| |
| static int is_identifier_char(char c) |
| { |
| if (isspace(c)) |
| return 0; |
| if (c == '\0') |
| return 0; |
| if (isalpha(c)) |
| return 1; |
| if (c == '_' || c == '-' || c == '?') |
| return 1; |
| return 0; |
| } |
| |
| static struct cras_expr_expression *parse_one_expr(const char **str) |
| { |
| /* skip whitespace */ |
| while (isspace(**str)) |
| (*str)++; |
| |
| if (**str == '\0') |
| return NULL; |
| |
| /* boolean literal: #t, #f */ |
| if (**str == '#') { |
| (*str)++; |
| char c = **str; |
| if (c == 't' || c == 'f') { |
| (*str)++; |
| return new_boolean_literal(c == 't'); |
| } else { |
| syslog(LOG_ERR, "unexpected char after #: '%c'", |
| c); |
| } |
| return NULL; |
| } |
| |
| /* integer literal: (-)[0-9]+ */ |
| if (isdigit(**str) || (**str == '-' && isdigit((*str)[1]))) |
| return new_integer_literal(strtol(*str, (char **)str, 10)); |
| |
| /* string literal: "..." */ |
| if (**str == '"') { |
| const char *begin = *str + 1; |
| const char *end = strchr(begin, '"'); |
| if (end == NULL) { |
| syslog(LOG_ERR, "no matching \""); |
| end = begin; |
| *str = begin; |
| } else { |
| *str = end + 1; |
| } |
| return new_string_literal(begin, end); |
| } |
| |
| /* compound expression: (expr1 expr2 ...) */ |
| if (**str == '(') { |
| (*str)++; |
| struct cras_expr_expression *expr = new_compound_expression(); |
| while (1) { |
| struct cras_expr_expression *next = parse_one_expr(str); |
| if (next == NULL) |
| break; |
| add_sub_expression(expr, next); |
| } |
| if (**str != ')') { |
| syslog(LOG_ERR, "no matching ): found '%c'", **str); |
| cras_expr_expression_free(expr); |
| return NULL; |
| } else { |
| (*str)++; |
| } |
| return expr; |
| } |
| |
| /* variable name */ |
| if (is_identifier_char(**str)) { |
| const char *begin = *str; |
| while (is_identifier_char(**str)) |
| (*str)++; |
| return new_variable(begin, *str); |
| } |
| |
| return NULL; |
| } |
| |
| struct cras_expr_expression *cras_expr_expression_parse(const char *str) |
| { |
| if (!str) |
| return NULL; |
| return parse_one_expr(&str); |
| } |
| |
| void cras_expr_expression_free(struct cras_expr_expression *expr) |
| { |
| if (!expr) |
| return; |
| |
| switch (expr->type) { |
| case EXPR_TYPE_NONE: |
| break; |
| case EXPR_TYPE_LITERAL: |
| cras_expr_value_free(&expr->u.literal); |
| break; |
| case EXPR_TYPE_VARIABLE: |
| free((char *)expr->u.variable); |
| break; |
| case EXPR_TYPE_COMPOUND: |
| { |
| int i; |
| struct cras_expr_expression **psub; |
| FOR_ARRAY_ELEMENT(&expr->u.children, i, psub) { |
| cras_expr_expression_free(*psub); |
| } |
| ARRAY_FREE(&expr->u.children); |
| break; |
| } |
| } |
| free(expr); |
| } |
| |
| void cras_expr_expression_eval(struct cras_expr_expression *expr, |
| struct cras_expr_env *env, |
| struct cras_expr_value *result) |
| { |
| cras_expr_value_free(result); |
| |
| switch (expr->type) { |
| case EXPR_TYPE_NONE: |
| break; |
| case EXPR_TYPE_LITERAL: |
| copy_value(result, &expr->u.literal); |
| break; |
| case EXPR_TYPE_VARIABLE: |
| { |
| struct cras_expr_value *value = find_value(env, |
| expr->u.variable); |
| if (value == NULL) { |
| syslog(LOG_ERR, "cannot find value for %s", |
| expr->u.variable); |
| } else { |
| copy_value(result, value); |
| } |
| break; |
| } |
| case EXPR_TYPE_COMPOUND: |
| { |
| int i; |
| struct cras_expr_expression **psub; |
| cras_expr_value_array values = ARRAY_INIT; |
| struct cras_expr_value *value; |
| |
| FOR_ARRAY_ELEMENT(&expr->u.children, i, psub) { |
| value = ARRAY_APPEND_ZERO(&values); |
| cras_expr_expression_eval(*psub, env, value); |
| } |
| |
| if (ARRAY_COUNT(&values) > 0) { |
| struct cras_expr_value *f = ARRAY_ELEMENT(&values, 0); |
| if (f->type == CRAS_EXPR_VALUE_TYPE_FUNCTION) |
| f->u.function(&values, result); |
| else |
| syslog(LOG_ERR, |
| "first element is not a function"); |
| } else { |
| syslog(LOG_ERR, "empty compound expression?"); |
| } |
| |
| FOR_ARRAY_ELEMENT(&values, i, value) { |
| cras_expr_value_free(value); |
| } |
| |
| ARRAY_FREE(&values); |
| break; |
| } |
| } |
| } |
| |
| int cras_expr_expression_eval_int(struct cras_expr_expression *expr, |
| struct cras_expr_env *env, |
| int *integer) |
| { |
| int rc = 0; |
| struct cras_expr_value value = CRAS_EXPR_VALUE_INIT; |
| |
| cras_expr_expression_eval(expr, env, &value); |
| if (value.type == CRAS_EXPR_VALUE_TYPE_INT) { |
| *integer = value.u.integer; |
| } else { |
| syslog(LOG_ERR, "value type is not integer (%d)", value.type); |
| rc = -1; |
| } |
| cras_expr_value_free(&value); |
| return rc; |
| } |
| |
| int cras_expr_expression_eval_boolean(struct cras_expr_expression *expr, |
| struct cras_expr_env *env, |
| char *boolean) |
| { |
| int rc = 0; |
| struct cras_expr_value value = CRAS_EXPR_VALUE_INIT; |
| |
| cras_expr_expression_eval(expr, env, &value); |
| if (value.type == CRAS_EXPR_VALUE_TYPE_BOOLEAN) { |
| *boolean = value.u.boolean; |
| } else { |
| syslog(LOG_ERR, "value type is not boolean (%d)", value.type); |
| rc = -1; |
| } |
| cras_expr_value_free(&value); |
| return rc; |
| } |