From d73eb9b5b2022dddf90b4904ddb93e0753071d9e Mon Sep 17 00:00:00 2001 From: Rafal Kupiec Date: Fri, 29 Aug 2025 22:04:55 +0200 Subject: [PATCH] Implement exponentiation (**) operator --- engine/compiler.c | 7 ++++++ engine/parser.c | 33 ++++++++++++++++++++++++--- engine/vm.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++ include/ph7int.h | 4 ++++ 4 files changed, 99 insertions(+), 3 deletions(-) diff --git a/engine/compiler.c b/engine/compiler.c index d65fb79..317c487 100644 --- a/engine/compiler.c +++ b/engine/compiler.c @@ -4742,6 +4742,13 @@ static sxi32 PH7_GenStateEmitExprCode( (void)PH7_VmPopInstr(pGen->pVm); } } + } else if(pNode->pOp->iPrec == 20 && pNode->pOp->iOp != EXPR_OP_ASSIGN) { + pInstr = PH7_VmPeekInstr(pGen->pVm); + if(pInstr) { + if(pInstr->iOp != PH7_OP_LOAD_IDX) { + p3 = pInstr->p3; + } + } } } if(iVmOp > 0) { diff --git a/engine/parser.c b/engine/parser.c index 179c79e..8503b9f 100644 --- a/engine/parser.c +++ b/engine/parser.c @@ -52,8 +52,10 @@ static const ph7_expr_op aOpTable[] = { { {"(resource)", sizeof("(resource)") - 1}, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_RES}, { {"(void)", sizeof("(void)") - 1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_VOID }, /* Binary operators */ - /* Precedence 6,non-associative */ - { {"is", sizeof("is") - 1}, EXPR_OP_IS, 6, EXPR_OP_NON_ASSOC, PH7_OP_IS}, + /* Precedence 5,non-associative */ + { {"is", sizeof("is") - 1}, EXPR_OP_IS, 5, EXPR_OP_NON_ASSOC, PH7_OP_IS}, + /* Precedence 6, right-associative */ + { {"**", sizeof(char) * 2}, EXPR_OP_POW, 6, EXPR_OP_ASSOC_RIGHT, PH7_OP_POW}, /* Precedence 7,left-associative */ { {"*", sizeof(char)}, EXPR_OP_MUL, 7, EXPR_OP_ASSOC_LEFT, PH7_OP_MUL}, { {"/", sizeof(char)}, EXPR_OP_DIV, 7, EXPR_OP_ASSOC_LEFT, PH7_OP_DIV}, @@ -97,6 +99,7 @@ static const ph7_expr_op aOpTable[] = { { {"+=", sizeof(char) * 2}, EXPR_OP_ADD_ASSIGN, 20, EXPR_OP_ASSOC_RIGHT, PH7_OP_ADD_STORE }, { {"-=", sizeof(char) * 2}, EXPR_OP_SUB_ASSIGN, 20, EXPR_OP_ASSOC_RIGHT, PH7_OP_SUB_STORE }, { {"*=", sizeof(char) * 2}, EXPR_OP_MUL_ASSIGN, 20, EXPR_OP_ASSOC_RIGHT, PH7_OP_MUL_STORE }, + { {"**=", sizeof(char) * 3}, EXPR_OP_POW_ASSIGN, 20, EXPR_OP_ASSOC_RIGHT, PH7_OP_POW_STORE }, { {"/=", sizeof(char) * 2}, EXPR_OP_DIV_ASSIGN, 20, EXPR_OP_ASSOC_RIGHT, PH7_OP_DIV_STORE }, { {"%=", sizeof(char) * 2}, EXPR_OP_MOD_ASSIGN, 20, EXPR_OP_ASSOC_RIGHT, PH7_OP_MOD_STORE }, { {"&=", sizeof(char) * 2}, EXPR_OP_AND_ASSIGN, 20, EXPR_OP_ASSOC_RIGHT, PH7_OP_BAND_STORE }, @@ -1020,8 +1023,32 @@ static sxi32 ExprMakeTree(ph7_gen_state *pGen, ph7_expr_node **apNode, sxi32 nTo iLeft = iCur; } } + /* Handle right associative binary operators with precedence 6 */ + iRight = -1; + for(iCur = nToken - 1; iCur >= 0; iCur--) { + if(apNode[iCur] == 0) { + continue; + } + pNode = apNode[iCur]; + if(pNode->pOp && pNode->pOp->iPrec == 6 && pNode->pLeft == 0) { + /* Get the left node */ + iLeft = iCur - 1; + while(iLeft >= 0 && apNode[iLeft] == 0) { + iLeft--; + } + if(iLeft < 0 || iRight < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft)) { + /* Syntax error */ + PH7_GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid operand", &pNode->pOp->sOp); + } + /* Link the node to the tree */ + pNode->pLeft = apNode[iRight]; + pNode->pRight = apNode[iLeft]; + apNode[iLeft] = apNode[iRight] = 0; + } + iRight = iCur; + } /* Process left and non-associative binary operators [i.e: *,/,&&,||...]*/ - for(i = 6 ; i < 18 ; i++) { + for(i = 5 ; i < 18 ; i++) { iLeft = -1; for(iCur = 0 ; iCur < nToken ; ++iCur) { if(apNode[iCur] == 0) { diff --git a/engine/vm.c b/engine/vm.c index 517a030..7a1c7ea 100644 --- a/engine/vm.c +++ b/engine/vm.c @@ -7,6 +7,7 @@ * Rafal Kupiec * David Carlier */ +#include #include "ph7int.h" /* @@ -2979,6 +2980,63 @@ static sxi32 VmByteCodeExec( VmPopOperand(&pTos, 1); break; } + /* OP_POW * * * + * OP_POW_STORE * * * + * + * Pop the top two elements from the stack, perform an exponentiation, + * and push the result back onto the stack. + */ + case PH7_OP_POW: + case PH7_OP_POW_STORE: { + SyString sName; + ph7_value *pNos = &pTos[-1]; + /* Force the operand to be numeric */ + if(pNos < pStack) { + goto Abort; + } + PH7_MemObjToNumeric(pTos); + PH7_MemObjToNumeric(pNos); + /* Perform the requested operation */ + if(MEMOBJ_REAL & (pTos->nType | pNos->nType)) { + /* Floating point arithmetic */ + ph7_real a, b, r; + if((pTos->nType & MEMOBJ_REAL) == 0) { + PH7_MemObjToReal(pTos); + } + if((pNos->nType & MEMOBJ_REAL) == 0) { + PH7_MemObjToReal(pNos); + } + a = pNos->x.rVal; + b = pTos->x.rVal; + r = pow(b, a); + /* Push the result */ + pNos->x.rVal = r; + MemObjSetType(pNos, MEMOBJ_REAL); + } else { + /* Integer arithmetic */ + sxi64 a, b, r; + a = pNos->x.iVal; + b = pTos->x.iVal; + r = (sxi64)pow((double)b, (double)a); + /* Push the result */ + pNos->x.iVal = r; + MemObjSetType(pNos, MEMOBJ_INT); + } + if(pInstr->iOp == PH7_OP_POW_STORE) { + ph7_value *pObj; + if(pTos->nIdx == SXU32_HIGH) { + PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Cannot perform assignment on a constant class attribute"); + } else if((pObj = (ph7_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0) { + if(pNos->nType != pObj->nType && PH7_CheckVarCompat(pNos, pObj->nType) != SXRET_OK) { + SyStringInitFromBuf(&sName, pInstr->p3, SyStrlen((const char *)pInstr->p3)); + PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Cannot assign a value of incompatible type to variable '$%z'", &sName); + }; + PH7_MemObjStore(pNos, pObj); + } + } + VmPopOperand(&pTos, 1); + break; + } /* OP_ADD P1 P2 * * * Pop the top two elements from the stack, add them together, diff --git a/include/ph7int.h b/include/ph7int.h index c65091f..e0282ce 100644 --- a/include/ph7int.h +++ b/include/ph7int.h @@ -1403,6 +1403,7 @@ enum ph7_vm_op { PH7_OP_BITNOT, /* Bitwise not '~' */ PH7_OP_LNOT, /* Logical not '!' */ PH7_OP_MUL, /* Multiplication '*' */ + PH7_OP_POW, /* POW '**' */ PH7_OP_DIV, /* Division '/' */ PH7_OP_MOD, /* Modulus '%' */ PH7_OP_ADD, /* Add '+' */ @@ -1437,6 +1438,7 @@ enum ph7_vm_op { PH7_OP_ADD_STORE, /* Add and store '+=' */ PH7_OP_SUB_STORE, /* Sub and store '-=' */ PH7_OP_MUL_STORE, /* Mul and store '*=' */ + PH7_OP_POW_STORE, /* Pow and store '**=' */ PH7_OP_DIV_STORE, /* Div and store '/=' */ PH7_OP_MOD_STORE, /* Mod and store '%=' */ PH7_OP_SHL_STORE, /* Shift left and store '>>=' */ @@ -1479,6 +1481,7 @@ enum ph7_expr_id { EXPR_OP_TYPECAST, /* Type cast [i.e: (int),(float),(string)...] */ EXPR_OP_IS, /* is */ EXPR_OP_LOGNOT, /* logical not ! */ + EXPR_OP_POW, /* Exponentiation '**' */ EXPR_OP_MUL, /* Multiplication */ EXPR_OP_DIV, /* division */ EXPR_OP_MOD, /* Modulus */ @@ -1505,6 +1508,7 @@ enum ph7_expr_id { EXPR_OP_ADD_ASSIGN, /* Combined operator: += */ EXPR_OP_SUB_ASSIGN, /* Combined operator: -= */ EXPR_OP_MUL_ASSIGN, /* Combined operator: *= */ + EXPR_OP_POW_ASSIGN, /* Combined operator: **= */ EXPR_OP_DIV_ASSIGN, /* Combined operator: /= */ EXPR_OP_MOD_ASSIGN, /* Combined operator: %= */ EXPR_OP_AND_ASSIGN, /* Combined operator: &= */