简体   繁体   中英

C - boolean expression evaluator

I have a program in C that solves the circuit satisfiability with MPI. The circuit can contain AND,OR and NOT gates.

In my program the circuit is "hard coded" as it follows:

( v[0] ||  v[1]  ) && ( !v[1]  || !v[3]  ) && (  v[2]  ||  v[3]  )

with mapping: || = OR, && = AND, ! = NOT || = OR, && = AND, ! = NOT

v[0], v[1] , etc is an array of 0 and 1

At some point, i evaluate the circuit like this:

value = ( v[0] ||  v[1]  ) && ( !v[1]  || !v[3]  ) && (  v[2]  ||  v[3]  );

I want to test multiple circuits which are read from a text file. Now, my question is: How can i convert from string to logical expression in C?

Basically, i want something like value = 'string from file here'.

Any suggestions?

Ok. I've modified Shunting-Yard algo for working with boolean expressions.
Here's code for evaluating boolean expressions:

#include <string.h>
#include <stdio.h>
#define bool int
#define false 0
#define true 1

int op_preced(const char c)
{
    switch(c)    {
        case '|':
            return 6;
        case '&':
            return 5;
        case '!':
            return 4;
        case '*':  case '/': case '%':
            return 3;
        case '+': case '-':
            return 2;
        case '=':
            return 1;
    }
    return 0;
}

bool op_left_assoc(const char c)
{
    switch(c)    {
        // left to right
        case '*': case '/': case '%': case '+': case '-': case '|': case '&':
            return true;
        // right to left
        case '=': case '!':
            return false;
    }
    return false;
}

unsigned int op_arg_count(const char c)
{
    switch(c)  {
        case '*': case '/': case '%': case '+': case '-': case '=': case '&': case '|':
            return 2;
        case '!':
            return 1;
        default:
            return c - 'A';
    }
    return 0;
}

#define is_operator(c)  (c == '+' || c == '-' || c == '/' || c == '*' || c == '!' || c == '%' || c == '=' || c == '&' || c == '|')
#define is_function(c)  (c >= 'A' && c <= 'Z')
#define is_ident(c)     ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z'))

bool shunting_yard(const char *input, char *output)
{
    const char *strpos = input, *strend = input + strlen(input);
    char c, *outpos = output;

    char stack[32];       // operator stack
    unsigned int sl = 0;  // stack length
    char     sc;          // used for record stack element

    while(strpos < strend)   {
        // read one token from the input stream
        c = *strpos;
        if(c != ' ')    {
            // If the token is a number (identifier), then add it to the output queue.
            if(is_ident(c))  {
                *outpos = c; ++outpos;
            }
            // If the token is a function token, then push it onto the stack.
            else if(is_function(c))   {
                stack[sl] = c;
                ++sl;
            }
            // If the token is a function argument separator (e.g., a comma):
            else if(c == ',')   {
                bool pe = false;
                while(sl > 0)   {
                    sc = stack[sl - 1];
                    if(sc == '(')  {
                        pe = true;
                        break;
                    }
                    else  {
                        // Until the token at the top of the stack is a left parenthesis,
                        // pop operators off the stack onto the output queue.
                        *outpos = sc;
                        ++outpos;
                        sl--;
                    }
                }
                // If no left parentheses are encountered, either the separator was misplaced
                // or parentheses were mismatched.
                if(!pe)   {
                    printf("Error: separator or parentheses mismatched\n");
                    return false;
                }
            }
            // If the token is an operator, op1, then:
            else if(is_operator(c))  {
                while(sl > 0)    {
                    sc = stack[sl - 1];
                    if(is_operator(sc) &&
                        ((op_left_assoc(c) && (op_preced(c) >= op_preced(sc))) ||
                           (op_preced(c) > op_preced(sc))))   {
                        // Pop op2 off the stack, onto the output queue;
                        *outpos = sc;
                        ++outpos;
                        sl--;
                    }
                    else   {
                        break;
                    }
                }
                // push op1 onto the stack.
                stack[sl] = c;
                ++sl;
            }
            // If the token is a left parenthesis, then push it onto the stack.
            else if(c == '(')   {
                stack[sl] = c;
                ++sl;
            }
            // If the token is a right parenthesis:
            else if(c == ')')    {
                bool pe = false;
                // Until the token at the top of the stack is a left parenthesis,
                // pop operators off the stack onto the output queue
                while(sl > 0)     {
                    sc = stack[sl - 1];
                    if(sc == '(')    {
                        pe = true;
                        break;
                    }
                    else  {
                        *outpos = sc;
                        ++outpos;
                        sl--;
                    }
                }
                // If the stack runs out without finding a left parenthesis, then there are mismatched parentheses.
                if(!pe)  {
                    printf("Error: parentheses mismatched\n");
                    return false;
                }
                // Pop the left parenthesis from the stack, but not onto the output queue.
                sl--;
                // If the token at the top of the stack is a function token, pop it onto the output queue.
                if(sl > 0)   {
                    sc = stack[sl - 1];
                    if(is_function(sc))   {
                        *outpos = sc;
                        ++outpos;
                        sl--;
                    }
                }
            }
            else  {
                printf("Unknown token %c\n", c);
                return false; // Unknown token
            }
        }
        ++strpos;
    }
    // When there are no more tokens to read:
    // While there are still operator tokens in the stack:
    while(sl > 0)  {
        sc = stack[sl - 1];
        if(sc == '(' || sc == ')')   {
            printf("Error: parentheses mismatched\n");
            return false;
        }
        *outpos = sc;
        ++outpos;
        --sl;
    }
    *outpos = 0; // Null terminator
    return true;
}

bool evalBoolExpr(char * expr)  {
    char output[500] = {0};
    char * op;
    bool tmp;
    char part1[250], part2[250];

    if(!shunting_yard(expr, output))
      return false;  // oops can't convert to postfix form

    while (strlen(output) > 1) {
        op = &output[0];
        while (!is_operator(*op) && *op != '\0')
          op++;
        if (*op == '\0') {
          return false;  // oops - zero operators found
        }
        else if (*op == '!') {
            tmp = !(*(op-1) - 48);
            *(op-1) = '\0';
        }
        else if(*op == '&') {
            tmp = (*(op-1) - 48) && (*(op-2) - 48);
            *(op-2) = '\0';
        }
        else if (*op == '|') {
            tmp = (*(op-1) - 48) || (*(op-2) - 48);
            *(op-2) = '\0';
        }

        memset(part1, 0, sizeof(part1));
        memset(part2, 0, sizeof(part2));
        strcpy(part1, output);
        strcpy(part2, op+1);
        memset(output, 0, sizeof(output));
        strcat(output, part1);
        strcat(output, ((tmp==false) ? "0" : "1"));
        strcat(output, part2);
    }
    return *output - 48;
}

int main() {
    char * boolString[2] = {"FALSE", "TRUE"};
    char * expr = "!((1 | 0) & (1 & ((1 & !0) | 0)))";
    bool result = evalBoolExpr(expr);
    printf("Boolean expr. %s is %s", expr, boolString[result]);
    return 0;
}

Just put & / | symbols in logical expresion instead of double && / || .

You could build a simple virtual machine or build a parser that reads from a text file. Both would work, it depends on what you want. Having a "valid logical expression in c from a text file" wouldn't work exactly well. C isn't a scripting language and doesn't run new code during run time. This would require a virtual machine (LUA) or a parser. I build a small virtual machine that executes a small set of instructions (5 or 6) that can do additions, subtraction, multiplications, division, etc.. and you could implement gates (ie OR = 0x01, AND = 0x02) and your text file would just contain some binary or hexadecimal representation of the binary.

This would add some complexities, and to become extremely effective in writing the text files, you'd need to implement some sort of compiler, unless you'd want to write the binary by hand (with a small set of instructions wouldn't be so bad).

You can check some examples on Github and there are a lot of sites online that explains simple virtual machines.

You need to write some parser for your expressions. Perhaps tools like ANTLR (or flex & bison ) could help. I suggest parsing into some abstract syntax tree , then evaluating that AST as your value.

You could perhaps also consider embedding an interpreter (eg Lua ) in your program

Maybe you could also consider translating your expressions into C code. Or just #include -ing a file containing them.

A possibility could be to generate a correct C source file generated.c at runtime, containing the C source code of some functions (which you could emit from some parsed AST), then to compile it (eg, assuming a Linux system, by forking some gcc -Wall -O -fPIC -shared generated.c -o generated.so command), then to dynamically load the so produced ./generated.so with dlopen(3) and use dlsym(3) to find there the relevant function addresses (so you could use them as function pointers and call these). FWIW, the MELT domain specific language to extend GCC does that successfully.

I still don't exactly understand what you are trying to do and what you ask

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM