https://github.com/jeremyko/expression-tree-with-user-functions

C++ 로 작성된, 사용자가 정의한 함수를 사용할수 있게한 수식트리.

간단한 사칙연산 수식의 경우엔 이미 공개된 소스가 많지만, 좀 더 복잡한 경우에 적용 불가하므로 한번 작성해보았다.

사용자가 입력하는 infix 수식을 postfix로 변환 후, 이를 가지고 수식트리를 생성하게 된다.

다음과 같은 간단한 사칙연산의 경우엔

'A'+'B'+'C'='ABC'

다음처럼 수식트리가 구성될것이다.

blog-image

그런데 좀더 복잡한 경우, 예를 들어서

'1' + StrCat3('A','B','C') = '1ABC'

라는 수식이 있다고 가정해보자. 여기서 StrCat3 은 사용자가 임의로 작성한 함수이다.
이 함수는 어떤식으로 트리에 표현하면 될까..?

blog-image

이 코드에서는 약간 변형된 이진트리를 사용했는데, 피 연산되는 오른쪽 노드가 list를 가지고, 동적으로 함수 인자를 할당, 추가하는 방식으로 구현됬다. 위의 수식 '1' + StrCat3('A','B','C') = '1ABC' 경우엔 다음처럼 트리가 구성된다.

blog-image

함수내에 함수들이 중첩되는 좀더 복잡한 경우를 생각해보자.

'1' + StrCat3('A', 'B', StrCat3('C','D','E')) = '1ABCDE'

이경우에 구성되는 트리는 다음과 같다.

blog-image

만약 어떤 함수가 인자를 하나도 갖지 않는 경우엔 어떻게 될까.

100 = GetInt100()

이 경우에 구성되는 트리는 다음과 같다.

blog-image

이진 트리를 만들기 위해 NULL 자식을 추가했다.

트리를 평가해서 결과를 구하기 위해서는 스택이 넘치는것을 방지 하기 위해서 비 재귀적인 방법으로 처리를 했다.

그런데 함수 인자를 위해 추가된 노드를 위해서는, 일반적 후위순회 처리에 추가해서, 동적으로 할당된 함수의 인자 노드까지 모두 순회하게 처리해줘야 한다.

이 부분에서는 재귀적인 방법으로 처리를 하고 있는데,먼저 해당 노드의 right->rightSiblingForMore2funcArgs 을 처리하고, 이후 rightSiblingForMore2funcArgs 을 처리하면 된다.

Usage

general arithmetic

ExpressionTree expTree;
expression_result* pExpressionRslt;
bool bRslt = false;

bRslt = expTree.SetInfixExpression("1 * -2 ");
EXPECT_TRUE(bRslt);
if (bRslt)
{
   bRslt = expTree.EvaluateExpression();
   EXPECT_TRUE(bRslt);
   pExpressionRslt = expTree.GetResult();
   cout << "expTree.EvaluateExpression returns :"
        << pExpressionRslt->nResultLong << "\n";
   EXPECT_EQ(-2, pExpressionRslt->nResultLong);
}

custom user functions

//see user_functions.h, user_functions.cpp
//-----------------------------------------------------
bRslt = expTree.SetInfixExpression("SumInt4(SumInt4(1,1+1,1+1,2), 1+1, 1+1, 1+1)");
EXPECT_TRUE(bRslt);
bRslt = expTree.EvaluateExpression();
EXPECT_TRUE(bRslt);
pExpressionRslt = expTree.GetResult();
cout << "expTree.EvaluateExpression returns :"
     << pExpressionRslt->nResultLong << "\n";
EXPECT_EQ(13, pExpressionRslt->nResultLong);

//-----------------------------------------------------
bRslt = expTree.SetInfixExpression("GetBool('1'='1')");
EXPECT_TRUE(bRslt);
bRslt = expTree.EvaluateExpression();
EXPECT_TRUE(bRslt);

//-----------------------------------------------------
bRslt = expTree.SetInfixExpression("StrCat('ab','cd')=StrCat('a','b')+StrCat('c','d') ");
EXPECT_TRUE(bRslt);
bRslt = expTree.EvaluateExpression();
EXPECT_TRUE(bRslt);

//-----------------------------------------------------
bRslt = expTree.SetInfixExpression("StrCat3('1','2',StrCat3('a','b',StrCat3('X','Y','Z')) )");
EXPECT_TRUE(bRslt);
bRslt = expTree.EvaluateExpression();
EXPECT_TRUE(bRslt);
EXPECT_EQ(4, expTree.GetDepth());
pExpressionRslt = expTree.GetResult();
cout << "expTree.EvaluateExpression returns :" << pExpressionRslt->strResult << "\n";
EXPECT_STREQ("12abXYZ", pExpressionRslt->strResult);

using placeholder

// placeholder identifier is :$ph1, :$ph2, .... :$ph50 (max)
bRslt = expTree.SetInfixExpression ( ":$ph1+:$ph2" );
EXPECT_TRUE ( bRslt );
if ( bRslt )
{
    expTree.SetNumberLongValueOfPlaceHolder ( 1, 1 );
    expTree.SetNumberLongValueOfPlaceHolder ( 2, 2 );
    bRslt = expTree.EvaluateExpression ( );
    EXPECT_TRUE ( bRslt );
    pExpressionRslt = expTree.GetResult ( );
    cout << "expTree.EvaluateExpression returns :" << pExpressionRslt->nResultLong << "\n";
    EXPECT_EQ ( 3, pExpressionRslt->nResultLong );

    //-----------------------------------------------------
    //there's no rebuild tree
    expTree.SetNumberFloatValueOfPlaceHolder ( 1, 2.0f );
    expTree.SetNumberFloatValueOfPlaceHolder ( 2, 3.0f );
    bRslt = expTree.EvaluateExpression ( );
    EXPECT_TRUE ( bRslt );
    pExpressionRslt = expTree.GetResult ( );
    cout << "expTree.EvaluateExpression returns :" << pExpressionRslt->nResultFloat << "\n";
    EXPECT_FLOAT_EQ ( 5.0f, pExpressionRslt->nResultFloat );

    //-----------------------------------------------------
    expTree.SetStringValueOfPlaceHolder ( 1, "A" );
    expTree.SetStringValueOfPlaceHolder ( 2, "B" );
    bRslt = expTree.EvaluateExpression ( );
    EXPECT_TRUE ( bRslt );
    pExpressionRslt = expTree.GetResult ( );
    cout << "expTree.EvaluateExpression returns :" << pExpressionRslt->strResult << "\n";
    EXPECT_STREQ ( "AB", pExpressionRslt->strResult );
}