/* 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;
}