974 lines
21 KiB
C
974 lines
21 KiB
C
|
/*
|
||
|
* Copyright (c) 2010, Swedish Institute of Computer Science
|
||
|
* All rights reserved.
|
||
|
*
|
||
|
* Redistribution and use in source and binary forms, with or without
|
||
|
* modification, are permitted provided that the following conditions
|
||
|
* are met:
|
||
|
* 1. Redistributions of source code must retain the above copyright
|
||
|
* notice, this list of conditions and the following disclaimer.
|
||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||
|
* notice, this list of conditions and the following disclaimer in the
|
||
|
* documentation and/or other materials provided with the distribution.
|
||
|
* 3. Neither the name of the Institute nor the names of its contributors
|
||
|
* may be used to endorse or promote products derived from this software
|
||
|
* without specific prior written permission.
|
||
|
*
|
||
|
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||
|
* SUCH DAMAGE.
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* \file
|
||
|
* Logic engine used for quickly evaluating data constraints in relations.
|
||
|
* \author
|
||
|
* Nicolas Tsiftes <nvt@sics.se>
|
||
|
*/
|
||
|
|
||
|
#include <limits.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include "aql.h"
|
||
|
#include "lvm.h"
|
||
|
|
||
|
#define DEBUG DEBUG_NONE
|
||
|
#include "debug.h"
|
||
|
|
||
|
/*
|
||
|
* The logic engine determines whether a logical predicate is true for
|
||
|
* each tuple in a relation. It uses a stack-based execution model of
|
||
|
* operations that are arranged in prefix (Polish) notation.
|
||
|
*/
|
||
|
|
||
|
/* Default option values. */
|
||
|
#ifndef LVM_MAX_NAME_LENGTH
|
||
|
#define LVM_MAX_NAME_LENGTH 16
|
||
|
#endif
|
||
|
|
||
|
#ifndef LVM_MAX_VARIABLE_ID
|
||
|
#define LVM_MAX_VARIABLE_ID 8
|
||
|
#endif
|
||
|
|
||
|
#ifndef LVM_USE_FLOATS
|
||
|
#define LVM_USE_FLOATS 0
|
||
|
#endif
|
||
|
|
||
|
#define IS_CONNECTIVE(op) ((op) & LVM_CONNECTIVE)
|
||
|
|
||
|
struct variable {
|
||
|
operand_type_t type;
|
||
|
operand_value_t value;
|
||
|
char name[LVM_MAX_NAME_LENGTH + 1];
|
||
|
};
|
||
|
typedef struct variable variable_t;
|
||
|
|
||
|
struct derivation {
|
||
|
operand_value_t max;
|
||
|
operand_value_t min;
|
||
|
uint8_t derived;
|
||
|
};
|
||
|
typedef struct derivation derivation_t;
|
||
|
|
||
|
/* Registered variables for a LVM expression. Their values may be
|
||
|
changed between executions of the expression. */
|
||
|
static variable_t variables[LVM_MAX_VARIABLE_ID - 1];
|
||
|
|
||
|
/* Range derivations of variables that are used for index searches. */
|
||
|
static derivation_t derivations[LVM_MAX_VARIABLE_ID - 1];
|
||
|
|
||
|
#if DEBUG
|
||
|
static void
|
||
|
print_derivations(derivation_t *d)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for(i = 0; i < LVM_MAX_VARIABLE_ID; i++) {
|
||
|
if(d[i].derived) {
|
||
|
printf("%s is constrained to (%ld,%ld)\n", variables[i].name,
|
||
|
d[i].min.l, d[i].max.l);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif /* DEBUG */
|
||
|
|
||
|
static variable_id_t
|
||
|
lookup(char *name)
|
||
|
{
|
||
|
variable_t *var;
|
||
|
|
||
|
for(var = variables; var <= &variables[LVM_MAX_VARIABLE_ID - 1] && var->name[0] != '\0'; var++) {
|
||
|
if(strcmp(var->name, name) == 0) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return (variable_id_t)(var - &variables[0]);
|
||
|
}
|
||
|
|
||
|
static operator_t *
|
||
|
get_operator(lvm_instance_t *p)
|
||
|
{
|
||
|
operator_t *operator;
|
||
|
|
||
|
operator = (operator_t *)&p->code[p->ip];
|
||
|
p->ip += sizeof(operator_t);
|
||
|
return operator;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
get_operand(lvm_instance_t *p, operand_t *operand)
|
||
|
{
|
||
|
memcpy(operand, &p->code[p->ip], sizeof(*operand));
|
||
|
p->ip += sizeof(*operand);
|
||
|
}
|
||
|
|
||
|
static node_type_t
|
||
|
get_type(lvm_instance_t *p)
|
||
|
{
|
||
|
node_type_t node_type;
|
||
|
|
||
|
node_type = *(node_type_t *)(p->code + p->ip);
|
||
|
p->ip += sizeof(node_type);
|
||
|
|
||
|
return node_type;
|
||
|
}
|
||
|
|
||
|
static long
|
||
|
operand_to_long(operand_t *operand)
|
||
|
{
|
||
|
switch(operand->type) {
|
||
|
case LVM_LONG:
|
||
|
return operand->value.l;
|
||
|
#if LVM_USE_FLOATS
|
||
|
case LVM_FLOAT:
|
||
|
return (long)operand->value.f;
|
||
|
break;
|
||
|
#endif /* LVM_USE_FLOATS */
|
||
|
case LVM_VARIABLE:
|
||
|
return variables[operand->value.id].value.l;
|
||
|
default:
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static lvm_status_t
|
||
|
eval_expr(lvm_instance_t *p, operator_t op, operand_t *result)
|
||
|
{
|
||
|
int i;
|
||
|
node_type_t type;
|
||
|
operator_t *operator;
|
||
|
operand_t operand[2];
|
||
|
long value[2];
|
||
|
long result_value;
|
||
|
lvm_status_t r;
|
||
|
|
||
|
for(i = 0; i < 2; i++) {
|
||
|
type = get_type(p);
|
||
|
switch(type) {
|
||
|
case LVM_ARITH_OP:
|
||
|
operator = get_operator(p);
|
||
|
r = eval_expr(p, *operator, &operand[i]);
|
||
|
if(LVM_ERROR(r)) {
|
||
|
return r;
|
||
|
}
|
||
|
break;
|
||
|
case LVM_OPERAND:
|
||
|
get_operand(p, &operand[i]);
|
||
|
break;
|
||
|
default:
|
||
|
return SEMANTIC_ERROR;
|
||
|
}
|
||
|
value[i] = operand_to_long(&operand[i]);
|
||
|
}
|
||
|
|
||
|
switch(op) {
|
||
|
case LVM_ADD:
|
||
|
result_value = value[0] + value[1];
|
||
|
break;
|
||
|
case LVM_SUB:
|
||
|
result_value = value[0] - value[1];
|
||
|
break;
|
||
|
case LVM_MUL:
|
||
|
result_value = value[0] * value[1];
|
||
|
break;
|
||
|
case LVM_DIV:
|
||
|
if(value[1] == 0) {
|
||
|
return MATH_ERROR;
|
||
|
}
|
||
|
result_value = value[0] / value[1];
|
||
|
break;
|
||
|
default:
|
||
|
return EXECUTION_ERROR;
|
||
|
}
|
||
|
|
||
|
result->type = LVM_LONG;
|
||
|
result->value.l = result_value;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
eval_logic(lvm_instance_t *p, operator_t *op)
|
||
|
{
|
||
|
int i;
|
||
|
int r;
|
||
|
operand_t operand;
|
||
|
long result[2];
|
||
|
node_type_t type;
|
||
|
operator_t *operator;
|
||
|
long l1, l2;
|
||
|
int logic_result[2];
|
||
|
unsigned arguments;
|
||
|
|
||
|
if(IS_CONNECTIVE(*op)) {
|
||
|
arguments = *op == LVM_NOT ? 1 : 2;
|
||
|
for(i = 0; i < arguments; i++) {
|
||
|
type = get_type(p);
|
||
|
if(type != LVM_CMP_OP) {
|
||
|
return SEMANTIC_ERROR;
|
||
|
}
|
||
|
operator = get_operator(p);
|
||
|
logic_result[i] = eval_logic(p, operator);
|
||
|
if(LVM_ERROR(logic_result[i])) {
|
||
|
return logic_result[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(*op == LVM_NOT) {
|
||
|
return !logic_result[0];
|
||
|
} else if(*op == LVM_AND) {
|
||
|
return logic_result[0] == TRUE && logic_result[1] == TRUE;
|
||
|
} else {
|
||
|
return logic_result[0] == TRUE || logic_result[1] == TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for(i = 0; i < 2; i++) {
|
||
|
type = get_type(p);
|
||
|
switch(type) {
|
||
|
case LVM_ARITH_OP:
|
||
|
operator = get_operator(p);
|
||
|
r = eval_expr(p, *operator, &operand);
|
||
|
if(LVM_ERROR(r)) {
|
||
|
return r;
|
||
|
}
|
||
|
break;
|
||
|
case LVM_OPERAND:
|
||
|
get_operand(p, &operand);
|
||
|
break;
|
||
|
default:
|
||
|
return SEMANTIC_ERROR;
|
||
|
}
|
||
|
result[i] = operand_to_long(&operand);
|
||
|
}
|
||
|
|
||
|
l1 = result[0];
|
||
|
l2 = result[1];
|
||
|
PRINTF("Result1: %ld\nResult2: %ld\n", l1, l2);
|
||
|
|
||
|
switch(*op) {
|
||
|
case LVM_EQ:
|
||
|
return l1 == l2;
|
||
|
case LVM_NEQ:
|
||
|
return l1 != l2;
|
||
|
case LVM_GE:
|
||
|
return l1 > l2;
|
||
|
case LVM_GEQ:
|
||
|
return l1 >= l2;
|
||
|
case LVM_LE:
|
||
|
return l1 < l2;
|
||
|
case LVM_LEQ:
|
||
|
return l1 <= l2;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return EXECUTION_ERROR;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
lvm_reset(lvm_instance_t *p, unsigned char *code, lvm_ip_t size)
|
||
|
{
|
||
|
memset(code, 0, size);
|
||
|
p->code = code;
|
||
|
p->size = size;
|
||
|
p->end = 0;
|
||
|
p->ip = 0;
|
||
|
p->error = 0;
|
||
|
|
||
|
memset(variables, 0, sizeof(variables));
|
||
|
memset(derivations, 0, sizeof(derivations));
|
||
|
}
|
||
|
|
||
|
lvm_ip_t
|
||
|
lvm_jump_to_operand(lvm_instance_t *p)
|
||
|
{
|
||
|
lvm_ip_t old_end;
|
||
|
|
||
|
old_end = p->end;
|
||
|
p->end += sizeof(operator_t) + sizeof(node_type_t);
|
||
|
if(p->end >= p->size) {
|
||
|
p->error = __LINE__;
|
||
|
p->end = old_end;
|
||
|
}
|
||
|
|
||
|
return old_end;
|
||
|
}
|
||
|
|
||
|
lvm_ip_t
|
||
|
lvm_shift_for_operator(lvm_instance_t *p, lvm_ip_t end)
|
||
|
{
|
||
|
unsigned char *ptr;
|
||
|
lvm_ip_t old_end;
|
||
|
|
||
|
old_end = p->end;
|
||
|
|
||
|
if(p->end + sizeof(operator_t) > p->size || end >= old_end) {
|
||
|
p->error = __LINE__;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
ptr = p->code + end;
|
||
|
|
||
|
memmove(ptr + sizeof(operator_t) + sizeof(node_type_t), ptr, old_end - end);
|
||
|
p->end = end;
|
||
|
|
||
|
return old_end + sizeof(operator_t) + sizeof(node_type_t);
|
||
|
}
|
||
|
|
||
|
lvm_ip_t
|
||
|
lvm_get_end(lvm_instance_t *p)
|
||
|
{
|
||
|
return p->end;
|
||
|
}
|
||
|
|
||
|
lvm_ip_t
|
||
|
lvm_set_end(lvm_instance_t *p, lvm_ip_t end)
|
||
|
{
|
||
|
lvm_ip_t old_end;
|
||
|
|
||
|
if(end >= p->size) {
|
||
|
p->error = __LINE__;
|
||
|
return p->end;
|
||
|
}
|
||
|
|
||
|
old_end = p->end;
|
||
|
p->end = end;
|
||
|
|
||
|
return old_end;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
lvm_set_type(lvm_instance_t *p, node_type_t type)
|
||
|
{
|
||
|
*(node_type_t *)(p->code + p->end) = type;
|
||
|
p->end += sizeof(type);
|
||
|
}
|
||
|
|
||
|
lvm_status_t
|
||
|
lvm_execute(lvm_instance_t *p)
|
||
|
{
|
||
|
node_type_t type;
|
||
|
operator_t *operator;
|
||
|
lvm_status_t status;
|
||
|
|
||
|
p->ip = 0;
|
||
|
status = EXECUTION_ERROR;
|
||
|
type = get_type(p);
|
||
|
switch(type) {
|
||
|
case LVM_CMP_OP:
|
||
|
operator = get_operator(p);
|
||
|
status = eval_logic(p, operator);
|
||
|
if(!LVM_ERROR(status)) {
|
||
|
PRINTF("The statement is %s\n", status == TRUE ? "true" : "false");
|
||
|
} else {
|
||
|
PRINTF("Execution error: %d\n", (int)status);
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
PRINTF("Error: The code must start with a relational operator\n");
|
||
|
}
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
lvm_set_op(lvm_instance_t *p, operator_t op)
|
||
|
{
|
||
|
lvm_set_type(p, LVM_ARITH_OP);
|
||
|
memcpy(&p->code[p->end], &op, sizeof(op));
|
||
|
p->end += sizeof(op);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
lvm_set_relation(lvm_instance_t *p, operator_t op)
|
||
|
{
|
||
|
lvm_set_type(p, LVM_CMP_OP);
|
||
|
memcpy(&p->code[p->end], &op, sizeof(op));
|
||
|
p->end += sizeof(op);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
lvm_set_operand(lvm_instance_t *p, operand_t *op)
|
||
|
{
|
||
|
lvm_set_type(p, LVM_OPERAND);
|
||
|
memcpy(&p->code[p->end], op, sizeof(*op));
|
||
|
p->end += sizeof(*op);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
lvm_set_long(lvm_instance_t *p, long l)
|
||
|
{
|
||
|
operand_t op;
|
||
|
|
||
|
op.type = LVM_LONG;
|
||
|
op.value.l = l;
|
||
|
|
||
|
lvm_set_operand(p, &op);
|
||
|
}
|
||
|
|
||
|
lvm_status_t
|
||
|
lvm_register_variable(char *name, operand_type_t type)
|
||
|
{
|
||
|
variable_id_t id;
|
||
|
variable_t *var;
|
||
|
|
||
|
id = lookup(name);
|
||
|
if(id == LVM_MAX_VARIABLE_ID) {
|
||
|
return VARIABLE_LIMIT_REACHED;
|
||
|
}
|
||
|
|
||
|
var = &variables[id];
|
||
|
if(var->name[0] == '\0') {
|
||
|
strncpy(var->name, name, sizeof(var->name) - 1);
|
||
|
var->name[sizeof(var->name) - 1] = '\0';
|
||
|
var->type = type;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
lvm_status_t
|
||
|
lvm_set_variable_value(char *name, operand_value_t value)
|
||
|
{
|
||
|
variable_id_t id;
|
||
|
|
||
|
id = lookup(name);
|
||
|
if(id == LVM_MAX_VARIABLE_ID) {
|
||
|
return INVALID_IDENTIFIER;
|
||
|
}
|
||
|
variables[id].value = value;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
lvm_set_variable(lvm_instance_t *p, char *name)
|
||
|
{
|
||
|
operand_t op;
|
||
|
variable_id_t id;
|
||
|
|
||
|
id = lookup(name);
|
||
|
if(id < LVM_MAX_VARIABLE_ID) {
|
||
|
PRINTF("var id = %d\n", id);
|
||
|
op.type = LVM_VARIABLE;
|
||
|
op.value.id = id;
|
||
|
lvm_set_operand(p, &op);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
lvm_clone(lvm_instance_t *dst, lvm_instance_t *src)
|
||
|
{
|
||
|
memcpy(dst, src, sizeof(*dst));
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
create_intersection(derivation_t *result, derivation_t *d1, derivation_t *d2)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for(i = 0; i < LVM_MAX_VARIABLE_ID; i++) {
|
||
|
if(!d1[i].derived && !d2[i].derived) {
|
||
|
continue;
|
||
|
} else if(d1[i].derived && !d2[i].derived) {
|
||
|
result[i].min.l = d1[i].min.l;
|
||
|
result[i].max.l = d1[i].max.l;
|
||
|
} else if(!d1[i].derived && d2[i].derived) {
|
||
|
result[i].min.l = d2[i].min.l;
|
||
|
result[i].max.l = d2[i].max.l;
|
||
|
} else {
|
||
|
/* Both derivations have been made; create an
|
||
|
intersection of the ranges. */
|
||
|
if(d1[i].min.l > d2[i].min.l) {
|
||
|
result[i].min.l = d1[i].min.l;
|
||
|
} else {
|
||
|
result[i].min.l = d2[i].min.l;
|
||
|
}
|
||
|
|
||
|
if(d1[i].max.l < d2[i].max.l) {
|
||
|
result[i].max.l = d1[i].max.l;
|
||
|
} else {
|
||
|
result[i].max.l = d2[i].max.l;
|
||
|
}
|
||
|
}
|
||
|
result[i].derived = 1;
|
||
|
}
|
||
|
|
||
|
#if DEBUG
|
||
|
PRINTF("Created an intersection of D1 and D2\n");
|
||
|
PRINTF("D1: \n");
|
||
|
print_derivations(d1);
|
||
|
PRINTF("D2: \n");
|
||
|
print_derivations(d2);
|
||
|
PRINTF("Result: \n");
|
||
|
print_derivations(result);
|
||
|
#endif /* DEBUG */
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
create_union(derivation_t *result, derivation_t *d1, derivation_t *d2)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for(i = 0; i < LVM_MAX_VARIABLE_ID; i++) {
|
||
|
if(!d1[i].derived && !d2[i].derived) {
|
||
|
continue;
|
||
|
} else if(d1[i].derived && !d2[i].derived) {
|
||
|
result[i].min.l = d1[i].min.l;
|
||
|
result[i].max.l = d1[i].max.l;
|
||
|
} else if(!d1[i].derived && d2[i].derived) {
|
||
|
result[i].min.l = d2[i].min.l;
|
||
|
result[i].max.l = d2[i].max.l;
|
||
|
} else {
|
||
|
/* Both derivations have been made; create a
|
||
|
union of the ranges. */
|
||
|
if(d1[i].min.l > d2[i].min.l) {
|
||
|
result[i].min.l = d2[i].min.l;
|
||
|
} else {
|
||
|
result[i].min.l = d1[i].min.l;
|
||
|
}
|
||
|
|
||
|
if(d1[i].max.l < d2[i].max.l) {
|
||
|
result[i].max.l = d2[i].max.l;
|
||
|
} else {
|
||
|
result[i].max.l = d1[i].max.l;
|
||
|
}
|
||
|
}
|
||
|
result[i].derived = 1;
|
||
|
}
|
||
|
|
||
|
#if DEBUG
|
||
|
PRINTF("Created a union of D1 and D2\n");
|
||
|
PRINTF("D1: \n");
|
||
|
print_derivations(d1);
|
||
|
PRINTF("D2: \n");
|
||
|
print_derivations(d2);
|
||
|
PRINTF("Result: \n");
|
||
|
print_derivations(result);
|
||
|
#endif /* DEBUG */
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
derive_relation(lvm_instance_t *p, derivation_t *local_derivations)
|
||
|
{
|
||
|
operator_t *operator;
|
||
|
node_type_t type;
|
||
|
operand_t operand[2];
|
||
|
int i;
|
||
|
int variable_id;
|
||
|
operand_value_t *value;
|
||
|
derivation_t *derivation;
|
||
|
|
||
|
type = get_type(p);
|
||
|
operator = get_operator(p);
|
||
|
|
||
|
if(IS_CONNECTIVE(*operator)) {
|
||
|
derivation_t d1[LVM_MAX_VARIABLE_ID];
|
||
|
derivation_t d2[LVM_MAX_VARIABLE_ID];
|
||
|
|
||
|
if(*operator != LVM_AND && *operator != LVM_OR) {
|
||
|
return DERIVATION_ERROR;
|
||
|
}
|
||
|
|
||
|
PRINTF("Attempting to infer ranges from a logical connective\n");
|
||
|
|
||
|
memset(d1, 0, sizeof(d1));
|
||
|
memset(d2, 0, sizeof(d2));
|
||
|
|
||
|
if(LVM_ERROR(derive_relation(p, d1)) ||
|
||
|
LVM_ERROR(derive_relation(p, d2))) {
|
||
|
return DERIVATION_ERROR;
|
||
|
}
|
||
|
|
||
|
if(*operator == LVM_AND) {
|
||
|
create_intersection(local_derivations, d1, d2);
|
||
|
} else if(*operator == LVM_OR) {
|
||
|
create_union(local_derivations, d1, d2);
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
for(i = 0; i < 2; i++) {
|
||
|
type = get_type(p);
|
||
|
switch(type) {
|
||
|
case LVM_OPERAND:
|
||
|
get_operand(p, &operand[i]);
|
||
|
break;
|
||
|
default:
|
||
|
return DERIVATION_ERROR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(operand[0].type == LVM_VARIABLE && operand[1].type == LVM_VARIABLE) {
|
||
|
return DERIVATION_ERROR;
|
||
|
}
|
||
|
|
||
|
/* Determine which of the operands that is the variable. */
|
||
|
if(operand[0].type == LVM_VARIABLE) {
|
||
|
if(operand[1].type == LVM_VARIABLE) {
|
||
|
return DERIVATION_ERROR;
|
||
|
}
|
||
|
variable_id = operand[0].value.id;
|
||
|
value = &operand[1].value;
|
||
|
} else {
|
||
|
variable_id = operand[1].value.id;
|
||
|
value = &operand[0].value;
|
||
|
}
|
||
|
|
||
|
if(variable_id >= LVM_MAX_VARIABLE_ID) {
|
||
|
return DERIVATION_ERROR;
|
||
|
}
|
||
|
|
||
|
PRINTF("variable id %d, value %ld\n", variable_id, *(long *)value);
|
||
|
|
||
|
derivation = local_derivations + variable_id;
|
||
|
/* Default values. */
|
||
|
derivation->max.l = LONG_MAX;
|
||
|
derivation->min.l = LONG_MIN;
|
||
|
|
||
|
switch(*operator) {
|
||
|
case LVM_EQ:
|
||
|
derivation->max = *value;
|
||
|
derivation->min = *value;
|
||
|
break;
|
||
|
case LVM_GE:
|
||
|
derivation->min.l = value->l + 1;
|
||
|
break;
|
||
|
case LVM_GEQ:
|
||
|
derivation->min.l = value->l;
|
||
|
break;
|
||
|
case LVM_LE:
|
||
|
derivation->max.l = value->l - 1;
|
||
|
break;
|
||
|
case LVM_LEQ:
|
||
|
derivation->max.l = value->l;
|
||
|
break;
|
||
|
default:
|
||
|
return DERIVATION_ERROR;
|
||
|
}
|
||
|
|
||
|
derivation->derived = 1;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
lvm_status_t
|
||
|
lvm_derive(lvm_instance_t *p)
|
||
|
{
|
||
|
return derive_relation(p, derivations);
|
||
|
}
|
||
|
|
||
|
lvm_status_t
|
||
|
lvm_get_derived_range(lvm_instance_t *p, char *name,
|
||
|
operand_value_t *min, operand_value_t *max)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for(i = 0; i < LVM_MAX_VARIABLE_ID; i++) {
|
||
|
if(strcmp(name, variables[i].name) == 0) {
|
||
|
if(derivations[i].derived) {
|
||
|
*min = derivations[i].min;
|
||
|
*max = derivations[i].max;
|
||
|
return TRUE;
|
||
|
}
|
||
|
return DERIVATION_ERROR;
|
||
|
}
|
||
|
}
|
||
|
return INVALID_IDENTIFIER;
|
||
|
}
|
||
|
|
||
|
#if DEBUG
|
||
|
static lvm_ip_t
|
||
|
print_operator(lvm_instance_t *p, lvm_ip_t index)
|
||
|
{
|
||
|
operator_t operator;
|
||
|
struct operator_map {
|
||
|
operator_t op;
|
||
|
char *representation;
|
||
|
};
|
||
|
struct operator_map operator_map[] = {
|
||
|
{LVM_ADD, "+"},
|
||
|
{LVM_SUB, "-"},
|
||
|
{LVM_MUL, "*"},
|
||
|
{LVM_DIV, "/"},
|
||
|
{LVM_GE, ">"},
|
||
|
{LVM_GEQ, ">="},
|
||
|
{LVM_LE, "<"},
|
||
|
{LVM_LEQ, "<="},
|
||
|
{LVM_EQ, "="},
|
||
|
{LVM_NEQ, "<>"},
|
||
|
{LVM_AND, "/\\"},
|
||
|
{LVM_OR, "\\/"},
|
||
|
{LVM_NOT, "!"}
|
||
|
};
|
||
|
int i;
|
||
|
|
||
|
memcpy(&operator, p->code + index, sizeof(operator));
|
||
|
|
||
|
for(i = 0; i < sizeof(operator_map) / sizeof(operator_map[0]); i++) {
|
||
|
if(operator_map[i].op == operator) {
|
||
|
PRINTF("%s ", operator_map[i].representation);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return index + sizeof(operator_t);
|
||
|
}
|
||
|
|
||
|
static lvm_ip_t
|
||
|
print_operand(lvm_instance_t *p, lvm_ip_t index)
|
||
|
{
|
||
|
operand_t operand;
|
||
|
|
||
|
memcpy(&operand, p->code + index, sizeof(operand));
|
||
|
|
||
|
switch(operand.type) {
|
||
|
case LVM_VARIABLE:
|
||
|
if(operand.value.id >= LVM_MAX_VARIABLE_ID || variables[operand.value.id].name == NULL) {
|
||
|
PRINTF("var(id:%d):?? ", operand.value.id);
|
||
|
} else {
|
||
|
PRINTF("var(%s):%ld ", variables[operand.value.id].name,
|
||
|
variables[operand.value.id].value.l);
|
||
|
}
|
||
|
break;
|
||
|
case LVM_LONG:
|
||
|
PRINTF("long:%ld ", operand.value.l);
|
||
|
break;
|
||
|
default:
|
||
|
PRINTF("?? ");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return index + sizeof(operand_t);
|
||
|
}
|
||
|
|
||
|
static lvm_ip_t
|
||
|
print_relation(lvm_instance_t *p, lvm_ip_t index)
|
||
|
{
|
||
|
/* Relational operators are stored as ordinary operators. */
|
||
|
return print_operator(p, index);
|
||
|
}
|
||
|
#endif /* DEBUG */
|
||
|
|
||
|
void
|
||
|
lvm_print_code(lvm_instance_t *p)
|
||
|
{
|
||
|
#if DEBUG
|
||
|
lvm_ip_t ip;
|
||
|
|
||
|
PRINTF("Code: ");
|
||
|
|
||
|
for(ip = 0; ip < p->end;) {
|
||
|
switch(*(node_type_t *)(p->code + ip)) {
|
||
|
case LVM_CMP_OP:
|
||
|
ip = print_relation(p, ip + sizeof(node_type_t));
|
||
|
break;
|
||
|
case LVM_ARITH_OP:
|
||
|
ip = print_operator(p, ip + sizeof(node_type_t));
|
||
|
break;
|
||
|
case LVM_OPERAND:
|
||
|
ip = print_operand(p, ip + sizeof(node_type_t));
|
||
|
break;
|
||
|
default:
|
||
|
PRINTF("Invalid opcode: 0x%x ", p->code[ip]);
|
||
|
ip = p->end;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
putchar('\n');
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void
|
||
|
lvm_print_derivations(lvm_instance_t *p)
|
||
|
{
|
||
|
#if DEBUG
|
||
|
print_derivations(derivations);
|
||
|
#endif /* DEBUG */
|
||
|
}
|
||
|
|
||
|
#ifdef TEST
|
||
|
int
|
||
|
main(void)
|
||
|
{
|
||
|
lvm_instance_t p;
|
||
|
unsigned char code[256];
|
||
|
|
||
|
lvm_reset(&p, code, sizeof(code));
|
||
|
|
||
|
lvm_register_variable("z", LVM_LONG);
|
||
|
lvm_set_variable_value("z", (operand_value_t)15L);
|
||
|
|
||
|
lvm_register_variable("y", LVM_LONG);
|
||
|
lvm_set_variable_value("y", (operand_value_t)109L);
|
||
|
|
||
|
/* Infix: 109 = y /\ 20 > 70 - (6 + z * 3) => 109 = 109 /\ 20 > 19 => true */
|
||
|
lvm_set_relation(&p, LVM_AND);
|
||
|
lvm_set_relation(&p, LVM_EQ);
|
||
|
lvm_set_long(&p, 109);
|
||
|
lvm_set_variable(&p, "y");
|
||
|
lvm_set_relation(&p, LVM_GE);
|
||
|
lvm_set_long(&p, 20);
|
||
|
lvm_set_op(&p, LVM_SUB);
|
||
|
lvm_set_long(&p, 70);
|
||
|
lvm_set_op(&p, LVM_ADD);
|
||
|
lvm_set_long(&p, 6);
|
||
|
lvm_set_op(&p, LVM_MUL);
|
||
|
lvm_set_variable(&p, "z");
|
||
|
lvm_set_long(&p, 3);
|
||
|
|
||
|
lvm_print_code(&p);
|
||
|
|
||
|
lvm_execute(&p);
|
||
|
|
||
|
/* Infix: !(9999 + 1 < -1 + 10001) => !(10000 < 10000) => true */
|
||
|
lvm_reset(&p, code, sizeof(code));
|
||
|
lvm_set_relation(&p, LVM_NOT);
|
||
|
lvm_set_relation(&p, LVM_LE);
|
||
|
lvm_set_op(&p, LVM_ADD);
|
||
|
lvm_set_long(&p, 9999);
|
||
|
lvm_set_long(&p, 1);
|
||
|
lvm_set_op(&p, LVM_ADD);
|
||
|
lvm_set_long(&p, -1);
|
||
|
lvm_set_long(&p, 10001);
|
||
|
|
||
|
lvm_print_code(&p);
|
||
|
|
||
|
lvm_execute(&p);
|
||
|
|
||
|
/* Derivation tests */
|
||
|
|
||
|
/* Infix: a = 5 => a:(5,5) */
|
||
|
lvm_reset(&p, code, sizeof(code));
|
||
|
lvm_register_variable("a", LVM_LONG);
|
||
|
lvm_set_relation(&p, LVM_EQ);
|
||
|
lvm_set_variable(&p, "a");
|
||
|
lvm_set_long(&p, 5);
|
||
|
|
||
|
lvm_derive(&p);
|
||
|
lvm_print_derivations(&p);
|
||
|
|
||
|
/* Infix: a < 10 => a:(-oo,9) */
|
||
|
lvm_reset(&p, code, sizeof(code));
|
||
|
lvm_register_variable("a", LVM_LONG);
|
||
|
lvm_set_relation(&p, LVM_LE);
|
||
|
lvm_set_variable(&p, "a");
|
||
|
lvm_set_long(&p, 10);
|
||
|
|
||
|
lvm_derive(&p);
|
||
|
lvm_print_derivations(&p);
|
||
|
|
||
|
/* Infix: a < 100 /\ 10 < a => a:(11,99) */
|
||
|
lvm_reset(&p, code, sizeof(code));
|
||
|
lvm_register_variable("a", LVM_LONG);
|
||
|
lvm_set_relation(&p, LVM_AND);
|
||
|
lvm_set_relation(&p, LVM_LE);
|
||
|
lvm_set_variable(&p, "a");
|
||
|
lvm_set_long(&p, 100);
|
||
|
lvm_set_relation(&p, LVM_GE);
|
||
|
lvm_set_long(&p, 10);
|
||
|
lvm_set_variable(&p, "a");
|
||
|
|
||
|
lvm_derive(&p);
|
||
|
lvm_print_derivations(&p);
|
||
|
|
||
|
/* Infix: a < 100 /\ b > 100 => a:(-oo,99), b:(101,oo) */
|
||
|
lvm_reset(&p, code, sizeof(code));
|
||
|
lvm_register_variable("a", LVM_LONG);
|
||
|
lvm_register_variable("b", LVM_LONG);
|
||
|
lvm_set_relation(&p, LVM_AND);
|
||
|
lvm_set_relation(&p, LVM_LE);
|
||
|
lvm_set_variable(&p, "a");
|
||
|
lvm_set_long(&p, 100);
|
||
|
lvm_set_relation(&p, LVM_GE);
|
||
|
lvm_set_variable(&p, "b");
|
||
|
lvm_set_long(&p, 100);
|
||
|
|
||
|
lvm_derive(&p);
|
||
|
lvm_print_derivations(&p);
|
||
|
|
||
|
/* Infix: a < 100 \/ a < 1000 \/ a < 1902 => a:(-oo,1901) */
|
||
|
lvm_reset(&p, code, sizeof(code));
|
||
|
lvm_register_variable("a", LVM_LONG);
|
||
|
lvm_set_relation(&p, LVM_OR);
|
||
|
lvm_set_relation(&p, LVM_LE);
|
||
|
lvm_set_variable(&p, "a");
|
||
|
lvm_set_long(&p, 100);
|
||
|
lvm_set_relation(&p, LVM_OR);
|
||
|
lvm_set_relation(&p, LVM_LE);
|
||
|
lvm_set_long(&p, 1000);
|
||
|
lvm_set_variable(&p, "a");
|
||
|
lvm_set_relation(&p, LVM_LE);
|
||
|
lvm_set_variable(&p, "a");
|
||
|
lvm_set_long(&p, 1902);
|
||
|
|
||
|
lvm_derive(&p);
|
||
|
lvm_print_derivations(&p);
|
||
|
|
||
|
/* Infix: (a < 100 /\ a < 90 /\ a > 80 /\ a < 105) \/ b > 10000 =>
|
||
|
a:(81,89), b:(10001:oo) */
|
||
|
lvm_reset(&p, code, sizeof(code));
|
||
|
lvm_register_variable("a", LVM_LONG);
|
||
|
lvm_register_variable("b", LVM_LONG);
|
||
|
|
||
|
lvm_set_relation(&p, LVM_OR);
|
||
|
lvm_set_relation(&p, LVM_GE);
|
||
|
lvm_set_variable(&p, "b");
|
||
|
lvm_set_long(&p, 10000);
|
||
|
|
||
|
lvm_set_relation(&p, LVM_AND);
|
||
|
lvm_set_relation(&p, LVM_LE);
|
||
|
lvm_set_variable(&p, "a");
|
||
|
lvm_set_long(&p, 100);
|
||
|
lvm_set_relation(&p, LVM_AND);
|
||
|
lvm_set_relation(&p, LVM_LE);
|
||
|
lvm_set_variable(&p, "a");
|
||
|
lvm_set_long(&p, 90);
|
||
|
lvm_set_relation(&p, LVM_AND);
|
||
|
lvm_set_relation(&p, LVM_GE);
|
||
|
lvm_set_variable(&p, "a");
|
||
|
lvm_set_long(&p, 80);
|
||
|
lvm_set_relation(&p, LVM_LE);
|
||
|
lvm_set_variable(&p, "a");
|
||
|
lvm_set_long(&p, 105);
|
||
|
|
||
|
lvm_derive(&p);
|
||
|
lvm_print_derivations(&p);
|
||
|
|
||
|
printf("Done\n");
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
#endif
|