[英]C - boolean expression evaluator
我有一個用C編寫的程序,用於解決MPI的電路可滿足性。 該電路可以包含AND,OR和NOT門。
在我的程序中,電路是“硬編碼的”,如下所示:
( v[0] || v[1] ) && ( !v[1] || !v[3] ) && ( v[2] || v[3] )
與映射: || = OR, && = AND, ! = NOT
|| = OR, && = AND, ! = NOT
v[0], v[1]
等是0和1的數組
在某些時候,我這樣評估電路:
value = ( v[0] || v[1] ) && ( !v[1] || !v[3] ) && ( v[2] || v[3] );
我想測試從文本文件中讀取的多個電路。 現在,我的問題是:如何在C中從字符串轉換為邏輯表達式?
基本上,我想要類似value ='file from string here'的東西。
有什么建議么?
好。 我修改了Shunting-Yard
算法來處理布爾表達式。
這是用於評估布爾表達式的代碼:
#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;
}
只要把&
/ |
邏輯表達式中的符號而不是double &&
/ ||
。
您可以構建一個簡單的虛擬機或構建一個從文本文件中讀取的解析器。 兩者都有效,這取決於你想要什么。 在文本文件中使用“c中的有效邏輯表達式”將無法正常工作。 C不是腳本語言,並且在運行時不運行新代碼。 這將需要虛擬機(LUA)或解析器。 我構建了一個小型虛擬機,它執行一小組指令(5或6),可以進行加法,減法,乘法,除法等。你可以實現門(即OR = 0x01,AND = 0x02)和你的文本file只包含二進制的二進制或十六進制表示。
這會增加一些復雜性,並且為了在編寫文本文件方面變得非常有效,你需要實現某種編譯器,除非你想手動編寫二進制文件(一小組指令不會是太糟了)。
您可以在Github上查看一些示例,在線上有很多網站介紹了簡單的虛擬機。
您需要為表達式編寫一些解析器。 也許像ANTLR (或flex & bison )這樣的工具可能有所幫助。 我建議解析一些抽象語法樹 ,然后將AST作為你的值進行評估。
你也許也可以考慮在你的程序中嵌入一個解釋器(例如Lua )
也許您也可以考慮將表達式轉換為C代碼。 或者只是#include
-ing包含它們的文件。
一種可能性可能是產生一個正確的C源文件generated.c
在運行時,含有的一些功能(這你可以發射從一些解析AST)的C源代碼,然后對其進行編譯(例如,假設一個Linux系統中,由分叉一些gcc -Wall -O -fPIC -shared generated.c -o generated.so
命令),然后使用dlopen(3)動態加載生成的./generated.so
並使用dlsym(3)在此處找到相關功能地址(因此您可以將它們用作函數指針並調用它們)。 FWIW是擴展GCC的MELT域特定語言,可以成功地做到這一點。
我仍然不完全明白你要做什么和你問什么
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.