【发布时间】:2010-07-16 07:46:57
【问题描述】:
我有一个基于二叉树的数学表达式解析器,它非常适用于“普通”数学,例如:(3.5 * 2) ^ 1 / (1 << 6)。但是,我想稍微扩展它以添加一个三元选择运算符,镜像来自 C:{expr} ? {true-expr} : {false-expr} 的那个。我还想添加功能,例如sin(x) 或ave(...)。
但是,我不知道如何处理此问题(由于评估的工作方式),我也无法在网络上找到任何涵盖此内容的内容,至少以非基于语法的方式(我想避免语法分析器生成器,如果可能的话)。
我的解析器当前通过评估中缀表达式并立即将其转换为树来工作,然后可以从那里评估树,即:它是标准表达式树。
目前我的评估员看起来像这样:
struct Node
{
int nType;
union
{
unsigned long dwOperator;
BOOL bValue;
int nValue; //for indices, args & functions
number_t fValue;
char* szValue; //for string literals to pass to functions
};
Node* pLeft;
Node* pRight;
};
number_t EvaluateTree(Node* pNode)
{
if(pNode == NULL)
return 0.0f;
int nType = pNode->nType;
if(nType == TOKEN_OPERATOR)
{
number_t fLeft = EvaluateTree(pNode->pLeft);
number_t fRight = EvaluateTree(pNode->pRight);
switch(pNode->dwOperator)
{
case '+': return fLeft + fRight;
case '-': return fLeft - fRight;
case '*': return fLeft * fRight;
case '/': return fLeft / fRight;
case '^': return pow(fLeft,fRight);
case '_': return pow(fLeft,1.0f/fRight);
case '%': return fmod(fLeft,fRight);
//case '?': return bSelect = ?;
//case ':': return (bSelect) ? fLeft : fRight;
//case '>': return fLeft > fRight;
//case '<': return fLeft < fRight;
//case '>=': return fLeft >= fRight;
//case '<=': return fLeft <= fRight;
//case '==': return fLeft == fRight;
//case '!=': return fLeft != fRight;
//case '||': return fLeft || fRight;
//case '&&': return fLeft && fRight;
case '&': return static_cast<number_t>(static_cast<unsigned long>(fLeft) & static_cast<unsigned long>(fRight));
case '|': return static_cast<number_t>(static_cast<unsigned long>(fLeft) | static_cast<unsigned long>(fRight));
case '~': return static_cast<number_t>(~static_cast<unsigned long>(fRight));
case '>>': return static_cast<number_t>(static_cast<unsigned long>(fLeft) >> static_cast<unsigned long>(fRight));
case '<<': return static_cast<number_t>(static_cast<unsigned long>(fLeft) << static_cast<unsigned long>(fRight));
default:
{
printf("ERROR: Invalid Operator Found\n");
return 0.0f;
}
}
}
else if(nType == TOKEN_NUMBER)
return pNode->fValue;
else if(nType == TOKEN_CALL)
return CreateCall(pNode); //not implemented
else if(nType == TOKEN_GLOBAL)
return GetGlobal(pNode);
else if(nType == TOKEN_ARGUMENT)
return GetArgument(pNode);
else if(nType == TOKEN_STRING)
return 0.0f;
return 0.0f;
}
关于我如何做到这一点的任何提示/指针/建议或有用的链接?
一小部分示例(根据要求):
我已有的工作
输入:2 * (3 ^ 1.5) - 4 / (1 << 3)
输出:In-Order: 2.0 * 3.0 ^ 1.5 - 4.0 / 1.0 << 3.0
Pre-Order: - * 2.0 ^ 3.0 1.5 / 4.0 << 1.0 3.0
Post-Order: 2.0 3.0 1.5 ^ * 4.0 1.0 3.0 << / -
Result: 9.892304
我要补充的内容
输入:(GetDay() == 31) ? -15.5 : 8.4
输出:8.4
31日输出:-15.5
输入:max([0],20)(其中 [0] 表示参数 0,[0] = 35)
输出:20
输入:(GetField('employees','years_of_service',[0]) >= 10) ? 0.15 : 0.07(其中 [0] 是参数 0,[0] 设置为有效索引)
输出(如果员工的 years_of_service 小于 10:0.15
其他输出:0.07
它基本上是数学,带有一些受 C 启发的添加,除了参数不是按名称传递,而是按索引传递,字符串由单引号转义而不是双引号。
完成最后一点后,我希望编译字节码或 JIT输入集可以更改,但它被频繁使用,因此它需要“快速”,并且需要非程序员也可以使用。
【问题讨论】:
-
我知道你想避免它,但是解析器生成器的存在让你可以专注于真正重要的事情:你的语法。然而,我承认为教育目的创建一个很有趣。
-
是否有可能提供一个完整的示例,包括一些输入数据以及您期望的输出是什么?
-
@ereOn:是的,这比其他任何事情都更有助于理解和启发@Jon Cage:当然,我现在会添加一些示例:)
-
您是在问如何解析 i 表达式或如何表示和评估它?作为旁注,EvaluateTree 不应该是 Node.js 的成员函数。此外,您可能需要考虑为不同的节点类型使用派生类,并摆脱节点类型字段和 switch 语句。
-
hmmm,从来没有这样想过,但我想说更多的是如何表示和评估表示。至于为什么我不使用类(还),我决定让这件事以最简单的形式实际工作,然后我才完全喜欢 OOP。
标签: c++ c expression-trees mathematical-expressions